1// Package styles define styling and theming for the project.
2package styles
3
4import (
5 "fmt"
6 "image/color"
7 "strings"
8
9 "charm.land/bubbles/v2/filepicker"
10 "charm.land/bubbles/v2/help"
11 "charm.land/bubbles/v2/textarea"
12 "charm.land/bubbles/v2/textinput"
13 "charm.land/glamour/v2/ansi"
14 "charm.land/lipgloss/v2"
15 "github.com/alecthomas/chroma/v2"
16 "github.com/charmbracelet/crush/internal/ui/diffview"
17)
18
19const (
20 CheckIcon string = "β"
21 SpinnerIcon string = "β―"
22 LoadingIcon string = "β³"
23 ModelIcon string = "β"
24 HypercreditIcon string = "β"
25
26 ArrowRightIcon string = "β"
27
28 ToolPending string = "β"
29 ToolSuccess string = "β"
30 ToolError string = "Γ"
31
32 RadioOn string = "β"
33 RadioOff string = "β"
34
35 BorderThin string = "β"
36 BorderThick string = "β"
37
38 SectionSeparator string = "β"
39
40 TodoCompletedIcon string = "β"
41 TodoPendingIcon string = "β’"
42 TodoInProgressIcon string = "β"
43
44 ImageIcon string = "β "
45 TextIcon string = "β‘"
46
47 ScrollbarThumb string = "β"
48 ScrollbarTrack string = "β"
49
50 LSPErrorIcon string = "E"
51 LSPWarningIcon string = "W"
52 LSPInfoIcon string = "I"
53 LSPHintIcon string = "H"
54)
55
56const (
57 defaultMargin = 2
58 defaultListIndent = 2
59)
60
61type Styles struct {
62 // Header
63 Header struct {
64 Charm lipgloss.Style // Style for "Charmβ’" label
65 Diagonals lipgloss.Style // Style for diagonal separators (β±)
66 Percentage lipgloss.Style // Style for context percentage
67 Keystroke lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
68 KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
69 WorkingDir lipgloss.Style // Style for current working directory
70 Separator lipgloss.Style // Style for separator dots (β’)
71 Wrapper lipgloss.Style // Outer container for the entire header row
72 LogoGradCanvas lipgloss.Style // Canvas for the compact "CRUSH" gradient
73 LogoGradFromColor color.Color // "CRUSH" wordmark gradient start
74 LogoGradToColor color.Color // "CRUSH" wordmark gradient end
75 }
76
77 CompactDetails struct {
78 View lipgloss.Style
79 Version lipgloss.Style
80 Title lipgloss.Style
81 }
82
83 // Tool calls
84 ToolCallSuccess lipgloss.Style
85
86 // Text selection
87 TextSelection lipgloss.Style
88
89 // Markdown & Chroma
90 Markdown ansi.StyleConfig
91 QuietMarkdown ansi.StyleConfig
92
93 // Inputs
94 TextInput textinput.Styles
95
96 // Help
97 Help help.Styles
98
99 // Diff
100 Diff diffview.Style
101
102 // FilePicker
103 FilePicker filepicker.Styles
104
105 // Buttons
106 Button struct {
107 Focused lipgloss.Style
108 Blurred lipgloss.Style
109 }
110
111 // Editor
112 Editor struct {
113 Textarea textarea.Styles
114
115 // Normal mode prompt (default "::: ").
116 PromptNormalFocused lipgloss.Style
117 PromptNormalBlurred lipgloss.Style
118
119 // YOLO mode prompt (" ! " icon + ":::" dots).
120 PromptYoloIconFocused lipgloss.Style
121 PromptYoloIconBlurred lipgloss.Style
122 PromptYoloDotsFocused lipgloss.Style
123 PromptYoloDotsBlurred lipgloss.Style
124 }
125
126 // Radio
127 Radio struct {
128 On lipgloss.Style
129 Off lipgloss.Style
130 Label lipgloss.Style // Text next to a radio button
131 }
132
133 // Background
134 Background color.Color
135
136 // Logo
137 Logo struct {
138 FieldColor color.Color
139 TitleColorA color.Color
140 TitleColorB color.Color
141 CharmColor color.Color
142 VersionColor color.Color
143 SmallCharm lipgloss.Style // "Charmβ’" label in SmallRender
144 SmallDiagonals lipgloss.Style // Diagonal line fill in SmallRender
145 GradCanvas lipgloss.Style // Blank canvas for gradient painting
146 SmallGradFromColor color.Color // Small "Crush" wordmark gradient start
147 SmallGradToColor color.Color // Small "Crush" wordmark gradient end
148 }
149
150 // Working indicator gradient (spinners/shimmers on assistant "thinking",
151 // tool-call pending, CLI generating, startup).
152 WorkingGradFromColor color.Color
153 WorkingGradToColor color.Color
154 WorkingLabelColor color.Color // Label text color next to the indicator
155
156 // Section Title
157 Section struct {
158 Title lipgloss.Style
159 Line lipgloss.Style
160 }
161
162 // Initialize
163 Initialize struct {
164 Header lipgloss.Style
165 Content lipgloss.Style
166 Accent lipgloss.Style
167 }
168
169 // LSP
170 LSP struct {
171 ErrorDiagnostic lipgloss.Style
172 WarningDiagnostic lipgloss.Style
173 HintDiagnostic lipgloss.Style
174 InfoDiagnostic lipgloss.Style
175 }
176
177 // Sidebar
178 Sidebar struct {
179 SessionTitle lipgloss.Style // Current session title at top of sidebar
180 WorkingDir lipgloss.Style // Working directory path (PrettyPath)
181 }
182
183 // ModelInfo (model name, provider, reasoning, token/cost summary)
184 ModelInfo struct {
185 Icon lipgloss.Style // Model icon (β)
186 Name lipgloss.Style // Model name text
187 Provider lipgloss.Style // "via <provider>" text
188 ProviderFallback lipgloss.Style // Provider on its own second line
189 Reasoning lipgloss.Style // Reasoning effort text
190 TokenCount lipgloss.Style // "(42K)" token count
191 TokenPercentage lipgloss.Style // "42%" percent of context window
192 Cost lipgloss.Style // "$0.42" cost readout
193 HypercreditIcon lipgloss.Style // Hypercredit icon (β)
194 HypercreditText lipgloss.Style // Remaining Hypercredits text
195 }
196
197 // Resource styles the LSP/MCP/skills sidebar lists: their heading,
198 // each row's status icon, name, status text, and truncation hints.
199 Resource struct {
200 Heading lipgloss.Style // Section header ("LSPs", "MCPs", "Skills")
201 Name lipgloss.Style // Resource name (e.g. "gopls")
202 StatusText lipgloss.Style // Row status description (e.g. "starting...")
203 OfflineIcon lipgloss.Style // Offline/unstarted/stopped status icon
204 DisabledIcon lipgloss.Style // Disabled status icon
205 BusyIcon lipgloss.Style // Busy/starting status icon
206 ErrorIcon lipgloss.Style // Error status icon
207 OnlineIcon lipgloss.Style // Online/ready status icon
208 AdditionalText lipgloss.Style // "None" and "β¦and N more" text
209 CapabilityCount lipgloss.Style // "N tools" / "N prompts" / "N resources"
210 RowTitleBase lipgloss.Style // Base style applied over row titles in common.Status
211 RowDescBase lipgloss.Style // Base style applied over row descriptions in common.Status
212 DefaultTitleFg color.Color // Default title color when opt is zero
213 DefaultDescFg color.Color // Default description color when opt is zero
214 }
215
216 // Files
217 Files struct {
218 Path lipgloss.Style
219 Additions lipgloss.Style
220 Deletions lipgloss.Style
221 SectionTitle lipgloss.Style // "Modified Files" heading
222 EmptyMessage lipgloss.Style // "None" placeholder when no files
223 TruncationHint lipgloss.Style // "β¦and N more" message
224 }
225
226 // Chat
227 // Messages - chat message item styles
228 Messages struct {
229 UserBlurred lipgloss.Style
230 UserFocused lipgloss.Style
231 AssistantBlurred lipgloss.Style
232 AssistantFocused lipgloss.Style
233 NoContent lipgloss.Style
234 Thinking lipgloss.Style
235 ErrorTag lipgloss.Style
236 ErrorTitle lipgloss.Style
237 ErrorDetails lipgloss.Style
238 ToolCallFocused lipgloss.Style
239 ToolCallCompact lipgloss.Style
240 ToolCallBlurred lipgloss.Style
241 SectionHeader lipgloss.Style
242
243 // Thinking section styles
244 ThinkingBox lipgloss.Style // Background for thinking content
245 ThinkingTruncationHint lipgloss.Style // "β¦ (N lines hidden)" hint
246 ThinkingFooterTitle lipgloss.Style // "Thought for" text
247 ThinkingFooterDuration lipgloss.Style // Duration value
248 AssistantInfoIcon lipgloss.Style
249 AssistantInfoModel lipgloss.Style
250 AssistantInfoProvider lipgloss.Style
251 AssistantInfoDuration lipgloss.Style
252 AssistantCanceled lipgloss.Style // Italic "Canceled" footer
253 }
254
255 // Tool - styles for tool call rendering
256 Tool struct {
257 // Icon styles with tool status
258 IconPending lipgloss.Style
259 IconSuccess lipgloss.Style
260 IconError lipgloss.Style
261 IconCancelled lipgloss.Style
262
263 // Tool name styles
264 NameNormal lipgloss.Style // Top-level tool name
265 NameNested lipgloss.Style // Nested child tool name (inside Agent/Agentic Fetch)
266
267 // Parameter list styles
268 ParamMain lipgloss.Style
269 ParamKey lipgloss.Style
270
271 // Content rendering styles
272 ContentLine lipgloss.Style // Individual content line with background and width
273 ContentTruncation lipgloss.Style // Truncation message "β¦ (N lines)"
274 ContentCodeLine lipgloss.Style // Code line with background and width
275 ContentCodeTruncation lipgloss.Style // Code truncation message with bgBase
276 ContentCodeBg color.Color // Background color for syntax highlighting
277 Body lipgloss.Style // Body content padding (PaddingLeft(2))
278
279 // Deprecated - kept for backward compatibility
280 ContentBg lipgloss.Style // Content background
281 ContentText lipgloss.Style // Content text
282 ContentLineNumber lipgloss.Style // Line numbers in code
283
284 // State message styles
285 StateWaiting lipgloss.Style // "Waiting for tool response..."
286 StateCancelled lipgloss.Style // "Canceled."
287
288 // Error styles
289 ErrorTag lipgloss.Style // ERROR tag
290 ErrorMessage lipgloss.Style // Error message text
291
292 // Diff styles
293 DiffTruncation lipgloss.Style // Diff truncation message with padding
294
295 // Multi-edit note styles
296 NoteTag lipgloss.Style // NOTE tag (yellow background)
297 NoteMessage lipgloss.Style // Note message text
298
299 // Job header styles (for bash jobs)
300 JobIconPending lipgloss.Style // Pending job icon (green dark)
301 JobIconError lipgloss.Style // Error job icon (red dark)
302 JobIconSuccess lipgloss.Style // Success job icon (green)
303 JobToolName lipgloss.Style // Job tool name "Bash" (blue)
304 JobAction lipgloss.Style // Action text (Start, Output, Kill)
305 JobPID lipgloss.Style // PID text
306 JobDescription lipgloss.Style // Description text
307
308 // Agent task styles
309 AgentTaskTag lipgloss.Style // Agent task tag (blue background, bold)
310 AgentPrompt lipgloss.Style // Agent prompt text
311
312 // Agentic fetch styles
313 AgenticFetchPromptTag lipgloss.Style // Agentic fetch prompt tag (green background, bold)
314
315 // Todo styles
316 TodoRatio lipgloss.Style // Todo ratio (e.g., "2/5")
317 TodoCompletedIcon lipgloss.Style // Completed todo icon
318 TodoInProgressIcon lipgloss.Style // In-progress todo icon
319 TodoPendingIcon lipgloss.Style // Pending todo icon
320 TodoStatusNote lipgloss.Style // " Β· completed N" / " Β· starting task" trailing note
321 TodoItem lipgloss.Style // Default body text for todo list items
322 TodoJustStarted lipgloss.Style // Text of the just-started todo in tool-call bodies
323
324 // MCP tools
325 MCPName lipgloss.Style // The mcp name
326 MCPToolName lipgloss.Style // The mcp tool name
327 MCPArrow lipgloss.Style // The mcp arrow icon
328
329 // Images and external resources
330 ResourceLoadedText lipgloss.Style
331 ResourceLoadedIndicator lipgloss.Style
332 ResourceName lipgloss.Style
333 ResourceSize lipgloss.Style
334 MediaType lipgloss.Style
335
336 // Hooks
337 HookLabel lipgloss.Style // "Hook" label
338 HookName lipgloss.Style // Hook command name
339 HookMatcher lipgloss.Style // Matcher regex pattern
340 HookArrow lipgloss.Style // Arrow indicator
341 HookDetail lipgloss.Style // Decision detail text
342 HookOK lipgloss.Style // "OK" status
343 HookDenied lipgloss.Style // "Denied" status
344 HookDeniedLabel lipgloss.Style // "Hook" label when denied
345 HookDeniedReason lipgloss.Style // Denied reason text
346 HookRewrote lipgloss.Style // "Rewrote Input" indicator
347
348 // Action verb colors for tool-call headers.
349 ActionCreate lipgloss.Style // Constructive actions (e.g. "Add", "Create")
350 ActionDestroy lipgloss.Style // Destructive actions (e.g. "Remove", "Delete")
351
352 // Tool result helpers.
353 ResultEmpty lipgloss.Style // "No results" placeholder
354 ResultTruncation lipgloss.Style // "β¦ and N more" truncation line
355 ResultItemName lipgloss.Style // Item name (left column in result lists)
356 ResultItemDesc lipgloss.Style // Item description (right column)
357 }
358
359 // Dialog styles
360 Dialog struct {
361 Title lipgloss.Style
362 TitleText lipgloss.Style
363 TitleError lipgloss.Style
364 TitleAccent lipgloss.Style
365 TitleLineBase lipgloss.Style // Base for the gradient β±β±β± next to dialog titles
366 TitleGradFromColor color.Color // Default dialog title β±β±β± gradient start
367 TitleGradToColor color.Color // Default dialog title β±β±β± gradient end
368 // View is the main content area style.
369 View lipgloss.Style
370 PrimaryText lipgloss.Style
371 SecondaryText lipgloss.Style
372 // HelpView is the line that contains the help.
373 HelpView lipgloss.Style
374 Help struct {
375 Ellipsis lipgloss.Style
376 ShortKey lipgloss.Style
377 ShortDesc lipgloss.Style
378 ShortSeparator lipgloss.Style
379 FullKey lipgloss.Style
380 FullDesc lipgloss.Style
381 FullSeparator lipgloss.Style
382 }
383
384 NormalItem lipgloss.Style
385 SelectedItem lipgloss.Style
386 InputPrompt lipgloss.Style
387
388 List lipgloss.Style
389
390 Spinner lipgloss.Style
391
392 // ContentPanel is used for content blocks with subtle background.
393 ContentPanel lipgloss.Style
394
395 // Scrollbar styles for scrollable content.
396 ScrollbarThumb lipgloss.Style
397 ScrollbarTrack lipgloss.Style
398
399 // Arguments
400 Arguments struct {
401 Content lipgloss.Style
402 Description lipgloss.Style
403 InputLabelBlurred lipgloss.Style
404 InputLabelFocused lipgloss.Style
405 InputRequiredMarkBlurred lipgloss.Style
406 InputRequiredMarkFocused lipgloss.Style
407 }
408
409 // ListItem styles the info-text rendered alongside list items (commands,
410 // models, reasoning options). Sessions have their own overrides below.
411 ListItem struct {
412 InfoBlurred lipgloss.Style
413 InfoFocused lipgloss.Style
414 }
415
416 Models struct {
417 ConfiguredText lipgloss.Style // "Configured" badge shown on the ModelGroup header
418 }
419
420 Permissions struct {
421 KeyText lipgloss.Style // Left key cell of a key/value row
422 ValueText lipgloss.Style // Right value cell of a key/value row
423 ParamsBg color.Color // Background color behind highlighted JSON parameters
424 }
425
426 Quit struct {
427 Content lipgloss.Style // Wrapper for the quit dialog's inner content
428 Frame lipgloss.Style // Outer rounded border framing the quit dialog
429 }
430
431 APIKey struct {
432 Spinner lipgloss.Style // Loading spinner while validating the key
433 }
434
435 OAuth struct {
436 Spinner lipgloss.Style // Loading spinner
437 Instructions lipgloss.Style // Emphasized instruction text
438 UserCode lipgloss.Style // Prominent user code display
439 Success lipgloss.Style // Positive status text (e.g. "Authentication successful!")
440 Link lipgloss.Style // Underlined verification URL
441 Enter lipgloss.Style // "enter" keyword highlight in instructions
442 ErrorText lipgloss.Style // Error message when authentication fails
443 StatusText lipgloss.Style // Narrative status text ("Initializing...", "Verifying...", etc.)
444 UserCodeBg color.Color // Background color of the centered user-code box
445 }
446
447 ImagePreview lipgloss.Style
448
449 Sessions struct {
450 // styles for when we are in delete mode
451 DeletingView lipgloss.Style
452 DeletingItemFocused lipgloss.Style
453 DeletingItemBlurred lipgloss.Style
454 DeletingTitle lipgloss.Style
455 DeletingMessage lipgloss.Style
456 DeletingTitleGradientFromColor color.Color
457 DeletingTitleGradientToColor color.Color
458
459 // styles for when we are in update mode
460 RenamingView lipgloss.Style
461 RenamingingItemFocused lipgloss.Style
462 RenamingItemBlurred lipgloss.Style
463 RenamingingTitle lipgloss.Style
464 RenamingingMessage lipgloss.Style
465 RenamingTitleGradientFromColor color.Color
466 RenamingTitleGradientToColor color.Color
467 RenamingPlaceholder lipgloss.Style
468
469 InfoBlurred lipgloss.Style // Timestamp text on unfocused session items
470 InfoFocused lipgloss.Style // Timestamp text on the focused session item
471 }
472 }
473
474 // Status bar and help
475 Status struct {
476 Help lipgloss.Style
477
478 ErrorIndicator lipgloss.Style
479 WarnIndicator lipgloss.Style
480 InfoIndicator lipgloss.Style
481 UpdateIndicator lipgloss.Style
482 SuccessIndicator lipgloss.Style
483
484 ErrorMessage lipgloss.Style
485 WarnMessage lipgloss.Style
486 InfoMessage lipgloss.Style
487 UpdateMessage lipgloss.Style
488 SuccessMessage lipgloss.Style
489 }
490
491 // Completions popup styles
492 Completions struct {
493 Normal lipgloss.Style
494 Focused lipgloss.Style
495 Match lipgloss.Style
496 }
497
498 // Attachments styles
499 Attachments struct {
500 Normal lipgloss.Style
501 Image lipgloss.Style
502 Text lipgloss.Style
503 Deleting lipgloss.Style
504 }
505
506 // Pills styles for todo/queue pills
507 Pills struct {
508 Base lipgloss.Style // Base pill style with padding
509 Focused lipgloss.Style // Focused pill with visible border
510 Blurred lipgloss.Style // Blurred pill with hidden border
511 QueueItemPrefix lipgloss.Style // Prefix for queue list items
512 QueueItemText lipgloss.Style // Queue list item body text
513 QueueLabel lipgloss.Style // "N Queued" label text
514 QueueIconBase lipgloss.Style // Base style for queue gradient triangles
515 QueueGradFromColor color.Color // Start color for queue indicator gradient
516 QueueGradToColor color.Color // End color for queue indicator gradient
517 TodoLabel lipgloss.Style // "To-Do" label
518 TodoProgress lipgloss.Style // Todo ratio (e.g. "2/5")
519 TodoCurrentTask lipgloss.Style // Current in-progress task name
520 TodoSpinner lipgloss.Style // Todo spinner style
521 HelpKey lipgloss.Style // Keystroke hint style
522 HelpText lipgloss.Style // Help action text style
523 Area lipgloss.Style // Pills area container
524 }
525}
526
527// ChromaTheme converts the current markdown chroma styles to a chroma
528// StyleEntries map.
529func (s *Styles) ChromaTheme() chroma.StyleEntries {
530 rules := s.Markdown.CodeBlock
531
532 return chroma.StyleEntries{
533 chroma.Text: chromaStyle(rules.Chroma.Text),
534 chroma.Error: chromaStyle(rules.Chroma.Error),
535 chroma.Comment: chromaStyle(rules.Chroma.Comment),
536 chroma.CommentPreproc: chromaStyle(rules.Chroma.CommentPreproc),
537 chroma.Keyword: chromaStyle(rules.Chroma.Keyword),
538 chroma.KeywordReserved: chromaStyle(rules.Chroma.KeywordReserved),
539 chroma.KeywordNamespace: chromaStyle(rules.Chroma.KeywordNamespace),
540 chroma.KeywordType: chromaStyle(rules.Chroma.KeywordType),
541 chroma.Operator: chromaStyle(rules.Chroma.Operator),
542 chroma.Punctuation: chromaStyle(rules.Chroma.Punctuation),
543 chroma.Name: chromaStyle(rules.Chroma.Name),
544 chroma.NameBuiltin: chromaStyle(rules.Chroma.NameBuiltin),
545 chroma.NameTag: chromaStyle(rules.Chroma.NameTag),
546 chroma.NameAttribute: chromaStyle(rules.Chroma.NameAttribute),
547 chroma.NameClass: chromaStyle(rules.Chroma.NameClass),
548 chroma.NameConstant: chromaStyle(rules.Chroma.NameConstant),
549 chroma.NameDecorator: chromaStyle(rules.Chroma.NameDecorator),
550 chroma.NameException: chromaStyle(rules.Chroma.NameException),
551 chroma.NameFunction: chromaStyle(rules.Chroma.NameFunction),
552 chroma.NameOther: chromaStyle(rules.Chroma.NameOther),
553 chroma.Literal: chromaStyle(rules.Chroma.Literal),
554 chroma.LiteralNumber: chromaStyle(rules.Chroma.LiteralNumber),
555 chroma.LiteralDate: chromaStyle(rules.Chroma.LiteralDate),
556 chroma.LiteralString: chromaStyle(rules.Chroma.LiteralString),
557 chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
558 chroma.GenericDeleted: chromaStyle(rules.Chroma.GenericDeleted),
559 chroma.GenericEmph: chromaStyle(rules.Chroma.GenericEmph),
560 chroma.GenericInserted: chromaStyle(rules.Chroma.GenericInserted),
561 chroma.GenericStrong: chromaStyle(rules.Chroma.GenericStrong),
562 chroma.GenericSubheading: chromaStyle(rules.Chroma.GenericSubheading),
563 chroma.Background: chromaStyle(rules.Chroma.Background),
564 }
565}
566
567// DialogHelpStyles returns the styles for dialog help.
568func (s *Styles) DialogHelpStyles() help.Styles {
569 return help.Styles(s.Dialog.Help)
570}
571
572// hex returns a pointer to the "#rrggbb" representation of c. It's used to
573// satisfy glamour's string-pointer API when configuring markdown colors
574// from the theme palette.
575func hex(c color.Color) *string {
576 r, g, b, _ := c.RGBA()
577 s := fmt.Sprintf("#%02x%02x%02x", r>>8, g>>8, b>>8)
578 return &s
579}
580
581func chromaStyle(style ansi.StylePrimitive) string {
582 var s strings.Builder
583
584 if style.Color != nil {
585 s.WriteString(*style.Color)
586 }
587 if style.BackgroundColor != nil {
588 if s.Len() > 0 {
589 s.WriteString(" ")
590 }
591 s.WriteString("bg:")
592 s.WriteString(*style.BackgroundColor)
593 }
594 if style.Italic != nil && *style.Italic {
595 if s.Len() > 0 {
596 s.WriteString(" ")
597 }
598 s.WriteString("italic")
599 }
600 if style.Bold != nil && *style.Bold {
601 if s.Len() > 0 {
602 s.WriteString(" ")
603 }
604 s.WriteString("bold")
605 }
606 if style.Underline != nil && *style.Underline {
607 if s.Len() > 0 {
608 s.WriteString(" ")
609 }
610 s.WriteString("underline")
611 }
612
613 return s.String()
614}