buildBulletItem computed `leading` as the median over ALL paragraph line gaps. In a bullet list
the bullet-to-bullet gap (~14.2pt) is larger than the within-bullet wrap gap (~12.2pt) and dominates
the median, so a newly-wrapped continuation line was placed ~2pt too low (visible extra spacing
between e.g. the Architecture line and its "Design Patterns…" continuation).
Use the WRAP spacing instead: prefer the edited item's own internal line gap (if it already wraps),
then any hanging-continuation gap in the block, then the all-lines median, then font*1.2. Only
affects multi-line (wrapped) bullet items; single-line edits emit no continuation so are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Typing at a run boundary (e.g. near "Async" in a bullet) often lands the new text in a
browser-created wrapper element whose only child is that text. resolveStyleEl's sibling-only
search then found no data-fid run and fell back to dominantFid — which for a bullet is the bold
LABEL run (its first run) — so the inserted text rendered bold.
Make resolveStyleEl CLIMB toward the editor root and scan siblings at each level (nearest
preceding run first, then following), so it reaches the real run spans alongside the wrapper and
the inserted text continues the adjacent run's font. data-advances is still read only from the
exact wrapping span, so inherited fonts correctly re-measure. Text typed inside an existing run
is unaffected (step 1 returns its span immediately).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two bugs made centered/right-aligned heading edits vanish on commit:
1. Gateway: ReflowParagraphData.align was Literal["left","justify"], so a center/right reflow
commit failed Pydantic validation -> 422 Unprocessable Entity. Widened to include
"center","right" (matches the frontend + engine, which already handle them).
2. Frontend: gatewayService.applyEdits caught ALL errors (incl. HTTP 422/403/500) and returned
fake success with a phantom `${documentId}_edited` id. The app then adopted that id and 404'd
on every fetch, so the edit silently reverted instead of showing an error. Now only genuine
network failures use the offline fallback; real HTTP errors throw the gateway's detail so the
caller toasts a clear message and stays on the current document.
Gateway runs with --reload (auto-picks up the model change); frontend needs a refresh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>