Skip to content

fix(ui): prevent structured Markdown reflow during post-terminal handoff #731

Closed

Description

@Astro-Han

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-root geometry 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.tsx
  • packages/ui/src/chat-view.tsx
  • packages/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

  1. Astro-Han commented on Jul 11, 2026

    @Astro-Han
    ContributorAuthor

    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 from text_delta events and restored only by text_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_delta payloads now exactly equal text_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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions