@office-kit/pptx-editor 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +385 -2
- package/dist/agent-tools-emRGeP2H.js +23323 -0
- package/dist/agent-tools-emRGeP2H.js.map +1 -0
- package/dist/animation-player-D2mt2MI8.js +1400 -0
- package/dist/animation-player-D2mt2MI8.js.map +1 -0
- package/dist/deck-merge-XZc2oNxd.js +1304 -0
- package/dist/deck-merge-XZc2oNxd.js.map +1 -0
- package/dist/deck-merge-r5VCQNH_.d.ts +38 -0
- package/dist/index-BFQN6sI6.d.ts +243 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/internal/animation-player.d.ts +104 -0
- package/dist/internal/animation-player.js +2 -0
- package/dist/internal.d.ts +33 -0
- package/dist/internal.js +18 -0
- package/dist/internal.js.map +1 -0
- package/dist/merge.d.ts +2 -0
- package/dist/merge.js +2 -0
- package/dist/src-iNPXxN6v.js +77762 -0
- package/dist/src-iNPXxN6v.js.map +1 -0
- package/package.json +66 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuichiro Yamashita
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,386 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @office-kit/pptx-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A browser editor for `.pptx` presentations that you embed in your own web
|
|
4
|
+
application. It opens a `.pptx`, lets people edit it with a ribbon, menus,
|
|
5
|
+
slide thumbnails and direct manipulation, and hands the edited `.pptx` back.
|
|
6
|
+
Everything it does goes through the public API of
|
|
7
|
+
[`@office-kit/pptx`](https://github.com/office-kit/pptx), so the saved file is
|
|
8
|
+
an ordinary, editable presentation.
|
|
9
|
+
|
|
10
|
+
> **Experimental (0.x).** The editor's behaviour is still growing and a minor
|
|
11
|
+
> release may change the API. Pin a version.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @office-kit/pptx-editor
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Embedding
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { mountEditor } from '@office-kit/pptx-editor';
|
|
21
|
+
|
|
22
|
+
const editor = mountEditor(document.getElementById('editor')!, {
|
|
23
|
+
source: new Uint8Array(await (await fetch('/decks/q3.pptx')).arrayBuffer()),
|
|
24
|
+
locale: 'en',
|
|
25
|
+
onSave: async (pptx) => {
|
|
26
|
+
await fetch('/decks/q3.pptx', { method: 'PUT', body: pptx });
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await editor.ready; // the deck is open
|
|
31
|
+
const pptx = await editor.snapshot(); // the deck as it is now
|
|
32
|
+
editor.destroy(); // remove the editor again
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The editor fills the element you mount it in, so give that element a size
|
|
36
|
+
(for example `height: 100vh`).
|
|
37
|
+
|
|
38
|
+
### `mountEditor(target, options?)`
|
|
39
|
+
|
|
40
|
+
| Option | Type | Default |
|
|
41
|
+
| ---------- | ------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
42
|
+
| `source` | `Uint8Array` | A new presentation with one title slide, as File ▸ New. |
|
|
43
|
+
| `fileName` | `string` | `Untitled.pptx`: shown in the title bar, used for downloads. |
|
|
44
|
+
| `locale` | `'en' \| 'ja'` | The language last picked in the editor, else the browser's. |
|
|
45
|
+
| `onSave` | `(pptx: Uint8Array) => Promise<boolean \| void> \| boolean \| void` | Without it, saving downloads the file. |
|
|
46
|
+
| `autoSave` | `boolean` | `false`. Shows the AutoSave switch; needs `onSave`. |
|
|
47
|
+
| `compact` | `boolean` | `false`. A slimmer title bar without the product name. |
|
|
48
|
+
| `status` | `HTMLElement` | None. Your own element in the title bar (a save status). |
|
|
49
|
+
| `isolate` | `boolean` | `true`. `false` renders into the page, without shadow root. |
|
|
50
|
+
|
|
51
|
+
`onSave` runs when the user saves: the Save button, File ▸ Save, or
|
|
52
|
+
Ctrl/Cmd+S; when you call `save()`; and, with `autoSave` while its switch is
|
|
53
|
+
on, shortly after each edit. It receives the complete `.pptx`. If it throws or
|
|
54
|
+
rejects, the editor tells the user the save failed. Resolve `false` when you
|
|
55
|
+
did not save and have told the user why yourself (you are offline, say): the
|
|
56
|
+
deck stays marked as changed, the editor shows nothing and does not retry, so
|
|
57
|
+
call `save()` once you can. A call never starts before the previous one has
|
|
58
|
+
settled.
|
|
59
|
+
|
|
60
|
+
`status` stays in your page's DOM, slotted into the editor's title bar, so your
|
|
61
|
+
own styles apply to it. `isolate: false` is only for a page that belongs to the
|
|
62
|
+
editor alone, such as its own `<iframe>`: the page's styles then reach the
|
|
63
|
+
editor and the editor's reach the page.
|
|
64
|
+
|
|
65
|
+
It returns an `EditorHandle`:
|
|
66
|
+
|
|
67
|
+
| Member | Description |
|
|
68
|
+
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
69
|
+
| `ready` | Resolves once `source` is open and the editor is shown; rejects if `source` is not a readable `.pptx`. |
|
|
70
|
+
| `dirty` | Whether the deck has changes `onSave` has not saved. |
|
|
71
|
+
| `locale` | The interface language in use. |
|
|
72
|
+
| `snapshot()` | The presentation as it is now, as `.pptx` bytes. It does not call `onSave` or mark the deck saved. |
|
|
73
|
+
| `selection()` | The shapes the user has selected, as `ShapeRef`s (see [Agents in the browser](#agents-in-the-browser)). |
|
|
74
|
+
| `apply(label, edit)` | Runs `edit(presentation)` with the `@office-kit/pptx` API as one undo step named "Agent: `label`". |
|
|
75
|
+
| `propose(base, edited, options)` | Merges a version made elsewhere into the deck (see [Long-running agents](#long-running-agents)). |
|
|
76
|
+
| `open(pptx, options?)` | Opens another deck in place of this one, as File ▸ Open does; options `{ fileName, unsaved }`. |
|
|
77
|
+
| `save()` | Saves as the Save button does: through `onSave`, or as a download without it. |
|
|
78
|
+
| `tools()` | Every editing capability as tool definitions for a language model (see [Tools for a model](#tools-for-a-model)). |
|
|
79
|
+
| `run(name, input)` | Runs one of those tools with the model's JSON input, as one undo step named "Agent: `name`". |
|
|
80
|
+
| `on(type, listener)` | Calls `listener` for each event below until the returned `off()` is called. |
|
|
81
|
+
| `destroy()` | Removes the editor and every listener it added, leaving `target` as it was, ready to mount again. |
|
|
82
|
+
|
|
83
|
+
| Event | Payload | Fires when |
|
|
84
|
+
| ------------------- | ------------------------------------------- | ------------------------------------------------- |
|
|
85
|
+
| `'change'` | `{ source: 'user' \| 'agent' \| 'source' }` | The presentation keeps an edit (see below). |
|
|
86
|
+
| `'selectionchange'` | `{ selection: ShapeRef[] }` | The user selects other shapes. |
|
|
87
|
+
| `'dirtychange'` | `{ dirty: boolean }` | The deck gets unsaved changes, or they are saved. |
|
|
88
|
+
| `'localechange'` | `{ locale: 'en' \| 'ja' }` | The user picks another language in the header. |
|
|
89
|
+
|
|
90
|
+
`'change'` fires after every edit the presentation keeps: the user's, an
|
|
91
|
+
`apply` or `propose`, Undo, Redo, File ▸ New / Open and `open()`. It does not
|
|
92
|
+
fire for the source you passed, nor while a shape is still being dragged.
|
|
93
|
+
|
|
94
|
+
### React
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { useEffect, useRef } from 'react';
|
|
98
|
+
import { mountEditor } from '@office-kit/pptx-editor';
|
|
99
|
+
|
|
100
|
+
export function DeckEditor({
|
|
101
|
+
source,
|
|
102
|
+
onSave,
|
|
103
|
+
}: {
|
|
104
|
+
source: Uint8Array;
|
|
105
|
+
onSave: (pptx: Uint8Array) => Promise<void>;
|
|
106
|
+
}) {
|
|
107
|
+
const target = useRef<HTMLDivElement>(null);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const editor = mountEditor(target.current!, { source, onSave });
|
|
110
|
+
return () => editor.destroy();
|
|
111
|
+
}, [source, onSave]);
|
|
112
|
+
return <div ref={target} style={{ height: '100vh' }} />;
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Vue
|
|
117
|
+
|
|
118
|
+
```vue
|
|
119
|
+
<script setup lang="ts">
|
|
120
|
+
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
|
121
|
+
import { mountEditor, type EditorHandle } from '@office-kit/pptx-editor';
|
|
122
|
+
|
|
123
|
+
const props = defineProps<{ source: Uint8Array; onSave: (pptx: Uint8Array) => Promise<void> }>();
|
|
124
|
+
const target = ref<HTMLElement>();
|
|
125
|
+
let editor: EditorHandle | undefined;
|
|
126
|
+
onMounted(() => {
|
|
127
|
+
editor = mountEditor(target.value!, { source: props.source, onSave: props.onSave });
|
|
128
|
+
});
|
|
129
|
+
onBeforeUnmount(() => editor?.destroy());
|
|
130
|
+
</script>
|
|
131
|
+
|
|
132
|
+
<template>
|
|
133
|
+
<div ref="target" style="height: 100vh" />
|
|
134
|
+
</template>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Agents in the browser
|
|
138
|
+
|
|
139
|
+
An agent running in your page — an LLM with tools, a script, a chat panel next
|
|
140
|
+
to the editor — works on the same document the user sees, through the handle:
|
|
141
|
+
|
|
142
|
+
- **Read**: `snapshot()` gives the `.pptx`; open it with `loadPresentation`
|
|
143
|
+
from `@office-kit/pptx` to read text, layout and anything else. To _look_ at
|
|
144
|
+
a slide, render it with `renderSlideToSvg` from `@office-kit/pptx-preview`,
|
|
145
|
+
the renderer the editor itself uses.
|
|
146
|
+
- **"This shape"**: `selection()` returns what the user selected as
|
|
147
|
+
`ShapeRef`s — `{ slideIndex, slide, shapeId, name }`. A ref names the slide
|
|
148
|
+
by its part name and the shape by its id, so it stays valid while slides
|
|
149
|
+
move and across Undo. `resolveShape(presentation, ref)` finds the shape again
|
|
150
|
+
and throws if it was deleted, so an agent never edits the wrong shape.
|
|
151
|
+
- **Write**: `apply(label, edit)` runs `edit` against the live presentation
|
|
152
|
+
with the `@office-kit/pptx` API. The user sees the change at once, and it is
|
|
153
|
+
one undo step named "Agent: `label`" (「エージェント: `label`」 in Japanese).
|
|
154
|
+
It marks the deck as changed exactly as a user edit does; `onSave` still runs
|
|
155
|
+
only when the user saves. If `edit` throws, nothing of it is kept and
|
|
156
|
+
`apply` rejects with the error. `edit` must be synchronous.
|
|
157
|
+
- **Follow along**: `on('change', …)` reports every kept edit with its
|
|
158
|
+
`source` (`'user'` or `'agent'`), including Undo and Redo;
|
|
159
|
+
`on('selectionchange', …)` reports the user's new selection.
|
|
160
|
+
|
|
161
|
+
`apply` is for code you write. When a model decides the edits, use
|
|
162
|
+
[`tools()` and `run`](#tools-for-a-model) instead: they check the model's
|
|
163
|
+
input for you.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { mountEditor, resolveShape } from '@office-kit/pptx-editor';
|
|
167
|
+
import { setShapeFill } from '@office-kit/pptx';
|
|
168
|
+
|
|
169
|
+
const editor = mountEditor(target, { source });
|
|
170
|
+
await editor.ready;
|
|
171
|
+
|
|
172
|
+
const [selected] = editor.selection();
|
|
173
|
+
if (selected)
|
|
174
|
+
await editor.apply('Highlight', (presentation) => {
|
|
175
|
+
setShapeFill(resolveShape(presentation, selected), '#FFF2CC');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
editor.on('change', ({ source }) => {
|
|
179
|
+
if (source === 'user') {
|
|
180
|
+
// The user edited (or undid an agent's edit): refresh what the agent knows.
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
There is no `renderSlide` on the handle: `snapshot()` plus
|
|
186
|
+
`@office-kit/pptx-preview` already does it, with the preview's own options.
|
|
187
|
+
|
|
188
|
+
### Tools for a model
|
|
189
|
+
|
|
190
|
+
`tools()` describes every mutating `@office-kit/pptx` export as a tool a
|
|
191
|
+
language model can call, and `run(name, input)` runs the call the model makes.
|
|
192
|
+
There is no per-function glue to write, and a new library function becomes a
|
|
193
|
+
tool with the next release.
|
|
194
|
+
|
|
195
|
+
- **Names** are the export names (`setShapeFill`, `addSlideShape`, …), sorted.
|
|
196
|
+
Four tools are the editor's own, for finding what the others take:
|
|
197
|
+
`listSlides`, `listShapes` (refs, kind, bounds and text of each shape),
|
|
198
|
+
`listLayouts` and `listComments`.
|
|
199
|
+
- **Descriptions** are the exports' TSDoc. **Input schemas** are JSON Schema
|
|
200
|
+
(draft 2020-12) generated from the functions' TypeScript signatures, one
|
|
201
|
+
property per parameter. The open presentation is never an input; a slide is
|
|
202
|
+
its part name (`"/ppt/slides/slide1.xml"`), a shape a `ShapeRef`, a table
|
|
203
|
+
cell a `ShapeRef` plus `row` and `col`, a layout its part name. Lengths are
|
|
204
|
+
integers in EMU (914400 per inch, 12700 per point). Bytes (pictures, media)
|
|
205
|
+
are base64. Each schema is self-contained (`$defs`, `$ref`).
|
|
206
|
+
- **`run`** checks `input` against the schema before anything happens and
|
|
207
|
+
never coerces: `"5"` is not a number. On a mismatch it rejects with every
|
|
208
|
+
problem by its JSON Pointer, for example
|
|
209
|
+
`Invalid input for setShapePosition:\n- /x: expected integer, got string "1in"`;
|
|
210
|
+
send that back to the model as the tool's error and it can correct the call.
|
|
211
|
+
A ref to a slide or shape that is gone also rejects, editing nothing. An
|
|
212
|
+
editing tool is one undo step, named "Agent: `setShapeFill`"; a read tool
|
|
213
|
+
edits nothing. It resolves with the result as JSON: `null` for most tools,
|
|
214
|
+
the ref of the slide or shape a tool added, the count a `clear…` tool reports.
|
|
215
|
+
- **Not tools**: `createPresentation`, `importSlide`, `mergePresentations` and
|
|
216
|
+
`sortSlides`, which take another presentation or a function.
|
|
217
|
+
|
|
218
|
+
Each element of `tools()` is `{ name, description, input_schema }`, which the
|
|
219
|
+
Anthropic Messages API takes as it is. For OpenAI's function calling, pass
|
|
220
|
+
`{ type: 'function', function: { name, description, parameters: input_schema } }`.
|
|
221
|
+
|
|
222
|
+
All the tools together are large (on the order of 150k tokens; the chart tools
|
|
223
|
+
alone are about 30k). Give the model the ones its job needs:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
227
|
+
import { mountEditor } from '@office-kit/pptx-editor';
|
|
228
|
+
|
|
229
|
+
const editor = mountEditor(target, { source });
|
|
230
|
+
await editor.ready;
|
|
231
|
+
|
|
232
|
+
const wanted = new Set([
|
|
233
|
+
'listSlides',
|
|
234
|
+
'listShapes',
|
|
235
|
+
'setShapeText',
|
|
236
|
+
'setShapeFill',
|
|
237
|
+
'addSlideShape',
|
|
238
|
+
]);
|
|
239
|
+
const tools = (await editor.tools()).filter((tool) => wanted.has(tool.name));
|
|
240
|
+
|
|
241
|
+
// Keep your API key on your server: point the SDK at a proxy that adds it.
|
|
242
|
+
const client = new Anthropic({
|
|
243
|
+
baseURL: '/api/anthropic',
|
|
244
|
+
apiKey: 'unused',
|
|
245
|
+
dangerouslyAllowBrowser: true,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
async function instruct(instruction: string): Promise<void> {
|
|
249
|
+
const messages: Anthropic.MessageParam[] = [
|
|
250
|
+
{
|
|
251
|
+
role: 'user',
|
|
252
|
+
content: `${instruction}\n\nSelected shapes: ${JSON.stringify(editor.selection())}`,
|
|
253
|
+
},
|
|
254
|
+
];
|
|
255
|
+
for (;;) {
|
|
256
|
+
const response = await client.messages.create({
|
|
257
|
+
model: 'claude-opus-5-5',
|
|
258
|
+
max_tokens: 16000,
|
|
259
|
+
tools,
|
|
260
|
+
messages,
|
|
261
|
+
});
|
|
262
|
+
messages.push({ role: 'assistant', content: response.content });
|
|
263
|
+
if (response.stop_reason !== 'tool_use') return;
|
|
264
|
+
const results: Anthropic.ToolResultBlockParam[] = [];
|
|
265
|
+
for (const block of response.content) {
|
|
266
|
+
if (block.type !== 'tool_use') continue;
|
|
267
|
+
try {
|
|
268
|
+
const result = await editor.run(block.name, block.input);
|
|
269
|
+
results.push({
|
|
270
|
+
type: 'tool_result',
|
|
271
|
+
tool_use_id: block.id,
|
|
272
|
+
content: JSON.stringify(result),
|
|
273
|
+
});
|
|
274
|
+
} catch (error) {
|
|
275
|
+
results.push({
|
|
276
|
+
type: 'tool_result',
|
|
277
|
+
tool_use_id: block.id,
|
|
278
|
+
content: error instanceof Error ? error.message : String(error),
|
|
279
|
+
is_error: true,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
messages.push({ role: 'user', content: results });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Long-running agents
|
|
289
|
+
|
|
290
|
+
`apply` edits the live deck at once. An agent that works for longer — on a
|
|
291
|
+
server, in a worker, over many tool calls — takes a copy instead, works on
|
|
292
|
+
that, and hands the result back with `propose`. The user keeps editing
|
|
293
|
+
meanwhile:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
const base = await editor.snapshot();
|
|
297
|
+
const edited = await agent.revise(base); // minutes later, perhaps
|
|
298
|
+
const result = await editor.propose(base, edited, { label: 'Tighten the wording' });
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The editor merges `base`, the deck as it is now (unsaved edits included) and
|
|
302
|
+
`edited`. Changes to different slides, shapes, media and links combine into one
|
|
303
|
+
undo step named "Agent: `label`", and `result` is `{ status: 'applied' }`.
|
|
304
|
+
When the same item changed on both sides, nothing is applied until the user
|
|
305
|
+
chooses in the title bar, which names each collision ("Slide 2: Title 1 was
|
|
306
|
+
changed both here and by the agent."):
|
|
307
|
+
|
|
308
|
+
- **Keep my edits** resolves `{ status: 'kept-mine', conflicts }`; the proposal
|
|
309
|
+
is dropped.
|
|
310
|
+
- **Use the agent's version** resolves `{ status: 'took-theirs', conflicts }`;
|
|
311
|
+
the deck becomes `edited` as one undo step, so Undo brings the user's
|
|
312
|
+
version back.
|
|
313
|
+
|
|
314
|
+
The user can download the agent's version before choosing. `conflicts` lists
|
|
315
|
+
`{ part, slide, shape, reason }` for each collision. Pass `base: null` when
|
|
316
|
+
there is no common version: any difference then asks the user. One proposal
|
|
317
|
+
runs at a time.
|
|
318
|
+
|
|
319
|
+
`from: 'source'` is for a newer version of the file itself (it changed on
|
|
320
|
+
disk, or was rebuilt): the undo step is named `label`, `'change'` reports
|
|
321
|
+
`'source'`, the prompt speaks of "the source", and because `edited` is what you
|
|
322
|
+
have saved, a deck that ends up equal to it has no unsaved changes.
|
|
323
|
+
[`@office-kit/pptx-dev`](../dev) applies its TSX rebuilds this way.
|
|
324
|
+
|
|
325
|
+
The merge itself is `mergeDecks(base, ours, theirs)` from
|
|
326
|
+
`@office-kit/pptx-editor/merge`. It has no DOM dependency, for hosts that merge
|
|
327
|
+
on a server, and returns `{ ok: true, bytes }` or `{ ok: false, conflicts }`.
|
|
328
|
+
|
|
329
|
+
## Behaviour on your page
|
|
330
|
+
|
|
331
|
+
- **Styles are isolated.** The editor renders inside a shadow root on an
|
|
332
|
+
element it adds to `target`. Your page's stylesheets do not reach it, and
|
|
333
|
+
inherited text styles (font, line height, letter spacing …) stop at its
|
|
334
|
+
boundary; its styles do not reach your page.
|
|
335
|
+
- **Keyboard shortcuts stay in the editor.** Keys typed into your page's own
|
|
336
|
+
inputs and buttons are left alone. Keys typed while nothing on the page has
|
|
337
|
+
focus go to the editor used last, as in a desktop application.
|
|
338
|
+
- **Several editors can share a page.** Each has its own presentation, undo
|
|
339
|
+
history and selection. The interface language is page-wide: every editor on
|
|
340
|
+
a page uses the same one.
|
|
341
|
+
- **Preferences are remembered in `localStorage`** (the language picked in the
|
|
342
|
+
editor, view options such as rulers and gridlines). A `locale` you pass is not
|
|
343
|
+
stored.
|
|
344
|
+
- **Copy and paste use the system clipboard.** Copying slides or objects
|
|
345
|
+
writes one clipboard item with HTML, a PNG picture, an SVG drawing (in
|
|
346
|
+
browsers that write SVG) and plain text, so other apps paste something
|
|
347
|
+
useful: slides and drawn objects as pictures at their size on the slide,
|
|
348
|
+
text boxes and tables on their own as text and tables. It also holds a
|
|
349
|
+
private copy of the slides that another editor
|
|
350
|
+
(another tab, page or browser) pastes as editable slides and objects. The
|
|
351
|
+
browser asks the user before the editor may read the clipboard; without that
|
|
352
|
+
permission, ⌘V / Ctrl+V still pastes text and pictures, and copies made in
|
|
353
|
+
the same editor still paste. To embed in a cross-origin frame, allow
|
|
354
|
+
`clipboard-read; clipboard-write` on it.
|
|
355
|
+
- **Moving slides into other presentation apps.** A web page cannot put
|
|
356
|
+
slides on the clipboard in a form other presentation apps import as slides:
|
|
357
|
+
pasting there gives a picture or text. To keep slides editable, right-click
|
|
358
|
+
the selected thumbnails, choose **Download Selected Slides...**, and import
|
|
359
|
+
the downloaded `.pptx` with the other app's reuse- or import-slides command.
|
|
360
|
+
- Slide Show and the Agents pane are only available in the
|
|
361
|
+
[`@office-kit/pptx-dev`](../dev) preview, which hosts the editor in a frame.
|
|
362
|
+
|
|
363
|
+
## Why the editor ships compiled
|
|
364
|
+
|
|
365
|
+
The editor is written in [Svelte](https://svelte.dev). The package contains
|
|
366
|
+
the compiled JavaScript with the small Svelte runtime bundled in, so your
|
|
367
|
+
application needs no Svelte, no build plugin and no particular framework, and a
|
|
368
|
+
Svelte version of your own never conflicts with the editor's. `@office-kit/pptx`
|
|
369
|
+
and `@office-kit/pptx-preview` stay ordinary dependencies.
|
|
370
|
+
|
|
371
|
+
## Contributing
|
|
372
|
+
|
|
373
|
+
How the editor is put together, and how its command registry follows the
|
|
374
|
+
library's API, is described in [`src/README.md`](src/README.md).
|
|
375
|
+
|
|
376
|
+
`@office-kit/pptx-editor/internal` and
|
|
377
|
+
`@office-kit/pptx-editor/internal/animation-player` are **not public API**.
|
|
378
|
+
They exist only for `@office-kit/pptx-dev` and change or disappear in any
|
|
379
|
+
release: the first is `mountEditor` for the dev tool's editor page, which in
|
|
380
|
+
the preview's frame presents slide shows and hosts the Agents pane over
|
|
381
|
+
`postMessage`; the second is the slide-show animation player that page loads on
|
|
382
|
+
its own.
|
|
383
|
+
|
|
384
|
+
## Trademarks
|
|
385
|
+
|
|
386
|
+
Not affiliated with or endorsed by Microsoft. PowerPoint is a trademark of the Microsoft group of companies.
|