on main help sign in
fix(styles): use semantic names in styles + drop deadcode
commit81daa266819ce8bf4bbb93bb03095116b8de42b4 authorChristian Rocha <christian@rocha.is> dateThu 23 Apr 2026, 17:11 (6 months ago) parent4f3a308
36 files changed+571−480
internal/app/app.go+3−3 internal/cmd/run.go+3−3 internal/ui/chat/assistant.go+14−14 internal/ui/chat/docker_mcp.go+5−5 internal/ui/chat/messages.go+5−5 internal/ui/chat/todos.go+6−6 internal/ui/chat/tools.go+7−7 internal/ui/chat/user.go+2−2 internal/ui/common/button.go+2−2 internal/ui/common/elements.go+14−14 internal/ui/common/markdown.go+3−3 internal/ui/dialog/api_key_input.go+2−2 internal/ui/dialog/arguments.go+1−1 internal/ui/dialog/commands.go+2−2 internal/ui/dialog/commands_item.go+2−2 internal/ui/dialog/common.go+6−4 internal/ui/dialog/models.go+5−5 internal/ui/dialog/models_item.go+3−3 internal/ui/dialog/oauth.go+10−13 internal/ui/dialog/permissions.go+4−4 internal/ui/dialog/quit.go+2−2 internal/ui/dialog/reasoning.go+2−2 internal/ui/dialog/sessions_item.go+2−2 internal/ui/logo/example/main.go+7−7 internal/ui/logo/logo.go+3−3 internal/ui/model/header.go+2−2 internal/ui/model/layout_test.go+1−1 internal/ui/model/lsp.go+16−16 internal/ui/model/mcp.go+16−16 internal/ui/model/pills.go+6−6 internal/ui/model/session.go+3−3 internal/ui/model/sidebar.go+1−1 internal/ui/model/skills.go+8−8 internal/ui/model/skills_test.go+2−2 internal/ui/model/ui.go+14−14 internal/ui/styles/styles.go+387−295
internal/app/app.go+3−3
@@ -242,14 +242,14 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt,
242242 if f, ok := output.(*os.File); ok && stdinTTY && stdoutTTY {
243243 hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, f)
244244 }
245− defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
245+ defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
246246
247247 spinner = format.NewSpinner(ctx, cancel, anim.Settings{
248248 Size: 10,
249249 Label: "Generating",
250250 LabelColor: defaultFG,
251− GradColorA: t.Primary,
252− GradColorB: t.Secondary,
251+ GradColorA: t.WorkingGradFromColor,
252+ GradColorB: t.WorkingGradToColor,
253253 CycleColors: true,
254254 })
255255 spinner.Start()
internal/cmd/run.go+3−3
@@ -195,14 +195,14 @@ func runNonInteractive(
195195 if stdinTTY && stdoutTTY {
196196 hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
197197 }
198− defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
198+ defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
199199
200200 spinner = format.NewSpinner(ctx, cancel, anim.Settings{
201201 Size: 10,
202202 Label: "Generating",
203203 LabelColor: defaultFG,
204− GradColorA: t.Primary,
205− GradColorB: t.Secondary,
204+ GradColorA: t.WorkingGradFromColor,
205+ GradColorB: t.WorkingGradToColor,
206206 CycleColors: true,
207207 })
208208 spinner.Start()
internal/ui/chat/assistant.go+14−14
@@ -48,9 +48,9 @@ func NewAssistantMessageItem(sty *styles.Styles, message *message.Message) Messa
4848 a.anim = anim.New(anim.Settings{
4949 ID: a.ID(),
5050 Size: 15,
51− GradColorA: sty.Primary,
52− GradColorB: sty.Secondary,
53− LabelColor: sty.FgBase,
51+ GradColorA: sty.WorkingGradFromColor,
52+ GradColorB: sty.WorkingGradToColor,
53+ LabelColor: sty.WorkingLabelColor,
5454 CycleColors: true,
5555 })
5656 return a
@@ -112,8 +112,8 @@ func (a *AssistantMessageItem) Render(width int) string {
112112 // it's wrapping logic.
113113 // We already know that the content is wrapped to the correct width in
114114 // RawRender, so we can just apply the styles directly to each line.
115− focused := a.sty.Chat.Message.AssistantFocused.Render()
116− blurred := a.sty.Chat.Message.AssistantBlurred.Render()
115+ focused := a.sty.Messages.AssistantFocused.Render()
116+ blurred := a.sty.Messages.AssistantBlurred.Render()
117117 rendered := a.RawRender(width)
118118 lines := strings.Split(rendered, "\n")
119119 for i, line := range lines {
@@ -149,7 +149,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
149149 if a.message.IsFinished() {
150150 switch a.message.FinishReason() {
151151 case message.FinishReasonCanceled:
152− messageParts = append(messageParts, a.sty.Base.Italic(true).Render("Canceled"))
152+ messageParts = append(messageParts, a.sty.Messages.AssistantCanceled.Render("Canceled"))
153153 case message.FinishReasonError:
154154 messageParts = append(messageParts, a.renderError(width))
155155 }
@@ -160,7 +160,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
160160
161161// renderThinking renders the thinking/reasoning content with footer.
162162func (a *AssistantMessageItem) renderThinking(thinking string, width int) string {
163− renderer := common.PlainMarkdownRenderer(a.sty, width)
163+ renderer := common.QuietMarkdownRenderer(a.sty, width)
164164 rendered, err := renderer.Render(thinking)
165165 if err != nil {
166166 rendered = thinking
@@ -173,13 +173,13 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
173173 isTruncated := totalLines > maxCollapsedThinkingHeight
174174 if !a.thinkingExpanded && isTruncated {
175175 lines = lines[totalLines-maxCollapsedThinkingHeight:]
176− hint := a.sty.Chat.Message.ThinkingTruncationHint.Render(
176+ hint := a.sty.Messages.ThinkingTruncationHint.Render(
177177 fmt.Sprintf(assistantMessageTruncateFormat, totalLines-maxCollapsedThinkingHeight),
178178 )
179179 lines = append([]string{hint, ""}, lines...)
180180 }
181181
182− thinkingStyle := a.sty.Chat.Message.ThinkingBox.Width(width)
182+ thinkingStyle := a.sty.Messages.ThinkingBox.Width(width)
183183 result := thinkingStyle.Render(strings.Join(lines, "\n"))
184184 a.thinkingBoxHeight = lipgloss.Height(result)
185185
@@ -188,8 +188,8 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
188188 if !a.message.IsThinking() || len(a.message.ToolCalls()) > 0 {
189189 duration := a.message.ThinkingDuration()
190190 if duration.String() != "0s" {
191− footer = a.sty.Chat.Message.ThinkingFooterTitle.Render("Thought for ") +
192− a.sty.Chat.Message.ThinkingFooterDuration.Render(duration.String())
191+ footer = a.sty.Messages.ThinkingFooterTitle.Render("Thought for ") +
192+ a.sty.Messages.ThinkingFooterDuration.Render(duration.String())
193193 }
194194 }
195195
@@ -222,10 +222,10 @@ func (a *AssistantMessageItem) renderSpinning() string {
222222// renderError renders an error message.
223223func (a *AssistantMessageItem) renderError(width int) string {
224224 finishPart := a.message.FinishPart()
225− errTag := a.sty.Chat.Message.ErrorTag.Render("ERROR")
225+ errTag := a.sty.Messages.ErrorTag.Render("ERROR")
226226 truncated := ansi.Truncate(finishPart.Message, width-2-lipgloss.Width(errTag), "...")
227− title := fmt.Sprintf("%s %s", errTag, a.sty.Chat.Message.ErrorTitle.Render(truncated))
228− details := a.sty.Chat.Message.ErrorDetails.Width(width - 2).Render(finishPart.Details)
227+ title := fmt.Sprintf("%s %s", errTag, a.sty.Messages.ErrorTitle.Render(truncated))
228+ details := a.sty.Messages.ErrorDetails.Width(width - 2).Render(finishPart.Details)
229229 return fmt.Sprintf("%s\n\n%s", title, details)
230230}
231231
internal/ui/chat/docker_mcp.go+5−5
@@ -173,7 +173,7 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
173173 }
174174
175175 if len(result.Servers) == 0 {
176− return sty.Subtle.Render("No MCP servers found.")
176+ return sty.Tool.ResultEmpty.Render("No MCP servers found.")
177177 }
178178
179179 bodyWidth := min(120, width) - toolBodyLeftPaddingTotal
@@ -181,10 +181,10 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
181181 moreServers := ""
182182 for i, server := range result.Servers {
183183 if i > 9 {
184− moreServers = sty.Subtle.Render(fmt.Sprintf("... and %d more", len(result.Servers)-10))
184+ moreServers = sty.Tool.ResultTruncation.Render(fmt.Sprintf("... and %d more", len(result.Servers)-10))
185185 break
186186 }
187− rows = append(rows, []string{sty.Base.Render(server.Name), sty.Subtle.Render(server.Description)})
187+ rows = append(rows, []string{sty.Tool.ResultItemName.Render(server.Name), sty.Tool.ResultItemDesc.Render(server.Description)})
188188 }
189189 serverTable := table.New().
190190 Wrap(false).
@@ -236,10 +236,10 @@ func (d *DockerMCPToolRenderContext) formatToolName(sty *styles.Styles, tool str
236236 action = "Find"
237237 case "mcp-add":
238238 action = "Add"
239− actionStyle = sty.Tool.DockerMCPActionAdd
239+ actionStyle = sty.Tool.ActionCreate
240240 case "mcp-remove":
241241 action = "Remove"
242− actionStyle = sty.Tool.DockerMCPActionDel
242+ actionStyle = sty.Tool.ActionDestroy
243243 case "code-mode":
244244 action = "Code Mode"
245245 default:
internal/ui/chat/messages.go+5−5
@@ -221,7 +221,7 @@ func (a *AssistantInfoItem) RawRender(width int) string {
221221
222222// Render implements MessageItem.
223223func (a *AssistantInfoItem) Render(width int) string {
224− prefix := a.sty.Chat.Message.SectionHeader.Render()
224+ prefix := a.sty.Messages.SectionHeader.Render()
225225 lines := strings.Split(a.RawRender(width), "\n")
226226 for i, line := range lines {
227227 lines[i] = prefix + line
@@ -236,18 +236,18 @@ func (a *AssistantInfoItem) renderContent(width int) string {
236236 }
237237 finishTime := time.Unix(finishData.Time, 0)
238238 duration := finishTime.Sub(a.lastUserMessageTime)
239− infoMsg := a.sty.Chat.Message.AssistantInfoDuration.Render(duration.String())
240− icon := a.sty.Chat.Message.AssistantInfoIcon.Render(styles.ModelIcon)
239+ infoMsg := a.sty.Messages.AssistantInfoDuration.Render(duration.String())
240+ icon := a.sty.Messages.AssistantInfoIcon.Render(styles.ModelIcon)
241241 model := a.cfg.GetModel(a.message.Provider, a.message.Model)
242242 if model == nil {
243243 model = &catwalk.Model{Name: "Unknown Model"}
244244 }
245− modelFormatted := a.sty.Chat.Message.AssistantInfoModel.Render(model.Name)
245+ modelFormatted := a.sty.Messages.AssistantInfoModel.Render(model.Name)
246246 providerName := a.message.Provider
247247 if providerConfig, ok := a.cfg.Providers.Get(a.message.Provider); ok {
248248 providerName = providerConfig.Name
249249 }
250− provider := a.sty.Chat.Message.AssistantInfoProvider.Render(fmt.Sprintf("via %s", providerName))
250+ provider := a.sty.Messages.AssistantInfoProvider.Render(fmt.Sprintf("via %s", providerName))
251251 assistant := fmt.Sprintf("%s %s %s %s", icon, modelFormatted, provider, infoMsg)
252252 return common.Section(a.sty, assistant, width)
253253}
internal/ui/chat/todos.go+6−6
@@ -91,16 +91,16 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
9191
9292 ratio := sty.Tool.TodoRatio.Render(fmt.Sprintf("%d/%d", meta.Completed, meta.Total))
9393 if hasCompleted && hasStarted {
94− text := sty.Subtle.Render(fmt.Sprintf(" · completed %d, starting next", len(meta.JustCompleted)))
94+ text := sty.Tool.TodoStatusNote.Render(fmt.Sprintf(" · completed %d, starting next", len(meta.JustCompleted)))
9595 headerText = fmt.Sprintf("%s%s", ratio, text)
9696 } else if hasCompleted {
97− text := sty.Subtle.Render(fmt.Sprintf(" · completed %d", len(meta.JustCompleted)))
97+ text := sty.Tool.TodoStatusNote.Render(fmt.Sprintf(" · completed %d", len(meta.JustCompleted)))
9898 if allCompleted {
99− text = sty.Subtle.Render(" · completed all")
99+ text = sty.Tool.TodoStatusNote.Render(" · completed all")
100100 }
101101 headerText = fmt.Sprintf("%s%s", ratio, text)
102102 } else if hasStarted {
103− headerText = fmt.Sprintf("%s%s", ratio, sty.Subtle.Render(" · starting task"))
103+ headerText = fmt.Sprintf("%s%s", ratio, sty.Tool.TodoStatusNote.Render(" · starting task"))
104104 } else {
105105 headerText = ratio
106106 }
@@ -111,7 +111,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
111111 body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
112112 } else if meta.JustStarted != "" {
113113 body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
114− sty.Base.Render(meta.JustStarted)
114+ sty.Tool.TodoJustStarted.Render(meta.JustStarted)
115115 }
116116 }
117117 }
@@ -148,7 +148,7 @@ func FormatTodosList(sty *styles.Styles, todos []session.Todo, inProgressIcon st
148148 var lines []string
149149 for _, todo := range sorted {
150150 var prefix string
151− textStyle := sty.Base
151+ textStyle := sty.Tool.TodoItem
152152
153153 switch todo.Status {
154154 case session.TodoStatusCompleted:
internal/ui/chat/tools.go+7−7
@@ -189,9 +189,9 @@ func newBaseToolMessageItem(
189189 t.anim = anim.New(anim.Settings{
190190 ID: toolCall.ID,
191191 Size: 15,
192− GradColorA: sty.Primary,
193− GradColorB: sty.Secondary,
194− LabelColor: sty.FgBase,
192+ GradColorA: sty.WorkingGradFromColor,
193+ GradColorB: sty.WorkingGradToColor,
194+ LabelColor: sty.WorkingLabelColor,
195195 CycleColors: true,
196196 })
197197
@@ -325,11 +325,11 @@ func (t *baseToolMessageItem) RawRender(width int) string {
325325func (t *baseToolMessageItem) Render(width int) string {
326326 var prefix string
327327 if t.isCompact {
328− prefix = t.sty.Chat.Message.ToolCallCompact.Render()
328+ prefix = t.sty.Messages.ToolCallCompact.Render()
329329 } else if t.focused {
330− prefix = t.sty.Chat.Message.ToolCallFocused.Render()
330+ prefix = t.sty.Messages.ToolCallFocused.Render()
331331 } else {
332− prefix = t.sty.Chat.Message.ToolCallBlurred.Render()
332+ prefix = t.sty.Messages.ToolCallBlurred.Render()
333333 }
334334 lines := strings.Split(t.RawRender(width), "\n")
335335 for i, ln := range lines {
@@ -799,7 +799,7 @@ func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, ex
799799 width = maxTextWidth
800800 }
801801
802− renderer := common.PlainMarkdownRenderer(sty, width)
802+ renderer := common.QuietMarkdownRenderer(sty, width)
803803 rendered, err := renderer.Render(content)
804804 if err != nil {
805805 return toolOutputPlainContent(sty, content, width, expanded)
internal/ui/chat/user.go+2−2
@@ -72,9 +72,9 @@ func (m *UserMessageItem) RawRender(width int) string {
7272func (m *UserMessageItem) Render(width int) string {
7373 var prefix string
7474 if m.focused {
75− prefix = m.sty.Chat.Message.UserFocused.Render()
75+ prefix = m.sty.Messages.UserFocused.Render()
7676 } else {
77− prefix = m.sty.Chat.Message.UserBlurred.Render()
77+ prefix = m.sty.Messages.UserBlurred.Render()
7878 }
7979 lines := strings.Split(m.RawRender(width), "\n")
8080 for i, line := range lines {
internal/ui/common/button.go+2−2
@@ -22,9 +22,9 @@ type ButtonOpts struct {
2222// Button creates a button with an underlined character and selection state
2323func Button(t *styles.Styles, opts ButtonOpts) string {
2424 // Select style based on selection state
25− style := t.ButtonBlur
25+ style := t.Button.Blurred
2626 if opts.Selected {
27− style = t.ButtonFocus
27+ style = t.Button.Focused
2828 }
2929
3030 text := opts.Text
internal/ui/common/elements.go+14−14
@@ -18,7 +18,7 @@ import (
1818// muted styling.
1919func PrettyPath(t *styles.Styles, path string, width int) string {
2020 formatted := home.Short(path)
21− return t.Muted.Width(width).Render(formatted)
21+ return t.Sidebar.WorkingDir.Width(width).Render(formatted)
2222}
2323
2424// FormatReasoningEffort formats a reasoning effort level for display.
@@ -39,13 +39,13 @@ type ModelContextInfo struct {
3939// ModelInfo renders model information including name, provider, reasoning
4040// settings, and optional context usage/cost.
4141func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string, context *ModelContextInfo, width int) string {
42− modelIcon := t.Subtle.Render(styles.ModelIcon)
43− modelName = t.Base.Render(modelName)
42+ modelIcon := t.ModelInfo.Icon.Render(styles.ModelIcon)
43+ modelName = t.ModelInfo.Name.Render(modelName)
4444
4545 // Build first line with model name and optionally provider on the same line
4646 var firstLine string
4747 if providerName != "" {
48− providerInfo := t.Muted.Render(fmt.Sprintf("via %s", providerName))
48+ providerInfo := t.ModelInfo.Provider.Render(fmt.Sprintf("via %s", providerName))
4949 modelWithProvider := fmt.Sprintf("%s %s %s", modelIcon, modelName, providerInfo)
5050
5151 // Check if it fits on one line
@@ -64,11 +64,11 @@ func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string,
6464 // If provider didn't fit on first line, add it as second line
6565 if providerName != "" && !strings.Contains(firstLine, "via") {
6666 providerInfo := fmt.Sprintf("via %s", providerName)
67− parts = append(parts, t.Muted.PaddingLeft(2).Render(providerInfo))
67+ parts = append(parts, t.ModelInfo.ProviderFallback.Render(providerInfo))
6868 }
6969
7070 if reasoningInfo != "" {
71− parts = append(parts, t.Subtle.PaddingLeft(2).Render(reasoningInfo))
71+ parts = append(parts, t.ModelInfo.Reasoning.Render(reasoningInfo))
7272 }
7373
7474 if context != nil {
@@ -103,10 +103,10 @@ func formatTokensAndCost(t *styles.Styles, tokens, contextWindow int64, cost flo
103103
104104 percentage := (float64(tokens) / float64(contextWindow)) * 100
105105
106− formattedCost := t.Muted.Render(fmt.Sprintf("$%.2f", cost))
106+ formattedCost := t.ModelInfo.Cost.Render(fmt.Sprintf("$%.2f", cost))
107107
108− formattedTokens = t.Subtle.Render(fmt.Sprintf("(%s)", formattedTokens))
109− formattedPercentage := t.Muted.Render(fmt.Sprintf("%d%%", int(percentage)))
108+ formattedTokens = t.ModelInfo.TokenCount.Render(fmt.Sprintf("(%s)", formattedTokens))
109+ formattedPercentage := t.ModelInfo.TokenPercentage.Render(fmt.Sprintf("%d%%", int(percentage)))
110110 formattedTokens = fmt.Sprintf("%s %s", formattedPercentage, formattedTokens)
111111 if percentage > 80 {
112112 formattedTokens = fmt.Sprintf("%s %s", styles.LSPWarningIcon, formattedTokens)
@@ -133,10 +133,10 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
133133 title := opts.Title
134134 description := opts.Description
135135
136− titleColor := cmp.Or(opts.TitleColor, t.Muted.GetForeground())
137− descriptionColor := cmp.Or(opts.DescriptionColor, t.Subtle.GetForeground())
136+ titleColor := cmp.Or(opts.TitleColor, t.Resource.DefaultTitleFg)
137+ descriptionColor := cmp.Or(opts.DescriptionColor, t.Resource.DefaultDescFg)
138138
139− title = t.Base.Foreground(titleColor).Render(title)
139+ title = t.Resource.RowTitleBase.Foreground(titleColor).Render(title)
140140
141141 if description != "" {
142142 extraContentWidth := lipgloss.Width(opts.ExtraContent)
@@ -144,7 +144,7 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
144144 extraContentWidth += 1
145145 }
146146 description = ansi.Truncate(description, width-lipgloss.Width(icon)-lipgloss.Width(title)-2-extraContentWidth, "…")
147− description = t.Base.Foreground(descriptionColor).Render(description)
147+ description = t.Resource.RowDescBase.Foreground(descriptionColor).Render(description)
148148 }
149149
150150 var content []string
@@ -193,7 +193,7 @@ func DialogTitle(t *styles.Styles, title string, width int, fromColor, toColor c
193193 remainingWidth := width - length
194194 if remainingWidth > 0 {
195195 lines := strings.Repeat(char, remainingWidth)
196− lines = styles.ApplyForegroundGrad(t.Base, lines, fromColor, toColor)
196+ lines = styles.ApplyForegroundGrad(t.Dialog.TitleLineBase, lines, fromColor, toColor)
197197 title = title + " " + lines
198198 }
199199 return title
internal/ui/common/markdown.go+3−3
@@ -29,11 +29,11 @@ func MarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
2929 return r
3030}
3131
32−// PlainMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
32+// QuietMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
3333// (plain text with structure) and the given width.
34−func PlainMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
34+func QuietMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
3535 r, _ := glamour.NewTermRenderer(
36− glamour.WithStyles(sty.PlainMarkdown),
36+ glamour.WithStyles(sty.QuietMarkdown),
3737 glamour.WithWordWrap(width),
3838 glamour.WithChromaFormatter(formatterName),
3939 )
internal/ui/dialog/api_key_input.go+2−2
@@ -83,7 +83,7 @@ func NewAPIKeyInput(
8383
8484 m.spinner = spinner.New(
8585 spinner.WithSpinner(spinner.Dot),
86− spinner.WithStyle(t.Base.Foreground(t.Green)),
86+ spinner.WithStyle(t.Dialog.APIKey.Spinner),
8787 )
8888
8989 m.help = help.New()
@@ -202,7 +202,7 @@ func (m *APIKeyInput) headerView() string {
202202 return textStyle.Render(m.dialogTitle())
203203 }
204204 headerOffset := titleStyle.GetHorizontalFrameSize() + dialogStyle.GetHorizontalFrameSize()
205− return common.DialogTitle(t, titleStyle.Render(m.dialogTitle()), m.width-headerOffset, m.com.Styles.Primary, m.com.Styles.Secondary)
205+ return common.DialogTitle(t, titleStyle.Render(m.dialogTitle()), m.width-headerOffset, m.com.Styles.Dialog.TitleGradFromColor, m.com.Styles.Dialog.TitleGradToColor)
206206}
207207
208208func (m *APIKeyInput) dialogTitle() string {
internal/ui/dialog/arguments.go+1−1
@@ -314,7 +314,7 @@ func (a *Arguments) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
314314
315315 titleText := cmp.Or(a.title, "Arguments")
316316
317− header := common.DialogTitle(s, titleText, width, s.Primary, s.Secondary)
317+ header := common.DialogTitle(s, titleText, width, s.Dialog.TitleGradFromColor, s.Dialog.TitleGradToColor)
318318
319319 // Add description if available.
320320 var description string
internal/ui/dialog/commands.go+2−2
@@ -252,9 +252,9 @@ func commandsRadioView(sty *styles.Styles, selected CommandType, hasUserCmds boo
252252
253253 selectedFn := func(t CommandType) string {
254254 if t == selected {
255− return sty.RadioOn.Padding(0, 1).Render() + sty.HalfMuted.Render(t.String())
255+ return sty.Radio.On.Padding(0, 1).Render() + sty.Radio.Label.Render(t.String())
256256 }
257− return sty.RadioOff.Padding(0, 1).Render() + sty.HalfMuted.Render(t.String())
257+ return sty.Radio.Off.Padding(0, 1).Render() + sty.Radio.Label.Render(t.String())
258258 }
259259
260260 parts := []string{
internal/ui/dialog/commands_item.go+2−2
@@ -69,8 +69,8 @@ func (c *CommandItem) Render(width int) string {
6969 styles := ListItemStyles{
7070 ItemBlurred: c.t.Dialog.NormalItem,
7171 ItemFocused: c.t.Dialog.SelectedItem,
72− InfoTextBlurred: c.t.Base,
73− InfoTextFocused: c.t.Base,
72+ InfoTextBlurred: c.t.Dialog.ListItem.InfoBlurred,
73+ InfoTextFocused: c.t.Dialog.ListItem.InfoFocused,
7474 }
7575 return renderItem(styles, c.title, c.shortcut, c.focused, width, c.cache, &c.m)
7676}
internal/ui/dialog/common.go+6−4
@@ -65,9 +65,11 @@ type RenderContext struct {
6565 TitleStyle lipgloss.Style
6666 // ViewStyle is the style of the dialog title by default it uses Styles.Dialog.View
6767 ViewStyle lipgloss.Style
68− // TitleGradientFromColor is the color the title gradient starts by defaults its Style.Primary
68+ // TitleGradientFromColor is the color the title gradient starts by default
69+ // its Styles.Dialog.TitleGradFromColor
6970 TitleGradientFromColor color.Color
70− // TitleGradientToColor is the color the title gradient starts by defaults its Style.Secondary
71+ // TitleGradientToColor is the color the title gradient ends by default its
72+ // Styles.Dialog.TitleGradToColor
7173 TitleGradientToColor color.Color
7274 // Width is the total width of the dialog including any margins, borders,
7375 // and paddings.
@@ -98,8 +100,8 @@ func NewRenderContext(t *styles.Styles, width int) *RenderContext {
98100 Styles: t,
99101 TitleStyle: t.Dialog.Title,
100102 ViewStyle: t.Dialog.View,
101− TitleGradientFromColor: t.Primary,
102− TitleGradientToColor: t.Secondary,
103+ TitleGradientFromColor: t.Dialog.TitleGradFromColor,
104+ TitleGradientToColor: t.Dialog.TitleGradToColor,
103105 Width: width,
104106 Parts: []string{},
105107 }
internal/ui/dialog/models.go+5−5
@@ -236,13 +236,13 @@ func (m *Models) Cursor() *tea.Cursor {
236236// modelTypeRadioView returns the radio view for model type selection.
237237func (m *Models) modelTypeRadioView() string {
238238 t := m.com.Styles
239− textStyle := t.HalfMuted
240− largeRadioStyle := t.RadioOff
241− smallRadioStyle := t.RadioOff
239+ textStyle := t.Radio.Label
240+ largeRadioStyle := t.Radio.Off
241+ smallRadioStyle := t.Radio.Off
242242 if m.modelType == ModelTypeLarge {
243− largeRadioStyle = t.RadioOn
243+ largeRadioStyle = t.Radio.On
244244 } else {
245− smallRadioStyle = t.RadioOn
245+ smallRadioStyle = t.Radio.On
246246 }
247247
248248 largeRadio := largeRadioStyle.Padding(0, 1).Render()
internal/ui/dialog/models_item.go+3−3
@@ -38,7 +38,7 @@ func (m *ModelGroup) Render(width int) string {
3838 var configured string
3939 if m.configured {
4040 configuredIcon := m.t.ToolCallSuccess.Render()
41− configuredText := m.t.Subtle.Render("Configured")
41+ configuredText := m.t.Dialog.Models.ConfiguredText.Render("Configured")
4242 configured = configuredIcon + " " + configuredText
4343 }
4444
@@ -109,8 +109,8 @@ func (m *ModelItem) Render(width int) string {
109109 styles := ListItemStyles{
110110 ItemBlurred: m.t.Dialog.NormalItem,
111111 ItemFocused: m.t.Dialog.SelectedItem,
112− InfoTextBlurred: m.t.Base,
113− InfoTextFocused: m.t.Base,
112+ InfoTextBlurred: m.t.Dialog.ListItem.InfoBlurred,
113+ InfoTextFocused: m.t.Dialog.ListItem.InfoFocused,
114114 }
115115 return renderItem(styles, m.model.Name, providerInfo, m.focused, width, m.cache, &m.m)
116116}
internal/ui/dialog/oauth.go+10−13
@@ -94,7 +94,7 @@ func newOAuth(
9494
9595 m.spinner = spinner.New(
9696 spinner.WithSpinner(spinner.Dot),
97− spinner.WithStyle(t.Base.Foreground(t.GreenLight)),
97+ spinner.WithStyle(t.Dialog.OAuth.Spinner),
9898 )
9999
100100 m.help = help.New()
@@ -227,18 +227,18 @@ func (m *OAuth) headerContent() string {
227227 if m.isOnboarding {
228228 return textStyle.Render(dialogTitle)
229229 }
230− return common.DialogTitle(t, titleStyle.Render(dialogTitle), m.width-headerOffset, t.Primary, t.Secondary)
230+ return common.DialogTitle(t, titleStyle.Render(dialogTitle), m.width-headerOffset, t.Dialog.TitleGradFromColor, t.Dialog.TitleGradToColor)
231231}
232232
233233func (m *OAuth) innerDialogContent() string {
234234 var (
235235 t = m.com.Styles
236− whiteStyle = lipgloss.NewStyle().Foreground(t.White)
237− primaryStyle = lipgloss.NewStyle().Foreground(t.Primary)
238− greenStyle = lipgloss.NewStyle().Foreground(t.GreenLight)
239− linkStyle = lipgloss.NewStyle().Foreground(t.GreenDark).Underline(true)
240− errorStyle = lipgloss.NewStyle().Foreground(t.Error)
241− mutedStyle = lipgloss.NewStyle().Foreground(t.FgMuted)
236+ whiteStyle = t.Dialog.OAuth.Instructions
237+ primaryStyle = t.Dialog.OAuth.Enter
238+ greenStyle = t.Dialog.OAuth.Success
239+ linkStyle = t.Dialog.OAuth.Link
240+ errorStyle = t.Dialog.OAuth.ErrorText
241+ mutedStyle = t.Dialog.OAuth.StatusText
242242 )
243243
244244 switch m.State {
@@ -266,13 +266,10 @@ func (m *OAuth) innerDialogContent() string {
266266 Width(m.width-2).
267267 Height(7).
268268 Align(lipgloss.Center, lipgloss.Center).
269− Background(t.BgBaseLighter).
269+ Background(t.Dialog.OAuth.UserCodeBg).
270270 Margin(0, 1).
271271 Render(
272− lipgloss.NewStyle().
273− Bold(true).
274− Foreground(t.White).
275− Render(m.userCode),
272+ t.Dialog.OAuth.UserCode.Render(m.userCode),
276273 )
277274
278275 link := linkStyle.Hyperlink(m.verificationURL, "id=oauth-verify").Render(m.verificationURL)
internal/ui/dialog/permissions.go+4−4
@@ -443,7 +443,7 @@ func (p *Permissions) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
443443func (p *Permissions) renderHeader(contentWidth int) string {
444444 t := p.com.Styles
445445
446− title := common.DialogTitle(t, "Permission Required", contentWidth-t.Dialog.Title.GetHorizontalFrameSize(), t.Primary, t.Secondary)
446+ title := common.DialogTitle(t, "Permission Required", contentWidth-t.Dialog.Title.GetHorizontalFrameSize(), t.Dialog.TitleGradFromColor, t.Dialog.TitleGradToColor)
447447 title = t.Dialog.Title.Render(title)
448448
449449 // Tool info.
@@ -489,8 +489,8 @@ func (p *Permissions) renderHeader(contentWidth int) string {
489489
490490func (p *Permissions) renderKeyValue(key, value string, width int) string {
491491 t := p.com.Styles
492− keyStyle := t.Muted
493− valueStyle := t.Base
492+ keyStyle := t.Dialog.Permissions.KeyText
493+ valueStyle := t.Dialog.Permissions.ValueText
494494
495495 keyStr := keyStyle.Render(key)
496496 valueStr := valueStyle.Width(width - lipgloss.Width(keyStr) - 1).Render(" " + value)
@@ -699,7 +699,7 @@ func (p *Permissions) renderDefaultContent(width int) string {
699699 if err := json.Unmarshal([]byte(paramStr), &parsed); err == nil {
700700 if b, err := json.MarshalIndent(parsed, "", " "); err == nil {
701701 jsonContent := string(b)
702− highlighted, err := common.SyntaxHighlight(t, jsonContent, "params.json", t.BgSubtle)
702+ highlighted, err := common.SyntaxHighlight(t, jsonContent, "params.json", t.Dialog.Permissions.ParamsBg)
703703 if err == nil {
704704 jsonContent = highlighted
705705 }
internal/ui/dialog/quit.go+2−2
@@ -96,7 +96,7 @@ func (q *Quit) HandleMsg(msg tea.Msg) Action {
9696// Draw implements [Dialog].
9797func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
9898 const question = "Are you sure you want to quit?"
99− baseStyle := q.com.Styles.Base
99+ baseStyle := q.com.Styles.Dialog.Quit.Content
100100 buttonOpts := []common.ButtonOpts{
101101 {Text: "Yep!", Selected: !q.selectedNo, Padding: 3},
102102 {Text: "Nope", Selected: q.selectedNo, Padding: 3},
@@ -111,7 +111,7 @@ func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
111111 ),
112112 )
113113
114− view := q.com.Styles.BorderFocus.Render(content)
114+ view := q.com.Styles.Dialog.Quit.Frame.Render(content)
115115 DrawCenter(scr, area, view)
116116 return nil
117117}
internal/ui/dialog/reasoning.go+2−2
@@ -293,8 +293,8 @@ func (r *ReasoningItem) Render(width int) string {
293293 styles := ListItemStyles{
294294 ItemBlurred: r.t.Dialog.NormalItem,
295295 ItemFocused: r.t.Dialog.SelectedItem,
296− InfoTextBlurred: r.t.Base,
297− InfoTextFocused: r.t.Base,
296+ InfoTextBlurred: r.t.Dialog.ListItem.InfoBlurred,
297+ InfoTextFocused: r.t.Dialog.ListItem.InfoFocused,
298298 }
299299 return renderItem(styles, r.title, info, r.focused, width, r.cache, &r.m)
300300}
internal/ui/dialog/sessions_item.go+2−2
@@ -79,8 +79,8 @@ func (s *SessionItem) Render(width int) string {
7979 styles := ListItemStyles{
8080 ItemBlurred: s.t.Dialog.NormalItem,
8181 ItemFocused: s.t.Dialog.SelectedItem,
82− InfoTextBlurred: s.t.Subtle,
83− InfoTextFocused: s.t.Base,
82+ InfoTextBlurred: s.t.Dialog.Sessions.InfoBlurred,
83+ InfoTextFocused: s.t.Dialog.Sessions.InfoFocused,
8484 }
8585
8686 switch s.sessionsMode {
internal/ui/logo/example/main.go+7−7
@@ -20,23 +20,23 @@ func main() {
2020
2121 s := styles.DefaultStyles()
2222 opts := logo.Opts{
23− FieldColor: s.LogoFieldColor,
24− TitleColorA: s.LogoTitleColorA,
25− TitleColorB: s.LogoTitleColorB,
26− CharmColor: s.LogoCharmColor,
27− VersionColor: s.LogoVersionColor,
23+ FieldColor: s.Logo.FieldColor,
24+ TitleColorA: s.Logo.TitleColorA,
25+ TitleColorB: s.Logo.TitleColorB,
26+ CharmColor: s.Logo.CharmColor,
27+ VersionColor: s.Logo.VersionColor,
2828 Width: w,
2929 Unstable: true,
3030 }
3131
3232 renderCompact := func(hyper bool) string {
3333 opts.Hyper = hyper
34− return logo.Render(s.Base, "v1.0.0", true, opts)
34+ return logo.Render(s.Logo.GradCanvas, "v1.0.0", true, opts)
3535 }
3636
3737 renderWide := func(hyper bool) string {
3838 opts.Hyper = hyper
39− return logo.Render(s.Base, "v1.0.0", false, opts)
39+ return logo.Render(s.Logo.GradCanvas, "v1.0.0", false, opts)
4040 }
4141
4242 lipgloss.Println(
internal/ui/logo/logo.go+3−3
@@ -149,12 +149,12 @@ func Render(base lipgloss.Style, version string, compact bool, o Opts) string {
149149// SmallRender renders a smaller version of the Crush logo, suitable for
150150// smaller windows or sidebar usage.
151151func SmallRender(t *styles.Styles, width int) string {
152− title := t.Base.Foreground(t.Secondary).Render("Charm™")
153− title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Base, "Crush", t.Secondary, t.Primary))
152+ title := t.Logo.SmallCharm.Render("Charm™")
153+ title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Logo.GradCanvas, "Crush", t.Logo.SmallGradFromColor, t.Logo.SmallGradToColor))
154154 remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush"
155155 if remainingWidth > 0 {
156156 lines := strings.Repeat("╱", remainingWidth)
157− title = fmt.Sprintf("%s %s", title, t.Base.Foreground(t.Primary).Render(lines))
157+ title = fmt.Sprintf("%s %s", title, t.Logo.SmallDiagonals.Render(lines))
158158 }
159159 return title
160160}
internal/ui/model/header.go+2−2
@@ -39,7 +39,7 @@ func newHeader(com *common.Common) *header {
3939 }
4040 t := com.Styles
4141 h.compactLogo = t.Header.Charm.Render("Charm™") + " " +
42− styles.ApplyBoldForegroundGrad(t.Base, "CRUSH", t.Secondary, t.Primary) + " "
42+ styles.ApplyBoldForegroundGrad(t.Header.LogoGradCanvas, "CRUSH", t.Header.LogoGradFromColor, t.Header.LogoGradToColor) + " "
4343 return h
4444}
4545
@@ -102,7 +102,7 @@ func (h *header) drawHeader(
102102 b.WriteString(details)
103103
104104 view := uv.NewStyledString(
105− t.Base.Padding(0, rightPadding, 0, leftPadding).Render(b.String()))
105+ t.Header.Wrapper.Padding(0, rightPadding, 0, leftPadding).Render(b.String()))
106106 view.Draw(scr, area)
107107}
108108
internal/ui/model/layout_test.go+1−1
@@ -30,7 +30,7 @@ func newTestUI() *UI {
3030 com := common.DefaultCommon(nil)
3131
3232 ta := textarea.New()
33− ta.SetStyles(com.Styles.TextArea)
33+ ta.SetStyles(com.Styles.Editor.Textarea)
3434 ta.ShowLineNumbers = false
3535 ta.CharLimit = -1
3636 ta.SetVirtualCursor(false)
internal/ui/model/lsp.go+16−16
@@ -41,11 +41,11 @@ func (m *UI) lspInfo(width, maxItems int, isSection bool) string {
4141 lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
4242 }
4343
44− title := t.ResourceGroupTitle.Render("LSPs")
44+ title := t.Resource.Heading.Render("LSPs")
4545 if isSection {
4646 title = common.Section(t, title, width)
4747 }
48− list := t.ResourceAdditionalText.Render("None")
48+ list := t.Resource.AdditionalText.Render("None")
4949 if len(lsps) > 0 {
5050 list = lspList(t, lsps, width, maxItems)
5151 }
@@ -80,31 +80,31 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
8080 var renderedLsps []string
8181 for _, l := range lsps {
8282 var icon string
83− title := t.ResourceName.Render(l.Name)
83+ title := t.Resource.Name.Render(l.Name)
8484 var description string
8585 var diagnostics string
8686 switch l.State {
8787 case lsp.StateUnstarted:
88− icon = t.ResourceOfflineIcon.String()
89− description = t.ResourceStatus.Render("unstarted")
88+ icon = t.Resource.OfflineIcon.String()
89+ description = t.Resource.StatusText.Render("unstarted")
9090 case lsp.StateStopped:
91− icon = t.ResourceOfflineIcon.String()
92− description = t.ResourceStatus.Render("stopped")
91+ icon = t.Resource.OfflineIcon.String()
92+ description = t.Resource.StatusText.Render("stopped")
9393 case lsp.StateStarting:
94− icon = t.ResourceBusyIcon.String()
95− description = t.ResourceStatus.Render("starting...")
94+ icon = t.Resource.BusyIcon.String()
95+ description = t.Resource.StatusText.Render("starting...")
9696 case lsp.StateReady:
97− icon = t.ResourceOnlineIcon.String()
97+ icon = t.Resource.OnlineIcon.String()
9898 diagnostics = lspDiagnostics(t, l.Diagnostics)
9999 case lsp.StateError:
100− icon = t.ResourceErrorIcon.String()
101− description = t.ResourceStatus.Render("error")
100+ icon = t.Resource.ErrorIcon.String()
101+ description = t.Resource.StatusText.Render("error")
102102 if l.Error != nil {
103− description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", l.Error.Error()))
103+ description = t.Resource.StatusText.Render(fmt.Sprintf("error: %s", l.Error.Error()))
104104 }
105105 case lsp.StateDisabled:
106− icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
107− description = t.ResourceStatus.Render("disabled")
106+ icon = t.Resource.DisabledIcon.String()
107+ description = t.Resource.StatusText.Render("disabled")
108108 default:
109109 continue
110110 }
@@ -119,7 +119,7 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
119119 if len(renderedLsps) > maxItems {
120120 visibleItems := renderedLsps[:maxItems-1]
121121 remaining := len(renderedLsps) - maxItems
122− visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
122+ visibleItems = append(visibleItems, t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
123123 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
124124 }
125125 return lipgloss.JoinVertical(lipgloss.Left, renderedLsps...)
internal/ui/model/mcp.go+16−16
@@ -23,11 +23,11 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
2323 }
2424 }
2525
26− title := t.ResourceGroupTitle.Render("MCPs")
26+ title := t.Resource.Heading.Render("MCPs")
2727 if isSection {
2828 title = common.Section(t, title, width)
2929 }
30− list := t.ResourceAdditionalText.Render("None")
30+ list := t.Resource.AdditionalText.Render("None")
3131 if len(mcps) > 0 {
3232 list = mcpList(t, mcps, width, maxItems)
3333 }
@@ -39,13 +39,13 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
3939func mcpCounts(t *styles.Styles, counts mcp.Counts) string {
4040 var parts []string
4141 if counts.Tools > 0 {
42− parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d tools", counts.Tools)))
42+ parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d tools", counts.Tools)))
4343 }
4444 if counts.Prompts > 0 {
45− parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d prompts", counts.Prompts)))
45+ parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d prompts", counts.Prompts)))
4646 }
4747 if counts.Resources > 0 {
48− parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d resources", counts.Resources)))
48+ parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d resources", counts.Resources)))
4949 }
5050 return strings.Join(parts, " ")
5151}
@@ -65,28 +65,28 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
6565 if m.Name == config.DockerMCPName {
6666 title = "Docker MCP"
6767 }
68− title = t.ResourceName.Render(title)
68+ title = t.Resource.Name.Render(title)
6969 var description string
7070 var extraContent string
7171
7272 switch m.State {
7373 case mcp.StateStarting:
74− icon = t.ResourceBusyIcon.String()
75− description = t.ResourceStatus.Render("starting...")
74+ icon = t.Resource.BusyIcon.String()
75+ description = t.Resource.StatusText.Render("starting...")
7676 case mcp.StateConnected:
77− icon = t.ResourceOnlineIcon.String()
77+ icon = t.Resource.OnlineIcon.String()
7878 extraContent = mcpCounts(t, m.Counts)
7979 case mcp.StateError:
80− icon = t.ResourceErrorIcon.String()
81− description = t.ResourceStatus.Render("error")
80+ icon = t.Resource.ErrorIcon.String()
81+ description = t.Resource.StatusText.Render("error")
8282 if m.Error != nil {
83− description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", m.Error.Error()))
83+ description = t.Resource.StatusText.Render(fmt.Sprintf("error: %s", m.Error.Error()))
8484 }
8585 case mcp.StateDisabled:
86− icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
87− description = t.ResourceStatus.Render("disabled")
86+ icon = t.Resource.DisabledIcon.String()
87+ description = t.Resource.StatusText.Render("disabled")
8888 default:
89− icon = t.ResourceOfflineIcon.String()
89+ icon = t.Resource.OfflineIcon.String()
9090 }
9191
9292 renderedMcps = append(renderedMcps, common.Status(t, common.StatusOpts{
@@ -100,7 +100,7 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
100100 if len(renderedMcps) > maxItems {
101101 visibleItems := renderedMcps[:maxItems-1]
102102 remaining := len(renderedMcps) - maxItems
103− visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
103+ visibleItems = append(visibleItems, t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
104104 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
105105 }
106106 return lipgloss.JoinVertical(lipgloss.Left, renderedMcps...)
internal/ui/model/pills.go+6−6
@@ -56,12 +56,12 @@ func queuePill(queue int, focused, panelFocused bool, t *styles.Styles) string {
5656 if queue <= 0 {
5757 return ""
5858 }
59− triangles := styles.ForegroundGrad(t.Base, "▶▶▶▶▶▶▶▶▶", false, t.Pills.QueueGradFromColor, t.Secondary)
59+ triangles := styles.ForegroundGrad(t.Pills.QueueIconBase, "▶▶▶▶▶▶▶▶▶", false, t.Pills.QueueGradFromColor, t.Pills.QueueGradToColor)
6060 if queue < len(triangles) {
6161 triangles = triangles[:queue]
6262 }
6363
64− text := t.Base.Render(fmt.Sprintf("%d Queued", queue))
64+ text := t.Pills.QueueLabel.Render(fmt.Sprintf("%d Queued", queue))
6565 content := fmt.Sprintf("%s %s", strings.Join(triangles, ""), text)
6666 return pillStyle(focused, panelFocused, t).Render(content)
6767}
@@ -87,8 +87,8 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
8787
8888 total := len(todos)
8989
90− label := t.Base.Render("To-Do")
91− progress := t.Muted.Render(fmt.Sprintf("%d/%d", completed, total))
90+ label := t.Pills.TodoLabel.Render("To-Do")
91+ progress := t.Pills.TodoProgress.Render(fmt.Sprintf("%d/%d", completed, total))
9292
9393 var content string
9494 if panelFocused {
@@ -101,7 +101,7 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
101101 if len(taskText) > maxTaskDisplayLength {
102102 taskText = taskText[:maxTaskDisplayLength-1] + "…"
103103 }
104− task := t.Subtle.Render(taskText)
104+ task := t.Pills.TodoCurrentTask.Render(taskText)
105105 content = fmt.Sprintf("%s %s %s %s", spinnerView, label, progress, task)
106106 } else {
107107 content = fmt.Sprintf("%s %s", label, progress)
@@ -128,7 +128,7 @@ func queueList(queueItems []string, t *styles.Styles) string {
128128 text = text[:maxQueueDisplayLength-1] + "…"
129129 }
130130 prefix := t.Pills.QueueItemPrefix.Render() + " "
131− lines = append(lines, prefix+t.Muted.Render(text))
131+ lines = append(lines, prefix+t.Pills.QueueItemText.Render(text))
132132 }
133133
134134 return strings.Join(lines, "\n")
internal/ui/model/session.go+3−3
@@ -163,11 +163,11 @@ func (m *UI) handleFileEvent(file history.File) tea.Cmd {
163163func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
164164 t := m.com.Styles
165165
166− title := t.Subtle.Render("Modified Files")
166+ title := t.Files.SectionTitle.Render("Modified Files")
167167 if isSection {
168168 title = common.Section(t, "Modified Files", width)
169169 }
170− list := t.Subtle.Render("None")
170+ list := t.Files.EmptyMessage.Render("None")
171171 var filesWithChanges []SessionFile
172172 for _, f := range m.sessionFiles {
173173 if f.Additions == 0 && f.Deletions == 0 {
@@ -226,7 +226,7 @@ func fileList(t *styles.Styles, cwd string, filesWithChanges []SessionFile, widt
226226
227227 if len(filesWithChanges) > maxItems {
228228 remaining := len(filesWithChanges) - maxItems
229− renderedFiles = append(renderedFiles, t.Subtle.Render(fmt.Sprintf("…and %d more", remaining)))
229+ renderedFiles = append(renderedFiles, t.Files.TruncationHint.Render(fmt.Sprintf("…and %d more", remaining)))
230230 }
231231
232232 return lipgloss.JoinVertical(lipgloss.Left, renderedFiles...)
internal/ui/model/sidebar.go+1−1
@@ -138,7 +138,7 @@ func (m *UI) drawSidebar(scr uv.Screen, area uv.Rectangle) {
138138 width := area.Dx()
139139 height := area.Dy()
140140
141− title := t.Muted.Width(width).MaxHeight(2).Render(m.session.Title)
141+ title := t.Sidebar.SessionTitle.Width(width).MaxHeight(2).Render(m.session.Title)
142142 cwd := common.PrettyPath(t, m.com.Workspace.WorkingDir(), width)
143143 sidebarLogo := m.sidebarLogo
144144 if height < logoHeightBreakpoint {
internal/ui/model/skills.go+8−8
@@ -38,14 +38,14 @@ func cachedBuiltinSkills() []*skills.Skill {
3838func (m *UI) skillsInfo(width, maxItems int, isSection bool) string {
3939 t := m.com.Styles
4040
41− title := t.ResourceGroupTitle.Render("Skills")
41+ title := t.Resource.Heading.Render("Skills")
4242 if isSection {
4343 title = common.Section(t, title, width)
4444 }
4545
4646 items := m.skillStatusItems()
4747 if len(items) == 0 {
48− list := t.ResourceAdditionalText.Render("None")
48+ list := t.Resource.AdditionalText.Render("None")
4949 return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
5050 }
5151
@@ -68,14 +68,14 @@ func (m *UI) skillStatusItems() []skillStatusItem {
6868 name = filepath.Base(filepath.Dir(state.Path))
6969 }
7070 stateNames[name] = struct{}{}
71− icon := t.ResourceOnlineIcon.String()
71+ icon := t.Resource.OnlineIcon.String()
7272 if state.State == skills.StateError {
73− icon = t.ResourceErrorIcon.String()
73+ icon = t.Resource.ErrorIcon.String()
7474 }
7575 items = append(items, skillStatusItem{
7676 icon: icon,
7777 name: name,
78− title: t.ResourceName.Render(name),
78+ title: t.Resource.Name.Render(name),
7979 })
8080 }
8181
@@ -88,9 +88,9 @@ func (m *UI) skillStatusItems() []skillStatusItem {
8888 continue
8989 }
9090 items = append(items, skillStatusItem{
91− icon: t.ResourceOnlineIcon.String(),
91+ icon: t.Resource.OnlineIcon.String(),
9292 name: skill.Name,
93− title: t.ResourceName.Render(skill.Name),
93+ title: t.Resource.Name.Render(skill.Name),
9494 })
9595 }
9696
@@ -111,7 +111,7 @@ func skillsList(t *styles.Styles, items []skillStatusItem, width, maxItems int)
111111 remaining := len(items) - (maxItems - 1)
112112 items = append(visibleItems, skillStatusItem{
113113 name: "more",
114− title: t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)),
114+ title: t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)),
115115 })
116116 }
117117
internal/ui/model/skills_test.go+2−2
@@ -28,7 +28,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
2828
2929 var hasGoDoc bool
3030 for _, item := range items {
31− if item.title == st.ResourceName.Render("go-doc") {
31+ if item.title == st.Resource.Name.Render("go-doc") {
3232 hasGoDoc = true
3333 break
3434 }
@@ -43,7 +43,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
4343 if skill.Name == "go-doc" {
4444 continue
4545 }
46− expected := st.ResourceName.Render(skill.Name)
46+ expected := st.Resource.Name.Render(skill.Name)
4747 for _, item := range items {
4848 if item.title == expected {
4949 hasBuiltin = true
internal/ui/model/ui.go+14−14
@@ -269,7 +269,7 @@ type UI struct {
269269func New(com *common.Common, initialSessionID string, continueLast bool) *UI {
270270 // Editor components
271271 ta := textarea.New()
272− ta.SetStyles(com.Styles.TextArea)
272+ ta.SetStyles(com.Styles.Editor.Textarea)
273273 ta.ShowLineNumbers = false
274274 ta.CharLimit = -1
275275 ta.SetVirtualCursor(false)
@@ -2745,9 +2745,9 @@ func (m *UI) normalPromptFunc(info textarea.PromptInfo) string {
27452745 return "::: "
27462746 }
27472747 if info.Focused {
2748− return t.EditorPromptNormalFocused.Render()
2748+ return t.Editor.PromptNormalFocused.Render()
27492749 }
2750− return t.EditorPromptNormalBlurred.Render()
2750+ return t.Editor.PromptNormalBlurred.Render()
27512751}
27522752
27532753// yoloPromptFunc returns the yolo mode editor prompt style with warning icon
@@ -2756,15 +2756,15 @@ func (m *UI) yoloPromptFunc(info textarea.PromptInfo) string {
27562756 t := m.com.Styles
27572757 if info.LineNumber == 0 {
27582758 if info.Focused {
2759− return t.EditorPromptYoloIconFocused.Render()
2759+ return t.Editor.PromptYoloIconFocused.Render()
27602760 } else {
2761− return t.EditorPromptYoloIconBlurred.Render()
2761+ return t.Editor.PromptYoloIconBlurred.Render()
27622762 }
27632763 }
27642764 if info.Focused {
2765− return t.EditorPromptYoloDotsFocused.Render()
2765+ return t.Editor.PromptYoloDotsFocused.Render()
27662766 }
2767− return t.EditorPromptYoloDotsBlurred.Render()
2767+ return t.Editor.PromptYoloDotsBlurred.Render()
27682768}
27692769
27702770// closeCompletions closes the completions popup and resets state.
@@ -3536,7 +3536,7 @@ func (m *UI) drawSessionDetails(scr uv.Screen, area uv.Rectangle) {
35363536 blocks...,
35373537 )
35383538
3539− version := s.CompactDetails.Version.Foreground(s.Border).Width(width).AlignHorizontal(lipgloss.Right).Render(version.Version)
3539+ version := s.CompactDetails.Version.Width(width).AlignHorizontal(lipgloss.Right).Render(version.Version)
35403540
35413541 remainingHeight := height - lipgloss.Height(detailsHeader) - lipgloss.Height(version)
35423542
@@ -3651,12 +3651,12 @@ func (m *UI) disableDockerMCP() tea.Msg {
36513651
36523652// renderLogo renders the Crush logo with the given styles and dimensions.
36533653func renderLogo(t *styles.Styles, compact bool, width int) string {
3654− return logo.Render(t.Base, version.Version, compact, logo.Opts{
3655− FieldColor: t.LogoFieldColor,
3656− TitleColorA: t.LogoTitleColorA,
3657− TitleColorB: t.LogoTitleColorB,
3658− CharmColor: t.LogoCharmColor,
3659− VersionColor: t.LogoVersionColor,
3654+ return logo.Render(t.Logo.GradCanvas, version.Version, compact, logo.Opts{
3655+ FieldColor: t.Logo.FieldColor,
3656+ TitleColorA: t.Logo.TitleColorA,
3657+ TitleColorB: t.Logo.TitleColorB,
3658+ CharmColor: t.Logo.CharmColor,
3659+ VersionColor: t.Logo.VersionColor,
36603660 Width: width,
36613661 })
36623662}
internal/ui/styles/styles.go+387−295
@@ -58,28 +58,19 @@ const (
5858)
5959
6060type Styles struct {
61− WindowTooSmall lipgloss.Style
62−
63− // Reusable text styles
64− Base lipgloss.Style
65− Muted lipgloss.Style
66− HalfMuted lipgloss.Style
67− Subtle lipgloss.Style
68−
69− // Tags
70− TagBase lipgloss.Style
71− TagError lipgloss.Style
72− TagInfo lipgloss.Style
73−
7461 // Header
7562 Header struct {
76− Charm lipgloss.Style // Style for "Charm™" label
77− Diagonals lipgloss.Style // Style for diagonal separators (╱)
78− Percentage lipgloss.Style // Style for context percentage
79− Keystroke lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
80− KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
81− WorkingDir lipgloss.Style // Style for current working directory
82− Separator lipgloss.Style // Style for separator dots (•)
63+ Charm lipgloss.Style // Style for "Charm™" label
64+ Diagonals lipgloss.Style // Style for diagonal separators (╱)
65+ Percentage lipgloss.Style // Style for context percentage
66+ Keystroke lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
67+ KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
68+ WorkingDir lipgloss.Style // Style for current working directory
69+ Separator lipgloss.Style // Style for separator dots (•)
70+ Wrapper lipgloss.Style // Outer container for the entire header row
71+ LogoGradCanvas lipgloss.Style // Canvas for the compact "CRUSH" gradient
72+ LogoGradFromColor color.Color // "CRUSH" wordmark gradient start
73+ LogoGradToColor color.Color // "CRUSH" wordmark gradient end
8374 }
8475
8576 CompactDetails struct {
@@ -88,43 +79,18 @@ type Styles struct {
8879 Title lipgloss.Style
8980 }
9081
91− // Panels
92− PanelMuted lipgloss.Style
93− PanelBase lipgloss.Style
94−
95− // Line numbers for code blocks
96− LineNumber lipgloss.Style
97−
98− // Message borders
99− FocusedMessageBorder lipgloss.Border
100−
10182 // Tool calls
102− ToolCallPending lipgloss.Style
103− ToolCallError lipgloss.Style
104− ToolCallSuccess lipgloss.Style
105− ToolCallCancelled lipgloss.Style
106− EarlyStateMessage lipgloss.Style
83+ ToolCallSuccess lipgloss.Style
10784
10885 // Text selection
10986 TextSelection lipgloss.Style
11087
111− // LSP and MCP status indicators
112− ResourceGroupTitle lipgloss.Style
113− ResourceOfflineIcon lipgloss.Style
114− ResourceBusyIcon lipgloss.Style
115− ResourceErrorIcon lipgloss.Style
116− ResourceOnlineIcon lipgloss.Style
117− ResourceName lipgloss.Style
118− ResourceStatus lipgloss.Style
119− ResourceAdditionalText lipgloss.Style
120−
12188 // Markdown & Chroma
12289 Markdown ansi.StyleConfig
123− PlainMarkdown ansi.StyleConfig
90+ QuietMarkdown ansi.StyleConfig
12491
12592 // Inputs
12693 TextInput textinput.Styles
127− TextArea textarea.Styles
12894
12995 // Help
13096 Help help.Styles
@@ -136,62 +102,55 @@ type Styles struct {
136102 FilePicker filepicker.Styles
137103
138104 // Buttons
139− ButtonFocus lipgloss.Style
140− ButtonBlur lipgloss.Style
141−
142− // Borders
143− BorderFocus lipgloss.Style
144− BorderBlur lipgloss.Style
105+ Button struct {
106+ Focused lipgloss.Style
107+ Blurred lipgloss.Style
108+ }
145109
146110 // Editor
147− EditorPromptNormalFocused lipgloss.Style
148− EditorPromptNormalBlurred lipgloss.Style
149− EditorPromptYoloIconFocused lipgloss.Style
150− EditorPromptYoloIconBlurred lipgloss.Style
151− EditorPromptYoloDotsFocused lipgloss.Style
152− EditorPromptYoloDotsBlurred lipgloss.Style
111+ Editor struct {
112+ Textarea textarea.Styles
113+
114+ // Normal mode prompt (default "::: ").
115+ PromptNormalFocused lipgloss.Style
116+ PromptNormalBlurred lipgloss.Style
117+
118+ // YOLO mode prompt (" ! " icon + ":::" dots).
119+ PromptYoloIconFocused lipgloss.Style
120+ PromptYoloIconBlurred lipgloss.Style
121+ PromptYoloDotsFocused lipgloss.Style
122+ PromptYoloDotsBlurred lipgloss.Style
123+ }
153124
154125 // Radio
155− RadioOn lipgloss.Style
156− RadioOff lipgloss.Style
126+ Radio struct {
127+ On lipgloss.Style
128+ Off lipgloss.Style
129+ Label lipgloss.Style // Text next to a radio button
130+ }
157131
158132 // Background
159133 Background color.Color
160134
161135 // Logo
162− LogoFieldColor color.Color
163− LogoTitleColorA color.Color
164− LogoTitleColorB color.Color
165− LogoCharmColor color.Color
166− LogoVersionColor color.Color
167−
168− // Colors - semantic colors for tool rendering.
169− Primary color.Color
170− Secondary color.Color
171− Tertiary color.Color
172− BgBase color.Color
173− BgBaseLighter color.Color
174− BgSubtle color.Color
175− BgOverlay color.Color
176− FgBase color.Color
177− FgMuted color.Color
178− FgHalfMuted color.Color
179− FgSubtle color.Color
180− Border color.Color
181− BorderColor color.Color // Border focus color
182− Error color.Color
183− Warning color.Color
184− Info color.Color
185− White color.Color
186− BlueLight color.Color
187− Blue color.Color
188− BlueDark color.Color
189− GreenLight color.Color
190− Green color.Color
191− GreenDark color.Color
192− Red color.Color
193− RedDark color.Color
194− Yellow color.Color
136+ Logo struct {
137+ FieldColor color.Color
138+ TitleColorA color.Color
139+ TitleColorB color.Color
140+ CharmColor color.Color
141+ VersionColor color.Color
142+ SmallCharm lipgloss.Style // "Charm™" label in SmallRender
143+ SmallDiagonals lipgloss.Style // Diagonal line fill in SmallRender
144+ GradCanvas lipgloss.Style // Blank canvas for gradient painting
145+ SmallGradFromColor color.Color // Small "Crush" wordmark gradient start
146+ SmallGradToColor color.Color // Small "Crush" wordmark gradient end
147+ }
148+
149+ // Working indicator gradient (spinners/shimmers on assistant "thinking",
150+ // tool-call pending, CLI generating, startup).
151+ WorkingGradFromColor color.Color
152+ WorkingGradToColor color.Color
153+ WorkingLabelColor color.Color // Label text color next to the indicator
195154
196155 // Section Title
197156 Section struct {
@@ -214,41 +173,80 @@ type Styles struct {
214173 InfoDiagnostic lipgloss.Style
215174 }
216175
176+ // Sidebar
177+ Sidebar struct {
178+ SessionTitle lipgloss.Style // Current session title at top of sidebar
179+ WorkingDir lipgloss.Style // Working directory path (PrettyPath)
180+ }
181+
182+ // ModelInfo (model name, provider, reasoning, token/cost summary)
183+ ModelInfo struct {
184+ Icon lipgloss.Style // Model icon (◇)
185+ Name lipgloss.Style // Model name text
186+ Provider lipgloss.Style // "via <provider>" text
187+ ProviderFallback lipgloss.Style // Provider on its own second line
188+ Reasoning lipgloss.Style // Reasoning effort text
189+ TokenCount lipgloss.Style // "(42K)" token count
190+ TokenPercentage lipgloss.Style // "42%" percent of context window
191+ Cost lipgloss.Style // "$0.42" cost readout
192+ }
193+
194+ // Resource styles the LSP/MCP/skills sidebar lists: their heading,
195+ // each row's status icon, name, status text, and truncation hints.
196+ Resource struct {
197+ Heading lipgloss.Style // Section header ("LSPs", "MCPs", "Skills")
198+ Name lipgloss.Style // Resource name (e.g. "gopls")
199+ StatusText lipgloss.Style // Row status description (e.g. "starting...")
200+ OfflineIcon lipgloss.Style // Offline/unstarted/stopped status icon
201+ DisabledIcon lipgloss.Style // Disabled status icon
202+ BusyIcon lipgloss.Style // Busy/starting status icon
203+ ErrorIcon lipgloss.Style // Error status icon
204+ OnlineIcon lipgloss.Style // Online/ready status icon
205+ AdditionalText lipgloss.Style // "None" and "…and N more" text
206+ CapabilityCount lipgloss.Style // "N tools" / "N prompts" / "N resources"
207+ RowTitleBase lipgloss.Style // Base style applied over row titles in common.Status
208+ RowDescBase lipgloss.Style // Base style applied over row descriptions in common.Status
209+ DefaultTitleFg color.Color // Default title color when opt is zero
210+ DefaultDescFg color.Color // Default description color when opt is zero
211+ }
212+
217213 // Files
218214 Files struct {
219− Path lipgloss.Style
220− Additions lipgloss.Style
221− Deletions lipgloss.Style
215+ Path lipgloss.Style
216+ Additions lipgloss.Style
217+ Deletions lipgloss.Style
218+ SectionTitle lipgloss.Style // "Modified Files" heading
219+ EmptyMessage lipgloss.Style // "None" placeholder when no files
220+ TruncationHint lipgloss.Style // "…and N more" message
222221 }
223222
224223 // Chat
225− Chat struct {
226− // Message item styles
227− Message struct {
228− UserBlurred lipgloss.Style
229− UserFocused lipgloss.Style
230− AssistantBlurred lipgloss.Style
231− AssistantFocused lipgloss.Style
232− NoContent lipgloss.Style
233− Thinking lipgloss.Style
234− ErrorTag lipgloss.Style
235− ErrorTitle lipgloss.Style
236− ErrorDetails lipgloss.Style
237− ToolCallFocused lipgloss.Style
238− ToolCallCompact lipgloss.Style
239− ToolCallBlurred lipgloss.Style
240− SectionHeader lipgloss.Style
241−
242− // Thinking section styles
243− ThinkingBox lipgloss.Style // Background for thinking content
244− ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
245− ThinkingFooterTitle lipgloss.Style // "Thought for" text
246− ThinkingFooterDuration lipgloss.Style // Duration value
247− AssistantInfoIcon lipgloss.Style
248− AssistantInfoModel lipgloss.Style
249− AssistantInfoProvider lipgloss.Style
250− AssistantInfoDuration lipgloss.Style
251− }
224+ // Messages - chat message item styles
225+ Messages struct {
226+ UserBlurred lipgloss.Style
227+ UserFocused lipgloss.Style
228+ AssistantBlurred lipgloss.Style
229+ AssistantFocused lipgloss.Style
230+ NoContent lipgloss.Style
231+ Thinking lipgloss.Style
232+ ErrorTag lipgloss.Style
233+ ErrorTitle lipgloss.Style
234+ ErrorDetails lipgloss.Style
235+ ToolCallFocused lipgloss.Style
236+ ToolCallCompact lipgloss.Style
237+ ToolCallBlurred lipgloss.Style
238+ SectionHeader lipgloss.Style
239+
240+ // Thinking section styles
241+ ThinkingBox lipgloss.Style // Background for thinking content
242+ ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
243+ ThinkingFooterTitle lipgloss.Style // "Thought for" text
244+ ThinkingFooterDuration lipgloss.Style // Duration value
245+ AssistantInfoIcon lipgloss.Style
246+ AssistantInfoModel lipgloss.Style
247+ AssistantInfoProvider lipgloss.Style
248+ AssistantInfoDuration lipgloss.Style
249+ AssistantCanceled lipgloss.Style // Italic "Canceled" footer
252250 }
253251
254252 // Tool - styles for tool call rendering
@@ -316,6 +314,9 @@ type Styles struct {
316314 TodoCompletedIcon lipgloss.Style // Completed todo icon
317315 TodoInProgressIcon lipgloss.Style // In-progress todo icon
318316 TodoPendingIcon lipgloss.Style // Pending todo icon
317+ TodoStatusNote lipgloss.Style // " · completed N" / " · starting task" trailing note
318+ TodoItem lipgloss.Style // Default body text for todo list items
319+ TodoJustStarted lipgloss.Style // Text of the just-started todo in tool-call bodies
319320
320321 // MCP tools
321322 MCPName lipgloss.Style // The mcp name
@@ -329,17 +330,26 @@ type Styles struct {
329330 ResourceSize lipgloss.Style
330331 MediaType lipgloss.Style
331332
332− // Docker MCP tools
333− DockerMCPActionAdd lipgloss.Style // Docker MCP add action (green)
334− DockerMCPActionDel lipgloss.Style // Docker MCP remove action (red)
333+ // Action verb colors for tool-call headers.
334+ ActionCreate lipgloss.Style // Constructive actions (e.g. "Add", "Create")
335+ ActionDestroy lipgloss.Style // Destructive actions (e.g. "Remove", "Delete")
336+
337+ // Tool result helpers.
338+ ResultEmpty lipgloss.Style // "No results" placeholder
339+ ResultTruncation lipgloss.Style // "… and N more" truncation line
340+ ResultItemName lipgloss.Style // Item name (left column in result lists)
341+ ResultItemDesc lipgloss.Style // Item description (right column)
335342 }
336343
337344 // Dialog styles
338345 Dialog struct {
339− Title lipgloss.Style
340− TitleText lipgloss.Style
341− TitleError lipgloss.Style
342− TitleAccent lipgloss.Style
346+ Title lipgloss.Style
347+ TitleText lipgloss.Style
348+ TitleError lipgloss.Style
349+ TitleAccent lipgloss.Style
350+ TitleLineBase lipgloss.Style // Base for the gradient ╱╱╱ next to dialog titles
351+ TitleGradFromColor color.Color // Default dialog title ╱╱╱ gradient start
352+ TitleGradToColor color.Color // Default dialog title ╱╱╱ gradient end
343353 // View is the main content area style.
344354 View lipgloss.Style
345355 PrimaryText lipgloss.Style
@@ -381,7 +391,43 @@ type Styles struct {
381391 InputRequiredMarkFocused lipgloss.Style
382392 }
383393
384− Commands struct{}
394+ // ListItem styles the info-text rendered alongside list items (commands,
395+ // models, reasoning options). Sessions have their own overrides below.
396+ ListItem struct {
397+ InfoBlurred lipgloss.Style
398+ InfoFocused lipgloss.Style
399+ }
400+
401+ Models struct {
402+ ConfiguredText lipgloss.Style // "Configured" badge shown on the ModelGroup header
403+ }
404+
405+ Permissions struct {
406+ KeyText lipgloss.Style // Left key cell of a key/value row
407+ ValueText lipgloss.Style // Right value cell of a key/value row
408+ ParamsBg color.Color // Background color behind highlighted JSON parameters
409+ }
410+
411+ Quit struct {
412+ Content lipgloss.Style // Wrapper for the quit dialog's inner content
413+ Frame lipgloss.Style // Outer rounded border framing the quit dialog
414+ }
415+
416+ APIKey struct {
417+ Spinner lipgloss.Style // Loading spinner while validating the key
418+ }
419+
420+ OAuth struct {
421+ Spinner lipgloss.Style // Loading spinner
422+ Instructions lipgloss.Style // Emphasized instruction text
423+ UserCode lipgloss.Style // Prominent user code display
424+ Success lipgloss.Style // Positive status text (e.g. "Authentication successful!")
425+ Link lipgloss.Style // Underlined verification URL
426+ Enter lipgloss.Style // "enter" keyword highlight in instructions
427+ ErrorText lipgloss.Style // Error message when authentication fails
428+ StatusText lipgloss.Style // Narrative status text ("Initializing...", "Verifying...", etc.)
429+ UserCodeBg color.Color // Background color of the centered user-code box
430+ }
385431
386432 ImagePreview lipgloss.Style
387433
@@ -404,6 +450,9 @@ type Styles struct {
404450 RenamingTitleGradientFromColor color.Color
405451 RenamingTitleGradientToColor color.Color
406452 RenamingPlaceholder lipgloss.Style
453+
454+ InfoBlurred lipgloss.Style // Timestamp text on unfocused session items
455+ InfoFocused lipgloss.Style // Timestamp text on the focused session item
407456 }
408457 }
409458
@@ -441,14 +490,22 @@ type Styles struct {
441490
442491 // Pills styles for todo/queue pills
443492 Pills struct {
444− Base lipgloss.Style // Base pill style with padding
445− Focused lipgloss.Style // Focused pill with visible border
446− Blurred lipgloss.Style // Blurred pill with hidden border
447− QueueItemPrefix lipgloss.Style // Prefix for queue list items
448− HelpKey lipgloss.Style // Keystroke hint style
449− HelpText lipgloss.Style // Help action text style
450− Area lipgloss.Style // Pills area container
451− TodoSpinner lipgloss.Style // Todo spinner style
493+ Base lipgloss.Style // Base pill style with padding
494+ Focused lipgloss.Style // Focused pill with visible border
495+ Blurred lipgloss.Style // Blurred pill with hidden border
496+ QueueItemPrefix lipgloss.Style // Prefix for queue list items
497+ QueueItemText lipgloss.Style // Queue list item body text
498+ QueueLabel lipgloss.Style // "N Queued" label text
499+ QueueIconBase lipgloss.Style // Base style for queue gradient triangles
500+ QueueGradFromColor color.Color // Start color for queue indicator gradient
501+ QueueGradToColor color.Color // End color for queue indicator gradient
502+ TodoLabel lipgloss.Style // "To-Do" label
503+ TodoProgress lipgloss.Style // Todo ratio (e.g. "2/5")
504+ TodoCurrentTask lipgloss.Style // Current in-progress task name
505+ TodoSpinner lipgloss.Style // Todo spinner style
506+ HelpKey lipgloss.Style // Keystroke hint style
507+ HelpText lipgloss.Style // Help action text style
508+ Area lipgloss.Style // Pills area container
452509 }
453510}
454511
@@ -552,38 +609,17 @@ func DefaultStyles() Styles {
552609 normalBorder := lipgloss.NormalBorder()
553610
554611 base := lipgloss.NewStyle().Foreground(fgBase)
612+ muted := lipgloss.NewStyle().Foreground(fgMuted)
613+ subtle := lipgloss.NewStyle().Foreground(fgSubtle)
555614
556615 s := Styles{}
557616
558617 s.Background = bgBase
559618
560619 // Populate color fields
561− s.Primary = primary
562− s.Secondary = secondary
563− s.Tertiary = tertiary
564− s.BgBase = bgBase
565− s.BgBaseLighter = bgBaseLighter
566− s.BgSubtle = bgSubtle
567− s.BgOverlay = bgOverlay
568− s.FgBase = fgBase
569− s.FgMuted = fgMuted
570− s.FgHalfMuted = fgHalfMuted
571− s.FgSubtle = fgSubtle
572− s.Border = border
573− s.BorderColor = borderFocus
574− s.Error = error
575− s.Warning = warning
576− s.Info = info
577− s.White = white
578− s.BlueLight = blueLight
579− s.Blue = blue
580− s.BlueDark = blueDark
581− s.GreenLight = greenLight
582− s.Green = green
583− s.GreenDark = greenDark
584− s.Red = red
585− s.RedDark = redDark
586− s.Yellow = yellow
620+ s.WorkingGradFromColor = primary
621+ s.WorkingGradToColor = secondary
622+ s.WorkingLabelColor = fgBase
587623
588624 s.TextInput = textinput.Styles{
589625 Focused: textinput.StyleState{
@@ -605,7 +641,7 @@ func DefaultStyles() Styles {
605641 },
606642 }
607643
608− s.TextArea = textarea.Styles{
644+ s.Editor.Textarea = textarea.Styles{
609645 Focused: textarea.StyleState{
610646 Base: base,
611647 Text: base,
@@ -840,10 +876,10 @@ func DefaultStyles() Styles {
840876 },
841877 }
842878
843− // PlainMarkdown style - muted colors on subtle background for thinking content.
879+ // QuietMarkdown style - muted colors on subtle background for thinking content.
844880 plainBg := new(bgBaseLighter.Hex())
845881 plainFg := new(fgMuted.Hex())
846− s.PlainMarkdown = ansi.StyleConfig{
882+ s.QuietMarkdown = ansi.StyleConfig{
847883 Document: ansi.StyleBlock{
848884 StylePrimitive: ansi.StylePrimitive{
849885 Color: plainFg,
@@ -1081,72 +1117,47 @@ func DefaultStyles() Styles {
10811117 }
10821118
10831119 // borders
1084− s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
1085−
1086− // text presets
1087− s.Base = lipgloss.NewStyle().Foreground(fgBase)
1088− s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
1089− s.HalfMuted = lipgloss.NewStyle().Foreground(fgHalfMuted)
1090− s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
1091−
1092− s.WindowTooSmall = s.Muted
1093−
1094− // tag presets
1095− s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
1096− s.TagError = s.TagBase.Background(redDark)
1097− s.TagInfo = s.TagBase.Background(blueLight)
1120+ s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
10981121
1099− // Compact header styles
11001122 s.Header.Charm = base.Foreground(secondary)
11011123 s.Header.Diagonals = base.Foreground(primary)
1102− s.Header.Percentage = s.Muted
1103− s.Header.Keystroke = s.Muted
1104− s.Header.KeystrokeTip = s.Subtle
1105− s.Header.WorkingDir = s.Muted
1106− s.Header.Separator = s.Subtle
1107−
1108− s.CompactDetails.Title = s.Base
1109− s.CompactDetails.View = s.Base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1110− s.CompactDetails.Version = s.Muted
1111−
1112− // panels
1113− s.PanelMuted = s.Muted.Background(bgBaseLighter)
1114− s.PanelBase = lipgloss.NewStyle().Background(bgBase)
1115−
1116− // code line number
1117− s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
1118−
1119− // Tool calls
1120− s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
1121− s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
1122− s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
1123− // Cancelled uses muted tone but same glyph as pending
1124− s.ToolCallCancelled = s.Muted.SetString(ToolPending)
1125− s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
1124+ s.Header.Percentage = muted
1125+ s.Header.Keystroke = muted
1126+ s.Header.KeystrokeTip = subtle
1127+ s.Header.WorkingDir = muted
1128+ s.Header.Separator = subtle
1129+ s.Header.Wrapper = lipgloss.NewStyle().Foreground(fgBase)
1130+ s.Header.LogoGradCanvas = lipgloss.NewStyle()
1131+ s.Header.LogoGradFromColor = secondary
1132+ s.Header.LogoGradToColor = primary
1133+
1134+ s.CompactDetails.Title = base
1135+ s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1136+ s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
11261137
11271138 // Tool rendering styles
11281139 s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
11291140 s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
11301141 s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
1131− s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
1142+ s.Tool.IconCancelled = muted.SetString(ToolPending)
11321143
11331144 s.Tool.NameNormal = base.Foreground(blue)
11341145 s.Tool.NameNested = base.Foreground(blue)
11351146
1136− s.Tool.ParamMain = s.Subtle
1137− s.Tool.ParamKey = s.Subtle
1147+ s.Tool.ParamMain = subtle
1148+ s.Tool.ParamKey = subtle
11381149
11391150 // Content rendering - prepared styles that accept width parameter
1140− s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
1141− s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
1142− s.Tool.ContentCodeLine = s.Base.Background(bgBase).PaddingLeft(2)
1143− s.Tool.ContentCodeTruncation = s.Muted.Background(bgBase).PaddingLeft(2)
1151+ s.Tool.ContentLine = muted.Background(bgBaseLighter)
1152+ s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
1153+ s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
1154+ s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
11441155 s.Tool.ContentCodeBg = bgBase
11451156 s.Tool.Body = base.PaddingLeft(2)
11461157
11471158 // Deprecated - kept for backward compatibility
1148− s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
1149− s.Tool.ContentText = s.Muted
1159+ s.Tool.ContentBg = muted.Background(bgBaseLighter)
1160+ s.Tool.ContentText = muted
11501161 s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
11511162
11521163 s.Tool.StateWaiting = base.Foreground(fgSubtle)
@@ -1156,7 +1167,7 @@ func DefaultStyles() Styles {
11561167 s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
11571168
11581169 // Diff and multi-edit styles
1159− s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
1170+ s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
11601171 s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
11611172 s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
11621173
@@ -1166,12 +1177,12 @@ func DefaultStyles() Styles {
11661177 s.Tool.JobIconSuccess = base.Foreground(green)
11671178 s.Tool.JobToolName = base.Foreground(blue)
11681179 s.Tool.JobAction = base.Foreground(blueDark)
1169− s.Tool.JobPID = s.Muted
1170− s.Tool.JobDescription = s.Subtle
1180+ s.Tool.JobPID = muted
1181+ s.Tool.JobDescription = subtle
11711182
11721183 // Agent task styles
11731184 s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
1174− s.Tool.AgentPrompt = s.Muted
1185+ s.Tool.AgentPrompt = muted
11751186
11761187 // Agentic fetch styles
11771188 s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
@@ -1181,6 +1192,9 @@ func DefaultStyles() Styles {
11811192 s.Tool.TodoCompletedIcon = base.Foreground(green)
11821193 s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
11831194 s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
1195+ s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(fgSubtle)
1196+ s.Tool.TodoItem = lipgloss.NewStyle().Foreground(fgBase)
1197+ s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(fgBase)
11841198
11851199 // MCP styles
11861200 s.Tool.MCPName = base.Foreground(blue)
@@ -1194,103 +1208,138 @@ func DefaultStyles() Styles {
11941208 s.Tool.MediaType = base
11951209 s.Tool.ResourceSize = base.Foreground(fgMuted)
11961210
1197− // Docker MCP styles
1198− s.Tool.DockerMCPActionAdd = base.Foreground(greenLight)
1199− s.Tool.DockerMCPActionDel = base.Foreground(red)
1211+ // Tool-call action verbs and result-list styling.
1212+ s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(greenLight)
1213+ s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(red)
1214+ s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
1215+ s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
1216+ s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
1217+ s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
12001218
12011219 // Buttons
1202− s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
1203− s.ButtonBlur = s.Base.Background(bgSubtle)
1204−
1205− // Borders
1206− s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
1220+ s.Button.Focused = lipgloss.NewStyle().Foreground(white).Background(secondary)
1221+ s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
12071222
12081223 // Editor
1209− s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1210− s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
1211− s.EditorPromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1212− s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1213− s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1214− s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
1215−
1216− s.RadioOn = s.HalfMuted.SetString(RadioOn)
1217− s.RadioOff = s.HalfMuted.SetString(RadioOff)
1218−
1219− // Logo colors
1220− s.LogoFieldColor = primary
1221− s.LogoTitleColorA = secondary
1222− s.LogoTitleColorB = primary
1223− s.LogoCharmColor = secondary
1224− s.LogoVersionColor = primary
1224+ s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1225+ s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
1226+ s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1227+ s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1228+ s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1229+ s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(charmtone.Squid)
1230+
1231+ s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
1232+ s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
1233+ s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
1234+
1235+ // Logo
1236+ s.Logo.FieldColor = primary
1237+ s.Logo.TitleColorA = secondary
1238+ s.Logo.TitleColorB = primary
1239+ s.Logo.CharmColor = secondary
1240+ s.Logo.VersionColor = primary
1241+ s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
1242+ s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
1243+ s.Logo.GradCanvas = lipgloss.NewStyle()
1244+ s.Logo.SmallGradFromColor = secondary
1245+ s.Logo.SmallGradToColor = primary
12251246
12261247 // Section
1227− s.Section.Title = s.Subtle
1228− s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
1248+ s.Section.Title = subtle
1249+ s.Section.Line = base.Foreground(charmtone.Charcoal)
12291250
12301251 // Initialize
1231− s.Initialize.Header = s.Base
1232− s.Initialize.Content = s.Muted
1233− s.Initialize.Accent = s.Base.Foreground(greenDark)
1234−
1235− // LSP and MCP status.
1236− s.ResourceGroupTitle = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1237− s.ResourceOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
1238− s.ResourceBusyIcon = s.ResourceOfflineIcon.Foreground(charmtone.Citron)
1239− s.ResourceErrorIcon = s.ResourceOfflineIcon.Foreground(charmtone.Coral)
1240− s.ResourceOnlineIcon = s.ResourceOfflineIcon.Foreground(charmtone.Guac)
1241− s.ResourceName = lipgloss.NewStyle().Foreground(charmtone.Squid)
1242− s.ResourceStatus = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1243− s.ResourceAdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1252+ s.Initialize.Header = base
1253+ s.Initialize.Content = muted
1254+ s.Initialize.Accent = base.Foreground(greenDark)
1255+
1256+ // ResourceGroup (LSP/MCP/skills sidebar lists).
1257+ s.Resource.Heading = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1258+ s.Resource.Name = lipgloss.NewStyle().Foreground(charmtone.Squid)
1259+ s.Resource.StatusText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1260+ s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
1261+ s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(charmtone.Citron)
1262+ s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(charmtone.Coral)
1263+ s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(charmtone.Guac)
1264+ s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
1265+ s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1266+ s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
1267+ s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
1268+ s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
1269+ s.Resource.DefaultTitleFg = fgMuted
1270+ s.Resource.DefaultDescFg = fgSubtle
12441271
12451272 // LSP
1246− s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
1247− s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
1248− s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
1249− s.LSP.InfoDiagnostic = s.Base.Foreground(info)
1273+ s.LSP.ErrorDiagnostic = base.Foreground(redDark)
1274+ s.LSP.WarningDiagnostic = base.Foreground(warning)
1275+ s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
1276+ s.LSP.InfoDiagnostic = base.Foreground(info)
12501277
12511278 // Files
1252− s.Files.Path = s.Muted
1253− s.Files.Additions = s.Base.Foreground(greenDark)
1254− s.Files.Deletions = s.Base.Foreground(redDark)
1279+ s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
1280+ s.Files.Additions = lipgloss.NewStyle().Foreground(greenDark)
1281+ s.Files.Deletions = lipgloss.NewStyle().Foreground(redDark)
1282+ s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
1283+ s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
1284+ s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
1285+
1286+ // Sidebar
1287+ s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
1288+ s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
1289+
1290+ // ModelInfo
1291+ s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
1292+ s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
1293+ s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
1294+ s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
1295+ s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
1296+ s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
1297+ s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
1298+ s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
1299+
1300+ // ResourceGroup
1301+ s.Resource.DefaultTitleFg = fgMuted
1302+ s.Resource.DefaultDescFg = fgSubtle
12551303
12561304 // Chat
12571305 messageFocussedBorder := lipgloss.Border{
12581306 Left: "▌",
12591307 }
12601308
1261− s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1262− s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1309+ s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1310+ s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
12631311 BorderForeground(primary).BorderStyle(normalBorder)
1264− s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1312+ s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
12651313 BorderForeground(primary).BorderStyle(messageFocussedBorder)
1266− s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
1267− s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1314+ s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
1315+ s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
12681316 BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1269− s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
1270− s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1317+ s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
1318+ s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
12711319 Background(red).Foreground(white)
1272− s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1273− s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1320+ s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1321+ s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
12741322
12751323 // Message item styles
1276− s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
1324+ s.Messages.ToolCallFocused = muted.PaddingLeft(1).
12771325 BorderStyle(messageFocussedBorder).
12781326 BorderLeft(true).
12791327 BorderForeground(greenDark)
1280− s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
1328+ s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
12811329 // No padding or border for compact tool calls within messages
1282− s.Chat.Message.ToolCallCompact = s.Muted
1283− s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
1284− s.Chat.Message.AssistantInfoIcon = s.Subtle
1285− s.Chat.Message.AssistantInfoModel = s.Muted
1286− s.Chat.Message.AssistantInfoProvider = s.Subtle
1287− s.Chat.Message.AssistantInfoDuration = s.Subtle
1330+ s.Messages.ToolCallCompact = muted
1331+ s.Messages.SectionHeader = base.PaddingLeft(2)
1332+ s.Messages.AssistantInfoIcon = subtle
1333+ s.Messages.AssistantInfoModel = muted
1334+ s.Messages.AssistantInfoProvider = subtle
1335+ s.Messages.AssistantInfoDuration = subtle
1336+ s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
12881337
12891338 // Thinking section styles
1290− s.Chat.Message.ThinkingBox = s.Subtle.Background(bgBaseLighter)
1291− s.Chat.Message.ThinkingTruncationHint = s.Muted
1292− s.Chat.Message.ThinkingFooterTitle = s.Muted
1293− s.Chat.Message.ThinkingFooterDuration = s.Subtle
1339+ s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
1340+ s.Messages.ThinkingTruncationHint = muted
1341+ s.Messages.ThinkingFooterTitle = muted
1342+ s.Messages.ThinkingFooterDuration = subtle
12941343
12951344 // Text selection.
12961345 s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
@@ -1300,6 +1349,25 @@ func DefaultStyles() Styles {
13001349 s.Dialog.TitleText = base.Foreground(primary)
13011350 s.Dialog.TitleError = base.Foreground(red)
13021351 s.Dialog.TitleAccent = base.Foreground(green).Bold(true)
1352+ s.Dialog.TitleLineBase = lipgloss.NewStyle()
1353+ s.Dialog.TitleGradFromColor = primary
1354+ s.Dialog.TitleGradToColor = secondary
1355+
1356+ // Dialog.ListItem (commands, reasoning, models)
1357+ s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
1358+ s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
1359+
1360+ // Dialog.Models
1361+ s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
1362+
1363+ // Dialog.Permissions
1364+ s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
1365+ s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
1366+ s.Dialog.Permissions.ParamsBg = bgSubtle
1367+
1368+ // Dialog.Quit
1369+ s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
1370+ s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
13031371 s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
13041372 s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
13051373 s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
@@ -1323,6 +1391,20 @@ func DefaultStyles() Styles {
13231391
13241392 s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
13251393
1394+ // API key input dialog
1395+ s.Dialog.APIKey.Spinner = base.Foreground(green)
1396+
1397+ // OAuth dialog
1398+ s.Dialog.OAuth.Spinner = base.Foreground(greenLight)
1399+ s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(white)
1400+ s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(white)
1401+ s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(greenLight)
1402+ s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(greenDark).Underline(true)
1403+ s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
1404+ s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
1405+ s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
1406+ s.Dialog.OAuth.UserCodeBg = bgBaseLighter
1407+
13261408 s.Dialog.Arguments.Content = base.Padding(1)
13271409 s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
13281410 s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
@@ -1332,20 +1414,22 @@ func DefaultStyles() Styles {
13321414
13331415 s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(red)
13341416 s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(red)
1335− s.Dialog.Sessions.DeletingMessage = s.Base.Padding(1)
1417+ s.Dialog.Sessions.DeletingMessage = base.Padding(1)
13361418 s.Dialog.Sessions.DeletingTitleGradientFromColor = red
1337− s.Dialog.Sessions.DeletingTitleGradientToColor = s.Primary
1419+ s.Dialog.Sessions.DeletingTitleGradientToColor = primary
13381420 s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
13391421 s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(red).Foreground(charmtone.Butter)
13401422
13411423 s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(charmtone.Zest)
13421424 s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(charmtone.Zest)
1343− s.Dialog.Sessions.RenamingingMessage = s.Base.Padding(1)
1425+ s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
13441426 s.Dialog.Sessions.RenamingTitleGradientFromColor = charmtone.Zest
13451427 s.Dialog.Sessions.RenamingTitleGradientToColor = charmtone.Bok
13461428 s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
13471429 s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
13481430 s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(charmtone.Squid)
1431+ s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
1432+ s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
13491433
13501434 s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
13511435 s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
@@ -1375,11 +1459,19 @@ func DefaultStyles() Styles {
13751459 s.Pills.Base = base.Padding(0, 1)
13761460 s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
13771461 s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
1378− s.Pills.QueueItemPrefix = s.Muted.SetString(" •")
1379− s.Pills.HelpKey = s.Muted
1380− s.Pills.HelpText = s.Subtle
1462+ s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString(" •")
1463+ s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
1464+ s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
1465+ s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
1466+ s.Pills.QueueGradFromColor = redDark
1467+ s.Pills.QueueGradToColor = secondary
1468+ s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
1469+ s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
1470+ s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
1471+ s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(greenDark)
1472+ s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
1473+ s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
13811474 s.Pills.Area = base
1382− s.Pills.TodoSpinner = base.Foreground(greenDark)
13831475
13841476 return s
13851477}