From 58e2d983c2ccd68969196398828e9ea0912b2226 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Thu, 2 Jul 2026 00:48:30 +0800 Subject: [PATCH] fix(tabs): restore previous tab after data tab close --- apps/desktop/src/stores/queryStore.ts | 28 +++++++++++++++++++++++---- packages/app-tests/queryStore.test.ts | 16 +++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index c035cbc34..52f540fe7 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -190,6 +190,7 @@ export const useQueryStore = defineStore("query", () => { const restored = loadSavedTabs(); const tabs = ref(restored.tabs); const activeTabId = ref(restored.activeTabId); + const activeTabHistory = ref(restored.activeTabId ? [restored.activeTabId] : []); const showCloseConfirm = ref(false); const pendingCloseTabId = ref(null); const pendingBatchCloseTabIds = ref(null); @@ -866,7 +867,7 @@ export const useQueryStore = defineStore("query", () => { clearResultPayload(tabs.value[idx]); tabs.value.splice(idx, 1); if (activeTabId.value === id) { - activeTabId.value = tabs.value[Math.min(idx, tabs.value.length - 1)]?.id ?? null; + activeTabId.value = fallbackActiveTabAfterClose(id, idx); } if (force) resumePendingBatchCloseAfter(id); } @@ -2246,9 +2247,28 @@ export const useQueryStore = defineStore("query", () => { } } - watch(activeTabId, (id) => { - touchResult(tabs.value.find((tab) => tab.id === id)); - }); + function rememberActiveTab(id: string | null) { + if (!id || !tabs.value.some((tab) => tab.id === id)) return; + activeTabHistory.value = [...activeTabHistory.value.filter((tabId) => tabId !== id), id]; + } + + function fallbackActiveTabAfterClose(closedId: string, closedIndex: number): string | null { + const remainingIds = new Set(tabs.value.map((tab) => tab.id)); + // Prefer the most recently focused remaining tab. This preserves the + // source query tab when a transient table-info/data tab is closed. + const history = activeTabHistory.value.filter((tabId) => tabId !== closedId && remainingIds.has(tabId)); + activeTabHistory.value = history; + return [...history].reverse().find((tabId) => remainingIds.has(tabId)) ?? tabs.value[Math.min(closedIndex, tabs.value.length - 1)]?.id ?? null; + } + + watch( + activeTabId, + (id) => { + rememberActiveTab(id); + touchResult(tabs.value.find((tab) => tab.id === id)); + }, + { flush: "sync" }, + ); function restoreCachedResultPayload(tab: QueryTab, snapshot: Awaited>) { if (!snapshot) return false; diff --git a/packages/app-tests/queryStore.test.ts b/packages/app-tests/queryStore.test.ts index 852fa4911..8df5a5b10 100644 --- a/packages/app-tests/queryStore.test.ts +++ b/packages/app-tests/queryStore.test.ts @@ -101,6 +101,22 @@ test("renames query tab titles", () => { assert.equal(tab?.customTitle, true); }); +test("closing an active data tab restores the previously focused query tab", () => { + setActivePinia(createPinia()); + const store = useQueryStore(); + const firstQueryId = store.createTab("conn-1", "db", "query_1", "query"); + store.createTab("conn-1", "db", "query_2", "query"); + + store.activeTabId = firstQueryId; + const dataTabId = store.createTab("conn-1", "db", "public.users", "data", "public"); + + assert.equal(store.activeTabId, dataTabId); + + store.closeTab(dataTabId); + + assert.equal(store.activeTabId, firstQueryId); +}); + test("linkExternalSqlPath records the local path and detaches saved SQL", () => { setActivePinia(createPinia()); const store = useQueryStore();