quickstyle.go

  1package styles
  2
  3import (
  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 simplify the
 18// process of building a theme.
 19type quickStyleOpts struct {
 20	// Brand.
 21	primary   color.Color
 22	secondary color.Color
 23	accent    color.Color
 24	keyword   color.Color
 25
 26	// Default foreground and background colors.
 27	fgBase color.Color
 28	bgBase color.Color
 29
 30	// Low-contrast dividers, separators, and rule lines.
 31	separator color.Color
 32
 33	fgSubtle     color.Color
 34	fgMoreSubtle color.Color
 35	fgMostSubtle color.Color
 36
 37	// Contrast pairings: foregrounds designed to sit on top of a
 38	// matching background role.
 39	onPrimary color.Color // foreground on primary backgrounds.
 40
 41	bgMostVisible  color.Color
 42	bgLessVisible  color.Color
 43	bgLeastVisible color.Color
 44
 45	// Statuses.
 46	destructive       color.Color
 47	error             color.Color
 48	warning           color.Color
 49	warningSubtle     color.Color
 50	denied            color.Color
 51	busy              color.Color
 52	info              color.Color
 53	infoMoreSubtle    color.Color
 54	infoMostSubtle    color.Color
 55	success           color.Color
 56	successMoreSubtle color.Color
 57	successMostSubtle color.Color
 58}
 59
 60// quickStyle builds the default Styles (that is, the default theme, Charmtone
 61// Pantera) from a palette of semi-semanticly-named colors.
 62//
 63// The idea here is that you can do most of the work on a theme with quickStyle,
 64// then add overrides as needed.
 65func quickStyle(o quickStyleOpts) Styles {
 66	var (
 67		base   = lipgloss.NewStyle().Foreground(o.fgBase)
 68		muted  = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
 69		subtle = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
 70		s      Styles
 71	)
 72
 73	s.Background = o.bgBase
 74
 75	// Populate color fields
 76	s.WorkingGradFromColor = o.primary
 77	s.WorkingGradToColor = o.secondary
 78	s.WorkingLabelColor = o.fgBase
 79
 80	s.TextInput = textinput.Styles{
 81		Focused: textinput.StyleState{
 82			Text:        base,
 83			Placeholder: base.Foreground(o.fgMostSubtle),
 84			Prompt:      base.Foreground(o.accent),
 85			Suggestion:  base.Foreground(o.fgMostSubtle),
 86		},
 87		Blurred: textinput.StyleState{
 88			Text:        base.Foreground(o.fgMoreSubtle),
 89			Placeholder: base.Foreground(o.fgMostSubtle),
 90			Prompt:      base.Foreground(o.fgMoreSubtle),
 91			Suggestion:  base.Foreground(o.fgMostSubtle),
 92		},
 93		Cursor: textinput.CursorStyle{
 94			Color: o.secondary,
 95			Shape: tea.CursorBlock,
 96			Blink: true,
 97		},
 98	}
 99
100	s.Editor.Textarea = textarea.Styles{
101		Focused: textarea.StyleState{
102			Base:             base,
103			Text:             base,
104			LineNumber:       base.Foreground(o.fgMostSubtle),
105			CursorLine:       base,
106			CursorLineNumber: base.Foreground(o.fgMostSubtle),
107			Placeholder:      base.Foreground(o.fgMostSubtle),
108			Prompt:           base.Foreground(o.accent),
109		},
110		Blurred: textarea.StyleState{
111			Base:             base,
112			Text:             base.Foreground(o.fgMoreSubtle),
113			LineNumber:       base.Foreground(o.fgMoreSubtle),
114			CursorLine:       base,
115			CursorLineNumber: base.Foreground(o.fgMoreSubtle),
116			Placeholder:      base.Foreground(o.fgMostSubtle),
117			Prompt:           base.Foreground(o.fgMoreSubtle),
118		},
119		Cursor: textarea.CursorStyle{
120			Color: o.secondary,
121			Shape: tea.CursorBlock,
122			Blink: true,
123		},
124	}
125
126	s.Markdown = ansi.StyleConfig{
127		Document: ansi.StyleBlock{
128			StylePrimitive: ansi.StylePrimitive{
129				// BlockPrefix: "\n",
130				// BlockSuffix: "\n",
131				Color: hex(o.fgSubtle),
132			},
133			// Margin: new(uint(defaultMargin)),
134		},
135		BlockQuote: ansi.StyleBlock{
136			StylePrimitive: ansi.StylePrimitive{},
137			Indent:         new(uint(1)),
138			IndentToken:    new("│ "),
139		},
140		List: ansi.StyleList{
141			LevelIndent: defaultListIndent,
142		},
143		Heading: ansi.StyleBlock{
144			StylePrimitive: ansi.StylePrimitive{
145				BlockSuffix: "\n",
146				Color:       hex(o.info),
147				Bold:        new(true),
148			},
149		},
150		H1: ansi.StyleBlock{
151			StylePrimitive: ansi.StylePrimitive{
152				Prefix:          " ",
153				Suffix:          " ",
154				Color:           hex(o.warningSubtle),
155				BackgroundColor: hex(o.primary),
156				Bold:            new(true),
157			},
158		},
159		H2: ansi.StyleBlock{
160			StylePrimitive: ansi.StylePrimitive{
161				Prefix: "## ",
162			},
163		},
164		H3: ansi.StyleBlock{
165			StylePrimitive: ansi.StylePrimitive{
166				Prefix: "### ",
167			},
168		},
169		H4: ansi.StyleBlock{
170			StylePrimitive: ansi.StylePrimitive{
171				Prefix: "#### ",
172			},
173		},
174		H5: ansi.StyleBlock{
175			StylePrimitive: ansi.StylePrimitive{
176				Prefix: "##### ",
177			},
178		},
179		H6: ansi.StyleBlock{
180			StylePrimitive: ansi.StylePrimitive{
181				Prefix: "###### ",
182				Color:  hex(o.successMostSubtle),
183				Bold:   new(false),
184			},
185		},
186		Strikethrough: ansi.StylePrimitive{
187			CrossedOut: new(true),
188		},
189		Emph: ansi.StylePrimitive{
190			Italic: new(true),
191		},
192		Strong: ansi.StylePrimitive{
193			Bold: new(true),
194		},
195		HorizontalRule: ansi.StylePrimitive{
196			Color:  hex(o.separator),
197			Format: "\n--------\n",
198		},
199		Item: ansi.StylePrimitive{
200			BlockPrefix: "• ",
201		},
202		Enumeration: ansi.StylePrimitive{
203			BlockPrefix: ". ",
204		},
205		Task: ansi.StyleTask{
206			StylePrimitive: ansi.StylePrimitive{},
207			Ticked:         "[✓] ",
208			Unticked:       "[ ] ",
209		},
210		Link: ansi.StylePrimitive{
211			Color:     hex(charmtone.Zinc),
212			Underline: new(true),
213		},
214		LinkText: ansi.StylePrimitive{
215			Color: hex(o.successMostSubtle),
216			Bold:  new(true),
217		},
218		Image: ansi.StylePrimitive{
219			Color:     hex(charmtone.Cheeky),
220			Underline: new(true),
221		},
222		ImageText: ansi.StylePrimitive{
223			Color:  hex(o.fgMoreSubtle),
224			Format: "Image: {{.text}} →",
225		},
226		Code: ansi.StyleBlock{
227			StylePrimitive: ansi.StylePrimitive{
228				Prefix:          " ",
229				Suffix:          " ",
230				Color:           hex(o.destructive),
231				BackgroundColor: hex(o.bgLessVisible),
232			},
233		},
234		CodeBlock: ansi.StyleCodeBlock{
235			StyleBlock: ansi.StyleBlock{
236				StylePrimitive: ansi.StylePrimitive{
237					Color: hex(o.bgLessVisible),
238				},
239				Margin: new(uint(defaultMargin)),
240			},
241			Chroma: &ansi.Chroma{
242				Text: ansi.StylePrimitive{
243					Color: hex(o.fgSubtle),
244				},
245				Error: ansi.StylePrimitive{
246					Color:           hex(o.onPrimary),
247					BackgroundColor: hex(o.error),
248				},
249				Comment: ansi.StylePrimitive{
250					Color: hex(o.fgMostSubtle),
251				},
252				CommentPreproc: ansi.StylePrimitive{
253					Color: hex(charmtone.Bengal),
254				},
255				Keyword: ansi.StylePrimitive{
256					Color: hex(o.info),
257				},
258				KeywordReserved: ansi.StylePrimitive{
259					Color: hex(charmtone.Pony),
260				},
261				KeywordNamespace: ansi.StylePrimitive{
262					Color: hex(charmtone.Pony),
263				},
264				KeywordType: ansi.StylePrimitive{
265					Color: hex(charmtone.Guppy),
266				},
267				Operator: ansi.StylePrimitive{
268					Color: hex(charmtone.Salmon),
269				},
270				Punctuation: ansi.StylePrimitive{
271					Color: hex(o.warningSubtle),
272				},
273				Name: ansi.StylePrimitive{
274					Color: hex(o.fgSubtle),
275				},
276				NameBuiltin: ansi.StylePrimitive{
277					Color: hex(charmtone.Cheeky),
278				},
279				NameTag: ansi.StylePrimitive{
280					Color: hex(charmtone.Mauve),
281				},
282				NameAttribute: ansi.StylePrimitive{
283					Color: hex(charmtone.Hazy),
284				},
285				NameClass: ansi.StylePrimitive{
286					Color:     hex(charmtone.Salt),
287					Underline: new(true),
288					Bold:      new(true),
289				},
290				NameDecorator: ansi.StylePrimitive{
291					Color: hex(charmtone.Citron),
292				},
293				NameFunction: ansi.StylePrimitive{
294					Color: hex(o.successMostSubtle),
295				},
296				LiteralNumber: ansi.StylePrimitive{
297					Color: hex(o.success),
298				},
299				LiteralString: ansi.StylePrimitive{
300					Color: hex(charmtone.Cumin),
301				},
302				LiteralStringEscape: ansi.StylePrimitive{
303					Color: hex(o.successMoreSubtle),
304				},
305				GenericDeleted: ansi.StylePrimitive{
306					Color: hex(o.destructive),
307				},
308				GenericEmph: ansi.StylePrimitive{
309					Italic: new(true),
310				},
311				GenericInserted: ansi.StylePrimitive{
312					Color: hex(o.successMostSubtle),
313				},
314				GenericStrong: ansi.StylePrimitive{
315					Bold: new(true),
316				},
317				GenericSubheading: ansi.StylePrimitive{
318					Color: hex(o.fgMoreSubtle),
319				},
320				Background: ansi.StylePrimitive{
321					BackgroundColor: hex(o.bgLessVisible),
322				},
323			},
324		},
325		Table: ansi.StyleTable{
326			StyleBlock: ansi.StyleBlock{
327				StylePrimitive: ansi.StylePrimitive{},
328			},
329		},
330		DefinitionDescription: ansi.StylePrimitive{
331			BlockPrefix: "\n ",
332		},
333	}
334
335	// QuietMarkdown style - muted colors on subtle background for thinking content.
336	plainBg := hex(o.bgLeastVisible)
337	plainFg := hex(o.fgMoreSubtle)
338	s.QuietMarkdown = ansi.StyleConfig{
339		Document: ansi.StyleBlock{
340			StylePrimitive: ansi.StylePrimitive{
341				Color:           plainFg,
342				BackgroundColor: plainBg,
343			},
344		},
345		BlockQuote: ansi.StyleBlock{
346			StylePrimitive: ansi.StylePrimitive{
347				Color:           plainFg,
348				BackgroundColor: plainBg,
349			},
350			Indent:      new(uint(1)),
351			IndentToken: new("│ "),
352		},
353		List: ansi.StyleList{
354			LevelIndent: defaultListIndent,
355		},
356		Heading: ansi.StyleBlock{
357			StylePrimitive: ansi.StylePrimitive{
358				BlockSuffix:     "\n",
359				Bold:            new(true),
360				Color:           plainFg,
361				BackgroundColor: plainBg,
362			},
363		},
364		H1: ansi.StyleBlock{
365			StylePrimitive: ansi.StylePrimitive{
366				Prefix:          " ",
367				Suffix:          " ",
368				Bold:            new(true),
369				Color:           plainFg,
370				BackgroundColor: plainBg,
371			},
372		},
373		H2: ansi.StyleBlock{
374			StylePrimitive: ansi.StylePrimitive{
375				Prefix:          "## ",
376				Color:           plainFg,
377				BackgroundColor: plainBg,
378			},
379		},
380		H3: ansi.StyleBlock{
381			StylePrimitive: ansi.StylePrimitive{
382				Prefix:          "### ",
383				Color:           plainFg,
384				BackgroundColor: plainBg,
385			},
386		},
387		H4: ansi.StyleBlock{
388			StylePrimitive: ansi.StylePrimitive{
389				Prefix:          "#### ",
390				Color:           plainFg,
391				BackgroundColor: plainBg,
392			},
393		},
394		H5: ansi.StyleBlock{
395			StylePrimitive: ansi.StylePrimitive{
396				Prefix:          "##### ",
397				Color:           plainFg,
398				BackgroundColor: plainBg,
399			},
400		},
401		H6: ansi.StyleBlock{
402			StylePrimitive: ansi.StylePrimitive{
403				Prefix:          "###### ",
404				Color:           plainFg,
405				BackgroundColor: plainBg,
406			},
407		},
408		Strikethrough: ansi.StylePrimitive{
409			CrossedOut:      new(true),
410			Color:           plainFg,
411			BackgroundColor: plainBg,
412		},
413		Emph: ansi.StylePrimitive{
414			Italic:          new(true),
415			Color:           plainFg,
416			BackgroundColor: plainBg,
417		},
418		Strong: ansi.StylePrimitive{
419			Bold:            new(true),
420			Color:           plainFg,
421			BackgroundColor: plainBg,
422		},
423		HorizontalRule: ansi.StylePrimitive{
424			Format:          "\n--------\n",
425			Color:           plainFg,
426			BackgroundColor: plainBg,
427		},
428		Item: ansi.StylePrimitive{
429			BlockPrefix:     "• ",
430			Color:           plainFg,
431			BackgroundColor: plainBg,
432		},
433		Enumeration: ansi.StylePrimitive{
434			BlockPrefix:     ". ",
435			Color:           plainFg,
436			BackgroundColor: plainBg,
437		},
438		Task: ansi.StyleTask{
439			StylePrimitive: ansi.StylePrimitive{
440				Color:           plainFg,
441				BackgroundColor: plainBg,
442			},
443			Ticked:   "[✓] ",
444			Unticked: "[ ] ",
445		},
446		Link: ansi.StylePrimitive{
447			Underline:       new(true),
448			Color:           plainFg,
449			BackgroundColor: plainBg,
450		},
451		LinkText: ansi.StylePrimitive{
452			Bold:            new(true),
453			Color:           plainFg,
454			BackgroundColor: plainBg,
455		},
456		Image: ansi.StylePrimitive{
457			Underline:       new(true),
458			Color:           plainFg,
459			BackgroundColor: plainBg,
460		},
461		ImageText: ansi.StylePrimitive{
462			Format:          "Image: {{.text}} →",
463			Color:           plainFg,
464			BackgroundColor: plainBg,
465		},
466		Code: ansi.StyleBlock{
467			StylePrimitive: ansi.StylePrimitive{
468				Prefix:          " ",
469				Suffix:          " ",
470				Color:           plainFg,
471				BackgroundColor: plainBg,
472			},
473		},
474		CodeBlock: ansi.StyleCodeBlock{
475			StyleBlock: ansi.StyleBlock{
476				StylePrimitive: ansi.StylePrimitive{
477					Color:           plainFg,
478					BackgroundColor: plainBg,
479				},
480				Margin: new(uint(defaultMargin)),
481			},
482		},
483		Table: ansi.StyleTable{
484			StyleBlock: ansi.StyleBlock{
485				StylePrimitive: ansi.StylePrimitive{
486					Color:           plainFg,
487					BackgroundColor: plainBg,
488				},
489			},
490		},
491		DefinitionDescription: ansi.StylePrimitive{
492			BlockPrefix:     "\n ",
493			Color:           plainFg,
494			BackgroundColor: plainBg,
495		},
496	}
497
498	s.Help = help.Styles{
499		ShortKey:       base.Foreground(o.fgMoreSubtle),
500		ShortDesc:      base.Foreground(o.fgMostSubtle),
501		ShortSeparator: base.Foreground(o.separator),
502		Ellipsis:       base.Foreground(o.separator),
503		FullKey:        base.Foreground(o.fgMoreSubtle),
504		FullDesc:       base.Foreground(o.fgMostSubtle),
505		FullSeparator:  base.Foreground(o.separator),
506	}
507
508	s.Diff = diffview.Style{
509		DividerLine: diffview.LineStyle{
510			LineNumber: lipgloss.NewStyle().
511				Foreground(o.fgSubtle).
512				Background(o.bgLeastVisible),
513			Code: lipgloss.NewStyle().
514				Foreground(o.fgSubtle).
515				Background(o.bgLeastVisible),
516		},
517		MissingLine: diffview.LineStyle{
518			LineNumber: lipgloss.NewStyle().
519				Background(o.bgLeastVisible),
520			Code: lipgloss.NewStyle().
521				Background(o.bgLeastVisible),
522		},
523		EqualLine: diffview.LineStyle{
524			LineNumber: lipgloss.NewStyle().
525				Foreground(o.fgMoreSubtle).
526				Background(o.bgBase),
527			Code: lipgloss.NewStyle().
528				Foreground(o.fgMoreSubtle).
529				Background(o.bgBase),
530		},
531		InsertLine: diffview.LineStyle{
532			LineNumber: lipgloss.NewStyle().
533				Foreground(lipgloss.Color("#629657")).
534				Background(lipgloss.Color("#2b322a")),
535			Symbol: lipgloss.NewStyle().
536				Foreground(lipgloss.Color("#629657")).
537				Background(lipgloss.Color("#323931")),
538			Code: lipgloss.NewStyle().
539				Background(lipgloss.Color("#323931")),
540		},
541		DeleteLine: diffview.LineStyle{
542			LineNumber: lipgloss.NewStyle().
543				Foreground(lipgloss.Color("#a45c59")).
544				Background(lipgloss.Color("#312929")),
545			Symbol: lipgloss.NewStyle().
546				Foreground(lipgloss.Color("#a45c59")).
547				Background(lipgloss.Color("#383030")),
548			Code: lipgloss.NewStyle().
549				Background(lipgloss.Color("#383030")),
550		},
551		Filename: diffview.LineStyle{
552			LineNumber: lipgloss.NewStyle().
553				Foreground(o.fgSubtle).
554				Background(o.bgLeastVisible),
555			Code: lipgloss.NewStyle().
556				Foreground(o.fgSubtle).
557				Background(o.bgLeastVisible),
558		},
559	}
560
561	s.FilePicker = filepicker.Styles{
562		DisabledCursor:   base.Foreground(o.fgMoreSubtle),
563		Cursor:           base.Foreground(o.fgBase),
564		Symlink:          base.Foreground(o.fgMostSubtle),
565		Directory:        base.Foreground(o.primary),
566		File:             base.Foreground(o.fgBase),
567		DisabledFile:     base.Foreground(o.fgMoreSubtle),
568		DisabledSelected: base.Background(o.bgMostVisible).Foreground(o.fgMoreSubtle),
569		Permission:       base.Foreground(o.fgMoreSubtle),
570		Selected:         base.Background(o.primary).Foreground(o.fgBase),
571		FileSize:         base.Foreground(o.fgMoreSubtle),
572		EmptyDirectory:   base.Foreground(o.fgMoreSubtle).PaddingLeft(2).SetString("Empty directory"),
573	}
574
575	// borders
576	s.ToolCallSuccess = lipgloss.NewStyle().Foreground(o.success).SetString(ToolSuccess)
577
578	s.Header.Charm = base.Foreground(o.secondary)
579	s.Header.Diagonals = base.Foreground(o.primary)
580	s.Header.Percentage = muted
581	s.Header.Hypercredit = base.Foreground(charmtone.Dolly)
582	s.Header.Keystroke = muted
583	s.Header.KeystrokeTip = subtle
584	s.Header.WorkingDir = muted
585	s.Header.Separator = subtle
586	s.Header.Wrapper = lipgloss.NewStyle().Foreground(o.fgBase)
587	s.Header.LogoGradCanvas = lipgloss.NewStyle()
588	s.Header.LogoGradFromColor = o.secondary
589	s.Header.LogoGradToColor = o.primary
590
591	s.CompactDetails.Title = base
592	s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(o.primary)
593	s.CompactDetails.Version = lipgloss.NewStyle().Foreground(o.separator)
594
595	// Tool rendering styles
596	s.Tool.IconPending = base.Foreground(o.successMostSubtle).SetString(ToolPending)
597	s.Tool.IconSuccess = base.Foreground(o.success).SetString(ToolSuccess)
598	s.Tool.IconError = base.Foreground(o.error).SetString(ToolError)
599	s.Tool.IconCancelled = muted.SetString(ToolPending)
600
601	s.Tool.NameNormal = base.Foreground(o.info)
602	s.Tool.NameNested = base.Foreground(o.info)
603
604	s.Tool.ParamMain = subtle
605	s.Tool.ParamKey = subtle
606
607	// Content rendering - prepared styles that accept width parameter
608	s.Tool.ContentLine = muted.Background(o.bgLeastVisible)
609	s.Tool.ContentTruncation = muted.Background(o.bgLeastVisible)
610	s.Tool.ContentCodeLine = base.Background(o.bgBase).PaddingLeft(2)
611	s.Tool.ContentCodeTruncation = muted.Background(o.bgBase).PaddingLeft(2)
612	s.Tool.ContentCodeBg = o.bgBase
613	s.Tool.Body = base.PaddingLeft(2)
614
615	// Deprecated - kept for backward compatibility
616	s.Tool.ContentBg = muted.Background(o.bgLeastVisible)
617	s.Tool.ContentText = muted
618	s.Tool.ContentLineNumber = base.Foreground(o.fgMoreSubtle).Background(o.bgBase).PaddingRight(1).PaddingLeft(1)
619
620	s.Tool.StateWaiting = base.Foreground(o.fgMostSubtle)
621	s.Tool.StateCancelled = base.Foreground(o.fgMostSubtle)
622
623	s.Tool.ErrorTag = base.Padding(0, 1).Background(o.destructive).Foreground(o.onPrimary)
624	s.Tool.ErrorMessage = base.Foreground(o.fgSubtle)
625
626	s.Tool.WarnTag = base.Padding(0, 1).Background(o.denied).Foreground(o.bgBase).Bold(true)
627	s.Tool.WarnMessage = base.Foreground(o.fgSubtle)
628
629	// Diff and multi-edit styles
630	s.Tool.DiffTruncation = muted.Background(o.bgLeastVisible).PaddingLeft(2)
631	s.Tool.NoteTag = base.Padding(0, 1).Background(o.info).Foreground(o.onPrimary)
632	s.Tool.NoteMessage = base.Foreground(o.fgSubtle)
633
634	// Job header styles
635	s.Tool.JobIconPending = base.Foreground(o.successMostSubtle)
636	s.Tool.JobIconError = base.Foreground(o.error)
637	s.Tool.JobIconSuccess = base.Foreground(o.success)
638	s.Tool.JobToolName = base.Foreground(o.info)
639	s.Tool.JobAction = base.Foreground(o.infoMostSubtle)
640	s.Tool.JobPID = muted
641	s.Tool.JobDescription = subtle
642
643	// Agent task styles
644	s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(o.infoMoreSubtle).Foreground(o.onPrimary)
645	s.Tool.AgentPrompt = muted
646
647	// Agentic fetch styles
648	s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(o.success).Foreground(o.separator)
649
650	// Todo styles
651	s.Tool.TodoRatio = base.Foreground(o.infoMostSubtle)
652	s.Tool.TodoCompletedIcon = base.Foreground(o.success)
653	s.Tool.TodoInProgressIcon = base.Foreground(o.successMostSubtle)
654	s.Tool.TodoPendingIcon = base.Foreground(o.fgMoreSubtle)
655	s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
656	s.Tool.TodoItem = lipgloss.NewStyle().Foreground(o.fgBase)
657	s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(o.fgBase)
658
659	// MCP styles
660	s.Tool.MCPName = base.Foreground(o.info)
661	s.Tool.MCPToolName = base.Foreground(o.infoMostSubtle)
662	s.Tool.MCPArrow = base.Foreground(o.info).SetString(ArrowRightIcon)
663
664	// Loading indicators for images, skills
665	s.Tool.ResourceLoadedText = base.Foreground(o.success)
666	s.Tool.ResourceLoadedIndicator = base.Foreground(o.successMostSubtle)
667	s.Tool.ResourceName = base
668	s.Tool.MediaType = base
669	s.Tool.ResourceSize = base.Foreground(o.fgMoreSubtle)
670
671	// Hook styles
672	s.Tool.HookLabel = base.Foreground(o.successMoreSubtle)
673	s.Tool.HookName = base
674	s.Tool.HookMatcher = base.Foreground(o.fgMoreSubtle)
675	s.Tool.HookArrow = base.Foreground(o.successMoreSubtle)
676	s.Tool.HookDetail = base.Foreground(o.fgMoreSubtle)
677	s.Tool.HookOK = base.Foreground(o.successMostSubtle)
678	s.Tool.HookDenied = base.Foreground(o.error)
679	s.Tool.HookDeniedLabel = base.Foreground(o.destructive)
680	s.Tool.HookDeniedReason = base.Foreground(o.bgMostVisible)
681	s.Tool.HookRewrote = base.Foreground(o.bgMostVisible)
682
683	// Tool-call action verbs and result-list styling.
684	s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(o.successMoreSubtle)
685	s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(o.destructive)
686	s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
687	s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
688	s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(o.fgBase)
689	s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
690
691	// Buttons
692	s.Button.Focused = lipgloss.NewStyle().Foreground(o.onPrimary).Background(o.secondary)
693	s.Button.Blurred = lipgloss.NewStyle().Foreground(o.fgBase).Background(o.bgLessVisible)
694
695	// Editor
696	s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(o.successMostSubtle).SetString("::: ")
697	s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(o.fgMoreSubtle)
698	s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(o.fgMostSubtle).Background(o.busy).Bold(true).SetString(" ! ")
699	s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(o.bgBase).Background(o.fgMoreSubtle)
700	s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(o.warningSubtle).SetString(":::")
701	s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(o.fgMoreSubtle)
702
703	s.Radio.On = lipgloss.NewStyle().Foreground(o.fgSubtle).SetString(RadioOn)
704	s.Radio.Off = lipgloss.NewStyle().Foreground(o.fgSubtle).SetString(RadioOff)
705	s.Radio.Label = lipgloss.NewStyle().Foreground(o.fgSubtle)
706
707	// Logo
708	s.Logo.FieldColor = o.primary
709	s.Logo.TitleColorA = o.secondary
710	s.Logo.TitleColorB = o.primary
711	s.Logo.CharmColor = o.secondary
712	s.Logo.VersionColor = o.primary
713	s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(o.secondary)
714	s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(o.primary)
715	s.Logo.GradCanvas = lipgloss.NewStyle()
716	s.Logo.SmallGradFromColor = o.secondary
717	s.Logo.SmallGradToColor = o.primary
718
719	// Section
720	s.Section.Title = subtle
721	s.Section.Line = base.Foreground(o.separator)
722
723	// Initialize
724	s.Initialize.Header = base
725	s.Initialize.Content = muted
726	s.Initialize.Accent = base.Foreground(o.successMostSubtle)
727
728	// ResourceGroup (LSP/MCP/skills sidebar lists).
729	s.Resource.Heading = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
730	s.Resource.Name = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
731	s.Resource.StatusText = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
732	s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(o.bgMostVisible).SetString("●")
733	s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(o.busy)
734	s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(o.destructive)
735	s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(o.successMostSubtle)
736	s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(o.fgMoreSubtle).SetString("●")
737	s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
738	s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
739	s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(o.fgBase)
740	s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(o.fgBase)
741	s.Resource.DefaultTitleFg = o.fgMoreSubtle
742	s.Resource.DefaultDescFg = o.fgMostSubtle
743
744	// LSP
745	s.LSP.ErrorDiagnostic = base.Foreground(o.error)
746	s.LSP.WarningDiagnostic = base.Foreground(o.warningSubtle)
747	s.LSP.HintDiagnostic = base.Foreground(o.fgSubtle)
748	s.LSP.InfoDiagnostic = base.Foreground(o.info)
749
750	// Files
751	s.Files.Path = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
752	s.Files.Additions = lipgloss.NewStyle().Foreground(o.successMostSubtle)
753	s.Files.Deletions = lipgloss.NewStyle().Foreground(o.error)
754	s.Files.SectionTitle = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
755	s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
756	s.Files.TruncationHint = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
757
758	// Sidebar
759	s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
760	s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
761
762	// ModelInfo
763	s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
764	s.ModelInfo.Name = lipgloss.NewStyle().Foreground(o.fgBase)
765	s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
766	s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(o.fgMoreSubtle).PaddingLeft(2)
767	s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(o.fgMostSubtle).PaddingLeft(2)
768	s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
769	s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
770	s.ModelInfo.EstimatedUsagePrefix = s.ModelInfo.TokenPercentage
771	s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
772	s.ModelInfo.HypercreditIcon = lipgloss.NewStyle().Foreground(charmtone.Dolly)
773	s.ModelInfo.HypercreditText = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
774
775	// ResourceGroup
776	s.Resource.DefaultTitleFg = o.fgMoreSubtle
777	s.Resource.DefaultDescFg = o.fgMostSubtle
778
779	// Chat
780	messageFocussedBorder := lipgloss.Border{
781		Left: "▌",
782	}
783
784	s.Messages.NoContent = lipgloss.NewStyle().Foreground(o.fgBase)
785	s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
786		BorderForeground(o.primary).BorderStyle(lipgloss.NormalBorder())
787	s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
788		BorderForeground(o.primary).BorderStyle(messageFocussedBorder)
789	s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
790	s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
791		BorderForeground(o.successMostSubtle).BorderStyle(messageFocussedBorder)
792	s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
793	s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
794		Background(o.destructive).Foreground(o.onPrimary)
795	s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(o.fgSubtle)
796	s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
797
798	// Message item styles
799	s.Messages.ToolCallFocused = muted.PaddingLeft(1).
800		BorderStyle(messageFocussedBorder).
801		BorderLeft(true).
802		BorderForeground(o.successMostSubtle)
803	s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
804	// No padding or border for compact tool calls within messages
805	s.Messages.ToolCallCompact = muted
806	s.Messages.SectionHeader = base.PaddingLeft(2)
807	s.Messages.AssistantInfoIcon = subtle
808	s.Messages.AssistantInfoModel = muted
809	s.Messages.AssistantInfoProvider = subtle
810	s.Messages.AssistantInfoDuration = subtle
811	s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(o.fgBase).Italic(true)
812
813	// Thinking section styles
814	s.Messages.ThinkingBox = subtle.Background(o.bgLeastVisible)
815	s.Messages.ThinkingTruncationHint = muted
816	s.Messages.ThinkingFooterTitle = muted
817	s.Messages.ThinkingFooterDuration = subtle
818
819	// Text selection.
820	s.TextSelection = lipgloss.NewStyle().Foreground(o.onPrimary).Background(o.primary)
821
822	// Dialog styles
823	s.Dialog.Title = base.Padding(0, 1).Foreground(o.primary)
824	s.Dialog.TitleText = base.Foreground(o.primary)
825	s.Dialog.TitleError = base.Foreground(o.destructive)
826	s.Dialog.TitleAccent = base.Foreground(o.success).Bold(true)
827	s.Dialog.TitleLineBase = lipgloss.NewStyle()
828	s.Dialog.TitleGradFromColor = o.primary
829	s.Dialog.TitleGradToColor = o.secondary
830
831	// Dialog.ListItem (commands, reasoning, models)
832	s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(o.fgBase)
833	s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(o.fgBase)
834
835	// Dialog.Models
836	s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
837
838	// Dialog.Permissions
839	s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
840	s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(o.fgBase)
841	s.Dialog.Permissions.ParamsBg = o.bgLessVisible
842
843	// Dialog.Quit
844	s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(o.fgBase)
845	s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(o.primary).Border(lipgloss.RoundedBorder()).Padding(1, 2)
846	s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(o.primary)
847	s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(o.primary)
848	s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(o.fgMostSubtle)
849	s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
850	s.Dialog.Help.ShortKey = base.Foreground(o.fgMoreSubtle)
851	s.Dialog.Help.ShortDesc = base.Foreground(o.fgMostSubtle)
852	s.Dialog.Help.ShortSeparator = base.Foreground(o.separator)
853	s.Dialog.Help.Ellipsis = base.Foreground(o.separator)
854	s.Dialog.Help.FullKey = base.Foreground(o.fgMoreSubtle)
855	s.Dialog.Help.FullDesc = base.Foreground(o.fgMostSubtle)
856	s.Dialog.Help.FullSeparator = base.Foreground(o.separator)
857	s.Dialog.NormalItem = base.Padding(0, 1).Foreground(o.fgBase)
858	s.Dialog.SelectedItem = base.Padding(0, 1).Background(o.primary).Foreground(o.onPrimary)
859	s.Dialog.InputPrompt = base.Margin(1, 1)
860
861	s.Dialog.List = base.Margin(0, 0, 1, 0)
862	s.Dialog.ContentPanel = base.Background(o.bgLessVisible).Foreground(o.fgBase).Padding(1, 2)
863	s.Dialog.Spinner = base.Foreground(o.secondary)
864	s.Dialog.ScrollbarThumb = base.Foreground(o.secondary)
865	s.Dialog.ScrollbarTrack = base.Foreground(o.separator)
866
867	s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(o.fgMostSubtle)
868
869	// API key input dialog
870	s.Dialog.APIKey.Spinner = base.Foreground(o.success)
871
872	// OAuth dialog
873	s.Dialog.OAuth.Spinner = base.Foreground(o.successMoreSubtle)
874	s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(o.fgBase)
875	s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(o.fgBase)
876	s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(o.successMoreSubtle)
877	s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(o.successMostSubtle).Underline(true)
878	s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(o.keyword)
879	s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(o.error)
880	s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
881	s.Dialog.OAuth.UserCodeBg = o.bgLeastVisible
882
883	s.Dialog.Arguments.Content = base.Padding(1)
884	s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
885	s.Dialog.Arguments.InputLabelBlurred = base.Foreground(o.fgMoreSubtle)
886	s.Dialog.Arguments.InputLabelFocused = base.Bold(true)
887	s.Dialog.Arguments.InputRequiredMarkBlurred = base.Foreground(o.fgMoreSubtle).SetString("*")
888	s.Dialog.Arguments.InputRequiredMarkFocused = base.Foreground(o.primary).Bold(true).SetString("*")
889
890	s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(o.destructive)
891	s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(o.destructive)
892	s.Dialog.Sessions.DeletingMessage = base.Padding(1)
893	s.Dialog.Sessions.DeletingTitleGradientFromColor = o.destructive
894	s.Dialog.Sessions.DeletingTitleGradientToColor = o.primary
895	s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(o.fgMostSubtle)
896	s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(o.destructive).Foreground(o.onPrimary)
897
898	s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(o.warningSubtle)
899	s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(o.warningSubtle)
900	s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
901	s.Dialog.Sessions.RenamingTitleGradientFromColor = o.warningSubtle
902	s.Dialog.Sessions.RenamingTitleGradientToColor = o.accent
903	s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(o.fgMostSubtle)
904	s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
905	s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(o.fgMoreSubtle)
906	s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
907	s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(o.fgBase)
908
909	s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
910	s.Status.SuccessIndicator = base.Foreground(o.bgLessVisible).Background(o.success).Padding(0, 1).Bold(true).SetString("OKAY!")
911	s.Status.InfoIndicator = s.Status.SuccessIndicator
912	s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
913	s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(o.bgMostVisible).Background(o.warning).SetString("WARNING")
914	s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(o.bgBase).Background(o.destructive).SetString("ERROR")
915	s.Status.SuccessMessage = base.Foreground(o.bgLessVisible).Background(o.successMostSubtle).Padding(0, 1)
916	s.Status.InfoMessage = s.Status.SuccessMessage
917	s.Status.UpdateMessage = s.Status.SuccessMessage
918	s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(o.bgMostVisible).Background(o.warningSubtle)
919	s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(o.onPrimary).Background(o.error)
920
921	// Completions styles
922	s.Completions.Normal = base.Background(o.bgLessVisible).Foreground(o.fgBase)
923	s.Completions.Focused = base.Background(o.primary).Foreground(o.onPrimary)
924	s.Completions.Match = base.Underline(true)
925
926	// Attachments styles
927	attachmentIconStyle := base.Foreground(o.bgLessVisible).Background(o.success).Padding(0, 1)
928	s.Attachments.Image = attachmentIconStyle.SetString(ImageIcon)
929	s.Attachments.Text = attachmentIconStyle.SetString(TextIcon)
930	s.Attachments.Skill = attachmentIconStyle.SetString(SkillIcon)
931	s.Attachments.Normal = base.Padding(0, 1).MarginRight(1).Background(o.fgMoreSubtle).Foreground(o.fgBase)
932	s.Attachments.Deleting = base.Padding(0, 1).Bold(true).Background(o.destructive).Foreground(o.fgBase)
933
934	// Pills styles
935	s.Pills.Base = base.Padding(0, 1)
936	s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(o.bgMostVisible)
937	s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
938	s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(o.fgMoreSubtle).SetString("  •")
939	s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
940	s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(o.fgBase)
941	s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(o.fgBase)
942	s.Pills.QueueGradFromColor = o.error
943	s.Pills.QueueGradToColor = o.secondary
944	s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(o.fgBase)
945	s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
946	s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
947	s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(o.successMostSubtle)
948	s.Pills.HelpKey = lipgloss.NewStyle().Foreground(o.fgMoreSubtle)
949	s.Pills.HelpText = lipgloss.NewStyle().Foreground(o.fgMostSubtle)
950	s.Pills.Area = base
951
952	return s
953}