@stll/folio-core 0.10.0 → 0.12.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 (217) hide show
  1. package/dist/ai-edits/apply.js +602 -50
  2. package/dist/ai-edits/block-identity.d.ts +4 -0
  3. package/dist/ai-edits/block-identity.js +9 -0
  4. package/dist/ai-edits/blockRange.d.ts +28 -1
  5. package/dist/ai-edits/blockRange.js +67 -1
  6. package/dist/ai-edits/headless.d.ts +4 -4
  7. package/dist/ai-edits/headless.js +9 -9
  8. package/dist/ai-edits/index.d.ts +2 -2
  9. package/dist/ai-edits/index.js +2 -2
  10. package/dist/ai-edits/read.d.ts +7 -7
  11. package/dist/ai-edits/read.js +110 -3
  12. package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
  13. package/dist/ai-edits/table-cell-mutations.js +237 -0
  14. package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
  15. package/dist/ai-edits/table-mutation-plan.js +90 -0
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +100 -0
  17. package/dist/ai-edits/table-row-column-mutations.js +300 -0
  18. package/dist/ai-edits/table-targets.d.ts +32 -0
  19. package/dist/ai-edits/table-targets.js +70 -0
  20. package/dist/ai-edits/types.d.ts +34 -0
  21. package/dist/compat/eigenpal.js +1 -1
  22. package/dist/content-controls/mutateContentControls.js +1 -1
  23. package/dist/controller/activeEditorStory.d.ts +36 -0
  24. package/dist/controller/activeEditorStory.js +30 -0
  25. package/dist/controller/collaborationModules.d.ts +7 -0
  26. package/dist/controller/collaborationModules.js +15 -0
  27. package/dist/controller/contentControlWidgetController.d.ts +55 -0
  28. package/dist/controller/contentControlWidgetController.js +141 -0
  29. package/dist/controller/folioEditor.d.ts +1 -1
  30. package/dist/controller/fontReadiness.js +2 -0
  31. package/dist/controller/headerFooterEditorManager.d.ts +41 -0
  32. package/dist/controller/headerFooterEditorManager.js +182 -0
  33. package/dist/controller/hiddenEditorApi.js +2 -2
  34. package/dist/controller/hiddenEditorManager.d.ts +13 -2
  35. package/dist/controller/hiddenEditorManager.js +32 -11
  36. package/dist/controller/layoutPipeline.d.ts +2 -0
  37. package/dist/controller/layoutPipeline.js +16 -1
  38. package/dist/controller/noteEditorManager.d.ts +36 -0
  39. package/dist/controller/noteEditorManager.js +210 -0
  40. package/dist/document-operations.d.ts +35 -3
  41. package/dist/document-operations.js +161 -3
  42. package/dist/docx/blockContentParser.js +2 -2
  43. package/dist/docx/commentParser.js +6 -1
  44. package/dist/docx/conformance.js +8 -1
  45. package/dist/docx/documentGrid.d.ts +8 -0
  46. package/dist/docx/documentGrid.js +8 -0
  47. package/dist/docx/drawingUtils.js +1 -1
  48. package/dist/docx/ensureParaIds.js +1 -1
  49. package/dist/docx/fieldParser.js +1 -1
  50. package/dist/docx/notePropertiesParser.js +1 -1
  51. package/dist/docx/numberingParser.js +1 -1
  52. package/dist/docx/paragraphParser.js +63 -58
  53. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  54. package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
  55. package/dist/docx/paragraphTraversal.js +1 -1
  56. package/dist/docx/parser.js +3 -3
  57. package/dist/docx/rezip.js +1 -1
  58. package/dist/docx/runParser.js +4 -2
  59. package/dist/docx/sdtProperties.js +1 -1
  60. package/dist/docx/sectionParser.js +1 -1
  61. package/dist/docx/selectiveSave.js +1 -1
  62. package/dist/docx/serializer/commentSerializer.js +1 -2
  63. package/dist/docx/serializer/documentSerializer.js +2 -2
  64. package/dist/docx/serializer/headerFooterSerializer.js +2 -2
  65. package/dist/docx/serializer/noteSerializer.js +2 -2
  66. package/dist/docx/serializer/paragraphSerializer.js +7 -1
  67. package/dist/docx/serializer/runSerializer.js +4 -2
  68. package/dist/docx/serializer/stylesSerializer.js +1 -1
  69. package/dist/docx/serializer/tableSerializer.d.ts +5 -4
  70. package/dist/docx/serializer/tableSerializer.js +17 -13
  71. package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
  72. package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
  73. package/dist/docx/server/boundedArchive.d.ts +14 -3
  74. package/dist/docx/server/boundedArchive.js +56 -11
  75. package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
  76. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
  77. package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
  78. package/dist/docx/server/inspectDocxPackage.js +217 -0
  79. package/dist/docx/server/validateDocxConformance.d.ts +41 -0
  80. package/dist/docx/server/validateDocxConformance.js +336 -0
  81. package/dist/docx/settingsParser.js +11 -1
  82. package/dist/docx/shapeParser.js +1 -1
  83. package/dist/docx/styleParser.js +4 -2
  84. package/dist/docx/tableParser.js +18 -7
  85. package/dist/docx/textBoxParser.d.ts +3 -3
  86. package/dist/docx/textBoxParser.js +16 -12
  87. package/dist/docx/xmlSafety.d.ts +5 -0
  88. package/dist/docx/xmlSafety.js +9 -0
  89. package/dist/fields/resolveFieldValues.js +1 -3
  90. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  91. package/dist/i18n/messages/catalogs.gen.js +17 -0
  92. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  93. package/dist/index.js +1 -1
  94. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  95. package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
  96. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  97. package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
  98. package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
  99. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  100. package/dist/layout-bridge/convert/toFlowBlocks.js +136 -20
  101. package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
  102. package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
  103. package/dist/layout-bridge/engine/clickToPosition.js +2 -2
  104. package/dist/layout-engine/index.d.ts +2 -2
  105. package/dist/layout-engine/index.js +43 -28
  106. package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
  107. package/dist/layout-engine/justificationCompatibility.js +8 -0
  108. package/dist/layout-engine/keep-together.js +11 -1
  109. package/dist/layout-engine/measure/cache.js +21 -5
  110. package/dist/layout-engine/measure/lineBreakProvider.js +53 -34
  111. package/dist/layout-engine/measure/measureBlocks.js +6 -3
  112. package/dist/layout-engine/measure/measureContainer.js +1 -1
  113. package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
  114. package/dist/layout-engine/measure/measureHelpers.js +3 -3
  115. package/dist/layout-engine/measure/measureParagraph.js +69 -25
  116. package/dist/layout-engine/measure/measureProvider.js +4 -3
  117. package/dist/layout-engine/measure/tableCellFlow.js +5 -2
  118. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
  119. package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
  120. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
  121. package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
  122. package/dist/layout-engine/paginator.js +5 -1
  123. package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
  124. package/dist/layout-engine/paragraphSpacing.js +107 -13
  125. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  126. package/dist/layout-engine/types.d.ts +26 -13
  127. package/dist/layout-painter/registry/modules/textBox.js +5 -1
  128. package/dist/layout-painter/renderPage.d.ts +5 -2
  129. package/dist/layout-painter/renderPage.js +56 -12
  130. package/dist/layout-painter/renderParagraph.js +22 -6
  131. package/dist/layout-painter/renderTable.d.ts +5 -1
  132. package/dist/layout-painter/renderTable.js +5 -2
  133. package/dist/layout-painter/renderTextBox.d.ts +2 -1
  134. package/dist/layout-painter/renderTextBox.js +25 -14
  135. package/dist/managers/DocumentLoaderManager.js +1 -1
  136. package/dist/managers/FindReplaceManager.d.ts +8 -0
  137. package/dist/managers/FindReplaceManager.js +18 -0
  138. package/dist/managers/TableSelectionManager.d.ts +6 -0
  139. package/dist/managers/TableSelectionManager.js +14 -0
  140. package/dist/model.js +1 -1
  141. package/dist/prosemirror/attrs/index.d.ts +4 -2
  142. package/dist/prosemirror/attrs/index.js +154 -18
  143. package/dist/prosemirror/commands/comments.d.ts +3 -3
  144. package/dist/prosemirror/commands/comments.js +248 -12
  145. package/dist/prosemirror/commands/contentControls.js +1 -1
  146. package/dist/prosemirror/commands/index.js +1 -1
  147. package/dist/prosemirror/commands/pageBreak.js +1 -1
  148. package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
  149. package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
  150. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
  151. package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
  152. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
  153. package/dist/prosemirror/conversion/fromProseDoc.js +286 -61
  154. package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
  155. package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
  156. package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
  157. package/dist/prosemirror/conversion/toProseDoc.js +347 -98
  158. package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
  159. package/dist/prosemirror/extensions/StarterKit.js +6 -1
  160. package/dist/prosemirror/extensions/core/ParagraphExtension.js +4 -1
  161. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
  162. package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
  163. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
  164. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
  165. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
  166. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
  167. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
  168. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
  169. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +24 -1
  170. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  171. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
  172. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
  173. package/dist/prosemirror/extensions/nodes/TableExtension.js +9 -3
  174. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
  175. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
  176. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
  177. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
  178. package/dist/prosemirror/findReplaceSelection.d.ts +6 -1
  179. package/dist/prosemirror/findReplaceSelection.js +57 -13
  180. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +34 -8
  181. package/dist/prosemirror/plugins/contentControlWidgets.js +125 -91
  182. package/dist/prosemirror/plugins/selectionTracker.js +1 -0
  183. package/dist/prosemirror/plugins/suggestionMode.js +1 -1
  184. package/dist/prosemirror/schema/index.d.ts +2 -2
  185. package/dist/prosemirror/schema/marks.d.ts +5 -1
  186. package/dist/prosemirror/schema/nodes.d.ts +64 -7
  187. package/dist/prosemirror/selectionState.js +1 -0
  188. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  189. package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
  190. package/dist/prosemirror/tableCellMergeRevision.js +22 -0
  191. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
  192. package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
  193. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
  194. package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
  195. package/dist/prosemirror/validation.js +9 -1
  196. package/dist/render-dom/BodySelectionOverlay.d.ts +31 -0
  197. package/dist/render-dom/BodySelectionOverlay.js +76 -0
  198. package/dist/render-dom/HeaderFooterSelectionOverlay.d.ts +28 -0
  199. package/dist/render-dom/HeaderFooterSelectionOverlay.js +121 -0
  200. package/dist/render-dom/RemoteSelectionOverlay.d.ts +30 -0
  201. package/dist/render-dom/RemoteSelectionOverlay.js +67 -0
  202. package/dist/render-dom/RenderedDomContext.d.ts +40 -0
  203. package/dist/render-dom/RenderedDomContext.js +126 -0
  204. package/dist/render-dom/resolveSidebarItemPositions.d.ts +33 -0
  205. package/dist/render-dom/resolveSidebarItemPositions.js +45 -0
  206. package/dist/server.d.ts +6 -2
  207. package/dist/server.js +5 -1
  208. package/dist/utils/findReplace.d.ts +32 -0
  209. package/dist/utils/findReplace.js +118 -0
  210. package/dist/utils/fontFamilyMerge.js +9 -11
  211. package/dist/utils/fontLoader.js +3 -2
  212. package/dist/utils/fontResolver.js +22 -4
  213. package/dist/utils/fontWeights.d.ts +1 -1
  214. package/dist/utils/fontWeights.js +1 -1
  215. package/dist/utils/headerFooter.js +24 -1
  216. package/dist/utils/paragraphFormattingMerge.js +1 -0
  217. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import { expectRunPropertyChangeMarkAttrs } from "../attrs/index.js";
2
+ import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
1
3
  //#region src/prosemirror/utils/extractTrackedChanges.ts
2
4
  const EMPTY_RESULT = {
3
5
  entries: [],
@@ -23,7 +25,8 @@ function extractTrackedChanges(state) {
23
25
  const insertionType = schema.marks["insertion"];
24
26
  const deletionType = schema.marks["deletion"];
25
27
  const commentType = schema.marks["comment"];
26
- if (!insertionType && !deletionType) return EMPTY_RESULT;
28
+ const runPropertyChangeType = schema.marks["runPropertyChange"];
29
+ if (!insertionType && !deletionType && !runPropertyChangeType) return EMPTY_RESULT;
27
30
  const raw = [];
28
31
  const commentToRevision = /* @__PURE__ */ new Map();
29
32
  const commentMetadata = /* @__PURE__ */ new Map();
@@ -116,6 +119,20 @@ function extractTrackedChanges(state) {
116
119
  revisionId: cellMarker.info.revisionId
117
120
  });
118
121
  }
122
+ const continuationCells = node.attrs["_docxVMergeContinuationCells"];
123
+ if (Array.isArray(continuationCells)) for (const continuationCell of continuationCells) {
124
+ const change = getTableCellMergeChange(continuationCell);
125
+ if (!change) continue;
126
+ raw.push({
127
+ type: "cellMerged",
128
+ text: node.textContent || "",
129
+ author: change.info.author,
130
+ date: change.info.date,
131
+ from: pos,
132
+ to: pos + node.nodeSize,
133
+ revisionId: change.info.id
134
+ });
135
+ }
119
136
  const tcPrChange = node.attrs["tcPrChange"];
120
137
  if (Array.isArray(tcPrChange)) for (const entry of tcPrChange) {
121
138
  if (!entry?.info || typeof entry.info.id !== "number") continue;
@@ -193,17 +210,31 @@ function extractTrackedChanges(state) {
193
210
  else if (node.type.name === "image") inlineText = node.attrs["alt"] || "image";
194
211
  else inlineText = node.type.name;
195
212
  let tcMark = null;
196
- for (const mark of node.marks) if (mark.type === insertionType || mark.type === deletionType) {
197
- raw.push({
198
- type: mark.type === insertionType ? "insertion" : "deletion",
199
- text: inlineText,
200
- author: mark.attrs["author"] || "",
201
- date: mark.attrs["date"],
202
- from: pos,
203
- to: pos + node.nodeSize,
204
- revisionId: mark.attrs["revisionId"]
205
- });
206
- tcMark = mark;
213
+ for (const mark of node.marks) {
214
+ if (mark.type === runPropertyChangeType) {
215
+ for (const change of expectRunPropertyChangeMarkAttrs(mark).changes) raw.push({
216
+ type: "runPropertiesChanged",
217
+ text: inlineText,
218
+ author: change.info.author,
219
+ date: change.info.date,
220
+ from: pos,
221
+ to: pos + node.nodeSize,
222
+ revisionId: change.info.id
223
+ });
224
+ continue;
225
+ }
226
+ if (mark.type === insertionType || mark.type === deletionType) {
227
+ raw.push({
228
+ type: mark.type === insertionType ? "insertion" : "deletion",
229
+ text: inlineText,
230
+ author: mark.attrs["author"] || "",
231
+ date: mark.attrs["date"],
232
+ from: pos,
233
+ to: pos + node.nodeSize,
234
+ revisionId: mark.attrs["revisionId"]
235
+ });
236
+ tcMark = mark;
237
+ }
207
238
  }
208
239
  if (commentType) {
209
240
  const commentMark = node.marks.find((m) => m.type === commentType);
@@ -239,7 +270,24 @@ function extractTrackedChanges(state) {
239
270
  cellPropertiesChanged: 3,
240
271
  paragraphMarkInsertion: 2,
241
272
  paragraphMarkDeletion: 2,
242
- paragraphPropertiesChanged: 2
273
+ paragraphPropertiesChanged: 2,
274
+ runPropertiesChanged: 1
275
+ };
276
+ const STRUCTURAL_FAMILY_BY_TYPE = {
277
+ tableInserted: "insertion",
278
+ rowInserted: "insertion",
279
+ cellInserted: "insertion",
280
+ paragraphMarkInsertion: "insertion",
281
+ tableDeleted: "deletion",
282
+ rowDeleted: "deletion",
283
+ cellDeleted: "deletion",
284
+ paragraphMarkDeletion: "deletion",
285
+ tablePropertiesChanged: "properties",
286
+ rowPropertiesChanged: "properties",
287
+ cellPropertiesChanged: "properties",
288
+ paragraphPropertiesChanged: "properties",
289
+ runPropertiesChanged: "properties",
290
+ cellMerged: "merge"
243
291
  };
244
292
  const isStructuralType = (t) => t in STRUCTURAL_PRIORITY;
245
293
  const slotByKey = /* @__PURE__ */ new Map();
@@ -256,7 +304,7 @@ function extractTrackedChanges(state) {
256
304
  ordered.push(entry);
257
305
  continue;
258
306
  }
259
- const key = `${entry.author}|${entry.date ?? ""}`;
307
+ const key = `${STRUCTURAL_FAMILY_BY_TYPE[entry.type] ?? entry.type}|${entry.author}|${entry.date ?? ""}`;
260
308
  const slot = slotByKey.get(key);
261
309
  if (slot === void 0) {
262
310
  slotByKey.set(key, ordered.push(entry) - 1);
@@ -1,4 +1,5 @@
1
- import { readBlockSdtAttrs, readCharacterSpacingMarkAttrs, readCharacterStyleMarkAttrs, readCommentMarkAttrs, readEmphasisMarkAttrs, readFieldAttrs, readFontFamilyMarkAttrs, readFontSizeMarkAttrs, readFootnoteRefMarkAttrs, readHardBreakAttrs, readHighlightMarkAttrs, readHyperlinkMarkAttrs, readImageAttrs, readLanguageMarkAttrs, readMathAttrs, readParagraphAttrs, readRunFormattingOverrideMarkAttrs, readRunShadingMarkAttrs, readSdtAttrs, readShapeAttrs, readStrikeMarkAttrs, readTableAttrs, readTableCellAttrs, readTableRowAttrs, readTextBoxAttrs, readTextColorMarkAttrs, readTextEffectMarkAttrs, readTrackedChangeMarkAttrs, readUnderlineMarkAttrs } from "./attrs/index.js";
1
+ import { readBlockSdtAttrs, readCharacterSpacingMarkAttrs, readCharacterStyleMarkAttrs, readCommentMarkAttrs, readEmphasisMarkAttrs, readFieldAttrs, readFontFamilyMarkAttrs, readFontSizeMarkAttrs, readFootnoteRefMarkAttrs, readHardBreakAttrs, readHighlightMarkAttrs, readHyperlinkMarkAttrs, readImageAttrs, readLanguageMarkAttrs, readMathAttrs, readParagraphAttrs, readRunFormattingOverrideMarkAttrs, readRunPropertyChangeMarkAttrs, readRunShadingMarkAttrs, readSdtAttrs, readShapeAttrs, readStrikeMarkAttrs, readTableAttrs, readTableCellAttrs, readTableRowAttrs, readTextBoxAttrs, readTextColorMarkAttrs, readTextEffectMarkAttrs, readTrackedChangeMarkAttrs, readUnderlineMarkAttrs } from "./attrs/index.js";
2
+ import { readTextBoxAnchorAttrs } from "./textBoxAnchorAttrs.js";
2
3
  //#region src/prosemirror/validation.ts
3
4
  var ProseMirrorDocumentValidationError = class extends Error {
4
5
  issues;
@@ -48,6 +49,7 @@ const validateNodeAttrs = (node, path, issues) => {
48
49
  case "text":
49
50
  case "horizontalRule":
50
51
  case "pageBreak":
52
+ case "renderedPageBreak":
51
53
  case "tab": return;
52
54
  case "hardBreak":
53
55
  appendAttrIssues(path, readHardBreakAttrs(node), issues);
@@ -87,6 +89,9 @@ const validateNodeAttrs = (node, path, issues) => {
87
89
  case "textBox":
88
90
  appendAttrIssues(path, readTextBoxAttrs(node), issues);
89
91
  return;
92
+ case "textBoxAnchor":
93
+ appendAttrIssues(path, readTextBoxAnchorAttrs(node), issues);
94
+ return;
90
95
  default: issues.push({
91
96
  path: `${path}.type.name`,
92
97
  message: `Unsupported ProseMirror node type ${node.type.name}.`
@@ -155,6 +160,9 @@ const validateMarks = (marks, path, issues) => {
155
160
  case "deletion":
156
161
  appendAttrIssues(markPath, readTrackedChangeMarkAttrs(mark), issues);
157
162
  continue;
163
+ case "runPropertyChange":
164
+ appendAttrIssues(markPath, readRunPropertyChangeMarkAttrs(mark), issues);
165
+ continue;
158
166
  case "runFormattingOverride":
159
167
  appendAttrIssues(markPath, readRunFormattingOverrideMarkAttrs(mark), issues);
160
168
  continue;
@@ -0,0 +1,31 @@
1
+ import { EditorState } from "prosemirror-state";
2
+
3
+ //#region src/render-dom/BodySelectionOverlay.d.ts
4
+ type BodySelectionOverlayResult = {
5
+ type: "text";
6
+ } | {
7
+ type: "image";
8
+ element: HTMLElement;
9
+ pmPos: number;
10
+ };
11
+ type SyncBodySelectionOverlayOptions = {
12
+ pagesContainer: HTMLElement;
13
+ state: EditorState;
14
+ zoom: number;
15
+ zIndex?: number;
16
+ caretColor?: string;
17
+ selectionColor?: string;
18
+ };
19
+ declare class BodySelectionOverlay {
20
+ clear(pagesContainer: HTMLElement): void;
21
+ sync({
22
+ pagesContainer,
23
+ state,
24
+ zoom,
25
+ zIndex,
26
+ caretColor,
27
+ selectionColor
28
+ }: SyncBodySelectionOverlayOptions): BodySelectionOverlayResult;
29
+ }
30
+ //#endregion
31
+ export { BodySelectionOverlay, BodySelectionOverlayResult, SyncBodySelectionOverlayOptions };
@@ -0,0 +1,76 @@
1
+ import { findBodyPmAnchor } from "../layout-bridge/dom/findBodyPmSpans.js";
2
+ import { applyCellSelectionHighlight } from "../layout-bridge/cellSelectionHighlight.js";
3
+ import { findImageElement } from "../layout-painter/imageLayout.js";
4
+ import { getCaretPositionFromDom, getSelectionRectsFromDom } from "../layout-bridge/dom/clickToPositionDom.js";
5
+ import { NodeSelection } from "prosemirror-state";
6
+ //#region src/render-dom/BodySelectionOverlay.ts
7
+ /**
8
+ * Imperative body-selection painter shared by framework adapters. The editable
9
+ * ProseMirror view is hidden, so its caret, range selection, and table-cell
10
+ * selection must be projected onto the paginated DOM.
11
+ */
12
+ const CARET_CLASS = "folio-body-selection-caret";
13
+ const RANGE_CLASS = "folio-body-selection-rect";
14
+ const OWNED_OVERLAY_SELECTOR = `.${CARET_CLASS}, .${RANGE_CLASS}`;
15
+ const setOverlayBox = (element, rect, pagesContainer, zoom) => {
16
+ const divisor = zoom > 0 ? zoom : 1;
17
+ element.style.left = `${rect.x / divisor + pagesContainer.scrollLeft}px`;
18
+ element.style.top = `${rect.y / divisor + pagesContainer.scrollTop}px`;
19
+ element.style.width = `${rect.width / divisor}px`;
20
+ element.style.height = `${rect.height / divisor}px`;
21
+ };
22
+ var BodySelectionOverlay = class {
23
+ clear(pagesContainer) {
24
+ for (const element of pagesContainer.querySelectorAll(OWNED_OVERLAY_SELECTOR)) element.remove();
25
+ }
26
+ sync({ pagesContainer, state, zoom, zIndex = 10, caretColor = "var(--doc-caret, #000)", selectionColor = "var(--doc-selection, rgba(66, 133, 244, 0.3))" }) {
27
+ this.clear(pagesContainer);
28
+ applyCellSelectionHighlight(pagesContainer, state);
29
+ const { selection } = state;
30
+ if (selection instanceof NodeSelection && selection.node.type.name === "image") {
31
+ const anchor = findBodyPmAnchor(pagesContainer, selection.from);
32
+ const element = anchor ? findImageElement(anchor) : null;
33
+ if (element) return {
34
+ type: "image",
35
+ element,
36
+ pmPos: selection.from
37
+ };
38
+ }
39
+ if (selection.empty) {
40
+ const caret = getCaretPositionFromDom(pagesContainer, selection.from, pagesContainer.getBoundingClientRect());
41
+ if (!caret) return { type: "text" };
42
+ const element = pagesContainer.ownerDocument.createElement("div");
43
+ element.className = CARET_CLASS;
44
+ element.dataset["folioCaretRect"] = "";
45
+ element.style.position = "absolute";
46
+ element.style.background = caretColor;
47
+ element.style.pointerEvents = "none";
48
+ element.style.zIndex = String(zIndex);
49
+ element.style.animation = "folio-caret-blink 1060ms steps(1, end) infinite";
50
+ setOverlayBox(element, {
51
+ x: caret.x,
52
+ y: caret.y,
53
+ width: 2 * (zoom > 0 ? zoom : 1),
54
+ height: caret.height
55
+ }, pagesContainer, zoom);
56
+ pagesContainer.appendChild(element);
57
+ return { type: "text" };
58
+ }
59
+ const rects = getSelectionRectsFromDom(pagesContainer, selection.from, selection.to, pagesContainer.getBoundingClientRect());
60
+ for (const rect of rects) {
61
+ const element = pagesContainer.ownerDocument.createElement("div");
62
+ element.className = RANGE_CLASS;
63
+ element.dataset["folioSelectionRect"] = "";
64
+ element.dataset["pageIndex"] = String(rect.pageIndex);
65
+ element.style.position = "absolute";
66
+ element.style.background = selectionColor;
67
+ element.style.pointerEvents = "none";
68
+ element.style.zIndex = String(zIndex);
69
+ setOverlayBox(element, rect, pagesContainer, zoom);
70
+ pagesContainer.appendChild(element);
71
+ }
72
+ return { type: "text" };
73
+ }
74
+ };
75
+ //#endregion
76
+ export { BodySelectionOverlay };
@@ -0,0 +1,28 @@
1
+ import { HfSlotKind } from "../layout-bridge/dom/findHfPmSpans.js";
2
+
3
+ //#region src/render-dom/HeaderFooterSelectionOverlay.d.ts
4
+ type HeaderFooterSelection = {
5
+ from: number;
6
+ kind: HfSlotKind;
7
+ pageNumber?: number;
8
+ rId: string;
9
+ to: number;
10
+ };
11
+ type HeaderFooterSelectionRect = {
12
+ height: number;
13
+ width: number;
14
+ x: number;
15
+ y: number;
16
+ };
17
+ type HeaderFooterSelectionGeometry = {
18
+ caret: Omit<HeaderFooterSelectionRect, "width"> | null;
19
+ ranges: HeaderFooterSelectionRect[];
20
+ };
21
+ /** Resolve a persistent header/footer PM selection against its painted DOM. */
22
+ declare const resolveHeaderFooterSelectionGeometry: (pagesContainer: HTMLElement, selection: HeaderFooterSelection, zoom: number) => HeaderFooterSelectionGeometry;
23
+ declare class HeaderFooterSelectionOverlay {
24
+ clear(pagesContainer: HTMLElement): void;
25
+ sync(pagesContainer: HTMLElement, selection: HeaderFooterSelection | null, zoom: number): void;
26
+ }
27
+ //#endregion
28
+ export { HeaderFooterSelection, HeaderFooterSelectionGeometry, HeaderFooterSelectionOverlay, HeaderFooterSelectionRect, resolveHeaderFooterSelectionGeometry };
@@ -0,0 +1,121 @@
1
+ import { findHfCaretSpan, findHfPmSpans } from "../layout-bridge/dom/findHfPmSpans.js";
2
+ //#region src/render-dom/HeaderFooterSelectionOverlay.ts
3
+ const CARET_CLASS = "folio-hf-selection-caret";
4
+ const RANGE_CLASS = "folio-hf-selection-rect";
5
+ const OWNED_SELECTOR = `.${CARET_CLASS}, .${RANGE_CLASS}`;
6
+ const TEXT_NODE_TYPE = 3;
7
+ const relativeRect = (rect, containerRect, zoom) => ({
8
+ x: (rect.left - containerRect.left) / zoom,
9
+ y: (rect.top - containerRect.top) / zoom,
10
+ width: rect.width / zoom,
11
+ height: rect.height / zoom
12
+ });
13
+ /** Resolve a persistent header/footer PM selection against its painted DOM. */
14
+ const resolveHeaderFooterSelectionGeometry = (pagesContainer, selection, zoom) => {
15
+ const containerRect = pagesContainer.getBoundingClientRect();
16
+ const zoomDivisor = zoom === 0 ? 1 : zoom;
17
+ const pageScope = selection.pageNumber ? pagesContainer.querySelector(`.layout-page[data-page-number="${selection.pageNumber}"]`) ?? pagesContainer : pagesContainer;
18
+ if (selection.from === selection.to) {
19
+ const hit = findHfCaretSpan(pageScope, selection.kind, selection.rId, selection.from);
20
+ if (!hit) return {
21
+ caret: null,
22
+ ranges: []
23
+ };
24
+ const anchorRect = hit.element.getBoundingClientRect();
25
+ let left = hit.edge === "right" ? anchorRect.right : anchorRect.left;
26
+ let top = anchorRect.top;
27
+ let height = anchorRect.height || 16;
28
+ const pmStart = Number.parseInt(hit.element.dataset["pmStart"] ?? "", 10);
29
+ const pmEnd = Number.parseInt(hit.element.dataset["pmEnd"] ?? "", 10);
30
+ const textNode = hit.element.firstChild;
31
+ if (textNode?.nodeType === TEXT_NODE_TYPE && Number.isFinite(pmStart) && Number.isFinite(pmEnd)) {
32
+ const offset = Math.min(Math.max(0, selection.from - pmStart), textNode.textContent?.length ?? 0);
33
+ const range = hit.element.ownerDocument.createRange();
34
+ range.setStart(textNode, offset);
35
+ range.setEnd(textNode, offset);
36
+ const rangeRect = range.getBoundingClientRect();
37
+ if (rangeRect.height > 0 || rangeRect.width > 0 || rangeRect.left > 0) {
38
+ left = rangeRect.left;
39
+ top = rangeRect.top;
40
+ height = rangeRect.height || height;
41
+ }
42
+ }
43
+ return {
44
+ caret: {
45
+ x: (left - containerRect.left) / zoomDivisor,
46
+ y: (top - containerRect.top) / zoomDivisor,
47
+ height: height / zoomDivisor
48
+ },
49
+ ranges: []
50
+ };
51
+ }
52
+ const from = Math.min(selection.from, selection.to);
53
+ const to = Math.max(selection.from, selection.to);
54
+ const ranges = [];
55
+ for (const span of findHfPmSpans(pageScope, selection.kind, selection.rId)) {
56
+ const spanStart = Number.parseInt(span.dataset["pmStart"] ?? "", 10);
57
+ const spanEnd = Number.parseInt(span.dataset["pmEnd"] ?? "", 10);
58
+ if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) continue;
59
+ if (spanEnd <= from || spanStart >= to) continue;
60
+ const textNode = span.firstChild;
61
+ if (textNode?.nodeType === TEXT_NODE_TYPE) {
62
+ const length = textNode.textContent?.length ?? 0;
63
+ const startOffset = Math.max(0, from - spanStart);
64
+ const endOffset = Math.min(length, to - spanStart);
65
+ if (startOffset < endOffset) {
66
+ const range = span.ownerDocument.createRange();
67
+ range.setStart(textNode, startOffset);
68
+ range.setEnd(textNode, endOffset);
69
+ for (const rect of Array.from(range.getClientRects())) ranges.push(relativeRect(rect, containerRect, zoomDivisor));
70
+ continue;
71
+ }
72
+ }
73
+ ranges.push(relativeRect(span.getBoundingClientRect(), containerRect, zoomDivisor));
74
+ }
75
+ return {
76
+ caret: null,
77
+ ranges
78
+ };
79
+ };
80
+ const createOverlayElement = (pagesContainer, className) => {
81
+ const element = pagesContainer.ownerDocument.createElement("div");
82
+ element.className = className;
83
+ element.style.position = "absolute";
84
+ element.style.pointerEvents = "none";
85
+ return element;
86
+ };
87
+ var HeaderFooterSelectionOverlay = class {
88
+ clear(pagesContainer) {
89
+ for (const element of pagesContainer.querySelectorAll(OWNED_SELECTOR)) element.remove();
90
+ }
91
+ sync(pagesContainer, selection, zoom) {
92
+ this.clear(pagesContainer);
93
+ if (!selection) return;
94
+ const geometry = resolveHeaderFooterSelectionGeometry(pagesContainer, selection, zoom);
95
+ for (const rect of geometry.ranges) {
96
+ const element = createOverlayElement(pagesContainer, RANGE_CLASS);
97
+ element.dataset["testid"] = "hf-selection-rect";
98
+ element.style.left = `${rect.x}px`;
99
+ element.style.top = `${rect.y}px`;
100
+ element.style.width = `${rect.width}px`;
101
+ element.style.height = `${rect.height}px`;
102
+ element.style.background = "var(--doc-selection, rgba(66, 133, 244, 0.3))";
103
+ element.style.opacity = "0.35";
104
+ element.style.zIndex = "10";
105
+ pagesContainer.append(element);
106
+ }
107
+ if (!geometry.caret) return;
108
+ const caret = createOverlayElement(pagesContainer, CARET_CLASS);
109
+ caret.dataset["testid"] = "hf-caret";
110
+ caret.style.left = `${geometry.caret.x}px`;
111
+ caret.style.top = `${geometry.caret.y}px`;
112
+ caret.style.width = "2px";
113
+ caret.style.height = `${geometry.caret.height}px`;
114
+ caret.style.background = "var(--doc-canvas-text, #000)";
115
+ caret.style.zIndex = "11";
116
+ caret.style.animation = "folio-caret-blink 1060ms steps(1, end) infinite";
117
+ pagesContainer.append(caret);
118
+ }
119
+ };
120
+ //#endregion
121
+ export { HeaderFooterSelectionOverlay, resolveHeaderFooterSelectionGeometry };
@@ -0,0 +1,30 @@
1
+ import { HiddenProseMirrorRemoteSelection } from "../controller/hiddenEditorManager.js";
2
+ import { RenderedDomContext, RenderedDomPoint, RenderedDomRect } from "./RenderedDomContext.js";
3
+
4
+ //#region src/render-dom/RemoteSelectionOverlay.d.ts
5
+ type SyncRemoteSelectionOverlayOptions = {
6
+ pagesContainer: HTMLElement;
7
+ selections: readonly HiddenProseMirrorRemoteSelection[];
8
+ zoom: number;
9
+ zIndex?: number;
10
+ renderedDomContext?: RenderedDomContext;
11
+ };
12
+ type RemoteSelectionOverlayGeometry = {
13
+ caret: RenderedDomPoint | null;
14
+ rects: RenderedDomRect[];
15
+ selection: HiddenProseMirrorRemoteSelection;
16
+ };
17
+ declare const resolveRemoteSelectionOverlayGeometry: (renderedDomContext: RenderedDomContext, selections: readonly HiddenProseMirrorRemoteSelection[]) => RemoteSelectionOverlayGeometry[];
18
+ /** Paint collaborative cursors and selections over the rendered page DOM. */
19
+ declare class RemoteSelectionOverlay {
20
+ clear(pagesContainer: HTMLElement): void;
21
+ sync({
22
+ pagesContainer,
23
+ selections,
24
+ zoom,
25
+ zIndex,
26
+ renderedDomContext
27
+ }: SyncRemoteSelectionOverlayOptions): void;
28
+ }
29
+ //#endregion
30
+ export { RemoteSelectionOverlay, RemoteSelectionOverlayGeometry, SyncRemoteSelectionOverlayOptions, resolveRemoteSelectionOverlayGeometry };
@@ -0,0 +1,67 @@
1
+ import { createRenderedDomContext } from "./RenderedDomContext.js";
2
+ //#region src/render-dom/RemoteSelectionOverlay.ts
3
+ const CARET_CLASS = "folio-remote-selection-caret";
4
+ const LABEL_CLASS = "folio-remote-selection-label";
5
+ const RANGE_CLASS = "folio-remote-selection-rect";
6
+ const OWNED_OVERLAY_SELECTOR = `.${CARET_CLASS}, .${LABEL_CLASS}, .${RANGE_CLASS}`;
7
+ const resolveRemoteSelectionOverlayGeometry = (renderedDomContext, selections) => selections.map((selection) => ({
8
+ caret: renderedDomContext.getCoordinatesForPosition(selection.head),
9
+ rects: renderedDomContext.getRectsForRange(Math.min(selection.anchor, selection.head), Math.max(selection.anchor, selection.head)),
10
+ selection
11
+ }));
12
+ const setBox = (element, rect) => {
13
+ element.style.left = `${rect.x}px`;
14
+ element.style.top = `${rect.y}px`;
15
+ element.style.width = `${rect.width}px`;
16
+ element.style.height = `${rect.height}px`;
17
+ };
18
+ const createOverlayElement = (pagesContainer, className, zIndex) => {
19
+ const element = pagesContainer.ownerDocument.createElement("div");
20
+ element.className = className;
21
+ element.style.position = "absolute";
22
+ element.style.pointerEvents = "none";
23
+ element.style.zIndex = String(zIndex);
24
+ return element;
25
+ };
26
+ /** Paint collaborative cursors and selections over the rendered page DOM. */
27
+ var RemoteSelectionOverlay = class {
28
+ clear(pagesContainer) {
29
+ for (const element of pagesContainer.querySelectorAll(OWNED_OVERLAY_SELECTOR)) element.remove();
30
+ }
31
+ sync({ pagesContainer, selections, zoom, zIndex = 20, renderedDomContext = createRenderedDomContext(pagesContainer, zoom) }) {
32
+ this.clear(pagesContainer);
33
+ for (const { caret, rects, selection } of resolveRemoteSelectionOverlayGeometry(renderedDomContext, selections)) {
34
+ for (const rect of rects) {
35
+ const element = createOverlayElement(pagesContainer, RANGE_CLASS, zIndex);
36
+ element.dataset["clientId"] = String(selection.clientId);
37
+ element.style.background = `color-mix(in srgb, ${selection.color} 24%, transparent)`;
38
+ setBox(element, rect);
39
+ pagesContainer.appendChild(element);
40
+ }
41
+ if (!caret) continue;
42
+ const caretElement = createOverlayElement(pagesContainer, CARET_CLASS, zIndex + 1);
43
+ caretElement.dataset["clientId"] = String(selection.clientId);
44
+ caretElement.style.background = selection.color;
45
+ setBox(caretElement, {
46
+ ...caret,
47
+ width: 2
48
+ });
49
+ pagesContainer.appendChild(caretElement);
50
+ const label = createOverlayElement(pagesContainer, LABEL_CLASS, zIndex + 2);
51
+ label.dataset["clientId"] = String(selection.clientId);
52
+ label.textContent = selection.name;
53
+ label.style.background = selection.color;
54
+ label.style.color = "var(--background, #fff)";
55
+ label.style.fontSize = "10px";
56
+ label.style.lineHeight = "1";
57
+ label.style.padding = "2px 4px";
58
+ label.style.borderRadius = "2px";
59
+ label.style.left = `${caret.x}px`;
60
+ label.style.top = `${Math.max(0, caret.y - 18)}px`;
61
+ label.style.whiteSpace = "nowrap";
62
+ pagesContainer.appendChild(label);
63
+ }
64
+ }
65
+ };
66
+ //#endregion
67
+ export { RemoteSelectionOverlay, resolveRemoteSelectionOverlayGeometry };
@@ -0,0 +1,40 @@
1
+ //#region src/render-dom/RenderedDomContext.d.ts
2
+ /**
3
+ * Framework-neutral mapping from ProseMirror positions to the DOM emitted by
4
+ * the layout painter. Framework adapters use this for decorations, anchored
5
+ * chrome, and plugin surfaces without duplicating range geometry.
6
+ */
7
+ type RenderedDomPoint = {
8
+ x: number;
9
+ y: number;
10
+ height: number;
11
+ };
12
+ type RenderedDomRect = {
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ };
18
+ type RenderedDomContext = {
19
+ getCoordinatesForPosition(pmPos: number): RenderedDomPoint | null;
20
+ findElementsForRange(from: number, to: number): Element[];
21
+ getRectsForRange(from: number, to: number): RenderedDomRect[];
22
+ getContainerOffset(): {
23
+ x: number;
24
+ y: number;
25
+ };
26
+ };
27
+ declare class RenderedDomContextImpl implements RenderedDomContext {
28
+ #private;
29
+ constructor(pagesContainer: HTMLElement, zoom?: number);
30
+ getCoordinatesForPosition(pmPos: number): RenderedDomPoint | null;
31
+ findElementsForRange(from: number, to: number): Element[];
32
+ getRectsForRange(from: number, to: number): RenderedDomRect[];
33
+ getContainerOffset(): {
34
+ x: number;
35
+ y: number;
36
+ };
37
+ }
38
+ declare const createRenderedDomContext: (pagesContainer: HTMLElement, zoom?: number) => RenderedDomContext;
39
+ //#endregion
40
+ export { RenderedDomContext, RenderedDomContextImpl, RenderedDomPoint, RenderedDomRect, createRenderedDomContext };
@@ -0,0 +1,126 @@
1
+ import { findBodyEmptyRuns, findBodyPmSpans } from "../layout-bridge/dom/findBodyPmSpans.js";
2
+ import { closestHtmlElement } from "../utils/domGuards.js";
3
+ //#region src/render-dom/RenderedDomContext.ts
4
+ /**
5
+ * Framework-neutral mapping from ProseMirror positions to the DOM emitted by
6
+ * the layout painter. Framework adapters use this for decorations, anchored
7
+ * chrome, and plugin surfaces without duplicating range geometry.
8
+ */
9
+ const isTextNode = (node) => node?.nodeType === Node.TEXT_NODE;
10
+ const textNodeForSpan = (span) => {
11
+ const firstChild = span.firstChild;
12
+ if (isTextNode(firstChild)) return firstChild;
13
+ if (firstChild instanceof HTMLElement && firstChild.tagName === "A" && isTextNode(firstChild.firstChild)) return firstChild.firstChild;
14
+ return null;
15
+ };
16
+ const lineHeightFor = (element, zoom) => {
17
+ const line = closestHtmlElement(element, ".layout-line");
18
+ return line ? line.getBoundingClientRect().height / zoom : 16;
19
+ };
20
+ var RenderedDomContextImpl = class {
21
+ #pagesContainer;
22
+ #zoom;
23
+ constructor(pagesContainer, zoom = 1) {
24
+ this.#pagesContainer = pagesContainer;
25
+ this.#zoom = zoom > 0 ? zoom : 1;
26
+ }
27
+ getCoordinatesForPosition(pmPos) {
28
+ const containerRect = this.#pagesContainer.getBoundingClientRect();
29
+ for (const span of findBodyPmSpans(this.#pagesContainer)) {
30
+ const pmStart = Number(span.dataset["pmStart"]);
31
+ const pmEnd = Number(span.dataset["pmEnd"]);
32
+ if (!(span.classList.contains("layout-run-tab") ? pmPos >= pmStart && pmPos < pmEnd : pmPos >= pmStart && pmPos <= pmEnd)) continue;
33
+ const spanRect = span.getBoundingClientRect();
34
+ if (span.classList.contains("layout-run-tab")) return {
35
+ x: (spanRect.left - containerRect.left) / this.#zoom,
36
+ y: (spanRect.top - containerRect.top) / this.#zoom,
37
+ height: lineHeightFor(span, this.#zoom)
38
+ };
39
+ const textNode = textNodeForSpan(span);
40
+ if (!textNode) return {
41
+ x: (spanRect.left - containerRect.left) / this.#zoom,
42
+ y: (spanRect.top - containerRect.top) / this.#zoom,
43
+ height: lineHeightFor(span, this.#zoom)
44
+ };
45
+ const charIndex = Math.min(Math.max(0, pmPos - pmStart), textNode.length);
46
+ const range = span.ownerDocument.createRange();
47
+ range.setStart(textNode, charIndex);
48
+ range.setEnd(textNode, charIndex);
49
+ const rangeRect = range.getBoundingClientRect();
50
+ return {
51
+ x: (rangeRect.left - containerRect.left) / this.#zoom,
52
+ y: (rangeRect.top - containerRect.top) / this.#zoom,
53
+ height: lineHeightFor(span, this.#zoom)
54
+ };
55
+ }
56
+ for (const emptyRun of findBodyEmptyRuns(this.#pagesContainer)) {
57
+ const paragraph = closestHtmlElement(emptyRun, ".layout-paragraph");
58
+ if (!paragraph) continue;
59
+ const pmStart = Number(paragraph.dataset["pmStart"]);
60
+ const pmEnd = Number(paragraph.dataset["pmEnd"]);
61
+ if (pmPos < pmStart || pmPos > pmEnd) continue;
62
+ const rect = emptyRun.getBoundingClientRect();
63
+ return {
64
+ x: (rect.left - containerRect.left) / this.#zoom,
65
+ y: (rect.top - containerRect.top) / this.#zoom,
66
+ height: lineHeightFor(emptyRun, this.#zoom)
67
+ };
68
+ }
69
+ return null;
70
+ }
71
+ findElementsForRange(from, to) {
72
+ return findBodyPmSpans(this.#pagesContainer).filter((span) => {
73
+ const pmStart = Number(span.dataset["pmStart"]);
74
+ return Number(span.dataset["pmEnd"]) > from && pmStart < to;
75
+ });
76
+ }
77
+ getRectsForRange(from, to) {
78
+ const containerRect = this.#pagesContainer.getBoundingClientRect();
79
+ const rects = [];
80
+ for (const element of this.findElementsForRange(from, to)) {
81
+ if (!(element instanceof HTMLElement)) continue;
82
+ const pmStart = Number(element.dataset["pmStart"]);
83
+ if (element.classList.contains("layout-run-tab")) {
84
+ const rect = element.getBoundingClientRect();
85
+ rects.push({
86
+ x: (rect.left - containerRect.left) / this.#zoom,
87
+ y: (rect.top - containerRect.top) / this.#zoom,
88
+ width: rect.width / this.#zoom,
89
+ height: rect.height / this.#zoom
90
+ });
91
+ continue;
92
+ }
93
+ const textNode = textNodeForSpan(element);
94
+ if (!textNode) continue;
95
+ const startChar = Math.max(0, from - pmStart);
96
+ const endChar = Math.min(textNode.length, to - pmStart);
97
+ if (startChar >= endChar) continue;
98
+ const range = element.ownerDocument.createRange();
99
+ range.setStart(textNode, startChar);
100
+ range.setEnd(textNode, endChar);
101
+ for (const rect of Array.from(range.getClientRects())) rects.push({
102
+ x: (rect.left - containerRect.left) / this.#zoom,
103
+ y: (rect.top - containerRect.top) / this.#zoom,
104
+ width: rect.width / this.#zoom,
105
+ height: rect.height / this.#zoom
106
+ });
107
+ }
108
+ return rects;
109
+ }
110
+ getContainerOffset() {
111
+ const parent = this.#pagesContainer.parentElement;
112
+ if (!parent) return {
113
+ x: 0,
114
+ y: 0
115
+ };
116
+ const containerRect = this.#pagesContainer.getBoundingClientRect();
117
+ const parentRect = parent.getBoundingClientRect();
118
+ return {
119
+ x: (containerRect.left - parentRect.left) / this.#zoom,
120
+ y: (containerRect.top - parentRect.top) / this.#zoom
121
+ };
122
+ }
123
+ };
124
+ const createRenderedDomContext = (pagesContainer, zoom = 1) => new RenderedDomContextImpl(pagesContainer, zoom);
125
+ //#endregion
126
+ export { RenderedDomContextImpl, createRenderedDomContext };