fix: a paste with CR line breaks draws as the paragraphs it is - #388
Merged
Merged
Conversation
The toolkit lays text out breaking only at LF, while on macOS the host draws each laid-out row through AppKit, which also breaks at a lone CR, vertical tab, form feed, NEL, U+2028 and U+2029. Pasting paragraphs separated by any of those drew the words after each break one row lower, on top of the next row. The text was intact; only the drawing was wrong. Those separators now become LF before an edit reaches the draft, the reply box or the profile's about field, and before a draft restored from disk is shown. CRLF is left alone: its CR ends a row, so it always drew correctly. When the stored text ends up different from what the editor inserted, because a separator was replaced or the paste was cut to fit, the editor takes the stored text and moves its caret to the end, and the toolkit has no way to say otherwise. The model's caret now goes there too, so typing lands where the caret is drawn; before, a paste cut to fit in the middle of a note left the two carets apart. A cut that splits a CRLF is made plain as well. The comment that blamed the field's width for this is corrected.
This was referenced Sep 25, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #165
What it was
The Native SDK lays text out breaking only at LF (and wraps only at space and tab), so
end.\rNextis one word on one row. On macOS the host draws each laid-out row through AppKit, which breaks at a lone CR, so the words after it are drawn a row lower, on top of the next row. The same holds for vertical tab, form feed, NEL, U+2028 and U+2029. CRLF draws correctly, because its CR is always the last byte of a row.Reproduced for the first time by pasting through the real clipboard into the composer of an automation build (isolated home, fixture key, loopback relay). Before the fix, bare CR, U+2028 and vertical tab each overprint in the host render; CRLF and LF are clean. The field-width change the old comment credited was not the cause; the comment is corrected.
The fix
plainLineBreaksturns those separators into LF.applyPlainEditruns every insert into the draft, the reply box and the profile's about field through it, and drafts restored from disk go throughsetPlain.ui.elhas no way to pass it a selection. So the model's caret moves to the end too. Before this, a paste cut to fit in the middle of a note left the two carets apart, and the next key landed out of sight. A cut that splits a CRLF would leave a lone CR, so the whole text is made plain again then. An insert refused outright changes nothing and moves nothing.What it costs
Only a paste that had to change pays: the caret ends at the end of the text, the editor's undo history for that box restarts, and the box does not scroll to the caret until the next key. CRLF and ordinary pastes are untouched, caret, undo and scroll included.
Verified
zig build test: 661 pass, including new tests for every separator, near-miss byte sequences, the UTF-8 boundary when the buffer fills, all three boxes, the caret after a changed paste, a clamp that splits a CRLF, a clamped mid-text paste, and a refused paste. Six deliberate breaks of the fix were each caught by those tests.Not changed