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