[Docs][Util][Content] - refactor docs to single source#919
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the WalkthroughReplaces 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
|
Generated with ❤️ by ellipsis.dev |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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(anumber) into a ref typed asNodeJS.Timeout, triggering theType 'number' is not assignable to type 'Timeout'compile error that’s causingpnpm run build docsto fail. Type the ref usingReturnType<typeof setTimeout>and guard againstnullso 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 callingsource.getPages()on every redirect.The helper function calls
source.getPages()on every request to/docswithout 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 : indexfor 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: MemoizedocsSectionto prevent unnecessary recalculations.Line 254 computes
docsSectionon every render. Since it only depends onpathnameand is used in thesectionTreememoization (line 255), wrapping it withuseMemowould 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_PREFIXis 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 renamingplatformColorto reflect its new purpose.The variable name
platformColoris now misleading since platform-specific logic has been removed. Consider renaming toaccentColor,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 forsrc/lib/platform-navigation.tsfrom 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 runfilterTreeForSectionwhen 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.mdxin 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 prependsdocs/contentglobally, so../../components/stack-provider.mdxis flagged “missing” even though it exists atdocs/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.mdxindocs/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 & Organizationsdocs/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
PlatformConfigdoesn'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
📒 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.tsdocs/src/components/sdk/overview.tsxdocs/src/components/layouts/docs-header-wrapper.tsxdocs/src/components/mdx/simple-platform-codeblock.tsxdocs/src/components/layouts/docs-layout-router.tsxdocs/src/app/api/search/route.tsdocs/src/components/homepage/iconHover.tsxdocs/src/components/layouts/shared/section-utils.tsdocs/src/components/layouts/docs.tsxdocs/src/components/mdx/platform-codeblock.tsxdocs/src/app/docs/[[...slug]]/page.tsxdocs/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
docs/content/docs/(guides)/concepts/auth-providers/x-twitter.mdx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
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: Useslice, not deprecatedsubstr, and prefercrypto.randomUUIDwhen 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:dangerouslySetInnerHTMLrequires sanitization or a lint suppression.If you adopt the sanitization above, this is resolved. Otherwise, add a documented
eslint/biomesuppression 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: ConsiderMapover index signatures for dynamic key–value state.
globalSelectedFrameworksandselectedFrameworksmodel dictionaries;Mapoffers 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
📒 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.tsxdocs/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 forrunAsynchronously.
runAsynchronouslyisn’t exposed by@stackframe/stack-shared’s public API—no re-export insrc/index.tsorpackage.jsonexports—so importing from/distis necessary until the package adds a root export.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
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"andaria-modal="true", with a clear label (e.g.,aria-labelledbyon 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
tabIndexas needed.- Opening and closing are announced to assistive technologies (via
aria-liveor similar).docs/src/components/layouts/shared-header.tsx (1)
30-41: Fix section matchers to new section-based routes; avoid “Guides” false-positivesCurrent 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-screenandh-[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
topPositionis now constant andheighthas minimal variation, theisHomePageandisScrolledstate tracking (and the associatedMutationObserver) 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
topPositionandheightnow resolve to constant values regardless ofisHomePageandisScrolled, the entire state tracking mechanism (including theMutationObserver) 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-labelattribute for screen readers, astitleattributes 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-contentSame 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 animationsAdd 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: MakeclassNameoptional on toggle componentsLoosen 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', + classNameAlso 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 pagesComment 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 tokenUse 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}40withhsl(var(--fd-primary) / 0.25), and borders with1px solid hsl(var(--fd-primary)).Also applies to: 445-446, 600-601
354-355: Prefer Link for navigation to preserve prefetch and accessibilityUsing 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
📒 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.tsxdocs/src/components/chat/ai-chat.tsxdocs/src/components/layouts/sidebar-context.tsxdocs/src/components/layouts/api/api-sidebar.tsxdocs/src/components/mdx/dynamic-code-block-overlay.tsxdocs/src/components/layouts/shared-header.tsxdocs/src/components/api/auth-panel.tsxdocs/src/app/api/layout.tsxdocs/src/components/layout/toc.tsxdocs/src/components/layouts/docs-header-wrapper.tsxdocs/src/components/layouts/page.tsxdocs/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)withsetChatExpanded(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
ArrowLeftimport 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
resolveDocsSectionandfilterTreeForSectionutilities- Properly memoizes derived values (
navLinks,docsSection,sectionTree)- Updates dependency arrays correctly to include
sectionTreeThe 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-autocenters the content horizontally- Removing
pt-14eliminates 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-6to the content container increases vertical spacing above the page content, providing better visual separation after the breadcrumb navigation. This complements the existing bottom margin.
There was a problem hiding this comment.
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.tsxResult: 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
There was a problem hiding this comment.
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.tsxResult: 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
|
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. |
There was a problem hiding this comment.
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:
- Load any page using PlatformCodeblock component
- Component initialization triggers useEffect before useState
- 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
N2D4
left a comment
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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/undefinedResult: 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
There was a problem hiding this comment.
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 asServerContactChannel[].- 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:#serveruserusecontactchannelsdocs/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.mdxpath. 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
servereven when only aclientvariant 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
suppressHydrationWarningattribute 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
RootProviderIf 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-storagedocs/content/docs/sdk/types/team.mdx (1)
780-787: Move example section to proper AsideSection wrapper.Line 782–787 places
### Examplesand 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 fencesOr 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
anyand the lint suppression. You can derive the correct prop type fromMDXComponents['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
platformsand 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, andselectedVariantsmodel maps; usingMapcan 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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.tsxdocs/src/lib/merge-refs.tsdocs/src/components/mdx/base-codeblock.tsxdocs/src/components/mdx/platform-codeblock.tsxdocs/src/components/mdx/sdk-components.tsxdocs/src/mdx-components.tsxdocs/src/components/mdx/dynamic-code-block.tsxdocs/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.currentafter 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.
There was a problem hiding this comment.
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), andstackServerApp(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
📒 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
There was a problem hiding this comment.
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.mjsshould prevent users from reaching this page, returningnullmeans 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
groupsfield inOrganizedSectionusesRecord<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
titleMapis 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
organizedPagesmemo builds a dynamic collection usingRecord<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'shas()andset()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 thesetChatOpenfunction 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
setChatExpandedbeforesetChatOpenis 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)andrgb(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; currenthash & hashis a no‑op.Use
| 0for 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
📒 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.tsxdocs/lib/platform-config.tsdocs/src/app/(home)/page.tsxdocs/src/app/api/layout.tsxdocs/src/components/layouts/api/api-sidebar.tsxdocs/src/components/layouts/platform-indicator.tsxdocs/src/components/layouts/sidebar-context.tsxdocs/src/components/mdx/platform-codeblock.tsxdocs/src/components/layouts/docs.tsxdocs/src/components/layouts/shared-header.tsxdocs/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/overviewvia fumadocs routing. The redirect implementation is valid, properly usespermanent: falsefor 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 forisMainSidebarCollapsed:Based on my verification, the
eslint-disablecomment on line 894 for@typescript-eslint/no-unnecessary-conditionis 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,
isMainSidebarCollapsedis 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, andSharedContentLayoutprovides 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-opentotruehere; 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 todocs/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 todocs/content/docs/(guides)/rest-api/overview.mdxNo platform-scoped links (e.g.,
/docs/next/,/docs/react/) are present in the file under review.
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
/contentfolder. 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
docs/package.jsondocs/src/lib/docs-tree.tsdocs/src/lib/navigation-utils.tsdocs/src/components/homepage/iconHover.tsxdocs/src/components/sdk/overview.tsxdocs/src/components/layouts/shared/section-utils.tsdocs/src/components/layout/custom-search-dialog.tsxdocs/src/app/api/search/route.tsdocs/src/app/docs/[[...slug]]/page.tsxdocs/src/components/layouts/docs-header-wrapper.tsxdocs/src/components/layouts/docs-layout-router.tsxdocs/src/components/layouts/docs.tsxpackage.jsonSummary by CodeRabbit
Documentation
Developer Experience