on main help sign in
feat(chat): expandable thinking for assistant
4316ef3Kujtim Hoxha
internal/ui/chat/assistant.go+19
@@ -230,3 +230,22 @@ func (a *AssistantMessageItem) SetMessage(message *message.Message) tea.Cmd {
230 }
230 }
231 return nil
231 return nil
232}
232}
233+
234+// ToggleExpanded toggles the expanded state of the thinking box.
235+func (a *AssistantMessageItem) ToggleExpanded() {
236+ a.thinkingExpanded = !a.thinkingExpanded
237+ a.clearCache()
238+}
239+
240+// HandleMouseClick implements MouseClickable.
241+func (a *AssistantMessageItem) HandleMouseClick(btn ansi.MouseButton, x, y int) bool {
242+ if btn != ansi.MouseLeft {
243+ return false
244+ }
245+ // check if the click is within the thinking box
246+ if a.thinkingBoxHeight > 0 && y < a.thinkingBoxHeight {
247+ a.ToggleExpanded()
248+ return true
249+ }
250+ return false
251+}
internal/ui/chat/messages.go+6
@@ -26,11 +26,17 @@ type Identifiable interface {
26 ID() string
26 ID() string
27}
27}
28
28
29+// Animatable is an interface for items that support animation.
29type Animatable interface {
30type Animatable interface {
30 StartAnimation() tea.Cmd
31 StartAnimation() tea.Cmd
31 Animate(msg anim.StepMsg) tea.Cmd
32 Animate(msg anim.StepMsg) tea.Cmd
32}
33}
33
34
35+// Expandable is an interface for items that can be expanded or collapsed.
36+type Expandable interface {
37+ ToggleExpanded()
38+}
39+
34// MessageItem represents a [message.Message] item that can be displayed in the
40// MessageItem represents a [message.Message] item that can be displayed in the
35// UI and be part of a [list.List] identifiable by a unique ID.
41// UI and be part of a [list.List] identifiable by a unique ID.
36type MessageItem interface {
42type MessageItem interface {
internal/ui/model/chat.go+7
@@ -250,6 +250,13 @@ func (m *Chat) GetMessageItem(id string) chat.MessageItem {
250 return item
250 return item
251}
251}
252
252
253+// ToggleExpandedSelectedItem expands the selected message item if it is expandable.
254+func (m *Chat) ToggleExpandedSelectedItem() {
255+ if expandable, ok := m.list.SelectedItem().(chat.Expandable); ok {
256+ expandable.ToggleExpanded()
257+ }
258+}
259+
253// HandleMouseDown handles mouse down events for the chat component.
260// HandleMouseDown handles mouse down events for the chat component.
254func (m *Chat) HandleMouseDown(x, y int) bool {
261func (m *Chat) HandleMouseDown(x, y int) bool {
255 if m.list.Len() == 0 {
262 if m.list.Len() == 0 {
internal/ui/model/keys.go+5−1
@@ -37,6 +37,7 @@ type KeyMap struct {
37 End key.Binding
37 End key.Binding
38 Copy key.Binding
38 Copy key.Binding
39 ClearHighlight key.Binding
39 ClearHighlight key.Binding
40+ Expand key.Binding
40 }
41 }
41
42
42 Initialize struct {
43 Initialize struct {
@@ -205,7 +206,10 @@ func DefaultKeyMap() KeyMap {
205 key.WithKeys("esc", "alt+esc"),
206 key.WithKeys("esc", "alt+esc"),
206 key.WithHelp("esc", "clear selection"),
207 key.WithHelp("esc", "clear selection"),
207 )
208 )
208−
209+ km.Chat.Expand = key.NewBinding(
210+ key.WithKeys("space"),
211+ key.WithHelp("space", "expand/collapse"),
212+ )
209 km.Initialize.Yes = key.NewBinding(
213 km.Initialize.Yes = key.NewBinding(
210 key.WithKeys("y", "Y"),
214 key.WithKeys("y", "Y"),
211 key.WithHelp("y", "yes"),
215 key.WithHelp("y", "yes"),
internal/ui/model/ui.go+2
@@ -577,6 +577,8 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
577 m.focus = uiFocusEditor
577 m.focus = uiFocusEditor
578 cmds = append(cmds, m.textarea.Focus())
578 cmds = append(cmds, m.textarea.Focus())
579 m.chat.Blur()
579 m.chat.Blur()
580+ case key.Matches(msg, m.keyMap.Chat.Expand):
581+ m.chat.ToggleExpandedSelectedItem()
580 case key.Matches(msg, m.keyMap.Chat.Up):
582 case key.Matches(msg, m.keyMap.Chat.Up):
581 if cmd := m.chat.ScrollBy(-1); cmd != nil {
583 if cmd := m.chat.ScrollBy(-1); cmd != nil {
582 cmds = append(cmds, cmd)
584 cmds = append(cmds, cmd)