feat(desktop): rebindable keyboard shortcuts panel

Add a central keybind registry + nanostore so desktop hotkeys are
discoverable and user-rebindable. A titlebar ⌨ button (and ⌘/) opens a
collapsible map grouped by Composer (read-only) / Profiles / Session /
Navigation / View; click any chip to capture a new combo. Overrides
persist to localStorage as a delta against shipped defaults, so future
default changes aren't shadowed by a stored snapshot.

Migrates the previously scattered inline listeners (palette, command
center, new session, sidebar, theme) into the registry, and adds profile
switch/cycle/create + default-profile hotkeys.
This commit is contained in:
Brooklyn Nicholson
2026-06-06 11:41:57 -05:00
parent 1c2189839d
commit 5e2b83a8ad
17 changed files with 1124 additions and 95 deletions
+39
View File
@@ -1188,3 +1188,42 @@ canvas {
animation: none;
}
}
/* ── Keybind panel / edit overlay: small key chips ──────────────────────────
A quiet `kbd`-style chip shared by the shortcuts panel and the on-screen
editor so both read as the same control. No animation, no glow. */
.kbd-cap {
display: inline-grid;
place-items: center;
min-width: 1.5rem;
height: 1.4rem;
padding: 0 0.4rem;
border-radius: 0.375rem;
font-family: var(--dt-font-mono, ui-monospace, monospace);
font-size: 0.72rem;
font-weight: 500;
line-height: 1;
color: color-mix(in srgb, var(--dt-foreground) 82%, transparent);
background: color-mix(in srgb, var(--ui-bg-elevated) 70%, transparent);
border: 1px solid var(--ui-stroke-secondary);
box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--ui-stroke-tertiary) 50%, transparent);
}
/* Unbound slot: a hollow dashed chip inviting a binding. */
.kbd-cap--ghost {
color: color-mix(in srgb, var(--dt-foreground) 42%, transparent);
background: none;
border-style: dashed;
border-color: var(--ui-stroke-tertiary);
box-shadow: none;
font-style: italic;
}
/* Waiting for a keypress: solid accent, no motion. */
.kbd-capturing {
color: var(--theme-primary);
border-color: color-mix(in srgb, var(--theme-primary) 55%, var(--ui-stroke-secondary)) !important;
border-style: solid;
background: color-mix(in srgb, var(--theme-primary) 9%, var(--ui-bg-elevated));
box-shadow: none;
}