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/glamour/v2/ansi"
 12	"charm.land/lipgloss/v2"
 13	"github.com/alecthomas/chroma/v2"
 14	"github.com/charmbracelet/crush/internal/tui/exp/diffview"
 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	// Dialog styles
261	Dialog struct {
262		Title lipgloss.Style
263		// View is the main content area style.
264		View lipgloss.Style
265		// HelpView is the line that contains the help.
266		HelpView lipgloss.Style
267		Help     struct {
268			Ellipsis       lipgloss.Style
269			ShortKey       lipgloss.Style
270			ShortDesc      lipgloss.Style
271			ShortSeparator lipgloss.Style
272			FullKey        lipgloss.Style
273			FullDesc       lipgloss.Style
274			FullSeparator  lipgloss.Style
275		}
276		NormalItem   lipgloss.Style
277		SelectedItem lipgloss.Style
278		InputPrompt  lipgloss.Style
279	}
280}
281
282// ChromaTheme converts the current markdown chroma styles to a chroma
283// StyleEntries map.
284func (s *Styles) ChromaTheme() chroma.StyleEntries {
285	rules := s.Markdown.CodeBlock
286
287	return chroma.StyleEntries{
288		chroma.Text:                chromaStyle(rules.Chroma.Text),
289		chroma.Error:               chromaStyle(rules.Chroma.Error),
290		chroma.Comment:             chromaStyle(rules.Chroma.Comment),
291		chroma.CommentPreproc:      chromaStyle(rules.Chroma.CommentPreproc),
292		chroma.Keyword:             chromaStyle(rules.Chroma.Keyword),
293		chroma.KeywordReserved:     chromaStyle(rules.Chroma.KeywordReserved),
294		chroma.KeywordNamespace:    chromaStyle(rules.Chroma.KeywordNamespace),
295		chroma.KeywordType:         chromaStyle(rules.Chroma.KeywordType),
296		chroma.Operator:            chromaStyle(rules.Chroma.Operator),
297		chroma.Punctuation:         chromaStyle(rules.Chroma.Punctuation),
298		chroma.Name:                chromaStyle(rules.Chroma.Name),
299		chroma.NameBuiltin:         chromaStyle(rules.Chroma.NameBuiltin),
300		chroma.NameTag:             chromaStyle(rules.Chroma.NameTag),
301		chroma.NameAttribute:       chromaStyle(rules.Chroma.NameAttribute),
302		chroma.NameClass:           chromaStyle(rules.Chroma.NameClass),
303		chroma.NameConstant:        chromaStyle(rules.Chroma.NameConstant),
304		chroma.NameDecorator:       chromaStyle(rules.Chroma.NameDecorator),
305		chroma.NameException:       chromaStyle(rules.Chroma.NameException),
306		chroma.NameFunction:        chromaStyle(rules.Chroma.NameFunction),
307		chroma.NameOther:           chromaStyle(rules.Chroma.NameOther),
308		chroma.Literal:             chromaStyle(rules.Chroma.Literal),
309		chroma.LiteralNumber:       chromaStyle(rules.Chroma.LiteralNumber),
310		chroma.LiteralDate:         chromaStyle(rules.Chroma.LiteralDate),
311		chroma.LiteralString:       chromaStyle(rules.Chroma.LiteralString),
312		chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
313		chroma.GenericDeleted:      chromaStyle(rules.Chroma.GenericDeleted),
314		chroma.GenericEmph:         chromaStyle(rules.Chroma.GenericEmph),
315		chroma.GenericInserted:     chromaStyle(rules.Chroma.GenericInserted),
316		chroma.GenericStrong:       chromaStyle(rules.Chroma.GenericStrong),
317		chroma.GenericSubheading:   chromaStyle(rules.Chroma.GenericSubheading),
318		chroma.Background:          chromaStyle(rules.Chroma.Background),
319	}
320}
321
322// DialogHelpStyles returns the styles for dialog help.
323func (s *Styles) DialogHelpStyles() help.Styles {
324	return help.Styles(s.Dialog.Help)
325}
326
327// DefaultStyles returns the default styles for the UI.
328func DefaultStyles() Styles {
329	var (
330		primary   = charmtone.Charple
331		secondary = charmtone.Dolly
332		tertiary  = charmtone.Bok
333		// accent    = charmtone.Zest
334
335		// Backgrounds
336		bgBase        = charmtone.Pepper
337		bgBaseLighter = charmtone.BBQ
338		bgSubtle      = charmtone.Charcoal
339		bgOverlay     = charmtone.Iron
340
341		// Foregrounds
342		fgBase      = charmtone.Ash
343		fgMuted     = charmtone.Squid
344		fgHalfMuted = charmtone.Smoke
345		fgSubtle    = charmtone.Oyster
346		// fgSelected  = charmtone.Salt
347
348		// Borders
349		border      = charmtone.Charcoal
350		borderFocus = charmtone.Charple
351
352		// Status
353		warning = charmtone.Zest
354		info    = charmtone.Malibu
355
356		// Colors
357		white = charmtone.Butter
358
359		blueLight = charmtone.Sardine
360		blue      = charmtone.Malibu
361
362		// yellow = charmtone.Mustard
363		yellow = charmtone.Mustard
364		// citron = charmtone.Citron
365
366		green     = charmtone.Julep
367		greenDark = charmtone.Guac
368		// greenLight = charmtone.Bok
369
370		red     = charmtone.Coral
371		redDark = charmtone.Sriracha
372		// redLight = charmtone.Salmon
373		// cherry   = charmtone.Cherry
374	)
375
376	normalBorder := lipgloss.NormalBorder()
377
378	base := lipgloss.NewStyle().Foreground(fgBase)
379
380	s := Styles{}
381
382	s.Background = bgBase
383
384	// Populate color fields
385	s.Primary = primary
386	s.Secondary = secondary
387	s.Tertiary = tertiary
388	s.BgBase = bgBase
389	s.BgBaseLighter = bgBaseLighter
390	s.BgSubtle = bgSubtle
391	s.BgOverlay = bgOverlay
392	s.FgBase = fgBase
393	s.FgMuted = fgMuted
394	s.FgHalfMuted = fgHalfMuted
395	s.FgSubtle = fgSubtle
396	s.Border = border
397	s.BorderColor = borderFocus
398	s.Warning = warning
399	s.Info = info
400	s.White = white
401	s.BlueLight = blueLight
402	s.Blue = blue
403	s.Green = green
404	s.GreenDark = greenDark
405	s.Red = red
406	s.RedDark = redDark
407	s.Yellow = yellow
408
409	s.TextInput = textinput.Styles{
410		Focused: textinput.StyleState{
411			Text:        base,
412			Placeholder: base.Foreground(fgSubtle),
413			Prompt:      base.Foreground(tertiary),
414			Suggestion:  base.Foreground(fgSubtle),
415		},
416		Blurred: textinput.StyleState{
417			Text:        base.Foreground(fgMuted),
418			Placeholder: base.Foreground(fgSubtle),
419			Prompt:      base.Foreground(fgMuted),
420			Suggestion:  base.Foreground(fgSubtle),
421		},
422		Cursor: textinput.CursorStyle{
423			Color: secondary,
424			Shape: tea.CursorBlock,
425			Blink: true,
426		},
427	}
428
429	s.TextArea = textarea.Styles{
430		Focused: textarea.StyleState{
431			Base:             base,
432			Text:             base,
433			LineNumber:       base.Foreground(fgSubtle),
434			CursorLine:       base,
435			CursorLineNumber: base.Foreground(fgSubtle),
436			Placeholder:      base.Foreground(fgSubtle),
437			Prompt:           base.Foreground(tertiary),
438		},
439		Blurred: textarea.StyleState{
440			Base:             base,
441			Text:             base.Foreground(fgMuted),
442			LineNumber:       base.Foreground(fgMuted),
443			CursorLine:       base,
444			CursorLineNumber: base.Foreground(fgMuted),
445			Placeholder:      base.Foreground(fgSubtle),
446			Prompt:           base.Foreground(fgMuted),
447		},
448		Cursor: textarea.CursorStyle{
449			Color: secondary,
450			Shape: tea.CursorBlock,
451			Blink: true,
452		},
453	}
454
455	s.Markdown = ansi.StyleConfig{
456		Document: ansi.StyleBlock{
457			StylePrimitive: ansi.StylePrimitive{
458				// BlockPrefix: "\n",
459				// BlockSuffix: "\n",
460				Color: stringPtr(charmtone.Smoke.Hex()),
461			},
462			// Margin: uintPtr(defaultMargin),
463		},
464		BlockQuote: ansi.StyleBlock{
465			StylePrimitive: ansi.StylePrimitive{},
466			Indent:         uintPtr(1),
467			IndentToken:    stringPtr("│ "),
468		},
469		List: ansi.StyleList{
470			LevelIndent: defaultListIndent,
471		},
472		Heading: ansi.StyleBlock{
473			StylePrimitive: ansi.StylePrimitive{
474				BlockSuffix: "\n",
475				Color:       stringPtr(charmtone.Malibu.Hex()),
476				Bold:        boolPtr(true),
477			},
478		},
479		H1: ansi.StyleBlock{
480			StylePrimitive: ansi.StylePrimitive{
481				Prefix:          " ",
482				Suffix:          " ",
483				Color:           stringPtr(charmtone.Zest.Hex()),
484				BackgroundColor: stringPtr(charmtone.Charple.Hex()),
485				Bold:            boolPtr(true),
486			},
487		},
488		H2: ansi.StyleBlock{
489			StylePrimitive: ansi.StylePrimitive{
490				Prefix: "## ",
491			},
492		},
493		H3: ansi.StyleBlock{
494			StylePrimitive: ansi.StylePrimitive{
495				Prefix: "### ",
496			},
497		},
498		H4: ansi.StyleBlock{
499			StylePrimitive: ansi.StylePrimitive{
500				Prefix: "#### ",
501			},
502		},
503		H5: ansi.StyleBlock{
504			StylePrimitive: ansi.StylePrimitive{
505				Prefix: "##### ",
506			},
507		},
508		H6: ansi.StyleBlock{
509			StylePrimitive: ansi.StylePrimitive{
510				Prefix: "###### ",
511				Color:  stringPtr(charmtone.Guac.Hex()),
512				Bold:   boolPtr(false),
513			},
514		},
515		Strikethrough: ansi.StylePrimitive{
516			CrossedOut: boolPtr(true),
517		},
518		Emph: ansi.StylePrimitive{
519			Italic: boolPtr(true),
520		},
521		Strong: ansi.StylePrimitive{
522			Bold: boolPtr(true),
523		},
524		HorizontalRule: ansi.StylePrimitive{
525			Color:  stringPtr(charmtone.Charcoal.Hex()),
526			Format: "\n--------\n",
527		},
528		Item: ansi.StylePrimitive{
529			BlockPrefix: "• ",
530		},
531		Enumeration: ansi.StylePrimitive{
532			BlockPrefix: ". ",
533		},
534		Task: ansi.StyleTask{
535			StylePrimitive: ansi.StylePrimitive{},
536			Ticked:         "[✓] ",
537			Unticked:       "[ ] ",
538		},
539		Link: ansi.StylePrimitive{
540			Color:     stringPtr(charmtone.Zinc.Hex()),
541			Underline: boolPtr(true),
542		},
543		LinkText: ansi.StylePrimitive{
544			Color: stringPtr(charmtone.Guac.Hex()),
545			Bold:  boolPtr(true),
546		},
547		Image: ansi.StylePrimitive{
548			Color:     stringPtr(charmtone.Cheeky.Hex()),
549			Underline: boolPtr(true),
550		},
551		ImageText: ansi.StylePrimitive{
552			Color:  stringPtr(charmtone.Squid.Hex()),
553			Format: "Image: {{.text}} →",
554		},
555		Code: ansi.StyleBlock{
556			StylePrimitive: ansi.StylePrimitive{
557				Prefix:          " ",
558				Suffix:          " ",
559				Color:           stringPtr(charmtone.Coral.Hex()),
560				BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
561			},
562		},
563		CodeBlock: ansi.StyleCodeBlock{
564			StyleBlock: ansi.StyleBlock{
565				StylePrimitive: ansi.StylePrimitive{
566					Color: stringPtr(charmtone.Charcoal.Hex()),
567				},
568				Margin: uintPtr(defaultMargin),
569			},
570			Chroma: &ansi.Chroma{
571				Text: ansi.StylePrimitive{
572					Color: stringPtr(charmtone.Smoke.Hex()),
573				},
574				Error: ansi.StylePrimitive{
575					Color:           stringPtr(charmtone.Butter.Hex()),
576					BackgroundColor: stringPtr(charmtone.Sriracha.Hex()),
577				},
578				Comment: ansi.StylePrimitive{
579					Color: stringPtr(charmtone.Oyster.Hex()),
580				},
581				CommentPreproc: ansi.StylePrimitive{
582					Color: stringPtr(charmtone.Bengal.Hex()),
583				},
584				Keyword: ansi.StylePrimitive{
585					Color: stringPtr(charmtone.Malibu.Hex()),
586				},
587				KeywordReserved: ansi.StylePrimitive{
588					Color: stringPtr(charmtone.Pony.Hex()),
589				},
590				KeywordNamespace: ansi.StylePrimitive{
591					Color: stringPtr(charmtone.Pony.Hex()),
592				},
593				KeywordType: ansi.StylePrimitive{
594					Color: stringPtr(charmtone.Guppy.Hex()),
595				},
596				Operator: ansi.StylePrimitive{
597					Color: stringPtr(charmtone.Salmon.Hex()),
598				},
599				Punctuation: ansi.StylePrimitive{
600					Color: stringPtr(charmtone.Zest.Hex()),
601				},
602				Name: ansi.StylePrimitive{
603					Color: stringPtr(charmtone.Smoke.Hex()),
604				},
605				NameBuiltin: ansi.StylePrimitive{
606					Color: stringPtr(charmtone.Cheeky.Hex()),
607				},
608				NameTag: ansi.StylePrimitive{
609					Color: stringPtr(charmtone.Mauve.Hex()),
610				},
611				NameAttribute: ansi.StylePrimitive{
612					Color: stringPtr(charmtone.Hazy.Hex()),
613				},
614				NameClass: ansi.StylePrimitive{
615					Color:     stringPtr(charmtone.Salt.Hex()),
616					Underline: boolPtr(true),
617					Bold:      boolPtr(true),
618				},
619				NameDecorator: ansi.StylePrimitive{
620					Color: stringPtr(charmtone.Citron.Hex()),
621				},
622				NameFunction: ansi.StylePrimitive{
623					Color: stringPtr(charmtone.Guac.Hex()),
624				},
625				LiteralNumber: ansi.StylePrimitive{
626					Color: stringPtr(charmtone.Julep.Hex()),
627				},
628				LiteralString: ansi.StylePrimitive{
629					Color: stringPtr(charmtone.Cumin.Hex()),
630				},
631				LiteralStringEscape: ansi.StylePrimitive{
632					Color: stringPtr(charmtone.Bok.Hex()),
633				},
634				GenericDeleted: ansi.StylePrimitive{
635					Color: stringPtr(charmtone.Coral.Hex()),
636				},
637				GenericEmph: ansi.StylePrimitive{
638					Italic: boolPtr(true),
639				},
640				GenericInserted: ansi.StylePrimitive{
641					Color: stringPtr(charmtone.Guac.Hex()),
642				},
643				GenericStrong: ansi.StylePrimitive{
644					Bold: boolPtr(true),
645				},
646				GenericSubheading: ansi.StylePrimitive{
647					Color: stringPtr(charmtone.Squid.Hex()),
648				},
649				Background: ansi.StylePrimitive{
650					BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
651				},
652			},
653		},
654		Table: ansi.StyleTable{
655			StyleBlock: ansi.StyleBlock{
656				StylePrimitive: ansi.StylePrimitive{},
657			},
658		},
659		DefinitionDescription: ansi.StylePrimitive{
660			BlockPrefix: "\n ",
661		},
662	}
663
664	s.Help = help.Styles{
665		ShortKey:       base.Foreground(fgMuted),
666		ShortDesc:      base.Foreground(fgSubtle),
667		ShortSeparator: base.Foreground(border),
668		Ellipsis:       base.Foreground(border),
669		FullKey:        base.Foreground(fgMuted),
670		FullDesc:       base.Foreground(fgSubtle),
671		FullSeparator:  base.Foreground(border),
672	}
673
674	s.Diff = diffview.Style{
675		DividerLine: diffview.LineStyle{
676			LineNumber: lipgloss.NewStyle().
677				Foreground(fgHalfMuted).
678				Background(bgBaseLighter),
679			Code: lipgloss.NewStyle().
680				Foreground(fgHalfMuted).
681				Background(bgBaseLighter),
682		},
683		MissingLine: diffview.LineStyle{
684			LineNumber: lipgloss.NewStyle().
685				Background(bgBaseLighter),
686			Code: lipgloss.NewStyle().
687				Background(bgBaseLighter),
688		},
689		EqualLine: diffview.LineStyle{
690			LineNumber: lipgloss.NewStyle().
691				Foreground(fgMuted).
692				Background(bgBase),
693			Code: lipgloss.NewStyle().
694				Foreground(fgMuted).
695				Background(bgBase),
696		},
697		InsertLine: diffview.LineStyle{
698			LineNumber: lipgloss.NewStyle().
699				Foreground(lipgloss.Color("#629657")).
700				Background(lipgloss.Color("#2b322a")),
701			Symbol: lipgloss.NewStyle().
702				Foreground(lipgloss.Color("#629657")).
703				Background(lipgloss.Color("#323931")),
704			Code: lipgloss.NewStyle().
705				Background(lipgloss.Color("#323931")),
706		},
707		DeleteLine: diffview.LineStyle{
708			LineNumber: lipgloss.NewStyle().
709				Foreground(lipgloss.Color("#a45c59")).
710				Background(lipgloss.Color("#312929")),
711			Symbol: lipgloss.NewStyle().
712				Foreground(lipgloss.Color("#a45c59")).
713				Background(lipgloss.Color("#383030")),
714			Code: lipgloss.NewStyle().
715				Background(lipgloss.Color("#383030")),
716		},
717	}
718
719	s.FilePicker = filepicker.Styles{
720		DisabledCursor:   base.Foreground(fgMuted),
721		Cursor:           base.Foreground(fgBase),
722		Symlink:          base.Foreground(fgSubtle),
723		Directory:        base.Foreground(primary),
724		File:             base.Foreground(fgBase),
725		DisabledFile:     base.Foreground(fgMuted),
726		DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
727		Permission:       base.Foreground(fgMuted),
728		Selected:         base.Background(primary).Foreground(fgBase),
729		FileSize:         base.Foreground(fgMuted),
730		EmptyDirectory:   base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
731	}
732
733	// borders
734	s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
735
736	// text presets
737	s.Base = lipgloss.NewStyle().Foreground(fgBase)
738	s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
739	s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
740
741	s.WindowTooSmall = s.Muted
742
743	// tag presets
744	s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
745	s.TagError = s.TagBase.Background(redDark)
746	s.TagInfo = s.TagBase.Background(blueLight)
747
748	// headers
749	s.HeaderTool = lipgloss.NewStyle().Foreground(blue)
750	s.HeaderToolNested = lipgloss.NewStyle().Foreground(fgHalfMuted)
751
752	// panels
753	s.PanelMuted = s.Muted.Background(bgBaseLighter)
754	s.PanelBase = lipgloss.NewStyle().Background(bgBase)
755
756	// code line number
757	s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
758
759	// Tool calls
760	s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
761	s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
762	s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
763	// Cancelled uses muted tone but same glyph as pending
764	s.ToolCallCancelled = s.Muted.SetString(ToolPending)
765	s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
766
767	// Tool rendering styles
768	s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
769	s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
770	s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
771	s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
772
773	s.Tool.NameNormal = base.Foreground(blue)
774	s.Tool.NameNested = base.Foreground(fgHalfMuted)
775
776	s.Tool.ParamMain = s.Subtle
777	s.Tool.ParamKey = s.Subtle
778
779	// Content rendering - prepared styles that accept width parameter
780	s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
781	s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
782	s.Tool.ContentCodeLine = s.Base.Background(bgBaseLighter)
783	s.Tool.ContentCodeBg = bgBase
784	s.Tool.BodyPadding = base.PaddingLeft(2)
785
786	// Deprecated - kept for backward compatibility
787	s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
788	s.Tool.ContentText = s.Muted
789	s.Tool.ContentLineNumber = s.Subtle
790
791	s.Tool.StateWaiting = base.Foreground(fgSubtle)
792	s.Tool.StateCancelled = base.Foreground(fgSubtle)
793
794	s.Tool.ErrorTag = base.Padding(0, 1).Background(red).Foreground(white)
795	s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
796
797	// Diff and multi-edit styles
798	s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
799	s.Tool.NoteTag = base.Padding(0, 1).Background(yellow).Foreground(white)
800	s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
801
802	// Job header styles
803	s.Tool.JobIconPending = base.Foreground(greenDark)
804	s.Tool.JobIconError = base.Foreground(redDark)
805	s.Tool.JobIconSuccess = base.Foreground(green)
806	s.Tool.JobToolName = base.Foreground(blue)
807	s.Tool.JobAction = base.Foreground(fgHalfMuted)
808	s.Tool.JobPID = s.Subtle
809	s.Tool.JobDescription = s.Subtle
810
811	// Agent task styles
812	s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
813	s.Tool.AgentPrompt = s.Muted
814
815	// Buttons
816	s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
817	s.ButtonBlur = s.Base.Background(bgSubtle)
818
819	// Borders
820	s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
821
822	// Editor
823	s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
824	s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
825	s.EditorPromptYoloIconFocused = lipgloss.NewStyle().Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
826	s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
827	s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().Foreground(charmtone.Zest).SetString(":::")
828	s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
829
830	// Logo colors
831	s.LogoFieldColor = primary
832	s.LogoTitleColorA = secondary
833	s.LogoTitleColorB = primary
834	s.LogoCharmColor = secondary
835	s.LogoVersionColor = primary
836
837	// Section
838	s.Section.Title = s.Subtle
839	s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
840
841	// Initialize
842	s.Initialize.Header = s.Base
843	s.Initialize.Content = s.Muted
844	s.Initialize.Accent = s.Base.Foreground(greenDark)
845
846	// LSP and MCP status.
847	s.ItemOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Squid).SetString("●")
848	s.ItemBusyIcon = s.ItemOfflineIcon.Foreground(charmtone.Citron)
849	s.ItemErrorIcon = s.ItemOfflineIcon.Foreground(charmtone.Coral)
850	s.ItemOnlineIcon = s.ItemOfflineIcon.Foreground(charmtone.Guac)
851
852	// LSP
853	s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
854	s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
855	s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
856	s.LSP.InfoDiagnostic = s.Base.Foreground(info)
857
858	// Files
859	s.Files.Path = s.Muted
860	s.Files.Additions = s.Base.Foreground(greenDark)
861	s.Files.Deletions = s.Base.Foreground(redDark)
862
863	// Chat
864	messageFocussedBorder := lipgloss.Border{
865		Left: "▌",
866	}
867
868	s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
869	s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
870		BorderForeground(primary).BorderStyle(normalBorder)
871	s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
872		BorderForeground(primary).BorderStyle(messageFocussedBorder)
873	s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
874	s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
875		BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
876	s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
877	s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
878		Background(red).Foreground(white)
879	s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
880	s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
881
882	// Message item styles
883	s.Chat.Message.Attachment = lipgloss.NewStyle().MarginLeft(1).Background(bgSubtle)
884	s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
885		BorderStyle(messageFocussedBorder).
886		BorderLeft(true).
887		BorderForeground(greenDark)
888	s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
889	s.Chat.Message.ThinkingFooter = s.Base
890	s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
891
892	// Text selection.
893	s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
894
895	// Dialog styles
896	s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
897	s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
898	s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
899	s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
900	s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
901	s.Dialog.Help.ShortSeparator = base.Foreground(border)
902	s.Dialog.Help.Ellipsis = base.Foreground(border)
903	s.Dialog.Help.FullKey = base.Foreground(fgMuted)
904	s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
905	s.Dialog.Help.FullSeparator = base.Foreground(border)
906	s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
907	s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
908	s.Dialog.InputPrompt = base.Padding(0, 1)
909
910	return s
911}
912
913// Helper functions for style pointers
914func boolPtr(b bool) *bool       { return &b }
915func stringPtr(s string) *string { return &s }
916func uintPtr(u uint) *uint       { return &u }
917func chromaStyle(style ansi.StylePrimitive) string {
918	var s string
919
920	if style.Color != nil {
921		s = *style.Color
922	}
923	if style.BackgroundColor != nil {
924		if s != "" {
925			s += " "
926		}
927		s += "bg:" + *style.BackgroundColor
928	}
929	if style.Italic != nil && *style.Italic {
930		if s != "" {
931			s += " "
932		}
933		s += "italic"
934	}
935	if style.Bold != nil && *style.Bold {
936		if s != "" {
937			s += " "
938		}
939		s += "bold"
940	}
941	if style.Underline != nil && *style.Underline {
942		if s != "" {
943			s += " "
944		}
945		s += "underline"
946	}
947
948	return s
949}