feat: migrate command to Base UI#748
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe 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
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
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: MakeCommandRootPropsgeneric to preserve item type information.
AutocompletePrimitive.Root.Props<any>combined withitems?: readonly any[]discards the item-type generic, causing downstream callbacks likefilteroritemToStringValueto receiveanyfor each item. Preserve type safety by makingCommandRootPropsgeneric 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 toItem = 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
📒 Files selected for processing (15)
apps/www/src/components/demo/demo.tsxapps/www/src/components/playground/command-examples.tsxapps/www/src/content/docs/components/command/demo.tsapps/www/src/content/docs/components/command/index.mdxapps/www/src/content/docs/components/command/props.tspackages/raystack/components/command/__tests__/command.test.tsxpackages/raystack/components/command/command-dialog.tsxpackages/raystack/components/command/command-empty.tsxpackages/raystack/components/command/command-input.tsxpackages/raystack/components/command/command-item.tsxpackages/raystack/components/command/command-list.tsxpackages/raystack/components/command/command-misc.tsxpackages/raystack/components/command/command-root.tsxpackages/raystack/components/command/command.module.csspackages/raystack/components/command/command.tsx
| 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> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
🧩 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:
- 1: https://base-ui.com/react/components/autocomplete
- 2: https://github.com/mui/base-ui/blob/d81ec002/packages/react/src/autocomplete/root/AutocompleteRoot.tsx
- 3: https://base-ui.com/react/components/autocomplete/
- 4: https://mui-base-ui.mintlify.app/components/autocomplete
🏁 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 -20Repository: raystack/apsara
Length of output: 2382
🏁 Script executed:
fd -e "tsx" -e "ts" | xargs grep -l "CommandRoot" | head -10Repository: raystack/apsara
Length of output: 215
🏁 Script executed:
cat -n packages/raystack/components/command/command-root.tsxRepository: raystack/apsara
Length of output: 3218
🏁 Script executed:
cat -n packages/raystack/components/command/command-item.tsxRepository: raystack/apsara
Length of output: 1754
🏁 Script executed:
cat -n packages/raystack/components/command/command-input.tsxRepository: 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.
There was a problem hiding this comment.
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 | 🟡 MinorRemove unused
Comboboximport and address route/content mismatch.
Comboboxis imported on line 2 but not used anywhere in the component, which will trigger unused import warnings. Additionally, this page lives atapp/examples/combobox/but renders only aSlidercomponent, 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 | 🟡 MinorSeparator condition references the wrong array.
Inside the loop over
items, the separator usesdefaultItems.length - 1. WhenisSearchingis true,itemsissearchResults, which can have a different length thandefaultItems, 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.Separatorhides itself while searching via context, but the logic should still be correct againstitems.🤖 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 | 🟡 MinorRemove 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 | 🟠 MajorGive every documented command dialog an accessible name.
All generated dialog demos currently render unnamed modal content. Add
aria-labelhere 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 | 🟡 MinorDocument 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 | 🟠 MajorLabel the copy-pastable dialog example.
This dialog content has no accessible name. Add an
aria-labelor 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 | 🟡 MinorKeep the group-search behavior copy aligned with the implementation.
Command.Groupunwraps 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.keybecomes'K'. The analogous handler inapps/www/src/components/docs/search.tsxusesevent.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.
CommandDialogContentrenders aDialogPrimitive.Popupbut exposes no siblingCommand.DialogTitle/Command.DialogDescription/Command.DialogClosehelpers. Consumers (including the playground example inapps/www/src/components/playground/command-examples.tsx) have no in-namespace way to give the dialog an accessible name and must either drop toDialogPrimitive.*directly or addaria-labelon the popup. Consider exporting these the way the generalDialogAPI 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:forwardRefstyle inconsistent with sibling components.The other Command subcomponents in this PR (e.g.,
CommandContent,CommandInput,CommandItem) adopt the React 19 ref-as-prop pattern.CommandDialogTriggeris the only one still usingforwardRef. 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 usesvar(--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:.listmax-height may under-utilize the dialog popup height.
.listcaps atmax-height: 320pxwhile.dialogPopupallows up tomin(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
📒 Files selected for processing (15)
apps/www/src/app/examples/combobox/page.tsxapps/www/src/components/demo/demo.tsxapps/www/src/components/docs/search.tsxapps/www/src/components/playground/command-examples.tsxapps/www/src/content/docs/components/command/demo.tsapps/www/src/content/docs/components/command/index.mdxapps/www/src/content/docs/components/command/props.tspackages/raystack/components/command/__tests__/command.test.tsxpackages/raystack/components/command/command-content.tsxpackages/raystack/components/command/command-dialog.tsxpackages/raystack/components/command/command-item.tsxpackages/raystack/components/command/command-misc.tsxpackages/raystack/components/command/command-root.tsxpackages/raystack/components/command/command.module.csspackages/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
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
apps/www/src/content/docs/components/command/demo.ts (1)
8-17:⚠️ Potential issue | 🟠 MajorDialog examples still lack an accessible name.
None of the demos produced by
wrapInDialog(noritemsDemo/controlledDemo) pass anaria-labelor aDialog.TitletoCommand.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. Addaria-label="Command menu"(or similar) onCommand.DialogContentinwrapInDialogand 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.Shortcutis 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 whereclassName/propsland (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: DeadonClickon a disabled item.The "Run workflow..." item is rendered with both
disabledandonClick={() => setOpen(false)}. The disabled item haspointer-events: none(percommand.module.css) and is skipped in keyboard navigation, so the handler can never fire. Either drop theonClickto keep the example honest, or dropdisabled— 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 nestedInputField(or other controls) ships with. IfCommand.Inputever 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 "
Escapecloses the dialog" and line 162 describes focus trapping "When used insideCommand.Dialog" — but the component is also documented as renderable inline (see the Inline example). For inline usage,Escapedoes 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
📒 Files selected for processing (5)
apps/www/src/content/docs/components/command/demo.tsapps/www/src/content/docs/components/command/index.mdxdocs/V1-migration.mdpackages/raystack/components/command/command-item.tsxpackages/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
Summary
cmdkto the Base UI Autocomplete primitive with built-in auto-search filteringcommand.tsxinto dedicated subcomponent files (command-root,command-input,command-list,command-item,command-dialog,command-empty,command-misc)Panel,Footer,Label,Collection, and a composableDialogAPI (Dialog.Trigger,Dialog.Content,Dialog.Close,Dialog.Title,Dialog.Description)