fix(tea): fall back to 80x24 when initial GetSize returns 0 (blank first frame under tmux) - #1718
Conversation
|
Gentle ping on this one 🙂 It's a small (+52/-1), self-contained fix with a unit test for a real tmux issue: 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! |
|
Hi! Just a friendly ping on this PR. It's been open for a while — would appreciate a review when you get a chance. Thanks for your time! |
858b1f0 to
4a5e73a
Compare
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.
4a5e73a to
a101b65
Compare
|
Substantive update after today's rebase: this branch is now based on current I also reran the full project validation suite: root tests, race tests, The upstream |
|
Hey @Ricardo-M-L, Do you have any code to reproduce the blank screen? Because all bubbletea v2 apps work fine in tmux for me |
|
Hi @andrinoff — good question, and sorry I didn't include a reproducer earlier. Here's one. ReproducerThe issue is timing-sensitive: it happens when # 1. Create a new detached tmux session and immediately run a bubbletea program inside it
tmux new-session -d -s testblank 'sleep 0.05 && your-bubbletea-app'
# 2. Attach right away
tmux attach -t testblankOn attach you'll see the first frame is blank (all-black / empty cells) for a split second before the real content paints on the first Why it happens
What this PR doesIt falls back to 80x24 (the standard default terminal size) when the initial size query returns zero in either dimension, so the first frame paints correctly. The real size replaces it on the very next Let me know if you'd like me to add this reproducer to the commit message or PR description. |
|
Hi @andrinoff, following up - I provided a reproducer for the blank first frame issue. Is there anything else needed? |
|
Weirdly it works with all of my TUIs on bubbletea v2... Can you provide an example of an app / code of the app to reproduce? |
Use shutdown(true) instead of cancel() in the panic recovery path. Previously cancel() only cancelled the program context without cleaning up the renderer goroutine or closing the cancelReader, causing resource leaks when the program panics.
Use p.Send() instead of direct channel send so Println and Printf safely become no-ops once the program has exited, matching the behavior of Send(), Quit(), and the documented safety contract.
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.Runreads the terminal size once at startup and trusts the result, checking only the error:Under tmux the pty size often isn't negotiated yet at this instant, so
term.GetSizereturns(0, 0)witherr == nil.Runthen resizes the renderer to0x0. Because the event loop only paints after it receives a message, and the only startup message is the resultingWindowSizeMsg{0,0}, the first frame is clipped to a 0x0 viewport — blank — until a realSIGWINCHarrives.This is independent of the model's
View(): even a view that ignores width entirely renders blank, because the renderer viewport is0x0.Fix
Fall back to a standard
80x24for any zero dimension, mirroring what terminal apps conventionally do. The first frame then paints, and the genuine size replaces it viaWindowSizeMsga moment later.Adds
fallbackZeroSize(a small pure helper) plus a unit test. The startupGetSizepath itself is awkward to unit-test (it needs a real tty Fd), so the fallback logic is factored into a tested helper.Validation
Rebased onto current upstream
mainon 2026-08-08.go test ./...go test -race -count=1 ./...go vet ./...go test ./...inexamples/go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.9.0 rungo test -run TestFallbackZeroSize -count=100 .git diff --checkReproduction: a minimal v2 program launched in an early/detached tmux pane renders blank until the first event; with the fallback it paints immediately.
I have read CONTRIBUTING.md.