From 719aa3e849d2d407f44c3a20c47b2357b134edbf Mon Sep 17 00:00:00 2001 From: Lem26 <38876848+Lem26@users.noreply.github.com> Date: Sat, 5 Sep 2026 00:41:15 +0800 Subject: [PATCH] fix(tabs): keep overflow popup search from filtering tab strip --- .../components/layout/EditorGroupTabBar.vue | 18 +++++++++++++----- .../__tests__/EditorGroupTabBar.groups.spec.ts | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/components/layout/EditorGroupTabBar.vue b/apps/desktop/src/components/layout/EditorGroupTabBar.vue index d22dcd75b4..894a6b9870 100644 --- a/apps/desktop/src/components/layout/EditorGroupTabBar.vue +++ b/apps/desktop/src/components/layout/EditorGroupTabBar.vue @@ -176,9 +176,16 @@ const tabScrollbarThumbStyle = computed(() => ({ // Overflow search lists this group's tabs (mirrors the legacy AppTabBar // overflow popover, scoped to the group that owns the strip). const tabOverflowOpen = ref(false); +// The overflow popover's "search opened tabs" query is scoped to the popover +// list only. It must not reach the strip: with a shared query, typing a term +// with no match would empty the always-visible top tab bar while the active +// tab's content stays on screen. +const tabOverflowSearchQuery = ref(""); +// The strip's own search box lives only in the vertical toolbar, where +// filtering the strip itself is the point of the search. const tabSearchQuery = ref(""); const filteredGroupTabs = computed(() => { - const query = tabSearchQuery.value.trim().toLocaleLowerCase(); + const query = tabOverflowSearchQuery.value.trim().toLocaleLowerCase(); if (!query) { return props.tabs; } @@ -186,7 +193,7 @@ const filteredGroupTabs = computed(() => { }); watch(tabOverflowOpen, (open) => { - tabSearchQuery.value = ""; + tabOverflowSearchQuery.value = ""; if (open) { nextTick(() => document.querySelector("[data-group-tab-search-input]")?.focus()); } @@ -523,8 +530,9 @@ function tabMatchesSearch(tab: QueryTab, query: string) { } /** - * The strips apply the search box (overflow popover and vertical toolbar - * share it): sections filter by title before clustering. + * The strips apply the vertical toolbar search box: sections filter by title + * before clustering. The overflow popover's query (tabOverflowSearchQuery) is + * scoped to the popover list and deliberately does not reach the strip. */ const filteredPinnedTabs = computed(() => { const query = tabSearchQuery.value.trim().toLocaleLowerCase(); @@ -1201,7 +1209,7 @@ watch(
- +
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 de8a4c1b7e..2bbb3eba1c 100644 --- a/apps/desktop/src/components/layout/__tests__/EditorGroupTabBar.groups.spec.ts +++ b/apps/desktop/src/components/layout/__tests__/EditorGroupTabBar.groups.spec.ts @@ -97,6 +97,21 @@ describe("EditorGroupTabBar semantic tab groups", () => { expect(source).toContain("grouping && !tabSearchQuery.value.trim() && isTabGroupCollapsed(tab)"); }); + it("keeps the overflow search scoped to the popover list without filtering the strip", () => { + // Regression: the popover's "search opened tabs" box shared the strip's + // query, so typing a term with no match emptied the top tab bar while the + // active tab's content stayed on screen. + const overflowFilter = sourceBetween("const filteredGroupTabs", "watch(tabOverflowOpen"); + expect(overflowFilter).toContain("tabOverflowSearchQuery.value.trim()"); + expect(overflowFilter).not.toContain("tabSearchQuery"); + const overflowOpenWatch = sourceBetween("watch(tabOverflowOpen", "const showOverflowControl"); + expect(overflowOpenWatch).toContain('tabOverflowSearchQuery.value = "";'); + const stripFilter = sourceBetween("const filteredPinnedTabs", "const stripEntries"); + expect(stripFilter).toContain("tabSearchQuery.value.trim()"); + expect(stripFilter).not.toContain("tabOverflowSearchQuery"); + expect(source).toContain(' { expect(source).toContain('const isWrapLayout = computed(() => !isVerticalLayout.value && settingsStore.editorSettings.tabLayout === "wrap");'); });