Navigation

Command Palette

Ctrl+K opens one palette on every page. It offers what the page in front of the reader can reach or do, by name, and the shortcuts dialog lists every key that works there. Both read one registry of commands, so a key the dialog lists always does something and the palette never offers what the page cannot do.

flowchart TD
  M[A surface mounts] --> REG[useCommands: its commands. useCommandScope: its search]
  U[It unmounts] --> UN[Its entries and their keys leave with it]
  REG --> S[The command store]
  REG -->|a command with a shortcut and something to do| HK[Its key, bound through Vuetify 0's hotkey composable]
  K[Ctrl+K or the dock's command button] --> P{Is a scope registered?}
  P -->|yes| SC[The palette searches the scope: the docs, the rooms, the resources]
  P -->|no| APP[The palette searches every offered command]
  SC -->|Backspace on an empty query| APP
  APP --> MS[MiniSearch over the registered titles]
  SC --> ST[The surface's own search stack, unchanged]
  MS --> RUN[A link followed or a command run]
  ST --> RUN
  Q[Shift+?] --> D[The shortcuts dialog: every command with a shortcut, grouped by surface]
  S --> D

How it works

  • A command is data. A UiCommand has a title, a group — the surface that registered it — an icon or a picture, and somewhere to go, something to run, or neither. One with somewhere to go is drawn as a real link, which a reader can open in a second tab. One with neither is a key its surface handles itself, such as the composer's Enter: the shortcuts dialog lists it, and nothing binds or offers it.
  • A surface registers for as long as it is mounted. useCommands adds a surface's commands to the store and binds each shortcut through Vuetify 0's hotkey composable until the surface unmounts, so the shortcuts dialog never lists a key that does nothing and the palette only offers what the page can do. A sequence, such as the resource explorer's G then A, is a shortcut like any other. The composable only warns on a shortcut it cannot parse and binds nothing, so a key its syntax reserves is spelled by its alias — slash for / — and useCommands.test.ts binds every shortcut written in source and fails on one that does not bind. A shortcut never fires while a field has focus, where its keys are typing. A command whose surface has a state it only applies in — a sheet's cell commands while cells are selected — is registered only while that state holds.
  • The app's own commands are the dock's. Home, every product, the reader's bookmarked and recent pages and the entries of the dock's theme, style and account menus are registered app-wide, and those menus read the same lists, so the two never disagree. A condition such as being signed in is part of the list, so it is re-read whenever the palette shows it. Choices among several — the design styles, the theme modes — are listed whole under their heading, the chosen one marked.
  • A surface's search is a scope, not a second palette. The docs, the room list and the resource explorer's home page keep their search stacks as the search standard describes, and hand the palette their query and what it finds through useCommandScope. The palette opens in the scope of the surface in front, and Backspace on an empty query steps out to the whole app. Ctrl+K keeps its meaning on their pages, and everything else is one keystroke further.
  • App-wide search is on the client. The titles are in memory, so the palette searches them with MiniSearch, the search standard's client branch, and keeps each group together under its heading.
  • The palette binds its own key, through useCommandPaletteShortcut, in a field too, since no typing holds Ctrl, and does not offer itself; its registry entry only lists the key.
  • Both are the library's dialogs. The palette and the shortcuts dialog open in the top layer through UiDialog, high on the screen so a list changing length never moves the field. The palette's list is UiCommandList, whose keyboard contract is the library's (keyboard contracts). A shortcut is drawn as UiShortcut's raised key caps.

The surfaces

SurfaceWhat it registers
The appHome, the products, the reader's places, the theme, style and account menus' entries, and the palette's and dialog's keys
The message composerIts own keys: sending, a new line, slash commands, mentions, editing the last message
The docsA scope over the docs' sections
The room listA scope over the reader's rooms, read a page at a time
The resource explorer's homeSearching resources, going to every resource and opening notifications as G chords, and a scope over resources, services and pages
A sheetIts cell commands, while cells are selected

Key files

FileRole
apps/web/app/models/ui/UiCommand.tsOne command: a title, a group, a mark, and somewhere to go or something to run
apps/web/app/models/ui/UiCommandScope.tsA surface's search, handed to the palette
apps/web/app/store/ui/command.tsThe registered commands and scopes, and whether the palette and the shortcuts dialog are open
apps/web/app/composables/ui/useCommands.tsRegisters a surface's commands and binds their shortcuts while it is mounted
apps/web/app/composables/ui/useCommandScope.tsHands a surface's search to the palette while it is mounted
apps/web/app/composables/ui/useCommandPaletteShortcut.tsThe palette's own key, bound in a field too
apps/web/app/components/App/CommandPalette.vueThe palette, and the app's own commands
apps/web/app/components/App/ShortcutsDialog.vueEvery registered shortcut, grouped by surface
apps/web/app/components/Ui/CommandList.vueThe search field over the commands it finds

Sources

  • Hotkey, Vuetify 0: the hotkey composable every shortcut binds through, sequences included.
  • MiniSearch: the client index the app-wide palette searches.

Details

Command palette

Keyboard shortcuts