Skip to content

feat: migrate command to Base UI#748

Merged
rohanchkrabrty merged 9 commits intomainfrom
worktree-command
Apr 23, 2026
Merged

feat: migrate command to Base UI#748
rohanchkrabrty merged 9 commits intomainfrom
worktree-command

Conversation

@rohanchkrabrty
Copy link
Copy Markdown
Contributor

Summary

  • Migrated Command component from cmdk to the Base UI Autocomplete primitive with built-in auto-search filtering
  • Split monolithic command.tsx into dedicated subcomponent files (command-root, command-input, command-list, command-item, command-dialog, command-empty, command-misc)
  • Added new subcomponents: Panel, Footer, Label, Collection, and a composable Dialog API (Dialog.Trigger, Dialog.Content, Dialog.Close, Dialog.Title, Dialog.Description)
  • Expanded tests, documentation (API reference, usage examples), demos, and playground examples for the new API surface
  • Updated CSS module with styles for all new subcomponents and refined existing styles

Split the monolithic Command component into separate files (command-root,
command-input, command-list, command-item, command-dialog, command-empty,
command-misc) and migrated from cmdk to the Base UI Autocomplete primitive.

Added new subcomponents: Panel, Footer, Label, Collection, and a
composable Dialog API (Dialog.Trigger, Dialog.Content, Dialog.Close,
Dialog.Title, Dialog.Description). Updated tests, docs, demos, and
playground examples to cover the new API surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 21, 2026

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

Project Deployment Actions Updated (UTC)
apsara Ready Ready Preview, Comment Apr 23, 2026 5:56am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Warning

Rate limit exceeded

@rohanchkrabrty has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 27 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 45 minutes and 27 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 04e1fd33-55b0-4552-9f97-e00ed412dc04

📥 Commits

Reviewing files that changed from the base of the PR and between baeb4ad and 6564710.

📒 Files selected for processing (1)
  • docs/V1-migration.md
📝 Walkthrough

Walkthrough

The PR modularizes the Command component into separate modules (root, dialog, content, input, item, misc), adds a Command dialog wrapper and dialog content with overlay/blur options, introduces a Command context for shared input state, implements new subcomponents (Command.Input, Command.Item, Command.Empty, Command.Group/Label/Separator/Shortcut, Command.Content, Command.DialogTrigger, Command.DialogContent), updates CSS for dialog and list UX, expands demos/docs/tests, and changes the exported Command namespace (Command.List → Command.Content and new subcomponents attached).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Document as Document (keydown)
    participant CommandDialog as Command.Dialog
    participant CommandRoot as Command.Root
    participant CommandInput as Command.Input
    participant CommandContent as Command.Content
    participant CommandItem as Command.Item

    User->>Document: Press Ctrl/Cmd+K
    Document->>CommandDialog: toggle open state
    CommandDialog->>CommandRoot: render Command (in portal)
    User->>CommandInput: Type query
    CommandInput->>CommandRoot: onValueChange / update context
    CommandRoot->>CommandContent: provide filtered items
    CommandContent->>CommandItem: render matching items
    User->>CommandItem: Click or press Enter
    CommandItem->>CommandDialog: invoke onClick -> setOpen(false)
    CommandDialog-->>User: Close dialog and restore focus
Loading

Suggested reviewers

  • rohilsurana
  • rsbh
  • paanSinghCoder
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: migrate command to Base UI' clearly and concisely summarizes the main change: migrating the Command component from cmdk to Base UI Autocomplete.
Description check ✅ Passed The description is directly related to the changeset, detailing the migration from cmdk to Base UI, file splitting, new subcomponents, tests/docs expansion, and CSS updates—all confirmed by the raw summary.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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: 8

🧹 Nitpick comments (3)
packages/raystack/components/command/command-empty.tsx (1)

7-7: Nit: empty interface extension.

interface CommandEmptyProps extends ComponentProps<'div'> {} is equivalent to a type alias and commonly flagged by @typescript-eslint/no-empty-object-type. Prefer:

-export interface CommandEmptyProps extends ComponentProps<'div'> {}
+export type CommandEmptyProps = ComponentProps<'div'>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command-empty.tsx` at line 7, The empty
interface CommandEmptyProps extends ComponentProps<'div'> should be replaced
with a type alias or removed; update CommandEmptyProps to "type
CommandEmptyProps = ComponentProps<'div'>" (or remove the alias and use
ComponentProps<'div'> inline where used) to avoid the empty-interface lint
warning and keep types minimal.
packages/raystack/components/command/command-root.tsx (1)

32-40: Make CommandRootProps generic to preserve item type information.

AutocompletePrimitive.Root.Props<any> combined with items?: readonly any[] discards the item-type generic, causing downstream callbacks like filter or itemToStringValue to receive any for each item. Preserve type safety by making CommandRootProps generic over the item type with a sensible default:

♻️ Proposed refactor
-export interface CommandRootProps
-  extends Omit<AutocompletePrimitive.Root.Props<any>, 'items'> {
-  /**
-   * Items to be displayed and filtered by Base UI internally.
-   * When provided, the auto-search fallback is disabled and filtering is
-   * delegated to Base UI's built-in logic (or the user's custom `filter`).
-   */
-  items?: readonly any[];
-}
+export interface CommandRootProps<Item = string>
+  extends Omit<AutocompletePrimitive.Root.Props<Item>, 'items'> {
+  /**
+   * Items to be displayed and filtered by Base UI internally.
+   * When provided, the auto-search fallback is disabled and filtering is
+   * delegated to Base UI's built-in logic (or the user's custom `filter`).
+   */
+  items?: readonly Item[];
+}

This change is backward-compatible (existing <Command items={[...]}> usage defaults to Item = string) and enables type-safe callbacks when users provide custom filter or selection logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command-root.tsx` around lines 32 - 40,
CommandRootProps currently erases item generics by extending
AutocompletePrimitive.Root.Props<any> and using items?: readonly any[]; update
CommandRootProps to be generic (e.g., CommandRootProps<Item = string>) so it
extends AutocompletePrimitive.Root.Props<Item> and items?: readonly Item[];
ensuring downstream callbacks like filter and itemToStringValue receive the
concrete Item type; update any places that reference CommandRootProps (and the
items prop) to use the new generic form or allow the default.
packages/raystack/components/command/__tests__/command.test.tsx (1)

156-197: Assert separator visibility in the separator test.

The test name covers separators, but it only asserts labels are hidden. Add a direct separator assertion so Command.Separator’s search-time behavior is covered.

Proposed test tightening
-            <Command.Separator />
+            <Command.Separator data-testid='command-separator' />
...
       await waitFor(() => {
         expect(screen.queryByText('Suggestions')).not.toBeInTheDocument();
         expect(screen.queryByText('Settings')).not.toBeInTheDocument();
+        expect(screen.queryByTestId('command-separator')).not.toBeInTheDocument();
         expect(screen.getByText('Calendar')).toBeInTheDocument();
         expect(screen.getByText('Calculator')).toBeInTheDocument();
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/__tests__/command.test.tsx` around lines
156 - 197, The test currently checks group labels but not the separator; update
the 'hides group labels and separators while searching' spec to also assert
Command.Separator visibility by querying for the separator element (e.g.
role="separator" or the separator's text/selector used in the component) — after
initial render assert the separator exists (to cover the non-search state) and
after typing 'cal' assert the separator is not present (use
screen.getByRole('separator') or screen.queryByRole('separator') accordingly) so
Command.Separator's search-time behavior is directly tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/www/src/components/playground/command-examples.tsx`:
- Around line 67-90: Add an accessible name for the dialog by adding a visible
or visually-hidden title component inside the Command.Dialog so screen readers
can identify the modal; specifically update the JSX around
Command.Dialog.Content to include a Command.Dialog.Title (or an aria-label on
Command.Dialog.Content) with descriptive text (e.g., "Command Menu") and ensure
it remains associated with the dialog opened by
Command.Dialog/Command.Dialog.Trigger and referenced by Command.Input if needed.

In `@apps/www/src/content/docs/components/command/demo.ts`:
- Around line 175-193: The dialog demo is missing an accessibility label; update
the Command.Dialog usage so the modal has an accessible name by either adding an
aria-label to <Command.Dialog> (e.g., aria-label="Command Menu") or by adding a
<Command.Dialog.Title> inside <Command.Dialog.Content> with a visible title;
modify the Command.Dialog component instance shown (the one wrapping
Command.Dialog.Trigger and Command.Dialog.Content) to include one of these
options so the dialog is labeled for assistive tech.

In `@apps/www/src/content/docs/components/command/index.mdx`:
- Around line 101-104: The current "Group" description incorrectly states "The
group and its children are hidden automatically while the user is searching";
update the copy in the Group section (the "Group" heading/paragraph in
index.mdx) to reflect actual behavior: when searching the group is unwrapped so
matching child items remain visible while the grouping chrome (headers/dividers)
is hidden; replace the sentence to explicitly say the group is collapsed
visually but matching items are shown with grouping chrome removed.

In `@apps/www/src/content/docs/components/command/props.ts`:
- Around line 16-20: The prop documentation and type for onValueChange only list
the first parameter but the implementation calls onValueChange(value,
eventDetails) and tests consume a second "details" argument; update the prop
signature for onValueChange to accept a second optional parameter (e.g. details
or eventDetails) and amend the JSDoc comment to describe that second argument
and its shape/usage (referencing the onValueChange prop and the tests that
expect details).
- Around line 163-172: The CommandDialogContentProps interface is missing the
portalProps entry so the generated API docs omit a supported customization;
update the CommandDialogContentProps (the props interface used by
CommandDialogContent) to add an optional portalProps property matching the type
used by the component implementation (use the same PortalProps/prop type
exported or imported where CommandDialogContent is defined), and ensure its
JSDoc comment describes its purpose so the API table documents this
customization point.

In `@packages/raystack/components/command/command-input.tsx`:
- Around line 19-26: CommandInput currently defaults autoFocus to true which can
steal focus when used inside persistent UI like Command.Panel; change the
default value of the autoFocus prop in the CommandInput component to false, and
instead enable auto-focus when the input is mounted from inside the dialog
wrapper (for example, set autoFocus to true or call focus programmatically from
Command.Dialog.Content or the dialog mounting logic). Update any related
docs/tests and ensure the prop signature (CommandInputProps) still accepts an
explicit autoFocus override so consumers can opt in when desired.

In `@packages/raystack/components/command/command-root.tsx`:
- Around line 42-99: The CommandRoot component's context inputValue (used by
CommandItem for filtering) can become stale when a parent passes a non-string
providedValue without forwarding the typed input via onValueChange; document
that when using a controlled non-string value callers must call onValueChange
with the input text (or omit items to disable filtering), and update
handleValueChange to only call setInternalValue when providedValue is not a
string (guard on typeof providedValue !== 'string') to avoid redundant state
updates; reference CommandRoot, providedValue, onValueChange, handleValueChange,
setInternalValue, items, and CommandItem in the documentation and comments so
callers and future maintainers see the required contract.

In `@packages/raystack/components/command/command.tsx`:
- Line 1: The public Command entrypoint is missing the 'use client' directive,
causing it to be treated as a server component even though it composes
client-only hook components; add the top-of-file "use client" directive in
packages/raystack/components/command/command.tsx so the exported Command (and
the Object.assign composition of its client subcomponents) are treated as client
components, ensuring all hook-based subcomponents remain inside the client
boundary.

---

Nitpick comments:
In `@packages/raystack/components/command/__tests__/command.test.tsx`:
- Around line 156-197: The test currently checks group labels but not the
separator; update the 'hides group labels and separators while searching' spec
to also assert Command.Separator visibility by querying for the separator
element (e.g. role="separator" or the separator's text/selector used in the
component) — after initial render assert the separator exists (to cover the
non-search state) and after typing 'cal' assert the separator is not present
(use screen.getByRole('separator') or screen.queryByRole('separator')
accordingly) so Command.Separator's search-time behavior is directly tested.

In `@packages/raystack/components/command/command-empty.tsx`:
- Line 7: The empty interface CommandEmptyProps extends ComponentProps<'div'>
should be replaced with a type alias or removed; update CommandEmptyProps to
"type CommandEmptyProps = ComponentProps<'div'>" (or remove the alias and use
ComponentProps<'div'> inline where used) to avoid the empty-interface lint
warning and keep types minimal.

In `@packages/raystack/components/command/command-root.tsx`:
- Around line 32-40: CommandRootProps currently erases item generics by
extending AutocompletePrimitive.Root.Props<any> and using items?: readonly
any[]; update CommandRootProps to be generic (e.g., CommandRootProps<Item =
string>) so it extends AutocompletePrimitive.Root.Props<Item> and items?:
readonly Item[]; ensuring downstream callbacks like filter and itemToStringValue
receive the concrete Item type; update any places that reference
CommandRootProps (and the items prop) to use the new generic form or allow the
default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45cfe37b-3510-4832-a98f-7c0465a2739a

📥 Commits

Reviewing files that changed from the base of the PR and between 628f237 and 5ae10ce.

📒 Files selected for processing (15)
  • apps/www/src/components/demo/demo.tsx
  • apps/www/src/components/playground/command-examples.tsx
  • apps/www/src/content/docs/components/command/demo.ts
  • apps/www/src/content/docs/components/command/index.mdx
  • apps/www/src/content/docs/components/command/props.ts
  • packages/raystack/components/command/__tests__/command.test.tsx
  • packages/raystack/components/command/command-dialog.tsx
  • packages/raystack/components/command/command-empty.tsx
  • packages/raystack/components/command/command-input.tsx
  • packages/raystack/components/command/command-item.tsx
  • packages/raystack/components/command/command-list.tsx
  • packages/raystack/components/command/command-misc.tsx
  • packages/raystack/components/command/command-root.tsx
  • packages/raystack/components/command/command.module.css
  • packages/raystack/components/command/command.tsx

Comment thread apps/www/src/components/playground/command-examples.tsx Outdated
Comment thread apps/www/src/content/docs/components/command/demo.ts Outdated
Comment thread apps/www/src/content/docs/components/command/index.mdx
Comment thread apps/www/src/content/docs/components/command/props.ts
Comment thread apps/www/src/content/docs/components/command/props.ts
Comment thread packages/raystack/components/command/command-input.tsx
Comment on lines +42 to +99
export const CommandRoot = ({
value: providedValue,
defaultValue,
onValueChange,
items,
inline = true,
open = true,
autoHighlight = 'always',
keepHighlight = true,
children,
...props
}: CommandRootProps) => {
const [internalValue, setInternalValue] = useState<string>(
typeof defaultValue === 'string' ? defaultValue : ''
);
const inputContainerRef = useRef<HTMLDivElement>(null);

const computedValue =
typeof providedValue === 'string' ? providedValue : internalValue;

const handleValueChange = useCallback(
(
value: string,
eventDetails: AutocompletePrimitive.Root.ChangeEventDetails
) => {
setInternalValue(value);
onValueChange?.(value, eventDetails);
},
[onValueChange]
);

const contextValue = useMemo(
() => ({
inputValue: computedValue,
hasItems: !!items,
inputContainerRef
}),
[computedValue, items]
);

return (
<CommandContext.Provider value={contextValue}>
<AutocompletePrimitive.Root
inline={inline}
open={open}
autoHighlight={autoHighlight}
keepHighlight={keepHighlight}
value={providedValue}
defaultValue={defaultValue}
onValueChange={handleValueChange}
items={items}
{...props}
>
{children}
</AutocompletePrimitive.Root>
</CommandContext.Provider>
);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Base UI React Autocomplete Root onValueChange inputValue prop

💡 Result:

In Base UI's React Autocomplete component, the Autocomplete.Root handles both the selected value and input value through unified props: value (controlled input value) and defaultValue (uncontrolled), with onValueChange as the callback when the input value changes. There is no separate inputValue prop on Autocomplete.Root. The value prop controls what appears in the input field. The onValueChange handler receives the new value: string as its first argument, along with event details. Example usage for controlled mode: const [inputValue, setInputValue] = useState(''); <Autocomplete.Root value={inputValue} onValueChange={(value, eventDetails) => { console.log('New input value:', value); setInputValue(value); }} items={items} > {/* child components */} </Autocomplete.Root> This differs from MUI Base's useAutocomplete hook, which separates inputValue/onInputChange (text in input) from value/onChange (selected option). Base UI Autocomplete simplifies to a single value prop for the input contents, with separate item selection handled via selectedValue internally (not directly exposed on Root). The component builds on ARIA Combobox patterns, passing inputValue={resolvedInputValue} and onInputValueChange={handleValueChange} internally to lower-level primitives, but exposes value/onValueChange at the Root level.

Citations:


🏁 Script executed:

find . -type f -name "package.json" | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && grep -A 5 "base-ui\|@base-ui" {}'

Repository: raystack/apsara

Length of output: 611


🏁 Script executed:

grep -r "AutocompletePrimitive" --include="*.ts" --include="*.tsx" -n | head -20

Repository: raystack/apsara

Length of output: 2382


🏁 Script executed:

fd -e "tsx" -e "ts" | xargs grep -l "CommandRoot" | head -10

Repository: raystack/apsara

Length of output: 215


🏁 Script executed:

cat -n packages/raystack/components/command/command-root.tsx

Repository: raystack/apsara

Length of output: 3218


🏁 Script executed:

cat -n packages/raystack/components/command/command-item.tsx

Repository: raystack/apsara

Length of output: 1754


🏁 Script executed:

cat -n packages/raystack/components/command/command-input.tsx

Repository: raystack/apsara

Length of output: 1980


Document the value/onValueChange contract to prevent silent filtering failures.

The inputValue in context (used by CommandItem for filtering) is derived from a non-string controlled value via internalValue, which only updates when onValueChange's string argument is provided. If a parent controls value as a non-string object without feeding input text through onValueChange, the context inputValue will be stale/empty, causing CommandItem filtering to silently fail.

While Base UI's Autocomplete.Root does not expose inputValue/onInputValueChange at the root level (these are internal), consider documenting that when using value with non-string items, callers must ensure onValueChange receives the input text value, or disable item filtering by omitting items.

Also note: setInternalValue runs unconditionally even when providedValue is a controlled string — harmless but redundant; guarding with if (typeof providedValue !== 'string') would clarify intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command-root.tsx` around lines 42 - 99,
The CommandRoot component's context inputValue (used by CommandItem for
filtering) can become stale when a parent passes a non-string providedValue
without forwarding the typed input via onValueChange; document that when using a
controlled non-string value callers must call onValueChange with the input text
(or omit items to disable filtering), and update handleValueChange to only call
setInternalValue when providedValue is not a string (guard on typeof
providedValue !== 'string') to avoid redundant state updates; reference
CommandRoot, providedValue, onValueChange, handleValueChange, setInternalValue,
items, and CommandItem in the documentation and comments so callers and future
maintainers see the required contract.

Comment thread packages/raystack/components/command/command.tsx
@rohanchkrabrty rohanchkrabrty changed the title refactor(command): migrate to Base UI and split into subcomponent files feat: migrate command to Base UI Apr 21, 2026
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

Caution

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

⚠️ Outside diff range comments (3)
apps/www/src/app/examples/combobox/page.tsx (1)

2-16: ⚠️ Potential issue | 🟡 Minor

Remove unused Combobox import and address route/content mismatch.

Combobox is imported on line 2 but not used anywhere in the component, which will trigger unused import warnings. Additionally, this page lives at app/examples/combobox/ but renders only a Slider component, making the route misleading compared to other example pages that match their content to their paths.

🔧 Proposed fix
-import { Combobox, Flex, Slider } from '@raystack/apsara';
+import { Flex, Slider } from '@raystack/apsara';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/app/examples/combobox/page.tsx` around lines 2 - 16, Remove the
unused Combobox import and either update the component to render a Combobox
example or move/rename the file to match the Slider content; specifically, edit
the Page component to (a) delete "Combobox" from the import list if you intend
to keep the Slider-only demo, or (b) replace the Slider JSX with a proper
Combobox example using the Combobox component so the file at
app/examples/combobox/ actually showcases Combobox; ensure imports and exported
Page component reflect the chosen content so there are no unused imports or
route/content mismatches.
apps/www/src/components/docs/search.tsx (2)

170-216: ⚠️ Potential issue | 🟡 Minor

Separator condition references the wrong array.

Inside the loop over items, the separator uses defaultItems.length - 1. When isSearching is true, items is searchResults, which can have a different length than defaultItems, so separators may render for the wrong indices (or be missing). Use the iterated collection instead.

Proposed fix
-                {index < defaultItems.length - 1 && <Command.Separator />}
+                {index < items.length - 1 && <Command.Separator />}

In practice Command.Separator hides itself while searching via context, but the logic should still be correct against items.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/components/docs/search.tsx` around lines 170 - 216, The
separator condition is checking the wrong collection: inside the items.map loop
use the iterated array length instead of defaultItems; update the condition that
renders Command.Separator (currently using defaultItems.length - 1) to compare
index against items.length - 1 so separators are based on the current mapped
collection (items which may be searchResults when isSearching is true).

116-116: ⚠️ Potential issue | 🟡 Minor

Remove debug console.log.

This is a debug artifact that will run in production on every search input change.

Proposed fix
-    console.log('grouped', grouped);
-
     return Object.entries(grouped).map(([heading, items]) => ({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/components/docs/search.tsx` at line 116, Remove the debug
console.log call that prints the grouped variable in the search component:
delete the line console.log('grouped', grouped) inside the search logic in
apps/www/src/components/docs/search.tsx (where grouped is computed). Ensure no
other debug console.log statements remain in the same function (e.g., inside the
search handler or render path) so production searches do not log to the console.
♻️ Duplicate comments (4)
apps/www/src/content/docs/components/command/demo.ts (1)

12-12: ⚠️ Potential issue | 🟠 Major

Give every documented command dialog an accessible name.

All generated dialog demos currently render unnamed modal content. Add aria-label here so the live examples and copied snippets are accessible by default.

Proposed fix
-        <Command.DialogContent>
+        <Command.DialogContent aria-label="Command menu">
           <Command>
-        <Command.DialogContent>
+        <Command.DialogContent aria-label="Command menu">
           <Command items={items}>
-        <Command.DialogContent>
+        <Command.DialogContent aria-label="Command menu">
           <Command value={value} onValueChange={setValue}>

Also applies to: 192-192, 225-225

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/demo.ts` at line 12, The
Command.DialogContent usage renders an unnamed modal; add an accessible name by
adding an aria-label (or aria-labelledby) attribute to every
Command.DialogContent instance (e.g., the occurrences around
Command.DialogContent in this file) so the demo dialogs are accessible and
copied snippets include the label; update each Command.DialogContent element to
include a descriptive aria-label string (or reference a heading id via
aria-labelledby) to satisfy accessibility requirements.
apps/www/src/content/docs/components/command/props.ts (1)

16-20: ⚠️ Potential issue | 🟡 Minor

Document callback detail arguments where the implementation exposes them.

These API-table interfaces narrow the callbacks to one argument. If CommandRoot/Base UI forwards event details, users won’t see the full supported callback shape.

Proposed shape if verification confirms details are forwarded
-  onValueChange?: (value: string) => void;
+  onValueChange?: (value: string, details?: unknown) => void;
-  onOpenChange?: (open: boolean) => void;
+  onOpenChange?: (open: boolean, details?: unknown) => void;
#!/bin/bash
# Description: Inspect current Command callback forwarding so props docs match the implementation.

set -euo pipefail

echo "CommandRoot onValueChange forwarding:"
rg -n -C5 'onValueChange\?\.\(|onValueChange\s*=' packages/raystack/components/command --glob '*.tsx' --glob '*.ts'

echo
echo "Command dialog root prop type / forwarding:"
rg -n -C5 'CommandDialog|onOpenChange|DialogPrimitive.Root' packages/raystack/components/command --glob '*.tsx' --glob '*.ts'

Also applies to: 149-150

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/props.ts` around lines 16 - 20,
The prop JSDoc for onValueChange in props.ts only documents a single string
argument but the Command components (e.g., CommandRoot / Command / Base UI) may
forward richer event details; verify how onValueChange is invoked in the Command
implementation (search for symbols onValueChange, CommandRoot, CommandDialog,
DialogPrimitive.Root in the command component files) and then update the
onValueChange type and JSDoc to match the actual forwarded signature (e.g.,
accept an event or detail object instead of just (value: string) => void) so the
API docs reflect the real callback shape; change the onValueChange declaration
and comment in props.ts to the verified parameter shape and add a short example
of the payload if appropriate.
apps/www/src/content/docs/components/command/index.mdx (2)

29-41: ⚠️ Potential issue | 🟠 Major

Label the copy-pastable dialog example.

This dialog content has no accessible name. Add an aria-label or a title so consumers copying the docs don’t ship an unnamed modal.

Proposed fix
-  <Command.DialogContent>
+  <Command.DialogContent aria-label="Command menu">
     <Command>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/index.mdx` around lines 29 - 41,
The example dialog rendered via Command.DialogContent has no accessible name;
add an accessible label by supplying an aria-label or title on the dialog
wrapper (the Command.DialogContent element) or the top-level Command component
so the modal is announced and copy-paste examples don’t produce an unnamed
dialog; update the example to include a descriptive string (e.g.,
aria-label="Command Palette" or title="Command Palette") on
Command.DialogContent (or Command) to resolve the accessibility issue.

80-82: ⚠️ Potential issue | 🟡 Minor

Keep the group-search behavior copy aligned with the implementation.

Command.Group unwraps matching children while search is active, while labels/separators are hidden. The current wording implies matching child items disappear too.

Proposed wording
-Groups related items. The group and its children are hidden automatically while the user is searching.
+Groups related items. While searching, grouping chrome is hidden and matching items are rendered in a flat list.
-Organise items into groups with labels and visual separators. Groups, labels, and separators are hidden automatically while searching.
+Organise items into groups with labels and visual separators. While searching, labels and separators are hidden and matching items are rendered without grouping chrome.

Also applies to: 124-126

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/index.mdx` around lines 80 - 82,
The doc copy for the "Group" section is inaccurate: update the wording around
Command.Group to state that when search is active the group itself is hidden but
matching child items are unwrapped (shown) while group labels/separators are
hidden; locate the "Group" heading in
apps/www/src/content/docs/components/command/index.mdx and replace the current
two-line description with a sentence that clearly says Command.Group hides the
group wrapper and labels/separators during search but preserves and displays
matching child items (i.e., unwraps them).
🧹 Nitpick comments (5)
apps/www/src/components/playground/command-examples.tsx (1)

10-19: Hotkey miss when CapsLock is on.

event.key === 'k' won't match when the user has CapsLock enabled (or holds Shift) — event.key becomes 'K'. The analogous handler in apps/www/src/components/docs/search.tsx uses event.key.toLowerCase() === 'k'. Align for consistency.

Proposed fix
-      if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
+      if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/components/playground/command-examples.tsx` around lines 10 -
19, The keydown handler in the useEffect uses event.key === 'k' which fails when
CapsLock or Shift produce 'K'; update the handler in the useEffect to normalize
the key (e.g., use event.key.toLowerCase()) before comparing to 'k', keep
event.preventDefault() and toggling setOpen(prev => !prev), and ensure you still
add/remove the same handler via document.addEventListener/removeEventListener so
behavior matches the implementation in docs/search.tsx.
packages/raystack/components/command/command-dialog.tsx (2)

37-56: No exports for DialogTitle / DialogDescription / DialogClose — hinders accessible naming.

CommandDialogContent renders a DialogPrimitive.Popup but exposes no sibling Command.DialogTitle / Command.DialogDescription / Command.DialogClose helpers. Consumers (including the playground example in apps/www/src/components/playground/command-examples.tsx) have no in-namespace way to give the dialog an accessible name and must either drop to DialogPrimitive.* directly or add aria-label on the popup. Consider exporting these the way the general Dialog API does (per PR summary).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command-dialog.tsx` around lines 37 -
56, CommandDialogContent renders DialogPrimitive.Popup but the package doesn't
export companion accessible helpers (DialogTitle/DialogDescription/DialogClose),
forcing consumers to use DialogPrimitive directly or aria-label; add and export
namespaced re-exports (e.g., Command.DialogTitle, Command.DialogDescription,
Command.DialogClose) that wrap or re-export DialogPrimitive.Title,
DialogPrimitive.Description, and DialogPrimitive.Close (or simple passthrough
components) so consumers can provide accessible names; update the module's
public exports to include these symbols alongside CommandDialogContent/Command
to match the general Dialog API and ensure examples (like in
command-examples.tsx) can import them from the Command namespace.

13-17: forwardRef style inconsistent with sibling components.

The other Command subcomponents in this PR (e.g., CommandContent, CommandInput, CommandItem) adopt the React 19 ref-as-prop pattern. CommandDialogTrigger is the only one still using forwardRef. Consider aligning for consistency:

Proposed change
-export const CommandDialogTrigger = forwardRef<
-  HTMLButtonElement,
-  DialogPrimitive.Trigger.Props
->((props, ref) => <DialogPrimitive.Trigger ref={ref} {...props} />);
+export const CommandDialogTrigger = ({
+  ref,
+  ...props
+}: DialogPrimitive.Trigger.Props & { ref?: React.Ref<HTMLButtonElement> }) => (
+  <DialogPrimitive.Trigger ref={ref} {...props} />
+);
 CommandDialogTrigger.displayName = 'Command.DialogTrigger';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command-dialog.tsx` around lines 13 -
17, CommandDialogTrigger is the only Command subcomponent still using
forwardRef; change it to the React 19 ref-as-prop pattern to match
CommandContent/CommandInput/CommandItem by accepting a ref prop instead of using
forwardRef. Update the component signature for CommandDialogTrigger to a plain
function/component that takes props including ref?: React.Ref<HTMLButtonElement>
and spreads the rest into <DialogPrimitive.Trigger {...props} ref={ref}>, keep
the existing type for props (DialogPrimitive.Trigger.Props) and preserve
CommandDialogTrigger.displayName.
packages/raystack/components/command/command.module.css (2)

160-168: Hard-coded backdrop color bypasses design tokens.

background-color: rgb(0 0 0 / 0.32) is the only hard-coded color in this module. Every other surface uses var(--rs-color-*) tokens. Consider introducing/using a token (e.g., --rs-color-background-backdrop) so theming (light/dark, brand overrides) remains consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command.module.css` around lines 160 -
168, Replace the hard-coded backdrop color in the .backdrop rule
(background-color: rgb(0 0 0 / 0.32)) with a design token so theming works;
update the .backdrop CSS to use a token such as
var(--rs-color-background-backdrop) (with a sensible fallback token like
var(--rs-color-surface-overlay) or an rgba fallback) and ensure the new token is
defined in your global token/theme files so light/dark and brand overrides apply
consistently.

22-29: .list max-height may under-utilize the dialog popup height.

.list caps at max-height: 320px while .dialogPopup allows up to min(420px, 80vh). Because the list is the scrollable region inside the popup, the dialog will never actually use the extra ~100px it reserves — the list will stop growing at 320px regardless. Consider letting the list flex to fill available popup space (e.g., flex: 1; min-height: 0; with the 320px cap only for the inline/non-dialog case) so the dialog's max-height is meaningful.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command.module.css` around lines 22 -
29, The .list class currently uses max-height: 320px which prevents the dialog
(.dialogPopup) from using its larger max (min(420px, 80vh)); update .list to
allow flex growth inside the popup by adding flex: 1 and min-height: 0 so it
will fill available popup space and become the scroll container, and remove or
move the 320px cap so it only applies for the inline/non-dialog variant (e.g.,
keep the 320px limit in a separate .list--inline or media/context selector),
ensuring .dialogPopup + .list can reach the dialog's max height.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/www/src/content/docs/components/command/index.mdx`:
- Around line 98-100: Update the docs to describe Command.Shortcut as a wrapper
element (not a single <kbd>) that renders a span container with one <kbd> per
key token; change the wording in the Command.Shortcut section so it explains
that classes/props apply to the wrapper span and that it typically renders
multiple <kbd> elements (often used as trailingIcon on Command.Item) rather than
being a single <kbd>.

---

Outside diff comments:
In `@apps/www/src/app/examples/combobox/page.tsx`:
- Around line 2-16: Remove the unused Combobox import and either update the
component to render a Combobox example or move/rename the file to match the
Slider content; specifically, edit the Page component to (a) delete "Combobox"
from the import list if you intend to keep the Slider-only demo, or (b) replace
the Slider JSX with a proper Combobox example using the Combobox component so
the file at app/examples/combobox/ actually showcases Combobox; ensure imports
and exported Page component reflect the chosen content so there are no unused
imports or route/content mismatches.

In `@apps/www/src/components/docs/search.tsx`:
- Around line 170-216: The separator condition is checking the wrong collection:
inside the items.map loop use the iterated array length instead of defaultItems;
update the condition that renders Command.Separator (currently using
defaultItems.length - 1) to compare index against items.length - 1 so separators
are based on the current mapped collection (items which may be searchResults
when isSearching is true).
- Line 116: Remove the debug console.log call that prints the grouped variable
in the search component: delete the line console.log('grouped', grouped) inside
the search logic in apps/www/src/components/docs/search.tsx (where grouped is
computed). Ensure no other debug console.log statements remain in the same
function (e.g., inside the search handler or render path) so production searches
do not log to the console.

---

Duplicate comments:
In `@apps/www/src/content/docs/components/command/demo.ts`:
- Line 12: The Command.DialogContent usage renders an unnamed modal; add an
accessible name by adding an aria-label (or aria-labelledby) attribute to every
Command.DialogContent instance (e.g., the occurrences around
Command.DialogContent in this file) so the demo dialogs are accessible and
copied snippets include the label; update each Command.DialogContent element to
include a descriptive aria-label string (or reference a heading id via
aria-labelledby) to satisfy accessibility requirements.

In `@apps/www/src/content/docs/components/command/index.mdx`:
- Around line 29-41: The example dialog rendered via Command.DialogContent has
no accessible name; add an accessible label by supplying an aria-label or title
on the dialog wrapper (the Command.DialogContent element) or the top-level
Command component so the modal is announced and copy-paste examples don’t
produce an unnamed dialog; update the example to include a descriptive string
(e.g., aria-label="Command Palette" or title="Command Palette") on
Command.DialogContent (or Command) to resolve the accessibility issue.
- Around line 80-82: The doc copy for the "Group" section is inaccurate: update
the wording around Command.Group to state that when search is active the group
itself is hidden but matching child items are unwrapped (shown) while group
labels/separators are hidden; locate the "Group" heading in
apps/www/src/content/docs/components/command/index.mdx and replace the current
two-line description with a sentence that clearly says Command.Group hides the
group wrapper and labels/separators during search but preserves and displays
matching child items (i.e., unwraps them).

In `@apps/www/src/content/docs/components/command/props.ts`:
- Around line 16-20: The prop JSDoc for onValueChange in props.ts only documents
a single string argument but the Command components (e.g., CommandRoot / Command
/ Base UI) may forward richer event details; verify how onValueChange is invoked
in the Command implementation (search for symbols onValueChange, CommandRoot,
CommandDialog, DialogPrimitive.Root in the command component files) and then
update the onValueChange type and JSDoc to match the actual forwarded signature
(e.g., accept an event or detail object instead of just (value: string) => void)
so the API docs reflect the real callback shape; change the onValueChange
declaration and comment in props.ts to the verified parameter shape and add a
short example of the payload if appropriate.

---

Nitpick comments:
In `@apps/www/src/components/playground/command-examples.tsx`:
- Around line 10-19: The keydown handler in the useEffect uses event.key === 'k'
which fails when CapsLock or Shift produce 'K'; update the handler in the
useEffect to normalize the key (e.g., use event.key.toLowerCase()) before
comparing to 'k', keep event.preventDefault() and toggling setOpen(prev =>
!prev), and ensure you still add/remove the same handler via
document.addEventListener/removeEventListener so behavior matches the
implementation in docs/search.tsx.

In `@packages/raystack/components/command/command-dialog.tsx`:
- Around line 37-56: CommandDialogContent renders DialogPrimitive.Popup but the
package doesn't export companion accessible helpers
(DialogTitle/DialogDescription/DialogClose), forcing consumers to use
DialogPrimitive directly or aria-label; add and export namespaced re-exports
(e.g., Command.DialogTitle, Command.DialogDescription, Command.DialogClose) that
wrap or re-export DialogPrimitive.Title, DialogPrimitive.Description, and
DialogPrimitive.Close (or simple passthrough components) so consumers can
provide accessible names; update the module's public exports to include these
symbols alongside CommandDialogContent/Command to match the general Dialog API
and ensure examples (like in command-examples.tsx) can import them from the
Command namespace.
- Around line 13-17: CommandDialogTrigger is the only Command subcomponent still
using forwardRef; change it to the React 19 ref-as-prop pattern to match
CommandContent/CommandInput/CommandItem by accepting a ref prop instead of using
forwardRef. Update the component signature for CommandDialogTrigger to a plain
function/component that takes props including ref?: React.Ref<HTMLButtonElement>
and spreads the rest into <DialogPrimitive.Trigger {...props} ref={ref}>, keep
the existing type for props (DialogPrimitive.Trigger.Props) and preserve
CommandDialogTrigger.displayName.

In `@packages/raystack/components/command/command.module.css`:
- Around line 160-168: Replace the hard-coded backdrop color in the .backdrop
rule (background-color: rgb(0 0 0 / 0.32)) with a design token so theming works;
update the .backdrop CSS to use a token such as
var(--rs-color-background-backdrop) (with a sensible fallback token like
var(--rs-color-surface-overlay) or an rgba fallback) and ensure the new token is
defined in your global token/theme files so light/dark and brand overrides apply
consistently.
- Around line 22-29: The .list class currently uses max-height: 320px which
prevents the dialog (.dialogPopup) from using its larger max (min(420px, 80vh));
update .list to allow flex growth inside the popup by adding flex: 1 and
min-height: 0 so it will fill available popup space and become the scroll
container, and remove or move the 320px cap so it only applies for the
inline/non-dialog variant (e.g., keep the 320px limit in a separate
.list--inline or media/context selector), ensuring .dialogPopup + .list can
reach the dialog's max height.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ffe6c835-8959-4477-94e6-7e936e27c456

📥 Commits

Reviewing files that changed from the base of the PR and between 2b4dd86 and 341443b.

📒 Files selected for processing (15)
  • apps/www/src/app/examples/combobox/page.tsx
  • apps/www/src/components/demo/demo.tsx
  • apps/www/src/components/docs/search.tsx
  • apps/www/src/components/playground/command-examples.tsx
  • apps/www/src/content/docs/components/command/demo.ts
  • apps/www/src/content/docs/components/command/index.mdx
  • apps/www/src/content/docs/components/command/props.ts
  • packages/raystack/components/command/__tests__/command.test.tsx
  • packages/raystack/components/command/command-content.tsx
  • packages/raystack/components/command/command-dialog.tsx
  • packages/raystack/components/command/command-item.tsx
  • packages/raystack/components/command/command-misc.tsx
  • packages/raystack/components/command/command-root.tsx
  • packages/raystack/components/command/command.module.css
  • packages/raystack/components/command/command.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/www/src/components/demo/demo.tsx
  • packages/raystack/components/command/command-item.tsx
  • packages/raystack/components/command/command-root.tsx

Comment thread apps/www/src/content/docs/components/command/index.mdx
Comment thread apps/www/src/components/docs/search.tsx
Comment thread apps/www/src/components/playground/command-examples.tsx
Comment thread apps/www/src/content/docs/components/command/index.mdx
Comment thread packages/raystack/components/command/command-input.tsx
Comment thread packages/raystack/components/command/command-misc.tsx
Comment thread packages/raystack/components/command/command-item.tsx
Comment thread apps/www/src/content/docs/components/command/index.mdx
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 (2)
apps/www/src/content/docs/components/command/demo.ts (1)

8-17: ⚠️ Potential issue | 🟠 Major

Dialog examples still lack an accessible name.

None of the demos produced by wrapInDialog (nor itemsDemo / controlledDemo) pass an aria-label or a Dialog.Title to Command.DialogContent. Copy-pasting these into a real app produces a modal with no accessible name, which is an a11y violation and a bad default for a code sample users will mimic. Add aria-label="Command menu" (or similar) on Command.DialogContent in wrapInDialog and in the two inline demos that don't use it.

Proposed fix for the helper
-        <Command.DialogContent>
+        <Command.DialogContent aria-label="Command menu">
           <Command>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/demo.ts` around lines 8 - 17,
The dialog demos lack an accessible name: update the modal wrapper used by
wrapInDialog and the two inline demos itemsDemo and controlledDemo to add an
accessible name by setting aria-label="Command menu" (or adding a Dialog.Title)
on Command.DialogContent so the Command.DialogContent component receives an
explicit accessible name for screen readers.
apps/www/src/content/docs/components/command/index.mdx (1)

98-102: ⚠️ Potential issue | 🟡 Minor

Command.Shortcut is a wrapper, not a <kbd>.

The implementation renders a <span> containing one <kbd> per key token (string children are split on whitespace). Describing it as "A <kbd> element" will mislead consumers about where className/props land (they land on the wrapper span) and how many elements are in the DOM.

Proposed wording
-A `<kbd>` element for keyboard hints. Typically passed as `trailingIcon` on `Command.Item`.
+Renders keyboard hints as a `<span>` wrapper containing one `<kbd>` per key token (string children are split on whitespace). Typically passed as `trailingIcon` on `Command.Item`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/index.mdx` around lines 98 -
102, Update the docs copy for Command.Shortcut to clarify it's a wrapper
component (Command.Shortcut) that renders a wrapper <span> with any
className/props applied to that span and then outputs one <kbd> per key token
(it splits string children on whitespace), rather than saying "A `<kbd>`
element"; explicitly note that multiple `<kbd>` elements may be rendered and
that props do not land on the individual `<kbd>` elements.
🧹 Nitpick comments (3)
apps/www/src/content/docs/components/command/demo.ts (1)

35-42: Dead onClick on a disabled item.

The "Run workflow..." item is rendered with both disabled and onClick={() => setOpen(false)}. The disabled item has pointer-events: none (per command.module.css) and is skipped in keyboard navigation, so the handler can never fire. Either drop the onClick to keep the example honest, or drop disabled — as written the code teaches readers that disabled items still need handlers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/demo.ts` around lines 35 - 42,
The Command.Item "Run workflow..." is marked disabled but still provides an
onClick handler (onClick={() => setOpen(false)}) which can never fire; remove
the onClick prop from that Command.Item (or alternatively remove the disabled
prop if you intend it to be interactive) so the example is consistent—locate the
Command.Item node rendering "Run workflow..." and either delete the onClick
reference to setOpen or drop the disabled attribute accordingly.
packages/raystack/components/command/command.module.css (1)

17-20: Descendant hover/focus background rule is broad.

.inputWrapper > *:hover, .inputWrapper > *:focus-within { background: var(--rs-color-background-base-primary) } forces the primary background on any direct child that is hovered or focused-within, which will override whatever hover/focus styling the nested InputField (or other controls) ships with. If Command.Input ever renders a wrapper with its own hover state (e.g. a subtle surface color), this rule will silently neutralize it. Consider scoping to the specific input container you control (e.g. a dedicated class) instead of a universal child selector.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/command/command.module.css` around lines 17 -
20, The CSS rule using the broad selector `.inputWrapper > *:hover,
.inputWrapper > *:focus-within` overrides hover/focus styles of nested controls
like Command.Input and InputField; narrow it by targeting the specific container
class you control (e.g. add and use a dedicated class such as
`.inputWrapper__controlled` or `.commandInputContainer` and apply the
hover/focus background only to that class or its known child selector), leaving
other direct children (third‑party input wrappers) unaffected; update the
selector in command.module.css and any markup that renders Command.Input to
include the new class so only the intended input container gets the background
override.
apps/www/src/content/docs/components/command/index.mdx (1)

158-162: Accessibility section conflates inline and dialog modes.

Line 160 states "Escape closes the dialog" and line 162 describes focus trapping "When used inside Command.Dialog" — but the component is also documented as renderable inline (see the Inline example). For inline usage, Escape does not close anything, and there is no focus trap. Consider qualifying these bullets so inline consumers don't assume behaviors that only exist in dialog mode.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/command/index.mdx` around lines 158 -
162, The Accessibility bullets currently mix behaviors for both inline and
dialog modes; update the list so that dialog-only behaviors are explicitly
qualified as applying to Command.Dialog (e.g., change "Escape closes the dialog"
to "In Command.Dialog, Escape closes the dialog") and note that inline usage
does not close on Escape and has no focus trap, and similarly qualify "focus is
trapped..." to "When used inside Command.Dialog, focus is trapped..."; keep
shared behaviors (roles, keyboard arrow/enter handling, aria-disabled skipping)
unqualified or move dialog-specific behaviors into a separate "Dialog mode"
sub-bullet to avoid misleading inline consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/www/src/content/docs/components/command/index.mdx`:
- Around line 86-96: Update the wording in the Command component docs to clarify
context-aware hiding: change the Command.Label description to "While searching,
the label is hidden only when no child items match the query.", change the
Command.Separator description to "Hidden while searching when the surrounding
group has no matching items.", and update the Command.Group aside to "While
searching, grouping chrome is hidden for groups whose items don't match;
matching items remain visible." — locate and edit the text blocks for
CommandLabel, CommandSeparator, and CommandGroup in the docs file to replace the
current "hidden automatically while the user is searching" phrasing with these
exact sentences.

---

Duplicate comments:
In `@apps/www/src/content/docs/components/command/demo.ts`:
- Around line 8-17: The dialog demos lack an accessible name: update the modal
wrapper used by wrapInDialog and the two inline demos itemsDemo and
controlledDemo to add an accessible name by setting aria-label="Command menu"
(or adding a Dialog.Title) on Command.DialogContent so the Command.DialogContent
component receives an explicit accessible name for screen readers.

In `@apps/www/src/content/docs/components/command/index.mdx`:
- Around line 98-102: Update the docs copy for Command.Shortcut to clarify it's
a wrapper component (Command.Shortcut) that renders a wrapper <span> with any
className/props applied to that span and then outputs one <kbd> per key token
(it splits string children on whitespace), rather than saying "A `<kbd>`
element"; explicitly note that multiple `<kbd>` elements may be rendered and
that props do not land on the individual `<kbd>` elements.

---

Nitpick comments:
In `@apps/www/src/content/docs/components/command/demo.ts`:
- Around line 35-42: The Command.Item "Run workflow..." is marked disabled but
still provides an onClick handler (onClick={() => setOpen(false)}) which can
never fire; remove the onClick prop from that Command.Item (or alternatively
remove the disabled prop if you intend it to be interactive) so the example is
consistent—locate the Command.Item node rendering "Run workflow..." and either
delete the onClick reference to setOpen or drop the disabled attribute
accordingly.

In `@apps/www/src/content/docs/components/command/index.mdx`:
- Around line 158-162: The Accessibility bullets currently mix behaviors for
both inline and dialog modes; update the list so that dialog-only behaviors are
explicitly qualified as applying to Command.Dialog (e.g., change "Escape closes
the dialog" to "In Command.Dialog, Escape closes the dialog") and note that
inline usage does not close on Escape and has no focus trap, and similarly
qualify "focus is trapped..." to "When used inside Command.Dialog, focus is
trapped..."; keep shared behaviors (roles, keyboard arrow/enter handling,
aria-disabled skipping) unqualified or move dialog-specific behaviors into a
separate "Dialog mode" sub-bullet to avoid misleading inline consumers.

In `@packages/raystack/components/command/command.module.css`:
- Around line 17-20: The CSS rule using the broad selector `.inputWrapper >
*:hover, .inputWrapper > *:focus-within` overrides hover/focus styles of nested
controls like Command.Input and InputField; narrow it by targeting the specific
container class you control (e.g. add and use a dedicated class such as
`.inputWrapper__controlled` or `.commandInputContainer` and apply the
hover/focus background only to that class or its known child selector), leaving
other direct children (third‑party input wrappers) unaffected; update the
selector in command.module.css and any markup that renders Command.Input to
include the new class so only the intended input container gets the background
override.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d231fce9-505a-4a04-9bf0-fa426e61e313

📥 Commits

Reviewing files that changed from the base of the PR and between 341443b and baeb4ad.

📒 Files selected for processing (5)
  • apps/www/src/content/docs/components/command/demo.ts
  • apps/www/src/content/docs/components/command/index.mdx
  • docs/V1-migration.md
  • packages/raystack/components/command/command-item.tsx
  • packages/raystack/components/command/command.module.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/raystack/components/command/command-item.tsx

Comment thread apps/www/src/content/docs/components/command/index.mdx
@rohanchkrabrty rohanchkrabrty merged commit d6e37b9 into main Apr 23, 2026
5 checks passed
@rohanchkrabrty rohanchkrabrty deleted the worktree-command branch April 23, 2026 06:25
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