export type ShortcutActionId = | "executeSql" | "saveSql" | "acceptCompletion" | "copyCurrentRow" | "deleteCurrentRow" | "newQuery" | "openSettings" | "closeTab" | "focusSearch" | "zoomInUi" | "zoomOutUi" | "resetUiZoom" | "find" | "replace" | "refreshData" | "toggleTranspose" | "cancelSearch"; export type ShortcutScope = "global" | "editor" | "grid" | "search"; export interface ShortcutDefinition { id: ShortcutActionId; labelKey: string; scope: ShortcutScope; defaultShortcut: string; } export type ShortcutSettings = Record; export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [ { id: "executeSql", labelKey: "settings.shortcutExecuteSql", scope: "editor", defaultShortcut: "Mod+Enter", }, { id: "saveSql", labelKey: "settings.shortcutSaveSql", scope: "editor", defaultShortcut: "Mod+S", }, { id: "acceptCompletion", labelKey: "settings.shortcutAcceptCompletion", scope: "editor", defaultShortcut: "Tab", }, { id: "copyCurrentRow", labelKey: "settings.shortcutCopyCurrentRow", scope: "grid", defaultShortcut: "Mod+D", }, { id: "deleteCurrentRow", labelKey: "settings.shortcutDeleteCurrentRow", scope: "grid", defaultShortcut: "Delete", }, { id: "newQuery", labelKey: "settings.shortcutNewQuery", scope: "global", defaultShortcut: "Mod+T", }, { id: "openSettings", labelKey: "settings.shortcutOpenSettings", scope: "global", defaultShortcut: "Mod+,", }, { id: "closeTab", labelKey: "settings.shortcutCloseTab", scope: "global", defaultShortcut: "Meta+W", }, { id: "focusSearch", labelKey: "settings.shortcutFocusSearch", scope: "global", defaultShortcut: "Mod+F", }, { id: "zoomInUi", labelKey: "settings.shortcutZoomInUi", scope: "global", defaultShortcut: "Mod+=", }, { id: "zoomOutUi", labelKey: "settings.shortcutZoomOutUi", scope: "global", defaultShortcut: "Mod+-", }, { id: "resetUiZoom", labelKey: "settings.shortcutResetUiZoom", scope: "global", defaultShortcut: "Mod+0", }, { id: "find", labelKey: "settings.shortcutFind", scope: "editor", defaultShortcut: "Mod+F", }, { id: "replace", labelKey: "settings.shortcutReplace", scope: "editor", defaultShortcut: "Mod+R", }, { id: "refreshData", labelKey: "settings.shortcutRefreshData", scope: "global", defaultShortcut: "F5", }, { id: "toggleTranspose", labelKey: "settings.shortcutToggleTranspose", scope: "grid", defaultShortcut: "Tab", }, { id: "cancelSearch", labelKey: "settings.shortcutCancelSearch", scope: "search", defaultShortcut: "Escape", }, ]; export const DEFAULT_SHORTCUT_SETTINGS: ShortcutSettings = Object.fromEntries( SHORTCUT_DEFINITIONS.map((definition) => [definition.id, definition.defaultShortcut]), ) as ShortcutSettings; export function normalizeShortcutSettings(settings?: Partial): ShortcutSettings { return Object.fromEntries( SHORTCUT_DEFINITIONS.map((definition) => [ definition.id, typeof settings?.[definition.id] === "string" && settings[definition.id]?.trim() ? settings[definition.id] : definition.defaultShortcut, ]), ) as ShortcutSettings; } export function shortcutToCodeMirrorKey(shortcut: string): string { return shortcut .split("+") .map((part) => (part.length === 1 ? part.toLowerCase() : part)) .join("-"); } export function formatShortcut(shortcut: string, platform = globalThis.navigator?.platform || ""): string { const isMac = platform.toLowerCase().includes("mac"); return shortcut .split("+") .map((part) => { if (part === "Mod") return isMac ? "Cmd" : "Ctrl"; if (part === "Meta") return isMac ? "Cmd" : "Meta"; return part; }) .join("+"); } export function findShortcutConflict( actionId: ShortcutActionId, shortcut: string, shortcuts: ShortcutSettings, ): ShortcutActionId | null { const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === actionId); if (!definition) return null; const conflict = SHORTCUT_DEFINITIONS.find( (item) => item.id !== actionId && item.scope === definition.scope && shortcuts[item.id] === shortcut, ); return conflict?.id ?? null; }