export type ShortcutActionId = | "executeSql" | "formatSql" | "saveSql" | "acceptCompletion" | "indentMore" | "indentLess" | "duplicateLine" | "deleteLine" | "moveLineUp" | "moveLineDown" | "copyLineUp" | "copyLineDown" | "undo" | "redo" | "selectAll" | "copyCurrentRow" | "deleteCurrentRow" | "newQuery" | "openSettings" | "closeTab" | "focusSearch" | "zoomInUi" | "zoomOutUi" | "resetUiZoom" | "find" | "replace" | "refreshData" | "toggleTranspose" | "cancelSearch" | "toggleSidebar"; 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: "formatSql", labelKey: "settings.shortcutFormatSql", scope: "editor", defaultShortcut: "Shift+Mod+F", }, { id: "saveSql", labelKey: "settings.shortcutSaveSql", scope: "editor", defaultShortcut: "Mod+S", }, { id: "acceptCompletion", labelKey: "settings.shortcutAcceptCompletion", scope: "editor", defaultShortcut: "Tab", }, { id: "indentMore", labelKey: "settings.shortcutIndentMore", scope: "editor", defaultShortcut: "", }, { id: "indentLess", labelKey: "settings.shortcutIndentLess", scope: "editor", defaultShortcut: "Shift+Tab", }, { id: "duplicateLine", labelKey: "settings.shortcutDuplicateLine", scope: "editor", defaultShortcut: "Mod+D", }, { id: "deleteLine", labelKey: "settings.shortcutDeleteLine", scope: "editor", defaultShortcut: "Shift+Mod+K", }, { id: "moveLineUp", labelKey: "settings.shortcutMoveLineUp", scope: "editor", defaultShortcut: "Alt+ArrowUp", }, { id: "moveLineDown", labelKey: "settings.shortcutMoveLineDown", scope: "editor", defaultShortcut: "Alt+ArrowDown", }, { id: "copyLineUp", labelKey: "settings.shortcutCopyLineUp", scope: "editor", defaultShortcut: "Shift+Alt+ArrowUp", }, { id: "copyLineDown", labelKey: "settings.shortcutCopyLineDown", scope: "editor", defaultShortcut: "Shift+Alt+ArrowDown", }, { id: "undo", labelKey: "settings.shortcutUndo", scope: "editor", defaultShortcut: "Mod+Z", }, { id: "redo", labelKey: "settings.shortcutRedo", scope: "editor", defaultShortcut: "Shift+Mod+Z", }, { id: "selectAll", labelKey: "settings.shortcutSelectAll", scope: "editor", defaultShortcut: "Mod+A", }, { 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", }, { id: "toggleSidebar", labelKey: "settings.shortcutToggleSidebar", scope: "global", defaultShortcut: "Mod+B", }, ]; 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] : 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 { if (!shortcut) return 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; }