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