@stll/folio-core 0.55.0 → 0.57.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 (225) hide show
  1. package/dist/ai-edits/apply.js +214 -52
  2. package/dist/ai-edits/batch-claims.d.ts +3 -1
  3. package/dist/ai-edits/batch-claims.js +25 -1
  4. package/dist/ai-edits/headless.js +2 -2
  5. package/dist/ai-edits/table-row-column-mutations.js +17 -3
  6. package/dist/ai-edits/types.d.ts +7 -1
  7. package/dist/ai-suggestions/apply.js +20 -1
  8. package/dist/compare/compare.js +1 -0
  9. package/dist/compare/inline-atoms.js +40 -16
  10. package/dist/compare/inline-provenance.js +12 -5
  11. package/dist/compare/reproducible-package.d.ts +0 -17
  12. package/dist/compare/reproducible-package.js +12 -18
  13. package/dist/compat/eigenpal.d.ts +2 -1
  14. package/dist/compat/eigenpal.js +2 -1
  15. package/dist/controller/canonicalClipboard.d.ts +23 -0
  16. package/dist/controller/canonicalClipboard.js +482 -0
  17. package/dist/controller/canonicalClipboardResources.d.ts +21 -0
  18. package/dist/controller/canonicalClipboardResources.js +382 -0
  19. package/dist/controller/canonicalComments.d.ts +41 -0
  20. package/dist/controller/canonicalComments.js +152 -0
  21. package/dist/controller/canonicalComposition.d.ts +24 -3
  22. package/dist/controller/canonicalComposition.js +208 -48
  23. package/dist/controller/canonicalInlineProjection.d.ts +118 -0
  24. package/dist/controller/canonicalInlineProjection.js +476 -0
  25. package/dist/controller/canonicalInput.d.ts +13 -4
  26. package/dist/controller/canonicalInput.js +62 -14
  27. package/dist/controller/canonicalInputTimer.d.ts +16 -0
  28. package/dist/controller/canonicalInputTimer.js +34 -0
  29. package/dist/controller/canonicalPublicOperations.d.ts +48 -0
  30. package/dist/controller/canonicalPublicOperations.js +648 -0
  31. package/dist/controller/canonicalSelection.d.ts +18 -0
  32. package/dist/controller/canonicalSelection.js +32 -0
  33. package/dist/controller/canonicalSession.d.ts +27 -6
  34. package/dist/controller/canonicalSession.js +298 -116
  35. package/dist/controller/canonicalStoryEditor.d.ts +7 -3
  36. package/dist/controller/canonicalStoryEditor.js +27 -2
  37. package/dist/controller/canonicalStructure.js +57 -4
  38. package/dist/controller/canonicalTocSelection.d.ts +11 -0
  39. package/dist/controller/canonicalTocSelection.js +43 -0
  40. package/dist/controller/folioEditor.d.ts +2 -9
  41. package/dist/controller/folioEditor.js +18 -4
  42. package/dist/controller/headerFooterEditorManager.d.ts +2 -1
  43. package/dist/controller/headerFooterEditorManager.js +26 -11
  44. package/dist/controller/hiddenEditorApi.d.ts +24 -3
  45. package/dist/controller/hiddenEditorApi.js +8 -0
  46. package/dist/controller/hiddenEditorManager.d.ts +5 -1
  47. package/dist/controller/hiddenEditorManager.js +378 -43
  48. package/dist/controller/noteEditorManager.d.ts +2 -1
  49. package/dist/controller/noteEditorManager.js +29 -13
  50. package/dist/document-operations.d.ts +8 -1
  51. package/dist/document-operations.js +6 -4
  52. package/dist/docx/blockPlainText.d.ts +1 -1
  53. package/dist/docx/blockPlainText.js +11 -3
  54. package/dist/docx/builtInStyles.js +13 -6
  55. package/dist/docx/canonicalResourceSave.d.ts +16 -0
  56. package/dist/docx/canonicalResourceSave.js +27 -0
  57. package/dist/docx/canonicalSave.d.ts +29 -0
  58. package/dist/docx/canonicalSave.js +101 -0
  59. package/dist/docx/canonicalStoryRepack.d.ts +1 -1
  60. package/dist/docx/canonicalStoryRepack.js +38 -5
  61. package/dist/docx/containerChildren.gen.d.ts +3 -1
  62. package/dist/docx/containerChildren.gen.js +33 -1
  63. package/dist/docx/documentParser.d.ts +1 -13
  64. package/dist/docx/documentParser.js +1 -47
  65. package/dist/docx/ensureParaIds.js +10 -4
  66. package/dist/docx/fieldParser.js +14 -2
  67. package/dist/docx/footnoteParser.d.ts +1 -11
  68. package/dist/docx/footnoteParser.js +1 -16
  69. package/dist/docx/headerFooterParser.d.ts +1 -9
  70. package/dist/docx/headerFooterParser.js +1 -12
  71. package/dist/docx/headerFooterVerbatim.d.ts +26 -1
  72. package/dist/docx/headerFooterVerbatim.js +81 -3
  73. package/dist/docx/hyperlinkParser.d.ts +1 -1
  74. package/dist/docx/hyperlinkParser.js +20 -1
  75. package/dist/docx/imageParser.d.ts +13 -1
  76. package/dist/docx/imageParser.js +20 -11
  77. package/dist/docx/inlineWrapperContent.d.ts +4 -4
  78. package/dist/docx/inlineWrapperContent.js +4 -4
  79. package/dist/docx/linkedSdtPreservation.d.ts +10 -0
  80. package/dist/docx/linkedSdtPreservation.js +37 -0
  81. package/dist/docx/metadataPrivacy.js +6 -1
  82. package/dist/docx/packageParts.js +9 -3
  83. package/dist/docx/paragraphParser.js +88 -67
  84. package/dist/docx/paragraphPropertySource.d.ts +22 -1
  85. package/dist/docx/paragraphPropertySource.js +109 -2
  86. package/dist/docx/parser.js +2 -1
  87. package/dist/docx/preservedRunContent.js +2 -1
  88. package/dist/docx/relsParser.js +26 -2
  89. package/dist/docx/removeHeaderFooterParts.js +17 -6
  90. package/dist/docx/rezip.d.ts +49 -5
  91. package/dist/docx/rezip.js +754 -235
  92. package/dist/docx/saveDiagnostics.d.ts +11 -0
  93. package/dist/docx/selectiveSave.d.ts +2 -1
  94. package/dist/docx/selectiveSave.js +42 -11
  95. package/dist/docx/selectiveXmlPatch.js +7 -5
  96. package/dist/docx/serializer/commentSerializer.d.ts +4 -1
  97. package/dist/docx/serializer/commentSerializer.js +7 -3
  98. package/dist/docx/serializer/documentSerializer.d.ts +9 -2
  99. package/dist/docx/serializer/documentSerializer.js +29 -10
  100. package/dist/docx/serializer/paragraphSerializer.js +207 -94
  101. package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
  102. package/dist/docx/server/materializeYjsDocx.js +14 -6
  103. package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
  104. package/dist/docx/storyBlockReplay.d.ts +21 -1
  105. package/dist/docx/storyBlockReplay.js +113 -9
  106. package/dist/docx/storyPlainText.d.ts +20 -0
  107. package/dist/docx/storyPlainText.js +23 -0
  108. package/dist/docx/structuralXmlPatch.js +10 -22
  109. package/dist/docx/styleParser.d.ts +5 -1
  110. package/dist/docx/styleParser.js +1 -1
  111. package/dist/docx/tableParser.d.ts +1 -8
  112. package/dist/docx/tableParser.js +1 -28
  113. package/dist/docx/textBoxParser.d.ts +1 -5
  114. package/dist/docx/textBoxParser.js +1 -20
  115. package/dist/docx/unzip.js +6 -1
  116. package/dist/i18n/messages/ar.gen.js +4 -0
  117. package/dist/i18n/messages/catalogs.gen.d.ts +68 -0
  118. package/dist/i18n/messages/catalogs.gen.js +68 -0
  119. package/dist/i18n/messages/cs.gen.js +4 -0
  120. package/dist/i18n/messages/de.gen.js +4 -0
  121. package/dist/i18n/messages/en.gen.js +4 -0
  122. package/dist/i18n/messages/es.gen.js +4 -0
  123. package/dist/i18n/messages/et.gen.js +4 -0
  124. package/dist/i18n/messages/fr.gen.js +4 -0
  125. package/dist/i18n/messages/he.gen.js +4 -0
  126. package/dist/i18n/messages/hi.gen.js +4 -0
  127. package/dist/i18n/messages/hu.gen.js +4 -0
  128. package/dist/i18n/messages/lt.gen.js +4 -0
  129. package/dist/i18n/messages/lv.gen.js +4 -0
  130. package/dist/i18n/messages/messages.gen.d.ts +4 -0
  131. package/dist/i18n/messages/pl.gen.js +4 -0
  132. package/dist/i18n/messages/pt-BR.gen.js +4 -0
  133. package/dist/i18n/messages/sk.gen.js +4 -0
  134. package/dist/i18n/messages/tr.gen.js +4 -0
  135. package/dist/i18n/messages/zh-CN.gen.js +4 -0
  136. package/dist/index.d.ts +2 -1
  137. package/dist/index.js +2 -1
  138. package/dist/internal/acceptedBlockProjection.d.ts +12 -0
  139. package/dist/internal/acceptedBlockProjection.js +45 -0
  140. package/dist/internal/revisionResolutionInline.js +25 -3
  141. package/dist/managers/DocumentLoaderManager.js +2 -1
  142. package/dist/markdown/escape.d.ts +3 -1
  143. package/dist/markdown/escape.js +9 -2
  144. package/dist/markdown/renderBlock.js +5 -120
  145. package/dist/markdown/renderParagraph.js +4 -2
  146. package/dist/markdown/renderRuns.d.ts +7 -1
  147. package/dist/markdown/renderRuns.js +33 -24
  148. package/dist/panel-layout.d.ts +11 -9
  149. package/dist/panel-layout.js +13 -9
  150. package/dist/prosemirror/addTrackedDeletionMark.d.ts +18 -0
  151. package/dist/prosemirror/addTrackedDeletionMark.js +87 -0
  152. package/dist/prosemirror/attrs/index.js +5 -0
  153. package/dist/prosemirror/canonicalCommands.d.ts +28 -4
  154. package/dist/prosemirror/canonicalCommands.js +11 -4
  155. package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
  156. package/dist/prosemirror/canonicalSelectionRange.js +15 -0
  157. package/dist/prosemirror/clipboardIntent.d.ts +7 -0
  158. package/dist/prosemirror/clipboardIntent.js +9 -0
  159. package/dist/prosemirror/commands/comments.js +60 -35
  160. package/dist/prosemirror/commands/pageBreak.js +2 -2
  161. package/dist/prosemirror/commands/pastePlainText.js +2 -1
  162. package/dist/prosemirror/commands/propertyChangeScope.js +3 -6
  163. package/dist/prosemirror/commands/resolveParagraphProperties.js +22 -13
  164. package/dist/prosemirror/commentOps.js +15 -1
  165. package/dist/prosemirror/conversion/fromProseDoc.js +22 -3
  166. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
  167. package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
  168. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -1
  169. package/dist/prosemirror/conversion/toProseDoc.js +124 -22
  170. package/dist/prosemirror/extensions/core/ParagraphExtension.js +61 -35
  171. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +74 -25
  172. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +11 -0
  173. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +65 -29
  174. package/dist/prosemirror/extensions/marks/markUtils.js +11 -8
  175. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
  176. package/dist/prosemirror/fieldRepresentation.d.ts +8 -0
  177. package/dist/prosemirror/fieldRepresentation.js +18 -0
  178. package/dist/prosemirror/hyperlinkRemoval.d.ts +13 -1
  179. package/dist/prosemirror/hyperlinkRemoval.js +28 -9
  180. package/dist/prosemirror/insertOperations.js +2 -1
  181. package/dist/prosemirror/listNumbering.js +11 -5
  182. package/dist/prosemirror/noteReferenceOccurrenceId.d.ts +5 -0
  183. package/dist/prosemirror/noteReferenceOccurrenceId.js +5 -0
  184. package/dist/prosemirror/noteReferenceOccurrences.d.ts +21 -0
  185. package/dist/prosemirror/noteReferenceOccurrences.js +185 -0
  186. package/dist/prosemirror/noteReferenceReview.js +14 -1
  187. package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
  188. package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
  189. package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
  190. package/dist/prosemirror/plugins/documentStyles.js +17 -10
  191. package/dist/prosemirror/plugins/suggestionMode.js +48 -48
  192. package/dist/prosemirror/preservedXmlReview.d.ts +6 -0
  193. package/dist/prosemirror/preservedXmlReview.js +35 -0
  194. package/dist/prosemirror/runFormattingFromMarks.js +2 -2
  195. package/dist/prosemirror/runFormattingReconciliation.js +5 -4
  196. package/dist/prosemirror/runPropertyChangeResolution.d.ts +8 -1
  197. package/dist/prosemirror/runPropertyChangeResolution.js +24 -2
  198. package/dist/prosemirror/runStyleFormatting.d.ts +3 -1
  199. package/dist/prosemirror/runStyleFormatting.js +3 -1
  200. package/dist/prosemirror/schema/marks.d.ts +1 -0
  201. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  202. package/dist/prosemirror/styles/paragraphStyleCascade.js +4 -1
  203. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +15 -15
  204. package/dist/prosemirror/styles/resolvedStyleAttrs.js +69 -28
  205. package/dist/prosemirror/textInput.d.ts +5 -1
  206. package/dist/prosemirror/textInput.js +3 -3
  207. package/dist/prosemirror/trackedRevisionPath.d.ts +5 -1
  208. package/dist/prosemirror/trackedRevisionPath.js +39 -1
  209. package/dist/prosemirror/validation.js +3 -5
  210. package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
  211. package/dist/prosemirror/yjsDocumentMetadata.js +37 -8
  212. package/dist/types/canonicalCapabilities.d.ts +201 -0
  213. package/dist/types/canonicalCapabilities.js +198 -0
  214. package/dist/types/canonicalComments.d.ts +51 -0
  215. package/dist/types/canonicalComments.js +0 -0
  216. package/dist/types/canonicalSave.d.ts +11 -0
  217. package/dist/types/canonicalSave.js +0 -0
  218. package/dist/types/canonicalSections.d.ts +12 -0
  219. package/dist/types/canonicalSections.js +0 -0
  220. package/dist/types/docxSerialization.d.ts +14 -0
  221. package/dist/types/docxSerialization.js +7 -0
  222. package/dist/utils/headingCollector.js +45 -4
  223. package/dist/utils/outlineDepth.d.ts +9 -0
  224. package/dist/utils/outlineDepth.js +9 -0
  225. package/package.json +4 -4
@@ -0,0 +1,382 @@
1
+ import { visitParagraphRuns } from "../docx/paragraphTraversal.js";
2
+ import { resolveStyleInheritance } from "../docx/styleParser.js";
3
+ import { resolveParagraphBodyRunFormatting } from "../prosemirror/runStyleFormatting.js";
4
+ import { cascadeStyleTextFormatting } from "../prosemirror/styles/styleToggleCascade.js";
5
+ import { createStyleEngine } from "../style-engine/styleEngine.js";
6
+ import { FONT_THEME_VALUES } from "../types/documentEnumValues.js";
7
+ import { isValidHexColor, resolveColorToHex } from "../utils/colorResolver.js";
8
+ import { resolveThemeFont } from "../utils/fontResolver.js";
9
+ import { mergeParagraphFormatting } from "../utils/paragraphFormattingMerge.js";
10
+ import { Result, panic } from "better-result";
11
+ import { BODY_TEXT_OUTLINE_LEVEL, NO_PARAGRAPH_NUMBERING, isThemeColor, themeColorSlot } from "@stll/docx-core/model";
12
+ import { DOCUMENT_OP_REFUSAL_REASONS, DOCUMENT_OP_TYPES, DocumentOpRefusal } from "@stll/docx-core/ops";
13
+ //#region src/controller/canonicalClipboardResources.ts
14
+ const STYLE_REFERENCE_FIELDS = /* @__PURE__ */ new Set([
15
+ "styleId",
16
+ "basedOn",
17
+ "next",
18
+ "link",
19
+ "pStyle",
20
+ "numStyleLink",
21
+ "styleLink"
22
+ ]);
23
+ const visitReferences = (value, visitor) => {
24
+ if (typeof value !== "object" || value === null) return;
25
+ if (Array.isArray(value)) {
26
+ for (const item of value) visitReferences(item, visitor);
27
+ return;
28
+ }
29
+ for (const [key, item] of Object.entries(value)) {
30
+ if (key === "listRendering") continue;
31
+ visitor(key, item, value);
32
+ visitReferences(item, visitor);
33
+ }
34
+ };
35
+ /** Normalized HTML states effective CSS; foreign style names carry no package ownership. */
36
+ const flattenClipboardStyleReferences = (paragraphs) => {
37
+ visitReferences(paragraphs, (key, _value, owner) => {
38
+ if (STYLE_REFERENCE_FIELDS.has(key)) Reflect.deleteProperty(owner, key);
39
+ });
40
+ };
41
+ const refused = (message) => Result.err(new DocumentOpRefusal({
42
+ opType: DOCUMENT_OP_TYPES.SET_PACKAGE_RESOURCES,
43
+ reason: DOCUMENT_OP_REFUSAL_REASONS.STRUCTURE_MISMATCH,
44
+ message
45
+ }));
46
+ const THEME_FONT_SLOTS = new Map(Object.entries({
47
+ asciiTheme: "ascii",
48
+ hAnsiTheme: "hAnsi",
49
+ eastAsiaTheme: "eastAsia",
50
+ csTheme: "cs"
51
+ }));
52
+ const THEME_FONT_REFERENCES = new Set(FONT_THEME_VALUES);
53
+ const lowerSourceTheme = ({ value, source }) => {
54
+ let unavailable;
55
+ visitReferences(value, (key, item, owner) => {
56
+ if (unavailable !== void 0) return;
57
+ const fontSlot = THEME_FONT_SLOTS.get(key);
58
+ if (fontSlot !== void 0 && typeof item === "string") {
59
+ const explicit = Reflect.get(owner, fontSlot);
60
+ const font = (THEME_FONT_REFERENCES.has(item) ? resolveThemeFont(item, source.package.theme?.fontScheme) : null) || (typeof explicit === "string" ? explicit : void 0);
61
+ if (font === void 0 || font.length === 0) {
62
+ unavailable = `The source package does not resolve theme font ${item}.`;
63
+ return;
64
+ }
65
+ Reflect.set(owner, fontSlot, font);
66
+ Reflect.deleteProperty(owner, key);
67
+ return;
68
+ }
69
+ if (key !== "themeColor") return;
70
+ if (item === "none") return;
71
+ if (Reflect.get(owner, "auto") === true) {
72
+ Reflect.deleteProperty(owner, "themeColor");
73
+ Reflect.deleteProperty(owner, "themeTint");
74
+ Reflect.deleteProperty(owner, "themeShade");
75
+ return;
76
+ }
77
+ const rgb = Reflect.get(owner, "rgb");
78
+ const tint = Reflect.get(owner, "themeTint");
79
+ const shade = Reflect.get(owner, "themeShade");
80
+ if (typeof item !== "string" || !isThemeColor(item)) {
81
+ if (typeof rgb !== "string" || !isValidHexColor(rgb)) {
82
+ unavailable = "A source theme colour has no known slot or explicit RGB value.";
83
+ return;
84
+ }
85
+ Reflect.deleteProperty(owner, "themeColor");
86
+ Reflect.deleteProperty(owner, "themeTint");
87
+ Reflect.deleteProperty(owner, "themeShade");
88
+ return;
89
+ }
90
+ const slot = themeColorSlot(item);
91
+ const schemeColour = slot === void 0 ? void 0 : source.package.theme?.colorScheme?.[slot];
92
+ if (schemeColour === void 0 || !isValidHexColor(schemeColour)) {
93
+ unavailable = `The source package does not resolve theme colour ${item}.`;
94
+ return;
95
+ }
96
+ const resolved = resolveColorToHex({
97
+ themeColor: item,
98
+ ...typeof rgb === "string" ? { rgb } : {},
99
+ ...typeof tint === "string" ? { themeTint: tint } : {},
100
+ ...typeof shade === "string" ? { themeShade: shade } : {}
101
+ }, source.package.theme);
102
+ if (resolved === void 0) {
103
+ unavailable = `The source package does not resolve theme colour ${item}.`;
104
+ return;
105
+ }
106
+ Reflect.set(owner, "rgb", resolved);
107
+ Reflect.deleteProperty(owner, "themeColor");
108
+ Reflect.deleteProperty(owner, "themeTint");
109
+ Reflect.deleteProperty(owner, "themeShade");
110
+ });
111
+ return unavailable === void 0 ? Result.ok(void 0) : refused(unavailable);
112
+ };
113
+ const NEUTRAL_PARAGRAPH_PROPERTIES = {
114
+ bidi: false,
115
+ keepNext: false,
116
+ keepLines: false,
117
+ widowControl: true,
118
+ pageBreakBefore: false,
119
+ contextualSpacing: false,
120
+ beforeAutospacing: false,
121
+ afterAutospacing: false,
122
+ hangingIndent: false,
123
+ suppressLineNumbers: false,
124
+ suppressAutoHyphens: false,
125
+ runInWithNext: false,
126
+ alignment: "start",
127
+ indentLeft: 0,
128
+ indentRight: 0,
129
+ indentFirstLine: 0,
130
+ outlineLevel: BODY_TEXT_OUTLINE_LEVEL,
131
+ numPr: NO_PARAGRAPH_NUMBERING,
132
+ tabs: [],
133
+ spaceBefore: 0,
134
+ spaceAfter: 0,
135
+ lineSpacing: 240,
136
+ lineSpacingRule: "auto"
137
+ };
138
+ const NEUTRAL_RUN_PROPERTIES = {
139
+ color: { auto: true },
140
+ highlight: "none",
141
+ underline: { style: "none" },
142
+ spacing: 0,
143
+ position: 0,
144
+ kerning: 0,
145
+ vertAlign: "baseline"
146
+ };
147
+ const materializeClipboardDefaults = ({ source, destination, paragraphs }) => {
148
+ const sourceDefinitions = structuredClone(source.package.styles);
149
+ if (sourceDefinitions !== void 0) {
150
+ const sourceMap = new Map(sourceDefinitions.styles.map((style) => [style.styleId, style]));
151
+ sourceDefinitions.styles = sourceDefinitions.styles.map((style) => resolveStyleInheritance(style, sourceMap));
152
+ }
153
+ const engine = createStyleEngine(sourceDefinitions);
154
+ const destinationDefaults = createStyleEngine(destination.package.styles).resolveParagraphStyle(void 0);
155
+ const neutralizeBooleanDefaults = (formatting, defaults) => {
156
+ for (const [key, value] of Object.entries(defaults ?? {})) if (typeof value === "boolean" && Reflect.get(formatting, key) === void 0) Reflect.set(formatting, key, false);
157
+ };
158
+ let unavailable;
159
+ const recordRefusal = (message) => {
160
+ unavailable ??= message;
161
+ };
162
+ for (const paragraph of paragraphs) {
163
+ const authored = paragraph.formatting;
164
+ const implicitStyle = engine.getDefaultParagraphStyle();
165
+ const styleId = authored?.styleId ?? implicitStyle?.styleId;
166
+ const resolved = engine.resolveParagraphStyle(styleId);
167
+ const formatting = mergeParagraphFormatting(resolved.paragraphFormatting, authored) ?? (destinationDefaults.paragraphFormatting === void 0 ? void 0 : {});
168
+ if (formatting !== void 0) {
169
+ if (authored?.numPr === void 0 && sourceDefinitions?.docDefaults?.pPr?.numPr === void 0) delete formatting.numPr;
170
+ if (authored?.numPrFromStyle === void 0) delete formatting.numPrFromStyle;
171
+ if (authored?.preserved !== void 0) formatting.preserved = authored.preserved;
172
+ for (const [key, value] of Object.entries(destinationDefaults.paragraphFormatting ?? {})) {
173
+ if (key === "preserved" || key === "styleId" || key === "runProperties" || key === "spacingExplicit" || value === void 0 || Reflect.get(formatting, key) !== void 0 || Reflect.get(resolved.paragraphFormatting ?? {}, key) !== void 0) continue;
174
+ const neutral = Reflect.get(NEUTRAL_PARAGRAPH_PROPERTIES, key);
175
+ if (neutral === void 0) {
176
+ recordRefusal(`The source package does not resolve inherited paragraph property ${key}.`);
177
+ continue;
178
+ }
179
+ Reflect.set(formatting, key, structuredClone(neutral));
180
+ }
181
+ if (authored?.styleId === void 0 && implicitStyle !== void 0 && engine.hasStyle(implicitStyle.styleId)) formatting.styleId = implicitStyle.styleId;
182
+ paragraph.formatting = formatting;
183
+ }
184
+ const inherited = resolveParagraphBodyRunFormatting({
185
+ styleId,
186
+ styleResolver: engine
187
+ });
188
+ const materializeRun = (direct) => {
189
+ const named = direct?.styleId;
190
+ const cascade = cascadeStyleTextFormatting([
191
+ {
192
+ cascade: named === void 0 ? inherited.defaultToggleCascade : inherited.baseToggleCascade,
193
+ type: "carried"
194
+ },
195
+ {
196
+ formatting: named === void 0 ? void 0 : engine.getRunStyleOwnProperties(named),
197
+ type: "style"
198
+ },
199
+ {
200
+ formatting: direct,
201
+ type: "direct"
202
+ }
203
+ ]).formatting ?? {};
204
+ neutralizeBooleanDefaults(cascade, destinationDefaults.runFormatting);
205
+ const themeLowered = lowerSourceTheme({
206
+ value: cascade,
207
+ source
208
+ });
209
+ if (themeLowered.isErr()) recordRefusal(themeLowered.error.message);
210
+ for (const [slot, value] of Object.entries(destinationDefaults.runFormatting?.fontFamily ?? {})) {
211
+ const concreteSlot = THEME_FONT_SLOTS.get(slot) ?? slot;
212
+ if (value === void 0 || Reflect.get(cascade.fontFamily ?? {}, concreteSlot) !== void 0) continue;
213
+ if (slot === "hint") {
214
+ cascade.fontFamily = {
215
+ ...cascade.fontFamily,
216
+ hint: "default"
217
+ };
218
+ continue;
219
+ }
220
+ recordRefusal(`The source package does not resolve inherited font slot ${slot}.`);
221
+ }
222
+ for (const [key, value] of Object.entries(destinationDefaults.runFormatting ?? {})) {
223
+ if (key === "preserved" || key === "styleId" || value === void 0 || Reflect.get(cascade, key) !== void 0) continue;
224
+ const neutral = Reflect.get(NEUTRAL_RUN_PROPERTIES, key);
225
+ if (neutral === void 0) {
226
+ recordRefusal(`The source package does not resolve inherited run property ${key}.`);
227
+ continue;
228
+ }
229
+ Reflect.set(cascade, key, structuredClone(neutral));
230
+ }
231
+ if (direct?.preserved !== void 0) cascade.preserved = direct.preserved;
232
+ return cascade;
233
+ };
234
+ visitParagraphRuns(paragraph, (run) => {
235
+ const runFormatting = materializeRun(run.formatting);
236
+ if (runFormatting !== void 0) run.formatting = runFormatting;
237
+ for (const change of run.propertyChanges ?? []) {
238
+ const previous = materializeRun(change.previousFormatting);
239
+ const current = materializeRun(change.currentFormatting);
240
+ if (previous !== void 0) change.previousFormatting = previous;
241
+ if (current !== void 0 && Object.hasOwn(change, "currentFormatting")) change.currentFormatting = current;
242
+ }
243
+ });
244
+ for (const change of paragraph.propertyChanges ?? []) {
245
+ const previous = change.previousFormatting;
246
+ const before = mergeParagraphFormatting(engine.resolveParagraphStyle(previous?.styleId).paragraphFormatting, previous);
247
+ if (before !== void 0) {
248
+ if (previous?.numPr === void 0) delete before.numPr;
249
+ if (previous?.preserved !== void 0) before.preserved = previous.preserved;
250
+ change.previousFormatting = before;
251
+ }
252
+ }
253
+ if (paragraph.content.length === 0) {
254
+ const markFormatting = materializeRun(paragraph.formatting?.runProperties);
255
+ if (markFormatting !== void 0) paragraph.formatting = {
256
+ ...paragraph.formatting,
257
+ runProperties: markFormatting
258
+ };
259
+ }
260
+ }
261
+ return unavailable === void 0 ? Result.ok(void 0) : refused(unavailable);
262
+ };
263
+ /** Import the transitive style/numbering dependencies of the clipboard, retaining destination defaults. */
264
+ const importClipboardStyles = ({ destination, source, paragraphs }) => {
265
+ const clonedParagraphs = structuredClone([...paragraphs]);
266
+ const materialized = materializeClipboardDefaults({
267
+ source,
268
+ destination,
269
+ paragraphs: clonedParagraphs
270
+ });
271
+ if (materialized.isErr()) return materialized;
272
+ const sourceStyles = /* @__PURE__ */ new Map();
273
+ for (const style of source.package.styles?.styles ?? []) {
274
+ if (sourceStyles.has(style.styleId)) return refused("The source package contains duplicate style identities.");
275
+ sourceStyles.set(style.styleId, style);
276
+ }
277
+ const styleQueue = [];
278
+ const numberingQueue = [];
279
+ const selectedStyles = /* @__PURE__ */ new Set();
280
+ const numberingIds = /* @__PURE__ */ new Set();
281
+ const selectReferences = (key, value) => {
282
+ if (STYLE_REFERENCE_FIELDS.has(key) && typeof value === "string" && !selectedStyles.has(value)) {
283
+ selectedStyles.add(value);
284
+ styleQueue.push(value);
285
+ }
286
+ if (key === "numId" && typeof value === "number" && !numberingIds.has(value)) {
287
+ numberingIds.add(value);
288
+ numberingQueue.push(value);
289
+ }
290
+ };
291
+ for (const paragraph of clonedParagraphs) visitReferences(paragraph, selectReferences);
292
+ const sourceNums = source.package.numbering?.nums ?? [];
293
+ const sourceAbstracts = source.package.numbering?.abstractNums ?? [];
294
+ const nums = new Map(sourceNums.map((num) => [num.numId, num]));
295
+ const abstracts = new Map(sourceAbstracts.map((abstract) => [abstract.abstractNumId, abstract]));
296
+ if (nums.size !== sourceNums.length || abstracts.size !== sourceAbstracts.length) return refused("The source package contains duplicate numbering identities.");
297
+ let styleIndex = 0;
298
+ let numberingIndex = 0;
299
+ while (styleIndex < styleQueue.length || numberingIndex < numberingQueue.length) {
300
+ while (styleIndex < styleQueue.length) {
301
+ const id = styleQueue.at(styleIndex++);
302
+ if (id === void 0) return refused("A clipboard style dependency was lost.");
303
+ const style = sourceStyles.get(id);
304
+ if (style === void 0) return refused(`The source package does not define clipboard style ${id}.`);
305
+ visitReferences(style, selectReferences);
306
+ }
307
+ while (numberingIndex < numberingQueue.length) {
308
+ const id = numberingQueue.at(numberingIndex++);
309
+ if (id === void 0) return refused("A clipboard numbering dependency was lost.");
310
+ const num = nums.get(id);
311
+ if (num === void 0) return refused(`The source package does not define clipboard numbering ${id}.`);
312
+ const abstract = abstracts.get(num.abstractNumId);
313
+ if (abstract === void 0) return refused("A clipboard numbering instance has no abstract definition.");
314
+ visitReferences(num, selectReferences);
315
+ visitReferences(abstract, selectReferences);
316
+ }
317
+ }
318
+ for (const id of selectedStyles) {
319
+ const ancestors = /* @__PURE__ */ new Set();
320
+ let ancestor = id;
321
+ while (ancestor !== void 0) {
322
+ if (ancestors.has(ancestor)) return refused("The source clipboard style inheritance contains a cycle.");
323
+ ancestors.add(ancestor);
324
+ ancestor = sourceStyles.get(ancestor)?.basedOn;
325
+ }
326
+ }
327
+ const occupied = new Set(destination.package.styles?.styles.map(({ styleId }) => styleId));
328
+ const reserved = /* @__PURE__ */ new Set([...occupied, ...selectedStyles]);
329
+ const styleIds = /* @__PURE__ */ new Map();
330
+ for (const id of [...selectedStyles].toSorted()) {
331
+ if (!occupied.has(id)) {
332
+ styleIds.set(id, id);
333
+ continue;
334
+ }
335
+ let ordinal = 1;
336
+ let importedId = `${id}_clipboard${ordinal}`;
337
+ while (reserved.has(importedId)) importedId = `${id}_clipboard${++ordinal}`;
338
+ reserved.add(importedId);
339
+ styleIds.set(id, importedId);
340
+ }
341
+ const remap = (key, value, owner) => {
342
+ if (!STYLE_REFERENCE_FIELDS.has(key) || typeof value !== "string") return;
343
+ const mapped = styleIds.get(value);
344
+ if (mapped === void 0) panic("A selected clipboard style has no allocated destination identity.");
345
+ Reflect.set(owner, key, mapped);
346
+ };
347
+ const importedStyles = [];
348
+ for (const id of [...selectedStyles].toSorted()) {
349
+ const style = sourceStyles.get(id);
350
+ if (style === void 0) return refused("A selected clipboard style has no source definition.");
351
+ const imported = structuredClone(style);
352
+ visitReferences(imported, remap);
353
+ const lowered = lowerSourceTheme({
354
+ value: imported,
355
+ source
356
+ });
357
+ if (lowered.isErr()) return lowered;
358
+ if (imported.default === true) delete imported.default;
359
+ importedStyles.push(imported);
360
+ }
361
+ for (const paragraph of clonedParagraphs) {
362
+ visitReferences(paragraph, remap);
363
+ const lowered = lowerSourceTheme({
364
+ value: paragraph,
365
+ source
366
+ });
367
+ if (lowered.isErr()) return lowered;
368
+ }
369
+ const prior = destination.package.styles;
370
+ const styles = importedStyles.length === 0 ? prior : {
371
+ ...prior,
372
+ styles: [...prior?.styles ?? [], ...importedStyles]
373
+ };
374
+ return Result.ok({
375
+ styles,
376
+ paragraphs: clonedParagraphs,
377
+ styleIds,
378
+ numberingIds
379
+ });
380
+ };
381
+ //#endregion
382
+ export { flattenClipboardStyleReferences, importClipboardStyles };
@@ -0,0 +1,41 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
3
+ import { Comment } from "../types/content.js";
4
+ import { CreateCommentOp, DocumentOp } from "@stll/docx-core/ops";
5
+ //#region src/controller/canonicalComments.d.ts
6
+ declare const CanonicalCommentError_base: import("better-result").TaggedErrorClass<"CanonicalCommentError">;
7
+ declare class CanonicalCommentError extends CanonicalCommentError_base<{
8
+ message: string;
9
+ gap: typeof CANONICAL_GAP.comments;
10
+ }> {}
11
+ type CanonicalCommentCommand = {
12
+ type: "create";
13
+ comment: CreateCommentOp["comment"];
14
+ anchor: CreateCommentOp["anchor"];
15
+ } | {
16
+ type: "update";
17
+ id: number;
18
+ content: Comment["content"];
19
+ } | {
20
+ type: "resolve";
21
+ id: number;
22
+ status: "open" | "resolved";
23
+ } | {
24
+ type: "delete";
25
+ id: number;
26
+ } | {
27
+ type: "replace";
28
+ comments: readonly Comment[];
29
+ };
30
+ type CompileCanonicalCommentsOptions = {
31
+ document: document_d_exports.Document;
32
+ command: CanonicalCommentCommand;
33
+ };
34
+ declare const compileCanonicalComments: ({ document, command }: CompileCanonicalCommentsOptions) => import("better-result").Err<never, CanonicalCommentError> | import("better-result").Ok<{
35
+ ops: DocumentOp[];
36
+ document: document_d_exports.Document;
37
+ commentId: number | undefined;
38
+ }, never>;
39
+ declare const canonicalCommentBody: (document: document_d_exports.Document, text: string) => Comment["content"];
40
+ //#endregion
41
+ export { CanonicalCommentCommand, CanonicalCommentError, canonicalCommentBody, compileCanonicalComments };
@@ -0,0 +1,152 @@
1
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
2
+ import { canonicalJson } from "../utils/canonicalJson.js";
3
+ import { Result, TaggedError } from "better-result";
4
+ import { DOCUMENT_OP_TYPES, allocateCommentAnchorIds, applyDocumentOps, packageParagraphIds } from "@stll/docx-core/ops";
5
+ //#region src/controller/canonicalComments.ts
6
+ /** The one semantic compiler for canonical sidebar, controlled and public comments. */
7
+ var CanonicalCommentError = class extends TaggedError("CanonicalCommentError") {};
8
+ const refuse = (message) => Result.err(new CanonicalCommentError({
9
+ message,
10
+ gap: CANONICAL_GAP.comments
11
+ }));
12
+ const compileCanonicalComments = ({ document, command }) => {
13
+ const current = document.package.document.comments ?? [];
14
+ const byId = new Map(current.map((comment) => [comment.id, comment]));
15
+ const ops = [];
16
+ let commentId;
17
+ switch (command.type) {
18
+ case "create":
19
+ ops.push({
20
+ type: DOCUMENT_OP_TYPES.CREATE_COMMENT,
21
+ comment: command.comment,
22
+ anchor: command.anchor
23
+ });
24
+ commentId = command.comment.id;
25
+ break;
26
+ case "update":
27
+ ops.push({
28
+ type: DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT,
29
+ id: command.id,
30
+ content: command.content
31
+ });
32
+ break;
33
+ case "resolve":
34
+ ops.push({
35
+ type: DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION,
36
+ id: command.id,
37
+ status: command.status
38
+ });
39
+ break;
40
+ case "delete": {
41
+ const comment = byId.get(command.id);
42
+ if (!comment) return refuse("The comment no longer exists.");
43
+ ops.push({
44
+ type: DOCUMENT_OP_TYPES.DELETE_COMMENT,
45
+ id: command.id,
46
+ scope: comment.parentId !== void 0 && byId.has(comment.parentId) ? "reply" : "thread"
47
+ });
48
+ break;
49
+ }
50
+ case "replace": {
51
+ const nextById = new Map(command.comments.map((comment) => [comment.id, comment]));
52
+ if (nextById.size !== command.comments.length) return refuse("Comment ids must be unique.");
53
+ for (const comment of current) {
54
+ if (nextById.has(comment.id) && comment.parentId !== void 0 && byId.has(comment.parentId) && !nextById.has(comment.parentId)) return refuse("A retained reply requires its parent thread.");
55
+ if (nextById.has(comment.id)) continue;
56
+ if (comment.parentId !== void 0 && !nextById.has(comment.parentId) && byId.has(comment.parentId)) continue;
57
+ ops.push({
58
+ type: DOCUMENT_OP_TYPES.DELETE_COMMENT,
59
+ id: comment.id,
60
+ scope: comment.parentId !== void 0 && byId.has(comment.parentId) ? "reply" : "thread"
61
+ });
62
+ }
63
+ const pending = command.comments.filter((comment) => !byId.has(comment.id));
64
+ const available = new Set(current.filter((comment) => nextById.has(comment.id)).map((comment) => comment.id));
65
+ while (pending.length > 0) {
66
+ const index = pending.findIndex((comment) => comment.parentId !== void 0 && available.has(comment.parentId));
67
+ if (index < 0) return refuse("New controlled comments require an existing anchored parent without cycles.");
68
+ const next = pending.splice(index, 1).at(0);
69
+ if (next === void 0 || next.parentId === void 0) return refuse("The controlled reply is missing its parent.");
70
+ const { parentId, ...comment } = next;
71
+ ops.push({
72
+ type: DOCUMENT_OP_TYPES.CREATE_COMMENT,
73
+ comment,
74
+ anchor: {
75
+ kind: "reply",
76
+ parentId
77
+ }
78
+ });
79
+ available.add(next.id);
80
+ }
81
+ for (const next of command.comments) {
82
+ const previous = byId.get(next.id);
83
+ if (!previous) continue;
84
+ const { content: priorContent, done: priorDone, author: priorAuthor, initials: priorInitials, date: priorDate, ...priorMetadata } = previous;
85
+ const { content: nextContent, done: nextDone, author: nextAuthor, initials: nextInitials, date: nextDate, ...nextMetadata } = next;
86
+ if (canonicalJson(priorMetadata) !== canonicalJson(nextMetadata)) return refuse("Controlled comment identity and opaque metadata cannot be replaced.");
87
+ const patch = {
88
+ ...priorAuthor === nextAuthor ? {} : { author: nextAuthor },
89
+ ...priorInitials === nextInitials ? {} : { initials: Object.hasOwn(next, "initials") ? nextInitials : null },
90
+ ...priorDate === nextDate ? {} : { date: Object.hasOwn(next, "date") ? nextDate : null }
91
+ };
92
+ if (canonicalJson(priorContent) !== canonicalJson(nextContent) || Object.keys(patch).length > 0) ops.push({
93
+ type: DOCUMENT_OP_TYPES.UPDATE_COMMENT_CONTENT,
94
+ id: next.id,
95
+ content: nextContent,
96
+ patch
97
+ });
98
+ if (priorDone !== nextDone) {
99
+ if (nextDone === void 0) return refuse("Resolution changes must name an open or resolved state.");
100
+ ops.push({
101
+ type: DOCUMENT_OP_TYPES.SET_COMMENT_RESOLUTION,
102
+ id: next.id,
103
+ status: nextDone ? "resolved" : "open"
104
+ });
105
+ }
106
+ }
107
+ break;
108
+ }
109
+ default: return command;
110
+ }
111
+ const compiled = [];
112
+ let draft = document;
113
+ for (const op of ops) {
114
+ const materialized = (() => {
115
+ if (op.type !== DOCUMENT_OP_TYPES.CREATE_COMMENT) return Result.ok(op);
116
+ const allocation = allocateCommentAnchorIds(draft, op);
117
+ if (allocation.isErr()) return Result.err(allocation.error);
118
+ return Result.ok({
119
+ ...op,
120
+ newIds: allocation.value
121
+ });
122
+ })();
123
+ if (materialized.isErr()) return refuse(materialized.error.message);
124
+ const applied = applyDocumentOps(draft, [materialized.value]);
125
+ if (applied.isErr()) return refuse(applied.error.message);
126
+ compiled.push(materialized.value);
127
+ draft = applied.value.document;
128
+ }
129
+ return Result.ok({
130
+ ops: compiled,
131
+ document: draft,
132
+ commentId
133
+ });
134
+ };
135
+ const canonicalCommentBody = (document, text) => {
136
+ const occupied = new Set(packageParagraphIds(document.package).map((id) => id.toUpperCase()));
137
+ let id = 1;
138
+ while (occupied.has(id.toString(16).padStart(8, "0").toUpperCase())) id += 1;
139
+ return [{
140
+ type: "paragraph",
141
+ paraId: id.toString(16).padStart(8, "0").toUpperCase(),
142
+ content: [{
143
+ type: "run",
144
+ content: [{
145
+ type: "text",
146
+ text
147
+ }]
148
+ }]
149
+ }];
150
+ };
151
+ //#endregion
152
+ export { CanonicalCommentError, canonicalCommentBody, compileCanonicalComments };
@@ -9,26 +9,47 @@ type CompositionOptions = {
9
9
  to: number;
10
10
  text: string;
11
11
  semantic: "composition";
12
+ compositionPhase?: "correction";
12
13
  }) => void;
13
14
  refuse: (reason: string) => void;
14
15
  };
15
16
  type CompositionState = {
16
17
  type: "committed";
17
18
  } | {
18
- type: "provisional" | "refused";
19
+ type: "provisional";
19
20
  baseline: EditorState;
21
+ view: EditorView;
20
22
  phase: "active" | "ended";
23
+ } | {
24
+ type: "refused";
25
+ baseline: EditorState;
26
+ view: EditorView;
27
+ phase: "active" | "ended";
28
+ text: string | null;
29
+ };
30
+ type CompositionFinish = {
31
+ type: "idle" | "cancelled";
32
+ } | {
33
+ type: "committed" | "refused";
34
+ text: string | null;
21
35
  };
22
36
  /** Native IME owns the provisional view; the captured canonical projection stays unchanged. */
23
37
  declare const createCanonicalComposition: (options: CompositionOptions) => {
24
38
  readonly status: CompositionState["type"];
25
39
  readonly active: boolean;
40
+ matchesCancellationRange: (view: EditorView, range: {
41
+ from: number;
42
+ to: number;
43
+ }) => boolean;
26
44
  start: (view: EditorView) => void;
27
45
  ended: (view: EditorView) => void;
46
+ forgetCompleted: () => void;
47
+ readonly pendingFinal: boolean;
48
+ authorizeLateFinal: (view: EditorView, text: string | null) => boolean;
28
49
  authorizeNative: (view: EditorView) => void;
29
50
  flushed: (view: EditorView) => void;
30
- recover: (view: EditorView) => void;
31
- cancel: (view: EditorView) => void;
51
+ recover: (view: EditorView) => CompositionFinish;
52
+ cancel: (view: EditorView) => CompositionFinish;
32
53
  reset: () => void;
33
54
  accept: (view: EditorView, transaction: Transaction) => boolean;
34
55
  };