Skip to content

Cmd K#1037

Merged
N2D4 merged 38 commits intodevfrom
cmd-k
Dec 4, 2025
Merged

Cmd K#1037
N2D4 merged 38 commits intodevfrom
cmd-k

Conversation

@Developing-Gamer
Copy link
Copy Markdown
Contributor

@Developing-Gamer Developing-Gamer commented Dec 1, 2025


Note

Introduces a dev-only Spotlight command palette with AI-powered doc chat and app discovery/enable flows, updates the design guide, adds a dev port banner, and improves the companion drawer.

  • Dashboard UI / Navigation:
    • Add dev-only Spotlight command palette (CmdKTrigger, CmdKSearch) with nested navigation, previews, and keyboard controls.
    • Integrate Spotlight into sidebar-layout.tsx; enable/disable per environment.
  • AI Search:
    • New API POST /api/ai-search (apps/dashboard/src/app/api/ai-search/route.ts) streaming mock responses with a strict system prompt.
    • New AI chat preview component (components/commands/ask-ai.tsx) rendered within Spotlight.
  • App Discovery:
    • Command palette supports installed/uninstalled apps with previews and in-place enable flow (cmdk-commands.tsx, lib/apps-utils.ts).
  • Companion Drawer:
    • Refine handle/layout logic and conditional rendering for smooth open/close and split-screen behavior (components/stack-companion.tsx).
  • Dev Experience:
    • Add dismissible development port banner (development-port-display.tsx) and wire into layout.tsx.
  • Design System / Docs:
    • Replace DESIGN_GUIDE.md with comprehensive DESIGN-GUIDE.md covering colors, surfaces, interactions, and patterns.
    • Update AGENTS.md with env var prefix rule.
  • Utilities / Components:
    • Minor class fix in PacificaCard (grow-1).
  • Dependencies:
    • Add ai, @ai-sdk/openai, react-markdown; lockfile updates.

Written by Cursor Bugbot for commit 025c5c1. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • AI-powered command palette and spotlight search with app discovery and enable flows.
    • Interactive AI chat preview for running and exploring queries inside the dashboard.
    • Development port banner for local dev visibility (dismissible).
  • Documentation

    • Consolidated and updated dashboard design guide covering visual and interaction patterns.
  • Bug Fixes

    • Improved drawer/handle behavior, animations, and touch resizing.
  • Refactor

    • Header and layout reorganized for better spacing and search integration.

✏️ Tip: You can customize this high-level summary in your review settings.

@Developing-Gamer Developing-Gamer requested a review from N2D4 December 1, 2025 22:50
@vercel
Copy link
Copy Markdown

vercel bot commented Dec 1, 2025

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

Project Deployment Preview Comments Updated (UTC)
stack-dashboard Ready Ready Preview Comment Dec 4, 2025 5:37pm
stack-demo Ready Ready Preview Comment Dec 4, 2025 5:37pm
stack-docs Ready Ready Preview Comment Dec 4, 2025 5:37pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 1, 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 CmdK command palette (UI, commands, previews, caches), an AI-search POST streaming route and AI chat preview, design-document swaps, new AI/markdown dependencies, development-only UI (port banner, CmdK trigger), layout and drawer rendering tweaks, and utility helpers for app IDs.

Changes

Cohort / File(s) Summary
Design & Docs
apps/dashboard/DESIGN-GUIDE.md, apps/dashboard/DESIGN_GUIDE.md, AGENTS.md
Added new DESIGN-GUIDE.md, removed legacy DESIGN_GUIDE.md, and added STACK_ env var naming guidance in AGENTS.md.
Dependencies
apps/dashboard/package.json
Added @ai-sdk/openai, ai, and react-markdown dependencies.
CmdK — UI & Commands
apps/dashboard/src/components/cmdk-search.tsx, apps/dashboard/src/components/cmdk-commands.tsx
New command-palette UI (CmdKTrigger, CmdKSearch, CmdKResultsList) and command-generation system with previews, caches, helpers, and AI-aware command entries.
AI Search & Chat
apps/dashboard/src/app/api/ai-search/route.ts, apps/dashboard/src/components/commands/ask-ai.tsx
New POST /api/ai-search route that streams a mock LLM response; AIChatPreview with word-by-word streaming, markdown rendering, copy helpers, input/focus handling.
Layout / Integration
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx, apps/dashboard/src/app/layout.tsx
Integrated CmdK/Spotlight dev wrapper and trigger into project header, adjusted header growth/layout, and replaced inline DevelopmentPortDisplay with external import.
Dev UI
apps/dashboard/src/app/development-port-display.tsx
New dismissible DevelopmentPortDisplay banner reading NEXT_PUBLIC_STACK_PORT_PREFIX.
Components & UX tweaks
apps/dashboard/src/components/stack-companion.tsx, apps/dashboard/src/components/pacifica/card.tsx
StackCompanion: conditional drawer render guards, touch start support, handle/layout tweaks; pacifica/card: Tailwind class change flex-grow-1grow-1.
Utilities
apps/dashboard/src/lib/apps-utils.ts
New helpers: getAllAvailableAppIds() and getUninstalledAppIds(installedApps).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CmdK as CmdK UI
    participant API as /api/ai-search
    participant LLM as MockLanguageModelV1
    participant Chat as AIChatPreview

    User->>CmdK: open palette / type query
    CmdK->>API: POST messages (debounced)
    API->>LLM: streamText(system + messages)
    LLM-->>API: chunked delta stream
    API-->>CmdK: EventStream response
    CmdK->>Chat: deliver streamed chunks
    Chat->>Chat: useWordStreaming → progressively render (ReactMarkdown)
    Chat-->>User: display formatted streaming reply
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus review on:
    • apps/dashboard/src/components/cmdk-search.tsx — keyboard navigation, nested selection, focus lifecycle, mobile/desktop previews.
    • apps/dashboard/src/components/commands/ask-ai.tsx — streaming lifecycle, cancellation, markdown rendering, auto-scroll edge cases.
    • apps/dashboard/src/app/api/ai-search/route.ts — streaming response format and contract with frontend.

Possibly related PRs

Suggested reviewers

  • TheUntraceable

Poem

🐰 I hopped through code beneath moonlight,

A palette glowed, commands took flight,
Streams of words like ribbons flowed,
Markdown carrots on the road,
The dashboard danced — a rabbit's night 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Cmd K' is overly vague and does not clearly summarize the main change; it lacks context about what this feature/component is or does. Consider a more descriptive title such as 'Add Cmd+K command palette with AI chat and app discovery' to better convey the pull request's main objective and scope.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description follows the repository template and includes a detailed Cursor Bugbot summary covering all major changes: UI/navigation, AI search, app discovery, companion drawer, dev experience, design system, utilities, and dependencies.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cmd-k

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

❤️ Share

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

Copy link
Copy Markdown
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 (1)
apps/dashboard/src/components/commands/ask-ai.tsx (1)

11-23: Clear copy timeout on unmount to avoid stale state updates

setTimeout in CopyButton isn’t cleared if the component unmounts before it fires, which can cause setCopied to run on an unmounted component. This was already flagged in a previous review and is still present.

You can store the timeout id in a ref and clear it on unmount:

 const CopyButton = memo(function CopyButton({ text, className, size = "sm" }: {
   text: string,
   className?: string,
   size?: "sm" | "xs",
 }) {
   const [copied, setCopied] = useState(false);
+  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
+
+  useEffect(() => {
+    return () => {
+      if (timeoutRef.current) {
+        clearTimeout(timeoutRef.current);
+        timeoutRef.current = null;
+      }
+    };
+  }, []);

   const handleCopy = useCallback(async () => {
     await navigator.clipboard.writeText(text);
     setCopied(true);
-    setTimeout(() => setCopied(false), 1500);
-  }, [text]);
+    timeoutRef.current = setTimeout(() => setCopied(false), 1500);
+  }, [text]);
🧹 Nitpick comments (3)
apps/dashboard/src/components/commands/ask-ai.tsx (3)

65-223: Markdown / code / link renderers look solid; minor reuse opportunity

The inline code, code block, smart link, and markdown component setup is clean, consistent, and uses the recommended transition pattern (transition-colors hover:transition-none). This follows the UI guidelines and integrates nicely with the local CopyButton. As per coding guidelines, the transitions and lack of toasts for errors are appropriate.

If you want stricter consistency and less duplication, consider reusing the shared CopyButton from packages/stack-ui/src/components/copy-button.tsx and styling it via props/classes instead of maintaining a separate variant here, but the current approach is acceptable.


280-313: Reset word-streaming when a new assistant reply starts

useWordStreaming works well for the first assistant response, but it never resets displayedWordCount when a new assistant message becomes the lastAssistantMessage. That can cause follow-up answers to appear partially or fully revealed immediately instead of re-streaming from the start.

A light refactor is to key the hook off the assistant message id (or index) and reset when it changes:

-function useWordStreaming(content: string) {
+function useWordStreaming(content: string, messageKey: string | number | null) {
   const [displayedWordCount, setDisplayedWordCount] = useState(0);
@@
-  const hasContent = Boolean(content);
-  useEffect(() => {
-    if (!hasContent) {
+  useEffect(() => {
+    if (!content) {
       setDisplayedWordCount(0);
       return;
     }
@@
-  }, [hasContent]);
+  }, [content, messageKey]);

And when calling it:

-const { displayedWordCount, targetWordCount, getDisplayContent, isRevealing } = useWordStreaming(lastAssistantContent);
+const { displayedWordCount, targetWordCount, getDisplayContent, isRevealing } =
+  useWordStreaming(lastAssistantContent, lastAssistantMessage?.id ?? null);

This keeps the nice streaming effect per assistant turn.

Also applies to: 366-405, 490-499


341-369: Clarify useChat session identity and avoid findLast if older browsers are targeted

Two small, optional polish items here:

  1. useChat is currently called without an explicit id, so it uses the default session key. If there are (or might be) other useChat instances elsewhere, consider setting a stable id (e.g. id: "cmdk-ai-chat") to avoid state collisions between surfaces.

  2. messages.slice(1).findLast(...) is concise, but Array.prototype.findLast is relatively new. If you support older browsers without a polyfill, you may want to replace this with a manual reverse scan to avoid relying on that built-in.

As part of verification, please confirm your browser support matrix (and whether a polyfill for findLast is present) and check the ai/react docs for recommended id usage with multiple chat instances.

Also applies to: 456-473

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d80018a and 379ca36.

📒 Files selected for processing (1)
  • apps/dashboard/src/components/commands/ask-ai.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use runAsynchronously or runAsynchronouslyWithAlert instead for asynchronous error handling
When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none)
Use ES6 maps instead of records wherever possible

Files:

  • apps/dashboard/src/components/commands/ask-ai.tsx
apps/{backend,dashboard}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer usePathname instead of await params

Files:

  • apps/dashboard/src/components/commands/ask-ai.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/components/commands/ask-ai.tsx (5)
packages/stack-ui/src/components/copy-button.tsx (1)
  • CopyButton (36-36)
packages/stack-shared/src/utils/promises.tsx (2)
  • runAsynchronously (343-366)
  • wait (260-268)
docs/src/components/icons.tsx (3)
  • Check (163-165)
  • Copy (185-205)
  • ExternalLink (98-108)
apps/dashboard/src/components/code-block.tsx (1)
  • CodeBlock (33-87)
apps/dashboard/src/components/cmdk-commands.tsx (1)
  • CmdKPreviewProps (15-30)
⏰ 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). (12)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: build (22.x)
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: docker
  • GitHub Check: all-good
  • GitHub Check: setup-tests
🔇 Additional comments (1)
apps/dashboard/src/components/commands/ask-ai.tsx (1)

406-443: Follow-up handling, keyboard controls, and error UI look good

The follow-up flow (handleFollowUp + keyboard handler + send button) correctly uses runAsynchronously for async work, debounces on aiLoading, and provides intuitive keyboard shortcuts and scroll control. The inline error display avoids toasts and matches the “blocking alerts vs toast” guideline for this context. As per coding guidelines, the async handling pattern and transitions look appropriate.

Also applies to: 510-543

Copy link
Copy Markdown
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 (6)
apps/dashboard/src/components/cmdk-search.tsx (6)

258-258: Remove unused variable.

The hasResults variable is computed but never used in the component body.

Apply this diff:

  const itemRefs = useRef<Map<number, HTMLButtonElement>>(new Map());
- const hasResults = commands.length > 0;

727-728: Remove unused variables.

Both hasResults and hasQuery are computed but never used in the component.

Apply this diff:

  if (!open) return null;

- const hasResults = filteredCommands.length > 0;
- const hasQuery = query.trim().length > 0;

  return (

829-829: Remove unused variable.

The isActive variable is computed but never used in the rendering logic.

Apply this diff:

                  const columnDepth = depth + 1;
-                 const isActive = columnDepth === activeDepth;
                  const columnSelectedIndex = selectedIndices[columnDepth] ?? 0;

85-95: Clean up timeout to avoid setState after unmount.

The cleanup function clears the interval but not the inner setTimeout, allowing it to fire after unmount and trigger a setState warning.

Apply this diff to capture and clear the timeout:

  useEffect(() => {
+   let timeoutId: ReturnType<typeof setTimeout> | undefined;
    const interval = setInterval(() => {
      setIsVisible(false);
-     setTimeout(() => {
+     timeoutId = setTimeout(() => {
        setCurrentIndex((prev) => (prev + 1) % EXAMPLE_QUERIES.length);
        setIsVisible(true);
      }, 300);
    }, 7000);

-   return () => clearInterval(interval);
+   return () => {
+     clearInterval(interval);
+     if (timeoutId) clearTimeout(timeoutId);
+   };
  }, []);

772-772: Fix invalid Tailwind class: use flex-1 instead of grow-1.

grow-1 is not a standard Tailwind utility class, so this container won't flex to fill the remaining vertical space.

Apply this diff:

-             <div className="border-t border-foreground/[0.06] grow-1 h-full flex flex-col">
+             <div className="border-t border-foreground/[0.06] flex-1 h-full flex flex-col">

802-802: Fix invalid Tailwind class: use flex-1 instead of grow-1.

grow-1 is not a standard Tailwind utility class, so this container won't flex correctly.

Apply this diff:

                className={cn(
-                 "border-t border-foreground/[0.06] grow-1 h-full flex overflow-x-auto",
+                 "border-t border-foreground/[0.06] flex-1 h-full flex overflow-x-auto",
                  "md:flex-row flex-col"
                )}
🧹 Nitpick comments (5)
apps/dashboard/src/components/cmdk-search.tsx (5)

315-315: Consider accessibility: tabIndex={-1} removes keyboard navigation.

While arrow-key navigation is implemented, removing these buttons from the tab order may hinder users who rely on standard keyboard navigation patterns.

Consider adding ARIA attributes and potentially allowing tab navigation to reach the command list, or document why this pattern is acceptable for a command palette:

tabIndex={-1}
role="option"
aria-selected={isSelected}

570-572: Fix indentation.

Line 571 is not indented properly.

Apply this diff:

      if (command.onAction.type === "navigate") {
-     setOpen(false);
+       setOpen(false);
        router.push(command.onAction.href);

688-690: Fix indentation.

Line 690 is not indented properly.

Apply this diff:

        // Navigate back from nested preview
        if (activeDepth > 0) {
-       e.preventDefault();
+         e.preventDefault();
        const blurHandlerIndex = activeDepth - 1;

963-974: Consider performance impact of global mousemove listener.

The trigger button tracks mouse position globally to create a cursor-following effect. This adds a mousemove listener to the document that fires on every mouse movement, even when not hovering the button.

Consider adding the mousemove listener only when hovering the button:

useEffect(() => {
  let isHovering = false;
  const parent = mouseCursorParentRef.current;
  
  const handleMouseMove = (e: MouseEvent) => {
    if (isHovering && mouseCursorRef.current && parent) {
      const rect = parent.getBoundingClientRect();
      mouseCursorRef.current.style.left = `${e.clientX - rect.left}px`;
      mouseCursorRef.current.style.top = `${e.clientY - rect.top}px`;
      mouseCursorRef.current.style.display = "block";
    }
  };
  
  const handleMouseEnter = () => {
    isHovering = true;
    document.addEventListener("mousemove", handleMouseMove);
  };
  
  const handleMouseLeave = () => {
    isHovering = false;
    document.removeEventListener("mousemove", handleMouseMove);
  };
  
  parent?.addEventListener("mouseenter", handleMouseEnter);
  parent?.addEventListener("mouseleave", handleMouseLeave);
  
  return () => {
    document.removeEventListener("mousemove", handleMouseMove);
    parent?.removeEventListener("mouseenter", handleMouseEnter);
    parent?.removeEventListener("mouseleave", handleMouseLeave);
  };
}, []);

386-956: Consider accessibility enhancements for the modal.

The command palette lacks ARIA attributes that would improve screen reader support, such as role="dialog", aria-modal="true", aria-label for the search input, and role="listbox"/role="option" for the results list.

Consider adding ARIA attributes:

// On the spotlight container (line 740-744)
<div
  role="dialog"
  aria-modal="true"
  aria-label="Command palette"
  className="fixed inset-0 flex items-center justify-center z-50 px-4 pointer-events-none"
  // ...
>

// On the search input (line 755-767)
<input
  ref={inputRef}
  type="text"
  role="combobox"
  aria-expanded={hasResults}
  aria-controls="cmdk-results"
  aria-label="Search commands or ask AI"
  // ...
/>

// On the results container
<div role="listbox" id="cmdk-results">
  {/* results */}
</div>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 379ca36 and a7609f8.

📒 Files selected for processing (2)
  • apps/dashboard/src/app/layout.tsx (1 hunks)
  • apps/dashboard/src/components/cmdk-search.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/src/app/layout.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use runAsynchronously or runAsynchronouslyWithAlert instead for asynchronous error handling
When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none)
Use ES6 maps instead of records wherever possible

Files:

  • apps/dashboard/src/components/cmdk-search.tsx
apps/{backend,dashboard}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer usePathname instead of await params

Files:

  • apps/dashboard/src/components/cmdk-search.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/components/cmdk-search.tsx (3)
apps/dashboard/src/components/cmdk-commands.tsx (2)
  • CmdKCommand (271-291)
  • useCmdKCommands (343-497)
apps/dashboard/src/components/router.tsx (1)
  • useRouter (15-33)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
⏰ 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). (2)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (1)
apps/dashboard/src/components/cmdk-search.tsx (1)

921-930: Animation implementation appears correct.

The spotlight-slide-in animation uses translateY for vertical sliding, which is appropriate for the modal's flexbox-centered positioning. The past review comment mentioning translateX(-50%) appears to be outdated.

Comment thread apps/dashboard/src/components/cmdk-search.tsx Outdated
Comment thread apps/dashboard/src/app/api/ai-search/route.ts
@N2D4 N2D4 enabled auto-merge (squash) December 3, 2025 23:15
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
apps/dashboard/src/app/api/ai-search/route.ts (2)

21-30: Add authentication and development-only guard.

This endpoint lacks authentication and can be publicly accessed if GROQ_API_KEY is configured, potentially allowing abuse and cost incurrence. Additionally, the UI components using this route are conditionally rendered only in development mode, but the endpoint itself has no such restriction.


32-38: Handle message content type safely.

The Message type from the ai SDK allows content to be either a string or an array of content parts. Line 37 assumes it's always a string, which could cause issues with multi-modal messages.

apps/dashboard/src/components/cmdk-search.tsx (1)

759-759: Fix invalid Tailwind class grow-1.

grow-1 is not a standard Tailwind utility class. Use flex-1 instead to properly allow these containers to flex and fill available space.

Apply this diff:

-              <div className="border-t border-foreground/[0.06] grow-1 h-full flex flex-col">
+              <div className="border-t border-foreground/[0.06] flex-1 h-full flex flex-col">

And:

                 className={cn(
-                  "border-t border-foreground/[0.06] grow-1 h-full flex overflow-x-auto",
+                  "border-t border-foreground/[0.06] flex-1 h-full flex overflow-x-auto",
                   "md:flex-row flex-col"
                 )}

Also applies to: 789-789

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7609f8 and 8640d15.

📒 Files selected for processing (4)
  • AGENTS.md (1 hunks)
  • apps/dashboard/src/app/api/ai-search/route.ts (1 hunks)
  • apps/dashboard/src/app/development-port-display.tsx (1 hunks)
  • apps/dashboard/src/components/cmdk-search.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/src/app/development-port-display.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use runAsynchronously or runAsynchronouslyWithAlert instead for asynchronous error handling
When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none)
Use ES6 maps instead of records wherever possible

Files:

  • apps/dashboard/src/app/api/ai-search/route.ts
  • apps/dashboard/src/components/cmdk-search.tsx
apps/{backend,dashboard}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer usePathname instead of await params

Files:

  • apps/dashboard/src/app/api/ai-search/route.ts
  • apps/dashboard/src/components/cmdk-search.tsx
🧠 Learnings (10)
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to packages/stack-shared/src/config/schema.ts : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to **/*.{ts,tsx} : NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use `runAsynchronously` or `runAsynchronouslyWithAlert` instead for asynchronous error handling

Applied to files:

  • AGENTS.md
  • apps/dashboard/src/app/api/ai-search/route.ts
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to apps/{backend,dashboard}/src/**/*.{ts,tsx} : NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer `usePathname` instead of `await params`

Applied to files:

  • AGENTS.md
  • apps/dashboard/src/app/api/ai-search/route.ts
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to **/*.{ts,tsx} : Use ES6 maps instead of records wherever possible

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to {packages/stack/**,packages/js/**} : NEVER UPDATE packages/stack OR packages/js. Instead, update packages/template, as the others are simply copies of that package

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to **/*.{ts,tsx} : When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., `transition-colors hover:transition-none`)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to **/*.{ts,tsx} : Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead

Applied to files:

  • AGENTS.md
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to **/*.{ts,tsx} : For blocking alerts and errors, never use `toast`, as they are easily missed by the user. Instead, use alerts

Applied to files:

  • AGENTS.md
  • apps/dashboard/src/app/api/ai-search/route.ts
📚 Learning: 2025-11-28T21:21:39.142Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:21:39.142Z
Learning: Applies to apps/backend/src/app/api/**/*.{ts,tsx} : The project uses a custom route handler system in the backend for consistent API responses

Applied to files:

  • apps/dashboard/src/app/api/ai-search/route.ts
🧬 Code graph analysis (2)
apps/dashboard/src/app/api/ai-search/route.ts (2)
packages/stack-shared/src/sessions.ts (1)
  • payload (34-36)
packages/stack-shared/src/utils/strings.tsx (1)
  • deindent (235-238)
apps/dashboard/src/components/cmdk-search.tsx (4)
docs/src/components/icons.tsx (1)
  • Search (93-96)
apps/dashboard/src/components/cmdk-commands.tsx (2)
  • CmdKCommand (271-291)
  • useCmdKCommands (343-497)
apps/dashboard/src/components/router.tsx (1)
  • useRouter (15-33)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
⏰ 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). (2)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (1)
AGENTS.md (1)

85-85: Excellent addition to document the environment variable naming convention.

This new guideline clearly establishes the STACK_ prefix convention for private variables and NEXT_PUBLIC_STACK_ for public ones, with solid reasoning tied to Turborepo's change detection. The guidance is well-positioned in the "Important Notes" section and complements the existing port prefix examples already referenced throughout the file (lines 37, 40–41).

Comment thread apps/dashboard/src/components/cmdk-search.tsx
Comment thread apps/dashboard/src/components/cmdk-search.tsx Outdated
Comment thread apps/dashboard/src/components/cmdk-search.tsx
N2D4 and others added 2 commits December 3, 2025 16:36
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Copy link
Copy Markdown
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)
apps/dashboard/src/components/cmdk-search.tsx (3)

517-523: Remove unused registerNestedBlur plumbing (dead code)

registerNestedBlur is never passed to previews or called, so nestedBlurHandlers are only ever reset to [] and ArrowLeft’s blur-handling branch never actually finds a handler. This also matches the prior “unused variable registerNestedBlur” warning. Given that behavior today doesn’t depend on per-depth blur hooks, the simplest fix is to drop the unused callback.

-  // Register onBlur handler for a depth level
-  const registerNestedBlur = useCallback((onBlur: () => void, depth: number) => {
-    setNestedBlurHandlers((prev) => {
-      const next = [...prev];
-      next[depth] = onBlur;
-      return next;
-    });
-  }, []);
-

(You can keep nestedBlurHandlers and its resets as-is; they’re still harmless and used in the ArrowLeft logic. If you later need per-depth blur, you can reintroduce a wired-up registration function.)

Also applies to: 687-697


818-823: Remove unused isActive in nested columns

isActive is computed for each nested column but never used, which aligns with the earlier “unused variable isActive” comment. It’s safe to drop and will quiet linters.

-                {nestedColumns.map((commands, depth) => {
-                  const columnDepth = depth + 1;
-                  const isActive = columnDepth === activeDepth;
-                  const columnSelectedIndex = selectedIndices[columnDepth] ?? 0;
+                {nestedColumns.map((commands, depth) => {
+                  const columnDepth = depth + 1;
+                  const columnSelectedIndex = selectedIndices[columnDepth] ?? 0;

764-797: Fix invalid Tailwind flex class: use flex-1 instead of grow-1

grow-1 isn’t a standard Tailwind utility, so these containers won’t actually flex to fill the remaining vertical space. Swapping to flex-1 matches Tailwind’s flex-grow shorthand and aligns with earlier automated review feedback.

-              <div className="border-t border-foreground/[0.06] grow-1 h-full flex flex-col">
+              <div className="border-t border-foreground/[0.06] flex-1 h-full flex flex-col">
@@
-                className={cn(
-                  "border-t border-foreground/[0.06] grow-1 h-full flex overflow-x-auto",
+                className={cn(
+                  "border-t border-foreground/[0.06] flex-1 h-full flex overflow-x-auto",
                   "md:flex-row flex-col"
                 )}

Please double-check against your Tailwind setup, but unless you’ve added a custom grow-1 alias, flex-1 is the correct utility.

🧹 Nitpick comments (3)
apps/dashboard/src/components/cmdk-search.tsx (3)

74-112: Simplify cycling timer and hide inactive examples from interaction

The cycling behavior works, but the effect recreates the interval on every tick and invisible examples remain focusable/clickable (they’re just opacity-0). You can simplify the timer and tighten accessibility by using a stable interval plus functional updates, and fully disabling inactive buttons.

   const [currentIndex, setCurrentIndex] = useState(() =>
     Math.floor(Math.random() * EXAMPLE_QUERIES.length)
   );
 
   useEffect(() => {
-    const interval = setInterval(() => {
-      setCurrentIndex((currentIndex + 1) % EXAMPLE_QUERIES.length);
-    }, 3000);
-    return () => clearInterval(interval);
-  }, [currentIndex]);
+    const interval = setInterval(() => {
+      setCurrentIndex((prev) => (prev + 1) % EXAMPLE_QUERIES.length);
+    }, 3000);
+    return () => clearInterval(interval);
+  }, []);
 
   return <>
     {EXAMPLE_QUERIES.map((example, index) => {
       return <button
         key={index}
         type="button"
         onClick={() => onSelectQuery?.(example.query)}
+        aria-hidden={index !== currentIndex}
+        tabIndex={index === currentIndex ? 0 : -1}
         className={cn(
           "flex flex-col items-center gap-1 cursor-pointer group",
-          index === currentIndex ? "opacity-100" : "opacity-0",
+          index === currentIndex ? "opacity-100" : "opacity-0 pointer-events-none",
           "transition-opacity duration-300"
         )}
       >

443-458: Make keyword filtering case-insensitive

cmd.keywords are checked with includes(searchLower) but without lowercasing the keyword itself, so mixed/upper-case keywords won’t match lowercase queries. Normalizing both sides will give more predictable search behavior.

-      if (cmd.description.toLowerCase().includes(searchLower)) return true;
-      if (cmd.keywords?.some((k) => k.includes(searchLower))) return true;
+      if (cmd.description.toLowerCase().includes(searchLower)) return true;
+      if (cmd.keywords?.some((k) => k.toLowerCase().includes(searchLower))) return true;

563-595: Avoid wrapping actions in Promise.resolve before runAsynchronously

runAsynchronously already accepts a function or a promise, so you don’t need to eagerly call command.onAction.action() inside Promise.resolve. Passing the function directly keeps intent clearer and avoids an unnecessary wrap.

-      } else if (command.onAction.type === "action") {
-        runAsynchronously(Promise.resolve(command.onAction.action()));
+      } else if (command.onAction.type === "action") {
+        runAsynchronously(command.onAction.action);
         // Don't close for highlighted commands (like AI)
         if (!command.highlightColor) {
           setOpen(false);
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f778928 and e0e5d36.

📒 Files selected for processing (2)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (6 hunks)
  • apps/dashboard/src/components/cmdk-search.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition; apply transitions after the action instead
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use runAsynchronously or runAsynchronouslyWithAlert instead for asynchronous error handling
When creating hover transitions, avoid hover-enter transitions and just use hover-exit transitions (e.g., transition-colors hover:transition-none)
Use ES6 maps instead of records wherever possible

Files:

  • apps/dashboard/src/components/cmdk-search.tsx
apps/{backend,dashboard}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component and prefer usePathname instead of await params

Files:

  • apps/dashboard/src/components/cmdk-search.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/components/cmdk-search.tsx (3)
docs/src/components/icons.tsx (1)
  • Search (93-96)
apps/dashboard/src/components/router.tsx (1)
  • useRouter (15-33)
packages/stack-shared/src/utils/promises.tsx (1)
  • runAsynchronously (343-366)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build (22.x)
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: docker
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: setup-tests
  • GitHub Check: all-good
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (4)
apps/dashboard/src/components/cmdk-search.tsx (4)

18-72: Example and feature metadata looks consistent

Example queries and feature highlights are coherent and well-structured; icons, copy, and colors line up with the intended UX. No changes needed here.


114-225: Empty-state experience is well thought out

The placeholder layout (keybinding hint, feature highlights, and “try something like …” section) is clear and inviting, and the example-query click behavior is straightforward. No issues from my side here.


227-378: Results list implementation looks solid

Item ref tracking, smooth scrollIntoView, selection styling (including parent vs active column), and keyboard hint rendering are all cohesive and follow the hover/transition guidelines. I don’t see any functional problems in this component.


950-1021: CmdKTrigger wiring and hover behavior look good

The trigger correctly dispatches the custom spotlight-toggle event, and the hover visuals respect the “snappy, hover-exit transitions” guideline (transition-* with group-hover:transition-none). No functional issues here.

Comment thread apps/dashboard/src/components/commands/ask-ai.tsx
@N2D4 N2D4 merged commit 0c0a382 into dev Dec 4, 2025
22 checks passed
@N2D4 N2D4 deleted the cmd-k branch December 4, 2025 18:03
@coderabbitai coderabbitai bot mentioned this pull request Jan 6, 2026
@coderabbitai coderabbitai bot mentioned this pull request Feb 5, 2026
@coderabbitai coderabbitai bot mentioned this pull request Feb 17, 2026
@coderabbitai coderabbitai bot mentioned this pull request Feb 24, 2026
This was referenced Mar 28, 2026
@coderabbitai coderabbitai bot mentioned this pull request Apr 8, 2026
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.

3 participants