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/charmbracelet/crush/internal/ui/diffview"
14 "github.com/charmbracelet/x/exp/charmtone"
15)
16
17// quickStyleOpts is the palette of colors used by quickStyle to build a
18// complete Styles value. Each field maps to a semantic role in the UI.
19type quickStyleOpts struct {
20 // Brand.
21 primary color.Color
22 secondary color.Color
23 tertiary color.Color
24
25 // Foregrounds.
26 fgBase color.Color
27 fgMuted color.Color
28 fgHalfMuted color.Color
29 fgSubtle color.Color
30
31 // Contrast pairings: foregrounds designed to sit on top of a
32 // matching background role.
33 onPrimary color.Color // foreground on primary backgrounds.
34 onAccent color.Color // foreground on saturated status/accent backgrounds.
35
36 // Backgrounds.
37 bgBase color.Color
38 bgBaseLighter color.Color
39 bgSubtle color.Color
40 bgOverlay color.Color
41
42 // Borders.
43 border color.Color
44 borderFocus color.Color
45
46 // Status.
47 danger color.Color
48 error color.Color
49 warning color.Color
50 warningStrong color.Color
51 busy color.Color
52 info color.Color
53 infoSubtle color.Color
54 infoMuted color.Color
55 success color.Color
56 successSubtle color.Color
57 successMuted color.Color
58}
59
60// quickStyle builds a complete Styles value from a palette of semantic
61// colors. Themes should populate quickStyleOpts and call this rather than
62// re-implementing every style rule.
63//
64// The idea here is that you can do most of the work with quickStyle, then
65// add overrides as needed.
66func quickStyle(o quickStyleOpts) Styles {
67 var (
68 primary = o.primary
69 secondary = o.secondary
70 tertiary = o.tertiary
71
72 fgBase = o.fgBase
73 fgMuted = o.fgMuted
74 fgHalfMuted = o.fgHalfMuted
75 fgSubtle = o.fgSubtle
76
77 onPrimary = o.onPrimary
78 onAccent = o.onAccent
79
80 bgBase = o.bgBase
81 bgBaseLighter = o.bgBaseLighter
82 bgSubtle = o.bgSubtle
83 bgOverlay = o.bgOverlay
84
85 border = o.border
86 borderFocus = o.borderFocus
87
88 danger = o.danger
89 error = o.error
90 warning = o.warning
91 warningStrong = o.warningStrong
92 busy = o.busy
93 info = o.info
94 infoSubtle = o.infoSubtle
95 infoMuted = o.infoMuted
96 success = o.success
97 successSubtle = o.successSubtle
98 successMuted = o.successMuted
99 )
100
101 var (
102 base = lipgloss.NewStyle().Foreground(fgBase)
103 muted = lipgloss.NewStyle().Foreground(fgMuted)
104 subtle = lipgloss.NewStyle().Foreground(fgSubtle)
105 s Styles
106 )
107
108 s.Background = bgBase
109
110 // Populate color fields
111 s.WorkingGradFromColor = primary
112 s.WorkingGradToColor = secondary
113 s.WorkingLabelColor = fgBase
114
115 s.TextInput = textinput.Styles{
116 Focused: textinput.StyleState{
117 Text: base,
118 Placeholder: base.Foreground(fgSubtle),
119 Prompt: base.Foreground(tertiary),
120 Suggestion: base.Foreground(fgSubtle),
121 },
122 Blurred: textinput.StyleState{
123 Text: base.Foreground(fgMuted),
124 Placeholder: base.Foreground(fgSubtle),
125 Prompt: base.Foreground(fgMuted),
126 Suggestion: base.Foreground(fgSubtle),
127 },
128 Cursor: textinput.CursorStyle{
129 Color: secondary,
130 Shape: tea.CursorBlock,
131 Blink: true,
132 },
133 }
134
135 s.Editor.Textarea = textarea.Styles{
136 Focused: textarea.StyleState{
137 Base: base,
138 Text: base,
139 LineNumber: base.Foreground(fgSubtle),
140 CursorLine: base,
141 CursorLineNumber: base.Foreground(fgSubtle),
142 Placeholder: base.Foreground(fgSubtle),
143 Prompt: base.Foreground(tertiary),
144 },
145 Blurred: textarea.StyleState{
146 Base: base,
147 Text: base.Foreground(fgMuted),
148 LineNumber: base.Foreground(fgMuted),
149 CursorLine: base,
150 CursorLineNumber: base.Foreground(fgMuted),
151 Placeholder: base.Foreground(fgSubtle),
152 Prompt: base.Foreground(fgMuted),
153 },
154 Cursor: textarea.CursorStyle{
155 Color: secondary,
156 Shape: tea.CursorBlock,
157 Blink: true,
158 },
159 }
160
161 s.Markdown = ansi.StyleConfig{
162 Document: ansi.StyleBlock{
163 StylePrimitive: ansi.StylePrimitive{
164 // BlockPrefix: "\n",
165 // BlockSuffix: "\n",
166 Color: hex(fgHalfMuted),
167 },
168 // Margin: new(uint(defaultMargin)),
169 },
170 BlockQuote: ansi.StyleBlock{
171 StylePrimitive: ansi.StylePrimitive{},
172 Indent: new(uint(1)),
173 IndentToken: new("│ "),
174 },
175 List: ansi.StyleList{
176 LevelIndent: defaultListIndent,
177 },
178 Heading: ansi.StyleBlock{
179 StylePrimitive: ansi.StylePrimitive{
180 BlockSuffix: "\n",
181 Color: hex(info),
182 Bold: new(true),
183 },
184 },
185 H1: ansi.StyleBlock{
186 StylePrimitive: ansi.StylePrimitive{
187 Prefix: " ",
188 Suffix: " ",
189 Color: hex(warning),
190 BackgroundColor: hex(primary),
191 Bold: new(true),
192 },
193 },
194 H2: ansi.StyleBlock{
195 StylePrimitive: ansi.StylePrimitive{
196 Prefix: "## ",
197 },
198 },
199 H3: ansi.StyleBlock{
200 StylePrimitive: ansi.StylePrimitive{
201 Prefix: "### ",
202 },
203 },
204 H4: ansi.StyleBlock{
205 StylePrimitive: ansi.StylePrimitive{
206 Prefix: "#### ",
207 },
208 },
209 H5: ansi.StyleBlock{
210 StylePrimitive: ansi.StylePrimitive{
211 Prefix: "##### ",
212 },
213 },
214 H6: ansi.StyleBlock{
215 StylePrimitive: ansi.StylePrimitive{
216 Prefix: "###### ",
217 Color: hex(successMuted),
218 Bold: new(false),
219 },
220 },
221 Strikethrough: ansi.StylePrimitive{
222 CrossedOut: new(true),
223 },
224 Emph: ansi.StylePrimitive{
225 Italic: new(true),
226 },
227 Strong: ansi.StylePrimitive{
228 Bold: new(true),
229 },
230 HorizontalRule: ansi.StylePrimitive{
231 Color: hex(border),
232 Format: "\n--------\n",
233 },
234 Item: ansi.StylePrimitive{
235 BlockPrefix: "• ",
236 },
237 Enumeration: ansi.StylePrimitive{
238 BlockPrefix: ". ",
239 },
240 Task: ansi.StyleTask{
241 StylePrimitive: ansi.StylePrimitive{},
242 Ticked: "[✓] ",
243 Unticked: "[ ] ",
244 },
245 Link: ansi.StylePrimitive{
246 Color: hex(charmtone.Zinc),
247 Underline: new(true),
248 },
249 LinkText: ansi.StylePrimitive{
250 Color: hex(successMuted),
251 Bold: new(true),
252 },
253 Image: ansi.StylePrimitive{
254 Color: hex(charmtone.Cheeky),
255 Underline: new(true),
256 },
257 ImageText: ansi.StylePrimitive{
258 Color: hex(fgMuted),
259 Format: "Image: {{.text}} →",
260 },
261 Code: ansi.StyleBlock{
262 StylePrimitive: ansi.StylePrimitive{
263 Prefix: " ",
264 Suffix: " ",
265 Color: hex(danger),
266 BackgroundColor: hex(bgSubtle),
267 },
268 },
269 CodeBlock: ansi.StyleCodeBlock{
270 StyleBlock: ansi.StyleBlock{
271 StylePrimitive: ansi.StylePrimitive{
272 Color: hex(bgSubtle),
273 },
274 Margin: new(uint(defaultMargin)),
275 },
276 Chroma: &ansi.Chroma{
277 Text: ansi.StylePrimitive{
278 Color: hex(fgHalfMuted),
279 },
280 Error: ansi.StylePrimitive{
281 Color: hex(onAccent),
282 BackgroundColor: hex(error),
283 },
284 Comment: ansi.StylePrimitive{
285 Color: hex(fgSubtle),
286 },
287 CommentPreproc: ansi.StylePrimitive{
288 Color: hex(charmtone.Bengal),
289 },
290 Keyword: ansi.StylePrimitive{
291 Color: hex(info),
292 },
293 KeywordReserved: ansi.StylePrimitive{
294 Color: hex(charmtone.Pony),
295 },
296 KeywordNamespace: ansi.StylePrimitive{
297 Color: hex(charmtone.Pony),
298 },
299 KeywordType: ansi.StylePrimitive{
300 Color: hex(charmtone.Guppy),
301 },
302 Operator: ansi.StylePrimitive{
303 Color: hex(charmtone.Salmon),
304 },
305 Punctuation: ansi.StylePrimitive{
306 Color: hex(warning),
307 },
308 Name: ansi.StylePrimitive{
309 Color: hex(fgHalfMuted),
310 },
311 NameBuiltin: ansi.StylePrimitive{
312 Color: hex(charmtone.Cheeky),
313 },
314 NameTag: ansi.StylePrimitive{
315 Color: hex(charmtone.Mauve),
316 },
317 NameAttribute: ansi.StylePrimitive{
318 Color: hex(charmtone.Hazy),
319 },
320 NameClass: ansi.StylePrimitive{
321 Color: hex(charmtone.Salt),
322 Underline: new(true),
323 Bold: new(true),
324 },
325 NameDecorator: ansi.StylePrimitive{
326 Color: hex(charmtone.Citron),
327 },
328 NameFunction: ansi.StylePrimitive{
329 Color: hex(successMuted),
330 },
331 LiteralNumber: ansi.StylePrimitive{
332 Color: hex(success),
333 },
334 LiteralString: ansi.StylePrimitive{
335 Color: hex(charmtone.Cumin),
336 },
337 LiteralStringEscape: ansi.StylePrimitive{
338 Color: hex(successSubtle),
339 },
340 GenericDeleted: ansi.StylePrimitive{
341 Color: hex(danger),
342 },
343 GenericEmph: ansi.StylePrimitive{
344 Italic: new(true),
345 },
346 GenericInserted: ansi.StylePrimitive{
347 Color: hex(successMuted),
348 },
349 GenericStrong: ansi.StylePrimitive{
350 Bold: new(true),
351 },
352 GenericSubheading: ansi.StylePrimitive{
353 Color: hex(fgMuted),
354 },
355 Background: ansi.StylePrimitive{
356 BackgroundColor: hex(bgSubtle),
357 },
358 },
359 },
360 Table: ansi.StyleTable{
361 StyleBlock: ansi.StyleBlock{
362 StylePrimitive: ansi.StylePrimitive{},
363 },
364 },
365 DefinitionDescription: ansi.StylePrimitive{
366 BlockPrefix: "\n ",
367 },
368 }
369
370 // QuietMarkdown style - muted colors on subtle background for thinking content.
371 plainBg := hex(bgBaseLighter)
372 plainFg := hex(fgMuted)
373 s.QuietMarkdown = ansi.StyleConfig{
374 Document: ansi.StyleBlock{
375 StylePrimitive: ansi.StylePrimitive{
376 Color: plainFg,
377 BackgroundColor: plainBg,
378 },
379 },
380 BlockQuote: ansi.StyleBlock{
381 StylePrimitive: ansi.StylePrimitive{
382 Color: plainFg,
383 BackgroundColor: plainBg,
384 },
385 Indent: new(uint(1)),
386 IndentToken: new("│ "),
387 },
388 List: ansi.StyleList{
389 LevelIndent: defaultListIndent,
390 },
391 Heading: ansi.StyleBlock{
392 StylePrimitive: ansi.StylePrimitive{
393 BlockSuffix: "\n",
394 Bold: new(true),
395 Color: plainFg,
396 BackgroundColor: plainBg,
397 },
398 },
399 H1: ansi.StyleBlock{
400 StylePrimitive: ansi.StylePrimitive{
401 Prefix: " ",
402 Suffix: " ",
403 Bold: new(true),
404 Color: plainFg,
405 BackgroundColor: plainBg,
406 },
407 },
408 H2: ansi.StyleBlock{
409 StylePrimitive: ansi.StylePrimitive{
410 Prefix: "## ",
411 Color: plainFg,
412 BackgroundColor: plainBg,
413 },
414 },
415 H3: ansi.StyleBlock{
416 StylePrimitive: ansi.StylePrimitive{
417 Prefix: "### ",
418 Color: plainFg,
419 BackgroundColor: plainBg,
420 },
421 },
422 H4: ansi.StyleBlock{
423 StylePrimitive: ansi.StylePrimitive{
424 Prefix: "#### ",
425 Color: plainFg,
426 BackgroundColor: plainBg,
427 },
428 },
429 H5: ansi.StyleBlock{
430 StylePrimitive: ansi.StylePrimitive{
431 Prefix: "##### ",
432 Color: plainFg,
433 BackgroundColor: plainBg,
434 },
435 },
436 H6: ansi.StyleBlock{
437 StylePrimitive: ansi.StylePrimitive{
438 Prefix: "###### ",
439 Color: plainFg,
440 BackgroundColor: plainBg,
441 },
442 },
443 Strikethrough: ansi.StylePrimitive{
444 CrossedOut: new(true),
445 Color: plainFg,
446 BackgroundColor: plainBg,
447 },
448 Emph: ansi.StylePrimitive{
449 Italic: new(true),
450 Color: plainFg,
451 BackgroundColor: plainBg,
452 },
453 Strong: ansi.StylePrimitive{
454 Bold: new(true),
455 Color: plainFg,
456 BackgroundColor: plainBg,
457 },
458 HorizontalRule: ansi.StylePrimitive{
459 Format: "\n--------\n",
460 Color: plainFg,
461 BackgroundColor: plainBg,
462 },
463 Item: ansi.StylePrimitive{
464 BlockPrefix: "• ",
465 Color: plainFg,
466 BackgroundColor: plainBg,
467 },
468 Enumeration: ansi.StylePrimitive{
469 BlockPrefix: ". ",
470 Color: plainFg,
471 BackgroundColor: plainBg,
472 },
473 Task: ansi.StyleTask{
474 StylePrimitive: ansi.StylePrimitive{
475 Color: plainFg,
476 BackgroundColor: plainBg,
477 },
478 Ticked: "[✓] ",
479 Unticked: "[ ] ",
480 },
481 Link: ansi.StylePrimitive{
482 Underline: new(true),
483 Color: plainFg,
484 BackgroundColor: plainBg,
485 },
486 LinkText: ansi.StylePrimitive{
487 Bold: new(true),
488 Color: plainFg,
489 BackgroundColor: plainBg,
490 },
491 Image: ansi.StylePrimitive{
492 Underline: new(true),
493 Color: plainFg,
494 BackgroundColor: plainBg,
495 },
496 ImageText: ansi.StylePrimitive{
497 Format: "Image: {{.text}} →",
498 Color: plainFg,
499 BackgroundColor: plainBg,
500 },
501 Code: ansi.StyleBlock{
502 StylePrimitive: ansi.StylePrimitive{
503 Prefix: " ",
504 Suffix: " ",
505 Color: plainFg,
506 BackgroundColor: plainBg,
507 },
508 },
509 CodeBlock: ansi.StyleCodeBlock{
510 StyleBlock: ansi.StyleBlock{
511 StylePrimitive: ansi.StylePrimitive{
512 Color: plainFg,
513 BackgroundColor: plainBg,
514 },
515 Margin: new(uint(defaultMargin)),
516 },
517 },
518 Table: ansi.StyleTable{
519 StyleBlock: ansi.StyleBlock{
520 StylePrimitive: ansi.StylePrimitive{
521 Color: plainFg,
522 BackgroundColor: plainBg,
523 },
524 },
525 },
526 DefinitionDescription: ansi.StylePrimitive{
527 BlockPrefix: "\n ",
528 Color: plainFg,
529 BackgroundColor: plainBg,
530 },
531 }
532
533 s.Help = help.Styles{
534 ShortKey: base.Foreground(fgMuted),
535 ShortDesc: base.Foreground(fgSubtle),
536 ShortSeparator: base.Foreground(border),
537 Ellipsis: base.Foreground(border),
538 FullKey: base.Foreground(fgMuted),
539 FullDesc: base.Foreground(fgSubtle),
540 FullSeparator: base.Foreground(border),
541 }
542
543 s.Diff = diffview.Style{
544 DividerLine: diffview.LineStyle{
545 LineNumber: lipgloss.NewStyle().
546 Foreground(fgHalfMuted).
547 Background(bgBaseLighter),
548 Code: lipgloss.NewStyle().
549 Foreground(fgHalfMuted).
550 Background(bgBaseLighter),
551 },
552 MissingLine: diffview.LineStyle{
553 LineNumber: lipgloss.NewStyle().
554 Background(bgBaseLighter),
555 Code: lipgloss.NewStyle().
556 Background(bgBaseLighter),
557 },
558 EqualLine: diffview.LineStyle{
559 LineNumber: lipgloss.NewStyle().
560 Foreground(fgMuted).
561 Background(bgBase),
562 Code: lipgloss.NewStyle().
563 Foreground(fgMuted).
564 Background(bgBase),
565 },
566 InsertLine: diffview.LineStyle{
567 LineNumber: lipgloss.NewStyle().
568 Foreground(lipgloss.Color("#629657")).
569 Background(lipgloss.Color("#2b322a")),
570 Symbol: lipgloss.NewStyle().
571 Foreground(lipgloss.Color("#629657")).
572 Background(lipgloss.Color("#323931")),
573 Code: lipgloss.NewStyle().
574 Background(lipgloss.Color("#323931")),
575 },
576 DeleteLine: diffview.LineStyle{
577 LineNumber: lipgloss.NewStyle().
578 Foreground(lipgloss.Color("#a45c59")).
579 Background(lipgloss.Color("#312929")),
580 Symbol: lipgloss.NewStyle().
581 Foreground(lipgloss.Color("#a45c59")).
582 Background(lipgloss.Color("#383030")),
583 Code: lipgloss.NewStyle().
584 Background(lipgloss.Color("#383030")),
585 },
586 Filename: diffview.LineStyle{
587 LineNumber: lipgloss.NewStyle().
588 Foreground(fgHalfMuted).
589 Background(bgBaseLighter),
590 Code: lipgloss.NewStyle().
591 Foreground(fgHalfMuted).
592 Background(bgBaseLighter),
593 },
594 }
595
596 s.FilePicker = filepicker.Styles{
597 DisabledCursor: base.Foreground(fgMuted),
598 Cursor: base.Foreground(fgBase),
599 Symlink: base.Foreground(fgSubtle),
600 Directory: base.Foreground(primary),
601 File: base.Foreground(fgBase),
602 DisabledFile: base.Foreground(fgMuted),
603 DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
604 Permission: base.Foreground(fgMuted),
605 Selected: base.Background(primary).Foreground(fgBase),
606 FileSize: base.Foreground(fgMuted),
607 EmptyDirectory: base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
608 }
609
610 // borders
611 s.ToolCallSuccess = lipgloss.NewStyle().Foreground(success).SetString(ToolSuccess)
612
613 s.Header.Charm = base.Foreground(secondary)
614 s.Header.Diagonals = base.Foreground(primary)
615 s.Header.Percentage = muted
616 s.Header.Keystroke = muted
617 s.Header.KeystrokeTip = subtle
618 s.Header.WorkingDir = muted
619 s.Header.Separator = subtle
620 s.Header.Wrapper = lipgloss.NewStyle().Foreground(fgBase)
621 s.Header.LogoGradCanvas = lipgloss.NewStyle()
622 s.Header.LogoGradFromColor = secondary
623 s.Header.LogoGradToColor = primary
624
625 s.CompactDetails.Title = base
626 s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
627 s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
628
629 // Tool rendering styles
630 s.Tool.IconPending = base.Foreground(successMuted).SetString(ToolPending)
631 s.Tool.IconSuccess = base.Foreground(success).SetString(ToolSuccess)
632 s.Tool.IconError = base.Foreground(error).SetString(ToolError)
633 s.Tool.IconCancelled = muted.SetString(ToolPending)
634
635 s.Tool.NameNormal = base.Foreground(info)
636 s.Tool.NameNested = base.Foreground(info)
637
638 s.Tool.ParamMain = subtle
639 s.Tool.ParamKey = subtle
640
641 // Content rendering - prepared styles that accept width parameter
642 s.Tool.ContentLine = muted.Background(bgBaseLighter)
643 s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
644 s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
645 s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
646 s.Tool.ContentCodeBg = bgBase
647 s.Tool.Body = base.PaddingLeft(2)
648
649 // Deprecated - kept for backward compatibility
650 s.Tool.ContentBg = muted.Background(bgBaseLighter)
651 s.Tool.ContentText = muted
652 s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
653
654 s.Tool.StateWaiting = base.Foreground(fgSubtle)
655 s.Tool.StateCancelled = base.Foreground(fgSubtle)
656
657 s.Tool.ErrorTag = base.Padding(0, 1).Background(danger).Foreground(onAccent)
658 s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
659
660 // Diff and multi-edit styles
661 s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
662 s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(onAccent)
663 s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
664
665 // Job header styles
666 s.Tool.JobIconPending = base.Foreground(successMuted)
667 s.Tool.JobIconError = base.Foreground(error)
668 s.Tool.JobIconSuccess = base.Foreground(success)
669 s.Tool.JobToolName = base.Foreground(info)
670 s.Tool.JobAction = base.Foreground(infoMuted)
671 s.Tool.JobPID = muted
672 s.Tool.JobDescription = subtle
673
674 // Agent task styles
675 s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(infoSubtle).Foreground(onAccent)
676 s.Tool.AgentPrompt = muted
677
678 // Agentic fetch styles
679 s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(success).Foreground(border)
680
681 // Todo styles
682 s.Tool.TodoRatio = base.Foreground(infoMuted)
683 s.Tool.TodoCompletedIcon = base.Foreground(success)
684 s.Tool.TodoInProgressIcon = base.Foreground(successMuted)
685 s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
686 s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(fgSubtle)
687 s.Tool.TodoItem = lipgloss.NewStyle().Foreground(fgBase)
688 s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(fgBase)
689
690 // MCP styles
691 s.Tool.MCPName = base.Foreground(info)
692 s.Tool.MCPToolName = base.Foreground(infoMuted)
693 s.Tool.MCPArrow = base.Foreground(info).SetString(ArrowRightIcon)
694
695 // Loading indicators for images, skills
696 s.Tool.ResourceLoadedText = base.Foreground(success)
697 s.Tool.ResourceLoadedIndicator = base.Foreground(successMuted)
698 s.Tool.ResourceName = base
699 s.Tool.MediaType = base
700 s.Tool.ResourceSize = base.Foreground(fgMuted)
701
702 // Hook styles
703 s.Tool.HookLabel = base.Foreground(successSubtle)
704 s.Tool.HookName = base
705 s.Tool.HookMatcher = base.Foreground(fgMuted)
706 s.Tool.HookArrow = base.Foreground(successSubtle)
707 s.Tool.HookDetail = base.Foreground(fgMuted)
708 s.Tool.HookOK = base.Foreground(successMuted)
709 s.Tool.HookDenied = base.Foreground(error)
710 s.Tool.HookDeniedLabel = base.Foreground(danger)
711 s.Tool.HookDeniedReason = base.Foreground(bgOverlay)
712 s.Tool.HookRewrote = base.Foreground(bgOverlay)
713
714 // Tool-call action verbs and result-list styling.
715 s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(successSubtle)
716 s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(danger)
717 s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
718 s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
719 s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
720 s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
721
722 // Buttons
723 s.Button.Focused = lipgloss.NewStyle().Foreground(onAccent).Background(secondary)
724 s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
725
726 // Editor
727 s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(successMuted).SetString("::: ")
728 s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
729 s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(fgSubtle).Background(busy).Bold(true).SetString(" ! ")
730 s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(bgBase).Background(fgMuted)
731 s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(warning).SetString(":::")
732 s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(fgMuted)
733
734 s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
735 s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
736 s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
737
738 // Logo
739 s.Logo.FieldColor = primary
740 s.Logo.TitleColorA = secondary
741 s.Logo.TitleColorB = primary
742 s.Logo.CharmColor = secondary
743 s.Logo.VersionColor = primary
744 s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
745 s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
746 s.Logo.GradCanvas = lipgloss.NewStyle()
747 s.Logo.SmallGradFromColor = secondary
748 s.Logo.SmallGradToColor = primary
749
750 // Section
751 s.Section.Title = subtle
752 s.Section.Line = base.Foreground(border)
753
754 // Initialize
755 s.Initialize.Header = base
756 s.Initialize.Content = muted
757 s.Initialize.Accent = base.Foreground(successMuted)
758
759 // ResourceGroup (LSP/MCP/skills sidebar lists).
760 s.Resource.Heading = lipgloss.NewStyle().Foreground(fgSubtle)
761 s.Resource.Name = lipgloss.NewStyle().Foreground(fgMuted)
762 s.Resource.StatusText = lipgloss.NewStyle().Foreground(fgSubtle)
763 s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(bgOverlay).SetString("●")
764 s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(busy)
765 s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(danger)
766 s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(successMuted)
767 s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
768 s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(fgSubtle)
769 s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
770 s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
771 s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
772 s.Resource.DefaultTitleFg = fgMuted
773 s.Resource.DefaultDescFg = fgSubtle
774
775 // LSP
776 s.LSP.ErrorDiagnostic = base.Foreground(error)
777 s.LSP.WarningDiagnostic = base.Foreground(warning)
778 s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
779 s.LSP.InfoDiagnostic = base.Foreground(info)
780
781 // Files
782 s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
783 s.Files.Additions = lipgloss.NewStyle().Foreground(successMuted)
784 s.Files.Deletions = lipgloss.NewStyle().Foreground(error)
785 s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
786 s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
787 s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
788
789 // Sidebar
790 s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
791 s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
792
793 // ModelInfo
794 s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
795 s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
796 s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
797 s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
798 s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
799 s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
800 s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
801 s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
802
803 // ResourceGroup
804 s.Resource.DefaultTitleFg = fgMuted
805 s.Resource.DefaultDescFg = fgSubtle
806
807 // Chat
808 messageFocussedBorder := lipgloss.Border{
809 Left: "▌",
810 }
811
812 s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
813 s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
814 BorderForeground(primary).BorderStyle(lipgloss.NormalBorder())
815 s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
816 BorderForeground(primary).BorderStyle(messageFocussedBorder)
817 s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
818 s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
819 BorderForeground(successMuted).BorderStyle(messageFocussedBorder)
820 s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
821 s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
822 Background(danger).Foreground(onAccent)
823 s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
824 s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
825
826 // Message item styles
827 s.Messages.ToolCallFocused = muted.PaddingLeft(1).
828 BorderStyle(messageFocussedBorder).
829 BorderLeft(true).
830 BorderForeground(successMuted)
831 s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
832 // No padding or border for compact tool calls within messages
833 s.Messages.ToolCallCompact = muted
834 s.Messages.SectionHeader = base.PaddingLeft(2)
835 s.Messages.AssistantInfoIcon = subtle
836 s.Messages.AssistantInfoModel = muted
837 s.Messages.AssistantInfoProvider = subtle
838 s.Messages.AssistantInfoDuration = subtle
839 s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
840
841 // Thinking section styles
842 s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
843 s.Messages.ThinkingTruncationHint = muted
844 s.Messages.ThinkingFooterTitle = muted
845 s.Messages.ThinkingFooterDuration = subtle
846
847 // Text selection.
848 s.TextSelection = lipgloss.NewStyle().Foreground(onPrimary).Background(primary)
849
850 // Dialog styles
851 s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
852 s.Dialog.TitleText = base.Foreground(primary)
853 s.Dialog.TitleError = base.Foreground(danger)
854 s.Dialog.TitleAccent = base.Foreground(success).Bold(true)
855 s.Dialog.TitleLineBase = lipgloss.NewStyle()
856 s.Dialog.TitleGradFromColor = primary
857 s.Dialog.TitleGradToColor = secondary
858
859 // Dialog.ListItem (commands, reasoning, models)
860 s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
861 s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
862
863 // Dialog.Models
864 s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
865
866 // Dialog.Permissions
867 s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
868 s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
869 s.Dialog.Permissions.ParamsBg = bgSubtle
870
871 // Dialog.Quit
872 s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
873 s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
874 s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
875 s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
876 s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
877 s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
878 s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
879 s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
880 s.Dialog.Help.ShortSeparator = base.Foreground(border)
881 s.Dialog.Help.Ellipsis = base.Foreground(border)
882 s.Dialog.Help.FullKey = base.Foreground(fgMuted)
883 s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
884 s.Dialog.Help.FullSeparator = base.Foreground(border)
885 s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
886 s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
887 s.Dialog.InputPrompt = base.Margin(1, 1)
888
889 s.Dialog.List = base.Margin(0, 0, 1, 0)
890 s.Dialog.ContentPanel = base.Background(bgSubtle).Foreground(fgBase).Padding(1, 2)
891 s.Dialog.Spinner = base.Foreground(secondary)
892 s.Dialog.ScrollbarThumb = base.Foreground(secondary)
893 s.Dialog.ScrollbarTrack = base.Foreground(border)
894
895 s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
896
897 // API key input dialog
898 s.Dialog.APIKey.Spinner = base.Foreground(success)
899
900 // OAuth dialog
901 s.Dialog.OAuth.Spinner = base.Foreground(successSubtle)
902 s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(onAccent)
903 s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(onAccent)
904 s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(successSubtle)
905 s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(successMuted).Underline(true)
906 s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
907 s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
908 s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
909 s.Dialog.OAuth.UserCodeBg = bgBaseLighter
910
911 s.Dialog.Arguments.Content = base.Padding(1)
912 s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
913 s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
914 s.Dialog.Arguments.InputLabelFocused = base.Bold(true)
915 s.Dialog.Arguments.InputRequiredMarkBlurred = base.Foreground(fgMuted).SetString("*")
916 s.Dialog.Arguments.InputRequiredMarkFocused = base.Foreground(primary).Bold(true).SetString("*")
917
918 s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(danger)
919 s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(danger)
920 s.Dialog.Sessions.DeletingMessage = base.Padding(1)
921 s.Dialog.Sessions.DeletingTitleGradientFromColor = danger
922 s.Dialog.Sessions.DeletingTitleGradientToColor = primary
923 s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
924 s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(danger).Foreground(onAccent)
925
926 s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(warning)
927 s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(warning)
928 s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
929 s.Dialog.Sessions.RenamingTitleGradientFromColor = warning
930 s.Dialog.Sessions.RenamingTitleGradientToColor = tertiary
931 s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
932 s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
933 s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(fgMuted)
934 s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
935 s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
936
937 s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
938 s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(success).Padding(0, 1).Bold(true).SetString("OKAY!")
939 s.Status.InfoIndicator = s.Status.SuccessIndicator
940 s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
941 s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(bgOverlay).Background(warningStrong).SetString("WARNING")
942 s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(bgBase).Background(danger).SetString("ERROR")
943 s.Status.SuccessMessage = base.Foreground(bgSubtle).Background(successMuted).Padding(0, 1)
944 s.Status.InfoMessage = s.Status.SuccessMessage
945 s.Status.UpdateMessage = s.Status.SuccessMessage
946 s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(bgOverlay).Background(warning)
947 s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(onAccent).Background(error)
948
949 // Completions styles
950 s.Completions.Normal = base.Background(bgSubtle).Foreground(fgBase)
951 s.Completions.Focused = base.Background(primary).Foreground(onAccent)
952 s.Completions.Match = base.Underline(true)
953
954 // Attachments styles
955 attachmentIconStyle := base.Foreground(bgSubtle).Background(success).Padding(0, 1)
956 s.Attachments.Image = attachmentIconStyle.SetString(ImageIcon)
957 s.Attachments.Text = attachmentIconStyle.SetString(TextIcon)
958 s.Attachments.Normal = base.Padding(0, 1).MarginRight(1).Background(fgMuted).Foreground(fgBase)
959 s.Attachments.Deleting = base.Padding(0, 1).Bold(true).Background(danger).Foreground(fgBase)
960
961 // Pills styles
962 s.Pills.Base = base.Padding(0, 1)
963 s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
964 s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
965 s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString(" •")
966 s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
967 s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
968 s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
969 s.Pills.QueueGradFromColor = error
970 s.Pills.QueueGradToColor = secondary
971 s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
972 s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
973 s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
974 s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(successMuted)
975 s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
976 s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
977 s.Pills.Area = base
978
979 return s
980}