Merge pull request #748: fix(tabs): keep the L-shaped strip whichever tab is selected

This commit is contained in:
Codeman maintainer committed 2026-11-04 16:47:48 +01:00
commit 9d6f84f446
5 files changed
+151 -8

No files matched your search

+29 -3
View File
@@ -7035,6 +7035,8 @@ class CodemanApp {
container.classList.remove('tabs-corner');
container.style.removeProperty('--tab-corner-width');
container.style.removeProperty('--tab-corner-height');
container.style.removeProperty('--tab-corner-first-max');
for (const tab of container.querySelectorAll(':scope > .tab-corner-fit')) tab.classList.remove('tab-corner-fit');
};
// A strip past its max-height scrolls, and the float would scroll its tabs
// under the toolbar, which stays put: keep the plain wrap. The plain strip
@@ -7073,12 +7075,36 @@ class CodemanApp {
const contentRight = strip.left + container.clientLeft + container.clientWidth - parseFloat(style.paddingRight);
// A first tab that cannot fit beside the toolbar would drop the whole first
// line below it and leave an empty band: keep the plain strip instead.
const firstTab = container.querySelector(':scope > .session-tab');
if (firstTab && box.left - 8 - contentLeft < firstTab.offsetWidth + 2) {
// The first tab must fit beside the toolbar, or the whole first line drops
// below it and leaves an empty band. A selected tab is wider (gear and
// close show), so falling back to the plain strip here made the layout
// follow the selection; the first tab's name is trimmed to the room
// instead. Only a toolbar that leaves no usable room keeps the plain strip.
// The room is what the float (rounded up below) leaves, less the tab's 2px
// right margin and a pixel for subpixel layout; a tab at the exact width
// still wrapped under the float.
const floatWidth = Math.max(0, Math.ceil(contentRight - box.left + 8));
const room = Math.floor(contentRight - floatWidth - contentLeft) - 3;
if (room < TAB_CORNER_MIN_FIRST_TAB) {
off();
return;
}
const width = `${Math.max(0, Math.ceil(contentRight - box.left + 8))}px`;
const firstTab = container.querySelector(':scope > .session-tab');
// Cleared first, the first tab's included: the last pass's trim would
// otherwise be measured as its width, and a room that grew would untrim a
// tab still too wide for it.
for (const tab of container.querySelectorAll(':scope > .tab-corner-fit')) tab.classList.remove('tab-corner-fit');
// A selection change slides the gear and close open or shut (a width
// transition), and this runs in the same render: finish the first tab's
// slide, or it is judged by the width it is leaving.
for (const anim of firstTab?.getAnimations?.({ subtree: true }) ?? []) {
if (/^(width|padding)/.test(anim.transitionProperty ?? '')) anim.finish();
}
if (firstTab && firstTab.offsetWidth > room) firstTab.classList.add('tab-corner-fit');
if (container.style.getPropertyValue('--tab-corner-first-max') !== `${room}px`) {
container.style.setProperty('--tab-corner-first-max', `${room}px`);
}
const width = `${floatWidth}px`;
const height = `${Math.max(0, Math.ceil(box.bottom - contentTop))}px`;
if (container.style.getPropertyValue('--tab-corner-width') !== width) {
container.style.setProperty('--tab-corner-width', width);
+5
View File
@@ -281,6 +281,11 @@ function resolveTabRailKeyboardWidth(input = {}) {
// the gap is what tells the user there is more strip to swipe to.
const TAB_SCROLL_REVEAL_PX = 16;
// Narrowest room beside the toolbar the L-shaped header strip trims its first
// tab into (`_syncTabStripCorner()`, app.js). Less than this keeps the plain
// wrap: a tab squeezed further shows its number and buttons but no name.
const TAB_CORNER_MIN_FIRST_TAB = 120;
// Phone/tablet tab-strip scroll policy (issue #257). Those breakpoints scroll
// the strip horizontally (desktop wraps to a second row instead and never
// scrolls), so the active tab can sit entirely outside the visible slice with
+7
View File
@@ -651,6 +651,13 @@ body {
margin: 0 2px var(--lineage-row-gap, 4px) 0;
}
/* A first tab wider than the room beside the toolbar (a selected one shows its
gear and close) has its name trimmed to fit, so the layout never depends on
which tab is selected. */
.session-tabs-host > .session-tabs.tabs-corner > .session-tab.tab-corner-fit {
max-width: var(--tab-corner-first-max, none);
}
.session-tabs-host > .session-tabs.lineage-tree.tabs-corner {
padding-bottom: calc(var(--lineage-bottom-room) - var(--lineage-row-gap));
}