fix(ui): improvements

Carlos Alexandro Becker created

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

Change summary

internal/llm/agent/mcp-tools.go                      | 12 -
internal/tui/components/dialogs/commands/commands.go | 83 +++++++------
2 files changed, 52 insertions(+), 43 deletions(-)

Detailed changes

internal/llm/agent/mcp-tools.go 🔗

@@ -336,13 +336,11 @@ func doGetMCPTools(ctx context.Context, permissions permission.Service, cfg *con
 			updateMcpTools(name, tools)
 			updateMcpPrompts(name, prompts)
 			mcpClients.Set(name, c)
-			updateMCPState(
-				name, MCPStateConnected, nil, c,
-				MCPCounts{
-					Tools:   len(tools),
-					Prompts: len(prompts),
-				},
-			)
+			counts := MCPCounts{
+				Tools:   len(tools),
+				Prompts: len(prompts),
+			}
+			updateMCPState(name, MCPStateConnected, nil, c, counts)
 		}(name, m)
 	}
 	wg.Wait()

internal/tui/components/dialogs/commands/commands.go 🔗

@@ -29,8 +29,12 @@ const (
 	defaultWidth int = 70
 )
 
+type CommandType uint
+
+func (c CommandType) String() string { return []string{"System", "User", "MCP"}[c] }
+
 const (
-	SystemCommands int = iota
+	SystemCommands CommandType = iota
 	UserCommands
 	MCPPrompts
 )
@@ -59,10 +63,10 @@ type commandDialogCmp struct {
 	commandList  listModel
 	keyMap       CommandsDialogKeyMap
 	help         help.Model
-	commandType  int       // SystemCommands, UserCommands, or MCPPrompts
-	userCommands []Command // User-defined commands
-	mcpPrompts   []Command // MCP prompts
-	sessionID    string    // Current session ID
+	selected     CommandType // Selected SystemCommands, UserCommands, or MCPPrompts
+	userCommands []Command   // User-defined commands
+	mcpPrompts   []Command   // MCP prompts
+	sessionID    string      // Current session ID
 	ctx          context.Context
 	cancel       context.CancelFunc
 }
@@ -110,7 +114,7 @@ func NewCommandDialog(sessionID string) CommandsDialog {
 		width:       defaultWidth,
 		keyMap:      DefaultCommandsDialogKeyMap(),
 		help:        help,
-		commandType: SystemCommands,
+		selected:    SystemCommands,
 		sessionID:   sessionID,
 	}
 }
@@ -126,7 +130,7 @@ func (c *commandDialogCmp) Init() tea.Cmd {
 	// Subscribe to MCP events
 	c.ctx, c.cancel = context.WithCancel(context.Background())
 	return tea.Batch(
-		c.SetCommandType(c.commandType),
+		c.SetCommandType(c.selected),
 		c.subscribeMCPEvents(),
 	)
 }
@@ -149,7 +153,7 @@ func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		c.wWidth = msg.Width
 		c.wHeight = msg.Height
 		return c, tea.Batch(
-			c.SetCommandType(c.commandType),
+			c.SetCommandType(c.selected),
 			c.commandList.SetSize(c.listWidth(), c.listHeight()),
 		)
 	case pubsub.Event[agent.MCPEvent]:
@@ -157,7 +161,7 @@ func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		if msg.Type == pubsub.UpdatedEvent {
 			c.mcpPrompts = LoadMCPPrompts()
 			// If we're currently viewing MCP prompts, refresh the list
-			if c.commandType == MCPPrompts {
+			if c.selected == MCPPrompts {
 				return c, tea.Batch(
 					c.SetCommandType(MCPPrompts),
 					c.subscribeMCPEvents(),
@@ -184,23 +188,23 @@ func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			if len(c.userCommands) == 0 && len(c.mcpPrompts) == 0 {
 				return c, nil
 			}
-			// Cycle through command types: System -> User -> MCP -> System
-			nextType := (c.commandType + 1) % 3
-			// Skip empty types
-			for {
-				if nextType == UserCommands && len(c.userCommands) == 0 {
-					nextType = (nextType + 1) % 3
-				} else if nextType == MCPPrompts && len(c.mcpPrompts) == 0 {
-					nextType = (nextType + 1) % 3
-				} else {
-					break
+			switch c.selected {
+			case SystemCommands:
+				if len(c.userCommands) > 0 {
+					return c, c.SetCommandType(UserCommands)
 				}
-				// Prevent infinite loop
-				if nextType == c.commandType {
-					return c, nil
+				if len(c.mcpPrompts) > 0 {
+					return c, c.SetCommandType(MCPPrompts)
 				}
+				return c, nil
+			case UserCommands:
+				if len(c.mcpPrompts) > 0 {
+					return c, c.SetCommandType(MCPPrompts)
+				}
+				return c, c.SetCommandType(SystemCommands)
+			case MCPPrompts:
+				return c, c.SetCommandType(SystemCommands)
 			}
-			return c, c.SetCommandType(nextType)
 		case key.Matches(msg, c.keyMap.Close):
 			if c.cancel != nil {
 				c.cancel()
@@ -247,17 +251,24 @@ func (c *commandDialogCmp) Cursor() *tea.Cursor {
 
 func (c *commandDialogCmp) commandTypeRadio() string {
 	t := styles.CurrentTheme()
-	choices := []string{"System", "User", "MCP"}
-	iconSelected := "◉"
-	iconUnselected := "○"
-
-	icons := make([]string, 3)
-	for i := range icons {
-		if i == c.commandType {
-			icons[i] = iconSelected
-		} else {
-			icons[i] = iconUnselected
+
+	selected := func(i CommandType) string {
+		if i == c.selected {
+			return "◉"
 		}
+		return "○"
+	}
+
+	choices := []string{SystemCommands.String()}
+	icons := []string{selected(SystemCommands)}
+
+	if len(c.userCommands) > 0 {
+		choices = append(choices, UserCommands.String())
+		icons = append(icons, selected(UserCommands))
+	}
+	if len(c.mcpPrompts) > 0 {
+		choices = append(choices, MCPPrompts.String())
+		icons = append(icons, selected(MCPPrompts))
 	}
 
 	parts := make([]string, 0, 6)
@@ -272,11 +283,11 @@ func (c *commandDialogCmp) listWidth() int {
 	return defaultWidth - 2 // 4 for padding
 }
 
-func (c *commandDialogCmp) SetCommandType(commandType int) tea.Cmd {
-	c.commandType = commandType
+func (c *commandDialogCmp) SetCommandType(commandType CommandType) tea.Cmd {
+	c.selected = commandType
 
 	var commands []Command
-	switch c.commandType {
+	switch c.selected {
 	case SystemCommands:
 		commands = c.defaultCommands()
 	case UserCommands: