styles.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/lipgloss/v2"
 12	"github.com/alecthomas/chroma/v2"
 13	"github.com/charmbracelet/crush/internal/tui/exp/diffview"
 14	"github.com/charmbracelet/glamour/v2/ansi"
 15	"github.com/charmbracelet/x/exp/charmtone"
 16)
 17
 18const (
 19	CheckIcon    string = "✓"
 20	ErrorIcon    string = "×"
 21	WarningIcon  string = "⚠"
 22	InfoIcon     string = "ⓘ"
 23	HintIcon     string = "∵"
 24	SpinnerIcon  string = "..."
 25	LoadingIcon  string = "⟳"
 26	DocumentIcon string = "🖼"
 27	ModelIcon    string = "◇"
 28
 29	ToolPending string = "●"
 30	ToolSuccess string = "✓"
 31	ToolError   string = "×"
 32
 33	BorderThin  string = "│"
 34	BorderThick string = "▌"
 35
 36	SectionSeparator string = "─"
 37)
 38
 39const (
 40	defaultMargin     = 2
 41	defaultListIndent = 2
 42)
 43
 44type Styles struct {
 45	WindowTooSmall lipgloss.Style
 46
 47	// Reusable text styles
 48	Base   lipgloss.Style
 49	Muted  lipgloss.Style
 50	Subtle lipgloss.Style
 51
 52	// Tags
 53	TagBase  lipgloss.Style
 54	TagError lipgloss.Style
 55	TagInfo  lipgloss.Style
 56
 57	// Headers
 58	HeaderTool       lipgloss.Style
 59	HeaderToolNested lipgloss.Style
 60
 61	// Panels
 62	PanelMuted lipgloss.Style
 63	PanelBase  lipgloss.Style
 64
 65	// Line numbers for code blocks
 66	LineNumber lipgloss.Style
 67
 68	// Message borders
 69	FocusedMessageBorder lipgloss.Border
 70
 71	// Tool calls
 72	ToolCallPending   lipgloss.Style
 73	ToolCallError     lipgloss.Style
 74	ToolCallSuccess   lipgloss.Style
 75	ToolCallCancelled lipgloss.Style
 76	EarlyStateMessage lipgloss.Style
 77
 78	// Text selection
 79	TextSelection lipgloss.Style
 80
 81	// LSP and MCP status indicators
 82	ItemOfflineIcon lipgloss.Style
 83	ItemBusyIcon    lipgloss.Style
 84	ItemErrorIcon   lipgloss.Style
 85	ItemOnlineIcon  lipgloss.Style
 86
 87	// Markdown & Chroma
 88	Markdown ansi.StyleConfig
 89
 90	// Inputs
 91	TextInput textinput.Styles
 92	TextArea  textarea.Styles
 93
 94	// Help
 95	Help help.Styles
 96
 97	// Diff
 98	Diff diffview.Style
 99
100	// FilePicker
101	FilePicker filepicker.Styles
102
103	// Buttons
104	ButtonFocus lipgloss.Style
105	ButtonBlur  lipgloss.Style
106
107	// Borders
108	BorderFocus lipgloss.Style
109	BorderBlur  lipgloss.Style
110
111	// Editor
112	EditorPromptNormalFocused   lipgloss.Style
113	EditorPromptNormalBlurred   lipgloss.Style
114	EditorPromptYoloIconFocused lipgloss.Style
115	EditorPromptYoloIconBlurred lipgloss.Style
116	EditorPromptYoloDotsFocused lipgloss.Style
117	EditorPromptYoloDotsBlurred lipgloss.Style
118
119	// Background
120	Background color.Color
121
122	// Logo
123	LogoFieldColor   color.Color
124	LogoTitleColorA  color.Color
125	LogoTitleColorB  color.Color
126	LogoCharmColor   color.Color
127	LogoVersionColor color.Color
128
129	// Colors - semantic colors for tool rendering.
130	Primary       color.Color
131	Secondary     color.Color
132	Tertiary      color.Color
133	BgBase        color.Color
134	BgBaseLighter color.Color
135	BgSubtle      color.Color
136	BgOverlay     color.Color
137	FgBase        color.Color
138	FgMuted       color.Color
139	FgHalfMuted   color.Color
140	FgSubtle      color.Color
141	Border        color.Color
142	BorderColor   color.Color // Border focus color
143	Warning       color.Color
144	Info          color.Color
145	White         color.Color
146	BlueLight     color.Color
147	Blue          color.Color
148	Green         color.Color
149	GreenDark     color.Color
150	Red           color.Color
151	RedDark       color.Color
152	Yellow        color.Color
153
154	// Section Title
155	Section struct {
156		Title lipgloss.Style
157		Line  lipgloss.Style
158	}
159
160	// Initialize
161	Initialize struct {
162		Header  lipgloss.Style
163		Content lipgloss.Style
164		Accent  lipgloss.Style
165	}
166
167	// LSP
168	LSP struct {
169		ErrorDiagnostic   lipgloss.Style
170		WarningDiagnostic lipgloss.Style
171		HintDiagnostic    lipgloss.Style
172		InfoDiagnostic    lipgloss.Style
173	}
174
175	// Files
176	Files struct {
177		Path      lipgloss.Style
178		Additions lipgloss.Style
179		Deletions lipgloss.Style
180	}
181
182	// Chat
183	Chat struct {
184		// Message item styles
185		Message struct {
186			UserBlurred      lipgloss.Style
187			UserFocused      lipgloss.Style
188			AssistantBlurred lipgloss.Style
189			AssistantFocused lipgloss.Style
190			NoContent        lipgloss.Style
191			Thinking         lipgloss.Style
192			ErrorTag         lipgloss.Style
193			ErrorTitle       lipgloss.Style
194			ErrorDetails     lipgloss.Style
195			Attachment       lipgloss.Style
196			ToolCallFocused  lipgloss.Style
197			ToolCallBlurred  lipgloss.Style
198			ThinkingFooter   lipgloss.Style
199			SectionHeader    lipgloss.Style
200		}
201	}
202
203	// Tool - styles for tool call rendering
204	Tool struct {
205		// Icon styles with tool status
206		IconPending   lipgloss.Style // Pending operation icon
207		IconSuccess   lipgloss.Style // Successful operation icon
208		IconError     lipgloss.Style // Error operation icon
209		IconCancelled lipgloss.Style // Cancelled operation icon
210
211		// Tool name styles
212		NameNormal lipgloss.Style // Normal tool name
213		NameNested lipgloss.Style // Nested tool name
214
215		// Parameter list styles
216		ParamMain lipgloss.Style // Main parameter
217		ParamKey  lipgloss.Style // Parameter keys
218
219		// Content rendering styles
220		ContentLine       lipgloss.Style // Individual content line with background and width
221		ContentTruncation lipgloss.Style // Truncation message "… (N lines)"
222		ContentCodeLine   lipgloss.Style // Code line with background and width
223		ContentCodeBg     color.Color    // Background color for syntax highlighting
224		BodyPadding       lipgloss.Style // Body content padding (PaddingLeft(2))
225
226		// Deprecated - kept for backward compatibility
227		ContentBg         lipgloss.Style // Content background
228		ContentText       lipgloss.Style // Content text
229		ContentLineNumber lipgloss.Style // Line numbers in code
230
231		// State message styles
232		StateWaiting   lipgloss.Style // "Waiting for tool response..."
233		StateCancelled lipgloss.Style // "Canceled."
234
235		// Error styles
236		ErrorTag     lipgloss.Style // ERROR tag
237		ErrorMessage lipgloss.Style // Error message text
238
239		// Diff styles
240		DiffTruncation lipgloss.Style // Diff truncation message with padding
241
242		// Multi-edit note styles
243		NoteTag     lipgloss.Style // NOTE tag (yellow background)
244		NoteMessage lipgloss.Style // Note message text
245
246		// Job header styles (for bash jobs)
247		JobIconPending lipgloss.Style // Pending job icon (green dark)
248		JobIconError   lipgloss.Style // Error job icon (red dark)
249		JobIconSuccess lipgloss.Style // Success job icon (green)
250		JobToolName    lipgloss.Style // Job tool name "Bash" (blue)
251		JobAction      lipgloss.Style // Action text (Start, Output, Kill)
252		JobPID         lipgloss.Style // PID text
253		JobDescription lipgloss.Style // Description text
254
255		// Agent task styles
256		AgentTaskTag lipgloss.Style // Agent task tag (blue background, bold)
257		AgentPrompt  lipgloss.Style // Agent prompt text
258	}
259}
260
261// ChromaTheme converts the current markdown chroma styles to a chroma
262// StyleEntries map.
263func (s *Styles) ChromaTheme() chroma.StyleEntries {
264	rules := s.Markdown.CodeBlock
265
266	return chroma.StyleEntries{
267		chroma.Text:                chromaStyle(rules.Chroma.Text),
268		chroma.Error:               chromaStyle(rules.Chroma.Error),
269		chroma.Comment:             chromaStyle(rules.Chroma.Comment),
270		chroma.CommentPreproc:      chromaStyle(rules.Chroma.CommentPreproc),
271		chroma.Keyword:             chromaStyle(rules.Chroma.Keyword),
272		chroma.KeywordReserved:     chromaStyle(rules.Chroma.KeywordReserved),
273		chroma.KeywordNamespace:    chromaStyle(rules.Chroma.KeywordNamespace),
274		chroma.KeywordType:         chromaStyle(rules.Chroma.KeywordType),
275		chroma.Operator:            chromaStyle(rules.Chroma.Operator),
276		chroma.Punctuation:         chromaStyle(rules.Chroma.Punctuation),
277		chroma.Name:                chromaStyle(rules.Chroma.Name),
278		chroma.NameBuiltin:         chromaStyle(rules.Chroma.NameBuiltin),
279		chroma.NameTag:             chromaStyle(rules.Chroma.NameTag),
280		chroma.NameAttribute:       chromaStyle(rules.Chroma.NameAttribute),
281		chroma.NameClass:           chromaStyle(rules.Chroma.NameClass),
282		chroma.NameConstant:        chromaStyle(rules.Chroma.NameConstant),
283		chroma.NameDecorator:       chromaStyle(rules.Chroma.NameDecorator),
284		chroma.NameException:       chromaStyle(rules.Chroma.NameException),
285		chroma.NameFunction:        chromaStyle(rules.Chroma.NameFunction),
286		chroma.NameOther:           chromaStyle(rules.Chroma.NameOther),
287		chroma.Literal:             chromaStyle(rules.Chroma.Literal),
288		chroma.LiteralNumber:       chromaStyle(rules.Chroma.LiteralNumber),
289		chroma.LiteralDate:         chromaStyle(rules.Chroma.LiteralDate),
290		chroma.LiteralString:       chromaStyle(rules.Chroma.LiteralString),
291		chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
292		chroma.GenericDeleted:      chromaStyle(rules.Chroma.GenericDeleted),
293		chroma.GenericEmph:         chromaStyle(rules.Chroma.GenericEmph),
294		chroma.GenericInserted:     chromaStyle(rules.Chroma.GenericInserted),
295		chroma.GenericStrong:       chromaStyle(rules.Chroma.GenericStrong),
296		chroma.GenericSubheading:   chromaStyle(rules.Chroma.GenericSubheading),
297		chroma.Background:          chromaStyle(rules.Chroma.Background),
298	}
299}
300
301func DefaultStyles() Styles {
302	var (
303		primary   = charmtone.Charple
304		secondary = charmtone.Dolly
305		tertiary  = charmtone.Bok
306		// accent    = charmtone.Zest
307
308		// Backgrounds
309		bgBase        = charmtone.Pepper
310		bgBaseLighter = charmtone.BBQ
311		bgSubtle      = charmtone.Charcoal
312		bgOverlay     = charmtone.Iron
313
314		// Foregrounds
315		fgBase      = charmtone.Ash
316		fgMuted     = charmtone.Squid
317		fgHalfMuted = charmtone.Smoke
318		fgSubtle    = charmtone.Oyster
319		// fgSelected  = charmtone.Salt
320
321		// Borders
322		border      = charmtone.Charcoal
323		borderFocus = charmtone.Charple
324
325		// Status
326		warning = charmtone.Zest
327		info    = charmtone.Malibu
328
329		// Colors
330		white = charmtone.Butter
331
332		blueLight = charmtone.Sardine
333		blue      = charmtone.Malibu
334
335		// yellow = charmtone.Mustard
336		yellow = charmtone.Mustard
337		// citron = charmtone.Citron
338
339		green     = charmtone.Julep
340		greenDark = charmtone.Guac
341		// greenLight = charmtone.Bok
342
343		red     = charmtone.Coral
344		redDark = charmtone.Sriracha
345		// redLight = charmtone.Salmon
346		// cherry   = charmtone.Cherry
347	)
348
349	normalBorder := lipgloss.NormalBorder()
350
351	base := lipgloss.NewStyle().Foreground(fgBase)
352
353	s := Styles{}
354
355	s.Background = bgBase
356
357	// Populate color fields
358	s.Primary = primary
359	s.Secondary = secondary
360	s.Tertiary = tertiary
361	s.BgBase = bgBase
362	s.BgBaseLighter = bgBaseLighter
363	s.BgSubtle = bgSubtle
364	s.BgOverlay = bgOverlay
365	s.FgBase = fgBase
366	s.FgMuted = fgMuted
367	s.FgHalfMuted = fgHalfMuted
368	s.FgSubtle = fgSubtle
369	s.Border = border
370	s.BorderColor = borderFocus
371	s.Warning = warning
372	s.Info = info
373	s.White = white
374	s.BlueLight = blueLight
375	s.Blue = blue
376	s.Green = green
377	s.GreenDark = greenDark
378	s.Red = red
379	s.RedDark = redDark
380	s.Yellow = yellow
381
382	s.TextInput = textinput.Styles{
383		Focused: textinput.StyleState{
384			Text:        base,
385			Placeholder: base.Foreground(fgSubtle),
386			Prompt:      base.Foreground(tertiary),
387			Suggestion:  base.Foreground(fgSubtle),
388		},
389		Blurred: textinput.StyleState{
390			Text:        base.Foreground(fgMuted),
391			Placeholder: base.Foreground(fgSubtle),
392			Prompt:      base.Foreground(fgMuted),
393			Suggestion:  base.Foreground(fgSubtle),
394		},
395		Cursor: textinput.CursorStyle{
396			Color: secondary,
397			Shape: tea.CursorBar,
398			Blink: true,
399		},
400	}
401
402	s.TextArea = textarea.Styles{
403		Focused: textarea.StyleState{
404			Base:             base,
405			Text:             base,
406			LineNumber:       base.Foreground(fgSubtle),
407			CursorLine:       base,
408			CursorLineNumber: base.Foreground(fgSubtle),
409			Placeholder:      base.Foreground(fgSubtle),
410			Prompt:           base.Foreground(tertiary),
411		},
412		Blurred: textarea.StyleState{
413			Base:             base,
414			Text:             base.Foreground(fgMuted),
415			LineNumber:       base.Foreground(fgMuted),
416			CursorLine:       base,
417			CursorLineNumber: base.Foreground(fgMuted),
418			Placeholder:      base.Foreground(fgSubtle),
419			Prompt:           base.Foreground(fgMuted),
420		},
421		Cursor: textarea.CursorStyle{
422			Color: secondary,
423			Shape: tea.CursorBar,
424			Blink: true,
425		},
426	}
427
428	s.Markdown = ansi.StyleConfig{
429		Document: ansi.StyleBlock{
430			StylePrimitive: ansi.StylePrimitive{
431				// BlockPrefix: "\n",
432				// BlockSuffix: "\n",
433				Color: stringPtr(charmtone.Smoke.Hex()),
434			},
435			// Margin: uintPtr(defaultMargin),
436		},
437		BlockQuote: ansi.StyleBlock{
438			StylePrimitive: ansi.StylePrimitive{},
439			Indent:         uintPtr(1),
440			IndentToken:    stringPtr("│ "),
441		},
442		List: ansi.StyleList{
443			LevelIndent: defaultListIndent,
444		},
445		Heading: ansi.StyleBlock{
446			StylePrimitive: ansi.StylePrimitive{
447				BlockSuffix: "\n",
448				Color:       stringPtr(charmtone.Malibu.Hex()),
449				Bold:        boolPtr(true),
450			},
451		},
452		H1: ansi.StyleBlock{
453			StylePrimitive: ansi.StylePrimitive{
454				Prefix:          " ",
455				Suffix:          " ",
456				Color:           stringPtr(charmtone.Zest.Hex()),
457				BackgroundColor: stringPtr(charmtone.Charple.Hex()),
458				Bold:            boolPtr(true),
459			},
460		},
461		H2: ansi.StyleBlock{
462			StylePrimitive: ansi.StylePrimitive{
463				Prefix: "## ",
464			},
465		},
466		H3: ansi.StyleBlock{
467			StylePrimitive: ansi.StylePrimitive{
468				Prefix: "### ",
469			},
470		},
471		H4: ansi.StyleBlock{
472			StylePrimitive: ansi.StylePrimitive{
473				Prefix: "#### ",
474			},
475		},
476		H5: ansi.StyleBlock{
477			StylePrimitive: ansi.StylePrimitive{
478				Prefix: "##### ",
479			},
480		},
481		H6: ansi.StyleBlock{
482			StylePrimitive: ansi.StylePrimitive{
483				Prefix: "###### ",
484				Color:  stringPtr(charmtone.Guac.Hex()),
485				Bold:   boolPtr(false),
486			},
487		},
488		Strikethrough: ansi.StylePrimitive{
489			CrossedOut: boolPtr(true),
490		},
491		Emph: ansi.StylePrimitive{
492			Italic: boolPtr(true),
493		},
494		Strong: ansi.StylePrimitive{
495			Bold: boolPtr(true),
496		},
497		HorizontalRule: ansi.StylePrimitive{
498			Color:  stringPtr(charmtone.Charcoal.Hex()),
499			Format: "\n--------\n",
500		},
501		Item: ansi.StylePrimitive{
502			BlockPrefix: "• ",
503		},
504		Enumeration: ansi.StylePrimitive{
505			BlockPrefix: ". ",
506		},
507		Task: ansi.StyleTask{
508			StylePrimitive: ansi.StylePrimitive{},
509			Ticked:         "[✓] ",
510			Unticked:       "[ ] ",
511		},
512		Link: ansi.StylePrimitive{
513			Color:     stringPtr(charmtone.Zinc.Hex()),
514			Underline: boolPtr(true),
515		},
516		LinkText: ansi.StylePrimitive{
517			Color: stringPtr(charmtone.Guac.Hex()),
518			Bold:  boolPtr(true),
519		},
520		Image: ansi.StylePrimitive{
521			Color:     stringPtr(charmtone.Cheeky.Hex()),
522			Underline: boolPtr(true),
523		},
524		ImageText: ansi.StylePrimitive{
525			Color:  stringPtr(charmtone.Squid.Hex()),
526			Format: "Image: {{.text}} →",
527		},
528		Code: ansi.StyleBlock{
529			StylePrimitive: ansi.StylePrimitive{
530				Prefix:          " ",
531				Suffix:          " ",
532				Color:           stringPtr(charmtone.Coral.Hex()),
533				BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
534			},
535		},
536		CodeBlock: ansi.StyleCodeBlock{
537			StyleBlock: ansi.StyleBlock{
538				StylePrimitive: ansi.StylePrimitive{
539					Color: stringPtr(charmtone.Charcoal.Hex()),
540				},
541				Margin: uintPtr(defaultMargin),
542			},
543			Chroma: &ansi.Chroma{
544				Text: ansi.StylePrimitive{
545					Color: stringPtr(charmtone.Smoke.Hex()),
546				},
547				Error: ansi.StylePrimitive{
548					Color:           stringPtr(charmtone.Butter.Hex()),
549					BackgroundColor: stringPtr(charmtone.Sriracha.Hex()),
550				},
551				Comment: ansi.StylePrimitive{
552					Color: stringPtr(charmtone.Oyster.Hex()),
553				},
554				CommentPreproc: ansi.StylePrimitive{
555					Color: stringPtr(charmtone.Bengal.Hex()),
556				},
557				Keyword: ansi.StylePrimitive{
558					Color: stringPtr(charmtone.Malibu.Hex()),
559				},
560				KeywordReserved: ansi.StylePrimitive{
561					Color: stringPtr(charmtone.Pony.Hex()),
562				},
563				KeywordNamespace: ansi.StylePrimitive{
564					Color: stringPtr(charmtone.Pony.Hex()),
565				},
566				KeywordType: ansi.StylePrimitive{
567					Color: stringPtr(charmtone.Guppy.Hex()),
568				},
569				Operator: ansi.StylePrimitive{
570					Color: stringPtr(charmtone.Salmon.Hex()),
571				},
572				Punctuation: ansi.StylePrimitive{
573					Color: stringPtr(charmtone.Zest.Hex()),
574				},
575				Name: ansi.StylePrimitive{
576					Color: stringPtr(charmtone.Smoke.Hex()),
577				},
578				NameBuiltin: ansi.StylePrimitive{
579					Color: stringPtr(charmtone.Cheeky.Hex()),
580				},
581				NameTag: ansi.StylePrimitive{
582					Color: stringPtr(charmtone.Mauve.Hex()),
583				},
584				NameAttribute: ansi.StylePrimitive{
585					Color: stringPtr(charmtone.Hazy.Hex()),
586				},
587				NameClass: ansi.StylePrimitive{
588					Color:     stringPtr(charmtone.Salt.Hex()),
589					Underline: boolPtr(true),
590					Bold:      boolPtr(true),
591				},
592				NameDecorator: ansi.StylePrimitive{
593					Color: stringPtr(charmtone.Citron.Hex()),
594				},
595				NameFunction: ansi.StylePrimitive{
596					Color: stringPtr(charmtone.Guac.Hex()),
597				},
598				LiteralNumber: ansi.StylePrimitive{
599					Color: stringPtr(charmtone.Julep.Hex()),
600				},
601				LiteralString: ansi.StylePrimitive{
602					Color: stringPtr(charmtone.Cumin.Hex()),
603				},
604				LiteralStringEscape: ansi.StylePrimitive{
605					Color: stringPtr(charmtone.Bok.Hex()),
606				},
607				GenericDeleted: ansi.StylePrimitive{
608					Color: stringPtr(charmtone.Coral.Hex()),
609				},
610				GenericEmph: ansi.StylePrimitive{
611					Italic: boolPtr(true),
612				},
613				GenericInserted: ansi.StylePrimitive{
614					Color: stringPtr(charmtone.Guac.Hex()),
615				},
616				GenericStrong: ansi.StylePrimitive{
617					Bold: boolPtr(true),
618				},
619				GenericSubheading: ansi.StylePrimitive{
620					Color: stringPtr(charmtone.Squid.Hex()),
621				},
622				Background: ansi.StylePrimitive{
623					BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
624				},
625			},
626		},
627		Table: ansi.StyleTable{
628			StyleBlock: ansi.StyleBlock{
629				StylePrimitive: ansi.StylePrimitive{},
630			},
631		},
632		DefinitionDescription: ansi.StylePrimitive{
633			BlockPrefix: "\n ",
634		},
635	}
636
637	s.Help = help.Styles{
638		ShortKey:       base.Foreground(fgMuted),
639		ShortDesc:      base.Foreground(fgSubtle),
640		ShortSeparator: base.Foreground(border),
641		Ellipsis:       base.Foreground(border),
642		FullKey:        base.Foreground(fgMuted),
643		FullDesc:       base.Foreground(fgSubtle),
644		FullSeparator:  base.Foreground(border),
645	}
646
647	s.Diff = diffview.Style{
648		DividerLine: diffview.LineStyle{
649			LineNumber: lipgloss.NewStyle().
650				Foreground(fgHalfMuted).
651				Background(bgBaseLighter),
652			Code: lipgloss.NewStyle().
653				Foreground(fgHalfMuted).
654				Background(bgBaseLighter),
655		},
656		MissingLine: diffview.LineStyle{
657			LineNumber: lipgloss.NewStyle().
658				Background(bgBaseLighter),
659			Code: lipgloss.NewStyle().
660				Background(bgBaseLighter),
661		},
662		EqualLine: diffview.LineStyle{
663			LineNumber: lipgloss.NewStyle().
664				Foreground(fgMuted).
665				Background(bgBase),
666			Code: lipgloss.NewStyle().
667				Foreground(fgMuted).
668				Background(bgBase),
669		},
670		InsertLine: diffview.LineStyle{
671			LineNumber: lipgloss.NewStyle().
672				Foreground(lipgloss.Color("#629657")).
673				Background(lipgloss.Color("#2b322a")),
674			Symbol: lipgloss.NewStyle().
675				Foreground(lipgloss.Color("#629657")).
676				Background(lipgloss.Color("#323931")),
677			Code: lipgloss.NewStyle().
678				Background(lipgloss.Color("#323931")),
679		},
680		DeleteLine: diffview.LineStyle{
681			LineNumber: lipgloss.NewStyle().
682				Foreground(lipgloss.Color("#a45c59")).
683				Background(lipgloss.Color("#312929")),
684			Symbol: lipgloss.NewStyle().
685				Foreground(lipgloss.Color("#a45c59")).
686				Background(lipgloss.Color("#383030")),
687			Code: lipgloss.NewStyle().
688				Background(lipgloss.Color("#383030")),
689		},
690	}
691
692	s.FilePicker = filepicker.Styles{
693		DisabledCursor:   base.Foreground(fgMuted),
694		Cursor:           base.Foreground(fgBase),
695		Symlink:          base.Foreground(fgSubtle),
696		Directory:        base.Foreground(primary),
697		File:             base.Foreground(fgBase),
698		DisabledFile:     base.Foreground(fgMuted),
699		DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
700		Permission:       base.Foreground(fgMuted),
701		Selected:         base.Background(primary).Foreground(fgBase),
702		FileSize:         base.Foreground(fgMuted),
703		EmptyDirectory:   base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
704	}
705
706	// borders
707	s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
708
709	// text presets
710	s.Base = lipgloss.NewStyle().Foreground(fgBase)
711	s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
712	s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
713
714	s.WindowTooSmall = s.Muted
715
716	// tag presets
717	s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
718	s.TagError = s.TagBase.Background(redDark)
719	s.TagInfo = s.TagBase.Background(blueLight)
720
721	// headers
722	s.HeaderTool = lipgloss.NewStyle().Foreground(blue)
723	s.HeaderToolNested = lipgloss.NewStyle().Foreground(fgHalfMuted)
724
725	// panels
726	s.PanelMuted = s.Muted.Background(bgBaseLighter)
727	s.PanelBase = lipgloss.NewStyle().Background(bgBase)
728
729	// code line number
730	s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
731
732	// Tool calls
733	s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
734	s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
735	s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
736	// Cancelled uses muted tone but same glyph as pending
737	s.ToolCallCancelled = s.Muted.SetString(ToolPending)
738	s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
739
740	// Tool rendering styles
741	s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
742	s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
743	s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
744	s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
745
746	s.Tool.NameNormal = base.Foreground(blue)
747	s.Tool.NameNested = base.Foreground(fgHalfMuted)
748
749	s.Tool.ParamMain = s.Subtle
750	s.Tool.ParamKey = s.Subtle
751
752	// Content rendering - prepared styles that accept width parameter
753	s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
754	s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
755	s.Tool.ContentCodeLine = s.Base.Background(bgBaseLighter)
756	s.Tool.ContentCodeBg = bgBase
757	s.Tool.BodyPadding = base.PaddingLeft(2)
758
759	// Deprecated - kept for backward compatibility
760	s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
761	s.Tool.ContentText = s.Muted
762	s.Tool.ContentLineNumber = s.Subtle
763
764	s.Tool.StateWaiting = base.Foreground(fgSubtle)
765	s.Tool.StateCancelled = base.Foreground(fgSubtle)
766
767	s.Tool.ErrorTag = base.Padding(0, 1).Background(red).Foreground(white)
768	s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
769
770	// Diff and multi-edit styles
771	s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
772	s.Tool.NoteTag = base.Padding(0, 1).Background(yellow).Foreground(white)
773	s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
774
775	// Job header styles
776	s.Tool.JobIconPending = base.Foreground(greenDark)
777	s.Tool.JobIconError = base.Foreground(redDark)
778	s.Tool.JobIconSuccess = base.Foreground(green)
779	s.Tool.JobToolName = base.Foreground(blue)
780	s.Tool.JobAction = base.Foreground(fgHalfMuted)
781	s.Tool.JobPID = s.Subtle
782	s.Tool.JobDescription = s.Subtle
783
784	// Agent task styles
785	s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
786	s.Tool.AgentPrompt = s.Muted
787
788	// Buttons
789	s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
790	s.ButtonBlur = s.Base.Background(bgSubtle)
791
792	// Borders
793	s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
794
795	// Editor
796	s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
797	s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
798	s.EditorPromptYoloIconFocused = lipgloss.NewStyle().Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
799	s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
800	s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().Foreground(charmtone.Zest).SetString(":::")
801	s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
802
803	// Logo colors
804	s.LogoFieldColor = primary
805	s.LogoTitleColorA = secondary
806	s.LogoTitleColorB = primary
807	s.LogoCharmColor = secondary
808	s.LogoVersionColor = primary
809
810	// Section
811	s.Section.Title = s.Subtle
812	s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
813
814	// Initialize
815	s.Initialize.Header = s.Base
816	s.Initialize.Content = s.Muted
817	s.Initialize.Accent = s.Base.Foreground(greenDark)
818
819	// LSP and MCP status.
820	s.ItemOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Squid).SetString("●")
821	s.ItemBusyIcon = s.ItemOfflineIcon.Foreground(charmtone.Citron)
822	s.ItemErrorIcon = s.ItemOfflineIcon.Foreground(charmtone.Coral)
823	s.ItemOnlineIcon = s.ItemOfflineIcon.Foreground(charmtone.Guac)
824
825	// LSP
826	s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
827	s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
828	s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
829	s.LSP.InfoDiagnostic = s.Base.Foreground(info)
830
831	// Files
832	s.Files.Path = s.Muted
833	s.Files.Additions = s.Base.Foreground(greenDark)
834	s.Files.Deletions = s.Base.Foreground(redDark)
835
836	// Chat
837	messageFocussedBorder := lipgloss.Border{
838		Left: "▌",
839	}
840
841	s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
842	s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
843		BorderForeground(primary).BorderStyle(normalBorder)
844	s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
845		BorderForeground(primary).BorderStyle(messageFocussedBorder)
846	s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
847	s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
848		BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
849	s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
850	s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
851		Background(red).Foreground(white)
852	s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
853	s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
854
855	// Message item styles
856	s.Chat.Message.Attachment = lipgloss.NewStyle().MarginLeft(1).Background(bgSubtle)
857	s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
858		BorderStyle(messageFocussedBorder).
859		BorderLeft(true).
860		BorderForeground(greenDark)
861	s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
862	s.Chat.Message.ThinkingFooter = s.Base
863	s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
864
865	// Text selection.
866	s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
867
868	return s
869}
870
871// Helper functions for style pointers
872func boolPtr(b bool) *bool       { return &b }
873func stringPtr(s string) *string { return &s }
874func uintPtr(u uint) *uint       { return &u }
875func chromaStyle(style ansi.StylePrimitive) string {
876	var s string
877
878	if style.Color != nil {
879		s = *style.Color
880	}
881	if style.BackgroundColor != nil {
882		if s != "" {
883			s += " "
884		}
885		s += "bg:" + *style.BackgroundColor
886	}
887	if style.Italic != nil && *style.Italic {
888		if s != "" {
889			s += " "
890		}
891		s += "italic"
892	}
893	if style.Bold != nil && *style.Bold {
894		if s != "" {
895			s += " "
896		}
897		s += "bold"
898	}
899	if style.Underline != nil && *style.Underline {
900		if s != "" {
901			s += " "
902		}
903		s += "underline"
904	}
905
906	return s
907}