wordinweb 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -123,10 +123,21 @@ interface RunProps {
123
123
  raise?: number;
124
124
  /** w:outline — hollow stroked glyphs. */
125
125
  outline?: boolean;
126
+ /** w14:textOutline — glyph outline stroke (WordArt gallery styles). */
127
+ textOutline?: {
128
+ color: string;
129
+ width: number;
130
+ };
131
+ /** w:shadow / w14:shadow — offset glyph shadow. */
132
+ textShadow?: boolean;
126
133
  /** w:emboss / w:imprint — Word triple-draws offset copies. */
127
134
  emboss?: boolean;
128
135
  imprint?: boolean;
129
136
  vanish?: boolean;
137
+ /** w:rPr w:snapToGrid: whether this run participates in the section's
138
+ * docGrid. A line whose runs ALL declare 0 lays off the grid (see
139
+ * finishLine); ParaProps.snapToGrid covers the whole paragraph. */
140
+ snapToGrid?: boolean;
130
141
  /** Character style id (w:rStyle). */
131
142
  styleId?: string;
132
143
  lang?: string;
@@ -308,7 +319,7 @@ interface EmbeddedObjectReference {
308
319
  progId: string;
309
320
  }
310
321
  /** OMML equation node (subset: runs, scripts, fractions, radicals). */
311
- type MathNode = {
322
+ type MathNode = ({
312
323
  t: "run";
313
324
  text: string;
314
325
  normal?: boolean;
@@ -377,6 +388,15 @@ type MathNode = {
377
388
  pos: "low" | "upp";
378
389
  e: MathNode[];
379
390
  lim: MathNode[];
391
+ }) & {
392
+ /**
393
+ * The OMML element this node was read from. Only the math EDITOR's reader
394
+ * (edit/math.ts) sets it; the renderer's parser leaves it undefined. Editing
395
+ * re-emits through it, so the properties the linear syntax does not spell
396
+ * out — run fonts, m:ctrlPr, a matrix's column specs, an n-ary's limit
397
+ * placement — survive a text edit instead of being dropped.
398
+ */
399
+ src?: XmlElement;
380
400
  };
381
401
  interface MathContent {
382
402
  kind: "math";
@@ -405,6 +425,11 @@ interface FieldContent {
405
425
  * (in the fldChar begin's w:ffData). Its presence makes the rendered ballot
406
426
  * box a click-to-toggle target; the glyph derives from its w:checked child. */
407
427
  checkbox?: XmlElement;
428
+ /** The field's own XML: the `w:fldSimple`, or the `w:fldChar` begin of a
429
+ * complex field. The update pass (edit/update-fields.ts) writes a recomputed
430
+ * result back through it — layout resolves fields from this model, so
431
+ * without the link the computed value has nowhere to land in the file. */
432
+ src?: XmlElement;
408
433
  }
409
434
  /** What an anchored shape's coordinates are measured from. "char" (relH=
410
435
  * "character") measures from the anchor run's pen position; "line" (relV=
@@ -420,8 +445,11 @@ interface ShapeLine {
420
445
  color: string;
421
446
  /** Stroke weight px. */
422
447
  weight: number;
448
+ style?: "single" | "dotted" | "dashed";
423
449
  hRel: AnchorRel;
424
450
  vRel: AnchorRel;
451
+ /** Source VML v:line for selection and save-back editing. */
452
+ src?: XmlElement;
425
453
  }
426
454
  interface ShapeTextbox {
427
455
  type: "textbox";
@@ -477,12 +505,18 @@ interface ShapeTextbox {
477
505
  r: number;
478
506
  b: number;
479
507
  };
480
- /** Non-rect preset geometry: paint this outline (in a viewW x viewH space
481
- * scaled to the shape box) instead of a rectangle. */
508
+ /** Non-rect preset geometry: paint these outline paths (in a viewW x viewH
509
+ * space scaled to the shape box) instead of a rectangle. Each path carries
510
+ * its resolved fill colour (undefined = unfilled) and whether the shape's
511
+ * stroke applies to it. */
482
512
  geom?: {
483
- d: string;
484
513
  viewW: number;
485
514
  viewH: number;
515
+ paths: {
516
+ d: string;
517
+ fill?: string;
518
+ stroke: boolean;
519
+ }[];
486
520
  };
487
521
  /** bodyPr a:noAutofit: the box does NOT grow with its text — Word hides
488
522
  * whole lines that stick out past the shape bottom (phase23 p12 ovals:
@@ -492,6 +526,13 @@ interface ShapeTextbox {
492
526
  * plus the top/bottom insets (the stored cy is only Word's last cached
493
527
  * value). Width is fixed. */
494
528
  autofitHeight?: boolean;
529
+ /** bodyPr a:normAutofit — "shrink text on overflow". The mode is recorded
530
+ * because the fit report names it, NOT because it changes the layout:
531
+ * probe-shapefit measured fourteen normAutofit shapes through desktop Word
532
+ * and every one paints at its authored size and clips at the box bottom,
533
+ * exactly like a:noAutofit. `clipText` is set alongside this and is what the
534
+ * layout reads. */
535
+ shrinkText?: boolean;
495
536
  /** Linked text-box chain id (wps:txbx/@id or wps:linkedTxbx/@id): boxes with
496
537
  * the same id form one story. The seq-0 box (wps:txbx) holds the content;
497
538
  * later boxes (wps:linkedTxbx, chainSeq>0) are empty sinks that continue the
@@ -507,6 +548,10 @@ interface ShapeTextbox {
507
548
  textboxStory?: boolean;
508
549
  /** DrawingML WordArt, including the identity textNoShape preset. */
509
550
  wordArt?: boolean;
551
+ /** WordArt glyph fill from w14:textFill or the fallback run color. */
552
+ wordArtFill?: string;
553
+ /** WordArt glyph alpha, 0..1. */
554
+ wordArtOpacity?: number;
510
555
  /** bodyPr a:prstTxWarp preset name (textArchUp, textWave1, textChevron,
511
556
  * textCirclePour, …): the shape's text is bent onto the preset's envelope
512
557
  * rather than flowed as ordinary lines. "textNoShape" (no warp) is dropped. */
@@ -564,6 +609,16 @@ interface ShapeArt {
564
609
  behind?: boolean;
565
610
  /** a:xfrm rotation, degrees clockwise. */
566
611
  rotation?: number;
612
+ /** wp:wrapSquare/.../wrapTopAndBottom on the anchor. Only FRAME stories
613
+ * (header/footer/cell) act on it - body flow ignores art wrap, as before. */
614
+ wrap?: WrapMode;
615
+ /** wp:anchor dist* in px, used with `wrap`. */
616
+ dist?: {
617
+ t: number;
618
+ b: number;
619
+ l: number;
620
+ r: number;
621
+ };
567
622
  lines: DrawingLine[];
568
623
  images: DrawingImage[];
569
624
  paths: DrawingPath[];
@@ -697,16 +752,91 @@ interface DrawingContent {
697
752
  * SmartArt cached-drawing shapes). Painted above lines/paths. */
698
753
  texts?: DrawingTextShape[];
699
754
  }
700
- type ChartType = "column" | "bar" | "line" | "pie";
755
+ /** The c:chartSpace plot kinds the renderer paints. Anything else (3-D, radar,
756
+ * surface, stock, bubble) parses with `unsupported` set instead. */
757
+ type ChartType = "column" | "bar" | "line" | "pie" | "doughnut" | "area" | "scatter";
701
758
  interface ChartSeries {
702
759
  name: string;
703
760
  values: number[];
761
+ /** c:xVal of a scatter series. Every other type shares one category axis,
762
+ * so only scatter carries per-series x values. */
763
+ xValues?: number[];
764
+ /** Explicit series fill/line from c:spPr, overriding the palette slot. */
765
+ color?: string;
766
+ /** Explicit c:dPt fills, keyed by point index (pie slices, single bars). */
767
+ pointColors?: Record<number, string>;
768
+ /** c:smooth — draw the line series through a spline instead of segments. */
769
+ smooth?: boolean;
770
+ /** c:spPr/a:ln/@w, in px. A line or scatter series says here how heavy its
771
+ * stroke is; a scatter series, whose stroke is optional, says it has one. */
772
+ lineWidth?: number;
773
+ /** c:marker/c:symbol, an ST_MarkerStyle name. "none" draws no marker, and a
774
+ * name with no shape behind it takes the series' slot in Word's sequence. */
775
+ markerSymbol?: string;
776
+ /** c:marker/c:size, the width across the marker in px. */
777
+ markerSize?: number;
778
+ }
779
+ type ChartTickMark = "none" | "in" | "out" | "cross";
780
+ /** One axis of a 2-D chart (c:catAx/c:valAx/c:dateAx). */
781
+ interface ChartAxis {
782
+ /** c:delete val="1": Word draws no line, ticks or labels for the axis. */
783
+ hidden?: boolean;
784
+ /** A c:majorGridlines child is present. */
785
+ gridlines?: boolean;
786
+ /** c:numFmt formatCode applied to the tick labels, e.g. "0.0%". */
787
+ format?: string;
788
+ title?: string;
789
+ /** c:scaling overrides of the automatic scale. */
790
+ min?: number;
791
+ max?: number;
792
+ majorUnit?: number;
793
+ /** c:scaling/c:orientation val="maxMin". */
794
+ reversed?: boolean;
795
+ /** c:majorTickMark / c:minorTickMark. The schema default is "out" for major
796
+ * and "none" for minor; Word writes both explicitly. */
797
+ majorTick?: ChartTickMark;
798
+ minorTick?: ChartTickMark;
799
+ /** c:tickLblPos val="none" keeps the axis but drops its labels. */
800
+ labels?: boolean;
801
+ /** c:crossBetween on a value axis, which decides where the category axis puts
802
+ * its points: "between" insets them by half a category (Word's choice for
803
+ * bar and column), "midCat" seats the first and last on the plot's edges
804
+ * (Word's choice for line and area). */
805
+ crossBetween?: "between" | "midCat";
704
806
  }
705
807
  interface ChartData {
706
808
  type: ChartType;
809
+ /** Local name of a plot the renderer does not paint (bar3DChart, radarChart,
810
+ * …). When set, `type` is a placeholder and the renderer draws a labeled box
811
+ * rather than pretending the data is a column chart. */
812
+ unsupported?: string;
707
813
  title?: string;
708
814
  categories: string[];
709
815
  series: ChartSeries[];
816
+ /** c:grouping — how bar/column/area series stack. */
817
+ grouping?: "clustered" | "stacked" | "percentStacked";
818
+ /** Legend edge. Absent when the chart has no c:legend. */
819
+ legend?: "l" | "r" | "t" | "b" | "tr";
820
+ /** c:dLbls showVal on the plot or on every series. */
821
+ dataLabels?: boolean;
822
+ /** c:varyColors — give each point of a single series its own palette slot. */
823
+ varyColors?: boolean;
824
+ categoryAxis?: ChartAxis;
825
+ valueAxis?: ChartAxis;
826
+ /** c:holeSize percent of a doughnut (Word's default is 75). */
827
+ holeSize?: number;
828
+ /** c:gapWidth percent between bar/column categories (Word's default is 150). */
829
+ gapWidth?: number;
830
+ /** c:overlap percent between bars of one category (Word: -27 clustered,
831
+ * 100 stacked). */
832
+ overlap?: number;
833
+ /** c:marker val="1" on a line chart: draw a point marker at each value. */
834
+ markers?: boolean;
835
+ /** Font size in px from c:title//a:defRPr@sz. Word's default title is 14pt. */
836
+ titleSize?: number;
837
+ /** Font size in px from c:txPr//a:defRPr@sz, the chart-wide text size. Word's
838
+ * default for axis labels, legend entries and data labels is 9pt. */
839
+ textSize?: number;
710
840
  }
711
841
  type SmartArtLayout = "process" | "cycle" | "hierarchy" | "list";
712
842
  interface SmartArtData {
@@ -992,7 +1122,10 @@ interface SectionProps {
992
1122
  right?: Border;
993
1123
  offsetFrom: "text" | "page";
994
1124
  };
995
- /** w:lnNumType: margin line numbering. distance px from the text edge. */
1125
+ /** w:lnNumType: margin line numbering. distance px from the text edge.
1126
+ * start is the raw w:start OFFSET (0 when the attribute is absent, its
1127
+ * literal value otherwise) added to the running per-line count — see
1128
+ * parse/section.ts for why an absent attribute isn't just "start 1". */
996
1129
  lineNumbering?: {
997
1130
  countBy: number;
998
1131
  start: number;
@@ -1004,6 +1137,16 @@ interface SectionProps {
1004
1137
  footnoteNumStart?: number;
1005
1138
  endnoteNumFmt?: string;
1006
1139
  endnoteNumStart?: number;
1140
+ /** w:numRestart: when the mark counter resets. Absent means "continuous"
1141
+ * (the whole document shares one counter — this engine's only honored
1142
+ * value; eachSect/eachPage are round-trip preserved but not laid out). */
1143
+ footnoteNumRestart?: "continuous" | "eachSect" | "eachPage";
1144
+ endnoteNumRestart?: "continuous" | "eachSect" | "eachPage";
1145
+ /** w:pos: where the note text is placed. Round-trip preserved only — the
1146
+ * layout always places footnotes at the page bottom and endnotes at the
1147
+ * document end, regardless of this value. */
1148
+ footnotePos?: "pageBottom" | "beneathText";
1149
+ endnotePos?: "sectEnd" | "docEnd";
1007
1150
  /** w:textDirection tbRl: the whole section flows as East-Asian vertical
1008
1151
  * writing — lines run top-to-bottom, progressing right-to-left. */
1009
1152
  textDirection?: "tbRl";
@@ -1025,6 +1168,10 @@ interface DocComment {
1025
1168
  paraId?: string;
1026
1169
  /** Parent comment id when this comment is a reply (commentsExtended). */
1027
1170
  parentId?: string;
1171
+ /** Thread resolved (w15:commentEx w15:done="1" — the [MS-DOCX] CT_CommentEx
1172
+ * extension; ECMA-376 itself has no resolved state). Meaningful on the
1173
+ * thread parent; Word grays the whole thread from the parent's flag. */
1174
+ resolved?: boolean;
1028
1175
  }
1029
1176
  interface HeaderFooter {
1030
1177
  blocks: Block[];
@@ -1146,10 +1293,10 @@ declare class Package {
1146
1293
  * the table is keyed by element identity and in-place XML mutation preserves
1147
1294
  * identity across `refresh()`.
1148
1295
  */
1149
- type StableId$1 = number;
1296
+ type StableId$2 = number;
1150
1297
  interface EncodedCaret {
1151
- blockId: StableId$1;
1152
- runId: StableId$1;
1298
+ blockId: StableId$2;
1299
+ runId: StableId$2;
1153
1300
  /** Offset in the run's WIRE space: cumulative through the run's inline
1154
1301
  * content in document order, where each w:t contributes its text length
1155
1302
  * and each inline separator (w:tab / w:br / w:cr) contributes ONE unit.
@@ -1193,14 +1340,21 @@ declare class StableIds {
1193
1340
  /** Explicitly assign an id to a newly created node. Local edits allocate
1194
1341
  * (`assign(el)`); applying a replicated edit installs the carried value
1195
1342
  * (`assign(el, carriedId)`). Carried ids must not collide. */
1196
- assign(el: XmlElement, id?: StableId$1): StableId$1;
1343
+ assign(el: XmlElement, id?: StableId$2): StableId$2;
1197
1344
  /** Force `el` to carry `id`, replacing any id it already has (e.g. one
1198
1345
  * auto-assigned by a refresh() before a carried id could be installed).
1199
1346
  * The previous id is retired. Throws if `id` is held by a different element. */
1200
- reassign(el: XmlElement, id: StableId$1): void;
1347
+ reassign(el: XmlElement, id: StableId$2): void;
1348
+ /** Drop an element's mapping entirely; its id retires (never reused —
1349
+ * `next` stays monotonic). Used before carried-id assignment: a refresh
1350
+ * inside a mutation auto-assigns sequential ids to the nodes it created,
1351
+ * and one of those autos can equal a LATER id of the same carried batch,
1352
+ * which would make reassign throw on a perfectly good intent. No-op for an
1353
+ * unmapped element. */
1354
+ unassign(el: XmlElement): void;
1201
1355
  private install;
1202
- idOf(el: XmlElement): StableId$1 | undefined;
1203
- elOf(id: StableId$1): XmlElement | undefined;
1356
+ idOf(el: XmlElement): StableId$2 | undefined;
1357
+ elOf(id: StableId$2): XmlElement | undefined;
1204
1358
  /** Drop mappings for elements no longer reachable from the given roots
1205
1359
  * (deleted content). Keeps the table from growing across long sessions.
1206
1360
  * Ids of dropped elements are retired, never reused. */
@@ -1210,10 +1364,10 @@ declare class StableIds {
1210
1364
  * the tree: `pairs` maps originals→clones (as produced by walking original
1211
1365
  * and cloned roots in step). Used by the reconciliation snapshot (plan
1212
1366
  * doc 03) so a restore re-keys the table to the restored elements. */
1213
- captureForClone(originalToClone: Map<XmlElement, XmlElement>): Map<StableId$1, XmlElement>;
1367
+ captureForClone(originalToClone: Map<XmlElement, XmlElement>): Map<StableId$2, XmlElement>;
1214
1368
  /** Replace the table wholesale from a captured mapping (restore path). */
1215
- restore(fromCapture: Map<StableId$1, XmlElement>, nextId: StableId$1): void;
1216
- nextId(): StableId$1;
1369
+ restore(fromCapture: Map<StableId$2, XmlElement>, nextId: StableId$2): void;
1370
+ nextId(): StableId$2;
1217
1371
  /**
1218
1372
  * Export the table as `(id, path)` pairs, where a path is the structural
1219
1373
  * location of the id'd element within the given roots: `[rootIndex,
@@ -1224,14 +1378,14 @@ declare class StableIds {
1224
1378
  * consistently.
1225
1379
  */
1226
1380
  exportSidecar(roots: XmlElement[]): {
1227
- next: StableId$1;
1228
- entries: [StableId$1, number[]][];
1381
+ next: StableId$2;
1382
+ entries: [StableId$2, number[]][];
1229
1383
  };
1230
1384
  /** Rebuild the table from a sidecar against freshly parsed roots (the
1231
1385
  * snapshot the sidecar was exported from). Replaces any current mappings. */
1232
1386
  importSidecar(roots: XmlElement[], sidecar: {
1233
- next: StableId$1;
1234
- entries: [StableId$1, number[]][];
1387
+ next: StableId$2;
1388
+ entries: [StableId$2, number[]][];
1235
1389
  }): void;
1236
1390
  /** Encode a caret as wire-stable addresses. `t` is the w:t (or other text
1237
1391
  * holder) the caret sits in; the run is its nearest tracked ancestor run,
@@ -1259,6 +1413,8 @@ interface Relationship {
1259
1413
  }
1260
1414
  type Relationships = Map<string, Relationship>;
1261
1415
 
1416
+ /** The two note parts a document can carry — see notePartsHolding. */
1417
+ type NotePart = "footnotes" | "endnotes";
1262
1418
  /**
1263
1419
  * A fully parsed .docx: sections of blocks, styles, numbering, theme, and
1264
1420
  * header/footer parts, with helpers to resolve effective formatting.
@@ -1279,7 +1435,7 @@ declare class DocxDocument {
1279
1435
  /** Header/footer parts keyed by relationship id from document.xml.rels. */
1280
1436
  readonly headers: Map<string, HeaderFooter>;
1281
1437
  readonly footers: Map<string, HeaderFooter>;
1282
- /** Note content by note id (render-only; sources stripped). */
1438
+ /** Note content by note id. */
1283
1439
  readonly footnotes: Map<number, Block[]>;
1284
1440
  readonly endnotes: Map<number, Block[]>;
1285
1441
  /** The separator paragraph controls the gap between its rule and the first
@@ -1292,11 +1448,23 @@ declare class DocxDocument {
1292
1448
  readonly documentRels: Relationships;
1293
1449
  /** settings.xml w:evenAndOddHeaders — enables the "even" header/footer variants. */
1294
1450
  readonly evenAndOddHeaders: boolean;
1451
+ /** settings.xml w:autoHyphenation (§17.15.1.10). This engine's layout does
1452
+ * not hyphenate automatically — the setting is round-trip state that
1453
+ * governs Word's own rendering of the document. */
1454
+ readonly autoHyphenation: boolean;
1455
+ /** settings.xml w:hyphenationZone/@w:val in twips (§17.15.1.53); null when
1456
+ * the document does not declare one (Word defaults to 360 = 0.25"). */
1457
+ readonly hyphenationZoneTwips: number | null;
1458
+ /** settings.xml w:doNotHyphenateCaps (§17.15.1.41). */
1459
+ readonly doNotHyphenateCaps: boolean;
1295
1460
  /** settings.xml w:mirrorMargins — facing-page (book fold) margins: even
1296
1461
  * (verso) pages swap the left/right margins and place the gutter on the
1297
1462
  * inside (right) edge so the binding margin stays on the inner side of
1298
1463
  * each spread. */
1299
1464
  readonly mirrorMargins: boolean;
1465
+ /** settings.xml w:compat w:suppressTopSpacing: the first line of a page
1466
+ * takes its character height, not its authored (exact) line spacing. */
1467
+ readonly suppressTopSpacing: boolean;
1300
1468
  /** settings.xml w:defaultTabStop in px (Word default 0.5"). */
1301
1469
  readonly defaultTabStop: number;
1302
1470
  /** settings.xml w:compat compatibilityMode (12=Word2007, 14=Word2010,
@@ -1305,6 +1473,12 @@ declare class DocxDocument {
1305
1473
  * it (nccih: a Heading1/2 after a page break sits at margin + its before).
1306
1474
  * Absent → treated as current (15). */
1307
1475
  readonly compatibilityMode: number;
1476
+ /** The compatibilityMode settings.xml actually declares, or undefined when
1477
+ * the setting (or settings.xml itself) is missing. `compatibilityMode`
1478
+ * defaults an omitted value to 15, but a table's percentage width needs the
1479
+ * distinction: Word fits the horizontal cell margins inside the table box at
1480
+ * an EXPLICIT 15 and adds them around it when the setting is absent. */
1481
+ readonly declaredCompatibilityMode: number | undefined;
1308
1482
  /** settings.xml m:mathPr/m:defJc — default justification for display
1309
1483
  * equations whose m:oMathParaPr carries no explicit m:jc (Word default:
1310
1484
  * centerGroup — the rows of a broken equation left-align to each other and
@@ -1340,6 +1514,31 @@ declare class DocxDocument {
1340
1514
  private footnotesRoot;
1341
1515
  private footnotesDirty;
1342
1516
  private footnotesRels;
1517
+ /** Retained endnotes.xml tree (endnote insertion + save round-trip). */
1518
+ private endnotesPart;
1519
+ private endnotesRoot;
1520
+ private endnotesDirty;
1521
+ private endnotesRels;
1522
+ /** Retained bibliography sources part (the customXml b:Sources data of
1523
+ * ECMA-376 §22.6), when present. Source-management editing mutates this
1524
+ * tree in place; a document with none gets the part on first create. */
1525
+ private sourcesPart;
1526
+ private sourcesRoot;
1527
+ private sourcesDirty;
1528
+ /** Companion parts written when THIS session created the sources part
1529
+ * (itemProps + its .rels); an arriving part keeps its own companions. */
1530
+ private sourcesAux;
1531
+ /** Retained glossary document part (word/glossary/document.xml, ECMA-376
1532
+ * §17.12: glossaryDocument/docParts/docPart — Word's Quick Parts / Building
1533
+ * Blocks store), when present. Building-block editing mutates this tree in
1534
+ * place; a document with none gets the part on first create. Unlike
1535
+ * headers/footers, a docPart's body is never caret-editable — it is only
1536
+ * ever cloned INTO the main document body (insertBuildingBlock) or captured
1537
+ * OUT of a selection (createBuildingBlock) — so this part carries no
1538
+ * editableRoots()/stable-id integration of its own. */
1539
+ private glossaryPart;
1540
+ private glossaryRoot;
1541
+ private glossaryDirty;
1343
1542
  /** Serialize retained optional parts only once actually mutated, keeping
1344
1543
  * untouched parts byte-identical through save(). */
1345
1544
  private stylesDirty;
@@ -1462,12 +1661,80 @@ declare class DocxDocument {
1462
1661
  */
1463
1662
  numberingTree(create?: boolean): XmlElement | null;
1464
1663
  markNumberingChanged(): void;
1664
+ /** Retained styles.xml tree, or null when the package has no styles part.
1665
+ * Style-definition editing mutates this tree in place. */
1666
+ stylesTree(): XmlElement | null;
1667
+ /**
1668
+ * Re-resolve the style cascade after a definition changed in place.
1669
+ *
1670
+ * A style definition is not addressed by any paragraph's XML, so mutating one
1671
+ * changes how EVERY paragraph resolves without changing a single w:p. Two
1672
+ * things therefore have to be reset by hand: the parsed Styles map every
1673
+ * effective-props call reads, and layoutGlobalSig, which is what makes the
1674
+ * line-break cache treat cached breaks for unchanged paragraph XML as stale.
1675
+ */
1676
+ markStylesChanged(): void;
1677
+ /**
1678
+ * Retained bibliography sources tree (b:Sources), or null when the package
1679
+ * has no sources part. With create=true, a missing part is created and
1680
+ * registered the way Word lays one out (ECMA-376 §22.6 + Part 2 custom XML
1681
+ * data storage): the data itself as customXml/itemN.xml, a datastore-item
1682
+ * properties part (itemPropsN.xml) declaring the Bibliography schema URI as
1683
+ * its schemaRef, a .rels from the item to its properties, a customXml
1684
+ * relationship from the main document part, and the content-type overrides.
1685
+ *
1686
+ * DETERMINISM. Everything written is a pure function of the package —
1687
+ * the item number is the first free one, and the datastore itemID GUID is a
1688
+ * fixed constant rather than Word's random draw — so two replicas creating
1689
+ * the part through the same sequenced operation write identical bytes.
1690
+ */
1691
+ sourcesTree(create?: boolean): XmlElement | null;
1692
+ /**
1693
+ * Invalidate what a sources-part edit changes downstream. Like
1694
+ * markStylesChanged, the part is addressed by no paragraph's XML: a
1695
+ * CITATION field's painted text resolves from the part at layout, so the
1696
+ * layout signature has to drop for unchanged paragraph XML to repaint.
1697
+ */
1698
+ markSourcesChanged(): void;
1699
+ /**
1700
+ * Retained glossary document tree (w:glossaryDocument), or null when the
1701
+ * package has no glossary part. With create=true, a missing part is
1702
+ * created and registered — package part, document relationship,
1703
+ * content-type override — the createNotesPart discipline.
1704
+ *
1705
+ * The glossary part gets none of the sources part's companion machinery
1706
+ * (no datastore item, no random itemID): it is an ordinary WordprocessingML
1707
+ * part named by relationship TYPE, so creation just mints an empty
1708
+ * `<w:glossaryDocument><w:docParts/></w:glossaryDocument>` and wires it up
1709
+ * — closer to createNotesPart than to sourcesTree. A real Word-authored
1710
+ * glossary part additionally carries its own styles.xml / settings.xml /
1711
+ * fontTable.xml; this engine writes none of them, because a docPart's
1712
+ * content is read once — to clone its blocks into the main document, where
1713
+ * they resolve styles against the MAIN document's own styles.xml — and is
1714
+ * never laid out or rendered as its own story the way a header/footer is.
1715
+ *
1716
+ * DETERMINISM: nothing here depends on Word's random draws (no GUID is
1717
+ * written at all — w:docPartPr/w:guid is optional and this engine never
1718
+ * reads it back), so two replicas creating the part through the same
1719
+ * sequenced operation write identical bytes.
1720
+ */
1721
+ glossaryTree(create?: boolean): XmlElement | null;
1722
+ /** Invalidate what a glossary-part edit changes: nothing downstream, since
1723
+ * createBuildingBlock/deleteBuildingBlock touch only the glossary tree, not
1724
+ * any paragraph the layout reads. Exists for the same "explicit intent to
1725
+ * persist" reason markSourcesChanged does. */
1726
+ markGlossaryChanged(): void;
1465
1727
  /**
1466
1728
  * Retained footnotes tree. With create=true, a missing footnotes.xml part
1467
1729
  * is created and registered (with Word's required separator footnotes) so
1468
1730
  * inserted footnotes serialize and round-trip.
1469
1731
  */
1470
1732
  footnotesTree(create?: boolean): XmlElement | null;
1733
+ /** Retained endnotes tree; the endnote mirror of footnotesTree. */
1734
+ endnotesTree(create?: boolean): XmlElement | null;
1735
+ /** Register a new notes part in document.xml.rels and [Content_Types].xml,
1736
+ * and return the part path. The tree itself is the caller's. */
1737
+ private createNotesPart;
1471
1738
  /**
1472
1739
  * Create an empty header/footer part (with a default-type reference in
1473
1740
  * every sectPr) when the document has none - Word does this implicitly the
@@ -1482,7 +1749,29 @@ declare class DocxDocument {
1482
1749
  * whether an ensureHeaderFooter intent has anything to do). */
1483
1750
  hasHfPart(kind: "header" | "footer"): boolean;
1484
1751
  ensureHfPart(kind: "header" | "footer"): XmlElement;
1752
+ /** A w:headerReference / w:footerReference element. */
1753
+ private hfReference;
1754
+ /** Materialize the default body-level sectPr when the document declares no
1755
+ * section properties at all (schema: the body's sectPr is its LAST child). */
1756
+ private ensureBodySectPr;
1757
+ /**
1758
+ * Give every section a header and footer reference of the given variant
1759
+ * type, creating one empty part per band the way Word does the first time
1760
+ * "different first page" (type "first") or "different odd & even pages"
1761
+ * (type "even") is enabled. Sections that already carry a reference of the
1762
+ * type keep it — and its part's content. True when anything was added.
1763
+ */
1764
+ ensureHfVariantParts(type: "first" | "even"): boolean;
1765
+ /** Create and register a new empty header/footer part — package part,
1766
+ * document relationship, content-type override, hfParts entry — with no
1767
+ * section references of its own; the caller decides which sectPrs point at
1768
+ * it and with which w:type. */
1769
+ private createHfPart;
1485
1770
  markFootnotesChanged(): void;
1771
+ markEndnotesChanged(): void;
1772
+ /** Re-derive both id -> blocks maps from the retained trees, so layout sees
1773
+ * an inserted or edited note. */
1774
+ private rederiveNotes;
1486
1775
  /** Called by comment edit commands after mutating the comments tree. */
1487
1776
  markCommentsChanged(): void;
1488
1777
  /**
@@ -1498,23 +1787,78 @@ declare class DocxDocument {
1498
1787
  * nearest preceding w:t.
1499
1788
  */
1500
1789
  commentAnchors(): Map<string, XmlElement[]>;
1501
- /** Flag the footnotes part dirty when `t` lives inside it, so save()
1502
- * re-serializes footnotes.xml. Called by the editor after a text edit; a
1503
- * no-op for body/header/footer targets. */
1790
+ /** Flag a notes part dirty when `t` lives inside it, so save()
1791
+ * re-serializes footnotes.xml / endnotes.xml. Called by the editor after a
1792
+ * text edit; a no-op for body/header/footer targets. */
1504
1793
  markDirtyIfFootnote(t: XmlElement): void;
1794
+ /**
1795
+ * Which note parts hold `el` right now. An operation that DETACHES `el` —
1796
+ * accepting or rejecting a revision, for one — has to ask BEFORE it mutates
1797
+ * and mark the answer afterwards: a detached element belongs to no part, so
1798
+ * a containment test run after the fact reports nothing and save() then
1799
+ * leaves the stale footnotes.xml / endnotes.xml in the package.
1800
+ */
1801
+ notePartsHolding(el: XmlElement): NotePart[];
1802
+ /** Flag the given note parts dirty, so save() re-serializes exactly those. */
1803
+ markNotePartsChanged(parts: readonly NotePart[]): void;
1505
1804
  /** The mutable XML roots (document body, related modeled parts, settings).
1506
1805
  * settingsRoot is always second and always present so its history snapshot
1507
1806
  * index stays stable even when optional related roots are created later. */
1508
1807
  editableRoots(): XmlElement[];
1808
+ /** CT_Settings children that precede w:mirrorMargins in the schema sequence
1809
+ * (§17.15.1.78). Insertions go before the first child that is not a
1810
+ * predecessor, so Word never has to repair settings.xml. */
1811
+ private static readonly SETTINGS_BEFORE_MIRROR;
1812
+ /** The schema predecessors of w:evenAndOddHeaders (§17.10.1): everything up
1813
+ * to mirrorMargins, then the sequence through defaultTableStyle. */
1814
+ private static readonly SETTINGS_BEFORE_EVEN_AND_ODD;
1815
+ /** Set or remove one settings.xml element at its schema position. Null
1816
+ * removes; otherwise the element is (re)inserted before the first child
1817
+ * that is not a schema predecessor. */
1818
+ private setSettingsElement;
1819
+ /** Set or remove one on/off settings.xml toggle at its schema position. */
1820
+ private setSettingsToggle;
1821
+ /** The schema predecessors of w:autoHyphenation: everything through
1822
+ * w:defaultTabStop (§17.15.1.78). Its cluster mates append in order. */
1823
+ private static readonly SETTINGS_BEFORE_AUTO_HYPHENATION;
1824
+ /**
1825
+ * Patch the hyphenation settings (w:autoHyphenation §17.15.1.10,
1826
+ * w:hyphenationZone §17.15.1.53, w:doNotHyphenateCaps §17.15.1.41). Each
1827
+ * present key is written; `zoneTwips: null` removes the zone element. True
1828
+ * when anything changed — the honest no-op a replicated document-scoped
1829
+ * operation needs. This engine's layout does not hyphenate automatically;
1830
+ * the settings govern Word's own rendering of the file.
1831
+ */
1832
+ setHyphenation(patch: {
1833
+ auto?: boolean;
1834
+ zoneTwips?: number | null;
1835
+ noCaps?: boolean;
1836
+ }): boolean;
1509
1837
  /** Toggle the document-global facing-page margin mode in settings.xml. */
1510
1838
  setMirrorMargins(enabled: boolean): void;
1839
+ /** Toggle w:evenAndOddHeaders (§17.10.1) — the document-global switch that
1840
+ * makes even pages use the "even" header/footer variants. */
1841
+ setEvenAndOddHeaders(enabled: boolean): void;
1842
+ /** Register settings.xml in document.xml.rels and [Content_Types].xml when
1843
+ * the package was born without it (a minimal document). */
1844
+ private registerSettingsPart;
1511
1845
  /**
1512
1846
  * Find the parent element of `target` in any modeled XML tree (document
1513
1847
  * body, headers, footers). Linear scan — documents are small and this only
1514
1848
  * runs on structural edits (Enter, paragraph merge).
1515
1849
  */
1516
- /** XML roots that can carry tracked changes: body, headers/footers, footnotes. */
1850
+ /** The header part roots, in package order. A watermark is authored into
1851
+ * every one of them: Word paints it on every page, and a document with a
1852
+ * first-page or even-page header has more than one header part. */
1853
+ headerRoots(): XmlElement[];
1854
+ /** The footer part roots, in package order — headerRoots' counterpart, for
1855
+ * operations that walk both bands (the page-number gallery's removal
1856
+ * sweep). */
1857
+ footerRoots(): XmlElement[];
1858
+ /** XML roots that can carry tracked changes: body, headers/footers, notes. */
1517
1859
  revisionRoots(): XmlElement[];
1860
+ /** Body, header/footer and note roots — every tree the caret can reach. */
1861
+ private contentRoots;
1518
1862
  /**
1519
1863
  * Memoized child→parent links. Nothing invalidates this map — the tree is
1520
1864
  * spliced constantly — so every answer is RE-DERIVED before it is returned:
@@ -1561,6 +1905,27 @@ declare class DocxDocument {
1561
1905
  saveAsync(): Promise<Uint8Array>;
1562
1906
  private buildPackage;
1563
1907
  private buildPackageFiles;
1908
+ /**
1909
+ * Leave out the chart parts the document no longer references.
1910
+ *
1911
+ * Deleting a chart splices its w:drawing out of the story, but the chart
1912
+ * part, its rels part, its embedded workbook, the document relationship, and
1913
+ * the two [Content_Types].xml overrides all stay. An agent that inserts a
1914
+ * chart, deletes it, and inserts a replacement therefore saves TWO chart
1915
+ * parts for the one chart the document shows.
1916
+ *
1917
+ * The release happens here, at save time, rather than when the drawing goes,
1918
+ * because undo restores the drawing XML — r:id and all — from a snapshot
1919
+ * that models package parts but no relationship state. Dropping the
1920
+ * relationship early would make undo produce a chart pointing at nothing.
1921
+ * The live tree keeps everything; only the written bytes lose the orphans,
1922
+ * and the saveJournal puts back the two trees this touches.
1923
+ *
1924
+ * Relationship ids are scoped to the part that owns the rels file, so the
1925
+ * document body is the only place that can reference these ids, and reading
1926
+ * it is a complete check. Headers, footers, and notes carry their own rels.
1927
+ */
1928
+ private dropOrphanChartParts;
1564
1929
  /** Fresh unique docPr id for inserted drawings. Seeded once past the
1565
1930
  * highest id already present in any editable root (floor 1000, matching
1566
1931
  * the historical seed for documents with no drawings) so a document that
@@ -1580,6 +1945,23 @@ declare class DocxDocument {
1580
1945
  addHyperlinkRel(url: string): string;
1581
1946
  /** Retarget an existing external relationship (hyperlink href edit). */
1582
1947
  setRelTarget(relId: string, url: string): boolean;
1948
+ /** The relationships of the part that holds `source`: a header or footer
1949
+ * part when the element is inside one, else the document part. */
1950
+ private imageRelsOwner;
1951
+ /** Add an image relationship pointing at `part`, in the relationships of
1952
+ * whichever part holds `source`. */
1953
+ private addImageRel;
1954
+ /**
1955
+ * Point a SECOND part at an image part this document already has.
1956
+ *
1957
+ * Word's picture watermark is ONE media part that every header part
1958
+ * references. Registering the image again per header would put a full copy
1959
+ * of the bytes in the package for each one — three headers, three copies of
1960
+ * the same logo — and give the media-install path three pending parts to
1961
+ * fill from one upload.
1962
+ */
1963
+ linkImageResource(part: string, source: XmlElement): string;
1964
+ private addImageResourceAt;
1583
1965
  addImageResource(bytes: Uint8Array, ext: string): string;
1584
1966
  /** Add a GLB model part and its Office 2019 model3d relationship. */
1585
1967
  addModel3DResource(bytes: Uint8Array): {
@@ -1652,7 +2034,16 @@ declare class DocxDocument {
1652
2034
  registerPendingImage(sha: string, ext: string, meta?: {
1653
2035
  iv?: string;
1654
2036
  genesisId?: string;
1655
- }): string;
2037
+ }, source?: XmlElement): string;
2038
+ /** As registerPendingImage, and also names the part it made — what a second
2039
+ * part needs to reach the same image through linkImageResource. */
2040
+ registerPendingImagePart(sha: string, ext: string, meta?: {
2041
+ iv?: string;
2042
+ genesisId?: string;
2043
+ }, source?: XmlElement): {
2044
+ relId: string;
2045
+ part: string;
2046
+ };
1656
2047
  /** Install fetched bytes into a pending part (doc 05). The caller has
1657
2048
  * ALREADY verified the sha against the intent's declaration (the
1658
2049
  * reservation is the hash commitment — doc 16 §1.1); this installs and
@@ -1773,6 +2164,11 @@ interface TextItem {
1773
2164
  href?: string;
1774
2165
  /** Present for editable text (absent on numbering labels etc.). */
1775
2166
  src?: TextSource;
2167
+ /** This line is one a fixed-size text box hides past its bottom edge (see
2168
+ * LaidOutPage.hiddenText). It mounts unpainted so a caret can bind to it, so
2169
+ * anything that draws from item geometry — selection, comment highlights —
2170
+ * must skip it or it paints a block where there is no text. */
2171
+ hidden?: true;
1776
2172
  /** Source drawing when this text belongs to an independently editable
1777
2173
  * text-box story. */
1778
2174
  textboxStory?: XmlElement;
@@ -1788,6 +2184,8 @@ interface TextItem {
1788
2184
  * layers in-front shapes above the text layer). */
1789
2185
  front?: boolean;
1790
2186
  z?: number;
2187
+ /** Paint-only alpha for decorative text such as DrawingML WordArt. */
2188
+ opacity?: number;
1791
2189
  /** Right-to-left run: renderer sets direction:rtl so the browser shapes and
1792
2190
  * orders the (Arabic/Hebrew) glyphs within the span box. */
1793
2191
  rtl?: boolean;
@@ -1898,6 +2296,28 @@ interface ChartItem {
1898
2296
  height: number;
1899
2297
  data: ChartData;
1900
2298
  }
2299
+ /** bodyPr autofit mode: how a shape reconciles its text with its box.
2300
+ * "resizeShape" is a:spAutoFit (grow the box), "shrinkText" is a:normAutofit
2301
+ * (scale the text down), "none" is a:noAutofit or no bodyPr autofit at all. */
2302
+ type ShapeAutofit = "none" | "resizeShape" | "shrinkText";
2303
+ /** What a text-bearing shape's laid-out text measured, beside the frame that
2304
+ * holds it. The layout already measures the text to place it; recording the
2305
+ * measurement here is what lets an agent see whether the text fits without
2306
+ * re-measuring anything. */
2307
+ interface DrawingTextFit {
2308
+ /** Widest laid line, px (the text extent, not the box). It can exceed the
2309
+ * box's inset width by a trailing space or an unbreakable token. */
2310
+ textW: number;
2311
+ /** Full laid text height, px. */
2312
+ textH: number;
2313
+ /** The text is taller than the box's insets leave room for. Overflow is a
2314
+ * vertical question: the text is wrapped to the box width by construction,
2315
+ * and a token too wide to wrap spends its excess on extra LINES. */
2316
+ overflow: boolean;
2317
+ /** Lines the box hides because they fall past its bottom. */
2318
+ clippedLines: number;
2319
+ autofit: ShapeAutofit;
2320
+ }
1901
2321
  /** Interactive resize zone over a table boundary (column or row). */
1902
2322
  interface DrawingHitItem {
1903
2323
  kind: "drawingHit";
@@ -1931,6 +2351,9 @@ interface DrawingHitItem {
1931
2351
  behind?: boolean;
1932
2352
  /** The drawing owns an independently editable text-box story. */
1933
2353
  textboxStory?: boolean;
2354
+ /** Measured text extent for a text-bearing shape (agent fit inspection).
2355
+ * Absent on drawings that flow no text: pictures, lines, charts, WordArt. */
2356
+ textFit?: DrawingTextFit;
1934
2357
  rotate?: {
1935
2358
  deg: number;
1936
2359
  ox: number;
@@ -2000,6 +2423,15 @@ interface WarpTextItem {
2000
2423
  italic?: boolean;
2001
2424
  /** CSS text color. */
2002
2425
  fill: string;
2426
+ /** WordArt glyph alpha, 0..1. */
2427
+ opacity?: number;
2428
+ /** w14:textOutline glyph stroke. */
2429
+ outline?: {
2430
+ color: string;
2431
+ width: number;
2432
+ };
2433
+ /** w14:shadow / w:shadow offset glyph shadow. */
2434
+ shadow?: boolean;
2003
2435
  /** Preset name (textArchUp, textWave1, textChevron, textCirclePour, …). */
2004
2436
  warp: string;
2005
2437
  rotate?: {
@@ -2032,6 +2464,24 @@ interface LaidOutPage {
2032
2464
  colXs: number[];
2033
2465
  colWidths: number[];
2034
2466
  }>;
2467
+ /** Lines a fixed-size text box (a:noAutofit) hides past its bottom edge.
2468
+ * Held apart from `items` so nothing that paints, measures, or reads the
2469
+ * page picks them up: they exist only so a caret that lands in the hidden
2470
+ * part of a text-box story still has a place to be. */
2471
+ hiddenText?: TextItem[];
2472
+ }
2473
+ /** Internal page-model window used by the DOM virtualizer. */
2474
+ interface LayoutWindow {
2475
+ /** Ensure that each requested page has a complete positioned item model. */
2476
+ materialize(pageIndexes: Iterable<number>): void;
2477
+ /** Release positioned items outside the requested page window. */
2478
+ releaseExcept(pageIndexes: Iterable<number>): void;
2479
+ /** Return the pages whose positioned items are retained. */
2480
+ retainedPages(): Set<number>;
2481
+ }
2482
+ interface LayoutFontSample {
2483
+ font: FontSpec;
2484
+ text: string;
2035
2485
  }
2036
2486
  interface LayoutResult {
2037
2487
  pages: LaidOutPage[];
@@ -2050,6 +2500,12 @@ interface LayoutResult {
2050
2500
  /** Opaque page state used to refresh headers/footers without repaginating
2051
2501
  * the body when their measured geometry is unchanged. */
2052
2502
  _hf?: unknown;
2503
+ /** Internal positioned-model window. */
2504
+ _window?: LayoutWindow;
2505
+ /** Font samples collected before offscreen page items were released. */
2506
+ _fontSamples?: LayoutFontSample[];
2507
+ /** True when any page contains a 3D model item. */
2508
+ _hasModel3D?: boolean;
2053
2509
  }
2054
2510
 
2055
2511
  interface FontMetrics {
@@ -2085,8 +2541,18 @@ interface TextMeasurer {
2085
2541
  } | undefined;
2086
2542
  }
2087
2543
 
2544
+ /** One mail-merge data record: column name → this record's value. The host
2545
+ * parses the data file and hands over plain strings; the engine never learns a
2546
+ * path, a connection string or a query. */
2547
+ type MergeRecord = Readonly<Record<string, string>>;
2088
2548
  interface LayoutOptions {
2089
2549
  measurer?: TextMeasurer;
2550
+ /** Retain positioned items only for a viewport-sized page window. */
2551
+ windowModel?: boolean;
2552
+ /** Mail-merge PREVIEW: the active record's column values, substituted into
2553
+ * MERGEFIELD fields as they are painted. Nothing is written to the document —
2554
+ * see FieldContext.mergeField. Absent renders the «Name» placeholders. */
2555
+ mergeRecord?: MergeRecord;
2090
2556
  /** Previous layout result (from an earlier layoutDocument call on the same
2091
2557
  * document). Enables incremental relayout: pages whose input blocks and
2092
2558
  * lead-in state are unchanged are reused instead of re-laid. The engine falls
@@ -2129,6 +2595,15 @@ interface RenderOptions {
2129
2595
  /** Called when the user submits a reply from a balloon's reply box. The
2130
2596
  * reply box only renders when this is provided. */
2131
2597
  onReplyComment?: (id: string, text: string) => void;
2598
+ /** Called when the user resolves (true) or reopens (false) a thread from
2599
+ * its balloon. The button only renders when this is provided. */
2600
+ onResolveComment?: (id: string, resolved: boolean) => void;
2601
+ /** Called when the user commits an in-balloon edit of a comment's text.
2602
+ * Offered only on comments whose author matches `commentAuthor`. */
2603
+ onEditComment?: (id: string, text: string) => void;
2604
+ /** The local user's comment author name — gates the edit affordance to
2605
+ * their own comments (Word's edit-my-comment rule). */
2606
+ commentAuthor?: string;
2132
2607
  }
2133
2608
  interface TextBinding {
2134
2609
  el: HTMLElement;
@@ -2210,6 +2685,48 @@ declare function renderToDom(doc: DocxDocument, layout: LayoutResult, container:
2210
2685
  */
2211
2686
  declare function printPages(root: HTMLElement, pageWidthPx: number, pageHeightPx: number): void;
2212
2687
 
2688
+ /** The legacy named presets, or any DrawingML ST_ShapeType name the preset
2689
+ * geometry table knows (isValidShapePreset). */
2690
+ type ShapePreset = "line" | "verticalLine" | "rectangle" | "roundedRectangle" | "ellipse" | "diamond" | "textBox" | (string & {});
2691
+ type WordArtPreset = "plain" | "archUp" | "archDown" | "wave" | "chevron" | "circle" | "button" | "chevronDown";
2692
+ /** A WordArt gallery style: glyph fill plus optional outline and shadow
2693
+ * (w14:textFill / w14:textOutline / w14:shadow run effects). */
2694
+ interface WordArtStyle {
2695
+ /** 6-hex-digit RGB, with or without "#". */
2696
+ fill: string;
2697
+ outline?: {
2698
+ color: string;
2699
+ widthPt: number;
2700
+ };
2701
+ shadow?: boolean;
2702
+ }
2703
+ type DrawingLineDash = "solid" | "dashed" | "dotted";
2704
+ type DrawingTool = {
2705
+ kind?: "pen";
2706
+ color: string;
2707
+ width: number;
2708
+ } | {
2709
+ kind: "highlighter";
2710
+ color: string;
2711
+ width: number;
2712
+ } | {
2713
+ kind: "eraser";
2714
+ size: number;
2715
+ } | {
2716
+ kind: "lasso";
2717
+ };
2718
+ /** Outline/shadow for an ordinary run — Word's Home-tab "Text Effects and
2719
+ * Typography" gallery, distinct from inserting a WordArt object. */
2720
+ interface RunTextEffectPatch {
2721
+ outline?: {
2722
+ color: string;
2723
+ widthPt: number;
2724
+ } | null;
2725
+ shadow?: boolean;
2726
+ }
2727
+ /** bodyPr's autofit choice: grow the shape, shrink the text, or neither. */
2728
+ type DrawingTextFitMode = "none" | "resizeShape" | "shrinkText";
2729
+
2213
2730
  interface RunFormatPatch {
2214
2731
  bold?: boolean;
2215
2732
  italic?: boolean;
@@ -2224,6 +2741,24 @@ interface RunFormatPatch {
2224
2741
  fontFamily?: string;
2225
2742
  /** Superscript/subscript; null returns the run to the baseline. */
2226
2743
  verticalAlign?: "superscript" | "subscript" | null;
2744
+ /**
2745
+ * Character style (w:rStyle); null removes the reference.
2746
+ *
2747
+ * This rides the run patch rather than taking an operation of its own
2748
+ * because applying a character style to PART of a run splits the run — which
2749
+ * breaks the registry's position-stable precondition, and which formatRange
2750
+ * already handles with its carried before/middle/after piece ids.
2751
+ */
2752
+ characterStyleId?: string | null;
2753
+ /**
2754
+ * w14 outline/shadow run effects (Word's Home-tab "Text Effects and
2755
+ * Typography" gallery) — the same vocabulary a WordArt object's text
2756
+ * carries, applied directly to ordinary runs. null removes every w14
2757
+ * effect, reverting to plain text. Rides the run patch rather than an
2758
+ * operation of its own for the same reason characterStyleId does: applying
2759
+ * it to PART of a run splits the run, which formatRange already handles.
2760
+ */
2761
+ textEffect?: RunTextEffectPatch | null;
2227
2762
  /** Remove all direct character formatting (and character style). */
2228
2763
  clear?: boolean;
2229
2764
  }
@@ -2238,175 +2773,1121 @@ interface SelectionFormat {
2238
2773
  highlight?: string;
2239
2774
  fontFamily?: string;
2240
2775
  verticalAlign?: "superscript" | "subscript";
2776
+ /** The character style every selected run carries, if they agree. Null when
2777
+ * they agree on carrying none; undefined when the selection is mixed. */
2778
+ characterStyleId?: string | null;
2241
2779
  }
2242
2780
 
2243
- type ShapePreset = "line" | "verticalLine" | "rectangle" | "roundedRectangle" | "ellipse" | "diamond" | "textBox";
2244
- type WordArtPreset = "plain" | "archUp" | "archDown" | "wave" | "chevron";
2245
- type DrawingLineDash = "solid" | "dashed" | "dotted";
2246
- type DrawingTool = {
2247
- kind?: "pen";
2248
- color: string;
2249
- width: number;
2250
- } | {
2251
- kind: "highlighter";
2252
- color: string;
2253
- width: number;
2254
- } | {
2255
- kind: "eraser";
2256
- size: number;
2257
- } | {
2258
- kind: "lasso";
2259
- };
2781
+ /**
2782
+ * Shortcuts owned by the embedding application rather than the editor — a
2783
+ * desktop app's menu accelerators, say. A menu accelerator is consumed by the
2784
+ * menu before the editor's keydown handler ever sees it, so the editor cannot
2785
+ * discover these; the host injects them (HelpGuide's `hostShortcuts` prop) and
2786
+ * the sheet becomes a reference for the whole keyboard instead of half of it.
2787
+ */
2788
+ interface HostShortcutSection {
2789
+ title: string;
2790
+ items: {
2791
+ label: string;
2792
+ keys: string;
2793
+ detail?: string;
2794
+ }[];
2795
+ }
2260
2796
 
2261
- interface SmartArtTextFormat {
2262
- fontFamily: string;
2263
- fontSizePt: number;
2264
- color: string;
2265
- bold: boolean;
2266
- italic: boolean;
2267
- alignment: "left" | "center" | "right";
2797
+ /**
2798
+ * CITATION resolution: the document's bibliography sources and the simple
2799
+ * parenthetical and bibliography-entry formats Word paints for the built-in
2800
+ * styles.
2801
+ *
2802
+ * WHERE THE SOURCES LIVE. Word stores the bibliography source list as a
2803
+ * Custom XML Data part (customXml/item1.xml in Word's own files), targeted
2804
+ * from the main document part by a customXml relationship. The markup is the
2805
+ * Bibliography namespace of ECMA-376 §22.6: a b:Sources root whose
2806
+ * StyleName attribute names the selected citation style ("APA", "MLA", …)
2807
+ * and whose b:Source children each carry a b:Tag the CITATION instruction
2808
+ * references. The part is retained by DocxDocument (sourcesTree) so the
2809
+ * source-management operations in edit/sources.ts can mutate it; a part
2810
+ * those operations never touch keeps its original bytes through save().
2811
+ *
2812
+ * WHAT IS FORMATTED. The author-date parenthetical of the simple built-in
2813
+ * styles: "(Author, Year)" for the APA-shaped family and "(Author)" /
2814
+ * "(Author page)" for MLA — plus the matching bibliography ENTRY per source
2815
+ * (bibliographyEntryText below). Full style-sheet fidelity (Word ships an
2816
+ * XSL per style) is out of scope; a CITATION whose display this module
2817
+ * cannot build keeps its cached result, which is Word's own rendering.
2818
+ *
2819
+ * It lives here, above both consumers, for the style-ref.ts reason: layout
2820
+ * paints what the reader sees and the update pass writes what the saved file
2821
+ * carries, and the two must not disagree about the same field.
2822
+ */
2823
+ /** A person author, in b:NameList order. */
2824
+ interface BibliographyPerson {
2825
+ last: string;
2826
+ first?: string;
2268
2827
  }
2828
+ interface BibliographySource {
2829
+ tag: string;
2830
+ /** b:SourceType text ("Book", "JournalArticle", "InternetSite", "Report", …). */
2831
+ sourceType?: string;
2832
+ /** Person authors, in b:NameList order. */
2833
+ authors: BibliographyPerson[];
2834
+ /** b:Author/b:Corporate, used when there is no person author. */
2835
+ corporate?: string;
2836
+ title?: string;
2837
+ year?: string;
2838
+ /** b:JournalName (journal articles). */
2839
+ journal?: string;
2840
+ /** b:Publisher (books, reports). */
2841
+ publisher?: string;
2842
+ /** b:URL (internet sites). */
2843
+ url?: string;
2844
+ }
2845
+ /** The citation styles the style selector offers: the two whose parenthetical
2846
+ * and bibliography-entry rules this module models. One list, so the operation
2847
+ * validator, the agent schema, and the toolbar cannot disagree. */
2848
+ declare const CITATION_STYLES: readonly ["APA", "MLA"];
2849
+ type CitationStyle = (typeof CITATION_STYLES)[number];
2269
2850
 
2270
- type ObjectArrangeAction = "alignLeft" | "alignCenter" | "alignRight" | "alignTop" | "alignMiddle" | "alignBottom" | "rotateLeft" | "rotateRight" | "bringToFront" | "sendToBack";
2271
- type SelectedObjectKind = "shape" | "line" | "smartArt" | "chart" | "image" | "model3d";
2272
- type SelectedObjectCommand = "altText" | "editText" | "fill" | "outline" | "lineStyle" | "rotate" | "size" | "position" | "wrapInline" | "wrapSquare" | "wrapTopAndBottom" | "wrapFront" | "wrapBehind" | "bringForward" | "sendBackward" | "reset3d" | "delete";
2273
- /** A local edit expressed as a replicable intent, emitted by the editor for
2274
- * the collab layer. Positions are stable ids; the connection adds wire
2275
- * bookkeeping (clientId/clientSeq/base). Mirrors the @wordinweb/collab intent
2276
- * shapes without a dependency on that package. */
2277
- type EditorIntent = {
2278
- kind: "insertText";
2279
- at: {
2280
- blockId: number;
2281
- runId: number;
2282
- offset: number;
2283
- };
2284
- text: string;
2285
- suggest?: {
2286
- author: string;
2287
- date: string;
2288
- };
2851
+ /** An a:srcRect crop as fractions of the source bitmap trimmed off each edge.
2852
+ * The four together must leave a visible strip on both axes. */
2853
+ interface ImageCrop {
2854
+ l: number;
2855
+ t: number;
2856
+ r: number;
2857
+ b: number;
2858
+ }
2859
+
2860
+ /**
2861
+ * Numbering DEFINITION editing: the levels of an abstract numbering
2862
+ * definition, and the per-instance overrides that restart or continue a
2863
+ * sequence. Applying numbering to a paragraph lives in lists.ts.
2864
+ *
2865
+ * Two things are edited here, and the difference matters:
2866
+ *
2867
+ * - A LEVEL of an abstract definition (w:abstractNum/w:lvl). Every list
2868
+ * instance sharing that abstract definition changes with it.
2869
+ * - An OVERRIDE on one instance (w:num/w:lvlOverride). Only the paragraphs
2870
+ * carrying that numId change, which is how one list restarts at 1 while
2871
+ * another sharing the same look keeps counting.
2872
+ */
2873
+ /** The numbering formats Word offers for a level. */
2874
+ declare const NUMBER_FORMATS: readonly ["decimal", "decimalZero", "upperRoman", "lowerRoman", "upperLetter", "lowerLetter", "ordinal", "bullet", "none"];
2875
+ type NumberFormat = (typeof NUMBER_FORMATS)[number];
2876
+ /** What one level of a multilevel definition can be changed to. Undefined
2877
+ * leaves a property alone. */
2878
+ interface LevelPatch {
2879
+ format?: NumberFormat;
2880
+ /** The label template: "%1." or "%1.%2." for a multilevel definition, or the
2881
+ * bullet glyph when format is "bullet". */
2882
+ text?: string;
2883
+ /** The counter this level starts at. */
2884
+ start?: number;
2885
+ alignment?: "left" | "center" | "right";
2886
+ /** Text indent, in points. */
2887
+ indentLeftPt?: number;
2888
+ /** Hanging indent of the label, in points. */
2889
+ hangingPt?: number;
2890
+ }
2891
+ /**
2892
+ * Word's multilevel-list gallery, expressed as per-level patches over the
2893
+ * deep numbering ops (setNumberingLevel). One entry per preset; index = ilvl.
2894
+ */
2895
+ declare const NUMBERING_PRESETS: Record<string, {
2896
+ name: string;
2897
+ levels: LevelPatch[];
2898
+ }>;
2899
+ type NumberingPresetId = keyof typeof NUMBERING_PRESETS;
2900
+
2901
+ /**
2902
+ * Table manipulation: add/remove rows and columns relative to the cell
2903
+ * containing the caret. Mutates source XML; callers checkpoint + relayout.
2904
+ *
2905
+ * v1 scope: column operations refuse tables using gridSpan (merged cells)
2906
+ * rather than corrupt them.
2907
+ *
2908
+ * SUGGESTING MODE. Every formatting operation below takes an optional
2909
+ * RevisionMeta. With one, it records the properties it is about to replace as
2910
+ * a tracked change — w:tblPrChange on the table, w:trPrChange on a row,
2911
+ * w:tcPrChange on a cell — before mutating them, so a reviewer can accept or
2912
+ * reject the change (edit/suggest.ts). Without one it mutates outright, which
2913
+ * is what a direct edit does. STRUCTURAL operations (row and column insert and
2914
+ * delete, merge, split) take no meta: they are not tracked, and the surfaces
2915
+ * that enforce suggesting mode refuse them rather than apply them untracked.
2916
+ */
2917
+ type TableOp = "rowAbove" | "rowBelow" | "deleteRow" | "colLeft" | "colRight" | "deleteCol" | "deleteTable" | "mergeRight" | "mergeDown" | "splitCell" | {
2918
+ kind: "cellShading";
2919
+ fill: string | null;
2289
2920
  } | {
2290
- kind: "deleteText";
2291
- blockId: number;
2292
- runId: number;
2293
- start: number;
2294
- end: number;
2921
+ kind: "cellVAlign";
2922
+ v: "top" | "center" | "bottom";
2295
2923
  } | {
2296
- kind: "splitParagraph";
2297
- at: {
2298
- blockId: number;
2299
- runId: number;
2300
- offset: number;
2924
+ kind: "textWrapping";
2925
+ wrapping: "none" | "around";
2926
+ xPx: number;
2927
+ yPx: number;
2928
+ };
2929
+ /** The w:val values w:tblBorders / w:tcBorders edges accept. "none" writes
2930
+ * w:val="nil", Word's spelling for an explicitly suppressed rule. */
2931
+ type TableBorderStyle = "single" | "thick" | "double" | "dotted" | "dashed" | "dotDash" | "dotDotDash" | "thinThickSmallGap" | "triple" | "wave" | "none";
2932
+ /** One border edge. Table scope carries the four sides plus the two inside
2933
+ * rules; cell scope carries the four sides plus the two diagonals. Neither
2934
+ * carries the other's — the parser reads only these, so writing the rest
2935
+ * would emit XML that renders nothing. */
2936
+ type TableBorderEdge = "top" | "bottom" | "left" | "right" | "insideH" | "insideV" | "tl2br" | "tr2bl";
2937
+ interface TableBorderSpec {
2938
+ style: TableBorderStyle;
2939
+ /** Stroke width in eighths of a point (w:sz). Word's UI offers 2..48. */
2940
+ sz?: number;
2941
+ /** "#RRGGBB", "RRGGBB", or "auto". */
2942
+ color?: string;
2943
+ /** Gap between rule and content, in points (w:space). */
2944
+ space?: number;
2945
+ }
2946
+ /** Word's six "Table Style Options" checkboxes. Banding is expressed
2947
+ * positively here and inverted into w:noHBand / w:noVBand on write — the
2948
+ * negative spelling is a wire detail, and every caller that had to remember
2949
+ * the inversion got it wrong at least once. */
2950
+ interface TableLookToggles {
2951
+ firstRow: boolean;
2952
+ lastRow: boolean;
2953
+ firstColumn: boolean;
2954
+ lastColumn: boolean;
2955
+ bandedRows: boolean;
2956
+ bandedCols: boolean;
2957
+ }
2958
+ /** Cell padding in points. An omitted side is left as it was; pass null to
2959
+ * setTableCellMargins to drop the whole override. */
2960
+ interface CellMarginsPt {
2961
+ top?: number;
2962
+ left?: number;
2963
+ bottom?: number;
2964
+ right?: number;
2965
+ }
2966
+ /**
2967
+ * What a Table Properties dialog needs to show the table around the caret,
2968
+ * in points throughout so no caller handles twips.
2969
+ *
2970
+ * Read-only, and separate from the setters because Word's dialog PREFILLS:
2971
+ * a width box that opens empty asks the user to retype a number the document
2972
+ * already carries, and one that opens on a guess is worse than empty.
2973
+ */
2974
+ interface TablePropertiesPt {
2975
+ /** Columns in the table's grid. */
2976
+ columnCount: number;
2977
+ /** Grid column the caret sits in, counting gridSpans to its left. */
2978
+ columnIdx: number;
2979
+ /** w:tblW. An absent element IS "auto" — that is what the schema's default
2980
+ * says — so the unit is always answered. */
2981
+ width: {
2982
+ unit: "pt" | "pct" | "auto";
2983
+ value: number;
2301
2984
  };
2302
- newBlockId: number;
2303
- newRunId: number;
2304
- suggest?: {
2305
- author: string;
2306
- date: string;
2985
+ /** Each grid column's width. */
2986
+ columnWidthsPt: number[];
2987
+ /** The table's default cell margins (w:tblCellMar). A side the table does
2988
+ * not declare is ABSENT rather than zero, so a caller can tell an inherited
2989
+ * margin from a suppressed one. */
2990
+ cellMargins: CellMarginsPt;
2991
+ /** Rows in the repeating header band (see setTableHeaderRows). */
2992
+ headerRows: number;
2993
+ }
2994
+
2995
+ /**
2996
+ * Style DEFINITION editing: the entries in styles.xml, as opposed to applying
2997
+ * an existing style to content (setParagraphStyle in blocks.ts, w:rStyle via
2998
+ * the run format patch).
2999
+ *
3000
+ * Everything here mutates the retained styles.xml tree and then calls
3001
+ * doc.markStylesChanged(), which re-parses the tree and drops the layout
3002
+ * signature. That is the whole of the cascade re-resolution: a definition is
3003
+ * not addressed by any paragraph's XML, so no w:p changes and the model
3004
+ * re-derives effective props from the new Styles map on the next layout.
3005
+ */
3006
+ /** The paragraph properties a style definition can carry, in points.
3007
+ * Null clears the property; undefined leaves it alone. */
3008
+ interface StyleParaPatch {
3009
+ alignment?: "left" | "center" | "right" | "both" | null;
3010
+ spacingBeforePt?: number | null;
3011
+ spacingAfterPt?: number | null;
3012
+ /** Line spacing as a multiple of single (1.5 → w:line="360" auto). */
3013
+ lineMultiple?: number | null;
3014
+ indentLeftPt?: number | null;
3015
+ indentFirstLinePt?: number | null;
3016
+ keepNext?: boolean | null;
3017
+ /** w:outlineLvl 0-8 — what makes a paragraph style a TOC heading level. */
3018
+ outlineLevel?: number | null;
3019
+ }
3020
+ /** The run properties a style definition can carry. Same names as direct
3021
+ * character formatting; `clear` has no meaning for a definition. */
3022
+ type StyleRunPatch = Omit<RunFormatPatch, "clear">;
3023
+ /** The four style kinds a w:style can declare. One list, so a validator and
3024
+ * a schema cannot disagree about which types exist. */
3025
+ declare const STYLE_TYPES: readonly ["paragraph", "character", "table", "numbering"];
3026
+ type StyleType = (typeof STYLE_TYPES)[number];
3027
+ interface StyleSpec {
3028
+ styleId: string;
3029
+ type: StyleType;
3030
+ /** The display name (w:name) — what a gallery shows. */
3031
+ name: string;
3032
+ /** Parent in the cascade. Defaults per type to what Word writes: Normal for
3033
+ * a paragraph style, DefaultParagraphFont for a character style,
3034
+ * TableNormal for a table style, and nothing for a numbering style. */
3035
+ basedOn?: string | null;
3036
+ /** Style applied to the NEXT paragraph after this one (paragraph only). */
3037
+ next?: string | null;
3038
+ /** w:qFormat — show the style in the quick-style gallery. */
3039
+ quickStyle?: boolean;
3040
+ /** w:uiPriority — gallery sort order. */
3041
+ uiPriority?: number;
3042
+ paragraph?: StyleParaPatch;
3043
+ run?: StyleRunPatch;
3044
+ /**
3045
+ * PARAGRAPH styles only: also write the linked character companion Word
3046
+ * pairs with one, so the same look can be applied to a span of text as well
3047
+ * as to a whole paragraph. The companion takes the id `<styleId>Char` and
3048
+ * the name `<name> Char`, copies the paragraph style's run properties, and
3049
+ * the two point at each other through w:link.
3050
+ *
3051
+ * Refused when `<styleId>Char` is already taken, rather than picking
3052
+ * another id: the companion's id has to be derivable from the paragraph
3053
+ * style's, or two replicas applying the same operation would disagree
3054
+ * about what to call it.
3055
+ */
3056
+ linked?: boolean;
3057
+ /** TABLE styles only: the grid the style draws. Conditional formats
3058
+ * (w:tblStylePr — banded rows, header row) are not expressible yet. */
3059
+ table?: {
3060
+ borders?: Partial<Record<TableBorderEdge, TableBorderSpec>>;
2307
3061
  };
2308
- } | {
2309
- kind: "formatRun";
2310
- blockId: number;
2311
- runId: number;
2312
- patch: Record<string, unknown>;
2313
- } | {
2314
- kind: "formatRange";
2315
- blockId: number;
2316
- runId: number;
2317
- start: number;
2318
- end: number;
2319
- patch: Record<string, unknown>;
2320
- beforeId?: number;
2321
- middleId: number;
2322
- afterId?: number;
2323
- } | {
2324
- kind: "formatParagraph";
2325
- blockId: number;
2326
- align?: "left" | "center" | "right" | "justify";
2327
- styleId?: string | null;
2328
- } | {
2329
- kind: "setListType";
2330
- blockId: number;
2331
- listKind: "bullet" | "number" | null;
2332
- } | {
2333
- kind: "mergeParagraph";
2334
- blockId: number;
2335
- } | {
2336
- kind: "suggestRevision";
2337
- ranges?: {
2338
- blockId: number;
2339
- runId: number;
2340
- start: number;
2341
- end: number;
2342
- }[];
2343
- marks?: {
2344
- blockId: number;
2345
- glyph: "ins" | "del";
2346
- }[];
2347
- suggest: {
2348
- author: string;
2349
- date: string;
3062
+ /** NUMBERING styles only, and required for them: the w:numId of the
3063
+ * numbering definition the style names. A numbering style is a NAME for a
3064
+ * list definition, so it carries nothing else of its own. */
3065
+ numbering?: {
3066
+ numId: number;
2350
3067
  };
2351
- } | {
2352
- kind: "acceptRevision";
2353
- index: number;
2354
- } | {
2355
- kind: "rejectRevision";
2356
- index: number;
2357
- } | {
2358
- kind: "acceptAllRevisions";
2359
- } | {
3068
+ }
3069
+ interface StylePatch {
3070
+ name?: string;
3071
+ basedOn?: string | null;
3072
+ next?: string | null;
3073
+ quickStyle?: boolean;
3074
+ uiPriority?: number;
3075
+ paragraph?: StyleParaPatch;
3076
+ run?: StyleRunPatch;
3077
+ }
3078
+ /** A gallery row: what a style chooser needs to draw one entry. */
3079
+ interface StyleGalleryEntry {
3080
+ id: string;
3081
+ name: string;
3082
+ type: "paragraph" | "character" | "table" | "numbering";
3083
+ basedOn?: string;
3084
+ /** w:qFormat: Word shows these in the ribbon's quick-style gallery. */
3085
+ quickStyle: boolean;
3086
+ uiPriority?: number;
3087
+ /** True for a style this document declares, false for a built-in offered
3088
+ * without a definition (applying one injects the definition). */
3089
+ defined: boolean;
3090
+ /** Number of paragraphs or runs that reference the style. */
3091
+ usageCount: number;
3092
+ /** Effective run props through the style's basedOn chain — enough to paint a
3093
+ * live preview of the entry. */
3094
+ preview: RunProps;
3095
+ }
3096
+
3097
+ interface Model3DRotation {
3098
+ x: number;
3099
+ y: number;
3100
+ z: number;
3101
+ }
3102
+
3103
+ /**
3104
+ * Bibliography SOURCE editing: the b:Source entries of the customXml sources
3105
+ * part (ECMA-376 §22.6), as opposed to citing one (insertCitationField in
3106
+ * fields.ts) or rendering one (src/citations.ts).
3107
+ *
3108
+ * Everything here mutates the retained sources tree and then calls
3109
+ * doc.markSourcesChanged() — the styles.ts discipline. The part is created on
3110
+ * the first source (doc.sourcesTree(true)); only the sources part is written,
3111
+ * so a package whose sources this engine never touches keeps its original
3112
+ * bytes through save().
3113
+ */
3114
+ /** The source kinds the manager offers, mapped to the b:SourceType values
3115
+ * Word's own Source Manager writes. The four cover the common cases; an
3116
+ * arriving source of any other type still renders and can be cited. */
3117
+ declare const CITATION_SOURCE_TYPES: readonly ["book", "article", "website", "report"];
3118
+ type CitationSourceType = (typeof CITATION_SOURCE_TYPES)[number];
3119
+ interface CitationSourceSpec {
3120
+ /** Word's Source-Manager tag shape (isValidCitationTag): what a CITATION
3121
+ * instruction references. */
3122
+ tag: string;
3123
+ type: CitationSourceType;
3124
+ /** Person authors. Mutually exclusive with `corporate`. */
3125
+ authors?: BibliographyPerson[];
3126
+ /** Corporate author (b:Corporate). */
3127
+ corporate?: string;
3128
+ title?: string;
3129
+ year?: string;
3130
+ /** Books and reports. */
3131
+ publisher?: string;
3132
+ /** Journal articles (b:JournalName). */
3133
+ journal?: string;
3134
+ /** Internet sites. */
3135
+ url?: string;
3136
+ }
3137
+ /** Everything but the tag, each field optional; an empty string clears the
3138
+ * field. `authors`/`corporate` replace the whole author element. */
3139
+ type CitationSourcePatch = Partial<Omit<CitationSourceSpec, "tag">>;
3140
+
3141
+ type ParagraphDividerStyle = "single" | "double" | "dotted" | "dashed" | "thinThickSmallGap";
3142
+ interface ParagraphDivider {
3143
+ style: ParagraphDividerStyle;
3144
+ color: string;
3145
+ widthPt: number;
3146
+ spacePt: number;
3147
+ }
3148
+ declare const PARAGRAPH_BORDER_EDGES: readonly ["top", "left", "bottom", "right", "between", "bar"];
3149
+ type ParagraphBorderEdge = (typeof PARAGRAPH_BORDER_EDGES)[number];
3150
+ interface ParagraphBordersPatch {
3151
+ /** Edges to change: a spec writes the rule, null removes the direct edge.
3152
+ * Absent edges are left alone. The spec vocabulary is the table border
3153
+ * picker's (style / sz in eighths / color / space in points). */
3154
+ borders?: Partial<Record<ParagraphBorderEdge, TableBorderSpec | null>>;
3155
+ /** Paragraph shading fill "RRGGBB"; null removes the direct w:shd.
3156
+ * Undefined leaves shading alone. */
3157
+ shading?: string | null;
3158
+ }
3159
+ declare const TAB_STOP_ALIGNMENTS: readonly ["left", "center", "right", "decimal", "bar"];
3160
+ declare const TAB_STOP_LEADERS: readonly ["none", "dot", "hyphen", "underscore", "middleDot"];
3161
+ interface TabStopSpec {
3162
+ /** Position in points from the left text edge. */
3163
+ posPt: number;
3164
+ align: (typeof TAB_STOP_ALIGNMENTS)[number];
3165
+ leader: (typeof TAB_STOP_LEADERS)[number];
3166
+ }
3167
+
3168
+ /** The cover-page design gallery. "title" is Word's plain centered title
3169
+ * block (this engine's original single form). "banner" shades the title
3170
+ * paragraph into a colored band — Word's "Sideline"/"Facet" family look —
3171
+ * reusing setParagraphBorders' shading rather than inventing a second way to
3172
+ * paint a fill. "sidebar" left-aligns the block lower on the page with a
3173
+ * colored accent rule on the title, the same reuse for the rule itself. */
3174
+ declare const COVER_PAGE_LAYOUTS: readonly ["title", "banner", "sidebar"];
3175
+ type CoverPageLayout = (typeof COVER_PAGE_LAYOUTS)[number];
3176
+ interface CoverPageContent {
3177
+ title: string;
3178
+ subtitle?: string;
3179
+ author?: string;
3180
+ /** Defaults to "title" — the original single layout, unchanged. */
3181
+ layout?: CoverPageLayout;
3182
+ }
3183
+ /** The w:pgNumType number formats the layout engine paints (its PAGE_FMT
3184
+ * table); the editable subset of ST_NumberFormat (ECMA-376 §17.18.59). */
3185
+ declare const PAGE_NUMBER_FORMATS: readonly ["decimal", "lowerRoman", "upperRoman", "lowerLetter", "upperLetter"];
3186
+ type PageNumberFormat = (typeof PAGE_NUMBER_FORMATS)[number];
3187
+ interface PageNumberFormatPatch {
3188
+ /** w:pgNumType@fmt. null removes the attribute (back to decimal). */
3189
+ fmt?: PageNumberFormat | null;
3190
+ /** w:pgNumType@start. null removes it (numbering continues from the
3191
+ * previous section, the OOXML default). */
3192
+ start?: number | null;
3193
+ }
3194
+ /** Note-mark numbering formats the layout engine paints (formatNoteMark's
3195
+ * table): the page-number format subset plus "chicago", Word's symbol style
3196
+ * (*, †, ‡, §) — footnotes' traditional default in some templates. */
3197
+ declare const NOTE_NUMBER_FORMATS: readonly ["decimal", "lowerRoman", "upperRoman", "lowerLetter", "upperLetter", "chicago"];
3198
+ type NoteNumberFormat = (typeof NOTE_NUMBER_FORMATS)[number];
3199
+ /** ST_RestartNumber (ECMA-376 §17.18.81): when the mark counter resets.
3200
+ * Only "eachSect" is honored by layout (assignNoteNumbers); "continuous" is
3201
+ * the OOXML default; "eachPage" round-trips but is not laid out — see
3202
+ * assignNoteNumbers' comment for why. */
3203
+ declare const NOTE_RESTART_VALUES: readonly ["continuous", "eachSect", "eachPage"];
3204
+ type NoteRestart = (typeof NOTE_RESTART_VALUES)[number];
3205
+ interface FootnoteOptionsPatch {
3206
+ /** w:footnotePr/w:numFmt@val. null removes it (back to decimal). */
3207
+ fmt?: NoteNumberFormat | null;
3208
+ /** w:footnotePr/w:numStart@val. null removes it (back to 1). */
3209
+ start?: number | null;
3210
+ /** w:footnotePr/w:numRestart@val. null removes it (back to continuous). */
3211
+ restart?: NoteRestart | null;
3212
+ /** w:footnotePr/w:pos@val (§17.11.21). null removes it (back to pageBottom). */
3213
+ pos?: "pageBottom" | "beneathText" | null;
3214
+ }
3215
+ interface EndnoteOptionsPatch {
3216
+ fmt?: NoteNumberFormat | null;
3217
+ start?: number | null;
3218
+ restart?: NoteRestart | null;
3219
+ /** w:endnotePr/w:pos@val. null removes it (back to docEnd). */
3220
+ pos?: "sectEnd" | "docEnd" | null;
3221
+ }
3222
+ /** Current w:footnotePr settings for the section governing `t`. */
3223
+ declare function footnoteOptionsAt(doc: DocxDocument, t: XmlElement): {
3224
+ fmt: NoteNumberFormat;
3225
+ start: number | null;
3226
+ restart: NoteRestart;
3227
+ pos: "pageBottom" | "beneathText";
3228
+ };
3229
+ /** Current w:endnotePr settings for the section governing `t`. */
3230
+ declare function endnoteOptionsAt(doc: DocxDocument, t: XmlElement): {
3231
+ fmt: NoteNumberFormat;
3232
+ start: number | null;
3233
+ restart: NoteRestart;
3234
+ pos: "sectEnd" | "docEnd";
3235
+ };
3236
+ interface LineNumberingPatch {
3237
+ /** Turn margin line numbering on/off for the target section(s). */
3238
+ enabled: boolean;
3239
+ /** Number every Nth line (1/5/10). Default 1 when first enabled. */
3240
+ countBy?: number;
3241
+ /** When the count resets. Default newPage when first enabled. */
3242
+ restart?: "continuous" | "newPage" | "newSection";
3243
+ /** First line number. Default 1. */
3244
+ start?: number;
3245
+ }
3246
+
3247
+ declare const PAGE_NUMBER_POSITIONS: readonly ["top", "bottom"];
3248
+ type PageNumberGalleryPosition = (typeof PAGE_NUMBER_POSITIONS)[number];
3249
+ declare const PAGE_NUMBER_ALIGNMENTS: readonly ["left", "center", "right"];
3250
+ type PageNumberGalleryAlign = (typeof PAGE_NUMBER_ALIGNMENTS)[number];
3251
+ declare const HEADER_FOOTER_PRESETS: readonly ["blank", "centeredTitle", "titleAndDate", "threeColumn"];
3252
+ type HeaderFooterPreset = (typeof HEADER_FOOTER_PRESETS)[number];
3253
+
3254
+ /**
3255
+ * A native Word table of contents: a TOC field whose cached result is the
3256
+ * entry list.
3257
+ *
3258
+ * WHY A FIELD AND NOT PLAIN PARAGRAPHS. Word recognises a TOC only by its
3259
+ * field structure, and everything a user expects from one — "Update Table",
3260
+ * the grey field shading, Ctrl-click navigation — follows from it. A pile of
3261
+ * paragraphs that merely looks like a TOC is a document Word cannot maintain.
3262
+ *
3263
+ * THE STRUCTURE, as Word writes it (verified against the parity corpus's
3264
+ * Word-authored TOCs, e.g. wild2-med-nccih-protocol.docx):
3265
+ *
3266
+ * <w:p> first entry paragraph, pStyle TOC1
3267
+ * fldChar begin
3268
+ * instrText ` TOC \o "1-3" \h \z \u `
3269
+ * fldChar separate
3270
+ * <w:hyperlink w:anchor="_Toc…" w:history="1"> … </w:hyperlink>
3271
+ * </w:p>
3272
+ * <w:p> … </w:p> one paragraph per further entry
3273
+ * <w:p> fldChar end </w:p> closes the field
3274
+ *
3275
+ * The field's begin/separate live in the FIRST entry paragraph rather than in
3276
+ * a paragraph of their own, and everything between separate and end is the
3277
+ * cached result. Each entry is a hyperlink to the heading's `_Toc` bookmark,
3278
+ * holding the title, a tab to a right-aligned dot-leader stop, and a PAGEREF
3279
+ * field for the page number.
3280
+ *
3281
+ * HOW IT STAYS CURRENT. The entries' PAGEREF fields are ordinary complex
3282
+ * fields, so the update pass (edit/update-fields.ts) repages the whole table
3283
+ * with no TOC-specific code: it harvests each PAGEREF's page from a layout
3284
+ * like any other. Only a change to the HEADING SET needs rebuildToc, which
3285
+ * replaces the entry paragraphs outright.
3286
+ */
3287
+ /** Levels a `\o` switch includes, e.g. [1, 3] for `\o "1-3"`. */
3288
+ type TocLevels = [number, number];
3289
+ /** The tab leaders a TOC entry can draw. One list, so a validator and a
3290
+ * schema cannot disagree about which exist. */
3291
+ declare const TOC_LEADERS: readonly ["dot", "hyphen", "underscore", "none"];
3292
+ type TocLeader = (typeof TOC_LEADERS)[number];
3293
+ interface TocOptions {
3294
+ /** Outline levels to include. Default [1, 3], Word's own default. */
3295
+ levels?: TocLevels;
3296
+ /** Tab leader between an entry's title and its page number. */
3297
+ leader?: TocLeader;
3298
+ /** Build a TABLE OF FIGURES instead: entries are the caption paragraphs
3299
+ * whose SEQ field carries this label ("Figure", "Table", …), and the field
3300
+ * instruction takes Word's `\c "Label"` switch (§17.16.5.68). */
3301
+ captionLabel?: string;
3302
+ }
3303
+
3304
+ /**
3305
+ * The operation registry: ONE declaration per edit operation, read by every
3306
+ * surface that used to carry its own hand-written copy.
3307
+ *
3308
+ * Adding an operation used to cost four to six coordinated edits — a core
3309
+ * mutation, an editor dispatch, a wire intent variant in TWO packages, a
3310
+ * transform/validate case, an agent capability row, an agent id-budget case.
3311
+ * Each of those is guarded by a compile-time exhaustiveness gate, so nothing
3312
+ * could silently go missing; the cost was the coordination, not the risk. A
3313
+ * registered operation is declared once here, and the gates then consume the
3314
+ * registry instead of a second hand-maintained list. The gates are NOT
3315
+ * relaxed: an operation that is not registered still has to satisfy every one
3316
+ * of them by hand.
3317
+ *
3318
+ * WHAT MAY BE REGISTERED. The declaration shape below is deliberately narrow.
3319
+ * A registered operation must be:
3320
+ *
3321
+ * 1. ADDRESSED BY STABLE ID — a run, a paragraph, or a table cell paragraph.
3322
+ * The address is the whole of its collaborative "honest no-op" predicate:
3323
+ * when the id does not resolve in the room, the operation applies as a
3324
+ * clean rejection everywhere rather than mutating anything locally.
3325
+ * 2. POSITION-STABLE — it moves no run's text, so a concurrent intent's
3326
+ * offsets need no remapping and its transform is identity. The
3327
+ * text-shaped intents (insertText, deleteText, splitParagraph,
3328
+ * formatRange) each carry bespoke transform logic and stay hand-written.
3329
+ * 3. WITHOUT A WIRE INVERSE — collaborative undo skips it, which is the
3330
+ * status quo for every intent except the four in @wordinweb/collab's
3331
+ * invert.ts. The local path still takes a history checkpoint.
3332
+ *
3333
+ * Those three preconditions ARE the operation's undo and transform
3334
+ * classification. They are stated once, here, instead of repeated per
3335
+ * operation, because every operation the registry admits shares them; an
3336
+ * operation that breaks one of them does not belong in the registry yet.
3337
+ */
3338
+ /** A stable id from the core side table (the wire's addressing unit). */
3339
+ type StableId$1 = number;
3340
+ /**
3341
+ * How a registered operation names its target on the wire.
3342
+ *
3343
+ * "document" scopes an operation to the whole document, which means it has no
3344
+ * stable id and therefore none of the honest-no-op protection an id gives: a
3345
+ * document address ALWAYS resolves. Such an operation has to supply its own
3346
+ * rejection predicate from the payload — see updateFields below, whose result
3347
+ * count must match the document's field count — and it must carry every value
3348
+ * a replica cannot re-derive identically.
3349
+ *
3350
+ * "object" names a DRAWING: the stable id of the run that CARRIES it, plus an
3351
+ * `objectIndex` into that run's content picking out which drawing it is (a run
3352
+ * can hold several). This is the addressing every hand-written drawing intent
3353
+ * already uses — setImageWrap, resizeDrawing — so a migrated one keeps its
3354
+ * wire shape. The index is NOT in the table below: it is a position inside the
3355
+ * run rather than an id of its own, which is also why it stays out of the
3356
+ * agent-facing field list, where a raw index is forbidden. BOTH halves reject:
3357
+ * an id nobody has and an index that no longer names a drawing are each the
3358
+ * same clean no-op, so the honest-no-op predicate is exactly the one every
3359
+ * addressed operation has. The index's WELL-FORMEDNESS is checked centrally
3360
+ * (see validateRegisteredOperation) rather than by each operation, for the
3361
+ * same reason the three preconditions above are stated once.
3362
+ */
3363
+ type OperationAddress = "run" | "block" | "cell" | "object" | "document";
3364
+ /**
3365
+ * Agent capability category. Mirrors @wordinweb/agent's
3366
+ * AgentEditCapability["category"]; the agent's typed capability map is what
3367
+ * checks the two stay equal.
3368
+ */
3369
+ type OperationCategory = "text" | "paragraph" | "review" | "table" | "insert" | "drawing" | "math" | "document";
3370
+ /** An agent-facing payload field beyond the address reference. Order is the
3371
+ * order the agent's JSON schema lists them in. */
3372
+ interface OperationField {
3373
+ name: string;
3374
+ /** Absent means required. */
3375
+ optional?: true;
3376
+ }
3377
+ /** What an address resolved to in the document. */
3378
+ interface OperationTarget {
3379
+ /** The addressed element: the w:r, the w:p, or the w:tbl owning the cell. */
3380
+ el: XmlElement;
3381
+ /** The first w:t under a run, or the first text descendant of a paragraph.
3382
+ * Null when the target carries no text (and for cell addressing). */
3383
+ t: XmlElement | null;
3384
+ /** The parsed run for a run address, null for the other two. Needed by
3385
+ * operations whose state is reachable only through the model — the checkbox
3386
+ * marker parsing hangs on a run's content — rather than from the XML. */
3387
+ run: Run | null;
3388
+ /**
3389
+ * For cell addressing: the addressed w:p itself. `el` deliberately widens a
3390
+ * cell address to the owning w:tbl, which is what a table-scoped operation
3391
+ * wants; a CELL-scoped one (per-edge borders, a margin override) needs the
3392
+ * one cell the caret is in, and the paragraph is how it gets there. Null
3393
+ * for run and block addressing, whose addressed element IS `el`.
3394
+ */
3395
+ cellParagraph: XmlElement | null;
3396
+ /** For object addressing: the w:drawing (or VML shape) the address named.
3397
+ * Null for every other address. */
3398
+ drawing: XmlElement | null;
3399
+ }
3400
+ interface OperationContext<Payload> {
3401
+ doc: DocxDocument;
3402
+ target: OperationTarget;
3403
+ payload: Payload;
3404
+ }
3405
+ /**
3406
+ * The caller-supplied part of a wire payload: everything except the address
3407
+ * field the dispatcher fills in and the carried ids it allocates.
3408
+ */
3409
+ type OperationArgs<Payload> = Omit<Payload, "runId" | "blockId" | "cellParagraphId" | "nodeIds">;
3410
+ interface OperationDefinition<Kind extends string, Payload> {
3411
+ kind: Kind;
3412
+ address: OperationAddress;
3413
+ /** Agent capability row: how the operation is grouped and described. */
3414
+ category: OperationCategory;
3415
+ description: string;
3416
+ /** Agent-facing fields beyond the address reference, in schema order. */
3417
+ fields: readonly OperationField[];
3418
+ /**
3419
+ * How many fresh stable ids the mutation needs for the id-tracked nodes it
3420
+ * creates. Omit when it creates none. Every producer (the editor, the React
3421
+ * host, the agent compiler) reads this instead of repeating the arithmetic.
3422
+ */
3423
+ nodeIds?: (args: OperationArgs<Payload>) => number;
3424
+ /** The mutation removes id-tracked nodes, so the caller must retire their
3425
+ * stable ids afterwards. Deletion and pruning are paired everywhere else in
3426
+ * the apply path (mergeParagraph, removeDrawing); declaring it here keeps
3427
+ * the pairing in the operation's single declaration. */
3428
+ prunesIds?: true;
3429
+ /**
3430
+ * Reject a malformed payload before it is sequenced. Runs on both sides of
3431
+ * the wire, so it must be a pure function of the payload.
3432
+ */
3433
+ validate?: (payload: Payload) => string | null;
3434
+ /**
3435
+ * The XML mutation, headless and from a resolved target. False means a
3436
+ * clean no-op: the operation applied nothing and every replica agrees.
3437
+ */
3438
+ apply: (ctx: OperationContext<Payload>) => boolean;
3439
+ }
3440
+ /**
3441
+ * The optional suggest payload a TRACKABLE operation carries, and the
3442
+ * validation they share.
3443
+ *
3444
+ * With it the operation records what it replaced — w:pPrChange for a list
3445
+ * marker, w:tblPrChange / w:trPrChange / w:tcPrChange for table formatting —
3446
+ * instead of mutating outright, so a reviewer can accept or reject it. The
3447
+ * author and date travel in the payload so every replica writes byte-identical
3448
+ * XML; the w:id is scan-derived from identical tree state and needs no
3449
+ * carrying. Omitting it is a direct, untracked edit.
3450
+ */
3451
+ interface SuggestablePayload {
3452
+ suggest?: {
3453
+ author: string;
3454
+ date: string;
3455
+ };
3456
+ }
3457
+ /** A payload with no fields of its own. Not `Record<string, never>`: that
3458
+ * carries an index signature, which would forbid the clientId/clientSeq/base
3459
+ * bookkeeping @wordinweb/collab intersects onto every wire body. */
3460
+ type NoPayload = Record<never, never>;
3461
+ declare const OPERATIONS: readonly [OperationDefinition<"setListType", {
3462
+ blockId: StableId$1;
3463
+ listKind: "bullet" | "number" | null;
3464
+ /** Additional target paragraphs beyond the addressed one, sharing its
3465
+ * minted numbering definition. Absent = the old single-paragraph form. */
3466
+ moreBlockIds?: StableId$1[];
3467
+ } & SuggestablePayload>, OperationDefinition<"insertTable", {
3468
+ runId: StableId$1;
3469
+ rows: number;
3470
+ cols: number;
3471
+ cells?: string[][];
3472
+ headerRow?: boolean;
3473
+ nodeIds: StableId$1[];
3474
+ }>, OperationDefinition<"resizeTableRow", {
3475
+ cellParagraphId: StableId$1;
3476
+ rowIdx: number;
3477
+ heightPx: number;
3478
+ }>, OperationDefinition<"toggleCheckbox", {
3479
+ runId: StableId$1;
3480
+ }>, OperationDefinition<"updateFields", {
3481
+ results: string[];
3482
+ }>, OperationDefinition<"insertToc", {
3483
+ runId: StableId$1;
3484
+ levels?: TocLevels;
3485
+ leader?: TocLeader;
3486
+ /** With a label the field is a TABLE OF FIGURES (`TOC \c "Label"`): the
3487
+ * entries come from the label's caption paragraphs instead of headings. */
3488
+ captionLabel?: string;
3489
+ entryCount: number;
3490
+ nodeIds: StableId$1[];
3491
+ }>, OperationDefinition<"insertCaption", {
3492
+ blockId: StableId$1;
3493
+ label: string;
3494
+ text?: string;
3495
+ position?: "below" | "above";
3496
+ nodeIds: StableId$1[];
3497
+ }>, OperationDefinition<"ensureRefBookmark", {
3498
+ blockId: StableId$1;
3499
+ name: string;
3500
+ }>, OperationDefinition<"insertIndexEntry", {
3501
+ runId: StableId$1;
3502
+ entry: string;
3503
+ nodeIds: StableId$1[];
3504
+ }>, OperationDefinition<"insertIndex", {
3505
+ runId: StableId$1;
3506
+ entryCount: number;
3507
+ nodeIds: StableId$1[];
3508
+ }>, OperationDefinition<"refreshIndex", {
3509
+ entryCount: number;
3510
+ nodeIds: StableId$1[];
3511
+ }>, OperationDefinition<"createStyle", {
3512
+ style: StyleSpec;
3513
+ }>, OperationDefinition<"modifyStyle", {
3514
+ styleId: string;
3515
+ patch: StylePatch;
3516
+ }>, OperationDefinition<"deleteStyle", {
3517
+ styleId: string;
3518
+ }>, OperationDefinition<"setNumberingLevel", {
3519
+ blockId: StableId$1;
3520
+ ilvl: number | null;
3521
+ patch: LevelPatch;
3522
+ }>, OperationDefinition<"setNumberingRestart", {
3523
+ blockId: StableId$1;
3524
+ start: number | null;
3525
+ }>, OperationDefinition<"setParagraphBorders", {
3526
+ blockId: StableId$1;
3527
+ patch: ParagraphBordersPatch;
3528
+ } & SuggestablePayload>, OperationDefinition<"setTabStops", {
3529
+ blockId: StableId$1;
3530
+ stops: TabStopSpec[];
3531
+ } & SuggestablePayload>, OperationDefinition<"setTableBorders", {
3532
+ cellParagraphId: StableId$1;
3533
+ scope: "cell" | "table";
3534
+ edges: TableBorderEdge[];
3535
+ border: TableBorderSpec | null;
3536
+ } & SuggestablePayload>, OperationDefinition<"setTableStyle", {
3537
+ cellParagraphId: StableId$1;
3538
+ styleId: string | null;
3539
+ } & SuggestablePayload>, OperationDefinition<"setTableLook", {
3540
+ cellParagraphId: StableId$1;
3541
+ look: Partial<TableLookToggles>;
3542
+ } & SuggestablePayload>, OperationDefinition<"setTableWidth", {
3543
+ cellParagraphId: StableId$1;
3544
+ unit: "pt" | "pct" | "auto";
3545
+ value?: number;
3546
+ } & SuggestablePayload>, OperationDefinition<"setTableColumnWidth", {
3547
+ cellParagraphId: StableId$1;
3548
+ colIdx: number;
3549
+ widthPt: number;
3550
+ } & SuggestablePayload>, OperationDefinition<"setTableLayout", {
3551
+ cellParagraphId: StableId$1;
3552
+ layout: "fixed" | "autofit";
3553
+ renderedWidths?: number[];
3554
+ } & SuggestablePayload>, OperationDefinition<"setTableCellMargins", {
3555
+ cellParagraphId: StableId$1;
3556
+ scope: "cell" | "table";
3557
+ margins: CellMarginsPt | null;
3558
+ } & SuggestablePayload>, OperationDefinition<"setTableHeaderRows", {
3559
+ cellParagraphId: StableId$1;
3560
+ count: number;
3561
+ } & SuggestablePayload>, OperationDefinition<"insertTableFormula", {
3562
+ cellParagraphId: StableId$1;
3563
+ formula: string;
3564
+ numFmt?: string;
3565
+ nodeIds: StableId$1[];
3566
+ }>, OperationDefinition<"sortTableRows", {
3567
+ cellParagraphId: StableId$1;
3568
+ colIdx: number;
3569
+ order: "asc" | "desc";
3570
+ /** "kind" would collide with the intent discriminant, hence "compare". */
3571
+ compare: "text" | "number";
3572
+ hasHeader?: boolean;
3573
+ }>, OperationDefinition<"convertTextToTable", {
3574
+ blockId: StableId$1;
3575
+ moreBlockIds?: StableId$1[];
3576
+ separator: "tab" | "comma";
3577
+ cellCount: number;
3578
+ nodeIds: StableId$1[];
3579
+ }>, OperationDefinition<"convertTableToText", {
3580
+ cellParagraphId: StableId$1;
3581
+ separator: "tab" | "comma";
3582
+ rowCount: number;
3583
+ nodeIds: StableId$1[];
3584
+ }>, OperationDefinition<"insertEndnote", {
3585
+ runId: StableId$1;
3586
+ text: string;
3587
+ nodeIds: StableId$1[];
3588
+ }>, OperationDefinition<"setCrop", {
3589
+ runId: StableId$1;
3590
+ objectIndex?: number;
3591
+ crop: ImageCrop;
3592
+ }>, OperationDefinition<"setModel3DRotation", {
3593
+ runId: StableId$1;
3594
+ objectIndex?: number;
3595
+ rotation: Model3DRotation;
3596
+ }>, OperationDefinition<"setDrawingTextFit", {
3597
+ runId: StableId$1;
3598
+ objectIndex?: number;
3599
+ mode: DrawingTextFitMode;
3600
+ fontScalePct?: number;
3601
+ }>, OperationDefinition<"insertMergeField", {
3602
+ runId: StableId$1;
3603
+ name: string;
3604
+ nodeIds: StableId$1[];
3605
+ }>, OperationDefinition<"insertCitation", {
3606
+ runId: StableId$1;
3607
+ tag: string;
3608
+ nodeIds: StableId$1[];
3609
+ }>, OperationDefinition<"insertBibliography", {
3610
+ runId: StableId$1;
3611
+ entryCount: number;
3612
+ nodeIds: StableId$1[];
3613
+ }>, OperationDefinition<"refreshBibliography", {
3614
+ entryCount: number;
3615
+ nodeIds: StableId$1[];
3616
+ }>, OperationDefinition<"createCitationSource", {
3617
+ source: CitationSourceSpec;
3618
+ }>, OperationDefinition<"editCitationSource", {
3619
+ tag: string;
3620
+ patch: CitationSourcePatch;
3621
+ }>, OperationDefinition<"deleteCitationSource", {
3622
+ tag: string;
3623
+ }>, OperationDefinition<"setCitationStyle", {
3624
+ style: CitationStyle;
3625
+ }>, OperationDefinition<"createBuildingBlock", {
3626
+ name: string;
3627
+ category?: string;
3628
+ blocksXml: string;
3629
+ }>, OperationDefinition<"insertBuildingBlock", {
3630
+ name: string;
3631
+ /** The carried-id budget, computed by the caller from the SAME already-
3632
+ * synced glossary state the apply side reads (buildingBlockNodeCount) —
3633
+ * the insertBibliography entryCount pattern. */
3634
+ blockCount: number;
3635
+ }>, OperationDefinition<"deleteBuildingBlock", {
3636
+ name: string;
3637
+ }>, OperationDefinition<"insertWatermark", {
3638
+ text: string;
3639
+ headerCount: number;
3640
+ diagonal?: boolean;
3641
+ color?: string;
3642
+ opacity?: number;
3643
+ nodeIds: StableId$1[];
3644
+ }>, OperationDefinition<"insertPictureWatermark", {
3645
+ blobSha: string;
3646
+ bytesLen: number;
3647
+ ext: string;
3648
+ iv?: string;
3649
+ naturalWidthPx: number;
3650
+ naturalHeightPx: number;
3651
+ headerCount: number;
3652
+ washout?: boolean;
3653
+ nodeIds: StableId$1[];
3654
+ }>, OperationDefinition<"removeWatermark", NoPayload>, OperationDefinition<"setTitlePage", {
3655
+ enabled: boolean;
3656
+ nodeIds: StableId$1[];
3657
+ }>, OperationDefinition<"setEvenOddHeaders", {
3658
+ enabled: boolean;
3659
+ nodeIds: StableId$1[];
3660
+ }>, OperationDefinition<"setPageNumberFormat", {
3661
+ fmt?: PageNumberFormat | null;
3662
+ start?: number | null;
3663
+ }>, OperationDefinition<"setHyphenation", {
3664
+ auto?: boolean;
3665
+ zonePt?: number | null;
3666
+ noCaps?: boolean;
3667
+ }>, OperationDefinition<"insertPageNumberPosition", {
3668
+ position: PageNumberGalleryPosition;
3669
+ align: PageNumberGalleryAlign;
3670
+ nodeIds: StableId$1[];
3671
+ }>, OperationDefinition<"removePageNumbers", Record<never, never>>, OperationDefinition<"insertHeaderFooterPreset", {
3672
+ hfKind: "header" | "footer";
3673
+ preset: HeaderFooterPreset;
3674
+ nodeIds: StableId$1[];
3675
+ }>, OperationDefinition<"setFootnoteOptions", {
3676
+ fmt?: NoteNumberFormat | null;
3677
+ start?: number | null;
3678
+ restart?: NoteRestart | null;
3679
+ pos?: "pageBottom" | "beneathText" | null;
3680
+ }>, OperationDefinition<"setEndnoteOptions", {
3681
+ fmt?: NoteNumberFormat | null;
3682
+ start?: number | null;
3683
+ restart?: NoteRestart | null;
3684
+ pos?: "sectEnd" | "docEnd" | null;
3685
+ }>];
3686
+ type AnyOperationDefinition = (typeof OPERATIONS)[number];
3687
+ /** Distributes: one body type per registered operation. */
3688
+ type BodyOf<D> = D extends OperationDefinition<infer Kind, infer Payload> ? {
3689
+ kind: Kind;
3690
+ } & Payload : never;
3691
+ type RegisteredOperationKind = AnyOperationDefinition["kind"];
3692
+ /**
3693
+ * The wire body of every registered operation. @wordinweb/collab intersects
3694
+ * this with its IntentBase bookkeeping to form the Intent variants, so the
3695
+ * payload is declared once and the wire shape follows.
3696
+ */
3697
+ type RegisteredOperationBody = BodyOf<AnyOperationDefinition>;
3698
+
3699
+ interface SmartArtTextFormat {
3700
+ fontFamily: string;
3701
+ fontSizePt: number;
3702
+ color: string;
3703
+ bold: boolean;
3704
+ italic: boolean;
3705
+ alignment: "left" | "center" | "right";
3706
+ }
3707
+
3708
+ type ObjectArrangeAction = "alignLeft" | "alignCenter" | "alignRight" | "alignTop" | "alignMiddle" | "alignBottom" | "rotateLeft" | "rotateRight" | "bringToFront" | "sendToBack";
3709
+ type SelectedObjectKind = "shape" | "line" | "smartArt" | "chart" | "image" | "model3d";
3710
+ type SelectedObjectCommand = "altText" | "editText" | "fill" | "outline" | "lineStyle" | "rotate" | "size" | "position" | "crop" | "wrapInline" | "wrapSquare" | "wrapTopAndBottom" | "wrapFront" | "wrapBehind" | "autofitNone" | "autofitResizeShape" | "autofitShrinkText" | "bringForward" | "sendBackward" | "reset3d" | "delete";
3711
+ /** A local edit expressed as a replicable intent, emitted by the editor for
3712
+ * the collab layer. Positions are stable ids; the connection adds wire
3713
+ * bookkeeping (clientId/clientSeq/base). Mirrors the @wordinweb/collab intent
3714
+ * shapes without a dependency on that package — except for the registered
3715
+ * operations, whose single declaration in registry.ts feeds BOTH this type
3716
+ * and the collab intent union, so there is nothing left to mirror. */
3717
+ type EditorIntent = RegisteredOperationBody | {
3718
+ kind: "insertText";
3719
+ at: {
3720
+ blockId: number;
3721
+ runId: number;
3722
+ offset: number;
3723
+ };
3724
+ text: string;
3725
+ suggest?: {
3726
+ author: string;
3727
+ date: string;
3728
+ };
3729
+ } | {
3730
+ kind: "insertSeparator";
3731
+ at: {
3732
+ blockId: number;
3733
+ runId: number;
3734
+ offset: number;
3735
+ };
3736
+ separator: "br" | "tab";
3737
+ suggest?: {
3738
+ author: string;
3739
+ date: string;
3740
+ };
3741
+ } | {
3742
+ kind: "deleteSeparator";
3743
+ at: {
3744
+ blockId: number;
3745
+ runId: number;
3746
+ offset: number;
3747
+ };
3748
+ suggest?: {
3749
+ author: string;
3750
+ date: string;
3751
+ };
3752
+ } | {
3753
+ kind: "deleteText";
3754
+ blockId: number;
3755
+ runId: number;
3756
+ start: number;
3757
+ end: number;
3758
+ } | {
3759
+ kind: "splitParagraph";
3760
+ at: {
3761
+ blockId: number;
3762
+ runId: number;
3763
+ offset: number;
3764
+ };
3765
+ newBlockId: number;
3766
+ newRunId: number;
3767
+ suggest?: {
3768
+ author: string;
3769
+ date: string;
3770
+ };
3771
+ } | {
3772
+ kind: "formatRun";
3773
+ blockId: number;
3774
+ runId: number;
3775
+ patch: Record<string, unknown>;
3776
+ } | {
3777
+ kind: "formatRange";
3778
+ blockId: number;
3779
+ runId: number;
3780
+ start: number;
3781
+ end: number;
3782
+ patch: Record<string, unknown>;
3783
+ beforeId?: number;
3784
+ middleId: number;
3785
+ afterId?: number;
3786
+ } | {
3787
+ kind: "formatParagraph";
3788
+ blockId: number;
3789
+ align?: "left" | "center" | "right" | "justify";
3790
+ styleId?: string | null;
3791
+ } | {
3792
+ kind: "mergeParagraph";
3793
+ blockId: number;
3794
+ } | {
3795
+ kind: "setListLevel";
3796
+ blockId: number;
3797
+ delta: 1 | -1;
3798
+ } | {
3799
+ kind: "pasteBlocks";
3800
+ afterBlockId: number;
3801
+ blocksXml: string;
3802
+ nodeIds: number[];
3803
+ } | {
3804
+ kind: "suggestRevision";
3805
+ ranges?: {
3806
+ blockId: number;
3807
+ runId: number;
3808
+ start: number;
3809
+ end: number;
3810
+ }[];
3811
+ marks?: {
3812
+ blockId: number;
3813
+ glyph: "ins" | "del";
3814
+ }[];
3815
+ suggest: {
3816
+ author: string;
3817
+ date: string;
3818
+ };
3819
+ } | {
3820
+ kind: "acceptRevision";
3821
+ index: number;
3822
+ } | {
3823
+ kind: "rejectRevision";
3824
+ index: number;
3825
+ } | {
3826
+ kind: "acceptAllRevisions";
3827
+ author?: string;
3828
+ } | {
2360
3829
  kind: "rejectAllRevisions";
3830
+ author?: string;
2361
3831
  } | {
2362
3832
  kind: "setImageWrap";
2363
3833
  runId: number;
3834
+ objectIndex?: number;
2364
3835
  mode: "inline" | "square" | "topAndBottom" | "none" | "behind";
2365
3836
  } | {
2366
3837
  kind: "setFloatingPagePosition";
2367
3838
  runId: number;
3839
+ objectIndex?: number;
2368
3840
  xPx: number;
2369
3841
  yPx: number;
2370
3842
  } | {
2371
3843
  kind: "resizeDrawing";
2372
3844
  runId: number;
3845
+ objectIndex?: number;
2373
3846
  widthPx: number;
2374
3847
  heightPx: number;
2375
3848
  } | {
2376
3849
  kind: "setDrawingRotation";
2377
3850
  runId: number;
3851
+ objectIndex?: number;
2378
3852
  degrees: number;
2379
3853
  } | {
2380
3854
  kind: "setDrawingOrder";
2381
3855
  runId: number;
3856
+ objectIndex?: number;
2382
3857
  order: "front" | "back";
2383
3858
  } | {
2384
3859
  kind: "setDrawingLineStyle";
2385
3860
  runId: number;
3861
+ objectIndex?: number;
2386
3862
  color: string;
2387
3863
  widthPx: number;
2388
3864
  dash: "solid" | "dashed" | "dotted";
2389
3865
  } | {
2390
3866
  kind: "setImageAltText";
2391
3867
  runId: number;
3868
+ objectIndex?: number;
2392
3869
  alt: string;
2393
3870
  } | {
2394
3871
  kind: "setDrawingFill";
2395
3872
  runId: number;
3873
+ objectIndex?: number;
2396
3874
  color: string | null;
2397
3875
  } | {
2398
3876
  kind: "setSmartArtFill";
2399
3877
  runId: number;
3878
+ objectIndex?: number;
2400
3879
  color: string | null;
2401
3880
  nodeIndex?: number;
2402
3881
  } | {
2403
3882
  kind: "setSmartArtNodeText";
2404
3883
  runId: number;
3884
+ objectIndex?: number;
2405
3885
  index: number;
2406
3886
  text: string;
2407
3887
  } | {
2408
3888
  kind: "setDrawingWordArtText";
2409
3889
  runId: number;
3890
+ objectIndex?: number;
2410
3891
  text: string;
2411
3892
  } | {
2412
3893
  kind: "insertBreak";
@@ -2419,11 +3900,6 @@ type EditorIntent = {
2419
3900
  boundary: number;
2420
3901
  deltaPx: number;
2421
3902
  renderedWidths?: number[];
2422
- } | {
2423
- kind: "resizeTableRow";
2424
- cellParagraphId: number;
2425
- rowIdx: number;
2426
- heightPx: number;
2427
3903
  } | {
2428
3904
  kind: "moveTable";
2429
3905
  cellParagraphId: number;
@@ -2434,7 +3910,7 @@ type EditorIntent = {
2434
3910
  } | {
2435
3911
  kind: "tableOp";
2436
3912
  cellParagraphId: number;
2437
- op: {
3913
+ op: "deleteTable" | {
2438
3914
  kind: "textWrapping";
2439
3915
  wrapping: "none" | "around";
2440
3916
  xPx: number;
@@ -2443,16 +3919,20 @@ type EditorIntent = {
2443
3919
  } | {
2444
3920
  kind: "removeDrawing";
2445
3921
  runId: number;
3922
+ objectIndex?: number;
2446
3923
  } | {
2447
3924
  kind: "setMathLinear";
2448
3925
  blockId: number;
3926
+ mathIndex: number;
2449
3927
  mathText: string;
2450
3928
  } | {
2451
3929
  kind: "deleteMath";
2452
3930
  blockId: number;
3931
+ mathIndex: number;
2453
3932
  } | {
2454
3933
  kind: "moveMath";
2455
3934
  blockId: number;
3935
+ mathIndex: number;
2456
3936
  at: {
2457
3937
  blockId: number;
2458
3938
  runId: number;
@@ -2465,6 +3945,76 @@ type EditorIntent = {
2465
3945
  nodeIds: number[];
2466
3946
  };
2467
3947
 
3948
+ /**
3949
+ * Compare Documents — Word's "legal blackline".
3950
+ *
3951
+ * Produces a THIRD document: `revised`, with every difference from `original`
3952
+ * expressed as an ordinary tracked change attributed to one reviewer. Nothing
3953
+ * here invents a revision format; it drives the same w:ins / w:del / *PrChange
3954
+ * vocabulary the suggesting editor writes, so the result is reviewable with
3955
+ * the accept/reject surface that already exists, and round-trips through it:
3956
+ *
3957
+ * accept every revision in the result → `revised`
3958
+ * reject every revision in the result → `original`
3959
+ *
3960
+ * (Equal modulo run coalescing — accepting a revision unwraps it without
3961
+ * re-merging the runs it split, which is true of Word too. The gate test in
3962
+ * test/compare-roundtrip.test.ts states the canonical form exactly.)
3963
+ *
3964
+ * ## Tiers, stated plainly
3965
+ *
3966
+ * **Text — full.** Paragraphs align by histogram diff over a text/style/
3967
+ * numbering fingerprint, then by Dice similarity inside the gaps; matched
3968
+ * paragraphs are diffed word by word. Insertions, deletions, paragraph splits
3969
+ * and merges, paragraph-property and run-property changes are all expressed
3970
+ * faithfully.
3971
+ *
3972
+ * **Tables — rows and cells.** A matched table pair aligns its ROWS by the
3973
+ * same histogram pass, so a mid-table insert does not cascade. Matched rows
3974
+ * with the same cell count recurse into each cell's paragraphs. A row that
3975
+ * matches nothing becomes a row insertion or deletion (w:trPr/w:ins, w:trPr/
3976
+ * w:del). A row pair whose cell COUNTS differ is coarse: the whole row is
3977
+ * struck and the whole new row inserted.
3978
+ *
3979
+ * **Drawings, fields, footnote references and other non-text run content —
3980
+ * atomic.** They match themselves byte for byte or they are a change. A
3981
+ * changed image is a struck image plus an inserted one, never a patched one.
3982
+ *
3983
+ * **Paragraphs holding a hyperlink, content control, smart tag or inline
3984
+ * math — coarse.** Word-level diffing rebuilds a paragraph's runs, and a
3985
+ * revision wrapper may not sit around a `w:hyperlink` (the schema puts w:ins
3986
+ * INSIDE it). Such a paragraph is never word-diffed: if it changed at all it
3987
+ * is struck whole and the new one inserted whole, and `onNote` says so.
3988
+ *
3989
+ * **Headers, footers, footnotes, endnotes, comments, styles and numbering —
3990
+ * NOT compared.** The result carries `revised`'s. `onNote` reports when
3991
+ * `original`'s differ, so a reviewer is never told "no change" about something
3992
+ * that was never looked at.
3993
+ *
3994
+ * **Moves — not detected, by architecture.** `RevisionKind` has no moveFrom /
3995
+ * moveTo member and nothing in suggest.ts writes those elements, so a move is
3996
+ * a new output primitive, not a smarter aligner. A moved paragraph comes out
3997
+ * as a deletion plus an insertion, which is what Word usually produces too.
3998
+ */
3999
+ interface CompareNote {
4000
+ code:
4001
+ /** A paragraph pair too structured to word-diff; struck and reinserted whole. */
4002
+ "coarse-paragraph"
4003
+ /** A table row pair whose cell counts differ; struck and reinserted whole. */
4004
+ | "coarse-row"
4005
+ /** A block that is neither a paragraph nor a table, added or removed. */
4006
+ | "untracked-block"
4007
+ /** A paragraph mark whose run properties differ across the swap; see below. */
4008
+ | "paragraph-mark-props"
4009
+ /** A part outside the body differs and was not compared. */
4010
+ | "part-not-compared"
4011
+ /** The documents share no paragraph at all; one paragraph mark goes untracked. */
4012
+ | "no-common-paragraph"
4013
+ /** Original-only content referencing a relationship the result does not have. */
4014
+ | "unresolved-relationship";
4015
+ detail: string;
4016
+ }
4017
+
2468
4018
  type ParagraphAlignment = "left" | "center" | "right" | "justify";
2469
4019
  interface PageLayoutPatch {
2470
4020
  /** Margins in inches. */
@@ -2495,47 +4045,58 @@ interface PageLayoutPatch {
2495
4045
  }
2496
4046
 
2497
4047
  /**
2498
- * Table manipulation: add/remove rows and columns relative to the cell
2499
- * containing the caret. Mutates source XML; callers checkpoint + relayout.
4048
+ * Find & replace and selection text transforms. Matching runs over the
4049
+ * parsed model — every story: the body (incl. tables), each header and
4050
+ * footer part, and each footnote and endnote. Positions map back to source
4051
+ * w:t elements so hits can be selected, replaced, or case-transformed.
2500
4052
  *
2501
- * v1 scope: column operations refuse tables using gridSpan (merged cells)
2502
- * rather than corrupt them.
4053
+ * Within one story, paragraphs concatenate with "\n" standing for the
4054
+ * paragraph mark (Word's ^p), so a query containing "\n" matches across
4055
+ * paragraph boundaries. A match never crosses a story boundary.
2503
4056
  */
2504
- type TableOp = "rowAbove" | "rowBelow" | "deleteRow" | "colLeft" | "colRight" | "deleteCol" | "deleteTable" | "mergeRight" | "mergeDown" | "splitCell" | {
2505
- kind: "cellShading";
2506
- fill: string | null;
2507
- } | {
2508
- kind: "cellVAlign";
2509
- v: "top" | "center" | "bottom";
2510
- } | {
2511
- kind: "textWrapping";
2512
- wrapping: "none" | "around";
2513
- xPx: number;
2514
- yPx: number;
2515
- };
2516
-
2517
- type ParagraphDividerStyle = "single" | "double" | "dotted" | "dashed" | "thinThickSmallGap";
2518
- interface ParagraphDivider {
2519
- style: ParagraphDividerStyle;
2520
- color: string;
2521
- widthPt: number;
2522
- spacePt: number;
4057
+ /** Which story a match sits in. Every header part is "header" (and every
4058
+ * footer part "footer") regardless of which section references it. */
4059
+ type FindStory = "body" | "header" | "footer" | "footnote" | "endnote";
4060
+ interface FindOptions {
4061
+ /** Case-sensitive matching. Default: case-insensitive. */
4062
+ matchCase?: boolean;
4063
+ /** Match whole words only: the characters adjacent to the hit must not be
4064
+ * letters, digits, or underscores (Word's "Find whole words only"). */
4065
+ wholeWord?: boolean;
4066
+ /** Word's wildcard pattern language (the subset below). Like Word, wildcard
4067
+ * matching is always case-sensitive and ignores wholeWord — the pattern's
4068
+ * own `<` and `>` express word boundaries. */
4069
+ wildcards?: boolean;
4070
+ }
4071
+ interface ReplaceAllResult {
4072
+ total: number;
4073
+ byStory: Partial<Record<FindStory, number>>;
4074
+ }
4075
+ /** A wire range inside one run: the shape suggestRevision and presence use. */
4076
+ interface WireRange {
4077
+ blockId: number;
4078
+ runId: number;
4079
+ start: number;
4080
+ end: number;
2523
4081
  }
2524
4082
 
2525
- interface CoverPageContent {
2526
- title: string;
2527
- subtitle?: string;
2528
- author?: string;
4083
+ /** Every building block the glossary part holds, name and category, in
4084
+ * document order — the gallery listing the toolbar groups by category. Empty
4085
+ * when the package has no glossary part. */
4086
+ interface BuildingBlockInfo {
4087
+ name: string;
4088
+ category: string;
2529
4089
  }
2530
- interface LineNumberingPatch {
2531
- /** Turn margin line numbering on/off for the target section(s). */
2532
- enabled: boolean;
2533
- /** Number every Nth line (1/5/10). Default 1 when first enabled. */
2534
- countBy?: number;
2535
- /** When the count resets. Default newPage when first enabled. */
2536
- restart?: "continuous" | "newPage" | "newSection";
2537
- /** First line number. Default 1. */
2538
- start?: number;
4090
+
4091
+ interface WatermarkSpec {
4092
+ text: string;
4093
+ /** Word's default 45° stamp. False lays the text flat across the page. */
4094
+ diagonal?: boolean;
4095
+ /** Glyph colour as RRGGBB, the form every other operation uses. Defaults to
4096
+ * Word's silver. */
4097
+ color?: string;
4098
+ /** v:fill opacity, 0..1. Defaults to Word's washed-out 0.5. */
4099
+ opacity?: number;
2539
4100
  }
2540
4101
 
2541
4102
  interface MissingFont {
@@ -2559,6 +4120,7 @@ interface MissingFont {
2559
4120
  * against everything sequenced in `(base, seq)` before applying and
2560
4121
  * broadcasting the canonical form (doc 03).
2561
4122
  */
4123
+
2562
4124
  type StableId = number;
2563
4125
  /** A caret/endpoint position within a run. */
2564
4126
  interface Position {
@@ -2599,10 +4161,13 @@ interface SuggestRevisionIntent extends IntentBase {
2599
4161
  blockId: StableId;
2600
4162
  glyph: "ins" | "del";
2601
4163
  }[];
2602
- suggest: {
2603
- author: string;
2604
- date: string;
2605
- };
4164
+ suggest: SuggestMeta;
4165
+ }
4166
+ /** Author + date of a tracked change, generated once by the originating
4167
+ * client so every replica writes the same w:author/w:date (rule a above). */
4168
+ interface SuggestMeta {
4169
+ author: string;
4170
+ date: string;
2606
4171
  }
2607
4172
  /** Insert `text` at a position. With `suggest`, the insertion is recorded as
2608
4173
  * a tracked change (w:ins) carrying the author + date (revision tracking /
@@ -2617,6 +4182,37 @@ interface InsertTextIntent extends IntentBase {
2617
4182
  date: string;
2618
4183
  };
2619
4184
  }
4185
+ /**
4186
+ * Insert an inline separator — a soft line break (w:br, Shift+Enter) or a
4187
+ * tab character (w:tab, Tab in a body paragraph) — at a position. The
4188
+ * mutation splits the addressed run's w:t IN PLACE (no new run, every stable
4189
+ * id preserved), and a separator counts ONE unit in the run's wire-offset
4190
+ * basis (core runWireLength), so the transform is exactly an insertText of
4191
+ * length one. With `suggest` the separator is recorded as a w:ins sibling
4192
+ * (insertSuggestedSeparator) like a typed suggestion.
4193
+ */
4194
+ interface InsertSeparatorIntent extends IntentBase {
4195
+ kind: "insertSeparator";
4196
+ at: Position;
4197
+ separator: "br" | "tab";
4198
+ suggest?: SuggestMeta;
4199
+ }
4200
+ /**
4201
+ * Delete the inline separator (w:br / w:tab / w:cr) occupying wire unit
4202
+ * [at.offset, at.offset+1) of the addressed run — insertSeparator's inverse
4203
+ * (a separator-aware Backspace/Delete). The plain mutation removes the
4204
+ * separator element and joins the two w:t halves its insert split, so the
4205
+ * transform is exactly a deleteText of length one in the separator-counting
4206
+ * wire basis. With `suggest` the separator is recorded as a tracked deletion
4207
+ * instead (deleteSuggestedSeparator: a w:del around the separator's own run
4208
+ * slice, the host run splitting like a text strike). A unit that no longer
4209
+ * holds a separator (concurrent edit) is a clean no-op on every replica.
4210
+ */
4211
+ interface DeleteSeparatorIntent extends IntentBase {
4212
+ kind: "deleteSeparator";
4213
+ at: Position;
4214
+ suggest?: SuggestMeta;
4215
+ }
2620
4216
  /** Delete `[start, end)` characters within a single run. */
2621
4217
  interface DeleteTextIntent extends IntentBase {
2622
4218
  kind: "deleteText";
@@ -2658,6 +4254,9 @@ interface FormatRunIntent extends IntentBase {
2658
4254
  /** RunFormatPatch (bold/italic/underline/strike/color/…). Structural shape
2659
4255
  * mirrors @wordinweb/core's RunFormatPatch; carried verbatim. */
2660
4256
  patch: Record<string, unknown>;
4257
+ /** Tracked-change (suggesting) metadata: the run records the properties it
4258
+ * had in a w:rPrChange instead of losing them. Omit for a direct format. */
4259
+ suggest?: SuggestMeta;
2661
4260
  }
2662
4261
  /**
2663
4262
  * Paragraph-level formatting: alignment and/or paragraph style. Block-level —
@@ -2670,17 +4269,26 @@ interface FormatParagraphIntent extends IntentBase {
2670
4269
  align?: "left" | "center" | "right" | "justify";
2671
4270
  /** Paragraph style id; null clears to Normal. Omit to leave unchanged. */
2672
4271
  styleId?: string | null;
4272
+ /** Tracked-change (suggesting) metadata: the paragraph records the
4273
+ * properties it had in a w:pPrChange. Omit for a direct format. */
4274
+ suggest?: SuggestMeta;
2673
4275
  }
2674
4276
  /**
2675
4277
  * Turn a paragraph into a bullet/numbered list item, or clear its list
2676
4278
  * formatting (kind null). Block-level: mutates w:pPr/numbering, preserves the
2677
4279
  * block id, moves no text — transform identity.
4280
+ *
4281
+ * A multi-paragraph selection rides in ONE intent (optional `moreBlockIds`
4282
+ * beyond the addressed paragraph): the single apply mints ONE numbering
4283
+ * definition shared by every target, which is both Word's semantics (one
4284
+ * continuous list) and the only convergent shape — per-paragraph intents
4285
+ * each minted a fresh definition at apply, diverging from the originator's
4286
+ * single shared-definition local mutation.
4287
+ *
4288
+ * REGISTERED: shape and wiring come from the core operation registry (see
4289
+ * RegisteredIntent below). The alias keeps the published type name.
2678
4290
  */
2679
- interface SetListTypeIntent extends IntentBase {
2680
- kind: "setListType";
2681
- blockId: StableId;
2682
- listKind: "bullet" | "number" | null;
2683
- }
4291
+ type SetListTypeIntent = RegisteredIntentOf<"setListType">;
2684
4292
  /**
2685
4293
  * Format a character sub-range of a single run (plan doc 03 F3). The run is
2686
4294
  * split into up to three pieces — before [0,start), middle [start,end)
@@ -2701,19 +4309,23 @@ interface FormatRangeIntent extends IntentBase {
2701
4309
  beforeId?: StableId;
2702
4310
  middleId: StableId;
2703
4311
  afterId?: StableId;
4312
+ /** Tracked-change (suggesting) metadata: the middle piece records the
4313
+ * properties it had in a w:rPrChange. Omit for a direct format. */
4314
+ suggest?: SuggestMeta;
2704
4315
  }
2705
4316
  /**
2706
- * A table operation that does NOT create new tracked nodes (so no carried ids
2707
- * are needed and the transform is identity): delete row/column/table, cell
2708
- * shading, cell vertical align. The target cell is addressed by the stable id
2709
- * of a paragraph inside it. Row/column INSERTION (which creates cells with new
2710
- * paragraphs and runs needing carried ids) is a documented harder extension.
4317
+ * A table operation addressed by the stable id of a paragraph inside the
4318
+ * target cell. Every op is position-stable (no surviving run's offsets move),
4319
+ * so the transform is identity; concurrent edits into removed cells fail
4320
+ * cleanly at apply (retired ids). Ops that create tracked nodes (the inserts,
4321
+ * mergeDown's fresh continuation paragraph, splitCell's new cells) take their
4322
+ * ids from `nodeIds`; ops that remove content prune the retired ids at apply.
2711
4323
  */
2712
4324
  interface TableOpIntent extends IntentBase {
2713
4325
  /** Stable id of a paragraph inside the target cell. */
2714
4326
  cellParagraphId: StableId;
2715
4327
  kind: "tableOp";
2716
- op: "deleteRow" | "deleteCol" | "deleteTable" | "rowAbove" | "rowBelow" | "colLeft" | "colRight" | {
4328
+ op: "deleteRow" | "deleteCol" | "deleteTable" | "rowAbove" | "rowBelow" | "colLeft" | "colRight" | "mergeRight" | "mergeDown" | "splitCell" | {
2717
4329
  kind: "cellShading";
2718
4330
  fill: string | null;
2719
4331
  } | {
@@ -2725,10 +4337,18 @@ interface TableOpIntent extends IntentBase {
2725
4337
  xPx: number;
2726
4338
  yPx: number;
2727
4339
  };
2728
- /** For INSERT ops (rowAbove/rowBelow/colLeft/colRight): carried ids for the
2729
- * new tracked nodes (p / r) in document order, so replicas address them
2730
- * alike. */
4340
+ /** For ops that create tracked nodes (rowAbove/rowBelow/colLeft/colRight,
4341
+ * mergeDown, splitCell): carried ids for the new nodes (p / r) in document
4342
+ * order, so replicas address them alike. */
2731
4343
  nodeIds?: StableId[];
4344
+ /**
4345
+ * Tracked-change (suggesting) metadata; see FormatParagraphIntent. Only the
4346
+ * three FORMATTING ops honour it — cellShading and cellVAlign record a
4347
+ * w:tcPrChange, textWrapping a w:tblPrChange. The structural ops have no
4348
+ * tracked form and ignore it, so the surfaces that enforce suggesting mode
4349
+ * refuse those outright rather than send one.
4350
+ */
4351
+ suggest?: SuggestMeta;
2732
4352
  }
2733
4353
  /**
2734
4354
  * Merge a paragraph into its predecessor (Backspace at paragraph start). The
@@ -2740,6 +4360,14 @@ interface MergeParagraphIntent extends IntentBase {
2740
4360
  kind: "mergeParagraph";
2741
4361
  /** The paragraph to merge into the one before it. */
2742
4362
  blockId: StableId;
4363
+ /**
4364
+ * Tracked-change (suggesting) metadata. Word tracks a merge as a DELETED
4365
+ * PARAGRAPH MARK: the pilcrow that separates the two paragraphs — the
4366
+ * PREVIOUS paragraph's mark — gets a w:del in its pPr/rPr, and both
4367
+ * paragraphs stay in place until the reviewer accepts. Omit for a direct
4368
+ * merge.
4369
+ */
4370
+ suggest?: SuggestMeta;
2743
4371
  }
2744
4372
  /**
2745
4373
  * Add a review comment anchored to an entire run. Comment markers
@@ -2835,11 +4463,14 @@ interface InsertMathIntent extends IntentBase {
2835
4463
  mathText: string;
2836
4464
  nodeIds: StableId[];
2837
4465
  }
2838
- /** Insert a shape/textbox drawing at the end of a run (sibling — identity). */
4466
+ /** Insert a shape/textbox drawing at the end of a run (sibling — identity).
4467
+ * `preset` is a legacy named preset (line/verticalLine/rectangle/
4468
+ * roundedRectangle/ellipse/diamond/textBox) or any ST_ShapeType name core's
4469
+ * preset-geometry table knows; validated with isValidShapePreset. */
2839
4470
  interface InsertShapeIntent extends IntentBase {
2840
4471
  kind: "insertShape";
2841
4472
  runId: StableId;
2842
- preset: "line" | "verticalLine" | "rectangle" | "roundedRectangle" | "ellipse" | "diamond" | "textBox";
4473
+ preset: string;
2843
4474
  text?: string;
2844
4475
  nodeIds: StableId[];
2845
4476
  }
@@ -2865,6 +4496,8 @@ interface AdjustIndentIntent extends IntentBase {
2865
4496
  kind: "adjustIndent";
2866
4497
  blockId: StableId;
2867
4498
  direction: 1 | -1;
4499
+ /** Tracked-change (suggesting) metadata; see FormatParagraphIntent. */
4500
+ suggest?: SuggestMeta;
2868
4501
  }
2869
4502
  /** Set paragraph line/before/after spacing. Block-level, identity. */
2870
4503
  interface SetSpacingIntent extends IntentBase {
@@ -2872,6 +4505,8 @@ interface SetSpacingIntent extends IntentBase {
2872
4505
  blockId: StableId;
2873
4506
  /** ParagraphSpacingPatch (before/after/line/lineRule) — carried verbatim. */
2874
4507
  patch: Record<string, unknown>;
4508
+ /** Tracked-change (suggesting) metadata; see FormatParagraphIntent. */
4509
+ suggest?: SuggestMeta;
2875
4510
  }
2876
4511
  /** Insert a page-number field at the end of a run (sibling insertion,
2877
4512
  * identity). Page fields are deterministic (no clock). */
@@ -2915,7 +4550,22 @@ interface InsertBookmarkIntent extends IntentBase {
2915
4550
  runId: StableId;
2916
4551
  name: string;
2917
4552
  }
2918
- type Intent = InsertTextIntent | DeleteTextIntent | SplitParagraphIntent | FormatRunIntent | FormatParagraphIntent | SetListTypeIntent | FormatRangeIntent | TableOpIntent | MergeParagraphIntent | CommentRunIntent | PasteBlocksIntent | InsertImageIntent | SuggestRevisionIntent | InsertBreakIntent | InsertMathIntent | InsertShapeIntent | ReplyCommentIntent | AdjustIndentIntent | SetSpacingIntent | InsertPageFieldIntent | SetLinkIntent | InsertFootnoteIntent | SetDropCapIntent | SetDividerIntent | InsertBookmarkIntent | InsertBlankPageIntent | InsertSectionBreakIntent | InsertCrossRefIntent | InsertCoverPageIntent | SetPageLayoutIntent | SetListLevelIntent | InsertWordArtIntent | InsertChartIntent | InsertSmartArtIntent | SetLineNumberingIntent | InsertDateTimeFieldIntent | InsertFieldIntent | SetDrawingRotationIntent | SetDrawingFillIntent | SetChartDataIntent | SetSmartArtNodeTextIntent | SetDrawingWordArtTextIntent | SetDrawingLineStyleIntent | SetImageAltTextIntent | RemoveLinkIntent | SetImageWrapIntent | SetDrawingOrderIntent | SetSmartArtDataIntent | SetSmartArtFillIntent | SetSmartArtTextFormatIntent | SetFloatingPagePositionIntent | ResizeDrawingIntent | ResizeTableColumnIntent | ResizeTableRowIntent | MoveTableIntent | RemoveDrawingIntent | SetMathLinearIntent | DeleteMathIntent | MoveMathIntent | EnsureHeaderFooterIntent | DeleteCommentIntent | InsertBookmarkRangeIntent | ToggleCheckboxIntent | AcceptRevisionIntent | RejectRevisionIntent | AcceptAllRevisionsIntent | RejectAllRevisionsIntent | InsertTableIntent;
4553
+ /**
4554
+ * Intents whose wire shape comes from the core operation registry. One
4555
+ * declaration in @wordinweb/core's edit/registry.ts produces the payload, the
4556
+ * apply, the validation, the carried-id budget, and the agent capability row;
4557
+ * here it just picks up the wire bookkeeping every intent carries.
4558
+ *
4559
+ * The registry admits only id-addressed, position-stable operations with no
4560
+ * wire inverse, so every registered intent's transform is identity and
4561
+ * collaborative undo skips it — see the registry's module comment.
4562
+ */
4563
+ type RegisteredIntent = RegisteredOperationBody & IntentBase;
4564
+ /** One registered intent, by kind. */
4565
+ type RegisteredIntentOf<Kind extends RegisteredOperationKind> = Extract<RegisteredIntent, {
4566
+ kind: Kind;
4567
+ }>;
4568
+ type Intent = InsertTextIntent | InsertSeparatorIntent | DeleteSeparatorIntent | DeleteTextIntent | SplitParagraphIntent | FormatRunIntent | FormatParagraphIntent | SetListTypeIntent | FormatRangeIntent | TableOpIntent | MergeParagraphIntent | CommentRunIntent | PasteBlocksIntent | InsertImageIntent | SuggestRevisionIntent | InsertBreakIntent | InsertMathIntent | InsertShapeIntent | ReplyCommentIntent | AdjustIndentIntent | SetSpacingIntent | InsertPageFieldIntent | SetLinkIntent | InsertFootnoteIntent | SetDropCapIntent | SetDividerIntent | InsertBookmarkIntent | InsertBlankPageIntent | InsertSectionBreakIntent | InsertCrossRefIntent | InsertCoverPageIntent | SetPageLayoutIntent | SetListLevelIntent | InsertWordArtIntent | InsertChartIntent | InsertSmartArtIntent | SetLineNumberingIntent | InsertDateTimeFieldIntent | InsertFieldIntent | SetDrawingRotationIntent | SetDrawingFillIntent | SetChartDataIntent | SetSmartArtNodeTextIntent | SetDrawingWordArtTextIntent | SetDrawingWordArtStyleIntent | SetDrawingLineStyleIntent | SetImageAltTextIntent | RemoveLinkIntent | SetImageWrapIntent | SetDrawingOrderIntent | SetSmartArtDataIntent | SetSmartArtFillIntent | SetSmartArtTextFormatIntent | SetFloatingPagePositionIntent | ResizeDrawingIntent | ResizeTableColumnIntent | MoveTableIntent | RemoveDrawingIntent | SetMathLinearIntent | DeleteMathIntent | MoveMathIntent | EnsureHeaderFooterIntent | DeleteCommentIntent | ResolveCommentIntent | EditCommentIntent | InsertBookmarkRangeIntent | AcceptRevisionIntent | RejectRevisionIntent | AcceptAllRevisionsIntent | RejectAllRevisionsIntent | RegisteredIntent;
2919
4569
  /** Insert a blank page at the end of a run. */
2920
4570
  interface InsertBlankPageIntent extends IntentBase {
2921
4571
  kind: "insertBlankPage";
@@ -2963,22 +4613,33 @@ interface InsertWordArtIntent extends IntentBase {
2963
4613
  kind: "insertWordArt";
2964
4614
  runId: StableId;
2965
4615
  text: string;
2966
- preset: "plain" | "archUp" | "archDown" | "wave" | "chevron";
4616
+ preset: "plain" | "archUp" | "archDown" | "wave" | "chevron" | "circle" | "button" | "chevronDown";
4617
+ /** Gallery style: glyph fill plus optional outline and shadow. */
4618
+ style?: {
4619
+ fill: string;
4620
+ outline?: {
4621
+ color: string;
4622
+ widthPt: number;
4623
+ };
4624
+ shadow?: boolean;
4625
+ };
2967
4626
  nodeIds: StableId[];
2968
4627
  }
2969
- /** Insert a data chart (column/bar/line/pie) at the end of a run. The chart
2970
- * data is carried verbatim; the workbook part is generated deterministically. */
4628
+ /** Insert a data chart at the end of a run. The chart data is carried
4629
+ * verbatim; the workbook part is generated deterministically. */
2971
4630
  interface InsertChartIntent extends IntentBase {
2972
4631
  kind: "insertChart";
2973
4632
  runId: StableId;
2974
4633
  chart: {
2975
- type: "column" | "bar" | "line" | "pie";
4634
+ type: "column" | "bar" | "line" | "pie" | "doughnut" | "area" | "scatter";
2976
4635
  title?: string;
2977
4636
  categories: string[];
2978
4637
  series: {
2979
4638
  name: string;
2980
4639
  values: number[];
2981
4640
  }[];
4641
+ /** How bar/column/area series group; absent = clustered. */
4642
+ grouping?: "clustered" | "stacked" | "percentStacked";
2982
4643
  };
2983
4644
  nodeIds: StableId[];
2984
4645
  }
@@ -3021,58 +4682,67 @@ interface InsertFieldIntent extends IntentBase {
3021
4682
  cachedResult?: string;
3022
4683
  nodeIds: StableId[];
3023
4684
  }
4685
+ interface DrawingTarget {
4686
+ runId: StableId;
4687
+ /** Parsed run-content index. It disambiguates several drawings carried by one run. */
4688
+ objectIndex?: number;
4689
+ }
3024
4690
  /** Rotate the drawing carried by a run. Identity transform (run-addressed). */
3025
- interface SetDrawingRotationIntent extends IntentBase {
4691
+ interface SetDrawingRotationIntent extends IntentBase, DrawingTarget {
3026
4692
  kind: "setDrawingRotation";
3027
4693
  runId: StableId;
3028
4694
  degrees: number;
3029
4695
  }
3030
4696
  /** Set/clear the solid fill of the drawing carried by a run. `color` is a
3031
4697
  * 6-hex-digit RGB (no #) or null to clear. */
3032
- interface SetDrawingFillIntent extends IntentBase {
4698
+ interface SetDrawingFillIntent extends IntentBase, DrawingTarget {
3033
4699
  kind: "setDrawingFill";
3034
4700
  runId: StableId;
3035
4701
  color: string | null;
3036
4702
  }
3037
4703
  /** Replace the data of the chart carried by a run (same shape as insertChart's
3038
4704
  * chart). Edits an existing chart in place. */
3039
- interface SetChartDataIntent extends IntentBase {
4705
+ interface SetChartDataIntent extends IntentBase, DrawingTarget {
3040
4706
  kind: "setChartData";
3041
4707
  runId: StableId;
3042
- chart: {
3043
- type: "column" | "bar" | "line" | "pie";
3044
- title?: string;
3045
- categories: string[];
3046
- series: {
3047
- name: string;
3048
- values: number[];
3049
- }[];
3050
- };
4708
+ chart: InsertChartIntent["chart"];
3051
4709
  }
3052
4710
  /** Set the text of one node of the SmartArt diagram carried by a run. */
3053
- interface SetSmartArtNodeTextIntent extends IntentBase {
4711
+ interface SetSmartArtNodeTextIntent extends IntentBase, DrawingTarget {
3054
4712
  kind: "setSmartArtNodeText";
3055
4713
  runId: StableId;
3056
4714
  index: number;
3057
4715
  text: string;
3058
4716
  }
3059
4717
  /** Replace the text of the WordArt drawing carried by a run. */
3060
- interface SetDrawingWordArtTextIntent extends IntentBase {
4718
+ interface SetDrawingWordArtTextIntent extends IntentBase, DrawingTarget {
3061
4719
  kind: "setDrawingWordArtText";
3062
4720
  runId: StableId;
3063
4721
  text: string;
3064
4722
  }
3065
- /** Set the outline (line) style of the drawing carried by a run. */
3066
- interface SetDrawingLineStyleIntent extends IntentBase {
4723
+ /** Set the glyph fill and alpha of the WordArt drawing carried by a run. */
4724
+ interface SetDrawingWordArtStyleIntent extends IntentBase, DrawingTarget {
4725
+ kind: "setDrawingWordArtStyle";
4726
+ runId: StableId;
4727
+ color: string;
4728
+ opacity: number;
4729
+ }
4730
+ /** Set or clear the outline of the drawing carried by a run. */
4731
+ type SetDrawingLineStyleIntent = IntentBase & DrawingTarget & {
3067
4732
  kind: "setDrawingLineStyle";
3068
4733
  runId: StableId;
4734
+ } & ({
4735
+ color: null;
4736
+ widthPx?: number;
4737
+ dash?: "solid" | "dashed" | "dotted";
4738
+ } | {
3069
4739
  color: string;
3070
4740
  widthPx: number;
3071
4741
  dash: "solid" | "dashed" | "dotted";
3072
- }
4742
+ });
3073
4743
  /** Set/clear the alt text (accessibility description) of the image/drawing
3074
4744
  * carried by a run. Empty string clears it. */
3075
- interface SetImageAltTextIntent extends IntentBase {
4745
+ interface SetImageAltTextIntent extends IntentBase, DrawingTarget {
3076
4746
  kind: "setImageAltText";
3077
4747
  runId: StableId;
3078
4748
  alt: string;
@@ -3084,19 +4754,19 @@ interface RemoveLinkIntent extends IntentBase {
3084
4754
  }
3085
4755
  /** Set the text-wrap mode of the drawing carried by a run. A floating mode
3086
4756
  * converts an inline drawing to an anchor. */
3087
- interface SetImageWrapIntent extends IntentBase {
4757
+ interface SetImageWrapIntent extends IntentBase, DrawingTarget {
3088
4758
  kind: "setImageWrap";
3089
4759
  runId: StableId;
3090
4760
  mode: "inline" | "square" | "topAndBottom" | "none" | "behind";
3091
4761
  }
3092
4762
  /** Bring the (floating) drawing carried by a run to front/back. */
3093
- interface SetDrawingOrderIntent extends IntentBase {
4763
+ interface SetDrawingOrderIntent extends IntentBase, DrawingTarget {
3094
4764
  kind: "setDrawingOrder";
3095
4765
  runId: StableId;
3096
4766
  order: "front" | "back";
3097
4767
  }
3098
4768
  /** Replace the whole SmartArt diagram (layout + items) carried by a run. */
3099
- interface SetSmartArtDataIntent extends IntentBase {
4769
+ interface SetSmartArtDataIntent extends IntentBase, DrawingTarget {
3100
4770
  kind: "setSmartArtData";
3101
4771
  runId: StableId;
3102
4772
  smartArt: {
@@ -3106,14 +4776,14 @@ interface SetSmartArtDataIntent extends IntentBase {
3106
4776
  }
3107
4777
  /** Set/clear the fill of a SmartArt node (or all nodes when nodeIndex omitted).
3108
4778
  * `color` is 6-hex RGB (no #) or null to clear. */
3109
- interface SetSmartArtFillIntent extends IntentBase {
4779
+ interface SetSmartArtFillIntent extends IntentBase, DrawingTarget {
3110
4780
  kind: "setSmartArtFill";
3111
4781
  runId: StableId;
3112
4782
  color: string | null;
3113
4783
  nodeIndex?: number;
3114
4784
  }
3115
4785
  /** Set the text format of a SmartArt node (or all nodes). */
3116
- interface SetSmartArtTextFormatIntent extends IntentBase {
4786
+ interface SetSmartArtTextFormatIntent extends IntentBase, DrawingTarget {
3117
4787
  kind: "setSmartArtTextFormat";
3118
4788
  runId: StableId;
3119
4789
  format: {
@@ -3127,7 +4797,7 @@ interface SetSmartArtTextFormatIntent extends IntentBase {
3127
4797
  nodeIndex?: number;
3128
4798
  }
3129
4799
  /** Position the (floating) drawing carried by a run relative to the page. */
3130
- interface SetFloatingPagePositionIntent extends IntentBase {
4800
+ interface SetFloatingPagePositionIntent extends IntentBase, DrawingTarget {
3131
4801
  kind: "setFloatingPagePosition";
3132
4802
  runId: StableId;
3133
4803
  xPx: number;
@@ -3135,7 +4805,7 @@ interface SetFloatingPagePositionIntent extends IntentBase {
3135
4805
  }
3136
4806
  /** Resize the drawing carried by a run (extent in px; the mutation clamps to
3137
4807
  * a 1px-EMU floor and keeps line geometry degenerate on its minor axis). */
3138
- interface ResizeDrawingIntent extends IntentBase {
4808
+ interface ResizeDrawingIntent extends IntentBase, DrawingTarget {
3139
4809
  kind: "resizeDrawing";
3140
4810
  runId: StableId;
3141
4811
  widthPx: number;
@@ -3151,13 +4821,6 @@ interface ResizeTableColumnIntent extends IntentBase {
3151
4821
  deltaPx: number;
3152
4822
  renderedWidths?: number[];
3153
4823
  }
3154
- /** Drag-resize a table row's height (trHeight atLeast). */
3155
- interface ResizeTableRowIntent extends IntentBase {
3156
- kind: "resizeTableRow";
3157
- cellParagraphId: StableId;
3158
- rowIdx: number;
3159
- heightPx: number;
3160
- }
3161
4824
  /** Position a table at page coordinates (converts inline to float). */
3162
4825
  interface MoveTableIntent extends IntentBase {
3163
4826
  kind: "moveTable";
@@ -3167,30 +4830,37 @@ interface MoveTableIntent extends IntentBase {
3167
4830
  preservePageStart: boolean;
3168
4831
  pageDelta: number;
3169
4832
  }
3170
- /** Delete the drawing carried by a run (removes the carrier run). */
3171
- interface RemoveDrawingIntent extends IntentBase {
4833
+ /** Delete the drawing at the supplied run-content target. */
4834
+ interface RemoveDrawingIntent extends IntentBase, DrawingTarget {
3172
4835
  kind: "removeDrawing";
3173
4836
  runId: StableId;
3174
4837
  }
3175
- /** Replace the equation of the math object in a paragraph (linear syntax).
4838
+ /**
4839
+ * Which equation inside a block a math intent means: its position among the
4840
+ * block's m:oMath elements in document order. Absent means the first one, so
4841
+ * an intent recorded before a block could hold more than one addressable
4842
+ * equation still names what it always named.
4843
+ */
4844
+ interface MathTarget {
4845
+ blockId: StableId;
4846
+ mathIndex?: number;
4847
+ }
4848
+ /** Replace the equation of a math object in a paragraph (linear syntax).
3176
4849
  * Math (m:oMath) lives at paragraph level, so this is block-addressed. */
3177
- interface SetMathLinearIntent extends IntentBase {
4850
+ interface SetMathLinearIntent extends IntentBase, MathTarget {
3178
4851
  kind: "setMathLinear";
3179
- blockId: StableId;
3180
4852
  mathText: string;
3181
4853
  }
3182
- /** Delete the math object in a paragraph. */
3183
- interface DeleteMathIntent extends IntentBase {
4854
+ /** Delete a math object in a paragraph. */
4855
+ interface DeleteMathIntent extends IntentBase, MathTarget {
3184
4856
  kind: "deleteMath";
3185
- blockId: StableId;
3186
4857
  }
3187
4858
  /** Drag-move the equation out of `blockId` to the text position `at`. The
3188
- * equation is addressed like the other math intents (block + firstMathIn); the
4859
+ * equation is addressed like the other math intents (block + mathIndex); the
3189
4860
  * destination is an ordinary wire caret. Dropping mid-text splits the
3190
4861
  * destination run, so the split-off tail run takes a carried id. */
3191
- interface MoveMathIntent extends IntentBase {
4862
+ interface MoveMathIntent extends IntentBase, MathTarget {
3192
4863
  kind: "moveMath";
3193
- blockId: StableId;
3194
4864
  at: Position;
3195
4865
  nodeIds: StableId[];
3196
4866
  }
@@ -3206,11 +4876,36 @@ interface EnsureHeaderFooterIntent extends IntentBase {
3206
4876
  hfKind: "header" | "footer";
3207
4877
  nodeIds: StableId[];
3208
4878
  }
3209
- /** Delete a comment (and its reply thread) by id. */
4879
+ /** Delete a comment (and its reply thread) by id. Addressed with a reply's
4880
+ * id it deletes just that reply — the thread/reply distinction is the id. */
3210
4881
  interface DeleteCommentIntent extends IntentBase {
3211
4882
  kind: "deleteComment";
3212
4883
  commentId: string;
3213
4884
  }
4885
+ /**
4886
+ * Resolve (true) or reopen (false) a comment thread. Addressed like
4887
+ * deleteComment, by the comment's deterministic string id (a reply id
4888
+ * resolves its thread's parent). The mutation writes w15:done on the thread
4889
+ * parent's commentsExtended entry; when the parent's body paragraph carries
4890
+ * no w14:paraId yet, the carried `paraId` candidate is consumed to mint one
4891
+ * (provenance rule a — generated once by the originator, identical XML on
4892
+ * every replica). No document run's text moves — identity transform.
4893
+ */
4894
+ interface ResolveCommentIntent extends IntentBase {
4895
+ kind: "resolveComment";
4896
+ commentId: string;
4897
+ resolved: boolean;
4898
+ /** paraId candidate, consumed only when the thread parent lacks one. */
4899
+ paraId: string;
4900
+ }
4901
+ /** Replace a comment's body text (Word's edit-my-comment). Author, date, id,
4902
+ * anchors and threading stay; deterministic given the carried text — identity
4903
+ * transform, clean no-op for an unknown id or unchanged text. */
4904
+ interface EditCommentIntent extends IntentBase {
4905
+ kind: "editComment";
4906
+ commentId: string;
4907
+ text: string;
4908
+ }
3214
4909
  /** Wrap a sub-range of a run's text in a named bookmark. */
3215
4910
  interface InsertBookmarkRangeIntent extends IntentBase {
3216
4911
  kind: "insertBookmarkRange";
@@ -3219,11 +4914,6 @@ interface InsertBookmarkRangeIntent extends IntentBase {
3219
4914
  start: number;
3220
4915
  end: number;
3221
4916
  }
3222
- /** Toggle the checkbox content control carried by a run. */
3223
- interface ToggleCheckboxIntent extends IntentBase {
3224
- kind: "toggleCheckbox";
3225
- runId: StableId;
3226
- }
3227
4917
  /** Accept one tracked change, addressed by its position in document order
3228
4918
  * (collectRevisions order — deterministic across replicas). */
3229
4919
  interface AcceptRevisionIntent extends IntentBase {
@@ -3235,21 +4925,17 @@ interface RejectRevisionIntent extends IntentBase {
3235
4925
  kind: "rejectRevision";
3236
4926
  index: number;
3237
4927
  }
3238
- /** Accept every tracked change in the document. */
4928
+ /** Accept every tracked change in the document, or only one author's when
4929
+ * `author` is given — a surface that owns some of the revisions must not
4930
+ * resolve the rest. Absent means all, which is what older clients send. */
3239
4931
  interface AcceptAllRevisionsIntent extends IntentBase {
3240
4932
  kind: "acceptAllRevisions";
4933
+ author?: string;
3241
4934
  }
3242
- /** Reject every tracked change in the document. */
4935
+ /** Reject every tracked change, or only one author's. See AcceptAll. */
3243
4936
  interface RejectAllRevisionsIntent extends IntentBase {
3244
4937
  kind: "rejectAllRevisions";
3245
- }
3246
- /** Insert a rows×cols table after the paragraph containing the anchor run. */
3247
- interface InsertTableIntent extends IntentBase {
3248
- kind: "insertTable";
3249
- runId: StableId;
3250
- rows: number;
3251
- cols: number;
3252
- nodeIds: StableId[];
4938
+ author?: string;
3253
4939
  }
3254
4940
  /** A sequenced log entry: an applied intent with its assigned seq, or a
3255
4941
  * rejection no-op (doc 03) that still occupies a position in the total order
@@ -3287,8 +4973,13 @@ type IdSidecar = ReturnType<StableIds["exportSidecar"]>;
3287
4973
  * nothing-to-undo the stack is empty
3288
4974
  * unavailable this connection has no collaborative undo (plaintext rooms,
3289
4975
  * whose authority is the server)
4976
+ * queued the press is HELD because this client's own edits are still
4977
+ * in flight and the undo stack cannot see them yet. It runs
4978
+ * when they confirm, or is dropped at the deadline — either
4979
+ * way onUndoQueue reports it (see
4980
+ * EncryptedCollabConnection.undoLast)
3290
4981
  */
3291
- type UndoOutcome = "undone" | "cannot-undo" | "changed-since" | "nothing-to-undo" | "unavailable";
4982
+ type UndoOutcome = "undone" | "cannot-undo" | "changed-since" | "nothing-to-undo" | "unavailable" | "queued";
3292
4983
 
3293
4984
  /**
3294
4985
  * E2EE primitives for the blind-sequencer mode (plan doc 13). Pure WebCrypto
@@ -3503,6 +5194,8 @@ interface RosterEntry {
3503
5194
  clientId: string;
3504
5195
  profile: ParticipantProfile;
3505
5196
  connected: boolean;
5197
+ /** Set by the server after a valid one-time AI invitation is redeemed. */
5198
+ participantType?: "human" | "agent";
3506
5199
  /**
3507
5200
  * Whether this participant may write, re-fanned on every transition.
3508
5201
  *
@@ -3570,12 +5263,37 @@ type ClientMessage = {
3570
5263
  * accounts: lose the bundle, lose the crown; re-seed to reclaim.
3571
5264
  */
3572
5265
  ownerToken?: string;
5266
+ /** One-time room invitation created by an already joined participant. */
5267
+ agentInvite?: {
5268
+ inviteId: string;
5269
+ token: string;
5270
+ };
3573
5271
  }
3574
5272
  /** Update this connection's profile mid-session (rename / recolor). */
3575
5273
  | {
3576
5274
  t: "profile";
3577
5275
  profile: ParticipantProfile;
3578
5276
  }
5277
+ /** Register or revoke an AI invitation. The server binds it to this
5278
+ * connection's room identity and never broadcasts the token. */
5279
+ | {
5280
+ t: "agent-invite";
5281
+ inviteId: string;
5282
+ token: string;
5283
+ expiresAt: number;
5284
+ } | {
5285
+ t: "agent-invite-revoke";
5286
+ inviteId: string;
5287
+ }
5288
+ /** End-to-end encrypted private chat. The server only routes the envelope
5289
+ * between the invited agent and the participant who created its invite. */
5290
+ | {
5291
+ t: "agent-chat";
5292
+ agentClientId: string;
5293
+ messageId: string;
5294
+ iv: string;
5295
+ ciphertext: string;
5296
+ }
3579
5297
  /**
3580
5298
  * Owner admin channel (doc 14 §2.5) — only honored from a connection
3581
5299
  * whose hello proved the owner token. All enforceable by a BLIND server
@@ -3813,6 +5531,26 @@ type ServerMessage = {
3813
5531
  | {
3814
5532
  t: "roster";
3815
5533
  roster: RosterEntry[];
5534
+ } | {
5535
+ t: "agent-invite-registered";
5536
+ inviteId: string;
5537
+ expiresAt: number;
5538
+ } | {
5539
+ t: "agent-invite-refused";
5540
+ inviteId: string;
5541
+ reason: string;
5542
+ } | {
5543
+ t: "agent-connected";
5544
+ inviteId: string;
5545
+ agentClientId: string;
5546
+ profile: ParticipantProfile;
5547
+ } | {
5548
+ t: "agent-chat";
5549
+ agentClientId: string;
5550
+ messageId: string;
5551
+ sender: "inviter" | "agent";
5552
+ iv: string;
5553
+ ciphertext: string;
3816
5554
  }
3817
5555
  /**
3818
5556
  * The session is going to END, and this is the grace period before it does
@@ -3886,13 +5624,28 @@ interface ToolbarMenuSelectProps {
3886
5624
  menuWidth?: number;
3887
5625
  className?: string;
3888
5626
  style?: React.CSSProperties;
5627
+ /** Greys the control and refuses to open it. Distinct from an option's own
5628
+ * `disabled`, which only rules out that one choice. */
5629
+ disabled?: boolean;
5630
+ /**
5631
+ * `"bar"` (default) sits in the ribbon: no border, no background, 26px.
5632
+ * `"field"` sits in a popover or dialog beside a label, where the control
5633
+ * has to read as an input — visible box, full width, popover background.
5634
+ *
5635
+ * A preset rather than a style object at each call site: the 23 selects
5636
+ * this replaced carried four different near-identical style sources between
5637
+ * them, which is how they drifted apart in the first place.
5638
+ */
5639
+ variant?: "bar" | "field";
5640
+ /** Fold priority for the bar's layout engine; see RibbonItem.fold. */
5641
+ fold?: number;
3889
5642
  }
3890
5643
  /** CSS-overridable replacement for visible native selects. An inert,
3891
5644
  * transparent select is kept as an event bridge for existing integrations;
3892
5645
  * every user-facing part is a button/listbox rendered by us. */
3893
- declare function ToolbarMenuSelect({ value, options, onChange, placeholder, title, ariaLabel, triggerAriaLabel, width, menuWidth, className, style, }: ToolbarMenuSelectProps): react.JSX.Element;
5646
+ declare function ToolbarMenuSelect({ value, options, onChange, placeholder, title, ariaLabel, triggerAriaLabel, width, menuWidth, className, style, disabled, variant, fold, }: ToolbarMenuSelectProps): react.JSX.Element;
3894
5647
  /** Toolbar control groups a host can disable via the `features` prop. */
3895
- type ToolbarFeature = "history" | "styles" | "font" | "size" | "format" | "color" | "highlight" | "alignment" | "indent" | "spacing" | "link" | "lists" | "table" | "image" | "icon" | "screenshot" | "model3D" | "media" | "object" | "chart" | "smartArt" | "comment" | "footnote" | "bookmark" | "crossReference" | "dateTime" | "field" | "equation" | "symbol" | "shape" | "divider" | "textBox" | "wordArt" | "drawing" | "arrange" | "dropCap" | "headerFooter" | "coverPage" | "pageNumber" | "break" | "layout" | "help" | "download";
5648
+ type ToolbarFeature = "history" | "styles" | "charStyles" | "formatPainter" | "font" | "size" | "format" | "color" | "highlight" | "alignment" | "indent" | "spacing" | "link" | "lists" | "borders" | "table" | "image" | "icon" | "screenshot" | "model3D" | "media" | "object" | "chart" | "smartArt" | "comment" | "footnote" | "bookmark" | "crossReference" | "dateTime" | "field" | "citations" | "quickParts" | "equation" | "symbol" | "shape" | "divider" | "textBox" | "wordArt" | "drawing" | "arrange" | "dropCap" | "headerFooter" | "watermark" | "coverPage" | "pageNumber" | "break" | "layout" | "review" | "help" | "download";
3896
5649
  /**
3897
5650
  * Every `DocxViewApi` command that INSERTS content at the caret, paired with
3898
5651
  * the toolbar feature group that gates its control.
@@ -3922,7 +5675,7 @@ type ToolbarMode = "simple" | "advanced";
3922
5675
  interface DocxToolbarProps {
3923
5676
  api: DocxViewApi | null;
3924
5677
  onSave?: (bytes: Uint8Array) => void;
3925
- /** Simple shows basic Home editing; advanced adds the Insert, Draw, and Layout ribbons. */
5678
+ /** Simple shows basic Home editing; advanced adds the Insert, Draw, Layout, and Review ribbons. */
3926
5679
  mode?: ToolbarMode;
3927
5680
  /** Per-group overrides; every group defaults to enabled. */
3928
5681
  features?: Partial<Record<ToolbarFeature, boolean>>;
@@ -3930,8 +5683,80 @@ interface DocxToolbarProps {
3930
5683
  className?: string;
3931
5684
  /** Inline overrides merged onto the toolbar root; wins over the defaults. */
3932
5685
  style?: React.CSSProperties;
5686
+ /** Connected collaborator names offered as @mention shortcuts in comments. */
5687
+ commentMentions?: string[];
5688
+ /** Start with the toolbar expanded (all groups inline, wrapping onto extra
5689
+ * rows). A choice the user makes via the chevron toggle is persisted in
5690
+ * localStorage and wins over this default. */
5691
+ defaultExpanded?: boolean;
5692
+ /**
5693
+ * Shortcuts the embedding application owns, listed in the help guide
5694
+ * alongside the editor's own. A desktop menu accelerator is consumed by the
5695
+ * menu before the editor sees the key, so the engine cannot discover it;
5696
+ * the app declares it once and passes it here, and the reference stays
5697
+ * complete. Electron hosts can read the live menu rather than repeat it:
5698
+ * walk Menu.getApplicationMenu() for {label, accelerator} pairs.
5699
+ */
5700
+ hostShortcuts?: HostShortcutSection[];
3933
5701
  }
3934
- declare function DocxToolbar({ api, onSave, mode, features, className, style, }: DocxToolbarProps): react.JSX.Element;
5702
+ declare function DocxToolbar({ api, onSave, mode, features, className, style, commentMentions, defaultExpanded, hostShortcuts, }: DocxToolbarProps): react.JSX.Element;
5703
+
5704
+ /**
5705
+ * The host affordance for Compare Documents — Word's "legal blackline".
5706
+ *
5707
+ * The engine call (`compareDocuments` in @wordinweb/core) is pure: two
5708
+ * documents in, a third out. Everything a browser has to do around it lives
5709
+ * here — pick a file, read it, hand the result somewhere — so the toolbar
5710
+ * entry is one button and the engine stays free of the DOM.
5711
+ *
5712
+ * The document on screen is the ORIGINAL and the picked file is the REVISED
5713
+ * one, which is the direction Word's dialog defaults to. The result is a new
5714
+ * document whose differences are tracked changes attributed to `author`;
5715
+ * accepting them all gives the picked file back, rejecting them all gives what
5716
+ * is on screen back.
5717
+ */
5718
+ interface CompareResult {
5719
+ /** The compared document, as .docx bytes. */
5720
+ bytes: Uint8Array;
5721
+ /** What the comparison could not express as a tracked change. */
5722
+ notes: CompareNote[];
5723
+ /** The picked file's name, for whatever the host calls the result. */
5724
+ revisedName: string;
5725
+ }
5726
+ interface CompareWithFileOptions {
5727
+ /** Who the tracked changes are attributed to. */
5728
+ author?: string;
5729
+ /** Timestamp for every revision. Defaults to now. */
5730
+ date?: string;
5731
+ /** Compare formatting as well as text. Default true, as in Word. */
5732
+ formatting?: boolean;
5733
+ /** Report whitespace-only differences. Default true, as in Word. */
5734
+ whitespace?: boolean;
5735
+ }
5736
+ /**
5737
+ * Compare `originalBytes` with a .docx `file`. Returns the result and the list
5738
+ * of differences that could not be tracked, which a caller should show rather
5739
+ * than swallow — a comparison that quietly skipped something is worse than one
5740
+ * that says what it skipped.
5741
+ */
5742
+ declare function compareWithFile(originalBytes: Uint8Array, file: File, options?: CompareWithFileOptions): Promise<CompareResult>;
5743
+ /** Ask for one .docx. Resolves to null when the picker is dismissed. */
5744
+ declare function pickDocx(): Promise<File | null>;
5745
+ /** Save bytes to the user's downloads under `name`. */
5746
+ declare function downloadDocx(bytes: Uint8Array, name: string): void;
5747
+ /**
5748
+ * The whole affordance behind one click: pick a file, compare, hand the result
5749
+ * to `onResult` (or download it), and report anything untrackable.
5750
+ *
5751
+ * Returns false when no file was picked, so a caller can tell "cancelled" from
5752
+ * "compared with nothing to say".
5753
+ */
5754
+ declare function pickAndCompare(originalBytes: Uint8Array, options?: CompareWithFileOptions & {
5755
+ onResult?: (result: CompareResult) => void;
5756
+ onNotes?: (notes: CompareNote[]) => void;
5757
+ }): Promise<boolean>;
5758
+ /** "report.docx" → "report (compared).docx". */
5759
+ declare function comparedName(revisedName: string): string;
3935
5760
 
3936
5761
  interface DocxViewApi {
3937
5762
  /** Apply character formatting to the current browser selection. */
@@ -3940,6 +5765,8 @@ interface DocxViewApi {
3940
5765
  addComment(text: string): boolean;
3941
5766
  /** Insert a footnote at the caret. False without a caret. */
3942
5767
  addFootnote(text: string): boolean;
5768
+ /** Insert an endnote at the caret. False without a caret. */
5769
+ addEndnote(text: string): boolean;
3943
5770
  /** Insert a dynamic page-number field at the caret (body, header or footer). */
3944
5771
  insertPageNumber(kind?: "page" | "pageOfTotal"): boolean;
3945
5772
  /** Insert any Word field instruction supported by the renderer. */
@@ -3948,10 +5775,137 @@ interface DocxViewApi {
3948
5775
  insertDateTime(kind: "date" | "time", picture?: string): boolean;
3949
5776
  /** Named bookmarks in document order. */
3950
5777
  listBookmarks(): string[];
5778
+ /**
5779
+ * How many entries a table of contents would find right now, and how many
5780
+ * index entries have been marked.
5781
+ *
5782
+ * A TOC and an INDEX are both built from something the user has to do first —
5783
+ * apply heading styles, or mark entries — and inserting one before that puts
5784
+ * Word's own "No table of contents entries found." into the document, which
5785
+ * is accurate and explains nothing. A surface that can ask beforehand can say
5786
+ * what is missing instead.
5787
+ */
5788
+ contentsAvailability(): {
5789
+ headings: number;
5790
+ indexEntries: number;
5791
+ };
3951
5792
  /** Add a bookmark around the selection, or a zero-length bookmark at the caret. */
3952
5793
  addBookmark(name: string): boolean;
3953
5794
  /** Insert a live text or page reference to a bookmark. */
3954
5795
  insertCrossReference(bookmark: string, kind: "text" | "page"): boolean;
5796
+ /**
5797
+ * Cross-reference targets beyond plain bookmarks, in document order:
5798
+ * headings, captions (SEQ paragraphs), and numbered list items — what
5799
+ * Word's Cross-reference dialog lists per reference type. The keys stay
5800
+ * valid until the next call (or a document change).
5801
+ */
5802
+ listCrossRefTargets(): {
5803
+ key: string;
5804
+ kind: "heading" | "caption" | "numberedItem";
5805
+ text: string;
5806
+ }[];
5807
+ /**
5808
+ * Insert a live text or page reference to a listed target. A target with
5809
+ * no hidden `_Ref` bookmark gets one first (Word's own mechanism), so the
5810
+ * REF/PAGEREF has something durable to point at.
5811
+ */
5812
+ insertCrossRefToTarget(key: string, kind: "text" | "page"): boolean;
5813
+ /**
5814
+ * Insert a Word caption — "<label> <n>" in the Caption style, the number a
5815
+ * live SEQ field — below or above the selected object (or the caret's
5816
+ * block; a caret inside a table captions the table). updateFields
5817
+ * renumbers the whole label sequence.
5818
+ */
5819
+ insertCaption(label: string, text?: string, position?: "below" | "above"): boolean;
5820
+ /**
5821
+ * Recompute every supported field's cached result — Word's F9, and what a
5822
+ * host calls before printing or exporting so the file it hands on carries
5823
+ * current page numbers, dates and cross-references. False when nothing
5824
+ * changed.
5825
+ *
5826
+ * `fileName` and `author` are the host's to supply: the engine has no
5827
+ * filesystem of its own, so FILENAME and AUTHOR fields keep their cached
5828
+ * results without them.
5829
+ */
5830
+ updateFields(values?: {
5831
+ fileName?: string;
5832
+ author?: string;
5833
+ }): boolean;
5834
+ /**
5835
+ * Insert a native Word table of contents at the caret.
5836
+ *
5837
+ * Outside a room the page numbers are filled in immediately, from a layout
5838
+ * this call runs. IN a room the entries land with placeholders: page numbers
5839
+ * come from a layout, a layout depends on the host's font metrics, and that
5840
+ * is precisely the value `updateFields` carries as data instead of letting
5841
+ * each replica recompute it. Run the update pass to fill them.
5842
+ */
5843
+ insertToc(options?: TocOptions): boolean;
5844
+ /**
5845
+ * Rebuild every table of contents from the document's current headings, then
5846
+ * repage it. Use after headings are added, removed or retitled; repaging an
5847
+ * unchanged heading set needs only updateFields. False in a shared document,
5848
+ * for the reason insertToc gives.
5849
+ */
5850
+ refreshTocs(): boolean;
5851
+ /**
5852
+ * Every data column this document's MERGEFIELD fields name, in document
5853
+ * order, once each — headers and footers included.
5854
+ *
5855
+ * A mail-merge UI compares this against its data source's headers to report
5856
+ * which fields the data does not supply. Those keep their «Name» placeholder
5857
+ * while previewing, so the user sees an unbound field instead of a blank.
5858
+ *
5859
+ * Read-only, like the whole preview path: no operation, no undo entry, no
5860
+ * collab traffic. Set the values through the `mergeRecord` prop.
5861
+ */
5862
+ listMergeFieldNames(): string[];
5863
+ /** The bibliography sources the document's sources part declares, in part
5864
+ * order — what a citation picker and the source manager list. */
5865
+ listCitationSources(): BibliographySource[];
5866
+ /** The selected citation style (b:Sources/@StyleName — "APA", "MLA", or
5867
+ * whatever an arriving document declares), or null with no sources part. */
5868
+ getCitationStyle(): string | null;
5869
+ /** Select the citation style (APA or MLA) for citations and the
5870
+ * bibliography, creating the sources part when the document has none.
5871
+ * Refreshes citation displays and bibliographies in the same call. */
5872
+ setCitationStyle(style: CitationStyle): boolean;
5873
+ /** Add a bibliography source (book, article, website, or report), creating
5874
+ * the sources part on first use. False when the tag is already taken. */
5875
+ createCitationSource(spec: CitationSourceSpec): boolean;
5876
+ /** Change a source's fields, by tag. Refreshes citation displays and
5877
+ * bibliographies in the same call. */
5878
+ editCitationSource(tag: string, patch: CitationSourcePatch): boolean;
5879
+ /** Delete a source. False while a CITATION field still cites it — remove
5880
+ * the citations first. */
5881
+ deleteCitationSource(tag: string): boolean;
5882
+ /** Insert a citation to an existing source at the caret, displayed in the
5883
+ * document's citation style. False for a tag the sources part lacks. */
5884
+ insertCitation(tag: string): boolean;
5885
+ /** Insert a bibliography at the caret: a BIBLIOGRAPHY field whose entries
5886
+ * are generated from the document's sources (regenerated by updateFields). */
5887
+ insertBibliography(): boolean;
5888
+ /** The Quick Parts (building blocks) the document's glossary part declares,
5889
+ * name and category, in part order — what the gallery lists. */
5890
+ listBuildingBlocks(): BuildingBlockInfo[];
5891
+ /** Save the current selection as a named Quick Part, creating the glossary
5892
+ * part on first use. False with nothing selected, a name already taken, or
5893
+ * a selection the paste-fragment gate refuses (the pasteBlocks gate). */
5894
+ createBuildingBlock(name: string, category?: string): boolean;
5895
+ /** Insert a named Quick Part at the caret, cloning its stored content.
5896
+ * False for a name the glossary part lacks. */
5897
+ insertBuildingBlock(name: string): boolean;
5898
+ /** Delete a Quick Part from the glossary part, by name. False when the name
5899
+ * names none. */
5900
+ deleteBuildingBlock(name: string): boolean;
5901
+ /** Mark an index entry: an invisible XE field after the selection (or the
5902
+ * caret). With no `entry` the selected text is the entry; a colon makes a
5903
+ * subentry ("Widgets:assembly"). False with nothing to mark. */
5904
+ addIndexEntry(entry?: string): boolean;
5905
+ /** Insert an index at the caret, built from the document's XE entry marks
5906
+ * (alphabetized; page numbers filled by the update pass). Rebuilt from the
5907
+ * current marks by updateFields. */
5908
+ insertIndex(): boolean;
3955
5909
  /** Insert an editable inline equation from WordInWeb's linear math syntax. */
3956
5910
  insertEquation(linear: string): boolean;
3957
5911
  /** Insert a Unicode symbol through the normal undo/suggestion-aware typing path. */
@@ -3963,7 +5917,7 @@ interface DocxViewApi {
3963
5917
  dash: DrawingLineDash;
3964
5918
  }): boolean;
3965
5919
  /** Insert editable DrawingML WordArt at the caret. */
3966
- insertWordArt(text: string, preset?: WordArtPreset): boolean;
5920
+ insertWordArt(text: string, preset?: WordArtPreset, style?: WordArtStyle): boolean;
3967
5921
  /** Insert a native editable ChartML chart at the caret. */
3968
5922
  insertChart(data: ChartData): boolean;
3969
5923
  /** Replace the selected native chart's type and data. */
@@ -4007,12 +5961,82 @@ interface DocxViewApi {
4007
5961
  redo(): void;
4008
5962
  canUndo(): boolean;
4009
5963
  canRedo(): boolean;
5964
+ /**
5965
+ * Record the document's current state as an undo boundary.
5966
+ *
5967
+ * For a host that mutates the document THROUGH SOMETHING OTHER THAN THIS API
5968
+ * — an agent session applying intents to the same DocxDocument — and still
5969
+ * wants Cmd+Z to reach those changes. Every editing method here checkpoints
5970
+ * on its own; nothing outside can, so without this an external edit is
5971
+ * invisible to undo and leaves the history's shadow describing a document
5972
+ * that no longer exists.
5973
+ */
5974
+ checkpoint(): void;
5975
+ /**
5976
+ * MAIL MERGE OUTPUT: this document with one record's values baked into its
5977
+ * MERGEFIELD results, as bytes. The open document is NOT modified — the
5978
+ * merge runs against a fresh parse of its serialized form, so a hundred
5979
+ * records produce a hundred files and leave the template on screen alone.
5980
+ */
5981
+ mergeRecordToBytes(record: MergeRecord): Uint8Array;
4010
5982
  /** Insert a rows×cols table at the caret's paragraph. */
4011
5983
  insertTable(rows: number, cols: number): void;
4012
5984
  /** Row/column/table operations on the table containing the caret. */
4013
5985
  tableOp(op: TableOp): void;
4014
5986
  /** Current table-cell fill, undefined when the caret is outside a table. */
4015
5987
  getTableCellFill(): string | null | undefined;
5988
+ /** What a Table Properties dialog prefills from — the table's width, its
5989
+ * grid columns, the caret's column, the default cell margins and the header
5990
+ * band, in points. Undefined when the caret is outside a table. */
5991
+ getTableProperties(): TablePropertiesPt | undefined;
5992
+ /** Set or clear border edges on the caret's cell or its whole table. A null
5993
+ * `border` removes the edges so they inherit again; `{ style: "none" }`
5994
+ * instead suppresses them, which is Word's No Border. */
5995
+ setTableBorders(scope: "cell" | "table", edges: TableBorderEdge[], border: TableBorderSpec | null): void;
5996
+ /** The table styles this document's styles.xml defines — the whole valid
5997
+ * domain for setTableStyle, since an undefined id renders nothing. */
5998
+ listTableStyles(): {
5999
+ id: string;
6000
+ name: string;
6001
+ }[];
6002
+ /** Style id of the table containing the caret: null when it has none,
6003
+ * undefined when the caret is outside a table. */
6004
+ getTableStyleId(): string | null | undefined;
6005
+ /** Apply a table style, or with null remove the reference. */
6006
+ setTableStyle(styleId: string | null): void;
6007
+ /** Word's six table style options for the caret's table, undefined outside
6008
+ * a table. */
6009
+ getTableLook(): TableLookToggles | undefined;
6010
+ /** Set some of the six table style options. */
6011
+ setTableLook(patch: Partial<TableLookToggles>): void;
6012
+ /** Set the table's preferred width. `value` is points for "pt", 0-100 for
6013
+ * "pct", and ignored for "auto". */
6014
+ setTableWidth(unit: "pt" | "pct" | "auto", value?: number): void;
6015
+ /** Set one column to an exact width in points. */
6016
+ setTableColumnWidth(colIdx: number, widthPt: number): void;
6017
+ /** Switch the table between fixed column widths and autofit. Switching to
6018
+ * fixed freezes the columns as currently RENDERED, which is what the user
6019
+ * is looking at; switching to autofit measures them from content again. */
6020
+ setTableLayout(layout: "fixed" | "autofit"): void;
6021
+ /** Set the table's default cell margins, or the caret cell's override, in
6022
+ * points. A null patch drops the override. */
6023
+ setTableCellMargins(scope: "cell" | "table", margins: CellMarginsPt | null): void;
6024
+ /** Repeat the first `count` rows as a header band on every page. */
6025
+ setTableHeaderRows(count: number): void;
6026
+ /** Sort the caret table's body rows by one grid column, as text or as
6027
+ * numbers. The repeating header band always stays in place; `hasHeader`
6028
+ * additionally pins the first row. Refuses tables with merged cells. */
6029
+ sortTableRows(colIdx: number, order: "asc" | "desc", compare: "text" | "number", hasHeader?: boolean): void;
6030
+ /** Convert the selected paragraphs (or the caret paragraph) into a table,
6031
+ * one row per paragraph, cells split on the separator. */
6032
+ convertTextToTable(separator: "tab" | "comma"): boolean;
6033
+ /** Convert the caret's table into paragraphs, one per row, cell texts
6034
+ * joined by the separator. */
6035
+ convertTableToText(separator: "tab" | "comma"): boolean;
6036
+ /** Insert a table formula field ("=SUM(ABOVE)", "=A1+B2", …) at the caret's
6037
+ * cell paragraph, with an optional \# number format like "#,##0.00". The
6038
+ * result is evaluated immediately; updateFields recomputes it. */
6039
+ insertTableFormula(formula: string, numFmt?: string): boolean;
4016
6040
  /** Insert an image file at the caret (inline, natural size clamped to column).
4017
6041
  * The result is REPORTED rather than swallowed: a picker that accepts a file
4018
6042
  * and then does nothing is indistinguishable from a broken button. */
@@ -4053,22 +6077,51 @@ interface DocxViewApi {
4053
6077
  setParagraphDivider(divider: ParagraphDivider | null): boolean;
4054
6078
  /** Direct bottom-border divider on the caret paragraph. */
4055
6079
  getParagraphDivider(): ParagraphDivider | null;
6080
+ /** Replace the selected paragraphs' direct tab stops (w:tabs). An empty
6081
+ * list removes them, so style stops and the default grid apply again. */
6082
+ setTabStops(stops: TabStopSpec[]): boolean;
6083
+ /** Direct tab stops on the caret paragraph, sorted by position. */
6084
+ getTabStops(): TabStopSpec[];
6085
+ /** Patch the selected paragraphs' border edges (w:pBdr) and/or shading
6086
+ * fill (w:shd). Each named edge is set (spec) or cleared (null); absent
6087
+ * edges stay. `shading: null` removes the fill. */
6088
+ setParagraphBorders(patch: ParagraphBordersPatch): boolean;
6089
+ /** Direct borders and shading on the caret paragraph. */
6090
+ getParagraphBorders(): {
6091
+ borders: Partial<Record<ParagraphBorderEdge, TableBorderSpec>>;
6092
+ shading: string | null;
6093
+ };
4056
6094
  /** Apply or remove a native Word drop cap on the caret paragraph. */
4057
6095
  setDropCap(mode: "drop" | "margin" | null, lines?: number): boolean;
4058
6096
  /** Remove direct character formatting from the selection. */
4059
6097
  clearFormatting(): void;
4060
6098
  /** Change the selection's case. */
4061
6099
  changeCase(mode: "upper" | "lower" | "title"): void;
4062
- /** Find matches for a query; selects the first and returns the count. */
4063
- find(query: string, opts?: {
4064
- matchCase?: boolean;
4065
- }): number;
6100
+ /** Find matches for a query across every story (body, headers, footers,
6101
+ * footnotes, endnotes); selects the first and returns the count. */
6102
+ find(query: string, opts?: FindOptions): number;
4066
6103
  /** Select the next/previous match; returns 1-based index or 0. */
4067
6104
  findStep(delta: 1 | -1): number;
4068
6105
  /** Replace the current match; returns remaining match count. */
4069
6106
  replaceCurrent(replacement: string): number;
4070
- /** Replace every match; returns how many were replaced. */
4071
- replaceAll(query: string, replacement: string): number;
6107
+ /** Replace every match; reports how many were replaced, per story. */
6108
+ replaceAll(query: string, replacement: string, opts?: FindOptions): ReplaceAllResult;
6109
+ /** Go To: scroll the given 1-based page into view. */
6110
+ goToPage(page: number): boolean;
6111
+ /** Go To: move the caret to a named bookmark and scroll it into view. */
6112
+ goToBookmark(name: string): boolean;
6113
+ /**
6114
+ * Select an exact text range by stable address — the `{blockId, runId,
6115
+ * start, end}` shape the wire, presence, and suggestRevision already use
6116
+ * (offsets in the run's wire basis; see getEncodedCaret for the caret
6117
+ * half). Several ranges select together, for a word split across runs.
6118
+ * Selection and scroll ride find-navigation's machinery; the view only
6119
+ * scrolls when the range is off screen. In a local document the first call
6120
+ * populates the stable-id table (enableStableIds), so hosts — the desktop
6121
+ * spellcheck's select-and-replace — can address text without a collab
6122
+ * mount. False when no range resolves to text.
6123
+ */
6124
+ selectRange(range: WireRange | WireRange[]): boolean;
4072
6125
  /** Paragraph styles for the style menu (declared + Word built-ins). */
4073
6126
  listParagraphStyles(): {
4074
6127
  id: string;
@@ -4076,6 +6129,55 @@ interface DocxViewApi {
4076
6129
  }[];
4077
6130
  /** pStyle id of the caret paragraph (null = Normal). */
4078
6131
  getParagraphStyleId(): string | null;
6132
+ /**
6133
+ * Every declared style with the data a gallery entry needs: identity,
6134
+ * cascade parent, quick-style flag, usage count, and resolved preview props.
6135
+ * Richer than listParagraphStyles, which is the flat menu the toolbar's
6136
+ * existing style dropdown reads.
6137
+ */
6138
+ listStyles(filter?: {
6139
+ type?: StyleGalleryEntry["type"];
6140
+ quickStyleOnly?: boolean;
6141
+ }): StyleGalleryEntry[];
6142
+ /** Add a paragraph or character style definition. */
6143
+ createStyle(spec: StyleSpec): boolean;
6144
+ /** Patch an existing style definition; the cascade re-resolves live. */
6145
+ modifyStyle(styleId: string, patch: StylePatch): boolean;
6146
+ /** Delete a style definition; its users fall back to its basedOn. */
6147
+ deleteStyle(styleId: string): boolean;
6148
+ /**
6149
+ * Stamp a text watermark across every page. Word keeps a watermark in the
6150
+ * header parts, so a document with none gets one first.
6151
+ */
6152
+ insertWatermark(spec: WatermarkSpec): boolean;
6153
+ /**
6154
+ * Stamp a PICTURE across every page — Word's Watermark > Picture. Replaces
6155
+ * whichever watermark is already there, text or picture.
6156
+ *
6157
+ * The picture is washed out (Word's own recolor) and fitted to the page.
6158
+ * Takes the same formats a shared document can carry, so SVG is refused;
6159
+ * `imageAccept()` is the list to hand a file picker.
6160
+ */
6161
+ insertPictureWatermark(file: Blob): Promise<ImageInsertResult>;
6162
+ /** Take the watermark back off every page. False when there was none. */
6163
+ removeWatermark(): boolean;
6164
+ /** Apply (or with null remove) a character style over the selection. */
6165
+ setCharacterStyle(styleId: string | null): void;
6166
+ /** Change a list level's number format, label text, or indent. */
6167
+ setNumberingLevel(ilvl: number | null, patch: LevelPatch): boolean;
6168
+ /**
6169
+ * Apply a preset multilevel definition (Word's multilevel gallery) to the
6170
+ * caret's list — the paragraphs join a numbered list first when they are
6171
+ * not in one. Compiles onto the existing per-level patch operation, so it
6172
+ * rides the same wire ops and converges like any level edit.
6173
+ */
6174
+ applyNumberingPreset(preset: NumberingPresetId): boolean;
6175
+ /** Restart list numbering at the caret, or (null) continue the preceding list. */
6176
+ setNumberingRestart(start: number | null): boolean;
6177
+ /** Format painter, half one: the selection's formatting, or null. */
6178
+ copyFormatting(): SelectionFormat | null;
6179
+ /** Format painter, half two: paint a copied format over the selection. */
6180
+ applyCopiedFormatting(format: SelectionFormat): void;
4079
6181
  /** Change margins / page size / orientation (inches). */
4080
6182
  setPageLayout(patch: PageLayoutPatch, scope?: "document" | "section"): void;
4081
6183
  /** One-based logical section containing the caret or selection. */
@@ -4102,10 +6204,90 @@ interface DocxViewApi {
4102
6204
  closeHeaderFooter(): void;
4103
6205
  /** Enter and, if needed, create the header or footer on the caret's page. */
4104
6206
  openHeaderFooter(kind: "header" | "footer"): boolean;
6207
+ /** Word's "Different First Page" (w:titlePg). Enabling creates the empty
6208
+ * first-page header/footer parts; disabling keeps them for re-enable. */
6209
+ setDifferentFirstPage(on: boolean): boolean;
6210
+ /** Whether any section requests a different first page. */
6211
+ getDifferentFirstPage(): boolean;
6212
+ /** Word's "Different Odd & Even Pages" (settings.xml w:evenAndOddHeaders).
6213
+ * Enabling creates the empty even-page header/footer parts. */
6214
+ setOddEvenHeaders(on: boolean): boolean;
6215
+ getOddEvenHeaders(): boolean;
6216
+ /** Page-number format and start-at (w:pgNumType): decimal / roman / letter
6217
+ * numbering, restart value. scope "section" targets the caret's section;
6218
+ * "document" (and any shared document) every section. */
6219
+ setPageNumberFormat(patch: PageNumberFormatPatch, scope?: "document" | "section"): boolean;
6220
+ /** Current page-number settings for the caret's section. */
6221
+ getPageNumberFormat(): {
6222
+ fmt: PageNumberFormat;
6223
+ start: number | null;
6224
+ };
6225
+ /** Patch the automatic-hyphenation settings (w:autoHyphenation,
6226
+ * w:hyphenationZone in points, w:doNotHyphenateCaps). Round-trip state:
6227
+ * this engine's layout does not hyphenate automatically — the settings
6228
+ * govern Word's own rendering of the document. */
6229
+ setHyphenation(patch: {
6230
+ auto?: boolean;
6231
+ zonePt?: number | null;
6232
+ noCaps?: boolean;
6233
+ }): boolean;
6234
+ /** Current hyphenation settings (zonePt null = Word's default 18pt). */
6235
+ getHyphenation(): {
6236
+ auto: boolean;
6237
+ zonePt: number | null;
6238
+ noCaps: boolean;
6239
+ };
6240
+ /** Word's page-number position gallery: a single live PAGE field in the
6241
+ * header (top) or footer (bottom), aligned left/center/right. Creates the
6242
+ * part on demand and replaces its content — a gallery pick, not a merge. */
6243
+ insertPageNumberPosition(position: PageNumberGalleryPosition, align: PageNumberGalleryAlign): boolean;
6244
+ /** Word's "Remove Page Numbers": strips PAGE/NUMPAGES fields from every
6245
+ * header and footer part. */
6246
+ removePageNumbers(): boolean;
6247
+ /** The Header & Footer preset gallery: replace a header or footer's
6248
+ * content with a preset layout (blank / centered title / title + date /
6249
+ * three-column). */
6250
+ insertHeaderFooterPreset(kind: "header" | "footer", preset: HeaderFooterPreset): boolean;
6251
+ /** Footnote options (w:footnotePr): number format, restart rule, start-at,
6252
+ * and position (§17.11.17/19/21). scope "section" targets the caret's
6253
+ * section; "document" (and any shared document) every section. Format and
6254
+ * start drive the painted marks; restart honors "eachSect" only; position
6255
+ * round-trips without changing where footnotes lay out (always page
6256
+ * bottom in this engine). */
6257
+ setFootnoteOptions(patch: FootnoteOptionsPatch, scope?: "document" | "section"): boolean;
6258
+ /** Current footnote options for the caret's section. */
6259
+ getFootnoteOptions(): ReturnType<typeof footnoteOptionsAt>;
6260
+ /** Same as setFootnoteOptions, for endnotes — position vocabulary differs
6261
+ * (sectEnd / docEnd). */
6262
+ setEndnoteOptions(patch: EndnoteOptionsPatch, scope?: "document" | "section"): boolean;
6263
+ getEndnoteOptions(): ReturnType<typeof endnoteOptionsAt>;
6264
+ /** Resolve (true) or reopen (false) a comment thread. */
6265
+ resolveComment(id: string, resolved: boolean): boolean;
6266
+ /** Replace a comment's body text (Word's edit-my-comment). */
6267
+ editComment(id: string, text: string): boolean;
6268
+ /** Select and scroll to the next/previous commented range (Review-tab
6269
+ * navigation). Returns the focused thread's comment id, or null when the
6270
+ * document has no anchored comments. */
6271
+ stepComment(delta: 1 | -1): string | null;
6272
+ /** Word Count: body text statistics from the model plus the page count
6273
+ * from the latest layout. */
6274
+ wordCount(): {
6275
+ words: number;
6276
+ characters: number;
6277
+ charactersWithSpaces: number;
6278
+ paragraphs: number;
6279
+ pages: number;
6280
+ };
4105
6281
  /** Effective formatting of the current selection (toolbar state), or null. */
4106
6282
  getSelectionFormat(): SelectionFormat | null;
4107
6283
  /** Print the rendered pages (browser print dialog / save as PDF). */
4108
6284
  print(): void;
6285
+ /**
6286
+ * The standalone print document (all pages plus styles) as an HTML string,
6287
+ * for hosts that produce the PDF themselves (e.g. a desktop shell).
6288
+ * Null before the first render.
6289
+ */
6290
+ exportPrintHtml(): string | null;
4109
6291
  /** Serialize the (edited) document back to .docx bytes. */
4110
6292
  save(): Uint8Array;
4111
6293
  /** Page count after the latest layout. */
@@ -4123,14 +6305,21 @@ interface DocxViewApi {
4123
6305
  /** Reject the tracked change at the caret (drop insertion / restore deletion). */
4124
6306
  rejectRevisionAtCaret(): boolean;
4125
6307
  /** How many tracked changes (suggestions) the document currently holds. */
4126
- revisionCount(): number;
6308
+ /** Tracked changes in the document, or only `author`'s when given. */
6309
+ revisionCount(author?: string): number;
4127
6310
  /** Accept every tracked change (one undo step). Returns how many applied. */
4128
- acceptAllRevisions(): number;
6311
+ /** Accept every tracked change, or only `author`'s. A surface that owns some
6312
+ * of the document's revisions must pass its own author, or it resolves work
6313
+ * it never made. */
6314
+ acceptAllRevisions(author?: string): number;
4129
6315
  /** Reject every tracked change (one undo step). Returns how many applied. */
4130
- rejectAllRevisions(): number;
4131
- /** Current caret as stable-id addresses (collab), or null. The encoding
4132
- * survives a reconciliation reload, so it can be captured from a view
4133
- * about to remount and restored into its replacement. */
6316
+ /** Reject every tracked change, or only `author`'s. See acceptAllRevisions. */
6317
+ rejectAllRevisions(author?: string): number;
6318
+ /** Current caret as stable-id addresses, or null. The encoding survives a
6319
+ * reconciliation reload, so it can be captured from a view about to
6320
+ * remount and restored into its replacement. In a local document the first
6321
+ * call populates the stable-id table (like selectRange), so hosts can
6322
+ * capture addresses outside a collab mount too. */
4134
6323
  getEncodedCaret(): EncodedCaret | null;
4135
6324
  /** Restore a caret captured by getEncodedCaret. False when the position no
4136
6325
  * longer resolves (or outside collab mode). */
@@ -4154,6 +6343,7 @@ interface DocxViewApi {
4154
6343
  document: DocxDocument;
4155
6344
  }
4156
6345
  type ScreenshotInsertResult = "inserted" | "unsupported" | "cancelled" | "error" | "no-caret";
6346
+
4157
6347
  /**
4158
6348
  * Image formats a SHARED document accepts, and the single source of truth for
4159
6349
  * both halves of that promise: the guard that declines an intent and the
@@ -4169,6 +6359,10 @@ type ScreenshotInsertResult = "inserted" | "unsupported" | "cancelled" | "error"
4169
6359
  * it is an intent-shape change with an ENGINE_VERSION bump, not a UI edit.
4170
6360
  */
4171
6361
  declare const COLLAB_IMAGE_EXTS: readonly ["png", "jpg", "jpeg", "gif", "bmp", "webp"];
6362
+ /** File-picker accept list for the raster formats. Also the picture
6363
+ * watermark's whole list: a VML `v:imagedata` cannot carry an SVG, in a
6364
+ * shared document or a local one. */
6365
+ declare const RASTER_IMAGE_ACCEPT = "image/png,image/jpeg,image/gif,image/bmp,image/webp";
4172
6366
  /**
4173
6367
  * Why an image insert did not happen, so the caller can SAY so. Every one of
4174
6368
  * these was a bare `return` until a user hit the `unsupported-format` path and
@@ -4307,9 +6501,17 @@ interface DocxViewProps {
4307
6501
  * stack would edit this replica with nothing on the wire. Absent ⇒ undo
4308
6502
  * declines rather than mutating. */
4309
6503
  undoLast?: () => void;
6504
+ /** Single-process session marker (useAgentDocumentSession over a
6505
+ * LocalDocumentSession). Its presence tells the editor there are NO peers,
6506
+ * so undo/redo replay the local history stack; it fires after each applied
6507
+ * undo/redo so the session can bump its revision. A REAL room must leave
6508
+ * this unset, or Cmd+Z would fork it. */
6509
+ noteLocalHistory?: () => void;
4310
6510
  };
4311
6511
  /** Author name stamped on comment replies (default "You"). */
4312
6512
  commentAuthor?: string;
6513
+ /** Connected collaborator names offered as @mention shortcuts in comments. */
6514
+ commentMentions?: string[];
4313
6515
  /** Render review comments (range highlights + margin balloons). Default true. */
4314
6516
  showComments?: boolean;
4315
6517
  /** Tracked-changes display: "final" (default) or "markup". */
@@ -4318,7 +6520,53 @@ interface DocxViewProps {
4318
6520
  * render (unavailable, or lacking the document's script) — the page is
4319
6521
  * silently substituting and may differ from Word. Empty array = all good. */
4320
6522
  onMissingFonts?: (missing: MissingFont[]) => void;
6523
+ /**
6524
+ * Mail-merge PREVIEW: the active record's column values, painted into the
6525
+ * document's MERGEFIELD fields. Absent (or undefined) shows the «Name»
6526
+ * placeholders — that is preview "off".
6527
+ *
6528
+ * Nothing is written to the document. The values are resolved as the pages
6529
+ * are laid out, so they cannot reach a saved file, an undo entry or the
6530
+ * collab wire; preview is per-viewer state by construction. Changing this
6531
+ * prop relays the pages without re-parsing the .docx.
6532
+ *
6533
+ * A column the record does NOT carry keeps its «Name» placeholder, so the
6534
+ * user can see which fields the data leaves unbound. Word renders such a
6535
+ * field blank; this is a deliberate divergence. A column that IS present but
6536
+ * empty renders empty and suppresses the field's \b and \f switch texts.
6537
+ */
6538
+ mergeRecord?: MergeRecord;
6539
+ }
6540
+ interface AgentDocumentViewBinding {
6541
+ subscribe(listener: () => void): () => void;
6542
+ getSnapshot(): number;
6543
+ doc: DocxDocument;
6544
+ submit(intent: EditorIntent): void;
6545
+ submitOp(intent: {
6546
+ kind: string;
6547
+ } & Record<string, unknown>): void;
6548
+ allocIds(count: number): number[];
6549
+ uploadMedia?(bytes: Uint8Array): Promise<{
6550
+ blobSha: string;
6551
+ bytesLen: number;
6552
+ iv?: string;
6553
+ } | null>;
6554
+ /** Bump the session revision after a local history undo/redo mutated the
6555
+ * document outside the intent path. See collab.noteLocalHistory. */
6556
+ noteHistory?(): void;
6557
+ takeRenderScope(): {
6558
+ kind: "doc";
6559
+ } | {
6560
+ kind: "block";
6561
+ blocks: XmlElement[];
6562
+ } | {
6563
+ kind: "split";
6564
+ before: XmlElement;
6565
+ after: XmlElement;
6566
+ } | null;
4321
6567
  }
6568
+ /** Connect a framework-neutral local document session to DocxView. */
6569
+ declare function useAgentDocumentSession(binding: AgentDocumentViewBinding): Pick<DocxViewProps, "source" | "collab">;
4322
6570
  /**
4323
6571
  * High-fidelity paginated DOCX viewer (and, with `editable`, editor).
4324
6572
  *
@@ -4327,6 +6575,6 @@ interface DocxViewProps {
4327
6575
  * <DocxView source="/report.docx" editable onReady={setApi} /> // editing
4328
6576
  * ```
4329
6577
  */
4330
- declare function DocxView({ source, zoom, fitWidth, narrowWidth, editable, className, style, onLoad, onPageCountChange, onReady, onError, commentAuthor, showComments, revisions, onMissingFonts, collab, }: DocxViewProps): react.JSX.Element;
6578
+ declare function DocxView({ source, zoom, fitWidth, narrowWidth, editable, className, style, onLoad, onPageCountChange, onReady, onError, commentAuthor, commentMentions, showComments, revisions, onMissingFonts, mergeRecord, collab, }: DocxViewProps): react.JSX.Element;
4331
6579
 
4332
- export { type ShapePreset as A, ToolbarMenuSelect as B, type ClientMessage as C, DocxDocument as D, type ToolbarMenuSelectOption as E, type ToolbarMenuSelectProps as F, layoutDocument as G, printPages as H, type Intent as I, renderToDom as J, type LineageHead as L, type PresencePosition as P, type RosterEntry as R, type ServerMessage as S, type ToolbarFeature as T, type UndoOutcome as U, type WriteStatus as W, type XmlElement as X, type ParticipantProfile as a, type IdSidecar as b, type ToolbarMode as c, type DocxViewApi as d, bytesToB64 as e, deriveEpochKeys as f, docKeyFromFragment as g, stretchShareCode as h, COLLAB_IMAGE_EXTS as i, type CoverPageContent as j, DocxToolbar as k, type DocxToolbarProps as l, mintDocKey as m, DocxView as n, type DocxViewProps as o, type DrawingTool as p, INSERT_COMMANDS as q, type ImageInsertResult as r, sealCheckpoint as s, type InsertCommandSpec as t, type LineNumberingPatch as u, type PageLayoutPatch as v, type ParagraphAlignment as w, type RunFormatPatch as x, type ScreenshotInsertResult as y, type SelectionFormat as z };
6580
+ export { downloadDocx as $, type AgentDocumentViewBinding as A, RASTER_IMAGE_ACCEPT as B, type ClientMessage as C, DocxDocument as D, type EncodedCaret as E, type RunFormatPatch as F, type ScreenshotInsertResult as G, type HostShortcutSection as H, type Intent as I, type SelectionFormat as J, type ShapePreset as K, type LineageHead as L, type MergeRecord as M, NUMBERING_PRESETS as N, ToolbarMenuSelect as O, type PresencePosition as P, type ToolbarMenuSelectOption as Q, type RosterEntry as R, type ServerMessage as S, type ToolbarFeature as T, type UndoOutcome as U, type ToolbarMenuSelectProps as V, type WriteStatus as W, type XmlElement as X, type WireRange as Y, compareWithFile as Z, comparedName as _, type ParticipantProfile as a, layoutDocument as a0, pickAndCompare as a1, pickDocx as a2, printPages as a3, renderToDom as a4, useAgentDocumentSession as a5, type IdSidecar as b, type ToolbarMode as c, type DocxViewApi as d, bytesToB64 as e, deriveEpochKeys as f, docKeyFromFragment as g, stretchShareCode as h, COLLAB_IMAGE_EXTS as i, type CompareResult as j, type CompareWithFileOptions as k, type CoverPageContent as l, mintDocKey as m, DocxToolbar as n, type DocxToolbarProps as o, DocxView as p, type DocxViewProps as q, type DrawingTool as r, sealCheckpoint as s, INSERT_COMMANDS as t, type ImageInsertResult as u, type InsertCommandSpec as v, type LineNumberingPatch as w, type NumberingPresetId as x, type PageLayoutPatch as y, type ParagraphAlignment as z };