diff --git a/src/components/layout/__tests__/useConnectionSwitching.test.ts b/src/components/layout/__tests__/useConnectionSwitching.test.ts index 99381478..4baecf89 100644 --- a/src/components/layout/__tests__/useConnectionSwitching.test.ts +++ b/src/components/layout/__tests__/useConnectionSwitching.test.ts @@ -110,4 +110,29 @@ describe("editor tab and selected connection agree (#788)", () => { expect(activeTab()?.connectionId).toBe("sqlite-1"); expect(useEditorStore.getState().tabs.find((t) => t.id === "tab-0")?.connectionId).toBe("dev"); }); + + it("drops the old server's profile from a tab a SQLite file takes over (#962)", () => { + // The tab's server is gone, so the file takes the tab rather than a new one. + setConnections([], null); + const { result } = renderHook(() => useConnectionSwitching()); + useSqliteStore.setState({ sessions: [{ id: "sqlite-1", path: "data.db" } as unknown as SqliteSession] }); + + act(() => result.current.attachSqliteSession("sqlite-1")); + + expect(activeTab()?.id).toBe("tab-0"); + expect(activeTab()?.connectionId).toBe("sqlite-1"); + // Kept, it would reconnect p-dev and bind the tab back to it at the next start. + expect(activeTab()?.profileId).toBeUndefined(); + }); + + it("replaces the old server's profile on a tab a new server takes over", () => { + setConnections([], null); + const { result } = renderHook(() => useConnectionSwitching()); + setConnections([prod], "prod"); + + act(() => result.current.attachNewConnection(prod)); + + expect(activeTab()?.id).toBe("tab-0"); + expect(activeTab()?.profileId).toBe("p-prod"); + }); }); diff --git a/src/components/layout/useConnectionSwitching.ts b/src/components/layout/useConnectionSwitching.ts index 9ea8235f..7e8e9942 100644 --- a/src/components/layout/useConnectionSwitching.ts +++ b/src/components/layout/useConnectionSwitching.ts @@ -75,7 +75,10 @@ export function useConnectionSwitching() { if (active && !activeHasServer) { // Only a tab with no live server is taken over, so this does not // normally move a session; through the guard all the same (#843). - requestTabConnection(active.id, connectionId, conn?.database ?? undefined, conn?.profile_id); + // A SQLite file has no profile. `null` drops the one the tab still has + // from the server it was on, which would otherwise send it back there + // at the next start (#962). + requestTabConnection(active.id, connectionId, conn?.database ?? undefined, conn?.profile_id ?? null); return; } openTabFor(connectionId, conn); diff --git a/src/components/schema/SchemaTree.tsx b/src/components/schema/SchemaTree.tsx index bfec03ab..3708c593 100644 --- a/src/components/schema/SchemaTree.tsx +++ b/src/components/schema/SchemaTree.tsx @@ -129,7 +129,9 @@ export function SchemaTree({ connectionId }: { connectionId: string }) { const selectDatabase = (dbName: string) => { if (!activeTabId) return; const conn = activeConnections.find((c) => c.id === connectionId); - requestTabConnection(activeTabId, connectionId, dbName, conn?.profile_id); + // `null` for a tree with no profile (a SQLite file), so the tab does not + // keep the profile of the server it is moved from (#962). + requestTabConnection(activeTabId, connectionId, dbName, conn?.profile_id ?? null); }; // Tracks pending single-click timers keyed by "db.name" diff --git a/src/stores/editorStore.ts b/src/stores/editorStore.ts index a0932c73..a1f99d6a 100644 --- a/src/stores/editorStore.ts +++ b/src/stores/editorStore.ts @@ -155,7 +155,7 @@ interface EditorState { * server where its transaction is open (#843). `setTabConnection` sets it * unconditionally. */ - requestTabConnection: (id: string, connectionId: string, database?: string, profileId?: string) => void; + requestTabConnection: (id: string, connectionId: string, database?: string, profileId?: string | null) => void; setActiveTab: (id: string) => void; updateTabContent: (id: string, content: string) => void; /**