Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit af992bdc9c20dd37767e70413a20035d553b405c
parent 01a66959299e7308c3fd6bba1e219b7e90e2c245
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri,  9 Oct 2026 21:09:01 -0400

reports: the part being read is the last place at the top of the reading area (under the sticky bars), not above the top third

A jump to a section with a short lead, then a switch, opened its first
claim's slide: the claim's top was already above the top-third line. The
line is now where a jump lands a place (its scroll-margin-top, under the
site's header and the switch) plus 32 px, on the export site and in umtool.
overview.spec waits for ArrowRight's focus (moved on the next frame) before
Enter, as it does after every other key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mcommon/components/report/slides/ReportReader.tsx | 19+++++++++++--------
Mexport/e2e-report/overview.spec.ts | 2++
Mumtool/components/articles/ArticleReader.tsx | 7+++++--
3 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/common/components/report/slides/ReportReader.tsx b/common/components/report/slides/ReportReader.tsx @@ -29,9 +29,9 @@ import { ViewSwitch } from "./ViewSwitch"; // HTML never carries the slides twice). // // THE PLACE IS KEPT. From the article, the slide is the one for the part of -// the article in view (the last anchor at or above the top third); from a -// slide or the overview, the article opens at its anchor, scrolled to and -// focused. The view and the place are the URL's — `#s-<n>` in the slides, an +// the article being read (the last anchor at the top of the reading area); +// from a slide or the overview, the article opens at its anchor, scrolled to +// and focused. The view and the place are the URL's — `#s-<n>` in the slides, an // anchor id elsewhere — so a shared link opens the same view at the same // place, and Back undoes a switch. // @@ -41,14 +41,17 @@ import { ViewSwitch } from "./ViewSwitch"; // The article's places, in document order: what each slide stands for. const PLACE_SELECTOR = "#report-head, #in-brief, #found, [data-section], article[data-claim], #report-end"; -// The anchor of the part of the article in view: the last place whose top is -// at or above the top third of the window (the first, at the top) — never -// above where a landed anchor sits (its scroll-margin-top, which keeps it -// below the page's sticky bars: the site's header and this switch). +// The anchor of the part of the article being read: the last place whose top +// has reached the top of the reading area — just under the page's sticky bars +// (the site's header and this switch), where a jump to a place lands it (its +// scroll-margin-top) — give or take a line; the first place, at the top. So a +// jump to a section and a switch opens that section's slide, however short +// the section's lead before its first claim. +const READING_SLACK = 32; function anchorInView(): string | null { const places = Array.from(document.querySelectorAll<HTMLElement>(PLACE_SELECTOR)); const landed = places[0] ? parseFloat(getComputedStyle(places[0]).scrollMarginTop) || 0 : 0; - const line = Math.max(Math.min(window.innerHeight / 3, 280), landed + 2); + const line = landed + READING_SLACK; let best: string | null = null; for (const el of places) { if (!el.id) continue; diff --git a/export/e2e-report/overview.spec.ts b/export/e2e-report/overview.spec.ts @@ -31,6 +31,8 @@ test("overview: each part beside its slide; arrows walk the pairs; a card opens // Only the pair in force is in the tab order. await expect(ov.locator('[data-ov-block][tabindex="0"], [data-ov-card][tabindex="0"]')).toHaveCount(2); await page.keyboard.press("ArrowRight"); + // The focus moves on the next frame; Enter opens what has it. + await expect(ov.locator('[data-pair="claim:claim-2"] [data-ov-card]')).toBeFocused(); await page.keyboard.press("Enter"); await expect(page).toHaveURL(`${REPORT}?rv=slides#s-8`); await expect(stage(page).locator('[data-slide="claim:claim-2"]')).toBeVisible(); diff --git a/umtool/components/articles/ArticleReader.tsx b/umtool/components/articles/ArticleReader.tsx @@ -134,10 +134,13 @@ export default function ArticleReader({ (next: ReaderView) => { let anchor: string | null = place.anchor; if (shape === "article" && bodyRef.current) { - // The part of the text in view: the last place at or above the top third. + // The part of the text being read: the last place at the top of the + // column, under the sticky switch, where a jump lands it (its + // scroll-margin-top), give or take a line — as the export site's reader. const main = bodyRef.current.closest("main"); const top = main?.getBoundingClientRect().top ?? 0; - const line = top + Math.min(((main?.clientHeight ?? window.innerHeight) / 3), 280); + const first = slides.length > 0 ? placeEl(bodyRef.current, slides[0].anchorId) : null; + const line = top + (first ? parseFloat(getComputedStyle(first).scrollMarginTop) || 0 : 0) + 32; anchor = null; for (const s of slides) { const el = placeEl(bodyRef.current, s.anchorId);