Skip to content

fix(renderer): prevent terminal output disappearing on window resize#2898

Open
hessnd wants to merge 4 commits intotailcallhq:mainfrom
hessnd:fix/terminal-resize-output-loss
Open

fix(renderer): prevent terminal output disappearing on window resize#2898
hessnd wants to merge 4 commits intotailcallhq:mainfrom
hessnd:fix/terminal-resize-output-loss

Conversation

@hessnd
Copy link
Copy Markdown

@hessnd hessnd commented Apr 9, 2026

Summary

Fix terminal output disappearing when the window is resized during active streaming, affecting both the interactive TUI and ZSH plugin inline mode.

Closes #2893

Context

When using Forge in Ghostty (and potentially other terminals), resizing the window during streaming causes rendered output to vanish. The root cause is twofold: (1) the markdown streaming renderer captures terminal width once at construction and never updates it on resize, and (2) the spinner's cursor manipulation writes stale cursor positions after the terminal reflows content.

Changes

  • Added Renderer::set_width() and StreamdownRenderer::set_width() in forge_markdown_stream to update the rendering width in place without tearing down parser/renderer state
  • Added last_width field to StreamingWriter in forge_main, initialized from term_width() at construction
  • Modified StreamingWriter::ensure_renderer() to poll term_width() on every call, comparing against last_width and calling set_width() when the terminal has been resized
  • Added spinner pause (spinner.stop(None)) before width updates to prevent finish_and_clear() from erasing content with stale cursor positions

Key Implementation Details

The fix uses in-place width polling rather than SIGWINCH signal handling or renderer tear-down/recreate. ioctl(TIOCGWINSZ) costs ~100ns-1us reading a kernel-cached struct, making per-call polling negligible. In-place update was chosen over tear-down because StreamdownRenderer::finish() calls parser.finalize(), which emits closing events for all open block structures — a mid-stream recreate would lose code block syntax highlighting, blockquote borders, list numbering, and table state. The polling approach also avoids SIGWINCH delivery issues in the ZSH plugin's ZLE widget context, where ZSH's own signal handler may consume the signal before Forge sees it.

Testing

cargo check --workspace
cargo test -p forge_main -p forge_markdown_stream

Manually verified in Ghostty:

  • Resize during active streaming — content remains visible, new lines render at updated width
  • Resize mid-code-block — syntax highlighting preserved
  • Resize mid-blockquote — borders and margins preserved
  • Resize via ZSH plugin inline mode (: prompt) — works correctly

@github-actions github-actions bot added the type: fix Iterations on existing features or infrastructure. label Apr 9, 2026
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 9, 2026

CLA assistant check
All committers have signed the CLA.

@hessnd hessnd marked this pull request as ready for review April 9, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix Iterations on existing features or infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Terminal output disappears on window resize in Ghostty (ZSH plugin and interactive mode)

2 participants