From 013c6f74e7dd7b18abc4898cbe4b2eae649495c8 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 31 Mar 2026 13:50:26 -0400 Subject: [PATCH 1/7] fix(app): block submit during revert --- packages/app/src/components/prompt-input.tsx | 54 ++++++++++++++----- packages/app/src/pages/session.tsx | 1 + .../composer/session-composer-region.tsx | 2 + 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index ee98e68cd5cf..21033ea90519 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -25,6 +25,7 @@ import { ProviderIcon } from "@opencode-ai/ui/provider-icon" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { IconButton } from "@opencode-ai/ui/icon-button" import { Select } from "@opencode-ai/ui/select" +import { Spinner } from "@opencode-ai/ui/spinner" import { useDialog } from "@opencode-ai/ui/context/dialog" import { ModelSelectorPopover } from "@/components/dialog-select-model" import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid" @@ -60,6 +61,7 @@ import { ImagePreview } from "@opencode-ai/ui/image-preview" interface PromptInputProps { class?: string ref?: (el: HTMLDivElement) => void + reverting: boolean newSessionWorktree?: string onNewSessionWorktreeReset?: () => void edit?: { id: string; prompt: Prompt; context: FollowupDraft["context"] } @@ -245,6 +247,8 @@ export const PromptInput: Component = (props) => { ) const working = createMemo(() => status()?.type !== "idle") const tip = () => { + if (props.reverting) return "Reverting..." + if (working()) { return (
@@ -1244,6 +1248,7 @@ export const PromptInput: Component = (props) => { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault() if (event.repeat) return + if (props.reverting) return if ( working() && prompt @@ -1278,7 +1283,13 @@ export const PromptInput: Component = (props) => { t={(key) => language.t(key as Parameters[0])} /> { + if (props.reverting) { + event.preventDefault() + return + } + handleSubmit(event) + }} classList={{ "group/prompt-input": true, "focus-within:shadow-xs-border": true, @@ -1395,18 +1406,35 @@ export const PromptInput: Component = (props) => { />
- - + + + } + > + +
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 8c32a7237f58..f375e91ad5a8 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -1951,6 +1951,7 @@ export default function Page() { state={composer} ready={!store.deferRender && messagesReady()} centered={centered()} + reverting={reverting()} inputRef={(el) => { inputRef = el }} diff --git a/packages/app/src/pages/session/composer/session-composer-region.tsx b/packages/app/src/pages/session/composer/session-composer-region.tsx index a5263cd743ec..7d450aa51a33 100644 --- a/packages/app/src/pages/session/composer/session-composer-region.tsx +++ b/packages/app/src/pages/session/composer/session-composer-region.tsx @@ -18,6 +18,7 @@ export function SessionComposerRegion(props: { state: SessionComposerState ready: boolean centered: boolean + reverting: boolean inputRef: (el: HTMLDivElement) => void newSessionWorktree: string onNewSessionWorktreeReset: () => void @@ -237,6 +238,7 @@ export function SessionComposerRegion(props: { Date: Tue, 31 Mar 2026 14:01:54 -0400 Subject: [PATCH 2/7] tweak(app): center revert spinner over send button --- packages/app/src/components/prompt-input.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 21033ea90519..7f4adfec32c7 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1427,12 +1427,15 @@ export const PromptInput: Component = (props) => { data-action="prompt-submit" type="button" variant="primary" - class="size-8 p-0" + class="relative size-8 p-0" disabled style={buttons()} aria-label="Reverting..." > - + +
+ +
From 09de8683b4d643c63c792906bbfc505a2acafa73 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 31 Mar 2026 14:19:12 -0400 Subject: [PATCH 3/7] fix(app): guard prompt submit during revert --- packages/app/src/components/prompt-input.tsx | 1 + packages/app/src/components/prompt-input/submit.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 7f4adfec32c7..f25f4d5c335d 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1084,6 +1084,7 @@ export const PromptInput: Component = (props) => { commentCount, autoAccept: () => accepting(), mode: () => store.mode, + reverting: () => props.reverting, working, editor: () => editorRef, queueScroll, diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index ba299fe3650d..b492cbd31d3d 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -171,6 +171,7 @@ type PromptSubmitInput = { commentCount: Accessor autoAccept: Accessor mode: Accessor<"normal" | "shell"> + reverting?: Accessor working: Accessor editor: () => HTMLDivElement | undefined queueScroll: () => void @@ -283,6 +284,7 @@ export function createPromptSubmit(input: PromptSubmitInput) { const handleSubmit = async (event: Event) => { event.preventDefault() + if (input.reverting?.()) return const currentPrompt = prompt.current() const text = currentPrompt.map((part) => ("content" in part ? part.content : "")).join("") From 25285e8681e4c689fb53a1ca5ab2e8fb1b084b46 Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 1 Apr 2026 12:23:20 -0400 Subject: [PATCH 4/7] refactor(app): remove duplicate submit guard --- packages/app/src/components/prompt-input.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index f25f4d5c335d..9b93fa3d869e 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1284,13 +1284,7 @@ export const PromptInput: Component = (props) => { t={(key) => language.t(key as Parameters[0])} /> { - if (props.reverting) { - event.preventDefault() - return - } - handleSubmit(event) - }} + onSubmit={handleSubmit} classList={{ "group/prompt-input": true, "focus-within:shadow-xs-border": true, From 4dc5e0581bdd21070cb142292cffb6e42ce0c511 Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 1 Apr 2026 12:36:41 -0400 Subject: [PATCH 5/7] tweak(app): localize revert tooltip --- packages/app/src/components/prompt-input.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 9b93fa3d869e..4fe81b3090b7 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -247,7 +247,16 @@ export const PromptInput: Component = (props) => { ) const working = createMemo(() => status()?.type !== "idle") const tip = () => { - if (props.reverting) return "Reverting..." + if (props.reverting) { + return ( +
+ + {language.t("common.loading")} + {language.t("common.loading.ellipsis")} + +
+ ) + } if (working()) { return ( From 31d3e2d795b1d0428007b683911227e75ab18bdd Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 1 Apr 2026 12:42:03 -0400 Subject: [PATCH 6/7] refactor(app): require revert state in submit --- packages/app/src/components/prompt-input/submit.test.ts | 4 ++++ packages/app/src/components/prompt-input/submit.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/app/src/components/prompt-input/submit.test.ts b/packages/app/src/components/prompt-input/submit.test.ts index b0166c43a80f..278a79f5805d 100644 --- a/packages/app/src/components/prompt-input/submit.test.ts +++ b/packages/app/src/components/prompt-input/submit.test.ts @@ -224,6 +224,7 @@ describe("prompt submit worktree selection", () => { commentCount: () => 0, autoAccept: () => false, mode: () => "shell", + reverting: () => false, working: () => false, editor: () => undefined, queueScroll: () => undefined, @@ -261,6 +262,7 @@ describe("prompt submit worktree selection", () => { commentCount: () => 0, autoAccept: () => true, mode: () => "shell", + reverting: () => false, working: () => false, editor: () => undefined, queueScroll: () => undefined, @@ -291,6 +293,7 @@ describe("prompt submit worktree selection", () => { commentCount: () => 0, autoAccept: () => false, mode: () => "normal", + reverting: () => false, working: () => false, editor: () => undefined, queueScroll: () => undefined, @@ -323,6 +326,7 @@ describe("prompt submit worktree selection", () => { commentCount: () => 0, autoAccept: () => false, mode: () => "normal", + reverting: () => false, working: () => false, editor: () => undefined, queueScroll: () => undefined, diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index b492cbd31d3d..858dbb79bedd 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -171,7 +171,7 @@ type PromptSubmitInput = { commentCount: Accessor autoAccept: Accessor mode: Accessor<"normal" | "shell"> - reverting?: Accessor + reverting: Accessor working: Accessor editor: () => HTMLDivElement | undefined queueScroll: () => void @@ -284,7 +284,7 @@ export function createPromptSubmit(input: PromptSubmitInput) { const handleSubmit = async (event: Event) => { event.preventDefault() - if (input.reverting?.()) return + if (input.reverting()) return const currentPrompt = prompt.current() const text = currentPrompt.map((part) => ("content" in part ? part.content : "")).join("") From 800673d36075341cc11ba5c365f76f97e413e8c6 Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 1 Apr 2026 12:48:39 -0400 Subject: [PATCH 7/7] tweak(app): localize revert button label --- packages/app/src/components/prompt-input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 4fe81b3090b7..889b7a84c3e2 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1434,7 +1434,7 @@ export const PromptInput: Component = (props) => { class="relative size-8 p-0" disabled style={buttons()} - aria-label="Reverting..." + aria-label={`${language.t("common.loading")}${language.t("common.loading.ellipsis")}`} >