styles.go

   1package styles
   2
   3import (
   4	"image/color"
   5
   6	"charm.land/bubbles/v2/filepicker"
   7	"charm.land/bubbles/v2/help"
   8	"charm.land/bubbles/v2/textarea"
   9	"charm.land/bubbles/v2/textinput"
  10	tea "charm.land/bubbletea/v2"
  11	"charm.land/glamour/v2/ansi"
  12	"charm.land/lipgloss/v2"
  13	"github.com/alecthomas/chroma/v2"
  14	"github.com/charmbracelet/crush/internal/tui/exp/diffview"
  15	"github.com/charmbracelet/x/exp/charmtone"
  16)
  17
  18const (
  19	CheckIcon    string = "✓"
  20	ErrorIcon    string = "×"
  21	WarningIcon  string = "⚠"
  22	InfoIcon     string = "ⓘ"
  23	HintIcon     string = "∵"
  24	SpinnerIcon  string = "..."
  25	LoadingIcon  string = "⟳"
  26	DocumentIcon string = "🖼"
  27	ModelIcon    string = "◇"
  28
  29	ToolPending string = "●"
  30	ToolSuccess string = "✓"
  31	ToolError   string = "×"
  32
  33	BorderThin  string = "│"
  34	BorderThick string = "▌"
  35
  36	SectionSeparator string = "─"
  37)
  38
  39const (
  40	defaultMargin     = 2
  41	defaultListIndent = 2
  42)
  43
  44type Styles struct {
  45	WindowTooSmall lipgloss.Style
  46
  47	// Reusable text styles
  48	Base   lipgloss.Style
  49	Muted  lipgloss.Style
  50	Subtle lipgloss.Style
  51
  52	// Tags
  53	TagBase  lipgloss.Style
  54	TagError lipgloss.Style
  55	TagInfo  lipgloss.Style
  56
  57	// Headers
  58	HeaderTool       lipgloss.Style
  59	HeaderToolNested lipgloss.Style
  60
  61	// Panels
  62	PanelMuted lipgloss.Style
  63	PanelBase  lipgloss.Style
  64
  65	// Line numbers for code blocks
  66	LineNumber lipgloss.Style
  67
  68	// Message borders
  69	FocusedMessageBorder lipgloss.Border
  70
  71	// Tool calls
  72	ToolCallPending   lipgloss.Style
  73	ToolCallError     lipgloss.Style
  74	ToolCallSuccess   lipgloss.Style
  75	ToolCallCancelled lipgloss.Style
  76	EarlyStateMessage lipgloss.Style
  77
  78	// Text selection
  79	TextSelection lipgloss.Style
  80
  81	// LSP and MCP status indicators
  82	ItemOfflineIcon lipgloss.Style
  83	ItemBusyIcon    lipgloss.Style
  84	ItemErrorIcon   lipgloss.Style
  85	ItemOnlineIcon  lipgloss.Style
  86
  87	// Markdown & Chroma
  88	Markdown      ansi.StyleConfig
  89	PlainMarkdown ansi.StyleConfig
  90
  91	// Inputs
  92	TextInput textinput.Styles
  93	TextArea  textarea.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	ButtonFocus lipgloss.Style
 106	ButtonBlur  lipgloss.Style
 107
 108	// Borders
 109	BorderFocus lipgloss.Style
 110	BorderBlur  lipgloss.Style
 111
 112	// Editor
 113	EditorPromptNormalFocused   lipgloss.Style
 114	EditorPromptNormalBlurred   lipgloss.Style
 115	EditorPromptYoloIconFocused lipgloss.Style
 116	EditorPromptYoloIconBlurred lipgloss.Style
 117	EditorPromptYoloDotsFocused lipgloss.Style
 118	EditorPromptYoloDotsBlurred lipgloss.Style
 119
 120	// Background
 121	Background color.Color
 122
 123	// Logo
 124	LogoFieldColor   color.Color
 125	LogoTitleColorA  color.Color
 126	LogoTitleColorB  color.Color
 127	LogoCharmColor   color.Color
 128	LogoVersionColor color.Color
 129
 130	// Colors - semantic colors for tool rendering.
 131	Primary       color.Color
 132	Secondary     color.Color
 133	Tertiary      color.Color
 134	BgBase        color.Color
 135	BgBaseLighter color.Color
 136	BgSubtle      color.Color
 137	BgOverlay     color.Color
 138	FgBase        color.Color
 139	FgMuted       color.Color
 140	FgHalfMuted   color.Color
 141	FgSubtle      color.Color
 142	Border        color.Color
 143	BorderColor   color.Color // Border focus color
 144	Warning       color.Color
 145	Info          color.Color
 146	White         color.Color
 147	BlueLight     color.Color
 148	Blue          color.Color
 149	Green         color.Color
 150	GreenDark     color.Color
 151	Red           color.Color
 152	RedDark       color.Color
 153	Yellow        color.Color
 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	// Files
 177	Files struct {
 178		Path      lipgloss.Style
 179		Additions lipgloss.Style
 180		Deletions lipgloss.Style
 181	}
 182
 183	// Chat
 184	Chat struct {
 185		// Message item styles
 186		Message struct {
 187			UserBlurred      lipgloss.Style
 188			UserFocused      lipgloss.Style
 189			AssistantBlurred lipgloss.Style
 190			AssistantFocused lipgloss.Style
 191			NoContent        lipgloss.Style
 192			Thinking         lipgloss.Style
 193			ErrorTag         lipgloss.Style
 194			ErrorTitle       lipgloss.Style
 195			ErrorDetails     lipgloss.Style
 196			Attachment       lipgloss.Style
 197			ToolCallFocused  lipgloss.Style
 198			ToolCallBlurred  lipgloss.Style
 199			SectionHeader    lipgloss.Style
 200
 201			// Thinking section styles
 202			ThinkingBox            lipgloss.Style // Background for thinking content
 203			ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
 204			ThinkingFooterTitle    lipgloss.Style // "Thought for" text
 205			ThinkingFooterDuration lipgloss.Style // Duration value
 206		}
 207	}
 208
 209	// Tool - styles for tool call rendering
 210	Tool struct {
 211		// Icon styles with tool status
 212		IconPending   lipgloss.Style // Pending operation icon
 213		IconSuccess   lipgloss.Style // Successful operation icon
 214		IconError     lipgloss.Style // Error operation icon
 215		IconCancelled lipgloss.Style // Cancelled operation icon
 216
 217		// Tool name styles
 218		NameNormal lipgloss.Style // Normal tool name
 219		NameNested lipgloss.Style // Nested tool name
 220
 221		// Parameter list styles
 222		ParamMain lipgloss.Style // Main parameter
 223		ParamKey  lipgloss.Style // Parameter keys
 224
 225		// Content rendering styles
 226		ContentLine       lipgloss.Style // Individual content line with background and width
 227		ContentTruncation lipgloss.Style // Truncation message "… (N lines)"
 228		ContentCodeLine   lipgloss.Style // Code line with background and width
 229		ContentCodeBg     color.Color    // Background color for syntax highlighting
 230		BodyPadding       lipgloss.Style // Body content padding (PaddingLeft(2))
 231
 232		// Deprecated - kept for backward compatibility
 233		ContentBg         lipgloss.Style // Content background
 234		ContentText       lipgloss.Style // Content text
 235		ContentLineNumber lipgloss.Style // Line numbers in code
 236
 237		// State message styles
 238		StateWaiting   lipgloss.Style // "Waiting for tool response..."
 239		StateCancelled lipgloss.Style // "Canceled."
 240
 241		// Error styles
 242		ErrorTag     lipgloss.Style // ERROR tag
 243		ErrorMessage lipgloss.Style // Error message text
 244
 245		// Diff styles
 246		DiffTruncation lipgloss.Style // Diff truncation message with padding
 247
 248		// Multi-edit note styles
 249		NoteTag     lipgloss.Style // NOTE tag (yellow background)
 250		NoteMessage lipgloss.Style // Note message text
 251
 252		// Job header styles (for bash jobs)
 253		JobIconPending lipgloss.Style // Pending job icon (green dark)
 254		JobIconError   lipgloss.Style // Error job icon (red dark)
 255		JobIconSuccess lipgloss.Style // Success job icon (green)
 256		JobToolName    lipgloss.Style // Job tool name "Bash" (blue)
 257		JobAction      lipgloss.Style // Action text (Start, Output, Kill)
 258		JobPID         lipgloss.Style // PID text
 259		JobDescription lipgloss.Style // Description text
 260
 261		// Agent task styles
 262		AgentTaskTag lipgloss.Style // Agent task tag (blue background, bold)
 263		AgentPrompt  lipgloss.Style // Agent prompt text
 264	}
 265
 266	// Dialog styles
 267	Dialog struct {
 268		Title lipgloss.Style
 269		// View is the main content area style.
 270		View lipgloss.Style
 271		// HelpView is the line that contains the help.
 272		HelpView lipgloss.Style
 273		Help     struct {
 274			Ellipsis       lipgloss.Style
 275			ShortKey       lipgloss.Style
 276			ShortDesc      lipgloss.Style
 277			ShortSeparator lipgloss.Style
 278			FullKey        lipgloss.Style
 279			FullDesc       lipgloss.Style
 280			FullSeparator  lipgloss.Style
 281		}
 282		NormalItem   lipgloss.Style
 283		SelectedItem lipgloss.Style
 284		InputPrompt  lipgloss.Style
 285
 286		List lipgloss.Style
 287
 288		Commands struct {
 289			CommandTypeSelector lipgloss.Style
 290		}
 291	}
 292}
 293
 294// ChromaTheme converts the current markdown chroma styles to a chroma
 295// StyleEntries map.
 296func (s *Styles) ChromaTheme() chroma.StyleEntries {
 297	rules := s.Markdown.CodeBlock
 298
 299	return chroma.StyleEntries{
 300		chroma.Text:                chromaStyle(rules.Chroma.Text),
 301		chroma.Error:               chromaStyle(rules.Chroma.Error),
 302		chroma.Comment:             chromaStyle(rules.Chroma.Comment),
 303		chroma.CommentPreproc:      chromaStyle(rules.Chroma.CommentPreproc),
 304		chroma.Keyword:             chromaStyle(rules.Chroma.Keyword),
 305		chroma.KeywordReserved:     chromaStyle(rules.Chroma.KeywordReserved),
 306		chroma.KeywordNamespace:    chromaStyle(rules.Chroma.KeywordNamespace),
 307		chroma.KeywordType:         chromaStyle(rules.Chroma.KeywordType),
 308		chroma.Operator:            chromaStyle(rules.Chroma.Operator),
 309		chroma.Punctuation:         chromaStyle(rules.Chroma.Punctuation),
 310		chroma.Name:                chromaStyle(rules.Chroma.Name),
 311		chroma.NameBuiltin:         chromaStyle(rules.Chroma.NameBuiltin),
 312		chroma.NameTag:             chromaStyle(rules.Chroma.NameTag),
 313		chroma.NameAttribute:       chromaStyle(rules.Chroma.NameAttribute),
 314		chroma.NameClass:           chromaStyle(rules.Chroma.NameClass),
 315		chroma.NameConstant:        chromaStyle(rules.Chroma.NameConstant),
 316		chroma.NameDecorator:       chromaStyle(rules.Chroma.NameDecorator),
 317		chroma.NameException:       chromaStyle(rules.Chroma.NameException),
 318		chroma.NameFunction:        chromaStyle(rules.Chroma.NameFunction),
 319		chroma.NameOther:           chromaStyle(rules.Chroma.NameOther),
 320		chroma.Literal:             chromaStyle(rules.Chroma.Literal),
 321		chroma.LiteralNumber:       chromaStyle(rules.Chroma.LiteralNumber),
 322		chroma.LiteralDate:         chromaStyle(rules.Chroma.LiteralDate),
 323		chroma.LiteralString:       chromaStyle(rules.Chroma.LiteralString),
 324		chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
 325		chroma.GenericDeleted:      chromaStyle(rules.Chroma.GenericDeleted),
 326		chroma.GenericEmph:         chromaStyle(rules.Chroma.GenericEmph),
 327		chroma.GenericInserted:     chromaStyle(rules.Chroma.GenericInserted),
 328		chroma.GenericStrong:       chromaStyle(rules.Chroma.GenericStrong),
 329		chroma.GenericSubheading:   chromaStyle(rules.Chroma.GenericSubheading),
 330		chroma.Background:          chromaStyle(rules.Chroma.Background),
 331	}
 332}
 333
 334// DialogHelpStyles returns the styles for dialog help.
 335func (s *Styles) DialogHelpStyles() help.Styles {
 336	return help.Styles(s.Dialog.Help)
 337}
 338
 339// DefaultStyles returns the default styles for the UI.
 340func DefaultStyles() Styles {
 341	var (
 342		primary   = charmtone.Charple
 343		secondary = charmtone.Dolly
 344		tertiary  = charmtone.Bok
 345		// accent    = charmtone.Zest
 346
 347		// Backgrounds
 348		bgBase        = charmtone.Pepper
 349		bgBaseLighter = charmtone.BBQ
 350		bgSubtle      = charmtone.Charcoal
 351		bgOverlay     = charmtone.Iron
 352
 353		// Foregrounds
 354		fgBase      = charmtone.Ash
 355		fgMuted     = charmtone.Squid
 356		fgHalfMuted = charmtone.Smoke
 357		fgSubtle    = charmtone.Oyster
 358		// fgSelected  = charmtone.Salt
 359
 360		// Borders
 361		border      = charmtone.Charcoal
 362		borderFocus = charmtone.Charple
 363
 364		// Status
 365		warning = charmtone.Zest
 366		info    = charmtone.Malibu
 367
 368		// Colors
 369		white = charmtone.Butter
 370
 371		blueLight = charmtone.Sardine
 372		blue      = charmtone.Malibu
 373
 374		// yellow = charmtone.Mustard
 375		yellow = charmtone.Mustard
 376		// citron = charmtone.Citron
 377
 378		green     = charmtone.Julep
 379		greenDark = charmtone.Guac
 380		// greenLight = charmtone.Bok
 381
 382		red     = charmtone.Coral
 383		redDark = charmtone.Sriracha
 384		// redLight = charmtone.Salmon
 385		// cherry   = charmtone.Cherry
 386	)
 387
 388	normalBorder := lipgloss.NormalBorder()
 389
 390	base := lipgloss.NewStyle().Foreground(fgBase)
 391
 392	s := Styles{}
 393
 394	s.Background = bgBase
 395
 396	// Populate color fields
 397	s.Primary = primary
 398	s.Secondary = secondary
 399	s.Tertiary = tertiary
 400	s.BgBase = bgBase
 401	s.BgBaseLighter = bgBaseLighter
 402	s.BgSubtle = bgSubtle
 403	s.BgOverlay = bgOverlay
 404	s.FgBase = fgBase
 405	s.FgMuted = fgMuted
 406	s.FgHalfMuted = fgHalfMuted
 407	s.FgSubtle = fgSubtle
 408	s.Border = border
 409	s.BorderColor = borderFocus
 410	s.Warning = warning
 411	s.Info = info
 412	s.White = white
 413	s.BlueLight = blueLight
 414	s.Blue = blue
 415	s.Green = green
 416	s.GreenDark = greenDark
 417	s.Red = red
 418	s.RedDark = redDark
 419	s.Yellow = yellow
 420
 421	s.TextInput = textinput.Styles{
 422		Focused: textinput.StyleState{
 423			Text:        base,
 424			Placeholder: base.Foreground(fgSubtle),
 425			Prompt:      base.Foreground(tertiary),
 426			Suggestion:  base.Foreground(fgSubtle),
 427		},
 428		Blurred: textinput.StyleState{
 429			Text:        base.Foreground(fgMuted),
 430			Placeholder: base.Foreground(fgSubtle),
 431			Prompt:      base.Foreground(fgMuted),
 432			Suggestion:  base.Foreground(fgSubtle),
 433		},
 434		Cursor: textinput.CursorStyle{
 435			Color: secondary,
 436			Shape: tea.CursorBlock,
 437			Blink: true,
 438		},
 439	}
 440
 441	s.TextArea = textarea.Styles{
 442		Focused: textarea.StyleState{
 443			Base:             base,
 444			Text:             base,
 445			LineNumber:       base.Foreground(fgSubtle),
 446			CursorLine:       base,
 447			CursorLineNumber: base.Foreground(fgSubtle),
 448			Placeholder:      base.Foreground(fgSubtle),
 449			Prompt:           base.Foreground(tertiary),
 450		},
 451		Blurred: textarea.StyleState{
 452			Base:             base,
 453			Text:             base.Foreground(fgMuted),
 454			LineNumber:       base.Foreground(fgMuted),
 455			CursorLine:       base,
 456			CursorLineNumber: base.Foreground(fgMuted),
 457			Placeholder:      base.Foreground(fgSubtle),
 458			Prompt:           base.Foreground(fgMuted),
 459		},
 460		Cursor: textarea.CursorStyle{
 461			Color: secondary,
 462			Shape: tea.CursorBlock,
 463			Blink: true,
 464		},
 465	}
 466
 467	s.Markdown = ansi.StyleConfig{
 468		Document: ansi.StyleBlock{
 469			StylePrimitive: ansi.StylePrimitive{
 470				// BlockPrefix: "\n",
 471				// BlockSuffix: "\n",
 472				Color: stringPtr(charmtone.Smoke.Hex()),
 473			},
 474			// Margin: uintPtr(defaultMargin),
 475		},
 476		BlockQuote: ansi.StyleBlock{
 477			StylePrimitive: ansi.StylePrimitive{},
 478			Indent:         uintPtr(1),
 479			IndentToken:    stringPtr("│ "),
 480		},
 481		List: ansi.StyleList{
 482			LevelIndent: defaultListIndent,
 483		},
 484		Heading: ansi.StyleBlock{
 485			StylePrimitive: ansi.StylePrimitive{
 486				BlockSuffix: "\n",
 487				Color:       stringPtr(charmtone.Malibu.Hex()),
 488				Bold:        boolPtr(true),
 489			},
 490		},
 491		H1: ansi.StyleBlock{
 492			StylePrimitive: ansi.StylePrimitive{
 493				Prefix:          " ",
 494				Suffix:          " ",
 495				Color:           stringPtr(charmtone.Zest.Hex()),
 496				BackgroundColor: stringPtr(charmtone.Charple.Hex()),
 497				Bold:            boolPtr(true),
 498			},
 499		},
 500		H2: ansi.StyleBlock{
 501			StylePrimitive: ansi.StylePrimitive{
 502				Prefix: "## ",
 503			},
 504		},
 505		H3: ansi.StyleBlock{
 506			StylePrimitive: ansi.StylePrimitive{
 507				Prefix: "### ",
 508			},
 509		},
 510		H4: ansi.StyleBlock{
 511			StylePrimitive: ansi.StylePrimitive{
 512				Prefix: "#### ",
 513			},
 514		},
 515		H5: ansi.StyleBlock{
 516			StylePrimitive: ansi.StylePrimitive{
 517				Prefix: "##### ",
 518			},
 519		},
 520		H6: ansi.StyleBlock{
 521			StylePrimitive: ansi.StylePrimitive{
 522				Prefix: "###### ",
 523				Color:  stringPtr(charmtone.Guac.Hex()),
 524				Bold:   boolPtr(false),
 525			},
 526		},
 527		Strikethrough: ansi.StylePrimitive{
 528			CrossedOut: boolPtr(true),
 529		},
 530		Emph: ansi.StylePrimitive{
 531			Italic: boolPtr(true),
 532		},
 533		Strong: ansi.StylePrimitive{
 534			Bold: boolPtr(true),
 535		},
 536		HorizontalRule: ansi.StylePrimitive{
 537			Color:  stringPtr(charmtone.Charcoal.Hex()),
 538			Format: "\n--------\n",
 539		},
 540		Item: ansi.StylePrimitive{
 541			BlockPrefix: "• ",
 542		},
 543		Enumeration: ansi.StylePrimitive{
 544			BlockPrefix: ". ",
 545		},
 546		Task: ansi.StyleTask{
 547			StylePrimitive: ansi.StylePrimitive{},
 548			Ticked:         "[✓] ",
 549			Unticked:       "[ ] ",
 550		},
 551		Link: ansi.StylePrimitive{
 552			Color:     stringPtr(charmtone.Zinc.Hex()),
 553			Underline: boolPtr(true),
 554		},
 555		LinkText: ansi.StylePrimitive{
 556			Color: stringPtr(charmtone.Guac.Hex()),
 557			Bold:  boolPtr(true),
 558		},
 559		Image: ansi.StylePrimitive{
 560			Color:     stringPtr(charmtone.Cheeky.Hex()),
 561			Underline: boolPtr(true),
 562		},
 563		ImageText: ansi.StylePrimitive{
 564			Color:  stringPtr(charmtone.Squid.Hex()),
 565			Format: "Image: {{.text}} →",
 566		},
 567		Code: ansi.StyleBlock{
 568			StylePrimitive: ansi.StylePrimitive{
 569				Prefix:          " ",
 570				Suffix:          " ",
 571				Color:           stringPtr(charmtone.Coral.Hex()),
 572				BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
 573			},
 574		},
 575		CodeBlock: ansi.StyleCodeBlock{
 576			StyleBlock: ansi.StyleBlock{
 577				StylePrimitive: ansi.StylePrimitive{
 578					Color: stringPtr(charmtone.Charcoal.Hex()),
 579				},
 580				Margin: uintPtr(defaultMargin),
 581			},
 582			Chroma: &ansi.Chroma{
 583				Text: ansi.StylePrimitive{
 584					Color: stringPtr(charmtone.Smoke.Hex()),
 585				},
 586				Error: ansi.StylePrimitive{
 587					Color:           stringPtr(charmtone.Butter.Hex()),
 588					BackgroundColor: stringPtr(charmtone.Sriracha.Hex()),
 589				},
 590				Comment: ansi.StylePrimitive{
 591					Color: stringPtr(charmtone.Oyster.Hex()),
 592				},
 593				CommentPreproc: ansi.StylePrimitive{
 594					Color: stringPtr(charmtone.Bengal.Hex()),
 595				},
 596				Keyword: ansi.StylePrimitive{
 597					Color: stringPtr(charmtone.Malibu.Hex()),
 598				},
 599				KeywordReserved: ansi.StylePrimitive{
 600					Color: stringPtr(charmtone.Pony.Hex()),
 601				},
 602				KeywordNamespace: ansi.StylePrimitive{
 603					Color: stringPtr(charmtone.Pony.Hex()),
 604				},
 605				KeywordType: ansi.StylePrimitive{
 606					Color: stringPtr(charmtone.Guppy.Hex()),
 607				},
 608				Operator: ansi.StylePrimitive{
 609					Color: stringPtr(charmtone.Salmon.Hex()),
 610				},
 611				Punctuation: ansi.StylePrimitive{
 612					Color: stringPtr(charmtone.Zest.Hex()),
 613				},
 614				Name: ansi.StylePrimitive{
 615					Color: stringPtr(charmtone.Smoke.Hex()),
 616				},
 617				NameBuiltin: ansi.StylePrimitive{
 618					Color: stringPtr(charmtone.Cheeky.Hex()),
 619				},
 620				NameTag: ansi.StylePrimitive{
 621					Color: stringPtr(charmtone.Mauve.Hex()),
 622				},
 623				NameAttribute: ansi.StylePrimitive{
 624					Color: stringPtr(charmtone.Hazy.Hex()),
 625				},
 626				NameClass: ansi.StylePrimitive{
 627					Color:     stringPtr(charmtone.Salt.Hex()),
 628					Underline: boolPtr(true),
 629					Bold:      boolPtr(true),
 630				},
 631				NameDecorator: ansi.StylePrimitive{
 632					Color: stringPtr(charmtone.Citron.Hex()),
 633				},
 634				NameFunction: ansi.StylePrimitive{
 635					Color: stringPtr(charmtone.Guac.Hex()),
 636				},
 637				LiteralNumber: ansi.StylePrimitive{
 638					Color: stringPtr(charmtone.Julep.Hex()),
 639				},
 640				LiteralString: ansi.StylePrimitive{
 641					Color: stringPtr(charmtone.Cumin.Hex()),
 642				},
 643				LiteralStringEscape: ansi.StylePrimitive{
 644					Color: stringPtr(charmtone.Bok.Hex()),
 645				},
 646				GenericDeleted: ansi.StylePrimitive{
 647					Color: stringPtr(charmtone.Coral.Hex()),
 648				},
 649				GenericEmph: ansi.StylePrimitive{
 650					Italic: boolPtr(true),
 651				},
 652				GenericInserted: ansi.StylePrimitive{
 653					Color: stringPtr(charmtone.Guac.Hex()),
 654				},
 655				GenericStrong: ansi.StylePrimitive{
 656					Bold: boolPtr(true),
 657				},
 658				GenericSubheading: ansi.StylePrimitive{
 659					Color: stringPtr(charmtone.Squid.Hex()),
 660				},
 661				Background: ansi.StylePrimitive{
 662					BackgroundColor: stringPtr(charmtone.Charcoal.Hex()),
 663				},
 664			},
 665		},
 666		Table: ansi.StyleTable{
 667			StyleBlock: ansi.StyleBlock{
 668				StylePrimitive: ansi.StylePrimitive{},
 669			},
 670		},
 671		DefinitionDescription: ansi.StylePrimitive{
 672			BlockPrefix: "\n ",
 673		},
 674	}
 675
 676	// PlainMarkdown style - muted colors on subtle background for thinking content.
 677	plainBg := stringPtr(bgBaseLighter.Hex())
 678	plainFg := stringPtr(fgMuted.Hex())
 679	s.PlainMarkdown = ansi.StyleConfig{
 680		Document: ansi.StyleBlock{
 681			StylePrimitive: ansi.StylePrimitive{
 682				Color:           plainFg,
 683				BackgroundColor: plainBg,
 684			},
 685		},
 686		BlockQuote: ansi.StyleBlock{
 687			StylePrimitive: ansi.StylePrimitive{
 688				Color:           plainFg,
 689				BackgroundColor: plainBg,
 690			},
 691			Indent:      uintPtr(1),
 692			IndentToken: stringPtr("│ "),
 693		},
 694		List: ansi.StyleList{
 695			LevelIndent: defaultListIndent,
 696		},
 697		Heading: ansi.StyleBlock{
 698			StylePrimitive: ansi.StylePrimitive{
 699				BlockSuffix:     "\n",
 700				Bold:            boolPtr(true),
 701				Color:           plainFg,
 702				BackgroundColor: plainBg,
 703			},
 704		},
 705		H1: ansi.StyleBlock{
 706			StylePrimitive: ansi.StylePrimitive{
 707				Prefix:          " ",
 708				Suffix:          " ",
 709				Bold:            boolPtr(true),
 710				Color:           plainFg,
 711				BackgroundColor: plainBg,
 712			},
 713		},
 714		H2: ansi.StyleBlock{
 715			StylePrimitive: ansi.StylePrimitive{
 716				Prefix:          "## ",
 717				Color:           plainFg,
 718				BackgroundColor: plainBg,
 719			},
 720		},
 721		H3: ansi.StyleBlock{
 722			StylePrimitive: ansi.StylePrimitive{
 723				Prefix:          "### ",
 724				Color:           plainFg,
 725				BackgroundColor: plainBg,
 726			},
 727		},
 728		H4: ansi.StyleBlock{
 729			StylePrimitive: ansi.StylePrimitive{
 730				Prefix:          "#### ",
 731				Color:           plainFg,
 732				BackgroundColor: plainBg,
 733			},
 734		},
 735		H5: ansi.StyleBlock{
 736			StylePrimitive: ansi.StylePrimitive{
 737				Prefix:          "##### ",
 738				Color:           plainFg,
 739				BackgroundColor: plainBg,
 740			},
 741		},
 742		H6: ansi.StyleBlock{
 743			StylePrimitive: ansi.StylePrimitive{
 744				Prefix:          "###### ",
 745				Color:           plainFg,
 746				BackgroundColor: plainBg,
 747			},
 748		},
 749		Strikethrough: ansi.StylePrimitive{
 750			CrossedOut:      boolPtr(true),
 751			Color:           plainFg,
 752			BackgroundColor: plainBg,
 753		},
 754		Emph: ansi.StylePrimitive{
 755			Italic:          boolPtr(true),
 756			Color:           plainFg,
 757			BackgroundColor: plainBg,
 758		},
 759		Strong: ansi.StylePrimitive{
 760			Bold:            boolPtr(true),
 761			Color:           plainFg,
 762			BackgroundColor: plainBg,
 763		},
 764		HorizontalRule: ansi.StylePrimitive{
 765			Format:          "\n--------\n",
 766			Color:           plainFg,
 767			BackgroundColor: plainBg,
 768		},
 769		Item: ansi.StylePrimitive{
 770			BlockPrefix:     "• ",
 771			Color:           plainFg,
 772			BackgroundColor: plainBg,
 773		},
 774		Enumeration: ansi.StylePrimitive{
 775			BlockPrefix:     ". ",
 776			Color:           plainFg,
 777			BackgroundColor: plainBg,
 778		},
 779		Task: ansi.StyleTask{
 780			StylePrimitive: ansi.StylePrimitive{
 781				Color:           plainFg,
 782				BackgroundColor: plainBg,
 783			},
 784			Ticked:   "[✓] ",
 785			Unticked: "[ ] ",
 786		},
 787		Link: ansi.StylePrimitive{
 788			Underline:       boolPtr(true),
 789			Color:           plainFg,
 790			BackgroundColor: plainBg,
 791		},
 792		LinkText: ansi.StylePrimitive{
 793			Bold:            boolPtr(true),
 794			Color:           plainFg,
 795			BackgroundColor: plainBg,
 796		},
 797		Image: ansi.StylePrimitive{
 798			Underline:       boolPtr(true),
 799			Color:           plainFg,
 800			BackgroundColor: plainBg,
 801		},
 802		ImageText: ansi.StylePrimitive{
 803			Format:          "Image: {{.text}} →",
 804			Color:           plainFg,
 805			BackgroundColor: plainBg,
 806		},
 807		Code: ansi.StyleBlock{
 808			StylePrimitive: ansi.StylePrimitive{
 809				Prefix:          " ",
 810				Suffix:          " ",
 811				Color:           plainFg,
 812				BackgroundColor: plainBg,
 813			},
 814		},
 815		CodeBlock: ansi.StyleCodeBlock{
 816			StyleBlock: ansi.StyleBlock{
 817				StylePrimitive: ansi.StylePrimitive{
 818					Color:           plainFg,
 819					BackgroundColor: plainBg,
 820				},
 821				Margin: uintPtr(defaultMargin),
 822			},
 823		},
 824		Table: ansi.StyleTable{
 825			StyleBlock: ansi.StyleBlock{
 826				StylePrimitive: ansi.StylePrimitive{
 827					Color:           plainFg,
 828					BackgroundColor: plainBg,
 829				},
 830			},
 831		},
 832		DefinitionDescription: ansi.StylePrimitive{
 833			BlockPrefix:     "\n ",
 834			Color:           plainFg,
 835			BackgroundColor: plainBg,
 836		},
 837	}
 838
 839	s.Help = help.Styles{
 840		ShortKey:       base.Foreground(fgMuted),
 841		ShortDesc:      base.Foreground(fgSubtle),
 842		ShortSeparator: base.Foreground(border),
 843		Ellipsis:       base.Foreground(border),
 844		FullKey:        base.Foreground(fgMuted),
 845		FullDesc:       base.Foreground(fgSubtle),
 846		FullSeparator:  base.Foreground(border),
 847	}
 848
 849	s.Diff = diffview.Style{
 850		DividerLine: diffview.LineStyle{
 851			LineNumber: lipgloss.NewStyle().
 852				Foreground(fgHalfMuted).
 853				Background(bgBaseLighter),
 854			Code: lipgloss.NewStyle().
 855				Foreground(fgHalfMuted).
 856				Background(bgBaseLighter),
 857		},
 858		MissingLine: diffview.LineStyle{
 859			LineNumber: lipgloss.NewStyle().
 860				Background(bgBaseLighter),
 861			Code: lipgloss.NewStyle().
 862				Background(bgBaseLighter),
 863		},
 864		EqualLine: diffview.LineStyle{
 865			LineNumber: lipgloss.NewStyle().
 866				Foreground(fgMuted).
 867				Background(bgBase),
 868			Code: lipgloss.NewStyle().
 869				Foreground(fgMuted).
 870				Background(bgBase),
 871		},
 872		InsertLine: diffview.LineStyle{
 873			LineNumber: lipgloss.NewStyle().
 874				Foreground(lipgloss.Color("#629657")).
 875				Background(lipgloss.Color("#2b322a")),
 876			Symbol: lipgloss.NewStyle().
 877				Foreground(lipgloss.Color("#629657")).
 878				Background(lipgloss.Color("#323931")),
 879			Code: lipgloss.NewStyle().
 880				Background(lipgloss.Color("#323931")),
 881		},
 882		DeleteLine: diffview.LineStyle{
 883			LineNumber: lipgloss.NewStyle().
 884				Foreground(lipgloss.Color("#a45c59")).
 885				Background(lipgloss.Color("#312929")),
 886			Symbol: lipgloss.NewStyle().
 887				Foreground(lipgloss.Color("#a45c59")).
 888				Background(lipgloss.Color("#383030")),
 889			Code: lipgloss.NewStyle().
 890				Background(lipgloss.Color("#383030")),
 891		},
 892	}
 893
 894	s.FilePicker = filepicker.Styles{
 895		DisabledCursor:   base.Foreground(fgMuted),
 896		Cursor:           base.Foreground(fgBase),
 897		Symlink:          base.Foreground(fgSubtle),
 898		Directory:        base.Foreground(primary),
 899		File:             base.Foreground(fgBase),
 900		DisabledFile:     base.Foreground(fgMuted),
 901		DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
 902		Permission:       base.Foreground(fgMuted),
 903		Selected:         base.Background(primary).Foreground(fgBase),
 904		FileSize:         base.Foreground(fgMuted),
 905		EmptyDirectory:   base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
 906	}
 907
 908	// borders
 909	s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
 910
 911	// text presets
 912	s.Base = lipgloss.NewStyle().Foreground(fgBase)
 913	s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
 914	s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
 915
 916	s.WindowTooSmall = s.Muted
 917
 918	// tag presets
 919	s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
 920	s.TagError = s.TagBase.Background(redDark)
 921	s.TagInfo = s.TagBase.Background(blueLight)
 922
 923	// headers
 924	s.HeaderTool = lipgloss.NewStyle().Foreground(blue)
 925	s.HeaderToolNested = lipgloss.NewStyle().Foreground(fgHalfMuted)
 926
 927	// panels
 928	s.PanelMuted = s.Muted.Background(bgBaseLighter)
 929	s.PanelBase = lipgloss.NewStyle().Background(bgBase)
 930
 931	// code line number
 932	s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
 933
 934	// Tool calls
 935	s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
 936	s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
 937	s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
 938	// Cancelled uses muted tone but same glyph as pending
 939	s.ToolCallCancelled = s.Muted.SetString(ToolPending)
 940	s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
 941
 942	// Tool rendering styles
 943	s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
 944	s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
 945	s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
 946	s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
 947
 948	s.Tool.NameNormal = base.Foreground(blue)
 949	s.Tool.NameNested = base.Foreground(fgHalfMuted)
 950
 951	s.Tool.ParamMain = s.Subtle
 952	s.Tool.ParamKey = s.Subtle
 953
 954	// Content rendering - prepared styles that accept width parameter
 955	s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
 956	s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
 957	s.Tool.ContentCodeLine = s.Base.Background(bgBaseLighter)
 958	s.Tool.ContentCodeBg = bgBase
 959	s.Tool.BodyPadding = base.PaddingLeft(2)
 960
 961	// Deprecated - kept for backward compatibility
 962	s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
 963	s.Tool.ContentText = s.Muted
 964	s.Tool.ContentLineNumber = s.Subtle
 965
 966	s.Tool.StateWaiting = base.Foreground(fgSubtle)
 967	s.Tool.StateCancelled = base.Foreground(fgSubtle)
 968
 969	s.Tool.ErrorTag = base.Padding(0, 1).Background(red).Foreground(white)
 970	s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
 971
 972	// Diff and multi-edit styles
 973	s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
 974	s.Tool.NoteTag = base.Padding(0, 1).Background(yellow).Foreground(white)
 975	s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
 976
 977	// Job header styles
 978	s.Tool.JobIconPending = base.Foreground(greenDark)
 979	s.Tool.JobIconError = base.Foreground(redDark)
 980	s.Tool.JobIconSuccess = base.Foreground(green)
 981	s.Tool.JobToolName = base.Foreground(blue)
 982	s.Tool.JobAction = base.Foreground(fgHalfMuted)
 983	s.Tool.JobPID = s.Subtle
 984	s.Tool.JobDescription = s.Subtle
 985
 986	// Agent task styles
 987	s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
 988	s.Tool.AgentPrompt = s.Muted
 989
 990	// Buttons
 991	s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
 992	s.ButtonBlur = s.Base.Background(bgSubtle)
 993
 994	// Borders
 995	s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
 996
 997	// Editor
 998	s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
 999	s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
1000	s.EditorPromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1001	s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1002	s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1003	s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
1004
1005	// Logo colors
1006	s.LogoFieldColor = primary
1007	s.LogoTitleColorA = secondary
1008	s.LogoTitleColorB = primary
1009	s.LogoCharmColor = secondary
1010	s.LogoVersionColor = primary
1011
1012	// Section
1013	s.Section.Title = s.Subtle
1014	s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
1015
1016	// Initialize
1017	s.Initialize.Header = s.Base
1018	s.Initialize.Content = s.Muted
1019	s.Initialize.Accent = s.Base.Foreground(greenDark)
1020
1021	// LSP and MCP status.
1022	s.ItemOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Squid).SetString("●")
1023	s.ItemBusyIcon = s.ItemOfflineIcon.Foreground(charmtone.Citron)
1024	s.ItemErrorIcon = s.ItemOfflineIcon.Foreground(charmtone.Coral)
1025	s.ItemOnlineIcon = s.ItemOfflineIcon.Foreground(charmtone.Guac)
1026
1027	// LSP
1028	s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
1029	s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
1030	s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
1031	s.LSP.InfoDiagnostic = s.Base.Foreground(info)
1032
1033	// Files
1034	s.Files.Path = s.Muted
1035	s.Files.Additions = s.Base.Foreground(greenDark)
1036	s.Files.Deletions = s.Base.Foreground(redDark)
1037
1038	// Chat
1039	messageFocussedBorder := lipgloss.Border{
1040		Left: "▌",
1041	}
1042
1043	s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1044	s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1045		BorderForeground(primary).BorderStyle(normalBorder)
1046	s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1047		BorderForeground(primary).BorderStyle(messageFocussedBorder)
1048	s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
1049	s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1050		BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1051	s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
1052	s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1053		Background(red).Foreground(white)
1054	s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1055	s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1056
1057	// Message item styles
1058	s.Chat.Message.Attachment = lipgloss.NewStyle().MarginLeft(1).Background(bgSubtle)
1059	s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
1060		BorderStyle(messageFocussedBorder).
1061		BorderLeft(true).
1062		BorderForeground(greenDark)
1063	s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
1064	s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
1065
1066	// Thinking section styles
1067	s.Chat.Message.ThinkingBox = s.Subtle.Background(bgBaseLighter)
1068	s.Chat.Message.ThinkingTruncationHint = s.Muted
1069	s.Chat.Message.ThinkingFooterTitle = s.Muted
1070	s.Chat.Message.ThinkingFooterDuration = s.Subtle
1071
1072	// Text selection.
1073	s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
1074
1075	// Dialog styles
1076	s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
1077	s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1078	s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
1079	s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
1080	s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
1081	s.Dialog.Help.ShortSeparator = base.Foreground(border)
1082	s.Dialog.Help.Ellipsis = base.Foreground(border)
1083	s.Dialog.Help.FullKey = base.Foreground(fgMuted)
1084	s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
1085	s.Dialog.Help.FullSeparator = base.Foreground(border)
1086	s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
1087	s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
1088	s.Dialog.InputPrompt = base.Margin(1, 1)
1089
1090	s.Dialog.List = base.Margin(0, 0, 1, 0)
1091
1092	s.Dialog.Commands.CommandTypeSelector = base.Foreground(fgHalfMuted)
1093
1094	return s
1095}
1096
1097// Helper functions for style pointers
1098func boolPtr(b bool) *bool       { return &b }
1099func stringPtr(s string) *string { return &s }
1100func uintPtr(u uint) *uint       { return &u }
1101func chromaStyle(style ansi.StylePrimitive) string {
1102	var s string
1103
1104	if style.Color != nil {
1105		s = *style.Color
1106	}
1107	if style.BackgroundColor != nil {
1108		if s != "" {
1109			s += " "
1110		}
1111		s += "bg:" + *style.BackgroundColor
1112	}
1113	if style.Italic != nil && *style.Italic {
1114		if s != "" {
1115			s += " "
1116		}
1117		s += "italic"
1118	}
1119	if style.Bold != nil && *style.Bold {
1120		if s != "" {
1121			s += " "
1122		}
1123		s += "bold"
1124	}
1125	if style.Underline != nil && *style.Underline {
1126		if s != "" {
1127			s += " "
1128		}
1129		s += "underline"
1130	}
1131
1132	return s
1133}