summaryrefslogtreecommitdiff
path: root/internal/tui/dashboard/model.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-09 11:11:01 +0300
committerPaul Buetow <paul@buetow.org>2026-05-09 11:11:01 +0300
commiteed407b0e252a0105619daf79b8bc236ff5f487d (patch)
treeec2b1e6feb79d1eb2e9b4f10b5463cdc01f552db /internal/tui/dashboard/model.go
parent8da473aed2c3e901615294df398b26db5aea6032 (diff)
refine auto-reset timer cycle and pause on blur
Two follow-up refinements to the auto-reset timer added in 8da473a. - Hotkey cycle now goes off -> 10s -> 30s -> 60s -> 2m -> 5m -> off, giving the user finer control between 60s and 5m and a quicker starting cadence. - The timer now pauses while the TUI is blurred. SetFocused returns a tea.Cmd that re-arms a fresh tick on focus regain, and bumps the generation counter on every focus change so any tick scheduled before blur is dropped on arrival. autoResetTickCmd and handleAutoResetTick also gate on m.focused as defense in depth. - Dashboard chrome shows 'auto-reset: 30s (paused)' while the timer is enabled but blurred, distinguishing it from the disabled 'off' state. Tests cover the full preset cycle (including custom-value passthrough) and the pause-on-blur lifecycle: stale ticks ignored, current-gen ticks ignored while blurred, focus regain re-arms and fires the reset, no-op focus calls don't churn the generation counter, and the chrome label flips to '(paused)' as expected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'internal/tui/dashboard/model.go')
-rw-r--r--internal/tui/dashboard/model.go37
1 files changed, 31 insertions, 6 deletions
diff --git a/internal/tui/dashboard/model.go b/internal/tui/dashboard/model.go
index 0437f05..478e735 100644
--- a/internal/tui/dashboard/model.go
+++ b/internal/tui/dashboard/model.go
@@ -916,9 +916,11 @@ func (m *Model) resetBaselineCmd() tea.Cmd {
// autoResetTickCmd returns a command that fires an autoResetTickMsg after
// the current auto-reset interval. Returns nil when the timer is disabled
-// (interval <= 0), so callers can compose it without extra branching.
+// (interval <= 0) or while the dashboard is blurred, so callers can
+// compose it without extra branching. SetFocused re-arms the tick when
+// focus returns.
func (m Model) autoResetTickCmd() tea.Cmd {
- if m.autoResetEvery <= 0 {
+ if m.autoResetEvery <= 0 || !m.focused {
return nil
}
gen := m.autoResetGen
@@ -930,9 +932,11 @@ func (m Model) autoResetTickCmd() tea.Cmd {
// handleAutoResetTick fires the same reset path as the `r` key (live trie
// + stats engine) and re-arms the timer for the next tick. Stale ticks
// from a previous cadence are dropped via the generation counter so that
-// changing the interval does not double-fire.
+// changing the interval does not double-fire. While the dashboard is
+// blurred the tick is also dropped without re-arming; SetFocused will
+// arm a fresh tick on focus regain.
func (m Model) handleAutoResetTick(msg autoResetTickMsg) (tea.Model, tea.Cmd) {
- if msg.generation != m.autoResetGen || m.autoResetEvery <= 0 {
+ if msg.generation != m.autoResetGen || m.autoResetEvery <= 0 || !m.focused {
return m, nil
}
resetCmd := m.resetBaselineCmd()
@@ -1048,9 +1052,24 @@ func (m *Model) SetDarkMode(isDark bool) {
m.processesChart.SetDarkMode(isDark)
}
-// SetFocused controls whether periodic refresh ticks are processed.
-func (m *Model) SetFocused(focused bool) {
+// SetFocused controls whether periodic refresh ticks are processed and
+// returns a tea.Cmd that arms a fresh auto-reset tick when focus returns
+// (or nil otherwise). The auto-reset generation counter is bumped on
+// every focus change so any in-flight tick scheduled before a blur is
+// dropped when it eventually arrives — the tick payload's generation
+// will no longer match. Without bumping, a tick that was already in
+// flight when blur occurred could fire moments after the user re-focuses
+// and surprise them with a reset.
+func (m *Model) SetFocused(focused bool) tea.Cmd {
+ if m.focused == focused {
+ return nil
+ }
m.focused = focused
+ m.autoResetGen++
+ if !focused {
+ return nil
+ }
+ return m.autoResetTickCmd()
}
// SnapshotCmd returns a command that fetches and emits a fresh dashboard snapshot.
@@ -1106,10 +1125,16 @@ func (m Model) filterSummary() string {
// autoResetStatus is the human-readable label for the current
// auto-reset cadence shown in the dashboard chrome. "off" when the
// timer is disabled, otherwise the configured interval (e.g. "30s").
+// When the timer is enabled but the TUI has lost focus, a "(paused)"
+// suffix is appended so the user knows the timer will not fire until
+// focus returns. Disabled timers stay "off" regardless of focus.
func (m Model) autoResetStatus() string {
if m.autoResetEvery <= 0 {
return "auto-reset: off"
}
+ if !m.focused {
+ return "auto-reset: " + m.autoResetEvery.String() + " (paused)"
+ }
return "auto-reset: " + m.autoResetEvery.String()
}