Skip to content

[FEATURE]: Double Esc right after sending should undo the message and restore it to the input #52537

Open

Description

@moritzWa

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

In Claude Code, if you hit Esc right after sending a message, the turn stops, the message is removed from the conversation, and its text goes back into the input so you can fix it and resend.

In opencode, double Esc only interrupts. The half-sent message stays in the transcript and the text is gone from the input, so you have to interrupt, then /undo, every time you notice a typo or a missing detail a second after hitting enter.

Proposal: if you press Esc twice within ~2s of sending, abort + revert that message and put it back in the prompt (attachments included). Outside that window, Esc behaves exactly as today.

There was a PR for this before (#36538) that got closed by the stale-PR cleanup, not on merit.

Activity

  1. github-actions commented on Oct 1, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    For keybind-related issues, please also check our pinned keybinds documentation: #4997

  2. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the suggestion! As I understand it, you'd like pressing Esc twice within a couple of seconds of sending to abort the turn, remove that message from the conversation and put its text and attachments back in the input, so you don't have to interrupt and then /undo every time you spot a typo. Outside that window, Esc would work as it does now.

    This isn't something a plugin can do yet. TUI plugins can add keybinds and commands, but they can't read or set what's in the prompt input, so there's no way to restore the message there. I've noted the needed plugin API addition for the maintainers.

    Related:

    The maintainers will take a look and consider it.

  3. ZacxDev commented on Nov 7, 2026

    @ZacxDev

    Adding measurements that may help whoever implements this — not a competing request.

    The gesture is still unbound as of the v2.0.32 source tree I have checked out: there is no timed two-press / double-Escape handling anywhere in packages/tui/src or packages/app/src (searching double-esc / double-tap / two-press finds only the mini-mode ctrl+c exit hint). session.interrupt owns the single escape binding, and a keymap sequence cannot express a double tap without swallowing the first press.

    The surface already ships, which is why this looks like a gesture-only gap: session.timeline renders the user's own messages (searchable, select → Revert/Copy) via dialog-timeline.tsx, and /undo stages the rollback + restores the prompt to the composer.

    Two trackers worth knowing about:

    FWIW this is expressible today with no core change, as a TUI plugin: listen on context.renderer.keyInput "keypress", detect a second escape within ~800 ms, then context.keymap.dispatch("session.timeline") (or kick off a revert). Listening on the renderer key stream does not consume the event, so single-Esc interrupt is preserved by construction. I have this implemented locally and unit-tested off the compiled plugin (window boundary, session gating, pair-reset, cleanup); I have not yet confirmed it in a live TUI. Happy to share the plugin if the approach is of interest.

  4. ZacxDev commented on Nov 7, 2026

    @ZacxDev

    Follow-up: we ended up solving this for ourselves, and it's now public in case it's useful here.

    https://github.com/ZacxDev/opencode-double-esc-timeline (MIT)

    It's an opencode TUI plugin, not a fork — the stock binary is untouched:

    • double-tap Escape → context.keymap.dispatch("session.timeline"), i.e. opencode's own timeline dialog (the searchable list of your sent messages, select → Revert/Copy). Nothing is reimplemented.
    • The listener is on the raw context.renderer.keyInput "keypress" stream, which does not consume the event, so single-Escape interrupt/close behaviour is preserved by construction. A keymap sequence couldn't do this without swallowing the first press, which is why it's a timing check (800 ms) rather than a binding.
    • It ships a no-op index.ts server half, because a directory under plugins/ is a target for both the TUI loader and the server loader; without <dir>/index the server raises Plugin entrypoint not found.

    Install (no npm publish yet — that's pending): either a nix flake (imports = [ inputs.opencode-double-esc-timeline.homeManagerModules.default ]) or copying tui.ts + index.ts into ~/.config/opencode/plugins/opencode-double-esc-timeline/.

    Verified working on opencode v2.0.32. If the gesture gets bound natively (and #14037's closure implies someone thought it was), this plugin can just be deleted — it only adds the gesture, it doesn't own any state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions