mce 0.24.4 → 0.25.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/README.md +302 -4
- package/dist/components/EditorPanels.vue.d.ts +6 -0
- package/dist/components/Interactions.vue.d.ts +3 -0
- package/dist/components/Selection.vue.d.ts +20 -20
- package/dist/components/shared/Cropper.vue.d.ts +5 -1
- package/dist/components/shared/Dialog.vue.d.ts +33 -38
- package/dist/components/shared/FloatPanel.vue.d.ts +4 -2
- package/dist/components/shared/LayoutItem.vue.d.ts +29 -0
- package/dist/components/shared/PanelContent.vue.d.ts +29 -0
- package/dist/components/shared/Transform.vue.d.ts +1 -1
- package/dist/components/timeline/EasingCurve.vue.d.ts +11 -0
- package/dist/components/timeline/KeyframePopover.vue.d.ts +3 -0
- package/dist/crdt/YDoc.d.ts +39 -2
- package/dist/crdt/YDoc.test.d.ts +1 -0
- package/dist/crdt/index.d.ts +3 -0
- package/dist/crdt/providers/indexeddb/IndexeddbProvider.d.ts +3 -1
- package/dist/crdt/reactivity.d.ts +27 -0
- package/dist/crdt/reactivity.test.d.ts +1 -0
- package/dist/crdt/vueReactivity.d.ts +8 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +10433 -9049
- package/dist/locale/en.d.ts +51 -9
- package/dist/locale/zh-Hans.d.ts +51 -9
- package/dist/mixins/0.context.d.ts +8 -0
- package/dist/mixins/extensions.d.ts +105 -0
- package/dist/mixins/panels.d.ts +46 -0
- package/dist/plugin.d.ts +14 -0
- package/dist/plugins/animate.d.ts +40 -0
- package/dist/plugins/components.d.ts +32 -0
- package/dist/plugins/flexLayout.d.ts +15 -0
- package/dist/plugins/history.d.ts +2 -0
- package/dist/plugins/interactions.d.ts +44 -0
- package/dist/plugins/shape.d.ts +0 -4
- package/dist/plugins/slice.d.ts +10 -0
- package/dist/plugins/smartGuides.d.ts +2 -2
- package/dist/plugins/timeline.d.ts +17 -0
- package/dist/plugins/toolbelt.d.ts +4 -0
- package/dist/plugins/variables.d.ts +27 -0
- package/dist/plugins/zoom.d.ts +2 -0
- package/dist/scene/Doc.d.ts +13 -1
- package/dist/typed-global.d.ts +6 -2
- package/dist/typed-plugins.d.ts +6 -3
- package/dist/utils/animationPresets.d.ts +27 -0
- package/dist/utils/components.d.ts +33 -0
- package/dist/utils/components.test.d.ts +1 -0
- package/dist/utils/create.d.ts +1 -15
- package/dist/utils/easing.d.ts +23 -0
- package/dist/utils/easing.test.d.ts +1 -0
- package/dist/utils/image.d.ts +6 -0
- package/dist/utils/index.d.ts +8 -1
- package/dist/utils/keyframes.d.ts +31 -0
- package/dist/utils/keyframes.test.d.ts +1 -0
- package/dist/utils/lottie.d.ts +27 -0
- package/dist/utils/lottie.test.d.ts +1 -0
- package/dist/utils/remapTextSelection.d.ts +23 -0
- package/dist/utils/remapTextSelection.test.d.ts +1 -0
- package/dist/utils/traverse.d.ts +9 -0
- package/dist/utils/variables.d.ts +58 -0
- package/dist/utils/variables.test.d.ts +1 -0
- package/package.json +4 -8
- package/dist/plugins/html.d.ts +0 -2
- package/dist/plugins/table.d.ts +0 -2
- package/dist/plugins/workflow.d.ts +0 -31
- package/dist/utils/table.d.ts +0 -93
- package/dist/utils/workflow.d.ts +0 -9
- /package/dist/components/{TableEditor.vue.d.ts → Components.vue.d.ts} +0 -0
- /package/dist/components/{Workflow.vue.d.ts → InteractionBadges.vue.d.ts} +0 -0
- /package/dist/utils/{smartGuides-geometry.d.ts → smartGuidesGeometry.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
|
-
<p align="center">
|
|
21
|
+
<p align="center">An infinite canvas editor framework (Vue 3 + TypeScript) with real-time collaboration, a timeline, components and design tokens — built on WebGL rendering. Bring your own UI. ESM only.</p>
|
|
22
22
|
|
|
23
23
|
<p align="center">
|
|
24
24
|
<a href="https://qq15725.github.io/mce/">📚 Documentation</a>
|
|
@@ -26,6 +26,76 @@
|
|
|
26
26
|
<a href="https://codesandbox.io/p/github/qq15725/mce/main">🎮 Try in CodeSandbox</a>
|
|
27
27
|
</p>
|
|
28
28
|
|
|
29
|
+
## ✨ Features
|
|
30
|
+
|
|
31
|
+
**Canvas & editing**
|
|
32
|
+
- Infinite canvas with pan / zoom, rulers, scrollbars, pixel grid and checkerboard
|
|
33
|
+
- Smart guides & snapping, alignment / distribution, z-order arrange, tidy-up
|
|
34
|
+
- Multi-select & marquee, transform (move / resize / rotate / flip), foreground crop
|
|
35
|
+
- Frames (artboards) with auto-nesting, and Flex auto-layout (drag-to-reorder)
|
|
36
|
+
|
|
37
|
+
**Content**
|
|
38
|
+
- Shapes, pen / freehand paths, lines & arrows
|
|
39
|
+
- Rich text (fragment styling, custom fonts, format painter, auto-fit strategies)
|
|
40
|
+
- Images (insert / upload / crop), video, tables (`@mce/table`) and charts (`@mce/chart`)
|
|
41
|
+
|
|
42
|
+
**Motion**
|
|
43
|
+
- Timeline with frame-based playback
|
|
44
|
+
- Keyframe animation with reusable easing (presets + custom cubic-bezier)
|
|
45
|
+
- Export to GIF, MP4 and Lottie
|
|
46
|
+
|
|
47
|
+
**Collaboration & history**
|
|
48
|
+
- CRDT document model ([Yjs](https://github.com/yjs/yjs)) in the core — undo / redo and offline persistence (IndexedDB) build on it
|
|
49
|
+
- Real-time multi-user editing + awareness (remote cursors / selection / avatars) via `@mce/collaboration` (WebSocket / pluggable transport)
|
|
50
|
+
- Comments anchored to elements (pins follow on move / scale / rotate, threads with replies & resolve) via `@mce/comments`
|
|
51
|
+
|
|
52
|
+
**Design systems**
|
|
53
|
+
- Components / symbols / instances with per-instance overrides and master propagation
|
|
54
|
+
- Design tokens / variables (collections + modes) for theming and responsive values
|
|
55
|
+
|
|
56
|
+
**AI** (`@mce/ai`)
|
|
57
|
+
- A typed AI canvas action schema — drive edits from an LLM over the existing command & undo stack (model wiring left to the consumer)
|
|
58
|
+
|
|
59
|
+
**Workflow** (`@mce/workflow`)
|
|
60
|
+
- A node-graph editing mode: connectable nodes with input / output ports and curved connections
|
|
61
|
+
|
|
62
|
+
**Extensible**
|
|
63
|
+
- ~40 built-in plugins; a plugin can contribute commands, tools, hotkeys, exporters, loaders, components and events
|
|
64
|
+
- Element types & modes are decoupled via extension points (selection redirect, resize override, enter handler, editing state, toolbelt item, icon, mode, statusbar item) — see `mixins/extensions.ts`
|
|
65
|
+
- Unified command system, hotkeys, and i18n
|
|
66
|
+
|
|
67
|
+
## 📤 Import & export
|
|
68
|
+
|
|
69
|
+
- **Export**: `PNG` · `JPEG` · `WebP` · `SVG` · `PDF` · `GIF` · `MP4` · `Lottie` · `PPTX` / `XLSX` / `DOCX` · `JSON`
|
|
70
|
+
- **Import**: `PPTX` / `XLSX` / `DOCX` · `PSD` · `SVG` · `HTML` · images · `JSON`
|
|
71
|
+
|
|
72
|
+
These ship as optional plugins; their heavy encoders / parsers are lazy-loaded on first use:
|
|
73
|
+
|
|
74
|
+
| Package | Adds |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `@mce/gif` | GIF export |
|
|
77
|
+
| `@mce/mp4` | MP4 export |
|
|
78
|
+
| `@mce/pdf` | PDF export |
|
|
79
|
+
| `@mce/svg` | SVG import & export |
|
|
80
|
+
| `@mce/openxml` | PPTX / XLSX / DOCX import & export |
|
|
81
|
+
| `@mce/psd` | PSD import (Photoshop layers → elements) |
|
|
82
|
+
| `@mce/html` | HTML import |
|
|
83
|
+
|
|
84
|
+
(`PNG` / `JPEG` / `WebP` / `JSON` / `Lottie` export are built in.)
|
|
85
|
+
|
|
86
|
+
## 🔌 Feature plugins
|
|
87
|
+
|
|
88
|
+
Specialized features also ship as optional packages, registered the same way (`plugins: [...]`):
|
|
89
|
+
|
|
90
|
+
| Package | Adds |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `@mce/table` | Table element + in-canvas table editor |
|
|
93
|
+
| `@mce/chart` | Chart elements (bar / line / pie / …) |
|
|
94
|
+
| `@mce/ai` | Typed AI canvas action schema (`applyAiActions`) |
|
|
95
|
+
| `@mce/workflow` | Node-graph editing mode |
|
|
96
|
+
| `@mce/collaboration` | Real-time collaboration: transport providers + presence (cursors / selection / avatars) |
|
|
97
|
+
| `@mce/comments` | Comments: comment tool + pins anchored to elements + threads (stored on `element.comments`) |
|
|
98
|
+
|
|
29
99
|
## 📦 Install
|
|
30
100
|
|
|
31
101
|
```shell
|
|
@@ -38,22 +108,38 @@ npm i mce
|
|
|
38
108
|
<script setup lang="ts">
|
|
39
109
|
import { Editor, EditorLayout, EditorLayoutItem } from 'mce'
|
|
40
110
|
import 'mce/styles'
|
|
111
|
+
import ai from '@mce/ai'
|
|
112
|
+
import chart from '@mce/chart'
|
|
113
|
+
import collaboration from '@mce/collaboration'
|
|
114
|
+
import comments from '@mce/comments'
|
|
41
115
|
import gif from '@mce/gif'
|
|
42
116
|
import mp4 from '@mce/mp4'
|
|
43
117
|
import openxml from '@mce/openxml'
|
|
44
118
|
import pdf from '@mce/pdf'
|
|
45
119
|
import svg from '@mce/svg'
|
|
46
|
-
import
|
|
120
|
+
import table from '@mce/table'
|
|
121
|
+
import workflow from '@mce/workflow'
|
|
47
122
|
|
|
48
123
|
const editor = new Editor({
|
|
49
124
|
plugins: [
|
|
125
|
+
// export / import formats
|
|
50
126
|
gif(),
|
|
51
127
|
mp4(),
|
|
52
128
|
svg(),
|
|
53
129
|
pdf(),
|
|
54
130
|
openxml(),
|
|
131
|
+
// feature plugins (all optional)
|
|
132
|
+
table(),
|
|
133
|
+
chart(),
|
|
134
|
+
ai(),
|
|
135
|
+
workflow(),
|
|
136
|
+
collaboration(), // registers the collaboration + presence plugins
|
|
137
|
+
comments(),
|
|
55
138
|
],
|
|
56
|
-
|
|
139
|
+
// @mce/gif bundles its encoding worker by default. To self-host it
|
|
140
|
+
// (e.g. under a strict CSP), pass `gifWorkerUrl` explicitly:
|
|
141
|
+
// import gifWorkerUrl from 'modern-gif/worker?url'
|
|
142
|
+
// ...new Editor({ gifWorkerUrl })
|
|
57
143
|
locale: { locale: 'en' },
|
|
58
144
|
viewport: {
|
|
59
145
|
camera: { enabled: true },
|
|
@@ -132,7 +218,7 @@ npm i mce
|
|
|
132
218
|
</template>
|
|
133
219
|
```
|
|
134
220
|
|
|
135
|
-
|
|
221
|
+
Slot sub component — read editor state via `useEditor()`:
|
|
136
222
|
|
|
137
223
|
```vue
|
|
138
224
|
<script setup lang="ts">
|
|
@@ -147,3 +233,215 @@ slot sub component
|
|
|
147
233
|
</template>
|
|
148
234
|
```
|
|
149
235
|
|
|
236
|
+
## 🧩 Commands
|
|
237
|
+
|
|
238
|
+
Everything the editor does is a command — call `editor.exec(name, ...args)`. A few examples:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
// Arrange & layout
|
|
242
|
+
editor.exec('alignHorizontalCenter')
|
|
243
|
+
editor.exec('distributeHorizontalSpacing')
|
|
244
|
+
editor.exec('tidyUp')
|
|
245
|
+
|
|
246
|
+
// Design tokens / variables
|
|
247
|
+
const collection = editor.exec('createVariableCollection', 'Theme', 'Light')
|
|
248
|
+
const dark = editor.exec('addVariableMode', collection, 'Dark')
|
|
249
|
+
const brand = editor.exec('addVariable', collection, { name: 'brand', type: 'color', value: '#ff0000' })
|
|
250
|
+
editor.exec('setVariableValue', brand, dark, '#0000ff')
|
|
251
|
+
editor.exec('bindVariable', 'fill.color', brand) // bind selected element's fill
|
|
252
|
+
editor.exec('setActiveVariableMode', collection, dark) // theme switch → canvas recolors
|
|
253
|
+
|
|
254
|
+
// Components / instances
|
|
255
|
+
const component = editor.exec('createComponent') // from selection
|
|
256
|
+
editor.exec('createInstance', component, { position: { x: 200, y: 200 } })
|
|
257
|
+
|
|
258
|
+
// Keyframe animation → Lottie
|
|
259
|
+
editor.exec('addAnimationKeyframe', 0, { left: 0, opacity: 0 })
|
|
260
|
+
editor.exec('addAnimationKeyframe', 1, { left: 300, opacity: 1 })
|
|
261
|
+
const lottie = editor.exec('exportLottie')
|
|
262
|
+
|
|
263
|
+
// AI canvas actions (validated, applied in one undo step) — needs @mce/ai
|
|
264
|
+
editor.exec('applyAiActions', [
|
|
265
|
+
{ type: 'createText', text: 'Hello', x: 40, y: 40 },
|
|
266
|
+
{ type: 'align', direction: 'left' },
|
|
267
|
+
])
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## 🤖 AI
|
|
271
|
+
|
|
272
|
+
`@mce/ai` ships a **typed action layer**, not a model. It gives you a schema to
|
|
273
|
+
put in your prompt and a safe `applyAiActions` that validates / sanitizes a batch
|
|
274
|
+
of actions and applies them in a single undo step — wiring the LLM call is up to you.
|
|
275
|
+
|
|
276
|
+
**1. Register the plugin**
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
import ai from '@mce/ai'
|
|
280
|
+
new Editor({ plugins: [ai()] })
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**2. Build the prompt — `getAiPrompt` assembles schema + node ids + request for you**
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
const prompt = editor.exec('getAiPrompt', userInput)
|
|
287
|
+
// Already includes the action schema and every existing node id (so the model can
|
|
288
|
+
// reference current elements). Need the raw schema instead? editor.exec('getAiActionSchema').
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**3. Call your own model, then apply the returned actions**
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
// ← your LLM / SDK; @mce/ai is model-agnostic
|
|
295
|
+
const text = await callYourLLM(prompt)
|
|
296
|
+
const actions = JSON.parse(text) // e.g. [{ type: 'createText', text: 'Hi', x: 40, y: 40 }]
|
|
297
|
+
|
|
298
|
+
const { created, errors } = editor.exec('applyAiActions', actions)
|
|
299
|
+
// created: ids of newly created elements
|
|
300
|
+
// errors: rejected actions + reasons (invalid fields / unknown node ids) — skipped, not applied
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
- **Model-agnostic** — any LLM / SDK works as long as it emits schema-conforming JSON.
|
|
304
|
+
- **Safe** — invalid actions (bad fields, unknown ids) are rejected into `errors`, never written to the document.
|
|
305
|
+
- **One undo step** — the whole batch is a single undo entry.
|
|
306
|
+
- **Node ids included** — `getAiPrompt` embeds all current node ids, so actions referencing existing elements (`setStyle` / `move` / `delete` / `select` / `duplicate` / `align(ids)`) validate; building the prompt yourself means adding them manually.
|
|
307
|
+
|
|
308
|
+
## 🤝 Collaboration
|
|
309
|
+
|
|
310
|
+
The CRDT document model (Yjs) lives in the **core** — undo / redo and offline
|
|
311
|
+
persistence (IndexedDB) build on it. The **network transport and presence**
|
|
312
|
+
(awareness) layer is the optional `@mce/collaboration` package.
|
|
313
|
+
|
|
314
|
+
**1. Register the plugin**
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import collaboration from '@mce/collaboration'
|
|
318
|
+
|
|
319
|
+
const editor = new Editor({
|
|
320
|
+
plugins: [
|
|
321
|
+
collaboration(), // registers the collaboration + presence plugins
|
|
322
|
+
],
|
|
323
|
+
})
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**2. Identify the local user (presence)**
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
editor.presence.setUser({
|
|
330
|
+
id: 'u-1', // optional, for dedupe / avatars
|
|
331
|
+
name: 'Alice',
|
|
332
|
+
color: '#E64980',
|
|
333
|
+
avatar: 'https://…', // optional
|
|
334
|
+
})
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**3. Connect to a room**
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
// Built-in WebSocket transport (y-websocket compatible server)
|
|
341
|
+
editor.collaboration.connect({
|
|
342
|
+
url: 'wss://your-server',
|
|
343
|
+
room: 'doc-1', // defaults to the current document id
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// …or a custom / pluggable transport (WebRTC, BroadcastChannel, …)
|
|
347
|
+
import { AbstractProvider } from '@mce/collaboration'
|
|
348
|
+
editor.collaboration.connect({
|
|
349
|
+
provider: doc => new MyProvider(doc), // doc is the document's YDoc
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
editor.collaboration.disconnect() // end the session
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
You can also auto-connect on startup via the editor option:
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
new Editor({ collaboration: { url: 'wss://your-server', room: 'doc-1' } })
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**4. Reactive status & remote peers**
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
editor.collaboration.connected // Ref<boolean> — transport connected
|
|
365
|
+
editor.collaboration.synced // Ref<boolean> — first full sync done
|
|
366
|
+
editor.collaboration.active // Ref<boolean> — a session is active
|
|
367
|
+
editor.presence.peers // Ref<Peer[]> — remote users (cursor / selection / user)
|
|
368
|
+
editor.presence.localUser // Ref<PresenceUser>
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Remote cursors, selection boxes and a connection/avatars status-bar item render
|
|
372
|
+
automatically once a session is active. Document switching rebuilds the provider
|
|
373
|
+
on the new document's `YDoc`; the transport is bound per-document.
|
|
374
|
+
|
|
375
|
+
> Comments (`@mce/comments`) live on `element.comments` and are part of the
|
|
376
|
+
> document model, so they sync over the same session automatically.
|
|
377
|
+
|
|
378
|
+
## 📚 Packages
|
|
379
|
+
|
|
380
|
+
Every package ships as ESM and registers the same way (`new Editor({ plugins: [pkg()] })`).
|
|
381
|
+
The default export of each `@mce/*` package is its plugin function; commands it adds are
|
|
382
|
+
called via `editor.exec(name, …)` rather than imported.
|
|
383
|
+
|
|
384
|
+
| Package | Description | Key exports |
|
|
385
|
+
| --- | --- | --- |
|
|
386
|
+
| `mce` | Headless infinite-canvas editor core (WebGL; export to image / video / PPT). | `Editor`, `EditorLayout`, `EditorLayoutItem`, `EditorLayers`, `createShapeElement` / `createTextElement` / … factories, `useEditor` |
|
|
387
|
+
| `@mce/ai` | LLM-driven, typed canvas actions (`createText` / `createShape` / `setStyle` / `move` / `select` / `delete` / `duplicate` / `align`) applied in one undo step with automatic validation. | `plugin` (default); `validateAiActions`, `AI_ACTION_SCHEMA` (commands: `applyAiActions`, `getAiActionSchema`, `getAiPrompt`) |
|
|
388
|
+
| `@mce/bigesj` | Bigesj design-doc integration: font preloading, clipboard paste detection, and PPTX / XLSX / DOCX loading. | `plugin(options)` (default); `useFonts`, `bigeLoader`, `bidTidLoader`, `clipboardLoader` |
|
|
389
|
+
| `@mce/chart` | Bar / line / pie chart elements with a built-in data editor and toolbelt entry. | `plugin` (default); `createChartElement(type, options)` |
|
|
390
|
+
| `@mce/collaboration` | Real-time multi-user editing (Yjs CRDT) over a pluggable provider (built-in WebSocket, y-websocket compatible; swap for WebRTC / BroadcastChannel) plus presence (remote cursors / selection / avatars). | `plugin` (default, registers collaboration + presence); `collaborationPlugin`, `presencePlugin`, `AbstractProvider`, `WebsocketProvider` |
|
|
391
|
+
| `@mce/comments` | Anchored comments: pins anchored to elements that follow on move / scale / rotate, with thread replies / resolve / reopen / delete. | `plugin` (default); `useComments`, `createCommentsStore` |
|
|
392
|
+
| `@mce/gaoding` | Gaoding design-doc clipboard-paste support. | `plugin` (default); `clipboardLoader` |
|
|
393
|
+
| `@mce/gif` | GIF export (frame-by-frame render from timeline keyframes; `modern-gif` lazy-loaded). | `plugin` (default) |
|
|
394
|
+
| `@mce/html` | HTML file / MIME import — DOM converted into canvas elements. | `plugin` (default) |
|
|
395
|
+
| `@mce/mp4` | MP4 export (adaptive bitrate, 720p–2160p, 30fps; `modern-mp4` lazy-loaded). | `plugin` (default) |
|
|
396
|
+
| `@mce/openxml` | PPTX / XLSX / DOCX two-way import & export with smart layer & font mapping (`modern-openxml`). | `plugin` (default) |
|
|
397
|
+
| `@mce/pdf` | PDF export with page metadata (size / margins; `modern-pdf` lazy-loaded). | `plugin` (default) |
|
|
398
|
+
| `@mce/psd` | PSD import — Photoshop layers expanded into elements, layer canvases auto-uploaded as image assets. | `plugin` (default); `psdToFrame` |
|
|
399
|
+
| `@mce/svg` | SVG import & export (Path2D path sets + viewBox, multi-MIME copy). | `plugin` (default) |
|
|
400
|
+
| `@mce/table` | Table element + in-canvas editor (add / remove rows & columns, merge / split cells, style editing, zoom-aware grid, toolbelt entry). | `plugin` (default); `createTableElement(rows, cols, options)` |
|
|
401
|
+
| `@mce/workflow` | Node-graph editing mode (connectable nodes, templated node types, preset text / image / video generation nodes). | `plugin` (default); `getWorkflowPorts`, `toConnectionPoints`, `INPUT_PORT`, `OUTPUT_PORT` (commands: `addWorkflowNode`, `addWorkflowConnection`) |
|
|
402
|
+
|
|
403
|
+
## 🏗️ Architecture
|
|
404
|
+
|
|
405
|
+
```
|
|
406
|
+
packages/
|
|
407
|
+
mce/ # core editor library (npm: mce)
|
|
408
|
+
gif/ # GIF export (@mce/gif)
|
|
409
|
+
mp4/ # MP4 export (@mce/mp4)
|
|
410
|
+
pdf/ # PDF export (@mce/pdf)
|
|
411
|
+
svg/ # SVG import & export (@mce/svg)
|
|
412
|
+
openxml/ # PPTX/XLSX/DOCX import & export (@mce/openxml)
|
|
413
|
+
psd/ # PSD import (@mce/psd)
|
|
414
|
+
html/ # HTML import (@mce/html)
|
|
415
|
+
table/ # table element + editor (@mce/table)
|
|
416
|
+
chart/ # chart elements (@mce/chart)
|
|
417
|
+
ai/ # AI canvas actions (@mce/ai)
|
|
418
|
+
workflow/ # node-graph mode (@mce/workflow)
|
|
419
|
+
collaboration/ # real-time collaboration (@mce/collaboration)
|
|
420
|
+
comments/ # comments (@mce/comments)
|
|
421
|
+
bigesj/ # Bigesj design-doc integration (@mce/bigesj)
|
|
422
|
+
gaoding/ # Gaoding clipboard paste (@mce/gaoding)
|
|
423
|
+
playground/ # demo & test app
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
The `Editor` is composed from layered mixins and a plugin system. Rendering is powered by
|
|
427
|
+
[`modern-canvas`](https://www.npmjs.com/package/modern-canvas) (WebGL), with text / fonts /
|
|
428
|
+
document model from `modern-text`, `modern-font` and `modern-idoc`.
|
|
429
|
+
|
|
430
|
+
The core stays lean: element types and editing modes are decoupled through extension points
|
|
431
|
+
(`mixins/extensions.ts`), so feature packages register their behavior instead of the core
|
|
432
|
+
hard-coding it. The CRDT document model (`yjs` + `y-protocols`) lives in the core; the
|
|
433
|
+
real-time transport and presence layer is the optional `@mce/collaboration` package.
|
|
434
|
+
|
|
435
|
+
## 🛠️ Development
|
|
436
|
+
|
|
437
|
+
```shell
|
|
438
|
+
pnpm dev # start the playground
|
|
439
|
+
pnpm build # build core + all plugins
|
|
440
|
+
pnpm test # run tests
|
|
441
|
+
pnpm -F mce typecheck
|
|
442
|
+
pnpm lint
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## 📄 License
|
|
446
|
+
|
|
447
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
slots: Record<string, any>;
|
|
3
|
+
};
|
|
4
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
5
|
+
declare const _default: typeof __VLS_export;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -30,7 +30,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
30
30
|
readonly lockAspectRatioStrategy?: "all" | "diagonal" | undefined;
|
|
31
31
|
readonly handleStyle?: "8-points" | "4-points" | undefined;
|
|
32
32
|
readonly handleShape?: "rect" | "circle" | undefined;
|
|
33
|
-
readonly handles?: ("move" | "resize-b" | "resize-
|
|
33
|
+
readonly handles?: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[] | undefined;
|
|
34
34
|
readonly scale?: [number, number] | undefined;
|
|
35
35
|
readonly offset?: [number, number] | undefined;
|
|
36
36
|
readonly initialSize?: boolean | undefined;
|
|
@@ -69,7 +69,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
69
69
|
lockAspectRatioStrategy?: "all" | "diagonal";
|
|
70
70
|
handleStyle?: "8-points" | "4-points";
|
|
71
71
|
handleShape?: "rect" | "circle";
|
|
72
|
-
handles?: ("move" | "resize-b" | "resize-
|
|
72
|
+
handles?: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
73
73
|
scale?: [number, number];
|
|
74
74
|
offset?: [number, number];
|
|
75
75
|
initialSize?: boolean;
|
|
@@ -85,7 +85,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
85
85
|
"onUpdate:modelValue"?: ((value: Partial<Mce.TransformValue> | undefined) => any) | undefined;
|
|
86
86
|
}>, {
|
|
87
87
|
start: (event?: MouseEvent, index?: number) => boolean;
|
|
88
|
-
activeHandle: import("vue").Ref<("move" | "resize-b" | "resize-
|
|
88
|
+
activeHandle: import("vue").Ref<("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl") | undefined, ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl") | undefined>;
|
|
89
89
|
transforming: import("vue").Ref<boolean, boolean>;
|
|
90
90
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
91
91
|
move: (context: Mce.TransformContext) => any;
|
|
@@ -97,14 +97,14 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
97
97
|
scale: [number, number];
|
|
98
98
|
threshold: number;
|
|
99
99
|
ui: boolean;
|
|
100
|
+
resizeStrategy: "free" | "lockAspectRatio";
|
|
100
101
|
movable: boolean;
|
|
101
102
|
rotatable: boolean;
|
|
102
103
|
resizable: boolean;
|
|
103
|
-
resizeStrategy: "free" | "lockAspectRatio";
|
|
104
104
|
tag: string | any;
|
|
105
105
|
lockAspectRatioStrategy: "all" | "diagonal";
|
|
106
106
|
handleShape: "rect" | "circle";
|
|
107
|
-
handles: ("move" | "resize-b" | "resize-
|
|
107
|
+
handles: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
108
108
|
}, {}, string, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & {
|
|
109
109
|
beforeCreate?: (() => void) | (() => void)[];
|
|
110
110
|
created?: (() => void) | (() => void)[];
|
|
@@ -130,14 +130,14 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
130
130
|
scale: [number, number];
|
|
131
131
|
threshold: number;
|
|
132
132
|
ui: boolean;
|
|
133
|
+
resizeStrategy: "free" | "lockAspectRatio";
|
|
133
134
|
movable: boolean;
|
|
134
135
|
rotatable: boolean;
|
|
135
136
|
resizable: boolean;
|
|
136
|
-
resizeStrategy: "free" | "lockAspectRatio";
|
|
137
137
|
tag: string | any;
|
|
138
138
|
lockAspectRatioStrategy: "all" | "diagonal";
|
|
139
139
|
handleShape: "rect" | "circle";
|
|
140
|
-
handles: ("move" | "resize-b" | "resize-
|
|
140
|
+
handles: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
141
141
|
}> & Omit<Readonly<{
|
|
142
142
|
ui?: boolean;
|
|
143
143
|
tag?: string | any;
|
|
@@ -152,7 +152,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
152
152
|
lockAspectRatioStrategy?: "all" | "diagonal";
|
|
153
153
|
handleStyle?: "8-points" | "4-points";
|
|
154
154
|
handleShape?: "rect" | "circle";
|
|
155
|
-
handles?: ("move" | "resize-b" | "resize-
|
|
155
|
+
handles?: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
156
156
|
scale?: [number, number];
|
|
157
157
|
offset?: [number, number];
|
|
158
158
|
initialSize?: boolean;
|
|
@@ -166,9 +166,9 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
166
166
|
onStart?: ((context: Mce.TransformContext) => any) | undefined;
|
|
167
167
|
onEnd?: ((context: Mce.TransformContext) => any) | undefined;
|
|
168
168
|
"onUpdate:modelValue"?: ((value: Partial<Mce.TransformValue> | undefined) => any) | undefined;
|
|
169
|
-
}>, "transforming" | "start" | "activeHandle" | ("offset" | "scale" | "threshold" | "ui" | "
|
|
169
|
+
}>, "transforming" | "start" | "activeHandle" | ("offset" | "scale" | "threshold" | "ui" | "resizeStrategy" | "movable" | "rotatable" | "resizable" | "tag" | "lockAspectRatioStrategy" | "handleShape" | "handles")> & {
|
|
170
170
|
start: (event?: MouseEvent, index?: number) => boolean;
|
|
171
|
-
activeHandle: ("move" | "resize-b" | "resize-
|
|
171
|
+
activeHandle: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl") | undefined;
|
|
172
172
|
transforming: boolean;
|
|
173
173
|
} & {} & import("vue").ComponentCustomProperties & {} & {
|
|
174
174
|
$slots: {
|
|
@@ -215,7 +215,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
215
215
|
readonly lockAspectRatioStrategy?: "all" | "diagonal" | undefined;
|
|
216
216
|
readonly handleStyle?: "8-points" | "4-points" | undefined;
|
|
217
217
|
readonly handleShape?: "rect" | "circle" | undefined;
|
|
218
|
-
readonly handles?: ("move" | "resize-b" | "resize-
|
|
218
|
+
readonly handles?: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[] | undefined;
|
|
219
219
|
readonly scale?: [number, number] | undefined;
|
|
220
220
|
readonly offset?: [number, number] | undefined;
|
|
221
221
|
readonly initialSize?: boolean | undefined;
|
|
@@ -254,7 +254,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
254
254
|
lockAspectRatioStrategy?: "all" | "diagonal";
|
|
255
255
|
handleStyle?: "8-points" | "4-points";
|
|
256
256
|
handleShape?: "rect" | "circle";
|
|
257
|
-
handles?: ("move" | "resize-b" | "resize-
|
|
257
|
+
handles?: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
258
258
|
scale?: [number, number];
|
|
259
259
|
offset?: [number, number];
|
|
260
260
|
initialSize?: boolean;
|
|
@@ -270,7 +270,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
270
270
|
"onUpdate:modelValue"?: ((value: Partial<Mce.TransformValue> | undefined) => any) | undefined;
|
|
271
271
|
}>, {
|
|
272
272
|
start: (event?: MouseEvent, index?: number) => boolean;
|
|
273
|
-
activeHandle: import("vue").Ref<("move" | "resize-b" | "resize-
|
|
273
|
+
activeHandle: import("vue").Ref<("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl") | undefined, ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl") | undefined>;
|
|
274
274
|
transforming: import("vue").Ref<boolean, boolean>;
|
|
275
275
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
276
276
|
move: (context: Mce.TransformContext) => any;
|
|
@@ -282,14 +282,14 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
282
282
|
scale: [number, number];
|
|
283
283
|
threshold: number;
|
|
284
284
|
ui: boolean;
|
|
285
|
+
resizeStrategy: "free" | "lockAspectRatio";
|
|
285
286
|
movable: boolean;
|
|
286
287
|
rotatable: boolean;
|
|
287
288
|
resizable: boolean;
|
|
288
|
-
resizeStrategy: "free" | "lockAspectRatio";
|
|
289
289
|
tag: string | any;
|
|
290
290
|
lockAspectRatioStrategy: "all" | "diagonal";
|
|
291
291
|
handleShape: "rect" | "circle";
|
|
292
|
-
handles: ("move" | "resize-b" | "resize-
|
|
292
|
+
handles: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
293
293
|
}, {}, string, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & {
|
|
294
294
|
beforeCreate?: (() => void) | (() => void)[];
|
|
295
295
|
created?: (() => void) | (() => void)[];
|
|
@@ -315,14 +315,14 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
315
315
|
scale: [number, number];
|
|
316
316
|
threshold: number;
|
|
317
317
|
ui: boolean;
|
|
318
|
+
resizeStrategy: "free" | "lockAspectRatio";
|
|
318
319
|
movable: boolean;
|
|
319
320
|
rotatable: boolean;
|
|
320
321
|
resizable: boolean;
|
|
321
|
-
resizeStrategy: "free" | "lockAspectRatio";
|
|
322
322
|
tag: string | any;
|
|
323
323
|
lockAspectRatioStrategy: "all" | "diagonal";
|
|
324
324
|
handleShape: "rect" | "circle";
|
|
325
|
-
handles: ("move" | "resize-b" | "resize-
|
|
325
|
+
handles: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
326
326
|
}> & Omit<Readonly<{
|
|
327
327
|
ui?: boolean;
|
|
328
328
|
tag?: string | any;
|
|
@@ -337,7 +337,7 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
337
337
|
lockAspectRatioStrategy?: "all" | "diagonal";
|
|
338
338
|
handleStyle?: "8-points" | "4-points";
|
|
339
339
|
handleShape?: "rect" | "circle";
|
|
340
|
-
handles?: ("move" | "resize-b" | "resize-
|
|
340
|
+
handles?: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl")[];
|
|
341
341
|
scale?: [number, number];
|
|
342
342
|
offset?: [number, number];
|
|
343
343
|
initialSize?: boolean;
|
|
@@ -351,9 +351,9 @@ declare const __VLS_base: import("vue").DefineComponent<{}, {
|
|
|
351
351
|
onStart?: ((context: Mce.TransformContext) => any) | undefined;
|
|
352
352
|
onEnd?: ((context: Mce.TransformContext) => any) | undefined;
|
|
353
353
|
"onUpdate:modelValue"?: ((value: Partial<Mce.TransformValue> | undefined) => any) | undefined;
|
|
354
|
-
}>, "transforming" | "start" | "activeHandle" | ("offset" | "scale" | "threshold" | "ui" | "
|
|
354
|
+
}>, "transforming" | "start" | "activeHandle" | ("offset" | "scale" | "threshold" | "ui" | "resizeStrategy" | "movable" | "rotatable" | "resizable" | "tag" | "lockAspectRatioStrategy" | "handleShape" | "handles")> & {
|
|
355
355
|
start: (event?: MouseEvent, index?: number) => boolean;
|
|
356
|
-
activeHandle: ("move" | "resize-b" | "resize-
|
|
356
|
+
activeHandle: ("move" | "resize-b" | "resize-t" | "resize-r" | "resize-l" | "resize-br" | "resize-tr" | "resize-tl" | "resize-bl" | "rotate-br" | "rotate-tr" | "rotate-tl" | "rotate-bl" | "round-br" | "round-tr" | "round-tl" | "round-bl") | undefined;
|
|
357
357
|
transforming: boolean;
|
|
358
358
|
} & {} & import("vue").ComponentCustomProperties & {} & {
|
|
359
359
|
$slots: {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { ImageFillCropRect } from 'modern-idoc';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* 撤回 / 重做修复说明:
|
|
4
|
+
* 下方 `viewEffect` watch 在 `view` 变化时反向补偿 `cropRect` 以保持 source 视觉不变。
|
|
5
|
+
* 但撤回/重做会由 UndoManager 同时回退 `view` 与 `cropRect`,此时不应再据旧 `view` 重算
|
|
6
|
+
* `cropRect`(会把已回退的 `cropRect` 覆盖掉)。因此 viewEffect 在 `editor.isUndoRedoing`
|
|
7
|
+
* 期间跳过补偿,仅对用户拖拽 / 改宽高比产生的 view 变化补偿。
|
|
4
8
|
*/
|
|
5
9
|
type View = Record<'left' | 'top' | 'width' | 'height' | 'scaleX' | 'scaleY', number>;
|
|
6
10
|
type __VLS_Props = {
|