@stll/folio-core 0.55.0 → 0.56.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 (119) hide show
  1. package/dist/ai-edits/apply.js +169 -38
  2. package/dist/ai-edits/headless.js +2 -2
  3. package/dist/ai-edits/types.d.ts +7 -1
  4. package/dist/compare/compare.js +1 -0
  5. package/dist/compare/reproducible-package.d.ts +0 -17
  6. package/dist/compare/reproducible-package.js +12 -18
  7. package/dist/controller/canonicalClipboard.d.ts +23 -0
  8. package/dist/controller/canonicalClipboard.js +476 -0
  9. package/dist/controller/canonicalClipboardResources.d.ts +21 -0
  10. package/dist/controller/canonicalClipboardResources.js +382 -0
  11. package/dist/controller/canonicalComposition.d.ts +20 -3
  12. package/dist/controller/canonicalComposition.js +166 -38
  13. package/dist/controller/canonicalInlineProjection.d.ts +113 -0
  14. package/dist/controller/canonicalInlineProjection.js +393 -0
  15. package/dist/controller/canonicalInput.d.ts +13 -4
  16. package/dist/controller/canonicalInput.js +52 -13
  17. package/dist/controller/canonicalInputTimer.d.ts +7 -0
  18. package/dist/controller/canonicalInputTimer.js +11 -0
  19. package/dist/controller/canonicalPublicOperations.d.ts +48 -0
  20. package/dist/controller/canonicalPublicOperations.js +556 -0
  21. package/dist/controller/canonicalSession.d.ts +24 -6
  22. package/dist/controller/canonicalSession.js +259 -111
  23. package/dist/controller/canonicalStoryEditor.d.ts +5 -3
  24. package/dist/controller/canonicalStoryEditor.js +7 -2
  25. package/dist/controller/canonicalStructure.js +57 -4
  26. package/dist/controller/canonicalTocSelection.d.ts +11 -0
  27. package/dist/controller/canonicalTocSelection.js +43 -0
  28. package/dist/controller/folioEditor.d.ts +2 -9
  29. package/dist/controller/folioEditor.js +5 -4
  30. package/dist/controller/headerFooterEditorManager.d.ts +2 -1
  31. package/dist/controller/headerFooterEditorManager.js +8 -7
  32. package/dist/controller/hiddenEditorApi.d.ts +12 -2
  33. package/dist/controller/hiddenEditorApi.js +4 -0
  34. package/dist/controller/hiddenEditorManager.d.ts +3 -1
  35. package/dist/controller/hiddenEditorManager.js +216 -34
  36. package/dist/controller/noteEditorManager.d.ts +2 -1
  37. package/dist/controller/noteEditorManager.js +8 -7
  38. package/dist/document-operations.d.ts +8 -1
  39. package/dist/document-operations.js +6 -4
  40. package/dist/docx/blockPlainText.d.ts +1 -1
  41. package/dist/docx/blockPlainText.js +11 -3
  42. package/dist/docx/canonicalResourceSave.d.ts +16 -0
  43. package/dist/docx/canonicalResourceSave.js +27 -0
  44. package/dist/docx/canonicalSave.d.ts +29 -0
  45. package/dist/docx/canonicalSave.js +101 -0
  46. package/dist/docx/canonicalStoryRepack.d.ts +1 -1
  47. package/dist/docx/canonicalStoryRepack.js +38 -5
  48. package/dist/docx/documentParser.d.ts +1 -13
  49. package/dist/docx/documentParser.js +1 -47
  50. package/dist/docx/ensureParaIds.js +10 -4
  51. package/dist/docx/footnoteParser.d.ts +1 -11
  52. package/dist/docx/footnoteParser.js +1 -16
  53. package/dist/docx/headerFooterParser.d.ts +1 -9
  54. package/dist/docx/headerFooterParser.js +1 -12
  55. package/dist/docx/headerFooterVerbatim.d.ts +26 -1
  56. package/dist/docx/headerFooterVerbatim.js +81 -3
  57. package/dist/docx/imageParser.d.ts +13 -1
  58. package/dist/docx/imageParser.js +20 -11
  59. package/dist/docx/metadataPrivacy.js +6 -1
  60. package/dist/docx/packageParts.js +9 -3
  61. package/dist/docx/paragraphPropertySource.js +2 -1
  62. package/dist/docx/parser.js +2 -1
  63. package/dist/docx/removeHeaderFooterParts.js +17 -6
  64. package/dist/docx/rezip.d.ts +49 -5
  65. package/dist/docx/rezip.js +745 -229
  66. package/dist/docx/saveDiagnostics.d.ts +11 -0
  67. package/dist/docx/selectiveSave.d.ts +2 -1
  68. package/dist/docx/selectiveSave.js +32 -3
  69. package/dist/docx/selectiveXmlPatch.js +7 -5
  70. package/dist/docx/serializer/documentSerializer.d.ts +9 -2
  71. package/dist/docx/serializer/documentSerializer.js +29 -10
  72. package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
  73. package/dist/docx/storyBlockReplay.d.ts +21 -1
  74. package/dist/docx/storyBlockReplay.js +113 -9
  75. package/dist/docx/storyPlainText.d.ts +20 -0
  76. package/dist/docx/storyPlainText.js +23 -0
  77. package/dist/docx/structuralXmlPatch.js +10 -22
  78. package/dist/docx/styleParser.d.ts +5 -1
  79. package/dist/docx/styleParser.js +1 -1
  80. package/dist/docx/tableParser.d.ts +1 -8
  81. package/dist/docx/tableParser.js +1 -28
  82. package/dist/docx/textBoxParser.d.ts +1 -5
  83. package/dist/docx/textBoxParser.js +1 -20
  84. package/dist/docx/unzip.js +6 -1
  85. package/dist/internal/acceptedBlockProjection.d.ts +12 -0
  86. package/dist/internal/acceptedBlockProjection.js +45 -0
  87. package/dist/managers/DocumentLoaderManager.js +2 -1
  88. package/dist/markdown/escape.d.ts +3 -1
  89. package/dist/markdown/escape.js +9 -2
  90. package/dist/markdown/renderBlock.js +5 -120
  91. package/dist/markdown/renderParagraph.js +4 -2
  92. package/dist/markdown/renderRuns.d.ts +7 -1
  93. package/dist/markdown/renderRuns.js +32 -13
  94. package/dist/prosemirror/canonicalCommands.d.ts +28 -4
  95. package/dist/prosemirror/canonicalCommands.js +11 -4
  96. package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
  97. package/dist/prosemirror/canonicalSelectionRange.js +15 -0
  98. package/dist/prosemirror/clipboardIntent.d.ts +7 -0
  99. package/dist/prosemirror/clipboardIntent.js +9 -0
  100. package/dist/prosemirror/commands/pageBreak.js +2 -2
  101. package/dist/prosemirror/commands/pastePlainText.js +2 -1
  102. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
  103. package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
  104. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -1
  105. package/dist/prosemirror/conversion/toProseDoc.js +5 -4
  106. package/dist/prosemirror/extensions/core/ParagraphExtension.js +39 -30
  107. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +56 -28
  108. package/dist/prosemirror/extensions/marks/markUtils.js +3 -1
  109. package/dist/prosemirror/insertOperations.js +2 -1
  110. package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
  111. package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
  112. package/dist/prosemirror/plugins/suggestionMode.js +7 -5
  113. package/dist/types/canonicalCapabilities.d.ts +215 -0
  114. package/dist/types/canonicalCapabilities.js +212 -0
  115. package/dist/types/canonicalSave.d.ts +11 -0
  116. package/dist/types/canonicalSave.js +0 -0
  117. package/dist/types/docxSerialization.d.ts +14 -0
  118. package/dist/types/docxSerialization.js +7 -0
  119. 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 };
@@ -9,15 +9,29 @@ 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) => {
@@ -25,10 +39,13 @@ declare const createCanonicalComposition: (options: CompositionOptions) => {
25
39
  readonly active: boolean;
26
40
  start: (view: EditorView) => void;
27
41
  ended: (view: EditorView) => void;
42
+ forgetCompleted: () => void;
43
+ readonly pendingFinal: boolean;
44
+ authorizeLateFinal: (view: EditorView, text: string | null) => boolean;
28
45
  authorizeNative: (view: EditorView) => void;
29
46
  flushed: (view: EditorView) => void;
30
- recover: (view: EditorView) => void;
31
- cancel: (view: EditorView) => void;
47
+ recover: (view: EditorView) => CompositionFinish;
48
+ cancel: (view: EditorView) => CompositionFinish;
32
49
  reset: () => void;
33
50
  accept: (view: EditorView, transaction: Transaction) => boolean;
34
51
  };