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