summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-17 13:10:38 -0600
committerAdam <[email protected]>2026-02-17 13:10:43 -0600
commitbab3124e8b74343fb93a4ff543fb7f9ed1a6f3c3 (patch)
tree93a5a1fecf3c561ebd0bdb1b764f09556b303b22
parent7a66ec6bc9e98c158d56c01ce5f3d23e1f8d512e (diff)
downloadopencode-bab3124e8b74343fb93a4ff543fb7f9ed1a6f3c3.tar.gz
opencode-bab3124e8b74343fb93a4ff543fb7f9ed1a6f3c3.zip
fix(app): prompt input quirks
-rw-r--r--packages/app/src/components/prompt-input.tsx85
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