From b81f18e4f5cdcaf3886b08e8f8f45a1490e13d34 Mon Sep 17 00:00:00 2001 From: zipg Date: Mon, 19 Oct 2026 20:01:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(tabs):=20=E6=9C=AA=E5=88=86=E7=BB=84?= =?UTF-8?q?=E6=A8=AA=E7=89=88=E6=A0=87=E7=AD=BE=E4=BF=9D=E7=95=99=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=85=8D=E7=BD=AE=E7=9A=84=E8=BF=9E=E6=8E=A5=E5=BA=95?= =?UTF-8?q?=E8=89=B2=20(#12756)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zipg <4047349+zipg@users.noreply.github.com> --- .../components/layout/EditorGroupTabBar.vue | 32 +++++++++++--- .../EditorGroupTabBar.groups.spec.ts | 43 +++++++++++++++++++ 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/apps/desktop/src/components/layout/EditorGroupTabBar.vue b/apps/desktop/src/components/layout/EditorGroupTabBar.vue index 8e5a891503..af73885787 100644 --- a/apps/desktop/src/components/layout/EditorGroupTabBar.vue +++ b/apps/desktop/src/components/layout/EditorGroupTabBar.vue @@ -103,7 +103,20 @@ import { createSaveAllTabsMenuItem, } from "@/lib/tabs/tabMenu"; import { formatShortcut } from "@/lib/editor/shortcutRegistry"; -import { findTabWithSameTitleSuffix, joinTabTitleParts, TAB_TITLE_SUFFIX_MAX_LENGTH, tabConnectionColor, dirtyTabTitleStyle, tabColorStyle as sharedTabColorStyle, tabDatabaseIconType, tabDisplayTitle, tabDisplayTitleParts, tabIconClass, tabTooltipLines } from "@/lib/tabs/tabPresentation"; +import { + connectionColor, + findTabWithSameTitleSuffix, + joinTabTitleParts, + TAB_TITLE_SUFFIX_MAX_LENGTH, + tabConnectionColor, + dirtyTabTitleStyle, + tabColorStyle as sharedTabColorStyle, + tabDatabaseIconType, + tabDisplayTitle, + tabDisplayTitleParts, + tabIconClass, + tabTooltipLines, +} from "@/lib/tabs/tabPresentation"; import { activeTabSidebarTarget } from "@/lib/sidebar/sidebarActiveTabTarget"; import "./appTabBar.css"; import type { QueryTab } from "@/types/database"; @@ -1112,9 +1125,9 @@ const stripSections = computed(() => { }); function tabColorStyle(tab: QueryTab): CSSProperties | undefined { - // Keep ungrouped sidebar tabs aligned with the horizontal tab strip: every - // tab gets a connection-colored wash, while the active tab gets the stronger - // border and leading accent. + // Grouped sidebar tabs rely on the tree rails; ungrouped ones keep a + // connection-colored wash (configured or generated), with a stronger border + // and leading accent on the active tab. if (isVerticalLayout.value) { if (settingsStore.editorSettings.tabGroupMode !== "none" || !settingsStore.editorSettings.colorizeConnectionTabs) return undefined; const active = isTabActive(tab); @@ -1130,10 +1143,15 @@ function tabColorStyle(tab: QueryTab): CSSProperties | undefined { : { borderColor: hexToRgba(color, 0.18) }), } as CSSProperties; } - // Keep horizontal tabs quiet by default; group headers and rails already - // provide the connection identity, while the selected tab gets the wash. if (!isTabActive(tab)) { - return undefined; + // Grouped horizontal tabs already show the group identity on the header + // and rail (the connection color when grouping by connection), so keep + // their washes quiet to make the active tab easy to spot (issue #12485). + // Ungrouped tabs have no rail: a color the user configured in the + // connection settings still marks them, while the auto-generated hue stays + // quiet. + const grouped = settingsStore.editorSettings.tabGroupMode !== "none"; + if (grouped || !connectionColor(tab.connectionId)) return undefined; } return sharedTabColorStyle(tab, isTabActive(tab), isClassicLayout.value); } diff --git a/apps/desktop/src/components/layout/__tests__/EditorGroupTabBar.groups.spec.ts b/apps/desktop/src/components/layout/__tests__/EditorGroupTabBar.groups.spec.ts index 6684adbfa8..4b88bd816a 100644 --- a/apps/desktop/src/components/layout/__tests__/EditorGroupTabBar.groups.spec.ts +++ b/apps/desktop/src/components/layout/__tests__/EditorGroupTabBar.groups.spec.ts @@ -222,6 +222,49 @@ describe("EditorGroupTabBar group behavior", () => { } }); + it.each(["classic", "separated"] as const)("keeps configured connection colors on inactive horizontal tabs when grouping is off in %s layout", async (layout) => { + const store = useQueryStore(); + const settings = useSettingsStore(); + const connectionStore = useConnectionStore(); + connectionStore.connections = [{ id: "pg-1", name: "Postgres", db_type: "postgres", driver_profile: "postgres", color: "#3b82f6" } as ConnectionConfig, { id: "pg-2", name: "Auto", db_type: "postgres", driver_profile: "postgres", color: "" } as ConnectionConfig]; + settings.editorSettings.appLayout = layout; + settings.editorSettings.tabGroupMode = "none"; + settings.editorSettings.tabPlacement = "top"; + settings.editorSettings.colorizeConnectionTabs = true; + const activeTabId = store.createTab("pg-1", "app", "PG 1", "query"); + const configuredTabId = store.createTab("pg-1", "app", "PG 2", "query"); + const autoTabId = store.createTab("pg-2", "app", "PG 3", "query"); + const { app, host } = mountBar(store.groups[0]!.id, store.tabs.slice(), activeTabId, pinia); + + try { + for (const placement of ["top", "bottom"] as const) { + settings.editorSettings.tabPlacement = placement; + await settle(); + const activeTab = host.querySelector(`[data-tab-id="${activeTabId}"]`)!; + const configuredTab = host.querySelector(`[data-tab-id="${configuredTabId}"]`)!; + const autoTab = host.querySelector(`[data-tab-id="${autoTabId}"]`)!; + const activeBackground = activeTab.style.getPropertyValue("--app-tab-background"); + const configuredBackground = configuredTab.style.getPropertyValue("--app-tab-background"); + expect(configuredTab.getAttribute("data-active-tab")).toBe("false"); + // A color the user picked in the connection settings keeps marking its + // tabs when no group header/rail carries it; auto-generated hues stay + // quiet and the active tab still reads stronger than its peers. + expect(configuredBackground).toBe(layout === "classic" ? "rgba(59, 130, 246, 0.07)" : "rgba(59, 130, 246, 0.09)"); + expect(autoTab.style.getPropertyValue("--app-tab-background")).toBe(""); + expect(activeBackground).toBe("rgba(59, 130, 246, 0.24)"); + } + + // Turning the connection-tab colors off must silence the inactive wash + // even for a configured connection color. + settings.editorSettings.colorizeConnectionTabs = false; + await settle(); + expect(host.querySelector(`[data-tab-id="${configuredTabId}"]`)!.style.getPropertyValue("--app-tab-background")).toBe(""); + } finally { + app.unmount(); + host.remove(); + } + }); + it("renders one header per connection cluster and collapses it to a count badge", async () => { const store = useQueryStore(); const settings = useSettingsStore();