import type { AppTranslationKey } from '@/lib/i18n'; /* Every keyboard shortcut the shell owns, as data. ── Why a registry ── Ported in shape from the owner's `vimazing` (`vim-maze/src/gameInfo.ts`), where the controls are declared once and the help screen renders them. The alternative is what this replaced: chords spelled out inside three separate key handlers, and a help text nobody updates because it lives somewhere else. Two things follow from having it. `Ctrl+?` can render an accurate list without anybody maintaining prose. And when shortcuts become configurable — the next big piece — this is the table a binding UI edits, rather than a grep across handlers. ── What it is NOT, yet ── Declarative but not authoritative: the handlers still contain their own conditions, so a chord changed here does not move. Making this the single source is part of the configurable-shortcuts work, and doing it now would mean a rewrite of three handlers for no visible gain. Where a chord appears in both places the file that owns it is named in `implementedIn`, so the pair can be found in one search. */ export type Shortcut = { /** As a person reads it — `Ctrl+Shift+Space`, not a KeyboardEvent spelling. */ keys: string; /** A catalog key. This is data with no component around it, so ShortcutsModal resolves it. */ descriptionKey: AppTranslationKey; /** The file whose handler actually implements it, until this registry is authoritative. */ implementedIn: string; }; export type ShortcutSection = { titleKey: AppTranslationKey; /** Shown under the title when the section needs a caveat that a per-row description cannot carry. */ noteKey?: AppTranslationKey; shortcuts: Shortcut[]; }; export const SHORTCUT_SECTIONS: ShortcutSection[] = [ { titleKey: 'shortcuts.navigation', shortcuts: [ { keys: 'Ctrl+Space', descriptionKey: 'shortcuts.spotlight', implementedIn: 'Layout/Spotlight/Spotlight.tsx', }, { keys: 'Ctrl+?', descriptionKey: 'shortcuts.thisList', implementedIn: 'Layout/ShortcutsModal.tsx' }, ], }, { titleKey: 'shortcuts.themes', noteKey: 'shortcuts.themesNote', shortcuts: [ { keys: 'Ctrl+Shift+↑ / ↓', descriptionKey: 'shortcuts.openBrowser', implementedIn: 'theme/useThemeKeys.ts', }, { keys: 'Ctrl+Shift+← / →', descriptionKey: 'shortcuts.prevNextTheme', implementedIn: 'theme/useThemeKeys.ts', }, { keys: 'Ctrl+Shift+F', descriptionKey: 'shortcuts.starCurrent', implementedIn: 'theme/useThemeKeys.ts', }, ], }, { titleKey: 'shortcuts.inBrowser', shortcuts: [ { keys: 'j / k ↑ / ↓', descriptionKey: 'shortcuts.moveList', implementedIn: 'theme/useThemeKeys.ts' }, { keys: 'l →', descriptionKey: 'shortcuts.applyKeepBrowsing', implementedIn: 'theme/useThemeKeys.ts', }, { keys: 'h ←', descriptionKey: 'shortcuts.restoreStart', implementedIn: 'theme/useThemeKeys.ts' }, { keys: 'f', descriptionKey: 'shortcuts.starFocused', implementedIn: 'theme/useThemeKeys.ts' }, { keys: 'Enter', descriptionKey: 'shortcuts.keepLooking', implementedIn: 'theme/useThemeKeys.ts' }, { keys: 'Esc', descriptionKey: 'shortcuts.cancelRestore', implementedIn: 'theme/useThemeKeys.ts' }, ], }, { titleKey: 'shortcuts.inSpotlight', shortcuts: [ { keys: '↑ / ↓', descriptionKey: 'shortcuts.moveResults', implementedIn: 'Layout/Spotlight/Spotlight.tsx' }, { keys: 'Enter', descriptionKey: 'shortcuts.goThere', implementedIn: 'Layout/Spotlight/Spotlight.tsx' }, { keys: 'Esc', descriptionKey: 'shortcuts.close', implementedIn: 'Layout/Spotlight/Spotlight.tsx' }, ], }, { titleKey: 'shortcuts.chat', noteKey: 'shortcuts.chatNote', shortcuts: [ { keys: '↑ / ↓', descriptionKey: 'shortcuts.prevNextPrompt', implementedIn: 'Chat/EmbeddableChat/useEmbeddableChat.ts', }, { keys: 'Enter', descriptionKey: 'shortcuts.send', implementedIn: 'Chat/EmbeddableChat/useEmbeddableChat.ts', }, { keys: 'Shift+Enter', descriptionKey: 'shortcuts.newline', implementedIn: 'Chat/EmbeddableChat/useEmbeddableChat.ts', }, { keys: 'Esc', descriptionKey: 'shortcuts.stopTurn', implementedIn: 'Chat/EmbeddableChat/useEmbeddableChat.ts', }, ], }, ]; /** * Is this the `Ctrl+?` chord? * * `?` is `Shift+/` on most layouts and its own key on some, so both spellings are accepted rather than * guessing which keyboard is attached. `code === 'Slash'` is the physical key, which is stable where `key` * is not. */ export function isShortcutsChord(ev: KeyboardEvent): boolean { if (!ev.ctrlKey) return false; return ev.key === '?' || (ev.shiftKey && ev.code === 'Slash'); }