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