diff --git a/internal/session/session.go b/internal/session/session.go index 0ef6cfe22bebbf35df48f0db1fbe00c6d128251b..f9279f9f4d45f8562fd868074721d27dca0901f6 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -28,6 +28,16 @@ type Todo struct { ActiveForm string `json:"active_form"` } +// HasIncompleteTodos returns true if there are any non-completed todos. +func HasIncompleteTodos(todos []Todo) bool { + for _, todo := range todos { + if todo.Status != TodoStatusCompleted { + return true + } + } + return false +} + type Session struct { ID string ParentSessionID string diff --git a/internal/ui/dialog/actions.go b/internal/ui/dialog/actions.go index 5c96f1c96111222a270a4529d39bfaac4162205c..79e11a64bec50937b36a198b6096f83273041142 100644 --- a/internal/ui/dialog/actions.go +++ b/internal/ui/dialog/actions.go @@ -48,6 +48,7 @@ type ( ActionToggleHelp struct{} ActionToggleCompactMode struct{} ActionToggleThinking struct{} + ActionTogglePills struct{} ActionExternalEditor struct{} ActionToggleYoloMode struct{} // ActionInitializeProject is a message to initialize a project. diff --git a/internal/ui/dialog/commands.go b/internal/ui/dialog/commands.go index 6e769e66f7217c994f877582e8ca2eca80577b9a..e74d92775de24ee2672b251005e1e6d501e35aab 100644 --- a/internal/ui/dialog/commands.go +++ b/internal/ui/dialog/commands.go @@ -49,8 +49,11 @@ type Commands struct { Close key.Binding } - sessionID string // can be empty for non-session-specific commands - selected CommandType + sessionID string + hasSession bool + hasTodos bool + hasQueue bool + selected CommandType spinner spinner.Model loading bool @@ -68,11 +71,14 @@ type Commands struct { var _ Dialog = (*Commands)(nil) // NewCommands creates a new commands dialog. -func NewCommands(com *common.Common, sessionID string, customCommands []commands.CustomCommand, mcpPrompts []commands.MCPPrompt) (*Commands, error) { +func NewCommands(com *common.Common, sessionID string, hasSession, hasTodos, hasQueue bool, customCommands []commands.CustomCommand, mcpPrompts []commands.MCPPrompt) (*Commands, error) { c := &Commands{ com: com, selected: SystemCommands, sessionID: sessionID, + hasSession: hasSession, + hasTodos: hasTodos, + hasQueue: hasQueue, customCommands: customCommands, mcpPrompts: mcpPrompts, } @@ -387,7 +393,7 @@ func (c *Commands) defaultCommands() []*CommandItem { } // Only show compact command if there's an active session - if c.sessionID != "" { + if c.hasSession { commands = append(commands, NewCommandItem(c.com.Styles, "summarize", "Summarize Session", "", ActionSummarize{SessionID: c.sessionID})) } @@ -417,10 +423,10 @@ func (c *Commands) defaultCommands() []*CommandItem { } } // Only show toggle compact mode command if window width is larger than compact breakpoint (120) - if c.windowWidth >= sidebarCompactModeBreakpoint && c.sessionID != "" { + if c.windowWidth >= sidebarCompactModeBreakpoint && c.hasSession { commands = append(commands, NewCommandItem(c.com.Styles, "toggle_sidebar", "Toggle Sidebar", "", ActionToggleCompactMode{})) } - if c.sessionID != "" { + if c.hasSession { cfg := c.com.Config() agentCfg := cfg.Agents[config.AgentCoder] model := cfg.GetModelByType(agentCfg.Model) @@ -437,12 +443,27 @@ func (c *Commands) defaultCommands() []*CommandItem { commands = append(commands, NewCommandItem(c.com.Styles, "open_external_editor", "Open External Editor", "ctrl+o", ActionExternalEditor{})) } - return append(commands, + if c.hasTodos || c.hasQueue { + var label string + switch { + case c.hasTodos && c.hasQueue: + label = "Toggle To-Dos/Queue" + case c.hasQueue: + label = "Toggle Queue" + default: + label = "Toggle To-Dos" + } + commands = append(commands, NewCommandItem(c.com.Styles, "toggle_pills", label, "ctrl+t", ActionTogglePills{})) + } + + commands = append(commands, NewCommandItem(c.com.Styles, "toggle_yolo", "Toggle Yolo Mode", "", ActionToggleYoloMode{}), NewCommandItem(c.com.Styles, "toggle_help", "Toggle Help", "ctrl+g", ActionToggleHelp{}), NewCommandItem(c.com.Styles, "init", "Initialize Project", "", ActionInitializeProject{}), NewCommandItem(c.com.Styles, "quit", "Quit", "ctrl+c", tea.QuitMsg{}), ) + + return commands } // SetCustomCommands sets the custom commands and refreshes the view if user commands are currently displayed. diff --git a/internal/ui/model/pills.go b/internal/ui/model/pills.go index 6dc87fabd3f14733f3da7e56eb1382df0d825b94..fb3dcc1e3a86cb63d9e0a267476863a6260d0816 100644 --- a/internal/ui/model/pills.go +++ b/internal/ui/model/pills.go @@ -38,12 +38,7 @@ const ( // hasIncompleteTodos returns true if there are any non-completed todos. func hasIncompleteTodos(todos []session.Todo) bool { - for _, todo := range todos { - if todo.Status != session.TodoStatusCompleted { - return true - } - } - return false + return session.HasIncompleteTodos(todos) } // hasInProgressTodo returns true if there is at least one in-progress todo. diff --git a/internal/ui/model/ui.go b/internal/ui/model/ui.go index 0545aa3eca7fb661e6f8e6c906af094bcbd44bc1..04b644361be47ed223201ba9d0744e084feb7119 100644 --- a/internal/ui/model/ui.go +++ b/internal/ui/model/ui.go @@ -1213,6 +1213,11 @@ func (m *UI) handleDialogMsg(msg tea.Msg) tea.Cmd { case dialog.ActionToggleCompactMode: cmds = append(cmds, m.toggleCompactMode()) m.dialog.CloseDialog(dialog.CommandsID) + case dialog.ActionTogglePills: + if cmd := m.togglePillsExpanded(); cmd != nil { + cmds = append(cmds, cmd) + } + m.dialog.CloseDialog(dialog.CommandsID) case dialog.ActionToggleThinking: cmds = append(cmds, func() tea.Msg { cfg := m.com.Config() @@ -2858,12 +2863,15 @@ func (m *UI) openCommandsDialog() tea.Cmd { return nil } - sessionID := "" - if m.session != nil { + var sessionID string + hasSession := m.session != nil + if hasSession { sessionID = m.session.ID } + hasTodos := hasSession && hasIncompleteTodos(m.session.Todos) + hasQueue := m.promptQueue > 0 - commands, err := dialog.NewCommands(m.com, sessionID, m.customCommands, m.mcpPrompts) + commands, err := dialog.NewCommands(m.com, sessionID, hasSession, hasTodos, hasQueue, m.customCommands, m.mcpPrompts) if err != nil { return util.ReportError(err) }