Skip to content

zidane-ui/keybindings/headless

import { … } from 'zidane-ui/keybindings/headless'ui/src/keybindings/headless/index.ts

Functions#

bindingAllowedInFunction#

Whether an app-level binding may fire under scope. In the editor, ctrl+<letter> without shift belongs to the editor (movement and editing chords), and so do cmd+[ / cmd+] (indent). In the editor and text fields, an arrow with ⌘ or ⌥ alone (shift or not) moves the caret. Everything else with shift, meta or a non-letter key is an app intent (settings, panels, zoom).

TypeScript
function bindingAllowedIn(
  scope: KeyScope,
  spec: ParsedBinding,
  action?: string,
): boolean
Parameters
NameTypeDescription
scopeKeyScope

Who owns the press.

specParsedBinding

The binding.

action?string

The binding's action (closeTab fires everywhere).

boolean

ui/src/keybindings/headless/key-scope.ts:93

bindingAlternativesFunction#

The app's keyboard actions: the catalog (KEYBINDING_DEFS, one default spec, label and description per action) and the default map.

It shares the TUI's action names and spec grammar, and adds the desktop app's own actions. The grammar (parseBindingSpec, aliases, | alternatives) is core's, from zidane/chat/pure, so a keybindings.json parses the same on both surfaces. The user's overrides come from the host (KeybindingsHost.overrides); getMergedKeybindings layers them over DEFAULT_KEYBINDINGS. The DOM matcher is matchesBinding (zidane-ui/keybindings).

TypeScript
function bindingAlternatives(spec: string | null | undefined): string[]
Parameters
NameTypeDescription
specstring | null | undefined

Binding spec, possibly listing several chords separated by |.

string[]

dist/index-Bq87OcGy.d.ts:24915

createKeybindingOverridesFunction#

The overrides store over a read: what a host's overrides and refresh are.

TypeScript
function createKeybindingOverrides(
  read: () => Promise<unknown>,
): KeybindingOverridesState
Parameters
NameTypeDescription
read() => Promise<unknown>

Reads the stored map (rejects or answers nothing when there is none).

ui/src/keybindings/headless/overrides.ts:40

createKeyCommandRegistryFunction#

A registry with no parts: what the dispatcher reads until something registers.

TypeScript
function createKeyCommandRegistry(): KeyCommandRegistry

ui/src/keybindings/headless/commands.ts:37

filterOverridesFunction#

The known actions of a stored map, each with a non-empty spec. Entries a future build wrote are dropped, so the matcher never parses them.

TypeScript
function filterOverrides(raw: unknown): KeyOverrides
Parameters
NameTypeDescription
rawunknown

What was stored (anything).

ui/src/keybindings/headless/overrides.ts:15

getMergedKeybindingsFunction#

The bindings in effect: DEFAULT_KEYBINDINGS with the host's overrides on top.

TypeScript
function getMergedKeybindings(
  host?: { overrides: Readable<Readonly<Partial<Record<KeyAction, string>>>> } | null,
): KeyBindings
Parameters
NameTypeDescription
host?{ overrides: Readable<Readonly<Partial<Record<KeyAction, string>>>> } | null

The host whose overrides apply; none: the defaults.

ui/src/keybindings/headless/overrides.ts:62

isTypingFocusedFunction#

True when the focused element takes the user's typing, so moving focus would swallow keystrokes. Walks into shadow roots (the editor's host is document.activeElement while its inner content holds the caret).

TypeScript
function isTypingFocused(): boolean
boolean

ui/src/keybindings/headless/key-scope.ts:63

isTypingTargetFunction#

True when the key press belongs to a surface the user is typing in.

TypeScript
function isTypingTarget(event: Event): boolean
Parameters
NameTypeDescription
eventEvent

The key event.

boolean

ui/src/keybindings/headless/key-scope.ts:50

keyScopeOfFunction#

Who owns a key press, from its composed path.

TypeScript
function keyScopeOf(event: Event): KeyScope
Parameters
NameTypeDescription
eventEvent

The key event.

ui/src/keybindings/headless/key-scope.ts:29

parseBindingSpecFunction#

The app's keyboard actions: the catalog (KEYBINDING_DEFS, one default spec, label and description per action) and the default map.

It shares the TUI's action names and spec grammar, and adds the desktop app's own actions. The grammar (parseBindingSpec, aliases, | alternatives) is core's, from zidane/chat/pure, so a keybindings.json parses the same on both surfaces. The user's overrides come from the host (KeybindingsHost.overrides); getMergedKeybindings layers them over DEFAULT_KEYBINDINGS. The DOM matcher is matchesBinding (zidane-ui/keybindings).

TypeScript
function parseBindingSpec(spec: string | null | undefined): ParsedBinding | null
Parameters
NameTypeDescription
specstring | null | undefined

Binding spec, possibly listing several chords separated by |.

dist/index-Bq87OcGy.d.ts:24928

parseBindingSpecsFunction#

The app's keyboard actions: the catalog (KEYBINDING_DEFS, one default spec, label and description per action) and the default map.

It shares the TUI's action names and spec grammar, and adds the desktop app's own actions. The grammar (parseBindingSpec, aliases, | alternatives) is core's, from zidane/chat/pure, so a keybindings.json parses the same on both surfaces. The user's overrides come from the host (KeybindingsHost.overrides); getMergedKeybindings layers them over DEFAULT_KEYBINDINGS. The DOM matcher is matchesBinding (zidane-ui/keybindings).

TypeScript
function parseBindingSpecs(spec: string | null | undefined): ParsedBinding[]
Parameters
NameTypeDescription
specstring | null | undefined

Binding spec, possibly listing several chords separated by |.

dist/index-Bq87OcGy.d.ts:24921

Constants#

DEFAULT_KEYBINDINGSConstant#

The default spec per action.

TypeScript
const DEFAULT_KEYBINDINGS: KeyBindings

ui/src/keybindings/headless/catalog.ts:370

DEFAULT_KEYBINDINGS_LABELSConstant#

The default English strings of the keybindings components.

TypeScript
const DEFAULT_KEYBINDINGS_LABELS: { keyHintOr: string }

ui/src/keybindings/headless/labels.ts:8

KEYBINDING_DEF_BY_ACTIONConstant#

The catalog by action.

TypeScript
const KEYBINDING_DEF_BY_ACTION: Readonly<Record<KeyAction, KeyBindingDef>>

ui/src/keybindings/headless/catalog.ts:376

KEYBINDING_DEFSConstant#

The catalog, in display order.

TypeScript
const KEYBINDING_DEFS: readonly KeyBindingDef[]

ui/src/keybindings/headless/catalog.ts:75

KEYSCOPE_ATTRConstant#

The attribute an editor host sets (data-zg-keyscope="editor") to own its keys.

TypeScript
const KEYSCOPE_ATTR = 'data-zg-keyscope'

ui/src/keybindings/headless/key-scope.ts:20

Interfaces#

KeyBindingDefInterface#

One action of the catalog.

TypeScript
interface KeyBindingDef
KeyBindingDef properties
NameTypeDescription
actionKeyAction

The action.

defaultstring

Its default spec (cmd+k, ctrl+w | cmd+w); '' for unbound.

descriptionstring

What it does.

labelstring

Its short name.

urgent?true

Its state updates render before the next key. Without it they render as a transition (bindKeyListeners), so a shortcut that redraws much of the window ends its keydown at once and never holds up a frame or a keystroke. Set it on an action that moves the focus or opens what the next key goes to (a dialog, the palette, a terminal, a new chat's composer), since a key typed before that rendered would land where the focus was; and on a copy, which confirms at once.

ui/src/keybindings/headless/catalog.ts:55

KeybindingOverridesStateInterface#

A host's overrides store and the read that fills it.

TypeScript
interface KeybindingOverridesState
KeybindingOverridesState properties
NameTypeDescription
overridesReadable<Readonly<Partial<Record<KeyAction, string>>>>

The overrides: {} until read.

refresh() => Promise<void>

Reads them, keeps the known actions and announces them (also when unchanged). A failed read is none.

ui/src/keybindings/headless/overrides.ts:28

KeybindingsHostInterface#

The keybindings host.

TypeScript
interface KeybindingsHost
KeybindingsHost properties
NameTypeDescription
overridesReadable<Readonly<Partial<Record<KeyAction, string>>>>

The overrides: {} until read.

location?() => Promise<string>

Where the overrides live, to show (a path). Absent: not shown.

openInEditor?() => Promise<void>

Opens the file the overrides live in with the user's editor. Absent: no such file.

refresh() => Promise<void>

Reads the overrides again and tells subscribers. Never rejects: missing or unreadable reads as none.

save?(overrides: Readonly<Record<string, string>>) => Promise<void>

Replaces the overrides (action → spec), then reads them back. Absent: read-only.

ui/src/keybindings/headless/host.ts:18

KeyCommandRegistryInterface#

The merged handlers of every registered part.

TypeScript
interface KeyCommandRegistry extends Readable<KeyCommands>
KeyCommandRegistry properties
NameTypeDescription
actions() => string

The bound actions, in merge order (the first action matching a chord wins). One string per set: compare it.

getfrom Readable() => T

Returns the current value.

register(commands?: Partial<Record<KeyAction, () => void>>) => KeyCommandSlot

Adds a part. Parts merge in the order they registered: an action an earlier part binds keeps its place, a later part's handler for it wins.

subscribefrom Readable(onChange: () => void) => () => void

Registers a listener called after the value changes. Returns a function that removes the listener.

ui/src/keybindings/headless/commands.ts:24

KeyCommandSlotInterface#

One part's handlers in a KeyCommandRegistry.

TypeScript
interface KeyCommandSlot
KeyCommandSlot properties
NameTypeDescription
dispose() => void

Removes this part's handlers.

set(commands: KeyCommands) => void

Replaces this part's handlers.

ui/src/keybindings/headless/commands.ts:16

ParsedBindingInterface#

Parsed shape of a "ctrl+shift+o" spec.

TypeScript
interface ParsedBinding
ParsedBinding properties
NameTypeDescription
altboolean

Whether alt (or option) is held.

ctrlboolean

Whether ctrl is held.

metaboolean

Whether meta (cmd, super, or win) is held.

namestring

The key name (e.g. 'o', 'tab', 'escape'). Always lowercase.

shiftboolean

Whether shift is held.

dist/index-Bq87OcGy.d.ts:24890

Types#

KeyActionType#

Every action a key binding can fire.

TypeScript
type KeyAction =
  | 'openSettings'
  | 'openSessionDetails'
  | 'openModelPicker'
  | 'openEffortPicker'
  | 'openTodos'
  | 'openArtifacts'
  | 'openMainSession'
  | 'openUsagePanel'
  | 'newSession'
  | 'openKeybindings'
  | 'openPalette'
  | 'cancelToolCall'
  | 'cycleAgent'
  | 'enterSelectTurnMode'
  | 'toggleSidebar'
  | 'navigateBack'
  | 'navigateForward'
  | 'sessionGenerateTitle'
  | 'sessionCompact'
  | 'sessionExportMarkdown'
  | 'sessionExportJson'
  | 'sessionDelete'
  | 'sessionCopyId'
  | 'sessionFavorite'
  | 'zoomIn'
  | 'zoomOut'
  | 'zoomReset'
  | 'panelNext'
  | 'panelPrev'
  | 'tabNext'
  | 'tabPrev'
  | 'closeTab'
  | 'toggleTerminal'
  | 'newTerminal'
  | 'annotatePage'
  | 'splitTabRight'
  | 'splitTabDown'

ui/src/keybindings/headless/catalog.ts:15

KeyBindingsType#

A spec per action.

TypeScript
type KeyBindings = Record<KeyAction, string>

ui/src/keybindings/headless/catalog.ts:367

KeybindingsLabelsType#

The shape of the keybindings label dictionary.

TypeScript
type KeybindingsLabels = typeof DEFAULT_KEYBINDINGS_LABELS

ui/src/keybindings/headless/labels.ts:14

KeyCommandsType#

A handler per action.

TypeScript
type KeyCommands = Partial<Record<KeyAction, () => void>>

ui/src/keybindings/headless/commands.ts:13

KeyOverridesType#

The user's specs per action, over the defaults. Only known actions with a non-empty spec.

TypeScript
type KeyOverrides = Readonly<Partial<Record<KeyAction, string>>>

ui/src/keybindings/headless/host.ts:15

KeyScopeType#

Who owns a key press: the code editor, a text field, an open menu, or the app.

TypeScript
type KeyScope = 'editor' | 'input' | 'menu' | 'app'

ui/src/keybindings/headless/key-scope.ts:17