From 642fa39bb8e8f8b460f44c777cfd76284740b645 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Fri, 5 Jun 2026 20:45:00 +0800 Subject: [PATCH] feat: support multiple desktop windows --- Cargo.lock | 352 ------------------ apps/desktop/src/App.vue | 24 +- .../src/components/editor/AiAssistant.vue | 2 +- apps/desktop/src/components/grid/DataGrid.vue | 7 + .../src/components/layout/AppSidebar.vue | 4 +- .../src/components/layout/AppTabBar.vue | 22 +- .../src/components/layout/AppToolbar.vue | 4 +- .../src/components/layout/ContentArea.vue | 9 + .../src/composables/useDataGridEditor.ts | 18 + apps/desktop/src/composables/useTabDrag.ts | 22 +- .../src/composables/useWindowDragRegion.ts | 37 ++ apps/desktop/src/i18n/locales/en.ts | 1 + apps/desktop/src/i18n/locales/es.ts | 1 + apps/desktop/src/i18n/locales/it.ts | 1 + apps/desktop/src/i18n/locales/zh-CN.ts | 1 + apps/desktop/src/i18n/locales/zh-TW.ts | 1 + apps/desktop/src/lib/desktopWindows.ts | 34 ++ apps/desktop/src/lib/keyboardShortcuts.ts | 4 + .../desktop/src/lib/tableMutationBroadcast.ts | 49 +++ apps/desktop/src/stores/queryStore.ts | 56 +++ apps/desktop/src/types/database.ts | 1 + crates/dbx-core/src/storage.rs | 72 ++++ src-tauri/Cargo.toml | 1 - src-tauri/capabilities/default.json | 3 +- src-tauri/src/lib.rs | 231 ++++++++---- src-tauri/tauri.conf.json | 1 + 26 files changed, 522 insertions(+), 436 deletions(-) create mode 100644 apps/desktop/src/composables/useWindowDragRegion.ts create mode 100644 apps/desktop/src/lib/desktopWindows.ts create mode 100644 apps/desktop/src/lib/tableMutationBroadcast.ts diff --git a/Cargo.lock b/Cargo.lock index 27a5d7297..f901f6565 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -385,30 +385,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "async-broadcast" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - [[package]] name = "async-compression" version = "0.4.42" @@ -421,96 +397,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix 1.1.4", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix 1.1.4", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-signal" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 1.1.4", - "signal-hook-registry", - "slab", - "windows-sys 0.61.2", -] - [[package]] name = "async-stream" version = "0.3.6" @@ -533,12 +419,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -823,19 +703,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "blowfish" version = "0.9.1" @@ -1299,15 +1166,6 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "connection-string" version = "0.2.0" @@ -1872,7 +1730,6 @@ dependencies = [ "tauri-plugin-log", "tauri-plugin-process", "tauri-plugin-shell", - "tauri-plugin-single-instance", "tauri-plugin-updater", "tauri-plugin-window-state", "tiberius", @@ -2422,12 +2279,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "endi" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" - [[package]] name = "enum_dispatch" version = "0.3.13" @@ -2447,7 +2298,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", - "serde", ] [[package]] @@ -2504,27 +2354,6 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -2811,19 +2640,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.32" @@ -5233,16 +5049,6 @@ dependencies = [ "hashbrown 0.14.5", ] -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "os_pipe" version = "1.2.3" @@ -5353,12 +5159,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - [[package]] name = "parking_lot" version = "0.12.5" @@ -5542,17 +5342,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "pkcs1" version = "0.8.0-rc.4" @@ -5637,20 +5426,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix 1.1.4", - "windows-sys 0.61.2", -] - [[package]] name = "poly1305" version = "0.8.0" @@ -8059,21 +7834,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tauri-plugin-single-instance" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8f29386f5e9fdc699182388a33ee80a56de436d91b67459e86afef426282af" -dependencies = [ - "serde", - "serde_json", - "tauri", - "thiserror 2.0.18", - "tracing", - "windows-sys 0.60.2", - "zbus", -] - [[package]] name = "tauri-plugin-updater" version = "2.10.1" @@ -8864,17 +8624,6 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" -[[package]] -name = "uds_windows" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" -dependencies = [ - "memoffset", - "tempfile", - "windows-sys 0.61.2", -] - [[package]] name = "unic-char-property" version = "0.9.0" @@ -10383,67 +10132,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zbus" -version = "5.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" -dependencies = [ - "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "libc", - "ordered-stream", - "rustix 1.1.4", - "serde", - "serde_repr", - "tracing", - "uds_windows", - "uuid", - "windows-sys 0.61.2", - "winnow 1.0.3", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" -dependencies = [ - "serde", - "winnow 1.0.3", - "zvariant", -] - [[package]] name = "zerocopy" version = "0.8.48" @@ -10645,43 +10333,3 @@ checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ "zune-core", ] - -[[package]] -name = "zvariant" -version = "5.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" -dependencies = [ - "endi", - "enumflags2", - "serde", - "winnow 1.0.3", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.117", - "winnow 1.0.3", -] diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 97768bddd..0de3e5ecb 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -27,6 +27,7 @@ import { useNavigationTargets } from "@/composables/useNavigationTargets"; import { useDataGridActions } from "@/composables/useDataGridActions"; import { useTauriEvents } from "@/composables/useTauriEvents"; import { useVisibilityChange } from "@/composables/useVisibilityChange"; +import { installWindowDragRegions } from "@/composables/useWindowDragRegion"; import "@/i18n"; import { translateBackendError } from "@/i18n/backend-errors"; import * as api from "@/lib/api"; @@ -46,6 +47,7 @@ import { isFocusSearchShortcut, isModRShortcut, isNewQueryShortcut, + isNewWindowShortcut, isObjectSourceSaveShortcutTarget, isResetZoomShortcut, isRefreshDataShortcut, @@ -59,6 +61,8 @@ import { classifyAiSqlExecution } from "@/lib/aiSqlExecutionPolicy"; import { buildHistoryAiAnalysisPrompt } from "@/lib/historyAiAnalysis"; import { countAvailableAgentDriverUpdates, type AgentDriverUpdateBadgeState } from "@/lib/agentDriverUpdateBadge"; import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/safeStorage"; +import { openDesktopWindow } from "@/lib/desktopWindows"; +import { subscribeTableMutations } from "@/lib/tableMutationBroadcast"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -103,6 +107,8 @@ const { setupFileDrop } = useFileDrop(); const isDesktop = isTauriRuntime(); const UPDATE_CHECK_INTERVAL_MS = 60 * 60 * 1000; let updateCheckTimer: ReturnType | undefined; +let unsubscribeTableMutations: (() => void) | undefined; +let uninstallWindowDragRegions: (() => void) | undefined; const needsAuth = ref(!isDesktop); const authenticated = ref(isDesktop); const setupRequired = ref(false); @@ -760,6 +766,12 @@ function handleKeydown(e: KeyboardEvent) { contentAreaRef.value?.refreshData(); return; } + if (isNewWindowShortcut(e)) { + e.preventDefault(); + e.stopPropagation(); + void openDesktopWindow(); + return; + } if (isNewQueryShortcut(e, shortcuts)) { e.preventDefault(); e.stopPropagation(); @@ -888,7 +900,11 @@ onMounted(async () => { }); applyTheme(); void applyUiScale(settingsStore.editorSettings.uiScale); - window.addEventListener("keydown", handleKeydown); + window.addEventListener("keydown", handleKeydown, true); + uninstallWindowDragRegions = installWindowDragRegions(); + unsubscribeTableMutations = subscribeTableMutations((message) => { + queryStore.markTableTabsStale(message); + }); window.addEventListener("dbx-open-driver-store", openDriverStoreFromEvent); if (isDesktop) { document.addEventListener("contextmenu", handleContextMenu); @@ -944,7 +960,11 @@ onUnmounted(() => { if (updateCheckTimer) { clearInterval(updateCheckTimer); } - window.removeEventListener("keydown", handleKeydown); + window.removeEventListener("keydown", handleKeydown, true); + uninstallWindowDragRegions?.(); + uninstallWindowDragRegions = undefined; + unsubscribeTableMutations?.(); + unsubscribeTableMutations = undefined; window.removeEventListener("dbx-open-driver-store", openDriverStoreFromEvent); document.removeEventListener("contextmenu", handleContextMenu); }); diff --git a/apps/desktop/src/components/editor/AiAssistant.vue b/apps/desktop/src/components/editor/AiAssistant.vue index 4cac802fb..f807a95b8 100644 --- a/apps/desktop/src/components/editor/AiAssistant.vue +++ b/apps/desktop/src/components/editor/AiAssistant.vue @@ -690,7 +690,7 @@ const messageRenderer = computed(() => { class="flex items-center gap-2 border-b px-3 shrink-0" :class="settings.editorSettings.appLayout === 'classic' ? 'h-9' : 'h-10'" > - + {{ chatTitle }}