fix(core): render text boxes anchored inside table cells in the editor#975
Draft
sorenlouv wants to merge 12 commits into
Draft
fix(core): render text boxes anchored inside table cells in the editor#975sorenlouv wants to merge 12 commits into
sorenlouv wants to merge 12 commits into
Conversation
A text box anchored from a run inside a table cell was dropped: the parser's text-box enrichment ran only on block-level paragraphs (not cell paragraphs), the serializer emitted text-box content only for `textBox`-geometry shapes (parsed boxes are always `rect`), and the ProseMirror conversion never surfaced cell boxes — so the box was lost on save and never rendered in the editor. - parser: enrich cell paragraphs; serializer: emit a shape's text body on text presence rather than geometry - schema: allow a `textBox` inside `tableCell`/`tableHeader` - toProseDoc/fromProseDoc: promote a cell paragraph's anchored boxes to sibling `textBox` nodes and re-anchor them back on save - layout-bridge + painter: render the cell box in-flow inside the cell Anchored/floating boxes render in-flow; true in-cell float positioning is a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
|
All contributors have signed the CLA ✍️ ✅ Posted by the CLA bot. |
* feat(core): footnote-PM-doc layout seam + data-footnote-id + note-ref marker on serialize Adds getFootnotePmDoc seam (mirrors getHfPmDoc) so the painter can read a live footnote ProseMirror doc; stamps data-footnote-id on .layout-footnote-content for per-footnote position disambiguation; re-inserts the w:footnoteRef/w:endnoteRef marker run when serializing a normal note from content (parser drops it). Groundwork for editable footnotes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(react): lazy hidden footnote ProseMirror view + writeback Mounts one off-screen EditorView for the actively-edited footnote (lazy single slot keyed by activeFootnoteId), mirroring HiddenHeaderFooterPMs. Transactions sync proseDocToBlocks back to Document.package.footnotes and clear verbatimXml; useLayoutPipeline feeds the live doc through getFootnotePmDoc so the painter reflects edits. Footnote-edit wiring lives in a new useFootnoteEditState hook (keeps PagedEditor under the line cap). Click routing/overlay follow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(react): route clicks into footnotes for editing Clicking a painted footnote enters footnote-edit mode for that data-footnote-id, places the caret in its hidden view (scoped span-snap so colliding per-footnote PM positions never cross-match), focuses it, and blurs+read-onlys the body; clicking outside exits. activeSurface() now routes body|HF|footnote. Adds e2e spec (run via playwright-runner) and a unit test for the hit-resolver. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(react): restore body focus when a click exits footnote-edit mode Exiting footnote mode via a body click now hands focus + caret back to the body in the same gesture. The restore is deferred to a parent-level effect (exitFootnoteToBody) that runs after HiddenFootnotePM tears its view down, so the teardown's destroy-blur can't steal the focus, and the body PM is already editable by then. Verified by the footnote-editing e2e (click→type→exit→type). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(react): visible caret + selection overlay for footnote editing Draws the caret and range-selection rects over the actively-edited footnote, mirroring the header/footer overlay. New framework-free core helpers (computeFootnoteCaretRectFromView/SelectionRectsFromView) scope the DOM walk to the active .layout-footnote-content[data-footnote-id] so colliding per-footnote PM positions never cross-match; DocxEditorPagedArea portals the overlay and recomputes on painter:painted + resize. e2e asserts the caret renders within the clicked footnote bounds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(vue): editable footnotes at parity with React Mirrors the React footnote-editing feature in the Vue adapter: lazy single hidden footnote EditorView + writeback (useFootnotePM), click routing into footnotes (usePagesPointer), and caret/selection overlay (useFootnoteOverlay + FootnoteOverlay.vue) reusing the framework-free core helpers. getFootnotePmDoc feeds the live doc to the painter. No public API change; parity contract holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: changeset for editable footnotes * chore: update API report for footnote-editing exports * fix(react): exit footnote-edit mode when the edited footnote is gone Reset footnoteEditId when the actively-edited footnote is no longer present in the document (e.g. a new document loaded mid-edit) — otherwise the body PM stays readOnly and the next writeback resolves the stale id against the new document. Keyed on footnote existence, not document identity (history.state gets a fresh object per body edit, so a bare [document] reset would exit mid-edit). Mirrors the Vue adapter's destroyFootnotePM-on-load behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(footnotes): black caret + glyph-accurate click placement The footnote caret rendered blue (HF chrome color) — make it black to match the body caret in both adapters. Footnote clicks resolved position via a coarse estimate that drifted (clicking between letters landed at the word end); React now reuses the body's glyph-accurate findPositionInSpan (exported from core) on the span under the cursor, scoped to the active footnote. Vue keeps the coarse snap for now (precise caret exposes a separate Vue typing issue). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(footnotes): enable click-drag text selection The footnote click handler placed the caret and returned without arming drag state, so click-drag selected nothing. Arm dragAnchorRef + isDraggingRef from the clicked position on mousedown (DOM-resolved, no view needed); the existing move handler already extends the selection through the footnote surface. e2e covers it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Jacob Jove <jacobjove@Jacobs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…genpal#991) * fix(core): keep a footnote reference on the same line as its word The line-breaker created a wrap opportunity at every run boundary, so a footnote/endnote reference run (a separate superscript with no space before it, e.g. copyright.¹) could split onto the next line. Fold the width of the unbreakable content that follows a run's last word into the wrap decision so adjacent runs with no whitespace between them wrap as a single cluster, matching Word. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(core): derive footnote-glue maxWidth from measureTextWidth The hardcoded maxWidth assumed this file's 8px/char canvas stub, but the guard only installs it when no global document exists; run after another test that sets one, measureTextWidth returned different widths and the control case failed. Derive the threshold from measureTextWidth so the test is self-consistent under whichever stub is active. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Jacob Jove <jacobjove@Jacobs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…igenpal#994) * fix: superscript footnote/endnote refs only when the style says so (Word parity) A bare anchor run (no FootnoteReference rStyle, e.g. Pandoc output) was force-raised by the layout bridge, diverging from Word, which renders an unstyled anchor at the baseline. Drop the implicit superscript; it now flows solely from the resolved character-style chain or the run's own vertAlign. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(core): note basedOn limitation + endnote-mark intent in footnote-ref parity test Addresses review feedback on PR eigenpal#994 — clarify that getRunStyleOwnProperties resolves only own (not basedOn-inherited) vertAlign, and that endnoteRef content maps to the footnoteRef mark. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(core): correct basedOn note — styleParser pre-merges the chain The prior comment (and a PR eigenpal#994 review reply) wrongly claimed a basedOn-inherited vertAlign renders at the baseline. styleParser.resolveStyleInheritance merges the parent rPr into the child before getRunStyleOwnProperties reads it, so production honors inherited superscript; the own-only behavior is the test mock's, not the real resolver's. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Jacob Jove <jacobjove@Jacobs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…igenpal#988) * fix(core): grow a section's footer/header band per its own margins extendMarginsForHeaderFooter decided whether to grow the header/footer band once, from the body section's margins, then applied that decision to every section. A section whose own margins are thin — e.g. a landscape table section with a 0.5in bottom margin (≈ the footer distance) embedded in a 1in-margin portrait body — never grew its footer band, so the footer overlapped the footnote area and the page number rode up beside the last footnote instead of sitting below it. Decide the overflow per margin set, using that set's own top/bottom and header/footer distances. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: changeset for header/footer band fix --------- Co-authored-by: Jacob Jove <jacobjove@Jacobs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…, hyperlinks) (eigenpal#987) * feat(core): options-aware generateTableOfContents (level range, title, hyperlinks) * Address review: clamp/order TOC level range + test empty-string title
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…al#996) The editable-footnotes change (eigenpal#995) pushed the Vue composable to 1003 lines, past the default 1000-line cap, failing the lint job on main. Add a per-file override with modest headroom, matching how DocxEditor.vue (bumped to 1250 in the same PR) and the other cohesive orchestrators are handled. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Resolves conflicts from eigenpal#967 (text boxes + block-level bookmarks on round-trip), which landed an identical drawing.ts 'gate on text presence, not geometry' fix plus overlapping textbox/table conversion changes: - runSerializer/drawing.ts: kept eigenpal#967's text-presence gating (code was identical on both sides; took its more detailed comments) - tableParser.ts: kept eigenpal#967's BlockMarkerCollector/parseBlockMarker import - toProseDoc/textbox.ts: unioned the type imports (Paragraph, ParagraphContent, Run, TextBox, Shape, TextFormatting) typecheck passes; textbox + block-bookmark round-trip tests (61) green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Compress the multi-line comments added for the cell text-box path to one or two lines and drop the dangling "Option A" references. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
A text box anchored inside a table cell didn't appear in the editor and was lost on save — unlike text boxes in the body, headers, or footers, which work. They now render and round-trip from inside a table cell like anywhere else.
Fixture:
e2e/fixtures/textbox-in-table-cell.docx(synthetic), covered by regression tests.A floating box renders in-flow inside the cell for now; exact in-cell float positioning is a follow-up. The text-box serializer change here is identical to #967's and merges cleanly with it.
Opening as a draft for review.