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