You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(Markdown): implement sticky-height streaming to fix layout jump
Streaming assistant messages now keep a maximum observed rendered height
and add temporary blank rows when live markdown causes content to reflow
upward, so later transcript content no longer jumps.
Factored the line/height math into `src/components/Messages/layout.ts`
and exported the markdown terminal renderer from
`src/components/Markdown/Markdown.tsx` via `src/components/Markdown/index.ts`.
---
Preserve Live Markdown and Prevent Transcript Jump
Summary:
Keep live markdown rendering for streaming assistant messages and prevent surrounding layout from jumping upward by giving the active streaming message a sticky minimum height.
The fix should preserve the current live-rendered markdown/code behavior, but when a rerender would make the streaming message shorter because content reflowed upward, the component should pad the message so it keeps its previous on-screen height until the stream finishes.
Implementation Changes:
- Add a dedicated wrapper around the active streaming assistant message in `src/components/Messages/Messages.tsx`.
- Track a sticky height for the current streaming message instance:
- compute the rendered line count of the streaming message after markdown/code rendering
- store the maximum line count seen so far for that in-flight message
- if the newest rendered output is shorter than the stored maximum, append blank lines so the wrapper still occupies the stored maximum height
- Apply sticky-height behavior only to `streamingMessage`.
- Do not change committed transcript items rendered through `Static`
- Do not change user/system message behavior
- Reset sticky height when any of these happen:
- the streaming message is committed and removed
- a different streaming message starts
- `sessionId` changes
- terminal width changes
- Keep the existing live markdown/code rendering path intact:
- assistant prose still renders through `Markdown`
- completed code fences still render through `CodeBlock`
- existing streaming inline markdown behavior stays unchanged
Measurement Strategy:
- Measure rendered height from the final display string that Ink will wrap, not from raw markdown source.
- Add a shared line-count utility for terminal output that:
- strips ANSI escape sequences before width measurement
- splits on explicit newlines
- counts wrapped rows using the current available content width
- treats empty lines as occupying one row
- Use the same available width assumptions already used by `Markdown`:
- terminal columns from `useStdout()`
- minus horizontal margins used for assistant message containers
- For code blocks:
- count visible rows from the rendered code block content plus its surrounding border/padding rows
- use the same width calculation the `CodeBlock` container effectively occupies
- Keep the measurement utility local to the message-rendering subsystem unless another component clearly needs it.
Public Interfaces / Behavior:
- No CLI/API/type changes.
- User-visible behavior:
- live markdown rendering remains enabled during streaming
- when inline markdown completion causes content to reflow upward, later transcript content and the input area should no longer jump upward during that stream
- temporary blank space may appear below the active streaming assistant message until the stream completes
- once committed, the final message renders normally with no sticky padding retained
Assumptions:
- Live markdown during streaming is a hard requirement and should not be reduced.
- The priority is preventing upward movement of surrounding layout, not preventing the streamed text itself from reflowing internally.
- Temporary blank space under the active streaming message is acceptable during streaming if it removes transcript/input jump.
- Terminal-width changes should invalidate prior sticky height rather than trying to preserve it across different wrap widths.
0 commit comments