Skip to content

[Docs][UI] - Components pop-up dynamic-codeblock#877

Merged
madster456 merged 15 commits intodevfrom
docs-dynamic-codeblock-updates
Sep 10, 2025
Merged

[Docs][UI] - Components pop-up dynamic-codeblock#877
madster456 merged 15 commits intodevfrom
docs-dynamic-codeblock-updates

Conversation

@madster456
Copy link
Collaborator

@madster456 madster456 commented Sep 4, 2025

Adds a dynamic popup codeblock on components pages for components that have live examples with prop manipulation.

image

Important

Adds a dynamic code block overlay feature to documentation pages, enhancing code example interaction with new components, hooks, and styling.

  • Behavior:
    • Adds DynamicCodeblockOverlay in dynamic-code-block-overlay.tsx for interactive code display with syntax highlighting, copy, expand/collapse, and close features.
    • Integrates CodeOverlayProvider and useCodeOverlay in use-code-overlay.tsx to manage overlay state and behavior.
    • Updates DocsLayout in docs.tsx to include the code overlay in the documentation layout.
  • Components:
    • DynamicCodeblock in dynamic-code-block.tsx now supports overlay mode with a floating "View Code" button.
    • StackContainer in stack-container.tsx updated for better layout and styling.
  • Styling:
    • Adds stack-reset.css for isolating stack component styles from global styles.
    • Updates stack-team-switcher.tsx to use new styling and layout for team switcher demos.

This description was created by Ellipsis for 4ef6c67. You can customize this summary. It will automatically update as commits are pushed.


Review by RecurseML

🔍 Review performed on 8424c4d..9c860b3

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (5)

docs/src/components/mdx/dynamic-code-block-overlay.tsx
docs/src/components/mdx/dynamic-code-block.tsx
docs/src/components/layouts/docs.tsx
docs/src/hooks/use-code-overlay.tsx
docs/src/components/stack-auth/stack-team-switcher.tsx

⏭️ Files skipped (trigger manually) (2)
Locations Trigger Analysis
docs/src/components/mdx/stack-container.tsx analyze
docs/src/components/mdx/stack-reset.css analyze

Need help? Join our Discord

Summary by CodeRabbit

  • New Features

    • Interactive code overlay in docs with syntax-highlighted view, copy-to-clipboard, expand/collapse, ESC-to-close, and floating “View Code” trigger; responsive sizing and auto-open behavior.
    • Adds overlay provider/hooks to control overlay state and exposes a reusable overlay component and trigger.
  • Refactor

    • Integrates the overlay into DocsLayout and updates sidebar composition without changing public APIs.
  • Style

    • Adjusts Stack layout/title positioning, adds scoped stack CSS reset, and updates team-switcher demo wrappers.

@vercel
Copy link

vercel bot commented Sep 4, 2025

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

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Sep 10, 2025 6:48am
stack-dashboard Ready Ready Preview Comment Sep 10, 2025 6:48am
stack-demo Ready Ready Preview Comment Sep 10, 2025 6:48am
stack-docs Ready Ready Preview Comment Sep 10, 2025 6:48am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 4, 2025

Note

Other AI code review bot(s) detected

CodeRabbit 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.

Walkthrough

Adds a docs-wide code overlay: a provider/context, a dynamic bottom-aligned overlay component with highlighting and controls, integration into DocsLayout, DynamicCodeblock overlay-mode with floating trigger, and scoped stack layout/CSS resets plus minor demo wrapper class changes.

Changes

Cohort / File(s) Summary
Overlay context
docs/src/hooks/use-code-overlay.tsx
New client context/provider CodeOverlayProvider and hook useCodeOverlay managing isOpen, code, language, title, open/close/toggle; closes on route change.
Layout integration
docs/src/components/layouts/docs.tsx
Wraps DocsLayout with CodeOverlayProvider, mounts internal CodeOverlayRenderer (consumes useCodeOverlay) alongside UI to render dynamic overlay.
Overlay UI component
docs/src/components/mdx/dynamic-code-block-overlay.tsx
New client component DynamicCodeblockOverlay and CodeBlockTrigger: bottom-anchored overlay with Shiki highlighting, copy, expand/collapse, escape/close, responsive height, resize handling, and a floating "View Code" trigger.
Dynamic codeblock changes
docs/src/components/mdx/dynamic-code-block.tsx
DynamicCodeblock gets optional useOverlay?: boolean (default true); auto-opens overlay, shows floating trigger, syncs content with overlay; bypasses inline highlighting when overlay enabled; retains inline fallback.
Stack layout & styles
docs/src/components/mdx/stack-container.tsx, docs/src/components/mdx/stack-reset.css, docs/src/components/stack-auth/stack-team-switcher.tsx
StackContainer layout/title repositioned; new scoped .stack-reset CSS file resets typography/spacing for stack content; team-switcher demos gain stack-reset / stack-reset-light wrapper classes for isolation.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DocsPage
  participant DocsLayout
  participant CodeCtx as CodeOverlayProvider
  participant DynBlock as DynamicCodeblock
  participant Overlay as DynamicCodeblockOverlay

  User->>DocsPage: open docs route
  DocsPage->>DocsLayout: render (wrapped by CodeCtx)
  DocsLayout->>CodeCtx: provide context
  DocsPage->>DynBlock: render code block (useOverlay=true)
  DynBlock->>CodeCtx: openOverlay(code, lang, title) [auto or trigger]
  CodeCtx-->>Overlay: isOpen=true + props
  Overlay->>User: show overlay (highlight/copy/expand)
  User-->>Overlay: copy / expand / close
  Overlay->>CodeCtx: closeOverlay()
  CodeCtx-->>Overlay: isOpen=false
  User->>DocsPage: navigate away
  DocsLayout->>CodeCtx: pathname change -> closeOverlay()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I nibble keys and make a trail,
A sliding pane that tells the tale—
"View Code" afloat, a button bright,
I copy, fold, and vanish light.
Hooray! The docs glow through the night. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch docs-dynamic-codeblock-updates

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@greptile-apps greptile-apps bot left a 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 adds a dynamic popup codeblock feature to the documentation site that allows users to view and interact with code examples for components with live prop manipulation. The implementation consists of several key components:

  1. Context-based overlay system: A new React context (useCodeOverlay) manages the global state for code overlays, including visibility, content, and automatic cleanup when navigating between pages.

  2. Dynamic overlay component: The DynamicCodeblockOverlay component provides a full-featured popup with syntax highlighting via Shiki, copy-to-clipboard functionality, responsive design, and keyboard accessibility (ESC to close).

  3. Enhanced component documentation: The team switcher documentation page now includes an interactive demo with prop manipulation controls and dynamic code generation that updates based on the current component configuration.

  4. Styling isolation system: A comprehensive CSS reset system (stack-reset.css) ensures Stack UI components display correctly within the documentation site without interference from global styles.

  5. Layout integration: The overlay system is integrated at the root layout level, ensuring proper z-index layering and global accessibility across all documentation pages.

The feature enhances the developer experience by bridging the gap between static documentation and actual implementation, allowing users to see how different prop configurations translate to actual React/JSX code. The implementation follows React best practices with proper context usage, error handling, and responsive design considerations.

Confidence score: 3/5

  • This PR introduces significant new functionality that could have unexpected interactions with existing documentation components
  • Score reflects the complexity of the overlay positioning system and direct DOM manipulation in event handlers
  • Pay close attention to docs/src/components/mdx/dynamic-code-block.tsx for potential performance issues with scroll/resize listeners

7 files reviewed, 7 comments

Edit Code Review Bot Settings | Greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (4)
docs/src/components/stack-auth/stack-team-switcher.tsx (1)

232-241: Undefined CSS class 'stack-reset-light' + missing stylesheet import

  • The wrappers use a 'stack-reset-light' class that isn’t defined in stack-reset.css. As-is, these wrappers won’t get any reset styling.
  • Also ensure the reset stylesheet is imported somewhere global (e.g., Docs layout) or locally; otherwise even 'stack-reset' won’t apply.

Action:

  • Define a 'stack-reset-light' variant in the CSS (see my CSS comment for a ready diff), and import the stylesheet globally in DocsLayout.
  • If you prefer a quick stopgap, swap 'stack-reset-light' → 'stack-reset' in this file:
-      <div className="flex items-center justify-center p-4 stack-reset-light">
+      <div className="flex items-center justify-center p-4 stack-reset">

Also applies to: 258-266, 272-280, 286-298, 323-331

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

1-4: This file needs to be a Client Component ('use client')

You’re using React/Next client hooks (useState, useEffect, usePathname, useRouter, createPortal). Add the directive at the very top to avoid RSC/hydration errors.

+'use client';
 /**
  * DOCS BASE LAYOUT
  *
docs/src/components/mdx/stack-reset.css (1)

1-59: docs/src/components/mdx/stack-reset.css: define .stack-reset-light and flatten nested rules

  • Add a .stack-reset-light rule (aliasing the .stack-reset styles) to cover the stack-reset-light class used in docs/src/components/stack-auth/stack-team-switcher.tsx.
  • Unnest selectors (e.g. replace the * { … } block inside .stack-reset { … } with flat rules like .stack-reset * { … }) because PostCSS nesting isn’t enabled in the docs’ PostCSS config.
docs/src/components/mdx/dynamic-code-block.tsx (1)

105-109: Fix XSS risk in fallback HTML.

When Shiki fails, raw code is injected without escaping, enabling HTML/JS injection. Escape before injecting.

Apply this diff:

-      } catch (error) {
-        console.error('Error highlighting code:', error);
-        setHighlightedCode(`<pre><code>${code}</code></pre>`);
-      }
+      } catch (error) {
+        console.error('Error highlighting code:', error);
+        setHighlightedCode(`<pre><code>${escapeHtml(code)}</code></pre>`);
+      }

Add this helper (top-level in the file):

function escapeHtml(s: string) {
  return s
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;');
}
🧹 Nitpick comments (13)
docs/src/hooks/use-code-overlay.tsx (1)

26-33: Simplify navigation-close logic; avoid extra state churn

Tracking currentPage in state causes an extra effect run. Use a ref for the previous pathname.

-  const [currentPage, setCurrentPage] = useState('');
+  const prevPathRef = useRef<string | null>(null);
   const pathname = usePathname();
 
   // Close overlay when navigating to a different page
   useEffect(() => {
-    if (pathname !== currentPage && currentPage !== '') {
-      setIsOpen(false);
-    }
-    setCurrentPage(pathname);
-  }, [pathname, currentPage]);
+    if (prevPathRef.current && pathname !== prevPathRef.current) {
+      setIsOpen(false);
+    }
+    prevPathRef.current = pathname;
+  }, [pathname]);
docs/src/components/mdx/dynamic-code-block-overlay.tsx (4)

172-181: Improve accessibility: add landmark and relationships

Declare a landmark and link the header title for screen readers. Also expose expanded state.

-      <div
+      <div
         className={cn(
           "fixed bottom-0 bg-fd-background border-t border-fd-border z-50",
           "transition-all duration-300 ease-out",
           "shadow-2xl",
           "flex flex-col", // Add flex container
           // Position to avoid sidebar overlap - adjust based on sidebar state
           "left-0 right-0",
           isMainSidebarCollapsed ? "md:left-16" : "md:left-64"
         )}
         style={{
           maxHeight: getOptimalHeight(), // Use maxHeight instead of fixed height
         }}
+        role="region"
+        aria-labelledby="dynamic-codeblock-overlay-title"
+        aria-expanded={isExpanded}
       >
...
-              <h3 className="font-semibold text-fd-foreground text-sm sm:text-base truncate">{title}</h3>
+              <h3 id="dynamic-codeblock-overlay-title" className="font-semibold text-fd-foreground text-sm sm:text-base truncate">{title}</h3>

Also applies to: 206-214, 156-170


120-136: Avoid state updates after unmount for copy timeout

Store the timeout id and clear it on unmount.

-import { useEffect, useState } from "react";
+import { useEffect, useRef, useState } from "react";
...
   const [copied, setCopied] = useState(false);
+  const copyTimeoutRef = useRef<number | null>(null);
...
   const handleCopy = () => {
     const copyToClipboard = async () => {
       try {
         await navigator.clipboard.writeText(code);
         setCopied(true);
-        setTimeout(() => setCopied(false), 2000);
+        if (copyTimeoutRef.current) clearTimeout(copyTimeoutRef.current);
+        copyTimeoutRef.current = window.setTimeout(() => setCopied(false), 2000);
       } catch (error) {
         // Handle clipboard error gracefully
         console.error('Failed to copy code:', error instanceof Error ? error.message : 'Unknown error');
         // Could show a toast notification here in the future
       }
     };
 
     // Run async function - all errors are handled within the function
     runAsynchronously(copyToClipboard);
   };
+
+  useEffect(() => {
+    return () => {
+      if (copyTimeoutRef.current) clearTimeout(copyTimeoutRef.current);
+    };
+  }, []);

72-86: Throttle resize handler to reduce layout work on rapid resizes

Current listener updates state on every event. Throttle to an animation frame or a short timeout.

-  useEffect(() => {
-    const updateWindowSize = () => {
-      setWindowSize({ width: window.innerWidth, height: window.innerHeight });
-    };
+  useEffect(() => {
+    let rafId = 0;
+    const updateWindowSize = () => {
+      cancelAnimationFrame(rafId);
+      rafId = requestAnimationFrame(() => {
+        setWindowSize({ width: window.innerWidth, height: window.innerHeight });
+      });
+    };
...
-    return () => window.removeEventListener('resize', updateWindowSize);
+    return () => {
+      cancelAnimationFrame(rafId);
+      window.removeEventListener('resize', updateWindowSize);
+    };
   }, []);

220-224: Theme consistency

Hardcoded #0a0a0a may clash with theming. Prefer a design token or Tailwind var consistent with the docs theme.

-            style={{
-              background: '#0a0a0a'
-            }}
+            className="bg-fd-code"  /* or bg-neutral-950 if available */
docs/src/components/layouts/docs.tsx (1)

1357-1373: LGTM with a minor optimization option

Renderer correctly closes on toggle and reads context safely. Optional: lazy-load the overlay to shave initial JS.

- import { DynamicCodeblockOverlay } from '../mdx/dynamic-code-block-overlay';
+ import dynamic from 'next/dynamic';
+ const DynamicCodeblockOverlay = dynamic(
+   () => import('../mdx/dynamic-code-block-overlay').then(m => m.DynamicCodeblockOverlay),
+   { ssr: false }
+ );
docs/src/components/mdx/dynamic-code-block.tsx (7)

4-4: Defer Shiki to reduce bundle size.

import { codeToHtml } from "shiki" eagerly pulls a heavy dependency into the client bundle even when useOverlay is true (no inline highlight). Dynamically import Shiki inside the effect.

-import { codeToHtml } from "shiki";
+// Defer Shiki to when inline highlighting is needed

@@
   useEffect(() => {
     const updateHighlightedCode = async () => {
       try {
-        const html = await codeToHtml(code, {
+        const { codeToHtml } = await import('shiki');
+        const html = await codeToHtml(code, {
           lang: language,
           theme: 'github-dark',

Also applies to: 84-104


151-154: Remove ineffective !important in inline style.

!important doesn’t work in React inline styles and is redundant with Tailwind classes already applied.

-        <div
-          className="rounded-lg border bg-[#0a0a0a] p-4 overflow-auto max-h-[500px] text-sm"
-          style={{
-            background: '#0a0a0a !important',
-          }}
-        >
+        <div
+          className="rounded-lg border bg-[#0a0a0a] p-4 overflow-auto max-h-[500px] text-sm"
+        >

12-16: Make auto-open user/configurable.

Auto-opening can feel intrusive. Expose a prop to control it.

 type DynamicCodeblockProps = {
   code: string,
   language?: string,
   title?: string,
   useOverlay?: boolean,
+  autoOpen?: boolean,
 }
 
-export function DynamicCodeblock({ code, language = 'tsx', title, useOverlay = true }: DynamicCodeblockProps) {
+export function DynamicCodeblock({ code, language = 'tsx', title, useOverlay = true, autoOpen = true }: DynamicCodeblockProps) {
@@
-  useEffect(() => {
-    if (useOverlay && code && !hasInitialized) {
+  useEffect(() => {
+    if (useOverlay && autoOpen && code && !hasInitialized) {
       const timer = setTimeout(() => {
         openOverlay(code, language, title);
         setHasInitialized(true);
       }, 100);
       return () => clearTimeout(timer);
     }
-  }, [useOverlay, code, language, title, openOverlay, hasInitialized]);
+  }, [useOverlay, autoOpen, code, language, title, openOverlay, hasInitialized]);

Also applies to: 62-74


75-81: Avoid redundant overlay refreshes.

Prevent repeated openOverlay calls when props haven’t effectively changed.

+  const [lastOverlayKey, setLastOverlayKey] = useState<string | null>(null);
@@
-  useEffect(() => {
-    if (useOverlay && code && hasInitialized && isOpen) {
-      openOverlay(code, language, title);
-    }
-  }, [code, language, title, useOverlay, openOverlay, hasInitialized, isOpen]);
+  useEffect(() => {
+    if (!useOverlay || !code || !hasInitialized || !isOpen) return;
+    const key = `${language}|${title}|${code}`;
+    if (key !== lastOverlayKey) {
+      openOverlay(code, language, title);
+      setLastOverlayKey(key);
+    }
+  }, [code, language, title, useOverlay, openOverlay, hasInitialized, isOpen, lastOverlayKey]);

24-37: Throttle the resize listener.

Directly setting state on every resize event can cause jank. Throttle via rAF.

   useEffect(() => {
-    const updateWindowWidth = () => {
-      setWindowWidth(window.innerWidth);
-    };
+    let raf = 0;
+    const updateWindowWidth = () => {
+      if (raf) return;
+      raf = requestAnimationFrame(() => {
+        raf = 0;
+        setWindowWidth(window.innerWidth);
+      });
+    };
@@
-    return () => window.removeEventListener('resize', updateWindowWidth);
+    return () => {
+      window.removeEventListener('resize', updateWindowWidth);
+      if (raf) cancelAnimationFrame(raf);
+    };
   }, []);

39-61: Prefer IntersectionObserver for near-bottom detection.

A sentinel with IntersectionObserver is cheaper than per-scroll math and respects passive behavior.

I can provide a small useNearBottom hook using an invisible footer sentinel if you want it bundled in this PR.


120-143: Disable the trigger when no code is available.

Avoid opening an empty overlay and improve a11y.

-    return !isOpen ? (
+    return !isOpen ? (
       <button
-        onClick={() => openOverlay(code, language, title)}
-        className="fixed bottom-6 z-30 flex items-center gap-1.5 px-3 py-2 bg-fd-primary text-fd-primary-foreground rounded-full shadow-lg hover:scale-105 active:scale-95 transition-all duration-300 border border-fd-primary/20"
+        onClick={() => code?.trim() && openOverlay(code, language, title)}
+        disabled={!code?.trim()}
+        aria-disabled={!code?.trim()}
+        className="fixed bottom-6 z-30 flex items-center gap-1.5 px-3 py-2 bg-fd-primary text-fd-primary-foreground rounded-full shadow-lg hover:scale-105 active:scale-95 transition-all duration-300 border border-fd-primary/20 disabled:opacity-50 disabled:pointer-events-none"
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 8424c4d and 9c860b3.

📒 Files selected for processing (7)
  • docs/src/components/layouts/docs.tsx (4 hunks)
  • docs/src/components/mdx/dynamic-code-block-overlay.tsx (1 hunks)
  • docs/src/components/mdx/dynamic-code-block.tsx (2 hunks)
  • docs/src/components/mdx/stack-container.tsx (1 hunks)
  • docs/src/components/mdx/stack-reset.css (1 hunks)
  • docs/src/components/stack-auth/stack-team-switcher.tsx (5 hunks)
  • docs/src/hooks/use-code-overlay.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/hooks/use-code-overlay.tsx
  • docs/src/components/stack-auth/stack-team-switcher.tsx
  • docs/src/components/mdx/stack-container.tsx
  • docs/src/components/mdx/dynamic-code-block-overlay.tsx
  • docs/src/components/mdx/dynamic-code-block.tsx
  • docs/src/components/layouts/docs.tsx
🧬 Code graph analysis (3)
docs/src/components/mdx/dynamic-code-block-overlay.tsx (2)
docs/src/components/layouts/sidebar-context.tsx (1)
  • useSidebar (46-49)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
docs/src/components/mdx/dynamic-code-block.tsx (2)
docs/src/hooks/use-code-overlay.tsx (1)
  • useCodeOverlay (66-72)
docs/src/components/layouts/sidebar-context.tsx (1)
  • useSidebar (46-49)
docs/src/components/layouts/docs.tsx (2)
docs/src/hooks/use-code-overlay.tsx (2)
  • CodeOverlayProvider (18-64)
  • useCodeOverlay (66-72)
docs/src/components/mdx/dynamic-code-block-overlay.tsx (1)
  • DynamicCodeblockOverlay (18-234)
🪛 ast-grep (0.38.6)
docs/src/components/mdx/dynamic-code-block-overlay.tsx

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

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
docs/src/components/mdx/dynamic-code-block-overlay.tsx

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

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

(lint/security/noDangerouslySetInnerHtml)

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

3-3: Deep import required until public API is available
The @stackframe/stack-shared package.json defines no subpath exports for utils/promises and its root index doesn’t re-export runAsynchronously, so the only way to import it today is via the deep path. Keep the import as is until a public export is added.

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

65-66: LGTM: Overlay UI import is correctly scoped

Importing DynamicCodeblockOverlay here keeps layout concerns centralized. No issues.


1119-1202: LGTM: Provider wiring and layout integration

Wrapping the layout with CodeOverlayProvider and maintaining existing Nav/Sidebar structure looks solid. Search dialog and slots remain intact.

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

20-23: All DynamicCodeblock usages are already covered by CodeOverlayProvider via the docs layout. No further action required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
docs/src/components/mdx/dynamic-code-block-overlay.tsx (3)

35-41: SSR-safe window usage looks good

Guarded access avoids hydration pitfalls; sensible defaults used.


32-34: Nice: memoized line count

Addresses the earlier perf note.


92-119: Sanitize highlighted HTML and escape fallback to prevent XSS

dangerouslySetInnerHTML renders raw HTML. Sanitize the Shiki output and HTML‑escape the fallback to avoid XSS in docs.

Apply within this range:

-        setHighlightedCode(html);
+        setHighlightedCode(DOMPurify.sanitize(html));
       } catch (error) {
         console.error('Error highlighting code:', error);
-        setHighlightedCode(`<pre><code>${code}</code></pre>`);
+        setHighlightedCode(`<pre><code>${escapeHtml(code)}</code></pre>`);

Additions outside this range:

// Top-level import
+import DOMPurify from "isomorphic-dompurify";
// Helper (module-local; place near the component)
+function escapeHtml(input: string): string {
+  return input
+    .replaceAll('&', '&amp;')
+    .replaceAll('<', '&lt;')
+    .replaceAll('>', '&gt;')
+    .replaceAll('"', '&quot;')
+    .replaceAll("'", '&#39;');
+}
🧹 Nitpick comments (6)
docs/src/components/mdx/dynamic-code-block-overlay.tsx (6)

231-233: Document sanitization and suppress the lint warning in place

Keep dangerouslySetInnerHTML but annotate that content is sanitized to satisfy Biome.

           <div
             className="[&_*]:!bg-transparent [&_pre]:!bg-transparent [&_code]:!bg-transparent text-xs sm:text-sm leading-[1.4] sm:leading-[1.5] [&_pre]:text-xs [&_pre]:sm:text-sm [&_code]:text-xs [&_code]:sm:text-sm [&_pre]:leading-[1.4] [&_pre]:sm:leading-[1.5] [&_code]:leading-[1.4] [&_code]:sm:leading-[1.5] [&_pre]:m-0 [&_pre]:p-0 [&_pre]:overflow-visible"
-              dangerouslySetInnerHTML={{ __html: highlightedCode }}
+              {/* biome-ignore lint/security/noDangerouslySetInnerHtml: Shiki HTML sanitized with DOMPurify above */}
+              dangerouslySetInnerHTML={{ __html: highlightedCode }}
           />

143-153: Attach Escape key listener only when overlay is open

Avoid a global listener when closed; simpler handler too.

-useEffect(() => {
-  const handleEscape = (e: KeyboardEvent) => {
-    if (e.key === 'Escape' && isOpen) {
-      onToggle?.(false);
-    }
-  };
-  document.addEventListener('keydown', handleEscape);
-  return () => document.removeEventListener('keydown', handleEscape);
-}, [isOpen, onToggle]);
+useEffect(() => {
+  if (!isOpen) return;
+  const handleEscape = (e: KeyboardEvent) => {
+    if (e.key === 'Escape') onToggle?.(false);
+  };
+  document.addEventListener('keydown', handleEscape);
+  return () => document.removeEventListener('keydown', handleEscape);
+}, [isOpen, onToggle]);

75-91: Throttle resize recalcs to reduce layout thrash

Use rAF to coalesce rapid resize events.

 useEffect(() => {
   const updateWindowSize = () => {
     if (typeof window !== 'undefined') {
       setWindowSize({ width: window.innerWidth, height: window.innerHeight });
     }
   };
 
   // Set initial window size
   updateWindowSize();
 
   // Add resize listener
-  window.addEventListener('resize', updateWindowSize);
+  let raf = 0;
+  const onResize = () => {
+    cancelAnimationFrame(raf);
+    raf = requestAnimationFrame(updateWindowSize);
+  };
+  window.addEventListener('resize', onResize);
 
-  return () => window.removeEventListener('resize', updateWindowSize);
+  return () => {
+    cancelAnimationFrame(raf);
+    window.removeEventListener('resize', onResize);
+  };
 }, []);

162-176: Add dialog semantics for accessibility

Mark the overlay as a modal dialog; improves SR navigation.

-      <div
+      <div
         className={cn(
           "fixed bottom-0 bg-fd-background border-t border-fd-border z-50",
           "transition-all duration-300 ease-out",
           "shadow-2xl",
           "flex flex-col", // Add flex container
           // Position to avoid sidebar overlap - adjust based on sidebar state
           "left-0 right-0",
           isMainSidebarCollapsed ? "md:left-16" : "md:left-64"
         )}
         style={{
           maxHeight: getOptimalHeight(), // Use maxHeight instead of fixed height
         }}
+        role="dialog"
+        aria-modal="true"
+        aria-label={title}
+        tabIndex={-1}
       >

3-3: Avoid deep import path to shared package

Deep imports are brittle; prefer the package entrypoint if available.

-import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
+import { runAsynchronously } from "@stackframe/stack-shared/utils/promises";

92-119: Reduce bundle size by lazy-loading Shiki

Defer Shiki until needed to keep docs’ initial JS smaller.

Example:

// move import out; lazy-load inside the effect
useEffect(() => {
  const updateHighlightedCode = async () => {
    try {
      const { codeToHtml } = await import("shiki/bundle/web");
      const html = await codeToHtml(code, { /* ... */ });
      setHighlightedCode(DOMPurify.sanitize(html));
    } catch (e) {
      setHighlightedCode(`<pre><code>${escapeHtml(code)}</code></pre>`);
    }
  };
  runAsynchronously(updateHighlightedCode());
}, [code, language]);
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 9c860b3 and cdcca17.

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

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • docs/src/components/mdx/dynamic-code-block-overlay.tsx
🧬 Code graph analysis (1)
docs/src/components/mdx/dynamic-code-block-overlay.tsx (2)
docs/src/components/layouts/sidebar-context.tsx (1)
  • useSidebar (46-49)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
🪛 Biome (2.1.2)
docs/src/components/mdx/dynamic-code-block-overlay.tsx

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

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

(lint/security/noDangerouslySetInnerHtml)

🪛 ast-grep (0.38.6)
docs/src/components/mdx/dynamic-code-block-overlay.tsx

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

(react-unsafe-html-injection)

⏰ 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). (8)
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: all-good
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: setup-tests
  • GitHub Check: build (22.x)
  • GitHub Check: Security Check

@madster456 madster456 changed the title [Docs] Components pop-up dynamic-codeblock [Docs][UI] Components pop-up dynamic-codeblock Sep 5, 2025
@madster456 madster456 changed the title [Docs][UI] Components pop-up dynamic-codeblock [Docs][UI] - Components pop-up dynamic-codeblock Sep 5, 2025
@N2D4 N2D4 removed their assignment Sep 9, 2025
@madster456 madster456 merged commit 0ab5fcc into dev Sep 10, 2025
18 checks passed
@madster456 madster456 deleted the docs-dynamic-codeblock-updates branch September 10, 2025 06:54
sicarius97 pushed a commit to sicarius97/stack-auth that referenced this pull request Sep 10, 2025
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

Adds a dynamic popup codeblock on components pages for components that
have live examples with prop manipulation.

<img width="1253" height="298" alt="image"
src="https://github.com/user-attachments/assets/6d046c5f-77c1-4bec-98ed-fd0c2e347635"
/>


<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Adds a dynamic code block overlay feature to documentation pages,
enhancing code example interaction with new components, hooks, and
styling.
> 
>   - **Behavior**:
> - Adds `DynamicCodeblockOverlay` in `dynamic-code-block-overlay.tsx`
for interactive code display with syntax highlighting, copy,
expand/collapse, and close features.
> - Integrates `CodeOverlayProvider` and `useCodeOverlay` in
`use-code-overlay.tsx` to manage overlay state and behavior.
> - Updates `DocsLayout` in `docs.tsx` to include the code overlay in
the documentation layout.
>   - **Components**:
> - `DynamicCodeblock` in `dynamic-code-block.tsx` now supports overlay
mode with a floating "View Code" button.
> - `StackContainer` in `stack-container.tsx` updated for better layout
and styling.
>   - **Styling**:
> - Adds `stack-reset.css` for isolating stack component styles from
global styles.
> - Updates `stack-team-switcher.tsx` to use new styling and layout for
team switcher demos.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 4ef6c67. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>

----


<!-- ELLIPSIS_HIDDEN -->

<!-- RECURSEML_SUMMARY:START -->
## Review by RecurseML

_🔍 Review performed on
[8424c4d..9c860b3](stack-auth/stack-auth@8424c4d...9c860b3067a3d93a3df660d151d8454f516129fe)_

✨ No bugs found, your code is sparkling clean

<details>
<summary>✅ Files analyzed, no issues (5)</summary>

  • `docs/src/components/mdx/dynamic-code-block-overlay.tsx`
  • `docs/src/components/mdx/dynamic-code-block.tsx`
  • `docs/src/components/layouts/docs.tsx`
  • `docs/src/hooks/use-code-overlay.tsx`
  • `docs/src/components/stack-auth/stack-team-switcher.tsx`
</details>

<details>
<summary>⏭️ Files skipped (trigger manually) (2)</summary>

| Locations | Trigger Analysis |
|-----------|------------------|
`docs/src/components/mdx/stack-container.tsx` |
[![analyze](https://img.shields.io/badge/analyze-238636?style=plastic)](https://squash-322339097191.europe-west3.run.app/interactive/0af8763e3d3b011d25ba56322596b6af1f11ecc409a1a617dfa65424263568b8/?repo_owner=stack-auth&repo_name=stack-auth&pr_number=877)
`docs/src/components/mdx/stack-reset.css` |
[![analyze](https://img.shields.io/badge/analyze-238636?style=plastic)](https://squash-322339097191.europe-west3.run.app/interactive/675653c9b4eee353126aeb1239a30eebf8458add6c467614a2281bc849f1ad14/?repo_owner=stack-auth&repo_name=stack-auth&pr_number=877)
</details>

[![Need help? Join our
Discord](https://img.shields.io/badge/Need%20help%3F%20Join%20our%20Discord-5865F2?style=plastic&logo=discord&logoColor=white)](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_SUMMARY:END -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* New Features
* Interactive code overlay in docs with syntax-highlighted view,
copy-to-clipboard, expand/collapse, ESC-to-close, and floating “View
Code” trigger; responsive sizing and auto-open behavior.
* Adds overlay provider/hooks to control overlay state and exposes a
reusable overlay component and trigger.

* Refactor
* Integrates the overlay into DocsLayout and updates sidebar composition
without changing public APIs.

* Style
* Adjusts Stack layout/title positioning, adds scoped stack CSS reset,
and updates team-switcher demo wrappers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
Copy link

@recurseml recurseml bot left a comment

Choose a reason for hiding this comment

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

⚙️ Scanning changes in bc2b7a1..860d534 for bugs...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants