Repository navigation
Normalize CR and CRLF line endings in bracketed paste #30
Description
Problem
Bracketed paste containing carriage-return line endings (\r, or CRLF) can overwrite composer rows and leave the cursor separated from the visible draft. Composer.paste() currently counts/splits only on LF and inserts CR unchanged. xterm.js normalizes clipboard LF/CRLF to CR before bracketed paste, so this affects an ordinary terminal paste path.
This reproduces on main 9fb0d50 (0.3.3) and PR #29's original head ccf79b4; it is not introduced by #29. It is separate from #4's paste-history restoration and #28's footer clipping.
Reproduction
-
Run the built interactive CLI in an 80×24 terminal (observed with xterm.js 6.0.0 connected to a real macOS PTY).
-
Paste these three lines through the terminal's bracketed-paste path:
첫째 줄 한글🙂 둘째 줄 café 漢字 셋째 줄 cursor -
Observe the composer, then move/edit the cursor.
Actual: later lines overwrite earlier visible text and the cursor position does not match the displayed draft. The submitted request still contains all text, but with raw CR separators; this is not a claim that the payload disappears. Direct LF multiline editing does not show this failure.
Small, suitable first contribution
Normalize CRLF and lone CR to LF at the paste input boundary, before counting lines, collapsing long pastes, or inserting text. Start in packages/tui/src/composer.ts (paste) and the corresponding composer tests; check the bracketed-paste decoder contract in packages/tui/src/keys.ts. Keep ordinary Enter key handling unchanged.
Acceptance criteria
- LF, CRLF, and CR variants of the same pasted text produce the same logical draft, with no literal CR reaching the rendered composer.
- Short pastes support cursor movement, deletion, submission, and history recall without corruption.
- Long-paste placeholders count normalized lines correctly and expand to the complete normalized text; existing paste-history behavior remains intact.
- Regression tests cover all three line endings, and a visible PTY-backed terminal paste confirms layout/cursor behavior with Korean and emoji text.
No live API credentials are needed: a local response fixture is sufficient. Please keep this patch focused on paste newline normalization.
Activity
TaewoooPark commentedon Oct 4, 2026
Resolved by #41. All four acceptance criteria have been independently verified and are now checked.
LF, CRLF, and lone-CR pasted input now produce the same normalized draft before line counting, collapse, and insertion. Short-paste navigation, deletion, submission, and history recall remain correct; long-paste placeholders count normalized lines and expand to the complete normalized text. Ordinary Enter handling is unchanged.
All five new regression tests failed on the unchanged base and passed on the reviewed head. In addition to the full 1,644-test local suite and independent boundary checks, the actual built base/head CLIs were compared in a visible xterm.js 6 terminal backed by a real macOS PTY. Korean/emoji text stayed on its three intended rows, cursor movement/deletion and history worked, and a 30-line paste collapsed and submitted correctly. A localhost response fixture verified the complete normalized request text.
All five CI jobs passed: https://github.com/TaewoooPark/Motifcode/actions/runs/37177849187 . These are local input/rendering and fixture checks; no live Infron/Motif model call was made.
Thanks @nimbly1 for the focused fix and regression coverage. This records the source fix in main; an npm release is separate.
Merged commit: c2adbb7 . Post-merge main CI also passed all five jobs: https://github.com/TaewoooPark/Motifcode/actions/runs/37179908938 .