fix(Markdown): implement sticky-height streaming to fix layout jump#75
Merged
Conversation
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.
- src/components/Messages/parsing.ts - src/components/Messages/streaming.ts - src/components/Messages/styles.ts
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
Refactored the markdown renderer into `src/components/Markdown/render.ts`, leaving `src/components/Markdown/Markdown.tsx` as the thin Ink component wrapper.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the motivation for this pull request?
Bug fix: streaming assistant messages cause transcript content to jump upward when live markdown causes content to reflow to fewer lines.
What is the current behavior?
When streaming an assistant message, markdown rendering can cause content to reflow upward, making later transcript items and the input area jump unexpectedly.
What is the new behavior?
Streaming assistant messages now keep a maximum observed rendered height and append temporary blank rows when live markdown causes content to reflow upward, so later transcript content no longer jumps during streaming.
Also refactors
src/components/Messages/utils.tsinto focused modules:src/components/Messages/parsing.tssrc/components/Messages/streaming.tssrc/components/Messages/styles.tssrc/components/Messages/layout.tsplan.md
Checklist: