feat: store large/multiline pastes behind a placeholder token (#53)#67
Conversation
A large or multi-line paste was inserted inline into the single-line textarea buffer (newlines flattened to spaces), which exploded the input across many wrapped rows and mangled the frame/caret math — and lost the original newlines. Now a paste above a threshold (any newline, or > 200 chars) is kept in a per-textarea paste store and represented by a compact placeholder token (`[Pasted text #1 +12 lines]`), expanded back to the exact original text (newlines preserved) at submit. Small single-line pastes inline as before. - paste-store.ts (new, pure/unit-tested): shouldStorePaste threshold, register → placeholder, expand (multiple tokens; unknown look-alikes left as typed), tokenEndingAt for atomic deletion, clear (ids stay monotonic). - input.ts: Ctrl+V stores large pastes; backspace at a token's right edge deletes the whole token (and frees its entry); submit expands placeholders and clears the store. Scope: the Ctrl+V/onPaste path (deterministic). Capturing raw terminal bracketed paste (the path behind the IntelliJ-terminal hang, #60) needs stdin-level work and is a follow-up. Tests: 14 unit (paste-store) + 4 textarea integration (multiline round-trip with newlines preserved, embedded placeholder, single-backspace atomic delete, short paste still inlined); updated the prior newline-flatten test to the new preserve-newlines behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
✅ VerificationFull suite + typecheck + lint (clean)
New/updated tests for this feature (21 across the two files)Behavior confirmed (integration, real
|
|
✅ PR OK — verified end-to-end (posted as comment; GitHub blocks self-approval). Addresses #53: large/multi-line Ctrl+V pastes are now kept in a per-textarea paste store and shown as a compact Verified the integration seams in
Scope is honestly documented: this covers only the deterministic Verification: build ✓ · typecheck ✓ · lint ✓ · full suite 646/646 · 21 new tests — 14 unit (thresholds, singular/plural & char-count formatting, single/multi expansion, unknown-token passthrough, Pure, well-tested core + correct wiring. LGTM. |
Closes #53. (Related: #60 — see Scope.)
Problem
A large/multi-line paste was inserted inline into the textarea's single logical line, with newlines flattened to spaces (
input.tsCtrl+V handler). A big paste therefore soft-wrapped across many rows and broke the input frame, footer, completion panel, and caret math — and the original newlines were lost.Fix
Keep the raw text in a per-textarea paste store and render a compact placeholder token in the buffer, expanding it back to the exact original text only at submit.
cli/paste-store.ts(new, pure & unit-tested):shouldStorePaste(text)— store on any newline or> 200chars; inline otherwise.register(text)→[Pasted text #N +L lines](or… C chars]for a long single line).expand(value)— replaces every known token with its raw text; unknown look-alikes are left as typed.tokenEndingAt(value, pos)— for atomic deletion.clear()— ids stay monotonic so a stale token in scrollback never collides with a fresh entry.cli/input.ts:Acceptance criteria
Scope
This covers the Ctrl+V /
onPastepath (the deterministic, testable entry point). Capturing raw terminal bracketed paste — the path behind the IntelliJ-terminal hang in #60, where readline turns pasted newlines into repeated submits — requires stdin/bracketed-paste handling and is a focused follow-up; I didn't want to bundle that less-testable terminal work into this PR. #60 is therefore not closed here.Tests
cli/tests/paste-store.test.ts— 14 unit tests (thresholds, placeholder formatting incl. singular/plural and char-count, single/multi-token expansion, unknown-token passthrough,tokenEndingAtpositive/negative, clear + monotonic ids).cli/tests/textarea-paste.test.ts— integration via the realcreateTextarea+ fake stdin: multiline round-trip with newlines preserved, placeholder embedded among typed text, single-backspace atomic delete (proves it's one token, not raw chars), short paste still inlined. Updated the prior newline-flatten test to the new preserve-newlines behavior.🤖 Generated with Claude Code