All panels wrapped in titled rounded borders (╭─ Title ──╮). Focused
panel gets accent-colored border, unfocused panels get muted border.
- Monitors panel: titled "Monitors", focused when detail is closed
- Logs panel: titled "Logs", always unfocused (passive display)
- Detail panel: titled with monitor name, focused when open
Table's own RoundedBorder replaced with HiddenBorder — the titled
panel border provides the visual frame, table uses space-separated
columns internally. Consistent chrome across all panels.
Table columns were computed from terminal width, causing row wrapping
when the monitors panel only gets 70% of the space. Introduced
contentWidth field set per-tab in viewDashboard. computeLayout,
isWide, and renderTable now use contentWidth for column visibility,
available space, and max table width calculations.
Columns gracefully hide (SSL, RETRIES, TYPE, UPTIME) when the panel
is narrower, matching the existing responsive breakpoint behavior.
applyTheme mutated ~18 package-global lipgloss styles while every SSH
session's tea.Program read them concurrently from its own goroutine.
Pressing T or opening a new connection raced other sessions' View and
bled themes across users.
Styles now live in an immutable per-Model struct built by newStyles;
free formatter helpers that consumed the globals became Model methods.
- Show version in dashboard footer (wired from goreleaser ldflags)
- Cap name column at 35, raise sparkline minimum to 15 chars
- Preserve zebra background on group rows (was lost by style override)
- Group sparkline uses bullet • instead of heavy circle ●
Flexoki Dark (default), Flexoki Light, Catppuccin Mocha, Nord.
Press T to cycle themes; selection persists in preferences.
All hardcoded colors replaced with theme-driven values.
Dedicated ZebraBg per theme for subtle row striping.
Add alternating row backgrounds for easier table scanning. Detail panel
now shows breadcrumb path (Sites > Group > Name) and min/avg/max latency
stats below the sparkline. Group collapse state persists across restarts
via new preferences table in both SQLite and Postgres.
Group rows now show selection background when navigated to. Layout
chrome extracted to named constants to prevent viewport drift. Groups
display aggregate history as dot sparkline (●) distinct from site
bar sparklines, with uptime computed from active children only.
Paused and maintenance children excluded from group aggregates.
- New table_helpers.go with renderTable() and shared styles
- Remove 4 duplicated style blocks (header/cell/selected/border)
from tab_alerts.go and tab_users.go
- All 3 tab views now use renderTable() for offset/end calc,
selected row highlighting, and table construction
- Sites tab keeps siteGroupStyle via StyleOverride callback
- Clamp cursor to list length at end of refreshData() to prevent
index-out-of-bounds after concurrent list changes
- Fix off-by-one in tab click handler (i <= maxTabs → i < tabCount)