Skip to content

Commit ded8733

Browse files
authored
Merge pull request #2402 from dgageot/board/agent-sidebar-click-bug-report-39c63441
Fix sidebar agent click zones mapping all lines to first agent
2 parents 674aedc + 1eabf50 commit ded8733

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package sidebar
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
8+
"github.com/docker/docker-agent/pkg/runtime"
9+
"github.com/docker/docker-agent/pkg/session"
10+
"github.com/docker/docker-agent/pkg/tui/service"
11+
)
12+
13+
func TestSidebar_HandleClickType_Agent(t *testing.T) {
14+
t.Parallel()
15+
16+
sess := session.New()
17+
sessionState := service.NewSessionState(sess)
18+
sessionState.SetCurrentAgentName("agent1")
19+
sb := New(sessionState)
20+
21+
m := sb.(*model)
22+
m.sessionHasContent = true
23+
m.titleGenerated = true
24+
m.sessionTitle = "Test"
25+
m.currentAgent = "agent1"
26+
m.availableAgents = []runtime.AgentDetails{
27+
{Name: "agent1", Provider: "openai", Model: "gpt-4", Description: "First agent"},
28+
{Name: "agent2", Provider: "anthropic", Model: "claude", Description: "Second agent"},
29+
}
30+
m.width = 40
31+
m.height = 50
32+
33+
// Force a render to populate agentClickZones
34+
_ = sb.View()
35+
36+
paddingLeft := m.layoutCfg.PaddingLeft
37+
38+
// Verify clicking on agent1 lines returns ClickAgent with "agent1"
39+
foundAgent1 := false
40+
foundAgent2 := false
41+
for y := range len(m.cachedLines) {
42+
result, agentName := sb.HandleClickType(paddingLeft+2, y)
43+
if result == ClickAgent {
44+
if agentName == "agent1" {
45+
foundAgent1 = true
46+
}
47+
if agentName == "agent2" {
48+
foundAgent2 = true
49+
}
50+
}
51+
}
52+
assert.True(t, foundAgent1, "should be able to click on agent1")
53+
assert.True(t, foundAgent2, "should be able to click on agent2")
54+
}

pkg/tui/components/sidebar/sidebar.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"charm.land/bubbles/v2/textinput"
1414
tea "charm.land/bubbletea/v2"
1515
"charm.land/lipgloss/v2"
16+
"github.com/charmbracelet/x/ansi"
1617

1718
"github.com/docker/docker-agent/pkg/paths"
1819
"github.com/docker/docker-agent/pkg/runtime"
@@ -1214,6 +1215,13 @@ func (m *model) renderAgentEntry(content *strings.Builder, agent runtime.AgentDe
12141215
content.WriteString(toolcommon.TruncateText("Model: "+agent.Model, maxWidth))
12151216
}
12161217

1218+
// isVisuallyBlank returns true if a rendered line contains no visible content.
1219+
// Lines may contain ANSI escape codes and whitespace padding from lipgloss styles
1220+
// (e.g., TabStyle.Width()), so we strip ANSI sequences and check for whitespace.
1221+
func isVisuallyBlank(line string) bool {
1222+
return strings.TrimSpace(ansi.Strip(line)) == ""
1223+
}
1224+
12171225
// buildAgentClickZones populates agentClickZones by scanning the rendered lines
12181226
// to find which lines belong to which agent. It relies on the structure produced
12191227
// by renderTab + agentInfo: a 2-line tab header, then agent blocks separated by
@@ -1230,7 +1238,7 @@ func (m *model) buildAgentClickZones(agentSectionStart int, lines []string) {
12301238
inBlock := false
12311239

12321240
for i := agentSectionStart + tabHeaderLines; i < len(lines) && agentIdx < len(m.availableAgents); i++ {
1233-
if lipgloss.Width(lines[i]) == 0 {
1241+
if isVisuallyBlank(lines[i]) {
12341242
// Blank line: if we were inside a block, advance to the next agent
12351243
if inBlock {
12361244
agentIdx++

0 commit comments

Comments
 (0)