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
+2 -24
View File
@@ -289,30 +289,8 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
window.localStorage.setItem(MODE_KEY, next)
}, [])
// Shift+X toggles light/dark anywhere outside an editable field.
useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => {
const t = event.target as HTMLElement | null
const editing =
t?.isContentEditable ||
t instanceof HTMLInputElement ||
t instanceof HTMLTextAreaElement ||
t instanceof HTMLSelectElement
if (editing || event.repeat || event.altKey || event.ctrlKey || event.metaKey) {
return
}
if (event.shiftKey && event.code === 'KeyX') {
setMode(resolvedMode === 'dark' ? 'light' : 'dark')
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
}, [resolvedMode, setMode])
// The light/dark toggle (Shift+X by default) is owned by the keybind runtime
// (`appearance.toggleMode`) so it shows up in the hotkey map and is rebindable.
const value = useMemo<ThemeContextValue>(
() => ({ theme: activeTheme, themeName, mode, resolvedMode, availableThemes: SKIN_LIST, setTheme, setMode }),