summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Hill <[email protected]>2026-03-08 01:27:45 +0000
committerDavid Hill <[email protected]>2026-03-08 01:27:45 +0000
commit09388c98f3bbf737615845e3d40c2adfe66089f7 (patch)
tree7527ed5793345b7cb110420f6cc9d0bdc6a929fb
parentae25c1e7b75c17d799f7a245f03ca0cd8bef1eab (diff)
downloadopencode-09388c98f3bbf737615845e3d40c2adfe66089f7.tar.gz
opencode-09388c98f3bbf737615845e3d40c2adfe66089f7.zip
Revert "tui: remove prompt model/thinking/permissions selectors on dev so the composer stays simple"
This reverts commit ae25c1e7b75c17d799f7a245f03ca0cd8bef1eab.
-rw-r--r--packages/app/src/components/prompt-input.tsx149
1 files changed, 148 insertions, 1 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index de7a219c8..3ae362e6f 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -23,11 +23,15 @@ import { useComments } from "@/context/comments"
import { Button } from "@opencode-ai/ui/button"
import { DockShellForm, DockTray } from "@opencode-ai/ui/dock-surface"
import { Icon } from "@opencode-ai/ui/icon"
+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 { RadioGroup } from "@opencode-ai/ui/radio-group"
import { useDialog } from "@opencode-ai/ui/context/dialog"
+import { ModelSelectorPopover } from "@/components/dialog-select-model"
+import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
+import { useProviders } from "@/hooks/use-providers"
import { useCommand } from "@/context/command"
import { Persist, persisted } from "@/utils/persist"
import { usePermission } from "@/context/permission"
@@ -100,6 +104,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const comments = useComments()
const params = useParams()
const dialog = useDialog()
+ const providers = useProviders()
const command = useCommand()
const permission = usePermission()
const language = useLanguage()
@@ -945,12 +950,21 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
readClipboardImage: platform.readClipboardImage,
})
+ const variants = createMemo(() => ["default", ...local.model.variant.list()])
const accepting = createMemo(() => {
const id = params.id
if (!id) return permission.isAutoAcceptingDirectory(sdk.directory)
return permission.isAutoAccepting(id, sdk.directory)
})
+ const flip = () => {
+ if (!params.id) {
+ permission.toggleAutoAcceptDirectory(sdk.directory)
+ return
+ }
+ permission.toggleAutoAccept(params.id, sdk.directory)
+ }
+
const { abort, handleSubmit } = createPromptSubmit({
info,
imageAttachments,
@@ -1176,7 +1190,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
onMouseDown={(e) => {
const target = e.target
if (!(target instanceof HTMLElement)) return
- if (target.closest('[data-action="prompt-attach"], [data-action="prompt-submit"]')) {
+ if (
+ target.closest(
+ '[data-action="prompt-attach"], [data-action="prompt-submit"], [data-action="prompt-permissions"]',
+ )
+ ) {
return
}
editorRef?.focus()
@@ -1310,6 +1328,99 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<Icon name="plus" class="size-4.5" />
</Button>
</TooltipKeybind>
+
+ <Show
+ when={providers.paid().length > 0}
+ fallback={
+ <TooltipKeybind
+ placement="top"
+ gutter={8}
+ title={language.t("command.model.choose")}
+ keybind={command.keybind("model.choose")}
+ >
+ <Button
+ as="div"
+ variant="ghost"
+ size="small"
+ class="min-w-0 max-w-[240px] text-13-regular text-text-base group"
+ style={{
+ opacity: buttonsSpring(),
+ transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
+ filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
+ }}
+ onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}
+ >
+ <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={8}
+ title={language.t("command.model.choose")}
+ keybind={command.keybind("model.choose")}
+ >
+ <ModelSelectorPopover
+ triggerAs={Button}
+ triggerProps={{
+ variant: "ghost",
+ size: "small",
+ style: {
+ opacity: buttonsSpring(),
+ transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
+ filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
+ },
+ class: "min-w-0 max-w-[240px] text-13-regular text-text-base group",
+ }}
+ >
+ <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>
+ </TooltipKeybind>
+ </Show>
+
+ <TooltipKeybind
+ placement="top"
+ gutter={8}
+ title={language.t("command.model.variant.cycle")}
+ keybind={command.keybind("model.variant.cycle")}
+ >
+ <Select
+ size="small"
+ 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]"
+ valueClass="truncate text-13-regular text-text-base"
+ triggerStyle={{
+ opacity: buttonsSpring(),
+ transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
+ filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
+ }}
+ variant="ghost"
+ />
+ </TooltipKeybind>
</div>
</div>
</div>
@@ -1355,6 +1466,42 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
variant="ghost"
/>
</TooltipKeybind>
+
+ <TooltipKeybind
+ placement="top"
+ gutter={4}
+ title={language.t(
+ accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable",
+ )}
+ keybind={command.keybind("permissions.autoaccept")}
+ >
+ <Select
+ size="normal"
+ options={["default", "autoaccept"] as const}
+ current={accepting() ? "autoaccept" : "default"}
+ label={(x) =>
+ x === "autoaccept"
+ ? language.t("command.permissions.autoaccept.enable")
+ : `${language.t("common.default")} ${language.t("command.category.permissions")}`
+ }
+ onSelect={(x) => {
+ if (!x) return
+ if (x === "autoaccept" && accepting()) return
+ if (x === "default" && !accepting()) return
+ flip()
+ }}
+ class="max-w-[220px]"
+ valueClass="truncate text-13-regular text-text-base"
+ triggerStyle={{
+ height: "28px",
+ opacity: buttonsSpring(),
+ transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
+ filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
+ "pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
+ }}
+ variant="ghost"
+ />
+ </TooltipKeybind>
</div>
</div>
<div class="shrink-0">