Replace the static-scan boot in the TUI with a reactive scan loop anchored on bubbletea.Tick and user-driven 'r' rescan. Scan loop (tea.Tick instead of goroutine — no leak): - Init returns a tick Cmd when --interval > 0; on each tick the model fires scanCmd and chains the next tick in the Update handler, producing a self-regulating interval loop that stops naturally on quit - 'r' key triggers an immediate rescan when the model is not already loading, for on-demand refresh - scanResultMsg handler records prevResult, computes status.Changed into a changed set (keyed by repo path), and updates lastScan - scanErrorMsg stores the error text on the model (displayed in the status bar) without stopping the loop Change detection in the view: - Repos whose state changed since the previous scan get a ▲ prefix (yellow bold) in the repo list, distinguishing them from the > cursor Status bar improvements: - Countdown "next: 23s" when --interval is active, computed from time.Since(lastScan); "scanning…" shown during async scans - Updated keybinding hints: r refresh, g/G home/end Navigation additions: - g/Home jumps to top, G/End jumps to bottom Styles: - Changed style (yellow bold foreground) for ▲ markers - Styles struct now includes all five variants Tests: - Home/End keys navigate to first/last repo - 'r' fires scanCmd when not loading - ▲ marker appears for changed repos - Interval tick fires scan when idle - Countdown "next:" shown in status bar Verified: go build, go vet, go test -race (12 packages), gofmt clean.
55 lines
1.5 KiB
Go
55 lines
1.5 KiB
Go
package tui
|
|
|
|
import (
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
"gitea.oblak.solutions/dimitar/gitFlow/internal/presenter"
|
|
)
|
|
|
|
// Styles groups the lipgloss styles used across TUI views.
|
|
type Styles struct {
|
|
Header lipgloss.Style
|
|
Row lipgloss.Style
|
|
CursorRow lipgloss.Style
|
|
StatusBar lipgloss.Style
|
|
Changed lipgloss.Style
|
|
}
|
|
|
|
// Dim applies a dimmed, lower-contrast style (shared helper).
|
|
func (s Styles) Dim(text string) string {
|
|
return lipgloss.NewStyle().Faint(true).Render(text)
|
|
}
|
|
|
|
// Err applies an error-highlight style.
|
|
func (s Styles) Err(text string) string {
|
|
return lipgloss.NewStyle().Foreground(lipgloss.Color("1")).Render(text)
|
|
}
|
|
|
|
// NewStyles builds a Styles palette from the presenter options so the TUI
|
|
// theme matches the CLI.
|
|
func NewStyles(opts presenter.Options) Styles {
|
|
headerFg, rowFg, cursorFg, barFg := darkColors()
|
|
if opts.Theme == presenter.ThemeLight {
|
|
headerFg, rowFg, cursorFg, barFg = lightColors()
|
|
}
|
|
return Styles{
|
|
Header: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(headerFg)).
|
|
Bold(true),
|
|
Row: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(rowFg)),
|
|
CursorRow: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(cursorFg)).
|
|
Background(lipgloss.Color("235")),
|
|
StatusBar: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(barFg)).
|
|
Background(lipgloss.Color("236")),
|
|
Changed: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("11")).
|
|
Bold(true),
|
|
}
|
|
}
|
|
|
|
func darkColors() (h, r, c, b string) { return "14", "7", "15", "8" }
|
|
func lightColors() (h, r, c, b string) { return "6", "0", "0", "7" }
|