diff options
| author | Shoubhit Dash <[email protected]> | 2026-04-03 18:46:26 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-03 13:16:26 +0000 |
| commit | 2002f08f2ed564f1d0148101bfd8f261a216e20c (patch) | |
| tree | c1cdcf21815b0463b7bb58f84b26b649f4e2ed8a /packages/app | |
| parent | c307505f8b3629a3ffda291fa8496f474c3e097d (diff) | |
| download | opencode-2002f08f2ed564f1d0148101bfd8f261a216e20c.tar.gz opencode-2002f08f2ed564f1d0148101bfd8f261a216e20c.zip | |
fix(prompt): unmount model controls in shell mode (#20886)
Diffstat (limited to 'packages/app')
| -rw-r--r-- | packages/app/e2e/prompt/prompt-shell.spec.ts | 32 | ||||
| -rw-r--r-- | packages/app/src/components/prompt-input.tsx | 134 |
2 files changed, 90 insertions, 76 deletions
diff --git a/packages/app/e2e/prompt/prompt-shell.spec.ts b/packages/app/e2e/prompt/prompt-shell.spec.ts index d81f1d4c4..28fa02dcd 100644 --- a/packages/app/e2e/prompt/prompt-shell.spec.ts +++ b/packages/app/e2e/prompt/prompt-shell.spec.ts @@ -1,6 +1,7 @@ import type { ToolPart } from "@opencode-ai/sdk/v2/client" import { test, expect } from "../fixtures" import { withSession } from "../actions" +import { promptModelSelector, promptSelector, promptVariantSelector } from "../selectors" const isBash = (part: unknown): part is ToolPart => { if (!part || typeof part !== "object") return false @@ -9,15 +10,6 @@ const isBash = (part: unknown): part is ToolPart => { return "state" in part } -async function setAutoAccept(page: Parameters<typeof test>[0]["page"], enabled: boolean) { - const button = page.locator('[data-action="prompt-permissions"]').first() - await expect(button).toBeVisible() - const pressed = (await button.getAttribute("aria-pressed")) === "true" - if (pressed === enabled) return - await button.click() - await expect(button).toHaveAttribute("aria-pressed", enabled ? "true" : "false") -} - test("shell mode runs a command in the project directory", async ({ page, project }) => { test.setTimeout(120_000) @@ -27,7 +19,12 @@ test("shell mode runs a command in the project directory", async ({ page, projec await withSession(project.sdk, `e2e shell ${Date.now()}`, async (session) => { project.trackSession(session.id) await project.gotoSession(session.id) - await setAutoAccept(page, true) + const button = page.locator('[data-action="prompt-permissions"]').first() + await expect(button).toBeVisible() + if ((await button.getAttribute("aria-pressed")) !== "true") { + await button.click() + await expect(button).toHaveAttribute("aria-pressed", "true") + } await project.shell(cmd) await expect @@ -57,3 +54,18 @@ test("shell mode runs a command in the project directory", async ({ page, projec .toEqual(expect.objectContaining({ cwd: project.directory, output: expect.stringContaining("README.md") })) }) }) + +test("shell mode unmounts model and variant controls", async ({ page, project }) => { + await project.open() + + const prompt = page.locator(promptSelector).first() + await expect(page.locator(promptModelSelector)).toHaveCount(1) + await expect(page.locator(promptVariantSelector)).toHaveCount(1) + + await prompt.click() + await page.keyboard.type("!") + + await expect(prompt).toHaveAttribute("aria-label", /enter shell command/i) + await expect(page.locator(promptModelSelector)).toHaveCount(0) + await expect(page.locator(promptVariantSelector)).toHaveCount(0) +}) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 338b04ba6..653e89f51 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1480,27 +1480,60 @@ export const PromptInput: Component<PromptInputProps> = (props) => { /> </TooltipKeybind> </div> - <div data-component="prompt-model-control"> - <Show - when={providers.paid().length > 0} - fallback={ + <Show when={store.mode !== "shell"}> + <div data-component="prompt-model-control"> + <Show + when={providers.paid().length > 0} + fallback={ + <TooltipKeybind + placement="top" + gutter={4} + title={language.t("command.model.choose")} + keybind={command.keybind("model.choose")} + > + <Button + data-action="prompt-model" + as="div" + variant="ghost" + size="normal" + class="min-w-0 max-w-[320px] text-13-regular text-text-base group" + style={control()} + onClick={() => { + void import("@/components/dialog-select-model-unpaid").then((x) => { + dialog.show(() => <x.DialogSelectModelUnpaid model={local.model} />) + }) + }} + > + <Show when={local.model.current()?.provider?.id}> + <ProviderIcon + id={local.model.current()?.provider?.id ?? ""} + class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150" + style={{ "will-change": "opacity", transform: "translateZ(0)" }} + /> + </Show> + <span class="truncate"> + {local.model.current()?.name ?? language.t("dialog.model.select.title")} + </span> + <Icon name="chevron-down" size="small" class="shrink-0" /> + </Button> + </TooltipKeybind> + } + > <TooltipKeybind placement="top" gutter={4} title={language.t("command.model.choose")} keybind={command.keybind("model.choose")} > - <Button - data-action="prompt-model" - as="div" - variant="ghost" - size="normal" - class="min-w-0 max-w-[320px] text-13-regular text-text-base group" - style={control()} - onClick={() => { - void import("@/components/dialog-select-model-unpaid").then((x) => { - dialog.show(() => <x.DialogSelectModelUnpaid model={local.model} />) - }) + <ModelSelectorPopover + model={local.model} + triggerAs={Button} + triggerProps={{ + variant: "ghost", + size: "normal", + style: control(), + class: "min-w-0 max-w-[320px] text-13-regular text-text-base group", + "data-action": "prompt-model", }} > <Show when={local.model.current()?.provider?.id}> @@ -1514,63 +1547,32 @@ export const PromptInput: Component<PromptInputProps> = (props) => { {local.model.current()?.name ?? language.t("dialog.model.select.title")} </span> <Icon name="chevron-down" size="small" class="shrink-0" /> - </Button> + </ModelSelectorPopover> </TooltipKeybind> - } - > + </Show> + </div> + <div data-component="prompt-variant-control"> <TooltipKeybind placement="top" gutter={4} - title={language.t("command.model.choose")} - keybind={command.keybind("model.choose")} + title={language.t("command.model.variant.cycle")} + keybind={command.keybind("model.variant.cycle")} > - <ModelSelectorPopover - model={local.model} - triggerAs={Button} - triggerProps={{ - variant: "ghost", - size: "normal", - style: control(), - class: "min-w-0 max-w-[320px] text-13-regular text-text-base group", - "data-action": "prompt-model", - }} - > - <Show when={local.model.current()?.provider?.id}> - <ProviderIcon - id={local.model.current()?.provider?.id ?? ""} - class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150" - style={{ "will-change": "opacity", transform: "translateZ(0)" }} - /> - </Show> - <span class="truncate"> - {local.model.current()?.name ?? language.t("dialog.model.select.title")} - </span> - <Icon name="chevron-down" size="small" class="shrink-0" /> - </ModelSelectorPopover> + <Select + size="normal" + options={variants()} + current={local.model.variant.current() ?? "default"} + label={(x) => (x === "default" ? language.t("common.default") : x)} + onSelect={(x) => local.model.variant.set(x === "default" ? undefined : x)} + class="capitalize max-w-[160px] text-text-base" + valueClass="truncate text-13-regular text-text-base" + triggerStyle={control()} + triggerProps={{ "data-action": "prompt-model-variant" }} + variant="ghost" + /> </TooltipKeybind> - </Show> - </div> - <div data-component="prompt-variant-control"> - <TooltipKeybind - placement="top" - gutter={4} - title={language.t("command.model.variant.cycle")} - keybind={command.keybind("model.variant.cycle")} - > - <Select - size="normal" - options={variants()} - current={local.model.variant.current() ?? "default"} - label={(x) => (x === "default" ? language.t("common.default") : x)} - onSelect={(x) => local.model.variant.set(x === "default" ? undefined : x)} - class="capitalize max-w-[160px] text-text-base" - valueClass="truncate text-13-regular text-text-base" - triggerStyle={control()} - triggerProps={{ "data-action": "prompt-model-variant" }} - variant="ghost" - /> - </TooltipKeybind> - </div> + </div> + </Show> <TooltipKeybind placement="top" gutter={8} |
