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