a074975d6f59f3ed2bc5fc0269b08dbc0e7f9795
Sharing one `ScrollState` across the viewer's rows was not enough to make them move together. `Modifier.horizontalScroll` is a node per row, and each one coerces the shared offset into *its own* range -- its content width less its viewport -- so a short line's range is zero and it stayed put while the long line beside it moved. Each row also writes `maxValue` on the shared state as it measures, so how far the file could be dragged at all was decided by whichever row happened to measure last, and changed as the list scrolled. Both go away once every row is the same width. `FileLines` now carries the longest line in columns, and the viewer turns that into one content width from a single character's advance -- arithmetic rather than twenty thousand measurements, because the face is monospace -- and gives it to every row. A tab counts as eight columns and deliberately upwards: over-estimating leaves a little empty space past the longest line, under-estimating puts the end of that line out of reach. The width is capped well under what `Constraints` can carry, so a minified file is a scroll that stops early rather than a crash. Reported by Iris on 2026-09-04. Checked on the emulator against the generated 1 MB file, whose lines run from one character to sixty-eight: the file now moves as a block, the offset survives scrolling vertically and newly composed rows arrive at it, and the far end of the longest line is reachable. Also checked on the two cases the change had no reason to touch -- a file narrower than the screen, which still does not scroll at all, and an empty one, whose zero content width draws its one numbered line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Languages
Rust
53%
Kotlin
44.4%
Shell
2.6%