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