@@ -25,11 +25,12 @@ import (
2525// MessageCmp defines the interface for message components in the chat interface.
2626// It combines standard UI model interfaces with message-specific functionality.
2727type MessageCmp interface {
28− util.Model // Basic Bubble Tea model interface
29− layout.Sizeable // Width/height management
30− layout.Focusable // Focus state management
31− GetMessage() message.Message // Access to underlying message data
32− Spinning() bool // Animation state for loading messages
28+ util.Model // Basic Bubble Tea model interface
29+ layout.Sizeable // Width/height management
30+ layout.Focusable // Focus state management
31+ GetMessage() message.Message // Access to underlying message data
32+ SetMessage(msg message.Message) // Update the message content
33+ Spinning() bool // Animation state for loading messages
3334}
3435
3536// messageCmp implements the MessageCmp interface for displaying chat messages.
@@ -42,7 +43,7 @@ type messageCmp struct {
4243 // Core message data and state
4344 message message.Message // The underlying message content
4445 spinning bool // Whether to show loading animation
45− anim util.Model // Animation component for loading states
46+ anim anim.Anim // Animation component for loading states
4647
4748 // Thinking viewport for displaying reasoning content
4849 thinkingViewport viewport.Model
@@ -88,7 +89,7 @@ func (m *messageCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
8889 m.spinning = m.shouldSpin()
8990 if m.spinning {
9091 u, cmd := m.anim.Update(msg)
91− m.anim = u.(util.Model)
92+ m.anim = u.(anim.Anim)
9293 return m, cmd
9394 }
9495 }
@@ -98,7 +99,7 @@ func (m *messageCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
9899// View renders the message component based on its current state.
99100// Returns different views for spinning, user, and assistant messages.
100101func (m *messageCmp) View() string {
101− if m.spinning {
102+ if m.spinning && m.message.ReasoningContent().Thinking == "" {
102103 return m.style().PaddingLeft(1).Render(m.anim.View())
103104 }
104105 if m.message.ID != "" {
@@ -118,6 +119,10 @@ func (m *messageCmp) GetMessage() message.Message {
118119 return m.message
119120}
120121
122+func (m *messageCmp) SetMessage(msg message.Message) {
123+ m.message = msg
124+}
125+
121126// textWidth calculates the available width for text content,
122127// accounting for borders and padding
123128func (m *messageCmp) textWidth() int {
@@ -158,6 +163,7 @@ func (m *messageCmp) renderAssistantMessage() string {
158163 thinkingContent := ""
159164
160165 if thinking || m.message.ReasoningContent().Thinking != "" {
166+ m.anim.SetLabel("Thinking")
161167 thinkingContent = m.renderThinkingContent()
162168 } else if finished && content == "" && finishedData.Reason == message.FinishReasonEndTurn {
163169 content = ""
@@ -230,7 +236,7 @@ func (m *messageCmp) renderThinkingContent() string {
230236 }
231237 lines := strings.Split(reasoningContent.Thinking, "\n")
232238 var content strings.Builder
233− lineStyle := t.S().Muted.Background(t.BgBaseLighter)
239+ lineStyle := t.S().Subtle.Background(t.BgBaseLighter)
234240 for _, line := range lines {
235241 if line == "" {
236242 continue
@@ -246,15 +252,18 @@ func (m *messageCmp) renderThinkingContent() string {
246252 var footer string
247253 if reasoningContent.StartedAt > 0 {
248254 duration := m.message.ThinkingDuration()
249− opts := core.StatusOpts{
250− Title: "Thinking...",
251− Description: duration.String(),
252− }
253255 if reasoningContent.FinishedAt > 0 {
254− opts.NoIcon = true
255− opts.Title = "Thought for"
256+ m.anim.SetLabel("")
257+ opts := core.StatusOpts{
258+ Title: "Thought for",
259+ Description: duration.String(),
260+ NoIcon: true,
261+ }
262+ footer = t.S().Base.PaddingLeft(1).Render(core.Status(opts, m.textWidth()-1))
263+ } else {
264+ footer = m.anim.View()
256265 }
257− footer = t.S().Base.PaddingLeft(1).Render(core.Status(opts, m.textWidth()-1))
266+
258267 }
259268 return lineStyle.Width(m.textWidth()).Padding(0, 1).Render(m.thinkingViewport.View()) + "\n\n" + footer
260269}
@@ -273,11 +282,12 @@ func (m *messageCmp) shouldSpin() bool {
273282 if m.message.Content().Text != "" {
274283 return false
275284 }
285+ if len(m.message.ToolCalls()) > 0 {
286+ return false
287+ }
276288 return true
277289}
278290
279−// Focus management methods
280−
281291// Blur removes focus from the message component
282292func (m *messageCmp) Blur() tea.Cmd {
283293 m.focused = false