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