Draw a fence plain while it is still being written
Warming covers a settled message, so it did not reach the block a reply is still writing: that one was re-lexed at every delta, on the composing thread. Measured streaming a two-hundred-line Kotlin fence -- 211 lexes, 13.7 seconds across the turn, the worst 177ms -- for colours on text being replaced as fast as they were computed. A live reply's last segment now draws its code plain and takes its colours when the block freezes, which for a finished fence is as soon as the next block starts. The settle-time lex then happens in warm, off the drawing thread. Same fixture after: one lex of 374ms in warm, draw phase 1.51ms to 1.01ms per frame while streaming, and the fence coloured on screen once the reply settles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ab6a797941
commit
b0b91ad28f
3 files changed
+71
-21
No files matched your search
@@ -93,7 +93,9 @@ fun MarkdownText(
|
||||
var previousSegment: Segment? = null
|
||||
segments.forEachIndexed { at, segment ->
|
||||
val nextContinues = segments.getOrNull(at + 1)?.continues == true
|
||||
MarkdownRoot(segment.parse, replies) {
|
||||
// Only the tail is still being written; a frozen segment is finished text that
|
||||
// happens to sit in a live reply, and it takes its colours now. See [MarkdownRoot].
|
||||
MarkdownRoot(segment.parse, replies, streaming = live && at == segments.lastIndex) {
|
||||
segment.pieces.forEachIndexed { index, piece ->
|
||||
val gap =
|
||||
when {
|
||||
@@ -328,9 +330,22 @@ fun MarkdownPiece(
|
||||
*
|
||||
* Colours come from the theme rather than from the renderer's defaults, so code, links and rules
|
||||
* are the same Catppuccin values the rest of the app uses. Nothing here picks a colour of its own.
|
||||
*
|
||||
* [streaming] says this parse is the part of a reply still being written, which only the fences
|
||||
* care about: lexing is proportional to how much code there is, and a fence still arriving is
|
||||
* re-lexed at every delta on the composing thread. Measured streaming a two-hundred-line Kotlin
|
||||
* fence: **13.7 seconds** of lexing across the turn, 211 of them, the worst 177ms -- for colours on
|
||||
* text that was being replaced as fast as they were computed. So a fence still being written is
|
||||
* drawn plain and takes its colours when the block freezes, which is the same bargain [LiveParse]
|
||||
* already makes for a reference link defined at the foot of a message.
|
||||
*/
|
||||
@Composable
|
||||
private fun MarkdownRoot(parse: State, replies: ParsedReplies, content: @Composable () -> Unit) {
|
||||
private fun MarkdownRoot(
|
||||
parse: State,
|
||||
replies: ParsedReplies,
|
||||
streaming: Boolean = false,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
if (parse !is State.Success) {
|
||||
// Nothing below needs the environment; [MarkdownPiece] draws the words plainly.
|
||||
content()
|
||||
@@ -460,8 +475,12 @@ private fun MarkdownRoot(parse: State, replies: ParsedReplies, content: @Composa
|
||||
unorderedList = { MarkdownList(it.content, it.node, it.listDepth) },
|
||||
table = { LinkedTable(it.content, it.node, it.typography.table) },
|
||||
// Code is highlighted the way a tool call's input is; see [CodeFence].
|
||||
codeFence = { CodeFence(it.content, it.node, it.typography.code, replies) },
|
||||
codeBlock = { CodeBlock(it.content, it.node, it.typography.code, replies) },
|
||||
codeFence = {
|
||||
CodeFence(it.content, it.node, it.typography.code, replies, streaming)
|
||||
},
|
||||
codeBlock = {
|
||||
CodeBlock(it.content, it.node, it.typography.code, replies, streaming)
|
||||
},
|
||||
),
|
||||
content = content,
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user