Repository navigation
fix(docx-io): keep figures and list paragraphs in Word export - #5331
Merged
Merged
Conversation
Word export now keeps figures inside table cells and list items, with their captions and their bold and italic. Each block paragraph of a list item is its own paragraph. The first one carries the bullet, and later ones take the item's indent. Images fit the width of their table cell or list item. Inside a list item, line breaks, kbd and inline images stay in the item's paragraph, an image that starts the item sits on its bullet, and the item keeps its number when its figure has nothing to export. Plate's own static image, which sits in its void's div beside a hidden spacer, stays in its list item too. This carries #5330, which does not allow maintainer edits, with review fixes on top. Co-authored-by: Taras Romilovych <tarasromil@gmail.com>
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
🦋 Changeset detectedLatest commit: 03dd7c7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This was referenced Nov 4, 2026
zbeyens
added a commit
that referenced
this pull request
Nov 4, 2026
…mmit When a PR's head branch does not allow maintainer edits, or a push to it is refused, babysit now folds the PR's commits and the repair into one commit, with a Co-authored-by line for each author, and delivers it into the PR's base. A PR into next lands through land.mjs, and a PR into main gets our own PR. Once that PR is open or the commit has landed, the run closes the author's PR with a comment that links its replacement. Before this, the run opened a PR stacked on the author's branch, which only the author could merge. Before the run first installs or runs the code of a PR whose author is not on the team, locally or in CI, a security review reads every line the PR adds, and each later commit of theirs gets the same read before it runs. An author's change to a workflow, a package script, a build or test config, a lockfile or a dependency waits for the owner's word, and so does a line the review finds reading a secret or the environment, or calling the network. Commits the author adds before the close join our PR as one more commit that holds only their own changes, with its own review round. A babysit merge into main that would publish the pending changesets now adds the skip-release marker to its squash subject and names the pending release in the hand-back, because a release needs the owner's word. A review thread whose reply waits as a draft no longer blocks the merge, and a verdict that drops part of the change no longer stops it. Another person's assignment on the PR or on its issue stops the run on any PR, not only an issue PR. A main PR's repair now runs the bug-fix playbook on the PR's defect, installs dependencies with ../plate's own pnpm install, and its v2 port carries a Co-authored-by line for each author whose code it keeps. Claude Code and Codex both read this playbook, so a session in either runtime that babysits a contributor's PR now carries it in our own PR instead of stacking one on the contributor's branch. The plan records that PR 5330 shipped as #5331, merged as 521a967 with Taras Romilovych as co-author, and that the Plate v2 port of the same fix comes next. Beyond the ask: the main-sync row now takes the number of the PR that merged, and main-sync.mjs matches that number only as the last (#N) before markers such as [skip release], so a revert subject that quotes an older PR's number no longer marks that older row. The merge read-back checks the Co-authored-by lines, and suggested task t_48de3f0b moves the carry rule into the shared land rule for every pstack project. Session: local_532137ea-4141-4034-8100-f563b8a8393a Plan: docs/plans/2026-11-04-pr-5330-babysit.md Lead: unknown
This branch was successfully deployed
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.
This carries @tarasromil's #5330 with my review fixes on top, because that PR does not allow maintainer edits. Taras is the commit's co-author.
Word export keeps images and captions in lists and table cells, and each paragraph in a list item stays separate. My fix puts an item's first image on its bullet, keeps line breaks in the item, and stops renumbering.
Caution
It only touches how the Word exporter writes lists and figures, and a test covers each case my fix changes. Nobody opened the file in Word, only Quick Look.
How it works
A Word file stores a list as paragraphs. The first paragraph of an item carries the bullet. Any later paragraph in that item has no bullet and is indented to line up with the item's text. The exporter turns the editor's HTML into those paragraphs.
Before the PR, the list code folded everything in an item into one paragraph, and a figure inside it vanished. The PR gives each block its own paragraph and exports figures. But it split on any tag outside a short list of inline tags, so a line break started a new paragraph. A figure that opened an item also lost its bullet.
My fix treats the image and the caption as two blocks of the item. The first block takes the bullet, every later block is indented, and inline content stays in the paragraph before it.
On top of the PR
TABLE_CELL_MARGINand themaximumWidththatbuildListtakes.kbdor an inline image, stays in the item's paragraph, as onmain. A paragraph, a div, a figure or another block tag from the file'sSPECIAL_BLOCK_ELEMENTS, such as a blockquote orpre, starts a new one.main, so later items do not renumber.buildFigurePartexports a part for bothbuildFigureandbuildList.buildImagekeeps the author's single attributes parameter, typed as the exportedParagraphAttributes, but uses itsmaximumWidthas given. Each caller passes the width left after the paragraph's indent, which is the rule inline images already follow. So every block of a list item gets the item's text width, whether its bullet or an indent sets where it starts.getWrappedTableand the PR'sgetWrappedFigurebecome onegetWrappedElement(node, tag)inxml-builder.ts, which unwraps single-child divs only. This keeps the PR from adding names to the existing import cycle.data-slate-spacerspan Plate's static render puts beside a void's content. A Plate image in a list item sits in such a div, so both the PR and my first repair still dropped it, while hand-written<div><figure>markup passed. Body and cell output do not change.getListTextIndent(level)inutils/list.tsgives the numbering, the continuation paragraphs and the list's image width one indent rule.buildTableCell's two block loops become oneimportCellBlock, because the inner loop for a div's children repeated the outer loop's branches.buildListtypes its queue as elements only and drops the checks that could never fail, and its width never drops below 1 twip, like the cell's.preafter a paragraph start their own paragraphs, a list item that opens with a figure gets the bullet while the rest is indented, Plate's static image beside its void spacer stays in its list item, an item keeps its number when its figure starts with an unfetchable image or a table or has nothing to export, a wide image that opens an item fits the item's text width, and a table inside a figure in a list item stays a table.Demo
main, the first item is an empty bullet, both images and the text after them are gone, and so is the cell's image. With the PR and my fix, every image and caption shows, the logo sits on its item's bullet, and the line break stays inside one bullet.Before
After
Before
After
Verification
nextis unchanged.Out of scope
next, which comes in its own change once this PR merges.