@stll/folio-core 0.51.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 (316) 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/inline-provenance.js +18 -22
  47. package/dist/compare/plan.js +10 -5
  48. package/dist/compare/scenario.js +14 -3
  49. package/dist/compare/section-boundary-properties.js +14 -1
  50. package/dist/compare/types.d.ts +4 -2
  51. package/dist/compare/types.js +2 -1
  52. package/dist/compare/verification.d.ts +1 -1
  53. package/dist/compare/verification.js +15 -3
  54. package/dist/controller/committedLayoutDiff.d.ts +25 -0
  55. package/dist/controller/committedLayoutDiff.js +71 -0
  56. package/dist/controller/contentControlWidgetController.d.ts +1 -1
  57. package/dist/controller/folioEditor.d.ts +1 -1
  58. package/dist/controller/fontReadiness.d.ts +70 -3
  59. package/dist/controller/fontReadiness.js +100 -10
  60. package/dist/controller/headerFooterEditorManager.js +6 -1
  61. package/dist/controller/layoutPipeline.d.ts +12 -10
  62. package/dist/controller/layoutPipeline.js +62 -9
  63. package/dist/controller/layoutRunOptions.d.ts +24 -0
  64. package/dist/controller/layoutRunOptions.js +17 -0
  65. package/dist/controller/layoutScheduler.d.ts +19 -14
  66. package/dist/controller/layoutScheduler.js +15 -12
  67. package/dist/controller/layoutSession.d.ts +26 -2
  68. package/dist/controller/layoutSession.js +1 -1
  69. package/dist/controller/noteEditorManager.js +8 -3
  70. package/dist/document-operations.d.ts +11 -2
  71. package/dist/document-operations.js +230 -21
  72. package/dist/docx/altChunk.d.ts +8 -0
  73. package/dist/docx/altChunk.js +17 -0
  74. package/dist/docx/attributeRemainder.d.ts +1 -1
  75. package/dist/docx/blockContentParser.d.ts +64 -1
  76. package/dist/docx/blockContentParser.js +17 -4
  77. package/dist/docx/blockCustomXmlShell.d.ts +7 -0
  78. package/dist/docx/blockCustomXmlShell.js +14 -0
  79. package/dist/docx/blockPlainText.js +7 -0
  80. package/dist/docx/commentAnchorIndex.js +1 -1
  81. package/dist/docx/commentRangeIntegrity.js +8 -3
  82. package/dist/docx/commentReferenceNormalization.js +1 -1
  83. package/dist/docx/commentReplyMarkers.js +20 -1
  84. package/dist/docx/compatibility.d.ts +1 -1
  85. package/dist/docx/compatibility.js +34 -13
  86. package/dist/docx/containerChildren.gen.d.ts +1 -1
  87. package/dist/docx/containerChildren.gen.js +1 -0
  88. package/dist/docx/documentParser.js +12 -12
  89. package/dist/docx/drawingIdNormalization.d.ts +3 -1
  90. package/dist/docx/drawingIdNormalization.js +6 -1
  91. package/dist/docx/ensureParaIds.js +119 -97
  92. package/dist/docx/footnoteParser.js +8 -4
  93. package/dist/docx/headerFooterParser.js +3 -3
  94. package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
  95. package/dist/docx/headerFooterReferenceNormalization.js +52 -27
  96. package/dist/docx/hyperlinkParser.d.ts +1 -1
  97. package/dist/docx/index.d.ts +1 -1
  98. package/dist/docx/listNumberingInstances.d.ts +59 -0
  99. package/dist/docx/listNumberingInstances.js +249 -0
  100. package/dist/docx/normalizeBaseDirection.js +2 -1
  101. package/dist/docx/opaqueCarrier.d.ts +7 -0
  102. package/dist/docx/opaqueCarrier.js +38 -0
  103. package/dist/docx/packageParts.js +4 -1
  104. package/dist/docx/paragraphParser.js +91 -24
  105. package/dist/docx/paragraphPropertySource.d.ts +4 -2
  106. package/dist/docx/paragraphPropertySource.js +7 -1
  107. package/dist/docx/paragraphTraversal.js +1 -0
  108. package/dist/docx/parseWarningMessage.js +4 -1
  109. package/dist/docx/parser.js +173 -20
  110. package/dist/docx/rezip.js +27 -5
  111. package/dist/docx/sdtProperties.js +1 -1
  112. package/dist/docx/sdtPropertiesPatch.js +3 -0
  113. package/dist/docx/selectiveSave.js +16 -9
  114. package/dist/docx/selectiveXmlPatch.js +106 -37
  115. package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
  116. package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
  117. package/dist/docx/serializer/documentSerializer.js +2 -0
  118. package/dist/docx/serializer/headerFooterSerializer.js +2 -0
  119. package/dist/docx/serializer/noteSerializer.js +3 -1
  120. package/dist/docx/serializer/paragraphSerializer.js +33 -9
  121. package/dist/docx/serializer/tableSerializer.js +25 -38
  122. package/dist/docx/server/createBilingualDocument.js +3 -3
  123. package/dist/docx/tableParser.d.ts +157 -1
  124. package/dist/docx/tableParser.js +89 -10
  125. package/dist/docx/textBoxParser.js +1 -1
  126. package/dist/docx/unzip.d.ts +2 -1
  127. package/dist/docx/unzip.js +1 -1
  128. package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
  129. package/dist/docx/wordprocessingPrefixes.js +286 -0
  130. package/dist/generated/text_shaper.js +26 -0
  131. package/dist/generated/text_shaper_bg.wasm +0 -0
  132. package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
  133. package/dist/i18n/messages/catalogs.gen.js +153 -17
  134. package/dist/i18n/messages/messages.gen.d.ts +8 -0
  135. package/dist/internal/compare/inline-presentation.d.ts +2 -1
  136. package/dist/internal/compare/inline-presentation.js +11 -10
  137. package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
  138. package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
  139. package/dist/internal/indexedPositionMap.d.ts +11 -0
  140. package/dist/internal/indexedPositionMap.js +42 -0
  141. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  142. package/dist/internal/revisionResolutionEdits.d.ts +13 -0
  143. package/dist/internal/revisionResolutionEdits.js +19 -0
  144. package/dist/internal/revisionResolutionInline.d.ts +24 -0
  145. package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
  146. package/dist/internal/revisionResolutionStep.d.ts +34 -0
  147. package/dist/internal/revisionResolutionStep.js +282 -0
  148. package/dist/internal/revisionResolutionTracking.d.ts +11 -0
  149. package/dist/internal/revisionResolutionTracking.js +124 -0
  150. package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
  151. package/dist/internal/wholeStoryRevisionResolution.js +530 -0
  152. package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
  153. package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
  154. package/dist/layout-bridge/convert/tableConversion.js +1 -1
  155. package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
  156. package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
  157. package/dist/layout-engine/layoutInstrumentation.js +4 -1
  158. package/dist/layout-engine/measure/cache.d.ts +18 -1
  159. package/dist/layout-engine/measure/cache.js +31 -1
  160. package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
  161. package/dist/layout-engine/measure/measureContainer.js +7 -47
  162. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
  163. package/dist/layout-engine/measure/measureHelpers.js +22 -2
  164. package/dist/layout-engine/measure/measureWorker.js +2 -3
  165. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
  166. package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
  167. package/dist/layout-engine/types.d.ts +6 -0
  168. package/dist/layout-painter/renderPage.js +1 -0
  169. package/dist/layout-painter/renderParagraph.js +1 -1
  170. package/dist/managers/ContextMenuManager.js +5 -2
  171. package/dist/managers/types.d.ts +3 -0
  172. package/dist/markdown/fromMarkdown.js +2 -1
  173. package/dist/markdown/internals.d.ts +11 -1
  174. package/dist/markdown/internals.js +24 -3
  175. package/dist/markdown/renderBlock.js +39 -9
  176. package/dist/markdown/renderParagraph.d.ts +13 -1
  177. package/dist/markdown/renderParagraph.js +85 -38
  178. package/dist/markdown/renderRuns.js +3 -10
  179. package/dist/markdown/renderTable.js +24 -12
  180. package/dist/markdown/trailers.js +2 -2
  181. package/dist/markdown/types.d.ts +29 -7
  182. package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
  183. package/dist/paged-layout/incrementalMeasure.js +1 -9
  184. package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
  185. package/dist/paged-layout/transactionDirtyRange.js +29 -41
  186. package/dist/panel-layout.d.ts +83 -0
  187. package/dist/panel-layout.js +96 -0
  188. package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
  189. package/dist/prosemirror/anchoredTextBoxes.js +156 -0
  190. package/dist/prosemirror/attrs/index.d.ts +6 -2
  191. package/dist/prosemirror/attrs/index.js +172 -1
  192. package/dist/prosemirror/commands/comments.d.ts +7 -8
  193. package/dist/prosemirror/commands/comments.js +241 -207
  194. package/dist/prosemirror/commands/contentControls.d.ts +1 -1
  195. package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
  196. package/dist/prosemirror/commands/formatting.d.ts +1 -1
  197. package/dist/prosemirror/commands/index.d.ts +2 -2
  198. package/dist/prosemirror/commands/index.js +2 -2
  199. package/dist/prosemirror/commands/paragraph.d.ts +7 -1
  200. package/dist/prosemirror/commands/paragraph.js +9 -1
  201. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
  202. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
  203. package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
  204. package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
  205. package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
  206. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
  207. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
  208. package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
  209. package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
  210. package/dist/prosemirror/commands/sectionBreak.js +2 -1
  211. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
  212. package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
  213. package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
  214. package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
  215. package/dist/prosemirror/conversion/toProseDoc.js +39 -4
  216. package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
  217. package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
  218. package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
  219. package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
  220. package/dist/prosemirror/extensions/StarterKit.js +3 -1
  221. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  222. package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
  223. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
  224. package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
  225. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
  226. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
  227. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
  228. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
  229. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
  230. package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
  231. package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
  232. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
  233. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
  234. package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
  235. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
  236. package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
  237. package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
  238. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
  239. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
  240. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
  241. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
  242. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
  243. package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
  244. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
  245. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  246. package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
  247. package/dist/prosemirror/extensions/types.d.ts +4 -1
  248. package/dist/prosemirror/findReplaceSelection.js +1 -1
  249. package/dist/prosemirror/index.d.ts +3 -2
  250. package/dist/prosemirror/index.js +3 -2
  251. package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
  252. package/dist/prosemirror/indexedNodeLookup.js +103 -0
  253. package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
  254. package/dist/prosemirror/listAutoformatMarkers.js +98 -0
  255. package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
  256. package/dist/prosemirror/listInstanceReferences.js +72 -0
  257. package/dist/prosemirror/listLabels.d.ts +24 -0
  258. package/dist/prosemirror/listLabels.js +111 -0
  259. package/dist/prosemirror/listNumbering.d.ts +110 -0
  260. package/dist/prosemirror/listNumbering.js +345 -0
  261. package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
  262. package/dist/prosemirror/listRenderingAttrs.js +8 -1
  263. package/dist/prosemirror/markupViewProjection.d.ts +95 -0
  264. package/dist/prosemirror/markupViewProjection.js +83 -0
  265. package/dist/prosemirror/numberingAttr.d.ts +9 -1
  266. package/dist/prosemirror/numberingAttr.js +9 -1
  267. package/dist/prosemirror/outlineLevelAttr.js +1 -1
  268. package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
  269. package/dist/prosemirror/paragraphIndentation.js +29 -7
  270. package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
  271. package/dist/prosemirror/paragraphMarkJoin.js +24 -0
  272. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
  273. package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
  274. package/dist/prosemirror/plugins/documentNumbering.js +97 -11
  275. package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
  276. package/dist/prosemirror/plugins/documentStyles.js +14 -1
  277. package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
  278. package/dist/prosemirror/plugins/suggestionMode.js +116 -52
  279. package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
  280. package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
  281. package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
  282. package/dist/prosemirror/positionSweep.d.ts +24 -0
  283. package/dist/prosemirror/positionSweep.js +222 -0
  284. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
  285. package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
  286. package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
  287. package/dist/prosemirror/replacedAnnotations.js +170 -1
  288. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
  289. package/dist/prosemirror/runStyleFormatting.d.ts +7 -1
  290. package/dist/prosemirror/runStyleFormatting.js +13 -7
  291. package/dist/prosemirror/schema/index.d.ts +2 -2
  292. package/dist/prosemirror/schema/index.js +3 -0
  293. package/dist/prosemirror/schema/nodes.d.ts +42 -11
  294. package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
  295. package/dist/prosemirror/sectionMarkEdits.js +59 -0
  296. package/dist/prosemirror/storyListNumbering.d.ts +17 -0
  297. package/dist/prosemirror/storyListNumbering.js +102 -0
  298. package/dist/prosemirror/tableGridMutation.d.ts +48 -2
  299. package/dist/prosemirror/tableGridMutation.js +106 -1
  300. package/dist/prosemirror/validation.js +5 -1
  301. package/dist/redline.js +93 -9
  302. package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
  303. package/dist/render-dom/BodySelectionOverlay.js +3 -2
  304. package/dist/shaping/shaper.d.ts +47 -4
  305. package/dist/shaping/shaper.js +46 -15
  306. package/dist/text-shaping.d.ts +4 -0
  307. package/dist/text-shaping.js +4 -0
  308. package/dist/utils/findReplace.js +2 -2
  309. package/dist/utils/mergeDocumentContent.js +1 -1
  310. package/dist/utils/noteReferenceLabels.d.ts +36 -0
  311. package/dist/utils/noteReferenceLabels.js +32 -0
  312. package/dist/utils/replaceText.js +1 -1
  313. package/dist/version-comparison.d.ts +1 -0
  314. package/dist/version-comparison.js +1 -0
  315. package/package.json +8 -2
  316. package/dist/internal/headlessRevisionResolution.d.ts +0 -23
@@ -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 };
@@ -11,16 +11,18 @@ import { buildSeqValues } from "../fields/seqValues.js";
11
11
  import { buildFontAlternates } from "../fonts/fontAlternates.js";
12
12
  import { prepareEndnoteAreas, resolveEndnotePosition, spliceEndnoteAreas, trailingEndnoteIds } from "../layout-bridge/convert/endnoteLayout.js";
13
13
  import { buildFootnoteContentMap, collectEndnoteRefs, collectFootnoteRefs, computeNoteDisplayNumbers, remapNoteMarkerText } from "../layout-bridge/convert/footnoteLayout.js";
14
+ import { remapMarkupViewBlocks } from "../layout-bridge/convert/markupViewFlow.js";
14
15
  import { applyTemplatePreviewToBlocks } from "../layout-bridge/convert/templatePreviewFlow.js";
15
16
  import { toFlowBlocks } from "../layout-bridge/convert/toFlowBlocks.js";
16
17
  import { getColumns } from "../layout-bridge/sectionColumns.js";
17
18
  import { resolveAnchorLayoutInCellCompatibility } from "../layout-engine/anchorLayoutInCellCompatibility.js";
18
19
  import { layoutDocument } from "../layout-engine/index.js";
19
20
  import { resolveJustificationCompatibility } from "../layout-engine/justificationCompatibility.js";
20
- import { recordLayoutComplete, recordLayoutError, recordLayoutPhase, recordLayoutStart } from "../layout-engine/layoutInstrumentation.js";
21
+ import { recordLayoutComplete, recordLayoutError, recordLayoutPhase, recordLayoutStart, recordMarkupViewIncomplete } from "../layout-engine/layoutInstrumentation.js";
22
+ import { labelMeasureCachesFontSet, syncMeasureCachesToFontSet } from "../layout-engine/measure/cache.js";
21
23
  import { collectRequestedHyphenationDictionaries } from "../layout-engine/measure/hyphenationDictionaries.js";
22
24
  import { measureBlocks, measureSingleBlockWithoutFloatingZones } from "../layout-engine/measure/measureBlocks.js";
23
- import { installCanvasMeasureProvider } from "../layout-engine/measure/measureContainer.js";
25
+ import { installCanvasMeasureProvider, resetCanvasContext } from "../layout-engine/measure/measureContainer.js";
24
26
  import { resolveEffectiveParagraphSpacingTree } from "../layout-engine/paragraphSpacing.js";
25
27
  import { resolveTableIndentCompatibility } from "../layout-engine/tableIndentCompatibility.js";
26
28
  import "../layout-engine/types.js";
@@ -28,14 +30,39 @@ import { renderPages } from "../layout-painter/renderPage.js";
28
30
  import { tryBuildIncrementalMeasures } from "../paged-layout/incrementalMeasure.js";
29
31
  import { computePerBlockMeasureInputs } from "../paged-layout/sectionBlockWidths.js";
30
32
  import { getMargins, getPageNumbering, getPageSize, twipsToPixels } from "../paged-layout/sectionGeometry.js";
33
+ import { projectMarkupView } from "../prosemirror/markupViewProjection.js";
31
34
  import { templatePreviewValuesKey } from "../prosemirror/plugins/templatePreviewValues.js";
32
35
  import { getDocumentWatermark } from "../watermark/index.js";
36
+ import { diffAgainstCommittedLayout } from "./committedLayoutDiff.js";
37
+ import { LAYOUT_MEASURE } from "./layoutRunOptions.js";
33
38
  //#region src/controller/layoutPipeline.ts
34
39
  const formatEndnoteTexts = (numbers, formatNumber) => {
35
40
  const texts = /* @__PURE__ */ new Map();
36
41
  for (const [id, displayNumber] of numbers) texts.set(id, formatNumber(displayNumber));
37
42
  return texts;
38
43
  };
44
+ const sameMeasureInputs = (left, right) => left.styles === right.styles && left.theme === right.theme && left.defaultTabStop === right.defaultTabStop && left.pageContentHeight === right.pageContentHeight && left.fontSet === right.fontSet && left.fontAlternates.size === right.fontAlternates.size && [...left.fontAlternates].every(([name, alternate]) => right.fontAlternates.get(name) === alternate) && left.markupView === right.markupView;
45
+ /**
46
+ * The range an incremental pass re-measures, or `null` for a full measure: the
47
+ * committed measures must come from the same measure inputs, and the range is
48
+ * where the current document and preview differ from the committed ones.
49
+ */
50
+ const incrementalDirtyRange = ({ session, state, preview, measureInputs }) => {
51
+ const { lastPmDoc, lastMeasureInputs } = session;
52
+ if (!lastPmDoc || !lastMeasureInputs || !sameMeasureInputs(lastMeasureInputs, measureInputs)) return null;
53
+ const diff = diffAgainstCommittedLayout({
54
+ doc: lastPmDoc,
55
+ preview: session.lastTemplatePreview
56
+ }, {
57
+ doc: state.doc,
58
+ preview
59
+ });
60
+ switch (diff.type) {
61
+ case "full": return null;
62
+ case "range": return diff.range;
63
+ default: return diff;
64
+ }
65
+ };
39
66
  /** Resolve only package-owned picture-watermark bytes for the DOM painter. */
40
67
  const resolveWatermarkImageSrc = (document, watermark) => {
41
68
  if (watermark?.kind !== "picture" || watermark.imageTargetExternal === true) return;
@@ -108,11 +135,13 @@ function runLayoutPipeline(deps, state, options = {}) {
108
135
  return result;
109
136
  }
110
137
  function runLayoutPipelineMeasured(deps, state, options) {
111
- const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, documentFontsAreLoaded, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN } = deps;
138
+ const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, readFontSetSignature, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN, markupView } = deps;
112
139
  let outcome = {};
113
140
  let pendingArtifacts;
114
141
  let pendingTemplatePreview;
115
142
  installCanvasMeasureProvider();
143
+ const fontSet = readFontSetSignature();
144
+ if (syncMeasureCachesToFontSet(fontSet)) resetCanvasContext();
116
145
  const reason = options.reason ?? "manual";
117
146
  const recordPhaseDuration = (phase, startedAt) => {
118
147
  recordLayoutPhase(reason, phase, performance.now() - startedAt);
@@ -124,6 +153,15 @@ function runLayoutPipelineMeasured(deps, state, options) {
124
153
  let phaseStartedAt = performance.now();
125
154
  const pageContentHeight = pageSize.h - margins.top - margins.bottom;
126
155
  const fontAlternates = buildFontAlternates(document?.package.fontTable);
156
+ const measureInputs = {
157
+ styles,
158
+ theme: _theme,
159
+ defaultTabStop,
160
+ pageContentHeight,
161
+ fontSet,
162
+ fontAlternates,
163
+ markupView
164
+ };
127
165
  const flowOpts = {
128
166
  pageContentHeight,
129
167
  fontAlternates,
@@ -151,10 +189,14 @@ function runLayoutPipelineMeasured(deps, state, options) {
151
189
  const endnotePosition = resolveEndnotePosition(document?.package.settings?.endnotePr?.position, document?.package.document.sections?.at(-1)?.properties);
152
190
  const bodyTrailingEndnoteIds = trailingEndnoteIds(document?.package.endnotes, endnotePosition);
153
191
  if (bodyTrailingEndnoteIds.size > 0) flowOpts.trailingEndnoteIds = bodyTrailingEndnoteIds;
154
- let newBlocks = toFlowBlocks(document === null ? state.doc.type.create({
155
- ...state.doc.attrs,
192
+ const projection = projectMarkupView(state, markupView);
193
+ if (projection.type === "resolved" && projection.completeness === "partial") recordMarkupViewIncomplete(markupView);
194
+ const viewDoc = projection.type === "resolved" ? projection.doc : state.doc;
195
+ let newBlocks = toFlowBlocks(document === null ? viewDoc.type.create({
196
+ ...viewDoc.attrs,
156
197
  _finalSectionStart: sectionProperties?.sectionStart ?? null
157
- }, state.doc.content, state.doc.marks) : state.doc, flowOpts);
198
+ }, viewDoc.content, viewDoc.marks) : viewDoc, flowOpts);
199
+ if (projection.type === "resolved") newBlocks = remapMarkupViewBlocks(newBlocks, projection);
158
200
  const previewState = templatePreviewValuesKey.getState(state);
159
201
  const previewEntries = previewState?.entries ?? EMPTY_TEMPLATE_PREVIEW_ENTRIES;
160
202
  const previewHidden = previewState?.hidden ?? EMPTY_TEMPLATE_PREVIEW_HIDDEN;
@@ -286,13 +328,19 @@ function runLayoutPipelineMeasured(deps, state, options) {
286
328
  });
287
329
  const blockWidths = blockMeasureInputs.widths;
288
330
  const previousArtifacts = session.artifacts;
289
- const incrementalResult = options.dirtyRange && !options.forceFull && previousArtifacts ? tryBuildIncrementalMeasures({
331
+ const dirtyRange = options.measure === LAYOUT_MEASURE.incremental ? incrementalDirtyRange({
332
+ session,
333
+ state,
334
+ preview: pendingTemplatePreview,
335
+ measureInputs
336
+ }) : null;
337
+ const incrementalResult = dirtyRange && previousArtifacts ? tryBuildIncrementalMeasures({
290
338
  previousBlocks: previousArtifacts.blocks,
291
339
  previousMeasures: previousArtifacts.measures,
292
340
  previousBlockWidths: previousArtifacts.blockWidths,
293
341
  nextBlocks: newBlocks,
294
342
  nextBlockWidths: blockWidths,
295
- dirtyRange: options.dirtyRange,
343
+ dirtyRange,
296
344
  measureBlock: measureSingleBlockWithoutFloatingZones
297
345
  }) : null;
298
346
  let newMeasures = incrementalResult?.measures ?? measureBlocks(newBlocks, blockWidths, blockMeasureInputs.marginTops, {
@@ -583,11 +631,16 @@ function runLayoutPipelineMeasured(deps, state, options) {
583
631
  renderPages(newLayout.pages, pagesContainer, renderOpts);
584
632
  recordPhaseDuration("render-pages", phaseStartedAt);
585
633
  }
634
+ const measuredFontSet = readFontSetSignature();
635
+ labelMeasureCachesFontSet(measuredFontSet);
586
636
  session.artifacts = pendingArtifacts;
587
637
  session.lastTemplatePreview = pendingTemplatePreview;
588
638
  session.lastEditorState = state;
589
639
  session.lastPmDoc = state.doc;
590
- session.usedLoadedFonts = documentFontsAreLoaded();
640
+ session.lastMeasureInputs = {
641
+ ...measureInputs,
642
+ fontSet: measuredFontSet
643
+ };
591
644
  recordLayoutComplete(reason);
592
645
  } catch (error) {
593
646
  const invalidHighlights = describeInvalidHighlightMarks(state.doc);
@@ -0,0 +1,24 @@
1
+ import { LayoutRunReason } from "../layout-engine/layoutInstrumentation.js";
2
+ //#region src/controller/layoutRunOptions.d.ts
3
+ /**
4
+ * How a layout pass measures its blocks.
5
+ *
6
+ * - `full` measures every block.
7
+ * - `incremental` may keep the committed layout's measures for the blocks the
8
+ * current document shares with the document that layout was computed for.
9
+ * The pass derives what changed from those two documents itself; a caller
10
+ * never supplies a changed range, so the range cannot be relative to a
11
+ * document the committed measures did not come from.
12
+ */
13
+ declare const LAYOUT_MEASURE: {
14
+ readonly full: "full";
15
+ readonly incremental: "incremental";
16
+ };
17
+ type LayoutMeasure = (typeof LAYOUT_MEASURE)[keyof typeof LAYOUT_MEASURE];
18
+ type LayoutRunOptions = {
19
+ /** Defaults to {@link LAYOUT_MEASURE.full}. */
20
+ measure?: LayoutMeasure;
21
+ reason?: LayoutRunReason;
22
+ };
23
+ //#endregion
24
+ export { LAYOUT_MEASURE, LayoutMeasure, LayoutRunOptions };
@@ -0,0 +1,17 @@
1
+ //#region src/controller/layoutRunOptions.ts
2
+ /**
3
+ * How a layout pass measures its blocks.
4
+ *
5
+ * - `full` measures every block.
6
+ * - `incremental` may keep the committed layout's measures for the blocks the
7
+ * current document shares with the document that layout was computed for.
8
+ * The pass derives what changed from those two documents itself; a caller
9
+ * never supplies a changed range, so the range cannot be relative to a
10
+ * document the committed measures did not come from.
11
+ */
12
+ const LAYOUT_MEASURE = {
13
+ full: "full",
14
+ incremental: "incremental"
15
+ };
16
+ //#endregion
17
+ export { LAYOUT_MEASURE };
@@ -1,11 +1,5 @@
1
- import { LayoutRunReason } from "../layout-engine/layoutInstrumentation.js";
2
- import { DirtyRange } from "../paged-layout/incrementalMeasure.js";
1
+ import { LayoutRunOptions } from "./layoutRunOptions.js";
3
2
  //#region src/controller/layoutScheduler.d.ts
4
- type LayoutRunOptions = {
5
- dirtyRange?: DirtyRange;
6
- forceFull?: boolean;
7
- reason: LayoutRunReason;
8
- };
9
3
  type RunLayout<TState> = (state: TState, options: LayoutRunOptions) => void;
10
4
  type SchedulerClock = {
11
5
  requestFrame: (callback: () => void) => number;
@@ -16,6 +10,11 @@ type SchedulerClock = {
16
10
  };
17
11
  declare const browserClock: SchedulerClock;
18
12
  type LayoutSchedulerConfig<TState> = {
13
+ /**
14
+ * The editor's current state, read when a pass runs; `null` (no editor)
15
+ * skips the pass.
16
+ */
17
+ readState: () => TState | null;
19
18
  runLayout: RunLayout<TState>;
20
19
  /** Quiet-window debounce before an interactive layout pass. */
21
20
  debounceMs: number;
@@ -30,16 +29,22 @@ type LayoutSchedulerConfig<TState> = {
30
29
  */
31
30
  clock: SchedulerClock;
32
31
  };
33
- type LayoutScheduler<TState> = {
32
+ /** Interactive transaction layout timing shared by framework adapters. */
33
+ declare const TRANSACTION_LAYOUT_TIMING: {
34
+ readonly debounceMs: 32;
35
+ readonly maxDelayMs: 96;
36
+ readonly leadingFrame: true;
37
+ };
38
+ type LayoutScheduler = {
34
39
  /**
35
- * Schedule a layout pass after a short coalescing window. Repeated calls in
36
- * the window replace the pending state and merge dirty ranges, so a typing
37
- * burst paints once while still honoring the max-latency cap.
40
+ * Request an incremental layout pass after a short coalescing window.
41
+ * Repeated calls in the window join the pending request, so a typing burst
42
+ * paints once while still honoring the max-latency cap.
38
43
  */
39
- schedule: (state: TState, dirtyRange: DirtyRange | null) => void;
44
+ schedule: () => void;
40
45
  /** Cancel any pending timer/frame. Call on teardown. */
41
46
  dispose: () => void;
42
47
  };
43
- declare const createLayoutScheduler: <TState>(config: LayoutSchedulerConfig<TState>) => LayoutScheduler<TState>;
48
+ declare const createLayoutScheduler: <TState>(config: LayoutSchedulerConfig<TState>) => LayoutScheduler;
44
49
  //#endregion
45
- export { LayoutRunOptions, LayoutScheduler, LayoutSchedulerConfig, RunLayout, SchedulerClock, browserClock, createLayoutScheduler };
50
+ export { LayoutScheduler, LayoutSchedulerConfig, RunLayout, SchedulerClock, TRANSACTION_LAYOUT_TIMING, browserClock, createLayoutScheduler };
@@ -1,4 +1,4 @@
1
- import { mergeDirtyRanges } from "../paged-layout/incrementalMeasure.js";
1
+ import { LAYOUT_MEASURE } from "./layoutRunOptions.js";
2
2
  //#region src/controller/layoutScheduler.ts
3
3
  const browserClock = {
4
4
  requestFrame: (callback) => window.requestAnimationFrame(callback),
@@ -7,6 +7,12 @@ const browserClock = {
7
7
  clearTimer: (id) => window.clearTimeout(id),
8
8
  now: () => window.performance.now()
9
9
  };
10
+ /** Interactive transaction layout timing shared by framework adapters. */
11
+ const TRANSACTION_LAYOUT_TIMING = {
12
+ debounceMs: 32,
13
+ maxDelayMs: 96,
14
+ leadingFrame: true
15
+ };
10
16
  const createLayoutScheduler = (config) => {
11
17
  const clock = config.clock;
12
18
  let lastRunAt = null;
@@ -15,13 +21,14 @@ const createLayoutScheduler = (config) => {
15
21
  if (!pending || pending.rafId !== null) return;
16
22
  pending.timerId = null;
17
23
  pending.rafId = clock.requestFrame(() => {
18
- const latest = pending;
19
24
  pending = null;
20
- if (!latest) return;
21
25
  lastRunAt = clock.now();
22
- const options = { reason: "transaction" };
23
- if (latest.dirtyRange) options.dirtyRange = latest.dirtyRange;
24
- config.runLayout(latest.state, options);
26
+ const state = config.readState();
27
+ if (state === null) return;
28
+ config.runLayout(state, {
29
+ reason: "transaction",
30
+ measure: LAYOUT_MEASURE.incremental
31
+ });
25
32
  });
26
33
  };
27
34
  const armTimer = (request) => {
@@ -32,18 +39,14 @@ const createLayoutScheduler = (config) => {
32
39
  request.timerId = clock.setTimer(flushPending, delayMs);
33
40
  };
34
41
  return {
35
- schedule(state, dirtyRange) {
42
+ schedule() {
36
43
  if (pending) {
37
- pending.state = state;
38
- pending.dirtyRange = mergeDirtyRanges(pending.dirtyRange, dirtyRange);
39
44
  armTimer(pending);
40
45
  return;
41
46
  }
42
47
  const next = {
43
- dirtyRange,
44
48
  firstScheduledAt: clock.now(),
45
49
  rafId: null,
46
- state,
47
50
  timerId: null
48
51
  };
49
52
  pending = next;
@@ -62,4 +65,4 @@ const createLayoutScheduler = (config) => {
62
65
  };
63
66
  };
64
67
  //#endregion
65
- export { browserClock, createLayoutScheduler };
68
+ export { TRANSACTION_LAYOUT_TIMING, browserClock, createLayoutScheduler };
@@ -1,5 +1,8 @@
1
+ import { document_d_exports } from "../types/document.js";
1
2
  import { TemplatePreviewEntry, TemplatePreviewHiddenRange, TemplatePreviewValues } from "../prosemirror/plugins/templatePreviewValues.js";
2
3
  import { FlowBlock, Measure } from "../layout-engine/types.js";
4
+ import { DisplayMode } from "../managers/EditorModeManager.js";
5
+ import { FontAlternates } from "../fonts/fontAlternates.js";
3
6
  import { EditorState } from "prosemirror-state";
4
7
  //#region src/controller/layoutSession.d.ts
5
8
  type LayoutArtifacts = {
@@ -12,13 +15,34 @@ type LayoutTemplatePreview = {
12
15
  hidden: readonly TemplatePreviewHiddenRange[];
13
16
  mode: TemplatePreviewValues["mode"];
14
17
  };
18
+ /**
19
+ * The inputs besides the ProseMirror document and block widths that a block's
20
+ * measure depends on. An incremental pass keeps committed measures only when
21
+ * these are the ones they were computed with. The `Document` model itself is
22
+ * not among them: adapters hand back a new one after every edit, while a load
23
+ * that changes how text measures brings new styles or font alternates.
24
+ */
25
+ type LayoutMeasureInputs = {
26
+ styles: document_d_exports.StyleDefinitions | null | undefined;
27
+ theme: document_d_exports.Theme | null | undefined;
28
+ defaultTabStop: number | undefined;
29
+ pageContentHeight: number;
30
+ /**
31
+ * The font set (`readFontSetSignature`) the measures were taken in. A face
32
+ * that loads afterwards changes it, so fallback measures are never reused.
33
+ */
34
+ fontSet: string;
35
+ fontAlternates: FontAlternates;
36
+ /** The review view: each lays out different text, so a measure is reusable only within one. */
37
+ markupView: DisplayMode;
38
+ };
15
39
  type LayoutSession = {
16
40
  artifacts: LayoutArtifacts | null;
17
41
  lastEditorState: EditorState | null;
18
42
  lastPmDoc: EditorState["doc"] | null;
19
- usedLoadedFonts: boolean;
43
+ lastMeasureInputs: LayoutMeasureInputs | null;
20
44
  lastTemplatePreview: LayoutTemplatePreview;
21
45
  };
22
46
  declare const createLayoutSession: () => LayoutSession;
23
47
  //#endregion
24
- export { LayoutArtifacts, LayoutSession, LayoutTemplatePreview, createLayoutSession };
48
+ export { LayoutArtifacts, LayoutMeasureInputs, LayoutSession, LayoutTemplatePreview, createLayoutSession };
@@ -3,7 +3,7 @@ const createLayoutSession = () => ({
3
3
  artifacts: null,
4
4
  lastEditorState: null,
5
5
  lastPmDoc: null,
6
- usedLoadedFonts: false,
6
+ lastMeasureInputs: null,
7
7
  lastTemplatePreview: {
8
8
  entries: [],
9
9
  hidden: [],
@@ -8,6 +8,7 @@ import { ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdA
8
8
  import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNumbering.js";
9
9
  import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
10
10
  import { schema } from "../prosemirror/schema/index.js";
11
+ import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
11
12
  import { EditorState } from "prosemirror-state";
12
13
  import { EditorView } from "prosemirror-view";
13
14
  //#region src/controller/noteEditorManager.ts
@@ -163,8 +164,11 @@ const createNoteEditorManager = (deps) => {
163
164
  let endnotes = document.package.endnotes;
164
165
  let footnotesChanged = false;
165
166
  let endnotesChanged = false;
167
+ let numbering = document.package.numbering;
166
168
  for (const story of mounted.values()) {
167
169
  if (!story.dirty) continue;
170
+ const lists = storyListNumbering(story.view.state, numbering);
171
+ numbering = lists.numbering;
168
172
  if (story.note.kind === "footnote") {
169
173
  const index = footnotes?.findIndex(({ id }) => id === story.note.noteId) ?? -1;
170
174
  const current = index === -1 ? null : footnotes?.at(index);
@@ -175,7 +179,7 @@ const createNoteEditorManager = (deps) => {
175
179
  }
176
180
  const updated = {
177
181
  ...current,
178
- content: proseDocToBlocks(story.view.state.doc, current.content, document.package.styles)
182
+ content: proseDocToBlocks(lists.doc, current.content, document.package.styles)
179
183
  };
180
184
  footnotes[index] = updated;
181
185
  story.appliedNote = updated;
@@ -192,7 +196,7 @@ const createNoteEditorManager = (deps) => {
192
196
  }
193
197
  const updated = {
194
198
  ...current,
195
- content: proseDocToBlocks(story.view.state.doc, current.content, document.package.styles)
199
+ content: proseDocToBlocks(lists.doc, current.content, document.package.styles)
196
200
  };
197
201
  endnotes[index] = updated;
198
202
  story.appliedNote = updated;
@@ -205,7 +209,8 @@ const createNoteEditorManager = (deps) => {
205
209
  package: {
206
210
  ...document.package,
207
211
  ...footnotesChanged && footnotes ? { footnotes } : {},
208
- ...endnotesChanged && endnotes ? { endnotes } : {}
212
+ ...endnotesChanged && endnotes ? { endnotes } : {},
213
+ ...numbering ? { numbering } : {}
209
214
  }
210
215
  };
211
216
  },