Add task sidebar workflow

This commit is contained in:
2026-06-24 12:28:56 +01:00
parent 45263d59f8
commit d133839194
33 changed files with 1890 additions and 134 deletions
+23 -13
View File
@@ -1,16 +1,21 @@
package app
import "github.com/hjbdev/patterm/internal/scratchpad"
import (
"github.com/hjbdev/patterm/internal/scratchpad"
"github.com/hjbdev/patterm/internal/task"
)
// navEntry is one row in the unified sidebar navigation list. Exactly
// one of childID or pad is set. childID points at a Child by ID; pad
// names a scratchpad entry. Empty zero-value means "no target".
// one of taskID, childID, or pad is set. childID points at a Child by ID;
// pad names a scratchpad entry. Empty zero-value means "no target".
type navEntry struct {
taskID string
childID string
pad string
}
func (n navEntry) empty() bool { return n.childID == "" && n.pad == "" }
func (n navEntry) empty() bool { return n.taskID == "" && n.childID == "" && n.pad == "" }
func (n navEntry) isTask() bool { return n.taskID != "" }
func (n navEntry) isPad() bool { return n.pad != "" }
func (n navEntry) isChild() bool { return n.childID != "" }
@@ -221,12 +226,14 @@ func sidebarNavList(children []*Child, activeAgentID string) []*Child {
return out
}
// sidebarNav returns the combined Processes + Agent Tree + Scratchpads
// navigation list. Scratchpads always appear after children so the
// existing "step past the tree" expectation still holds.
func sidebarNav(children []*Child, activeAgentID string, pads []scratchpad.Entry) []navEntry {
// sidebarNav returns the combined Tasks + Processes + Agent Tree + Scratchpads
// navigation list. Order matches the right rail top-to-bottom.
func sidebarNav(children []*Child, activeAgentID string, tasks []task.Task, pads []scratchpad.Entry) []navEntry {
flat := sidebarNavList(children, activeAgentID)
out := make([]navEntry, 0, len(flat)+len(pads))
out := make([]navEntry, 0, len(tasks)+len(flat)+len(pads))
for _, t := range tasks {
out = append(out, navEntry{taskID: t.ID})
}
for _, c := range flat {
out = append(out, navEntry{childID: c.ID})
}
@@ -237,15 +244,18 @@ func sidebarNav(children []*Child, activeAgentID string, pads []scratchpad.Entry
}
// nextNavEntry returns the entry `step` positions away from the
// current focus in the unified nav list. Either focusChildID or
// focusPad will be set (or both empty for "nothing focused yet").
// current focus in the unified nav list. Exactly one focus identifier is
// usually set (or all empty for "nothing focused yet").
// Empty when there's nothing else to land on.
func nextNavEntry(children []*Child, focusChildID, focusPad, activeAgentID string, pads []scratchpad.Entry, step int) navEntry {
flat := sidebarNav(children, activeAgentID, pads)
func nextNavEntry(children []*Child, focusChildID, focusPad, focusTaskID, activeAgentID string, tasks []task.Task, pads []scratchpad.Entry, step int) navEntry {
flat := sidebarNav(children, activeAgentID, tasks, pads)
if len(flat) == 0 {
return navEntry{}
}
matches := func(e navEntry) bool {
if focusTaskID != "" && e.taskID != "" {
return e.taskID == focusTaskID
}
if focusPad != "" && e.pad != "" {
return e.pad == focusPad
}