408681c8e4
- Add Theme struct with 3 predefined themes (dark/light/druntime) - Add Profile struct for named configuration profiles - Add ErrorBanner with severity levels and auto-dismiss - Add unit tests for theme, error banner, and models - Update CHANGELOG.md and PROJECT_STATE.md
149 lines
4.9 KiB
Go
149 lines
4.9 KiB
Go
package tui
|
|
|
|
import "github.com/charmbracelet/lipgloss"
|
|
|
|
// Theme defines a complete color palette for the TUI
|
|
type Theme struct {
|
|
Name string
|
|
Fg lipgloss.Color
|
|
FgMute lipgloss.Color
|
|
Bg lipgloss.Color
|
|
BgSel lipgloss.Color
|
|
Red lipgloss.Color
|
|
Orange lipgloss.Color
|
|
Yellow lipgloss.Color
|
|
Green lipgloss.Color
|
|
Aqua lipgloss.Color
|
|
Blue lipgloss.Color
|
|
Purple lipgloss.Color
|
|
Border lipgloss.Color
|
|
TabBg lipgloss.Color
|
|
}
|
|
|
|
// Predefined themes
|
|
var (
|
|
ThemeDark = Theme{
|
|
Name: "dark",
|
|
Fg: lipgloss.Color("#d4be98"),
|
|
FgMute: lipgloss.Color("#7c6f64"),
|
|
Bg: lipgloss.Color("#1d2021"),
|
|
BgSel: lipgloss.Color("#45403d"),
|
|
Red: lipgloss.Color("#ea6962"),
|
|
Orange: lipgloss.Color("#e78a4e"),
|
|
Yellow: lipgloss.Color("#d8a657"),
|
|
Green: lipgloss.Color("#a9b665"),
|
|
Aqua: lipgloss.Color("#89b482"),
|
|
Blue: lipgloss.Color("#7daea3"),
|
|
Purple: lipgloss.Color("#d3869b"),
|
|
Border: lipgloss.Color("#504945"),
|
|
TabBg: lipgloss.Color("#1d2021"),
|
|
}
|
|
|
|
ThemeLight = Theme{
|
|
Name: "light",
|
|
Fg: lipgloss.Color("#3c3836"),
|
|
FgMute: lipgloss.Color("#7c6f64"),
|
|
Bg: lipgloss.Color("#f2e5bc"),
|
|
BgSel: lipgloss.Color("#d5c4a1"),
|
|
Red: lipgloss.Color("#cc241d"),
|
|
Orange: lipgloss.Color("#d65d0e"),
|
|
Yellow: lipgloss.Color("#d79921"),
|
|
Green: lipgloss.Color("#98971a"),
|
|
Aqua: lipgloss.Color("#689d6a"),
|
|
Blue: lipgloss.Color("#458588"),
|
|
Purple: lipgloss.Color("#b16286"),
|
|
Border: lipgloss.Color("#a89984"),
|
|
TabBg: lipgloss.Color("#f2e5bc"),
|
|
}
|
|
|
|
ThemeDracula = Theme{
|
|
Name: "dracula",
|
|
Fg: lipgloss.Color("#f8f8f2"),
|
|
FgMute: lipgloss.Color("#6272a4"),
|
|
Bg: lipgloss.Color("#282a36"),
|
|
BgSel: lipgloss.Color("#44475a"),
|
|
Red: lipgloss.Color("#ff5555"),
|
|
Orange: lipgloss.Color("#ffb86c"),
|
|
Yellow: lipgloss.Color("#f1fa8c"),
|
|
Green: lipgloss.Color("#50fa7b"),
|
|
Aqua: lipgloss.Color("#8be9fd"),
|
|
Blue: lipgloss.Color("#6272a4"),
|
|
Purple: lipgloss.Color("#bd93f9"),
|
|
Border: lipgloss.Color("#44475a"),
|
|
TabBg: lipgloss.Color("#282a36"),
|
|
}
|
|
)
|
|
|
|
// Themes is the registry of all available themes
|
|
var Themes = map[string]Theme{
|
|
"dark": ThemeDark,
|
|
"light": ThemeLight,
|
|
"dracula": ThemeDracula,
|
|
}
|
|
|
|
// activeTheme holds the currently active theme
|
|
var activeTheme = ThemeDark
|
|
|
|
// GetTheme returns a theme by name, defaults to dark
|
|
func GetTheme(name string) Theme {
|
|
if t, ok := Themes[name]; ok {
|
|
return t
|
|
}
|
|
return ThemeDark
|
|
}
|
|
|
|
// SetTheme applies a theme by name and updates all component styles
|
|
func SetTheme(name string) {
|
|
theme := GetTheme(name)
|
|
activeTheme = theme
|
|
applyTheme(theme)
|
|
}
|
|
|
|
// GetActiveTheme returns the currently active theme
|
|
func GetActiveTheme() Theme {
|
|
return activeTheme
|
|
}
|
|
|
|
// applyTheme updates all component styles from the given theme
|
|
func applyTheme(t Theme) {
|
|
// Palette aliases
|
|
gbFg = t.Fg
|
|
gbFgMute = t.FgMute
|
|
gbBgSel = t.BgSel
|
|
gbRed = t.Red
|
|
gbOrange = t.Orange
|
|
gbYellow = t.Yellow
|
|
gbGreen = t.Green
|
|
gbAqua = t.Aqua
|
|
gbBlue = t.Blue
|
|
gbPurple = t.Purple
|
|
gbBorder = t.Border
|
|
|
|
// Component styles
|
|
TabActiveStyle = lipgloss.NewStyle().Background(t.Yellow).Foreground(t.Bg).Bold(true).Padding(0, 2)
|
|
TabInactiveStyle = lipgloss.NewStyle().Background(t.Border).Foreground(t.FgMute).Padding(0, 2)
|
|
TabBarStyle = lipgloss.NewStyle().Background(t.TabBg)
|
|
StatusBarStyle = lipgloss.NewStyle().Background(t.Green).Foreground(t.Bg).Padding(0, 1)
|
|
AppTitleStyle = lipgloss.NewStyle().Foreground(t.Yellow).Bold(true)
|
|
HighlightStyle = lipgloss.NewStyle().Foreground(t.Orange).Bold(true)
|
|
SelectedStyle = lipgloss.NewStyle().Foreground(t.Fg).Background(t.BgSel).Bold(true).Padding(0, 1)
|
|
ErrorStyle = lipgloss.NewStyle().Foreground(t.Red).Bold(true)
|
|
SuccessStyle = lipgloss.NewStyle().Foreground(t.Green).Bold(true)
|
|
InfoStyle = lipgloss.NewStyle().Foreground(t.Aqua)
|
|
SubtitleStyle = lipgloss.NewStyle().Foreground(t.FgMute)
|
|
HostNameStyle = lipgloss.NewStyle().Foreground(t.Yellow).Bold(true)
|
|
HostDetailStyle = lipgloss.NewStyle().Foreground(t.FgMute)
|
|
TagStyle = lipgloss.NewStyle().Foreground(t.Green)
|
|
TitleStyle = AppTitleStyle
|
|
SectionStyle = lipgloss.NewStyle().Foreground(t.Orange).Bold(true)
|
|
BorderStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(t.Border).Padding(1, 2)
|
|
|
|
// Pane styles
|
|
StylePaneActive = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(t.Green).Padding(0, 1)
|
|
StylePaneInactive = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(t.Border).Padding(0, 1)
|
|
|
|
// Host card styles
|
|
HostCardStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(t.Border).Padding(0, 1)
|
|
HostCardActiveStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(t.Green).Padding(0, 1)
|
|
}
|