Timeline is as tall as its lanes need: no reserved space under the last lane, an overlay lane makes it grow

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-11-10 00:33:40 +02:00
co-authored by Claude Sonnet 5.5
parent 609be1a203
commit 3e5a75f87d
2 changed files with 18 additions and 3 deletions
+2
View File
@@ -21,6 +21,7 @@ const inspect = () => {
over: [document.documentElement.scrollWidth - innerWidth, document.documentElement.scrollHeight - innerHeight],
windows: out.filter(({ r }) => r.left < -1 || r.top < -1 || r.right > innerWidth + 1 || r.bottom > innerHeight + 1).map(({ name, r }) => `${name} ${Math.round(r.left)},${Math.round(r.top)}..${Math.round(r.right)},${Math.round(r.bottom)}`),
lanes: lanes.length < 2 ? ["fewer than two lanes"] : lanes.filter((l) => box(l).bottom > box(nle).bottom + 1).map((l) => `lane ends ${Math.round(box(l).bottom)}, timeline ${Math.round(box(nle).bottom)}`),
slack: nle && nle.querySelector(".cx-nle-stage") && nle.clientHeight - nle.querySelector(".cx-nle-stage").offsetHeight > 6 ? [`${Math.round(nle.clientHeight - nle.querySelector(".cx-nle-stage").offsetHeight)}px of empty space under the last lane`] : [],
};
};
@@ -86,6 +87,7 @@ for (const name of wanted) {
const main = await page.evaluate(inspect);
if (main.over[0] > 0 || main.over[1] > 0) note(`page overflows by ${main.over}`);
main.lanes.forEach((m) => note(m));
main.slack.forEach((m) => note(m));
main.header.forEach((m) => note(m));
for (const [label, text] of WINDOWS) {
await page.getByText(text).first().click({ timeout: 1500 }).catch(() => note(`${label} cannot be opened`));
+16 -3
View File
@@ -66,9 +66,22 @@ export function build(root) {
return out;
} catch { return {}; } // nothing, or nothing usable, remembered
})();
// The timeline is never shorter than its lanes need: the stylesheet's own floor for this window width (taller when narrow).
const floor = (k) => k === "--timeline-h" ? (window.matchMedia?.("(max-width: 1250px)").matches ? 380 : 340) : size[k][0];
const apply = () => { for (const k in size) saved[k] ? css.setProperty(k, Math.max(floor(k), saved[k]) + "px") : css.removeProperty(k); };
// The timeline is as tall as its lanes need and no taller: what the zone spends on everything but the lanes (title, toolbar rows,
// which wrap in narrow windows), plus the lanes that exist now. An empty overlay lane takes no room; adding one grows it.
// A taller height the person dragged to is kept; a shorter one is not allowed.
let need = 0;
const floor = (k) => k === "--timeline-h" ? need : size[k][0];
const apply = () => { for (const k in size) { const v = Math.max(floor(k), saved[k] || 0); v ? css.setProperty(k, v + "px") : css.removeProperty(k); } };
const measure = () => {
const zone = main.node.children[1], nle = zone && zone.querySelector(".cx-nle"), stage = nle && nle.querySelector(".cx-nle-stage");
if (!stage || document.documentElement.classList.contains("fp-peek")) return; // nothing drawn yet, or the timeline is folded to its strip
const next = Math.min(size["--timeline-h"][1], Math.round(window.innerHeight * 0.6), Math.ceil(zone.offsetHeight - nle.clientHeight + stage.offsetHeight + (nle.offsetHeight - nle.clientHeight)));
if (next > 0 && next !== need) { need = next; apply(); }
};
let queued = false;
const remeasure = () => { if (queued) return; queued = true; requestAnimationFrame(() => { queued = false; measure(); }); };
if (typeof MutationObserver === "function") new MutationObserver(remeasure).observe(main.node, { childList: true, subtree: true });
if (typeof ResizeObserver === "function") new ResizeObserver(remeasure).observe(main.node);
const keep = () => { try { localStorage.setItem(KEY, JSON.stringify(saved)); } catch { /* not remembered */ } };
const splitter = (host, cls, prop, sign, axis) => {
const bar = document.createElement("div");