Skip to content

fix: make TUI subprocess calls async to resolve UI freezing (#215)#249

Open
dion-jy wants to merge 1 commit intosmtg-ai:mainfrom
dion-jy:fix/tui-performance-215
Open

fix: make TUI subprocess calls async to resolve UI freezing (#215)#249
dion-jy wants to merge 1 commit intosmtg-ai:mainfrom
dion-jy:fix/tui-performance-215

Conversation

@dion-jy
Copy link

@dion-jy dion-jy commented Feb 10, 2026

Summary

  • Metadata polling (tmux capture-pane, git diff): moved from synchronous Update() to async tea.Cmd pattern — UI blocking drops from 100ms+ to µs per tick
  • Preview capture (tmux capture-pane): same async pattern applied — no longer freezes UI while capturing pane content
  • Trust screen detection: polling loop moved from blocking Start() to a background goroutine — session creation returns immediately

Fixes #215

Problem

The bubbletea Update() handler was calling subprocess commands synchronously. Every metadata tick blocked the entire UI for 100-500ms depending on disk speed. With multiple sessions, this scaled linearly (e.g., 8 sessions × 200ms = 1.6s of UI freeze per tick cycle). Even with a single session, keystrokes were noticeably delayed.

Approach

Converted blocking subprocess calls into the standard bubbletea async pattern:

  1. Update() captures state and returns a tea.Cmd closure (returns in µs)
  2. The closure runs subprocess calls in a bubbletea-managed goroutine
  3. Results come back as a new message type, applied on the main event loop

Thread safety is maintained because:

  • Goroutines only perform stateless subprocess calls (read-only)
  • All mutable state writes happen exclusively on the main event loop thread
  • Next tick is scheduled only after the result message is processed (no concurrent goroutine overlap)

Changed files

File Change
app/app.go Async metadata tick + async preview tick handlers
session/instance.go Added SetDiffStats() for async result application
session/tmux/tmux.go Trust screen polling moved to goroutine
ui/preview.go Added SetPreviewContent() for async preview
ui/tabbed_window.go Added SetPreviewContent() delegation

Benchmark results (before → after)

(you can check benchmark code here, https://github.com/CUN-bjy/claude-squad/tree/fix/tui-performance-215-with-tests)

Scenario OLD (sync) NEW (async) Speedup
Metadata tick (1 session, typical) 107ms 4µs 26,000x
Preview tick (1 session, typical) 50ms 5µs 9,000x
Metadata tick (8 sessions, slow disk) 1.66s 4µs 414,000x
End-to-end keystroke responsiveness 323ms blocked 6µs blocked 53,000x

Note: Total work time is unchanged — subprocesses still run in the background. The improvement is in UI blocking time (how long keystrokes are delayed).

Test plan

  • All existing tests pass (go test ./...)
  • Race detector clean (go test -race ./...)
  • Before/after benchmark comparison tests added
  • Trust screen async goroutine verification tests added
  • Manual testing: confirmed TUI is responsive with real sessions

If you find my work helpful, fuel the next commit with a coffee ☕

🤖 Generated with Claude Code

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

…sync (smtg-ai#215)

The TUI becomes unresponsive with multi-second keystroke delays because
synchronous subprocess calls (tmux capture-pane, git add -N, git diff)
block the bubbletea event loop. This commit moves all expensive
operations to background goroutines via tea.Cmd:

1. Metadata polling (app/app.go): HasUpdated() and Diff() now run in a
   goroutine, sending results back as metadataResultMsg for safe
   application on the main thread.

2. Preview capture (app/app.go): CapturePaneContent() runs async via
   previewResultMsg. Tick interval increased from 100ms to 200ms.

3. Trust screen handling (session/tmux/tmux.go): The 30-45s polling
   loop now runs in a goroutine so Start() returns immediately.

4. Diff stats (session/instance.go): Added SetDiffStats() for the
   async metadata path to safely write results on the main thread.

Closes smtg-ai#215

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dion-jy dion-jy force-pushed the fix/tui-performance-215 branch from fc5f1a1 to a93b8ec Compare February 10, 2026 07:16
@dion-jy
Copy link
Author

dion-jy commented Feb 10, 2026

I have read the CLA Document and I hereby sign the CLA

@miamachine
Copy link

Hey @dion-jy — great work on this, I have a similar PR open (#253) that tackles the same root issue. Wanted to flag it so the maintainers can see both and decide how to proceed, but I think they're different enough to land separately.

The main differences:

Not trying to compete, just wanted to make the overlap visible. Happy to coordinate if it helps.

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.

TUI Interactions very slow

2 participants