You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tui: clipboard-pasted text lands with multi-second delay in question dialog answer field #44857
When dictating into a question dialog's free-text field (the "type your own answer" option of the multiple-choice question prompt), text inserted via clipboard paste takes several seconds to appear. The same dictation method into the main message composer lands instantly, and manual keyboard typing into the question dialog field is fully responsive — so the delay appears specific to how the question dialog's text input handles pasted input.
Environment
opencode version: 0.0.0-beta-18138
OS: macOS (Darwin 25.6.0, arm64)
Terminal: cmux on the client Mac, connected over SSH to the host running opencode inside a tmux session (TERM=xterm-256color, COLORTERM=truecolor)
Shell: /bin/zsh
Install/channel: beta
Active plugins: none configured
Reproduction
Run the opencode TUI (v0.0.0-beta-18138) in tmux over SSH.
Use Handy (voice-to-text) configured with the clipboard paste insertion method — it copies the transcript to the clipboard and simulates a paste keystroke.
Trigger an agent question dialog (e.g. a tool asking a multiple-choice question) and select the free-text/custom answer option.
Dictate a response so Handy pastes it into the custom-answer field.
Compare with dictating into the main message composer.
Expected Behavior
Pasted/dictated text should appear in the question dialog's custom-answer field as promptly as it does in the main message composer.
Actual Behavior
The text appears only after a noticeable fixed lag of roughly a few seconds, regardless of dictated length. Observed behavior:
Main composer + same Handy clipboard-paste method: instant.
Question dialog custom-answer field + manual keyboard typing: instant/responsive.
Question dialog custom-answer field + Handy clipboard-paste: consistent multi-second delay before the text shows up.
No missing or duplicated characters were observed — the text arrives complete once the delay passes. Reproduces consistently.
Additional Context
Since manual typing is fast and the identical paste path is instant in the main composer, the culprit seems to be in the question dialog's input handling of pasted text (e.g. how it consumes bracketed-paste input from the terminal vs. the main editor). Happy to help narrow it down further if useful.
Real TUI in tmux, genuine paste into main composer (buffer paste)
88 ms
Real TUI in tmux, genuine paste into the question dialog's custom-answer textarea
27 ms
Terminal modes (mouse tracking, alt-screen) are identical between composer and dialog states, so there is no mode-related divergence either. The multi-second lag in the original report does not originate anywhere inside opencode/opentui.
Environment detail discovered during diagnosis: the reporter's terminal client (cmux, over SSH) sends pasted text as plain characters without bracketed-paste wrapping — verified by pasting into cat -v inside tmux and seeing no ESC[200~/ESC[201~ markers.
Consequence: every newline in a pasted or dictated block arrives as a genuine Return keypress. In the question dialog's edit state (QUESTION_MODE, "type your own answer"), Return is bound to Submit answer edit, so any multi-line input self-submits on its first line break and the remaining lines scatter into option navigation.
Deterministic repro (verified interactively): focus the custom-answer textarea, then inject first line submits + Return + second line wanders. Result: the answer submits on the first line; the second line lands outside the field.
This explains the original symptoms: dictated answers (Handy, clipboard-paste method) appeared "late/broken/never", while manual typing was fine and single-segment input worked. The main composer is unaffected because it overrides onPaste and normalizes multi-line content itself.
Suggestions
Harden question edit mode against unwrapped-paste bursts — e.g. treat Return as newline-insert when the textarea received a high-rate character burst within the last few hundred ms, or require an explicit confirmation for submit when editing began < N seconds ago.
Alternatively, document that reliable paste handling assumes bracketed paste from the terminal.
Separately, the missing bracketed-paste support belongs on the terminal-client side (cmux) — filing there too.
Happy to test patches.
Correction (same day): the reporter's primary symptom involves single-line pastes with no line breaks, which still fail to appear — so the Return-submits mechanism above is a real, reproducible adjacent defect but does not explain the main report. Diagnosis continues; measurements in the table above remain valid.
Final follow-up: mechanism confirmed — in the question menu, printable keys are live commands, which destroys unwrapped paste input
Reporter here again. With the reporter watching live, injecting the plain string 12abc into the terminal while a question dialog sat on its option list (custom-answer box not opened) caused the form to react to the text — selecting/submitting an option — instead of displaying it.
Complete causal chain for the original report
The reporter's terminal client (cmux over SSH) sends pasted text as plain characters without bracketed-paste wrapping — verified by pasting into cat -v: no ESC[200~…ESC[201~ markers. Pasted input is therefore byte-identical to very fast typing.
In the question dialog's default state, bare printable keys are bound as UI commands: 1–9 select answers (instantly submitting single-choice questions), h/j/k/l/arrows navigate, Return submits, Escape dismisses (packages/tui/src/routes/session/question.tsx binding sets).
Any dictated/pasted burst arriving while the custom-answer textarea is not yet focused is consumed as UI actions: the text vanishes, forms auto-submit, selections jump. Whether a given paste "works" reduces to millisecond-level focus timing — matching the originally reported intermittent lag/broken-input behavior.
The main composer is immune to the identical byte stream because plain printable keys are never commands there.
What was ruled out
Paste ingestion speed inside opencode/opentui: 1–88 ms end-to-end everywhere we could measure (table in the earlier comment), including a genuine paste landing in the question dialog's textarea itself in 27 ms. Ingest performance was never the issue — input routing semantics in the modal state are.
Suggested fixes (any one would do)
Don't bind bare digit/letter keys in QUESTION_MODE (require a modifier for numeric selection), or
Suppress character-command bindings for a short window after dialog open / tab switch, or
Detect a rapid character run hitting command bindings and surface it as recoverable pasted text.
Note for other reporters: terminals that send proper bracketed paste (most desktop terminals) deliver pastes as PasteEvents routed to the focused widget, so they won't trigger this. It takes an unwrapped-paste client to expose the command-key hazard — which also means the client side (cmux) is worth a feature request for bracketed-paste support.
Root cause confirmed via byte-level capture: paste events are silently discarded when the question dialog has no focused editor
Original reporter here. We attached a transparent PTY logger (timestamps + hex dump of everything reaching the TUI's stdin) and captured a real failing dictation inside the question dialog. Final story:
Properly wrapped bracketed paste, delivered instantly and completely. Transport, encoding, timing: all clean. (Earlier conclusion that the reporter's terminal sends unwrapped text was wrong — tmux strips/adds markers per-pane depending on each pane's own mode, so a cat -v probe cannot reveal markers. Mea culpa.)
Why the text still vanished
The captured keystroke stream shows the reporter pressed ↓ ×3 to highlight "Type your own answer", then began dictating — without pressing Enter/clicking to open the editor. During the multi-second transcription window the dialog stayed in selection mode (store.editing === false, provable from the arrow keys still navigating). When the paste finally arrived:
PasteEvents are dispatched to the focused renderable's paste handler only;
in selection mode no TextareaRenderable is mounted/focused, and no global listener handles paste;
result: the event has zero listeners and is dropped without any feedback.
So the failure condition is: any dictation/paste that completes while the custom-answer editor is not open. Human typing always works because by definition you only type once the box is open. The main composer always works because its textarea is permanently mounted and focused. Everything else (intermittency, "fixed lag", text "ending up elsewhere" after retries) follows from this.
Suggested fixes (opencode side)
Catch-all paste routing: when a PasteEvent reaches the question dialog in selection mode, auto-enter editing on the custom answer (when enabled) and insert the text — i.e., treat paste as intent-to-answer.
Minimally: surface undelivered paste ("paste ignored — open a field first") instead of dropping silently.
Docs/workaround meanwhile: press Enter on "Type your own answer" before finishing dictation.
Root cause (final, confirmed live): pasted text inserts into the question dialog's textarea but the UI never repaints until the next keypress
Original reporter here. After byte-level capture plus a controlled replay against the live binary, the actual mechanism is now confirmed — and it supersedes the earlier theories in this thread (transport, parser speed, and routing were all red herrings; measurements showing healthy ingestion were accurate).
The experiment
With the question dialog open and the custom-answer edit box focused (focus proven by hand-typing a character), two bracketed pastes were replayed into the terminal with byte-exact control:
ESC[200~Alpha one ESC[201~ — delivered as a single chunk
ESC[200~ … 17ms … Bravo two ESC[201~ — delivered split across chunks, matching a real dictation capture
Result reported live by the user: neither string appeared. Then the user pressed ↑, and both strings immediately appeared in the edit box.
Mechanism
The pasted text is inserted into the textarea's buffer — but the question dialog's <textarea> (packages/tui/src/routes/session/question.tsx, ~line 427) declares none of the reactive callbacks the main composer's textarea declares (onContentChange / onCursorChange, see packages/tui/src/component/prompt/index.tsx ~1373-1383, which push updates into Solid stores on every mutation). With no state change and no dirty-frame trigger, the renderer never repaints the inserted content while the UI is otherwise idle. The next key event (any key) forces a frame and the previously "missing" text pops into view.
This explains the full history of symptoms:
dictation "not appearing at all" in forms (UI idle → no frame until user gives up),
text appearing "seconds later" or "after moving away" (some unrelated event flushed a frame),
hand-typed characters always visible (each keystroke drives its own rendering),
main composer always fine (its callbacks force re-renders on every insertion),
all transport/parser/widget-ingest measurements being fast and clean (insertion genuinely worked every time).
Suggested fix
Wire the same callbacks the composer uses onto the question dialog's textarea — e.g.
onContentChange={()=>{/* touch a signal to trigger re-render */}}
(or address the underlying issue in @opentui/core: programmatic insertText() from a paste event should mark the renderable dirty / schedule a frame even when no reactive subscribers are attached).
Workaround for users until patched
After dictating/pasting into a question form field, press any harmless key (e.g. an arrow key, or space+backspace) — the text will instantly appear.
Following up on the root-cause comment: we validated fix candidates against @opentui/core 0.4.5 with a painted-frame harness (captureCharFrame() after pasteBracketedText, rather than reading the buffer — buffer reads always pass and mask this bug).
Results (0.4.5, bun harness)
Variant
Buffer
Painted frame
A) bare <textarea> (current dialog code)
✓
✓
B) <textarea onContentChange={() => renderer.requestRender()} />
✓
✓
C) composer-style onPaste self-insert
✓
✓
Notable: on 0.4.5 even variant A paints — meaning the missing-repaint behavior regressed somewhere between the published 0.4.5 and whatever @opentui/core build is bundled into the running binary (0.0.0-beta-18138; its bundle is minified so we couldn't diff statically — worth auditing what opentui revision the release embeds vs. the catalog: pin).
Recommended fix (works regardless of the embedded opentui revision)
In packages/tui/src/routes/session/question.tsx, give the custom-answer textarea the same reactive wiring the composer relies on — the renderer reference is already in scope:
<textarearef={(val: TextareaRenderable)=>{textarea=valval.traits={status: "ANSWER"}queueMicrotask(()=>{val.focus();val.gotoLineEnd()})}}initialValue={input()}placeholder="Type your own answer"placeholderColor={theme.textMuted}minHeight={1}maxHeight={6}textColor={theme.text}focusedTextColor={theme.text}cursorColor={theme.primary}cursorStyle={tuiConfig.cursor}onContentChange={()=>renderer.requestRender()}/>
This guarantees a frame on every buffer mutation (paste included) even if the embedded core stops self-scheduling renders on programmatic inserts. Optionally also file upstream on @opentui/core: EditBufferRenderable.insertText() triggered via a paste event should mark the renderable dirty / wake the render loop when idle.
Happy to test a patched build — the reporter can reproduce on demand within seconds using their dictation setup.
Root cause refined: shipped form-dialog writes paste into the Solid store but never into the mounted editor buffer
Final correction after auditing the actual bundle embedded in 0.0.0-beta-18138 (recovered via byte-offset extraction from the installed executable).
What ships
The reporter's binary contains a form dialog implementation that does not match current main'spackages/tui/src/routes/session/question.tsx (the shipped one replies via t.session.form.reply, supports string/number/integer/multiselect questions, dynamic placeholders like name@example.com). Its paste handler, de-minified:
intercept("paste",(xe)=>{if(mode.current()!==FORM_MODE)returnconsttext=normalize(decodePasteBytes(xe.bytes))if(!X(text))return// X(): select custom option + setStore("custom", key, existing + text)// + setStore("editing", true) ← STORE WRITE ONLYxe.preventDefault()// ← suppresses the widget's own paint-capable insert})
The mounted <textarea> only reads its value at mount (initialValue). So when the edit box is already open:
paste arrives → handler writes text into the Solid store,
preventDefault() stops the widget's built-in handlePaste (which would have inserted + requestRender()ed),
nothing tells the live editor buffer about the new text → invisible,
navigating selection away & back closes/reopens the box → remount picks up initialValue from the store → text suddenly appears.
Exactly matches the reporter's observed behavior, including the "text pops in after pressing ↑/↓" confirmation.
Fix
In that paste path, when the editor is already mounted (T.editing === true), route the text into the live buffer instead of (or in addition to) the store:
if(T.editing){D?.insertText(text);/* plus store sync via onContentChange */}
and/or make the textarea controlled (effect syncing store.custom[key] → D.setText(...) while editing), plus a requestRender() safety net.
Version note
Current public main's packages/tui/src/routes/session/question.tsx (last touched 2026-08-07) is a different implementation relying on the default widget paste path — which repaints correctly. If this forms dialog still exists on a preview branch, apply the fix there; users on affected builds can work around it by pressing any arrow key after dictating/pasting to force the pending frame.
V2 now inserts pasted text directly into the focused form editor instead of updating only the stored initial value, so it no longer needs an editor remount to appear. Pasting on a custom-choice question also opens its answer editor, and regression coverage checks pasting into an already active editor. If you still see this on v2, please open a new issue with the details.
Activity
JavaGT commentedon Aug 25, 2026
Follow-up: root cause narrowed — TUI paste ingestion exonerated; destructive submit-on-newline reproduced
Original reporter here. We instrumented the whole chain end-to-end. Two headline results:
1. Paste ingestion is fast everywhere it can be measured
All on v0.0.0-beta-18138, macOS arm64, tmux over SSH:
@opentui/coreStdinParser emitting a paste event split across SSH-style chunkshandlePastepathonPastehandlerTerminal modes (mouse tracking, alt-screen) are identical between composer and dialog states, so there is no mode-related divergence either. The multi-second lag in the original report does not originate anywhere inside opencode/opentui.
2. Actual failure mode: unwrapped paste + Return-bound submit corrupts multi-line answers
Environment detail discovered during diagnosis: the reporter's terminal client (cmux, over SSH) sends pasted text as plain characters without bracketed-paste wrapping — verified by pasting into
cat -vinside tmux and seeing noESC[200~/ESC[201~markers.Consequence: every newline in a pasted or dictated block arrives as a genuine Return keypress. In the question dialog's edit state (
QUESTION_MODE, "type your own answer"), Return is bound to Submit answer edit, so any multi-line input self-submits on its first line break and the remaining lines scatter into option navigation.Deterministic repro (verified interactively): focus the custom-answer textarea, then inject
first line submits+ Return +second line wanders. Result: the answer submits on the first line; the second line lands outside the field.This explains the original symptoms: dictated answers (Handy, clipboard-paste method) appeared "late/broken/never", while manual typing was fine and single-segment input worked. The main composer is unaffected because it overrides
onPasteand normalizes multi-line content itself.Suggestions
Happy to test patches.
Correction (same day): the reporter's primary symptom involves single-line pastes with no line breaks, which still fail to appear — so the Return-submits mechanism above is a real, reproducible adjacent defect but does not explain the main report. Diagnosis continues; measurements in the table above remain valid.
JavaGT commentedon Aug 25, 2026
Final follow-up: mechanism confirmed — in the question menu, printable keys are live commands, which destroys unwrapped paste input
Reporter here again. With the reporter watching live, injecting the plain string
12abcinto the terminal while a question dialog sat on its option list (custom-answer box not opened) caused the form to react to the text — selecting/submitting an option — instead of displaying it.Complete causal chain for the original report
cat -v: noESC[200~…ESC[201~markers. Pasted input is therefore byte-identical to very fast typing.1–9select answers (instantly submitting single-choice questions),h/j/k/l/arrows navigate, Return submits, Escape dismisses (packages/tui/src/routes/session/question.tsxbinding sets).What was ruled out
Paste ingestion speed inside opencode/opentui: 1–88 ms end-to-end everywhere we could measure (table in the earlier comment), including a genuine paste landing in the question dialog's textarea itself in 27 ms. Ingest performance was never the issue — input routing semantics in the modal state are.
Suggested fixes (any one would do)
QUESTION_MODE(require a modifier for numeric selection), orNote for other reporters: terminals that send proper bracketed paste (most desktop terminals) deliver pastes as PasteEvents routed to the focused widget, so they won't trigger this. It takes an unwrapped-paste client to expose the command-key hazard — which also means the client side (cmux) is worth a feature request for bracketed-paste support.
JavaGT commentedon Aug 25, 2026
Root cause confirmed via byte-level capture: paste events are silently discarded when the question dialog has no focused editor
Original reporter here. We attached a transparent PTY logger (timestamps + hex dump of everything reaching the TUI's stdin) and captured a real failing dictation inside the question dialog. Final story:
What actually arrives (healthy)
Properly wrapped bracketed paste, delivered instantly and completely. Transport, encoding, timing: all clean. (Earlier conclusion that the reporter's terminal sends unwrapped text was wrong — tmux strips/adds markers per-pane depending on each pane's own mode, so a
cat -vprobe cannot reveal markers. Mea culpa.)Why the text still vanished
The captured keystroke stream shows the reporter pressed ↓ ×3 to highlight "Type your own answer", then began dictating — without pressing Enter/clicking to open the editor. During the multi-second transcription window the dialog stayed in selection mode (
store.editing === false, provable from the arrow keys still navigating). When the paste finally arrived:TextareaRenderableis mounted/focused, and no global listener handles paste;So the failure condition is: any dictation/paste that completes while the custom-answer editor is not open. Human typing always works because by definition you only type once the box is open. The main composer always works because its textarea is permanently mounted and focused. Everything else (intermittency, "fixed lag", text "ending up elsewhere" after retries) follows from this.
Suggested fixes (opencode side)
Happy to test patches.
JavaGT commentedon Aug 25, 2026
Root cause (final, confirmed live): pasted text inserts into the question dialog's textarea but the UI never repaints until the next keypress
Original reporter here. After byte-level capture plus a controlled replay against the live binary, the actual mechanism is now confirmed — and it supersedes the earlier theories in this thread (transport, parser speed, and routing were all red herrings; measurements showing healthy ingestion were accurate).
The experiment
With the question dialog open and the custom-answer edit box focused (focus proven by hand-typing a character), two bracketed pastes were replayed into the terminal with byte-exact control:
ESC[200~Alpha one ESC[201~— delivered as a single chunkESC[200~… 17ms …Bravo two ESC[201~— delivered split across chunks, matching a real dictation captureResult reported live by the user: neither string appeared. Then the user pressed ↑, and both strings immediately appeared in the edit box.
Mechanism
The pasted text is inserted into the textarea's buffer — but the question dialog's
<textarea>(packages/tui/src/routes/session/question.tsx, ~line 427) declares none of the reactive callbacks the main composer's textarea declares (onContentChange/onCursorChange, seepackages/tui/src/component/prompt/index.tsx~1373-1383, which push updates into Solid stores on every mutation). With no state change and no dirty-frame trigger, the renderer never repaints the inserted content while the UI is otherwise idle. The next key event (any key) forces a frame and the previously "missing" text pops into view.This explains the full history of symptoms:
Suggested fix
Wire the same callbacks the composer uses onto the question dialog's textarea — e.g.
(or address the underlying issue in @opentui/core: programmatic
insertText()from a paste event should mark the renderable dirty / schedule a frame even when no reactive subscribers are attached).Workaround for users until patched
After dictating/pasting into a question form field, press any harmless key (e.g. an arrow key, or space+backspace) — the text will instantly appear.
JavaGT commentedon Aug 25, 2026
Proposed fix + validation results
Following up on the root-cause comment: we validated fix candidates against
@opentui/core0.4.5 with a painted-frame harness (captureCharFrame()afterpasteBracketedText, rather than reading the buffer — buffer reads always pass and mask this bug).Results (0.4.5, bun harness)
<textarea>(current dialog code)<textarea onContentChange={() => renderer.requestRender()} />onPasteself-insertNotable: on 0.4.5 even variant A paints — meaning the missing-repaint behavior regressed somewhere between the published 0.4.5 and whatever @opentui/core build is bundled into the running binary (0.0.0-beta-18138; its bundle is minified so we couldn't diff statically — worth auditing what opentui revision the release embeds vs. the
catalog:pin).Recommended fix (works regardless of the embedded opentui revision)
In
packages/tui/src/routes/session/question.tsx, give the custom-answer textarea the same reactive wiring the composer relies on — the renderer reference is already in scope:This guarantees a frame on every buffer mutation (paste included) even if the embedded core stops self-scheduling renders on programmatic inserts. Optionally also file upstream on @opentui/core:
EditBufferRenderable.insertText()triggered via a paste event should mark the renderable dirty / wake the render loop when idle.Happy to test a patched build — the reporter can reproduce on demand within seconds using their dictation setup.
JavaGT commentedon Aug 25, 2026
Root cause refined: shipped form-dialog writes paste into the Solid store but never into the mounted editor buffer
Final correction after auditing the actual bundle embedded in 0.0.0-beta-18138 (recovered via byte-offset extraction from the installed executable).
What ships
The reporter's binary contains a form dialog implementation that does not match current
main'spackages/tui/src/routes/session/question.tsx(the shipped one replies viat.session.form.reply, supportsstring/number/integer/multiselectquestions, dynamic placeholders likename@example.com). Its paste handler, de-minified:The mounted
<textarea>only reads its value at mount (initialValue). So when the edit box is already open:preventDefault()stops the widget's built-inhandlePaste(which would have inserted +requestRender()ed),initialValuefrom the store → text suddenly appears.Exactly matches the reporter's observed behavior, including the "text pops in after pressing ↑/↓" confirmation.
Fix
In that paste path, when the editor is already mounted (
T.editing === true), route the text into the live buffer instead of (or in addition to) the store:and/or make the textarea controlled (effect syncing
store.custom[key] → D.setText(...)while editing), plus arequestRender()safety net.Version note
Current public
main'spackages/tui/src/routes/session/question.tsx(last touched 2026-08-07) is a different implementation relying on the default widget paste path — which repaints correctly. If this forms dialog still exists on a preview branch, apply the fix there; users on affected builds can work around it by pressing any arrow key after dictating/pasting to force the pending frame.neriousy commentedon Oct 20, 2026
V2 now inserts pasted text directly into the focused form editor instead of updating only the stored initial value, so it no longer needs an editor remount to appear. Pasting on a custom-choice question also opens its answer editor, and regression coverage checks pasting into an already active editor. If you still see this on v2, please open a new issue with the details.