Keep a wrapped line in the paragraph it belongs to - #63
Merged
Conversation
ptheofan
force-pushed
the
62-soft-wrapped-paragraphs
branch
from
August 22, 2026 17:42
d766ce8 to
6b0c101
Compare
A soft break became a literal newline in the model. insertText does not read that as a line break -- it ends the paragraph and starts another, so one model element became two Doc paragraphs. Formatting is matched to paragraphs by exact text, so neither half matched, apiIdx stopped advancing, and every element after it was skipped. Nothing was styled from there on, leaving the text with whatever insertText gave it: the style of the character before the insertion point, which after a heading is a heading. A soft break renders as a space in markdown, so emit a space. A hard break is a line break inside the paragraph, which Docs spells as a vertical tab; emit that and reverse-convert it back to a markdown hard break, or it would read as a difference on every later sync. Closes #62 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptheofan
force-pushed
the
62-soft-wrapped-paragraphs
branch
from
August 22, 2026 17:46
6b0c101 to
0e0b9f3
Compare
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.
A pushed document came back with every paragraph styled as a heading —
bold, oversized, no bullets, no monospace. #60 clears inherited styles,
but the paragraphs never reach the code that would clear them.
Root cause. A soft-wrapped source line became a literal newline in
the model.
insertTextdoes not read that as a line break: it ends theparagraph there and starts another, so one model element becomes two Doc
paragraphs.
buildFormattingFromApiDocmatches by exact text, so neitherhalf matches,
apiIdxstops advancing, and with a lookahead window of 10every element after it is skipped. The text then keeps whatever
insertTextgave it — the style of the character before the insertionpoint, which after a heading is a heading.
Measured, not inferred: the stored
.remote.mdfrom a real push hasevery paragraph prefixed
###, and the log shows only 24 formattingrequests emitted for the whole document.
Fix. A soft break renders as a space in markdown, so emit a space. A
hard break is a line break within the paragraph, which Docs spells as a
vertical tab (U+000B); emit that so the paragraph stays whole, and
reverse-convert it back to a markdown hard break so it does not read as a
difference on every later sync.
Verification
Three converter tests plus one round-trip test, all watched failing
first — including never leaves a newline in an inline element, which
covers headings, list items and body text together.
One trap worth recording: importing the constant from
MarkdownToDocsConverterintoDocsToMarkdownConvertercreated acircular import that silently left it
undefinedand broke 23 unrelatedtests. It now lives in
@shared/constants.pnpm test1039 passed / 62 files · typecheck clean · lint clean.Closes #62
🤖 Generated with Claude Code