Detailed changes
@@ -242,14 +242,14 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt,
if f, ok := output.(*os.File); ok && stdinTTY && stdoutTTY {
hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, f)
}
- defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
+ defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
spinner = format.NewSpinner(ctx, cancel, anim.Settings{
Size: 10,
Label: "Generating",
LabelColor: defaultFG,
- GradColorA: t.Primary,
- GradColorB: t.Secondary,
+ GradColorA: t.WorkingGradFromColor,
+ GradColorB: t.WorkingGradToColor,
CycleColors: true,
})
spinner.Start()
@@ -195,14 +195,14 @@ func runNonInteractive(
if stdinTTY && stdoutTTY {
hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
}
- defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
+ defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
spinner = format.NewSpinner(ctx, cancel, anim.Settings{
Size: 10,
Label: "Generating",
LabelColor: defaultFG,
- GradColorA: t.Primary,
- GradColorB: t.Secondary,
+ GradColorA: t.WorkingGradFromColor,
+ GradColorB: t.WorkingGradToColor,
CycleColors: true,
})
spinner.Start()
@@ -48,9 +48,9 @@ func NewAssistantMessageItem(sty *styles.Styles, message *message.Message) Messa
a.anim = anim.New(anim.Settings{
ID: a.ID(),
Size: 15,
- GradColorA: sty.Primary,
- GradColorB: sty.Secondary,
- LabelColor: sty.FgBase,
+ GradColorA: sty.WorkingGradFromColor,
+ GradColorB: sty.WorkingGradToColor,
+ LabelColor: sty.WorkingLabelColor,
CycleColors: true,
})
return a
@@ -112,8 +112,8 @@ func (a *AssistantMessageItem) Render(width int) string {
// it's wrapping logic.
// We already know that the content is wrapped to the correct width in
// RawRender, so we can just apply the styles directly to each line.
- focused := a.sty.Chat.Message.AssistantFocused.Render()
- blurred := a.sty.Chat.Message.AssistantBlurred.Render()
+ focused := a.sty.Messages.AssistantFocused.Render()
+ blurred := a.sty.Messages.AssistantBlurred.Render()
rendered := a.RawRender(width)
lines := strings.Split(rendered, "\n")
for i, line := range lines {
@@ -149,7 +149,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
if a.message.IsFinished() {
switch a.message.FinishReason() {
case message.FinishReasonCanceled:
- messageParts = append(messageParts, a.sty.Base.Italic(true).Render("Canceled"))
+ messageParts = append(messageParts, a.sty.Messages.AssistantCanceled.Render("Canceled"))
case message.FinishReasonError:
messageParts = append(messageParts, a.renderError(width))
}
@@ -160,7 +160,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
// renderThinking renders the thinking/reasoning content with footer.
func (a *AssistantMessageItem) renderThinking(thinking string, width int) string {
- renderer := common.PlainMarkdownRenderer(a.sty, width)
+ renderer := common.QuietMarkdownRenderer(a.sty, width)
rendered, err := renderer.Render(thinking)
if err != nil {
rendered = thinking
@@ -173,13 +173,13 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
isTruncated := totalLines > maxCollapsedThinkingHeight
if !a.thinkingExpanded && isTruncated {
lines = lines[totalLines-maxCollapsedThinkingHeight:]
- hint := a.sty.Chat.Message.ThinkingTruncationHint.Render(
+ hint := a.sty.Messages.ThinkingTruncationHint.Render(
fmt.Sprintf(assistantMessageTruncateFormat, totalLines-maxCollapsedThinkingHeight),
)
lines = append([]string{hint, ""}, lines...)
}
- thinkingStyle := a.sty.Chat.Message.ThinkingBox.Width(width)
+ thinkingStyle := a.sty.Messages.ThinkingBox.Width(width)
result := thinkingStyle.Render(strings.Join(lines, "\n"))
a.thinkingBoxHeight = lipgloss.Height(result)
@@ -188,8 +188,8 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
if !a.message.IsThinking() || len(a.message.ToolCalls()) > 0 {
duration := a.message.ThinkingDuration()
if duration.String() != "0s" {
- footer = a.sty.Chat.Message.ThinkingFooterTitle.Render("Thought for ") +
- a.sty.Chat.Message.ThinkingFooterDuration.Render(duration.String())
+ footer = a.sty.Messages.ThinkingFooterTitle.Render("Thought for ") +
+ a.sty.Messages.ThinkingFooterDuration.Render(duration.String())
}
}
@@ -222,10 +222,10 @@ func (a *AssistantMessageItem) renderSpinning() string {
// renderError renders an error message.
func (a *AssistantMessageItem) renderError(width int) string {
finishPart := a.message.FinishPart()
- errTag := a.sty.Chat.Message.ErrorTag.Render("ERROR")
+ errTag := a.sty.Messages.ErrorTag.Render("ERROR")
truncated := ansi.Truncate(finishPart.Message, width-2-lipgloss.Width(errTag), "...")
- title := fmt.Sprintf("%s %s", errTag, a.sty.Chat.Message.ErrorTitle.Render(truncated))
- details := a.sty.Chat.Message.ErrorDetails.Width(width - 2).Render(finishPart.Details)
+ title := fmt.Sprintf("%s %s", errTag, a.sty.Messages.ErrorTitle.Render(truncated))
+ details := a.sty.Messages.ErrorDetails.Width(width - 2).Render(finishPart.Details)
return fmt.Sprintf("%s\n\n%s", title, details)
}
@@ -173,7 +173,7 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
}
if len(result.Servers) == 0 {
- return sty.Subtle.Render("No MCP servers found.")
+ return sty.Tool.ResultEmpty.Render("No MCP servers found.")
}
bodyWidth := min(120, width) - toolBodyLeftPaddingTotal
@@ -181,10 +181,10 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
moreServers := ""
for i, server := range result.Servers {
if i > 9 {
- moreServers = sty.Subtle.Render(fmt.Sprintf("... and %d more", len(result.Servers)-10))
+ moreServers = sty.Tool.ResultTruncation.Render(fmt.Sprintf("... and %d more", len(result.Servers)-10))
break
}
- rows = append(rows, []string{sty.Base.Render(server.Name), sty.Subtle.Render(server.Description)})
+ rows = append(rows, []string{sty.Tool.ResultItemName.Render(server.Name), sty.Tool.ResultItemDesc.Render(server.Description)})
}
serverTable := table.New().
Wrap(false).
@@ -236,10 +236,10 @@ func (d *DockerMCPToolRenderContext) formatToolName(sty *styles.Styles, tool str
action = "Find"
case "mcp-add":
action = "Add"
- actionStyle = sty.Tool.DockerMCPActionAdd
+ actionStyle = sty.Tool.ActionCreate
case "mcp-remove":
action = "Remove"
- actionStyle = sty.Tool.DockerMCPActionDel
+ actionStyle = sty.Tool.ActionDestroy
case "code-mode":
action = "Code Mode"
default:
@@ -221,7 +221,7 @@ func (a *AssistantInfoItem) RawRender(width int) string {
// Render implements MessageItem.
func (a *AssistantInfoItem) Render(width int) string {
- prefix := a.sty.Chat.Message.SectionHeader.Render()
+ prefix := a.sty.Messages.SectionHeader.Render()
lines := strings.Split(a.RawRender(width), "\n")
for i, line := range lines {
lines[i] = prefix + line
@@ -236,18 +236,18 @@ func (a *AssistantInfoItem) renderContent(width int) string {
}
finishTime := time.Unix(finishData.Time, 0)
duration := finishTime.Sub(a.lastUserMessageTime)
- infoMsg := a.sty.Chat.Message.AssistantInfoDuration.Render(duration.String())
- icon := a.sty.Chat.Message.AssistantInfoIcon.Render(styles.ModelIcon)
+ infoMsg := a.sty.Messages.AssistantInfoDuration.Render(duration.String())
+ icon := a.sty.Messages.AssistantInfoIcon.Render(styles.ModelIcon)
model := a.cfg.GetModel(a.message.Provider, a.message.Model)
if model == nil {
model = &catwalk.Model{Name: "Unknown Model"}
}
- modelFormatted := a.sty.Chat.Message.AssistantInfoModel.Render(model.Name)
+ modelFormatted := a.sty.Messages.AssistantInfoModel.Render(model.Name)
providerName := a.message.Provider
if providerConfig, ok := a.cfg.Providers.Get(a.message.Provider); ok {
providerName = providerConfig.Name
}
- provider := a.sty.Chat.Message.AssistantInfoProvider.Render(fmt.Sprintf("via %s", providerName))
+ provider := a.sty.Messages.AssistantInfoProvider.Render(fmt.Sprintf("via %s", providerName))
assistant := fmt.Sprintf("%s %s %s %s", icon, modelFormatted, provider, infoMsg)
return common.Section(a.sty, assistant, width)
}
@@ -91,16 +91,16 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
ratio := sty.Tool.TodoRatio.Render(fmt.Sprintf("%d/%d", meta.Completed, meta.Total))
if hasCompleted && hasStarted {
- text := sty.Subtle.Render(fmt.Sprintf(" · completed %d, starting next", len(meta.JustCompleted)))
+ text := sty.Tool.TodoStatusNote.Render(fmt.Sprintf(" · completed %d, starting next", len(meta.JustCompleted)))
headerText = fmt.Sprintf("%s%s", ratio, text)
} else if hasCompleted {
- text := sty.Subtle.Render(fmt.Sprintf(" · completed %d", len(meta.JustCompleted)))
+ text := sty.Tool.TodoStatusNote.Render(fmt.Sprintf(" · completed %d", len(meta.JustCompleted)))
if allCompleted {
- text = sty.Subtle.Render(" · completed all")
+ text = sty.Tool.TodoStatusNote.Render(" · completed all")
}
headerText = fmt.Sprintf("%s%s", ratio, text)
} else if hasStarted {
- headerText = fmt.Sprintf("%s%s", ratio, sty.Subtle.Render(" · starting task"))
+ headerText = fmt.Sprintf("%s%s", ratio, sty.Tool.TodoStatusNote.Render(" · starting task"))
} else {
headerText = ratio
}
@@ -111,7 +111,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
} else if meta.JustStarted != "" {
body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
- sty.Base.Render(meta.JustStarted)
+ sty.Tool.TodoJustStarted.Render(meta.JustStarted)
}
}
}
@@ -148,7 +148,7 @@ func FormatTodosList(sty *styles.Styles, todos []session.Todo, inProgressIcon st
var lines []string
for _, todo := range sorted {
var prefix string
- textStyle := sty.Base
+ textStyle := sty.Tool.TodoItem
switch todo.Status {
case session.TodoStatusCompleted:
@@ -189,9 +189,9 @@ func newBaseToolMessageItem(
t.anim = anim.New(anim.Settings{
ID: toolCall.ID,
Size: 15,
- GradColorA: sty.Primary,
- GradColorB: sty.Secondary,
- LabelColor: sty.FgBase,
+ GradColorA: sty.WorkingGradFromColor,
+ GradColorB: sty.WorkingGradToColor,
+ LabelColor: sty.WorkingLabelColor,
CycleColors: true,
})
@@ -325,11 +325,11 @@ func (t *baseToolMessageItem) RawRender(width int) string {
func (t *baseToolMessageItem) Render(width int) string {
var prefix string
if t.isCompact {
- prefix = t.sty.Chat.Message.ToolCallCompact.Render()
+ prefix = t.sty.Messages.ToolCallCompact.Render()
} else if t.focused {
- prefix = t.sty.Chat.Message.ToolCallFocused.Render()
+ prefix = t.sty.Messages.ToolCallFocused.Render()
} else {
- prefix = t.sty.Chat.Message.ToolCallBlurred.Render()
+ prefix = t.sty.Messages.ToolCallBlurred.Render()
}
lines := strings.Split(t.RawRender(width), "\n")
for i, ln := range lines {
@@ -799,7 +799,7 @@ func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, ex
width = maxTextWidth
}
- renderer := common.PlainMarkdownRenderer(sty, width)
+ renderer := common.QuietMarkdownRenderer(sty, width)
rendered, err := renderer.Render(content)
if err != nil {
return toolOutputPlainContent(sty, content, width, expanded)
@@ -72,9 +72,9 @@ func (m *UserMessageItem) RawRender(width int) string {
func (m *UserMessageItem) Render(width int) string {
var prefix string
if m.focused {
- prefix = m.sty.Chat.Message.UserFocused.Render()
+ prefix = m.sty.Messages.UserFocused.Render()
} else {
- prefix = m.sty.Chat.Message.UserBlurred.Render()
+ prefix = m.sty.Messages.UserBlurred.Render()
}
lines := strings.Split(m.RawRender(width), "\n")
for i, line := range lines {
@@ -22,9 +22,9 @@ type ButtonOpts struct {
// Button creates a button with an underlined character and selection state
func Button(t *styles.Styles, opts ButtonOpts) string {
// Select style based on selection state
- style := t.ButtonBlur
+ style := t.Button.Blurred
if opts.Selected {
- style = t.ButtonFocus
+ style = t.Button.Focused
}
text := opts.Text
@@ -18,7 +18,7 @@ import (
// muted styling.
func PrettyPath(t *styles.Styles, path string, width int) string {
formatted := home.Short(path)
- return t.Muted.Width(width).Render(formatted)
+ return t.Sidebar.WorkingDir.Width(width).Render(formatted)
}
// FormatReasoningEffort formats a reasoning effort level for display.
@@ -39,13 +39,13 @@ type ModelContextInfo struct {
// ModelInfo renders model information including name, provider, reasoning
// settings, and optional context usage/cost.
func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string, context *ModelContextInfo, width int) string {
- modelIcon := t.Subtle.Render(styles.ModelIcon)
- modelName = t.Base.Render(modelName)
+ modelIcon := t.ModelInfo.Icon.Render(styles.ModelIcon)
+ modelName = t.ModelInfo.Name.Render(modelName)
// Build first line with model name and optionally provider on the same line
var firstLine string
if providerName != "" {
- providerInfo := t.Muted.Render(fmt.Sprintf("via %s", providerName))
+ providerInfo := t.ModelInfo.Provider.Render(fmt.Sprintf("via %s", providerName))
modelWithProvider := fmt.Sprintf("%s %s %s", modelIcon, modelName, providerInfo)
// Check if it fits on one line
@@ -64,11 +64,11 @@ func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string,
// If provider didn't fit on first line, add it as second line
if providerName != "" && !strings.Contains(firstLine, "via") {
providerInfo := fmt.Sprintf("via %s", providerName)
- parts = append(parts, t.Muted.PaddingLeft(2).Render(providerInfo))
+ parts = append(parts, t.ModelInfo.ProviderFallback.Render(providerInfo))
}
if reasoningInfo != "" {
- parts = append(parts, t.Subtle.PaddingLeft(2).Render(reasoningInfo))
+ parts = append(parts, t.ModelInfo.Reasoning.Render(reasoningInfo))
}
if context != nil {
@@ -103,10 +103,10 @@ func formatTokensAndCost(t *styles.Styles, tokens, contextWindow int64, cost flo
percentage := (float64(tokens) / float64(contextWindow)) * 100
- formattedCost := t.Muted.Render(fmt.Sprintf("$%.2f", cost))
+ formattedCost := t.ModelInfo.Cost.Render(fmt.Sprintf("$%.2f", cost))
- formattedTokens = t.Subtle.Render(fmt.Sprintf("(%s)", formattedTokens))
- formattedPercentage := t.Muted.Render(fmt.Sprintf("%d%%", int(percentage)))
+ formattedTokens = t.ModelInfo.TokenCount.Render(fmt.Sprintf("(%s)", formattedTokens))
+ formattedPercentage := t.ModelInfo.TokenPercentage.Render(fmt.Sprintf("%d%%", int(percentage)))
formattedTokens = fmt.Sprintf("%s %s", formattedPercentage, formattedTokens)
if percentage > 80 {
formattedTokens = fmt.Sprintf("%s %s", styles.LSPWarningIcon, formattedTokens)
@@ -133,10 +133,10 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
title := opts.Title
description := opts.Description
- titleColor := cmp.Or(opts.TitleColor, t.Muted.GetForeground())
- descriptionColor := cmp.Or(opts.DescriptionColor, t.Subtle.GetForeground())
+ titleColor := cmp.Or(opts.TitleColor, t.Resource.DefaultTitleFg)
+ descriptionColor := cmp.Or(opts.DescriptionColor, t.Resource.DefaultDescFg)
- title = t.Base.Foreground(titleColor).Render(title)
+ title = t.Resource.RowTitleBase.Foreground(titleColor).Render(title)
if description != "" {
extraContentWidth := lipgloss.Width(opts.ExtraContent)
@@ -144,7 +144,7 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
extraContentWidth += 1
}
description = ansi.Truncate(description, width-lipgloss.Width(icon)-lipgloss.Width(title)-2-extraContentWidth, "…")
- description = t.Base.Foreground(descriptionColor).Render(description)
+ description = t.Resource.RowDescBase.Foreground(descriptionColor).Render(description)
}
var content []string
@@ -193,7 +193,7 @@ func DialogTitle(t *styles.Styles, title string, width int, fromColor, toColor c
remainingWidth := width - length
if remainingWidth > 0 {
lines := strings.Repeat(char, remainingWidth)
- lines = styles.ApplyForegroundGrad(t.Base, lines, fromColor, toColor)
+ lines = styles.ApplyForegroundGrad(t.Dialog.TitleLineBase, lines, fromColor, toColor)
title = title + " " + lines
}
return title
@@ -29,11 +29,11 @@ func MarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
return r
}
-// PlainMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
+// QuietMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
// (plain text with structure) and the given width.
-func PlainMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
+func QuietMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
r, _ := glamour.NewTermRenderer(
- glamour.WithStyles(sty.PlainMarkdown),
+ glamour.WithStyles(sty.QuietMarkdown),
glamour.WithWordWrap(width),
glamour.WithChromaFormatter(formatterName),
)
@@ -83,7 +83,7 @@ func NewAPIKeyInput(
m.spinner = spinner.New(
spinner.WithSpinner(spinner.Dot),
- spinner.WithStyle(t.Base.Foreground(t.Green)),
+ spinner.WithStyle(t.Dialog.APIKey.Spinner),
)
m.help = help.New()
@@ -202,7 +202,7 @@ func (m *APIKeyInput) headerView() string {
return textStyle.Render(m.dialogTitle())
}
headerOffset := titleStyle.GetHorizontalFrameSize() + dialogStyle.GetHorizontalFrameSize()
- return common.DialogTitle(t, titleStyle.Render(m.dialogTitle()), m.width-headerOffset, m.com.Styles.Primary, m.com.Styles.Secondary)
+ return common.DialogTitle(t, titleStyle.Render(m.dialogTitle()), m.width-headerOffset, m.com.Styles.Dialog.TitleGradFromColor, m.com.Styles.Dialog.TitleGradToColor)
}
func (m *APIKeyInput) dialogTitle() string {
@@ -314,7 +314,7 @@ func (a *Arguments) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
titleText := cmp.Or(a.title, "Arguments")
- header := common.DialogTitle(s, titleText, width, s.Primary, s.Secondary)
+ header := common.DialogTitle(s, titleText, width, s.Dialog.TitleGradFromColor, s.Dialog.TitleGradToColor)
// Add description if available.
var description string
@@ -252,9 +252,9 @@ func commandsRadioView(sty *styles.Styles, selected CommandType, hasUserCmds boo
selectedFn := func(t CommandType) string {
if t == selected {
- return sty.RadioOn.Padding(0, 1).Render() + sty.HalfMuted.Render(t.String())
+ return sty.Radio.On.Padding(0, 1).Render() + sty.Radio.Label.Render(t.String())
}
- return sty.RadioOff.Padding(0, 1).Render() + sty.HalfMuted.Render(t.String())
+ return sty.Radio.Off.Padding(0, 1).Render() + sty.Radio.Label.Render(t.String())
}
parts := []string{
@@ -69,8 +69,8 @@ func (c *CommandItem) Render(width int) string {
styles := ListItemStyles{
ItemBlurred: c.t.Dialog.NormalItem,
ItemFocused: c.t.Dialog.SelectedItem,
- InfoTextBlurred: c.t.Base,
- InfoTextFocused: c.t.Base,
+ InfoTextBlurred: c.t.Dialog.ListItem.InfoBlurred,
+ InfoTextFocused: c.t.Dialog.ListItem.InfoFocused,
}
return renderItem(styles, c.title, c.shortcut, c.focused, width, c.cache, &c.m)
}
@@ -65,9 +65,11 @@ type RenderContext struct {
TitleStyle lipgloss.Style
// ViewStyle is the style of the dialog title by default it uses Styles.Dialog.View
ViewStyle lipgloss.Style
- // TitleGradientFromColor is the color the title gradient starts by defaults its Style.Primary
+ // TitleGradientFromColor is the color the title gradient starts by default
+ // its Styles.Dialog.TitleGradFromColor
TitleGradientFromColor color.Color
- // TitleGradientToColor is the color the title gradient starts by defaults its Style.Secondary
+ // TitleGradientToColor is the color the title gradient ends by default its
+ // Styles.Dialog.TitleGradToColor
TitleGradientToColor color.Color
// Width is the total width of the dialog including any margins, borders,
// and paddings.
@@ -98,8 +100,8 @@ func NewRenderContext(t *styles.Styles, width int) *RenderContext {
Styles: t,
TitleStyle: t.Dialog.Title,
ViewStyle: t.Dialog.View,
- TitleGradientFromColor: t.Primary,
- TitleGradientToColor: t.Secondary,
+ TitleGradientFromColor: t.Dialog.TitleGradFromColor,
+ TitleGradientToColor: t.Dialog.TitleGradToColor,
Width: width,
Parts: []string{},
}
@@ -236,13 +236,13 @@ func (m *Models) Cursor() *tea.Cursor {
// modelTypeRadioView returns the radio view for model type selection.
func (m *Models) modelTypeRadioView() string {
t := m.com.Styles
- textStyle := t.HalfMuted
- largeRadioStyle := t.RadioOff
- smallRadioStyle := t.RadioOff
+ textStyle := t.Radio.Label
+ largeRadioStyle := t.Radio.Off
+ smallRadioStyle := t.Radio.Off
if m.modelType == ModelTypeLarge {
- largeRadioStyle = t.RadioOn
+ largeRadioStyle = t.Radio.On
} else {
- smallRadioStyle = t.RadioOn
+ smallRadioStyle = t.Radio.On
}
largeRadio := largeRadioStyle.Padding(0, 1).Render()
@@ -38,7 +38,7 @@ func (m *ModelGroup) Render(width int) string {
var configured string
if m.configured {
configuredIcon := m.t.ToolCallSuccess.Render()
- configuredText := m.t.Subtle.Render("Configured")
+ configuredText := m.t.Dialog.Models.ConfiguredText.Render("Configured")
configured = configuredIcon + " " + configuredText
}
@@ -109,8 +109,8 @@ func (m *ModelItem) Render(width int) string {
styles := ListItemStyles{
ItemBlurred: m.t.Dialog.NormalItem,
ItemFocused: m.t.Dialog.SelectedItem,
- InfoTextBlurred: m.t.Base,
- InfoTextFocused: m.t.Base,
+ InfoTextBlurred: m.t.Dialog.ListItem.InfoBlurred,
+ InfoTextFocused: m.t.Dialog.ListItem.InfoFocused,
}
return renderItem(styles, m.model.Name, providerInfo, m.focused, width, m.cache, &m.m)
}
@@ -94,7 +94,7 @@ func newOAuth(
m.spinner = spinner.New(
spinner.WithSpinner(spinner.Dot),
- spinner.WithStyle(t.Base.Foreground(t.GreenLight)),
+ spinner.WithStyle(t.Dialog.OAuth.Spinner),
)
m.help = help.New()
@@ -227,18 +227,18 @@ func (m *OAuth) headerContent() string {
if m.isOnboarding {
return textStyle.Render(dialogTitle)
}
- return common.DialogTitle(t, titleStyle.Render(dialogTitle), m.width-headerOffset, t.Primary, t.Secondary)
+ return common.DialogTitle(t, titleStyle.Render(dialogTitle), m.width-headerOffset, t.Dialog.TitleGradFromColor, t.Dialog.TitleGradToColor)
}
func (m *OAuth) innerDialogContent() string {
var (
t = m.com.Styles
- whiteStyle = lipgloss.NewStyle().Foreground(t.White)
- primaryStyle = lipgloss.NewStyle().Foreground(t.Primary)
- greenStyle = lipgloss.NewStyle().Foreground(t.GreenLight)
- linkStyle = lipgloss.NewStyle().Foreground(t.GreenDark).Underline(true)
- errorStyle = lipgloss.NewStyle().Foreground(t.Error)
- mutedStyle = lipgloss.NewStyle().Foreground(t.FgMuted)
+ whiteStyle = t.Dialog.OAuth.Instructions
+ primaryStyle = t.Dialog.OAuth.Enter
+ greenStyle = t.Dialog.OAuth.Success
+ linkStyle = t.Dialog.OAuth.Link
+ errorStyle = t.Dialog.OAuth.ErrorText
+ mutedStyle = t.Dialog.OAuth.StatusText
)
switch m.State {
@@ -266,13 +266,10 @@ func (m *OAuth) innerDialogContent() string {
Width(m.width-2).
Height(7).
Align(lipgloss.Center, lipgloss.Center).
- Background(t.BgBaseLighter).
+ Background(t.Dialog.OAuth.UserCodeBg).
Margin(0, 1).
Render(
- lipgloss.NewStyle().
- Bold(true).
- Foreground(t.White).
- Render(m.userCode),
+ t.Dialog.OAuth.UserCode.Render(m.userCode),
)
link := linkStyle.Hyperlink(m.verificationURL, "id=oauth-verify").Render(m.verificationURL)
@@ -443,7 +443,7 @@ func (p *Permissions) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
func (p *Permissions) renderHeader(contentWidth int) string {
t := p.com.Styles
- title := common.DialogTitle(t, "Permission Required", contentWidth-t.Dialog.Title.GetHorizontalFrameSize(), t.Primary, t.Secondary)
+ title := common.DialogTitle(t, "Permission Required", contentWidth-t.Dialog.Title.GetHorizontalFrameSize(), t.Dialog.TitleGradFromColor, t.Dialog.TitleGradToColor)
title = t.Dialog.Title.Render(title)
// Tool info.
@@ -489,8 +489,8 @@ func (p *Permissions) renderHeader(contentWidth int) string {
func (p *Permissions) renderKeyValue(key, value string, width int) string {
t := p.com.Styles
- keyStyle := t.Muted
- valueStyle := t.Base
+ keyStyle := t.Dialog.Permissions.KeyText
+ valueStyle := t.Dialog.Permissions.ValueText
keyStr := keyStyle.Render(key)
valueStr := valueStyle.Width(width - lipgloss.Width(keyStr) - 1).Render(" " + value)
@@ -699,7 +699,7 @@ func (p *Permissions) renderDefaultContent(width int) string {
if err := json.Unmarshal([]byte(paramStr), &parsed); err == nil {
if b, err := json.MarshalIndent(parsed, "", " "); err == nil {
jsonContent := string(b)
- highlighted, err := common.SyntaxHighlight(t, jsonContent, "params.json", t.BgSubtle)
+ highlighted, err := common.SyntaxHighlight(t, jsonContent, "params.json", t.Dialog.Permissions.ParamsBg)
if err == nil {
jsonContent = highlighted
}
@@ -96,7 +96,7 @@ func (q *Quit) HandleMsg(msg tea.Msg) Action {
// Draw implements [Dialog].
func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
const question = "Are you sure you want to quit?"
- baseStyle := q.com.Styles.Base
+ baseStyle := q.com.Styles.Dialog.Quit.Content
buttonOpts := []common.ButtonOpts{
{Text: "Yep!", Selected: !q.selectedNo, Padding: 3},
{Text: "Nope", Selected: q.selectedNo, Padding: 3},
@@ -111,7 +111,7 @@ func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
),
)
- view := q.com.Styles.BorderFocus.Render(content)
+ view := q.com.Styles.Dialog.Quit.Frame.Render(content)
DrawCenter(scr, area, view)
return nil
}
@@ -293,8 +293,8 @@ func (r *ReasoningItem) Render(width int) string {
styles := ListItemStyles{
ItemBlurred: r.t.Dialog.NormalItem,
ItemFocused: r.t.Dialog.SelectedItem,
- InfoTextBlurred: r.t.Base,
- InfoTextFocused: r.t.Base,
+ InfoTextBlurred: r.t.Dialog.ListItem.InfoBlurred,
+ InfoTextFocused: r.t.Dialog.ListItem.InfoFocused,
}
return renderItem(styles, r.title, info, r.focused, width, r.cache, &r.m)
}
@@ -79,8 +79,8 @@ func (s *SessionItem) Render(width int) string {
styles := ListItemStyles{
ItemBlurred: s.t.Dialog.NormalItem,
ItemFocused: s.t.Dialog.SelectedItem,
- InfoTextBlurred: s.t.Subtle,
- InfoTextFocused: s.t.Base,
+ InfoTextBlurred: s.t.Dialog.Sessions.InfoBlurred,
+ InfoTextFocused: s.t.Dialog.Sessions.InfoFocused,
}
switch s.sessionsMode {
@@ -20,23 +20,23 @@ func main() {
s := styles.DefaultStyles()
opts := logo.Opts{
- FieldColor: s.LogoFieldColor,
- TitleColorA: s.LogoTitleColorA,
- TitleColorB: s.LogoTitleColorB,
- CharmColor: s.LogoCharmColor,
- VersionColor: s.LogoVersionColor,
+ FieldColor: s.Logo.FieldColor,
+ TitleColorA: s.Logo.TitleColorA,
+ TitleColorB: s.Logo.TitleColorB,
+ CharmColor: s.Logo.CharmColor,
+ VersionColor: s.Logo.VersionColor,
Width: w,
Unstable: true,
}
renderCompact := func(hyper bool) string {
opts.Hyper = hyper
- return logo.Render(s.Base, "v1.0.0", true, opts)
+ return logo.Render(s.Logo.GradCanvas, "v1.0.0", true, opts)
}
renderWide := func(hyper bool) string {
opts.Hyper = hyper
- return logo.Render(s.Base, "v1.0.0", false, opts)
+ return logo.Render(s.Logo.GradCanvas, "v1.0.0", false, opts)
}
lipgloss.Println(
@@ -149,12 +149,12 @@ func Render(base lipgloss.Style, version string, compact bool, o Opts) string {
// SmallRender renders a smaller version of the Crush logo, suitable for
// smaller windows or sidebar usage.
func SmallRender(t *styles.Styles, width int) string {
- title := t.Base.Foreground(t.Secondary).Render("Charm™")
- title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Base, "Crush", t.Secondary, t.Primary))
+ title := t.Logo.SmallCharm.Render("Charm™")
+ title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Logo.GradCanvas, "Crush", t.Logo.SmallGradFromColor, t.Logo.SmallGradToColor))
remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush"
if remainingWidth > 0 {
lines := strings.Repeat("╱", remainingWidth)
- title = fmt.Sprintf("%s %s", title, t.Base.Foreground(t.Primary).Render(lines))
+ title = fmt.Sprintf("%s %s", title, t.Logo.SmallDiagonals.Render(lines))
}
return title
}
@@ -39,7 +39,7 @@ func newHeader(com *common.Common) *header {
}
t := com.Styles
h.compactLogo = t.Header.Charm.Render("Charm™") + " " +
- styles.ApplyBoldForegroundGrad(t.Base, "CRUSH", t.Secondary, t.Primary) + " "
+ styles.ApplyBoldForegroundGrad(t.Header.LogoGradCanvas, "CRUSH", t.Header.LogoGradFromColor, t.Header.LogoGradToColor) + " "
return h
}
@@ -102,7 +102,7 @@ func (h *header) drawHeader(
b.WriteString(details)
view := uv.NewStyledString(
- t.Base.Padding(0, rightPadding, 0, leftPadding).Render(b.String()))
+ t.Header.Wrapper.Padding(0, rightPadding, 0, leftPadding).Render(b.String()))
view.Draw(scr, area)
}
@@ -30,7 +30,7 @@ func newTestUI() *UI {
com := common.DefaultCommon(nil)
ta := textarea.New()
- ta.SetStyles(com.Styles.TextArea)
+ ta.SetStyles(com.Styles.Editor.Textarea)
ta.ShowLineNumbers = false
ta.CharLimit = -1
ta.SetVirtualCursor(false)
@@ -41,11 +41,11 @@ func (m *UI) lspInfo(width, maxItems int, isSection bool) string {
lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
}
- title := t.ResourceGroupTitle.Render("LSPs")
+ title := t.Resource.Heading.Render("LSPs")
if isSection {
title = common.Section(t, title, width)
}
- list := t.ResourceAdditionalText.Render("None")
+ list := t.Resource.AdditionalText.Render("None")
if len(lsps) > 0 {
list = lspList(t, lsps, width, maxItems)
}
@@ -80,31 +80,31 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
var renderedLsps []string
for _, l := range lsps {
var icon string
- title := t.ResourceName.Render(l.Name)
+ title := t.Resource.Name.Render(l.Name)
var description string
var diagnostics string
switch l.State {
case lsp.StateUnstarted:
- icon = t.ResourceOfflineIcon.String()
- description = t.ResourceStatus.Render("unstarted")
+ icon = t.Resource.OfflineIcon.String()
+ description = t.Resource.StatusText.Render("unstarted")
case lsp.StateStopped:
- icon = t.ResourceOfflineIcon.String()
- description = t.ResourceStatus.Render("stopped")
+ icon = t.Resource.OfflineIcon.String()
+ description = t.Resource.StatusText.Render("stopped")
case lsp.StateStarting:
- icon = t.ResourceBusyIcon.String()
- description = t.ResourceStatus.Render("starting...")
+ icon = t.Resource.BusyIcon.String()
+ description = t.Resource.StatusText.Render("starting...")
case lsp.StateReady:
- icon = t.ResourceOnlineIcon.String()
+ icon = t.Resource.OnlineIcon.String()
diagnostics = lspDiagnostics(t, l.Diagnostics)
case lsp.StateError:
- icon = t.ResourceErrorIcon.String()
- description = t.ResourceStatus.Render("error")
+ icon = t.Resource.ErrorIcon.String()
+ description = t.Resource.StatusText.Render("error")
if l.Error != nil {
- description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", l.Error.Error()))
+ description = t.Resource.StatusText.Render(fmt.Sprintf("error: %s", l.Error.Error()))
}
case lsp.StateDisabled:
- icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
- description = t.ResourceStatus.Render("disabled")
+ icon = t.Resource.DisabledIcon.String()
+ description = t.Resource.StatusText.Render("disabled")
default:
continue
}
@@ -119,7 +119,7 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
if len(renderedLsps) > maxItems {
visibleItems := renderedLsps[:maxItems-1]
remaining := len(renderedLsps) - maxItems
- visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
+ visibleItems = append(visibleItems, t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
}
return lipgloss.JoinVertical(lipgloss.Left, renderedLsps...)
@@ -23,11 +23,11 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
}
}
- title := t.ResourceGroupTitle.Render("MCPs")
+ title := t.Resource.Heading.Render("MCPs")
if isSection {
title = common.Section(t, title, width)
}
- list := t.ResourceAdditionalText.Render("None")
+ list := t.Resource.AdditionalText.Render("None")
if len(mcps) > 0 {
list = mcpList(t, mcps, width, maxItems)
}
@@ -39,13 +39,13 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
func mcpCounts(t *styles.Styles, counts mcp.Counts) string {
var parts []string
if counts.Tools > 0 {
- parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d tools", counts.Tools)))
+ parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d tools", counts.Tools)))
}
if counts.Prompts > 0 {
- parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d prompts", counts.Prompts)))
+ parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d prompts", counts.Prompts)))
}
if counts.Resources > 0 {
- parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d resources", counts.Resources)))
+ parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d resources", counts.Resources)))
}
return strings.Join(parts, " ")
}
@@ -65,28 +65,28 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
if m.Name == config.DockerMCPName {
title = "Docker MCP"
}
- title = t.ResourceName.Render(title)
+ title = t.Resource.Name.Render(title)
var description string
var extraContent string
switch m.State {
case mcp.StateStarting:
- icon = t.ResourceBusyIcon.String()
- description = t.ResourceStatus.Render("starting...")
+ icon = t.Resource.BusyIcon.String()
+ description = t.Resource.StatusText.Render("starting...")
case mcp.StateConnected:
- icon = t.ResourceOnlineIcon.String()
+ icon = t.Resource.OnlineIcon.String()
extraContent = mcpCounts(t, m.Counts)
case mcp.StateError:
- icon = t.ResourceErrorIcon.String()
- description = t.ResourceStatus.Render("error")
+ icon = t.Resource.ErrorIcon.String()
+ description = t.Resource.StatusText.Render("error")
if m.Error != nil {
- description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", m.Error.Error()))
+ description = t.Resource.StatusText.Render(fmt.Sprintf("error: %s", m.Error.Error()))
}
case mcp.StateDisabled:
- icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
- description = t.ResourceStatus.Render("disabled")
+ icon = t.Resource.DisabledIcon.String()
+ description = t.Resource.StatusText.Render("disabled")
default:
- icon = t.ResourceOfflineIcon.String()
+ icon = t.Resource.OfflineIcon.String()
}
renderedMcps = append(renderedMcps, common.Status(t, common.StatusOpts{
@@ -100,7 +100,7 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
if len(renderedMcps) > maxItems {
visibleItems := renderedMcps[:maxItems-1]
remaining := len(renderedMcps) - maxItems
- visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
+ visibleItems = append(visibleItems, t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
}
return lipgloss.JoinVertical(lipgloss.Left, renderedMcps...)
@@ -56,12 +56,12 @@ func queuePill(queue int, focused, panelFocused bool, t *styles.Styles) string {
if queue <= 0 {
return ""
}
- triangles := styles.ForegroundGrad(t.Base, "▶▶▶▶▶▶▶▶▶", false, t.Pills.QueueGradFromColor, t.Secondary)
+ triangles := styles.ForegroundGrad(t.Pills.QueueIconBase, "▶▶▶▶▶▶▶▶▶", false, t.Pills.QueueGradFromColor, t.Pills.QueueGradToColor)
if queue < len(triangles) {
triangles = triangles[:queue]
}
- text := t.Base.Render(fmt.Sprintf("%d Queued", queue))
+ text := t.Pills.QueueLabel.Render(fmt.Sprintf("%d Queued", queue))
content := fmt.Sprintf("%s %s", strings.Join(triangles, ""), text)
return pillStyle(focused, panelFocused, t).Render(content)
}
@@ -87,8 +87,8 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
total := len(todos)
- label := t.Base.Render("To-Do")
- progress := t.Muted.Render(fmt.Sprintf("%d/%d", completed, total))
+ label := t.Pills.TodoLabel.Render("To-Do")
+ progress := t.Pills.TodoProgress.Render(fmt.Sprintf("%d/%d", completed, total))
var content string
if panelFocused {
@@ -101,7 +101,7 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
if len(taskText) > maxTaskDisplayLength {
taskText = taskText[:maxTaskDisplayLength-1] + "…"
}
- task := t.Subtle.Render(taskText)
+ task := t.Pills.TodoCurrentTask.Render(taskText)
content = fmt.Sprintf("%s %s %s %s", spinnerView, label, progress, task)
} else {
content = fmt.Sprintf("%s %s", label, progress)
@@ -128,7 +128,7 @@ func queueList(queueItems []string, t *styles.Styles) string {
text = text[:maxQueueDisplayLength-1] + "…"
}
prefix := t.Pills.QueueItemPrefix.Render() + " "
- lines = append(lines, prefix+t.Muted.Render(text))
+ lines = append(lines, prefix+t.Pills.QueueItemText.Render(text))
}
return strings.Join(lines, "\n")
@@ -163,11 +163,11 @@ func (m *UI) handleFileEvent(file history.File) tea.Cmd {
func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
t := m.com.Styles
- title := t.Subtle.Render("Modified Files")
+ title := t.Files.SectionTitle.Render("Modified Files")
if isSection {
title = common.Section(t, "Modified Files", width)
}
- list := t.Subtle.Render("None")
+ list := t.Files.EmptyMessage.Render("None")
var filesWithChanges []SessionFile
for _, f := range m.sessionFiles {
if f.Additions == 0 && f.Deletions == 0 {
@@ -226,7 +226,7 @@ func fileList(t *styles.Styles, cwd string, filesWithChanges []SessionFile, widt
if len(filesWithChanges) > maxItems {
remaining := len(filesWithChanges) - maxItems
- renderedFiles = append(renderedFiles, t.Subtle.Render(fmt.Sprintf("…and %d more", remaining)))
+ renderedFiles = append(renderedFiles, t.Files.TruncationHint.Render(fmt.Sprintf("…and %d more", remaining)))
}
return lipgloss.JoinVertical(lipgloss.Left, renderedFiles...)
@@ -138,7 +138,7 @@ func (m *UI) drawSidebar(scr uv.Screen, area uv.Rectangle) {
width := area.Dx()
height := area.Dy()
- title := t.Muted.Width(width).MaxHeight(2).Render(m.session.Title)
+ title := t.Sidebar.SessionTitle.Width(width).MaxHeight(2).Render(m.session.Title)
cwd := common.PrettyPath(t, m.com.Workspace.WorkingDir(), width)
sidebarLogo := m.sidebarLogo
if height < logoHeightBreakpoint {
@@ -38,14 +38,14 @@ func cachedBuiltinSkills() []*skills.Skill {
func (m *UI) skillsInfo(width, maxItems int, isSection bool) string {
t := m.com.Styles
- title := t.ResourceGroupTitle.Render("Skills")
+ title := t.Resource.Heading.Render("Skills")
if isSection {
title = common.Section(t, title, width)
}
items := m.skillStatusItems()
if len(items) == 0 {
- list := t.ResourceAdditionalText.Render("None")
+ list := t.Resource.AdditionalText.Render("None")
return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
}
@@ -68,14 +68,14 @@ func (m *UI) skillStatusItems() []skillStatusItem {
name = filepath.Base(filepath.Dir(state.Path))
}
stateNames[name] = struct{}{}
- icon := t.ResourceOnlineIcon.String()
+ icon := t.Resource.OnlineIcon.String()
if state.State == skills.StateError {
- icon = t.ResourceErrorIcon.String()
+ icon = t.Resource.ErrorIcon.String()
}
items = append(items, skillStatusItem{
icon: icon,
name: name,
- title: t.ResourceName.Render(name),
+ title: t.Resource.Name.Render(name),
})
}
@@ -88,9 +88,9 @@ func (m *UI) skillStatusItems() []skillStatusItem {
continue
}
items = append(items, skillStatusItem{
- icon: t.ResourceOnlineIcon.String(),
+ icon: t.Resource.OnlineIcon.String(),
name: skill.Name,
- title: t.ResourceName.Render(skill.Name),
+ title: t.Resource.Name.Render(skill.Name),
})
}
@@ -111,7 +111,7 @@ func skillsList(t *styles.Styles, items []skillStatusItem, width, maxItems int)
remaining := len(items) - (maxItems - 1)
items = append(visibleItems, skillStatusItem{
name: "more",
- title: t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)),
+ title: t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)),
})
}
@@ -28,7 +28,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
var hasGoDoc bool
for _, item := range items {
- if item.title == st.ResourceName.Render("go-doc") {
+ if item.title == st.Resource.Name.Render("go-doc") {
hasGoDoc = true
break
}
@@ -43,7 +43,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
if skill.Name == "go-doc" {
continue
}
- expected := st.ResourceName.Render(skill.Name)
+ expected := st.Resource.Name.Render(skill.Name)
for _, item := range items {
if item.title == expected {
hasBuiltin = true
@@ -269,7 +269,7 @@ type UI struct {
func New(com *common.Common, initialSessionID string, continueLast bool) *UI {
// Editor components
ta := textarea.New()
- ta.SetStyles(com.Styles.TextArea)
+ ta.SetStyles(com.Styles.Editor.Textarea)
ta.ShowLineNumbers = false
ta.CharLimit = -1
ta.SetVirtualCursor(false)
@@ -2745,9 +2745,9 @@ func (m *UI) normalPromptFunc(info textarea.PromptInfo) string {
return "::: "
}
if info.Focused {
- return t.EditorPromptNormalFocused.Render()
+ return t.Editor.PromptNormalFocused.Render()
}
- return t.EditorPromptNormalBlurred.Render()
+ return t.Editor.PromptNormalBlurred.Render()
}
// yoloPromptFunc returns the yolo mode editor prompt style with warning icon
@@ -2756,15 +2756,15 @@ func (m *UI) yoloPromptFunc(info textarea.PromptInfo) string {
t := m.com.Styles
if info.LineNumber == 0 {
if info.Focused {
- return t.EditorPromptYoloIconFocused.Render()
+ return t.Editor.PromptYoloIconFocused.Render()
} else {
- return t.EditorPromptYoloIconBlurred.Render()
+ return t.Editor.PromptYoloIconBlurred.Render()
}
}
if info.Focused {
- return t.EditorPromptYoloDotsFocused.Render()
+ return t.Editor.PromptYoloDotsFocused.Render()
}
- return t.EditorPromptYoloDotsBlurred.Render()
+ return t.Editor.PromptYoloDotsBlurred.Render()
}
// closeCompletions closes the completions popup and resets state.
@@ -3536,7 +3536,7 @@ func (m *UI) drawSessionDetails(scr uv.Screen, area uv.Rectangle) {
blocks...,
)
- version := s.CompactDetails.Version.Foreground(s.Border).Width(width).AlignHorizontal(lipgloss.Right).Render(version.Version)
+ version := s.CompactDetails.Version.Width(width).AlignHorizontal(lipgloss.Right).Render(version.Version)
remainingHeight := height - lipgloss.Height(detailsHeader) - lipgloss.Height(version)
@@ -3651,12 +3651,12 @@ func (m *UI) disableDockerMCP() tea.Msg {
// renderLogo renders the Crush logo with the given styles and dimensions.
func renderLogo(t *styles.Styles, compact bool, width int) string {
- return logo.Render(t.Base, version.Version, compact, logo.Opts{
- FieldColor: t.LogoFieldColor,
- TitleColorA: t.LogoTitleColorA,
- TitleColorB: t.LogoTitleColorB,
- CharmColor: t.LogoCharmColor,
- VersionColor: t.LogoVersionColor,
+ return logo.Render(t.Logo.GradCanvas, version.Version, compact, logo.Opts{
+ FieldColor: t.Logo.FieldColor,
+ TitleColorA: t.Logo.TitleColorA,
+ TitleColorB: t.Logo.TitleColorB,
+ CharmColor: t.Logo.CharmColor,
+ VersionColor: t.Logo.VersionColor,
Width: width,
})
}
@@ -58,28 +58,19 @@ const (
)
type Styles struct {
- WindowTooSmall lipgloss.Style
-
- // Reusable text styles
- Base lipgloss.Style
- Muted lipgloss.Style
- HalfMuted lipgloss.Style
- Subtle lipgloss.Style
-
- // Tags
- TagBase lipgloss.Style
- TagError lipgloss.Style
- TagInfo lipgloss.Style
-
// Header
Header struct {
- Charm lipgloss.Style // Style for "Charm™" label
- Diagonals lipgloss.Style // Style for diagonal separators (╱)
- Percentage lipgloss.Style // Style for context percentage
- Keystroke lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
- KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
- WorkingDir lipgloss.Style // Style for current working directory
- Separator lipgloss.Style // Style for separator dots (•)
+ Charm lipgloss.Style // Style for "Charm™" label
+ Diagonals lipgloss.Style // Style for diagonal separators (╱)
+ Percentage lipgloss.Style // Style for context percentage
+ Keystroke lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
+ KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
+ WorkingDir lipgloss.Style // Style for current working directory
+ Separator lipgloss.Style // Style for separator dots (•)
+ Wrapper lipgloss.Style // Outer container for the entire header row
+ LogoGradCanvas lipgloss.Style // Canvas for the compact "CRUSH" gradient
+ LogoGradFromColor color.Color // "CRUSH" wordmark gradient start
+ LogoGradToColor color.Color // "CRUSH" wordmark gradient end
}
CompactDetails struct {
@@ -88,43 +79,18 @@ type Styles struct {
Title lipgloss.Style
}
- // Panels
- PanelMuted lipgloss.Style
- PanelBase lipgloss.Style
-
- // Line numbers for code blocks
- LineNumber lipgloss.Style
-
- // Message borders
- FocusedMessageBorder lipgloss.Border
-
// Tool calls
- ToolCallPending lipgloss.Style
- ToolCallError lipgloss.Style
- ToolCallSuccess lipgloss.Style
- ToolCallCancelled lipgloss.Style
- EarlyStateMessage lipgloss.Style
+ ToolCallSuccess lipgloss.Style
// Text selection
TextSelection lipgloss.Style
- // LSP and MCP status indicators
- ResourceGroupTitle lipgloss.Style
- ResourceOfflineIcon lipgloss.Style
- ResourceBusyIcon lipgloss.Style
- ResourceErrorIcon lipgloss.Style
- ResourceOnlineIcon lipgloss.Style
- ResourceName lipgloss.Style
- ResourceStatus lipgloss.Style
- ResourceAdditionalText lipgloss.Style
-
// Markdown & Chroma
Markdown ansi.StyleConfig
- PlainMarkdown ansi.StyleConfig
+ QuietMarkdown ansi.StyleConfig
// Inputs
TextInput textinput.Styles
- TextArea textarea.Styles
// Help
Help help.Styles
@@ -136,62 +102,55 @@ type Styles struct {
FilePicker filepicker.Styles
// Buttons
- ButtonFocus lipgloss.Style
- ButtonBlur lipgloss.Style
-
- // Borders
- BorderFocus lipgloss.Style
- BorderBlur lipgloss.Style
+ Button struct {
+ Focused lipgloss.Style
+ Blurred lipgloss.Style
+ }
// Editor
- EditorPromptNormalFocused lipgloss.Style
- EditorPromptNormalBlurred lipgloss.Style
- EditorPromptYoloIconFocused lipgloss.Style
- EditorPromptYoloIconBlurred lipgloss.Style
- EditorPromptYoloDotsFocused lipgloss.Style
- EditorPromptYoloDotsBlurred lipgloss.Style
+ Editor struct {
+ Textarea textarea.Styles
+
+ // Normal mode prompt (default "::: ").
+ PromptNormalFocused lipgloss.Style
+ PromptNormalBlurred lipgloss.Style
+
+ // YOLO mode prompt (" ! " icon + ":::" dots).
+ PromptYoloIconFocused lipgloss.Style
+ PromptYoloIconBlurred lipgloss.Style
+ PromptYoloDotsFocused lipgloss.Style
+ PromptYoloDotsBlurred lipgloss.Style
+ }
// Radio
- RadioOn lipgloss.Style
- RadioOff lipgloss.Style
+ Radio struct {
+ On lipgloss.Style
+ Off lipgloss.Style
+ Label lipgloss.Style // Text next to a radio button
+ }
// Background
Background color.Color
// Logo
- LogoFieldColor color.Color
- LogoTitleColorA color.Color
- LogoTitleColorB color.Color
- LogoCharmColor color.Color
- LogoVersionColor color.Color
-
- // Colors - semantic colors for tool rendering.
- Primary color.Color
- Secondary color.Color
- Tertiary color.Color
- BgBase color.Color
- BgBaseLighter color.Color
- BgSubtle color.Color
- BgOverlay color.Color
- FgBase color.Color
- FgMuted color.Color
- FgHalfMuted color.Color
- FgSubtle color.Color
- Border color.Color
- BorderColor color.Color // Border focus color
- Error color.Color
- Warning color.Color
- Info color.Color
- White color.Color
- BlueLight color.Color
- Blue color.Color
- BlueDark color.Color
- GreenLight color.Color
- Green color.Color
- GreenDark color.Color
- Red color.Color
- RedDark color.Color
- Yellow color.Color
+ Logo struct {
+ FieldColor color.Color
+ TitleColorA color.Color
+ TitleColorB color.Color
+ CharmColor color.Color
+ VersionColor color.Color
+ SmallCharm lipgloss.Style // "Charm™" label in SmallRender
+ SmallDiagonals lipgloss.Style // Diagonal line fill in SmallRender
+ GradCanvas lipgloss.Style // Blank canvas for gradient painting
+ SmallGradFromColor color.Color // Small "Crush" wordmark gradient start
+ SmallGradToColor color.Color // Small "Crush" wordmark gradient end
+ }
+
+ // Working indicator gradient (spinners/shimmers on assistant "thinking",
+ // tool-call pending, CLI generating, startup).
+ WorkingGradFromColor color.Color
+ WorkingGradToColor color.Color
+ WorkingLabelColor color.Color // Label text color next to the indicator
// Section Title
Section struct {
@@ -214,41 +173,80 @@ type Styles struct {
InfoDiagnostic lipgloss.Style
}
+ // Sidebar
+ Sidebar struct {
+ SessionTitle lipgloss.Style // Current session title at top of sidebar
+ WorkingDir lipgloss.Style // Working directory path (PrettyPath)
+ }
+
+ // ModelInfo (model name, provider, reasoning, token/cost summary)
+ ModelInfo struct {
+ Icon lipgloss.Style // Model icon (◇)
+ Name lipgloss.Style // Model name text
+ Provider lipgloss.Style // "via <provider>" text
+ ProviderFallback lipgloss.Style // Provider on its own second line
+ Reasoning lipgloss.Style // Reasoning effort text
+ TokenCount lipgloss.Style // "(42K)" token count
+ TokenPercentage lipgloss.Style // "42%" percent of context window
+ Cost lipgloss.Style // "$0.42" cost readout
+ }
+
+ // Resource styles the LSP/MCP/skills sidebar lists: their heading,
+ // each row's status icon, name, status text, and truncation hints.
+ Resource struct {
+ Heading lipgloss.Style // Section header ("LSPs", "MCPs", "Skills")
+ Name lipgloss.Style // Resource name (e.g. "gopls")
+ StatusText lipgloss.Style // Row status description (e.g. "starting...")
+ OfflineIcon lipgloss.Style // Offline/unstarted/stopped status icon
+ DisabledIcon lipgloss.Style // Disabled status icon
+ BusyIcon lipgloss.Style // Busy/starting status icon
+ ErrorIcon lipgloss.Style // Error status icon
+ OnlineIcon lipgloss.Style // Online/ready status icon
+ AdditionalText lipgloss.Style // "None" and "…and N more" text
+ CapabilityCount lipgloss.Style // "N tools" / "N prompts" / "N resources"
+ RowTitleBase lipgloss.Style // Base style applied over row titles in common.Status
+ RowDescBase lipgloss.Style // Base style applied over row descriptions in common.Status
+ DefaultTitleFg color.Color // Default title color when opt is zero
+ DefaultDescFg color.Color // Default description color when opt is zero
+ }
+
// Files
Files struct {
- Path lipgloss.Style
- Additions lipgloss.Style
- Deletions lipgloss.Style
+ Path lipgloss.Style
+ Additions lipgloss.Style
+ Deletions lipgloss.Style
+ SectionTitle lipgloss.Style // "Modified Files" heading
+ EmptyMessage lipgloss.Style // "None" placeholder when no files
+ TruncationHint lipgloss.Style // "…and N more" message
}
// Chat
- Chat struct {
- // Message item styles
- Message struct {
- UserBlurred lipgloss.Style
- UserFocused lipgloss.Style
- AssistantBlurred lipgloss.Style
- AssistantFocused lipgloss.Style
- NoContent lipgloss.Style
- Thinking lipgloss.Style
- ErrorTag lipgloss.Style
- ErrorTitle lipgloss.Style
- ErrorDetails lipgloss.Style
- ToolCallFocused lipgloss.Style
- ToolCallCompact lipgloss.Style
- ToolCallBlurred lipgloss.Style
- SectionHeader lipgloss.Style
-
- // Thinking section styles
- ThinkingBox lipgloss.Style // Background for thinking content
- ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
- ThinkingFooterTitle lipgloss.Style // "Thought for" text
- ThinkingFooterDuration lipgloss.Style // Duration value
- AssistantInfoIcon lipgloss.Style
- AssistantInfoModel lipgloss.Style
- AssistantInfoProvider lipgloss.Style
- AssistantInfoDuration lipgloss.Style
- }
+ // Messages - chat message item styles
+ Messages struct {
+ UserBlurred lipgloss.Style
+ UserFocused lipgloss.Style
+ AssistantBlurred lipgloss.Style
+ AssistantFocused lipgloss.Style
+ NoContent lipgloss.Style
+ Thinking lipgloss.Style
+ ErrorTag lipgloss.Style
+ ErrorTitle lipgloss.Style
+ ErrorDetails lipgloss.Style
+ ToolCallFocused lipgloss.Style
+ ToolCallCompact lipgloss.Style
+ ToolCallBlurred lipgloss.Style
+ SectionHeader lipgloss.Style
+
+ // Thinking section styles
+ ThinkingBox lipgloss.Style // Background for thinking content
+ ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
+ ThinkingFooterTitle lipgloss.Style // "Thought for" text
+ ThinkingFooterDuration lipgloss.Style // Duration value
+ AssistantInfoIcon lipgloss.Style
+ AssistantInfoModel lipgloss.Style
+ AssistantInfoProvider lipgloss.Style
+ AssistantInfoDuration lipgloss.Style
+ AssistantCanceled lipgloss.Style // Italic "Canceled" footer
}
// Tool - styles for tool call rendering
@@ -316,6 +314,9 @@ type Styles struct {
TodoCompletedIcon lipgloss.Style // Completed todo icon
TodoInProgressIcon lipgloss.Style // In-progress todo icon
TodoPendingIcon lipgloss.Style // Pending todo icon
+ TodoStatusNote lipgloss.Style // " · completed N" / " · starting task" trailing note
+ TodoItem lipgloss.Style // Default body text for todo list items
+ TodoJustStarted lipgloss.Style // Text of the just-started todo in tool-call bodies
// MCP tools
MCPName lipgloss.Style // The mcp name
@@ -329,17 +330,26 @@ type Styles struct {
ResourceSize lipgloss.Style
MediaType lipgloss.Style
- // Docker MCP tools
- DockerMCPActionAdd lipgloss.Style // Docker MCP add action (green)
- DockerMCPActionDel lipgloss.Style // Docker MCP remove action (red)
+ // Action verb colors for tool-call headers.
+ ActionCreate lipgloss.Style // Constructive actions (e.g. "Add", "Create")
+ ActionDestroy lipgloss.Style // Destructive actions (e.g. "Remove", "Delete")
+
+ // Tool result helpers.
+ ResultEmpty lipgloss.Style // "No results" placeholder
+ ResultTruncation lipgloss.Style // "… and N more" truncation line
+ ResultItemName lipgloss.Style // Item name (left column in result lists)
+ ResultItemDesc lipgloss.Style // Item description (right column)
}
// Dialog styles
Dialog struct {
- Title lipgloss.Style
- TitleText lipgloss.Style
- TitleError lipgloss.Style
- TitleAccent lipgloss.Style
+ Title lipgloss.Style
+ TitleText lipgloss.Style
+ TitleError lipgloss.Style
+ TitleAccent lipgloss.Style
+ TitleLineBase lipgloss.Style // Base for the gradient ╱╱╱ next to dialog titles
+ TitleGradFromColor color.Color // Default dialog title ╱╱╱ gradient start
+ TitleGradToColor color.Color // Default dialog title ╱╱╱ gradient end
// View is the main content area style.
View lipgloss.Style
PrimaryText lipgloss.Style
@@ -381,7 +391,43 @@ type Styles struct {
InputRequiredMarkFocused lipgloss.Style
}
- Commands struct{}
+ // ListItem styles the info-text rendered alongside list items (commands,
+ // models, reasoning options). Sessions have their own overrides below.
+ ListItem struct {
+ InfoBlurred lipgloss.Style
+ InfoFocused lipgloss.Style
+ }
+
+ Models struct {
+ ConfiguredText lipgloss.Style // "Configured" badge shown on the ModelGroup header
+ }
+
+ Permissions struct {
+ KeyText lipgloss.Style // Left key cell of a key/value row
+ ValueText lipgloss.Style // Right value cell of a key/value row
+ ParamsBg color.Color // Background color behind highlighted JSON parameters
+ }
+
+ Quit struct {
+ Content lipgloss.Style // Wrapper for the quit dialog's inner content
+ Frame lipgloss.Style // Outer rounded border framing the quit dialog
+ }
+
+ APIKey struct {
+ Spinner lipgloss.Style // Loading spinner while validating the key
+ }
+
+ OAuth struct {
+ Spinner lipgloss.Style // Loading spinner
+ Instructions lipgloss.Style // Emphasized instruction text
+ UserCode lipgloss.Style // Prominent user code display
+ Success lipgloss.Style // Positive status text (e.g. "Authentication successful!")
+ Link lipgloss.Style // Underlined verification URL
+ Enter lipgloss.Style // "enter" keyword highlight in instructions
+ ErrorText lipgloss.Style // Error message when authentication fails
+ StatusText lipgloss.Style // Narrative status text ("Initializing...", "Verifying...", etc.)
+ UserCodeBg color.Color // Background color of the centered user-code box
+ }
ImagePreview lipgloss.Style
@@ -404,6 +450,9 @@ type Styles struct {
RenamingTitleGradientFromColor color.Color
RenamingTitleGradientToColor color.Color
RenamingPlaceholder lipgloss.Style
+
+ InfoBlurred lipgloss.Style // Timestamp text on unfocused session items
+ InfoFocused lipgloss.Style // Timestamp text on the focused session item
}
}
@@ -441,14 +490,22 @@ type Styles struct {
// Pills styles for todo/queue pills
Pills struct {
- Base lipgloss.Style // Base pill style with padding
- Focused lipgloss.Style // Focused pill with visible border
- Blurred lipgloss.Style // Blurred pill with hidden border
- QueueItemPrefix lipgloss.Style // Prefix for queue list items
- HelpKey lipgloss.Style // Keystroke hint style
- HelpText lipgloss.Style // Help action text style
- Area lipgloss.Style // Pills area container
- TodoSpinner lipgloss.Style // Todo spinner style
+ Base lipgloss.Style // Base pill style with padding
+ Focused lipgloss.Style // Focused pill with visible border
+ Blurred lipgloss.Style // Blurred pill with hidden border
+ QueueItemPrefix lipgloss.Style // Prefix for queue list items
+ QueueItemText lipgloss.Style // Queue list item body text
+ QueueLabel lipgloss.Style // "N Queued" label text
+ QueueIconBase lipgloss.Style // Base style for queue gradient triangles
+ QueueGradFromColor color.Color // Start color for queue indicator gradient
+ QueueGradToColor color.Color // End color for queue indicator gradient
+ TodoLabel lipgloss.Style // "To-Do" label
+ TodoProgress lipgloss.Style // Todo ratio (e.g. "2/5")
+ TodoCurrentTask lipgloss.Style // Current in-progress task name
+ TodoSpinner lipgloss.Style // Todo spinner style
+ HelpKey lipgloss.Style // Keystroke hint style
+ HelpText lipgloss.Style // Help action text style
+ Area lipgloss.Style // Pills area container
}
}
@@ -552,38 +609,17 @@ func DefaultStyles() Styles {
normalBorder := lipgloss.NormalBorder()
base := lipgloss.NewStyle().Foreground(fgBase)
+ muted := lipgloss.NewStyle().Foreground(fgMuted)
+ subtle := lipgloss.NewStyle().Foreground(fgSubtle)
s := Styles{}
s.Background = bgBase
// Populate color fields
- s.Primary = primary
- s.Secondary = secondary
- s.Tertiary = tertiary
- s.BgBase = bgBase
- s.BgBaseLighter = bgBaseLighter
- s.BgSubtle = bgSubtle
- s.BgOverlay = bgOverlay
- s.FgBase = fgBase
- s.FgMuted = fgMuted
- s.FgHalfMuted = fgHalfMuted
- s.FgSubtle = fgSubtle
- s.Border = border
- s.BorderColor = borderFocus
- s.Error = error
- s.Warning = warning
- s.Info = info
- s.White = white
- s.BlueLight = blueLight
- s.Blue = blue
- s.BlueDark = blueDark
- s.GreenLight = greenLight
- s.Green = green
- s.GreenDark = greenDark
- s.Red = red
- s.RedDark = redDark
- s.Yellow = yellow
+ s.WorkingGradFromColor = primary
+ s.WorkingGradToColor = secondary
+ s.WorkingLabelColor = fgBase
s.TextInput = textinput.Styles{
Focused: textinput.StyleState{
@@ -605,7 +641,7 @@ func DefaultStyles() Styles {
},
}
- s.TextArea = textarea.Styles{
+ s.Editor.Textarea = textarea.Styles{
Focused: textarea.StyleState{
Base: base,
Text: base,
@@ -840,10 +876,10 @@ func DefaultStyles() Styles {
},
}
- // PlainMarkdown style - muted colors on subtle background for thinking content.
+ // QuietMarkdown style - muted colors on subtle background for thinking content.
plainBg := new(bgBaseLighter.Hex())
plainFg := new(fgMuted.Hex())
- s.PlainMarkdown = ansi.StyleConfig{
+ s.QuietMarkdown = ansi.StyleConfig{
Document: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Color: plainFg,
@@ -1081,72 +1117,47 @@ func DefaultStyles() Styles {
}
// borders
- s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
-
- // text presets
- s.Base = lipgloss.NewStyle().Foreground(fgBase)
- s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
- s.HalfMuted = lipgloss.NewStyle().Foreground(fgHalfMuted)
- s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
-
- s.WindowTooSmall = s.Muted
-
- // tag presets
- s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
- s.TagError = s.TagBase.Background(redDark)
- s.TagInfo = s.TagBase.Background(blueLight)
+ s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
- // Compact header styles
s.Header.Charm = base.Foreground(secondary)
s.Header.Diagonals = base.Foreground(primary)
- s.Header.Percentage = s.Muted
- s.Header.Keystroke = s.Muted
- s.Header.KeystrokeTip = s.Subtle
- s.Header.WorkingDir = s.Muted
- s.Header.Separator = s.Subtle
-
- s.CompactDetails.Title = s.Base
- s.CompactDetails.View = s.Base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
- s.CompactDetails.Version = s.Muted
-
- // panels
- s.PanelMuted = s.Muted.Background(bgBaseLighter)
- s.PanelBase = lipgloss.NewStyle().Background(bgBase)
-
- // code line number
- s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
-
- // Tool calls
- s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
- s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
- s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
- // Cancelled uses muted tone but same glyph as pending
- s.ToolCallCancelled = s.Muted.SetString(ToolPending)
- s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
+ s.Header.Percentage = muted
+ s.Header.Keystroke = muted
+ s.Header.KeystrokeTip = subtle
+ s.Header.WorkingDir = muted
+ s.Header.Separator = subtle
+ s.Header.Wrapper = lipgloss.NewStyle().Foreground(fgBase)
+ s.Header.LogoGradCanvas = lipgloss.NewStyle()
+ s.Header.LogoGradFromColor = secondary
+ s.Header.LogoGradToColor = primary
+
+ s.CompactDetails.Title = base
+ s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
+ s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
// Tool rendering styles
s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
- s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
+ s.Tool.IconCancelled = muted.SetString(ToolPending)
s.Tool.NameNormal = base.Foreground(blue)
s.Tool.NameNested = base.Foreground(blue)
- s.Tool.ParamMain = s.Subtle
- s.Tool.ParamKey = s.Subtle
+ s.Tool.ParamMain = subtle
+ s.Tool.ParamKey = subtle
// Content rendering - prepared styles that accept width parameter
- s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
- s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
- s.Tool.ContentCodeLine = s.Base.Background(bgBase).PaddingLeft(2)
- s.Tool.ContentCodeTruncation = s.Muted.Background(bgBase).PaddingLeft(2)
+ s.Tool.ContentLine = muted.Background(bgBaseLighter)
+ s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
+ s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
+ s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
s.Tool.ContentCodeBg = bgBase
s.Tool.Body = base.PaddingLeft(2)
// Deprecated - kept for backward compatibility
- s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
- s.Tool.ContentText = s.Muted
+ s.Tool.ContentBg = muted.Background(bgBaseLighter)
+ s.Tool.ContentText = muted
s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
s.Tool.StateWaiting = base.Foreground(fgSubtle)
@@ -1156,7 +1167,7 @@ func DefaultStyles() Styles {
s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
// Diff and multi-edit styles
- s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
+ s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
@@ -1166,12 +1177,12 @@ func DefaultStyles() Styles {
s.Tool.JobIconSuccess = base.Foreground(green)
s.Tool.JobToolName = base.Foreground(blue)
s.Tool.JobAction = base.Foreground(blueDark)
- s.Tool.JobPID = s.Muted
- s.Tool.JobDescription = s.Subtle
+ s.Tool.JobPID = muted
+ s.Tool.JobDescription = subtle
// Agent task styles
s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
- s.Tool.AgentPrompt = s.Muted
+ s.Tool.AgentPrompt = muted
// Agentic fetch styles
s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
@@ -1181,6 +1192,9 @@ func DefaultStyles() Styles {
s.Tool.TodoCompletedIcon = base.Foreground(green)
s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
+ s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Tool.TodoItem = lipgloss.NewStyle().Foreground(fgBase)
+ s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(fgBase)
// MCP styles
s.Tool.MCPName = base.Foreground(blue)
@@ -1194,103 +1208,138 @@ func DefaultStyles() Styles {
s.Tool.MediaType = base
s.Tool.ResourceSize = base.Foreground(fgMuted)
- // Docker MCP styles
- s.Tool.DockerMCPActionAdd = base.Foreground(greenLight)
- s.Tool.DockerMCPActionDel = base.Foreground(red)
+ // Tool-call action verbs and result-list styling.
+ s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(greenLight)
+ s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(red)
+ s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
+ s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
// Buttons
- s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
- s.ButtonBlur = s.Base.Background(bgSubtle)
-
- // Borders
- s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
+ s.Button.Focused = lipgloss.NewStyle().Foreground(white).Background(secondary)
+ s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
// Editor
- s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
- s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
- s.EditorPromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
- s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
- s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
- s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
-
- s.RadioOn = s.HalfMuted.SetString(RadioOn)
- s.RadioOff = s.HalfMuted.SetString(RadioOff)
-
- // Logo colors
- s.LogoFieldColor = primary
- s.LogoTitleColorA = secondary
- s.LogoTitleColorB = primary
- s.LogoCharmColor = secondary
- s.LogoVersionColor = primary
+ s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
+ s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
+ s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
+ s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
+ s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
+ s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(charmtone.Squid)
+
+ s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
+ s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
+ s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
+
+ // Logo
+ s.Logo.FieldColor = primary
+ s.Logo.TitleColorA = secondary
+ s.Logo.TitleColorB = primary
+ s.Logo.CharmColor = secondary
+ s.Logo.VersionColor = primary
+ s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
+ s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
+ s.Logo.GradCanvas = lipgloss.NewStyle()
+ s.Logo.SmallGradFromColor = secondary
+ s.Logo.SmallGradToColor = primary
// Section
- s.Section.Title = s.Subtle
- s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
+ s.Section.Title = subtle
+ s.Section.Line = base.Foreground(charmtone.Charcoal)
// Initialize
- s.Initialize.Header = s.Base
- s.Initialize.Content = s.Muted
- s.Initialize.Accent = s.Base.Foreground(greenDark)
-
- // LSP and MCP status.
- s.ResourceGroupTitle = lipgloss.NewStyle().Foreground(charmtone.Oyster)
- s.ResourceOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
- s.ResourceBusyIcon = s.ResourceOfflineIcon.Foreground(charmtone.Citron)
- s.ResourceErrorIcon = s.ResourceOfflineIcon.Foreground(charmtone.Coral)
- s.ResourceOnlineIcon = s.ResourceOfflineIcon.Foreground(charmtone.Guac)
- s.ResourceName = lipgloss.NewStyle().Foreground(charmtone.Squid)
- s.ResourceStatus = lipgloss.NewStyle().Foreground(charmtone.Oyster)
- s.ResourceAdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+ s.Initialize.Header = base
+ s.Initialize.Content = muted
+ s.Initialize.Accent = base.Foreground(greenDark)
+
+ // ResourceGroup (LSP/MCP/skills sidebar lists).
+ s.Resource.Heading = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+ s.Resource.Name = lipgloss.NewStyle().Foreground(charmtone.Squid)
+ s.Resource.StatusText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+ s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
+ s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(charmtone.Citron)
+ s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(charmtone.Coral)
+ s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(charmtone.Guac)
+ s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
+ s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+ s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
+ s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
+ s.Resource.DefaultTitleFg = fgMuted
+ s.Resource.DefaultDescFg = fgSubtle
// LSP
- s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
- s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
- s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
- s.LSP.InfoDiagnostic = s.Base.Foreground(info)
+ s.LSP.ErrorDiagnostic = base.Foreground(redDark)
+ s.LSP.WarningDiagnostic = base.Foreground(warning)
+ s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
+ s.LSP.InfoDiagnostic = base.Foreground(info)
// Files
- s.Files.Path = s.Muted
- s.Files.Additions = s.Base.Foreground(greenDark)
- s.Files.Deletions = s.Base.Foreground(redDark)
+ s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
+ s.Files.Additions = lipgloss.NewStyle().Foreground(greenDark)
+ s.Files.Deletions = lipgloss.NewStyle().Foreground(redDark)
+ s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
+
+ // Sidebar
+ s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
+ s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
+
+ // ModelInfo
+ s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
+ s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
+ s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
+ s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
+ s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
+ s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
+
+ // ResourceGroup
+ s.Resource.DefaultTitleFg = fgMuted
+ s.Resource.DefaultDescFg = fgSubtle
// Chat
messageFocussedBorder := lipgloss.Border{
Left: "▌",
}
- s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
- s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
+ s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
+ s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
BorderForeground(primary).BorderStyle(normalBorder)
- s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
+ s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
BorderForeground(primary).BorderStyle(messageFocussedBorder)
- s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
- s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
+ s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
+ s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
- s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
- s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
+ s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
+ s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
Background(red).Foreground(white)
- s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
- s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
+ s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
// Message item styles
- s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
+ s.Messages.ToolCallFocused = muted.PaddingLeft(1).
BorderStyle(messageFocussedBorder).
BorderLeft(true).
BorderForeground(greenDark)
- s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
+ s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
// No padding or border for compact tool calls within messages
- s.Chat.Message.ToolCallCompact = s.Muted
- s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
- s.Chat.Message.AssistantInfoIcon = s.Subtle
- s.Chat.Message.AssistantInfoModel = s.Muted
- s.Chat.Message.AssistantInfoProvider = s.Subtle
- s.Chat.Message.AssistantInfoDuration = s.Subtle
+ s.Messages.ToolCallCompact = muted
+ s.Messages.SectionHeader = base.PaddingLeft(2)
+ s.Messages.AssistantInfoIcon = subtle
+ s.Messages.AssistantInfoModel = muted
+ s.Messages.AssistantInfoProvider = subtle
+ s.Messages.AssistantInfoDuration = subtle
+ s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
// Thinking section styles
- s.Chat.Message.ThinkingBox = s.Subtle.Background(bgBaseLighter)
- s.Chat.Message.ThinkingTruncationHint = s.Muted
- s.Chat.Message.ThinkingFooterTitle = s.Muted
- s.Chat.Message.ThinkingFooterDuration = s.Subtle
+ s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
+ s.Messages.ThinkingTruncationHint = muted
+ s.Messages.ThinkingFooterTitle = muted
+ s.Messages.ThinkingFooterDuration = subtle
// Text selection.
s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
@@ -1300,6 +1349,25 @@ func DefaultStyles() Styles {
s.Dialog.TitleText = base.Foreground(primary)
s.Dialog.TitleError = base.Foreground(red)
s.Dialog.TitleAccent = base.Foreground(green).Bold(true)
+ s.Dialog.TitleLineBase = lipgloss.NewStyle()
+ s.Dialog.TitleGradFromColor = primary
+ s.Dialog.TitleGradToColor = secondary
+
+ // Dialog.ListItem (commands, reasoning, models)
+ s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
+ s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
+
+ // Dialog.Models
+ s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
+
+ // Dialog.Permissions
+ s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
+ s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
+ s.Dialog.Permissions.ParamsBg = bgSubtle
+
+ // Dialog.Quit
+ s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
+ s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
@@ -1323,6 +1391,20 @@ func DefaultStyles() Styles {
s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
+ // API key input dialog
+ s.Dialog.APIKey.Spinner = base.Foreground(green)
+
+ // OAuth dialog
+ s.Dialog.OAuth.Spinner = base.Foreground(greenLight)
+ s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(white)
+ s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(white)
+ s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(greenLight)
+ s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(greenDark).Underline(true)
+ s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
+ s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
+ s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
+ s.Dialog.OAuth.UserCodeBg = bgBaseLighter
+
s.Dialog.Arguments.Content = base.Padding(1)
s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
@@ -1332,20 +1414,22 @@ func DefaultStyles() Styles {
s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(red)
s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(red)
- s.Dialog.Sessions.DeletingMessage = s.Base.Padding(1)
+ s.Dialog.Sessions.DeletingMessage = base.Padding(1)
s.Dialog.Sessions.DeletingTitleGradientFromColor = red
- s.Dialog.Sessions.DeletingTitleGradientToColor = s.Primary
+ s.Dialog.Sessions.DeletingTitleGradientToColor = primary
s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(red).Foreground(charmtone.Butter)
s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(charmtone.Zest)
s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(charmtone.Zest)
- s.Dialog.Sessions.RenamingingMessage = s.Base.Padding(1)
+ s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
s.Dialog.Sessions.RenamingTitleGradientFromColor = charmtone.Zest
s.Dialog.Sessions.RenamingTitleGradientToColor = charmtone.Bok
s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(charmtone.Squid)
+ s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
@@ -1375,11 +1459,19 @@ func DefaultStyles() Styles {
s.Pills.Base = base.Padding(0, 1)
s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
- s.Pills.QueueItemPrefix = s.Muted.SetString(" •")
- s.Pills.HelpKey = s.Muted
- s.Pills.HelpText = s.Subtle
+ s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString(" •")
+ s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
+ s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
+ s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
+ s.Pills.QueueGradFromColor = redDark
+ s.Pills.QueueGradToColor = secondary
+ s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
+ s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
+ s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
+ s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(greenDark)
+ s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
+ s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
s.Pills.Area = base
- s.Pills.TodoSpinner = base.Foreground(greenDark)
return s
}