@stll/folio-core 0.11.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 (194) 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/contentControlWidgetController.d.ts +55 -0
  26. package/dist/controller/contentControlWidgetController.js +141 -0
  27. package/dist/controller/fontReadiness.js +2 -0
  28. package/dist/controller/headerFooterEditorManager.js +1 -1
  29. package/dist/controller/hiddenEditorApi.js +2 -2
  30. package/dist/controller/hiddenEditorManager.d.ts +12 -1
  31. package/dist/controller/hiddenEditorManager.js +32 -11
  32. package/dist/controller/layoutPipeline.d.ts +2 -0
  33. package/dist/controller/layoutPipeline.js +16 -1
  34. package/dist/controller/noteEditorManager.d.ts +36 -0
  35. package/dist/controller/noteEditorManager.js +210 -0
  36. package/dist/document-operations.d.ts +35 -3
  37. package/dist/document-operations.js +161 -3
  38. package/dist/docx/blockContentParser.js +2 -2
  39. package/dist/docx/commentParser.js +6 -1
  40. package/dist/docx/conformance.js +8 -1
  41. package/dist/docx/documentGrid.d.ts +8 -0
  42. package/dist/docx/documentGrid.js +8 -0
  43. package/dist/docx/drawingUtils.js +1 -1
  44. package/dist/docx/ensureParaIds.js +1 -1
  45. package/dist/docx/fieldParser.js +1 -1
  46. package/dist/docx/notePropertiesParser.js +1 -1
  47. package/dist/docx/numberingParser.js +1 -1
  48. package/dist/docx/paragraphParser.js +63 -58
  49. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  50. package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
  51. package/dist/docx/paragraphTraversal.js +1 -1
  52. package/dist/docx/parser.js +3 -3
  53. package/dist/docx/rezip.js +1 -1
  54. package/dist/docx/runParser.js +4 -2
  55. package/dist/docx/sdtProperties.js +1 -1
  56. package/dist/docx/sectionParser.js +1 -1
  57. package/dist/docx/selectiveSave.js +1 -1
  58. package/dist/docx/serializer/commentSerializer.js +1 -2
  59. package/dist/docx/serializer/documentSerializer.js +2 -2
  60. package/dist/docx/serializer/headerFooterSerializer.js +2 -2
  61. package/dist/docx/serializer/noteSerializer.js +2 -2
  62. package/dist/docx/serializer/paragraphSerializer.js +7 -1
  63. package/dist/docx/serializer/runSerializer.js +4 -2
  64. package/dist/docx/serializer/stylesSerializer.js +1 -1
  65. package/dist/docx/serializer/tableSerializer.d.ts +5 -4
  66. package/dist/docx/serializer/tableSerializer.js +17 -13
  67. package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
  68. package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
  69. package/dist/docx/server/boundedArchive.d.ts +14 -3
  70. package/dist/docx/server/boundedArchive.js +56 -11
  71. package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
  72. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
  73. package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
  74. package/dist/docx/server/inspectDocxPackage.js +217 -0
  75. package/dist/docx/server/validateDocxConformance.d.ts +41 -0
  76. package/dist/docx/server/validateDocxConformance.js +336 -0
  77. package/dist/docx/settingsParser.js +11 -1
  78. package/dist/docx/shapeParser.js +1 -1
  79. package/dist/docx/styleParser.js +4 -2
  80. package/dist/docx/tableParser.js +18 -7
  81. package/dist/docx/textBoxParser.d.ts +3 -3
  82. package/dist/docx/textBoxParser.js +16 -12
  83. package/dist/docx/xmlSafety.d.ts +5 -0
  84. package/dist/docx/xmlSafety.js +9 -0
  85. package/dist/fields/resolveFieldValues.js +1 -3
  86. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  87. package/dist/i18n/messages/catalogs.gen.js +17 -0
  88. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  89. package/dist/index.js +1 -1
  90. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  91. package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
  92. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  93. package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
  94. package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
  95. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  96. package/dist/layout-bridge/convert/toFlowBlocks.js +136 -20
  97. package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
  98. package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
  99. package/dist/layout-bridge/engine/clickToPosition.js +2 -2
  100. package/dist/layout-engine/index.d.ts +2 -2
  101. package/dist/layout-engine/index.js +43 -28
  102. package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
  103. package/dist/layout-engine/justificationCompatibility.js +8 -0
  104. package/dist/layout-engine/keep-together.js +11 -1
  105. package/dist/layout-engine/measure/cache.js +21 -5
  106. package/dist/layout-engine/measure/lineBreakProvider.js +53 -34
  107. package/dist/layout-engine/measure/measureBlocks.js +6 -3
  108. package/dist/layout-engine/measure/measureContainer.js +1 -1
  109. package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
  110. package/dist/layout-engine/measure/measureHelpers.js +3 -3
  111. package/dist/layout-engine/measure/measureParagraph.js +69 -25
  112. package/dist/layout-engine/measure/measureProvider.js +4 -3
  113. package/dist/layout-engine/measure/tableCellFlow.js +5 -2
  114. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
  115. package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
  116. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
  117. package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
  118. package/dist/layout-engine/paginator.js +5 -1
  119. package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
  120. package/dist/layout-engine/paragraphSpacing.js +107 -13
  121. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  122. package/dist/layout-engine/types.d.ts +26 -13
  123. package/dist/layout-painter/registry/modules/textBox.js +5 -1
  124. package/dist/layout-painter/renderPage.d.ts +5 -2
  125. package/dist/layout-painter/renderPage.js +56 -12
  126. package/dist/layout-painter/renderParagraph.js +22 -6
  127. package/dist/layout-painter/renderTable.d.ts +5 -1
  128. package/dist/layout-painter/renderTable.js +5 -2
  129. package/dist/layout-painter/renderTextBox.d.ts +2 -1
  130. package/dist/layout-painter/renderTextBox.js +25 -14
  131. package/dist/managers/DocumentLoaderManager.js +1 -1
  132. package/dist/model.js +1 -1
  133. package/dist/prosemirror/attrs/index.d.ts +4 -2
  134. package/dist/prosemirror/attrs/index.js +154 -18
  135. package/dist/prosemirror/commands/comments.d.ts +3 -3
  136. package/dist/prosemirror/commands/comments.js +248 -12
  137. package/dist/prosemirror/commands/contentControls.js +1 -1
  138. package/dist/prosemirror/commands/index.js +1 -1
  139. package/dist/prosemirror/commands/pageBreak.js +1 -1
  140. package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
  141. package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
  142. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
  143. package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
  144. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
  145. package/dist/prosemirror/conversion/fromProseDoc.js +286 -61
  146. package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
  147. package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
  148. package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
  149. package/dist/prosemirror/conversion/toProseDoc.js +347 -98
  150. package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
  151. package/dist/prosemirror/extensions/StarterKit.js +6 -1
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +4 -1
  153. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
  154. package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
  156. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
  157. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
  158. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
  159. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
  160. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
  161. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +24 -1
  162. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  163. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
  164. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +9 -3
  166. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
  167. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
  168. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
  169. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
  170. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +34 -8
  171. package/dist/prosemirror/plugins/contentControlWidgets.js +125 -91
  172. package/dist/prosemirror/plugins/selectionTracker.js +1 -0
  173. package/dist/prosemirror/plugins/suggestionMode.js +1 -1
  174. package/dist/prosemirror/schema/index.d.ts +2 -2
  175. package/dist/prosemirror/schema/marks.d.ts +5 -1
  176. package/dist/prosemirror/schema/nodes.d.ts +64 -7
  177. package/dist/prosemirror/selectionState.js +1 -0
  178. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  179. package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
  180. package/dist/prosemirror/tableCellMergeRevision.js +22 -0
  181. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
  182. package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
  183. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
  184. package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
  185. package/dist/prosemirror/validation.js +9 -1
  186. package/dist/server.d.ts +6 -2
  187. package/dist/server.js +5 -1
  188. package/dist/utils/fontFamilyMerge.js +9 -11
  189. package/dist/utils/fontLoader.js +3 -2
  190. package/dist/utils/fontResolver.js +22 -4
  191. package/dist/utils/fontWeights.d.ts +1 -1
  192. package/dist/utils/fontWeights.js +1 -1
  193. package/dist/utils/paragraphFormattingMerge.js +1 -0
  194. package/package.json +1 -1
@@ -1,4 +1,7 @@
1
+ import { continuesNumberedSequence } from "./paragraphSequence.js";
1
2
  //#region src/layout-engine/paragraphSpacing.ts
3
+ /** Collapse adjacent paragraph spacing to the larger authored side. */
4
+ const collapseParagraphSpacing = ({ before, after }) => Math.max(before, after);
2
5
  /** Whether a paragraph has no visible run content. */
3
6
  function isEmptyParagraph(block) {
4
7
  if (block.runs.length === 0) return true;
@@ -10,21 +13,112 @@ function isEmptyParagraph(block) {
10
13
  * Resolve leading paragraph spacing after applying the empty-paragraph
11
14
  * inherited-spacing collapse rule.
12
15
  */
13
- function getParagraphSpacingBefore(block) {
14
- const value = block.attrs?.spacing?.before ?? 0;
15
- if (value === 0) return 0;
16
- if (isEmptyParagraph(block) && !block.attrs?.styleId && !block.attrs?.hasDirectParagraphFormatting && !block.attrs?.hasDirectParagraphMarkFormatting && !block.attrs?.spacingExplicit?.before) return 0;
17
- return value;
18
- }
16
+ const getParagraphSpacingBefore = (block) => resolveEffectiveParagraphSpacing(block).before;
19
17
  /**
20
18
  * Resolve trailing paragraph spacing after applying the empty-paragraph
21
19
  * inherited-spacing collapse rule.
22
20
  */
23
- function getParagraphSpacingAfter(block) {
24
- const value = block.attrs?.spacing?.after ?? 0;
25
- if (value === 0) return 0;
26
- if (isEmptyParagraph(block) && !block.attrs?.styleId && !block.attrs?.hasDirectParagraphFormatting && !block.attrs?.hasDirectParagraphMarkFormatting && !block.attrs?.spacingExplicit?.after) return 0;
27
- return value;
28
- }
21
+ const getParagraphSpacingAfter = (block) => resolveEffectiveParagraphSpacing(block).after;
22
+ /** Resolve empty-paragraph spacing without modifying the authored block. */
23
+ const resolveEffectiveParagraphSpacing = (block) => {
24
+ const before = block.attrs?.spacing?.before ?? 0;
25
+ const after = block.attrs?.spacing?.after ?? 0;
26
+ if (!isEmptyParagraph(block) || preservesInheritedEmptySpacing(block)) return {
27
+ before,
28
+ after
29
+ };
30
+ return {
31
+ before: block.attrs?.spacingExplicit?.before ? before : 0,
32
+ after: block.attrs?.spacingExplicit?.after ? after : 0
33
+ };
34
+ };
35
+ /**
36
+ * Derive sequence-level spacing suppression with structural sharing.
37
+ *
38
+ * Contextual and automatic list spacing affect layout values, not authored
39
+ * OOXML formatting. Only changed branches are cloned; the input tree remains
40
+ * untouched and repeated resolution is idempotent.
41
+ */
42
+ const resolveEffectiveParagraphSpacingTree = (blocks) => {
43
+ let changed = false;
44
+ const resolved = blocks.map((block, index) => {
45
+ const next = resolveBlockSpacing(blocks, index);
46
+ if (next !== block) changed = true;
47
+ return next;
48
+ });
49
+ return changed ? resolved : blocks;
50
+ };
51
+ const paragraphsShareStyle = (previous, current) => previous.attrs?.styleId === current.attrs?.styleId;
52
+ const preservesInheritedEmptySpacing = (block) => Boolean(block.attrs?.styleId || block.attrs?.hasDirectParagraphFormatting || block.attrs?.hasDirectParagraphMarkFormatting);
53
+ const resolveBlockSpacing = (blocks, index) => {
54
+ const block = blocks[index];
55
+ if (block.kind === "paragraph") return resolveParagraphSequenceSpacing(blocks, index, block);
56
+ if (block.kind === "table") return resolveTableSpacing(block);
57
+ if (block.kind === "textBox") return resolveTextBoxSpacing(block);
58
+ return block;
59
+ };
60
+ const resolveParagraphSequenceSpacing = (blocks, index, block) => {
61
+ const spacing = block.attrs?.spacing;
62
+ if (!spacing) return block;
63
+ const previous = blocks[index - 1];
64
+ const next = blocks[index + 1];
65
+ const suppressBefore = previous?.kind === "paragraph" && (block.attrs?.contextualSpacing === true && paragraphsShareStyle(previous, block) || block.attrs?.automaticSpacing?.before === true && continuesNumberedSequence(previous, block));
66
+ const suppressAfter = next?.kind === "paragraph" && (block.attrs?.contextualSpacing === true && paragraphsShareStyle(block, next) || block.attrs?.automaticSpacing?.after === true && continuesNumberedSequence(block, next));
67
+ const beforeChanged = suppressBefore && spacing.before !== 0;
68
+ const afterChanged = suppressAfter && spacing.after !== 0;
69
+ if (!beforeChanged && !afterChanged) return block;
70
+ const effectiveSpacing = { ...spacing };
71
+ if (beforeChanged) effectiveSpacing.before = 0;
72
+ if (afterChanged) effectiveSpacing.after = 0;
73
+ return {
74
+ ...block,
75
+ attrs: {
76
+ ...block.attrs,
77
+ spacing: effectiveSpacing
78
+ }
79
+ };
80
+ };
81
+ const resolveTextBoxContentSpacing = (blocks) => {
82
+ let changed = false;
83
+ const resolved = blocks.map((block, index) => {
84
+ const next = block.kind === "table" ? resolveTableSpacing(block) : resolveParagraphSequenceSpacing(blocks, index, block);
85
+ if (next !== block) changed = true;
86
+ return next;
87
+ });
88
+ return changed ? resolved : blocks;
89
+ };
90
+ const resolveTableSpacing = (block) => {
91
+ let changed = false;
92
+ const rows = block.rows.map((row) => {
93
+ let rowChanged = false;
94
+ const cells = row.cells.map((cell) => {
95
+ const blocks = resolveEffectiveParagraphSpacingTree(cell.blocks);
96
+ if (blocks === cell.blocks) return cell;
97
+ rowChanged = true;
98
+ return {
99
+ ...cell,
100
+ blocks
101
+ };
102
+ });
103
+ if (!rowChanged) return row;
104
+ changed = true;
105
+ return {
106
+ ...row,
107
+ cells
108
+ };
109
+ });
110
+ return changed ? {
111
+ ...block,
112
+ rows
113
+ } : block;
114
+ };
115
+ const resolveTextBoxSpacing = (block) => {
116
+ const content = resolveTextBoxContentSpacing(block.content);
117
+ if (content === block.content) return block;
118
+ return {
119
+ ...block,
120
+ content
121
+ };
122
+ };
29
123
  //#endregion
30
- export { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph };
124
+ export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree };
@@ -21,7 +21,7 @@ const reconcileBreakBeforeBlock = ({ state, block, previousBlock, page, blocksBy
21
21
  state
22
22
  };
23
23
  const markerAlreadySatisfied = pageStartsWithPreviousParagraphContinuation(page, previousBlock) || state.type === "pageAdvance" && state.reason === "authoredBoundary" || state.type === "pageAdvance" && state.reason === "reflowBoundary" || state.type === "pageAdvance" && isPaginationEmptyParagraph(block) || state.type === "pageAdvance" && (continuesNumberedSequence(previousBlock, block) || continuesTabbedParagraphSequence(previousBlock, block));
24
- const forcePageBreak = (renderedBreakNeedsSnap || block.attrs?.keepNext === true) && !markerAlreadySatisfied && pageHasVisibleBodyContent(page, blocksById);
24
+ const forcePageBreak = (renderedBreakNeedsSnap || block.attrs?.keepNext === true || (block.attrs?.spacing?.before ?? 0) > 0 && previousBlock?.kind !== "sectionBreak" || previousBlock?.kind === "table") && !markerAlreadySatisfied && pageHasVisibleBodyContent(page, blocksById);
25
25
  const followsAuthoredPageBreak = previousBlock?.kind === "pageBreak";
26
26
  const followsSectionBreak = previousBlock?.kind === "sectionBreak";
27
27
  const followsPageBreakingSection = followsSectionBreak && previousBlock.type !== "continuous";
@@ -3,7 +3,7 @@ import { ImagePosition, ImageWrap, SdtProperties, SdtType, ShapeTextBody, TableC
3
3
 
4
4
  //#region src/layout-engine/types.d.ts
5
5
  declare namespace types_d_exports {
6
- export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
6
+ export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
7
7
  }
8
8
  /**
9
9
  * Unique identifier for a block in the document.
@@ -196,6 +196,12 @@ type LineBreakRun = {
196
196
  pmStart?: number;
197
197
  pmEnd?: number;
198
198
  };
199
+ /** Zero-width cached page boundary inside a paragraph. */
200
+ type RenderedPageBreakRun = RunFormatting & {
201
+ kind: "renderedPageBreak";
202
+ pmStart?: number;
203
+ pmEnd?: number;
204
+ };
199
205
  /**
200
206
  * A field run (PAGE, NUMPAGES, etc.) that gets substituted at render time.
201
207
  */
@@ -230,7 +236,7 @@ type MathRun = RunFormatting & {
230
236
  /**
231
237
  * Union of all run types.
232
238
  */
233
- type Run = TextRun | TabRun | ImageRun | LineBreakRun | FieldRun | MathRun;
239
+ type Run = TextRun | TabRun | ImageRun | LineBreakRun | RenderedPageBreakRun | FieldRun | MathRun;
234
240
  /**
235
241
  * Paragraph spacing configuration.
236
242
  */
@@ -293,7 +299,10 @@ type ListNumPr = {
293
299
  * Paragraph block attributes.
294
300
  */
295
301
  type ParagraphAttrs = {
296
- alignment?: "left" | "center" | "right" | "justify"; /** East Asian first/last-character restrictions (`w:kinsoku`). */
302
+ alignment?: "left" | "center" | "right" | "justify"; /** Document-generation policy for justified line fitting. */
303
+ justificationCompatibility?: {
304
+ type: "legacy";
305
+ }; /** East Asian first/last-character restrictions (`w:kinsoku`). */
297
306
  kinsoku?: boolean; /** Hanging punctuation (`w:overflowPunct`); defaults to enabled when omitted. */
298
307
  overflowPunctuation?: boolean; /** Exempt this paragraph from document automatic hyphenation. */
299
308
  suppressAutoHyphens?: boolean; /** Document-wide automatic hyphenation controls retained for line layout. */
@@ -315,7 +324,9 @@ type ParagraphAttrs = {
315
324
  useLegacyEthiopicAmharicRules?: boolean;
316
325
  }; /** OOXML outline level (`w:outlineLvl`), where zero is the top level. */
317
326
  outlineLevel?: number;
318
- spacing?: ParagraphSpacing; /** Spacing sides resolved from OOXML automatic paragraph spacing. */
327
+ spacing?: ParagraphSpacing; /** Whether this paragraph participates in the active section line grid. */
328
+ snapToGrid?: boolean; /** Active section line pitch in pixels; present only on top-level body paragraphs. */
329
+ documentGridLinePitch?: number; /** Spacing sides resolved from OOXML automatic paragraph spacing. */
319
330
  automaticSpacing?: {
320
331
  before?: boolean;
321
332
  after?: boolean;
@@ -343,7 +354,7 @@ type ParagraphAttrs = {
343
354
  keepNext?: boolean;
344
355
  keepLines?: boolean;
345
356
  widowControl?: boolean;
346
- pageBreakBefore?: boolean; /** Word's cached pagination hint (`w:lastRenderedPageBreak`). */
357
+ pageBreakBefore?: boolean; /** Cached OOXML pagination hint (`w:lastRenderedPageBreak`). */
347
358
  renderedPageBreakBefore?: boolean;
348
359
  styleId?: string;
349
360
  contextualSpacing?: boolean;
@@ -572,7 +583,8 @@ type SectionBreakBlock = {
572
583
  };
573
584
  orientation?: "portrait" | "landscape";
574
585
  margins?: PageMargins;
575
- columns?: ColumnLayout;
586
+ columns?: ColumnLayout; /** Line pitch authored by the section properties that end at this boundary, in twips. */
587
+ documentGridLinePitchTwips?: number;
576
588
  };
577
589
  type PageHeaderFooterRefs = {
578
590
  titlePg?: boolean;
@@ -612,7 +624,7 @@ declare const DEFAULT_TEXTBOX_MARGINS: {
612
624
  /** Default text box width in pixels when no width is specified */
613
625
  declare const DEFAULT_TEXTBOX_WIDTH = 200;
614
626
  /**
615
- * Text box block — positioned container with paragraph content.
627
+ * Text box block — positioned container with block content.
616
628
  */
617
629
  type TextBoxBlock = {
618
630
  kind: "textBox";
@@ -629,8 +641,8 @@ type TextBoxBlock = {
629
641
  bottom: number;
630
642
  left: number;
631
643
  right: number;
632
- }; /** Paragraph blocks inside the text box */
633
- content: ParagraphBlock[]; /** Display mode copied from the ProseMirror text box node. */
644
+ }; /** Flow blocks inside the text box */
645
+ content: (ParagraphBlock | TableBlock)[]; /** Display mode copied from the ProseMirror text box node. */
634
646
  displayMode?: "inline" | "float" | "block"; /** CSS float direction copied from the ProseMirror text box node. */
635
647
  cssFloat?: "left" | "right" | "none"; /** OOXML wrap type for anchored text boxes. */
636
648
  wrapType?: ImageWrap["type"]; /** OOXML wrapText direction for anchored text boxes. */
@@ -704,7 +716,8 @@ type MeasuredLine = {
704
716
  floatSkipBefore?: number; /** Decorative hyphen inserted at a dictionary break without changing source text. */
705
717
  discretionaryHyphen?: {
706
718
  runIndex: number;
707
- };
719
+ }; /** A cached pagination boundary occurs immediately before this line. */
720
+ renderedPageBreakBefore?: boolean;
708
721
  };
709
722
  /**
710
723
  * Measurement result for a paragraph block.
@@ -782,8 +795,8 @@ type ColumnBreakMeasure = {
782
795
  type TextBoxMeasure = {
783
796
  kind: "textBox";
784
797
  width: number;
785
- height: number; /** Pre-measured inner paragraph measures (avoids re-measuring during render) */
786
- innerMeasures: ParagraphMeasure[];
798
+ height: number; /** Pre-measured inner content (avoids re-measuring during render) */
799
+ innerMeasures: (ParagraphMeasure | TableMeasure)[];
787
800
  };
788
801
  /**
789
802
  * Union of all measurement types.
@@ -1112,4 +1125,4 @@ declare function tableColumnsArePinned(table: TableBlock): boolean;
1112
1125
  /** Return the leading visual offset for a row with omitted grid columns. */
1113
1126
  declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly number[]) => number;
1114
1127
  //#endregion
1115
- export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
1128
+ export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
@@ -1,4 +1,5 @@
1
1
  import { renderTextBoxFragment } from "../../renderTextBox.js";
2
+ import { renderNestedTable } from "../../renderTable.js";
2
3
  //#region src/layout-painter/registry/modules/textBox.ts
3
4
  /**
4
5
  * Text-box feature module — renders `TextBoxFragment`.
@@ -6,7 +7,10 @@ import { renderTextBoxFragment } from "../../renderTextBox.js";
6
7
  const textBoxModule = {
7
8
  kind: "textBox",
8
9
  render({ fragment, block, measure, context, doc }) {
9
- return renderTextBoxFragment(fragment, block, measure, context, { document: doc });
10
+ return renderTextBoxFragment(fragment, block, measure, context, {
11
+ document: doc,
12
+ renderTable: renderNestedTable
13
+ });
10
14
  }
11
15
  };
12
16
  //#endregion
@@ -71,7 +71,8 @@ declare const getDefaultPageFontFamily: () => string;
71
71
  * A single footnote item ready for rendering at page bottom.
72
72
  */
73
73
  type FootnoteRenderItem = {
74
- /** Display number (e.g. "1", "2") */displayNumber: string; /** Plain text content */
74
+ /** Stable story id used by editable-note surfaces. */noteId?: number; /** Display number (e.g. "1", "2") */
75
+ displayNumber: string; /** Plain text content */
75
76
  text?: string; /** Pre-measured structured footnote content. */
76
77
  content?: Pick<FootnoteContent, "blocks" | "measures" | "height">;
77
78
  };
@@ -87,7 +88,9 @@ type RenderPageOptions = {
87
88
  pageClassName?: string; /** Show page borders (for debugging) */
88
89
  showBorders?: boolean; /** Background color for pages */
89
90
  backgroundColor?: string; /** Drop shadow on pages */
90
- showShadow?: boolean; /** Header content to render (used for all pages, or pages 2+ when titlePg is set). */
91
+ showShadow?: boolean; /** Show the effective body-content boundary for each page. */
92
+ showMarginGuides?: boolean; /** CSS color used for margin guides. */
93
+ marginGuideColor?: string; /** Header content to render (used for all pages, or pages 2+ when titlePg is set). */
91
94
  headerContent?: HeaderFooterContent; /** Footer content to render (used for all pages, or pages 2+ when titlePg is set). */
92
95
  footerContent?: HeaderFooterContent; /** Header content by part relationship id, used for section-scoped rendering. */
93
96
  headerContentByRId?: ReadonlyMap<string, HeaderFooterContent>; /** Footer content by part relationship id, used for section-scoped rendering. */
@@ -10,7 +10,7 @@ import { renderFragment } from "./renderFragment.js";
10
10
  import { applyImageVisualAttrs, hasImageVisualAttrs, renderImageFragment } from "./renderImage.js";
11
11
  import { renderParagraphFragment } from "./renderParagraph.js";
12
12
  import { renderTextBoxFragment } from "./renderTextBox.js";
13
- import { renderTableFragment } from "./renderTable.js";
13
+ import { renderNestedTable, renderTableFragment } from "./renderTable.js";
14
14
  import { renderWatermarkLayer } from "./renderWatermark.js";
15
15
  import { panic } from "better-result";
16
16
  //#region src/layout-painter/renderPage.ts
@@ -35,6 +35,7 @@ const PAGE_CLASS_NAMES = {
35
35
  footer: "layout-page-footer"
36
36
  };
37
37
  const HEADER_FOOTER_RULE_CLIP_MARGIN = pointsToPixels(1.5);
38
+ const DEFAULT_MARGIN_GUIDE_COLOR = "#c0c0c0";
38
39
  const getDefaultPageFontFamily = () => resolveFontFamily("Calibri").cssFallback;
39
40
  /**
40
41
  * Apply page styles to an element
@@ -127,6 +128,26 @@ function renderPageBorderOverlay(page, options, doc) {
127
128
  applyPageBorderSide(overlay, pb.right, "Right", options.theme);
128
129
  return overlay;
129
130
  }
131
+ function renderPageMarginGuideOverlay(page, options, doc) {
132
+ if (options.showMarginGuides !== true) return null;
133
+ const overlay = doc.createElement("div");
134
+ overlay.className = "layout-page-margin-guide";
135
+ overlay.style.position = "absolute";
136
+ overlay.style.top = `${page.margins.top}px`;
137
+ overlay.style.right = `${page.margins.right}px`;
138
+ overlay.style.bottom = `${page.margins.bottom}px`;
139
+ overlay.style.left = `${page.margins.left}px`;
140
+ overlay.style.boxSizing = "border-box";
141
+ overlay.style.border = `1px dashed ${options.marginGuideColor ?? DEFAULT_MARGIN_GUIDE_COLOR}`;
142
+ overlay.style.pointerEvents = "none";
143
+ overlay.style.zIndex = "19";
144
+ return overlay;
145
+ }
146
+ function syncPageMarginGuideOverlay(pageEl, page, options, doc) {
147
+ for (const stale of Array.from(pageEl.querySelectorAll(":scope > .layout-page-margin-guide"))) stale.remove();
148
+ const overlay = renderPageMarginGuideOverlay(page, options, doc);
149
+ if (overlay) pageEl.append(overlay);
150
+ }
130
151
  /**
131
152
  * Refresh the page-border overlay on an existing page shell so incremental
132
153
  * rerenders pick up changes to size, margins, page number, or pageBorders
@@ -470,7 +491,10 @@ function renderHeaderFooterContent(content, context, options, layout) {
470
491
  height: measure.height,
471
492
  ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
472
493
  ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
473
- }, block, measure, hfContext, { document: doc });
494
+ }, block, measure, hfContext, {
495
+ document: doc,
496
+ renderTable: renderNestedTable
497
+ });
474
498
  const textBoxTop = block.position ? resolveHeaderFooterFloatTop({
475
499
  height: measure.height,
476
500
  paragraphY: cursorY,
@@ -546,6 +570,10 @@ function renderFootnoteArea(footnotes, contentWidth, doc, context) {
546
570
  container.append(separator);
547
571
  for (const fn of footnotes) {
548
572
  const fnEl = doc.createElement("div");
573
+ if (fn.noteId !== void 0) {
574
+ fnEl.dataset["noteKind"] = "footnote";
575
+ fnEl.dataset["noteId"] = String(fn.noteId);
576
+ }
549
577
  fnEl.style.marginBottom = `0px`;
550
578
  fnEl.style.color = "var(--doc-canvas-text, #000)";
551
579
  if (fn.content) {
@@ -642,7 +670,10 @@ function renderFootnoteBlock(block, measure, contentWidth, y, context, doc) {
642
670
  y,
643
671
  width: measure.width,
644
672
  height: measure.height
645
- }, renderBlock, measure, context, { document: doc });
673
+ }, renderBlock, measure, context, {
674
+ document: doc,
675
+ renderTable: renderNestedTable
676
+ });
646
677
  positionFootnoteBlock(element, y, measure.width, measure.height);
647
678
  return element;
648
679
  }
@@ -651,13 +682,7 @@ function renderFootnoteBlock(block, measure, contentWidth, y, context, doc) {
651
682
  function stripFootnotePmAnchors(block) {
652
683
  switch (block.kind) {
653
684
  case "paragraph": return stripFootnoteParagraphPmAnchors(block);
654
- case "table": {
655
- const { pmStart: _pmStart, pmEnd: _pmEnd, ...table } = block;
656
- return {
657
- ...table,
658
- rows: table.rows.map(stripFootnoteTableRowPmAnchors)
659
- };
660
- }
685
+ case "table": return stripFootnoteTablePmAnchors(block);
661
686
  case "image": {
662
687
  const { pmStart: _pmStart, pmEnd: _pmEnd, ...image } = block;
663
688
  return image;
@@ -666,7 +691,7 @@ function stripFootnotePmAnchors(block) {
666
691
  const { pmStart: _pmStart, pmEnd: _pmEnd, ...textBox } = block;
667
692
  return {
668
693
  ...textBox,
669
- content: textBox.content.map(stripFootnoteParagraphPmAnchors)
694
+ content: textBox.content.map((contentBlock) => contentBlock.kind === "table" ? stripFootnoteTablePmAnchors(contentBlock) : stripFootnoteParagraphPmAnchors(contentBlock))
670
695
  };
671
696
  }
672
697
  case "pageBreak":
@@ -678,6 +703,13 @@ function stripFootnotePmAnchors(block) {
678
703
  default: return block;
679
704
  }
680
705
  }
706
+ function stripFootnoteTablePmAnchors(block) {
707
+ const { pmStart: _pmStart, pmEnd: _pmEnd, ...table } = block;
708
+ return {
709
+ ...table,
710
+ rows: table.rows.map(stripFootnoteTableRowPmAnchors)
711
+ };
712
+ }
681
713
  function stripFootnoteTableRowPmAnchors(row) {
682
714
  return {
683
715
  ...row,
@@ -931,7 +963,10 @@ function renderPage(page, context, options = {}) {
931
963
  fragmentEl = renderImageFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
932
964
  prevParagraphBorders = void 0;
933
965
  } else if (fragment.kind === "textBox" && blockData?.block.kind === "textBox" && blockData.measure.kind === "textBox") {
934
- fragmentEl = renderTextBoxFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
966
+ fragmentEl = renderTextBoxFragment(fragment, blockData.block, blockData.measure, fragmentContext, {
967
+ document: doc,
968
+ renderTable: renderNestedTable
969
+ });
935
970
  prevParagraphBorders = void 0;
936
971
  } else {
937
972
  fragmentEl = renderFragment(fragment, fragmentContext, { document: doc });
@@ -1074,6 +1109,8 @@ function renderPage(page, context, options = {}) {
1074
1109
  moveBehindHeaderFooterElementsToPage(footerEl, pageEl, footerNaturalTop, page.margins.left);
1075
1110
  }
1076
1111
  if (pageBorderEl && options.pageBorders?.zOrder !== "back") pageEl.append(pageBorderEl);
1112
+ const marginGuideEl = renderPageMarginGuideOverlay(page, options, doc);
1113
+ if (marginGuideEl) pageEl.append(marginGuideEl);
1077
1114
  return pageEl;
1078
1115
  }
1079
1116
  function moveBehindHeaderFooterElementsToPage(slotEl, pageEl, contentTop, contentLeft) {
@@ -1265,6 +1302,7 @@ function collectContentFingerprint(block, parts) {
1265
1302
  }
1266
1303
  function runContentKey(run) {
1267
1304
  if (run.kind === "lineBreak") return "lb";
1305
+ if (run.kind === "renderedPageBreak") return "rpb";
1268
1306
  if (run.kind === "image") return `img:${run.src}|${run.width}x${run.height}${run.transform ? `|tr:${run.transform}` : ""}`;
1269
1307
  const parts = [run.kind];
1270
1308
  if (run.kind === "text") {
@@ -1324,6 +1362,7 @@ function computeOptionsHash(options) {
1324
1362
  if (options.titlePg) parts.push("titlePg");
1325
1363
  if (options.theme) parts.push(`thm:${options.theme.name ?? "default"}`);
1326
1364
  if (options.pageBorders) parts.push(`pb:${JSON.stringify(options.pageBorders)}`);
1365
+ if (options.showMarginGuides === true) parts.push(`mg:${options.marginGuideColor ?? DEFAULT_MARGIN_GUIDE_COLOR}`);
1327
1366
  if (options.headerDistance !== void 0) parts.push(`hd:${options.headerDistance}`);
1328
1367
  if (options.footerDistance !== void 0) parts.push(`fd:${options.footerDistance}`);
1329
1368
  if (options.watermark) parts.push(`wm:${JSON.stringify(options.watermark)}`);
@@ -1412,6 +1451,7 @@ function renderPages(pages, container, options = {}) {
1412
1451
  prev.renderFingerprint = null;
1413
1452
  applyPageStyles(prev.element, page.size.w, page.size.h, options);
1414
1453
  syncPageBorderOverlay(prev.element, page, options, options.document ?? document);
1454
+ syncPageMarginGuideOverlay(prev.element, page, options, options.document ?? document);
1415
1455
  prev.element.dataset["pageNumber"] = String(page.number);
1416
1456
  continue;
1417
1457
  }
@@ -1424,6 +1464,7 @@ function renderPages(pages, container, options = {}) {
1424
1464
  populatePageShell(shell, prevDataMap, totalPages, options);
1425
1465
  applyPageStyles(shell, page.size.w, page.size.h, options);
1426
1466
  syncPageBorderOverlay(shell, page, options, options.document ?? document);
1467
+ syncPageMarginGuideOverlay(shell, page, options, options.document ?? document);
1427
1468
  shell.dataset["pageNumber"] = String(page.number);
1428
1469
  continue;
1429
1470
  }
@@ -1433,6 +1474,7 @@ function renderPages(pages, container, options = {}) {
1433
1474
  prev.renderFingerprint = newRenderFp;
1434
1475
  applyPageStyles(shell, page.size.w, page.size.h, options);
1435
1476
  syncPageBorderOverlay(shell, page, options, options.document ?? document);
1477
+ syncPageMarginGuideOverlay(shell, page, options, options.document ?? document);
1436
1478
  shell.dataset["pageNumber"] = String(page.number);
1437
1479
  }
1438
1480
  if (pages.length > prevShells.length) {
@@ -1445,6 +1487,7 @@ function renderPages(pages, container, options = {}) {
1445
1487
  pageEl.dataset["pageIndex"] = String(i);
1446
1488
  applyPageStyles(pageEl, page.size.w, page.size.h, options);
1447
1489
  syncPageBorderOverlay(pageEl, page, options, doc);
1490
+ syncPageMarginGuideOverlay(pageEl, page, options, doc);
1448
1491
  container.append(pageEl);
1449
1492
  prevShells.push({
1450
1493
  element: pageEl,
@@ -1505,6 +1548,7 @@ function renderPages(pages, container, options = {}) {
1505
1548
  pageEl.dataset["pageIndex"] = String(i);
1506
1549
  applyPageStyles(pageEl, page.size.w, page.size.h, options);
1507
1550
  syncPageBorderOverlay(pageEl, page, options, doc);
1551
+ syncPageMarginGuideOverlay(pageEl, page, options, doc);
1508
1552
  }
1509
1553
  container.insertBefore(pageEl, overlayBefore);
1510
1554
  pageShells.push(pageEl);
@@ -2,8 +2,8 @@ import { parseXmlDocument } from "../docx/xmlParser.js";
2
2
  import { resolveFontFamily } from "../utils/fontResolver.js";
3
3
  import { detectBaseDirection } from "../utils/baseDirection.js";
4
4
  import { AUTHOR_COLORS, getAuthorColorIdx } from "../utils/authorColors.js";
5
+ import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText } from "../layout-engine/measure/textMeasurementPolicy.js";
5
6
  import "../utils/fontWeights.js";
6
- import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode } from "../layout-engine/measure/textMeasurementPolicy.js";
7
7
  import "../layout-engine/measure/measureHelpers.js";
8
8
  import { calculateTabWidth } from "../prosemirror/utils/tabCalculator.js";
9
9
  import { inlineImageBoundingBox, parseRotationDegrees, rotatedBoundingBox } from "../utils/rotationBoundingBox.js";
@@ -113,7 +113,7 @@ function getRaisedRunFontSize(run) {
113
113
  function applyRunStyles(element, run) {
114
114
  if (run.fontFamily) element.style.fontFamily = resolveFontFamily(run.fontFamily).cssFallback;
115
115
  if (run.fontSize) element.style.fontSize = `${fontSizePtToPx(run.fontSize)}px`;
116
- if (run.bold) element.style.fontWeight = "800";
116
+ if (run.bold) element.style.fontWeight = "700";
117
117
  if (run.italic) element.style.fontStyle = "italic";
118
118
  let hasExplicitTextColor = false;
119
119
  const textColor = getRenderableTextColor(run);
@@ -246,12 +246,20 @@ function applyPmPositions(element, pmStart, pmEnd) {
246
246
  function renderTextRun(run, doc) {
247
247
  const span = doc.createElement("span");
248
248
  span.className = `${PARAGRAPH_CLASS_NAMES.run} ${PARAGRAPH_CLASS_NAMES.text}`;
249
+ if (run.footnoteRefId !== void 0) {
250
+ span.dataset["noteKind"] = "footnote";
251
+ span.dataset["noteId"] = String(run.footnoteRefId);
252
+ } else if (run.endnoteRefId !== void 0) {
253
+ span.dataset["noteKind"] = "endnote";
254
+ span.dataset["noteId"] = String(run.endnoteRefId);
255
+ }
249
256
  if (run.templatePreview) {
250
257
  span.classList.add("folio-template-preview-run");
251
258
  if (run.templatePreview === "highlighted") span.classList.add("folio-template-preview-run--highlighted");
252
259
  }
253
260
  applyRunStyles(span, run);
254
261
  applyPmPositions(span, run.pmStart, run.pmEnd);
262
+ const paintedText = toPaintedText(run.text);
255
263
  if (run.hyperlink) {
256
264
  const anchor = doc.createElement("a");
257
265
  anchor.href = run.hyperlink.href;
@@ -260,7 +268,7 @@ function renderTextRun(run, doc) {
260
268
  anchor.rel = "noopener noreferrer";
261
269
  }
262
270
  if (run.hyperlink.tooltip) anchor.title = run.hyperlink.tooltip;
263
- anchor.textContent = run.text;
271
+ anchor.textContent = paintedText;
264
272
  if (!run.hyperlink.noDefaultStyle) {
265
273
  const hyperlinkColor = getHyperlinkTextColor(run, span.style.color);
266
274
  anchor.style.color = hyperlinkColor;
@@ -270,7 +278,7 @@ function renderTextRun(run, doc) {
270
278
  span.style.setProperty("--doc-run-color", hyperlinkColor);
271
279
  }
272
280
  span.append(anchor);
273
- } else span.textContent = run.text;
281
+ } else span.textContent = paintedText;
274
282
  applyWhitespaceUnderline(span, run);
275
283
  return span;
276
284
  }
@@ -642,6 +650,14 @@ function renderRun(run, doc, context) {
642
650
  if (isLineBreakRun(run)) return renderLineBreakRun(run, doc);
643
651
  if (isFieldRun(run) && context) return renderFieldRun(run, doc, context);
644
652
  if (isMathRun(run)) return renderMathRun(run, doc);
653
+ if (run.kind === "renderedPageBreak") {
654
+ const span = doc.createElement("span");
655
+ span.className = PARAGRAPH_CLASS_NAMES.run;
656
+ span.style.display = "none";
657
+ span.dataset["docxRenderedPageBreak"] = "true";
658
+ applyPmPositions(span, run.pmStart, run.pmEnd);
659
+ return span;
660
+ }
645
661
  const span = doc.createElement("span");
646
662
  span.className = PARAGRAPH_CLASS_NAMES.run;
647
663
  return span;
@@ -913,7 +929,7 @@ function createTextMeasurer(doc) {
913
929
  const fontPrefixParts = [];
914
930
  if (style.italic) fontPrefixParts.push("italic");
915
931
  if (style.smallCaps) fontPrefixParts.push("small-caps");
916
- if (style.bold) fontPrefixParts.push("800");
932
+ if (style.bold) fontPrefixParts.push("700");
917
933
  fontPrefixParts.push(`${fontSizePx}px`);
918
934
  const fontPrefix = fontPrefixParts.join(" ");
919
935
  if (style.eastAsiaFontFamily && !style.letterSpacing && hasCjk(text)) {
@@ -1391,7 +1407,7 @@ function renderListMarker(marker, inlineWidth, visualOffset, doc, fontFamily, fo
1391
1407
  span.style.display = "inline-block";
1392
1408
  if (fontFamily) span.style.fontFamily = resolveFontFamily(fontFamily).cssFallback;
1393
1409
  if (fontSize) span.style.fontSize = `${fontSize * 96 / 72}px`;
1394
- if (bold !== void 0) span.style.fontWeight = bold ? "800" : "normal";
1410
+ if (bold !== void 0) span.style.fontWeight = bold ? "700" : "normal";
1395
1411
  span.style.textAlign = "left";
1396
1412
  span.style.textAlignLast = "left";
1397
1413
  span.style.boxSizing = "border-box";
@@ -23,6 +23,10 @@ type RenderTableFragmentOptions = {
23
23
  document?: Document;
24
24
  pageGeometry?: PageGeometry;
25
25
  };
26
+ /**
27
+ * Render a nested table (within a cell)
28
+ */
29
+ declare function renderNestedTable(block: TableBlock, measure: TableMeasure, context: RenderContext, doc: Document): HTMLElement;
26
30
  /**
27
31
  * Render a table fragment to DOM
28
32
  *
@@ -35,4 +39,4 @@ type RenderTableFragmentOptions = {
35
39
  */
36
40
  declare function renderTableFragment(fragment: TableFragment, block: TableBlock, measure: TableMeasure, context: RenderContext, options?: RenderTableFragmentOptions): HTMLElement;
37
41
  //#endregion
38
- export { RenderTableFragmentOptions, TABLE_CLASS_NAMES, renderTableFragment };
42
+ export { RenderTableFragmentOptions, TABLE_CLASS_NAMES, renderNestedTable, renderTableFragment };
@@ -156,7 +156,10 @@ function renderCellContent({ cell, cellMeasure, context, doc, contentWidthOverri
156
156
  }, textBoxBlock, textBoxMeasure, {
157
157
  ...context,
158
158
  insideTableCell: true
159
- }, { document: doc });
159
+ }, {
160
+ document: doc,
161
+ renderTable: renderNestedTable
162
+ });
160
163
  if (isFloatingTextBoxBlock(textBoxBlock)) {
161
164
  floatingTextBoxesLayer ??= createCellFloatingTextBoxesLayer(doc);
162
165
  textBoxEl.style.left = `${resolveCellTextBoxX(textBoxBlock, contentWidth)}px`;
@@ -737,4 +740,4 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
737
740
  return tableEl;
738
741
  }
739
742
  //#endregion
740
- export { TABLE_CLASS_NAMES, renderTableFragment };
743
+ export { TABLE_CLASS_NAMES, renderNestedTable, renderTableFragment };
@@ -1,4 +1,4 @@
1
- import { TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../layout-engine/types.js";
1
+ import { TableBlock, TableMeasure, TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../layout-engine/types.js";
2
2
  import { RenderContext } from "./renderUtils.js";
3
3
 
4
4
  //#region src/layout-painter/renderTextBox.d.ts
@@ -13,6 +13,7 @@ declare const TEXTBOX_CLASS_NAMES: {
13
13
  */
14
14
  type RenderTextBoxFragmentOptions = {
15
15
  document?: Document;
16
+ renderTable?: (block: TableBlock, measure: TableMeasure, context: RenderContext, document: Document) => HTMLElement;
16
17
  };
17
18
  /**
18
19
  * Render a text box fragment to DOM