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
 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}