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