diff --git a/src/App.vue b/src/App.vue index e60c37f57..b0906bdb2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -202,12 +202,27 @@ function handleKeydown(e: KeyboardEvent) { } function initApp() { - connectionStore.initFromDisk().catch((e: any) => { + connectionStore.initFromDisk().then(() => { + reconnectRestoredTabs(); + }).catch((e: any) => { toast(t("connection.loadFailed", { message: e?.message || String(e) }), 5000); }); settingsStore.initAiConfig(); } +async function reconnectRestoredTabs() { + const connectionIds = new Set(queryStore.tabs.map(t => t.connectionId).filter(Boolean)); + for (const id of connectionIds) { + try { + await connectionStore.ensureConnected(id); + } catch {} + } + const tab = queryStore.tabs.find(t => t.id === queryStore.activeTabId); + if (tab && tab.mode === "data" && tab.tableMeta) { + queryStore.executeCurrentTab(); + } +} + onMounted(async () => { applyTheme(); window.addEventListener("keydown", handleKeydown, true); diff --git a/src/stores/connectionStore.ts b/src/stores/connectionStore.ts index 065288a40..4ca6baf45 100644 --- a/src/stores/connectionStore.ts +++ b/src/stores/connectionStore.ts @@ -1,5 +1,5 @@ import { defineStore } from "pinia"; -import { ref } from "vue"; +import { ref, watch } from "vue"; import type { ColumnInfo, ConnectionConfig, SidebarLayout, TreeNode } from "@/types/database"; import { orderPinnedFirst } from "@/lib/pinnedItems"; import { @@ -24,7 +24,14 @@ const PINNED_TREE_NODES_STORAGE_KEY = "dbx-pinned-tree-nodes"; export const useConnectionStore = defineStore("connection", () => { const connections = ref([]); - const activeConnectionId = ref(null); + const isDesktop = isTauriRuntime(); + const activeConnectionId = ref(!isDesktop ? localStorage.getItem("dbx-active-connection") : null); + + watch(activeConnectionId, (id) => { + if (isDesktop) return; + if (id) localStorage.setItem("dbx-active-connection", id); + else localStorage.removeItem("dbx-active-connection"); + }); const treeNodes = ref([]); const pinnedTreeNodeIds = ref>(loadPinnedTreeNodeIds()); const connectedIds = ref>(new Set()); diff --git a/src/stores/queryStore.ts b/src/stores/queryStore.ts index b1aa6d706..35d769e5f 100644 --- a/src/stores/queryStore.ts +++ b/src/stores/queryStore.ts @@ -1,5 +1,5 @@ import { defineStore } from "pinia"; -import { ref } from "vue"; +import { ref, watch } from "vue"; import type { DatabaseType, QueryTab } from "@/types/database"; import { orderPinnedFirst } from "@/lib/pinnedItems"; import { canCancelQueryExecution } from "@/lib/queryExecutionState"; @@ -7,11 +7,63 @@ import { closeAllTabsState, closeOtherTabsState } from "@/lib/tabCloseActions"; import { buildExplainSql, parseExplainResult } from "@/lib/explainPlan"; import * as api from "@/lib/api"; +import { isTauriRuntime } from "@/lib/tauriRuntime"; + +interface SavedTab { + id: string; + title: string; + connectionId: string; + database: string; + sql: string; + pinned?: boolean; + mode: "data" | "query" | "redis" | "mongo"; + tableMeta?: QueryTab["tableMeta"]; +} + +const STORAGE_KEY = "dbx-open-tabs"; +const ACTIVE_TAB_KEY = "dbx-active-tab"; +const isDesktop = isTauriRuntime(); + +function saveTabs(tabs: QueryTab[], activeTabId: string | null) { + if (isDesktop) return; + try { + const saved: SavedTab[] = tabs.map(t => ({ + id: t.id, title: t.title, connectionId: t.connectionId, + database: t.database, sql: t.sql, pinned: t.pinned, + mode: t.mode, tableMeta: t.tableMeta, + })); + localStorage.setItem(STORAGE_KEY, JSON.stringify(saved)); + localStorage.setItem(ACTIVE_TAB_KEY, activeTabId || ""); + } catch {} +} + +function loadSavedTabs(): { tabs: QueryTab[]; activeTabId: string | null } { + if (isDesktop) return { tabs: [], activeTabId: null }; + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return { tabs: [], activeTabId: null }; + const saved: SavedTab[] = JSON.parse(raw); + const tabs: QueryTab[] = saved.map(s => ({ + ...s, + isExecuting: false, + isCancelling: false, + isExplaining: false, + })); + const activeTabId = localStorage.getItem(ACTIVE_TAB_KEY) || null; + return { tabs, activeTabId: tabs.some(t => t.id === activeTabId) ? activeTabId : tabs[0]?.id || null }; + } catch { + return { tabs: [], activeTabId: null }; + } +} + export const useQueryStore = defineStore("query", () => { - const tabs = ref([]); - const activeTabId = ref(null); + const restored = loadSavedTabs(); + const tabs = ref(restored.tabs); + const activeTabId = ref(restored.activeTabId); const MAX_CACHED_RESULTS = 10; + watch([tabs, activeTabId], () => saveTabs(tabs.value, activeTabId.value), { deep: true }); + function findTabByTitle(connectionId: string, database: string, title: string) { return tabs.value.find((t) => t.connectionId === connectionId && t.database === database && t.title === title); }