package tui import ( "fmt" "strings" "github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/lipgloss" ) type ThemeMode string const ( ThemeLight ThemeMode = "light" ThemeDark ThemeMode = "dark" ) func ParseThemeMode(value string) (ThemeMode, error) { switch ThemeMode(strings.ToLower(strings.TrimSpace(value))) { case "", ThemeLight: return ThemeLight, nil case ThemeDark: return ThemeDark, nil default: return "", fmt.Errorf("unsupported theme %q; expected light or dark", value) } } type theme struct { Mode ThemeMode Background string Surface string SurfaceAlt string Border string Text string Muted string Title string User string Assistant string Tool string Error string System string } type styles struct { App lipgloss.Style Header lipgloss.Style Title lipgloss.Style Muted lipgloss.Style Status lipgloss.Style Viewport lipgloss.Style InputBox lipgloss.Style CommandHint lipgloss.Style UserLabel lipgloss.Style AssistantLabel lipgloss.Style ToolLabel lipgloss.Style ErrorLabel lipgloss.Style SystemLabel lipgloss.Style UserMessage lipgloss.Style AssistantMsg lipgloss.Style ToolMessage lipgloss.Style ErrorMessage lipgloss.Style SystemMessage lipgloss.Style } func themeForMode(mode ThemeMode) theme { if mode == ThemeDark { return darkTheme() } return lightTheme() } func lightTheme() theme { return theme{ Mode: ThemeLight, Background: "#F8FAFC", Surface: "#FFFFFF", SurfaceAlt: "#EEF2F7", Border: "#CBD5E1", Text: "#111827", Muted: "#64748B", Title: "#0F766E", User: "#2563EB", Assistant: "#0F766E", Tool: "#B45309", Error: "#DC2626", System: "#64748B", } } func darkTheme() theme { return theme{ Mode: ThemeDark, Background: "#111827", Surface: "#1F2937", SurfaceAlt: "#0F172A", Border: "#475569", Text: "#E5E7EB", Muted: "#94A3B8", Title: "#5EEAD4", User: "#93C5FD", Assistant: "#5EEAD4", Tool: "#FBBF24", Error: "#FCA5A5", System: "#94A3B8", } } func (t theme) styles() styles { return styles{ App: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Text)). Background(lipgloss.Color(t.Background)), Header: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Text)). Background(lipgloss.Color(t.SurfaceAlt)). Padding(0, 1), Title: lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color(t.Title)), Muted: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Muted)), Status: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Muted)). Background(lipgloss.Color(t.SurfaceAlt)). Padding(0, 1), Viewport: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Text)). Background(lipgloss.Color(t.Background)), InputBox: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Text)). Background(lipgloss.Color(t.Surface)). Border(lipgloss.NormalBorder()). BorderForeground(lipgloss.Color(t.Border)). Padding(0, 1), CommandHint: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Muted)). Background(lipgloss.Color(t.SurfaceAlt)). Padding(0, 1), UserLabel: labelStyle(t.User), AssistantLabel: labelStyle(t.Assistant), ToolLabel: labelStyle(t.Tool), ErrorLabel: labelStyle(t.Error), SystemLabel: labelStyle(t.System), UserMessage: messageStyle(t.Text, t.User), AssistantMsg: messageStyle(t.Text, t.Assistant), ToolMessage: messageStyle(t.Text, t.Tool), ErrorMessage: messageStyle(t.Error, t.Error), SystemMessage: lipgloss.NewStyle().Foreground(lipgloss.Color(t.System)).PaddingLeft(1), } } func labelStyle(color string) lipgloss.Style { return lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color(color)) } func messageStyle(textColor, accentColor string) lipgloss.Style { return lipgloss.NewStyle(). Foreground(lipgloss.Color(textColor)). Border(lipgloss.ThickBorder(), false, false, false, true). BorderForeground(lipgloss.Color(accentColor)). PaddingLeft(1) } func applyTextareaTheme(input *textarea.Model, t theme) { focused := textarea.Style{ Base: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Text)). Background(lipgloss.Color(t.Surface)), CursorLine: lipgloss.NewStyle(). Background(lipgloss.Color(t.Surface)), Placeholder: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Muted)), Prompt: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Muted)), Text: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Text)), EndOfBuffer: lipgloss.NewStyle(). Foreground(lipgloss.Color(t.Surface)), } blurred := focused blurred.CursorLine = lipgloss.NewStyle().Background(lipgloss.Color(t.Surface)) input.FocusedStyle = focused input.BlurredStyle = blurred }