pptx-viewer-mcp 2.5.9 → 2.7.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.
@@ -1,11 +1,10 @@
1
- #!/usr/bin/env node
2
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
2
  import { z } from 'zod';
5
- import { validatePptx, repairPptx, PptxHandler, hasTextProperties, resolveThemeColorRef, cloneElement as cloneElement$1, isTemplateElementId, makeStoreAwareId, reassignDescendantIds, groupElements as groupElements$1, ungroupElements as ungroupElements$1, rebuildTableStructureInRawXml, TABLE_STYLE_PART_SEQUENCE, normalizeTableStyleGuid, createTableStyleEntry, addTableStyleToMap, deleteTableStyleFromMap, PptxMarkdownConverter, ThemePresets, getThemePreset, setChartType, setChartTitle, setChartGrouping, setChartLegend, setChartDataLabels, setChartAxis, setChartCategories, addChartSeries, removeChartSeries, updateChartSeriesValues, ChartBuilder, setChartDataPointStyle, setChartDataPointExplosion, setChartDataPointMarker, setChartDataPointLabel, setChartSeriesMarker, setChartSeriesTrendline, setChartSeriesErrorBars, setChartHelperLine, setChartColorMapOverride, listChartUserShapes, addChartUserShape, updateChartUserShape, removeChartUserShape, decomposeSmartArt, demoteSmartArtNode, promoteSmartArtNode, reorderSmartArtNode, updateSmartArtNodeText, removeSmartArtNode, addSmartArtNode, findPlaceholders, applyTemplate, getSectionForSlide, moveSlidesToSection, reorderSections, removeSection, addSection, SvgExporter, buildPptxJsonDocument, deserializePptxFromJson, applyImportedPptxData, replaceWithCustomGeometry, replaceShapeGeometry, setElementLocked, findLayoutByName, findLayoutByType, resolveOleEditorKindFromPayload, getOleNestedDeckDetail, getOleDocumentParagraphs, getOleSheetGrid, applyOleSheetCellEdit, applyOleDocumentParagraphEdit, applyOleNestedDeckElementTextEdit, parseDataUrlToBytes, replaceOleFile, setOleObjectName, resolveDisplayedChartTypeName } from 'pptx-viewer-core';
3
+ import { apiToolsChartFormattingTools, apiToolsChartTools, apiToolsChartUserShapeTools, apiToolsContentTools, apiToolsConversionTools, apiToolsElementTools, apiToolsExportTools, apiToolsGeometryTools, apiToolsHyperlinkTools, apiToolsJsonTools, apiToolsLayoutTools, apiToolsLockTools, apiToolsMetadataTools, apiToolsOleTools, apiToolsPresentationTools, apiToolsSectionTools, apiToolsSlideTools, apiToolsSmartartTools, apiToolsStyleTools, apiToolsTableStyleTools, apiToolsTableTools, apiToolsTemplateTools, apiToolsThemeTools, apiToolsValidationTools, apiExecution } from 'ooxml-core/pptx/automation';
6
4
  import { writeFile, readFile } from 'fs/promises';
7
5
  import { resolve, isAbsolute, sep } from 'path';
8
6
 
7
+ // src/mcp/server.ts
9
8
  var GetSlideSchema = z.object({
10
9
  filePath: z.string().describe("Path to the PPTX file"),
11
10
  slideIndex: z.number().int().min(0).describe("Zero-based slide index")
@@ -930,3162 +929,86 @@ var SetOleObjectNameSchema = z.object({
930
929
  elementId: z.string().describe("OLE element ID"),
931
930
  name: z.string().describe("New Object Name (p:oleObj/@name); blank/whitespace clears it")
932
931
  });
933
- function generateElementId() {
934
- return `el-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
935
- }
936
- function generateSlideId() {
937
- return `slide-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
938
- }
939
- function describeElement(el) {
940
- const base = {
941
- id: el.id,
942
- type: el.type,
943
- x: Math.round(el.x),
944
- y: Math.round(el.y),
945
- width: Math.round(el.width),
946
- height: Math.round(el.height)
947
- };
948
- if (el.rotation) {
949
- base.rotation = el.rotation;
950
- }
951
- if (el.hidden) {
952
- base.hidden = true;
953
- }
954
- if (el.opacity !== void 0 && el.opacity !== 1) {
955
- base.opacity = el.opacity;
956
- }
957
- if (hasTextProperties(el)) {
958
- base.text = el.text ?? "";
959
- if (el.textStyle) {
960
- const ts = {};
961
- if (el.textStyle.fontSize) {
962
- ts.fontSize = el.textStyle.fontSize;
963
- }
964
- if (el.textStyle.fontFamily) {
965
- ts.fontFamily = el.textStyle.fontFamily;
966
- }
967
- if (el.textStyle.bold) {
968
- ts.bold = true;
969
- }
970
- if (el.textStyle.italic) {
971
- ts.italic = true;
972
- }
973
- if (el.textStyle.underline) {
974
- ts.underline = el.textStyle.underline;
975
- }
976
- if (el.textStyle.align) {
977
- ts.align = el.textStyle.align;
978
- }
979
- if (el.textStyle.color) {
980
- ts.color = el.textStyle.color;
981
- }
982
- if (Object.keys(ts).length > 0) {
983
- base.textStyle = ts;
984
- }
985
- }
986
- }
987
- if ("shapeType" in el && el.shapeType) {
988
- base.shapeType = el.shapeType;
989
- }
990
- if ("shapeStyle" in el && el.shapeStyle) {
991
- const ss = {};
992
- const style = el.shapeStyle;
993
- if (style.fillColor) {
994
- ss.fillColor = style.fillColor;
995
- }
996
- if (style.strokeColor) {
997
- ss.strokeColor = style.strokeColor;
998
- }
999
- if (style.strokeWidth) {
1000
- ss.strokeWidth = style.strokeWidth;
1001
- }
1002
- if (Object.keys(ss).length > 0) {
1003
- base.shapeStyle = ss;
1004
- }
1005
- }
1006
- if (el.type === "image" || el.type === "picture") {
1007
- const img = el;
1008
- if (img.altText) {
1009
- base.altText = img.altText;
1010
- }
1011
- if (img.imagePath) {
1012
- base.imagePath = img.imagePath;
1013
- }
1014
- base.hasImageData = Boolean(img.imageData);
1015
- }
1016
- if (el.type === "table") {
1017
- const tbl = el;
1018
- if (tbl.tableData) {
1019
- base.rowCount = tbl.tableData.rows.length;
1020
- base.columnCount = tbl.tableData.columnWidths.length;
1021
- base.cells = tbl.tableData.rows.map((row) => row.cells.map((cell) => cell.text ?? ""));
1022
- }
1023
- }
1024
- if (el.type === "chart" && el.chartData) {
1025
- base.chartType = resolveDisplayedChartTypeName(el.chartData);
1026
- }
1027
- if (el.type === "group") {
1028
- const grp = el;
1029
- base.childCount = grp.children?.length ?? 0;
1030
- }
1031
- return base;
1032
- }
1033
- function validateSlideIndex(slideIndex, slideCount) {
1034
- if (slideIndex < 0 || slideIndex >= slideCount) {
1035
- return `Slide index ${slideIndex} out of range (0\u2013${slideCount - 1}).`;
1036
- }
1037
- return null;
1038
- }
1039
-
1040
- // src/tools/chart-formatting-tools.ts
1041
- function findChartElement(ctx, slideIndex, elementId) {
1042
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
1043
- if (err) {
1044
- throw new Error(err);
1045
- }
1046
- const slide = ctx.pptxData.slides[slideIndex];
1047
- const el = slide.elements.find((e) => e.id === elementId);
1048
- if (!el) {
1049
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
1050
- }
1051
- if (el.type !== "chart") {
1052
- throw new Error(`Element '${elementId}' is not a chart.`);
1053
- }
1054
- return el;
1055
- }
1056
- function formatChartDataPoint(ctx, params) {
1057
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
1058
- if (params.clearStyle) {
1059
- setChartDataPointStyle(chart, params.seriesIndex, params.pointIndex, null);
1060
- } else if (params.fillColor !== void 0 || params.strokeColor !== void 0 || params.strokeWidth !== void 0 || params.strokeDashStyle !== void 0) {
1061
- setChartDataPointStyle(chart, params.seriesIndex, params.pointIndex, {
1062
- ...params.fillColor !== void 0 ? { fillColor: params.fillColor } : {},
1063
- ...params.strokeColor !== void 0 ? { strokeColor: params.strokeColor } : {},
1064
- ...params.strokeWidth !== void 0 ? { strokeWidth: params.strokeWidth } : {},
1065
- ...params.strokeDashStyle !== void 0 ? { strokeDashStyle: params.strokeDashStyle } : {}
1066
- });
1067
- }
1068
- if (params.explosion !== void 0) {
1069
- setChartDataPointExplosion(chart, params.seriesIndex, params.pointIndex, params.explosion);
1070
- }
1071
- if (params.marker !== void 0) {
1072
- setChartDataPointMarker(chart, params.seriesIndex, params.pointIndex, params.marker);
1073
- }
1074
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1075
- }
1076
- function formatChartDataLabel(ctx, params) {
1077
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
1078
- if (params.remove) {
1079
- setChartDataPointLabel(chart, params.seriesIndex, params.pointIndex, null);
1080
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1081
- }
1082
- setChartDataPointLabel(chart, params.seriesIndex, params.pointIndex, {
1083
- ...params.showValue !== void 0 ? { showValue: params.showValue } : {},
1084
- ...params.showCategory !== void 0 ? { showCategory: params.showCategory } : {},
1085
- ...params.showSeriesName !== void 0 ? { showSeriesName: params.showSeriesName } : {},
1086
- ...params.showPercent !== void 0 ? { showPercent: params.showPercent } : {},
1087
- ...params.showLegendKey !== void 0 ? { showLegendKey: params.showLegendKey } : {},
1088
- ...params.position !== void 0 ? { position: params.position } : {},
1089
- ...params.text !== void 0 ? { text: params.text } : {},
1090
- ...params.spPr !== void 0 ? { spPr: params.spPr } : {},
1091
- ...params.txPr !== void 0 ? { txPr: params.txPr } : {}
1092
- });
1093
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1094
- }
1095
- function formatChartSeries(ctx, params) {
1096
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
1097
- if (params.marker !== void 0) {
1098
- if (params.marker === null) {
1099
- setChartSeriesMarker(chart, params.seriesIndex, null);
1100
- } else if (params.marker.strokeColor !== void 0 || params.marker.strokeWidth !== void 0 || params.marker.strokeDashStyle !== void 0) {
1101
- setChartSeriesMarker(chart, params.seriesIndex, {
1102
- symbol: params.marker.symbol ?? "circle",
1103
- ...params.marker.size !== void 0 ? { size: params.marker.size } : {},
1104
- spPr: {
1105
- ...params.marker.fillColor !== void 0 ? { fillColor: params.marker.fillColor } : {},
1106
- ...params.marker.strokeColor !== void 0 ? { strokeColor: params.marker.strokeColor } : {},
1107
- ...params.marker.strokeWidth !== void 0 ? { strokeWidth: params.marker.strokeWidth } : {},
1108
- ...params.marker.strokeDashStyle !== void 0 ? { strokeDashStyle: params.marker.strokeDashStyle } : {}
1109
- }
1110
- });
1111
- } else {
1112
- setChartSeriesMarker(chart, params.seriesIndex, params.marker);
1113
- }
1114
- }
1115
- if (params.trendline !== void 0) {
1116
- setChartSeriesTrendline(chart, params.seriesIndex, params.trendline);
1117
- }
1118
- if (params.errorBars !== void 0) {
1119
- setChartSeriesErrorBars(chart, params.seriesIndex, params.errorBars);
1120
- }
1121
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1122
- }
1123
- function setChartHelperLineT(ctx, params) {
1124
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
1125
- setChartHelperLine(chart, params.line, params.style);
1126
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1127
- }
1128
- function setChartColorMapOverrideT(ctx, params) {
1129
- const chart = findChartElement(ctx, params.slideIndex, params.elementId);
1130
- setChartColorMapOverride(chart, params.overrides);
1131
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1132
- }
1133
-
1134
- // src/tools/chart-pareto.ts
1135
- function cumulativePercent(values) {
1136
- const total = values.reduce((sum, value) => sum + value, 0);
1137
- if (total === 0) {
1138
- return values.map(() => 0);
1139
- }
1140
- let running = 0;
1141
- return values.map((value) => {
1142
- running += value;
1143
- return Math.round(running / total * 1e4) / 100;
1144
- });
1145
- }
1146
- function applyParetoChartTypeAlias(chart, requestedType) {
1147
- if (requestedType.trim().toLowerCase() !== "pareto" || !chart.chartData) {
1148
- return;
1149
- }
1150
- const data = chart.chartData;
1151
- data.chartType = "histogram";
1152
- const [frequency] = data.series;
1153
- if (!frequency) {
1154
- return;
1155
- }
1156
- frequency.histogramOptions = { ...frequency.histogramOptions, layout: "histogram" };
1157
- const hasParetoLine = data.series.some((s) => s.histogramOptions?.layout === "pareto");
1158
- if (!hasParetoLine) {
1159
- data.series.push({
1160
- name: "Cumulative %",
1161
- values: cumulativePercent(frequency.values),
1162
- histogramOptions: { layout: "pareto" }
1163
- });
1164
- }
1165
- }
1166
-
1167
- // src/tools/chart-tools.ts
1168
- function updateChart(ctx, params) {
1169
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1170
- if (err) {
1171
- throw new Error(err);
1172
- }
1173
- const slide = ctx.pptxData.slides[params.slideIndex];
1174
- const el = slide.elements.find((e) => e.id === params.elementId);
1175
- if (!el) {
1176
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1177
- }
1178
- if (el.type !== "chart") {
1179
- throw new Error(`Element '${params.elementId}' is not a chart.`);
1180
- }
1181
- const chart = el;
1182
- if (params.chartType) {
1183
- setChartType(chart, params.chartType);
1184
- applyParetoChartTypeAlias(chart, params.chartType);
1185
- }
1186
- if (params.title !== void 0) {
1187
- setChartTitle(chart, params.title);
1188
- }
1189
- if (params.grouping) {
1190
- setChartGrouping(chart, params.grouping);
1191
- }
1192
- if (params.legend) {
1193
- setChartLegend(chart, {
1194
- show: params.legend.show,
1195
- position: params.legend.position
1196
- });
1197
- }
1198
- if (params.dataLabels) {
1199
- setChartDataLabels(chart, params.dataLabels);
1200
- }
1201
- if (params.axis) {
1202
- setChartAxis(chart, params.axis.type, params.axis.edit);
1203
- }
1204
- if (params.categories) {
1205
- setChartCategories(chart, params.categories);
1206
- }
1207
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1208
- }
1209
- function addChartSeriesT(ctx, params) {
1210
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1211
- if (err) {
1212
- throw new Error(err);
1213
- }
1214
- const slide = ctx.pptxData.slides[params.slideIndex];
1215
- const el = slide.elements.find((e) => e.id === params.elementId);
1216
- if (!el) {
1217
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1218
- }
1219
- if (el.type !== "chart") {
1220
- throw new Error(`Element '${params.elementId}' is not a chart.`);
1221
- }
1222
- const chart = el;
1223
- addChartSeries(chart, { name: params.name, values: params.values, color: params.color });
1224
- const seriesCount = chart.chartData?.series?.length ?? 0;
1225
- return {
1226
- pptxData: ctx.pptxData,
1227
- dirty: true,
1228
- result: { elementId: params.elementId, seriesCount }
1229
- };
1230
- }
1231
- function removeChartSeriesT(ctx, params) {
1232
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1233
- if (err) {
1234
- throw new Error(err);
1235
- }
1236
- const slide = ctx.pptxData.slides[params.slideIndex];
1237
- const el = slide.elements.find((e) => e.id === params.elementId);
1238
- if (!el) {
1239
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1240
- }
1241
- if (el.type !== "chart") {
1242
- throw new Error(`Element '${params.elementId}' is not a chart.`);
1243
- }
1244
- const chart = el;
1245
- removeChartSeries(chart, params.seriesIndex);
1246
- const seriesCount = chart.chartData?.series?.length ?? 0;
1247
- return {
1248
- pptxData: ctx.pptxData,
1249
- dirty: true,
1250
- result: { elementId: params.elementId, seriesCount }
1251
- };
1252
- }
1253
- function updateChartSeriesData(ctx, params) {
1254
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1255
- if (err) {
1256
- throw new Error(err);
1257
- }
1258
- const slide = ctx.pptxData.slides[params.slideIndex];
1259
- const el = slide.elements.find((e) => e.id === params.elementId);
1260
- if (!el) {
1261
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1262
- }
1263
- if (el.type !== "chart") {
1264
- throw new Error(`Element '${params.elementId}' is not a chart.`);
1265
- }
1266
- const chart = el;
1267
- updateChartSeriesValues(chart, params.seriesIndex, params.values);
1268
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1269
- }
1270
- function createChart(ctx, params) {
1271
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1272
- if (err) {
1273
- throw new Error(err);
1274
- }
1275
- const builder = ChartBuilder.create(
1276
- params.chartType
1277
- );
1278
- builder.position(params.x ?? 100, params.y ?? 100);
1279
- builder.size(params.width ?? 500, params.height ?? 350);
1280
- if (params.title) {
1281
- builder.title(params.title);
1282
- }
1283
- if (params.categories) {
1284
- builder.categories(params.categories);
1285
- }
1286
- if (params.series) {
1287
- for (const s of params.series) {
1288
- builder.addSeries(s.name, s.values, s.color);
1289
- }
1290
- }
1291
- if (params.legend) {
1292
- builder.legend(
1293
- params.legend.show,
1294
- params.legend.position
1295
- );
1296
- }
1297
- const chart = builder.build();
1298
- applyParetoChartTypeAlias(chart, params.chartType);
1299
- const slide = ctx.pptxData.slides[params.slideIndex];
1300
- slide.elements.push(chart);
1301
- return {
1302
- pptxData: ctx.pptxData,
1303
- dirty: true,
1304
- result: { elementId: chart.id, slideIndex: params.slideIndex }
1305
- };
1306
- }
1307
- function findChartElement2(ctx, slideIndex, elementId) {
1308
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
1309
- if (err) {
1310
- throw new Error(err);
1311
- }
1312
- const slide = ctx.pptxData.slides[slideIndex];
1313
- const el = slide.elements.find((e) => e.id === elementId);
1314
- if (!el) {
1315
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
1316
- }
1317
- if (el.type !== "chart") {
1318
- throw new Error(`Element '${elementId}' is not a chart.`);
1319
- }
1320
- return el;
1321
- }
1322
- function buildShape(input) {
1323
- return {
1324
- kind: input.kind ?? "sp",
1325
- anchor: input.anchor ?? "rel",
1326
- from: input.from,
1327
- ...input.to ? { to: input.to } : {},
1328
- ...input.ext ? { ext: input.ext } : {},
1329
- ...input.prst ? { prst: input.prst } : {},
1330
- ...input.fill ? { fill: input.fill } : {},
1331
- ...input.stroke ? { stroke: input.stroke } : {},
1332
- ...input.strokeWidth !== void 0 ? { strokeWidth: input.strokeWidth } : {},
1333
- ...input.text ? { paragraphs: [{ text: input.text, align: "ctr" }] } : {}
1334
- };
1335
- }
1336
- function listChartUserShapesT(ctx, params) {
1337
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
1338
- return { pptxData: ctx.pptxData, dirty: false, result: { shapes: listChartUserShapes(chart) } };
1339
- }
1340
- function addChartUserShapeT(ctx, params) {
1341
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
1342
- addChartUserShape(chart, buildShape(params.shape));
1343
- const index = (chart.chartData?.userShapes?.length ?? 1) - 1;
1344
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId, index } };
1345
- }
1346
- function updateChartUserShapeT(ctx, params) {
1347
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
1348
- const patch = {
1349
- ...params.patch.kind ? { kind: params.patch.kind } : {},
1350
- ...params.patch.anchor ? { anchor: params.patch.anchor } : {},
1351
- ...params.patch.from ? { from: params.patch.from } : {},
1352
- ...params.patch.to ? { to: params.patch.to } : {},
1353
- ...params.patch.ext ? { ext: params.patch.ext } : {},
1354
- ...params.patch.prst ? { prst: params.patch.prst } : {},
1355
- ...params.patch.fill ? { fill: params.patch.fill } : {},
1356
- ...params.patch.stroke ? { stroke: params.patch.stroke } : {},
1357
- ...params.patch.strokeWidth !== void 0 ? { strokeWidth: params.patch.strokeWidth } : {},
1358
- ...params.patch.text !== void 0 ? { paragraphs: [{ text: params.patch.text, align: "ctr" }] } : {}
1359
- };
1360
- updateChartUserShape(chart, params.index, patch);
1361
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1362
- }
1363
- function removeChartUserShapeT(ctx, params) {
1364
- const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
1365
- removeChartUserShape(chart, params.index);
1366
- return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
1367
- }
1368
-
1369
- // src/tools/text-editing.ts
1370
- function editRange(segments, start, end, replacement) {
1371
- let offset = 0;
1372
- let inserted = false;
1373
- for (const segment of segments) {
1374
- const text = segment.text;
1375
- const next = offset + text.length;
1376
- if (!segment.bulletInfo?.ownedByParagraph && next > start && offset < end) {
1377
- const from = Math.max(0, start - offset);
1378
- const to = Math.min(text.length, end - offset);
1379
- segment.text = text.slice(0, from) + (inserted ? "" : replacement) + text.slice(to);
1380
- inserted = true;
1381
- } else if (!inserted && start === end && offset <= start && next >= start && !segment.bulletInfo?.ownedByParagraph) {
1382
- const at = start - offset;
1383
- segment.text = text.slice(0, at) + replacement + text.slice(at);
1384
- inserted = true;
1385
- }
1386
- offset = next;
1387
- }
1388
- if (!inserted && replacement) {
1389
- segments.push({ text: replacement, style: {} });
1390
- }
1391
- }
1392
- function changedRange(before, text) {
1393
- let start = 0;
1394
- while (start < before.length && start < text.length && before[start] === text[start]) {
1395
- start++;
1396
- }
1397
- let suffix = 0;
1398
- while (suffix < before.length - start && suffix < text.length - start && before[before.length - suffix - 1] === text[text.length - suffix - 1]) {
1399
- suffix++;
1400
- }
1401
- return {
1402
- start,
1403
- end: before.length - suffix,
1404
- replacement: text.slice(start, text.length - suffix)
1405
- };
1406
- }
1407
- function setElementText(element, text) {
1408
- const segments = element.textSegments;
1409
- if (segments?.length) {
1410
- const before = segments.map((segment) => segment.text).join("");
1411
- const oldLines = before.split("\n");
1412
- const newLines = text.split("\n");
1413
- if (oldLines.length === newLines.length) {
1414
- let offset = before.length;
1415
- for (let index = oldLines.length - 1; index >= 0; index--) {
1416
- const line = oldLines[index];
1417
- offset -= line.length;
1418
- const edit = changedRange(line, newLines[index]);
1419
- editRange(segments, offset + edit.start, offset + edit.end, edit.replacement);
1420
- offset--;
1421
- }
1422
- } else {
1423
- const edit = changedRange(before, text);
1424
- editRange(segments, edit.start, edit.end, edit.replacement);
1425
- }
1426
- element.text = segments.map((segment) => segment.text).join("");
1427
- } else {
1428
- element.text = text;
1429
- }
1430
- }
1431
- function replaceElementText(element, regex, replacement) {
1432
- const before = element.text ?? element.textSegments?.map((segment) => segment.text).join("") ?? "";
1433
- const matches = Array.from(before.matchAll(regex));
1434
- if (before.replace(regex, replacement) === before) {
1435
- return 0;
1436
- }
1437
- if (!element.textSegments?.length) {
1438
- element.text = before.replace(regex, replacement);
1439
- return matches.length;
1440
- }
1441
- for (const match of matches.reverse()) {
1442
- const start = match.index;
1443
- const end = start + match[0].length;
1444
- const single = new RegExp(regex.source, `${regex.flags.replace("g", "")}y`);
1445
- single.lastIndex = start;
1446
- const after = before.replace(single, replacement);
1447
- editRange(
1448
- element.textSegments,
1449
- start,
1450
- end,
1451
- after.slice(start, after.length - (before.length - end))
1452
- );
1453
- }
1454
- element.text = element.textSegments.map((segment) => segment.text).join("");
1455
- return matches.length;
1456
- }
1457
-
1458
- // src/tools/content-tools.ts
1459
- var MAX_REGEX_LEN = 200;
1460
- function hasNestedUnboundedQuantifier(pattern) {
1461
- return /\([^)]*[+*]\)[+*]/u.test(pattern);
1462
- }
1463
- function buildSearchRegex(query, useRegex, caseSensitive) {
1464
- if (useRegex) {
1465
- if (query.length > MAX_REGEX_LEN) {
1466
- return null;
1467
- }
1468
- if (hasNestedUnboundedQuantifier(query)) {
1469
- return null;
1470
- }
1471
- }
1472
- const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1473
- const flags = caseSensitive ? "g" : "gi";
1474
- try {
1475
- return new RegExp(pattern, flags);
1476
- } catch {
1477
- return null;
1478
- }
1479
- }
1480
- function findText(ctx, params) {
1481
- const regex = buildSearchRegex(
1482
- params.query,
1483
- params.useRegex === true,
1484
- params.caseSensitive === true
1485
- );
1486
- if (!regex) {
1487
- return {
1488
- pptxData: ctx.pptxData,
1489
- dirty: false,
1490
- result: { query: params.query, matchCount: 0, matches: [] }
1491
- };
1492
- }
1493
- const targetIndexes = params.slideIndexes ? params.slideIndexes : ctx.pptxData.slides.map((_, i) => i);
1494
- const matches = [];
1495
- for (const si of targetIndexes) {
1496
- const err = validateSlideIndex(si, ctx.pptxData.slides.length);
1497
- if (err) {
1498
- throw new Error(err);
1499
- }
1500
- const slide = ctx.pptxData.slides[si];
1501
- for (const el of slide.elements) {
1502
- if (hasTextProperties(el)) {
1503
- const textEl = el;
1504
- if (textEl.text) {
1505
- const localMatches = Array.from(textEl.text.matchAll(regex));
1506
- for (const m of localMatches) {
1507
- matches.push({
1508
- slideIndex: si,
1509
- elementId: el.id,
1510
- elementType: el.type,
1511
- matchedText: m[0],
1512
- context: textEl.text.slice(
1513
- Math.max(0, (m.index ?? 0) - 20),
1514
- (m.index ?? 0) + m[0].length + 20
1515
- )
1516
- });
1517
- }
1518
- }
1519
- }
1520
- if (el.type === "table") {
1521
- const tbl = el;
1522
- if (tbl.tableData) {
1523
- for (const row of tbl.tableData.rows) {
1524
- for (const cell of row.cells) {
1525
- if (cell.text) {
1526
- const localMatches = Array.from(cell.text.matchAll(regex));
1527
- for (const m of localMatches) {
1528
- matches.push({
1529
- slideIndex: si,
1530
- elementId: el.id,
1531
- elementType: "table-cell",
1532
- matchedText: m[0],
1533
- context: cell.text.slice(
1534
- Math.max(0, (m.index ?? 0) - 20),
1535
- (m.index ?? 0) + m[0].length + 20
1536
- )
1537
- });
1538
- }
1539
- }
1540
- }
1541
- }
1542
- }
1543
- }
1544
- }
1545
- if (slide.notes) {
1546
- const noteMatches = Array.from(slide.notes.matchAll(regex));
1547
- for (const m of noteMatches) {
1548
- matches.push({
1549
- slideIndex: si,
1550
- elementId: "notes",
1551
- elementType: "notes",
1552
- matchedText: m[0],
1553
- context: slide.notes.slice(
1554
- Math.max(0, (m.index ?? 0) - 20),
1555
- (m.index ?? 0) + m[0].length + 20
1556
- )
1557
- });
1558
- }
1559
- }
1560
- }
1561
- return {
1562
- pptxData: ctx.pptxData,
1563
- dirty: false,
1564
- result: {
1565
- query: params.query,
1566
- matchCount: matches.length,
1567
- matches
1568
- }
1569
- };
1570
- }
1571
- function replaceText(ctx, params) {
1572
- const regex = buildSearchRegex(
1573
- params.query,
1574
- params.useRegex === true,
1575
- params.caseSensitive === true
1576
- );
1577
- if (!regex) {
1578
- return {
1579
- pptxData: ctx.pptxData,
1580
- dirty: false,
1581
- result: { query: params.query, replacement: params.replacement, replacementCount: 0 }
1582
- };
1583
- }
1584
- const targetIndexes = params.slideIndexes ? params.slideIndexes : ctx.pptxData.slides.map((_, i) => i);
1585
- let count = 0;
1586
- for (const si of targetIndexes) {
1587
- const err = validateSlideIndex(si, ctx.pptxData.slides.length);
1588
- if (err) {
1589
- throw new Error(err);
1590
- }
1591
- const slide = ctx.pptxData.slides[si];
1592
- for (const el of slide.elements) {
1593
- if (hasTextProperties(el)) {
1594
- const textEl = el;
1595
- if (textEl.text) {
1596
- const before = textEl.text;
1597
- const after = before.replace(regex, params.replacement);
1598
- if (after !== before) {
1599
- const matchCount = Array.from(before.matchAll(regex)).length;
1600
- count += matchCount;
1601
- replaceElementText(textEl, regex, params.replacement);
1602
- }
1603
- }
1604
- }
1605
- if (el.type === "table") {
1606
- const tbl = el;
1607
- if (tbl.tableData) {
1608
- for (const row of tbl.tableData.rows) {
1609
- for (const cell of row.cells) {
1610
- if (cell.text) {
1611
- const before = cell.text;
1612
- const after = before.replace(regex, params.replacement);
1613
- if (after !== before) {
1614
- count += Array.from(before.matchAll(regex)).length;
1615
- cell.text = after;
1616
- }
1617
- }
1618
- }
1619
- }
1620
- }
1621
- }
1622
- }
1623
- if (slide.notes) {
1624
- const before = slide.notes;
1625
- const after = before.replace(regex, params.replacement);
1626
- if (after !== before) {
1627
- count += Array.from(before.matchAll(regex)).length;
1628
- slide.notes = after;
1629
- }
1630
- }
1631
- }
1632
- return {
1633
- pptxData: ctx.pptxData,
1634
- dirty: count > 0,
1635
- result: {
1636
- query: params.query,
1637
- replacement: params.replacement,
1638
- replacementCount: count
1639
- }
1640
- };
1641
- }
1642
- function manageComments(ctx, params) {
1643
- switch (params.action) {
1644
- case "list": {
1645
- const all = [];
1646
- for (let si = 0; si < ctx.pptxData.slides.length; si++) {
1647
- if (params.slideIndex !== void 0 && si !== params.slideIndex) {
1648
- continue;
1649
- }
1650
- const slide = ctx.pptxData.slides[si];
1651
- for (const c of slide.comments ?? []) {
1652
- if (!params.includeResolved && c.resolved) {
1653
- continue;
1654
- }
1655
- all.push({
1656
- id: c.id,
1657
- slideIndex: si,
1658
- text: c.text,
1659
- author: c.author,
1660
- createdAt: c.createdAt,
1661
- resolved: c.resolved
1662
- });
1663
- }
1664
- }
1665
- return {
1666
- pptxData: ctx.pptxData,
1667
- dirty: false,
1668
- result: { action: "list", comments: all, count: all.length }
1669
- };
1670
- }
1671
- case "add": {
1672
- if (params.slideIndex === void 0) {
1673
- throw new Error("slideIndex is required for add action.");
1674
- }
1675
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1676
- if (err) {
1677
- throw new Error(err);
1678
- }
1679
- if (!params.text) {
1680
- throw new Error("text is required for add action.");
1681
- }
1682
- const slide = ctx.pptxData.slides[params.slideIndex];
1683
- if (!slide.comments) {
1684
- slide.comments = [];
1685
- }
1686
- const newId = `comment-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1687
- slide.comments.push({
1688
- id: newId,
1689
- text: params.text,
1690
- author: params.author,
1691
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1692
- resolved: false
1693
- });
1694
- return {
1695
- pptxData: ctx.pptxData,
1696
- dirty: true,
1697
- result: { action: "add", commentId: newId }
1698
- };
1699
- }
1700
- case "delete": {
1701
- if (!params.commentId) {
1702
- throw new Error("commentId is required for delete action.");
1703
- }
1704
- let deleted = false;
1705
- for (const slide of ctx.pptxData.slides) {
1706
- if (!slide.comments) {
1707
- continue;
1708
- }
1709
- const before = slide.comments.length;
1710
- slide.comments = slide.comments.filter((c) => c.id !== params.commentId);
1711
- if (slide.comments.length < before) {
1712
- deleted = true;
1713
- break;
1714
- }
1715
- }
1716
- if (!deleted) {
1717
- throw new Error(`Comment '${params.commentId}' not found.`);
1718
- }
1719
- return {
1720
- pptxData: ctx.pptxData,
1721
- dirty: true,
1722
- result: { action: "delete", commentId: params.commentId }
1723
- };
1724
- }
1725
- case "resolve": {
1726
- if (!params.commentId) {
1727
- throw new Error("commentId is required for resolve action.");
1728
- }
1729
- let found = false;
1730
- for (const slide of ctx.pptxData.slides) {
1731
- const comment = slide.comments?.find((c) => c.id === params.commentId);
1732
- if (comment) {
1733
- comment.resolved = params.resolved !== false;
1734
- found = true;
1735
- break;
1736
- }
1737
- }
1738
- if (!found) {
1739
- throw new Error(`Comment '${params.commentId}' not found.`);
1740
- }
1741
- return {
1742
- pptxData: ctx.pptxData,
1743
- dirty: true,
1744
- result: { action: "resolve", commentId: params.commentId }
1745
- };
1746
- }
1747
- default: {
1748
- throw new Error(`Unknown action: ${String(params.action)}`);
1749
- }
1750
- }
1751
- }
1752
- async function convertToMarkdown(ctx, params) {
1753
- const options = {
1754
- mediaFolderName: params.mediaFolderName ?? "media",
1755
- includeMetadata: params.includeMetadata !== false,
1756
- slideRange: params.slideRange,
1757
- includeSpeakerNotes: params.includeSpeakerNotes !== false,
1758
- semanticMode: params.semanticMode === true,
1759
- sourceName: params.sourceName ?? "unknown"
1760
- };
1761
- const outputDir = params.outputDir ?? ".";
1762
- const safeOutputDir = params.scopeOutputDir ? params.scopeOutputDir(outputDir, params.rootDir) : outputDir;
1763
- const converter = new PptxMarkdownConverter(safeOutputDir, options);
1764
- const markdown = await converter.convert(ctx.pptxData);
1765
- return {
1766
- pptxData: ctx.pptxData,
1767
- dirty: false,
1768
- result: {
1769
- markdown,
1770
- slidesConverted: converter.slidesConverted,
1771
- totalSlides: converter.presentationSlides,
1772
- imagesExtracted: converter.imagesExtracted
1773
- }
1774
- };
1775
- }
1776
- var ALT_TEXT_ELEMENT_TYPES = /* @__PURE__ */ new Set(["image", "picture", "table", "chart", "smartArt", "ole", "media", "text", "shape", "connector"]);
1777
- var TITLE_ELEMENT_TYPES = /* @__PURE__ */ new Set([
1778
- "table",
1779
- "chart",
1780
- "smartArt",
1781
- "ole",
1782
- "media",
1783
- "text",
1784
- "shape",
1785
- "connector"
1786
- ]);
1787
- function applyElementAltText(el, altText) {
1788
- if (!ALT_TEXT_ELEMENT_TYPES.has(el.type)) {
1789
- throw new Error(
1790
- `Element '${el.id}' (${el.type}) does not support altText; only ${[...ALT_TEXT_ELEMENT_TYPES].join(", ")} elements do.`
1791
- );
1792
- }
1793
- el.altText = altText;
1794
- }
1795
- function applyElementTitle(el, title) {
1796
- if (!TITLE_ELEMENT_TYPES.has(el.type)) {
1797
- throw new Error(
1798
- `Element '${el.id}' (${el.type}) does not support title; only ${[...TITLE_ELEMENT_TYPES].join(", ")} elements do.`
1799
- );
1800
- }
1801
- el.title = title;
1802
- }
1803
- function updateElementStyle(ctx, params) {
1804
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1805
- if (err) {
1806
- throw new Error(err);
1807
- }
1808
- const slide = ctx.pptxData.slides[params.slideIndex];
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
- if ("shapeStyle" in el) {
1814
- if (!el.shapeStyle) {
1815
- el.shapeStyle = {};
1816
- }
1817
- const ss = el.shapeStyle;
1818
- if (params.fillThemeColor !== void 0) {
1819
- ss.fillColorRef = params.fillThemeColor;
1820
- const resolved = resolveThemeColorRef(params.fillThemeColor, ctx.pptxData.themeColorMap);
1821
- if (params.fillColor !== void 0) {
1822
- ss.fillColor = params.fillColor;
1823
- } else if (resolved) {
1824
- ss.fillColor = resolved;
1825
- }
1826
- } else if (params.fillColor !== void 0) {
1827
- ss.fillColor = params.fillColor;
1828
- ss.fillColorRef = void 0;
1829
- }
1830
- if (params.fillMode !== void 0) {
1831
- ss.fillMode = params.fillMode;
1832
- }
1833
- if (params.fillGradientStops !== void 0) {
1834
- ss.fillGradientStops = params.fillGradientStops;
1835
- }
1836
- if (params.fillGradientAngle !== void 0) {
1837
- ss.fillGradientAngle = params.fillGradientAngle;
1838
- }
1839
- if (params.fillGradientType !== void 0) {
1840
- ss.fillGradientType = params.fillGradientType;
1841
- }
1842
- if (params.fillOpacity !== void 0) {
1843
- ss.fillOpacity = params.fillOpacity;
1844
- }
1845
- if (params.strokeThemeColor !== void 0) {
1846
- ss.strokeColorRef = params.strokeThemeColor;
1847
- const resolved = resolveThemeColorRef(params.strokeThemeColor, ctx.pptxData.themeColorMap);
1848
- if (params.strokeColor !== void 0) {
1849
- ss.strokeColor = params.strokeColor;
1850
- } else if (resolved) {
1851
- ss.strokeColor = resolved;
1852
- }
1853
- } else if (params.strokeColor !== void 0) {
1854
- ss.strokeColor = params.strokeColor;
1855
- ss.strokeColorRef = void 0;
1856
- }
1857
- if (params.strokeWidth !== void 0) {
1858
- ss.strokeWidth = params.strokeWidth;
1859
- }
1860
- if (params.strokeDash !== void 0) {
1861
- ss.strokeDash = params.strokeDash;
1862
- }
1863
- if (params.strokeOpacity !== void 0) {
1864
- ss.strokeOpacity = params.strokeOpacity;
1865
- }
1866
- if (params.shadowColor !== void 0) {
1867
- ss.shadowColor = params.shadowColor;
1868
- }
1869
- if (params.shadowBlur !== void 0) {
1870
- ss.shadowBlur = params.shadowBlur;
1871
- }
1872
- if (params.shadowOffsetX !== void 0) {
1873
- ss.shadowOffsetX = params.shadowOffsetX;
1874
- }
1875
- if (params.shadowOffsetY !== void 0) {
1876
- ss.shadowOffsetY = params.shadowOffsetY;
1877
- }
1878
- if (params.shadowOpacity !== void 0) {
1879
- ss.shadowOpacity = params.shadowOpacity;
1880
- }
1881
- if (params.glowColor !== void 0) {
1882
- ss.glowColor = params.glowColor;
1883
- }
1884
- if (params.glowRadius !== void 0) {
1885
- ss.glowRadius = params.glowRadius;
1886
- }
1887
- if (params.glowOpacity !== void 0) {
1888
- ss.glowOpacity = params.glowOpacity;
1889
- }
1890
- if (params.softEdgeRadius !== void 0) {
1891
- ss.softEdgeRadius = params.softEdgeRadius;
1892
- }
1893
- }
1894
- if (params.altText !== void 0) {
1895
- applyElementAltText(el, params.altText);
1896
- }
1897
- if (params.title !== void 0) {
1898
- applyElementTitle(el, params.title);
1899
- }
1900
- if (el.type === "image" || el.type === "picture") {
1901
- const img = el;
1902
- if (params.cropLeft !== void 0) {
1903
- img.cropLeft = params.cropLeft;
1904
- }
1905
- if (params.cropTop !== void 0) {
1906
- img.cropTop = params.cropTop;
1907
- }
1908
- if (params.cropRight !== void 0) {
1909
- img.cropRight = params.cropRight;
1910
- }
1911
- if (params.cropBottom !== void 0) {
1912
- img.cropBottom = params.cropBottom;
1913
- }
1914
- if (params.brightness !== void 0 || params.contrast !== void 0 || params.grayscale !== void 0) {
1915
- if (!img.imageEffects) {
1916
- img.imageEffects = {};
1917
- }
1918
- if (params.brightness !== void 0) {
1919
- img.imageEffects.brightness = params.brightness;
1920
- }
1921
- if (params.contrast !== void 0) {
1922
- img.imageEffects.contrast = params.contrast;
1923
- }
1924
- if (params.grayscale !== void 0) {
1925
- img.imageEffects.grayscale = params.grayscale;
1926
- }
1927
- }
1928
- }
1929
- return {
1930
- pptxData: ctx.pptxData,
1931
- dirty: true,
1932
- result: { elementId: params.elementId }
1933
- };
1934
- }
1935
- function runAccessibilityCheck(ctx) {
1936
- const issues = [];
1937
- for (let si = 0; si < ctx.pptxData.slides.length; si++) {
1938
- const slide = ctx.pptxData.slides[si];
1939
- let slideHasText = false;
1940
- for (const el of slide.elements) {
1941
- if (el.type === "image" || el.type === "picture") {
1942
- const img = el;
1943
- if (!img.altText) {
1944
- issues.push({
1945
- slideIndex: si,
1946
- elementId: el.id,
1947
- severity: "error",
1948
- message: `Image element '${el.id}' has no alt text.`
1949
- });
1950
- }
1951
- }
1952
- if (hasTextProperties(el)) {
1953
- const textEl = el;
1954
- slideHasText = true;
1955
- if (!textEl.text || textEl.text.trim() === "") {
1956
- issues.push({
1957
- slideIndex: si,
1958
- elementId: el.id,
1959
- severity: "info",
1960
- message: `Text element '${el.id}' is empty.`
1961
- });
1962
- }
1963
- const fs = textEl.textStyle?.fontSize;
1964
- if (fs !== void 0 && fs < 10) {
1965
- issues.push({
1966
- slideIndex: si,
1967
- elementId: el.id,
1968
- severity: "warning",
1969
- message: `Text element '${el.id}' has very small font size (${fs}pt < 10pt).`
1970
- });
1971
- }
1972
- const textColor = textEl.textStyle?.color?.toLowerCase();
1973
- const bgColor = slide.backgroundColor?.toLowerCase();
1974
- if (textColor && bgColor && textColor === bgColor) {
1975
- issues.push({
1976
- slideIndex: si,
1977
- elementId: el.id,
1978
- severity: "error",
1979
- message: `Text element '${el.id}' has same color as slide background (${textColor}).`
1980
- });
1981
- }
1982
- }
1983
- }
1984
- if (!slideHasText) {
1985
- issues.push({
1986
- slideIndex: si,
1987
- severity: "warning",
1988
- message: `Slide ${si + 1} contains no text elements.`
1989
- });
1990
- }
1991
- }
1992
- const errorCount = issues.filter((i) => i.severity === "error").length;
1993
- const warningCount = issues.filter((i) => i.severity === "warning").length;
1994
- const infoCount = issues.filter((i) => i.severity === "info").length;
1995
- return {
1996
- pptxData: ctx.pptxData,
1997
- dirty: false,
1998
- result: {
1999
- slideCount: ctx.pptxData.slides.length,
2000
- totalIssues: issues.length,
2001
- errorCount,
2002
- warningCount,
2003
- infoCount,
2004
- issues
2005
- }
2006
- };
2007
- }
2008
-
2009
- // src/tools/element-tools.ts
2010
- function addElement(ctx, params) {
2011
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2012
- if (err) {
2013
- throw new Error(err);
2014
- }
2015
- const slide = ctx.pptxData.slides[params.slideIndex];
2016
- const id = generateElementId();
2017
- const x = params.x ?? 100;
2018
- const y = params.y ?? 100;
2019
- const width = params.width ?? 300;
2020
- const height = params.height ?? 60;
2021
- switch (params.type) {
2022
- case "text": {
2023
- const el = {
2024
- id,
2025
- type: "text",
2026
- x,
2027
- y,
2028
- width,
2029
- height,
2030
- text: params.text ?? "",
2031
- textStyle: {
2032
- fontSize: params.fontSize,
2033
- fontFamily: params.fontFamily,
2034
- color: params.fontColor,
2035
- bold: params.bold,
2036
- italic: params.italic,
2037
- underline: params.underline,
2038
- align: params.alignment
2039
- },
2040
- textSegments: [
2041
- {
2042
- text: params.text ?? "",
2043
- style: {
2044
- fontSize: params.fontSize,
2045
- fontFamily: params.fontFamily,
2046
- color: params.fontColor,
2047
- bold: params.bold,
2048
- italic: params.italic,
2049
- underline: params.underline
2050
- }
2051
- }
2052
- ]
2053
- };
2054
- slide.elements.push(el);
2055
- break;
2056
- }
2057
- case "shape": {
2058
- const el = {
2059
- id,
2060
- type: "shape",
2061
- x,
2062
- y,
2063
- width,
2064
- height,
2065
- shapeType: params.shapeType ?? "rect",
2066
- text: params.text,
2067
- textStyle: params.text ? {
2068
- fontSize: params.fontSize,
2069
- fontFamily: params.fontFamily,
2070
- color: params.fontColor,
2071
- bold: params.bold,
2072
- italic: params.italic,
2073
- align: params.alignment
2074
- } : void 0,
2075
- shapeStyle: {
2076
- fillColor: params.fillColor,
2077
- strokeColor: params.strokeColor,
2078
- strokeWidth: params.strokeWidth
2079
- }
2080
- };
2081
- slide.elements.push(el);
2082
- break;
2083
- }
2084
- case "image": {
2085
- const el = {
2086
- id,
2087
- type: "image",
2088
- x,
2089
- y,
2090
- width,
2091
- height,
2092
- imageData: params.imageData,
2093
- altText: params.altText
2094
- };
2095
- slide.elements.push(el);
2096
- break;
2097
- }
2098
- case "table": {
2099
- const rowCount = params.rows ?? 2;
2100
- const colCount = params.columns ?? 2;
2101
- const colWidth = 1 / colCount;
2102
- const tableRows = Array.from({ length: rowCount }, (_, r) => ({
2103
- height: 40,
2104
- cells: Array.from({ length: colCount }, (__, c) => ({
2105
- text: params.cellData?.[r]?.[c] ?? ""
2106
- }))
2107
- }));
2108
- const tableEl = {
2109
- id,
2110
- type: "table",
2111
- x,
2112
- y,
2113
- width,
2114
- height: params.height ?? rowCount * 40,
2115
- tableData: {
2116
- rows: tableRows,
2117
- columnWidths: Array.from({ length: colCount }, () => colWidth),
2118
- firstRowHeader: params.headerRow ?? false
2119
- }
2120
- };
2121
- slide.elements.push(tableEl);
2122
- break;
2123
- }
2124
- case "connector": {
2125
- const el = {
2126
- id,
2127
- type: "connector",
2128
- x,
2129
- y,
2130
- width,
2131
- height,
2132
- shapeStyle: {
2133
- strokeColor: params.strokeColor ?? "#000000",
2134
- strokeWidth: params.strokeWidth ?? 1,
2135
- connectorStartArrow: params.startArrow,
2136
- connectorEndArrow: params.endArrow
2137
- }
2138
- };
2139
- slide.elements.push(el);
2140
- break;
2141
- }
2142
- default: {
2143
- throw new Error(`Unknown element type: ${String(params.type)}`);
2144
- }
2145
- }
2146
- return {
2147
- pptxData: ctx.pptxData,
2148
- dirty: true,
2149
- result: { elementId: id, slideIndex: params.slideIndex }
2150
- };
2151
- }
2152
- function updateElement(ctx, params) {
2153
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2154
- if (err) {
2155
- throw new Error(err);
2156
- }
2157
- const slide = ctx.pptxData.slides[params.slideIndex];
2158
- const el = slide.elements.find((e) => e.id === params.elementId);
2159
- if (!el) {
2160
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2161
- }
2162
- if (params.x !== void 0) {
2163
- el.x = params.x;
2164
- }
2165
- if (params.y !== void 0) {
2166
- el.y = params.y;
2167
- }
2168
- if (params.width !== void 0) {
2169
- el.width = params.width;
2170
- }
2171
- if (params.height !== void 0) {
2172
- el.height = params.height;
2173
- }
2174
- if (params.rotation !== void 0) {
2175
- el.rotation = params.rotation;
2176
- }
2177
- if (params.opacity !== void 0) {
2178
- el.opacity = params.opacity;
2179
- }
2180
- if (params.hidden !== void 0) {
2181
- el.hidden = params.hidden;
2182
- }
2183
- if (params.flipH !== void 0) {
2184
- el.flipHorizontal = params.flipH;
2185
- }
2186
- if (params.flipV !== void 0) {
2187
- el.flipVertical = params.flipV;
2188
- }
2189
- if (params.altText !== void 0) {
2190
- applyElementAltText(el, params.altText);
2191
- }
2192
- if (params.title !== void 0) {
2193
- applyElementTitle(el, params.title);
2194
- }
2195
- if (hasTextProperties(el)) {
2196
- const textEl = el;
2197
- if (params.textSegments !== void 0) {
2198
- textEl.textSegments = structuredClone(params.textSegments);
2199
- textEl.text = textEl.textSegments.map((segment) => segment.text).join("");
2200
- } else if (params.text !== void 0) {
2201
- setElementText(textEl, params.text);
2202
- }
2203
- if (!textEl.textStyle) {
2204
- textEl.textStyle = {};
2205
- }
2206
- if (params.fontSize !== void 0) {
2207
- textEl.textStyle.fontSize = params.fontSize;
2208
- }
2209
- if (params.fontFamily !== void 0) {
2210
- textEl.textStyle.fontFamily = params.fontFamily;
2211
- }
2212
- if (params.fontThemeColor !== void 0) {
2213
- textEl.textStyle.colorRef = params.fontThemeColor;
2214
- const resolved = resolveThemeColorRef(params.fontThemeColor, ctx.pptxData.themeColorMap);
2215
- if (params.fontColor !== void 0) {
2216
- textEl.textStyle.color = params.fontColor;
2217
- } else if (resolved) {
2218
- textEl.textStyle.color = resolved;
2219
- }
2220
- } else if (params.fontColor !== void 0) {
2221
- textEl.textStyle.color = params.fontColor;
2222
- textEl.textStyle.colorRef = void 0;
2223
- }
2224
- if (params.bold !== void 0) {
2225
- textEl.textStyle.bold = params.bold;
2226
- }
2227
- if (params.italic !== void 0) {
2228
- textEl.textStyle.italic = params.italic;
2229
- }
2230
- if (params.underline !== void 0) {
2231
- textEl.textStyle.underline = params.underline;
2232
- }
2233
- if (params.alignment !== void 0) {
2234
- textEl.textStyle.align = params.alignment;
2235
- }
2236
- }
2237
- if ("shapeStyle" in el && el.shapeStyle) {
2238
- const ss = el.shapeStyle;
2239
- if (params.fillColor !== void 0) {
2240
- ss.fillColor = params.fillColor;
2241
- }
2242
- if (params.strokeColor !== void 0) {
2243
- ss.strokeColor = params.strokeColor;
2244
- }
2245
- if (params.strokeWidth !== void 0) {
2246
- ss.strokeWidth = params.strokeWidth;
2247
- }
2248
- } else if (params.fillColor !== void 0 || params.strokeColor !== void 0 || params.strokeWidth !== void 0) {
2249
- if ("shapeStyle" in el) {
2250
- el.shapeStyle = {
2251
- fillColor: params.fillColor,
2252
- strokeColor: params.strokeColor,
2253
- strokeWidth: params.strokeWidth
2254
- };
2255
- }
2256
- }
2257
- return {
2258
- pptxData: ctx.pptxData,
2259
- dirty: true,
2260
- result: { elementId: params.elementId }
2261
- };
2262
- }
2263
- function renameElement(ctx, params) {
2264
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2265
- if (err) {
2266
- throw new Error(err);
2267
- }
2268
- const slide = ctx.pptxData.slides[params.slideIndex];
2269
- const el = slide.elements.find((e) => e.id === params.elementId);
2270
- if (!el) {
2271
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2272
- }
2273
- const name = params.name.trim();
2274
- if (name.length === 0) {
2275
- delete el.name;
2276
- } else {
2277
- el.name = name;
2278
- }
2279
- return {
2280
- pptxData: ctx.pptxData,
2281
- dirty: true,
2282
- result: { elementId: params.elementId, name }
2283
- };
2284
- }
2285
- function deleteElements(ctx, params) {
2286
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2287
- if (err) {
2288
- throw new Error(err);
2289
- }
2290
- const slide = ctx.pptxData.slides[params.slideIndex];
2291
- const idSet = new Set(params.elementIds);
2292
- const existingIds = new Set(slide.elements.map((element) => element.id));
2293
- const notFound = params.elementIds.filter((id) => !existingIds.has(id));
2294
- if (notFound.length > 0) {
2295
- throw new Error(`Elements not found: ${notFound.join(", ")}`);
2296
- }
2297
- const before = slide.elements.length;
2298
- slide.elements = slide.elements.filter((e) => !idSet.has(e.id));
2299
- return {
2300
- pptxData: ctx.pptxData,
2301
- dirty: true,
2302
- result: { deletedCount: before - slide.elements.length }
2303
- };
2304
- }
2305
- function arrangeElements(ctx, params) {
2306
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2307
- if (err) {
2308
- throw new Error(err);
2309
- }
2310
- const slide = ctx.pptxData.slides[params.slideIndex];
2311
- if (params.action === "align") {
2312
- if (!params.elementIds || params.elementIds.length === 0) {
2313
- throw new Error("elementIds is required for align action.");
2314
- }
2315
- const targetIds = new Set(params.elementIds);
2316
- const targets = slide.elements.filter((element) => targetIds.has(element.id));
2317
- if (targets.length === 0) {
2318
- throw new Error("No matching elements found for align.");
2319
- }
2320
- switch (params.alignment) {
2321
- case "left": {
2322
- const minX = Math.min(...targets.map((e) => e.x));
2323
- for (const t of targets) {
2324
- t.x = minX;
2325
- }
2326
- break;
2327
- }
2328
- case "right": {
2329
- const maxRight = Math.max(...targets.map((e) => e.x + e.width));
2330
- for (const t of targets) {
2331
- t.x = maxRight - t.width;
2332
- }
2333
- break;
2334
- }
2335
- case "top": {
2336
- const minY = Math.min(...targets.map((e) => e.y));
2337
- for (const t of targets) {
2338
- t.y = minY;
2339
- }
2340
- break;
2341
- }
2342
- case "bottom": {
2343
- const maxBottom = Math.max(...targets.map((e) => e.y + e.height));
2344
- for (const t of targets) {
2345
- t.y = maxBottom - t.height;
2346
- }
2347
- break;
2348
- }
2349
- case "centerH": {
2350
- const avgCX = targets.reduce((s, e) => s + e.x + e.width / 2, 0) / targets.length;
2351
- for (const t of targets) {
2352
- t.x = avgCX - t.width / 2;
2353
- }
2354
- break;
2355
- }
2356
- case "centerV": {
2357
- const avgCY = targets.reduce((s, e) => s + e.y + e.height / 2, 0) / targets.length;
2358
- for (const t of targets) {
2359
- t.y = avgCY - t.height / 2;
2360
- }
2361
- break;
2362
- }
2363
- default: {
2364
- throw new Error(`Unknown alignment: ${String(params.alignment)}`);
2365
- }
2366
- }
2367
- } else if (params.action === "reorderLayer") {
2368
- if (!params.elementId) {
2369
- throw new Error("elementId is required for reorderLayer action.");
2370
- }
2371
- const idx = slide.elements.findIndex((e) => e.id === params.elementId);
2372
- if (idx < 0) {
2373
- throw new Error(`Element '${params.elementId}' not found.`);
2374
- }
2375
- const [el] = slide.elements.splice(idx, 1);
2376
- switch (params.layerAction) {
2377
- case "bringToFront":
2378
- slide.elements.push(el);
2379
- break;
2380
- case "sendToBack":
2381
- slide.elements.unshift(el);
2382
- break;
2383
- case "bringForward": {
2384
- const newIdx = Math.min(idx + 1, slide.elements.length);
2385
- slide.elements.splice(newIdx, 0, el);
2386
- break;
2387
- }
2388
- case "sendBackward": {
2389
- const newIdx = Math.max(idx - 1, 0);
2390
- slide.elements.splice(newIdx, 0, el);
2391
- break;
2392
- }
2393
- default: {
2394
- slide.elements.splice(idx, 0, el);
2395
- throw new Error(`Unknown layerAction: ${String(params.layerAction)}`);
2396
- }
2397
- }
2398
- } else {
2399
- throw new Error(`Unknown action: ${String(params.action)}`);
2400
- }
2401
- return {
2402
- pptxData: ctx.pptxData,
2403
- dirty: true,
2404
- result: { slideIndex: params.slideIndex }
2405
- };
2406
- }
2407
- function cloneElement(ctx, params) {
2408
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2409
- if (err) {
2410
- throw new Error(err);
2411
- }
2412
- const srcSlide = ctx.pptxData.slides[params.slideIndex];
2413
- const original = srcSlide.elements.find((e) => e.id === params.elementId);
2414
- if (!original) {
2415
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2416
- }
2417
- const targets = params.targetSlideIndexes ?? [params.slideIndex];
2418
- const clonedIds = [];
2419
- const offsetX = params.offsetX ?? 20;
2420
- const offsetY = params.offsetY ?? 20;
2421
- for (const targetIdx of targets) {
2422
- const terr = validateSlideIndex(targetIdx, ctx.pptxData.slides.length);
2423
- if (terr) {
2424
- throw new Error(terr);
2425
- }
2426
- const clone = cloneElement$1(original);
2427
- const intoTemplate = isTemplateElementId(original.id);
2428
- clone.id = makeStoreAwareId(intoTemplate, original.id);
2429
- reassignDescendantIds(clone, () => makeStoreAwareId(intoTemplate, original.id));
2430
- clone.x += offsetX;
2431
- clone.y += offsetY;
2432
- ctx.pptxData.slides[targetIdx].elements.push(clone);
2433
- clonedIds.push(clone.id);
2434
- }
2435
- return {
2436
- pptxData: ctx.pptxData,
2437
- dirty: true,
2438
- result: { clonedIds }
2439
- };
2440
- }
2441
- function setElementAnimation(ctx, params) {
2442
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2443
- if (err) {
2444
- throw new Error(err);
2445
- }
2446
- const slide = ctx.pptxData.slides[params.slideIndex];
2447
- const elExists = slide.elements.some((e) => e.id === params.elementId);
2448
- if (!elExists) {
2449
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2450
- }
2451
- if (!slide.animations) {
2452
- slide.animations = [];
2453
- }
2454
- const existing = slide.animations.find((a) => a.elementId === params.elementId);
2455
- if (existing) {
2456
- if (params.entrance !== void 0) {
2457
- existing.entrance = params.entrance;
2458
- }
2459
- if (params.exit !== void 0) {
2460
- existing.exit = params.exit;
2461
- }
2462
- if (params.durationMs !== void 0) {
2463
- existing.durationMs = params.durationMs;
2464
- }
2465
- if (params.delayMs !== void 0) {
2466
- existing.delayMs = params.delayMs;
2467
- }
2468
- if (params.order !== void 0) {
2469
- existing.order = params.order;
2470
- }
2471
- } else {
2472
- const anim = {
2473
- elementId: params.elementId,
2474
- entrance: params.entrance,
2475
- exit: params.exit,
2476
- durationMs: params.durationMs,
2477
- delayMs: params.delayMs,
2478
- order: params.order
2479
- };
2480
- slide.animations.push(anim);
2481
- }
2482
- return {
2483
- pptxData: ctx.pptxData,
2484
- dirty: true,
2485
- result: { elementId: params.elementId }
2486
- };
2487
- }
2488
- function groupElements(ctx, params) {
2489
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2490
- if (err) {
2491
- throw new Error(err);
2492
- }
2493
- if (params.elementIds.length < 2) {
2494
- throw new Error("At least 2 elements are required to form a group.");
2495
- }
2496
- const slide = ctx.pptxData.slides[params.slideIndex];
2497
- const idSet = new Set(params.elementIds);
2498
- const found = new Set(slide.elements.filter((e) => idSet.has(e.id)).map((e) => e.id));
2499
- if (found.size !== idSet.size) {
2500
- const missing = params.elementIds.filter((id) => !found.has(id));
2501
- throw new Error(`Elements not found: ${missing.join(", ")}`);
2502
- }
2503
- const groupId = generateElementId();
2504
- const grouped = groupElements$1(slide.elements, params.elementIds, groupId);
2505
- slide.elements = grouped.elements;
2506
- return {
2507
- pptxData: ctx.pptxData,
2508
- dirty: true,
2509
- result: { groupId: grouped.groupId ?? groupId }
2510
- };
2511
- }
2512
- function ungroupElements(ctx, params) {
2513
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2514
- if (err) {
2515
- throw new Error(err);
2516
- }
2517
- const slide = ctx.pptxData.slides[params.slideIndex];
2518
- const group = slide.elements.find((e) => e.id === params.groupElementId);
2519
- if (!group) {
2520
- throw new Error(`Group element '${params.groupElementId}' not found.`);
2521
- }
2522
- if (group.type !== "group") {
2523
- throw new Error(`Element '${params.groupElementId}' is not a group.`);
2524
- }
2525
- const intoTemplate = isTemplateElementId(group.id);
2526
- const childIds = group.children.map(
2527
- (child) => isTemplateElementId(child.id) === intoTemplate ? child.id : makeStoreAwareId(intoTemplate, group.id)
2528
- );
2529
- const promoted = ungroupElements$1(slide.elements, params.groupElementId, childIds, {
2530
- intoTemplate
2531
- });
2532
- slide.elements = promoted.elements;
2533
- return {
2534
- pptxData: ctx.pptxData,
2535
- dirty: true,
2536
- result: { restoredIds: promoted.childIds }
2537
- };
2538
- }
2539
- function batchUpdateElements(ctx, params) {
2540
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2541
- if (err) {
2542
- throw new Error(err);
2543
- }
2544
- const slide = ctx.pptxData.slides[params.slideIndex];
2545
- const idSet = new Set(params.elementIds);
2546
- let count = 0;
2547
- for (const el of slide.elements) {
2548
- if (!idSet.has(el.id)) {
2549
- continue;
2550
- }
2551
- if (params.x !== void 0) {
2552
- el.x = params.x;
2553
- }
2554
- if (params.y !== void 0) {
2555
- el.y = params.y;
2556
- }
2557
- if (params.width !== void 0) {
2558
- el.width = params.width;
2559
- }
2560
- if (params.height !== void 0) {
2561
- el.height = params.height;
2562
- }
2563
- if (params.rotation !== void 0) {
2564
- el.rotation = params.rotation;
2565
- }
2566
- if (params.opacity !== void 0) {
2567
- el.opacity = params.opacity;
2568
- }
2569
- if (params.hidden !== void 0) {
2570
- el.hidden = params.hidden;
2571
- }
2572
- count++;
2573
- }
2574
- return {
2575
- pptxData: ctx.pptxData,
2576
- dirty: true,
2577
- result: { updatedCount: count }
2578
- };
2579
- }
2580
- function exportToSvg(ctx, params) {
2581
- if (params.slideIndices) {
2582
- for (const si of params.slideIndices) {
2583
- const err = validateSlideIndex(si, ctx.pptxData.slides.length);
2584
- if (err) {
2585
- throw new Error(err);
2586
- }
2587
- }
2588
- }
2589
- const options = {};
2590
- if (params.slideIndices) {
2591
- options.slideIndices = params.slideIndices;
2592
- }
2593
- if (params.includeHidden !== void 0) {
2594
- options.includeHidden = params.includeHidden;
2595
- }
2596
- if (params.defaultFontFamily) {
2597
- options.defaultFontFamily = params.defaultFontFamily;
2598
- }
2599
- if (params.defaultFontSize) {
2600
- options.defaultFontSize = params.defaultFontSize;
2601
- }
2602
- const svgStrings = SvgExporter.exportAll(ctx.pptxData, options);
2603
- return {
2604
- pptxData: ctx.pptxData,
2605
- dirty: false,
2606
- result: { svgStrings, slideCount: svgStrings.length }
2607
- };
2608
- }
2609
- function exportSlideSvg(ctx, params) {
2610
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2611
- if (err) {
2612
- throw new Error(err);
2613
- }
2614
- const slide = ctx.pptxData.slides[params.slideIndex];
2615
- const width = ctx.pptxData.width ?? 960;
2616
- const height = ctx.pptxData.height ?? 540;
2617
- const options = {};
2618
- if (params.defaultFontFamily) {
2619
- options.defaultFontFamily = params.defaultFontFamily;
2620
- }
2621
- if (params.defaultFontSize) {
2622
- options.defaultFontSize = params.defaultFontSize;
2623
- }
2624
- const svg = SvgExporter.exportSlide(slide, width, height, options);
2625
- return {
2626
- pptxData: ctx.pptxData,
2627
- dirty: false,
2628
- result: { svg, slideIndex: params.slideIndex }
2629
- };
2630
- }
2631
- function replaceGeometry(ctx, params) {
2632
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2633
- if (err) {
2634
- throw new Error(err);
2635
- }
2636
- const slide = ctx.pptxData.slides[params.slideIndex];
2637
- const el = slide.elements.find((e) => e.id === params.elementId);
2638
- if (!el) {
2639
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2640
- }
2641
- if (el.type !== "shape") {
2642
- throw new Error(`Element '${params.elementId}' is not a shape.`);
2643
- }
2644
- const shape = el;
2645
- if (params.svgPath) {
2646
- replaceWithCustomGeometry(shape, params.svgPath, params.pathWidth, params.pathHeight);
2647
- return {
2648
- pptxData: ctx.pptxData,
2649
- dirty: true,
2650
- result: { elementId: params.elementId, geometryType: "custom" }
2651
- };
2652
- }
2653
- if (params.shapeType) {
2654
- replaceShapeGeometry(shape, params.shapeType, params.adjustments);
2655
- return {
2656
- pptxData: ctx.pptxData,
2657
- dirty: true,
2658
- result: { elementId: params.elementId, geometryType: params.shapeType }
2659
- };
2660
- }
2661
- throw new Error("Either shapeType or svgPath must be provided.");
2662
- }
2663
-
2664
- // src/tools/hyperlink-tools.ts
2665
- function manageHyperlinks(ctx, params) {
2666
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2667
- if (err) {
2668
- throw new Error(err);
2669
- }
2670
- const slide = ctx.pptxData.slides[params.slideIndex];
2671
- switch (params.action) {
2672
- case "list": {
2673
- const hyperlinks = [];
2674
- for (const el of slide.elements) {
2675
- if (el.actionClick) {
2676
- hyperlinks.push({
2677
- elementId: el.id,
2678
- trigger: "click",
2679
- url: el.actionClick.url,
2680
- tooltip: el.actionClick.tooltip,
2681
- targetSlideIndex: el.actionClick.targetSlideIndex,
2682
- actionType: el.actionClick.action
2683
- });
2684
- }
2685
- if (el.actionHover) {
2686
- hyperlinks.push({
2687
- elementId: el.id,
2688
- trigger: "hover",
2689
- url: el.actionHover.url,
2690
- tooltip: el.actionHover.tooltip,
2691
- targetSlideIndex: el.actionHover.targetSlideIndex,
2692
- actionType: el.actionHover.action
2693
- });
2694
- }
2695
- }
2696
- return { pptxData: ctx.pptxData, dirty: false, result: { action: "list", hyperlinks } };
2697
- }
2698
- case "set": {
2699
- if (!params.elementId) {
2700
- throw new Error("elementId is required for set action.");
2701
- }
2702
- const el = slide.elements.find((e) => e.id === params.elementId);
2703
- if (!el) {
2704
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2705
- }
2706
- const trigger = params.trigger ?? "click";
2707
- const action = {};
2708
- if (params.url) {
2709
- action.url = params.url;
2710
- }
2711
- if (params.tooltip) {
2712
- action.tooltip = params.tooltip;
2713
- }
2714
- if (params.targetSlideIndex !== void 0) {
2715
- action.targetSlideIndex = params.targetSlideIndex;
2716
- }
2717
- if (params.actionType) {
2718
- switch (params.actionType) {
2719
- case "url":
2720
- action.action = "ppaction://hlinkpres";
2721
- break;
2722
- case "slide":
2723
- action.action = "ppaction://hlinksldjump";
2724
- break;
2725
- case "nextSlide":
2726
- action.action = "ppaction://hlinkshowjump?jump=nextslide";
2727
- break;
2728
- case "prevSlide":
2729
- action.action = "ppaction://hlinkshowjump?jump=previousslide";
2730
- break;
2731
- case "firstSlide":
2732
- action.action = "ppaction://hlinkshowjump?jump=firstslide";
2733
- break;
2734
- case "lastSlide":
2735
- action.action = "ppaction://hlinkshowjump?jump=lastslide";
2736
- break;
2737
- case "endShow":
2738
- action.action = "ppaction://hlinkshowjump?jump=endshow";
2739
- break;
2740
- }
2741
- }
2742
- if (trigger === "click") {
2743
- el.actionClick = action;
2744
- } else {
2745
- el.actionHover = action;
2746
- }
2747
- return {
2748
- pptxData: ctx.pptxData,
2749
- dirty: true,
2750
- result: { action: "set", elementId: params.elementId }
2751
- };
2752
- }
2753
- case "remove": {
2754
- if (!params.elementId) {
2755
- throw new Error("elementId is required for remove action.");
2756
- }
2757
- const el = slide.elements.find((e) => e.id === params.elementId);
2758
- if (!el) {
2759
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2760
- }
2761
- const trigger = params.trigger ?? "click";
2762
- if (trigger === "click") {
2763
- el.actionClick = void 0;
2764
- } else {
2765
- el.actionHover = void 0;
2766
- }
2767
- return {
2768
- pptxData: ctx.pptxData,
2769
- dirty: true,
2770
- result: { action: "remove", elementId: params.elementId }
2771
- };
2772
- }
2773
- default:
2774
- throw new Error(`Unknown hyperlink action: ${String(params.action)}`);
2775
- }
2776
- }
2777
- function exportToJson(ctx, params) {
2778
- const document = buildPptxJsonDocument(ctx.pptxData, { generator: "pptx-viewer-mcp" });
2779
- const json = JSON.stringify(document, null, params.pretty === false ? void 0 : 2);
2780
- return {
2781
- pptxData: ctx.pptxData,
2782
- dirty: false,
2783
- result: {
2784
- json,
2785
- slideCount: document.slideCount,
2786
- byteSize: new TextEncoder().encode(json).byteLength,
2787
- assets: document.assets
2788
- }
2789
- };
2790
- }
2791
- function importFromJson(ctx, params) {
2792
- const imported = deserializePptxFromJson(params.json);
2793
- applyImportedPptxData(ctx.pptxData, imported);
2794
- return {
2795
- pptxData: ctx.pptxData,
2796
- dirty: true,
2797
- result: {
2798
- slideCount: ctx.pptxData.slides.length,
2799
- width: ctx.pptxData.width,
2800
- height: ctx.pptxData.height
2801
- }
2802
- };
2803
- }
2804
- function getLayouts(ctx) {
2805
- const layouts = [];
2806
- if (ctx.pptxData.slideMasters) {
2807
- for (const master of ctx.pptxData.slideMasters) {
2808
- if (master.layouts) {
2809
- for (const layout of master.layouts) {
2810
- layouts.push({
2811
- name: layout.name ?? "Unknown",
2812
- path: layout.path
2813
- });
2814
- }
2815
- }
2816
- }
2817
- }
2818
- if (ctx.pptxData.layoutOptions) {
2819
- for (const opt of ctx.pptxData.layoutOptions) {
2820
- if (!layouts.some((l) => l.path === opt.path)) {
2821
- layouts.push({ name: opt.name, path: opt.path, type: opt.type });
2822
- }
2823
- }
2824
- }
2825
- return {
2826
- pptxData: ctx.pptxData,
2827
- dirty: false,
2828
- result: { layouts, count: layouts.length }
2829
- };
2830
- }
2831
- function applyLayout(ctx, params) {
2832
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2833
- if (err) {
2834
- throw new Error(err);
2835
- }
2836
- let layout;
2837
- if (params.layoutName) {
2838
- layout = findLayoutByName(ctx.pptxData, params.layoutName);
2839
- } else if (params.layoutType) {
2840
- layout = findLayoutByType(ctx.pptxData, params.layoutType);
2841
- } else {
2842
- throw new Error("Either layoutName or layoutType must be provided.");
2843
- }
2844
- if (!layout) {
2845
- throw new Error(
2846
- `Layout not found: ${params.layoutName ?? params.layoutType}. Use get_layouts to see available layouts.`
2847
- );
2848
- }
2849
- const slide = ctx.pptxData.slides[params.slideIndex];
2850
- slide.layoutName = layout.name;
2851
- if (layout.path) {
2852
- slide.layoutPath = layout.path;
2853
- }
2854
- const master = ctx.pptxData.slideMasters?.find(
2855
- (candidate) => candidate.layouts?.some((definition2) => definition2.path === layout.path)
2856
- );
2857
- const definition = master?.layouts?.find((candidate) => candidate.path === layout.path);
2858
- for (const frame of definition?.placeholders ?? []) {
2859
- if (["dt", "ftr", "sldnum", "hdr"].includes(frame.type)) {
2860
- continue;
2861
- }
2862
- const ph = {
2863
- "@_type": frame.type === "ctrtitle" ? "ctrTitle" : frame.type,
2864
- ...frame.idx !== void 0 ? { "@_idx": frame.idx } : {}
2865
- };
2866
- if (slide.elements.some((element) => {
2867
- const nv = element.rawXml?.["p:nvSpPr"];
2868
- const nvPr = nv?.["p:nvPr"];
2869
- const existing = nvPr?.["p:ph"];
2870
- return element.placeholderType === frame.type && String(existing?.["@_idx"] ?? "0") === String(frame.idx ?? "0");
2871
- })) {
2872
- continue;
2873
- }
2874
- const inherited = master?.placeholders?.find(
2875
- (candidate) => candidate.type === frame.type && (candidate.idx ?? "0") === (frame.idx ?? "0")
2876
- ) ?? master?.placeholders?.find((candidate) => candidate.type === frame.type);
2877
- slide.elements.push({
2878
- id: generateElementId(),
2879
- type: "text",
2880
- placeholderType: frame.type,
2881
- x: frame.x ?? inherited?.x ?? 0,
2882
- y: frame.y ?? inherited?.y ?? 0,
2883
- width: frame.width ?? inherited?.width ?? ctx.pptxData.width,
2884
- height: frame.height ?? inherited?.height ?? ctx.pptxData.height,
2885
- text: "",
2886
- textSegments: [{ text: "", style: {} }],
2887
- rawXml: {
2888
- "p:nvSpPr": {
2889
- "p:cNvPr": { "@_name": frame.type },
2890
- "p:cNvSpPr": {},
2891
- "p:nvPr": { "p:ph": ph }
2892
- },
2893
- "p:spPr": {}
2894
- }
2895
- });
2896
- }
2897
- return {
2898
- pptxData: ctx.pptxData,
2899
- dirty: true,
2900
- result: { slideIndex: params.slideIndex, layoutName: layout.name ?? "Unknown" }
2901
- };
2902
- }
2903
- function setElementLockT(ctx, params) {
2904
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
2905
- if (err) {
2906
- throw new Error(err);
2907
- }
2908
- const slide = ctx.pptxData.slides[params.slideIndex];
2909
- const idx = slide.elements.findIndex((e) => e.id === params.elementId);
2910
- if (idx < 0) {
2911
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
2912
- }
2913
- const extraLocks = {};
2914
- if (params.noMove !== void 0) {
2915
- extraLocks["noMove"] = params.noMove;
2916
- }
2917
- if (params.noResize !== void 0) {
2918
- extraLocks["noResize"] = params.noResize;
2919
- }
2920
- if (params.noRotation !== void 0) {
2921
- extraLocks["noRotation"] = params.noRotation;
2922
- }
2923
- if (params.noSelect !== void 0) {
2924
- extraLocks["noSelect"] = params.noSelect;
2925
- }
2926
- if (params.noTextEdit !== void 0) {
2927
- extraLocks["noTextEdit"] = params.noTextEdit;
2928
- }
2929
- const updated = setElementLocked(slide.elements[idx], params.locked, extraLocks);
2930
- slide.elements[idx] = updated;
2931
- return {
2932
- pptxData: ctx.pptxData,
2933
- dirty: true,
2934
- result: { elementId: params.elementId, locked: params.locked }
2935
- };
2936
- }
2937
-
2938
- // src/tools/metadata-tools.ts
2939
- function getMetadata(ctx) {
2940
- return {
2941
- pptxData: ctx.pptxData,
2942
- dirty: false,
2943
- result: {
2944
- coreProperties: ctx.pptxData.coreProperties,
2945
- appProperties: ctx.pptxData.appProperties,
2946
- customProperties: ctx.pptxData.customProperties,
2947
- slideCount: ctx.pptxData.slides.length
2948
- }
2949
- };
2950
- }
2951
- function updateMetadata(ctx, params) {
2952
- const updated = [];
2953
- 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) {
2954
- if (!ctx.pptxData.coreProperties) {
2955
- ctx.pptxData.coreProperties = {};
2956
- }
2957
- const cp = ctx.pptxData.coreProperties;
2958
- if (params.title !== void 0) {
2959
- cp.title = params.title;
2960
- updated.push("title");
2961
- }
2962
- if (params.subject !== void 0) {
2963
- cp.subject = params.subject;
2964
- updated.push("subject");
2965
- }
2966
- if (params.creator !== void 0) {
2967
- cp.creator = params.creator;
2968
- updated.push("creator");
2969
- }
2970
- if (params.keywords !== void 0) {
2971
- cp.keywords = params.keywords;
2972
- updated.push("keywords");
2973
- }
2974
- if (params.description !== void 0) {
2975
- cp.description = params.description;
2976
- updated.push("description");
2977
- }
2978
- if (params.lastModifiedBy !== void 0) {
2979
- cp.lastModifiedBy = params.lastModifiedBy;
2980
- updated.push("lastModifiedBy");
2981
- }
2982
- if (params.category !== void 0) {
2983
- cp.category = params.category;
2984
- updated.push("category");
2985
- }
2986
- }
2987
- if (params.company !== void 0 || params.manager !== void 0) {
2988
- if (!ctx.pptxData.appProperties) {
2989
- ctx.pptxData.appProperties = {};
2990
- }
2991
- const ap = ctx.pptxData.appProperties;
2992
- if (params.company !== void 0) {
2993
- ap.company = params.company;
2994
- updated.push("company");
2995
- }
2996
- if (params.manager !== void 0) {
2997
- ap.manager = params.manager;
2998
- updated.push("manager");
2999
- }
3000
- }
3001
- if (params.customProperties) {
3002
- if (!ctx.pptxData.customProperties) {
3003
- ctx.pptxData.customProperties = [];
3004
- }
3005
- for (const cp of params.customProperties) {
3006
- const valueStr = String(cp.value);
3007
- const vtType = typeof cp.value === "number" ? "i4" : typeof cp.value === "boolean" ? "bool" : "lpwstr";
3008
- const existing = ctx.pptxData.customProperties.find((p) => p.name === cp.name);
3009
- if (existing) {
3010
- existing.value = valueStr;
3011
- existing.type = vtType;
3012
- } else {
3013
- ctx.pptxData.customProperties.push({ name: cp.name, value: valueStr, type: vtType });
3014
- }
3015
- updated.push(`custom:${cp.name}`);
3016
- }
3017
- }
3018
- return {
3019
- pptxData: ctx.pptxData,
3020
- dirty: updated.length > 0,
3021
- result: { updated }
3022
- };
3023
- }
3024
- function resolveOleElement(ctx, slideIndex, elementId) {
3025
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
3026
- if (err) {
3027
- throw new Error(err);
3028
- }
3029
- const slide = ctx.pptxData.slides[slideIndex];
3030
- const index = slide.elements.findIndex((e) => e.id === elementId);
3031
- if (index === -1) {
3032
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
3033
- }
3034
- const element = slide.elements[index];
3035
- if (element.type !== "ole") {
3036
- throw new Error(`Element '${elementId}' is not an OLE object.`);
3037
- }
3038
- return { slide, element, index };
3039
- }
3040
- function commitOleElement(slide, index, updated) {
3041
- slide.elements.splice(index, 1, updated);
3042
- slide.isDirty = true;
3043
- }
3044
- async function getOleContent(ctx, params) {
3045
- const { element } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3046
- const kind = await resolveOleEditorKindFromPayload(element);
3047
- const base = { pptxData: ctx.pptxData, dirty: false };
3048
- switch (kind) {
3049
- case "sheet-xlsx":
3050
- case "sheet-xls": {
3051
- const sheet = await getOleSheetGrid(element);
3052
- return { ...base, result: { elementId: element.id, kind, editable: true, sheet } };
3053
- }
3054
- case "document-docx":
3055
- case "document-doc": {
3056
- const paragraphs = await getOleDocumentParagraphs(element);
3057
- return { ...base, result: { elementId: element.id, kind, editable: true, paragraphs } };
3058
- }
3059
- case "deck-pptx": {
3060
- const deckSlides = await getOleNestedDeckDetail(element);
3061
- return { ...base, result: { elementId: element.id, kind, editable: true, deckSlides } };
3062
- }
3063
- default:
3064
- return {
3065
- ...base,
3066
- result: {
3067
- elementId: element.id,
3068
- kind,
3069
- editable: false,
3070
- message: `This OLE object's content (${kind}) is not directly editable; use ole_replace_file to replace it wholesale.`
3071
- }
3072
- };
3073
- }
3074
- }
3075
- async function setOleSheetCell(ctx, params) {
3076
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3077
- const updated = await applyOleSheetCellEdit(element, {
3078
- row: params.row,
3079
- col: params.col,
3080
- value: params.value
3081
- });
3082
- const changed = updated !== element;
3083
- if (changed) {
3084
- commitOleElement(slide, index, updated);
3085
- }
3086
- return {
3087
- pptxData: ctx.pptxData,
3088
- dirty: changed,
3089
- result: { elementId: params.elementId, changed }
3090
- };
3091
- }
3092
- async function setOleDocumentParagraph(ctx, params) {
3093
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3094
- const updated = await applyOleDocumentParagraphEdit(element, params.paragraphIndex, params.text);
3095
- const changed = updated !== element;
3096
- if (changed) {
3097
- commitOleElement(slide, index, updated);
3098
- }
3099
- return {
3100
- pptxData: ctx.pptxData,
3101
- dirty: changed,
3102
- result: { elementId: params.elementId, changed }
3103
- };
3104
- }
3105
- async function setOleDeckSlideTitle(ctx, params) {
3106
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3107
- const updated = await applyOleNestedDeckElementTextEdit(
3108
- element,
3109
- params.deckSlideIndex,
3110
- params.deckElementId,
3111
- params.title
3112
- );
3113
- const changed = updated !== element;
3114
- if (changed) {
3115
- commitOleElement(slide, index, updated);
3116
- }
3117
- return {
3118
- pptxData: ctx.pptxData,
3119
- dirty: changed,
3120
- result: { elementId: params.elementId, changed }
3121
- };
3122
- }
3123
- async function setOleObjectNameT(ctx, params) {
3124
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3125
- const updated = await setOleObjectName(element, params.name);
3126
- commitOleElement(slide, index, updated);
3127
- return {
3128
- pptxData: ctx.pptxData,
3129
- dirty: true,
3130
- result: { elementId: params.elementId, changed: true }
3131
- };
3132
- }
3133
- async function replaceOleFileT(ctx, params) {
3134
- const { slide, element, index } = resolveOleElement(ctx, params.slideIndex, params.elementId);
3135
- const parsed = parseDataUrlToBytes(params.fileData);
3136
- if (!parsed) {
3137
- throw new Error('fileData must be a base64 data URL, e.g. "data:<mime-type>;base64,<...>".');
3138
- }
3139
- const updated = await replaceOleFile(element, parsed.bytes, params.fileName);
3140
- commitOleElement(slide, index, updated);
3141
- return {
3142
- pptxData: ctx.pptxData,
3143
- dirty: true,
3144
- result: { elementId: params.elementId, changed: true }
3145
- };
3146
- }
3147
-
3148
- // src/tools/presentation-tools.ts
3149
- function getPresentationProperties(ctx) {
3150
- return {
3151
- pptxData: ctx.pptxData,
3152
- dirty: false,
3153
- result: {
3154
- properties: ctx.pptxData.presentationProperties,
3155
- slideCount: ctx.pptxData.slides.length,
3156
- width: ctx.pptxData.width,
3157
- height: ctx.pptxData.height
3158
- }
3159
- };
3160
- }
3161
- function updatePresentationProperties(ctx, params) {
3162
- if (!ctx.pptxData.presentationProperties) {
3163
- ctx.pptxData.presentationProperties = {};
3164
- }
3165
- const pp = ctx.pptxData.presentationProperties;
3166
- if (params.showType !== void 0) {
3167
- pp.showType = params.showType;
3168
- }
3169
- if (params.loopContinuously !== void 0) {
3170
- pp.loopContinuously = params.loopContinuously;
3171
- }
3172
- if (params.showWithNarration !== void 0) {
3173
- pp.showWithNarration = params.showWithNarration;
3174
- }
3175
- if (params.showWithAnimation !== void 0) {
3176
- pp.showWithAnimation = params.showWithAnimation;
3177
- }
3178
- if (params.advanceMode !== void 0) {
3179
- pp.advanceMode = params.advanceMode;
3180
- }
3181
- if (params.showSlidesMode !== void 0) {
3182
- pp.showSlidesMode = params.showSlidesMode;
3183
- }
3184
- if (params.showSlidesFrom !== void 0) {
3185
- pp.showSlidesFrom = params.showSlidesFrom;
3186
- }
3187
- if (params.showSlidesTo !== void 0) {
3188
- pp.showSlidesTo = params.showSlidesTo;
3189
- }
3190
- if (params.penColor !== void 0) {
3191
- pp.penColor = params.penColor;
3192
- }
3193
- return {
3194
- pptxData: ctx.pptxData,
3195
- dirty: true,
3196
- result: { properties: pp }
3197
- };
3198
- }
3199
- function manageSections(ctx, params) {
3200
- switch (params.action) {
3201
- case "list": {
3202
- const sections = (ctx.pptxData.sections ?? []).map((s) => ({
3203
- id: s.id,
3204
- name: s.name,
3205
- slideIds: s.slideIds,
3206
- collapsed: s.collapsed,
3207
- color: s.color
3208
- }));
3209
- return { pptxData: ctx.pptxData, dirty: false, result: { action: "list", sections } };
3210
- }
3211
- case "add": {
3212
- if (!params.name) {
3213
- throw new Error("name is required for add action.");
3214
- }
3215
- const section = addSection(ctx.pptxData, params.name, params.slideIndices ?? []);
3216
- return {
3217
- pptxData: ctx.pptxData,
3218
- dirty: true,
3219
- result: {
3220
- action: "add",
3221
- section: { id: section.id, name: section.name, slideIds: section.slideIds }
3222
- }
3223
- };
3224
- }
3225
- case "remove": {
3226
- if (!params.sectionId) {
3227
- throw new Error("sectionId is required for remove action.");
3228
- }
3229
- const success = removeSection(ctx.pptxData, params.sectionId);
3230
- return { pptxData: ctx.pptxData, dirty: success, result: { action: "remove", success } };
3231
- }
3232
- case "reorder": {
3233
- if (!params.sectionIds) {
3234
- throw new Error("sectionIds is required for reorder action.");
3235
- }
3236
- reorderSections(ctx.pptxData, params.sectionIds);
3237
- return { pptxData: ctx.pptxData, dirty: true, result: { action: "reorder", success: true } };
3238
- }
3239
- case "moveSlides": {
3240
- if (!params.sectionId) {
3241
- throw new Error("sectionId is required for moveSlides action.");
3242
- }
3243
- if (!params.slideIndices) {
3244
- throw new Error("slideIndices is required for moveSlides action.");
3245
- }
3246
- const success = moveSlidesToSection(ctx.pptxData, params.slideIndices, params.sectionId);
3247
- return { pptxData: ctx.pptxData, dirty: success, result: { action: "moveSlides", success } };
3248
- }
3249
- case "getForSlide": {
3250
- if (params.slideIndex === void 0) {
3251
- throw new Error("slideIndex is required for getForSlide action.");
3252
- }
3253
- const section = getSectionForSlide(ctx.pptxData, params.slideIndex);
3254
- const info = section ? { id: section.id, name: section.name, slideIds: section.slideIds } : null;
3255
- return {
3256
- pptxData: ctx.pptxData,
3257
- dirty: false,
3258
- result: { action: "getForSlide", section: info }
3259
- };
3260
- }
3261
- default:
3262
- throw new Error(`Unknown section action: ${String(params.action)}`);
3263
- }
3264
- }
3265
-
3266
- // src/tools/slide-tools.ts
3267
- function getSlide(ctx, params) {
3268
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3269
- if (err) {
3270
- throw new Error(err);
3271
- }
3272
- const slide = ctx.pptxData.slides[params.slideIndex];
3273
- return {
3274
- pptxData: ctx.pptxData,
3275
- dirty: false,
3276
- result: {
3277
- slideIndex: params.slideIndex,
3278
- slideNumber: slide.slideNumber,
3279
- hidden: slide.hidden ?? false,
3280
- layoutName: slide.layoutName,
3281
- backgroundColor: slide.backgroundColor,
3282
- notes: slide.notes ?? "",
3283
- commentCount: slide.comments?.length ?? 0,
3284
- elements: slide.elements.map(describeElement)
3285
- }
3286
- };
3287
- }
3288
- function addSlide(ctx, params) {
3289
- const { slides } = ctx.pptxData;
3290
- const insertAt = params.insertAfterIndex !== void 0 && params.insertAfterIndex >= 0 ? Math.min(params.insertAfterIndex + 1, slides.length) : slides.length;
3291
- const newSlide = {
3292
- id: generateSlideId(),
3293
- rId: "",
3294
- slideNumber: insertAt + 1,
3295
- elements: [],
3296
- backgroundColor: params.backgroundColor
3297
- };
3298
- slides.splice(insertAt, 0, newSlide);
3299
- slides.forEach((s, i) => {
3300
- s.slideNumber = i + 1;
3301
- });
3302
- return {
3303
- pptxData: ctx.pptxData,
3304
- dirty: true,
3305
- result: { newSlideIndex: insertAt, slideCount: slides.length }
3306
- };
3307
- }
3308
- function deleteSlides(ctx, params) {
3309
- const { slides } = ctx.pptxData;
3310
- if (!params.slideIndexes || params.slideIndexes.length === 0) {
3311
- throw new Error("slideIndexes is required for the delete action.");
3312
- }
3313
- const indexSet = new Set(params.slideIndexes);
3314
- const invalid = params.slideIndexes.filter((i) => i < 0 || i >= slides.length);
3315
- if (invalid.length > 0) {
3316
- throw new Error(
3317
- `Invalid slide indexes: ${invalid.join(", ")}. Valid range: 0\u2013${slides.length - 1}.`
3318
- );
3319
- }
3320
- if (indexSet.size >= slides.length) {
3321
- throw new Error("Cannot delete all slides. At least one slide must remain.");
3322
- }
3323
- ctx.pptxData.slides = slides.filter((_, i) => !indexSet.has(i));
3324
- ctx.pptxData.slides.forEach((s, i) => {
3325
- s.slideNumber = i + 1;
3326
- });
3327
- return {
3328
- pptxData: ctx.pptxData,
3329
- dirty: true,
3330
- result: { deletedCount: indexSet.size, slideCount: ctx.pptxData.slides.length }
3331
- };
3332
- }
3333
- function reorderSlides(ctx, params) {
3334
- const { slides } = ctx.pptxData;
3335
- if (!params.newOrder || params.newOrder.length !== slides.length) {
3336
- throw new Error(
3337
- `newOrder must have exactly ${slides.length} indexes. Got ${params.newOrder?.length ?? 0}.`
3338
- );
3339
- }
3340
- const sorted = [...params.newOrder].sort((a, b) => a - b);
3341
- const expected = slides.map((_, i) => i);
3342
- if (JSON.stringify(sorted) !== JSON.stringify(expected)) {
3343
- throw new Error(
3344
- `newOrder must contain each index from 0 to ${slides.length - 1} exactly once.`
3345
- );
3346
- }
3347
- ctx.pptxData.slides = params.newOrder.map((idx) => slides[idx]);
3348
- ctx.pptxData.slides.forEach((s, i) => {
3349
- s.slideNumber = i + 1;
3350
- });
3351
- return {
3352
- pptxData: ctx.pptxData,
3353
- dirty: true,
3354
- result: { slideCount: ctx.pptxData.slides.length }
3355
- };
3356
- }
3357
- function duplicateSlide(ctx, params) {
3358
- const { slides } = ctx.pptxData;
3359
- const err = validateSlideIndex(params.slideIndex, slides.length);
3360
- if (err) {
3361
- throw new Error(err);
3362
- }
3363
- const original = slides[params.slideIndex];
3364
- const clone = structuredClone(original);
3365
- clone.id = generateSlideId();
3366
- clone.rId = "";
3367
- for (const el of clone.elements) {
3368
- el.id = generateElementId();
3369
- }
3370
- const insertIndex = params.targetIndex ?? params.slideIndex + 1;
3371
- slides.splice(insertIndex, 0, clone);
3372
- slides.forEach((s, i) => {
3373
- s.slideNumber = i + 1;
3374
- });
3375
- return {
3376
- pptxData: ctx.pptxData,
3377
- dirty: true,
3378
- result: { newSlideIndex: insertIndex, slideCount: slides.length }
3379
- };
3380
- }
3381
- function updateSlideProperties(ctx, params) {
3382
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3383
- if (err) {
3384
- throw new Error(err);
3385
- }
3386
- const slide = ctx.pptxData.slides[params.slideIndex];
3387
- if (params.backgroundColor !== void 0) {
3388
- slide.backgroundColor = params.backgroundColor;
3389
- }
3390
- if (params.backgroundGradient !== void 0) {
3391
- slide.backgroundGradient = params.backgroundGradient;
3392
- }
3393
- if (params.backgroundImage !== void 0) {
3394
- slide.backgroundImage = params.backgroundImage;
3395
- }
3396
- if (params.notes !== void 0) {
3397
- slide.notes = params.notes;
3398
- }
3399
- if (params.hidden !== void 0) {
3400
- slide.hidden = params.hidden;
3401
- }
3402
- return {
3403
- pptxData: ctx.pptxData,
3404
- dirty: true,
3405
- result: { slideIndex: params.slideIndex }
3406
- };
3407
- }
3408
- function setSlideTransition(ctx, params) {
3409
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3410
- if (err) {
3411
- throw new Error(err);
3412
- }
3413
- const slide = ctx.pptxData.slides[params.slideIndex];
3414
- if (params.type === "none") {
3415
- slide.transition = void 0;
3416
- } else {
3417
- const transition = {
3418
- type: params.type
3419
- };
3420
- if (params.durationMs !== void 0) {
3421
- transition.durationMs = params.durationMs;
3422
- }
3423
- if (params.direction !== void 0) {
3424
- transition.direction = params.direction;
3425
- }
3426
- if (params.advanceOnClick !== void 0) {
3427
- transition.advanceOnClick = params.advanceOnClick;
3428
- }
3429
- if (params.advanceAfterMs !== void 0) {
3430
- transition.advanceAfterMs = params.advanceAfterMs;
3431
- }
3432
- slide.transition = transition;
3433
- }
3434
- return {
3435
- pptxData: ctx.pptxData,
3436
- dirty: true,
3437
- result: { slideIndex: params.slideIndex, transitionType: params.type }
3438
- };
3439
- }
3440
- function setCanvasSize(ctx, params) {
3441
- ctx.pptxData.width = params.width;
3442
- ctx.pptxData.height = params.height;
3443
- if (ctx.pptxData.widthEmu !== void 0) {
3444
- ctx.pptxData.widthEmu = Math.round(params.width * 12700);
3445
- }
3446
- if (ctx.pptxData.heightEmu !== void 0) {
3447
- ctx.pptxData.heightEmu = Math.round(params.height * 12700);
3448
- }
3449
- return {
3450
- pptxData: ctx.pptxData,
3451
- dirty: true,
3452
- result: { canvasWidth: params.width, canvasHeight: params.height }
3453
- };
3454
- }
3455
- function extractNodes(data) {
3456
- if (!data.nodes) {
3457
- return [];
3458
- }
3459
- return data.nodes.map((n) => ({
3460
- id: n.id,
3461
- text: n.text ?? "",
3462
- children: n.children?.map((c) => ({ id: c.id, text: c.text ?? "" }))
3463
- }));
3464
- }
3465
- function manageSmartArt(ctx, params) {
3466
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3467
- if (err) {
3468
- throw new Error(err);
3469
- }
3470
- const slide = ctx.pptxData.slides[params.slideIndex];
3471
- const el = slide.elements.find((e) => e.id === params.elementId);
3472
- if (!el) {
3473
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
3474
- }
3475
- if (el.type !== "smartArt") {
3476
- throw new Error(`Element '${params.elementId}' is not a SmartArt.`);
3477
- }
3478
- const smartArt = el;
3479
- if (!smartArt.smartArtData) {
3480
- throw new Error(`SmartArt '${params.elementId}' has no data.`);
3481
- }
3482
- switch (params.action) {
3483
- case "getNodes": {
3484
- return {
3485
- pptxData: ctx.pptxData,
3486
- dirty: false,
3487
- result: {
3488
- action: "getNodes",
3489
- elementId: params.elementId,
3490
- nodes: extractNodes(smartArt.smartArtData)
3491
- }
3492
- };
3493
- }
3494
- case "addNode": {
3495
- smartArt.smartArtData = addSmartArtNode(
3496
- smartArt.smartArtData,
3497
- params.text ?? "",
3498
- params.afterNodeId
3499
- );
3500
- return {
3501
- pptxData: ctx.pptxData,
3502
- dirty: true,
3503
- result: {
3504
- action: "addNode",
3505
- elementId: params.elementId,
3506
- nodes: extractNodes(smartArt.smartArtData)
3507
- }
3508
- };
3509
- }
3510
- case "removeNode": {
3511
- if (!params.nodeId) {
3512
- throw new Error("nodeId is required for removeNode.");
3513
- }
3514
- smartArt.smartArtData = removeSmartArtNode(smartArt.smartArtData, params.nodeId);
3515
- return {
3516
- pptxData: ctx.pptxData,
3517
- dirty: true,
3518
- result: {
3519
- action: "removeNode",
3520
- elementId: params.elementId,
3521
- nodes: extractNodes(smartArt.smartArtData)
3522
- }
3523
- };
3524
- }
3525
- case "updateNodeText": {
3526
- if (!params.nodeId) {
3527
- throw new Error("nodeId is required for updateNodeText.");
3528
- }
3529
- smartArt.smartArtData = updateSmartArtNodeText(
3530
- smartArt.smartArtData,
3531
- params.nodeId,
3532
- params.text ?? ""
3533
- );
3534
- return {
3535
- pptxData: ctx.pptxData,
3536
- dirty: true,
3537
- result: { action: "updateNodeText", elementId: params.elementId }
3538
- };
3539
- }
3540
- case "reorderNode": {
3541
- if (!params.nodeId) {
3542
- throw new Error("nodeId is required for reorderNode.");
3543
- }
3544
- const dir = params.direction ?? 1;
3545
- smartArt.smartArtData = reorderSmartArtNode(smartArt.smartArtData, params.nodeId, dir);
3546
- return {
3547
- pptxData: ctx.pptxData,
3548
- dirty: true,
3549
- result: {
3550
- action: "reorderNode",
3551
- elementId: params.elementId,
3552
- nodes: extractNodes(smartArt.smartArtData)
3553
- }
3554
- };
3555
- }
3556
- case "promoteNode": {
3557
- if (!params.nodeId) {
3558
- throw new Error("nodeId is required for promoteNode.");
3559
- }
3560
- smartArt.smartArtData = promoteSmartArtNode(smartArt.smartArtData, params.nodeId);
3561
- return {
3562
- pptxData: ctx.pptxData,
3563
- dirty: true,
3564
- result: {
3565
- action: "promoteNode",
3566
- elementId: params.elementId,
3567
- nodes: extractNodes(smartArt.smartArtData)
3568
- }
3569
- };
3570
- }
3571
- case "demoteNode": {
3572
- if (!params.nodeId) {
3573
- throw new Error("nodeId is required for demoteNode.");
3574
- }
3575
- smartArt.smartArtData = demoteSmartArtNode(smartArt.smartArtData, params.nodeId);
3576
- return {
3577
- pptxData: ctx.pptxData,
3578
- dirty: true,
3579
- result: {
3580
- action: "demoteNode",
3581
- elementId: params.elementId,
3582
- nodes: extractNodes(smartArt.smartArtData)
3583
- }
3584
- };
3585
- }
3586
- case "decompose": {
3587
- const bounds = {
3588
- x: smartArt.x,
3589
- y: smartArt.y,
3590
- width: smartArt.width,
3591
- height: smartArt.height
3592
- };
3593
- const decomposed = decomposeSmartArt(smartArt.smartArtData, bounds);
3594
- if (!decomposed || decomposed.length === 0) {
3595
- throw new Error("Failed to decompose SmartArt into shapes.");
3596
- }
3597
- const idx = slide.elements.indexOf(el);
3598
- slide.elements.splice(idx, 1, ...decomposed);
3599
- return {
3600
- pptxData: ctx.pptxData,
3601
- dirty: true,
3602
- result: {
3603
- action: "decompose",
3604
- elementId: params.elementId,
3605
- decomposedCount: decomposed.length
3606
- }
3607
- };
3608
- }
3609
- default:
3610
- throw new Error(`Unknown SmartArt action: ${String(params.action)}`);
3611
- }
3612
- }
3613
- function findTableElement(ctx, slideIndex, elementId) {
3614
- const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
3615
- if (err) {
3616
- throw new Error(err);
3617
- }
3618
- const slide = ctx.pptxData.slides[slideIndex];
3619
- const el = slide.elements.find((e) => e.id === elementId);
3620
- if (!el) {
3621
- throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
3622
- }
3623
- if (el.type !== "table") {
3624
- throw new Error(`Element '${elementId}' is not a table.`);
3625
- }
3626
- return el;
3627
- }
3628
- function tableStyleIdsInUse(ctx) {
3629
- const used = /* @__PURE__ */ new Set();
3630
- for (const slide of ctx.pptxData.slides) {
3631
- for (const el of slide.elements) {
3632
- if (el.type === "table" && el.tableData?.tableStyleId) {
3633
- used.add(normalizeTableStyleGuid(el.tableData.tableStyleId));
3634
- }
3635
- }
3636
- }
3637
- return used;
3638
- }
3639
- function setTableStyleSection(ctx, params) {
3640
- if (!TABLE_STYLE_PART_SEQUENCE.includes(params.section)) {
3641
- throw new Error(
3642
- `Unknown table style section '${params.section}'. Valid sections: ${TABLE_STYLE_PART_SEQUENCE.join(", ")}.`
3643
- );
3644
- }
3645
- const map = ctx.pptxData.tableStyleMap ?? {};
3646
- ctx.pptxData.tableStyleMap = map;
3647
- const normalizedId = normalizeTableStyleGuid(params.styleId);
3648
- const entry = map[normalizedId];
3649
- if (!entry) {
3650
- throw new Error(
3651
- `Table style '${params.styleId}' is not loaded in this presentation. Use create_table_style to add a new one.`
3652
- );
3653
- }
3654
- if (params.styleName !== void 0) {
3655
- entry.styleName = params.styleName;
3656
- }
3657
- const mutableEntry = entry;
3658
- if (params.fill !== void 0) {
3659
- mutableEntry[`${params.section}Fill`] = params.fill;
3660
- }
3661
- if (params.text !== void 0) {
3662
- mutableEntry[`${params.section}Text`] = params.text;
3663
- }
3664
- if (params.borders !== void 0) {
3665
- mutableEntry[`${params.section}Borders`] = params.borders;
3666
- }
3667
- if (params.cell3D !== void 0) {
3668
- mutableEntry[`${params.section}Cell3D`] = params.cell3D;
3669
- }
3670
- return {
3671
- pptxData: ctx.pptxData,
3672
- dirty: true,
3673
- result: { styleId: normalizedId, section: params.section }
3674
- };
3675
- }
3676
- function createTableStyle(ctx, params) {
3677
- const map = ctx.pptxData.tableStyleMap ?? {};
3678
- ctx.pptxData.tableStyleMap = map;
3679
- const basedOn = params.basedOnStyleId ? map[normalizeTableStyleGuid(params.basedOnStyleId)] : void 0;
3680
- if (params.basedOnStyleId && !basedOn) {
3681
- throw new Error(`basedOnStyleId '${params.basedOnStyleId}' is not a loaded table style.`);
3682
- }
3683
- const entry = createTableStyleEntry(map, { styleName: params.styleName, basedOn });
3684
- addTableStyleToMap(map, entry);
3685
- return {
3686
- pptxData: ctx.pptxData,
3687
- dirty: true,
3688
- result: { styleId: entry.styleId },
3689
- ...params.setAsDefault ? { saveOptions: { tableStylesDefaultId: entry.styleId } } : {}
3690
- };
3691
- }
3692
- function deleteTableStyle(ctx, params) {
3693
- const normalizedId = normalizeTableStyleGuid(params.styleId);
3694
- if (!params.force) {
3695
- const used = tableStyleIdsInUse(ctx);
3696
- if (used.has(normalizedId)) {
3697
- throw new Error(
3698
- `Table style '${params.styleId}' is assigned to at least one table on this deck. Reassign those tables first, or pass force: true.`
3699
- );
3700
- }
3701
- }
3702
- const map = ctx.pptxData.tableStyleMap ?? {};
3703
- ctx.pptxData.tableStyleMap = map;
3704
- deleteTableStyleFromMap(map, normalizedId);
3705
- return {
3706
- pptxData: ctx.pptxData,
3707
- dirty: true,
3708
- result: { deleted: true },
3709
- saveOptions: { tableStylesToDelete: [normalizedId] }
3710
- };
3711
- }
3712
- function assignTableStyle(ctx, params) {
3713
- const el = findTableElement(ctx, params.slideIndex, params.elementId);
3714
- if (!el.tableData) {
3715
- throw new Error(`Table '${params.elementId}' has no tableData.`);
3716
- }
3717
- const styleId = params.styleId ?? ctx.pptxData.tableStylesDefaultId;
3718
- if (!styleId) {
3719
- throw new Error(
3720
- "styleId was not provided and this presentation has no tableStylesDefaultId to fall back to."
3721
- );
3722
- }
3723
- const normalizedId = normalizeTableStyleGuid(styleId);
3724
- el.tableData.tableStyleId = normalizedId;
3725
- if (params.bandedRows !== void 0) {
3726
- el.tableData.bandedRows = params.bandedRows;
3727
- }
3728
- if (params.bandedColumns !== void 0) {
3729
- el.tableData.bandedColumns = params.bandedColumns;
3730
- }
3731
- if (params.firstRowHeader !== void 0) {
3732
- el.tableData.firstRowHeader = params.firstRowHeader;
3733
- }
3734
- if (params.lastRow !== void 0) {
3735
- el.tableData.lastRow = params.lastRow;
3736
- }
3737
- if (params.firstCol !== void 0) {
3738
- el.tableData.firstCol = params.firstCol;
3739
- }
3740
- if (params.lastCol !== void 0) {
3741
- el.tableData.lastCol = params.lastCol;
3742
- }
3743
- return {
3744
- pptxData: ctx.pptxData,
3745
- dirty: true,
3746
- result: { elementId: params.elementId, styleId: normalizedId }
3747
- };
3748
- }
3749
- function updateTableCells(ctx, params) {
3750
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3751
- if (err) {
3752
- throw new Error(err);
3753
- }
3754
- const slide = ctx.pptxData.slides[params.slideIndex];
3755
- const el = slide.elements.find((e) => e.id === params.elementId);
3756
- if (!el) {
3757
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
3758
- }
3759
- if (el.type !== "table") {
3760
- throw new Error(`Element '${params.elementId}' is not a table.`);
3761
- }
3762
- const tbl = el;
3763
- if (!tbl.tableData) {
3764
- throw new Error(`Table '${params.elementId}' has no tableData.`);
3765
- }
3766
- let count = 0;
3767
- for (const cellUpdate of params.cells) {
3768
- const row = tbl.tableData.rows[cellUpdate.row];
3769
- if (!row) {
3770
- throw new Error(
3771
- `Row ${cellUpdate.row} out of range (table has ${tbl.tableData.rows.length} rows).`
3772
- );
3773
- }
3774
- const cell = row.cells[cellUpdate.col];
3775
- if (!cell) {
3776
- throw new Error(`Column ${cellUpdate.col} out of range (row has ${row.cells.length} cells).`);
3777
- }
3778
- cell.text = cellUpdate.text;
3779
- count++;
3780
- }
3781
- return {
3782
- pptxData: ctx.pptxData,
3783
- dirty: true,
3784
- result: { updatedCount: count }
3785
- };
3786
- }
3787
- function manageTableStructure(ctx, params) {
3788
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3789
- if (err) {
3790
- throw new Error(err);
3791
- }
3792
- const slide = ctx.pptxData.slides[params.slideIndex];
3793
- const el = slide.elements.find((e) => e.id === params.elementId);
3794
- if (!el) {
3795
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
3796
- }
3797
- if (el.type !== "table") {
3798
- throw new Error(`Element '${params.elementId}' is not a table.`);
3799
- }
3800
- const tbl = el;
3801
- if (!tbl.tableData) {
3802
- throw new Error(`Table '${params.elementId}' has no tableData.`);
3803
- }
3804
- const { rows, columnWidths } = tbl.tableData;
3805
- const source = { ...tbl, tableData: structuredClone(tbl.tableData) };
3806
- let edit;
3807
- const colCount = columnWidths.length;
3808
- const rowCount = rows.length;
3809
- switch (params.action) {
3810
- case "insertRow": {
3811
- const pos = params.position !== void 0 ? Math.min(Math.max(params.position, 0), rowCount) : rowCount;
3812
- const newRow = {
3813
- height: 40,
3814
- cells: Array.from({ length: colCount }, (_, c) => ({
3815
- text: params.cellTexts?.[c] ?? ""
3816
- }))
3817
- };
3818
- rows.splice(pos, 0, newRow);
3819
- edit = { axis: "row", action: "insert", index: pos };
3820
- break;
3821
- }
3822
- case "deleteRow": {
3823
- if (rowCount <= 1) {
3824
- throw new Error("Cannot delete the last row of a table.");
3825
- }
3826
- const ref = params.referenceIndex ?? rowCount - 1;
3827
- if (ref < 0 || ref >= rowCount) {
3828
- throw new Error(`Row index ${ref} out of range (0\u2013${rowCount - 1}).`);
3829
- }
3830
- rows.splice(ref, 1);
3831
- edit = { axis: "row", action: "delete", index: ref };
3832
- break;
3833
- }
3834
- case "insertColumn": {
3835
- const pos = params.position !== void 0 ? Math.min(Math.max(params.position, 0), colCount) : colCount;
3836
- const newWidth = 1 / (colCount + 1);
3837
- const scaleFactor = colCount / (colCount + 1);
3838
- for (let i = 0; i < columnWidths.length; i++) {
3839
- columnWidths[i] *= scaleFactor;
3840
- }
3841
- columnWidths.splice(pos, 0, newWidth);
3842
- edit = { axis: "column", action: "insert", index: pos };
3843
- for (let r = 0; r < rows.length; r++) {
3844
- rows[r].cells.splice(pos, 0, {
3845
- text: params.cellTexts?.[r] ?? ""
3846
- });
3847
- }
3848
- break;
3849
- }
3850
- case "deleteColumn": {
3851
- if (colCount <= 1) {
3852
- throw new Error("Cannot delete the last column of a table.");
3853
- }
3854
- const ref = params.referenceIndex ?? colCount - 1;
3855
- if (ref < 0 || ref >= colCount) {
3856
- throw new Error(`Column index ${ref} out of range (0\u2013${colCount - 1}).`);
3857
- }
3858
- const removedWidth = columnWidths[ref];
3859
- columnWidths.splice(ref, 1);
3860
- edit = { axis: "column", action: "delete", index: ref };
3861
- const remaining = columnWidths.length;
3862
- if (remaining > 0) {
3863
- const extra = removedWidth / remaining;
3864
- for (let i = 0; i < columnWidths.length; i++) {
3865
- columnWidths[i] += extra;
3866
- }
3867
- }
3868
- for (const row of rows) {
3869
- row.cells.splice(ref, 1);
3870
- }
3871
- break;
3872
- }
3873
- default: {
3874
- throw new Error(`Unknown action: ${String(params.action)}`);
3875
- }
3876
- }
3877
- const rawXml = rebuildTableStructureInRawXml(source, tbl.tableData, edit);
3878
- if (rawXml) {
3879
- tbl.rawXml = rawXml;
3880
- }
3881
- return {
3882
- pptxData: ctx.pptxData,
3883
- dirty: true,
3884
- result: {
3885
- rowCount: tbl.tableData.rows.length,
3886
- columnCount: tbl.tableData.columnWidths.length
3887
- }
3888
- };
3889
- }
3890
- function findPlaceholdersT(ctx) {
3891
- const placeholders = findPlaceholders(ctx.pptxData);
3892
- return {
3893
- pptxData: ctx.pptxData,
3894
- dirty: false,
3895
- result: { placeholders, count: placeholders.length }
3896
- };
3897
- }
3898
- function applyTemplateT(ctx, params) {
3899
- applyTemplate(ctx.pptxData, params.data);
3900
- return {
3901
- pptxData: ctx.pptxData,
3902
- dirty: true,
3903
- result: { applied: true, slideCount: ctx.pptxData.slides.length }
3904
- };
3905
- }
3906
- function getThemeInfo(ctx) {
3907
- const theme = ctx.pptxData.theme;
3908
- return {
3909
- pptxData: ctx.pptxData,
3910
- dirty: false,
3911
- result: {
3912
- themeName: theme?.name,
3913
- colorScheme: theme?.colorScheme,
3914
- fontScheme: theme?.fontScheme,
3915
- availablePresets: Object.keys(ThemePresets)
3916
- }
3917
- };
3918
- }
3919
- function applyThemePreset(ctx, params) {
3920
- const preset = getThemePreset(params.presetName);
3921
- if (!preset) {
3922
- throw new Error(
3923
- `Unknown theme preset: "${params.presetName}". Available: ${Object.keys(ThemePresets).join(", ")}`
3924
- );
3925
- }
3926
- if (!ctx.pptxData.theme) {
3927
- ctx.pptxData.theme = {};
3928
- }
3929
- const theme = ctx.pptxData.theme;
3930
- theme.name = preset.name;
3931
- theme.colorScheme = preset.colors;
3932
- if (preset.fonts) {
3933
- theme.fontScheme = preset.fonts;
3934
- }
3935
- return {
3936
- pptxData: ctx.pptxData,
3937
- dirty: true,
3938
- result: {
3939
- appliedPreset: params.presetName,
3940
- colorScheme: theme.colorScheme
3941
- }
3942
- };
3943
- }
3944
- function updateThemeColors(ctx, params) {
3945
- if (!ctx.pptxData.theme) {
3946
- ctx.pptxData.theme = {};
3947
- }
3948
- const theme = ctx.pptxData.theme;
3949
- if (!theme.colorScheme) {
3950
- theme.colorScheme = {
3951
- dk1: "#000000",
3952
- lt1: "#FFFFFF",
3953
- dk2: "#44546A",
3954
- lt2: "#E7E6E6",
3955
- accent1: "#4472C4",
3956
- accent2: "#ED7D31",
3957
- accent3: "#A5A5A5",
3958
- accent4: "#FFC000",
3959
- accent5: "#5B9BD5",
3960
- accent6: "#70AD47",
3961
- hlink: "#0563C1",
3962
- folHlink: "#954F72"
3963
- };
3964
- }
3965
- const cs = theme.colorScheme;
3966
- for (const key of Object.keys(params)) {
3967
- if (params[key] !== void 0) {
3968
- cs[key] = params[key];
3969
- }
3970
- }
3971
- return {
3972
- pptxData: ctx.pptxData,
3973
- dirty: true,
3974
- result: { colorScheme: cs }
3975
- };
3976
- }
3977
- function updateThemeFonts(ctx, params) {
3978
- if (!ctx.pptxData.theme) {
3979
- ctx.pptxData.theme = {};
3980
- }
3981
- const theme = ctx.pptxData.theme;
3982
- if (!theme.fontScheme) {
3983
- theme.fontScheme = {};
3984
- }
3985
- if (params.majorFont !== void 0) {
3986
- theme.fontScheme.majorFont = { latin: params.majorFont };
3987
- }
3988
- if (params.minorFont !== void 0) {
3989
- theme.fontScheme.minorFont = { latin: params.minorFont };
3990
- }
3991
- return {
3992
- pptxData: ctx.pptxData,
3993
- dirty: true,
3994
- result: { fontScheme: theme.fontScheme }
3995
- };
3996
- }
3997
- async function validatePresentation(rawBytes) {
3998
- const result = await validatePptx(rawBytes);
3999
- return {
4000
- valid: result.valid,
4001
- issueCount: result.issues.length,
4002
- errors: result.issues.filter((i) => i.severity === "error").length,
4003
- warnings: result.issues.filter((i) => i.severity === "warning").length,
4004
- issues: result.issues
4005
- };
4006
- }
4007
- async function repairPresentation(rawBytes) {
4008
- const result = await repairPptx(rawBytes);
4009
- return {
4010
- result: {
4011
- repairCount: result.repairs.length,
4012
- repairs: result.repairs
4013
- },
4014
- repairedBytes: result.repaired
4015
- };
4016
- }
4017
- var PPTX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
4018
- async function loadPresentation(filePath, execCtx) {
4019
- const rawBytes = await execCtx.filesystem.readFile(filePath);
4020
- if (execCtx.collaboration) {
4021
- const room = execCtx.collaboration.getRoom(filePath);
4022
- if (room) {
4023
- const codec = execCtx.collaboration.getCodec(filePath);
4024
- if (codec) {
4025
- const currentBytes = await codec.dehydrate(room.ydoc);
4026
- const handler2 = new PptxHandler();
4027
- const pptxData2 = await handler2.load(currentBytes.buffer);
4028
- return { pptxData: pptxData2, rawBytes };
4029
- }
4030
- }
4031
- }
4032
- const handler = new PptxHandler();
4033
- const pptxData = await handler.load(rawBytes.buffer);
4034
- return { pptxData, rawBytes };
4035
- }
4036
- async function savePresentation(filePath, pptxData, rawBytes, execCtx, tableStyleSaveOptions) {
4037
- const handler = new PptxHandler();
4038
- await handler.load(rawBytes.buffer);
4039
- const outputBytes = await handler.save(pptxData.slides, {
4040
- headerFooter: pptxData.headerFooter,
4041
- // `pptxData.tableStyleMap` carries any in-memory table-style edits a
4042
- // tool made (e.g. `set_table_style_section`, `create_table_style`);
4043
- // without forwarding it here those edits were silently dropped on
4044
- // save even though the core save pipeline already supports them.
4045
- tableStyles: pptxData.tableStyleMap,
4046
- ...tableStyleSaveOptions
4047
- });
4048
- if (execCtx.collaboration) {
4049
- const room = execCtx.collaboration.getRoom(filePath);
4050
- if (room) {
4051
- const codec = execCtx.collaboration.getCodec(filePath);
4052
- if (codec) {
4053
- const origin = execCtx.collaboration.agentOrigin(execCtx.agentName ?? "pptx-tool");
4054
- await codec.hydrate(room.ydoc, outputBytes, origin);
4055
- if (execCtx.viewer) {
4056
- execCtx.viewer.replaceContent(filePath, outputBytes, {
4057
- markDirty: false,
4058
- mimeType: PPTX_MIME_TYPE
4059
- });
4060
- }
4061
- return { savedToDisk: false, routedThroughCollaboration: true };
4062
- }
4063
- }
4064
- }
4065
- await execCtx.filesystem.writeFile(filePath, outputBytes);
4066
- if (execCtx.viewer) {
4067
- execCtx.viewer.replaceContent(filePath, outputBytes, {
4068
- markDirty: false,
4069
- mimeType: PPTX_MIME_TYPE
4070
- });
4071
- }
4072
- return { savedToDisk: true, routedThroughCollaboration: false };
4073
- }
4074
- async function executeToolWithContext(filePath, execCtx, toolFn) {
4075
- const { pptxData, rawBytes } = await loadPresentation(filePath, execCtx);
4076
- const toolResult = await toolFn({ pptxData });
4077
- if (toolResult.dirty) {
4078
- const saveResult = await savePresentation(
4079
- filePath,
4080
- toolResult.pptxData,
4081
- rawBytes,
4082
- execCtx,
4083
- toolResult.saveOptions
4084
- );
4085
- return { ...toolResult.result, ...saveResult };
4086
- }
4087
- return toolResult.result;
4088
- }
932
+ var formatChartDataPoint = apiToolsChartFormattingTools.formatChartDataPoint;
933
+ var formatChartDataLabel = apiToolsChartFormattingTools.formatChartDataLabel;
934
+ var formatChartSeries = apiToolsChartFormattingTools.formatChartSeries;
935
+ var setChartHelperLineT = apiToolsChartFormattingTools.setChartHelperLineT;
936
+ var setChartColorMapOverrideT = apiToolsChartFormattingTools.setChartColorMapOverrideT;
937
+ var updateChart = apiToolsChartTools.updateChart;
938
+ var addChartSeriesT = apiToolsChartTools.addChartSeriesT;
939
+ var removeChartSeriesT = apiToolsChartTools.removeChartSeriesT;
940
+ var updateChartSeriesData = apiToolsChartTools.updateChartSeriesData;
941
+ var createChart = apiToolsChartTools.createChart;
942
+ var listChartUserShapesT = apiToolsChartUserShapeTools.listChartUserShapesT;
943
+ var addChartUserShapeT = apiToolsChartUserShapeTools.addChartUserShapeT;
944
+ var updateChartUserShapeT = apiToolsChartUserShapeTools.updateChartUserShapeT;
945
+ var removeChartUserShapeT = apiToolsChartUserShapeTools.removeChartUserShapeT;
946
+ var findText = apiToolsContentTools.findText;
947
+ var replaceText = apiToolsContentTools.replaceText;
948
+ var manageComments = apiToolsContentTools.manageComments;
949
+ var convertToMarkdown = apiToolsConversionTools.convertToMarkdown;
950
+ var addElement = apiToolsElementTools.addElement;
951
+ var updateElement = apiToolsElementTools.updateElement;
952
+ var renameElement = apiToolsElementTools.renameElement;
953
+ var deleteElements = apiToolsElementTools.deleteElements;
954
+ var arrangeElements = apiToolsElementTools.arrangeElements;
955
+ var cloneElement = apiToolsElementTools.cloneElement;
956
+ var setElementAnimation = apiToolsElementTools.setElementAnimation;
957
+ var groupElements = apiToolsElementTools.groupElements;
958
+ var ungroupElements = apiToolsElementTools.ungroupElements;
959
+ var batchUpdateElements = apiToolsElementTools.batchUpdateElements;
960
+ var exportToSvg = apiToolsExportTools.exportToSvg;
961
+ var exportSlideSvg = apiToolsExportTools.exportSlideSvg;
962
+ var replaceGeometry = apiToolsGeometryTools.replaceGeometry;
963
+ var manageHyperlinks = apiToolsHyperlinkTools.manageHyperlinks;
964
+ var exportToJson = apiToolsJsonTools.exportToJson;
965
+ var importFromJson = apiToolsJsonTools.importFromJson;
966
+ var getLayouts = apiToolsLayoutTools.getLayouts;
967
+ var applyLayout = apiToolsLayoutTools.applyLayout;
968
+ var setElementLockT = apiToolsLockTools.setElementLockT;
969
+ var getMetadata = apiToolsMetadataTools.getMetadata;
970
+ var updateMetadata = apiToolsMetadataTools.updateMetadata;
971
+ var getOleContent = apiToolsOleTools.getOleContent;
972
+ var setOleSheetCell = apiToolsOleTools.setOleSheetCell;
973
+ var setOleDocumentParagraph = apiToolsOleTools.setOleDocumentParagraph;
974
+ var setOleDeckSlideTitle = apiToolsOleTools.setOleDeckSlideTitle;
975
+ var setOleObjectNameT = apiToolsOleTools.setOleObjectNameT;
976
+ var replaceOleFileT = apiToolsOleTools.replaceOleFileT;
977
+ var getPresentationProperties = apiToolsPresentationTools.getPresentationProperties;
978
+ var updatePresentationProperties = apiToolsPresentationTools.updatePresentationProperties;
979
+ var manageSections = apiToolsSectionTools.manageSections;
980
+ var getSlide = apiToolsSlideTools.getSlide;
981
+ var addSlide = apiToolsSlideTools.addSlide;
982
+ var deleteSlides = apiToolsSlideTools.deleteSlides;
983
+ var reorderSlides = apiToolsSlideTools.reorderSlides;
984
+ var duplicateSlide = apiToolsSlideTools.duplicateSlide;
985
+ var updateSlideProperties = apiToolsSlideTools.updateSlideProperties;
986
+ var setSlideTransition = apiToolsSlideTools.setSlideTransition;
987
+ var setCanvasSize = apiToolsSlideTools.setCanvasSize;
988
+ var manageSmartArt = apiToolsSmartartTools.manageSmartArt;
989
+ apiToolsStyleTools.ALT_TEXT_ELEMENT_TYPES;
990
+ apiToolsStyleTools.TITLE_ELEMENT_TYPES;
991
+ apiToolsStyleTools.applyElementAltText;
992
+ apiToolsStyleTools.applyElementTitle;
993
+ var updateElementStyle = apiToolsStyleTools.updateElementStyle;
994
+ var runAccessibilityCheck = apiToolsStyleTools.runAccessibilityCheck;
995
+ var setTableStyleSection = apiToolsTableStyleTools.setTableStyleSection;
996
+ var createTableStyle = apiToolsTableStyleTools.createTableStyle;
997
+ var deleteTableStyle = apiToolsTableStyleTools.deleteTableStyle;
998
+ var assignTableStyle = apiToolsTableStyleTools.assignTableStyle;
999
+ var updateTableCells = apiToolsTableTools.updateTableCells;
1000
+ var manageTableStructure = apiToolsTableTools.manageTableStructure;
1001
+ var findPlaceholdersT = apiToolsTemplateTools.findPlaceholdersT;
1002
+ var applyTemplateT = apiToolsTemplateTools.applyTemplateT;
1003
+ var getThemeInfo = apiToolsThemeTools.getThemeInfo;
1004
+ var applyThemePreset = apiToolsThemeTools.applyThemePreset;
1005
+ var updateThemeColors = apiToolsThemeTools.updateThemeColors;
1006
+ var updateThemeFonts = apiToolsThemeTools.updateThemeFonts;
1007
+ var validatePresentation = apiToolsValidationTools.validatePresentation;
1008
+ var repairPresentation = apiToolsValidationTools.repairPresentation;
1009
+ apiExecution.loadPresentation;
1010
+ apiExecution.savePresentation;
1011
+ var executeToolWithContext = apiExecution.executeToolWithContext;
4089
1012
 
4090
1013
  // src/mcp/handlers.ts
4091
1014
  var ALLOWED_EXTENSIONS = /* @__PURE__ */ new Set([".pptx", ".ppt"]);
@@ -4137,19 +1060,26 @@ async function runMcpTool(filePath, fn, options) {
4137
1060
  }
4138
1061
 
4139
1062
  // src/mcp/server.ts
4140
- function createServer() {
4141
- const server2 = new McpServer({
1063
+ function createServer(options) {
1064
+ const server = new McpServer({
4142
1065
  name: "pptx-viewer-tools",
4143
1066
  version: "1.0.0"
4144
1067
  });
4145
- server2.registerTool(
1068
+ registerTools(server, options);
1069
+ return server;
1070
+ }
1071
+ function registerTools(server, options) {
1072
+ const runMcpTool2 = (filePath, fn) => runMcpTool(filePath, fn, options);
1073
+ const resolveScopedDir2 = (dir) => resolveScopedDir(dir, options?.rootDir);
1074
+ const resolveScopedFilePath2 = (filePath) => resolveScopedFilePath(filePath, options?.rootDir);
1075
+ server.registerTool(
4146
1076
  "get_slide",
4147
1077
  {
4148
1078
  description: "Inspect a single slide: returns layout, notes, elements",
4149
1079
  inputSchema: GetSlideSchema.shape
4150
1080
  },
4151
1081
  async (params) => {
4152
- const result = await runMcpTool(
1082
+ const result = await runMcpTool2(
4153
1083
  params.filePath,
4154
1084
  (ctx) => getSlide(ctx, { slideIndex: params.slideIndex })
4155
1085
  );
@@ -4163,14 +1093,14 @@ function createServer() {
4163
1093
  };
4164
1094
  }
4165
1095
  );
4166
- server2.registerTool(
1096
+ server.registerTool(
4167
1097
  "add_slide",
4168
1098
  {
4169
1099
  description: "Add a new blank slide to the presentation",
4170
1100
  inputSchema: AddSlideSchema.shape
4171
1101
  },
4172
1102
  async (params) => {
4173
- const result = await runMcpTool(params.filePath, (ctx) => addSlide(ctx, params));
1103
+ const result = await runMcpTool2(params.filePath, (ctx) => addSlide(ctx, params));
4174
1104
  return {
4175
1105
  content: [
4176
1106
  {
@@ -4181,14 +1111,14 @@ function createServer() {
4181
1111
  };
4182
1112
  }
4183
1113
  );
4184
- server2.registerTool(
1114
+ server.registerTool(
4185
1115
  "delete_slides",
4186
1116
  {
4187
1117
  description: "Delete one or more slides by index",
4188
1118
  inputSchema: DeleteSlidesSchema.shape
4189
1119
  },
4190
1120
  async (params) => {
4191
- const result = await runMcpTool(
1121
+ const result = await runMcpTool2(
4192
1122
  params.filePath,
4193
1123
  (ctx) => deleteSlides(ctx, {
4194
1124
  slideIndexes: params.slideIndexes
@@ -4204,14 +1134,14 @@ function createServer() {
4204
1134
  };
4205
1135
  }
4206
1136
  );
4207
- server2.registerTool(
1137
+ server.registerTool(
4208
1138
  "reorder_slides",
4209
1139
  {
4210
1140
  description: "Reorder slides by providing a new index array",
4211
1141
  inputSchema: ReorderSlidesSchema.shape
4212
1142
  },
4213
1143
  async (params) => {
4214
- const result = await runMcpTool(
1144
+ const result = await runMcpTool2(
4215
1145
  params.filePath,
4216
1146
  (ctx) => reorderSlides(ctx, { newOrder: params.newOrder })
4217
1147
  );
@@ -4225,14 +1155,14 @@ function createServer() {
4225
1155
  };
4226
1156
  }
4227
1157
  );
4228
- server2.registerTool(
1158
+ server.registerTool(
4229
1159
  "duplicate_slide",
4230
1160
  {
4231
1161
  description: "Duplicate a slide with new element IDs",
4232
1162
  inputSchema: DuplicateSlideSchema.shape
4233
1163
  },
4234
1164
  async (params) => {
4235
- const result = await runMcpTool(
1165
+ const result = await runMcpTool2(
4236
1166
  params.filePath,
4237
1167
  (ctx) => duplicateSlide(ctx, params)
4238
1168
  );
@@ -4246,14 +1176,14 @@ function createServer() {
4246
1176
  };
4247
1177
  }
4248
1178
  );
4249
- server2.registerTool(
1179
+ server.registerTool(
4250
1180
  "update_slide_properties",
4251
1181
  {
4252
1182
  description: "Update slide background, notes, or visibility",
4253
1183
  inputSchema: UpdateSlidePropertiesSchema.shape
4254
1184
  },
4255
1185
  async (params) => {
4256
- const result = await runMcpTool(
1186
+ const result = await runMcpTool2(
4257
1187
  params.filePath,
4258
1188
  (ctx) => updateSlideProperties(ctx, params)
4259
1189
  );
@@ -4267,14 +1197,14 @@ function createServer() {
4267
1197
  };
4268
1198
  }
4269
1199
  );
4270
- server2.registerTool(
1200
+ server.registerTool(
4271
1201
  "set_slide_transition",
4272
1202
  {
4273
1203
  description: "Set or remove a slide transition effect",
4274
1204
  inputSchema: SetSlideTransitionSchema.shape
4275
1205
  },
4276
1206
  async (params) => {
4277
- const result = await runMcpTool(
1207
+ const result = await runMcpTool2(
4278
1208
  params.filePath,
4279
1209
  (ctx) => setSlideTransition(ctx, params)
4280
1210
  );
@@ -4288,14 +1218,14 @@ function createServer() {
4288
1218
  };
4289
1219
  }
4290
1220
  );
4291
- server2.registerTool(
1221
+ server.registerTool(
4292
1222
  "set_canvas_size",
4293
1223
  {
4294
1224
  description: "Change the presentation canvas dimensions",
4295
1225
  inputSchema: SetCanvasSizeSchema.shape
4296
1226
  },
4297
1227
  async (params) => {
4298
- const result = await runMcpTool(
1228
+ const result = await runMcpTool2(
4299
1229
  params.filePath,
4300
1230
  (ctx) => setCanvasSize(ctx, params)
4301
1231
  );
@@ -4309,14 +1239,14 @@ function createServer() {
4309
1239
  };
4310
1240
  }
4311
1241
  );
4312
- server2.registerTool(
1242
+ server.registerTool(
4313
1243
  "add_element",
4314
1244
  {
4315
1245
  description: "Add a text, shape, image, table, or connector element",
4316
1246
  inputSchema: AddElementSchema.shape
4317
1247
  },
4318
1248
  async (params) => {
4319
- const result = await runMcpTool(
1249
+ const result = await runMcpTool2(
4320
1250
  params.filePath,
4321
1251
  (ctx) => addElement(ctx, params)
4322
1252
  );
@@ -4330,14 +1260,14 @@ function createServer() {
4330
1260
  };
4331
1261
  }
4332
1262
  );
4333
- server2.registerTool(
1263
+ server.registerTool(
4334
1264
  "update_element",
4335
1265
  {
4336
1266
  description: "Update element position, size, text, or style",
4337
1267
  inputSchema: UpdateElementSchema.shape
4338
1268
  },
4339
1269
  async (params) => {
4340
- const result = await runMcpTool(
1270
+ const result = await runMcpTool2(
4341
1271
  params.filePath,
4342
1272
  (ctx) => updateElement(ctx, params)
4343
1273
  );
@@ -4351,14 +1281,14 @@ function createServer() {
4351
1281
  };
4352
1282
  }
4353
1283
  );
4354
- server2.registerTool(
1284
+ server.registerTool(
4355
1285
  "rename_element",
4356
1286
  {
4357
1287
  description: "Rename an element (the selection pane's display name; cNvPr/@name)",
4358
1288
  inputSchema: RenameElementSchema.shape
4359
1289
  },
4360
1290
  async (params) => {
4361
- const result = await runMcpTool(
1291
+ const result = await runMcpTool2(
4362
1292
  params.filePath,
4363
1293
  (ctx) => renameElement(ctx, params)
4364
1294
  );
@@ -4372,14 +1302,14 @@ function createServer() {
4372
1302
  };
4373
1303
  }
4374
1304
  );
4375
- server2.registerTool(
1305
+ server.registerTool(
4376
1306
  "delete_elements",
4377
1307
  {
4378
1308
  description: "Delete one or more elements by ID",
4379
1309
  inputSchema: DeleteElementsSchema.shape
4380
1310
  },
4381
1311
  async (params) => {
4382
- const result = await runMcpTool(
1312
+ const result = await runMcpTool2(
4383
1313
  params.filePath,
4384
1314
  (ctx) => deleteElements(ctx, params)
4385
1315
  );
@@ -4393,14 +1323,14 @@ function createServer() {
4393
1323
  };
4394
1324
  }
4395
1325
  );
4396
- server2.registerTool(
1326
+ server.registerTool(
4397
1327
  "arrange_elements",
4398
1328
  {
4399
1329
  description: "Align elements or change z-order (layer)",
4400
1330
  inputSchema: ArrangeElementsSchema.shape
4401
1331
  },
4402
1332
  async (params) => {
4403
- const result = await runMcpTool(
1333
+ const result = await runMcpTool2(
4404
1334
  params.filePath,
4405
1335
  (ctx) => arrangeElements(ctx, params)
4406
1336
  );
@@ -4414,14 +1344,14 @@ function createServer() {
4414
1344
  };
4415
1345
  }
4416
1346
  );
4417
- server2.registerTool(
1347
+ server.registerTool(
4418
1348
  "clone_element",
4419
1349
  {
4420
1350
  description: "Clone an element within or across slides",
4421
1351
  inputSchema: CloneElementSchema.shape
4422
1352
  },
4423
1353
  async (params) => {
4424
- const result = await runMcpTool(
1354
+ const result = await runMcpTool2(
4425
1355
  params.filePath,
4426
1356
  (ctx) => cloneElement(ctx, params)
4427
1357
  );
@@ -4435,14 +1365,14 @@ function createServer() {
4435
1365
  };
4436
1366
  }
4437
1367
  );
4438
- server2.registerTool(
1368
+ server.registerTool(
4439
1369
  "set_element_animation",
4440
1370
  {
4441
1371
  description: "Set entrance/exit animation on an element",
4442
1372
  inputSchema: SetElementAnimationSchema.shape
4443
1373
  },
4444
1374
  async (params) => {
4445
- const result = await runMcpTool(
1375
+ const result = await runMcpTool2(
4446
1376
  params.filePath,
4447
1377
  (ctx) => setElementAnimation(ctx, params)
4448
1378
  );
@@ -4456,14 +1386,14 @@ function createServer() {
4456
1386
  };
4457
1387
  }
4458
1388
  );
4459
- server2.registerTool(
1389
+ server.registerTool(
4460
1390
  "group_elements",
4461
1391
  {
4462
1392
  description: "Group multiple elements into a group",
4463
1393
  inputSchema: GroupElementsSchema.shape
4464
1394
  },
4465
1395
  async (params) => {
4466
- const result = await runMcpTool(
1396
+ const result = await runMcpTool2(
4467
1397
  params.filePath,
4468
1398
  (ctx) => groupElements(ctx, params)
4469
1399
  );
@@ -4477,14 +1407,14 @@ function createServer() {
4477
1407
  };
4478
1408
  }
4479
1409
  );
4480
- server2.registerTool(
1410
+ server.registerTool(
4481
1411
  "ungroup_elements",
4482
1412
  {
4483
1413
  description: "Ungroup a group element back to individual elements",
4484
1414
  inputSchema: UngroupElementsSchema.shape
4485
1415
  },
4486
1416
  async (params) => {
4487
- const result = await runMcpTool(
1417
+ const result = await runMcpTool2(
4488
1418
  params.filePath,
4489
1419
  (ctx) => ungroupElements(ctx, params)
4490
1420
  );
@@ -4498,14 +1428,14 @@ function createServer() {
4498
1428
  };
4499
1429
  }
4500
1430
  );
4501
- server2.registerTool(
1431
+ server.registerTool(
4502
1432
  "batch_update_elements",
4503
1433
  {
4504
1434
  description: "Apply the same properties to multiple elements",
4505
1435
  inputSchema: BatchUpdateElementsSchema.shape
4506
1436
  },
4507
1437
  async (params) => {
4508
- const result = await runMcpTool(
1438
+ const result = await runMcpTool2(
4509
1439
  params.filePath,
4510
1440
  (ctx) => batchUpdateElements(ctx, params)
4511
1441
  );
@@ -4519,14 +1449,14 @@ function createServer() {
4519
1449
  };
4520
1450
  }
4521
1451
  );
4522
- server2.registerTool(
1452
+ server.registerTool(
4523
1453
  "update_table_cells",
4524
1454
  {
4525
1455
  description: "Update text content of table cells",
4526
1456
  inputSchema: UpdateTableCellsSchema.shape
4527
1457
  },
4528
1458
  async (params) => {
4529
- const result = await runMcpTool(
1459
+ const result = await runMcpTool2(
4530
1460
  params.filePath,
4531
1461
  (ctx) => updateTableCells(ctx, params)
4532
1462
  );
@@ -4540,14 +1470,14 @@ function createServer() {
4540
1470
  };
4541
1471
  }
4542
1472
  );
4543
- server2.registerTool(
1473
+ server.registerTool(
4544
1474
  "manage_table_structure",
4545
1475
  {
4546
1476
  description: "Insert or delete table rows and columns",
4547
1477
  inputSchema: ManageTableStructureSchema.shape
4548
1478
  },
4549
1479
  async (params) => {
4550
- const result = await runMcpTool(
1480
+ const result = await runMcpTool2(
4551
1481
  params.filePath,
4552
1482
  (ctx) => manageTableStructure(ctx, params)
4553
1483
  );
@@ -4561,14 +1491,14 @@ function createServer() {
4561
1491
  };
4562
1492
  }
4563
1493
  );
4564
- server2.registerTool(
1494
+ server.registerTool(
4565
1495
  "set_table_style_section",
4566
1496
  {
4567
1497
  description: "Patch the fill, text, borders, or cell3D of one section (wholeTbl, banding, first/last row/col, or a corner cell) of an existing loaded table style",
4568
1498
  inputSchema: SetTableStyleSectionSchema.shape
4569
1499
  },
4570
1500
  async (params) => {
4571
- const result = await runMcpTool(
1501
+ const result = await runMcpTool2(
4572
1502
  params.filePath,
4573
1503
  (ctx) => setTableStyleSection(ctx, params)
4574
1504
  );
@@ -4577,14 +1507,14 @@ function createServer() {
4577
1507
  };
4578
1508
  }
4579
1509
  );
4580
- server2.registerTool(
1510
+ server.registerTool(
4581
1511
  "create_table_style",
4582
1512
  {
4583
1513
  description: "Create a new table style (optionally cloned from an existing one) and optionally set it as the default",
4584
1514
  inputSchema: CreateTableStyleSchema.shape
4585
1515
  },
4586
1516
  async (params) => {
4587
- const result = await runMcpTool(
1517
+ const result = await runMcpTool2(
4588
1518
  params.filePath,
4589
1519
  (ctx) => createTableStyle(ctx, params)
4590
1520
  );
@@ -4593,14 +1523,14 @@ function createServer() {
4593
1523
  };
4594
1524
  }
4595
1525
  );
4596
- server2.registerTool(
1526
+ server.registerTool(
4597
1527
  "delete_table_style",
4598
1528
  {
4599
1529
  description: "Remove a table style from the presentation",
4600
1530
  inputSchema: DeleteTableStyleSchema.shape
4601
1531
  },
4602
1532
  async (params) => {
4603
- const result = await runMcpTool(
1533
+ const result = await runMcpTool2(
4604
1534
  params.filePath,
4605
1535
  (ctx) => deleteTableStyle(ctx, params)
4606
1536
  );
@@ -4609,14 +1539,14 @@ function createServer() {
4609
1539
  };
4610
1540
  }
4611
1541
  );
4612
- server2.registerTool(
1542
+ server.registerTool(
4613
1543
  "assign_table_style",
4614
1544
  {
4615
1545
  description: "Assign a table style (and row/column emphasis flags) to an existing table",
4616
1546
  inputSchema: AssignTableStyleSchema.shape
4617
1547
  },
4618
1548
  async (params) => {
4619
- const result = await runMcpTool(
1549
+ const result = await runMcpTool2(
4620
1550
  params.filePath,
4621
1551
  (ctx) => assignTableStyle(ctx, params)
4622
1552
  );
@@ -4625,14 +1555,14 @@ function createServer() {
4625
1555
  };
4626
1556
  }
4627
1557
  );
4628
- server2.registerTool(
1558
+ server.registerTool(
4629
1559
  "update_element_style",
4630
1560
  {
4631
1561
  description: "Update fill, stroke, shadow, glow, or image effects",
4632
1562
  inputSchema: UpdateElementStyleSchema.shape
4633
1563
  },
4634
1564
  async (params) => {
4635
- const result = await runMcpTool(
1565
+ const result = await runMcpTool2(
4636
1566
  params.filePath,
4637
1567
  (ctx) => updateElementStyle(ctx, params)
4638
1568
  );
@@ -4646,14 +1576,14 @@ function createServer() {
4646
1576
  };
4647
1577
  }
4648
1578
  );
4649
- server2.registerTool(
1579
+ server.registerTool(
4650
1580
  "run_accessibility_check",
4651
1581
  {
4652
1582
  description: "Audit the presentation for accessibility issues",
4653
1583
  inputSchema: AccessibilityCheckSchema.shape
4654
1584
  },
4655
1585
  async (params) => {
4656
- const result = await runMcpTool(
1586
+ const result = await runMcpTool2(
4657
1587
  params.filePath,
4658
1588
  (ctx) => runAccessibilityCheck(ctx)
4659
1589
  );
@@ -4667,14 +1597,14 @@ function createServer() {
4667
1597
  };
4668
1598
  }
4669
1599
  );
4670
- server2.registerTool(
1600
+ server.registerTool(
4671
1601
  "find_text",
4672
1602
  {
4673
1603
  description: "Search for text across all slides",
4674
1604
  inputSchema: FindTextSchema.shape
4675
1605
  },
4676
1606
  async (params) => {
4677
- const result = await runMcpTool(params.filePath, (ctx) => findText(ctx, params));
1607
+ const result = await runMcpTool2(params.filePath, (ctx) => findText(ctx, params));
4678
1608
  return {
4679
1609
  content: [
4680
1610
  {
@@ -4685,14 +1615,14 @@ function createServer() {
4685
1615
  };
4686
1616
  }
4687
1617
  );
4688
- server2.registerTool(
1618
+ server.registerTool(
4689
1619
  "replace_text",
4690
1620
  {
4691
1621
  description: "Find and replace text across slides",
4692
1622
  inputSchema: ReplaceTextSchema.shape
4693
1623
  },
4694
1624
  async (params) => {
4695
- const result = await runMcpTool(
1625
+ const result = await runMcpTool2(
4696
1626
  params.filePath,
4697
1627
  (ctx) => replaceText(ctx, params)
4698
1628
  );
@@ -4706,14 +1636,14 @@ function createServer() {
4706
1636
  };
4707
1637
  }
4708
1638
  );
4709
- server2.registerTool(
1639
+ server.registerTool(
4710
1640
  "manage_comments",
4711
1641
  {
4712
1642
  description: "List, add, delete, or resolve slide comments",
4713
1643
  inputSchema: ManageCommentsSchema.shape
4714
1644
  },
4715
1645
  async (params) => {
4716
- const result = await runMcpTool(
1646
+ const result = await runMcpTool2(
4717
1647
  params.filePath,
4718
1648
  (ctx) => manageComments(ctx, params)
4719
1649
  );
@@ -4727,16 +1657,16 @@ function createServer() {
4727
1657
  };
4728
1658
  }
4729
1659
  );
4730
- server2.registerTool(
1660
+ server.registerTool(
4731
1661
  "convert_to_markdown",
4732
1662
  {
4733
1663
  description: "Convert the presentation to Markdown format",
4734
1664
  inputSchema: ConvertToMarkdownSchema.shape
4735
1665
  },
4736
1666
  async (params) => {
4737
- const result = await runMcpTool(
1667
+ const result = await runMcpTool2(
4738
1668
  params.filePath,
4739
- (ctx) => convertToMarkdown(ctx, { ...params, scopeOutputDir: resolveScopedDir })
1669
+ (ctx) => convertToMarkdown(ctx, { ...params, scopeOutputDir: resolveScopedDir2 })
4740
1670
  );
4741
1671
  return {
4742
1672
  content: [
@@ -4748,414 +1678,414 @@ function createServer() {
4748
1678
  };
4749
1679
  }
4750
1680
  );
4751
- server2.registerTool(
1681
+ server.registerTool(
4752
1682
  "get_theme_info",
4753
1683
  {
4754
1684
  description: "Get current theme information (colors, fonts, available presets)",
4755
1685
  inputSchema: GetThemeInfoSchema.shape
4756
1686
  },
4757
1687
  async (params) => {
4758
- const result = await runMcpTool(params.filePath, (ctx) => getThemeInfo(ctx));
1688
+ const result = await runMcpTool2(params.filePath, (ctx) => getThemeInfo(ctx));
4759
1689
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4760
1690
  }
4761
1691
  );
4762
- server2.registerTool(
1692
+ server.registerTool(
4763
1693
  "apply_theme_preset",
4764
1694
  {
4765
1695
  description: "Apply a built-in theme preset to the presentation",
4766
1696
  inputSchema: ApplyThemePresetSchema.shape
4767
1697
  },
4768
1698
  async (params) => {
4769
- const result = await runMcpTool(
1699
+ const result = await runMcpTool2(
4770
1700
  params.filePath,
4771
1701
  (ctx) => applyThemePreset(ctx, params)
4772
1702
  );
4773
1703
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4774
1704
  }
4775
1705
  );
4776
- server2.registerTool(
1706
+ server.registerTool(
4777
1707
  "update_theme_colors",
4778
1708
  {
4779
1709
  description: "Update individual theme colors (accent1-6, dk1/2, lt1/2, hlink, folHlink)",
4780
1710
  inputSchema: UpdateThemeColorsSchema.shape
4781
1711
  },
4782
1712
  async (params) => {
4783
- const result = await runMcpTool(
1713
+ const result = await runMcpTool2(
4784
1714
  params.filePath,
4785
1715
  (ctx) => updateThemeColors(ctx, params)
4786
1716
  );
4787
1717
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4788
1718
  }
4789
1719
  );
4790
- server2.registerTool(
1720
+ server.registerTool(
4791
1721
  "update_theme_fonts",
4792
1722
  {
4793
1723
  description: "Update theme heading (major) and body (minor) fonts",
4794
1724
  inputSchema: UpdateThemeFontsSchema.shape
4795
1725
  },
4796
1726
  async (params) => {
4797
- const result = await runMcpTool(
1727
+ const result = await runMcpTool2(
4798
1728
  params.filePath,
4799
1729
  (ctx) => updateThemeFonts(ctx, params)
4800
1730
  );
4801
1731
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4802
1732
  }
4803
1733
  );
4804
- server2.registerTool(
1734
+ server.registerTool(
4805
1735
  "update_chart",
4806
1736
  {
4807
1737
  description: "Update chart type, title, legend, data labels, axis, or categories",
4808
1738
  inputSchema: UpdateChartSchema.shape
4809
1739
  },
4810
1740
  async (params) => {
4811
- const result = await runMcpTool(
1741
+ const result = await runMcpTool2(
4812
1742
  params.filePath,
4813
1743
  (ctx) => updateChart(ctx, params)
4814
1744
  );
4815
1745
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4816
1746
  }
4817
1747
  );
4818
- server2.registerTool(
1748
+ server.registerTool(
4819
1749
  "add_chart_series",
4820
1750
  {
4821
1751
  description: "Add a data series to an existing chart",
4822
1752
  inputSchema: AddChartSeriesSchema.shape
4823
1753
  },
4824
1754
  async (params) => {
4825
- const result = await runMcpTool(
1755
+ const result = await runMcpTool2(
4826
1756
  params.filePath,
4827
1757
  (ctx) => addChartSeriesT(ctx, params)
4828
1758
  );
4829
1759
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4830
1760
  }
4831
1761
  );
4832
- server2.registerTool(
1762
+ server.registerTool(
4833
1763
  "remove_chart_series",
4834
1764
  {
4835
1765
  description: "Remove a data series from a chart by index",
4836
1766
  inputSchema: RemoveChartSeriesSchema.shape
4837
1767
  },
4838
1768
  async (params) => {
4839
- const result = await runMcpTool(
1769
+ const result = await runMcpTool2(
4840
1770
  params.filePath,
4841
1771
  (ctx) => removeChartSeriesT(ctx, params)
4842
1772
  );
4843
1773
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4844
1774
  }
4845
1775
  );
4846
- server2.registerTool(
1776
+ server.registerTool(
4847
1777
  "update_chart_series_data",
4848
1778
  {
4849
1779
  description: "Update data values for a chart series",
4850
1780
  inputSchema: UpdateChartSeriesDataSchema.shape
4851
1781
  },
4852
1782
  async (params) => {
4853
- const result = await runMcpTool(
1783
+ const result = await runMcpTool2(
4854
1784
  params.filePath,
4855
1785
  (ctx) => updateChartSeriesData(ctx, params)
4856
1786
  );
4857
1787
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4858
1788
  }
4859
1789
  );
4860
- server2.registerTool(
1790
+ server.registerTool(
4861
1791
  "create_chart",
4862
1792
  {
4863
1793
  description: "Create a new chart element on a slide",
4864
1794
  inputSchema: CreateChartSchema.shape
4865
1795
  },
4866
1796
  async (params) => {
4867
- const result = await runMcpTool(
1797
+ const result = await runMcpTool2(
4868
1798
  params.filePath,
4869
1799
  (ctx) => createChart(ctx, params)
4870
1800
  );
4871
1801
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4872
1802
  }
4873
1803
  );
4874
- server2.registerTool(
1804
+ server.registerTool(
4875
1805
  "format_chart_data_point",
4876
1806
  {
4877
1807
  description: "Set per-data-point chart formatting: fill/stroke colour, stroke width, dash style (c:dPt/c:spPr), explosion, and marker override",
4878
1808
  inputSchema: FormatChartDataPointSchema.shape
4879
1809
  },
4880
1810
  async (params) => {
4881
- const result = await runMcpTool(
1811
+ const result = await runMcpTool2(
4882
1812
  params.filePath,
4883
1813
  (ctx) => formatChartDataPoint(ctx, params)
4884
1814
  );
4885
1815
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4886
1816
  }
4887
1817
  );
4888
- server2.registerTool(
1818
+ server.registerTool(
4889
1819
  "format_chart_data_label",
4890
1820
  {
4891
1821
  description: "Set a per-data-point data-label override (c:dLbl): show flags, position, custom text, and the label's own fill/line formatting (c:spPr) and font (c:txPr)",
4892
1822
  inputSchema: FormatChartDataLabelSchema.shape
4893
1823
  },
4894
1824
  async (params) => {
4895
- const result = await runMcpTool(
1825
+ const result = await runMcpTool2(
4896
1826
  params.filePath,
4897
1827
  (ctx) => formatChartDataLabel(ctx, params)
4898
1828
  );
4899
1829
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4900
1830
  }
4901
1831
  );
4902
- server2.registerTool(
1832
+ server.registerTool(
4903
1833
  "format_chart_series",
4904
1834
  {
4905
1835
  description: "Set a chart series marker (c:marker), trendline (c:trendline, incl. line width/dash), or error bars (c:errBars, incl. line width/dash)",
4906
1836
  inputSchema: FormatChartSeriesSchema.shape
4907
1837
  },
4908
1838
  async (params) => {
4909
- const result = await runMcpTool(
1839
+ const result = await runMcpTool2(
4910
1840
  params.filePath,
4911
1841
  (ctx) => formatChartSeries(ctx, params)
4912
1842
  );
4913
1843
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4914
1844
  }
4915
1845
  );
4916
- server2.registerTool(
1846
+ server.registerTool(
4917
1847
  "set_chart_helper_line",
4918
1848
  {
4919
1849
  description: "Set or remove a chart-level helper line on a line/stock/combo chart: c:dropLines or c:hiLowLines",
4920
1850
  inputSchema: SetChartHelperLineSchema.shape
4921
1851
  },
4922
1852
  async (params) => {
4923
- const result = await runMcpTool(
1853
+ const result = await runMcpTool2(
4924
1854
  params.filePath,
4925
1855
  (ctx) => setChartHelperLineT(ctx, params)
4926
1856
  );
4927
1857
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4928
1858
  }
4929
1859
  );
4930
- server2.registerTool(
1860
+ server.registerTool(
4931
1861
  "set_chart_color_map_override",
4932
1862
  {
4933
1863
  description: "Set or remove a chart's theme colour-map override (c:clrMapOvr), remapping theme colour roles for this chart only",
4934
1864
  inputSchema: SetChartColorMapOverrideSchema.shape
4935
1865
  },
4936
1866
  async (params) => {
4937
- const result = await runMcpTool(
1867
+ const result = await runMcpTool2(
4938
1868
  params.filePath,
4939
1869
  (ctx) => setChartColorMapOverrideT(ctx, params)
4940
1870
  );
4941
1871
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4942
1872
  }
4943
1873
  );
4944
- server2.registerTool(
1874
+ server.registerTool(
4945
1875
  "chart_user_shape_list",
4946
1876
  {
4947
1877
  description: "List a chart's drawing-overlay shapes (c:userShapes)",
4948
1878
  inputSchema: ListChartUserShapesSchema.shape
4949
1879
  },
4950
1880
  async (params) => {
4951
- const result = await runMcpTool(
1881
+ const result = await runMcpTool2(
4952
1882
  params.filePath,
4953
1883
  (ctx) => listChartUserShapesT(ctx, params)
4954
1884
  );
4955
1885
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4956
1886
  }
4957
1887
  );
4958
- server2.registerTool(
1888
+ server.registerTool(
4959
1889
  "chart_user_shape_add",
4960
1890
  {
4961
1891
  description: "Add a drawing-overlay shape (text box or connector) to a chart's c:userShapes",
4962
1892
  inputSchema: AddChartUserShapeSchema.shape
4963
1893
  },
4964
1894
  async (params) => {
4965
- const result = await runMcpTool(
1895
+ const result = await runMcpTool2(
4966
1896
  params.filePath,
4967
1897
  (ctx) => addChartUserShapeT(ctx, params)
4968
1898
  );
4969
1899
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4970
1900
  }
4971
1901
  );
4972
- server2.registerTool(
1902
+ server.registerTool(
4973
1903
  "chart_user_shape_update",
4974
1904
  {
4975
1905
  description: "Move, resize, or restyle one of a chart's drawing-overlay shapes by index",
4976
1906
  inputSchema: UpdateChartUserShapeSchema.shape
4977
1907
  },
4978
1908
  async (params) => {
4979
- const result = await runMcpTool(
1909
+ const result = await runMcpTool2(
4980
1910
  params.filePath,
4981
1911
  (ctx) => updateChartUserShapeT(ctx, params)
4982
1912
  );
4983
1913
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4984
1914
  }
4985
1915
  );
4986
- server2.registerTool(
1916
+ server.registerTool(
4987
1917
  "chart_user_shape_remove",
4988
1918
  {
4989
1919
  description: "Remove one of a chart's drawing-overlay shapes by index",
4990
1920
  inputSchema: RemoveChartUserShapeSchema.shape
4991
1921
  },
4992
1922
  async (params) => {
4993
- const result = await runMcpTool(
1923
+ const result = await runMcpTool2(
4994
1924
  params.filePath,
4995
1925
  (ctx) => removeChartUserShapeT(ctx, params)
4996
1926
  );
4997
1927
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4998
1928
  }
4999
1929
  );
5000
- server2.registerTool(
1930
+ server.registerTool(
5001
1931
  "manage_smart_art",
5002
1932
  {
5003
1933
  description: "Manage SmartArt: get nodes, add/remove/reorder/promote/demote nodes, or decompose to shapes",
5004
1934
  inputSchema: ManageSmartArtSchema.shape
5005
1935
  },
5006
1936
  async (params) => {
5007
- const result = await runMcpTool(
1937
+ const result = await runMcpTool2(
5008
1938
  params.filePath,
5009
1939
  (ctx) => manageSmartArt(ctx, params)
5010
1940
  );
5011
1941
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5012
1942
  }
5013
1943
  );
5014
- server2.registerTool(
1944
+ server.registerTool(
5015
1945
  "find_placeholders",
5016
1946
  {
5017
1947
  description: "Discover all {{placeholder}} tokens in the presentation",
5018
1948
  inputSchema: FindPlaceholdersSchema.shape
5019
1949
  },
5020
1950
  async (params) => {
5021
- const result = await runMcpTool(
1951
+ const result = await runMcpTool2(
5022
1952
  params.filePath,
5023
1953
  (ctx) => findPlaceholdersT(ctx)
5024
1954
  );
5025
1955
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5026
1956
  }
5027
1957
  );
5028
- server2.registerTool(
1958
+ server.registerTool(
5029
1959
  "apply_template",
5030
1960
  {
5031
1961
  description: "Replace {{placeholder}} tokens with provided values (mail merge)",
5032
1962
  inputSchema: ApplyTemplateSchema.shape
5033
1963
  },
5034
1964
  async (params) => {
5035
- const result = await runMcpTool(
1965
+ const result = await runMcpTool2(
5036
1966
  params.filePath,
5037
1967
  (ctx) => applyTemplateT(ctx, { data: params.data })
5038
1968
  );
5039
1969
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5040
1970
  }
5041
1971
  );
5042
- server2.registerTool(
1972
+ server.registerTool(
5043
1973
  "get_metadata",
5044
1974
  {
5045
1975
  description: "Get presentation metadata (title, author, keywords, custom properties)",
5046
1976
  inputSchema: GetMetadataSchema.shape
5047
1977
  },
5048
1978
  async (params) => {
5049
- const result = await runMcpTool(params.filePath, (ctx) => getMetadata(ctx));
1979
+ const result = await runMcpTool2(params.filePath, (ctx) => getMetadata(ctx));
5050
1980
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5051
1981
  }
5052
1982
  );
5053
- server2.registerTool(
1983
+ server.registerTool(
5054
1984
  "update_metadata",
5055
1985
  {
5056
1986
  description: "Update presentation metadata (title, author, company, custom properties)",
5057
1987
  inputSchema: UpdateMetadataSchema.shape
5058
1988
  },
5059
1989
  async (params) => {
5060
- const result = await runMcpTool(
1990
+ const result = await runMcpTool2(
5061
1991
  params.filePath,
5062
1992
  (ctx) => updateMetadata(ctx, params)
5063
1993
  );
5064
1994
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5065
1995
  }
5066
1996
  );
5067
- server2.registerTool(
1997
+ server.registerTool(
5068
1998
  "manage_sections",
5069
1999
  {
5070
2000
  description: "List, add, remove, reorder sections or move slides between sections",
5071
2001
  inputSchema: ManageSectionsSchema.shape
5072
2002
  },
5073
2003
  async (params) => {
5074
- const result = await runMcpTool(
2004
+ const result = await runMcpTool2(
5075
2005
  params.filePath,
5076
2006
  (ctx) => manageSections(ctx, params)
5077
2007
  );
5078
2008
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5079
2009
  }
5080
2010
  );
5081
- server2.registerTool(
2011
+ server.registerTool(
5082
2012
  "export_to_svg",
5083
2013
  {
5084
2014
  description: "Export all or specific slides to SVG strings",
5085
2015
  inputSchema: ExportToSvgSchema.shape
5086
2016
  },
5087
2017
  async (params) => {
5088
- const result = await runMcpTool(
2018
+ const result = await runMcpTool2(
5089
2019
  params.filePath,
5090
2020
  (ctx) => exportToSvg(ctx, params)
5091
2021
  );
5092
2022
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5093
2023
  }
5094
2024
  );
5095
- server2.registerTool(
2025
+ server.registerTool(
5096
2026
  "export_slide_svg",
5097
2027
  {
5098
2028
  description: "Export a single slide to SVG",
5099
2029
  inputSchema: ExportSlideSvgSchema.shape
5100
2030
  },
5101
2031
  async (params) => {
5102
- const result = await runMcpTool(
2032
+ const result = await runMcpTool2(
5103
2033
  params.filePath,
5104
2034
  (ctx) => exportSlideSvg(ctx, params)
5105
2035
  );
5106
2036
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5107
2037
  }
5108
2038
  );
5109
- server2.registerTool(
2039
+ server.registerTool(
5110
2040
  "export_to_json",
5111
2041
  {
5112
2042
  description: "Export the presentation to the portable pptx-viewer-json document format (self-contained, binary assets embedded as base64)",
5113
2043
  inputSchema: ExportToJsonSchema.shape
5114
2044
  },
5115
2045
  async (params) => {
5116
- const result = await runMcpTool(
2046
+ const result = await runMcpTool2(
5117
2047
  params.filePath,
5118
2048
  (ctx) => exportToJson(ctx, params)
5119
2049
  );
5120
2050
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5121
2051
  }
5122
2052
  );
5123
- server2.registerTool(
2053
+ server.registerTool(
5124
2054
  "import_from_json",
5125
2055
  {
5126
2056
  description: "Replace the presentation content with a deck imported from a pptx-viewer-json document",
5127
2057
  inputSchema: ImportFromJsonSchema.shape
5128
2058
  },
5129
2059
  async (params) => {
5130
- const result = await runMcpTool(
2060
+ const result = await runMcpTool2(
5131
2061
  params.filePath,
5132
2062
  (ctx) => importFromJson(ctx, params)
5133
2063
  );
5134
2064
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5135
2065
  }
5136
2066
  );
5137
- server2.registerTool(
2067
+ server.registerTool(
5138
2068
  "manage_hyperlinks",
5139
2069
  {
5140
2070
  description: "List, set, or remove hyperlinks/actions on elements",
5141
2071
  inputSchema: ManageHyperlinksSchema.shape
5142
2072
  },
5143
2073
  async (params) => {
5144
- const result = await runMcpTool(
2074
+ const result = await runMcpTool2(
5145
2075
  params.filePath,
5146
2076
  (ctx) => manageHyperlinks(ctx, params)
5147
2077
  );
5148
2078
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5149
2079
  }
5150
2080
  );
5151
- server2.registerTool(
2081
+ server.registerTool(
5152
2082
  "replace_geometry",
5153
2083
  {
5154
2084
  description: "Replace a shape element geometry with a preset or custom SVG path",
5155
2085
  inputSchema: ReplaceGeometrySchema.shape
5156
2086
  },
5157
2087
  async (params) => {
5158
- const result = await runMcpTool(
2088
+ const result = await runMcpTool2(
5159
2089
  params.filePath,
5160
2090
  (ctx) => replaceGeometry(ctx, {
5161
2091
  ...params,
@@ -5165,21 +2095,21 @@ function createServer() {
5165
2095
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5166
2096
  }
5167
2097
  );
5168
- server2.registerTool(
2098
+ server.registerTool(
5169
2099
  "set_element_lock",
5170
2100
  {
5171
2101
  description: "Lock or unlock an element (prevent move, resize, rotation, selection, text edit)",
5172
2102
  inputSchema: SetElementLockSchema.shape
5173
2103
  },
5174
2104
  async (params) => {
5175
- const result = await runMcpTool(
2105
+ const result = await runMcpTool2(
5176
2106
  params.filePath,
5177
2107
  (ctx) => setElementLockT(ctx, params)
5178
2108
  );
5179
2109
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5180
2110
  }
5181
2111
  );
5182
- server2.registerTool(
2112
+ server.registerTool(
5183
2113
  "validate_presentation",
5184
2114
  {
5185
2115
  description: "Validate PPTX structural integrity (content types, relationships, XML)",
@@ -5187,13 +2117,13 @@ function createServer() {
5187
2117
  },
5188
2118
  async (params) => {
5189
2119
  const { readFile: readFile2 } = await import('fs/promises');
5190
- const safePath = resolveScopedFilePath(params.filePath);
2120
+ const safePath = resolveScopedFilePath2(params.filePath);
5191
2121
  const bytes = await readFile2(safePath);
5192
2122
  const result = await validatePresentation(bytes.buffer);
5193
2123
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5194
2124
  }
5195
2125
  );
5196
- server2.registerTool(
2126
+ server.registerTool(
5197
2127
  "repair_presentation",
5198
2128
  {
5199
2129
  description: "Auto-repair common PPTX structural issues",
@@ -5201,7 +2131,7 @@ function createServer() {
5201
2131
  },
5202
2132
  async (params) => {
5203
2133
  const { readFile: readFile2, writeFile: writeFile2 } = await import('fs/promises');
5204
- const safePath = resolveScopedFilePath(params.filePath);
2134
+ const safePath = resolveScopedFilePath2(params.filePath);
5205
2135
  const bytes = await readFile2(safePath);
5206
2136
  const { result, repairedBytes } = await repairPresentation(
5207
2137
  bytes.buffer
@@ -5212,147 +2142,143 @@ function createServer() {
5212
2142
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5213
2143
  }
5214
2144
  );
5215
- server2.registerTool(
2145
+ server.registerTool(
5216
2146
  "get_presentation_properties",
5217
2147
  {
5218
2148
  description: "Get slideshow properties (show type, loop, advance mode, slide range)",
5219
2149
  inputSchema: GetPresentationPropertiesSchema.shape
5220
2150
  },
5221
2151
  async (params) => {
5222
- const result = await runMcpTool(
2152
+ const result = await runMcpTool2(
5223
2153
  params.filePath,
5224
2154
  (ctx) => getPresentationProperties(ctx)
5225
2155
  );
5226
2156
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5227
2157
  }
5228
2158
  );
5229
- server2.registerTool(
2159
+ server.registerTool(
5230
2160
  "update_presentation_properties",
5231
2161
  {
5232
2162
  description: "Update slideshow properties (show type, loop, advance mode, pen color)",
5233
2163
  inputSchema: UpdatePresentationPropertiesSchema.shape
5234
2164
  },
5235
2165
  async (params) => {
5236
- const result = await runMcpTool(
2166
+ const result = await runMcpTool2(
5237
2167
  params.filePath,
5238
2168
  (ctx) => updatePresentationProperties(ctx, params)
5239
2169
  );
5240
2170
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5241
2171
  }
5242
2172
  );
5243
- server2.registerTool(
2173
+ server.registerTool(
5244
2174
  "get_layouts",
5245
2175
  {
5246
2176
  description: "List available slide layouts from the presentation masters",
5247
2177
  inputSchema: GetLayoutsSchema.shape
5248
2178
  },
5249
2179
  async (params) => {
5250
- const result = await runMcpTool(params.filePath, (ctx) => getLayouts(ctx));
2180
+ const result = await runMcpTool2(params.filePath, (ctx) => getLayouts(ctx));
5251
2181
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5252
2182
  }
5253
2183
  );
5254
- server2.registerTool(
2184
+ server.registerTool(
5255
2185
  "apply_layout",
5256
2186
  {
5257
2187
  description: "Apply a slide layout to a specific slide",
5258
2188
  inputSchema: ApplyLayoutSchema.shape
5259
2189
  },
5260
2190
  async (params) => {
5261
- const result = await runMcpTool(
2191
+ const result = await runMcpTool2(
5262
2192
  params.filePath,
5263
2193
  (ctx) => applyLayout(ctx, params)
5264
2194
  );
5265
2195
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5266
2196
  }
5267
2197
  );
5268
- server2.registerTool(
2198
+ server.registerTool(
5269
2199
  "ole_get_content",
5270
2200
  {
5271
2201
  description: "Inspect an embedded OLE object's editable content: resolved kind, plus the sheet grid, paragraph list, or nested-deck slide summaries, whichever applies",
5272
2202
  inputSchema: GetOleContentSchema.shape
5273
2203
  },
5274
2204
  async (params) => {
5275
- const result = await runMcpTool(
2205
+ const result = await runMcpTool2(
5276
2206
  params.filePath,
5277
2207
  (ctx) => getOleContent(ctx, params)
5278
2208
  );
5279
2209
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5280
2210
  }
5281
2211
  );
5282
- server2.registerTool(
2212
+ server.registerTool(
5283
2213
  "ole_set_sheet_cell",
5284
2214
  {
5285
2215
  description: "Edit one cell of an embedded Excel OLE object's first worksheet",
5286
2216
  inputSchema: SetOleSheetCellSchema.shape
5287
2217
  },
5288
2218
  async (params) => {
5289
- const result = await runMcpTool(
2219
+ const result = await runMcpTool2(
5290
2220
  params.filePath,
5291
2221
  (ctx) => setOleSheetCell(ctx, params)
5292
2222
  );
5293
2223
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5294
2224
  }
5295
2225
  );
5296
- server2.registerTool(
2226
+ server.registerTool(
5297
2227
  "ole_set_document_paragraph",
5298
2228
  {
5299
2229
  description: "Replace one paragraph's text in an embedded Word (.docx or legacy .doc) OLE object",
5300
2230
  inputSchema: SetOleDocumentParagraphSchema.shape
5301
2231
  },
5302
2232
  async (params) => {
5303
- const result = await runMcpTool(
2233
+ const result = await runMcpTool2(
5304
2234
  params.filePath,
5305
2235
  (ctx) => setOleDocumentParagraph(ctx, params)
5306
2236
  );
5307
2237
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5308
2238
  }
5309
2239
  );
5310
- server2.registerTool(
2240
+ server.registerTool(
5311
2241
  "ole_set_deck_slide_title",
5312
2242
  {
5313
2243
  description: "Edit one text-bearing shape on one slide of an embedded nested-deck (PowerPoint) OLE object, via a full load/save round-trip through the nested deck (call ole_get_content first to discover each slide's elementIds)",
5314
2244
  inputSchema: SetOleDeckSlideTitleSchema.shape
5315
2245
  },
5316
2246
  async (params) => {
5317
- const result = await runMcpTool(
2247
+ const result = await runMcpTool2(
5318
2248
  params.filePath,
5319
2249
  (ctx) => setOleDeckSlideTitle(ctx, params)
5320
2250
  );
5321
2251
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5322
2252
  }
5323
2253
  );
5324
- server2.registerTool(
2254
+ server.registerTool(
5325
2255
  "ole_replace_file",
5326
2256
  {
5327
2257
  description: `Replace an embedded OLE object's payload wholesale with an arbitrary file; works for any OLE kind, including a generic "Package" object or an unsupported format`,
5328
2258
  inputSchema: ReplaceOleFileSchema.shape
5329
2259
  },
5330
2260
  async (params) => {
5331
- const result = await runMcpTool(
2261
+ const result = await runMcpTool2(
5332
2262
  params.filePath,
5333
2263
  (ctx) => replaceOleFileT(ctx, params)
5334
2264
  );
5335
2265
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5336
2266
  }
5337
2267
  );
5338
- server2.registerTool(
2268
+ server.registerTool(
5339
2269
  "ole_set_object_name",
5340
2270
  {
5341
2271
  description: "Rename an OLE object's Object Name (p:oleObj/@name); regenerates the baked-in icon caption too when the object is displayed as an icon",
5342
2272
  inputSchema: SetOleObjectNameSchema.shape
5343
2273
  },
5344
2274
  async (params) => {
5345
- const result = await runMcpTool(
2275
+ const result = await runMcpTool2(
5346
2276
  params.filePath,
5347
2277
  (ctx) => setOleObjectNameT(ctx, params)
5348
2278
  );
5349
2279
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5350
2280
  }
5351
2281
  );
5352
- return server2;
5353
2282
  }
5354
2283
 
5355
- // src/mcp/index.ts
5356
- var server = createServer();
5357
- var transport = new StdioServerTransport();
5358
- void server.connect(transport);
2284
+ export { createServer, registerTools };