diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index e953e07fce..9b5066a9c2 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -68,6 +68,7 @@ import { } from "@/lib/backend/api"; import { eventToShortcut } from "@/lib/editor/keyboardShortcuts"; import { SHORTCUT_DEFINITIONS, findShortcutConflict, normalizeShortcutSettings, type ShortcutActionId } from "@/lib/editor/shortcutRegistry"; +import { formatShortcutDisplay } from "@/lib/editor/shortcutDisplay"; import { normalizeSidebarHiddenTablePrefixes } from "@/lib/sidebar/sidebarTableNameDisplay"; import { normalizeSqlFormatterSettings, type SqlFormatterSettings } from "@/lib/sql/sqlFormatterConfig"; import { EMPTY_TABLE_COLUMN_TEMPLATE_DATA_TYPE, parseTableColumnTemplateFields, TABLE_COLUMN_TEMPLATE_DATABASE_TYPES } from "@/lib/table/tableColumnTemplates"; @@ -590,7 +591,26 @@ const shortcutConflicts = computed(() => return conflict ? [definition.id] : []; }), ); -const formatterEditorShortcutIds: ShortcutActionId[] = ["formatSql", "find", "replace", "saveSql", "acceptCompletion", "indentMore", "indentLess", "duplicateLine", "deleteLine", "moveLineUp", "moveLineDown", "copyLineUp", "copyLineDown", "undo", "redo", "selectAll"]; +const formatterEditorShortcutIds: ShortcutActionId[] = [ + "formatSql", + "find", + "replace", + "saveSql", + "acceptCompletion", + "indentMore", + "indentLess", + "duplicateLine", + "deleteLine", + "moveLineUp", + "moveLineDown", + "copyLineUp", + "copyLineDown", + "undo", + "redo", + "selectAll", + "uppercaseSelection", + "lowercaseSelection", +]; const formatterEditorShortcutDefinitions = computed(() => formatterEditorShortcutIds.map((id) => SHORTCUT_DEFINITIONS.find((definition) => definition.id === id)).filter((definition): definition is (typeof SHORTCUT_DEFINITIONS)[number] => !!definition)); const hasShortcutConflicts = computed(() => shortcutConflicts.value.length > 0); const shortcutsChanged = computed(() => JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts)); @@ -1082,29 +1102,7 @@ function onShortcutKeydown(actionId: ShortcutActionId, event: KeyboardEvent) { } function formatShortcutPill(shortcut: string): string { - if (!shortcut) return "—"; - const isMac = globalThis.navigator?.platform?.toLowerCase().includes("mac") ?? false; - return shortcut - .split("+") - .filter(Boolean) - .map((part) => { - if (part === "Mod") return isMac ? "⌘" : "Ctrl"; - if (part === "Meta") return isMac ? "⌘" : "Meta"; - if (part === "Shift") return isMac ? "⇧" : "Shift"; - if (part === "Alt") return isMac ? "⌥" : "Alt"; - if (part === "Control" || part === "Ctrl") return isMac ? "⌃" : "Ctrl"; - if (part === "Enter") return "↵"; - if (part === "Backspace") return "⌫"; - if (part === "Delete") return isMac ? "⌦" : "Del"; - if (part === "Escape") return "Esc"; - if (part === "ArrowUp") return "↑"; - if (part === "ArrowDown") return "↓"; - if (part === "ArrowLeft") return "←"; - if (part === "ArrowRight") return "→"; - if (part === " ") return "Space"; - return part.length === 1 ? part.toUpperCase() : part; - }) - .join(isMac ? " " : " + "); + return formatShortcutDisplay(shortcut); } const shortcutPressShortcutLabel = computed(() => t("settings.shortcutPressShortcut")); diff --git a/apps/desktop/src/components/editor/QueryEditor.vue b/apps/desktop/src/components/editor/QueryEditor.vue index 17db614032..4a77acbc5a 100644 --- a/apps/desktop/src/components/editor/QueryEditor.vue +++ b/apps/desktop/src/components/editor/QueryEditor.vue @@ -566,10 +566,10 @@ function selectAllSqlFromContextMenu() { focusEditor(); } -function convertSelectedSqlCaseFromContextMenu(mode: SelectionCaseMode) { +function convertSelectedSqlCase(mode: SelectionCaseMode): boolean { const currentView = view.value; const EditorSelection = codeMirrorEditorSelection; - if (!currentView || !EditorSelection || !canCopySelectedSql.value) return; + if (!currentView || !EditorSelection) return false; const state = currentView.state; const transaction = state.changeByRange((range) => { @@ -585,8 +585,10 @@ function convertSelectedSqlCaseFromContextMenu(mode: SelectionCaseMode) { if (!transaction.changes.empty) { currentView.dispatch({ ...transaction, scrollIntoView: true, userEvent: "input" }); + focusEditor(); + return true; } - focusEditor(); + return false; } function openTableFromContextMenu() { @@ -637,52 +639,59 @@ function selectSqlLineFromGutter(currentView: EditorViewType, line: { from: numb return true; } -const contextMenuItems = computed(() => [ - { - label: executeContextMenuLabel.value, - action: executeFromContextMenu, - disabled: !canExecuteContextSql.value, - icon: Play, - }, - { - label: t("contextMenu.viewData"), - action: openTableFromContextMenu, - disabled: !contextTableName.value, - icon: Table2, - }, - { - label: t("contextMenu.editStructure"), - action: editTableStructureFromContextMenu, - disabled: !contextTableName.value, - icon: PencilRuler, - }, - { - label: t("contextMenu.viewDdl"), - action: openTableDdlFromContextMenu, - disabled: !contextTableName.value, - icon: FileCode, - }, - { label: "", separator: true }, - { - label: t("editor.contextMenu.copySelection"), - action: copySelectedSqlFromContextMenu, - disabled: !canCopySelectedSql.value, - icon: Copy, - }, - { - label: t("editor.contextMenu.uppercaseSelection"), - action: () => convertSelectedSqlCaseFromContextMenu("upper"), - disabled: !canCopySelectedSql.value, - icon: CaseUpper, - }, - { - label: t("editor.contextMenu.lowercaseSelection"), - action: () => convertSelectedSqlCaseFromContextMenu("lower"), - disabled: !canCopySelectedSql.value, - icon: CaseLower, - }, - { label: t("editor.contextMenu.selectAll"), action: selectAllSqlFromContextMenu, icon: TextSelect }, -]); +const contextMenuItems = computed(() => { + const shortcuts = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts); + return [ + { + label: executeContextMenuLabel.value, + action: executeFromContextMenu, + disabled: !canExecuteContextSql.value, + icon: Play, + shortcut: shortcuts.executeSql, + }, + { + label: t("contextMenu.viewData"), + action: openTableFromContextMenu, + disabled: !contextTableName.value, + icon: Table2, + }, + { + label: t("contextMenu.editStructure"), + action: editTableStructureFromContextMenu, + disabled: !contextTableName.value, + icon: PencilRuler, + }, + { + label: t("contextMenu.viewDdl"), + action: openTableDdlFromContextMenu, + disabled: !contextTableName.value, + icon: FileCode, + }, + { label: "", separator: true }, + { + label: t("editor.contextMenu.copySelection"), + action: copySelectedSqlFromContextMenu, + disabled: !canCopySelectedSql.value, + icon: Copy, + shortcut: "Mod+C", + }, + { + label: t("editor.contextMenu.uppercaseSelection"), + action: () => convertSelectedSqlCase("upper"), + disabled: !canCopySelectedSql.value, + icon: CaseUpper, + shortcut: shortcuts.uppercaseSelection, + }, + { + label: t("editor.contextMenu.lowercaseSelection"), + action: () => convertSelectedSqlCase("lower"), + disabled: !canCopySelectedSql.value, + icon: CaseLower, + shortcut: shortcuts.lowercaseSelection, + }, + { label: t("editor.contextMenu.selectAll"), action: selectAllSqlFromContextMenu, icon: TextSelect, shortcut: shortcuts.selectAll }, + ]; +}); function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view"))["keymap"]) { const shortcuts = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts); @@ -718,6 +727,8 @@ function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view") ...binding(shortcuts.undo, (view) => codeMirrorUndo?.(view) ?? false), ...binding(shortcuts.redo, (view) => codeMirrorRedo?.(view) ?? false), ...binding(shortcuts.selectAll, (view) => codeMirrorSelectAll?.(view) ?? false), + ...binding(shortcuts.uppercaseSelection, () => convertSelectedSqlCase("upper")), + ...binding(shortcuts.lowercaseSelection, () => convertSelectedSqlCase("lower")), ]), ) ?? [], codeMirrorKeymap.of( diff --git a/apps/desktop/src/components/sidebar/TreeItem.vue b/apps/desktop/src/components/sidebar/TreeItem.vue index 80528ba9cb..1e8a9aa15d 100644 --- a/apps/desktop/src/components/sidebar/TreeItem.vue +++ b/apps/desktop/src/components/sidebar/TreeItem.vue @@ -131,7 +131,6 @@ import { useExportTracker, type ExportTask } from "@/composables/useExportTracke import { isTauriRuntime } from "@/lib/backend/tauriRuntime"; import { copyToClipboard } from "@/lib/common/clipboard"; import { hasEnabledTransportLayers } from "@/lib/backend/connectionTransport"; -import { formatShortcut } from "@/lib/editor/shortcutRegistry"; import { isWindows } from "@/lib/backend/platform"; import { rankSavedSqlHistory, type SavedSqlHistoryScope } from "@/lib/savedSql/savedSqlHistory"; import { isSqlServerLinkedNode } from "@/lib/database/sqlServerLinkedServers"; @@ -4038,8 +4037,8 @@ onBeforeUnmount(() => { // ---- CustomContextMenu ---- -const shortcutCopyName = computed(() => formatShortcut(settingsStore.editorSettings.shortcuts.copySidebarSelection)); -const shortcutEditConnection = computed(() => formatShortcut(settingsStore.editorSettings.shortcuts.editSidebarConnection)); +const shortcutCopyName = computed(() => settingsStore.editorSettings.shortcuts.copySidebarSelection); +const shortcutEditConnection = computed(() => settingsStore.editorSettings.shortcuts.editSidebarConnection); const shortcutRename = "F2"; const shortcutRefresh = "F5"; const shortcutDelete = "Delete"; diff --git a/apps/desktop/src/components/ui/CustomContextMenu.vue b/apps/desktop/src/components/ui/CustomContextMenu.vue index 6cb342a414..9c2672d661 100644 --- a/apps/desktop/src/components/ui/CustomContextMenu.vue +++ b/apps/desktop/src/components/ui/CustomContextMenu.vue @@ -1,6 +1,7 @@