feat: add TUI reusable components: metric cards, sparklines, tab bar, and status bar
Implement shared UI components used across the dashboard tabs:
- tui/components/card.go: Three components:
* MetricCard: bordered card with label (muted), value (bold), and
optional delta (dim) — used for the dashboard's top-level KPIs.
* MetricCardRow: renders N cards side-by-side using lipgloss
horizontal join, auto-calculating card width from available space.
* Sparkline: theme-colored Unicode block sparkline (8-level,
auto-scaled to series max). Used in Dashboard and Trends tabs.
* ProgressBar: filled/empty bar (accent + dim) with percentage
label. Used in the Budget tab for plan-relative spend.
- tui/components/statusbar.go: Bottom status bar with left-aligned
keybinding hints ([f]ilter [?]help [q]uit), current filter info,
and right-aligned data age indicator. Padding auto-fills to
terminal width.
- tui/components/tabbar.go: 10-tab navigation bar split across two
rows (Dashboard/Costs/Sessions/Models/Projects on row 1,
Trends/Efficiency/Activity/Budget/Settings on row 2). Each
inactive tab highlights its keyboard shortcut letter with [bracket]
notation. Active tab renders in accent color. Settings uses 'x'
as its shortcut (not present in the name, so appended). TabIdxByKey
maps key presses to tab indices.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
42
internal/tui/components/statusbar.go
Normal file
42
internal/tui/components/statusbar.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"cburn/internal/tui/theme"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// RenderStatusBar renders the bottom status bar.
|
||||
func RenderStatusBar(width int, filterInfo string, dataAge string) string {
|
||||
t := theme.Active
|
||||
|
||||
style := lipgloss.NewStyle().
|
||||
Foreground(t.TextMuted).
|
||||
Width(width)
|
||||
|
||||
left := " [f]ilter [?]help [q]uit"
|
||||
right := ""
|
||||
if dataAge != "" {
|
||||
right = fmt.Sprintf("Data: %s ", dataAge)
|
||||
}
|
||||
|
||||
if filterInfo != "" {
|
||||
left += " " + filterInfo
|
||||
}
|
||||
|
||||
// Pad middle
|
||||
padding := width - lipgloss.Width(left) - lipgloss.Width(right)
|
||||
if padding < 0 {
|
||||
padding = 0
|
||||
}
|
||||
|
||||
bar := left
|
||||
for i := 0; i < padding; i++ {
|
||||
bar += " "
|
||||
}
|
||||
bar += right
|
||||
|
||||
return style.Render(bar)
|
||||
}
|
||||
Reference in New Issue
Block a user