#2118·wtf

Improve UI responsiveness during widget refresh

Author: SeanstoppableCreated Jul 23, 2026Updated Jul 23, 2026
Labelsenhancement

Problem

When a widget refreshes (either on a timer or via the r keyboard shortcut), the UI can become unresponsive while waiting for API calls to complete. This is because Refresh() runs synchronously — the widget's scheduler goroutine (or the tview event loop, in the case of keyboard-triggered refresh) is blocked on network I/O until all HTTP calls finish.

This is noticeable on widgets that make multiple or slow API calls (e.g. GitHub, NewRelic, GitLab, Jira).

Proposed approach

1. Run keyboard-triggered refresh in a background goroutine

Currently, pressing r calls widget.Refresh() directly on the tview input handler goroutine. Wrapping this in a goroutine would keep the UI responsive:

go
widget.InitializeRefreshKeyboardControl(func() {
    go widget.Refresh()
})

This could be done once in KeyboardWidget.InitializeRefreshKeyboardControl() so all widgets benefit.

2. Add a context.WithTimeout at the scheduler level

Individual modules shouldn't need to manage their own timeouts. app.Schedule() could wrap each Refresh() call with a deadline:

go
ctx, cancel := context.WithTimeout(context.Background(), refreshTimeout)
defer cancel()
widget.Refresh(ctx)

This would require updating the Wtfable interface to accept a context, which is a larger change but would protect against any single module hanging the scheduler.

3. Ensure content() functions are always CPU-only

Establish as a project convention that the function passed to Redraw() should never do network I/O. All data fetching should happen in Refresh(), with results cached for content() to read. This was addressed for the GitHub widget in #2117 but other modules (e.g. NewRelic) have the same pattern.

Modules affected

A quick scan shows these modules make HTTP calls without timeouts or have network calls in their display path:

  • NewReliccontent() makes HTTP calls directly
  • DigitalOcean — keyboard handlers call API methods synchronously
  • Transmission — keyboard handlers call API methods synchronously
  • PagerDuty, AzureDevOps, DevTo, Gerrit — use context.Background() without timeouts

Impact

This is a quality-of-life improvement. The app remains functional but feels sluggish when widgets are refreshing, especially with slow or rate-limited APIs.