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