e922b73d7ae2fe37376da251449ccf9c97b728df
Iris's phone, 2026-09-07, two screenshots of the transcript at its top edge wrong in opposite directions: rows already scrolled past still drawn, over the header bar (`version = "0.1.0"` behind "Run benchmark"), and a blank band where the row straddling the edge should be. Three faults, one rule -- `List::intersects_viewport`: a row is drawn if any part of it is inside the list's own box, and nothing outside that box reaches the screen. 1. **The walk drew everything between the anchor and the viewport.** `scroll` moves the anchor's offset and nothing else, so panning leaves the anchor's own row further and further outside the viewport, and every row in between was placed *and drawn* on every frame. Measured on the bench fixture: 8 scrolls of 3000px left 64 rows drawn for a 2012px viewport, ~59 of them off screen. `place` now skips a row whose height is already known and whose box does not overlap; `rehome_anchor` moves the anchor onto a visible row each frame, without moving anything drawn, so the walk is O(visible) again whatever distance was travelled. `extents` holds only what is on screen, which is what `key_at` already claimed of it, asserted at the end of every draw. 2. **Nothing clipped the list.** A straddling row is drawn in full -- that is the rule -- so the part above the list was on screen. The transcript's list is `.masked()` now (the mechanism `examples/ message_list.rs` and the composer already use, and one that nests as of the previous commit), and `List::draw` asserts it has a mask rather than leaving that to each caller to remember. 3. **A fling past the first row stayed past it.** `tick_fling` stops a fling that has reached an end, wherever the spline's last step had put it: `fling_toward_the_start_stops_at_the_first_row` was leaving the first row 1398px below a 600px viewport -- a blank screen -- and its assertion could not see it, since `extents` then held off-screen rows too and `top >= -0.5` is satisfied by +1398. `clamp_to_content` gives the gap back from the ends the walk already placed. Only when the opposite end is not also in the viewport, so a list shorter than its viewport stays bottom-anchored as before. Layer 1 of the test rig throughout (`transcript-fixture/tests/ top_edge.rs`, the real screen under a bench-app-shaped header): each of the five fails on its own subject and no other -- culling on the row's top instead of its bottom fails only `the_row_across_the_top_edge_is_ drawn`, the pre-fix walk fails only the two about what is placed, dropping `.masked()` fails only `the_list_is_clipped_to_its_own_box`, dropping the clamp fails only `scrolling_past_the_first_row_settles_on_ it`. The bottom edge and a list shorter than the viewport are the ends none of this had a reason to touch and are covered too.
Languages
Rust
53%
Kotlin
44.4%
Shell
2.6%