pptx-viewer-mcp 2.3.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/dist/mcp/index.js CHANGED
@@ -154,7 +154,13 @@ var UpdateElementSchema = zod.z.object({
154
154
  opacity: zod.z.number().min(0).max(1).optional(),
155
155
  hidden: zod.z.boolean().optional(),
156
156
  flipH: zod.z.boolean().optional(),
157
- flipV: zod.z.boolean().optional()
157
+ flipV: zod.z.boolean().optional(),
158
+ altText: zod.z.string().optional().describe(
159
+ "Accessibility description (p:cNvPr/@descr) for image, picture, table, chart, smartArt, ole, media, text, shape and connector elements"
160
+ ),
161
+ title: zod.z.string().optional().describe(
162
+ "Accessibility title (p:cNvPr/@title) for table, chart, smartArt, ole, media, text, shape and connector elements (not pictures)"
163
+ )
158
164
  });
159
165
  var RenameElementSchema = zod.z.object({
160
166
  filePath: zod.z.string().describe("Path to the PPTX file"),
@@ -309,7 +315,12 @@ var UpdateElementStyleSchema = zod.z.object({
309
315
  brightness: zod.z.number().optional(),
310
316
  contrast: zod.z.number().optional(),
311
317
  grayscale: zod.z.boolean().optional(),
312
- altText: zod.z.string().optional()
318
+ altText: zod.z.string().optional().describe(
319
+ "Accessibility description (p:cNvPr/@descr) for image, picture, table, chart, smartArt, ole, media, text, shape and connector elements"
320
+ ),
321
+ title: zod.z.string().optional().describe(
322
+ "Accessibility title (p:cNvPr/@title) for table, chart, smartArt, ole, media, text, shape and connector elements (not pictures)"
323
+ )
313
324
  });
314
325
  var FindTextSchema = zod.z.object({
315
326
  filePath: zod.z.string().describe("Path to the PPTX file"),
@@ -1631,6 +1642,240 @@ async function convertToMarkdown(ctx, params) {
1631
1642
  }
1632
1643
  };
1633
1644
  }
1645
+ var ALT_TEXT_ELEMENT_TYPES = /* @__PURE__ */ new Set(["image", "picture", "table", "chart", "smartArt", "ole", "media", "text", "shape", "connector"]);
1646
+ var TITLE_ELEMENT_TYPES = /* @__PURE__ */ new Set([
1647
+ "table",
1648
+ "chart",
1649
+ "smartArt",
1650
+ "ole",
1651
+ "media",
1652
+ "text",
1653
+ "shape",
1654
+ "connector"
1655
+ ]);
1656
+ function applyElementAltText(el, altText) {
1657
+ if (!ALT_TEXT_ELEMENT_TYPES.has(el.type)) {
1658
+ throw new Error(
1659
+ `Element '${el.id}' (${el.type}) does not support altText; only ${[...ALT_TEXT_ELEMENT_TYPES].join(", ")} elements do.`
1660
+ );
1661
+ }
1662
+ el.altText = altText;
1663
+ }
1664
+ function applyElementTitle(el, title) {
1665
+ if (!TITLE_ELEMENT_TYPES.has(el.type)) {
1666
+ throw new Error(
1667
+ `Element '${el.id}' (${el.type}) does not support title; only ${[...TITLE_ELEMENT_TYPES].join(", ")} elements do.`
1668
+ );
1669
+ }
1670
+ el.title = title;
1671
+ }
1672
+ function updateElementStyle(ctx, params) {
1673
+ const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1674
+ if (err) {
1675
+ throw new Error(err);
1676
+ }
1677
+ const slide = ctx.pptxData.slides[params.slideIndex];
1678
+ const el = slide.elements.find((e) => e.id === params.elementId);
1679
+ if (!el) {
1680
+ throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
1681
+ }
1682
+ if ("shapeStyle" in el) {
1683
+ if (!el.shapeStyle) {
1684
+ el.shapeStyle = {};
1685
+ }
1686
+ const ss = el.shapeStyle;
1687
+ if (params.fillThemeColor !== void 0) {
1688
+ ss.fillColorRef = params.fillThemeColor;
1689
+ const resolved = pptxViewerCore.resolveThemeColorRef(params.fillThemeColor, ctx.pptxData.themeColorMap);
1690
+ if (params.fillColor !== void 0) {
1691
+ ss.fillColor = params.fillColor;
1692
+ } else if (resolved) {
1693
+ ss.fillColor = resolved;
1694
+ }
1695
+ } else if (params.fillColor !== void 0) {
1696
+ ss.fillColor = params.fillColor;
1697
+ ss.fillColorRef = void 0;
1698
+ }
1699
+ if (params.fillMode !== void 0) {
1700
+ ss.fillMode = params.fillMode;
1701
+ }
1702
+ if (params.fillGradientStops !== void 0) {
1703
+ ss.fillGradientStops = params.fillGradientStops;
1704
+ }
1705
+ if (params.fillGradientAngle !== void 0) {
1706
+ ss.fillGradientAngle = params.fillGradientAngle;
1707
+ }
1708
+ if (params.fillGradientType !== void 0) {
1709
+ ss.fillGradientType = params.fillGradientType;
1710
+ }
1711
+ if (params.fillOpacity !== void 0) {
1712
+ ss.fillOpacity = params.fillOpacity;
1713
+ }
1714
+ if (params.strokeThemeColor !== void 0) {
1715
+ ss.strokeColorRef = params.strokeThemeColor;
1716
+ const resolved = pptxViewerCore.resolveThemeColorRef(params.strokeThemeColor, ctx.pptxData.themeColorMap);
1717
+ if (params.strokeColor !== void 0) {
1718
+ ss.strokeColor = params.strokeColor;
1719
+ } else if (resolved) {
1720
+ ss.strokeColor = resolved;
1721
+ }
1722
+ } else if (params.strokeColor !== void 0) {
1723
+ ss.strokeColor = params.strokeColor;
1724
+ ss.strokeColorRef = void 0;
1725
+ }
1726
+ if (params.strokeWidth !== void 0) {
1727
+ ss.strokeWidth = params.strokeWidth;
1728
+ }
1729
+ if (params.strokeDash !== void 0) {
1730
+ ss.strokeDash = params.strokeDash;
1731
+ }
1732
+ if (params.strokeOpacity !== void 0) {
1733
+ ss.strokeOpacity = params.strokeOpacity;
1734
+ }
1735
+ if (params.shadowColor !== void 0) {
1736
+ ss.shadowColor = params.shadowColor;
1737
+ }
1738
+ if (params.shadowBlur !== void 0) {
1739
+ ss.shadowBlur = params.shadowBlur;
1740
+ }
1741
+ if (params.shadowOffsetX !== void 0) {
1742
+ ss.shadowOffsetX = params.shadowOffsetX;
1743
+ }
1744
+ if (params.shadowOffsetY !== void 0) {
1745
+ ss.shadowOffsetY = params.shadowOffsetY;
1746
+ }
1747
+ if (params.shadowOpacity !== void 0) {
1748
+ ss.shadowOpacity = params.shadowOpacity;
1749
+ }
1750
+ if (params.glowColor !== void 0) {
1751
+ ss.glowColor = params.glowColor;
1752
+ }
1753
+ if (params.glowRadius !== void 0) {
1754
+ ss.glowRadius = params.glowRadius;
1755
+ }
1756
+ if (params.glowOpacity !== void 0) {
1757
+ ss.glowOpacity = params.glowOpacity;
1758
+ }
1759
+ if (params.softEdgeRadius !== void 0) {
1760
+ ss.softEdgeRadius = params.softEdgeRadius;
1761
+ }
1762
+ }
1763
+ if (params.altText !== void 0) {
1764
+ applyElementAltText(el, params.altText);
1765
+ }
1766
+ if (params.title !== void 0) {
1767
+ applyElementTitle(el, params.title);
1768
+ }
1769
+ if (el.type === "image" || el.type === "picture") {
1770
+ const img = el;
1771
+ if (params.cropLeft !== void 0) {
1772
+ img.cropLeft = params.cropLeft;
1773
+ }
1774
+ if (params.cropTop !== void 0) {
1775
+ img.cropTop = params.cropTop;
1776
+ }
1777
+ if (params.cropRight !== void 0) {
1778
+ img.cropRight = params.cropRight;
1779
+ }
1780
+ if (params.cropBottom !== void 0) {
1781
+ img.cropBottom = params.cropBottom;
1782
+ }
1783
+ if (params.brightness !== void 0 || params.contrast !== void 0 || params.grayscale !== void 0) {
1784
+ if (!img.imageEffects) {
1785
+ img.imageEffects = {};
1786
+ }
1787
+ if (params.brightness !== void 0) {
1788
+ img.imageEffects.brightness = params.brightness;
1789
+ }
1790
+ if (params.contrast !== void 0) {
1791
+ img.imageEffects.contrast = params.contrast;
1792
+ }
1793
+ if (params.grayscale !== void 0) {
1794
+ img.imageEffects.grayscale = params.grayscale;
1795
+ }
1796
+ }
1797
+ }
1798
+ return {
1799
+ pptxData: ctx.pptxData,
1800
+ dirty: true,
1801
+ result: { elementId: params.elementId }
1802
+ };
1803
+ }
1804
+ function runAccessibilityCheck(ctx) {
1805
+ const issues = [];
1806
+ for (let si = 0; si < ctx.pptxData.slides.length; si++) {
1807
+ const slide = ctx.pptxData.slides[si];
1808
+ let slideHasText = false;
1809
+ for (const el of slide.elements) {
1810
+ if (el.type === "image" || el.type === "picture") {
1811
+ const img = el;
1812
+ if (!img.altText) {
1813
+ issues.push({
1814
+ slideIndex: si,
1815
+ elementId: el.id,
1816
+ severity: "error",
1817
+ message: `Image element '${el.id}' has no alt text.`
1818
+ });
1819
+ }
1820
+ }
1821
+ if (pptxViewerCore.hasTextProperties(el)) {
1822
+ const textEl = el;
1823
+ slideHasText = true;
1824
+ if (!textEl.text || textEl.text.trim() === "") {
1825
+ issues.push({
1826
+ slideIndex: si,
1827
+ elementId: el.id,
1828
+ severity: "info",
1829
+ message: `Text element '${el.id}' is empty.`
1830
+ });
1831
+ }
1832
+ const fs = textEl.textStyle?.fontSize;
1833
+ if (fs !== void 0 && fs < 10) {
1834
+ issues.push({
1835
+ slideIndex: si,
1836
+ elementId: el.id,
1837
+ severity: "warning",
1838
+ message: `Text element '${el.id}' has very small font size (${fs}pt < 10pt).`
1839
+ });
1840
+ }
1841
+ const textColor = textEl.textStyle?.color?.toLowerCase();
1842
+ const bgColor = slide.backgroundColor?.toLowerCase();
1843
+ if (textColor && bgColor && textColor === bgColor) {
1844
+ issues.push({
1845
+ slideIndex: si,
1846
+ elementId: el.id,
1847
+ severity: "error",
1848
+ message: `Text element '${el.id}' has same color as slide background (${textColor}).`
1849
+ });
1850
+ }
1851
+ }
1852
+ }
1853
+ if (!slideHasText) {
1854
+ issues.push({
1855
+ slideIndex: si,
1856
+ severity: "warning",
1857
+ message: `Slide ${si + 1} contains no text elements.`
1858
+ });
1859
+ }
1860
+ }
1861
+ const errorCount = issues.filter((i) => i.severity === "error").length;
1862
+ const warningCount = issues.filter((i) => i.severity === "warning").length;
1863
+ const infoCount = issues.filter((i) => i.severity === "info").length;
1864
+ return {
1865
+ pptxData: ctx.pptxData,
1866
+ dirty: false,
1867
+ result: {
1868
+ slideCount: ctx.pptxData.slides.length,
1869
+ totalIssues: issues.length,
1870
+ errorCount,
1871
+ warningCount,
1872
+ infoCount,
1873
+ issues
1874
+ }
1875
+ };
1876
+ }
1877
+
1878
+ // src/tools/element-tools.ts
1634
1879
  function addElement(ctx, params) {
1635
1880
  const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
1636
1881
  if (err) {
@@ -1810,6 +2055,12 @@ function updateElement(ctx, params) {
1810
2055
  if (params.flipV !== void 0) {
1811
2056
  el.flipVertical = params.flipV;
1812
2057
  }
2058
+ if (params.altText !== void 0) {
2059
+ applyElementAltText(el, params.altText);
2060
+ }
2061
+ if (params.title !== void 0) {
2062
+ applyElementTitle(el, params.title);
2063
+ }
1813
2064
  if (pptxViewerCore.hasTextProperties(el)) {
1814
2065
  const textEl = el;
1815
2066
  if (params.text !== void 0) {
@@ -3064,208 +3315,6 @@ function manageSmartArt(ctx, params) {
3064
3315
  throw new Error(`Unknown SmartArt action: ${String(params.action)}`);
3065
3316
  }
3066
3317
  }
3067
- function updateElementStyle(ctx, params) {
3068
- const err = validateSlideIndex(params.slideIndex, ctx.pptxData.slides.length);
3069
- if (err) {
3070
- throw new Error(err);
3071
- }
3072
- const slide = ctx.pptxData.slides[params.slideIndex];
3073
- const el = slide.elements.find((e) => e.id === params.elementId);
3074
- if (!el) {
3075
- throw new Error(`Element '${params.elementId}' not found on slide ${params.slideIndex}.`);
3076
- }
3077
- if ("shapeStyle" in el) {
3078
- if (!el.shapeStyle) {
3079
- el.shapeStyle = {};
3080
- }
3081
- const ss = el.shapeStyle;
3082
- if (params.fillThemeColor !== void 0) {
3083
- ss.fillColorRef = params.fillThemeColor;
3084
- const resolved = pptxViewerCore.resolveThemeColorRef(params.fillThemeColor, ctx.pptxData.themeColorMap);
3085
- if (params.fillColor !== void 0) {
3086
- ss.fillColor = params.fillColor;
3087
- } else if (resolved) {
3088
- ss.fillColor = resolved;
3089
- }
3090
- } else if (params.fillColor !== void 0) {
3091
- ss.fillColor = params.fillColor;
3092
- ss.fillColorRef = void 0;
3093
- }
3094
- if (params.fillMode !== void 0) {
3095
- ss.fillMode = params.fillMode;
3096
- }
3097
- if (params.fillGradientStops !== void 0) {
3098
- ss.fillGradientStops = params.fillGradientStops;
3099
- }
3100
- if (params.fillGradientAngle !== void 0) {
3101
- ss.fillGradientAngle = params.fillGradientAngle;
3102
- }
3103
- if (params.fillGradientType !== void 0) {
3104
- ss.fillGradientType = params.fillGradientType;
3105
- }
3106
- if (params.fillOpacity !== void 0) {
3107
- ss.fillOpacity = params.fillOpacity;
3108
- }
3109
- if (params.strokeThemeColor !== void 0) {
3110
- ss.strokeColorRef = params.strokeThemeColor;
3111
- const resolved = pptxViewerCore.resolveThemeColorRef(params.strokeThemeColor, ctx.pptxData.themeColorMap);
3112
- if (params.strokeColor !== void 0) {
3113
- ss.strokeColor = params.strokeColor;
3114
- } else if (resolved) {
3115
- ss.strokeColor = resolved;
3116
- }
3117
- } else if (params.strokeColor !== void 0) {
3118
- ss.strokeColor = params.strokeColor;
3119
- ss.strokeColorRef = void 0;
3120
- }
3121
- if (params.strokeWidth !== void 0) {
3122
- ss.strokeWidth = params.strokeWidth;
3123
- }
3124
- if (params.strokeDash !== void 0) {
3125
- ss.strokeDash = params.strokeDash;
3126
- }
3127
- if (params.strokeOpacity !== void 0) {
3128
- ss.strokeOpacity = params.strokeOpacity;
3129
- }
3130
- if (params.shadowColor !== void 0) {
3131
- ss.shadowColor = params.shadowColor;
3132
- }
3133
- if (params.shadowBlur !== void 0) {
3134
- ss.shadowBlur = params.shadowBlur;
3135
- }
3136
- if (params.shadowOffsetX !== void 0) {
3137
- ss.shadowOffsetX = params.shadowOffsetX;
3138
- }
3139
- if (params.shadowOffsetY !== void 0) {
3140
- ss.shadowOffsetY = params.shadowOffsetY;
3141
- }
3142
- if (params.shadowOpacity !== void 0) {
3143
- ss.shadowOpacity = params.shadowOpacity;
3144
- }
3145
- if (params.glowColor !== void 0) {
3146
- ss.glowColor = params.glowColor;
3147
- }
3148
- if (params.glowRadius !== void 0) {
3149
- ss.glowRadius = params.glowRadius;
3150
- }
3151
- if (params.glowOpacity !== void 0) {
3152
- ss.glowOpacity = params.glowOpacity;
3153
- }
3154
- if (params.softEdgeRadius !== void 0) {
3155
- ss.softEdgeRadius = params.softEdgeRadius;
3156
- }
3157
- }
3158
- if (el.type === "image" || el.type === "picture") {
3159
- const img = el;
3160
- if (params.altText !== void 0) {
3161
- img.altText = params.altText;
3162
- }
3163
- if (params.cropLeft !== void 0) {
3164
- img.cropLeft = params.cropLeft;
3165
- }
3166
- if (params.cropTop !== void 0) {
3167
- img.cropTop = params.cropTop;
3168
- }
3169
- if (params.cropRight !== void 0) {
3170
- img.cropRight = params.cropRight;
3171
- }
3172
- if (params.cropBottom !== void 0) {
3173
- img.cropBottom = params.cropBottom;
3174
- }
3175
- if (params.brightness !== void 0 || params.contrast !== void 0 || params.grayscale !== void 0) {
3176
- if (!img.imageEffects) {
3177
- img.imageEffects = {};
3178
- }
3179
- if (params.brightness !== void 0) {
3180
- img.imageEffects.brightness = params.brightness;
3181
- }
3182
- if (params.contrast !== void 0) {
3183
- img.imageEffects.contrast = params.contrast;
3184
- }
3185
- if (params.grayscale !== void 0) {
3186
- img.imageEffects.grayscale = params.grayscale;
3187
- }
3188
- }
3189
- }
3190
- return {
3191
- pptxData: ctx.pptxData,
3192
- dirty: true,
3193
- result: { elementId: params.elementId }
3194
- };
3195
- }
3196
- function runAccessibilityCheck(ctx) {
3197
- const issues = [];
3198
- for (let si = 0; si < ctx.pptxData.slides.length; si++) {
3199
- const slide = ctx.pptxData.slides[si];
3200
- let slideHasText = false;
3201
- for (const el of slide.elements) {
3202
- if (el.type === "image" || el.type === "picture") {
3203
- const img = el;
3204
- if (!img.altText) {
3205
- issues.push({
3206
- slideIndex: si,
3207
- elementId: el.id,
3208
- severity: "error",
3209
- message: `Image element '${el.id}' has no alt text.`
3210
- });
3211
- }
3212
- }
3213
- if (pptxViewerCore.hasTextProperties(el)) {
3214
- const textEl = el;
3215
- slideHasText = true;
3216
- if (!textEl.text || textEl.text.trim() === "") {
3217
- issues.push({
3218
- slideIndex: si,
3219
- elementId: el.id,
3220
- severity: "info",
3221
- message: `Text element '${el.id}' is empty.`
3222
- });
3223
- }
3224
- const fs = textEl.textStyle?.fontSize;
3225
- if (fs !== void 0 && fs < 10) {
3226
- issues.push({
3227
- slideIndex: si,
3228
- elementId: el.id,
3229
- severity: "warning",
3230
- message: `Text element '${el.id}' has very small font size (${fs}pt < 10pt).`
3231
- });
3232
- }
3233
- const textColor = textEl.textStyle?.color?.toLowerCase();
3234
- const bgColor = slide.backgroundColor?.toLowerCase();
3235
- if (textColor && bgColor && textColor === bgColor) {
3236
- issues.push({
3237
- slideIndex: si,
3238
- elementId: el.id,
3239
- severity: "error",
3240
- message: `Text element '${el.id}' has same color as slide background (${textColor}).`
3241
- });
3242
- }
3243
- }
3244
- }
3245
- if (!slideHasText) {
3246
- issues.push({
3247
- slideIndex: si,
3248
- severity: "warning",
3249
- message: `Slide ${si + 1} contains no text elements.`
3250
- });
3251
- }
3252
- }
3253
- const errorCount = issues.filter((i) => i.severity === "error").length;
3254
- const warningCount = issues.filter((i) => i.severity === "warning").length;
3255
- const infoCount = issues.filter((i) => i.severity === "info").length;
3256
- return {
3257
- pptxData: ctx.pptxData,
3258
- dirty: false,
3259
- result: {
3260
- slideCount: ctx.pptxData.slides.length,
3261
- totalIssues: issues.length,
3262
- errorCount,
3263
- warningCount,
3264
- infoCount,
3265
- issues
3266
- }
3267
- };
3268
- }
3269
3318
  function findTableElement(ctx, slideIndex, elementId) {
3270
3319
  const err = validateSlideIndex(slideIndex, ctx.pptxData.slides.length);
3271
3320
  if (err) {