Repository navigation
fix(ui): prevent structured Markdown reflow during post-terminal handoff #731
Description
Problem
PR #711 replaced full-document streaming Markdown rendering with Streamdown block rendering and materially stabilized the active streaming phase. However, current main still performs a large structural reflow after the runtime reaches a terminal state, while the live answer completes its catch-up / handoff.
This is narrower than the earlier #646 field finding:
- active provider streaming is now stable;
- the remaining jump occurs after Stop has disappeared;
- structured Markdown changes from a temporary single block into its final block tree in one frame.
Current evidence
Measured on current main (d736901d) through the production Electron renderer path using the deterministic MAKA_E2E FakeBackend:
- FakeBackend emitted 9-character deltas every 45 ms.
- The lazy Markdown module was warmed before measurement.
.maka-markdown-rootgeometry and structure were sampled every 8 ms.- Table, multi-paragraph, and unstructured controls ran in isolated app sessions.
| Input | Maximum height change while running | Maximum post-terminal height change | Preceding text offset change |
|---|---|---|---|
| GFM table | 19.5 px | 159.125 px | 31.5 px |
| Multi-paragraph text | 19.5 px | 133.5 px | 31.5 px |
| Unstructured single-line text | 19.5 px | 31.5 px | 0 px |
At the table jump, the Markdown root changed in one frame from:
P
to:
P / P / DIV(table) / P
The Stop control was already absent before and after that frame, locating the failure in post-terminal completion rather than provider delta delivery.
Relevant paths:
packages/ui/src/markdown-body.tsxpackages/ui/src/chat-view.tsxpackages/ui/src/smooth-stream.ts
Goal
Keep already-visible Markdown blocks visually stable when a live answer completes, without changing the final settled Markdown semantics.
Scope
- GFM tables.
- Multi-paragraph Markdown.
- The post-terminal live catch-up / settled handoff.
- Production Streamdown rendering and the existing single live-turn render path.
Acceptance criteria
- A completed paragraph preceding a trailing table or later paragraph keeps the same DOM ownership and moves by no more than 1 CSS px during completion.
- A table answer does not replace a temporary whole-answer
<p>with the final multi-block tree in one frame after terminal state. - Final settled Markdown content, table semantics, link safety, highlighting, and copy behavior remain unchanged.
- Active-stream behavior from fix(ui): stabilize streaming turn order and markdown layout #711 remains bounded and does not regress.
- A deterministic DOM geometry test covers the table and multi-paragraph cases, with an unstructured-text control.
- Verification uses temporal DOM measurements; a static screenshot alone is not sufficient.
Non-goals
- Provider delta coalescing or stream-speed tuning.
- Markdown typography or visual redesign.
- Headings and fenced code blocks until they have independent current-main reproductions.
- A parallel Markdown render path or custom parser.
Activity
Astro-Han commentedon Jul 11, 2026
Investigation update: the apparent post-terminal Markdown reflow was caused by the deterministic E2E
FakeBackend, not the production renderer handoff.Its chunker used
/.{1,9}/g; because.does not match line terminators, every newline was omitted fromtext_deltaevents and restored only bytext_complete. That forced a temporary single paragraph to become the complete table and paragraph block tree at terminal completion.PR #732 fixes the invariant at its owner: concatenated
text_deltapayloads now exactly equaltext_complete, including Markdown line breaks. A deterministic runtime regression test is RED on the old expression and GREEN on the fix.No production renderer or smooth-stream change is needed. An attempted Electron geometry regression was removed after review because it sampled only after the completed Markdown root mounted and therefore did not reliably exercise the handoff. All CI checks pass.