@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
@@ -1,4 +1,6 @@
1
1
  import { isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
2
+ import { cloneDocumentWithParagraphPropertySources } from "../docx/paragraphPropertySource.js";
3
+ import { visitDocxParagraphs, visitParagraphRuns } from "../docx/paragraphTraversal.js";
2
4
  import { proseDocToBlocks } from "../prosemirror/conversion/fromProseDoc.js";
3
5
  import { footnoteToProseDoc } from "../prosemirror/conversion/toProseDoc.js";
4
6
  import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js";
@@ -9,7 +11,9 @@ import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNu
9
11
  import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
10
12
  import { schema } from "../prosemirror/schema/index.js";
11
13
  import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
12
- import { EditorState } from "prosemirror-state";
14
+ import { createCanonicalStoryEditor } from "./canonicalStoryEditor.js";
15
+ import { OP_STORIES } from "@stll/docx-core/ops";
16
+ import { EditorState, TextSelection } from "prosemirror-state";
13
17
  import { EditorView } from "prosemirror-view";
14
18
  //#region src/controller/noteEditorManager.ts
15
19
  /** Framework-neutral lifecycle owner for editable footnote and endnote stories. */
@@ -28,12 +32,7 @@ const enumerateDocumentNoteStories = (document) => [...(document?.package.footno
28
32
  const resolveNote = (document, story) => {
29
33
  return (story.kind === "footnote" ? document?.package.footnotes : document?.package.endnotes)?.find(({ id }) => id === story.noteId) ?? null;
30
34
  };
31
- const noteToProseDocument = (note, styles, theme) => {
32
- const options = {};
33
- if (styles) options.styles = styles;
34
- if (theme !== void 0) options.theme = theme;
35
- return footnoteToProseDoc(note.content, options);
36
- };
35
+ const noteToProseDocument = (note, options) => footnoteToProseDoc(note.content, options);
37
36
  const buildInitialState = (document, styles, numbering, manager, externalPlugins) => {
38
37
  return ensureBaseDirectionInState(ensureParaIdsInState(EditorState.create({
39
38
  doc: document,
@@ -46,6 +45,31 @@ const buildInitialState = (document, styles, numbering, manager, externalPlugins
46
45
  ]
47
46
  })));
48
47
  };
48
+ /** Automatic note marks have no PM width; retain their authored runs on default writeback. */
49
+ const preserveNoteMarkers = ({ source, projected }) => {
50
+ const marks = [];
51
+ visitDocxParagraphs({ documentBody: { content: source } }, (paragraph) => visitParagraphRuns(paragraph, (run) => {
52
+ const content = run.content.filter((child) => child.type === "noteMarker");
53
+ if (content.length > 0) marks.push({
54
+ ...run,
55
+ content
56
+ });
57
+ }));
58
+ if (marks.length === 0) return projected;
59
+ let hasMarker = false;
60
+ visitDocxParagraphs({ documentBody: { content: projected } }, (paragraph) => visitParagraphRuns(paragraph, (run) => {
61
+ if (run.content.some((child) => child.type === "noteMarker")) hasMarker = true;
62
+ }));
63
+ if (hasMarker) return projected;
64
+ const owned = cloneDocumentWithParagraphPropertySources({ package: { document: { content: projected } } }).package.document.content;
65
+ const paragraphs = [];
66
+ visitDocxParagraphs({ documentBody: { content: owned } }, (paragraph) => {
67
+ paragraphs.push(paragraph);
68
+ });
69
+ const first = paragraphs.at(0);
70
+ if (first) first.content.unshift(...marks);
71
+ return owned;
72
+ };
49
73
  const createNoteEditorManager = (deps) => {
50
74
  const mounted = /* @__PURE__ */ new Map();
51
75
  let active = null;
@@ -66,10 +90,16 @@ const createNoteEditorManager = (deps) => {
66
90
  const sync = () => {
67
91
  const host = deps.getHost();
68
92
  if (!host) return;
69
- const document = deps.getDocument();
93
+ if (deps.getExperimentalSession?.() === "canonical" && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
94
+ const document = deps.getExperimentalSession?.() === "canonical" ? deps.getCanonicalApi?.()?.getCanonicalDocument() ?? deps.getDocument() : deps.getDocument();
70
95
  const styles = deps.getStyles();
71
96
  const theme = deps.getTheme();
72
97
  const numbering = document?.package.numbering;
98
+ const proseOptions = {
99
+ ...styles ? { styles } : {},
100
+ ...theme !== void 0 ? { theme } : {},
101
+ ...numbering !== void 0 ? { numbering } : {}
102
+ };
73
103
  const externalPlugins = deps.getPlugins?.() ?? EMPTY_PLUGINS;
74
104
  const wanted = new Map(enumerateDocumentNoteStories(document).map((story) => [storyMapKey(story), story]));
75
105
  const activeKey = active ? storyMapKey(active) : null;
@@ -85,7 +115,33 @@ const createNoteEditorManager = (deps) => {
85
115
  const existing = mounted.get(key);
86
116
  if (existing) {
87
117
  if (existing.mountNode.parentElement !== host) host.append(existing.mountNode);
88
- const nextProseDocument = noteToProseDocument(note, styles, theme);
118
+ if (deps.getExperimentalSession?.() === "canonical") {
119
+ const projected = deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
120
+ kind: storyKey.kind,
121
+ id: storyKey.noteId
122
+ });
123
+ if (projected && !existing.view.state.doc.eq(projected)) {
124
+ const transaction = existing.view.state.tr.replaceWith(0, existing.view.state.doc.content.size, projected.content);
125
+ const restored = deps.getCanonicalApi?.()?.getCanonicalStorySelection({
126
+ kind: storyKey.kind,
127
+ id: storyKey.noteId
128
+ });
129
+ const anchor = Math.min(existing.view.state.selection.anchor, projected.content.size);
130
+ transaction.setSelection(restored ? TextSelection.create(transaction.doc, restored.anchor, restored.head) : TextSelection.near(transaction.doc.resolve(anchor)));
131
+ transaction.setMeta("addToHistory", false);
132
+ existing.view.updateState(existing.view.state.apply(transaction));
133
+ }
134
+ existing.appliedNote = note;
135
+ existing.appliedContent = note.content;
136
+ existing.appliedProseDocument = existing.view.state.doc;
137
+ existing.appliedStyles = styles;
138
+ existing.appliedTheme = theme;
139
+ existing.appliedNumbering = numbering;
140
+ existing.appliedPlugins = externalPlugins;
141
+ existing.dirty = false;
142
+ continue;
143
+ }
144
+ const nextProseDocument = noteToProseDocument(note, proseOptions);
89
145
  const contextIsCurrent = existing.appliedStyles === styles && existing.appliedTheme === theme && existing.appliedNumbering === numbering && existing.appliedPlugins === externalPlugins;
90
146
  if ((existing.appliedNote === note && existing.appliedContent === note.content || existing.appliedProseDocument.eq(nextProseDocument)) && contextIsCurrent) {
91
147
  existing.appliedNote = note;
@@ -111,10 +167,40 @@ const createNoteEditorManager = (deps) => {
111
167
  mountNode.dataset["noteKind"] = storyKey.kind;
112
168
  mountNode.dataset["noteId"] = String(storyKey.noteId);
113
169
  host.append(mountNode);
114
- const proseDocument = noteToProseDocument(note, styles, theme);
170
+ const proseDocument = noteToProseDocument(note, proseOptions);
171
+ const canonical = createCanonicalStoryEditor({
172
+ story: {
173
+ kind: storyKey.kind,
174
+ id: storyKey.noteId
175
+ },
176
+ getView: () => view,
177
+ getApi: () => deps.getCanonicalApi?.() ?? null,
178
+ enabled: () => deps.getExperimentalSession?.() === "canonical",
179
+ onRefusal: deps.onSessionRefusal,
180
+ onSelectionChange: () => deps.onTransaction?.({
181
+ ...storyKey,
182
+ view,
183
+ docChanged: false,
184
+ selectionChanged: true
185
+ })
186
+ });
187
+ const canonicalProjection = deps.getExperimentalSession?.() === "canonical" ? deps.getCanonicalApi?.()?.getCanonicalStoryProjection({
188
+ kind: storyKey.kind,
189
+ id: storyKey.noteId
190
+ }) : null;
191
+ if (deps.getExperimentalSession?.() === "canonical" && !canonicalProjection) {
192
+ manager.destroy();
193
+ mountNode.remove();
194
+ continue;
195
+ }
115
196
  const view = (deps.createView ?? createEditorView)(mountNode, {
116
- state: buildInitialState(proseDocument, styles, numbering, manager, externalPlugins),
197
+ ...canonical.props,
198
+ state: canonicalProjection ? EditorState.create({
199
+ doc: canonicalProjection,
200
+ plugins: [createDocumentStylesPlugin(styles), createDocumentNumberingPlugin(numbering)]
201
+ }) : buildInitialState(proseDocument, styles, numbering, manager, externalPlugins),
117
202
  dispatchTransaction(transaction) {
203
+ if (canonical.dispatch(transaction)) return;
118
204
  view.updateState(view.state.apply(transaction));
119
205
  const mountedStory = mounted.get(key);
120
206
  if (mountedStory && transaction.docChanged) mountedStory.dirty = true;
@@ -160,6 +246,7 @@ const createNoteEditorManager = (deps) => {
160
246
  getView: (story) => mounted.get(storyMapKey(story))?.view ?? null,
161
247
  listStories: () => enumerateDocumentNoteStories(deps.getDocument()),
162
248
  snapshotDocument: (document) => {
249
+ if (deps.getExperimentalSession?.() === "canonical") return deps.getCanonicalApi?.()?.getCanonicalDocument() ?? document;
163
250
  let footnotes = document.package.footnotes;
164
251
  let endnotes = document.package.endnotes;
165
252
  let footnotesChanged = false;
@@ -169,6 +256,11 @@ const createNoteEditorManager = (deps) => {
169
256
  if (!story.dirty) continue;
170
257
  const lists = storyListNumbering(story.view.state, numbering);
171
258
  numbering = lists.numbering;
259
+ const proseOptions = {
260
+ ...story.appliedStyles ? { styles: story.appliedStyles } : {},
261
+ ...story.appliedTheme !== void 0 ? { theme: story.appliedTheme } : {},
262
+ ...numbering !== void 0 ? { numbering } : {}
263
+ };
172
264
  if (story.note.kind === "footnote") {
173
265
  const index = footnotes?.findIndex(({ id }) => id === story.note.noteId) ?? -1;
174
266
  const current = index === -1 ? null : footnotes?.at(index);
@@ -179,12 +271,15 @@ const createNoteEditorManager = (deps) => {
179
271
  }
180
272
  const updated = {
181
273
  ...current,
182
- content: proseDocToBlocks(lists.doc, current.content, document.package.styles)
274
+ content: preserveNoteMarkers({
275
+ source: current.content,
276
+ projected: proseDocToBlocks(lists.doc, current.content, document.package.styles)
277
+ })
183
278
  };
184
279
  footnotes[index] = updated;
185
280
  story.appliedNote = updated;
186
281
  story.appliedContent = updated.content;
187
- story.appliedProseDocument = noteToProseDocument(updated, story.appliedStyles, story.appliedTheme);
282
+ story.appliedProseDocument = noteToProseDocument(updated, proseOptions);
188
283
  continue;
189
284
  }
190
285
  const index = endnotes?.findIndex(({ id }) => id === story.note.noteId) ?? -1;
@@ -196,12 +291,15 @@ const createNoteEditorManager = (deps) => {
196
291
  }
197
292
  const updated = {
198
293
  ...current,
199
- content: proseDocToBlocks(lists.doc, current.content, document.package.styles)
294
+ content: preserveNoteMarkers({
295
+ source: current.content,
296
+ projected: proseDocToBlocks(lists.doc, current.content, document.package.styles)
297
+ })
200
298
  };
201
299
  endnotes[index] = updated;
202
300
  story.appliedNote = updated;
203
301
  story.appliedContent = updated.content;
204
- story.appliedProseDocument = noteToProseDocument(updated, story.appliedStyles, story.appliedTheme);
302
+ story.appliedProseDocument = noteToProseDocument(updated, proseOptions);
205
303
  }
206
304
  if (!footnotesChanged && !endnotesChanged) return document;
207
305
  return {
@@ -55,6 +55,7 @@ const attributeRemainder = ({ element, modelled }) => {
55
55
  const name = getLocalName(spelling);
56
56
  if (modelled.has(name)) continue;
57
57
  const namespace = resolveAttributeNamespaceUri(element, spelling);
58
+ if (namespace === OOXML_NAMESPACES.mc.uri && name === "Ignorable") continue;
58
59
  if (namespace !== void 0 && !CANONICAL_PREFIX.has(namespace)) continue;
59
60
  remainder.push({
60
61
  ...namespace === void 0 ? {} : { namespace },
@@ -0,0 +1,11 @@
1
+ import { DocxInput } from "../utils/docxInput.js";
2
+ //#region src/docx/canonicalSessionInput.d.ts
3
+ declare const CanonicalDocxInputError_base: import("better-result").TaggedErrorClass<"CanonicalDocxInputError">;
4
+ declare class CanonicalDocxInputError extends CanonicalDocxInputError_base<{
5
+ message: string;
6
+ cause?: unknown;
7
+ }> {}
8
+ /** Canonical seeding supports plaintext packages; refuse encryption before ZIP normalization. */
9
+ declare const prepareCanonicalDocxInput: (input: DocxInput) => Promise<import("better-result").Err<ArrayBuffer, CanonicalDocxInputError> | import("better-result").Ok<Uint8Array<ArrayBufferLike>, CanonicalDocxInputError>>;
10
+ //#endregion
11
+ export { CanonicalDocxInputError, prepareCanonicalDocxInput };
@@ -0,0 +1,27 @@
1
+ import { toArrayBuffer } from "../utils/docxInput.js";
2
+ import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./encryption/containerFormat.js";
3
+ import { ensureParaIds } from "./ensureParaIds.js";
4
+ import { Result, TaggedError } from "better-result";
5
+ //#region src/docx/canonicalSessionInput.ts
6
+ var CanonicalDocxInputError = class extends TaggedError("CanonicalDocxInputError") {};
7
+ /** Canonical seeding supports plaintext packages; refuse encryption before ZIP normalization. */
8
+ const prepareCanonicalDocxInput = async (input) => {
9
+ const bytes = await Result.tryPromise({
10
+ try: () => toArrayBuffer(input),
11
+ catch: (cause) => new CanonicalDocxInputError({
12
+ message: "Cannot read canonical document bytes.",
13
+ cause
14
+ })
15
+ });
16
+ if (bytes.isErr()) return bytes;
17
+ if (detectDocxContainerType(bytes.value) === DOCX_CONTAINER_TYPES.CFB) return Result.err(new CanonicalDocxInputError({ message: "Password-protected documents are unavailable in the experimental canonical session." }));
18
+ return Result.tryPromise({
19
+ try: async () => (await ensureParaIds(bytes.value)).docx,
20
+ catch: (cause) => new CanonicalDocxInputError({
21
+ message: cause instanceof Error ? cause.message : "Canonical document normalization failed.",
22
+ cause
23
+ })
24
+ });
25
+ };
26
+ //#endregion
27
+ export { CanonicalDocxInputError, prepareCanonicalDocxInput };
@@ -0,0 +1,10 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/canonicalStoryRepack.d.ts
3
+ type CanonicalStoryRepackOptions<T> = {
4
+ document: document_d_exports.Document;
5
+ repack: () => Promise<T>;
6
+ };
7
+ /** Authorize only references missing from the committed canonical snapshot, with exact multiplicity. */
8
+ declare const repackWithCanonicalStoryRemovals: <T>({ document, repack }: CanonicalStoryRepackOptions<T>) => Promise<T>;
9
+ //#endregion
10
+ export { repackWithCanonicalStoryRemovals };
@@ -0,0 +1,41 @@
1
+ import { withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
2
+ import { readDocumentSectionFacts } from "./documentSectionFacts.js";
3
+ import { serializeDocument } from "./serializer/documentSerializer.js";
4
+ import JSZip from "jszip";
5
+ //#region src/docx/canonicalStoryRepack.ts
6
+ /** Canonical snapshots own explicit section references; the PM projection does not. */
7
+ const referenceKey = ({ element, type, rId }) => `${element}:${type}:${rId}`;
8
+ /** Authorize only references missing from the committed canonical snapshot, with exact multiplicity. */
9
+ const repackWithCanonicalStoryRemovals = async ({ document, repack }) => {
10
+ if (!document.originalBuffer) return repack();
11
+ const xml = await (await JSZip.loadAsync(document.originalBuffer)).file("word/document.xml")?.async("text");
12
+ if (!xml) return repack();
13
+ const before = readDocumentSectionFacts(xml);
14
+ const after = readDocumentSectionFacts(serializeDocument(document));
15
+ const remaining = /* @__PURE__ */ new Map();
16
+ for (const reference of after.headerFooterReferences) {
17
+ const key = referenceKey(reference);
18
+ remaining.set(key, (remaining.get(key) ?? 0) + 1);
19
+ }
20
+ const removedReferences = [];
21
+ for (const reference of before.headerFooterReferences) {
22
+ const key = referenceKey(reference);
23
+ const count = remaining.get(key) ?? 0;
24
+ if (count > 0) {
25
+ remaining.set(key, count - 1);
26
+ continue;
27
+ }
28
+ removedReferences.push({
29
+ part: reference.element === "headerReference" ? "header" : "footer",
30
+ type: reference.type,
31
+ relationshipId: reference.rId
32
+ });
33
+ }
34
+ return withSectionReferenceResolution({
35
+ document,
36
+ removedReferences,
37
+ repack
38
+ });
39
+ };
40
+ //#endregion
41
+ export { repackWithCanonicalStoryRemovals };
@@ -0,0 +1,8 @@
1
+ //#region src/docx/contentTypeRegistry.d.ts
2
+ type ContentTypePart = {
3
+ partName: string;
4
+ contentType: string;
5
+ };
6
+ declare const registerContentTypeParts: (xml: string, parts: readonly ContentTypePart[]) => string;
7
+ //#endregion
8
+ export { registerContentTypeParts };
@@ -0,0 +1,64 @@
1
+ import { resolvePackageRelationshipTarget } from "./packageParts.js";
2
+ import { spliceXml } from "./selectiveXmlPatch.js";
3
+ import { getAttribute, getLocalName, getNamespacePrefix, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
4
+ import { panic } from "better-result";
5
+ import { escapeXmlAttribute } from "@stll/docx-core";
6
+ import { OOXML_NS } from "@stll/docx-utils";
7
+ //#region src/docx/contentTypeRegistry.ts
8
+ /** Register a package part without rewriting authored content-type entries. */
9
+ const registerContentTypeParts = (xml, parts) => {
10
+ const root = parseXmlDocument(xml);
11
+ if (root?.name === void 0 || getLocalName(root.name) !== "Types" || getNamespaceUri(root) !== OOXML_NS.ct) return panic("The package content-type registry must have a valid Types root.");
12
+ const overrides = /* @__PURE__ */ new Map();
13
+ const defaults = /* @__PURE__ */ new Map();
14
+ for (const element of root.elements ?? []) {
15
+ if (getNamespaceUri(element) !== OOXML_NS.ct) continue;
16
+ const type = getAttribute(element, null, "ContentType");
17
+ if (type === null) continue;
18
+ if (getLocalName(element.name) === "Override") {
19
+ const name = getAttribute(element, null, "PartName");
20
+ const path = name === null ? void 0 : resolvePackageRelationshipTarget(name, "_rels/.rels");
21
+ if (path !== void 0) overrides.set(path, type);
22
+ }
23
+ if (getLocalName(element.name) === "Default") {
24
+ const extension = getAttribute(element, null, "Extension");
25
+ if (extension !== null) defaults.set(extension.toLowerCase(), type);
26
+ }
27
+ }
28
+ const prefix = getNamespacePrefix(root.name);
29
+ const name = prefix ? `${prefix}:Override` : "Override";
30
+ const additions = [];
31
+ for (const part of parts) {
32
+ const path = resolvePackageRelationshipTarget(part.partName, "_rels/.rels");
33
+ if (path === void 0) return panic("A registered part must have a safe package URI.");
34
+ const type = overrides.get(path);
35
+ if (type !== void 0) {
36
+ if (type !== part.contentType) return panic("A part's registered content type cannot change.");
37
+ continue;
38
+ }
39
+ const extension = path.slice(path.lastIndexOf(".") + 1);
40
+ if (defaults.get(extension) === part.contentType) continue;
41
+ additions.push(`<${name} PartName="${escapeXmlAttribute(part.partName)}" ContentType="${escapeXmlAttribute(part.contentType)}"/>`);
42
+ overrides.set(path, part.contentType);
43
+ }
44
+ if (additions.length === 0) return xml;
45
+ for (const token of xml.matchAll(/<!--[\s\S]*?-->|<\?[\s\S]*?\?>|<!\[CDATA\[[\s\S]*?\]\]>|<(?:"[^"]*"|'[^']*'|[^'">])*>/gu)) {
46
+ const markup = token[0];
47
+ if (markup.startsWith("</") && markup.slice(2, -1).trim() === root.name) return spliceXml(xml, [{
48
+ start: token.index,
49
+ end: token.index,
50
+ newXml: additions.join("")
51
+ }]) ?? panic("A registry insertion must preserve XML ranges.");
52
+ if (markup.startsWith(`<${root.name}`) && /[\s/>]/u.test(markup.charAt(root.name.length + 1)) && markup.endsWith("/>")) {
53
+ const end = token.index + markup.length - 2;
54
+ return spliceXml(xml, [{
55
+ start: end,
56
+ end: end + 2,
57
+ newXml: ">" + additions.join("") + `</${root.name}>`
58
+ }]) ?? panic("A registry insertion must preserve XML ranges.");
59
+ }
60
+ }
61
+ return panic("A content-type registry needs a closing root boundary.");
62
+ };
63
+ //#endregion
64
+ export { registerContentTypeParts };
@@ -1,8 +1,14 @@
1
1
  //#region src/docx/ensureParaIds.d.ts
2
+ declare const ENSURE_PARA_IDS_REASONS: {
3
+ readonly NORMALIZATION_FAILED: "normalization-failed";
4
+ readonly NAMESPACE_INVALID: "namespace-invalid";
5
+ readonly SIGNED_PACKAGE: "signed-package";
6
+ };
2
7
  declare const EnsureParaIdsError_base: import("better-result").TaggedErrorClass<"EnsureParaIdsError">;
3
8
  /** A malformed or unsupported package prevented paragraph-ID normalization. */
4
9
  declare class EnsureParaIdsError extends EnsureParaIdsError_base<{
5
10
  message: string;
11
+ reason: (typeof ENSURE_PARA_IDS_REASONS)[keyof typeof ENSURE_PARA_IDS_REASONS];
6
12
  cause?: unknown;
7
13
  }> {}
8
14
  /** Counts and normalized bytes returned by {@link ensureParaIds}. */
@@ -35,4 +41,4 @@ type EnsureParaIdsOptions = {
35
41
  */
36
42
  declare const ensureParaIds: (docx: Uint8Array | ArrayBuffer, options?: EnsureParaIdsOptions) => Promise<EnsureParaIdsResult>;
37
43
  //#endregion
38
- export { EnsureParaIdsError, EnsureParaIdsOptions, EnsureParaIdsResult, ensureParaIds };
44
+ export { ENSURE_PARA_IDS_REASONS, EnsureParaIdsError, EnsureParaIdsOptions, EnsureParaIdsResult, ensureParaIds };