on main help sign in
fix(styles): use semantic names in styles + drop deadcode
81daa26Christian Rocha
internal/app/app.go+3−3
@@ -242,14 +242,14 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt,
242 if f, ok := output.(*os.File); ok && stdinTTY && stdoutTTY {
242 if f, ok := output.(*os.File); ok && stdinTTY && stdoutTTY {
243 hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, f)
243 hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, f)
244 }
244 }
245− defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
245+ defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
246
246
247 spinner = format.NewSpinner(ctx, cancel, anim.Settings{
247 spinner = format.NewSpinner(ctx, cancel, anim.Settings{
248 Size: 10,
248 Size: 10,
249 Label: "Generating",
249 Label: "Generating",
250 LabelColor: defaultFG,
250 LabelColor: defaultFG,
251− GradColorA: t.Primary,
251+ GradColorA: t.WorkingGradFromColor,
252− GradColorB: t.Secondary,
252+ GradColorB: t.WorkingGradToColor,
253 CycleColors: true,
253 CycleColors: true,
254 })
254 })
255 spinner.Start()
255 spinner.Start()
internal/cmd/run.go+3−3
@@ -195,14 +195,14 @@ func runNonInteractive(
195 if stdinTTY && stdoutTTY {
195 if stdinTTY && stdoutTTY {
196 hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
196 hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
197 }
197 }
198− defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
198+ defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
199
199
200 spinner = format.NewSpinner(ctx, cancel, anim.Settings{
200 spinner = format.NewSpinner(ctx, cancel, anim.Settings{
201 Size: 10,
201 Size: 10,
202 Label: "Generating",
202 Label: "Generating",
203 LabelColor: defaultFG,
203 LabelColor: defaultFG,
204− GradColorA: t.Primary,
204+ GradColorA: t.WorkingGradFromColor,
205− GradColorB: t.Secondary,
205+ GradColorB: t.WorkingGradToColor,
206 CycleColors: true,
206 CycleColors: true,
207 })
207 })
208 spinner.Start()
208 spinner.Start()
internal/ui/chat/assistant.go+14−14
@@ -48,9 +48,9 @@ func NewAssistantMessageItem(sty *styles.Styles, message *message.Message) Messa
48 a.anim = anim.New(anim.Settings{
48 a.anim = anim.New(anim.Settings{
49 ID: a.ID(),
49 ID: a.ID(),
50 Size: 15,
50 Size: 15,
51− GradColorA: sty.Primary,
51+ GradColorA: sty.WorkingGradFromColor,
52− GradColorB: sty.Secondary,
52+ GradColorB: sty.WorkingGradToColor,
53− LabelColor: sty.FgBase,
53+ LabelColor: sty.WorkingLabelColor,
54 CycleColors: true,
54 CycleColors: true,
55 })
55 })
56 return a
56 return a
@@ -112,8 +112,8 @@ func (a *AssistantMessageItem) Render(width int) string {
112 // it's wrapping logic.
112 // it's wrapping logic.
113 // We already know that the content is wrapped to the correct width in
113 // We already know that the content is wrapped to the correct width in
114 // RawRender, so we can just apply the styles directly to each line.
114 // RawRender, so we can just apply the styles directly to each line.
115− focused := a.sty.Chat.Message.AssistantFocused.Render()
115+ focused := a.sty.Messages.AssistantFocused.Render()
116− blurred := a.sty.Chat.Message.AssistantBlurred.Render()
116+ blurred := a.sty.Messages.AssistantBlurred.Render()
117 rendered := a.RawRender(width)
117 rendered := a.RawRender(width)
118 lines := strings.Split(rendered, "\n")
118 lines := strings.Split(rendered, "\n")
119 for i, line := range lines {
119 for i, line := range lines {
@@ -149,7 +149,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
149 if a.message.IsFinished() {
149 if a.message.IsFinished() {
150 switch a.message.FinishReason() {
150 switch a.message.FinishReason() {
151 case message.FinishReasonCanceled:
151 case message.FinishReasonCanceled:
152− messageParts = append(messageParts, a.sty.Base.Italic(true).Render("Canceled"))
152+ messageParts = append(messageParts, a.sty.Messages.AssistantCanceled.Render("Canceled"))
153 case message.FinishReasonError:
153 case message.FinishReasonError:
154 messageParts = append(messageParts, a.renderError(width))
154 messageParts = append(messageParts, a.renderError(width))
155 }
155 }
@@ -160,7 +160,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
160
160
161// renderThinking renders the thinking/reasoning content with footer.
161// renderThinking renders the thinking/reasoning content with footer.
162func (a *AssistantMessageItem) renderThinking(thinking string, width int) string {
162func (a *AssistantMessageItem) renderThinking(thinking string, width int) string {
163− renderer := common.PlainMarkdownRenderer(a.sty, width)
163+ renderer := common.QuietMarkdownRenderer(a.sty, width)
164 rendered, err := renderer.Render(thinking)
164 rendered, err := renderer.Render(thinking)
165 if err != nil {
165 if err != nil {
166 rendered = thinking
166 rendered = thinking
@@ -173,13 +173,13 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
173 isTruncated := totalLines > maxCollapsedThinkingHeight
173 isTruncated := totalLines > maxCollapsedThinkingHeight
174 if !a.thinkingExpanded && isTruncated {
174 if !a.thinkingExpanded && isTruncated {
175 lines = lines[totalLines-maxCollapsedThinkingHeight:]
175 lines = lines[totalLines-maxCollapsedThinkingHeight:]
176− hint := a.sty.Chat.Message.ThinkingTruncationHint.Render(
176+ hint := a.sty.Messages.ThinkingTruncationHint.Render(
177 fmt.Sprintf(assistantMessageTruncateFormat, totalLines-maxCollapsedThinkingHeight),
177 fmt.Sprintf(assistantMessageTruncateFormat, totalLines-maxCollapsedThinkingHeight),
178 )
178 )
179 lines = append([]string{hint, ""}, lines...)
179 lines = append([]string{hint, ""}, lines...)
180 }
180 }
181
181
182− thinkingStyle := a.sty.Chat.Message.ThinkingBox.Width(width)
182+ thinkingStyle := a.sty.Messages.ThinkingBox.Width(width)
183 result := thinkingStyle.Render(strings.Join(lines, "\n"))
183 result := thinkingStyle.Render(strings.Join(lines, "\n"))
184 a.thinkingBoxHeight = lipgloss.Height(result)
184 a.thinkingBoxHeight = lipgloss.Height(result)
185
185
@@ -188,8 +188,8 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
188 if !a.message.IsThinking() || len(a.message.ToolCalls()) > 0 {
188 if !a.message.IsThinking() || len(a.message.ToolCalls()) > 0 {
189 duration := a.message.ThinkingDuration()
189 duration := a.message.ThinkingDuration()
190 if duration.String() != "0s" {
190 if duration.String() != "0s" {
191− footer = a.sty.Chat.Message.ThinkingFooterTitle.Render("Thought for ") +
191+ footer = a.sty.Messages.ThinkingFooterTitle.Render("Thought for ") +
192− a.sty.Chat.Message.ThinkingFooterDuration.Render(duration.String())
192+ a.sty.Messages.ThinkingFooterDuration.Render(duration.String())
193 }
193 }
194 }
194 }
195
195
@@ -222,10 +222,10 @@ func (a *AssistantMessageItem) renderSpinning() string {
222// renderError renders an error message.
222// renderError renders an error message.
223func (a *AssistantMessageItem) renderError(width int) string {
223func (a *AssistantMessageItem) renderError(width int) string {
224 finishPart := a.message.FinishPart()
224 finishPart := a.message.FinishPart()
225− errTag := a.sty.Chat.Message.ErrorTag.Render("ERROR")
225+ errTag := a.sty.Messages.ErrorTag.Render("ERROR")
226 truncated := ansi.Truncate(finishPart.Message, width-2-lipgloss.Width(errTag), "...")
226 truncated := ansi.Truncate(finishPart.Message, width-2-lipgloss.Width(errTag), "...")
227− title := fmt.Sprintf("%s %s", errTag, a.sty.Chat.Message.ErrorTitle.Render(truncated))
227+ title := fmt.Sprintf("%s %s", errTag, a.sty.Messages.ErrorTitle.Render(truncated))
228− details := a.sty.Chat.Message.ErrorDetails.Width(width - 2).Render(finishPart.Details)
228+ details := a.sty.Messages.ErrorDetails.Width(width - 2).Render(finishPart.Details)
229 return fmt.Sprintf("%s\n\n%s", title, details)
229 return fmt.Sprintf("%s\n\n%s", title, details)
230}
230}
231
231
internal/ui/chat/docker_mcp.go+5−5
@@ -173,7 +173,7 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
173 }
173 }
174
174
175 if len(result.Servers) == 0 {
175 if len(result.Servers) == 0 {
176− return sty.Subtle.Render("No MCP servers found.")
176+ return sty.Tool.ResultEmpty.Render("No MCP servers found.")
177 }
177 }
178
178
179 bodyWidth := min(120, width) - toolBodyLeftPaddingTotal
179 bodyWidth := min(120, width) - toolBodyLeftPaddingTotal
@@ -181,10 +181,10 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
181 moreServers := ""
181 moreServers := ""
182 for i, server := range result.Servers {
182 for i, server := range result.Servers {
183 if i > 9 {
183 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))
185 break
185 break
186 }
186 }
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)})
188 }
188 }
189 serverTable := table.New().
189 serverTable := table.New().
190 Wrap(false).
190 Wrap(false).
@@ -236,10 +236,10 @@ func (d *DockerMCPToolRenderContext) formatToolName(sty *styles.Styles, tool str
236 action = "Find"
236 action = "Find"
237 case "mcp-add":
237 case "mcp-add":
238 action = "Add"
238 action = "Add"
239− actionStyle = sty.Tool.DockerMCPActionAdd
239+ actionStyle = sty.Tool.ActionCreate
240 case "mcp-remove":
240 case "mcp-remove":
241 action = "Remove"
241 action = "Remove"
242− actionStyle = sty.Tool.DockerMCPActionDel
242+ actionStyle = sty.Tool.ActionDestroy
243 case "code-mode":
243 case "code-mode":
244 action = "Code Mode"
244 action = "Code Mode"
245 default:
245 default:
internal/ui/chat/messages.go+5−5
@@ -221,7 +221,7 @@ func (a *AssistantInfoItem) RawRender(width int) string {
221
221
222// Render implements MessageItem.
222// Render implements MessageItem.
223func (a *AssistantInfoItem) Render(width int) string {
223func (a *AssistantInfoItem) Render(width int) string {
224− prefix := a.sty.Chat.Message.SectionHeader.Render()
224+ prefix := a.sty.Messages.SectionHeader.Render()
225 lines := strings.Split(a.RawRender(width), "\n")
225 lines := strings.Split(a.RawRender(width), "\n")
226 for i, line := range lines {
226 for i, line := range lines {
227 lines[i] = prefix + line
227 lines[i] = prefix + line
@@ -236,18 +236,18 @@ func (a *AssistantInfoItem) renderContent(width int) string {
236 }
236 }
237 finishTime := time.Unix(finishData.Time, 0)
237 finishTime := time.Unix(finishData.Time, 0)
238 duration := finishTime.Sub(a.lastUserMessageTime)
238 duration := finishTime.Sub(a.lastUserMessageTime)
239− infoMsg := a.sty.Chat.Message.AssistantInfoDuration.Render(duration.String())
239+ infoMsg := a.sty.Messages.AssistantInfoDuration.Render(duration.String())
240− icon := a.sty.Chat.Message.AssistantInfoIcon.Render(styles.ModelIcon)
240+ icon := a.sty.Messages.AssistantInfoIcon.Render(styles.ModelIcon)
241 model := a.cfg.GetModel(a.message.Provider, a.message.Model)
241 model := a.cfg.GetModel(a.message.Provider, a.message.Model)
242 if model == nil {
242 if model == nil {
243 model = &catwalk.Model{Name: "Unknown Model"}
243 model = &catwalk.Model{Name: "Unknown Model"}
244 }
244 }
245− modelFormatted := a.sty.Chat.Message.AssistantInfoModel.Render(model.Name)
245+ modelFormatted := a.sty.Messages.AssistantInfoModel.Render(model.Name)
246 providerName := a.message.Provider
246 providerName := a.message.Provider
247 if providerConfig, ok := a.cfg.Providers.Get(a.message.Provider); ok {
247 if providerConfig, ok := a.cfg.Providers.Get(a.message.Provider); ok {
248 providerName = providerConfig.Name
248 providerName = providerConfig.Name
249 }
249 }
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))
251 assistant := fmt.Sprintf("%s %s %s %s", icon, modelFormatted, provider, infoMsg)
251 assistant := fmt.Sprintf("%s %s %s %s", icon, modelFormatted, provider, infoMsg)
252 return common.Section(a.sty, assistant, width)
252 return common.Section(a.sty, assistant, width)
253}
253}
internal/ui/chat/todos.go+6−6
@@ -91,16 +91,16 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
91
91
92 ratio := sty.Tool.TodoRatio.Render(fmt.Sprintf("%d/%d", meta.Completed, meta.Total))
92 ratio := sty.Tool.TodoRatio.Render(fmt.Sprintf("%d/%d", meta.Completed, meta.Total))
93 if hasCompleted && hasStarted {
93 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)))
95 headerText = fmt.Sprintf("%s%s", ratio, text)
95 headerText = fmt.Sprintf("%s%s", ratio, text)
96 } else if hasCompleted {
96 } 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)))
98 if allCompleted {
98 if allCompleted {
99− text = sty.Subtle.Render(" · completed all")
99+ text = sty.Tool.TodoStatusNote.Render(" · completed all")
100 }
100 }
101 headerText = fmt.Sprintf("%s%s", ratio, text)
101 headerText = fmt.Sprintf("%s%s", ratio, text)
102 } else if hasStarted {
102 } 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"))
104 } else {
104 } else {
105 headerText = ratio
105 headerText = ratio
106 }
106 }
@@ -111,7 +111,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
111 body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
111 body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
112 } else if meta.JustStarted != "" {
112 } else if meta.JustStarted != "" {
113 body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
113 body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
114− sty.Base.Render(meta.JustStarted)
114+ sty.Tool.TodoJustStarted.Render(meta.JustStarted)
115 }
115 }
116 }
116 }
117 }
117 }
@@ -148,7 +148,7 @@ func FormatTodosList(sty *styles.Styles, todos []session.Todo, inProgressIcon st
148 var lines []string
148 var lines []string
149 for _, todo := range sorted {
149 for _, todo := range sorted {
150 var prefix string
150 var prefix string
151− textStyle := sty.Base
151+ textStyle := sty.Tool.TodoItem
152
152
153 switch todo.Status {
153 switch todo.Status {
154 case session.TodoStatusCompleted:
154 case session.TodoStatusCompleted:
internal/ui/chat/tools.go+7−7
@@ -189,9 +189,9 @@ func newBaseToolMessageItem(
189 t.anim = anim.New(anim.Settings{
189 t.anim = anim.New(anim.Settings{
190 ID: toolCall.ID,
190 ID: toolCall.ID,
191 Size: 15,
191 Size: 15,
192− GradColorA: sty.Primary,
192+ GradColorA: sty.WorkingGradFromColor,
193− GradColorB: sty.Secondary,
193+ GradColorB: sty.WorkingGradToColor,
194− LabelColor: sty.FgBase,
194+ LabelColor: sty.WorkingLabelColor,
195 CycleColors: true,
195 CycleColors: true,
196 })
196 })
197
197
@@ -325,11 +325,11 @@ func (t *baseToolMessageItem) RawRender(width int) string {
325func (t *baseToolMessageItem) Render(width int) string {
325func (t *baseToolMessageItem) Render(width int) string {
326 var prefix string
326 var prefix string
327 if t.isCompact {
327 if t.isCompact {
328− prefix = t.sty.Chat.Message.ToolCallCompact.Render()
328+ prefix = t.sty.Messages.ToolCallCompact.Render()
329 } else if t.focused {
329 } else if t.focused {
330− prefix = t.sty.Chat.Message.ToolCallFocused.Render()
330+ prefix = t.sty.Messages.ToolCallFocused.Render()
331 } else {
331 } else {
332− prefix = t.sty.Chat.Message.ToolCallBlurred.Render()
332+ prefix = t.sty.Messages.ToolCallBlurred.Render()
333 }
333 }
334 lines := strings.Split(t.RawRender(width), "\n")
334 lines := strings.Split(t.RawRender(width), "\n")
335 for i, ln := range lines {
335 for i, ln := range lines {
@@ -799,7 +799,7 @@ func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, ex
799 width = maxTextWidth
799 width = maxTextWidth
800 }
800 }
801
801
802− renderer := common.PlainMarkdownRenderer(sty, width)
802+ renderer := common.QuietMarkdownRenderer(sty, width)
803 rendered, err := renderer.Render(content)
803 rendered, err := renderer.Render(content)
804 if err != nil {
804 if err != nil {
805 return toolOutputPlainContent(sty, content, width, expanded)
805 return toolOutputPlainContent(sty, content, width, expanded)
internal/ui/chat/user.go+2−2
@@ -72,9 +72,9 @@ func (m *UserMessageItem) RawRender(width int) string {
72func (m *UserMessageItem) Render(width int) string {
72func (m *UserMessageItem) Render(width int) string {
73 var prefix string
73 var prefix string
74 if m.focused {
74 if m.focused {
75− prefix = m.sty.Chat.Message.UserFocused.Render()
75+ prefix = m.sty.Messages.UserFocused.Render()
76 } else {
76 } else {
77− prefix = m.sty.Chat.Message.UserBlurred.Render()
77+ prefix = m.sty.Messages.UserBlurred.Render()
78 }
78 }
79 lines := strings.Split(m.RawRender(width), "\n")
79 lines := strings.Split(m.RawRender(width), "\n")
80 for i, line := range lines {
80 for i, line := range lines {
internal/ui/common/button.go+2−2
@@ -22,9 +22,9 @@ type ButtonOpts struct {
22// Button creates a button with an underlined character and selection state
22// Button creates a button with an underlined character and selection state
23func Button(t *styles.Styles, opts ButtonOpts) string {
23func Button(t *styles.Styles, opts ButtonOpts) string {
24 // Select style based on selection state
24 // Select style based on selection state
25− style := t.ButtonBlur
25+ style := t.Button.Blurred
26 if opts.Selected {
26 if opts.Selected {
27− style = t.ButtonFocus
27+ style = t.Button.Focused
28 }
28 }
29
29
30 text := opts.Text
30 text := opts.Text
internal/ui/common/elements.go+14−14
@@ -18,7 +18,7 @@ import (
18// muted styling.
18// muted styling.
19func PrettyPath(t *styles.Styles, path string, width int) string {
19func PrettyPath(t *styles.Styles, path string, width int) string {
20 formatted := home.Short(path)
20 formatted := home.Short(path)
21− return t.Muted.Width(width).Render(formatted)
21+ return t.Sidebar.WorkingDir.Width(width).Render(formatted)
22}
22}
23
23
24// FormatReasoningEffort formats a reasoning effort level for display.
24// FormatReasoningEffort formats a reasoning effort level for display.
@@ -39,13 +39,13 @@ type ModelContextInfo struct {
39// ModelInfo renders model information including name, provider, reasoning
39// ModelInfo renders model information including name, provider, reasoning
40// settings, and optional context usage/cost.
40// settings, and optional context usage/cost.
41func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string, context *ModelContextInfo, width int) string {
41func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string, context *ModelContextInfo, width int) string {
42− modelIcon := t.Subtle.Render(styles.ModelIcon)
42+ modelIcon := t.ModelInfo.Icon.Render(styles.ModelIcon)
43− modelName = t.Base.Render(modelName)
43+ modelName = t.ModelInfo.Name.Render(modelName)
44
44
45 // Build first line with model name and optionally provider on the same line
45 // Build first line with model name and optionally provider on the same line
46 var firstLine string
46 var firstLine string
47 if providerName != "" {
47 if providerName != "" {
48− providerInfo := t.Muted.Render(fmt.Sprintf("via %s", providerName))
48+ providerInfo := t.ModelInfo.Provider.Render(fmt.Sprintf("via %s", providerName))
49 modelWithProvider := fmt.Sprintf("%s %s %s", modelIcon, modelName, providerInfo)
49 modelWithProvider := fmt.Sprintf("%s %s %s", modelIcon, modelName, providerInfo)
50
50
51 // Check if it fits on one line
51 // Check if it fits on one line
@@ -64,11 +64,11 @@ func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string,
64 // If provider didn't fit on first line, add it as second line
64 // If provider didn't fit on first line, add it as second line
65 if providerName != "" && !strings.Contains(firstLine, "via") {
65 if providerName != "" && !strings.Contains(firstLine, "via") {
66 providerInfo := fmt.Sprintf("via %s", providerName)
66 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))
68 }
68 }
69
69
70 if reasoningInfo != "" {
70 if reasoningInfo != "" {
71− parts = append(parts, t.Subtle.PaddingLeft(2).Render(reasoningInfo))
71+ parts = append(parts, t.ModelInfo.Reasoning.Render(reasoningInfo))
72 }
72 }
73
73
74 if context != nil {
74 if context != nil {
@@ -103,10 +103,10 @@ func formatTokensAndCost(t *styles.Styles, tokens, contextWindow int64, cost flo
103
103
104 percentage := (float64(tokens) / float64(contextWindow)) * 100
104 percentage := (float64(tokens) / float64(contextWindow)) * 100
105
105
106− formattedCost := t.Muted.Render(fmt.Sprintf("$%.2f", cost))
106+ formattedCost := t.ModelInfo.Cost.Render(fmt.Sprintf("$%.2f", cost))
107
107
108− formattedTokens = t.Subtle.Render(fmt.Sprintf("(%s)", formattedTokens))
108+ formattedTokens = t.ModelInfo.TokenCount.Render(fmt.Sprintf("(%s)", formattedTokens))
109− formattedPercentage := t.Muted.Render(fmt.Sprintf("%d%%", int(percentage)))
109+ formattedPercentage := t.ModelInfo.TokenPercentage.Render(fmt.Sprintf("%d%%", int(percentage)))
110 formattedTokens = fmt.Sprintf("%s %s", formattedPercentage, formattedTokens)
110 formattedTokens = fmt.Sprintf("%s %s", formattedPercentage, formattedTokens)
111 if percentage > 80 {
111 if percentage > 80 {
112 formattedTokens = fmt.Sprintf("%s %s", styles.LSPWarningIcon, formattedTokens)
112 formattedTokens = fmt.Sprintf("%s %s", styles.LSPWarningIcon, formattedTokens)
@@ -133,10 +133,10 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
133 title := opts.Title
133 title := opts.Title
134 description := opts.Description
134 description := opts.Description
135
135
136− titleColor := cmp.Or(opts.TitleColor, t.Muted.GetForeground())
136+ titleColor := cmp.Or(opts.TitleColor, t.Resource.DefaultTitleFg)
137− descriptionColor := cmp.Or(opts.DescriptionColor, t.Subtle.GetForeground())
137+ descriptionColor := cmp.Or(opts.DescriptionColor, t.Resource.DefaultDescFg)
138
138
139− title = t.Base.Foreground(titleColor).Render(title)
139+ title = t.Resource.RowTitleBase.Foreground(titleColor).Render(title)
140
140
141 if description != "" {
141 if description != "" {
142 extraContentWidth := lipgloss.Width(opts.ExtraContent)
142 extraContentWidth := lipgloss.Width(opts.ExtraContent)
@@ -144,7 +144,7 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
144 extraContentWidth += 1
144 extraContentWidth += 1
145 }
145 }
146 description = ansi.Truncate(description, width-lipgloss.Width(icon)-lipgloss.Width(title)-2-extraContentWidth, "…")
146 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)
148 }
148 }
149
149
150 var content []string
150 var content []string
@@ -193,7 +193,7 @@ func DialogTitle(t *styles.Styles, title string, width int, fromColor, toColor c
193 remainingWidth := width - length
193 remainingWidth := width - length
194 if remainingWidth > 0 {
194 if remainingWidth > 0 {
195 lines := strings.Repeat(char, remainingWidth)
195 lines := strings.Repeat(char, remainingWidth)
196− lines = styles.ApplyForegroundGrad(t.Base, lines, fromColor, toColor)
196+ lines = styles.ApplyForegroundGrad(t.Dialog.TitleLineBase, lines, fromColor, toColor)
197 title = title + " " + lines
197 title = title + " " + lines
198 }
198 }
199 return title
199 return title
internal/ui/common/markdown.go+3−3
@@ -29,11 +29,11 @@ func MarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
29 return r
29 return r
30}
30}
31
31
32−// PlainMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
32+// QuietMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
33// (plain text with structure) and the given width.
33// (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 {
35 r, _ := glamour.NewTermRenderer(
35 r, _ := glamour.NewTermRenderer(
36− glamour.WithStyles(sty.PlainMarkdown),
36+ glamour.WithStyles(sty.QuietMarkdown),
37 glamour.WithWordWrap(width),
37 glamour.WithWordWrap(width),
38 glamour.WithChromaFormatter(formatterName),
38 glamour.WithChromaFormatter(formatterName),
39 )
39 )
internal/ui/dialog/api_key_input.go+2−2
@@ -83,7 +83,7 @@ func NewAPIKeyInput(
83
83
84 m.spinner = spinner.New(
84 m.spinner = spinner.New(
85 spinner.WithSpinner(spinner.Dot),
85 spinner.WithSpinner(spinner.Dot),
86− spinner.WithStyle(t.Base.Foreground(t.Green)),
86+ spinner.WithStyle(t.Dialog.APIKey.Spinner),
87 )
87 )
88
88
89 m.help = help.New()
89 m.help = help.New()
@@ -202,7 +202,7 @@ func (m *APIKeyInput) headerView() string {
202 return textStyle.Render(m.dialogTitle())
202 return textStyle.Render(m.dialogTitle())
203 }
203 }
204 headerOffset := titleStyle.GetHorizontalFrameSize() + dialogStyle.GetHorizontalFrameSize()
204 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)
206}
206}
207
207
208func (m *APIKeyInput) dialogTitle() string {
208func (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 {
314
314
315 titleText := cmp.Or(a.title, "Arguments")
315 titleText := cmp.Or(a.title, "Arguments")
316
316
317− header := common.DialogTitle(s, titleText, width, s.Primary, s.Secondary)
317+ header := common.DialogTitle(s, titleText, width, s.Dialog.TitleGradFromColor, s.Dialog.TitleGradToColor)
318
318
319 // Add description if available.
319 // Add description if available.
320 var description string
320 var description string
internal/ui/dialog/commands.go+2−2
@@ -252,9 +252,9 @@ func commandsRadioView(sty *styles.Styles, selected CommandType, hasUserCmds boo
252
252
253 selectedFn := func(t CommandType) string {
253 selectedFn := func(t CommandType) string {
254 if t == selected {
254 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())
256 }
256 }
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())
258 }
258 }
259
259
260 parts := []string{
260 parts := []string{
internal/ui/dialog/commands_item.go+2−2
@@ -69,8 +69,8 @@ func (c *CommandItem) Render(width int) string {
69 styles := ListItemStyles{
69 styles := ListItemStyles{
70 ItemBlurred: c.t.Dialog.NormalItem,
70 ItemBlurred: c.t.Dialog.NormalItem,
71 ItemFocused: c.t.Dialog.SelectedItem,
71 ItemFocused: c.t.Dialog.SelectedItem,
72− InfoTextBlurred: c.t.Base,
72+ InfoTextBlurred: c.t.Dialog.ListItem.InfoBlurred,
73− InfoTextFocused: c.t.Base,
73+ InfoTextFocused: c.t.Dialog.ListItem.InfoFocused,
74 }
74 }
75 return renderItem(styles, c.title, c.shortcut, c.focused, width, c.cache, &c.m)
75 return renderItem(styles, c.title, c.shortcut, c.focused, width, c.cache, &c.m)
76}
76}
internal/ui/dialog/common.go+6−4
@@ -65,9 +65,11 @@ type RenderContext struct {
65 TitleStyle lipgloss.Style
65 TitleStyle lipgloss.Style
66 // ViewStyle is the style of the dialog title by default it uses Styles.Dialog.View
66 // ViewStyle is the style of the dialog title by default it uses Styles.Dialog.View
67 ViewStyle lipgloss.Style
67 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
69 TitleGradientFromColor color.Color
70 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
71 TitleGradientToColor color.Color
73 TitleGradientToColor color.Color
72 // Width is the total width of the dialog including any margins, borders,
74 // Width is the total width of the dialog including any margins, borders,
73 // and paddings.
75 // and paddings.
@@ -98,8 +100,8 @@ func NewRenderContext(t *styles.Styles, width int) *RenderContext {
98 Styles: t,
100 Styles: t,
99 TitleStyle: t.Dialog.Title,
101 TitleStyle: t.Dialog.Title,
100 ViewStyle: t.Dialog.View,
102 ViewStyle: t.Dialog.View,
101− TitleGradientFromColor: t.Primary,
103+ TitleGradientFromColor: t.Dialog.TitleGradFromColor,
102− TitleGradientToColor: t.Secondary,
104+ TitleGradientToColor: t.Dialog.TitleGradToColor,
103 Width: width,
105 Width: width,
104 Parts: []string{},
106 Parts: []string{},
105 }
107 }
internal/ui/dialog/models.go+5−5
@@ -236,13 +236,13 @@ func (m *Models) Cursor() *tea.Cursor {
236// modelTypeRadioView returns the radio view for model type selection.
236// modelTypeRadioView returns the radio view for model type selection.
237func (m *Models) modelTypeRadioView() string {
237func (m *Models) modelTypeRadioView() string {
238 t := m.com.Styles
238 t := m.com.Styles
239− textStyle := t.HalfMuted
239+ textStyle := t.Radio.Label
240− largeRadioStyle := t.RadioOff
240+ largeRadioStyle := t.Radio.Off
241− smallRadioStyle := t.RadioOff
241+ smallRadioStyle := t.Radio.Off
242 if m.modelType == ModelTypeLarge {
242 if m.modelType == ModelTypeLarge {
243− largeRadioStyle = t.RadioOn
243+ largeRadioStyle = t.Radio.On
244 } else {
244 } else {
245− smallRadioStyle = t.RadioOn
245+ smallRadioStyle = t.Radio.On
246 }
246 }
247
247
248 largeRadio := largeRadioStyle.Padding(0, 1).Render()
248 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 {
38 var configured string
38 var configured string
39 if m.configured {
39 if m.configured {
40 configuredIcon := m.t.ToolCallSuccess.Render()
40 configuredIcon := m.t.ToolCallSuccess.Render()
41− configuredText := m.t.Subtle.Render("Configured")
41+ configuredText := m.t.Dialog.Models.ConfiguredText.Render("Configured")
42 configured = configuredIcon + " " + configuredText
42 configured = configuredIcon + " " + configuredText
43 }
43 }
44
44
@@ -109,8 +109,8 @@ func (m *ModelItem) Render(width int) string {
109 styles := ListItemStyles{
109 styles := ListItemStyles{
110 ItemBlurred: m.t.Dialog.NormalItem,
110 ItemBlurred: m.t.Dialog.NormalItem,
111 ItemFocused: m.t.Dialog.SelectedItem,
111 ItemFocused: m.t.Dialog.SelectedItem,
112− InfoTextBlurred: m.t.Base,
112+ InfoTextBlurred: m.t.Dialog.ListItem.InfoBlurred,
113− InfoTextFocused: m.t.Base,
113+ InfoTextFocused: m.t.Dialog.ListItem.InfoFocused,
114 }
114 }
115 return renderItem(styles, m.model.Name, providerInfo, m.focused, width, m.cache, &m.m)
115 return renderItem(styles, m.model.Name, providerInfo, m.focused, width, m.cache, &m.m)
116}
116}
internal/ui/dialog/oauth.go+10−13
@@ -94,7 +94,7 @@ func newOAuth(
94
94
95 m.spinner = spinner.New(
95 m.spinner = spinner.New(
96 spinner.WithSpinner(spinner.Dot),
96 spinner.WithSpinner(spinner.Dot),
97− spinner.WithStyle(t.Base.Foreground(t.GreenLight)),
97+ spinner.WithStyle(t.Dialog.OAuth.Spinner),
98 )
98 )
99
99
100 m.help = help.New()
100 m.help = help.New()
@@ -227,18 +227,18 @@ func (m *OAuth) headerContent() string {
227 if m.isOnboarding {
227 if m.isOnboarding {
228 return textStyle.Render(dialogTitle)
228 return textStyle.Render(dialogTitle)
229 }
229 }
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)
231}
231}
232
232
233func (m *OAuth) innerDialogContent() string {
233func (m *OAuth) innerDialogContent() string {
234 var (
234 var (
235 t = m.com.Styles
235 t = m.com.Styles
236− whiteStyle = lipgloss.NewStyle().Foreground(t.White)
236+ whiteStyle = t.Dialog.OAuth.Instructions
237− primaryStyle = lipgloss.NewStyle().Foreground(t.Primary)
237+ primaryStyle = t.Dialog.OAuth.Enter
238− greenStyle = lipgloss.NewStyle().Foreground(t.GreenLight)
238+ greenStyle = t.Dialog.OAuth.Success
239− linkStyle = lipgloss.NewStyle().Foreground(t.GreenDark).Underline(true)
239+ linkStyle = t.Dialog.OAuth.Link
240− errorStyle = lipgloss.NewStyle().Foreground(t.Error)
240+ errorStyle = t.Dialog.OAuth.ErrorText
241− mutedStyle = lipgloss.NewStyle().Foreground(t.FgMuted)
241+ mutedStyle = t.Dialog.OAuth.StatusText
242 )
242 )
243
243
244 switch m.State {
244 switch m.State {
@@ -266,13 +266,10 @@ func (m *OAuth) innerDialogContent() string {
266 Width(m.width-2).
266 Width(m.width-2).
267 Height(7).
267 Height(7).
268 Align(lipgloss.Center, lipgloss.Center).
268 Align(lipgloss.Center, lipgloss.Center).
269− Background(t.BgBaseLighter).
269+ Background(t.Dialog.OAuth.UserCodeBg).
270 Margin(0, 1).
270 Margin(0, 1).
271 Render(
271 Render(
272− lipgloss.NewStyle().
273− Bold(true).
274− Foreground(t.White).
275− Render(m.userCode),
272+ t.Dialog.OAuth.UserCode.Render(m.userCode),
276 )
273 )
277
274
278 link := linkStyle.Hyperlink(m.verificationURL, "id=oauth-verify").Render(m.verificationURL)
275 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 {
443func (p *Permissions) renderHeader(contentWidth int) string {
443func (p *Permissions) renderHeader(contentWidth int) string {
444 t := p.com.Styles
444 t := p.com.Styles
445
445
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)
447 title = t.Dialog.Title.Render(title)
447 title = t.Dialog.Title.Render(title)
448
448
449 // Tool info.
449 // Tool info.
@@ -489,8 +489,8 @@ func (p *Permissions) renderHeader(contentWidth int) string {
489
489
490func (p *Permissions) renderKeyValue(key, value string, width int) string {
490func (p *Permissions) renderKeyValue(key, value string, width int) string {
491 t := p.com.Styles
491 t := p.com.Styles
492− keyStyle := t.Muted
492+ keyStyle := t.Dialog.Permissions.KeyText
493− valueStyle := t.Base
493+ valueStyle := t.Dialog.Permissions.ValueText
494
494
495 keyStr := keyStyle.Render(key)
495 keyStr := keyStyle.Render(key)
496 valueStr := valueStyle.Width(width - lipgloss.Width(keyStr) - 1).Render(" " + value)
496 valueStr := valueStyle.Width(width - lipgloss.Width(keyStr) - 1).Render(" " + value)
@@ -699,7 +699,7 @@ func (p *Permissions) renderDefaultContent(width int) string {
699 if err := json.Unmarshal([]byte(paramStr), &parsed); err == nil {
699 if err := json.Unmarshal([]byte(paramStr), &parsed); err == nil {
700 if b, err := json.MarshalIndent(parsed, "", " "); err == nil {
700 if b, err := json.MarshalIndent(parsed, "", " "); err == nil {
701 jsonContent := string(b)
701 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)
703 if err == nil {
703 if err == nil {
704 jsonContent = highlighted
704 jsonContent = highlighted
705 }
705 }
internal/ui/dialog/quit.go+2−2
@@ -96,7 +96,7 @@ func (q *Quit) HandleMsg(msg tea.Msg) Action {
96// Draw implements [Dialog].
96// Draw implements [Dialog].
97func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
97func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
98 const question = "Are you sure you want to quit?"
98 const question = "Are you sure you want to quit?"
99− baseStyle := q.com.Styles.Base
99+ baseStyle := q.com.Styles.Dialog.Quit.Content
100 buttonOpts := []common.ButtonOpts{
100 buttonOpts := []common.ButtonOpts{
101 {Text: "Yep!", Selected: !q.selectedNo, Padding: 3},
101 {Text: "Yep!", Selected: !q.selectedNo, Padding: 3},
102 {Text: "Nope", Selected: q.selectedNo, Padding: 3},
102 {Text: "Nope", Selected: q.selectedNo, Padding: 3},
@@ -111,7 +111,7 @@ func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
111 ),
111 ),
112 )
112 )
113
113
114− view := q.com.Styles.BorderFocus.Render(content)
114+ view := q.com.Styles.Dialog.Quit.Frame.Render(content)
115 DrawCenter(scr, area, view)
115 DrawCenter(scr, area, view)
116 return nil
116 return nil
117}
117}
internal/ui/dialog/reasoning.go+2−2
@@ -293,8 +293,8 @@ func (r *ReasoningItem) Render(width int) string {
293 styles := ListItemStyles{
293 styles := ListItemStyles{
294 ItemBlurred: r.t.Dialog.NormalItem,
294 ItemBlurred: r.t.Dialog.NormalItem,
295 ItemFocused: r.t.Dialog.SelectedItem,
295 ItemFocused: r.t.Dialog.SelectedItem,
296− InfoTextBlurred: r.t.Base,
296+ InfoTextBlurred: r.t.Dialog.ListItem.InfoBlurred,
297− InfoTextFocused: r.t.Base,
297+ InfoTextFocused: r.t.Dialog.ListItem.InfoFocused,
298 }
298 }
299 return renderItem(styles, r.title, info, r.focused, width, r.cache, &r.m)
299 return renderItem(styles, r.title, info, r.focused, width, r.cache, &r.m)
300}
300}
internal/ui/dialog/sessions_item.go+2−2
@@ -79,8 +79,8 @@ func (s *SessionItem) Render(width int) string {
79 styles := ListItemStyles{
79 styles := ListItemStyles{
80 ItemBlurred: s.t.Dialog.NormalItem,
80 ItemBlurred: s.t.Dialog.NormalItem,
81 ItemFocused: s.t.Dialog.SelectedItem,
81 ItemFocused: s.t.Dialog.SelectedItem,
82− InfoTextBlurred: s.t.Subtle,
82+ InfoTextBlurred: s.t.Dialog.Sessions.InfoBlurred,
83− InfoTextFocused: s.t.Base,
83+ InfoTextFocused: s.t.Dialog.Sessions.InfoFocused,
84 }
84 }
85
85
86 switch s.sessionsMode {
86 switch s.sessionsMode {
internal/ui/logo/example/main.go+7−7
@@ -20,23 +20,23 @@ func main() {
20
20
21 s := styles.DefaultStyles()
21 s := styles.DefaultStyles()
22 opts := logo.Opts{
22 opts := logo.Opts{
23− FieldColor: s.LogoFieldColor,
23+ FieldColor: s.Logo.FieldColor,
24− TitleColorA: s.LogoTitleColorA,
24+ TitleColorA: s.Logo.TitleColorA,
25− TitleColorB: s.LogoTitleColorB,
25+ TitleColorB: s.Logo.TitleColorB,
26− CharmColor: s.LogoCharmColor,
26+ CharmColor: s.Logo.CharmColor,
27− VersionColor: s.LogoVersionColor,
27+ VersionColor: s.Logo.VersionColor,
28 Width: w,
28 Width: w,
29 Unstable: true,
29 Unstable: true,
30 }
30 }
31
31
32 renderCompact := func(hyper bool) string {
32 renderCompact := func(hyper bool) string {
33 opts.Hyper = hyper
33 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)
35 }
35 }
36
36
37 renderWide := func(hyper bool) string {
37 renderWide := func(hyper bool) string {
38 opts.Hyper = hyper
38 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)
40 }
40 }
41
41
42 lipgloss.Println(
42 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 {
149// SmallRender renders a smaller version of the Crush logo, suitable for
149// SmallRender renders a smaller version of the Crush logo, suitable for
150// smaller windows or sidebar usage.
150// smaller windows or sidebar usage.
151func SmallRender(t *styles.Styles, width int) string {
151func SmallRender(t *styles.Styles, width int) string {
152− title := t.Base.Foreground(t.Secondary).Render("Charm™")
152+ title := t.Logo.SmallCharm.Render("Charm™")
153− title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Base, "Crush", t.Secondary, t.Primary))
153+ title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Logo.GradCanvas, "Crush", t.Logo.SmallGradFromColor, t.Logo.SmallGradToColor))
154 remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush"
154 remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush"
155 if remainingWidth > 0 {
155 if remainingWidth > 0 {
156 lines := strings.Repeat("╱", remainingWidth)
156 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))
158 }
158 }
159 return title
159 return title
160}
160}
internal/ui/model/header.go+2−2
@@ -39,7 +39,7 @@ func newHeader(com *common.Common) *header {
39 }
39 }
40 t := com.Styles
40 t := com.Styles
41 h.compactLogo = t.Header.Charm.Render("Charm™") + " " +
41 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) + " "
43 return h
43 return h
44}
44}
45
45
@@ -102,7 +102,7 @@ func (h *header) drawHeader(
102 b.WriteString(details)
102 b.WriteString(details)
103
103
104 view := uv.NewStyledString(
104 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()))
106 view.Draw(scr, area)
106 view.Draw(scr, area)
107}
107}
108
108
internal/ui/model/layout_test.go+1−1
@@ -30,7 +30,7 @@ func newTestUI() *UI {
30 com := common.DefaultCommon(nil)
30 com := common.DefaultCommon(nil)
31
31
32 ta := textarea.New()
32 ta := textarea.New()
33− ta.SetStyles(com.Styles.TextArea)
33+ ta.SetStyles(com.Styles.Editor.Textarea)
34 ta.ShowLineNumbers = false
34 ta.ShowLineNumbers = false
35 ta.CharLimit = -1
35 ta.CharLimit = -1
36 ta.SetVirtualCursor(false)
36 ta.SetVirtualCursor(false)
internal/ui/model/lsp.go+16−16
@@ -41,11 +41,11 @@ func (m *UI) lspInfo(width, maxItems int, isSection bool) string {
41 lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
41 lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
42 }
42 }
43
43
44− title := t.ResourceGroupTitle.Render("LSPs")
44+ title := t.Resource.Heading.Render("LSPs")
45 if isSection {
45 if isSection {
46 title = common.Section(t, title, width)
46 title = common.Section(t, title, width)
47 }
47 }
48− list := t.ResourceAdditionalText.Render("None")
48+ list := t.Resource.AdditionalText.Render("None")
49 if len(lsps) > 0 {
49 if len(lsps) > 0 {
50 list = lspList(t, lsps, width, maxItems)
50 list = lspList(t, lsps, width, maxItems)
51 }
51 }
@@ -80,31 +80,31 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
80 var renderedLsps []string
80 var renderedLsps []string
81 for _, l := range lsps {
81 for _, l := range lsps {
82 var icon string
82 var icon string
83− title := t.ResourceName.Render(l.Name)
83+ title := t.Resource.Name.Render(l.Name)
84 var description string
84 var description string
85 var diagnostics string
85 var diagnostics string
86 switch l.State {
86 switch l.State {
87 case lsp.StateUnstarted:
87 case lsp.StateUnstarted:
88− icon = t.ResourceOfflineIcon.String()
88+ icon = t.Resource.OfflineIcon.String()
89− description = t.ResourceStatus.Render("unstarted")
89+ description = t.Resource.StatusText.Render("unstarted")
90 case lsp.StateStopped:
90 case lsp.StateStopped:
91− icon = t.ResourceOfflineIcon.String()
91+ icon = t.Resource.OfflineIcon.String()
92− description = t.ResourceStatus.Render("stopped")
92+ description = t.Resource.StatusText.Render("stopped")
93 case lsp.StateStarting:
93 case lsp.StateStarting:
94− icon = t.ResourceBusyIcon.String()
94+ icon = t.Resource.BusyIcon.String()
95− description = t.ResourceStatus.Render("starting...")
95+ description = t.Resource.StatusText.Render("starting...")
96 case lsp.StateReady:
96 case lsp.StateReady:
97− icon = t.ResourceOnlineIcon.String()
97+ icon = t.Resource.OnlineIcon.String()
98 diagnostics = lspDiagnostics(t, l.Diagnostics)
98 diagnostics = lspDiagnostics(t, l.Diagnostics)
99 case lsp.StateError:
99 case lsp.StateError:
100− icon = t.ResourceErrorIcon.String()
100+ icon = t.Resource.ErrorIcon.String()
101− description = t.ResourceStatus.Render("error")
101+ description = t.Resource.StatusText.Render("error")
102 if l.Error != nil {
102 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()))
104 }
104 }
105 case lsp.StateDisabled:
105 case lsp.StateDisabled:
106− icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
106+ icon = t.Resource.DisabledIcon.String()
107− description = t.ResourceStatus.Render("disabled")
107+ description = t.Resource.StatusText.Render("disabled")
108 default:
108 default:
109 continue
109 continue
110 }
110 }
@@ -119,7 +119,7 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
119 if len(renderedLsps) > maxItems {
119 if len(renderedLsps) > maxItems {
120 visibleItems := renderedLsps[:maxItems-1]
120 visibleItems := renderedLsps[:maxItems-1]
121 remaining := len(renderedLsps) - maxItems
121 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)))
123 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
123 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
124 }
124 }
125 return lipgloss.JoinVertical(lipgloss.Left, renderedLsps...)
125 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 {
23 }
23 }
24 }
24 }
25
25
26− title := t.ResourceGroupTitle.Render("MCPs")
26+ title := t.Resource.Heading.Render("MCPs")
27 if isSection {
27 if isSection {
28 title = common.Section(t, title, width)
28 title = common.Section(t, title, width)
29 }
29 }
30− list := t.ResourceAdditionalText.Render("None")
30+ list := t.Resource.AdditionalText.Render("None")
31 if len(mcps) > 0 {
31 if len(mcps) > 0 {
32 list = mcpList(t, mcps, width, maxItems)
32 list = mcpList(t, mcps, width, maxItems)
33 }
33 }
@@ -39,13 +39,13 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
39func mcpCounts(t *styles.Styles, counts mcp.Counts) string {
39func mcpCounts(t *styles.Styles, counts mcp.Counts) string {
40 var parts []string
40 var parts []string
41 if counts.Tools > 0 {
41 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)))
43 }
43 }
44 if counts.Prompts > 0 {
44 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)))
46 }
46 }
47 if counts.Resources > 0 {
47 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)))
49 }
49 }
50 return strings.Join(parts, " ")
50 return strings.Join(parts, " ")
51}
51}
@@ -65,28 +65,28 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
65 if m.Name == config.DockerMCPName {
65 if m.Name == config.DockerMCPName {
66 title = "Docker MCP"
66 title = "Docker MCP"
67 }
67 }
68− title = t.ResourceName.Render(title)
68+ title = t.Resource.Name.Render(title)
69 var description string
69 var description string
70 var extraContent string
70 var extraContent string
71
71
72 switch m.State {
72 switch m.State {
73 case mcp.StateStarting:
73 case mcp.StateStarting:
74− icon = t.ResourceBusyIcon.String()
74+ icon = t.Resource.BusyIcon.String()
75− description = t.ResourceStatus.Render("starting...")
75+ description = t.Resource.StatusText.Render("starting...")
76 case mcp.StateConnected:
76 case mcp.StateConnected:
77− icon = t.ResourceOnlineIcon.String()
77+ icon = t.Resource.OnlineIcon.String()
78 extraContent = mcpCounts(t, m.Counts)
78 extraContent = mcpCounts(t, m.Counts)
79 case mcp.StateError:
79 case mcp.StateError:
80− icon = t.ResourceErrorIcon.String()
80+ icon = t.Resource.ErrorIcon.String()
81− description = t.ResourceStatus.Render("error")
81+ description = t.Resource.StatusText.Render("error")
82 if m.Error != nil {
82 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()))
84 }
84 }
85 case mcp.StateDisabled:
85 case mcp.StateDisabled:
86− icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
86+ icon = t.Resource.DisabledIcon.String()
87− description = t.ResourceStatus.Render("disabled")
87+ description = t.Resource.StatusText.Render("disabled")
88 default:
88 default:
89− icon = t.ResourceOfflineIcon.String()
89+ icon = t.Resource.OfflineIcon.String()
90 }
90 }
91
91
92 renderedMcps = append(renderedMcps, common.Status(t, common.StatusOpts{
92 renderedMcps = append(renderedMcps, common.Status(t, common.StatusOpts{
@@ -100,7 +100,7 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
100 if len(renderedMcps) > maxItems {
100 if len(renderedMcps) > maxItems {
101 visibleItems := renderedMcps[:maxItems-1]
101 visibleItems := renderedMcps[:maxItems-1]
102 remaining := len(renderedMcps) - maxItems
102 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)))
104 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
104 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
105 }
105 }
106 return lipgloss.JoinVertical(lipgloss.Left, renderedMcps...)
106 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 {
56 if queue <= 0 {
56 if queue <= 0 {
57 return ""
57 return ""
58 }
58 }
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)
60 if queue < len(triangles) {
60 if queue < len(triangles) {
61 triangles = triangles[:queue]
61 triangles = triangles[:queue]
62 }
62 }
63
63
64− text := t.Base.Render(fmt.Sprintf("%d Queued", queue))
64+ text := t.Pills.QueueLabel.Render(fmt.Sprintf("%d Queued", queue))
65 content := fmt.Sprintf("%s %s", strings.Join(triangles, ""), text)
65 content := fmt.Sprintf("%s %s", strings.Join(triangles, ""), text)
66 return pillStyle(focused, panelFocused, t).Render(content)
66 return pillStyle(focused, panelFocused, t).Render(content)
67}
67}
@@ -87,8 +87,8 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
87
87
88 total := len(todos)
88 total := len(todos)
89
89
90− label := t.Base.Render("To-Do")
90+ label := t.Pills.TodoLabel.Render("To-Do")
91− progress := t.Muted.Render(fmt.Sprintf("%d/%d", completed, total))
91+ progress := t.Pills.TodoProgress.Render(fmt.Sprintf("%d/%d", completed, total))
92
92
93 var content string
93 var content string
94 if panelFocused {
94 if panelFocused {
@@ -101,7 +101,7 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
101 if len(taskText) > maxTaskDisplayLength {
101 if len(taskText) > maxTaskDisplayLength {
102 taskText = taskText[:maxTaskDisplayLength-1] + "…"
102 taskText = taskText[:maxTaskDisplayLength-1] + "…"
103 }
103 }
104− task := t.Subtle.Render(taskText)
104+ task := t.Pills.TodoCurrentTask.Render(taskText)
105 content = fmt.Sprintf("%s %s %s %s", spinnerView, label, progress, task)
105 content = fmt.Sprintf("%s %s %s %s", spinnerView, label, progress, task)
106 } else {
106 } else {
107 content = fmt.Sprintf("%s %s", label, progress)
107 content = fmt.Sprintf("%s %s", label, progress)
@@ -128,7 +128,7 @@ func queueList(queueItems []string, t *styles.Styles) string {
128 text = text[:maxQueueDisplayLength-1] + "…"
128 text = text[:maxQueueDisplayLength-1] + "…"
129 }
129 }
130 prefix := t.Pills.QueueItemPrefix.Render() + " "
130 prefix := t.Pills.QueueItemPrefix.Render() + " "
131− lines = append(lines, prefix+t.Muted.Render(text))
131+ lines = append(lines, prefix+t.Pills.QueueItemText.Render(text))
132 }
132 }
133
133
134 return strings.Join(lines, "\n")
134 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 {
163func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
163func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
164 t := m.com.Styles
164 t := m.com.Styles
165
165
166− title := t.Subtle.Render("Modified Files")
166+ title := t.Files.SectionTitle.Render("Modified Files")
167 if isSection {
167 if isSection {
168 title = common.Section(t, "Modified Files", width)
168 title = common.Section(t, "Modified Files", width)
169 }
169 }
170− list := t.Subtle.Render("None")
170+ list := t.Files.EmptyMessage.Render("None")
171 var filesWithChanges []SessionFile
171 var filesWithChanges []SessionFile
172 for _, f := range m.sessionFiles {
172 for _, f := range m.sessionFiles {
173 if f.Additions == 0 && f.Deletions == 0 {
173 if f.Additions == 0 && f.Deletions == 0 {
@@ -226,7 +226,7 @@ func fileList(t *styles.Styles, cwd string, filesWithChanges []SessionFile, widt
226
226
227 if len(filesWithChanges) > maxItems {
227 if len(filesWithChanges) > maxItems {
228 remaining := len(filesWithChanges) - maxItems
228 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)))
230 }
230 }
231
231
232 return lipgloss.JoinVertical(lipgloss.Left, renderedFiles...)
232 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) {
138 width := area.Dx()
138 width := area.Dx()
139 height := area.Dy()
139 height := area.Dy()
140
140
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)
142 cwd := common.PrettyPath(t, m.com.Workspace.WorkingDir(), width)
142 cwd := common.PrettyPath(t, m.com.Workspace.WorkingDir(), width)
143 sidebarLogo := m.sidebarLogo
143 sidebarLogo := m.sidebarLogo
144 if height < logoHeightBreakpoint {
144 if height < logoHeightBreakpoint {
internal/ui/model/skills.go+8−8
@@ -38,14 +38,14 @@ func cachedBuiltinSkills() []*skills.Skill {
38func (m *UI) skillsInfo(width, maxItems int, isSection bool) string {
38func (m *UI) skillsInfo(width, maxItems int, isSection bool) string {
39 t := m.com.Styles
39 t := m.com.Styles
40
40
41− title := t.ResourceGroupTitle.Render("Skills")
41+ title := t.Resource.Heading.Render("Skills")
42 if isSection {
42 if isSection {
43 title = common.Section(t, title, width)
43 title = common.Section(t, title, width)
44 }
44 }
45
45
46 items := m.skillStatusItems()
46 items := m.skillStatusItems()
47 if len(items) == 0 {
47 if len(items) == 0 {
48− list := t.ResourceAdditionalText.Render("None")
48+ list := t.Resource.AdditionalText.Render("None")
49 return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
49 return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
50 }
50 }
51
51
@@ -68,14 +68,14 @@ func (m *UI) skillStatusItems() []skillStatusItem {
68 name = filepath.Base(filepath.Dir(state.Path))
68 name = filepath.Base(filepath.Dir(state.Path))
69 }
69 }
70 stateNames[name] = struct{}{}
70 stateNames[name] = struct{}{}
71− icon := t.ResourceOnlineIcon.String()
71+ icon := t.Resource.OnlineIcon.String()
72 if state.State == skills.StateError {
72 if state.State == skills.StateError {
73− icon = t.ResourceErrorIcon.String()
73+ icon = t.Resource.ErrorIcon.String()
74 }
74 }
75 items = append(items, skillStatusItem{
75 items = append(items, skillStatusItem{
76 icon: icon,
76 icon: icon,
77 name: name,
77 name: name,
78− title: t.ResourceName.Render(name),
78+ title: t.Resource.Name.Render(name),
79 })
79 })
80 }
80 }
81
81
@@ -88,9 +88,9 @@ func (m *UI) skillStatusItems() []skillStatusItem {
88 continue
88 continue
89 }
89 }
90 items = append(items, skillStatusItem{
90 items = append(items, skillStatusItem{
91− icon: t.ResourceOnlineIcon.String(),
91+ icon: t.Resource.OnlineIcon.String(),
92 name: skill.Name,
92 name: skill.Name,
93− title: t.ResourceName.Render(skill.Name),
93+ title: t.Resource.Name.Render(skill.Name),
94 })
94 })
95 }
95 }
96
96
@@ -111,7 +111,7 @@ func skillsList(t *styles.Styles, items []skillStatusItem, width, maxItems int)
111 remaining := len(items) - (maxItems - 1)
111 remaining := len(items) - (maxItems - 1)
112 items = append(visibleItems, skillStatusItem{
112 items = append(visibleItems, skillStatusItem{
113 name: "more",
113 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)),
115 })
115 })
116 }
116 }
117
117
internal/ui/model/skills_test.go+2−2
@@ -28,7 +28,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
28
28
29 var hasGoDoc bool
29 var hasGoDoc bool
30 for _, item := range items {
30 for _, item := range items {
31− if item.title == st.ResourceName.Render("go-doc") {
31+ if item.title == st.Resource.Name.Render("go-doc") {
32 hasGoDoc = true
32 hasGoDoc = true
33 break
33 break
34 }
34 }
@@ -43,7 +43,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
43 if skill.Name == "go-doc" {
43 if skill.Name == "go-doc" {
44 continue
44 continue
45 }
45 }
46− expected := st.ResourceName.Render(skill.Name)
46+ expected := st.Resource.Name.Render(skill.Name)
47 for _, item := range items {
47 for _, item := range items {
48 if item.title == expected {
48 if item.title == expected {
49 hasBuiltin = true
49 hasBuiltin = true
internal/ui/model/ui.go+14−14
@@ -269,7 +269,7 @@ type UI struct {
269func New(com *common.Common, initialSessionID string, continueLast bool) *UI {
269func New(com *common.Common, initialSessionID string, continueLast bool) *UI {
270 // Editor components
270 // Editor components
271 ta := textarea.New()
271 ta := textarea.New()
272− ta.SetStyles(com.Styles.TextArea)
272+ ta.SetStyles(com.Styles.Editor.Textarea)
273 ta.ShowLineNumbers = false
273 ta.ShowLineNumbers = false
274 ta.CharLimit = -1
274 ta.CharLimit = -1
275 ta.SetVirtualCursor(false)
275 ta.SetVirtualCursor(false)
@@ -2745,9 +2745,9 @@ func (m *UI) normalPromptFunc(info textarea.PromptInfo) string {
2745 return "::: "
2745 return "::: "
2746 }
2746 }
2747 if info.Focused {
2747 if info.Focused {
2748− return t.EditorPromptNormalFocused.Render()
2748+ return t.Editor.PromptNormalFocused.Render()
2749 }
2749 }
2750− return t.EditorPromptNormalBlurred.Render()
2750+ return t.Editor.PromptNormalBlurred.Render()
2751}
2751}
2752
2752
2753// yoloPromptFunc returns the yolo mode editor prompt style with warning icon
2753// yoloPromptFunc returns the yolo mode editor prompt style with warning icon
@@ -2756,15 +2756,15 @@ func (m *UI) yoloPromptFunc(info textarea.PromptInfo) string {
2756 t := m.com.Styles
2756 t := m.com.Styles
2757 if info.LineNumber == 0 {
2757 if info.LineNumber == 0 {
2758 if info.Focused {
2758 if info.Focused {
2759− return t.EditorPromptYoloIconFocused.Render()
2759+ return t.Editor.PromptYoloIconFocused.Render()
2760 } else {
2760 } else {
2761− return t.EditorPromptYoloIconBlurred.Render()
2761+ return t.Editor.PromptYoloIconBlurred.Render()
2762 }
2762 }
2763 }
2763 }
2764 if info.Focused {
2764 if info.Focused {
2765− return t.EditorPromptYoloDotsFocused.Render()
2765+ return t.Editor.PromptYoloDotsFocused.Render()
2766 }
2766 }
2767− return t.EditorPromptYoloDotsBlurred.Render()
2767+ return t.Editor.PromptYoloDotsBlurred.Render()
2768}
2768}
2769
2769
2770// closeCompletions closes the completions popup and resets state.
2770// closeCompletions closes the completions popup and resets state.
@@ -3536,7 +3536,7 @@ func (m *UI) drawSessionDetails(scr uv.Screen, area uv.Rectangle) {
3536 blocks...,
3536 blocks...,
3537 )
3537 )
3538
3538
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)
3540
3540
3541 remainingHeight := height - lipgloss.Height(detailsHeader) - lipgloss.Height(version)
3541 remainingHeight := height - lipgloss.Height(detailsHeader) - lipgloss.Height(version)
3542
3542
@@ -3651,12 +3651,12 @@ func (m *UI) disableDockerMCP() tea.Msg {
3651
3651
3652// renderLogo renders the Crush logo with the given styles and dimensions.
3652// renderLogo renders the Crush logo with the given styles and dimensions.
3653func renderLogo(t *styles.Styles, compact bool, width int) string {
3653func renderLogo(t *styles.Styles, compact bool, width int) string {
3654− return logo.Render(t.Base, version.Version, compact, logo.Opts{
3654+ return logo.Render(t.Logo.GradCanvas, version.Version, compact, logo.Opts{
3655− FieldColor: t.LogoFieldColor,
3655+ FieldColor: t.Logo.FieldColor,
3656− TitleColorA: t.LogoTitleColorA,
3656+ TitleColorA: t.Logo.TitleColorA,
3657− TitleColorB: t.LogoTitleColorB,
3657+ TitleColorB: t.Logo.TitleColorB,
3658− CharmColor: t.LogoCharmColor,
3658+ CharmColor: t.Logo.CharmColor,
3659− VersionColor: t.LogoVersionColor,
3659+ VersionColor: t.Logo.VersionColor,
3660 Width: width,
3660 Width: width,
3661 })
3661 })
3662}
3662}
internal/ui/styles/styles.go+387−295
@@ -58,28 +58,19 @@ const (
58)
58)
59
59
60type Styles struct {
60type 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−
74 // Header
61 // Header
75 Header struct {
62 Header struct {
76− Charm lipgloss.Style // Style for "Charm™" label
63+ Charm lipgloss.Style // Style for "Charm™" label
77− Diagonals lipgloss.Style // Style for diagonal separators (╱)
64+ Diagonals lipgloss.Style // Style for diagonal separators (╱)
78− Percentage lipgloss.Style // Style for context percentage
65+ Percentage lipgloss.Style // Style for context percentage
79− Keystroke lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
66+ Keystroke lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
80− KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
67+ KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
81− WorkingDir lipgloss.Style // Style for current working directory
68+ WorkingDir lipgloss.Style // Style for current working directory
82− Separator lipgloss.Style // Style for separator dots (•)
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
83 }
74 }
84
75
85 CompactDetails struct {
76 CompactDetails struct {
@@ -88,43 +79,18 @@ type Styles struct {
88 Title lipgloss.Style
79 Title lipgloss.Style
89 }
80 }
90
81
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−
101 // Tool calls
82 // Tool calls
102− ToolCallPending lipgloss.Style
103− ToolCallError lipgloss.Style
104− ToolCallSuccess lipgloss.Style
83+ ToolCallSuccess lipgloss.Style
105− ToolCallCancelled lipgloss.Style
106− EarlyStateMessage lipgloss.Style
107
84
108 // Text selection
85 // Text selection
109 TextSelection lipgloss.Style
86 TextSelection lipgloss.Style
110
87
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−
121 // Markdown & Chroma
88 // Markdown & Chroma
122 Markdown ansi.StyleConfig
89 Markdown ansi.StyleConfig
123− PlainMarkdown ansi.StyleConfig
90+ QuietMarkdown ansi.StyleConfig
124
91
125 // Inputs
92 // Inputs
126 TextInput textinput.Styles
93 TextInput textinput.Styles
127− TextArea textarea.Styles
128
94
129 // Help
95 // Help
130 Help help.Styles
96 Help help.Styles
@@ -136,62 +102,55 @@ type Styles struct {
136 FilePicker filepicker.Styles
102 FilePicker filepicker.Styles
137
103
138 // Buttons
104 // Buttons
139− ButtonFocus lipgloss.Style
105+ Button struct {
140− ButtonBlur lipgloss.Style
106+ Focused lipgloss.Style
141−
142− // Borders
143− BorderFocus lipgloss.Style
107+ Blurred lipgloss.Style
144− BorderBlur lipgloss.Style
108+ }
145
109
146 // Editor
110 // Editor
147− EditorPromptNormalFocused lipgloss.Style
111+ Editor struct {
148− EditorPromptNormalBlurred lipgloss.Style
112+ Textarea textarea.Styles
149− EditorPromptYoloIconFocused lipgloss.Style
113+
150− EditorPromptYoloIconBlurred lipgloss.Style
114+ // Normal mode prompt (default "::: ").
151− EditorPromptYoloDotsFocused lipgloss.Style
115+ PromptNormalFocused lipgloss.Style
152− EditorPromptYoloDotsBlurred 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+ }
153
124
154 // Radio
125 // Radio
126+ Radio struct {
127+ On lipgloss.Style
155− RadioOn lipgloss.Style
128+ Off lipgloss.Style
156− RadioOff lipgloss.Style
129+ Label lipgloss.Style // Text next to a radio button
130+ }
157
131
158 // Background
132 // Background
159 Background color.Color
133 Background color.Color
160
134
161 // Logo
135 // Logo
136+ Logo struct {
162− LogoFieldColor color.Color
137+ FieldColor color.Color
163− LogoTitleColorA color.Color
138+ TitleColorA color.Color
164− LogoTitleColorB color.Color
139+ TitleColorB color.Color
165− LogoCharmColor color.Color
140+ CharmColor color.Color
166− LogoVersionColor color.Color
141+ VersionColor color.Color
167−
142+ SmallCharm lipgloss.Style // "Charm™" label in SmallRender
168− // Colors - semantic colors for tool rendering.
143+ SmallDiagonals lipgloss.Style // Diagonal line fill in SmallRender
169− Primary color.Color
144+ GradCanvas lipgloss.Style // Blank canvas for gradient painting
170− Secondary color.Color
145+ SmallGradFromColor color.Color // Small "Crush" wordmark gradient start
171− Tertiary color.Color
146+ SmallGradToColor color.Color // Small "Crush" wordmark gradient end
172− BgBase color.Color
147+ }
173− BgBaseLighter color.Color
148+
174− BgSubtle color.Color
149+ // Working indicator gradient (spinners/shimmers on assistant "thinking",
175− BgOverlay color.Color
150+ // tool-call pending, CLI generating, startup).
176− FgBase color.Color
151+ WorkingGradFromColor color.Color
177− FgMuted color.Color
178− FgHalfMuted color.Color
152+ WorkingGradToColor color.Color
179− FgSubtle color.Color
153+ WorkingLabelColor color.Color // Label text color next to the indicator
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
195
154
196 // Section Title
155 // Section Title
197 Section struct {
156 Section struct {
@@ -214,41 +173,80 @@ type Styles struct {
214 InfoDiagnostic lipgloss.Style
173 InfoDiagnostic lipgloss.Style
215 }
174 }
216
175
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+
217 // Files
213 // Files
218 Files struct {
214 Files struct {
219− Path lipgloss.Style
215+ Path lipgloss.Style
220− Additions lipgloss.Style
216+ Additions lipgloss.Style
221− Deletions 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
222 }
221 }
223
222
224 // Chat
223 // Chat
225− Chat struct {
226− // Message item styles
224+ // Messages - chat message item styles
227− Message struct {
225+ Messages struct {
228− UserBlurred lipgloss.Style
226+ UserBlurred lipgloss.Style
229− UserFocused lipgloss.Style
227+ UserFocused lipgloss.Style
230− AssistantBlurred lipgloss.Style
228+ AssistantBlurred lipgloss.Style
231− AssistantFocused lipgloss.Style
229+ AssistantFocused lipgloss.Style
232− NoContent lipgloss.Style
230+ NoContent lipgloss.Style
233− Thinking lipgloss.Style
231+ Thinking lipgloss.Style
234− ErrorTag lipgloss.Style
232+ ErrorTag lipgloss.Style
235− ErrorTitle lipgloss.Style
233+ ErrorTitle lipgloss.Style
236− ErrorDetails lipgloss.Style
234+ ErrorDetails lipgloss.Style
237− ToolCallFocused lipgloss.Style
235+ ToolCallFocused lipgloss.Style
238− ToolCallCompact lipgloss.Style
236+ ToolCallCompact lipgloss.Style
239− ToolCallBlurred lipgloss.Style
237+ ToolCallBlurred lipgloss.Style
240− SectionHeader lipgloss.Style
238+ SectionHeader lipgloss.Style
241−
239+
242− // Thinking section styles
240+ // Thinking section styles
243− ThinkingBox lipgloss.Style // Background for thinking content
241+ ThinkingBox lipgloss.Style // Background for thinking content
244− ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
242+ ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
245− ThinkingFooterTitle lipgloss.Style // "Thought for" text
243+ ThinkingFooterTitle lipgloss.Style // "Thought for" text
246− ThinkingFooterDuration lipgloss.Style // Duration value
244+ ThinkingFooterDuration lipgloss.Style // Duration value
247− AssistantInfoIcon lipgloss.Style
245+ AssistantInfoIcon lipgloss.Style
248− AssistantInfoModel lipgloss.Style
246+ AssistantInfoModel lipgloss.Style
249− AssistantInfoProvider lipgloss.Style
247+ AssistantInfoProvider lipgloss.Style
250− AssistantInfoDuration lipgloss.Style
248+ AssistantInfoDuration lipgloss.Style
251− }
249+ AssistantCanceled lipgloss.Style // Italic "Canceled" footer
252 }
250 }
253
251
254 // Tool - styles for tool call rendering
252 // Tool - styles for tool call rendering
@@ -316,6 +314,9 @@ type Styles struct {
316 TodoCompletedIcon lipgloss.Style // Completed todo icon
314 TodoCompletedIcon lipgloss.Style // Completed todo icon
317 TodoInProgressIcon lipgloss.Style // In-progress todo icon
315 TodoInProgressIcon lipgloss.Style // In-progress todo icon
318 TodoPendingIcon lipgloss.Style // Pending todo icon
316 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
319
320
320 // MCP tools
321 // MCP tools
321 MCPName lipgloss.Style // The mcp name
322 MCPName lipgloss.Style // The mcp name
@@ -329,17 +330,26 @@ type Styles struct {
329 ResourceSize lipgloss.Style
330 ResourceSize lipgloss.Style
330 MediaType lipgloss.Style
331 MediaType lipgloss.Style
331
332
332− // Docker MCP tools
333+ // Action verb colors for tool-call headers.
333− DockerMCPActionAdd lipgloss.Style // Docker MCP add action (green)
334+ ActionCreate lipgloss.Style // Constructive actions (e.g. "Add", "Create")
334− DockerMCPActionDel lipgloss.Style // Docker MCP remove action (red)
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)
335 }
342 }
336
343
337 // Dialog styles
344 // Dialog styles
338 Dialog struct {
345 Dialog struct {
339− Title lipgloss.Style
346+ Title lipgloss.Style
340− TitleText lipgloss.Style
347+ TitleText lipgloss.Style
341− TitleError lipgloss.Style
348+ TitleError lipgloss.Style
342− TitleAccent 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
343 // View is the main content area style.
353 // View is the main content area style.
344 View lipgloss.Style
354 View lipgloss.Style
345 PrimaryText lipgloss.Style
355 PrimaryText lipgloss.Style
@@ -381,7 +391,43 @@ type Styles struct {
381 InputRequiredMarkFocused lipgloss.Style
391 InputRequiredMarkFocused lipgloss.Style
382 }
392 }
383
393
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+
384− Commands struct{}
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+ }
385
431
386 ImagePreview lipgloss.Style
432 ImagePreview lipgloss.Style
387
433
@@ -404,6 +450,9 @@ type Styles struct {
404 RenamingTitleGradientFromColor color.Color
450 RenamingTitleGradientFromColor color.Color
405 RenamingTitleGradientToColor color.Color
451 RenamingTitleGradientToColor color.Color
406 RenamingPlaceholder lipgloss.Style
452 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
407 }
456 }
408 }
457 }
409
458
@@ -441,14 +490,22 @@ type Styles struct {
441
490
442 // Pills styles for todo/queue pills
491 // Pills styles for todo/queue pills
443 Pills struct {
492 Pills struct {
444− Base lipgloss.Style // Base pill style with padding
493+ Base lipgloss.Style // Base pill style with padding
445− Focused lipgloss.Style // Focused pill with visible border
494+ Focused lipgloss.Style // Focused pill with visible border
446− Blurred lipgloss.Style // Blurred pill with hidden border
495+ Blurred lipgloss.Style // Blurred pill with hidden border
447− QueueItemPrefix lipgloss.Style // Prefix for queue list items
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
448− HelpKey lipgloss.Style // Keystroke hint style
506+ HelpKey lipgloss.Style // Keystroke hint style
449− HelpText lipgloss.Style // Help action text style
507+ HelpText lipgloss.Style // Help action text style
450− Area lipgloss.Style // Pills area container
508+ Area lipgloss.Style // Pills area container
451− TodoSpinner lipgloss.Style // Todo spinner style
452 }
509 }
453}
510}
454
511
@@ -552,38 +609,17 @@ func DefaultStyles() Styles {
552 normalBorder := lipgloss.NormalBorder()
609 normalBorder := lipgloss.NormalBorder()
553
610
554 base := lipgloss.NewStyle().Foreground(fgBase)
611 base := lipgloss.NewStyle().Foreground(fgBase)
612+ muted := lipgloss.NewStyle().Foreground(fgMuted)
613+ subtle := lipgloss.NewStyle().Foreground(fgSubtle)
555
614
556 s := Styles{}
615 s := Styles{}
557
616
558 s.Background = bgBase
617 s.Background = bgBase
559
618
560 // Populate color fields
619 // Populate color fields
561− s.Primary = primary
620+ s.WorkingGradFromColor = primary
562− s.Secondary = secondary
621+ s.WorkingGradToColor = secondary
563− s.Tertiary = tertiary
622+ s.WorkingLabelColor = fgBase
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
587
623
588 s.TextInput = textinput.Styles{
624 s.TextInput = textinput.Styles{
589 Focused: textinput.StyleState{
625 Focused: textinput.StyleState{
@@ -605,7 +641,7 @@ func DefaultStyles() Styles {
605 },
641 },
606 }
642 }
607
643
608− s.TextArea = textarea.Styles{
644+ s.Editor.Textarea = textarea.Styles{
609 Focused: textarea.StyleState{
645 Focused: textarea.StyleState{
610 Base: base,
646 Base: base,
611 Text: base,
647 Text: base,
@@ -840,10 +876,10 @@ func DefaultStyles() Styles {
840 },
876 },
841 }
877 }
842
878
843− // PlainMarkdown style - muted colors on subtle background for thinking content.
879+ // QuietMarkdown style - muted colors on subtle background for thinking content.
844 plainBg := new(bgBaseLighter.Hex())
880 plainBg := new(bgBaseLighter.Hex())
845 plainFg := new(fgMuted.Hex())
881 plainFg := new(fgMuted.Hex())
846− s.PlainMarkdown = ansi.StyleConfig{
882+ s.QuietMarkdown = ansi.StyleConfig{
847 Document: ansi.StyleBlock{
883 Document: ansi.StyleBlock{
848 StylePrimitive: ansi.StylePrimitive{
884 StylePrimitive: ansi.StylePrimitive{
849 Color: plainFg,
885 Color: plainFg,
@@ -1081,72 +1117,47 @@ func DefaultStyles() Styles {
1081 }
1117 }
1082
1118
1083 // borders
1119 // borders
1084− s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
1085−
1086− // text presets
1087− s.Base = lipgloss.NewStyle().Foreground(fgBase)
1120+ s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
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)
1098
1121
1099− // Compact header styles
1100 s.Header.Charm = base.Foreground(secondary)
1122 s.Header.Charm = base.Foreground(secondary)
1101 s.Header.Diagonals = base.Foreground(primary)
1123 s.Header.Diagonals = base.Foreground(primary)
1102− s.Header.Percentage = s.Muted
1124+ s.Header.Percentage = muted
1103− s.Header.Keystroke = s.Muted
1125+ s.Header.Keystroke = muted
1104− s.Header.KeystrokeTip = s.Subtle
1126+ s.Header.KeystrokeTip = subtle
1105− s.Header.WorkingDir = s.Muted
1127+ s.Header.WorkingDir = muted
1106− s.Header.Separator = s.Subtle
1128+ s.Header.Separator = subtle
1107−
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+
1108− s.CompactDetails.Title = s.Base
1134+ s.CompactDetails.Title = base
1109− s.CompactDetails.View = s.Base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1135+ s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1110− s.CompactDetails.Version = s.Muted
1136+ s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
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)
1126
1137
1127 // Tool rendering styles
1138 // Tool rendering styles
1128 s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
1139 s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
1129 s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
1140 s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
1130 s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
1141 s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
1131− s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
1142+ s.Tool.IconCancelled = muted.SetString(ToolPending)
1132
1143
1133 s.Tool.NameNormal = base.Foreground(blue)
1144 s.Tool.NameNormal = base.Foreground(blue)
1134 s.Tool.NameNested = base.Foreground(blue)
1145 s.Tool.NameNested = base.Foreground(blue)
1135
1146
1136− s.Tool.ParamMain = s.Subtle
1147+ s.Tool.ParamMain = subtle
1137− s.Tool.ParamKey = s.Subtle
1148+ s.Tool.ParamKey = subtle
1138
1149
1139 // Content rendering - prepared styles that accept width parameter
1150 // Content rendering - prepared styles that accept width parameter
1140− s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
1151+ s.Tool.ContentLine = muted.Background(bgBaseLighter)
1141− s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
1152+ s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
1142− s.Tool.ContentCodeLine = s.Base.Background(bgBase).PaddingLeft(2)
1153+ s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
1143− s.Tool.ContentCodeTruncation = s.Muted.Background(bgBase).PaddingLeft(2)
1154+ s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
1144 s.Tool.ContentCodeBg = bgBase
1155 s.Tool.ContentCodeBg = bgBase
1145 s.Tool.Body = base.PaddingLeft(2)
1156 s.Tool.Body = base.PaddingLeft(2)
1146
1157
1147 // Deprecated - kept for backward compatibility
1158 // Deprecated - kept for backward compatibility
1148− s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
1159+ s.Tool.ContentBg = muted.Background(bgBaseLighter)
1149− s.Tool.ContentText = s.Muted
1160+ s.Tool.ContentText = muted
1150 s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
1161 s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
1151
1162
1152 s.Tool.StateWaiting = base.Foreground(fgSubtle)
1163 s.Tool.StateWaiting = base.Foreground(fgSubtle)
@@ -1156,7 +1167,7 @@ func DefaultStyles() Styles {
1156 s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
1167 s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
1157
1168
1158 // Diff and multi-edit styles
1169 // Diff and multi-edit styles
1159− s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
1170+ s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
1160 s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
1171 s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
1161 s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
1172 s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
1162
1173
@@ -1166,12 +1177,12 @@ func DefaultStyles() Styles {
1166 s.Tool.JobIconSuccess = base.Foreground(green)
1177 s.Tool.JobIconSuccess = base.Foreground(green)
1167 s.Tool.JobToolName = base.Foreground(blue)
1178 s.Tool.JobToolName = base.Foreground(blue)
1168 s.Tool.JobAction = base.Foreground(blueDark)
1179 s.Tool.JobAction = base.Foreground(blueDark)
1169− s.Tool.JobPID = s.Muted
1180+ s.Tool.JobPID = muted
1170− s.Tool.JobDescription = s.Subtle
1181+ s.Tool.JobDescription = subtle
1171
1182
1172 // Agent task styles
1183 // Agent task styles
1173 s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
1184 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
1175
1186
1176 // Agentic fetch styles
1187 // Agentic fetch styles
1177 s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
1188 s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
@@ -1181,6 +1192,9 @@ func DefaultStyles() Styles {
1181 s.Tool.TodoCompletedIcon = base.Foreground(green)
1192 s.Tool.TodoCompletedIcon = base.Foreground(green)
1182 s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
1193 s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
1183 s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
1194 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)
1184
1198
1185 // MCP styles
1199 // MCP styles
1186 s.Tool.MCPName = base.Foreground(blue)
1200 s.Tool.MCPName = base.Foreground(blue)
@@ -1194,103 +1208,138 @@ func DefaultStyles() Styles {
1194 s.Tool.MediaType = base
1208 s.Tool.MediaType = base
1195 s.Tool.ResourceSize = base.Foreground(fgMuted)
1209 s.Tool.ResourceSize = base.Foreground(fgMuted)
1196
1210
1197− // Docker MCP styles
1211+ // Tool-call action verbs and result-list styling.
1198− s.Tool.DockerMCPActionAdd = base.Foreground(greenLight)
1212+ s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(greenLight)
1199− s.Tool.DockerMCPActionDel = base.Foreground(red)
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)
1200
1218
1201 // Buttons
1219 // Buttons
1202− s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
1220+ s.Button.Focused = lipgloss.NewStyle().Foreground(white).Background(secondary)
1203− s.ButtonBlur = s.Base.Background(bgSubtle)
1221+ s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
1204−
1205− // Borders
1206− s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
1207
1222
1208 // Editor
1223 // Editor
1209− s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1224+ s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1210− s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
1225+ s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
1211− s.EditorPromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1226+ s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1212− s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1227+ s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1213− s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1228+ s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1214− s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
1229+ s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(charmtone.Squid)
1215−
1230+
1216− s.RadioOn = s.HalfMuted.SetString(RadioOn)
1231+ s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
1217− s.RadioOff = s.HalfMuted.SetString(RadioOff)
1232+ s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
1218−
1233+ s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
1234+
1219− // Logo colors
1235+ // Logo
1220− s.LogoFieldColor = primary
1236+ s.Logo.FieldColor = primary
1237+ s.Logo.TitleColorA = secondary
1238+ s.Logo.TitleColorB = primary
1221− s.LogoTitleColorA = secondary
1239+ s.Logo.CharmColor = secondary
1222− s.LogoTitleColorB = primary
1240+ s.Logo.VersionColor = primary
1223− s.LogoCharmColor = secondary
1241+ s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
1224− s.LogoVersionColor = primary
1242+ s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
1243+ s.Logo.GradCanvas = lipgloss.NewStyle()
1244+ s.Logo.SmallGradFromColor = secondary
1245+ s.Logo.SmallGradToColor = primary
1225
1246
1226 // Section
1247 // Section
1227− s.Section.Title = s.Subtle
1248+ s.Section.Title = subtle
1228− s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
1249+ s.Section.Line = base.Foreground(charmtone.Charcoal)
1229
1250
1230 // Initialize
1251 // Initialize
1231− s.Initialize.Header = s.Base
1252+ s.Initialize.Header = base
1232− s.Initialize.Content = s.Muted
1253+ s.Initialize.Content = muted
1233− s.Initialize.Accent = s.Base.Foreground(greenDark)
1254+ s.Initialize.Accent = base.Foreground(greenDark)
1234−
1255+
1235− // LSP and MCP status.
1256+ // ResourceGroup (LSP/MCP/skills sidebar lists).
1236− s.ResourceGroupTitle = lipgloss.NewStyle().Foreground(charmtone.Oyster)
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)
1237− s.ResourceOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
1260+ s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
1238− s.ResourceBusyIcon = s.ResourceOfflineIcon.Foreground(charmtone.Citron)
1261+ s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(charmtone.Citron)
1239− s.ResourceErrorIcon = s.ResourceOfflineIcon.Foreground(charmtone.Coral)
1262+ s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(charmtone.Coral)
1240− s.ResourceOnlineIcon = s.ResourceOfflineIcon.Foreground(charmtone.Guac)
1263+ s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(charmtone.Guac)
1241− s.ResourceName = lipgloss.NewStyle().Foreground(charmtone.Squid)
1264+ s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
1242− s.ResourceStatus = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1265+ s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1243− s.ResourceAdditionalText = 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
1244
1271
1245 // LSP
1272 // LSP
1246− s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
1273+ s.LSP.ErrorDiagnostic = base.Foreground(redDark)
1247− s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
1274+ s.LSP.WarningDiagnostic = base.Foreground(warning)
1248− s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
1275+ s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
1249− s.LSP.InfoDiagnostic = s.Base.Foreground(info)
1276+ s.LSP.InfoDiagnostic = base.Foreground(info)
1250
1277
1251 // Files
1278 // Files
1252− s.Files.Path = s.Muted
1279+ s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
1253− s.Files.Additions = s.Base.Foreground(greenDark)
1280+ s.Files.Additions = lipgloss.NewStyle().Foreground(greenDark)
1254− s.Files.Deletions = s.Base.Foreground(redDark)
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
1255
1303
1256 // Chat
1304 // Chat
1257 messageFocussedBorder := lipgloss.Border{
1305 messageFocussedBorder := lipgloss.Border{
1258 Left: "▌",
1306 Left: "▌",
1259 }
1307 }
1260
1308
1261− s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1309+ s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1262− s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1310+ s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1263 BorderForeground(primary).BorderStyle(normalBorder)
1311 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).
1265 BorderForeground(primary).BorderStyle(messageFocussedBorder)
1313 BorderForeground(primary).BorderStyle(messageFocussedBorder)
1266− s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
1314+ s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
1267− s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
1315+ s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1268 BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1316 BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1269− s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
1317+ s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
1270− s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1318+ s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1271 Background(red).Foreground(white)
1319 Background(red).Foreground(white)
1272− s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1320+ s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1273− s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1321+ s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1274
1322
1275 // Message item styles
1323 // Message item styles
1276− s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
1324+ s.Messages.ToolCallFocused = muted.PaddingLeft(1).
1277 BorderStyle(messageFocussedBorder).
1325 BorderStyle(messageFocussedBorder).
1278 BorderLeft(true).
1326 BorderLeft(true).
1279 BorderForeground(greenDark)
1327 BorderForeground(greenDark)
1280− s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
1328+ s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
1281 // No padding or border for compact tool calls within messages
1329 // No padding or border for compact tool calls within messages
1282− s.Chat.Message.ToolCallCompact = s.Muted
1330+ s.Messages.ToolCallCompact = muted
1283− s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
1331+ s.Messages.SectionHeader = base.PaddingLeft(2)
1284− s.Chat.Message.AssistantInfoIcon = s.Subtle
1332+ s.Messages.AssistantInfoIcon = subtle
1285− s.Chat.Message.AssistantInfoModel = s.Muted
1333+ s.Messages.AssistantInfoModel = muted
1286− s.Chat.Message.AssistantInfoProvider = s.Subtle
1334+ s.Messages.AssistantInfoProvider = subtle
1287− s.Chat.Message.AssistantInfoDuration = s.Subtle
1335+ s.Messages.AssistantInfoDuration = subtle
1336+ s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
1288
1337
1289 // Thinking section styles
1338 // Thinking section styles
1290− s.Chat.Message.ThinkingBox = s.Subtle.Background(bgBaseLighter)
1339+ s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
1291− s.Chat.Message.ThinkingTruncationHint = s.Muted
1340+ s.Messages.ThinkingTruncationHint = muted
1292− s.Chat.Message.ThinkingFooterTitle = s.Muted
1341+ s.Messages.ThinkingFooterTitle = muted
1293− s.Chat.Message.ThinkingFooterDuration = s.Subtle
1342+ s.Messages.ThinkingFooterDuration = subtle
1294
1343
1295 // Text selection.
1344 // Text selection.
1296 s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
1345 s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
@@ -1300,6 +1349,25 @@ func DefaultStyles() Styles {
1300 s.Dialog.TitleText = base.Foreground(primary)
1349 s.Dialog.TitleText = base.Foreground(primary)
1301 s.Dialog.TitleError = base.Foreground(red)
1350 s.Dialog.TitleError = base.Foreground(red)
1302 s.Dialog.TitleAccent = base.Foreground(green).Bold(true)
1351 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)
1303 s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1371 s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1304 s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
1372 s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
1305 s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
1373 s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
@@ -1323,6 +1391,20 @@ func DefaultStyles() Styles {
1323
1391
1324 s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
1392 s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
1325
1393
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+
1326 s.Dialog.Arguments.Content = base.Padding(1)
1408 s.Dialog.Arguments.Content = base.Padding(1)
1327 s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
1409 s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
1328 s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
1410 s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
@@ -1332,20 +1414,22 @@ func DefaultStyles() Styles {
1332
1414
1333 s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(red)
1415 s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(red)
1334 s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(red)
1416 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)
1336 s.Dialog.Sessions.DeletingTitleGradientFromColor = red
1418 s.Dialog.Sessions.DeletingTitleGradientFromColor = red
1337− s.Dialog.Sessions.DeletingTitleGradientToColor = s.Primary
1419+ s.Dialog.Sessions.DeletingTitleGradientToColor = primary
1338 s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1420 s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1339 s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(red).Foreground(charmtone.Butter)
1421 s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(red).Foreground(charmtone.Butter)
1340
1422
1341 s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(charmtone.Zest)
1423 s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(charmtone.Zest)
1342 s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(charmtone.Zest)
1424 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)
1344 s.Dialog.Sessions.RenamingTitleGradientFromColor = charmtone.Zest
1426 s.Dialog.Sessions.RenamingTitleGradientFromColor = charmtone.Zest
1345 s.Dialog.Sessions.RenamingTitleGradientToColor = charmtone.Bok
1427 s.Dialog.Sessions.RenamingTitleGradientToColor = charmtone.Bok
1346 s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1428 s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1347 s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
1429 s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
1348 s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(charmtone.Squid)
1430 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)
1349
1433
1350 s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
1434 s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
1351 s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
1435 s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
@@ -1375,11 +1459,19 @@ func DefaultStyles() Styles {
1375 s.Pills.Base = base.Padding(0, 1)
1459 s.Pills.Base = base.Padding(0, 1)
1376 s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
1460 s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
1377 s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
1461 s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
1378− s.Pills.QueueItemPrefix = s.Muted.SetString(" •")
1462+ s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString(" •")
1379− s.Pills.HelpKey = s.Muted
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)
1380− s.Pills.HelpText = s.Subtle
1473+ s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
1381 s.Pills.Area = base
1474 s.Pills.Area = base
1382− s.Pills.TodoSpinner = base.Foreground(greenDark)
1383
1475
1384 return s
1476 return s
1385}
1477}