on main help sign in
Merge pull request #219 from charmbracelet/crush-thinking

chore: add thinking/reasoning indicator

commitf664df60bcc07ae4055f2b3a0536fb6a72dc83f3 authorKujtim Hoxha <kujtimii.h@gmail.com> dateThu 17 Jul 2025, 00:25 (1 year ago) parentfe6c788596128a
2 files changed+31−1
internal/tui/components/chat/messages/messages.go+3 internal/tui/components/chat/sidebar/sidebar.go+28−1
internal/tui/components/chat/messages/messages.go+3
@@ -252,6 +252,7 @@ func (m *messageCmp) renderThinkingContent() string {
252252 m.thinkingViewport.SetWidth(m.textWidth())
253253 m.thinkingViewport.SetContent(fullContent)
254254 m.thinkingViewport.GotoBottom()
255+ finishReason := m.message.FinishPart()
255256 var footer string
256257 if reasoningContent.StartedAt > 0 {
257258 duration := m.message.ThinkingDuration()
@@ -263,6 +264,8 @@ func (m *messageCmp) renderThinkingContent() string {
263264 NoIcon: true,
264265 }
265266 footer = t.S().Base.PaddingLeft(1).Render(core.Status(opts, m.textWidth()-1))
267+ } else if finishReason != nil && finishReason.Reason == message.FinishReasonCanceled {
268+ footer = t.S().Base.PaddingLeft(1).Render(m.toMarkdown("*Canceled*"))
266269 } else {
267270 footer = m.anim.View()
268271 }
internal/tui/components/chat/sidebar/sidebar.go+28−1
@@ -12,6 +12,7 @@ import (
1212 "github.com/charmbracelet/crush/internal/config"
1313 "github.com/charmbracelet/crush/internal/diff"
1414 "github.com/charmbracelet/crush/internal/fsext"
15+ "github.com/charmbracelet/crush/internal/fur/provider"
1516 "github.com/charmbracelet/crush/internal/history"
1617 "github.com/charmbracelet/crush/internal/lsp"
1718 "github.com/charmbracelet/crush/internal/lsp/protocol"
@@ -26,6 +27,8 @@ import (
2627 "github.com/charmbracelet/crush/internal/version"
2728 "github.com/charmbracelet/lipgloss/v2"
2829 "github.com/charmbracelet/x/ansi"
30+ "golang.org/x/text/cases"
31+ "golang.org/x/text/language"
2932)
3033
3134type FileHistory struct {
@@ -880,8 +883,13 @@ func formatTokensAndCost(tokens, contextWindow int64, cost float64) string {
880883}
881884
882885func (s *sidebarCmp) currentModelBlock() string {
883− agentCfg := config.Get().Agents["coder"]
886+ cfg := config.Get()
887+ agentCfg := cfg.Agents["coder"]
888+
889+ selectedModel := cfg.Models[agentCfg.Model]
890+
884891 model := config.Get().GetModelByType(agentCfg.Model)
892+ modelProvider := config.Get().GetProviderForModel(agentCfg.Model)
885893
886894 t := styles.CurrentTheme()
887895
@@ -891,6 +899,25 @@ func (s *sidebarCmp) currentModelBlock() string {
891899 parts := []string{
892900 modelInfo,
893901 }
902+ if model.CanReason {
903+ reasoningInfoStyle := t.S().Subtle.PaddingLeft(2)
904+ switch modelProvider.Type {
905+ case provider.TypeOpenAI:
906+ reasoningEffort := model.DefaultReasoningEffort
907+ if selectedModel.ReasoningEffort != "" {
908+ reasoningEffort = selectedModel.ReasoningEffort
909+ }
910+ formatter := cases.Title(language.English, cases.NoLower)
911+ parts = append(parts, reasoningInfoStyle.Render(formatter.String(fmt.Sprintf("Reasoning %s", reasoningEffort))))
912+ case provider.TypeAnthropic:
913+ formatter := cases.Title(language.English, cases.NoLower)
914+ if selectedModel.Think {
915+ parts = append(parts, reasoningInfoStyle.Render(formatter.String("Thinking on")))
916+ } else {
917+ parts = append(parts, reasoningInfoStyle.Render(formatter.String("Thinking off")))
918+ }
919+ }
920+ }
894921 if s.session.ID != "" {
895922 parts = append(
896923 parts,