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
@@ -673,6 +673,13 @@ export const SIDEBAR_INDENT_DEFAULT = 16;
|
||||
const DISCONNECT_TAB_HANDLING_MODES = ["close-tabs", "keep-tabs-clear-results", "keep-tabs-keep-results"] as const;
|
||||
export type DisconnectTabHandlingMode = (typeof DISCONNECT_TAB_HANDLING_MODES)[number];
|
||||
|
||||
/**
|
||||
* 删除连接(连接配置已从磁盘移除,无法再重连)时,对该连接已打开页签的处理策略。
|
||||
* 与 {@link DisconnectTabHandlingMode} 分开:断开连接仍可重连,删除则不会。
|
||||
*/
|
||||
const DELETE_CONNECTION_TAB_HANDLING_MODES = ["close-tabs", "keep-sql-tabs", "keep-pinned-sql-tabs", "keep-all-tabs"] as const;
|
||||
export type DeleteConnectionTabHandlingMode = (typeof DELETE_CONNECTION_TAB_HANDLING_MODES)[number];
|
||||
|
||||
const CLICK_TABLE_NAVIGATION_TARGETS = ["data", "ddl"] as const;
|
||||
export type ClickTableNavigationTarget = (typeof CLICK_TABLE_NAVIGATION_TARGETS)[number];
|
||||
|
||||
@@ -744,6 +751,16 @@ export const DEFAULT_CUSTOM_THEMES: CustomTheme[] = [
|
||||
|
||||
export type SidebarObjectInfoMode = "comment-inline" | "comment-aligned" | "comment-right" | "size" | "hidden";
|
||||
|
||||
/**
|
||||
* 删除连接时记住的「连接名 → 数据库」。带上 `dbType` 是为了只在新建同名**同类型**
|
||||
* 连接时回填——像 "test"/"local" 这类名字常被不同数据库类型复用,跨类型回填会把
|
||||
* 无意义的库名写进新连接配置。
|
||||
*/
|
||||
export interface RememberedConnectionDatabase {
|
||||
database: string;
|
||||
dbType: string;
|
||||
}
|
||||
|
||||
export interface EditorSettings {
|
||||
fontFamily: string;
|
||||
fontSize: number;
|
||||
@@ -863,6 +880,11 @@ export interface EditorSettings {
|
||||
sidebarBrowseObjectsOnDatabaseActivationMigrationVersion: number;
|
||||
openTabsRestoreMode: OpenTabsRestoreMode;
|
||||
disconnectTabHandlingMode: DisconnectTabHandlingMode;
|
||||
deleteConnectionTabHandlingMode: DeleteConnectionTabHandlingMode;
|
||||
/** 删除连接时记住「连接名 → 数据库名」,新建同名同类型连接时自动回填并重绑保留的 SQL 页签。 */
|
||||
rememberConnectionDatabaseOnDelete: boolean;
|
||||
/** 已记住的「连接名 → { 数据库名, 数据库类型 }」映射,用于新建同名连接时自动选中数据库。 */
|
||||
rememberedConnectionDatabases: Record<string, RememberedConnectionDatabase>;
|
||||
dataTabReuseMode: DataTabReuseMode;
|
||||
openDataTabsNextToActive: boolean;
|
||||
prefillNewQueryWithSelect: boolean;
|
||||
@@ -1121,6 +1143,9 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
sidebarBrowseObjectsOnDatabaseActivationMigrationVersion: SIDEBAR_BROWSE_OBJECTS_MIGRATION_VERSION,
|
||||
openTabsRestoreMode: "all",
|
||||
disconnectTabHandlingMode: "close-tabs",
|
||||
deleteConnectionTabHandlingMode: "close-tabs",
|
||||
rememberConnectionDatabaseOnDelete: true,
|
||||
rememberedConnectionDatabases: {},
|
||||
dataTabReuseMode: DEFAULT_DATA_TAB_REUSE_MODE,
|
||||
openDataTabsNextToActive: false,
|
||||
prefillNewQueryWithSelect: true,
|
||||
@@ -1310,6 +1335,35 @@ function normalizeDisconnectTabHandlingMode(value: unknown, legacyCloseTabsOnDis
|
||||
return DEFAULT_EDITOR_SETTINGS.disconnectTabHandlingMode;
|
||||
}
|
||||
|
||||
function normalizeDeleteConnectionTabHandlingMode(value: unknown): DeleteConnectionTabHandlingMode {
|
||||
if (DELETE_CONNECTION_TAB_HANDLING_MODES.includes(value as DeleteConnectionTabHandlingMode)) {
|
||||
return value as DeleteConnectionTabHandlingMode;
|
||||
}
|
||||
// 兼容早期试验值:把「保留页签」统一收敛到最接近的正式取值。
|
||||
if (value === "keep-tabs" || value === "keep-sql") return "keep-sql-tabs";
|
||||
if (value === "keep-pinned" || value === "keep-fixed-tabs") return "keep-pinned-sql-tabs";
|
||||
if (value === "keep-all") return "keep-all-tabs";
|
||||
return DEFAULT_EDITOR_SETTINGS.deleteConnectionTabHandlingMode;
|
||||
}
|
||||
|
||||
/** 记住的连接名 → 数据库映射:只保留合法条目,并限制条目数量避免无限增长。 */
|
||||
const REMEMBERED_CONNECTION_DATABASES_LIMIT = 200;
|
||||
|
||||
function normalizeRememberedConnectionDatabases(value: unknown): Record<string, RememberedConnectionDatabase> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
||||
const entries: [string, RememberedConnectionDatabase][] = [];
|
||||
for (const [name, entry] of Object.entries(value as Record<string, unknown>)) {
|
||||
const key = name.trim();
|
||||
if (!key || !entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
||||
const { database, dbType } = entry as { database?: unknown; dbType?: unknown };
|
||||
if (typeof database !== "string" || !database.trim() || typeof dbType !== "string") continue;
|
||||
entries.push([key, { database, dbType }]);
|
||||
}
|
||||
if (entries.length <= REMEMBERED_CONNECTION_DATABASES_LIMIT) return Object.fromEntries(entries);
|
||||
// 超出上限时保留最后写入的部分(对象插入顺序即写入顺序)。
|
||||
return Object.fromEntries(entries.slice(entries.length - REMEMBERED_CONNECTION_DATABASES_LIMIT));
|
||||
}
|
||||
|
||||
function normalizeClickTableNavigationTarget(value: unknown): ClickTableNavigationTarget {
|
||||
return value === "data" || value === "ddl" ? value : DEFAULT_EDITOR_SETTINGS.clickTableNavigationTarget;
|
||||
}
|
||||
@@ -1650,6 +1704,9 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
||||
}
|
||||
).closeQueryTabsOnDisconnect,
|
||||
),
|
||||
deleteConnectionTabHandlingMode: normalizeDeleteConnectionTabHandlingMode(settings.deleteConnectionTabHandlingMode),
|
||||
rememberConnectionDatabaseOnDelete: typeof settings.rememberConnectionDatabaseOnDelete === "boolean" ? settings.rememberConnectionDatabaseOnDelete : DEFAULT_EDITOR_SETTINGS.rememberConnectionDatabaseOnDelete,
|
||||
rememberedConnectionDatabases: normalizeRememberedConnectionDatabases(settings.rememberedConnectionDatabases),
|
||||
dataTabReuseMode: normalizeDataTabReuseMode(
|
||||
settings.dataTabReuseMode,
|
||||
(
|
||||
@@ -2414,6 +2471,9 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
if (partial.sidebarBrowseObjectsOnDatabaseActivation !== undefined) editorSettings.value.sidebarBrowseObjectsOnDatabaseActivation = partial.sidebarBrowseObjectsOnDatabaseActivation === true;
|
||||
if (partial.openTabsRestoreMode !== undefined) editorSettings.value.openTabsRestoreMode = normalizeOpenTabsRestoreMode(partial.openTabsRestoreMode);
|
||||
if (partial.disconnectTabHandlingMode !== undefined) editorSettings.value.disconnectTabHandlingMode = normalizeDisconnectTabHandlingMode(partial.disconnectTabHandlingMode);
|
||||
if (partial.deleteConnectionTabHandlingMode !== undefined) editorSettings.value.deleteConnectionTabHandlingMode = normalizeDeleteConnectionTabHandlingMode(partial.deleteConnectionTabHandlingMode);
|
||||
if (partial.rememberConnectionDatabaseOnDelete !== undefined) editorSettings.value.rememberConnectionDatabaseOnDelete = partial.rememberConnectionDatabaseOnDelete === true;
|
||||
if (partial.rememberedConnectionDatabases !== undefined) editorSettings.value.rememberedConnectionDatabases = normalizeRememberedConnectionDatabases(partial.rememberedConnectionDatabases);
|
||||
if (partial.dataTabReuseMode !== undefined) editorSettings.value.dataTabReuseMode = normalizeDataTabReuseMode(partial.dataTabReuseMode);
|
||||
if (partial.openDataTabsNextToActive !== undefined) editorSettings.value.openDataTabsNextToActive = partial.openDataTabsNextToActive === true;
|
||||
if (partial.prefillNewQueryWithSelect !== undefined) editorSettings.value.prefillNewQueryWithSelect = partial.prefillNewQueryWithSelect;
|
||||
@@ -2526,6 +2586,46 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量记住「连接名 → 数据库名 + 类型」;数据库名或类型为空表示清除该条目。
|
||||
* 删除连接时写入,新建同名同类型连接时回填。整体只触发一次设置落盘。
|
||||
*/
|
||||
function rememberConnectionDatabases(entries: Iterable<readonly [string, string | undefined, string | undefined]>) {
|
||||
const next = { ...editorSettings.value.rememberedConnectionDatabases };
|
||||
let changed = false;
|
||||
for (const [connectionName, database, dbType] of entries) {
|
||||
const name = connectionName.trim();
|
||||
const value = database?.trim();
|
||||
if (!name || !value || !dbType) {
|
||||
if (name && name in next) {
|
||||
delete next[name];
|
||||
changed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const previous = next[name];
|
||||
if (previous?.database === value && previous.dbType === dbType) continue;
|
||||
// 重新插入以刷新写入顺序,让上限裁剪优先淘汰最旧的条目。
|
||||
delete next[name];
|
||||
next[name] = { database: value, dbType };
|
||||
changed = true;
|
||||
}
|
||||
if (!changed) return;
|
||||
updateEditorSettings({ rememberedConnectionDatabases: next });
|
||||
}
|
||||
|
||||
/** 只在连接名与数据库类型都匹配时返回记住的数据库;类型不同视为无关记录。 */
|
||||
function rememberedDatabaseForConnection(connectionName: string, dbType: string): string {
|
||||
const name = connectionName.trim();
|
||||
if (!name || !dbType) return "";
|
||||
const entry = editorSettings.value.rememberedConnectionDatabases[name];
|
||||
return entry && entry.dbType === dbType ? entry.database : "";
|
||||
}
|
||||
|
||||
function clearRememberedConnectionDatabases() {
|
||||
updateEditorSettings({ rememberedConnectionDatabases: {} });
|
||||
}
|
||||
|
||||
function updateColumnFormatter(key: string, formatter: ColumnFormatterConfig | undefined) {
|
||||
const columnFormatters = { ...editorSettings.value.columnFormatters };
|
||||
const normalized = normalizeColumnFormatter(formatter);
|
||||
@@ -2677,6 +2777,9 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
initMcpGlobalPolicy,
|
||||
updateMcpGlobalPolicy,
|
||||
updateColumnFormatter,
|
||||
rememberConnectionDatabases,
|
||||
rememberedDatabaseForConnection,
|
||||
clearRememberedConnectionDatabases,
|
||||
customColumnFormatterDeleteVersion,
|
||||
upsertCustomColumnFormatter,
|
||||
deleteCustomColumnFormatter,
|
||||
|
||||
Reference in new issue
Block a user