pptx-viewer-mcp 2.5.8 → 2.6.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/dist/index.js CHANGED
@@ -1,3214 +1,100 @@
1
1
  'use strict';
2
2
 
3
- var pptxViewerCore = require('pptx-viewer-core');
3
+ var automation = require('ooxml-core/pptx/automation');
4
+ var pptx = require('ooxml-core/pptx');
4
5
 
5
6
  // src/execution.ts
6
- var PPTX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
7
- async function loadPresentation(filePath, execCtx) {
8
- const rawBytes = await execCtx.filesystem.readFile(filePath);
9
- if (execCtx.collaboration) {
10
- const room = execCtx.collaboration.getRoom(filePath);
11
- if (room) {
12
- const codec = execCtx.collaboration.getCodec(filePath);
13
- if (codec) {
14
- const currentBytes = await codec.dehydrate(room.ydoc);
15
- const handler2 = new pptxViewerCore.PptxHandler();
16
- const pptxData2 = await handler2.load(currentBytes.buffer);
17
- return { pptxData: pptxData2, rawBytes };
18
- }
19
- }
20
- }
21
- const handler = new pptxViewerCore.PptxHandler();
22
- const pptxData = await handler.load(rawBytes.buffer);
23
- return { pptxData, rawBytes };
24
- }
25
- async function savePresentation(filePath, pptxData, rawBytes, execCtx, tableStyleSaveOptions) {
26
- const handler = new pptxViewerCore.PptxHandler();
27
- await handler.load(rawBytes.buffer);
28
- const outputBytes = await handler.save(pptxData.slides, {
29
- headerFooter: pptxData.headerFooter,
30
- // `pptxData.tableStyleMap` carries any in-memory table-style edits a
31
- // tool made (e.g. `set_table_style_section`, `create_table_style`);
32
- // without forwarding it here those edits were silently dropped on
33
- // save even though the core save pipeline already supports them.
34
- tableStyles: pptxData.tableStyleMap,
35
- ...tableStyleSaveOptions
36
- });
37
- if (execCtx.collaboration) {
38
- const room = execCtx.collaboration.getRoom(filePath);
39
- if (room) {
40
- const codec = execCtx.collaboration.getCodec(filePath);
41
- if (codec) {
42
- const origin = execCtx.collaboration.agentOrigin(execCtx.agentName ?? "pptx-tool");
43
- await codec.hydrate(room.ydoc, outputBytes, origin);
44
- if (execCtx.viewer) {
45
- execCtx.viewer.replaceContent(filePath, outputBytes, {
46
- markDirty: false,
47
- mimeType: PPTX_MIME_TYPE
48
- });
49
- }
50
- return { savedToDisk: false, routedThroughCollaboration: true };
51
- }
52
- }
53
- }
54
- await execCtx.filesystem.writeFile(filePath, outputBytes);
55
- if (execCtx.viewer) {
56
- execCtx.viewer.replaceContent(filePath, outputBytes, {
57
- markDirty: false,
58
- mimeType: PPTX_MIME_TYPE
59
- });
60
- }
61
- return { savedToDisk: true, routedThroughCollaboration: false };
62
- }
63
- async function executeToolWithContext(filePath, execCtx, toolFn) {
64
- const { pptxData, rawBytes } = await loadPresentation(filePath, execCtx);
65
- const toolResult = await toolFn({ pptxData });
66
- if (toolResult.dirty) {
67
- const saveResult = await savePresentation(
68
- filePath,
69
- toolResult.pptxData,
70
- rawBytes,
71
- execCtx,
72
- toolResult.saveOptions
73
- );
74
- return { ...toolResult.result, ...saveResult };
75
- }
76
- return toolResult.result;
77
- }
78
- function generateElementId() {
79
- return `el-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
80
- }
81
- function generateSlideId() {
82
- return `slide-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
83
- }
84
- function describeElement(el) {
85
- const base = {
86
- id: el.id,
87
- type: el.type,
88
- x: Math.round(el.x),
89
- y: Math.round(el.y),
90
- width: Math.round(el.width),
91
- height: Math.round(el.height)
92
- };
93
- if (el.rotation) {
94
- base.rotation = el.rotation;
95
- }
96
- if (el.hidden) {
97
- base.hidden = true;
98
- }
99
- if (el.opacity !== void 0 && el.opacity !== 1) {
100
- base.opacity = el.opacity;
101
- }
102
- if (pptxViewerCore.hasTextProperties(el)) {
103
- base.text = el.text ?? "";
104
- if (el.textStyle) {
105
- const ts = {};
106
- if (el.textStyle.fontSize) {
107
- ts.fontSize = el.textStyle.fontSize;
108
- }
109
- if (el.textStyle.fontFamily) {
110
- ts.fontFamily = el.textStyle.fontFamily;
111
- }
112
- if (el.textStyle.bold) {
113
- ts.bold = true;
114
- }
115
- if (el.textStyle.italic) {
116
- ts.italic = true;
117
- }
118
- if (el.textStyle.underline) {
119
- ts.underline = el.textStyle.underline;
120
- }
121
- if (el.textStyle.align) {
122
- ts.align = el.textStyle.align;
123
- }
124
- if (el.textStyle.color) {
125
- ts.color = el.textStyle.color;
126
- }
127
- if (Object.keys(ts).length > 0) {
128
- base.textStyle = ts;
129
- }
130
- }
131
- }
132
- if ("shapeType" in el && el.shapeType) {
133
- base.shapeType = el.shapeType;
134
- }
135
- if ("shapeStyle" in el && el.shapeStyle) {
136
- const ss = {};
137
- const style = el.shapeStyle;
138
- if (style.fillColor) {
139
- ss.fillColor = style.fillColor;
140
- }
141
- if (style.strokeColor) {
142
- ss.strokeColor = style.strokeColor;
143
- }
144
- if (style.strokeWidth) {
145
- ss.strokeWidth = style.strokeWidth;
146
- }
147
- if (Object.keys(ss).length > 0) {
148
- base.shapeStyle = ss;
149
- }
150
- }
151
- if (el.type === "image" || el.type === "picture") {
152
- const img = el;
153
- if (img.altText) {
154
- base.altText = img.altText;
155
- }
156
- if (img.imagePath) {
157
- base.imagePath = img.imagePath;
158
- }
159
- base.hasImageData = Boolean(img.imageData);
160
- }
161
- if (el.type === "table") {
162
- const tbl = el;
163
- if (tbl.tableData) {
164
- base.rowCount = tbl.tableData.rows.length;
165
- base.columnCount = tbl.tableData.columnWidths.length;
166
- base.cells = tbl.tableData.rows.map((row) => row.cells.map((cell) => cell.text ?? ""));
167
- }
168
- }
169
- if (el.type === "chart" && el.chartData) {
170
- base.chartType = pptxViewerCore.resolveDisplayedChartTypeName(el.chartData);
171
- }
172
- if (el.type === "group") {
173
- const grp = el;
174
- base.childCount = grp.children?.length ?? 0;
175
- }
176
- return base;
177
- }
178
- function extractSlideText(slide) {
179
- const texts = [];
180
- for (const el of slide.elements) {
181
- if (pptxViewerCore.hasTextProperties(el) && el.text) {
182
- texts.push(el.text);
183
- }
184
- if (el.type === "table") {
185
- const tbl = el;
186
- if (tbl.tableData) {
187
- for (const row of tbl.tableData.rows) {
188
- for (const cell of row.cells) {
189
- if (cell.text) {
190
- texts.push(cell.text);
191
- }
192
- }
193
- }
194
- }
195
- }
196
- }
197
- if (slide.notes) {
198
- texts.push(slide.notes);
199
- }
200
- return texts.join(" ");
201
- }
202
- function validateSlideIndex(slideIndex, slideCount) {
203
- if (slideIndex < 0 || slideIndex >= slideCount) {
204
- return `Slide index ${slideIndex} out of range (0\u2013${slideCount - 1}).`;
205
- }
206
- return null;
207
- }
208
-
209
- // src/tools/slide-tools.ts
210
- function getSlide(ctx, params) {
211
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
212
- if (err) {
213
- throw new Error(err);
214
- }
215
- const slide = ctx.pptxData.slides[params.slideIndex];
216
- return {
217
- pptxData: ctx.pptxData,
218
- dirty: false,
219
- result: {
220
- slideIndex: params.slideIndex,
221
- slideNumber: slide.slideNumber,
222
- hidden: slide.hidden ?? false,
223
- layoutName: slide.layoutName,
224
- backgroundColor: slide.backgroundColor,
225
- notes: slide.notes ?? "",
226
- commentCount: slide.comments?.length ?? 0,
227
- elements: slide.elements.map(describeElement)
228
- }
229
- };
230
- }
231
- function addSlide(ctx, params) {
232
- const { slides } = ctx.pptxData;
233
- const insertAt = params.insertAfterIndex !== void 0 && params.insertAfterIndex >= 0 ? Math.min(params.insertAfterIndex + 1, slides.length) : slides.length;
234
- const newSlide = {
235
- id: generateSlideId(),
236
- rId: "",
237
- slideNumber: insertAt + 1,
238
- elements: [],
239
- backgroundColor: params.backgroundColor
240
- };
241
- slides.splice(insertAt, 0, newSlide);
242
- slides.forEach((s, i) => {
243
- s.slideNumber = i + 1;
244
- });
245
- return {
246
- pptxData: ctx.pptxData,
247
- dirty: true,
248
- result: { newSlideIndex: insertAt, slideCount: slides.length }
249
- };
250
- }
251
- function deleteSlides(ctx, params) {
252
- const { slides } = ctx.pptxData;
253
- if (!params.slideIndexes || params.slideIndexes.length === 0) {
254
- throw new Error("slideIndexes is required for the delete action.");
255
- }
256
- const indexSet = new Set(params.slideIndexes);
257
- const invalid = params.slideIndexes.filter((i) => i < 0 || i >= slides.length);
258
- if (invalid.length > 0) {
259
- throw new Error(
260
- `Invalid slide indexes: ${invalid.join(", ")}. Valid range: 0\u2013${slides.length - 1}.`
261
- );
262
- }
263
- if (indexSet.size >= slides.length) {
264
- throw new Error("Cannot delete all slides. At least one slide must remain.");
265
- }
266
- ctx.pptxData.slides = slides.filter((_, i) => !indexSet.has(i));
267
- ctx.pptxData.slides.forEach((s, i) => {
268
- s.slideNumber = i + 1;
269
- });
270
- return {
271
- pptxData: ctx.pptxData,
272
- dirty: true,
273
- result: { deletedCount: indexSet.size, slideCount: ctx.pptxData.slides.length }
274
- };
275
- }
276
- function reorderSlides(ctx, params) {
277
- const { slides } = ctx.pptxData;
278
- if (!params.newOrder || params.newOrder.length !== slides.length) {
279
- throw new Error(
280
- `newOrder must have exactly ${slides.length} indexes. Got ${params.newOrder?.length ?? 0}.`
281
- );
282
- }
283
- const sorted = [...params.newOrder].sort((a, b) => a - b);
284
- const expected = slides.map((_, i) => i);
285
- if (JSON.stringify(sorted) !== JSON.stringify(expected)) {
286
- throw new Error(
287
- `newOrder must contain each index from 0 to ${slides.length - 1} exactly once.`
288
- );
289
- }
290
- ctx.pptxData.slides = params.newOrder.map((idx) => slides[idx]);
291
- ctx.pptxData.slides.forEach((s, i) => {
292
- s.slideNumber = i + 1;
293
- });
294
- return {
295
- pptxData: ctx.pptxData,
296
- dirty: true,
297
- result: { slideCount: ctx.pptxData.slides.length }
298
- };
299
- }
300
- function duplicateSlide(ctx, params) {
301
- const { slides } = ctx.pptxData;
302
- const err = validateSlideIndex(params.slideIndex, slides.length);
303
- if (err) {
304
- throw new Error(err);
305
- }
306
- const original = slides[params.slideIndex];
307
- const clone = structuredClone(original);
308
- clone.id = generateSlideId();
309
- clone.rId = "";
310
- for (const el of clone.elements) {
311
- el.id = generateElementId();
312
- }
313
- const insertIndex = params.targetIndex ?? params.slideIndex + 1;
314
- slides.splice(insertIndex, 0, clone);
315
- slides.forEach((s, i) => {
316
- s.slideNumber = i + 1;
317
- });
318
- return {
319
- pptxData: ctx.pptxData,
320
- dirty: true,
321
- result: { newSlideIndex: insertIndex, slideCount: slides.length }
322
- };
323
- }
324
- function updateSlideProperties(ctx, params) {
325
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
326
- if (err) {
327
- throw new Error(err);
328
- }
329
- const slide = ctx.pptxData.slides[params.slideIndex];
330
- if (params.backgroundColor !== void 0) {
331
- slide.backgroundColor = params.backgroundColor;
332
- }
333
- if (params.backgroundGradient !== void 0) {
334
- slide.backgroundGradient = params.backgroundGradient;
335
- }
336
- if (params.backgroundImage !== void 0) {
337
- slide.backgroundImage = params.backgroundImage;
338
- }
339
- if (params.notes !== void 0) {
340
- slide.notes = params.notes;
341
- }
342
- if (params.hidden !== void 0) {
343
- slide.hidden = params.hidden;
344
- }
345
- return {
346
- pptxData: ctx.pptxData,
347
- dirty: true,
348
- result: { slideIndex: params.slideIndex }
349
- };
350
- }
351
- function setSlideTransition(ctx, params) {
352
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
353
- if (err) {
354
- throw new Error(err);
355
- }
356
- const slide = ctx.pptxData.slides[params.slideIndex];
357
- if (params.type === "none") {
358
- slide.transition = void 0;
359
- } else {
360
- const transition = {
361
- type: params.type
362
- };
363
- if (params.durationMs !== void 0) {
364
- transition.durationMs = params.durationMs;
365
- }
366
- if (params.direction !== void 0) {
367
- transition.direction = params.direction;
368
- }
369
- if (params.advanceOnClick !== void 0) {
370
- transition.advanceOnClick = params.advanceOnClick;
371
- }
372
- if (params.advanceAfterMs !== void 0) {
373
- transition.advanceAfterMs = params.advanceAfterMs;
374
- }
375
- slide.transition = transition;
376
- }
377
- return {
378
- pptxData: ctx.pptxData,
379
- dirty: true,
380
- result: { slideIndex: params.slideIndex, transitionType: params.type }
381
- };
382
- }
383
- function setCanvasSize(ctx, params) {
384
- ctx.pptxData.width = params.width;
385
- ctx.pptxData.height = params.height;
386
- if (ctx.pptxData.widthEmu !== void 0) {
387
- ctx.pptxData.widthEmu = Math.round(params.width * 12700);
388
- }
389
- if (ctx.pptxData.heightEmu !== void 0) {
390
- ctx.pptxData.heightEmu = Math.round(params.height * 12700);
391
- }
392
- return {
393
- pptxData: ctx.pptxData,
394
- dirty: true,
395
- result: { canvasWidth: params.width, canvasHeight: params.height }
396
- };
397
- }
398
- var ALT_TEXT_ELEMENT_TYPES = /* @__PURE__ */ new Set(["image", "picture", "table", "chart", "smartArt", "ole", "media", "text", "shape", "connector"]);
399
- var TITLE_ELEMENT_TYPES = /* @__PURE__ */ new Set([
400
- "table",
401
- "chart",
402
- "smartArt",
403
- "ole",
404
- "media",
405
- "text",
406
- "shape",
407
- "connector"
408
- ]);
409
- function applyElementAltText(el, altText) {
410
- if (!ALT_TEXT_ELEMENT_TYPES.has(el.type)) {
411
- throw new Error(
412
- `Element '${el.id}' (${el.type}) does not support altText; only ${[...ALT_TEXT_ELEMENT_TYPES].join(", ")} elements do.`
413
- );
414
- }
415
- el.altText = altText;
416
- }
417
- function applyElementTitle(el, title) {
418
- if (!TITLE_ELEMENT_TYPES.has(el.type)) {
419
- throw new Error(
420
- `Element '${el.id}' (${el.type}) does not support title; only ${[...TITLE_ELEMENT_TYPES].join(", ")} elements do.`
421
- );
422
- }
423
- el.title = title;
424
- }
425
- function updateElementStyle(ctx, params) {
426
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
427
- if (err) {
428
- throw new Error(err);
429
- }
430
- const slide = ctx.pptxData.slides[params.slideIndex];
431
- const el = slide.elements.find((e) => e.id === params.elementId);
432
- if (!el) {
433
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
434
- }
435
- if ("shapeStyle" in el) {
436
- if (!el.shapeStyle) {
437
- el.shapeStyle = {};
438
- }
439
- const ss = el.shapeStyle;
440
- if (params.fillThemeColor !== void 0) {
441
- ss.fillColorRef = params.fillThemeColor;
442
- const resolved = pptxViewerCore.resolveThemeColorRef(params.fillThemeColor, ctx.pptxData.themeColorMap);
443
- if (params.fillColor !== void 0) {
444
- ss.fillColor = params.fillColor;
445
- } else if (resolved) {
446
- ss.fillColor = resolved;
447
- }
448
- } else if (params.fillColor !== void 0) {
449
- ss.fillColor = params.fillColor;
450
- ss.fillColorRef = void 0;
451
- }
452
- if (params.fillMode !== void 0) {
453
- ss.fillMode = params.fillMode;
454
- }
455
- if (params.fillGradientStops !== void 0) {
456
- ss.fillGradientStops = params.fillGradientStops;
457
- }
458
- if (params.fillGradientAngle !== void 0) {
459
- ss.fillGradientAngle = params.fillGradientAngle;
460
- }
461
- if (params.fillGradientType !== void 0) {
462
- ss.fillGradientType = params.fillGradientType;
463
- }
464
- if (params.fillOpacity !== void 0) {
465
- ss.fillOpacity = params.fillOpacity;
466
- }
467
- if (params.strokeThemeColor !== void 0) {
468
- ss.strokeColorRef = params.strokeThemeColor;
469
- const resolved = pptxViewerCore.resolveThemeColorRef(params.strokeThemeColor, ctx.pptxData.themeColorMap);
470
- if (params.strokeColor !== void 0) {
471
- ss.strokeColor = params.strokeColor;
472
- } else if (resolved) {
473
- ss.strokeColor = resolved;
474
- }
475
- } else if (params.strokeColor !== void 0) {
476
- ss.strokeColor = params.strokeColor;
477
- ss.strokeColorRef = void 0;
478
- }
479
- if (params.strokeWidth !== void 0) {
480
- ss.strokeWidth = params.strokeWidth;
481
- }
482
- if (params.strokeDash !== void 0) {
483
- ss.strokeDash = params.strokeDash;
484
- }
485
- if (params.strokeOpacity !== void 0) {
486
- ss.strokeOpacity = params.strokeOpacity;
487
- }
488
- if (params.shadowColor !== void 0) {
489
- ss.shadowColor = params.shadowColor;
490
- }
491
- if (params.shadowBlur !== void 0) {
492
- ss.shadowBlur = params.shadowBlur;
493
- }
494
- if (params.shadowOffsetX !== void 0) {
495
- ss.shadowOffsetX = params.shadowOffsetX;
496
- }
497
- if (params.shadowOffsetY !== void 0) {
498
- ss.shadowOffsetY = params.shadowOffsetY;
499
- }
500
- if (params.shadowOpacity !== void 0) {
501
- ss.shadowOpacity = params.shadowOpacity;
502
- }
503
- if (params.glowColor !== void 0) {
504
- ss.glowColor = params.glowColor;
505
- }
506
- if (params.glowRadius !== void 0) {
507
- ss.glowRadius = params.glowRadius;
508
- }
509
- if (params.glowOpacity !== void 0) {
510
- ss.glowOpacity = params.glowOpacity;
511
- }
512
- if (params.softEdgeRadius !== void 0) {
513
- ss.softEdgeRadius = params.softEdgeRadius;
514
- }
515
- }
516
- if (params.altText !== void 0) {
517
- applyElementAltText(el, params.altText);
518
- }
519
- if (params.title !== void 0) {
520
- applyElementTitle(el, params.title);
521
- }
522
- if (el.type === "image" || el.type === "picture") {
523
- const img = el;
524
- if (params.cropLeft !== void 0) {
525
- img.cropLeft = params.cropLeft;
526
- }
527
- if (params.cropTop !== void 0) {
528
- img.cropTop = params.cropTop;
529
- }
530
- if (params.cropRight !== void 0) {
531
- img.cropRight = params.cropRight;
532
- }
533
- if (params.cropBottom !== void 0) {
534
- img.cropBottom = params.cropBottom;
535
- }
536
- if (params.brightness !== void 0 || params.contrast !== void 0 || params.grayscale !== void 0) {
537
- if (!img.imageEffects) {
538
- img.imageEffects = {};
539
- }
540
- if (params.brightness !== void 0) {
541
- img.imageEffects.brightness = params.brightness;
542
- }
543
- if (params.contrast !== void 0) {
544
- img.imageEffects.contrast = params.contrast;
545
- }
546
- if (params.grayscale !== void 0) {
547
- img.imageEffects.grayscale = params.grayscale;
548
- }
549
- }
550
- }
551
- return {
552
- pptxData: ctx.pptxData,
553
- dirty: true,
554
- result: { elementId: params.elementId }
555
- };
556
- }
557
- function runAccessibilityCheck(ctx) {
558
- const issues = [];
559
- for (let si = 0; si < ctx.pptxData.slides.length; si++) {
560
- const slide = ctx.pptxData.slides[si];
561
- let slideHasText = false;
562
- for (const el of slide.elements) {
563
- if (el.type === "image" || el.type === "picture") {
564
- const img = el;
565
- if (!img.altText) {
566
- issues.push({
567
- slideIndex: si,
568
- elementId: el.id,
569
- severity: "error",
570
- message: `Image element '${el.id}' has no alt text.`
571
- });
572
- }
573
- }
574
- if (pptxViewerCore.hasTextProperties(el)) {
575
- const textEl = el;
576
- slideHasText = true;
577
- if (!textEl.text || textEl.text.trim() === "") {
578
- issues.push({
579
- slideIndex: si,
580
- elementId: el.id,
581
- severity: "info",
582
- message: `Text element '${el.id}' is empty.`
583
- });
584
- }
585
- const fs = textEl.textStyle?.fontSize;
586
- if (fs !== void 0 && fs < 10) {
587
- issues.push({
588
- slideIndex: si,
589
- elementId: el.id,
590
- severity: "warning",
591
- message: `Text element '${el.id}' has very small font size (${fs}pt < 10pt).`
592
- });
593
- }
594
- const textColor = textEl.textStyle?.color?.toLowerCase();
595
- const bgColor = slide.backgroundColor?.toLowerCase();
596
- if (textColor && bgColor && textColor === bgColor) {
597
- issues.push({
598
- slideIndex: si,
599
- elementId: el.id,
600
- severity: "error",
601
- message: `Text element '${el.id}' has same color as slide background (${textColor}).`
602
- });
603
- }
604
- }
605
- }
606
- if (!slideHasText) {
607
- issues.push({
608
- slideIndex: si,
609
- severity: "warning",
610
- message: `Slide ${si + 1} contains no text elements.`
611
- });
612
- }
613
- }
614
- const errorCount = issues.filter((i) => i.severity === "error").length;
615
- const warningCount = issues.filter((i) => i.severity === "warning").length;
616
- const infoCount = issues.filter((i) => i.severity === "info").length;
617
- return {
618
- pptxData: ctx.pptxData,
619
- dirty: false,
620
- result: {
621
- slideCount: ctx.pptxData.slides.length,
622
- totalIssues: issues.length,
623
- errorCount,
624
- warningCount,
625
- infoCount,
626
- issues
627
- }
628
- };
629
- }
630
-
631
- // src/tools/text-editing.ts
632
- function editRange(segments, start, end, replacement) {
633
- let offset = 0;
634
- let inserted = false;
635
- for (const segment of segments) {
636
- const text = segment.text;
637
- const next = offset + text.length;
638
- if (!segment.bulletInfo?.ownedByParagraph && next > start && offset < end) {
639
- const from = Math.max(0, start - offset);
640
- const to = Math.min(text.length, end - offset);
641
- segment.text = text.slice(0, from) + (inserted ? "" : replacement) + text.slice(to);
642
- inserted = true;
643
- } else if (!inserted && start === end && offset <= start && next >= start && !segment.bulletInfo?.ownedByParagraph) {
644
- const at = start - offset;
645
- segment.text = text.slice(0, at) + replacement + text.slice(at);
646
- inserted = true;
647
- }
648
- offset = next;
649
- }
650
- if (!inserted && replacement) {
651
- segments.push({ text: replacement, style: {} });
652
- }
653
- }
654
- function changedRange(before, text) {
655
- let start = 0;
656
- while (start < before.length && start < text.length && before[start] === text[start]) {
657
- start++;
658
- }
659
- let suffix = 0;
660
- while (suffix < before.length - start && suffix < text.length - start && before[before.length - suffix - 1] === text[text.length - suffix - 1]) {
661
- suffix++;
662
- }
663
- return {
664
- start,
665
- end: before.length - suffix,
666
- replacement: text.slice(start, text.length - suffix)
667
- };
668
- }
669
- function setElementText(element, text) {
670
- const segments = element.textSegments;
671
- if (segments?.length) {
672
- const before = segments.map((segment) => segment.text).join("");
673
- const oldLines = before.split("\n");
674
- const newLines = text.split("\n");
675
- if (oldLines.length === newLines.length) {
676
- let offset = before.length;
677
- for (let index = oldLines.length - 1; index >= 0; index--) {
678
- const line = oldLines[index];
679
- offset -= line.length;
680
- const edit = changedRange(line, newLines[index]);
681
- editRange(segments, offset + edit.start, offset + edit.end, edit.replacement);
682
- offset--;
683
- }
684
- } else {
685
- const edit = changedRange(before, text);
686
- editRange(segments, edit.start, edit.end, edit.replacement);
687
- }
688
- element.text = segments.map((segment) => segment.text).join("");
689
- } else {
690
- element.text = text;
691
- }
692
- }
693
- function replaceElementText(element, regex, replacement) {
694
- const before = element.text ?? element.textSegments?.map((segment) => segment.text).join("") ?? "";
695
- const matches = Array.from(before.matchAll(regex));
696
- if (before.replace(regex, replacement) === before) {
697
- return 0;
698
- }
699
- if (!element.textSegments?.length) {
700
- element.text = before.replace(regex, replacement);
701
- return matches.length;
702
- }
703
- for (const match of matches.reverse()) {
704
- const start = match.index;
705
- const end = start + match[0].length;
706
- const single = new RegExp(regex.source, `${regex.flags.replace("g", "")}y`);
707
- single.lastIndex = start;
708
- const after = before.replace(single, replacement);
709
- editRange(
710
- element.textSegments,
711
- start,
712
- end,
713
- after.slice(start, after.length - (before.length - end))
714
- );
715
- }
716
- element.text = element.textSegments.map((segment) => segment.text).join("");
717
- return matches.length;
718
- }
719
-
720
- // src/tools/element-tools.ts
721
- function addElement(ctx, params) {
722
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
723
- if (err) {
724
- throw new Error(err);
725
- }
726
- const slide = ctx.pptxData.slides[params.slideIndex];
727
- const id = generateElementId();
728
- const x = params.x ?? 100;
729
- const y = params.y ?? 100;
730
- const width = params.width ?? 300;
731
- const height = params.height ?? 60;
732
- switch (params.type) {
733
- case "text": {
734
- const el = {
735
- id,
736
- type: "text",
737
- x,
738
- y,
739
- width,
740
- height,
741
- text: params.text ?? "",
742
- textStyle: {
743
- fontSize: params.fontSize,
744
- fontFamily: params.fontFamily,
745
- color: params.fontColor,
746
- bold: params.bold,
747
- italic: params.italic,
748
- underline: params.underline,
749
- align: params.alignment
750
- },
751
- textSegments: [
752
- {
753
- text: params.text ?? "",
754
- style: {
755
- fontSize: params.fontSize,
756
- fontFamily: params.fontFamily,
757
- color: params.fontColor,
758
- bold: params.bold,
759
- italic: params.italic,
760
- underline: params.underline
761
- }
762
- }
763
- ]
764
- };
765
- slide.elements.push(el);
766
- break;
767
- }
768
- case "shape": {
769
- const el = {
770
- id,
771
- type: "shape",
772
- x,
773
- y,
774
- width,
775
- height,
776
- shapeType: params.shapeType ?? "rect",
777
- text: params.text,
778
- textStyle: params.text ? {
779
- fontSize: params.fontSize,
780
- fontFamily: params.fontFamily,
781
- color: params.fontColor,
782
- bold: params.bold,
783
- italic: params.italic,
784
- align: params.alignment
785
- } : void 0,
786
- shapeStyle: {
787
- fillColor: params.fillColor,
788
- strokeColor: params.strokeColor,
789
- strokeWidth: params.strokeWidth
790
- }
791
- };
792
- slide.elements.push(el);
793
- break;
794
- }
795
- case "image": {
796
- const el = {
797
- id,
798
- type: "image",
799
- x,
800
- y,
801
- width,
802
- height,
803
- imageData: params.imageData,
804
- altText: params.altText
805
- };
806
- slide.elements.push(el);
807
- break;
808
- }
809
- case "table": {
810
- const rowCount = params.rows ?? 2;
811
- const colCount = params.columns ?? 2;
812
- const colWidth = 1 / colCount;
813
- const tableRows = Array.from({ length: rowCount }, (_, r) => ({
814
- height: 40,
815
- cells: Array.from({ length: colCount }, (__, c) => ({
816
- text: params.cellData?.[r]?.[c] ?? ""
817
- }))
818
- }));
819
- const tableEl = {
820
- id,
821
- type: "table",
822
- x,
823
- y,
824
- width,
825
- height: params.height ?? rowCount * 40,
826
- tableData: {
827
- rows: tableRows,
828
- columnWidths: Array.from({ length: colCount }, () => colWidth),
829
- firstRowHeader: params.headerRow ?? false
830
- }
831
- };
832
- slide.elements.push(tableEl);
833
- break;
834
- }
835
- case "connector": {
836
- const el = {
837
- id,
838
- type: "connector",
839
- x,
840
- y,
841
- width,
842
- height,
843
- shapeStyle: {
844
- strokeColor: params.strokeColor ?? "#000000",
845
- strokeWidth: params.strokeWidth ?? 1,
846
- connectorStartArrow: params.startArrow,
847
- connectorEndArrow: params.endArrow
848
- }
849
- };
850
- slide.elements.push(el);
851
- break;
852
- }
853
- default: {
854
- throw new Error(`Unknown element type: ${String(params.type)}`);
855
- }
856
- }
857
- return {
858
- pptxData: ctx.pptxData,
859
- dirty: true,
860
- result: { elementId: id, slideIndex: params.slideIndex }
861
- };
862
- }
863
- function updateElement(ctx, params) {
864
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
865
- if (err) {
866
- throw new Error(err);
867
- }
868
- const slide = ctx.pptxData.slides[params.slideIndex];
869
- const el = slide.elements.find((e) => e.id === params.elementId);
870
- if (!el) {
871
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
872
- }
873
- if (params.x !== void 0) {
874
- el.x = params.x;
875
- }
876
- if (params.y !== void 0) {
877
- el.y = params.y;
878
- }
879
- if (params.width !== void 0) {
880
- el.width = params.width;
881
- }
882
- if (params.height !== void 0) {
883
- el.height = params.height;
884
- }
885
- if (params.rotation !== void 0) {
886
- el.rotation = params.rotation;
887
- }
888
- if (params.opacity !== void 0) {
889
- el.opacity = params.opacity;
890
- }
891
- if (params.hidden !== void 0) {
892
- el.hidden = params.hidden;
893
- }
894
- if (params.flipH !== void 0) {
895
- el.flipHorizontal = params.flipH;
896
- }
897
- if (params.flipV !== void 0) {
898
- el.flipVertical = params.flipV;
899
- }
900
- if (params.altText !== void 0) {
901
- applyElementAltText(el, params.altText);
902
- }
903
- if (params.title !== void 0) {
904
- applyElementTitle(el, params.title);
905
- }
906
- if (pptxViewerCore.hasTextProperties(el)) {
907
- const textEl = el;
908
- if (params.textSegments !== void 0) {
909
- textEl.textSegments = structuredClone(params.textSegments);
910
- textEl.text = textEl.textSegments.map((segment) => segment.text).join("");
911
- } else if (params.text !== void 0) {
912
- setElementText(textEl, params.text);
913
- }
914
- if (!textEl.textStyle) {
915
- textEl.textStyle = {};
916
- }
917
- if (params.fontSize !== void 0) {
918
- textEl.textStyle.fontSize = params.fontSize;
919
- }
920
- if (params.fontFamily !== void 0) {
921
- textEl.textStyle.fontFamily = params.fontFamily;
922
- }
923
- if (params.fontThemeColor !== void 0) {
924
- textEl.textStyle.colorRef = params.fontThemeColor;
925
- const resolved = pptxViewerCore.resolveThemeColorRef(params.fontThemeColor, ctx.pptxData.themeColorMap);
926
- if (params.fontColor !== void 0) {
927
- textEl.textStyle.color = params.fontColor;
928
- } else if (resolved) {
929
- textEl.textStyle.color = resolved;
930
- }
931
- } else if (params.fontColor !== void 0) {
932
- textEl.textStyle.color = params.fontColor;
933
- textEl.textStyle.colorRef = void 0;
934
- }
935
- if (params.bold !== void 0) {
936
- textEl.textStyle.bold = params.bold;
937
- }
938
- if (params.italic !== void 0) {
939
- textEl.textStyle.italic = params.italic;
940
- }
941
- if (params.underline !== void 0) {
942
- textEl.textStyle.underline = params.underline;
943
- }
944
- if (params.alignment !== void 0) {
945
- textEl.textStyle.align = params.alignment;
946
- }
947
- }
948
- if ("shapeStyle" in el && el.shapeStyle) {
949
- const ss = el.shapeStyle;
950
- if (params.fillColor !== void 0) {
951
- ss.fillColor = params.fillColor;
952
- }
953
- if (params.strokeColor !== void 0) {
954
- ss.strokeColor = params.strokeColor;
955
- }
956
- if (params.strokeWidth !== void 0) {
957
- ss.strokeWidth = params.strokeWidth;
958
- }
959
- } else if (params.fillColor !== void 0 || params.strokeColor !== void 0 || params.strokeWidth !== void 0) {
960
- if ("shapeStyle" in el) {
961
- el.shapeStyle = {
962
- fillColor: params.fillColor,
963
- strokeColor: params.strokeColor,
964
- strokeWidth: params.strokeWidth
965
- };
966
- }
967
- }
968
- return {
969
- pptxData: ctx.pptxData,
970
- dirty: true,
971
- result: { elementId: params.elementId }
972
- };
973
- }
974
- function renameElement(ctx, params) {
975
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
976
- if (err) {
977
- throw new Error(err);
978
- }
979
- const slide = ctx.pptxData.slides[params.slideIndex];
980
- const el = slide.elements.find((e) => e.id === params.elementId);
981
- if (!el) {
982
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
983
- }
984
- const name = params.name.trim();
985
- if (name.length === 0) {
986
- delete el.name;
987
- } else {
988
- el.name = name;
989
- }
990
- return {
991
- pptxData: ctx.pptxData,
992
- dirty: true,
993
- result: { elementId: params.elementId, name }
994
- };
995
- }
996
- function deleteElements(ctx, params) {
997
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
998
- if (err) {
999
- throw new Error(err);
1000
- }
1001
- const slide = ctx.pptxData.slides[params.slideIndex];
1002
- const idSet = new Set(params.elementIds);
1003
- const existingIds = new Set(slide.elements.map((element) => element.id));
1004
- const notFound = params.elementIds.filter((id) => !existingIds.has(id));
1005
- if (notFound.length > 0) {
1006
- throw new Error(`Elements not found: ${notFound.join(", ")}`);
1007
- }
1008
- const before = slide.elements.length;
1009
- slide.elements = slide.elements.filter((e) => !idSet.has(e.id));
1010
- return {
1011
- pptxData: ctx.pptxData,
1012
- dirty: true,
1013
- result: { deletedCount: before - slide.elements.length }
1014
- };
1015
- }
1016
- function arrangeElements(ctx, params) {
1017
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1018
- if (err) {
1019
- throw new Error(err);
1020
- }
1021
- const slide = ctx.pptxData.slides[params.slideIndex];
1022
- if (params.action === "align") {
1023
- if (!params.elementIds || params.elementIds.length === 0) {
1024
- throw new Error("elementIds is required for align action.");
1025
- }
1026
- const targetIds = new Set(params.elementIds);
1027
- const targets = slide.elements.filter((element) => targetIds.has(element.id));
1028
- if (targets.length === 0) {
1029
- throw new Error("No matching elements found for align.");
1030
- }
1031
- switch (params.alignment) {
1032
- case "left": {
1033
- const minX = Math.min(...targets.map((e) => e.x));
1034
- for (const t of targets) {
1035
- t.x = minX;
1036
- }
1037
- break;
1038
- }
1039
- case "right": {
1040
- const maxRight = Math.max(...targets.map((e) => e.x + e.width));
1041
- for (const t of targets) {
1042
- t.x = maxRight - t.width;
1043
- }
1044
- break;
1045
- }
1046
- case "top": {
1047
- const minY = Math.min(...targets.map((e) => e.y));
1048
- for (const t of targets) {
1049
- t.y = minY;
1050
- }
1051
- break;
1052
- }
1053
- case "bottom": {
1054
- const maxBottom = Math.max(...targets.map((e) => e.y + e.height));
1055
- for (const t of targets) {
1056
- t.y = maxBottom - t.height;
1057
- }
1058
- break;
1059
- }
1060
- case "centerH": {
1061
- const avgCX = targets.reduce((s, e) => s + e.x + e.width / 2, 0) / targets.length;
1062
- for (const t of targets) {
1063
- t.x = avgCX - t.width / 2;
1064
- }
1065
- break;
1066
- }
1067
- case "centerV": {
1068
- const avgCY = targets.reduce((s, e) => s + e.y + e.height / 2, 0) / targets.length;
1069
- for (const t of targets) {
1070
- t.y = avgCY - t.height / 2;
1071
- }
1072
- break;
1073
- }
1074
- default: {
1075
- throw new Error(`Unknown alignment: ${String(params.alignment)}`);
1076
- }
1077
- }
1078
- } else if (params.action === "reorderLayer") {
1079
- if (!params.elementId) {
1080
- throw new Error("elementId is required for reorderLayer action.");
1081
- }
1082
- const idx = slide.elements.findIndex((e) => e.id === params.elementId);
1083
- if (idx < 0) {
1084
- throw new Error(`Element '${params.elementId}' not found.`);
1085
- }
1086
- const [el] = slide.elements.splice(idx, 1);
1087
- switch (params.layerAction) {
1088
- case "bringToFront":
1089
- slide.elements.push(el);
1090
- break;
1091
- case "sendToBack":
1092
- slide.elements.unshift(el);
1093
- break;
1094
- case "bringForward": {
1095
- const newIdx = Math.min(idx + 1, slide.elements.length);
1096
- slide.elements.splice(newIdx, 0, el);
1097
- break;
1098
- }
1099
- case "sendBackward": {
1100
- const newIdx = Math.max(idx - 1, 0);
1101
- slide.elements.splice(newIdx, 0, el);
1102
- break;
1103
- }
1104
- default: {
1105
- slide.elements.splice(idx, 0, el);
1106
- throw new Error(`Unknown layerAction: ${String(params.layerAction)}`);
1107
- }
1108
- }
1109
- } else {
1110
- throw new Error(`Unknown action: ${String(params.action)}`);
1111
- }
1112
- return {
1113
- pptxData: ctx.pptxData,
1114
- dirty: true,
1115
- result: { slideIndex: params.slideIndex }
1116
- };
1117
- }
1118
- function cloneElement(ctx, params) {
1119
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1120
- if (err) {
1121
- throw new Error(err);
1122
- }
1123
- const srcSlide = ctx.pptxData.slides[params.slideIndex];
1124
- const original = srcSlide.elements.find((e) => e.id === params.elementId);
1125
- if (!original) {
1126
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1127
- }
1128
- const targets = params.targetSlideIndexes ?? [params.slideIndex];
1129
- const clonedIds = [];
1130
- const offsetX = params.offsetX ?? 20;
1131
- const offsetY = params.offsetY ?? 20;
1132
- for (const targetIdx of targets) {
1133
- const terr = validateSlideIndex(targetIdx, ctx.pptxData.slides.length);
1134
- if (terr) {
1135
- throw new Error(terr);
1136
- }
1137
- const clone = pptxViewerCore.cloneElement(original);
1138
- const intoTemplate = pptxViewerCore.isTemplateElementId(original.id);
1139
- clone.id = pptxViewerCore.makeStoreAwareId(intoTemplate, original.id);
1140
- pptxViewerCore.reassignDescendantIds(clone, () => pptxViewerCore.makeStoreAwareId(intoTemplate, original.id));
1141
- clone.x += offsetX;
1142
- clone.y += offsetY;
1143
- ctx.pptxData.slides[targetIdx].elements.push(clone);
1144
- clonedIds.push(clone.id);
1145
- }
1146
- return {
1147
- pptxData: ctx.pptxData,
1148
- dirty: true,
1149
- result: { clonedIds }
1150
- };
1151
- }
1152
- function setElementAnimation(ctx, params) {
1153
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1154
- if (err) {
1155
- throw new Error(err);
1156
- }
1157
- const slide = ctx.pptxData.slides[params.slideIndex];
1158
- const elExists = slide.elements.some((e) => e.id === params.elementId);
1159
- if (!elExists) {
1160
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1161
- }
1162
- if (!slide.animations) {
1163
- slide.animations = [];
1164
- }
1165
- const existing = slide.animations.find((a) => a.elementId === params.elementId);
1166
- if (existing) {
1167
- if (params.entrance !== void 0) {
1168
- existing.entrance = params.entrance;
1169
- }
1170
- if (params.exit !== void 0) {
1171
- existing.exit = params.exit;
1172
- }
1173
- if (params.durationMs !== void 0) {
1174
- existing.durationMs = params.durationMs;
1175
- }
1176
- if (params.delayMs !== void 0) {
1177
- existing.delayMs = params.delayMs;
1178
- }
1179
- if (params.order !== void 0) {
1180
- existing.order = params.order;
1181
- }
1182
- } else {
1183
- const anim = {
1184
- elementId: params.elementId,
1185
- entrance: params.entrance,
1186
- exit: params.exit,
1187
- durationMs: params.durationMs,
1188
- delayMs: params.delayMs,
1189
- order: params.order
1190
- };
1191
- slide.animations.push(anim);
1192
- }
1193
- return {
1194
- pptxData: ctx.pptxData,
1195
- dirty: true,
1196
- result: { elementId: params.elementId }
1197
- };
1198
- }
1199
- function groupElements(ctx, params) {
1200
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1201
- if (err) {
1202
- throw new Error(err);
1203
- }
1204
- if (params.elementIds.length < 2) {
1205
- throw new Error("At least 2 elements are required to form a group.");
1206
- }
1207
- const slide = ctx.pptxData.slides[params.slideIndex];
1208
- const idSet = new Set(params.elementIds);
1209
- const found = new Set(slide.elements.filter((e) => idSet.has(e.id)).map((e) => e.id));
1210
- if (found.size !== idSet.size) {
1211
- const missing = params.elementIds.filter((id) => !found.has(id));
1212
- throw new Error(`Elements not found: ${missing.join(", ")}`);
1213
- }
1214
- const groupId = generateElementId();
1215
- const grouped = pptxViewerCore.groupElements(slide.elements, params.elementIds, groupId);
1216
- slide.elements = grouped.elements;
1217
- return {
1218
- pptxData: ctx.pptxData,
1219
- dirty: true,
1220
- result: { groupId: grouped.groupId ?? groupId }
1221
- };
1222
- }
1223
- function ungroupElements(ctx, params) {
1224
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1225
- if (err) {
1226
- throw new Error(err);
1227
- }
1228
- const slide = ctx.pptxData.slides[params.slideIndex];
1229
- const group = slide.elements.find((e) => e.id === params.groupElementId);
1230
- if (!group) {
1231
- throw new Error(`Group element '${params.groupElementId}' not found.`);
1232
- }
1233
- if (group.type !== "group") {
1234
- throw new Error(`Element '${params.groupElementId}' is not a group.`);
1235
- }
1236
- const intoTemplate = pptxViewerCore.isTemplateElementId(group.id);
1237
- const childIds = group.children.map(
1238
- (child) => pptxViewerCore.isTemplateElementId(child.id) === intoTemplate ? child.id : pptxViewerCore.makeStoreAwareId(intoTemplate, group.id)
1239
- );
1240
- const promoted = pptxViewerCore.ungroupElements(slide.elements, params.groupElementId, childIds, {
1241
- intoTemplate
1242
- });
1243
- slide.elements = promoted.elements;
1244
- return {
1245
- pptxData: ctx.pptxData,
1246
- dirty: true,
1247
- result: { restoredIds: promoted.childIds }
1248
- };
1249
- }
1250
- function batchUpdateElements(ctx, params) {
1251
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1252
- if (err) {
1253
- throw new Error(err);
1254
- }
1255
- const slide = ctx.pptxData.slides[params.slideIndex];
1256
- const idSet = new Set(params.elementIds);
1257
- let count = 0;
1258
- for (const el of slide.elements) {
1259
- if (!idSet.has(el.id)) {
1260
- continue;
1261
- }
1262
- if (params.x !== void 0) {
1263
- el.x = params.x;
1264
- }
1265
- if (params.y !== void 0) {
1266
- el.y = params.y;
1267
- }
1268
- if (params.width !== void 0) {
1269
- el.width = params.width;
1270
- }
1271
- if (params.height !== void 0) {
1272
- el.height = params.height;
1273
- }
1274
- if (params.rotation !== void 0) {
1275
- el.rotation = params.rotation;
1276
- }
1277
- if (params.opacity !== void 0) {
1278
- el.opacity = params.opacity;
1279
- }
1280
- if (params.hidden !== void 0) {
1281
- el.hidden = params.hidden;
1282
- }
1283
- count++;
1284
- }
1285
- return {
1286
- pptxData: ctx.pptxData,
1287
- dirty: true,
1288
- result: { updatedCount: count }
1289
- };
1290
- }
1291
- function updateTableCells(ctx, params) {
1292
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1293
- if (err) {
1294
- throw new Error(err);
1295
- }
1296
- const slide = ctx.pptxData.slides[params.slideIndex];
1297
- const el = slide.elements.find((e) => e.id === params.elementId);
1298
- if (!el) {
1299
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1300
- }
1301
- if (el.type !== "table") {
1302
- throw new Error(`Element '${params.elementId}' is not a table.`);
1303
- }
1304
- const tbl = el;
1305
- if (!tbl.tableData) {
1306
- throw new Error(`Table '${params.elementId}' has no tableData.`);
1307
- }
1308
- let count = 0;
1309
- for (const cellUpdate of params.cells) {
1310
- const row = tbl.tableData.rows[cellUpdate.row];
1311
- if (!row) {
1312
- throw new Error(
1313
- `Row ${cellUpdate.row} out of range (table has ${tbl.tableData.rows.length} rows).`
1314
- );
1315
- }
1316
- const cell = row.cells[cellUpdate.col];
1317
- if (!cell) {
1318
- throw new Error(`Column ${cellUpdate.col} out of range (row has ${row.cells.length} cells).`);
1319
- }
1320
- cell.text = cellUpdate.text;
1321
- count++;
1322
- }
1323
- return {
1324
- pptxData: ctx.pptxData,
1325
- dirty: true,
1326
- result: { updatedCount: count }
1327
- };
1328
- }
1329
- function manageTableStructure(ctx, params) {
1330
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1331
- if (err) {
1332
- throw new Error(err);
1333
- }
1334
- const slide = ctx.pptxData.slides[params.slideIndex];
1335
- const el = slide.elements.find((e) => e.id === params.elementId);
1336
- if (!el) {
1337
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1338
- }
1339
- if (el.type !== "table") {
1340
- throw new Error(`Element '${params.elementId}' is not a table.`);
1341
- }
1342
- const tbl = el;
1343
- if (!tbl.tableData) {
1344
- throw new Error(`Table '${params.elementId}' has no tableData.`);
1345
- }
1346
- const { rows, columnWidths } = tbl.tableData;
1347
- const source = { ...tbl, tableData: structuredClone(tbl.tableData) };
1348
- let edit;
1349
- const colCount = columnWidths.length;
1350
- const rowCount = rows.length;
1351
- switch (params.action) {
1352
- case "insertRow": {
1353
- const pos = params.position !== void 0 ? Math.min(Math.max(params.position, 0), rowCount) : rowCount;
1354
- const newRow = {
1355
- height: 40,
1356
- cells: Array.from({ length: colCount }, (_, c) => ({
1357
- text: params.cellTexts?.[c] ?? ""
1358
- }))
1359
- };
1360
- rows.splice(pos, 0, newRow);
1361
- edit = { axis: "row", action: "insert", index: pos };
1362
- break;
1363
- }
1364
- case "deleteRow": {
1365
- if (rowCount <= 1) {
1366
- throw new Error("Cannot delete the last row of a table.");
1367
- }
1368
- const ref = params.referenceIndex ?? rowCount - 1;
1369
- if (ref < 0 || ref >= rowCount) {
1370
- throw new Error(`Row index ${ref} out of range (0\u2013${rowCount - 1}).`);
1371
- }
1372
- rows.splice(ref, 1);
1373
- edit = { axis: "row", action: "delete", index: ref };
1374
- break;
1375
- }
1376
- case "insertColumn": {
1377
- const pos = params.position !== void 0 ? Math.min(Math.max(params.position, 0), colCount) : colCount;
1378
- const newWidth = 1 / (colCount + 1);
1379
- const scaleFactor = colCount / (colCount + 1);
1380
- for (let i = 0; i < columnWidths.length; i++) {
1381
- columnWidths[i] *= scaleFactor;
1382
- }
1383
- columnWidths.splice(pos, 0, newWidth);
1384
- edit = { axis: "column", action: "insert", index: pos };
1385
- for (let r = 0; r < rows.length; r++) {
1386
- rows[r].cells.splice(pos, 0, {
1387
- text: params.cellTexts?.[r] ?? ""
1388
- });
1389
- }
1390
- break;
1391
- }
1392
- case "deleteColumn": {
1393
- if (colCount <= 1) {
1394
- throw new Error("Cannot delete the last column of a table.");
1395
- }
1396
- const ref = params.referenceIndex ?? colCount - 1;
1397
- if (ref < 0 || ref >= colCount) {
1398
- throw new Error(`Column index ${ref} out of range (0\u2013${colCount - 1}).`);
1399
- }
1400
- const removedWidth = columnWidths[ref];
1401
- columnWidths.splice(ref, 1);
1402
- edit = { axis: "column", action: "delete", index: ref };
1403
- const remaining = columnWidths.length;
1404
- if (remaining > 0) {
1405
- const extra = removedWidth / remaining;
1406
- for (let i = 0; i < columnWidths.length; i++) {
1407
- columnWidths[i] += extra;
1408
- }
1409
- }
1410
- for (const row of rows) {
1411
- row.cells.splice(ref, 1);
1412
- }
1413
- break;
1414
- }
1415
- default: {
1416
- throw new Error(`Unknown action: ${String(params.action)}`);
1417
- }
1418
- }
1419
- const rawXml = pptxViewerCore.rebuildTableStructureInRawXml(source, tbl.tableData, edit);
1420
- if (rawXml) {
1421
- tbl.rawXml = rawXml;
1422
- }
1423
- return {
1424
- pptxData: ctx.pptxData,
1425
- dirty: true,
1426
- result: {
1427
- rowCount: tbl.tableData.rows.length,
1428
- columnCount: tbl.tableData.columnWidths.length
1429
- }
1430
- };
1431
- }
1432
- function findTableElement(ctx, slideIndex, elementId) {
1433
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
1434
- if (err) {
1435
- throw new Error(err);
1436
- }
1437
- const slide = ctx.pptxData.slides[slideIndex];
1438
- const el = slide.elements.find((e) => e.id === elementId);
1439
- if (!el) {
1440
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
1441
- }
1442
- if (el.type !== "table") {
1443
- throw new Error(`Element '${elementId}' is not a table.`);
1444
- }
1445
- return el;
1446
- }
1447
- function tableStyleIdsInUse(ctx) {
1448
- const used = /* @__PURE__ */ new Set();
1449
- for (const slide of ctx.pptxData.slides) {
1450
- for (const el of slide.elements) {
1451
- if (el.type === "table" && el.tableData?.tableStyleId) {
1452
- used.add(pptxViewerCore.normalizeTableStyleGuid(el.tableData.tableStyleId));
1453
- }
1454
- }
1455
- }
1456
- return used;
1457
- }
1458
- function setTableStyleSection(ctx, params) {
1459
- if (!pptxViewerCore.TABLE_STYLE_PART_SEQUENCE.includes(params.section)) {
1460
- throw new Error(
1461
- `Unknown table style section '${params.section}'. Valid sections: ${pptxViewerCore.TABLE_STYLE_PART_SEQUENCE.join(", ")}.`
1462
- );
1463
- }
1464
- const map = ctx.pptxData.tableStyleMap ?? {};
1465
- ctx.pptxData.tableStyleMap = map;
1466
- const normalizedId = pptxViewerCore.normalizeTableStyleGuid(params.styleId);
1467
- const entry = map[normalizedId];
1468
- if (!entry) {
1469
- throw new Error(
1470
- `Table style '${params.styleId}' is not loaded in this presentation. Use create_table_style to add a new one.`
1471
- );
1472
- }
1473
- if (params.styleName !== void 0) {
1474
- entry.styleName = params.styleName;
1475
- }
1476
- const mutableEntry = entry;
1477
- if (params.fill !== void 0) {
1478
- mutableEntry[`${params.section}Fill`] = params.fill;
1479
- }
1480
- if (params.text !== void 0) {
1481
- mutableEntry[`${params.section}Text`] = params.text;
1482
- }
1483
- if (params.borders !== void 0) {
1484
- mutableEntry[`${params.section}Borders`] = params.borders;
1485
- }
1486
- if (params.cell3D !== void 0) {
1487
- mutableEntry[`${params.section}Cell3D`] = params.cell3D;
1488
- }
1489
- return {
1490
- pptxData: ctx.pptxData,
1491
- dirty: true,
1492
- result: { styleId: normalizedId, section: params.section }
1493
- };
1494
- }
1495
- function createTableStyle(ctx, params) {
1496
- const map = ctx.pptxData.tableStyleMap ?? {};
1497
- ctx.pptxData.tableStyleMap = map;
1498
- const basedOn = params.basedOnStyleId ? map[pptxViewerCore.normalizeTableStyleGuid(params.basedOnStyleId)] : void 0;
1499
- if (params.basedOnStyleId && !basedOn) {
1500
- throw new Error(`basedOnStyleId '${params.basedOnStyleId}' is not a loaded table style.`);
1501
- }
1502
- const entry = pptxViewerCore.createTableStyleEntry(map, { styleName: params.styleName, basedOn });
1503
- pptxViewerCore.addTableStyleToMap(map, entry);
1504
- return {
1505
- pptxData: ctx.pptxData,
1506
- dirty: true,
1507
- result: { styleId: entry.styleId },
1508
- ...params.setAsDefault ? { saveOptions: { tableStylesDefaultId: entry.styleId } } : {}
1509
- };
1510
- }
1511
- function deleteTableStyle(ctx, params) {
1512
- const normalizedId = pptxViewerCore.normalizeTableStyleGuid(params.styleId);
1513
- if (!params.force) {
1514
- const used = tableStyleIdsInUse(ctx);
1515
- if (used.has(normalizedId)) {
1516
- throw new Error(
1517
- `Table style '${params.styleId}' is assigned to at least one table on this deck. Reassign those tables first, or pass force: true.`
1518
- );
1519
- }
1520
- }
1521
- const map = ctx.pptxData.tableStyleMap ?? {};
1522
- ctx.pptxData.tableStyleMap = map;
1523
- pptxViewerCore.deleteTableStyleFromMap(map, normalizedId);
1524
- return {
1525
- pptxData: ctx.pptxData,
1526
- dirty: true,
1527
- result: { deleted: true },
1528
- saveOptions: { tableStylesToDelete: [normalizedId] }
1529
- };
1530
- }
1531
- function assignTableStyle(ctx, params) {
1532
- const el = findTableElement(ctx, params.slideIndex, params.elementId);
1533
- if (!el.tableData) {
1534
- throw new Error(`Table '${params.elementId}' has no tableData.`);
1535
- }
1536
- const styleId = params.styleId ?? ctx.pptxData.tableStylesDefaultId;
1537
- if (!styleId) {
1538
- throw new Error(
1539
- "styleId was not provided and this presentation has no tableStylesDefaultId to fall back to."
1540
- );
1541
- }
1542
- const normalizedId = pptxViewerCore.normalizeTableStyleGuid(styleId);
1543
- el.tableData.tableStyleId = normalizedId;
1544
- if (params.bandedRows !== void 0) {
1545
- el.tableData.bandedRows = params.bandedRows;
1546
- }
1547
- if (params.bandedColumns !== void 0) {
1548
- el.tableData.bandedColumns = params.bandedColumns;
1549
- }
1550
- if (params.firstRowHeader !== void 0) {
1551
- el.tableData.firstRowHeader = params.firstRowHeader;
1552
- }
1553
- if (params.lastRow !== void 0) {
1554
- el.tableData.lastRow = params.lastRow;
1555
- }
1556
- if (params.firstCol !== void 0) {
1557
- el.tableData.firstCol = params.firstCol;
1558
- }
1559
- if (params.lastCol !== void 0) {
1560
- el.tableData.lastCol = params.lastCol;
1561
- }
1562
- return {
1563
- pptxData: ctx.pptxData,
1564
- dirty: true,
1565
- result: { elementId: params.elementId, styleId: normalizedId }
1566
- };
1567
- }
1568
- var MAX_REGEX_LEN = 200;
1569
- function hasNestedUnboundedQuantifier(pattern) {
1570
- return /\([^)]*[+*]\)[+*]/u.test(pattern);
1571
- }
1572
- function buildSearchRegex(query, useRegex, caseSensitive) {
1573
- if (useRegex) {
1574
- if (query.length > MAX_REGEX_LEN) {
1575
- return null;
1576
- }
1577
- if (hasNestedUnboundedQuantifier(query)) {
1578
- return null;
1579
- }
1580
- }
1581
- const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1582
- const flags = caseSensitive ? "g" : "gi";
1583
- try {
1584
- return new RegExp(pattern, flags);
1585
- } catch {
1586
- return null;
1587
- }
1588
- }
1589
- function findText(ctx, params) {
1590
- const regex = buildSearchRegex(
1591
- params.query,
1592
- params.useRegex === true,
1593
- params.caseSensitive === true
1594
- );
1595
- if (!regex) {
1596
- return {
1597
- pptxData: ctx.pptxData,
1598
- dirty: false,
1599
- result: { query: params.query, matchCount: 0, matches: [] }
1600
- };
1601
- }
1602
- const targetIndexes = params.slideIndexes ? params.slideIndexes : ctx.pptxData.slides.map((_, i) => i);
1603
- const matches = [];
1604
- for (const si of targetIndexes) {
1605
- const err = validateSlideIndex(si, ctx.pptxData.slides.length);
1606
- if (err) {
1607
- throw new Error(err);
1608
- }
1609
- const slide = ctx.pptxData.slides[si];
1610
- for (const el of slide.elements) {
1611
- if (pptxViewerCore.hasTextProperties(el)) {
1612
- const textEl = el;
1613
- if (textEl.text) {
1614
- const localMatches = Array.from(textEl.text.matchAll(regex));
1615
- for (const m of localMatches) {
1616
- matches.push({
1617
- slideIndex: si,
1618
- elementId: el.id,
1619
- elementType: el.type,
1620
- matchedText: m[0],
1621
- context: textEl.text.slice(
1622
- Math.max(0, (m.index ?? 0) - 20),
1623
- (m.index ?? 0) + m[0].length + 20
1624
- )
1625
- });
1626
- }
1627
- }
1628
- }
1629
- if (el.type === "table") {
1630
- const tbl = el;
1631
- if (tbl.tableData) {
1632
- for (const row of tbl.tableData.rows) {
1633
- for (const cell of row.cells) {
1634
- if (cell.text) {
1635
- const localMatches = Array.from(cell.text.matchAll(regex));
1636
- for (const m of localMatches) {
1637
- matches.push({
1638
- slideIndex: si,
1639
- elementId: el.id,
1640
- elementType: "table-cell",
1641
- matchedText: m[0],
1642
- context: cell.text.slice(
1643
- Math.max(0, (m.index ?? 0) - 20),
1644
- (m.index ?? 0) + m[0].length + 20
1645
- )
1646
- });
1647
- }
1648
- }
1649
- }
1650
- }
1651
- }
1652
- }
1653
- }
1654
- if (slide.notes) {
1655
- const noteMatches = Array.from(slide.notes.matchAll(regex));
1656
- for (const m of noteMatches) {
1657
- matches.push({
1658
- slideIndex: si,
1659
- elementId: "notes",
1660
- elementType: "notes",
1661
- matchedText: m[0],
1662
- context: slide.notes.slice(
1663
- Math.max(0, (m.index ?? 0) - 20),
1664
- (m.index ?? 0) + m[0].length + 20
1665
- )
1666
- });
1667
- }
1668
- }
1669
- }
1670
- return {
1671
- pptxData: ctx.pptxData,
1672
- dirty: false,
1673
- result: {
1674
- query: params.query,
1675
- matchCount: matches.length,
1676
- matches
1677
- }
1678
- };
1679
- }
1680
- function replaceText(ctx, params) {
1681
- const regex = buildSearchRegex(
1682
- params.query,
1683
- params.useRegex === true,
1684
- params.caseSensitive === true
1685
- );
1686
- if (!regex) {
1687
- return {
1688
- pptxData: ctx.pptxData,
1689
- dirty: false,
1690
- result: { query: params.query, replacement: params.replacement, replacementCount: 0 }
1691
- };
1692
- }
1693
- const targetIndexes = params.slideIndexes ? params.slideIndexes : ctx.pptxData.slides.map((_, i) => i);
1694
- let count = 0;
1695
- for (const si of targetIndexes) {
1696
- const err = validateSlideIndex(si, ctx.pptxData.slides.length);
1697
- if (err) {
1698
- throw new Error(err);
1699
- }
1700
- const slide = ctx.pptxData.slides[si];
1701
- for (const el of slide.elements) {
1702
- if (pptxViewerCore.hasTextProperties(el)) {
1703
- const textEl = el;
1704
- if (textEl.text) {
1705
- const before = textEl.text;
1706
- const after = before.replace(regex, params.replacement);
1707
- if (after !== before) {
1708
- const matchCount = Array.from(before.matchAll(regex)).length;
1709
- count += matchCount;
1710
- replaceElementText(textEl, regex, params.replacement);
1711
- }
1712
- }
1713
- }
1714
- if (el.type === "table") {
1715
- const tbl = el;
1716
- if (tbl.tableData) {
1717
- for (const row of tbl.tableData.rows) {
1718
- for (const cell of row.cells) {
1719
- if (cell.text) {
1720
- const before = cell.text;
1721
- const after = before.replace(regex, params.replacement);
1722
- if (after !== before) {
1723
- count += Array.from(before.matchAll(regex)).length;
1724
- cell.text = after;
1725
- }
1726
- }
1727
- }
1728
- }
1729
- }
1730
- }
1731
- }
1732
- if (slide.notes) {
1733
- const before = slide.notes;
1734
- const after = before.replace(regex, params.replacement);
1735
- if (after !== before) {
1736
- count += Array.from(before.matchAll(regex)).length;
1737
- slide.notes = after;
1738
- }
1739
- }
1740
- }
1741
- return {
1742
- pptxData: ctx.pptxData,
1743
- dirty: count > 0,
1744
- result: {
1745
- query: params.query,
1746
- replacement: params.replacement,
1747
- replacementCount: count
1748
- }
1749
- };
1750
- }
1751
- function manageComments(ctx, params) {
1752
- switch (params.action) {
1753
- case "list": {
1754
- const all = [];
1755
- for (let si = 0; si < ctx.pptxData.slides.length; si++) {
1756
- if (params.slideIndex !== void 0 && si !== params.slideIndex) {
1757
- continue;
1758
- }
1759
- const slide = ctx.pptxData.slides[si];
1760
- for (const c of slide.comments ?? []) {
1761
- if (!params.includeResolved && c.resolved) {
1762
- continue;
1763
- }
1764
- all.push({
1765
- id: c.id,
1766
- slideIndex: si,
1767
- text: c.text,
1768
- author: c.author,
1769
- createdAt: c.createdAt,
1770
- resolved: c.resolved
1771
- });
1772
- }
1773
- }
1774
- return {
1775
- pptxData: ctx.pptxData,
1776
- dirty: false,
1777
- result: { action: "list", comments: all, count: all.length }
1778
- };
1779
- }
1780
- case "add": {
1781
- if (params.slideIndex === void 0) {
1782
- throw new Error("slideIndex is required for add action.");
1783
- }
1784
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1785
- if (err) {
1786
- throw new Error(err);
1787
- }
1788
- if (!params.text) {
1789
- throw new Error("text is required for add action.");
1790
- }
1791
- const slide = ctx.pptxData.slides[params.slideIndex];
1792
- if (!slide.comments) {
1793
- slide.comments = [];
1794
- }
1795
- const newId = `comment-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1796
- slide.comments.push({
1797
- id: newId,
1798
- text: params.text,
1799
- author: params.author,
1800
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1801
- resolved: false
1802
- });
1803
- return {
1804
- pptxData: ctx.pptxData,
1805
- dirty: true,
1806
- result: { action: "add", commentId: newId }
1807
- };
1808
- }
1809
- case "delete": {
1810
- if (!params.commentId) {
1811
- throw new Error("commentId is required for delete action.");
1812
- }
1813
- let deleted = false;
1814
- for (const slide of ctx.pptxData.slides) {
1815
- if (!slide.comments) {
1816
- continue;
1817
- }
1818
- const before = slide.comments.length;
1819
- slide.comments = slide.comments.filter((c) => c.id !== params.commentId);
1820
- if (slide.comments.length < before) {
1821
- deleted = true;
1822
- break;
1823
- }
1824
- }
1825
- if (!deleted) {
1826
- throw new Error(`Comment '${params.commentId}' not found.`);
1827
- }
1828
- return {
1829
- pptxData: ctx.pptxData,
1830
- dirty: true,
1831
- result: { action: "delete", commentId: params.commentId }
1832
- };
1833
- }
1834
- case "resolve": {
1835
- if (!params.commentId) {
1836
- throw new Error("commentId is required for resolve action.");
1837
- }
1838
- let found = false;
1839
- for (const slide of ctx.pptxData.slides) {
1840
- const comment = slide.comments?.find((c) => c.id === params.commentId);
1841
- if (comment) {
1842
- comment.resolved = params.resolved !== false;
1843
- found = true;
1844
- break;
1845
- }
1846
- }
1847
- if (!found) {
1848
- throw new Error(`Comment '${params.commentId}' not found.`);
1849
- }
1850
- return {
1851
- pptxData: ctx.pptxData,
1852
- dirty: true,
1853
- result: { action: "resolve", commentId: params.commentId }
1854
- };
1855
- }
1856
- default: {
1857
- throw new Error(`Unknown action: ${String(params.action)}`);
1858
- }
1859
- }
1860
- }
1861
- async function convertToMarkdown(ctx, params) {
1862
- const options = {
1863
- mediaFolderName: params.mediaFolderName ?? "media",
1864
- includeMetadata: params.includeMetadata !== false,
1865
- slideRange: params.slideRange,
1866
- includeSpeakerNotes: params.includeSpeakerNotes !== false,
1867
- semanticMode: params.semanticMode === true,
1868
- sourceName: params.sourceName ?? "unknown"
1869
- };
1870
- const outputDir = params.outputDir ?? ".";
1871
- const safeOutputDir = params.scopeOutputDir ? params.scopeOutputDir(outputDir, params.rootDir) : outputDir;
1872
- const converter = new pptxViewerCore.PptxMarkdownConverter(safeOutputDir, options);
1873
- const markdown = await converter.convert(ctx.pptxData);
1874
- return {
1875
- pptxData: ctx.pptxData,
1876
- dirty: false,
1877
- result: {
1878
- markdown,
1879
- slidesConverted: converter.slidesConverted,
1880
- totalSlides: converter.presentationSlides,
1881
- imagesExtracted: converter.imagesExtracted
1882
- }
1883
- };
1884
- }
1885
- function getThemeInfo(ctx) {
1886
- const theme = ctx.pptxData.theme;
1887
- return {
1888
- pptxData: ctx.pptxData,
1889
- dirty: false,
1890
- result: {
1891
- themeName: theme?.name,
1892
- colorScheme: theme?.colorScheme,
1893
- fontScheme: theme?.fontScheme,
1894
- availablePresets: Object.keys(pptxViewerCore.ThemePresets)
1895
- }
1896
- };
1897
- }
1898
- function applyThemePreset(ctx, params) {
1899
- const preset = pptxViewerCore.getThemePreset(params.presetName);
1900
- if (!preset) {
1901
- throw new Error(
1902
- `Unknown theme preset: "${params.presetName}". Available: ${Object.keys(pptxViewerCore.ThemePresets).join(", ")}`
1903
- );
1904
- }
1905
- if (!ctx.pptxData.theme) {
1906
- ctx.pptxData.theme = {};
1907
- }
1908
- const theme = ctx.pptxData.theme;
1909
- theme.name = preset.name;
1910
- theme.colorScheme = preset.colors;
1911
- if (preset.fonts) {
1912
- theme.fontScheme = preset.fonts;
1913
- }
1914
- return {
1915
- pptxData: ctx.pptxData,
1916
- dirty: true,
1917
- result: {
1918
- appliedPreset: params.presetName,
1919
- colorScheme: theme.colorScheme
1920
- }
1921
- };
1922
- }
1923
- function updateThemeColors(ctx, params) {
1924
- if (!ctx.pptxData.theme) {
1925
- ctx.pptxData.theme = {};
1926
- }
1927
- const theme = ctx.pptxData.theme;
1928
- if (!theme.colorScheme) {
1929
- theme.colorScheme = {
1930
- dk1: "#000000",
1931
- lt1: "#FFFFFF",
1932
- dk2: "#44546A",
1933
- lt2: "#E7E6E6",
1934
- accent1: "#4472C4",
1935
- accent2: "#ED7D31",
1936
- accent3: "#A5A5A5",
1937
- accent4: "#FFC000",
1938
- accent5: "#5B9BD5",
1939
- accent6: "#70AD47",
1940
- hlink: "#0563C1",
1941
- folHlink: "#954F72"
1942
- };
1943
- }
1944
- const cs = theme.colorScheme;
1945
- for (const key of Object.keys(params)) {
1946
- if (params[key] !== void 0) {
1947
- cs[key] = params[key];
1948
- }
1949
- }
1950
- return {
1951
- pptxData: ctx.pptxData,
1952
- dirty: true,
1953
- result: { colorScheme: cs }
1954
- };
1955
- }
1956
- function updateThemeFonts(ctx, params) {
1957
- if (!ctx.pptxData.theme) {
1958
- ctx.pptxData.theme = {};
1959
- }
1960
- const theme = ctx.pptxData.theme;
1961
- if (!theme.fontScheme) {
1962
- theme.fontScheme = {};
1963
- }
1964
- if (params.majorFont !== void 0) {
1965
- theme.fontScheme.majorFont = { latin: params.majorFont };
1966
- }
1967
- if (params.minorFont !== void 0) {
1968
- theme.fontScheme.minorFont = { latin: params.minorFont };
1969
- }
1970
- return {
1971
- pptxData: ctx.pptxData,
1972
- dirty: true,
1973
- result: { fontScheme: theme.fontScheme }
1974
- };
1975
- }
1976
-
1977
- // src/tools/chart-pareto.ts
1978
- function cumulativePercent(values) {
1979
- const total = values.reduce((sum, value) => sum + value, 0);
1980
- if (total === 0) {
1981
- return values.map(() => 0);
1982
- }
1983
- let running = 0;
1984
- return values.map((value) => {
1985
- running += value;
1986
- return Math.round(running / total * 1e4) / 100;
1987
- });
1988
- }
1989
- function applyParetoChartTypeAlias(chart, requestedType) {
1990
- if (requestedType.trim().toLowerCase() !== "pareto" || !chart.chartData) {
1991
- return;
1992
- }
1993
- const data = chart.chartData;
1994
- data.chartType = "histogram";
1995
- const [frequency] = data.series;
1996
- if (!frequency) {
1997
- return;
1998
- }
1999
- frequency.histogramOptions = { ...frequency.histogramOptions, layout: "histogram" };
2000
- const hasParetoLine = data.series.some((s) => s.histogramOptions?.layout === "pareto");
2001
- if (!hasParetoLine) {
2002
- data.series.push({
2003
- name: "Cumulative %",
2004
- values: cumulativePercent(frequency.values),
2005
- histogramOptions: { layout: "pareto" }
2006
- });
2007
- }
2008
- }
2009
-
2010
- // src/tools/chart-tools.ts
2011
- function updateChart(ctx, params) {
2012
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2013
- if (err) {
2014
- throw new Error(err);
2015
- }
2016
- const slide = ctx.pptxData.slides[params.slideIndex];
2017
- const el = slide.elements.find((e) => e.id === params.elementId);
2018
- if (!el) {
2019
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2020
- }
2021
- if (el.type !== "chart") {
2022
- throw new Error(`Element '${params.elementId}' is not a chart.`);
2023
- }
2024
- const chart = el;
2025
- if (params.chartType) {
2026
- pptxViewerCore.setChartType(chart, params.chartType);
2027
- applyParetoChartTypeAlias(chart, params.chartType);
2028
- }
2029
- if (params.title !== void 0) {
2030
- pptxViewerCore.setChartTitle(chart, params.title);
2031
- }
2032
- if (params.grouping) {
2033
- pptxViewerCore.setChartGrouping(chart, params.grouping);
2034
- }
2035
- if (params.legend) {
2036
- pptxViewerCore.setChartLegend(chart, {
2037
- show: params.legend.show,
2038
- position: params.legend.position
2039
- });
2040
- }
2041
- if (params.dataLabels) {
2042
- pptxViewerCore.setChartDataLabels(chart, params.dataLabels);
2043
- }
2044
- if (params.axis) {
2045
- pptxViewerCore.setChartAxis(chart, params.axis.type, params.axis.edit);
2046
- }
2047
- if (params.categories) {
2048
- pptxViewerCore.setChartCategories(chart, params.categories);
2049
- }
2050
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2051
- }
2052
- function addChartSeriesT(ctx, params) {
2053
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2054
- if (err) {
2055
- throw new Error(err);
2056
- }
2057
- const slide = ctx.pptxData.slides[params.slideIndex];
2058
- const el = slide.elements.find((e) => e.id === params.elementId);
2059
- if (!el) {
2060
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2061
- }
2062
- if (el.type !== "chart") {
2063
- throw new Error(`Element '${params.elementId}' is not a chart.`);
2064
- }
2065
- const chart = el;
2066
- pptxViewerCore.addChartSeries(chart, { name: params.name, values: params.values, color: params.color });
2067
- const seriesCount = chart.chartData?.series?.length ?? 0;
2068
- return {
2069
- pptxData: ctx.pptxData,
2070
- dirty: true,
2071
- result: { elementId: params.elementId, seriesCount }
2072
- };
2073
- }
2074
- function removeChartSeriesT(ctx, params) {
2075
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2076
- if (err) {
2077
- throw new Error(err);
2078
- }
2079
- const slide = ctx.pptxData.slides[params.slideIndex];
2080
- const el = slide.elements.find((e) => e.id === params.elementId);
2081
- if (!el) {
2082
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2083
- }
2084
- if (el.type !== "chart") {
2085
- throw new Error(`Element '${params.elementId}' is not a chart.`);
2086
- }
2087
- const chart = el;
2088
- pptxViewerCore.removeChartSeries(chart, params.seriesIndex);
2089
- const seriesCount = chart.chartData?.series?.length ?? 0;
2090
- return {
2091
- pptxData: ctx.pptxData,
2092
- dirty: true,
2093
- result: { elementId: params.elementId, seriesCount }
2094
- };
2095
- }
2096
- function updateChartSeriesData(ctx, params) {
2097
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2098
- if (err) {
2099
- throw new Error(err);
2100
- }
2101
- const slide = ctx.pptxData.slides[params.slideIndex];
2102
- const el = slide.elements.find((e) => e.id === params.elementId);
2103
- if (!el) {
2104
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2105
- }
2106
- if (el.type !== "chart") {
2107
- throw new Error(`Element '${params.elementId}' is not a chart.`);
2108
- }
2109
- const chart = el;
2110
- pptxViewerCore.updateChartSeriesValues(chart, params.seriesIndex, params.values);
2111
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2112
- }
2113
- function createChart(ctx, params) {
2114
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2115
- if (err) {
2116
- throw new Error(err);
2117
- }
2118
- const builder = pptxViewerCore.ChartBuilder.create(
2119
- params.chartType
2120
- );
2121
- builder.position(params.x ?? 100, params.y ?? 100);
2122
- builder.size(params.width ?? 500, params.height ?? 350);
2123
- if (params.title) {
2124
- builder.title(params.title);
2125
- }
2126
- if (params.categories) {
2127
- builder.categories(params.categories);
2128
- }
2129
- if (params.series) {
2130
- for (const s of params.series) {
2131
- builder.addSeries(s.name, s.values, s.color);
2132
- }
2133
- }
2134
- if (params.legend) {
2135
- builder.legend(
2136
- params.legend.show,
2137
- params.legend.position
2138
- );
2139
- }
2140
- const chart = builder.build();
2141
- applyParetoChartTypeAlias(chart, params.chartType);
2142
- const slide = ctx.pptxData.slides[params.slideIndex];
2143
- slide.elements.push(chart);
2144
- return {
2145
- pptxData: ctx.pptxData,
2146
- dirty: true,
2147
- result: { elementId: chart.id, slideIndex: params.slideIndex }
2148
- };
2149
- }
2150
- function findChartElement(ctx, slideIndex, elementId) {
2151
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
2152
- if (err) {
2153
- throw new Error(err);
2154
- }
2155
- const slide = ctx.pptxData.slides[slideIndex];
2156
- const el = slide.elements.find((e) => e.id === elementId);
2157
- if (!el) {
2158
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
2159
- }
2160
- if (el.type !== "chart") {
2161
- throw new Error(`Element '${elementId}' is not a chart.`);
2162
- }
2163
- return el;
2164
- }
2165
- function formatChartDataPoint(ctx, params) {
2166
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
2167
- if (params.clearStyle) {
2168
- pptxViewerCore.setChartDataPointStyle(chart, params.seriesIndex, params.pointIndex, null);
2169
- } else if (params.fillColor !== void 0 || params.strokeColor !== void 0 || params.strokeWidth !== void 0 || params.strokeDashStyle !== void 0) {
2170
- pptxViewerCore.setChartDataPointStyle(chart, params.seriesIndex, params.pointIndex, {
2171
- ...params.fillColor !== void 0 ? { fillColor: params.fillColor } : {},
2172
- ...params.strokeColor !== void 0 ? { strokeColor: params.strokeColor } : {},
2173
- ...params.strokeWidth !== void 0 ? { strokeWidth: params.strokeWidth } : {},
2174
- ...params.strokeDashStyle !== void 0 ? { strokeDashStyle: params.strokeDashStyle } : {}
2175
- });
2176
- }
2177
- if (params.explosion !== void 0) {
2178
- pptxViewerCore.setChartDataPointExplosion(chart, params.seriesIndex, params.pointIndex, params.explosion);
2179
- }
2180
- if (params.marker !== void 0) {
2181
- pptxViewerCore.setChartDataPointMarker(chart, params.seriesIndex, params.pointIndex, params.marker);
2182
- }
2183
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2184
- }
2185
- function formatChartDataLabel(ctx, params) {
2186
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
2187
- if (params.remove) {
2188
- pptxViewerCore.setChartDataPointLabel(chart, params.seriesIndex, params.pointIndex, null);
2189
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2190
- }
2191
- pptxViewerCore.setChartDataPointLabel(chart, params.seriesIndex, params.pointIndex, {
2192
- ...params.showValue !== void 0 ? { showValue: params.showValue } : {},
2193
- ...params.showCategory !== void 0 ? { showCategory: params.showCategory } : {},
2194
- ...params.showSeriesName !== void 0 ? { showSeriesName: params.showSeriesName } : {},
2195
- ...params.showPercent !== void 0 ? { showPercent: params.showPercent } : {},
2196
- ...params.showLegendKey !== void 0 ? { showLegendKey: params.showLegendKey } : {},
2197
- ...params.position !== void 0 ? { position: params.position } : {},
2198
- ...params.text !== void 0 ? { text: params.text } : {},
2199
- ...params.spPr !== void 0 ? { spPr: params.spPr } : {},
2200
- ...params.txPr !== void 0 ? { txPr: params.txPr } : {}
2201
- });
2202
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2203
- }
2204
- function formatChartSeries(ctx, params) {
2205
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
2206
- if (params.marker !== void 0) {
2207
- if (params.marker === null) {
2208
- pptxViewerCore.setChartSeriesMarker(chart, params.seriesIndex, null);
2209
- } else if (params.marker.strokeColor !== void 0 || params.marker.strokeWidth !== void 0 || params.marker.strokeDashStyle !== void 0) {
2210
- pptxViewerCore.setChartSeriesMarker(chart, params.seriesIndex, {
2211
- symbol: params.marker.symbol ?? "circle",
2212
- ...params.marker.size !== void 0 ? { size: params.marker.size } : {},
2213
- spPr: {
2214
- ...params.marker.fillColor !== void 0 ? { fillColor: params.marker.fillColor } : {},
2215
- ...params.marker.strokeColor !== void 0 ? { strokeColor: params.marker.strokeColor } : {},
2216
- ...params.marker.strokeWidth !== void 0 ? { strokeWidth: params.marker.strokeWidth } : {},
2217
- ...params.marker.strokeDashStyle !== void 0 ? { strokeDashStyle: params.marker.strokeDashStyle } : {}
2218
- }
2219
- });
2220
- } else {
2221
- pptxViewerCore.setChartSeriesMarker(chart, params.seriesIndex, params.marker);
2222
- }
2223
- }
2224
- if (params.trendline !== void 0) {
2225
- pptxViewerCore.setChartSeriesTrendline(chart, params.seriesIndex, params.trendline);
2226
- }
2227
- if (params.errorBars !== void 0) {
2228
- pptxViewerCore.setChartSeriesErrorBars(chart, params.seriesIndex, params.errorBars);
2229
- }
2230
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2231
- }
2232
- function setChartHelperLineT(ctx, params) {
2233
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
2234
- pptxViewerCore.setChartHelperLine(chart, params.line, params.style);
2235
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2236
- }
2237
- function setChartColorMapOverrideT(ctx, params) {
2238
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
2239
- pptxViewerCore.setChartColorMapOverride(chart, params.overrides);
2240
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2241
- }
2242
- function findChartElement2(ctx, slideIndex, elementId) {
2243
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
2244
- if (err) {
2245
- throw new Error(err);
2246
- }
2247
- const slide = ctx.pptxData.slides[slideIndex];
2248
- const el = slide.elements.find((e) => e.id === elementId);
2249
- if (!el) {
2250
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
2251
- }
2252
- if (el.type !== "chart") {
2253
- throw new Error(`Element '${elementId}' is not a chart.`);
2254
- }
2255
- return el;
2256
- }
2257
- function buildShape(input) {
2258
- return {
2259
- kind: input.kind ?? "sp",
2260
- anchor: input.anchor ?? "rel",
2261
- from: input.from,
2262
- ...input.to ? { to: input.to } : {},
2263
- ...input.ext ? { ext: input.ext } : {},
2264
- ...input.prst ? { prst: input.prst } : {},
2265
- ...input.fill ? { fill: input.fill } : {},
2266
- ...input.stroke ? { stroke: input.stroke } : {},
2267
- ...input.strokeWidth !== void 0 ? { strokeWidth: input.strokeWidth } : {},
2268
- ...input.text ? { paragraphs: [{ text: input.text, align: "ctr" }] } : {}
2269
- };
2270
- }
2271
- function listChartUserShapesT(ctx, params) {
2272
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
2273
- return { pptxData: ctx.pptxData, dirty: false, result: { shapes: pptxViewerCore.listChartUserShapes(chart) } };
2274
- }
2275
- function addChartUserShapeT(ctx, params) {
2276
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
2277
- pptxViewerCore.addChartUserShape(chart, buildShape(params.shape));
2278
- const index = (chart.chartData?.userShapes?.length ?? 1) - 1;
2279
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId, index } };
2280
- }
2281
- function updateChartUserShapeT(ctx, params) {
2282
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
2283
- const patch = {
2284
- ...params.patch.kind ? { kind: params.patch.kind } : {},
2285
- ...params.patch.anchor ? { anchor: params.patch.anchor } : {},
2286
- ...params.patch.from ? { from: params.patch.from } : {},
2287
- ...params.patch.to ? { to: params.patch.to } : {},
2288
- ...params.patch.ext ? { ext: params.patch.ext } : {},
2289
- ...params.patch.prst ? { prst: params.patch.prst } : {},
2290
- ...params.patch.fill ? { fill: params.patch.fill } : {},
2291
- ...params.patch.stroke ? { stroke: params.patch.stroke } : {},
2292
- ...params.patch.strokeWidth !== void 0 ? { strokeWidth: params.patch.strokeWidth } : {},
2293
- ...params.patch.text !== void 0 ? { paragraphs: [{ text: params.patch.text, align: "ctr" }] } : {}
2294
- };
2295
- pptxViewerCore.updateChartUserShape(chart, params.index, patch);
2296
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2297
- }
2298
- function removeChartUserShapeT(ctx, params) {
2299
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
2300
- pptxViewerCore.removeChartUserShape(chart, params.index);
2301
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
2302
- }
2303
- function extractNodes(data) {
2304
- if (!data.nodes) {
2305
- return [];
2306
- }
2307
- return data.nodes.map((n) => ({
2308
- id: n.id,
2309
- text: n.text ?? "",
2310
- children: n.children?.map((c) => ({ id: c.id, text: c.text ?? "" }))
2311
- }));
2312
- }
2313
- function manageSmartArt(ctx, params) {
2314
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2315
- if (err) {
2316
- throw new Error(err);
2317
- }
2318
- const slide = ctx.pptxData.slides[params.slideIndex];
2319
- const el = slide.elements.find((e) => e.id === params.elementId);
2320
- if (!el) {
2321
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2322
- }
2323
- if (el.type !== "smartArt") {
2324
- throw new Error(`Element '${params.elementId}' is not a SmartArt.`);
2325
- }
2326
- const smartArt = el;
2327
- if (!smartArt.smartArtData) {
2328
- throw new Error(`SmartArt '${params.elementId}' has no data.`);
2329
- }
2330
- switch (params.action) {
2331
- case "getNodes": {
2332
- return {
2333
- pptxData: ctx.pptxData,
2334
- dirty: false,
2335
- result: {
2336
- action: "getNodes",
2337
- elementId: params.elementId,
2338
- nodes: extractNodes(smartArt.smartArtData)
2339
- }
2340
- };
2341
- }
2342
- case "addNode": {
2343
- smartArt.smartArtData = pptxViewerCore.addSmartArtNode(
2344
- smartArt.smartArtData,
2345
- params.text ?? "",
2346
- params.afterNodeId
2347
- );
2348
- return {
2349
- pptxData: ctx.pptxData,
2350
- dirty: true,
2351
- result: {
2352
- action: "addNode",
2353
- elementId: params.elementId,
2354
- nodes: extractNodes(smartArt.smartArtData)
2355
- }
2356
- };
2357
- }
2358
- case "removeNode": {
2359
- if (!params.nodeId) {
2360
- throw new Error("nodeId is required for removeNode.");
2361
- }
2362
- smartArt.smartArtData = pptxViewerCore.removeSmartArtNode(smartArt.smartArtData, params.nodeId);
2363
- return {
2364
- pptxData: ctx.pptxData,
2365
- dirty: true,
2366
- result: {
2367
- action: "removeNode",
2368
- elementId: params.elementId,
2369
- nodes: extractNodes(smartArt.smartArtData)
2370
- }
2371
- };
2372
- }
2373
- case "updateNodeText": {
2374
- if (!params.nodeId) {
2375
- throw new Error("nodeId is required for updateNodeText.");
2376
- }
2377
- smartArt.smartArtData = pptxViewerCore.updateSmartArtNodeText(
2378
- smartArt.smartArtData,
2379
- params.nodeId,
2380
- params.text ?? ""
2381
- );
2382
- return {
2383
- pptxData: ctx.pptxData,
2384
- dirty: true,
2385
- result: { action: "updateNodeText", elementId: params.elementId }
2386
- };
2387
- }
2388
- case "reorderNode": {
2389
- if (!params.nodeId) {
2390
- throw new Error("nodeId is required for reorderNode.");
2391
- }
2392
- const dir = params.direction ?? 1;
2393
- smartArt.smartArtData = pptxViewerCore.reorderSmartArtNode(smartArt.smartArtData, params.nodeId, dir);
2394
- return {
2395
- pptxData: ctx.pptxData,
2396
- dirty: true,
2397
- result: {
2398
- action: "reorderNode",
2399
- elementId: params.elementId,
2400
- nodes: extractNodes(smartArt.smartArtData)
2401
- }
2402
- };
2403
- }
2404
- case "promoteNode": {
2405
- if (!params.nodeId) {
2406
- throw new Error("nodeId is required for promoteNode.");
2407
- }
2408
- smartArt.smartArtData = pptxViewerCore.promoteSmartArtNode(smartArt.smartArtData, params.nodeId);
2409
- return {
2410
- pptxData: ctx.pptxData,
2411
- dirty: true,
2412
- result: {
2413
- action: "promoteNode",
2414
- elementId: params.elementId,
2415
- nodes: extractNodes(smartArt.smartArtData)
2416
- }
2417
- };
2418
- }
2419
- case "demoteNode": {
2420
- if (!params.nodeId) {
2421
- throw new Error("nodeId is required for demoteNode.");
2422
- }
2423
- smartArt.smartArtData = pptxViewerCore.demoteSmartArtNode(smartArt.smartArtData, params.nodeId);
2424
- return {
2425
- pptxData: ctx.pptxData,
2426
- dirty: true,
2427
- result: {
2428
- action: "demoteNode",
2429
- elementId: params.elementId,
2430
- nodes: extractNodes(smartArt.smartArtData)
2431
- }
2432
- };
2433
- }
2434
- case "decompose": {
2435
- const bounds = {
2436
- x: smartArt.x,
2437
- y: smartArt.y,
2438
- width: smartArt.width,
2439
- height: smartArt.height
2440
- };
2441
- const decomposed = pptxViewerCore.decomposeSmartArt(smartArt.smartArtData, bounds);
2442
- if (!decomposed || decomposed.length === 0) {
2443
- throw new Error("Failed to decompose SmartArt into shapes.");
2444
- }
2445
- const idx = slide.elements.indexOf(el);
2446
- slide.elements.splice(idx, 1, ...decomposed);
2447
- return {
2448
- pptxData: ctx.pptxData,
2449
- dirty: true,
2450
- result: {
2451
- action: "decompose",
2452
- elementId: params.elementId,
2453
- decomposedCount: decomposed.length
2454
- }
2455
- };
2456
- }
2457
- default:
2458
- throw new Error(`Unknown SmartArt action: ${String(params.action)}`);
2459
- }
2460
- }
2461
- function mergePresentationT(ctx, params) {
2462
- const options = {};
2463
- if (params.slideIndices) {
2464
- options.slideIndices = params.slideIndices;
2465
- }
2466
- if (params.insertAt !== void 0) {
2467
- options.insertAt = params.insertAt;
2468
- }
2469
- if (params.keepSourceTheme !== void 0) {
2470
- options.keepSourceTheme = params.keepSourceTheme;
2471
- }
2472
- const added = pptxViewerCore.mergePresentation(ctx.pptxData, params.sourceData, options);
2473
- return {
2474
- pptxData: ctx.pptxData,
2475
- dirty: added > 0,
2476
- result: { slidesAdded: added, totalSlides: ctx.pptxData.slides.length }
2477
- };
2478
- }
2479
- function diffPresentationsT(ctx, params) {
2480
- const diff = pptxViewerCore.diffPresentations(ctx.pptxData, params.otherData);
2481
- return {
2482
- pptxData: ctx.pptxData,
2483
- dirty: false,
2484
- result: diff
2485
- };
2486
- }
2487
- function findPlaceholdersT(ctx) {
2488
- const placeholders = pptxViewerCore.findPlaceholders(ctx.pptxData);
2489
- return {
2490
- pptxData: ctx.pptxData,
2491
- dirty: false,
2492
- result: { placeholders, count: placeholders.length }
2493
- };
2494
- }
2495
- function applyTemplateT(ctx, params) {
2496
- pptxViewerCore.applyTemplate(ctx.pptxData, params.data);
2497
- return {
2498
- pptxData: ctx.pptxData,
2499
- dirty: true,
2500
- result: { applied: true, slideCount: ctx.pptxData.slides.length }
2501
- };
2502
- }
2503
-
2504
- // src/tools/metadata-tools.ts
2505
- function getMetadata(ctx) {
2506
- return {
2507
- pptxData: ctx.pptxData,
2508
- dirty: false,
2509
- result: {
2510
- coreProperties: ctx.pptxData.coreProperties,
2511
- appProperties: ctx.pptxData.appProperties,
2512
- customProperties: ctx.pptxData.customProperties,
2513
- slideCount: ctx.pptxData.slides.length
2514
- }
2515
- };
2516
- }
2517
- function updateMetadata(ctx, params) {
2518
- const updated = [];
2519
- if (params.title !== void 0 || params.subject !== void 0 || params.creator !== void 0 || params.keywords !== void 0 || params.description !== void 0 || params.lastModifiedBy !== void 0 || params.category !== void 0) {
2520
- if (!ctx.pptxData.coreProperties) {
2521
- ctx.pptxData.coreProperties = {};
2522
- }
2523
- const cp = ctx.pptxData.coreProperties;
2524
- if (params.title !== void 0) {
2525
- cp.title = params.title;
2526
- updated.push("title");
2527
- }
2528
- if (params.subject !== void 0) {
2529
- cp.subject = params.subject;
2530
- updated.push("subject");
2531
- }
2532
- if (params.creator !== void 0) {
2533
- cp.creator = params.creator;
2534
- updated.push("creator");
2535
- }
2536
- if (params.keywords !== void 0) {
2537
- cp.keywords = params.keywords;
2538
- updated.push("keywords");
2539
- }
2540
- if (params.description !== void 0) {
2541
- cp.description = params.description;
2542
- updated.push("description");
2543
- }
2544
- if (params.lastModifiedBy !== void 0) {
2545
- cp.lastModifiedBy = params.lastModifiedBy;
2546
- updated.push("lastModifiedBy");
2547
- }
2548
- if (params.category !== void 0) {
2549
- cp.category = params.category;
2550
- updated.push("category");
2551
- }
2552
- }
2553
- if (params.company !== void 0 || params.manager !== void 0) {
2554
- if (!ctx.pptxData.appProperties) {
2555
- ctx.pptxData.appProperties = {};
2556
- }
2557
- const ap = ctx.pptxData.appProperties;
2558
- if (params.company !== void 0) {
2559
- ap.company = params.company;
2560
- updated.push("company");
2561
- }
2562
- if (params.manager !== void 0) {
2563
- ap.manager = params.manager;
2564
- updated.push("manager");
2565
- }
2566
- }
2567
- if (params.customProperties) {
2568
- if (!ctx.pptxData.customProperties) {
2569
- ctx.pptxData.customProperties = [];
2570
- }
2571
- for (const cp of params.customProperties) {
2572
- const valueStr = String(cp.value);
2573
- const vtType = typeof cp.value === "number" ? "i4" : typeof cp.value === "boolean" ? "bool" : "lpwstr";
2574
- const existing = ctx.pptxData.customProperties.find((p) => p.name === cp.name);
2575
- if (existing) {
2576
- existing.value = valueStr;
2577
- existing.type = vtType;
2578
- } else {
2579
- ctx.pptxData.customProperties.push({ name: cp.name, value: valueStr, type: vtType });
2580
- }
2581
- updated.push(`custom:${cp.name}`);
2582
- }
2583
- }
2584
- return {
2585
- pptxData: ctx.pptxData,
2586
- dirty: updated.length > 0,
2587
- result: { updated }
2588
- };
2589
- }
2590
- function manageSections(ctx, params) {
2591
- switch (params.action) {
2592
- case "list": {
2593
- const sections = (ctx.pptxData.sections ?? []).map((s) => ({
2594
- id: s.id,
2595
- name: s.name,
2596
- slideIds: s.slideIds,
2597
- collapsed: s.collapsed,
2598
- color: s.color
2599
- }));
2600
- return { pptxData: ctx.pptxData, dirty: false, result: { action: "list", sections } };
2601
- }
2602
- case "add": {
2603
- if (!params.name) {
2604
- throw new Error("name is required for add action.");
2605
- }
2606
- const section = pptxViewerCore.addSection(ctx.pptxData, params.name, params.slideIndices ?? []);
2607
- return {
2608
- pptxData: ctx.pptxData,
2609
- dirty: true,
2610
- result: {
2611
- action: "add",
2612
- section: { id: section.id, name: section.name, slideIds: section.slideIds }
2613
- }
2614
- };
2615
- }
2616
- case "remove": {
2617
- if (!params.sectionId) {
2618
- throw new Error("sectionId is required for remove action.");
2619
- }
2620
- const success = pptxViewerCore.removeSection(ctx.pptxData, params.sectionId);
2621
- return { pptxData: ctx.pptxData, dirty: success, result: { action: "remove", success } };
2622
- }
2623
- case "reorder": {
2624
- if (!params.sectionIds) {
2625
- throw new Error("sectionIds is required for reorder action.");
2626
- }
2627
- pptxViewerCore.reorderSections(ctx.pptxData, params.sectionIds);
2628
- return { pptxData: ctx.pptxData, dirty: true, result: { action: "reorder", success: true } };
2629
- }
2630
- case "moveSlides": {
2631
- if (!params.sectionId) {
2632
- throw new Error("sectionId is required for moveSlides action.");
2633
- }
2634
- if (!params.slideIndices) {
2635
- throw new Error("slideIndices is required for moveSlides action.");
2636
- }
2637
- const success = pptxViewerCore.moveSlidesToSection(ctx.pptxData, params.slideIndices, params.sectionId);
2638
- return { pptxData: ctx.pptxData, dirty: success, result: { action: "moveSlides", success } };
2639
- }
2640
- case "getForSlide": {
2641
- if (params.slideIndex === void 0) {
2642
- throw new Error("slideIndex is required for getForSlide action.");
2643
- }
2644
- const section = pptxViewerCore.getSectionForSlide(ctx.pptxData, params.slideIndex);
2645
- const info = section ? { id: section.id, name: section.name, slideIds: section.slideIds } : null;
2646
- return {
2647
- pptxData: ctx.pptxData,
2648
- dirty: false,
2649
- result: { action: "getForSlide", section: info }
2650
- };
2651
- }
2652
- default:
2653
- throw new Error(`Unknown section action: ${String(params.action)}`);
2654
- }
2655
- }
2656
- function exportToSvg(ctx, params) {
2657
- if (params.slideIndices) {
2658
- for (const si of params.slideIndices) {
2659
- const err = validateSlideIndex(si, ctx.pptxData.slides.length);
2660
- if (err) {
2661
- throw new Error(err);
2662
- }
2663
- }
2664
- }
2665
- const options = {};
2666
- if (params.slideIndices) {
2667
- options.slideIndices = params.slideIndices;
2668
- }
2669
- if (params.includeHidden !== void 0) {
2670
- options.includeHidden = params.includeHidden;
2671
- }
2672
- if (params.defaultFontFamily) {
2673
- options.defaultFontFamily = params.defaultFontFamily;
2674
- }
2675
- if (params.defaultFontSize) {
2676
- options.defaultFontSize = params.defaultFontSize;
2677
- }
2678
- const svgStrings = pptxViewerCore.SvgExporter.exportAll(ctx.pptxData, options);
2679
- return {
2680
- pptxData: ctx.pptxData,
2681
- dirty: false,
2682
- result: { svgStrings, slideCount: svgStrings.length }
2683
- };
2684
- }
2685
- function exportSlideSvg(ctx, params) {
2686
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2687
- if (err) {
2688
- throw new Error(err);
2689
- }
2690
- const slide = ctx.pptxData.slides[params.slideIndex];
2691
- const width = ctx.pptxData.width ?? 960;
2692
- const height = ctx.pptxData.height ?? 540;
2693
- const options = {};
2694
- if (params.defaultFontFamily) {
2695
- options.defaultFontFamily = params.defaultFontFamily;
2696
- }
2697
- if (params.defaultFontSize) {
2698
- options.defaultFontSize = params.defaultFontSize;
2699
- }
2700
- const svg = pptxViewerCore.SvgExporter.exportSlide(slide, width, height, options);
2701
- return {
2702
- pptxData: ctx.pptxData,
2703
- dirty: false,
2704
- result: { svg, slideIndex: params.slideIndex }
2705
- };
2706
- }
2707
- function exportToJson(ctx, params) {
2708
- const document = pptxViewerCore.buildPptxJsonDocument(ctx.pptxData, { generator: "pptx-viewer-mcp" });
2709
- const json = JSON.stringify(document, null, params.pretty === false ? void 0 : 2);
2710
- return {
2711
- pptxData: ctx.pptxData,
2712
- dirty: false,
2713
- result: {
2714
- json,
2715
- slideCount: document.slideCount,
2716
- byteSize: new TextEncoder().encode(json).byteLength,
2717
- assets: document.assets
2718
- }
2719
- };
2720
- }
2721
- function importFromJson(ctx, params) {
2722
- const imported = pptxViewerCore.deserializePptxFromJson(params.json);
2723
- pptxViewerCore.applyImportedPptxData(ctx.pptxData, imported);
2724
- return {
2725
- pptxData: ctx.pptxData,
2726
- dirty: true,
2727
- result: {
2728
- slideCount: ctx.pptxData.slides.length,
2729
- width: ctx.pptxData.width,
2730
- height: ctx.pptxData.height
2731
- }
2732
- };
2733
- }
2734
-
2735
- // src/tools/hyperlink-tools.ts
2736
- function manageHyperlinks(ctx, params) {
2737
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2738
- if (err) {
2739
- throw new Error(err);
2740
- }
2741
- const slide = ctx.pptxData.slides[params.slideIndex];
2742
- switch (params.action) {
2743
- case "list": {
2744
- const hyperlinks = [];
2745
- for (const el of slide.elements) {
2746
- if (el.actionClick) {
2747
- hyperlinks.push({
2748
- elementId: el.id,
2749
- trigger: "click",
2750
- url: el.actionClick.url,
2751
- tooltip: el.actionClick.tooltip,
2752
- targetSlideIndex: el.actionClick.targetSlideIndex,
2753
- actionType: el.actionClick.action
2754
- });
2755
- }
2756
- if (el.actionHover) {
2757
- hyperlinks.push({
2758
- elementId: el.id,
2759
- trigger: "hover",
2760
- url: el.actionHover.url,
2761
- tooltip: el.actionHover.tooltip,
2762
- targetSlideIndex: el.actionHover.targetSlideIndex,
2763
- actionType: el.actionHover.action
2764
- });
2765
- }
2766
- }
2767
- return { pptxData: ctx.pptxData, dirty: false, result: { action: "list", hyperlinks } };
2768
- }
2769
- case "set": {
2770
- if (!params.elementId) {
2771
- throw new Error("elementId is required for set action.");
2772
- }
2773
- const el = slide.elements.find((e) => e.id === params.elementId);
2774
- if (!el) {
2775
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2776
- }
2777
- const trigger = params.trigger ?? "click";
2778
- const action = {};
2779
- if (params.url) {
2780
- action.url = params.url;
2781
- }
2782
- if (params.tooltip) {
2783
- action.tooltip = params.tooltip;
2784
- }
2785
- if (params.targetSlideIndex !== void 0) {
2786
- action.targetSlideIndex = params.targetSlideIndex;
2787
- }
2788
- if (params.actionType) {
2789
- switch (params.actionType) {
2790
- case "url":
2791
- action.action = "ppaction://hlinkpres";
2792
- break;
2793
- case "slide":
2794
- action.action = "ppaction://hlinksldjump";
2795
- break;
2796
- case "nextSlide":
2797
- action.action = "ppaction://hlinkshowjump?jump=nextslide";
2798
- break;
2799
- case "prevSlide":
2800
- action.action = "ppaction://hlinkshowjump?jump=previousslide";
2801
- break;
2802
- case "firstSlide":
2803
- action.action = "ppaction://hlinkshowjump?jump=firstslide";
2804
- break;
2805
- case "lastSlide":
2806
- action.action = "ppaction://hlinkshowjump?jump=lastslide";
2807
- break;
2808
- case "endShow":
2809
- action.action = "ppaction://hlinkshowjump?jump=endshow";
2810
- break;
2811
- }
2812
- }
2813
- if (trigger === "click") {
2814
- el.actionClick = action;
2815
- } else {
2816
- el.actionHover = action;
2817
- }
2818
- return {
2819
- pptxData: ctx.pptxData,
2820
- dirty: true,
2821
- result: { action: "set", elementId: params.elementId }
2822
- };
2823
- }
2824
- case "remove": {
2825
- if (!params.elementId) {
2826
- throw new Error("elementId is required for remove action.");
2827
- }
2828
- const el = slide.elements.find((e) => e.id === params.elementId);
2829
- if (!el) {
2830
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2831
- }
2832
- const trigger = params.trigger ?? "click";
2833
- if (trigger === "click") {
2834
- el.actionClick = void 0;
2835
- } else {
2836
- el.actionHover = void 0;
2837
- }
2838
- return {
2839
- pptxData: ctx.pptxData,
2840
- dirty: true,
2841
- result: { action: "remove", elementId: params.elementId }
2842
- };
2843
- }
2844
- default:
2845
- throw new Error(`Unknown hyperlink action: ${String(params.action)}`);
2846
- }
2847
- }
2848
- function replaceGeometry(ctx, params) {
2849
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2850
- if (err) {
2851
- throw new Error(err);
2852
- }
2853
- const slide = ctx.pptxData.slides[params.slideIndex];
2854
- const el = slide.elements.find((e) => e.id === params.elementId);
2855
- if (!el) {
2856
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2857
- }
2858
- if (el.type !== "shape") {
2859
- throw new Error(`Element '${params.elementId}' is not a shape.`);
2860
- }
2861
- const shape = el;
2862
- if (params.svgPath) {
2863
- pptxViewerCore.replaceWithCustomGeometry(shape, params.svgPath, params.pathWidth, params.pathHeight);
2864
- return {
2865
- pptxData: ctx.pptxData,
2866
- dirty: true,
2867
- result: { elementId: params.elementId, geometryType: "custom" }
2868
- };
2869
- }
2870
- if (params.shapeType) {
2871
- pptxViewerCore.replaceShapeGeometry(shape, params.shapeType, params.adjustments);
2872
- return {
2873
- pptxData: ctx.pptxData,
2874
- dirty: true,
2875
- result: { elementId: params.elementId, geometryType: params.shapeType }
2876
- };
2877
- }
2878
- throw new Error("Either shapeType or svgPath must be provided.");
2879
- }
2880
- function setElementLockT(ctx, params) {
2881
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2882
- if (err) {
2883
- throw new Error(err);
2884
- }
2885
- const slide = ctx.pptxData.slides[params.slideIndex];
2886
- const idx = slide.elements.findIndex((e) => e.id === params.elementId);
2887
- if (idx < 0) {
2888
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2889
- }
2890
- const extraLocks = {};
2891
- if (params.noMove !== void 0) {
2892
- extraLocks["noMove"] = params.noMove;
2893
- }
2894
- if (params.noResize !== void 0) {
2895
- extraLocks["noResize"] = params.noResize;
2896
- }
2897
- if (params.noRotation !== void 0) {
2898
- extraLocks["noRotation"] = params.noRotation;
2899
- }
2900
- if (params.noSelect !== void 0) {
2901
- extraLocks["noSelect"] = params.noSelect;
2902
- }
2903
- if (params.noTextEdit !== void 0) {
2904
- extraLocks["noTextEdit"] = params.noTextEdit;
2905
- }
2906
- const updated = pptxViewerCore.setElementLocked(slide.elements[idx], params.locked, extraLocks);
2907
- slide.elements[idx] = updated;
2908
- return {
2909
- pptxData: ctx.pptxData,
2910
- dirty: true,
2911
- result: { elementId: params.elementId, locked: params.locked }
2912
- };
2913
- }
2914
- async function validatePresentation(rawBytes) {
2915
- const result = await pptxViewerCore.validatePptx(rawBytes);
2916
- return {
2917
- valid: result.valid,
2918
- issueCount: result.issues.length,
2919
- errors: result.issues.filter((i) => i.severity === "error").length,
2920
- warnings: result.issues.filter((i) => i.severity === "warning").length,
2921
- issues: result.issues
2922
- };
2923
- }
2924
- async function repairPresentation(rawBytes) {
2925
- const result = await pptxViewerCore.repairPptx(rawBytes);
2926
- return {
2927
- result: {
2928
- repairCount: result.repairs.length,
2929
- repairs: result.repairs
2930
- },
2931
- repairedBytes: result.repaired
2932
- };
2933
- }
2934
-
2935
- // src/tools/presentation-tools.ts
2936
- function getPresentationProperties(ctx) {
2937
- return {
2938
- pptxData: ctx.pptxData,
2939
- dirty: false,
2940
- result: {
2941
- properties: ctx.pptxData.presentationProperties,
2942
- slideCount: ctx.pptxData.slides.length,
2943
- width: ctx.pptxData.width,
2944
- height: ctx.pptxData.height
2945
- }
2946
- };
2947
- }
2948
- function updatePresentationProperties(ctx, params) {
2949
- if (!ctx.pptxData.presentationProperties) {
2950
- ctx.pptxData.presentationProperties = {};
2951
- }
2952
- const pp = ctx.pptxData.presentationProperties;
2953
- if (params.showType !== void 0) {
2954
- pp.showType = params.showType;
2955
- }
2956
- if (params.loopContinuously !== void 0) {
2957
- pp.loopContinuously = params.loopContinuously;
2958
- }
2959
- if (params.showWithNarration !== void 0) {
2960
- pp.showWithNarration = params.showWithNarration;
2961
- }
2962
- if (params.showWithAnimation !== void 0) {
2963
- pp.showWithAnimation = params.showWithAnimation;
2964
- }
2965
- if (params.advanceMode !== void 0) {
2966
- pp.advanceMode = params.advanceMode;
2967
- }
2968
- if (params.showSlidesMode !== void 0) {
2969
- pp.showSlidesMode = params.showSlidesMode;
2970
- }
2971
- if (params.showSlidesFrom !== void 0) {
2972
- pp.showSlidesFrom = params.showSlidesFrom;
2973
- }
2974
- if (params.showSlidesTo !== void 0) {
2975
- pp.showSlidesTo = params.showSlidesTo;
2976
- }
2977
- if (params.penColor !== void 0) {
2978
- pp.penColor = params.penColor;
2979
- }
2980
- return {
2981
- pptxData: ctx.pptxData,
2982
- dirty: true,
2983
- result: { properties: pp }
2984
- };
2985
- }
2986
- function getLayouts(ctx) {
2987
- const layouts = [];
2988
- if (ctx.pptxData.slideMasters) {
2989
- for (const master of ctx.pptxData.slideMasters) {
2990
- if (master.layouts) {
2991
- for (const layout of master.layouts) {
2992
- layouts.push({
2993
- name: layout.name ?? "Unknown",
2994
- path: layout.path
2995
- });
2996
- }
2997
- }
2998
- }
2999
- }
3000
- if (ctx.pptxData.layoutOptions) {
3001
- for (const opt of ctx.pptxData.layoutOptions) {
3002
- if (!layouts.some((l) => l.path === opt.path)) {
3003
- layouts.push({ name: opt.name, path: opt.path, type: opt.type });
3004
- }
3005
- }
3006
- }
3007
- return {
3008
- pptxData: ctx.pptxData,
3009
- dirty: false,
3010
- result: { layouts, count: layouts.length }
3011
- };
3012
- }
3013
- function applyLayout(ctx, params) {
3014
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3015
- if (err) {
3016
- throw new Error(err);
3017
- }
3018
- let layout;
3019
- if (params.layoutName) {
3020
- layout = pptxViewerCore.findLayoutByName(ctx.pptxData, params.layoutName);
3021
- } else if (params.layoutType) {
3022
- layout = pptxViewerCore.findLayoutByType(ctx.pptxData, params.layoutType);
3023
- } else {
3024
- throw new Error("Either layoutName or layoutType must be provided.");
3025
- }
3026
- if (!layout) {
3027
- throw new Error(
3028
- `Layout not found: ${params.layoutName ?? params.layoutType}. Use get_layouts to see available layouts.`
3029
- );
3030
- }
3031
- const slide = ctx.pptxData.slides[params.slideIndex];
3032
- slide.layoutName = layout.name;
3033
- if (layout.path) {
3034
- slide.layoutPath = layout.path;
3035
- }
3036
- const master = ctx.pptxData.slideMasters?.find(
3037
- (candidate) => candidate.layouts?.some((definition2) => definition2.path === layout.path)
3038
- );
3039
- const definition = master?.layouts?.find((candidate) => candidate.path === layout.path);
3040
- for (const frame of definition?.placeholders ?? []) {
3041
- if (["dt", "ftr", "sldnum", "hdr"].includes(frame.type)) {
3042
- continue;
3043
- }
3044
- const ph = {
3045
- "@_type": frame.type === "ctrtitle" ? "ctrTitle" : frame.type,
3046
- ...frame.idx !== void 0 ? { "@_idx": frame.idx } : {}
3047
- };
3048
- if (slide.elements.some((element) => {
3049
- const nv = element.rawXml?.["p:nvSpPr"];
3050
- const nvPr = nv?.["p:nvPr"];
3051
- const existing = nvPr?.["p:ph"];
3052
- return element.placeholderType === frame.type && String(existing?.["@_idx"] ?? "0") === String(frame.idx ?? "0");
3053
- })) {
3054
- continue;
3055
- }
3056
- const inherited = master?.placeholders?.find(
3057
- (candidate) => candidate.type === frame.type && (candidate.idx ?? "0") === (frame.idx ?? "0")
3058
- ) ?? master?.placeholders?.find((candidate) => candidate.type === frame.type);
3059
- slide.elements.push({
3060
- id: generateElementId(),
3061
- type: "text",
3062
- placeholderType: frame.type,
3063
- x: frame.x ?? inherited?.x ?? 0,
3064
- y: frame.y ?? inherited?.y ?? 0,
3065
- width: frame.width ?? inherited?.width ?? ctx.pptxData.width,
3066
- height: frame.height ?? inherited?.height ?? ctx.pptxData.height,
3067
- text: "",
3068
- textSegments: [{ text: "", style: {} }],
3069
- rawXml: {
3070
- "p:nvSpPr": {
3071
- "p:cNvPr": { "@_name": frame.type },
3072
- "p:cNvSpPr": {},
3073
- "p:nvPr": { "p:ph": ph }
3074
- },
3075
- "p:spPr": {}
3076
- }
3077
- });
3078
- }
3079
- return {
3080
- pptxData: ctx.pptxData,
3081
- dirty: true,
3082
- result: { slideIndex: params.slideIndex, layoutName: layout.name ?? "Unknown" }
3083
- };
3084
- }
3085
- function resolveOleElement(ctx, slideIndex, elementId) {
3086
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
3087
- if (err) {
3088
- throw new Error(err);
3089
- }
3090
- const slide = ctx.pptxData.slides[slideIndex];
3091
- const index = slide.elements.findIndex((e) => e.id === elementId);
3092
- if (index === -1) {
3093
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
3094
- }
3095
- const element = slide.elements[index];
3096
- if (element.type !== "ole") {
3097
- throw new Error(`Element '${elementId}' is not an OLE object.`);
3098
- }
3099
- return { slide, element, index };
3100
- }
3101
- function commitOleElement(slide, index, updated) {
3102
- slide.elements.splice(index, 1, updated);
3103
- slide.isDirty = true;
3104
- }
3105
- async function getOleContent(ctx, params) {
3106
- const { element } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3107
- const kind = await pptxViewerCore.resolveOleEditorKindFromPayload(element);
3108
- const base = { pptxData: ctx.pptxData, dirty: false };
3109
- switch (kind) {
3110
- case "sheet-xlsx":
3111
- case "sheet-xls": {
3112
- const sheet = await pptxViewerCore.getOleSheetGrid(element);
3113
- return { ...base, result: { elementId: element.id, kind, editable: true, sheet } };
3114
- }
3115
- case "document-docx":
3116
- case "document-doc": {
3117
- const paragraphs = await pptxViewerCore.getOleDocumentParagraphs(element);
3118
- return { ...base, result: { elementId: element.id, kind, editable: true, paragraphs } };
3119
- }
3120
- case "deck-pptx": {
3121
- const deckSlides = await pptxViewerCore.getOleNestedDeckDetail(element);
3122
- return { ...base, result: { elementId: element.id, kind, editable: true, deckSlides } };
3123
- }
3124
- default:
3125
- return {
3126
- ...base,
3127
- result: {
3128
- elementId: element.id,
3129
- kind,
3130
- editable: false,
3131
- message: `This OLE object's content (${kind}) is not directly editable; use ole_replace_file to replace it wholesale.`
3132
- }
3133
- };
3134
- }
3135
- }
3136
- async function setOleSheetCell(ctx, params) {
3137
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3138
- const updated = await pptxViewerCore.applyOleSheetCellEdit(element, {
3139
- row: params.row,
3140
- col: params.col,
3141
- value: params.value
3142
- });
3143
- const changed = updated !== element;
3144
- if (changed) {
3145
- commitOleElement(slide, index, updated);
3146
- }
3147
- return {
3148
- pptxData: ctx.pptxData,
3149
- dirty: changed,
3150
- result: { elementId: params.elementId, changed }
3151
- };
3152
- }
3153
- async function setOleDocumentParagraph(ctx, params) {
3154
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3155
- const updated = await pptxViewerCore.applyOleDocumentParagraphEdit(element, params.paragraphIndex, params.text);
3156
- const changed = updated !== element;
3157
- if (changed) {
3158
- commitOleElement(slide, index, updated);
3159
- }
3160
- return {
3161
- pptxData: ctx.pptxData,
3162
- dirty: changed,
3163
- result: { elementId: params.elementId, changed }
3164
- };
3165
- }
3166
- async function setOleDeckSlideTitle(ctx, params) {
3167
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3168
- const updated = await pptxViewerCore.applyOleNestedDeckElementTextEdit(
3169
- element,
3170
- params.deckSlideIndex,
3171
- params.deckElementId,
3172
- params.title
3173
- );
3174
- const changed = updated !== element;
3175
- if (changed) {
3176
- commitOleElement(slide, index, updated);
3177
- }
3178
- return {
3179
- pptxData: ctx.pptxData,
3180
- dirty: changed,
3181
- result: { elementId: params.elementId, changed }
3182
- };
3183
- }
3184
- async function setOleObjectNameT(ctx, params) {
3185
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3186
- const updated = await pptxViewerCore.setOleObjectName(element, params.name);
3187
- commitOleElement(slide, index, updated);
3188
- return {
3189
- pptxData: ctx.pptxData,
3190
- dirty: true,
3191
- result: { elementId: params.elementId, changed: true }
3192
- };
3193
- }
3194
- async function replaceOleFileT(ctx, params) {
3195
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3196
- const parsed = pptxViewerCore.parseDataUrlToBytes(params.fileData);
3197
- if (!parsed) {
3198
- throw new Error('fileData must be a base64 data URL, e.g. "data:<mime-type>;base64,<...>".');
3199
- }
3200
- const updated = await pptxViewerCore.replaceOleFile(element, parsed.bytes, params.fileName);
3201
- commitOleElement(slide, index, updated);
3202
- return {
3203
- pptxData: ctx.pptxData,
3204
- dirty: true,
3205
- result: { elementId: params.elementId, changed: true }
3206
- };
3207
- }
7
+ var loadPresentation = automation.apiExecution.loadPresentation;
8
+ var savePresentation = automation.apiExecution.savePresentation;
9
+ var executeToolWithContext = automation.apiExecution.executeToolWithContext;
10
+ var getSlide = automation.apiToolsSlideTools.getSlide;
11
+ var addSlide = automation.apiToolsSlideTools.addSlide;
12
+ var deleteSlides = automation.apiToolsSlideTools.deleteSlides;
13
+ var reorderSlides = automation.apiToolsSlideTools.reorderSlides;
14
+ var duplicateSlide = automation.apiToolsSlideTools.duplicateSlide;
15
+ var updateSlideProperties = automation.apiToolsSlideTools.updateSlideProperties;
16
+ var setSlideTransition = automation.apiToolsSlideTools.setSlideTransition;
17
+ var setCanvasSize = automation.apiToolsSlideTools.setCanvasSize;
18
+ var generateElementId = automation.apiToolsHelpers.generateElementId;
19
+ var generateSlideId = automation.apiToolsHelpers.generateSlideId;
20
+ var describeElement = automation.apiToolsHelpers.describeElement;
21
+ var extractSlideText = automation.apiToolsHelpers.extractSlideText;
22
+ var validateSlideIndex = automation.apiToolsHelpers.validateSlideIndex;
23
+ var addElement = automation.apiToolsElementTools.addElement;
24
+ var updateElement = automation.apiToolsElementTools.updateElement;
25
+ var renameElement = automation.apiToolsElementTools.renameElement;
26
+ var deleteElements = automation.apiToolsElementTools.deleteElements;
27
+ var arrangeElements = automation.apiToolsElementTools.arrangeElements;
28
+ var cloneElement = automation.apiToolsElementTools.cloneElement;
29
+ var setElementAnimation = automation.apiToolsElementTools.setElementAnimation;
30
+ var groupElements = automation.apiToolsElementTools.groupElements;
31
+ var ungroupElements = automation.apiToolsElementTools.ungroupElements;
32
+ var batchUpdateElements = automation.apiToolsElementTools.batchUpdateElements;
33
+ var updateTableCells = automation.apiToolsTableTools.updateTableCells;
34
+ var manageTableStructure = automation.apiToolsTableTools.manageTableStructure;
35
+ var setTableStyleSection = automation.apiToolsTableStyleTools.setTableStyleSection;
36
+ var createTableStyle = automation.apiToolsTableStyleTools.createTableStyle;
37
+ var deleteTableStyle = automation.apiToolsTableStyleTools.deleteTableStyle;
38
+ var assignTableStyle = automation.apiToolsTableStyleTools.assignTableStyle;
39
+ automation.apiToolsStyleTools.ALT_TEXT_ELEMENT_TYPES;
40
+ automation.apiToolsStyleTools.TITLE_ELEMENT_TYPES;
41
+ automation.apiToolsStyleTools.applyElementAltText;
42
+ automation.apiToolsStyleTools.applyElementTitle;
43
+ var updateElementStyle = automation.apiToolsStyleTools.updateElementStyle;
44
+ var runAccessibilityCheck = automation.apiToolsStyleTools.runAccessibilityCheck;
45
+ var findText = automation.apiToolsContentTools.findText;
46
+ var replaceText = automation.apiToolsContentTools.replaceText;
47
+ var manageComments = automation.apiToolsContentTools.manageComments;
48
+ var convertToMarkdown = automation.apiToolsConversionTools.convertToMarkdown;
49
+ var getThemeInfo = automation.apiToolsThemeTools.getThemeInfo;
50
+ var applyThemePreset = automation.apiToolsThemeTools.applyThemePreset;
51
+ var updateThemeColors = automation.apiToolsThemeTools.updateThemeColors;
52
+ var updateThemeFonts = automation.apiToolsThemeTools.updateThemeFonts;
53
+ var updateChart = automation.apiToolsChartTools.updateChart;
54
+ var addChartSeriesT = automation.apiToolsChartTools.addChartSeriesT;
55
+ var removeChartSeriesT = automation.apiToolsChartTools.removeChartSeriesT;
56
+ var updateChartSeriesData = automation.apiToolsChartTools.updateChartSeriesData;
57
+ var createChart = automation.apiToolsChartTools.createChart;
58
+ var formatChartDataPoint = automation.apiToolsChartFormattingTools.formatChartDataPoint;
59
+ var formatChartDataLabel = automation.apiToolsChartFormattingTools.formatChartDataLabel;
60
+ var formatChartSeries = automation.apiToolsChartFormattingTools.formatChartSeries;
61
+ var setChartHelperLineT = automation.apiToolsChartFormattingTools.setChartHelperLineT;
62
+ var setChartColorMapOverrideT = automation.apiToolsChartFormattingTools.setChartColorMapOverrideT;
63
+ var listChartUserShapesT = automation.apiToolsChartUserShapeTools.listChartUserShapesT;
64
+ var addChartUserShapeT = automation.apiToolsChartUserShapeTools.addChartUserShapeT;
65
+ var updateChartUserShapeT = automation.apiToolsChartUserShapeTools.updateChartUserShapeT;
66
+ var removeChartUserShapeT = automation.apiToolsChartUserShapeTools.removeChartUserShapeT;
67
+ var manageSmartArt = automation.apiToolsSmartartTools.manageSmartArt;
68
+ var mergePresentationT = automation.apiToolsMergeTools.mergePresentationT;
69
+ var diffPresentationsT = automation.apiToolsMergeTools.diffPresentationsT;
70
+ var findPlaceholdersT = automation.apiToolsTemplateTools.findPlaceholdersT;
71
+ var applyTemplateT = automation.apiToolsTemplateTools.applyTemplateT;
72
+ var getMetadata = automation.apiToolsMetadataTools.getMetadata;
73
+ var updateMetadata = automation.apiToolsMetadataTools.updateMetadata;
74
+ var manageSections = automation.apiToolsSectionTools.manageSections;
75
+ var exportToSvg = automation.apiToolsExportTools.exportToSvg;
76
+ var exportSlideSvg = automation.apiToolsExportTools.exportSlideSvg;
77
+ var exportToJson = automation.apiToolsJsonTools.exportToJson;
78
+ var importFromJson = automation.apiToolsJsonTools.importFromJson;
79
+ var manageHyperlinks = automation.apiToolsHyperlinkTools.manageHyperlinks;
80
+ var replaceGeometry = automation.apiToolsGeometryTools.replaceGeometry;
81
+ var setElementLockT = automation.apiToolsLockTools.setElementLockT;
82
+ var validatePresentation = automation.apiToolsValidationTools.validatePresentation;
83
+ var repairPresentation = automation.apiToolsValidationTools.repairPresentation;
84
+ var getPresentationProperties = automation.apiToolsPresentationTools.getPresentationProperties;
85
+ var updatePresentationProperties = automation.apiToolsPresentationTools.updatePresentationProperties;
86
+ var getLayouts = automation.apiToolsLayoutTools.getLayouts;
87
+ var applyLayout = automation.apiToolsLayoutTools.applyLayout;
88
+ var getOleContent = automation.apiToolsOleTools.getOleContent;
89
+ var setOleSheetCell = automation.apiToolsOleTools.setOleSheetCell;
90
+ var setOleDocumentParagraph = automation.apiToolsOleTools.setOleDocumentParagraph;
91
+ var setOleDeckSlideTitle = automation.apiToolsOleTools.setOleDeckSlideTitle;
92
+ var setOleObjectNameT = automation.apiToolsOleTools.setOleObjectNameT;
93
+ var replaceOleFileT = automation.apiToolsOleTools.replaceOleFileT;
3208
94
 
3209
95
  Object.defineProperty(exports, "PptxHandler", {
3210
96
  enumerable: true,
3211
- get: function () { return pptxViewerCore.PptxHandler; }
97
+ get: function () { return pptx.PptxHandler; }
3212
98
  });
3213
99
  exports.addChartSeriesT = addChartSeriesT;
3214
100
  exports.addChartUserShapeT = addChartUserShapeT;