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 +51 -0
- package/README.md +75 -70
- package/dist/codec/index.d.mts +10 -3
- package/dist/codec/index.d.ts +10 -3
- package/dist/codec/index.js +194 -22
- package/dist/codec/index.mjs +195 -23
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +6 -2
- package/dist/index.mjs +3 -2
- package/dist/mcp/index.js +3 -3
- package/dist/mcp/index.mjs +3 -3
- package/package.json +14 -9
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
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/pptx-viewer-mcp)
|
|
4
|
+
[](LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15
|
+
### 1. Run as an MCP server (recommended)
|
|
25
16
|
|
|
26
|
-
|
|
17
|
+
No clone, no build, no separate core install. Point your MCP client at the published binary via `npx`:
|
|
27
18
|
|
|
28
|
-
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"pptx": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["pptx-viewer-mcp"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
29
|
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
+
If you are embedding PPTX editing in your own app instead of running the MCP server, install the package once:
|
|
39
39
|
|
|
40
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
70
|
+
## Exports
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
92
|
+
These APIs back the MCP server. Reach for them when you embed PPTX editing directly rather than running the server.
|
|
84
93
|
|
|
85
|
-
###
|
|
94
|
+
### Call tools directly
|
|
86
95
|
|
|
87
|
-
|
|
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
|
-
|
|
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
|
|
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 `
|
|
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
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
|
|
|
130
|
+
v
|
|
131
|
+
tool function -> ToolResult { pptxData, result, dirty }
|
|
132
|
+
|
|
|
133
|
+
v (when dirty)
|
|
127
134
|
savePresentation()
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
151
|
+
Apache-2.0
|
package/dist/codec/index.d.mts
CHANGED
|
@@ -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)
|
|
11
|
-
dehydrate(ydoc: Doc, dirtyPaths?: string[])
|
|
12
|
-
observe(ydoc: Doc, onChange: () => 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";
|
package/dist/codec/index.d.ts
CHANGED
|
@@ -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)
|
|
11
|
-
dehydrate(ydoc: Doc, dirtyPaths?: string[])
|
|
12
|
-
observe(ydoc: Doc, onChange: () => 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";
|
package/dist/codec/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
meta.observeDeep(handler);
|
|
325
|
+
slidesArray.observeDeep(onChange);
|
|
326
|
+
meta.observeDeep(onChange);
|
|
160
327
|
return () => {
|
|
161
|
-
slidesArray.unobserveDeep(
|
|
162
|
-
meta.unobserveDeep(
|
|
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
|
|
334
|
+
const rec = slide;
|
|
168
335
|
for (const key of SCALAR_SLIDE_KEYS) {
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
209
|
-
elements.push(this._yMapToElement(elemMap));
|
|
373
|
+
elements.push(this._yMapToElement(elementsArray.get(i)));
|
|
210
374
|
}
|
|
211
375
|
}
|
|
212
|
-
slide
|
|
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 (
|
|
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 (
|
|
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 {
|
package/dist/codec/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
meta.observeDeep(handler);
|
|
323
|
+
slidesArray.observeDeep(onChange);
|
|
324
|
+
meta.observeDeep(onChange);
|
|
158
325
|
return () => {
|
|
159
|
-
slidesArray.unobserveDeep(
|
|
160
|
-
meta.unobserveDeep(
|
|
326
|
+
slidesArray.unobserveDeep(onChange);
|
|
327
|
+
meta.unobserveDeep(onChange);
|
|
161
328
|
};
|
|
162
329
|
}
|
|
163
330
|
_slideToYMap(slide) {
|
|
164
331
|
const slideMap = new Map();
|
|
165
|
-
const
|
|
332
|
+
const rec = slide;
|
|
166
333
|
for (const key of SCALAR_SLIDE_KEYS) {
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
-
|
|
207
|
-
elements.push(this._yMapToElement(elemMap));
|
|
371
|
+
elements.push(this._yMapToElement(elementsArray.get(i)));
|
|
208
372
|
}
|
|
209
373
|
}
|
|
210
|
-
slide
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
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 /\([^)]*[+*]\)[+*]
|
|
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(/[.*+?^${}()|[\]\\]/
|
|
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 /\([^)]*[+*]\)[+*]
|
|
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(/[.*+?^${}()|[\]\\]/
|
|
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 /\([^)]*[+*]\)[+*]
|
|
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(/[.*+?^${}()|[\]\\]/
|
|
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
|
|
1879
|
+
description: "Inspect a single slide: returns layout, notes, elements",
|
|
1880
1880
|
inputSchema: GetSlideSchema.shape
|
|
1881
1881
|
},
|
|
1882
1882
|
async (params) => {
|
package/dist/mcp/index.mjs
CHANGED
|
@@ -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 /\([^)]*[+*]\)[+*]
|
|
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(/[.*+?^${}()|[\]\\]/
|
|
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
|
|
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.
|
|
4
|
-
"description": "PPTX
|
|
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.
|
|
81
|
+
"vitest": "^4.1.9",
|
|
75
82
|
"yjs": "^13.6.31"
|
|
76
83
|
},
|
|
77
84
|
"peerDependencies": {
|
|
78
|
-
"
|
|
79
|
-
"jszip": "^3.10.0",
|
|
80
|
-
"pptx-viewer-core": "latest"
|
|
85
|
+
"yjs": "^13.6.31"
|
|
81
86
|
},
|
|
82
87
|
"peerDependenciesMeta": {
|
|
83
88
|
"yjs": {
|