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