on main help sign in
feat(pills): add toggle todos/pills menu item (#2202)
commit8ccb3c70918b2affbe0b0f96717e972313f2cd8f authorChristian Rocha <christian@rocha.is> dateThu 12 Feb 2026, 10:46 (8 months ago) parenteae61e1
5 files changed+51−16
internal/session/session.go+10 internal/ui/dialog/actions.go+1 internal/ui/dialog/commands.go+28−7 internal/ui/model/pills.go+1−6 internal/ui/model/ui.go+11−3
internal/session/session.go+10
@@ -28,6 +28,16 @@ type Todo struct {
2828 ActiveForm string `json:"active_form"`
2929}
3030
31+// HasIncompleteTodos returns true if there are any non-completed todos.
32+func HasIncompleteTodos(todos []Todo) bool {
33+ for _, todo := range todos {
34+ if todo.Status != TodoStatusCompleted {
35+ return true
36+ }
37+ }
38+ return false
39+}
40+
3141type Session struct {
3242 ID string
3343 ParentSessionID string
internal/ui/dialog/actions.go+1
@@ -48,6 +48,7 @@ type (
4848 ActionToggleHelp struct{}
4949 ActionToggleCompactMode struct{}
5050 ActionToggleThinking struct{}
51+ ActionTogglePills struct{}
5152 ActionExternalEditor struct{}
5253 ActionToggleYoloMode struct{}
5354 // ActionInitializeProject is a message to initialize a project.
internal/ui/dialog/commands.go+28−7
@@ -49,8 +49,11 @@ type Commands struct {
4949 Close key.Binding
5050 }
5151
52− sessionID string // can be empty for non-session-specific commands
53− selected CommandType
52+ sessionID string
53+ hasSession bool
54+ hasTodos bool
55+ hasQueue bool
56+ selected CommandType
5457
5558 spinner spinner.Model
5659 loading bool
@@ -68,11 +71,14 @@ type Commands struct {
6871var _ Dialog = (*Commands)(nil)
6972
7073// NewCommands creates a new commands dialog.
71−func NewCommands(com *common.Common, sessionID string, customCommands []commands.CustomCommand, mcpPrompts []commands.MCPPrompt) (*Commands, error) {
74+func NewCommands(com *common.Common, sessionID string, hasSession, hasTodos, hasQueue bool, customCommands []commands.CustomCommand, mcpPrompts []commands.MCPPrompt) (*Commands, error) {
7275 c := &Commands{
7376 com: com,
7477 selected: SystemCommands,
7578 sessionID: sessionID,
79+ hasSession: hasSession,
80+ hasTodos: hasTodos,
81+ hasQueue: hasQueue,
7682 customCommands: customCommands,
7783 mcpPrompts: mcpPrompts,
7884 }
@@ -387,7 +393,7 @@ func (c *Commands) defaultCommands() []*CommandItem {
387393 }
388394
389395 // Only show compact command if there's an active session
390− if c.sessionID != "" {
396+ if c.hasSession {
391397 commands = append(commands, NewCommandItem(c.com.Styles, "summarize", "Summarize Session", "", ActionSummarize{SessionID: c.sessionID}))
392398 }
393399
@@ -417,10 +423,10 @@ func (c *Commands) defaultCommands() []*CommandItem {
417423 }
418424 }
419425 // Only show toggle compact mode command if window width is larger than compact breakpoint (120)
420− if c.windowWidth >= sidebarCompactModeBreakpoint && c.sessionID != "" {
426+ if c.windowWidth >= sidebarCompactModeBreakpoint && c.hasSession {
421427 commands = append(commands, NewCommandItem(c.com.Styles, "toggle_sidebar", "Toggle Sidebar", "", ActionToggleCompactMode{}))
422428 }
423− if c.sessionID != "" {
429+ if c.hasSession {
424430 cfg := c.com.Config()
425431 agentCfg := cfg.Agents[config.AgentCoder]
426432 model := cfg.GetModelByType(agentCfg.Model)
@@ -437,12 +443,27 @@ func (c *Commands) defaultCommands() []*CommandItem {
437443 commands = append(commands, NewCommandItem(c.com.Styles, "open_external_editor", "Open External Editor", "ctrl+o", ActionExternalEditor{}))
438444 }
439445
440− return append(commands,
446+ if c.hasTodos || c.hasQueue {
447+ var label string
448+ switch {
449+ case c.hasTodos && c.hasQueue:
450+ label = "Toggle To-Dos/Queue"
451+ case c.hasQueue:
452+ label = "Toggle Queue"
453+ default:
454+ label = "Toggle To-Dos"
455+ }
456+ commands = append(commands, NewCommandItem(c.com.Styles, "toggle_pills", label, "ctrl+t", ActionTogglePills{}))
457+ }
458+
459+ commands = append(commands,
441460 NewCommandItem(c.com.Styles, "toggle_yolo", "Toggle Yolo Mode", "", ActionToggleYoloMode{}),
442461 NewCommandItem(c.com.Styles, "toggle_help", "Toggle Help", "ctrl+g", ActionToggleHelp{}),
443462 NewCommandItem(c.com.Styles, "init", "Initialize Project", "", ActionInitializeProject{}),
444463 NewCommandItem(c.com.Styles, "quit", "Quit", "ctrl+c", tea.QuitMsg{}),
445464 )
465+
466+ return commands
446467}
447468
448469// SetCustomCommands sets the custom commands and refreshes the view if user commands are currently displayed.
internal/ui/model/pills.go+1−6
@@ -38,12 +38,7 @@ const (
3838
3939// hasIncompleteTodos returns true if there are any non-completed todos.
4040func hasIncompleteTodos(todos []session.Todo) bool {
41− for _, todo := range todos {
42− if todo.Status != session.TodoStatusCompleted {
43− return true
44− }
45− }
46− return false
41+ return session.HasIncompleteTodos(todos)
4742}
4843
4944// hasInProgressTodo returns true if there is at least one in-progress todo.
internal/ui/model/ui.go+11−3
@@ -1213,6 +1213,11 @@ func (m *UI) handleDialogMsg(msg tea.Msg) tea.Cmd {
12131213 case dialog.ActionToggleCompactMode:
12141214 cmds = append(cmds, m.toggleCompactMode())
12151215 m.dialog.CloseDialog(dialog.CommandsID)
1216+ case dialog.ActionTogglePills:
1217+ if cmd := m.togglePillsExpanded(); cmd != nil {
1218+ cmds = append(cmds, cmd)
1219+ }
1220+ m.dialog.CloseDialog(dialog.CommandsID)
12161221 case dialog.ActionToggleThinking:
12171222 cmds = append(cmds, func() tea.Msg {
12181223 cfg := m.com.Config()
@@ -2858,12 +2863,15 @@ func (m *UI) openCommandsDialog() tea.Cmd {
28582863 return nil
28592864 }
28602865
2861− sessionID := ""
2862− if m.session != nil {
2866+ var sessionID string
2867+ hasSession := m.session != nil
2868+ if hasSession {
28632869 sessionID = m.session.ID
28642870 }
2871+ hasTodos := hasSession && hasIncompleteTodos(m.session.Todos)
2872+ hasQueue := m.promptQueue > 0
28652873
2866− commands, err := dialog.NewCommands(m.com, sessionID, m.customCommands, m.mcpPrompts)
2874+ commands, err := dialog.NewCommands(m.com, sessionID, hasSession, hasTodos, hasQueue, m.customCommands, m.mcpPrompts)
28672875 if err != nil {
28682876 return util.ReportError(err)
28692877 }