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: 2 additions & 0 deletions internal/tui/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ func init() {
{"theme", "Pick the colour theme (Ctrl+T)", (*Model).cmdTheme},
{"reasoning", "Toggle reasoning display", (*Model).cmdReasoning},
{"clear", "Clear the transcript", (*Model).cmdClear},
{"revert", "Revert to an earlier turn (picker)", (*Model).cmdRevert},
{"stop", "Interrupt the current turn", (*Model).cmdStop},
{"undo", "Undo the last turn", (*Model).cmdUndo},
{"status", "Runtime summary", (*Model).cmdStatus},
{"web", "Print the dashboard URL", (*Model).cmdWeb},
{"version", "Show the version", (*Model).cmdVersion},
Expand Down
28 changes: 28 additions & 0 deletions internal/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ type Model struct {
picker picker
input inputModal

// pending is a staged /undo or /revert awaiting the operator's "y" to
// commit. Nil when nothing is pending; a pointer keeps the hot-path
// check cheap (nil vs zero-valued struct).
pending *pendingRevert

cache map[string]string // memoised block renders, keyed by content+width

welcomeFrame int // animation frame for the empty-state splash
Expand Down Expand Up @@ -260,6 +265,29 @@ func (m *Model) onKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
return m, m.modalCmd()
}

// A staged /undo or /revert intercepts the next keystroke: "y" (with
// an empty composer) commits, Esc discards, everything else discards
// and falls through so the keystroke still lands normally. Placed
// above the palette check so a stray key while a rollback is pending
// never silently disappears into palette navigation.
if m.pending != nil {
if m.ta.Value() == "" {
if r := msg.Runes; len(r) == 1 && (r[0] == 'y' || r[0] == 'Y') {
m.commitPending()
m.refreshTranscript()
return m, nil
}
if msg.Type == tea.KeyEsc {
m.cancelPending()
m.refreshTranscript()
return m, nil
}
}
m.cancelPending()
m.refreshTranscript()
// fall through so the keystroke still routes as normal input
}

// Palette navigation takes priority.
if len(m.palette) > 0 {
switch msg.Type {
Expand Down
Loading
Loading