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