diff --git a/apps/desktop/src/components/layout/AppTabBar.vue b/apps/desktop/src/components/layout/AppTabBar.vue index d794287e50..9d03dbcf6e 100644 --- a/apps/desktop/src/components/layout/AppTabBar.vue +++ b/apps/desktop/src/components/layout/AppTabBar.vue @@ -8,7 +8,8 @@ import { Button } from "@/components/ui/button"; import { useQueryStore } from "@/stores/queryStore"; import { useSettingsStore } from "@/stores/settingsStore"; import { GROUP_TAB_BAR_PORTAL } from "./groupTabBarPortal"; -import { tabDisplayTitle } from "@/lib/tabs/tabPresentation"; +import { tabDisplayTitle, tabDisplayTitles } from "@/lib/tabs/tabPresentation"; +import type { QueryTab } from "@/types/database"; import "./appTabBar.css"; const props = defineProps<{ @@ -69,9 +70,15 @@ function setTabBarTarget(groupId: string, element: unknown) { const closeConfirmDirtyCount = computed(() => queryStore.closeConfirmDirtyTabIds.length); const showCloseConfirmBulkActions = computed(() => closeConfirmDirtyCount.value > 1); const closeConfirmDirtyTabs = computed(() => queryStore.closeConfirmDirtyTabIds.map((id) => queryStore.tabs.find((tab) => tab.id === id)).filter((tab): tab is NonNullable> => !!tab)); +// Numbered across every open tab so unsaved-change confirmations show the +// same suffixed titles as the tab strip. +const tabTitles = computed(() => tabDisplayTitles(queryStore.tabs, t)); +function closeConfirmTabTitle(tab: QueryTab): string { + return tabTitles.value.get(tab.id) ?? tabDisplayTitle(tab, t); +} const closeConfirmCurrentTitle = computed(() => { const focusedTab = closeConfirmDirtyTabs.value.find((tab) => tab.id === queryStore.pendingCloseTabId) ?? closeConfirmDirtyTabs.value[0]; - return focusedTab ? tabDisplayTitle(focusedTab, t) : ""; + return focusedTab ? closeConfirmTabTitle(focusedTab) : ""; }); const closeConfirmMessage = computed(() => { const params = { @@ -249,7 +256,7 @@ function handleCancelClose() {
- {{ tabDisplayTitle(tab, t) }} + {{ closeConfirmTabTitle(tab) }}
diff --git a/apps/desktop/src/components/layout/EditorGroupTabBar.vue b/apps/desktop/src/components/layout/EditorGroupTabBar.vue index 60dc82c486..d71b235fcd 100644 --- a/apps/desktop/src/components/layout/EditorGroupTabBar.vue +++ b/apps/desktop/src/components/layout/EditorGroupTabBar.vue @@ -83,7 +83,7 @@ import { hexToRgba } from "@/lib/common/color"; import { copyToClipboard } from "@/lib/common/clipboard"; import { parseTabDragPayload, serializeTabDragPayload } from "@/lib/tabs/tabDrag"; import { createCloseAllTabMenuItem, createCloseLeftTabMenuItem, createCloseOtherTabMenuItem, createCloseRightTabMenuItem, createCloseTabMenuItem, createLocateTabMenuItem, createPinTabMenuItem, createRenameDuplicateTabItems } from "@/lib/tabs/tabMenu"; -import { connectionColor, dirtyTabTitleStyle, tabColorStyle as sharedTabColorStyle, tabDatabaseIconType, tabDisplayTitle, tabIconClass, tabTooltipLines } from "@/lib/tabs/tabPresentation"; +import { connectionColor, dirtyTabTitleStyle, tabColorStyle as sharedTabColorStyle, tabDatabaseIconType, tabDisplayTitle, tabDisplayTitles, tabIconClass, tabTooltipLines } from "@/lib/tabs/tabPresentation"; import { activeTabSidebarTarget } from "@/lib/sidebar/sidebarActiveTabTarget"; import "./appTabBar.css"; import type { QueryTab } from "@/types/database"; @@ -266,7 +266,7 @@ const filteredGroupTabs = computed(() => { if (!query) { return props.tabs; } - return props.tabs.filter((tab) => tabDisplayTitle(tab, t).toLocaleLowerCase().includes(query) || tab.title.toLocaleLowerCase().includes(query)); + return props.tabs.filter((tab) => tabTitleText(tab).toLocaleLowerCase().includes(query) || tab.title.toLocaleLowerCase().includes(query)); }); watch(tabOverflowOpen, (open) => { @@ -411,8 +411,12 @@ function compareTabGroupKeys(left: string, right: string) { return localized || (left < right ? -1 : 1); } +// Numbered across every open tab (not just this group's) so a tab keeps the +// same label when the same query tab is moved between panes. +const tabTitles = computed(() => tabDisplayTitles(queryStore.tabs, t)); + function tabTitleText(tab: QueryTab) { - return tabDisplayTitle(tab, t); + return tabTitles.value.get(tab.id) ?? tabDisplayTitle(tab, t); } function tabConnectionLabel(tab: QueryTab) { @@ -843,7 +847,7 @@ type StripEntry = { kind: "header"; key: string; tab: QueryTab; pinned: boolean; * pills. Collapsed pills remain mounted so their visibility can animate. */ function tabMatchesSearch(tab: QueryTab, query: string) { - const title = tabDisplayTitle(tab, t).toLocaleLowerCase(); + const title = tabTitleText(tab).toLocaleLowerCase(); return title.includes(query) || tab.title.toLocaleLowerCase().includes(query); } @@ -1107,7 +1111,7 @@ function getTabMenuItems(tab: QueryTab): ContextMenuItem[] { label: t("contextMenu.copyName"), action: async () => { try { - await copyToClipboard(tabDisplayTitle(tab, t)); + await copyToClipboard(tabTitleText(tab)); toast(t("connection.copied"), 2000); } catch (e: any) { toast(t("grid.copyFailed", { message: e?.message || String(e) }), 5000); @@ -1617,7 +1621,7 @@ watch([() => props.specialPageTabs?.settingsActive, () => props.specialPageTabs? /> - {{ tabDisplayTitle(entry.tab, t) }} + {{ tabTitleText(entry.tab) }}