tui: pause animation ticks while the terminal is blurred#2668
Merged
dgageot merged 3 commits intodocker:mainfrom May 6, 2026
Merged
tui: pause animation ticks while the terminal is blurred#2668dgageot merged 3 commits intodocker:mainfrom
dgageot merged 3 commits intodocker:mainfrom
Conversation
The commit 33d0d5c3b changed focused field initialization from false (default) to true. This breaks the Docker Desktop terminal restoration fix from cc2dfcf. In Docker Desktop, the first FocusMsg after startup triggers the ReleaseTerminal/RestoreTerminal cycle that fixes broken display after tab switches. With focused=true at startup, this first FocusMsg is filtered out by the spurious-FocusMsg check, preventing the restoration. The animation tick pausing behavior is correct: dropping ticks while blurred saves CPU, and the active counter staying positive is intentional (animations are registered but paused). StartTick() on FocusMsg correctly restarts the chain when focus is regained.
derekmisler
approved these changes
May 6, 2026
Contributor
derekmisler
left a comment
There was a problem hiding this comment.
my almost-4-year-old laptop thanks you.
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The PR cleanly decouples the animation tick-pause concern from the focus-tracking concern by introducing two boolean flags with different initialization semantics. The logic is correct:
- Startup safety:
tickPausedstartsfalseso ticks flow before any focus event arrives (terminals that never send FocusMsg work correctly). - Spurious-FocusMsg filter:
focusedstill correctly suppresses the spurious FocusMsg emitted by Docker Desktop'sRestoreTerminalcycle. - Tick resumption: On FocusMsg,
StartTick()is only called whenanimation.HasActive()is true — if no animations are active at refocus time, the tick chain will be restarted by the first component that callsStartTickIfFirst(), which is the existing behavior. m.programguard: The removal of them.program == nilearly-return is safe becauseSetProgramis called synchronously beforep.Run()incmd/root/new.go, som.programis always set before the event loop can deliver any messages.tea.Batchwith empty slice: Returns a nil Cmd, which bubbletea handles correctly.
No bugs found in the changed code.
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.
Why
When docker-agent runs in a background tab while the agent is streaming, the bubbletea animation tick chain (14 fps spinner + cache invalidations + view rebuilds) keeps firing into a screen nobody is looking at. That's pure CPU waste.
What
Drop `animation.TickMsg` while the terminal is blurred and let the chain die. Re-arm it on the next `FocusMsg` via `animation.StartTick()` so spinners resume immediately when the user comes back. The existing Docker Desktop release/restore cycle (commit cc2dfcf) is preserved.
Why not just `tea.WithFPS(30)`?
Static FPS reduction halves the ~60 cheap goroutine wake-ups per second everywhere — well under 1 % CPU savings. This change targets the expensive work — full `Update` + `View` + cache invalidation per spinner tick — exactly when the user can't see it (background tab), and costs nothing when they're looking at the terminal (foreground stays at full responsiveness).
Implementation note
Two flags rather than one:
They share most transitions but their initial values differ, so keeping them separate avoids cute-but-wrong overloading.