registry
@automindzsheets 01–77shadcn 4.x
SHEET 20

Command Palette

block / layout

⌘K launcher that searches navigation, actions and recents in one result set, with awaited commands, scoped child lists and async results.

type
block
files
2
registry deps
button, command, dialog
npm deps
lucide-react

installs

  • lib/automindz/commands.ts
  • components/automindz/command-palette.tsx
  • Commands are data — nav, actions and recents in one result set
  • ⌘K / Ctrl+K, and never while the user is typing in a field
  • Awaited `run`: pending on the row, failure in place, no optimistic close
  • One level of scoped child commands, Escape or Backspace to leave
  • Async `search` with its own loading and error states

Commands are data: pass commands (contract in lib/automindz/commands.ts). Mount it once, high in the tree. run is awaited — the row stays busy and a failure shows in place, so never close optimistically. href goes through onNavigate; pass onNavigate={(href) => router.push(href)} for client-side routing. Always render CommandPaletteTrigger as well: a palette reachable only by ⌘K is invisible to anyone who was not told the hotkey. Set hotkey={false} if the host app already owns ⌘K.

Variations

one state per section

01

The launcher, opened two ways

Press ⌘K (Ctrl+K) anywhere on this page, or the button — a palette only a hotkey can reach is invisible to anyone who has not been told the hotkey. Open with nothing typed and you get recents and the groups, never a blank box; recents vanish the moment you type, because a recent row plus its own group row is one command listed twice. Groups sit in the order the array declares them, not alphabetically. Type inv to reach Billing through a keyword that is not in its label, zzz for a no-results message that names what you typed. Focus a field first and ⌘K does nothing: the keystroke belongs to whoever is typing. This is the only preview on the page with the hotkey bound.

fluid

Command palette

Search navigation, actions and recents in one list.

02

A command that runs, and one that fails

run is awaited. Press “Rebuild search index”: the row spins, everything else recedes, the palette refuses to close, and it dismisses only once the work settles. Press “Deploy to production”: the rejection renders on its own row with the reason and turns it into a retry — icon, label and colour all change, so it never depends on red alone. A palette that closes optimistically and swallows that error is worse than no palette.

fluid

Command palette

Search navigation, actions and recents in one list.

03

A scoped child list

“Change status →” pushes its own list. One level, capped in the type rather than left to a recursion the row height cannot carry. Escape goes up before it closes the palette, Backspace on an empty query does the same, and the breadcrumb’s back button is there for the pointer — with focus handed back to the input, so the arrow keys keep working.

fluid

Command palette

Search navigation, actions and recents in one list.

04

Async results, with their own states

The search hook is for rows a client fetches. It is debounced, runs only with a query, and lands **below** the local groups so late arrivals never shove the list out from under the cursor. Three distinct outcomes, all pressable: type sa for results, zz for “no matches”, and fail for the service error — loading, failed and nothing-matched are three messages, never one.

fluid

Command palette

Search navigation, actions and recents in one list.

05

Blocked, with the reason on the row

A command the current user cannot run stays in the list, disabled, with the reason as its second line rather than a tooltip — hover-only text is unreachable for a keyboard, and a palette is a keyboard surface. Arrow keys skip them, so the block is real and not just visual.

fluid

Command palette

Search navigation, actions and recents in one list.

06

Nothing configured

The fourth message: no commands at all is not the same as nothing matched, not the same as still loading, and not the same as a failed fetch. commands={[]} says so instead of showing an empty frame.

fluid

Command palette

Search navigation, actions and recents in one list.