on main help sign in
cleanup nested tool rendering
a23e1a3Kujtim Hoxha
internal/tui/components/chat/list.go+10−4
@@ -46,13 +46,19 @@ type messageListCmp struct {
46// and reverse ordering (newest messages at bottom).
46// and reverse ordering (newest messages at bottom).
47func NewMessagesListCmp(app *app.App) MessageListCmp {
47func NewMessagesListCmp(app *app.App) MessageListCmp {
48 defaultKeymaps := list.DefaultKeymap()
48 defaultKeymaps := list.DefaultKeymap()
49− defaultKeymaps.NDown.SetEnabled(false)
49+ defaultKeymaps.Up.SetEnabled(false)
50+ defaultKeymaps.Down.SetEnabled(false)
51+ defaultKeymaps.NDown = key.NewBinding(
52+ key.WithKeys("ctrl+j"),
53+ )
50− defaultKeymaps.NUp.SetEnabled(false)
54+ defaultKeymaps.NUp = key.NewBinding(
55+ key.WithKeys("ctrl+k"),
56+ )
51 defaultKeymaps.Home = key.NewBinding(
57 defaultKeymaps.Home = key.NewBinding(
52− key.WithKeys("ctrl+g"),
58+ key.WithKeys("ctrl+shift+up"),
53 )
59 )
54 defaultKeymaps.End = key.NewBinding(
60 defaultKeymaps.End = key.NewBinding(
55− key.WithKeys("ctrl+G"),
61+ key.WithKeys("ctrl+shift+down"),
56 )
62 )
57 return &messageListCmp{
63 return &messageListCmp{
58 app: app,
64 app: app,
internal/tui/components/chat/messages/renderer.go+8−3
@@ -7,6 +7,7 @@ import (
7 "time"
7 "time"
8
8
9 "github.com/charmbracelet/lipgloss/v2"
9 "github.com/charmbracelet/lipgloss/v2"
10+ "github.com/charmbracelet/lipgloss/v2/tree"
10 "github.com/charmbracelet/x/ansi"
11 "github.com/charmbracelet/x/ansi"
11 "github.com/opencode-ai/opencode/internal/config"
12 "github.com/opencode-ai/opencode/internal/config"
12 "github.com/opencode-ai/opencode/internal/diff"
13 "github.com/opencode-ai/opencode/internal/diff"
@@ -93,7 +94,7 @@ func (pb *paramBuilder) build() []string {
93func (br baseRenderer) renderWithParams(v *toolCallCmp, toolName string, args []string, contentRenderer func() string) string {
94func (br baseRenderer) renderWithParams(v *toolCallCmp, toolName string, args []string, contentRenderer func() string) string {
94 width := v.textWidth()
95 width := v.textWidth()
95 if v.isNested {
96 if v.isNested {
96− width -= 3 // Adjust for nested tool call indentation
97+ width -= 4 // Adjust for nested tool call indentation
97 }
98 }
98 header := makeHeader(toolName, width, args...)
99 header := makeHeader(toolName, width, args...)
99 if v.isNested {
100 if v.isNested {
@@ -495,11 +496,15 @@ func (tr agentRenderer) Render(v *toolCallCmp) string {
495 args := newParamBuilder().addMain(prompt).build()
496 args := newParamBuilder().addMain(prompt).build()
496
497
497 header := makeHeader("Task", v.textWidth(), args...)
498 header := makeHeader("Task", v.textWidth(), args...)
498− parts := []string{header}
499+ t := tree.Root(header)
500+
499 for _, call := range v.nestedToolCalls {
501 for _, call := range v.nestedToolCalls {
500− parts = append(parts, call.View())
502+ t.Child(call.View())
501 }
503 }
502
504
505+ parts := []string{
506+ t.Enumerator(tree.RoundedEnumerator).String(),
507+ }
503 if v.result.ToolCallID == "" {
508 if v.result.ToolCallID == "" {
504 v.spinning = true
509 v.spinning = true
505 parts = append(parts, v.anim.View())
510 parts = append(parts, v.anim.View())
internal/tui/components/chat/messages/tool.go+4−9
@@ -138,18 +138,16 @@ func (m *toolCallCmp) View() string {
138 box := m.style()
138 box := m.style()
139
139
140 if !m.call.Finished && !m.cancelled {
140 if !m.call.Finished && !m.cancelled {
141+ if m.isNested {
142+ return box.Render(m.renderPending())
143+ }
141 return box.PaddingLeft(1).Render(m.renderPending())
144 return box.PaddingLeft(1).Render(m.renderPending())
142 }
145 }
143
146
144 r := registry.lookup(m.call.Name)
147 r := registry.lookup(m.call.Name)
145
148
146 if m.isNested {
149 if m.isNested {
147− return box.Render(
150+ return box.Render(r.Render(m))
148− lipgloss.JoinHorizontal(lipgloss.Left,
149− " └ ",
150− r.Render(m),
151− ),
152− )
153 }
151 }
154 return box.PaddingLeft(1).Render(r.Render(m))
152 return box.PaddingLeft(1).Render(r.Render(m))
155}
153}
@@ -212,9 +210,6 @@ func (m *toolCallCmp) SetIsNested(isNested bool) {
212
210
213// renderPending displays the tool name with a loading animation for pending tool calls
211// renderPending displays the tool name with a loading animation for pending tool calls
214func (m *toolCallCmp) renderPending() string {
212func (m *toolCallCmp) renderPending() string {
215− if m.isNested {
216− return fmt.Sprintf("└ %s: %s", prettifyToolName(m.call.Name), m.anim.View())
217− }
218 return fmt.Sprintf("%s: %s", prettifyToolName(m.call.Name), m.anim.View())
213 return fmt.Sprintf("%s: %s", prettifyToolName(m.call.Name), m.anim.View())
219}
214}
220
215
internal/tui/components/core/list/keys.go+16−13
@@ -1,6 +1,9 @@
1package list
1package list
2
2
3+import (
3−import "github.com/charmbracelet/bubbles/v2/key"
4+ "github.com/charmbracelet/bubbles/v2/key"
5+ "github.com/opencode-ai/opencode/internal/tui/layout"
6+)
4
7
5type KeyMap struct {
8type KeyMap struct {
6 Down,
9 Down,
@@ -12,8 +15,7 @@ type KeyMap struct {
12 HalfPageDown,
15 HalfPageDown,
13 HalfPageUp,
16 HalfPageUp,
14 Home,
17 Home,
15− End,
16− Submit key.Binding
18+ End key.Binding
17}
19}
18
20
19func DefaultKeymap() KeyMap {
21func DefaultKeymap() KeyMap {
@@ -48,23 +50,24 @@ func DefaultKeymap() KeyMap {
48 End: key.NewBinding(
50 End: key.NewBinding(
49 key.WithKeys("shift+g", "end"),
51 key.WithKeys("shift+g", "end"),
50 ),
52 ),
51− Submit: key.NewBinding(
52− key.WithKeys("enter", "space"),
53− key.WithHelp("enter/space", "select"),
54− ),
55 }
53 }
56}
54}
57
55
58// FullHelp implements help.KeyMap.
56// FullHelp implements help.KeyMap.
59−func (k KeyMap) FullHelp() [][]key.Binding { return nil }
57+func (k KeyMap) FullHelp() [][]key.Binding {
58+ m := [][]key.Binding{}
59+ slice := layout.KeyMapToSlice(k)
60+ for i := 0; i < len(slice); i += 4 {
61+ end := min(i+4, len(slice))
62+ m = append(m, slice[i:end])
63+ }
64+ return m
65+}
60
66
61// ShortHelp implements help.KeyMap.
67// ShortHelp implements help.KeyMap.
62func (k KeyMap) ShortHelp() []key.Binding {
68func (k KeyMap) ShortHelp() []key.Binding {
63 return []key.Binding{
69 return []key.Binding{
64− key.NewBinding(
70+ k.Up,
65− key.WithKeys("up", "down"),
71+ k.Down,
66− key.WithHelp("↓↑", "navigate"),
67− ),
68− k.Submit,
69 }
72 }
70}
73}