Draw a reply one block at a time, and skip the blocks off screen
A reply's display list holds every glyph of it and is re-recorded whenever drawing is invalidated, so one long message costs as much to draw as a hundred short ones and skipping the rows around it cannot help while it is the one on screen. That was the whole of the remaining draw cost: 97% of rows correctly skipped, and the tallest one still drawn was 36,982px -- about twenty-five screens in a single message. So a message is cut into its top-level blocks and each is drawn, or not, on its own. The cut comes from the parser's own boundaries rather than from a line scanner looking for blank lines, which is what makes it safe: a heading, a table, a fenced block and a list are each one node whatever is inside them, so a loose list does not become five one-item lists and a fence is never split down the middle. Checked against a real reply whose list items are separated by blank lines -- it still draws as one list with its bullets aligned, which is the case a blank-line split gets wrong. It bounds parsing too, which was the other symptom in the same reading: one message took 1.4 seconds to parse as a single unit, and a block is a paragraph. The row and the message each know half of where a block is, so they meet at an interface declared where it is used: the list supplies the row's position, the message supplies the block's offset inside it, and drawing a message does not have to know it is inside a transcript. What this cannot divide is a single node, and a long fenced code block is one -- so the report now also carries the tallest *drawn block*, which is the number that says whether splitting bounded anything. This emulator's tallest row is one such fence, which is why its own figures do not move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dffa365e54
commit
981856e046
5 files changed
+203
-9
No files matched your search
@@ -578,7 +578,10 @@ private suspend fun warm(replies: ParsedReplies, rows: List<TranscriptItem>) {
|
||||
// the work it finds stays one page's worth. Off the calling thread it is nobody's frame.
|
||||
withContext(Dispatchers.Default) {
|
||||
val texts =
|
||||
rows.filterIsInstance<TranscriptItem.AssistantMsg>().flatMap { markdownIn(it.text) }
|
||||
rows
|
||||
.filterIsInstance<TranscriptItem.AssistantMsg>()
|
||||
.flatMap { markdownIn(it.text) }
|
||||
.flatMap { replies.blocksOf(it) }
|
||||
if (texts.isNotEmpty()) replies.warm(texts)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user