summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Hill <[email protected]>2026-03-13 15:48:10 +0000
committerGitHub <[email protected]>2026-03-13 10:48:10 -0500
commit5c7088338c07ad632834ebd4a87feb23d255fb8a (patch)
tree66e378a6d8bb71642b2d594e221a1856e1e42429
parent389daa03df9ba591b4629655e77c9229ec3fa184 (diff)
downloadopencode-5c7088338c07ad632834ebd4a87feb23d255fb8a.tar.gz
opencode-5c7088338c07ad632834ebd4a87feb23d255fb8a.zip
fix(app): polish prompt composer controls (#17388)
-rw-r--r--packages/app/src/components/prompt-input.tsx123
-rw-r--r--packages/ui/src/components/icon.tsx1
2 files changed, 60 insertions, 64 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index 3b78e8033..fd54de9a0 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -121,7 +121,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
let slashPopoverRef!: HTMLDivElement
const mirror = { input: false }
- const inset = 52
+ const inset = 56
const space = `${inset}px`
const scrollCursorIntoView = () => {
@@ -1031,6 +1031,17 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (!id) return permission.isAutoAcceptingDirectory(sdk.directory)
return permission.isAutoAccepting(id, sdk.directory)
})
+ const acceptLabel = createMemo(() =>
+ language.t(accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable"),
+ )
+ const toggleAccept = () => {
+ if (!params.id) {
+ permission.toggleAutoAcceptDirectory(sdk.directory)
+ return
+ }
+
+ permission.toggleAutoAccept(params.id, sdk.directory)
+ }
const { abort, handleSubmit } = createPromptSubmit({
info,
@@ -1337,33 +1348,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}}
/>
- <div
- aria-hidden={store.mode !== "normal"}
- class="flex items-center gap-1"
- style={{
- "pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
- }}
- >
- <TooltipKeybind
- placement="top"
- title={language.t("prompt.action.attachFile")}
- keybind={command.keybind("file.attach")}
- >
- <Button
- data-action="prompt-attach"
- type="button"
- variant="ghost"
- class="size-8 p-0"
- style={buttons()}
- onClick={pick}
- disabled={store.mode !== "normal"}
- tabIndex={store.mode === "normal" ? undefined : -1}
- aria-label={language.t("prompt.action.attachFile")}
- >
- <Icon name="plus" class="size-4.5" />
- </Button>
- </TooltipKeybind>
-
+ <div class="flex items-center gap-1 pointer-events-auto">
<Tooltip
placement="top"
inactive={!prompt.dirty() && !working()}
@@ -1400,42 +1385,30 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</div>
<div class="pointer-events-none absolute bottom-2 left-2">
- <div class="pointer-events-auto">
+ <div
+ aria-hidden={store.mode !== "normal"}
+ class="pointer-events-auto"
+ style={{
+ "pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
+ }}
+ >
<TooltipKeybind
placement="top"
- gutter={8}
- title={language.t(
- accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable",
- )}
- keybind={command.keybind("permissions.autoaccept")}
+ title={language.t("prompt.action.attachFile")}
+ keybind={command.keybind("file.attach")}
>
<Button
- data-action="prompt-permissions"
+ data-action="prompt-attach"
+ type="button"
variant="ghost"
- onClick={() => {
- if (!params.id) {
- permission.toggleAutoAcceptDirectory(sdk.directory)
- return
- }
- permission.toggleAutoAccept(params.id, sdk.directory)
- }}
- classList={{
- "size-6 flex items-center justify-center": true,
- "text-text-base": !accepting(),
- "hover:bg-surface-success-base": accepting(),
- }}
- aria-label={
- accepting()
- ? language.t("command.permissions.autoaccept.disable")
- : language.t("command.permissions.autoaccept.enable")
- }
- aria-pressed={accepting()}
+ class="size-8 p-0"
+ style={buttons()}
+ onClick={pick}
+ disabled={store.mode !== "normal"}
+ tabIndex={store.mode === "normal" ? undefined : -1}
+ aria-label={language.t("prompt.action.attachFile")}
>
- <Icon
- name="chevron-double-right"
- size="small"
- classList={{ "text-icon-success-base": accepting() }}
- />
+ <Icon name="plus" class="size-4.5" />
</Button>
</TooltipKeybind>
</div>
@@ -1468,8 +1441,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
options={agentNames()}
current={local.agent.current()?.name ?? ""}
onSelect={local.agent.set}
- class="capitalize max-w-[160px]"
- valueClass="truncate text-13-regular"
+ class="capitalize max-w-[160px] text-text-base"
+ valueClass="truncate text-13-regular text-text-base"
triggerStyle={control()}
variant="ghost"
/>
@@ -1487,7 +1460,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
as="div"
variant="ghost"
size="normal"
- class="min-w-0 max-w-[320px] text-13-regular group"
+ class="min-w-0 max-w-[320px] text-13-regular text-text-base group"
style={control()}
onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}
>
@@ -1518,7 +1491,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
variant: "ghost",
size: "normal",
style: control(),
- class: "min-w-0 max-w-[320px] text-13-regular group",
+ class: "min-w-0 max-w-[320px] text-13-regular text-text-base group",
}}
>
<Show when={local.model.current()?.provider?.id}>
@@ -1547,12 +1520,34 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
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"
+ class="capitalize max-w-[160px] text-text-base"
+ valueClass="truncate text-13-regular text-text-base"
triggerStyle={control()}
variant="ghost"
/>
</TooltipKeybind>
+ <TooltipKeybind
+ placement="top"
+ gutter={8}
+ title={acceptLabel()}
+ keybind={command.keybind("permissions.autoaccept")}
+ >
+ <Button
+ data-action="prompt-permissions"
+ variant="ghost"
+ onClick={toggleAccept}
+ classList={{
+ "h-7 w-7 p-0 shrink-0 flex items-center justify-center": true,
+ "text-text-base": !accepting(),
+ "hover:bg-surface-success-base": accepting(),
+ }}
+ style={control()}
+ aria-label={acceptLabel()}
+ aria-pressed={accepting()}
+ >
+ <Icon name="shield" size="small" classList={{ "text-icon-success-base": accepting() }} />
+ </Button>
+ </TooltipKeybind>
</div>
</div>
</div>
diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx
index aacfa476b..e2eaf107a 100644
--- a/packages/ui/src/components/icon.tsx
+++ b/packages/ui/src/components/icon.tsx
@@ -82,6 +82,7 @@ const icons = {
check: `<path d="M5 11.9657L8.37838 14.7529L15 5.83398" stroke="currentColor" stroke-linecap="square"/>`,
photo: `<path d="M16.6665 16.6666L11.6665 11.6666L9.99984 13.3333L6.6665 9.99996L3.08317 13.5833M2.9165 2.91663H17.0832V17.0833H2.9165V2.91663ZM13.3332 7.49996C13.3332 8.30537 12.6803 8.95829 11.8748 8.95829C11.0694 8.95829 10.4165 8.30537 10.4165 7.49996C10.4165 6.69454 11.0694 6.04163 11.8748 6.04163C12.6803 6.04163 13.3332 6.69454 13.3332 7.49996Z" stroke="currentColor" stroke-linecap="square"/>`,
share: `<path d="M10.0013 12.0846L10.0013 3.33464M13.7513 6.66797L10.0013 2.91797L6.2513 6.66797M17.0846 10.418V17.0846H2.91797V10.418" stroke="currentColor" stroke-linecap="square"/>`,
+ shield: `<path d="M7.49935 9.3737L9.16602 11.0404L12.4994 7.70703M9.99935 2.08203L17.0827 4.3737V9.92565C17.0827 14.0694 13.3327 16.2487 9.99935 18.047C6.66602 16.2487 2.91602 14.0694 2.91602 9.92565V4.3737L9.99935 2.08203Z" stroke="currentColor" stroke-linecap="square"/>`,
download: `<path d="M13.9583 10.6257L10 14.584L6.04167 10.6257M10 2.08398V13.959M16.25 17.9173H3.75" stroke="currentColor" stroke-linecap="square"/>`,
menu: `<path d="M2.5 5H17.5M2.5 10H17.5M2.5 15H17.5" stroke="currentColor" stroke-linecap="square"/>`,
server: `<rect x="3.35547" y="1.92969" width="13.2857" height="16.1429" stroke="currentColor"/><rect x="3.35547" y="11.9297" width="13.2857" height="6.14286" stroke="currentColor"/><rect x="12.8555" y="14.2852" width="1.42857" height="1.42857" fill="currentColor"/><rect x="10" y="14.2852" width="1.42857" height="1.42857" fill="currentColor"/>`,