fix(web): guard dockview measure against mid-transition sidebar width (#1288)

* fix(web): guard dockview measure against mid-transition sidebar width

The unified AppSidebar (#1165) animates its width over 300ms; the dockview
content container (a flex sibling) can be measured mid-transition at a tiny
positive width in onReady, so the default layout builds too narrow and dockview
collapses the horizontal columns into a vertical stack. measureDockviewContainer
trusted any positive parent clientWidth. Reject an implausibly-small live width
(< 50% of viewport) as a not-yet-laid-out transient and fall back to the
viewport; the resize observer then snaps to the exact size.

* fix(web): preserve live height when width guard fires; assert it
This commit is contained in:
José Almeida
2026-06-07 12:35:29 +01:00
committed by GitHub
parent 91d5861519
commit 2ed65cfde3
2 changed files with 41 additions and 1 deletions
@@ -23,6 +23,29 @@ describe("measureDockviewContainer", () => {
expect(measureDockviewContainer(fakeApi(0, 0))).toEqual({ width: 1500, height: 700 });
});
it("ignores an implausibly-narrow live width (sidebar mid-transition) but keeps the live height", () => {
// Regression (post unified-sidebar #1165): the AppSidebar is a root-layout
// flex sibling with `transition-all duration-300`, so the dockview content
// container can be measured mid-animation at a tiny positive width. Building
// the default layout at that width collapses the horizontal columns into a
// vertical stack (chat / files+changes / terminal). On desktop the sidebar
// maxes at 30vw, so content is always >= ~70vw — a sub-half-viewport read is
// definitionally a transient: fall back the width to the viewport so the
// build stays horizontal (the resize observer then snaps to the exact size).
// The animation is horizontal, so the live height is reliable and is kept.
const parent = document.createElement("div");
Object.defineProperty(parent, "clientWidth", { value: 80, configurable: true });
Object.defineProperty(parent, "clientHeight", { value: 700, configurable: true });
const dv = document.createElement("div");
dv.className = "dv-dockview";
parent.appendChild(dv);
document.body.appendChild(parent);
const result = measureDockviewContainer(fakeApi(0, 0));
expect(result.width).toBe(window.innerWidth);
expect(result.height).toBe(700);
});
it("never returns a zero size on a fresh mount (no container, api not laid out yet)", () => {
// Regression: a 0×0 measurement builds the default layout at zero width, so
// dockview collapses the horizontal columns into a vertical stack (chat /
+18 -1
View File
@@ -25,12 +25,29 @@ export function measureDockviewContainer(api: DockviewApi): { width: number; hei
};
}
// The dockview content area sits next to the unified AppSidebar (#1165), whose
// width animates over 300ms (`transition-all`). During that animation `onReady`
// can read the parent at a tiny positive width — building the default layout at
// that width collapses the horizontal columns into a vertical stack. The
// sidebar maxes at 30vw (desktop-only surface), so a healthy content width is
// always >= ~70vw; anything below half the viewport is a mid-transition
// transient and must be treated like a not-yet-laid-out (0-width) container.
const MIN_PLAUSIBLE_WIDTH_RATIO = 0.5;
function liveContainerSize(): { width: number; height: number } | null {
if (typeof document === "undefined") return null;
const dv = document.querySelector(".dv-dockview") as HTMLElement | null;
const parent = dv?.parentElement;
if (!parent || parent.clientWidth <= 0 || parent.clientHeight <= 0) return null;
return { width: parent.clientWidth, height: parent.clientHeight };
// The sidebar animation is horizontal, so only the width can be a transient —
// the live height is reliable. Fall back just the width to the viewport so the
// build stays horizontal; keep the real height to avoid a needless vertical
// transient. The resize observer then snaps the width to the exact size.
const plausibleWidth = parent.clientWidth >= viewportWidth() * MIN_PLAUSIBLE_WIDTH_RATIO;
return {
width: plausibleWidth ? parent.clientWidth : viewportWidth(),
height: parent.clientHeight,
};
}
function viewportWidth(): number {