Skip to content
Open
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
59 changes: 47 additions & 12 deletions packages/app/src/components/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 { useProviders } from "@/hooks/use-providers"
Expand Down Expand Up @@ -59,6 +60,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"] }
Expand Down Expand Up @@ -244,6 +246,17 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
)
const working = createMemo(() => status()?.type !== "idle")
const tip = () => {
if (props.reverting) {
return (
<div class="flex items-center gap-2">
<span>
{language.t("common.loading")}
{language.t("common.loading.ellipsis")}
</span>
</div>
)
}

if (working()) {
return (
<div class="flex items-center gap-2">
Expand Down Expand Up @@ -1080,6 +1093,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
commentCount,
autoAccept: () => accepting(),
mode: () => store.mode,
reverting: () => props.reverting,
working,
editor: () => editorRef,
queueScroll,
Expand Down Expand Up @@ -1244,6 +1258,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault()
if (event.repeat) return
if (props.reverting) return
if (
working() &&
prompt
Expand Down Expand Up @@ -1398,18 +1413,38 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
/>

<div class="flex items-center gap-1 pointer-events-auto">
<Tooltip placement="top" inactive={!prompt.dirty() && !working()} value={tip()}>
<IconButton
data-action="prompt-submit"
type="submit"
disabled={store.mode !== "normal" || (!prompt.dirty() && !working() && commentCount() === 0)}
tabIndex={store.mode === "normal" ? undefined : -1}
icon={working() ? "stop" : "arrow-up"}
variant="primary"
class="size-8"
style={buttons()}
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/>
<Tooltip placement="top" inactive={!props.reverting && !prompt.dirty() && !working()} value={tip()}>
<Show
when={props.reverting}
fallback={
<IconButton
data-action="prompt-submit"
type="submit"
disabled={store.mode !== "normal" || (!prompt.dirty() && !working() && commentCount() === 0)}
tabIndex={store.mode === "normal" ? undefined : -1}
icon={working() ? "stop" : "arrow-up"}
variant="primary"
class="size-8"
style={buttons()}
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/>
}
>
<Button
data-action="prompt-submit"
type="button"
variant="primary"
class="relative size-8 p-0"
disabled
style={buttons()}
aria-label={`${language.t("common.loading")}${language.t("common.loading.ellipsis")}`}
>
<Icon name="arrow-up" class="opacity-0" />
<div class="absolute inset-0 flex items-center justify-center">
<Spinner class="size-3.5" />
</div>
</Button>
</Show>
</Tooltip>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions packages/app/src/components/prompt-input/submit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ describe("prompt submit worktree selection", () => {
commentCount: () => 0,
autoAccept: () => false,
mode: () => "shell",
reverting: () => false,
working: () => false,
editor: () => undefined,
queueScroll: () => undefined,
Expand Down Expand Up @@ -261,6 +262,7 @@ describe("prompt submit worktree selection", () => {
commentCount: () => 0,
autoAccept: () => true,
mode: () => "shell",
reverting: () => false,
working: () => false,
editor: () => undefined,
queueScroll: () => undefined,
Expand Down Expand Up @@ -291,6 +293,7 @@ describe("prompt submit worktree selection", () => {
commentCount: () => 0,
autoAccept: () => false,
mode: () => "normal",
reverting: () => false,
working: () => false,
editor: () => undefined,
queueScroll: () => undefined,
Expand Down Expand Up @@ -323,6 +326,7 @@ describe("prompt submit worktree selection", () => {
commentCount: () => 0,
autoAccept: () => false,
mode: () => "normal",
reverting: () => false,
working: () => false,
editor: () => undefined,
queueScroll: () => undefined,
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/components/prompt-input/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ type PromptSubmitInput = {
commentCount: Accessor<number>
autoAccept: Accessor<boolean>
mode: Accessor<"normal" | "shell">
reverting: Accessor<boolean>
working: Accessor<boolean>
editor: () => HTMLDivElement | undefined
queueScroll: () => void
Expand Down Expand Up @@ -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("")
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/pages/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,7 @@ export default function Page() {
state={composer}
ready={!store.deferRender && messagesReady()}
centered={centered()}
reverting={reverting()}
inputRef={(el) => {
inputRef = el
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function SessionComposerRegion(props: {
state: SessionComposerState
ready: boolean
centered: boolean
reverting: boolean
inputRef: (el: HTMLDivElement) => void
newSessionWorktree: string
onNewSessionWorktreeReset: () => void
Expand Down Expand Up @@ -237,6 +238,7 @@ export function SessionComposerRegion(props: {
</Show>
<PromptInput
ref={props.inputRef}
reverting={props.reverting}
newSessionWorktree={props.newSessionWorktree}
onNewSessionWorktreeReset={props.onNewSessionWorktreeReset}
edit={props.followup?.edit}
Expand Down
Loading