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 ArrowRightIcon string = "→"
30
31 ToolPending string = "●"
32 ToolSuccess string = "✓"
33 ToolError string = "×"
34
35 RadioOn string = "◉"
36 RadioOff string = "○"
37
38 BorderThin string = "│"
39 BorderThick string = "▌"
40
41 SectionSeparator string = "─"
42
43 TodoCompletedIcon string = "✓"
44 TodoPendingIcon string = "•"
45 TodoInProgressIcon string = "→"
46)
47
48const (
49 defaultMargin = 2
50 defaultListIndent = 2
51)
52
53type Styles struct {
54 WindowTooSmall lipgloss.Style
55
56 // Reusable text styles
57 Base lipgloss.Style
58 Muted lipgloss.Style
59 HalfMuted lipgloss.Style
60 Subtle lipgloss.Style
61
62 // Tags
63 TagBase lipgloss.Style
64 TagError lipgloss.Style
65 TagInfo lipgloss.Style
66
67 // Headers
68 HeaderTool lipgloss.Style
69 HeaderToolNested lipgloss.Style
70
71 // Panels
72 PanelMuted lipgloss.Style
73 PanelBase lipgloss.Style
74
75 // Line numbers for code blocks
76 LineNumber lipgloss.Style
77
78 // Message borders
79 FocusedMessageBorder lipgloss.Border
80
81 // Tool calls
82 ToolCallPending lipgloss.Style
83 ToolCallError lipgloss.Style
84 ToolCallSuccess lipgloss.Style
85 ToolCallCancelled lipgloss.Style
86 EarlyStateMessage lipgloss.Style
87
88 // Text selection
89 TextSelection lipgloss.Style
90
91 // LSP and MCP status indicators
92 ItemOfflineIcon lipgloss.Style
93 ItemBusyIcon lipgloss.Style
94 ItemErrorIcon lipgloss.Style
95 ItemOnlineIcon lipgloss.Style
96
97 // Markdown & Chroma
98 Markdown ansi.StyleConfig
99 PlainMarkdown ansi.StyleConfig
100
101 // Inputs
102 TextInput textinput.Styles
103 TextArea textarea.Styles
104
105 // Help
106 Help help.Styles
107
108 // Diff
109 Diff diffview.Style
110
111 // FilePicker
112 FilePicker filepicker.Styles
113
114 // Buttons
115 ButtonFocus lipgloss.Style
116 ButtonBlur lipgloss.Style
117
118 // Borders
119 BorderFocus lipgloss.Style
120 BorderBlur lipgloss.Style
121
122 // Editor
123 EditorPromptNormalFocused lipgloss.Style
124 EditorPromptNormalBlurred lipgloss.Style
125 EditorPromptYoloIconFocused lipgloss.Style
126 EditorPromptYoloIconBlurred lipgloss.Style
127 EditorPromptYoloDotsFocused lipgloss.Style
128 EditorPromptYoloDotsBlurred lipgloss.Style
129
130 // Radio
131 RadioOn lipgloss.Style
132 RadioOff lipgloss.Style
133
134 // Background
135 Background color.Color
136
137 // Logo
138 LogoFieldColor color.Color
139 LogoTitleColorA color.Color
140 LogoTitleColorB color.Color
141 LogoCharmColor color.Color
142 LogoVersionColor color.Color
143
144 // Colors - semantic colors for tool rendering.
145 Primary color.Color
146 Secondary color.Color
147 Tertiary color.Color
148 BgBase color.Color
149 BgBaseLighter color.Color
150 BgSubtle color.Color
151 BgOverlay color.Color
152 FgBase color.Color
153 FgMuted color.Color
154 FgHalfMuted color.Color
155 FgSubtle color.Color
156 Border color.Color
157 BorderColor color.Color // Border focus color
158 Warning color.Color
159 Info color.Color
160 White color.Color
161 BlueLight color.Color
162 Blue color.Color
163 BlueDark color.Color
164 Green color.Color
165 GreenDark color.Color
166 Red color.Color
167 RedDark color.Color
168 Yellow color.Color
169
170 // Section Title
171 Section struct {
172 Title lipgloss.Style
173 Line lipgloss.Style
174 }
175
176 // Initialize
177 Initialize struct {
178 Header lipgloss.Style
179 Content lipgloss.Style
180 Accent lipgloss.Style
181 }
182
183 // LSP
184 LSP struct {
185 ErrorDiagnostic lipgloss.Style
186 WarningDiagnostic lipgloss.Style
187 HintDiagnostic lipgloss.Style
188 InfoDiagnostic lipgloss.Style
189 }
190
191 // Files
192 Files struct {
193 Path lipgloss.Style
194 Additions lipgloss.Style
195 Deletions lipgloss.Style
196 }
197
198 // Chat
199 Chat struct {
200 // Message item styles
201 Message struct {
202 UserBlurred lipgloss.Style
203 UserFocused lipgloss.Style
204 AssistantBlurred lipgloss.Style
205 AssistantFocused lipgloss.Style
206 NoContent lipgloss.Style
207 Thinking lipgloss.Style
208 ErrorTag lipgloss.Style
209 ErrorTitle lipgloss.Style
210 ErrorDetails lipgloss.Style
211 Attachment lipgloss.Style
212 ToolCallFocused lipgloss.Style
213 ToolCallCompact lipgloss.Style
214 ToolCallBlurred lipgloss.Style
215 SectionHeader lipgloss.Style
216
217 // Thinking section styles
218 ThinkingBox lipgloss.Style // Background for thinking content
219 ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
220 ThinkingFooterTitle lipgloss.Style // "Thought for" text
221 ThinkingFooterDuration lipgloss.Style // Duration value
222 }
223 }
224
225 // Tool - styles for tool call rendering
226 Tool struct {
227 // Icon styles with tool status
228 IconPending lipgloss.Style // Pending operation icon
229 IconSuccess lipgloss.Style // Successful operation icon
230 IconError lipgloss.Style // Error operation icon
231 IconCancelled lipgloss.Style // Cancelled operation icon
232
233 // Tool name styles
234 NameNormal lipgloss.Style // Normal tool name
235 NameNested lipgloss.Style // Nested tool name
236
237 // Parameter list styles
238 ParamMain lipgloss.Style // Main parameter
239 ParamKey lipgloss.Style // Parameter keys
240
241 // Content rendering styles
242 ContentLine lipgloss.Style // Individual content line with background and width
243 ContentTruncation lipgloss.Style // Truncation message "… (N lines)"
244 ContentCodeLine lipgloss.Style // Code line with background and width
245 ContentCodeTruncation lipgloss.Style // Code truncation message with bgBase
246 ContentCodeBg color.Color // Background color for syntax highlighting
247 Body lipgloss.Style // Body content padding (PaddingLeft(2))
248
249 // Deprecated - kept for backward compatibility
250 ContentBg lipgloss.Style // Content background
251 ContentText lipgloss.Style // Content text
252 ContentLineNumber lipgloss.Style // Line numbers in code
253
254 // State message styles
255 StateWaiting lipgloss.Style // "Waiting for tool response..."
256 StateCancelled lipgloss.Style // "Canceled."
257
258 // Error styles
259 ErrorTag lipgloss.Style // ERROR tag
260 ErrorMessage lipgloss.Style // Error message text
261
262 // Diff styles
263 DiffTruncation lipgloss.Style // Diff truncation message with padding
264
265 // Multi-edit note styles
266 NoteTag lipgloss.Style // NOTE tag (yellow background)
267 NoteMessage lipgloss.Style // Note message text
268
269 // Job header styles (for bash jobs)
270 JobIconPending lipgloss.Style // Pending job icon (green dark)
271 JobIconError lipgloss.Style // Error job icon (red dark)
272 JobIconSuccess lipgloss.Style // Success job icon (green)
273 JobToolName lipgloss.Style // Job tool name "Bash" (blue)
274 JobAction lipgloss.Style // Action text (Start, Output, Kill)
275 JobPID lipgloss.Style // PID text
276 JobDescription lipgloss.Style // Description text
277
278 // Agent task styles
279 AgentTaskTag lipgloss.Style // Agent task tag (blue background, bold)
280 AgentPrompt lipgloss.Style // Agent prompt text
281
282 // Agentic fetch styles
283 AgenticFetchPromptTag lipgloss.Style // Agentic fetch prompt tag (green background, bold)
284
285 // Todo styles
286 TodoRatio lipgloss.Style // Todo ratio (e.g., "2/5")
287 TodoCompletedIcon lipgloss.Style // Completed todo icon
288 TodoInProgressIcon lipgloss.Style // In-progress todo icon
289 TodoPendingIcon lipgloss.Style // Pending todo icon
290 }
291
292 // Dialog styles
293 Dialog struct {
294 Title lipgloss.Style
295 // View is the main content area style.
296 View lipgloss.Style
297 // HelpView is the line that contains the help.
298 HelpView lipgloss.Style
299 Help struct {
300 Ellipsis lipgloss.Style
301 ShortKey lipgloss.Style
302 ShortDesc lipgloss.Style
303 ShortSeparator lipgloss.Style
304 FullKey lipgloss.Style
305 FullDesc lipgloss.Style
306 FullSeparator lipgloss.Style
307 }
308 NormalItem lipgloss.Style
309 SelectedItem lipgloss.Style
310 InputPrompt lipgloss.Style
311
312 List lipgloss.Style
313
314 Commands struct{}
315 }
316
317 // Status bar and help
318 Status struct {
319 Help lipgloss.Style
320
321 ErrorIndicator lipgloss.Style
322 WarnIndicator lipgloss.Style
323 InfoIndicator lipgloss.Style
324 UpdateIndicator lipgloss.Style
325 SuccessIndicator lipgloss.Style
326
327 ErrorMessage lipgloss.Style
328 WarnMessage lipgloss.Style
329 InfoMessage lipgloss.Style
330 UpdateMessage lipgloss.Style
331 SuccessMessage lipgloss.Style
332 }
333
334 // Completions popup styles
335 Completions struct {
336 Normal lipgloss.Style
337 Focused lipgloss.Style
338 Match lipgloss.Style
339 }
340}
341
342// ChromaTheme converts the current markdown chroma styles to a chroma
343// StyleEntries map.
344func (s *Styles) ChromaTheme() chroma.StyleEntries {
345 rules := s.Markdown.CodeBlock
346
347 return chroma.StyleEntries{
348 chroma.Text: chromaStyle(rules.Chroma.Text),
349 chroma.Error: chromaStyle(rules.Chroma.Error),
350 chroma.Comment: chromaStyle(rules.Chroma.Comment),
351 chroma.CommentPreproc: chromaStyle(rules.Chroma.CommentPreproc),
352 chroma.Keyword: chromaStyle(rules.Chroma.Keyword),
353 chroma.KeywordReserved: chromaStyle(rules.Chroma.KeywordReserved),
354 chroma.KeywordNamespace: chromaStyle(rules.Chroma.KeywordNamespace),
355 chroma.KeywordType: chromaStyle(rules.Chroma.KeywordType),
356 chroma.Operator: chromaStyle(rules.Chroma.Operator),
357 chroma.Punctuation: chromaStyle(rules.Chroma.Punctuation),
358 chroma.Name: chromaStyle(rules.Chroma.Name),
359 chroma.NameBuiltin: chromaStyle(rules.Chroma.NameBuiltin),
360 chroma.NameTag: chromaStyle(rules.Chroma.NameTag),
361 chroma.NameAttribute: chromaStyle(rules.Chroma.NameAttribute),
362 chroma.NameClass: chromaStyle(rules.Chroma.NameClass),
363 chroma.NameConstant: chromaStyle(rules.Chroma.NameConstant),
364 chroma.NameDecorator: chromaStyle(rules.Chroma.NameDecorator),
365 chroma.NameException: chromaStyle(rules.Chroma.NameException),
366 chroma.NameFunction: chromaStyle(rules.Chroma.NameFunction),
367 chroma.NameOther: chromaStyle(rules.Chroma.NameOther),
368 chroma.Literal: chromaStyle(rules.Chroma.Literal),
369 chroma.LiteralNumber: chromaStyle(rules.Chroma.LiteralNumber),
370 chroma.LiteralDate: chromaStyle(rules.Chroma.LiteralDate),
371 chroma.LiteralString: chromaStyle(rules.Chroma.LiteralString),
372 chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
373 chroma.GenericDeleted: chromaStyle(rules.Chroma.GenericDeleted),
374 chroma.GenericEmph: chromaStyle(rules.Chroma.GenericEmph),
375 chroma.GenericInserted: chromaStyle(rules.Chroma.GenericInserted),
376 chroma.GenericStrong: chromaStyle(rules.Chroma.GenericStrong),
377 chroma.GenericSubheading: chromaStyle(rules.Chroma.GenericSubheading),
378 chroma.Background: chromaStyle(rules.Chroma.Background),
379 }
380}
381
382// DialogHelpStyles returns the styles for dialog help.
383func (s *Styles) DialogHelpStyles() help.Styles {
384 return help.Styles(s.Dialog.Help)
385}
386
387// DefaultStyles returns the default styles for the UI.
388func DefaultStyles() Styles {
389 var (
390 primary = charmtone.Charple
391 secondary = charmtone.Dolly
392 tertiary = charmtone.Bok
393 // accent = charmtone.Zest
394
395 // Backgrounds
396 bgBase = charmtone.Pepper
397 bgBaseLighter = charmtone.BBQ
398 bgSubtle = charmtone.Charcoal
399 bgOverlay = charmtone.Iron
400
401 // Foregrounds
402 fgBase = charmtone.Ash
403 fgMuted = charmtone.Squid
404 fgHalfMuted = charmtone.Smoke
405 fgSubtle = charmtone.Oyster
406 // fgSelected = charmtone.Salt
407
408 // Borders
409 border = charmtone.Charcoal
410 borderFocus = charmtone.Charple
411
412 // Status
413 warning = charmtone.Zest
414 info = charmtone.Malibu
415
416 // Colors
417 white = charmtone.Butter
418
419 blueLight = charmtone.Sardine
420 blue = charmtone.Malibu
421 blueDark = charmtone.Damson
422
423 // yellow = charmtone.Mustard
424 yellow = charmtone.Mustard
425 // citron = charmtone.Citron
426
427 green = charmtone.Julep
428 greenDark = charmtone.Guac
429 // greenLight = charmtone.Bok
430
431 red = charmtone.Coral
432 redDark = charmtone.Sriracha
433 // redLight = charmtone.Salmon
434 // cherry = charmtone.Cherry
435 )
436
437 normalBorder := lipgloss.NormalBorder()
438
439 base := lipgloss.NewStyle().Foreground(fgBase)
440
441 s := Styles{}
442
443 s.Background = bgBase
444
445 // Populate color fields
446 s.Primary = primary
447 s.Secondary = secondary
448 s.Tertiary = tertiary
449 s.BgBase = bgBase
450 s.BgBaseLighter = bgBaseLighter
451 s.BgSubtle = bgSubtle
452 s.BgOverlay = bgOverlay
453 s.FgBase = fgBase
454 s.FgMuted = fgMuted
455 s.FgHalfMuted = fgHalfMuted
456 s.FgSubtle = fgSubtle
457 s.Border = border
458 s.BorderColor = borderFocus
459 s.Warning = warning
460 s.Info = info
461 s.White = white
462 s.BlueLight = blueLight
463 s.Blue = blue
464 s.BlueDark = blueDark
465 s.Green = green
466 s.GreenDark = greenDark
467 s.Red = red
468 s.RedDark = redDark
469 s.Yellow = yellow
470
471 s.TextInput = textinput.Styles{
472 Focused: textinput.StyleState{
473 Text: base,
474 Placeholder: base.Foreground(fgSubtle),
475 Prompt: base.Foreground(tertiary),
476 Suggestion: base.Foreground(fgSubtle),
477 },
478 Blurred: textinput.StyleState{
479 Text: base.Foreground(fgMuted),
480 Placeholder: base.Foreground(fgSubtle),
481 Prompt: base.Foreground(fgMuted),
482 Suggestion: base.Foreground(fgSubtle),
483 },
484 Cursor: textinput.CursorStyle{
485 Color: secondary,
486 Shape: tea.CursorBlock,
487 Blink: true,
488 },
489 }
490
491 s.TextArea = textarea.Styles{
492 Focused: textarea.StyleState{
493 Base: base,
494 Text: base,
495 LineNumber: base.Foreground(fgSubtle),
496 CursorLine: base,
497 CursorLineNumber: base.Foreground(fgSubtle),
498 Placeholder: base.Foreground(fgSubtle),
499 Prompt: base.Foreground(tertiary),
500 },
501 Blurred: textarea.StyleState{
502 Base: base,
503 Text: base.Foreground(fgMuted),
504 LineNumber: base.Foreground(fgMuted),
505 CursorLine: base,
506 CursorLineNumber: base.Foreground(fgMuted),
507 Placeholder: base.Foreground(fgSubtle),
508 Prompt: base.Foreground(fgMuted),
509 },
510 Cursor: textarea.CursorStyle{
511 Color: secondary,
512 Shape: tea.CursorBlock,
513 Blink: true,
514 },
515 }
516
517 s.Markdown = ansi.StyleConfig{
518 Document: ansi.StyleBlock{
519 StylePrimitive: ansi.StylePrimitive{
520 // BlockPrefix: "\n",
521 // BlockSuffix: "\n",
522 Color: stringPtr(charmtone.Smoke.Hex()),
523 },
524 // Margin: uintPtr(defaultMargin),
525 },
526 BlockQuote: ansi.StyleBlock{
527 StylePrimitive: ansi.StylePrimitive{},
528 Indent: uintPtr(1),
529 IndentToken: stringPtr("│ "),
530 },
531 List: ansi.StyleList{
532 LevelIndent: defaultListIndent,
533 },
534 Heading: ansi.StyleBlock{
535 StylePrimitive: ansi.StylePrimitive{
536 BlockSuffix: "\n",
537 Color: stringPtr(charmtone.Malibu.Hex()),
538 Bold: boolPtr(true),
539 },
540 },
541 H1: ansi.StyleBlock{
542 StylePrimitive: ansi.StylePrimitive{
543 Prefix: " ",
544 Suffix: " ",
545 Color: stringPtr(charmtone.Zest.Hex()),
546 BackgroundColor: stringPtr(charmtone.Charple.Hex()),
547 Bold: boolPtr(true),
548 },
549 },
550 H2: ansi.StyleBlock{
551 StylePrimitive: ansi.StylePrimitive{
552 Prefix: "## ",
553 },
554 },
555 H3: ansi.StyleBlock{
556 StylePrimitive: ansi.StylePrimitive{
557 Prefix: "### ",
558 },
559 },
560 H4: ansi.StyleBlock{
561 StylePrimitive: ansi.StylePrimitive{
562 Prefix: "#### ",
563 },
564 },
565 H5: ansi.StyleBlock{
566 StylePrimitive: ansi.StylePrimitive{
567 Prefix: "##### ",
568 },
569 },
570 H6: ansi.StyleBlock{
571 StylePrimitive: ansi.StylePrimitive{
572 Prefix: "###### ",
573 Color: stringPtr(charmtone.Guac.Hex()),
574 Bold: boolPtr(false),
575 },
576 },
577 Strikethrough: ansi.StylePrimitive{
578 CrossedOut: boolPtr(true),
579 },
580 Emph: ansi.StylePrimitive{
581 Italic: boolPtr(true),
582 },
583 Strong: ansi.StylePrimitive{
584 Bold: boolPtr(true),
585 },
586 HorizontalRule: ansi.StylePrimitive{
587 Color: stringPtr(charmtone.Charcoal.Hex()),
588 Format: "\n--------\n",
589 },
590 Item: ansi.StylePrimitive{
591 BlockPrefix: "• ",
592 },
593 Enumeration: ansi.StylePrimitive{
594 BlockPrefix: ". ",
595 },
596 Task: ansi.StyleTask{
597 StylePrimitive: ansi.StylePrimitive{},
598 Ticked: "[✓] ",
599 Unticked: "[ ] ",
600 },
601 Link: ansi.StylePrimitive{
602 Color: stringPtr(charmtone.Zinc.Hex()),
603 Underline: boolPtr(true),
604 },
605 LinkText: ansi.StylePrimitive{
606 Color: stringPtr(charmtone.Guac.Hex()),
607 Bold: boolPtr(true),
608 },
609 Image: ansi.StylePrimitive{
610 Color: stringPtr(charmtone.Cheeky.Hex()),
611 Underline: boolPtr(true),
612 },
613 ImageText: ansi.StylePrimitive{
614 Color: stringPtr(charmtone.Squid.Hex()),
615 Format: "Image: {{.text}} →",
616 },
617 Code: ansi.StyleBlock{
618 StylePrimitive: ansi.StylePrimitive{
619 Prefix: " ",
620 Suffix: " ",
621 Color: stringPtr(charmtone.Coral.Hex()),
622 BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
623 },
624 },
625 CodeBlock: ansi.StyleCodeBlock{
626 StyleBlock: ansi.StyleBlock{
627 StylePrimitive: ansi.StylePrimitive{
628 Color: stringPtr(charmtone.Charcoal.Hex()),
629 },
630 Margin: uintPtr(defaultMargin),
631 },
632 Chroma: &ansi.Chroma{
633 Text: ansi.StylePrimitive{
634 Color: stringPtr(charmtone.Smoke.Hex()),
635 },
636 Error: ansi.StylePrimitive{
637 Color: stringPtr(charmtone.Butter.Hex()),
638 BackgroundColor: stringPtr(charmtone.Sriracha.Hex()),
639 },
640 Comment: ansi.StylePrimitive{
641 Color: stringPtr(charmtone.Oyster.Hex()),
642 },
643 CommentPreproc: ansi.StylePrimitive{
644 Color: stringPtr(charmtone.Bengal.Hex()),
645 },
646 Keyword: ansi.StylePrimitive{
647 Color: stringPtr(charmtone.Malibu.Hex()),
648 },
649 KeywordReserved: ansi.StylePrimitive{
650 Color: stringPtr(charmtone.Pony.Hex()),
651 },
652 KeywordNamespace: ansi.StylePrimitive{
653 Color: stringPtr(charmtone.Pony.Hex()),
654 },
655 KeywordType: ansi.StylePrimitive{
656 Color: stringPtr(charmtone.Guppy.Hex()),
657 },
658 Operator: ansi.StylePrimitive{
659 Color: stringPtr(charmtone.Salmon.Hex()),
660 },
661 Punctuation: ansi.StylePrimitive{
662 Color: stringPtr(charmtone.Zest.Hex()),
663 },
664 Name: ansi.StylePrimitive{
665 Color: stringPtr(charmtone.Smoke.Hex()),
666 },
667 NameBuiltin: ansi.StylePrimitive{
668 Color: stringPtr(charmtone.Cheeky.Hex()),
669 },
670 NameTag: ansi.StylePrimitive{
671 Color: stringPtr(charmtone.Mauve.Hex()),
672 },
673 NameAttribute: ansi.StylePrimitive{
674 Color: stringPtr(charmtone.Hazy.Hex()),
675 },
676 NameClass: ansi.StylePrimitive{
677 Color: stringPtr(charmtone.Salt.Hex()),
678 Underline: boolPtr(true),
679 Bold: boolPtr(true),
680 },
681 NameDecorator: ansi.StylePrimitive{
682 Color: stringPtr(charmtone.Citron.Hex()),
683 },
684 NameFunction: ansi.StylePrimitive{
685 Color: stringPtr(charmtone.Guac.Hex()),
686 },
687 LiteralNumber: ansi.StylePrimitive{
688 Color: stringPtr(charmtone.Julep.Hex()),
689 },
690 LiteralString: ansi.StylePrimitive{
691 Color: stringPtr(charmtone.Cumin.Hex()),
692 },
693 LiteralStringEscape: ansi.StylePrimitive{
694 Color: stringPtr(charmtone.Bok.Hex()),
695 },
696 GenericDeleted: ansi.StylePrimitive{
697 Color: stringPtr(charmtone.Coral.Hex()),
698 },
699 GenericEmph: ansi.StylePrimitive{
700 Italic: boolPtr(true),
701 },
702 GenericInserted: ansi.StylePrimitive{
703 Color: stringPtr(charmtone.Guac.Hex()),
704 },
705 GenericStrong: ansi.StylePrimitive{
706 Bold: boolPtr(true),
707 },
708 GenericSubheading: ansi.StylePrimitive{
709 Color: stringPtr(charmtone.Squid.Hex()),
710 },
711 Background: ansi.StylePrimitive{
712 BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
713 },
714 },
715 },
716 Table: ansi.StyleTable{
717 StyleBlock: ansi.StyleBlock{
718 StylePrimitive: ansi.StylePrimitive{},
719 },
720 },
721 DefinitionDescription: ansi.StylePrimitive{
722 BlockPrefix: "\n ",
723 },
724 }
725
726 // PlainMarkdown style - muted colors on subtle background for thinking content.
727 plainBg := stringPtr(bgBaseLighter.Hex())
728 plainFg := stringPtr(fgMuted.Hex())
729 s.PlainMarkdown = ansi.StyleConfig{
730 Document: ansi.StyleBlock{
731 StylePrimitive: ansi.StylePrimitive{
732 Color: plainFg,
733 BackgroundColor: plainBg,
734 },
735 },
736 BlockQuote: ansi.StyleBlock{
737 StylePrimitive: ansi.StylePrimitive{
738 Color: plainFg,
739 BackgroundColor: plainBg,
740 },
741 Indent: uintPtr(1),
742 IndentToken: stringPtr("│ "),
743 },
744 List: ansi.StyleList{
745 LevelIndent: defaultListIndent,
746 },
747 Heading: ansi.StyleBlock{
748 StylePrimitive: ansi.StylePrimitive{
749 BlockSuffix: "\n",
750 Bold: boolPtr(true),
751 Color: plainFg,
752 BackgroundColor: plainBg,
753 },
754 },
755 H1: ansi.StyleBlock{
756 StylePrimitive: ansi.StylePrimitive{
757 Prefix: " ",
758 Suffix: " ",
759 Bold: boolPtr(true),
760 Color: plainFg,
761 BackgroundColor: plainBg,
762 },
763 },
764 H2: ansi.StyleBlock{
765 StylePrimitive: ansi.StylePrimitive{
766 Prefix: "## ",
767 Color: plainFg,
768 BackgroundColor: plainBg,
769 },
770 },
771 H3: ansi.StyleBlock{
772 StylePrimitive: ansi.StylePrimitive{
773 Prefix: "### ",
774 Color: plainFg,
775 BackgroundColor: plainBg,
776 },
777 },
778 H4: ansi.StyleBlock{
779 StylePrimitive: ansi.StylePrimitive{
780 Prefix: "#### ",
781 Color: plainFg,
782 BackgroundColor: plainBg,
783 },
784 },
785 H5: ansi.StyleBlock{
786 StylePrimitive: ansi.StylePrimitive{
787 Prefix: "##### ",
788 Color: plainFg,
789 BackgroundColor: plainBg,
790 },
791 },
792 H6: ansi.StyleBlock{
793 StylePrimitive: ansi.StylePrimitive{
794 Prefix: "###### ",
795 Color: plainFg,
796 BackgroundColor: plainBg,
797 },
798 },
799 Strikethrough: ansi.StylePrimitive{
800 CrossedOut: boolPtr(true),
801 Color: plainFg,
802 BackgroundColor: plainBg,
803 },
804 Emph: ansi.StylePrimitive{
805 Italic: boolPtr(true),
806 Color: plainFg,
807 BackgroundColor: plainBg,
808 },
809 Strong: ansi.StylePrimitive{
810 Bold: boolPtr(true),
811 Color: plainFg,
812 BackgroundColor: plainBg,
813 },
814 HorizontalRule: ansi.StylePrimitive{
815 Format: "\n--------\n",
816 Color: plainFg,
817 BackgroundColor: plainBg,
818 },
819 Item: ansi.StylePrimitive{
820 BlockPrefix: "• ",
821 Color: plainFg,
822 BackgroundColor: plainBg,
823 },
824 Enumeration: ansi.StylePrimitive{
825 BlockPrefix: ". ",
826 Color: plainFg,
827 BackgroundColor: plainBg,
828 },
829 Task: ansi.StyleTask{
830 StylePrimitive: ansi.StylePrimitive{
831 Color: plainFg,
832 BackgroundColor: plainBg,
833 },
834 Ticked: "[✓] ",
835 Unticked: "[ ] ",
836 },
837 Link: ansi.StylePrimitive{
838 Underline: boolPtr(true),
839 Color: plainFg,
840 BackgroundColor: plainBg,
841 },
842 LinkText: ansi.StylePrimitive{
843 Bold: boolPtr(true),
844 Color: plainFg,
845 BackgroundColor: plainBg,
846 },
847 Image: ansi.StylePrimitive{
848 Underline: boolPtr(true),
849 Color: plainFg,
850 BackgroundColor: plainBg,
851 },
852 ImageText: ansi.StylePrimitive{
853 Format: "Image: {{.text}} →",
854 Color: plainFg,
855 BackgroundColor: plainBg,
856 },
857 Code: ansi.StyleBlock{
858 StylePrimitive: ansi.StylePrimitive{
859 Prefix: " ",
860 Suffix: " ",
861 Color: plainFg,
862 BackgroundColor: plainBg,
863 },
864 },
865 CodeBlock: ansi.StyleCodeBlock{
866 StyleBlock: ansi.StyleBlock{
867 StylePrimitive: ansi.StylePrimitive{
868 Color: plainFg,
869 BackgroundColor: plainBg,
870 },
871 Margin: uintPtr(defaultMargin),
872 },
873 },
874 Table: ansi.StyleTable{
875 StyleBlock: ansi.StyleBlock{
876 StylePrimitive: ansi.StylePrimitive{
877 Color: plainFg,
878 BackgroundColor: plainBg,
879 },
880 },
881 },
882 DefinitionDescription: ansi.StylePrimitive{
883 BlockPrefix: "\n ",
884 Color: plainFg,
885 BackgroundColor: plainBg,
886 },
887 }
888
889 s.Help = help.Styles{
890 ShortKey: base.Foreground(fgMuted),
891 ShortDesc: base.Foreground(fgSubtle),
892 ShortSeparator: base.Foreground(border),
893 Ellipsis: base.Foreground(border),
894 FullKey: base.Foreground(fgMuted),
895 FullDesc: base.Foreground(fgSubtle),
896 FullSeparator: base.Foreground(border),
897 }
898
899 s.Diff = diffview.Style{
900 DividerLine: diffview.LineStyle{
901 LineNumber: lipgloss.NewStyle().
902 Foreground(fgHalfMuted).
903 Background(bgBaseLighter),
904 Code: lipgloss.NewStyle().
905 Foreground(fgHalfMuted).
906 Background(bgBaseLighter),
907 },
908 MissingLine: diffview.LineStyle{
909 LineNumber: lipgloss.NewStyle().
910 Background(bgBaseLighter),
911 Code: lipgloss.NewStyle().
912 Background(bgBaseLighter),
913 },
914 EqualLine: diffview.LineStyle{
915 LineNumber: lipgloss.NewStyle().
916 Foreground(fgMuted).
917 Background(bgBase),
918 Code: lipgloss.NewStyle().
919 Foreground(fgMuted).
920 Background(bgBase),
921 },
922 InsertLine: diffview.LineStyle{
923 LineNumber: lipgloss.NewStyle().
924 Foreground(lipgloss.Color("#629657")).
925 Background(lipgloss.Color("#2b322a")),
926 Symbol: lipgloss.NewStyle().
927 Foreground(lipgloss.Color("#629657")).
928 Background(lipgloss.Color("#323931")),
929 Code: lipgloss.NewStyle().
930 Background(lipgloss.Color("#323931")),
931 },
932 DeleteLine: diffview.LineStyle{
933 LineNumber: lipgloss.NewStyle().
934 Foreground(lipgloss.Color("#a45c59")).
935 Background(lipgloss.Color("#312929")),
936 Symbol: lipgloss.NewStyle().
937 Foreground(lipgloss.Color("#a45c59")).
938 Background(lipgloss.Color("#383030")),
939 Code: lipgloss.NewStyle().
940 Background(lipgloss.Color("#383030")),
941 },
942 }
943
944 s.FilePicker = filepicker.Styles{
945 DisabledCursor: base.Foreground(fgMuted),
946 Cursor: base.Foreground(fgBase),
947 Symlink: base.Foreground(fgSubtle),
948 Directory: base.Foreground(primary),
949 File: base.Foreground(fgBase),
950 DisabledFile: base.Foreground(fgMuted),
951 DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
952 Permission: base.Foreground(fgMuted),
953 Selected: base.Background(primary).Foreground(fgBase),
954 FileSize: base.Foreground(fgMuted),
955 EmptyDirectory: base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
956 }
957
958 // borders
959 s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
960
961 // text presets
962 s.Base = lipgloss.NewStyle().Foreground(fgBase)
963 s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
964 s.HalfMuted = lipgloss.NewStyle().Foreground(fgHalfMuted)
965 s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
966
967 s.WindowTooSmall = s.Muted
968
969 // tag presets
970 s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
971 s.TagError = s.TagBase.Background(redDark)
972 s.TagInfo = s.TagBase.Background(blueLight)
973
974 // headers
975 s.HeaderTool = lipgloss.NewStyle().Foreground(blue)
976 s.HeaderToolNested = lipgloss.NewStyle().Foreground(fgHalfMuted)
977
978 // panels
979 s.PanelMuted = s.Muted.Background(bgBaseLighter)
980 s.PanelBase = lipgloss.NewStyle().Background(bgBase)
981
982 // code line number
983 s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
984
985 // Tool calls
986 s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
987 s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
988 s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
989 // Cancelled uses muted tone but same glyph as pending
990 s.ToolCallCancelled = s.Muted.SetString(ToolPending)
991 s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
992
993 // Tool rendering styles
994 s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
995 s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
996 s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
997 s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
998
999 s.Tool.NameNormal = base.Foreground(blue)
1000 s.Tool.NameNested = base.Foreground(fgHalfMuted)
1001
1002 s.Tool.ParamMain = s.Subtle
1003 s.Tool.ParamKey = s.Subtle
1004
1005 // Content rendering - prepared styles that accept width parameter
1006 s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
1007 s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
1008 s.Tool.ContentCodeLine = s.Base.Background(bgBase)
1009 s.Tool.ContentCodeTruncation = s.Muted.Background(bgBase).PaddingLeft(2)
1010 s.Tool.ContentCodeBg = bgBase
1011 s.Tool.Body = base.PaddingLeft(2)
1012
1013 // Deprecated - kept for backward compatibility
1014 s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
1015 s.Tool.ContentText = s.Muted
1016 s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
1017
1018 s.Tool.StateWaiting = base.Foreground(fgSubtle)
1019 s.Tool.StateCancelled = base.Foreground(fgSubtle)
1020
1021 s.Tool.ErrorTag = base.Padding(0, 1).Background(red).Foreground(white)
1022 s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
1023
1024 // Diff and multi-edit styles
1025 s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
1026 s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
1027 s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
1028
1029 // Job header styles
1030 s.Tool.JobIconPending = base.Foreground(greenDark)
1031 s.Tool.JobIconError = base.Foreground(redDark)
1032 s.Tool.JobIconSuccess = base.Foreground(green)
1033 s.Tool.JobToolName = base.Foreground(blue)
1034 s.Tool.JobAction = base.Foreground(blueDark)
1035 s.Tool.JobPID = s.Muted
1036 s.Tool.JobDescription = s.Subtle
1037
1038 // Agent task styles
1039 s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
1040 s.Tool.AgentPrompt = s.Muted
1041
1042 // Agentic fetch styles
1043 s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
1044
1045 // Todo styles
1046 s.Tool.TodoRatio = base.Foreground(blueDark)
1047 s.Tool.TodoCompletedIcon = base.Foreground(green)
1048 s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
1049 s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
1050
1051 // Buttons
1052 s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
1053 s.ButtonBlur = s.Base.Background(bgSubtle)
1054
1055 // Borders
1056 s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
1057
1058 // Editor
1059 s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1060 s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
1061 s.EditorPromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1062 s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1063 s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1064 s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
1065
1066 s.RadioOn = s.HalfMuted.SetString(RadioOn)
1067 s.RadioOff = s.HalfMuted.SetString(RadioOff)
1068
1069 // Logo colors
1070 s.LogoFieldColor = primary
1071 s.LogoTitleColorA = secondary
1072 s.LogoTitleColorB = primary
1073 s.LogoCharmColor = secondary
1074 s.LogoVersionColor = primary
1075
1076 // Section
1077 s.Section.Title = s.Subtle
1078 s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
1079
1080 // Initialize
1081 s.Initialize.Header = s.Base
1082 s.Initialize.Content = s.Muted
1083 s.Initialize.Accent = s.Base.Foreground(greenDark)
1084
1085 // LSP and MCP status.
1086 s.ItemOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Squid).SetString("●")
1087 s.ItemBusyIcon = s.ItemOfflineIcon.Foreground(charmtone.Citron)
1088 s.ItemErrorIcon = s.ItemOfflineIcon.Foreground(charmtone.Coral)
1089 s.ItemOnlineIcon = s.ItemOfflineIcon.Foreground(charmtone.Guac)
1090
1091 // LSP
1092 s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
1093 s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
1094 s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
1095 s.LSP.InfoDiagnostic = s.Base.Foreground(info)
1096
1097 // Files
1098 s.Files.Path = s.Muted
1099 s.Files.Additions = s.Base.Foreground(greenDark)
1100 s.Files.Deletions = s.Base.Foreground(redDark)
1101
1102 // Chat
1103 messageFocussedBorder := lipgloss.Border{
1104 Left: "▌",
1105 }
1106
1107 s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1108 s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1109 BorderForeground(primary).BorderStyle(normalBorder)
1110 s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1111 BorderForeground(primary).BorderStyle(messageFocussedBorder)
1112 s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
1113 s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1114 BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1115 s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
1116 s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1117 Background(red).Foreground(white)
1118 s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1119 s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1120
1121 // Message item styles
1122 s.Chat.Message.Attachment = lipgloss.NewStyle().MarginLeft(1).Background(bgSubtle)
1123 s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
1124 BorderStyle(messageFocussedBorder).
1125 BorderLeft(true).
1126 BorderForeground(greenDark)
1127 s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
1128 // No padding or border for compact tool calls within messages
1129 s.Chat.Message.ToolCallCompact = s.Muted
1130 s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
1131
1132 // Thinking section styles
1133 s.Chat.Message.ThinkingBox = s.Subtle.Background(bgBaseLighter)
1134 s.Chat.Message.ThinkingTruncationHint = s.Muted
1135 s.Chat.Message.ThinkingFooterTitle = s.Muted
1136 s.Chat.Message.ThinkingFooterDuration = s.Subtle
1137
1138 // Text selection.
1139 s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
1140
1141 // Dialog styles
1142 s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
1143 s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1144 s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
1145 s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
1146 s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
1147 s.Dialog.Help.ShortSeparator = base.Foreground(border)
1148 s.Dialog.Help.Ellipsis = base.Foreground(border)
1149 s.Dialog.Help.FullKey = base.Foreground(fgMuted)
1150 s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
1151 s.Dialog.Help.FullSeparator = base.Foreground(border)
1152 s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
1153 s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
1154 s.Dialog.InputPrompt = base.Margin(1, 1)
1155
1156 s.Dialog.List = base.Margin(0, 0, 1, 0)
1157
1158 s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
1159 s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
1160 s.Status.InfoIndicator = s.Status.SuccessIndicator
1161 s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
1162 s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(bgOverlay).Background(yellow).SetString("WARNING")
1163 s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(bgBase).Background(red).SetString("ERROR")
1164 s.Status.SuccessMessage = base.Foreground(bgSubtle).Background(greenDark).Padding(0, 1)
1165 s.Status.InfoMessage = s.Status.SuccessMessage
1166 s.Status.UpdateMessage = s.Status.SuccessMessage
1167 s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(bgOverlay).Background(warning)
1168 s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(white).Background(redDark)
1169
1170 // Completions styles
1171 s.Completions.Normal = base.Background(bgSubtle).Foreground(fgBase)
1172 s.Completions.Focused = base.Background(primary).Foreground(white)
1173 s.Completions.Match = base.Underline(true)
1174
1175 return s
1176}
1177
1178// Helper functions for style pointers
1179func boolPtr(b bool) *bool { return &b }
1180func stringPtr(s string) *string { return &s }
1181func uintPtr(u uint) *uint { return &u }
1182func chromaStyle(style ansi.StylePrimitive) string {
1183 var s string
1184
1185 if style.Color != nil {
1186 s = *style.Color
1187 }
1188 if style.BackgroundColor != nil {
1189 if s != "" {
1190 s += " "
1191 }
1192 s += "bg:" + *style.BackgroundColor
1193 }
1194 if style.Italic != nil && *style.Italic {
1195 if s != "" {
1196 s += " "
1197 }
1198 s += "italic"
1199 }
1200 if style.Bold != nil && *style.Bold {
1201 if s != "" {
1202 s += " "
1203 }
1204 s += "bold"
1205 }
1206 if style.Underline != nil && *style.Underline {
1207 if s != "" {
1208 s += " "
1209 }
1210 s += "underline"
1211 }
1212
1213 return s
1214}