on main help sign in
chore(ui): theme prep
commit755f6fae6f723333adfe47c01949409d243fdfdb authorChristian Rocha <christian@rocha.is> dateSun 26 Apr 2026, 10:46 (6 months ago) parentf686999
10 files changed+1027−943
internal/app/app.go+1−1 internal/cmd/run.go+1−1 internal/cmd/session.go+2−2 internal/ui/chat/tool_result_content_test.go+1−1 internal/ui/common/common.go+1−1 internal/ui/logo/example/main.go+1−1 internal/ui/model/skills_test.go+2−2 internal/ui/styles/quickstyle.go+968 internal/ui/styles/styles.go+9−934 internal/ui/styles/themes.go+41
internal/app/app.go+1−1
@@ -233,7 +233,7 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt,
233233 progress = app.config.Config().Options.Progress == nil || *app.config.Config().Options.Progress
234234
235235 if !hideSpinner && stderrTTY {
236− t := styles.DefaultStyles()
236+ t := styles.CharmtonePantera()
237237
238238 // Detect background color to set the appropriate color for the
239239 // spinner's 'Generating...' text. Without this, that text would be
internal/cmd/run.go+1−1
@@ -189,7 +189,7 @@ func runNonInteractive(
189189 progress = ws.Config.Options.Progress == nil || *ws.Config.Options.Progress
190190
191191 if !hideSpinner && stderrTTY {
192− t := styles.DefaultStyles()
192+ t := styles.CharmtonePantera()
193193
194194 hasDarkBG := true
195195 if stdinTTY && stdoutTTY {
internal/cmd/session.go+2−2
@@ -438,7 +438,7 @@ func outputSessionJSON(w io.Writer, sess session.Session, msgs []*message.Messag
438438}
439439
440440func outputSessionHuman(ctx context.Context, sess session.Session, msgs []*message.Message) error {
441− sty := styles.DefaultStyles()
441+ styles := styles.CharmtonePantera()
442442 toolResults := chat.BuildToolResultMap(msgs)
443443
444444 width := sessionOutputWidth
@@ -479,7 +479,7 @@ func outputSessionHuman(ctx context.Context, sess session.Session, msgs []*messa
479479
480480 first := true
481481 for _, msg := range msgs {
482− items := chat.ExtractMessageItems(&sty, msg, toolResults)
482+ items := chat.ExtractMessageItems(&styles, msg, toolResults)
483483 for _, item := range items {
484484 if !first {
485485 fmt.Fprintln(&buf)
internal/ui/chat/tool_result_content_test.go+1−1
@@ -56,7 +56,7 @@ func TestLooksLikeMarkdown(t *testing.T) {
5656func TestRenderToolResultTextContent(t *testing.T) {
5757 t.Parallel()
5858
59− sty := styles.DefaultStyles()
59+ sty := styles.CharmtonePantera()
6060 styPtr := &sty
6161 widths := toolResultContentWidths{Body: 80, Diff: 82}
6262
internal/ui/common/common.go+1−1
@@ -33,7 +33,7 @@ func (c *Common) Config() *config.Config {
3333
3434// DefaultCommon returns the default common UI configurations.
3535func DefaultCommon(ws workspace.Workspace) *Common {
36− s := styles.DefaultStyles()
36+ s := styles.CharmtonePantera()
3737 return &Common{
3838 Workspace: ws,
3939 Styles: &s,
internal/ui/logo/example/main.go+1−1
@@ -18,7 +18,7 @@ func main() {
1818 fmt.Fprintf(os.Stderr, "Could not get terminal size: %s", err)
1919 }
2020
21− s := styles.DefaultStyles()
21+ s := styles.CharmtonePantera()
2222 opts := logo.Opts{
2323 FieldColor: s.Logo.FieldColor,
2424 TitleColorA: s.Logo.TitleColorA,
internal/ui/model/skills_test.go+2−2
@@ -16,7 +16,7 @@ import (
1616func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
1717 t.Parallel()
1818
19− st := uistyles.DefaultStyles()
19+ st := uistyles.CharmtonePantera()
2020 ui := &UI{
2121 com: &common.Common{Styles: &st},
2222 skillStates: []*skills.SkillState{
@@ -61,7 +61,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
6161func TestSkillStatusItemsExcludesDisabledSkills(t *testing.T) {
6262 t.Parallel()
6363
64− st := uistyles.DefaultStyles()
64+ st := uistyles.CharmtonePantera()
6565 ui := &UI{
6666 com: &common.Common{
6767 Styles: &st,
internal/ui/styles/quickstyle.gonew file+968
@@ -0,0 +1,968 @@
1+package styles
2+
3+import (
4+ "image/color"
5+
6+ "charm.land/bubbles/v2/filepicker"
7+ "charm.land/bubbles/v2/help"
8+ "charm.land/bubbles/v2/textarea"
9+ "charm.land/bubbles/v2/textinput"
10+ tea "charm.land/bubbletea/v2"
11+ "charm.land/glamour/v2/ansi"
12+ "charm.land/lipgloss/v2"
13+ "github.com/charmbracelet/crush/internal/ui/diffview"
14+ "github.com/charmbracelet/x/exp/charmtone"
15+)
16+
17+// quickStyleOpts is the palette of colors used by quickStyle to build a
18+// complete Styles value. Each field maps to a semantic role in the UI.
19+type quickStyleOpts struct {
20+ // Brand.
21+ primary color.Color
22+ secondary color.Color
23+ tertiary color.Color
24+
25+ // Foregrounds.
26+ fgBase color.Color
27+ fgMuted color.Color
28+ fgHalfMuted color.Color
29+ fgSubtle color.Color
30+
31+ // Contrast pairings: foregrounds designed to sit on top of a
32+ // matching background role.
33+ onPrimary color.Color // foreground on primary backgrounds.
34+ onAccent color.Color // foreground on saturated status/accent backgrounds.
35+
36+ // Backgrounds.
37+ bgBase color.Color
38+ bgBaseLighter color.Color
39+ bgSubtle color.Color
40+ bgOverlay color.Color
41+
42+ // Borders.
43+ border color.Color
44+ borderFocus color.Color
45+
46+ // Status.
47+ danger color.Color
48+ error color.Color
49+ warning color.Color
50+ warningStrong color.Color
51+ busy color.Color
52+ info color.Color
53+ infoSubtle color.Color
54+ infoMuted color.Color
55+ success color.Color
56+ successSubtle color.Color
57+ successMuted color.Color
58+}
59+
60+// quickStyle builds a complete Styles value from a palette of semantic
61+// colors. Themes should populate quickStyleOpts and call this rather than
62+// re-implementing every style rule.
63+//
64+// The idea here is that you can do most of the work with quickStyle, then
65+// add overrides as needed.
66+func quickStyle(o quickStyleOpts) Styles {
67+ var (
68+ primary = o.primary
69+ secondary = o.secondary
70+ tertiary = o.tertiary
71+
72+ fgBase = o.fgBase
73+ fgMuted = o.fgMuted
74+ fgHalfMuted = o.fgHalfMuted
75+ fgSubtle = o.fgSubtle
76+
77+ onPrimary = o.onPrimary
78+ onAccent = o.onAccent
79+
80+ bgBase = o.bgBase
81+ bgBaseLighter = o.bgBaseLighter
82+ bgSubtle = o.bgSubtle
83+ bgOverlay = o.bgOverlay
84+
85+ border = o.border
86+ borderFocus = o.borderFocus
87+
88+ danger = o.danger
89+ error = o.error
90+ warning = o.warning
91+ warningStrong = o.warningStrong
92+ busy = o.busy
93+ info = o.info
94+ infoSubtle = o.infoSubtle
95+ infoMuted = o.infoMuted
96+ success = o.success
97+ successSubtle = o.successSubtle
98+ successMuted = o.successMuted
99+ )
100+
101+ var (
102+ base = lipgloss.NewStyle().Foreground(fgBase)
103+ muted = lipgloss.NewStyle().Foreground(fgMuted)
104+ subtle = lipgloss.NewStyle().Foreground(fgSubtle)
105+ s Styles
106+ )
107+
108+ s.Background = bgBase
109+
110+ // Populate color fields
111+ s.WorkingGradFromColor = primary
112+ s.WorkingGradToColor = secondary
113+ s.WorkingLabelColor = fgBase
114+
115+ s.TextInput = textinput.Styles{
116+ Focused: textinput.StyleState{
117+ Text: base,
118+ Placeholder: base.Foreground(fgSubtle),
119+ Prompt: base.Foreground(tertiary),
120+ Suggestion: base.Foreground(fgSubtle),
121+ },
122+ Blurred: textinput.StyleState{
123+ Text: base.Foreground(fgMuted),
124+ Placeholder: base.Foreground(fgSubtle),
125+ Prompt: base.Foreground(fgMuted),
126+ Suggestion: base.Foreground(fgSubtle),
127+ },
128+ Cursor: textinput.CursorStyle{
129+ Color: secondary,
130+ Shape: tea.CursorBlock,
131+ Blink: true,
132+ },
133+ }
134+
135+ s.Editor.Textarea = textarea.Styles{
136+ Focused: textarea.StyleState{
137+ Base: base,
138+ Text: base,
139+ LineNumber: base.Foreground(fgSubtle),
140+ CursorLine: base,
141+ CursorLineNumber: base.Foreground(fgSubtle),
142+ Placeholder: base.Foreground(fgSubtle),
143+ Prompt: base.Foreground(tertiary),
144+ },
145+ Blurred: textarea.StyleState{
146+ Base: base,
147+ Text: base.Foreground(fgMuted),
148+ LineNumber: base.Foreground(fgMuted),
149+ CursorLine: base,
150+ CursorLineNumber: base.Foreground(fgMuted),
151+ Placeholder: base.Foreground(fgSubtle),
152+ Prompt: base.Foreground(fgMuted),
153+ },
154+ Cursor: textarea.CursorStyle{
155+ Color: secondary,
156+ Shape: tea.CursorBlock,
157+ Blink: true,
158+ },
159+ }
160+
161+ s.Markdown = ansi.StyleConfig{
162+ Document: ansi.StyleBlock{
163+ StylePrimitive: ansi.StylePrimitive{
164+ // BlockPrefix: "\n",
165+ // BlockSuffix: "\n",
166+ Color: hex(fgHalfMuted),
167+ },
168+ // Margin: new(uint(defaultMargin)),
169+ },
170+ BlockQuote: ansi.StyleBlock{
171+ StylePrimitive: ansi.StylePrimitive{},
172+ Indent: new(uint(1)),
173+ IndentToken: new("│ "),
174+ },
175+ List: ansi.StyleList{
176+ LevelIndent: defaultListIndent,
177+ },
178+ Heading: ansi.StyleBlock{
179+ StylePrimitive: ansi.StylePrimitive{
180+ BlockSuffix: "\n",
181+ Color: hex(info),
182+ Bold: new(true),
183+ },
184+ },
185+ H1: ansi.StyleBlock{
186+ StylePrimitive: ansi.StylePrimitive{
187+ Prefix: " ",
188+ Suffix: " ",
189+ Color: hex(warning),
190+ BackgroundColor: hex(primary),
191+ Bold: new(true),
192+ },
193+ },
194+ H2: ansi.StyleBlock{
195+ StylePrimitive: ansi.StylePrimitive{
196+ Prefix: "## ",
197+ },
198+ },
199+ H3: ansi.StyleBlock{
200+ StylePrimitive: ansi.StylePrimitive{
201+ Prefix: "### ",
202+ },
203+ },
204+ H4: ansi.StyleBlock{
205+ StylePrimitive: ansi.StylePrimitive{
206+ Prefix: "#### ",
207+ },
208+ },
209+ H5: ansi.StyleBlock{
210+ StylePrimitive: ansi.StylePrimitive{
211+ Prefix: "##### ",
212+ },
213+ },
214+ H6: ansi.StyleBlock{
215+ StylePrimitive: ansi.StylePrimitive{
216+ Prefix: "###### ",
217+ Color: hex(successMuted),
218+ Bold: new(false),
219+ },
220+ },
221+ Strikethrough: ansi.StylePrimitive{
222+ CrossedOut: new(true),
223+ },
224+ Emph: ansi.StylePrimitive{
225+ Italic: new(true),
226+ },
227+ Strong: ansi.StylePrimitive{
228+ Bold: new(true),
229+ },
230+ HorizontalRule: ansi.StylePrimitive{
231+ Color: hex(border),
232+ Format: "\n--------\n",
233+ },
234+ Item: ansi.StylePrimitive{
235+ BlockPrefix: "• ",
236+ },
237+ Enumeration: ansi.StylePrimitive{
238+ BlockPrefix: ". ",
239+ },
240+ Task: ansi.StyleTask{
241+ StylePrimitive: ansi.StylePrimitive{},
242+ Ticked: "[✓] ",
243+ Unticked: "[ ] ",
244+ },
245+ Link: ansi.StylePrimitive{
246+ Color: hex(charmtone.Zinc),
247+ Underline: new(true),
248+ },
249+ LinkText: ansi.StylePrimitive{
250+ Color: hex(successMuted),
251+ Bold: new(true),
252+ },
253+ Image: ansi.StylePrimitive{
254+ Color: hex(charmtone.Cheeky),
255+ Underline: new(true),
256+ },
257+ ImageText: ansi.StylePrimitive{
258+ Color: hex(fgMuted),
259+ Format: "Image: {{.text}} →",
260+ },
261+ Code: ansi.StyleBlock{
262+ StylePrimitive: ansi.StylePrimitive{
263+ Prefix: " ",
264+ Suffix: " ",
265+ Color: hex(danger),
266+ BackgroundColor: hex(bgSubtle),
267+ },
268+ },
269+ CodeBlock: ansi.StyleCodeBlock{
270+ StyleBlock: ansi.StyleBlock{
271+ StylePrimitive: ansi.StylePrimitive{
272+ Color: hex(bgSubtle),
273+ },
274+ Margin: new(uint(defaultMargin)),
275+ },
276+ Chroma: &ansi.Chroma{
277+ Text: ansi.StylePrimitive{
278+ Color: hex(fgHalfMuted),
279+ },
280+ Error: ansi.StylePrimitive{
281+ Color: hex(onAccent),
282+ BackgroundColor: hex(error),
283+ },
284+ Comment: ansi.StylePrimitive{
285+ Color: hex(fgSubtle),
286+ },
287+ CommentPreproc: ansi.StylePrimitive{
288+ Color: hex(charmtone.Bengal),
289+ },
290+ Keyword: ansi.StylePrimitive{
291+ Color: hex(info),
292+ },
293+ KeywordReserved: ansi.StylePrimitive{
294+ Color: hex(charmtone.Pony),
295+ },
296+ KeywordNamespace: ansi.StylePrimitive{
297+ Color: hex(charmtone.Pony),
298+ },
299+ KeywordType: ansi.StylePrimitive{
300+ Color: hex(charmtone.Guppy),
301+ },
302+ Operator: ansi.StylePrimitive{
303+ Color: hex(charmtone.Salmon),
304+ },
305+ Punctuation: ansi.StylePrimitive{
306+ Color: hex(warning),
307+ },
308+ Name: ansi.StylePrimitive{
309+ Color: hex(fgHalfMuted),
310+ },
311+ NameBuiltin: ansi.StylePrimitive{
312+ Color: hex(charmtone.Cheeky),
313+ },
314+ NameTag: ansi.StylePrimitive{
315+ Color: hex(charmtone.Mauve),
316+ },
317+ NameAttribute: ansi.StylePrimitive{
318+ Color: hex(charmtone.Hazy),
319+ },
320+ NameClass: ansi.StylePrimitive{
321+ Color: hex(charmtone.Salt),
322+ Underline: new(true),
323+ Bold: new(true),
324+ },
325+ NameDecorator: ansi.StylePrimitive{
326+ Color: hex(charmtone.Citron),
327+ },
328+ NameFunction: ansi.StylePrimitive{
329+ Color: hex(successMuted),
330+ },
331+ LiteralNumber: ansi.StylePrimitive{
332+ Color: hex(success),
333+ },
334+ LiteralString: ansi.StylePrimitive{
335+ Color: hex(charmtone.Cumin),
336+ },
337+ LiteralStringEscape: ansi.StylePrimitive{
338+ Color: hex(successSubtle),
339+ },
340+ GenericDeleted: ansi.StylePrimitive{
341+ Color: hex(danger),
342+ },
343+ GenericEmph: ansi.StylePrimitive{
344+ Italic: new(true),
345+ },
346+ GenericInserted: ansi.StylePrimitive{
347+ Color: hex(successMuted),
348+ },
349+ GenericStrong: ansi.StylePrimitive{
350+ Bold: new(true),
351+ },
352+ GenericSubheading: ansi.StylePrimitive{
353+ Color: hex(fgMuted),
354+ },
355+ Background: ansi.StylePrimitive{
356+ BackgroundColor: hex(bgSubtle),
357+ },
358+ },
359+ },
360+ Table: ansi.StyleTable{
361+ StyleBlock: ansi.StyleBlock{
362+ StylePrimitive: ansi.StylePrimitive{},
363+ },
364+ },
365+ DefinitionDescription: ansi.StylePrimitive{
366+ BlockPrefix: "\n ",
367+ },
368+ }
369+
370+ // QuietMarkdown style - muted colors on subtle background for thinking content.
371+ plainBg := hex(bgBaseLighter)
372+ plainFg := hex(fgMuted)
373+ s.QuietMarkdown = ansi.StyleConfig{
374+ Document: ansi.StyleBlock{
375+ StylePrimitive: ansi.StylePrimitive{
376+ Color: plainFg,
377+ BackgroundColor: plainBg,
378+ },
379+ },
380+ BlockQuote: ansi.StyleBlock{
381+ StylePrimitive: ansi.StylePrimitive{
382+ Color: plainFg,
383+ BackgroundColor: plainBg,
384+ },
385+ Indent: new(uint(1)),
386+ IndentToken: new("│ "),
387+ },
388+ List: ansi.StyleList{
389+ LevelIndent: defaultListIndent,
390+ },
391+ Heading: ansi.StyleBlock{
392+ StylePrimitive: ansi.StylePrimitive{
393+ BlockSuffix: "\n",
394+ Bold: new(true),
395+ Color: plainFg,
396+ BackgroundColor: plainBg,
397+ },
398+ },
399+ H1: ansi.StyleBlock{
400+ StylePrimitive: ansi.StylePrimitive{
401+ Prefix: " ",
402+ Suffix: " ",
403+ Bold: new(true),
404+ Color: plainFg,
405+ BackgroundColor: plainBg,
406+ },
407+ },
408+ H2: ansi.StyleBlock{
409+ StylePrimitive: ansi.StylePrimitive{
410+ Prefix: "## ",
411+ Color: plainFg,
412+ BackgroundColor: plainBg,
413+ },
414+ },
415+ H3: ansi.StyleBlock{
416+ StylePrimitive: ansi.StylePrimitive{
417+ Prefix: "### ",
418+ Color: plainFg,
419+ BackgroundColor: plainBg,
420+ },
421+ },
422+ H4: ansi.StyleBlock{
423+ StylePrimitive: ansi.StylePrimitive{
424+ Prefix: "#### ",
425+ Color: plainFg,
426+ BackgroundColor: plainBg,
427+ },
428+ },
429+ H5: ansi.StyleBlock{
430+ StylePrimitive: ansi.StylePrimitive{
431+ Prefix: "##### ",
432+ Color: plainFg,
433+ BackgroundColor: plainBg,
434+ },
435+ },
436+ H6: ansi.StyleBlock{
437+ StylePrimitive: ansi.StylePrimitive{
438+ Prefix: "###### ",
439+ Color: plainFg,
440+ BackgroundColor: plainBg,
441+ },
442+ },
443+ Strikethrough: ansi.StylePrimitive{
444+ CrossedOut: new(true),
445+ Color: plainFg,
446+ BackgroundColor: plainBg,
447+ },
448+ Emph: ansi.StylePrimitive{
449+ Italic: new(true),
450+ Color: plainFg,
451+ BackgroundColor: plainBg,
452+ },
453+ Strong: ansi.StylePrimitive{
454+ Bold: new(true),
455+ Color: plainFg,
456+ BackgroundColor: plainBg,
457+ },
458+ HorizontalRule: ansi.StylePrimitive{
459+ Format: "\n--------\n",
460+ Color: plainFg,
461+ BackgroundColor: plainBg,
462+ },
463+ Item: ansi.StylePrimitive{
464+ BlockPrefix: "• ",
465+ Color: plainFg,
466+ BackgroundColor: plainBg,
467+ },
468+ Enumeration: ansi.StylePrimitive{
469+ BlockPrefix: ". ",
470+ Color: plainFg,
471+ BackgroundColor: plainBg,
472+ },
473+ Task: ansi.StyleTask{
474+ StylePrimitive: ansi.StylePrimitive{
475+ Color: plainFg,
476+ BackgroundColor: plainBg,
477+ },
478+ Ticked: "[✓] ",
479+ Unticked: "[ ] ",
480+ },
481+ Link: ansi.StylePrimitive{
482+ Underline: new(true),
483+ Color: plainFg,
484+ BackgroundColor: plainBg,
485+ },
486+ LinkText: ansi.StylePrimitive{
487+ Bold: new(true),
488+ Color: plainFg,
489+ BackgroundColor: plainBg,
490+ },
491+ Image: ansi.StylePrimitive{
492+ Underline: new(true),
493+ Color: plainFg,
494+ BackgroundColor: plainBg,
495+ },
496+ ImageText: ansi.StylePrimitive{
497+ Format: "Image: {{.text}} →",
498+ Color: plainFg,
499+ BackgroundColor: plainBg,
500+ },
501+ Code: ansi.StyleBlock{
502+ StylePrimitive: ansi.StylePrimitive{
503+ Prefix: " ",
504+ Suffix: " ",
505+ Color: plainFg,
506+ BackgroundColor: plainBg,
507+ },
508+ },
509+ CodeBlock: ansi.StyleCodeBlock{
510+ StyleBlock: ansi.StyleBlock{
511+ StylePrimitive: ansi.StylePrimitive{
512+ Color: plainFg,
513+ BackgroundColor: plainBg,
514+ },
515+ Margin: new(uint(defaultMargin)),
516+ },
517+ },
518+ Table: ansi.StyleTable{
519+ StyleBlock: ansi.StyleBlock{
520+ StylePrimitive: ansi.StylePrimitive{
521+ Color: plainFg,
522+ BackgroundColor: plainBg,
523+ },
524+ },
525+ },
526+ DefinitionDescription: ansi.StylePrimitive{
527+ BlockPrefix: "\n ",
528+ Color: plainFg,
529+ BackgroundColor: plainBg,
530+ },
531+ }
532+
533+ s.Help = help.Styles{
534+ ShortKey: base.Foreground(fgMuted),
535+ ShortDesc: base.Foreground(fgSubtle),
536+ ShortSeparator: base.Foreground(border),
537+ Ellipsis: base.Foreground(border),
538+ FullKey: base.Foreground(fgMuted),
539+ FullDesc: base.Foreground(fgSubtle),
540+ FullSeparator: base.Foreground(border),
541+ }
542+
543+ s.Diff = diffview.Style{
544+ DividerLine: diffview.LineStyle{
545+ LineNumber: lipgloss.NewStyle().
546+ Foreground(fgHalfMuted).
547+ Background(bgBaseLighter),
548+ Code: lipgloss.NewStyle().
549+ Foreground(fgHalfMuted).
550+ Background(bgBaseLighter),
551+ },
552+ MissingLine: diffview.LineStyle{
553+ LineNumber: lipgloss.NewStyle().
554+ Background(bgBaseLighter),
555+ Code: lipgloss.NewStyle().
556+ Background(bgBaseLighter),
557+ },
558+ EqualLine: diffview.LineStyle{
559+ LineNumber: lipgloss.NewStyle().
560+ Foreground(fgMuted).
561+ Background(bgBase),
562+ Code: lipgloss.NewStyle().
563+ Foreground(fgMuted).
564+ Background(bgBase),
565+ },
566+ InsertLine: diffview.LineStyle{
567+ LineNumber: lipgloss.NewStyle().
568+ Foreground(lipgloss.Color("#629657")).
569+ Background(lipgloss.Color("#2b322a")),
570+ Symbol: lipgloss.NewStyle().
571+ Foreground(lipgloss.Color("#629657")).
572+ Background(lipgloss.Color("#323931")),
573+ Code: lipgloss.NewStyle().
574+ Background(lipgloss.Color("#323931")),
575+ },
576+ DeleteLine: diffview.LineStyle{
577+ LineNumber: lipgloss.NewStyle().
578+ Foreground(lipgloss.Color("#a45c59")).
579+ Background(lipgloss.Color("#312929")),
580+ Symbol: lipgloss.NewStyle().
581+ Foreground(lipgloss.Color("#a45c59")).
582+ Background(lipgloss.Color("#383030")),
583+ Code: lipgloss.NewStyle().
584+ Background(lipgloss.Color("#383030")),
585+ },
586+ Filename: diffview.LineStyle{
587+ LineNumber: lipgloss.NewStyle().
588+ Foreground(fgHalfMuted).
589+ Background(bgBaseLighter),
590+ Code: lipgloss.NewStyle().
591+ Foreground(fgHalfMuted).
592+ Background(bgBaseLighter),
593+ },
594+ }
595+
596+ s.FilePicker = filepicker.Styles{
597+ DisabledCursor: base.Foreground(fgMuted),
598+ Cursor: base.Foreground(fgBase),
599+ Symlink: base.Foreground(fgSubtle),
600+ Directory: base.Foreground(primary),
601+ File: base.Foreground(fgBase),
602+ DisabledFile: base.Foreground(fgMuted),
603+ DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
604+ Permission: base.Foreground(fgMuted),
605+ Selected: base.Background(primary).Foreground(fgBase),
606+ FileSize: base.Foreground(fgMuted),
607+ EmptyDirectory: base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
608+ }
609+
610+ // borders
611+ s.ToolCallSuccess = lipgloss.NewStyle().Foreground(success).SetString(ToolSuccess)
612+
613+ s.Header.Charm = base.Foreground(secondary)
614+ s.Header.Diagonals = base.Foreground(primary)
615+ s.Header.Percentage = muted
616+ s.Header.Keystroke = muted
617+ s.Header.KeystrokeTip = subtle
618+ s.Header.WorkingDir = muted
619+ s.Header.Separator = subtle
620+ s.Header.Wrapper = lipgloss.NewStyle().Foreground(fgBase)
621+ s.Header.LogoGradCanvas = lipgloss.NewStyle()
622+ s.Header.LogoGradFromColor = secondary
623+ s.Header.LogoGradToColor = primary
624+
625+ s.CompactDetails.Title = base
626+ s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
627+ s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
628+
629+ // Tool rendering styles
630+ s.Tool.IconPending = base.Foreground(successMuted).SetString(ToolPending)
631+ s.Tool.IconSuccess = base.Foreground(success).SetString(ToolSuccess)
632+ s.Tool.IconError = base.Foreground(error).SetString(ToolError)
633+ s.Tool.IconCancelled = muted.SetString(ToolPending)
634+
635+ s.Tool.NameNormal = base.Foreground(info)
636+ s.Tool.NameNested = base.Foreground(info)
637+
638+ s.Tool.ParamMain = subtle
639+ s.Tool.ParamKey = subtle
640+
641+ // Content rendering - prepared styles that accept width parameter
642+ s.Tool.ContentLine = muted.Background(bgBaseLighter)
643+ s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
644+ s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
645+ s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
646+ s.Tool.ContentCodeBg = bgBase
647+ s.Tool.Body = base.PaddingLeft(2)
648+
649+ // Deprecated - kept for backward compatibility
650+ s.Tool.ContentBg = muted.Background(bgBaseLighter)
651+ s.Tool.ContentText = muted
652+ s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
653+
654+ s.Tool.StateWaiting = base.Foreground(fgSubtle)
655+ s.Tool.StateCancelled = base.Foreground(fgSubtle)
656+
657+ s.Tool.ErrorTag = base.Padding(0, 1).Background(danger).Foreground(onAccent)
658+ s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
659+
660+ // Diff and multi-edit styles
661+ s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
662+ s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(onAccent)
663+ s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
664+
665+ // Job header styles
666+ s.Tool.JobIconPending = base.Foreground(successMuted)
667+ s.Tool.JobIconError = base.Foreground(error)
668+ s.Tool.JobIconSuccess = base.Foreground(success)
669+ s.Tool.JobToolName = base.Foreground(info)
670+ s.Tool.JobAction = base.Foreground(infoMuted)
671+ s.Tool.JobPID = muted
672+ s.Tool.JobDescription = subtle
673+
674+ // Agent task styles
675+ s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(infoSubtle).Foreground(onAccent)
676+ s.Tool.AgentPrompt = muted
677+
678+ // Agentic fetch styles
679+ s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(success).Foreground(border)
680+
681+ // Todo styles
682+ s.Tool.TodoRatio = base.Foreground(infoMuted)
683+ s.Tool.TodoCompletedIcon = base.Foreground(success)
684+ s.Tool.TodoInProgressIcon = base.Foreground(successMuted)
685+ s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
686+ s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(fgSubtle)
687+ s.Tool.TodoItem = lipgloss.NewStyle().Foreground(fgBase)
688+ s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(fgBase)
689+
690+ // MCP styles
691+ s.Tool.MCPName = base.Foreground(info)
692+ s.Tool.MCPToolName = base.Foreground(infoMuted)
693+ s.Tool.MCPArrow = base.Foreground(info).SetString(ArrowRightIcon)
694+
695+ // Loading indicators for images, skills
696+ s.Tool.ResourceLoadedText = base.Foreground(success)
697+ s.Tool.ResourceLoadedIndicator = base.Foreground(successMuted)
698+ s.Tool.ResourceName = base
699+ s.Tool.MediaType = base
700+ s.Tool.ResourceSize = base.Foreground(fgMuted)
701+
702+ // Tool-call action verbs and result-list styling.
703+ s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(successSubtle)
704+ s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(danger)
705+ s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
706+ s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
707+ s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
708+ s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
709+
710+ // Buttons
711+ s.Button.Focused = lipgloss.NewStyle().Foreground(onAccent).Background(secondary)
712+ s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
713+
714+ // Editor
715+ s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(successMuted).SetString("::: ")
716+ s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
717+ s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(fgSubtle).Background(busy).Bold(true).SetString(" ! ")
718+ s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(bgBase).Background(fgMuted)
719+ s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(warning).SetString(":::")
720+ s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(fgMuted)
721+
722+ s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
723+ s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
724+ s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
725+
726+ // Logo
727+ s.Logo.FieldColor = primary
728+ s.Logo.TitleColorA = secondary
729+ s.Logo.TitleColorB = primary
730+ s.Logo.CharmColor = secondary
731+ s.Logo.VersionColor = primary
732+ s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
733+ s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
734+ s.Logo.GradCanvas = lipgloss.NewStyle()
735+ s.Logo.SmallGradFromColor = secondary
736+ s.Logo.SmallGradToColor = primary
737+
738+ // Section
739+ s.Section.Title = subtle
740+ s.Section.Line = base.Foreground(border)
741+
742+ // Initialize
743+ s.Initialize.Header = base
744+ s.Initialize.Content = muted
745+ s.Initialize.Accent = base.Foreground(successMuted)
746+
747+ // ResourceGroup (LSP/MCP/skills sidebar lists).
748+ s.Resource.Heading = lipgloss.NewStyle().Foreground(fgSubtle)
749+ s.Resource.Name = lipgloss.NewStyle().Foreground(fgMuted)
750+ s.Resource.StatusText = lipgloss.NewStyle().Foreground(fgSubtle)
751+ s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(bgOverlay).SetString("●")
752+ s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(busy)
753+ s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(danger)
754+ s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(successMuted)
755+ s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
756+ s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(fgSubtle)
757+ s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
758+ s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
759+ s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
760+ s.Resource.DefaultTitleFg = fgMuted
761+ s.Resource.DefaultDescFg = fgSubtle
762+
763+ // LSP
764+ s.LSP.ErrorDiagnostic = base.Foreground(error)
765+ s.LSP.WarningDiagnostic = base.Foreground(warning)
766+ s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
767+ s.LSP.InfoDiagnostic = base.Foreground(info)
768+
769+ // Files
770+ s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
771+ s.Files.Additions = lipgloss.NewStyle().Foreground(successMuted)
772+ s.Files.Deletions = lipgloss.NewStyle().Foreground(error)
773+ s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
774+ s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
775+ s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
776+
777+ // Sidebar
778+ s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
779+ s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
780+
781+ // ModelInfo
782+ s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
783+ s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
784+ s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
785+ s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
786+ s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
787+ s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
788+ s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
789+ s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
790+
791+ // ResourceGroup
792+ s.Resource.DefaultTitleFg = fgMuted
793+ s.Resource.DefaultDescFg = fgSubtle
794+
795+ // Chat
796+ messageFocussedBorder := lipgloss.Border{
797+ Left: "▌",
798+ }
799+
800+ s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
801+ s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
802+ BorderForeground(primary).BorderStyle(lipgloss.NormalBorder())
803+ s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
804+ BorderForeground(primary).BorderStyle(messageFocussedBorder)
805+ s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
806+ s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
807+ BorderForeground(successMuted).BorderStyle(messageFocussedBorder)
808+ s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
809+ s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
810+ Background(danger).Foreground(onAccent)
811+ s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
812+ s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
813+
814+ // Message item styles
815+ s.Messages.ToolCallFocused = muted.PaddingLeft(1).
816+ BorderStyle(messageFocussedBorder).
817+ BorderLeft(true).
818+ BorderForeground(successMuted)
819+ s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
820+ // No padding or border for compact tool calls within messages
821+ s.Messages.ToolCallCompact = muted
822+ s.Messages.SectionHeader = base.PaddingLeft(2)
823+ s.Messages.AssistantInfoIcon = subtle
824+ s.Messages.AssistantInfoModel = muted
825+ s.Messages.AssistantInfoProvider = subtle
826+ s.Messages.AssistantInfoDuration = subtle
827+ s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
828+
829+ // Thinking section styles
830+ s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
831+ s.Messages.ThinkingTruncationHint = muted
832+ s.Messages.ThinkingFooterTitle = muted
833+ s.Messages.ThinkingFooterDuration = subtle
834+
835+ // Text selection.
836+ s.TextSelection = lipgloss.NewStyle().Foreground(onPrimary).Background(primary)
837+
838+ // Dialog styles
839+ s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
840+ s.Dialog.TitleText = base.Foreground(primary)
841+ s.Dialog.TitleError = base.Foreground(danger)
842+ s.Dialog.TitleAccent = base.Foreground(success).Bold(true)
843+ s.Dialog.TitleLineBase = lipgloss.NewStyle()
844+ s.Dialog.TitleGradFromColor = primary
845+ s.Dialog.TitleGradToColor = secondary
846+
847+ // Dialog.ListItem (commands, reasoning, models)
848+ s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
849+ s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
850+
851+ // Dialog.Models
852+ s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
853+
854+ // Dialog.Permissions
855+ s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
856+ s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
857+ s.Dialog.Permissions.ParamsBg = bgSubtle
858+
859+ // Dialog.Quit
860+ s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
861+ s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
862+ s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
863+ s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
864+ s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
865+ s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
866+ s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
867+ s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
868+ s.Dialog.Help.ShortSeparator = base.Foreground(border)
869+ s.Dialog.Help.Ellipsis = base.Foreground(border)
870+ s.Dialog.Help.FullKey = base.Foreground(fgMuted)
871+ s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
872+ s.Dialog.Help.FullSeparator = base.Foreground(border)
873+ s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
874+ s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
875+ s.Dialog.InputPrompt = base.Margin(1, 1)
876+
877+ s.Dialog.List = base.Margin(0, 0, 1, 0)
878+ s.Dialog.ContentPanel = base.Background(bgSubtle).Foreground(fgBase).Padding(1, 2)
879+ s.Dialog.Spinner = base.Foreground(secondary)
880+ s.Dialog.ScrollbarThumb = base.Foreground(secondary)
881+ s.Dialog.ScrollbarTrack = base.Foreground(border)
882+
883+ s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
884+
885+ // API key input dialog
886+ s.Dialog.APIKey.Spinner = base.Foreground(success)
887+
888+ // OAuth dialog
889+ s.Dialog.OAuth.Spinner = base.Foreground(successSubtle)
890+ s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(onAccent)
891+ s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(onAccent)
892+ s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(successSubtle)
893+ s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(successMuted).Underline(true)
894+ s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
895+ s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
896+ s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
897+ s.Dialog.OAuth.UserCodeBg = bgBaseLighter
898+
899+ s.Dialog.Arguments.Content = base.Padding(1)
900+ s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
901+ s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
902+ s.Dialog.Arguments.InputLabelFocused = base.Bold(true)
903+ s.Dialog.Arguments.InputRequiredMarkBlurred = base.Foreground(fgMuted).SetString("*")
904+ s.Dialog.Arguments.InputRequiredMarkFocused = base.Foreground(primary).Bold(true).SetString("*")
905+
906+ s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(danger)
907+ s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(danger)
908+ s.Dialog.Sessions.DeletingMessage = base.Padding(1)
909+ s.Dialog.Sessions.DeletingTitleGradientFromColor = danger
910+ s.Dialog.Sessions.DeletingTitleGradientToColor = primary
911+ s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
912+ s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(danger).Foreground(onAccent)
913+
914+ s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(warning)
915+ s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(warning)
916+ s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
917+ s.Dialog.Sessions.RenamingTitleGradientFromColor = warning
918+ s.Dialog.Sessions.RenamingTitleGradientToColor = tertiary
919+ s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
920+ s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
921+ s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(fgMuted)
922+ s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
923+ s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
924+
925+ s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
926+ s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(success).Padding(0, 1).Bold(true).SetString("OKAY!")
927+ s.Status.InfoIndicator = s.Status.SuccessIndicator
928+ s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
929+ s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(bgOverlay).Background(warningStrong).SetString("WARNING")
930+ s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(bgBase).Background(danger).SetString("ERROR")
931+ s.Status.SuccessMessage = base.Foreground(bgSubtle).Background(successMuted).Padding(0, 1)
932+ s.Status.InfoMessage = s.Status.SuccessMessage
933+ s.Status.UpdateMessage = s.Status.SuccessMessage
934+ s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(bgOverlay).Background(warning)
935+ s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(onAccent).Background(error)
936+
937+ // Completions styles
938+ s.Completions.Normal = base.Background(bgSubtle).Foreground(fgBase)
939+ s.Completions.Focused = base.Background(primary).Foreground(onAccent)
940+ s.Completions.Match = base.Underline(true)
941+
942+ // Attachments styles
943+ attachmentIconStyle := base.Foreground(bgSubtle).Background(success).Padding(0, 1)
944+ s.Attachments.Image = attachmentIconStyle.SetString(ImageIcon)
945+ s.Attachments.Text = attachmentIconStyle.SetString(TextIcon)
946+ s.Attachments.Normal = base.Padding(0, 1).MarginRight(1).Background(fgMuted).Foreground(fgBase)
947+ s.Attachments.Deleting = base.Padding(0, 1).Bold(true).Background(danger).Foreground(fgBase)
948+
949+ // Pills styles
950+ s.Pills.Base = base.Padding(0, 1)
951+ s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
952+ s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
953+ s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString(" •")
954+ s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
955+ s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
956+ s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
957+ s.Pills.QueueGradFromColor = error
958+ s.Pills.QueueGradToColor = secondary
959+ s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
960+ s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
961+ s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
962+ s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(successMuted)
963+ s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
964+ s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
965+ s.Pills.Area = base
966+
967+ return s
968+}
internal/ui/styles/styles.go+9−934
@@ -1,6 +1,8 @@
1+// Package styles define styling and theming for the project.
12package styles
23
34import (
5+ "fmt"
46 "image/color"
57 "strings"
68
@@ -8,12 +10,10 @@ import (
810 "charm.land/bubbles/v2/help"
911 "charm.land/bubbles/v2/textarea"
1012 "charm.land/bubbles/v2/textinput"
11− tea "charm.land/bubbletea/v2"
1213 "charm.land/glamour/v2/ansi"
1314 "charm.land/lipgloss/v2"
1415 "github.com/alecthomas/chroma/v2"
1516 "github.com/charmbracelet/crush/internal/ui/diffview"
16− "github.com/charmbracelet/x/exp/charmtone"
1717)
1818
1919const (
@@ -566,938 +566,13 @@ func (s *Styles) DialogHelpStyles() help.Styles {
566566 return help.Styles(s.Dialog.Help)
567567}
568568
569−// DefaultStyles returns the default styles for the UI.
570−func DefaultStyles() Styles {
571− var (
572− primary = charmtone.Charple
573− secondary = charmtone.Dolly
574− tertiary = charmtone.Bok
575− // accent = charmtone.Zest
576−
577− // Backgrounds
578− bgBase = charmtone.Pepper
579− bgBaseLighter = charmtone.BBQ
580− bgSubtle = charmtone.Charcoal
581− bgOverlay = charmtone.Iron
582−
583− // Foregrounds
584− fgBase = charmtone.Ash
585− fgMuted = charmtone.Squid
586− fgHalfMuted = charmtone.Smoke
587− fgSubtle = charmtone.Oyster
588− // fgSelected = charmtone.Salt
589−
590− // Borders
591− border = charmtone.Charcoal
592− borderFocus = charmtone.Charple
593−
594− // Status
595− error = charmtone.Sriracha
596− warning = charmtone.Zest
597− info = charmtone.Malibu
598−
599− // Colors
600− white = charmtone.Butter
601−
602− blueLight = charmtone.Sardine
603− blue = charmtone.Malibu
604− blueDark = charmtone.Damson
605−
606− // yellow = charmtone.Mustard
607− yellow = charmtone.Mustard
608− // citron = charmtone.Citron
609−
610− greenLight = charmtone.Bok
611− green = charmtone.Julep
612− greenDark = charmtone.Guac
613− // greenLight = charmtone.Bok
614−
615− red = charmtone.Coral
616− redDark = charmtone.Sriracha
617− // redLight = charmtone.Salmon
618− // cherry = charmtone.Cherry
619− )
620−
621− normalBorder := lipgloss.NormalBorder()
622−
623− base := lipgloss.NewStyle().Foreground(fgBase)
624− muted := lipgloss.NewStyle().Foreground(fgMuted)
625− subtle := lipgloss.NewStyle().Foreground(fgSubtle)
626−
627− s := Styles{}
628−
629− s.Background = bgBase
630−
631− // Populate color fields
632− s.WorkingGradFromColor = primary
633− s.WorkingGradToColor = secondary
634− s.WorkingLabelColor = fgBase
635−
636− s.TextInput = textinput.Styles{
637− Focused: textinput.StyleState{
638− Text: base,
639− Placeholder: base.Foreground(fgSubtle),
640− Prompt: base.Foreground(tertiary),
641− Suggestion: base.Foreground(fgSubtle),
642− },
643− Blurred: textinput.StyleState{
644− Text: base.Foreground(fgMuted),
645− Placeholder: base.Foreground(fgSubtle),
646− Prompt: base.Foreground(fgMuted),
647− Suggestion: base.Foreground(fgSubtle),
648− },
649− Cursor: textinput.CursorStyle{
650− Color: secondary,
651− Shape: tea.CursorBlock,
652− Blink: true,
653− },
654− }
655−
656− s.Editor.Textarea = textarea.Styles{
657− Focused: textarea.StyleState{
658− Base: base,
659− Text: base,
660− LineNumber: base.Foreground(fgSubtle),
661− CursorLine: base,
662− CursorLineNumber: base.Foreground(fgSubtle),
663− Placeholder: base.Foreground(fgSubtle),
664− Prompt: base.Foreground(tertiary),
665− },
666− Blurred: textarea.StyleState{
667− Base: base,
668− Text: base.Foreground(fgMuted),
669− LineNumber: base.Foreground(fgMuted),
670− CursorLine: base,
671− CursorLineNumber: base.Foreground(fgMuted),
672− Placeholder: base.Foreground(fgSubtle),
673− Prompt: base.Foreground(fgMuted),
674− },
675− Cursor: textarea.CursorStyle{
676− Color: secondary,
677− Shape: tea.CursorBlock,
678− Blink: true,
679− },
680− }
681−
682− s.Markdown = ansi.StyleConfig{
683− Document: ansi.StyleBlock{
684− StylePrimitive: ansi.StylePrimitive{
685− // BlockPrefix: "\n",
686− // BlockSuffix: "\n",
687− Color: new(charmtone.Smoke.Hex()),
688− },
689− // Margin: new(uint(defaultMargin)),
690− },
691− BlockQuote: ansi.StyleBlock{
692− StylePrimitive: ansi.StylePrimitive{},
693− Indent: new(uint(1)),
694− IndentToken: new("│ "),
695− },
696− List: ansi.StyleList{
697− LevelIndent: defaultListIndent,
698− },
699− Heading: ansi.StyleBlock{
700− StylePrimitive: ansi.StylePrimitive{
701− BlockSuffix: "\n",
702− Color: new(charmtone.Malibu.Hex()),
703− Bold: new(true),
704− },
705− },
706− H1: ansi.StyleBlock{
707− StylePrimitive: ansi.StylePrimitive{
708− Prefix: " ",
709− Suffix: " ",
710− Color: new(charmtone.Zest.Hex()),
711− BackgroundColor: new(charmtone.Charple.Hex()),
712− Bold: new(true),
713− },
714− },
715− H2: ansi.StyleBlock{
716− StylePrimitive: ansi.StylePrimitive{
717− Prefix: "## ",
718− },
719− },
720− H3: ansi.StyleBlock{
721− StylePrimitive: ansi.StylePrimitive{
722− Prefix: "### ",
723− },
724− },
725− H4: ansi.StyleBlock{
726− StylePrimitive: ansi.StylePrimitive{
727− Prefix: "#### ",
728− },
729− },
730− H5: ansi.StyleBlock{
731− StylePrimitive: ansi.StylePrimitive{
732− Prefix: "##### ",
733− },
734− },
735− H6: ansi.StyleBlock{
736− StylePrimitive: ansi.StylePrimitive{
737− Prefix: "###### ",
738− Color: new(charmtone.Guac.Hex()),
739− Bold: new(false),
740− },
741− },
742− Strikethrough: ansi.StylePrimitive{
743− CrossedOut: new(true),
744− },
745− Emph: ansi.StylePrimitive{
746− Italic: new(true),
747− },
748− Strong: ansi.StylePrimitive{
749− Bold: new(true),
750− },
751− HorizontalRule: ansi.StylePrimitive{
752− Color: new(charmtone.Charcoal.Hex()),
753− Format: "\n--------\n",
754− },
755− Item: ansi.StylePrimitive{
756− BlockPrefix: "• ",
757− },
758− Enumeration: ansi.StylePrimitive{
759− BlockPrefix: ". ",
760− },
761− Task: ansi.StyleTask{
762− StylePrimitive: ansi.StylePrimitive{},
763− Ticked: "[✓] ",
764− Unticked: "[ ] ",
765− },
766− Link: ansi.StylePrimitive{
767− Color: new(charmtone.Zinc.Hex()),
768− Underline: new(true),
769− },
770− LinkText: ansi.StylePrimitive{
771− Color: new(charmtone.Guac.Hex()),
772− Bold: new(true),
773− },
774− Image: ansi.StylePrimitive{
775− Color: new(charmtone.Cheeky.Hex()),
776− Underline: new(true),
777− },
778− ImageText: ansi.StylePrimitive{
779− Color: new(charmtone.Squid.Hex()),
780− Format: "Image: {{.text}} →",
781− },
782− Code: ansi.StyleBlock{
783− StylePrimitive: ansi.StylePrimitive{
784− Prefix: " ",
785− Suffix: " ",
786− Color: new(charmtone.Coral.Hex()),
787− BackgroundColor: new(charmtone.Charcoal.Hex()),
788− },
789− },
790− CodeBlock: ansi.StyleCodeBlock{
791− StyleBlock: ansi.StyleBlock{
792− StylePrimitive: ansi.StylePrimitive{
793− Color: new(charmtone.Charcoal.Hex()),
794− },
795− Margin: new(uint(defaultMargin)),
796− },
797− Chroma: &ansi.Chroma{
798− Text: ansi.StylePrimitive{
799− Color: new(charmtone.Smoke.Hex()),
800− },
801− Error: ansi.StylePrimitive{
802− Color: new(charmtone.Butter.Hex()),
803− BackgroundColor: new(charmtone.Sriracha.Hex()),
804− },
805− Comment: ansi.StylePrimitive{
806− Color: new(charmtone.Oyster.Hex()),
807− },
808− CommentPreproc: ansi.StylePrimitive{
809− Color: new(charmtone.Bengal.Hex()),
810− },
811− Keyword: ansi.StylePrimitive{
812− Color: new(charmtone.Malibu.Hex()),
813− },
814− KeywordReserved: ansi.StylePrimitive{
815− Color: new(charmtone.Pony.Hex()),
816− },
817− KeywordNamespace: ansi.StylePrimitive{
818− Color: new(charmtone.Pony.Hex()),
819− },
820− KeywordType: ansi.StylePrimitive{
821− Color: new(charmtone.Guppy.Hex()),
822− },
823− Operator: ansi.StylePrimitive{
824− Color: new(charmtone.Salmon.Hex()),
825− },
826− Punctuation: ansi.StylePrimitive{
827− Color: new(charmtone.Zest.Hex()),
828− },
829− Name: ansi.StylePrimitive{
830− Color: new(charmtone.Smoke.Hex()),
831− },
832− NameBuiltin: ansi.StylePrimitive{
833− Color: new(charmtone.Cheeky.Hex()),
834− },
835− NameTag: ansi.StylePrimitive{
836− Color: new(charmtone.Mauve.Hex()),
837− },
838− NameAttribute: ansi.StylePrimitive{
839− Color: new(charmtone.Hazy.Hex()),
840− },
841− NameClass: ansi.StylePrimitive{
842− Color: new(charmtone.Salt.Hex()),
843− Underline: new(true),
844− Bold: new(true),
845− },
846− NameDecorator: ansi.StylePrimitive{
847− Color: new(charmtone.Citron.Hex()),
848− },
849− NameFunction: ansi.StylePrimitive{
850− Color: new(charmtone.Guac.Hex()),
851− },
852− LiteralNumber: ansi.StylePrimitive{
853− Color: new(charmtone.Julep.Hex()),
854− },
855− LiteralString: ansi.StylePrimitive{
856− Color: new(charmtone.Cumin.Hex()),
857− },
858− LiteralStringEscape: ansi.StylePrimitive{
859− Color: new(charmtone.Bok.Hex()),
860− },
861− GenericDeleted: ansi.StylePrimitive{
862− Color: new(charmtone.Coral.Hex()),
863− },
864− GenericEmph: ansi.StylePrimitive{
865− Italic: new(true),
866− },
867− GenericInserted: ansi.StylePrimitive{
868− Color: new(charmtone.Guac.Hex()),
869− },
870− GenericStrong: ansi.StylePrimitive{
871− Bold: new(true),
872− },
873− GenericSubheading: ansi.StylePrimitive{
874− Color: new(charmtone.Squid.Hex()),
875− },
876− Background: ansi.StylePrimitive{
877− BackgroundColor: new(charmtone.Charcoal.Hex()),
878− },
879− },
880− },
881− Table: ansi.StyleTable{
882− StyleBlock: ansi.StyleBlock{
883− StylePrimitive: ansi.StylePrimitive{},
884− },
885− },
886− DefinitionDescription: ansi.StylePrimitive{
887− BlockPrefix: "\n ",
888− },
889− }
890−
891− // QuietMarkdown style - muted colors on subtle background for thinking content.
892− plainBg := new(bgBaseLighter.Hex())
893− plainFg := new(fgMuted.Hex())
894− s.QuietMarkdown = ansi.StyleConfig{
895− Document: ansi.StyleBlock{
896− StylePrimitive: ansi.StylePrimitive{
897− Color: plainFg,
898− BackgroundColor: plainBg,
899− },
900− },
901− BlockQuote: ansi.StyleBlock{
902− StylePrimitive: ansi.StylePrimitive{
903− Color: plainFg,
904− BackgroundColor: plainBg,
905− },
906− Indent: new(uint(1)),
907− IndentToken: new("│ "),
908− },
909− List: ansi.StyleList{
910− LevelIndent: defaultListIndent,
911− },
912− Heading: ansi.StyleBlock{
913− StylePrimitive: ansi.StylePrimitive{
914− BlockSuffix: "\n",
915− Bold: new(true),
916− Color: plainFg,
917− BackgroundColor: plainBg,
918− },
919− },
920− H1: ansi.StyleBlock{
921− StylePrimitive: ansi.StylePrimitive{
922− Prefix: " ",
923− Suffix: " ",
924− Bold: new(true),
925− Color: plainFg,
926− BackgroundColor: plainBg,
927− },
928− },
929− H2: ansi.StyleBlock{
930− StylePrimitive: ansi.StylePrimitive{
931− Prefix: "## ",
932− Color: plainFg,
933− BackgroundColor: plainBg,
934− },
935− },
936− H3: ansi.StyleBlock{
937− StylePrimitive: ansi.StylePrimitive{
938− Prefix: "### ",
939− Color: plainFg,
940− BackgroundColor: plainBg,
941− },
942− },
943− H4: ansi.StyleBlock{
944− StylePrimitive: ansi.StylePrimitive{
945− Prefix: "#### ",
946− Color: plainFg,
947− BackgroundColor: plainBg,
948− },
949− },
950− H5: ansi.StyleBlock{
951− StylePrimitive: ansi.StylePrimitive{
952− Prefix: "##### ",
953− Color: plainFg,
954− BackgroundColor: plainBg,
955− },
956− },
957− H6: ansi.StyleBlock{
958− StylePrimitive: ansi.StylePrimitive{
959− Prefix: "###### ",
960− Color: plainFg,
961− BackgroundColor: plainBg,
962− },
963− },
964− Strikethrough: ansi.StylePrimitive{
965− CrossedOut: new(true),
966− Color: plainFg,
967− BackgroundColor: plainBg,
968− },
969− Emph: ansi.StylePrimitive{
970− Italic: new(true),
971− Color: plainFg,
972− BackgroundColor: plainBg,
973− },
974− Strong: ansi.StylePrimitive{
975− Bold: new(true),
976− Color: plainFg,
977− BackgroundColor: plainBg,
978− },
979− HorizontalRule: ansi.StylePrimitive{
980− Format: "\n--------\n",
981− Color: plainFg,
982− BackgroundColor: plainBg,
983− },
984− Item: ansi.StylePrimitive{
985− BlockPrefix: "• ",
986− Color: plainFg,
987− BackgroundColor: plainBg,
988− },
989− Enumeration: ansi.StylePrimitive{
990− BlockPrefix: ". ",
991− Color: plainFg,
992− BackgroundColor: plainBg,
993− },
994− Task: ansi.StyleTask{
995− StylePrimitive: ansi.StylePrimitive{
996− Color: plainFg,
997− BackgroundColor: plainBg,
998− },
999− Ticked: "[✓] ",
1000− Unticked: "[ ] ",
1001− },
1002− Link: ansi.StylePrimitive{
1003− Underline: new(true),
1004− Color: plainFg,
1005− BackgroundColor: plainBg,
1006− },
1007− LinkText: ansi.StylePrimitive{
1008− Bold: new(true),
1009− Color: plainFg,
1010− BackgroundColor: plainBg,
1011− },
1012− Image: ansi.StylePrimitive{
1013− Underline: new(true),
1014− Color: plainFg,
1015− BackgroundColor: plainBg,
1016− },
1017− ImageText: ansi.StylePrimitive{
1018− Format: "Image: {{.text}} →",
1019− Color: plainFg,
1020− BackgroundColor: plainBg,
1021− },
1022− Code: ansi.StyleBlock{
1023− StylePrimitive: ansi.StylePrimitive{
1024− Prefix: " ",
1025− Suffix: " ",
1026− Color: plainFg,
1027− BackgroundColor: plainBg,
1028− },
1029− },
1030− CodeBlock: ansi.StyleCodeBlock{
1031− StyleBlock: ansi.StyleBlock{
1032− StylePrimitive: ansi.StylePrimitive{
1033− Color: plainFg,
1034− BackgroundColor: plainBg,
1035− },
1036− Margin: new(uint(defaultMargin)),
1037− },
1038− },
1039− Table: ansi.StyleTable{
1040− StyleBlock: ansi.StyleBlock{
1041− StylePrimitive: ansi.StylePrimitive{
1042− Color: plainFg,
1043− BackgroundColor: plainBg,
1044− },
1045− },
1046− },
1047− DefinitionDescription: ansi.StylePrimitive{
1048− BlockPrefix: "\n ",
1049− Color: plainFg,
1050− BackgroundColor: plainBg,
1051− },
1052− }
1053−
1054− s.Help = help.Styles{
1055− ShortKey: base.Foreground(fgMuted),
1056− ShortDesc: base.Foreground(fgSubtle),
1057− ShortSeparator: base.Foreground(border),
1058− Ellipsis: base.Foreground(border),
1059− FullKey: base.Foreground(fgMuted),
1060− FullDesc: base.Foreground(fgSubtle),
1061− FullSeparator: base.Foreground(border),
1062− }
1063−
1064− s.Diff = diffview.Style{
1065− DividerLine: diffview.LineStyle{
1066− LineNumber: lipgloss.NewStyle().
1067− Foreground(fgHalfMuted).
1068− Background(bgBaseLighter),
1069− Code: lipgloss.NewStyle().
1070− Foreground(fgHalfMuted).
1071− Background(bgBaseLighter),
1072− },
1073− MissingLine: diffview.LineStyle{
1074− LineNumber: lipgloss.NewStyle().
1075− Background(bgBaseLighter),
1076− Code: lipgloss.NewStyle().
1077− Background(bgBaseLighter),
1078− },
1079− EqualLine: diffview.LineStyle{
1080− LineNumber: lipgloss.NewStyle().
1081− Foreground(fgMuted).
1082− Background(bgBase),
1083− Code: lipgloss.NewStyle().
1084− Foreground(fgMuted).
1085− Background(bgBase),
1086− },
1087− InsertLine: diffview.LineStyle{
1088− LineNumber: lipgloss.NewStyle().
1089− Foreground(lipgloss.Color("#629657")).
1090− Background(lipgloss.Color("#2b322a")),
1091− Symbol: lipgloss.NewStyle().
1092− Foreground(lipgloss.Color("#629657")).
1093− Background(lipgloss.Color("#323931")),
1094− Code: lipgloss.NewStyle().
1095− Background(lipgloss.Color("#323931")),
1096− },
1097− DeleteLine: diffview.LineStyle{
1098− LineNumber: lipgloss.NewStyle().
1099− Foreground(lipgloss.Color("#a45c59")).
1100− Background(lipgloss.Color("#312929")),
1101− Symbol: lipgloss.NewStyle().
1102− Foreground(lipgloss.Color("#a45c59")).
1103− Background(lipgloss.Color("#383030")),
1104− Code: lipgloss.NewStyle().
1105− Background(lipgloss.Color("#383030")),
1106− },
1107− Filename: diffview.LineStyle{
1108− LineNumber: lipgloss.NewStyle().
1109− Foreground(fgHalfMuted).
1110− Background(bgBaseLighter),
1111− Code: lipgloss.NewStyle().
1112− Foreground(fgHalfMuted).
1113− Background(bgBaseLighter),
1114− },
1115− }
1116−
1117− s.FilePicker = filepicker.Styles{
1118− DisabledCursor: base.Foreground(fgMuted),
1119− Cursor: base.Foreground(fgBase),
1120− Symlink: base.Foreground(fgSubtle),
1121− Directory: base.Foreground(primary),
1122− File: base.Foreground(fgBase),
1123− DisabledFile: base.Foreground(fgMuted),
1124− DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
1125− Permission: base.Foreground(fgMuted),
1126− Selected: base.Background(primary).Foreground(fgBase),
1127− FileSize: base.Foreground(fgMuted),
1128− EmptyDirectory: base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
1129− }
1130−
1131− // borders
1132− s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
1133−
1134− s.Header.Charm = base.Foreground(secondary)
1135− s.Header.Diagonals = base.Foreground(primary)
1136− s.Header.Percentage = muted
1137− s.Header.Keystroke = muted
1138− s.Header.KeystrokeTip = subtle
1139− s.Header.WorkingDir = muted
1140− s.Header.Separator = subtle
1141− s.Header.Wrapper = lipgloss.NewStyle().Foreground(fgBase)
1142− s.Header.LogoGradCanvas = lipgloss.NewStyle()
1143− s.Header.LogoGradFromColor = secondary
1144− s.Header.LogoGradToColor = primary
1145−
1146− s.CompactDetails.Title = base
1147− s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1148− s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
1149−
1150− // Tool rendering styles
1151− s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
1152− s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
1153− s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
1154− s.Tool.IconCancelled = muted.SetString(ToolPending)
1155−
1156− s.Tool.NameNormal = base.Foreground(blue)
1157− s.Tool.NameNested = base.Foreground(blue)
1158−
1159− s.Tool.ParamMain = subtle
1160− s.Tool.ParamKey = subtle
1161−
1162− // Content rendering - prepared styles that accept width parameter
1163− s.Tool.ContentLine = muted.Background(bgBaseLighter)
1164− s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
1165− s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
1166− s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
1167− s.Tool.ContentCodeBg = bgBase
1168− s.Tool.Body = base.PaddingLeft(2)
1169−
1170− // Deprecated - kept for backward compatibility
1171− s.Tool.ContentBg = muted.Background(bgBaseLighter)
1172− s.Tool.ContentText = muted
1173− s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
1174−
1175− s.Tool.StateWaiting = base.Foreground(fgSubtle)
1176− s.Tool.StateCancelled = base.Foreground(fgSubtle)
1177−
1178− s.Tool.ErrorTag = base.Padding(0, 1).Background(red).Foreground(white)
1179− s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
1180−
1181− // Diff and multi-edit styles
1182− s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
1183− s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
1184− s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
1185−
1186− // Job header styles
1187− s.Tool.JobIconPending = base.Foreground(greenDark)
1188− s.Tool.JobIconError = base.Foreground(redDark)
1189− s.Tool.JobIconSuccess = base.Foreground(green)
1190− s.Tool.JobToolName = base.Foreground(blue)
1191− s.Tool.JobAction = base.Foreground(blueDark)
1192− s.Tool.JobPID = muted
1193− s.Tool.JobDescription = subtle
1194−
1195− // Agent task styles
1196− s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
1197− s.Tool.AgentPrompt = muted
1198−
1199− // Agentic fetch styles
1200− s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
1201−
1202− // Todo styles
1203− s.Tool.TodoRatio = base.Foreground(blueDark)
1204− s.Tool.TodoCompletedIcon = base.Foreground(green)
1205− s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
1206− s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
1207− s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(fgSubtle)
1208− s.Tool.TodoItem = lipgloss.NewStyle().Foreground(fgBase)
1209− s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(fgBase)
1210−
1211− // MCP styles
1212− s.Tool.MCPName = base.Foreground(blue)
1213− s.Tool.MCPToolName = base.Foreground(blueDark)
1214− s.Tool.MCPArrow = base.Foreground(blue).SetString(ArrowRightIcon)
1215−
1216− // Loading indicators for images, skills
1217− s.Tool.ResourceLoadedText = base.Foreground(green)
1218− s.Tool.ResourceLoadedIndicator = base.Foreground(greenDark)
1219− s.Tool.ResourceName = base
1220− s.Tool.MediaType = base
1221− s.Tool.ResourceSize = base.Foreground(fgMuted)
1222−
1223− // Tool-call action verbs and result-list styling.
1224− s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(greenLight)
1225− s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(red)
1226− s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
1227− s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
1228− s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
1229− s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
1230−
1231− // Hook styles
1232− s.Tool.HookLabel = base.Foreground(green)
1233− s.Tool.HookName = base
1234− s.Tool.HookMatcher = base.Foreground(fgMuted)
1235− s.Tool.HookArrow = base.Foreground(green)
1236− s.Tool.HookDetail = base.Foreground(fgMuted)
1237− s.Tool.HookOK = base.Foreground(charmtone.Guac)
1238− s.Tool.HookDenied = base.Foreground(charmtone.Sriracha)
1239− s.Tool.HookDeniedLabel = base.Foreground(red)
1240− s.Tool.HookDeniedReason = base.Foreground(charmtone.Iron)
1241− s.Tool.HookRewrote = base.Foreground(charmtone.Iron)
1242−
1243− // Buttons
1244− s.Button.Focused = lipgloss.NewStyle().Foreground(white).Background(secondary)
1245− s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
1246−
1247− // Editor
1248− s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1249− s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
1250− s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1251− s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1252− s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1253− s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(charmtone.Squid)
1254−
1255− s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
1256− s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
1257− s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
1258−
1259− // Logo
1260− s.Logo.FieldColor = primary
1261− s.Logo.TitleColorA = secondary
1262− s.Logo.TitleColorB = primary
1263− s.Logo.CharmColor = secondary
1264− s.Logo.VersionColor = primary
1265− s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
1266− s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
1267− s.Logo.GradCanvas = lipgloss.NewStyle()
1268− s.Logo.SmallGradFromColor = secondary
1269− s.Logo.SmallGradToColor = primary
1270−
1271− // Section
1272− s.Section.Title = subtle
1273− s.Section.Line = base.Foreground(charmtone.Charcoal)
1274−
1275− // Initialize
1276− s.Initialize.Header = base
1277− s.Initialize.Content = muted
1278− s.Initialize.Accent = base.Foreground(greenDark)
1279−
1280− // ResourceGroup (LSP/MCP/skills sidebar lists).
1281− s.Resource.Heading = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1282− s.Resource.Name = lipgloss.NewStyle().Foreground(charmtone.Squid)
1283− s.Resource.StatusText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1284− s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
1285− s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(charmtone.Citron)
1286− s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(charmtone.Coral)
1287− s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(charmtone.Guac)
1288− s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
1289− s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1290− s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
1291− s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
1292− s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
1293− s.Resource.DefaultTitleFg = fgMuted
1294− s.Resource.DefaultDescFg = fgSubtle
1295−
1296− // LSP
1297− s.LSP.ErrorDiagnostic = base.Foreground(redDark)
1298− s.LSP.WarningDiagnostic = base.Foreground(warning)
1299− s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
1300− s.LSP.InfoDiagnostic = base.Foreground(info)
1301−
1302− // Files
1303− s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
1304− s.Files.Additions = lipgloss.NewStyle().Foreground(greenDark)
1305− s.Files.Deletions = lipgloss.NewStyle().Foreground(redDark)
1306− s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
1307− s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
1308− s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
1309−
1310− // Sidebar
1311− s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
1312− s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
1313−
1314− // ModelInfo
1315− s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
1316− s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
1317− s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
1318− s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
1319− s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
1320− s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
1321− s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
1322− s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
1323−
1324− // ResourceGroup
1325− s.Resource.DefaultTitleFg = fgMuted
1326− s.Resource.DefaultDescFg = fgSubtle
1327−
1328− // Chat
1329− messageFocussedBorder := lipgloss.Border{
1330− Left: "▌",
1331− }
1332−
1333− s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1334− s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1335− BorderForeground(primary).BorderStyle(normalBorder)
1336− s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1337− BorderForeground(primary).BorderStyle(messageFocussedBorder)
1338− s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
1339− s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1340− BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1341− s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
1342− s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1343− Background(red).Foreground(white)
1344− s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1345− s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1346−
1347− // Message item styles
1348− s.Messages.ToolCallFocused = muted.PaddingLeft(1).
1349− BorderStyle(messageFocussedBorder).
1350− BorderLeft(true).
1351− BorderForeground(greenDark)
1352− s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
1353− // No padding or border for compact tool calls within messages
1354− s.Messages.ToolCallCompact = muted
1355− s.Messages.SectionHeader = base.PaddingLeft(2)
1356− s.Messages.AssistantInfoIcon = subtle
1357− s.Messages.AssistantInfoModel = muted
1358− s.Messages.AssistantInfoProvider = subtle
1359− s.Messages.AssistantInfoDuration = subtle
1360− s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
1361−
1362− // Thinking section styles
1363− s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
1364− s.Messages.ThinkingTruncationHint = muted
1365− s.Messages.ThinkingFooterTitle = muted
1366− s.Messages.ThinkingFooterDuration = subtle
1367−
1368− // Text selection.
1369− s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
1370−
1371− // Dialog styles
1372− s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
1373− s.Dialog.TitleText = base.Foreground(primary)
1374− s.Dialog.TitleError = base.Foreground(red)
1375− s.Dialog.TitleAccent = base.Foreground(green).Bold(true)
1376− s.Dialog.TitleLineBase = lipgloss.NewStyle()
1377− s.Dialog.TitleGradFromColor = primary
1378− s.Dialog.TitleGradToColor = secondary
1379−
1380− // Dialog.ListItem (commands, reasoning, models)
1381− s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
1382− s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
1383−
1384− // Dialog.Models
1385− s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
1386−
1387− // Dialog.Permissions
1388− s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
1389− s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
1390− s.Dialog.Permissions.ParamsBg = bgSubtle
1391−
1392− // Dialog.Quit
1393− s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
1394− s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
1395− s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1396− s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
1397− s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
1398− s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
1399− s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
1400− s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
1401− s.Dialog.Help.ShortSeparator = base.Foreground(border)
1402− s.Dialog.Help.Ellipsis = base.Foreground(border)
1403− s.Dialog.Help.FullKey = base.Foreground(fgMuted)
1404− s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
1405− s.Dialog.Help.FullSeparator = base.Foreground(border)
1406− s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
1407− s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
1408− s.Dialog.InputPrompt = base.Margin(1, 1)
1409−
1410− s.Dialog.List = base.Margin(0, 0, 1, 0)
1411− s.Dialog.ContentPanel = base.Background(bgSubtle).Foreground(fgBase).Padding(1, 2)
1412− s.Dialog.Spinner = base.Foreground(secondary)
1413− s.Dialog.ScrollbarThumb = base.Foreground(secondary)
1414− s.Dialog.ScrollbarTrack = base.Foreground(border)
1415−
1416− s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
1417−
1418− // API key input dialog
1419− s.Dialog.APIKey.Spinner = base.Foreground(green)
1420−
1421− // OAuth dialog
1422− s.Dialog.OAuth.Spinner = base.Foreground(greenLight)
1423− s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(white)
1424− s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(white)
1425− s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(greenLight)
1426− s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(greenDark).Underline(true)
1427− s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
1428− s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
1429− s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
1430− s.Dialog.OAuth.UserCodeBg = bgBaseLighter
1431−
1432− s.Dialog.Arguments.Content = base.Padding(1)
1433− s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
1434− s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
1435− s.Dialog.Arguments.InputLabelFocused = base.Bold(true)
1436− s.Dialog.Arguments.InputRequiredMarkBlurred = base.Foreground(fgMuted).SetString("*")
1437− s.Dialog.Arguments.InputRequiredMarkFocused = base.Foreground(primary).Bold(true).SetString("*")
1438−
1439− s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(red)
1440− s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(red)
1441− s.Dialog.Sessions.DeletingMessage = base.Padding(1)
1442− s.Dialog.Sessions.DeletingTitleGradientFromColor = red
1443− s.Dialog.Sessions.DeletingTitleGradientToColor = primary
1444− s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1445− s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(red).Foreground(charmtone.Butter)
1446−
1447− s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(charmtone.Zest)
1448− s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(charmtone.Zest)
1449− s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
1450− s.Dialog.Sessions.RenamingTitleGradientFromColor = charmtone.Zest
1451− s.Dialog.Sessions.RenamingTitleGradientToColor = charmtone.Bok
1452− s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1453− s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
1454− s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(charmtone.Squid)
1455− s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
1456− s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
1457−
1458− s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
1459− s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
1460− s.Status.InfoIndicator = s.Status.SuccessIndicator
1461− s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
1462− s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(bgOverlay).Background(yellow).SetString("WARNING")
1463− s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(bgBase).Background(red).SetString("ERROR")
1464− s.Status.SuccessMessage = base.Foreground(bgSubtle).Background(greenDark).Padding(0, 1)
1465− s.Status.InfoMessage = s.Status.SuccessMessage
1466− s.Status.UpdateMessage = s.Status.SuccessMessage
1467− s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(bgOverlay).Background(warning)
1468− s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(white).Background(redDark)
1469−
1470− // Completions styles
1471− s.Completions.Normal = base.Background(bgSubtle).Foreground(fgBase)
1472− s.Completions.Focused = base.Background(primary).Foreground(white)
1473− s.Completions.Match = base.Underline(true)
1474−
1475− // Attachments styles
1476− attachmentIconStyle := base.Foreground(bgSubtle).Background(green).Padding(0, 1)
1477− s.Attachments.Image = attachmentIconStyle.SetString(ImageIcon)
1478− s.Attachments.Text = attachmentIconStyle.SetString(TextIcon)
1479− s.Attachments.Normal = base.Padding(0, 1).MarginRight(1).Background(fgMuted).Foreground(fgBase)
1480− s.Attachments.Deleting = base.Padding(0, 1).Bold(true).Background(red).Foreground(fgBase)
1481−
1482− // Pills styles
1483− s.Pills.Base = base.Padding(0, 1)
1484− s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
1485− s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
1486− s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString(" •")
1487− s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
1488− s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
1489− s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
1490− s.Pills.QueueGradFromColor = redDark
1491− s.Pills.QueueGradToColor = secondary
1492− s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
1493− s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
1494− s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
1495− s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(greenDark)
1496− s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
1497− s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
1498− s.Pills.Area = base
1499−
1500− return s
569+// hex returns a pointer to the "#rrggbb" representation of c. It's used to
570+// satisfy glamour's string-pointer API when configuring markdown colors
571+// from the theme palette.
572+func hex(c color.Color) *string {
573+ r, g, b, _ := c.RGBA()
574+ s := fmt.Sprintf("#%02x%02x%02x", r>>8, g>>8, b>>8)
575+ return &s
1501576}
1502577
1503578func chromaStyle(style ansi.StylePrimitive) string {
internal/ui/styles/themes.gonew file+41
@@ -0,0 +1,41 @@
1+package styles
2+
3+import "github.com/charmbracelet/x/exp/charmtone"
4+
5+// CharmtonePantera returns the Charmtone dark theme. It's the default style
6+// for the UI.
7+func CharmtonePantera() Styles {
8+ return quickStyle(quickStyleOpts{
9+ primary: charmtone.Charple,
10+ secondary: charmtone.Dolly,
11+ tertiary: charmtone.Bok,
12+
13+ fgBase: charmtone.Ash,
14+ fgMuted: charmtone.Squid,
15+ fgHalfMuted: charmtone.Smoke,
16+ fgSubtle: charmtone.Oyster,
17+
18+ onPrimary: charmtone.Salt,
19+ onAccent: charmtone.Butter,
20+
21+ bgBase: charmtone.Pepper,
22+ bgBaseLighter: charmtone.BBQ,
23+ bgSubtle: charmtone.Charcoal,
24+ bgOverlay: charmtone.Iron,
25+
26+ border: charmtone.Charcoal,
27+ borderFocus: charmtone.Charple,
28+
29+ danger: charmtone.Coral,
30+ error: charmtone.Sriracha,
31+ warning: charmtone.Zest,
32+ warningStrong: charmtone.Mustard,
33+ busy: charmtone.Citron,
34+ info: charmtone.Malibu,
35+ infoSubtle: charmtone.Sardine,
36+ infoMuted: charmtone.Damson,
37+ success: charmtone.Julep,
38+ successSubtle: charmtone.Bok,
39+ successMuted: charmtone.Guac,
40+ })
41+}