Apply PR #11615: fix(app): mobile prompt input buttons overflow on narrow viewports

This commit is contained in:
opencode-agent[bot]
2026-02-01 14:06:37 +00:00
4 changed files with 74 additions and 31 deletions

View File

@@ -118,6 +118,17 @@ export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
}
const language = useLanguage()
// Set data-active on trigger when popover is open
createEffect(() => {
const trigger = store.trigger
if (!trigger) return
if (store.open) {
trigger.setAttribute("data-active", "true")
} else {
trigger.removeAttribute("data-active")
}
})
createEffect(() => {
if (!store.open) return
@@ -174,7 +185,8 @@ export function ModelSelectorPopover<T extends ValidComponent = "div">(props: {
setStore("open", next)
}}
modal={false}
placement="top-start"
placement="top"
flip
gutter={8}
>
<Kobalte.Trigger

View File

@@ -42,6 +42,7 @@ import { Select } from "@opencode-ai/ui/select"
import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/util/path"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { ImagePreview } from "@opencode-ai/ui/image-preview"
import { ModelSelectorPopover } from "@/components/dialog-select-model"
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
import { useProviders } from "@/hooks/use-providers"
@@ -1616,6 +1617,15 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
})
}
const currentModelVariant = createMemo(() => {
const modelVariant = local.model.variant.current() ?? ""
return modelVariant === "xhigh"
? "xHigh"
: modelVariant.length > 0
? modelVariant[0].toUpperCase() + modelVariant.slice(1)
: "Default"
})
return (
<div class="relative size-full _max-h-[320px] flex flex-col gap-3">
<Show when={store.popover}>
@@ -1891,7 +1901,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</Show>
</div>
<div class="relative p-3 flex items-center justify-between">
<div class="flex items-center justify-start gap-0.5">
<div class="flex items-center justify-start gap-1 sm:gap-0.5 min-w-0">
<Switch>
<Match when={store.mode === "shell"}>
<div class="flex items-center gap-2 px-2 h-6">
@@ -1910,7 +1920,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
options={local.agent.list().map((agent) => agent.name)}
current={local.agent.current()?.name ?? ""}
onSelect={local.agent.set}
class="capitalize"
class="capitalize shrink-0"
variant="ghost"
/>
</TooltipKeybind>
@@ -1922,12 +1932,19 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
title={language.t("command.model.choose")}
keybind={command.keybind("model.choose")}
>
<Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
<Button
as="div"
variant="ghost"
class="shrink-0"
onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}
>
<Show when={local.model.current()?.provider?.id}>
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
</Show>
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
<Icon name="chevron-down" size="small" />
<span class="hidden sm:inline">
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
</span>
<Icon name="chevron-down" class="size-3.5" />
</Button>
</TooltipKeybind>
}
@@ -1937,12 +1954,20 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
title={language.t("command.model.choose")}
keybind={command.keybind("model.choose")}
>
<ModelSelectorPopover triggerAs={Button} triggerProps={{ variant: "ghost" }}>
<ModelSelectorPopover
triggerAs={Button}
triggerProps={{
variant: "ghost",
class: "shrink-0",
}}
>
<Show when={local.model.current()?.provider?.id}>
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
</Show>
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
<Icon name="chevron-down" size="small" />
<span class="hidden sm:inline">
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
</span>
<Icon name="chevron-down" class="size-3.5" />
</ModelSelectorPopover>
</TooltipKeybind>
</Show>
@@ -1952,14 +1977,22 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
title={language.t("command.model.variant.cycle")}
keybind={command.keybind("model.variant.cycle")}
>
<Button
data-action="model-variant-cycle"
<Select
options={local.model.variant
.list()
.map((v) => (v === "" ? "Default" : v === "xhigh" ? "xHigh" : v[0].toUpperCase() + v.slice(1)))}
current={currentModelVariant()}
onSelect={(display) => {
if (!display) return
const variant =
display === "Default" ? "" : display === "xHigh" ? "xhigh" : display.toLowerCase()
local.model.variant.set(variant)
}}
class="shrink-0"
variant="ghost"
class="text-text-base _hidden group-hover/prompt-input:inline-block capitalize text-12-regular"
onClick={() => local.model.variant.cycle()}
>
{local.model.variant.current() ?? language.t("common.default")}
</Button>
icon={<Icon name="bulb" class="size-4 shrink-0" />}
valueClass="hidden sm:inline"
/>
</TooltipKeybind>
</Show>
<Show when={permission.permissionsEnabled() && params.id}>
@@ -1994,7 +2027,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</Match>
</Switch>
</div>
<div class="flex items-center gap-3 absolute right-3 bottom-3">
<div class="flex items-center gap-1 sm:gap-3 shrink-0">
<input
ref={fileInputRef}
type="file"
@@ -2006,18 +2039,17 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
e.currentTarget.value = ""
}}
/>
<div class="flex items-center gap-2">
<div class="flex items-center gap-1 sm:gap-2">
<SessionContextUsage />
<Show when={store.mode === "normal"}>
<Tooltip placement="top" value={language.t("prompt.action.attachFile")}>
<Button
type="button"
variant="ghost"
class="size-6"
onClick={() => fileInputRef.click()}
aria-label={language.t("prompt.action.attachFile")}
>
<Icon name="photo" class="size-4.5" />
<Icon name="photo" />
</Button>
</Tooltip>
</Show>
@@ -2047,7 +2079,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
disabled={!prompt.dirty() && !working()}
icon={working() ? "stop" : "arrow-up"}
variant="primary"
class="h-6 w-4.5"
aria-label={working() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/>
</Tooltip>

View File

@@ -9,6 +9,7 @@ const icons = {
"bubble-5": `<path d="M18.3327 9.99935C18.3327 5.57227 15.0919 2.91602 9.99935 2.91602C4.90676 2.91602 1.66602 5.57227 1.66602 9.99935C1.66602 11.1487 2.45505 13.1006 2.57637 13.3939C2.58707 13.4197 2.59766 13.4434 2.60729 13.4697C2.69121 13.6987 3.04209 14.9354 1.66602 16.7674C3.51787 17.6528 5.48453 16.1973 5.48453 16.1973C6.84518 16.9193 8.46417 17.0827 9.99935 17.0827C15.0919 17.0827 18.3327 14.4264 18.3327 9.99935Z" stroke="currentColor" stroke-linecap="square"/>`,
brain: `<path d="M13.332 8.7487C11.4911 8.7487 9.9987 7.25631 9.9987 5.41536M6.66536 11.2487C8.50631 11.2487 9.9987 12.7411 9.9987 14.582M9.9987 2.78209L9.9987 17.0658M16.004 15.0475C17.1255 14.5876 17.9154 13.4849 17.9154 12.1978C17.9154 11.3363 17.5615 10.5575 16.9913 9.9987C17.5615 9.43991 17.9154 8.66108 17.9154 7.79962C17.9154 6.21199 16.7136 4.90504 15.1702 4.73878C14.7858 3.21216 13.4039 2.08203 11.758 2.08203C11.1171 2.08203 10.5162 2.25337 9.9987 2.55275C9.48117 2.25337 8.88032 2.08203 8.23944 2.08203C6.59353 2.08203 5.21157 3.21216 4.82722 4.73878C3.28377 4.90504 2.08203 6.21199 2.08203 7.79962C2.08203 8.66108 2.43585 9.43991 3.00609 9.9987C2.43585 10.5575 2.08203 11.3363 2.08203 12.1978C2.08203 13.4849 2.87191 14.5876 3.99339 15.0475C4.46688 16.7033 5.9917 17.9154 7.79962 17.9154C8.61335 17.9154 9.36972 17.6698 9.9987 17.2488C10.6277 17.6698 11.384 17.9154 12.1978 17.9154C14.0057 17.9154 15.5305 16.7033 16.004 15.0475Z" stroke="currentColor"/>`,
"bullet-list": `<path d="M9.58329 13.7497H17.0833M9.58329 6.24967H17.0833M6.24996 6.24967C6.24996 7.17015 5.50377 7.91634 4.58329 7.91634C3.66282 7.91634 2.91663 7.17015 2.91663 6.24967C2.91663 5.3292 3.66282 4.58301 4.58329 4.58301C5.50377 4.58301 6.24996 5.3292 6.24996 6.24967ZM6.24996 13.7497C6.24996 14.6701 5.50377 15.4163 4.58329 15.4163C3.66282 15.4163 2.91663 14.6701 2.91663 13.7497C2.91663 12.8292 3.66282 12.083 4.58329 12.083C5.50377 12.083 6.24996 12.8292 6.24996 13.7497Z" stroke="currentColor" stroke-linecap="square"/>`,
bulb: `<path d="M7.29 12.9V13.96C7.29 14.65 7.85 15.21 8.54 15.21H11.46C12.15 15.21 12.71 14.65 12.71 13.96V12.9M7.29 12.9C6.95 12.73 6.62 12.52 6.32 12.29C4.89 11.18 4 9.45 4 7.5C4 4.18 6.69 1.5 10 1.5C13.31 1.5 16 4.18 16 7.5C16 9.45 15.11 11.18 13.68 12.29C13.38 12.52 13.05 12.73 12.71 12.9M7.29 12.9H12.71M8.125 17.67H11.875" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>`,
"check-small": `<path d="M6.5 11.4412L8.97059 13.5L13.5 6.5" stroke="currentColor" stroke-linecap="square"/>`,
"chevron-down": `<path d="M6.6665 8.33325L9.99984 11.6666L13.3332 8.33325" stroke="currentColor" stroke-linecap="square"/>`,
"chevron-right": `<path d="M8.33301 13.3327L11.6663 9.99935L8.33301 6.66602" stroke="currentColor" stroke-linecap="square"/>`,

View File

@@ -18,6 +18,8 @@ export type SelectProps<T> = Omit<ComponentProps<typeof Kobalte<T>>, "value" | "
children?: (item: T | undefined) => JSX.Element
triggerStyle?: JSX.CSSProperties
triggerVariant?: "settings"
icon?: JSX.Element
valueClass?: string
}
export function Select<T>(props: SelectProps<T> & Omit<ButtonProps, "children">) {
@@ -36,6 +38,8 @@ export function Select<T>(props: SelectProps<T> & Omit<ButtonProps, "children">)
"children",
"triggerStyle",
"triggerVariant",
"icon",
"valueClass",
])
const state = {
@@ -84,7 +88,8 @@ export function Select<T>(props: SelectProps<T> & Omit<ButtonProps, "children">)
{...others}
data-component="select"
data-trigger-style={local.triggerVariant}
placement={local.triggerVariant === "settings" ? "bottom-end" : "bottom-start"}
placement={local.triggerVariant === "settings" ? "bottom-end" : "top-start"}
flip
gutter={4}
value={local.current}
options={grouped()}
@@ -140,7 +145,8 @@ export function Select<T>(props: SelectProps<T> & Omit<ButtonProps, "children">)
[local.class ?? ""]: !!local.class,
}}
>
<Kobalte.Value<T> data-slot="select-select-trigger-value">
{local.icon}
<Kobalte.Value<T> data-slot="select-select-trigger-value" class={local.valueClass}>
{(state) => {
const selected = state.selectedOption() ?? local.current
if (!selected) return local.placeholder || ""
@@ -153,14 +159,7 @@ export function Select<T>(props: SelectProps<T> & Omit<ButtonProps, "children">)
</Kobalte.Icon>
</Kobalte.Trigger>
<Kobalte.Portal>
<Kobalte.Content
classList={{
...(local.classList ?? {}),
[local.class ?? ""]: !!local.class,
}}
data-component="select-content"
data-trigger-style={local.triggerVariant}
>
<Kobalte.Content data-component="select-content" data-trigger-style={local.triggerVariant}>
<Kobalte.Listbox data-slot="select-select-content-list" />
</Kobalte.Content>
</Kobalte.Portal>