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 build a
 18// complete Styles value. Each field maps to a semantic role in the UI.
 19type 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.
 66func 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	// Hook styles
703	s.Tool.HookLabel = base.Foreground(successSubtle)
704	s.Tool.HookName = base
705	s.Tool.HookMatcher = base.Foreground(fgMuted)
706	s.Tool.HookArrow = base.Foreground(successSubtle)
707	s.Tool.HookDetail = base.Foreground(fgMuted)
708	s.Tool.HookOK = base.Foreground(successMuted)
709	s.Tool.HookDenied = base.Foreground(error)
710	s.Tool.HookDeniedLabel = base.Foreground(danger)
711	s.Tool.HookDeniedReason = base.Foreground(bgOverlay)
712	s.Tool.HookRewrote = base.Foreground(bgOverlay)
713
714	// Tool-call action verbs and result-list styling.
715	s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(successSubtle)
716	s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(danger)
717	s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
718	s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
719	s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
720	s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
721
722	// Buttons
723	s.Button.Focused = lipgloss.NewStyle().Foreground(onAccent).Background(secondary)
724	s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
725
726	// Editor
727	s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(successMuted).SetString("::: ")
728	s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
729	s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(fgSubtle).Background(busy).Bold(true).SetString(" ! ")
730	s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(bgBase).Background(fgMuted)
731	s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(warning).SetString(":::")
732	s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(fgMuted)
733
734	s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
735	s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
736	s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
737
738	// Logo
739	s.Logo.FieldColor = primary
740	s.Logo.TitleColorA = secondary
741	s.Logo.TitleColorB = primary
742	s.Logo.CharmColor = secondary
743	s.Logo.VersionColor = primary
744	s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
745	s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
746	s.Logo.GradCanvas = lipgloss.NewStyle()
747	s.Logo.SmallGradFromColor = secondary
748	s.Logo.SmallGradToColor = primary
749
750	// Section
751	s.Section.Title = subtle
752	s.Section.Line = base.Foreground(border)
753
754	// Initialize
755	s.Initialize.Header = base
756	s.Initialize.Content = muted
757	s.Initialize.Accent = base.Foreground(successMuted)
758
759	// ResourceGroup (LSP/MCP/skills sidebar lists).
760	s.Resource.Heading = lipgloss.NewStyle().Foreground(fgSubtle)
761	s.Resource.Name = lipgloss.NewStyle().Foreground(fgMuted)
762	s.Resource.StatusText = lipgloss.NewStyle().Foreground(fgSubtle)
763	s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(bgOverlay).SetString("●")
764	s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(busy)
765	s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(danger)
766	s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(successMuted)
767	s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
768	s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(fgSubtle)
769	s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
770	s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
771	s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
772	s.Resource.DefaultTitleFg = fgMuted
773	s.Resource.DefaultDescFg = fgSubtle
774
775	// LSP
776	s.LSP.ErrorDiagnostic = base.Foreground(error)
777	s.LSP.WarningDiagnostic = base.Foreground(warning)
778	s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
779	s.LSP.InfoDiagnostic = base.Foreground(info)
780
781	// Files
782	s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
783	s.Files.Additions = lipgloss.NewStyle().Foreground(successMuted)
784	s.Files.Deletions = lipgloss.NewStyle().Foreground(error)
785	s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
786	s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
787	s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
788
789	// Sidebar
790	s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
791	s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
792
793	// ModelInfo
794	s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
795	s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
796	s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
797	s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
798	s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
799	s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
800	s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
801	s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
802
803	// ResourceGroup
804	s.Resource.DefaultTitleFg = fgMuted
805	s.Resource.DefaultDescFg = fgSubtle
806
807	// Chat
808	messageFocussedBorder := lipgloss.Border{
809		Left: "▌",
810	}
811
812	s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
813	s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
814		BorderForeground(primary).BorderStyle(lipgloss.NormalBorder())
815	s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
816		BorderForeground(primary).BorderStyle(messageFocussedBorder)
817	s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
818	s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
819		BorderForeground(successMuted).BorderStyle(messageFocussedBorder)
820	s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
821	s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
822		Background(danger).Foreground(onAccent)
823	s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
824	s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
825
826	// Message item styles
827	s.Messages.ToolCallFocused = muted.PaddingLeft(1).
828		BorderStyle(messageFocussedBorder).
829		BorderLeft(true).
830		BorderForeground(successMuted)
831	s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
832	// No padding or border for compact tool calls within messages
833	s.Messages.ToolCallCompact = muted
834	s.Messages.SectionHeader = base.PaddingLeft(2)
835	s.Messages.AssistantInfoIcon = subtle
836	s.Messages.AssistantInfoModel = muted
837	s.Messages.AssistantInfoProvider = subtle
838	s.Messages.AssistantInfoDuration = subtle
839	s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
840
841	// Thinking section styles
842	s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
843	s.Messages.ThinkingTruncationHint = muted
844	s.Messages.ThinkingFooterTitle = muted
845	s.Messages.ThinkingFooterDuration = subtle
846
847	// Text selection.
848	s.TextSelection = lipgloss.NewStyle().Foreground(onPrimary).Background(primary)
849
850	// Dialog styles
851	s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
852	s.Dialog.TitleText = base.Foreground(primary)
853	s.Dialog.TitleError = base.Foreground(danger)
854	s.Dialog.TitleAccent = base.Foreground(success).Bold(true)
855	s.Dialog.TitleLineBase = lipgloss.NewStyle()
856	s.Dialog.TitleGradFromColor = primary
857	s.Dialog.TitleGradToColor = secondary
858
859	// Dialog.ListItem (commands, reasoning, models)
860	s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
861	s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
862
863	// Dialog.Models
864	s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
865
866	// Dialog.Permissions
867	s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
868	s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
869	s.Dialog.Permissions.ParamsBg = bgSubtle
870
871	// Dialog.Quit
872	s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
873	s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
874	s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
875	s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
876	s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
877	s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
878	s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
879	s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
880	s.Dialog.Help.ShortSeparator = base.Foreground(border)
881	s.Dialog.Help.Ellipsis = base.Foreground(border)
882	s.Dialog.Help.FullKey = base.Foreground(fgMuted)
883	s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
884	s.Dialog.Help.FullSeparator = base.Foreground(border)
885	s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
886	s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
887	s.Dialog.InputPrompt = base.Margin(1, 1)
888
889	s.Dialog.List = base.Margin(0, 0, 1, 0)
890	s.Dialog.ContentPanel = base.Background(bgSubtle).Foreground(fgBase).Padding(1, 2)
891	s.Dialog.Spinner = base.Foreground(secondary)
892	s.Dialog.ScrollbarThumb = base.Foreground(secondary)
893	s.Dialog.ScrollbarTrack = base.Foreground(border)
894
895	s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
896
897	// API key input dialog
898	s.Dialog.APIKey.Spinner = base.Foreground(success)
899
900	// OAuth dialog
901	s.Dialog.OAuth.Spinner = base.Foreground(successSubtle)
902	s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(onAccent)
903	s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(onAccent)
904	s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(successSubtle)
905	s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(successMuted).Underline(true)
906	s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
907	s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
908	s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
909	s.Dialog.OAuth.UserCodeBg = bgBaseLighter
910
911	s.Dialog.Arguments.Content = base.Padding(1)
912	s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
913	s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
914	s.Dialog.Arguments.InputLabelFocused = base.Bold(true)
915	s.Dialog.Arguments.InputRequiredMarkBlurred = base.Foreground(fgMuted).SetString("*")
916	s.Dialog.Arguments.InputRequiredMarkFocused = base.Foreground(primary).Bold(true).SetString("*")
917
918	s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(danger)
919	s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(danger)
920	s.Dialog.Sessions.DeletingMessage = base.Padding(1)
921	s.Dialog.Sessions.DeletingTitleGradientFromColor = danger
922	s.Dialog.Sessions.DeletingTitleGradientToColor = primary
923	s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
924	s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(danger).Foreground(onAccent)
925
926	s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(warning)
927	s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(warning)
928	s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
929	s.Dialog.Sessions.RenamingTitleGradientFromColor = warning
930	s.Dialog.Sessions.RenamingTitleGradientToColor = tertiary
931	s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
932	s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
933	s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(fgMuted)
934	s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
935	s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
936
937	s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
938	s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(success).Padding(0, 1).Bold(true).SetString("OKAY!")
939	s.Status.InfoIndicator = s.Status.SuccessIndicator
940	s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
941	s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(bgOverlay).Background(warningStrong).SetString("WARNING")
942	s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(bgBase).Background(danger).SetString("ERROR")
943	s.Status.SuccessMessage = base.Foreground(bgSubtle).Background(successMuted).Padding(0, 1)
944	s.Status.InfoMessage = s.Status.SuccessMessage
945	s.Status.UpdateMessage = s.Status.SuccessMessage
946	s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(bgOverlay).Background(warning)
947	s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(onAccent).Background(error)
948
949	// Completions styles
950	s.Completions.Normal = base.Background(bgSubtle).Foreground(fgBase)
951	s.Completions.Focused = base.Background(primary).Foreground(onAccent)
952	s.Completions.Match = base.Underline(true)
953
954	// Attachments styles
955	attachmentIconStyle := base.Foreground(bgSubtle).Background(success).Padding(0, 1)
956	s.Attachments.Image = attachmentIconStyle.SetString(ImageIcon)
957	s.Attachments.Text = attachmentIconStyle.SetString(TextIcon)
958	s.Attachments.Normal = base.Padding(0, 1).MarginRight(1).Background(fgMuted).Foreground(fgBase)
959	s.Attachments.Deleting = base.Padding(0, 1).Bold(true).Background(danger).Foreground(fgBase)
960
961	// Pills styles
962	s.Pills.Base = base.Padding(0, 1)
963	s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
964	s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
965	s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString("  •")
966	s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
967	s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
968	s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
969	s.Pills.QueueGradFromColor = error
970	s.Pills.QueueGradToColor = secondary
971	s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
972	s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
973	s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
974	s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(successMuted)
975	s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
976	s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
977	s.Pills.Area = base
978
979	return s
980}