iris: a transcript row is a column of markdown blocks, so a streamed delta costs one block
A row was one TextEdit holding the whole message, so every delta re-shaped every paragraph of a long reply through parley -- the one phase where iris trails Compose on the phone (p50 18.2ms vs 13.4ms, bench v2). - client-core/src/markdown_blocks.rs: split a message into its top-level blocks with their source, through the same pulldown-cmark the renderer parses with so the two cannot disagree about where a block starts, plus common_prefix. Appending markdown can rewrite an earlier block (a trailing --- turns the paragraph above into a heading), so the fast path compares the prefix it keeps rather than assuming it -- with the test that says so. - transcript-ui: a row is a Span of one TextEdit per block; RowBlocks::apply_delta replaces the block a delta lands in; TranscriptScreen keeps the tail row's blocks, seeded in build_tree as well as push_row (a screen opened onto a streaming reply took the rebuild path for its first delta otherwise, with nothing to say so). - A block is the selection unit: Selection is keyed by (RowKey, u32), which is reading order at both levels, and the pointer-captured half of a drag resolves the block under the finger from its drawn box (Selection::locate) instead of from the row's extent. Pass condition: a_delta_into_a_long_reply_redraws_the_same_widgets_as_a_short_one drives a real UiRenderState and asserts the draw count for a delta into a 100-paragraph (3,000+ char) reply equals the count for a one-paragraph one. 30 either way; it read 630 against 30 twice on the way there. Emulator stream phase, same AVD before and after: p50 61.5 -> 54.5ms, p90 211.7 -> 113.1ms, p99 342.6 -> 137.4ms, worst 403.6 -> 143.0ms, 202 -> 293 frames in the same 21 seconds. Selection across blocks verified with a real long-press drag. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
167862ca1b
commit
e1030d69f6
14 files changed
+871
-82
No files matched your search
+66
-5
@@ -107,17 +107,78 @@ Rig fix on the way past: `iris/android-app/run-bench.sh` polled logcat for
|
||||
and printed a report that had never been run. It polls for the report's
|
||||
own first line now.
|
||||
|
||||
### Bench, before Task B (emulator, 2026-09-06)
|
||||
### Task B, closed 2026-09-06: a streamed delta costs one markdown block
|
||||
|
||||
A transcript row was one `TextEdit` holding the whole message, so every
|
||||
delta re-shaped every paragraph of a long reply through parley -- the one
|
||||
phase where iris trailed Compose on Iris's phone. A row is a **column of
|
||||
one `TextEdit` per top-level markdown block** now, and a delta that lands
|
||||
in the last block is one `set_with_spans` on that block.
|
||||
|
||||
- **`client-core/src/markdown_blocks.rs`** is the split: `split_blocks`
|
||||
(top-level blocks with their source, via the same `pulldown-cmark` the
|
||||
renderer parses with, so the two cannot disagree about where a block
|
||||
starts) and `common_prefix`. Seven tests, including the one that says
|
||||
the fast path must **compare** rather than assume: appending `---` under
|
||||
a paragraph turns that paragraph into a heading, so an already
|
||||
laid-out block is not always still what it was.
|
||||
- **`iris/transcript-ui/src/row.rs`** builds the column and owns
|
||||
`RowBlocks::apply_delta`; **`lib.rs`** keeps the *tail* row's blocks
|
||||
(`TranscriptScreen::tail`) since that is the only row a delta reaches.
|
||||
- **A block is the selection unit**, not a row: `Selection` is keyed by
|
||||
`SelKey = (RowKey, u32)`, which compares in reading order at both
|
||||
levels so every range query in that file is unchanged. The list-level
|
||||
(pointer-captured) half of a drag resolves the block under the finger
|
||||
from its drawn box (`Selection::locate`) instead of doing arithmetic
|
||||
from the row's extent.
|
||||
|
||||
**Pass condition, met**: `a_delta_into_a_long_reply_redraws_the_same_widgets_as_a_short_one`
|
||||
(`transcript-ui/src/lib.rs`) drives a real `UiRenderState` and asserts the
|
||||
`Widget::draw` count for one delta into a 100-paragraph (3,000+ character)
|
||||
reply equals the count for the same delta into a one-paragraph reply.
|
||||
**30 either way.** It is a real test, not a tautology: it read **630
|
||||
against 30** at three points on the way -- once because `Span`'s measure
|
||||
pass redrew every child, and once because `build_tree` did not seed
|
||||
`tail`, so the first delta after opening a screen took the rebuild path
|
||||
with nothing on screen or in `take_rebuilds()` to say so.
|
||||
|
||||
Two things tried and dropped, so the next session does not redo them.
|
||||
`Painter::measure` (a container asking a clean child for its size instead
|
||||
of drawing it provisionally) fixed one of the 630s but the test passes
|
||||
without it once the `tail` seeding is right, so it was removed rather than
|
||||
kept on speculation. And the emulator's own numbers say the remaining
|
||||
cost is not in the block split.
|
||||
|
||||
**Verified on the emulator** beyond the counter: the transcript draws its
|
||||
blocks with their own spacing (heading, prose, fence), and
|
||||
`ui-trace record --do "holddrag 300 700 700 1000 700 600"` logs
|
||||
`iris selection: begin at row (3187, 0)` then `extend to row (3187, 1)`
|
||||
with the highlight crossing from the heading into the code block -- a
|
||||
selection that spans blocks, which is what the re-key had to keep.
|
||||
|
||||
### Bench, stream phase, before and after Task B (emulator, 2026-09-06)
|
||||
|
||||
`iris/android-app/build-apk.sh debug --abi x86_64 --features
|
||||
"transcript-screen bench force-gles"` + `run-bench.sh`, this checkout's
|
||||
AVD. Emulator absolutes transfer nothing; the before/after ratio on the
|
||||
same emulator does.
|
||||
|
||||
stream: 202 frames over 21.0s
|
||||
late: 197 (97.5%)
|
||||
total p50 61.5ms p90 211.7ms p99 342.6ms
|
||||
worst 403.6ms
|
||||
Same AVD, same fixture, same build flags, 20 minutes apart. Emulator
|
||||
absolutes transfer nothing; the ratio does.
|
||||
|
||||
before after
|
||||
stream: 202 frames over 21.0s stream: 293 frames over 21.0s
|
||||
late: 197 (97.5%) late: 285 (97.3%)
|
||||
p50 61.5ms p50 54.5ms (-11%)
|
||||
p90 211.7ms p90 113.1ms (-47%)
|
||||
p99 342.6ms p99 137.4ms (-60%)
|
||||
worst 403.6ms worst 143.0ms (-65%)
|
||||
|
||||
The tail is where the whole-message re-layout lived, and it is where the
|
||||
change shows: 91 more frames delivered in the same 21 seconds. The p50
|
||||
moves least, which is consistent -- a delta into a *short* message never
|
||||
cost much. **The phone number is Iris's to take**; nothing here is a
|
||||
statement about her device.
|
||||
|
||||
|
||||
- [x] **Merge the `DragGesture` work** -- done 2026-09-06 (merge commit
|
||||
|
||||
Reference in new issue
Block a user