@wordinweb/agent 0.1.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.
@@ -0,0 +1,3092 @@
1
+ /**
2
+ * Minimal, fast XML parser producing a lightweight element tree.
3
+ *
4
+ * OOXML parts are machine-generated, namespace-prefixed, well-formed XML with
5
+ * no DTDs. This parser handles exactly that subset: elements, attributes,
6
+ * text, CDATA, comments, processing instructions, and the built-in + numeric
7
+ * character entities. Namespace prefixes are kept verbatim (`w:p`), and
8
+ * lookups match on either the full name or the local name, which is robust
9
+ * for every mainstream producer (Word, LibreOffice, Google Docs export).
10
+ */
11
+ interface XmlElement {
12
+ name: string;
13
+ attrs: Record<string, string>;
14
+ children: XmlElement[];
15
+ /** Concatenated character data directly inside this element. */
16
+ text: string;
17
+ /** Editor placeholder retained in memory but omitted from XML while empty. */
18
+ omitWhenEmpty?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Typed document model produced by the parser and consumed by the layout
23
+ * engine. All lengths are CSS px (see units.ts) unless a field name says
24
+ * otherwise. Property bags are Partial-style: `undefined` means "not set at
25
+ * this level" so the style-inheritance merge can distinguish absence from an
26
+ * explicit value.
27
+ *
28
+ * Model nodes keep `src` references to the XML elements they were parsed
29
+ * from: the XML tree is the source of truth for editing — commands mutate it
30
+ * and the model is re-derived, which preserves round-trip fidelity for
31
+ * everything untouched.
32
+ */
33
+
34
+ type BorderStyle = "none" | "single" | "double" | "dotted" | "dashed" | "thick" | "wave" | "dotDash" | "dotDotDash" | "thinThickSmallGap" | "triple";
35
+ interface Border {
36
+ style: BorderStyle;
37
+ /** Stroke width in px. */
38
+ width: number;
39
+ /** CSS color. */
40
+ color: string;
41
+ /** Gap between border and content in px (w:space, points in OOXML). */
42
+ space: number;
43
+ /** Declared stroke width in px BEFORE the 0.75px paint floor (w:sz/8 pt).
44
+ * Word's LAYOUT math (table row border share) uses this true width — a
45
+ * sz-4 rule contributes 0.5pt, not the floored 0.5625pt (phase23 p66:
46
+ * 45 rows drift 2.5px down without it). */
47
+ rawWidth?: number;
48
+ }
49
+ interface ParagraphBorders {
50
+ top?: Border;
51
+ bottom?: Border;
52
+ left?: Border;
53
+ right?: Border;
54
+ /** Drawn between consecutive paragraphs that both specify it. */
55
+ between?: Border;
56
+ }
57
+ type LineSpacingRule = "auto" | "atLeast" | "exact";
58
+ interface LineSpacing {
59
+ rule: LineSpacingRule;
60
+ /**
61
+ * For "auto": multiple of single line height (240ths in OOXML, stored here
62
+ * as the multiplier, e.g. 1.15). For atLeast/exact: px.
63
+ */
64
+ value: number;
65
+ }
66
+ type Alignment = "left" | "center" | "right" | "justify";
67
+ interface TabStop {
68
+ /** px from the left text edge */
69
+ pos: number;
70
+ align: "left" | "center" | "right" | "decimal" | "bar";
71
+ leader: "none" | "dot" | "hyphen" | "underscore" | "middleDot";
72
+ /** w:val="clear": removes the inherited stop at this pos when tab lists
73
+ * merge down the style chain (never a live stop itself). */
74
+ clear?: boolean;
75
+ }
76
+ interface RunProps {
77
+ bold?: boolean;
78
+ italic?: boolean;
79
+ underline?: string;
80
+ strike?: boolean;
81
+ doubleStrike?: boolean;
82
+ /** Resolved primary font family name (ascii/hAnsi, theme-resolved). */
83
+ font?: string;
84
+ /** East Asian font (rFonts w:eastAsia): used for CJK codepoints. */
85
+ fontEastAsia?: string;
86
+ /** hAnsi font (rFonts w:hAnsi, theme-resolved): Word uses this channel for
87
+ * non-ASCII, non-CJK, non-complex characters (curly quotes, ≤, dashes). A
88
+ * run that declares only w:ascii leaves it inherited from the style chain. */
89
+ fontHAnsi?: string;
90
+ /** Complex-script font (rFonts w:cs): used for RTL/complex runs. */
91
+ fontComplex?: string;
92
+ /** w:rtl — this run's text is right-to-left (Arabic/Hebrew). */
93
+ rtl?: boolean;
94
+ /** Font size in px. */
95
+ size?: number;
96
+ /** CSS color; "auto" resolved to inherit/black by renderer. */
97
+ color?: string;
98
+ highlight?: string;
99
+ /** Character shading fill (w:shd). */
100
+ shading?: string;
101
+ /** Run border (w:bdr): a box painted around the run. Word merges adjacent
102
+ * runs with identical borders into one box; a run wrapping across lines
103
+ * closes the box on every line segment. */
104
+ border?: Border;
105
+ verticalAlign?: "baseline" | "superscript" | "subscript";
106
+ caps?: boolean;
107
+ smallCaps?: boolean;
108
+ /** Letter spacing px (w:spacing, twips in OOXML). */
109
+ letterSpacing?: number;
110
+ /** Minimum font size in px at which w:kern enables pair kerning; null is
111
+ * the explicit w:kern=0 override. */
112
+ kerningThreshold?: number | null;
113
+ /** Paint-only letter spacing when layout advances intentionally differ. */
114
+ paintLetterSpacing?: number;
115
+ /** w:w horizontal character scaling as a fraction (1.5 = 150%). */
116
+ textScale?: number;
117
+ /** w:fitText target width in px: the run's advances scale so its text
118
+ * occupies exactly this width (resolved to textScale at layout time). */
119
+ fitText?: number;
120
+ /** w:position baseline shift in px, positive = raised. */
121
+ raise?: number;
122
+ /** w:outline — hollow stroked glyphs. */
123
+ outline?: boolean;
124
+ /** w:emboss / w:imprint — Word triple-draws offset copies. */
125
+ emboss?: boolean;
126
+ imprint?: boolean;
127
+ vanish?: boolean;
128
+ /** Character style id (w:rStyle). */
129
+ styleId?: string;
130
+ lang?: string;
131
+ }
132
+ interface NumberingRef {
133
+ numId: number;
134
+ ilvl: number;
135
+ }
136
+ interface ParaProps {
137
+ styleId?: string;
138
+ alignment?: Alignment;
139
+ /** The exact OOXML w:jc value when it is a "justify" flavor whose behavior
140
+ * differs from plain "both": `distribute` also stretches the LAST line, and
141
+ * the Arabic `*Kashida` levels elongate glyphs (kashida) so lines pack fewer
142
+ * words and — at medium/high — wrap to more lines. Absent for plain "both".
143
+ * `alignment` stays "justify" for all of these. */
144
+ justifyKind?: "distribute" | "lowKashida" | "mediumKashida" | "highKashida";
145
+ /** w:bidi — right-to-left paragraph: lines assemble RTL and default
146
+ * alignment flips to the right edge. */
147
+ bidi?: boolean;
148
+ /** px */
149
+ indentLeft?: number;
150
+ indentRight?: number;
151
+ /** Positive px; mutually exclusive with hanging in effect. */
152
+ indentFirstLine?: number;
153
+ /** Positive px hanging indent. */
154
+ indentHanging?: number;
155
+ spacingBefore?: number;
156
+ spacingAfter?: number;
157
+ /** w:beforeAutospacing/afterAutospacing: HTML-style automatic paragraph
158
+ * spacing — Word ignores the literal before/after and inserts one blank
159
+ * line's worth of space. */
160
+ beforeAutospacing?: boolean;
161
+ afterAutospacing?: boolean;
162
+ /** Word's default when absent is auto/1.0 via docDefaults. */
163
+ lineSpacing?: LineSpacing;
164
+ /** w:snapToGrid: whether this paragraph's lines use the section docGrid. */
165
+ snapToGrid?: boolean;
166
+ contextualSpacing?: boolean;
167
+ keepNext?: boolean;
168
+ /** w:framePr w:dropCap: the paragraph is a drop-cap letter frame. */
169
+ dropCap?: {
170
+ mode: "drop" | "margin";
171
+ lines: number;
172
+ hSpace: number;
173
+ pageAnchored?: boolean;
174
+ };
175
+ /** w:framePr positioned text frame (absolute-positioned paragraph the body
176
+ * text wraps around). Geometry in px. Individual attributes are optional so a
177
+ * framePr merges attribute-by-attribute across the style cascade (a direct
178
+ * framePr that sets only h/x/y keeps the style framePr's width/anchor — IEEE
179
+ * authors); the engine normalizes defaults at layout time. */
180
+ frame?: {
181
+ w?: number;
182
+ h?: number;
183
+ hRule?: "auto" | "atLeast" | "exact";
184
+ x?: number;
185
+ y?: number;
186
+ hAnchor?: "page" | "margin" | "text" | "column";
187
+ vAnchor?: "page" | "margin" | "text" | "paragraph";
188
+ xAlign?: "left" | "center" | "right" | "inside" | "outside";
189
+ yAlign?: "top" | "center" | "bottom" | "inside" | "outside" | "inline";
190
+ wrap?: "around" | "auto" | "notBeside" | "through" | "tight" | "none";
191
+ /** w:hSpace / w:vSpace: horizontal/vertical margin around the frame (px). */
192
+ hSpace?: number;
193
+ vSpace?: number;
194
+ };
195
+ keepLines?: boolean;
196
+ pageBreakBefore?: boolean;
197
+ widowControl?: boolean;
198
+ borders?: ParagraphBorders;
199
+ /** Paragraph shading fill as CSS color. */
200
+ shading?: string;
201
+ numbering?: NumberingRef | null;
202
+ /** A numPr that carries ilvl but no numId (Heading3 basedOn Heading2)
203
+ * overrides only the list LEVEL, keeping the inherited numId; the style
204
+ * chain applies it to `numbering` on merge. */
205
+ numberingLevelOverride?: number;
206
+ tabs?: TabStop[];
207
+ outlineLevel?: number;
208
+ /** Run props declared on pPr/rPr — apply to the paragraph mark & numbering label. */
209
+ markRunProps?: RunProps;
210
+ /**
211
+ * styleId of the enclosing table (set on cell paragraphs at parse time).
212
+ * A table style's own pPr layers between docDefaults and the paragraph
213
+ * style, e.g. TableGrid's `spacing after=0 line=240` overrides docDefaults'
214
+ * `after=200 line=276` so list cells lay out compactly.
215
+ */
216
+ tableStyleId?: string;
217
+ /**
218
+ * Grid position of the enclosing cell plus the table's tblLook (set on cell
219
+ * paragraphs at parse time, like tableStyleId). Lets run/paragraph
220
+ * resolution layer the applicable w:tblStylePr conditional formats (e.g.
221
+ * firstRow bold + white) between the table style's own rPr and the
222
+ * paragraph style chain.
223
+ */
224
+ tableCellCond?: {
225
+ look?: TableLook;
226
+ rowIdx: number;
227
+ nRows: number;
228
+ colStart: number;
229
+ colSpan: number;
230
+ nCols: number;
231
+ };
232
+ }
233
+ interface TextContent {
234
+ kind: "text";
235
+ text: string;
236
+ /** Source w:t element inside the run, when this text came verbatim from one. */
237
+ srcT?: XmlElement;
238
+ /** When this text is the glyph of a modern checkbox content control, the
239
+ * source `w14:checkbox` element (in the enclosing w:sdt's sdtPr). Its
240
+ * presence makes the glyph a click-to-toggle target rather than plain text. */
241
+ checkbox?: XmlElement;
242
+ }
243
+ interface BreakContent {
244
+ kind: "break";
245
+ breakType: "line" | "page" | "column";
246
+ }
247
+ interface TabContent {
248
+ kind: "tab";
249
+ }
250
+ interface PTabContent {
251
+ kind: "ptab";
252
+ /** Absolute-position tab: jump to left/center/right of the base. */
253
+ alignment: "left" | "center" | "right";
254
+ relativeTo: "margin" | "indent";
255
+ }
256
+ interface ImageContent {
257
+ kind: "image";
258
+ /** Part path of the image inside the package. */
259
+ part: string;
260
+ width: number;
261
+ height: number;
262
+ /** True when the drawing is floating (wp:anchor); v1 lays it out inline. */
263
+ anchored?: boolean;
264
+ /** a:srcRect crop, fractions of the source bitmap. */
265
+ crop?: {
266
+ l: number;
267
+ t: number;
268
+ r: number;
269
+ b: number;
270
+ };
271
+ /** a:xfrm rotation, degrees clockwise. */
272
+ rotation?: number;
273
+ /** a:ln picture outline (Word draws it just outside the image). */
274
+ border?: {
275
+ color: string;
276
+ width: number;
277
+ };
278
+ /** Source w:drawing (or pict) element, for resize/move editing. */
279
+ srcDrawing?: XmlElement;
280
+ /** Native Office 3D model shown through its saved poster raster. */
281
+ model3D?: Model3DReference;
282
+ /** Word online-video metadata shown through its saved poster raster. */
283
+ webVideo?: WebVideoReference;
284
+ /** Embedded OLE package activated as a safe download in the browser. */
285
+ embeddedObject?: EmbeddedObjectReference;
286
+ }
287
+ interface Model3DReference {
288
+ part: string;
289
+ posterPart: string;
290
+ /** Native Office model orientation, in degrees around the X/Y/Z axes. */
291
+ rotation?: {
292
+ x: number;
293
+ y: number;
294
+ z: number;
295
+ };
296
+ }
297
+ interface WebVideoReference {
298
+ url: string;
299
+ embeddedHtml: string;
300
+ width: number;
301
+ height: number;
302
+ }
303
+ interface EmbeddedObjectReference {
304
+ part: string;
305
+ filename: string;
306
+ progId: string;
307
+ }
308
+ /** OMML equation node (subset: runs, scripts, fractions, radicals). */
309
+ type MathNode = {
310
+ t: "run";
311
+ text: string;
312
+ normal?: boolean;
313
+ /** m:func function name (cos, log, …): Word kerns a thin space on
314
+ * both sides of the name. */
315
+ fname?: boolean;
316
+ } | {
317
+ t: "sup" | "sub";
318
+ base: MathNode[];
319
+ script: MathNode[];
320
+ } | {
321
+ t: "frac";
322
+ num: MathNode[];
323
+ den: MathNode[];
324
+ bar?: boolean;
325
+ }
326
+ /** Radical; deg is the optional m:deg index (∛), absent when m:degHide. */
327
+ | {
328
+ t: "rad";
329
+ e: MathNode[];
330
+ deg?: MathNode[];
331
+ }
332
+ /** n-ary operator (sum/integral); chr defaults to the integral sign. */
333
+ | {
334
+ t: "nary";
335
+ chr: string;
336
+ sub: MathNode[];
337
+ sup: MathNode[];
338
+ e: MathNode[];
339
+ }
340
+ /** Delimiters grown to the content height; beg/end default to parens.
341
+ * An empty beg/end string means that side has no delimiter (cases "{"). */
342
+ | {
343
+ t: "dlm";
344
+ beg: string;
345
+ end: string;
346
+ e: MathNode[][];
347
+ }
348
+ /** Matrix: rows x cells. */
349
+ | {
350
+ t: "mat";
351
+ rows: MathNode[][][];
352
+ }
353
+ /** m:eqArr equation array (cases/piecewise rows). */
354
+ | {
355
+ t: "eqarr";
356
+ rows: MathNode[][];
357
+ }
358
+ /** m:acc combining accent (hat/bar/vector) over the base. */
359
+ | {
360
+ t: "acc";
361
+ chr: string;
362
+ e: MathNode[];
363
+ }
364
+ /** m:groupChr horizontal group character (over/under brace). */
365
+ | {
366
+ t: "grp";
367
+ chr: string;
368
+ pos: "top" | "bot";
369
+ vertJc: "top" | "bot";
370
+ e: MathNode[];
371
+ }
372
+ /** m:limLow / m:limUpp limit stacked under/over a text operator. */
373
+ | {
374
+ t: "lim";
375
+ pos: "low" | "upp";
376
+ e: MathNode[];
377
+ lim: MathNode[];
378
+ };
379
+ interface MathContent {
380
+ kind: "math";
381
+ nodes: MathNode[];
382
+ /** Source m:oMath element (math editing). */
383
+ src?: XmlElement;
384
+ /** Display equation (wrapped in m:oMathPara): centered on its own line with
385
+ * display-style layout - larger n-ary operators with limits stacked
386
+ * above/below, and full-size fraction numerators/denominators. */
387
+ display?: boolean;
388
+ /** Display group with several direct equations. Word leaves a small
389
+ * advance after script clusters in these grouped displays. */
390
+ multiEquationDisplay?: boolean;
391
+ /** m:oMathParaPr/m:jc - display-equation justification. Absent: the
392
+ * document default (settings m:mathPr/m:defJc, itself "centerGroup" when
393
+ * unset). */
394
+ jc?: "left" | "right" | "center" | "centerGroup";
395
+ }
396
+ interface FieldContent {
397
+ kind: "field";
398
+ /** Raw field instruction, e.g. ` PAGE \\* MERGEFORMAT `. */
399
+ instruction: string;
400
+ /** Last cached result text from the file, used for unsupported fields. */
401
+ cachedResult: string;
402
+ /** When this field is a legacy FORMCHECKBOX, the source `w:checkBox` element
403
+ * (in the fldChar begin's w:ffData). Its presence makes the rendered ballot
404
+ * box a click-to-toggle target; the glyph derives from its w:checked child. */
405
+ checkbox?: XmlElement;
406
+ }
407
+ /** What an anchored shape's coordinates are measured from. "char" (relH=
408
+ * "character") measures from the anchor run's pen position; "line" (relV=
409
+ * "line") from the top of the line holding the anchor — both resolved from
410
+ * the anchor paragraph's first-pass layout. */
411
+ type AnchorRel = "page" | "margin" | "text" | "column" | "char" | "line";
412
+ interface ShapeLine {
413
+ type: "line";
414
+ x1: number;
415
+ y1: number;
416
+ x2: number;
417
+ y2: number;
418
+ color: string;
419
+ /** Stroke weight px. */
420
+ weight: number;
421
+ style?: "single" | "dotted" | "dashed";
422
+ hRel: AnchorRel;
423
+ vRel: AnchorRel;
424
+ /** Source VML v:line for selection and save-back editing. */
425
+ src?: XmlElement;
426
+ }
427
+ interface ShapeTextbox {
428
+ type: "textbox";
429
+ x: number;
430
+ y: number;
431
+ width: number;
432
+ height: number;
433
+ hRel: AnchorRel;
434
+ vRel: AnchorRel;
435
+ blocks: Block[];
436
+ /** Background fill (CSS color) painted behind the text. */
437
+ fill?: string;
438
+ /** Outline. */
439
+ stroke?: {
440
+ color: string;
441
+ weight: number;
442
+ };
443
+ /** Alignment-based positioning (mso-position-horizontal/vertical). */
444
+ hAlign?: "left" | "center" | "right";
445
+ vAlign?: "top" | "center" | "bottom";
446
+ /** Percent-of-base geometry, 0..1 (mso-*-percent / wp14 pct offsets).
447
+ * Bases: pctWidthRel/pctHeightRel say page vs margin. */
448
+ pctX?: number;
449
+ pctY?: number;
450
+ pctWidth?: number;
451
+ pctHeight?: number;
452
+ pctWidthRel?: "page" | "margin";
453
+ pctHeightRel?: "page" | "margin";
454
+ /** Vertical anchoring of the text INSIDE the box (v-text-anchor). */
455
+ textAnchor?: "top" | "middle" | "bottom";
456
+ /** How body text flows around a DrawingML textbox (wp:wrap*). */
457
+ wrap?: WrapMode;
458
+ /** behindDoc: paint under the body text, never displace it. */
459
+ behind?: boolean;
460
+ /** wp:anchor @allowOverlap="0": Word shifts this shape clear of earlier
461
+ * overlapping floats rather than letting them overlap. Default true. */
462
+ allowOverlap?: boolean;
463
+ /** Wrap distances px (wp:anchor distT/B/L/R). */
464
+ dist?: {
465
+ t: number;
466
+ b: number;
467
+ l: number;
468
+ r: number;
469
+ };
470
+ /** a:xfrm rotation, degrees clockwise (rotates the whole box). */
471
+ rotation?: number;
472
+ /** Text insets px (bodyPr lIns/tIns/rIns/bIns); default 9.6/4.8. For
473
+ * non-rect preset geometries these INCLUDE the geometry's text-rectangle
474
+ * insets (ellipse: inscribed rect; diamond: middle-half rect). */
475
+ insets?: {
476
+ l: number;
477
+ t: number;
478
+ r: number;
479
+ b: number;
480
+ };
481
+ /** Non-rect preset geometry: paint this outline (in a viewW x viewH space
482
+ * scaled to the shape box) instead of a rectangle. */
483
+ geom?: {
484
+ d: string;
485
+ viewW: number;
486
+ viewH: number;
487
+ };
488
+ /** bodyPr a:noAutofit: the box does NOT grow with its text — Word hides
489
+ * whole lines that stick out past the shape bottom (phase23 p12 ovals:
490
+ * "Was 0 / B" visible, the wrapped tail rows hidden). */
491
+ clipText?: boolean;
492
+ /** bodyPr a:spAutoFit: the box grows in height to exactly fit its laid text
493
+ * plus the top/bottom insets (the stored cy is only Word's last cached
494
+ * value). Width is fixed. */
495
+ autofitHeight?: boolean;
496
+ /** Linked text-box chain id (wps:txbx/@id or wps:linkedTxbx/@id): boxes with
497
+ * the same id form one story. The seq-0 box (wps:txbx) holds the content;
498
+ * later boxes (wps:linkedTxbx, chainSeq>0) are empty sinks that continue the
499
+ * overflow. */
500
+ chainId?: string;
501
+ /** Position in the linked chain (0 = the content box). */
502
+ chainSeq?: number;
503
+ /** Source w:drawing element, attached at parse time so the shape's fill can
504
+ * be an interactive hit target (select the shape instead of the body text
505
+ * behind it). */
506
+ srcDrawing?: XmlElement;
507
+ /** Header/footer-owned textbox whose text is edited as an independent story. */
508
+ textboxStory?: boolean;
509
+ /** DrawingML WordArt, including the identity textNoShape preset. */
510
+ wordArt?: boolean;
511
+ /** WordArt glyph fill from w14:textFill or the fallback run color. */
512
+ wordArtFill?: string;
513
+ /** WordArt glyph alpha, 0..1. */
514
+ wordArtOpacity?: number;
515
+ /** bodyPr a:prstTxWarp preset name (textArchUp, textWave1, textChevron,
516
+ * textCirclePour, …): the shape's text is bent onto the preset's envelope
517
+ * rather than flowed as ordinary lines. "textNoShape" (no warp) is dropped. */
518
+ warp?: string;
519
+ }
520
+ /** WordArt (VML v:textpath, e.g. a "CONFIDENTIAL" watermark): text scaled to
521
+ * fill a box, optionally rotated, painted semi-transparent behind the body. */
522
+ interface ShapeWordArt {
523
+ type: "wordart";
524
+ text: string;
525
+ fontFamily: string;
526
+ bold?: boolean;
527
+ italic?: boolean;
528
+ /** CSS fill color. */
529
+ fill: string;
530
+ /** 0..1 alpha. */
531
+ opacity: number;
532
+ x: number;
533
+ y: number;
534
+ width: number;
535
+ height: number;
536
+ hRel: AnchorRel;
537
+ vRel: AnchorRel;
538
+ hAlign?: "left" | "center" | "right";
539
+ vAlign?: "top" | "center" | "bottom";
540
+ /** Clockwise degrees. */
541
+ rotation: number;
542
+ behind?: boolean;
543
+ /** The source v:textpath font-size (px). */
544
+ fontSize?: number;
545
+ /** The referenced v:shapetype has a malformed text-guide path (missing
546
+ * coordinates), so Word collapses its horizontal glyph outlines into a thin
547
+ * vertical band instead of painting a box-filling watermark. */
548
+ noFit?: boolean;
549
+ /** Source VML shape element (v:shape / v:rect) for interactive editing:
550
+ * the v:textpath string, fill/opacity, and rotation live under it. */
551
+ src?: XmlElement;
552
+ }
553
+ interface ShapeArt {
554
+ type: "art";
555
+ srcDrawing?: XmlElement;
556
+ /** A freehand stroke, selectable by the Draw ribbon's stroke eraser. */
557
+ ink?: boolean;
558
+ x: number;
559
+ y: number;
560
+ /** Percent-of-page offsets (wp14:pctPos*Offset), 0..1. */
561
+ pctX?: number;
562
+ pctY?: number;
563
+ width: number;
564
+ height: number;
565
+ hRel: AnchorRel;
566
+ vRel: AnchorRel;
567
+ hAlign?: "left" | "center" | "right";
568
+ vAlign?: "top" | "center" | "bottom";
569
+ behind?: boolean;
570
+ /** a:xfrm rotation, degrees clockwise. */
571
+ rotation?: number;
572
+ lines: DrawingLine[];
573
+ images: DrawingImage[];
574
+ paths: DrawingPath[];
575
+ /** Positioned text bodies inside the group (wps textboxes, dsp:sp txBody). */
576
+ texts?: DrawingTextShape[];
577
+ }
578
+ /** How text interacts with a floating image. */
579
+ type WrapMode = "square" | "topAndBottom" | "none";
580
+ interface ShapeImage {
581
+ type: "image";
582
+ part: string;
583
+ /** Offset from the anchor origin, px. */
584
+ x: number;
585
+ y: number;
586
+ width: number;
587
+ height: number;
588
+ hRel: AnchorRel;
589
+ vRel: AnchorRel;
590
+ /** Horizontal alignment when the file uses wp:align instead of an offset. */
591
+ hAlign?: "left" | "center" | "right";
592
+ /** Vertical alignment keyword (VML mso-position-vertical; watermarks center). */
593
+ vAlign?: "top" | "center" | "bottom";
594
+ wrap: WrapMode;
595
+ /** behindDoc anchors render under the text and never displace it. */
596
+ behind?: boolean;
597
+ /** Wrap distances px (wp:anchor distT/B/L/R); text clears the image by these. */
598
+ dist?: {
599
+ t: number;
600
+ b: number;
601
+ l: number;
602
+ r: number;
603
+ };
604
+ crop?: {
605
+ l: number;
606
+ t: number;
607
+ r: number;
608
+ b: number;
609
+ };
610
+ rotation?: number;
611
+ /** Watermark "washout" recolor (VML v:imagedata gain/blacklevel, 0..1). */
612
+ washout?: {
613
+ gain: number;
614
+ blacklevel: number;
615
+ };
616
+ /** Source w:drawing element (editing). */
617
+ srcDrawing?: XmlElement;
618
+ model3D?: Model3DReference;
619
+ webVideo?: WebVideoReference;
620
+ embeddedObject?: EmbeddedObjectReference;
621
+ }
622
+ type Shape = (ShapeLine | ShapeTextbox | ShapeImage | ShapeArt | ShapeWordArt) & {
623
+ /** wp:anchor relativeHeight, used for object stacking order. */
624
+ z?: number;
625
+ };
626
+ /**
627
+ * Floating/anchored object: does not occupy inline space; positioned against
628
+ * page/margin/paragraph during layout. (How classic pleading paper draws its
629
+ * margin line numbers and vertical rules.)
630
+ */
631
+ interface AnchorContent {
632
+ kind: "anchor";
633
+ shape: Shape;
634
+ }
635
+ /** A stroked segment inside a composite drawing, px relative to its box. */
636
+ interface DrawingLine {
637
+ x1: number;
638
+ y1: number;
639
+ x2: number;
640
+ y2: number;
641
+ color: string;
642
+ weight: number;
643
+ style?: "single" | "dotted" | "dashed";
644
+ }
645
+ interface DrawingImage {
646
+ part: string;
647
+ x: number;
648
+ y: number;
649
+ width: number;
650
+ height: number;
651
+ crop?: {
652
+ l: number;
653
+ t: number;
654
+ r: number;
655
+ b: number;
656
+ };
657
+ rotation?: number;
658
+ /** a:ln picture outline (Word draws it just outside the image). */
659
+ border?: {
660
+ color: string;
661
+ width: number;
662
+ };
663
+ }
664
+ /**
665
+ * Composite inline drawing (DrawingML groups): vector lines + placed images
666
+ * inside a width×height box that flows like an image.
667
+ */
668
+ interface DrawingContent {
669
+ kind: "drawing";
670
+ width: number;
671
+ height: number;
672
+ lines: DrawingLine[];
673
+ images: DrawingImage[];
674
+ /** Native ChartML chart data resolved from the related chart part. */
675
+ chart?: ChartData;
676
+ /** Native SmartArt diagram data resolved from its diagram parts. */
677
+ smartArt?: SmartArtData;
678
+ /** Freeform vector shapes (a:custGeom), as SVG path data. */
679
+ paths?: DrawingPath[];
680
+ /** An INLINE wps text box (wp:inline wps:txbx): a fixed-extent box that
681
+ * flows in the text (occupying its width x height like an inline image) and
682
+ * carries a fill/border + its own block content. Distinct from the floating
683
+ * ShapeTextbox, which is absolutely placed via a wp:anchor. */
684
+ textbox?: {
685
+ blocks: Block[];
686
+ fill?: string;
687
+ stroke?: {
688
+ color: string;
689
+ weight: number;
690
+ };
691
+ insets?: {
692
+ l: number;
693
+ t: number;
694
+ r: number;
695
+ b: number;
696
+ };
697
+ textAnchor?: "top" | "middle" | "bottom";
698
+ };
699
+ /** Source w:drawing element (select/move as a group). */
700
+ srcDrawing?: XmlElement;
701
+ /** Positioned text bodies inside the drawing (multi-textbox groups and
702
+ * SmartArt cached-drawing shapes). Painted above lines/paths. */
703
+ texts?: DrawingTextShape[];
704
+ }
705
+ type ChartType = "column" | "bar" | "line" | "pie";
706
+ interface ChartSeries {
707
+ name: string;
708
+ values: number[];
709
+ }
710
+ interface ChartData {
711
+ type: ChartType;
712
+ title?: string;
713
+ categories: string[];
714
+ series: ChartSeries[];
715
+ }
716
+ type SmartArtLayout = "process" | "cycle" | "hierarchy" | "list";
717
+ interface SmartArtData {
718
+ layout: SmartArtLayout;
719
+ items: string[];
720
+ }
721
+ /** A text body positioned inside a composite drawing (a wps textbox in a
722
+ * group, or a SmartArt dsp:sp txBody), px relative to the drawing's box. */
723
+ interface DrawingTextShape {
724
+ x: number;
725
+ y: number;
726
+ width: number;
727
+ height: number;
728
+ blocks: Block[];
729
+ /** Background fill painted behind the text box. */
730
+ fill?: string;
731
+ /** Outline. */
732
+ stroke?: {
733
+ color: string;
734
+ weight: number;
735
+ };
736
+ /** Text insets px (bodyPr lIns/tIns/rIns/bIns). */
737
+ insets: {
738
+ l: number;
739
+ t: number;
740
+ r: number;
741
+ b: number;
742
+ };
743
+ /** Vertical anchoring of the text inside the box (bodyPr anchor). */
744
+ textAnchor?: "top" | "middle" | "bottom";
745
+ /** DrawingML cached text paint-only baseline adjustment in px. */
746
+ paintOffsetY?: number;
747
+ }
748
+ interface DrawingPath {
749
+ /** Position/size inside the drawing, px. */
750
+ x: number;
751
+ y: number;
752
+ width: number;
753
+ height: number;
754
+ /** SVG path in the `viewW x viewH` source coordinate space. */
755
+ d: string;
756
+ viewW: number;
757
+ viewH: number;
758
+ fill?: string;
759
+ stroke?: {
760
+ color: string;
761
+ width: number;
762
+ opacity?: number;
763
+ };
764
+ /** Zero-based visible SmartArt node this path paints. */
765
+ smartArtNodeIndex?: number;
766
+ }
767
+ /**
768
+ * Footnote/endnote reference mark. In body text (w:footnoteReference /
769
+ * w:endnoteReference) `id` points into the notes part. Inside a note body,
770
+ * w:footnoteRef / w:endnoteRef render the note's own mark: `self` is true and
771
+ * `id` is meaningless.
772
+ */
773
+ interface NoteRefContent {
774
+ kind: "noteRef";
775
+ noteType: "footnote" | "endnote";
776
+ id: number;
777
+ self?: boolean;
778
+ /** The document supplies the visible mark in the following run content. */
779
+ customMarkFollows?: boolean;
780
+ }
781
+ /** w:ruby — an East-Asian ruby (furigana) cluster: an annotation run (w:rt)
782
+ * riding above a base run (w:rubyBase). Both sides render as their own runs;
783
+ * the annotation is centered over the base and raised above its baseline. */
784
+ interface RubyContent {
785
+ kind: "ruby";
786
+ base: Run;
787
+ rt: Run;
788
+ /** w:rubyPr/w:hpsRaise — annotation baseline raise above the base baseline,
789
+ * half-points. */
790
+ hpsRaise?: number;
791
+ /** w:rubyPr/w:rubyAlign — how the annotation distributes over the base. */
792
+ align?: "center" | "distributeLetter" | "distributeSpace" | "left" | "right" | "rightVertical";
793
+ }
794
+ type RunContent = MathContent | TextContent | BreakContent | TabContent | PTabContent | ImageContent | FieldContent | AnchorContent | DrawingContent | NoteRefContent | RubyContent;
795
+ interface Run {
796
+ type: "run";
797
+ props: RunProps;
798
+ content: RunContent[];
799
+ /** Source w:r element. */
800
+ src?: XmlElement;
801
+ /** Element whose children array contains src (w:p, w:hyperlink, …). */
802
+ srcParent?: XmlElement;
803
+ }
804
+ interface Hyperlink {
805
+ type: "hyperlink";
806
+ href?: string;
807
+ anchor?: string;
808
+ runs: Run[];
809
+ }
810
+ type ParaChild = Run | Hyperlink;
811
+ interface Paragraph {
812
+ type: "paragraph";
813
+ props: ParaProps;
814
+ children: ParaChild[];
815
+ /** Section break attached to this paragraph's pPr (ends a section). */
816
+ sectionBreak?: SectionProps;
817
+ /** w:bookmarkStart names in this paragraph (PAGEREF targets). */
818
+ bookmarks?: string[];
819
+ /** Final revision view: the paragraph mark AND all content are tracked
820
+ * deletions — the paragraph does not exist (no line, no numbering). */
821
+ revisionHidden?: boolean;
822
+ /** Source w:p element. */
823
+ src?: XmlElement;
824
+ }
825
+ interface TableCellProps {
826
+ /** Preferred width px (from tcW when dxa). */
827
+ width?: number;
828
+ /** The source explicitly declares tcW type="auto" (distinct from no tcW). */
829
+ widthAuto?: boolean;
830
+ gridSpan: number;
831
+ vMerge?: "restart" | "continue";
832
+ borders?: {
833
+ top?: Border;
834
+ bottom?: Border;
835
+ left?: Border;
836
+ right?: Border;
837
+ tl2br?: Border;
838
+ tr2bl?: Border;
839
+ };
840
+ shading?: string;
841
+ /** Cell margins px. */
842
+ margins?: {
843
+ top?: number;
844
+ right?: number;
845
+ bottom?: number;
846
+ left?: number;
847
+ };
848
+ verticalAlign?: "top" | "center" | "bottom";
849
+ textDirection?: "btLr" | "tbRl";
850
+ }
851
+ interface TableCell {
852
+ props: TableCellProps;
853
+ blocks: Block[];
854
+ }
855
+ interface TableRowProps {
856
+ /** px; undefined = auto */
857
+ height?: number;
858
+ heightRule?: "auto" | "atLeast" | "exact";
859
+ cantSplit?: boolean;
860
+ /** Repeat as header row on each page. */
861
+ tblHeader?: boolean;
862
+ }
863
+ interface TableRow {
864
+ props: TableRowProps;
865
+ cells: TableCell[];
866
+ }
867
+ interface TableProps {
868
+ styleId?: string;
869
+ /** px table indent from left margin. */
870
+ indent?: number;
871
+ alignment?: Alignment;
872
+ /** w:bidiVisual — RTL table: column order reverses and the table hugs the
873
+ * right margin. */
874
+ bidiVisual?: boolean;
875
+ borders?: {
876
+ top?: Border;
877
+ bottom?: Border;
878
+ left?: Border;
879
+ right?: Border;
880
+ insideH?: Border;
881
+ insideV?: Border;
882
+ };
883
+ /** Default cell margins px. */
884
+ cellMargins?: {
885
+ top?: number;
886
+ right?: number;
887
+ bottom?: number;
888
+ left?: number;
889
+ };
890
+ /** w:tblCellSpacing px — old-style separated cell borders: every cell gets
891
+ * its own border box inset by this much from the grid slot on all sides
892
+ * (adjacent cells end up 2x apart), and the table outline is a separate box
893
+ * around everything. */
894
+ cellSpacing?: number;
895
+ /** w:tblpPr — floating table: absolutely positioned against page/margin/
896
+ * column, painted over an opaque white sheet, with body text (including
897
+ * text EARLIER in the flow on the same page) wrapping square around it. */
898
+ floating?: {
899
+ hAnchor: "page" | "margin" | "text";
900
+ vAnchor: "page" | "margin" | "text";
901
+ /** tblpX/tblpY px (absent when an alignment keyword is used). */
902
+ x?: number;
903
+ y?: number;
904
+ xAlign?: "left" | "center" | "right";
905
+ yAlign?: "top" | "center" | "bottom";
906
+ /** Wrap distances px (leftFromText, …). */
907
+ dist: {
908
+ l: number;
909
+ r: number;
910
+ t: number;
911
+ b: number;
912
+ };
913
+ /** w:tblOverlap "never": shift clear of earlier floating tables. */
914
+ allowOverlap: boolean;
915
+ };
916
+ /** Preferred total width px (tblW dxa) or undefined for auto. */
917
+ width?: number;
918
+ /** Preferred total width as a fraction of available width (tblW pct). */
919
+ widthPct?: number;
920
+ layout?: "fixed" | "autofit";
921
+ /** w:tblLook flags controlling which conditional formats apply. */
922
+ tblLook?: TableLook;
923
+ }
924
+ /** w:tblLook flags controlling which conditional table-style formats apply. */
925
+ interface TableLook {
926
+ firstRow: boolean;
927
+ lastRow: boolean;
928
+ firstColumn: boolean;
929
+ lastColumn: boolean;
930
+ noHBand: boolean;
931
+ noVBand: boolean;
932
+ }
933
+ interface Table {
934
+ type: "table";
935
+ props: TableProps;
936
+ /** Column grid widths px (tblGrid). */
937
+ grid: number[];
938
+ rows: TableRow[];
939
+ /** Source w:tbl element. */
940
+ src?: XmlElement;
941
+ }
942
+ type Block = Paragraph | Table;
943
+ interface HeaderFooterRefs {
944
+ default?: string;
945
+ first?: string;
946
+ even?: string;
947
+ }
948
+ interface ColumnSpec {
949
+ count: number;
950
+ /** Space between columns px. */
951
+ space: number;
952
+ /** Explicit widths px when equalWidth=false. */
953
+ widths?: number[];
954
+ /** Per-column trailing space px (w:col w:space), when explicit w:col
955
+ * entries are present. spaces[i] separates column i from column i+1. */
956
+ spaces?: number[];
957
+ /** w:cols w:sep: paint a vertical rule centered in each inter-column gap. */
958
+ sep?: boolean;
959
+ }
960
+ interface SectionProps {
961
+ pageWidth: number;
962
+ pageHeight: number;
963
+ marginTop: number;
964
+ marginRight: number;
965
+ marginBottom: number;
966
+ marginLeft: number;
967
+ /** Distance of header top from page top, px (w:headerReference distance). */
968
+ headerDistance: number;
969
+ footerDistance: number;
970
+ gutter: number;
971
+ headerRefs: HeaderFooterRefs;
972
+ footerRefs: HeaderFooterRefs;
973
+ /** Different first-page header/footer enabled. */
974
+ titlePage: boolean;
975
+ /** Page numbering restart value, if set. */
976
+ pageNumberStart?: number;
977
+ pageNumberFormat?: string;
978
+ columns: ColumnSpec;
979
+ /** w:docGrid type=lines/linesAndChars: minimum single-line height (px) that
980
+ * the line-spacing multiplier is applied over. Word snaps every line's font
981
+ * height up to this grid pitch (CJK documents). */
982
+ docGridLinePitch?: number;
983
+ /** Present w:docGrid type; omitted w:type means "default". */
984
+ docGridType?: "default" | "lines" | "linesAndChars" | "snapToChars";
985
+ /** w:docGrid type="charsAndLines": a combined char+line grid that (in compat
986
+ * 15) keeps natural East-Asian line pitch. Drives the CJK line-height
987
+ * correction during measurement. */
988
+ docGridCharGrid?: boolean;
989
+ type?: "nextPage" | "continuous" | "evenPage" | "oddPage" | "nextColumn";
990
+ /** Vertical alignment of page content. */
991
+ vAlign?: "top" | "center" | "both" | "bottom";
992
+ /** w:pgBorders. Offsets (border.space, px) measure from text or page edge. */
993
+ pageBorders?: {
994
+ top?: Border;
995
+ bottom?: Border;
996
+ left?: Border;
997
+ right?: Border;
998
+ offsetFrom: "text" | "page";
999
+ };
1000
+ /** w:lnNumType: margin line numbering. distance px from the text edge. */
1001
+ lineNumbering?: {
1002
+ countBy: number;
1003
+ start: number;
1004
+ distance: number;
1005
+ restart: "continuous" | "newPage" | "newSection";
1006
+ };
1007
+ /** Footnote/endnote mark numbering (w:footnotePr / w:endnotePr). */
1008
+ footnoteNumFmt?: string;
1009
+ footnoteNumStart?: number;
1010
+ endnoteNumFmt?: string;
1011
+ endnoteNumStart?: number;
1012
+ /** w:textDirection tbRl: the whole section flows as East-Asian vertical
1013
+ * writing — lines run top-to-bottom, progressing right-to-left. */
1014
+ textDirection?: "tbRl";
1015
+ }
1016
+ interface Section {
1017
+ props: SectionProps;
1018
+ blocks: Block[];
1019
+ }
1020
+ /** A review comment from word/comments.xml. */
1021
+ interface DocComment {
1022
+ id: string;
1023
+ author: string;
1024
+ initials?: string;
1025
+ /** ISO timestamp from w:date, verbatim. */
1026
+ date?: string;
1027
+ /** Plain text of the comment body (paragraphs joined with newlines). */
1028
+ text: string;
1029
+ /** w14:paraId of the comment's last body paragraph (threading key). */
1030
+ paraId?: string;
1031
+ /** Parent comment id when this comment is a reply (commentsExtended). */
1032
+ parentId?: string;
1033
+ }
1034
+ interface HeaderFooter {
1035
+ blocks: Block[];
1036
+ }
1037
+ /** One w:tblStylePr conditional-formatting block (firstRow, band1Horz, …). */
1038
+ interface TableCondFormat {
1039
+ /** Cell shading fill as CSS color. */
1040
+ shd?: string;
1041
+ /** Conditional cell borders. */
1042
+ borders?: {
1043
+ top?: Border;
1044
+ bottom?: Border;
1045
+ left?: Border;
1046
+ right?: Border;
1047
+ insideH?: Border;
1048
+ insideV?: Border;
1049
+ };
1050
+ /** Conditional run bold (firstRow/firstCol headers). */
1051
+ bold?: boolean;
1052
+ /** Full conditional run props (w:tblStylePr > w:rPr). */
1053
+ rPr?: RunProps;
1054
+ }
1055
+ type TableCondType = "wholeTable" | "band1Vert" | "band2Vert" | "band1Horz" | "band2Horz" | "firstRow" | "lastRow" | "firstCol" | "lastCol" | "nwCell" | "neCell" | "swCell" | "seCell";
1056
+ interface Style {
1057
+ id: string;
1058
+ type: "paragraph" | "character" | "table" | "numbering";
1059
+ name?: string;
1060
+ basedOn?: string;
1061
+ isDefault?: boolean;
1062
+ pPr?: ParaProps;
1063
+ rPr?: RunProps;
1064
+ tblPr?: TableProps;
1065
+ /** w:tblStylePr conditional formats, by type (table styles only). */
1066
+ condFormats?: Map<TableCondType, TableCondFormat>;
1067
+ /** w:tblStyleRowBandSize / ColBandSize (default 1). */
1068
+ rowBandSize?: number;
1069
+ colBandSize?: number;
1070
+ }
1071
+ interface Styles {
1072
+ byId: Map<string, Style>;
1073
+ defaultParagraphStyle?: string;
1074
+ defaultRPr: RunProps;
1075
+ defaultPPr: ParaProps;
1076
+ }
1077
+ interface NumberingLevel {
1078
+ ilvl: number;
1079
+ start: number;
1080
+ format: string;
1081
+ /** Template like "%1." */
1082
+ text: string;
1083
+ alignment: Alignment;
1084
+ /** Paragraph props contributed by the level (indents). */
1085
+ pPr?: ParaProps;
1086
+ rPr?: RunProps;
1087
+ suffix: "tab" | "space" | "nothing";
1088
+ restartAfter?: number;
1089
+ }
1090
+ interface AbstractNum {
1091
+ id: number;
1092
+ levels: Map<number, NumberingLevel>;
1093
+ numStyleLink?: string;
1094
+ }
1095
+ interface NumInstance {
1096
+ numId: number;
1097
+ abstractNumId: number;
1098
+ overrides: Map<number, {
1099
+ startOverride?: number;
1100
+ level?: NumberingLevel;
1101
+ }>;
1102
+ }
1103
+ interface Numbering {
1104
+ abstract: Map<number, AbstractNum>;
1105
+ instances: Map<number, NumInstance>;
1106
+ }
1107
+ interface Theme {
1108
+ majorFont: string;
1109
+ minorFont: string;
1110
+ majorBidiFont?: string;
1111
+ minorBidiFont?: string;
1112
+ /** East Asian faces from theme <a:ea> (fontScheme major/minorFont). Word's
1113
+ * eastAsiaTheme="minor/majorEastAsia" resolves through these, NOT the Latin
1114
+ * minor/major font — routing a CJK run's theme reference to the Latin face
1115
+ * (Calibri) leaves East Asian text in a glyphless Latin font. */
1116
+ majorEastAsiaFont?: string;
1117
+ minorEastAsiaFont?: string;
1118
+ colors: Map<string, string>;
1119
+ }
1120
+
1121
+ /** A read-only view over the OPC (zip) package inside a .docx file. */
1122
+ declare class Package {
1123
+ private files;
1124
+ constructor(data: Uint8Array);
1125
+ static from(data: ArrayBuffer | Uint8Array): Package;
1126
+ has(name: string): boolean;
1127
+ binary(name: string): Uint8Array | undefined;
1128
+ text(name: string): string | undefined;
1129
+ names(): string[];
1130
+ /** Raw entry map (shared, do not mutate) — used for write-back. */
1131
+ raw(): Record<string, Uint8Array>;
1132
+ /** OPC part names never start with '/' inside the zip; tolerate both. */
1133
+ private normalize;
1134
+ }
1135
+
1136
+ /**
1137
+ * Stable node identity for replicated editing.
1138
+ *
1139
+ * Positions on the wire cannot be object references, and numeric child paths
1140
+ * shift under concurrent structural edits — so blocks (w:p, w:tbl) and runs
1141
+ * (w:r) get stable numeric ids held in an in-memory side table. The table is
1142
+ * identity-keyed and NEVER serialized into the XML: writing ids as attributes
1143
+ * would dirty every part on open and break the byte-identical round-trip
1144
+ * guarantee for untouched parts.
1145
+ *
1146
+ * Assignment is deterministic: a document-order walk over the given roots,
1147
+ * so two replicas that parse the same document derive the same table. Nodes
1148
+ * created later by edits get ids explicitly (locally via `assign`, or from a
1149
+ * replicated edit that carries the originating client's allocation) — a
1150
+ * re-walk after edits only fills gaps and never renumbers survivors, because
1151
+ * the table is keyed by element identity and in-place XML mutation preserves
1152
+ * identity across `refresh()`.
1153
+ */
1154
+ type StableId$1 = number;
1155
+ interface EncodedCaret {
1156
+ blockId: StableId$1;
1157
+ runId: StableId$1;
1158
+ /** Offset in the run's WIRE space: cumulative through the run's inline
1159
+ * content in document order, where each w:t contributes its text length
1160
+ * and each inline separator (w:tab / w:br / w:cr) contributes ONE unit.
1161
+ * Counting separators makes encoding ONE-TO-ONE: a caret at the end of
1162
+ * the w:t before a tab and one at the start of the w:t after it are
1163
+ * different physical positions and get different wire offsets — without
1164
+ * the separator unit both collapsed to the same number and the decoder
1165
+ * had to guess (review bug: boundary intents applied into the wrong w:t
1166
+ * on remote replicas, and boundary-starting ranges always rejected). */
1167
+ offset: number;
1168
+ }
1169
+ declare class StableIds {
1170
+ private byEl;
1171
+ private byId;
1172
+ private next;
1173
+ /** Document-order assignment over the given roots (typically
1174
+ * `doc.editableRoots()`). Idempotent: elements that already have ids keep
1175
+ * them; untracked ids are assigned in walk order. Call after load and
1176
+ * after any edit that created tracked nodes without explicit ids. */
1177
+ assignFromRoots(roots: XmlElement[]): void;
1178
+ /**
1179
+ * Assign ids inside the given subtrees only — the scoped counterpart of
1180
+ * assignFromRoots, for an edit that is known to have created nodes in one
1181
+ * or two paragraphs (dirty-scoped reconciliation, perf B9/B10).
1182
+ *
1183
+ * EQUIVALENCE (the property the scoped hot path rests on): this produces
1184
+ * the same table a full assignFromRoots would, PROVIDED every tracked node
1185
+ * outside the subtrees already has an id. Assignment is order-dependent —
1186
+ * fresh nodes take sequential numbers from `next` in walk order — and a
1187
+ * subtree walk visits exactly a contiguous slice of the document-order
1188
+ * walk, in the same order. So the only way the two can disagree is an
1189
+ * un-id'd node elsewhere that the full walk would have numbered first.
1190
+ * That invariant holds inductively: load/importSidecar ids everything, and
1191
+ * every apply either ids the nodes it creates (carried ids), reports
1192
+ * document scope (full refresh + full assign), or creates nodes only
1193
+ * inside the subtrees passed here. Callers that cannot establish it must
1194
+ * fall back to assignFromRoots.
1195
+ */
1196
+ assignFromSubtrees(subtrees: XmlElement[]): void;
1197
+ private walk;
1198
+ /** Explicitly assign an id to a newly created node. Local edits allocate
1199
+ * (`assign(el)`); applying a replicated edit installs the carried value
1200
+ * (`assign(el, carriedId)`). Carried ids must not collide. */
1201
+ assign(el: XmlElement, id?: StableId$1): StableId$1;
1202
+ /** Force `el` to carry `id`, replacing any id it already has (e.g. one
1203
+ * auto-assigned by a refresh() before a carried id could be installed).
1204
+ * The previous id is retired. Throws if `id` is held by a different element. */
1205
+ reassign(el: XmlElement, id: StableId$1): void;
1206
+ private install;
1207
+ idOf(el: XmlElement): StableId$1 | undefined;
1208
+ elOf(id: StableId$1): XmlElement | undefined;
1209
+ /** Drop mappings for elements no longer reachable from the given roots
1210
+ * (deleted content). Keeps the table from growing across long sessions.
1211
+ * Ids of dropped elements are retired, never reused. */
1212
+ prune(roots: XmlElement[]): void;
1213
+ size(): number;
1214
+ /** Snapshot the table as [id, element] pairs against a parallel clone of
1215
+ * the tree: `pairs` maps originals→clones (as produced by walking original
1216
+ * and cloned roots in step). Used by the reconciliation snapshot (plan
1217
+ * doc 03) so a restore re-keys the table to the restored elements. */
1218
+ captureForClone(originalToClone: Map<XmlElement, XmlElement>): Map<StableId$1, XmlElement>;
1219
+ /** Replace the table wholesale from a captured mapping (restore path). */
1220
+ restore(fromCapture: Map<StableId$1, XmlElement>, nextId: StableId$1): void;
1221
+ nextId(): StableId$1;
1222
+ /**
1223
+ * Export the table as `(id, path)` pairs, where a path is the structural
1224
+ * location of the id'd element within the given roots: `[rootIndex,
1225
+ * childIndex, childIndex, …]`. This is the checkpoint ID sidecar (plan doc
1226
+ * 03): it lets a replica that reloads a snapshot reproduce the exact id
1227
+ * table — parse-order re-derivation cannot, because split-created nodes
1228
+ * carry non-sequential ids. Also carries `next` so id allocation resumes
1229
+ * consistently.
1230
+ */
1231
+ exportSidecar(roots: XmlElement[]): {
1232
+ next: StableId$1;
1233
+ entries: [StableId$1, number[]][];
1234
+ };
1235
+ /** Rebuild the table from a sidecar against freshly parsed roots (the
1236
+ * snapshot the sidecar was exported from). Replaces any current mappings. */
1237
+ importSidecar(roots: XmlElement[], sidecar: {
1238
+ next: StableId$1;
1239
+ entries: [StableId$1, number[]][];
1240
+ }): void;
1241
+ /** Encode a caret as wire-stable addresses. `t` is the w:t (or other text
1242
+ * holder) the caret sits in; the run is its nearest tracked ancestor run,
1243
+ * the block the nearest tracked block above that. Returns null when the
1244
+ * position isn't inside id-tracked content (e.g. math internals) — such
1245
+ * positions are not yet addressable on the wire. */
1246
+ encodeCaret(t: XmlElement, offset: number, parentOf: (el: XmlElement) => XmlElement | null): EncodedCaret | null;
1247
+ /** Resolve an encoded caret back to a concrete (w:t, offset) in THIS
1248
+ * table's tree — the inverse of encodeCaret, for restoring a caret across
1249
+ * a reconciliation reload (the ids survive via the sidecar; the node
1250
+ * references do not). When the exact run is gone (deleted or replaced by
1251
+ * a concurrent edit) the caret falls back to the block's first text; null
1252
+ * when nothing addressable remains. */
1253
+ decodeCaret(pos: EncodedCaret): {
1254
+ t: XmlElement;
1255
+ offset: number;
1256
+ } | null;
1257
+ }
1258
+
1259
+ interface Relationship {
1260
+ id: string;
1261
+ type: string;
1262
+ target: string;
1263
+ external: boolean;
1264
+ }
1265
+ type Relationships = Map<string, Relationship>;
1266
+
1267
+ /**
1268
+ * A fully parsed .docx: sections of blocks, styles, numbering, theme, and
1269
+ * header/footer parts, with helpers to resolve effective formatting.
1270
+ */
1271
+ declare class DocxDocument {
1272
+ /** Changes whenever refresh() rebuilds the parsed model. Plain in-place text
1273
+ * edits can keep this stable so incremental layout reuses model-only caches. */
1274
+ private _modelVersion;
1275
+ private _packageResourceVersion;
1276
+ get modelVersion(): number;
1277
+ /** Invalidate layout derived from related parts such as ChartML. */
1278
+ markPackageResourceChanged(): void;
1279
+ readonly pkg: Package;
1280
+ readonly theme: Theme;
1281
+ styles: Styles;
1282
+ numbering: Numbering;
1283
+ sections: Section[];
1284
+ /** Header/footer parts keyed by relationship id from document.xml.rels. */
1285
+ readonly headers: Map<string, HeaderFooter>;
1286
+ readonly footers: Map<string, HeaderFooter>;
1287
+ /** Note content by note id (render-only; sources stripped). */
1288
+ readonly footnotes: Map<number, Block[]>;
1289
+ readonly endnotes: Map<number, Block[]>;
1290
+ /** The separator paragraph controls the gap between its rule and the first
1291
+ * footnote. */
1292
+ readonly footnoteSeparator: Block[];
1293
+ /** `_Ref` cross-reference bookmark ranges (name → captured runs). REF
1294
+ * fields re-render the referenced text from these — Word recomputes REF on
1295
+ * open, so the cached field result in the file is stale. */
1296
+ refBookmarks: Map<string, Run[]>;
1297
+ readonly documentRels: Relationships;
1298
+ /** settings.xml w:evenAndOddHeaders — enables the "even" header/footer variants. */
1299
+ readonly evenAndOddHeaders: boolean;
1300
+ /** settings.xml w:mirrorMargins — facing-page (book fold) margins: even
1301
+ * (verso) pages swap the left/right margins and place the gutter on the
1302
+ * inside (right) edge so the binding margin stays on the inner side of
1303
+ * each spread. */
1304
+ readonly mirrorMargins: boolean;
1305
+ /** settings.xml w:defaultTabStop in px (Word default 0.5"). */
1306
+ readonly defaultTabStop: number;
1307
+ /** settings.xml w:compat compatibilityMode (12=Word2007, 14=Word2010,
1308
+ * 15=Word2013+). Word 2013 (mode 15) introduced suppressing a paragraph's
1309
+ * space-before when it lands at the top of a page; mode 14 and earlier keep
1310
+ * it (nccih: a Heading1/2 after a page break sits at margin + its before).
1311
+ * Absent → treated as current (15). */
1312
+ readonly compatibilityMode: number;
1313
+ /** settings.xml m:mathPr/m:defJc — default justification for display
1314
+ * equations whose m:oMathParaPr carries no explicit m:jc (Word default:
1315
+ * centerGroup — the rows of a broken equation left-align to each other and
1316
+ * the group is centered in the column). */
1317
+ readonly mathDefJc: "left" | "right" | "center" | "centerGroup";
1318
+ /** settings.xml m:mathPr/m:wrapIndent in px (Word default 1440tw = 1"):
1319
+ * indent of auto-wrapped display-equation continuation rows from the
1320
+ * equation group's left edge (dense p13: the "+Dc(...)" continuations sit
1321
+ * exactly 72pt right of the explicit rows). */
1322
+ readonly mathWrapIndent: number;
1323
+ /** Review comments from word/comments.xml (empty when the part is absent).
1324
+ * Re-derived from the retained comments XML on every refresh(). */
1325
+ comments: DocComment[];
1326
+ /** Retained comments.xml tree (editing + save round-trip), when present. */
1327
+ private commentsPart;
1328
+ private commentsRoot;
1329
+ /** Retained commentsExtended.xml tree (comment threading), when present. */
1330
+ private commentsExtPart;
1331
+ private commentsExtRoot;
1332
+ private commentsExtDirty;
1333
+ /** Conditional table formats per table style id, keyed by the Styles object
1334
+ * so re-parsing styles.xml (edits) naturally invalidates the cache. */
1335
+ private tableCondCache;
1336
+ /** Retained styles.xml tree (built-in style injection + save). */
1337
+ private stylesPart;
1338
+ private stylesRoot;
1339
+ /** Retained numbering.xml tree (list creation + save round-trip). */
1340
+ private numberingPart;
1341
+ private numberingRoot;
1342
+ private numberingDirty;
1343
+ /** Retained footnotes.xml tree (footnote insertion + save round-trip). */
1344
+ private footnotesPart;
1345
+ private footnotesRoot;
1346
+ private footnotesDirty;
1347
+ private footnotesRels;
1348
+ /** Serialize retained optional parts only once actually mutated, keeping
1349
+ * untouched parts byte-identical through save(). */
1350
+ private stylesDirty;
1351
+ private commentsDirty;
1352
+ /** Retained XML roots — source of truth for editing and save(). */
1353
+ private readonly docPart;
1354
+ /** Retained settings.xml tree. A synthetic empty root keeps history root
1355
+ * indices stable for documents that did not originally contain the part. */
1356
+ private readonly settingsPart;
1357
+ private readonly settingsRoot;
1358
+ private settingsDirty;
1359
+ /** Parsed document.xml root (read-only outside the class; the layout engine
1360
+ * scans it for incremental-reuse eligibility, tests walk it). */
1361
+ readonly docRoot: XmlElement;
1362
+ private readonly hfParts;
1363
+ private readonly ctxBase;
1364
+ /** Tracked-changes display mode; refresh() re-derives after changes. */
1365
+ revisionView: "final" | "markup";
1366
+ private readonly relsPath;
1367
+ private relsRoot;
1368
+ private contentTypesRoot;
1369
+ /** Canonical XML as first parsed from each always-modeled package part.
1370
+ * If the retained tree still matches on save, keep the part's original
1371
+ * bytes instead of replacing producer formatting such as CRLF line ends. */
1372
+ private readonly originalModeledXml;
1373
+ private docPrIdCounter;
1374
+ /** Transient layout state: set by the engine while laying out a docGrid
1375
+ * type="charsAndLines" section so line measurement can give East-Asian
1376
+ * glyphs their true (uninflated) grid line height. Off outside such a
1377
+ * section. Safe as document-scoped mutable state because layout is
1378
+ * single-threaded and sequential per section. */
1379
+ charGridEa: boolean;
1380
+ private constructor();
1381
+ /** Resolve content controls mapped to standard package core properties.
1382
+ * Word refreshes these bindings on open, so the serialized sdtContent can
1383
+ * be stale even though the visible value comes from docProps/core.xml. */
1384
+ private hydrateCorePropertyControls;
1385
+ /** Repair only objects emitted by older WordInWeb builds that Word rejects. */
1386
+ private repairLegacyWordInWebObjects;
1387
+ /**
1388
+ * Re-derive the document model from the retained XML trees. Called after
1389
+ * edit commands mutate the XML.
1390
+ */
1391
+ /** Switch tracked-changes display and re-derive the model. */
1392
+ setRevisionView(view: "final" | "markup"): void;
1393
+ /** Invalidated on refresh; see layoutGlobalSig. */
1394
+ private _layoutGlobalSig;
1395
+ /** Signature of everything OUTSIDE a paragraph's own XML that affects how it
1396
+ * breaks into lines: style + numbering definitions, doc-level layout scalars,
1397
+ * and the tracked-changes view mode. The line-break cache (layout/inline.ts)
1398
+ * combines this with a paragraph's own content signature so a style/numbering/
1399
+ * settings edit invalidates cached breaks even though the paragraph XML is
1400
+ * unchanged. Memoized until the next refresh() (styles/numbering trees are
1401
+ * stable across a plain text edit). */
1402
+ layoutGlobalSig(): string;
1403
+ refresh(): void;
1404
+ /** Stable node-id side table for replicated editing. Null (and zero cost)
1405
+ * for local-only documents; call `enableStableIds()` to populate and
1406
+ * maintain it. Kept in memory only — never serialized into the XML. */
1407
+ stableIds: StableIds | null;
1408
+ /** Populate the stable-id table from current content and keep it updated
1409
+ * on every subsequent refresh(). Idempotent. */
1410
+ enableStableIds(): StableIds;
1411
+ /** Reparse the two sibling body-story paragraphs created by Enter without
1412
+ * rebuilding the complete document model. Paragraphs nested in table cells
1413
+ * are included because legal documents spend most of their body inside
1414
+ * tables; revisions, bookmarks, fields, and section breaks use refresh(). */
1415
+ reparseDirectBodyParagraphSplit(beforeSource: XmlElement, afterSource: XmlElement): {
1416
+ before: Paragraph;
1417
+ after: Paragraph;
1418
+ } | null;
1419
+ /**
1420
+ * The parsed body-story block list holding the paragraph parsed from
1421
+ * `source`, and its index in it — searching sections and, recursively,
1422
+ * table cells. Null when the model doesn't hold that paragraph (it lives in
1423
+ * a header/footer/footnote part, or the model predates its creation), which
1424
+ * is every targeted-reparse helper's signal to fall back to a full
1425
+ * refresh(). Shared by those helpers and by paragraphBySource.
1426
+ */
1427
+ private locateParagraph;
1428
+ private _paraLoc;
1429
+ /** The parsed Paragraph for a retained w:p in the body story (table cells
1430
+ * included), or null when the model doesn't hold it. Lets a caller that
1431
+ * already knows which paragraph an edit addresses reach that paragraph's
1432
+ * runs without walking the whole model. */
1433
+ paragraphBySource(source: XmlElement): Paragraph | null;
1434
+ /** Insert a new paragraph immediately before a retained body paragraph
1435
+ * without rebuilding the complete document model. Used by Enter at the
1436
+ * exact paragraph start, where the existing paragraph itself is unchanged. */
1437
+ insertDirectBodyParagraphBefore(referenceSource: XmlElement, insertedSource: XmlElement): Paragraph | null;
1438
+ /** Reparse a body paragraph plus several new siblings created by
1439
+ * click-and-type without rebuilding the complete document model. */
1440
+ reparseDirectBodyParagraphSplits(beforeSource: XmlElement, afterSources: XmlElement[]): Paragraph[] | null;
1441
+ /** Reparse one retained body-story paragraph after a structural edit that
1442
+ * leaves its surrounding block list unchanged, such as inserting ink. */
1443
+ reparseBodyParagraph(source: XmlElement): Paragraph | null;
1444
+ /** Reparse two sibling body-story paragraphs after Backspace/Delete merged
1445
+ * their XML into one. Keeps the parsed model generation stable so long
1446
+ * documents can use incremental layout instead of repaginating in full. */
1447
+ reparseDirectBodyParagraphMerge(beforeSource: XmlElement, afterSource: XmlElement, survivorSource: XmlElement): Paragraph | null;
1448
+ private deriveComments;
1449
+ /** Retained comments tree for edit commands (null when the doc has none). */
1450
+ /**
1451
+ * Retained comments tree. With create=true, a missing comments.xml part is
1452
+ * created and registered (content type + document relationship) so newly
1453
+ * added comments serialize and round-trip through Word.
1454
+ */
1455
+ commentsTree(create?: boolean): XmlElement | null;
1456
+ /**
1457
+ * Make sure a paragraph style is usable: Word ships built-in definitions
1458
+ * for Heading 1-6/Title even when a file doesn't declare them, so applying
1459
+ * one to such a file must inject a standard definition (otherwise the
1460
+ * paragraph would reference an undefined style and render as Normal).
1461
+ */
1462
+ ensureParagraphStyle(styleId: string): boolean;
1463
+ /**
1464
+ * Retained numbering tree. With create=true, a missing numbering.xml part
1465
+ * is created and registered (content type + document relationship) so list
1466
+ * definitions added by editing serialize and round-trip.
1467
+ */
1468
+ numberingTree(create?: boolean): XmlElement | null;
1469
+ markNumberingChanged(): void;
1470
+ /**
1471
+ * Retained footnotes tree. With create=true, a missing footnotes.xml part
1472
+ * is created and registered (with Word's required separator footnotes) so
1473
+ * inserted footnotes serialize and round-trip.
1474
+ */
1475
+ footnotesTree(create?: boolean): XmlElement | null;
1476
+ /**
1477
+ * Create an empty header/footer part (with a default-type reference in
1478
+ * every sectPr) when the document has none - Word does this implicitly the
1479
+ * first time you edit the header area. Returns the part's root.
1480
+ */
1481
+ /** Whether the document declares any section properties at all. A blank
1482
+ * minimal document has none; see ensureHfPart. */
1483
+ private hasSectPr;
1484
+ /** Whether a header (or footer) part already exists — the precondition
1485
+ * `ensureHfPart` tests internally, exposed so a caller can tell a real
1486
+ * creation from a no-op before mutating (the collab apply needs to know
1487
+ * whether an ensureHeaderFooter intent has anything to do). */
1488
+ hasHfPart(kind: "header" | "footer"): boolean;
1489
+ ensureHfPart(kind: "header" | "footer"): XmlElement;
1490
+ markFootnotesChanged(): void;
1491
+ /** Called by comment edit commands after mutating the comments tree. */
1492
+ markCommentsChanged(): void;
1493
+ /**
1494
+ * Retained commentsExtended tree (threading). With create=true, a missing
1495
+ * part is created and registered (content type + document relationship) so
1496
+ * Word picks up reply threading.
1497
+ */
1498
+ commentsExtendedTree(create?: boolean): XmlElement | null;
1499
+ markCommentsExtendedChanged(): void;
1500
+ /**
1501
+ * The w:t elements covered by each comment's range, in document order.
1502
+ * Point comments (a bare commentReference with no range) anchor to the
1503
+ * nearest preceding w:t.
1504
+ */
1505
+ commentAnchors(): Map<string, XmlElement[]>;
1506
+ /** Flag the footnotes part dirty when `t` lives inside it, so save()
1507
+ * re-serializes footnotes.xml. Called by the editor after a text edit; a
1508
+ * no-op for body/header/footer targets. */
1509
+ markDirtyIfFootnote(t: XmlElement): void;
1510
+ /** The mutable XML roots (document body, related modeled parts, settings).
1511
+ * settingsRoot is always second and always present so its history snapshot
1512
+ * index stays stable even when optional related roots are created later. */
1513
+ editableRoots(): XmlElement[];
1514
+ /** Toggle the document-global facing-page margin mode in settings.xml. */
1515
+ setMirrorMargins(enabled: boolean): void;
1516
+ /**
1517
+ * Find the parent element of `target` in any modeled XML tree (document
1518
+ * body, headers, footers). Linear scan — documents are small and this only
1519
+ * runs on structural edits (Enter, paragraph merge).
1520
+ */
1521
+ /** XML roots that can carry tracked changes: body, headers/footers, footnotes. */
1522
+ revisionRoots(): XmlElement[];
1523
+ /**
1524
+ * Memoized child→parent links. Nothing invalidates this map — the tree is
1525
+ * spliced constantly — so every answer is RE-DERIVED before it is returned:
1526
+ * the memoized parent must still list the target among its children, and
1527
+ * must still hang off a root (see memoIsLive). A stale entry can then only
1528
+ * cost a cache miss, never a wrong answer.
1529
+ *
1530
+ * Both halves are needed. Containment alone is not proof of a live parent:
1531
+ * a run split for formatting is spliced OUT of its paragraph while its own
1532
+ * `children` array still lists the elements that moved into the replacement
1533
+ * runs, so a containment-only check would hand back the detached run where a
1534
+ * walk from the roots finds the new one.
1535
+ */
1536
+ private _parentMemo;
1537
+ private memoIsLive;
1538
+ findParentOf(target: XmlElement): XmlElement | undefined;
1539
+ /** Record a parent link the caller just created (a splice), so the next
1540
+ * lookup skips the full-document walk. Advisory only — findParentOf
1541
+ * verifies every memo against the live tree before trusting it. */
1542
+ noteParent(child: XmlElement, parent: XmlElement): void;
1543
+ /**
1544
+ * Serialize the (possibly edited) document back to .docx bytes. Only the
1545
+ * XML parts we model are re-serialized; every other part round-trips
1546
+ * byte-for-byte.
1547
+ */
1548
+ private rememberOriginalXml;
1549
+ private writeModeledXml;
1550
+ /**
1551
+ * Canonicalize producer shorthand that Google Docs otherwise interprets as
1552
+ * a fixed, few-twip table. Word treats `tblW="100%"` plus a placeholder
1553
+ * grid as autofit; Google needs the standard pct value and a usable cached
1554
+ * grid. The cached widths follow the same content-dominant shape and do not
1555
+ * change Word's autofit result.
1556
+ */
1557
+ /** Undo closures recorded by save-time fixups while `saveJournal` is
1558
+ * active, so `save()` can revert every live-tree mutation and remain
1559
+ * side-effect-free. Collaborative checkpoints re-serialize the
1560
+ * authoritative document repeatedly; a save that mutated the tree would
1561
+ * change its hash outside the intent stream and desync the fleet. */
1562
+ private saveJournal;
1563
+ private journalSetAttr;
1564
+ private normalizePercentageTableGrids;
1565
+ save(): Uint8Array;
1566
+ saveAsync(): Promise<Uint8Array>;
1567
+ private buildPackage;
1568
+ private buildPackageFiles;
1569
+ /** Fresh unique docPr id for inserted drawings. Seeded once past the
1570
+ * highest id already present in any editable root (floor 1000, matching
1571
+ * the historical seed for documents with no drawings) so a document that
1572
+ * already carries drawings never gets a colliding id. */
1573
+ nextDrawingId(): number;
1574
+ /** Next unused revision id (w:id on w:ins/w:del). Seeded once past the
1575
+ * highest id already present in any editable root so a document that
1576
+ * already has tracked changes never collides. */
1577
+ private revIdCounter;
1578
+ nextRevisionId(): number;
1579
+ /**
1580
+ * Add image bytes as a new media part + relationship (+ content-type
1581
+ * default). Returns the relationship id for use in a w:drawing.
1582
+ */
1583
+ private ensureRelsRoot;
1584
+ /** Register an external hyperlink relationship and return its rId. */
1585
+ addHyperlinkRel(url: string): string;
1586
+ /** Retarget an existing external relationship (hyperlink href edit). */
1587
+ setRelTarget(relId: string, url: string): boolean;
1588
+ private addImageResourceAt;
1589
+ addImageResource(bytes: Uint8Array, ext: string): string;
1590
+ /** Add a GLB model part and its Office 2019 model3d relationship. */
1591
+ addModel3DResource(bytes: Uint8Array): {
1592
+ relId: string;
1593
+ part: string;
1594
+ };
1595
+ /** Add an OLE package part used by a Word w:object. */
1596
+ addEmbeddedObjectResource(bytes: Uint8Array): {
1597
+ relId: string;
1598
+ part: string;
1599
+ };
1600
+ /** Add a DOCX package embedded as an activatable Word.Document.12 object. */
1601
+ addEmbeddedWordDocumentResource(bytes: Uint8Array): {
1602
+ relId: string;
1603
+ part: string;
1604
+ };
1605
+ /** Add a native ChartML part and its embedded editable workbook. */
1606
+ addChartResource(chartXml: string, workbook: Uint8Array): {
1607
+ relId: string;
1608
+ part: string;
1609
+ };
1610
+ /** Add the SmartArt data/layout/style/color parts and its cached diagram drawing. */
1611
+ addSmartArtResources(layoutXml: string, styleXml: string, colorsXml: string, drawingXml: string, dataXml: (drawingRelId: string) => string): {
1612
+ dataRelId: string;
1613
+ layoutRelId: string;
1614
+ styleRelId: string;
1615
+ colorsRelId: string;
1616
+ drawingRelId: string;
1617
+ };
1618
+ static load(data: ArrayBuffer | Uint8Array): DocxDocument;
1619
+ media(part: string): Uint8Array | undefined;
1620
+ /**
1621
+ * Pending-media registry (plan doc 05 change 1 / doc 16 §6): parts whose
1622
+ * XML registration (rels + content-type + extent geometry) exists but
1623
+ * whose BYTES have not arrived yet — the out-of-band media model. Layout
1624
+ * needs no bytes (extents live in the XML), so a doc with pending parts
1625
+ * lays out pixel-identically; the renderer shows a placeholder until
1626
+ * `installMedia`. Keyed by part name; the value is the doc-16 §5.3
1627
+ * metadata (declared sha + optional E2EE iv/epoch for re-supply).
1628
+ */
1629
+ readonly pendingMedia: Map<string, {
1630
+ sha: string;
1631
+ iv?: string;
1632
+ genesisId?: string;
1633
+ }>;
1634
+ /** DISPLAY-ONLY transfer state for a pending part (doc 16 §5.2 step 4), so
1635
+ * the skeleton can say "fetching" versus "nobody online has this yet".
1636
+ * Written by the media transfer layer, read by the renderer; never part of
1637
+ * the document's identity and never serialized. */
1638
+ readonly mediaTransferState: Map<string, "fetching" | "waiting" | "unavailable">;
1639
+ /** Per-part media metadata that PERSISTS after install (doc 16 §5.3):
1640
+ * holder duty needs the sha (and E2EE iv/epoch) of READY parts to answer
1641
+ * re-supply requests; pendingMedia above only tracks not-yet-arrived. */
1642
+ readonly mediaMeta: Map<string, {
1643
+ sha: string;
1644
+ iv?: string;
1645
+ genesisId?: string;
1646
+ }>;
1647
+ /** "ready" = bytes present; "pending" = registered, bytes absent (doc 05).
1648
+ * Unregistered parts report "pending" too — a rel pointing at a missing
1649
+ * zip entry after a save/parse round-trip IS the pending state (a hole is
1650
+ * detectable, never corrupt — doc 16 §6 round-trip rule). */
1651
+ mediaStatus(part: string): "ready" | "pending";
1652
+ /**
1653
+ * Register an image part + relationship WITHOUT bytes (doc 16 §2: the
1654
+ * intent carries the sha; bytes travel out of band). Same deterministic
1655
+ * naming/rId scan as addImageResource so every replica applying the same
1656
+ * canonical intent derives identical registration.
1657
+ */
1658
+ registerPendingImage(sha: string, ext: string, meta?: {
1659
+ iv?: string;
1660
+ genesisId?: string;
1661
+ }, source?: XmlElement): string;
1662
+ /** Install fetched bytes into a pending part (doc 05). The caller has
1663
+ * ALREADY verified the sha against the intent's declaration (the
1664
+ * reservation is the hash commitment — doc 16 §1.1); this installs and
1665
+ * clears the pending record. Returns false for unknown parts. */
1666
+ installMedia(part: string, bytes: Uint8Array): boolean;
1667
+ /** Effective paragraph properties: docDefaults → table style → style chain → direct. */
1668
+ effectiveParaProps(para: Paragraph): ParaProps;
1669
+ /**
1670
+ * Run props contributed by the enclosing table style's conditional
1671
+ * w:tblStylePr blocks for this paragraph's cell (undefined when the
1672
+ * paragraph isn't in a styled table cell or nothing applies).
1673
+ */
1674
+ private tableCondRPr;
1675
+ /** Effective run properties for a run inside a paragraph. */
1676
+ effectiveRunProps(para: Paragraph, runProps: RunProps): RunProps;
1677
+ numberingLevel(numId: number, ilvl: number): NumberingLevel | undefined;
1678
+ numberingInstance(numId: number): NumInstance | undefined;
1679
+ private findDocumentPart;
1680
+ private readXmlOptional;
1681
+ }
1682
+
1683
+ /**
1684
+ * Wire intents for replicated editing.
1685
+ *
1686
+ * Positions address content by stable id (plan doc 02): `{ blockId, runId,
1687
+ * offset }`, where offset is a character offset within the run's text. Ids
1688
+ * come from the core StableIds side table; the server validates every
1689
+ * client-supplied id before applying (validate-then-install, doc 11) — the
1690
+ * types here are the transport shape, not a trust boundary.
1691
+ *
1692
+ * `base` is the last server sequence number the originating client had
1693
+ * applied when it produced the intent; the server transforms the intent
1694
+ * against everything sequenced in `(base, seq)` before applying and
1695
+ * broadcasting the canonical form (doc 03).
1696
+ */
1697
+ type StableId = number;
1698
+ /** A caret/endpoint position within a run. */
1699
+ interface Position {
1700
+ blockId: StableId;
1701
+ runId: StableId;
1702
+ offset: number;
1703
+ }
1704
+ interface IntentBase {
1705
+ /** Originating client (authenticated identity; the server never trusts a
1706
+ * claimed id — see doc 11). */
1707
+ clientId: string;
1708
+ /** Per-client monotonic counter; `(clientId, clientSeq)` is the idempotency
1709
+ * key that dedups re-sends after reconnect (doc 03). */
1710
+ clientSeq: number;
1711
+ /** Last applied server seq at production time. */
1712
+ base: number;
1713
+ }
1714
+ /**
1715
+ * Tracked-change marks (plan doc 14 §3 L2): strike text ranges as w:del
1716
+ * revisions and/or mark paragraph glyphs (split "ins" marks, merge "del"
1717
+ * suggestions) — the SUGGESTING counterparts of deleteText/mergeParagraph.
1718
+ * Nothing is removed: Word keeps both sides until accept/reject (the
1719
+ * already-collab-wired accept/reject revision intents).
1720
+ * Author/date travel IN the intent (doc 05 rule a: nondeterministic values
1721
+ * are generated once by the originator); revision w:ids are scan-derived
1722
+ * from identical tree state on every replica. Positional offsets were
1723
+ * client-resolved (rule b — no Intl re-derivation at apply).
1724
+ */
1725
+ interface SuggestRevisionIntent extends IntentBase {
1726
+ kind: "suggestRevision";
1727
+ ranges?: {
1728
+ blockId: StableId;
1729
+ runId: StableId;
1730
+ start: number;
1731
+ end: number;
1732
+ }[];
1733
+ marks?: {
1734
+ blockId: StableId;
1735
+ glyph: "ins" | "del";
1736
+ }[];
1737
+ suggest: {
1738
+ author: string;
1739
+ date: string;
1740
+ };
1741
+ }
1742
+ /** Insert `text` at a position. With `suggest`, the insertion is recorded as
1743
+ * a tracked change (w:ins) carrying the author + date (revision tracking /
1744
+ * suggesting mode), rather than a plain insert. */
1745
+ interface InsertTextIntent extends IntentBase {
1746
+ kind: "insertText";
1747
+ at: Position;
1748
+ text: string;
1749
+ /** Tracked-change (suggesting) metadata; omit for a plain insert. */
1750
+ suggest?: {
1751
+ author: string;
1752
+ date: string;
1753
+ };
1754
+ }
1755
+ /** Delete `[start, end)` characters within a single run. */
1756
+ interface DeleteTextIntent extends IntentBase {
1757
+ kind: "deleteText";
1758
+ blockId: StableId;
1759
+ runId: StableId;
1760
+ start: number;
1761
+ end: number;
1762
+ }
1763
+ /**
1764
+ * Split a paragraph at a position. Text after `at.offset` in run `at.runId`
1765
+ * (and following runs) moves to a new paragraph. The originating client
1766
+ * allocates the ids for the new paragraph and the new run holding the moved
1767
+ * tail; every replica installs these recorded values (doc 03), and they also
1768
+ * let the transform remap concurrent positions that fall in the moved tail.
1769
+ */
1770
+ interface SplitParagraphIntent extends IntentBase {
1771
+ kind: "splitParagraph";
1772
+ at: Position;
1773
+ newBlockId: StableId;
1774
+ newRunId: StableId;
1775
+ /** With metadata, Enter remains a tracked paragraph-break suggestion. */
1776
+ suggest?: {
1777
+ author: string;
1778
+ date: string;
1779
+ };
1780
+ }
1781
+ /**
1782
+ * Character-format an entire run (bold/italic/underline/…). Whole-run only:
1783
+ * it mutates the run's w:rPr in place, so no run splits, no new ids, and the
1784
+ * run id is preserved — which makes its transform identity (formatting moves
1785
+ * no text). Sub-range formatting splits the run into up to three, needing the
1786
+ * run-split id-inheritance + position remapping (plan doc 03 F3); that is a
1787
+ * documented extension, not implemented here.
1788
+ */
1789
+ interface FormatRunIntent extends IntentBase {
1790
+ kind: "formatRun";
1791
+ blockId: StableId;
1792
+ runId: StableId;
1793
+ /** RunFormatPatch (bold/italic/underline/strike/color/…). Structural shape
1794
+ * mirrors @wordinweb/core's RunFormatPatch; carried verbatim. */
1795
+ patch: Record<string, unknown>;
1796
+ }
1797
+ /**
1798
+ * Paragraph-level formatting: alignment and/or paragraph style. Block-level —
1799
+ * it mutates the paragraph's w:pPr, creates no tracked nodes, and moves no
1800
+ * text, so the block id is preserved and its transform is identity.
1801
+ */
1802
+ interface FormatParagraphIntent extends IntentBase {
1803
+ kind: "formatParagraph";
1804
+ blockId: StableId;
1805
+ align?: "left" | "center" | "right" | "justify";
1806
+ /** Paragraph style id; null clears to Normal. Omit to leave unchanged. */
1807
+ styleId?: string | null;
1808
+ }
1809
+ /**
1810
+ * Turn a paragraph into a bullet/numbered list item, or clear its list
1811
+ * formatting (kind null). Block-level: mutates w:pPr/numbering, preserves the
1812
+ * block id, moves no text — transform identity.
1813
+ */
1814
+ interface SetListTypeIntent extends IntentBase {
1815
+ kind: "setListType";
1816
+ blockId: StableId;
1817
+ listKind: "bullet" | "number" | null;
1818
+ }
1819
+ /**
1820
+ * Format a character sub-range of a single run (plan doc 03 F3). The run is
1821
+ * split into up to three pieces — before [0,start), middle [start,end)
1822
+ * (formatted), after [end,len) — replacing the original run. The originating
1823
+ * client allocates ids for the pieces that exist and carries them so every
1824
+ * replica addresses the pieces identically; the transform remaps any
1825
+ * concurrent position in the old run into the correct piece.
1826
+ */
1827
+ interface FormatRangeIntent extends IntentBase {
1828
+ kind: "formatRange";
1829
+ blockId: StableId;
1830
+ runId: StableId;
1831
+ start: number;
1832
+ end: number;
1833
+ patch: Record<string, unknown>;
1834
+ /** Piece ids, present iff the piece exists: before when start>0, after when
1835
+ * end<runLen. middle always. */
1836
+ beforeId?: StableId;
1837
+ middleId: StableId;
1838
+ afterId?: StableId;
1839
+ }
1840
+ /**
1841
+ * A table operation that does NOT create new tracked nodes (so no carried ids
1842
+ * are needed and the transform is identity): delete row/column/table, cell
1843
+ * shading, cell vertical align. The target cell is addressed by the stable id
1844
+ * of a paragraph inside it. Row/column INSERTION (which creates cells with new
1845
+ * paragraphs and runs needing carried ids) is a documented harder extension.
1846
+ */
1847
+ interface TableOpIntent extends IntentBase {
1848
+ /** Stable id of a paragraph inside the target cell. */
1849
+ cellParagraphId: StableId;
1850
+ kind: "tableOp";
1851
+ op: "deleteRow" | "deleteCol" | "deleteTable" | "rowAbove" | "rowBelow" | "colLeft" | "colRight" | {
1852
+ kind: "cellShading";
1853
+ fill: string | null;
1854
+ } | {
1855
+ kind: "cellVAlign";
1856
+ v: "top" | "center" | "bottom";
1857
+ } | {
1858
+ kind: "textWrapping";
1859
+ wrapping: "none" | "around";
1860
+ xPx: number;
1861
+ yPx: number;
1862
+ };
1863
+ /** For INSERT ops (rowAbove/rowBelow/colLeft/colRight): carried ids for the
1864
+ * new tracked nodes (p / r) in document order, so replicas address them
1865
+ * alike. */
1866
+ nodeIds?: StableId[];
1867
+ }
1868
+ /**
1869
+ * Merge a paragraph into its predecessor (Backspace at paragraph start). The
1870
+ * paragraph's runs MOVE into the previous paragraph (element identity
1871
+ * preserved), so run-addressed positions survive unchanged — an identity
1872
+ * transform. The merged paragraph's block id is retired.
1873
+ */
1874
+ interface MergeParagraphIntent extends IntentBase {
1875
+ kind: "mergeParagraph";
1876
+ /** The paragraph to merge into the one before it. */
1877
+ blockId: StableId;
1878
+ }
1879
+ /**
1880
+ * Add a review comment anchored to an entire run. Comment markers
1881
+ * (commentRangeStart/End + a commentReference run) are inserted as run
1882
+ * siblings and the comment body goes into comments.xml — no commented run's
1883
+ * text moves, so the transform is identity. The nondeterministic values
1884
+ * (w14:paraId, w:date) are generated once by the originating client and
1885
+ * carried here (plan doc 05) so every replica writes identical XML. Sub-range
1886
+ * comments (splitting the run) are the documented harder extension.
1887
+ */
1888
+ interface CommentRunIntent extends IntentBase {
1889
+ kind: "commentRun";
1890
+ runId: StableId;
1891
+ text: string;
1892
+ author: string;
1893
+ initials?: string;
1894
+ /** Carried provenance for deterministic XML across replicas. */
1895
+ date: string;
1896
+ paraId: string;
1897
+ }
1898
+ /**
1899
+ * Rich paste: splice validated OOXML paragraph blocks after a target
1900
+ * paragraph. The pasting client converts clipboard HTML to OOXML locally
1901
+ * (browser-only, engine-dependent — doc 02 M4) and carries the serialized
1902
+ * blocks; the server VALIDATES them against the positive allowlist before
1903
+ * applying (doc 11 gate 2). `nodeIds` are carried ids for the new tracked
1904
+ * nodes (p / r) in document order, so every replica addresses them alike.
1905
+ * Inserting separate blocks shifts no existing run's offsets — identity
1906
+ * transform.
1907
+ */
1908
+ interface PasteBlocksIntent extends IntentBase {
1909
+ kind: "pasteBlocks";
1910
+ /** Paragraph after which to insert the pasted blocks. */
1911
+ afterBlockId: StableId;
1912
+ /** Serialized OOXML block list (w:p elements), validated at apply. */
1913
+ blocksXml: string;
1914
+ /** Carried ids for the new p/r nodes, in document order. */
1915
+ nodeIds: StableId[];
1916
+ }
1917
+ /**
1918
+ * Insert an image at a run (as a sibling drawing run — no text split, identity
1919
+ * transform). The plan's media design carries bytes out-of-band via presigned
1920
+ * upload with the intent carrying (part, extents, sha) — modeled here with the
1921
+ * bytes inline (base64), which is correct for small images and keeps the whole
1922
+ * flow testable headlessly; the out-of-band path is a transport optimization
1923
+ * over the same convergence. Client-measured dimensions (widthPx/heightPx) are
1924
+ * carried so layout reserves space deterministically. `nodeIds` are carried
1925
+ * ids for the new drawing run(s).
1926
+ */
1927
+ interface InsertImageIntent extends IntentBase {
1928
+ kind: "insertImage";
1929
+ runId: StableId;
1930
+ /**
1931
+ * sha256 hex of the BLOB (plan doc 16 §2): plaintext bytes in plaintext
1932
+ * mode, ciphertext in E2EE mode. This is the reservation's hash
1933
+ * COMMITMENT — every replica registers the part pending and verifies any
1934
+ * later-arriving bytes against it (the swap-proofing trust chain, doc 16
1935
+ * §1.1). Bytes NEVER ride the intent (doc 06: sequencer bandwidth is
1936
+ * proportional to typing, not content).
1937
+ */
1938
+ blobSha: string;
1939
+ /** Blob length in bytes — validate.ts bounds it (doc 13 blocker 3). */
1940
+ bytesLen: number;
1941
+ /** File extension without dot (png/jpg/gif…) — drives the content type. */
1942
+ ext: string;
1943
+ /** E2EE only: base64 12-byte GCM IV, recorded so any holder can re-seal
1944
+ * byte-identically for re-supply (doc 16 §5.3). */
1945
+ iv?: string;
1946
+ widthPx: number;
1947
+ heightPx: number;
1948
+ nodeIds: StableId[];
1949
+ }
1950
+ /**
1951
+ * Insert a page/column break at the END of a run (inserts sibling break + tail
1952
+ * runs — no text split, identity transform). Mid-run breaks (which split the
1953
+ * run) follow the carried-id-split pattern (formatRange) and are the
1954
+ * documented extension. `nodeIds` are carried ids for the new runs.
1955
+ */
1956
+ interface InsertBreakIntent extends IntentBase {
1957
+ kind: "insertBreak";
1958
+ runId: StableId;
1959
+ breakKind: "page" | "column";
1960
+ nodeIds: StableId[];
1961
+ }
1962
+ /**
1963
+ * Insert a math (OMML) formula from a linear expression at the end of a run
1964
+ * (sibling insertion — identity transform). `nodeIds` for any new tracked
1965
+ * nodes.
1966
+ */
1967
+ interface InsertMathIntent extends IntentBase {
1968
+ kind: "insertMath";
1969
+ runId: StableId;
1970
+ mathText: string;
1971
+ nodeIds: StableId[];
1972
+ }
1973
+ /** Insert a shape/textbox drawing at the end of a run (sibling — identity). */
1974
+ interface InsertShapeIntent extends IntentBase {
1975
+ kind: "insertShape";
1976
+ runId: StableId;
1977
+ preset: "line" | "verticalLine" | "rectangle" | "roundedRectangle" | "ellipse" | "diamond" | "textBox";
1978
+ text?: string;
1979
+ nodeIds: StableId[];
1980
+ }
1981
+ /**
1982
+ * Reply to an existing comment (threading). Addressed by the parent comment's
1983
+ * id (a deterministic string). Carries provenance (w14:paraId candidates in
1984
+ * consumption order + the w:date) so every replica writes identical XML. No
1985
+ * document run's text moves — identity transform.
1986
+ */
1987
+ interface ReplyCommentIntent extends IntentBase {
1988
+ kind: "replyComment";
1989
+ parentId: string;
1990
+ text: string;
1991
+ author: string;
1992
+ initials?: string;
1993
+ date: string;
1994
+ /** paraId candidates in the order replyToComment consumes them: the reply's
1995
+ * paraId, preceded by the parent's if the parent lacks one. */
1996
+ paraIds: string[];
1997
+ }
1998
+ /** Adjust a paragraph's indent (Tab/Shift-Tab). Block-level, identity. */
1999
+ interface AdjustIndentIntent extends IntentBase {
2000
+ kind: "adjustIndent";
2001
+ blockId: StableId;
2002
+ direction: 1 | -1;
2003
+ }
2004
+ /** Set paragraph line/before/after spacing. Block-level, identity. */
2005
+ interface SetSpacingIntent extends IntentBase {
2006
+ kind: "setSpacing";
2007
+ blockId: StableId;
2008
+ /** ParagraphSpacingPatch (before/after/line/lineRule) — carried verbatim. */
2009
+ patch: Record<string, unknown>;
2010
+ }
2011
+ /** Insert a page-number field at the end of a run (sibling insertion,
2012
+ * identity). Page fields are deterministic (no clock). */
2013
+ interface InsertPageFieldIntent extends IntentBase {
2014
+ kind: "insertPageField";
2015
+ runId: StableId;
2016
+ fieldKind: "page" | "pageOfTotal";
2017
+ nodeIds: StableId[];
2018
+ }
2019
+ /** Wrap a run in a hyperlink to `url`. The url is scheme-validated at apply
2020
+ * (rejects javascript:/data: etc. — doc 11 gate 1). Run-level, identity. */
2021
+ interface SetLinkIntent extends IntentBase {
2022
+ kind: "setLink";
2023
+ runId: StableId;
2024
+ url: string;
2025
+ nodeIds: StableId[];
2026
+ }
2027
+ /** Insert a footnote reference at the end of a run + its footnote text. */
2028
+ interface InsertFootnoteIntent extends IntentBase {
2029
+ kind: "insertFootnote";
2030
+ runId: StableId;
2031
+ text: string;
2032
+ nodeIds: StableId[];
2033
+ }
2034
+ /** Set/clear a drop cap on a paragraph. Block-level. */
2035
+ interface SetDropCapIntent extends IntentBase {
2036
+ kind: "setDropCap";
2037
+ blockId: StableId;
2038
+ mode: "drop" | "margin" | null;
2039
+ nodeIds: StableId[];
2040
+ }
2041
+ /** Set/clear a paragraph divider (horizontal rule). Block-level, identity. */
2042
+ interface SetDividerIntent extends IntentBase {
2043
+ kind: "setDivider";
2044
+ blockId: StableId;
2045
+ divider: Record<string, unknown> | null;
2046
+ }
2047
+ /** Insert a bookmark anchor at the end of a run. */
2048
+ interface InsertBookmarkIntent extends IntentBase {
2049
+ kind: "insertBookmark";
2050
+ runId: StableId;
2051
+ name: string;
2052
+ }
2053
+ 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 | SetDrawingWordArtStyleIntent | 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;
2054
+ /** Insert a blank page at the end of a run. */
2055
+ interface InsertBlankPageIntent extends IntentBase {
2056
+ kind: "insertBlankPage";
2057
+ runId: StableId;
2058
+ nodeIds: StableId[];
2059
+ }
2060
+ /** Insert a section break at the end of a run. */
2061
+ interface InsertSectionBreakIntent extends IntentBase {
2062
+ kind: "insertSectionBreak";
2063
+ runId: StableId;
2064
+ breakType: "nextPage" | "continuous";
2065
+ nodeIds: StableId[];
2066
+ }
2067
+ /** Insert a cross-reference to a bookmark at the end of a run. */
2068
+ interface InsertCrossRefIntent extends IntentBase {
2069
+ kind: "insertCrossRef";
2070
+ runId: StableId;
2071
+ bookmark: string;
2072
+ refKind: "text" | "page";
2073
+ nodeIds: StableId[];
2074
+ }
2075
+ /** Insert a cover page (document-level). */
2076
+ interface InsertCoverPageIntent extends IntentBase {
2077
+ kind: "insertCoverPage";
2078
+ /** CoverPageContent ({title, subtitle?, author?, …}) — carried verbatim. */
2079
+ content: Record<string, unknown>;
2080
+ nodeIds: StableId[];
2081
+ }
2082
+ /** Update page setup: margins/size/orientation/columns/borders. When target is
2083
+ * omitted it applies to every section; document-level, identity transform. */
2084
+ interface SetPageLayoutIntent extends IntentBase {
2085
+ kind: "setPageLayout";
2086
+ /** PageLayoutPatch ({margins?, size?, orientation?, columns?, …}) — carried
2087
+ * verbatim; validated against a shape+range allowlist. */
2088
+ patch: Record<string, unknown>;
2089
+ }
2090
+ /** Indent (+1) or outdent (-1) a paragraph's list nesting level. Block-level. */
2091
+ interface SetListLevelIntent extends IntentBase {
2092
+ kind: "setListLevel";
2093
+ blockId: StableId;
2094
+ delta: 1 | -1;
2095
+ }
2096
+ /** Insert decorative WordArt (a text drawing) at the end of a run. */
2097
+ interface InsertWordArtIntent extends IntentBase {
2098
+ kind: "insertWordArt";
2099
+ runId: StableId;
2100
+ text: string;
2101
+ preset: "plain" | "archUp" | "archDown" | "wave" | "chevron";
2102
+ nodeIds: StableId[];
2103
+ }
2104
+ /** Insert a data chart (column/bar/line/pie) at the end of a run. The chart
2105
+ * data is carried verbatim; the workbook part is generated deterministically. */
2106
+ interface InsertChartIntent extends IntentBase {
2107
+ kind: "insertChart";
2108
+ runId: StableId;
2109
+ chart: {
2110
+ type: "column" | "bar" | "line" | "pie";
2111
+ title?: string;
2112
+ categories: string[];
2113
+ series: {
2114
+ name: string;
2115
+ values: number[];
2116
+ }[];
2117
+ };
2118
+ nodeIds: StableId[];
2119
+ }
2120
+ /** Insert a SmartArt diagram (process/cycle/hierarchy/list) at end of a run. */
2121
+ interface InsertSmartArtIntent extends IntentBase {
2122
+ kind: "insertSmartArt";
2123
+ runId: StableId;
2124
+ smartArt: {
2125
+ layout: "process" | "cycle" | "hierarchy" | "list";
2126
+ items: string[];
2127
+ };
2128
+ nodeIds: StableId[];
2129
+ }
2130
+ /** Toggle/configure margin line numbering for the section(s). Document-level. */
2131
+ interface SetLineNumberingIntent extends IntentBase {
2132
+ kind: "setLineNumbering";
2133
+ patch: {
2134
+ enabled: boolean;
2135
+ countBy?: number;
2136
+ restart?: "continuous" | "newPage" | "newSection";
2137
+ start?: number;
2138
+ };
2139
+ }
2140
+ /** Insert an auto-updating DATE or TIME field at the end of a run. The picture
2141
+ * (format string) is carried so every replica renders identical field XML. */
2142
+ interface InsertDateTimeFieldIntent extends IntentBase {
2143
+ kind: "insertDateTimeField";
2144
+ runId: StableId;
2145
+ dtKind: "date" | "time";
2146
+ picture: string;
2147
+ nodeIds: StableId[];
2148
+ }
2149
+ /** Insert a Word field (e.g. PAGE, NUMPAGES, REF) at the end of a run. The
2150
+ * instruction is restricted to a safe positive allowlist (no INCLUDETEXT/DDE/
2151
+ * LINK external-content fields). */
2152
+ interface InsertFieldIntent extends IntentBase {
2153
+ kind: "insertField";
2154
+ runId: StableId;
2155
+ instruction: string;
2156
+ cachedResult?: string;
2157
+ nodeIds: StableId[];
2158
+ }
2159
+ interface DrawingTarget {
2160
+ runId: StableId;
2161
+ /** Parsed run-content index. It disambiguates several drawings carried by one run. */
2162
+ objectIndex?: number;
2163
+ }
2164
+ /** Rotate the drawing carried by a run. Identity transform (run-addressed). */
2165
+ interface SetDrawingRotationIntent extends IntentBase, DrawingTarget {
2166
+ kind: "setDrawingRotation";
2167
+ runId: StableId;
2168
+ degrees: number;
2169
+ }
2170
+ /** Set/clear the solid fill of the drawing carried by a run. `color` is a
2171
+ * 6-hex-digit RGB (no #) or null to clear. */
2172
+ interface SetDrawingFillIntent extends IntentBase, DrawingTarget {
2173
+ kind: "setDrawingFill";
2174
+ runId: StableId;
2175
+ color: string | null;
2176
+ }
2177
+ /** Replace the data of the chart carried by a run (same shape as insertChart's
2178
+ * chart). Edits an existing chart in place. */
2179
+ interface SetChartDataIntent extends IntentBase, DrawingTarget {
2180
+ kind: "setChartData";
2181
+ runId: StableId;
2182
+ chart: {
2183
+ type: "column" | "bar" | "line" | "pie";
2184
+ title?: string;
2185
+ categories: string[];
2186
+ series: {
2187
+ name: string;
2188
+ values: number[];
2189
+ }[];
2190
+ };
2191
+ }
2192
+ /** Set the text of one node of the SmartArt diagram carried by a run. */
2193
+ interface SetSmartArtNodeTextIntent extends IntentBase, DrawingTarget {
2194
+ kind: "setSmartArtNodeText";
2195
+ runId: StableId;
2196
+ index: number;
2197
+ text: string;
2198
+ }
2199
+ /** Replace the text of the WordArt drawing carried by a run. */
2200
+ interface SetDrawingWordArtTextIntent extends IntentBase, DrawingTarget {
2201
+ kind: "setDrawingWordArtText";
2202
+ runId: StableId;
2203
+ text: string;
2204
+ }
2205
+ /** Set the glyph fill and alpha of the WordArt drawing carried by a run. */
2206
+ interface SetDrawingWordArtStyleIntent extends IntentBase, DrawingTarget {
2207
+ kind: "setDrawingWordArtStyle";
2208
+ runId: StableId;
2209
+ color: string;
2210
+ opacity: number;
2211
+ }
2212
+ /** Set or clear the outline of the drawing carried by a run. */
2213
+ type SetDrawingLineStyleIntent = IntentBase & DrawingTarget & {
2214
+ kind: "setDrawingLineStyle";
2215
+ runId: StableId;
2216
+ } & ({
2217
+ color: null;
2218
+ widthPx?: number;
2219
+ dash?: "solid" | "dashed" | "dotted";
2220
+ } | {
2221
+ color: string;
2222
+ widthPx: number;
2223
+ dash: "solid" | "dashed" | "dotted";
2224
+ });
2225
+ /** Set/clear the alt text (accessibility description) of the image/drawing
2226
+ * carried by a run. Empty string clears it. */
2227
+ interface SetImageAltTextIntent extends IntentBase, DrawingTarget {
2228
+ kind: "setImageAltText";
2229
+ runId: StableId;
2230
+ alt: string;
2231
+ }
2232
+ /** Remove the hyperlink wrapping a run's text (inverse of setLink). */
2233
+ interface RemoveLinkIntent extends IntentBase {
2234
+ kind: "removeLink";
2235
+ runId: StableId;
2236
+ }
2237
+ /** Set the text-wrap mode of the drawing carried by a run. A floating mode
2238
+ * converts an inline drawing to an anchor. */
2239
+ interface SetImageWrapIntent extends IntentBase, DrawingTarget {
2240
+ kind: "setImageWrap";
2241
+ runId: StableId;
2242
+ mode: "inline" | "square" | "topAndBottom" | "none" | "behind";
2243
+ }
2244
+ /** Bring the (floating) drawing carried by a run to front/back. */
2245
+ interface SetDrawingOrderIntent extends IntentBase, DrawingTarget {
2246
+ kind: "setDrawingOrder";
2247
+ runId: StableId;
2248
+ order: "front" | "back";
2249
+ }
2250
+ /** Replace the whole SmartArt diagram (layout + items) carried by a run. */
2251
+ interface SetSmartArtDataIntent extends IntentBase, DrawingTarget {
2252
+ kind: "setSmartArtData";
2253
+ runId: StableId;
2254
+ smartArt: {
2255
+ layout: "process" | "cycle" | "hierarchy" | "list";
2256
+ items: string[];
2257
+ };
2258
+ }
2259
+ /** Set/clear the fill of a SmartArt node (or all nodes when nodeIndex omitted).
2260
+ * `color` is 6-hex RGB (no #) or null to clear. */
2261
+ interface SetSmartArtFillIntent extends IntentBase, DrawingTarget {
2262
+ kind: "setSmartArtFill";
2263
+ runId: StableId;
2264
+ color: string | null;
2265
+ nodeIndex?: number;
2266
+ }
2267
+ /** Set the text format of a SmartArt node (or all nodes). */
2268
+ interface SetSmartArtTextFormatIntent extends IntentBase, DrawingTarget {
2269
+ kind: "setSmartArtTextFormat";
2270
+ runId: StableId;
2271
+ format: {
2272
+ fontFamily: string;
2273
+ fontSizePt: number;
2274
+ color: string;
2275
+ bold: boolean;
2276
+ italic: boolean;
2277
+ alignment: "left" | "center" | "right";
2278
+ };
2279
+ nodeIndex?: number;
2280
+ }
2281
+ /** Position the (floating) drawing carried by a run relative to the page. */
2282
+ interface SetFloatingPagePositionIntent extends IntentBase, DrawingTarget {
2283
+ kind: "setFloatingPagePosition";
2284
+ runId: StableId;
2285
+ xPx: number;
2286
+ yPx: number;
2287
+ }
2288
+ /** Resize the drawing carried by a run (extent in px; the mutation clamps to
2289
+ * a 1px-EMU floor and keeps line geometry degenerate on its minor axis). */
2290
+ interface ResizeDrawingIntent extends IntentBase, DrawingTarget {
2291
+ kind: "resizeDrawing";
2292
+ runId: StableId;
2293
+ widthPx: number;
2294
+ heightPx: number;
2295
+ }
2296
+ /** Drag-resize a table column boundary. Addressed like tableOp: a paragraph
2297
+ * inside the table. `renderedWidths` (the dragger's measured column widths)
2298
+ * rides as DATA so every replica applies the identical mutation. */
2299
+ interface ResizeTableColumnIntent extends IntentBase {
2300
+ kind: "resizeTableColumn";
2301
+ cellParagraphId: StableId;
2302
+ boundary: number;
2303
+ deltaPx: number;
2304
+ renderedWidths?: number[];
2305
+ }
2306
+ /** Drag-resize a table row's height (trHeight atLeast). */
2307
+ interface ResizeTableRowIntent extends IntentBase {
2308
+ kind: "resizeTableRow";
2309
+ cellParagraphId: StableId;
2310
+ rowIdx: number;
2311
+ heightPx: number;
2312
+ }
2313
+ /** Position a table at page coordinates (converts inline to float). */
2314
+ interface MoveTableIntent extends IntentBase {
2315
+ kind: "moveTable";
2316
+ cellParagraphId: StableId;
2317
+ xPx: number;
2318
+ yPx: number;
2319
+ preservePageStart: boolean;
2320
+ pageDelta: number;
2321
+ }
2322
+ /** Delete the drawing at the supplied run-content target. */
2323
+ interface RemoveDrawingIntent extends IntentBase, DrawingTarget {
2324
+ kind: "removeDrawing";
2325
+ runId: StableId;
2326
+ }
2327
+ /** Replace the equation of the math object in a paragraph (linear syntax).
2328
+ * Math (m:oMath) lives at paragraph level, so this is block-addressed. */
2329
+ interface SetMathLinearIntent extends IntentBase {
2330
+ kind: "setMathLinear";
2331
+ blockId: StableId;
2332
+ mathText: string;
2333
+ }
2334
+ /** Delete the math object in a paragraph. */
2335
+ interface DeleteMathIntent extends IntentBase {
2336
+ kind: "deleteMath";
2337
+ blockId: StableId;
2338
+ }
2339
+ /** Drag-move the equation out of `blockId` to the text position `at`. The
2340
+ * equation is addressed like the other math intents (block + firstMathIn); the
2341
+ * destination is an ordinary wire caret. Dropping mid-text splits the
2342
+ * destination run, so the split-off tail run takes a carried id. */
2343
+ interface MoveMathIntent extends IntentBase {
2344
+ kind: "moveMath";
2345
+ blockId: StableId;
2346
+ at: Position;
2347
+ nodeIds: StableId[];
2348
+ }
2349
+ /** Create the document's header (or footer) part the way Word does on first
2350
+ * entry into the margin band: a new part + relationship + content-type
2351
+ * override, referenced from every sectPr (one is materialized when a minimal
2352
+ * document has none). Applies as a clean no-op when the part already exists,
2353
+ * so it is safe for two participants to open the header at the same moment.
2354
+ * The created paragraph and run take carried ids — every later edit in the
2355
+ * header addresses them. */
2356
+ interface EnsureHeaderFooterIntent extends IntentBase {
2357
+ kind: "ensureHeaderFooter";
2358
+ hfKind: "header" | "footer";
2359
+ nodeIds: StableId[];
2360
+ }
2361
+ /** Delete a comment (and its reply thread) by id. */
2362
+ interface DeleteCommentIntent extends IntentBase {
2363
+ kind: "deleteComment";
2364
+ commentId: string;
2365
+ }
2366
+ /** Wrap a sub-range of a run's text in a named bookmark. */
2367
+ interface InsertBookmarkRangeIntent extends IntentBase {
2368
+ kind: "insertBookmarkRange";
2369
+ runId: StableId;
2370
+ name: string;
2371
+ start: number;
2372
+ end: number;
2373
+ }
2374
+ /** Toggle the checkbox content control carried by a run. */
2375
+ interface ToggleCheckboxIntent extends IntentBase {
2376
+ kind: "toggleCheckbox";
2377
+ runId: StableId;
2378
+ }
2379
+ /** Accept one tracked change, addressed by its position in document order
2380
+ * (collectRevisions order — deterministic across replicas). */
2381
+ interface AcceptRevisionIntent extends IntentBase {
2382
+ kind: "acceptRevision";
2383
+ index: number;
2384
+ }
2385
+ /** Reject one tracked change, addressed by document-order index. */
2386
+ interface RejectRevisionIntent extends IntentBase {
2387
+ kind: "rejectRevision";
2388
+ index: number;
2389
+ }
2390
+ /** Accept every tracked change in the document. */
2391
+ interface AcceptAllRevisionsIntent extends IntentBase {
2392
+ kind: "acceptAllRevisions";
2393
+ }
2394
+ /** Reject every tracked change in the document. */
2395
+ interface RejectAllRevisionsIntent extends IntentBase {
2396
+ kind: "rejectAllRevisions";
2397
+ }
2398
+ /** Insert a rows×cols table after the paragraph containing the anchor run. */
2399
+ interface InsertTableIntent extends IntentBase {
2400
+ kind: "insertTable";
2401
+ runId: StableId;
2402
+ rows: number;
2403
+ cols: number;
2404
+ nodeIds: StableId[];
2405
+ }
2406
+ /** An intent minus its wire bookkeeping — distributive over the union so each
2407
+ * variant keeps its own fields (a plain Omit<Intent,…> would collapse to the
2408
+ * shared keys only). Used by the editor producer and the undo inverse. */
2409
+ type IntentBody = Intent extends infer T ? (T extends Intent ? Omit<T, "clientId" | "clientSeq" | "base"> : never) : never;
2410
+
2411
+ /**
2412
+ * How much of the document an applied intent disturbed — the input to
2413
+ * dirty-scoped reconciliation (perf B9/B10). Reconciling an edit used to cost
2414
+ * O(document) on every path (full model reparse + full id walk), so per-op
2415
+ * cost grew with the document; the scope lets the caller pay for THE EDIT.
2416
+ *
2417
+ * `doc` is the conservative default and means exactly today's behavior: full
2418
+ * refresh() + assignFromRoots(). Only intents whose blast radius is provably
2419
+ * one paragraph (or the two sides of a split) report a narrower scope; every
2420
+ * structural, document-level, or exotic intent keeps the document scope.
2421
+ */
2422
+ type Scope = {
2423
+ kind: "doc";
2424
+ }
2425
+ /** Only these paragraphs' contents changed. */
2426
+ | {
2427
+ kind: "block";
2428
+ blocks: XmlElement[];
2429
+ }
2430
+ /** `before` was split; `after` is the new sibling that follows it. */
2431
+ | {
2432
+ kind: "split";
2433
+ before: XmlElement;
2434
+ after: XmlElement;
2435
+ };
2436
+
2437
+ /**
2438
+ * Wire protocol between a collab client and the server host. Transport-
2439
+ * agnostic: the hub speaks these messages; a WebSocket (or in-memory test)
2440
+ * adapter serializes them. Kept deliberately small (plan doc 06).
2441
+ */
2442
+ /** One highlighted stretch of a remote participant's selection: a half-open
2443
+ * `[start, end)` inside ONE run, in the same WIRE offset basis carets and
2444
+ * suggestRevision use (cumulative within the run, inline separators counting
2445
+ * one unit each — see core `EncodedCaret`). The sender emits one entry per
2446
+ * selection segment, and a segment never straddles a w:t, so a range stays
2447
+ * inside a single run by construction. */
2448
+ interface PresenceRange {
2449
+ blockId: number;
2450
+ runId: number;
2451
+ start: number;
2452
+ end: number;
2453
+ }
2454
+ /** An ephemeral cursor/selection position on the presence channel: stable-id
2455
+ * addresses (plan doc 03). Never logged, never persisted. */
2456
+ interface PresencePosition {
2457
+ anchor: {
2458
+ blockId: number;
2459
+ runId: number;
2460
+ offset: number;
2461
+ };
2462
+ focus?: {
2463
+ blockId: number;
2464
+ runId: number;
2465
+ offset: number;
2466
+ };
2467
+ /** Selection highlight (Google-Docs style): the stretches the participant
2468
+ * has selected, drawn in their presence color under the remote caret.
2469
+ * OPTIONAL and additive — a payload from an older client carries only the
2470
+ * anchor and still renders a caret. Absent/empty ⇒ no selection. */
2471
+ ranges?: PresenceRange[];
2472
+ }
2473
+
2474
+ interface AgentEditCapability {
2475
+ category: "text" | "paragraph" | "review" | "table" | "insert" | "drawing" | "math" | "document";
2476
+ description: string;
2477
+ required: string[];
2478
+ optional?: string[];
2479
+ }
2480
+ /**
2481
+ * The agent-facing coverage map for the complete canonical edit surface.
2482
+ * Record<Intent["kind"], ...> makes a new intent fail the agent build until
2483
+ * its tool contract is declared here.
2484
+ */
2485
+ declare const AGENT_EDIT_CAPABILITIES: Record<Intent["kind"], AgentEditCapability>;
2486
+ type JsonSchema$1 = Record<string, unknown>;
2487
+ declare function agentOperationSchema(kind: Intent["kind"]): JsonSchema$1;
2488
+ declare function validateAgentOperationShape(input: unknown): string | null;
2489
+ declare function agentCapabilities(category?: AgentEditCapability["category"], requestedKind?: Intent["kind"]): Array<AgentEditCapability & {
2490
+ kind: Intent["kind"];
2491
+ inputSchema: Record<string, unknown>;
2492
+ }>;
2493
+
2494
+ type AgentReference = string;
2495
+ interface AgentCursor {
2496
+ value: string;
2497
+ }
2498
+ interface AgentOverview {
2499
+ revision: string;
2500
+ sections: number;
2501
+ mirrorMargins: boolean;
2502
+ sectionLayouts: AgentSectionLayout[];
2503
+ stories: Array<{
2504
+ id: string;
2505
+ kind: "body" | "header" | "footer" | "footnote" | "endnote" | "textbox";
2506
+ blocks: number;
2507
+ }>;
2508
+ blocks: {
2509
+ paragraphs: number;
2510
+ tables: number;
2511
+ };
2512
+ characters: number;
2513
+ comments: number;
2514
+ components: Record<string, number>;
2515
+ objectCounts: Record<string, number>;
2516
+ outline: OutlineEntry[];
2517
+ }
2518
+ interface AgentSectionLayout {
2519
+ index: number;
2520
+ page: {
2521
+ width: number;
2522
+ height: number;
2523
+ };
2524
+ margins: {
2525
+ top: number;
2526
+ right: number;
2527
+ bottom: number;
2528
+ left: number;
2529
+ };
2530
+ headerDistance: number;
2531
+ footerDistance: number;
2532
+ gutter: number;
2533
+ columns: {
2534
+ count: number;
2535
+ space: number;
2536
+ widths?: number[];
2537
+ spaces?: number[];
2538
+ separator?: boolean;
2539
+ };
2540
+ titlePage: boolean;
2541
+ pageNumberStart?: number;
2542
+ pageNumberFormat?: string;
2543
+ breakType?: "nextPage" | "continuous" | "evenPage" | "oddPage" | "nextColumn";
2544
+ verticalAlignment?: "top" | "center" | "both" | "bottom";
2545
+ pageBorders?: AgentPageBorders;
2546
+ lineNumbering?: {
2547
+ countBy: number;
2548
+ start: number;
2549
+ distance: number;
2550
+ restart: "continuous" | "newPage" | "newSection";
2551
+ };
2552
+ }
2553
+ interface AgentBorder {
2554
+ style: "none" | "single" | "double" | "dotted" | "dashed" | "thick" | "wave" | "dotDash" | "dotDotDash" | "thinThickSmallGap" | "triple";
2555
+ width: number;
2556
+ color: string;
2557
+ space: number;
2558
+ rawWidth?: number;
2559
+ }
2560
+ interface AgentPageBorders {
2561
+ top?: AgentBorder;
2562
+ bottom?: AgentBorder;
2563
+ left?: AgentBorder;
2564
+ right?: AgentBorder;
2565
+ offsetFrom: "text" | "page";
2566
+ }
2567
+ interface OutlineEntry {
2568
+ ref: AgentReference;
2569
+ level: number;
2570
+ text: string;
2571
+ styleId?: string;
2572
+ }
2573
+ interface AgentRun {
2574
+ ref: AgentReference;
2575
+ editable: boolean;
2576
+ paragraphStart: number;
2577
+ paragraphEnd: number;
2578
+ wireLength: number;
2579
+ text: string;
2580
+ formatting: Record<string, unknown>;
2581
+ hyperlink?: {
2582
+ href?: string;
2583
+ anchor?: string;
2584
+ };
2585
+ components: AgentComponentSummary[];
2586
+ }
2587
+ interface AgentParagraph {
2588
+ type: "paragraph";
2589
+ ref: AgentReference;
2590
+ editable: boolean;
2591
+ story: string;
2592
+ styleId?: string;
2593
+ outlineLevel?: number;
2594
+ alignment?: string;
2595
+ list?: {
2596
+ numId: number;
2597
+ level: number;
2598
+ } | null;
2599
+ text: string;
2600
+ textRange: {
2601
+ start: number;
2602
+ end: number;
2603
+ total: number;
2604
+ };
2605
+ runs: AgentRun[];
2606
+ bookmarks: string[];
2607
+ }
2608
+ interface AgentTable {
2609
+ type: "table";
2610
+ ref: AgentReference;
2611
+ editable: boolean;
2612
+ story: string;
2613
+ rows: number;
2614
+ columns: number;
2615
+ styleId?: string;
2616
+ floating: boolean;
2617
+ cells: Array<{
2618
+ row: number;
2619
+ column: number;
2620
+ blocks: AgentReference[];
2621
+ }>;
2622
+ }
2623
+ type AgentBlock = AgentParagraph | AgentTable;
2624
+ interface AgentReadResult {
2625
+ revision: string;
2626
+ story: string;
2627
+ blocks: AgentBlock[];
2628
+ next?: AgentCursor;
2629
+ truncated: boolean;
2630
+ }
2631
+ interface AgentContextRun {
2632
+ ref: AgentReference;
2633
+ start: number;
2634
+ end: number;
2635
+ wireLength?: number;
2636
+ }
2637
+ interface AgentContextParagraph {
2638
+ type: "paragraph";
2639
+ ref: AgentReference;
2640
+ text: string;
2641
+ range?: {
2642
+ start: number;
2643
+ end: number;
2644
+ total: number;
2645
+ };
2646
+ runs: AgentContextRun[];
2647
+ styleId?: string;
2648
+ outlineLevel?: number;
2649
+ list?: {
2650
+ numId: number;
2651
+ level: number;
2652
+ };
2653
+ bookmarks?: string[];
2654
+ objects?: AgentComponentSummary[];
2655
+ }
2656
+ interface AgentContextTable {
2657
+ type: "table";
2658
+ ref: AgentReference;
2659
+ rows: number;
2660
+ columns: number;
2661
+ }
2662
+ interface AgentContextStory {
2663
+ story: string;
2664
+ kind: "body" | "header" | "footer" | "footnote" | "endnote" | "textbox";
2665
+ blocks: Array<AgentContextParagraph | AgentContextTable>;
2666
+ next?: AgentCursor;
2667
+ }
2668
+ interface AgentContextResult {
2669
+ revision: string;
2670
+ contents: AgentContextStory[];
2671
+ truncated: boolean;
2672
+ remainingStories?: string[];
2673
+ }
2674
+ interface AgentSearchResult {
2675
+ revision: string;
2676
+ matches: Array<{
2677
+ blockRef: AgentReference;
2678
+ runRef: AgentReference;
2679
+ editable: boolean;
2680
+ start: number;
2681
+ end: number;
2682
+ excerpt: string;
2683
+ }>;
2684
+ truncated: boolean;
2685
+ }
2686
+ interface AgentComponentSummary {
2687
+ ref: AgentReference;
2688
+ editRef?: AgentReference;
2689
+ type: string;
2690
+ label?: string;
2691
+ }
2692
+ interface AgentObjectResult {
2693
+ revision: string;
2694
+ ref: AgentReference;
2695
+ editRef?: AgentReference;
2696
+ type: string;
2697
+ detail: Record<string, unknown>;
2698
+ }
2699
+ interface AgentAsset {
2700
+ ref: AgentReference;
2701
+ bytes: Uint8Array;
2702
+ mediaType: string;
2703
+ }
2704
+ interface SpatialBounds {
2705
+ x: number;
2706
+ y: number;
2707
+ width: number;
2708
+ height: number;
2709
+ }
2710
+ interface AgentSpatialObject {
2711
+ ref: AgentReference;
2712
+ editRef?: AgentReference;
2713
+ type: string;
2714
+ page: number;
2715
+ bounds: SpatialBounds;
2716
+ rotation?: number;
2717
+ layer: "behind-text" | "body" | "in-front-of-text";
2718
+ zOrder?: number;
2719
+ }
2720
+ interface AgentOverlap {
2721
+ objects: [AgentReference, AgentReference];
2722
+ overlapBounds: SpatialBounds;
2723
+ overlapArea: number;
2724
+ topObject?: AgentReference;
2725
+ }
2726
+ interface AgentSpatialResult {
2727
+ revision: string;
2728
+ layout: {
2729
+ quality: "exact" | "approximate";
2730
+ profile: string;
2731
+ };
2732
+ totalPages: number;
2733
+ pages: Array<{
2734
+ index: number;
2735
+ width: number;
2736
+ height: number;
2737
+ }>;
2738
+ objects: AgentSpatialObject[];
2739
+ overlaps: AgentOverlap[];
2740
+ }
2741
+ type AgentInspectRequest = {
2742
+ kind: "overview";
2743
+ } | {
2744
+ kind: "context";
2745
+ stories?: string[];
2746
+ maxBlocks?: number;
2747
+ maxCharacters?: number;
2748
+ include?: Array<"bookmarks" | "objects">;
2749
+ includeEmpty?: boolean;
2750
+ } | {
2751
+ kind: "read";
2752
+ story?: string;
2753
+ cursor?: AgentCursor;
2754
+ maxBlocks?: number;
2755
+ maxCharacters?: number;
2756
+ } | {
2757
+ kind: "search";
2758
+ query: string;
2759
+ maxResults?: number;
2760
+ } | {
2761
+ kind: "object";
2762
+ ref: AgentReference;
2763
+ } | {
2764
+ kind: "spatial";
2765
+ pages?: {
2766
+ start: number;
2767
+ count: number;
2768
+ };
2769
+ includeOverlaps?: boolean;
2770
+ };
2771
+ type AgentInspectResult = AgentOverview | AgentContextResult | AgentReadResult | AgentSearchResult | AgentObjectResult | AgentSpatialResult;
2772
+ type AgentOperation = Record<string, unknown> & {
2773
+ kind: string;
2774
+ };
2775
+ interface AgentEditRequest {
2776
+ revision: string;
2777
+ operations: AgentOperation[];
2778
+ }
2779
+ interface AgentEditResult {
2780
+ revision: string;
2781
+ status: "applied" | "submitted";
2782
+ operations: string[];
2783
+ connection?: "local" | "live" | "offline" | "reconnecting";
2784
+ }
2785
+ interface AgentComposeRun {
2786
+ text: string;
2787
+ bold?: boolean;
2788
+ italic?: boolean;
2789
+ underline?: boolean;
2790
+ color?: string;
2791
+ highlight?: string;
2792
+ fontSizePt?: number;
2793
+ fontFamily?: string;
2794
+ }
2795
+ interface AgentComposeCell {
2796
+ text?: string;
2797
+ runs?: AgentComposeRun[];
2798
+ fill?: string;
2799
+ align?: "left" | "center" | "right";
2800
+ verticalAlign?: "top" | "center" | "bottom";
2801
+ }
2802
+ interface AgentComposeTextStyle {
2803
+ bold?: boolean;
2804
+ italic?: boolean;
2805
+ underline?: boolean;
2806
+ color?: string;
2807
+ fontSizePt?: number;
2808
+ fontFamily?: string;
2809
+ align?: "left" | "center" | "right";
2810
+ }
2811
+ type AgentComposeBlock = {
2812
+ type: "paragraph";
2813
+ text?: string;
2814
+ runs?: AgentComposeRun[];
2815
+ styleId?: "Normal" | "Title" | "Subtitle" | "Heading1" | "Heading2" | "Heading3";
2816
+ align?: "left" | "center" | "right" | "both";
2817
+ spacing?: {
2818
+ beforePt?: number;
2819
+ afterPt?: number;
2820
+ lineMultiple?: number;
2821
+ };
2822
+ list?: "bullet" | "number";
2823
+ } | {
2824
+ type: "heading";
2825
+ level: 1 | 2 | 3;
2826
+ text: string;
2827
+ } | {
2828
+ type: "table";
2829
+ rows: Array<Array<string | AgentComposeCell>>;
2830
+ headerRows?: number;
2831
+ headerFill?: string;
2832
+ headerTextColor?: string;
2833
+ columnWidths?: number[];
2834
+ } | {
2835
+ type: "equation";
2836
+ mathText: string;
2837
+ align?: "left" | "center" | "right";
2838
+ } | {
2839
+ type: "chart";
2840
+ chart: {
2841
+ type: "column" | "bar" | "line" | "pie";
2842
+ title?: string;
2843
+ categories: string[];
2844
+ series: Array<{
2845
+ name: string;
2846
+ values: number[];
2847
+ }>;
2848
+ };
2849
+ widthPx?: number;
2850
+ heightPx?: number;
2851
+ align?: "left" | "center" | "right";
2852
+ } | {
2853
+ type: "smartArt";
2854
+ smartArt: {
2855
+ layout: "process" | "cycle" | "hierarchy" | "list";
2856
+ items: string[];
2857
+ };
2858
+ widthPx?: number;
2859
+ heightPx?: number;
2860
+ align?: "left" | "center" | "right";
2861
+ } | {
2862
+ type: "image";
2863
+ assetRef: string;
2864
+ widthPx: number;
2865
+ heightPx: number;
2866
+ alt?: string;
2867
+ align?: "left" | "center" | "right";
2868
+ wrap?: "inline" | "square" | "topAndBottom" | "none" | "behind";
2869
+ position?: {
2870
+ xPx: number;
2871
+ yPx: number;
2872
+ };
2873
+ } | {
2874
+ type: "shape";
2875
+ preset: "line" | "verticalLine" | "rectangle" | "roundedRectangle" | "ellipse" | "diamond" | "textBox";
2876
+ text?: string;
2877
+ textStyle?: AgentComposeTextStyle;
2878
+ widthPx?: number;
2879
+ heightPx?: number;
2880
+ position?: {
2881
+ xPx: number;
2882
+ yPx: number;
2883
+ };
2884
+ fill?: string | null;
2885
+ line?: {
2886
+ color: string;
2887
+ widthPx: number;
2888
+ dash: "solid" | "dashed" | "dotted";
2889
+ } | null;
2890
+ wrap?: "inline" | "square" | "topAndBottom" | "none" | "behind";
2891
+ order?: "front" | "back";
2892
+ } | {
2893
+ type: "wordArt";
2894
+ text: string;
2895
+ preset: "plain" | "archUp" | "archDown" | "wave" | "chevron";
2896
+ widthPx?: number;
2897
+ heightPx?: number;
2898
+ position?: {
2899
+ xPx: number;
2900
+ yPx: number;
2901
+ };
2902
+ rotation?: number;
2903
+ fill?: string;
2904
+ opacity?: number;
2905
+ wrap?: "inline" | "square" | "topAndBottom" | "none" | "behind";
2906
+ order?: "front" | "back";
2907
+ } | {
2908
+ type: "pageNumber";
2909
+ fieldKind: "page" | "pageOfTotal";
2910
+ align?: "left" | "center" | "right";
2911
+ color?: string;
2912
+ fontSizePt?: number;
2913
+ fontFamily?: string;
2914
+ bold?: boolean;
2915
+ } | {
2916
+ type: "pageBreak";
2917
+ };
2918
+ interface AgentComposeRequest {
2919
+ revision: string;
2920
+ body: AgentComposeBlock[];
2921
+ header?: AgentComposeBlock[];
2922
+ footer?: AgentComposeBlock[];
2923
+ firstHeader?: AgentComposeBlock[];
2924
+ firstFooter?: AgentComposeBlock[];
2925
+ page?: {
2926
+ margins?: {
2927
+ top?: number;
2928
+ right?: number;
2929
+ bottom?: number;
2930
+ left?: number;
2931
+ };
2932
+ size?: {
2933
+ width: number;
2934
+ height: number;
2935
+ };
2936
+ orientation?: "portrait" | "landscape";
2937
+ columns?: number;
2938
+ columnSeparator?: boolean;
2939
+ mirrorMargins?: boolean;
2940
+ };
2941
+ }
2942
+ interface AgentComposeResult {
2943
+ revision: string;
2944
+ status: "applied";
2945
+ blocks: number;
2946
+ components: number;
2947
+ overview: AgentOverview;
2948
+ createdObjects: AgentComponentSummary[];
2949
+ createdObjectsTruncated: boolean;
2950
+ }
2951
+ interface AgentCollaborativeTarget {
2952
+ getDocument(): DocxDocument | null;
2953
+ getRevision(): string | number;
2954
+ allocateIds(count: number): number[];
2955
+ submit(operation: IntentBody): void | Promise<void>;
2956
+ setPresence?(position: PresencePosition | null): void;
2957
+ uploadMedia?(bytes: Uint8Array): Promise<{
2958
+ blobSha: string;
2959
+ bytesLen: number;
2960
+ iv?: string;
2961
+ } | null>;
2962
+ getConnectionState?(): "local" | "live" | "offline" | "reconnecting";
2963
+ }
2964
+ interface AgentProvenance {
2965
+ author: string;
2966
+ now?: () => string;
2967
+ nextId?: () => string;
2968
+ }
2969
+ interface AgentTool {
2970
+ name: string;
2971
+ description: string;
2972
+ inputSchema: Record<string, unknown>;
2973
+ execute(input: unknown): Promise<unknown>;
2974
+ }
2975
+
2976
+ declare class AgentDocument {
2977
+ private localDoc;
2978
+ private localRevision;
2979
+ private readonly target;
2980
+ private readonly provenance;
2981
+ private readonly addedAssets;
2982
+ private assetCounter;
2983
+ private composeAvailable;
2984
+ private readonly listeners;
2985
+ private readonly inspectedTargets;
2986
+ private constructor();
2987
+ static load(bytes: ArrayBuffer | Uint8Array, options?: {
2988
+ provenance?: AgentProvenance;
2989
+ }): AgentDocument;
2990
+ static create(options?: {
2991
+ provenance?: AgentProvenance;
2992
+ }): AgentDocument;
2993
+ static connect(target: AgentCollaborativeTarget, options?: {
2994
+ provenance?: AgentProvenance;
2995
+ }): AgentDocument;
2996
+ get revision(): string;
2997
+ get document(): DocxDocument;
2998
+ subscribe(listener: (document: DocxDocument, revision: string) => void): () => void;
2999
+ addAsset(bytes: Uint8Array, mediaType: string): string;
3000
+ private inspector;
3001
+ asset(ref: string): AgentAsset;
3002
+ inspect(request: AgentInspectRequest): AgentInspectResult;
3003
+ private recordInspection;
3004
+ private revisionMatchesEdit;
3005
+ capabilities(category?: AgentEditCapability["category"], kind?: Intent["kind"]): ReturnType<typeof agentCapabilities>;
3006
+ compose(request: AgentComposeRequest): Promise<AgentComposeResult>;
3007
+ edit(request: AgentEditRequest): Promise<AgentEditResult>;
3008
+ private editLocal;
3009
+ private editCollaborative;
3010
+ save(): Uint8Array;
3011
+ tools(): AgentTool[];
3012
+ }
3013
+ interface CollabSessionLike {
3014
+ doc: DocxDocument | null;
3015
+ version: number;
3016
+ submitOp(operation: IntentBody): void;
3017
+ setPresence?(position: PresencePosition | null): void;
3018
+ allocIds(count: number): number[];
3019
+ uploadMedia?(bytes: Uint8Array): Promise<{
3020
+ blobSha: string;
3021
+ bytesLen: number;
3022
+ iv?: string;
3023
+ } | null>;
3024
+ connection: "live" | "reconnecting" | "lost";
3025
+ }
3026
+ /** Adapt the existing browser CollabSession without adding a React dependency.
3027
+ * The accessor must return the latest hook value after each render. */
3028
+ declare function collaborativeAgentTarget(getSession: () => CollabSessionLike): AgentCollaborativeTarget;
3029
+
3030
+ /**
3031
+ * A canonical document session for one process. It lets a browser editor and
3032
+ * an AgentDocument share one DocxDocument without a server or a DOM dependency.
3033
+ */
3034
+ declare class LocalDocumentSession implements AgentCollaborativeTarget {
3035
+ readonly doc: DocxDocument;
3036
+ private revision;
3037
+ private renderRevision;
3038
+ private clientSequence;
3039
+ private nextId;
3040
+ private dirtyScope;
3041
+ private readonly listeners;
3042
+ private readonly uploadedMedia;
3043
+ constructor(source: ArrayBuffer | Uint8Array | DocxDocument);
3044
+ get version(): number;
3045
+ get renderSignal(): number;
3046
+ getDocument(): DocxDocument;
3047
+ getRevision(): number;
3048
+ getConnectionState(): "local";
3049
+ allocateIds(count: number): number[];
3050
+ allocIds: (count: number) => number[];
3051
+ subscribe(listener: () => void): () => void;
3052
+ takeRenderScope: () => Scope | null;
3053
+ submit: (operation: IntentBody) => void;
3054
+ submitOp: (operation: IntentBody) => void;
3055
+ /** Record an operation that DocxView already applied to this same object. */
3056
+ submitPreApplied: (operation: IntentBody) => void;
3057
+ uploadMedia(bytes: Uint8Array): Promise<{
3058
+ blobSha: string;
3059
+ bytesLen: number;
3060
+ }>;
3061
+ private fullIntent;
3062
+ private installUploadedImage;
3063
+ }
3064
+ interface LocalDocumentViewBinding {
3065
+ subscribe(listener: () => void): () => void;
3066
+ getSnapshot(): number;
3067
+ doc: DocxDocument;
3068
+ submit(operation: IntentBody): void;
3069
+ submitOp(operation: IntentBody): void;
3070
+ allocIds(count: number): number[];
3071
+ uploadMedia(bytes: Uint8Array): Promise<{
3072
+ blobSha: string;
3073
+ bytesLen: number;
3074
+ }>;
3075
+ takeRenderScope(): {
3076
+ kind: "doc";
3077
+ } | {
3078
+ kind: "block";
3079
+ blocks: XmlElement[];
3080
+ } | {
3081
+ kind: "split";
3082
+ before: XmlElement;
3083
+ after: XmlElement;
3084
+ } | null;
3085
+ }
3086
+ declare function localDocumentViewBinding(session: LocalDocumentSession): LocalDocumentViewBinding;
3087
+
3088
+ type JsonSchema = Record<string, unknown>;
3089
+ declare const AGENT_COMPOSE_SCHEMA: JsonSchema;
3090
+ declare function validateAgentComposeRequest(input: unknown): string | null;
3091
+
3092
+ export { AGENT_COMPOSE_SCHEMA, AGENT_EDIT_CAPABILITIES, type AgentAsset, type AgentBlock, type AgentBorder, type AgentCollaborativeTarget, type AgentComponentSummary, type AgentComposeBlock, type AgentComposeCell, type AgentComposeRequest, type AgentComposeResult, type AgentComposeRun, type AgentComposeTextStyle, type AgentContextParagraph, type AgentContextResult, type AgentContextRun, type AgentContextStory, type AgentContextTable, type AgentCursor, AgentDocument, type AgentEditCapability, type AgentEditRequest, type AgentEditResult, type AgentInspectRequest, type AgentInspectResult, type AgentObjectResult, type AgentOperation, type AgentOverlap, type AgentOverview, type AgentPageBorders, type AgentParagraph, type AgentProvenance, type AgentReadResult, type AgentReference, type AgentRun, type AgentSearchResult, type AgentSectionLayout, type AgentSpatialObject, type AgentSpatialResult, type AgentTable, type AgentTool, LocalDocumentSession, type LocalDocumentViewBinding, type OutlineEntry, type SpatialBounds, agentCapabilities, agentOperationSchema, collaborativeAgentTarget, localDocumentViewBinding, validateAgentComposeRequest, validateAgentOperationShape };