Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions internal/ui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,12 @@ func (m *AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.currentView = ViewDashboard
m.newTaskForm = nil
m.showWelcome = false // Hide welcome message after first task is created
cmds = append(cmds, m.loadTasks())
if msg.task != nil && msg.task.Status == db.StatusQueued {
// Navigate to detail view with executor pane focused when task is queued for execution
cmds = append(cmds, tea.Batch(m.loadTasks(), m.loadTaskWithFocus(msg.task.ID)))
} else {
cmds = append(cmds, m.loadTasks())
}
} else {
m.err = msg.err
}
Expand Down Expand Up @@ -1994,7 +1999,8 @@ func (m *AppModel) updateDashboard(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
// Immediately update UI for responsiveness
task.Status = db.StatusQueued
m.updateTaskInList(task)
return m, m.queueTask(task.ID)
// Queue the task and navigate to detail view with executor pane focused
return m, tea.Batch(m.queueTask(task.ID), m.loadTaskWithFocus(task.ID))
}

case key.Matches(msg, m.keys.TogglePin):
Expand Down
107 changes: 107 additions & 0 deletions internal/ui/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2220,3 +2220,110 @@ func TestSystemMessages_NotSwallowedByOverlayViews(t *testing.T) {
})
}
}

func TestTaskCreatedMsg_QueuedTaskNavigatesToDetailView(t *testing.T) {
database, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Failed to create test database: %v", err)
}
defer database.Close()

task := &db.Task{Title: "Test task", Status: db.StatusQueued}
if err := database.CreateTask(task); err != nil {
t.Fatalf("Failed to create task: %v", err)
}

m := &AppModel{
width: 100,
height: 50,
db: database,
keys: DefaultKeyMap(),
kanban: NewKanbanBoard(100, 50),
currentView: ViewNewTaskConfirm,
prevStatuses: make(map[int64]string),
tasksNeedingInput: make(map[int64]bool),
questionPrompts: make(map[int64]bool),
executorPrompts: make(map[int64]string),
}

// Send taskCreatedMsg with a queued task
model, cmd := m.Update(taskCreatedMsg{task: task, err: nil})
am := model.(*AppModel)

// Should temporarily be on dashboard (loadTaskWithFocus will switch to detail)
if am.currentView != ViewDashboard {
t.Errorf("expected ViewDashboard (temporary), got %v", am.currentView)
}

// Should have cleared the form
if am.newTaskForm != nil {
t.Error("expected newTaskForm to be nil")
}

// Should return commands (loadTasks + loadTaskWithFocus batched)
if cmd == nil {
t.Error("expected non-nil command for queued task (should include loadTaskWithFocus)")
}
}

func TestTaskCreatedMsg_BacklogTaskStaysOnDashboard(t *testing.T) {
database, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Failed to create test database: %v", err)
}
defer database.Close()

task := &db.Task{Title: "Test task", Status: db.StatusBacklog}
if err := database.CreateTask(task); err != nil {
t.Fatalf("Failed to create task: %v", err)
}

m := &AppModel{
width: 100,
height: 50,
db: database,
keys: DefaultKeyMap(),
kanban: NewKanbanBoard(100, 50),
currentView: ViewNewTaskConfirm,
prevStatuses: make(map[int64]string),
tasksNeedingInput: make(map[int64]bool),
questionPrompts: make(map[int64]bool),
executorPrompts: make(map[int64]string),
}

// Send taskCreatedMsg with a backlog task
model, cmd := m.Update(taskCreatedMsg{task: task, err: nil})
am := model.(*AppModel)

// Should stay on dashboard
if am.currentView != ViewDashboard {
t.Errorf("expected ViewDashboard, got %v", am.currentView)
}

// Should return command (loadTasks only)
if cmd == nil {
t.Error("expected non-nil command for loadTasks")
}
}

func TestNewDetailModel_FocusExecutorOnJoinFlag(t *testing.T) {
database, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Failed to create test database: %v", err)
}
defer database.Close()

task := &db.Task{ID: 1, Title: "Test task", Status: db.StatusQueued}

// When focusExecutor is true, detail model should have focusExecutorOnJoin set
detail, _ := NewDetailModel(task, database, nil, 100, 50, true)
if !detail.focusExecutorOnJoin {
t.Error("expected focusExecutorOnJoin=true when focusExecutor=true")
}

// When focusExecutor is false, detail model should NOT have focusExecutorOnJoin set
detail2, _ := NewDetailModel(task, database, nil, 100, 50, false)
if detail2.focusExecutorOnJoin {
t.Error("expected focusExecutorOnJoin=false when focusExecutor=false")
}
}
Loading