Summary
@netscript/fresh-ui ships no command-palette primitive. A ⌘K surface is now
standard furniture in this class of app, and building one correctly is more
subtle than it looks — I have just done it by hand in eis-chat and would
rather contribute it back than have the next app repeat the work.
What is missing
Combobox is close but not sufficient. It is a headless roving-keys input
over a listbox, which gives you the input and the keyboard, but a command
palette additionally needs:
- grouped results with headings, where arrow keys traverse the flattened
order across group boundaries;
- filtering and ranking owned by the consumer —
Combobox does not filter,
which is correct, but every consumer then reimplements the same fuzzy scorer;
- a virtual active row via
aria-activedescendant with rows as
role="option" that are not tab stops, so the surface has exactly one
focusable element;
mousemove-driven hover rather than mouseenter, or the keyboard
highlight is stolen whenever the list scrolls under a stationary pointer;
- an empty state that is a peer of the groups rather than a listbox child;
- Escape as a two-stage retreat (clear the query, then close).
Composing Dialog + Combobox also currently inherits #1688.
Proposed shape
A headless useCommandPalette plus a thin Command.* namespace mirroring the
parts that have become conventional (Root, Input, List, Empty, Group,
Item, Separator, Shortcut), with:
items: readonly {id, group}[] in display order, and the hook owning
activeIndex, wrap-around, Home/End/PageUp/PageDown, and
scrollIntoView({block: 'nearest'});
getInputProps() emitting role="combobox", aria-expanded,
aria-controls, aria-activedescendant, aria-autocomplete="list";
getItemProps(index) emitting role="option", aria-selected, the option
id, and the mousemove handler;
- ranking left to the consumer, with an optional exported subsequence scorer.
Prior art to copy from
eis-chat's implementation, which is tested and measured:
https://github.com/rickylabs/eis-chat/pull/255
lib/commands/score.ts — fuzzy subsequence scorer (DP over query x text)
with word-start / acronym / consecutive-run bonuses and gap penalties,
returning match ranges for highlighting. 10 unit tests.
lib/commands/search.ts — grouping, per-group browse caps, group ordering by
best member, and a "breadcrumb only when it is not an echo of its own group
heading" rule.
lib/commands/registry.ts — provider registry so features contribute
commands without editing the palette.
components/ui/command-palette.tsx — the a11y wiring described above.
Happy to raise a PR extracting the headless half if the shape looks right.
Summary
@netscript/fresh-uiships no command-palette primitive. A ⌘K surface is nowstandard furniture in this class of app, and building one correctly is more
subtle than it looks — I have just done it by hand in eis-chat and would
rather contribute it back than have the next app repeat the work.
What is missing
Comboboxis close but not sufficient. It is a headless roving-keys inputover a listbox, which gives you the input and the keyboard, but a command
palette additionally needs:
order across group boundaries;
Comboboxdoes not filter,which is correct, but every consumer then reimplements the same fuzzy scorer;
aria-activedescendantwith rows asrole="option"that are not tab stops, so the surface has exactly onefocusable element;
mousemove-driven hover rather thanmouseenter, or the keyboardhighlight is stolen whenever the list scrolls under a stationary pointer;
Composing
Dialog+Comboboxalso currently inherits #1688.Proposed shape
A headless
useCommandPaletteplus a thinCommand.*namespace mirroring theparts that have become conventional (
Root,Input,List,Empty,Group,Item,Separator,Shortcut), with:items: readonly {id, group}[]in display order, and the hook owningactiveIndex, wrap-around,Home/End/PageUp/PageDown, andscrollIntoView({block: 'nearest'});getInputProps()emittingrole="combobox",aria-expanded,aria-controls,aria-activedescendant,aria-autocomplete="list";getItemProps(index)emittingrole="option",aria-selected, the optionid, and the
mousemovehandler;Prior art to copy from
eis-chat's implementation, which is tested and measured:
https://github.com/rickylabs/eis-chat/pull/255
lib/commands/score.ts— fuzzy subsequence scorer (DP over query x text)with word-start / acronym / consecutive-run bonuses and gap penalties,
returning match ranges for highlighting. 10 unit tests.
lib/commands/search.ts— grouping, per-group browse caps, group ordering bybest member, and a "breadcrumb only when it is not an echo of its own group
heading" rule.
lib/commands/registry.ts— provider registry so features contributecommands without editing the palette.
components/ui/command-palette.tsx— the a11y wiring described above.Happy to raise a PR extracting the headless half if the shape looks right.