From 22e8fece1ca2de0f329b7f995c52e957f0d65bbe Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 27 May 2026 10:50:54 +0800 Subject: [PATCH] feat: add query editor replace shortcut --- .../src/components/editor/QueryEditor.vue | 24 ++++++++++++++++++- apps/desktop/src/types/database.ts | 1 + .../queryEditorSearchReplace.test.ts | 18 ++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 packages/app-tests/queryEditorSearchReplace.test.ts diff --git a/apps/desktop/src/components/editor/QueryEditor.vue b/apps/desktop/src/components/editor/QueryEditor.vue index 952e402b0f..b6513ae2e6 100644 --- a/apps/desktop/src/components/editor/QueryEditor.vue +++ b/apps/desktop/src/components/editor/QueryEditor.vue @@ -148,6 +148,7 @@ const useRegex = ref(false); const matchCount = ref(0); const currentMatchIndex = ref(0); const searchInputRef = ref(); +const replaceInputRef = ref(); interface EditorGestureEvent extends Event { scale?: number; @@ -315,6 +316,16 @@ function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view") return true; }, }, + { + key: "Mod-h", + preventDefault: true, + run: openReplace, + }, + { + key: "Ctrl-h", + preventDefault: true, + run: openReplace, + }, { key: shortcutToCodeMirrorKey(shortcuts.executeSql), preventDefault: true, @@ -1365,6 +1376,16 @@ function openSearch(): boolean { return true; } +function openReplace(): boolean { + openSearch(); + showReplace.value = true; + nextTick(() => { + replaceInputRef.value?.focus(); + replaceInputRef.value?.select(); + }); + return true; +} + function closeSearch() { searchVisible.value = false; showReplace.value = false; @@ -1504,6 +1525,7 @@ defineExpose({ openSearch });
- 全部 + {{ t("editor.search.replaceAll") }}
diff --git a/apps/desktop/src/types/database.ts b/apps/desktop/src/types/database.ts index 0736e3c698..06ed39f5ea 100644 --- a/apps/desktop/src/types/database.ts +++ b/apps/desktop/src/types/database.ts @@ -371,6 +371,7 @@ export interface QueryTab { | "cte" | "set-operation" | "aggregation" + | "external-source" | "complex-source" | "computed-columns" | "no-table" diff --git a/packages/app-tests/queryEditorSearchReplace.test.ts b/packages/app-tests/queryEditorSearchReplace.test.ts new file mode 100644 index 0000000000..fec9a77551 --- /dev/null +++ b/packages/app-tests/queryEditorSearchReplace.test.ts @@ -0,0 +1,18 @@ +import { strict as assert } from "node:assert"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +const source = readFileSync("apps/desktop/src/components/editor/QueryEditor.vue", "utf8"); + +test("query editor opens replace panel with Mod-H", () => { + assert.match(source, /key:\s*"Mod-h"/); + assert.match(source, /key:\s*"Ctrl-h"/); + assert.match(source, /run:\s*openReplace/); + assert.match(source, /showReplace\.value\s*=\s*true/); + assert.match(source, /replaceInputRef\.value\?\.focus\(\)/); +}); + +test("query editor localizes the replace all button", () => { + assert.match(source, /t\("editor\.search\.replaceAll"\)/); + assert.doesNotMatch(source, />\s*全部\s*