diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 62fe0d83bd..12db0efada 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -117,6 +117,7 @@ import { isBrowserTaskManagerShortcut, isCloseOtherTabsShortcut, isCloseTabShortcut, + isCloseWindowShortcut, isDisconnectAllActiveConnectionsShortcut, isEditTableStructureShortcut, isExecuteSqlInNewResultTabShortcut, @@ -4307,6 +4308,18 @@ async function handleKeydown(e: KeyboardEvent) { void appSidebarRef.value?.disconnectAllActiveConnections(); return; } + if (isCloseWindowShortcut(e, shortcuts)) { + e.preventDefault(); + e.stopPropagation(); + if (isDetachedWindowContext) { + void requestDetachedReturn("close"); + return; + } + if (isDesktop) { + void api.requestAppClose(); + } + return; + } if (isCloseTabShortcut(e, shortcuts)) { e.preventDefault(); await closeActiveSurface(); diff --git a/apps/desktop/src/i18n/locales/az.ts b/apps/desktop/src/i18n/locales/az.ts index 98224b8fb4..c06572dc3d 100644 --- a/apps/desktop/src/i18n/locales/az.ts +++ b/apps/desktop/src/i18n/locales/az.ts @@ -7872,6 +7872,7 @@ export default withEnglishFallback({ shortcutNewQuery: "Yeni sorğu", shortcutOpenSettings: "Parametrləri aç", shortcutCloseTab: "Vərəqi bağla", + shortcutCloseWindow: "Pəncərəni bağla", shortcutFocusSearch: "Cari görünüşün axtarış sahəsinə keç", shortcutQuickOpen: "Sürətli açılış (bütün verilənlər bazası obyektlərini axtar)", shortcutGlobalSearch: "Qlobal fayl məzmunu axtarışı", diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 329ea9e550..b9d313f8e4 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -8935,6 +8935,7 @@ export default { shortcutNewQuery: "New query", shortcutOpenSettings: "Open settings", shortcutCloseTab: "Close tab", + shortcutCloseWindow: "Close window", shortcutFocusSearch: "Focus current view search", shortcutQuickOpen: "Quick open (search all database objects)", shortcutGlobalSearch: "Global file content search", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index 3e35495512..5c72ed521e 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -8170,6 +8170,7 @@ export default withEnglishFallback({ shortcutNewQuery: "Nueva consulta", shortcutOpenSettings: "Abrir configuración", shortcutCloseTab: "Cerrar pestaña", + shortcutCloseWindow: "Cerrar ventana", shortcutToggleSidebar: "Alternar barra lateral", shortcutToggleZenMode: "Alternar modo Zen", shortcutFocusSearch: "Enfocar búsqueda de la vista actual", diff --git a/apps/desktop/src/i18n/locales/id.ts b/apps/desktop/src/i18n/locales/id.ts index 8817941298..0fe7bb44bc 100644 --- a/apps/desktop/src/i18n/locales/id.ts +++ b/apps/desktop/src/i18n/locales/id.ts @@ -8662,6 +8662,7 @@ export default withEnglishFallback({ shortcutNewQuery: "Kueri baru", shortcutOpenSettings: "Buka pengaturan", shortcutCloseTab: "Tutup tab", + shortcutCloseWindow: "Tutup jendela", shortcutFocusSearch: "Fokus pencarian tampilan saat ini", shortcutQuickOpen: "Buka cepat (cari semua objek database)", shortcutGlobalSearch: "Pencarian konten file global", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 3262a201bb..59e05edebc 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -7932,6 +7932,7 @@ export default withEnglishFallback({ shortcutNewQuery: "Nuova query", shortcutOpenSettings: "Apri impostazioni", shortcutCloseTab: "Chiudi scheda", + shortcutCloseWindow: "Chiudi finestra", shortcutFocusSearch: "Focalizza la ricerca nella vista corrente", shortcutQuickOpen: "Apertura rapida (ricerca tutti gli oggetti del database)", shortcutGlobalSearch: "Ricerca globale nel contenuto dei file", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index 91fe5145ab..eea6220ddf 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -7926,6 +7926,7 @@ export default withEnglishFallback({ shortcutNewQuery: "新しいクエリ", shortcutOpenSettings: "設定を開く", shortcutCloseTab: "タブを閉じる", + shortcutCloseWindow: "ウィンドウを閉じる", shortcutFocusSearch: "現在のビューの検索にフォーカス", shortcutQuickOpen: "クイックオープン (すべてのデータベースオブジェクトを検索)", shortcutGlobalSearch: "グローバルファイル内容検索", diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts index a25450b7b9..45d2e3e050 100644 --- a/apps/desktop/src/i18n/locales/ko.ts +++ b/apps/desktop/src/i18n/locales/ko.ts @@ -7784,6 +7784,7 @@ export default withEnglishFallback({ shortcutNewQuery: "새 쿼리", shortcutOpenSettings: "설정 열기", shortcutCloseTab: "탭 닫기", + shortcutCloseWindow: "창 닫기", shortcutFocusSearch: "현재 화면 검색에 포커스", shortcutQuickOpen: "빠른 열기 (모든 데이터베이스 객체 검색)", shortcutGlobalSearch: "전역 파일 내용 검색", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index e96b84a81b..87c5ba8607 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -7932,6 +7932,7 @@ export default withEnglishFallback({ shortcutNewQuery: "Nova consulta", shortcutOpenSettings: "Abrir configurações", shortcutCloseTab: "Fechar aba", + shortcutCloseWindow: "Fechar janela", shortcutToggleSidebar: "Alternar barra lateral", shortcutToggleZenMode: "Alternar modo Zen", shortcutFocusSearch: "Focar na pesquisa da tela atual", diff --git a/apps/desktop/src/i18n/locales/ru.ts b/apps/desktop/src/i18n/locales/ru.ts index 9ff7de7ff2..709a0a153b 100644 --- a/apps/desktop/src/i18n/locales/ru.ts +++ b/apps/desktop/src/i18n/locales/ru.ts @@ -9225,6 +9225,7 @@ export default withEnglishFallback({ shortcutNewQuery: "Новый запрос", shortcutOpenSettings: "Открыть настройки", shortcutCloseTab: "Закрыть вкладку", + shortcutCloseWindow: "Закрыть окно", shortcutFocusSearch: "Фокус на поиске в текущем представлении", shortcutQuickOpen: "Быстрое открытие (поиск по всем объектам базы данных)", shortcutGlobalSearch: "Глобальный поиск по содержимому файлов", diff --git a/apps/desktop/src/i18n/locales/tr.ts b/apps/desktop/src/i18n/locales/tr.ts index c092cdec96..1f74f0f73a 100644 --- a/apps/desktop/src/i18n/locales/tr.ts +++ b/apps/desktop/src/i18n/locales/tr.ts @@ -7773,6 +7773,7 @@ export default withEnglishFallback({ shortcutNewQuery: "Yeni sorgu", shortcutOpenSettings: "Ayarları aç", shortcutCloseTab: "Sekmeyi kapat", + shortcutCloseWindow: "Pencereyi kapat", shortcutFocusSearch: "Geçerli görünüm aramasına odaklan", shortcutQuickOpen: "Hızlı aç (tüm veritabanı nesnelerinde ara)", shortcutGlobalSearch: "Genel dosya içeriği arama", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 5f70c26353..9133232da2 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -8922,6 +8922,7 @@ export default withEnglishFallback({ shortcutNewQuery: "新建查询", shortcutOpenSettings: "打开设置", shortcutCloseTab: "关闭标签页", + shortcutCloseWindow: "关闭窗口", shortcutToggleSidebar: "切换侧边栏", shortcutToggleZenMode: "切换禅模式", shortcutFocusSearch: "聚焦当前页面搜索", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index b11871a599..5204c8bf67 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -7251,6 +7251,7 @@ export default withEnglishFallback({ shortcutNewQuery: "建立查詢", shortcutOpenSettings: "開啟設定", shortcutCloseTab: "關閉分頁", + shortcutCloseWindow: "關閉視窗", shortcutToggleSidebar: "切換側邊欄", shortcutToggleZenMode: "切換禪模式", shortcutFocusSearch: "聚焦目前頁面搜尋", diff --git a/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts b/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts index d4aceea8be..71abcdfe4a 100644 --- a/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts +++ b/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts @@ -275,6 +275,21 @@ describe("shortcutRegistry editor actions", () => { expect(normalizeShortcutSettings({ closeTab: "" }).closeTab).toBe(""); }); + it("registers closeWindow as a global shortcut with Shift+Mod+W default", () => { + const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === "closeWindow"); + expect(definition).toMatchObject({ + id: "closeWindow", + labelKey: "settings.shortcutCloseWindow", + scope: "global", + defaultShortcut: "Shift+Mod+W", + }); + expect(DEFAULT_SHORTCUT_SETTINGS.closeWindow).toBe("Shift+Mod+W"); + expect(formatShortcut(DEFAULT_SHORTCUT_SETTINGS.closeWindow, "Win32")).toBe("Ctrl+Shift+W"); + expect(formatShortcut(DEFAULT_SHORTCUT_SETTINGS.closeWindow, "MacIntel")).toBe("Shift+Cmd+W"); + expect(normalizeShortcutSettings({ closeWindow: "Mod+Alt+W" }).closeWindow).toBe("Mod+Alt+W"); + expect(normalizeShortcutSettings({ closeWindow: "" }).closeWindow).toBe(""); + }); + it("normalizes custom, cleared, and invalid modifier-only shortcuts", () => { expect(normalizeShortcutSettings({ openDataInNewTab: "Shift" }).openDataInNewTab).toBe("Shift"); expect(normalizeShortcutSettings({ openDataInNewTab: "" }).openDataInNewTab).toBe(""); diff --git a/apps/desktop/src/lib/editor/keyboardShortcuts.ts b/apps/desktop/src/lib/editor/keyboardShortcuts.ts index c63a7075d5..6a6f5b0224 100644 --- a/apps/desktop/src/lib/editor/keyboardShortcuts.ts +++ b/apps/desktop/src/lib/editor/keyboardShortcuts.ts @@ -188,6 +188,10 @@ export function isCloseOtherTabsShortcut(event: ShortcutLikeEvent, shortcuts?: P return matchesShortcut(event, actionShortcut("closeOtherTabs", shortcuts), platform); } +export function isCloseWindowShortcut(event: ShortcutLikeEvent, shortcuts?: Partial): boolean { + return matchesShortcut(event, actionShortcut("closeWindow", shortcuts)); +} + export function isSendSelectionToAiShortcut(event: ShortcutLikeEvent, shortcuts?: Partial): boolean { return matchesShortcut(event, actionShortcut("sendSelectionToAi", shortcuts)); } diff --git a/apps/desktop/src/lib/editor/shortcutRegistry.ts b/apps/desktop/src/lib/editor/shortcutRegistry.ts index 259415b6b1..aa6e6c152c 100644 --- a/apps/desktop/src/lib/editor/shortcutRegistry.ts +++ b/apps/desktop/src/lib/editor/shortcutRegistry.ts @@ -51,6 +51,7 @@ export type ShortcutActionId = | "openSettings" | "closeTab" | "closeOtherTabs" + | "closeWindow" | "focusSearch" | "quickOpen" | "globalSearch" @@ -474,6 +475,12 @@ export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [ scope: "global", defaultShortcut: closeOtherTabsDefaultShortcut(), }, + { + id: "closeWindow", + labelKey: "settings.shortcutCloseWindow", + scope: "global", + defaultShortcut: "Shift+Mod+W", + }, { id: "focusSearch", labelKey: "settings.shortcutFocusSearch", diff --git a/packages/app-tests/keyboardShortcuts.test.ts b/packages/app-tests/keyboardShortcuts.test.ts index 74a7d84899..02a40ed7b0 100644 --- a/packages/app-tests/keyboardShortcuts.test.ts +++ b/packages/app-tests/keyboardShortcuts.test.ts @@ -8,6 +8,7 @@ import { isCancelSearchShortcut, isCloseOtherTabsShortcut, isCloseTabShortcut, + isCloseWindowShortcut, isCopySidebarSelectionShortcut, isDisconnectSidebarConnectionShortcut, isExecuteSqlShortcut, @@ -295,6 +296,14 @@ test("matches the platform modifier for closing query tabs", () => { assert.equal(isCloseTabShortcut({ key: "w", ctrlKey: true }, { closeTab: "Meta+W" } as any), true); }); +test("matches the shortcut for closing window", () => { + assert.equal(isCloseWindowShortcut({ key: "W", metaKey: true, shiftKey: true }), true); + assert.equal(isCloseWindowShortcut({ key: "W", ctrlKey: true, shiftKey: true }), true); + assert.equal(isCloseWindowShortcut({ key: "w", metaKey: true, shiftKey: true }), true); + assert.equal(isCloseWindowShortcut({ key: "w", ctrlKey: true, shiftKey: true }), true); + assert.equal(isCloseWindowShortcut({ key: "q", ctrlKey: true }, { closeWindow: "Mod+Q" }), true); +}); + test("matches platform shortcuts for closing other tabs", () => { // macOS 默认 ⌥⌘W:Option 会把 event.key 变形(⌥W → "∑"),按 code 回退匹配。 // 平台默认集合内的值会被 normalize 按本机平台还原(云同步自愈),因此