pptx-viewer-mcp 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +20 -19
- package/dist/index.d.ts +237 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +486 -172
- package/dist/index.mjs +479 -174
- package/dist/mcp/index.js +1044 -281
- package/dist/mcp/index.mjs +1045 -282
- package/dist/schemas/index.d.ts +604 -1
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +338 -2
- package/dist/schemas/index.mjs +325 -3
- package/package.json +2 -2
package/dist/mcp/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { validatePptx, repairPptx, PptxHandler, hasTextProperties, cloneElement as cloneElement$1, isTemplateElementId, makeStoreAwareId, reassignDescendantIds, groupElements as groupElements$1, ungroupElements as ungroupElements$1, PptxMarkdownConverter, ThemePresets, getThemePreset, setChartType, setChartTitle, setChartGrouping, setChartLegend, setChartDataLabels, setChartAxis, setChartCategories, addChartSeries, removeChartSeries, updateChartSeriesValues, ChartBuilder, 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 } from 'pptx-viewer-core';
|
|
5
|
+
import { validatePptx, repairPptx, PptxHandler, hasTextProperties, resolveThemeColorRef, cloneElement as cloneElement$1, isTemplateElementId, makeStoreAwareId, reassignDescendantIds, groupElements as groupElements$1, ungroupElements as ungroupElements$1, 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 } from 'pptx-viewer-core';
|
|
6
6
|
import { writeFile, readFile } from 'fs/promises';
|
|
7
7
|
import { resolve, isAbsolute, sep } from 'path';
|
|
8
8
|
|
|
@@ -66,6 +66,32 @@ var ConvertToMarkdownSchema = z.object({
|
|
|
66
66
|
var AccessibilityCheckSchema = z.object({
|
|
67
67
|
filePath: z.string().describe("Path to the PPTX file")
|
|
68
68
|
});
|
|
69
|
+
var ThemeColorRefSchema = z.object({
|
|
70
|
+
scheme: z.enum([
|
|
71
|
+
"dk1",
|
|
72
|
+
"lt1",
|
|
73
|
+
"dk2",
|
|
74
|
+
"lt2",
|
|
75
|
+
"accent1",
|
|
76
|
+
"accent2",
|
|
77
|
+
"accent3",
|
|
78
|
+
"accent4",
|
|
79
|
+
"accent5",
|
|
80
|
+
"accent6",
|
|
81
|
+
"hlink",
|
|
82
|
+
"folHlink",
|
|
83
|
+
"bg1",
|
|
84
|
+
"tx1",
|
|
85
|
+
"bg2",
|
|
86
|
+
"tx2",
|
|
87
|
+
"phClr"
|
|
88
|
+
]),
|
|
89
|
+
lumMod: z.number().optional().describe("Multiply HSL luminance (0..1)"),
|
|
90
|
+
lumOff: z.number().optional().describe("Add to HSL luminance after lumMod (0..1)"),
|
|
91
|
+
tint: z.number().optional().describe("Blend towards white (0..1)"),
|
|
92
|
+
shade: z.number().optional().describe("Blend towards black (0..1)"),
|
|
93
|
+
alpha: z.number().optional().describe("Opacity fraction, 1 = opaque")
|
|
94
|
+
});
|
|
69
95
|
var AddElementSchema = z.object({
|
|
70
96
|
filePath: z.string().describe("Path to the PPTX file"),
|
|
71
97
|
slideIndex: z.number().int().min(0).describe("Zero-based slide index"),
|
|
@@ -110,6 +136,12 @@ var UpdateElementSchema = z.object({
|
|
|
110
136
|
fontSize: z.number().optional(),
|
|
111
137
|
fontFamily: z.string().optional(),
|
|
112
138
|
fontColor: z.string().optional(),
|
|
139
|
+
/**
|
|
140
|
+
* A theme colour for the run text. Wins on save; resolves `fontColor`
|
|
141
|
+
* immediately when `fontColor` was not also given. Passing `fontColor`
|
|
142
|
+
* alone clears a previously-set text theme colour.
|
|
143
|
+
*/
|
|
144
|
+
fontThemeColor: ThemeColorRefSchema.optional(),
|
|
113
145
|
bold: z.boolean().optional(),
|
|
114
146
|
italic: z.boolean().optional(),
|
|
115
147
|
underline: z.boolean().optional(),
|
|
@@ -120,7 +152,13 @@ var UpdateElementSchema = z.object({
|
|
|
120
152
|
opacity: z.number().min(0).max(1).optional(),
|
|
121
153
|
hidden: z.boolean().optional(),
|
|
122
154
|
flipH: z.boolean().optional(),
|
|
123
|
-
flipV: z.boolean().optional()
|
|
155
|
+
flipV: z.boolean().optional(),
|
|
156
|
+
altText: z.string().optional().describe(
|
|
157
|
+
"Accessibility description (p:cNvPr/@descr) for image, picture, table, chart, smartArt, ole, media, text, shape and connector elements"
|
|
158
|
+
),
|
|
159
|
+
title: z.string().optional().describe(
|
|
160
|
+
"Accessibility title (p:cNvPr/@title) for table, chart, smartArt, ole, media, text, shape and connector elements (not pictures)"
|
|
161
|
+
)
|
|
124
162
|
});
|
|
125
163
|
var RenameElementSchema = z.object({
|
|
126
164
|
filePath: z.string().describe("Path to the PPTX file"),
|
|
@@ -182,6 +220,32 @@ var BatchUpdateElementsSchema = z.object({
|
|
|
182
220
|
opacity: z.number().min(0).max(1).optional(),
|
|
183
221
|
hidden: z.boolean().optional()
|
|
184
222
|
});
|
|
223
|
+
var ThemeColorRefSchema2 = z.object({
|
|
224
|
+
scheme: z.enum([
|
|
225
|
+
"dk1",
|
|
226
|
+
"lt1",
|
|
227
|
+
"dk2",
|
|
228
|
+
"lt2",
|
|
229
|
+
"accent1",
|
|
230
|
+
"accent2",
|
|
231
|
+
"accent3",
|
|
232
|
+
"accent4",
|
|
233
|
+
"accent5",
|
|
234
|
+
"accent6",
|
|
235
|
+
"hlink",
|
|
236
|
+
"folHlink",
|
|
237
|
+
"bg1",
|
|
238
|
+
"tx1",
|
|
239
|
+
"bg2",
|
|
240
|
+
"tx2",
|
|
241
|
+
"phClr"
|
|
242
|
+
]),
|
|
243
|
+
lumMod: z.number().optional().describe("Multiply HSL luminance (0..1)"),
|
|
244
|
+
lumOff: z.number().optional().describe("Add to HSL luminance after lumMod (0..1)"),
|
|
245
|
+
tint: z.number().optional().describe("Blend towards white (0..1)"),
|
|
246
|
+
shade: z.number().optional().describe("Blend towards black (0..1)"),
|
|
247
|
+
alpha: z.number().optional().describe("Opacity fraction, 1 = opaque")
|
|
248
|
+
});
|
|
185
249
|
var UpdateTableCellsSchema = z.object({
|
|
186
250
|
filePath: z.string().describe("Path to the PPTX file"),
|
|
187
251
|
slideIndex: z.number().int().min(0),
|
|
@@ -208,6 +272,14 @@ var UpdateElementStyleSchema = z.object({
|
|
|
208
272
|
slideIndex: z.number().int().min(0),
|
|
209
273
|
elementId: z.string(),
|
|
210
274
|
fillColor: z.string().optional(),
|
|
275
|
+
/**
|
|
276
|
+
* A theme colour for the fill (e.g. `{ scheme: 'accent1', lumMod: 0.8 }`
|
|
277
|
+
* for "Accent 1, Lighter 80%"). When set, the shape saves as
|
|
278
|
+
* `<a:schemeClr>` and keeps following the theme after a later theme
|
|
279
|
+
* change, instead of freezing today's resolved hex. Setting `fillColor`
|
|
280
|
+
* without this clears any previously-set fill theme colour.
|
|
281
|
+
*/
|
|
282
|
+
fillThemeColor: ThemeColorRefSchema2.optional(),
|
|
211
283
|
fillMode: z.string().optional(),
|
|
212
284
|
fillGradientStops: z.array(
|
|
213
285
|
z.object({
|
|
@@ -220,6 +292,8 @@ var UpdateElementStyleSchema = z.object({
|
|
|
220
292
|
fillGradientType: z.string().optional(),
|
|
221
293
|
fillOpacity: z.number().optional(),
|
|
222
294
|
strokeColor: z.string().optional(),
|
|
295
|
+
/** A theme colour for the outline; see `fillThemeColor` above. */
|
|
296
|
+
strokeThemeColor: ThemeColorRefSchema2.optional(),
|
|
223
297
|
strokeWidth: z.number().optional(),
|
|
224
298
|
strokeDash: z.string().optional(),
|
|
225
299
|
strokeOpacity: z.number().optional(),
|
|
@@ -239,7 +313,12 @@ var UpdateElementStyleSchema = z.object({
|
|
|
239
313
|
brightness: z.number().optional(),
|
|
240
314
|
contrast: z.number().optional(),
|
|
241
315
|
grayscale: z.boolean().optional(),
|
|
242
|
-
altText: z.string().optional()
|
|
316
|
+
altText: z.string().optional().describe(
|
|
317
|
+
"Accessibility description (p:cNvPr/@descr) for image, picture, table, chart, smartArt, ole, media, text, shape and connector elements"
|
|
318
|
+
),
|
|
319
|
+
title: z.string().optional().describe(
|
|
320
|
+
"Accessibility title (p:cNvPr/@title) for table, chart, smartArt, ole, media, text, shape and connector elements (not pictures)"
|
|
321
|
+
)
|
|
243
322
|
});
|
|
244
323
|
var FindTextSchema = z.object({
|
|
245
324
|
filePath: z.string().describe("Path to the PPTX file"),
|
|
@@ -266,6 +345,116 @@ var ManageCommentsSchema = z.object({
|
|
|
266
345
|
resolved: z.boolean().optional(),
|
|
267
346
|
includeResolved: z.boolean().optional()
|
|
268
347
|
});
|
|
348
|
+
var TABLE_STYLE_SECTIONS = [
|
|
349
|
+
"wholeTbl",
|
|
350
|
+
"band1H",
|
|
351
|
+
"band2H",
|
|
352
|
+
"band1V",
|
|
353
|
+
"band2V",
|
|
354
|
+
"lastCol",
|
|
355
|
+
"firstCol",
|
|
356
|
+
"lastRow",
|
|
357
|
+
"seCell",
|
|
358
|
+
"swCell",
|
|
359
|
+
"firstRow",
|
|
360
|
+
"neCell",
|
|
361
|
+
"nwCell"
|
|
362
|
+
];
|
|
363
|
+
var TableStyleSimpleColorSchema = z.object({
|
|
364
|
+
schemeColor: z.string().describe('Theme colour key (e.g. "accent1"), or "" for a non-scheme fill'),
|
|
365
|
+
tint: z.number().optional().describe("0-100000"),
|
|
366
|
+
shade: z.number().optional().describe("0-100000"),
|
|
367
|
+
color: z.string().optional().describe('Explicit hex colour, e.g. "#FF8800"')
|
|
368
|
+
});
|
|
369
|
+
var TableStyleFillSchema = z.object({
|
|
370
|
+
schemeColor: z.string(),
|
|
371
|
+
tint: z.number().optional(),
|
|
372
|
+
shade: z.number().optional(),
|
|
373
|
+
color: z.string().optional(),
|
|
374
|
+
noFill: z.boolean().optional(),
|
|
375
|
+
gradient: z.object({
|
|
376
|
+
type: z.enum(["linear", "radial"]),
|
|
377
|
+
angle: z.number().optional(),
|
|
378
|
+
stops: z.array(z.object({ position: z.number(), fill: TableStyleSimpleColorSchema }))
|
|
379
|
+
}).optional(),
|
|
380
|
+
pattern: z.object({
|
|
381
|
+
preset: z.string(),
|
|
382
|
+
foreground: TableStyleSimpleColorSchema.optional(),
|
|
383
|
+
background: TableStyleSimpleColorSchema.optional()
|
|
384
|
+
}).optional(),
|
|
385
|
+
image: z.object({ path: z.string().optional(), data: z.string().optional() }).optional()
|
|
386
|
+
});
|
|
387
|
+
var TableStyleTextSchema = z.object({
|
|
388
|
+
bold: z.boolean().optional(),
|
|
389
|
+
italic: z.boolean().optional(),
|
|
390
|
+
underline: z.boolean().optional(),
|
|
391
|
+
fontSchemeColor: z.string().optional(),
|
|
392
|
+
fontTint: z.number().optional(),
|
|
393
|
+
fontShade: z.number().optional(),
|
|
394
|
+
fontColor: z.string().optional(),
|
|
395
|
+
fontFace: z.string().optional(),
|
|
396
|
+
fontRefIdx: z.string().optional()
|
|
397
|
+
});
|
|
398
|
+
var TableStyleBorderSideSchema = z.object({
|
|
399
|
+
width: z.number().optional(),
|
|
400
|
+
dash: z.string().optional(),
|
|
401
|
+
fill: TableStyleSimpleColorSchema.optional(),
|
|
402
|
+
color: z.string().optional(),
|
|
403
|
+
noFill: z.boolean().optional()
|
|
404
|
+
});
|
|
405
|
+
var TableStyleBordersSchema = z.object({
|
|
406
|
+
left: TableStyleBorderSideSchema.optional(),
|
|
407
|
+
right: TableStyleBorderSideSchema.optional(),
|
|
408
|
+
top: TableStyleBorderSideSchema.optional(),
|
|
409
|
+
bottom: TableStyleBorderSideSchema.optional(),
|
|
410
|
+
insideH: TableStyleBorderSideSchema.optional(),
|
|
411
|
+
insideV: TableStyleBorderSideSchema.optional(),
|
|
412
|
+
tl2br: TableStyleBorderSideSchema.optional(),
|
|
413
|
+
tr2bl: TableStyleBorderSideSchema.optional()
|
|
414
|
+
});
|
|
415
|
+
var TableStyleCell3DSchema = z.object({
|
|
416
|
+
bevelWidth: z.number().optional(),
|
|
417
|
+
bevelHeight: z.number().optional(),
|
|
418
|
+
bevelPreset: z.string().optional(),
|
|
419
|
+
material: z.string().optional(),
|
|
420
|
+
lightRig: z.string().optional(),
|
|
421
|
+
lightRigDirection: z.string().optional()
|
|
422
|
+
});
|
|
423
|
+
var SetTableStyleSectionSchema = z.object({
|
|
424
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
425
|
+
styleId: z.string().describe('Table style GUID, e.g. "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}"'),
|
|
426
|
+
section: z.enum(TABLE_STYLE_SECTIONS).describe("Which of the 13 CT_TableStyle parts to patch"),
|
|
427
|
+
styleName: z.string().optional().describe("Rename the style"),
|
|
428
|
+
fill: TableStyleFillSchema.optional(),
|
|
429
|
+
text: TableStyleTextSchema.optional(),
|
|
430
|
+
borders: TableStyleBordersSchema.optional(),
|
|
431
|
+
cell3D: TableStyleCell3DSchema.optional()
|
|
432
|
+
});
|
|
433
|
+
var CreateTableStyleSchema = z.object({
|
|
434
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
435
|
+
styleName: z.string().describe("Display name for the new style"),
|
|
436
|
+
basedOnStyleId: z.string().optional().describe("Deep-clone every section from this existing style GUID as the starting point"),
|
|
437
|
+
setAsDefault: z.boolean().optional().describe("Repoint ppt/tableStyles.xml's default style at the new one")
|
|
438
|
+
});
|
|
439
|
+
var DeleteTableStyleSchema = z.object({
|
|
440
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
441
|
+
styleId: z.string().describe("Table style GUID to remove"),
|
|
442
|
+
force: z.boolean().optional().describe("Delete even though a table on this deck still references the style")
|
|
443
|
+
});
|
|
444
|
+
var AssignTableStyleSchema = z.object({
|
|
445
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
446
|
+
slideIndex: z.number().int().min(0),
|
|
447
|
+
elementId: z.string().describe("Table element ID"),
|
|
448
|
+
styleId: z.string().optional().describe(
|
|
449
|
+
"Table style GUID to assign. Defaults to the presentation's current default table style when omitted."
|
|
450
|
+
),
|
|
451
|
+
bandedRows: z.boolean().optional(),
|
|
452
|
+
bandedColumns: z.boolean().optional(),
|
|
453
|
+
firstRowHeader: z.boolean().optional(),
|
|
454
|
+
lastRow: z.boolean().optional(),
|
|
455
|
+
firstCol: z.boolean().optional(),
|
|
456
|
+
lastCol: z.boolean().optional()
|
|
457
|
+
});
|
|
269
458
|
var GetThemeInfoSchema = z.object({
|
|
270
459
|
filePath: z.string().describe("Path to the PPTX file")
|
|
271
460
|
});
|
|
@@ -295,6 +484,139 @@ var UpdateThemeFontsSchema = z.object({
|
|
|
295
484
|
majorFont: z.string().optional().describe("Heading (major) font family"),
|
|
296
485
|
minorFont: z.string().optional().describe("Body (minor) font family")
|
|
297
486
|
});
|
|
487
|
+
var MarkerSymbolSchema = z.enum([
|
|
488
|
+
"circle",
|
|
489
|
+
"dash",
|
|
490
|
+
"diamond",
|
|
491
|
+
"dot",
|
|
492
|
+
"none",
|
|
493
|
+
"picture",
|
|
494
|
+
"plus",
|
|
495
|
+
"square",
|
|
496
|
+
"star",
|
|
497
|
+
"triangle",
|
|
498
|
+
"x",
|
|
499
|
+
"auto"
|
|
500
|
+
]);
|
|
501
|
+
var DataLabelPositionSchema = z.enum([
|
|
502
|
+
"bestFit",
|
|
503
|
+
"b",
|
|
504
|
+
"ctr",
|
|
505
|
+
"inBase",
|
|
506
|
+
"inEnd",
|
|
507
|
+
"l",
|
|
508
|
+
"outEnd",
|
|
509
|
+
"r",
|
|
510
|
+
"t"
|
|
511
|
+
]);
|
|
512
|
+
var FormatChartDataPointSchema = z.object({
|
|
513
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
514
|
+
slideIndex: z.number().int().min(0).describe("Zero-based slide index"),
|
|
515
|
+
elementId: z.string().describe("Chart element ID"),
|
|
516
|
+
seriesIndex: z.number().int().min(0).describe("Zero-based series index"),
|
|
517
|
+
pointIndex: z.number().int().min(0).describe("Zero-based data point (category) index"),
|
|
518
|
+
fillColor: z.string().optional().describe("Fill colour (hex) for this point only"),
|
|
519
|
+
strokeColor: z.string().optional().describe("Outline colour (hex) for this point only"),
|
|
520
|
+
strokeWidth: z.number().optional().describe("Outline width in points"),
|
|
521
|
+
strokeDashStyle: z.string().optional().describe("Outline dash style, e.g. solid, dash, dashDot, sysDot"),
|
|
522
|
+
clearStyle: z.boolean().optional().describe("Remove all per-point shape formatting"),
|
|
523
|
+
explosion: z.number().nullable().optional().describe("Pie/doughnut slice pull-out distance 0-100; null clears it"),
|
|
524
|
+
marker: z.object({
|
|
525
|
+
symbol: MarkerSymbolSchema.optional(),
|
|
526
|
+
size: z.number().optional(),
|
|
527
|
+
fillColor: z.string().optional()
|
|
528
|
+
}).nullable().optional().describe("Per-point marker override (line/scatter/bubble); null clears it")
|
|
529
|
+
});
|
|
530
|
+
var FormatChartDataLabelSchema = z.object({
|
|
531
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
532
|
+
slideIndex: z.number().int().min(0).describe("Zero-based slide index"),
|
|
533
|
+
elementId: z.string().describe("Chart element ID"),
|
|
534
|
+
seriesIndex: z.number().int().min(0).describe("Zero-based series index"),
|
|
535
|
+
pointIndex: z.number().int().min(0).describe("Zero-based data point (category) index"),
|
|
536
|
+
remove: z.boolean().optional().describe("Remove the per-point label override entirely"),
|
|
537
|
+
showValue: z.boolean().optional(),
|
|
538
|
+
showCategory: z.boolean().optional(),
|
|
539
|
+
showSeriesName: z.boolean().optional(),
|
|
540
|
+
showPercent: z.boolean().optional(),
|
|
541
|
+
showLegendKey: z.boolean().optional(),
|
|
542
|
+
position: DataLabelPositionSchema.optional(),
|
|
543
|
+
text: z.string().optional().describe("Custom label text override; '' clears it"),
|
|
544
|
+
spPr: z.object({
|
|
545
|
+
fillColor: z.string().optional(),
|
|
546
|
+
strokeColor: z.string().optional(),
|
|
547
|
+
strokeWidth: z.number().optional(),
|
|
548
|
+
strokeDashStyle: z.string().optional()
|
|
549
|
+
}).nullable().optional().describe("This label's own fill/line formatting; null removes it"),
|
|
550
|
+
txPr: z.object({
|
|
551
|
+
fontFamily: z.string().optional(),
|
|
552
|
+
fontSize: z.number().optional(),
|
|
553
|
+
bold: z.boolean().optional(),
|
|
554
|
+
italic: z.boolean().optional(),
|
|
555
|
+
color: z.string().optional()
|
|
556
|
+
}).nullable().optional().describe("This label's own font; null removes it")
|
|
557
|
+
});
|
|
558
|
+
var FormatChartSeriesSchema = z.object({
|
|
559
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
560
|
+
slideIndex: z.number().int().min(0).describe("Zero-based slide index"),
|
|
561
|
+
elementId: z.string().describe("Chart element ID"),
|
|
562
|
+
seriesIndex: z.number().int().min(0).describe("Zero-based series index"),
|
|
563
|
+
marker: z.object({
|
|
564
|
+
symbol: MarkerSymbolSchema.optional(),
|
|
565
|
+
size: z.number().optional(),
|
|
566
|
+
fillColor: z.string().optional(),
|
|
567
|
+
strokeColor: z.string().optional(),
|
|
568
|
+
strokeWidth: z.number().optional(),
|
|
569
|
+
strokeDashStyle: z.string().optional()
|
|
570
|
+
}).nullable().optional().describe("Series marker (line/scatter/bubble/radar); null removes it"),
|
|
571
|
+
trendline: z.object({
|
|
572
|
+
trendlineType: z.enum([
|
|
573
|
+
"linear",
|
|
574
|
+
"exponential",
|
|
575
|
+
"logarithmic",
|
|
576
|
+
"polynomial",
|
|
577
|
+
"power",
|
|
578
|
+
"movingAvg"
|
|
579
|
+
]),
|
|
580
|
+
color: z.string().optional(),
|
|
581
|
+
lineWidth: z.number().optional().describe("Trendline width in points"),
|
|
582
|
+
lineDashStyle: z.string().optional(),
|
|
583
|
+
order: z.number().optional().describe("Polynomial order, 2-6"),
|
|
584
|
+
period: z.number().optional().describe("Moving-average period"),
|
|
585
|
+
displayEq: z.boolean().optional(),
|
|
586
|
+
displayRSq: z.boolean().optional()
|
|
587
|
+
}).nullable().optional().describe("Series trendline; null removes it"),
|
|
588
|
+
errorBars: z.object({
|
|
589
|
+
direction: z.enum(["x", "y"]),
|
|
590
|
+
barType: z.enum(["both", "minus", "plus"]),
|
|
591
|
+
valType: z.enum(["cust", "fixedVal", "percentage", "stdDev", "stdErr"]),
|
|
592
|
+
val: z.number().optional(),
|
|
593
|
+
customPlus: z.array(z.number()).optional(),
|
|
594
|
+
customMinus: z.array(z.number()).optional(),
|
|
595
|
+
noEndCap: z.boolean().optional(),
|
|
596
|
+
color: z.string().optional(),
|
|
597
|
+
width: z.number().optional().describe("Error-bar line width in points"),
|
|
598
|
+
dashStyle: z.string().optional()
|
|
599
|
+
}).nullable().optional().describe("Series error bars; null removes them")
|
|
600
|
+
});
|
|
601
|
+
var SetChartHelperLineSchema = z.object({
|
|
602
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
603
|
+
slideIndex: z.number().int().min(0).describe("Zero-based slide index"),
|
|
604
|
+
elementId: z.string().describe("Chart element ID"),
|
|
605
|
+
line: z.enum(["dropLines", "hiLowLines"]).describe("Which helper line to set"),
|
|
606
|
+
style: z.object({
|
|
607
|
+
color: z.string().optional(),
|
|
608
|
+
width: z.number().optional(),
|
|
609
|
+
dashStyle: z.string().optional()
|
|
610
|
+
}).nullable().describe("Line colour/width/dash; {} shows the line with defaults, null removes it")
|
|
611
|
+
});
|
|
612
|
+
var SetChartColorMapOverrideSchema = z.object({
|
|
613
|
+
filePath: z.string().describe("Path to the PPTX file"),
|
|
614
|
+
slideIndex: z.number().int().min(0).describe("Zero-based slide index"),
|
|
615
|
+
elementId: z.string().describe("Chart element ID"),
|
|
616
|
+
overrides: z.record(z.string(), z.string()).nullable().describe(
|
|
617
|
+
'Theme colour role remap (bg1/tx1/bg2/tx2/accent1-6/hlink/folHlink -> role), e.g. { "accent1": "accent2" }; null removes the override'
|
|
618
|
+
)
|
|
619
|
+
});
|
|
298
620
|
var UpdateChartSchema = z.object({
|
|
299
621
|
filePath: z.string().describe("Path to the PPTX file"),
|
|
300
622
|
slideIndex: z.number().int().min(0).describe("Zero-based slide index"),
|
|
@@ -557,39 +879,6 @@ var ApplyLayoutSchema = z.object({
|
|
|
557
879
|
layoutName: z.string().optional().describe("Layout name to apply"),
|
|
558
880
|
layoutType: z.string().optional().describe("Layout type to apply (alternative to name)")
|
|
559
881
|
});
|
|
560
|
-
|
|
561
|
-
// src/tools/chart-pareto.ts
|
|
562
|
-
function cumulativePercent(values) {
|
|
563
|
-
const total = values.reduce((sum, value) => sum + value, 0);
|
|
564
|
-
if (total === 0) {
|
|
565
|
-
return values.map(() => 0);
|
|
566
|
-
}
|
|
567
|
-
let running = 0;
|
|
568
|
-
return values.map((value) => {
|
|
569
|
-
running += value;
|
|
570
|
-
return Math.round(running / total * 1e4) / 100;
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
function applyParetoChartTypeAlias(chart, requestedType) {
|
|
574
|
-
if (requestedType.trim().toLowerCase() !== "pareto" || !chart.chartData) {
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
const data = chart.chartData;
|
|
578
|
-
data.chartType = "histogram";
|
|
579
|
-
const [frequency] = data.series;
|
|
580
|
-
if (!frequency) {
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
frequency.histogramOptions = { ...frequency.histogramOptions, layout: "histogram" };
|
|
584
|
-
const hasParetoLine = data.series.some((s) => s.histogramOptions?.layout === "pareto");
|
|
585
|
-
if (!hasParetoLine) {
|
|
586
|
-
data.series.push({
|
|
587
|
-
name: "Cumulative %",
|
|
588
|
-
values: cumulativePercent(frequency.values),
|
|
589
|
-
histogramOptions: { layout: "pareto" }
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
882
|
function generateElementId() {
|
|
594
883
|
return `el-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
595
884
|
}
|
|
@@ -697,6 +986,133 @@ function validateSlideIndex(slideIndex, slideCount) {
|
|
|
697
986
|
return null;
|
|
698
987
|
}
|
|
699
988
|
|
|
989
|
+
// src/tools/chart-formatting-tools.ts
|
|
990
|
+
function findChartElement(ctx, slideIndex, elementId) {
|
|
991
|
+
const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
|
|
992
|
+
if (err) {
|
|
993
|
+
throw new Error(err);
|
|
994
|
+
}
|
|
995
|
+
const slide = ctx.pptxData.slides[slideIndex];
|
|
996
|
+
const el = slide.elements.find((e) => e.id === elementId);
|
|
997
|
+
if (!el) {
|
|
998
|
+
throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
|
|
999
|
+
}
|
|
1000
|
+
if (el.type !== "chart") {
|
|
1001
|
+
throw new Error(`Element '${elementId}' is not a chart.`);
|
|
1002
|
+
}
|
|
1003
|
+
return el;
|
|
1004
|
+
}
|
|
1005
|
+
function formatChartDataPoint(ctx, params) {
|
|
1006
|
+
const chart = findChartElement(ctx, params.slideIndex, params.elementId);
|
|
1007
|
+
if (params.clearStyle) {
|
|
1008
|
+
setChartDataPointStyle(chart, params.seriesIndex, params.pointIndex, null);
|
|
1009
|
+
} else if (params.fillColor !== void 0 || params.strokeColor !== void 0 || params.strokeWidth !== void 0 || params.strokeDashStyle !== void 0) {
|
|
1010
|
+
setChartDataPointStyle(chart, params.seriesIndex, params.pointIndex, {
|
|
1011
|
+
...params.fillColor !== void 0 ? { fillColor: params.fillColor } : {},
|
|
1012
|
+
...params.strokeColor !== void 0 ? { strokeColor: params.strokeColor } : {},
|
|
1013
|
+
...params.strokeWidth !== void 0 ? { strokeWidth: params.strokeWidth } : {},
|
|
1014
|
+
...params.strokeDashStyle !== void 0 ? { strokeDashStyle: params.strokeDashStyle } : {}
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
if (params.explosion !== void 0) {
|
|
1018
|
+
setChartDataPointExplosion(chart, params.seriesIndex, params.pointIndex, params.explosion);
|
|
1019
|
+
}
|
|
1020
|
+
if (params.marker !== void 0) {
|
|
1021
|
+
setChartDataPointMarker(chart, params.seriesIndex, params.pointIndex, params.marker);
|
|
1022
|
+
}
|
|
1023
|
+
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
1024
|
+
}
|
|
1025
|
+
function formatChartDataLabel(ctx, params) {
|
|
1026
|
+
const chart = findChartElement(ctx, params.slideIndex, params.elementId);
|
|
1027
|
+
if (params.remove) {
|
|
1028
|
+
setChartDataPointLabel(chart, params.seriesIndex, params.pointIndex, null);
|
|
1029
|
+
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
1030
|
+
}
|
|
1031
|
+
setChartDataPointLabel(chart, params.seriesIndex, params.pointIndex, {
|
|
1032
|
+
...params.showValue !== void 0 ? { showValue: params.showValue } : {},
|
|
1033
|
+
...params.showCategory !== void 0 ? { showCategory: params.showCategory } : {},
|
|
1034
|
+
...params.showSeriesName !== void 0 ? { showSeriesName: params.showSeriesName } : {},
|
|
1035
|
+
...params.showPercent !== void 0 ? { showPercent: params.showPercent } : {},
|
|
1036
|
+
...params.showLegendKey !== void 0 ? { showLegendKey: params.showLegendKey } : {},
|
|
1037
|
+
...params.position !== void 0 ? { position: params.position } : {},
|
|
1038
|
+
...params.text !== void 0 ? { text: params.text } : {},
|
|
1039
|
+
...params.spPr !== void 0 ? { spPr: params.spPr } : {},
|
|
1040
|
+
...params.txPr !== void 0 ? { txPr: params.txPr } : {}
|
|
1041
|
+
});
|
|
1042
|
+
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
1043
|
+
}
|
|
1044
|
+
function formatChartSeries(ctx, params) {
|
|
1045
|
+
const chart = findChartElement(ctx, params.slideIndex, params.elementId);
|
|
1046
|
+
if (params.marker !== void 0) {
|
|
1047
|
+
if (params.marker === null) {
|
|
1048
|
+
setChartSeriesMarker(chart, params.seriesIndex, null);
|
|
1049
|
+
} else if (params.marker.strokeColor !== void 0 || params.marker.strokeWidth !== void 0 || params.marker.strokeDashStyle !== void 0) {
|
|
1050
|
+
setChartSeriesMarker(chart, params.seriesIndex, {
|
|
1051
|
+
symbol: params.marker.symbol ?? "circle",
|
|
1052
|
+
...params.marker.size !== void 0 ? { size: params.marker.size } : {},
|
|
1053
|
+
spPr: {
|
|
1054
|
+
...params.marker.fillColor !== void 0 ? { fillColor: params.marker.fillColor } : {},
|
|
1055
|
+
...params.marker.strokeColor !== void 0 ? { strokeColor: params.marker.strokeColor } : {},
|
|
1056
|
+
...params.marker.strokeWidth !== void 0 ? { strokeWidth: params.marker.strokeWidth } : {},
|
|
1057
|
+
...params.marker.strokeDashStyle !== void 0 ? { strokeDashStyle: params.marker.strokeDashStyle } : {}
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
} else {
|
|
1061
|
+
setChartSeriesMarker(chart, params.seriesIndex, params.marker);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
if (params.trendline !== void 0) {
|
|
1065
|
+
setChartSeriesTrendline(chart, params.seriesIndex, params.trendline);
|
|
1066
|
+
}
|
|
1067
|
+
if (params.errorBars !== void 0) {
|
|
1068
|
+
setChartSeriesErrorBars(chart, params.seriesIndex, params.errorBars);
|
|
1069
|
+
}
|
|
1070
|
+
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
1071
|
+
}
|
|
1072
|
+
function setChartHelperLineT(ctx, params) {
|
|
1073
|
+
const chart = findChartElement(ctx, params.slideIndex, params.elementId);
|
|
1074
|
+
setChartHelperLine(chart, params.line, params.style);
|
|
1075
|
+
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
1076
|
+
}
|
|
1077
|
+
function setChartColorMapOverrideT(ctx, params) {
|
|
1078
|
+
const chart = findChartElement(ctx, params.slideIndex, params.elementId);
|
|
1079
|
+
setChartColorMapOverride(chart, params.overrides);
|
|
1080
|
+
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// src/tools/chart-pareto.ts
|
|
1084
|
+
function cumulativePercent(values) {
|
|
1085
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
1086
|
+
if (total === 0) {
|
|
1087
|
+
return values.map(() => 0);
|
|
1088
|
+
}
|
|
1089
|
+
let running = 0;
|
|
1090
|
+
return values.map((value) => {
|
|
1091
|
+
running += value;
|
|
1092
|
+
return Math.round(running / total * 1e4) / 100;
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
function applyParetoChartTypeAlias(chart, requestedType) {
|
|
1096
|
+
if (requestedType.trim().toLowerCase() !== "pareto" || !chart.chartData) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
const data = chart.chartData;
|
|
1100
|
+
data.chartType = "histogram";
|
|
1101
|
+
const [frequency] = data.series;
|
|
1102
|
+
if (!frequency) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
frequency.histogramOptions = { ...frequency.histogramOptions, layout: "histogram" };
|
|
1106
|
+
const hasParetoLine = data.series.some((s) => s.histogramOptions?.layout === "pareto");
|
|
1107
|
+
if (!hasParetoLine) {
|
|
1108
|
+
data.series.push({
|
|
1109
|
+
name: "Cumulative %",
|
|
1110
|
+
values: cumulativePercent(frequency.values),
|
|
1111
|
+
histogramOptions: { layout: "pareto" }
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
700
1116
|
// src/tools/chart-tools.ts
|
|
701
1117
|
function updateChart(ctx, params) {
|
|
702
1118
|
const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
|
|
@@ -837,7 +1253,7 @@ function createChart(ctx, params) {
|
|
|
837
1253
|
result: { elementId: chart.id, slideIndex: params.slideIndex }
|
|
838
1254
|
};
|
|
839
1255
|
}
|
|
840
|
-
function
|
|
1256
|
+
function findChartElement2(ctx, slideIndex, elementId) {
|
|
841
1257
|
const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
|
|
842
1258
|
if (err) {
|
|
843
1259
|
throw new Error(err);
|
|
@@ -867,17 +1283,17 @@ function buildShape(input) {
|
|
|
867
1283
|
};
|
|
868
1284
|
}
|
|
869
1285
|
function listChartUserShapesT(ctx, params) {
|
|
870
|
-
const chart =
|
|
1286
|
+
const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
|
|
871
1287
|
return { pptxData: ctx.pptxData, dirty: false, result: { shapes: listChartUserShapes(chart) } };
|
|
872
1288
|
}
|
|
873
1289
|
function addChartUserShapeT(ctx, params) {
|
|
874
|
-
const chart =
|
|
1290
|
+
const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
|
|
875
1291
|
addChartUserShape(chart, buildShape(params.shape));
|
|
876
1292
|
const index = (chart.chartData?.userShapes?.length ?? 1) - 1;
|
|
877
1293
|
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId, index } };
|
|
878
1294
|
}
|
|
879
1295
|
function updateChartUserShapeT(ctx, params) {
|
|
880
|
-
const chart =
|
|
1296
|
+
const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
|
|
881
1297
|
const patch = {
|
|
882
1298
|
...params.patch.kind ? { kind: params.patch.kind } : {},
|
|
883
1299
|
...params.patch.anchor ? { anchor: params.patch.anchor } : {},
|
|
@@ -894,7 +1310,7 @@ function updateChartUserShapeT(ctx, params) {
|
|
|
894
1310
|
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
895
1311
|
}
|
|
896
1312
|
function removeChartUserShapeT(ctx, params) {
|
|
897
|
-
const chart =
|
|
1313
|
+
const chart = findChartElement2(ctx, params.slideIndex, params.elementId);
|
|
898
1314
|
removeChartUserShape(chart, params.index);
|
|
899
1315
|
return { pptxData: ctx.pptxData, dirty: true, result: { elementId: params.elementId } };
|
|
900
1316
|
}
|
|
@@ -1224,85 +1640,319 @@ async function convertToMarkdown(ctx, params) {
|
|
|
1224
1640
|
}
|
|
1225
1641
|
};
|
|
1226
1642
|
}
|
|
1227
|
-
|
|
1643
|
+
var ALT_TEXT_ELEMENT_TYPES = /* @__PURE__ */ new Set(["image", "picture", "table", "chart", "smartArt", "ole", "media", "text", "shape", "connector"]);
|
|
1644
|
+
var TITLE_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
1645
|
+
"table",
|
|
1646
|
+
"chart",
|
|
1647
|
+
"smartArt",
|
|
1648
|
+
"ole",
|
|
1649
|
+
"media",
|
|
1650
|
+
"text",
|
|
1651
|
+
"shape",
|
|
1652
|
+
"connector"
|
|
1653
|
+
]);
|
|
1654
|
+
function applyElementAltText(el, altText) {
|
|
1655
|
+
if (!ALT_TEXT_ELEMENT_TYPES.has(el.type)) {
|
|
1656
|
+
throw new Error(
|
|
1657
|
+
`Element '${el.id}' (${el.type}) does not support altText; only ${[...ALT_TEXT_ELEMENT_TYPES].join(", ")} elements do.`
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
el.altText = altText;
|
|
1661
|
+
}
|
|
1662
|
+
function applyElementTitle(el, title) {
|
|
1663
|
+
if (!TITLE_ELEMENT_TYPES.has(el.type)) {
|
|
1664
|
+
throw new Error(
|
|
1665
|
+
`Element '${el.id}' (${el.type}) does not support title; only ${[...TITLE_ELEMENT_TYPES].join(", ")} elements do.`
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
el.title = title;
|
|
1669
|
+
}
|
|
1670
|
+
function updateElementStyle(ctx, params) {
|
|
1228
1671
|
const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
|
|
1229
1672
|
if (err) {
|
|
1230
1673
|
throw new Error(err);
|
|
1231
1674
|
}
|
|
1232
1675
|
const slide = ctx.pptxData.slides[params.slideIndex];
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
const el = {
|
|
1241
|
-
id,
|
|
1242
|
-
type: "text",
|
|
1243
|
-
x,
|
|
1244
|
-
y,
|
|
1245
|
-
width,
|
|
1246
|
-
height,
|
|
1247
|
-
text: params.text ?? "",
|
|
1248
|
-
textStyle: {
|
|
1249
|
-
fontSize: params.fontSize,
|
|
1250
|
-
fontFamily: params.fontFamily,
|
|
1251
|
-
color: params.fontColor,
|
|
1252
|
-
bold: params.bold,
|
|
1253
|
-
italic: params.italic,
|
|
1254
|
-
underline: params.underline,
|
|
1255
|
-
align: params.alignment
|
|
1256
|
-
},
|
|
1257
|
-
textSegments: [
|
|
1258
|
-
{
|
|
1259
|
-
text: params.text ?? "",
|
|
1260
|
-
style: {
|
|
1261
|
-
fontSize: params.fontSize,
|
|
1262
|
-
fontFamily: params.fontFamily,
|
|
1263
|
-
color: params.fontColor,
|
|
1264
|
-
bold: params.bold,
|
|
1265
|
-
italic: params.italic,
|
|
1266
|
-
underline: params.underline
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
]
|
|
1270
|
-
};
|
|
1271
|
-
slide.elements.push(el);
|
|
1272
|
-
break;
|
|
1676
|
+
const el = slide.elements.find((e) => e.id === params.elementId);
|
|
1677
|
+
if (!el) {
|
|
1678
|
+
throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
|
|
1679
|
+
}
|
|
1680
|
+
if ("shapeStyle" in el) {
|
|
1681
|
+
if (!el.shapeStyle) {
|
|
1682
|
+
el.shapeStyle = {};
|
|
1273
1683
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
fontFamily: params.fontFamily,
|
|
1287
|
-
color: params.fontColor,
|
|
1288
|
-
bold: params.bold,
|
|
1289
|
-
italic: params.italic,
|
|
1290
|
-
align: params.alignment
|
|
1291
|
-
} : void 0,
|
|
1292
|
-
shapeStyle: {
|
|
1293
|
-
fillColor: params.fillColor,
|
|
1294
|
-
strokeColor: params.strokeColor,
|
|
1295
|
-
strokeWidth: params.strokeWidth
|
|
1296
|
-
}
|
|
1297
|
-
};
|
|
1298
|
-
slide.elements.push(el);
|
|
1299
|
-
break;
|
|
1684
|
+
const ss = el.shapeStyle;
|
|
1685
|
+
if (params.fillThemeColor !== void 0) {
|
|
1686
|
+
ss.fillColorRef = params.fillThemeColor;
|
|
1687
|
+
const resolved = resolveThemeColorRef(params.fillThemeColor, ctx.pptxData.themeColorMap);
|
|
1688
|
+
if (params.fillColor !== void 0) {
|
|
1689
|
+
ss.fillColor = params.fillColor;
|
|
1690
|
+
} else if (resolved) {
|
|
1691
|
+
ss.fillColor = resolved;
|
|
1692
|
+
}
|
|
1693
|
+
} else if (params.fillColor !== void 0) {
|
|
1694
|
+
ss.fillColor = params.fillColor;
|
|
1695
|
+
ss.fillColorRef = void 0;
|
|
1300
1696
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1697
|
+
if (params.fillMode !== void 0) {
|
|
1698
|
+
ss.fillMode = params.fillMode;
|
|
1699
|
+
}
|
|
1700
|
+
if (params.fillGradientStops !== void 0) {
|
|
1701
|
+
ss.fillGradientStops = params.fillGradientStops;
|
|
1702
|
+
}
|
|
1703
|
+
if (params.fillGradientAngle !== void 0) {
|
|
1704
|
+
ss.fillGradientAngle = params.fillGradientAngle;
|
|
1705
|
+
}
|
|
1706
|
+
if (params.fillGradientType !== void 0) {
|
|
1707
|
+
ss.fillGradientType = params.fillGradientType;
|
|
1708
|
+
}
|
|
1709
|
+
if (params.fillOpacity !== void 0) {
|
|
1710
|
+
ss.fillOpacity = params.fillOpacity;
|
|
1711
|
+
}
|
|
1712
|
+
if (params.strokeThemeColor !== void 0) {
|
|
1713
|
+
ss.strokeColorRef = params.strokeThemeColor;
|
|
1714
|
+
const resolved = resolveThemeColorRef(params.strokeThemeColor, ctx.pptxData.themeColorMap);
|
|
1715
|
+
if (params.strokeColor !== void 0) {
|
|
1716
|
+
ss.strokeColor = params.strokeColor;
|
|
1717
|
+
} else if (resolved) {
|
|
1718
|
+
ss.strokeColor = resolved;
|
|
1719
|
+
}
|
|
1720
|
+
} else if (params.strokeColor !== void 0) {
|
|
1721
|
+
ss.strokeColor = params.strokeColor;
|
|
1722
|
+
ss.strokeColorRef = void 0;
|
|
1723
|
+
}
|
|
1724
|
+
if (params.strokeWidth !== void 0) {
|
|
1725
|
+
ss.strokeWidth = params.strokeWidth;
|
|
1726
|
+
}
|
|
1727
|
+
if (params.strokeDash !== void 0) {
|
|
1728
|
+
ss.strokeDash = params.strokeDash;
|
|
1729
|
+
}
|
|
1730
|
+
if (params.strokeOpacity !== void 0) {
|
|
1731
|
+
ss.strokeOpacity = params.strokeOpacity;
|
|
1732
|
+
}
|
|
1733
|
+
if (params.shadowColor !== void 0) {
|
|
1734
|
+
ss.shadowColor = params.shadowColor;
|
|
1735
|
+
}
|
|
1736
|
+
if (params.shadowBlur !== void 0) {
|
|
1737
|
+
ss.shadowBlur = params.shadowBlur;
|
|
1738
|
+
}
|
|
1739
|
+
if (params.shadowOffsetX !== void 0) {
|
|
1740
|
+
ss.shadowOffsetX = params.shadowOffsetX;
|
|
1741
|
+
}
|
|
1742
|
+
if (params.shadowOffsetY !== void 0) {
|
|
1743
|
+
ss.shadowOffsetY = params.shadowOffsetY;
|
|
1744
|
+
}
|
|
1745
|
+
if (params.shadowOpacity !== void 0) {
|
|
1746
|
+
ss.shadowOpacity = params.shadowOpacity;
|
|
1747
|
+
}
|
|
1748
|
+
if (params.glowColor !== void 0) {
|
|
1749
|
+
ss.glowColor = params.glowColor;
|
|
1750
|
+
}
|
|
1751
|
+
if (params.glowRadius !== void 0) {
|
|
1752
|
+
ss.glowRadius = params.glowRadius;
|
|
1753
|
+
}
|
|
1754
|
+
if (params.glowOpacity !== void 0) {
|
|
1755
|
+
ss.glowOpacity = params.glowOpacity;
|
|
1756
|
+
}
|
|
1757
|
+
if (params.softEdgeRadius !== void 0) {
|
|
1758
|
+
ss.softEdgeRadius = params.softEdgeRadius;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
if (params.altText !== void 0) {
|
|
1762
|
+
applyElementAltText(el, params.altText);
|
|
1763
|
+
}
|
|
1764
|
+
if (params.title !== void 0) {
|
|
1765
|
+
applyElementTitle(el, params.title);
|
|
1766
|
+
}
|
|
1767
|
+
if (el.type === "image" || el.type === "picture") {
|
|
1768
|
+
const img = el;
|
|
1769
|
+
if (params.cropLeft !== void 0) {
|
|
1770
|
+
img.cropLeft = params.cropLeft;
|
|
1771
|
+
}
|
|
1772
|
+
if (params.cropTop !== void 0) {
|
|
1773
|
+
img.cropTop = params.cropTop;
|
|
1774
|
+
}
|
|
1775
|
+
if (params.cropRight !== void 0) {
|
|
1776
|
+
img.cropRight = params.cropRight;
|
|
1777
|
+
}
|
|
1778
|
+
if (params.cropBottom !== void 0) {
|
|
1779
|
+
img.cropBottom = params.cropBottom;
|
|
1780
|
+
}
|
|
1781
|
+
if (params.brightness !== void 0 || params.contrast !== void 0 || params.grayscale !== void 0) {
|
|
1782
|
+
if (!img.imageEffects) {
|
|
1783
|
+
img.imageEffects = {};
|
|
1784
|
+
}
|
|
1785
|
+
if (params.brightness !== void 0) {
|
|
1786
|
+
img.imageEffects.brightness = params.brightness;
|
|
1787
|
+
}
|
|
1788
|
+
if (params.contrast !== void 0) {
|
|
1789
|
+
img.imageEffects.contrast = params.contrast;
|
|
1790
|
+
}
|
|
1791
|
+
if (params.grayscale !== void 0) {
|
|
1792
|
+
img.imageEffects.grayscale = params.grayscale;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
return {
|
|
1797
|
+
pptxData: ctx.pptxData,
|
|
1798
|
+
dirty: true,
|
|
1799
|
+
result: { elementId: params.elementId }
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function runAccessibilityCheck(ctx) {
|
|
1803
|
+
const issues = [];
|
|
1804
|
+
for (let si = 0; si < ctx.pptxData.slides.length; si++) {
|
|
1805
|
+
const slide = ctx.pptxData.slides[si];
|
|
1806
|
+
let slideHasText = false;
|
|
1807
|
+
for (const el of slide.elements) {
|
|
1808
|
+
if (el.type === "image" || el.type === "picture") {
|
|
1809
|
+
const img = el;
|
|
1810
|
+
if (!img.altText) {
|
|
1811
|
+
issues.push({
|
|
1812
|
+
slideIndex: si,
|
|
1813
|
+
elementId: el.id,
|
|
1814
|
+
severity: "error",
|
|
1815
|
+
message: `Image element '${el.id}' has no alt text.`
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
if (hasTextProperties(el)) {
|
|
1820
|
+
const textEl = el;
|
|
1821
|
+
slideHasText = true;
|
|
1822
|
+
if (!textEl.text || textEl.text.trim() === "") {
|
|
1823
|
+
issues.push({
|
|
1824
|
+
slideIndex: si,
|
|
1825
|
+
elementId: el.id,
|
|
1826
|
+
severity: "info",
|
|
1827
|
+
message: `Text element '${el.id}' is empty.`
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
const fs = textEl.textStyle?.fontSize;
|
|
1831
|
+
if (fs !== void 0 && fs < 10) {
|
|
1832
|
+
issues.push({
|
|
1833
|
+
slideIndex: si,
|
|
1834
|
+
elementId: el.id,
|
|
1835
|
+
severity: "warning",
|
|
1836
|
+
message: `Text element '${el.id}' has very small font size (${fs}pt < 10pt).`
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
const textColor = textEl.textStyle?.color?.toLowerCase();
|
|
1840
|
+
const bgColor = slide.backgroundColor?.toLowerCase();
|
|
1841
|
+
if (textColor && bgColor && textColor === bgColor) {
|
|
1842
|
+
issues.push({
|
|
1843
|
+
slideIndex: si,
|
|
1844
|
+
elementId: el.id,
|
|
1845
|
+
severity: "error",
|
|
1846
|
+
message: `Text element '${el.id}' has same color as slide background (${textColor}).`
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
if (!slideHasText) {
|
|
1852
|
+
issues.push({
|
|
1853
|
+
slideIndex: si,
|
|
1854
|
+
severity: "warning",
|
|
1855
|
+
message: `Slide ${si + 1} contains no text elements.`
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
1860
|
+
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
1861
|
+
const infoCount = issues.filter((i) => i.severity === "info").length;
|
|
1862
|
+
return {
|
|
1863
|
+
pptxData: ctx.pptxData,
|
|
1864
|
+
dirty: false,
|
|
1865
|
+
result: {
|
|
1866
|
+
slideCount: ctx.pptxData.slides.length,
|
|
1867
|
+
totalIssues: issues.length,
|
|
1868
|
+
errorCount,
|
|
1869
|
+
warningCount,
|
|
1870
|
+
infoCount,
|
|
1871
|
+
issues
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
// src/tools/element-tools.ts
|
|
1877
|
+
function addElement(ctx, params) {
|
|
1878
|
+
const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
|
|
1879
|
+
if (err) {
|
|
1880
|
+
throw new Error(err);
|
|
1881
|
+
}
|
|
1882
|
+
const slide = ctx.pptxData.slides[params.slideIndex];
|
|
1883
|
+
const id = generateElementId();
|
|
1884
|
+
const x = params.x ?? 100;
|
|
1885
|
+
const y = params.y ?? 100;
|
|
1886
|
+
const width = params.width ?? 300;
|
|
1887
|
+
const height = params.height ?? 60;
|
|
1888
|
+
switch (params.type) {
|
|
1889
|
+
case "text": {
|
|
1890
|
+
const el = {
|
|
1891
|
+
id,
|
|
1892
|
+
type: "text",
|
|
1893
|
+
x,
|
|
1894
|
+
y,
|
|
1895
|
+
width,
|
|
1896
|
+
height,
|
|
1897
|
+
text: params.text ?? "",
|
|
1898
|
+
textStyle: {
|
|
1899
|
+
fontSize: params.fontSize,
|
|
1900
|
+
fontFamily: params.fontFamily,
|
|
1901
|
+
color: params.fontColor,
|
|
1902
|
+
bold: params.bold,
|
|
1903
|
+
italic: params.italic,
|
|
1904
|
+
underline: params.underline,
|
|
1905
|
+
align: params.alignment
|
|
1906
|
+
},
|
|
1907
|
+
textSegments: [
|
|
1908
|
+
{
|
|
1909
|
+
text: params.text ?? "",
|
|
1910
|
+
style: {
|
|
1911
|
+
fontSize: params.fontSize,
|
|
1912
|
+
fontFamily: params.fontFamily,
|
|
1913
|
+
color: params.fontColor,
|
|
1914
|
+
bold: params.bold,
|
|
1915
|
+
italic: params.italic,
|
|
1916
|
+
underline: params.underline
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
]
|
|
1920
|
+
};
|
|
1921
|
+
slide.elements.push(el);
|
|
1922
|
+
break;
|
|
1923
|
+
}
|
|
1924
|
+
case "shape": {
|
|
1925
|
+
const el = {
|
|
1926
|
+
id,
|
|
1927
|
+
type: "shape",
|
|
1928
|
+
x,
|
|
1929
|
+
y,
|
|
1930
|
+
width,
|
|
1931
|
+
height,
|
|
1932
|
+
shapeType: params.shapeType ?? "rect",
|
|
1933
|
+
text: params.text,
|
|
1934
|
+
textStyle: params.text ? {
|
|
1935
|
+
fontSize: params.fontSize,
|
|
1936
|
+
fontFamily: params.fontFamily,
|
|
1937
|
+
color: params.fontColor,
|
|
1938
|
+
bold: params.bold,
|
|
1939
|
+
italic: params.italic,
|
|
1940
|
+
align: params.alignment
|
|
1941
|
+
} : void 0,
|
|
1942
|
+
shapeStyle: {
|
|
1943
|
+
fillColor: params.fillColor,
|
|
1944
|
+
strokeColor: params.strokeColor,
|
|
1945
|
+
strokeWidth: params.strokeWidth
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
slide.elements.push(el);
|
|
1949
|
+
break;
|
|
1950
|
+
}
|
|
1951
|
+
case "image": {
|
|
1952
|
+
const el = {
|
|
1953
|
+
id,
|
|
1954
|
+
type: "image",
|
|
1955
|
+
x,
|
|
1306
1956
|
y,
|
|
1307
1957
|
width,
|
|
1308
1958
|
height,
|
|
@@ -1403,6 +2053,12 @@ function updateElement(ctx, params) {
|
|
|
1403
2053
|
if (params.flipV !== void 0) {
|
|
1404
2054
|
el.flipVertical = params.flipV;
|
|
1405
2055
|
}
|
|
2056
|
+
if (params.altText !== void 0) {
|
|
2057
|
+
applyElementAltText(el, params.altText);
|
|
2058
|
+
}
|
|
2059
|
+
if (params.title !== void 0) {
|
|
2060
|
+
applyElementTitle(el, params.title);
|
|
2061
|
+
}
|
|
1406
2062
|
if (hasTextProperties(el)) {
|
|
1407
2063
|
const textEl = el;
|
|
1408
2064
|
if (params.text !== void 0) {
|
|
@@ -1422,8 +2078,17 @@ function updateElement(ctx, params) {
|
|
|
1422
2078
|
if (params.fontFamily !== void 0) {
|
|
1423
2079
|
textEl.textStyle.fontFamily = params.fontFamily;
|
|
1424
2080
|
}
|
|
1425
|
-
if (params.
|
|
2081
|
+
if (params.fontThemeColor !== void 0) {
|
|
2082
|
+
textEl.textStyle.colorRef = params.fontThemeColor;
|
|
2083
|
+
const resolved = resolveThemeColorRef(params.fontThemeColor, ctx.pptxData.themeColorMap);
|
|
2084
|
+
if (params.fontColor !== void 0) {
|
|
2085
|
+
textEl.textStyle.color = params.fontColor;
|
|
2086
|
+
} else if (resolved) {
|
|
2087
|
+
textEl.textStyle.color = resolved;
|
|
2088
|
+
}
|
|
2089
|
+
} else if (params.fontColor !== void 0) {
|
|
1426
2090
|
textEl.textStyle.color = params.fontColor;
|
|
2091
|
+
textEl.textStyle.colorRef = void 0;
|
|
1427
2092
|
}
|
|
1428
2093
|
if (params.bold !== void 0) {
|
|
1429
2094
|
textEl.textStyle.bold = params.bold;
|
|
@@ -2648,188 +3313,140 @@ function manageSmartArt(ctx, params) {
|
|
|
2648
3313
|
throw new Error(`Unknown SmartArt action: ${String(params.action)}`);
|
|
2649
3314
|
}
|
|
2650
3315
|
}
|
|
2651
|
-
function
|
|
2652
|
-
const err = validateSlideIndex(
|
|
3316
|
+
function findTableElement(ctx, slideIndex, elementId) {
|
|
3317
|
+
const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
|
|
2653
3318
|
if (err) {
|
|
2654
3319
|
throw new Error(err);
|
|
2655
3320
|
}
|
|
2656
|
-
const slide = ctx.pptxData.slides[
|
|
2657
|
-
const el = slide.elements.find((e) => e.id ===
|
|
2658
|
-
if (!el) {
|
|
2659
|
-
throw new Error(`Element '${
|
|
3321
|
+
const slide = ctx.pptxData.slides[slideIndex];
|
|
3322
|
+
const el = slide.elements.find((e) => e.id === elementId);
|
|
3323
|
+
if (!el) {
|
|
3324
|
+
throw new Error(`Element '${elementId}' not found on slide ${slideIndex}.`);
|
|
2660
3325
|
}
|
|
2661
|
-
if ("
|
|
2662
|
-
|
|
2663
|
-
el.shapeStyle = {};
|
|
2664
|
-
}
|
|
2665
|
-
const ss = el.shapeStyle;
|
|
2666
|
-
if (params.fillColor !== void 0) {
|
|
2667
|
-
ss.fillColor = params.fillColor;
|
|
2668
|
-
}
|
|
2669
|
-
if (params.fillMode !== void 0) {
|
|
2670
|
-
ss.fillMode = params.fillMode;
|
|
2671
|
-
}
|
|
2672
|
-
if (params.fillGradientStops !== void 0) {
|
|
2673
|
-
ss.fillGradientStops = params.fillGradientStops;
|
|
2674
|
-
}
|
|
2675
|
-
if (params.fillGradientAngle !== void 0) {
|
|
2676
|
-
ss.fillGradientAngle = params.fillGradientAngle;
|
|
2677
|
-
}
|
|
2678
|
-
if (params.fillGradientType !== void 0) {
|
|
2679
|
-
ss.fillGradientType = params.fillGradientType;
|
|
2680
|
-
}
|
|
2681
|
-
if (params.fillOpacity !== void 0) {
|
|
2682
|
-
ss.fillOpacity = params.fillOpacity;
|
|
2683
|
-
}
|
|
2684
|
-
if (params.strokeColor !== void 0) {
|
|
2685
|
-
ss.strokeColor = params.strokeColor;
|
|
2686
|
-
}
|
|
2687
|
-
if (params.strokeWidth !== void 0) {
|
|
2688
|
-
ss.strokeWidth = params.strokeWidth;
|
|
2689
|
-
}
|
|
2690
|
-
if (params.strokeDash !== void 0) {
|
|
2691
|
-
ss.strokeDash = params.strokeDash;
|
|
2692
|
-
}
|
|
2693
|
-
if (params.strokeOpacity !== void 0) {
|
|
2694
|
-
ss.strokeOpacity = params.strokeOpacity;
|
|
2695
|
-
}
|
|
2696
|
-
if (params.shadowColor !== void 0) {
|
|
2697
|
-
ss.shadowColor = params.shadowColor;
|
|
2698
|
-
}
|
|
2699
|
-
if (params.shadowBlur !== void 0) {
|
|
2700
|
-
ss.shadowBlur = params.shadowBlur;
|
|
2701
|
-
}
|
|
2702
|
-
if (params.shadowOffsetX !== void 0) {
|
|
2703
|
-
ss.shadowOffsetX = params.shadowOffsetX;
|
|
2704
|
-
}
|
|
2705
|
-
if (params.shadowOffsetY !== void 0) {
|
|
2706
|
-
ss.shadowOffsetY = params.shadowOffsetY;
|
|
2707
|
-
}
|
|
2708
|
-
if (params.shadowOpacity !== void 0) {
|
|
2709
|
-
ss.shadowOpacity = params.shadowOpacity;
|
|
2710
|
-
}
|
|
2711
|
-
if (params.glowColor !== void 0) {
|
|
2712
|
-
ss.glowColor = params.glowColor;
|
|
2713
|
-
}
|
|
2714
|
-
if (params.glowRadius !== void 0) {
|
|
2715
|
-
ss.glowRadius = params.glowRadius;
|
|
2716
|
-
}
|
|
2717
|
-
if (params.glowOpacity !== void 0) {
|
|
2718
|
-
ss.glowOpacity = params.glowOpacity;
|
|
2719
|
-
}
|
|
2720
|
-
if (params.softEdgeRadius !== void 0) {
|
|
2721
|
-
ss.softEdgeRadius = params.softEdgeRadius;
|
|
2722
|
-
}
|
|
3326
|
+
if (el.type !== "table") {
|
|
3327
|
+
throw new Error(`Element '${elementId}' is not a table.`);
|
|
2723
3328
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
if (params.cropTop !== void 0) {
|
|
2733
|
-
img.cropTop = params.cropTop;
|
|
2734
|
-
}
|
|
2735
|
-
if (params.cropRight !== void 0) {
|
|
2736
|
-
img.cropRight = params.cropRight;
|
|
2737
|
-
}
|
|
2738
|
-
if (params.cropBottom !== void 0) {
|
|
2739
|
-
img.cropBottom = params.cropBottom;
|
|
2740
|
-
}
|
|
2741
|
-
if (params.brightness !== void 0 || params.contrast !== void 0 || params.grayscale !== void 0) {
|
|
2742
|
-
if (!img.imageEffects) {
|
|
2743
|
-
img.imageEffects = {};
|
|
2744
|
-
}
|
|
2745
|
-
if (params.brightness !== void 0) {
|
|
2746
|
-
img.imageEffects.brightness = params.brightness;
|
|
2747
|
-
}
|
|
2748
|
-
if (params.contrast !== void 0) {
|
|
2749
|
-
img.imageEffects.contrast = params.contrast;
|
|
2750
|
-
}
|
|
2751
|
-
if (params.grayscale !== void 0) {
|
|
2752
|
-
img.imageEffects.grayscale = params.grayscale;
|
|
3329
|
+
return el;
|
|
3330
|
+
}
|
|
3331
|
+
function tableStyleIdsInUse(ctx) {
|
|
3332
|
+
const used = /* @__PURE__ */ new Set();
|
|
3333
|
+
for (const slide of ctx.pptxData.slides) {
|
|
3334
|
+
for (const el of slide.elements) {
|
|
3335
|
+
if (el.type === "table" && el.tableData?.tableStyleId) {
|
|
3336
|
+
used.add(normalizeTableStyleGuid(el.tableData.tableStyleId));
|
|
2753
3337
|
}
|
|
2754
3338
|
}
|
|
2755
3339
|
}
|
|
3340
|
+
return used;
|
|
3341
|
+
}
|
|
3342
|
+
function setTableStyleSection(ctx, params) {
|
|
3343
|
+
if (!TABLE_STYLE_PART_SEQUENCE.includes(params.section)) {
|
|
3344
|
+
throw new Error(
|
|
3345
|
+
`Unknown table style section '${params.section}'. Valid sections: ${TABLE_STYLE_PART_SEQUENCE.join(", ")}.`
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
const map = ctx.pptxData.tableStyleMap ?? {};
|
|
3349
|
+
ctx.pptxData.tableStyleMap = map;
|
|
3350
|
+
const normalizedId = normalizeTableStyleGuid(params.styleId);
|
|
3351
|
+
const entry = map[normalizedId];
|
|
3352
|
+
if (!entry) {
|
|
3353
|
+
throw new Error(
|
|
3354
|
+
`Table style '${params.styleId}' is not loaded in this presentation. Use create_table_style to add a new one.`
|
|
3355
|
+
);
|
|
3356
|
+
}
|
|
3357
|
+
if (params.styleName !== void 0) {
|
|
3358
|
+
entry.styleName = params.styleName;
|
|
3359
|
+
}
|
|
3360
|
+
const mutableEntry = entry;
|
|
3361
|
+
if (params.fill !== void 0) {
|
|
3362
|
+
mutableEntry[`${params.section}Fill`] = params.fill;
|
|
3363
|
+
}
|
|
3364
|
+
if (params.text !== void 0) {
|
|
3365
|
+
mutableEntry[`${params.section}Text`] = params.text;
|
|
3366
|
+
}
|
|
3367
|
+
if (params.borders !== void 0) {
|
|
3368
|
+
mutableEntry[`${params.section}Borders`] = params.borders;
|
|
3369
|
+
}
|
|
3370
|
+
if (params.cell3D !== void 0) {
|
|
3371
|
+
mutableEntry[`${params.section}Cell3D`] = params.cell3D;
|
|
3372
|
+
}
|
|
2756
3373
|
return {
|
|
2757
3374
|
pptxData: ctx.pptxData,
|
|
2758
3375
|
dirty: true,
|
|
2759
|
-
result: {
|
|
3376
|
+
result: { styleId: normalizedId, section: params.section }
|
|
2760
3377
|
};
|
|
2761
3378
|
}
|
|
2762
|
-
function
|
|
2763
|
-
const
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
severity: "info",
|
|
2787
|
-
message: `Text element '${el.id}' is empty.`
|
|
2788
|
-
});
|
|
2789
|
-
}
|
|
2790
|
-
const fs = textEl.textStyle?.fontSize;
|
|
2791
|
-
if (fs !== void 0 && fs < 10) {
|
|
2792
|
-
issues.push({
|
|
2793
|
-
slideIndex: si,
|
|
2794
|
-
elementId: el.id,
|
|
2795
|
-
severity: "warning",
|
|
2796
|
-
message: `Text element '${el.id}' has very small font size (${fs}pt < 10pt).`
|
|
2797
|
-
});
|
|
2798
|
-
}
|
|
2799
|
-
const textColor = textEl.textStyle?.color?.toLowerCase();
|
|
2800
|
-
const bgColor = slide.backgroundColor?.toLowerCase();
|
|
2801
|
-
if (textColor && bgColor && textColor === bgColor) {
|
|
2802
|
-
issues.push({
|
|
2803
|
-
slideIndex: si,
|
|
2804
|
-
elementId: el.id,
|
|
2805
|
-
severity: "error",
|
|
2806
|
-
message: `Text element '${el.id}' has same color as slide background (${textColor}).`
|
|
2807
|
-
});
|
|
2808
|
-
}
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
if (!slideHasText) {
|
|
2812
|
-
issues.push({
|
|
2813
|
-
slideIndex: si,
|
|
2814
|
-
severity: "warning",
|
|
2815
|
-
message: `Slide ${si + 1} contains no text elements.`
|
|
2816
|
-
});
|
|
3379
|
+
function createTableStyle(ctx, params) {
|
|
3380
|
+
const map = ctx.pptxData.tableStyleMap ?? {};
|
|
3381
|
+
ctx.pptxData.tableStyleMap = map;
|
|
3382
|
+
const basedOn = params.basedOnStyleId ? map[normalizeTableStyleGuid(params.basedOnStyleId)] : void 0;
|
|
3383
|
+
if (params.basedOnStyleId && !basedOn) {
|
|
3384
|
+
throw new Error(`basedOnStyleId '${params.basedOnStyleId}' is not a loaded table style.`);
|
|
3385
|
+
}
|
|
3386
|
+
const entry = createTableStyleEntry(map, { styleName: params.styleName, basedOn });
|
|
3387
|
+
addTableStyleToMap(map, entry);
|
|
3388
|
+
return {
|
|
3389
|
+
pptxData: ctx.pptxData,
|
|
3390
|
+
dirty: true,
|
|
3391
|
+
result: { styleId: entry.styleId },
|
|
3392
|
+
...params.setAsDefault ? { saveOptions: { tableStylesDefaultId: entry.styleId } } : {}
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
function deleteTableStyle(ctx, params) {
|
|
3396
|
+
const normalizedId = normalizeTableStyleGuid(params.styleId);
|
|
3397
|
+
if (!params.force) {
|
|
3398
|
+
const used = tableStyleIdsInUse(ctx);
|
|
3399
|
+
if (used.has(normalizedId)) {
|
|
3400
|
+
throw new Error(
|
|
3401
|
+
`Table style '${params.styleId}' is assigned to at least one table on this deck. Reassign those tables first, or pass force: true.`
|
|
3402
|
+
);
|
|
2817
3403
|
}
|
|
2818
3404
|
}
|
|
2819
|
-
const
|
|
2820
|
-
|
|
2821
|
-
|
|
3405
|
+
const map = ctx.pptxData.tableStyleMap ?? {};
|
|
3406
|
+
ctx.pptxData.tableStyleMap = map;
|
|
3407
|
+
deleteTableStyleFromMap(map, normalizedId);
|
|
2822
3408
|
return {
|
|
2823
3409
|
pptxData: ctx.pptxData,
|
|
2824
|
-
dirty:
|
|
2825
|
-
result: {
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
}
|
|
3410
|
+
dirty: true,
|
|
3411
|
+
result: { deleted: true },
|
|
3412
|
+
saveOptions: { tableStylesToDelete: [normalizedId] }
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
function assignTableStyle(ctx, params) {
|
|
3416
|
+
const el = findTableElement(ctx, params.slideIndex, params.elementId);
|
|
3417
|
+
if (!el.tableData) {
|
|
3418
|
+
throw new Error(`Table '${params.elementId}' has no tableData.`);
|
|
3419
|
+
}
|
|
3420
|
+
const styleId = params.styleId ?? ctx.pptxData.tableStylesDefaultId;
|
|
3421
|
+
if (!styleId) {
|
|
3422
|
+
throw new Error(
|
|
3423
|
+
"styleId was not provided and this presentation has no tableStylesDefaultId to fall back to."
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
const normalizedId = normalizeTableStyleGuid(styleId);
|
|
3427
|
+
el.tableData.tableStyleId = normalizedId;
|
|
3428
|
+
if (params.bandedRows !== void 0) {
|
|
3429
|
+
el.tableData.bandedRows = params.bandedRows;
|
|
3430
|
+
}
|
|
3431
|
+
if (params.bandedColumns !== void 0) {
|
|
3432
|
+
el.tableData.bandedColumns = params.bandedColumns;
|
|
3433
|
+
}
|
|
3434
|
+
if (params.firstRowHeader !== void 0) {
|
|
3435
|
+
el.tableData.firstRowHeader = params.firstRowHeader;
|
|
3436
|
+
}
|
|
3437
|
+
if (params.lastRow !== void 0) {
|
|
3438
|
+
el.tableData.lastRow = params.lastRow;
|
|
3439
|
+
}
|
|
3440
|
+
if (params.firstCol !== void 0) {
|
|
3441
|
+
el.tableData.firstCol = params.firstCol;
|
|
3442
|
+
}
|
|
3443
|
+
if (params.lastCol !== void 0) {
|
|
3444
|
+
el.tableData.lastCol = params.lastCol;
|
|
3445
|
+
}
|
|
3446
|
+
return {
|
|
3447
|
+
pptxData: ctx.pptxData,
|
|
3448
|
+
dirty: true,
|
|
3449
|
+
result: { elementId: params.elementId, styleId: normalizedId }
|
|
2833
3450
|
};
|
|
2834
3451
|
}
|
|
2835
3452
|
|
|
@@ -3111,11 +3728,17 @@ async function loadPresentation(filePath, execCtx) {
|
|
|
3111
3728
|
const pptxData = await handler.load(rawBytes.buffer);
|
|
3112
3729
|
return { pptxData, rawBytes };
|
|
3113
3730
|
}
|
|
3114
|
-
async function savePresentation(filePath, pptxData, rawBytes, execCtx) {
|
|
3731
|
+
async function savePresentation(filePath, pptxData, rawBytes, execCtx, tableStyleSaveOptions) {
|
|
3115
3732
|
const handler = new PptxHandler();
|
|
3116
3733
|
await handler.load(rawBytes.buffer);
|
|
3117
3734
|
const outputBytes = await handler.save(pptxData.slides, {
|
|
3118
|
-
headerFooter: pptxData.headerFooter
|
|
3735
|
+
headerFooter: pptxData.headerFooter,
|
|
3736
|
+
// `pptxData.tableStyleMap` carries any in-memory table-style edits a
|
|
3737
|
+
// tool made (e.g. `set_table_style_section`, `create_table_style`);
|
|
3738
|
+
// without forwarding it here those edits were silently dropped on
|
|
3739
|
+
// save even though the core save pipeline already supports them.
|
|
3740
|
+
tableStyles: pptxData.tableStyleMap,
|
|
3741
|
+
...tableStyleSaveOptions
|
|
3119
3742
|
});
|
|
3120
3743
|
if (execCtx.collaboration) {
|
|
3121
3744
|
const room = execCtx.collaboration.getRoom(filePath);
|
|
@@ -3147,7 +3770,13 @@ async function executeToolWithContext(filePath, execCtx, toolFn) {
|
|
|
3147
3770
|
const { pptxData, rawBytes } = await loadPresentation(filePath, execCtx);
|
|
3148
3771
|
const toolResult = await toolFn({ pptxData });
|
|
3149
3772
|
if (toolResult.dirty) {
|
|
3150
|
-
const saveResult = await savePresentation(
|
|
3773
|
+
const saveResult = await savePresentation(
|
|
3774
|
+
filePath,
|
|
3775
|
+
toolResult.pptxData,
|
|
3776
|
+
rawBytes,
|
|
3777
|
+
execCtx,
|
|
3778
|
+
toolResult.saveOptions
|
|
3779
|
+
);
|
|
3151
3780
|
return { ...toolResult.result, ...saveResult };
|
|
3152
3781
|
}
|
|
3153
3782
|
return toolResult.result;
|
|
@@ -3627,6 +4256,70 @@ function createServer() {
|
|
|
3627
4256
|
};
|
|
3628
4257
|
}
|
|
3629
4258
|
);
|
|
4259
|
+
server2.registerTool(
|
|
4260
|
+
"set_table_style_section",
|
|
4261
|
+
{
|
|
4262
|
+
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",
|
|
4263
|
+
inputSchema: SetTableStyleSectionSchema.shape
|
|
4264
|
+
},
|
|
4265
|
+
async (params) => {
|
|
4266
|
+
const result = await runMcpTool(
|
|
4267
|
+
params.filePath,
|
|
4268
|
+
(ctx) => setTableStyleSection(ctx, params)
|
|
4269
|
+
);
|
|
4270
|
+
return {
|
|
4271
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
4272
|
+
};
|
|
4273
|
+
}
|
|
4274
|
+
);
|
|
4275
|
+
server2.registerTool(
|
|
4276
|
+
"create_table_style",
|
|
4277
|
+
{
|
|
4278
|
+
description: "Create a new table style (optionally cloned from an existing one) and optionally set it as the default",
|
|
4279
|
+
inputSchema: CreateTableStyleSchema.shape
|
|
4280
|
+
},
|
|
4281
|
+
async (params) => {
|
|
4282
|
+
const result = await runMcpTool(
|
|
4283
|
+
params.filePath,
|
|
4284
|
+
(ctx) => createTableStyle(ctx, params)
|
|
4285
|
+
);
|
|
4286
|
+
return {
|
|
4287
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
4288
|
+
};
|
|
4289
|
+
}
|
|
4290
|
+
);
|
|
4291
|
+
server2.registerTool(
|
|
4292
|
+
"delete_table_style",
|
|
4293
|
+
{
|
|
4294
|
+
description: "Remove a table style from the presentation",
|
|
4295
|
+
inputSchema: DeleteTableStyleSchema.shape
|
|
4296
|
+
},
|
|
4297
|
+
async (params) => {
|
|
4298
|
+
const result = await runMcpTool(
|
|
4299
|
+
params.filePath,
|
|
4300
|
+
(ctx) => deleteTableStyle(ctx, params)
|
|
4301
|
+
);
|
|
4302
|
+
return {
|
|
4303
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
4304
|
+
};
|
|
4305
|
+
}
|
|
4306
|
+
);
|
|
4307
|
+
server2.registerTool(
|
|
4308
|
+
"assign_table_style",
|
|
4309
|
+
{
|
|
4310
|
+
description: "Assign a table style (and row/column emphasis flags) to an existing table",
|
|
4311
|
+
inputSchema: AssignTableStyleSchema.shape
|
|
4312
|
+
},
|
|
4313
|
+
async (params) => {
|
|
4314
|
+
const result = await runMcpTool(
|
|
4315
|
+
params.filePath,
|
|
4316
|
+
(ctx) => assignTableStyle(ctx, params)
|
|
4317
|
+
);
|
|
4318
|
+
return {
|
|
4319
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
4320
|
+
};
|
|
4321
|
+
}
|
|
4322
|
+
);
|
|
3630
4323
|
server2.registerTool(
|
|
3631
4324
|
"update_element_style",
|
|
3632
4325
|
{
|
|
@@ -3873,6 +4566,76 @@ function createServer() {
|
|
|
3873
4566
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
3874
4567
|
}
|
|
3875
4568
|
);
|
|
4569
|
+
server2.registerTool(
|
|
4570
|
+
"format_chart_data_point",
|
|
4571
|
+
{
|
|
4572
|
+
description: "Set per-data-point chart formatting: fill/stroke colour, stroke width, dash style (c:dPt/c:spPr), explosion, and marker override",
|
|
4573
|
+
inputSchema: FormatChartDataPointSchema.shape
|
|
4574
|
+
},
|
|
4575
|
+
async (params) => {
|
|
4576
|
+
const result = await runMcpTool(
|
|
4577
|
+
params.filePath,
|
|
4578
|
+
(ctx) => formatChartDataPoint(ctx, params)
|
|
4579
|
+
);
|
|
4580
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4581
|
+
}
|
|
4582
|
+
);
|
|
4583
|
+
server2.registerTool(
|
|
4584
|
+
"format_chart_data_label",
|
|
4585
|
+
{
|
|
4586
|
+
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)",
|
|
4587
|
+
inputSchema: FormatChartDataLabelSchema.shape
|
|
4588
|
+
},
|
|
4589
|
+
async (params) => {
|
|
4590
|
+
const result = await runMcpTool(
|
|
4591
|
+
params.filePath,
|
|
4592
|
+
(ctx) => formatChartDataLabel(ctx, params)
|
|
4593
|
+
);
|
|
4594
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4595
|
+
}
|
|
4596
|
+
);
|
|
4597
|
+
server2.registerTool(
|
|
4598
|
+
"format_chart_series",
|
|
4599
|
+
{
|
|
4600
|
+
description: "Set a chart series marker (c:marker), trendline (c:trendline, incl. line width/dash), or error bars (c:errBars, incl. line width/dash)",
|
|
4601
|
+
inputSchema: FormatChartSeriesSchema.shape
|
|
4602
|
+
},
|
|
4603
|
+
async (params) => {
|
|
4604
|
+
const result = await runMcpTool(
|
|
4605
|
+
params.filePath,
|
|
4606
|
+
(ctx) => formatChartSeries(ctx, params)
|
|
4607
|
+
);
|
|
4608
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4609
|
+
}
|
|
4610
|
+
);
|
|
4611
|
+
server2.registerTool(
|
|
4612
|
+
"set_chart_helper_line",
|
|
4613
|
+
{
|
|
4614
|
+
description: "Set or remove a chart-level helper line on a line/stock/combo chart: c:dropLines or c:hiLowLines",
|
|
4615
|
+
inputSchema: SetChartHelperLineSchema.shape
|
|
4616
|
+
},
|
|
4617
|
+
async (params) => {
|
|
4618
|
+
const result = await runMcpTool(
|
|
4619
|
+
params.filePath,
|
|
4620
|
+
(ctx) => setChartHelperLineT(ctx, params)
|
|
4621
|
+
);
|
|
4622
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4623
|
+
}
|
|
4624
|
+
);
|
|
4625
|
+
server2.registerTool(
|
|
4626
|
+
"set_chart_color_map_override",
|
|
4627
|
+
{
|
|
4628
|
+
description: "Set or remove a chart's theme colour-map override (c:clrMapOvr), remapping theme colour roles for this chart only",
|
|
4629
|
+
inputSchema: SetChartColorMapOverrideSchema.shape
|
|
4630
|
+
},
|
|
4631
|
+
async (params) => {
|
|
4632
|
+
const result = await runMcpTool(
|
|
4633
|
+
params.filePath,
|
|
4634
|
+
(ctx) => setChartColorMapOverrideT(ctx, params)
|
|
4635
|
+
);
|
|
4636
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4637
|
+
}
|
|
4638
|
+
);
|
|
3876
4639
|
server2.registerTool(
|
|
3877
4640
|
"chart_user_shape_list",
|
|
3878
4641
|
{
|