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