diff options
| author | adamdottv <[email protected]> | 2025-06-17 07:09:04 -0500 |
|---|---|---|
| committer | adamdottv <[email protected]> | 2025-06-17 07:09:04 -0500 |
| commit | a5da5127faffacd7703fc0dde061ef1f490d3dce (patch) | |
| tree | ab9c65d0eb45ab9fdc326187ca5881dc9b10936e /packages/tui/internal/components/status | |
| parent | b5a4439704c70a17d661f1984bb030d5325d141a (diff) | |
| download | opencode-a5da5127faffacd7703fc0dde061ef1f490d3dce.tar.gz opencode-a5da5127faffacd7703fc0dde061ef1f490d3dce.zip | |
chore: consolidate chat page into tui.go
Diffstat (limited to 'packages/tui/internal/components/status')
| -rw-r--r-- | packages/tui/internal/components/status/status.go | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/packages/tui/internal/components/status/status.go b/packages/tui/internal/components/status/status.go new file mode 100644 index 000000000..01a2659c6 --- /dev/null +++ b/packages/tui/internal/components/status/status.go @@ -0,0 +1,140 @@ +package status + +import ( + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea/v2" + "github.com/charmbracelet/lipgloss/v2" + "github.com/sst/opencode/internal/app" + "github.com/sst/opencode/internal/styles" + "github.com/sst/opencode/internal/theme" +) + +type StatusComponent interface { + tea.Model + tea.ViewModel +} + +type statusComponent struct { + app *app.App + width int +} + +func (m statusComponent) Init() tea.Cmd { + return nil +} + +func (m statusComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.width = msg.Width + return m, nil + } + return m, nil +} + +func (m statusComponent) logo() string { + t := theme.CurrentTheme() + base := lipgloss.NewStyle().Background(t.BackgroundElement()).Foreground(t.TextMuted()).Render + emphasis := lipgloss.NewStyle().Bold(true).Background(t.BackgroundElement()).Foreground(t.Text()).Render + + open := base("open") + code := emphasis("code ") + version := base(m.app.Version) + return styles.Padded(). + Background(t.BackgroundElement()). + Render(open + code + version) +} + +func formatTokensAndCost(tokens float32, contextWindow float32, cost float32) string { + // Format tokens in human-readable format (e.g., 110K, 1.2M) + var formattedTokens string + switch { + case tokens >= 1_000_000: + formattedTokens = fmt.Sprintf("%.1fM", float64(tokens)/1_000_000) + case tokens >= 1_000: + formattedTokens = fmt.Sprintf("%.1fK", float64(tokens)/1_000) + default: + formattedTokens = fmt.Sprintf("%d", int(tokens)) + } + + // Remove .0 suffix if present + if strings.HasSuffix(formattedTokens, ".0K") { + formattedTokens = strings.Replace(formattedTokens, ".0K", "K", 1) + } + if strings.HasSuffix(formattedTokens, ".0M") { + formattedTokens = strings.Replace(formattedTokens, ".0M", "M", 1) + } + + // Format cost with $ symbol and 2 decimal places + formattedCost := fmt.Sprintf("$%.2f", cost) + percentage := (float64(tokens) / float64(contextWindow)) * 100 + + return fmt.Sprintf("Tokens: %s (%d%%), Cost: %s", formattedTokens, int(percentage), formattedCost) +} + +func (m statusComponent) View() string { + t := theme.CurrentTheme() + if m.app.Session.Id == "" { + return styles.BaseStyle(). + Background(t.Background()). + Width(m.width). + Height(2). + Render("") + } + + logo := m.logo() + + cwd := styles.Padded(). + Foreground(t.TextMuted()). + Background(t.BackgroundSubtle()). + Render(m.app.Info.Path.Cwd) + + sessionInfo := "" + if m.app.Session.Id != "" { + tokens := float32(0) + cost := float32(0) + contextWindow := m.app.Model.Limit.Context + + for _, message := range m.app.Messages { + if message.Metadata.Assistant != nil { + cost += message.Metadata.Assistant.Cost + usage := message.Metadata.Assistant.Tokens + if usage.Output > 0 { + tokens = (usage.Input + + usage.Cache.Write + + usage.Cache.Read + + usage.Output + + usage.Reasoning) + } + } + } + + sessionInfo = styles.Padded(). + Background(t.BackgroundElement()). + Foreground(t.TextMuted()). + Render(formatTokensAndCost(tokens, contextWindow, cost)) + } + + // diagnostics := styles.Padded().Background(t.BackgroundElement()).Render(m.projectDiagnostics()) + + space := max( + 0, + m.width-lipgloss.Width(logo)-lipgloss.Width(cwd)-lipgloss.Width(sessionInfo), + ) + spacer := lipgloss.NewStyle().Background(t.BackgroundSubtle()).Width(space).Render("") + + status := logo + cwd + spacer + sessionInfo + + blank := styles.BaseStyle().Background(t.Background()).Width(m.width).Render("") + return blank + "\n" + status +} + +func NewStatusCmp(app *app.App) StatusComponent { + statusComponent := &statusComponent{ + app: app, + } + + return statusComponent +} |
