Command
A searchable list of things to do — and the one component here that the platform does not help with.
src/components/rahti_ui/command.rs
The palette
A search box, a list, and a filter that runs as you type.
shadcn's own demo, with this library's icon set standing in for its lucide names. Type `cal` and both Calendar and Calculator stay. Type `cldr` and Calendar alone survives: a row matches when the query's letters appear in order, and `calculator` has no `d`.
Empty the box and everything comes back. Type something nothing matches and the `No results found.` line appears — it is server-rendered `hidden`, because with no script no search has run and claiming otherwise would be a lie.
Watch the rule between the two groups while you type: it goes. That is cmdk's rule, not an accident — a line between two groups means nothing once the groups have been filtered down. `always` keeps it.
The highlight moves with the arrow keys and with the pointer, and the caret never leaves the search box. That is the whole reason shadcn's row says `data-[selected=true]:bg-accent` where every menu in this library says `focus:bg-accent`: here the highlighted row is not the focused element, so the input names it with `aria-activedescendant` instead.
In a dialog
A command palette, which is what the component is usually for.
The same palette, bigger: a taller search bar, taller rows, and groups that pay their own horizontal padding. None of that is written here — it is shadcn's twelve-selector override, and eleven of the twelve select on a `cmdk-*` attribute, which is the clearest argument this port has for writing those attributes at all.
Two of the twelve do nothing, and that is shadcn's arithmetic rather than this port's: the override asks for 20px icons, the row's own `size-4` rule asks for 16px, the two selectors have identical specificity, and Tailwind emits the row's later. Measured in this project's stylesheet: 16px. The strings are kept as published rather than quietly corrected.
`CommandDialog` is the dialog's *content* rather than the whole dialog, so the trigger is written beside it. shadcn's is the whole thing because Radix's `Dialog.Root` renders no element and takes `open` and `onOpenChange`; this library's Dialog is a real root with a real trigger, which is the same divergence every port in here makes.
Escape closes the box, and the palette does not intercept it: that key is the dialog's `cancel` event and the script leaves it alone. The title and description are `sr-only` and live inside the box, so the modal has an accessible name without a heading nobody wants above a search field.
Scrollable
A command menu with more rows than the list can show.
Seventeen rows in four groups, which is more than `max-h-[300px]` can show — so the list scrolls. That is the whole point of shadcn's example, and it is the one that puts real weight on `CommandList`: the height cap and `overflow-y-auto` are the component's, not the page's.
Hold ArrowDown from the top and the highlight walks off the bottom edge — and the list follows it. The script calls `scrollIntoView({ block: 'nearest' })` on every move, which scrolls only when the row is actually out of view, so a row already on screen does not make the list jump.
`scroll-py-1` is what keeps the row it scrolled to off the very edge of the box. It is in shadcn's `CommandList` string and does nothing at all until a list is long enough to scroll — which is why this example is the one that shows it.
Type `ne` and four groups collapse to one: every group whose rows all filtered out sets `hidden` on itself, and the separators step aside. The list stops scrolling because there is no longer enough in it to scroll.
The icons are this library's standing in for shadcn's lucide names, the same substitution the Menubar page makes — this icon set has 35 entries and lucide has a thousand.
Searching by something else
`value`, `keywords`, accents, and rows that are links.
`Billing` answers to `invoice`, `payment` and `receipt` through `keywords`, which are searched alongside its label. `Appearance` answers to `dark` through `value`, which replaces its label entirely — cmdk's two props, both written as `data-` attributes the script reads.
`Café Notes` answers to `cafe`. Both sides are decomposed and stripped of their combining marks before they are compared, so the accent is not something anyone has to type.
The rule between these two groups carries `always`, so it stays while you search. The rows under `Go to` have an `href`: they are `<a role="option">` and the browser activates them, which makes them the only part of this component that works before the script mounts.
What the script does
All of it, which is unusual here and worth being plain about.
There is no row in this table for the platform, and that is the honest shape of this component. A Dropdown Menu opens with `popovertarget` and a Dialog with `showModal()`; nothing filters a list as you type. What the server still does is render the whole list, the roles and the labels — so an unmounted Command is a readable list with a search box that does nothing, rather than an empty box.
The port
What crossed, what was left behind, and the one library this file quotes rather than replaces.
The dialog override is 13 selectors long and every one of them crossed untouched. Eleven select on a `cmdk-*` attribute and one on a `data-slot`, which is the whole argument for writing both.