irisandClaude Fable 5 acb59adcf6 Replace the hand-built transcript window with a reversed lazy list of blocks
The plain Column held every loaded row and reimplemented what a lazy
list is -- windowing (retained ranges, stand-in spacers), height
bookkeeping (a heights map and prefix-summed tops), scroll anchoring,
and restore -- and every constraint kept intersecting that surface
somewhere new: the retained window was a fresh way to flicker, the
spacers hit the Constraints height limit, the IME re-measured the
world, and the restore needed pending anchors threaded through layout.

The reason a lazy list was abandoned no longer holds. It was dropped
when an item was a whole message, and a message can be twenty-five
screens tall -- composing one mid-fling is a hundred-millisecond frame.
The block splitting built later (for draw granularity) is the missing
piece: with one item per markdown *block*, entering composition costs
laying out a paragraph, and the parse is already cached by the same
warm() that always ran. So the transcript is now a
LazyColumn(reverseLayout) over TranscriptUnits -- settled replies
flattened to block items, everything else one item, the live reply kept
whole because its text changes per delta.

What each constraint rests on now, all verified on the emulator against
a real 1200-event imported transcript at --delay 120:

- Following the newest message, paging in history, and the keyboard are
  the reversed layout itself: item zero is the bottom, an arriving
  message extends the pinned end, a page lands past every visible
  index, and an IME resize keeps the anchored item against the
  composer. A short conversation stacks from the bottom.
- No item enters unready: pages are warmed before the fold lands (the
  opening page now folds a scratch copy off-thread first), so heights
  are real on first measure and scrolling back is cache hits -- zero
  markdown parses on the composing thread across a full page-back
  through all 1200 events.
- Restore resolves the saved seq to a unit index and snaps before the
  draw gate lifts; anchors gained a unit ordinal (ScrollAnchor grew a
  third field, read compatibly) so a position inside a forty-block
  reply survives.
- Unloaded history is a spinner item at the far end while moreHistory
  holds; paging triggers on estimated pixels ahead (units remaining at
  the typical visible unit size), since a lazy list has not measured
  what it never composed.
- The tap-half expansion anchoring survives, but through
  requestScrollToItem: a raw dispatchRawDelta from onSizeChanged forces
  remeasure inside the measure pass and crashes
  ("performMeasureAndLayout called during measure").

Deleted: TranscriptScroll.kt entirely (1000 lines of window, spacers,
tops, anchors, seeding). ParsedReplies gained a parts cache so the
per-fold flatten never re-scans a settled message, and clear() now
empties all three maps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 14:30:02 -04:00
2026-08-24 20:32:18 -04:00
S
Description
No description provided
27 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 54%
Kotlin 43.6%
Shell 2.4%