From e648b15ebdad6c6cbb6fff91363022ea8965e7ec Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Mon, 5 Oct 2026 11:47:35 -0700 Subject: [PATCH] fix: the band's inset survives a resize made in full screen The panel was remembered as a width, sampled while it stood still. A window resized during full screen left that width describing a layout that no longer existed, and the way out pinned the band to it for the length of the move. It is now remembered as the share of the row the panel takes, which the new row width turns back into the right inset: leaving full screen at 1200px after entering it at 1600px, the band starts at 628 rather than at 909. The pane transition's duration is read from the property the timer is set from, so the class cannot outlive the movement it describes, and two comments that described earlier drafts are rewritten: the band's name in full screen comes from the session's own header once it has mounted, and the tab strip's wrapper carries a fade, not a measured move. Co-Authored-By: Claude Opus 5 (1M context) --- .../src/lib/components/NavBreadcrumb.svelte | 5 +- .../(root)/(logged)/sessions/+page.svelte | 57 ++++++++++--------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/frontend/src/lib/components/NavBreadcrumb.svelte b/frontend/src/lib/components/NavBreadcrumb.svelte index bffb4b7c43..496a5c2ca4 100644 --- a/frontend/src/lib/components/NavBreadcrumb.svelte +++ b/frontend/src/lib/components/NavBreadcrumb.svelte @@ -197,8 +197,7 @@ same weight, size and icon size, so the line reads as one control rather than fo
- +
{#snippet children({ createMenu })} diff --git a/frontend/src/routes/(root)/(logged)/sessions/+page.svelte b/frontend/src/routes/(root)/(logged)/sessions/+page.svelte index 32b71510a7..c1acc47acc 100644 --- a/frontend/src/routes/(root)/(logged)/sessions/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/sessions/+page.svelte @@ -467,12 +467,7 @@ if (reducedMotion.val) return panesAnimating = true clearTimeout(panesAnimationTimer) - panesAnimationTimer = setTimeout(() => { - panesAnimating = false - // The panel's final width was measured while the writes above were suppressed, and a - // settled panel may get no further resize to publish it. - if (!fullscreen) panelWidth = previewWidth - }, PANES_MOVE_MS) + panesAnimationTimer = setTimeout(() => (panesAnimating = false), PANES_MOVE_MS) } onDestroy(() => clearTimeout(panesAnimationTimer)) /** The arriving strip fades in over the expansion; the leaving one goes quickly, so the two @@ -490,12 +485,21 @@ } // Width of the preview panel, so the band can stop where it starts. let previewWidth = $state(0) - /** The same width, excluding what the panel measures while it is expanding or contracting for - * a full-screen switch. The band's inset is read from this: fed the live measurement, the bar - * would be sized against a panel that has not arrived yet, and on the way back from full - * screen it starts at 146px — narrow enough that the bar wraps to two rows for a tenth of a - * second before the panel settles. */ - let panelWidth = $state(0) + /** Width of the row the panes share, so the panel can be remembered as a share of it. */ + let contentWidth = $state(0) + /** What share of that row the panel takes, sampled only while it is standing still. The band's + * inset is read from this for the length of a full-screen switch: fed the live measurement, + * the bar would be sized against a panel that has not arrived yet, and on the way back from + * full screen it starts at 146px — narrow enough that the bar wraps to two rows before the + * panel settles. A share rather than a width, so a window resized while full screen still + * gives the right inset on the way out. */ + let panelShare = $state(0) + const bandInset = $derived.by(() => { + if (fullscreen) return 0 + if (panesAnimating && panelShare > 0 && contentWidth > 0) + return Math.round(panelShare * contentWidth) + return previewCollapsed || previewWidth === 0 ? undefined : previewWidth + }) // Fullscreen is page state, not per-session, so it outlives a session switch — // tell the incoming session's model, whose own collapsed flag it overrides, or // re-opening the item plainly on screen would be judged invisible and not flash. @@ -955,15 +959,12 @@ - +
previewWidth, (w) => { previewWidth = w - if (!fullscreen && !panesAnimating) panelWidth = w + if (!fullscreen && !panesAnimating && contentWidth > 0) + panelShare = w / contentWidth }} class="flex-1 min-h-0 flex flex-col {fullscreen ? 'p-0 pt-11' : 'p-2 pl-0'}" > @@ -1321,9 +1325,9 @@ {/snippet} {#snippet previewTabStrip(inBand: boolean)} - +
:global(.splitpanes__pane) { - /* `!important` to beat app.css's blanket `transition: none !important` on vertical panes, + /* The duration comes from PANES_MOVE_MS, which also times how long the class stays on. + `!important` to beat app.css's blanket `transition: none !important` on vertical panes, which is there so a splitter drag tracks the pointer exactly. */ - transition: width 240ms cubic-bezier(0.33, 1, 0.68, 1) !important; + transition: width var(--panes-move) cubic-bezier(0.33, 1, 0.68, 1) !important; } /* Collapsed preview: the pane is resized to 0 but stays mounted, so remove