Compare commits

...

1 Commits

Author SHA1 Message Date
Luke Parker 3a22a0251e fix(app): use custom composer scrollbar 2026-07-31 10:11:43 +00:00
@@ -3,6 +3,7 @@ import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon" import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { ScrollView } from "@opencode-ai/ui/scroll-view"
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2" import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon" import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2" import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
@@ -140,44 +141,46 @@ export function PromptInputV2(props: PromptInputV2Props) {
</Show> </Show>
<div class="relative min-h-[60px]"> <div class="relative min-h-[60px]">
<div <ScrollView class="min-h-[60px] max-h-[180px]">
ref={(element) => { <div
editor = element ref={(element) => {
props.controller.setEditor(element) editor = element
renderPromptInputV2Editor(element, props.controller.parts()) props.controller.setEditor(element)
}} renderPromptInputV2Editor(element, props.controller.parts())
data-component="prompt-input" }}
role="textbox" data-component="prompt-input"
aria-multiline="true" role="textbox"
aria-label="Prompt" aria-multiline="true"
contenteditable={!props.disabled && !props.readOnly} aria-label="Prompt"
autocapitalize={state.mode === "normal" ? "sentences" : "off"} contenteditable={!props.disabled && !props.readOnly}
autocorrect={state.mode === "normal" ? "on" : "off"} autocapitalize={state.mode === "normal" ? "sentences" : "off"}
spellcheck={state.mode === "normal"} autocorrect={state.mode === "normal" ? "on" : "off"}
// @ts-expect-error spellcheck={state.mode === "normal"}
autocomplete="off" // @ts-expect-error
class="relative z-10 block min-h-[60px] max-h-[180px] w-full overflow-y-auto whitespace-pre-wrap bg-transparent px-4 pt-4 pb-2 text-[13px] font-[440] leading-5 text-v2-text-text-base focus:outline-none empty:before:content-['\200B'] [&_[data-mention=file]]:text-syntax-property [&_[data-mention=agent]]:text-syntax-type [&_[data-mention=reference]]:text-syntax-keyword" autocomplete="off"
classList={{ "font-mono!": state.mode === "shell", "opacity-50": props.disabled }} class="relative z-10 block min-h-[60px] w-full whitespace-pre-wrap bg-transparent px-4 pt-4 pb-2 text-[13px] font-[440] leading-5 text-v2-text-text-base focus:outline-none empty:before:content-['\200B'] [&_[data-mention=file]]:text-syntax-property [&_[data-mention=agent]]:text-syntax-type [&_[data-mention=reference]]:text-syntax-keyword"
onInput={(event) => { classList={{ "font-mono!": state.mode === "shell", "opacity-50": props.disabled }}
const cursor = promptInputV2Cursor(event.currentTarget) onInput={(event) => {
const prompt = parsePromptInputV2Editor(event.currentTarget) const cursor = promptInputV2Cursor(event.currentTarget)
const images = props.controller.parts().filter((part) => part.type === "image") const prompt = parsePromptInputV2Editor(event.currentTarget)
localInput = true const images = props.controller.parts().filter((part) => part.type === "image")
props.controller.onInput(prompt.map((part) => part.content).join(""), [...prompt, ...images], cursor) localInput = true
}} props.controller.onInput(prompt.map((part) => part.content).join(""), [...prompt, ...images], cursor)
onKeyDown={(event) => { }}
if (props.controller.onKeyDown(event)) return onKeyDown={(event) => {
if (event.key === "Enter" && !event.shiftKey && !event.isComposing) { if (props.controller.onKeyDown(event)) return
event.preventDefault() if (event.key === "Enter" && !event.shiftKey && !event.isComposing) {
if (event.repeat) return event.preventDefault()
props.controller.submit() if (event.repeat) return
} props.controller.submit()
}} }
onKeyUp={updateCursor} }}
onPointerUp={updateCursor} onKeyUp={updateCursor}
onPaste={props.controller.onPaste} onPointerUp={updateCursor}
onFocus={() => props.controller.dispatch({ type: "focus.editor" })} onPaste={props.controller.onPaste}
/> onFocus={() => props.controller.dispatch({ type: "focus.editor" })}
/>
</ScrollView>
<Show when={!props.controller.value()}> <Show when={!props.controller.value()}>
<div <div
class="pointer-events-none absolute inset-x-0 top-0 px-4 pt-4 text-[13px] font-[440] leading-5 text-v2-text-text-faint" class="pointer-events-none absolute inset-x-0 top-0 px-4 pt-4 text-[13px] font-[440] leading-5 text-v2-text-text-faint"