-
Notifications
You must be signed in to change notification settings - Fork 468
[Docs][Site] + [Dashboard][UI] - Adds docs to Stack Companion #869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
….ts to handle dashboard docs
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughReplaces the dashboard's inline docs UI with a new UnifiedDocsWidget and adds embed-first support across the docs site: embed routes/layouts/pages, MDX embedded components, embedded link interception and rewriting, a sidebar toggle via postMessage, dashboard docs source/config/generation, CORS headers for embed routes, env var for docs base URL, and embed-focused CSS. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Dashboard as Dashboard (StackCompanion)
participant Widget as UnifiedDocsWidget
participant Iframe as Docs Embed (iframe)
participant DocsApp as Docs App (Next.js)
User->>Dashboard: open "Docs" tab
Dashboard->>Widget: mount(isActive=true)
Widget->>Widget: resolve path, docType, platform -> get embed URL
Widget->>Iframe: set src = embed URL
Iframe-->>Widget: onload
Widget->>Widget: set iframeLoaded, enable controls
Widget->>Iframe: postMessage("TOGGLE_SIDEBAR")
User->>Widget: click Back
Widget->>Iframe: iframe.contentWindow.history.back()
alt history.back not available
Widget->>Iframe: reload iframe src
end
sequenceDiagram
autonumber
actor User
participant Iframe as Embedded Page
participant Interceptor as EmbeddedLinkInterceptor
participant Router as Embedded Router
User->>Iframe: click internal link
Interceptor->>Interceptor: compute embeddedHref
Interceptor->>Router: HEAD embeddedHref
alt Exists
Interceptor->>Router: client navigate(embeddedHref)
else Not found
Interceptor-->>User: prevent navigation (no-op)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Summary
This PR implements the "Stack Companion" documentation system, which embeds contextual documentation directly within the Stack Auth dashboard interface. The changes introduce a comprehensive embedded documentation architecture with three main components:
-
Documentation Templates and Content: New MDX documentation files are added in
docs/templates-dashboard/
covering all major dashboard sections (overview, users, teams, permissions, settings, auth-providers, analytics). These templates provide contextual help content that explains dashboard features and workflows. -
Embedded Documentation Infrastructure: The docs application is extended with embedded routes (
/docs-embed/
,/api-embed/
,/dashboard-embed/
) that render documentation content optimized for iframe embedding. This includes specialized layouts without navigation elements, embedded link interceptors that rewrite internal links to maintain embedded context, and CORS configuration to allow cross-origin access from the dashboard. -
Dashboard Integration: The dashboard's Stack Companion sidebar is enhanced with sophisticated documentation widgets (
UnifiedDocsWidget
andDashboardDocsWidget
) that automatically display relevant documentation based on the current dashboard page. These widgets support multiple documentation types, include navigation controls, and provide seamless switching between different documentation contexts.
The system uses fumadocs for content management, with a new dashboardSource
loader that processes dashboard-specific MDX content. The embedded documentation system maintains proper styling with Tailwind CSS classes optimized for constrained iframe spaces, including overflow management and responsive design considerations. The architecture allows users to access comprehensive help without leaving their current workflow, significantly improving the developer experience by reducing context switching between the dashboard and external documentation.
Confidence score: 2/5
- This PR introduces significant functionality but contains critical production-breaking issues with hardcoded localhost URLs
- Score reflects major deployment concerns that will cause complete failure in non-development environments
- Pay close attention to
unified-docs-widget.tsx
anddashboard-docs-widget.tsx
for hardcoded localhost:8104 URLs that must be resolved before deployment
26 files reviewed, 8 comments
apps/dashboard/src/components/stack-companion/dashboard-docs-widget.tsx
Outdated
Show resolved
Hide resolved
Review by RecurseML🔍 Review performed on 6a3459e..dde5282 ✨ No bugs found, your code is sparkling clean ✅ Files analyzed, no issues (5)• ⏭️ Files skipped (low suspicion) (21)• |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
♻️ Duplicate comments (1)
docs/src/app/api-embed/layout.tsx (1)
1-16
: Same embed-layout nits as docs-embed.Mirror the dvh change and verify
EmbeddedLinkInterceptor
is a client component as noted in docs-embed/layout.tsx.
🧹 Nitpick comments (42)
docs/templates-dashboard/permissions.mdx (1)
48-57
: Add a brief best-practices note and link the Companion.Strengthen the “Permission Validation” section with least-privilege guidance and make “Stack Companion” a clickable link to its entry point.
Apply:
## Permission Validation Ensure proper access control by: - Testing permission assignments - Reviewing access logs - Validating permission logic + +### Best Practices +- Prefer least-privilege roles; avoid granting broad project-wide permissions by default. +- Review effective permissions after team/role changes. +- Periodically audit dormant users and stale teams. ## Need Help? -Use the Stack Companion for detailed permission configuration guidance. +Use the [Stack Companion](</dashboard-embed/companion>) for detailed permission configuration guidance.docs/templates-dashboard/settings.mdx (1)
28-48
: Clarify duplicated “Rate limiting” bullets.“Rate limiting” appears under both Security Settings and API Configuration; clarify scope or deduplicate to avoid confusion.
Option A (clarify scopes):
### Security Settings Configure security policies: - Password requirements - Multi-factor authentication - Session management -- Rate limiting +- Authentication rate limiting (login/signup, OTP attempts) ### API Configuration Manage API access: - API keys - Webhook endpoints - CORS settings -- Rate limiting +- API rate limiting (per-key/per-route quotas)Option B (deduplicate to Security Settings only and replace API bullet):
- - Rate limiting + - SDK configuration (environments, base URLs)docs/templates-dashboard/users.mdx (2)
2-7
: Name consistency: prefer “User Management”.Use singular form to align with common docs conventions and sidebar patterns (“User Management”).
-title: Users Management +title: User Management @@ -# Users Management +# User Management
29-35
: Add privacy/audit guidance when editing user data.A short caution helps set expectations around PII and compliance.
### Managing User Data You can update user information including: - Profile details - Custom metadata - Contact preferences - Account status +> Note: Limit access to PII to authorized roles only. Changes should be auditable (record who changed what and when).
docs/templates-dashboard/teams.mdx (1)
2-7
: Name consistency: prefer “Team Management”.Match singular form used elsewhere.
-title: Teams Management +title: Team Management @@ -# Teams Management +# Team Managementdocs/templates-dashboard/auth-providers.mdx (5)
21-26
: Name the provider consistently as “X (formerly Twitter)”.Minor branding nit for clarity.
-- **Twitter/X**: Social media integration +- **X (formerly Twitter)**: Social media integration
27-31
: Clarify SAML is a protocol and mention common IdPs.Prevents confusion vs. OAuth “providers.”
-### Enterprise Providers -- **Microsoft**: Azure AD and Office 365 integration -- **SAML**: Enterprise single sign-on -- **LinkedIn**: Professional network integration +### Enterprise Providers +- **Microsoft Entra ID (Azure AD)**: Enterprise Microsoft login +- **SAML (via IdPs like Okta, OneLogin, Entra ID, Auth0)**: Enterprise single sign‑on +- **LinkedIn**: Professional network integration
32-38
: Add critical OAuth setup steps (security + reliability).PKCE, exact redirect URIs, and environment-specific callbacks are easy to miss.
### Configuration Steps -1. **Create OAuth Application**: Set up your app with the provider -2. **Configure Credentials**: Add client ID and secret to Stack Auth -3. **Set Callback URLs**: Configure redirect URLs for your application -4. **Test Integration**: Verify the authentication flow works correctly +1. **Create OAuth Application**: Set up your app with the provider +2. **Configure Credentials**: Add client ID and secret to Stack Auth (store secrets server-side only) +3. **Set Callback URLs**: Add exact authorized redirect URIs per environment (e.g., dev, staging, prod) +4. **Enable PKCE** (recommended) and required grant types per provider +5. (If supported) **Set post‑logout redirect URIs** +6. **Test Integration**: Verify success, error, and cancel flows
41-47
: Call out post‑logout URIs and least‑privilege scopes.Keeps deployments secure by default.
### OAuth Configuration Each provider requires: - Client ID - Client Secret - Authorized redirect URIs - - Scope permissions + - (If supported) Post‑logout redirect URIs + - Scope permissions (use least privilege; request only what you need)
54-57
: Link to provider‑specific runbooks from Companion.If you have deep links (Google/GitHub/etc.), adding them here reduces bounce.
I can add Companion deeplinks for top providers if you share their slugs.
docs/src/components/mdx/embedded-link.tsx (3)
9-26
: Harden path rewriting and handle “/docs” root, queries, and hashes.Current replace() misses “/docs” without trailing slash and query/hash forms; also guard against re‑rewriting embedded routes.
-const getEmbeddedUrl = (href: string): string => { +const getEmbeddedUrl = (href: string): string => { // Handle relative links if (href.startsWith('/')) { - // Convert regular doc routes to embedded routes - if (href.startsWith('/docs/')) { - return href.replace('/docs/', '/docs-embed/'); - } - if (href.startsWith('/api/')) { - return href.replace('/api/', '/api-embed/'); - } - if (href.startsWith('/dashboard/')) { - return href.replace('/dashboard/', '/dashboard-embed/'); - } + // Skip if already embedded + if (href.startsWith('/docs-embed/') || href.startsWith('/api-embed/') || href.startsWith('/dashboard-embed/')) { + return href; + } + // Convert regular routes to embedded routes (match with or without trailing slash) + if (href === '/docs' || href.startsWith('/docs/') || href.startsWith('/docs?') || href.startsWith('/docs#')) { + return href.replace(/^\/docs(?=\/|$)/, '/docs-embed'); + } + if (href === '/api' || href.startsWith('/api/') || href.startsWith('/api?') || href.startsWith('/api#')) { + return href.replace(/^\/api(?=\/|$)/, '/api-embed'); + } + if (href === '/dashboard' || href.startsWith('/dashboard/') || href.startsWith('/dashboard?') || href.startsWith('/dashboard#')) { + return href.replace(/^\/dashboard(?=\/|$)/, '/dashboard-embed'); + } } // Return unchanged for external links or already embedded links return href; };
45-47
: Protect target=_blank links against reverse‑tabnabbing.Add rel when opening new tabs.
- // For external links, use regular anchor tag - return <a href={embeddedHref} {...props}>{children}</a>; + // For external links, use regular anchor tag + const rel = props.target === '_blank' + ? [props.rel, 'noopener', 'noreferrer'].filter(Boolean).join(' ') + : props.rel; + return <a href={embeddedHref} {...props} rel={rel}>{children}</a>;
8-26
: Deduplicate getEmbeddedUrl across the codebase.If a similar helper exists in embedded-link-interceptor.tsx, consider exporting a shared util to avoid drift.
Would you like me to extract a shared helper (e.g., docs/src/lib/embedded-url.ts) and update both call sites?
docs/templates-dashboard/analytics.mdx (2)
46-51
: Verify feature availability (“CSV export”, “PDF generation”, “API access”, “Custom dashboard”).If any are roadmap-only, mark as “coming soon” or scope by plan to avoid overpromising.
I can update copy once you confirm which features ship today vs. later.
21-37
: Tighten metric definitions (time windows, filters).Add brief definitions (e.g., DAU/MAU timezones, sign‑in “success” criteria) to reduce ambiguity.
docs/src/app/global.css (3)
198-205
: Be cautious hiding scrollbars (a11y).Hidden scrollbars can hurt discoverability and keyboard users. Limit usage to the embed container only, and ensure visible focus/scroll affordances.
Do all elements with .scrollbar-hide still meet WCAG 2.2 focus visibility?
241-249
: Avoid aggressive word-break on links/code.
word-break: break-all
can shatter readable text. Prefer overflow-wrap:anywhere for long tokens/URLs.-.prose a[href*="://"] { - word-break: break-all; -} +.prose a[href*="://"] { + overflow-wrap: anywhere; + word-break: break-word; +} /* Break very long strings that have no spaces (like tokens, hashes, etc.) */ -.prose code:not(pre code) { - word-break: break-all; -} +.prose code:not(pre code) { + overflow-wrap: anywhere; + word-break: break-word; + hyphens: auto; +}
227-233
: Consider momentum scrolling on narrow tables.Improves mobile UX inside embeds.
.prose table { /* Make tables responsive */ display: block; overflow-x: auto; white-space: nowrap; max-width: 100%; + -webkit-overflow-scrolling: touch; }
apps/dashboard/src/components/stack-companion/dashboard-docs-widget.tsx (7)
16-26
: Remove unused ROUTE_TO_DOC_MAP and prefer ES6 Map per guidelinesThis constant isn’t referenced; drop it to avoid drift. Also, for future lookups, prefer Map over Record.
-// Map dashboard routes to documentation pages -const ROUTE_TO_DOC_MAP: Record<string, string> = { - '/users': 'users', - '/teams': 'teams', - '/permissions': 'permissions', - '/settings': 'settings', - '/auth-providers': 'auth-providers', - '/analytics': 'analytics', - '/': 'overview', - '': 'overview', -}; +// (removed) Unused; route resolution happens via DASHBOARD_ROUTE_PATTERNS.
29-37
: Switch TITLE_MAP to ES6 MapAligns with repo guideline and avoids accidental key collisions.
-// Title mapping for pages -const TITLE_MAP: Record<string, string> = { - 'overview': 'Dashboard Overview', - 'users': 'Users Management', - 'teams': 'Teams Management', - 'permissions': 'Permissions Management', - 'settings': 'Project Settings', - 'auth-providers': 'Authentication Providers', - 'analytics': 'Analytics & Insights', -}; +// Title mapping for pages +const TITLE_MAP = new Map<string, string>([ + ['overview', 'Dashboard Overview'], + ['users', 'Users Management'], + ['teams', 'Teams Management'], + ['permissions', 'Permissions Management'], + ['settings', 'Project Settings'], + ['auth-providers', 'Authentication Providers'], + ['analytics', 'Analytics & Insights'], +]);Follow-ups needed where the map is read; see lines 75 and 212.
39-48
: Auth route mapping: handle both /auth-methods and /auth-providersIf the app ever routes to /auth-providers directly, we’ll miss it. Add an explicit pattern.
{ pattern: /\/settings(?:\/.*)?$/, docPage: 'settings' }, { pattern: /\/auth-methods(?:\/.*)?$/, docPage: 'auth-providers' }, // Route is auth-methods but docs are auth-providers + { pattern: /\/auth-providers(?:\/.*)?$/, docPage: 'auth-providers' }, { pattern: /\/analytics(?:\/.*)?$/, docPage: 'analytics' },
Please confirm actual dashboard paths.
191-199
: Harden iframe attrs and improve UXAdd lazy loading, referrer policy, and popup allowances (if embedded links open windows).
- <iframe + <iframe src={docContent.url} className="w-full h-full border-0 rounded-md bg-white dark:bg-gray-900" onLoad={handleIframeLoad} onError={handleIframeError} title={`Documentation: ${docContent.title}`} - sandbox="allow-scripts allow-same-origin" + sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox" + referrerPolicy="strict-origin-when-cross-origin" + loading="lazy" />If popups aren’t desired from inside the embed, drop the allow-popups flags.
75-75
: Update title access for MapFollow-up to Map switch.
- const title = TITLE_MAP[page] || `Dashboard ${page}`; + const title = TITLE_MAP.get(page) ?? `Dashboard ${page}`;
212-213
: Update prompt title access for MapFollow-up to Map switch.
- Switch to <span className="font-medium">{TITLE_MAP[getDashboardPage(pathname)] || 'current page'}</span>? + Switch to <span className="font-medium">{TITLE_MAP.get(getDashboardPage(pathname)) ?? 'current page'}</span>?
90-116
: Simplify effect logic to avoid extra runsCurrent deps (docContent/currentPageDoc) can retrigger the effect; splitting “initial load” and “route change while active” into two effects will reduce state juggling.
I can provide a small refactor if you want it applied here.
apps/dashboard/src/components/stack-companion.tsx (2)
12-12
: Lazy-load UnifiedDocsWidget to cut initial bundleAvoid pulling the docs embed code until the Docs tab is opened.
Example:
import dynamic from 'next/dynamic'; // … const UnifiedDocsWidget = dynamic( () => import('./stack-companion/unified-docs-widget').then(m => m.UnifiedDocsWidget), { ssr: false } );
366-366
: Render condition is fine; consider passing isActive=activeItem==='docs' for clarityNo behavior change; slightly clearer intent and future-proof if reused.
- <UnifiedDocsWidget isActive={true} /> + <UnifiedDocsWidget isActive={activeItem === 'docs'} />docs/lib/source.ts (1)
42-50
: Confirm /dashboard-embed loader exists (or add it here)Embeds point to /dashboard-embed/. If that route isn’t defined elsewhere, add a companion loader to mirror /dashboard with embedded components.
Example (add alongside):
export const dashboardEmbedSource = loader({ baseUrl: '/dashboard-embed', source: dashboard.toFumadocsSource(), pageTree: { attachFile }, icon: createIconResolver(), });docs/templates-dashboard/overview.mdx (1)
14-19
: Link the quick navigation items to their pagesImproves discoverability and verifies routes.
- - **Users**: Manage user accounts, view user details, and handle user-related operations + - **[Users](/dashboard/users)**: Manage user accounts, view user details, and handle user-related operations - - **Teams**: Create and manage teams, handle team memberships and permissions + - **[Teams](/dashboard/teams)**: Create and manage teams, handle team memberships and permissions - - **Permissions**: Configure role-based access control and user permissions + - **[Permissions](/dashboard/permissions)**: Configure role-based access control and user permissions - - **Settings**: Configure your project settings, authentication providers, and integrations + - **[Settings](/dashboard/settings)**: Configure your project settings, authentication providers, and integrations - - **Analytics**: View authentication metrics and user engagement data + - **[Analytics](/dashboard/analytics)**: View authentication metrics and user engagement dataIf the embed uses a custom link component, swap for it as appropriate.
docs/src/app/docs-embed/[[...slug]]/page.tsx (2)
5-11
: Fix params typing; Next.js App Router does not pass a Promise for params
params
should be a plain object. You don't needawait params
. This also improves type-safety and consistency with Next types.-export default async function DocsEmbedPage({ +export default async function DocsEmbedPage({ params, }: { - params: Promise<{ slug?: string[] }>, + params: { slug?: string[] }, }) { - const { slug } = await params; + const { slug } = params;
3-3
: Prefer notFound() over redirect("/") for missing pagesUse
notFound()
to return a proper 404 and avoid unexpected cross-origin redirects inside embeds.-import { redirect } from 'next/navigation'; +import { notFound } from 'next/navigation'; ... - if (!page) redirect("/"); + if (!page) notFound();Also applies to: 13-13
docs/src/app/api-embed/[[...slug]]/page.tsx (2)
5-11
: Align params typing with Next.js conventionsSame as docs-embed:
params
should not be a Promise; dropawait
.-export default async function ApiEmbedPage({ +export default async function ApiEmbedPage({ params, }: { - params: Promise<{ slug?: string[] }>, + params: { slug?: string[] }, }) { - const { slug } = await params; + const { slug } = params;
3-3
: Return 404 instead of redirect for missing pagesPrefer
notFound()
for correct HTTP semantics in embeds.-import { redirect } from 'next/navigation'; +import { notFound } from 'next/navigation'; ... - if (!page) redirect("/"); + if (!page) notFound();Also applies to: 13-13
docs/src/app/dashboard-embed/layout.tsx (1)
6-14
: Use dynamic viewport units to avoid mobile "100vh" issuesReplace
h-screen/min-h-screen
with 100dvh to prevent overflow under mobile browser UI.- <div className="min-h-screen bg-fd-background"> + <div className="min-h-[100dvh] bg-fd-background"> ... - <main className="h-screen overflow-hidden"> + <main className="h-[100dvh] overflow-hidden">docs/src/app/docs-embed/layout.tsx (1)
9-11
: Use dvh to avoid mobile viewport bugs.Replace
h-screen
/h-full
withh-dvh
(and keep the inneroverflow-y-auto
) to avoid iOS Safari 100vh issues in iframes.Apply:
- <main className="h-screen overflow-hidden"> - <div className="h-full overflow-y-auto overflow-x-hidden scrollbar-hide"> + <main className="h-dvh overflow-hidden"> + <div className="h-dvh overflow-y-auto overflow-x-hidden scrollbar-hide">docs/scripts/generate-docs.js (1)
368-417
: Include dashboard static assets (images) alongside MDX.
generateDashboardDocs()
copies only.mdx
and optionalmeta.json
. If dashboard docs reference images (e.g.,imgs
), they’ll 404.Append inside
generateDashboardDocs()
after writingmeta.json
:console.log('Dashboard documentation generation complete!'); } +// Copy dashboard asset dirs if present (parity with copyAssets()) +const DASHBOARD_ASSET_DIRS = ['imgs']; +for (const dir of DASHBOARD_ASSET_DIRS) { + const srcDir = path.join(DASHBOARD_TEMPLATE_DIR, dir); + if (fs.existsSync(srcDir)) { + const files = glob.sync('**/*', { cwd: srcDir, nodir: true }); + for (const file of files) { + const srcFile = path.join(srcDir, file); + const destFile = path.join(DASHBOARD_OUTPUT_DIR, dir, file); + fs.mkdirSync(path.dirname(destFile), { recursive: true }); + fs.copyFileSync(srcFile, destFile); + console.log(`Copied dashboard asset: ${srcFile} -> ${destFile}`); + } + } +}apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (2)
20-29
: Prefer ES6 Map over Record for title map (per repo guideline).Use
Map
for key lookups; adjust call site accordingly.Apply:
-const DASHBOARD_TITLE_MAP: Record<string, string> = { - 'overview': 'Dashboard Overview', - 'users': 'Users Management', - 'teams': 'Teams Management', - 'permissions': 'Permissions Management', - 'settings': 'Project Settings', - 'auth-providers': 'Authentication Providers', - 'analytics': 'Analytics & Insights', -}; +const DASHBOARD_TITLE_MAP = new Map<string, string>([ + ['overview', 'Dashboard Overview'], + ['users', 'Users Management'], + ['teams', 'Teams Management'], + ['permissions', 'Permissions Management'], + ['settings', 'Project Settings'], + ['auth-providers', 'Authentication Providers'], + ['analytics', 'Analytics & Insights'], +]);- const title = DASHBOARD_TITLE_MAP[page] || `Dashboard ${page}`; + const title = DASHBOARD_TITLE_MAP.get(page) ?? `Dashboard ${page}`;Also applies to: 88-89
58-75
: Remove unused DOC_TYPE_OPTIONS or wire it to UI.It’s currently unused; trim to reduce dead code or integrate into a selector.
Apply:
-// Navigation options for different doc types -const DOC_TYPE_OPTIONS: Array<{ type: DocType, label: string, description: string }> = [ - { - type: 'dashboard', - label: 'Dashboard Docs', - description: 'Documentation for this dashboard page' - }, - { - type: 'docs', - label: 'Main Docs', - description: 'Complete Stack Auth documentation' - }, - { - type: 'api', - label: 'API Reference', - description: 'API endpoints and reference' - }, -];docs/src/components/embedded-link-interceptor.tsx (4)
80-86
: Remove unused navigation history state.
setNavigationHistory
is write-only and not rendered. Trim for clarity.- const [, setNavigationHistory] = useState<string[]>([]); - - // Initialize navigation history with current URL - useEffect(() => { - setNavigationHistory([window.location.pathname]); - }, []); + // (removed) navigation history state- // Add to navigation history - setNavigationHistory(prev => [...prev, embeddedHref]); + // navigation history removedAlso applies to: 140-142
127-133
: Guard console logging or remove.Avoid noisy logs in production.
- // Debug logging - console.log('Link Debug:', { - originalHref: href, - currentPath, - resolvedHref: embeddedHref - }); + // Debug logging (guarded) + if (process.env.NEXT_PUBLIC_EMBED_DEBUG === '1') { + console.debug('Link Debug:', { originalHref: href, currentPath, resolvedHref: embeddedHref }); + }
171-181
: Hide the debug UI behind an env flag to avoid exposing it to end users.Gate the toggle/window with
NEXT_PUBLIC_EMBED_DEBUG === '1'
.- {/* Debug Toggle Button */} - <div className="fixed top-4 right-4 z-50"> + {process.env.NEXT_PUBLIC_EMBED_DEBUG === '1' && ( + {/* Debug Toggle Button */} + <div className="fixed top-4 right-4 z-50"> <button onClick={() => setShowDebug(!showDebug)} className="px-3 py-2 bg-gray-800 hover:bg-gray-700 text-white text-xs font-medium rounded-md shadow-lg transition-colors" title="Toggle debug window" > Debug {showDebug ? '▼' : '▲'} </button> </div> - {/* Debug Window */} - {showDebug && ( + {/* Debug Window */} + {showDebug && ( <div className="fixed top-16 right-4 w-96 max-h-96 bg-gray-900 text-white text-xs rounded-lg shadow-2xl z-50 overflow-hidden"> ... </div> - )} + )} + )}Also applies to: 183-257
5-67
: Extract shared URL‐rewriting helper. Bothembedded-link-interceptor.tsx
andmdx/embedded-link.tsx
duplicate the/docs/
,/api/
, and/dashboard/
→-embed/
mappings—pull that logic into a common util to keep them in sync.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (26)
apps/dashboard/src/components/stack-companion.tsx
(2 hunks)apps/dashboard/src/components/stack-companion/dashboard-docs-widget.tsx
(1 hunks)apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
(1 hunks)docs/.gitignore
(1 hunks)docs/lib/source.ts
(2 hunks)docs/next.config.mjs
(1 hunks)docs/scripts/generate-docs.js
(3 hunks)docs/source.config.ts
(1 hunks)docs/src/app/api-embed/[[...slug]]/page.tsx
(1 hunks)docs/src/app/api-embed/layout.tsx
(1 hunks)docs/src/app/dashboard-embed/[[...slug]]/page.tsx
(1 hunks)docs/src/app/dashboard-embed/layout.tsx
(1 hunks)docs/src/app/docs-embed/[[...slug]]/page.tsx
(1 hunks)docs/src/app/docs-embed/layout.tsx
(1 hunks)docs/src/app/global.css
(1 hunks)docs/src/components/embedded-link-interceptor.tsx
(1 hunks)docs/src/components/mdx/embedded-link.tsx
(1 hunks)docs/src/mdx-components.tsx
(3 hunks)docs/templates-dashboard/analytics.mdx
(1 hunks)docs/templates-dashboard/auth-providers.mdx
(1 hunks)docs/templates-dashboard/meta.json
(1 hunks)docs/templates-dashboard/overview.mdx
(1 hunks)docs/templates-dashboard/permissions.mdx
(1 hunks)docs/templates-dashboard/settings.mdx
(1 hunks)docs/templates-dashboard/teams.mdx
(1 hunks)docs/templates-dashboard/users.mdx
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Prefer ES6 Map over Record where feasible
Files:
docs/src/app/dashboard-embed/layout.tsx
apps/dashboard/src/components/stack-companion.tsx
docs/source.config.ts
docs/lib/source.ts
docs/src/mdx-components.tsx
docs/src/components/mdx/embedded-link.tsx
docs/src/app/docs-embed/layout.tsx
docs/src/app/api-embed/layout.tsx
docs/src/app/dashboard-embed/[[...slug]]/page.tsx
docs/src/app/api-embed/[[...slug]]/page.tsx
docs/src/app/docs-embed/[[...slug]]/page.tsx
docs/src/components/embedded-link-interceptor.tsx
apps/dashboard/src/components/stack-companion/dashboard-docs-widget.tsx
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
🧬 Code graph analysis (8)
docs/src/app/dashboard-embed/layout.tsx (1)
docs/src/components/embedded-link-interceptor.tsx (1)
EmbeddedLinkInterceptor
(77-260)
docs/src/mdx-components.tsx (1)
docs/src/components/mdx/embedded-link.tsx (1)
EmbeddedLink
(28-47)
docs/src/app/docs-embed/layout.tsx (1)
docs/src/components/embedded-link-interceptor.tsx (1)
EmbeddedLinkInterceptor
(77-260)
docs/src/app/api-embed/layout.tsx (1)
docs/src/components/embedded-link-interceptor.tsx (1)
EmbeddedLinkInterceptor
(77-260)
docs/src/app/dashboard-embed/[[...slug]]/page.tsx (1)
docs/src/mdx-components.tsx (1)
getEmbeddedMDXComponents
(88-94)
docs/src/app/api-embed/[[...slug]]/page.tsx (1)
docs/src/mdx-components.tsx (1)
getEmbeddedMDXComponents
(88-94)
docs/src/app/docs-embed/[[...slug]]/page.tsx (1)
docs/src/mdx-components.tsx (1)
getEmbeddedMDXComponents
(88-94)
docs/next.config.mjs (1)
apps/dashboard/next.config.mjs (1)
async headers() {
(94-127)
🪛 LanguageTool
docs/templates-dashboard/settings.mdx
[grammar] ~14-~14: There might be a mistake here.
Context: ...c.) - Security policies and requirements - Email templates and branding - API keys ...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...uirements - Email templates and branding - API keys and webhooks - Project integrat...
(QB_NEW_EN)
[grammar] ~16-~16: There might be a mistake here.
Context: ...tes and branding - API keys and webhooks - Project integrations ## Configuration A...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ...Microsoft, etc. - Custom OAuth providers - Provider-specific settings - Callback UR...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ...h providers - Provider-specific settings - Callback URL configuration ### Security...
(QB_NEW_EN)
[grammar] ~28-~28: There might be a mistake here.
Context: ...URL configuration ### Security Settings Configure security policies: - Password ...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ...ty Settings Configure security policies: - Password requirements - Multi-factor aut...
(QB_NEW_EN)
[grammar] ~30-~30: There might be a mistake here.
Context: ...curity policies: - Password requirements - Multi-factor authentication - Session ma...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ...quirements - Multi-factor authentication - Session management - Rate limiting ### ...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...ctor authentication - Session management - Rate limiting ### Branding & Templates ...
(QB_NEW_EN)
[grammar] ~35-~35: There might be a mistake here.
Context: ... Rate limiting ### Branding & Templates Customize the user experience: - Email t...
(QB_NEW_EN)
[grammar] ~36-~36: There might be a mistake here.
Context: ...Templates Customize the user experience: - Email templates - Login page branding - ...
(QB_NEW_EN)
[grammar] ~37-~37: There might be a mistake here.
Context: ...e the user experience: - Email templates - Login page branding - Custom domains - T...
(QB_NEW_EN)
[grammar] ~38-~38: There might be a mistake here.
Context: ... - Email templates - Login page branding - Custom domains - Theme configuration ##...
(QB_NEW_EN)
[grammar] ~39-~39: There might be a mistake here.
Context: ...s - Login page branding - Custom domains - Theme configuration ### API Configurati...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...eme configuration ### API Configuration Manage API access: - API keys - Webhook ...
(QB_NEW_EN)
[grammar] ~43-~43: There might be a mistake here.
Context: ...### API Configuration Manage API access: - API keys - Webhook endpoints - CORS sett...
(QB_NEW_EN)
[grammar] ~44-~44: There might be a mistake here.
Context: ...figuration Manage API access: - API keys - Webhook endpoints - CORS settings - Rate...
(QB_NEW_EN)
[grammar] ~45-~45: There might be a mistake here.
Context: ...I access: - API keys - Webhook endpoints - CORS settings - Rate limiting ## Need H...
(QB_NEW_EN)
[grammar] ~46-~46: There might be a mistake here.
Context: ...keys - Webhook endpoints - CORS settings - Rate limiting ## Need Help? Consult th...
(QB_NEW_EN)
docs/templates-dashboard/analytics.mdx
[grammar] ~12-~12: There might be a mistake here.
Context: ...erview Track key metrics to understand: - User authentication patterns - Sign-up a...
(QB_NEW_EN)
[grammar] ~13-~13: There might be a mistake here.
Context: ...derstand: - User authentication patterns - Sign-up and conversion rates - Popular a...
(QB_NEW_EN)
[grammar] ~14-~14: There might be a mistake here.
Context: ... patterns - Sign-up and conversion rates - Popular authentication methods - User en...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...n rates - Popular authentication methods - User engagement trends - System performa...
(QB_NEW_EN)
[grammar] ~16-~16: There might be a mistake here.
Context: ...ication methods - User engagement trends - System performance metrics ## Key Metri...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...ey Metrics ### Authentication Analytics - Daily/Monthly Active Users: Track user...
(QB_NEW_EN)
[grammar] ~27-~27: There might be a mistake here.
Context: ...ders are most popular ### User Behavior - Session Duration: Understand user enga...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ...users' platforms ### System Performance - API Response Times: Monitor system per...
(QB_NEW_EN)
[grammar] ~40-~40: There might be a mistake here.
Context: ...ata Visualization ### Charts and Graphs - Time series data for trends - Pie charts...
(QB_NEW_EN)
[grammar] ~41-~41: There might be a mistake here.
Context: ...and Graphs - Time series data for trends - Pie charts for distribution analysis - B...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...s - Pie charts for distribution analysis - Bar charts for comparative metrics - Hea...
(QB_NEW_EN)
[grammar] ~43-~43: There might be a mistake here.
Context: ...sis - Bar charts for comparative metrics - Heatmaps for geographic data ### Export...
(QB_NEW_EN)
[grammar] ~46-~46: There might be a mistake here.
Context: ... for geographic data ### Export Options - CSV data export - PDF report generation ...
(QB_NEW_EN)
[grammar] ~47-~47: There might be a mistake here.
Context: ...ta ### Export Options - CSV data export - PDF report generation - API access to an...
(QB_NEW_EN)
[grammar] ~48-~48: There might be a mistake here.
Context: ... CSV data export - PDF report generation - API access to analytics data - Custom da...
(QB_NEW_EN)
[grammar] ~49-~49: There might be a mistake here.
Context: ...eneration - API access to analytics data - Custom dashboard creation ## Need Help?...
(QB_NEW_EN)
docs/templates-dashboard/permissions.mdx
[grammar] ~12-~12: There might be a mistake here.
Context: ...access to your application resources by: - Defining custom permissions - Creating r...
(QB_NEW_EN)
[grammar] ~13-~13: There might be a mistake here.
Context: ...ources by: - Defining custom permissions - Creating roles and permission sets - Ass...
(QB_NEW_EN)
[grammar] ~14-~14: There might be a mistake here.
Context: ...ons - Creating roles and permission sets - Assigning permissions to users and teams...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...Assigning permissions to users and teams - Managing permission inheritance ## Perm...
(QB_NEW_EN)
[grammar] ~20-~20: There might be a mistake here.
Context: ...ermission Types ### Project Permissions Global permissions that apply across you...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...s that apply across your entire project: - Admin access - User management - Team ma...
(QB_NEW_EN)
[grammar] ~22-~22: There might be a mistake here.
Context: ...ross your entire project: - Admin access - User management - Team management - Sett...
(QB_NEW_EN)
[grammar] ~23-~23: There might be a mistake here.
Context: ...roject: - Admin access - User management - Team management - Settings configuration...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ...cess - User management - Team management - Settings configuration ### Team Permiss...
(QB_NEW_EN)
[grammar] ~27-~27: There might be a mistake here.
Context: ...ings configuration ### Team Permissions Permissions specific to team contexts: -...
(QB_NEW_EN)
[grammar] ~28-~28: There might be a mistake here.
Context: ...s Permissions specific to team contexts: - Team membership management - Team resour...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ...m contexts: - Team membership management - Team resource access - Team-specific ope...
(QB_NEW_EN)
[grammar] ~30-~30: There might be a mistake here.
Context: ...ership management - Team resource access - Team-specific operations ### Custom Per...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ...cific operations ### Custom Permissions Define your own permissions for: - Appli...
(QB_NEW_EN)
[grammar] ~34-~34: There might be a mistake here.
Context: ...issions Define your own permissions for: - Application-specific features - Resource...
(QB_NEW_EN)
[grammar] ~35-~35: There might be a mistake here.
Context: ...ons for: - Application-specific features - Resource-level access control - Custom b...
(QB_NEW_EN)
[grammar] ~36-~36: There might be a mistake here.
Context: ...features - Resource-level access control - Custom business logic ## Managing Permi...
(QB_NEW_EN)
[grammar] ~41-~41: There might be a mistake here.
Context: ...g Permissions ### Assigning Permissions Grant permissions through: - Direct user...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...g Permissions Grant permissions through: - Direct user assignment - Team membership...
(QB_NEW_EN)
[grammar] ~43-~43: There might be a mistake here.
Context: ...ssions through: - Direct user assignment - Team membership - Role-based assignment ...
(QB_NEW_EN)
[grammar] ~44-~44: There might be a mistake here.
Context: ...Direct user assignment - Team membership - Role-based assignment - Permission inher...
(QB_NEW_EN)
[grammar] ~45-~45: There might be a mistake here.
Context: ... Team membership - Role-based assignment - Permission inheritance ### Permission V...
(QB_NEW_EN)
[grammar] ~48-~48: There might be a mistake here.
Context: ...n inheritance ### Permission Validation Ensure proper access control by: - Testi...
(QB_NEW_EN)
[grammar] ~49-~49: There might be a mistake here.
Context: ...idation Ensure proper access control by: - Testing permission assignments - Reviewi...
(QB_NEW_EN)
docs/templates-dashboard/teams.mdx
[grammar] ~12-~12: There might be a mistake here.
Context: ...ontrol at scale. From this page you can: - Create new teams - Manage team membershi...
(QB_NEW_EN)
[grammar] ~20-~20: There might be a mistake here.
Context: ... ## Team Operations ### Creating Teams Set up new teams with: - Team name and d...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...## Creating Teams Set up new teams with: - Team name and description - Initial memb...
(QB_NEW_EN)
[grammar] ~22-~22: There might be a mistake here.
Context: ... teams with: - Team name and description - Initial members - Default permissions - ...
(QB_NEW_EN)
[grammar] ~23-~23: There might be a mistake here.
Context: ...m name and description - Initial members - Default permissions - Team settings ###...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ... - Initial members - Default permissions - Team settings ### Managing Members Cont...
(QB_NEW_EN)
[grammar] ~27-~27: There might be a mistake here.
Context: ...ns - Team settings ### Managing Members Control team membership by: - Adding new...
(QB_NEW_EN)
[grammar] ~28-~28: There might be a mistake here.
Context: ...ging Members Control team membership by: - Adding new members - Removing members - ...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ...team membership by: - Adding new members - Removing members - Changing member roles...
(QB_NEW_EN)
[grammar] ~30-~30: There might be a mistake here.
Context: ... - Adding new members - Removing members - Changing member roles - Setting team-spe...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ...Removing members - Changing member roles - Setting team-specific permissions ### T...
(QB_NEW_EN)
[grammar] ~34-~34: There might be a mistake here.
Context: ...ecific permissions ### Team Permissions Configure what team members can access: ...
(QB_NEW_EN)
[grammar] ~35-~35: There might be a mistake here.
Context: ... Configure what team members can access: - Team-level permissions - Resource access...
(QB_NEW_EN)
[grammar] ~36-~36: There might be a mistake here.
Context: ...ers can access: - Team-level permissions - Resource access control - Role-based res...
(QB_NEW_EN)
[grammar] ~37-~37: There might be a mistake here.
Context: ...el permissions - Resource access control - Role-based restrictions ## Need Help? ...
(QB_NEW_EN)
docs/templates-dashboard/users.mdx
[grammar] ~12-~12: There might be a mistake here.
Context: ...n. ## Overview From this page you can: - View all registered users - Search and f...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ...uthentication methods - Team memberships - Permission assignments - Recent activity...
(QB_NEW_EN)
[grammar] ~26-~26: There might be a mistake here.
Context: ...eam memberships - Permission assignments - Recent activity ### Managing User Data ...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ... Recent activity ### Managing User Data You can update user information includin...
(QB_NEW_EN)
[grammar] ~30-~30: There might be a mistake here.
Context: ...u can update user information including: - Profile details - Custom metadata - Cont...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ...information including: - Profile details - Custom metadata - Contact preferences - ...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...ing: - Profile details - Custom metadata - Contact preferences - Account status ##...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ... - Custom metadata - Contact preferences - Account status ### User Permissions Con...
(QB_NEW_EN)
[grammar] ~36-~36: There might be a mistake here.
Context: ...s - Account status ### User Permissions Control what users can access by: - Assi...
(QB_NEW_EN)
[grammar] ~37-~37: There might be a mistake here.
Context: ...ssions Control what users can access by: - Assigning roles - Setting specific permi...
(QB_NEW_EN)
[grammar] ~38-~38: There might be a mistake here.
Context: ...t users can access by: - Assigning roles - Setting specific permissions - Managing ...
(QB_NEW_EN)
[grammar] ~39-~39: There might be a mistake here.
Context: ...ing roles - Setting specific permissions - Managing team memberships ## Need Help?...
(QB_NEW_EN)
docs/templates-dashboard/auth-providers.mdx
[grammar] ~13-~13: There might be a mistake here.
Context: ...oviders (Google, GitHub, Facebook, etc.) - Enterprise providers (Microsoft, SAML, e...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...ft, SAML, etc.) - Custom OAuth providers - Passwordless options ## Supported Provi...
(QB_NEW_EN)
[grammar] ~20-~20: There might be a mistake here.
Context: ... Providers ### Popular Social Providers - Google: OAuth 2.0 integration with Goo...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...uth 2.0 integration with Google accounts - GitHub: Perfect for developer-focused ...
(QB_NEW_EN)
[grammar] ~22-~22: There might be a mistake here.
Context: ...rfect for developer-focused applications - Facebook: Social login for consumer ap...
(QB_NEW_EN)
[grammar] ~23-~23: There might be a mistake here.
Context: ...: Social login for consumer applications - Discord: Great for gaming and communit...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ...d**: Great for gaming and community apps - Twitter/X: Social media integration #...
(QB_NEW_EN)
[grammar] ~27-~27: There might be a mistake here.
Context: ...ia integration ### Enterprise Providers - Microsoft: Azure AD and Office 365 int...
(QB_NEW_EN)
[grammar] ~28-~28: There might be a mistake here.
Context: ...t**: Azure AD and Office 365 integration - SAML: Enterprise single sign-on - **Li...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ...on - SAML: Enterprise single sign-on - LinkedIn: Professional network integra...
(QB_NEW_EN)
[grammar] ~41-~41: There might be a mistake here.
Context: ...ovider Settings ### OAuth Configuration Each provider requires: - Client ID - Cl...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...th Configuration Each provider requires: - Client ID - Client Secret - Authorized r...
(QB_NEW_EN)
[grammar] ~43-~43: There might be a mistake here.
Context: ...tion Each provider requires: - Client ID - Client Secret - Authorized redirect URIs...
(QB_NEW_EN)
[grammar] ~44-~44: There might be a mistake here.
Context: ...er requires: - Client ID - Client Secret - Authorized redirect URIs - Scope permiss...
(QB_NEW_EN)
[grammar] ~45-~45: There might be a mistake here.
Context: ...Client Secret - Authorized redirect URIs - Scope permissions ### Advanced Options ...
(QB_NEW_EN)
[grammar] ~48-~48: There might be a mistake here.
Context: ... Scope permissions ### Advanced Options - Custom button styling - Provider-specifi...
(QB_NEW_EN)
[grammar] ~49-~49: There might be a mistake here.
Context: ...Advanced Options - Custom button styling - Provider-specific settings - User data m...
(QB_NEW_EN)
[grammar] ~50-~50: There might be a mistake here.
Context: ...ton styling - Provider-specific settings - User data mapping - Permission scopes #...
(QB_NEW_EN)
[grammar] ~51-~51: There might be a mistake here.
Context: ...er-specific settings - User data mapping - Permission scopes ## Need Help? Use th...
(QB_NEW_EN)
🪛 Biome (2.1.2)
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
[error] 220-220: src is assigned to itself.
This is where is assigned.
Self assignments have no effect and can be removed.
(lint/correctness/noSelfAssign)
⏰ 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). (9)
- GitHub Check: all-good
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: Security Check
🔇 Additional comments (14)
docs/.gitignore (1)
30-35
: Good addition: ignore generated dashboard docs output.Adding
/content/dashboard/
aligns with existing generated content ignores and prevents noisy diffs. No action needed.docs/templates-dashboard/teams.mdx (1)
12-17
: Verify “team analytics” availability or rephrase.If analytics aren’t yet in the Teams section, reword to avoid overpromising (e.g., “View team activity”).
Would you confirm the current dashboard includes team-level analytics in this view? If not, I can propose precise wording.
docs/next.config.mjs (1)
12-51
: Scope CORS headers to development, use dynamic origin, restrict methods, add Vary and caching, and verify credentials
- Wrap the existing
headers()
return in aif (process.env.NODE_ENV === 'development')
guard indocs/next.config.mjs
.- Replace hard-coded origin with
process.env.DASHBOARD_APP_ORIGIN ?? 'http://localhost:8101'
.- Change
Access-Control-Allow-Methods
to onlyGET, OPTIONS
.- Add
Vary: Origin
andAccess-Control-Max-Age: 600
to each route.- Confirm whether cookies are sent; if so, also add
Access-Control-Allow-Credentials: true
and echo the exact origin.docs/templates-dashboard/meta.json (1)
4-15
: Validate slugs and group markers with generatorEnsure the generator supports section dividers like "---General---" and that each slug matches the corresponding MDX filename (users, teams, permissions, settings, auth-providers, analytics).
If needed, I can script-check for missing/extra pages against content/dashboard.
docs/lib/source.ts (1)
1-1
: Import looks goodExtending to include dashboard is consistent with the loaders below.
docs/src/app/docs-embed/[[...slug]]/page.tsx (1)
15-23
: MDX render path looks correctUsing
page.data.body
withgetEmbeddedMDXComponents()
aligns with the embed strategy.docs/source.config.ts (1)
30-39
: Dashboard collection wiring LGTM
defineDocs({ dir: './content/dashboard', ... })
mirrors the API collection. No issues spotted.docs/src/app/api-embed/[[...slug]]/page.tsx (1)
15-23
: MDX embed rendering looks goodConsistent with the embedded MDX components strategy.
docs/src/app/dashboard-embed/layout.tsx (2)
1-8
: EmbeddedLinkInterceptor includes 'use client' directive; no changes required
10-10
: scrollbar-hide utility confirmed in global.css; no changes needed.docs/src/mdx-components.tsx (2)
5-5
: Imports look correct
ComponentProps
typing andEmbeddedLink
import are appropriate.Also applies to: 17-17
87-94
: Embedded MDX components override is solidOverriding
a
toEmbeddedLink
withisEmbedded
enforces embedded URL rewriting as intended.docs/src/app/docs-embed/layout.tsx (1)
1-8
: No action needed—EmbeddedLinkInterceptor
already declares"use client"
at the top.apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (1)
330-331
: Minimize iframe sandbox permissions (verify necessity of allow-same-origin).
allow-same-origin
significantly increases the embedded page’s capabilities. If not required, drop it to harden the sandbox.Proposed tightened sandbox (pending validation with embedded link behavior):
- sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox" + sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox"Please confirm embedded navigation and external links still function as expected.
apps/dashboard/src/components/stack-companion/dashboard-docs-widget.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
Outdated
Show resolved
Hide resolved
export default async function DashboardEmbedPage({ | ||
params, | ||
}: { | ||
params: Promise<{ slug?: string[] }>, | ||
}) { | ||
const { slug } = await params; | ||
const page = dashboardSource.getPage(slug ?? []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix params typing (don’t await params).
params
is a plain object in the App Router, not a Promise. Current code will type- or runtime-fail.
Apply:
-export default async function DashboardEmbedPage({
- params,
-}: {
- params: Promise<{ slug?: string[] }>,
-}) {
- const { slug } = await params;
+export default async function DashboardEmbedPage({
+ params,
+}: {
+ params: { slug?: string[] },
+}) {
+ const { slug } = params;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export default async function DashboardEmbedPage({ | |
params, | |
}: { | |
params: Promise<{ slug?: string[] }>, | |
}) { | |
const { slug } = await params; | |
const page = dashboardSource.getPage(slug ?? []); | |
export default async function DashboardEmbedPage({ | |
params, | |
}: { | |
params: { slug?: string[] }, | |
}) { | |
const { slug } = params; | |
const page = dashboardSource.getPage(slug ?? []); | |
// … | |
} |
🤖 Prompt for AI Agents
In docs/src/app/dashboard-embed/[[...slug]]/page.tsx around lines 5 to 11, the
handler types params as a Promise and uses await, but in the App Router params
is a plain object; remove the Promise typing and the await. Change the function
signature to accept params: { slug?: string[] } (not Promise<{ slug?: string[]
}>) and use const { slug } = params; then pass slug ?? [] to
dashboardSource.getPage(...) as before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
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/layouts/docs.tsx (2)
96-108
: Prefer Map over Record for accordion state (guideline) and avoid object churnSwitch to Map to align with project guideline and reduce spread-copy churn on updates.
Apply this diff:
-export function AccordionProvider({ children }: { children: ReactNode }) { - const [accordionState, setAccordionStateInternal] = useState<Record<string, boolean>>({}); +export function AccordionProvider({ children }: { children: ReactNode }) { + const [accordionState, setAccordionStateInternal] = useState<Map<string, boolean>>(new Map()); @@ - const setAccordionState = (key: string, isOpen: boolean) => { - setAccordionStateInternal(prev => ({ ...prev, [key]: isOpen })); - }; + const setAccordionState = (key: string, isOpen: boolean) => { + setAccordionStateInternal((prev) => { + const next = new Map(prev); + next.set(key, isOpen); + return next; + }); + };And update the context type accordingly (outside this hunk):
type AccordionContextType = { accordionState: Map<string, boolean>; setAccordionState: (key: string, isOpen: boolean) => void; };
268-294
: Avoid substring matches that can mis-route platforms (e.g., “react” matching “preact”); also prefer Map over RecordDrop the includes() check and use a Map. This prevents accidental matches and follows the Map guideline.
Apply this diff:
-export function findPlatformContent(tree: PageTree.Root, platform: string): PageTree.Node[] { - // Platform folder name mappings - const platformMappings: Record<string, string[]> = { - 'next': ['next.js', 'nextjs'], - 'react': ['react'], - 'js': ['javascript'], - 'python': ['python'] - }; +export function findPlatformContent(tree: PageTree.Root, platform: string): PageTree.Node[] { + // Platform folder name mappings + const platformMappings = new Map<string, string[]>([ + ['next', ['next.js', 'nextjs']], + ['react', ['react']], + ['js', ['javascript']], + ['python', ['python']], + ]); @@ - const possibleNames = platformKey in platformMappings ? platformMappings[platformKey] : [platformKey]; + const possibleNames = platformMappings.get(platformKey) ?? [platformKey]; @@ - if (possibleNames.some(name => { - const normalizedName = name.trim().toLowerCase(); - return itemName === normalizedName || itemName.includes(normalizedName); - })) { + if (possibleNames.some((name) => itemName === name.trim().toLowerCase())) { return item.children; }
♻️ Duplicate comments (2)
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (2)
99-113
: Remove hardcoded localhost; parameterize docs origin (avoids mixed-content and non-prod breakage).Build embed URLs from a configurable origin (e.g., NEXT_PUBLIC_DOCS_ORIGIN) or current origin; stop hardcoding http://localhost:8104.
Apply:
- const url = `http://localhost:8104/docs-embed/${docMapping.path}`; + const url = joinDocUrl(`/docs-embed/${docMapping.path}`);- const url = `http://localhost:8104/docs-embed/${platform}/getting-started/setup`; + const url = joinDocUrl(`/docs-embed/${platform}/getting-started/setup`);- const url = `http://localhost:8104/api-embed/overview`; + const url = joinDocUrl(`/api-embed/overview`);Add near the top (after types):
+const DOCS_ORIGIN = process.env.NEXT_PUBLIC_DOCS_ORIGIN ?? ''; +const joinDocUrl = (p: string) => { + const base = + DOCS_ORIGIN.trim() || + (typeof window !== 'undefined' ? window.location.origin : ''); + return `${base.replace(/\/$/, '')}${p.startsWith('/') ? p : `/${p}`}`; +};
241-255
: Fix iframe reload fallback (no-op self-assign + Biome error).Replace self-assign with cache-busting URL update to actually trigger reload.
- } catch (error) { - // If we can't access iframe history, try to reload the previous page - // This is a fallback that at least resets the iframe - console.warn('Cannot access iframe history, reloading current page'); - if (iframeRef) { - iframeRef.src = iframeRef.src; - } - } + } catch (error) { + console.warn('Cannot access iframe history, forcing reload via cache-buster'); + if (iframeRef?.src) { + try { + const u = new URL(iframeRef.src, window.location.origin); + u.searchParams.set('_r', Date.now().toString()); + setDocContent(prev => (prev ? { ...prev, url: u.toString() } : prev)); + } catch { + setDocContent(prev => (prev ? { ...prev } : prev)); // react remount fallback + } + } + }
🧹 Nitpick comments (12)
docs/src/components/layouts/docs.tsx (3)
110-124
: Use Map accessors in the hook; expose stable tupleIf you adopt Map in the provider, read via get().
Apply this diff:
-export function useAccordionState(key: string, defaultValue: boolean) { +export function useAccordionState(key: string, defaultValue: boolean) { @@ - const { accordionState, setAccordionState } = context; - const isOpen = accordionState[key] ?? defaultValue; + const { accordionState, setAccordionState } = context; + const isOpen = (accordionState instanceof Map + ? accordionState.get(key) + : // backward-compat if context not yet migrated + (accordionState as Record<string, boolean>)[key]) ?? defaultValue;
297-344
: Use stable React keys; avoid index to prevent reorder bugsKeys using index can cause state leakage on reordering. Prefer semantic keys.
Apply this diff:
- {item.children.map((child, index) => ( - <PageTreeItem key={child.type === 'page' ? child.url : index} item={child} currentPlatform={currentPlatform} /> - ))} + {item.children.map((child) => ( + <PageTreeItem + key={`${child.type}:${child.type === 'page' ? child.url : String(child.name ?? '')}`} + item={child} + currentPlatform={currentPlatform} + /> + ))} @@ - {item.children.map((child, index) => ( - <PageTreeItem key={child.type === 'page' ? child.url : index} item={child} currentPlatform={currentPlatform} /> - ))} + {item.children.map((child) => ( + <PageTreeItem + key={`${child.type}:${child.type === 'page' ? child.url : String(child.name ?? '')}`} + item={child} + currentPlatform={currentPlatform} + /> + ))}
346-376
: LGTM on making renderSidebarContent public; just one key nitApproach looks good. Mirror the stable-key change here too.
Apply this diff:
- {tree.children.map((item, index) => ( - <PageTreeItem key={item.type === 'page' ? item.url : index} item={item} currentPlatform={currentPlatform} /> - ))} + {tree.children.map((item) => ( + <PageTreeItem + key={`${item.type}:${item.type === 'page' ? item.url : String(item.name ?? '')}`} + item={item} + currentPlatform={currentPlatform} + /> + ))}docs/src/app/docs-embed/[[...slug]]/page.tsx (1)
18-23
: Consider 404 for unknown pages (optional)Redirecting unknown slugs to overview can mask broken links. Using notFound() yields a proper 404.
Apply this diff:
- if (!page) { - // Try to redirect to a sensible default if page not found - redirect('/docs-embed/next/overview'); - } + if (!page) { + // Unknown page → 404 (keeps bad links visible) + // import { notFound } from 'next/navigation' + // notFound(); + redirect('/docs-embed/next/overview'); + }docs/src/components/embedded-docs-with-sidebar.tsx (3)
24-34
: Harden postMessage handling with a simple channel guardAnyone can postMessage into the iframe; add a shared “source” token check (keeps cross-origin support without origin allowlists).
Apply this diff:
- useEffect(() => { + useEffect(() => { const handleMessage = (event: MessageEvent) => { - if (event.data?.type === 'TOGGLE_SIDEBAR') { + if (event.data?.source === 'STACK_EMBED' && event.data?.type === 'TOGGLE_SIDEBAR') { setIsSidebarVisible(event.data.visible); } };Parent should send: window.postMessage({ source: 'STACK_EMBED', type: 'TOGGLE_SIDEBAR', visible: true }, '*').
56-74
: Add dialog semantics for accessibility on the DrawerMark the drawer as a modal dialog for AT users.
Apply this diff:
- <div className="fixed left-0 top-0 bottom-0 w-[268px] z-50 bg-fd-background/95 backdrop-blur-md border-r border-fd-border shadow-lg"> + <div + className="fixed left-0 top-0 bottom-0 w-[268px] z-50 bg-fd-background/95 backdrop-blur-md border-r border-fd-border shadow-lg" + role="dialog" + aria-modal="true" + aria-label="Documentation navigation" + >
11-15
: Remove unused currentSlug prop (keeps API tight)The prop isn’t used; drop it here and at the call site.
Apply this diff:
type EmbeddedDocsWithSidebarProps = { pageTree: PageTree.Root, - currentSlug: string[], children: React.ReactNode, } -export function EmbeddedDocsWithSidebar({ pageTree, currentSlug, children }: EmbeddedDocsWithSidebarProps) { +export function EmbeddedDocsWithSidebar({ pageTree, children }: EmbeddedDocsWithSidebarProps) {And in docs/src/app/docs-embed/[[...slug]]/page.tsx remove the prop:
- <EmbeddedDocsWithSidebar - pageTree={source.pageTree} - currentSlug={slug} - > + <EmbeddedDocsWithSidebar pageTree={source.pageTree}>Also applies to: 17-18
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (5)
85-101
: Prefer Map over Record for key–value collections (repo guideline).Use Map and safe fallback for unknown pages.
- const dashboardToDocsMap: Record<string, { path: string, title: string }> = { - 'overview': { path: `${platform}/overview`, title: 'Stack Auth Overview' }, - 'users': { path: `${platform}/getting-started/users`, title: 'User Management' }, - 'auth-methods': { path: `${platform}/concepts/auth-providers`, title: 'Authentication Providers' }, - 'orgs-and-teams': { path: `${platform}/concepts/orgs-and-teams`, title: 'Teams & Organizations' }, - 'team-permissions': { path: `${platform}/concepts/permissions#team-permissions`, title: 'Team Permissions' }, - 'emails': { path: `${platform}/concepts/emails`, title: 'Emails' }, - 'domains': { path: `${platform}/getting-started/production#domains`, title: 'Domains' }, - 'webhooks': { path: `${platform}/concepts/webhooks`, title: 'Webhooks' }, - 'stack-auth-keys': { path: `${platform}/getting-started/setup#update-api-keys`, title: 'Stack Auth Keys' }, - 'project-settings': { path: `${platform}/getting-started/production#enabling-production-mode`, title: 'Project Configuration' }, - }; - - const docMapping = dashboardToDocsMap[page]; + const dashboardToDocsMap = new Map<string, { path: string; title: string }>([ + ['overview', { path: `${platform}/overview`, title: 'Stack Auth Overview' }], + ['users', { path: `${platform}/getting-started/users`, title: 'User Management' }], + ['auth-methods', { path: `${platform}/concepts/auth-providers`, title: 'Authentication Providers' }], + ['orgs-and-teams', { path: `${platform}/concepts/orgs-and-teams`, title: 'Teams & Organizations' }], + ['team-permissions', { path: `${platform}/concepts/permissions#team-permissions`, title: 'Team Permissions' }], + ['emails', { path: `${platform}/concepts/emails`, title: 'Emails' }], + ['domains', { path: `${platform}/getting-started/production#domains`, title: 'Domains' }], + ['webhooks', { path: `${platform}/concepts/webhooks`, title: 'Webhooks' }], + ['stack-auth-keys', { path: `${platform}/getting-started/setup#update-api-keys`, title: 'Stack Auth Keys' }], + ['project-settings', { path: `${platform}/getting-started/production#enabling-production-mode`, title: 'Project Configuration' }], + ]); + + const docMapping = + dashboardToDocsMap.get(page) ?? dashboardToDocsMap.get('overview')!;
136-147
: Reset back-button state on new loads.Avoid stale “Back” visibility after reloads.
setLoading(true); setError(null); setIframeLoaded(false); + setCanGoBack(false); s 1AF0 etCurrentPageDoc(newPageDoc);
149-158
: Guard debug logs for production.Keep console noise out of prod.
- console.log('Debug mapping:', { + if (process.env.NODE_ENV !== 'production') console.log('Debug mapping:', { pathname, normalizedPath: pathname.replace(/^\/projects\/[^/]+/, ''), detectedPage: page, platform: selectedPlatform }); - const content = getDocContentForPath(pathname, selectedDocType, selectedPlatform); - console.log('Loading docs:', { page, platform: selectedPlatform, url: content.url }); + const content = getDocContentForPath(pathname, selectedDocType, selectedPlatform); + if (process.env.NODE_ENV !== 'production') console.log('Loading docs:', { page, platform: selectedPlatform, url: content.url });
149-149
: Avoid recomputing getDashboardPage.Use the already computed value.
- const page = getDashboardPage(pathname); + const page = newPageDoc;
224-230
: Hide sidebar in the embed when switching doc types.Mirror local state to the iframe.
setSelectedDocType(docType); setShowSwitchPrompt(false); setIsSidebarVisible(false); // Hide sidebar when switching doc types + if (iframeRef) toggleEmbeddedSidebar(iframeRef, false);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
(1 hunks)docs/src/app/docs-embed/[[...slug]]/page.tsx
(1 hunks)docs/src/components/embedded-docs-with-sidebar.tsx
(1 hunks)docs/src/components/layouts/docs.tsx
(5 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value collections
Files:
docs/src/components/embedded-docs-with-sidebar.tsx
docs/src/app/docs-embed/[[...slug]]/page.tsx
docs/src/components/layouts/docs.tsx
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
For blocking alerts and errors in UI, do not use toast notifications; use alerts instead
Files:
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
{apps/dashboard,apps/dev-launchpad,packag F0D0 es/stack-ui,packages/react}/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Keep hover/click animations snappy; avoid pre-transition delays on hover and apply transitions after the action (e.g., fade-out on hover end)
Files:
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
🧬 Code graph analysis (3)
docs/src/components/embedded-docs-with-sidebar.tsx (2)
docs/src/components/layouts/docs.tsx (2)
AccordionProvider
(96-108)renderSidebarContent
(346-376)docs/src/components/layouts/sidebar-context.tsx (1)
SidebarProvider
(51-184)
docs/src/app/docs-embed/[[...slug]]/page.tsx (3)
docs/lib/source.ts (1)
source
(23-30)docs/src/components/embedded-docs-with-sidebar.tsx (1)
EmbeddedDocsWithSidebar
(17-91)docs/src/mdx-components.tsx (1)
getEmbeddedMDXComponents
(88-94)
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (1)
docs/src/components/icons.tsx (2)
Menu
(137-141)ExternalLink
(98-108)
🪛 Biome (2.1.2)
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
[error] 252-252: src is assigned to itself.
This is where is assigned.
Self assignments have no effect and can be removed.
(lint/correctness/noSelfAssign)
⏰ 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). (9)
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: all-good
- GitHub Check: docker
- GitHub Check: setup-tests
- GitHub Check: restart-dev-and-test
- GitHub Check: Security Check
🔇 Additional comments (3)
docs/src/app/docs-embed/[[...slug]]/page.tsx (1)
1-5
: Import 'lib/source' resolves correctly via baseUrl
docs/tsconfig.json sets baseUrl to "." so non-relative imports map to project root (docs/lib/source.ts exists and is already used via relative paths); no changes needed.apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (2)
398-406
: Revisit sandbox allowances; narrow if possible.If not strictly required, drop allow-same-origin and/or allow-popups-to-escape-sandbox to reduce risk. PostMessage doesn’t need same-origin.
Do you know if the embedded docs require same-origin APIs (e.g., localStorage)? If not, I can propose a tightened sandbox string.
286-313
: Good: inline alert for errors (no toasts).Complies with the repo’s UI guideline for blocking errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
docs/src/components/embedded-docs-with-sidebar.tsx (2)
59-65
: Prefer a Map-based label lookup over chained conditionals.Cleaner, extensible, and aligns with the repo guideline to prefer ES6 Map for key–value collections.
- <div id="embed-sidebar-title" className="text-sm font-medium text-fd-foreground"> - {currentPlatform === 'next' ? 'Next.js' : - currentPlatform === 'js' ? 'JavaScript' : - currentPlatform.charAt(0).toUpperCase() + currentPlatform.slice(1)} Documentation - </div> + <div id="embed-sidebar-title" className="text-sm font-medium text-fd-foreground"> + {platformLabel} Documentation + </div>Add these helpers (place near the top of the file, after imports):
const PLATFORM_LABELS = new Map<string, string>([ ['next', 'Next.js'], ['js', 'JavaScript'], ['react', 'React'], ['react-native', 'React Native'], ['node', 'Node.js'], ]); function toTitleCaseFromSlug(slug: string) { return slug.replace(/(^\w)|(-\w)/g, s => s.replace('-', ' ').toUpperCase()); }And compute the label after
currentPlatform
:const platformLabel = currentPlatform ? PLATFORM_LABELS.get(currentPlatform) ?? toTitleCaseFromSlug(currentPlatform) : 'Documentation';
34-35
: Optionally lock body scroll while the drawer is open (iOS Safari).Prevents background scrolling in some mobile browsers.
+ useEffect(() => { + if (!isSidebarVisible) return; + const prev = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { + document.body.style.overflow = prev; + }; + }, [isSidebarVisible]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
docs/src/components/embedded-docs-with-sidebar.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/embedded-docs-with-sidebar.tsx
🧬 Code graph analysis (1)
docs/src/components/embedded-docs-with-sidebar.tsx (2)
docs/src/components/layouts/docs.tsx (2)
AccordionProvider
(96-108)renderSidebarContent
(346-376)docs/src/components/layouts/sidebar-context.tsx (1)
SidebarProvider
(51-184)
⏰ 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). (9)
- GitHub Check: docker
- GitHub Check: lint_and_build (latest)
- GitHub Check: all-good
- GitHub Check: docker
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: build (22.x)
- GitHub Check: setup-tests
- GitHub Check: Security Check
<div className="fixed left-0 top-0 bottom-0 w-[268px] z-50 bg-fd-background/95 backdrop-blur-md border-r border-fd-border shadow-lg"> | ||
<div className="h-full flex flex-col"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add dialog/nav semantics and ARIA for accessibility.
Improve screen reader support and expected modal behavior.
- <div className="fixed left-0 top-0 bottom-0 w-[268px] z-50 bg-fd-background/95 backdrop-blur-md border-r border-fd-border shadow-lg">
+ <div
+ className="fixed left-0 top-0 bottom-0 w-[268px] z-50 bg-fd-background/95 backdrop-blur-md border-r border-fd-border shadow-lg"
+ role="dialog"
+ aria-modal="true"
+ aria-labelledby="embed-sidebar-title"
+ >
<div className="h-full flex flex-col">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-fd-border">
{currentPlatform && (
- <div className="text-sm font-medium text-fd-foreground">
+ <div id="embed-sidebar-title" className="text-sm font-medium text-fd-foreground">
{currentPlatform === 'next' ? 'Next.js' :
currentPlatform === 'js' ? 'JavaScript' :
currentPlatform.charAt(0).toUpperCase() + currentPlatform.slice(1)} Documentation
</div>
)}
<button
onClick={() => setIsSidebarVisible(false)}
- className="p-1 rounded-md hover:bg-fd-accent/50 text-fd-muted-foreground hover:text-fd-foreground transition-colors"
- title="Close sidebar"
+ className="p-1 rounded-md hover:bg-fd-accent/50 text-fd-muted-foreground hover:text-fd-foreground transition-colors"
+ title="Close sidebar"
+ aria-label="Close sidebar"
>
×
</button>
</div>
{/* Scrollable Content */}
- <div className="flex-1 overflow-y-auto">
+ <nav className="flex-1 overflow-y-auto" aria-label="Documentation navigation">
<div className="p-4">
{renderSidebarContent(pageTree, docsPath)}
</div>
- </div>
+ </nav>
Also applies to: 58-66, 75-80
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (4)
docs/src/components/embedded-link-interceptor.tsx (2)
88-96
: Remove HEAD preflight; navigate directly to avoid 405/308 pitfalls.HEAD can block valid navigations and adds latency. Let the platform handle 404s.
- // Function to check if a URL exists - const checkUrlExists = async (url: string): Promise<boolean> => { - try { - const response = await fetch(url, { method: 'HEAD' }); - return response.ok; - } catch { - return false; - } - }; + // (Optional) Add non-blocking prefetch later if needed.- // Check if the URL exists before navigating (async operation) - checkUrlExists(embeddedHref).then((urlExists) => { - if (urlExists) { - // Navigate to the embedded version - window.location.href = embeddedHref; - } - }).catch(() => { - // Network error or other issue - silently ignore - }); + // Navigate to the embedded version + window.location.href = embeddedHref;Also applies to: 129-137
5-84
: Avoid duplication with EmbeddedLink MDX component. Extract shared util.The rewriting logic here mirrors docs/src/components/mdx/embedded-link.tsx. Factor into a shared helper to keep rules in one place.
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (2)
28-43
: Good: docs origin is configurable (no hardcoded localhost).This addresses the earlier feedback and enables non-local/HTTPS deployments.
312-317
: Fix fallback reload (self-assign no-op; linter error).Force a real reload via cache-buster; resolves Biome error.
- // If we can't access iframe history, try to reload the previous page - // This is a fallback that at least resets the iframe - console.warn('Cannot access iframe history, reloading current page'); - if (iframeRef) { - iframeRef.src = iframeRef.src; - } + console.warn('Cannot access iframe history, forcing reload via cache-buster'); + if (iframeRef) { + try { + const u = new URL(iframeRef.src); + u.searchParams.set('_r', Date.now().toString()); + setDocContent(prev => (prev ? { ...prev, url: u.toString() } : prev)); + } catch { + // Fallback: trigger a remount + setDocContent(prev => (prev ? { ...prev } : prev)); + } + }
🧹 Nitpick comments (11)
apps/dashboard/.env (2)
8-8
: Add docs base URL: OK. Verify framing/CORS and slash semantics.
- Confirm docs origin is allowed to be iframed by the dashboard via CSP frame-ancestors/X-Frame-Options and CORS on the docs app.
- Decide and document whether a trailing slash is allowed/required; ensure consumers normalize.
6-9
: Fix dotenv-linter UnorderedKey warning by reordering keys.Move NEXT_PUBLIC_STACK_DOCS_BASE_URL before NEXT_PUBLIC_STACK_EXTRA_REQUEST_HEADERS.
-NEXT_PUBLIC_STACK_EXTRA_REQUEST_HEADERS=# a list of extra request headers to add to all Stack Auth API requests, as a JSON record -NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY=# enter your Stripe publishable key here -NEXT_PUBLIC_STACK_DOCS_BASE_URL=https://docs.stack-auth.com +NEXT_PUBLIC_STACK_DOCS_BASE_URL=https://docs.stack-auth.com +NEXT_PUBLIC_STACK_EXTRA_REQUEST_HEADERS=# a list of extra request headers to add to all Stack Auth API requests, as a JSON record +NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY=# enter your Stripe publishable key heredocs/src/components/platform-change-notifier.tsx (1)
13-15
: Make platform regex robust for paths without a trailing slash.Handle both /docs-embed/ and /docs-embed//.
- const match = path.match(/\/docs-embed\/([^\/]+)\//); + const match = path.match(/\/docs-embed\/([^/]+)(?:\/|$)/);docs/src/components/embedded-link-interceptor.tsx (3)
5-84
: URL rewriting is well-hardened.Preserves query/hash, handles roots, and normalizes relative paths. Consider also rewriting absolute same-origin doc links (e.g., https://docs.example.com/docs/...) as a nice-to-have.
100-101
: Tiny readability nit.Space around operator.
- if (event.defaultPrevented || event.button !==0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) { + if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
121-129
: Optionally intercept absolute same-origin links.Covers anchors that use fully-qualified URLs to the same docs host.
- // Intercept internal links that need to be rewritten OR relative links + // Intercept internal links that need to be rewritten OR relative links if (href.startsWith('/docs/') || href.startsWith('/api/') || href.startsWith('/dashboard/') || (!/^[a-zA-Z][a-zA-Z+\-.]*:/.test(href) && !href.startsWith('#'))) { event.preventDefault(); const currentPath = window.location.pathname; const embeddedHref = getEmbeddedUrl(href, currentPath); // Navigate to the embedded version window.location.href = embeddedHref; + } else if (/^https?:\/\//.test(href)) { + try { + const u = new URL(href); + if (u.origin === window.location.origin && + (u.pathname.startsWith('/docs/') || u.pathname.startsWith('/api/') || u.pathname.startsWith('/dashboard/'))) { + event.preventDefault(); + const embeddedHref = getEmbeddedUrl(u.pathname + u.search + u.hash, window.location.pathname); + window.location.href = embeddedHref; + } + } catch { + /* ignore */ + } }apps/dashboard/.env.development (1)
2-2
: Dev docs base URL: OK. Verify it matches the local docs server and is allowed by embed CSP.
- Ensure the docs dev server actually runs on :8104.
- Confirm the docs app’s CSP (frame-ancestors) and CORS allow embedding from the dashboard dev origin.
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (4)
45-57
: Derive postMessage target from iframe.src (avoid window-origin fallback).Use the resolved iframe.src; guard for empty src.
- try { - const src = iframe.getAttribute("src") ?? ""; - const targetOrigin = new URL(src, window.location.origin).origin; + try { + if (!iframe.src) return; + const targetOrigin = new URL(iframe.src, window.location.href).origin; iframe.contentWindow?.postMessage( { type: "TOGGLE_SIDEBAR", visible }, targetOrigin )
104-121
: Prefer ES6 Map over Record for key–value collections.This follows the repo guideline and avoids accidental prototype key collisions.
- const dashboardToDocsMap: Record<string, { path: string, title: string }> = { - 'overview': { path: `${platform}/overview`, title: 'Stack Auth Overview' }, - 'users': { path: `${platform}/getting-started/users`, title: 'User Management' }, - 'auth-methods': { path: `${platform}/concepts/auth-providers`, title: 'Authentication Providers' }, - 'orgs-and-teams': { path: `${platform}/concepts/orgs-and-teams`, title: 'Teams & Organizations' }, - 'team-permissions': { path: `${platform}/concepts/permissions#team-permissions`, title: 'Team Permissions' }, - 'emails': { path: `${platform}/concepts/emails`, title: 'Emails' }, - 'domains': { path: `${platform}/getting-started/production#domains`, title: 'Domains' }, - 'webhooks': { path: `${platform}/concepts/webhooks`, title: 'Webhooks' }, - 'stack-auth-keys': { path: `${platform}/getting-started/setup#update-api-keys`, title: 'Stack Auth Keys' }, - 'project-settings': { path: `${platform}/getting-started/production#enabling-production-mode`, title: 'Project Configuration' }, - }; - - const docMapping = dashboardToDocsMap[page]; + const dashboardToDocsMap = new Map<string, { path: string; title: string }>([ + ['overview', { path: `${platform}/overview`, title: 'Stack Auth Overview' }], + ['users', { path: `${platform}/getting-started/users`, title: 'User Management' }], + ['auth-methods', { path: `${platform}/concepts/auth-providers`, title: 'Authentication Providers' }], + ['orgs-and-teams', { path: `${platform}/concepts/orgs-and-teams`, title: 'Teams & Organizations' }], + ['team-permissions', { path: `${platform}/concepts/permissions#team-permissions`, title: 'Team Permissions' }], + ['emails', { path: `${platform}/concepts/emails`, title: 'Emails' }], + ['domains', { path: `${platform}/getting-started/production#domains`, title: 'Domains' }], + ['webhooks', { path: `${platform}/concepts/webhooks`, title: 'Webhooks' }], + ['stack-auth-keys', { path: `${platform}/getting-started/setup#update-api-keys`, title: 'Stack Auth Keys' }], + ['project-settings', { path: `${platform}/getting-started/production#enabling-production-mode`, title: 'Project Configuration' }], + ]); + + const docMapping = dashboardToDocsMap.get(page) ?? dashboardToDocsMap.get('overview')!;
171-179
: Gate debug logs in production.Prevent noisy consoles in prod.
- console.log('Debug mapping:', { - pathname, - normalizedPath: pathname.replace(/^\/projects\/[^/]+/, ''), - detectedPage: page, - platform: selectedPlatform - }); + if (process.env.NODE_ENV !== 'production') { + console.log('Debug mapping:', { + pathname, + normalizedPath: pathname.replace(/^\/projects\/[^/]+/, ''), + detectedPage: page, + platform: selectedPlatform + }); + } const content = getDocContentForPath(pathname, selectedDocType, selectedPlatform); - console.log('Loading docs:', { page, platform: selectedPlatform, url: content.url }); + if (process.env.NODE_ENV !== 'production') { + console.log('Loading docs:', { page, platform: selectedPlatform, url: content.url }); + }
469-470
: Set iframe referrer policy.Minimize cross-origin referrer leakage.
- sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox" + sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox" + referrerPolicy="strict-origin-when-cross-origin"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
apps/dashboard/.env
(1 hunks)apps/dashboard/.env.development
(1 hunks)apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
(1 hunks)apps/dashboard/src/lib/env.tsx
(2 hunks)docs/src/app/api-embed/layout.tsx
(1 hunks)docs/src/app/docs-embed/[[...slug]]/page.tsx
(1 hunks)docs/src/app/docs-embed/layout.tsx
(1 hunks)docs/src/app/global.css
(1 hunks)docs/src/components/embedded-link-interceptor.tsx
(1 hunks)docs/src/components/platform-change-notifier.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- docs/src/app/docs-embed/layout.tsx
- docs/src/app/api-embed/layout.tsx
- docs/src/app/docs-embed/[[...slug]]/page.tsx
- docs/src/app/global.css
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value col 4020 lections
Files:
docs/src/components/platform-change-notifier.tsx
docs/src/components/embedded-link-interceptor.tsx
apps/dashboard/src/lib/env.tsx
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
For blocking alerts and errors in UI, do not use toast notifications; use alerts instead
Files:
apps/dashboard/src/lib/env.tsx
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Keep hover/click animations snappy; avoid pre-transition delays on hover and apply transitions after the action (e.g., fade-out on hover end)
Files:
apps/dashboard/src/lib/env.tsx
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (2)
apps/dashboard/src/lib/env.tsx (1)
getPublicEnvVar
(51-61)packages/stack-shared/src/utils/urls.tsx (2)
url
(278-280)isLocalhost
(199-205)
🪛 dotenv-linter (3.3.0)
apps/dashboard/.env
[warning] 8-8: [UnorderedKey] The NEXT_PUBLIC_STACK_DOCS_BASE_URL key should go before the NEXT_PUBLIC_STACK_EXTRA_REQUEST_HEADERS key
(UnorderedKey)
🪛 Biome (2.1.2)
apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
[error] 316-316: src is assigned to itself.
This is where is assigned.
Self assignments have no effect and can be removed.
(lint/correctness/noSelfAssign)
⏰ 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). (9)
- GitHub Check: docker
- GitHub Check: lint_and_build (latest)
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: build (22.x)
- GitHub Check: setup-tests
- GitHub Check: docker
- GitHub Check: Security Check
🔇 Additional comments (1)
apps/dashboard/src/lib/env.tsx (1)
10-10
: Sentinel mapping for NEXT_PUBLIC_STACK_DOCS_BASE_URL verified present.
// Verify origin for security - allow localhost in dev and configured docs URL | ||
const isLocalhost = event.origin.includes('localhost') || event.origin.includes('127.0.0.1'); | ||
const expectedDocsOrigin = new URL(getDocsBaseUrl()).origin; | ||
const isValidDocsOrigin = event.origin === expectedDocsOrigin; | ||
|
||
if (!isLocalhost && !isValidDocsOrigin) return; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Tighten postMessage origin check (drop substring localhost match).
Compare strictly to the iframe’s origin (or configured docs origin) to avoid spoofed origins.
- // Verify origin for security - allow localhost in dev and configured docs URL
- const isLocalhost = event.origin.includes('localhost') || event.origin.includes('127.0.0.1');
- const expectedDocsOrigin = new URL(getDocsBaseUrl()).origin;
- const isValidDocsOrigin = event.origin === expectedDocsOrigin;
-
- if (!isLocalhost && !isValidDocsOrigin) return;
+ // Verify origin strictly against the embedded docs origin
+ const expectedDocsOrigin =
+ (docContent?.url ? new URL(docContent.url) : new URL(getDocsBaseUrl())).origin;
+ if (event.origin !== expectedDocsOrigin) return;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Verify origin for security - allow localhost in dev and configured docs URL | |
const isLocalhost = event.origin.includes('localhost') || event.origin.includes('127.0.0.1'); | |
const expectedDocsOrigin = new URL(getDocsBaseUrl()).origin; | |
const isValidDocsOrigin = event.origin === expectedDocsOrigin; | |
if (!isLocalhost && !isValidDocsOrigin) return; | |
// Verify origin strictly against the embedded docs origin | |
const expectedDocsOrigin = | |
(docContent?.url ? new URL(docContent.url) : new URL(getDocsBaseUrl())).origin; | |
if (event.origin !== expectedDocsOrigin) return; |
🤖 Prompt for AI Agents
In apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx around
lines 209–215, the postMessage origin check currently uses substring matching
for localhost which is unsafe; replace it with a strict origin comparison: build
an allowedOrigins set containing the configured docs origin (new
URL(getDocsBaseUrl()).origin) and, if the running host is local, add the exact
window.location.origin (or explicitly provided allowed localhost origins from
env/config), then only accept the message if event.origin exactly equals one of
those allowedOrigins.
// Update docContent to reflect the new URL without reloading the iframe | ||
if (docContent && event.data.pathname) { | ||
const newUrl = `${getDocsBaseUrl()}${event.data.pathname}`; | ||
setDocContent({ | ||
...docContent, | ||
url: newUrl | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Sanitize iframe-driven URL updates.
Only accept embed paths; prevents unintended navigation.
- // Update docContent to reflect the new URL without reloading the iframe
- if (docContent && event.data.pathname) {
- const newUrl = `${getDocsBaseUrl()}${event.data.pathname}`;
+ // Update docContent to reflect the new URL without reloading the iframe
+ if (docContent && event.data.pathname) {
+ const p = String(event.data.pathname);
+ if (!/^\/(docs-embed|api-embed)\//.test(p)) return;
+ const newUrl = `${getDocsBaseUrl()}${p}`;
setDocContent({
...docContent,
url: newUrl
});
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Update docContent to reflect the new URL without reloading the iframe | |
if (docContent && event.data.pathname) { | |
const newUrl = `${getDocsBaseUrl()}${event.data.pathname}`; | |
setDocContent({ | |
...docContent, | |
url: newUrl | |
}); | |
} | |
// Update docContent to reflect the new URL without reloading the iframe | |
if (docContent && event.data.pathname) { | |
const p = String(event.data.pathname); | |
// Only allow known embed routes to prevent unexpected navigation | |
if (!/^\/(docs-embed|api-embed)\//.test(p)) return; | |
const newUrl = `${getDocsBaseUrl()}${p}`; | |
setDocContent({ | |
...docContent, | |
url: newUrl | |
}); | |
} |
🤖 Prompt for AI Agents
In apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx around
lines 229 to 236, the code updates iframe-driven URLs without validation;
restrict updates to only safe embed paths. Validate that event.data.pathname is
a string and matches a strict embed-path pattern (for example begins with
"/embed/" and matches a whitelist
5B10
regex like ^/embed/[A-Za-z0-9/_-]*$), reject
any values containing protocol markers, "//", or ".."; only construct the newUrl
from getDocsBaseUrl() + pathname and call setDocContent when the pathname passes
validation; otherwise ignore the update (optionally log a warning).
const targetOrigin = process.env.NODE_ENV === 'development' ? '*' : '*'; | ||
|
||
window.parent.postMessage( | ||
{ | ||
type: 'PLATFORM_CHANGE', | ||
platform, | ||
pathname | ||
}, | ||
targetOrigin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid wildcard targetOrigin; gate to parent origin and skip when not in an iframe.
Limit postMessage to the embedding origin and no-op if not framed.
- // In development, send to any origin. In production, be more specific if needed
- const targetOrigin = process.env.NODE_ENV === 'development' ? '*' : '*';
+ // Prefer the embedding parent origin; fallback to '*' only in dev.
+ const parentOrigin = (() => {
+ try {
+ return document.referrer ? new URL(document.referrer).origin : '';
+ } catch {
+ return '';
+ }
+ })();
+ const targetOrigin =
+ process.env.NODE_ENV === 'development' ? '*' : (parentOrigin || '*');
+ if (window.parent === window) return; // not embedded
window.parent.postMessage(
- {
- type: 'PLATFORM_CHANGE',
+ {
+ source: 'stack-docs-embed',
+ type: 'PLATFORM_CHANGE',
platform,
pathname
},
targetOrigin
);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const targetOrigin = process.env.NODE_ENV === 'development' ? '*' : '*'; | |
window.parent.postMessage( | |
{ | |
type: 'PLATFORM_CHANGE', | |
platform, | |
pathname | |
}, | |
targetOrigin | |
// Prefer the embedding parent origin; fallback to '*' only in dev. | |
const parentOrigin = (() => { | |
try { | |
return document.referrer ? new URL(document.referrer).origin : ''; | |
} catch { | |
return ''; | |
} | |
})(); | |
const targetOrigin = | |
process.env.NODE_ENV === 'development' | |
? '*' | |
: (parentOrigin || '*'); | |
if (window.parent === window) return; // not embedded | |
window.parent.postMessage( | |
{ | |
source: 'stack-docs-embed', | |
type: 'PLATFORM_CHANGE', | |
platform, | |
pathname | |
}, | |
targetOrigin | |
); |
🤖 Prompt for AI Agents
In docs/src/components/platform-change-notifier.tsx around lines 23 to 31, the
code uses a wildcard targetOrigin and always posts to window.parent; change it
to no-op when not inside an iframe (skip if window.parent === window), and
compute a safe parent origin instead of '*' by deriving it from
document.referrer (fallback to window.location.origin) using
URL(document.referrer).origin in a try/catch; if you cannot determine a valid
origin, do not postMessage, otherwise pass that origin as targetOrigin to
window.parent.postMessage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
docs/next.config.mjs (2)
13-50
: Refactor duplicate CORS header configuration.The CORS headers for
/dashboard/:path*
and/dashboard-embed/:path*
are identical. Extract this into a reusable constant to improve maintainability.+const corsHeaders = [ + { + key: 'Access-Control-Allow-Origin', + value: process.env.NEXT_PUBLIC_DASHBOARD_ORIGIN || 'http://localhost:8101', + }, + { + key: 'Access-Control-Allow-Methods', + value: 'GET, POST, PUT, DELETE, OPTIONS', + }, + { + key: 'Access-Control-Allow-Headers', + value: 'Content-Type, Authorization', + }, +]; + async headers() { return [ { - // Allow CORS for dashboard routes to be accessed by the dashboard app source: '/dashboard/:path*', - headers: [ - { - key: 'Access-Control-Allow-Origin', - value: 'http://localhost:8101', // Dashboard app origin - }, - { - key: 'Access-Control-Allow-Methods', - value: 'GET, POST, PUT, DELETE, OPTIONS', - }, - { - key: 'Access-Control-Allow-Headers', - value: 'Content-Type, Authorization', - }, - ], + headers: corsHeaders, }, { - // Allow CORS for embedded dashboard routes to be accessed by the dashboard app source: '/dashboard-embed/:path*', - headers: [ - { - key: 'Access-Control-Allow-Origin', - value: 'http://localhost:8101', // Dashboard app origin - }, - { - key: 'Access-Control-Allow-Methods', - value: 'GET, POST, PUT, DELETE, OPTIONS', - }, - { - key: 'Access-Control-Allow-Headers', - value: 'Content-Type, Authorization', - }, - ], + headers: corsHeaders, }, ]; },
24-24
: Restrict CORS to GET, OPTIONS
NoPOST
,PUT
, orDELETE
calls to/dashboard
or/dashboard-embed
were found inapps/dashboard
, so you can safely remove them:- value: 'GET, POST, PUT, DELETE, OPTIONS', + value: 'GET, OPTIONS',
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
docs/next.config.mjs
(1 hunks)
⏰ 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: build (22.x)
- GitHub Check: setup-tests
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: docker
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: Security Check
async headers() { | ||
return [ | ||
{ | ||
// Allow CORS for dashboard routes to be accessed by the dashboard app | ||
source: '/dashboard/:path*', | ||
headers: [ | ||
{ | ||
key: 'Access-Control-Allow-Origin', | ||
value: 'http://localhost:8101', // Dashboard app origin | ||
}, | ||
{ | ||
key: 'Access-Control-Allow-Methods', | ||
value: 'GET, POST, PUT, DELETE, OPTIONS', | ||
}, | ||
{ | ||
key: 'Access-Control-Allow-Headers', | ||
value: 'Content-Type, Authorization', | ||
}, | ||
], | ||
}, | ||
{ | ||
// Allow CORS for embedded dashboard routes to be accessed by the dashboard app | ||
source: '/dashboard-embed/:path*', | ||
headers: [ | ||
{ | ||
key: 'Access-Control-Allow-Origin', | ||
value: 'http://localhost:8101', // Dashboard app origin | ||
}, | ||
{ | ||
key: 'Access-Control-Allow-Methods', | ||
value: 'GET, POST, PUT, DELETE, OPTIONS', | ||
}, | ||
{ | ||
key: 'Access-Control-Allow-Headers', | ||
value: 'Content-Type, Authorization', | ||
}, | ||
], | ||
}, | ||
]; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded localhost origin is a production security risk.
The CORS configuration only allows http://localhost:8101
, which will block embedded docs access in production and staging environments. This creates a critical deployment gap.
Consider replacing the hardcoded origin with an environment variable:
+const dashboardOrigin = process.env.NEXT_PUBLIC_DASHBOARD_ORIGIN || 'http://localhost:8101';
+
async headers() {
return [
{
// Allow CORS for dashboard routes to be accessed by the dashboard app
source: '/dashboard/:path*',
headers: [
{
key: 'Access-Control-Allow-Origin',
- value: 'http://localhost:8101', // Dashboard app origin
+ value: dashboardOrigin,
},
// ... other headers
],
},
{
// Allow CORS for embedded dashboard routes to be accessed by the dashboard app
source: '/dashboard-embed/:path*',
headers: [
{
key: 'Access-Control-Allow-Origin',
- value: 'http://localhost:8101', // Dashboard app origin
+ value: dashboardOrigin,
},
// ... other headers
],
},
];
},
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In docs/next.config.mjs around lines 12-51, the CORS headers hardcode
'http://localhost:8101'; replace that literal with a server-side environment
variable (e.g., process.env.DASHBOARD_ORIGIN) so staging/production can
configure allowed origins. Implement reading the env var at the top of the file,
provide a sensible default of 'http://localhost:8101' for local dev, and support
multiple origins by splitting a comma-separated env value and selecting the
incoming request Origin (or falling back to the default) before setting
Access-Control-Allow-Origin; keep the rest of the CORS headers unchanged.
} | ||
|
||
// Fallback logic for when env var is not set | ||
if (process.env.NODE_ENV === 'development' || window.location.hostname === 'localhost') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (process.env.NODE_ENV === 'development' || window.location.hostname === 'localhost') { | |
if (process.env.NODE_ENV === 'development' || (typeof window !== 'undefined' && window.location.hostname === 'localhost')) { |
The fallback logic uses window.location.hostname
which is undefined during server-side rendering, causing a runtime error.
View Details
Analysis
SSR runtime error in getDocsBaseUrl() function due to unchecked window access
What fails: getDocsBaseUrl()
function in unified-docs-widget.tsx
at line 37 accesses window.location.hostname
without checking if window
exists, causing a ReferenceError during server-side rendering
How to reproduce:
# In server environment (SSR):
node -e "delete global.window; console.log(process.env.NODE_ENV === 'development' || window.location.hostname === 'localhost')"
Result: ReferenceError: window is not defined
- This breaks Next.js SSR for any page using this component
Expected: Should safely check for window existence before accessing it, as confirmed by Next.js client components documentation which states client components are initially server-rendered and cannot access browser APIs like window
during that phase
const { slug } = await params; | ||
const page = dashboardSource.getPage(slug ?? []); | ||
|
||
if (!page) redirect("/"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!page) redirect("/"); | |
if (!page) redirect("/dashboard-embed"); |
Same redirect issue as API embed - redirects to "/" instead of staying in embedded context.
View Details
Analysis
Dashboard embed redirects break embedded iframe experience
What fails: DashboardEmbedPage component in docs/src/app/dashboard-embed/[[...slug]]/page.tsx
redirects to "/" when page not found, which loads the main docs site inside the iframe instead of staying in dashboard embed context
How to reproduce:
# Access non-existent dashboard embed page in iframe
curl -I http://localhost:3000/dashboard-embed/nonexistent-page
Result: Redirects to "/" (HTTP 307), causing iframe to display full docs site instead of dashboard content
Expected: Should redirect to "/dashboard-embed" to maintain embedded context, following the pattern established by docs-embed route which correctly redirects to "/docs-embed/next/overview"
Evidence: The docs-embed route demonstrates correct behavior by staying within its embed context when pages are not found
const { slug } = await params; | ||
const page = apiSource.getPage(slug ?? []); | ||
|
||
if (!page) redirect("/"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The redirect destination "/" will break users trying to access non-existent API pages in embedded context.
View Details
📝 Patch Details
diff --git a/docs/src/app/api-embed/[[...slug]]/page.tsx b/docs/src/app/api-embed/[[...slug]]/page.tsx
index 10f9574d..ed90dd79 100644
--- a/docs/src/app/api-embed/[[...slug]]/page.tsx
+++ b/docs/src/app/api-embed/[[...slug]]/page.tsx
@@ -10,7 +10,7 @@ export default async function ApiEmbedPage({
const { slug } = await params;
const page = apiSource.getPage(slug ?? []);
- if (!page) redirect("/");
+ if (!page) redirect("/api-embed");
const MDX = page.data.body;
diff --git a/docs/src/app/api-embed/page.tsx b/docs/src/app/api-embed/page.tsx
new file mode 100644
index 00000000..ccbd1a79
--- /dev/null
+++ b/docs/src/app/api-embed/page.tsx
@@ -0,0 +1,28 @@
+import { getEmbeddedMDXComponents } from '@/mdx-components';
+import { apiSource } from 'lib/source';
+
+export default async function ApiEmbedIndexPage() {
+ // Get the API overview page as the default
+ const page = apiSource.getPage(['overview']);
+
+ if (!page) {
+ return (
+ <div className="p-6 prose prose-neutral dark:prose-invert max-w-none overflow-x-hidden">
+ <div className="w-full">
+ <h1>API Documentation</h1>
+ <p>Welcome to the Stack Auth API documentation. Please select a specific API endpoint to view its documentation.</p>
+ </div>
+ </div>
+ );
+ }
+
+ const MDX = page.data.body;
+
+ return (
+ <div className="p-6 prose prose-neutral dark:prose-invert max-w-none overflow-x-hidden">
+ <div className="w-full">
+ <MDX components={getEmbeddedMDXComponents()} />
+ </div>
+ </div>
+ );
+}
\ No newline at end of file
Analysis
API embed route redirects to root instead of staying in embedded context
What fails: ApiEmbedPage
in docs/src/app/api-embed/[[...slug]]/page.tsx
redirects to "/"
when no page is found, taking users out of the embedded API documentation context
How to reproduce:
- Navigate to
/api-embed/nonexistent-page
in embedded iframe - Page not found triggers redirect to
"/"
- User is taken to main docs site root instead of staying in API embed context
Result: Users lose embedded API documentation context and navigate to main site
Expected: Should redirect to /api-embed
to maintain embedded context, consistent with docs-embed
route pattern that redirects to /docs-embed/next/overview
Fix: Changed redirect destination from "/"
to "/api-embed"
and created root API embed page for proper fallback experience
} | ||
}).catch(() => { | ||
// Network error or other issue - silently ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | |
}).catch(() => { | |
// Network error or other issue - silently ignore | |
} else { | |
// URL doesn't exist, fall back to original href | |
window.location.href = href; | |
} | |
}).catch(() => { | |
// Network error or other issue - fall back to original href | |
window.location.href = href; |
The link interceptor prevents navigation when URL checking fails, creating broken links that don't work when clicked.
View Details
Analysis
Link interceptor blocks navigation when URL validation fails
What fails: EmbeddedLinkInterceptor.handleClick() calls preventDefault() then checkUrlExists(), but navigation is permanently blocked if URL check fails (returns false or throws error)
How to reproduce:
- Load any page with embedded layout (e.g.,
/docs-embed/some-page
) - Click on a link to
/docs/non-existent-page
or any URL that fails HEAD request validation - Observe the link becomes completely non-functional
Result: Link appears clickable but does nothing when clicked. User gets no feedback or navigation.
Expected: Link should either navigate to embedded URL (if it exists) or fallback to original URL (if validation fails), ensuring users can always navigate even during network issues or false negatives from HEAD requests.
Root cause: Lines 130-137 in embedded-link-interceptor.tsx only handle the success case of checkUrlExists(), leaving failed checks with no navigation action.
|
||
window.addEventListener('message', handleMessage); | ||
return () => window.removeEventListener('message', handleMessage); | ||
}, [selectedPlatform, docContent]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The postMessage handler useEffect has docContent
in dependencies, causing it to re-register the event listener every time content changes.
View Details
📝 Patch Details
diff --git a/apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx b/apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
index 7f8d8beb..224dc611 100644
--- a/apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
+++ b/apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx
@@ -2,7 +2,7 @@
import { ArrowLeft, BookOpen, ExternalLink, Loader2, Menu } from 'lucide-react';
import { usePathname } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
import { getPublicEnvVar } from '../../lib/env';
type UnifiedDocsWidgetProps = {
@@ -152,6 +152,12 @@ export function UnifiedDocsWidget({ isActive }: UnifiedDocsWidgetProps) {
const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);
const [isSidebarVisible, setIsSidebarVisible] = useState(false);
const [platformChangeSource, setPlatformChangeSource] = useState<'manual' | 'iframe'>('manual');
+ const docContentRef = useRef<DocContent | null>(null);
+
+ // Keep docContentRef current
+ useEffect(() => {
+ docContentRef.current = docContent;
+ }, [docContent]);
// Load documentation when the component becomes active, doc type changes, platform changes, or pathname changes
useEffect(() => {
@@ -227,10 +233,11 @@ export function UnifiedDocsWidget({ isActive }: UnifiedDocsWidgetProps) {
setSelectedPlatform(detectedPlatform);
// Update docContent to reflect the new URL without reloading the iframe
- if (docContent && event.data.pathname) {
+ const currentDocContent = docContentRef.current;
+ if (currentDocContent && event.data.pathname) {
const newUrl = `${getDocsBaseUrl()}${event.data.pathname}`;
setDocContent({
- ...docContent,
+ ...currentDocContent,
url: newUrl
});
}
@@ -243,7 +250,7 @@ export function UnifiedDocsWidget({ isActive }: UnifiedDocsWidgetProps) {
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
- }, [selectedPlatform, docContent]);
+ }, [selectedPlatform]);
// Handle iframe load events
const handleIframeLoad = (event: React.SyntheticEvent<HTMLIFrameElement>) => {
Analysis
Unnecessary event listener re-registration in postMessage useEffect
What fails: The postMessage useEffect in UnifiedDocsWidget
(lines 207-246) includes docContent
in dependencies, causing the event listener to be removed and re-added every time docContent
changes.
How to reproduce:
- Open dashboard component with docs widget active
- Navigate between different dashboard pages (users → teams → emails)
- Each navigation triggers
docContent
state change - Event listener gets re-registered on every change
Result: Performance degradation from unnecessary removeEventListener
/addEventListener
calls. During rapid navigation, brief moments exist where no listener is active.
Expected: Event listener should be registered once and
5B10
use current docContent
via ref, following React's separation of events from effects pattern
Fix applied: Used useRef
to store current docContent
value, allowing handler to access latest state without including it in useEffect dependencies.
headers: [ | ||
{ | ||
key: 'Access-Control-Allow-Origin', | ||
value: 'http://localhost:8101', // Dashboard app origin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CORS headers are hardcoded to localhost:8101 which will break in production environments where the dashboard runs on different origins.
View Details
📝 Patch Details
diff --git a/docs/.env.development b/docs/.env.development
index b3d3207d..232b9049 100644
--- a/docs/.env.development
+++ b/docs/.env.development
@@ -1,6 +1,7 @@
# Contains the credentials for the internal project of Stack's default development environment setup.
# Do not use in a production environment, instead replace it with actual values gathered from https://app.stack-auth.com.
NEXT_PUBLIC_STACK_API_URL=http://localhost:8102
+NEXT_PUBLIC_STACK_DASHBOARD_URL=http://localhost:8101
NEXT_PUBLIC_STACK_PROJECT_ID=internal
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=this-publishable-client-key-is-for-local-development-only
STACK_SECRET_SERVER_KEY=this-secret-server-key-is-for-local-development-only
diff --git a/docs/next.config.mjs b/docs/next.config.mjs
index c0ab4ac2..2bcfe92e 100644
--- a/docs/next.config.mjs
+++ b/docs/next.config.mjs
@@ -17,7 +17,7 @@ const config = {
headers: [
{
key: 'Access-Control-Allow-Origin',
- value: 'http://localhost:8101', // Dashboard app origin
+ value: process.env.NEXT_PUBLIC_STACK_DASHBOARD_URL || 'http://localhost:8101',
},
{
key: 'Access-Control-Allow-Methods',
@@ -35,7 +35,7 @@ const config = {
headers: [
{
key: 'Access-Control-Allow-Origin',
- value: 'http://localhost:8101', // Dashboard app origin
+ value: process.env.NEXT_PUBLIC_STACK_DASHBOARD_URL || 'http://localhost:8101',
},
{
key: 'Access-Control-Allow-Methods',
@@ -98,4 +98,3 @@ const config = {
};
export default withMDX(config);
-
Analysis
CORS configuration hardcoded to localhost:8101 prevents production dashboard embedding
What fails: The Next.js CORS headers in docs/next.config.mjs
lines 20 and 38 are hardcoded to 'http://localhost:8101'
, preventing the production Stack Auth dashboard (https://app.stack-auth.com
) from embedding docs pages via iframe
How to reproduce:
# Production dashboard at https://app.stack-auth.com tries to embed docs
curl -H "Origin: https://app.stack-auth.com" -I http://localhost:8104/dashboard/overview
# Returns no Access-Control-Allow-Origin header or localhost:8101 only
Result: Production dashboard cannot embed docs content due to CORS policy violation. Browser blocks cross-origin requests from https://app.stack-auth.com
to docs site.
Expected: Should allow the production dashboard origin https://app.stack-auth.com
per Stack Auth production documentation which shows dashboard at app.stack-auth.com
Context: The docs site has /dashboard-embed/
routes specifically designed for iframe embedding from the dashboard, with embedded layouts and link interceptors, confirming this is an intended cross-origin use case.
Important
Adds a unified documentation widget to the dashboard, enabling in-app viewing and switching of documentation types with platform-specific adaptations.
UnifiedDocsWidget
tostack-companion.tsx
for viewing docs within the dashboard.docs/src/app
forapi-embed
,dashboard-embed
, anddocs-embed
.EmbeddedLinkInterceptor
andPlatformChangeNotifier
for link handling and platform change notifications.generate-docs.js
to include dashboard docs generation.NEXT_PUBLIC_STACK_DOCS_BASE_URL
to.env.development
andenv.tsx
.next.config.mjs
for dashboard embedding.global.css
to support embedded content.EmbeddedLink
component for MDX link handling inmdx-components.tsx
.This description was created by
for 5760b90. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
New Features
Documentation
Chores