@eigenpal/docx-editor-core/prosemirror
ProseMirror Integration for DOCX Editor
This module provides ProseMirror-based editing: - Schema for DOCX document structure - Bidirectional conversion between Document and PM - React wrapper component - Plugins for selection tracking - Commands for formatting - Extension system for schema, plugins, and keymaps
Functions(74)
addColumnLeft
declare function addColumnLeft(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;addColumnRight
declare function addColumnRight(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;addRowAbove
declare function addRowAbove(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;addRowBelow
declare function addRowBelow(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;declare function addTabStop(position: number, alignment?: TabStopAlignment, leader?: TabLeader): Command;declare function applyStyle(styleId: string, resolvedAttrs?: ResolvedStyleAttrs): Command;applyTableStyle
declare function applyTableStyle(styleData: {
styleId: string;
tableBorders?: Record<string, unknown>;
conditionals?: Record<string, unknown>;
look?: Record<string, boolean>;
}): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;autoFitContents
declare function autoFitContents(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;createEmptyDoc
Create an empty ProseMirror document
declare function createEmptyDoc(): Node;createSelectionTrackerPlugin
Create selection tracker plugin
declare function createSelectionTrackerPlugin(onSelectionChange?: SelectionChangeCallback): Plugin;createStyleResolver
Create a style resolver from document's style definitions
declare function createStyleResolver(styleDefinitions: StyleDefinitions | undefined): StyleResolver;decreaseIndent
declare function decreaseIndent(amount?: number): Command;deleteColumn
declare function deleteColumn(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;declare function deleteRow(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;deleteTable
declare function deleteTable(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;distributeColumns
declare function distributeColumns(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;extractSelectionContext
Extract selection context from editor state
declare function extractSelectionContext(state: EditorState): SelectionContext;extractSelectionState
Extract selection state from editor state. Used by PagedEditor integration in DocxEditor for toolbar state.
declare function extractSelectionState(state: EditorState): SelectionState | null;findHyperlinkRangeAt
Resolve the hyperlink mark + contiguous range that surrounds the current cursor. Used by edit/remove popup actions in both adapters.
Resolution order for the mark itself: 1. `$from.marks()` — the normal active-marks lookup 2. `$from.nodeAfter`/`nodeBefore` marks — boundary positions don't report active marks via `marks()` 3. (optional) text-node search by `fallbackHref` — last resort when the popup knows the href but the cursor sits at a gap
The returned range walks the parent block grouping consecutive text nodes that share the same href, and returns whichever range contains the cursor.
declare function findHyperlinkRangeAt(state: EditorState, fallbackHref?: string): {
mark: Mark;
start: number;
end: number;
} | null;findParagraphByParaId
ProseMirror position range for the paragraph (or any textblock) whose `paraId` attribute equals `paraId`. Returns the inclusive `from` and exclusive `to` positions, plus the node, so callers can both target the paragraph (e.g. addMark over its text range) and inspect it.
`from` is the position immediately before the textblock; `to` is `from + node.nodeSize`. The text content lives at `[from + 1, to - 1]`.
Returns null if no textblock with that paraId exists.
declare function findParagraphByParaId(doc: Node, paraId: string): {
node: Node;
from: number;
to: number;
} | null;findStartPosForParaId
ProseMirror position immediately before the first textblock whose `paraId` attribute equals `paraId` (Word `w14:paraId` / OOXML paragraph id).
Match is strict string equality on `node.attrs.paraId`.
declare function findStartPosForParaId(doc: Node, paraId: string): number | null;footnoteToProseDoc
Convert footnote/endnote content (array of Paragraph/Table blocks) to a ProseMirror document. Mirrors `headerFooterToProseDoc` so footnotes flow through the same body pipeline (toFlowBlocks → measureBlocks → renderFragment) and inherit its block support — paragraph + table + image + textBox + fields. Pre-PR, footnoteLayout's `convertFootnoteToContent` re-implemented run/paragraph conversion by hand and silently dropped tables, images, and fields nested inside a footnote.
declare function footnoteToProseDoc(content: Array<Paragraph | Table>, options?: ToProseDocOptions & {
theme?: Theme | null;
}): Node;Convert a ProseMirror document to our Document type
declare function fromProseDoc(pmDoc: Node, baseDocument?: Document): Document;getHyperlinkAttrs
declare function getHyperlinkAttrs(state: EditorState): {
href: string;
tooltip?: string;
} | null;declare function getListInfo(state: EditorState): {
numId: number;
ilvl: number;
} | null;Get the current value of a mark attribute
declare function getMarkAttr(state: EditorState, markType: MarkType, attr: string): unknown | null;getParagraphAlignment
declare function getParagraphAlignment(state: EditorState): ParagraphAlignment | null;getParagraphBidi
declare function getParagraphBidi(state: EditorState): boolean;declare function getSelectedText(state: EditorState): string;getSelectionContext
Get current selection context from editor state
declare function getSelectionContext(state: EditorState): SelectionContext | null;declare function getStyleId(state: EditorState): string | null;getTableContext
declare function getTableContext(state: EditorState): TableContextInfo;increaseIndent
declare function increaseIndent(amount?: number): Command;insertHyperlink
declare function insertHyperlink(text: string, href: string, tooltip?: string): Command;insertTable
declare function insertTable(rows: number, cols: number): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;isHyperlinkActive
declare function isHyperlinkActive(state: EditorState): boolean;declare function isInList(state: EditorState): boolean;declare function isInTableCell(state: EditorState): boolean;Check if a mark is active in the current selection
declare function isMarkActive(state: EditorState, markType: MarkType, attrs?: Record<string, unknown>): boolean;declare function mergeCells(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;removeTableBorders
declare function removeTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;removeTabStop
declare function removeTabStop(position: number): Command;selectColumn
declare function selectColumn(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;declare function selectRow(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;selectTable
declare function selectTable(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;setAlignment
Paragraph Formatting Commands — thin re-exports from extension system
Alignment, line spacing, indentation, lists, paragraph styles. All implementations live in extensions/; this file re-exports for backward compatibility.
declare function setAlignment(alignment: ParagraphAlignment): Command;setAllTableBorders
declare function setAllTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: {
style: string;
size: number;
color: {
rgb: string;
};
}): boolean;setCellBorder
declare function setCellBorder(side: 'top' | 'bottom' | 'left' | 'right' | 'all', spec: {
style: string;
size?: number;
color?: {
rgb: string;
};
} | null, clearOthers?: boolean): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setCellFillColor
declare function setCellFillColor(color: string | null): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setCellMargins
declare function setCellMargins(margins: {
top?: number;
bottom?: number;
left?: number;
right?: number;
}): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setCellTextDirection
declare function setCellTextDirection(direction: string | null): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setCellVerticalAlign
declare function setCellVerticalAlign(align: 'top' | 'center' | 'bottom'): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setFontFamily
declare function setFontFamily(fontName: string): Command;declare function setFontSize(size: number): Command;setHighlight
declare function setHighlight(color: string): Command;setHyperlink
declare function setHyperlink(href: string, tooltip?: string): Command;setIndentFirstLine
declare function setIndentFirstLine(twips: number, hanging?: boolean): Command;setIndentLeft
declare function setIndentLeft(twips: number): Command;setIndentRight
declare function setIndentRight(twips: number): Command;setInsideTableBorders
declare function setInsideTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: {
style: string;
size: number;
color: {
rgb: string;
};
}): boolean;setLineSpacing
declare function setLineSpacing(value: number, rule?: LineSpacingRule): Command;setOutsideTableBorders
declare function setOutsideTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: {
style: string;
size: number;
color: {
rgb: string;
};
}): boolean;setRowHeight
declare function setRowHeight(height: number | null, rule?: 'auto' | 'atLeast' | 'exact'): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setTableBorderColor
declare function setTableBorderColor(color: string): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setTableBorders
declare function setTableBorders(preset: BorderPreset, borderSpec?: {
style: string;
size: number;
color: {
rgb: string;
};
}): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setTableBorderWidth
declare function setTableBorderWidth(size: number): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setTableProperties
declare function setTableProperties(props: {
width?: number | null;
widthType?: string | null;
justification?: 'left' | 'center' | 'right' | null;
}): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;setTextColor
declare function setTextColor(attrs: TextColorAttrs): Command;declare function splitCell(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;toggleHeaderRow
declare function toggleHeaderRow(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;toggleNoWrap
declare function toggleNoWrap(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;Convert a Document to a ProseMirror document
declare function toProseDoc(document: Document, options?: ToProseDocOptions): Node;updateDocumentContent
Update a Document with content from a ProseMirror document Preserves all non-content parts of the original document
declare function updateDocumentContent(originalDocument: Document, pmDoc: Node): Document;Classes(2)
LayoutSelectionGate
LayoutSelectionGate coordinates the timing between document edits and layout reflow so that selection overlays are only painted against current DOM geometry.
Workflow: 1. Document changes → setStateSeq(++seq) 2. Layout starts → onLayoutStart() 3. Layout completes → onLayoutComplete(seq) 4. Selection update requested → requestRender() 5. If safe → callback is called
declare class LayoutSelectionGate| Member | Type | Summary |
|---|---|---|
| getDebugInfo | — | Get debug info about current state. |
| getRenderSeq | — | Get current layout render sequence. |
| getStateSeq | — | Get current document state sequence. |
| incrementStateSeq | — | Increment document state sequence (convenience method). Returns the new sequence value. |
| isSafeToRender | — | Check if it's safe to render selection. Safe when: layout is not updating AND render sequence = state sequence |
| onLayoutComplete | — | Called when layout computation and DOM painting completes. |
| onLayoutStart | — | Called when layout computation starts. |
| onRender | — | Register a callback to be called on render events. |
| requestRender | — | Request a selection render. Will be executed when safe. If already safe, executes immediately. |
| reset | — | Reset the gate state (useful for testing or document reload). |
| setStateSeq | — | Set the document state sequence (call when document changes). This should be called on every ProseMirror transaction that changes the doc. |
StyleResolver
StyleResolver provides efficient access to resolved style properties
declare class StyleResolver| Member | Type | Summary |
|---|---|---|
| (constructor) | — | Constructs a new instance of the `StyleResolver` class |
| getDefaultCharacterStyle | — | Get the default character style (the one marked `w:default="1"`). |
| getDefaultParagraphStyle | — | Get default paragraph style (usually "Normal") |
| getDefaultTableStyle | — | Get the default table style (the one marked `w:default="1"`). |
| getDocDefaults | — | Get document defaults |
| getParagraphStyles | — | Get all available paragraph styles (for toolbar dropdown) |
| getRunStyleOwnProperties | — | Get a character style's own properties WITHOUT docDefaults. Used when the caller already has docDefaults applied (e.g., from paragraph style resolution). This prevents docDefault fonts from incorrectly overriding paragraph style fonts. |
| getStyle | — | Get a style by ID |
| getTableStyles | — | Get all available table styles (for style gallery) |
| hasParagraphStyle | — | Whether a paragraph style with the given id is defined in the document's `styles.xml`. Used by the agent toolkit to refuse `set_paragraph_style({ styleId: 'NoSuchStyle' })` instead of silently writing an invalid `<w:pStyle>` reference. |
| hasStyle | — | Check if a style exists |
| resolveParagraphStyle | — | Resolve paragraph style properties, including docDefaults cascade |
| resolveRunStyle | — | Resolve run (character) style properties |
Interfaces(12)
FontFamilyAttrs
Font family mark attributes
interface FontFamilyAttrs| Member | Type | Summary |
|---|---|---|
| ascii? | string | |
| asciiTheme? | string | |
| cs? | string | |
| csTheme? | string | |
| eastAsia? | string | |
| eastAsiaTheme? | string | |
| hAnsi? | string | |
| hAnsiTheme? | string |
FontSizeAttrs
Font size mark attributes
interface FontSizeAttrs| Member | Type | Summary |
|---|---|---|
| size | number |
HyperlinkAttrs
Hyperlink mark attributes
interface HyperlinkAttrs| Member | Type | Summary |
|---|---|---|
| href | string | |
| rId? | string | |
| tooltip? | string |
ImageAttrs
Image node attributes
interface ImageAttrs| Member | Type | Summary |
|---|---|---|
| allowOverlap? | boolean | `wp:anchor allowOverlap`. Same tri-state convention as `layoutInCell`. |
| alt? | string | |
| borderColor? | string | Border color as CSS color string |
| borderStyle? | string | Border style (CSS border-style value) |
| borderWidth? | number | Border width in pixels |
| cropBottom? | number | |
| cropLeft? | number | |
| cropRight? | number | |
| cropTop? | number | `wp:srcRect` crop fractions in [0, 1]. Each side is the fraction of the source image that should be hidden. Renders as CSS `clip-path: inset(...)`. |
| cssFloat? | 'left' | 'right' | 'none' | CSS float direction for floating images |
| displayMode? | 'inline' | 'float' | 'block' | Display mode for CSS: inline (flows with text), float (left/right float), block (centered) |
| distBottom? | number | Distance from text below (pixels) |
| distLeft? | number | Distance from text left (pixels) |
| distRight? | number | Distance from text right (pixels) |
| distTop? | number | Distance from text above (pixels) |
| effectExtentBottom? | number | |
| effectExtentLeft? | number | |
| effectExtentRight? | number | |
| effectExtentTop? | number | `wp:effectExtent` padding (pixels) — extra space reserved around the image for shadows, glows, soft edges, etc. Applied as outer margin so the effect isn't clipped by surrounding content. |
| height? | number | Height in pixels (already converted from EMU) |
| hlinkHref? | string | Hyperlink URL for clickable image |
| layoutInCell? | boolean | `wp:anchor layoutInCell`. Tri-state: true / false / undefined (= Word's default "1"). Floating-only; round-tripped on save. |
| opacity? | number | `a:alphaModFix amt` mapped to CSS `opacity` in [0, 1]. |
| position? | ImagePositionAttrs | Position for floating images (horizontal and vertical alignment) |
| rId? | string | |
| src | string | |
| title? | string | |
| transform? | string | CSS transform string (rotation, flip) |
| width? | number | Width in pixels (already converted from EMU) |
| wrapText? | string | Wrap text setting from DOCX (left, right, bothSides, largest) for round-trip |
| wrapType? | WrapType | Wrap type from DOCX: inline, square, tight, through, topAndBottom, behind, inFront |
ParagraphAttrs
Paragraph node attributes - maps to ParagraphFormatting
interface ParagraphAttrs| Member | Type | Summary |
|---|---|---|
| _originalFormatting? | ParagraphFormatting | Original inline paragraph formatting from DOCX (pre-style-resolution). Used by fromProseDoc for lossless round-trip serialization. |
| _sectionProperties? | SectionProperties | Full section properties for paragraphs that end a section. Used by layout engine for per-section column/page config and round-trip. |
| alignment? | ParagraphAlignment | |
| bidi? | boolean | |
| bookmarks? | Array<{
id: number;
name: string;
}> | |
| borders? | {
top?: BorderSpec;
bottom?: BorderSpec;
left?: BorderSpec;
right?: BorderSpec;
between?: BorderSpec;
bar?: BorderSpec;
} | |
| contextualSpacing? | boolean | Contextual spacing — suppress space between same-style paragraphs |
| defaultTextFormatting? | TextFormatting | |
| hangingIndent? | boolean | |
| indentFirstLine? | number | |
| indentLeft? | number | |
| indentRight? | number | |
| keepLines? | boolean | |
| keepNext? | boolean | |
| lineSpacing? | number | |
| lineSpacingRule? | LineSpacingRule | |
| listAbstractNumId? | number | See ListRendering.abstractNumId. |
| listIsBullet? | boolean | Whether this is a bullet list |
| listLevelNumFmts? | NumberFormat[] | NumberFormat for each level 0..ilvl (inclusive). Lets toFlowBlocks resolve multi-level templates like "%1.%2." with the correct format per token. |
| listMarker? | string | Computed list marker text (e.g., "1.", "1.1.", "•") |
| listMarkerFontFamily? | string | Marker font family from numbering level rPr |
| listMarkerFontSize? | number | Marker font size from numbering level rPr, in points |
| listNumFmt? | NumberFormat | List number format (decimal, lowerRoman, upperRoman, etc.) for CSS counter styling |
| listStartOverride? | number | See ListRendering.startOverride. |
| numPr? | {
numId?: number;
ilvl?: number;
} | |
| outlineLevel? | number | |
| pageBreakBefore? | boolean | |
| paraId? | string | |
| renderedPageBreakBefore? | boolean | Word's cached layout marker (`<w:lastRenderedPageBreak/>`). Treated like `pageBreakBefore` for layout, kept as a separate attr so save+reload preserves the marker at the same position Word recorded. |
| sectionBreakType? | 'nextPage' | 'continuous' | 'oddPage' | 'evenPage' | |
| shading? | ShadingProperties | |
| spaceAfter? | number | |
| spaceBefore? | number | |
| spacingExplicit? | SpacingExplicit | See ParagraphFormatting.spacingExplicit. |
| styleId? | string | |
| tabs? | TabStop[] | |
| textId? | string |
ResolvedParagraphStyle
Resolved style properties ready for rendering
interface ResolvedParagraphStyle| Member | Type | Summary |
|---|---|---|
| paragraphFormatting? | ParagraphFormatting | Paragraph formatting (alignment, spacing, indentation, etc.) |
| runFormatting? | TextFormatting | Default run formatting from the style |
SelectionContext
Selection context for toolbar state
interface SelectionContext| Member | Type | Summary |
|---|---|---|
| activeCommentIds | number[] | Active comment IDs at cursor position |
| endParagraphIndex | number | End paragraph index |
| hasSelection | boolean | Whether there's a non-collapsed selection |
| inDeletion | boolean | Whether cursor is inside a tracked deletion |
| inInsertion | boolean | Whether cursor is inside a tracked insertion |
| inList | boolean | Whether cursor is in a list |
| isMultiParagraph | boolean | Whether selection spans multiple paragraphs |
| listLevel? | number | List level (0-8) |
| listType? | 'bullet' | 'numbered' | List type if in list |
| paragraphFormatting | ParagraphFormatting | Current paragraph formatting |
| startParagraphIndex | number | Start paragraph index |
| textFormatting | TextFormatting | Current text formatting at cursor/selection |
SelectionState
Selection state for toolbar integration
interface SelectionState| Member | Type | Summary |
|---|---|---|
| endParagraphIndex | number | End paragraph index |
| hasSelection | boolean | Whether there's an active selection (not just cursor) |
| isMultiParagraph | boolean | Whether selection spans multiple paragraphs |
| paragraphFormatting | ParagraphFormatting | Current paragraph formatting |
| startParagraphIndex | number | Start paragraph index |
| styleId | string | null | Current paragraph style ID (e.g., 'Heading1', 'Normal') |
| textFormatting | TextFormatting | Current text formatting at selection/cursor |
TableContextInfo
Table selection context + navigation helpers.
`getTableContext` walks the selection up from `$from` and reports which table / row / cell the cursor is in, plus the table's row/column counts, whether a multi-cell selection is active, and the current cell's border + fill colors (so the toolbar's color pickers can show the live values).
`goToNextCell` / `goToPrevCell` are tab-stop-style cell navigation commands registered by the plugin extension.
interface TableContextInfo| Member | Type | Summary |
|---|---|---|
| canSplitCell? | boolean | |
| cellBackgroundColor? | string | Current cell's background/fill color (RGB hex without #), if any |
| cellBorderColor? | ColorValue | Current cell's dominant border color, if any |
| columnCount? | number | |
| columnIndex? | number | |
| hasMultiCellSelection? | boolean | |
| isInTable | boolean | |
| rowCount? | number | |
| rowIndex? | number | |
| table? | Node | |
| tablePos? | number |
TextColorAttrs
Text color mark attributes
interface TextColorAttrs| Member | Type | Summary |
|---|---|---|
| rgb? | string | |
| themeColor? | ThemeColorSlot | |
| themeShade? | string | |
| themeTint? | string |
ToProseDocOptions
Options for document conversion
interface ToProseDocOptions| Member | Type | Summary |
|---|---|---|
| styles? | StyleDefinitions | Style definitions for resolving paragraph styles |
UnderlineAttrs
Underline mark attributes
interface UnderlineAttrs| Member | Type | Summary |
|---|---|---|
| color? | TextColorAttrs | |
| style? | UnderlineStyle |
Type aliases(2)
BorderPreset
Cell-border commands. Each command applies a preset / individual side / color / width to the cells targeted by the current selection (single cursor cell or active `CellSelection`).
All four commands use the shared `buildTableGrid` lookup to find each cell's neighbours in the grid, then sync the matching edge on the adjacent cell — Google-Docs style edge-symmetric border editing.
Schema-free: only attribute updates via `tr.setNodeMarkup`.
type BorderPreset = 'all' | 'outside' | 'inside' | 'none';SelectionChangeCallback
Callback type for selection changes
type SelectionChangeCallback = (context: SelectionContext) => void;Variables(28)
alignCenter
alignCenter: CommandalignJustify
alignJustify: CommandalignLeft
alignLeft: CommandalignRight
alignRight: CommandclearFontFamily
clearFontFamily: CommandclearFontSize
clearFontSize: CommandclearFormatting
Clear all text formatting (remove all marks)
clearFormatting: CommandclearHighlight
clearHighlight: CommandclearStyle
clearStyle: CommandclearTextColor
clearTextColor: CommanddecreaseListLevel
decreaseListLevel: CommandgenerateTOC
generateTOC: CommandincreaseListLevel
increaseListLevel: CommandinsertPageBreak
Insert a page break at the current cursor position. Always ensures a paragraph follows the page break and places the cursor there.
insertPageBreak: CommandremoveHyperlink
removeHyperlink: CommandremoveList
removeList: Commandschema
schema: prosemirror_model.Schema<any, any>selectionTrackerKey
Plugin key for accessing selection tracker state
selectionTrackerKey: PluginKey<SelectionContext>setLtr: CommandsetRtl: CommandtoggleBold
toggleBold: CommandtoggleBulletList
toggleBulletList: CommandtoggleItalic
toggleItalic: CommandtoggleNumberedList
toggleNumberedList: CommandtoggleStrike
toggleStrike: CommandtoggleSubscript
toggleSubscript: CommandtoggleSuperscript
toggleSuperscript: CommandtoggleUnderline
toggleUnderline: Command