From b918fe7ab1cca8d2cb5b8ff32d6905a7f3753e99 Mon Sep 17 00:00:00 2001 From: arpan Date: Sun, 6 Sep 2026 22:01:58 +0530 Subject: [PATCH] The transcript box could only grow, so the only scrollbar was the horizontal one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Try-it page's output box had a min-height and no max-height. As the reveal appended lines the box got taller and pushed the page down; it never scrolled, because there was no vertical overflow to scroll. The follow-the-newest-line in try-it.js was dead code against it — scrollTop stayed 0 — and the one scrollbar the reader did get was the horizontal one the demo's long lines need. Measured in a browser at 820px wide: the box grew to 910px with scrollHeight equal to clientHeight, and the page grew from 700 to 956. The box now has overflow-y and a max-height of min(60vh, 440px), which makes it a terminal that scrolls rather than a block that grows: it holds at 452px and scrolls to 458, and the page stops growing. The long lines still scroll horizontally inside it, which is what a transcript with aligned columns needs. Following the output is right up until somebody scrolls up to re-read, so the reveal now follows only a reader already within 40px of the bottom, and resumes when they scroll back down. Both verified in a browser. --- docs/try-it.js | 8 +++++++- docs/try-it.mdx | 2 ++ tests/test_docs_travelling.py | 26 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/try-it.js b/docs/try-it.js index b4f155a..c48f7fb 100644 --- a/docs/try-it.js +++ b/docs/try-it.js @@ -103,9 +103,15 @@ } else { node = document.createTextNode(line); } + // Follow the newest line, but only for a reader who is already at the bottom: + // yanking somebody back who has scrolled up to re-read scenario 1 is worse than + // not following at all. The box has a max height, so this is a real scroll — it + // was dead code while the box could only grow, and the reader got a horizontal + // scrollbar and a page that got taller instead of a terminal that scrolled. + var following = output.scrollHeight - output.scrollTop - output.clientHeight < 40; output.appendChild(node); output.appendChild(document.createTextNode("\n")); - output.scrollTop = output.scrollHeight; + if (following) output.scrollTop = output.scrollHeight; window.setTimeout(next, line.trim() === "" ? 0 : LINE_MS); } next(); diff --git a/docs/try-it.mdx b/docs/try-it.mdx index fa01dc3..8c5ae07 100644 --- a/docs/try-it.mdx +++ b/docs/try-it.mdx @@ -31,9 +31,11 @@ filesystem, and opens no socket. padding: "16px", marginTop: "12px", overflowX: "auto", + overflowY: "auto", fontSize: "13px", lineHeight: "1.5", minHeight: "180px", + maxHeight: "min(60vh, 440px)", whiteSpace: "pre", }} > diff --git a/tests/test_docs_travelling.py b/tests/test_docs_travelling.py index 425cf5a..853dfcb 100644 --- a/tests/test_docs_travelling.py +++ b/tests/test_docs_travelling.py @@ -142,6 +142,32 @@ def test_the_program_ends_on_an_expression_pyodide_can_return(): assert "buffer.getvalue()" in last, f"the last line does not return the demo: {last!r}" +def test_the_transcript_box_scrolls_down_rather_than_growing(): + """A `min-height` with no `max-height` is a box that can only get taller. + + That is what shipped. The transcript grew the page instead of scrolling, `scrollTop` + stayed 0 so the script's follow-the-newest-line was dead code, and the only scrollbar the + reader got was the horizontal one the long lines need. Measured in a browser at 820px: the + box grew to 910px with `scrollHeight == clientHeight`. With both bounds it holds at 452px + and scrolls to 458. + """ + style = re.search(r" with inline style" + box = dict(re.findall(r"(\w+):\s*\"([^\"]*)\"", style.group(1))) + + assert "maxHeight" in box, "minHeight without maxHeight is a box that can only grow" + assert box.get("overflowY") == "auto", "the box cannot scroll vertically" + assert box.get("overflowX") == "auto", "the long lines must scroll inside the box" + assert box.get("whiteSpace") == "pre", "the demo's columns are aligned; do not wrap them" + + +def test_the_reveal_follows_the_newest_line_only_for_a_reader_at_the_bottom(): + """Following the output is right until somebody scrolls up to re-read, and then it is + yanking them away from what they are reading.""" + assert "output.scrollHeight - output.scrollTop - output.clientHeight" in SCRIPT + assert "if (following) output.scrollTop = output.scrollHeight;" in SCRIPT + + def test_the_harness_runs_the_program_the_page_runs(): """A harness with its own copy of the artifact verifies the copy. This one reads docs/try-it.js, so the program it proves is the program the reader gets."""