@stll/folio-core 0.54.2 → 0.55.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 (199) hide show
  1. package/dist/ai-edits/apply.js +22 -1
  2. package/dist/ai-edits/headless.js +1 -0
  3. package/dist/compare/content-types.d.ts +3 -15
  4. package/dist/controller/canonicalComposition.d.ts +36 -0
  5. package/dist/controller/canonicalComposition.js +168 -0
  6. package/dist/controller/canonicalInput.d.ts +45 -0
  7. package/dist/controller/canonicalInput.js +338 -0
  8. package/dist/controller/canonicalOperations.d.ts +22 -0
  9. package/dist/controller/canonicalOperations.js +85 -0
  10. package/dist/controller/canonicalReview.d.ts +12 -0
  11. package/dist/controller/canonicalReview.js +19 -0
  12. package/dist/controller/canonicalSession.d.ts +149 -0
  13. package/dist/controller/canonicalSession.js +1038 -0
  14. package/dist/controller/canonicalStoryEditor.d.ts +34 -0
  15. package/dist/controller/canonicalStoryEditor.js +82 -0
  16. package/dist/controller/canonicalStructure.d.ts +16 -0
  17. package/dist/controller/canonicalStructure.js +269 -0
  18. package/dist/controller/folioEditor.d.ts +1 -1
  19. package/dist/controller/folioEditor.js +9 -0
  20. package/dist/controller/headerFooterEditorManager.d.ts +4 -0
  21. package/dist/controller/headerFooterEditorManager.js +71 -3
  22. package/dist/controller/hiddenEditorApi.d.ts +35 -0
  23. package/dist/controller/hiddenEditorApi.js +25 -8
  24. package/dist/controller/hiddenEditorManager.d.ts +11 -2
  25. package/dist/controller/hiddenEditorManager.js +456 -30
  26. package/dist/controller/layoutPipeline.d.ts +1 -1
  27. package/dist/controller/layoutPipeline.js +2 -0
  28. package/dist/controller/noteEditorManager.d.ts +4 -0
  29. package/dist/controller/noteEditorManager.js +113 -15
  30. package/dist/docx/attributeRemainder.js +1 -0
  31. package/dist/docx/canonicalSessionInput.d.ts +11 -0
  32. package/dist/docx/canonicalSessionInput.js +27 -0
  33. package/dist/docx/canonicalStoryRepack.d.ts +10 -0
  34. package/dist/docx/canonicalStoryRepack.js +41 -0
  35. package/dist/docx/contentTypeRegistry.d.ts +8 -0
  36. package/dist/docx/contentTypeRegistry.js +64 -0
  37. package/dist/docx/ensureParaIds.d.ts +7 -1
  38. package/dist/docx/ensureParaIds.js +163 -211
  39. package/dist/docx/foldedListNumberFields.d.ts +85 -0
  40. package/dist/docx/foldedListNumberFields.js +267 -0
  41. package/dist/docx/headerFooterVerbatim.d.ts +14 -1
  42. package/dist/docx/headerFooterVerbatim.js +95 -2
  43. package/dist/docx/noteIds.d.ts +5 -0
  44. package/dist/docx/noteIds.js +6 -2
  45. package/dist/docx/noteReferenceMark.d.ts +3 -1
  46. package/dist/docx/noteReferenceMark.js +15 -14
  47. package/dist/docx/numberingParser.d.ts +3 -1
  48. package/dist/docx/numberingParser.js +17 -3
  49. package/dist/docx/packageParts.d.ts +3 -1
  50. package/dist/docx/packageParts.js +65 -16
  51. package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
  52. package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
  53. package/dist/docx/paragraphParser.js +101 -53
  54. package/dist/docx/paragraphPropertySource.d.ts +15 -1
  55. package/dist/docx/paragraphPropertySource.js +76 -22
  56. package/dist/docx/parser.js +2 -1
  57. package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
  58. package/dist/docx/reviewHistoryNamespace.js +4 -0
  59. package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
  60. package/dist/docx/reviewResolutionProvenance.js +153 -0
  61. package/dist/docx/rezip.d.ts +7 -3
  62. package/dist/docx/rezip.js +107 -45
  63. package/dist/docx/runConsolidator.d.ts +2 -58
  64. package/dist/docx/runConsolidator.js +3 -175
  65. package/dist/docx/runParser.d.ts +2 -1
  66. package/dist/docx/runParser.js +156 -116
  67. package/dist/docx/saveDiagnostics.d.ts +11 -0
  68. package/dist/docx/saveDiagnostics.js +0 -0
  69. package/dist/docx/selectiveSave.d.ts +2 -1
  70. package/dist/docx/selectiveSave.js +4 -1
  71. package/dist/docx/selectiveXmlPatch.d.ts +16 -8
  72. package/dist/docx/selectiveXmlPatch.js +74 -2
  73. package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
  74. package/dist/docx/serializer/headerFooterSerializer.js +19 -11
  75. package/dist/docx/serializer/noteSerializer.d.ts +2 -2
  76. package/dist/docx/serializer/noteSerializer.js +24 -8
  77. package/dist/docx/serializer/paragraphSerializer.js +41 -4
  78. package/dist/docx/serializer/partNamespaces.js +2 -1
  79. package/dist/docx/serializer/runSerializer.js +3 -1
  80. package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
  81. package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
  82. package/dist/docx/settingsParser.d.ts +1 -2
  83. package/dist/docx/settingsParser.js +5 -6
  84. package/dist/docx/storyBlockReplay.d.ts +11 -0
  85. package/dist/docx/storyBlockReplay.js +367 -0
  86. package/dist/docx/streamingXmlParser.d.ts +22 -1
  87. package/dist/docx/streamingXmlParser.js +49 -3
  88. package/dist/docx/structuralXmlPatch.js +8 -1
  89. package/dist/docx/verbatimCapture.d.ts +3 -1
  90. package/dist/docx/verbatimCapture.js +49 -6
  91. package/dist/i18n/messages/ar.gen.d.ts +5 -0
  92. package/dist/i18n/messages/ar.gen.js +656 -0
  93. package/dist/i18n/messages/cs.gen.d.ts +5 -0
  94. package/dist/i18n/messages/cs.gen.js +656 -0
  95. package/dist/i18n/messages/de.gen.d.ts +5 -0
  96. package/dist/i18n/messages/de.gen.js +656 -0
  97. package/dist/i18n/messages/en.gen.d.ts +5 -0
  98. package/dist/i18n/messages/en.gen.js +656 -0
  99. package/dist/i18n/messages/es.gen.d.ts +5 -0
  100. package/dist/i18n/messages/es.gen.js +656 -0
  101. package/dist/i18n/messages/et.gen.d.ts +5 -0
  102. package/dist/i18n/messages/et.gen.js +656 -0
  103. package/dist/i18n/messages/fr.gen.d.ts +5 -0
  104. package/dist/i18n/messages/fr.gen.js +656 -0
  105. package/dist/i18n/messages/he.gen.d.ts +5 -0
  106. package/dist/i18n/messages/he.gen.js +656 -0
  107. package/dist/i18n/messages/hi.gen.d.ts +5 -0
  108. package/dist/i18n/messages/hi.gen.js +656 -0
  109. package/dist/i18n/messages/hu.gen.d.ts +5 -0
  110. package/dist/i18n/messages/hu.gen.js +656 -0
  111. package/dist/i18n/messages/locales.d.ts +5 -0
  112. package/dist/i18n/messages/locales.js +22 -0
  113. package/dist/i18n/messages/lt.gen.d.ts +5 -0
  114. package/dist/i18n/messages/lt.gen.js +656 -0
  115. package/dist/i18n/messages/lv.gen.d.ts +5 -0
  116. package/dist/i18n/messages/lv.gen.js +656 -0
  117. package/dist/i18n/messages/pl.gen.d.ts +5 -0
  118. package/dist/i18n/messages/pl.gen.js +656 -0
  119. package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
  120. package/dist/i18n/messages/pt-BR.gen.js +656 -0
  121. package/dist/i18n/messages/sk.gen.d.ts +5 -0
  122. package/dist/i18n/messages/sk.gen.js +656 -0
  123. package/dist/i18n/messages/tr.gen.d.ts +5 -0
  124. package/dist/i18n/messages/tr.gen.js +656 -0
  125. package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
  126. package/dist/i18n/messages/zh-CN.gen.js +656 -0
  127. package/dist/i18n/messages.d.ts +2 -3
  128. package/dist/i18n/messages.js +1 -19
  129. package/dist/internal/noteMarkerAttrs.d.ts +8 -0
  130. package/dist/internal/noteMarkerAttrs.js +16 -0
  131. package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
  132. package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
  133. package/dist/internal/prosemirrorAttrBoundary.js +50 -0
  134. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  135. package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
  136. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  137. package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
  138. package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
  139. package/dist/layout-bridge/convert/noteReferences.js +1 -6
  140. package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
  141. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
  142. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
  143. package/dist/managers/DocumentLoaderManager.d.ts +1 -0
  144. package/dist/managers/DocumentLoaderManager.js +8 -1
  145. package/dist/markdown/renderBlock.js +15 -1
  146. package/dist/markdown/renderParagraph.js +9 -13
  147. package/dist/markdown/renderRuns.js +3 -2
  148. package/dist/markdown/renderTable.js +2 -0
  149. package/dist/markdown/types.d.ts +3 -9
  150. package/dist/prosemirror/attrs/index.d.ts +2 -12
  151. package/dist/prosemirror/attrs/index.js +22 -48
  152. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
  153. package/dist/prosemirror/canonicalCommands.d.ts +97 -0
  154. package/dist/prosemirror/canonicalCommands.js +28 -0
  155. package/dist/prosemirror/commands/pageBreak.js +15 -1
  156. package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
  157. package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
  158. package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
  159. package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
  160. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -3
  161. package/dist/prosemirror/conversion/toProseDoc.js +51 -11
  162. package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
  163. package/dist/prosemirror/executeEditorCommand.js +26 -0
  164. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  165. package/dist/prosemirror/extensions/core/ParagraphExtension.js +50 -19
  166. package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
  167. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
  168. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
  169. package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
  170. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  171. package/dist/prosemirror/extensions/marks/markUtils.js +70 -8
  172. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
  173. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
  174. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
  175. package/dist/prosemirror/listMarker.d.ts +1 -0
  176. package/dist/prosemirror/listMarker.js +16 -7
  177. package/dist/prosemirror/listNumbering.js +26 -5
  178. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  179. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  180. package/dist/prosemirror/markupViewNotes.js +3 -2
  181. package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
  182. package/dist/prosemirror/noteReferenceReview.js +5 -3
  183. package/dist/prosemirror/plugins/suggestionMode.js +50 -2
  184. package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
  185. package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
  186. package/dist/prosemirror/runFormattingFromMarks.js +15 -1
  187. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
  188. package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
  189. package/dist/prosemirror/schema/marks.d.ts +2 -0
  190. package/dist/prosemirror/schema/nodes.d.ts +10 -2
  191. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
  192. package/dist/prosemirror/textInput.js +5 -1
  193. package/dist/prosemirror/trackedRevisionPath.js +2 -1
  194. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
  195. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
  196. package/dist/prosemirror/validation.js +4 -0
  197. package/dist/types/content.d.ts +2 -2
  198. package/dist/utils/createDocument.js +2 -2
  199. package/package.json +14 -2
@@ -0,0 +1,267 @@
1
+ //#region src/docx/foldedListNumberFields.ts
2
+ /** Zero-width markup that can stand between a field and its tab. */
3
+ const RANGE_MARKER_TYPES = /* @__PURE__ */ new Set([
4
+ "bookmarkStart",
5
+ "bookmarkEnd",
6
+ "commentRangeStart",
7
+ "commentRangeEnd",
8
+ "commentReference"
9
+ ]);
10
+ /** Markup that shows nothing and so does not end the start of a paragraph. */
11
+ const ZERO_WIDTH_TYPES = /* @__PURE__ */ new Set([
12
+ ...RANGE_MARKER_TYPES,
13
+ "moveFromRangeStart",
14
+ "moveFromRangeEnd",
15
+ "moveToRangeStart",
16
+ "moveToRangeEnd"
17
+ ]);
18
+ /** Whether `content` is markup that may stand between a field and the tab that follows it. */
19
+ const isListNumberGapMarker = (content) => RANGE_MARKER_TYPES.has(content.type);
20
+ const isZeroWidth = (content) => ZERO_WIDTH_TYPES.has(content.type) || content.type === "preservedInline" && content.text === "";
21
+ /** Whether `content` is a `LISTNUM` field, by its parsed type or its instruction. */
22
+ const isListNumberField = (content) => content.type === "complexField" && (content.fieldType === "LISTNUM" || content.instruction.trim().toUpperCase().startsWith("LISTNUM"));
23
+ /** A run that holds one tab and nothing else. */
24
+ const isTabOnlyRun = (content) => content.type === "run" && content.content.length === 1 && content.content[0]?.type === "tab";
25
+ /** The text a field's cached result puts in the marker. */
26
+ const cachedListNumberText = (field) => {
27
+ let text = "";
28
+ for (const run of field.fieldResult) for (const piece of run.content) if (piece.type === "text") text += piece.text;
29
+ return text;
30
+ };
31
+ const isRecord = (value) => typeof value === "object" && value !== null;
32
+ /** Whether `value` is what a capture stands for: a complex field, or a run. */
33
+ const isFoldedListNumber = (value) => {
34
+ if (!isRecord(value)) return false;
35
+ if (value["kind"] === "field") {
36
+ const field = value["field"];
37
+ return isRecord(field) && field["type"] === "complexField" && typeof field["instruction"] === "string" && Array.isArray(field["fieldCode"]) && Array.isArray(field["fieldResult"]);
38
+ }
39
+ if (value["kind"] === "tab") {
40
+ const run = value["run"];
41
+ return isRecord(run) && run["type"] === "run" && Array.isArray(run["content"]);
42
+ }
43
+ return false;
44
+ };
45
+ /** What `content` stands for when it is a capture the fold left, else nothing. */
46
+ const foldedListNumberOf = (content) => content.type === "preservedInline" ? content.foldedListNumber : void 0;
47
+ /** Whether `content` is a capture the fold left in place of a field or its tab. */
48
+ const isFoldedListNumberCapture = (content) => foldedListNumberOf(content) !== void 0;
49
+ /**
50
+ * Replace the `LISTNUM` fields that open a paragraph, and the tab after each,
51
+ * by captures of the markup they were read from.
52
+ *
53
+ * Only what stands ahead of the first thing the line shows is folded: a field
54
+ * after text is on the line where the text is. `sourceMarkupOf` answers the
55
+ * markup; a field it has none for is left as the field it is, and ends the
56
+ * fold. Bookmark and comment markers between a field and its tab stay where
57
+ * they are, and the tab is still the field's when only they separate the two.
58
+ */
59
+ const foldListNumberFields = (content, sourceMarkupOf) => {
60
+ const folded = [];
61
+ const cached = [];
62
+ let fieldCount = 0;
63
+ let leading = true;
64
+ let awaitingTab = false;
65
+ for (const item of content) {
66
+ if (isListNumberField(item)) fieldCount += 1;
67
+ if (!leading) {
68
+ folded.push(item);
69
+ continue;
70
+ }
71
+ if (awaitingTab && RANGE_MARKER_TYPES.has(item.type)) {
72
+ folded.push(item);
73
+ continue;
74
+ }
75
+ if (awaitingTab) {
76
+ awaitingTab = false;
77
+ const tabMarkup = isTabOnlyRun(item) ? sourceMarkupOf(item) : void 0;
78
+ if (isTabOnlyRun(item) && tabMarkup !== void 0) {
79
+ folded.push({
80
+ type: "preservedInline",
81
+ xml: tabMarkup,
82
+ text: "",
83
+ foldedListNumber: {
84
+ kind: "tab",
85
+ run: item
86
+ }
87
+ });
88
+ continue;
89
+ }
90
+ }
91
+ if (isListNumberField(item)) {
92
+ const markup = sourceMarkupOf(item);
93
+ if (markup !== void 0) {
94
+ folded.push({
95
+ type: "preservedInline",
96
+ xml: markup,
97
+ text: "",
98
+ foldedListNumber: {
99
+ kind: "field",
100
+ field: item
101
+ }
102
+ });
103
+ const text = cachedListNumberText(item);
104
+ if (text) cached.push(text);
105
+ awaitingTab = true;
106
+ continue;
107
+ }
108
+ }
109
+ folded.push(item);
110
+ leading = isZeroWidth(item);
111
+ }
112
+ return {
113
+ content: folded,
114
+ cached,
115
+ fieldCount
116
+ };
117
+ };
118
+ const isCapture = (item) => item?.kind === "field" || item?.kind === "tab";
119
+ /**
120
+ * Decide which captures of a paragraph stay hidden, and what its marker shows.
121
+ *
122
+ * `markerShowsFields` says whether the paragraph's marker shows folded fields
123
+ * at all. When it does not, no capture is hidden.
124
+ *
125
+ * When it does, the captures it shows are the ones that open the paragraph:
126
+ * those ahead of the first thing the line shows, each field followed at most
127
+ * by its own tab. Text put in front of them was put in front of the body, not
128
+ * of the marker, so if the first capture no longer opens the paragraph, it
129
+ * and the captures that stand with it go back to the start. Every capture
130
+ * further in is on the line.
131
+ */
132
+ const planListNumberFold = (items, markerShowsFields) => {
133
+ const unmoved = items.map((_, index) => index);
134
+ const none = {
135
+ order: unmoved,
136
+ hidden: /* @__PURE__ */ new Set(),
137
+ suffix: void 0
138
+ };
139
+ const first = items.findIndex((item) => isCapture(item));
140
+ if (!markerShowsFields || first === -1) return none;
141
+ let order = unmoved;
142
+ if (items.slice(0, first).some((item) => item.kind === "shown")) {
143
+ let end = first + 1;
144
+ for (let index = first + 1; index < items.length; index += 1) {
145
+ const item = items[index];
146
+ if (item?.kind === "shown") break;
147
+ if (isCapture(item)) end = index + 1;
148
+ }
149
+ order = [
150
+ ...unmoved.slice(first, end),
151
+ ...unmoved.slice(0, first),
152
+ ...unmoved.slice(end)
153
+ ];
154
+ }
155
+ const hidden = /* @__PURE__ */ new Set();
156
+ const cached = [];
157
+ let fieldAwaitsTab = false;
158
+ for (const index of order) {
159
+ const item = items[index];
160
+ if (item?.kind === "hidden") continue;
161
+ if (item?.kind === "field") {
162
+ hidden.add(index);
163
+ if (item.cached) cached.push(item.cached);
164
+ fieldAwaitsTab = true;
165
+ continue;
166
+ }
167
+ if (item?.kind === "tab" && fieldAwaitsTab) {
168
+ hidden.add(index);
169
+ fieldAwaitsTab = false;
170
+ continue;
171
+ }
172
+ break;
173
+ }
174
+ return cached.length === 0 ? none : {
175
+ order,
176
+ hidden,
177
+ suffix: cached.join(" ")
178
+ };
179
+ };
180
+ /**
181
+ * What a paragraph's marker says about folded fields. It shows them after a
182
+ * tab its own level text does not have; a bullet shows only its glyph.
183
+ */
184
+ const listMarkerFoldState = ({ marker, template, isBullet, numbered }) => {
185
+ const tab = marker?.indexOf(" ") ?? -1;
186
+ if (!marker || !numbered || isBullet || !template || template.includes(" ") || tab === -1) return {
187
+ showsFields: false,
188
+ base: marker ?? ""
189
+ };
190
+ return {
191
+ showsFields: true,
192
+ base: marker.slice(0, tab)
193
+ };
194
+ };
195
+ /** The marker text for `state` showing `suffix`. */
196
+ const listMarkerWithFold = (state, suffix) => suffix === void 0 ? state.base : `${state.base}\t${suffix}`;
197
+ const foldItemOf = (content) => {
198
+ const folded = foldedListNumberOf(content);
199
+ if (folded) return folded.kind === "field" ? {
200
+ kind: "field",
201
+ cached: cachedListNumberText(folded.field)
202
+ } : { kind: "tab" };
203
+ return isZeroWidth(content) ? { kind: "hidden" } : { kind: "shown" };
204
+ };
205
+ /** The item a capture stands for, which shows on the line; any other item is itself. */
206
+ const unfoldedListNumberContent = (content) => {
207
+ const folded = foldedListNumberOf(content);
208
+ if (!folded) return content;
209
+ return folded.kind === "field" ? folded.field : folded.run;
210
+ };
211
+ /**
212
+ * The one definition of a tracked change for the fold: content inserted,
213
+ * deleted, or moved from or to a place under revision tracking.
214
+ */
215
+ const isTrackedChange = (content) => content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo";
216
+ /**
217
+ * `content` with every capture inside a tracked change replaced by the item
218
+ * it stands for. A tracked change is written with its own spellings (deleted
219
+ * text is `w:delText`, a deleted instruction `w:delInstrText`), which markup
220
+ * replayed as it was read does not have; the field and the run do.
221
+ */
222
+ const withTrackedCapturesShown = (content) => {
223
+ let changed = false;
224
+ const next = content.map((item) => {
225
+ if (!isTrackedChange(item)) return item;
226
+ if (!item.content.some((child) => foldedListNumberOf(child) !== void 0)) return item;
227
+ changed = true;
228
+ return {
229
+ ...item,
230
+ content: item.content.map((child) => {
231
+ const folded = foldedListNumberOf(child);
232
+ if (!folded) return child;
233
+ return structuredClone(folded.kind === "field" ? folded.field : folded.run);
234
+ })
235
+ };
236
+ });
237
+ return changed ? next : content;
238
+ };
239
+ /**
240
+ * Bring `paragraph` to the one form the fold allows: the captures its marker
241
+ * shows stand at its start, every other capture is the field or the tab it
242
+ * stood for, and the marker shows exactly the fields still hidden behind it.
243
+ * A capture under a tracked change is never hidden.
244
+ */
245
+ const normalizeFoldedListNumbers = (paragraph) => {
246
+ paragraph.content = withTrackedCapturesShown(paragraph.content);
247
+ const rendering = paragraph.listRendering;
248
+ const state = listMarkerFoldState({
249
+ marker: rendering?.marker,
250
+ template: rendering?.markerTemplate,
251
+ isBullet: rendering?.isBullet === true,
252
+ numbered: rendering !== void 0
253
+ });
254
+ const content = paragraph.content;
255
+ if (!state.showsFields && !content.some(isFoldedListNumberCapture)) return;
256
+ const plan = planListNumberFold(content.map(foldItemOf), state.showsFields);
257
+ const next = [];
258
+ for (const index of plan.order) {
259
+ const item = content[index];
260
+ if (item === void 0) continue;
261
+ next.push(plan.hidden.has(index) || !isFoldedListNumberCapture(item) ? item : structuredClone(unfoldedListNumberContent(item)));
262
+ }
263
+ paragraph.content = next;
264
+ if (rendering && state.showsFields) rendering.marker = listMarkerWithFold(state, plan.suffix);
265
+ };
266
+ //#endregion
267
+ export { foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, unfoldedListNumberContent };
@@ -1,9 +1,22 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  //#region src/docx/headerFooterVerbatim.d.ts
3
+ type HeaderFooterSourceBaseline = {
4
+ type: "missing";
5
+ } | {
6
+ type: "mismatch";
7
+ } | {
8
+ type: "captured";
9
+ content: readonly document_d_exports.BlockContent[];
10
+ };
11
+ declare const captureHeaderFooterPackageBaselines: (document: document_d_exports.Document) => void;
12
+ /** Transfer the parsed package registry only across a trusted document graph clone. */
13
+ declare const copyHeaderFooterPackageBaselines: (target: document_d_exports.Document, source: document_d_exports.Document) => void;
14
+ declare const getHeaderFooterSourceBaseline: (hf: document_d_exports.HeaderFooter, originalBuffer?: ArrayBuffer) => HeaderFooterSourceBaseline;
15
+ declare const canReplayHeaderFooterBlocks: (hf: document_d_exports.HeaderFooter, baselineContent: readonly document_d_exports.BlockContent[]) => boolean;
3
16
  declare const getHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter) => string | undefined;
4
17
  declare const canReplayHeaderFooterVerbatim: (hf: document_d_exports.HeaderFooter) => boolean;
5
18
  declare const assignHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter, xml: string) => void;
6
19
  declare const refreshHeaderFooterVerbatimFingerprint: (hf: document_d_exports.HeaderFooter) => void;
7
20
  declare const clearHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter) => void;
8
21
  //#endregion
9
- export { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint };
22
+ export { assignHeaderFooterVerbatimXml, canReplayHeaderFooterBlocks, canReplayHeaderFooterVerbatim, captureHeaderFooterPackageBaselines, clearHeaderFooterVerbatimXml, copyHeaderFooterPackageBaselines, getHeaderFooterSourceBaseline, getHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint };
@@ -1,5 +1,89 @@
1
+ import { canonicalJson } from "../utils/canonicalJson.js";
2
+ import { Result } from "better-result";
1
3
  //#region src/docx/headerFooterVerbatim.ts
4
+ const fingerprintBaselines = /* @__PURE__ */ new WeakMap();
5
+ const readFingerprint = (hf) => {
6
+ const fingerprint = hf.verbatimFingerprint;
7
+ if (!fingerprint) return void 0;
8
+ const cached = fingerprintBaselines.get(hf);
9
+ if (cached?.fingerprint === fingerprint) return cached.value;
10
+ const parsed = Result.try(() => JSON.parse(fingerprint));
11
+ const value = parsed.isOk() ? parsed.value : void 0;
12
+ fingerprintBaselines.set(hf, {
13
+ fingerprint,
14
+ value
15
+ });
16
+ return value;
17
+ };
18
+ const BASELINE_HANDLE = Symbol("header-footer-source-baseline");
19
+ const contentBaselines = /* @__PURE__ */ new WeakMap();
20
+ const captureContentBaseline = (hf) => {
21
+ const fingerprint = hf.verbatimFingerprint;
22
+ if (fingerprint === void 0) return;
23
+ const handle = {};
24
+ const content = structuredClone(hf.content);
25
+ contentBaselines.set(handle, {
26
+ fingerprint,
27
+ xml: hf.verbatimXml,
28
+ contentFingerprint: canonicalJson(content),
29
+ content
30
+ });
31
+ Object.defineProperty(hf, BASELINE_HANDLE, {
32
+ value: handle,
33
+ enumerable: true,
34
+ configurable: true
35
+ });
36
+ };
37
+ const packageBaselines = /* @__PURE__ */ new WeakMap();
38
+ const captureHeaderFooterPackageBaselines = (document) => {
39
+ if (!document.originalBuffer) return;
40
+ const handles = /* @__PURE__ */ new Map();
41
+ for (const parts of [document.package.headers, document.package.footers]) for (const hf of parts?.values() ?? []) {
42
+ if (!(BASELINE_HANDLE in hf) || typeof hf[BASELINE_HANDLE] !== "object" || hf[BASELINE_HANDLE] === null || hf.verbatimFingerprint === void 0 || hf.verbatimXml === void 0) continue;
43
+ const bySource = handles.get(hf.verbatimFingerprint) ?? /* @__PURE__ */ new Map();
44
+ bySource.set(hf.verbatimXml, hf[BASELINE_HANDLE]);
45
+ handles.set(hf.verbatimFingerprint, bySource);
46
+ }
47
+ packageBaselines.set(document.originalBuffer, handles);
48
+ };
49
+ /** Transfer the parsed package registry only across a trusted document graph clone. */
50
+ const copyHeaderFooterPackageBaselines = (target, source) => {
51
+ if (!target.originalBuffer || !source.originalBuffer) return;
52
+ const baselines = packageBaselines.get(source.originalBuffer);
53
+ if (baselines) packageBaselines.set(target.originalBuffer, baselines);
54
+ };
55
+ const getHeaderFooterSourceBaseline = (hf, originalBuffer) => {
56
+ let handle;
57
+ if (BASELINE_HANDLE in hf) handle = hf[BASELINE_HANDLE];
58
+ else if (originalBuffer && hf.verbatimFingerprint !== void 0 && hf.verbatimXml !== void 0) handle = packageBaselines.get(originalBuffer)?.get(hf.verbatimFingerprint)?.get(hf.verbatimXml);
59
+ if (handle === void 0 && !(BASELINE_HANDLE in hf)) return { type: "missing" };
60
+ if (typeof handle !== "object" || handle === null) return { type: "mismatch" };
61
+ const baseline = contentBaselines.get(handle);
62
+ if (!baseline || baseline.fingerprint !== hf.verbatimFingerprint || baseline.xml !== hf.verbatimXml || baseline.contentFingerprint !== canonicalJson(baseline.content)) return { type: "mismatch" };
63
+ return {
64
+ type: "captured",
65
+ content: baseline.content
66
+ };
67
+ };
68
+ const canReplayHeaderFooterBlocks = (hf, baselineContent) => {
69
+ if (hf.rawWatermarkXml !== void 0) {
70
+ const index = hf.watermarkBlockIndex;
71
+ if (index === void 0 || !Number.isInteger(index) || index < 0 || index >= baselineContent.length || baselineContent.length !== hf.content.length || canonicalJson(baselineContent.at(index)) !== canonicalJson(hf.content.at(index))) return false;
72
+ }
73
+ const baseline = readFingerprint(hf);
74
+ if (baseline === null || typeof baseline !== "object" || !("type" in baseline) || baseline.type !== hf.type) return false;
75
+ return canonicalJson({
76
+ watermark: "watermark" in baseline ? baseline.watermark : void 0,
77
+ watermarkBlockIndex: "watermarkBlockIndex" in baseline ? baseline.watermarkBlockIndex : void 0,
78
+ rawWatermarkXml: "rawWatermarkXml" in baseline ? baseline.rawWatermarkXml : void 0
79
+ }) === canonicalJson({
80
+ watermark: hf.watermark,
81
+ watermarkBlockIndex: hf.watermarkBlockIndex,
82
+ rawWatermarkXml: hf.rawWatermarkXml
83
+ });
84
+ };
2
85
  const headerFooterSerializationFingerprint = (hf) => JSON.stringify({
86
+ type: hf.type,
3
87
  content: hf.content,
4
88
  watermark: hf.watermark,
5
89
  watermarkBlockIndex: hf.watermarkBlockIndex,
@@ -9,22 +93,31 @@ const getHeaderFooterVerbatimXml = (hf) => hf.verbatimXml;
9
93
  const canReplayHeaderFooterVerbatim = (hf) => {
10
94
  const ext = hf;
11
95
  if (!ext.verbatimXml || !ext.verbatimFingerprint) return false;
12
- return ext.verbatimFingerprint === headerFooterSerializationFingerprint(hf);
96
+ return canonicalJson(readFingerprint(hf)) === canonicalJson({
97
+ type: hf.type,
98
+ content: hf.content,
99
+ watermark: hf.watermark,
100
+ watermarkBlockIndex: hf.watermarkBlockIndex,
101
+ rawWatermarkXml: hf.rawWatermarkXml
102
+ });
13
103
  };
14
104
  const assignHeaderFooterVerbatimXml = (hf, xml) => {
15
105
  const ext = hf;
16
106
  ext.verbatimXml = xml;
17
107
  ext.verbatimFingerprint = headerFooterSerializationFingerprint(hf);
108
+ captureContentBaseline(hf);
18
109
  };
19
110
  const refreshHeaderFooterVerbatimFingerprint = (hf) => {
20
111
  const ext = hf;
21
112
  if (!ext.verbatimXml) return;
22
113
  ext.verbatimFingerprint = headerFooterSerializationFingerprint(hf);
114
+ captureContentBaseline(hf);
23
115
  };
24
116
  const clearHeaderFooterVerbatimXml = (hf) => {
25
117
  const ext = hf;
26
118
  delete ext.verbatimXml;
27
119
  delete ext.verbatimFingerprint;
120
+ Reflect.deleteProperty(hf, BASELINE_HANDLE);
28
121
  };
29
122
  //#endregion
30
- export { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint };
123
+ export { assignHeaderFooterVerbatimXml, canReplayHeaderFooterBlocks, canReplayHeaderFooterVerbatim, captureHeaderFooterPackageBaselines, clearHeaderFooterVerbatimXml, copyHeaderFooterPackageBaselines, getHeaderFooterSourceBaseline, getHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint };
@@ -1,4 +1,9 @@
1
1
  //#region src/docx/noteIds.d.ts
2
+ /**
3
+ * The next free endnote id among `existingIds`, a document's endnotes. Ids are
4
+ * unique per document, so the allocator lives for one call: the result
5
+ * depends only on the document, never on what other documents minted.
6
+ */
2
7
  declare const mintEndnoteId: (existingIds: Iterable<number>) => number;
3
8
  //#endregion
4
9
  export { mintEndnoteId };
@@ -1,8 +1,12 @@
1
1
  import { createNumericIdAllocator } from "./numericIdAllocator.js";
2
2
  //#region src/docx/noteIds.ts
3
- let endnoteIds;
3
+ /**
4
+ * The next free endnote id among `existingIds`, a document's endnotes. Ids are
5
+ * unique per document, so the allocator lives for one call: the result
6
+ * depends only on the document, never on what other documents minted.
7
+ */
4
8
  const mintEndnoteId = (existingIds) => {
5
- const allocator = endnoteIds ??= createNumericIdAllocator({
9
+ const allocator = createNumericIdAllocator({
6
10
  space: "endnote",
7
11
  firstId: 1
8
12
  });
@@ -3,6 +3,8 @@ import { document_d_exports } from "../types/document.js";
3
3
  type NoteKind = "footnote" | "endnote";
4
4
  /** The run carrying a note's in-note reference mark, in the note reference style. */
5
5
  declare const noteReferenceMarkRun: (kind: NoteKind) => document_d_exports.Run;
6
+ /** Whether a note already owns its typed marker, including inside tracked content. */
7
+ declare const hasNoteReferenceMark: (kind: NoteKind, content: readonly document_d_exports.BlockContent[]) => boolean;
6
8
  /**
7
9
  * A note's content with its reference mark run at the start of its first
8
10
  * paragraph, added when the note has none.
@@ -12,4 +14,4 @@ declare const withNoteReferenceMark: (kind: NoteKind, content: readonly document
12
14
  declare function createNote(kind: "footnote", id: number, content: readonly document_d_exports.BlockContent[]): document_d_exports.Footnote;
13
15
  declare function createNote(kind: "endnote", id: number, content: readonly document_d_exports.BlockContent[]): document_d_exports.Endnote;
14
16
  //#endregion
15
- export { createNote, noteReferenceMarkRun, withNoteReferenceMark };
17
+ export { createNote, hasNoteReferenceMark, noteReferenceMarkRun, withNoteReferenceMark };
@@ -1,37 +1,38 @@
1
1
  import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
2
+ import { visitDocxParagraphs, visitParagraphRuns } from "./paragraphTraversal.js";
2
3
  //#region src/docx/noteReferenceMark.ts
3
- const MARK_ELEMENT = /<(?:[\w.-]+:)?(?:footnote|endnote)Ref[\s/>]/u;
4
4
  /** The run carrying a note's in-note reference mark, in the note reference style. */
5
5
  const noteReferenceMarkRun = (kind) => ({
6
6
  type: "run",
7
7
  formatting: { styleId: kind === "footnote" ? "FootnoteReference" : "EndnoteReference" },
8
8
  content: [{
9
- type: "preservedXml",
10
- xml: `<w:${kind}Ref/>`,
11
- text: ""
9
+ type: "noteMarker",
10
+ kind
12
11
  }]
13
12
  });
14
- const holdsReferenceMark = (value) => {
15
- if (Array.isArray(value)) return value.some(holdsReferenceMark);
16
- if (typeof value !== "object" || value === null) return false;
17
- const record = value;
18
- if (record["type"] === "preservedXml" && typeof record["xml"] === "string") return MARK_ELEMENT.test(record["xml"]);
19
- return Object.values(record).some(holdsReferenceMark);
13
+ /** Whether a note already owns its typed marker, including inside tracked content. */
14
+ const hasNoteReferenceMark = (kind, content) => {
15
+ let found = false;
16
+ visitDocxParagraphs({ documentBody: { content: [...content] } }, (paragraph) => {
17
+ visitParagraphRuns(paragraph, (run) => {
18
+ if (run.content.some((child) => child.type === "noteMarker" && child.kind === kind)) found = true;
19
+ });
20
+ });
21
+ return found;
20
22
  };
21
23
  /**
22
24
  * A note's content with its reference mark run at the start of its first
23
25
  * paragraph, added when the note has none.
24
26
  */
25
27
  const withNoteReferenceMark = (kind, content) => {
26
- if (holdsReferenceMark(content)) return [...content];
28
+ if (hasNoteReferenceMark(kind, content)) return [...content];
27
29
  const index = content.findIndex((block) => block.type === "paragraph");
28
30
  const mark = noteReferenceMarkRun(kind);
29
31
  if (index === -1) return [{
30
32
  type: "paragraph",
31
33
  content: [mark]
32
34
  }, ...content];
33
- const paragraph = content[index];
34
- return content.map((block, position) => position === index ? cloneParagraphWithPropertySource(paragraph, { content: [mark, ...paragraph.content] }) : block);
35
+ return content.map((block, position) => position === index && block.type === "paragraph" ? cloneParagraphWithPropertySource(block, { content: [mark, ...block.content] }) : block);
35
36
  };
36
37
  function createNote(kind, id, content) {
37
38
  return kind === "footnote" ? {
@@ -45,4 +46,4 @@ function createNote(kind, id, content) {
45
46
  };
46
47
  }
47
48
  //#endregion
48
- export { createNote, noteReferenceMarkRun, withNoteReferenceMark };
49
+ export { createNote, hasNoteReferenceMark, noteReferenceMarkRun, withNoteReferenceMark };
@@ -81,7 +81,9 @@ declare function getBulletCharacter(level: document_d_exports.ListLevel): string
81
81
  * Check if a list level is a bullet (not numbered)
82
82
  */
83
83
  declare function isBulletLevel(level: document_d_exports.ListLevel): boolean;
84
+ /** Whether a numbering definition uses a bullet marker rather than a numeric or hidden one. */
85
+ declare const numberingLevelUsesBulletMarker: (level: Pick<document_d_exports.ListLevel, "numFmt"> | null | undefined) => boolean;
84
86
  /** Whether a numbering level reserves horizontal space for a visible marker. */
85
87
  declare const numberingLevelHasMarkerSlot: (level: Pick<document_d_exports.ListLevel, "numFmt">) => boolean;
86
88
  //#endregion
87
- export { NumberingMap, computeListRendering, counterFormatOf, createNumberingMap, customNumberFormatPadWidth, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerAlignmentForLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
89
+ export { NumberingMap, computeListRendering, counterFormatOf, createNumberingMap, customNumberFormatPadWidth, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerAlignmentForLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, numberingLevelUsesBulletMarker, padDecimal, parseNumbering, renderListMarker };
@@ -6,7 +6,20 @@ import { parseParagraphProperties } from "./paragraphProperties.js";
6
6
  import { LevelSuffixSchema, NumberFormatSchema, ParagraphAlignmentSchema, narrowEnum } from "./parserEnums.js";
7
7
  import { RUN_PROPERTY_OWNERS, parseRunProperties } from "./runParser.js";
8
8
  import { findChild, getAttribute, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, parseXmlDocument } from "./xmlParser.js";
9
+ import { isNumberingLevel } from "@stll/docx-core/model";
9
10
  //#region src/docx/numberingParser.ts
11
+ /**
12
+ * Numbering/List Parser for DOCX
13
+ *
14
+ * Parses numbering.xml to extract:
15
+ * - Abstract numbering definitions (templates with levels)
16
+ * - Numbering instances (concrete references with optional overrides)
17
+ *
18
+ * OOXML Structure:
19
+ * - w:abstractNum - Template definitions with 9 levels (0-8)
20
+ * - w:num - Instances that reference abstractNum and can override levels
21
+ * - w:lvl - Level definition with start, format, text pattern, etc.
22
+ */
10
23
  /** `w:abstractNum` reads its id; everything else it carries is the remainder's. */
11
24
  const MODELLED_ABSTRACT_NUM_ATTRIBUTES = /* @__PURE__ */ new Set(["abstractNumId"]);
12
25
  /** `w:lvl` reads the level it defines and Word's two template markers. */
@@ -117,7 +130,6 @@ function parseAbstractNumbering(element) {
117
130
  return abstractNum;
118
131
  }
119
132
  /** ECMA-376 §17.9.1: a `w:abstractNum` defines levels 0 through 8 and no more. */
120
- const MAX_LIST_LEVEL = 8;
121
133
  const MULTI_LEVEL_TYPES = [
122
134
  "hybridMultilevel",
123
135
  "multilevel",
@@ -392,7 +404,7 @@ function createNumberingMap(definitions) {
392
404
  return {
393
405
  definitions,
394
406
  getLevel(numId, ilvl) {
395
- if (ilvl < 0 || ilvl > MAX_LIST_LEVEL) return null;
407
+ if (!isNumberingLevel(ilvl)) return null;
396
408
  const num = numMap.get(numId);
397
409
  if (!num) return null;
398
410
  if (num.levelOverrides) {
@@ -524,7 +536,9 @@ function getBulletCharacter(level) {
524
536
  function isBulletLevel(level) {
525
537
  return level.numFmt === "bullet" || level.numFmt === "none";
526
538
  }
539
+ /** Whether a numbering definition uses a bullet marker rather than a numeric or hidden one. */
540
+ const numberingLevelUsesBulletMarker = (level) => level?.numFmt === "bullet";
527
541
  /** Whether a numbering level reserves horizontal space for a visible marker. */
528
542
  const numberingLevelHasMarkerSlot = (level) => level.numFmt !== "none";
529
543
  //#endregion
530
- export { computeListRendering, counterFormatOf, createNumberingMap, customNumberFormatPadWidth, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerAlignmentForLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
544
+ export { computeListRendering, counterFormatOf, createNumberingMap, customNumberFormatPadWidth, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerAlignmentForLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, numberingLevelUsesBulletMarker, padDecimal, parseNumbering, renderListMarker };
@@ -1,5 +1,7 @@
1
1
  import JSZip from "jszip";
2
2
  //#region src/docx/packageParts.d.ts
3
+ /** Resolve an internal URI reference once, before comparing it with ZIP entries. */
4
+ declare const resolvePackageRelationshipTarget: (target: string, relsPath: string) => string | undefined;
3
5
  /**
4
6
  * Whether an entry path would escape the package when a host writes the archive
5
7
  * to disk: absolute, backslash-separated, or climbing out through `..`. This is
@@ -76,4 +78,4 @@ type PackageIntegrityReport = {
76
78
  */
77
79
  declare const checkPackageIntegrity: (zip: JSZip) => Promise<PackageIntegrityReport>;
78
80
  //#endregion
79
- export { PackageIntegrityReport, PackageReferenceRepair, checkPackageIntegrity, isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries };
81
+ export { PackageIntegrityReport, PackageReferenceRepair, checkPackageIntegrity, isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries, resolvePackageRelationshipTarget };