theme.go RawHistoryFolder
1// Package styles provides reusable lipgloss-based TUI components.
2package styles
3
4import (
5 "github.com/charmbracelet/lipgloss"
6
7 "github.com/bnema/dumber/internal/infrastructure/config"
8)
9
10// Theme holds lipgloss colors and styles derived from config.
11type Theme struct {
12 // Base colors (from config.ColorPalette)
13 Background lipgloss.Color
14 Surface lipgloss.Color
15 SurfaceVariant lipgloss.Color
16 Text lipgloss.Color
17 Muted lipgloss.Color
18 Accent lipgloss.Color
19 Border lipgloss.Color
20
21 // Additional semantic colors
22 Error lipgloss.Color
23 Warning lipgloss.Color
24 Success lipgloss.Color
25
26 // Pre-built styles
27 Title lipgloss.Style
28 Subtitle lipgloss.Style
29 Normal lipgloss.Style
30 Subtle lipgloss.Style
31 Highlight lipgloss.Style
32 ErrorStyle lipgloss.Style
33 WarningStyle lipgloss.Style
34 SuccessStyle lipgloss.Style
35
36 // Component styles
37 ActiveTab lipgloss.Style
38 InactiveTab lipgloss.Style
39 TabBar lipgloss.Style
40
41 ListItem lipgloss.Style
42 ListItemSelected lipgloss.Style
43 ListItemTitle lipgloss.Style
44 ListItemDesc lipgloss.Style
45
46 Badge lipgloss.Style
47 BadgeMuted lipgloss.Style
48
49 Input lipgloss.Style
50 InputFocused lipgloss.Style
51
52 HelpKey lipgloss.Style
53 HelpDesc lipgloss.Style
54
55 Box lipgloss.Style
56 BoxHeader lipgloss.Style
57}
58
59// DefaultDarkPalette returns hardcoded dark theme colors.
60func DefaultDarkPalette() config.ColorPalette {
61 return config.ColorPalette{
62 Background: "#0a0a0b",
63 Surface: "#1a1a1b",
64 SurfaceVariant: "#2d2d2d",
65 Text: "#ffffff",
66 Muted: "#909090",
67 Accent: "#4ade80",
68 Border: "#333333",
69 }
70}
71
72// NewTheme creates a Theme from config, using dark palette only.
73func NewTheme(cfg *config.Config) *Theme {
74 var p config.ColorPalette
75 if cfg != nil && cfg.Appearance.DarkPalette.Background != "" {
76 p = cfg.Appearance.DarkPalette
77 } else {
78 p = DefaultDarkPalette()
79 }
80
81 return NewThemeFromPalette(p)
82}
83
84// NewThemeFromPalette creates a Theme from a ColorPalette.
85func NewThemeFromPalette(p config.ColorPalette) *Theme {
86 t := &Theme{
87 Background: lipgloss.Color(p.Background),
88 Surface: lipgloss.Color(p.Surface),
89 SurfaceVariant: lipgloss.Color(p.SurfaceVariant),
90 Text: lipgloss.Color(p.Text),
91 Muted: lipgloss.Color(p.Muted),
92 Accent: lipgloss.Color(p.Accent),
93 Border: lipgloss.Color(p.Border),
94
95 // Semantic colors (not in config, use sensible defaults)
96 Error: lipgloss.Color("#ef4444"),
97 Warning: lipgloss.Color("#f59e0b"),
98 Success: lipgloss.Color(p.Accent), // Use accent as success
99 }
100
101 // Build derived styles
102 t.buildStyles()
103 return t
104}
105
106// buildStyles creates all derived lipgloss styles.
107func (t *Theme) buildStyles() {
108 // Text styles
109 t.Title = lipgloss.NewStyle().
110 Foreground(t.Text).
111 Bold(true)
112
113 t.Subtitle = lipgloss.NewStyle().
114 Foreground(t.Muted).
115 Bold(true)
116
117 t.Normal = lipgloss.NewStyle().
118 Foreground(t.Text)
119
120 t.Subtle = lipgloss.NewStyle().
121 Foreground(t.Muted)
122
123 t.Highlight = lipgloss.NewStyle().
124 Foreground(t.Accent).
125 Bold(true)
126
127 t.ErrorStyle = lipgloss.NewStyle().
128 Foreground(t.Error)
129
130 t.WarningStyle = lipgloss.NewStyle().
131 Foreground(t.Warning)
132
133 t.SuccessStyle = lipgloss.NewStyle().
134 Foreground(t.Success)
135
136 // Tab styles
137 t.ActiveTab = lipgloss.NewStyle().
138 Foreground(t.Background).
139 Background(t.Accent).
140 Padding(0, 2).
141 Bold(true)
142
143 t.InactiveTab = lipgloss.NewStyle().
144 Foreground(t.Muted).
145 Background(t.Surface).
146 Padding(0, 2)
147
148 t.TabBar = lipgloss.NewStyle().
149 Background(t.Surface).
150 BorderStyle(lipgloss.NormalBorder()).
151 BorderBottom(true).
152 BorderForeground(t.Border)
153
154 // List item styles
155 t.ListItem = lipgloss.NewStyle().
156 Foreground(t.Text).
157 PaddingLeft(2)
158
159 t.ListItemSelected = lipgloss.NewStyle().
160 Foreground(t.Accent).
161 Background(t.SurfaceVariant).
162 PaddingLeft(2).
163 Bold(true)
164
165 t.ListItemTitle = lipgloss.NewStyle().
166 Foreground(t.Text)
167
168 t.ListItemDesc = lipgloss.NewStyle().
169 Foreground(t.Muted)
170
171 // Badge styles
172 t.Badge = lipgloss.NewStyle().
173 Foreground(t.Background).
174 Background(t.Accent).
175 Padding(0, 1)
176
177 t.BadgeMuted = lipgloss.NewStyle().
178 Foreground(t.Text).
179 Background(t.SurfaceVariant).
180 Padding(0, 1)
181
182 // Input styles
183 t.Input = lipgloss.NewStyle().
184 Foreground(t.Text).
185 Background(t.Surface).
186 BorderStyle(lipgloss.RoundedBorder()).
187 BorderForeground(t.Border).
188 Padding(0, 1)
189
190 t.InputFocused = lipgloss.NewStyle().
191 Foreground(t.Text).
192 Background(t.Surface).
193 BorderStyle(lipgloss.RoundedBorder()).
194 BorderForeground(t.Accent).
195 Padding(0, 1)
196
197 // Help styles
198 t.HelpKey = lipgloss.NewStyle().
199 Foreground(t.Accent)
200
201 t.HelpDesc = lipgloss.NewStyle().
202 Foreground(t.Muted)
203
204 // Box/container styles
205 t.Box = lipgloss.NewStyle().
206 BorderStyle(lipgloss.RoundedBorder()).
207 BorderForeground(t.Border).
208 Padding(1, 2)
209
210 t.BoxHeader = lipgloss.NewStyle().
211 Foreground(t.Text).
212 Bold(true).
213 BorderStyle(lipgloss.NormalBorder()).
214 BorderBottom(true).
215 BorderForeground(t.Border).
216 MarginBottom(1)
217}
218
219// Renderer returns a lipgloss renderer for the current output.
220func (t *Theme) Renderer() *lipgloss.Renderer {
221 if t == nil {
222 return lipgloss.DefaultRenderer()
223 }
224 return lipgloss.DefaultRenderer()
225}