@@ -1,11 +1,16 @@
1package sidebar
1package sidebar
7 tea "github.com/charmbracelet/bubbletea/v2"
8 tea "github.com/charmbracelet/bubbletea/v2"
8 "github.com/charmbracelet/crush/internal/config"
9 "github.com/charmbracelet/crush/internal/config"
10+ "github.com/charmbracelet/crush/internal/llm/models"
11+ "github.com/charmbracelet/crush/internal/logging"
12+ "github.com/charmbracelet/crush/internal/lsp"
13+ "github.com/charmbracelet/crush/internal/lsp/protocol"
9 "github.com/charmbracelet/crush/internal/pubsub"
14 "github.com/charmbracelet/crush/internal/pubsub"
10 "github.com/charmbracelet/crush/internal/session"
15 "github.com/charmbracelet/crush/internal/session"
11 "github.com/charmbracelet/crush/internal/tui/components/chat"
16 "github.com/charmbracelet/crush/internal/tui/components/chat"
@@ -32,10 +37,13 @@ type sidebarCmp struct {
32 session session.Session
37 session session.Session
33 logo string
38 logo string
34 cwd string
39 cwd string
40+ lspClients map[string]*lsp.Client
37−func NewSidebarCmp() Sidebar {
43+func NewSidebarCmp(lspClients map[string]*lsp.Client) Sidebar {
38− return &sidebarCmp{}
44+ return &sidebarCmp{
45+ lspClients: lspClients,
41func (m *sidebarCmp) Init() tea.Cmd {
49func (m *sidebarCmp) Init() tea.Cmd {
@@ -75,6 +83,8 @@ func (m *sidebarCmp) View() tea.View {
75 parts = append(parts,
83 parts = append(parts,
86+ m.currentModelBlock(),
78 m.lspBlock(),
88 m.lspBlock(),
80 m.mcpBlock(),
90 m.mcpBlock(),
@@ -137,12 +147,44 @@ func (m *sidebarCmp) lspBlock() string {
137 if l.Disabled {
147 if l.Disabled {
138 iconColor = t.FgMuted
148 iconColor = t.FgMuted
150+ lspErrs := map[protocol.DiagnosticSeverity]int{
151+ protocol.SeverityError: 0,
152+ protocol.SeverityWarning: 0,
153+ protocol.SeverityHint: 0,
154+ protocol.SeverityInformation: 0,
156+ if client, ok := m.lspClients[n]; ok {
157+ for _, diagnostics := range client.GetDiagnostics() {
158+ for _, diagnostic := range diagnostics {
159+ if severity, ok := lspErrs[diagnostic.Severity]; ok {
160+ lspErrs[diagnostic.Severity] = severity + 1
167+ if lspErrs[protocol.SeverityError] > 0 {
168+ errs = append(errs, t.S().Base.Foreground(t.Error).Render(fmt.Sprintf("%s%d", styles.ErrorIcon, lspErrs[protocol.SeverityError])))
170+ if lspErrs[protocol.SeverityWarning] > 0 {
171+ errs = append(errs, t.S().Base.Foreground(t.Warning).Render(fmt.Sprintf("%s%d", styles.WarningIcon, lspErrs[protocol.SeverityWarning])))
173+ if lspErrs[protocol.SeverityHint] > 0 {
174+ errs = append(errs, t.S().Base.Foreground(t.FgHalfMuted).Render(fmt.Sprintf("%s%d", styles.HintIcon, lspErrs[protocol.SeverityHint])))
176+ if lspErrs[protocol.SeverityInformation] > 0 {
177+ errs = append(errs, t.S().Base.Foreground(t.FgHalfMuted).Render(fmt.Sprintf("%s%d", styles.InfoIcon, lspErrs[protocol.SeverityInformation])))
180+ logging.Info("LSP Errors", "errors", errs)
140 lspList = append(lspList,
181 lspList = append(lspList,
141 core.Status(
182 core.Status(
142 core.StatusOpts{
183 core.StatusOpts{
143− IconColor: iconColor,
184+ IconColor: iconColor,
144− Title: n,
185+ Title: n,
145− Description: l.Command,
186+ Description: l.Command,
187+ ExtraContent: strings.Join(errs, " "),
@@ -195,6 +237,76 @@ func (m *sidebarCmp) mcpBlock() string {
240+func formatTokensAndCost(tokens, contextWindow int64, cost float64) string {
241+ t := styles.CurrentTheme()
242+ // Format tokens in human-readable format (e.g., 110K, 1.2M)
243+ var formattedTokens string
245+ case tokens >= 1_000_000:
246+ formattedTokens = fmt.Sprintf("%.1fM", float64(tokens)/1_000_000)
247+ case tokens >= 1_000:
248+ formattedTokens = fmt.Sprintf("%.1fK", float64(tokens)/1_000)
250+ formattedTokens = fmt.Sprintf("%d", tokens)
253+ // Remove .0 suffix if present
254+ if strings.HasSuffix(formattedTokens, ".0K") {
255+ formattedTokens = strings.Replace(formattedTokens, ".0K", "K", 1)
257+ if strings.HasSuffix(formattedTokens, ".0M") {
258+ formattedTokens = strings.Replace(formattedTokens, ".0M", "M", 1)
261+ percentage := (float64(tokens) / float64(contextWindow)) * 100
263+ baseStyle := t.S().Base
265+ formattedCost := baseStyle.Foreground(t.FgMuted).Render(fmt.Sprintf("$%.2f", cost))
267+ formattedTokens = baseStyle.Foreground(t.FgMuted).Render(fmt.Sprintf("(%s)", formattedTokens))
268+ formattedPercentage := baseStyle.Foreground(t.FgSubtle).Render(fmt.Sprintf("%d%%", int(percentage)))
269+ formattedTokens = fmt.Sprintf("%s %s", formattedPercentage, formattedTokens)
270+ if percentage > 80 {
271+ // add the warning icon
272+ formattedTokens = fmt.Sprintf("%s %s", styles.WarningIcon, formattedTokens)
275+ return fmt.Sprintf("%s %s", formattedTokens, formattedCost)
278+func (s *sidebarCmp) currentModelBlock() string {
280+ agentCfg := cfg.Agents[config.AgentCoder]
281+ selectedModelID := agentCfg.Model
282+ model := models.SupportedModels[selectedModelID]
284+ t := styles.CurrentTheme()
286+ modelIcon := t.S().Base.Foreground(t.FgSubtle).Render(styles.ModelIcon)
287+ modelName := t.S().Text.Render(model.Name)
288+ modelInfo := fmt.Sprintf("%s %s", modelIcon, modelName)
294+ if s.session.ID != "" {
297+ " "+formatTokensAndCost(
298+ s.session.CompletionTokens+s.session.PromptTokens,
299+ model.ContextWindow,
304+ return lipgloss.JoinVertical(
198func cwd() string {
310func cwd() string {
199 cwd := config.WorkingDirectory()
311 cwd := config.WorkingDirectory()
200 t := styles.CurrentTheme()
312 t := styles.CurrentTheme()