From 253776ffd5f376c3d2de1c705c13a86c2493e4b1 Mon Sep 17 00:00:00 2001 From: teernisse Date: Thu, 19 Feb 2026 13:02:33 -0500 Subject: [PATCH] feat: add TUI theme system with four color palettes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the theming layer for the interactive dashboard: - tui/theme/theme.go: Theme struct with 14 semantic color roles (Background, Surface, Border, BorderHover, TextDim, TextMuted, TextPrimary, Accent, Green, Orange, Red, Blue, Purple, Yellow). Four built-in themes: * Flexoki Dark (default): warm, low-contrast palette designed for extended reading. Teal accent (#3AA99F) on near-black background. * Catppuccin Mocha: pastel warm tones with blue accent (#89B4FA) on dark surface (#1E1E2E). * Tokyo Night: cool blue/purple palette with blue accent (#7AA2F7) on deep navy (#1A1B26). * Terminal: ANSI 16-color only for maximum compatibility — maps all roles to standard terminal colors (0-15), works correctly in any terminal emulator regardless of true-color support. Global Active variable holds the current theme. SetActive/ByName enable runtime theme switching from the settings tab and setup wizard. Co-Authored-By: Claude Opus 4.6 --- internal/tui/theme/theme.go | 119 ++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 internal/tui/theme/theme.go diff --git a/internal/tui/theme/theme.go b/internal/tui/theme/theme.go new file mode 100644 index 0000000..337409d --- /dev/null +++ b/internal/tui/theme/theme.go @@ -0,0 +1,119 @@ +package theme + +import "github.com/charmbracelet/lipgloss" + +// Theme defines the color roles used throughout the TUI. +type Theme struct { + Name string + Background lipgloss.Color + Surface lipgloss.Color + Border lipgloss.Color + BorderHover lipgloss.Color + TextDim lipgloss.Color + TextMuted lipgloss.Color + TextPrimary lipgloss.Color + Accent lipgloss.Color + Green lipgloss.Color + Orange lipgloss.Color + Red lipgloss.Color + Blue lipgloss.Color + Purple lipgloss.Color + Yellow lipgloss.Color +} + +// Active is the currently selected theme. +var Active = FlexokiDark + +// FlexokiDark is the default theme. +var FlexokiDark = Theme{ + Name: "flexoki-dark", + Background: lipgloss.Color("#100F0F"), + Surface: lipgloss.Color("#1C1B1A"), + Border: lipgloss.Color("#282726"), + BorderHover: lipgloss.Color("#343331"), + TextDim: lipgloss.Color("#575653"), + TextMuted: lipgloss.Color("#6F6E69"), + TextPrimary: lipgloss.Color("#FFFCF0"), + Accent: lipgloss.Color("#3AA99F"), + Green: lipgloss.Color("#879A39"), + Orange: lipgloss.Color("#DA702C"), + Red: lipgloss.Color("#D14D41"), + Blue: lipgloss.Color("#4385BE"), + Purple: lipgloss.Color("#8B7EC8"), + Yellow: lipgloss.Color("#D0A215"), +} + +// CatppuccinMocha is a warm pastel theme. +var CatppuccinMocha = Theme{ + Name: "catppuccin-mocha", + Background: lipgloss.Color("#1E1E2E"), + Surface: lipgloss.Color("#313244"), + Border: lipgloss.Color("#45475A"), + BorderHover: lipgloss.Color("#585B70"), + TextDim: lipgloss.Color("#6C7086"), + TextMuted: lipgloss.Color("#A6ADC8"), + TextPrimary: lipgloss.Color("#CDD6F4"), + Accent: lipgloss.Color("#89B4FA"), + Green: lipgloss.Color("#A6E3A1"), + Orange: lipgloss.Color("#FAB387"), + Red: lipgloss.Color("#F38BA8"), + Blue: lipgloss.Color("#89B4FA"), + Purple: lipgloss.Color("#CBA6F7"), + Yellow: lipgloss.Color("#F9E2AF"), +} + +// TokyoNight is a cool blue/purple theme. +var TokyoNight = Theme{ + Name: "tokyo-night", + Background: lipgloss.Color("#1A1B26"), + Surface: lipgloss.Color("#24283B"), + Border: lipgloss.Color("#414868"), + BorderHover: lipgloss.Color("#565F89"), + TextDim: lipgloss.Color("#565F89"), + TextMuted: lipgloss.Color("#A9B1D6"), + TextPrimary: lipgloss.Color("#C0CAF5"), + Accent: lipgloss.Color("#7AA2F7"), + Green: lipgloss.Color("#9ECE6A"), + Orange: lipgloss.Color("#FF9E64"), + Red: lipgloss.Color("#F7768E"), + Blue: lipgloss.Color("#7AA2F7"), + Purple: lipgloss.Color("#BB9AF7"), + Yellow: lipgloss.Color("#E0AF68"), +} + +// Terminal uses ANSI 16 colors only. +var Terminal = Theme{ + Name: "terminal", + Background: lipgloss.Color("0"), + Surface: lipgloss.Color("0"), + Border: lipgloss.Color("8"), + BorderHover: lipgloss.Color("7"), + TextDim: lipgloss.Color("8"), + TextMuted: lipgloss.Color("7"), + TextPrimary: lipgloss.Color("15"), + Accent: lipgloss.Color("6"), + Green: lipgloss.Color("2"), + Orange: lipgloss.Color("3"), + Red: lipgloss.Color("1"), + Blue: lipgloss.Color("4"), + Purple: lipgloss.Color("5"), + Yellow: lipgloss.Color("3"), +} + +// All available themes. +var All = []Theme{FlexokiDark, CatppuccinMocha, TokyoNight, Terminal} + +// ByName returns a theme by its name, defaulting to FlexokiDark. +func ByName(name string) Theme { + for _, t := range All { + if t.Name == name { + return t + } + } + return FlexokiDark +} + +// SetActive sets the active theme by name. +func SetActive(name string) { + Active = ByName(name) +}