styles.go

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