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