diff options
| author | Adam <[email protected]> | 2026-02-17 13:10:38 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-02-17 13:10:43 -0600 |
| commit | bab3124e8b74343fb93a4ff543fb7f9ed1a6f3c3 (patch) | |
| tree | 93a5a1fecf3c561ebd0bdb1b764f09556b303b22 | |
| parent | 7a66ec6bc9e98c158d56c01ce5f3d23e1f8d512e (diff) | |
| download | opencode-bab3124e8b74343fb93a4ff543fb7f9ed1a6f3c3.tar.gz opencode-bab3124e8b74343fb93a4ff543fb7f9ed1a6f3c3.zip | |
fix(app): prompt input quirks
| -rw-r--r-- | packages/app/src/components/prompt-input.tsx | 85 |
1 files changed, 47 insertions, 38 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 91c762590..628a7ad34 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -108,6 +108,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { let slashPopoverRef!: HTMLDivElement const mirror = { input: false } + const inset = 44 const scrollCursorIntoView = () => { const container = scrollRef @@ -117,7 +118,14 @@ export const PromptInput: Component<PromptInputProps> = (props) => { const range = selection.getRangeAt(0) if (!editorRef.contains(range.startContainer)) return - const rect = range.getBoundingClientRect() + const cursor = getCursorPosition(editorRef) + const length = promptLength(prompt.current().filter((part) => part.type !== "image")) + if (cursor >= length) { + container.scrollTop = container.scrollHeight + return + } + + const rect = range.getClientRects().item(0) ?? range.getBoundingClientRect() if (!rect.height) return const containerRect = container.getBoundingClientRect() @@ -130,8 +138,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => { return } - if (bottom > container.scrollTop + container.clientHeight - padding) { - container.scrollTop = bottom - container.clientHeight + padding + if (bottom > container.scrollTop + container.clientHeight - inset) { + container.scrollTop = bottom - container.clientHeight + inset } } @@ -1059,8 +1067,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { removeLabel={language.t("prompt.attachment.remove")} /> <div - class="relative max-h-[240px] overflow-y-auto" - ref={(el) => (scrollRef = el)} + class="relative" onMouseDown={(e) => { const target = e.target if (!(target instanceof HTMLElement)) return @@ -1074,40 +1081,42 @@ export const PromptInput: Component<PromptInputProps> = (props) => { editorRef?.focus() }} > - <div - data-component="prompt-input" - ref={(el) => { - editorRef = el - props.ref?.(el) - }} - role="textbox" - aria-multiline="true" - aria-label={placeholder()} - contenteditable="true" - autocapitalize="off" - autocorrect="off" - spellcheck={false} - onInput={handleInput} - onPaste={handlePaste} - onCompositionStart={() => setComposing(true)} - onCompositionEnd={() => setComposing(false)} - onKeyDown={handleKeyDown} - classList={{ - "select-text": true, - "w-full pl-3 pr-2 pt-2 pb-12 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true, - "[&_[data-type=file]]:text-syntax-property": true, - "[&_[data-type=agent]]:text-syntax-type": true, - "font-mono!": store.mode === "shell", - }} - /> - <Show when={!prompt.dirty()}> + <div class="relative max-h-[240px] overflow-y-auto no-scrollbar" ref={(el) => (scrollRef = el)}> <div - class="absolute top-0 inset-x-0 pl-3 pr-2 pt-2 pb-12 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate" - classList={{ "font-mono!": store.mode === "shell" }} - > - {placeholder()} - </div> - </Show> + data-component="prompt-input" + ref={(el) => { + editorRef = el + props.ref?.(el) + }} + role="textbox" + aria-multiline="true" + aria-label={placeholder()} + contenteditable="true" + autocapitalize="off" + autocorrect="off" + spellcheck={false} + onInput={handleInput} + onPaste={handlePaste} + onCompositionStart={() => setComposing(true)} + onCompositionEnd={() => setComposing(false)} + onKeyDown={handleKeyDown} + classList={{ + "select-text": true, + "w-full pl-3 pr-2 pt-2 pb-11 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true, + "[&_[data-type=file]]:text-syntax-property": true, + "[&_[data-type=agent]]:text-syntax-type": true, + "font-mono!": store.mode === "shell", + }} + /> + <Show when={!prompt.dirty()}> + <div + class="absolute top-0 inset-x-0 pl-3 pr-2 pt-2 pb-11 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate" + classList={{ "font-mono!": store.mode === "shell" }} + > + {placeholder()} + </div> + </Show> + </div> <div class="pointer-events-none absolute bottom-2 right-2 flex items-center gap-2"> <input |
