From a1e987ec0f69a975f2fbd6810d8f881531a6f98d Mon Sep 17 00:00:00 2001 From: Melody Ruch <396921921@qq.com> Date: Sat, 5 Sep 2026 22:06:21 +0800 Subject: [PATCH] fix(layout): avoid blank column-mode workspace on special tabs --- apps/desktop/src/App.vue | 7 ++++ ...ceLayout.column-mode-special-pages.spec.ts | 41 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 apps/desktop/src/components/layout/__tests__/tabWorkspaceLayout.column-mode-special-pages.spec.ts diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index a71a4ae3bd..bd05087207 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -911,8 +911,15 @@ useScheduledDatabaseBackups({ scheduler: true }); const appVersion = ref(""); const isClassicLayout = computed(() => settingsStore.editorSettings.appLayout === "classic"); const isVerticalTabPlacement = computed(() => settingsStore.editorSettings.tabPlacement === "left" || settingsStore.editorSettings.tabPlacement === "right"); +// AppTabBar only renders the settings/driver special-page strip, and that strip +// is still a horizontal-only "w-full" bar (see AppTabBar.vue's app-tab-bar class). +// Placing it inside a flex-row/flex-row-reverse workspace squeezes the sibling +// content pane (EditorSettingsPage/DriverStorePage) to zero width, so fall back +// to stacking it above the content instead of adopting the vertical layout here. +const isSpecialPageStripActive = computed(() => driverStoreActive.value || settingsStore.settingsPageActive); const tabWorkspaceLayoutClass = computed(() => { if (settingsStore.editorSettings.tabPlacement === "bottom") return "flex-col-reverse"; + if (isVerticalTabPlacement.value && isSpecialPageStripActive.value) return "flex-col"; if (settingsStore.editorSettings.tabPlacement === "right") return "flex-row-reverse"; return isVerticalTabPlacement.value ? "flex-row" : "flex-col"; }); diff --git a/apps/desktop/src/components/layout/__tests__/tabWorkspaceLayout.column-mode-special-pages.spec.ts b/apps/desktop/src/components/layout/__tests__/tabWorkspaceLayout.column-mode-special-pages.spec.ts new file mode 100644 index 0000000000..f335e16d4b --- /dev/null +++ b/apps/desktop/src/components/layout/__tests__/tabWorkspaceLayout.column-mode-special-pages.spec.ts @@ -0,0 +1,41 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const appSource = readFileSync(new URL("../../../App.vue", import.meta.url), "utf8"); +const tabBarSource = readFileSync(new URL("../AppTabBar.vue", import.meta.url), "utf8"); + +function sourceBetween(source: string, start: string, end: string): string { + const startIndex = source.indexOf(start); + const endIndex = source.indexOf(end, startIndex + start.length); + expect(startIndex).toBeGreaterThanOrEqual(0); + expect(endIndex).toBeGreaterThan(startIndex); + return source.slice(startIndex, endIndex); +} + +// Regression test for #8213: AppTabBar's settings/driver special-page strip is +// still a horizontal-only "w-full" bar. Placing it inside a flex-row / +// flex-row-reverse workspace (left/right column tab placement) squeezes the +// sibling content pane (EditorSettingsPage/DriverStorePage) to zero width, +// making the settings/driver page invisible after it's reopened. The fix +// falls back to a flex-col workspace while either special page is active. +describe("tabWorkspaceLayoutClass column-mode special-page fallback", () => { + const computedBlock = sourceBetween(appSource, "const tabWorkspaceLayoutClass = computed(() => {", "});"); + + it("falls back to flex-col while the settings/driver strip is active, before the vertical branch", () => { + const specialPageGuardIndex = computedBlock.indexOf("isSpecialPageStripActive.value"); + const verticalRowIndex = computedBlock.indexOf('isVerticalTabPlacement.value ? "flex-row" : "flex-col"'); + expect(specialPageGuardIndex).toBeGreaterThanOrEqual(0); + expect(verticalRowIndex).toBeGreaterThan(specialPageGuardIndex); + expect(computedBlock).toMatch(/isVerticalTabPlacement\.value\s*&&\s*isSpecialPageStripActive\.value\)\s*return\s*"flex-col"/); + }); + + it("derives isSpecialPageStripActive from the same flags AppTabBar uses to render", () => { + expect(appSource).toContain("const isSpecialPageStripActive = computed(() => driverStoreActive.value || settingsStore.settingsPageActive);"); + // AppTabBar itself only renders while one of these is true - keep the guard in sync with it. + expect(tabBarSource).toContain('v-if="driverStoreActive || settingsPageActive"'); + }); + + it("still stretches the special-page strip full width (the part the flex-col fallback protects against)", () => { + expect(tabBarSource).toMatch(/class="app-tab-bar[^"]*\bw-full\b/); + }); +});