feat(linked-styles): redefine named paragraph styles ("Update to match")#3758
feat(linked-styles): redefine named paragraph styles ("Update to match")#3758shri-scale wants to merge 2 commits into
Conversation
Add a supported way to redefine a named paragraph style's run-level look (font family, size, weight, color) so it updates live and survives .docx export — the editor equivalent of Google Docs' "Update Heading 1 to match". - editor.commands.updateLinkedStyle(styleId, formatting) rewrites the style across all three converter representations (linkedStyles definition, translatedLinkedStyles, word/styles.xml) under one snapshot/rollback; never throws. - Read helpers: getLinkedStyleFormatting(styleId) and getEffectiveFormattingAtSelection(). - Repaint on every redefinition in both render modes — a linked-styles plugin meta signal (ProseMirror decorations) plus PresentationEditor.refreshLinkedStyles() (clears the FlowBlockCache and re-lays out the painted view). - Styles dropdown gains a per-row "Update to match" (pencil) action. Pure shape conversions are isolated in style-formatting.js. Covered by unit tests (pure mappers) and integration tests (engine, command, helpers, repaint, and export round-trip).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e1fd20066
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…lean-off
Address review feedback on "Update to match":
- readEffectiveRunFormatting now resolves the selection paragraph's named style
(merged over its basedOn parent) as the base and layers direct marks on top,
instead of reading marks only. Previously, formatting that came purely from a
named style (the common .docx case) was captured as empty and wiped the style.
- Cleared booleans (bold/italic/underline) are now stored as explicit off
({ value: '0' } in definition styles, w:val="0"/w:u w:val="none" in OOXML)
rather than deleting the key, so a redefined style overrides an inherited
basedOn value instead of falling back to it. definitionStylesToFormatting
reads explicit-off back as false.
|
Thanks @shri-scale. This is a useful feature and I agree it fills a real gap. I do not think we should merge it in this shape though. The change adds a new Could you move this into the Document API styles area instead? A possible shape: editor.doc.styles.apply({
target: {
scope: 'style',
type: 'paragraph',
styleId,
channel: 'run',
},
patch: { bold, italic, underline, fontSize, fontFamily, color },
});The feature, UI, tests, and style-reading logic are worth keeping. The main request is to put the style update behind |
What
Adds a supported way to redefine a named paragraph style's run-level look (font family, size, bold/italic/underline, color) so it updates live and survives
.docxexport — the editor equivalent of Google Docs' "Update Heading 1 to match selection".Previously the linked-styles extension could only apply an existing style (
setLinkedStyle,setStyleById); there was no supported way to redefine what a style means.API
editor.commands.updateLinkedStyle(styleId, formatting)rewrites the style across all three converter representations (linkedStyles[].definition.styles,translatedLinkedStyles.styles[id],word/styles.xml) under a single snapshot/rollback; never throws.editor.helpers.linkedStyles.getLinkedStyleFormatting(styleId)— read a style's current run formatting.editor.helpers.linkedStyles.getEffectiveFormattingAtSelection()— read the run formatting at the cursor (the source for "update to match").formattingshape:{ bold, italic, underline, fontSizePt, fontFamily, colorHex }.Rendering
A redefinition doesn't change the document, so both render paths are refreshed explicitly on every call:
PresentationEditor.refreshLinkedStyles()clears theFlowBlockCache(keyed on node identity) and re-lays out, mirroring other non-edit re-render paths (e.g.setShowBookmarks).UI
The styles dropdown gains a per-row "Update to match" (✎) action. Clicking the style name still just applies the style as before.
Tests
style-formatting.test.js) — formatting ⇄ OOXML conversions.update-linked-style.test.js) — engine, command, read helpers, repaint-on-every-redefinition (not just the first), andword/styles.xml+translatedLinkedStylesexport round-trip.LinkedStyle.test.js).pnpm test(linked-styles suites) green ·pnpm check:typesclean · eslint clean.Notes
style-formatting.js(no editor/DOM deps).