pptx-viewer-mcp 1.1.26 → 1.2.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/mcp/index.js CHANGED
@@ -262,6 +262,241 @@ var ManageCommentsSchema = zod.z.object({
262
262
  resolved: zod.z.boolean().optional(),
263
263
  includeResolved: zod.z.boolean().optional()
264
264
  });
265
+ var GetThemeInfoSchema = zod.z.object({
266
+ filePath: zod.z.string().describe("Path to the PPTX file")
267
+ });
268
+ var ApplyThemePresetSchema = zod.z.object({
269
+ filePath: zod.z.string().describe("Path to the PPTX file"),
270
+ presetName: zod.z.string().describe(
271
+ 'Theme preset name (e.g. "OFFICE", "MODERN_BLUE", "EARTH", "MONOCHROME", "VIBRANT", "CORPORATE", "MINIMAL", "DARK")'
272
+ )
273
+ });
274
+ var UpdateThemeColorsSchema = zod.z.object({
275
+ filePath: zod.z.string().describe("Path to the PPTX file"),
276
+ dk1: zod.z.string().optional().describe("Dark 1 color (hex)"),
277
+ lt1: zod.z.string().optional().describe("Light 1 color (hex)"),
278
+ dk2: zod.z.string().optional().describe("Dark 2 color (hex)"),
279
+ lt2: zod.z.string().optional().describe("Light 2 color (hex)"),
280
+ accent1: zod.z.string().optional().describe("Accent 1 color (hex)"),
281
+ accent2: zod.z.string().optional().describe("Accent 2 color (hex)"),
282
+ accent3: zod.z.string().optional().describe("Accent 3 color (hex)"),
283
+ accent4: zod.z.string().optional().describe("Accent 4 color (hex)"),
284
+ accent5: zod.z.string().optional().describe("Accent 5 color (hex)"),
285
+ accent6: zod.z.string().optional().describe("Accent 6 color (hex)"),
286
+ hlink: zod.z.string().optional().describe("Hyperlink color (hex)"),
287
+ folHlink: zod.z.string().optional().describe("Followed hyperlink color (hex)")
288
+ });
289
+ var UpdateThemeFontsSchema = zod.z.object({
290
+ filePath: zod.z.string().describe("Path to the PPTX file"),
291
+ majorFont: zod.z.string().optional().describe("Heading (major) font family"),
292
+ minorFont: zod.z.string().optional().describe("Body (minor) font family")
293
+ });
294
+ var UpdateChartSchema = zod.z.object({
295
+ filePath: zod.z.string().describe("Path to the PPTX file"),
296
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
297
+ elementId: zod.z.string().describe("Chart element ID"),
298
+ chartType: zod.z.string().optional().describe("New chart type (bar, line, pie, scatter, area, doughnut, radar)"),
299
+ title: zod.z.string().optional().describe("Chart title"),
300
+ grouping: zod.z.enum(["clustered", "stacked", "percentStacked"]).optional().describe("Bar/column grouping"),
301
+ legend: zod.z.object({
302
+ show: zod.z.boolean().optional(),
303
+ position: zod.z.string().optional().describe("Legend position: b, t, l, r, tr")
304
+ }).optional().describe("Legend configuration"),
305
+ dataLabels: zod.z.object({
306
+ show: zod.z.boolean().optional(),
307
+ showValue: zod.z.boolean().optional(),
308
+ showCategory: zod.z.boolean().optional(),
309
+ showSeriesName: zod.z.boolean().optional(),
310
+ showPercent: zod.z.boolean().optional()
311
+ }).optional().describe("Data label configuration"),
312
+ axis: zod.z.object({
313
+ type: zod.z.string().describe("Axis type: valAx, catAx, dateAx, serAx"),
314
+ edit: zod.z.object({
315
+ min: zod.z.number().nullable().optional(),
316
+ max: zod.z.number().nullable().optional(),
317
+ majorUnit: zod.z.number().nullable().optional(),
318
+ title: zod.z.string().nullable().optional(),
319
+ numberFormat: zod.z.string().optional(),
320
+ majorGridlines: zod.z.boolean().optional(),
321
+ minorGridlines: zod.z.boolean().optional()
322
+ })
323
+ }).optional().describe("Axis configuration"),
324
+ categories: zod.z.array(zod.z.string()).optional().describe("Category axis labels")
325
+ });
326
+ var AddChartSeriesSchema = zod.z.object({
327
+ filePath: zod.z.string().describe("Path to the PPTX file"),
328
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
329
+ elementId: zod.z.string().describe("Chart element ID"),
330
+ name: zod.z.string().describe("Series name"),
331
+ values: zod.z.array(zod.z.number()).describe("Data values"),
332
+ color: zod.z.string().optional().describe("Series color (hex)")
333
+ });
334
+ var RemoveChartSeriesSchema = zod.z.object({
335
+ filePath: zod.z.string().describe("Path to the PPTX file"),
336
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
337
+ elementId: zod.z.string().describe("Chart element ID"),
338
+ seriesIndex: zod.z.number().int().min(0).describe("Zero-based series index to remove")
339
+ });
340
+ var UpdateChartSeriesDataSchema = zod.z.object({
341
+ filePath: zod.z.string().describe("Path to the PPTX file"),
342
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
343
+ elementId: zod.z.string().describe("Chart element ID"),
344
+ seriesIndex: zod.z.number().int().min(0).describe("Zero-based series index"),
345
+ values: zod.z.array(zod.z.number()).describe("New data values")
346
+ });
347
+ var CreateChartSchema = zod.z.object({
348
+ filePath: zod.z.string().describe("Path to the PPTX file"),
349
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
350
+ chartType: zod.z.string().describe("Chart type (bar, line, pie, scatter, area, doughnut, radar)"),
351
+ x: zod.z.number().optional().describe("X position in points"),
352
+ y: zod.z.number().optional().describe("Y position in points"),
353
+ width: zod.z.number().optional().describe("Width in points"),
354
+ height: zod.z.number().optional().describe("Height in points"),
355
+ title: zod.z.string().optional().describe("Chart title"),
356
+ categories: zod.z.array(zod.z.string()).optional().describe("Category labels"),
357
+ series: zod.z.array(
358
+ zod.z.object({
359
+ name: zod.z.string(),
360
+ values: zod.z.array(zod.z.number()),
361
+ color: zod.z.string().optional()
362
+ })
363
+ ).optional().describe("Data series"),
364
+ legend: zod.z.object({
365
+ show: zod.z.boolean(),
366
+ position: zod.z.string().optional()
367
+ }).optional().describe("Legend configuration")
368
+ });
369
+ var ManageSmartArtSchema = zod.z.object({
370
+ filePath: zod.z.string().describe("Path to the PPTX file"),
371
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
372
+ elementId: zod.z.string().describe("SmartArt element ID"),
373
+ action: zod.z.enum([
374
+ "getNodes",
375
+ "addNode",
376
+ "removeNode",
377
+ "updateNodeText",
378
+ "reorderNode",
379
+ "promoteNode",
380
+ "demoteNode",
381
+ "decompose"
382
+ ]).describe("SmartArt action"),
383
+ nodeId: zod.z.string().optional().describe("Node ID (required for most actions)"),
384
+ text: zod.z.string().optional().describe("Text for addNode or updateNodeText"),
385
+ afterNodeId: zod.z.string().optional().describe("Insert after this node ID"),
386
+ direction: zod.z.number().optional().describe("Reorder direction: 1 (forward) or -1 (backward)")
387
+ });
388
+ var FindPlaceholdersSchema = zod.z.object({
389
+ filePath: zod.z.string().describe("Path to the PPTX file")
390
+ });
391
+ var ApplyTemplateSchema = zod.z.object({
392
+ filePath: zod.z.string().describe("Path to the PPTX file"),
393
+ data: zod.z.record(zod.z.string(), zod.z.unknown()).describe("Template data: keys are placeholder names, values are replacement values")
394
+ });
395
+ var GetMetadataSchema = zod.z.object({
396
+ filePath: zod.z.string().describe("Path to the PPTX file")
397
+ });
398
+ var UpdateMetadataSchema = zod.z.object({
399
+ filePath: zod.z.string().describe("Path to the PPTX file"),
400
+ title: zod.z.string().optional().describe("Presentation title"),
401
+ subject: zod.z.string().optional().describe("Subject"),
402
+ creator: zod.z.string().optional().describe("Creator/author name"),
403
+ keywords: zod.z.string().optional().describe("Keywords (comma-separated)"),
404
+ description: zod.z.string().optional().describe("Description/comments"),
405
+ lastModifiedBy: zod.z.string().optional().describe("Last modified by"),
406
+ category: zod.z.string().optional().describe("Category"),
407
+ company: zod.z.string().optional().describe("Company name"),
408
+ manager: zod.z.string().optional().describe("Manager name"),
409
+ customProperties: zod.z.array(
410
+ zod.z.object({
411
+ name: zod.z.string(),
412
+ value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()])
413
+ })
414
+ ).optional().describe("Custom properties to set")
415
+ });
416
+ var ManageSectionsSchema = zod.z.object({
417
+ filePath: zod.z.string().describe("Path to the PPTX file"),
418
+ action: zod.z.enum(["list", "add", "remove", "reorder", "moveSlides", "getForSlide"]).describe("Section action"),
419
+ name: zod.z.string().optional().describe("Section name (for add)"),
420
+ slideIndices: zod.z.array(zod.z.number().int().min(0)).optional().describe("Slide indices (for add, moveSlides)"),
421
+ sectionId: zod.z.string().optional().describe("Section ID (for remove, moveSlides)"),
422
+ sectionIds: zod.z.array(zod.z.string()).optional().describe("Ordered section IDs (for reorder)"),
423
+ slideIndex: zod.z.number().int().min(0).optional().describe("Slide index (for getForSlide)")
424
+ });
425
+ var ExportToSvgSchema = zod.z.object({
426
+ filePath: zod.z.string().describe("Path to the PPTX file"),
427
+ slideIndices: zod.z.array(zod.z.number().int().min(0)).optional().describe("Specific slides to export (all if omitted)"),
428
+ includeHidden: zod.z.boolean().optional().describe("Include hidden slides"),
429
+ defaultFontFamily: zod.z.string().optional().describe("Default font family for SVG"),
430
+ defaultFontSize: zod.z.number().optional().describe("Default font size")
431
+ });
432
+ var ExportSlideSvgSchema = zod.z.object({
433
+ filePath: zod.z.string().describe("Path to the PPTX file"),
434
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index to export"),
435
+ defaultFontFamily: zod.z.string().optional().describe("Default font family for SVG"),
436
+ defaultFontSize: zod.z.number().optional().describe("Default font size")
437
+ });
438
+ var ManageHyperlinksSchema = zod.z.object({
439
+ filePath: zod.z.string().describe("Path to the PPTX file"),
440
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
441
+ action: zod.z.enum(["list", "set", "remove"]).describe("Hyperlink action"),
442
+ elementId: zod.z.string().optional().describe("Element ID (for set/remove)"),
443
+ trigger: zod.z.enum(["click", "hover"]).optional().describe("Action trigger (default: click)"),
444
+ url: zod.z.string().optional().describe("URL for the hyperlink"),
445
+ tooltip: zod.z.string().optional().describe("Tooltip text"),
446
+ targetSlideIndex: zod.z.number().int().min(0).optional().describe("Target slide index for internal links"),
447
+ actionType: zod.z.enum(["url", "slide", "nextSlide", "prevSlide", "firstSlide", "lastSlide", "endShow"]).optional().describe("Link action type")
448
+ });
449
+ var ReplaceGeometrySchema = zod.z.object({
450
+ filePath: zod.z.string().describe("Path to the PPTX file"),
451
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
452
+ elementId: zod.z.string().describe("Shape element ID"),
453
+ shapeType: zod.z.string().optional().describe('Preset shape type (e.g. "roundRect", "star5", "ellipse", "diamond")'),
454
+ svgPath: zod.z.string().optional().describe("Custom SVG path data (mutually exclusive with shapeType)"),
455
+ pathWidth: zod.z.number().optional().describe("Width of SVG path coordinate space"),
456
+ pathHeight: zod.z.number().optional().describe("Height of SVG path coordinate space"),
457
+ adjustments: zod.z.record(zod.z.string(), zod.z.number()).optional().describe("Shape adjustment values")
458
+ });
459
+ var SetElementLockSchema = zod.z.object({
460
+ filePath: zod.z.string().describe("Path to the PPTX file"),
461
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
462
+ elementId: zod.z.string().describe("Element ID to lock/unlock"),
463
+ locked: zod.z.boolean().describe("Whether the element should be locked"),
464
+ noMove: zod.z.boolean().optional().describe("Prevent moving"),
465
+ noResize: zod.z.boolean().optional().describe("Prevent resizing"),
466
+ noRotation: zod.z.boolean().optional().describe("Prevent rotation"),
467
+ noSelect: zod.z.boolean().optional().describe("Prevent selection"),
468
+ noTextEdit: zod.z.boolean().optional().describe("Prevent text editing")
469
+ });
470
+ var ValidatePresentationSchema = zod.z.object({
471
+ filePath: zod.z.string().describe("Path to the PPTX file")
472
+ });
473
+ var RepairPresentationSchema = zod.z.object({
474
+ filePath: zod.z.string().describe("Path to the PPTX file")
475
+ });
476
+ var GetPresentationPropertiesSchema = zod.z.object({
477
+ filePath: zod.z.string().describe("Path to the PPTX file")
478
+ });
479
+ var UpdatePresentationPropertiesSchema = zod.z.object({
480
+ filePath: zod.z.string().describe("Path to the PPTX file"),
481
+ showType: zod.z.enum(["presented", "browsed", "kiosk"]).optional().describe("Slideshow mode"),
482
+ loopContinuously: zod.z.boolean().optional().describe("Loop the slideshow"),
483
+ showWithNarration: zod.z.boolean().optional().describe("Play narration"),
484
+ showWithAnimation: zod.z.boolean().optional().describe("Play animations"),
485
+ advanceMode: zod.z.enum(["manual", "useTimings"]).optional().describe("Slide advance mode"),
486
+ showSlidesMode: zod.z.enum(["all", "customShow", "range"]).optional().describe("Which slides to show"),
487
+ showSlidesFrom: zod.z.number().int().min(1).optional().describe("Range start (1-based)"),
488
+ showSlidesTo: zod.z.number().int().min(1).optional().describe("Range end (1-based)"),
489
+ penColor: zod.z.string().optional().describe("Annotation pen color (hex)")
490
+ });
491
+ var GetLayoutsSchema = zod.z.object({
492
+ filePath: zod.z.string().describe("Path to the PPTX file")
493
+ });
494
+ var ApplyLayoutSchema = zod.z.object({
495
+ filePath: zod.z.string().describe("Path to the PPTX file"),
496
+ slideIndex: zod.z.number().int().min(0).describe("Zero-based slide index"),
497
+ layoutName: zod.z.string().optional().describe("Layout name to apply"),
498
+ layoutType: zod.z.string().optional().describe("Layout type to apply (alternative to name)")
499
+ });
265
500
  function generateElementId() {
266
501
  return `el-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
267
502
  }
@@ -369,7 +604,144 @@ function validateSlideIndex(slideIndex, slideCount) {
369
604
  return null;
370
605
  }
371
606
 
372
- // src/tools/content-tools.ts
607
+ // src/tools/chart-tools.ts
608
+ function updateChart(ctx, params) {
609
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
610
+ if (err) {
611
+ throw new Error(err);
612
+ }
613
+ const slide = ctx.pptxData.slides[params.slideIndex];
614
+ const el = slide.elements.find((e) => e.id === params.elementId);
615
+ if (!el) {
616
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
617
+ }
618
+ if (el.type !== "chart") {
619
+ throw new Error(`Element '${params.elementId}' is not a chart.`);
620
+ }
621
+ const chart = el;
622
+ if (params.chartType) {
623
+ pptxViewerCore.setChartType(chart, params.chartType);
624
+ }
625
+ if (params.title !== void 0) {
626
+ pptxViewerCore.setChartTitle(chart, params.title);
627
+ }
628
+ if (params.grouping) {
629
+ pptxViewerCore.setChartGrouping(chart, params.grouping);
630
+ }
631
+ if (params.legend) {
632
+ pptxViewerCore.setChartLegend(chart, {
633
+ show: params.legend.show,
634
+ position: params.legend.position
635
+ });
636
+ }
637
+ if (params.dataLabels) {
638
+ pptxViewerCore.setChartDataLabels(chart, params.dataLabels);
639
+ }
640
+ if (params.axis) {
641
+ pptxViewerCore.setChartAxis(chart, params.axis.type, params.axis.edit);
642
+ }
643
+ if (params.categories) {
644
+ pptxViewerCore.setChartCategories(chart, params.categories);
645
+ }
646
+ return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
647
+ }
648
+ function addChartSeriesT(ctx, params) {
649
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
650
+ if (err) {
651
+ throw new Error(err);
652
+ }
653
+ const slide = ctx.pptxData.slides[params.slideIndex];
654
+ const el = slide.elements.find((e) => e.id === params.elementId);
655
+ if (!el) {
656
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
657
+ }
658
+ if (el.type !== "chart") {
659
+ throw new Error(`Element '${params.elementId}' is not a chart.`);
660
+ }
661
+ const chart = el;
662
+ pptxViewerCore.addChartSeries(chart, { name: params.name, values: params.values, color: params.color });
663
+ const seriesCount = chart.chartData?.series?.length ?? 0;
664
+ return {
665
+ pptxData: ctx.pptxData,
666
+ dirty: true,
667
+ result: { elementId: params.elementId, seriesCount }
668
+ };
669
+ }
670
+ function removeChartSeriesT(ctx, params) {
671
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
672
+ if (err) {
673
+ throw new Error(err);
674
+ }
675
+ const slide = ctx.pptxData.slides[params.slideIndex];
676
+ const el = slide.elements.find((e) => e.id === params.elementId);
677
+ if (!el) {
678
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
679
+ }
680
+ if (el.type !== "chart") {
681
+ throw new Error(`Element '${params.elementId}' is not a chart.`);
682
+ }
683
+ const chart = el;
684
+ pptxViewerCore.removeChartSeries(chart, params.seriesIndex);
685
+ const seriesCount = chart.chartData?.series?.length ?? 0;
686
+ return {
687
+ pptxData: ctx.pptxData,
688
+ dirty: true,
689
+ result: { elementId: params.elementId, seriesCount }
690
+ };
691
+ }
692
+ function updateChartSeriesData(ctx, params) {
693
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
694
+ if (err) {
695
+ throw new Error(err);
696
+ }
697
+ const slide = ctx.pptxData.slides[params.slideIndex];
698
+ const el = slide.elements.find((e) => e.id === params.elementId);
699
+ if (!el) {
700
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
701
+ }
702
+ if (el.type !== "chart") {
703
+ throw new Error(`Element '${params.elementId}' is not a chart.`);
704
+ }
705
+ const chart = el;
706
+ pptxViewerCore.updateChartSeriesValues(chart, params.seriesIndex, params.values);
707
+ return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
708
+ }
709
+ function createChart(ctx, params) {
710
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
711
+ if (err) {
712
+ throw new Error(err);
713
+ }
714
+ const builder = pptxViewerCore.ChartBuilder.create(
715
+ params.chartType
716
+ );
717
+ builder.position(params.x ?? 100, params.y ?? 100);
718
+ builder.size(params.width ?? 500, params.height ?? 350);
719
+ if (params.title) {
720
+ builder.title(params.title);
721
+ }
722
+ if (params.categories) {
723
+ builder.categories(params.categories);
724
+ }
725
+ if (params.series) {
726
+ for (const s of params.series) {
727
+ builder.addSeries(s.name, s.values, s.color);
728
+ }
729
+ }
730
+ if (params.legend) {
731
+ builder.legend(
732
+ params.legend.show,
733
+ params.legend.position
734
+ );
735
+ }
736
+ const chart = builder.build();
737
+ const slide = ctx.pptxData.slides[params.slideIndex];
738
+ slide.elements.push(chart);
739
+ return {
740
+ pptxData: ctx.pptxData,
741
+ dirty: true,
742
+ result: { elementId: chart.id, slideIndex: params.slideIndex }
743
+ };
744
+ }
373
745
  var MAX_REGEX_LEN = 200;
374
746
  function hasNestedUnboundedQuantifier(pattern) {
375
747
  return /\([^)]*[+*]\)[+*]/u.test(pattern);
@@ -1257,129 +1629,621 @@ function batchUpdateElements(ctx, params) {
1257
1629
  result: { updatedCount: count }
1258
1630
  };
1259
1631
  }
1260
-
1261
- // src/tools/slide-tools.ts
1262
- function getSlide(ctx, params) {
1263
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1264
- if (err) {
1265
- throw new Error(err);
1266
- }
1267
- const slide = ctx.pptxData.slides[params.slideIndex];
1268
- return {
1269
- pptxData: ctx.pptxData,
1270
- dirty: false,
1271
- result: {
1272
- slideIndex: params.slideIndex,
1273
- slideNumber: slide.slideNumber,
1274
- hidden: slide.hidden ?? false,
1275
- layoutName: slide.layoutName,
1276
- backgroundColor: slide.backgroundColor,
1277
- notes: slide.notes ?? "",
1278
- commentCount: slide.comments?.length ?? 0,
1279
- elements: slide.elements.map(describeElement)
1632
+ function exportToSvg(ctx, params) {
1633
+ if (params.slideIndices) {
1634
+ for (const si of params.slideIndices) {
1635
+ const err = validateSlideIndex(si, ctx.pptxData.slides.length);
1636
+ if (err) {
1637
+ throw new Error(err);
1638
+ }
1280
1639
  }
1281
- };
1282
- }
1283
- function addSlide(ctx, params) {
1284
- const { slides } = ctx.pptxData;
1285
- const insertAt = params.insertAfterIndex !== void 0 && params.insertAfterIndex >= 0 ? Math.min(params.insertAfterIndex + 1, slides.length) : slides.length;
1286
- const newSlide = {
1287
- id: generateSlideId(),
1288
- rId: "",
1289
- slideNumber: insertAt + 1,
1290
- elements: [],
1291
- backgroundColor: params.backgroundColor
1292
- };
1293
- slides.splice(insertAt, 0, newSlide);
1294
- slides.forEach((s, i) => {
1295
- s.slideNumber = i + 1;
1296
- });
1297
- return {
1298
- pptxData: ctx.pptxData,
1299
- dirty: true,
1300
- result: { newSlideIndex: insertAt, slideCount: slides.length }
1301
- };
1302
- }
1303
- function deleteSlides(ctx, params) {
1304
- const { slides } = ctx.pptxData;
1305
- if (!params.slideIndexes || params.slideIndexes.length === 0) {
1306
- throw new Error("slideIndexes is required for the delete action.");
1307
1640
  }
1308
- const indexSet = new Set(params.slideIndexes);
1309
- const invalid = params.slideIndexes.filter((i) => i < 0 || i >= slides.length);
1310
- if (invalid.length > 0) {
1311
- throw new Error(
1312
- `Invalid slide indexes: ${invalid.join(", ")}. Valid range: 0\u2013${slides.length - 1}.`
1313
- );
1641
+ const options = {};
1642
+ if (params.slideIndices) {
1643
+ options.slideIndices = params.slideIndices;
1314
1644
  }
1315
- if (indexSet.size >= slides.length) {
1316
- throw new Error("Cannot delete all slides. At least one slide must remain.");
1645
+ if (params.includeHidden !== void 0) {
1646
+ options.includeHidden = params.includeHidden;
1317
1647
  }
1318
- ctx.pptxData.slides = slides.filter((_, i) => !indexSet.has(i));
1319
- ctx.pptxData.slides.forEach((s, i) => {
1320
- s.slideNumber = i + 1;
1321
- });
1322
- return {
1323
- pptxData: ctx.pptxData,
1324
- dirty: true,
1325
- result: { deletedCount: indexSet.size, slideCount: ctx.pptxData.slides.length }
1326
- };
1327
- }
1328
- function reorderSlides(ctx, params) {
1329
- const { slides } = ctx.pptxData;
1330
- if (!params.newOrder || params.newOrder.length !== slides.length) {
1331
- throw new Error(
1332
- `newOrder must have exactly ${slides.length} indexes. Got ${params.newOrder?.length ?? 0}.`
1333
- );
1648
+ if (params.defaultFontFamily) {
1649
+ options.defaultFontFamily = params.defaultFontFamily;
1334
1650
  }
1335
- const sorted = [...params.newOrder].sort((a, b) => a - b);
1336
- const expected = slides.map((_, i) => i);
1337
- if (JSON.stringify(sorted) !== JSON.stringify(expected)) {
1338
- throw new Error(
1339
- `newOrder must contain each index from 0 to ${slides.length - 1} exactly once.`
1340
- );
1651
+ if (params.defaultFontSize) {
1652
+ options.defaultFontSize = params.defaultFontSize;
1341
1653
  }
1342
- ctx.pptxData.slides = params.newOrder.map((idx) => slides[idx]);
1343
- ctx.pptxData.slides.forEach((s, i) => {
1344
- s.slideNumber = i + 1;
1345
- });
1654
+ const svgStrings = pptxViewerCore.SvgExporter.exportAll(ctx.pptxData, options);
1346
1655
  return {
1347
1656
  pptxData: ctx.pptxData,
1348
- dirty: true,
1349
- result: { slideCount: ctx.pptxData.slides.length }
1657
+ dirty: false,
1658
+ result: { svgStrings, slideCount: svgStrings.length }
1350
1659
  };
1351
1660
  }
1352
- function duplicateSlide(ctx, params) {
1353
- const { slides } = ctx.pptxData;
1354
- const err = validateSlideIndex(params.slideIndex, slides.length);
1661
+ function exportSlideSvg(ctx, params) {
1662
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1355
1663
  if (err) {
1356
1664
  throw new Error(err);
1357
1665
  }
1358
- const original = slides[params.slideIndex];
1359
- const clone = structuredClone(original);
1360
- clone.id = generateSlideId();
1361
- clone.rId = "";
1362
- for (const el of clone.elements) {
1363
- el.id = generateElementId();
1666
+ const slide = ctx.pptxData.slides[params.slideIndex];
1667
+ const width = ctx.pptxData.width ?? 960;
1668
+ const height = ctx.pptxData.height ?? 540;
1669
+ const options = {};
1670
+ if (params.defaultFontFamily) {
1671
+ options.defaultFontFamily = params.defaultFontFamily;
1364
1672
  }
1365
- const insertIndex = params.targetIndex ?? params.slideIndex + 1;
1366
- slides.splice(insertIndex, 0, clone);
1367
- slides.forEach((s, i) => {
1368
- s.slideNumber = i + 1;
1369
- });
1673
+ if (params.defaultFontSize) {
1674
+ options.defaultFontSize = params.defaultFontSize;
1675
+ }
1676
+ const svg = pptxViewerCore.SvgExporter.exportSlide(slide, width, height, options);
1370
1677
  return {
1371
1678
  pptxData: ctx.pptxData,
1372
- dirty: true,
1373
- result: { newSlideIndex: insertIndex, slideCount: slides.length }
1679
+ dirty: false,
1680
+ result: { svg, slideIndex: params.slideIndex }
1374
1681
  };
1375
1682
  }
1376
- function updateSlideProperties(ctx, params) {
1683
+ function replaceGeometry(ctx, params) {
1377
1684
  const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1378
1685
  if (err) {
1379
1686
  throw new Error(err);
1380
1687
  }
1381
1688
  const slide = ctx.pptxData.slides[params.slideIndex];
1382
- if (params.backgroundColor !== void 0) {
1689
+ const el = slide.elements.find((e) => e.id === params.elementId);
1690
+ if (!el) {
1691
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1692
+ }
1693
+ if (el.type !== "shape") {
1694
+ throw new Error(`Element '${params.elementId}' is not a shape.`);
1695
+ }
1696
+ const shape = el;
1697
+ if (params.svgPath) {
1698
+ pptxViewerCore.replaceWithCustomGeometry(shape, params.svgPath, params.pathWidth, params.pathHeight);
1699
+ return {
1700
+ pptxData: ctx.pptxData,
1701
+ dirty: true,
1702
+ result: { elementId: params.elementId, geometryType: "custom" }
1703
+ };
1704
+ }
1705
+ if (params.shapeType) {
1706
+ pptxViewerCore.replaceShapeGeometry(shape, params.shapeType, params.adjustments);
1707
+ return {
1708
+ pptxData: ctx.pptxData,
1709
+ dirty: true,
1710
+ result: { elementId: params.elementId, geometryType: params.shapeType }
1711
+ };
1712
+ }
1713
+ throw new Error("Either shapeType or svgPath must be provided.");
1714
+ }
1715
+
1716
+ // src/tools/hyperlink-tools.ts
1717
+ function manageHyperlinks(ctx, params) {
1718
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1719
+ if (err) {
1720
+ throw new Error(err);
1721
+ }
1722
+ const slide = ctx.pptxData.slides[params.slideIndex];
1723
+ switch (params.action) {
1724
+ case "list": {
1725
+ const hyperlinks = [];
1726
+ for (const el of slide.elements) {
1727
+ if (el.actionClick) {
1728
+ hyperlinks.push({
1729
+ elementId: el.id,
1730
+ trigger: "click",
1731
+ url: el.actionClick.url,
1732
+ tooltip: el.actionClick.tooltip,
1733
+ targetSlideIndex: el.actionClick.targetSlideIndex,
1734
+ actionType: el.actionClick.action
1735
+ });
1736
+ }
1737
+ if (el.actionHover) {
1738
+ hyperlinks.push({
1739
+ elementId: el.id,
1740
+ trigger: "hover",
1741
+ url: el.actionHover.url,
1742
+ tooltip: el.actionHover.tooltip,
1743
+ targetSlideIndex: el.actionHover.targetSlideIndex,
1744
+ actionType: el.actionHover.action
1745
+ });
1746
+ }
1747
+ }
1748
+ return { pptxData: ctx.pptxData, dirty: false, result: { action: "list", hyperlinks } };
1749
+ }
1750
+ case "set": {
1751
+ if (!params.elementId) {
1752
+ throw new Error("elementId is required for set action.");
1753
+ }
1754
+ const el = slide.elements.find((e) => e.id === params.elementId);
1755
+ if (!el) {
1756
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1757
+ }
1758
+ const trigger = params.trigger ?? "click";
1759
+ const action = {};
1760
+ if (params.url) {
1761
+ action.url = params.url;
1762
+ }
1763
+ if (params.tooltip) {
1764
+ action.tooltip = params.tooltip;
1765
+ }
1766
+ if (params.targetSlideIndex !== void 0) {
1767
+ action.targetSlideIndex = params.targetSlideIndex;
1768
+ }
1769
+ if (params.actionType) {
1770
+ switch (params.actionType) {
1771
+ case "url":
1772
+ action.action = "ppaction://hlinkpres";
1773
+ break;
1774
+ case "slide":
1775
+ action.action = "ppaction://hlinksldjump";
1776
+ break;
1777
+ case "nextSlide":
1778
+ action.action = "ppaction://hlinkshowjump?jump=nextslide";
1779
+ break;
1780
+ case "prevSlide":
1781
+ action.action = "ppaction://hlinkshowjump?jump=previousslide";
1782
+ break;
1783
+ case "firstSlide":
1784
+ action.action = "ppaction://hlinkshowjump?jump=firstslide";
1785
+ break;
1786
+ case "lastSlide":
1787
+ action.action = "ppaction://hlinkshowjump?jump=lastslide";
1788
+ break;
1789
+ case "endShow":
1790
+ action.action = "ppaction://hlinkshowjump?jump=endshow";
1791
+ break;
1792
+ }
1793
+ }
1794
+ if (trigger === "click") {
1795
+ el.actionClick = action;
1796
+ } else {
1797
+ el.actionHover = action;
1798
+ }
1799
+ return {
1800
+ pptxData: ctx.pptxData,
1801
+ dirty: true,
1802
+ result: { action: "set", elementId: params.elementId }
1803
+ };
1804
+ }
1805
+ case "remove": {
1806
+ if (!params.elementId) {
1807
+ throw new Error("elementId is required for remove action.");
1808
+ }
1809
+ const el = slide.elements.find((e) => e.id === params.elementId);
1810
+ if (!el) {
1811
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1812
+ }
1813
+ const trigger = params.trigger ?? "click";
1814
+ if (trigger === "click") {
1815
+ el.actionClick = void 0;
1816
+ } else {
1817
+ el.actionHover = void 0;
1818
+ }
1819
+ return {
1820
+ pptxData: ctx.pptxData,
1821
+ dirty: true,
1822
+ result: { action: "remove", elementId: params.elementId }
1823
+ };
1824
+ }
1825
+ default:
1826
+ throw new Error(`Unknown hyperlink action: ${String(params.action)}`);
1827
+ }
1828
+ }
1829
+ function getLayouts(ctx) {
1830
+ const layouts = [];
1831
+ if (ctx.pptxData.slideMasters) {
1832
+ for (const master of ctx.pptxData.slideMasters) {
1833
+ if (master.layouts) {
1834
+ for (const layout of master.layouts) {
1835
+ layouts.push({
1836
+ name: layout.name ?? "Unknown",
1837
+ path: layout.path
1838
+ });
1839
+ }
1840
+ }
1841
+ }
1842
+ }
1843
+ if (ctx.pptxData.layoutOptions) {
1844
+ for (const opt of ctx.pptxData.layoutOptions) {
1845
+ if (!layouts.some((l) => l.path === opt.path)) {
1846
+ layouts.push({ name: opt.name, path: opt.path, type: opt.type });
1847
+ }
1848
+ }
1849
+ }
1850
+ return {
1851
+ pptxData: ctx.pptxData,
1852
+ dirty: false,
1853
+ result: { layouts, count: layouts.length }
1854
+ };
1855
+ }
1856
+ function applyLayout(ctx, params) {
1857
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1858
+ if (err) {
1859
+ throw new Error(err);
1860
+ }
1861
+ let layout;
1862
+ if (params.layoutName) {
1863
+ layout = pptxViewerCore.findLayoutByName(ctx.pptxData, params.layoutName);
1864
+ } else if (params.layoutType) {
1865
+ layout = pptxViewerCore.findLayoutByType(ctx.pptxData, params.layoutType);
1866
+ } else {
1867
+ throw new Error("Either layoutName or layoutType must be provided.");
1868
+ }
1869
+ if (!layout) {
1870
+ throw new Error(
1871
+ `Layout not found: ${params.layoutName ?? params.layoutType}. Use get_layouts to see available layouts.`
1872
+ );
1873
+ }
1874
+ const slide = ctx.pptxData.slides[params.slideIndex];
1875
+ slide.layoutName = layout.name;
1876
+ if (layout.path) {
1877
+ slide.layoutPath = layout.path;
1878
+ }
1879
+ return {
1880
+ pptxData: ctx.pptxData,
1881
+ dirty: true,
1882
+ result: { slideIndex: params.slideIndex, layoutName: layout.name ?? "Unknown" }
1883
+ };
1884
+ }
1885
+ function setElementLockT(ctx, params) {
1886
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1887
+ if (err) {
1888
+ throw new Error(err);
1889
+ }
1890
+ const slide = ctx.pptxData.slides[params.slideIndex];
1891
+ const idx = slide.elements.findIndex((e) => e.id === params.elementId);
1892
+ if (idx < 0) {
1893
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1894
+ }
1895
+ const extraLocks = {};
1896
+ if (params.noMove !== void 0) {
1897
+ extraLocks["noMove"] = params.noMove;
1898
+ }
1899
+ if (params.noResize !== void 0) {
1900
+ extraLocks["noResize"] = params.noResize;
1901
+ }
1902
+ if (params.noRotation !== void 0) {
1903
+ extraLocks["noRotation"] = params.noRotation;
1904
+ }
1905
+ if (params.noSelect !== void 0) {
1906
+ extraLocks["noSelect"] = params.noSelect;
1907
+ }
1908
+ if (params.noTextEdit !== void 0) {
1909
+ extraLocks["noTextEdit"] = params.noTextEdit;
1910
+ }
1911
+ const updated = pptxViewerCore.setElementLocked(slide.elements[idx], params.locked, extraLocks);
1912
+ slide.elements[idx] = updated;
1913
+ return {
1914
+ pptxData: ctx.pptxData,
1915
+ dirty: true,
1916
+ result: { elementId: params.elementId, locked: params.locked }
1917
+ };
1918
+ }
1919
+
1920
+ // src/tools/metadata-tools.ts
1921
+ function getMetadata(ctx) {
1922
+ return {
1923
+ pptxData: ctx.pptxData,
1924
+ dirty: false,
1925
+ result: {
1926
+ coreProperties: ctx.pptxData.coreProperties,
1927
+ appProperties: ctx.pptxData.appProperties,
1928
+ customProperties: ctx.pptxData.customProperties,
1929
+ slideCount: ctx.pptxData.slides.length
1930
+ }
1931
+ };
1932
+ }
1933
+ function updateMetadata(ctx, params) {
1934
+ const updated = [];
1935
+ 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) {
1936
+ if (!ctx.pptxData.coreProperties) {
1937
+ ctx.pptxData.coreProperties = {};
1938
+ }
1939
+ const cp = ctx.pptxData.coreProperties;
1940
+ if (params.title !== void 0) {
1941
+ cp.title = params.title;
1942
+ updated.push("title");
1943
+ }
1944
+ if (params.subject !== void 0) {
1945
+ cp.subject = params.subject;
1946
+ updated.push("subject");
1947
+ }
1948
+ if (params.creator !== void 0) {
1949
+ cp.creator = params.creator;
1950
+ updated.push("creator");
1951
+ }
1952
+ if (params.keywords !== void 0) {
1953
+ cp.keywords = params.keywords;
1954
+ updated.push("keywords");
1955
+ }
1956
+ if (params.description !== void 0) {
1957
+ cp.description = params.description;
1958
+ updated.push("description");
1959
+ }
1960
+ if (params.lastModifiedBy !== void 0) {
1961
+ cp.lastModifiedBy = params.lastModifiedBy;
1962
+ updated.push("lastModifiedBy");
1963
+ }
1964
+ if (params.category !== void 0) {
1965
+ cp.category = params.category;
1966
+ updated.push("category");
1967
+ }
1968
+ }
1969
+ if (params.company !== void 0 || params.manager !== void 0) {
1970
+ if (!ctx.pptxData.appProperties) {
1971
+ ctx.pptxData.appProperties = {};
1972
+ }
1973
+ const ap = ctx.pptxData.appProperties;
1974
+ if (params.company !== void 0) {
1975
+ ap.company = params.company;
1976
+ updated.push("company");
1977
+ }
1978
+ if (params.manager !== void 0) {
1979
+ ap.manager = params.manager;
1980
+ updated.push("manager");
1981
+ }
1982
+ }
1983
+ if (params.customProperties) {
1984
+ if (!ctx.pptxData.customProperties) {
1985
+ ctx.pptxData.customProperties = [];
1986
+ }
1987
+ for (const cp of params.customProperties) {
1988
+ const valueStr = String(cp.value);
1989
+ const vtType = typeof cp.value === "number" ? "i4" : typeof cp.value === "boolean" ? "bool" : "lpwstr";
1990
+ const existing = ctx.pptxData.customProperties.find((p) => p.name === cp.name);
1991
+ if (existing) {
1992
+ existing.value = valueStr;
1993
+ existing.type = vtType;
1994
+ } else {
1995
+ ctx.pptxData.customProperties.push({ name: cp.name, value: valueStr, type: vtType });
1996
+ }
1997
+ updated.push(`custom:${cp.name}`);
1998
+ }
1999
+ }
2000
+ return {
2001
+ pptxData: ctx.pptxData,
2002
+ dirty: updated.length > 0,
2003
+ result: { updated }
2004
+ };
2005
+ }
2006
+
2007
+ // src/tools/presentation-tools.ts
2008
+ function getPresentationProperties(ctx) {
2009
+ return {
2010
+ pptxData: ctx.pptxData,
2011
+ dirty: false,
2012
+ result: {
2013
+ properties: ctx.pptxData.presentationProperties,
2014
+ slideCount: ctx.pptxData.slides.length,
2015
+ width: ctx.pptxData.width,
2016
+ height: ctx.pptxData.height
2017
+ }
2018
+ };
2019
+ }
2020
+ function updatePresentationProperties(ctx, params) {
2021
+ if (!ctx.pptxData.presentationProperties) {
2022
+ ctx.pptxData.presentationProperties = {};
2023
+ }
2024
+ const pp = ctx.pptxData.presentationProperties;
2025
+ if (params.showType !== void 0) {
2026
+ pp.showType = params.showType;
2027
+ }
2028
+ if (params.loopContinuously !== void 0) {
2029
+ pp.loopContinuously = params.loopContinuously;
2030
+ }
2031
+ if (params.showWithNarration !== void 0) {
2032
+ pp.showWithNarration = params.showWithNarration;
2033
+ }
2034
+ if (params.showWithAnimation !== void 0) {
2035
+ pp.showWithAnimation = params.showWithAnimation;
2036
+ }
2037
+ if (params.advanceMode !== void 0) {
2038
+ pp.advanceMode = params.advanceMode;
2039
+ }
2040
+ if (params.showSlidesMode !== void 0) {
2041
+ pp.showSlidesMode = params.showSlidesMode;
2042
+ }
2043
+ if (params.showSlidesFrom !== void 0) {
2044
+ pp.showSlidesFrom = params.showSlidesFrom;
2045
+ }
2046
+ if (params.showSlidesTo !== void 0) {
2047
+ pp.showSlidesTo = params.showSlidesTo;
2048
+ }
2049
+ if (params.penColor !== void 0) {
2050
+ pp.penColor = params.penColor;
2051
+ }
2052
+ return {
2053
+ pptxData: ctx.pptxData,
2054
+ dirty: true,
2055
+ result: { properties: pp }
2056
+ };
2057
+ }
2058
+ function manageSections(ctx, params) {
2059
+ switch (params.action) {
2060
+ case "list": {
2061
+ const sections = (ctx.pptxData.sections ?? []).map((s) => ({
2062
+ id: s.id,
2063
+ name: s.name,
2064
+ slideIds: s.slideIds,
2065
+ collapsed: s.collapsed,
2066
+ color: s.color
2067
+ }));
2068
+ return { pptxData: ctx.pptxData, dirty: false, result: { action: "list", sections } };
2069
+ }
2070
+ case "add": {
2071
+ if (!params.name) {
2072
+ throw new Error("name is required for add action.");
2073
+ }
2074
+ const section = pptxViewerCore.addSection(ctx.pptxData, params.name, params.slideIndices ?? []);
2075
+ return {
2076
+ pptxData: ctx.pptxData,
2077
+ dirty: true,
2078
+ result: {
2079
+ action: "add",
2080
+ section: { id: section.id, name: section.name, slideIds: section.slideIds }
2081
+ }
2082
+ };
2083
+ }
2084
+ case "remove": {
2085
+ if (!params.sectionId) {
2086
+ throw new Error("sectionId is required for remove action.");
2087
+ }
2088
+ const success = pptxViewerCore.removeSection(ctx.pptxData, params.sectionId);
2089
+ return { pptxData: ctx.pptxData, dirty: success, result: { action: "remove", success } };
2090
+ }
2091
+ case "reorder": {
2092
+ if (!params.sectionIds) {
2093
+ throw new Error("sectionIds is required for reorder action.");
2094
+ }
2095
+ pptxViewerCore.reorderSections(ctx.pptxData, params.sectionIds);
2096
+ return { pptxData: ctx.pptxData, dirty: true, result: { action: "reorder", success: true } };
2097
+ }
2098
+ case "moveSlides": {
2099
+ if (!params.sectionId) {
2100
+ throw new Error("sectionId is required for moveSlides action.");
2101
+ }
2102
+ if (!params.slideIndices) {
2103
+ throw new Error("slideIndices is required for moveSlides action.");
2104
+ }
2105
+ const success = pptxViewerCore.moveSlidesToSection(ctx.pptxData, params.slideIndices, params.sectionId);
2106
+ return { pptxData: ctx.pptxData, dirty: success, result: { action: "moveSlides", success } };
2107
+ }
2108
+ case "getForSlide": {
2109
+ if (params.slideIndex === void 0) {
2110
+ throw new Error("slideIndex is required for getForSlide action.");
2111
+ }
2112
+ const section = pptxViewerCore.getSectionForSlide(ctx.pptxData, params.slideIndex);
2113
+ const info = section ? { id: section.id, name: section.name, slideIds: section.slideIds } : null;
2114
+ return {
2115
+ pptxData: ctx.pptxData,
2116
+ dirty: false,
2117
+ result: { action: "getForSlide", section: info }
2118
+ };
2119
+ }
2120
+ default:
2121
+ throw new Error(`Unknown section action: ${String(params.action)}`);
2122
+ }
2123
+ }
2124
+
2125
+ // src/tools/slide-tools.ts
2126
+ function getSlide(ctx, params) {
2127
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2128
+ if (err) {
2129
+ throw new Error(err);
2130
+ }
2131
+ const slide = ctx.pptxData.slides[params.slideIndex];
2132
+ return {
2133
+ pptxData: ctx.pptxData,
2134
+ dirty: false,
2135
+ result: {
2136
+ slideIndex: params.slideIndex,
2137
+ slideNumber: slide.slideNumber,
2138
+ hidden: slide.hidden ?? false,
2139
+ layoutName: slide.layoutName,
2140
+ backgroundColor: slide.backgroundColor,
2141
+ notes: slide.notes ?? "",
2142
+ commentCount: slide.comments?.length ?? 0,
2143
+ elements: slide.elements.map(describeElement)
2144
+ }
2145
+ };
2146
+ }
2147
+ function addSlide(ctx, params) {
2148
+ const { slides } = ctx.pptxData;
2149
+ const insertAt = params.insertAfterIndex !== void 0 && params.insertAfterIndex >= 0 ? Math.min(params.insertAfterIndex + 1, slides.length) : slides.length;
2150
+ const newSlide = {
2151
+ id: generateSlideId(),
2152
+ rId: "",
2153
+ slideNumber: insertAt + 1,
2154
+ elements: [],
2155
+ backgroundColor: params.backgroundColor
2156
+ };
2157
+ slides.splice(insertAt, 0, newSlide);
2158
+ slides.forEach((s, i) => {
2159
+ s.slideNumber = i + 1;
2160
+ });
2161
+ return {
2162
+ pptxData: ctx.pptxData,
2163
+ dirty: true,
2164
+ result: { newSlideIndex: insertAt, slideCount: slides.length }
2165
+ };
2166
+ }
2167
+ function deleteSlides(ctx, params) {
2168
+ const { slides } = ctx.pptxData;
2169
+ if (!params.slideIndexes || params.slideIndexes.length === 0) {
2170
+ throw new Error("slideIndexes is required for the delete action.");
2171
+ }
2172
+ const indexSet = new Set(params.slideIndexes);
2173
+ const invalid = params.slideIndexes.filter((i) => i < 0 || i >= slides.length);
2174
+ if (invalid.length > 0) {
2175
+ throw new Error(
2176
+ `Invalid slide indexes: ${invalid.join(", ")}. Valid range: 0\u2013${slides.length - 1}.`
2177
+ );
2178
+ }
2179
+ if (indexSet.size >= slides.length) {
2180
+ throw new Error("Cannot delete all slides. At least one slide must remain.");
2181
+ }
2182
+ ctx.pptxData.slides = slides.filter((_, i) => !indexSet.has(i));
2183
+ ctx.pptxData.slides.forEach((s, i) => {
2184
+ s.slideNumber = i + 1;
2185
+ });
2186
+ return {
2187
+ pptxData: ctx.pptxData,
2188
+ dirty: true,
2189
+ result: { deletedCount: indexSet.size, slideCount: ctx.pptxData.slides.length }
2190
+ };
2191
+ }
2192
+ function reorderSlides(ctx, params) {
2193
+ const { slides } = ctx.pptxData;
2194
+ if (!params.newOrder || params.newOrder.length !== slides.length) {
2195
+ throw new Error(
2196
+ `newOrder must have exactly ${slides.length} indexes. Got ${params.newOrder?.length ?? 0}.`
2197
+ );
2198
+ }
2199
+ const sorted = [...params.newOrder].sort((a, b) => a - b);
2200
+ const expected = slides.map((_, i) => i);
2201
+ if (JSON.stringify(sorted) !== JSON.stringify(expected)) {
2202
+ throw new Error(
2203
+ `newOrder must contain each index from 0 to ${slides.length - 1} exactly once.`
2204
+ );
2205
+ }
2206
+ ctx.pptxData.slides = params.newOrder.map((idx) => slides[idx]);
2207
+ ctx.pptxData.slides.forEach((s, i) => {
2208
+ s.slideNumber = i + 1;
2209
+ });
2210
+ return {
2211
+ pptxData: ctx.pptxData,
2212
+ dirty: true,
2213
+ result: { slideCount: ctx.pptxData.slides.length }
2214
+ };
2215
+ }
2216
+ function duplicateSlide(ctx, params) {
2217
+ const { slides } = ctx.pptxData;
2218
+ const err = validateSlideIndex(params.slideIndex, slides.length);
2219
+ if (err) {
2220
+ throw new Error(err);
2221
+ }
2222
+ const original = slides[params.slideIndex];
2223
+ const clone = structuredClone(original);
2224
+ clone.id = generateSlideId();
2225
+ clone.rId = "";
2226
+ for (const el of clone.elements) {
2227
+ el.id = generateElementId();
2228
+ }
2229
+ const insertIndex = params.targetIndex ?? params.slideIndex + 1;
2230
+ slides.splice(insertIndex, 0, clone);
2231
+ slides.forEach((s, i) => {
2232
+ s.slideNumber = i + 1;
2233
+ });
2234
+ return {
2235
+ pptxData: ctx.pptxData,
2236
+ dirty: true,
2237
+ result: { newSlideIndex: insertIndex, slideCount: slides.length }
2238
+ };
2239
+ }
2240
+ function updateSlideProperties(ctx, params) {
2241
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2242
+ if (err) {
2243
+ throw new Error(err);
2244
+ }
2245
+ const slide = ctx.pptxData.slides[params.slideIndex];
2246
+ if (params.backgroundColor !== void 0) {
1383
2247
  slide.backgroundColor = params.backgroundColor;
1384
2248
  }
1385
2249
  if (params.backgroundGradient !== void 0) {
@@ -1447,6 +2311,164 @@ function setCanvasSize(ctx, params) {
1447
2311
  result: { canvasWidth: params.width, canvasHeight: params.height }
1448
2312
  };
1449
2313
  }
2314
+ function extractNodes(data) {
2315
+ if (!data.nodes) {
2316
+ return [];
2317
+ }
2318
+ return data.nodes.map((n) => ({
2319
+ id: n.id,
2320
+ text: n.text ?? "",
2321
+ children: n.children?.map((c) => ({ id: c.id, text: c.text ?? "" }))
2322
+ }));
2323
+ }
2324
+ function manageSmartArt(ctx, params) {
2325
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2326
+ if (err) {
2327
+ throw new Error(err);
2328
+ }
2329
+ const slide = ctx.pptxData.slides[params.slideIndex];
2330
+ const el = slide.elements.find((e) => e.id === params.elementId);
2331
+ if (!el) {
2332
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2333
+ }
2334
+ if (el.type !== "smartArt") {
2335
+ throw new Error(`Element '${params.elementId}' is not a SmartArt.`);
2336
+ }
2337
+ const smartArt = el;
2338
+ if (!smartArt.smartArtData) {
2339
+ throw new Error(`SmartArt '${params.elementId}' has no data.`);
2340
+ }
2341
+ switch (params.action) {
2342
+ case "getNodes": {
2343
+ return {
2344
+ pptxData: ctx.pptxData,
2345
+ dirty: false,
2346
+ result: {
2347
+ action: "getNodes",
2348
+ elementId: params.elementId,
2349
+ nodes: extractNodes(smartArt.smartArtData)
2350
+ }
2351
+ };
2352
+ }
2353
+ case "addNode": {
2354
+ smartArt.smartArtData = pptxViewerCore.addSmartArtNode(
2355
+ smartArt.smartArtData,
2356
+ params.text ?? "",
2357
+ params.afterNodeId
2358
+ );
2359
+ return {
2360
+ pptxData: ctx.pptxData,
2361
+ dirty: true,
2362
+ result: {
2363
+ action: "addNode",
2364
+ elementId: params.elementId,
2365
+ nodes: extractNodes(smartArt.smartArtData)
2366
+ }
2367
+ };
2368
+ }
2369
+ case "removeNode": {
2370
+ if (!params.nodeId) {
2371
+ throw new Error("nodeId is required for removeNode.");
2372
+ }
2373
+ smartArt.smartArtData = pptxViewerCore.removeSmartArtNode(smartArt.smartArtData, params.nodeId);
2374
+ return {
2375
+ pptxData: ctx.pptxData,
2376
+ dirty: true,
2377
+ result: {
2378
+ action: "removeNode",
2379
+ elementId: params.elementId,
2380
+ nodes: extractNodes(smartArt.smartArtData)
2381
+ }
2382
+ };
2383
+ }
2384
+ case "updateNodeText": {
2385
+ if (!params.nodeId) {
2386
+ throw new Error("nodeId is required for updateNodeText.");
2387
+ }
2388
+ smartArt.smartArtData = pptxViewerCore.updateSmartArtNodeText(
2389
+ smartArt.smartArtData,
2390
+ params.nodeId,
2391
+ params.text ?? ""
2392
+ );
2393
+ return {
2394
+ pptxData: ctx.pptxData,
2395
+ dirty: true,
2396
+ result: { action: "updateNodeText", elementId: params.elementId }
2397
+ };
2398
+ }
2399
+ case "reorderNode": {
2400
+ if (!params.nodeId) {
2401
+ throw new Error("nodeId is required for reorderNode.");
2402
+ }
2403
+ const dir = params.direction ?? 1;
2404
+ smartArt.smartArtData = pptxViewerCore.reorderSmartArtNode(smartArt.smartArtData, params.nodeId, dir);
2405
+ return {
2406
+ pptxData: ctx.pptxData,
2407
+ dirty: true,
2408
+ result: {
2409
+ action: "reorderNode",
2410
+ elementId: params.elementId,
2411
+ nodes: extractNodes(smartArt.smartArtData)
2412
+ }
2413
+ };
2414
+ }
2415
+ case "promoteNode": {
2416
+ if (!params.nodeId) {
2417
+ throw new Error("nodeId is required for promoteNode.");
2418
+ }
2419
+ smartArt.smartArtData = pptxViewerCore.promoteSmartArtNode(smartArt.smartArtData, params.nodeId);
2420
+ return {
2421
+ pptxData: ctx.pptxData,
2422
+ dirty: true,
2423
+ result: {
2424
+ action: "promoteNode",
2425
+ elementId: params.elementId,
2426
+ nodes: extractNodes(smartArt.smartArtData)
2427
+ }
2428
+ };
2429
+ }
2430
+ case "demoteNode": {
2431
+ if (!params.nodeId) {
2432
+ throw new Error("nodeId is required for demoteNode.");
2433
+ }
2434
+ smartArt.smartArtData = pptxViewerCore.demoteSmartArtNode(smartArt.smartArtData, params.nodeId);
2435
+ return {
2436
+ pptxData: ctx.pptxData,
2437
+ dirty: true,
2438
+ result: {
2439
+ action: "demoteNode",
2440
+ elementId: params.elementId,
2441
+ nodes: extractNodes(smartArt.smartArtData)
2442
+ }
2443
+ };
2444
+ }
2445
+ case "decompose": {
2446
+ const bounds = {
2447
+ x: smartArt.x,
2448
+ y: smartArt.y,
2449
+ width: smartArt.width,
2450
+ height: smartArt.height
2451
+ };
2452
+ const decomposed = pptxViewerCore.decomposeSmartArt(smartArt.smartArtData, bounds);
2453
+ if (!decomposed || decomposed.length === 0) {
2454
+ throw new Error("Failed to decompose SmartArt into shapes.");
2455
+ }
2456
+ const idx = slide.elements.indexOf(el);
2457
+ slide.elements.splice(idx, 1, ...decomposed);
2458
+ return {
2459
+ pptxData: ctx.pptxData,
2460
+ dirty: true,
2461
+ result: {
2462
+ action: "decompose",
2463
+ elementId: params.elementId,
2464
+ decomposedCount: decomposed.length
2465
+ }
2466
+ };
2467
+ }
2468
+ default:
2469
+ throw new Error(`Unknown SmartArt action: ${String(params.action)}`);
2470
+ }
2471
+ }
1450
2472
  function updateElementStyle(ctx, params) {
1451
2473
  const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1452
2474
  if (err) {
@@ -1751,17 +2773,144 @@ function manageTableStructure(ctx, params) {
1751
2773
  }
1752
2774
  break;
1753
2775
  }
1754
- default: {
1755
- throw new Error(`Unknown action: ${String(params.action)}`);
2776
+ default: {
2777
+ throw new Error(`Unknown action: ${String(params.action)}`);
2778
+ }
2779
+ }
2780
+ return {
2781
+ pptxData: ctx.pptxData,
2782
+ dirty: true,
2783
+ result: {
2784
+ rowCount: tbl.tableData.rows.length,
2785
+ columnCount: tbl.tableData.columnWidths.length
2786
+ }
2787
+ };
2788
+ }
2789
+ function findPlaceholdersT(ctx) {
2790
+ const placeholders = pptxViewerCore.findPlaceholders(ctx.pptxData);
2791
+ return {
2792
+ pptxData: ctx.pptxData,
2793
+ dirty: false,
2794
+ result: { placeholders, count: placeholders.length }
2795
+ };
2796
+ }
2797
+ function applyTemplateT(ctx, params) {
2798
+ pptxViewerCore.applyTemplate(ctx.pptxData, params.data);
2799
+ return {
2800
+ pptxData: ctx.pptxData,
2801
+ dirty: true,
2802
+ result: { applied: true, slideCount: ctx.pptxData.slides.length }
2803
+ };
2804
+ }
2805
+ function getThemeInfo(ctx) {
2806
+ const theme = ctx.pptxData.theme;
2807
+ return {
2808
+ pptxData: ctx.pptxData,
2809
+ dirty: false,
2810
+ result: {
2811
+ themeName: theme?.name,
2812
+ colorScheme: theme?.colorScheme,
2813
+ fontScheme: theme?.fontScheme,
2814
+ availablePresets: Object.keys(pptxViewerCore.ThemePresets)
2815
+ }
2816
+ };
2817
+ }
2818
+ function applyThemePreset(ctx, params) {
2819
+ const preset = pptxViewerCore.getThemePreset(params.presetName);
2820
+ if (!preset) {
2821
+ throw new Error(
2822
+ `Unknown theme preset: "${params.presetName}". Available: ${Object.keys(pptxViewerCore.ThemePresets).join(", ")}`
2823
+ );
2824
+ }
2825
+ if (!ctx.pptxData.theme) {
2826
+ ctx.pptxData.theme = {};
2827
+ }
2828
+ const theme = ctx.pptxData.theme;
2829
+ theme.name = preset.name;
2830
+ theme.colorScheme = preset.colors;
2831
+ if (preset.fonts) {
2832
+ theme.fontScheme = preset.fonts;
2833
+ }
2834
+ return {
2835
+ pptxData: ctx.pptxData,
2836
+ dirty: true,
2837
+ result: {
2838
+ appliedPreset: params.presetName,
2839
+ colorScheme: theme.colorScheme
2840
+ }
2841
+ };
2842
+ }
2843
+ function updateThemeColors(ctx, params) {
2844
+ if (!ctx.pptxData.theme) {
2845
+ ctx.pptxData.theme = {};
2846
+ }
2847
+ const theme = ctx.pptxData.theme;
2848
+ if (!theme.colorScheme) {
2849
+ theme.colorScheme = {
2850
+ dk1: "#000000",
2851
+ lt1: "#FFFFFF",
2852
+ dk2: "#44546A",
2853
+ lt2: "#E7E6E6",
2854
+ accent1: "#4472C4",
2855
+ accent2: "#ED7D31",
2856
+ accent3: "#A5A5A5",
2857
+ accent4: "#FFC000",
2858
+ accent5: "#5B9BD5",
2859
+ accent6: "#70AD47",
2860
+ hlink: "#0563C1",
2861
+ folHlink: "#954F72"
2862
+ };
2863
+ }
2864
+ const cs = theme.colorScheme;
2865
+ for (const key of Object.keys(params)) {
2866
+ if (params[key] !== void 0) {
2867
+ cs[key] = params[key];
1756
2868
  }
1757
2869
  }
1758
2870
  return {
1759
2871
  pptxData: ctx.pptxData,
1760
2872
  dirty: true,
2873
+ result: { colorScheme: cs }
2874
+ };
2875
+ }
2876
+ function updateThemeFonts(ctx, params) {
2877
+ if (!ctx.pptxData.theme) {
2878
+ ctx.pptxData.theme = {};
2879
+ }
2880
+ const theme = ctx.pptxData.theme;
2881
+ if (!theme.fontScheme) {
2882
+ theme.fontScheme = {};
2883
+ }
2884
+ if (params.majorFont !== void 0) {
2885
+ theme.fontScheme.majorFont = { latin: params.majorFont };
2886
+ }
2887
+ if (params.minorFont !== void 0) {
2888
+ theme.fontScheme.minorFont = { latin: params.minorFont };
2889
+ }
2890
+ return {
2891
+ pptxData: ctx.pptxData,
2892
+ dirty: true,
2893
+ result: { fontScheme: theme.fontScheme }
2894
+ };
2895
+ }
2896
+ async function validatePresentation(rawBytes) {
2897
+ const result = await pptxViewerCore.validatePptx(rawBytes);
2898
+ return {
2899
+ valid: result.valid,
2900
+ issueCount: result.issues.length,
2901
+ errors: result.issues.filter((i) => i.severity === "error").length,
2902
+ warnings: result.issues.filter((i) => i.severity === "warning").length,
2903
+ issues: result.issues
2904
+ };
2905
+ }
2906
+ async function repairPresentation(rawBytes) {
2907
+ const result = await pptxViewerCore.repairPptx(rawBytes);
2908
+ return {
1761
2909
  result: {
1762
- rowCount: tbl.tableData.rows.length,
1763
- columnCount: tbl.tableData.columnWidths.length
1764
- }
2910
+ repairCount: result.repairs.length,
2911
+ repairs: result.repairs
2912
+ },
2913
+ repairedBytes: result.repaired
1765
2914
  };
1766
2915
  }
1767
2916
  var PPTX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
@@ -2400,6 +3549,372 @@ function createServer() {
2400
3549
  };
2401
3550
  }
2402
3551
  );
3552
+ server2.registerTool(
3553
+ "get_theme_info",
3554
+ {
3555
+ description: "Get current theme information (colors, fonts, available presets)",
3556
+ inputSchema: GetThemeInfoSchema.shape
3557
+ },
3558
+ async (params) => {
3559
+ const result = await runMcpTool(params.filePath, (ctx) => getThemeInfo(ctx));
3560
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3561
+ }
3562
+ );
3563
+ server2.registerTool(
3564
+ "apply_theme_preset",
3565
+ {
3566
+ description: "Apply a built-in theme preset to the presentation",
3567
+ inputSchema: ApplyThemePresetSchema.shape
3568
+ },
3569
+ async (params) => {
3570
+ const result = await runMcpTool(
3571
+ params.filePath,
3572
+ (ctx) => applyThemePreset(ctx, params)
3573
+ );
3574
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3575
+ }
3576
+ );
3577
+ server2.registerTool(
3578
+ "update_theme_colors",
3579
+ {
3580
+ description: "Update individual theme colors (accent1-6, dk1/2, lt1/2, hlink, folHlink)",
3581
+ inputSchema: UpdateThemeColorsSchema.shape
3582
+ },
3583
+ async (params) => {
3584
+ const result = await runMcpTool(
3585
+ params.filePath,
3586
+ (ctx) => updateThemeColors(ctx, params)
3587
+ );
3588
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3589
+ }
3590
+ );
3591
+ server2.registerTool(
3592
+ "update_theme_fonts",
3593
+ {
3594
+ description: "Update theme heading (major) and body (minor) fonts",
3595
+ inputSchema: UpdateThemeFontsSchema.shape
3596
+ },
3597
+ async (params) => {
3598
+ const result = await runMcpTool(
3599
+ params.filePath,
3600
+ (ctx) => updateThemeFonts(ctx, params)
3601
+ );
3602
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3603
+ }
3604
+ );
3605
+ server2.registerTool(
3606
+ "update_chart",
3607
+ {
3608
+ description: "Update chart type, title, legend, data labels, axis, or categories",
3609
+ inputSchema: UpdateChartSchema.shape
3610
+ },
3611
+ async (params) => {
3612
+ const result = await runMcpTool(
3613
+ params.filePath,
3614
+ (ctx) => updateChart(ctx, params)
3615
+ );
3616
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3617
+ }
3618
+ );
3619
+ server2.registerTool(
3620
+ "add_chart_series",
3621
+ {
3622
+ description: "Add a data series to an existing chart",
3623
+ inputSchema: AddChartSeriesSchema.shape
3624
+ },
3625
+ async (params) => {
3626
+ const result = await runMcpTool(
3627
+ params.filePath,
3628
+ (ctx) => addChartSeriesT(ctx, params)
3629
+ );
3630
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3631
+ }
3632
+ );
3633
+ server2.registerTool(
3634
+ "remove_chart_series",
3635
+ {
3636
+ description: "Remove a data series from a chart by index",
3637
+ inputSchema: RemoveChartSeriesSchema.shape
3638
+ },
3639
+ async (params) => {
3640
+ const result = await runMcpTool(
3641
+ params.filePath,
3642
+ (ctx) => removeChartSeriesT(ctx, params)
3643
+ );
3644
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3645
+ }
3646
+ );
3647
+ server2.registerTool(
3648
+ "update_chart_series_data",
3649
+ {
3650
+ description: "Update data values for a chart series",
3651
+ inputSchema: UpdateChartSeriesDataSchema.shape
3652
+ },
3653
+ async (params) => {
3654
+ const result = await runMcpTool(
3655
+ params.filePath,
3656
+ (ctx) => updateChartSeriesData(ctx, params)
3657
+ );
3658
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3659
+ }
3660
+ );
3661
+ server2.registerTool(
3662
+ "create_chart",
3663
+ {
3664
+ description: "Create a new chart element on a slide",
3665
+ inputSchema: CreateChartSchema.shape
3666
+ },
3667
+ async (params) => {
3668
+ const result = await runMcpTool(
3669
+ params.filePath,
3670
+ (ctx) => createChart(ctx, params)
3671
+ );
3672
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3673
+ }
3674
+ );
3675
+ server2.registerTool(
3676
+ "manage_smart_art",
3677
+ {
3678
+ description: "Manage SmartArt: get nodes, add/remove/reorder/promote/demote nodes, or decompose to shapes",
3679
+ inputSchema: ManageSmartArtSchema.shape
3680
+ },
3681
+ async (params) => {
3682
+ const result = await runMcpTool(
3683
+ params.filePath,
3684
+ (ctx) => manageSmartArt(ctx, params)
3685
+ );
3686
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3687
+ }
3688
+ );
3689
+ server2.registerTool(
3690
+ "find_placeholders",
3691
+ {
3692
+ description: "Discover all {{placeholder}} tokens in the presentation",
3693
+ inputSchema: FindPlaceholdersSchema.shape
3694
+ },
3695
+ async (params) => {
3696
+ const result = await runMcpTool(
3697
+ params.filePath,
3698
+ (ctx) => findPlaceholdersT(ctx)
3699
+ );
3700
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3701
+ }
3702
+ );
3703
+ server2.registerTool(
3704
+ "apply_template",
3705
+ {
3706
+ description: "Replace {{placeholder}} tokens with provided values (mail merge)",
3707
+ inputSchema: ApplyTemplateSchema.shape
3708
+ },
3709
+ async (params) => {
3710
+ const result = await runMcpTool(
3711
+ params.filePath,
3712
+ (ctx) => applyTemplateT(ctx, { data: params.data })
3713
+ );
3714
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3715
+ }
3716
+ );
3717
+ server2.registerTool(
3718
+ "get_metadata",
3719
+ {
3720
+ description: "Get presentation metadata (title, author, keywords, custom properties)",
3721
+ inputSchema: GetMetadataSchema.shape
3722
+ },
3723
+ async (params) => {
3724
+ const result = await runMcpTool(
3725
+ params.filePath,
3726
+ (ctx) => getMetadata(ctx)
3727
+ );
3728
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3729
+ }
3730
+ );
3731
+ server2.registerTool(
3732
+ "update_metadata",
3733
+ {
3734
+ description: "Update presentation metadata (title, author, company, custom properties)",
3735
+ inputSchema: UpdateMetadataSchema.shape
3736
+ },
3737
+ async (params) => {
3738
+ const result = await runMcpTool(
3739
+ params.filePath,
3740
+ (ctx) => updateMetadata(ctx, params)
3741
+ );
3742
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3743
+ }
3744
+ );
3745
+ server2.registerTool(
3746
+ "manage_sections",
3747
+ {
3748
+ description: "List, add, remove, reorder sections or move slides between sections",
3749
+ inputSchema: ManageSectionsSchema.shape
3750
+ },
3751
+ async (params) => {
3752
+ const result = await runMcpTool(
3753
+ params.filePath,
3754
+ (ctx) => manageSections(ctx, params)
3755
+ );
3756
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3757
+ }
3758
+ );
3759
+ server2.registerTool(
3760
+ "export_to_svg",
3761
+ {
3762
+ description: "Export all or specific slides to SVG strings",
3763
+ inputSchema: ExportToSvgSchema.shape
3764
+ },
3765
+ async (params) => {
3766
+ const result = await runMcpTool(
3767
+ params.filePath,
3768
+ (ctx) => exportToSvg(ctx, params)
3769
+ );
3770
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3771
+ }
3772
+ );
3773
+ server2.registerTool(
3774
+ "export_slide_svg",
3775
+ {
3776
+ description: "Export a single slide to SVG",
3777
+ inputSchema: ExportSlideSvgSchema.shape
3778
+ },
3779
+ async (params) => {
3780
+ const result = await runMcpTool(
3781
+ params.filePath,
3782
+ (ctx) => exportSlideSvg(ctx, params)
3783
+ );
3784
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3785
+ }
3786
+ );
3787
+ server2.registerTool(
3788
+ "manage_hyperlinks",
3789
+ {
3790
+ description: "List, set, or remove hyperlinks/actions on elements",
3791
+ inputSchema: ManageHyperlinksSchema.shape
3792
+ },
3793
+ async (params) => {
3794
+ const result = await runMcpTool(
3795
+ params.filePath,
3796
+ (ctx) => manageHyperlinks(ctx, params)
3797
+ );
3798
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3799
+ }
3800
+ );
3801
+ server2.registerTool(
3802
+ "replace_geometry",
3803
+ {
3804
+ description: "Replace a shape element geometry with a preset or custom SVG path",
3805
+ inputSchema: ReplaceGeometrySchema.shape
3806
+ },
3807
+ async (params) => {
3808
+ const result = await runMcpTool(
3809
+ params.filePath,
3810
+ (ctx) => replaceGeometry(ctx, {
3811
+ ...params,
3812
+ adjustments: params.adjustments
3813
+ })
3814
+ );
3815
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3816
+ }
3817
+ );
3818
+ server2.registerTool(
3819
+ "set_element_lock",
3820
+ {
3821
+ description: "Lock or unlock an element (prevent move, resize, rotation, selection, text edit)",
3822
+ inputSchema: SetElementLockSchema.shape
3823
+ },
3824
+ async (params) => {
3825
+ const result = await runMcpTool(
3826
+ params.filePath,
3827
+ (ctx) => setElementLockT(ctx, params)
3828
+ );
3829
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3830
+ }
3831
+ );
3832
+ server2.registerTool(
3833
+ "validate_presentation",
3834
+ {
3835
+ description: "Validate PPTX structural integrity (content types, relationships, XML)",
3836
+ inputSchema: ValidatePresentationSchema.shape
3837
+ },
3838
+ async (params) => {
3839
+ const { readFile: readFile2 } = await import('fs/promises');
3840
+ const safePath = resolveScopedFilePath(params.filePath);
3841
+ const bytes = await readFile2(safePath);
3842
+ const result = await validatePresentation(bytes.buffer);
3843
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3844
+ }
3845
+ );
3846
+ server2.registerTool(
3847
+ "repair_presentation",
3848
+ {
3849
+ description: "Auto-repair common PPTX structural issues",
3850
+ inputSchema: RepairPresentationSchema.shape
3851
+ },
3852
+ async (params) => {
3853
+ const { readFile: readFile2, writeFile: writeFile2 } = await import('fs/promises');
3854
+ const safePath = resolveScopedFilePath(params.filePath);
3855
+ const bytes = await readFile2(safePath);
3856
+ const { result, repairedBytes } = await repairPresentation(
3857
+ bytes.buffer
3858
+ );
3859
+ if (result.repairCount > 0) {
3860
+ await writeFile2(safePath, new Uint8Array(repairedBytes));
3861
+ }
3862
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3863
+ }
3864
+ );
3865
+ server2.registerTool(
3866
+ "get_presentation_properties",
3867
+ {
3868
+ description: "Get slideshow properties (show type, loop, advance mode, slide range)",
3869
+ inputSchema: GetPresentationPropertiesSchema.shape
3870
+ },
3871
+ async (params) => {
3872
+ const result = await runMcpTool(
3873
+ params.filePath,
3874
+ (ctx) => getPresentationProperties(ctx)
3875
+ );
3876
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3877
+ }
3878
+ );
3879
+ server2.registerTool(
3880
+ "update_presentation_properties",
3881
+ {
3882
+ description: "Update slideshow properties (show type, loop, advance mode, pen color)",
3883
+ inputSchema: UpdatePresentationPropertiesSchema.shape
3884
+ },
3885
+ async (params) => {
3886
+ const result = await runMcpTool(
3887
+ params.filePath,
3888
+ (ctx) => updatePresentationProperties(ctx, params)
3889
+ );
3890
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3891
+ }
3892
+ );
3893
+ server2.registerTool(
3894
+ "get_layouts",
3895
+ {
3896
+ description: "List available slide layouts from the presentation masters",
3897
+ inputSchema: GetLayoutsSchema.shape
3898
+ },
3899
+ async (params) => {
3900
+ const result = await runMcpTool(params.filePath, (ctx) => getLayouts(ctx));
3901
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3902
+ }
3903
+ );
3904
+ server2.registerTool(
3905
+ "apply_layout",
3906
+ {
3907
+ description: "Apply a slide layout to a specific slide",
3908
+ inputSchema: ApplyLayoutSchema.shape
3909
+ },
3910
+ async (params) => {
3911
+ const result = await runMcpTool(
3912
+ params.filePath,
3913
+ (ctx) => applyLayout(ctx, params)
3914
+ );
3915
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3916
+ }
3917
+ );
2403
3918
  return server2;
2404
3919
  }
2405
3920