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