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
2 changes: 1 addition & 1 deletion internal/tui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ type app struct {
boardOrder []string
boardGroups []string
boardCursor int
boardStop int64 // session awaiting y/n stop confirmation
boardStop int64 // session awaiting Enter to confirm a stop
boardMachine bool
boardFilter int
boardIdle bool
Expand Down
22 changes: 10 additions & 12 deletions internal/tui/board.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,16 @@ func (m *app) boardSelected() *view.BoardRow {
return &r
}
func (m *app) boardKey(k string) (tea.Cmd, bool) {
if m.boardStop != 0 && k != "y" && k != "n" {
// A pending stop takes the next key: Enter stops, anything else cancels.
// y and n stay prompt answers only, so allowing a prompt can never stop a session.
if id := m.boardStop; id != 0 {
m.boardStop = 0
m.status = "Cancelled"
if k != "enter" {
m.status = "Stop cancelled"
return nil, true
}
ctx, run := m.opts.Context, m.opts.SessionAction
return func() tea.Msg { return actionMsg{status: "Session stopped", err: run(ctx, "kill", id)} }, true
}
m.syncBoard()
switch k {
Expand Down Expand Up @@ -218,19 +225,10 @@ func (m *app) boardKey(k string) (tea.Cmd, bool) {
m.status = fmt.Sprintf("driga agents kill %d · confirm in terminal", r.Node.ID)
default:
m.boardStop = r.Node.ID
m.status = fmt.Sprintf("Stop %q? y stop · n cancel", r.Title)
m.status = fmt.Sprintf("Stop %q? Enter stop · any other key cancels", r.Title)
}
return nil, true
case "y", "n":
if id := m.boardStop; id != 0 {
m.boardStop = 0
if k == "n" {
m.status = "Cancelled"
return nil, true
}
ctx, run := m.opts.Context, m.opts.SessionAction
return func() tea.Msg { return actionMsg{status: "Session stopped", err: run(ctx, "kill", id)} }, true
}
if r := m.boardSelected(); m.flowAttention && r != nil && r.PromptID != "" {
if m.boardPromptFocus != r.PromptID || m.opts.Now().Sub(m.boardPromptAt) < 600*time.Millisecond {
m.status = "Review prompt before answering"
Expand Down
15 changes: 10 additions & 5 deletions internal/tui/board_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,18 @@ func TestBoardStopNeedsConfirmation(t *testing.T) {
t.Fatal("another key must cancel a pending stop")
}
m.Update(tea.KeyPressMsg{Code: 'k'})
m.Update(tea.KeyPressMsg{Code: 'K'})
m.Update(tea.KeyPressMsg{Code: 'n'})
if len(stopped) != 0 {
t.Fatal("n stopped the session")
// y and n answer prompts; they must never confirm a stop.
for _, key := range []rune{'n', 'y'} {
m.Update(tea.KeyPressMsg{Code: 'K'})
if _, cmd := m.Update(tea.KeyPressMsg{Code: key}); cmd != nil {
cmd()
}
if len(stopped) != 0 || m.boardStop != 0 {
t.Fatalf("%c confirmed a stop", key)
}
}
m.Update(tea.KeyPressMsg{Code: 'K'})
_, cmd := m.Update(tea.KeyPressMsg{Code: 'y'})
_, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
execute(t, m, cmd)
if len(stopped) != 1 || stopped[0] != id {
t.Fatal("confirmed stop not dispatched", stopped, id)
Expand Down
2 changes: 1 addition & 1 deletion internal/tui/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ func (m *app) emptyText() string {
}

func (m *app) helpText() string {
body := wrapText("0 Flow · 1 Projects · 2 Machines · c Chat panel · Esc closes panel\nFlow: m Map · t Timeline · s Sessions · h activity / return\nMap and Timeline use a project tree in the terminal. Tab focuses Needs you.\nj/k or arrows select · Enter opens · / find · Esc returns\ny/n answer explicitly focused prompt after review · J Terminal · K Stop\nMachines: h health · p reclaim processes · d reclaim disk\nSessions: S sort · right/left expand families\nChat: Enter sends · Shift+Enter new line · Ctrl+n new · Ctrl+h history\nChat: y/n allow/deny · Alt+y/Alt+n while typing · m trust mode\nAlt+0/1/2 switches tabs while typing · Ctrl+x clears Chat context\n? help · q / ctrl+c quit", m.width)
body := wrapText("0 Flow · 1 Projects · 2 Machines · c Chat panel · Esc closes panel\nFlow: m Map · t Timeline · s Sessions · h activity / return\nMap and Timeline use a project tree in the terminal. Tab focuses Needs you.\nj/k or arrows select · Enter opens · / find · Esc returns\ny/n answer explicitly focused prompt after review · J Terminal · K Stop, then Enter\nMachines: h health · p reclaim processes · d reclaim disk\nSessions: S sort · right/left expand families\nChat: Enter sends · Shift+Enter new line · Ctrl+n new · Ctrl+h history\nChat: y/n allow/deny · Alt+y/Alt+n while typing · m trust mode\nAlt+0/1/2 switches tabs while typing · Ctrl+x clears Chat context\n? help · q / ctrl+c quit", m.width)
if m.tmuxTarget() != "" {
body += "\na attach tmux"
}
Expand Down
Loading