7 "charm.land/lipgloss/v2"
8 "github.com/charmbracelet/catwalk/pkg/catwalk"
9 "github.com/charmbracelet/crush/internal/ui/common"
10 "github.com/charmbracelet/crush/internal/ui/logo"
11 uv "github.com/charmbracelet/ultraviolet"
12 "golang.org/x/text/cases"
13 "golang.org/x/text/language"
16// modelInfo renders the current model information including reasoning
17// settings and context usage/cost for the sidebar.
18func (m *UI) modelInfo(width int) string {
19 model := m.selectedLargeModel()
21 if model != nil && model.CatwalkCfg.CanReason {
22 providerConfig, ok := m.com.Config().Providers.Get(model.ModelCfg.Provider)
24 switch providerConfig.Type {
25 case catwalk.TypeAnthropic:
26 if model.ModelCfg.Think {
27 reasoningInfo = "Thinking On"
29 reasoningInfo = "Thinking Off"
32 formatter := cases.Title(language.English, cases.NoLower)
33 reasoningEffort := cmp.Or(model.ModelCfg.ReasoningEffort, model.CatwalkCfg.DefaultReasoningEffort)
34 reasoningInfo = formatter.String(fmt.Sprintf("Reasoning %s", reasoningEffort))
38 var modelContext *common.ModelContextInfo
40 modelContext = &common.ModelContextInfo{
41 ContextUsed: m.session.CompletionTokens + m.session.PromptTokens,
43 ModelContext: model.CatwalkCfg.ContextWindow,
46 return common.ModelInfo(m.com.Styles, model.CatwalkCfg.Name, reasoningInfo, modelContext, width)
49// getDynamicHeightLimits will give us the num of items to show in each section based on the hight
50// some items are more important than others.
51func getDynamicHeightLimits(availableHeight int) (maxFiles, maxLSPs, maxMCPs int) {
53 minItemsPerSection = 2
54 defaultMaxFilesShown = 10
55 defaultMaxLSPsShown = 8
56 defaultMaxMCPsShown = 8
57 minAvailableHeightLimit = 10
60 // If we have very little space, use minimum values
61 if availableHeight < minAvailableHeightLimit {
62 return minItemsPerSection, minItemsPerSection, minItemsPerSection
65 // Distribute available height among the three sections
66 // Give priority to files, then LSPs, then MCPs
68 heightPerSection := availableHeight / totalSections
70 // Calculate limits for each section, ensuring minimums
71 maxFiles = max(minItemsPerSection, min(defaultMaxFilesShown, heightPerSection))
72 maxLSPs = max(minItemsPerSection, min(defaultMaxLSPsShown, heightPerSection))
73 maxMCPs = max(minItemsPerSection, min(defaultMaxMCPsShown, heightPerSection))
75 // If we have extra space, give it to files first
76 remainingHeight := availableHeight - (maxFiles + maxLSPs + maxMCPs)
77 if remainingHeight > 0 {
78 extraForFiles := min(remainingHeight, defaultMaxFilesShown-maxFiles)
79 maxFiles += extraForFiles
80 remainingHeight -= extraForFiles
82 if remainingHeight > 0 {
83 extraForLSPs := min(remainingHeight, defaultMaxLSPsShown-maxLSPs)
84 maxLSPs += extraForLSPs
85 remainingHeight -= extraForLSPs
87 if remainingHeight > 0 {
88 maxMCPs += min(remainingHeight, defaultMaxMCPsShown-maxMCPs)
93 return maxFiles, maxLSPs, maxMCPs
96// sidebar renders the chat sidebar containing session title, working
97// directory, model info, file list, LSP status, and MCP status.
98func (m *UI) drawSidebar(scr uv.Screen, area uv.Rectangle) {
103 const logoHeightBreakpoint = 30
109 title := t.Muted.Width(width).MaxHeight(2).Render(m.session.Title)
110 cwd := common.PrettyPath(t, m.com.Config().WorkingDir(), width)
111 sidebarLogo := m.sidebarLogo
112 if height < logoHeightBreakpoint {
113 sidebarLogo = logo.SmallRender(width)
125 sidebarHeader := lipgloss.JoinVertical(
130 _, remainingHeightArea := uv.SplitVertical(m.layout.sidebar, uv.Fixed(lipgloss.Height(sidebarHeader)))
131 remainingHeight := remainingHeightArea.Dy() - 10
132 maxFiles, maxLSPs, maxMCPs := getDynamicHeightLimits(remainingHeight)
134 lspSection := m.lspInfo(width, maxLSPs, true)
135 mcpSection := m.mcpInfo(width, maxMCPs, true)
136 filesSection := m.filesInfo(m.com.Config().WorkingDir(), width, maxFiles)
143 lipgloss.JoinVertical(
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