Skip to content

fix(session-ui): grow markdown live tail in place - #48432

Closed
dcerisano wants to merge 2 commits into
anomalyco:devfrom
dcerisano:live-tail
Closed

dcerisano wants to merge 2 commits into
anomalyco:devfrom
dcerisano:live-tail

Conversation

@dcerisano

Copy link
Copy Markdown

Issue for this PR

Closes #36043 — jointly with the tui-delta-coalesce PR. Each applies
independently (different layer, no shared code, merge in either order);
only together do they remove O(n²) from both client-side stages of the
stream path and fully close the freeze. Builds on the freeze diagnosis in
#41472.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

While streaming, the markdown projection re-parsed the entire accumulated text
on every delta, even when the previous text was unchanged and the new suffix
was small. Each re-lex is O(length), so a stream of n small deltas costs
O(n²) — the dominant cost in the TUI freeze once deltas are batched.

project() in markdown-stream.ts now grows the open live tail in place
instead of re-lexing, and only re-lexes when the new suffix contains a
blank-line block boundary or the raw text exceeds a 2048-char cap. A single
giant boundary-less paragraph/list/table therefore never re-parses per delta.

Because the projection lives in the shared session-ui package, web and
desktop also inherit the cheaper project() as a free performance win, even
though their per-token rendering is not the freeze described in #36043.

Counterpart on the store side: the tui-delta-coalesce PR (coalesces
message.part.delta events per part in a delta buffer, flushing to the store
on a 32 ms timer instead of once per event). Together they remove O(n²) from
both halves of the TUI stream path — the markdown projection here, the store
writes there.

How did you verify your code works?

  • bun test: 26 markdown-stream tests (incl. a new test growing a
    boundary-less oversized tail in place without splitting) — 86 tests, 0
    failures.
  • bun typecheck clean in packages/session-ui.
  • oxlint: 0 errors.
  • Micro-benchmark streaming 6–15 KB through project() in small deltas
    (Bun): paragraphs 1399 ms → 20 ms, single 15 KB paragraph 32345 ms → 161 ms,
    growing list 4467 ms → 60 ms.

Screenshots / recordings

N/A — no visual change; it's a rendering-perf fix (benchmark above).

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Tracking: #48430

@thdxr

thdxr commented Oct 10, 2026

Copy link
Copy Markdown
Member

This branch has been upgraded to V2. We're no longer taking PRs for V1. If you think this is still relevant, please port it over and open it for V2.

— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

@dcerisano

Copy link
Copy Markdown
Author

This branch has been upgraded to V2. We're no longer taking PRs for V1. If you think this is still relevant, please port it over and open it for V2.

— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

This is the dev branch which is still producing V1 binaries. V2 binary is still identified as beta. V1 is in LTS production environments and will be for many years. V1 requires maintenance PRs exactly like this one.

@dcerisano

Copy link
Copy Markdown
Author

V2 port opened: #55857

Per the request to port this to V2, #55857 re-implements the live-tail optimization against the current packages/session-ui/src/components/markdown-stream.ts. project() grows the open mode: "live" tail in place and re-lexes only the tail past the 2048-char cap on a blank-line boundary, preserving frozen blocks. The V2 port adds the edges V1 did not need: late reference definitions, frozen display math, fence-open detection, and rebuilding the tail raw from source text. Micro-benchmark: 12.6 KB boundary-less paragraph 10.8 s → 91 ms (~119×).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI scroll freezes for seconds during fast-streaming responses (message.part.delta applied per-chunk with no batching)

2 participants