The occasional bump left in an otherwise smooth transcript was the last
unbounded item: a pasted log in a user bubble is one Text whose layout runs
in the frame the row scrolls into -- 93,808px on the fixture, reported from
the phone as a 112ms worst measure. Per frame it was already cheap (one
node); the cost was entirely the entry.
A message past USER_SPLIT_CHARS is now cut at line starts into slices of
roughly 2,500 characters, each its own list unit. Lines lay out
independently, so slices that own whole lines stack back into exactly the
lines the single Text drew; the threshold is also what guarantees the
bubble was at full width, which the slices must share to read as one card.
Measured on the emulator, same fixture and gestures: worst transcript
measure 57.6ms -> 12.6ms. Fill continuity across slice seams and uniform
63px line pitch verified from full-resolution screenshots; a short message
keeps the ordinary wrapping bubble.
The corner-and-padding geometry that lets one visual card be several list
items now lives once, in Modifier.cardPiece -- Bryan asked for exactly this
generalization so future row types are cheap to add. An opened peer message
and a long user message are its two users; a new sliced kind needs only a
unit type, a flatten branch, and a body wrapped in cardPiece.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>