@web-ppt/edit-core 0.5.0-beta.1
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 +274 -0
- package/LICENSE +21 -0
- package/README.md +474 -0
- package/dist/animation-catalog.d.ts +21 -0
- package/dist/body-properties.d.ts +8 -0
- package/dist/body-property-schema.d.ts +3 -0
- package/dist/change-classification.d.ts +10 -0
- package/dist/clipboard-binary.d.ts +4 -0
- package/dist/clipboard-links.d.ts +11 -0
- package/dist/clipboard-source.d.ts +23 -0
- package/dist/clipboard.d.ts +4 -0
- package/dist/commands/add-image.d.ts +4 -0
- package/dist/commands/add-shape.d.ts +4 -0
- package/dist/commands/add-slide-identity.d.ts +8 -0
- package/dist/commands/add-slide-template.d.ts +9 -0
- package/dist/commands/add-slide.d.ts +3 -0
- package/dist/commands/add-table.d.ts +4 -0
- package/dist/commands/align-elements.d.ts +4 -0
- package/dist/commands/apply-format.d.ts +4 -0
- package/dist/commands/clipboard-types.d.ts +91 -0
- package/dist/commands/direct-format-patches.d.ts +8 -0
- package/dist/commands/dispatch.d.ts +7 -0
- package/dist/commands/duplicate-slide.d.ts +3 -0
- package/dist/commands/edit-text.d.ts +3 -0
- package/dist/commands/element-effects.d.ts +5 -0
- package/dist/commands/element-fill.d.ts +5 -0
- package/dist/commands/element-image-content.d.ts +17 -0
- package/dist/commands/element-interaction.d.ts +11 -0
- package/dist/commands/element-link.d.ts +5 -0
- package/dist/commands/element-name.d.ts +8 -0
- package/dist/commands/element-order.d.ts +7 -0
- package/dist/commands/element-stroke.d.ts +5 -0
- package/dist/commands/element-text.d.ts +6 -0
- package/dist/commands/element-transform.d.ts +5 -0
- package/dist/commands/element-tree.d.ts +8 -0
- package/dist/commands/fit-text-shape.d.ts +3 -0
- package/dist/commands/format-painter-types.d.ts +17 -0
- package/dist/commands/image-format.d.ts +7 -0
- package/dist/commands/image-resource.d.ts +4 -0
- package/dist/commands/insert-row.d.ts +3 -0
- package/dist/commands/insertion-rect.d.ts +9 -0
- package/dist/commands/paste-elements.d.ts +4 -0
- package/dist/commands/paste-resources.d.ts +18 -0
- package/dist/commands/patch.d.ts +6 -0
- package/dist/commands/remove-slide.d.ts +3 -0
- package/dist/commands/replace-image.d.ts +3 -0
- package/dist/commands/replace-text.d.ts +5 -0
- package/dist/commands/set-animations.d.ts +3 -0
- package/dist/commands/set-body-props.d.ts +3 -0
- package/dist/commands/set-crop.d.ts +3 -0
- package/dist/commands/set-effects.d.ts +3 -0
- package/dist/commands/set-fill.d.ts +3 -0
- package/dist/commands/set-flip.d.ts +4 -0
- package/dist/commands/set-link.d.ts +3 -0
- package/dist/commands/set-para-props.d.ts +3 -0
- package/dist/commands/set-run-props.d.ts +3 -0
- package/dist/commands/set-slide-properties.d.ts +7 -0
- package/dist/commands/set-stroke.d.ts +3 -0
- package/dist/commands/set-xfrm.d.ts +3 -0
- package/dist/commands/set-z.d.ts +5 -0
- package/dist/commands/slide-image.d.ts +4 -0
- package/dist/commands/slide-layout.d.ts +8 -0
- package/dist/commands/slide-notes.d.ts +8 -0
- package/dist/commands/slide-order.d.ts +7 -0
- package/dist/commands/slide-property-types.d.ts +135 -0
- package/dist/commands/slide-property.d.ts +20 -0
- package/dist/commands/slide-tree.d.ts +6 -0
- package/dist/commands/spid.d.ts +4 -0
- package/dist/commands/table-row.d.ts +5 -0
- package/dist/commands/text-target.d.ts +25 -0
- package/dist/commands/types.d.ts +509 -0
- package/dist/commands/xfrm.d.ts +8 -0
- package/dist/data-validation.d.ts +7 -0
- package/dist/document.d.ts +17 -0
- package/dist/dynamic-slide-fields.d.ts +6 -0
- package/dist/edit-core.js +5321 -0
- package/dist/editor.d.ts +38 -0
- package/dist/element-ancestry.d.ts +3 -0
- package/dist/element-name.d.ts +4 -0
- package/dist/element-order.d.ts +7 -0
- package/dist/field-text.d.ts +3 -0
- package/dist/fractional-index.d.ts +10 -0
- package/dist/history.d.ts +50 -0
- package/dist/hyperlink.d.ts +10 -0
- package/dist/image-content.d.ts +7 -0
- package/dist/image-resource-history.d.ts +6 -0
- package/dist/index-D4ZTaYOa.js +706 -0
- package/dist/index.d.ts +46 -0
- package/dist/insertion-DabLme_U.js +5471 -0
- package/dist/insertion-invariants.d.ts +5 -0
- package/dist/layout-assets.d.ts +10 -0
- package/dist/layout-projection.d.ts +24 -0
- package/dist/layout-text-rebase.d.ts +3 -0
- package/dist/model-invariants.d.ts +5 -0
- package/dist/notes-part.d.ts +2 -0
- package/dist/opc/bytes.d.ts +1 -0
- package/dist/opc/crc32.d.ts +1 -0
- package/dist/opc/index.d.ts +3 -0
- package/dist/opc/package-owner.d.ts +4 -0
- package/dist/opc/patch.d.ts +6 -0
- package/dist/opc/types.d.ts +12 -0
- package/dist/opc/zip.d.ts +28 -0
- package/dist/opc-owner-protocol.d.ts +5 -0
- package/dist/opc.js +5 -0
- package/dist/paragraph-properties.d.ts +4 -0
- package/dist/paragraph-property-schema.d.ts +4 -0
- package/dist/patch-DO8497wO.js +322 -0
- package/dist/projection.d.ts +19 -0
- package/dist/recovery-identity.d.ts +35 -0
- package/dist/recovery-types.d.ts +30 -0
- package/dist/recovery.d.ts +28 -0
- package/dist/run-links.d.ts +3 -0
- package/dist/run-properties.d.ts +4 -0
- package/dist/run-property-schema.d.ts +3 -0
- package/dist/save/animation.d.ts +6 -0
- package/dist/save/background-image.d.ts +8 -0
- package/dist/save/clipboard-parts.d.ts +11 -0
- package/dist/save/drawing-color.d.ts +2 -0
- package/dist/save/duplicate-slide-parts.d.ts +15 -0
- package/dist/save/effects.d.ts +6 -0
- package/dist/save/hyperlink.d.ts +18 -0
- package/dist/save/image-content.d.ts +4 -0
- package/dist/save/index.d.ts +5 -0
- package/dist/save/insertion.d.ts +14 -0
- package/dist/save/layout-fallback-source.d.ts +4 -0
- package/dist/save/layout-fallback.d.ts +7 -0
- package/dist/save/materialize.d.ts +5 -0
- package/dist/save/name.d.ts +5 -0
- package/dist/save/notes.d.ts +12 -0
- package/dist/save/order.d.ts +5 -0
- package/dist/save/remove-element.d.ts +4 -0
- package/dist/save/remove-slide-parts.d.ts +11 -0
- package/dist/save/shape-format.d.ts +14 -0
- package/dist/save/slide-parts.d.ts +11 -0
- package/dist/save/slide-properties.d.ts +4 -0
- package/dist/save/table.d.ts +7 -0
- package/dist/save/text-body.d.ts +4 -0
- package/dist/save/text.d.ts +6 -0
- package/dist/save/transition.d.ts +4 -0
- package/dist/save/xfrm.d.ts +22 -0
- package/dist/save/xml-element.d.ts +3 -0
- package/dist/save.js +1559 -0
- package/dist/selection-pane.d.ts +18 -0
- package/dist/selection.d.ts +11 -0
- package/dist/session-assets.d.ts +20 -0
- package/dist/shape-effects.d.ts +6 -0
- package/dist/shape-fill.d.ts +12 -0
- package/dist/shape-stroke.d.ts +9 -0
- package/dist/slide-animation.d.ts +15 -0
- package/dist/slide-background-source.d.ts +13 -0
- package/dist/slide-notes.d.ts +2 -0
- package/dist/slide-properties.d.ts +4 -0
- package/dist/slide-transition.d.ts +19 -0
- package/dist/space.d.ts +47 -0
- package/dist/table-cell.d.ts +10 -0
- package/dist/table-direct-markup.d.ts +3 -0
- package/dist/table-insertion-policy.d.ts +5 -0
- package/dist/table-row-append-validation.d.ts +3 -0
- package/dist/table-rows.d.ts +12 -0
- package/dist/text-model.d.ts +28 -0
- package/dist/text-override-projection.d.ts +8 -0
- package/dist/text-override-validation.d.ts +7 -0
- package/dist/text-position.d.ts +8 -0
- package/dist/text-search-types.d.ts +46 -0
- package/dist/text-search.d.ts +5 -0
- package/dist/transaction-validation.d.ts +5 -0
- package/dist/types.d.ts +519 -0
- package/dist/xml/index.d.ts +9 -0
- package/dist/xml/mutate.d.ts +7 -0
- package/dist/xml/namespace.d.ts +5 -0
- package/dist/xml/nodes.d.ts +22 -0
- package/dist/xml/order.d.ts +15 -0
- package/dist/xml/qname.d.ts +15 -0
- package/dist/xml/query.d.ts +10 -0
- package/dist/xml/state.d.ts +30 -0
- package/dist/xml/tree.d.ts +8 -0
- package/dist/xml/types.d.ts +46 -0
- package/dist/xml.js +19 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# @web-ppt/edit-core
|
|
2
|
+
|
|
3
|
+
**English** · [简体中文](https://github.com/unStone/web-ppt/blob/master/packages/edit-core/README-zh-CN.md)
|
|
4
|
+
|
|
5
|
+
The headless, framework-agnostic editing model for [Web-PPT](https://github.com/unStone/web-ppt).
|
|
6
|
+
It keeps parsed source values separate from user overrides, assigns stable session identities, and projects
|
|
7
|
+
an editable document back to the existing high-fidelity `Slide` schema. It has no DOM dependency.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @web-ppt/core @web-ppt/edit-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { layoutText, parse, renderElementToSvg, renderSlideToSvg, renderTextBodyToHtml } from '@web-ppt/core';
|
|
15
|
+
import { createDoc, Editor, querySelectionPane } from '@web-ppt/edit-core';
|
|
16
|
+
|
|
17
|
+
const source = await parse(file, { edit: true, keepPackage: true, lazy: false });
|
|
18
|
+
const doc = createDoc(source);
|
|
19
|
+
const editor = new Editor(doc);
|
|
20
|
+
const slideId = doc.slideOrder[0];
|
|
21
|
+
const elementId = doc.slides[slideId].children[0];
|
|
22
|
+
const objects = querySelectionPane(doc, slideId); // topmost-first stable element tree
|
|
23
|
+
|
|
24
|
+
const change = editor.exec({ type: 'SetXfrm', id: elementId, x: 120 });
|
|
25
|
+
editor.exec({ type: 'SetFlip', id: elementId, h: true });
|
|
26
|
+
editor.exec({ type: 'SetName', id: elementId, name: 'Title' });
|
|
27
|
+
editor.exec({ type: 'SetLocked', id: elementId, locked: true }); // session-only
|
|
28
|
+
editor.exec({ type: 'SetElementHidden', id: elementId, hidden: true }); // session-only
|
|
29
|
+
editor.exec({ type: 'AlignElements', ids: [elementId], edge: 'center' });
|
|
30
|
+
editor.exec({
|
|
31
|
+
type: 'AddShape', slideId, preset: 'roundRect',
|
|
32
|
+
rect: { x: 360, y: 180, w: 280, h: 160 },
|
|
33
|
+
});
|
|
34
|
+
const newShapeId = editor.selection.kind === 'elements' ? editor.selection.ids[0] : null;
|
|
35
|
+
const imageBytes = new Uint8Array(await imageFile.arrayBuffer());
|
|
36
|
+
editor.exec({
|
|
37
|
+
type: 'AddImage', slideId, bytes: imageBytes, mime: 'image/png',
|
|
38
|
+
rect: { x: 420, y: 180, w: 320, h: 220 },
|
|
39
|
+
});
|
|
40
|
+
editor.exec({
|
|
41
|
+
type: 'AddTable', slideId, rows: 3, cols: 4,
|
|
42
|
+
rect: { x: 180, y: 140, w: 920, h: 360 },
|
|
43
|
+
});
|
|
44
|
+
const layoutId = doc.layoutOrder[0];
|
|
45
|
+
const added = editor.exec({ type: 'AddSlide', layoutId, at: { after: slideId } });
|
|
46
|
+
const newSlideId = [...added.createdSlides][0];
|
|
47
|
+
editor.exec({ type: 'MoveSlide', id: newSlideId, at: { after: null } });
|
|
48
|
+
const duplicated = editor.exec({ type: 'DuplicateSlide', id: newSlideId });
|
|
49
|
+
const duplicateSlideId = [...duplicated.createdSlides][0];
|
|
50
|
+
|
|
51
|
+
const slide = editor.toSlide(slideId);
|
|
52
|
+
const svg = renderSlideToSvg(source, slide, { idPrefix: `${slideId}-` });
|
|
53
|
+
const dirty = renderElementToSvg(editor.effectiveElement(elementId), {
|
|
54
|
+
idPrefix: `${slideId}-${elementId}-`,
|
|
55
|
+
});
|
|
56
|
+
// Replace this element's `dirty.markup` and `dirty.defs` partitions together.
|
|
57
|
+
// change.dirtyElements 与 change.dirtySlides 给出精确失效范围。
|
|
58
|
+
|
|
59
|
+
editor.subscribe(({ dirtyElements, dirtySlides, movedSlides, removedSlides, removedSlideFallbacks }) => {
|
|
60
|
+
updateView(dirtyElements, dirtySlides);
|
|
61
|
+
if (movedSlides.size || removedSlides.size) updatePageNavigator(doc.slideOrder);
|
|
62
|
+
for (const [removedId, fallbackId] of removedSlideFallbacks) replaceActivePage(removedId, fallbackId);
|
|
63
|
+
});
|
|
64
|
+
editor.undo();
|
|
65
|
+
editor.redo();
|
|
66
|
+
const pptxBytes = await editor.save(); // dynamically loads the OOXML/ZIP save path
|
|
67
|
+
|
|
68
|
+
const element = editor.effectiveElement(elementId);
|
|
69
|
+
if (element.kind === 'shape' && element.text) {
|
|
70
|
+
const textLayer = document.querySelector<HTMLElement>('[data-text-layer]')!;
|
|
71
|
+
textLayer.innerHTML = renderTextBodyToHtml(element.text, element.w, element.h);
|
|
72
|
+
const textEditor = textLayer.firstElementChild as HTMLElement;
|
|
73
|
+
textEditor.contentEditable = 'true';
|
|
74
|
+
textEditor.spellcheck = false;
|
|
75
|
+
|
|
76
|
+
// Safari engine mode: map pointer x to UTF-16 caret offsets without editing inside SVG.
|
|
77
|
+
const engineLayout = layoutText(element.text, element.w, element.h);
|
|
78
|
+
const caretStops = engineLayout.lines.flatMap((line) =>
|
|
79
|
+
line.segments.flatMap((segment) => segment.carets));
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Commands and patches are plain JSON. A transaction validates and commits atomically, creates one local undo
|
|
84
|
+
unit, and restores selection on undo/redo. Repeated edits with the same `mergeKey` merge for at most 500ms;
|
|
85
|
+
remote `origin` values apply without entering local history. `isDirty()` compares the current state with the
|
|
86
|
+
last `markSaved()` checkpoint. React, Vue, Web Components, or vanilla adapters only need `subscribe()` and
|
|
87
|
+
the two projection methods; none of their runtimes enter this package.
|
|
88
|
+
Collaborative clients must pass a stable, client-unique `origin`; appended-row identities include it so two
|
|
89
|
+
structured-cloned documents can merge concurrent appends without sharing a path.
|
|
90
|
+
|
|
91
|
+
Document text search is a DOM-free query over stable page and element identities. It walks pages, group children,
|
|
92
|
+
and table cells in drawing order, returns UTF-16 ranges, can match across adjacent runs, and never joins paragraphs,
|
|
93
|
+
dynamic fields, or formula atoms. Empty queries and non-JSON-shaped options fail closed.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { findText } from '@web-ppt/edit-core';
|
|
97
|
+
|
|
98
|
+
const matches = findText(editor.doc, {
|
|
99
|
+
query: 'quarterly', scope: { kind: 'document' }, matchCase: false, wholeWord: true,
|
|
100
|
+
});
|
|
101
|
+
const current = matches[0];
|
|
102
|
+
editor.exec({
|
|
103
|
+
type: 'ReplaceText', from: 'quarterly', to: 'annual', matchCase: false, wholeWord: true,
|
|
104
|
+
scope: {
|
|
105
|
+
kind: 'match',
|
|
106
|
+
match: { slideId: current.slideId, id: current.id, range: current.range },
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
editor.exec({
|
|
110
|
+
type: 'ReplaceText', from: 'draft', to: 'final',
|
|
111
|
+
scope: { kind: 'slides', slideIds: selectedSlideIds },
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
An all-match replacement evaluates one source snapshot, writes every affected shape or cell in one transaction,
|
|
116
|
+
and creates one undo unit. A stale exact match, readonly document, locked target, or any invalid target rejects the
|
|
117
|
+
whole command before model mutation. Recovery replay and minimal OOXML save therefore use the same ordinary patches.
|
|
118
|
+
|
|
119
|
+
For crash recovery, subscribe to the separate JSON journal. It reports the patches that actually took effect—
|
|
120
|
+
including undo, redo, non-history writes, selection changes, savepoints, image resources, and allocator
|
|
121
|
+
watermarks. With no subscriber, commits do not clone recovery data. Persist frames in sequence order and keep the
|
|
122
|
+
first frame's `identity.prefix` beside the source-file key; construct the fresh document with that same prefix:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { createDoc, Editor, type RecoveryFrame } from '@web-ppt/edit-core';
|
|
126
|
+
|
|
127
|
+
const frames: RecoveryFrame[] = [];
|
|
128
|
+
const stop = editor.subscribeRecovery((frame) => frames.push(frame));
|
|
129
|
+
|
|
130
|
+
// After reopening and parsing the same source file:
|
|
131
|
+
const recoveredDoc = createDoc(reopenedSource, { idPrefix: frames[0].identity.prefix });
|
|
132
|
+
const recovered = new Editor(recoveredDoc, {
|
|
133
|
+
recoveryFrames: JSON.parse(JSON.stringify(frames)),
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Replay validates the version, ordering, source prefix, every patch, selection, and the complete final model on a
|
|
138
|
+
copy before swapping it into the target document. A bad tail therefore cannot leave a half-restored document.
|
|
139
|
+
The recovered current state and dirty checkpoint are preserved; the old undo stack deliberately is not. Use
|
|
140
|
+
`restoreRecoveryFrames()` directly only on a freshly parsed document before exposing it to views. Session-only
|
|
141
|
+
image URLs are replaced by reserved patch tokens; replay requires a one-to-one asset sidecar and either rebinds
|
|
142
|
+
the source OPC part or restores embedded legacy `.ppt` bytes. A missing sidecar fails closed.
|
|
143
|
+
|
|
144
|
+
`RemoveElement` recursively removes an element tree while its inverse patch retains stable parent/z identity.
|
|
145
|
+
For a populated placeholder, the first command writes an empty-text override and keeps the shape; the next
|
|
146
|
+
command removes it. Save patches only the owning OOXML host or paragraph list and intentionally retains media
|
|
147
|
+
and relationships, which may be shared by other elements.
|
|
148
|
+
|
|
149
|
+
`SetZ { id, to: 'front' | 'back' | 'forward' | 'backward' }` changes layer order within one parent and source
|
|
150
|
+
part. Source `z` remains immutable; only moved elements carry a sparse `order`, so untouched objects pay no
|
|
151
|
+
duplicated ordering state. Subscriber events separate `reorderedElements`, whose existing DOM partitions can
|
|
152
|
+
move in place, from `renderElements`, which require new markup/defs.
|
|
153
|
+
|
|
154
|
+
`AlignElements { ids, edge: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom' }` aligns one
|
|
155
|
+
element to the slide or multiple elements to their visual AABB union. Rotated objects, nested elements in
|
|
156
|
+
flipped/non-uniformly scaled groups, and frame-only objects share the same world-to-parent transform. One command
|
|
157
|
+
is one undo unit; already aligned targets create no empty history. A React, Vue, Web Component, or vanilla toolbar
|
|
158
|
+
can map its six buttons directly to this JSON command without importing DOM internals.
|
|
159
|
+
|
|
160
|
+
`SetFill { id, fill }` edits vector fills on shapes: explicit no-fill, solid color, linear/radial gradients,
|
|
161
|
+
and renderer-supported DrawingML patterns. `SetStroke { id, stroke }` edits shape outlines and image borders,
|
|
162
|
+
including color, pixel width, preset dash, cap, join, compound line, and line ends. Colors are normalized to the
|
|
163
|
+
same `rgb()` / `rgba()` representation returned by core parsing; angles, stops, alpha, and line widths are also
|
|
164
|
+
quantized to round-trippable OOXML precision. Queries materialize solid/default cap/join/no-end values so UI code
|
|
165
|
+
does not mistake XML defaults for unknown state. Passing `null` removes the direct override and reveals the
|
|
166
|
+
source/theme value; `{ type: 'none' }`
|
|
167
|
+
is an explicit no-fill or no-stroke choice and remains direct formatting.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import {
|
|
171
|
+
queryElementFill, queryElementStroke, SHAPE_PATTERN_PRESETS,
|
|
172
|
+
} from '@web-ppt/edit-core';
|
|
173
|
+
|
|
174
|
+
const fill = queryElementFill(editor.doc, selectedIds);
|
|
175
|
+
// { value, mixed, direct } is ready for React/Vue/Svelte/Web Component controls.
|
|
176
|
+
editor.exec({
|
|
177
|
+
type: 'SetFill', id: elementId,
|
|
178
|
+
fill: {
|
|
179
|
+
type: 'gradient', angle: 45,
|
|
180
|
+
stops: [{ pos: 0, color: '#38BDF8' }, { pos: 1, color: '#6366F1' }],
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
editor.exec({
|
|
184
|
+
type: 'SetStroke', id: elementId,
|
|
185
|
+
stroke: { color: '#0F172A', width: 2, dash: [8, 6], cap: 'round', join: 'round' },
|
|
186
|
+
});
|
|
187
|
+
editor.exec({ type: 'SetFill', id: elementId, fill: null }); // restore inherited fill
|
|
188
|
+
const stroke = queryElementStroke(editor.doc, selectedIds);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Image-fill editing, effects, text color, and table-cell borders are separate capabilities rather than overloaded
|
|
192
|
+
variants of these two commands.
|
|
193
|
+
|
|
194
|
+
Slide property controls use stable page ids too. `SetBackground { id, fill }` accepts the same vector fills;
|
|
195
|
+
`SetHidden { id, v }` edits the slide-directory flag. In both commands `null` restores the parsed source.
|
|
196
|
+
`querySlideBackground` and `querySlideHidden` accept multiple `SlideId`s and return effective/source,
|
|
197
|
+
mixed/sourceMixed, and direct state. A background change requests one full-slide render; hidden metadata does
|
|
198
|
+
not rebuild visually unchanged SVG.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { querySlideBackground, querySlideHidden } from '@web-ppt/edit-core';
|
|
202
|
+
|
|
203
|
+
const background = querySlideBackground(editor.doc, selectedSlideIds);
|
|
204
|
+
editor.exec({ type: 'SetBackground', id: slideId, fill: { type: 'solid', color: '#0F172A' } });
|
|
205
|
+
editor.exec({
|
|
206
|
+
type: 'SetBackgroundImage', id: slideId, bytes: imageBytes, mime: 'image/png',
|
|
207
|
+
crop: { l: 0.1, t: 0.05, r: 0.1, b: 0.05 }, alpha: 0.85,
|
|
208
|
+
});
|
|
209
|
+
editor.exec({ type: 'SetBackgroundCrop', id: slideId, crop: null }); // clear crop, keep image
|
|
210
|
+
editor.exec({ type: 'SetHidden', id: slideId, v: true });
|
|
211
|
+
const hidden = querySlideHidden(editor.doc, selectedSlideIds);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Slide transitions use the same stable page ids. `SetTransition { id, t }` accepts one of the 40 playable
|
|
215
|
+
effects, an explicit `{ type: 'none' }`, or `null` to restore the parsed source. Inputs are normalized before
|
|
216
|
+
commit: the default duration is 750ms, directions are effect-specific, and morph can use object, word, or
|
|
217
|
+
character matching. Metadata changes do not rebuild the slide SVG.
|
|
218
|
+
Omitting `advanceAfterMs` preserves the page's current timing, so changing a visual effect does not silently alter
|
|
219
|
+
the existing slideshow policy.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import {
|
|
223
|
+
querySlideTransition, SLIDE_TRANSITION_TYPES, transitionDirections,
|
|
224
|
+
} from '@web-ppt/edit-core';
|
|
225
|
+
|
|
226
|
+
const transition = querySlideTransition(editor.doc, selectedSlideIds);
|
|
227
|
+
editor.exec({
|
|
228
|
+
type: 'SetTransition', id: slideId,
|
|
229
|
+
t: { type: 'push', dir: 'r', durationMs: 600, advanceAfterMs: 4000 },
|
|
230
|
+
});
|
|
231
|
+
editor.exec({ type: 'SetTransition', id: slideId, t: null }); // restore source
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Element timelines keep `ElementId` targets stable while projection and save map them back to OOXML spids.
|
|
235
|
+
`SetAnimations { slideId, steps }` accepts entrance, exit, emphasis, and motion-path steps; `[]` explicitly
|
|
236
|
+
removes the timeline and `null` restores the untouched source. The first step must use `click`; motion paths retain
|
|
237
|
+
their 2–256 polyline vertices while playback uses arc-length offsets for constant speed. Query state reports
|
|
238
|
+
`sourceReadonly` for MCE, build, extension, or unsupported source timing nodes and preserves them byte-for-byte
|
|
239
|
+
until an explicit replacement.
|
|
240
|
+
|
|
241
|
+
| Kind | Safely writable effects | Directions |
|
|
242
|
+
|---|---|---|
|
|
243
|
+
| Entrance / exit | `appear`, `fade`, `fly`, `wipe`, `zoom`, `dissolve` | `fly`/`wipe`: `l/r/u/d`; `zoom`: `in/out` |
|
|
244
|
+
| Emphasis | `spin`, `grow` | none |
|
|
245
|
+
| Motion | `motionPath` | none |
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
import {
|
|
249
|
+
ANIMATION_EFFECTS, animationDirections, animationEffectsForKind, querySlideAnimations,
|
|
250
|
+
} from '@web-ppt/edit-core';
|
|
251
|
+
|
|
252
|
+
const animation = querySlideAnimations(editor.doc, selectedSlideIds);
|
|
253
|
+
const entranceEffects = animationEffectsForKind('entrance');
|
|
254
|
+
editor.exec({
|
|
255
|
+
type: 'SetAnimations', slideId,
|
|
256
|
+
steps: [{ target: elementId, kind: 'entrance', effect: 'fly', dir: 'r',
|
|
257
|
+
trigger: 'click', delayMs: 0, durationMs: 500 }],
|
|
258
|
+
});
|
|
259
|
+
editor.exec({ type: 'SetAnimations', slideId, steps: null });
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Image backgrounds use content-addressed media resources. Uploading identical bytes across pages stores one media
|
|
263
|
+
part; source and inherited image backgrounds can be cropped without editing the shared layout or master.
|
|
264
|
+
|
|
265
|
+
Hyperlinks use stable domain targets instead of page indexes or OOXML actions. `SetLink` edits shape/image links;
|
|
266
|
+
`SetRunProps` uses the same `link` field for a text range. `{ kind: 'none' }` explicitly removes a link, while
|
|
267
|
+
`null` restores the parsed source. `queryElementLink` and `queryRunLink` return effective/source/direct/mixed and
|
|
268
|
+
followable state, so React, Vue, Svelte, Web Component, and vanilla toolbars never inspect `src` or `ovr`.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { queryElementLink, queryRunLink } from '@web-ppt/edit-core';
|
|
272
|
+
|
|
273
|
+
editor.exec({
|
|
274
|
+
type: 'SetLink', id: elementId,
|
|
275
|
+
target: { kind: 'slide', slideId: doc.slideOrder[2] },
|
|
276
|
+
});
|
|
277
|
+
const elementLink = queryElementLink(doc, [elementId]);
|
|
278
|
+
editor.exec({
|
|
279
|
+
type: 'SetRunProps', id: elementId, range,
|
|
280
|
+
props: { link: { kind: 'external', href: 'https://example.com/docs' } },
|
|
281
|
+
});
|
|
282
|
+
const runLink = queryRunLink(doc, elementId, range);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
External targets accept normalized `http`, `https`, and `mailto` URLs only. Internal targets retain `SlideId`
|
|
286
|
+
through page reorder, undo/redo, save/reopen, and same/cross-document element copy. Relative or unsupported source
|
|
287
|
+
actions remain queryable and preserved as read-only source values.
|
|
288
|
+
|
|
289
|
+
`SetRunProps` applies sparse character-format overrides to a half-open text range. It supports font family,
|
|
290
|
+
font size in slide pixels, bold, italic, underline, and strike-through across run and paragraph boundaries.
|
|
291
|
+
Use `null` to remove a direct override and reveal the inherited OOXML value; formulas remain indivisible,
|
|
292
|
+
format-preserving atoms while dynamic fields retain their field identity on save. A collapsed headless range is intentionally a no-op—
|
|
293
|
+
pending typing style belongs to the mounted input adapter, so the document never stores zero-width OOXML runs.
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
import { queryRunProps } from '@web-ppt/edit-core';
|
|
297
|
+
|
|
298
|
+
const range = {
|
|
299
|
+
from: { p: 0, r: 0, off: 2 },
|
|
300
|
+
to: { p: 1, r: 0, off: 4 },
|
|
301
|
+
};
|
|
302
|
+
editor.exec({
|
|
303
|
+
type: 'SetRunProps', id: elementId, range,
|
|
304
|
+
props: { font: 'Inter', size: 24, b: true },
|
|
305
|
+
});
|
|
306
|
+
const state = queryRunProps(editor.doc, elementId, range);
|
|
307
|
+
// state.b: { value: true, mixed: false }; each property reports mixed state independently.
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`SetParaProps` applies paragraph formatting to every paragraph touched by a range, including empty paragraphs.
|
|
311
|
+
A collapsed range formats its current paragraph immediately. The P0 property set is alignment, effective line-height
|
|
312
|
+
multiplier, spacing before/after in slide pixels, left margin in slide pixels, and signed first-line indent in slide
|
|
313
|
+
pixels. `null` removes only that direct `pPr` field and reveals its inherited level style; clearing a field that was
|
|
314
|
+
never direct is a strict no-op. `queryParaProps` reports an independent `{ value, mixed }` state for every property.
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import { queryParaProps } from '@web-ppt/edit-core';
|
|
318
|
+
|
|
319
|
+
editor.exec({
|
|
320
|
+
type: 'SetParaProps', id: elementId, range,
|
|
321
|
+
props: { align: 'center', lineHeight: 1.5, spaceAfter: 8, indent: -12 },
|
|
322
|
+
});
|
|
323
|
+
const paragraphState = queryParaProps(editor.doc, elementId, range);
|
|
324
|
+
// paragraphState.align: { value: 'center', mixed: false }
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
`EditText` also accepts `replaceFragment`, a JSON-only rich-text splice for clipboard adapters. A fragment contains
|
|
328
|
+
paragraph strings and contiguous half-open marks with only the six P0 character properties; DOM nodes, CSS, and
|
|
329
|
+
OOXML source identities cannot cross this boundary. Unspecified fields inherit the replaced range's starting
|
|
330
|
+
style, blocks create paragraphs, and embedded `\n` values remain hard line breaks. `textFragmentFromRange()`
|
|
331
|
+
creates the inverse transport shape for copy/cut without exposing preservation metadata.
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { textFragmentFromRange } from '@web-ppt/edit-core';
|
|
335
|
+
|
|
336
|
+
const fragment = textFragmentFromRange(editor.effectiveElement(elementId).text!, range);
|
|
337
|
+
editor.exec({
|
|
338
|
+
type: 'EditText', id: elementId,
|
|
339
|
+
ops: [{ type: 'replaceFragment', ...range, fragment }],
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
`copyElements(doc, ids)` returns a versioned, JSON-only `ElementClipboardPayload`. Paste it through
|
|
344
|
+
`Editor.exec({ type: 'PasteElements', payload, at: { parentId, x, y } })`; the command allocates fresh session
|
|
345
|
+
and OOXML identities, preserves nested groups in slide coordinates, and enters history as one atomic unit.
|
|
346
|
+
Images are embedded as base64 plus SHA-256 and deduplicated against the destination package. Hyperlinks receive
|
|
347
|
+
new relationships. Complex objects such as SmartArt can reuse a verified same-package OPC closure; a different
|
|
348
|
+
package is rejected before any model identity is allocated instead of receiving a degraded preview.
|
|
349
|
+
|
|
350
|
+
`InsertRow` intentionally exposes append-only table semantics for now, shared by last-cell Tab and an
|
|
351
|
+
“add row at end” control. It does not accept an `at` value that would be unsafe across vertical merges. The new row
|
|
352
|
+
keeps the former last row's height, direct/input formatting, and horizontal merge topology, clears its content,
|
|
353
|
+
and recomputes `bandRow`, `lastRow`, and frame height. History stores one sparse stable-row patch, not a table copy.
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
editor.exec({ type: 'InsertRow', id: tableElementId });
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
`AddShape { slideId, preset, rect }` inserts a top-level DrawingML preset shape into an existing writable
|
|
360
|
+
slide. The command validates the preset and rectangle, allocates collision-free model/OOXML identities, selects
|
|
361
|
+
the new shape, and enters history as one tree patch. It is immediately compatible with the existing transform
|
|
362
|
+
and double-click text-editing paths. Toolbars in React, Vue, Svelte, Web Components, or vanilla code call this
|
|
363
|
+
same JSON command; the headless package does not depend on their runtimes.
|
|
364
|
+
|
|
365
|
+
`AddImage { slideId, bytes, mime, rect, placeholderId? }` recognizes complete PNG, JPEG, GIF, and WebP
|
|
366
|
+
containers from magic bytes, copies caller-owned bytes, and inserts one immediately renderable `ImageElement`.
|
|
367
|
+
SHA-256 deduplicates media already in the source package or inserted elsewhere in the session; each picture still
|
|
368
|
+
gets its own collision-free relationship and `p:pic` identity. Supplying an empty picture `placeholderId` replaces
|
|
369
|
+
the placeholder atomically. One tree-patch group owns element, relationship, media, Content Types, selection,
|
|
370
|
+
undo/redo, and minimal save semantics; the model stores one hash token instead of duplicating Base64 in `src`.
|
|
371
|
+
SVG input is deliberately excluded until its external references and scripts have a separate sanitizer contract.
|
|
372
|
+
|
|
373
|
+
`AddTable { slideId, rows, cols, rect, placeholderId? }` inserts a 1–75 by 1–75 native DrawingML table. Integer
|
|
374
|
+
EMU distribution keeps column and row sums exact. The source deck's default table style and current theme are
|
|
375
|
+
resolved once for the immediate model and referenced by the saved `a:tbl`, so first-row/banding colors do not jump
|
|
376
|
+
after reopening. Empty cells are editable immediately and already carry the append-row templates used by final-cell
|
|
377
|
+
`Tab`. An empty content placeholder can be replaced atomically; save changes only the owning slide part.
|
|
378
|
+
|
|
379
|
+
`doc.layoutOrder` and `doc.layouts` expose the source deck's real layout catalog only in edit mode.
|
|
380
|
+
`AddSlide { layoutId, at: { after } }` creates one page from that layout without cloning another slide. The
|
|
381
|
+
returned `createdSlides` set is the stable hand-off to any React, Vue, Svelte, Web Component, or vanilla page
|
|
382
|
+
navigator; subscribers receive the same set. Empty title/body placeholders keep layout geometry and text style
|
|
383
|
+
but not prompt content, while date/footer/page-number placeholders remain OOXML fields. Undo/redo restores the
|
|
384
|
+
same model and OPC identities; save adds the required package references without rebuilding untouched parts.
|
|
385
|
+
|
|
386
|
+
`MoveSlide { id, at: { after } }` reorders an existing or newly created page by stable identities; `null` moves
|
|
387
|
+
it to the front. It emits `movedSlides` without pretending the page was removed and re-created, so React, Vue,
|
|
388
|
+
Svelte, Web Component, and vanilla navigators can read the final `doc.slideOrder` while mounted canvases keep
|
|
389
|
+
their current `SlideId`. Undo/redo, page-number fields, relative links, section membership, notes, and minimal
|
|
390
|
+
OOXML save all follow the same order.
|
|
391
|
+
|
|
392
|
+
`RemoveSlide { id }` deletes an existing or session-created page and rejects deleting the only remaining page.
|
|
393
|
+
Transactions and subscriptions expose `removedSlides` plus `removedSlideFallbacks`: each removed id maps to its
|
|
394
|
+
nearest surviving successor, or the predecessor at the tail. This lets any framework update its active route
|
|
395
|
+
without deriving transient indexes. Undo/redo restores the same page/element/OPC identities. Save removes the
|
|
396
|
+
slide indexes and parts plus an exclusively owned notes slide, while shared media and unknown relationship targets
|
|
397
|
+
remain untouched.
|
|
398
|
+
|
|
399
|
+
`DuplicateSlide { id }` snapshots the page's current effective tree and inserts an independent copy immediately
|
|
400
|
+
after it. The returned `createdSlides` identity is the only value framework adapters need; use a separate
|
|
401
|
+
`MoveSlide` command for another destination. Page/element ids, slide/notes parts, presentation ids, and notes
|
|
402
|
+
back-references are independent, while layout, media, charts, comments, and unknown targets keep sharing their
|
|
403
|
+
original package resources. Later edits or deletion of either page cannot mutate the other.
|
|
404
|
+
|
|
405
|
+
`querySlideNotes(doc, ids)` returns `value/source/mixed/sourceMixed/direct` for plain-text speaker notes, so
|
|
406
|
+
toolbars never inspect `src/ovr`. `SetNotes { id, text }` maps newlines to DrawingML paragraphs and treats an
|
|
407
|
+
empty string as an explicit clear. Transactions publish only `notesSlides` and do not dirty the canvas projection.
|
|
408
|
+
Old or session-created pages without notes materialize their OPC closure only on first edit; shared notes first fork
|
|
409
|
+
to an independent part while other placeholders, formatting, notesMaster, external links, and unknown extensions
|
|
410
|
+
remain byte-preserved.
|
|
411
|
+
|
|
412
|
+
The HTML result shares the preview renderer and carries `data-p` / `data-r`, bullet, empty-run, and autofit
|
|
413
|
+
markers for a contenteditable overlay. The core function stays DOM-free; the editor adapter owns focus and IME.
|
|
414
|
+
`layoutText` shares native SVG line breaking and returns paragraph/run identities plus UTF-16 caret stops.
|
|
415
|
+
Its `transform` maps logical coordinates for vertical text; pass `{ includeCarets: false }` for geometry-only work.
|
|
416
|
+
|
|
417
|
+
`Editor.save()` is the normal API: it writes current transforms, layer order, text, character and paragraph formatting,
|
|
418
|
+
appended table rows, new shapes/pages, page duplication/removals, speaker notes, placeholder clears, and element removals, refreshes `doc.package` for the
|
|
419
|
+
next save, and advances the dirty checkpoint only after a successful write. For save diagnostics, use the
|
|
420
|
+
detailed method without changing lifecycle semantics:
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
const result = await editor.saveDetailed();
|
|
424
|
+
// result.mode: identity | passthrough | repacked
|
|
425
|
+
// result.fallbackReason explains why a package had to be rebuilt.
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Load the lower-level preserving OOXML tree only when building another writer:
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
import {
|
|
432
|
+
findXmlChild, findXmlDescendant, parseXmlTree, serializeXmlTreeBytes, setXmlAttribute,
|
|
433
|
+
} from '@web-ppt/edit-core/xml';
|
|
434
|
+
|
|
435
|
+
const tree = parseXmlTree(doc.package!.parts['ppt/slides/slide1.xml']);
|
|
436
|
+
const xfrm = findXmlDescendant(tree.root, { localName: 'xfrm' })!;
|
|
437
|
+
const off = findXmlChild(xfrm, { localName: 'off' })!;
|
|
438
|
+
setXmlAttribute(off, 'x', String(Math.round(element.x * 9525)));
|
|
439
|
+
const changedPart = serializeXmlTreeBytes(tree);
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
The lower-level OPC patcher remains available for standalone package transforms:
|
|
443
|
+
|
|
444
|
+
```ts
|
|
445
|
+
import { disposeOpcPackage, patchOpcPackage } from '@web-ppt/edit-core/opc';
|
|
446
|
+
|
|
447
|
+
const packageHandle = doc.package!;
|
|
448
|
+
const saved = patchOpcPackage(packageHandle, {
|
|
449
|
+
'ppt/slides/slide1.xml': changedPart,
|
|
450
|
+
});
|
|
451
|
+
const pptxBytes = saved.bytes;
|
|
452
|
+
// saved.mode: identity | passthrough | repacked
|
|
453
|
+
// A non-null fallbackReason lets the UI explain why this save rebuilt the archive.
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Do not assign a low-level result to a live `EditDoc`: `Editor.save()` coordinates the package with its undo
|
|
457
|
+
baseline. To intentionally adopt a complete external snapshot, call `replaceDocPackage(doc, saved.package)`;
|
|
458
|
+
this explicitly resets that baseline. `disposeDoc(doc)` releases the current package. If you keep a save
|
|
459
|
+
result outside an `EditDoc`, call `disposeOpcPackage(saved.package)` when it is no longer needed.
|
|
460
|
+
|
|
461
|
+
Untouched declarations, comments, processing instructions, prefixes, attribute order, self-closing form,
|
|
462
|
+
and `AlternateContent` remain lexical matches. `insertXmlInOrder` enforces OOXML sequence ordering, while
|
|
463
|
+
`reorderXmlChildren` replaces only existing target slots. UTF-8 and UTF-16 byte order/BOM are retained.
|
|
464
|
+
Measured Vite output, including each entry's static shared chunks, is 62.33 KB gzip for the editing entry,
|
|
465
|
+
8.07 KB for `xml`, and 4.38 KB for `opc`; calling save after the main entry adds 8.30 KB on demand. Clean local
|
|
466
|
+
headers, extra fields, and compressed streams are copied byte-for-byte. ZIP64, descriptors, archive comments,
|
|
467
|
+
and encrypted entries return an explicit reason and deterministically repack. Every entry is DOM-free.
|
|
468
|
+
|
|
469
|
+
`doc.meta.readonly` is `true` when a `.pptx` was not parsed with the package and write-back metadata.
|
|
470
|
+
Legacy `.ppt` documents remain editable through the future generated-save path; binary `.ppt` write-back is intentionally unsupported.
|
|
471
|
+
For collaborative ordering, pass the new element's stable ULID as the third argument to
|
|
472
|
+
`fractionalIndexBetween(lower, upper, ulid)`; single-user callers can omit it.
|
|
473
|
+
|
|
474
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AnimEffect } from '@web-ppt/core';
|
|
2
|
+
export type EditableVisualAnimationKind = 'entrance' | 'exit' | 'emphasis';
|
|
3
|
+
interface AnimationEffectSpec {
|
|
4
|
+
readonly kinds: readonly EditableVisualAnimationKind[];
|
|
5
|
+
readonly directions: readonly string[];
|
|
6
|
+
readonly defaultDirection?: string;
|
|
7
|
+
/** PowerPoint MsoAnimEffect;只收录有规范编码和实际保存证据的效果。 */
|
|
8
|
+
readonly preset: number;
|
|
9
|
+
readonly filter?: string | Readonly<Record<string, string>>;
|
|
10
|
+
}
|
|
11
|
+
/** 编辑目录宁可小而可信;来源中的其他效果仍由 core 播放并原样直通。 */
|
|
12
|
+
export declare const ANIMATION_EFFECTS: readonly ["appear", "fade", "fly", "wipe", "zoom", "dissolve", "spin", "grow"];
|
|
13
|
+
export type EditableAnimationEffect = typeof ANIMATION_EFFECTS[number];
|
|
14
|
+
export type EntranceExitAnimationEffect = Exclude<EditableAnimationEffect, 'spin' | 'grow'>;
|
|
15
|
+
export type EmphasisAnimationEffect = Extract<EditableAnimationEffect, 'spin' | 'grow'>;
|
|
16
|
+
export declare function animationEffectSpec(effect: AnimEffect): AnimationEffectSpec;
|
|
17
|
+
export declare function animationEffectsForKind(kind: EditableVisualAnimationKind): readonly AnimEffect[];
|
|
18
|
+
export declare function animationDirections(effect: AnimEffect): readonly string[];
|
|
19
|
+
export declare function animationDefaultDirection(effect: AnimEffect): string | undefined;
|
|
20
|
+
export declare function animationFilter(effect: AnimEffect, dir: string | undefined): string | undefined;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TextBodyEditInfo } from '@web-ppt/core';
|
|
2
|
+
import type { EditDoc, TableCellAddress, TextBodyProperties, TextBodyPropertyOverrides, TextOverride } from './types';
|
|
3
|
+
export declare function queryBodyProps(doc: EditDoc, id: string, cell?: TableCellAddress): TextBodyProperties;
|
|
4
|
+
export declare function applyBodyProps(initial: Extract<TextOverride, {
|
|
5
|
+
kind: 'flat';
|
|
6
|
+
}>, props: TextBodyPropertyOverrides, sourceEditInfo?: TextBodyEditInfo): Extract<TextOverride, {
|
|
7
|
+
kind: 'flat';
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { TextBodyPropertyOverrides } from './types';
|
|
2
|
+
export declare const BODY_PROPERTY_FIELDS: readonly ["anchor", "insets", "wrap", "vert", "anchorCtr", "columns", "columnGap", "autoFit"];
|
|
3
|
+
export declare function assertTextBodyPropertyOverrides(value: TextBodyPropertyOverrides, label: string): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Patch } from './commands/types';
|
|
2
|
+
import type { ElementId, SlideId } from './types';
|
|
3
|
+
export declare function patchElements(patches: readonly Patch[]): Set<ElementId>;
|
|
4
|
+
export declare function affectsSlideSequence(patches: readonly Patch[]): boolean;
|
|
5
|
+
export declare function renderPatchSlides(patches: readonly Patch[]): Set<SlideId>;
|
|
6
|
+
export declare function renderPatchElements(patches: readonly Patch[], dirtyElements?: ReadonlySet<ElementId>): Set<ElementId>;
|
|
7
|
+
export declare function reorderedPatchElements(patches: readonly Patch[]): Set<ElementId>;
|
|
8
|
+
export declare function panePatchElements(patches: readonly Patch[]): Set<ElementId>;
|
|
9
|
+
export declare const hasDocumentPatch: (patches: readonly Patch[]) => boolean;
|
|
10
|
+
export declare function bodyPropsPatchElements(forward: readonly Patch[], inverse: readonly Patch[]): Set<ElementId>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SlideElement } from '@web-ppt/core';
|
|
2
|
+
import type { ClipboardPortableLink, ClipboardTextLink, ElementClipboardRecordMeta } from './commands/types';
|
|
3
|
+
import type { EditDoc, ElementOverrides } from './types';
|
|
4
|
+
export declare function copiedLinkMeta(doc: EditDoc, id: string, element: SlideElement): Pick<ElementClipboardRecordMeta, 'link' | 'textLinks'>;
|
|
5
|
+
export declare function assertClipboardPortableLink(value: unknown, label: string): asserts value is ClipboardPortableLink;
|
|
6
|
+
export declare function assertClipboardTextLinks(value: unknown, label: string): asserts value is ClipboardTextLink[];
|
|
7
|
+
export declare function applyCopiedLinks(doc: EditDoc, element: SlideElement, meta: ElementClipboardRecordMeta): {
|
|
8
|
+
element: SlideElement;
|
|
9
|
+
overrides: ElementOverrides;
|
|
10
|
+
sourceLinkReadonly?: true;
|
|
11
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { OpcPackage } from '@web-ppt/core';
|
|
2
|
+
import type { ClipboardRelationship, ClipboardResource } from './commands/types';
|
|
3
|
+
import type { ElementInsertionSource } from './types';
|
|
4
|
+
import type { XmlElement } from './xml/types';
|
|
5
|
+
export declare function relationshipPartFor(part: string): string;
|
|
6
|
+
export declare function relativeTarget(fromPart: string, targetPart: string): string;
|
|
7
|
+
export declare function resolveRelationshipTarget(fromPart: string, target: string): string;
|
|
8
|
+
export interface PackageTargetIdentity {
|
|
9
|
+
rootHash: string;
|
|
10
|
+
closureHash: string;
|
|
11
|
+
}
|
|
12
|
+
/** 用内容与关系图描述复杂对象;OPC 路径只参与读取,绝不进入剪贴板协议。 */
|
|
13
|
+
export declare function packageTargetIdentity(pkg: OpcPackage, targetPart: string): PackageTargetIdentity;
|
|
14
|
+
export declare function resolvePackageTarget(pkg: OpcPackage, identity: PackageTargetIdentity): string | null;
|
|
15
|
+
export declare function packageContentType(pkg: OpcPackage, part: string, extension: string): string;
|
|
16
|
+
/** 新 part 尚未有字节,仍必须尊重包中预先存在的精确 Override。 */
|
|
17
|
+
export declare function packageContentTypeOverride(pkg: OpcPackage, part: string): string | undefined;
|
|
18
|
+
export interface ClipboardClosure {
|
|
19
|
+
relationships: ClipboardRelationship[];
|
|
20
|
+
resources: ClipboardResource[];
|
|
21
|
+
}
|
|
22
|
+
/** 只复制宿主实际引用的关系;不把整张幻灯片的无关资源塞入系统剪贴板。 */
|
|
23
|
+
export declare function clipboardClosure(pkg: OpcPackage, sourcePart: string, host: XmlElement, insertions?: readonly Pick<ElementInsertionSource, 'relationships' | 'resources'>[]): ClipboardClosure;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ElementClipboardPayload } from './commands/types';
|
|
2
|
+
import type { EditDoc, ElementId } from './types';
|
|
3
|
+
/** 载荷身份只在自身 JSON 内有效;不能泄漏或复用会话级 EditDoc id。 */
|
|
4
|
+
export declare function copyElements(doc: EditDoc, input: readonly ElementId[]): ElementClipboardPayload;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EditDoc } from '../types';
|
|
2
|
+
import type { AddImageCommand, CommandPatches } from './types';
|
|
3
|
+
/** 图片与它的关系、媒体闭包共用一个结构 patch,撤销不会留下半张图片或孤儿 part。 */
|
|
4
|
+
export declare function addImagePatches(doc: EditDoc, command: AddImageCommand, origin: string): CommandPatches;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EditDoc } from '../types';
|
|
2
|
+
export declare function allocateSlideOpcIdentity(doc: EditDoc): {
|
|
3
|
+
part: string;
|
|
4
|
+
presentationSlideId: number;
|
|
5
|
+
presentationRelationshipId: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function allocateNotesPart(doc: EditDoc): string;
|
|
8
|
+
export declare function presentationSlideIdForPart(doc: EditDoc, part: string): number | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SlideLayoutTemplate } from '@web-ppt/core';
|
|
2
|
+
import type { EditDoc, ElementId, ElementRecord, SlideId } from '../types';
|
|
3
|
+
export declare function layoutTemplateRecords(doc: EditDoc, layout: SlideLayoutTemplate, slideId: SlideId, slidePart: string): {
|
|
4
|
+
children: ElementId[];
|
|
5
|
+
records: Record<ElementId, ElementRecord>;
|
|
6
|
+
dynamicSlideNumbers: ElementId[];
|
|
7
|
+
dynamicSlideLinks: ElementId[];
|
|
8
|
+
nextSpid: number;
|
|
9
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EditDoc } from '../types';
|
|
2
|
+
import type { AddTableCommand, CommandPatches } from './types';
|
|
3
|
+
/** 表格视觉默认值、即时模型和 OOXML 宿主共用一个来源,再交给既有结构历史与保存主干。 */
|
|
4
|
+
export declare function addTablePatches(doc: EditDoc, command: AddTableCommand, origin: string): CommandPatches;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EditDoc } from '../types';
|
|
2
|
+
import type { AlignElementsCommand, CommandPatches } from './types';
|
|
3
|
+
/** 一个命令直接产出全部位置 patch,避免宿主把六按钮能力错误拆成多段历史。 */
|
|
4
|
+
export declare function alignElementsPatches(doc: EditDoc, command: AlignElementsCommand, origin: string): CommandPatches;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EditDoc } from '../types';
|
|
2
|
+
import type { ApplyFormatCommand } from './format-painter-types';
|
|
3
|
+
import type { CommandPatches } from './types';
|
|
4
|
+
export declare function applyFormatPatches(doc: EditDoc, command: ApplyFormatCommand, origin: string): CommandPatches;
|