@pptx-glimpse/editor 0.1.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.
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/index.cjs +719 -0
- package/dist/index.d.cts +149 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +712 -0
- package/package.json +51 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
EditorSession: () => EditorSession,
|
|
24
|
+
createEditorSession: () => createEditorSession
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var import_document = require("@pptx-glimpse/document");
|
|
28
|
+
var EditorSession = class {
|
|
29
|
+
#document;
|
|
30
|
+
#selection;
|
|
31
|
+
#undoStack = [];
|
|
32
|
+
#redoStack = [];
|
|
33
|
+
constructor(document) {
|
|
34
|
+
this.#document = document;
|
|
35
|
+
}
|
|
36
|
+
get document() {
|
|
37
|
+
return this.#document;
|
|
38
|
+
}
|
|
39
|
+
get selection() {
|
|
40
|
+
return this.#selection;
|
|
41
|
+
}
|
|
42
|
+
get canUndo() {
|
|
43
|
+
return this.#undoStack.length > 0;
|
|
44
|
+
}
|
|
45
|
+
get canRedo() {
|
|
46
|
+
return this.#redoStack.length > 0;
|
|
47
|
+
}
|
|
48
|
+
get undoDepth() {
|
|
49
|
+
return this.#undoStack.length;
|
|
50
|
+
}
|
|
51
|
+
get redoDepth() {
|
|
52
|
+
return this.#redoStack.length;
|
|
53
|
+
}
|
|
54
|
+
selectShape(handle) {
|
|
55
|
+
if ((0, import_document.findShapeNodeBySourceHandle)(this.#document, handle) === void 0) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
code: "invalid-selection",
|
|
59
|
+
message: "selectShape: shape handle was not found in PptxSourceModel source"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const selection = { shapeHandle: handle };
|
|
63
|
+
this.#selection = selection;
|
|
64
|
+
return { ok: true, selection };
|
|
65
|
+
}
|
|
66
|
+
deselectShape() {
|
|
67
|
+
this.#selection = void 0;
|
|
68
|
+
}
|
|
69
|
+
apply(command) {
|
|
70
|
+
return this.applyAll([command]);
|
|
71
|
+
}
|
|
72
|
+
applyAll(commands) {
|
|
73
|
+
const before = this.#document;
|
|
74
|
+
if (commands.length === 0) return { ok: true, document: before };
|
|
75
|
+
try {
|
|
76
|
+
let after = before;
|
|
77
|
+
const warnings = [];
|
|
78
|
+
for (const command of commands) {
|
|
79
|
+
after = applyCommandToDocument(after, command);
|
|
80
|
+
warnings.push(...collectCommandWarnings(after, [command]));
|
|
81
|
+
}
|
|
82
|
+
assertNoConflictingParagraphAndRunEdits(after.edits);
|
|
83
|
+
after = normalizeEditorEdits(after);
|
|
84
|
+
const dedupedWarnings = dedupeCommandWarnings(warnings);
|
|
85
|
+
if (after === before) {
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
document: before,
|
|
89
|
+
...dedupedWarnings.length > 0 ? { warnings: dedupedWarnings } : {}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
this.#document = after;
|
|
93
|
+
this.reconcileSelectionAfterDocumentChange();
|
|
94
|
+
this.#undoStack.push({ before, after });
|
|
95
|
+
this.#redoStack.length = 0;
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
document: after,
|
|
99
|
+
...dedupedWarnings.length > 0 ? { warnings: dedupedWarnings } : {}
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
code: "invalid-command",
|
|
105
|
+
message: error instanceof Error ? error.message : "Editor command was rejected.",
|
|
106
|
+
cause: error
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
undo() {
|
|
111
|
+
const entry = this.#undoStack.pop();
|
|
112
|
+
if (entry === void 0) return { ok: false, reason: "empty-undo-stack" };
|
|
113
|
+
this.#document = entry.before;
|
|
114
|
+
this.reconcileSelectionAfterDocumentChange();
|
|
115
|
+
this.#redoStack.push(entry);
|
|
116
|
+
return { ok: true, document: entry.before };
|
|
117
|
+
}
|
|
118
|
+
redo() {
|
|
119
|
+
const entry = this.#redoStack.pop();
|
|
120
|
+
if (entry === void 0) return { ok: false, reason: "empty-redo-stack" };
|
|
121
|
+
this.#document = entry.after;
|
|
122
|
+
this.reconcileSelectionAfterDocumentChange();
|
|
123
|
+
this.#undoStack.push(entry);
|
|
124
|
+
return { ok: true, document: entry.after };
|
|
125
|
+
}
|
|
126
|
+
reconcileSelectionAfterDocumentChange() {
|
|
127
|
+
if (this.#selection === void 0) return;
|
|
128
|
+
if ((0, import_document.findShapeNodeBySourceHandle)(this.#document, this.#selection.shapeHandle) === void 0) {
|
|
129
|
+
this.#selection = void 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
function createEditorSession(document) {
|
|
134
|
+
return new EditorSession(document);
|
|
135
|
+
}
|
|
136
|
+
function applyCommandToDocument(document, command) {
|
|
137
|
+
switch (command.kind) {
|
|
138
|
+
case "replaceTextRunPlainText":
|
|
139
|
+
return replaceTextRunPlainTextCommand(document, command);
|
|
140
|
+
case "replaceParagraphPlainText":
|
|
141
|
+
return replaceParagraphPlainTextCommand(document, command);
|
|
142
|
+
case "setTextRunProperties":
|
|
143
|
+
return setTextRunPropertiesCommand(document, command);
|
|
144
|
+
case "clearTextRunProperties":
|
|
145
|
+
return clearTextRunPropertiesCommand(document, command);
|
|
146
|
+
case "setParagraphProperties":
|
|
147
|
+
return setParagraphPropertiesCommand(document, command);
|
|
148
|
+
case "clearParagraphProperties":
|
|
149
|
+
return clearParagraphPropertiesCommand(document, command);
|
|
150
|
+
case "moveShape":
|
|
151
|
+
return moveShape(document, command);
|
|
152
|
+
case "resizeShape":
|
|
153
|
+
return resizeShape(document, command);
|
|
154
|
+
case "setShapeTransform":
|
|
155
|
+
return setShapeTransform(document, command);
|
|
156
|
+
case "setShapeFill":
|
|
157
|
+
return setShapeFillCommand(document, command);
|
|
158
|
+
case "setShapeOutline":
|
|
159
|
+
return setShapeOutlineCommand(document, command);
|
|
160
|
+
case "addTextBox":
|
|
161
|
+
return addTextBoxCommand(document, command);
|
|
162
|
+
case "addConnector":
|
|
163
|
+
return addConnectorCommand(document, command);
|
|
164
|
+
case "deleteShape":
|
|
165
|
+
return (0, import_document.deleteShape)(document, command.handle);
|
|
166
|
+
case "replaceImage":
|
|
167
|
+
return (0, import_document.replaceImageBytes)(document, command.handle, command.bytes);
|
|
168
|
+
case "addEmptySlideFromLayout":
|
|
169
|
+
return (0, import_document.addEmptySlideFromLayout)(document, command);
|
|
170
|
+
case "duplicateSlide":
|
|
171
|
+
return (0, import_document.duplicateSlide)(document, command.handle);
|
|
172
|
+
case "moveSlide":
|
|
173
|
+
return (0, import_document.moveSlide)(document, command.handle, command);
|
|
174
|
+
case "deleteSlide":
|
|
175
|
+
return (0, import_document.deleteSlide)(document, command.handle);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function replaceTextRunPlainTextCommand(document, command) {
|
|
179
|
+
if (typeof command.text !== "string") {
|
|
180
|
+
throw new Error("replaceTextRunPlainText: text must be a string");
|
|
181
|
+
}
|
|
182
|
+
return (0, import_document.replaceTextRunPlainText)(document, command.handle, command.text);
|
|
183
|
+
}
|
|
184
|
+
function replaceParagraphPlainTextCommand(document, command) {
|
|
185
|
+
if (typeof command.text !== "string") {
|
|
186
|
+
throw new Error("replaceParagraphPlainText: text must be a string");
|
|
187
|
+
}
|
|
188
|
+
return (0, import_document.replaceParagraphPlainText)(document, command.handle, command.text);
|
|
189
|
+
}
|
|
190
|
+
function collectCommandWarnings(document, commands) {
|
|
191
|
+
if (!commands.some((command) => command.kind === "replaceImage")) return [];
|
|
192
|
+
const warnings = [];
|
|
193
|
+
const imageEdits = document.edits?.filter((edit) => edit.kind === "replaceImage") ?? [];
|
|
194
|
+
for (const command of commands) {
|
|
195
|
+
if (command.kind !== "replaceImage") continue;
|
|
196
|
+
let edit;
|
|
197
|
+
for (let index = imageEdits.length - 1; index >= 0; index -= 1) {
|
|
198
|
+
const candidate = imageEdits[index];
|
|
199
|
+
if (candidate.handle.partPath === command.handle.partPath && candidate.handle.nodeId === command.handle.nodeId && candidate.handle.relationshipId === command.handle.relationshipId && candidate.handle.orderingSlot === command.handle.orderingSlot) {
|
|
200
|
+
edit = candidate;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (edit === void 0 || edit.sharedReferenceCount <= 1) continue;
|
|
205
|
+
warnings.push({
|
|
206
|
+
code: "shared-media-part",
|
|
207
|
+
mediaPartPath: edit.mediaPartPath,
|
|
208
|
+
referenceCount: edit.sharedReferenceCount,
|
|
209
|
+
message: `replaceImage: media part '${edit.mediaPartPath}' is referenced by ${edit.sharedReferenceCount} pic shapes; all references now use the replacement image.`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return warnings;
|
|
213
|
+
}
|
|
214
|
+
function dedupeCommandWarnings(warnings) {
|
|
215
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
216
|
+
for (const warning of warnings) {
|
|
217
|
+
const key = `${warning.code}\0${warning.mediaPartPath}`;
|
|
218
|
+
if (!deduped.has(key)) deduped.set(key, warning);
|
|
219
|
+
}
|
|
220
|
+
return [...deduped.values()];
|
|
221
|
+
}
|
|
222
|
+
function addTextBoxCommand(document, command) {
|
|
223
|
+
requireFiniteEmu(command.offsetX, "addTextBox", "offsetX");
|
|
224
|
+
requireFiniteEmu(command.offsetY, "addTextBox", "offsetY");
|
|
225
|
+
requirePositiveFiniteEmu(command.width, "addTextBox", "width");
|
|
226
|
+
requirePositiveFiniteEmu(command.height, "addTextBox", "height");
|
|
227
|
+
if (command.text !== void 0 && typeof command.text !== "string") {
|
|
228
|
+
throw new Error("addTextBox: text must be a string");
|
|
229
|
+
}
|
|
230
|
+
if (command.name !== void 0 && command.name.trim() === "") {
|
|
231
|
+
throw new Error("addTextBox: name must be a non-empty string when provided");
|
|
232
|
+
}
|
|
233
|
+
return (0, import_document.addTextBox)(document, command.slideHandle, command);
|
|
234
|
+
}
|
|
235
|
+
function addConnectorCommand(document, command) {
|
|
236
|
+
requireFiniteEmu(command.offsetX, "addConnector", "offsetX");
|
|
237
|
+
requireFiniteEmu(command.offsetY, "addConnector", "offsetY");
|
|
238
|
+
requirePositiveFiniteEmu(command.width, "addConnector", "width");
|
|
239
|
+
requirePositiveFiniteEmu(command.height, "addConnector", "height");
|
|
240
|
+
return (0, import_document.addConnector)(document, command.slideHandle, command);
|
|
241
|
+
}
|
|
242
|
+
function setTextRunPropertiesCommand(document, command) {
|
|
243
|
+
requireNonEmptyPropertySet(command.properties, "setTextRunProperties");
|
|
244
|
+
validateTextRunPropertySet(command.properties, "setTextRunProperties");
|
|
245
|
+
return (0, import_document.setTextRunProperties)(document, command.handle, command.properties);
|
|
246
|
+
}
|
|
247
|
+
function clearTextRunPropertiesCommand(document, command) {
|
|
248
|
+
if (command.properties.length === 0) {
|
|
249
|
+
throw new Error("clearTextRunProperties: properties must contain at least one property name");
|
|
250
|
+
}
|
|
251
|
+
for (const property of command.properties) {
|
|
252
|
+
if (!EDITABLE_TEXT_RUN_PROPERTY_SET.has(property)) {
|
|
253
|
+
throw new Error(`clearTextRunProperties: unsupported text run property '${property}'`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return (0, import_document.clearTextRunProperties)(document, command.handle, command.properties);
|
|
257
|
+
}
|
|
258
|
+
function setParagraphPropertiesCommand(document, command) {
|
|
259
|
+
requireNonEmptyParagraphPropertySet(command.properties, "setParagraphProperties");
|
|
260
|
+
validateParagraphPropertySet(command.properties, "setParagraphProperties");
|
|
261
|
+
return (0, import_document.setParagraphProperties)(document, command.handle, command.properties);
|
|
262
|
+
}
|
|
263
|
+
function clearParagraphPropertiesCommand(document, command) {
|
|
264
|
+
if (command.properties.length === 0) {
|
|
265
|
+
throw new Error("clearParagraphProperties: properties must contain at least one property name");
|
|
266
|
+
}
|
|
267
|
+
for (const property of command.properties) {
|
|
268
|
+
if (!EDITABLE_PARAGRAPH_PROPERTY_SET.has(property)) {
|
|
269
|
+
throw new Error(`clearParagraphProperties: unsupported paragraph property '${property}'`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return (0, import_document.clearParagraphProperties)(document, command.handle, command.properties);
|
|
273
|
+
}
|
|
274
|
+
function moveShape(document, command) {
|
|
275
|
+
requireFiniteEmu(command.offsetX, "moveShape", "offsetX");
|
|
276
|
+
requireFiniteEmu(command.offsetY, "moveShape", "offsetY");
|
|
277
|
+
const current = requireEditableShapeTransform(document, command.handle, "moveShape");
|
|
278
|
+
return (0, import_document.updateShapeTransform)(document, command.handle, {
|
|
279
|
+
offsetX: command.offsetX,
|
|
280
|
+
offsetY: command.offsetY,
|
|
281
|
+
width: current.width,
|
|
282
|
+
height: current.height
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function resizeShape(document, command) {
|
|
286
|
+
requirePositiveFiniteEmu(command.width, "resizeShape", "width");
|
|
287
|
+
requirePositiveFiniteEmu(command.height, "resizeShape", "height");
|
|
288
|
+
const current = requireEditableShapeTransform(document, command.handle, "resizeShape");
|
|
289
|
+
return (0, import_document.updateShapeTransform)(document, command.handle, {
|
|
290
|
+
offsetX: current.offsetX,
|
|
291
|
+
offsetY: current.offsetY,
|
|
292
|
+
width: command.width,
|
|
293
|
+
height: command.height
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function setShapeTransform(document, command) {
|
|
297
|
+
requireFiniteEmu(command.offsetX, "setShapeTransform", "offsetX");
|
|
298
|
+
requireFiniteEmu(command.offsetY, "setShapeTransform", "offsetY");
|
|
299
|
+
requirePositiveFiniteEmu(command.width, "setShapeTransform", "width");
|
|
300
|
+
requirePositiveFiniteEmu(command.height, "setShapeTransform", "height");
|
|
301
|
+
requireEditableShapeTransform(document, command.handle, "setShapeTransform");
|
|
302
|
+
return (0, import_document.updateShapeTransform)(document, command.handle, {
|
|
303
|
+
offsetX: command.offsetX,
|
|
304
|
+
offsetY: command.offsetY,
|
|
305
|
+
width: command.width,
|
|
306
|
+
height: command.height
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function setShapeFillCommand(document, command) {
|
|
310
|
+
validateShapeFill(command.fill, "setShapeFill");
|
|
311
|
+
return (0, import_document.setShapeFill)(document, command.handle, command.fill);
|
|
312
|
+
}
|
|
313
|
+
function setShapeOutlineCommand(document, command) {
|
|
314
|
+
validateShapeOutline(command.outline, "setShapeOutline");
|
|
315
|
+
return (0, import_document.setShapeOutline)(document, command.handle, command.outline);
|
|
316
|
+
}
|
|
317
|
+
var EDITABLE_TEXT_RUN_PROPERTIES = [
|
|
318
|
+
"bold",
|
|
319
|
+
"italic",
|
|
320
|
+
"underline",
|
|
321
|
+
"fontSize",
|
|
322
|
+
"color",
|
|
323
|
+
"typeface"
|
|
324
|
+
];
|
|
325
|
+
var EDITABLE_TEXT_RUN_PROPERTY_SET = new Set(EDITABLE_TEXT_RUN_PROPERTIES);
|
|
326
|
+
var EDITABLE_PARAGRAPH_PROPERTIES = [
|
|
327
|
+
"align",
|
|
328
|
+
"level",
|
|
329
|
+
"bullet"
|
|
330
|
+
];
|
|
331
|
+
var EDITABLE_PARAGRAPH_PROPERTY_SET = new Set(EDITABLE_PARAGRAPH_PROPERTIES);
|
|
332
|
+
var PARAGRAPH_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
333
|
+
var AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
|
|
334
|
+
"arabicPeriod",
|
|
335
|
+
"arabicParenR",
|
|
336
|
+
"romanUcPeriod",
|
|
337
|
+
"romanLcPeriod",
|
|
338
|
+
"alphaUcPeriod",
|
|
339
|
+
"alphaLcPeriod",
|
|
340
|
+
"alphaLcParenR",
|
|
341
|
+
"alphaUcParenR",
|
|
342
|
+
"arabicPlain"
|
|
343
|
+
]);
|
|
344
|
+
function requireNonEmptyPropertySet(properties, commandName) {
|
|
345
|
+
if (Object.values(properties).every((value) => value === void 0)) {
|
|
346
|
+
throw new Error(`${commandName}: properties must contain at least one defined property`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function validateTextRunPropertySet(properties, commandName) {
|
|
350
|
+
for (const property of Object.keys(properties)) {
|
|
351
|
+
if (!EDITABLE_TEXT_RUN_PROPERTY_SET.has(property)) {
|
|
352
|
+
throw new Error(`${commandName}: unsupported text run property '${property}'`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
requireBooleanOrUndefined(properties.bold, commandName, "bold");
|
|
356
|
+
requireBooleanOrUndefined(properties.italic, commandName, "italic");
|
|
357
|
+
requireBooleanOrUndefined(properties.underline, commandName, "underline");
|
|
358
|
+
if (properties.fontSize !== void 0 && (!Number.isFinite(properties.fontSize) || properties.fontSize <= 0)) {
|
|
359
|
+
throw new Error(`${commandName}: fontSize must be a finite positive pt value`);
|
|
360
|
+
}
|
|
361
|
+
if (properties.typeface !== void 0 && properties.typeface.trim() === "") {
|
|
362
|
+
throw new Error(`${commandName}: typeface must be a non-empty string`);
|
|
363
|
+
}
|
|
364
|
+
if (properties.color !== void 0) {
|
|
365
|
+
if (properties.color.kind !== "srgb") {
|
|
366
|
+
throw new Error(`${commandName}: only srgb text run color is supported`);
|
|
367
|
+
}
|
|
368
|
+
if (!/^[0-9A-Fa-f]{6}$/.test(properties.color.hex)) {
|
|
369
|
+
throw new Error(`${commandName}: color.hex must be a 6-digit hex value`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function requireNonEmptyParagraphPropertySet(properties, commandName) {
|
|
374
|
+
if (Object.values(properties).every((value) => value === void 0)) {
|
|
375
|
+
throw new Error(`${commandName}: properties must contain at least one defined property`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function validateParagraphPropertySet(properties, commandName) {
|
|
379
|
+
for (const property of Object.keys(properties)) {
|
|
380
|
+
if (!EDITABLE_PARAGRAPH_PROPERTY_SET.has(property)) {
|
|
381
|
+
throw new Error(`${commandName}: unsupported paragraph property '${property}'`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (properties.align !== void 0 && !PARAGRAPH_ALIGN_VALUES.has(properties.align)) {
|
|
385
|
+
throw new Error(`${commandName}: align must be left, center, right, or justify`);
|
|
386
|
+
}
|
|
387
|
+
if (properties.level !== void 0 && (!Number.isInteger(properties.level) || properties.level < 0 || properties.level > 8)) {
|
|
388
|
+
throw new Error(`${commandName}: level must be an integer from 0 to 8`);
|
|
389
|
+
}
|
|
390
|
+
if (properties.bullet !== void 0) {
|
|
391
|
+
validateParagraphBullet(properties.bullet, commandName);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function validateParagraphBullet(bullet, commandName) {
|
|
395
|
+
if (bullet.type === "none") return;
|
|
396
|
+
if (bullet.type === "char") {
|
|
397
|
+
if (bullet.char.length === 0) {
|
|
398
|
+
throw new Error(`${commandName}: bullet.char must be a non-empty string`);
|
|
399
|
+
}
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (bullet.type === "autoNum") {
|
|
403
|
+
if (!AUTO_NUM_SCHEMES.has(bullet.scheme)) {
|
|
404
|
+
throw new Error(`${commandName}: unsupported bullet auto-numbering scheme`);
|
|
405
|
+
}
|
|
406
|
+
if (!Number.isInteger(bullet.startAt) || bullet.startAt < 1) {
|
|
407
|
+
throw new Error(`${commandName}: bullet.startAt must be a positive integer`);
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
throw new Error(`${commandName}: unsupported bullet type`);
|
|
412
|
+
}
|
|
413
|
+
function requireBooleanOrUndefined(value, commandName, fieldName) {
|
|
414
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
415
|
+
throw new Error(`${commandName}: ${fieldName} must be a boolean value`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function validateShapeOutline(outline, commandName) {
|
|
419
|
+
if (outline.width === void 0 && outline.fill === void 0) {
|
|
420
|
+
throw new Error(`${commandName}: outline must set width or fill`);
|
|
421
|
+
}
|
|
422
|
+
if (outline.width !== void 0) {
|
|
423
|
+
requirePositiveFiniteEmu(outline.width, commandName, "width");
|
|
424
|
+
}
|
|
425
|
+
if (outline.fill !== void 0) validateShapeFill(outline.fill, commandName);
|
|
426
|
+
}
|
|
427
|
+
function validateShapeFill(fill, commandName) {
|
|
428
|
+
if (fill.kind === "none") return;
|
|
429
|
+
if (fill.kind !== "solid") {
|
|
430
|
+
throw new Error(`${commandName}: only solid and none fills are supported`);
|
|
431
|
+
}
|
|
432
|
+
if (fill.color.kind !== "srgb") {
|
|
433
|
+
throw new Error(`${commandName}: only srgb solid fill colors are supported`);
|
|
434
|
+
}
|
|
435
|
+
if (!/^[0-9A-Fa-f]{6}$/.test(fill.color.hex)) {
|
|
436
|
+
throw new Error(`${commandName}: color.hex must be a 6-digit hex value`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function requireEditableShapeTransform(document, handle, commandName) {
|
|
440
|
+
const shape = (0, import_document.findShapeNodeBySourceHandle)(document, handle);
|
|
441
|
+
if (shape === void 0) {
|
|
442
|
+
throw new Error(`${commandName}: shape handle was not found in PptxSourceModel source`);
|
|
443
|
+
}
|
|
444
|
+
if (!hasTransform(shape)) {
|
|
445
|
+
throw new Error(`${commandName}: shape handle does not reference a shape with xfrm`);
|
|
446
|
+
}
|
|
447
|
+
return shape.transform;
|
|
448
|
+
}
|
|
449
|
+
function requireFiniteEmu(value, commandName, fieldName) {
|
|
450
|
+
if (!Number.isFinite(value)) {
|
|
451
|
+
throw new Error(`${commandName}: ${fieldName} must be a finite EMU value`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function requirePositiveFiniteEmu(value, commandName, fieldName) {
|
|
455
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
456
|
+
throw new Error(`${commandName}: ${fieldName} must be a finite positive EMU value`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function hasTransform(shape) {
|
|
460
|
+
return shape.kind !== "raw" && shape.transform !== void 0;
|
|
461
|
+
}
|
|
462
|
+
function normalizeEditorEdits(document) {
|
|
463
|
+
const edits = document.edits;
|
|
464
|
+
if (edits === void 0) return document;
|
|
465
|
+
const seenTextRuns = /* @__PURE__ */ new Set();
|
|
466
|
+
const seenTextRunProperties = /* @__PURE__ */ new Map();
|
|
467
|
+
const seenParagraphProperties = /* @__PURE__ */ new Map();
|
|
468
|
+
const seenParagraphs = /* @__PURE__ */ new Set();
|
|
469
|
+
const seenShapeTransforms = /* @__PURE__ */ new Set();
|
|
470
|
+
const seenShapeFills = /* @__PURE__ */ new Set();
|
|
471
|
+
const seenShapeOutlineProperties = /* @__PURE__ */ new Map();
|
|
472
|
+
const normalizedShapeOutlineEdits = /* @__PURE__ */ new Map();
|
|
473
|
+
const normalizedReversed = [];
|
|
474
|
+
let changed = false;
|
|
475
|
+
for (let index = edits.length - 1; index >= 0; index -= 1) {
|
|
476
|
+
const edit = edits[index];
|
|
477
|
+
if (edit.kind === "replaceTextRunPlainText") {
|
|
478
|
+
const key = editHandleNodeKey(edit);
|
|
479
|
+
const paragraphKey = textRunParagraphEditKey(edit);
|
|
480
|
+
if (paragraphKey !== void 0 && seenParagraphs.has(paragraphKey)) {
|
|
481
|
+
changed = true;
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (seenTextRuns.has(key)) {
|
|
485
|
+
changed = true;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
seenTextRuns.add(key);
|
|
489
|
+
}
|
|
490
|
+
if (edit.kind === "updateTextRunProperties") {
|
|
491
|
+
const paragraphKey = textRunParagraphEditKey(edit);
|
|
492
|
+
if (paragraphKey !== void 0 && seenParagraphs.has(paragraphKey)) {
|
|
493
|
+
changed = true;
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const normalized = normalizeTextRunPropertiesEdit(edit, seenTextRunProperties);
|
|
497
|
+
if (normalized === void 0) {
|
|
498
|
+
changed = true;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
if (!editorEditsEqual(normalized, edit)) changed = true;
|
|
502
|
+
normalizedReversed.push(normalized);
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (edit.kind === "updateParagraphProperties") {
|
|
506
|
+
const normalized = normalizeParagraphPropertiesEdit(edit, seenParagraphProperties);
|
|
507
|
+
if (normalized === void 0) {
|
|
508
|
+
changed = true;
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (!editorEditsEqual(normalized, edit)) changed = true;
|
|
512
|
+
normalizedReversed.push(normalized);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (edit.kind === "replaceParagraphPlainText") {
|
|
516
|
+
const key = editHandleNodeKey(edit);
|
|
517
|
+
if (seenParagraphs.has(key)) {
|
|
518
|
+
changed = true;
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
seenParagraphs.add(key);
|
|
522
|
+
}
|
|
523
|
+
if (edit.kind === "updateShapeTransform") {
|
|
524
|
+
const key = editHandleNodeKey(edit);
|
|
525
|
+
if (seenShapeTransforms.has(key)) {
|
|
526
|
+
changed = true;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
seenShapeTransforms.add(key);
|
|
530
|
+
}
|
|
531
|
+
if (edit.kind === "updateShapeFill") {
|
|
532
|
+
const key = editHandleNodeKey(edit);
|
|
533
|
+
if (seenShapeFills.has(key)) {
|
|
534
|
+
changed = true;
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
seenShapeFills.add(key);
|
|
538
|
+
}
|
|
539
|
+
if (edit.kind === "updateShapeOutline") {
|
|
540
|
+
const normalized = normalizeShapeOutlineEdit(
|
|
541
|
+
edit,
|
|
542
|
+
seenShapeOutlineProperties,
|
|
543
|
+
normalizedShapeOutlineEdits
|
|
544
|
+
);
|
|
545
|
+
if (normalized === void 0) {
|
|
546
|
+
changed = true;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (normalized.merged) {
|
|
550
|
+
changed = true;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (!editorEditsEqual(normalized.edit, edit)) changed = true;
|
|
554
|
+
normalizedReversed.push(normalized.edit);
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
normalizedReversed.push(edit);
|
|
558
|
+
}
|
|
559
|
+
if (!changed && normalizedReversed.length === edits.length) return document;
|
|
560
|
+
return {
|
|
561
|
+
...document,
|
|
562
|
+
edits: normalizedReversed.reverse()
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function assertNoConflictingParagraphAndRunEdits(edits) {
|
|
566
|
+
if (edits === void 0) return;
|
|
567
|
+
const paragraphEditIndexes = /* @__PURE__ */ new Map();
|
|
568
|
+
edits.forEach((edit, index) => {
|
|
569
|
+
if (edit.kind === "replaceParagraphPlainText") {
|
|
570
|
+
paragraphEditIndexes.set(editHandleNodeKey(edit), index);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
for (const [index, edit] of edits.entries()) {
|
|
574
|
+
if (edit.kind !== "replaceTextRunPlainText" && edit.kind !== "updateTextRunProperties") {
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
const paragraphKey = textRunParagraphEditKey(edit);
|
|
578
|
+
const paragraphIndex = paragraphKey === void 0 ? void 0 : paragraphEditIndexes.get(paragraphKey);
|
|
579
|
+
if (paragraphIndex !== void 0 && (edit.kind === "updateTextRunProperties" || paragraphIndex < index)) {
|
|
580
|
+
throw new Error(
|
|
581
|
+
`${edit.kind}: run edits cannot be combined with replaceParagraphPlainText for the same paragraph`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
function editorEditsEqual(left, right) {
|
|
587
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
588
|
+
}
|
|
589
|
+
function editHandleNodeKey(edit) {
|
|
590
|
+
return [
|
|
591
|
+
edit.handle.partPath,
|
|
592
|
+
edit.handle.nodeId ?? "",
|
|
593
|
+
edit.handle.relationshipId ?? "",
|
|
594
|
+
edit.handle.orderingSlot ?? ""
|
|
595
|
+
].join("\0");
|
|
596
|
+
}
|
|
597
|
+
function textRunParagraphEditKey(edit) {
|
|
598
|
+
const nodeId = String(edit.handle.nodeId ?? "");
|
|
599
|
+
const byShapeId = /^(text:shape:.+:p:(\d+)):r:\d+$/.exec(nodeId);
|
|
600
|
+
const byShapeSlot = /^(text:shapeSlot:\d+:p:(\d+)):r:\d+$/.exec(nodeId);
|
|
601
|
+
const paragraphNodeId = byShapeId?.[1] ?? byShapeSlot?.[1];
|
|
602
|
+
const paragraphOrderingSlot = byShapeId?.[2] ?? byShapeSlot?.[2] ?? "";
|
|
603
|
+
if (paragraphNodeId === void 0) return void 0;
|
|
604
|
+
return [
|
|
605
|
+
edit.handle.partPath,
|
|
606
|
+
paragraphNodeId,
|
|
607
|
+
edit.handle.relationshipId ?? "",
|
|
608
|
+
paragraphOrderingSlot
|
|
609
|
+
].join("\0");
|
|
610
|
+
}
|
|
611
|
+
function normalizeTextRunPropertiesEdit(edit, seenTextRunProperties) {
|
|
612
|
+
const key = editHandleNodeKey(edit);
|
|
613
|
+
let seenProperties = seenTextRunProperties.get(key);
|
|
614
|
+
if (seenProperties === void 0) {
|
|
615
|
+
seenProperties = /* @__PURE__ */ new Set();
|
|
616
|
+
seenTextRunProperties.set(key, seenProperties);
|
|
617
|
+
}
|
|
618
|
+
const set = {};
|
|
619
|
+
if (edit.set?.bold !== void 0 && !seenProperties.has("bold")) {
|
|
620
|
+
seenProperties.add("bold");
|
|
621
|
+
set.bold = edit.set.bold;
|
|
622
|
+
}
|
|
623
|
+
if (edit.set?.italic !== void 0 && !seenProperties.has("italic")) {
|
|
624
|
+
seenProperties.add("italic");
|
|
625
|
+
set.italic = edit.set.italic;
|
|
626
|
+
}
|
|
627
|
+
if (edit.set?.underline !== void 0 && !seenProperties.has("underline")) {
|
|
628
|
+
seenProperties.add("underline");
|
|
629
|
+
set.underline = edit.set.underline;
|
|
630
|
+
}
|
|
631
|
+
if (edit.set?.fontSize !== void 0 && !seenProperties.has("fontSize")) {
|
|
632
|
+
seenProperties.add("fontSize");
|
|
633
|
+
set.fontSize = edit.set.fontSize;
|
|
634
|
+
}
|
|
635
|
+
if (edit.set?.color !== void 0 && !seenProperties.has("color")) {
|
|
636
|
+
seenProperties.add("color");
|
|
637
|
+
set.color = edit.set.color;
|
|
638
|
+
}
|
|
639
|
+
if (edit.set?.typeface !== void 0 && !seenProperties.has("typeface")) {
|
|
640
|
+
seenProperties.add("typeface");
|
|
641
|
+
set.typeface = edit.set.typeface;
|
|
642
|
+
}
|
|
643
|
+
const clear = (edit.clear ?? []).filter((property) => !seenProperties.has(property));
|
|
644
|
+
for (const property of clear) seenProperties.add(property);
|
|
645
|
+
if (clear.length === 0 && Object.keys(set).length === 0) return void 0;
|
|
646
|
+
const normalized = {
|
|
647
|
+
kind: "updateTextRunProperties",
|
|
648
|
+
handle: edit.handle,
|
|
649
|
+
...Object.keys(set).length > 0 ? { set } : {},
|
|
650
|
+
...clear.length > 0 ? { clear } : {}
|
|
651
|
+
};
|
|
652
|
+
return normalized;
|
|
653
|
+
}
|
|
654
|
+
function normalizeParagraphPropertiesEdit(edit, seenParagraphProperties) {
|
|
655
|
+
const key = editHandleNodeKey(edit);
|
|
656
|
+
let seenProperties = seenParagraphProperties.get(key);
|
|
657
|
+
if (seenProperties === void 0) {
|
|
658
|
+
seenProperties = /* @__PURE__ */ new Set();
|
|
659
|
+
seenParagraphProperties.set(key, seenProperties);
|
|
660
|
+
}
|
|
661
|
+
const set = {};
|
|
662
|
+
if (edit.set?.align !== void 0 && !seenProperties.has("align")) {
|
|
663
|
+
seenProperties.add("align");
|
|
664
|
+
set.align = edit.set.align;
|
|
665
|
+
}
|
|
666
|
+
if (edit.set?.level !== void 0 && !seenProperties.has("level")) {
|
|
667
|
+
seenProperties.add("level");
|
|
668
|
+
set.level = edit.set.level;
|
|
669
|
+
}
|
|
670
|
+
if (edit.set?.bullet !== void 0 && !seenProperties.has("bullet")) {
|
|
671
|
+
seenProperties.add("bullet");
|
|
672
|
+
set.bullet = edit.set.bullet;
|
|
673
|
+
}
|
|
674
|
+
const clear = (edit.clear ?? []).filter((property) => !seenProperties.has(property));
|
|
675
|
+
for (const property of clear) seenProperties.add(property);
|
|
676
|
+
if (clear.length === 0 && Object.keys(set).length === 0) return void 0;
|
|
677
|
+
const normalized = {
|
|
678
|
+
kind: "updateParagraphProperties",
|
|
679
|
+
handle: edit.handle,
|
|
680
|
+
...Object.keys(set).length > 0 ? { set } : {},
|
|
681
|
+
...clear.length > 0 ? { clear } : {}
|
|
682
|
+
};
|
|
683
|
+
return normalized;
|
|
684
|
+
}
|
|
685
|
+
function normalizeShapeOutlineEdit(edit, seenShapeOutlineProperties, normalizedShapeOutlineEdits) {
|
|
686
|
+
const key = editHandleNodeKey(edit);
|
|
687
|
+
let seenProperties = seenShapeOutlineProperties.get(key);
|
|
688
|
+
if (seenProperties === void 0) {
|
|
689
|
+
seenProperties = /* @__PURE__ */ new Set();
|
|
690
|
+
seenShapeOutlineProperties.set(key, seenProperties);
|
|
691
|
+
}
|
|
692
|
+
const outline = {};
|
|
693
|
+
if (edit.outline.width !== void 0 && !seenProperties.has("width")) {
|
|
694
|
+
seenProperties.add("width");
|
|
695
|
+
outline.width = edit.outline.width;
|
|
696
|
+
}
|
|
697
|
+
if (edit.outline.fill !== void 0 && !seenProperties.has("fill")) {
|
|
698
|
+
seenProperties.add("fill");
|
|
699
|
+
outline.fill = edit.outline.fill;
|
|
700
|
+
}
|
|
701
|
+
if (Object.keys(outline).length === 0) return void 0;
|
|
702
|
+
const existing = normalizedShapeOutlineEdits.get(key);
|
|
703
|
+
if (existing !== void 0) {
|
|
704
|
+
existing.outline = { ...outline, ...existing.outline };
|
|
705
|
+
return { merged: true };
|
|
706
|
+
}
|
|
707
|
+
const normalized = {
|
|
708
|
+
kind: "updateShapeOutline",
|
|
709
|
+
handle: edit.handle,
|
|
710
|
+
outline
|
|
711
|
+
};
|
|
712
|
+
normalizedShapeOutlineEdits.set(key, normalized);
|
|
713
|
+
return { edit: normalized, merged: false };
|
|
714
|
+
}
|
|
715
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
716
|
+
0 && (module.exports = {
|
|
717
|
+
EditorSession,
|
|
718
|
+
createEditorSession
|
|
719
|
+
});
|