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