diff --git a/website_next/AGENTS.md b/website_next/AGENTS.md index 1ea5f041c..7b6fbd267 100644 --- a/website_next/AGENTS.md +++ b/website_next/AGENTS.md @@ -21,6 +21,7 @@ ALWAYS - very well organized - contained - colocated +- idiomatic - composed - prefer one concept per file - prefer more files and folders than big files diff --git a/website_next/home/style.css b/website_next/home/style.css index 18837ec11..273916f7f 100644 --- a/website_next/home/style.css +++ b/website_next/home/style.css @@ -1,9 +1,11 @@ main.home { + --home-offset: 6rem; + display: grid; gap: 2rem; place-items: center; align-content: center; - padding: var(--offset, 6rem) var(--page-x); + padding: var(--home-offset) var(--page-x); h1 { margin: 0; diff --git a/website_next/learn/charts/index.js b/website_next/learn/charts/index.js index 2bf86bb69..bc0bc533b 100644 --- a/website_next/learn/charts/index.js +++ b/website_next/learn/charts/index.js @@ -71,22 +71,22 @@ async function loadSeries(chart, timeframe) { /** @param {Chart} chart */ function createLoadedSeriesCache(chart) { - /** @type {Map>} */ - const cache = new Map(); + /** @type {TimeframeValue | undefined} */ + let cachedTimeframe; + /** @type {Promise | undefined} */ + let cachedPromise; /** @param {TimeframeValue} timeframe */ return function getLoadedSeries(timeframe) { - let promise = cache.get(timeframe); - - if (!promise) { - promise = loadSeries(chart, timeframe).catch((error) => { - cache.delete(timeframe); + if (timeframe !== cachedTimeframe || !cachedPromise) { + cachedTimeframe = timeframe; + cachedPromise = loadSeries(chart, timeframe).catch((error) => { + if (timeframe === cachedTimeframe) cachedPromise = undefined; throw error; }); - cache.set(timeframe, promise); } - return promise; + return cachedPromise; }; } diff --git a/website_next/learn/charts/scrubber.js b/website_next/learn/charts/scrubber.js index 5ac69cadc..ad578df86 100644 --- a/website_next/learn/charts/scrubber.js +++ b/website_next/learn/charts/scrubber.js @@ -74,6 +74,8 @@ export function createScrubber(svg, readout, highlight) { * @param {boolean} [scrubbing] */ function update(ratio, scrubbing = true) { + if (!series.length) return; + const nextRatio = clamp(ratio, 0, 1); const points = series.map((item) => getPointAtRatio(item, nextRatio)); const x = points[0].x.toFixed(2); diff --git a/website_next/learn/hash-links.js b/website_next/learn/hash-links.js index 512aa89ee..bed318c46 100644 --- a/website_next/learn/hash-links.js +++ b/website_next/learn/hash-links.js @@ -18,10 +18,18 @@ function scrollToTarget(target, behavior) { target.scrollIntoView({ behavior, block: "start" }); } +/** + * @param {HTMLElement} main + * @param {ScrollBehavior} behavior + */ +function scrollToCurrentHash(main, behavior) { + const target = getHashTarget(main, window.location.hash); + + if (target) scrollToTarget(target, behavior); +} + /** @param {HTMLElement} main */ export function initHashLinks(main) { - const initialHash = window.location.hash; - main.addEventListener("click", (event) => { if (!isPlainLeftClick(event)) return; @@ -45,12 +53,8 @@ export function initHashLinks(main) { window.addEventListener("popstate", () => { if (main.hidden) return; - const target = getHashTarget(main, window.location.hash); - if (target) scrollToTarget(target, "auto"); + scrollToCurrentHash(main, "auto"); }); - requestAnimationFrame(() => { - const target = getHashTarget(main, initialHash); - if (target) scrollToTarget(target, "auto"); - }); + main.addEventListener("pageactive", () => scrollToCurrentHash(main, "auto")); } diff --git a/website_next/main.js b/website_next/main.js index 7228f79e3..7b6093629 100644 --- a/website_next/main.js +++ b/website_next/main.js @@ -37,6 +37,7 @@ function activatePage(page) { page.hidden = false; page.inert = false; currentPage = page; + page.dispatchEvent(new Event("pageactive")); } function renderPage() { @@ -44,10 +45,10 @@ function renderPage() { activatePage(getPage(pathname)); } -/** @param {string} pathname */ -function navigate(pathname) { - if (pathname === window.location.pathname) return; - history.pushState(null, "", pathname); +/** @param {string} path */ +function navigate(path) { + if (path === `${window.location.pathname}${window.location.hash}`) return; + history.pushState(null, "", path); void transitionPage(renderPage); } @@ -64,7 +65,7 @@ document.addEventListener("click", (event) => { if (!isRoute(url.pathname)) return; event.preventDefault(); - navigate(url.pathname); + navigate(`${url.pathname}${url.hash}`); }); window.addEventListener("popstate", renderPage); diff --git a/website_next/utils/event.js b/website_next/utils/event.js index 545d3bffe..660edcd31 100644 --- a/website_next/utils/event.js +++ b/website_next/utils/event.js @@ -19,6 +19,7 @@ export function getEventAnchor(event) { export function isPlainLeftClick(event) { return ( event.button === 0 && + !event.altKey && !event.metaKey && !event.ctrlKey && !event.shiftKey