@stll/folio-core 0.12.0 → 0.14.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.
Files changed (138) hide show
  1. package/dist/ai-edits/apply.d.ts +2 -1
  2. package/dist/ai-edits/apply.js +118 -40
  3. package/dist/ai-edits/blockRange.js +1 -1
  4. package/dist/ai-edits/clean-text.js +2 -1
  5. package/dist/ai-edits/headless.js +3 -3
  6. package/dist/ai-edits/snapshot.js +21 -3
  7. package/dist/ai-edits/table-cell-mutations.js +2 -1
  8. package/dist/ai-edits/table-row-column-mutations.d.ts +9 -1
  9. package/dist/ai-edits/table-row-column-mutations.js +1 -1
  10. package/dist/ai-edits/types.d.ts +28 -1
  11. package/dist/ai-edits/word-diff.js +21 -0
  12. package/dist/compat/eigenpal.d.ts +3 -3
  13. package/dist/compat/eigenpal.js +3 -3
  14. package/dist/controller/headerFooterEditorManager.js +1 -1
  15. package/dist/controller/hiddenEditorManager.js +12 -1
  16. package/dist/controller/layoutPipeline.js +1 -1
  17. package/dist/controller/noteEditorManager.js +1 -1
  18. package/dist/document-operations.d.ts +13 -13
  19. package/dist/document-operations.js +31 -21
  20. package/dist/docx/blockContentParser.js +19 -5
  21. package/dist/docx/commentRangeIntegrity.d.ts +6 -0
  22. package/dist/docx/commentRangeIntegrity.js +186 -0
  23. package/dist/docx/conformance.js +57 -3
  24. package/dist/docx/encryption/agileDecryption.js +12 -2
  25. package/dist/docx/encryption/compoundFile.js +6 -1
  26. package/dist/docx/encryption/encryptionInfo.d.ts +9 -1
  27. package/dist/docx/encryption/encryptionInfo.js +18 -2
  28. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  29. package/dist/docx/ensureParaIds.js +7 -6
  30. package/dist/docx/groupDrawingParser.js +10 -5
  31. package/dist/docx/hyperlinkParser.js +1 -1
  32. package/dist/docx/imageParser.js +10 -6
  33. package/dist/docx/metadataPrivacy.js +17 -3
  34. package/dist/docx/paragraphParser.js +7 -3
  35. package/dist/docx/rezip.js +19 -8
  36. package/dist/docx/runParser.js +13 -4
  37. package/dist/docx/sectionParser.js +17 -4
  38. package/dist/docx/selectiveSave.js +4 -0
  39. package/dist/docx/selectiveXmlPatch.d.ts +22 -1
  40. package/dist/docx/selectiveXmlPatch.js +61 -1
  41. package/dist/docx/serializer/blockSdtSerializer.js +3 -2
  42. package/dist/docx/serializer/commentSerializer.js +4 -4
  43. package/dist/docx/serializer/paragraphSerializer.js +16 -14
  44. package/dist/docx/serializer/runSerializer.js +21 -19
  45. package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -1
  46. package/dist/docx/serializer/tableSerializer.js +9 -7
  47. package/dist/docx/serializer/xmlUtils.d.ts +13 -1
  48. package/dist/docx/serializer/xmlUtils.js +27 -1
  49. package/dist/docx/server/docxToMarkdown.d.ts +15 -0
  50. package/dist/docx/server/docxToMarkdown.js +17 -0
  51. package/dist/docx/server/extractDocxText.js +55 -8
  52. package/dist/docx/settingsParser.js +15 -2
  53. package/dist/docx/tableParser.d.ts +8 -1
  54. package/dist/docx/tableParser.js +29 -8
  55. package/dist/docx/unzip.js +1 -0
  56. package/dist/docx/vmlImageParser.js +11 -3
  57. package/dist/docx/xmlParser.js +11 -1
  58. package/dist/fonts/embeddedFonts.d.ts +33 -5
  59. package/dist/fonts/embeddedFonts.js +37 -8
  60. package/dist/index.d.ts +3 -3
  61. package/dist/index.js +3 -3
  62. package/dist/layout-bridge/convert/toFlowBlocks.js +15 -0
  63. package/dist/layout-engine/index.js +19 -12
  64. package/dist/layout-engine/measure/cache.js +2 -0
  65. package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +8 -2
  66. package/dist/layout-engine/measure/floatingTablePosition.d.ts +9 -0
  67. package/dist/layout-engine/measure/floatingTablePosition.js +17 -0
  68. package/dist/layout-engine/measure/index.d.ts +2 -1
  69. package/dist/layout-engine/measure/index.js +2 -1
  70. package/dist/layout-engine/measure/lineBreakProvider.d.ts +2 -2
  71. package/dist/layout-engine/measure/lineBreakProvider.js +3 -3
  72. package/dist/layout-engine/measure/measureBlocks.js +11 -8
  73. package/dist/layout-engine/measure/measureHelpers.d.ts +10 -1
  74. package/dist/layout-engine/measure/measureHelpers.js +12 -1
  75. package/dist/layout-engine/measure/measureParagraph.js +16 -0
  76. package/dist/layout-engine/types.d.ts +22 -2
  77. package/dist/layout-painter/renderImage.d.ts +12 -1
  78. package/dist/layout-painter/renderImage.js +21 -4
  79. package/dist/layout-painter/renderPage.js +5 -8
  80. package/dist/layout-painter/renderParagraph.js +33 -9
  81. package/dist/layout-painter/renderTable.js +2 -1
  82. package/dist/layout-painter/renderWatermark.d.ts +1 -1
  83. package/dist/layout-painter/renderWatermark.js +2 -1
  84. package/dist/markdown/fromMarkdown.js +1 -1
  85. package/dist/prosemirror/attrs/index.d.ts +1 -1
  86. package/dist/prosemirror/attrs/index.js +21 -0
  87. package/dist/prosemirror/commands/comments.d.ts +76 -1
  88. package/dist/prosemirror/commands/comments.js +401 -28
  89. package/dist/prosemirror/commands/hyperlink.js +15 -1
  90. package/dist/prosemirror/commands/image.js +7 -1
  91. package/dist/prosemirror/commands/index.d.ts +2 -2
  92. package/dist/prosemirror/commands/index.js +2 -2
  93. package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
  94. package/dist/prosemirror/commentIdAllocator.js +13 -3
  95. package/dist/prosemirror/conversion/fromProseDoc.js +122 -5
  96. package/dist/prosemirror/conversion/index.js +1 -1
  97. package/dist/prosemirror/conversion/toProseDoc.js +20 -8
  98. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
  99. package/dist/prosemirror/extensions/features/ImagePasteExtension.js +10 -2
  100. package/dist/prosemirror/extensions/features/pasteCleanup.js +15 -2
  101. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +30 -4
  102. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +49 -13
  103. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  104. package/dist/prosemirror/extensions/nodes/TableExtension.js +5 -3
  105. package/dist/prosemirror/index.d.ts +1 -1
  106. package/dist/prosemirror/index.js +1 -1
  107. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +2 -2
  108. package/dist/prosemirror/plugins/contentControlWidgets.js +4 -4
  109. package/dist/prosemirror/plugins/revisionIds.d.ts +23 -0
  110. package/dist/prosemirror/plugins/revisionIds.js +57 -0
  111. package/dist/prosemirror/plugins/suggestionMode.js +4 -3
  112. package/dist/prosemirror/runFormattingMarkNames.d.ts +14 -0
  113. package/dist/prosemirror/runFormattingMarkNames.js +40 -0
  114. package/dist/prosemirror/schema/index.d.ts +1 -1
  115. package/dist/prosemirror/schema/marks.d.ts +27 -4
  116. package/dist/prosemirror/schema/marks.js +15 -0
  117. package/dist/prosemirror/schema/nodes.d.ts +50 -2
  118. package/dist/prosemirror/utils/tabCalculator.js +3 -3
  119. package/dist/render-dom/RemoteSelectionOverlay.js +2 -2
  120. package/dist/server.d.ts +3 -2
  121. package/dist/server.js +4 -3
  122. package/dist/utils/clipboard.d.ts +10 -1
  123. package/dist/utils/clipboard.js +33 -6
  124. package/dist/utils/colorResolver.d.ts +8 -1
  125. package/dist/utils/colorResolver.js +35 -5
  126. package/dist/utils/fontResolver.d.ts +2 -1
  127. package/dist/utils/fontResolver.js +9 -3
  128. package/dist/utils/hexId.d.ts +11 -1
  129. package/dist/utils/hexId.js +11 -1
  130. package/dist/utils/sanitizeImageSrc.d.ts +21 -0
  131. package/dist/utils/sanitizeImageSrc.js +30 -0
  132. package/dist/utils/stripXmlDeclarations.d.ts +11 -0
  133. package/dist/utils/stripXmlDeclarations.js +35 -0
  134. package/dist/version-comparison.d.ts +22 -2
  135. package/dist/version-comparison.js +41 -12
  136. package/dist/watermark/index.d.ts +11 -1
  137. package/dist/watermark/index.js +22 -1
  138. package/package.json +3 -2
@@ -20,7 +20,8 @@ type ApplyFolioAIEditOperationsOptions = {
20
20
  snapshot: FolioAIEditSnapshot;
21
21
  operations: FolioAIEditOperation[];
22
22
  mode?: FolioAIEditApplyMode;
23
- author?: string;
23
+ author?: string; /** Optional author initials (w:initials) stamped alongside the author. */
24
+ initials?: string;
24
25
  createCommentId?: (text: string) => number;
25
26
  };
26
27
  declare const applyFolioAIEditOperations: (options: ApplyFolioAIEditOperationsOptions) => FolioAIEditApplyResult;
@@ -12,6 +12,27 @@ import { applyTableColumnDeletion, applyTableColumnInsertion, applyTableRowDelet
12
12
  import { diffWordSegments } from "./word-diff.js";
13
13
  import { TableMap } from "prosemirror-tables";
14
14
  //#region src/ai-edits/apply.ts
15
+ /**
16
+ * Operation types applied in `"suggested"` mode. Every produced revision — an
17
+ * inline mark, a whole-node `_suggestedInsert` marker, or a suggested
18
+ * `trIns`/`trDel`/`cellMarker` — is stripped from serialized DOCX until
19
+ * accepted. Cell merge/split (not row/column ops) and comment ops stay
20
+ * `unsupportedMode`.
21
+ */
22
+ const SUGGESTED_SUPPORTED_OPERATION_TYPES = /* @__PURE__ */ new Set([
23
+ "replaceInBlock",
24
+ "replaceRange",
25
+ "formatRange",
26
+ "replaceBlock",
27
+ "deleteBlock",
28
+ "insertAfterBlock",
29
+ "insertBeforeBlock",
30
+ "insertSignatureTable",
31
+ "insertTableRow",
32
+ "deleteTableRow",
33
+ "insertTableColumn",
34
+ "deleteTableColumn"
35
+ ]);
15
36
  const applyReplaceBlockStyleId = ({ item, tr }) => {
16
37
  if (item.operation.type !== "replaceBlock" || item.operation.styleId === void 0) return tr;
17
38
  const block = tr.doc.nodeAt(item.blockFrom);
@@ -39,7 +60,7 @@ const formattingWouldChange = (marks, formatting) => Object.entries(formatting).
39
60
  if (name === "underline") return mark?.attrs["style"] !== "single";
40
61
  return mark === void 0;
41
62
  });
42
- const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, revisionId, author, date }) => {
63
+ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, revisionId, author, date, initials, suggestionId = null }) => {
43
64
  const propertyChangeType = schema.marks["runPropertyChange"];
44
65
  if (!propertyChangeType) return tr;
45
66
  const segments = [];
@@ -55,7 +76,8 @@ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, r
55
76
  info: {
56
77
  id: revisionId,
57
78
  author,
58
- date
79
+ date,
80
+ ...initials ? { initials } : {}
59
81
  },
60
82
  ...Object.keys(previousFormatting).length > 0 ? { previousFormatting } : {}
61
83
  };
@@ -66,6 +88,10 @@ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, r
66
88
  });
67
89
  });
68
90
  if (segments.length === 0) return tr;
91
+ const suggestionAttrs = suggestionId === null ? {} : {
92
+ provenance: "suggested",
93
+ suggestionId
94
+ };
69
95
  applyInlineFormatting({
70
96
  tr,
71
97
  schema,
@@ -73,7 +99,10 @@ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, r
73
99
  to,
74
100
  formatting
75
101
  });
76
- for (const segment of segments) tr.addMark(segment.from, segment.to, propertyChangeType.create({ changes: segment.changes }));
102
+ for (const segment of segments) tr.addMark(segment.from, segment.to, propertyChangeType.create({
103
+ changes: segment.changes,
104
+ ...suggestionAttrs
105
+ }));
77
106
  return tr;
78
107
  };
79
108
  /**
@@ -246,7 +275,7 @@ const buildEmphasisInlineContent = (schema, text, baseMarks) => {
246
275
  }
247
276
  return nodes.length > 0 ? nodes : [schema.text(text, [...baseMarks])];
248
277
  };
249
- const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", createCommentId, revisionIdSeed }) => {
278
+ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionIdSeed }) => {
250
279
  const applied = [];
251
280
  const skipped = [];
252
281
  const resolved = [];
@@ -255,7 +284,9 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
255
284
  const commentType = view.state.schema.marks["comment"];
256
285
  const claimedTableRows = /* @__PURE__ */ new Set();
257
286
  const claimedTableColumns = /* @__PURE__ */ new Set();
258
- if (mode === "tracked-changes" && (!insertionType || !deletionType)) return {
287
+ const producesTrackedChanges = mode !== "direct";
288
+ const isSuggested = mode === "suggested";
289
+ if (producesTrackedChanges && (!insertionType || !deletionType)) return {
259
290
  applied,
260
291
  skipped: operations.map((operation) => ({
261
292
  id: operation.id,
@@ -361,6 +392,27 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
361
392
  const commentMark = item.commentId !== void 0 && commentType ? commentType.create({ commentId: item.commentId }) : null;
362
393
  const stepsBefore = tr.steps.length;
363
394
  let appliedRevisionIds;
395
+ if (isSuggested && !SUGGESTED_SUPPORTED_OPERATION_TYPES.has(item.operation.type)) {
396
+ skipped.push({
397
+ id: item.operation.id,
398
+ reason: "unsupportedMode"
399
+ });
400
+ continue;
401
+ }
402
+ const suggestionId = isSuggested ? item.operation.suggestionId ?? item.operation.id : null;
403
+ const trackedRevisionExtras = {
404
+ ...initials ? { initials } : {},
405
+ ...suggestionId !== null ? {
406
+ provenance: "suggested",
407
+ suggestionId
408
+ } : {}
409
+ };
410
+ const structuralRevision = producesTrackedChanges ? {
411
+ revisionId: revisionSeed,
412
+ author,
413
+ date,
414
+ ...trackedRevisionExtras
415
+ } : null;
364
416
  switch (item.operation.type) {
365
417
  case "replaceInBlock":
366
418
  case "replaceRange": {
@@ -374,16 +426,18 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
374
426
  date,
375
427
  revisionIdDelete,
376
428
  revisionIdInsert,
377
- commentMark
429
+ commentMark,
430
+ suggestionId,
431
+ initials
378
432
  });
379
- if (mode === "tracked-changes") appliedRevisionIds = [revisionIdDelete, revisionIdInsert];
433
+ if (producesTrackedChanges) appliedRevisionIds = [revisionIdDelete, revisionIdInsert];
380
434
  break;
381
435
  }
382
436
  case "commentOnRange":
383
437
  if (commentMark) tr = tr.addMark(item.from, item.to, commentMark);
384
438
  break;
385
439
  case "formatRange":
386
- if (mode === "tracked-changes") {
440
+ if (producesTrackedChanges) {
387
441
  const revisionId = revisionSeed++;
388
442
  tr = applyTrackedInlineFormatting({
389
443
  tr,
@@ -394,7 +448,9 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
394
448
  formatting: item.operation.formatting,
395
449
  revisionId,
396
450
  author,
397
- date
451
+ date,
452
+ initials,
453
+ suggestionId
398
454
  });
399
455
  appliedRevisionIds = [revisionId];
400
456
  break;
@@ -437,13 +493,15 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
437
493
  date,
438
494
  revisionIdDelete,
439
495
  revisionIdInsert,
440
- commentMark
496
+ commentMark,
497
+ suggestionId,
498
+ initials
441
499
  });
442
500
  tr = applyReplaceBlockStyleId({
443
501
  item,
444
502
  tr
445
503
  });
446
- if (mode === "tracked-changes") appliedRevisionIds = [revisionIdDelete, revisionIdInsert];
504
+ if (producesTrackedChanges) appliedRevisionIds = [revisionIdDelete, revisionIdInsert];
447
505
  break;
448
506
  }
449
507
  case "insertAfterBlock":
@@ -456,12 +514,15 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
456
514
  continue;
457
515
  }
458
516
  const marks = [];
459
- if (mode === "tracked-changes" && insertionType) {
517
+ let insertedBlockRevisionId = null;
518
+ if (producesTrackedChanges && insertionType) {
460
519
  const revisionId = revisionSeed++;
520
+ insertedBlockRevisionId = revisionId;
461
521
  marks.push(insertionType.create({
462
522
  revisionId,
463
523
  author,
464
- date
524
+ date,
525
+ ...trackedRevisionExtras
465
526
  }));
466
527
  appliedRevisionIds = [revisionId];
467
528
  }
@@ -480,6 +541,13 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
480
541
  attrs["listStartOverride"] = null;
481
542
  }
482
543
  }
544
+ if (isSuggested && suggestionId !== null && insertedBlockRevisionId !== null) attrs["_suggestedInsert"] = {
545
+ suggestionId,
546
+ revisionId: insertedBlockRevisionId,
547
+ author,
548
+ date,
549
+ ...initials ? { initials } : {}
550
+ };
483
551
  const node = item.blockNode.type.create(attrs, content);
484
552
  tr = tr.insert(item.from, node);
485
553
  break;
@@ -492,17 +560,32 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
492
560
  });
493
561
  continue;
494
562
  }
495
- const node = buildSignatureTableNode({
563
+ const signatureTable = buildSignatureTableNode({
496
564
  schema: view.state.schema,
497
565
  parties: item.operation.parties
498
566
  });
499
- if (!node) {
567
+ if (!signatureTable) {
500
568
  skipped.push({
501
569
  id: item.operation.id,
502
570
  reason: "unsupportedBlock"
503
571
  });
504
572
  continue;
505
573
  }
574
+ let node = signatureTable;
575
+ if (isSuggested && suggestionId !== null) {
576
+ const revisionId = revisionSeed++;
577
+ node = signatureTable.type.create({
578
+ ...signatureTable.attrs,
579
+ _suggestedInsert: {
580
+ suggestionId,
581
+ revisionId,
582
+ author,
583
+ date,
584
+ ...initials ? { initials } : {}
585
+ }
586
+ }, signatureTable.content, signatureTable.marks);
587
+ appliedRevisionIds = [revisionId];
588
+ }
506
589
  tr = tr.insert(item.from, node);
507
590
  break;
508
591
  }
@@ -515,11 +598,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
515
598
  });
516
599
  continue;
517
600
  }
518
- const revision = mode === "tracked-changes" ? {
519
- revisionId: revisionSeed,
520
- author,
521
- date
522
- } : null;
601
+ const revision = structuralRevision;
523
602
  const result = applyTableRowInsertion({
524
603
  tr,
525
604
  insertion,
@@ -549,11 +628,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
549
628
  });
550
629
  continue;
551
630
  }
552
- const revision = mode === "tracked-changes" ? {
553
- revisionId: revisionSeed,
554
- author,
555
- date
556
- } : null;
631
+ const revision = structuralRevision;
557
632
  const result = applyTableColumnInsertion({
558
633
  tr,
559
634
  insertion,
@@ -586,11 +661,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
586
661
  continue;
587
662
  }
588
663
  const columnKey = getTableColumnCoordinateKey(deletion);
589
- const revision = mode === "tracked-changes" ? {
590
- revisionId: revisionSeed,
591
- author,
592
- date
593
- } : null;
664
+ const revision = structuralRevision;
594
665
  const result = applyTableColumnDeletion({
595
666
  tr,
596
667
  deletion,
@@ -703,11 +774,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
703
774
  const result = applyTableRowDeletion({
704
775
  tr,
705
776
  deletion,
706
- revision: mode === "tracked-changes" ? {
707
- revisionId: revisionSeed,
708
- author,
709
- date
710
- } : null
777
+ revision: structuralRevision
711
778
  });
712
779
  if (result.type === "unsupported") {
713
780
  skipped.push({
@@ -733,7 +800,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
733
800
  tr = tr.addMark(item.from, item.to, deletionType.create({
734
801
  revisionId,
735
802
  author,
736
- date
803
+ date,
804
+ ...trackedRevisionExtras
737
805
  }));
738
806
  appliedRevisionIds = [revisionId];
739
807
  }
@@ -757,7 +825,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
757
825
  ...appliedRevisionIds !== void 0 && appliedRevisionIds[0] !== void 0 && {
758
826
  revisionId: appliedRevisionIds[0],
759
827
  revisionIds: appliedRevisionIds
760
- }
828
+ },
829
+ ...suggestionId !== null && { suggestionId }
761
830
  });
762
831
  }
763
832
  if (tr.docChanged) view.dispatch(tr);
@@ -787,7 +856,7 @@ const previewFolioAIEditOperations = (options) => {
787
856
  skipped: result.skipped
788
857
  };
789
858
  };
790
- const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark }) => {
859
+ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark, suggestionId = null, initials }) => {
791
860
  let nextTr = tr;
792
861
  const replacement = stripInlineEmphasisMarkers((() => {
793
862
  if (item.operation.type === "replaceInBlock" || item.operation.type === "replaceRange") return item.operation.replace;
@@ -805,15 +874,24 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
805
874
  }
806
875
  const insertionType = nextTr.doc.type.schema.marks["insertion"];
807
876
  const deletionType = nextTr.doc.type.schema.marks["deletion"];
877
+ const suggestionAttrs = suggestionId === null ? {} : {
878
+ provenance: "suggested",
879
+ suggestionId
880
+ };
881
+ const initialsAttr = initials ? { initials } : {};
808
882
  const delAttrs = {
809
883
  revisionId: revisionIdDelete,
810
884
  author,
811
- date
885
+ date,
886
+ ...initialsAttr,
887
+ ...suggestionAttrs
812
888
  };
813
889
  const insAttrs = {
814
890
  revisionId: revisionIdInsert,
815
891
  author,
816
- date
892
+ date,
893
+ ...initialsAttr,
894
+ ...suggestionAttrs
817
895
  };
818
896
  const cleanBlock = item.blockNode.content.size === item.blockNode.textContent.length ? buildCleanBlockText(item.blockNode, item.blockFrom) : null;
819
897
  let sourceText = null;
@@ -13,7 +13,7 @@ const resolveFolioAIBlockRange = ({ blockId, doc, snapshot }) => {
13
13
  };
14
14
  }
15
15
  const resolvedSnapshot = snapshot ?? createFolioAIEditSnapshot(doc);
16
- const anchor = resolvedSnapshot.anchors[blockId] ?? resolveSequentialBlockAnchor(blockId, resolvedSnapshot);
16
+ const anchor = Object.hasOwn(resolvedSnapshot.anchors, blockId) ? resolvedSnapshot.anchors[blockId] : resolveSequentialBlockAnchor(blockId, resolvedSnapshot);
17
17
  if (!anchor) return null;
18
18
  return clampRangeToDocSize(doc.content.size, anchor);
19
19
  };
@@ -2,13 +2,14 @@
2
2
  const DELETION_MARK = "deletion";
3
3
  const INSERTION_MARK = "insertion";
4
4
  const COMMENT_MARK = "comment";
5
+ const HIDDEN_MARK = "hidden";
5
6
  const buildCleanBlockText = (blockNode, blockFrom) => {
6
7
  let text = "";
7
8
  const offsets = [];
8
9
  let lastEnd = blockFrom + 1;
9
10
  blockNode.descendants((node, pos) => {
10
11
  if (!node.isText || node.text === void 0) return true;
11
- if (node.marks.some((mark) => mark.type.name === DELETION_MARK)) return false;
12
+ if (node.marks.some((mark) => mark.type.name === DELETION_MARK || mark.type.name === HIDDEN_MARK)) return false;
12
13
  const startPos = blockFrom + 1 + pos;
13
14
  for (let i = 0; i < node.text.length; i++) offsets.push(startPos + i);
14
15
  text += node.text;
@@ -1,10 +1,10 @@
1
- import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
2
- import { buildAnnotatedBlockText } from "./clean-text.js";
3
- import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
4
1
  import { getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
5
2
  import { deterministicHexId } from "../utils/hexId.js";
6
3
  import { schema, singletonManager } from "../prosemirror/schema/index.js";
7
4
  import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
5
+ import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
6
+ import { buildAnnotatedBlockText } from "./clean-text.js";
7
+ import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
8
8
  import { applyFolioDocumentOperations } from "../document-operations.js";
9
9
  import { getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
10
10
  import { getHeaderFooterText } from "../docx/headerFooterParser.js";
@@ -18,6 +18,22 @@ const createFolioAITextRangeHandle = ({ blockId, text, startOffset, endOffset })
18
18
  selectedTextHash: hashFolioAIBlockText(text.slice(startOffset, endOffset))
19
19
  };
20
20
  };
21
+ const TABLE_ROW_NODE_NAME = "tableRow";
22
+ /**
23
+ * True when `pos`'s nearest `tableRow` ancestor is marked `hidden` (OOXML
24
+ * `w:trPr/w:hidden` — Word never renders the row). A textblock inside such a
25
+ * row must not surface its text to the AI/agent snapshot: an attacker DOCX
26
+ * could otherwise smuggle prompt-injection instructions into a row that no
27
+ * human ever sees.
28
+ */
29
+ const isInHiddenTableRow = (doc, pos) => {
30
+ const $pos = doc.resolve(pos);
31
+ for (let depth = $pos.depth; depth > 0; depth--) {
32
+ const ancestor = $pos.node(depth);
33
+ if (ancestor.type.name === TABLE_ROW_NODE_NAME) return ancestor.attrs["hidden"] === true;
34
+ }
35
+ return false;
36
+ };
21
37
  const createFolioAIEditSnapshot = (doc) => {
22
38
  const draftBlocks = [];
23
39
  const hashCounts = /* @__PURE__ */ new Map();
@@ -28,11 +44,12 @@ const createFolioAIEditSnapshot = (doc) => {
28
44
  };
29
45
  let blockIndex = 0;
30
46
  doc.descendants((node, pos, parent) => {
31
- if (!node.isTextblock) return;
47
+ if (!node.isTextblock) return true;
48
+ if (isInHiddenTableRow(doc, pos)) return false;
32
49
  const { text } = buildCleanBlockText(node, pos);
33
50
  const normalizedText = normalizeFolioAIBlockText(text);
34
51
  if (normalizedText.length === 0) {
35
- if (parent?.type !== doc.type) return;
52
+ if (parent?.type !== doc.type) return true;
36
53
  emptyAnchorState.textblockCount++;
37
54
  if (emptyAnchorState.candidate === null) {
38
55
  const paraIdAttr = node.attrs["paraId"];
@@ -42,7 +59,7 @@ const createFolioAIEditSnapshot = (doc) => {
42
59
  paraId: typeof paraIdAttr === "string" && paraIdAttr.length > 0 ? paraIdAttr : null
43
60
  };
44
61
  }
45
- return;
62
+ return true;
46
63
  }
47
64
  const textHash = hashFolioAIBlockText(normalizedText);
48
65
  hashCounts.set(textHash, (hashCounts.get(textHash) ?? 0) + 1);
@@ -78,6 +95,7 @@ const createFolioAIEditSnapshot = (doc) => {
78
95
  textHash
79
96
  }
80
97
  });
98
+ return true;
81
99
  });
82
100
  const blocks = [];
83
101
  const anchors = {};
@@ -1,7 +1,7 @@
1
1
  import { expectTableCellAttrs } from "../prosemirror/attrs/index.js";
2
- import { standaloneTableCellFromProseMirror } from "../prosemirror/conversion/fromProseDoc.js";
3
2
  import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
4
3
  import { standaloneTableCellToProseMirror } from "../prosemirror/conversion/toProseDoc.js";
4
+ import { standaloneTableCellFromProseMirror } from "../prosemirror/conversion/fromProseDoc.js";
5
5
  import { tableRectanglesEqual } from "./table-mutation-plan.js";
6
6
  import { tableRectangleCutsMergedCell } from "./table-targets.js";
7
7
  import { Result } from "better-result";
@@ -210,6 +210,7 @@ const splitTrackedVerticalTableCell = ({ tr, tablePosition, table, rectangle, re
210
210
  };
211
211
  const isEmptyTableCell = (cell) => cell.childCount === 1 && cell.firstChild?.isTextblock === true && cell.firstChild.childCount === 0;
212
212
  const trackedSplitCellFromStoredSource = ({ origin, source, marker }) => {
213
+ if (source.formatting?.gridSpan !== void 0 && source.formatting.gridSpan > 1) return null;
213
214
  const formatting = formattingWithoutVerticalMerge(source.formatting);
214
215
  const converted = standaloneTableCellToProseMirror({
215
216
  type: "tableCell",
@@ -1,3 +1,4 @@
1
+ import { TrackedChangeProvenance } from "../prosemirror/schema/marks.js";
1
2
  import { TableRowTarget } from "./table-targets.js";
2
3
  import { Transaction } from "prosemirror-state";
3
4
  import { Node } from "prosemirror-model";
@@ -19,7 +20,14 @@ type TableColumnDeletion = TableColumnInsertion;
19
20
  type TableStructureRevision = {
20
21
  revisionId: number;
21
22
  author: string;
22
- date: string;
23
+ date: string; /** Optional author initials (w:initials), carried for round-trip. */
24
+ initials?: string;
25
+ /**
26
+ * `"suggested"` marks the produced `trIns`/`trDel`/`cellMarker` as an AI
27
+ * proposal that is stripped from serialized DOCX until accepted.
28
+ */
29
+ provenance?: TrackedChangeProvenance;
30
+ suggestionId?: string;
23
31
  };
24
32
  type TableRowColumnMutationResult = {
25
33
  type: "applied";
@@ -1,5 +1,5 @@
1
- import { stripBlockIdentityAttrs } from "./block-identity.js";
2
1
  import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
2
+ import { stripBlockIdentityAttrs } from "./block-identity.js";
3
3
  import { findEnclosingTableCell, findEnclosingTableRow } from "./table-targets.js";
4
4
  import { TableMap, columnIsHeader, removeColumn, removeRow, rowIsHeader, tableNodeTypes } from "prosemirror-tables";
5
5
  //#region src/ai-edits/table-row-column-mutations.ts
@@ -122,6 +122,13 @@ type FolioAISignatureParty = {
122
122
  };
123
123
  type FolioAIEditOperation = FolioAIEditReviewMeta & {
124
124
  precondition?: FolioAIEditPrecondition;
125
+ /**
126
+ * Groups this operation's produced marks under one logical suggestion so the
127
+ * host can accept/reject them together. Only consulted in `"suggested"` apply
128
+ * mode; ignored otherwise. When omitted in suggested mode the applier falls
129
+ * back to the operation `id`, giving per-operation grouping.
130
+ */
131
+ suggestionId?: string;
125
132
  } & ({
126
133
  id: string;
127
134
  type: "replaceInBlock";
@@ -231,7 +238,21 @@ type FolioAIEditOperation = FolioAIEditReviewMeta & {
231
238
  type: "splitTableCell"; /** Stable paragraph anchor inside the cell to split. */
232
239
  blockId: string;
233
240
  });
234
- type FolioAIEditApplyMode = "direct" | "tracked-changes";
241
+ /**
242
+ * How AI-authored operations are applied:
243
+ * - `"direct"` — edits are written straight into the document.
244
+ * - `"tracked-changes"` — edits land as normal tracked changes (`w:ins`/`w:del`).
245
+ * - `"suggested"` — edits land as tracked changes carrying `"suggested"`
246
+ * provenance: rendered with the tracked-change grammar but stripped from
247
+ * serialized DOCX until a human accepts them. Behaves like `"tracked-changes"`
248
+ * and covers the inline text/format operations (replaceInBlock, replaceRange,
249
+ * formatRange) plus the block and table row/column structural operations
250
+ * (insertAfterBlock, insertBeforeBlock, replaceBlock, deleteBlock,
251
+ * insertSignatureTable, insertTableRow, deleteTableRow, insertTableColumn,
252
+ * deleteTableColumn). Only comment operations and cell merge/split report
253
+ * `unsupportedMode`.
254
+ */
255
+ type FolioAIEditApplyMode = "direct" | "tracked-changes" | "suggested";
235
256
  type FolioAIEditSkipReason = "missingBlock" | "changedBlock" | "ambiguousFind" | "missingFind" | "unsupportedBlock" | "unsupportedMode" | "atomicBatchRejected" | "preconditionFailed" | "staleRange" | "emptyOperation"
236
257
  /**
237
258
  * The operation would not change the document — find equals
@@ -259,6 +280,12 @@ type FolioAIEditAppliedOperation = {
259
280
  * belonging to this op.
260
281
  */
261
282
  revisionIds?: readonly number[];
283
+ /**
284
+ * The suggestion id stamped on every mark this operation produced (only set
285
+ * in `"suggested"` apply mode). Pass it to `acceptSuggestion` /
286
+ * `rejectSuggestion` / `scrollToSuggestion` to resolve the whole suggestion.
287
+ */
288
+ suggestionId?: string;
262
289
  };
263
290
  type FolioAIEditSkippedOperation = {
264
291
  id: string;
@@ -1,11 +1,32 @@
1
1
  //#region src/ai-edits/word-diff.ts
2
2
  const tokenize = (s) => s.match(/\s+|\S+/gu) ?? [];
3
+ /**
4
+ * Cell budget for the O(n*m) word-diff DP table below, mirroring
5
+ * `MAX_LCS_CELLS` in `version-comparison.ts`. `before`/`after` come from
6
+ * attacker-controlled document text (a `modified` block pair), so an
7
+ * unbounded pair of large strings would otherwise force a quadratic-sized
8
+ * allocation. Past this budget, skip the DP and fall back to a single
9
+ * whole-string `del` + `ins` pair — a coarser diff, but O(1) memory.
10
+ */
11
+ const MAX_WORD_DIFF_CELLS = 4e6;
3
12
  const diffWordSegments = (before, after) => {
4
13
  const a = tokenize(before);
5
14
  const b = tokenize(after);
6
15
  if (a.length === 0 && b.length === 0) return [];
7
16
  const m = a.length;
8
17
  const n = b.length;
18
+ if (m * n > MAX_WORD_DIFF_CELLS) {
19
+ const segments = [];
20
+ if (before.length > 0) segments.push({
21
+ type: "del",
22
+ text: before
23
+ });
24
+ if (after.length > 0) segments.push({
25
+ type: "ins",
26
+ text: after
27
+ });
28
+ return segments;
29
+ }
9
30
  const dp = Array.from({ length: m + 1 }, () => Array.from({ length: n + 1 }, () => 0));
10
31
  for (let i = 0; i < m; i++) for (let j = 0; j < n; j++) {
11
32
  const row = dp[i + 1];
@@ -27,9 +27,9 @@ import { AcceptAutocompleteResult, AutocompleteSuggestionPluginOptions, Autocomp
27
27
  import { ImageMeta, ImageRef, MarkdownOptions, MarkdownResult } from "../markdown/types.js";
28
28
  import { fromMarkdown } from "../markdown/fromMarkdown.js";
29
29
  import { toMarkdown, toMarkdownResult } from "../markdown/index.js";
30
- import { EmbeddedFont, EmbeddedFontParts, extractEmbeddedFonts, getEmbeddedFontFaces } from "../fonts/embeddedFonts.js";
31
- import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "../utils/fontResolver.js";
30
+ import { EmbeddedFont, EmbeddedFontParts, buildEmbeddedFontFamilyMap, extractEmbeddedFonts, getEmbeddedFontFaces, scopeEmbeddedFontFamily } from "../fonts/embeddedFonts.js";
31
+ import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "../utils/fontResolver.js";
32
32
  import { DOCX_CONFORMANCE_CLASSES } from "../index.js";
33
33
  type Document = document_d_exports.Document;
34
34
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
35
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidFolioDocumentOperationBatchError, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
35
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAISignatureParty, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidFolioDocumentOperationBatchError, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -1,6 +1,6 @@
1
+ import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "../utils/fontResolver.js";
1
2
  import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "../types/block-id.js";
2
3
  import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockText } from "../ai-edits/snapshot.js";
3
- import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "../utils/fontResolver.js";
4
4
  import { diffWordSegments } from "../ai-edits/word-diff.js";
5
5
  import { applyFolioAIEditOperations } from "../ai-edits/apply.js";
6
6
  import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
@@ -25,6 +25,6 @@ import { clearTemplateSlashMenu, consumeTemplateSlashQuery, getTemplateSlashMenu
25
25
  import { DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, acceptAutocompleteSuggestion, acceptAutocompleteWord, appendAutocompleteToken, autocompleteSuggestionKey, autocompleteSuggestionPlugin, clearAutocompleteSuggestion, finishAutocompleteSuggestion, getAutocompleteSuggestion, shouldTriggerAutocomplete, startAutocompleteSuggestion } from "../prosemirror/plugins/autocompleteSuggestion.js";
26
26
  import { fromMarkdown } from "../markdown/fromMarkdown.js";
27
27
  import { toMarkdown, toMarkdownResult } from "../markdown/index.js";
28
- import { extractEmbeddedFonts, getEmbeddedFontFaces } from "../fonts/embeddedFonts.js";
28
+ import { buildEmbeddedFontFamilyMap, extractEmbeddedFonts, getEmbeddedFontFaces, scopeEmbeddedFontFamily } from "../fonts/embeddedFonts.js";
29
29
  import { DOCX_CONFORMANCE_CLASSES } from "../index.js";
30
- export { DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
30
+ export { DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -1,4 +1,3 @@
1
- import { proseDocToBlocks } from "../prosemirror/conversion/fromProseDoc.js";
2
1
  import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js";
3
2
  import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
4
3
  import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
@@ -6,6 +5,7 @@ import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdA
6
5
  import { createStarterKit } from "../prosemirror/extensions/StarterKit.js";
7
6
  import { schema } from "../prosemirror/schema/index.js";
8
7
  import { headerFooterToProseDoc } from "../prosemirror/conversion/toProseDoc.js";
8
+ import { proseDocToBlocks } from "../prosemirror/conversion/fromProseDoc.js";
9
9
  import { clearHeaderFooterVerbatimXml } from "../docx/headerFooterVerbatim.js";
10
10
  import { EditorState } from "prosemirror-state";
11
11
  import { EditorView } from "prosemirror-view";