Skip to content

fix(tea): fall back to 80x24 when initial GetSize returns 0 (blank first frame under tmux)#1718

Open
Ricardo-M-L wants to merge 1 commit into
charmbracelet:mainfrom
Ricardo-M-L:fix/zero-size-blank-first-frame
Open

fix(tea): fall back to 80x24 when initial GetSize returns 0 (blank first frame under tmux)#1718
Ricardo-M-L wants to merge 1 commit into
charmbracelet:mainfrom
Ricardo-M-L:fix/zero-size-blank-first-frame

Conversation

@Ricardo-M-L

Copy link
Copy Markdown

Problem

Launching a Bubble Tea v2 program inside tmux (and some other pty setups) shows a blank screen until the first event — the user resizes the window or presses a key — at which point the UI finally paints. It looks like the program hung.

Root cause

Program.Run reads the terminal size once at startup and trusts the result, checking only the error:

w, h, err := term.GetSize(p.ttyOutput.Fd())
if err != nil {
    return p.initialModel, fmt.Errorf("bubbletea: error getting terminal size: %w", err)
}
width, height = w, h            // no check for w==0 / h==0

Under tmux the pty size often isn't negotiated yet at this instant, so term.GetSize returns (0, 0) with err == nil. Run then resizes the renderer to 0x0. Because the event loop only paints after it receives a message, and the only startup message is the resulting WindowSizeMsg{0,0}, the first frame is clipped to a 0x0 viewport — blank — until a real SIGWINCH arrives.

This is independent of the model's View(): even a view that ignores width entirely renders blank, because the renderer viewport is 0x0.

Fix

Fall back to a standard 80x24 for any zero dimension, mirroring what terminal apps conventionally do. The first frame then paints, and the genuine size replaces it via WindowSizeMsg a moment later.

width, height = fallbackZeroSize(w, h)

Adds fallbackZeroSize (a small pure helper) plus a unit test. The startup GetSize path itself is awkward to unit-test (it needs a real tty Fd), so the fallback logic is factored into a tested helper.

Testing

  • go test . passes.
  • Reproduction: a minimal v2 program launched in an early/detached tmux pane renders blank until the first event; with the fallback it paints immediately.

Program.Run reads the terminal size once at startup via term.GetSize
and trusts the result, checking only the error. Some terminals — tmux
is the common case, before the pty size is negotiated — return (0, 0)
with no error. Run then resizes the renderer to 0x0 and the event loop,
which only paints after a message, renders the first frame into a 0x0
viewport: the whole screen is blank until a real SIGWINCH (a resize or
keypress) arrives. This looks like the program hung, and it happens
regardless of the model's View() — even a width-agnostic view renders
blank because the renderer viewport is 0.

Fall back to a standard 80x24 for any zero dimension so the first frame
paints; the genuine size still arrives via WindowSizeMsg a moment later
and re-renders correctly.

Adds fallbackZeroSize + a unit test.
@Ricardo-M-L

Copy link
Copy Markdown
Author

Gentle ping on this one 🙂

It's a small (+52/-1), self-contained fix with a unit test for a real tmux issue: term.GetSize returns (0, 0) with err == nil before the pty size is negotiated, so the renderer is sized to 0x0 and the first frame is blank until a real SIGWINCH arrives (looks like the program hung). The fallback lets the first frame paint, and the genuine size still replaces it via WindowSizeMsg a moment later — no behavior change for terminals that report a valid size.

Happy to adjust the fallback dimensions or take a different approach if you'd prefer. Thanks for taking a look when you get a chance!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant