Skip to content

[Docs][Util][Content] - refactor docs to single source#919

Merged
madster456 merged 75 commits intodevfrom
docs/rework/platforms
Oct 20, 2025
Merged

[Docs][Util][Content] - refactor docs to single source#919
madster456 merged 75 commits intodevfrom
docs/rework/platforms

Conversation

@madster456
Copy link
Collaborator

@madster456 madster456 commented Oct 1, 2025

Removes Platform selection, moves docs to single /content folder and no longer gens docs. Only API docs are generated here.

High-level PR Summary

This PR makes significant changes to the documentation structure by removing platform-specific content organization and consolidating docs into a single /content folder. The primary goal is to simplify the documentation architecture by eliminating the platform-specific routing (Next.js, React, JavaScript, Python) and instead organizing content by topic (guides, SDK, components) regardless of platform. The PR removes platform selection functionality, platform-specific navigation, and the automatic generation of platform-specific documentation pages. It introduces a new docs tree filtering system that organizes content by section rather than by platform. These changes should make the documentation more maintainable and easier to navigate while focusing on the content itself rather than platform-specific variations.

⏱️ Estimated Review Time: 30-90 minutes

💡 Review Order Suggestion
Order File Path
1 docs/package.json
2 docs/src/lib/docs-tree.ts
3 docs/src/lib/navigation-utils.ts
4 docs/src/components/homepage/iconHover.tsx
5 docs/src/components/sdk/overview.tsx
6 docs/src/components/layouts/shared/section-utils.ts
7 docs/src/components/layout/custom-search-dialog.tsx
8 docs/src/app/api/search/route.ts
9 docs/src/app/docs/[[...slug]]/page.tsx
10 docs/src/components/layouts/docs-header-wrapper.tsx
11 docs/src/components/layouts/docs-layout-router.tsx
12 docs/src/components/layouts/docs.tsx
13 package.json

Need help? Join our Discord

Summary by CodeRabbit

  • Documentation

    • Added many new guides (auth providers, OAuth, JWT, API keys, emails, webhooks, orgs/teams, permissions, onboarding, customization), expanded SDK & component reference pages, examples, and navigation metadata.
    • Switched docs to a simpler section-based, platform-agnostic structure and improved getting-started and production checklists.
  • Developer Experience

    • Enhanced docs UX: improved code-example UI with platform/framework selectors, theme-aware highlighted code blocks, image zoom, and a centralized code-sample registry.

@vercel
Copy link

vercel bot commented Oct 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Oct 20, 2025 4:42pm
stack-dashboard Ready Ready Preview Comment Oct 20, 2025 4:42pm
stack-demo Ready Ready Preview Comment Oct 20, 2025 4:42pm
stack-docs Ready Ready Preview 15 resolved Oct 20, 2025 4:42pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 1, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Replaces platform-specific docs generation and routing with a section-based docs system, removes platform persistence and selector UI, centralizes code-example data and adds new code-block components and layout wrappers, refactors search/TOC/header/sidebar to be platform-agnostic, and adds ~100+ MDX pages & metadata.

Changes

Cohort / File(s) Summary
Docs generation & config removed
docs/docs-platform.yml, docs/scripts/generate-docs.js, docs/scripts/generate-platform-navigation.js, docs/.gitignore, docs/package.json, package.json
Remove platform-specific docs generation/config and scripts; update package scripts to drop generate-docs tasks and adjust docs build/dev flows; update gitignore entries.
Platform utilities & persistence removed
docs/src/lib/platform-utils.ts, docs/src/hooks/use-platform-persistence.ts, docs/src/hooks/use-platform-preference.ts, docs/src/components/platform-redirect.tsx, docs/src/components/layout/root-toggle.tsx, docs/src/components/layouts/platform-aware-header.tsx
Delete platform-detection, platform-URL helpers, platform preference hooks, redirect and UI components enforcing platform-scoped routing.
Section-based docs tree & nav
docs/src/lib/docs-tree.ts, docs/src/lib/navigation-utils.ts, docs/src/components/layouts/shared/section-utils.ts, docs/content/docs/(guides)/meta.json
Add section-resolution and tree-filtering utilities; simplify nav generation to fixed docs links and add DOCS_NAV_PATHS; switch to section-based regex logic.
Code example infrastructure
docs/code-examples/index.ts, docs/code-examples/setup.ts, docs/lib/code-examples.ts
Add centralized code-example registry, typed helpers (getExample, getDocumentExamples) and a large set of multi-platform setup examples.
MDX code block & highlighting refactor
docs/src/components/mdx/platform-codeblock.tsx, docs/src/components/mdx/platform-config.ts, docs/src/components/mdx/base-codeblock.tsx, docs/src/components/mdx/dynamic-code-block.tsx, docs/src/components/mdx/dynamic-code-block-overlay.tsx, docs/src/components/mdx/sdk-components.tsx, docs/templates/sdk/types/api-key.mdx
Introduce PlatformCodeblock (platform/framework selector + persistence), BaseCodeblock (Shiki highlighting + theme handling), overlay refactors; replace old inline highlight logic and adjust templates.
MDX registry & components changes
docs/src/mdx-components.tsx, docs/src/components/mdx/platform-codeblock.tsx, docs/src/components/mdx/platform-config.ts
Register PlatformCodeblock and ImageZoom in MDX components and remove CodeBlocks mapping.
Layout wrappers & shared content layouts
docs/src/components/layouts/api-layout-wrapper.tsx, docs/src/components/layouts/docs-layout-wrapper.tsx, docs/src/components/layouts/shared-content-layout.tsx, docs/src/app/api/layout.tsx, docs/src/app/docs/layout.tsx
Add ApiLayoutWrapper and DocsLayoutWrapper; introduce SharedContentLayout with content-variant sizing and sidebar-collapse awareness; update API/docs layouts to use wrappers.
Docs layout & header refactor
docs/src/components/layouts/docs.tsx, docs/src/components/layouts/docs-header-wrapper.tsx, docs/src/components/layouts/shared-header.tsx
Remove platform-aware routing/branching; simplify sidebar/tree rendering; replace PlatformAwareHeader usage with SharedHeader and a two-row header layout (platform selector removed).
Search / redirect / routing simplifications
docs/src/app/api/search/route.ts, docs/src/app/docs/[[...slug]]/page.tsx
Remove platform tie-breaking from search ranking; add getDefaultDocsRedirectUrl fallback for docs root redirect; simplify API/docs page rendering.
TOC, chat and homepage simplifications
docs/src/components/layout/toc.tsx, docs/src/components/chat/ai-chat.tsx, docs/src/components/homepage/iconHover.tsx, docs/src/components/layouts/sidebar-context.tsx
Ensure TOC tab visibility before scroll, simplify top positioning/height logic for chat/auth panels, remove platform selector from homepage icon hover, and persist sidebar/chat/TOC defaults with responsive fallback.
API sidebar & panels styling/layout tweaks
docs/src/components/layouts/api/api-sidebar.tsx, docs/src/components/api/auth-panel.tsx, docs/src/components/api/enhanced-api-page.tsx
Restructure API sidebar rendering (remove “Back to docs” link), adjust auth panel and enhanced API page wrapper classNames and sizing.
UI utilities & components added
docs/src/components/ui/tabs.tsx, docs/src/lib/merge-refs.ts, docs/src/components/image-zoom.css
Add tab root id support/data-attributes, introduce mergeRefs utility, and add ImageZoom stylesheet.
MDX/content additions
docs/content/docs/** (many files; examples include)
docs/content/docs/(guides)/concepts/api-keys.mdx, docs/content/docs/(guides)/concepts/auth-providers/*.mdx, docs/content/docs/components/*.mdx, docs/content/docs/sdk/types/*.mdx
Add ~100+ new MDX documentation pages and meta.json files covering guides, auth providers, components, SDK reference types/objects, getting-started, customization, examples, and FAQ.
MDX & dynamic code behavior updates
docs/src/components/mdx/dynamic-code-block.tsx, docs/src/components/mdx/dynamic-code-block-overlay.tsx, docs/src/components/mdx/sdk-components.tsx, docs/src/components/mdx/base-codeblock.tsx, docs/src/components/mdx/platform-codeblock.tsx
Change DynamicCodeblock to overlay-first behavior; wire clickable code blocks to BaseCodeblock; add theme-aware highlighting and overlay presentation.
Misc small changes
docs/src/components/layouts/docs/shared.tsx, docs/src/app/global.css, docs/src/app/layout.tsx, docs/next.config.mjs, docs/src/app/(home)/page.tsx
Inline Option type, adjust global CSS variables/layout rules, suppress hydration warnings on body, add non-permanent redirect //docs/overview, and simplify home page to early-return (redirect fallback).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser
    participant DocsApp as Docs App
    participant Tree as PageTree Filter
    participant Sidebar as Sidebar Renderer

    User->>Browser: Navigate to /docs/guides/setup
    Browser->>DocsApp: Request page
    DocsApp->>Tree: resolveDocsSection("/docs/guides/setup")
    Tree-->>DocsApp: section = "guides"
    DocsApp->>Tree: filterTreeForSection(pageTree, "guides")
    Tree->>Tree: filterNode() - keep guides pages & prune separators
    Tree->>Tree: flattenRootChildren()
    Tree-->>DocsApp: sectionTree (guides only)
    DocsApp->>Sidebar: Render sidebar with sectionTree
    Sidebar-->>Browser: Sidebar navigation (guides content)
    Browser-->>User: Display page with guides sidebar
Loading
sequenceDiagram
    participant User
    participant Component as PlatformCodeblock
    participant State as Global State
    participant Storage as SessionStorage
    participant Shiki as Shiki Highlighter

    User->>Component: Mount PlatformCodeblock
    Component->>Storage: Load stored platform/framework
    Storage-->>Component: Stored preferences
    Component->>State: Initialize global state
    State-->>Component: Listeners registered
    Component->>Component: Build platforms structure & render dropdown
    User->>Component: Select platform
    Component->>State: Update global platform
    State->>Storage: Persist platform
    Component->>Component: Filter frameworks for platform
    User->>Component: Select framework
    Component->>Shiki: Highlight code for framework
    Shiki-->>Component: Highlighted HTML
    Component-->>User: Display code block with variant tabs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

"🐇
I hopped through trees and trimmed the rails,
I swapped the platforms for tidy trails.
Code blocks now sparkle, examples aligned,
Docs hum a tune — neat, clear, and kind.
Hop on, dear reader, the guides are redesigned!"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "[Docs][Util][Content] - refactor docs to single source" clearly and specifically describes the main change in the pull request. The raw summary confirms this PR refactors the documentation structure by removing platform-specific routing and consolidating docs into a single /content folder, eliminating the need for platform-specific doc generation. The title is concise, directly related to the primary objective, and avoids generic phrasing. A developer scanning the commit history would immediately understand that this is a documentation architecture refactor focused on centralizing content into a single source rather than maintaining multiple platform-specific versions.
Description Check ✅ Passed The pull request description is comprehensive and well-structured despite the repository's minimal template. It provides a clear executive summary stating that platform selection is removed, docs are consolidated into /content, and only API docs are generated. The author includes a high-level summary explaining the refactoring goals, estimated review time, a detailed file review order, and relevant links. The description is specific, on-topic, and provides substantial context about what was changed and why. While the template itself is minimal (only requiring a CONTRIBUTING.md acknowledgment), the author has gone well beyond template requirements to document the changes thoroughly.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ellipsis-dev
Copy link
Contributor

ellipsis-dev bot commented Oct 1, 2025

⚠️ This PR is too big for Ellipsis, but support for larger PRs is coming soon. If you want us to prioritize this feature, let us know at help@ellipsis.dev


Generated with ❤️ by ellipsis.dev

Copy link

@recurseml recurseml bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review by RecurseML

🔍 Review performed on f5a3a99..47ccd2b

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (3)

docs/src/components/homepage/iconHover.tsx
docs/src/app/docs/[[...slug]]/page.tsx
docs/src/components/layouts/docs-header-wrapper.tsx

⏭️ Files skipped (102)
  Locations  
docs/.gitignore
docs/content/docs/(guides)/concepts/api-keys.mdx
docs/content/docs/(guides)/concepts/auth-providers/apple.mdx
docs/content/docs/(guides)/concepts/auth-providers/bitbucket.mdx
docs/content/docs/(guides)/concepts/auth-providers/discord.mdx
docs/content/docs/(guides)/concepts/auth-providers/facebook.mdx
docs/content/docs/(guides)/concepts/auth-providers/github.mdx
docs/content/docs/(guides)/concepts/auth-providers/gitlab.mdx
docs/content/docs/(guides)/concepts/auth-providers/google.mdx
docs/content/docs/(guides)/concepts/auth-providers/index.mdx
docs/content/docs/(guides)/concepts/auth-providers/linkedin.mdx
docs/content/docs/(guides)/concepts/auth-providers/meta.json
docs/content/docs/(guides)/concepts/auth-providers/microsoft.mdx
docs/content/docs/(guides)/concepts/auth-providers/passkey.mdx
docs/content/docs/(guides)/concepts/auth-providers/spotify.mdx
docs/content/docs/(guides)/concepts/auth-providers/twitch.mdx
docs/content/docs/(guides)/concepts/auth-providers/two-factor-auth.mdx
docs/content/docs/(guides)/concepts/auth-providers/x-twitter.mdx
docs/content/docs/(guides)/concepts/backend-integration.mdx
docs/content/docs/(guides)/concepts/custom-user-data.mdx
docs/content/docs/(guides)/concepts/emails.mdx
docs/content/docs/(guides)/concepts/jwt.mdx
docs/content/docs/(guides)/concepts/oauth.mdx
docs/content/docs/(guides)/concepts/orgs-and-teams.mdx
docs/content/docs/(guides)/concepts/permissions.mdx
docs/content/docs/(guides)/concepts/stack-app.mdx
docs/content/docs/(guides)/concepts/team-selection.mdx
docs/content/docs/(guides)/concepts/user-onboarding.mdx
docs/content/docs/(guides)/concepts/webhooks.mdx
docs/content/docs/(guides)/customization/custom-pages.mdx
docs/content/docs/(guides)/customization/custom-styles.mdx
docs/content/docs/(guides)/customization/dark-mode.mdx
docs/content/docs/(guides)/customization/internationalization.mdx
docs/content/docs/(guides)/customization/page-examples/forgot-password.mdx
docs/content/docs/(guides)/customization/page-examples/index.mdx
docs/content/docs/(guides)/customization/page-examples/meta.json
docs/content/docs/(guides)/customization/page-examples/password-reset.mdx
docs/content/docs/(guides)/customization/page-examples/sign-in.mdx
docs/content/docs/(guides)/customization/page-examples/sign-up.mdx
docs/content/docs/(guides)/faq.mdx
docs/content/docs/(guides)/getting-started/components.mdx
docs/content/docs/(guides)/getting-started/example-pages.mdx
docs/content/docs/(guides)/getting-started/production.mdx
docs/content/docs/(guides)/getting-started/setup.mdx
docs/content/docs/(guides)/getting-started/users.mdx
docs/content/docs/(guides)/meta.json
docs/content/docs/(guides)/others/cli-authentication.mdx
docs/content/docs/(guides)/others/convex.mdx
docs/content/docs/(guides)/others/self-host.mdx
docs/content/docs/(guides)/others/supabase.mdx
docs/content/docs/(guides)/overview.mdx
docs/content/docs/(guides)/rest-api/overview.mdx
docs/content/docs/components/account-settings.mdx
docs/content/docs/components/credential-sign-in.mdx
docs/content/docs/components/credential-sign-up.mdx
docs/content/docs/components/forgot-password.mdx
docs/content/docs/components/index.mdx
docs/content/docs/components/magic-link-sign-in.mdx
docs/content/docs/components/meta.json
docs/content/docs/components/oauth-button-group.mdx
docs/content/docs/components/oauth-button.mdx
docs/content/docs/components/password-reset.mdx
docs/content/docs/components/selected-team-switcher.mdx
docs/content/docs/components/sign-in.mdx
docs/content/docs/components/sign-up.mdx
docs/content/docs/components/stack-handler.mdx
docs/content/docs/components/stack-provider.mdx
docs/content/docs/components/stack-theme.mdx
docs/content/docs/components/user-button.mdx
docs/content/docs/sdk/hooks/use-stack-app.mdx
docs/content/docs/sdk/hooks/use-user.mdx
docs/content/docs/sdk/index.mdx
docs/content/docs/sdk/meta.json
docs/content/docs/sdk/objects/stack-app.mdx
docs/content/docs/sdk/overview-new.mdx
docs/content/docs/sdk/types/api-key.mdx
docs/content/docs/sdk/types/connected-account.mdx
docs/content/docs/sdk/types/contact-channel.mdx
docs/content/docs/sdk/types/email.mdx
docs/content/docs/sdk/types/project.mdx
docs/content/docs/sdk/types/team-permission.mdx
docs/content/docs/sdk/types/team-profile.mdx
docs/content/docs/sdk/types/team-user.mdx
docs/content/docs/sdk/types/team.mdx
docs/content/docs/sdk/types/user.mdx
docs/docs-platform.yml
docs/package.json
docs/scripts/generate-docs.js
docs/scripts/generate-platform-navigation.js
docs/src/app/api/search/route.ts
docs/src/components/layout/custom-search-dialog.tsx
docs/src/components/layout/root-toggle.tsx
docs/src/components/layouts/docs-layout-router.tsx
docs/src/components/layouts/docs.tsx
docs/src/components/layouts/platform-aware-header.tsx
docs/src/components/layouts/shared/section-utils.ts
docs/src/components/platform-redirect.tsx
docs/src/components/sdk/overview.tsx
docs/src/hooks/use-platform-persistence.ts
docs/src/hooks/use-platform-preference.ts
docs/src/lib/platform-utils.ts
package.json

vercel[bot]

This comment was marked as outdated.

vercel[bot]

This comment was marked as outdated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 56

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
docs/src/app/api/search/route.ts (1)

164-165: Fix relative path construction.

The relative path ./content/docs/ is fragile and depends on the current working directory at runtime. In production or different environments, this may not resolve correctly.

Apply this diff to use an absolute path:

-        const relativePath = url.replace('/docs/', './content/docs/') + '.mdx';
-        const fullPath = path.resolve(relativePath);
+        const contentDir = path.join(process.cwd(), 'content', 'docs');
+        const relativePath = url.replace('/docs/', '') + '.mdx';
+        const fullPath = path.join(contentDir, relativePath);
docs/src/components/layout/custom-search-dialog.tsx (1)

90-133: Fix timer ref typing to unblock the docs build.

This client component assigns the browser’s setTimeout (a number) into a ref typed as NodeJS.Timeout, triggering the Type 'number' is not assignable to type 'Timeout' compile error that’s causing pnpm run build docs to fail. Type the ref using ReturnType<typeof setTimeout> and guard against null so both browser and Node signatures are supported.

-  const searchTimeoutRef = useRef<NodeJS.Timeout>();
+  const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@
-    if (searchTimeoutRef.current) {
+    if (searchTimeoutRef.current !== null) {
       clearTimeout(searchTimeoutRef.current);
     }
@@
     return () => {
-      if (searchTimeoutRef.current) {
+      if (searchTimeoutRef.current !== null) {
         clearTimeout(searchTimeoutRef.current);
+        searchTimeoutRef.current = null;
       }
     };
docs/content/docs/(guides)/getting-started/example-pages.mdx (1)

1-456: Pipeline failure may be due to missing Tabs/TabsContent components.

The document uses <Tabs>, <TabsList>, <TabsTrigger>, and <TabsContent> components extensively throughout but doesn't import them. These components need to be available globally in the MDX context or imported explicitly.

Verify that these components are properly configured in your MDX setup:

#!/bin/bash
# Description: Check if Tabs components are globally available in MDX configuration

# Search for MDX component configuration
rg -n "Tabs|TabsContent|TabsList|TabsTrigger" --type=ts --type=tsx -g "**/mdx-components.*" -g "**/source.config.*" -C3

# Check fumadocs configuration
fd -e ts -e tsx -e js "source.config" --exec cat {}
docs/content/docs/sdk/types/connected-account.mdx (1)

1-6: Complete the ConnectedAccount documentation before merging.

This file only contains placeholder content. Publishing incomplete documentation provides no value to users and could indicate that the documentation refactoring is incomplete.

Would you like me to help draft the actual ConnectedAccount documentation content? I can generate documentation based on the typical structure for SDK type references.

docs/content/docs/sdk/types/user.mdx (1)

1573-1578: Critical syntax error: missing closing backticks.

The code block at the end of the file is missing its closing backticks (````), which will cause MDX parsing to fail and break the build. This likely explains the pipeline failure.

Apply this fix:

 <ClickableTableOfContents code={`type CurrentServerUser =
   // Inherits all functionality from CurrentUser
   & CurrentUser  //$stack-link-to:#currentuser
   // Inherits all functionality from ServerUser
-  & ServerUser;  //$stack-link-to:#serveruser`} />
+  & ServerUser;  //$stack-link-to:#serveruser`} />
+```
docs/content/docs/(guides)/getting-started/platform-codeblock-example.mdx (1)

598-614: Critical syntax error: missing closing backticks.

The markdown code block starting at "## Usage" is missing its closing backticks (```), which will cause MDX parsing to fail.

Apply this fix:

 - Adapt to light/dark theme changes
 - Persist selections as users switch between platforms and frameworks
+```
docs/content/docs/sdk/types/team.mdx (1)

1-691: Critical syntax error: missing closing backticks at end of file.

The code block at line 691 is missing its closing backticks (````), which will cause MDX parsing to fail and contribute to the build failure.

Apply this fix:

   </MethodLayout>
 </CollapsibleTypesSection>
+```
docs/content/docs/(guides)/getting-started/users.mdx (1)

1-254: Critical syntax error: missing closing backticks at end of file.

The code block at line 254 is missing its closing backticks (````), which will cause MDX parsing to fail and contribute to the build failure.

Apply this fix:

 For more examples on how to use the `User` object, check the [the SDK documentation](../sdk/types/user.mdx).
 
 ## Next steps
 
 In the next guide, we will show you how to put [your application into production](./production.mdx).
+```
🧹 Nitpick comments (33)
docs/src/app/docs/[[...slug]]/page.tsx (1)

14-25: Consider performance implications of calling source.getPages() on every redirect.

The helper function calls source.getPages() on every request to /docs without a slug. If this isn't internally cached by fumadocs, it could impact performance. Consider computing this value once at build time or memoizing the result.

You could cache the result:

+let cachedDefaultUrl: string | null | undefined;
+
 function getDefaultDocsRedirectUrl(): string | null {
+  if (cachedDefaultUrl !== undefined) {
+    return cachedDefaultUrl;
+  }
+
   const pages = source.getPages();
   // Prefer an overview page if one exists without platform prefix
   const overviewPage = pages.find(page => page.url === '/docs/overview');
   if (overviewPage) {
-    return overviewPage.url;
+    cachedDefaultUrl = overviewPage.url;
+    return cachedDefaultUrl;
   }
 
   // Fall back to the first docs page in the collection
   const firstDocsPage = pages.find(page => page.url.startsWith('/docs/'));
-  return firstDocsPage?.url ?? null;
+  cachedDefaultUrl = firstDocsPage?.url ?? null;
+  return cachedDefaultUrl;
 }
docs/src/components/layouts/docs-header-wrapper.tsx (3)

183-229: Improve key generation for recursive tree items.

Lines 205 and 218 use child.type === 'page' ? child.url : index for keys. Falling back to array index for non-page items (folders, separators) can cause React reconciliation issues when the tree structure changes.

Consider generating stable keys for all node types:

-            <MobilePageTreeItem key={child.type === 'page' ? child.url : index} item={child} />
+            <MobilePageTreeItem key={child.type === 'page' ? child.url : (child.type === 'folder' ? `folder-${child.name}-${index}` : `sep-${index}`)} item={child} />

Or extract a helper function to generate consistent keys:

function getNodeKey(node: PageTree.Node, index: number): string {
  if (node.type === 'page') return node.url;
  if (node.type === 'folder') return `folder-${node.name}-${index}`;
  return `separator-${node.name || index}`;
}

Then use: key={getNodeKey(child, index)}


231-241: Same key generation issue as MobilePageTreeItem.

Line 237 has the same fallback-to-index key pattern. Consider applying the same stable key generation strategy suggested for MobilePageTreeItem.


251-279: Memoize docsSection to prevent unnecessary recalculations.

Line 254 computes docsSection on every render. Since it only depends on pathname and is used in the sectionTree memoization (line 255), wrapping it with useMemo would prevent unnecessary re-filtering of the tree when other props change.

Apply this diff:

  const navLinks = useMemo(() => generateNavLinks(), []);
- const docsSection = resolveDocsSection(pathname);
+ const docsSection = useMemo(() => resolveDocsSection(pathname), [pathname]);
  const sectionTree = useMemo(() => (pageTree ? filterTreeForSection(pageTree, docsSection) : undefined), [pageTree, docsSection]);
docs/src/components/layouts/shared/section-utils.ts (2)

8-10: Replace RegExp constructor with literal regex.

Creating a RegExp via constructor on every call is less efficient than a literal regex. Since PLATFORM_PREFIX is a compile-time constant, compile the entire pattern once.

+const SDK_SECTION_REGEX = /^\/docs\/(?:[a-z-]+\/)?sdk(?:\/.*)?$/;
+
 export function isInSdkSection(pathname: string): boolean {
-  return new RegExp(`^/docs/${PLATFORM_PREFIX}sdk(?:/.*)?$`).test(pathname);
+  return SDK_SECTION_REGEX.test(pathname);
 }

This also addresses the static analysis ReDoS warning by eliminating runtime regex construction.


12-14: Replace RegExp constructors with literal regexes.

Same issue as isInSdkSection: use literal regexes for better performance.

+const COMPONENTS_SECTION_REGEX = /^\/docs\/(?:[a-z-]+\/)?components(?:\/.*)?$/;
+const CUSTOMIZATION_SECTION_REGEX = /^\/docs\/(?:[a-z-]+\/)?customization(?:\/.*)?$/;
+
 export function isInComponentsSection(pathname: string): boolean {
-  return new RegExp(`^/docs/${PLATFORM_PREFIX}components(?:/.*)?$`).test(pathname);
+  return COMPONENTS_SECTION_REGEX.test(pathname);
 }

 export function isInCustomizationSection(pathname: string): boolean {
-  return new RegExp(`^/docs/${PLATFORM_PREFIX}customization(?:/.*)?$`).test(pathname);
+  return CUSTOMIZATION_SECTION_REGEX.test(pathname);
 }

Also applies to: 20-22

docs/src/components/layouts/docs.tsx (1)

344-344: Consider renaming platformColor to reflect its new purpose.

The variable name platformColor is now misleading since platform-specific logic has been removed. Consider renaming to accentColor, primaryColor, or similar to better reflect that it's now a static theme color.

-const platformColor = 'rgb(59, 130, 246)';
+const accentColor = 'rgb(59, 130, 246)';

Then update all references to use the new name.

Also applies to: 444-444, 600-600

docs/.gitignore (1)

8-8: Remove the ignore rule for src/lib/platform-navigation.ts from docs/.gitignore. No file or references named “platform-navigation.ts” exist in the repo, so the entry is now stale.

docs/src/components/layouts/docs-layout-router.tsx (1)

42-43: Compute section and sectionTree lazily to skip filtering for API docs.

Move

const section = resolveDocsSection(pathname);
const sectionTree = useMemo(() => filterTreeForSection(props.tree, section), [props.tree, section]);

below the if (isInApiSection…) check (i.e. inside the non-API branches) so you don’t run filterTreeForSection when rendering the API layout.

docs/content/docs/(guides)/getting-started/production.mdx (1)

45-47: Brand casing: Gitlab → GitLab.

Align with official brand spelling.

-<TabsTrigger value="gitlab">Gitlab</TabsTrigger>
+<TabsTrigger value="gitlab">GitLab</TabsTrigger>
...
-    <TabsContent value="gitlab">
+    <TabsContent value="gitlab">
       [GitLab OAuth Setup Guide](https://docs.gitlab.com/ee/integration/oauth_provider.html)

Also applies to: 96-101

docs/src/components/mdx/simple-platform-example.mdx (1)

90-98: Trailing comment inside object literal is fine, but consider block style for consistency.

Not a blocker; consider /* Override default */ for consistency with MDX/JSX comment style.

docs/content/docs/(guides)/concepts/auth-providers/twitch.mdx (1)

17-31: Add local redirect URI example for dev.
Including a localhost redirect reduces first‑run friction.

-  5. Under **OAuth Redirect URLs**, add `https://api.stack-auth.com/api/v1/auth/oauth/callback/twitch`
+  5. Under **OAuth Redirect URLs**, add:
+     - `https://api.stack-auth.com/api/v1/auth/oauth/callback/twitch` (production)
+     - `http://localhost:3000/api/v1/auth/oauth/callback/twitch` (development; adjust port as needed)
docs/content/docs/(guides)/overview.mdx (2)

7-8: Use extensionless docs link.
Avoid .mdx in routes to prevent 404s.

-You can get started in five minutes with our [setup guide](./getting-started/setup.mdx), or jump straight into the documentation.
+You can get started in five minutes with our [setup guide](./getting-started/setup), or jump straight into the documentation.

17-34: Remove deprecated IF_PLATFORM markers.
Platform gating is gone; keep both cards visible and drop the comment noise.

-  {/* IF_PLATFORM: react-like */}
   <Card 
     title="Components"
     icon="fa-solid fa-puzzle"
     href="./components"
   >
     Use our pre-built React components, or create your own
   </Card>
-  {/* END_PLATFORM */}
-  {/* IF_PLATFORM: js-like */}
   <Card 
     title="SDK Reference"
     icon="fa-regular fa-file-lines" 
     href="./sdk"
   >
     Learn how to use Stack Auth's SDK
   </Card>
-  {/* END_PLATFORM */}
docs/content/docs/(guides)/concepts/auth-providers/spotify.mdx (1)

22-35: Add dev redirect URI for completeness.
Parity with other guides and smoother local testing.

-  5. Under **Redirect URI**, add `https://api.stack-auth.com/api/v1/auth/oauth/callback/spotify`
+  5. Under **Redirect URI**, add:
+     - `https://api.stack-auth.com/api/v1/auth/oauth/callback/spotify` (production)
+     - `http://localhost:3000/api/v1/auth/oauth/callback/spotify` (development; adjust port as needed)
docs/content/docs/(guides)/concepts/auth-providers/gitlab.mdx (1)

28-32: Self‑hosted instance field: add label/example.

Add the expected field label and an example (e.g., https://gitlab.example.com) to reduce confusion.

docs/content/docs/components/sign-in.mdx (1)

1-4: Frontmatter: full: true behavior.

Verify the docs theme respects full: true for layout; otherwise drop it to avoid layout mismatch.

docs/content/docs/(guides)/customization/page-examples/index.mdx (1)

10-15: Add direct links to the example pages for fast navigation.

Listing the child pages prevents orphaning and improves crawl/search relevance. Suggest linking to sign-in, sign-up, forgot-password, and password-reset here.

 Browse the examples to learn how to:
 
 - Create custom sign-in pages
 - Build custom sign-up forms  
 - Implement password reset flows
 - Handle forgotten password scenarios
+
+## Examples
+
+- [Sign-in](./sign-in)
+- [Sign-up](./sign-up)
+- [Forgot password](./forgot-password)
+- [Password reset](./password-reset)
docs/content/docs/sdk/types/team-profile.mdx (1)

25-37: Clarify nullability semantics for profile fields.

State whether empty strings are ever returned vs null, and note size/format constraints for profileImageUrl to reduce ambiguity for integrators.

-      The display name of the user within the team context as a `string` or `null` if no display name is set.
+      The display name of the user within the team context as a `string`, or `null` if unset.
+      Note: empty string (`""`) is not returned by the API — prefer `null` checks. Max length: 128 chars. [If different, update here.]
docs/content/docs/sdk/types/project.mdx (1)

57-76: Document config default values and server-only fields.

Add defaults and whether these flags are server-only or exposed to clients. Reduces guesswork and aligns SDK expectations.

-      <ParamField path="signUpEnabled" type="boolean">
-        Indicates if sign-up is enabled for the project.
+      <ParamField path="signUpEnabled" type="boolean" default={true}>
+        Indicates if sign-up is enabled for the project. Default: `true`.
       </ParamField>
@@
-      <ParamField path="credentialEnabled" type="boolean">
-        Specifies if credential-based authentication is enabled for the project.
+      <ParamField path="credentialEnabled" type="boolean" default={true}>
+        Specifies if credential-based authentication is enabled. Default: `true`.
       </ParamField>
@@
-      <ParamField path="magicLinkEnabled" type="boolean">
-        States whether magic link authentication is enabled for the project.
+      <ParamField path="magicLinkEnabled" type="boolean" default={false}>
+        Whether magic link authentication is enabled. Default: `false`.
       </ParamField>
@@
-      <ParamField path="clientTeamCreationEnabled" type="boolean">
-        Determines if client-side team creation is permitted within the project.
+      <ParamField path="clientTeamCreationEnabled" type="boolean" default={false}>
+        If client-side team creation is permitted. Default: `false`. Server-managed in admin UI.
       </ParamField>
@@
-      <ParamField path="clientUserDeletionEnabled" type="boolean">
-        Indicates if client-side user deletion is enabled for the project.
+      <ParamField path="clientUserDeletionEnabled" type="boolean" default={false}>
+        If client-side user deletion is enabled. Default: `false`. Server-managed in admin UI.
       </ParamField>
docs/content/docs/(guides)/concepts/auth-providers/google.mdx (1)

24-27: Add local development redirect URI and environment variable names.

Improves time-to-success and parity across environments.

-    7. Under **Authorized redirect URIs**, add `https://api.stack-auth.com/api/v1/auth/oauth/callback/google`
+    7. Under **Authorized redirect URIs**, add:
+       - Production: `https://api.stack-auth.com/api/v1/auth/oauth/callback/google`
+       - Local dev (example): `http://localhost:3000/api/v1/auth/oauth/callback/google`
@@
-    9. Save the **Client ID** and **Client Secret** that are displayed.
+    9. Save the **Client ID** and **Client Secret** that are displayed (e.g., `STACK_GOOGLE_CLIENT_ID`, `STACK_GOOGLE_CLIENT_SECRET`).
-    3. Set the **Client ID** and **Client Secret** you obtained from Google Cloud Console earlier.
+    3. Set the **Client ID** and **Client Secret** you obtained from Google Cloud Console earlier.
+    4. (Optional) Restrict scopes as needed; default `openid email profile` works for basic sign-in.
docs/content/docs/sdk/types/team-user.mdx (3)

16-19: Make platform note explicit and rearrange calls for broader applicability.

List generic API first, then framework-specific hooks, and clarify that the hook is only available in React-like environments.

-It is usually obtained by calling 
-`team.useUsers()` or {/* THIS_LINE_PLATFORM react-like */}
-`team.listUsers()` on a [`Team` object](../types/team.mdx#team).
+You can obtain users via:
+- `team.listUsers()` on a [`Team` object](../types/team.mdx#team).
+- `team.useUsers()` {/* React-only hook */} in React-like environments for subscription-driven UIs.

67-74: Unify link target style for ServerUser across page and ToC.

In ToC you use ./user#serveruser, elsewhere ../types/user.mdx#serveruser. Prefer one pattern to avoid mismatches in link resolvers.

-  & ServerUser //$stack-link-to:./user#serveruser
+  & ServerUser //$stack-link-to:../types/user#serveruser

1-12: Update broken-link check to resolve links per file directory
The current script prepends docs/content globally, so ../../components/stack-provider.mdx is flagged “missing” even though it exists at docs/content/docs/components/stack-provider.mdx. Adjust your validation to resolve each relative link against its MDX file’s directory so that ../../components/stack-provider.mdx in docs/content/docs/sdk/... correctly maps to the actual location.

docs/content/docs/components/index.mdx (1)

118-118: Unindent the “Teams & Organizations” heading.

Four leading spaces turn this heading into a code block, so it vanishes from the document outline. Align it flush-left (matching the other headings) to get proper heading styling and navigation anchors.

-    ## Teams & Organizations
+  ## Teams & Organizations
docs/src/components/mdx/platform-config.ts (1)

2-9: Consider using ES6 Map for better type safety and adherence to coding guidelines.

The nested object structure for PlatformConfig doesn't align with the coding guideline to "prefer ES6 Map over Record when representing key–value collections." While objects work here, using Maps would provide better type safety, built-in iteration, and clearer semantics for dynamic key-value lookups.

As per coding guidelines.

Consider refactoring to:

export type PlatformConfig = Map<string, Map<string, {
  defaultFilename?: string,
  language: string,
}>>

This would require updating the initialization and helper functions, but provides better runtime guarantees and aligns with the guideline.

docs/content/docs/(guides)/getting-started/users.mdx (1)

19-19: Use JSX-style comment for consistency.

Line 19 uses a plain comment inside JSX content, which may cause parsing issues depending on the MDX version. For safety and consistency with line 35 (which uses a similar comment in a JSX context), use a JSX-style comment block.

Apply this fix:

 `user.listTeams()` functions. The created team will then inherit the permissions of that user; for example, the `team.update(...)` function can only succeed if the user is allowed to make updates to the team.
+{/* or: user.useTeams() */}
-`user.listTeams()` functions. The created team will then inherit the permissions of that user; for example, the `team.update(...)` function can only succeed if the user is allowed to make updates to the team.

Or if the comment is intended as inline documentation, keep it but ensure it's properly formatted:

-`user.useTeams()` or {/* THIS_LINE_PLATFORM react-like */}
+{/* THIS_LINE_PLATFORM react-like */}
+`user.useTeams()` or
 `user.listTeams()` functions. The created team will then inherit the permissions of that user; for example, the `team.update(...)` function can only succeed if the user is allowed to make updates to the team.
docs/src/components/mdx/platform-codeblock.tsx (5)

25-31: Avoid listener map leaks; delete empty entries.

remove*Listener leaves empty arrays in the Map; over time this grows across mounts.

Apply this diff:

 function removePlatformListener(id: string, listener: PlatformChangeListener): void {
   const list = platformListeners.get(id) ?? [];
-  platformListeners.set(
-    id,
-    list.filter((item) => item !== listener),
-  );
+  const next = list.filter((item) => item !== listener);
+  if (next.length) platformListeners.set(id, next);
+  else platformListeners.delete(id);
 }
@@
 function removeFrameworkListener(id: string, listener: FrameworkChangeListener): void {
   const list = frameworkListeners.get(id) ?? [];
-  frameworkListeners.set(
-    id,
-    list.filter((item) => item !== listener),
-  );
+  const next = list.filter((item) => item !== listener);
+  if (next.length) frameworkListeners.set(id, next);
+  else frameworkListeners.delete(id);
 }

Also applies to: 39-45


181-182: Use slice instead of deprecated substr.

Minor nit; avoid deprecated substr.

Apply this diff:

-  const [componentId] = useState(() => Math.random().toString(36).substr(2, 9));
+  const [componentId] = useState(() => Math.random().toString(36).slice(2, 11));

280-334: Guard async highlight against races; only set state if latest.

Rapid changes can set stale highlightedCode. Add a cancellation flag.

Apply this diff:

-  useEffect(() => {
+  useEffect(() => {
+    let cancelled = false;
@@
-        const sanitized = DOMPurify.sanitize(html, {
+        const sanitized = DOMPurify.sanitize(html, {
           KEEP_CONTENT: true,
         });
-        setHighlightedCode(sanitized);
+        if (!cancelled) setHighlightedCode(sanitized);
@@
-        setHighlightedCode(`<pre><code>${escapeHtml(currentCodeConfig.code)}</code></pre>`);
+        if (!cancelled) setHighlightedCode(`<pre><code>${escapeHtml(currentCodeConfig.code)}</code></pre>`);
@@
-    observer.observe(document.documentElement, {
+    observer.observe(document.documentElement, {
       attributes: true,
       attributeFilter: ['class']
     });
 
-    return () => observer.disconnect();
+    return () => {
+      cancelled = true;
+      observer.disconnect();
+    };
   }, [currentCodeConfig]);

407-411: Handle missing frameworks gracefully in label.

Avoid showing “undefined” when a platform has no frameworks.

Apply this diff:

-                <span>{currentFramework}</span>
+                <span>{currentFramework ?? 'No frameworks'}</span>

70-116: Prefer Map over plain objects for key–value collections.

selectedFrameworks/selectedVariants/globalSelectedFrameworks fit Map per guidelines; objects work but Maps avoid prototype pitfalls and ease deletion.

As per coding guidelines

Also applies to: 171-176

docs/src/components/mdx/simple-platform-codeblock.tsx (1)

56-82: Minor: consider Map for fullPlatforms to align with guidelines.

Objects are fine here, but Map would align with our Map-over-Record guidance and mirror PlatformCodeblock’s listener Maps.

As per coding guidelines

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5a3a99 and eb2293c.

📒 Files selected for processing (107)
  • docs/.gitignore (1 hunks)
  • docs/content/docs/(guides)/concepts/api-keys.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/apple.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/bitbucket.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/discord.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/facebook.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/github.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/gitlab.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/google.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/index.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/linkedin.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/meta.json (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/microsoft.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/passkey.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/spotify.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/twitch.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/two-factor-auth.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/auth-providers/x-twitter.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/backend-integration.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/custom-user-data.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/emails.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/jwt.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/oauth.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/orgs-and-teams.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/permissions.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/stack-app.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/team-selection.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/user-onboarding.mdx (1 hunks)
  • docs/content/docs/(guides)/concepts/webhooks.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/custom-pages.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/custom-styles.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/dark-mode.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/internationalization.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/page-examples/forgot-password.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/page-examples/index.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/page-examples/meta.json (1 hunks)
  • docs/content/docs/(guides)/customization/page-examples/password-reset.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/page-examples/sign-in.mdx (1 hunks)
  • docs/content/docs/(guides)/customization/page-examples/sign-up.mdx (1 hunks)
  • docs/content/docs/(guides)/faq.mdx (1 hunks)
  • docs/content/docs/(guides)/getting-started/components.mdx (1 hunks)
  • docs/content/docs/(guides)/getting-started/example-pages.mdx (1 hunks)
  • docs/content/docs/(guides)/getting-started/platform-codeblock-example.mdx (1 hunks)
  • docs/content/docs/(guides)/getting-started/production.mdx (1 hunks)
  • docs/content/docs/(guides)/getting-started/setup.mdx (1 hunks)
  • docs/content/docs/(guides)/getting-started/users.mdx (1 hunks)
  • docs/content/docs/(guides)/meta.json (1 hunks)
  • docs/content/docs/(guides)/others/cli-authentication.mdx (1 hunks)
  • docs/content/docs/(guides)/others/convex.mdx (1 hunks)
  • docs/content/docs/(guides)/others/self-host.mdx (1 hunks)
  • docs/content/docs/(guides)/others/supabase.mdx (1 hunks)
  • docs/content/docs/(guides)/overview.mdx (1 hunks)
  • docs/content/docs/(guides)/rest-api/overview.mdx (1 hunks)
  • docs/content/docs/components/account-settings.mdx (1 hunks)
  • docs/content/docs/components/credential-sign-in.mdx (1 hunks)
  • docs/content/docs/components/credential-sign-up.mdx (1 hunks)
  • docs/content/docs/components/forgot-password.mdx (1 hunks)
  • docs/content/docs/components/index.mdx (1 hunks)
  • docs/content/docs/components/magic-link-sign-in.mdx (1 hunks)
  • docs/content/docs/components/meta.json (1 hunks)
  • docs/content/docs/components/oauth-button-group.mdx (1 hunks)
  • docs/content/docs/components/oauth-button.mdx (1 hunks)
  • docs/content/docs/components/password-reset.mdx (1 hunks)
  • docs/content/docs/components/selected-team-switcher.mdx (1 hunks)
  • docs/content/docs/components/sign-in.mdx (1 hunks)
  • docs/content/docs/components/sign-up.mdx (1 hunks)
  • docs/content/docs/components/stack-handler.mdx (1 hunks)
  • docs/content/docs/components/stack-provider.mdx (1 hunks)
  • docs/content/docs/components/stack-theme.mdx (1 hunks)
  • docs/content/docs/components/user-button.mdx (1 hunks)
  • docs/content/docs/sdk/hooks/use-stack-app.mdx (1 hunks)
  • docs/content/docs/sdk/hooks/use-user.mdx (1 hunks)
  • docs/content/docs/sdk/index.mdx (1 hunks)
  • docs/content/docs/sdk/meta.json (1 hunks)
  • docs/content/docs/sdk/objects/stack-app.mdx (1 hunks)
  • docs/content/docs/sdk/overview-new.mdx (1 hunks)
  • docs/content/docs/sdk/types/api-key.mdx (1 hunks)
  • docs/content/docs/sdk/types/connected-account.mdx (1 hunks)
  • docs/content/docs/sdk/types/contact-channel.mdx (1 hunks)
  • docs/content/docs/sdk/types/email.mdx (1 hunks)
  • docs/content/docs/sdk/types/project.mdx (1 hunks)
  • docs/content/docs/sdk/types/team-permission.mdx (1 hunks)
  • docs/content/docs/sdk/types/team-profile.mdx (1 hunks)
  • docs/content/docs/sdk/types/team-user.mdx (1 hunks)
  • docs/content/docs/sdk/types/team.mdx (1 hunks)
  • docs/content/docs/sdk/types/user.mdx (1 hunks)
  • docs/docs-platform.yml (0 hunks)
  • docs/package.json (1 hunks)
  • docs/scripts/generate-docs.js (0 hunks)
  • docs/scripts/generate-platform-navigation.js (0 hunks)
  • docs/src/app/api/search/route.ts (2 hunks)
  • docs/src/app/docs/[[...slug]]/page.tsx (1 hunks)
  • docs/src/components/homepage/iconHover.tsx (4 hunks)
  • docs/src/components/layout/custom-search-dialog.tsx (5 hunks)
  • docs/src/components/layout/root-toggle.tsx (0 hunks)
  • docs/src/components/layouts/docs-header-wrapper.tsx (7 hunks)
  • docs/src/components/layouts/docs-layout-router.tsx (3 hunks)
  • docs/src/components/layouts/docs.tsx (13 hunks)
  • docs/src/components/layouts/platform-aware-header.tsx (0 hunks)
  • docs/src/components/layouts/shared/section-utils.ts (1 hunks)
  • docs/src/components/mdx/platform-codeblock.tsx (1 hunks)
  • docs/src/components/mdx/platform-config.ts (1 hunks)
  • docs/src/components/mdx/simple-platform-codeblock.tsx (1 hunks)
  • docs/src/components/mdx/simple-platform-example.mdx (1 hunks)
  • docs/src/components/platform-redirect.tsx (0 hunks)
  • docs/src/components/sdk/overview.tsx (1 hunks)
  • docs/src/hooks/use-platform-persistence.ts (0 hunks)
⛔ Files not processed due to max files limit (6)
  • docs/src/hooks/use-platform-preference.ts
  • docs/src/lib/docs-tree.ts
  • docs/src/lib/navigation-utils.ts
  • docs/src/lib/platform-utils.ts
  • docs/src/mdx-components.tsx
  • package.json
💤 Files with no reviewable changes (7)
  • docs/src/hooks/use-platform-persistence.ts
  • docs/src/components/platform-redirect.tsx
  • docs/docs-platform.yml
  • docs/src/components/layouts/platform-aware-header.tsx
  • docs/scripts/generate-platform-navigation.js
  • docs/scripts/generate-docs.js
  • docs/src/components/layout/root-toggle.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • docs/src/components/mdx/platform-config.ts
  • docs/src/components/sdk/overview.tsx
  • docs/src/components/layouts/docs-header-wrapper.tsx
  • docs/src/components/mdx/simple-platform-codeblock.tsx
  • docs/src/components/layouts/docs-layout-router.tsx
  • docs/src/app/api/search/route.ts
  • docs/src/components/homepage/iconHover.tsx
  • docs/src/components/layouts/shared/section-utils.ts
  • docs/src/components/layouts/docs.tsx
  • docs/src/components/mdx/platform-codeblock.tsx
  • docs/src/app/docs/[[...slug]]/page.tsx
  • docs/src/components/layout/custom-search-dialog.tsx
🧬 Code graph analysis (7)
docs/src/components/mdx/platform-config.ts (1)
docs/src/components/mdx/simple-platform-codeblock.tsx (2)
  • PLATFORM_CONFIG (102-102)
  • DEFAULT_FRAMEWORK_PREFERENCES (102-102)
docs/src/components/layouts/docs-header-wrapper.tsx (3)
docs/src/lib/navigation-utils.ts (1)
  • generateNavLinks (14-37)
docs/src/lib/docs-tree.ts (2)
  • resolveDocsSection (7-17)
  • filterTreeForSection (19-32)
docs/src/components/layouts/shared-header.tsx (1)
  • SharedHeader (224-427)
docs/src/components/mdx/simple-platform-codeblock.tsx (2)
docs/src/components/mdx/platform-codeblock.tsx (2)
  • PlatformCodeblockProps (71-116)
  • PlatformCodeblock (118-527)
docs/src/components/mdx/platform-config.ts (2)
  • getPlatformFrameworkConfig (144-146)
  • DEFAULT_FRAMEWORK_PREFERENCES (149-159)
docs/src/components/layouts/docs-layout-router.tsx (4)
docs/src/components/layouts/docs.tsx (2)
  • DocsLayoutProps (801-813)
  • DocsLayout (815-921)
docs/src/lib/docs-tree.ts (2)
  • resolveDocsSection (7-17)
  • filterTreeForSection (19-32)
docs/src/components/layouts/shared/section-utils.ts (1)
  • isInApiSection (16-18)
docs/src/app/docs/layout.tsx (1)
  • DocsLayout (8-29)
docs/src/components/homepage/iconHover.tsx (2)
docs/src/lib/docs-tree.ts (1)
  • DocsSection (3-3)
docs/src/components/icons.tsx (2)
  • Code (340-343)
  • Zap (345-347)
docs/src/components/mdx/platform-codeblock.tsx (1)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
docs/src/app/docs/[[...slug]]/page.tsx (1)
docs/lib/source.ts (1)
  • source (23-30)
🪛 ast-grep (0.39.5)
docs/src/components/layouts/shared/section-utils.ts

[warning] 8-8: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^/docs/${PLATFORM_PREFIX}sdk(?:/.*)?$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 12-12: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^/docs/${PLATFORM_PREFIX}components(?:/.*)?$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 20-20: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^/docs/${PLATFORM_PREFIX}customization(?:/.*)?$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

docs/src/components/mdx/platform-codeblock.tsx

[warning] 456-456: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
docs/src/components/mdx/platform-codeblock.tsx

[error] 457-457: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🪛 GitHub Actions: Runs E2E API Tests
docs/content/docs/(guides)/customization/page-examples/forgot-password.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/api-keys.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/page-examples/meta.json

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/page-examples/index.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/mdx/platform-config.ts

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/index.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/.gitignore

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/meta.json

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/team-permission.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/meta.json

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/team.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/twitch.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/password-reset.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/google.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/custom-pages.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/overview-new.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/custom-user-data.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/facebook.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/connected-account.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/sdk/overview.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/user.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/sign-in.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/spotify.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/forgot-password.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/bitbucket.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/meta.json

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/apple.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/oauth.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/layouts/docs-header-wrapper.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/permissions.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/account-settings.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/team-selection.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/jwt.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/email.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/team-profile.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/index.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/two-factor-auth.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/index.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/team-user.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/webhooks.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/meta.json

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/getting-started/production.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/mdx/simple-platform-codeblock.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/hooks/use-stack-app.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/page-examples/password-reset.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/layouts/docs-layout-router.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/magic-link-sign-in.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/package.json

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/orgs-and-teams.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/credential-sign-in.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/microsoft.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/contact-channel.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/app/api/search/route.ts

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/stack-theme.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/credential-sign-up.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/mdx/simple-platform-example.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/gitlab.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/stack-handler.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/homepage/iconHover.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/discord.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/overview.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/api-key.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/custom-styles.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/x-twitter.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/objects/stack-app.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/sign-up.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/layouts/shared/section-utils.ts

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/layouts/docs.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/github.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/stack-provider.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/mdx/platform-codeblock.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/getting-started/platform-codeblock-example.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/internationalization.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/selected-team-switcher.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/user-onboarding.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/backend-integration.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/getting-started/users.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/rest-api/overview.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/getting-started/components.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/emails.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/oauth-button.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/getting-started/example-pages.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/faq.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/page-examples/sign-in.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/others/cli-authentication.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/getting-started/setup.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/hooks/use-user.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/others/convex.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/stack-app.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/passkey.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/dark-mode.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/oauth-button-group.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/customization/page-examples/sign-up.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/app/docs/[[...slug]]/page.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/sdk/types/project.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/src/components/layout/custom-search-dialog.tsx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/others/supabase.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/components/user-button.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/others/self-host.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

docs/content/docs/(guides)/concepts/auth-providers/linkedin.mdx

[error] 1-1: Command failed: pnpm run build in docs exited with code 1.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Security Check

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
docs/src/mdx-components.tsx (1)

16-16: Importing a Client Component into the MDX registry is fine; consider lazy import to limit bundle size.

If PlatformCodeblock is rarely used, wrapping it in a dynamic import can keep the default MDX bundle lean. I can sketch a lazy wrapper if desired.

docs/src/components/mdx/platform-codeblock.tsx (6)

183-184: Use slice, not deprecated substr, and prefer crypto.randomUUID when available.

-  const [componentId] = useState(() => Math.random().toString(36).substr(2, 9));
+  const [componentId] = useState(
+    () => (typeof crypto !== 'undefined' && 'randomUUID' in crypto)
+      ? crypto.randomUUID()
+      : Math.random().toString(36).slice(2, 11),
+  );

27-33: Listener cleanup can leak empty arrays; delete keys when empty.

Free the map entry after removing the last listener.

 function removePlatformListener(id: string, listener: PlatformChangeListener): void {
   const list = platformListeners.get(id) ?? [];
-  platformListeners.set(
-    id,
-    list.filter((item) => item !== listener),
-  );
+  const next = list.filter((item) => item !== listener);
+  if (next.length === 0) platformListeners.delete(id);
+  else platformListeners.set(id, next);
 }
 
 function removeFrameworkListener(id: string, listener: FrameworkChangeListener): void {
   const list = frameworkListeners.get(id) ?? [];
-  frameworkListeners.set(
-    id,
-    list.filter((item) => item !== listener),
-  );
+  const next = list.filter((item) => item !== listener);
+  if (next.length === 0) frameworkListeners.delete(id);
+  else frameworkListeners.set(id, next);
 }

Also applies to: 41-47


332-343: Avoid UI flicker after platform selection by updating local state immediately.

Set local state before broadcasting so the Framework view shows the correct list right away.

 const handlePlatformSelect = (platform: string) => {
-  broadcastPlatformChange(platform);
-  // Show framework selection for this platform
-  setDropdownView('framework');
+  setSelectedPlatform(platform);
+  broadcastPlatformChange(platform);
+  setDropdownView('framework'); // now shows the right frameworks
 
   // Auto-select first framework of new platform
   const newPlatformFrameworks = Object.keys(platforms[platform] ?? {});
   if (newPlatformFrameworks.length > 0) {
     const firstFramework = defaultFrameworks[platform] || newPlatformFrameworks[0];
-    broadcastFrameworkChange(platform, firstFramework);
+    setSelectedFrameworks(prev => ({ ...prev, [platform]: firstFramework }));
+    broadcastFrameworkChange(platform, firstFramework);
   }
 };

450-456: dangerouslySetInnerHTML requires sanitization or a lint suppression.

If you adopt the sanitization above, this is resolved. Otherwise, add a documented eslint/biome suppression with justification.


413-437: Add basic a11y: ARIA attributes and roles for the dropdown.

Expose state to AT and use menu semantics.

 <div className="relative" data-dropdown-id={componentId}>
   <button
     onClick={handleDropdownToggle}
+    aria-haspopup="menu"
+    aria-expanded={isDropdownOpen}
+    aria-controls={`${componentId}-menu`}
 ...
 {isDropdownOpen && (
   <div
     className="absolute right-3 z-50 min-w-[160px] rounded-lg border bg-fd-background shadow-lg"
+    id={`${componentId}-menu`}
+    role="menu"
 ...
   {dropdownView === 'platform' ? (
-    <div className="py-1">
+    <div className="py-1" role="menu">
 ...
-              {platformNames.map((platform) => (
+              {platformNames.map((platform) => (
                 <button
                   key={platform}
+                  role="menuitem"
 ...
-    <div className="py-1">
+    <div className="py-1" role="menu">
 ...
-              {currentFrameworks.map((framework) => (
+              {currentFrameworks.map((framework) => (
                 <button
                   key={framework}
+                  role="menuitem"

Also applies to: 461-521


13-20: Consider Map over index signatures for dynamic key–value state.

globalSelectedFrameworks and selectedFrameworks model dictionaries; Map offers clearer intent and safer key handling. Convert only if cost is low; otherwise defer.

As per coding guidelines

Also applies to: 173-175

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb2293c and a009f64.

📒 Files selected for processing (3)
  • docs/src/components/mdx/platform-codeblock.tsx (1 hunks)
  • docs/src/components/mdx/platform-config.ts (1 hunks)
  • docs/src/mdx-components.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/src/components/mdx/platform-config.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • docs/src/components/mdx/platform-codeblock.tsx
  • docs/src/mdx-components.tsx
🧬 Code graph analysis (1)
docs/src/components/mdx/platform-codeblock.tsx (1)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
🪛 ast-grep (0.39.5)
docs/src/components/mdx/platform-codeblock.tsx

[warning] 453-453: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
docs/src/components/mdx/platform-codeblock.tsx

[error] 454-454: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: setup-tests
  • GitHub Check: docker
  • GitHub Check: all-good
  • GitHub Check: Security Check
🔇 Additional comments (2)
docs/src/mdx-components.tsx (1)

72-72: MDX registry addition looks good.

Registration is correct; MDX pages can now use .

docs/src/components/mdx/platform-codeblock.tsx (1)

3-3: Deep import is currently required for runAsynchronously.

runAsynchronously isn’t exposed by @stackframe/stack-shared’s public API—no re-export in src/index.ts or package.json exports—so importing from /dist is necessary until the package adds a root export.

Likely an incorrect or invalid review comment.

@madster456 madster456 changed the title [Docs][Util][Content] [Docs][Util][Content] - refactor docs to single source Oct 1, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/src/components/mdx/dynamic-code-block-overlay.tsx (1)

144-238: Add accessibility support for keyboard and screen readers.

This overlay currently lacks ARIA roles, focus management, and screen-reader announcements.

Ensure:

  • The container uses role="dialog" and aria-modal="true", with a clear label (e.g., aria-labelledby on the header).
  • Focus is trapped inside the overlay while open and returned to the trigger on close.
  • All buttons and controls have logical tab order and tabIndex as needed.
  • Opening and closing are announced to assistive technologies (via aria-live or similar).
docs/src/components/layouts/shared-header.tsx (1)

30-41: Fix section matchers to new section-based routes; avoid “Guides” false-positives

Current regex assumes /docs/{platform}/section. With platform folders removed, “/docs/sdk” and “/docs/components” won’t match; “Guides” may be incorrectly marked active.

-export function isInSdkSection(pathname: string): boolean {
-  const match = pathname.match(/^\/docs\/[^\/]+\/sdk($|\/)/);
-  return Boolean(match);
-}
-export function isInComponentsSection(pathname: string): boolean {
-  const match = pathname.match(/^\/docs\/[^\/]+\/components($|\/)/);
-  return Boolean(match);
-}
+export function isInSdkSection(pathname: string): boolean {
+  // supports both legacy (/docs/{platform}/sdk) and new (/docs/sdk)
+  return /^\/docs\/(?:[^/]+\/)?sdk(?:$|\/)/.test(pathname);
+}
+export function isInComponentsSection(pathname: string): boolean {
+  return /^\/docs\/(?:[^/]+\/)?components(?:$|\/)/.test(pathname);
+}

Also applies to: 63-66

♻️ Duplicate comments (1)
docs/src/components/layouts/docs.tsx (1)

286-287: Avoid index as key in tree renders (same as prior review)

Use a deterministic key for non-page items to stabilize reconciliation. See earlier reviewer note.

-<PageTreeItem key={child.type === 'page' ? child.url : index} item={child} />
+<PageTreeItem key={child.type === 'page' ? child.url : `${child.type}-${typeof child.name === 'string' ? child.name : index}` } item={child} />
-<PageTreeItem key={item.type === 'page' ? item.url : index} item={item} />
+<PageTreeItem key={item.type === 'page' ? item.url : `${item.type}-${typeof item.name === 'string' ? item.name : index}` } item={item} />

Also applies to: 299-300, 321-322

🧹 Nitpick comments (14)
docs/src/components/api/auth-panel.tsx (2)

75-76: Simplify dead conditional and clarify height intent.

Line 75 has a no-op ternary (both branches return 'top-0'), which should be simplified to a constant assignment. Line 76's height calculation also uses nearly identical values ('h-screen' vs 'h-[calc(100vh)]') that behave identically in most viewport scenarios.

Apply this diff to simplify:

-  const topPosition = isHomePage && isScrolled ? 'top-0' : 'top-0';
-  const height = isHomePage && isScrolled ? 'h-screen' : 'h-[calc(100vh)]';
+  const topPosition = 'top-0';
+  const height = 'h-screen';

If the distinction between h-screen and h-[calc(100vh)] is meaningful for handling mobile browser chrome behavior, document the rationale in a comment. Otherwise, consolidate to a single value.


41-72: Consider removing unused state observation.

Given that topPosition is now constant and height has minimal variation, the isHomePage and isScrolled state tracking (and the associated MutationObserver) may no longer serve a purpose. If these states aren't used elsewhere or the height distinction isn't critical, removing this complexity would improve maintainability.

If you decide to keep the distinction, ensure the state is actually necessary. Otherwise, remove the observer setup:

-  const [isHomePage, setIsHomePage] = useState(false);
-  const [isScrolled, setIsScrolled] = useState(false);
-
-  // Detect if we're on homepage and scroll state (same as AIChatDrawer)
-  useEffect(() => {
-    const checkHomePage = () => {
-      setIsHomePage(document.body.classList.contains('home-page'));
-    };
-
-    const checkScrolled = () => {
-      setIsScrolled(document.body.classList.contains('scrolled'));
-    };
-
-    // Initial check
-    checkHomePage();
-    checkScrolled();
-
-    // Set up observers for class changes
-    const observer = new MutationObserver(() => {
-      checkHomePage();
-      checkScrolled();
-    });
-
-    observer.observe(document.body, {
-      attributes: true,
-      attributeFilter: ['class']
-    });
-
-    return () => {
-      observer.disconnect();
-    };
-  }, []);
docs/src/components/layout/toc.tsx (1)

42-73: Consider removing unused state tracking.

Since both topPosition and height now resolve to constant values regardless of isHomePage and isScrolled, the entire state tracking mechanism (including the MutationObserver) is unused and adds unnecessary runtime overhead.

If the simplified positioning is intentional, apply this diff to remove the unused code:

-  // State for tracking homepage and scroll detection (similar to AI Chat)
-  const [isHomePage, setIsHomePage] = useState(false);
-  const [isScrolled, setIsScrolled] = useState(false);
-
-  // Detect if we're on homepage and scroll state
-  useEffect(() => {
-    const checkHomePage = () => {
-      setIsHomePage(document.body.classList.contains('home-page'));
-    };
-
-    const checkScrolled = () => {
-      setIsScrolled(document.body.classList.contains('scrolled'));
-    };
-
-    // Initial check
-    checkHomePage();
-    checkScrolled();
-
-    // Set up observers for class changes
-    const observer = new MutationObserver(() => {
-      checkHomePage();
-      checkScrolled();
-    });
-
-    observer.observe(document.body, {
-      attributes: true,
-      attributeFilter: ['class']
-    });
-
-    return () => {
-      observer.disconnect();
-    };
-  }, []);
-
-  // Calculate position based on homepage and scroll state (same as AI Chat and Auth Panel)
-  const topPosition = 'top-0';
-  const height = 'h-screen';
docs/src/components/mdx/dynamic-code-block-overlay.tsx (1)

242-273: Add aria-label for better accessibility.

The trigger button looks good, but should include an aria-label attribute for screen readers, as title attributes are not consistently announced.

Apply this diff:

     <button
       onClick={onClick}
       className={cn(
         "fixed bottom-6 z-30",
         "flex items-center gap-1.5 px-3 py-2",
         "bg-fd-primary text-fd-primary-foreground",
         "rounded-full shadow-lg",
         "hover:scale-105 active:scale-95",
         "transition-all duration-200",
         "border border-fd-primary/20"
       )}
       style={{
         left: '50%',
         transform: 'translateX(-50%)'
       }}
       title="View Code Example"
+      aria-label="View Code Example"
     >
docs/src/components/layouts/api/api-sidebar.tsx (1)

68-69: Consider ES6 Map over Record for key-value collections.

Multiple locations use Record<string, ...> for runtime key-value collections:

  • Line 68: groups: Record<string, OrganizedGroup>
  • Line 85-92: accordionState: Record<string, boolean>

As per coding guidelines, prefer ES6 Map for dynamic key-value collections as it provides better type safety, avoids prototype pollution, and offers a cleaner API for runtime operations.

Example for accordion state:

-type AccordionContextType = {
-  accordionState: Record<string, boolean>,
-  setAccordionState: (key: string, isOpen: boolean) => void,
-}
+type AccordionContextType = {
+  accordionState: Map<string, boolean>,
+  setAccordionState: (key: string, isOpen: boolean) => void,
+}

 function AccordionProvider({ children }: { children: ReactNode }) {
-  const [accordionState, setAccordionStateInternal] = useState<Record<string, boolean>>({});
+  const [accordionState, setAccordionStateInternal] = useState<Map<string, boolean>>(new Map());

   const setAccordionState = (key: string, isOpen: boolean) => {
-    setAccordionStateInternal(prev => ({ ...prev, [key]: isOpen }));
+    setAccordionStateInternal(prev => new Map(prev).set(key, isOpen));
   };

Based on coding guidelines.

Also applies to: 85-92

docs/src/app/global.css (3)

33-39: Deduplicate transition rules for #nd-docs-layout and #api-main-content

Same transition declared twice; keep one to avoid override churn.

 /* Default transitions for main content */
-#nd-docs-layout {
-  transition: margin-left 300ms ease-out;
-}
-
-#api-main-content {
-  transition: margin-left 300ms ease-out;
-}
+/* (kept above at lines ~33 and ~37) */

Also applies to: 47-53


17-19: Anchor offset likely mismatched with header height on lg screens

[id]{ scroll-margin-top: 7rem } (112px) while header is h-14 (56px) on small and intended ~6.5rem (104px) on lg. Use a CSS var responsive to breakpoints to prevent awkward extra gap.

-:root {
-  --color-fd-background: #fafbfd;
-}
+:root {
+  --color-fd-background: #fafbfd;
+  --nd-header-offset: 3.5rem; /* h-14 */
+}
+.dark {
   --color-fd-background: hsl(0, 0%, 7.04%);
 }
+[id] { scroll-margin-top: var(--nd-header-offset); }
+@media (min-width: 1024px) {
+  :root { --nd-header-offset: 6.5rem; } /* matches lg header height */
+}
-
-[id] {
-  scroll-margin-top: 7rem;
-}

Also applies to: 263-266


145-196: Respect prefers-reduced-motion for heavy, infinite animations

Add a motion-reduced override to cut CPU/GPU cost and improve a11y.

 @keyframes gradient-shift { ... }
 @keyframes gradient-noise { ... }
 .chat-gradient-active::before { ... }
+@media (prefers-reduced-motion: reduce) {
+  .chat-gradient-active::before {
+    animation: none !important;
+  }
+  #nd-docs-layout, #api-main-content, .api-sidebar, body:not(.home-page) {
+    transition: none !important;
+  }
+}
docs/src/components/layouts/shared-header.tsx (3)

73-95: Make className optional on toggle components

Loosen prop typing; avoid forcing callers to pass a string.

-function AIChatToggleButton(props: { className: string }) {
+function AIChatToggleButton({ className = '' }: { className?: string }) {
 ...
-        props.className,
+        className,
 ...
-function TOCToggleButtonInner(props: { className: string }) {
+function TOCToggleButtonInner({ className = '' }: { className?: string }) {
 ...
-          : 'text-fd-muted-foreground hover:text-fd-foreground hover:bg-fd-muted/50',
-        props.className
+          : 'text-fd-muted-foreground hover:text-fd-foreground hover:bg-fd-muted/50',
+        className
 ...
-function TOCToggleButton(props: { className: string }) {
+function TOCToggleButton(props: { className?: string }) {
 ...
-function AuthToggleButton(props: { className: string }) {
+function AuthToggleButton({ className = '' }: { className?: string }) {
 ...
-          : 'text-fd-muted-foreground hover:text-fd-foreground hover:bg-fd-muted/50',
-        props.className
+          : 'text-fd-muted-foreground hover:text-fd-foreground hover:bg-fd-muted/50',
+        className

Also applies to: 101-133, 138-147, 152-183


322-345: Use stable keys for nav items (avoid array index)

Prevents reconciliation glitches when links change.

-  {navLinks.map((link, index) => {
+  {navLinks.map((link) => {
 ...
-    <Link key={index} href={link.href} ...
+    <Link key={link.href ?? link.label} href={link.href} ...
-  {navLinks.map((link, index) => {
+  {navLinks.map((link) => {
 ...
-    <Link key={index} href={link.href} ...
+    <Link key={link.href ?? link.label} href={link.href} ...

Also applies to: 366-385


293-299: Comment mismatch: Auth toggle only shows on API pages

Comment says “shows on all pages,” but component gates on isInApiSection. Update comment to avoid confusion.

-/* Auth Toggle Button - Shows on all pages like AI Chat button */
+/* Auth Toggle Button - Only on API pages */
docs/src/components/layouts/docs.tsx (3)

86-99: Use ES6 Map for accordion state (per guidelines)

Improves semantics and avoids accidental key collisions. Also aligns with repo guideline to prefer Map over Record for key–value collections.
[Based on coding guidelines]

-type AccordionContextType = {
-  accordionState: Record<string, boolean>,
-  setAccordionState: (key: string, isOpen: boolean) => void,
-};
+type AccordionContextType = {
+  accordionState: Map<string, boolean>,
+  setAccordionState: (key: string, isOpen: boolean) => void,
+};

 function AccordionProvider({ children }: { children: ReactNode }) {
-  const [accordionState, setAccordionStateInternal] = useState<Record<string, boolean>>({});
+  const [accordionState, setAccordionStateInternal] = useState<Map<string, boolean>>(
+    () => new Map<string, boolean>()
+  );

   const setAccordionState = (key: string, isOpen: boolean) => {
-    setAccordionStateInternal(prev => ({ ...prev, [key]: isOpen }));
+    setAccordionStateInternal(prev => {
+      const next = new Map(prev);
+      next.set(key, isOpen);
+      return next;
+    });
   };
 function useAccordionState(key: string, defaultValue: boolean) {
 ...
-  const isOpen = accordionState[key] ?? defaultValue;
+  const isOpen = accordionState.get(key) ?? defaultValue;

Also applies to: 106-120


344-345: Hard-coded blue; tie dot color to theme token

Use the design token to respect theming and reduce magic numbers.

-const platformColor = 'rgb(59, 130, 246)';
+const platformColor = 'hsl(var(--fd-primary))';

And where translucency is needed, prefer modern syntax:

- backgroundColor: isCurrentlyActive ? platformColor : 'rgb(100, 116, 139)',
+ backgroundColor: isCurrentlyActive ? platformColor : 'hsl(var(--fd-primary) / 0.4)',

Similarly replace ${platformColor}40 with hsl(var(--fd-primary) / 0.25), and borders with 1px solid hsl(var(--fd-primary)).

Also applies to: 445-446, 600-601


354-355: Prefer Link for navigation to preserve prefetch and accessibility

Using router.push inside buttons drops prefetch and default a11y affordances. Wrap the dot with a Link or render an anchor styled as a dot.

-<button onClick={handleNavigation} ...>
+<Link href={href} onClick={(e) => { setIsOpen?.(true); }} role="button" aria-label={`Go to ${title}`}>
   ...
-</button>
+</Link>

Also applies to: 456-457

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a009f64 and 0591064.

📒 Files selected for processing (13)
  • docs/src/app/api/layout.tsx (1 hunks)
  • docs/src/app/docs/layout.tsx (1 hunks)
  • docs/src/app/global.css (3 hunks)
  • docs/src/components/api/auth-panel.tsx (1 hunks)
  • docs/src/components/chat/ai-chat.tsx (1 hunks)
  • docs/src/components/layout/toc.tsx (2 hunks)
  • docs/src/components/layouts/api/api-sidebar.tsx (1 hunks)
  • docs/src/components/layouts/docs-header-wrapper.tsx (7 hunks)
  • docs/src/components/layouts/docs.tsx (16 hunks)
  • docs/src/components/layouts/page.tsx (2 hunks)
  • docs/src/components/layouts/shared-header.tsx (8 hunks)
  • docs/src/components/layouts/sidebar-context.tsx (2 hunks)
  • docs/src/components/mdx/dynamic-code-block-overlay.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • docs/src/app/docs/layout.tsx
  • docs/src/components/chat/ai-chat.tsx
  • docs/src/components/layouts/sidebar-context.tsx
  • docs/src/components/layouts/api/api-sidebar.tsx
  • docs/src/components/mdx/dynamic-code-block-overlay.tsx
  • docs/src/components/layouts/shared-header.tsx
  • docs/src/components/api/auth-panel.tsx
  • docs/src/app/api/layout.tsx
  • docs/src/components/layout/toc.tsx
  • docs/src/components/layouts/docs-header-wrapper.tsx
  • docs/src/components/layouts/page.tsx
  • docs/src/components/layouts/docs.tsx
🧬 Code graph analysis (4)
docs/src/app/docs/layout.tsx (2)
docs/src/components/layouts/docs-header-wrapper.tsx (1)
  • DocsHeaderWrapper (249-276)
docs/lib/source.ts (1)
  • source (23-30)
docs/src/components/layouts/shared-header.tsx (3)
docs/src/components/layout/custom-search-toggle.tsx (1)
  • SearchInputToggle (95-150)
docs/src/components/layout/custom-search-dialog.tsx (1)
  • CustomSearchDialog (83-320)
docs/src/components/icons.tsx (2)
  • X (143-146)
  • Menu (137-141)
docs/src/app/api/layout.tsx (1)
docs/src/components/layouts/docs-header-wrapper.tsx (1)
  • DocsHeaderWrapper (249-276)
docs/src/components/layouts/docs-header-wrapper.tsx (3)
docs/src/lib/navigation-utils.ts (1)
  • generateNavLinks (14-37)
docs/src/lib/docs-tree.ts (2)
  • resolveDocsSection (7-17)
  • filterTreeForSection (19-32)
docs/src/components/layouts/shared-header.tsx (1)
  • SharedHeader (224-412)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: setup-tests
  • GitHub Check: docker
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: build (22.x)
  • GitHub Check: all-good
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: Security Check
🔇 Additional comments (14)
docs/src/components/layouts/sidebar-context.tsx (1)

115-115: LGTM: Consistent persistence for chat expansion state.

Replacing setIsChatExpanded(false) with setChatExpanded(false) ensures that the chat expansion state is correctly persisted to localStorage when the chat is closed. This aligns with the pattern used for other stateful setters in the component.

Also applies to: 156-156

docs/src/components/api/auth-panel.tsx (1)

22-22: Record usage is appropriate here despite coding guideline.

While the coding guideline prefers ES6 Map for key-value collections, using Record<string, string> for HTTP headers is the correct choice here. Headers are serialized data that integrate naturally with object spread syntax (line 179) and JSON serialization. Converting to/from Map would add unnecessary complexity.

Based on coding guidelines (noting appropriate exception).

Also applies to: 39-39

docs/src/components/mdx/dynamic-code-block-overlay.tsx (5)

1-16: LGTM!

Imports and type definitions are clean and appropriate for the component's functionality.


18-33: LGTM!

State setup is clean, and memoizing the line count calculation is a good performance optimization.


36-73: LGTM!

The responsive height calculation logic is well-thought-out with appropriate mobile/desktop breakpoints and conservative ratios to prevent overflow. The comments clearly explain the calculation strategy.


76-90: LGTM!

Window resize handling is implemented correctly with proper cleanup and SSR safety checks.


93-141: LGTM!

Syntax highlighting and clipboard functionality are well-implemented with appropriate error handling and visual feedback.

docs/src/components/layouts/api/api-sidebar.tsx (1)

3-3: LGTM! Import cleanup aligns with navigation simplification.

The removal of the ArrowLeft import and related back-navigation UI is consistent with the broader PR objective to remove platform-specific navigation elements.

docs/src/app/api/layout.tsx (1)

93-113: LGTM! Layout restructuring supports new sidebar positioning.

The flex-based layout with sticky sidebar positioning is well-structured. The height calculations (h-[calc(100vh-3.5rem)] lg:h-[calc(100vh-6.5rem)]) correctly account for the header height differences between mobile and desktop viewports.

docs/src/components/layouts/docs-header-wrapper.tsx (2)

2-3: LGTM! Clean refactor to section-based navigation.

The changes successfully replace platform-aware navigation with section-based filtering:

  • Imports the new resolveDocsSection and filterTreeForSection utilities
  • Properly memoizes derived values (navLinks, docsSection, sectionTree)
  • Updates dependency arrays correctly to include sectionTree

The section-based approach simplifies the navigation logic and aligns with the PR's objective to remove platform-specific routing.

Also applies to: 249-267


181-227: All currentPlatform prop references removed. Search across .tsx/.ts returned no matches; safe to merge.

docs/src/app/docs/layout.tsx (1)

11-19: LGTM! Layout styling adjustments align with header refactoring.

The changes simplify the layout structure:

  • mx-auto centers the content horizontally
  • Removing pt-14 eliminates duplicate top padding (likely now handled by the header component or its wrapper)

These adjustments are consistent with the broader layout and navigation simplifications across the PR.

docs/src/components/layouts/page.tsx (2)

7-26: LGTM! Import reorganization improves code organization.

The imports are reorganized without changing runtime behavior. This cleanup aligns with the broader refactoring effort across layout components.


159-161: LGTM! Additional top margin improves content spacing.

The addition of mt-6 to the content container increases vertical spacing above the page content, providing better visual separation after the breadcrumb navigation. This complements the existing bottom margin.

vercel[bot]

This comment was marked as outdated.

vercel[bot]

This comment was marked as outdated.

Copy link

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments:

docs/src/components/layouts/shared-header.tsx (line 263):
Invalid CSS z-index value z-49 used in header component. CSS z-index classes are typically numbered in increments of 10 (z-10, z-20, z-30, z-40, z-50).

View Details
📝 Patch Details
diff --git a/docs/src/components/layouts/shared-header.tsx b/docs/src/components/layouts/shared-header.tsx
index 387c1e82..04cbefb0 100644
--- a/docs/src/components/layouts/shared-header.tsx
+++ b/docs/src/components/layouts/shared-header.tsx
@@ -260,7 +260,7 @@ export function SharedHeader({
 
   return (
     <>
-      <header className="sticky top-0 w-full h-14 lg:h-26 z-49 flex flex-col space-around bg-fd-background">
+      <header className="sticky top-0 w-full h-14 lg:h-26 z-50 flex flex-col space-around bg-fd-background">
 
         {/* First row */}
         <div className="flex items-center justify-between h-14 border-b border-fd-border px-4">

Analysis

Invalid Tailwind CSS z-index class z-49 in header component

What fails: SharedHeader component in docs/src/components/layouts/shared-header.tsx line 263 uses z-49 which is not a standard Tailwind CSS utility class

How to reproduce:

cd docs && grep -n "z-49" src/components/layouts/shared-header.tsx

Result: Shows z-49 class on line 263. Standard Tailwind CSS z-index utilities are: z-0, z-10, z-20, z-30, z-40, z-50, z-auto per official documentation

Expected: Should use z-50 (next standard value) or z-[49] (arbitrary value syntax) for proper z-index styling

Copy link

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments:

docs/src/components/layouts/shared-header.tsx (line 263):
Invalid CSS class lg:h-26 used in header - Tailwind CSS doesn't have an h-26 utility class.

View Details
📝 Patch Details
diff --git a/docs/src/components/layouts/shared-header.tsx b/docs/src/components/layouts/shared-header.tsx
index 387c1e82..96b2f84c 100644
--- a/docs/src/components/layouts/shared-header.tsx
+++ b/docs/src/components/layouts/shared-header.tsx
@@ -260,7 +260,7 @@ export function SharedHeader({
 
   return (
     <>
-      <header className="sticky top-0 w-full h-14 lg:h-26 z-49 flex flex-col space-around bg-fd-background">
+      <header className="sticky top-0 w-full h-14 lg:h-28 z-49 flex flex-col space-around bg-fd-background">
 
         {/* First row */}
         <div className="flex items-center justify-between h-14 border-b border-fd-border px-4">

Analysis

Invalid Tailwind CSS class lg:h-26 in SharedHeader component

What fails: SharedHeader component in docs/src/components/layouts/shared-header.tsx line 263 uses lg:h-26 which is not a valid Tailwind CSS utility class

How to reproduce:

# The class is present in the header component:
grep -n "lg:h-26" docs/src/components/layouts/shared-header.tsx

Result: Class lg:h-26 found at line 263 but Tailwind CSS default spacing scale jumps from h-24 (6rem) to h-28 (7rem), with no h-26 (6.5rem) utility available

Expected: Should use a valid Tailwind height class like lg:h-24 or lg:h-28, or implement a custom height utility if 6.5rem is specifically needed per Tailwind CSS height documentation

@cursor
Copy link

cursor bot commented Oct 15, 2025

You have run out of free Bugbot PR reviews for this billing cycle. This will reset on November 10.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Copy link

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments:

docs/src/components/mdx/platform-codeblock.tsx (lines 243-255):
React state is being updated during initialization before the component is mounted, which violates React's rules and can cause runtime errors.

View Details
📝 Patch Details
diff --git a/docs/src/components/mdx/platform-codeblock.tsx b/docs/src/components/mdx/platform-codeblock.tsx
index d0e4db75..237535b4 100644
--- a/docs/src/components/mdx/platform-codeblock.tsx
+++ b/docs/src/components/mdx/platform-codeblock.tsx
@@ -240,6 +240,8 @@ export function PlatformCodeblock({
     });
   }, [platformNames, platforms, defaultFrameworks]);
 
+  const [selectedPlatform, setSelectedPlatform] = useState(getInitialPlatform);
+
   // Initialize global state on first render
   useEffect(() => {
     initializeGlobalFrameworks();
@@ -251,8 +253,6 @@ export function PlatformCodeblock({
       }
     }
   }, [initializeGlobalFrameworks, platformNames]);
-
-  const [selectedPlatform, setSelectedPlatform] = useState(getInitialPlatform);
   const [selectedFrameworks, setSelectedFrameworks] = useState<{ [platform: string]: string }>(() => {
     // Don't initialize with defaults - let users manually select frameworks
     return { ...globalSelectedFrameworks };

Analysis

ReferenceError in PlatformCodeblock component due to useState called after useEffect

What fails: PlatformCodeblock.useEffect() on line 244 calls setSelectedPlatform() before the useState declaration on line 255, causing a Temporal Dead Zone violation

How to reproduce:

  1. Load any page using PlatformCodeblock component
  2. Component initialization triggers useEffect before useState
  3. Results in "Cannot access 'setSelectedPlatform' before initialization" ReferenceError

Result: Component crashes during initialization when setSelectedPlatform(storedPlatform) is called at line 250

Expected: useState should be declared before any useEffect that references its setter function, per JavaScript Temporal Dead Zone rules

Copy link
Contributor

@N2D4 N2D4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking forward to merging this! I added some comments on Vercel preview.

The largest blocker for me right now is that there are multiple (4?) different codeblocks (SDK, some have a theme picker, some with langauge dropdown, without language dropdown, with title, without, some white background, some blue background, etc...). I think we should unify all of these into a single codeblock component, and it should look the same for all

Copy link

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments:

docs/src/lib/docs-tree.ts (line 104):
The throwErr call for split('#')[0] returning undefined is logically impossible and will never execute, creating dead code.

View Details
📝 Patch Details
diff --git a/docs/src/lib/docs-tree.ts b/docs/src/lib/docs-tree.ts
index 8719dbcb..8eb053e7 100644
--- a/docs/src/lib/docs-tree.ts
+++ b/docs/src/lib/docs-tree.ts
@@ -101,7 +101,7 @@ function matchesSection(url: string, section: DocsSection): boolean {
 }
 
 function normalizeUrl(url: string): string {
-  const withoutFragment = url.split('#')[0] ?? throwErr("URL split by # returned empty array", { url });
+  const withoutFragment = url.split('#')[0];
   return withoutFragment.replace(/\/$/, '');
 }
 

Analysis

Dead code in normalizeUrl() - throwErr will never execute

What fails: The normalizeUrl() function in docs/src/lib/docs-tree.ts:104 uses url.split('#')[0] ?? throwErr(...) but the throwErr will never execute because split('#')[0] cannot return null or undefined

How to reproduce:

// String.prototype.split() always returns a non-empty array
console.log(''.split('#')); // ['']
console.log('#fragment'.split('#')); // ['', 'fragment']  
console.log('path#fragment'.split('#')); // ['path', 'fragment']
// Accessing [0] always returns a string (possibly empty), never null/undefined

Result: Dead code with misleading error message "URL split by # returned empty array" that can never be triggered

Expected: Per MDN documentation, split() always returns an array, so accessing [0] always returns a string value

vercel[bot]

This comment was marked as outdated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/content/docs/sdk/types/customer.mdx (1)

252-256: Fix formatting of "server side" and list structure.

Line 254 uses "server side -" with a trailing dash that breaks the markdown flow and reads awkwardly. Use a hyphenated compound adjective and proper list formatting.

-### Security Considerations

-- **Client-side safety**: All payment operations are designed to be safe for client-side use
-- **Server validation**: Critical operations should always be validated on the server side
-- **Race conditions**: Use [`tryDecreaseQuantity()`](../types/item.mdx#serveritemtrydecreasequantity) for atomic, race-condition-free item consumption
+### Security Considerations

+- **Client-side safety**: All payment operations are designed to be safe for client-side use
+- **Server-side validation**: Critical operations should always be validated server-side
+- **Race conditions**: Use [`tryDecreaseQuantity()`](../types/item.mdx#serveritemtrydecreasequantity) for atomic, race-condition-free item consumption
♻️ Duplicate comments (4)
docs/content/docs/sdk/types/user.mdx (1)

1321-1323: Fix ServerUser contact channel types in TOC.

The table of contents (line 1321–1323) shows ContactChannel[], but server-side contact channels should be typed as ServerContactChannel[].

-      listContactChannels(): Promise<ContactChannel[]>; //$stack-link-to:#serveruserlistcontactchannels
-      // NEXT_LINE_PLATFORM react-like
-      ⤷ useContactChannels(): ContactChannel[]; //$stack-link-to:#serveruserusecontactchannels
+      listContactChannels(): Promise<ServerContactChannel[]>; //$stack-link-to:#serveruserlistcontactchannels
+      // NEXT_LINE_PLATFORM react-like
+      ⤷ useContactChannels(): ServerContactChannel[]; //$stack-link-to:#serveruserusecontactchannels
docs/content/docs/sdk/types/team.mdx (1)

612-612: Update client-vs-server link to guides path.

Line 612 references the old ../../concepts/stack-app.mdx path. Update to the new guides structure introduced in this PR refactor.

-Like [`Team`](#team), but with [server permissions](../../concepts/stack-app.mdx#client-vs-server). Has full read and write access to everything.
+Like [`Team`](#team), but with [server permissions](/docs/guides/concepts/stack-app#client-vs-server). Has full read and write access to everything.
docs/src/components/mdx/platform-codeblock.tsx (2)

156-162: Default variant should be the first provided, not always 'server'.

This incorrectly picks server even when only a client variant exists.

Apply this diff:

-      // Initialize default variants
+      // Initialize default variants to the first provided (server or client)
       if (!(language in defaultVariants)) {
         defaultVariants[language] = {};
       }
       if (!defaultVariants[language]?.[framework]) {
-        defaultVariants[language]![framework] = 'server';
+        defaultVariants[language]![framework] = variantType;
       }

277-280: Validate persisted framework before use to avoid empty code panes.

A stale selectedFrameworks[selectedPlatform] can be invalid for the current platform, yielding no content.

Apply this diff:

-  const currentFramework = selectedFrameworks[selectedPlatform] || currentFrameworks[0];
+  const selected = selectedFrameworks[selectedPlatform];
+  const currentFramework = (selected && currentFrameworks.includes(selected))
+    ? selected
+    : currentFrameworks[0];
🧹 Nitpick comments (22)
docs/src/app/layout.tsx (1)

18-18: Clarify the need for suppressHydrationWarning on body.

The suppressHydrationWarning attribute has been added to the <body> element. While this suppresses hydration mismatch warnings, it's generally preferable to identify and fix the root cause of hydration differences between server and client rendering.

Could you clarify what specific hydration issue this is addressing? Common sources in this layout might include:

  • Dynamic theme classes from StackTheme
  • Third-party scripts injected by PostHogProvider
  • Client-side state from RootProvider

If there's a specific, intentional mismatch (e.g., theme persistence), consider adding a comment explaining why this suppression is necessary.

docs/content/docs/(guides)/others/convex.mdx (3)

52-56: Clarify that the three client setup approaches are mutually exclusive.

Presenting all three options (convexClient, convexReactClient, convexHttpClient) in a single code block may suggest they should all be used together. Add a clarifying comment or restructure to show that only one applies per context.

Consider updating the code block:

 Then, update your Convex client to use Stack Auth:

 ```ts
-convexClient.setAuth(stackServerApp.getConvexClientAuth({}));  // browser JS
-convexReactClient.setAuth(stackServerApp.getConvexClientAuth({}));  // React
-convexHttpClient.setAuth(stackServerApp.getAuthForConvexHttpClient({ tokenStore: requestObject }));  // HTTP, see Stack Auth docs for more information on tokenStore
+// Choose ONE of the following based on your context:
+
+convexClient.setAuth(stackServerApp.getConvexClientAuth({}));  // browser JS
+// OR
+convexReactClient.setAuth(stackServerApp.getConvexClientAuth({}));  // React
+// OR
+convexHttpClient.setAuth(stackServerApp.getAuthForConvexHttpClient({ tokenStore: requestObject }));  // HTTP client

---

`45-45`: **Clarify environment variable choice.**

Line 45 presents two possible environment variables (`STACK_PROJECT_ID` or `NEXT_PUBLIC_STACK_PROJECT_ID`) but doesn't explain when to use each. The comment suggests either works, which may confuse users about security/exposure implications.

Enhance the comment to clarify the choice:

```diff
-    projectId: process.env.STACK_PROJECT_ID,  // or: process.env.NEXT_PUBLIC_STACK_PROJECT_ID
+    projectId: process.env.STACK_PROJECT_ID || process.env.NEXT_PUBLIC_STACK_PROJECT_ID,  // Use STACK_PROJECT_ID on server, NEXT_PUBLIC_STACK_PROJECT_ID if client-side access needed

55-55: Provide direct link for tokenStore documentation.

Line 55 references "see Stack Auth docs for more information on tokenStore" but uses a vague reference. Include a direct link or more specific guidance for clarity.

Update the comment with a more specific reference:

-convexHttpClient.setAuth(stackServerApp.getAuthForConvexHttpClient({ tokenStore: requestObject }));  // HTTP, see Stack Auth docs for more information on tokenStore
+convexHttpClient.setAuth(stackServerApp.getAuthForConvexHttpClient({ tokenStore: requestObject }));  // HTTP; for tokenStore details, see https://docs.stack-auth.com/sdk/token-storage
docs/content/docs/sdk/types/team.mdx (1)

780-787: Move example section to proper AsideSection wrapper.

Line 782–787 places ### Examples and code block directly inside an <AsideSection> as Markdown, instead of wrapping them in a sibling <AsideSection title="Examples"> component. This breaks consistency with other method sections.

     <MethodAside>
       <AsideSection title="Signature">

         ```typescript
         declare function removeUser(userId: string): Promise<void>;
         ```
-      ### Examples
-
+      </AsideSection>
+      <AsideSection title="Examples">
         ```typescript Removing a user from the team
         await team.removeUser('user_id_123');
         ```
       </AsideSection>
     </MethodAside>
docs/src/components/mdx/base-codeblock.tsx (3)

67-70: Make theme detection resilient; avoid per‑instance observers.

Matching a specific CSS var value is brittle. Prefer a clear signal (html.dark or data-theme) and centralize theme change notifications to avoid N MutationObservers (one per block).

Example refactor:

+function getThemeFromDom(): 'github-dark' | 'github-light' {
+  const root = document.documentElement;
+  const dark = root.classList.contains('dark') || root.getAttribute('data-theme') === 'dark';
+  return dark ? 'github-dark' : 'github-light';
+}
...
-          const isDarkMode = document.documentElement.classList.contains('dark') ||
-                            getComputedStyle(document.documentElement).getPropertyValue('--fd-background').includes('0 0% 3.9%');
-          theme = isDarkMode ? 'github-dark' : 'github-light';
+          theme = getThemeFromDom();
...
-    const observer = new MutationObserver(() => {
-      runAsynchronously(updateHighlightedCode);
-    });
-    observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
-    return () => observer.disconnect();
+    // Prefer a shared ThemeContext or a window event 'docs:theme-change'
+    window.addEventListener('docs:theme-change', updateHighlightedCode);
+    return () => window.removeEventListener('docs:theme-change', updateHighlightedCode);

If a shared context/event isn’t available yet, keep one observer at the app shell and dispatch the event.

Also applies to: 112-118


72-75: Normalize code input predictably.

Removing only a single leading space is fragile. Normalize leading newline/indent consistently so highlighting output is stable.

Example:

-        const codeToHighlight = code.startsWith(' ')
-          ? code.slice(1)
-          : code;
+        const codeToHighlight = code.replace(/^\n/, ''); // drop a single leading newline from MDX fences

Or introduce a tiny helper to strip common indentation (dedent) without altering meaningful leading spaces.

Also applies to: 100-101


107-115: Many code blocks = many observers; consider centralizing.

Each BaseCodeblock attaches a MutationObserver. On pages with many blocks this adds overhead.

Expose a context/provider that emits theme changes once; BaseCodeblock subscribes via an event or context value (you already have themeKey — leverage it).

docs/src/components/mdx/dynamic-code-block.tsx (2)

66-76: Gate auto‑open behind a prop; avoid surprising users.

Auto‑opening the overlay on mount can be intrusive, especially on mobile or pages with multiple code blocks.

Apply this diff to make it opt‑in:

 type DynamicCodeblockProps = {
   code: string,
   language?: string,
   title?: string,
+  autoOpen?: boolean,
 }
...
 export function DynamicCodeblock({
   code,
   language = 'tsx',
-  title = "Code Example"
+  title = "Code Example",
+  autoOpen = false,
 }: DynamicCodeblockProps) {
...
-  if (code && !hasInitialized) {
+  if (autoOpen && code && !hasInitialized) {

85-113: Add button type and accessible label; respect reduced motion.

Improve a11y and motion sensitivity.

-      <button
+      <button
+        type="button"
         onClick={() => openOverlay(code, language, title)}
         className={cn(
           "fixed bottom-6 z-30",
           "flex items-center gap-1.5 px-3 py-2",
           "bg-fd-primary text-fd-primary-foreground",
           "rounded-full shadow-lg",
-          "hover:scale-105 active:scale-95",
-          "transition-all duration-300",
+          "hover:scale-105 active:scale-95",
+          "transition-all duration-300 motion-reduce:transition-none motion-reduce:hover:transform-none",
           "border border-fd-primary/20"
         )}
...
-        title="View Code Example"
+        title="View Code Example"
+        aria-label="View code example"
       >
docs/src/components/mdx/sdk-components.tsx (3)

28-99: Re‑measure line positions on theme change (fonts/metrics can shift).

Current effect runs on code changes, timeouts, and resize. Theme flips can alter line-height, desyncing overlays.

If you already emit a themeKey (BaseCodeblock supports it), pass it down and include it in dependencies:

 function ClickableCodeblock({ ... , title }: { ... }) {
   ...
-  useEffect(() => {
+  useEffect(() => {
     if (codeRef.current) {
       const measurePositions = () => { /* ... */ };
       measurePositions();
       const timeoutId1 = setTimeout(measurePositions, 100);
       const timeoutId2 = setTimeout(measurePositions, 300);
       const handleResize = () => measurePositions();
       window.addEventListener('resize', handleResize);
       return () => { /* cleanup */ };
     }
-  }, [code]);
+  }, [code, /* themeKey */]);

Alternatively, subscribe to a central “docs:theme-change” event and call measurePositions().


389-407: Avoid O(n²) mapping for clickableAreas.

Using findIndex over processedLines for each clickable item is quadratic. Track the rendered line index during the initial map.

Sketch:

const processedLines = [];
for (let i = 0; i < lines.length; i++) {
  // build item; push to processedLines
}
const clickableAreas = processedLines
  .map((item, idx) => ({ item, idx }))
  .filter(({ item }) => item.type === 'clickable')
  .map(({ item, idx }) => ({
    type: 'clickable' as const,
    code: item.code,
    anchor: item.anchor,
    lineNumber: idx,
    originalLineNumber: item.originalLineIndex,
  }));

167-258: Prefer Map over Record for key–value collections.

The snippetContent table is a collection; using Map improves semantics and iteration guarantees. As per coding guidelines.

Example diff:

-  const snippetContent: Record<string, () => React.ReactElement> = {
-    '../../snippets/stack-app-constructor-options-before-ssk.mdx': () => (/* ... */),
-    '../../snippets/stack-app-constructor-options-after-ssk.mdx': () => (/* ... */),
-  };
+  const snippetContent = new Map<string, () => React.ReactElement>([
+    ['../../snippets/stack-app-constructor-options-before-ssk.mdx', () => (/* ... */)],
+    ['../../snippets/stack-app-constructor-options-after-ssk.mdx', () => (/* ... */)],
+  ]);
...
-  const ContentComponent = snippetContent[src];
-  if (!(src in snippetContent)) {
+  const ContentComponent = snippetContent.get(src);
+  if (!ContentComponent) {
     console.warn(`Snippet not found: ${src}`);
     return <div className="markdown-include text-red-500">Snippet not found: {src}</div>;
   }
-  return <ContentComponent />;
+  return <ContentComponent />;
docs/src/components/mdx/dynamic-code-block-overlay.tsx (3)

75-125: DRY the highlight logic with BaseCodeblock.

This duplicates BaseCodeblock’s Shiki + theme logic. Extract a shared helper/hook (e.g., useShikiHighlight(code, language)) or a module-level function that returns sanitized HTML. Reduces bugs and keeps theming consistent.

I can factor this into a small hook used by both components.


201-233: Button a11y and form‑safety.

Add type="button" to buttons to avoid implicit submit, and add aria-live for the “Copied!” feedback.

-            <button
+            <button
+              type="button"
               onClick={() => runAsynchronously(() => handleCopy())}
...
-            <button
+            <button
+              type="button"
               onClick={() => setIsExpanded(!isExpanded)}
...
-            <button
+            <button
+              type="button"
               onClick={() => onToggle?.(false)}

Add an offscreen live region near the buttons:

+          <span aria-live="polite" className="sr-only">
+            {copied ? 'Code copied to clipboard' : ''}
+          </span>

75-105: Reuse a singleton Shiki highlighter for performance.

Creating a highlighter per call is expensive. Cache a highlighter instance and reuse across renders.

I can wire a module-level getHighlighterSingleton() and swap codeToHtml for highlighter.codeToHtml(theme). Based on learnings.

docs/src/mdx-components.tsx (1)

29-29: Type‑safe MDX img mapping; drop any‑cast.

Avoid any and the lint suppression. You can derive the correct prop type from MDXComponents['img'].

Apply this diff:

   JWTViewer,
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  img: (props) => <ImageZoom {...(props as any)} />,
+  // Type-safe: reuse MDX's img prop type
+  img: (props: Parameters<NonNullable<MDXComponents['img']>>[0]) => <ImageZoom {...props} />,

Also applies to: 93-95

docs/src/components/mdx/platform-codeblock.tsx (5)

27-33: Clean up listener maps when empty to avoid leaks.

After removing the last listener, delete the key from the Map.

Apply this diff:

 function removePlatformListener(id: string, listener: PlatformChangeListener): void {
   const list = platformListeners.get(id) ?? [];
-  platformListeners.set(
-    id,
-    list.filter((item) => item !== listener),
-  );
+  const next = list.filter((item) => item !== listener);
+  if (next.length) platformListeners.set(id, next);
+  else platformListeners.delete(id);
 }
 
 function removeFrameworkListener(id: string, listener: FrameworkChangeListener): void {
   const list = frameworkListeners.get(id) ?? [];
-  frameworkListeners.set(
-    id,
-    list.filter((item) => item !== listener),
-  );
+  const next = list.filter((item) => item !== listener);
+  if (next.length) frameworkListeners.set(id, next);
+  else frameworkListeners.delete(id);
 }

Also applies to: 41-47


214-241: Sanitize and validate restored frameworks from sessionStorage.

Merge only entries that exist in platforms and whose framework is valid for that platform to prevent inconsistent state.

Apply this diff:

   if (typeof window !== 'undefined') {
     const stored = sessionStorage.getItem('stack-docs-selected-frameworks');
     if (stored) {
       try {
-        const parsed = JSON.parse(stored);
-        globalSelectedFrameworks = { ...globalSelectedFrameworks, ...parsed };
+        const parsed = JSON.parse(stored) as Record<string, string>;
+        const valid: Record<string, string> = {};
+        for (const [plat, fw] of Object.entries(parsed)) {
+          if (platformNames.includes(plat)) {
+            const available = Object.keys(platforms[plat] ?? {});
+            if (available.includes(fw)) valid[plat] = fw;
+          }
+        }
+        globalSelectedFrameworks = { ...globalSelectedFrameworks, ...valid };
       } catch (e) {
         // Ignore parsing errors
       }
     }
   }

282-291: Simplify hasVariants (optional).

The typeof config !== 'object' guard is redundant. Directly probe keys on a safe fallback.

Apply this diff:

-  const hasVariants = (platform: string, framework: string) => {
-    const platformConfig = platforms[platform];
-    const config = platformConfig[framework];
-    if (typeof config !== 'object') {
-      return false;
-    }
-
-    return 'server' in config && 'client' in config;
-  };
+  const hasVariants = (platform: string, framework: string) => {
+    const config = platforms[platform]?.[framework] ?? {};
+    return 'server' in (config as object) && 'client' in (config as object);
+  };

416-433: Add basic ARIA to the dropdown trigger (optional).

Improve accessibility by indicating popup state.

Apply this diff:

-            <button
-              onClick={toggleDropdown}
+            <button
+              onClick={toggleDropdown}
+              aria-haspopup="listbox"
+              aria-expanded={isDropdownOpen}
+              aria-controls={`pc-menu-${componentId}`}
               className={cn(

And set the menu container id:

-              <div className="absolute right-0 top-full z-[200] mt-1 min-w-[220px] rounded-lg border border-fd-border/70 bg-fd-background shadow-lg">
+              <div
+                id={`pc-menu-${componentId}`}
+                role="listbox"
+                className="absolute right-0 top-full z-[200] mt-1 min-w-[220px] rounded-lg border border-fd-border/70 bg-fd-background shadow-lg"
+              >

117-121: Consider Map over Record for key–value collections (defer).

defaultFrameworks, selectedFrameworks, and selectedVariants model maps; using Map can clarify intent and avoid prototype pitfalls. This is optional given the current state management.

As per coding guidelines

Also applies to: 255-261

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ece1a1 and c4b5175.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • docs/content/docs/(guides)/others/convex.mdx (1 hunks)
  • docs/content/docs/sdk/types/api-key.mdx (1 hunks)
  • docs/content/docs/sdk/types/contact-channel.mdx (1 hunks)
  • docs/content/docs/sdk/types/customer.mdx (1 hunks)
  • docs/content/docs/sdk/types/email.mdx (1 hunks)
  • docs/content/docs/sdk/types/item.mdx (1 hunks)
  • docs/content/docs/sdk/types/project.mdx (1 hunks)
  • docs/content/docs/sdk/types/team-permission.mdx (1 hunks)
  • docs/content/docs/sdk/types/team-profile.mdx (1 hunks)
  • docs/content/docs/sdk/types/team-user.mdx (1 hunks)
  • docs/content/docs/sdk/types/team.mdx (1 hunks)
  • docs/content/docs/sdk/types/user.mdx (1 hunks)
  • docs/src/app/layout.tsx (1 hunks)
  • docs/src/components/mdx/base-codeblock.tsx (1 hunks)
  • docs/src/components/mdx/dynamic-code-block-overlay.tsx (5 hunks)
  • docs/src/components/mdx/dynamic-code-block.tsx (2 hunks)
  • docs/src/components/mdx/platform-codeblock.tsx (1 hunks)
  • docs/src/components/mdx/sdk-components.tsx (7 hunks)
  • docs/src/lib/merge-refs.ts (1 hunks)
  • docs/src/mdx-components.tsx (4 hunks)
  • docs/templates/sdk/types/api-key.mdx (0 hunks)
💤 Files with no reviewable changes (1)
  • docs/templates/sdk/types/api-key.mdx
✅ Files skipped from review due to trivial changes (3)
  • docs/content/docs/sdk/types/api-key.mdx
  • docs/content/docs/sdk/types/email.mdx
  • docs/content/docs/sdk/types/team-permission.mdx
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/content/docs/sdk/types/contact-channel.mdx
  • docs/content/docs/sdk/types/team-user.mdx
  • docs/content/docs/sdk/types/team-profile.mdx
  • docs/content/docs/sdk/types/project.mdx
  • docs/content/docs/sdk/types/item.mdx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • docs/src/app/layout.tsx
  • docs/src/lib/merge-refs.ts
  • docs/src/components/mdx/base-codeblock.tsx
  • docs/src/components/mdx/platform-codeblock.tsx
  • docs/src/components/mdx/sdk-components.tsx
  • docs/src/mdx-components.tsx
  • docs/src/components/mdx/dynamic-code-block.tsx
  • docs/src/components/mdx/dynamic-code-block-overlay.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
PR: stack-auth/stack-auth#943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.

Applied to files:

  • docs/src/components/mdx/platform-codeblock.tsx
🧬 Code graph analysis (5)
docs/src/components/mdx/base-codeblock.tsx (1)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
docs/src/components/mdx/platform-codeblock.tsx (3)
docs/lib/code-examples.ts (2)
  • CodeExample (15-22)
  • getExample (31-31)
docs/code-examples/index.ts (1)
  • getExample (11-15)
docs/src/components/mdx/base-codeblock.tsx (1)
  • BaseCodeblock (34-166)
docs/src/components/mdx/sdk-components.tsx (1)
docs/src/components/mdx/base-codeblock.tsx (1)
  • BaseCodeblock (34-166)
docs/src/components/mdx/dynamic-code-block.tsx (3)
docs/src/hooks/use-code-overlay.tsx (1)
  • useCodeOverlay (64-70)
docs/src/components/layouts/sidebar-context.tsx (1)
  • useSidebar (46-49)
docs/src/components/icons.tsx (1)
  • Code (340-343)
docs/src/components/mdx/dynamic-code-block-overlay.tsx (2)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
docs/src/components/icons.tsx (1)
  • Code (340-343)
🪛 ast-grep (0.39.6)
docs/src/components/mdx/base-codeblock.tsx

[warning] 157-157: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

docs/src/components/mdx/dynamic-code-block-overlay.tsx

[warning] 239-239: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
docs/src/components/mdx/base-codeblock.tsx

[error] 158-158: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🪛 LanguageTool
docs/content/docs/sdk/types/user.mdx

[style] ~1306-~1306: Consider using “except” or “except for”
Context: ...st CurrentUser properties and methods with the exception of those that require an active session (`...

(WITH_THE_EXCEPTION_OF)

docs/content/docs/sdk/types/customer.mdx

[uncategorized] ~254-~254: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...tions should always be validated on the server side - Race conditions: Use [`tryDecreas...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: all-good
  • GitHub Check: setup-tests
  • GitHub Check: restart-dev-and-test
  • GitHub Check: Security Check
🔇 Additional comments (9)
docs/src/lib/merge-refs.ts (1)

3-15: LGTM! Standard ref-merging implementation.

This is a clean and correct implementation of a ref-merging utility that follows well-established patterns in the React ecosystem. The function correctly handles both callback refs and object refs, properly filters out undefined/null values, and safely assigns to ref.current after appropriate type narrowing.

docs/src/components/mdx/base-codeblock.tsx (1)

3-3: Avoid deep “dist” import; use public entrypoint.

Deep imports can break with package internals and duplicate bundles. Import from the package’s public API instead (e.g., @stackframe/stack-shared/utils/promises), if available.

Can you confirm a stable public path for runAsynchronously? I’ll generate a repo‑wide fix once confirmed.

docs/src/components/mdx/dynamic-code-block.tsx (1)

47-64: Scroll fade logic looks good.

Good use of passive listeners and cleanup. No issues.

docs/src/components/mdx/sdk-components.tsx (1)

102-109: Nice reuse of BaseCodeblock.

Passing codeContainerRef and title through is clean and keeps concerns separated.

docs/src/components/mdx/dynamic-code-block-overlay.tsx (2)

3-3: Avoid deep “dist” import; use public entrypoint.

Same note as BaseCodeblock: prefer a stable exported path for runAsynchronously to avoid internal API drift.


170-188: Sticky overlay container structure looks solid.

Good constraints and sidebar-aware positioning.

docs/src/mdx-components.tsx (1)

16-16: Registration changes look good.

Importing and exposing PlatformCodeblock, plus the SDK MDX component group, aligns with the new unified docs model. No issues.

Also applies to: 26-26, 74-74

docs/src/components/mdx/platform-codeblock.tsx (2)

242-252: Good: restore persisted platform on mount.

This addresses continuity between page loads and is guarded for client-only usage.


391-399: Good: null‑safe spread for variant map.

The (prev[selectedPlatform] ?? {}) fallback avoids runtime errors on first assignment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
docs/content/docs/(guides)/others/convex.mdx (1)

62-78: Add missing imports and initialization for the usage example.

This code example uses useUser() (line 66), query() (line 71), and stackServerApp (line 74) without showing imports or how they're initialized. For readers following this guide, these omissions will cause runtime errors.

Add imports at the top of each code block and clarify the initialization context:

+// MyPage.tsx
+import { useUser } from "@stackframe/react";  // or adjust import based on your Stack Auth package
+
 export function MyPage() {
   // see https://docs.stack-auth.com for more information on how to use Stack Auth
   const user = useUser();
   return <div>Your email is {user.email}</div>;
 }
 
 // myFunctions.ts
+import { query } from "convex/server";
+import { stackServerApp } from "./path/to/stack-server-app";  // adjust import based on your setup
+
 export const myQuery = query({
   handler: async (ctx, args) => {

Consider whether to consolidate the browser JS, React, and HTTP client setup into a single recommended approach for this Convex+Next.js context, or add more explicit guidance on when to use each.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4b5175 and 1c1c3e5.

📒 Files selected for processing (2)
  • docs/content/docs/(guides)/others/convex.mdx (1 hunks)
  • docs/src/lib/merge-refs.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/src/lib/merge-refs.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: setup-tests
  • GitHub Check: restart-dev-and-test
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: docker
  • GitHub Check: all-good
  • GitHub Check: Vercel Agent Review
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: Security Check

vercel[bot]

This comment was marked as outdated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (6)
docs/code-examples/setup.ts (1)

661-664: Fix indentation in Flask code example.

Line 663 has incorrect indentation. The dictionary key should align with the opening of the headers={ dictionary on line 662.

Apply this diff to fix the indentation:

     try:
         user_data = stack_auth_request('GET', '/api/v1/users/me', headers={
-    'x-stack-access-token': access_token,
+            'x-stack-access-token': access_token,
         })
docs/src/components/layouts/shared-header.tsx (1)

275-329: Fix invalid Tailwind utilities: h-26, z-49, space-around.

These don’t exist by default and aren’t extended; they compile to nothing. Use arbitrary values/valid classes.

-      <header className="sticky top-0 w-full h-14 lg:h-26 z-49 flex flex-col space-around bg-fd-background">
+      <header className="sticky top-0 w-full h-14 lg:h-[6.5rem] z-[49] flex flex-col justify-between bg-fd-background">
#!/bin/bash
# Audit invalid utilities across repo
rg -n --glob '*.{ts,tsx,js,jsx}' -C1 '\b(h-26|z-49|lg:top-26|space-around|justify-stretch)\b'
docs/src/components/layouts/docs.tsx (2)

287-289: Avoid index as a fallback React key in tree renders.

Use a deterministic key to prevent reconciliation bugs on filtering/reordering.

-          {item.children.map((child, index) => (
-            <PageTreeItem key={child.type === 'page' ? child.url : index} item={child} />
-          ))}
+          {item.children.map((child, index) => (
+            <PageTreeItem
+              key={child.type === 'page' ? child.url : `${child.type}-${typeof child.name === 'string' ? child.name : index}`}
+              item={child}
+            />
+          ))}
@@
-        {item.children.map((child, index) => (
-          <PageTreeItem key={child.type === 'page' ? child.url : index} item={child} />
-        ))}
+        {item.children.map((child, index) => (
+          <PageTreeItem
+            key={child.type === 'page' ? child.url : `${child.type}-${typeof child.name === 'string' ? child.name : index}` }
+            item={child}
+          />
+        ))}
@@
-      {tree.children.map((item, index) => (
-        <PageTreeItem key={item.type === 'page' ? item.url : index} item={item} />
-      ))}
+      {tree.children.map((item, index) => (
+        <PageTreeItem
+          key={item.type === 'page' ? item.url : `${item.type}-${typeof item.name === 'string' ? item.name : index}`}
+          item={item}
+        />
+      ))}

Also applies to: 301-303, 321-325


972-976: Fix non‑standard lg:top-26 (sticky offset).

Use an arbitrary value matching header height.

-        "hidden md:block sticky left-0 top-14 lg:top-26 z-30 transition-all duration-300 ease-out",
+        "hidden md:block sticky left-0 top-14 lg:top-[6.5rem] z-30 transition-all duration-300 ease-out",
docs/src/components/mdx/platform-codeblock.tsx (2)

205-211: Default variant should be the first provided, not always 'server'.

When only a client variant exists, defaulting to 'server' yields empty content.

-      if (!defaultVariants[language]?.[framework]) {
-        defaultVariants[language]![framework] = 'server';
-      }
+      if (!defaultVariants[language]?.[framework]) {
+        defaultVariants[language]![framework] = variantType; // pick the first provided
+      }

365-368: Validate framework membership before using persisted selection.

Guard against stale sessionStorage values to avoid empty blocks.

-  const currentFramework = selectedFrameworks[selectedPlatform] || currentFrameworks[0];
+  const selected = selectedFrameworks[selectedPlatform];
+  const currentFramework = (selected && currentFrameworks.includes(selected))
+    ? selected
+    : currentFrameworks[0];
🧹 Nitpick comments (18)
docs/src/app/(home)/page.tsx (1)

1-5: Consider adding a user-facing fallback message.

While the redirect in next.config.mjs should prevent users from reaching this page, returning null means visitors would see a blank page if the redirect fails or is misconfigured. A minimal fallback UI (e.g., "Redirecting to documentation..." or a link to /docs) would improve resilience and developer experience.

Optionally, apply this diff to add a helpful fallback:

-// This page is never reached because of the redirect in next.config.mjs
-// Kept as a fallback in case the redirect configuration is removed
+// This page should not be reached due to redirect in next.config.mjs
+// Fallback UI in case redirect fails or is removed

 export default function HomePage() {
-  return null;
+  return (
+    <div style={{ padding: '2rem', textAlign: 'center' }}>
+      <p>Redirecting to documentation...</p>
+      <a href="https://github.com/docs">Go to Docs</a>
+    </div>
+  );
 }
docs/src/components/layouts/api/api-sidebar.tsx (4)

58-68: Consider using ES6 Map instead of Record for dynamic collections.

The groups field in OrganizedSection uses Record<string, OrganizedGroup> for a dynamically built collection. Per the coding guidelines, ES6 Map is preferred over Record for key-value collections, as it provides better performance for dynamic operations and clearer intent.

As per coding guidelines

Consider refactoring to:

 type OrganizedSection = {
   title: string,
   pages: PageData[],
-  groups: Record<string, OrganizedGroup>,
+  groups: Map<string, OrganizedGroup>,
 }

You'll also need to update the code that builds and iterates over this structure (lines 769-826).


82-118: Consider using ES6 Map for accordion state management.

The accordion state uses Record<string, boolean> to track open/closed states. Using an ES6 Map would be more appropriate for this dynamic collection and would provide cleaner update patterns.

As per coding guidelines

Consider refactoring to use Map:

 type AccordionContextType = {
-  accordionState: Record<string, boolean>,
+  accordionState: Map<string, boolean>,
   setAccordionState: (key: string, isOpen: boolean) => void,
 }

 function AccordionProvider({ children }: { children: ReactNode }) {
-  const [accordionState, setAccordionStateInternal] = useState<Record<string, boolean>>({});
+  const [accordionState, setAccordionStateInternal] = useState<Map<string, boolean>>(new Map());

   const setAccordionState = (key: string, isOpen: boolean) => {
-    setAccordionStateInternal(prev => ({ ...prev, [key]: isOpen }));
+    setAccordionStateInternal(prev => new Map(prev).set(key, isOpen));
   };

Then update line 111:

-  const isOpen = accordionState[key] ?? defaultValue;
+  const isOpen = accordionState.get(key) ?? defaultValue;

679-689: Optional: Consider Map for consistency.

While the static titleMap is acceptable as a plain object, using a Map would be more consistent with the coding guidelines and could make the intent clearer.

As per coding guidelines

 function getApiSectionTitle(sectionName: string): string {
-  const titleMap: Record<string, string> = {
-    'client': 'Client API',
-    'server': 'Server API',
-    'admin': 'Admin API',
-    'webhooks': 'Webhooks'
-  };
+  const titleMap = new Map([
+    ['client', 'Client API'],
+    ['server', 'Server API'],
+    ['admin', 'Admin API'],
+    ['webhooks', 'Webhooks']
+  ]);

-  return titleMap[sectionName] || formatSectionTitle(sectionName);
+  return titleMap.get(sectionName) || formatSectionTitle(sectionName);
 }

769-826: Refactor dynamic collections to use ES6 Map.

The organizedPages memo builds a dynamic collection using Record<string, OrganizedSection>. This involves multiple insertions and lookups, making Map a better choice for both performance and clarity. The nullish coalescing assignments (lines 777, 787) would translate naturally to Map's has() and set() methods.

As per coding guidelines

Consider refactoring to use Map:

   const organizedPages = useMemo(() => {
-    const organized: Record<string, OrganizedSection> = {};
+    const organized = new Map<string, OrganizedSection>();

     pages.forEach(page => {
       if (page.slugs[0] === 'overview') return;

       const [section, ...rest] = page.slugs;

-      organized[section] ??= {
+      if (!organized.has(section)) {
+        organized.set(section, {
+          title: getApiSectionTitle(section),
+          pages: [],
+          groups: new Map()
+        });
+      }
+      const sectionData = organized.get(section)!;

       if (rest.length === 1) {
-        organized[section].pages.push(page);
+        sectionData.pages.push(page);
       } else if (rest.length >= 2) {
         const groupName = rest[0];
-        organized[section].groups[groupName] ??= {
+        if (!sectionData.groups.has(groupName)) {
+          sectionData.groups.set(groupName, {
+            title: formatSectionTitle(groupName),
+            pages: []
+          });
+        }
+        sectionData.groups.get(groupName)!.pages.push(page);
       }
     });

     // Sort pages and groups
-    Object.values(organized).forEach(section => {
+    organized.forEach(section => {
       // ... sorting logic ...
-      section.groups = Object.fromEntries(sortedGroups);
+      section.groups = new Map(sortedGroups);
     });

     return organized;
   }, [pages]);

You'll also need to update the iteration code (lines 858-923) to use organized.entries() or similar Map methods.

docs/src/components/layouts/sidebar-context.tsx (2)

59-66: Harden storage access and use matchMedia for the responsive check.

Guard against storage exceptions (private mode, quotas) and prefer matchMedia for the breakpoint.

-    const savedToc = localStorage.getItem('toc-open');
-    const savedChat = localStorage.getItem('ai-chat-open');
-    const savedExpanded = localStorage.getItem('ai-chat-expanded');
-    const savedMainSidebarCollapsed = localStorage.getItem('main-sidebar-collapsed');
+    let savedToc: string | null = null;
+    let savedChat: string | null = null;
+    let savedExpanded: string | null = null;
+    let savedMainSidebarCollapsed: string | null = null;
+    try {
+      savedToc = localStorage.getItem('toc-open');
+      savedChat = localStorage.getItem('ai-chat-open');
+      savedExpanded = localStorage.getItem('ai-chat-expanded');
+      savedMainSidebarCollapsed = localStorage.getItem('main-sidebar-collapsed');
+    } catch {
+      // storage unavailable; fall back to defaults
+    }
@@
-    const isLargeScreen = window.innerWidth >= 768;
+    const isLargeScreen =
+      typeof window !== 'undefined' &&
+      (window.matchMedia?.('(min-width: 768px)').matches ?? window.innerWidth >= 768);

132-133: No actual TDZ issue, but optional refactoring is valid for code organization.

The call to setChatExpanded(false) at line 132 is inside the setChatOpen function body, not at the top level. Since this function only executes when invoked (e.g., as an event handler), the const declaration at line 144 will always be hoisted and available by execution time. This is a safe and common pattern—not a Temporal Dead Zone footgun.

However, the suggestion to reorganize code by defining setChatExpanded before setChatOpen is a reasonable optional refactoring for improved readability and logical grouping of state setters before their dependent handlers.

docs/src/components/layouts/platform-indicator.tsx (5)

101-109: Set framework immediately on platform select for consistent display.

Currently, changing platform keeps previous framework until user picks one. Set the per‑platform saved (or default) framework right away.

-  const handlePlatformSelect = (selectedPlatform: string) => {
+  const handlePlatformSelect = (selectedPlatform: PlatformName) => {
     // Broadcast the platform change
-    sessionStorage.setItem('stack-docs-selected-platform', selectedPlatform);
+    localStorage.setItem('stack-docs-selected-platform', selectedPlatform);
     window.dispatchEvent(new CustomEvent('stack-platform-change', { detail: { platform: selectedPlatform } }));
     setPlatform(selectedPlatform);
 
     // Move to framework selection
-    setDropdownView('framework');
+    try {
+      const raw = localStorage.getItem('stack-docs-selected-frameworks');
+      const map = raw ? JSON.parse(raw) as Record<string, string> : {};
+      const nextFw = map[selectedPlatform] ?? getDefaultFrameworkForPlatform(selectedPlatform);
+      if (nextFw) setFramework(nextFw);
+    } catch {/* noop */}
+    setDropdownView('framework');
   };

111-129: Update writers to localStorage and tighten types.

Also type the parameter as FrameworkName.

-  const handleFrameworkSelect = (selectedFramework: string) => {
+  const handleFrameworkSelect = (selectedFramework: FrameworkName) => {
     // Broadcast the framework change
-    const storedFrameworks = sessionStorage.getItem('stack-docs-selected-frameworks');
+    const storedFrameworks = localStorage.getItem('stack-docs-selected-frameworks');
     let frameworks: Record<string, string> = {};
@@
-    sessionStorage.setItem('stack-docs-selected-frameworks', JSON.stringify(frameworks));
+    localStorage.setItem('stack-docs-selected-frameworks', JSON.stringify(frameworks));
     window.dispatchEvent(new CustomEvent('stack-framework-change', { detail: { platform, framework: selectedFramework } }));

3-7: Type safety: use PlatformName/FrameworkName; remove casts.

Prevents invalid values and removes the need for as‑casts.

-import { DEFAULT_FRAMEWORK, DEFAULT_PLATFORM, getFrameworksForPlatform, type PlatformName, PLATFORMS } from '../../../lib/platform-config';
+import { DEFAULT_FRAMEWORK, DEFAULT_PLATFORM, getFrameworksForPlatform, getDefaultFrameworkForPlatform, type PlatformName, type FrameworkName, PLATFORMS } from '../../../lib/platform-config';
@@
-  const [platform, setPlatform] = useState<string>(DEFAULT_PLATFORM);
-  const [framework, setFramework] = useState<string>(DEFAULT_FRAMEWORK);
+  const [platform, setPlatform] = useState<PlatformName>(DEFAULT_PLATFORM);
+  const [framework, setFramework] = useState<FrameworkName>(DEFAULT_FRAMEWORK);
@@
-  const handlePlatformSelect = (selectedPlatform: string) => {
+  const handlePlatformSelect = (selectedPlatform: PlatformName) => {
@@
-  const currentFrameworks = getFrameworksForPlatform(platform as PlatformName);
+  const currentFrameworks = getFrameworksForPlatform(platform);

Also applies to: 15-20, 101-112, 131-131


85-99: Small UX/a11y nits: pointer events + ARIA.

  • Use pointerdown to handle touch.
  • Expose expanded state for the trigger.
-      document.addEventListener('mousedown', handleClickOutside);
-      return () => document.removeEventListener('mousedown', handleClickOutside);
+      document.addEventListener('pointerdown', handleClickOutside);
+      return () => document.removeEventListener('pointerdown', handleClickOutside);
@@
-      <button
+      <button
+        aria-haspopup="menu"
+        aria-expanded={isOpen}
         onClick={() => {

Also applies to: 135-157, 159-163


113-121: Prefer Map over Record for framework selections (optional)

frameworks is currently typed as Record<string, string> in docs/src/components/layouts/platform-indicator.tsx and persisted to sessionStorage. Per project guideline prefer ES6 Map for key–value collections; because you serialize to JSON here, either keep the Record or convert to Map and serialize with Array.from(map.entries()) (rebuild with new Map(parsed)). Optional refactor.

docs/lib/platform-config.ts (3)

66-70: Tighten isValidPlatformFramework types.

Use PlatformName/FrameworkName to catch typos at compile time.

-export function isValidPlatformFramework(platform: string, framework: string): boolean {
+export function isValidPlatformFramework(platform: PlatformName, framework: FrameworkName): boolean {
   const config = PLATFORMS.find(p => p.name === platform);
   if (!config) return false;
   return config.frameworks.includes(framework);
 }

34-39: Export shared storage keys to avoid drift.

Centralize the localStorage keys so UI/components don’t hardcode strings.

 export const DEFAULT_PLATFORM: PlatformName = 'JavaScript';
 export const DEFAULT_FRAMEWORK = 'Next.js';
+
+// Shared storage keys for platform/framework UI
+export const SELECTED_PLATFORM_KEY = 'stack-docs-selected-platform';
+export const SELECTED_FRAMEWORKS_KEY = 'stack-docs-selected-frameworks';

1-7: Avoid config duplication; make this the single source of truth.

There’s a second platform-config under docs/src/components/mdx. Prefer one module (export helpers here and re-export in MDX layer) to prevent divergence.

Also applies to: 21-32

docs/src/components/layouts/docs.tsx (1)

346-346: Extract hardcoded RGB colors to constants or design tokens.

The values rgb(59, 130, 246) and rgb(100, 116, 139) repeat across lines 346, 446, 602 (and 399, 515) without centralized definition. Extract to named constants in a shared file for maintainability, or integrate with a design token system if establishing one. Currently, no CSS variable or token system exists in the codebase to reference.

docs/src/components/mdx/platform-codeblock.tsx (2)

354-363: Simplify 32‑bit coercion in hash; current hash & hash is a no‑op.

Use | 0 for clarity or drop the line entirely.

-    for (let i = 0; i < hashString.length; i++) {
+    for (let i = 0; i < hashString.length; i++) {
       const char = hashString.charCodeAt(i);
       hash = ((hash << 5) - hash) + char;
-      hash = hash & hash; // Convert to 32-bit integer
+      hash |= 0; // force 32-bit int
     }

14-24: Prefer ES6 Map for key–value collections (state and helpers).

You’re modeling dictionaries with nested Records; Maps simplify updates and avoid accidental key conflicts, aligning with our guideline.

[As per coding guidelines]

Examples to consider (incremental, not blocking):

  • VariantSelections: Map<string, Map<string, 'server' | 'client'>>.
  • selectedFrameworks: Map<string, string> in state.
  • globalSelectedFrameworks: Map for global store. Convert to/from sessionStorage via Array.from(map.entries()).

Also applies to: 129-137, 239-247, 277-295, 503-511

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c1c3e5 and cc29fc3.

📒 Files selected for processing (13)
  • docs/code-examples/setup.ts (1 hunks)
  • docs/content/docs/(guides)/getting-started/setup.mdx (1 hunks)
  • docs/lib/platform-config.ts (1 hunks)
  • docs/next.config.mjs (1 hunks)
  • docs/src/app/(home)/page.tsx (1 hunks)
  • docs/src/app/api/layout.tsx (2 hunks)
  • docs/src/components/layouts/api/api-sidebar-wrapper.tsx (1 hunks)
  • docs/src/components/layouts/api/api-sidebar.tsx (4 hunks)
  • docs/src/components/layouts/docs.tsx (20 hunks)
  • docs/src/components/layouts/platform-indicator.tsx (1 hunks)
  • docs/src/components/layouts/shared-header.tsx (11 hunks)
  • docs/src/components/layouts/sidebar-context.tsx (4 hunks)
  • docs/src/components/mdx/platform-codeblock.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • docs/src/components/layouts/api/api-sidebar-wrapper.tsx
  • docs/lib/platform-config.ts
  • docs/src/app/(home)/page.tsx
  • docs/src/app/api/layout.tsx
  • docs/src/components/layouts/api/api-sidebar.tsx
  • docs/src/components/layouts/platform-indicator.tsx
  • docs/src/components/layouts/sidebar-context.tsx
  • docs/src/components/mdx/platform-codeblock.tsx
  • docs/src/components/layouts/docs.tsx
  • docs/src/components/layouts/shared-header.tsx
  • docs/code-examples/setup.ts
🧠 Learnings (1)
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
PR: stack-auth/stack-auth#943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.

Applied to files:

  • docs/src/components/mdx/platform-codeblock.tsx
🧬 Code graph analysis (8)
docs/src/components/layouts/api/api-sidebar-wrapper.tsx (1)
docs/src/components/layouts/sidebar-context.tsx (1)
  • useSidebar (46-49)
docs/src/app/api/layout.tsx (4)
docs/src/components/layouts/api-layout-wrapper.tsx (1)
  • ApiLayoutWrapper (7-19)
docs/src/components/layouts/docs-header-wrapper.tsx (1)
  • DocsHeaderWrapper (249-276)
docs/src/components/layouts/api/api-sidebar-wrapper.tsx (1)
  • ApiSidebarWrapper (7-23)
docs/src/components/layouts/shared-content-layout.tsx (1)
  • SharedContentLayout (19-52)
docs/src/components/layouts/api/api-sidebar.tsx (1)
docs/src/components/layout/theme-toggle.tsx (1)
  • ThemeToggle (26-111)
docs/src/components/layouts/platform-indicator.tsx (1)
docs/lib/platform-config.ts (5)
  • DEFAULT_PLATFORM (37-37)
  • DEFAULT_FRAMEWORK (38-38)
  • getFrameworksForPlatform (50-53)
  • PlatformName (8-8)
  • PLATFORMS (21-32)
docs/src/components/mdx/platform-codeblock.tsx (5)
docs/lib/platform-config.ts (3)
  • DEFAULT_PLATFORM (37-37)
  • DEFAULT_FRAMEWORK (38-38)
  • PLATFORMS (21-32)
docs/lib/code-examples.ts (2)
  • CodeExample (15-22)
  • getExample (31-31)
docs/code-examples/index.ts (1)
  • getExample (11-15)
docs/src/components/mdx/base-codeblock.tsx (1)
  • BaseCodeblock (34-166)
docs/src/components/icons.tsx (1)
  • ChevronDown (67-69)
docs/src/components/layouts/docs.tsx (1)
docs/src/components/layout/theme-toggle.tsx (1)
  • ThemeToggle (26-111)
docs/src/components/layouts/shared-header.tsx (3)
docs/src/components/layouts/sidebar-context.tsx (1)
  • useSidebar (46-49)
docs/src/components/layout/custom-search-dialog.tsx (1)
  • CustomSearchDialog (84-350)
docs/src/components/layouts/platform-indicator.tsx (1)
  • PlatformIndicator (15-222)
docs/code-examples/setup.ts (1)
docs/lib/code-examples.ts (1)
  • CodeExample (15-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build (22.x)
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: docker
  • GitHub Check: setup-tests
  • GitHub Check: restart-dev-and-test
  • GitHub Check: all-good
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: Security Check
🔇 Additional comments (15)
docs/next.config.mjs (1)

18-23: Destination page verified and redirect is correct.

The destination page exists at docs/content/docs/(guides)/overview.mdx, which correctly maps to /docs/overview via fumadocs routing. The redirect implementation is valid, properly uses permanent: false for temporary routing during refactoring, and the page has appropriate frontmatter and content. No issues found.

docs/code-examples/setup.ts (8)

1-36: LGTM!

The import and 'env-wizard' section are well-structured with consistent formatting and proper type annotations.


38-46: LGTM!

The install package commands are consistent and appropriate for each framework.


119-291: LGTM!

The stack configuration examples properly handle server/client variants and provide consistent patterns across frameworks.


293-356: LGTM!

The authentication handler examples are appropriate for each framework, with proper guidance for frameworks that don't use built-in handlers.


358-390: LGTM!

The provider setup examples are clear and appropriately reference earlier examples where applicable.


392-405: LGTM!

The loading boundary example is straightforward and follows Next.js conventions.


407-428: LGTM!

The suspense boundary example correctly demonstrates wrapping StackProvider with Suspense for async hooks.


430-508: LGTM!

The test setup commands are appropriate for each framework and include helpful API connection tests for Python frameworks.

docs/src/components/layouts/api/api-sidebar-wrapper.tsx (1)

7-23: LGTM! Clean wrapper implementation.

The component correctly handles the sidebar context with a safe fallback and implements responsive collapsible behavior. The transition classes ensure smooth width changes.

docs/src/components/layouts/api/api-sidebar.tsx (2)

241-286: LGTM! Well-structured collapsible section.

The CollapsibleSection component properly manages its open/closed state through the accordion context and handles both collapsed and expanded views appropriately.


832-948: Let me verify the component signature and type definitions for isMainSidebarCollapsed:

Based on my verification, the eslint-disable comment on line 894 for @typescript-eslint/no-unnecessary-condition is justified. The no-unnecessary-condition rule can be difficult to enable in projects that are not accurately typed or are susceptible to control flow analysis trade-offs, and ESLint disable comments are recommended for those specific situations.

In this case, isMainSidebarCollapsed is a boolean prop that can legitimately vary at runtime (representing whether the sidebar is in a collapsed or expanded state). The condition on line 895 {!isMainSidebarCollapsed ? (...) : (...)} is not unnecessary—it renders different UI based on the sidebar state. The disable comment is likely a workaround for a false positive from TypeScript's type narrowing in this specific render context.

No issues found. Code changes approved.

docs/src/app/api/layout.tsx (1)

93-129: Excellent layout refactoring!

The new wrapper-based layout structure is clean and well-composed. The use of ApiLayoutWrapper, ApiSidebarWrapper, and SharedContentLayout provides good separation of concerns and makes the layout more maintainable. The main content area properly uses flexbox for responsive behavior, and the sidebar/content relationship is clearly expressed.

docs/src/components/layouts/sidebar-context.tsx (1)

57-75: Confirm intent: should the large-screen TOC “default open” persist?

Current logic opens TOC on ≥768px only when no saved preference and doesn’t persist that open. If you want this default to be sticky across reloads, also set toc-open to true here; otherwise, keep as-is.

docs/content/docs/(guides)/getting-started/setup.mdx (1)

221-223: All intra-doc links and anchors verified as valid.

The referenced files and anchors exist correctly:

  • ./users.mdx → valid relative path to docs/content/docs/(guides)/getting-started/users.mdx
  • #protecting-a-page → anchor exists at line 49 of users.mdx as "## Protecting a page"
  • ../rest-api/overview.mdx → valid relative path to docs/content/docs/(guides)/rest-api/overview.mdx

No platform-scoped links (e.g., /docs/next/, /docs/react/) are present in the file under review.

@madster456 madster456 merged commit 8c805a8 into dev Oct 20, 2025
18 of 20 checks passed
@madster456 madster456 deleted the docs/rework/platforms branch October 20, 2025 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants