feat(editor): handle tabs after connection disconnect
This commit is contained in:
1 parent
3cb1faf1c6
commit
28cc1b9768
31 files changed
+1137
-60
No files matched your search
@@ -250,3 +250,28 @@ describe("openTabsPersistence originalSql round-trip", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("openTabsPersistence detached connection tabs", () => {
|
||||
it("preserves the original connection name of a kept SQL tab across a round-trip", () => {
|
||||
const [restored] = roundTrip([queryTab({ connectionId: "deleted-conn", detachedConnectionName: "prod" })]);
|
||||
|
||||
expect(restored.connectionId).toBe("deleted-conn");
|
||||
expect(restored.detachedConnectionName).toBe("prod");
|
||||
});
|
||||
|
||||
it("keeps a detached SQL tab even though its connection no longer exists", () => {
|
||||
const saved = serializeOpenTabs([queryTab({ id: "orphan", connectionId: "deleted-conn", detachedConnectionName: "prod" })]);
|
||||
|
||||
const { tabs } = restoreOpenTabsPayload({ tabs: saved, activeTabId: "orphan" }, { validConnectionIds: ["other-conn"] });
|
||||
|
||||
// SQL 页签不受 validConnectionIds 过滤,删除连接后保留下来的草稿页签才能跨重启存活。
|
||||
expect(tabs.map((tab) => tab.id)).toEqual(["orphan"]);
|
||||
expect(tabs[0].detachedConnectionName).toBe("prod");
|
||||
});
|
||||
|
||||
it("omits the detached connection name when the tab is bound to a live connection", () => {
|
||||
const [saved] = serializeOpenTabs([queryTab({})]);
|
||||
|
||||
expect(saved.detachedConnectionName).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -23,6 +23,8 @@ export interface SavedOpenTab {
|
||||
customTitle?: boolean;
|
||||
connectionId: string;
|
||||
database: string;
|
||||
/** 原连接已被删除但页签被保留时记录的原连接名,用于新建同名连接后重新绑定。 */
|
||||
detachedConnectionName?: string;
|
||||
catalog?: string;
|
||||
schema?: string;
|
||||
sql: string;
|
||||
@@ -163,6 +165,7 @@ export function serializeOpenTabs(tabs: QueryTab[]): SavedOpenTab[] {
|
||||
...(tab.customTitle ? { customTitle: true } : {}),
|
||||
connectionId: tab.connectionId,
|
||||
database: tab.database,
|
||||
...(tab.detachedConnectionName ? { detachedConnectionName: tab.detachedConnectionName } : {}),
|
||||
...(tab.catalog !== undefined ? { catalog: tab.catalog } : {}),
|
||||
schema: tab.schema,
|
||||
sql: shouldPersistTabSql(tab) ? tab.sql : "",
|
||||
|
||||
@@ -300,3 +300,8 @@ const DRAFT_VISIBLE_SCHEMAS_PREFIX = "__visible_schema_draft_";
|
||||
export function buildDraftVisibleSchemasConnectionId(seed: string): string {
|
||||
return `${DRAFT_VISIBLE_SCHEMAS_PREFIX}${seed}`;
|
||||
}
|
||||
|
||||
/** 可见 schema 选择器用的临时连接:只存在于弹窗交互期间,不是用户保存的连接。 */
|
||||
export function isDraftVisibleSchemasConnectionId(connectionId: string): boolean {
|
||||
return connectionId.startsWith(DRAFT_VISIBLE_SCHEMAS_PREFIX);
|
||||
}
|
||||
@@ -84,6 +84,8 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
|
||||
"sidebarBrowseObjectsOnDatabaseActivation",
|
||||
"openTabsRestoreMode",
|
||||
"disconnectTabHandlingMode",
|
||||
"deleteConnectionTabHandlingMode",
|
||||
"rememberConnectionDatabaseOnDelete",
|
||||
"dataTabReuseMode",
|
||||
"openDataTabsNextToActive",
|
||||
"prefillNewQueryWithSelect",
|
||||
|
||||
@@ -198,6 +198,8 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
|
||||
{ id: "navigation-hidden-tables", category: "navigation", titleKey: "settings.sidebarHiddenTablePrefixes", descriptionKey: "settings.sidebarHiddenTablePrefixesDescription", targetId: "navigation" },
|
||||
{ id: "navigation-table-page-size", category: "navigation", titleKey: "settings.sidebarTablePageSize", descriptionKey: "settings.sidebarTablePageSizeDescription", targetId: "navigation" },
|
||||
{ id: "navigation-disconnect-tabs", category: "navigation", titleKey: "settings.disconnectTabHandlingMode", descriptionKey: "settings.disconnectTabHandlingModeDescription", targetId: "navigation" },
|
||||
{ id: "navigation-delete-connection-tabs", category: "navigation", titleKey: "settings.deleteConnectionTabHandlingMode", descriptionKey: "settings.deleteConnectionTabHandlingModeDescription", targetId: "navigation" },
|
||||
{ id: "navigation-remember-connection-database", category: "navigation", titleKey: "settings.rememberConnectionDatabaseOnDelete", descriptionKey: "settings.rememberConnectionDatabaseOnDeleteDescription", targetId: "navigation" },
|
||||
{ id: "query-page-size", category: "data", titleKey: "settings.queryPageSize", descriptionKey: "settings.queryPageSizeDescription", targetId: "data" },
|
||||
{ id: "data-page-size", category: "data", titleKey: "settings.tableOpenPageSize", descriptionKey: "settings.tableOpenPageSizeDescription", targetId: "data" },
|
||||
{ id: "data-table-open-sort-mode", category: "data", titleKey: "settings.tableOpenSortMode", descriptionKey: "settings.tableOpenSortDescription", targetId: "data" },
|
||||
|
||||
@@ -121,6 +121,8 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record<SettingsTransferCategoryId, readon
|
||||
"sidebarBrowseObjectsOnDatabaseActivation",
|
||||
"openTabsRestoreMode",
|
||||
"disconnectTabHandlingMode",
|
||||
"deleteConnectionTabHandlingMode",
|
||||
"rememberConnectionDatabaseOnDelete",
|
||||
"dataTabReuseMode",
|
||||
"openDataTabsNextToActive",
|
||||
"clickTableNavigationTarget",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { deletedConnectionTabKeepMode } from "@/lib/tabs/deletedConnectionTabs";
|
||||
|
||||
describe("deletedConnectionTabKeepMode", () => {
|
||||
it("maps each delete-time tab handling mode to its keep scope", () => {
|
||||
expect(deletedConnectionTabKeepMode("close-tabs")).toBe("none");
|
||||
expect(deletedConnectionTabKeepMode("keep-sql-tabs")).toBe("sql");
|
||||
expect(deletedConnectionTabKeepMode("keep-pinned-sql-tabs")).toBe("pinned-sql");
|
||||
expect(deletedConnectionTabKeepMode("keep-all-tabs")).toBe("all");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { DeleteConnectionTabHandlingMode } from "@/stores/settingsStore";
|
||||
|
||||
/**
|
||||
* 删除连接时对已打开页签的保留范围。
|
||||
*
|
||||
* 与「断开连接」不同,删除后连接配置已从磁盘移除、无法再重连,因此被保留下来的
|
||||
* SQL 页签只是草稿工作区;它们会记录原连接名,等新建同名连接时再重新绑定。
|
||||
*
|
||||
* - `none`:关闭全部页签
|
||||
* - `sql`:保留全部 SQL 页签(清空结果),其余关闭
|
||||
* - `pinned-sql`:只保留固定的 SQL 页签(清空结果),其余关闭
|
||||
* - `all`:不关闭任何页签,保留 SQL 文本与当前结果,只回滚事务
|
||||
*/
|
||||
export type DeletedConnectionTabKeepMode = "none" | "sql" | "pinned-sql" | "all";
|
||||
|
||||
export function deletedConnectionTabKeepMode(mode: DeleteConnectionTabHandlingMode): DeletedConnectionTabKeepMode {
|
||||
if (mode === "keep-sql-tabs") return "sql";
|
||||
if (mode === "keep-pinned-sql-tabs") return "pinned-sql";
|
||||
if (mode === "keep-all-tabs") return "all";
|
||||
return "none";
|
||||
}
|
||||
Reference in new issue
Block a user