9 "charm.land/lipgloss/v2"
10 "github.com/charmbracelet/crush/internal/home"
11 "github.com/charmbracelet/crush/internal/ui/styles"
12 "github.com/charmbracelet/x/ansi"
15// PrettyPath formats a file path with home directory shortening and applies
17func PrettyPath(t *styles.Styles, path string, width int) string {
18 formatted := home.Short(path)
19 return t.Muted.Width(width).Render(formatted)
22// ModelContextInfo contains token usage and cost information for a model.
23type ModelContextInfo struct {
29// ModelInfo renders model information including name, reasoning settings, and
30// optional context usage/cost.
31func ModelInfo(t *styles.Styles, modelName string, reasoningInfo string, context *ModelContextInfo, width int) string {
32 modelIcon := t.Subtle.Render(styles.ModelIcon)
33 modelName = t.Base.Render(modelName)
34 modelInfo := fmt.Sprintf("%s %s", modelIcon, modelName)
40 if reasoningInfo != "" {
41 parts = append(parts, t.Subtle.PaddingLeft(2).Render(reasoningInfo))
45 formattedInfo := formatTokensAndCost(t, context.ContextUsed, context.ModelContext, context.Cost)
46 parts = append(parts, lipgloss.NewStyle().PaddingLeft(2).Render(formattedInfo))
49 return lipgloss.NewStyle().Width(width).Render(
50 lipgloss.JoinVertical(lipgloss.Left, parts...),
54// formatTokensAndCost formats token usage and cost with appropriate units
55// (K/M) and percentage of context window.
56func formatTokensAndCost(t *styles.Styles, tokens, contextWindow int64, cost float64) string {
57 var formattedTokens string
59 case tokens >= 1_000_000:
60 formattedTokens = fmt.Sprintf("%.1fM", float64(tokens)/1_000_000)
62 formattedTokens = fmt.Sprintf("%.1fK", float64(tokens)/1_000)
64 formattedTokens = fmt.Sprintf("%d", tokens)
67 if strings.HasSuffix(formattedTokens, ".0K") {
68 formattedTokens = strings.Replace(formattedTokens, ".0K", "K", 1)
70 if strings.HasSuffix(formattedTokens, ".0M") {
71 formattedTokens = strings.Replace(formattedTokens, ".0M", "M", 1)
74 percentage := (float64(tokens) / float64(contextWindow)) * 100
76 formattedCost := t.Muted.Render(fmt.Sprintf("$%.2f", cost))
78 formattedTokens = t.Subtle.Render(fmt.Sprintf("(%s)", formattedTokens))
79 formattedPercentage := t.Muted.Render(fmt.Sprintf("%d%%", int(percentage)))
80 formattedTokens = fmt.Sprintf("%s %s", formattedPercentage, formattedTokens)
82 formattedTokens = fmt.Sprintf("%s %s", styles.WarningIcon, formattedTokens)
85 return fmt.Sprintf("%s %s", formattedTokens, formattedCost)
88// StatusOpts defines options for rendering a status line with icon, title,
89// description, and optional extra content.
90type StatusOpts struct {
91 Icon string // if empty no icon will be shown
93 TitleColor color.Color
95 DescriptionColor color.Color
96 ExtraContent string // additional content to append after the description
99// Status renders a status line with icon, title, description, and extra
100// content. The description is truncated if it exceeds the available width.
101func Status(t *styles.Styles, opts StatusOpts, width int) string {
104 description := opts.Description
106 titleColor := cmp.Or(opts.TitleColor, t.Muted.GetForeground())
107 descriptionColor := cmp.Or(opts.DescriptionColor, t.Subtle.GetForeground())
109 title = t.Base.Foreground(titleColor).Render(title)
111 if description != "" {
112 extraContentWidth := lipgloss.Width(opts.ExtraContent)
113 if extraContentWidth > 0 {
114 extraContentWidth += 1
116 description = ansi.Truncate(description, width-lipgloss.Width(icon)-lipgloss.Width(title)-2-extraContentWidth, "…")
117 description = t.Base.Foreground(descriptionColor).Render(description)
120 content := []string{}
122 content = append(content, icon)
124 content = append(content, title)
125 if description != "" {
126 content = append(content, description)
128 if opts.ExtraContent != "" {
129 content = append(content, opts.ExtraContent)
132 return strings.Join(content, " ")
135// Section renders a section header with a title and a horizontal line filling
136// the remaining width.
137func Section(t *styles.Styles, text string, width int) string {
138 char := styles.SectionSeparator
139 length := lipgloss.Width(text) + 1
140 remainingWidth := width - length
141 text = t.Section.Title.Render(text)
142 if remainingWidth > 0 {
143 text = text + " " + t.Section.Line.Render(strings.Repeat(char, remainingWidth))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list