@docx-editor.dev/core/contracts/document
@docx-editor.dev/core/contracts/document — the document-level edit and query vocabulary.
The subset that means something without a live editor: what an automation host or an LLM tool can ask for and change.
A CONTRACT module, not a barrel. contracts/editor builds the Editor command and query surfaces on top of these, so they cannot live in the package root: the root re-exports runtime from ../editor, and a contract importing the root would invert the dependency — safe today only because that import is type-only, and one accidental value import away from pulling the painted engine into a server bundle.
Interfaces (44)
ApplyResultinterfaceSource ↗
What applying a batch of edits produced: the new document, and a per-edit verdict.
results is positionally aligned with the input, so an edit that failed is identified by its index rather than by anything the caller has to correlate.
interface ApplyResult| Member | Type | Summary |
|---|---|---|
| doc | DocxDocument | |
| results | ExecResult[] | One per edit, positionally aligned with the input. |
ContentControlinterfaceSource ↗
Structured document tag (w:sdt).
interface ContentControl| Member | Type | Summary |
|---|---|---|
| alias? | string | |
| content | readonly Block[] | |
| controlType | ContentControlType | |
| id | string | |
| kind | 'contentControl' | |
| locked? | boolean | |
| tag? | string |
ContentControlFilterinterfaceSource ↗
Narrows a content-control query. Fields combine with AND; an empty filter matches every control.
interface ContentControlFilter| Member | Type | Summary |
|---|---|---|
| alias? | string | |
| controlType? | ContentControlType | |
| tag? | string |
ContentControlSummaryinterfaceSource ↗
A content control reduced to what a listing needs: its identity, kind, and lock state.
interface ContentControlSummary| Member | Type | Summary |
|---|---|---|
| alias? | string | |
| controlType | ContentControlType | |
| id | string | |
| locked? | boolean | |
| tag? | string |
DocAnchorinterfaceSource ↗
The LLM- and JSON-facing address for a piece of a document.
paraId is the 8-hex w14:paraId, matched case-insensitively. search is a phrase that must match EXACTLY ONCE inside that paragraph; ambiguous or missing matches fail with 'ambiguous' / 'notFound' rather than falling back to first-match.
Offset-based addressing was tried and abandoned: an agent cannot compute a character offset it has not seen, and offsets do not survive concurrent edits. Do not reintroduce { blockId, offset }.
interface DocAnchor| Member | Type | Summary |
|---|---|---|
| occurrence? | number | Opt-in disambiguation. Omit to require uniqueness. |
| paraId | string | |
| search? | string |
DocAnchorRangeinterfaceSource ↗
A position in one story: a paragraph and a UTF-16 offset inside it.
The same offset space the ops and the caret use, so an anchor read here can be handed straight back to a selection without re-deriving anything.
interface DocAnchorRange| Member | Type | Summary |
|---|---|---|
| endOffset | number | |
| endParagraphId | string | May sit in a later paragraph: the range markers are independent elements. |
| part | string | Canonical part name of the story the range lives in, e.g. `/word/document.xml`. |
| startOffset | number | |
| startParagraphId | string |
DocCommentinterfaceSource ↗
One comment or reply, as comments.xml records it.
A reply is a comment with a parentId; OOXML gives replies no separate element, so the thread is reconstructed from that link rather than from nesting.
interface DocComment| Member | Type | Summary |
|---|---|---|
| anchor? | DocAnchorRange | Where the comment is anchored, absent when the file gave it no usable range. |
| author | string | |
| date? | string | OPTIONAL, because `CT_Comment` makes `@w:date` optional and files omit it. A comment with no date is a comment, not a defect, and fabricating one is a content change. |
| id | string | |
| orphaned? | boolean | True when the file gave this comment no usable range — a reference with no markers, or a start with no end. Reported rather than dropped: a reviewer's remark vanishing silently is worse than one that says it lost its text. |
| parentId? | string | |
| resolved? | boolean | |
| text | string |
DocEditsinterfaceSource ↗
The document-executable edit vocabulary.
An interface rather than a closed union so extensions can widen it by declaration merging. A sealed union cannot be extended by a plugin, and the runtime dispatch is already registry-backed.
interface DocEdits| Member | Type | Summary |
|---|---|---|
| acceptAllRevisions | Record<never, never> | |
| acceptRevision | {
id: number;
part?: 'body' | 'footnote' | 'endnote';
noteId?: number;
} | |
| addComment | {
target: DocTarget;
text: string;
author: string;
} | |
| addRepeatingSectionItem | {
target: DocTarget;
index?: number;
} | |
| adjustIndent | {
target: DocTarget;
direction: 'increase' | 'decrease';
} | Word's Increase/Decrease Indent. |
| applyFormatting | {
target: DocTarget;
marks: RunFormatting;
} | |
| applyVariables | {
values: Record<string, string>;
} | |
| deleteText | {
target: DocTarget;
} | |
| insertBreak | {
target: DocTarget;
kind: 'page' | 'column' | 'line' | 'section' | 'sectionContinuous';
} | `'section'` is Word's Next Page break; `'sectionContinuous'` keeps the new section on the sheet the previous one ended, which is how a mid-page column or margin change is authored. `'column'` belongs to the multi-column lane and is refused for now. |
| insertContentControl | {
target: DocTarget;
subtype: InsertableContentControlType;
tag?: string;
title?: string;
} | Author a NEW control over the addressed span. |
| insertHyperlink | {
target: DocTarget;
href: string;
text?: string;
} | |
| insertImage | {
target: DocTarget;
data: Uint8Array;
extent?: Extent;
} | |
| insertTable | {
target: DocTarget;
rows: number;
cols: number;
} | |
| insertText | {
target: DocTarget;
text: string;
} | |
| mergeParagraphs | {
target: DocTarget;
} | |
| proposeDeletion | {
target: DocTarget;
author: string;
} | |
| proposeInsertion | {
target: DocTarget;
text: string;
author: string;
} | |
| proposeReplacement | {
target: DocTarget;
replaceWith: string;
author: string;
} | Authored family. `author` is required: tracked-ness is verb identity, not a boolean flag, so there is no global trackChanges toggle to forget. |
| rejectAllRevisions | Record<never, never> | |
| rejectRevision | {
id: number;
part?: 'body' | 'footnote' | 'endnote';
noteId?: number;
} | |
| removeContentControl | {
target: DocTarget;
} | |
| removeHyperlink | {
target: DocTarget;
} | |
| removeRepeatingSectionItem | {
target: DocTarget;
index: number;
} | |
| replaceText | {
target: DocTarget;
text: string;
} | |
| replyComment | {
commentId: string;
text: string;
author: string;
} | |
| resolveComment | {
commentId: string;
} | |
| setContentControlValue | {
target: DocTarget;
value: string;
} | |
| setParagraphStyle | {
target: DocTarget;
styleId: string;
} | |
| setVariable | {
name: string;
value: string;
} | |
| splitParagraph | {
target: DocTarget;
} | |
| toggleList | {
target: DocTarget;
kind: 'bullet' | 'ordered';
} | Word's Bullets and Numbering. |
DocLocationinterfaceSource ↗
Structural addressing for content the paraId map cannot reach.
interface DocLocation| Member | Type | Summary |
|---|---|---|
| container | ContainerRef | |
| offset? | number | |
| path | number[] | Block indices, descending into tables and content controls. |
DocQueriesinterfaceSource ↗
The document-readable query vocabulary, keyed identically to [DocQueryResults](DocQueryResults).
An interface rather than a closed union for the same reason [DocEdits](DocEdits) is: an extension widens it by declaration merging, and the runtime dispatch is registry-backed.
interface DocQueries| Member | Type | Summary |
|---|---|---|
| comments | {
resolved?: boolean;
} | |
| contentControls | {
filter?: ContentControlFilter;
} | |
| findText | {
text: string;
container?: ContainerRef;
} | |
| paragraphs | {
container?: ContainerRef;
} | |
| revisions | {
part?: 'body' | 'footnote' | 'endnote';
} | |
| styles | Record<never, never> | |
| variables | Record<never, never> |
DocQueryResultsinterfaceSource ↗
What each query returns. Keyed identically to DocQueries.
interface DocQueryResults| Member | Type | Summary |
|---|---|---|
| comments | readonly DocComment[] | |
| contentControls | readonly ContentControlSummary[] | |
| findText | readonly DocRange[] | |
| paragraphs | readonly ParagraphSummary[] | |
| revisions | readonly Revision[] | |
| styles | StyleDefinitions | |
| variables | Readonly<Record<string, string>> |
DocRangeinterfaceSource ↗
A span between two positions. The endpoints may be addressed either way, independently.
interface DocRange| Member | Type | Summary |
|---|---|---|
| from | DocAnchor | DocLocation | |
| to | DocAnchor | DocLocation |
DocumentBodyinterfaceSource ↗
The main story: its blocks in reading order, plus the sections derived from them.
interface DocumentBody| Member | Type | Summary |
|---|---|---|
| content | readonly Block[] | |
| sections | readonly Section[] | Derived, not stored: recomputed on read from section-break markers and section inheritance. Never treat it as a spreadable field. |
DocxDocumentinterfaceSource ↗
A parsed .docx.
NOT JSON-round-trippable: it holds Maps and Dates, plus an internal side-table of verbatim XML used for lossless round-tripping. Use toJSON / fromJSON before sending it over JSON-RPC or handing it to a model.
interface DocxDocument| Member | Type | Summary |
|---|---|---|
| body | DocumentBody | |
| comments | readonly DocComment[] | |
| revisions | readonly Revision[] | |
| styles | StyleDefinitions | |
| theme? | Theme |
ExtentinterfaceSource ↗
A size in EMUs, the unit DrawingML stores extents in. 914400 EMU = 1 inch.
interface Extent| Member | Type | Summary |
|---|---|---|
| heightEmu | number | |
| widthEmu | number |
HeaderFooterSetinterfaceSource ↗
A section's three header (or footer) variants, each a relationship id.
All optional: a variant a section does not declare inherits the previous section's, and one absent everywhere means the document simply has none.
interface HeaderFooterSet| Member | Type | Summary |
|---|---|---|
| default? | string | |
| even? | string | |
| first? | string |
HistoryGroupOutcomeinterfaceSource ↗
The history authority's result for this frame.
interface HistoryGroupOutcome| Member | Type | Summary |
|---|---|---|
| kind | 'started' | 'extended' | 'split' | 'none' | |
| reason? | 'history-boundary' | 'undo-redo' | 'no-history'
/** @deprecated No longer reported: writes that span document parts now extend the group. */
| 'package-unit' | 'composition' |
IndentFormattinginterfaceSource ↗
Indent at the selection, in twips.
Unlike every other field here, this does NOT go absent when the selection's paragraphs disagree. A ruler has to draw its handles somewhere, and Word draws them at the FIRST selected paragraph's values — Select All is the commonest indent gesture, and hiding the handles for it would be worse than showing one paragraph's truth. The values are therefore always the first touched paragraph's, and [mixed](mixed) records per field whether the rest agree.
firstLine is ONE SIGNED offset: negative is a hanging indent. OOXML spells it as two mutually exclusive attributes and this collapses them hanging-wins (§17.3.1.12), which is the model Word itself keeps.
Absent inside a table. The value would be correct there, but it is measured from the cell's content edge while a ruler is drawn against the page's margin, and the ruler does not know the cell.
interface IndentFormatting| Member | Type | Summary |
|---|---|---|
| firstLine | number | First-line offset from [left](left), signed. Negative is a hanging indent. |
| left | number | Leading indent (`w:left`), signed: the left one, or the right one in a right-to-left paragraph. Negative pulls text into the margin, as Word allows. |
| mixed | {
readonly left: boolean;
readonly right: boolean;
readonly firstLine: boolean;
} | Per field, whether the selection's paragraphs disagree about it. |
| right | number | Trailing indent (`w:right`), signed: the right one, or the left one right to left. |
| rtl? | true | The first selected paragraph reads right to left, so `left` and the first line sit on the right margin. Absent for a left-to-right paragraph. |
NumberingRefinterfaceSource ↗
A paragraph's list membership: which numbering.xml definition, and at which level.
interface NumberingRef| Member | Type | Summary |
|---|---|---|
| level | number | Zero-based. OOXML numbering has nine levels, 0 through 8. |
| numId | string |
PageMarginsinterfaceSource ↗
Page margins in twips.
interface PageMargins| Member | Type | Summary |
|---|---|---|
| bottomTwips | number | |
| leftTwips | number | |
| rightTwips | number | |
| topTwips | number |
ParagraphinterfaceSource ↗
One paragraph: its runs, the style it names, and its list membership.
interface Paragraph| Member | Type | Summary |
|---|---|---|
| kind | 'paragraph' | |
| numbering? | NumberingRef | |
| paraId? | string | `w14:paraId`. The stable handle `DocAnchor` addresses. |
| runs | readonly Run[] | |
| styleId? | string |
ParagraphDisagreementsinterfaceSource ↗
Which paragraph-level reads the selection disagrees about.
interface ParagraphDisagreements| Member | Type | Summary |
|---|---|---|
| alignment | boolean | |
| direction | boolean | The selection mixes left-to-right and right-to-left paragraphs. |
| lineSpacing | boolean | |
| spaceAfterPt | boolean | |
| spaceBeforePt | boolean | |
| tabStops | boolean |
ParagraphFlagsinterfaceSource ↗
The five paragraph flags a Paragraph dialog shows as checkboxes.
contextualSpacing is Word's "Don't add space between paragraphs of the same style"; the rest are its Pagination block. null means the selection disagrees.
interface ParagraphFlags| Member | Type | Summary |
|---|---|---|
| contextualSpacing | boolean | null | |
| keepLines | boolean | null | |
| keepNext | boolean | null | |
| pageBreakBefore | boolean | null | |
| widowControl | boolean | null |
ParagraphSummaryinterfaceSource ↗
A paragraph reduced to what a listing needs: its stable handle, its text, and its style.
paraId is what a follow-up edit addresses, so a summary without one names a paragraph the file gave no w14:paraId and that a DocAnchor cannot reach.
interface ParagraphSummary| Member | Type | Summary |
|---|---|---|
| paraId? | string | |
| styleId? | string | |
| text | string |
ParagraphTabStopinterfaceSource ↗
One custom tab stop, as a command states it.
interface ParagraphTabStop| Member | Type | Summary |
|---|---|---|
| alignment | 'left' | 'center' | 'right' | 'decimal' | 'bar' | |
| leader? | 'none' | 'dot' | 'hyphen' | 'underscore' | 'heavy' | 'middleDot' | |
| positionTwips | number |
ReviewPositioninterfaceSource ↗
A position in the model offset space of one story.
interface ReviewPosition| Member | Type | Summary |
|---|---|---|
| offset | number | |
| paragraphId | string |
ReviewRangeinterfaceSource ↗
Where an item is anchored: a range in one story.
interface ReviewRange| Member | Type | Summary |
|---|---|---|
| end | ReviewPosition | |
| partName | string | |
| start | ReviewPosition |
ReviewRevisionIteminterfaceSource ↗
One tracked change as a review card.
Keyed per DECISION rather than per site: a revision spanning three ranges is one card, because accepting it accepts all three.
interface ReviewRevisionItem| Member | Type | Summary |
|---|---|---|
| address | RevisionAddress | The payload `acceptRevision` / `rejectRevision` take. |
| addresses | readonly RevisionAddress[] | EVERY address this decision covers, `address` first. |
| author | string | |
| date? | string | |
| formattingChanges? | readonly {
readonly property: 'bold' | 'italic' | 'underline' | 'strike' | 'fontFamily' | 'fontSize' | 'color' | 'highlight' | 'alignment' | 'leftIndent' | 'rightIndent' | 'firstLineIndent' | 'hangingIndent' | 'spaceBefore' | 'spaceAfter' | 'direction';
readonly value: string | null;
}[] | Changed direct formatting values, ready for localized review summaries. |
| formattingKind? | string | OOXML property-change element, used to resolve independently numbered formatting kinds. |
| formattingLanguages? | readonly string[] | Newly applied proofing language codes in a tracked formatting change. |
| id | string | Stable across renders and unique per decision within this editor instance. |
| kind | 'revision' | |
| markDirection? | 'insert' | 'delete' | 'moveFrom' | 'moveTo' | WHICH decision a `paragraphMark` records, absent for every other kind. |
| nesting | number | How deeply this change is NESTED inside other changes, 0 for an unenclosed one. |
| pairedWith? | string | The other half of a move, or the other side of a delete/insert replacement. |
| ranges | readonly ReviewRange[] | Every site this decision touches, in document order. |
| readOnly | boolean | True when the engine cannot resolve this kind, so accept and reject must not be offered. |
| replacedRangeCount? | number | How many leading `ranges` are the STRUCK half of a replacement. |
| replacedText | string | The words a replacement removes. Empty for every other kind. |
| replyIds | readonly string[] | Comments answering this change, in document order. |
| revisionKind | ReviewRevisionKind | |
| structuralChanges? | readonly ('rowInsert' | 'rowDelete' | 'cellInsert' | 'cellDelete' | 'cellMerge' | 'numberingInsert')[] | Distinct structural operations covered by this decision, in document order. |
| text | string | Text the revision covers, for the card summary. Empty for changes with no characters. |
RevisioninterfaceSource ↗
One tracked change.
Addressing needs BOTH id and part: @w:id is unique only within a part, so an id alone names two revisions in any package that has a header or a comments part.
interface Revision| Member | Type | Summary |
|---|---|---|
| author | string | |
| date? | string | OPTIONAL. `CT_TrackChange` requires `@w:id` and `@w:author` and makes `@w:date` optional, and producers that omit it are ordinary. Requiring it here forced either a fabricated date — a content change — or dropping the revision from the list. |
| id | number | Numeric, and unique only WITHIN a part. Pair with `part` to address one. |
| part | string | REQUIRED, and a canonical PART NAME rather than a three-value enum. |
| type | RevisionType |
RevisionAddressinterfaceSource ↗
How a tracked change is addressed: @w:id, @w:author and @w:date together, within a part.
All of them, always. @w:id is unique only within a part, so an id alone names two revisions in any package with a header or a comments part — and it is not author-scoped either, so (part, id) would merge two authors' distinct revisions. One logical revision is also deliberately many elements sharing an id, as a tracked row insertion is, which no uniqueness rule could express.
interface RevisionAddress| Member | Type | Summary |
|---|---|---|
| author | string | |
| date? | string | Absent when the file wrote no `@w:date`; part of the identity either way. |
| id | string |
RevisionAuthorEntryinterfaceSource ↗
One reattributed change. key and author describe the change after the update, because review keys include the author.
interface RevisionAuthorEntry extends RevisionBatchEntry| Member | Type | Summary |
|---|---|---|
| previousAuthor | string | The author the change had before the update. |
| previousKey | string | The key the caller selected the change by. It differs from `key` when the attribution changed, because review keys include the author and date. |
RevisionAuthorResultinterfaceSource ↗
Outcome of one attribution change. Counts refer to review decisions, not XML markers.
interface RevisionAuthorResult| Member | Type | Summary |
|---|---|---|
| skipped | readonly {
readonly key: string;
readonly reason: RevisionAuthorSkipReason;
readonly revision?: RevisionBatchEntry;
}[] | Requested changes that kept their attribution. |
| updated | readonly RevisionAuthorEntry[] | Changes that now carry the requested attribution, in selection order. |
RevisionBatchEntryinterfaceSource ↗
A canonical review decision. Keys are valid only in the open document.
interface RevisionBatchEntry| Member | Type | Summary |
|---|---|---|
| author | string | |
| formattingKind? | string | For a formatting change, its OOXML property-change element, as the review item has it. |
| key | string | |
| partName | string | |
| revisionKind | ReviewRevisionItem['revisionKind'] |
RevisionBatchResultinterfaceSource ↗
Outcome of one selected-set decision. Counts refer to review decisions, not XML markers.
interface RevisionBatchResult| Member | Type | Summary |
|---|---|---|
| remaining | number | All pending decisions in the addressed scope, including excluded and unsupported changes. |
| resolved | readonly RevisionBatchEntry[] | |
| skipped | readonly {
readonly key: string;
readonly reason: RevisionBatchSkipReason;
readonly revision?: RevisionBatchEntry;
readonly detail?: RevisionBatchSkipDetail;
}[] |
RuninterfaceSource ↗
A stretch of text sharing one set of character properties.
interface Run| Member | Type | Summary |
|---|---|---|
| formatting? | RunFormatting | |
| revisionId? | number | Set when the run carries a tracked change. Unique only within its part. |
| text | string |
RunFormattinginterfaceSource ↗
Character formatting, and — at selection level — the paragraph properties a toolbar reads alongside it.
One type serves both roles so a toolbar reads alignment, style and script state from the same object as bold and italic. On a [Run](Run) the selection-level fields stay absent: a run has no alignment or paragraph style of its own.
interface RunFormatting| Member | Type | Summary |
|---|---|---|
| alignment? | 'left' | 'center' | 'right' | 'both' | Paragraph alignment at the selection, as a PHYSICAL edge. `both` is OOXML's spelling of justify. A right-to-left paragraph with no `w:jc` reads `right`. |
| bold? | boolean | |
| color? | ColorValue | |
| direction? | 'ltr' | 'rtl' | Paragraph base direction at the selection (`w:bidi` through the style cascade). Absent when the selection's paragraphs disagree; `disagrees.direction` then says so. |
| disagrees? | ParagraphDisagreements | Which paragraph-level fields above are absent because the selection DISAGREES, as opposed to because nothing states them. |
| fontFamily? | string | |
| fontSizePt? | number | |
| highlight? | string | |
| indent? | IndentFormatting | The EFFECTIVE paragraph indent at the selection — cascade and numbering merge included, so a numbered item that authors no `w:ind` reports the indent its list definition gives it. Absent when nothing is loaded, or when the selection is inside a table (see [IndentFormatting](IndentFormatting)). |
| italic? | boolean | |
| lineSpacing? | {
readonly rule: 'multiple' | 'exact' | 'atLeast';
readonly value: number;
} | Line spacing at the selection, in the unit its rule implies — LINES for `multiple`, points for `exact` and `atLeast`. The same vocabulary `setLineSpacing` takes, so a control can show what it reads and send back what it shows. The effective value: a paragraph whose cascade states none reads as single spacing (`multiple`, 1). Absent when the selection's paragraphs disagree. |
| paragraphFlags? | ParagraphFlags | The paragraph flags the Paragraph dialog shows as checkboxes. |
| spaceAfterPt? | number | |
| spaceBeforePt? | number | Space above and below the paragraph at the selection, in points. |
| strike? | boolean | |
| styleId? | string | Paragraph style id (`w:pStyle`) at the selection. |
| subscript? | boolean | |
| superscript? | boolean | |
| tabStops? | readonly ParagraphTabStop[] | The paragraph's custom tab stops at the selection, cascade included. Absent when the selection disagrees or the paragraphs have none. |
| underline? | boolean |
SectioninterfaceSource ↗
One section: the page it lays out on, and the header/footer stories it declares.
interface Section| Member | Type | Summary |
|---|---|---|
| footers | HeaderFooterSet | |
| headers | HeaderFooterSet | |
| properties | SectionProperties |
SectionPropertiesinterfaceSource ↗
w:sectPr: the page a section lays out on. Twips throughout, as the file stores them.
interface SectionProperties| Member | Type | Summary |
|---|---|---|
| columns? | {
count: number;
gapTwips: number;
} | |
| margins | PageMargins | |
| pageSize | {
widthTwips: number;
heightTwips: number;
} | |
| titlePage? | boolean | `w:titlePg` — whether the section's first page takes the `first` header/footer variant. |
StyleDefinitioninterfaceSource ↗
One style: the ID content references, the name a reader sees, and its inheritance link.
interface StyleDefinition| Member | Type | Summary |
|---|---|---|
| basedOn? | string | `w:basedOn` — the style this one inherits from. Absent at the root of a chain. |
| id | string | |
| name | string |
StyleDefinitionsinterfaceSource ↗
styles.xml, split by the three style families that address separately.
Keyed by style ID rather than by the name a reader sees, because the ID is what a paragraph or run actually references.
interface StyleDefinitions| Member | Type | Summary |
|---|---|---|
| character | ReadonlyMap<string, StyleDefinition> | |
| paragraph | ReadonlyMap<string, StyleDefinition> | |
| table | ReadonlyMap<string, StyleDefinition> |
TableinterfaceSource ↗
A table: its rows, and the table style they resolve through.
interface Table| Member | Type | Summary |
|---|---|---|
| kind | 'table' | |
| rows | readonly TableRow[] | |
| styleId? | string |
TableCellinterfaceSource ↗
One cell. Its content is ordinary blocks, so a cell may hold paragraphs, nested tables and content controls alike.
interface TableCell| Member | Type | Summary |
|---|---|---|
| colSpan? | number | `w:gridSpan`. Absent means 1. |
| content | readonly Block[] | |
| rowSpan? | number | Vertical merge span. Absent means 1. |
TableRowinterfaceSource ↗
One table row. Cell count may vary between rows: colSpan and vertical merges reshape it.
interface TableRow| Member | Type | Summary |
|---|---|---|
| cells | readonly TableCell[] |
ThemeinterfaceSource ↗
theme1.xml — what a [ColorValue](ColorValue) of kind theme resolves against.
interface Theme| Member | Type | Summary |
|---|---|---|
| colorScheme | ThemeColorScheme | |
| fontScheme? | Record<string, string> |
Type aliases (21)
BlocktypeSource ↗
Anything that can sit at block level in a story. Discriminate on kind.
type Block = Paragraph | Table | ContentControl;ColorValuetypeSource ↗
A colour as the FILE expresses it, not as a resolved RGB string.
A theme colour stays a theme reference — slot plus tint or shade — so that changing the theme repaints the document the way Word does. Flattening to hex at read time would freeze the resolved value and break that link. auto is Word's "let the renderer decide", usually black on white.
type ColorValue = {
readonly kind: 'hex';
readonly value: string;
} | {
readonly kind: 'theme';
readonly slot: string;
readonly tint?: number;
readonly shade?: number;
} | {
readonly kind: 'auto';
};CompareDocumentsEngineErrorCodetypeSource ↗
Why the compare engine refused a compare, as a stable code: the codes that the engine, compareDocuments, and a background engine return. compareDocuments.errors.<code> in @docx-editor.dev/i18n translates every code.
type CompareDocumentsEngineErrorCode =
/**
* An option has a value of the wrong type, such as a `detectFormatChanges` that is not a
* boolean, or the revised document is not a `Uint8Array`.
*/
'invalidOptions'
/** The original document is not a readable DOCX package. */
| 'invalidOriginal'
/** The revised document is not a readable DOCX package. */
| 'invalidRevised'
/** The documents exceed the compare's size limits. */
| 'tooLarge'
/**
* The original document enforces protection that the compare would break: read-only,
* comments, forms, or tracked-changes protection. A compare also writes differences that
* have no tracked form, such as style definitions and content control properties, so
* tracked-changes protection refuses too.
*/
| 'protected'
/** The differences include content that no tracked change can record. */
| 'unsupportedContent';CompareDocumentsErrorCodetypeSource ↗
Why a compare was refused, as a stable code. compareDocuments.errors.<code> in @docx-editor.dev/i18n translates every code. The engine and compareDocuments return [CompareDocumentsEngineErrorCode](CompareDocumentsEngineErrorCode) values. The editor command and dialog can also return the editor-only codes.
type CompareDocumentsErrorCode = CompareDocumentsEngineErrorCode
/** Editor only: no review module, no open document, or no mount. */
| 'unavailable'
/** Editor only: a collaboration session is attached. */
| 'collaboration'
/** Editor only: the document is open for viewing only. */
| 'viewing'
/** Editor only: the dialog closed, or the compare was canceled, before it finished. */
| 'closed'
/** Editor only: the revised file is a `Uint8Array` with no bytes. */
| 'empty'
/**
* Editor only: another compare of the same dialog runs, or a preview or an external document
* refresh blocks writes to the document. Try again when it ends.
*/
| 'busy'
/**
* Editor only: the open document changed while a background compare ran, so its result
* no longer applies. The editor never retries by itself.
*/
| 'documentChanged'
/** Editor only: any other failure. */
| 'failed';CompareDocumentsOutcometypeSource ↗
The outcome of one compare.
type CompareDocumentsOutcome = {
readonly ok: true;
readonly revisionCount: number;
readonly changed: boolean;
} | {
readonly ok: false;
readonly code: CompareDocumentsErrorCode;
readonly reason?: CompareDocumentsRefusalReason;
readonly message: string;
};CompareDocumentsRefusalReasontypeSource ↗
What an unsupportedContent refusal is about, as a stable code. compareDocuments.reasons.<reason> in @docx-editor.dev/i18n translates every reason.
type CompareDocumentsRefusalReason =
/** The documents differ inside a content control that is locked or bound to data. */
'lockedContent'
/** A document holds tracked changes that cannot be accepted, so its final state is unknown. */
| 'unresolvedRevisions'
/** The documents differ in content with no tracked-change form, such as an embedded file. */
| 'untrackedContent'
/** The revised document adds section or table settings that a reject cannot restore. */
| 'unrecordedSettings'
/** A header or footer of an earlier section cannot be paired with one of the original. */
| 'headerFooter'
/** The result needs a part type that the original document does not declare. */
| 'packageStructure'
/**
* The result fails the check: accepting all changes does not give the revised text, rejecting
* them does not give the original text, or a tracked change cannot be accepted or rejected.
*/
| 'unverified';ContainerReftypeSource ↗
Which STORY a location belongs to.
The body is one container; every header, footer and note is another. A path alone is ambiguous without it, because block index 0 exists in every story a document has.
type ContainerRef = {
part: 'body';
} | {
part: 'header' | 'footer';
rId: string;
} | {
part: 'footnote' | 'endnote';
noteId: number;
};ContentControlTypetypeSource ↗
Which kind of control a w:sdt is, and therefore what a value written into it must be.
type ContentControlType = 'richText' | 'plainText' | 'checkbox' | 'dropdown' | 'comboBox' | 'date' | 'picture' | 'buildingBlockGallery' | 'repeatingSection';DocEdittypeSource ↗
One edit, as a discriminated union derived from [DocEdits](DocEdits).
Adding a key to DocEdits — including by declaration merging from a plugin — widens this automatically, so the union never drifts from the vocabulary it is built out of.
type DocEdit = {
[K in keyof DocEdits]: {
type: K;
} & DocEdits[K];
}[keyof DocEdits];DocQuerytypeSource ↗
One query, as a discriminated union derived from [DocQueries](DocQueries).
type DocQuery = {
[K in keyof DocQueries]: {
type: K;
} & DocQueries[K];
}[keyof DocQueries];DocTargettypeSource ↗
Anything an operation can be pointed at: a paragraph-relative [DocAnchor](DocAnchor), a structural [DocLocation](DocLocation), or a [DocRange](DocRange) spanning two of them.
type DocTarget = DocAnchor | DocLocation | DocRange;ExecErrorCodetypeSource ↗
Why a write was refused, as a value to branch on.
Deliberately finer-grained than a boolean: "no-op", "target not found" and "content control is locked" are different outcomes, and a caller retrying the first should not retry the third.
type ExecErrorCode = 'notFound' | 'ambiguous' | 'locked' | 'bound' | 'typeMismatch' | 'kindMismatch' | 'outOfBounds' | 'unsupported' | 'invalidArgs';ExecResulttypeSource ↗
Every write returns this rather than boolean.
A bare boolean cannot distinguish "no-op" from "target not found" from "content control is locked", and the editor layer already throws eight distinct ContentControl error classes that a boolean would flatten.
type ExecResult = {
ok: true;
changed: boolean;
revisions?: RevisionBatchResult;
revisionAuthors?: RevisionAuthorResult;
history?: HistoryGroupOutcome;
compareDocuments?: CompareDocumentsOutcome;
} | {
ok: false;
code: ExecErrorCode;
reason: string;
target?: DocTarget;
revisions?: RevisionBatchResult;
compareDocuments?: CompareDocumentsOutcome;
revisionAuthors?: RevisionAuthorResult;
};InsertableContentControlTypetypeSource ↗
The control kinds an insertion can author.
A NARROWING of [ContentControlType](ContentControlType), not a separate vocabulary: a kind read off an existing control can be handed straight back to insertContentControl. Three kinds are absent because an insertion would have to invent what they carry — a checkbox needs its two declared glyph states, a picture needs an image part, a building block gallery needs a glossary to list, and a repeating section is a container whose items have their own verbs. Reading still answers the wider type; a document may hold kinds this cannot create.
dropDownList is the same kind as dropdown, spelt as OOXML spells it. Both are accepted, so a type read off a control can be handed back without translating it — and the OOXML spelling is the one the automation protocol takes, so a caller who uses both surfaces can write that one everywhere.
type InsertableContentControlType = Extract<ContentControlType, 'richText' | 'plainText' | 'dropdown' | 'comboBox' | 'date'> | 'dropDownList';ReviewRevisionKindtypeSource ↗
What kind of decision a revision card represents.
Wider than the four content wrappers, because a reviewer has to be shown every pending decision, including the ones that decorate no characters. A card the surface cannot show is a change the reviewer never learns about — and acceptAllRevisions refuses if ANY revision in the document is one the engine cannot resolve, so an invisible one makes Accept All fail for a reason nothing on screen explains.
type ReviewRevisionKind = 'insert' | 'delete'
/**
* A combined deletion and insertion decision. The built-in reader lists them separately,
* unless a review query asks to pair replacements or a custom review provider combines them.
*/
| 'replace' | 'moveFrom' | 'moveTo'
/** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
| 'format'
/** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
| 'paragraphMark'
/** A row, cell, section or grid revision. Supported row revisions are resolvable. */
| 'structural';RevisionAuthorSkipReasontypeSource ↗
Why a requested change keeps its attribution.
type RevisionAuthorSkipReason = 'unknown-revision' | 'unsupported-revision';RevisionBatchSkipDetailtypeSource ↗
The structure behind a skip.
For unsupported-revision: a refused site, a formatting change whose earlier properties were not recorded so it cannot be rejected, a read-only review item, a change with no sites, a change a content control lock keeps (locked-content), or a change in a data-bound content control (bound-content). The last two have their own codes, review.locked-content and review.bound-content; reviewCodeOfSkip reads them.
For incomplete-group, the change the decision would also decide: the other half of a move or merge (paired-change), a change inside content the decision removes (removed-content) or inside properties a reject replaces (replaced-properties), a change the decision would empty (emptied-change), a change that encloses the selected one (enclosing-change), or another site of a group the planner keeps together (grouped-change).
For retained-structure: a row kept for the table nested in it (nested-table).
type RevisionBatchSkipDetail = RevisionSiteRefusal | 'unrecorded-properties' | 'read-only' | 'no-sites' | 'paired-change' | 'removed-content' | 'replaced-properties' | 'emptied-change' | 'enclosing-change' | 'grouped-change' | 'nested-table' | 'locked-content' | 'bound-content';RevisionBatchSkipReasontypeSource ↗
Why a requested decision remains pending.
type RevisionBatchSkipReason = 'unsupported-revision' | 'incomplete-group' | 'retained-structure' | 'unknown-revision';RevisionSiteRefusaltypeSource ↗
Why a revision site cannot be resolved: no w:id or author, a content marker where content cannot be, a wrapper whose content is itself marked, a structural revision outside tables and numbering, or a move whose ranges do not pair up.
type RevisionSiteRefusal = 'unaddressed' | 'misplaced-marker' | 'nested-marker' | 'unsupported-structure' | 'unsupported-move';RevisionTypetypeSource ↗
What kind of decision a revision represents.
Wider than insert/delete/format, and it has to be. w:moveFrom/w:moveTo are not a deletion and an insertion — resolving one half alone duplicates or loses the content; w:pPr/w:rPr/w:ins|w:del decorates no characters at all and merges paragraphs when resolved; a row or cell revision is structural. A reviewer shown only three kinds is a reviewer who never learns about the rest.
type RevisionType = 'insert' | 'delete'
/** A deletion and an insertion that are one edit: text typed over a selection. */
| 'replace' | 'moveFrom' | 'moveTo'
/** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
| 'format'
/** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
| 'paragraphMark'
/** A row, cell, section or grid revision. */
| 'structural';ThemeColorSchemetypeSource ↗
Theme colour slots (accent1, dk1, lt2, …) to hex.
type ThemeColorScheme = Readonly<Record<string, string>>;