Command Palette (Phase 4)
Synced read-only from
/home/synnet/mirrors/synvirt-product/docs/modules/web-ux-v2/COMMAND_PALETTE.md. Edit at the source, not here.
Command Palette (Phase 4)
Section titled “Command Palette (Phase 4)”The v2 console’s command palette is the keyboard-first surface. It
opens with Cmd+K (macOS) / Ctrl+K (everywhere else) and lets the
operator navigate, create, switch theme, or sign out without
touching the mouse.
Anatomy
Section titled “Anatomy”┌────────────────────────────────────────────────┐│ 🔍 Type a command — navigate, create, switch …│├────────────────────────────────────────────────┤│ CREATE ││ [+] Create virtual machine… Open the wizard││ [↑] Upload installation media Add an ISO ││ NAVIGATE ││ [▤] Go to Dashboard ││ [▤] Go to Virtual machines ││ ... │├────────────────────────────────────────────────┤│ ↑ ↓ Navigate · ⏎ Run · Esc Close · 12 commands│└────────────────────────────────────────────────┘Built on Headless UI’s Combobox so keyboard navigation, ARIA
roles, and focus management work out of the box.
Architecture
Section titled “Architecture”| Piece | Responsibility |
|---|---|
CommandPalette.vue |
Modal + Combobox UI |
useCommandPalette() |
Module-level singleton state — registry, query, filter |
useGlobalShortcuts() |
Window keyboard listener (Cmd/Ctrl+K, Cmd/Ctrl+B) |
useUiStore.paletteOpen |
Open/close flag everyone reads |
The palette is mounted by AppShell so any view can open it via
useUiStore().openPalette().
Filtering
Section titled “Filtering”Score formula:
- Exact substring anywhere in
label,hint,group, orkeywords. Bonus 20 points if the hit starts at offset 0. - Subsequence — every character of the query appears in order somewhere in the searchable text. Score = matched / target length.
- Otherwise drop the row.
Top results land first; tie-breaks fall back to registration order.
Adding a command
Section titled “Adding a command”import { useCommandPalette } from '@/composables/useCommandPalette';import { Plus } from 'lucide-vue-next';
const palette = useCommandPalette();palette.register({ id: 'create-snapshot', label: 'Create snapshot…', hint: 'Open the snapshot dialog', icon: Plus, group: 'create', keywords: ['snapshot', 'backup', 'restore'], keys: ['⇧', 'S'], run: () => doSomething(),});registerWithCleanup is the safer variant — it auto-unregisters on
component teardown so a route change won’t leak phantom entries.
Built-in commands
Section titled “Built-in commands”AppShell.vue::registerBuiltins() registers the always-available set:
| Group | Command | Action |
|---|---|---|
| create | Create virtual machine… | Navigate /vms/new |
| create | Upload installation media | Navigate /iso-library?upload=1 |
| navigate | Go to Dashboard | Navigate / |
| navigate | Go to Virtual machines | Navigate /vms |
| navigate | Go to ISO library | Navigate /iso-library |
| theme | Switch to light theme | useTheme.setMode('light') |
| theme | Switch to dark theme | useTheme.setMode('dark') |
| theme | Follow system theme | useTheme.setMode('system') |
| session | Toggle sidebar (⌘/Ctrl B) |
useUiStore.toggleSidebar() |
| session | Sign out | (Phase 5 stub) |
Keyboard shortcuts
Section titled “Keyboard shortcuts”The global listener in useGlobalShortcuts.ts watches:
Cmd/Ctrl + K— toggle the palette.Cmd/Ctrl + B— toggle the sidebar.
Anything else falls through to whatever is focused. Inside the
palette, Combobox handles ↑/↓/⏎/Esc directly.