feat: add TUI auto-refresh with configurable interval and manual refresh
Introduce background data refresh so the dashboard stays current without restarting. This touches four layers: Config (config.go): - New TUIConfig struct with AutoRefresh (bool) and RefreshIntervalSec (int) fields, defaulting to enabled at 30-second intervals. - Minimum interval floor of 10 seconds enforced at load time. App core (app.go): - RefreshDataMsg type for background refresh completion signaling. - Auto-refresh state: interval timer, refreshing flag, lastRefresh timestamp. Checked on every tick; fires refreshDataCmd when elapsed. - refreshDataCmd: background goroutine that loads session data via cache (with uncached fallback) and posts RefreshDataMsg on completion. - Manual refresh keybind: 'r' triggers immediate refresh. - Auto-refresh toggle keybind: 'R' toggles auto-refresh and persists the preference to config.toml. - Help text updated with r/R keybind documentation. Status bar (statusbar.go): - Shows spinning refresh indicator during active refresh. - Shows auto-refresh icon when auto-refresh is enabled. Settings tab (tab_settings.go): - Two new editable fields: Auto Refresh (bool) and Refresh Interval (seconds with 10s minimum). - Settings display reads live App state to stay consistent with the R toggle keybind (avoids stale config-file reads). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cburn/internal/cli"
|
||||
"cburn/internal/config"
|
||||
@@ -21,6 +22,8 @@ const (
|
||||
settingsFieldTheme
|
||||
settingsFieldDays
|
||||
settingsFieldBudget
|
||||
settingsFieldAutoRefresh
|
||||
settingsFieldRefreshInterval
|
||||
settingsFieldCount // sentinel
|
||||
)
|
||||
|
||||
@@ -80,6 +83,19 @@ func (a App) settingsStartEdit() (tea.Model, tea.Cmd) {
|
||||
ti.SetValue(fmt.Sprintf("%.0f", *cfg.Budget.MonthlyUSD))
|
||||
}
|
||||
ti.EchoMode = textinput.EchoNormal
|
||||
case settingsFieldAutoRefresh:
|
||||
ti.Placeholder = "true or false"
|
||||
ti.SetValue(strconv.FormatBool(a.autoRefresh))
|
||||
ti.EchoMode = textinput.EchoNormal
|
||||
case settingsFieldRefreshInterval:
|
||||
ti.Placeholder = "30 (seconds, minimum 10)"
|
||||
// Use effective value from App state to match display
|
||||
intervalSec := int(a.refreshInterval.Seconds())
|
||||
if intervalSec < 10 {
|
||||
intervalSec = 30
|
||||
}
|
||||
ti.SetValue(strconv.Itoa(intervalSec))
|
||||
ti.EchoMode = textinput.EchoNormal
|
||||
}
|
||||
|
||||
ti.Focus()
|
||||
@@ -144,6 +160,15 @@ func (a *App) settingsSave() {
|
||||
cfg.Budget.MonthlyUSD = &b
|
||||
}
|
||||
}
|
||||
case settingsFieldAutoRefresh:
|
||||
cfg.TUI.AutoRefresh = val == "true" || val == "1" || val == "yes"
|
||||
a.autoRefresh = cfg.TUI.AutoRefresh
|
||||
case settingsFieldRefreshInterval:
|
||||
var interval int
|
||||
if _, err := fmt.Sscanf(val, "%d", &interval); err == nil && interval >= 10 {
|
||||
cfg.TUI.RefreshIntervalSec = interval
|
||||
a.refreshInterval = time.Duration(interval) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
a.settings.saveErr = config.Save(cfg)
|
||||
@@ -184,6 +209,13 @@ func (a App) renderSettingsTab(cw int) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Use live App state for TUI-specific settings (auto-refresh, interval)
|
||||
// to ensure display matches actual behavior after R toggle
|
||||
refreshIntervalSec := int(a.refreshInterval.Seconds())
|
||||
if refreshIntervalSec < 10 {
|
||||
refreshIntervalSec = 30 // match the effective default
|
||||
}
|
||||
|
||||
fields := []field{
|
||||
{"Admin API Key", apiKeyDisplay},
|
||||
{"Session Key", sessionKeyDisplay},
|
||||
@@ -195,6 +227,8 @@ func (a App) renderSettingsTab(cw int) string {
|
||||
}
|
||||
return "(not set)"
|
||||
}()},
|
||||
{"Auto Refresh", strconv.FormatBool(a.autoRefresh)},
|
||||
{"Refresh Interval", fmt.Sprintf("%ds", refreshIntervalSec)},
|
||||
}
|
||||
|
||||
var formBody strings.Builder
|
||||
|
||||
Reference in New Issue
Block a user