improve(tabs): suffix duplicate tab titles with numbers

This commit is contained in:
zipg authored and GitHub committed 2026-09-22 01:53:33 +08:00
1 parent 0edd6554d5
commit 0c156076ea
8 files changed
+94 -15

No files matched your search

@@ -15,6 +15,7 @@ import {
tabColorStyle,
tabDatabaseIconType,
tabDisplayTitle,
tabDisplayTitles,
tabIconClass,
tabTooltipLines,
tabularResultItems,
@@ -191,6 +192,37 @@ describe("query result grid identity", () => {
});
describe("tab group presentation", () => {
it("numbers colliding query tab titles so several tabs stay distinguishable", () => {
const store = useConnectionStore();
store.connections = [{ id: "conn-1", name: "PostgreSQL", db_type: "postgres", driver_profile: "postgres", database: "app" } as ConnectionConfig];
const tabs = [queryTab({ id: "tab-1" }), queryTab({ id: "tab-2" }), queryTab({ id: "tab-3" })];
const titles = tabDisplayTitles(tabs, translate);
expect([...titles.values()]).toEqual(["PostgreSQL@db 1", "PostgreSQL@db 2", "PostgreSQL@db 3"]);
});
it("leaves tab titles untouched while they are unique", () => {
const store = useConnectionStore();
store.connections = [{ id: "conn-1", name: "PostgreSQL", db_type: "postgres", driver_profile: "postgres", database: "app" } as ConnectionConfig];
const tabs = [queryTab({ id: "tab-1" }), queryTab({ id: "tab-2", database: "other" }), queryTab({ id: "tab-3", customTitle: true, title: "orders.sql", savedSqlId: "sql-1" })];
const titles = tabDisplayTitles(tabs, translate);
expect(titles.get("tab-1")).toBe("PostgreSQL@db");
expect(titles.get("tab-2")).toBe("PostgreSQL@other");
expect(titles.get("tab-3")).toBe("orders.sql");
});
it("numbers duplicate custom titles in strip order and skips preview tabs", () => {
const store = useConnectionStore();
store.connections = [{ id: "conn-1", name: "PostgreSQL", db_type: "postgres", driver_profile: "postgres", database: "app" } as ConnectionConfig, { id: "conn-preview", name: "[Preview] PostgreSQL", db_type: "postgres", driver_profile: "postgres", database: "app" } as ConnectionConfig];
const tabs = [queryTab({ id: "tab-1", customTitle: true, title: "orders.sql", savedSqlId: "sql-1" }), queryTab({ id: "tab-preview", connectionId: "conn-preview" }), queryTab({ id: "tab-2", customTitle: true, title: "orders.sql", savedSqlId: "sql-2" })];
const titles = tabDisplayTitles(tabs, translate);
expect(titles.get("tab-1")).toBe("orders.sql 1");
expect(titles.get("tab-2")).toBe("orders.sql 2");
expect(titles.get("tab-preview")).toBe("SQL");
});
it("does not expose the internal objects mode in object browser tab titles", () => {
const store = useConnectionStore();
store.connections = [{ id: "conn-1", name: "PostgreSQL", db_type: "postgres", driver_profile: "postgres", database: "app" } as ConnectionConfig];
@@ -78,6 +78,37 @@ export function isEventObjectBrowserTab(tab: QueryTab): boolean {
return tab.mode === "objects" && (tab.objectBrowser?.initialObjectFilter === "events" || tab.objectBrowser?.eventName !== undefined || tab.objectBrowser?.eventCreateRequestId !== undefined);
}
/**
* Display titles for a whole tab list.
*
* Tabs whose plain title collides (most obviously several query tabs on the
* same connection and database, which all render `connection@database`) get a
* 1-based numeric suffix so the strip stays readable. The first tab keeps no
* suffix when its title is unique, so single-tab windows look exactly as
* before.
*/
export function tabDisplayTitles(tabs: QueryTab[], t: Translate): Map<string, string> {
const titles = new Map<string, string>();
for (const tab of tabs) titles.set(tab.id, tabDisplayTitle(tab, t));
const collisions = new Map<string, number>();
for (const tab of tabs) {
if (isPreviewTab(tab)) continue;
const title = titles.get(tab.id) ?? "";
collisions.set(title, (collisions.get(title) ?? 0) + 1);
}
const seen = new Map<string, number>();
for (const tab of tabs) {
const title = titles.get(tab.id) ?? "";
if (isPreviewTab(tab) || (collisions.get(title) ?? 0) < 2) continue;
const index = (seen.get(title) ?? 0) + 1;
seen.set(title, index);
titles.set(tab.id, `${title} ${index}`);
}
return titles;
}
export function tabDisplayTitle(tab: QueryTab, t: Translate): string {
const database = databaseDisplayNameForTab(tab.connectionId, tab.database, t);
const settingsStore = useSettingsStore();