fix(tui): collapse large pastes to prevent input freeze - #2777
Conversation
Pasting a large block into the TUI input froze the interface on terminals that do not honour bracketed-paste mode: the payload arrived as plain stdin and was inserted character-by-character, each keystroke triggering a full editor reflow (O(n^2)). - pi-tui stdin-buffer: coalesce plain stdin chunks over 800 chars into a single `paste` event (100ms debounce to merge split reads), so the editor folds the whole payload once instead of per-character. - pi-tui editor: fold pastes into a `[Pasted text #N]` marker when they exceed 800 chars or 10 newlines; keep short multi-line pastes visible. The marker regex accepts both the new and the legacy `[paste #N]` format. - kimi-code custom-editor: track the dual-format paste-marker regex. Resolve MoonshotAI#2776
🦋 Changeset detectedLatest commit: db559d2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a46b73604
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| this.appendNonBracketedPaste(str); | ||
| return; |
There was a problem hiding this comment.
Do not append post-paste key sequences to the paste buffer
When a large non-bracketed paste is pending, this branch appends every following stdin chunk for 100 ms, including control/escape sequences that are user keystrokes rather than paste continuations. If the user immediately presses Enter to submit, or an arrow/backspace key after pasting >800 chars, that key is swallowed into the synthetic paste event instead of being dispatched; escape sequences can also be rewrapped as bracketed paste content and leak printable tails like [D into the editor. Flush the pending paste before handling non-plain key sequences, or only coalesce chunks that are still plain paste content.
Useful? React with 👍 / 👎.
While the non-bracketed paste coalescing window is open, any stdin chunk arriving within 100ms was appended verbatim to the synthetic paste, so an arrow / backspace / Ctrl key pressed right after a large paste was swallowed into the paste event and escape sequences leaked printable tails (e.g. [D) into the editor. Only coalesce continuation chunks that are still plain paste content; the first ESC / DEL / C0 control flushes the pending paste and is processed as a normal keystroke instead. CR / LF / tab stay coalesced so a multi-line paste split across reads is not flushed mid-stream.
Related Issue
Resolve #2776
Problem
See linked issue. In short: pasting a large block of text (thousands of chars, especially mixed CJK/English) into the TUI input froze the whole interface until the process was killed/restarted. Typing the same text character-by-character was fine.
Root cause: on terminals that do not honour bracketed-paste mode (or where it gets stripped — e.g. tmux without pass-through, some SSH setups, older terminals), the paste arrives as plain stdin and is inserted character-by-character. Each character triggers a full editor reflow + re-render, so a single bulk paste is O(n²) on the main thread and locks all input.
What changed
stdin-buffer: detect plain stdin chunks over 800 chars (no escape sequences) and coalesce them into a singlepasteevent, with a 100 ms debounce window to merge read-split continuation chunks.terminal.tsalready re-wrapspasteevents with bracketed-paste markers, so the editor's existinghandlePastepath folds the whole payload in one O(n) pass instead of per-character.editor: fold pastes into a[Pasted text #N]marker when they exceed 800 chars or 10 newlines (keeps short multi-line pastes visible). Marker format switched to Claude-style[Pasted text #N +x lines]; the segmenting/expansion regex accepts both the new and the legacy[paste #N …]form, so existing markers still expand.custom-editor: its paste-marker regex tracks the same dual format.Notes / trade-offs
Checklist
.changeset/for@moonshot-ai/pi-tuiand@moonshot-ai/kimi-code(patch).