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
335// ChromaTheme converts the current markdown chroma styles to a chroma
336// StyleEntries map.
337func (s *Styles) ChromaTheme() chroma.StyleEntries {
338 rules := s.Markdown.CodeBlock
339
340 return chroma.StyleEntries{
341 chroma.Text: chromaStyle(rules.Chroma.Text),
342 chroma.Error: chromaStyle(rules.Chroma.Error),
343 chroma.Comment: chromaStyle(rules.Chroma.Comment),
344 chroma.CommentPreproc: chromaStyle(rules.Chroma.CommentPreproc),
345 chroma.Keyword: chromaStyle(rules.Chroma.Keyword),
346 chroma.KeywordReserved: chromaStyle(rules.Chroma.KeywordReserved),
347 chroma.KeywordNamespace: chromaStyle(rules.Chroma.KeywordNamespace),
348 chroma.KeywordType: chromaStyle(rules.Chroma.KeywordType),
349 chroma.Operator: chromaStyle(rules.Chroma.Operator),
350 chroma.Punctuation: chromaStyle(rules.Chroma.Punctuation),
351 chroma.Name: chromaStyle(rules.Chroma.Name),
352 chroma.NameBuiltin: chromaStyle(rules.Chroma.NameBuiltin),
353 chroma.NameTag: chromaStyle(rules.Chroma.NameTag),
354 chroma.NameAttribute: chromaStyle(rules.Chroma.NameAttribute),
355 chroma.NameClass: chromaStyle(rules.Chroma.NameClass),
356 chroma.NameConstant: chromaStyle(rules.Chroma.NameConstant),
357 chroma.NameDecorator: chromaStyle(rules.Chroma.NameDecorator),
358 chroma.NameException: chromaStyle(rules.Chroma.NameException),
359 chroma.NameFunction: chromaStyle(rules.Chroma.NameFunction),
360 chroma.NameOther: chromaStyle(rules.Chroma.NameOther),
361 chroma.Literal: chromaStyle(rules.Chroma.Literal),
362 chroma.LiteralNumber: chromaStyle(rules.Chroma.LiteralNumber),
363 chroma.LiteralDate: chromaStyle(rules.Chroma.LiteralDate),
364 chroma.LiteralString: chromaStyle(rules.Chroma.LiteralString),
365 chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
366 chroma.GenericDeleted: chromaStyle(rules.Chroma.GenericDeleted),
367 chroma.GenericEmph: chromaStyle(rules.Chroma.GenericEmph),
368 chroma.GenericInserted: chromaStyle(rules.Chroma.GenericInserted),
369 chroma.GenericStrong: chromaStyle(rules.Chroma.GenericStrong),
370 chroma.GenericSubheading: chromaStyle(rules.Chroma.GenericSubheading),
371 chroma.Background: chromaStyle(rules.Chroma.Background),
372 }
373}
374
375// DialogHelpStyles returns the styles for dialog help.
376func (s *Styles) DialogHelpStyles() help.Styles {
377 return help.Styles(s.Dialog.Help)
378}
379
380// DefaultStyles returns the default styles for the UI.
381func DefaultStyles() Styles {
382 var (
383 primary = charmtone.Charple
384 secondary = charmtone.Dolly
385 tertiary = charmtone.Bok
386 // accent = charmtone.Zest
387
388 // Backgrounds
389 bgBase = charmtone.Pepper
390 bgBaseLighter = charmtone.BBQ
391 bgSubtle = charmtone.Charcoal
392 bgOverlay = charmtone.Iron
393
394 // Foregrounds
395 fgBase = charmtone.Ash
396 fgMuted = charmtone.Squid
397 fgHalfMuted = charmtone.Smoke
398 fgSubtle = charmtone.Oyster
399 // fgSelected = charmtone.Salt
400
401 // Borders
402 border = charmtone.Charcoal
403 borderFocus = charmtone.Charple
404
405 // Status
406 warning = charmtone.Zest
407 info = charmtone.Malibu
408
409 // Colors
410 white = charmtone.Butter
411
412 blueLight = charmtone.Sardine
413 blue = charmtone.Malibu
414 blueDark = charmtone.Damson
415
416 // yellow = charmtone.Mustard
417 yellow = charmtone.Mustard
418 // citron = charmtone.Citron
419
420 green = charmtone.Julep
421 greenDark = charmtone.Guac
422 // greenLight = charmtone.Bok
423
424 red = charmtone.Coral
425 redDark = charmtone.Sriracha
426 // redLight = charmtone.Salmon
427 // cherry = charmtone.Cherry
428 )
429
430 normalBorder := lipgloss.NormalBorder()
431
432 base := lipgloss.NewStyle().Foreground(fgBase)
433
434 s := Styles{}
435
436 s.Background = bgBase
437
438 // Populate color fields
439 s.Primary = primary
440 s.Secondary = secondary
441 s.Tertiary = tertiary
442 s.BgBase = bgBase
443 s.BgBaseLighter = bgBaseLighter
444 s.BgSubtle = bgSubtle
445 s.BgOverlay = bgOverlay
446 s.FgBase = fgBase
447 s.FgMuted = fgMuted
448 s.FgHalfMuted = fgHalfMuted
449 s.FgSubtle = fgSubtle
450 s.Border = border
451 s.BorderColor = borderFocus
452 s.Warning = warning
453 s.Info = info
454 s.White = white
455 s.BlueLight = blueLight
456 s.Blue = blue
457 s.BlueDark = blueDark
458 s.Green = green
459 s.GreenDark = greenDark
460 s.Red = red
461 s.RedDark = redDark
462 s.Yellow = yellow
463
464 s.TextInput = textinput.Styles{
465 Focused: textinput.StyleState{
466 Text: base,
467 Placeholder: base.Foreground(fgSubtle),
468 Prompt: base.Foreground(tertiary),
469 Suggestion: base.Foreground(fgSubtle),
470 },
471 Blurred: textinput.StyleState{
472 Text: base.Foreground(fgMuted),
473 Placeholder: base.Foreground(fgSubtle),
474 Prompt: base.Foreground(fgMuted),
475 Suggestion: base.Foreground(fgSubtle),
476 },
477 Cursor: textinput.CursorStyle{
478 Color: secondary,
479 Shape: tea.CursorBlock,
480 Blink: true,
481 },
482 }
483
484 s.TextArea = textarea.Styles{
485 Focused: textarea.StyleState{
486 Base: base,
487 Text: base,
488 LineNumber: base.Foreground(fgSubtle),
489 CursorLine: base,
490 CursorLineNumber: base.Foreground(fgSubtle),
491 Placeholder: base.Foreground(fgSubtle),
492 Prompt: base.Foreground(tertiary),
493 },
494 Blurred: textarea.StyleState{
495 Base: base,
496 Text: base.Foreground(fgMuted),
497 LineNumber: base.Foreground(fgMuted),
498 CursorLine: base,
499 CursorLineNumber: base.Foreground(fgMuted),
500 Placeholder: base.Foreground(fgSubtle),
501 Prompt: base.Foreground(fgMuted),
502 },
503 Cursor: textarea.CursorStyle{
504 Color: secondary,
505 Shape: tea.CursorBlock,
506 Blink: true,
507 },
508 }
509
510 s.Markdown = ansi.StyleConfig{
511 Document: ansi.StyleBlock{
512 StylePrimitive: ansi.StylePrimitive{
513 // BlockPrefix: "\n",
514 // BlockSuffix: "\n",
515 Color: stringPtr(charmtone.Smoke.Hex()),
516 },
517 // Margin: uintPtr(defaultMargin),
518 },
519 BlockQuote: ansi.StyleBlock{
520 StylePrimitive: ansi.StylePrimitive{},
521 Indent: uintPtr(1),
522 IndentToken: stringPtr("│ "),
523 },
524 List: ansi.StyleList{
525 LevelIndent: defaultListIndent,
526 },
527 Heading: ansi.StyleBlock{
528 StylePrimitive: ansi.StylePrimitive{
529 BlockSuffix: "\n",
530 Color: stringPtr(charmtone.Malibu.Hex()),
531 Bold: boolPtr(true),
532 },
533 },
534 H1: ansi.StyleBlock{
535 StylePrimitive: ansi.StylePrimitive{
536 Prefix: " ",
537 Suffix: " ",
538 Color: stringPtr(charmtone.Zest.Hex()),
539 BackgroundColor: stringPtr(charmtone.Charple.Hex()),
540 Bold: boolPtr(true),
541 },
542 },
543 H2: ansi.StyleBlock{
544 StylePrimitive: ansi.StylePrimitive{
545 Prefix: "## ",
546 },
547 },
548 H3: ansi.StyleBlock{
549 StylePrimitive: ansi.StylePrimitive{
550 Prefix: "### ",
551 },
552 },
553 H4: ansi.StyleBlock{
554 StylePrimitive: ansi.StylePrimitive{
555 Prefix: "#### ",
556 },
557 },
558 H5: ansi.StyleBlock{
559 StylePrimitive: ansi.StylePrimitive{
560 Prefix: "##### ",
561 },
562 },
563 H6: ansi.StyleBlock{
564 StylePrimitive: ansi.StylePrimitive{
565 Prefix: "###### ",
566 Color: stringPtr(charmtone.Guac.Hex()),
567 Bold: boolPtr(false),
568 },
569 },
570 Strikethrough: ansi.StylePrimitive{
571 CrossedOut: boolPtr(true),
572 },
573 Emph: ansi.StylePrimitive{
574 Italic: boolPtr(true),
575 },
576 Strong: ansi.StylePrimitive{
577 Bold: boolPtr(true),
578 },
579 HorizontalRule: ansi.StylePrimitive{
580 Color: stringPtr(charmtone.Charcoal.Hex()),
581 Format: "\n--------\n",
582 },
583 Item: ansi.StylePrimitive{
584 BlockPrefix: "• ",
585 },
586 Enumeration: ansi.StylePrimitive{
587 BlockPrefix: ". ",
588 },
589 Task: ansi.StyleTask{
590 StylePrimitive: ansi.StylePrimitive{},
591 Ticked: "[✓] ",
592 Unticked: "[ ] ",
593 },
594 Link: ansi.StylePrimitive{
595 Color: stringPtr(charmtone.Zinc.Hex()),
596 Underline: boolPtr(true),
597 },
598 LinkText: ansi.StylePrimitive{
599 Color: stringPtr(charmtone.Guac.Hex()),
600 Bold: boolPtr(true),
601 },
602 Image: ansi.StylePrimitive{
603 Color: stringPtr(charmtone.Cheeky.Hex()),
604 Underline: boolPtr(true),
605 },
606 ImageText: ansi.StylePrimitive{
607 Color: stringPtr(charmtone.Squid.Hex()),
608 Format: "Image: {{.text}} →",
609 },
610 Code: ansi.StyleBlock{
611 StylePrimitive: ansi.StylePrimitive{
612 Prefix: " ",
613 Suffix: " ",
614 Color: stringPtr(charmtone.Coral.Hex()),
615 BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
616 },
617 },
618 CodeBlock: ansi.StyleCodeBlock{
619 StyleBlock: ansi.StyleBlock{
620 StylePrimitive: ansi.StylePrimitive{
621 Color: stringPtr(charmtone.Charcoal.Hex()),
622 },
623 Margin: uintPtr(defaultMargin),
624 },
625 Chroma: &ansi.Chroma{
626 Text: ansi.StylePrimitive{
627 Color: stringPtr(charmtone.Smoke.Hex()),
628 },
629 Error: ansi.StylePrimitive{
630 Color: stringPtr(charmtone.Butter.Hex()),
631 BackgroundColor: stringPtr(charmtone.Sriracha.Hex()),
632 },
633 Comment: ansi.StylePrimitive{
634 Color: stringPtr(charmtone.Oyster.Hex()),
635 },
636 CommentPreproc: ansi.StylePrimitive{
637 Color: stringPtr(charmtone.Bengal.Hex()),
638 },
639 Keyword: ansi.StylePrimitive{
640 Color: stringPtr(charmtone.Malibu.Hex()),
641 },
642 KeywordReserved: ansi.StylePrimitive{
643 Color: stringPtr(charmtone.Pony.Hex()),
644 },
645 KeywordNamespace: ansi.StylePrimitive{
646 Color: stringPtr(charmtone.Pony.Hex()),
647 },
648 KeywordType: ansi.StylePrimitive{
649 Color: stringPtr(charmtone.Guppy.Hex()),
650 },
651 Operator: ansi.StylePrimitive{
652 Color: stringPtr(charmtone.Salmon.Hex()),
653 },
654 Punctuation: ansi.StylePrimitive{
655 Color: stringPtr(charmtone.Zest.Hex()),
656 },
657 Name: ansi.StylePrimitive{
658 Color: stringPtr(charmtone.Smoke.Hex()),
659 },
660 NameBuiltin: ansi.StylePrimitive{
661 Color: stringPtr(charmtone.Cheeky.Hex()),
662 },
663 NameTag: ansi.StylePrimitive{
664 Color: stringPtr(charmtone.Mauve.Hex()),
665 },
666 NameAttribute: ansi.StylePrimitive{
667 Color: stringPtr(charmtone.Hazy.Hex()),
668 },
669 NameClass: ansi.StylePrimitive{
670 Color: stringPtr(charmtone.Salt.Hex()),
671 Underline: boolPtr(true),
672 Bold: boolPtr(true),
673 },
674 NameDecorator: ansi.StylePrimitive{
675 Color: stringPtr(charmtone.Citron.Hex()),
676 },
677 NameFunction: ansi.StylePrimitive{
678 Color: stringPtr(charmtone.Guac.Hex()),
679 },
680 LiteralNumber: ansi.StylePrimitive{
681 Color: stringPtr(charmtone.Julep.Hex()),
682 },
683 LiteralString: ansi.StylePrimitive{
684 Color: stringPtr(charmtone.Cumin.Hex()),
685 },
686 LiteralStringEscape: ansi.StylePrimitive{
687 Color: stringPtr(charmtone.Bok.Hex()),
688 },
689 GenericDeleted: ansi.StylePrimitive{
690 Color: stringPtr(charmtone.Coral.Hex()),
691 },
692 GenericEmph: ansi.StylePrimitive{
693 Italic: boolPtr(true),
694 },
695 GenericInserted: ansi.StylePrimitive{
696 Color: stringPtr(charmtone.Guac.Hex()),
697 },
698 GenericStrong: ansi.StylePrimitive{
699 Bold: boolPtr(true),
700 },
701 GenericSubheading: ansi.StylePrimitive{
702 Color: stringPtr(charmtone.Squid.Hex()),
703 },
704 Background: ansi.StylePrimitive{
705 BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
706 },
707 },
708 },
709 Table: ansi.StyleTable{
710 StyleBlock: ansi.StyleBlock{
711 StylePrimitive: ansi.StylePrimitive{},
712 },
713 },
714 DefinitionDescription: ansi.StylePrimitive{
715 BlockPrefix: "\n ",
716 },
717 }
718
719 // PlainMarkdown style - muted colors on subtle background for thinking content.
720 plainBg := stringPtr(bgBaseLighter.Hex())
721 plainFg := stringPtr(fgMuted.Hex())
722 s.PlainMarkdown = ansi.StyleConfig{
723 Document: ansi.StyleBlock{
724 StylePrimitive: ansi.StylePrimitive{
725 Color: plainFg,
726 BackgroundColor: plainBg,
727 },
728 },
729 BlockQuote: ansi.StyleBlock{
730 StylePrimitive: ansi.StylePrimitive{
731 Color: plainFg,
732 BackgroundColor: plainBg,
733 },
734 Indent: uintPtr(1),
735 IndentToken: stringPtr("│ "),
736 },
737 List: ansi.StyleList{
738 LevelIndent: defaultListIndent,
739 },
740 Heading: ansi.StyleBlock{
741 StylePrimitive: ansi.StylePrimitive{
742 BlockSuffix: "\n",
743 Bold: boolPtr(true),
744 Color: plainFg,
745 BackgroundColor: plainBg,
746 },
747 },
748 H1: ansi.StyleBlock{
749 StylePrimitive: ansi.StylePrimitive{
750 Prefix: " ",
751 Suffix: " ",
752 Bold: boolPtr(true),
753 Color: plainFg,
754 BackgroundColor: plainBg,
755 },
756 },
757 H2: ansi.StyleBlock{
758 StylePrimitive: ansi.StylePrimitive{
759 Prefix: "## ",
760 Color: plainFg,
761 BackgroundColor: plainBg,
762 },
763 },
764 H3: ansi.StyleBlock{
765 StylePrimitive: ansi.StylePrimitive{
766 Prefix: "### ",
767 Color: plainFg,
768 BackgroundColor: plainBg,
769 },
770 },
771 H4: ansi.StyleBlock{
772 StylePrimitive: ansi.StylePrimitive{
773 Prefix: "#### ",
774 Color: plainFg,
775 BackgroundColor: plainBg,
776 },
777 },
778 H5: ansi.StyleBlock{
779 StylePrimitive: ansi.StylePrimitive{
780 Prefix: "##### ",
781 Color: plainFg,
782 BackgroundColor: plainBg,
783 },
784 },
785 H6: ansi.StyleBlock{
786 StylePrimitive: ansi.StylePrimitive{
787 Prefix: "###### ",
788 Color: plainFg,
789 BackgroundColor: plainBg,
790 },
791 },
792 Strikethrough: ansi.StylePrimitive{
793 CrossedOut: boolPtr(true),
794 Color: plainFg,
795 BackgroundColor: plainBg,
796 },
797 Emph: ansi.StylePrimitive{
798 Italic: boolPtr(true),
799 Color: plainFg,
800 BackgroundColor: plainBg,
801 },
802 Strong: ansi.StylePrimitive{
803 Bold: boolPtr(true),
804 Color: plainFg,
805 BackgroundColor: plainBg,
806 },
807 HorizontalRule: ansi.StylePrimitive{
808 Format: "\n--------\n",
809 Color: plainFg,
810 BackgroundColor: plainBg,
811 },
812 Item: ansi.StylePrimitive{
813 BlockPrefix: "• ",
814 Color: plainFg,
815 BackgroundColor: plainBg,
816 },
817 Enumeration: ansi.StylePrimitive{
818 BlockPrefix: ". ",
819 Color: plainFg,
820 BackgroundColor: plainBg,
821 },
822 Task: ansi.StyleTask{
823 StylePrimitive: ansi.StylePrimitive{
824 Color: plainFg,
825 BackgroundColor: plainBg,
826 },
827 Ticked: "[✓] ",
828 Unticked: "[ ] ",
829 },
830 Link: ansi.StylePrimitive{
831 Underline: boolPtr(true),
832 Color: plainFg,
833 BackgroundColor: plainBg,
834 },
835 LinkText: ansi.StylePrimitive{
836 Bold: boolPtr(true),
837 Color: plainFg,
838 BackgroundColor: plainBg,
839 },
840 Image: ansi.StylePrimitive{
841 Underline: boolPtr(true),
842 Color: plainFg,
843 BackgroundColor: plainBg,
844 },
845 ImageText: ansi.StylePrimitive{
846 Format: "Image: {{.text}} →",
847 Color: plainFg,
848 BackgroundColor: plainBg,
849 },
850 Code: ansi.StyleBlock{
851 StylePrimitive: ansi.StylePrimitive{
852 Prefix: " ",
853 Suffix: " ",
854 Color: plainFg,
855 BackgroundColor: plainBg,
856 },
857 },
858 CodeBlock: ansi.StyleCodeBlock{
859 StyleBlock: ansi.StyleBlock{
860 StylePrimitive: ansi.StylePrimitive{
861 Color: plainFg,
862 BackgroundColor: plainBg,
863 },
864 Margin: uintPtr(defaultMargin),
865 },
866 },
867 Table: ansi.StyleTable{
868 StyleBlock: ansi.StyleBlock{
869 StylePrimitive: ansi.StylePrimitive{
870 Color: plainFg,
871 BackgroundColor: plainBg,
872 },
873 },
874 },
875 DefinitionDescription: ansi.StylePrimitive{
876 BlockPrefix: "\n ",
877 Color: plainFg,
878 BackgroundColor: plainBg,
879 },
880 }
881
882 s.Help = help.Styles{
883 ShortKey: base.Foreground(fgMuted),
884 ShortDesc: base.Foreground(fgSubtle),
885 ShortSeparator: base.Foreground(border),
886 Ellipsis: base.Foreground(border),
887 FullKey: base.Foreground(fgMuted),
888 FullDesc: base.Foreground(fgSubtle),
889 FullSeparator: base.Foreground(border),
890 }
891
892 s.Diff = diffview.Style{
893 DividerLine: diffview.LineStyle{
894 LineNumber: lipgloss.NewStyle().
895 Foreground(fgHalfMuted).
896 Background(bgBaseLighter),
897 Code: lipgloss.NewStyle().
898 Foreground(fgHalfMuted).
899 Background(bgBaseLighter),
900 },
901 MissingLine: diffview.LineStyle{
902 LineNumber: lipgloss.NewStyle().
903 Background(bgBaseLighter),
904 Code: lipgloss.NewStyle().
905 Background(bgBaseLighter),
906 },
907 EqualLine: diffview.LineStyle{
908 LineNumber: lipgloss.NewStyle().
909 Foreground(fgMuted).
910 Background(bgBase),
911 Code: lipgloss.NewStyle().
912 Foreground(fgMuted).
913 Background(bgBase),
914 },
915 InsertLine: diffview.LineStyle{
916 LineNumber: lipgloss.NewStyle().
917 Foreground(lipgloss.Color("#629657")).
918 Background(lipgloss.Color("#2b322a")),
919 Symbol: lipgloss.NewStyle().
920 Foreground(lipgloss.Color("#629657")).
921 Background(lipgloss.Color("#323931")),
922 Code: lipgloss.NewStyle().
923 Background(lipgloss.Color("#323931")),
924 },
925 DeleteLine: diffview.LineStyle{
926 LineNumber: lipgloss.NewStyle().
927 Foreground(lipgloss.Color("#a45c59")).
928 Background(lipgloss.Color("#312929")),
929 Symbol: lipgloss.NewStyle().
930 Foreground(lipgloss.Color("#a45c59")).
931 Background(lipgloss.Color("#383030")),
932 Code: lipgloss.NewStyle().
933 Background(lipgloss.Color("#383030")),
934 },
935 }
936
937 s.FilePicker = filepicker.Styles{
938 DisabledCursor: base.Foreground(fgMuted),
939 Cursor: base.Foreground(fgBase),
940 Symlink: base.Foreground(fgSubtle),
941 Directory: base.Foreground(primary),
942 File: base.Foreground(fgBase),
943 DisabledFile: base.Foreground(fgMuted),
944 DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
945 Permission: base.Foreground(fgMuted),
946 Selected: base.Background(primary).Foreground(fgBase),
947 FileSize: base.Foreground(fgMuted),
948 EmptyDirectory: base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
949 }
950
951 // borders
952 s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
953
954 // text presets
955 s.Base = lipgloss.NewStyle().Foreground(fgBase)
956 s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
957 s.HalfMuted = lipgloss.NewStyle().Foreground(fgHalfMuted)
958 s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
959
960 s.WindowTooSmall = s.Muted
961
962 // tag presets
963 s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
964 s.TagError = s.TagBase.Background(redDark)
965 s.TagInfo = s.TagBase.Background(blueLight)
966
967 // headers
968 s.HeaderTool = lipgloss.NewStyle().Foreground(blue)
969 s.HeaderToolNested = lipgloss.NewStyle().Foreground(fgHalfMuted)
970
971 // panels
972 s.PanelMuted = s.Muted.Background(bgBaseLighter)
973 s.PanelBase = lipgloss.NewStyle().Background(bgBase)
974
975 // code line number
976 s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
977
978 // Tool calls
979 s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
980 s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
981 s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
982 // Cancelled uses muted tone but same glyph as pending
983 s.ToolCallCancelled = s.Muted.SetString(ToolPending)
984 s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
985
986 // Tool rendering styles
987 s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
988 s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
989 s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
990 s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
991
992 s.Tool.NameNormal = base.Foreground(blue)
993 s.Tool.NameNested = base.Foreground(fgHalfMuted)
994
995 s.Tool.ParamMain = s.Subtle
996 s.Tool.ParamKey = s.Subtle
997
998 // Content rendering - prepared styles that accept width parameter
999 s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
1000 s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
1001 s.Tool.ContentCodeLine = s.Base.Background(bgBase)
1002 s.Tool.ContentCodeTruncation = s.Muted.Background(bgBase).PaddingLeft(2)
1003 s.Tool.ContentCodeBg = bgBase
1004 s.Tool.Body = base.PaddingLeft(2)
1005
1006 // Deprecated - kept for backward compatibility
1007 s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
1008 s.Tool.ContentText = s.Muted
1009 s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
1010
1011 s.Tool.StateWaiting = base.Foreground(fgSubtle)
1012 s.Tool.StateCancelled = base.Foreground(fgSubtle)
1013
1014 s.Tool.ErrorTag = base.Padding(0, 1).Background(red).Foreground(white)
1015 s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
1016
1017 // Diff and multi-edit styles
1018 s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
1019 s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
1020 s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
1021
1022 // Job header styles
1023 s.Tool.JobIconPending = base.Foreground(greenDark)
1024 s.Tool.JobIconError = base.Foreground(redDark)
1025 s.Tool.JobIconSuccess = base.Foreground(green)
1026 s.Tool.JobToolName = base.Foreground(blue)
1027 s.Tool.JobAction = base.Foreground(blueDark)
1028 s.Tool.JobPID = s.Muted
1029 s.Tool.JobDescription = s.Subtle
1030
1031 // Agent task styles
1032 s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
1033 s.Tool.AgentPrompt = s.Muted
1034
1035 // Agentic fetch styles
1036 s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
1037
1038 // Todo styles
1039 s.Tool.TodoRatio = base.Foreground(blueDark)
1040 s.Tool.TodoCompletedIcon = base.Foreground(green)
1041 s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
1042 s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
1043
1044 // Buttons
1045 s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
1046 s.ButtonBlur = s.Base.Background(bgSubtle)
1047
1048 // Borders
1049 s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
1050
1051 // Editor
1052 s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1053 s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
1054 s.EditorPromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1055 s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1056 s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1057 s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
1058
1059 s.RadioOn = s.HalfMuted.SetString(RadioOn)
1060 s.RadioOff = s.HalfMuted.SetString(RadioOff)
1061
1062 // Logo colors
1063 s.LogoFieldColor = primary
1064 s.LogoTitleColorA = secondary
1065 s.LogoTitleColorB = primary
1066 s.LogoCharmColor = secondary
1067 s.LogoVersionColor = primary
1068
1069 // Section
1070 s.Section.Title = s.Subtle
1071 s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
1072
1073 // Initialize
1074 s.Initialize.Header = s.Base
1075 s.Initialize.Content = s.Muted
1076 s.Initialize.Accent = s.Base.Foreground(greenDark)
1077
1078 // LSP and MCP status.
1079 s.ItemOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Squid).SetString("●")
1080 s.ItemBusyIcon = s.ItemOfflineIcon.Foreground(charmtone.Citron)
1081 s.ItemErrorIcon = s.ItemOfflineIcon.Foreground(charmtone.Coral)
1082 s.ItemOnlineIcon = s.ItemOfflineIcon.Foreground(charmtone.Guac)
1083
1084 // LSP
1085 s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
1086 s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
1087 s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
1088 s.LSP.InfoDiagnostic = s.Base.Foreground(info)
1089
1090 // Files
1091 s.Files.Path = s.Muted
1092 s.Files.Additions = s.Base.Foreground(greenDark)
1093 s.Files.Deletions = s.Base.Foreground(redDark)
1094
1095 // Chat
1096 messageFocussedBorder := lipgloss.Border{
1097 Left: "▌",
1098 }
1099
1100 s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1101 s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1102 BorderForeground(primary).BorderStyle(normalBorder)
1103 s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1104 BorderForeground(primary).BorderStyle(messageFocussedBorder)
1105 s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
1106 s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1107 BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1108 s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
1109 s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1110 Background(red).Foreground(white)
1111 s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1112 s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1113
1114 // Message item styles
1115 s.Chat.Message.Attachment = lipgloss.NewStyle().MarginLeft(1).Background(bgSubtle)
1116 s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
1117 BorderStyle(messageFocussedBorder).
1118 BorderLeft(true).
1119 BorderForeground(greenDark)
1120 s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
1121 // No padding or border for compact tool calls within messages
1122 s.Chat.Message.ToolCallCompact = s.Muted
1123 s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
1124
1125 // Thinking section styles
1126 s.Chat.Message.ThinkingBox = s.Subtle.Background(bgBaseLighter)
1127 s.Chat.Message.ThinkingTruncationHint = s.Muted
1128 s.Chat.Message.ThinkingFooterTitle = s.Muted
1129 s.Chat.Message.ThinkingFooterDuration = s.Subtle
1130
1131 // Text selection.
1132 s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
1133
1134 // Dialog styles
1135 s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
1136 s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1137 s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
1138 s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
1139 s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
1140 s.Dialog.Help.ShortSeparator = base.Foreground(border)
1141 s.Dialog.Help.Ellipsis = base.Foreground(border)
1142 s.Dialog.Help.FullKey = base.Foreground(fgMuted)
1143 s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
1144 s.Dialog.Help.FullSeparator = base.Foreground(border)
1145 s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
1146 s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
1147 s.Dialog.InputPrompt = base.Margin(1, 1)
1148
1149 s.Dialog.List = base.Margin(0, 0, 1, 0)
1150
1151 s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
1152 s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
1153 s.Status.InfoIndicator = s.Status.SuccessIndicator
1154 s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
1155 s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(bgOverlay).Background(yellow).SetString("WARNING")
1156 s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(bgBase).Background(red).SetString("ERROR")
1157 s.Status.SuccessMessage = base.Foreground(bgSubtle).Background(greenDark).Padding(0, 1)
1158 s.Status.InfoMessage = s.Status.SuccessMessage
1159 s.Status.UpdateMessage = s.Status.SuccessMessage
1160 s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(bgOverlay).Background(warning)
1161 s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(white).Background(redDark)
1162
1163 return s
1164}
1165
1166// Helper functions for style pointers
1167func boolPtr(b bool) *bool { return &b }
1168func stringPtr(s string) *string { return &s }
1169func uintPtr(u uint) *uint { return &u }
1170func chromaStyle(style ansi.StylePrimitive) string {
1171 var s string
1172
1173 if style.Color != nil {
1174 s = *style.Color
1175 }
1176 if style.BackgroundColor != nil {
1177 if s != "" {
1178 s += " "
1179 }
1180 s += "bg:" + *style.BackgroundColor
1181 }
1182 if style.Italic != nil && *style.Italic {
1183 if s != "" {
1184 s += " "
1185 }
1186 s += "italic"
1187 }
1188 if style.Bold != nil && *style.Bold {
1189 if s != "" {
1190 s += " "
1191 }
1192 s += "bold"
1193 }
1194 if style.Underline != nil && *style.Underline {
1195 if s != "" {
1196 s += " "
1197 }
1198 s += "underline"
1199 }
1200
1201 return s
1202}