pptx-viewer-mcp 1.1.23 → 1.1.26

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 ADDED
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
5
+ by [git-cliff](https://git-cliff.org); do not edit it by hand.
6
+
7
+ ## [1.1.26](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-viewer-mcp@1.1.26) - 2026-06-21
8
+
9
+ ### Features
10
+
11
+ - Extracted more shared logic into the shared package (by @ChristopherVR) ([977c608](https://github.com/ChristopherVR/pptx-viewer/commit/977c608ecdb142908b38aaa37104d983275b705b))
12
+
13
+ ### Documentation
14
+
15
+ - Sharpen npm descriptions and keywords for discoverability (by @ChristopherVR) ([8fea56d](https://github.com/ChristopherVR/pptx-viewer/commit/8fea56d7650f7dc2f3167dea97b94b612a03a4e7))
16
+
17
+ ## [1.1.24](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-viewer-mcp@1.1.24) - 2026-06-20
18
+
19
+ ### Features
20
+
21
+ - **tools:** Scaffold @pptx-viewer/tools package (by @ChristopherVR) ([f34b949](https://github.com/ChristopherVR/pptx-viewer/commit/f34b949e0f6d6460710aa223146399dfbc38a436))
22
+
23
+ ### Bug Fixes
24
+
25
+ - **tools:** Ship pptx-viewer-core as a dependency so npx installs it (by @ChristopherVR) ([da33db1](https://github.com/ChristopherVR/pptx-viewer/commit/da33db11281f3573dc49defaba7e7404e59bc43f))
26
+ - **deps:** Unblock install after the 1.1.23 version alignment (by @ChristopherVR) ([542a92d](https://github.com/ChristopherVR/pptx-viewer/commit/542a92dcafe2041e8b1c3cb4b371ef0353a470c9))
27
+
28
+ ### Documentation
29
+
30
+ - Streamline npm READMEs and add badges, screenshots, demo links (by @ChristopherVR) ([92e980d](https://github.com/ChristopherVR/pptx-viewer/commit/92e980d434900abd223c4d70c6cae19a623f9ca8))
31
+ - **tools:** Make MCP first-class in README and drop em-dashes (by @ChristopherVR) ([89ebd64](https://github.com/ChristopherVR/pptx-viewer/commit/89ebd6453719cf46a4e655dfe689c9d5fae19549))
32
+
33
+ ### Build & CI
34
+
35
+ - Add @pptx-viewer/tools to test, release, and publish pipeline (by @ChristopherVR) ([0e2ff95](https://github.com/ChristopherVR/pptx-viewer/commit/0e2ff9579a8ea039d4367d69f13998560ee9313d))
36
+ - Independent per-package versioning, tags, and changelogs (by @ChristopherVR) ([79595d9](https://github.com/ChristopherVR/pptx-viewer/commit/79595d972d7c4102e8b1e1e3926f439486f76ba1))
37
+
38
+ ### Dependencies
39
+
40
+ - **deps:** Update all dependencies to latest (by @ChristopherVR) ([e3287c0](https://github.com/ChristopherVR/pptx-viewer/commit/e3287c03ff58b1a1ae103ed32a513468a454a084))
41
+ - **deps:** Bump all workspace manifest floors to latest (by @ChristopherVR) ([890c33d](https://github.com/ChristopherVR/pptx-viewer/commit/890c33d667a39480a69e6a3da893964382993b29))
42
+
43
+ ### Chores
44
+
45
+ - **tools:** Bump @pptx-viewer/tools to v1.1.0 (by @ChristopherVR) ([c15aba6](https://github.com/ChristopherVR/pptx-viewer/commit/c15aba600c5f2a1137acb157b7dab896e659f37c))
46
+ - Rename package to pptx-viewer-mcp and publish to npm (by @ChristopherVR) ([9cb8a25](https://github.com/ChristopherVR/pptx-viewer/commit/9cb8a2567082b9bfdc91efee0b91cf2cbe2aa1c4))
47
+ - Bump all packages to 1.x.1 patch versions (by @ChristopherVR) ([c75205a](https://github.com/ChristopherVR/pptx-viewer/commit/c75205a96cc7797d1647ac4705395b7707ac8910))
48
+ - Bump all packages to minor versions for SDK table support (by @ChristopherVR) ([2d4b635](https://github.com/ChristopherVR/pptx-viewer/commit/2d4b6351b0bf328f8a556cf593733fd8ad36c7b5))
49
+ - Bump dependencies to latest and minor-bump packages for parity work (by @ChristopherVR) ([da19fdf](https://github.com/ChristopherVR/pptx-viewer/commit/da19fdf9a4670d274d9973b67aa22d34217b8555))
50
+ - Roll TypeScript back to 5.9.x; quiet new oxlint vitest rules (by @ChristopherVR) ([713c020](https://github.com/ChristopherVR/pptx-viewer/commit/713c020ac2428db0fb1eb6cb30e56b2cff19a80f))
51
+ - Relicense from MIT to Apache-2.0 (by @ChristopherVR) ([e12f926](https://github.com/ChristopherVR/pptx-viewer/commit/e12f9266f02bebbfc218986b617c418fee43a56b))
package/README.md CHANGED
@@ -1,146 +1,151 @@
1
1
  # pptx-viewer-mcp
2
2
 
3
- Pure tool functions, Zod schemas, a Y.Doc collaboration codec, and an MCP server for PPTX manipulation — all built on top of `pptx-viewer-core`.
3
+ [![npm](https://img.shields.io/npm/v/pptx-viewer-mcp.svg)](https://www.npmjs.com/package/pptx-viewer-mcp)
4
+ [![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
4
5
 
5
- ## Installation
6
+ Edit PowerPoint files from an AI agent. It ships a ready-to-run [MCP](https://modelcontextprotocol.io) server that gives an agent 25 PowerPoint editing tools (for example "add a slide" or "replace text"). The same tools are also available as plain functions you can call yourself, along with their Zod input schemas and a codec for real-time collaboration (Y.Doc). The [`pptx-viewer-core`](https://www.npmjs.com/package/pptx-viewer-core) engine is bundled in, so a single install gives you everything and you never install the core separately.
6
7
 
7
- ```sh
8
- npm install pptx-viewer-mcp pptx-viewer-core
9
- # optional — required only for the collaboration codec
10
- npm install yjs
11
- ```
12
-
13
- ## Exports
8
+ - **Live demo:** https://christophervr.github.io/pptx-viewer/demo/
9
+ - **Docs:** https://christophervr.github.io/pptx-viewer/
14
10
 
15
- | Entry point | Contents |
16
- | ------------------------- | ------------------------------------------------------- |
17
- | `pptx-viewer-mcp` | 24 tool functions + provider types + execution pipeline |
18
- | `pptx-viewer-mcp/schemas` | Zod schemas for every tool input |
19
- | `pptx-viewer-mcp/codec` | `PptxCodec` — Y.Doc ↔ PPTX bytes codec |
20
- | `pptx-viewer-mcp/mcp` | `createServer()` — programmatic MCP server factory |
11
+ ## Quick start
21
12
 
22
- ## Tool reference
13
+ This package is, first and foremost, an MCP server. The recommended way to use it is to point an MCP client at the published binary. Calling the tools as a library is a secondary, lower-level option (see [Use as a library](#use-as-a-library)).
23
14
 
24
- **Slide (8):** `getSlide`, `addSlide`, `deleteSlides`, `reorderSlides`, `duplicateSlide`, `updateSlideProperties`, `setSlideTransition`, `setCanvasSize`
15
+ ### 1. Run as an MCP server (recommended)
25
16
 
26
- **Element (9):** `addElement`, `updateElement`, `deleteElements`, `arrangeElements`, `cloneElement`, `setElementAnimation`, `groupElements`, `ungroupElements`, `batchUpdateElements`
17
+ No clone, no build, no separate core install. Point your MCP client at the published binary via `npx`:
27
18
 
28
- **Table (2):** `updateTableCells`, `manageTableStructure`
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "pptx": {
23
+ "command": "npx",
24
+ "args": ["pptx-viewer-mcp"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
29
 
30
- **Style (2):** `updateElementStyle`, `runAccessibilityCheck`
30
+ That is the entire setup. `npx` downloads `pptx-viewer-mcp` and its bundled `pptx-viewer-core` engine on first run, so there is nothing else to install. Add the config above, restart your client, and the tools are available.
31
31
 
32
- **Content (3):** `findText`, `replaceText`, `manageComments`
32
+ This works in Claude Desktop, Claude Code, Cursor, and any MCP-compatible client. All 25 tools are exposed over stdio in snake_case (for example `add_slide`, `batch_update_elements`). Every tool takes a `filePath` argument and the server handles load and save internally. File access is scoped to a root directory (`PPTX_TOOLS_ROOT`, defaulting to the process working directory) and restricted to `.pptx` and `.ppt` files.
33
33
 
34
- **Conversion (1):** `convertToMarkdown`
34
+ > If you prefer a global install (`npm i -g pptx-viewer-mcp`), the same server is available as the `pptx-tools` binary. Point `command` at `pptx-tools` with no args.
35
35
 
36
- ---
36
+ ### 2. Use as a library (advanced)
37
37
 
38
- ## Quick start
38
+ If you are embedding PPTX editing in your own app instead of running the MCP server, install the package once:
39
39
 
40
- ### 1. Using tool functions directly
40
+ ```sh
41
+ npm install pptx-viewer-mcp
42
+ # optional, only for the Y.Doc collaboration codec:
43
+ npm install yjs
44
+ ```
41
45
 
42
- Every tool is a pure function — no file I/O, no framework dependencies.
46
+ The `pptx-viewer-core` engine is a regular dependency, so it installs automatically. You import everything you need (tools and the engine) from `pptx-viewer-mcp`, with no separate `pptx-viewer-core` install or import:
43
47
 
44
48
  ```ts
45
- import { PptxHandler } from 'pptx-viewer-core';
46
- import { addSlide, getSlide } from 'pptx-viewer-mcp';
49
+ import { readFile, writeFile } from 'node:fs/promises';
50
+ import { PptxHandler, addSlide, getSlide } from 'pptx-viewer-mcp';
47
51
 
48
- // Load
49
52
  const handler = new PptxHandler();
50
- const bytes = await fs.readFile('deck.pptx');
53
+ const bytes = await readFile('deck.pptx');
51
54
  const pptxData = await handler.load(bytes.buffer);
52
55
 
53
- // Run a tool
54
56
  const ctx = { pptxData };
55
57
  const { pptxData: updated, dirty } = addSlide(ctx, { insertAfterIndex: 0 });
56
58
 
57
- // Save when dirty
58
59
  if (dirty) {
59
60
  const out = await handler.save(updated.slides);
60
- await fs.writeFile('deck.pptx', out);
61
+ await writeFile('deck.pptx', out);
61
62
  }
62
63
 
63
- // Inspect a slide
64
64
  const { result } = getSlide(ctx, { slideIndex: 0 });
65
65
  console.log(result.elements);
66
66
  ```
67
67
 
68
- ### 2. Using the MCP server
68
+ See [Use as a library](#use-as-a-library) below for the higher-level `executeToolWithContext` helper that handles the load and save cycle for you.
69
69
 
70
- Add to your MCP client configuration (Claude Desktop, Cursor, etc.):
70
+ ## Exports
71
71
 
72
- ```json
73
- {
74
- "mcpServers": {
75
- "pptx": {
76
- "command": "npx",
77
- "args": ["pptx-viewer-mcp"]
78
- }
79
- }
80
- }
81
- ```
72
+ | Entry point | Contents |
73
+ | ------------------------- | --------------------------------------------------------------------------------------- |
74
+ | `pptx-viewer-mcp` | 25 tool functions, the `PptxHandler` engine, provider types, and the execution pipeline |
75
+ | `pptx-viewer-mcp/schemas` | Zod schemas for every tool input |
76
+ | `pptx-viewer-mcp/codec` | `PptxCodec`, the Y.Doc to PPTX bytes codec |
77
+ | `pptx-viewer-mcp/mcp` | `createServer()`, the programmatic MCP server factory |
78
+
79
+ ## Tools
80
+
81
+ | Group | Tools |
82
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
83
+ | Slide (8) | `getSlide` `addSlide` `deleteSlides` `reorderSlides` `duplicateSlide` `updateSlideProperties` `setSlideTransition` `setCanvasSize` |
84
+ | Element (9) | `addElement` `updateElement` `deleteElements` `arrangeElements` `cloneElement` `setElementAnimation` `groupElements` `ungroupElements` `batchUpdateElements` |
85
+ | Table (2) | `updateTableCells` `manageTableStructure` |
86
+ | Style (2) | `updateElementStyle` `runAccessibilityCheck` |
87
+ | Content (3) | `findText` `replaceText` `manageComments` |
88
+ | Conversion (1) | `convertToMarkdown` |
89
+
90
+ ## Use as a library
82
91
 
83
- The binary is also available as `pptx-tools` after a global install. All 24 tools are exposed over stdio with the same names in snake_case (e.g. `add_slide`, `batch_update_elements`). Every tool accepts a `filePath` parameter — the server handles load/save internally.
92
+ These APIs back the MCP server. Reach for them when you embed PPTX editing directly rather than running the server.
84
93
 
85
- ### 3. Using `executeToolWithContext` with providers
94
+ ### Call tools directly
86
95
 
87
- `executeToolWithContext` wraps the load → tool → save cycle and routes through a live Y.Doc collaboration room when one is available.
96
+ Each tool is a plain function: it takes the presentation data in and returns the changed data out, without reading or writing files itself and without depending on any framework. The basic load, edit, save loop is shown in [Use as a library (advanced)](#2-use-as-a-library-advanced) above.
97
+
98
+ ### Wrap load, tool, and save with `executeToolWithContext`
99
+
100
+ `executeToolWithContext` handles the load and save cycle and, when a collaboration room is supplied, routes changes through a live Y.Doc instead of the disk.
88
101
 
89
102
  ```ts
90
103
  import {
91
104
  executeToolWithContext,
92
105
  type ExecutionContext,
93
106
  type FileSystemProvider,
94
- type CollaborationProvider,
107
+ replaceText,
95
108
  } from 'pptx-viewer-mcp';
96
- import { replaceText } from 'pptx-viewer-mcp';
97
109
  import { readFile, writeFile } from 'node:fs/promises';
98
110
 
99
- // Minimal file-system-only provider
100
111
  const filesystem: FileSystemProvider = {
101
112
  readFile: (p) => readFile(p),
102
113
  writeFile: (p, data) => writeFile(p, data),
103
114
  };
104
115
 
105
- const execCtx: ExecutionContext = { filesystem };
106
-
107
- const result = await executeToolWithContext('deck.pptx', execCtx, (ctx) =>
116
+ const result = await executeToolWithContext('deck.pptx', { filesystem }, (ctx) =>
108
117
  replaceText(ctx, { find: 'Draft', replace: 'Final', caseSensitive: false }),
109
118
  );
110
119
 
111
120
  console.log(result.replacements, result.savedToDisk);
112
121
  ```
113
122
 
114
- When `execCtx.collaboration` is provided, `executeToolWithContext` automatically dehydrates the current Y.Doc state before running the tool and re-hydrates the room after saving, so remote peers receive the change without a file reload.
115
-
116
- ---
123
+ When you supply `collaboration` on the `ExecutionContext`, the tool reads the current shared (Y.Doc) state before running and writes the result back into it after saving, so other people in the session see the change without reloading the file.
117
124
 
118
125
  ## Architecture
119
126
 
120
127
  ```
121
128
  ToolContext { pptxData }
122
- │
123
- ▼
124
- tool function → ToolResult { pptxData, result, dirty }
125
- │
126
- ▼ (when dirty)
129
+ |
130
+ v
131
+ tool function -> ToolResult { pptxData, result, dirty }
132
+ |
133
+ v (when dirty)
127
134
  savePresentation()
128
- ├─ collaboration room → hydrate Y.Doc → broadcast
129
- └─ no room → writeFile to disk
135
+ |- collaboration room -> hydrate Y.Doc -> broadcast
136
+ '- no room -> writeFile to disk
130
137
  ```
131
138
 
132
- `CollaborationProvider`, `FileSystemProvider`, and `ViewerProvider` are plain interfaces — implement them for any runtime (Node, Electron, browser, edge worker).
133
-
134
- ---
139
+ `CollaborationProvider`, `FileSystemProvider`, and `ViewerProvider` are plain interfaces. Implement them for any runtime (Node, Electron, browser, or edge worker).
135
140
 
136
141
  ## Development
137
142
 
138
143
  ```sh
139
- bun run build # tsup → dist/
144
+ bun run build # tsup -> dist/
140
145
  bun run typecheck # tsc --noEmit
141
146
  bun run test # vitest run
142
147
  ```
143
148
 
144
149
  ## License
145
150
 
146
- MIT
151
+ Apache-2.0
@@ -3,13 +3,20 @@ import { Doc } from 'yjs';
3
3
  declare const ORIGIN_FILE_LOAD = "file-load";
4
4
  /**
5
5
  * Bidirectional format codec: file bytes <-> Y.Doc shared types.
6
+ *
7
+ * Y.Doc schema:
8
+ * pptx:meta Y.Map - width, height, widthEmu, heightEmu, sourceBytes
9
+ * pptx:slides Y.Array<Y.Map> - one map per slide
10
+ * Each slide Y.Map: scalar fields + `_`-prefixed JSON blobs + `elements` Y.Array<Y.Map>
11
+ * Each element Y.Map: scalar fields + `_`-prefixed JSON blobs + `textBody` Y.Text
12
+ * textBody Y.Text: one delta op per TextSegment (see text-body-codec.ts)
6
13
  */
7
14
  interface FormatCodec {
8
15
  readonly formatId: string;
9
16
  readonly extensions: string[];
10
- hydrate(ydoc: Doc, bytes: Uint8Array, origin?: string): Promise<void>;
11
- dehydrate(ydoc: Doc, dirtyPaths?: string[]): Promise<Uint8Array>;
12
- observe(ydoc: Doc, onChange: () => void): () => void;
17
+ hydrate: (ydoc: Doc, bytes: Uint8Array, origin?: string) => Promise<void>;
18
+ dehydrate: (ydoc: Doc, dirtyPaths?: string[]) => Promise<Uint8Array>;
19
+ observe: (ydoc: Doc, onChange: () => void) => () => void;
13
20
  }
14
21
  declare class PptxCodec implements FormatCodec {
15
22
  readonly formatId = "pptx";
@@ -3,13 +3,20 @@ import { Doc } from 'yjs';
3
3
  declare const ORIGIN_FILE_LOAD = "file-load";
4
4
  /**
5
5
  * Bidirectional format codec: file bytes <-> Y.Doc shared types.
6
+ *
7
+ * Y.Doc schema:
8
+ * pptx:meta Y.Map - width, height, widthEmu, heightEmu, sourceBytes
9
+ * pptx:slides Y.Array<Y.Map> - one map per slide
10
+ * Each slide Y.Map: scalar fields + `_`-prefixed JSON blobs + `elements` Y.Array<Y.Map>
11
+ * Each element Y.Map: scalar fields + `_`-prefixed JSON blobs + `textBody` Y.Text
12
+ * textBody Y.Text: one delta op per TextSegment (see text-body-codec.ts)
6
13
  */
7
14
  interface FormatCodec {
8
15
  readonly formatId: string;
9
16
  readonly extensions: string[];
10
- hydrate(ydoc: Doc, bytes: Uint8Array, origin?: string): Promise<void>;
11
- dehydrate(ydoc: Doc, dirtyPaths?: string[]): Promise<Uint8Array>;
12
- observe(ydoc: Doc, onChange: () => void): () => void;
17
+ hydrate: (ydoc: Doc, bytes: Uint8Array, origin?: string) => Promise<void>;
18
+ dehydrate: (ydoc: Doc, dirtyPaths?: string[]) => Promise<Uint8Array>;
19
+ observe: (ydoc: Doc, onChange: () => void) => () => void;
13
20
  }
14
21
  declare class PptxCodec implements FormatCodec {
15
22
  readonly formatId = "pptx";
@@ -3,6 +3,177 @@
3
3
  var pptxViewerCore = require('pptx-viewer-core');
4
4
  var yjs = require('yjs');
5
5
 
6
+ // src/codec/pptx-codec.ts
7
+
8
+ // src/codec/text-body-codec.ts
9
+ var EMPTY_PLACEHOLDER = "\u200B";
10
+ function buildAttrs(seg) {
11
+ const a = {};
12
+ const style = seg.style;
13
+ if (style && typeof style === "object" && Object.keys(style).length > 0) {
14
+ a.s = JSON.stringify(style);
15
+ }
16
+ if (seg.isParagraphBreak) {
17
+ a.pb = "1";
18
+ }
19
+ if (seg.isLineBreak) {
20
+ a.lb = "1";
21
+ }
22
+ if (seg.ep === true) {
23
+ a.ep = "1";
24
+ }
25
+ if (seg.bulletInfo) {
26
+ a.bi = JSON.stringify(seg.bulletInfo);
27
+ }
28
+ if (seg.paragraphLevel !== void 0) {
29
+ a.pl = String(seg.paragraphLevel);
30
+ }
31
+ if (seg.endParaRunProperties) {
32
+ a.pr = JSON.stringify(seg.endParaRunProperties);
33
+ }
34
+ if (typeof seg.fieldType === "string") {
35
+ a.ft = seg.fieldType;
36
+ }
37
+ if (typeof seg.fieldGuid === "string") {
38
+ a.fg = seg.fieldGuid;
39
+ }
40
+ if (seg.fieldGuidAttr === "uuid" || seg.fieldGuidAttr === "id") {
41
+ a.fga = seg.fieldGuidAttr;
42
+ }
43
+ if (seg.fieldParagraphPropertiesXml) {
44
+ a.fp = JSON.stringify(seg.fieldParagraphPropertiesXml);
45
+ }
46
+ if (seg.equationXml) {
47
+ a.eq = JSON.stringify(seg.equationXml);
48
+ }
49
+ if (typeof seg.equationNumber === "string") {
50
+ a.en = seg.equationNumber;
51
+ }
52
+ if (seg.breakRunProperties) {
53
+ a.br = JSON.stringify(seg.breakRunProperties);
54
+ }
55
+ if (typeof seg.rubyText === "string") {
56
+ a.rt = seg.rubyText;
57
+ }
58
+ if (typeof seg.rubyAlignment === "string") {
59
+ a.ra = seg.rubyAlignment;
60
+ }
61
+ if (seg.rubyFontSize !== void 0) {
62
+ a.rfs = String(seg.rubyFontSize);
63
+ }
64
+ if (seg.rubyStyle) {
65
+ a.rs = JSON.stringify(seg.rubyStyle);
66
+ }
67
+ return Object.keys(a).length > 0 ? a : void 0;
68
+ }
69
+ function encodeTextBodyToYText(segments, ytext) {
70
+ let offset = 0;
71
+ for (const raw of segments) {
72
+ const seg = raw;
73
+ const attrs = buildAttrs(seg);
74
+ if (seg.isParagraphBreak === true || seg.isLineBreak === true) {
75
+ ytext.insert(offset, "\n", attrs);
76
+ offset += 1;
77
+ } else if (typeof seg.text === "string" && seg.text.length > 0) {
78
+ ytext.insert(offset, seg.text, attrs);
79
+ offset += seg.text.length;
80
+ } else {
81
+ const epAttrs = { ep: "1", ...attrs ?? {} };
82
+ ytext.insert(offset, EMPTY_PLACEHOLDER, epAttrs);
83
+ offset += 1;
84
+ }
85
+ }
86
+ }
87
+ function decodeTextBodyFromYText(ytext) {
88
+ const delta = ytext.toDelta();
89
+ const segments = [];
90
+ for (const op of delta) {
91
+ if (typeof op.insert !== "string" || op.insert === "") {
92
+ continue;
93
+ }
94
+ const a = op.attributes ?? {};
95
+ const seg = { text: "", style: {} };
96
+ if (a.s) {
97
+ try {
98
+ seg.style = JSON.parse(a.s);
99
+ } catch {
100
+ seg.style = {};
101
+ }
102
+ }
103
+ if (a.pb === "1") {
104
+ seg.isParagraphBreak = true;
105
+ }
106
+ if (a.lb === "1") {
107
+ seg.isLineBreak = true;
108
+ }
109
+ if (a.bi) {
110
+ try {
111
+ seg.bulletInfo = JSON.parse(a.bi);
112
+ } catch {
113
+ }
114
+ }
115
+ if (a.pl !== void 0) {
116
+ seg.paragraphLevel = Number(a.pl);
117
+ }
118
+ if (a.pr) {
119
+ try {
120
+ seg.endParaRunProperties = JSON.parse(a.pr);
121
+ } catch {
122
+ }
123
+ }
124
+ if (a.ft) {
125
+ seg.fieldType = a.ft;
126
+ }
127
+ if (a.fg) {
128
+ seg.fieldGuid = a.fg;
129
+ }
130
+ if (a.fga === "uuid" || a.fga === "id") {
131
+ seg.fieldGuidAttr = a.fga;
132
+ }
133
+ if (a.fp) {
134
+ try {
135
+ seg.fieldParagraphPropertiesXml = JSON.parse(a.fp);
136
+ } catch {
137
+ }
138
+ }
139
+ if (a.eq) {
140
+ try {
141
+ seg.equationXml = JSON.parse(a.eq);
142
+ } catch {
143
+ }
144
+ }
145
+ if (a.en) {
146
+ seg.equationNumber = a.en;
147
+ }
148
+ if (a.br) {
149
+ try {
150
+ seg.breakRunProperties = JSON.parse(a.br);
151
+ } catch {
152
+ }
153
+ }
154
+ if (a.rt) {
155
+ seg.rubyText = a.rt;
156
+ }
157
+ if (a.ra) {
158
+ seg.rubyAlignment = a.ra;
159
+ }
160
+ if (a.rfs !== void 0) {
161
+ seg.rubyFontSize = Number(a.rfs);
162
+ }
163
+ if (a.rs) {
164
+ try {
165
+ seg.rubyStyle = JSON.parse(a.rs);
166
+ } catch {
167
+ }
168
+ }
169
+ if (op.insert !== "\n" && op.insert !== EMPTY_PLACEHOLDER) {
170
+ seg.text = op.insert;
171
+ }
172
+ segments.push(seg);
173
+ }
174
+ return segments;
175
+ }
176
+
6
177
  // src/codec/pptx-codec.ts
7
178
  var ORIGIN_FILE_LOAD = "file-load";
8
179
  var SCALAR_ELEMENT_KEYS = /* @__PURE__ */ new Set([
@@ -34,7 +205,6 @@ var SCALAR_ELEMENT_KEYS = /* @__PURE__ */ new Set([
34
205
  "promptText"
35
206
  ]);
36
207
  var COMPLEX_FIELD_MAP = {
37
- textSegments: "_textSegments",
38
208
  textStyle: "_textStyle",
39
209
  shapeStyle: "_shapeStyle",
40
210
  shapeAdjustments: "_shapeAdjustments",
@@ -122,8 +292,7 @@ var PptxCodec = class {
122
292
  slidesArray.delete(0, slidesArray.length);
123
293
  }
124
294
  for (const slide of pptxData.slides) {
125
- const slideMap = this._slideToYMap(slide);
126
- slidesArray.push([slideMap]);
295
+ slidesArray.push([this._slideToYMap(slide)]);
127
296
  }
128
297
  }, effectiveOrigin);
129
298
  }
@@ -132,8 +301,7 @@ var PptxCodec = class {
132
301
  const slidesArray = ydoc.getArray("pptx:slides");
133
302
  const slides = [];
134
303
  for (let i = 0; i < slidesArray.length; i++) {
135
- const slideMap = slidesArray.get(i);
136
- slides.push(this._yMapToSlide(slideMap));
304
+ slides.push(this._yMapToSlide(slidesArray.get(i)));
137
305
  }
138
306
  const sourceBytesArr = meta.get("sourceBytes");
139
307
  try {
@@ -154,27 +322,24 @@ var PptxCodec = class {
154
322
  observe(ydoc, onChange) {
155
323
  const slidesArray = ydoc.getArray("pptx:slides");
156
324
  const meta = ydoc.getMap("pptx:meta");
157
- const handler = () => onChange();
158
- slidesArray.observeDeep(handler);
159
- meta.observeDeep(handler);
325
+ slidesArray.observeDeep(onChange);
326
+ meta.observeDeep(onChange);
160
327
  return () => {
161
- slidesArray.unobserveDeep(handler);
162
- meta.unobserveDeep(handler);
328
+ slidesArray.unobserveDeep(onChange);
329
+ meta.unobserveDeep(onChange);
163
330
  };
164
331
  }
165
332
  _slideToYMap(slide) {
166
333
  const slideMap = new yjs.Map();
167
- const slideRecord = slide;
334
+ const rec = slide;
168
335
  for (const key of SCALAR_SLIDE_KEYS) {
169
- const value = slideRecord[key];
170
- if (value !== void 0) {
171
- slideMap.set(key, value);
336
+ if (rec[key] !== void 0) {
337
+ slideMap.set(key, rec[key]);
172
338
  }
173
339
  }
174
340
  for (const [original, prefixed] of Object.entries(COMPLEX_SLIDE_FIELD_MAP)) {
175
- const value = slideRecord[original];
176
- if (value !== void 0) {
177
- slideMap.set(prefixed, JSON.stringify(value));
341
+ if (rec[original] !== void 0) {
342
+ slideMap.set(prefixed, JSON.stringify(rec[original]));
178
343
  }
179
344
  }
180
345
  const elementsArray = new yjs.Array();
@@ -205,11 +370,10 @@ var PptxCodec = class {
205
370
  const elements = [];
206
371
  if (elementsArray) {
207
372
  for (let i = 0; i < elementsArray.length; i++) {
208
- const elemMap = elementsArray.get(i);
209
- elements.push(this._yMapToElement(elemMap));
373
+ elements.push(this._yMapToElement(elementsArray.get(i)));
210
374
  }
211
375
  }
212
- slide["elements"] = elements;
376
+ slide.elements = elements;
213
377
  return slide;
214
378
  }
215
379
  _elementToYMap(element) {
@@ -219,7 +383,13 @@ var PptxCodec = class {
219
383
  if (value === void 0) {
220
384
  continue;
221
385
  }
222
- if (SCALAR_ELEMENT_KEYS.has(key)) {
386
+ if (key === "textSegments") {
387
+ if (Array.isArray(value)) {
388
+ const ytext = new yjs.Text();
389
+ encodeTextBodyToYText(value, ytext);
390
+ elemMap.set("textBody", ytext);
391
+ }
392
+ } else if (SCALAR_ELEMENT_KEYS.has(key)) {
223
393
  elemMap.set(key, value);
224
394
  } else if (COMPLEX_FIELD_MAP[key]) {
225
395
  elemMap.set(COMPLEX_FIELD_MAP[key], JSON.stringify(value));
@@ -230,7 +400,9 @@ var PptxCodec = class {
230
400
  _yMapToElement(elemMap) {
231
401
  const element = {};
232
402
  elemMap.forEach((value, key) => {
233
- if (REVERSE_COMPLEX_MAP[key]) {
403
+ if (key === "textBody" && value instanceof yjs.Text) {
404
+ element.textSegments = decodeTextBodyFromYText(value);
405
+ } else if (REVERSE_COMPLEX_MAP[key]) {
234
406
  try {
235
407
  element[REVERSE_COMPLEX_MAP[key]] = JSON.parse(value);
236
408
  } catch {
@@ -1,5 +1,176 @@
1
1
  import { PptxHandler } from 'pptx-viewer-core';
2
- import { Array as Array$1, Map } from 'yjs';
2
+ import { Array as Array$1, Map, Text } from 'yjs';
3
+
4
+ // src/codec/pptx-codec.ts
5
+
6
+ // src/codec/text-body-codec.ts
7
+ var EMPTY_PLACEHOLDER = "\u200B";
8
+ function buildAttrs(seg) {
9
+ const a = {};
10
+ const style = seg.style;
11
+ if (style && typeof style === "object" && Object.keys(style).length > 0) {
12
+ a.s = JSON.stringify(style);
13
+ }
14
+ if (seg.isParagraphBreak) {
15
+ a.pb = "1";
16
+ }
17
+ if (seg.isLineBreak) {
18
+ a.lb = "1";
19
+ }
20
+ if (seg.ep === true) {
21
+ a.ep = "1";
22
+ }
23
+ if (seg.bulletInfo) {
24
+ a.bi = JSON.stringify(seg.bulletInfo);
25
+ }
26
+ if (seg.paragraphLevel !== void 0) {
27
+ a.pl = String(seg.paragraphLevel);
28
+ }
29
+ if (seg.endParaRunProperties) {
30
+ a.pr = JSON.stringify(seg.endParaRunProperties);
31
+ }
32
+ if (typeof seg.fieldType === "string") {
33
+ a.ft = seg.fieldType;
34
+ }
35
+ if (typeof seg.fieldGuid === "string") {
36
+ a.fg = seg.fieldGuid;
37
+ }
38
+ if (seg.fieldGuidAttr === "uuid" || seg.fieldGuidAttr === "id") {
39
+ a.fga = seg.fieldGuidAttr;
40
+ }
41
+ if (seg.fieldParagraphPropertiesXml) {
42
+ a.fp = JSON.stringify(seg.fieldParagraphPropertiesXml);
43
+ }
44
+ if (seg.equationXml) {
45
+ a.eq = JSON.stringify(seg.equationXml);
46
+ }
47
+ if (typeof seg.equationNumber === "string") {
48
+ a.en = seg.equationNumber;
49
+ }
50
+ if (seg.breakRunProperties) {
51
+ a.br = JSON.stringify(seg.breakRunProperties);
52
+ }
53
+ if (typeof seg.rubyText === "string") {
54
+ a.rt = seg.rubyText;
55
+ }
56
+ if (typeof seg.rubyAlignment === "string") {
57
+ a.ra = seg.rubyAlignment;
58
+ }
59
+ if (seg.rubyFontSize !== void 0) {
60
+ a.rfs = String(seg.rubyFontSize);
61
+ }
62
+ if (seg.rubyStyle) {
63
+ a.rs = JSON.stringify(seg.rubyStyle);
64
+ }
65
+ return Object.keys(a).length > 0 ? a : void 0;
66
+ }
67
+ function encodeTextBodyToYText(segments, ytext) {
68
+ let offset = 0;
69
+ for (const raw of segments) {
70
+ const seg = raw;
71
+ const attrs = buildAttrs(seg);
72
+ if (seg.isParagraphBreak === true || seg.isLineBreak === true) {
73
+ ytext.insert(offset, "\n", attrs);
74
+ offset += 1;
75
+ } else if (typeof seg.text === "string" && seg.text.length > 0) {
76
+ ytext.insert(offset, seg.text, attrs);
77
+ offset += seg.text.length;
78
+ } else {
79
+ const epAttrs = { ep: "1", ...attrs ?? {} };
80
+ ytext.insert(offset, EMPTY_PLACEHOLDER, epAttrs);
81
+ offset += 1;
82
+ }
83
+ }
84
+ }
85
+ function decodeTextBodyFromYText(ytext) {
86
+ const delta = ytext.toDelta();
87
+ const segments = [];
88
+ for (const op of delta) {
89
+ if (typeof op.insert !== "string" || op.insert === "") {
90
+ continue;
91
+ }
92
+ const a = op.attributes ?? {};
93
+ const seg = { text: "", style: {} };
94
+ if (a.s) {
95
+ try {
96
+ seg.style = JSON.parse(a.s);
97
+ } catch {
98
+ seg.style = {};
99
+ }
100
+ }
101
+ if (a.pb === "1") {
102
+ seg.isParagraphBreak = true;
103
+ }
104
+ if (a.lb === "1") {
105
+ seg.isLineBreak = true;
106
+ }
107
+ if (a.bi) {
108
+ try {
109
+ seg.bulletInfo = JSON.parse(a.bi);
110
+ } catch {
111
+ }
112
+ }
113
+ if (a.pl !== void 0) {
114
+ seg.paragraphLevel = Number(a.pl);
115
+ }
116
+ if (a.pr) {
117
+ try {
118
+ seg.endParaRunProperties = JSON.parse(a.pr);
119
+ } catch {
120
+ }
121
+ }
122
+ if (a.ft) {
123
+ seg.fieldType = a.ft;
124
+ }
125
+ if (a.fg) {
126
+ seg.fieldGuid = a.fg;
127
+ }
128
+ if (a.fga === "uuid" || a.fga === "id") {
129
+ seg.fieldGuidAttr = a.fga;
130
+ }
131
+ if (a.fp) {
132
+ try {
133
+ seg.fieldParagraphPropertiesXml = JSON.parse(a.fp);
134
+ } catch {
135
+ }
136
+ }
137
+ if (a.eq) {
138
+ try {
139
+ seg.equationXml = JSON.parse(a.eq);
140
+ } catch {
141
+ }
142
+ }
143
+ if (a.en) {
144
+ seg.equationNumber = a.en;
145
+ }
146
+ if (a.br) {
147
+ try {
148
+ seg.breakRunProperties = JSON.parse(a.br);
149
+ } catch {
150
+ }
151
+ }
152
+ if (a.rt) {
153
+ seg.rubyText = a.rt;
154
+ }
155
+ if (a.ra) {
156
+ seg.rubyAlignment = a.ra;
157
+ }
158
+ if (a.rfs !== void 0) {
159
+ seg.rubyFontSize = Number(a.rfs);
160
+ }
161
+ if (a.rs) {
162
+ try {
163
+ seg.rubyStyle = JSON.parse(a.rs);
164
+ } catch {
165
+ }
166
+ }
167
+ if (op.insert !== "\n" && op.insert !== EMPTY_PLACEHOLDER) {
168
+ seg.text = op.insert;
169
+ }
170
+ segments.push(seg);
171
+ }
172
+ return segments;
173
+ }
3
174
 
4
175
  // src/codec/pptx-codec.ts
5
176
  var ORIGIN_FILE_LOAD = "file-load";
@@ -32,7 +203,6 @@ var SCALAR_ELEMENT_KEYS = /* @__PURE__ */ new Set([
32
203
  "promptText"
33
204
  ]);
34
205
  var COMPLEX_FIELD_MAP = {
35
- textSegments: "_textSegments",
36
206
  textStyle: "_textStyle",
37
207
  shapeStyle: "_shapeStyle",
38
208
  shapeAdjustments: "_shapeAdjustments",
@@ -120,8 +290,7 @@ var PptxCodec = class {
120
290
  slidesArray.delete(0, slidesArray.length);
121
291
  }
122
292
  for (const slide of pptxData.slides) {
123
- const slideMap = this._slideToYMap(slide);
124
- slidesArray.push([slideMap]);
293
+ slidesArray.push([this._slideToYMap(slide)]);
125
294
  }
126
295
  }, effectiveOrigin);
127
296
  }
@@ -130,8 +299,7 @@ var PptxCodec = class {
130
299
  const slidesArray = ydoc.getArray("pptx:slides");
131
300
  const slides = [];
132
301
  for (let i = 0; i < slidesArray.length; i++) {
133
- const slideMap = slidesArray.get(i);
134
- slides.push(this._yMapToSlide(slideMap));
302
+ slides.push(this._yMapToSlide(slidesArray.get(i)));
135
303
  }
136
304
  const sourceBytesArr = meta.get("sourceBytes");
137
305
  try {
@@ -152,27 +320,24 @@ var PptxCodec = class {
152
320
  observe(ydoc, onChange) {
153
321
  const slidesArray = ydoc.getArray("pptx:slides");
154
322
  const meta = ydoc.getMap("pptx:meta");
155
- const handler = () => onChange();
156
- slidesArray.observeDeep(handler);
157
- meta.observeDeep(handler);
323
+ slidesArray.observeDeep(onChange);
324
+ meta.observeDeep(onChange);
158
325
  return () => {
159
- slidesArray.unobserveDeep(handler);
160
- meta.unobserveDeep(handler);
326
+ slidesArray.unobserveDeep(onChange);
327
+ meta.unobserveDeep(onChange);
161
328
  };
162
329
  }
163
330
  _slideToYMap(slide) {
164
331
  const slideMap = new Map();
165
- const slideRecord = slide;
332
+ const rec = slide;
166
333
  for (const key of SCALAR_SLIDE_KEYS) {
167
- const value = slideRecord[key];
168
- if (value !== void 0) {
169
- slideMap.set(key, value);
334
+ if (rec[key] !== void 0) {
335
+ slideMap.set(key, rec[key]);
170
336
  }
171
337
  }
172
338
  for (const [original, prefixed] of Object.entries(COMPLEX_SLIDE_FIELD_MAP)) {
173
- const value = slideRecord[original];
174
- if (value !== void 0) {
175
- slideMap.set(prefixed, JSON.stringify(value));
339
+ if (rec[original] !== void 0) {
340
+ slideMap.set(prefixed, JSON.stringify(rec[original]));
176
341
  }
177
342
  }
178
343
  const elementsArray = new Array$1();
@@ -203,11 +368,10 @@ var PptxCodec = class {
203
368
  const elements = [];
204
369
  if (elementsArray) {
205
370
  for (let i = 0; i < elementsArray.length; i++) {
206
- const elemMap = elementsArray.get(i);
207
- elements.push(this._yMapToElement(elemMap));
371
+ elements.push(this._yMapToElement(elementsArray.get(i)));
208
372
  }
209
373
  }
210
- slide["elements"] = elements;
374
+ slide.elements = elements;
211
375
  return slide;
212
376
  }
213
377
  _elementToYMap(element) {
@@ -217,7 +381,13 @@ var PptxCodec = class {
217
381
  if (value === void 0) {
218
382
  continue;
219
383
  }
220
- if (SCALAR_ELEMENT_KEYS.has(key)) {
384
+ if (key === "textSegments") {
385
+ if (Array.isArray(value)) {
386
+ const ytext = new Text();
387
+ encodeTextBodyToYText(value, ytext);
388
+ elemMap.set("textBody", ytext);
389
+ }
390
+ } else if (SCALAR_ELEMENT_KEYS.has(key)) {
221
391
  elemMap.set(key, value);
222
392
  } else if (COMPLEX_FIELD_MAP[key]) {
223
393
  elemMap.set(COMPLEX_FIELD_MAP[key], JSON.stringify(value));
@@ -228,7 +398,9 @@ var PptxCodec = class {
228
398
  _yMapToElement(elemMap) {
229
399
  const element = {};
230
400
  elemMap.forEach((value, key) => {
231
- if (REVERSE_COMPLEX_MAP[key]) {
401
+ if (key === "textBody" && value instanceof Text) {
402
+ element.textSegments = decodeTextBodyFromYText(value);
403
+ } else if (REVERSE_COMPLEX_MAP[key]) {
232
404
  try {
233
405
  element[REVERSE_COMPLEX_MAP[key]] = JSON.parse(value);
234
406
  } catch {
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { PptxData, PptxElement, PptxSlide, ShapeStyle } from 'pptx-viewer-core';
2
+ export { PptxData, PptxElement, PptxHandler, PptxSlide } from 'pptx-viewer-core';
2
3
  import { Doc } from 'yjs';
3
4
 
4
5
  /**
@@ -87,7 +88,7 @@ declare function loadPresentation(filePath: string, execCtx: ExecutionContext):
87
88
  rawBytes: Uint8Array;
88
89
  }>;
89
90
  /**
90
- * Save mutated PptxData back — routes through collaboration Y.Doc or disk.
91
+ * Save mutated PptxData back: routes through collaboration Y.Doc or disk.
91
92
  */
92
93
  declare function savePresentation(filePath: string, pptxData: PptxData, rawBytes: Uint8Array, execCtx: ExecutionContext): Promise<{
93
94
  savedToDisk: boolean;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { PptxData, PptxElement, PptxSlide, ShapeStyle } from 'pptx-viewer-core';
2
+ export { PptxData, PptxElement, PptxHandler, PptxSlide } from 'pptx-viewer-core';
2
3
  import { Doc } from 'yjs';
3
4
 
4
5
  /**
@@ -87,7 +88,7 @@ declare function loadPresentation(filePath: string, execCtx: ExecutionContext):
87
88
  rawBytes: Uint8Array;
88
89
  }>;
89
90
  /**
90
- * Save mutated PptxData back — routes through collaboration Y.Doc or disk.
91
+ * Save mutated PptxData back: routes through collaboration Y.Doc or disk.
91
92
  */
92
93
  declare function savePresentation(filePath: string, pptxData: PptxData, rawBytes: Uint8Array, execCtx: ExecutionContext): Promise<{
93
94
  savedToDisk: boolean;
package/dist/index.js CHANGED
@@ -1254,7 +1254,7 @@ function runAccessibilityCheck(ctx) {
1254
1254
  }
1255
1255
  var MAX_REGEX_LEN = 200;
1256
1256
  function hasNestedUnboundedQuantifier(pattern) {
1257
- return /\([^)]*[+*]\)[+*]/.test(pattern);
1257
+ return /\([^)]*[+*]\)[+*]/u.test(pattern);
1258
1258
  }
1259
1259
  function buildSearchRegex(query, useRegex, caseSensitive) {
1260
1260
  if (useRegex) {
@@ -1265,7 +1265,7 @@ function buildSearchRegex(query, useRegex, caseSensitive) {
1265
1265
  return null;
1266
1266
  }
1267
1267
  }
1268
- const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1268
+ const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1269
1269
  const flags = caseSensitive ? "g" : "gi";
1270
1270
  try {
1271
1271
  return new RegExp(pattern, flags);
@@ -1589,6 +1589,10 @@ async function convertToMarkdown(ctx, params) {
1589
1589
  };
1590
1590
  }
1591
1591
 
1592
+ Object.defineProperty(exports, "PptxHandler", {
1593
+ enumerable: true,
1594
+ get: function () { return pptxViewerCore.PptxHandler; }
1595
+ });
1592
1596
  exports.addElement = addElement;
1593
1597
  exports.addSlide = addSlide;
1594
1598
  exports.arrangeElements = arrangeElements;
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { PptxHandler, hasTextProperties, PptxMarkdownConverter } from 'pptx-viewer-core';
2
+ export { PptxHandler } from 'pptx-viewer-core';
2
3
  import { resolve, isAbsolute, sep } from 'path';
3
4
 
4
5
  // src/execution.ts
@@ -1252,7 +1253,7 @@ function runAccessibilityCheck(ctx) {
1252
1253
  }
1253
1254
  var MAX_REGEX_LEN = 200;
1254
1255
  function hasNestedUnboundedQuantifier(pattern) {
1255
- return /\([^)]*[+*]\)[+*]/.test(pattern);
1256
+ return /\([^)]*[+*]\)[+*]/u.test(pattern);
1256
1257
  }
1257
1258
  function buildSearchRegex(query, useRegex, caseSensitive) {
1258
1259
  if (useRegex) {
@@ -1263,7 +1264,7 @@ function buildSearchRegex(query, useRegex, caseSensitive) {
1263
1264
  return null;
1264
1265
  }
1265
1266
  }
1266
- const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1267
+ const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1267
1268
  const flags = caseSensitive ? "g" : "gi";
1268
1269
  try {
1269
1270
  return new RegExp(pattern, flags);
package/dist/mcp/index.js CHANGED
@@ -372,7 +372,7 @@ function validateSlideIndex(slideIndex, slideCount) {
372
372
  // src/tools/content-tools.ts
373
373
  var MAX_REGEX_LEN = 200;
374
374
  function hasNestedUnboundedQuantifier(pattern) {
375
- return /\([^)]*[+*]\)[+*]/.test(pattern);
375
+ return /\([^)]*[+*]\)[+*]/u.test(pattern);
376
376
  }
377
377
  function buildSearchRegex(query, useRegex, caseSensitive) {
378
378
  if (useRegex) {
@@ -383,7 +383,7 @@ function buildSearchRegex(query, useRegex, caseSensitive) {
383
383
  return null;
384
384
  }
385
385
  }
386
- const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
386
+ const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
387
387
  const flags = caseSensitive ? "g" : "gi";
388
388
  try {
389
389
  return new RegExp(pattern, flags);
@@ -1876,7 +1876,7 @@ function createServer() {
1876
1876
  server2.registerTool(
1877
1877
  "get_slide",
1878
1878
  {
1879
- description: "Inspect a single slide \u2014 returns layout, notes, elements",
1879
+ description: "Inspect a single slide: returns layout, notes, elements",
1880
1880
  inputSchema: GetSlideSchema.shape
1881
1881
  },
1882
1882
  async (params) => {
@@ -370,7 +370,7 @@ function validateSlideIndex(slideIndex, slideCount) {
370
370
  // src/tools/content-tools.ts
371
371
  var MAX_REGEX_LEN = 200;
372
372
  function hasNestedUnboundedQuantifier(pattern) {
373
- return /\([^)]*[+*]\)[+*]/.test(pattern);
373
+ return /\([^)]*[+*]\)[+*]/u.test(pattern);
374
374
  }
375
375
  function buildSearchRegex(query, useRegex, caseSensitive) {
376
376
  if (useRegex) {
@@ -381,7 +381,7 @@ function buildSearchRegex(query, useRegex, caseSensitive) {
381
381
  return null;
382
382
  }
383
383
  }
384
- const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
384
+ const pattern = useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
385
385
  const flags = caseSensitive ? "g" : "gi";
386
386
  try {
387
387
  return new RegExp(pattern, flags);
@@ -1874,7 +1874,7 @@ function createServer() {
1874
1874
  server2.registerTool(
1875
1875
  "get_slide",
1876
1876
  {
1877
- description: "Inspect a single slide \u2014 returns layout, notes, elements",
1877
+ description: "Inspect a single slide: returns layout, notes, elements",
1878
1878
  inputSchema: GetSlideSchema.shape
1879
1879
  },
1880
1880
  async (params) => {
package/package.json CHANGED
@@ -1,12 +1,18 @@
1
1
  {
2
2
  "name": "pptx-viewer-mcp",
3
- "version": "1.1.23",
4
- "description": "PPTX manipulation tools, MCP server, and collaboration codec for pptx-viewer-core.",
3
+ "version": "1.1.26",
4
+ "description": "PowerPoint PPTX tools and MCP server: manipulate, convert, and collaborate on .pptx files from AI agents.",
5
5
  "keywords": [
6
6
  "ai",
7
+ "collaboration",
8
+ "converter",
7
9
  "mcp",
10
+ "office",
11
+ "openxml",
8
12
  "powerpoint",
13
+ "ppt",
9
14
  "pptx",
15
+ "presentation",
10
16
  "tools"
11
17
  ],
12
18
  "homepage": "https://github.com/ChristopherVR/pptx-viewer",
@@ -25,6 +31,7 @@
25
31
  },
26
32
  "files": [
27
33
  "dist/",
34
+ "CHANGELOG.md",
28
35
  "LICENSE",
29
36
  "README.md"
30
37
  ],
@@ -63,21 +70,19 @@
63
70
  },
64
71
  "dependencies": {
65
72
  "@modelcontextprotocol/sdk": "^1.29.0",
73
+ "fast-xml-parser": "^5.9.2",
74
+ "jszip": "^3.10.1",
75
+ "pptx-viewer-core": "^1.1.0",
66
76
  "zod": "^4.4.3"
67
77
  },
68
78
  "devDependencies": {
69
- "fast-xml-parser": "^5.9.0",
70
- "jszip": "^3.10.1",
71
- "pptx-viewer-core": "^1.1.7",
72
79
  "tsup": "^8.5.1",
73
80
  "typescript": "^6.0.3",
74
- "vitest": "^4.1.8",
81
+ "vitest": "^4.1.9",
75
82
  "yjs": "^13.6.31"
76
83
  },
77
84
  "peerDependencies": {
78
- "fast-xml-parser": "^5.5.0",
79
- "jszip": "^3.10.0",
80
- "pptx-viewer-core": "latest"
85
+ "yjs": "^13.6.31"
81
86
  },
82
87
  "peerDependenciesMeta": {
83
88
  "yjs": {