@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
@@ -1,5 +1,6 @@
1
1
  import { FolioDocxReviewer } from "../ai-edits/headless.js";
2
2
  import { createFolioAITextRangeHandle } from "../ai-edits/snapshot.js";
3
+ import "../document-operations.js";
3
4
  import { CompareDocxParseError, compareDocxSerializeError } from "./types.js";
4
5
  import { Result, panic } from "better-result";
5
6
  //#region src/compare/scenario.ts
@@ -234,14 +235,24 @@ const applyEditScript = async (base, script) => {
234
235
  operations: plan.operations
235
236
  });
236
237
  }
237
- const { skipped } = reviewer.applyOperations(planned.flatMap(({ operations }) => [...operations]), {
238
- mode: "direct",
239
- snapshot
238
+ const { skipped } = reviewer.applyDocumentOperationsToStory({
239
+ story: { type: "main" },
240
+ batch: {
241
+ version: 1,
242
+ mode: "direct",
243
+ operations: planned.flatMap(({ operations }) => [...operations])
244
+ },
245
+ snapshot,
246
+ undefinedStyles: "keep"
240
247
  });
241
248
  const skippedIds = new Set(skipped.map(({ id }) => id));
242
249
  const applied = [];
243
250
  for (const { step, operations } of planned) {
244
251
  if (operations.some(({ id }) => skippedIds.has(id))) {
252
+ if (!operations.every(({ id }) => skippedIds.has(id))) panic("An edit script step was applied in part", {
253
+ step,
254
+ skipped
255
+ });
245
256
  unresolved.push({
246
257
  step,
247
258
  reason: "refused"
@@ -12,7 +12,7 @@ const boundaryChildrenOf = (document) => {
12
12
  let hasChangeHistory = false;
13
13
  document.forEach((node, position) => {
14
14
  if (node.type.name !== "paragraph") {
15
- if (node.type.name === "preservedBlock") return;
15
+ if (node.type.name === "preservedBlock" || node.type.name === "blockBookmarkBoundary") return;
16
16
  children.push({
17
17
  type: "other",
18
18
  nodeType: node.type.name
@@ -25,7 +25,9 @@ type CompareDocxOptions = {
25
25
  * redline that reads plausibly and is wrong is worse than no redline.
26
26
  * `"emit"` returns the redline anyway, with `verification` naming every
27
27
  * invariant that did not hold, for a caller that would rather show its best
28
- * attempt and say what is missing.
28
+ * attempt and say what is missing. A reference to a note the base document
29
+ * does not have is refused either way: the redline would carry its marker
30
+ * as text that reads like a reference.
29
31
  */
30
32
  onUnverified?: "refuse" | "emit";
31
33
  /**
@@ -222,7 +224,7 @@ type CompareChange = {
222
224
  baseBlockIds: readonly string[];
223
225
  };
224
226
  /** Why a part of the package is absent from `changes`. */
225
- declare const COMPARE_UNSUPPORTED_REASONS: readonly ["story-missing-in-base", "story-missing-in-target", "story-not-editable"];
227
+ declare const COMPARE_UNSUPPORTED_REASONS: readonly ["story-missing-in-base", "story-missing-in-target", "story-not-editable", "unsupported-content"];
226
228
  type CompareUnsupportedReason = (typeof COMPARE_UNSUPPORTED_REASONS)[number];
227
229
  /**
228
230
  * A package part the comparison did not cover. Reported rather than dropped so
@@ -10,7 +10,8 @@ const COMPARE_REVISION_FORMATS = ["word", "folio-exact"];
10
10
  const COMPARE_UNSUPPORTED_REASONS = Object.freeze([
11
11
  "story-missing-in-base",
12
12
  "story-missing-in-target",
13
- "story-not-editable"
13
+ "story-not-editable",
14
+ "unsupported-content"
14
15
  ]);
15
16
  var InvalidCompareDocxOptionsError = class extends TaggedError("InvalidCompareDocxOptionsError") {};
16
17
  var CompareDocxParseError = class extends TaggedError("CompareDocxParseError") {};
@@ -33,7 +33,7 @@ type CompareVerification = {
33
33
  status: "unverified";
34
34
  failures: readonly CompareVerificationFailure[];
35
35
  };
36
- type ProjectedBlock = Pick<FolioAIBlock, "text" | "table" | "styleId" | "listLevel" | "listReference" | "directAlignment" | "directSpacing" | "directIndentation" | "structuralBoundaries">;
36
+ type ProjectedBlock = Pick<FolioAIBlock, "kind" | "headingLevel" | "text" | "table" | "styleId" | "directOutlineLevel" | "listLevel" | "listReference" | "directAlignment" | "directSpacing" | "directIndentation" | "structuralBoundaries">;
37
37
  type ClassifyOptions<T> = {
38
38
  invariant: CompareVerificationInvariant;
39
39
  story: FolioDocumentStoryHandle;
@@ -1,5 +1,6 @@
1
1
  import { paragraphIndentationEqual } from "../prosemirror/paragraphIndentation.js";
2
2
  import { paragraphSpacingEqual } from "../prosemirror/paragraphSpacing.js";
3
+ import { sameFolioContentOutlineLevel } from "./content.js";
3
4
  import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
4
5
  //#region src/compare/verification.ts
5
6
  /**
@@ -8,10 +9,15 @@ import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
8
9
  * The check compares two block projections: what accepting the generated
9
10
  * revisions leaves against the target, and what rejecting them leaves against
10
11
  * the base. A projection carries each block's container, style, list level,
11
- * direct alignment, spacing, indentation and text, so WHICH field diverged names
12
+ * direct outline level, alignment, spacing, indentation and text, so WHICH field diverged names
12
13
  * which part of the pipeline lost the difference — and that is worth reporting
13
14
  * as a typed cause rather than as one opaque "did not reproduce".
14
15
  *
16
+ * It also carries what a reader sees those properties add up to: the block's
17
+ * kind and heading level. The stated properties are what an operation writes,
18
+ * so a projection of them alone checks the applier against its own vocabulary;
19
+ * the resolved classification is checked independently of how it was reached.
20
+ *
15
21
  * Every `detail` string here is structural: counts, offsets, container kinds.
16
22
  * Never a phrase of either document, because a caller may log it, put it in a
17
23
  * report, or quote it in a review.
@@ -51,11 +57,15 @@ const sameStructuralBoundaries = (left, right) => {
51
57
  for (let index = 0; index < leftLength; index++) {
52
58
  const boundary = left[index];
53
59
  const other = right[index];
54
- if (boundary === void 0 || other === void 0 || boundary.type !== other.type || boundary.offset !== other.offset || boundary.clear !== other.clear) return false;
60
+ if (boundary === void 0 || other === void 0 || boundary.offset !== other.offset) return false;
61
+ if (boundary.type === "pageBreak") {
62
+ if (other.type !== "pageBreak" || boundary.clear !== other.clear) return false;
63
+ } else if (other.type !== "noteReference" || boundary.noteType !== other.noteType || boundary.length !== other.length) return false;
55
64
  }
56
65
  return true;
57
66
  };
58
- const sameProjectedBlock = (left, right) => sameContainer(left.table, right.table) && sameStructuralBoundaries(left.structuralBoundaries, right.structuralBoundaries) && left.styleId === right.styleId && left.listLevel === right.listLevel && left.listReference?.numId === right.listReference?.numId && left.listReference?.level === right.listReference?.level && left.directAlignment === right.directAlignment && paragraphSpacingEqual(left.directSpacing, right.directSpacing) && paragraphIndentationEqual(left.directIndentation, right.directIndentation) && left.text === right.text;
67
+ const sameClassification = (left, right) => left.kind === right.kind && left.headingLevel === right.headingLevel;
68
+ const sameProjectedBlock = (left, right) => sameContainer(left.table, right.table) && sameClassification(left, right) && sameStructuralBoundaries(left.structuralBoundaries, right.structuralBoundaries) && left.styleId === right.styleId && sameFolioContentOutlineLevel(left.directOutlineLevel, right.directOutlineLevel) && left.listLevel === right.listLevel && left.listReference?.numId === right.listReference?.numId && left.listReference?.level === right.listReference?.level && left.directAlignment === right.directAlignment && paragraphSpacingEqual(left.directSpacing, right.directSpacing) && paragraphIndentationEqual(left.directIndentation, right.directIndentation) && left.text === right.text;
59
69
  const containerKind = (container) => container ? "cell" : "body";
60
70
  const collapseWhitespace = (text) => text.replace(/\s+/gu, " ").trim();
61
71
  const sameBlockProjection = (left, right) => left.length === right.length && left.every((entry, index) => {
@@ -172,8 +182,10 @@ const classifyProjectionMismatch = ({ invariant, story, actual, expected }) => {
172
182
  if (!sameContainer(left.table, right.table)) return failure("container", `a block sits in a ${containerKind(left.table)} where it is expected in a ${containerKind(right.table)}, ${at} (${counts})`);
173
183
  if (!sameStructuralBoundaries(left.structuralBoundaries, right.structuralBoundaries)) return failure("inline-structure", `a block's zero-width inline structure does not match ${at} (${counts})`);
174
184
  if (left.text === right.text && left.styleId !== right.styleId) return failure("style", `the paragraph style did not move ${at} (${counts})`);
185
+ if (left.text === right.text && !sameFolioContentOutlineLevel(left.directOutlineLevel, right.directOutlineLevel)) return failure("style", `the direct outline level did not move ${at} (${counts})`);
175
186
  if (left.text === right.text && left.listLevel !== right.listLevel) return failure("list-level", `the list level did not move ${at} (${counts})`);
176
187
  if (left.text === right.text && (left.listReference?.numId !== right.listReference?.numId || left.listReference?.level !== right.listReference?.level)) return failure("list-level", `the numbering reference did not move ${at} (${counts})`);
188
+ if (left.text === right.text && !sameClassification(left, right)) return left.kind === "heading" || right.kind === "heading" ? failure("style", `the heading classification did not move ${at} (${counts})`) : failure("list-level", `the list membership did not move ${at} (${counts})`);
177
189
  if (left.text === right.text && left.directAlignment !== right.directAlignment) return failure("alignment", `the direct paragraph alignment did not move ${at} (${counts})`);
178
190
  if (left.text === right.text && !paragraphSpacingEqual(left.directSpacing, right.directSpacing)) return failure("spacing", `the direct paragraph spacing did not move ${at} (${counts})`);
179
191
  if (left.text === right.text && !paragraphIndentationEqual(left.directIndentation, right.directIndentation)) return failure("indentation", `the direct paragraph indentation did not move ${at} (${counts})`);
@@ -0,0 +1,25 @@
1
+ import { DirtyRange } from "../paged-layout/incrementalMeasure.js";
2
+ import { LayoutTemplatePreview } from "./layoutSession.js";
3
+ import { Node } from "prosemirror-model";
4
+ //#region src/controller/committedLayoutDiff.d.ts
5
+ type LayoutFlowSource = {
6
+ doc: Node;
7
+ preview: LayoutTemplatePreview;
8
+ };
9
+ type CommittedLayoutDiff = {
10
+ type: "full";
11
+ } | {
12
+ type: "range";
13
+ /** In the next document's positions. */
14
+ range: DirtyRange;
15
+ };
16
+ /**
17
+ * The span of `next` whose flow blocks may differ from the ones the committed
18
+ * layout measured for `committed`, or `full` when no span bounds it.
19
+ *
20
+ * `full` also covers "nothing in the body flow changed": such a pass was
21
+ * requested for an input outside it (a header, footer or note story).
22
+ */
23
+ declare const diffAgainstCommittedLayout: (committed: LayoutFlowSource, next: LayoutFlowSource) => CommittedLayoutDiff;
24
+ //#endregion
25
+ export { CommittedLayoutDiff, LayoutFlowSource, diffAgainstCommittedLayout };
@@ -0,0 +1,71 @@
1
+ import { templatePreviewDirtyRange } from "../layout-bridge/convert/templatePreviewFlow.js";
2
+ //#region src/controller/committedLayoutDiff.ts
3
+ const FULL = { type: "full" };
4
+ const diffDocuments = (before, after) => {
5
+ const start = before.content.findDiffStart(after.content);
6
+ if (start === null) return null;
7
+ const end = before.content.findDiffEnd(after.content);
8
+ if (end === null) return null;
9
+ const overlap = start - Math.min(end.a, end.b);
10
+ return overlap > 0 ? {
11
+ start,
12
+ endBefore: end.a + overlap,
13
+ endAfter: end.b + overlap
14
+ } : {
15
+ start,
16
+ endBefore: end.a,
17
+ endAfter: end.b
18
+ };
19
+ };
20
+ /** Map a range of the previous document into the next one's positions. */
21
+ const mapRangeThroughDiff = (range, diff) => {
22
+ const shift = diff.endAfter - diff.endBefore;
23
+ const mapPosition = (pos, insideChange) => {
24
+ if (pos < diff.start) return pos;
25
+ if (pos >= diff.endBefore) return pos + shift;
26
+ return insideChange;
27
+ };
28
+ return {
29
+ ...range,
30
+ from: mapPosition(range.from, diff.start),
31
+ to: mapPosition(range.to, diff.endAfter)
32
+ };
33
+ };
34
+ const mapPreviewThroughDiff = (preview, diff) => ({
35
+ entries: preview.entries.map((entry) => mapRangeThroughDiff(entry, diff)),
36
+ hidden: preview.hidden.map((range) => mapRangeThroughDiff(range, diff))
37
+ });
38
+ const unionRanges = (first, second) => {
39
+ if (!first) return second;
40
+ if (!second) return first;
41
+ return {
42
+ from: Math.min(first.from, second.from),
43
+ to: Math.max(first.to, second.to)
44
+ };
45
+ };
46
+ /**
47
+ * The span of `next` whose flow blocks may differ from the ones the committed
48
+ * layout measured for `committed`, or `full` when no span bounds it.
49
+ *
50
+ * `full` also covers "nothing in the body flow changed": such a pass was
51
+ * requested for an input outside it (a header, footer or note story).
52
+ */
53
+ const diffAgainstCommittedLayout = (committed, next) => {
54
+ if (committed.preview.mode !== next.preview.mode) return FULL;
55
+ if (!committed.doc.sameMarkup(next.doc)) return FULL;
56
+ const docDiff = diffDocuments(committed.doc, next.doc);
57
+ let range = docDiff ? {
58
+ from: docDiff.start,
59
+ to: docDiff.endAfter
60
+ } : null;
61
+ if (committed.preview.entries !== next.preview.entries || committed.preview.hidden !== next.preview.hidden) {
62
+ const previousPreview = docDiff ? mapPreviewThroughDiff(committed.preview, docDiff) : committed.preview;
63
+ range = unionRanges(range, templatePreviewDirtyRange(previousPreview, next.preview));
64
+ }
65
+ return range ? {
66
+ type: "range",
67
+ range
68
+ } : FULL;
69
+ };
70
+ //#endregion
71
+ export { diffAgainstCommittedLayout };
@@ -1,5 +1,5 @@
1
- import { ContentControlWidgetEvent } from "../prosemirror/plugins/contentControlWidgets.js";
2
1
  import { Subscribable } from "../managers/Subscribable.js";
2
+ import { ContentControlWidgetEvent } from "../prosemirror/plugins/contentControlWidgets.js";
3
3
  import { EditorView } from "prosemirror-view";
4
4
  //#region src/controller/contentControlWidgetController.d.ts
5
5
  type ContentControlListItem = {
@@ -3,7 +3,7 @@ import { DocxInput } from "../utils/docxInput.js";
3
3
  import { Layout } from "../layout-engine/types.js";
4
4
  import { HiddenEditorApi } from "./hiddenEditorApi.js";
5
5
  import { FolioEditorEmitter } from "./folioEditorEvents.js";
6
- import { LayoutRunOptions } from "./layoutScheduler.js";
6
+ import { LayoutRunOptions } from "./layoutRunOptions.js";
7
7
  import { EditorState } from "prosemirror-state";
8
8
  //#region src/controller/folioEditor.d.ts
9
9
  type FolioEditorDeps = {
@@ -1,8 +1,58 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { EditorState } from "prosemirror-state";
3
3
  //#region src/controller/fontReadiness.d.ts
4
+ /** The parts of a `FontFace` layout readiness reads. */
5
+ type LayoutFontSetFace = Pick<FontFace, "family" | "status">;
6
+ type LayoutFontSetListener = (event: {
7
+ fontfaces: readonly LayoutFontSetFace[];
8
+ }) => void;
9
+ /**
10
+ * The parts of a `FontFaceSet` layout readiness uses; `document.fonts` in a
11
+ * browser, a scripted stand-in in tests.
12
+ */
13
+ type LayoutFontSet = Iterable<LayoutFontSetFace> & {
14
+ load: (font: string, text?: string) => Promise<unknown>;
15
+ readonly ready: Promise<unknown>;
16
+ addEventListener: (type: "loadingdone", listener: LayoutFontSetListener) => void;
17
+ removeEventListener: (type: "loadingdone", listener: LayoutFontSetListener) => void;
18
+ };
4
19
  declare function getDocumentFontSet(): FontFaceSet | null;
5
- declare function documentFontsAreLoaded(): boolean;
20
+ /**
21
+ * Names the faces available to measurement: which faces of the families
22
+ * measured stacks name have loaded. Equal signatures mean every measured glyph
23
+ * resolves to the same face, so a measurement taken under one is valid under
24
+ * the other; a host UI face loading leaves it unchanged.
25
+ *
26
+ * Counted over loaded faces only (a face never unloads, and a failed one adds
27
+ * no glyphs) with a per-face identity sum, so a removal replaced by an addition
28
+ * still changes it: a new face's id exceeds every earlier one. The family set
29
+ * only grows, and a family joins it when first measured, so read it after
30
+ * measuring to name what a layout measured in. Without a font set (a server or
31
+ * headless host) there is nothing to load and the signature is constant.
32
+ */
33
+ declare function readFontSetSignature(fontSet?: LayoutFontSet | null): string;
34
+ type WatchLayoutFontLoadsOptions = {
35
+ /**
36
+ * The font-set signature the committed layout was measured in, or `null`
37
+ * before one is committed (the first layout measures whatever has loaded).
38
+ */
39
+ measuredFontSet: () => string | null;
40
+ relayout: () => void;
41
+ fontSet?: LayoutFontSet | null;
42
+ };
43
+ /**
44
+ * Re-run layout whenever a face that can change a measured width finishes
45
+ * loading after the committed layout measured without it.
46
+ *
47
+ * Tied to what loaded, never to a time window: fontsource splits each face
48
+ * into `unicode-range` subsets the browser fetches when painted text first
49
+ * needs them, so a subset can land at any moment after the first layout. A
50
+ * load the committed layout already saw (its event can arrive after the layout
51
+ * ran) leaves the signature unchanged and relays nothing, as does a face of a
52
+ * family no measured stack names (a host UI font). Returns the unsubscribe
53
+ * function.
54
+ */
55
+ declare function watchLayoutFontLoads({ measuredFontSet, relayout, fontSet }: WatchLayoutFontLoadsOptions): () => void;
6
56
  declare const LAYOUT_FONT_DESCRIPTORS: readonly [{
7
57
  readonly style: "normal";
8
58
  readonly weight: 400;
@@ -21,8 +71,25 @@ type LayoutFontFace = {
21
71
  style: (typeof LAYOUT_FONT_DESCRIPTORS)[number]["style"];
22
72
  weight: (typeof LAYOUT_FONT_DESCRIPTORS)[number]["weight"];
23
73
  };
24
- declare function waitForInitialLayoutFonts(documentModel: document_d_exports.Document | null, pmDoc: EditorState["doc"]): Promise<boolean>;
74
+ /**
75
+ * Resolve once every face the first layout will measure has loaded (`true`),
76
+ * or after a timeout (`false`), so the first layout does not measure fallbacks.
77
+ *
78
+ * Each face is loaded for the document's own characters. A bundled face is a
79
+ * set of `unicode-range` subsets, and `FontFaceSet.load` fetches only the
80
+ * subsets covering the text it is given; without text it takes a single space
81
+ * and loads only the Latin subset, so Czech, Polish, Greek and Cyrillic were
82
+ * measured in a fallback.
83
+ */
84
+ declare function waitForInitialLayoutFonts(documentModel: document_d_exports.Document | null, pmDoc: EditorState["doc"], fontSet?: LayoutFontSet | null): Promise<boolean>;
85
+ /**
86
+ * Every distinct character the layout will measure, as one string: the body
87
+ * text (tracked deletions included, since All Markup paints them), list
88
+ * markers, headers, footers and notes. Always carries a space, which every
89
+ * line measures.
90
+ */
91
+ declare function collectLayoutText(documentModel: document_d_exports.Document | null, pmDoc: EditorState["doc"]): string;
25
92
  declare function collectInitialLayoutFontFamilies(documentModel: document_d_exports.Document | null, pmDoc: EditorState["doc"]): Set<string>;
26
93
  declare function collectInitialLayoutFontFaces(documentModel: document_d_exports.Document | null, pmDoc: EditorState["doc"]): LayoutFontFace[];
27
94
  //#endregion
28
- export { LayoutFontFace, collectInitialLayoutFontFaces, collectInitialLayoutFontFamilies, documentFontsAreLoaded, getDocumentFontSet, waitForInitialLayoutFonts };
95
+ export { LayoutFontFace, LayoutFontSet, LayoutFontSetFace, WatchLayoutFontLoadsOptions, collectInitialLayoutFontFaces, collectInitialLayoutFontFamilies, collectLayoutText, getDocumentFontSet, readFontSetSignature, waitForInitialLayoutFonts, watchLayoutFontLoads };
@@ -1,5 +1,6 @@
1
+ import { blockPlainText } from "../docx/blockPlainText.js";
1
2
  import { buildFontAlternates, getFontAlternate } from "../fonts/fontAlternates.js";
2
- import { DEFAULT_FONT_FAMILY } from "../layout-engine/measure/measureHelpers.js";
3
+ import { DEFAULT_FONT_FAMILY, isLayoutFontFamily } from "../layout-engine/measure/measureHelpers.js";
3
4
  import { expectFontFamilyMarkAttrs, expectParagraphAttrs } from "../prosemirror/attrs/index.js";
4
5
  import { parseFontFamilyList, resolveFontFamily } from "../utils/fontResolver.js";
5
6
  //#region src/controller/fontReadiness.ts
@@ -7,9 +8,62 @@ function getDocumentFontSet() {
7
8
  if (typeof document === "undefined" || !("fonts" in document)) return null;
8
9
  return document.fonts;
9
10
  }
10
- function documentFontsAreLoaded() {
11
- const fontSet = getDocumentFontSet();
12
- return !fontSet || fontSet.status === "loaded";
11
+ /**
12
+ * Names the faces available to measurement: which faces of the families
13
+ * measured stacks name have loaded. Equal signatures mean every measured glyph
14
+ * resolves to the same face, so a measurement taken under one is valid under
15
+ * the other; a host UI face loading leaves it unchanged.
16
+ *
17
+ * Counted over loaded faces only (a face never unloads, and a failed one adds
18
+ * no glyphs) with a per-face identity sum, so a removal replaced by an addition
19
+ * still changes it: a new face's id exceeds every earlier one. The family set
20
+ * only grows, and a family joins it when first measured, so read it after
21
+ * measuring to name what a layout measured in. Without a font set (a server or
22
+ * headless host) there is nothing to load and the signature is constant.
23
+ */
24
+ function readFontSetSignature(fontSet = getDocumentFontSet()) {
25
+ if (!fontSet) return NO_FONT_SET_SIGNATURE;
26
+ let loadedFaces = 0;
27
+ let loadedFaceIdSum = 0;
28
+ for (const face of fontSet) {
29
+ if (face.status !== "loaded" || !isLayoutFontFamily(face.family)) continue;
30
+ loadedFaces += 1;
31
+ loadedFaceIdSum += fontFaceId(face);
32
+ }
33
+ return `${loadedFaces}:${loadedFaceIdSum}`;
34
+ }
35
+ const NO_FONT_SET_SIGNATURE = "none";
36
+ const fontFaceIds = /* @__PURE__ */ new WeakMap();
37
+ let nextFontFaceId = 1;
38
+ const fontFaceId = (face) => {
39
+ const known = fontFaceIds.get(face);
40
+ if (known !== void 0) return known;
41
+ const id = nextFontFaceId;
42
+ nextFontFaceId += 1;
43
+ fontFaceIds.set(face, id);
44
+ return id;
45
+ };
46
+ /**
47
+ * Re-run layout whenever a face that can change a measured width finishes
48
+ * loading after the committed layout measured without it.
49
+ *
50
+ * Tied to what loaded, never to a time window: fontsource splits each face
51
+ * into `unicode-range` subsets the browser fetches when painted text first
52
+ * needs them, so a subset can land at any moment after the first layout. A
53
+ * load the committed layout already saw (its event can arrive after the layout
54
+ * ran) leaves the signature unchanged and relays nothing, as does a face of a
55
+ * family no measured stack names (a host UI font). Returns the unsubscribe
56
+ * function.
57
+ */
58
+ function watchLayoutFontLoads({ measuredFontSet, relayout, fontSet = getDocumentFontSet() }) {
59
+ if (!fontSet) return () => void 0;
60
+ const handleLoadingDone = () => {
61
+ const measured = measuredFontSet();
62
+ if (measured === null || measured === readFontSetSignature(fontSet)) return;
63
+ relayout();
64
+ };
65
+ fontSet.addEventListener("loadingdone", handleLoadingDone);
66
+ return () => fontSet.removeEventListener("loadingdone", handleLoadingDone);
13
67
  }
14
68
  const INITIAL_LAYOUT_FONT_TIMEOUT_MS = 2e3;
15
69
  const CSS_GENERIC_FONT_FAMILIES = /* @__PURE__ */ new Set([
@@ -38,15 +92,51 @@ const REGULAR_LAYOUT_FONT_DESCRIPTOR = [
38
92
  weight: 700
39
93
  }
40
94
  ][0];
41
- function waitForInitialLayoutFonts(documentModel, pmDoc) {
42
- const fontSet = getDocumentFontSet();
95
+ /**
96
+ * Resolve once every face the first layout will measure has loaded (`true`),
97
+ * or after a timeout (`false`), so the first layout does not measure fallbacks.
98
+ *
99
+ * Each face is loaded for the document's own characters. A bundled face is a
100
+ * set of `unicode-range` subsets, and `FontFaceSet.load` fetches only the
101
+ * subsets covering the text it is given; without text it takes a single space
102
+ * and loads only the Latin subset, so Czech, Polish, Greek and Cyrillic were
103
+ * measured in a fallback.
104
+ */
105
+ function waitForInitialLayoutFonts(documentModel, pmDoc, fontSet = getDocumentFontSet()) {
43
106
  if (!fontSet) return Promise.resolve(true);
107
+ const text = collectLayoutText(documentModel, pmDoc);
44
108
  const loadChecks = [];
45
109
  for (const face of collectInitialLayoutFontFaces(documentModel, pmDoc)) loadChecks.push(`${face.style} ${face.weight} 16px "${escapeCssFontFamily(face.family)}"`);
46
- const loadFonts = Promise.allSettled(loadChecks.map((check) => fontSet.load(check))).then(() => fontSet.ready).then(() => true);
110
+ let timeout;
111
+ const loadFonts = Promise.allSettled(loadChecks.map((check) => fontSet.load(check, text))).then(() => fontSet.ready).then(() => true);
47
112
  return Promise.race([loadFonts, new Promise((resolve) => {
48
- globalThis.setTimeout(() => resolve(false), INITIAL_LAYOUT_FONT_TIMEOUT_MS);
49
- })]);
113
+ timeout = globalThis.setTimeout(() => resolve(false), INITIAL_LAYOUT_FONT_TIMEOUT_MS);
114
+ })]).finally(() => globalThis.clearTimeout(timeout));
115
+ }
116
+ /**
117
+ * Every distinct character the layout will measure, as one string: the body
118
+ * text (tracked deletions included, since All Markup paints them), list
119
+ * markers, headers, footers and notes. Always carries a space, which every
120
+ * line measures.
121
+ */
122
+ function collectLayoutText(documentModel, pmDoc) {
123
+ const characters = /* @__PURE__ */ new Set([" "]);
124
+ const addText = (text) => {
125
+ for (const character of text) if (character >= " ") characters.add(character);
126
+ };
127
+ pmDoc.descendants((node) => {
128
+ if (node.isText) addText(node.text ?? "");
129
+ else if (node.type.name === "paragraph") addText(expectParagraphAttrs(node).listMarker ?? "");
130
+ return true;
131
+ });
132
+ const documentPackage = documentModel?.package;
133
+ for (const story of [
134
+ ...documentPackage?.headers?.values() ?? [],
135
+ ...documentPackage?.footers?.values() ?? [],
136
+ ...documentPackage?.footnotes ?? [],
137
+ ...documentPackage?.endnotes ?? []
138
+ ]) addText(blockPlainText(story.content));
139
+ return Array.from(characters).join("");
50
140
  }
51
141
  function collectInitialLayoutFontFamilies(documentModel, pmDoc) {
52
142
  return new Set(collectInitialLayoutFontFaces(documentModel, pmDoc).map(({ family }) => family));
@@ -150,4 +240,4 @@ function escapeCssFontFamily(family) {
150
240
  return family.replace(/\\/gu, "\\\\").replace(/"/gu, "\\\"");
151
241
  }
152
242
  //#endregion
153
- export { collectInitialLayoutFontFaces, collectInitialLayoutFontFamilies, documentFontsAreLoaded, getDocumentFontSet, waitForInitialLayoutFonts };
243
+ export { collectInitialLayoutFontFaces, collectInitialLayoutFontFamilies, collectLayoutText, getDocumentFontSet, readFontSetSignature, waitForInitialLayoutFonts, watchLayoutFontLoads };
@@ -9,6 +9,7 @@ import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdA
9
9
  import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNumbering.js";
10
10
  import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
11
11
  import { schema } from "../prosemirror/schema/index.js";
12
+ import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
12
13
  import { EditorState } from "prosemirror-state";
13
14
  import { EditorView } from "prosemirror-view";
14
15
  //#region src/controller/headerFooterEditorManager.ts
@@ -153,14 +154,17 @@ const createHeaderFooterEditorManager = (deps) => {
153
154
  let footers = document.package.footers;
154
155
  let headersChanged = false;
155
156
  let footersChanged = false;
157
+ let numbering = document.package.numbering;
156
158
  for (const { dirty, kind, rId, view } of mounted.values()) {
157
159
  if (!dirty) continue;
158
160
  const source = kind === "header" ? headers : footers;
159
161
  const existing = source?.get(rId);
160
162
  if (!source || !existing) continue;
163
+ const story = storyListNumbering(view.state, numbering);
164
+ numbering = story.numbering;
161
165
  const updated = {
162
166
  ...existing,
163
- content: proseDocToBlocks(view.state.doc, existing.content, document.package.styles)
167
+ content: proseDocToBlocks(story.doc, existing.content, document.package.styles)
164
168
  };
165
169
  clearHeaderFooterVerbatimXml(updated);
166
170
  if (kind === "header") {
@@ -179,6 +183,7 @@ const createHeaderFooterEditorManager = (deps) => {
179
183
  }
180
184
  if (!headersChanged && !footersChanged) return document;
181
185
  const packageWithSnapshots = { ...document.package };
186
+ if (numbering) packageWithSnapshots.numbering = numbering;
182
187
  if (headersChanged && headers) packageWithSnapshots.headers = headers;
183
188
  if (footersChanged && footers) packageWithSnapshots.footers = footers;
184
189
  return {
@@ -1,8 +1,9 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { TemplatePreviewEntry, TemplatePreviewHiddenRange } from "../prosemirror/plugins/templatePreviewValues.js";
3
3
  import { ColumnLayout, FlowBlock, FootnoteContent, HeaderFooterContent, Layout, Measure, PageHeaderFooterRefs, PageMargins } from "../layout-engine/types.js";
4
- import { LayoutRunReason } from "../layout-engine/layoutInstrumentation.js";
5
- import { DirtyRange } from "../paged-layout/incrementalMeasure.js";
4
+ import { DisplayMode } from "../managers/EditorModeManager.js";
5
+ import { LayoutSession } from "./layoutSession.js";
6
+ import { LayoutRunOptions } from "./layoutRunOptions.js";
6
7
  import { HyphenationReadiness } from "./hyphenationReadiness.js";
7
8
  import { ConvertHeaderFooterOptions, HeaderFooterMetrics } from "../layout-bridge/convert/headerFooterLayout.js";
8
9
  import "../layout-engine/index.js";
@@ -10,14 +11,8 @@ import { FootnoteRenderItem } from "../layout-painter/renderPage.js";
10
11
  import { BlockLookup, LayoutPainter } from "../layout-painter/index.js";
11
12
  import { PageRendererName } from "../display-list/editor/pageRenderer.js";
12
13
  import { LayoutSelectionGate } from "../paged-layout/LayoutSelectionGate.js";
13
- import { LayoutSession } from "./layoutSession.js";
14
14
  import { EditorState } from "prosemirror-state";
15
15
  //#region src/controller/layoutPipeline.d.ts
16
- type LayoutRunOptions = {
17
- dirtyRange?: DirtyRange;
18
- forceFull?: boolean;
19
- reason?: LayoutRunReason;
20
- };
21
16
  type LayoutOutcome = {
22
17
  blocks?: FlowBlock[];
23
18
  measures?: Measure[];
@@ -58,7 +53,8 @@ type LayoutPipelineDeps<THfPMs> = {
58
53
  session: LayoutSession;
59
54
  renderHfFromContentOrPm: (hf: document_d_exports.HeaderFooter | null | undefined, rId: string | null | undefined, hfPMs: THfPMs, contentWidth: number, metrics: HeaderFooterMetrics, options: ConvertHeaderFooterOptions) => HeaderFooterContent | undefined;
60
55
  renderHeaderFooterContentByRId: (source: Map<string, document_d_exports.HeaderFooter> | undefined, hfPMs: THfPMs, contentWidth: number, metrics: HeaderFooterMetrics, options: ConvertHeaderFooterOptions) => Map<string, HeaderFooterContent> | undefined;
61
- documentFontsAreLoaded: () => boolean;
56
+ /** Names the faces measurement can use now (`fontReadiness.readFontSetSignature`). */
57
+ readFontSetSignature: () => string;
62
58
  buildFootnoteRenderItems: (pageFootnoteMap: Map<number, number[]>, footnoteContentMap: Map<number, FootnoteContent>, doc: document_d_exports.Document | null) => Map<number, FootnoteRenderItem[]>;
63
59
  describeInvalidHighlightMarks: (doc: EditorState["doc"]) => string;
64
60
  /**
@@ -77,7 +73,13 @@ type LayoutPipelineDeps<THfPMs> = {
77
73
  emptyTemplatePreviewHidden: readonly TemplatePreviewHiddenRange[];
78
74
  /** Follows up on the hyphenation dictionaries a run lacked (relayout or error). */
79
75
  hyphenationReadiness: HyphenationReadiness;
76
+ /**
77
+ * The review view the body is laid out for. A view other than All Markup
78
+ * lays out the text it shows (every revision accepted or rejected), not the
79
+ * authored text with runs hidden afterwards.
80
+ */
81
+ markupView: DisplayMode;
80
82
  };
81
83
  declare function runLayoutPipeline<THfPMs>(deps: LayoutPipelineDeps<THfPMs>, state: EditorState, options?: LayoutRunOptions): LayoutOutcome;
82
84
  //#endregion
83
- export { LayoutOutcome, LayoutPipelineDeps, LayoutRunOptions, runLayoutPipeline };
85
+ export { LayoutOutcome, LayoutPipelineDeps, runLayoutPipeline };