@beyondwork/docx-react-component 1.0.102 → 1.0.104

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.
Files changed (57) hide show
  1. package/package.json +1 -1
  2. package/src/api/public-types.ts +63 -1
  3. package/src/api/v3/_runtime-handle.ts +2 -0
  4. package/src/api/v3/ai/outline.ts +2 -7
  5. package/src/api/v3/runtime/geometry.ts +79 -0
  6. package/src/core/commands/formatting-commands.ts +8 -7
  7. package/src/core/commands/paragraph-layout-commands.ts +11 -10
  8. package/src/core/commands/section-layout-commands.ts +7 -6
  9. package/src/core/commands/style-commands.ts +3 -2
  10. package/src/io/normalize/normalize-text.ts +6 -5
  11. package/src/io/ooxml/parse-anchor.ts +15 -15
  12. package/src/io/ooxml/parse-drawing.ts +103 -5
  13. package/src/io/ooxml/parse-fields.ts +43 -21
  14. package/src/io/ooxml/parse-font-table.ts +2 -1
  15. package/src/io/ooxml/parse-footnotes.ts +3 -2
  16. package/src/io/ooxml/parse-headers-footers.ts +7 -6
  17. package/src/io/ooxml/parse-main-document.ts +41 -40
  18. package/src/io/ooxml/parse-numbering.ts +3 -2
  19. package/src/io/ooxml/parse-object.ts +6 -6
  20. package/src/io/ooxml/parse-paragraph-formatting.ts +12 -11
  21. package/src/io/ooxml/parse-picture.ts +16 -16
  22. package/src/io/ooxml/parse-run-formatting.ts +11 -10
  23. package/src/io/ooxml/parse-settings.ts +2 -1
  24. package/src/io/ooxml/parse-shapes.ts +148 -17
  25. package/src/io/ooxml/parse-styles.ts +16 -16
  26. package/src/io/ooxml/parse-theme.ts +5 -4
  27. package/src/model/canonical-document.ts +869 -836
  28. package/src/model/canonical-layout-inputs.ts +979 -0
  29. package/src/model/layout/index.ts +6 -0
  30. package/src/model/layout/page-graph-types.ts +61 -0
  31. package/src/model/layout/runtime-page-graph-types.ts +10 -0
  32. package/src/runtime/collab/runtime-collab-sync.ts +3 -3
  33. package/src/runtime/debug/build-debug-inspector-snapshot.ts +17 -4
  34. package/src/runtime/document-runtime.ts +30 -14
  35. package/src/runtime/event-refresh-hints.ts +3 -0
  36. package/src/runtime/formatting/document-lookup.ts +3 -2
  37. package/src/runtime/formatting/formatting-context.ts +176 -34
  38. package/src/runtime/formatting/index.ts +20 -0
  39. package/src/runtime/formatting/layout-inputs.ts +320 -0
  40. package/src/runtime/formatting/numbering/geometry.ts +13 -12
  41. package/src/runtime/formatting/style-cascade.ts +2 -1
  42. package/src/runtime/formatting/table-style-resolver.ts +8 -7
  43. package/src/runtime/geometry/caret-geometry.ts +82 -10
  44. package/src/runtime/geometry/geometry-facet.ts +36 -0
  45. package/src/runtime/geometry/geometry-index.ts +891 -0
  46. package/src/runtime/geometry/geometry-types.ts +221 -1
  47. package/src/runtime/geometry/index.ts +26 -0
  48. package/src/runtime/geometry/inert-geometry-facet.ts +3 -0
  49. package/src/runtime/geometry/replacement-envelope.ts +41 -2
  50. package/src/runtime/layout/layout-engine-version.ts +16 -1
  51. package/src/runtime/layout/page-graph.ts +191 -1
  52. package/src/runtime/prerender/graph-canonicalize.ts +30 -0
  53. package/src/runtime/surface-projection.ts +74 -39
  54. package/src/runtime/workflow/coordinator.ts +57 -11
  55. package/src/session/import/normalize.ts +2 -1
  56. package/src/session/import/source-package-evidence.ts +612 -1
  57. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +3 -0
@@ -2,7 +2,12 @@ import type { OpcRelationship } from "./part-manifest.ts";
2
2
  import { normalizePartPath, resolveRelationshipTarget } from "./part-manifest.ts";
3
3
  import type { InlineMediaPart } from "./parse-inline-media.ts";
4
4
  import type { ChartPartLookup } from "./parse-complex-content.ts";
5
- import type { DrawingFrameNode, AnchorGeometry } from "../../model/canonical-document.ts";
5
+ import type {
6
+ AnchorGeometry,
7
+ DrawingFrameNode,
8
+ Mutable,
9
+ PreserveOnlyObjectSizing,
10
+ } from "../../model/canonical-document.ts";
6
11
  import { parseAnchorGeometry } from "./parse-anchor.ts";
7
12
  import { parsePicture } from "./parse-picture.ts";
8
13
  import { parseShapeContent, type TxbxBlockParser } from "./parse-shapes.ts";
@@ -144,6 +149,13 @@ function resolveFromBranch(
144
149
  if (uri === WPS_SHAPE_GRAPHIC_URI && isWordArtGraphicData(graphicData)) return null;
145
150
 
146
151
  const content = resolveContent(uri, graphicData, outerRawXml, opts);
152
+ attachPreserveOnlyObjectSizing(content, {
153
+ anchor: geometry,
154
+ container,
155
+ fallbackHint: fallbackHintForContent(content, geometry),
156
+ rawXml: outerRawXml,
157
+ sourcePartPath: opts.sourcePartPath,
158
+ });
147
159
  return { type: "drawing_frame", anchor: geometry, content };
148
160
  }
149
161
 
@@ -190,14 +202,14 @@ function resolveContent(
190
202
  resolveRelationshipTarget(opts.sourcePartPath ?? "/word/document.xml", rel),
191
203
  );
192
204
  const mediaPart = opts.mediaParts?.get(partPath);
193
- pic.packagePartName = partPath;
194
- pic.mediaId = `media:${partPath.slice(1)}`;
205
+ (pic as Mutable<typeof pic>).packagePartName = partPath;
206
+ (pic as Mutable<typeof pic>).mediaId = `media:${partPath.slice(1)}`;
195
207
  if (mediaPart?.contentType) {
196
- pic.contentType = mediaPart.contentType;
208
+ (pic as Mutable<typeof pic>).contentType = mediaPart.contentType;
197
209
  }
198
210
  }
199
211
  // F4.1 — preserve outer drawing XML for lossless round-trip serialization
200
- pic.rawXml = rawXml;
212
+ (pic as Mutable<typeof pic>).rawXml = rawXml;
201
213
  return pic;
202
214
  }
203
215
  }
@@ -231,6 +243,92 @@ function resolveContent(
231
243
  return { type: "opaque", rawXml };
232
244
  }
233
245
 
246
+ function attachPreserveOnlyObjectSizing(
247
+ content: DrawingFrameNode["content"],
248
+ context: {
249
+ anchor: AnchorGeometry;
250
+ container: XmlElementNode;
251
+ fallbackHint: PreserveOnlyObjectSizing["fallbackHint"] | null;
252
+ rawXml: string;
253
+ sourcePartPath: string | undefined;
254
+ },
255
+ ): void {
256
+ if (content.type === "picture" || context.fallbackHint === null) {
257
+ return;
258
+ }
259
+
260
+ (content as Mutable<typeof content>).preserveOnlyObject = {
261
+ sourceId: createPreserveOnlyObjectSourceId(
262
+ context.sourcePartPath,
263
+ context.anchor,
264
+ context.rawXml,
265
+ ),
266
+ display: context.anchor.display,
267
+ extentEmu: {
268
+ widthEmu: context.anchor.extent.widthEmu,
269
+ heightEmu: context.anchor.extent.heightEmu,
270
+ },
271
+ fallbackHint: context.fallbackHint,
272
+ relationshipIds: collectRelationshipIds(context.container),
273
+ };
274
+ }
275
+
276
+ function fallbackHintForContent(
277
+ content: DrawingFrameNode["content"],
278
+ anchor: AnchorGeometry,
279
+ ): PreserveOnlyObjectSizing["fallbackHint"] | null {
280
+ switch (content.type) {
281
+ case "picture":
282
+ return null;
283
+ case "chart_preview":
284
+ return "chart";
285
+ case "smartart_preview":
286
+ return "smartart";
287
+ case "shape":
288
+ return "shape";
289
+ case "opaque":
290
+ return anchor.display === "floating" ? "drawing-floating" : "drawing-inline";
291
+ }
292
+ }
293
+
294
+ function createPreserveOnlyObjectSourceId(
295
+ sourcePartPath: string | undefined,
296
+ anchor: AnchorGeometry,
297
+ rawXml: string,
298
+ ): string {
299
+ const part = sourcePartPath ?? "/word/document.xml";
300
+ const docPrId = anchor.docPr?.id;
301
+ if (docPrId) return `part:${part}#drawing-docPr:${docPrId}`;
302
+ return `part:${part}#drawing:${hashString(rawXml)}`;
303
+ }
304
+
305
+ function collectRelationshipIds(node: XmlElementNode): string[] {
306
+ const ids = new Set<string>();
307
+ const visit = (current: XmlElementNode): void => {
308
+ const id =
309
+ current.attributes["r:id"] ??
310
+ current.attributes["r:embed"] ??
311
+ current.attributes.embed ??
312
+ current.attributes["r:link"] ??
313
+ current.attributes.link;
314
+ if (id) ids.add(id);
315
+ for (const child of current.children) {
316
+ if (child.type === "element") visit(child);
317
+ }
318
+ };
319
+ visit(node);
320
+ return [...ids].sort();
321
+ }
322
+
323
+ function hashString(value: string): string {
324
+ let hash = 2166136261;
325
+ for (let index = 0; index < value.length; index += 1) {
326
+ hash ^= value.charCodeAt(index);
327
+ hash = Math.imul(hash, 16777619);
328
+ }
329
+ return (hash >>> 0).toString(16).padStart(8, "0");
330
+ }
331
+
234
332
  function extractChartRelId(graphicData: XmlElementNode | undefined): string | null {
235
333
  if (!graphicData) return null;
236
334
  const chart = findFirstDescendant(graphicData, "chart");
@@ -328,7 +328,13 @@ import type {
328
328
  TocInstructionModel,
329
329
  TocRegion,
330
330
  TocStructure,
331
+ Mutable,
331
332
  } from "../../model/canonical-document.ts";
333
+ import {
334
+ MAIN_STORY_KEY,
335
+ createHeaderFooterStoryKey,
336
+ createNoteStoryKey,
337
+ } from "../../model/canonical-layout-inputs.ts";
332
338
  import { parseFieldSwitches } from "./parse-field-switches.ts";
333
339
 
334
340
  const FIELD_FAMILY_PATTERN =
@@ -379,14 +385,14 @@ export function classifyFieldInstruction(instruction: string): {
379
385
  let switches: FieldNode["switches"] | undefined;
380
386
  if (family === "REF" || family === "PAGEREF" || family === "NOTEREF" || family === "TOC") {
381
387
  const raw = parseFieldSwitches(trimmed);
382
- const sw: FieldNode["switches"] = {};
383
- if (raw.hyperlink) sw.hyperlink = true;
384
- if (raw.relativePosition) sw.relativePosition = true;
385
- if (raw.numericRef) sw.numericRef = true;
386
- if (raw.recursive) sw.recursive = true;
387
- if (raw.suppressNonDelimiter) sw.suppressNonDelimiter = true;
388
- if (raw.includeNumbering) sw.includeNumbering = true;
389
- if (raw.includeLevel) sw.includeLevel = true;
388
+ const sw: Mutable<NonNullable<FieldNode["switches"]>> = {};
389
+ if (raw.hyperlink) (sw as Mutable<typeof sw>).hyperlink = true;
390
+ if (raw.relativePosition) (sw as Mutable<typeof sw>).relativePosition = true;
391
+ if (raw.numericRef) (sw as Mutable<typeof sw>).numericRef = true;
392
+ if (raw.recursive) (sw as Mutable<typeof sw>).recursive = true;
393
+ if (raw.suppressNonDelimiter) (sw as Mutable<typeof sw>).suppressNonDelimiter = true;
394
+ if (raw.includeNumbering) (sw as Mutable<typeof sw>).includeNumbering = true;
395
+ if (raw.includeLevel) (sw as Mutable<typeof sw>).includeLevel = true;
390
396
  if (Object.keys(sw).length > 0) {
391
397
  switches = sw;
392
398
  }
@@ -430,7 +436,7 @@ export function buildFieldRegistry(
430
436
  ? { family: node.fieldFamily, supported: isSupportedFieldFamily(node.fieldFamily), target: node.fieldTarget, switches: node.switches }
431
437
  : classifyFieldInstruction(node.instruction);
432
438
  const displayText = flattenFieldText(node.children);
433
- const entry: FieldRegistryEntry = {
439
+ const entry: Mutable<FieldRegistryEntry> = {
434
440
  fieldIndex,
435
441
  fieldFamily: classification.family,
436
442
  supported: classification.supported,
@@ -438,6 +444,7 @@ export function buildFieldRegistry(
438
444
  ...(classification.target ? { fieldTarget: classification.target } : {}),
439
445
  displayText,
440
446
  paragraphIndex,
447
+ storyKey: MAIN_STORY_KEY,
441
448
  refreshStatus: node.refreshStatus ?? (classification.supported ? "stale" : "preserve-only"),
442
449
  ...(classification.switches ? { switches: classification.switches } : {}),
443
450
  };
@@ -453,14 +460,14 @@ export function buildFieldRegistry(
453
460
  }
454
461
  });
455
462
  if (document.subParts) {
456
- walkSubPartFields(document.subParts, (node, pIdx) => {
463
+ walkSubPartFields(document.subParts, (node, pIdx, storyKey) => {
457
464
  paragraphIndex = pIdx;
458
465
  if (node.type === "field") {
459
466
  const classification = node.fieldFamily
460
467
  ? { family: node.fieldFamily, supported: isSupportedFieldFamily(node.fieldFamily), target: node.fieldTarget, switches: node.switches }
461
468
  : classifyFieldInstruction(node.instruction);
462
469
  const displayText = flattenFieldText(node.children);
463
- const entry: FieldRegistryEntry = {
470
+ const entry: Mutable<FieldRegistryEntry> = {
464
471
  fieldIndex,
465
472
  fieldFamily: classification.family,
466
473
  supported: classification.supported,
@@ -468,6 +475,7 @@ export function buildFieldRegistry(
468
475
  ...(classification.target ? { fieldTarget: classification.target } : {}),
469
476
  displayText,
470
477
  paragraphIndex,
478
+ storyKey,
471
479
  refreshStatus: node.refreshStatus ?? (classification.supported ? "stale" : "preserve-only"),
472
480
  ...(classification.switches ? { switches: classification.switches } : {}),
473
481
  };
@@ -512,7 +520,7 @@ export function parseTocLevelRange(instruction: string): { from: number; to: num
512
520
  }
513
521
 
514
522
  export function parseTocInstruction(instruction: string): TocInstructionModel {
515
- const model: TocInstructionModel = {
523
+ const model: Mutable<TocInstructionModel> = {
516
524
  raw: instruction,
517
525
  outlineRange: { from: 1, to: 9 },
518
526
  };
@@ -867,7 +875,7 @@ function tocLevelFromStyle(styleId: string | undefined): number {
867
875
  }
868
876
 
869
877
  function extractCachedTocEntry(
870
- paragraph: ParagraphNode,
878
+ paragraph: Mutable<ParagraphNode>,
871
879
  paragraphIndexByNode: WeakMap<ParagraphNode, number>,
872
880
  ): TocCachedEntry | undefined {
873
881
  if (!isTocParagraphStyle(paragraph.styleId)) {
@@ -1004,7 +1012,7 @@ export function resolveRefFieldText(
1004
1012
  * applies these to the live document.
1005
1013
  */
1006
1014
  export function refreshFieldRegistry(
1007
- registry: FieldRegistry,
1015
+ registry: Mutable<FieldRegistry>,
1008
1016
  bookmarkNameMap: Map<string, { bookmarkId: string; paragraphIndex: number }>,
1009
1017
  ): FieldRegistry {
1010
1018
  const refreshed: FieldRegistryEntry[] = registry.supported.map((entry) => {
@@ -1060,28 +1068,42 @@ function walkFieldDocument(
1060
1068
  }
1061
1069
 
1062
1070
  function walkSubPartFields(
1063
- subParts: SubPartsCatalog,
1064
- visit: (node: DocumentNode, paragraphIndex: number) => void,
1071
+ subParts: Mutable<SubPartsCatalog>,
1072
+ visit: (node: DocumentNode, paragraphIndex: number, storyKey: string) => void,
1065
1073
  ): void {
1066
1074
  for (const header of subParts.headers) {
1075
+ const storyKey = createHeaderFooterStoryKey({
1076
+ kind: "header",
1077
+ relationshipId: header.relationshipId,
1078
+ variant: header.variant,
1079
+ ...(header.sectionIndex !== undefined ? { sectionIndex: header.sectionIndex } : {}),
1080
+ });
1067
1081
  for (const block of header.blocks) {
1068
- walkFieldDocument(block, visit);
1082
+ walkFieldDocument(block, (node, paragraphIndex) => visit(node, paragraphIndex, storyKey));
1069
1083
  }
1070
1084
  }
1071
1085
  for (const footer of subParts.footers) {
1086
+ const storyKey = createHeaderFooterStoryKey({
1087
+ kind: "footer",
1088
+ relationshipId: footer.relationshipId,
1089
+ variant: footer.variant,
1090
+ ...(footer.sectionIndex !== undefined ? { sectionIndex: footer.sectionIndex } : {}),
1091
+ });
1072
1092
  for (const block of footer.blocks) {
1073
- walkFieldDocument(block, visit);
1093
+ walkFieldDocument(block, (node, paragraphIndex) => visit(node, paragraphIndex, storyKey));
1074
1094
  }
1075
1095
  }
1076
1096
  if (subParts.footnoteCollection) {
1077
1097
  for (const note of Object.values(subParts.footnoteCollection.footnotes)) {
1098
+ const storyKey = createNoteStoryKey("footnote", note.noteId);
1078
1099
  for (const block of note.blocks) {
1079
- walkFieldDocument(block, visit);
1100
+ walkFieldDocument(block, (node, paragraphIndex) => visit(node, paragraphIndex, storyKey));
1080
1101
  }
1081
1102
  }
1082
1103
  for (const note of Object.values(subParts.footnoteCollection.endnotes)) {
1104
+ const storyKey = createNoteStoryKey("endnote", note.noteId);
1083
1105
  for (const block of note.blocks) {
1084
- walkFieldDocument(block, visit);
1106
+ walkFieldDocument(block, (node, paragraphIndex) => visit(node, paragraphIndex, storyKey));
1085
1107
  }
1086
1108
  }
1087
1109
  }
@@ -1112,7 +1134,7 @@ function flattenParagraphInlineText(children: InlineNode[]): string {
1112
1134
  }
1113
1135
 
1114
1136
  function flattenBookmarkContent(
1115
- paragraph: ParagraphNode,
1137
+ paragraph: Mutable<ParagraphNode>,
1116
1138
  bookmarkId: string,
1117
1139
  ): string {
1118
1140
  let inside = false;
@@ -13,6 +13,7 @@
13
13
  import type {
14
14
  CanonicalFontEntry,
15
15
  CanonicalFontTable,
16
+ Mutable,
16
17
  } from "../../model/canonical-document.ts";
17
18
  import type { XmlElementNode } from "./xml-element.ts";
18
19
  import { parseXml } from "./xml-parser.ts";
@@ -32,7 +33,7 @@ export function parseFontTable(xml: string): CanonicalFontTable {
32
33
  const name = child.attributes["w:name"] ?? child.attributes["name"];
33
34
  if (!name) continue;
34
35
 
35
- const entry: CanonicalFontEntry = { name };
36
+ const entry: Mutable<CanonicalFontEntry> = { name };
36
37
 
37
38
  for (const sub of child.children) {
38
39
  if (sub.type !== "element") continue;
@@ -11,6 +11,7 @@ import type {
11
11
  TableNode,
12
12
  TableRowNode,
13
13
  TextMark,
14
+ Mutable,
14
15
  } from "../../model/canonical-document.ts";
15
16
  import { classifyFieldInstruction } from "./parse-fields.ts";
16
17
  import { isSafeTableFieldInstruction } from "./table-opaque-preservation.ts";
@@ -644,7 +645,7 @@ function parseRunProperties(rElement: XmlElementNode): TextMark[] {
644
645
  function readParagraphSpacing(pPr: XmlElementNode): ParagraphSpacing | undefined {
645
646
  const spacingNode = findChildElementOptional(pPr, "spacing");
646
647
  if (!spacingNode) return undefined;
647
- const result: ParagraphSpacing = {};
648
+ const result: Mutable<ParagraphSpacing> = {};
648
649
  const before = readStringAttr(spacingNode, "w:before");
649
650
  if (before) result.before = Number.parseInt(before, 10);
650
651
  const after = readStringAttr(spacingNode, "w:after");
@@ -661,7 +662,7 @@ function readParagraphSpacing(pPr: XmlElementNode): ParagraphSpacing | undefined
661
662
  function readParagraphIndentation(pPr: XmlElementNode): ParagraphIndentation | undefined {
662
663
  const indNode = findChildElementOptional(pPr, "ind");
663
664
  if (!indNode) return undefined;
664
- const result: ParagraphIndentation = {};
665
+ const result: Mutable<ParagraphIndentation> = {};
665
666
  const left = readStringAttr(indNode, "w:left");
666
667
  if (left) result.left = Number.parseInt(left, 10);
667
668
  const right = readStringAttr(indNode, "w:right");
@@ -11,6 +11,7 @@ import type {
11
11
  TableNode,
12
12
  TableRowNode,
13
13
  TextMark,
14
+ Mutable,
14
15
  } from "../../model/canonical-document.ts";
15
16
  import type { LegacyFormFieldNode } from "../../model/canonical-document.ts";
16
17
  import { resolveHighlightColor } from "./highlight-colors.ts";
@@ -500,7 +501,7 @@ function parseRunElement(
500
501
  const noteId =
501
502
  readStringAttr(child, "w:id") ?? "";
502
503
  if (noteId) {
503
- const ref: FootnoteRefNode = {
504
+ const ref: Mutable<FootnoteRefNode> = {
504
505
  type: "footnote_ref",
505
506
  noteId,
506
507
  noteKind: "footnote",
@@ -511,7 +512,7 @@ function parseRunElement(
511
512
  const noteId =
512
513
  readStringAttr(child, "w:id") ?? "";
513
514
  if (noteId) {
514
- const ref: FootnoteRefNode = {
515
+ const ref: Mutable<FootnoteRefNode> = {
515
516
  type: "footnote_ref",
516
517
  noteId,
517
518
  noteKind: "endnote",
@@ -594,7 +595,7 @@ function parseRunChildNode(
594
595
  const noteId =
595
596
  readStringAttr(child, "w:id") ?? "";
596
597
  if (noteId) {
597
- const ref: FootnoteRefNode = {
598
+ const ref: Mutable<FootnoteRefNode> = {
598
599
  type: "footnote_ref",
599
600
  noteId,
600
601
  noteKind: "footnote",
@@ -607,7 +608,7 @@ function parseRunChildNode(
607
608
  const noteId =
608
609
  readStringAttr(child, "w:id") ?? "";
609
610
  if (noteId) {
610
- const ref: FootnoteRefNode = {
611
+ const ref: Mutable<FootnoteRefNode> = {
611
612
  type: "footnote_ref",
612
613
  noteId,
613
614
  noteKind: "endnote",
@@ -970,7 +971,7 @@ function parseRunProperties(rElement: XmlElementNode): TextMark[] {
970
971
  function readParagraphSpacing(pPr: XmlElementNode): ParagraphSpacing | undefined {
971
972
  const spacingNode = findChildElementOptional(pPr, "spacing");
972
973
  if (!spacingNode) return undefined;
973
- const result: ParagraphSpacing = {};
974
+ const result: Mutable<ParagraphSpacing> = {};
974
975
  const before = readStringAttr(spacingNode, "w:before");
975
976
  if (before) result.before = Number.parseInt(before, 10);
976
977
  const after = readStringAttr(spacingNode, "w:after");
@@ -987,7 +988,7 @@ function readParagraphSpacing(pPr: XmlElementNode): ParagraphSpacing | undefined
987
988
  function readParagraphIndentation(pPr: XmlElementNode): ParagraphIndentation | undefined {
988
989
  const indNode = findChildElementOptional(pPr, "ind");
989
990
  if (!indNode) return undefined;
990
- const result: ParagraphIndentation = {};
991
+ const result: Mutable<ParagraphIndentation> = {};
991
992
  const left = readStringAttr(indNode, "w:left");
992
993
  if (left) result.left = Number.parseInt(left, 10);
993
994
  const right = readStringAttr(indNode, "w:right");
@@ -26,6 +26,7 @@ import type {
26
26
  SectionPageBorders,
27
27
  DrawingFrameNode,
28
28
  UnknownPropertyChild,
29
+ Mutable,
29
30
  } from "../../model/canonical-document.ts";
30
31
  import type { OpcRelationship } from "./part-manifest.ts";
31
32
  import { SCOPE_MARKER_BOOKMARK_PREFIX } from "./parse-scope-markers.ts";
@@ -2288,7 +2289,7 @@ function readParagraphSpacing(node: XmlElementNode): ParagraphSpacing | undefine
2288
2289
  );
2289
2290
  if (!spacingNode) return undefined;
2290
2291
 
2291
- const spacing: ParagraphSpacing = {};
2292
+ const spacing: Mutable<ParagraphSpacing> = {};
2292
2293
  const before = spacingNode.attributes["w:before"] ?? spacingNode.attributes.before;
2293
2294
  const after = spacingNode.attributes["w:after"] ?? spacingNode.attributes.after;
2294
2295
  const line = spacingNode.attributes["w:line"] ?? spacingNode.attributes.line;
@@ -2332,7 +2333,7 @@ function readParagraphIndentation(node: XmlElementNode): ParagraphIndentation |
2332
2333
  );
2333
2334
  if (!indNode) return undefined;
2334
2335
 
2335
- const indentation: ParagraphIndentation = {};
2336
+ const indentation: Mutable<ParagraphIndentation> = {};
2336
2337
  const left = indNode.attributes["w:left"] ?? indNode.attributes.left;
2337
2338
  const right = indNode.attributes["w:right"] ?? indNode.attributes.right;
2338
2339
  const firstLine = indNode.attributes["w:firstLine"] ?? indNode.attributes.firstLine;
@@ -2450,7 +2451,7 @@ function readParagraphBorders(node: XmlElementNode): ParagraphBorders | undefine
2450
2451
  return undefined;
2451
2452
  }
2452
2453
 
2453
- const borders: ParagraphBorders = {};
2454
+ const borders: Mutable<ParagraphBorders> = {};
2454
2455
  for (const [name, key] of [
2455
2456
  ["top", "top"],
2456
2457
  ["left", "left"],
@@ -2481,7 +2482,7 @@ function readParagraphShading(node: XmlElementNode): ParagraphShading | undefine
2481
2482
  return undefined;
2482
2483
  }
2483
2484
 
2484
- const shading: ParagraphShading = {};
2485
+ const shading: Mutable<ParagraphShading> = {};
2485
2486
  const fill = shadingNode.attributes["w:fill"] ?? shadingNode.attributes.fill;
2486
2487
  const color = shadingNode.attributes["w:color"] ?? shadingNode.attributes.color;
2487
2488
  const val = shadingNode.attributes["w:val"] ?? shadingNode.attributes.val;
@@ -2491,14 +2492,14 @@ function readParagraphShading(node: XmlElementNode): ParagraphShading | undefine
2491
2492
  const themeColor = shadingNode.attributes["w:themeColor"] ?? shadingNode.attributes.themeColor;
2492
2493
  const themeColorTint = shadingNode.attributes["w:themeColorTint"] ?? shadingNode.attributes.themeColorTint;
2493
2494
  const themeColorShade = shadingNode.attributes["w:themeColorShade"] ?? shadingNode.attributes.themeColorShade;
2494
- if (fill) shading.fill = fill;
2495
- if (color) shading.color = color;
2496
- if (val) shading.val = val;
2497
- if (themeFill) shading.themeFill = themeFill;
2498
- if (themeFillTint) shading.themeFillTint = themeFillTint;
2499
- if (themeFillShade) shading.themeFillShade = themeFillShade;
2500
- if (themeColor) shading.themeColor = themeColor;
2501
- if (themeColorTint) shading.themeColorTint = themeColorTint;
2495
+ if (fill) (shading as Mutable<typeof shading>).fill = fill;
2496
+ if (color) (shading as Mutable<typeof shading>).color = color;
2497
+ if (val) (shading as Mutable<typeof shading>).val = val;
2498
+ if (themeFill) (shading as Mutable<typeof shading>).themeFill = themeFill;
2499
+ if (themeFillTint) (shading as Mutable<typeof shading>).themeFillTint = themeFillTint;
2500
+ if (themeFillShade) (shading as Mutable<typeof shading>).themeFillShade = themeFillShade;
2501
+ if (themeColor) (shading as Mutable<typeof shading>).themeColor = themeColor;
2502
+ if (themeColorTint) (shading as Mutable<typeof shading>).themeColorTint = themeColorTint;
2502
2503
  if (themeColorShade) shading.themeColorShade = themeColorShade;
2503
2504
  return Object.keys(shading).length > 0 ? shading : undefined;
2504
2505
  }
@@ -2511,7 +2512,7 @@ function readParagraphCnfStyle(node: XmlElementNode): string | undefined {
2511
2512
  }
2512
2513
 
2513
2514
  function readBorder(node: XmlElementNode): ParagraphBorders[keyof ParagraphBorders] {
2514
- const border: NonNullable<ParagraphBorders[keyof ParagraphBorders]> = {};
2515
+ const border: Mutable<NonNullable<ParagraphBorders[keyof ParagraphBorders]>> = {};
2515
2516
  const value = node.attributes["w:val"] ?? node.attributes.val;
2516
2517
  const size = node.attributes["w:sz"] ?? node.attributes.sz;
2517
2518
  const space = node.attributes["w:space"] ?? node.attributes.space;
@@ -3632,7 +3633,7 @@ function readSectionPropertiesXmlFromPPr(
3632
3633
  export function parseSectionPropertiesFromElement(
3633
3634
  node: XmlElementNode,
3634
3635
  ): SectionProperties {
3635
- const props: SectionProperties = {};
3636
+ const props: Mutable<SectionProperties> = {};
3636
3637
 
3637
3638
  for (const child of node.children) {
3638
3639
  if (child.type !== "element") continue;
@@ -3643,7 +3644,7 @@ export function parseSectionPropertiesFromElement(
3643
3644
  const w = safeParseInt(child.attributes["w:w"]);
3644
3645
  const h = safeParseInt(child.attributes["w:h"]);
3645
3646
  if (w !== undefined && h !== undefined) {
3646
- const pageSize: PageSize = { width: w, height: h };
3647
+ const pageSize: Mutable<PageSize> = { width: w, height: h };
3647
3648
  const orient = child.attributes["w:orient"];
3648
3649
  if (orient === "landscape" || orient === "portrait") {
3649
3650
  pageSize.orientation = orient;
@@ -3658,26 +3659,26 @@ export function parseSectionPropertiesFromElement(
3658
3659
  const bottom = safeParseInt(child.attributes["w:bottom"]);
3659
3660
  const left = safeParseInt(child.attributes["w:left"]);
3660
3661
  if (top !== undefined && right !== undefined && bottom !== undefined && left !== undefined) {
3661
- const margins: PageMargins = { top, right, bottom, left };
3662
+ const margins: Mutable<PageMargins> = { top, right, bottom, left };
3662
3663
  const header = safeParseInt(child.attributes["w:header"]);
3663
3664
  const footer = safeParseInt(child.attributes["w:footer"]);
3664
3665
  const gutter = safeParseInt(child.attributes["w:gutter"]);
3665
- if (header !== undefined) margins.header = header;
3666
- if (footer !== undefined) margins.footer = footer;
3667
- if (gutter !== undefined) margins.gutter = gutter;
3666
+ if (header !== undefined) (margins as Mutable<typeof margins>).header = header;
3667
+ if (footer !== undefined) (margins as Mutable<typeof margins>).footer = footer;
3668
+ if (gutter !== undefined) (margins as Mutable<typeof margins>).gutter = gutter;
3668
3669
  props.pageMargins = margins;
3669
3670
  }
3670
3671
  break;
3671
3672
  }
3672
3673
  case "cols": {
3673
- const columns: ColumnProperties = {};
3674
+ const columns: Mutable<ColumnProperties> = {};
3674
3675
  const num = safeParseInt(child.attributes["w:num"]);
3675
3676
  const space = safeParseInt(child.attributes["w:space"]);
3676
3677
  const equalWidth = child.attributes["w:equalWidth"];
3677
3678
  const sep = child.attributes["w:sep"];
3678
- if (num !== undefined) columns.count = num;
3679
- if (space !== undefined) columns.space = space;
3680
- if (equalWidth !== undefined) columns.equalWidth = equalWidth !== "0" && equalWidth !== "false";
3679
+ if (num !== undefined) (columns as Mutable<typeof columns>).count = num;
3680
+ if (space !== undefined) (columns as Mutable<typeof columns>).space = space;
3681
+ if (equalWidth !== undefined) (columns as Mutable<typeof columns>).equalWidth = equalWidth !== "0" && equalWidth !== "false";
3681
3682
  if (sep === "1" || sep === "true") columns.separator = true;
3682
3683
  const colDefs: Array<{ width: number; space?: number }> = [];
3683
3684
  for (const colChild of child.children) {
@@ -3689,31 +3690,31 @@ export function parseSectionPropertiesFromElement(
3689
3690
  }
3690
3691
  }
3691
3692
  }
3692
- if (colDefs.length > 0) columns.columns = colDefs;
3693
+ if (colDefs.length > 0) (columns as Mutable<typeof columns>).columns = colDefs;
3693
3694
  if (Object.keys(columns).length > 0) props.columns = columns;
3694
3695
  break;
3695
3696
  }
3696
3697
  case "pgNumType": {
3697
- const numbering: PageNumbering = {};
3698
+ const numbering: Mutable<PageNumbering> = {};
3698
3699
  const fmt = child.attributes["w:fmt"];
3699
3700
  const start = safeParseInt(child.attributes["w:start"]);
3700
3701
  const chapStyle = child.attributes["w:chapStyle"];
3701
3702
  const chapSep = child.attributes["w:chapSep"];
3702
- if (fmt) numbering.format = fmt;
3703
- if (start !== undefined) numbering.start = start;
3704
- if (chapStyle) numbering.chapStyle = chapStyle;
3705
- if (chapSep) numbering.chapSep = chapSep;
3703
+ if (fmt) (numbering as Mutable<typeof numbering>).format = fmt;
3704
+ if (start !== undefined) (numbering as Mutable<typeof numbering>).start = start;
3705
+ if (chapStyle) (numbering as Mutable<typeof numbering>).chapStyle = chapStyle;
3706
+ if (chapSep) (numbering as Mutable<typeof numbering>).chapSep = chapSep;
3706
3707
  if (Object.keys(numbering).length > 0) props.pageNumbering = numbering;
3707
3708
  break;
3708
3709
  }
3709
3710
  case "lnNumType": {
3710
- const lineNumbering: SectionLineNumbering = {};
3711
+ const lineNumbering: Mutable<SectionLineNumbering> = {};
3711
3712
  const countBy = safeParseInt(child.attributes["w:countBy"]);
3712
3713
  const start = safeParseInt(child.attributes["w:start"]);
3713
3714
  const distance = safeParseInt(child.attributes["w:distance"]);
3714
3715
  const restart = child.attributes["w:restart"];
3715
- if (countBy !== undefined) lineNumbering.countBy = countBy;
3716
- if (start !== undefined) lineNumbering.start = start;
3716
+ if (countBy !== undefined) (lineNumbering as Mutable<typeof lineNumbering>).countBy = countBy;
3717
+ if (start !== undefined) (lineNumbering as Mutable<typeof lineNumbering>).start = start;
3717
3718
  if (distance !== undefined) lineNumbering.distance = distance;
3718
3719
  if (
3719
3720
  restart === "newPage" ||
@@ -3728,7 +3729,7 @@ export function parseSectionPropertiesFromElement(
3728
3729
  break;
3729
3730
  }
3730
3731
  case "pgBorders": {
3731
- const pageBorders: SectionPageBorders = {};
3732
+ const pageBorders: Mutable<SectionPageBorders> = {};
3732
3733
  const offsetFrom = child.attributes["w:offsetFrom"];
3733
3734
  const display = child.attributes["w:display"];
3734
3735
  const zOrder = child.attributes["w:zOrder"];
@@ -3765,7 +3766,7 @@ export function parseSectionPropertiesFromElement(
3765
3766
  break;
3766
3767
  }
3767
3768
  case "docGrid": {
3768
- const documentGrid: SectionDocumentGrid = {};
3769
+ const documentGrid: Mutable<SectionDocumentGrid> = {};
3769
3770
  const type = child.attributes["w:type"];
3770
3771
  const linePitch = safeParseInt(child.attributes["w:linePitch"]);
3771
3772
  const charSpace = safeParseInt(child.attributes["w:charSpace"]);
@@ -3777,7 +3778,7 @@ export function parseSectionPropertiesFromElement(
3777
3778
  ) {
3778
3779
  documentGrid.type = type;
3779
3780
  }
3780
- if (linePitch !== undefined) documentGrid.linePitch = linePitch;
3781
+ if (linePitch !== undefined) (documentGrid as Mutable<typeof documentGrid>).linePitch = linePitch;
3781
3782
  if (charSpace !== undefined) documentGrid.charSpace = charSpace;
3782
3783
  if (Object.keys(documentGrid).length > 0) {
3783
3784
  props.documentGrid = documentGrid;
@@ -3856,7 +3857,7 @@ function safeParseInt(value: string | undefined): number | undefined {
3856
3857
  function readFootnoteLikeProperties(
3857
3858
  node: XmlElementNode,
3858
3859
  ): FootnoteProperties | undefined {
3859
- const result: FootnoteProperties = {};
3860
+ const result: Mutable<FootnoteProperties> = {};
3860
3861
 
3861
3862
  for (const child of node.children) {
3862
3863
  if (child.type !== "element") continue;
@@ -3902,15 +3903,15 @@ function readFootnoteLikeProperties(
3902
3903
  }
3903
3904
 
3904
3905
  function parseBorderSpec(node: XmlElementNode): BorderSpec | undefined {
3905
- const border: BorderSpec = {};
3906
+ const border: Mutable<BorderSpec> = {};
3906
3907
  const value = node.attributes["w:val"];
3907
3908
  const size = safeParseInt(node.attributes["w:sz"]);
3908
3909
  const space = safeParseInt(node.attributes["w:space"]);
3909
3910
  const color = node.attributes["w:color"];
3910
3911
 
3911
- if (value) border.value = value;
3912
- if (size !== undefined) border.size = size;
3913
- if (space !== undefined) border.space = space;
3912
+ if (value) (border as Mutable<typeof border>).value = value;
3913
+ if (size !== undefined) (border as Mutable<typeof border>).size = size;
3914
+ if (space !== undefined) (border as Mutable<typeof border>).space = space;
3914
3915
  if (color) border.color = color;
3915
3916
 
3916
3917
  return Object.keys(border).length > 0 ? border : undefined;
@@ -8,6 +8,7 @@ import type {
8
8
  ParagraphSpacing,
9
9
  ParagraphIndentation,
10
10
  TabStop,
11
+ Mutable,
11
12
  } from "../../model/canonical-document.ts";
12
13
  import { readRunProperties } from "./parse-run-formatting.ts";
13
14
  import type { OpcRelationship } from "./part-manifest.ts";
@@ -519,7 +520,7 @@ function readParagraphIndentation(node: XmlElementNode): ParagraphIndentation |
519
520
  return undefined;
520
521
  }
521
522
 
522
- const indentation: ParagraphIndentation = {};
523
+ const indentation: Mutable<ParagraphIndentation> = {};
523
524
  const left = readStringAttr(indNode, "w:start") ?? readStringAttr(indNode, "w:left");
524
525
  const right = readStringAttr(indNode, "w:end") ?? readStringAttr(indNode, "w:right");
525
526
  const firstLine = readStringAttr(indNode, "w:firstLine");
@@ -560,7 +561,7 @@ function readParagraphSpacing(node: XmlElementNode): ParagraphSpacing | undefine
560
561
  return undefined;
561
562
  }
562
563
 
563
- const spacing: ParagraphSpacing = {};
564
+ const spacing: Mutable<ParagraphSpacing> = {};
564
565
  const before = readStringAttr(spacingNode, "w:before");
565
566
  const after = readStringAttr(spacingNode, "w:after");
566
567
  const line = readStringAttr(spacingNode, "w:line");