mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-26 21:36:31 +02:00
fix(tui): slash popup + transient overlays swallow esc before busy-cancel
Two rules for how esc resolves while a turn is running got
implemented this round:
1) Let the user open the slash popup during a busy turn.
The suggest render path used to short-circuit on i.busy, so
typing / while the agent was working did nothing. The
dispatcher in runSlash already handles the busy-state routing
per command (safe ones run immediately, destructive ones
cancel first), so dropping the guard was safe. Now / opens
the popup whether or not a turn is in flight.
2) Esc dismisses overlays before it cancels the turn.
The global key switch used to fire the busy-cancel
unconditionally on esc. That meant three common patterns
silently ripped the active turn away:
- Open the slash popup, press esc to dismiss it ->
turn cancelled.
- Run /help to see the key bindings while a turn was
running, press esc when done -> turn cancelled.
- An extension pushed a notify/display line, user pressed
esc to clear it -> turn cancelled.
The esc case now checks, in order:
- slash popup active -> break out of the switch, let the
popup's own esc handler (later in handleKey) close it
- helpBlock or extNotes non-empty -> clear them, invalidate,
return (turn keeps running)
- busy + cancelable -> cancel the turn (old behaviour)
- idle -> fall through to the editor which clears itself
Result: esc feels like a dismiss key that escalates. It nukes
the turn only when nothing else on screen wants it.
No change to dialog handlers \u2014 those already intercept esc in
their own return-false branches before the global switch ever
runs.
This commit is contained in:
parent
6019404644
commit
112341ca3d
1 changed files with 31 additions and 3 deletions
|
|
@ -715,7 +715,13 @@ func (i *Interactive) redraw() {
|
|||
}
|
||||
var suggest []string
|
||||
currentInput := i.ed.Value()
|
||||
if len(dialog) == 0 && i.suggest.Active(currentInput) && !i.busy {
|
||||
// Slash popup renders even while the agent is busy so the user
|
||||
// can queue a destructive command (/clear, /compact, /logout,
|
||||
// /model) or a read-only one (/help, /jump, /sessions, etc.)
|
||||
// without waiting for the current turn to finish. The dispatcher
|
||||
// in runSlash already handles the busy case per-command: safe
|
||||
// ones run immediately, destructive ones cancel the turn first.
|
||||
if len(dialog) == 0 && i.suggest.Active(currentInput) {
|
||||
suggest = i.suggest.Render(currentInput, i.cfg.Theme, cols)
|
||||
}
|
||||
|
||||
|
|
@ -1105,8 +1111,30 @@ func (i *Interactive) handleKey(ctx context.Context, k tui.Key) (done bool) {
|
|||
i.armCtrlCExit()
|
||||
return false
|
||||
case tui.KeyEsc:
|
||||
// Esc interrupts a running turn. When idle, fall through so the
|
||||
// editor can clear itself.
|
||||
// Esc interrupts a running turn — but only when nothing
|
||||
// else on screen wants to consume the key first. The slash
|
||||
// popup has its own Esc behaviour (close + clear editor),
|
||||
// and transient overlays like the /help block and extension
|
||||
// notes should dismiss on Esc before we even consider the
|
||||
// turn. Without these guards, a casual Esc press after
|
||||
// running /help on a busy turn rips the turn away.
|
||||
if i.suggest.Active(i.ed.Value()) {
|
||||
break
|
||||
}
|
||||
i.mu.Lock()
|
||||
hadHelp := len(i.helpBlock) > 0
|
||||
hadNotes := len(i.extNotes) > 0
|
||||
if hadHelp {
|
||||
i.helpBlock = nil
|
||||
}
|
||||
if hadNotes {
|
||||
i.extNotes = nil
|
||||
}
|
||||
i.mu.Unlock()
|
||||
if hadHelp || hadNotes {
|
||||
i.invalidate()
|
||||
return false
|
||||
}
|
||||
if i.busy && i.cancelTurn != nil {
|
||||
i.cancelTurn()
|
||||
// If a confirm dialog is pending, refuse it so the agent
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue