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