Conversation
|
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. WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
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
setTimeoutinCopyButtonisn’t cleared if the component unmounts before it fires, which can causesetCopiedto 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 opportunityThe 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 localCopyButton. 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
CopyButtonfrompackages/stack-ui/src/components/copy-button.tsxand 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
useWordStreamingworks well for the first assistant response, but it never resetsdisplayedWordCountwhen a new assistant message becomes thelastAssistantMessage. 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: ClarifyuseChatsession identity and avoidfindLastif older browsers are targetedTwo small, optional polish items here:
useChatis currently called without an explicitid, so it uses the default session key. If there are (or might be) otheruseChatinstances elsewhere, consider setting a stable id (e.g.id: "cmdk-ai-chat") to avoid state collisions between surfaces.
messages.slice(1).findLast(...)is concise, butArray.prototype.findLastis 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
findLastis present) and check theai/reactdocs for recommendedidusage with multiple chat instances.Also applies to: 456-473
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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 usetoast, 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). UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead 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
usePathnameinstead ofawait 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 goodThe follow-up flow (
handleFollowUp+ keyboard handler + send button) correctly usesrunAsynchronouslyfor async work, debounces onaiLoading, 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
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (6)
apps/dashboard/src/components/cmdk-search.tsx (6)
258-258: Remove unused variable.The
hasResultsvariable 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
hasResultsandhasQueryare 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
isActivevariable 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: useflex-1instead ofgrow-1.
grow-1is 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: useflex-1instead ofgrow-1.
grow-1is 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-labelfor the search input, androle="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
📒 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 usetoast, 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). UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead 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
usePathnameinstead ofawait 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-inanimation usestranslateYfor vertical sliding, which is appropriate for the modal's flexbox-centered positioning. The past review comment mentioningtranslateX(-50%)appears to be outdated.
There was a problem hiding this comment.
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_KEYis 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
Messagetype from theaiSDK allowscontentto 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 classgrow-1.
grow-1is not a standard Tailwind utility class. Useflex-1instead 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
📒 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 usetoast, 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). UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead 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.tsapps/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
usePathnameinstead ofawait params
Files:
apps/dashboard/src/app/api/ai-search/route.tsapps/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.mdapps/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.mdapps/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.mdapps/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 andNEXT_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).
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
apps/dashboard/src/components/cmdk-search.tsx (3)
517-523: Remove unusedregisterNestedBlurplumbing (dead code)
registerNestedBluris never passed to previews or called, sonestedBlurHandlersare only ever reset to[]and ArrowLeft’s blur-handling branch never actually finds a handler. This also matches the prior “unused variableregisterNestedBlur” 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
nestedBlurHandlersand 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 unusedisActivein nested columns
isActiveis computed for each nested column but never used, which aligns with the earlier “unused variableisActive” 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: useflex-1instead ofgrow-1
grow-1isn’t a standard Tailwind utility, so these containers won’t actually flex to fill the remaining vertical space. Swapping toflex-1matches 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-1alias,flex-1is the correct utility.
🧹 Nitpick comments (3)
apps/dashboard/src/components/cmdk-search.tsx (3)
74-112: Simplify cycling timer and hide inactive examples from interactionThe 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.keywordsare checked withincludes(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 inPromise.resolvebeforerunAsynchronously
runAsynchronouslyalready accepts a function or a promise, so you don’t need to eagerly callcommand.onAction.action()insidePromise.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
📒 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 usetoast, 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). UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead 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
usePathnameinstead ofawait 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 consistentExample 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 outThe 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 solidItem 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 goodThe trigger correctly dispatches the custom
spotlight-toggleevent, and the hover visuals respect the “snappy, hover-exit transitions” guideline (transition-*withgroup-hover:transition-none). No functional issues here.
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.
CmdKTrigger,CmdKSearch) with nested navigation, previews, and keyboard controls.sidebar-layout.tsx; enable/disable per environment.POST /api/ai-search(apps/dashboard/src/app/api/ai-search/route.ts) streaming mock responses with a strict system prompt.components/commands/ask-ai.tsx) rendered within Spotlight.cmdk-commands.tsx,lib/apps-utils.ts).components/stack-companion.tsx).development-port-display.tsx) and wire intolayout.tsx.DESIGN_GUIDE.mdwith comprehensiveDESIGN-GUIDE.mdcovering colors, surfaces, interactions, and patterns.AGENTS.mdwith env var prefix rule.PacificaCard(grow-1).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
Documentation
Bug Fixes
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.