diff --git a/src/App.vue b/src/App.vue index c9445c4a7f..6f753a6212 100644 --- a/src/App.vue +++ b/src/App.vue @@ -35,7 +35,12 @@ import * as api from "@/lib/api"; import { resolveDefaultDatabase } from "@/lib/defaultDatabase"; import { resolveExecutableSql } from "@/lib/sqlExecutionTarget"; import { isTauriRuntime } from "@/lib/tauriRuntime"; -import { isCloseTabShortcut, isExecuteSqlShortcut, isFocusSearchShortcut } from "@/lib/keyboardShortcuts"; +import { + isCloseTabShortcut, + isExecuteSqlShortcut, + isFocusSearchShortcut, + isSaveShortcut, +} from "@/lib/keyboardShortcuts"; import { isPreviewTab } from "@/lib/tabPresentation"; import { supportsSqlFileExecution } from "@/lib/databaseCapabilities"; import { classifyAiSqlExecution } from "@/lib/aiSqlExecutionPolicy"; @@ -432,14 +437,7 @@ function handleKeydown(e: KeyboardEvent) { } return; } - if ( - activeTab.value?.mode === "query" && - !showSaveSqlDialog.value && - !e.isComposing && - !e.altKey && - (e.metaKey || e.ctrlKey) && - e.key.toLowerCase() === "s" - ) { + if (activeTab.value?.mode === "query" && !showSaveSqlDialog.value && isSaveShortcut(e)) { e.preventDefault(); e.stopPropagation(); void openSaveSqlDialog(); @@ -669,6 +667,7 @@ onUnmounted(() => { @editor-selection-change="(v: string) => (selectedSql = v)" @editor-cursor-change="(p: number) => (cursorPos = p)" @format-error="toast(t('toolbar.formatSqlFailed'))" + @save-sql="void openSaveSqlDialog()" @reload=" ( sql?: string, diff --git a/src/components/editor/QueryEditor.vue b/src/components/editor/QueryEditor.vue index 970a3853bc..12707cff3a 100644 --- a/src/components/editor/QueryEditor.vue +++ b/src/components/editor/QueryEditor.vue @@ -28,6 +28,7 @@ const emit = defineEmits<{ cursorChange: [pos: number]; formatError: [message: string]; execute: [sql: string]; + save: []; clickTable: [tableName: string]; clickColumn: [columns: Array<{ name: string; table: string; schema?: string }>, error?: string | undefined]; closeColumnPanel: []; @@ -319,6 +320,13 @@ onMounted(async () => { return true; }, }, + { + key: "Mod-s", + run: () => { + emit("save"); + return true; + }, + }, ]); const theme = await loadEditorTheme(ss.theme); diff --git a/src/components/layout/ContentArea.vue b/src/components/layout/ContentArea.vue index 44b5cf70ff..0803e3babb 100644 --- a/src/components/layout/ContentArea.vue +++ b/src/components/layout/ContentArea.vue @@ -35,6 +35,7 @@ const emit = defineEmits<{ "update:activeOutputView": [value: "result" | "explain" | "chart"]; fixWithAi: [errorMessage: string]; execute: [sqlOverride?: string]; + saveSql: []; cancel: []; explain: []; editorUpdate: [value: string]; @@ -194,6 +195,7 @@ defineExpose({ focusSearch }); @cursor-change="emit('editorCursorChange', $event)" @format-error="emit('formatError')" @execute="emit('execute')" + @save="emit('saveSql')" @click-table="onHandleClickTable" @click-column="onHandleClickColumn" @close-column-panel="onHandleCloseColumnPanel" diff --git a/src/components/objects/ObjectBrowser.vue b/src/components/objects/ObjectBrowser.vue index 9e6a0ac2fe..10385f11c0 100644 --- a/src/components/objects/ObjectBrowser.vue +++ b/src/components/objects/ObjectBrowser.vue @@ -648,7 +648,7 @@ watch(