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