summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src
diff options
context:
space:
mode:
authorDavid Hill <[email protected]>2026-03-08 00:46:26 +0000
committerDavid Hill <[email protected]>2026-03-08 00:53:57 +0000
commit426dcfa3b0cacc79abac6c6f61a03613b572ad64 (patch)
tree578f70c9d49f34d8707622e5cf445b83f9f12b06 /packages/app/src
parent69cb49f7cc6557f8d9529695d7ecc75c0ac8a5aa (diff)
downloadopencode-426dcfa3b0cacc79abac6c6f61a03613b572ad64.tar.gz
opencode-426dcfa3b0cacc79abac6c6f61a03613b572ad64.zip
tui: keep model + thinking selectors beside Add file
People change models and thinking settings while composing, so keeping those controls next to the Add file button avoids hunting in the footer and reduces context switching mid-message.
Diffstat (limited to 'packages/app/src')
-rw-r--r--packages/app/src/components/prompt-input.tsx193
1 files changed, 94 insertions, 99 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index 62b16cd7f..ab12af752 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -1291,8 +1291,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<div class="pointer-events-none absolute bottom-2 left-2">
<div
- aria-hidden={store.mode !== "normal"}
- class="pointer-events-auto"
+ class="pointer-events-auto flex items-center gap-1.5"
style={{
"pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
}}
@@ -1321,6 +1320,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 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 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"
+ triggerStyle={{
+ opacity: buttonsSpring(),
+ transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
+ filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
+ }}
+ variant="ghost"
+ />
+ </TooltipKeybind>
</div>
</div>
</div>
@@ -1366,103 +1458,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
variant="ghost"
/>
</TooltipKeybind>
- <Show
- when={providers.paid().length > 0}
- fallback={
- <TooltipKeybind
- placement="top"
- gutter={4}
- title={language.t("command.model.choose")}
- keybind={command.keybind("model.choose")}
- >
- <Button
- as="div"
- variant="ghost"
- size="normal"
- class="min-w-0 max-w-[320px] text-13-regular group"
- style={{
- height: "28px",
- opacity: buttonsSpring(),
- transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
- filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
- "pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
- }}
- 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={4}
- title={language.t("command.model.choose")}
- keybind={command.keybind("model.choose")}
- >
- <ModelSelectorPopover
- triggerAs={Button}
- triggerProps={{
- variant: "ghost",
- size: "normal",
- style: {
- height: "28px",
- opacity: buttonsSpring(),
- transform: `scale(${0.95 + buttonsSpring() * 0.05})`,
- filter: `blur(${(1 - buttonsSpring()) * 2}px)`,
- "pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
- },
- class: "min-w-0 max-w-[320px] text-13-regular 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={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]"
- valueClass="truncate text-13-regular"
- 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>
<TooltipKeybind
placement="top"