@stll/folio-core 0.52.0 → 0.53.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 (307) hide show
  1. package/README.md +23 -0
  2. package/dist/ai-edits/apply.d.ts +26 -1
  3. package/dist/ai-edits/apply.js +1519 -105
  4. package/dist/ai-edits/batch-claims.d.ts +130 -0
  5. package/dist/ai-edits/batch-claims.js +244 -0
  6. package/dist/ai-edits/blockRange.js +9 -3
  7. package/dist/ai-edits/character-boundaries.d.ts +43 -0
  8. package/dist/ai-edits/character-boundaries.js +95 -0
  9. package/dist/ai-edits/clean-text.d.ts +33 -3
  10. package/dist/ai-edits/clean-text.js +52 -6
  11. package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
  12. package/dist/ai-edits/comment-lifecycle.js +52 -0
  13. package/dist/ai-edits/headless.d.ts +71 -7
  14. package/dist/ai-edits/headless.js +381 -41
  15. package/dist/ai-edits/index.d.ts +2 -1
  16. package/dist/ai-edits/index.js +2 -1
  17. package/dist/ai-edits/minimal-replacement.d.ts +21 -6
  18. package/dist/ai-edits/minimal-replacement.js +134 -15
  19. package/dist/ai-edits/newListNumbering.d.ts +23 -0
  20. package/dist/ai-edits/newListNumbering.js +94 -0
  21. package/dist/ai-edits/note-references.d.ts +96 -0
  22. package/dist/ai-edits/note-references.js +139 -0
  23. package/dist/ai-edits/pending-suggestions.d.ts +79 -0
  24. package/dist/ai-edits/pending-suggestions.js +306 -0
  25. package/dist/ai-edits/read.d.ts +1 -1
  26. package/dist/ai-edits/read.js +56 -43
  27. package/dist/ai-edits/result-validation.d.ts +29 -0
  28. package/dist/ai-edits/result-validation.js +164 -0
  29. package/dist/ai-edits/snapshot.d.ts +36 -3
  30. package/dist/ai-edits/snapshot.js +177 -23
  31. package/dist/ai-edits/table-cell-mutations.d.ts +1 -1
  32. package/dist/ai-edits/table-cell-mutations.js +4 -1
  33. package/dist/ai-edits/table-geometry.d.ts +1 -1
  34. package/dist/ai-edits/table-row-column-mutations.d.ts +38 -16
  35. package/dist/ai-edits/table-row-column-mutations.js +61 -26
  36. package/dist/ai-edits/types.d.ts +155 -16
  37. package/dist/compare/compare.d.ts +1 -1
  38. package/dist/compare/compare.js +65 -16
  39. package/dist/compare/content-alignment.js +13 -2
  40. package/dist/compare/content-types.d.ts +7 -0
  41. package/dist/compare/content.d.ts +4 -1
  42. package/dist/compare/content.js +6 -1
  43. package/dist/compare/formatting.js +2 -1
  44. package/dist/compare/inline-atoms.d.ts +21 -4
  45. package/dist/compare/inline-atoms.js +138 -16
  46. package/dist/compare/plan.js +10 -5
  47. package/dist/compare/scenario.js +14 -3
  48. package/dist/compare/section-boundary-properties.js +1 -1
  49. package/dist/compare/types.d.ts +4 -2
  50. package/dist/compare/types.js +2 -1
  51. package/dist/compare/verification.d.ts +1 -1
  52. package/dist/compare/verification.js +15 -3
  53. package/dist/controller/committedLayoutDiff.d.ts +25 -0
  54. package/dist/controller/committedLayoutDiff.js +71 -0
  55. package/dist/controller/contentControlWidgetController.d.ts +1 -1
  56. package/dist/controller/folioEditor.d.ts +1 -1
  57. package/dist/controller/fontReadiness.d.ts +70 -3
  58. package/dist/controller/fontReadiness.js +100 -10
  59. package/dist/controller/headerFooterEditorManager.js +6 -1
  60. package/dist/controller/layoutPipeline.d.ts +12 -10
  61. package/dist/controller/layoutPipeline.js +62 -9
  62. package/dist/controller/layoutRunOptions.d.ts +24 -0
  63. package/dist/controller/layoutRunOptions.js +17 -0
  64. package/dist/controller/layoutScheduler.d.ts +19 -14
  65. package/dist/controller/layoutScheduler.js +15 -12
  66. package/dist/controller/layoutSession.d.ts +26 -2
  67. package/dist/controller/layoutSession.js +1 -1
  68. package/dist/controller/noteEditorManager.js +8 -3
  69. package/dist/document-operations.d.ts +11 -2
  70. package/dist/document-operations.js +230 -21
  71. package/dist/docx/altChunk.d.ts +8 -0
  72. package/dist/docx/altChunk.js +17 -0
  73. package/dist/docx/attributeRemainder.d.ts +1 -1
  74. package/dist/docx/blockContentParser.d.ts +64 -1
  75. package/dist/docx/blockContentParser.js +17 -4
  76. package/dist/docx/blockCustomXmlShell.d.ts +7 -0
  77. package/dist/docx/blockCustomXmlShell.js +14 -0
  78. package/dist/docx/blockPlainText.js +7 -0
  79. package/dist/docx/commentAnchorIndex.js +1 -1
  80. package/dist/docx/commentRangeIntegrity.js +8 -3
  81. package/dist/docx/commentReferenceNormalization.js +1 -1
  82. package/dist/docx/commentReplyMarkers.js +20 -1
  83. package/dist/docx/compatibility.d.ts +1 -1
  84. package/dist/docx/compatibility.js +34 -13
  85. package/dist/docx/containerChildren.gen.d.ts +1 -1
  86. package/dist/docx/containerChildren.gen.js +1 -0
  87. package/dist/docx/documentParser.js +12 -12
  88. package/dist/docx/drawingIdNormalization.d.ts +3 -1
  89. package/dist/docx/drawingIdNormalization.js +6 -1
  90. package/dist/docx/ensureParaIds.js +119 -97
  91. package/dist/docx/footnoteParser.js +8 -4
  92. package/dist/docx/headerFooterParser.js +3 -3
  93. package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
  94. package/dist/docx/headerFooterReferenceNormalization.js +52 -27
  95. package/dist/docx/hyperlinkParser.d.ts +1 -1
  96. package/dist/docx/index.d.ts +1 -1
  97. package/dist/docx/listNumberingInstances.d.ts +59 -0
  98. package/dist/docx/listNumberingInstances.js +249 -0
  99. package/dist/docx/normalizeBaseDirection.js +2 -1
  100. package/dist/docx/opaqueCarrier.d.ts +7 -0
  101. package/dist/docx/opaqueCarrier.js +38 -0
  102. package/dist/docx/packageParts.js +4 -1
  103. package/dist/docx/paragraphParser.js +91 -24
  104. package/dist/docx/paragraphPropertySource.d.ts +4 -2
  105. package/dist/docx/paragraphPropertySource.js +7 -1
  106. package/dist/docx/paragraphTraversal.js +1 -0
  107. package/dist/docx/parseWarningMessage.js +4 -1
  108. package/dist/docx/parser.js +173 -20
  109. package/dist/docx/rezip.js +27 -5
  110. package/dist/docx/sdtProperties.js +1 -1
  111. package/dist/docx/sdtPropertiesPatch.js +3 -0
  112. package/dist/docx/selectiveSave.js +16 -9
  113. package/dist/docx/selectiveXmlPatch.js +106 -37
  114. package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
  115. package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
  116. package/dist/docx/serializer/documentSerializer.js +2 -0
  117. package/dist/docx/serializer/headerFooterSerializer.js +2 -0
  118. package/dist/docx/serializer/noteSerializer.js +3 -1
  119. package/dist/docx/serializer/paragraphSerializer.js +33 -9
  120. package/dist/docx/serializer/tableSerializer.js +25 -38
  121. package/dist/docx/server/createBilingualDocument.js +3 -3
  122. package/dist/docx/tableParser.d.ts +157 -1
  123. package/dist/docx/tableParser.js +89 -10
  124. package/dist/docx/textBoxParser.js +1 -1
  125. package/dist/docx/unzip.d.ts +2 -1
  126. package/dist/docx/unzip.js +1 -1
  127. package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
  128. package/dist/docx/wordprocessingPrefixes.js +286 -0
  129. package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
  130. package/dist/i18n/messages/catalogs.gen.js +153 -17
  131. package/dist/i18n/messages/messages.gen.d.ts +8 -0
  132. package/dist/internal/compare/inline-presentation.d.ts +2 -1
  133. package/dist/internal/compare/inline-presentation.js +11 -10
  134. package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
  135. package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
  136. package/dist/internal/indexedPositionMap.d.ts +11 -0
  137. package/dist/internal/indexedPositionMap.js +42 -0
  138. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  139. package/dist/internal/revisionResolutionEdits.d.ts +13 -0
  140. package/dist/internal/revisionResolutionEdits.js +19 -0
  141. package/dist/internal/revisionResolutionInline.d.ts +24 -0
  142. package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
  143. package/dist/internal/revisionResolutionStep.d.ts +34 -0
  144. package/dist/internal/revisionResolutionStep.js +282 -0
  145. package/dist/internal/revisionResolutionTracking.d.ts +11 -0
  146. package/dist/internal/revisionResolutionTracking.js +124 -0
  147. package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
  148. package/dist/internal/wholeStoryRevisionResolution.js +530 -0
  149. package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
  150. package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
  151. package/dist/layout-bridge/convert/tableConversion.js +1 -1
  152. package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
  153. package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
  154. package/dist/layout-engine/layoutInstrumentation.js +4 -1
  155. package/dist/layout-engine/measure/cache.d.ts +18 -1
  156. package/dist/layout-engine/measure/cache.js +31 -1
  157. package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
  158. package/dist/layout-engine/measure/measureContainer.js +7 -47
  159. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
  160. package/dist/layout-engine/measure/measureHelpers.js +22 -2
  161. package/dist/layout-engine/measure/measureWorker.js +2 -3
  162. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
  163. package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
  164. package/dist/layout-engine/types.d.ts +6 -0
  165. package/dist/layout-painter/renderPage.js +1 -0
  166. package/dist/layout-painter/renderParagraph.js +1 -1
  167. package/dist/managers/ContextMenuManager.js +5 -2
  168. package/dist/managers/types.d.ts +3 -0
  169. package/dist/markdown/fromMarkdown.js +2 -1
  170. package/dist/markdown/internals.d.ts +11 -1
  171. package/dist/markdown/internals.js +24 -3
  172. package/dist/markdown/renderBlock.js +39 -9
  173. package/dist/markdown/renderParagraph.d.ts +13 -1
  174. package/dist/markdown/renderParagraph.js +85 -38
  175. package/dist/markdown/renderRuns.js +3 -10
  176. package/dist/markdown/renderTable.js +24 -12
  177. package/dist/markdown/trailers.js +2 -2
  178. package/dist/markdown/types.d.ts +29 -7
  179. package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
  180. package/dist/paged-layout/incrementalMeasure.js +1 -9
  181. package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
  182. package/dist/paged-layout/transactionDirtyRange.js +29 -41
  183. package/dist/panel-layout.d.ts +83 -0
  184. package/dist/panel-layout.js +96 -0
  185. package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
  186. package/dist/prosemirror/anchoredTextBoxes.js +156 -0
  187. package/dist/prosemirror/attrs/index.d.ts +6 -2
  188. package/dist/prosemirror/attrs/index.js +172 -1
  189. package/dist/prosemirror/commands/comments.d.ts +7 -8
  190. package/dist/prosemirror/commands/comments.js +241 -207
  191. package/dist/prosemirror/commands/contentControls.d.ts +1 -1
  192. package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
  193. package/dist/prosemirror/commands/formatting.d.ts +1 -1
  194. package/dist/prosemirror/commands/index.d.ts +2 -2
  195. package/dist/prosemirror/commands/index.js +2 -2
  196. package/dist/prosemirror/commands/paragraph.d.ts +7 -1
  197. package/dist/prosemirror/commands/paragraph.js +9 -1
  198. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
  199. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
  200. package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
  201. package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
  202. package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
  203. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
  204. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
  205. package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
  206. package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
  207. package/dist/prosemirror/commands/sectionBreak.js +2 -1
  208. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
  209. package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
  210. package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
  211. package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
  212. package/dist/prosemirror/conversion/toProseDoc.js +39 -4
  213. package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
  214. package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
  215. package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
  216. package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
  217. package/dist/prosemirror/extensions/StarterKit.js +3 -1
  218. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  219. package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
  220. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
  221. package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
  222. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
  223. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
  224. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
  225. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
  226. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
  227. package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
  228. package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
  229. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
  230. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
  231. package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
  232. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
  233. package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
  234. package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
  235. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
  236. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
  237. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
  238. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
  239. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
  240. package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
  241. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
  242. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  243. package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
  244. package/dist/prosemirror/extensions/types.d.ts +4 -1
  245. package/dist/prosemirror/findReplaceSelection.js +1 -1
  246. package/dist/prosemirror/index.d.ts +3 -2
  247. package/dist/prosemirror/index.js +3 -2
  248. package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
  249. package/dist/prosemirror/indexedNodeLookup.js +103 -0
  250. package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
  251. package/dist/prosemirror/listAutoformatMarkers.js +98 -0
  252. package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
  253. package/dist/prosemirror/listInstanceReferences.js +72 -0
  254. package/dist/prosemirror/listLabels.d.ts +24 -0
  255. package/dist/prosemirror/listLabels.js +111 -0
  256. package/dist/prosemirror/listNumbering.d.ts +110 -0
  257. package/dist/prosemirror/listNumbering.js +345 -0
  258. package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
  259. package/dist/prosemirror/listRenderingAttrs.js +8 -1
  260. package/dist/prosemirror/markupViewProjection.d.ts +95 -0
  261. package/dist/prosemirror/markupViewProjection.js +83 -0
  262. package/dist/prosemirror/numberingAttr.d.ts +9 -1
  263. package/dist/prosemirror/numberingAttr.js +9 -1
  264. package/dist/prosemirror/outlineLevelAttr.js +1 -1
  265. package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
  266. package/dist/prosemirror/paragraphIndentation.js +29 -7
  267. package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
  268. package/dist/prosemirror/paragraphMarkJoin.js +24 -0
  269. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
  270. package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
  271. package/dist/prosemirror/plugins/documentNumbering.js +97 -11
  272. package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
  273. package/dist/prosemirror/plugins/documentStyles.js +14 -1
  274. package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
  275. package/dist/prosemirror/plugins/suggestionMode.js +116 -52
  276. package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
  277. package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
  278. package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
  279. package/dist/prosemirror/positionSweep.d.ts +24 -0
  280. package/dist/prosemirror/positionSweep.js +222 -0
  281. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
  282. package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
  283. package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
  284. package/dist/prosemirror/replacedAnnotations.js +170 -1
  285. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
  286. package/dist/prosemirror/schema/index.d.ts +2 -2
  287. package/dist/prosemirror/schema/index.js +3 -0
  288. package/dist/prosemirror/schema/nodes.d.ts +42 -11
  289. package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
  290. package/dist/prosemirror/sectionMarkEdits.js +59 -0
  291. package/dist/prosemirror/storyListNumbering.d.ts +17 -0
  292. package/dist/prosemirror/storyListNumbering.js +102 -0
  293. package/dist/prosemirror/tableGridMutation.d.ts +48 -2
  294. package/dist/prosemirror/tableGridMutation.js +106 -1
  295. package/dist/prosemirror/validation.js +5 -1
  296. package/dist/redline.js +93 -9
  297. package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
  298. package/dist/render-dom/BodySelectionOverlay.js +3 -2
  299. package/dist/utils/findReplace.js +2 -2
  300. package/dist/utils/mergeDocumentContent.js +1 -1
  301. package/dist/utils/noteReferenceLabels.d.ts +36 -0
  302. package/dist/utils/noteReferenceLabels.js +32 -0
  303. package/dist/utils/replaceText.js +1 -1
  304. package/dist/version-comparison.d.ts +1 -0
  305. package/dist/version-comparison.js +1 -0
  306. package/package.json +3 -2
  307. package/dist/internal/headlessRevisionResolution.d.ts +0 -23
@@ -192,13 +192,22 @@ function toFlowBlocks(doc, options = {}) {
192
192
  trailingPageBreakSectionPositions.add(childStart);
193
193
  consumedPageBreakPositions.add(childStart + child.nodeSize);
194
194
  }
195
- if (child.type.name === "blockSdt") collectTrailingPageBreakSections(child, childStart + 1);
195
+ if (child.type.name === "blockSdt" || child.type.name === "blockCustomXml") collectTrailingPageBreakSections(child, childStart + 1);
196
196
  childStart += child.nodeSize;
197
197
  }
198
198
  };
199
199
  collectTrailingPageBreakSections(doc, offset);
200
200
  const visit = (node, pos) => {
201
201
  switch (node.type.name) {
202
+ case "blockCustomXml": {
203
+ let childOffset = pos + 1;
204
+ for (let i = 0; i < node.childCount; i += 1) {
205
+ const child = node.child(i);
206
+ visit(child, childOffset);
207
+ childOffset += child.nodeSize;
208
+ }
209
+ return;
210
+ }
202
211
  case "blockSdt": {
203
212
  const attrs = expectBlockSdtAttrs(node);
204
213
  sdtSeq += 1;
@@ -32,6 +32,13 @@ type LayoutInstrumentation = {
32
32
  onLayoutStart?: (event: {
33
33
  reason: LayoutRunReason;
34
34
  }) => void;
35
+ /**
36
+ * A review view (the editor's `DisplayMode`) laid out part of the story as
37
+ * authored: a revision there has no resolution.
38
+ */
39
+ onMarkupViewIncomplete?: (event: {
40
+ view: string;
41
+ }) => void;
35
42
  onLayoutPhase?: (event: {
36
43
  durationMs: number;
37
44
  phase: LayoutPhase;
@@ -55,6 +62,7 @@ declare function recordMeasureBlockError(blockIndex: number, block: FlowBlock, e
55
62
  declare function recordLayoutComplete(reason?: LayoutRunReason): void;
56
63
  declare function recordLayoutStart(reason: LayoutRunReason): void;
57
64
  declare function recordLayoutError(reason: LayoutRunReason, error: unknown): void;
65
+ declare function recordMarkupViewIncomplete(view: string): void;
58
66
  /** A dictionary failed to load; layout continues without hyphenating that language. */
59
67
  declare function recordHyphenationDictionaryError(dictionary: string, error: Error): void;
60
68
  declare function recordLayoutPhase(reason: LayoutRunReason, phase: LayoutPhase, durationMs: number): void;
@@ -62,4 +70,4 @@ declare function recordHiddenEditorStateCreate(reason: HiddenEditorStateReason):
62
70
  declare function recordHiddenEditorPhase(reason: HiddenEditorStateReason, phase: HiddenEditorPhase, durationMs: number): void;
63
71
  declare function recordDocumentLoadPhase(phase: DocumentLoadPhase, durationMs: number): void;
64
72
  //#endregion
65
- export { DocumentLoadPhase, HiddenEditorPhase, HiddenEditorStateReason, LayoutInstrumentation, LayoutPhase, LayoutRunReason, recordDocumentLoadPhase, recordHiddenEditorPhase, recordHiddenEditorStateCreate, recordHyphenationDictionaryError, recordLayoutComplete, recordLayoutError, recordLayoutPhase, recordLayoutStart, recordMeasureBlock, recordMeasureBlockError };
73
+ export { DocumentLoadPhase, HiddenEditorPhase, HiddenEditorStateReason, LayoutInstrumentation, LayoutPhase, LayoutRunReason, recordDocumentLoadPhase, recordHiddenEditorPhase, recordHiddenEditorStateCreate, recordHyphenationDictionaryError, recordLayoutComplete, recordLayoutError, recordLayoutPhase, recordLayoutStart, recordMarkupViewIncomplete, recordMeasureBlock, recordMeasureBlockError };
@@ -25,6 +25,9 @@ function recordLayoutError(reason, error) {
25
25
  reason
26
26
  });
27
27
  }
28
+ function recordMarkupViewIncomplete(view) {
29
+ globalThis.__folioLayoutInstrumentation?.onMarkupViewIncomplete?.({ view });
30
+ }
28
31
  /** A dictionary failed to load; layout continues without hyphenating that language. */
29
32
  function recordHyphenationDictionaryError(dictionary, error) {
30
33
  globalThis.__folioLayoutInstrumentation?.onHyphenationDictionaryError?.({
@@ -56,4 +59,4 @@ function recordDocumentLoadPhase(phase, durationMs) {
56
59
  });
57
60
  }
58
61
  //#endregion
59
- export { recordDocumentLoadPhase, recordHiddenEditorPhase, recordHiddenEditorStateCreate, recordHyphenationDictionaryError, recordLayoutComplete, recordLayoutError, recordLayoutPhase, recordLayoutStart, recordMeasureBlock, recordMeasureBlockError };
62
+ export { recordDocumentLoadPhase, recordHiddenEditorPhase, recordHiddenEditorStateCreate, recordHyphenationDictionaryError, recordLayoutComplete, recordLayoutError, recordLayoutPhase, recordLayoutStart, recordMarkupViewIncomplete, recordMeasureBlock, recordMeasureBlockError };
@@ -83,9 +83,26 @@ declare function getParagraphCacheSize(): number;
83
83
  * Call when fonts change, page width changes, or for testing
84
84
  */
85
85
  declare function clearAllCaches(): void;
86
+ /**
87
+ * Bind the caches to the font set the next measurements are taken in. When a
88
+ * face has loaded (or been added or removed) since the cached entries were
89
+ * measured, they are dropped: a width measured in a fallback cannot be served
90
+ * once the face it stood in for is available. Entries measured before the
91
+ * caches were first bound are dropped too: nothing records their font set.
92
+ * Returns whether it cleared.
93
+ */
94
+ declare function syncMeasureCachesToFontSet(fontSet: string): boolean;
95
+ /**
96
+ * Rename the font set the cached entries were measured in, without dropping
97
+ * them: for a layout run that has just measured, whose signature now also
98
+ * names the families it measured first. Only valid within the run that bound
99
+ * the caches with {@link syncMeasureCachesToFontSet}, since no face can load
100
+ * during a synchronous run.
101
+ */
102
+ declare function labelMeasureCachesFontSet(fontSet: string): void;
86
103
  /**
87
104
  * Get total size of all caches
88
105
  */
89
106
  declare function getTotalCacheSize(): number;
90
107
  //#endregion
91
- export { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTextWidthCacheGeneration, getTotalCacheSize, hashParagraphBlock, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize };
108
+ export { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTextWidthCacheGeneration, getTotalCacheSize, hashParagraphBlock, labelMeasureCachesFontSet, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize, syncMeasureCachesToFontSet };
@@ -259,6 +259,11 @@ function getParagraphCacheSize() {
259
259
  return paragraphMeasureCache.size;
260
260
  }
261
261
  /**
262
+ * The font set (a `readFontSetSignature` value) every cached measurement was
263
+ * taken in; `null` once the caches are cleared and before anything measures.
264
+ */
265
+ let cachedFontSet = null;
266
+ /**
262
267
  * Clear all measurement caches
263
268
  * Call when fonts change, page width changes, or for testing
264
269
  */
@@ -267,6 +272,31 @@ function clearAllCaches() {
267
272
  clearFontMetricsCache();
268
273
  clearParagraphMeasureCache();
269
274
  clearFontResolvedCache();
275
+ cachedFontSet = null;
276
+ }
277
+ /**
278
+ * Bind the caches to the font set the next measurements are taken in. When a
279
+ * face has loaded (or been added or removed) since the cached entries were
280
+ * measured, they are dropped: a width measured in a fallback cannot be served
281
+ * once the face it stood in for is available. Entries measured before the
282
+ * caches were first bound are dropped too: nothing records their font set.
283
+ * Returns whether it cleared.
284
+ */
285
+ function syncMeasureCachesToFontSet(fontSet) {
286
+ if (cachedFontSet === fontSet) return false;
287
+ clearAllCaches();
288
+ cachedFontSet = fontSet;
289
+ return true;
290
+ }
291
+ /**
292
+ * Rename the font set the cached entries were measured in, without dropping
293
+ * them: for a layout run that has just measured, whose signature now also
294
+ * names the families it measured first. Only valid within the run that bound
295
+ * the caches with {@link syncMeasureCachesToFontSet}, since no face can load
296
+ * during a synchronous run.
297
+ */
298
+ function labelMeasureCachesFontSet(fontSet) {
299
+ cachedFontSet = fontSet;
270
300
  }
271
301
  /**
272
302
  * Get total size of all caches
@@ -275,4 +305,4 @@ function getTotalCacheSize() {
275
305
  return getTextCacheSize() + getFontCacheSize() + getParagraphCacheSize();
276
306
  }
277
307
  //#endregion
278
- export { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTextWidthCacheGeneration, getTotalCacheSize, hashParagraphBlock, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize };
308
+ export { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTextWidthCacheGeneration, getTotalCacheSize, hashParagraphBlock, labelMeasureCachesFontSet, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize, syncMeasureCachesToFontSet };
@@ -1,4 +1,4 @@
1
- import { WORKER_FONT_FINGERPRINT_TEXT, countCodePoints } from "./measureWorkerProtocol.js";
1
+ import { countCodePoints } from "./measureWorkerProtocol.js";
2
2
  import { TaggedError } from "better-result";
3
3
  //#region src/layout-engine/measure/font-metrics.worker.ts
4
4
  /**
@@ -36,16 +36,12 @@ function getCtx() {
36
36
  ctx = next;
37
37
  return ctx;
38
38
  }
39
- function isFontFingerprintMatch(context, expectedWidth) {
40
- const actualWidth = context.measureText(WORKER_FONT_FINGERPRINT_TEXT).width;
41
- const tolerance = Math.max(FONT_FINGERPRINT_EPSILON, expectedWidth * 1e-4);
42
- return Math.abs(actualWidth - expectedWidth) <= tolerance;
43
- }
44
39
  function measureEntry(entry) {
45
40
  const context = getCtx();
46
41
  context.font = entry.font;
47
42
  context.fontKerning = entry.fontKerning;
48
- if (!isFontFingerprintMatch(context, entry.fontFingerprintWidth)) return null;
43
+ const actualFingerprintWidth = context.measureText(entry.fontFingerprintText).width;
44
+ if (Math.abs(actualFingerprintWidth - entry.fontFingerprintWidth) > FONT_FINGERPRINT_EPSILON) return null;
49
45
  let width = context.measureText(entry.text).width;
50
46
  const codePoints = countCodePoints(entry.text);
51
47
  if (entry.letterSpacing !== 0 && codePoints > 1) width += entry.letterSpacing * (codePoints - 1);
@@ -1,11 +1,11 @@
1
1
  import { getHorizontalScaleFactor } from "../../utils/horizontalScale.js";
2
2
  import { SCRIPT_CLASS, hasComplexScript, hasEastAsiaSlotText, scriptClassOf, segmentByScript } from "../../utils/scriptSegments.js";
3
- import { getCachedFontMetrics, getCachedTextWidth, getTextWidthCacheGeneration, setCachedFontMetrics, setCachedTextWidth } from "./cache.js";
3
+ import { getCachedFontMetrics, getCachedTextWidth, setCachedFontMetrics, setCachedTextWidth } from "./cache.js";
4
4
  import { applyComplexScriptFormatting, hasComplexScriptFormatting } from "./complexScriptFormatting.js";
5
5
  import { buildFontString, getResolvedData, ptToPx } from "./measureHelpers.js";
6
6
  import { setMeasureProvider } from "./measureProvider.js";
7
7
  import { canPrefetchMeasurement, prefetchMeasurement } from "./measureWorker.js";
8
- import { WORKER_FONT_FINGERPRINT_TEXT, countCodePoints } from "./measureWorkerProtocol.js";
8
+ import { countCodePoints } from "./measureWorkerProtocol.js";
9
9
  import { SMALL_CAPS_SCALE, smallCapsMask, smallCapsSegments } from "./smallCapsCasing.js";
10
10
  import { getFontKerningMode } from "./textMeasurementPolicy.js";
11
11
  import { panic } from "better-result";
@@ -31,7 +31,6 @@ const DEFAULT_LINE_HEIGHT_MULTIPLIER = 1;
31
31
  const DEFAULT_ASCENT_RATIO = .8;
32
32
  const DEFAULT_DESCENT_RATIO = .2;
33
33
  let canvasContext = null;
34
- const workerFontFingerprintCache = /* @__PURE__ */ new Map();
35
34
  /**
36
35
  * Get or create a canvas 2D context for text measurement
37
36
  */
@@ -50,19 +49,6 @@ function getCanvasContext() {
50
49
  function resetCanvasContext() {
51
50
  canvasContext = null;
52
51
  }
53
- function getWorkerFontFingerprintWidth(ctx, font, fontCacheKey, fontKerning) {
54
- const generation = getTextWidthCacheGeneration();
55
- const cached = workerFontFingerprintCache.get(fontCacheKey);
56
- if (cached?.generation === generation) return cached.width;
57
- ctx.font = font;
58
- ctx.fontKerning = fontKerning;
59
- const width = ctx.measureText(WORKER_FONT_FINGERPRINT_TEXT).width;
60
- workerFontFingerprintCache.set(fontCacheKey, {
61
- generation,
62
- width
63
- });
64
- return width;
65
- }
66
52
  /**
67
53
  * Get typography metrics for a given font size and family
68
54
  *
@@ -149,7 +135,8 @@ function canvasMeasureTextWidth(text, sourceStyle) {
149
135
  if (cached !== void 0) return cached;
150
136
  ctx.font = font;
151
137
  ctx.fontKerning = fontKerning;
152
- let width = ctx.measureText(measuredText).width;
138
+ const metrics = ctx.measureText(measuredText);
139
+ let width = metrics.width;
153
140
  if (letterSpacing) {
154
141
  const codePoints = countCodePoints(measuredText);
155
142
  if (codePoints > 1) width += letterSpacing * (codePoints - 1);
@@ -157,17 +144,18 @@ function canvasMeasureTextWidth(text, sourceStyle) {
157
144
  const scaledWidth = width * horizontalScale;
158
145
  setCachedTextWidth(measuredText, fontCacheKey, letterSpacing, scaledWidth);
159
146
  if (!canPrefetchMeasurement()) return scaledWidth;
160
- const fontFingerprintWidth = getWorkerFontFingerprintWidth(ctx, font, fontCacheKey, fontKerning);
147
+ const fontFingerprintText = measuredText;
148
+ const fontFingerprintWidth = metrics.width;
161
149
  prefetchMeasurement({
162
150
  text: measuredText,
163
151
  font,
164
152
  letterSpacing,
165
153
  horizontalScale,
166
154
  fontCacheKey,
155
+ fontFingerprintText,
167
156
  fontFingerprintWidth,
168
157
  fontKerning
169
158
  });
170
- prefetchBinarySearchProbes(measuredText, font, fontCacheKey, fontFingerprintWidth, letterSpacing, horizontalScale, fontKerning);
171
159
  return scaledWidth;
172
160
  }
173
161
  /**
@@ -262,34 +250,6 @@ function measureSmallCapsWidth(measuredText, style) {
262
250
  return width * horizontalScale;
263
251
  }
264
252
  /**
265
- * Speculatively enqueue the slice lengths that a subsequent
266
- * `findMaxFittingLength` binary search is likely to probe. We pick the
267
- * geometric series (full, half, quarter, eighth) which covers the
268
- * majority of probe points the binary search uses, without flooding
269
- * the worker for runs that will never trigger a line break.
270
- *
271
- * The worker is racing the main thread here: if the main thread asks
272
- * for slice(0, n/2) before the worker has answered, the cache miss
273
- * pays the main-thread cost as usual. When the worker wins, that probe
274
- * lands on a hit.
275
- */
276
- function prefetchBinarySearchProbes(text, font, fontCacheKey, fontFingerprintWidth, letterSpacing, horizontalScale, fontKerning) {
277
- if (text.length < 4) return;
278
- for (let denom = 2; denom <= 8; denom *= 2) {
279
- const len = Math.floor(text.length / denom);
280
- if (len < 2) break;
281
- prefetchMeasurement({
282
- text: text.slice(0, len),
283
- font,
284
- letterSpacing,
285
- horizontalScale,
286
- fontCacheKey,
287
- fontFingerprintWidth,
288
- fontKerning
289
- });
290
- }
291
- }
292
- /**
293
253
  * Measure text and return full metrics
294
254
  */
295
255
  function canvasMeasureText(text, style) {
@@ -34,6 +34,11 @@ type ResolvedFontCache = {
34
34
  * Get the resolved font data for a font family, with caching.
35
35
  */
36
36
  declare function getResolvedData(fontFamily: string, alternateFontFamily?: string): ResolvedFontCache;
37
+ /**
38
+ * Whether a face of `family` (a `FontFace.family`, possibly quoted) can change
39
+ * a width this page has measured.
40
+ */
41
+ declare function isLayoutFontFamily(family: string): boolean;
37
42
  /**
38
43
  * Clear the resolved-font-family cache. `resolveFontFamily`'s output for a
39
44
  * given key can change even when the raw font names don't — e.g. when the
@@ -100,4 +105,4 @@ declare function halfPtToPx(halfPt: number): number;
100
105
  */
101
106
  declare function pxToHalfPt(px: number): number;
102
107
  //#endregion
103
- export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, docxScriptFontSize, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
108
+ export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, docxScriptFontSize, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, isLayoutFontFamily, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
@@ -1,4 +1,4 @@
1
- import { resolveFontFamily } from "../../utils/fontResolver.js";
1
+ import { parseFontFamilyList, resolveFontFamily } from "../../utils/fontResolver.js";
2
2
  import "../../utils/fontWeights.js";
3
3
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
4
4
  import { eastAsiaHintApplies } from "../../utils/scriptSegments.js";
@@ -93,10 +93,30 @@ function getResolvedData(fontFamily, alternateFontFamily) {
93
93
  singleLineRatio: resolved.singleLineRatio
94
94
  };
95
95
  fontResolvedCache.set(cacheKey, cached);
96
+ for (const family of parseFontFamilyList(resolved.cssFallback)) layoutFontFamilies.add(normalizeFontFamilyName(family));
96
97
  }
97
98
  return cached;
98
99
  }
99
100
  /**
101
+ * Every family a measured font stack has named on this page, normalized.
102
+ *
103
+ * Recorded where measurement resolves its stacks, so it is exactly the set of
104
+ * families whose faces can change a measured width: a face of any other family
105
+ * loading (the host's UI font, say) cannot. Never cleared, because a family
106
+ * stays relevant for as long as a cached or committed measurement may hold it,
107
+ * and the set is bounded by the families the page's documents name.
108
+ */
109
+ const layoutFontFamilies = /* @__PURE__ */ new Set();
110
+ /** CSS family names match ASCII case-insensitively, quotes aside. */
111
+ const normalizeFontFamilyName = (family) => family.trim().replace(/^(["'])(.*)\1$/u, "$2").toLowerCase();
112
+ /**
113
+ * Whether a face of `family` (a `FontFace.family`, possibly quoted) can change
114
+ * a width this page has measured.
115
+ */
116
+ function isLayoutFontFamily(family) {
117
+ return layoutFontFamilies.has(normalizeFontFamilyName(family));
118
+ }
119
+ /**
100
120
  * Get the CSS fallback string for a font family, with caching.
101
121
  */
102
122
  function getResolvedFallback(fontFamily, alternateFontFamily) {
@@ -207,4 +227,4 @@ function pxToHalfPt(px) {
207
227
  return pxToPt(px) * 2;
208
228
  }
209
229
  //#endregion
210
- export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, docxScriptFontSize, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
230
+ export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, docxScriptFontSize, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, isLayoutFontFamily, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
@@ -118,9 +118,8 @@ function ensureProxy() {
118
118
  }
119
119
  /**
120
120
  * How long to wait before flushing the queue, in milliseconds. Short
121
- * enough to keep the worker warm and ahead of the binary-search probes,
122
- * long enough to amortise `postMessage` overhead across a typical
123
- * paragraph's worth of probes.
121
+ * enough to process measured entries promptly, long enough to amortise
122
+ * `postMessage` overhead across a typical paragraph's runs.
124
123
  */
125
124
  const FLUSH_INTERVAL_MS = 10;
126
125
  /**
@@ -14,22 +14,21 @@ import { FontKerningMode } from "./textMeasurementPolicy.js";
14
14
  * `fontCacheKey` is the main-thread cache identity. It intentionally
15
15
  * differs from `font` when scale metadata is appended.
16
16
  *
17
- * `fontFingerprintWidth` is the main-thread width for
18
- * `WORKER_FONT_FINGERPRINT_TEXT` in the same `font`. The worker checks
19
- * the same sentinel before measuring and skips entries whose font
20
- * metrics do not match, which prevents worker fallback fonts from
21
- * poisoning the main-thread cache.
17
+ * `fontFingerprintText` is text already measured on the main thread in
18
+ * this font. The worker checks its unscaled width against
19
+ * `fontFingerprintWidth` before caching a result. This covers the actual
20
+ * glyphs and font subsets used by the request, including non-Latin text.
22
21
  */
23
22
  type MeasureRequestEntry = {
24
23
  text: string;
25
24
  font: string;
26
25
  fontCacheKey: string;
26
+ fontFingerprintText: string;
27
27
  fontFingerprintWidth: number;
28
28
  letterSpacing: number;
29
29
  horizontalScale: number;
30
30
  fontKerning: FontKerningMode;
31
31
  };
32
- declare const WORKER_FONT_FINGERPRINT_TEXT = "HAMBURGEFONTS ivwqy 0123456789";
33
32
  /**
34
33
  * Count whole code points (not UTF-16 units) so letter spacing is applied once
35
34
  * per rendered glyph — an astral character is one glyph spanning two units.
@@ -73,4 +72,4 @@ type MeasureWorkerResponse = {
73
72
  error: string;
74
73
  };
75
74
  //#endregion
76
- export { MeasureRequestEntry, MeasureResponseEntry, MeasureWorkerRequest, MeasureWorkerResponse, WORKER_FONT_FINGERPRINT_TEXT, countCodePoints };
75
+ export { MeasureRequestEntry, MeasureResponseEntry, MeasureWorkerRequest, MeasureWorkerResponse, countCodePoints };
@@ -1,5 +1,4 @@
1
1
  //#region src/layout-engine/measure/measureWorkerProtocol.ts
2
- const WORKER_FONT_FINGERPRINT_TEXT = "HAMBURGEFONTS ivwqy 0123456789";
3
2
  /**
4
3
  * Count whole code points (not UTF-16 units) so letter spacing is applied once
5
4
  * per rendered glyph — an astral character is one glyph spanning two units.
@@ -17,4 +16,4 @@ function countCodePoints(text) {
17
16
  return count;
18
17
  }
19
18
  //#endregion
20
- export { WORKER_FONT_FINGERPRINT_TEXT, countCodePoints };
19
+ export { countCodePoints };
@@ -585,6 +585,12 @@ type ParagraphBlock = {
585
585
  * the innermost group and draws boundary chrome per outer→inner level.
586
586
  */
587
587
  sdtGroups?: SdtGroup[];
588
+ /**
589
+ * Set by a review view that lays out resolved text (Simple Markup) on a
590
+ * paragraph whose authored text carries a revision the view resolved: the
591
+ * painter marks it with the change bar its runs no longer show.
592
+ */
593
+ reviewIndicator?: "change-bar";
588
594
  };
589
595
  /**
590
596
  * Cell border specification for rendering.
@@ -1326,6 +1326,7 @@ function collectContentFingerprint(block, parts) {
1326
1326
  if (block.kind === "paragraph") {
1327
1327
  if (block.attrs?.styleId) parts.push(`p:st:${block.attrs.styleId}`);
1328
1328
  if (block.attrs?.alignment) parts.push(`p:al:${block.attrs.alignment}`);
1329
+ if (block.reviewIndicator) parts.push(`p:ri:${block.reviewIndicator}`);
1329
1330
  for (let index = 0; index < block.runs.length; index++) {
1330
1331
  const run = block.runs[index];
1331
1332
  if (!run) continue;
@@ -1500,7 +1500,7 @@ function bordersFormGroup(a, b) {
1500
1500
  if (!a || !b) return false;
1501
1501
  return bordersEqual(a.top, b.top) && bordersEqual(a.bottom, b.bottom) && bordersEqual(a.left, b.left) && bordersEqual(a.right, b.right) && bordersEqual(a.between, b.between);
1502
1502
  }
1503
- const paragraphHasTrackedChanges = (block) => block.attrs?.listMarkerRevision !== void 0 || block.runs.some((run) => run.kind !== "lineBreak" && (run.isInsertion === true || run.isDeletion === true));
1503
+ const paragraphHasTrackedChanges = (block) => block.reviewIndicator === "change-bar" || block.attrs?.listMarkerRevision !== void 0 || block.runs.some((run) => run.kind !== "lineBreak" && (run.isInsertion === true || run.isDeletion === true));
1504
1504
  /**
1505
1505
  * Render a paragraph fragment
1506
1506
  *
@@ -1,4 +1,5 @@
1
1
  import { isInTable as isInTableCell } from "../prosemirror/extensions/nodes/TableExtension.js";
2
+ import { NO_LIST_NUMBERING_MENU, listNumberingMenuState } from "../prosemirror/listNumbering.js";
2
3
  import { Subscribable } from "./Subscribable.js";
3
4
  //#region src/managers/ContextMenuManager.ts
4
5
  const CLOSED_STATE = {
@@ -13,7 +14,8 @@ const CLOSED_STATE = {
13
14
  to: 0
14
15
  },
15
16
  cursorInTable: false,
16
- cursorInTrackedChange: false
17
+ cursorInTrackedChange: false,
18
+ listNumbering: NO_LIST_NUMBERING_MENU
17
19
  };
18
20
  var ContextMenuManager = class extends Subscribable {
19
21
  constructor() {
@@ -36,7 +38,8 @@ var ContextMenuManager = class extends Subscribable {
36
38
  to: selection.to
37
39
  },
38
40
  cursorInTable,
39
- cursorInTrackedChange
41
+ cursorInTrackedChange,
42
+ listNumbering: state ? listNumberingMenuState(state) : NO_LIST_NUMBERING_MENU
40
43
  });
41
44
  }
42
45
  /** Close the menu. */
@@ -1,3 +1,4 @@
1
+ import { ListNumberingMenuState } from "../prosemirror/listNumbering.js";
1
2
  //#region src/managers/types.d.ts
2
3
  /** Cell coordinates in a table */
3
4
  type CellCoordinates = {
@@ -21,6 +22,8 @@ type ContextMenuSnapshot = {
21
22
  };
22
23
  cursorInTable: boolean;
23
24
  cursorInTrackedChange: boolean;
25
+ /** Restart / continue / set-value entries for the list item under the caret. */
26
+ listNumbering: ListNumberingMenuState;
24
27
  };
25
28
  /** Error severity levels */
26
29
  type ErrorSeverity = "error" | "warning" | "info";
@@ -36,9 +36,10 @@ const applyMarkdownPageGeometry = (document) => {
36
36
  */
37
37
  function fromMarkdown(markdown) {
38
38
  const document = createEmptyDocument();
39
- const { content, numbering } = compileMarkdownToContent(markdown);
39
+ const { content, numbering, warnings } = compileMarkdownToContent(markdown);
40
40
  if (content.length > 0) document.package.document.content = content;
41
41
  if (numbering) document.package.numbering = numbering;
42
+ if (warnings && warnings.length > 0) document.warnings = [...document.warnings ?? [], ...warnings];
42
43
  applyMarkdownPageGeometry(document);
43
44
  return document;
44
45
  }
@@ -1,4 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { NoteReferenceKind } from "../utils/noteReferenceLabels.js";
2
3
  import { MarkdownOptions, RenderContext } from "./types.js";
3
4
  //#region src/markdown/internals.d.ts
4
5
  /**
@@ -15,5 +16,14 @@ declare function newContext(opts?: MarkdownOptions, styles?: document_d_exports.
15
16
  * recurring messages appear at most once.
16
17
  */
17
18
  declare function pushWarning(ctx: RenderContext, message: string): void;
19
+ /**
20
+ * Number a footnote or endnote reference in reading order and record its
21
+ * note for the definitions trailer on the note's first reference. Returns the
22
+ * bare token (`1`, `e1`) and the inline marker (`[^1]`, `[^e1]`).
23
+ */
24
+ declare function numberNoteReference(ctx: RenderContext, kind: NoteReferenceKind, refId: number): {
25
+ token: string;
26
+ marker: string;
27
+ };
18
28
  //#endregion
19
- export { newContext, pushWarning };
29
+ export { newContext, numberNoteReference, pushWarning };
@@ -1,4 +1,6 @@
1
1
  import { createBuiltInStyleIndex } from "../docx/builtInStyles.js";
2
+ import { createListLabelCounter } from "../prosemirror/listLabels.js";
3
+ import { createNoteReferenceNumbering, noteReferenceMarker, noteReferenceToken } from "../utils/noteReferenceLabels.js";
2
4
  //#region src/markdown/internals.ts
3
5
  /**
4
6
  * Internal helpers shared by the markdown converter: context construction and
@@ -30,11 +32,12 @@ function newContext(opts = {}, styles) {
30
32
  imagesByPath: /* @__PURE__ */ new Map(),
31
33
  warnings: [],
32
34
  footnoteRefs: [],
35
+ noteNumbering: createNoteReferenceNumbering(),
33
36
  commentRefs: [],
34
37
  hyperlinkRefs: [],
35
38
  imageCounter: 0,
36
- listCounters: /* @__PURE__ */ new Map(),
37
- listSeenLevels: /* @__PURE__ */ new Set()
39
+ nextListLabel: createListLabelCounter(),
40
+ listIndentWidths: []
38
41
  };
39
42
  }
40
43
  /**
@@ -44,5 +47,23 @@ function newContext(opts = {}, styles) {
44
47
  function pushWarning(ctx, message) {
45
48
  if (!ctx.warnings.includes(message)) ctx.warnings.push(message);
46
49
  }
50
+ /**
51
+ * Number a footnote or endnote reference in reading order and record its
52
+ * note for the definitions trailer on the note's first reference. Returns the
53
+ * bare token (`1`, `e1`) and the inline marker (`[^1]`, `[^e1]`).
54
+ */
55
+ function numberNoteReference(ctx, kind, refId) {
56
+ const { displayNumber, first } = ctx.noteNumbering.next(kind, refId);
57
+ const marker = noteReferenceMarker(kind, displayNumber);
58
+ if (first) ctx.footnoteRefs.push({
59
+ refId,
60
+ marker,
61
+ kind
62
+ });
63
+ return {
64
+ token: noteReferenceToken(kind, displayNumber),
65
+ marker
66
+ };
67
+ }
47
68
  //#endregion
48
- export { newContext, pushWarning };
69
+ export { newContext, numberNoteReference, pushWarning };
@@ -1,5 +1,7 @@
1
+ import { ALT_CHUNK_READER_DIAGNOSTIC, isAltChunkMarkup } from "../docx/altChunk.js";
2
+ import { OPAQUE_REVISION_CARRIER_READER_DIAGNOSTIC, isOpaqueNestedRowMarkup, opaqueRevisionCarrierName } from "../docx/opaqueCarrier.js";
1
3
  import { cloneParagraphWithoutPropertySource } from "../docx/paragraphPropertySource.js";
2
- import { renderParagraph } from "./renderParagraph.js";
4
+ import { renderParagraphBlock } from "./renderParagraph.js";
3
5
  import { renderTable } from "./renderTable.js";
4
6
  import { panic } from "better-result";
5
7
  //#region src/markdown/renderBlock.ts
@@ -14,9 +16,12 @@ import { panic } from "better-result";
14
16
  * its break on accept and merges with the following paragraph. Word's join
15
17
  * keeps the FIRST paragraph's properties (style, list) and drops the resolved
16
18
  * mark; the surviving break is the next paragraph's, so a run of consecutive
17
- * deletions collapses into one paragraph. A non-paragraph next block (table,
18
- * SDT) is structurally incompatible and stays unmerged, matching the editor's
19
- * accept-change join guard (`commands/comments.ts`).
19
+ * deletions collapses into one paragraph. A first paragraph with nothing left
20
+ * once its deletions are accepted (a whole deleted paragraph) contributes only
21
+ * the join, so the NEXT paragraph keeps its own properties, as the editor's
22
+ * accept does. A non-paragraph next block (table, SDT) is structurally
23
+ * incompatible and stays unmerged, matching the editor's accept-change join
24
+ * guard (`commands/comments.ts`).
20
25
  */
21
26
  function mergeAcceptedParagraphBreaks(blocks) {
22
27
  const merged = [];
@@ -24,8 +29,9 @@ function mergeAcceptedParagraphBreaks(blocks) {
24
29
  const prev = merged.at(-1);
25
30
  if (prev?.type === "paragraph" && prev.pPrMark?.kind === "del" && block.type === "paragraph") {
26
31
  const next = block.pPrMark;
27
- const joined = cloneParagraphWithoutPropertySource(prev, {
28
- content: [...prev.content, ...block.content],
32
+ const content = [...prev.content, ...block.content];
33
+ const joined = cloneParagraphWithoutPropertySource(holdsNothingOnAccept(prev) ? block : prev, {
34
+ content,
29
35
  ...next ? { pPrMark: next } : {}
30
36
  });
31
37
  if (!next) Reflect.deleteProperty(joined, "pPrMark");
@@ -36,14 +42,33 @@ function mergeAcceptedParagraphBreaks(blocks) {
36
42
  }
37
43
  return merged;
38
44
  }
45
+ /** Paragraph content that shows nothing: range boundaries and anchors. */
46
+ const ZERO_WIDTH_CONTENT = /* @__PURE__ */ new Set([
47
+ "bookmarkStart",
48
+ "bookmarkEnd",
49
+ "commentRangeStart",
50
+ "commentRangeEnd",
51
+ "commentReference",
52
+ "moveFromRangeStart",
53
+ "moveFromRangeEnd",
54
+ "moveToRangeStart",
55
+ "moveToRangeEnd"
56
+ ]);
57
+ /** Whether an item shows nothing once every change is accepted. */
58
+ const showsNothingOnAccept = (item) => {
59
+ if (item.type === "deletion" || item.type === "moveFrom" || ZERO_WIDTH_CONTENT.has(item.type)) return true;
60
+ if (item.type === "insertion" || item.type === "moveTo") return item.content.every(showsNothingOnAccept);
61
+ return item.type === "run" && item.content.every((content) => content.type === "renderedPageBreak");
62
+ };
63
+ /** Whether a paragraph keeps no visible content once every change is accepted. */
64
+ const holdsNothingOnAccept = (paragraph) => paragraph.content.every(showsNothingOnAccept);
39
65
  function renderBlocks(ctx, pkg, blocks) {
40
66
  const out = [];
41
67
  let prevWasListItem = false;
42
68
  const ordered = ctx.opts.trackedChanges === "clean" ? mergeAcceptedParagraphBreaks(blocks) : blocks;
43
69
  for (const block of ordered) switch (block.type) {
44
70
  case "paragraph": {
45
- const isListItem = !!block.listRendering && !block.listRendering.markerHidden;
46
- const md = renderParagraph(ctx, pkg, block);
71
+ const { markdown: md, isListItem } = renderParagraphBlock(ctx, pkg, block);
47
72
  if (!md) {
48
73
  prevWasListItem = false;
49
74
  continue;
@@ -63,7 +88,8 @@ function renderBlocks(ctx, pkg, blocks) {
63
88
  prevWasListItem = false;
64
89
  break;
65
90
  }
66
- case "blockSdt": {
91
+ case "blockSdt":
92
+ case "blockCustomXml": {
67
93
  const nested = renderBlocks(ctx, pkg, block.content);
68
94
  if (nested) {
69
95
  if (out.length) out.push("");
@@ -72,6 +98,10 @@ function renderBlocks(ctx, pkg, blocks) {
72
98
  break;
73
99
  }
74
100
  case "preservedBlock":
101
+ if (isAltChunkMarkup(block.xml)) out.push(block.readerText === void 0 ? ALT_CHUNK_READER_DIAGNOSTIC : `${ALT_CHUNK_READER_DIAGNOSTIC}\n\n${block.readerText}`);
102
+ else if (opaqueRevisionCarrierName(block.xml) !== void 0) out.push(OPAQUE_REVISION_CARRIER_READER_DIAGNOSTIC);
103
+ else if (isOpaqueNestedRowMarkup(block.xml)) out.push("[Unsupported nested w:tr content]");
104
+ break;
75
105
  case "bookmarkStart":
76
106
  case "bookmarkEnd": break;
77
107
  default: panic(`Unsupported block content in markdown: ${JSON.stringify(block)}`);