Timeline scroll fix

This commit is contained in:
Nick Beckley 2026-06-02 22:03:23 +01:00
parent f1279f55ee
commit 66e21f7cc5

View File

@ -450,6 +450,7 @@
const scene = payload.scenes[points[0].index];
if (scene?.timelineUrl) {
window.PlotLineTimelineScroll?.save();
window.location.href = scene.timelineUrl;
}
});
@ -534,6 +535,15 @@
const collapsedKey = "plotline.timeline.collapsed";
const densityKey = "plotline.timeline.density";
const zoomKey = "plotline.timeline.zoom";
const scrollKey = "plotline.timeline.pending-scroll";
const scrollContainerSelector = [
".timeline-shell",
".plot-lane-board",
".asset-lane-board",
".character-lane-board",
".metric-graph-stack",
"[data-timeline-minimap]"
].join(",");
const densityValues = ["compact", "comfortable", "expanded"];
const zoomValues = ["book", "chapter", "scene-detail"];
@ -548,6 +558,62 @@
const hasCollapsedPreference = localStorage.getItem(collapsedKey) !== null;
const collapsed = readSet(collapsedKey);
const getScrollContainers = () => [...root.querySelectorAll(scrollContainerSelector)];
const saveScrollPosition = () => {
try {
sessionStorage.setItem(scrollKey, JSON.stringify({
path: window.location.pathname,
top: window.scrollY,
left: window.scrollX,
workspaceLeft: workspace.scrollLeft,
workspaceTop: workspace.scrollTop,
containers: getScrollContainers().map((container, index) => ({
index,
left: container.scrollLeft,
top: container.scrollTop
}))
}));
} catch {
// Storage can be unavailable in private or restricted browser modes.
}
};
const restoreScrollPosition = () => {
let scrollPosition = null;
try {
scrollPosition = JSON.parse(sessionStorage.getItem(scrollKey) || "null");
sessionStorage.removeItem(scrollKey);
} catch {
try {
sessionStorage.removeItem(scrollKey);
} catch {
// Ignore storage failures; normal navigation should continue.
}
return;
}
if (!scrollPosition || scrollPosition.path !== window.location.pathname) {
return;
}
window.requestAnimationFrame(() => {
workspace.scrollLeft = Number(scrollPosition.workspaceLeft) || 0;
workspace.scrollTop = Number(scrollPosition.workspaceTop) || 0;
const containers = getScrollContainers();
(scrollPosition.containers || []).forEach((saved) => {
const container = containers[saved.index];
if (container) {
container.scrollLeft = Number(saved.left) || 0;
container.scrollTop = Number(saved.top) || 0;
}
});
window.scrollTo(Number(scrollPosition.left) || 0, Number(scrollPosition.top) || 0);
});
};
window.PlotLineTimelineScroll = { save: saveScrollPosition };
const applyMode = (kind, value, values) => {
values.forEach((item) => root.classList.remove(`${kind}-${item}`));
root.classList.add(`${kind}-${value}`);
@ -638,7 +704,38 @@
zoomSelect?.addEventListener("change", () => applyZoom(zoomSelect.value));
applyDensity(localStorage.getItem(densityKey) || "comfortable");
applyZoom(localStorage.getItem(zoomKey) || "chapter");
window.requestAnimationFrame(() => scrollActiveChapterIntoView("auto"));
const hadPendingScrollRestore = (() => {
try {
return sessionStorage.getItem(scrollKey) !== null;
} catch {
return false;
}
})();
window.requestAnimationFrame(() => {
scrollActiveChapterIntoView("auto");
if (hadPendingScrollRestore) {
restoreScrollPosition();
}
});
const saveScrollForSceneSelection = (event) => {
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
return;
}
const target = event.target instanceof Element ? event.target : null;
const link = target?.closest("a[href]");
if (!link || !root.contains(link)) {
return;
}
const url = new URL(link.href, window.location.href);
if ([...url.searchParams.keys()].some((key) => key.toLowerCase() === "selectedsceneid")) {
saveScrollPosition();
}
};
root.addEventListener("click", saveScrollForSceneSelection, { capture: true });
const runSearch = () => {
const query = searchInput?.value.trim().toLowerCase() || "";