summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-11-11 22:03:54 -0500
committerDax Raad <[email protected]>2025-11-11 22:04:00 -0500
commite3a2728fa3cf331b6d075dbfd6543990083c72eb (patch)
tree6ee70989f99a4943e328e595d2d46fb9f7bf40ee
parent18260b037bf8d99da52f7179c488bf183850c26e (diff)
downloadopencode-e3a2728fa3cf331b6d075dbfd6543990083c72eb.tar.gz
opencode-e3a2728fa3cf331b6d075dbfd6543990083c72eb.zip
tui: add double-esc interrupt mechanism for long-running operations
Users can now press escape twice within 5 seconds to interrupt long-running operations in the TUI. The first press shows a visual hint, and the second press aborts the current session.
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx29
1 files changed, 22 insertions, 7 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
index 408c62cde..a7f248758 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
@@ -228,11 +228,21 @@ export function Prompt(props: PromptProps) {
if (!props.sessionID) return
if (autocomplete.visible) return
if (!input.focused) return
- sdk.client.session.abort({
- path: {
- id: props.sessionID,
- },
- })
+
+ setStore("interrupt", store.interrupt + 1)
+
+ setTimeout(() => {
+ setStore("interrupt", 0)
+ }, 5000)
+
+ if (store.interrupt >= 2) {
+ sdk.client.session.abort({
+ path: {
+ id: props.sessionID,
+ },
+ })
+ setStore("interrupt", 0)
+ }
dialog.clear()
},
},
@@ -252,6 +262,7 @@ export function Prompt(props: PromptProps) {
prompt: PromptInfo
mode: "normal" | "shell"
extmarkToPartIndex: Map<number, number>
+ interrupt: number
}>({
prompt: {
input: "",
@@ -259,6 +270,7 @@ export function Prompt(props: PromptProps) {
},
mode: "normal",
extmarkToPartIndex: new Map(),
+ interrupt: 0,
})
createEffect(() => {
@@ -746,8 +758,11 @@ export function Prompt(props: PromptProps) {
</Match>
<Match when={status() === "working"}>
<box flexDirection="row" gap={1}>
- <text fg={theme.text}>
- esc <span style={{ fg: theme.textMuted }}>interrupt</span>
+ <text fg={store.interrupt > 0 ? theme.primary : theme.text}>
+ esc{" "}
+ <span style={{ fg: store.interrupt > 0 ? theme.primary : theme.textMuted }}>
+ {store.interrupt > 0 ? "again to interrupt" : "interrupt"}
+ </span>
</text>
</box>
</Match>