@forevka/wordcanvas 0.3.2 → 0.4.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 +167 -150
- package/dist-lib/builder.js +1 -1
- package/dist-lib/{editorApp-DYckquXo.js → editorApp-DZ_5LIaR.js} +5565 -4572
- package/dist-lib/{types-5-egHV_r.js → types-BaYYqH97.js} +17 -17
- package/dist-lib/wordcanvas.js +53 -3
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,150 +1,167 @@
|
|
|
1
|
-
# @forevka/wordcanvas
|
|
2
|
-
|
|
3
|
-
An embeddable, canvas-rendered, Word-compatible document editor. It paints pages
|
|
4
|
-
to a `<canvas>` (no `contenteditable`), paginates exactly like Word, imports and
|
|
5
|
-
exports real `.docx` and `.pdf`, and — when pointed at a backend — supports live
|
|
6
|
-
multi-user collaboration with presence.
|
|
7
|
-
|
|
8
|
-
The published bundle is **self-contained**: it has **zero runtime dependencies**
|
|
9
|
-
(the layout engine, font tooling, and DOCX/PDF pipelines are all inlined and
|
|
10
|
-
code-split). You can drop it onto a page with a plain `<script type="module">` or
|
|
11
|
-
import it from any bundler.
|
|
12
|
-
|
|
13
|
-
## How it compares
|
|
14
|
-
|
|
15
|
-
If you have shopped for an embeddable Word editor, you have met Syncfusion
|
|
16
|
-
Document Editor, OnlyOffice, and DevExpress Rich Text Editor. They are mature
|
|
17
|
-
and cover more of the Word long tail than this package does. They also ask for a
|
|
18
|
-
commercial per-seat license, and two of the three want a server running before
|
|
19
|
-
the editor renders a page.
|
|
20
|
-
|
|
21
|
-
WordCanvas takes the other trade. It ships under MIT, paints to a `<canvas>`
|
|
22
|
-
the way Google Docs has since 2021, and runs fully in the browser with zero
|
|
23
|
-
runtime dependencies. You only stand up a backend if you want live
|
|
24
|
-
collaboration; reading, editing, and DOCX/PDF export all work offline.
|
|
25
|
-
|
|
26
|
-
| | WordCanvas | Syncfusion Document Editor | OnlyOffice | DevExpress Rich Text |
|
|
27
|
-
|---|---|---|---|---|
|
|
28
|
-
| License | MIT | Commercial seat | AGPL or commercial | Commercial seat |
|
|
29
|
-
| Rendering | Canvas | DOM | Canvas (in an iframe) | DOM |
|
|
30
|
-
| Server required to render | No | For some file conversions | Yes (Document Server) | Yes (.NET backend) |
|
|
31
|
-
| Runtime dependencies | Zero | Several | Bundled suite | .NET stack |
|
|
32
|
-
| DOCX import + export | In-browser | Yes | Yes | Yes |
|
|
33
|
-
| PDF export | Page-accurate, in-browser | Yes | Yes | Yes |
|
|
34
|
-
| Live collaboration | Built in (opt-in backend) | Add-on | Built in | Add-on |
|
|
35
|
-
| Primary target | Any JS app | Angular/React/Vue | Iframe / full suite | Blazor / .NET |
|
|
36
|
-
|
|
37
|
-
Where the commercial editors win today: RTL and complex-script editing, bundled
|
|
38
|
-
CJK fonts, charts and equations, and an enterprise support contract. The full
|
|
39
|
-
breakdown, including what each one does better, lives in
|
|
40
|
-
[Best embeddable JS Word editors](https://forevka.dev/articles/best-embeddable-js-word-editors/).
|
|
41
|
-
|
|
42
|
-
## Install
|
|
43
|
-
|
|
44
|
-
```sh
|
|
45
|
-
npm install @forevka/wordcanvas
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Quick start (offline)
|
|
49
|
-
|
|
50
|
-
Omit `backendUrl` and the editor runs fully local — no network, no sync, no
|
|
51
|
-
share. Perfect for a standalone document editor.
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
import { WordCanvas } from "@forevka/wordcanvas";
|
|
55
|
-
|
|
56
|
-
const editor = new WordCanvas({
|
|
57
|
-
container: document.getElementById("editor")!,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
editor.on("ready", () => console.log("editor mounted"));
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
```html
|
|
64
|
-
<div id="editor" style="position:fixed; inset:0;"></div>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### No build step
|
|
68
|
-
|
|
69
|
-
The bundle is a standalone ES module, so an import map is all you need:
|
|
70
|
-
|
|
71
|
-
```html
|
|
72
|
-
<div id="editor" style="position:fixed; inset:0;"></div>
|
|
73
|
-
<script type="importmap">
|
|
74
|
-
{ "imports": { "@forevka/wordcanvas": "/node_modules/@forevka/wordcanvas/dist-lib/wordcanvas.js" } }
|
|
75
|
-
</script>
|
|
76
|
-
<script type="module">
|
|
77
|
-
import { WordCanvas } from "@forevka/wordcanvas";
|
|
78
|
-
new WordCanvas({ container: document.getElementById("editor") });
|
|
79
|
-
</script>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Online + collaboration
|
|
83
|
-
|
|
84
|
-
Pass `backendUrl` to turn on sync. Opening a document then auto-publishes it and
|
|
85
|
-
exposes a shareable link; edits sync live and presence events fire. The embedder
|
|
86
|
-
owns identity — pass a `user` so carets and edits are attributed.
|
|
87
|
-
|
|
88
|
-
```ts
|
|
89
|
-
import { WordCanvas } from "@forevka/wordcanvas";
|
|
90
|
-
|
|
91
|
-
const params = new URLSearchParams(location.search);
|
|
92
|
-
|
|
93
|
-
const editor = new WordCanvas({
|
|
94
|
-
container: document.getElementById("editor")!,
|
|
95
|
-
backendUrl: "https://api.example.com",
|
|
96
|
-
user: { id: "u-42", firstName: "Ada", lastName: "Lovelace" },
|
|
97
|
-
// Join an existing session if the share link carried one (the editor's own
|
|
98
|
-
// share links use ?collab=<docId>):
|
|
99
|
-
docId: params.get("collab") ?? undefined,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
editor.on("shared", ({ url }) => navigator.clipboard.writeText(url));
|
|
103
|
-
editor.on("presence", ({ participants }) => renderAvatars(participants));
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
A runnable version of this lives in [`examples/embed-live`](../examples/embed-live).
|
|
107
|
-
A minimal offline embed lives in [`examples/embed-offline`](../examples/embed-offline).
|
|
108
|
-
|
|
109
|
-
## API
|
|
110
|
-
|
|
111
|
-
```ts
|
|
112
|
-
new WordCanvas(options)
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
| Option | Type | Notes |
|
|
116
|
-
| ------------- | -------------------------------------- | --------------------------------------------------------------------- |
|
|
117
|
-
| `container` | `HTMLElement` | **Required.** Element to mount into. |
|
|
118
|
-
| `backendUrl` | `string` | Online iff provided. Omit for a fully offline editor. |
|
|
119
|
-
| `docId` | `string` | Open this document on load (online only). |
|
|
120
|
-
| `user` | `{ id, firstName, lastName }` | Identity for attribution + presence. |
|
|
121
|
-
| `onShareLink` | `(url, docId) => void` | Override how the share link is surfaced (default: built-in dialog). |
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
`
|
|
125
|
-
`
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
1
|
+
# @forevka/wordcanvas
|
|
2
|
+
|
|
3
|
+
An embeddable, canvas-rendered, Word-compatible document editor. It paints pages
|
|
4
|
+
to a `<canvas>` (no `contenteditable`), paginates exactly like Word, imports and
|
|
5
|
+
exports real `.docx` and `.pdf`, and — when pointed at a backend — supports live
|
|
6
|
+
multi-user collaboration with presence.
|
|
7
|
+
|
|
8
|
+
The published bundle is **self-contained**: it has **zero runtime dependencies**
|
|
9
|
+
(the layout engine, font tooling, and DOCX/PDF pipelines are all inlined and
|
|
10
|
+
code-split). You can drop it onto a page with a plain `<script type="module">` or
|
|
11
|
+
import it from any bundler.
|
|
12
|
+
|
|
13
|
+
## How it compares
|
|
14
|
+
|
|
15
|
+
If you have shopped for an embeddable Word editor, you have met Syncfusion
|
|
16
|
+
Document Editor, OnlyOffice, and DevExpress Rich Text Editor. They are mature
|
|
17
|
+
and cover more of the Word long tail than this package does. They also ask for a
|
|
18
|
+
commercial per-seat license, and two of the three want a server running before
|
|
19
|
+
the editor renders a page.
|
|
20
|
+
|
|
21
|
+
WordCanvas takes the other trade. It ships under MIT, paints to a `<canvas>`
|
|
22
|
+
the way Google Docs has since 2021, and runs fully in the browser with zero
|
|
23
|
+
runtime dependencies. You only stand up a backend if you want live
|
|
24
|
+
collaboration; reading, editing, and DOCX/PDF export all work offline.
|
|
25
|
+
|
|
26
|
+
| | WordCanvas | Syncfusion Document Editor | OnlyOffice | DevExpress Rich Text |
|
|
27
|
+
|---|---|---|---|---|
|
|
28
|
+
| License | MIT | Commercial seat | AGPL or commercial | Commercial seat |
|
|
29
|
+
| Rendering | Canvas | DOM | Canvas (in an iframe) | DOM |
|
|
30
|
+
| Server required to render | No | For some file conversions | Yes (Document Server) | Yes (.NET backend) |
|
|
31
|
+
| Runtime dependencies | Zero | Several | Bundled suite | .NET stack |
|
|
32
|
+
| DOCX import + export | In-browser | Yes | Yes | Yes |
|
|
33
|
+
| PDF export | Page-accurate, in-browser | Yes | Yes | Yes |
|
|
34
|
+
| Live collaboration | Built in (opt-in backend) | Add-on | Built in | Add-on |
|
|
35
|
+
| Primary target | Any JS app | Angular/React/Vue | Iframe / full suite | Blazor / .NET |
|
|
36
|
+
|
|
37
|
+
Where the commercial editors win today: RTL and complex-script editing, bundled
|
|
38
|
+
CJK fonts, charts and equations, and an enterprise support contract. The full
|
|
39
|
+
breakdown, including what each one does better, lives in
|
|
40
|
+
[Best embeddable JS Word editors](https://forevka.dev/articles/best-embeddable-js-word-editors/).
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
npm install @forevka/wordcanvas
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick start (offline)
|
|
49
|
+
|
|
50
|
+
Omit `backendUrl` and the editor runs fully local — no network, no sync, no
|
|
51
|
+
share. Perfect for a standalone document editor.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { WordCanvas } from "@forevka/wordcanvas";
|
|
55
|
+
|
|
56
|
+
const editor = new WordCanvas({
|
|
57
|
+
container: document.getElementById("editor")!,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
editor.on("ready", () => console.log("editor mounted"));
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<div id="editor" style="position:fixed; inset:0;"></div>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### No build step
|
|
68
|
+
|
|
69
|
+
The bundle is a standalone ES module, so an import map is all you need:
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<div id="editor" style="position:fixed; inset:0;"></div>
|
|
73
|
+
<script type="importmap">
|
|
74
|
+
{ "imports": { "@forevka/wordcanvas": "/node_modules/@forevka/wordcanvas/dist-lib/wordcanvas.js" } }
|
|
75
|
+
</script>
|
|
76
|
+
<script type="module">
|
|
77
|
+
import { WordCanvas } from "@forevka/wordcanvas";
|
|
78
|
+
new WordCanvas({ container: document.getElementById("editor") });
|
|
79
|
+
</script>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Online + collaboration
|
|
83
|
+
|
|
84
|
+
Pass `backendUrl` to turn on sync. Opening a document then auto-publishes it and
|
|
85
|
+
exposes a shareable link; edits sync live and presence events fire. The embedder
|
|
86
|
+
owns identity — pass a `user` so carets and edits are attributed.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { WordCanvas } from "@forevka/wordcanvas";
|
|
90
|
+
|
|
91
|
+
const params = new URLSearchParams(location.search);
|
|
92
|
+
|
|
93
|
+
const editor = new WordCanvas({
|
|
94
|
+
container: document.getElementById("editor")!,
|
|
95
|
+
backendUrl: "https://api.example.com",
|
|
96
|
+
user: { id: "u-42", firstName: "Ada", lastName: "Lovelace" },
|
|
97
|
+
// Join an existing session if the share link carried one (the editor's own
|
|
98
|
+
// share links use ?collab=<docId>):
|
|
99
|
+
docId: params.get("collab") ?? undefined,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
editor.on("shared", ({ url }) => navigator.clipboard.writeText(url));
|
|
103
|
+
editor.on("presence", ({ participants }) => renderAvatars(participants));
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
A runnable version of this lives in [`examples/embed-live`](../examples/embed-live).
|
|
107
|
+
A minimal offline embed lives in [`examples/embed-offline`](../examples/embed-offline).
|
|
108
|
+
|
|
109
|
+
## API
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
new WordCanvas(options)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
| Option | Type | Notes |
|
|
116
|
+
| ------------- | -------------------------------------- | --------------------------------------------------------------------- |
|
|
117
|
+
| `container` | `HTMLElement` | **Required.** Element to mount into. |
|
|
118
|
+
| `backendUrl` | `string` | Online iff provided. Omit for a fully offline editor. |
|
|
119
|
+
| `docId` | `string` | Open this document on load (online only). |
|
|
120
|
+
| `user` | `{ id, firstName, lastName }` | Identity for attribution + presence. |
|
|
121
|
+
| `onShareLink` | `(url, docId) => void` | Override how the share link is surfaced (default: built-in dialog). |
|
|
122
|
+
| `readonly` | `boolean` | Mount as a view-only viewer (see below). Default `false`. |
|
|
123
|
+
|
|
124
|
+
Methods: `whenReady(): Promise<EditorHandle>`, `openDocx(file)`, `share()`,
|
|
125
|
+
`getDocId()`, `getShareLink()`, `destroy()`, and `on(event, handler)` /
|
|
126
|
+
`off(event, handler)`.
|
|
127
|
+
|
|
128
|
+
Events: `ready`, `shared`, `userEntered`, `userLeave`, `presence`.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// Load a .docx the user picked (auto-publishes + shares when online):
|
|
132
|
+
input.addEventListener("change", async () => {
|
|
133
|
+
await editor.openDocx(input.files![0]);
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Read-only / viewer mode
|
|
138
|
+
|
|
139
|
+
Pass `readonly: true` to mount a view-only viewer:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const viewer = new WordCanvas({ container, readonly: true });
|
|
143
|
+
await viewer.openDocx(file); // or viewer.setDocument(doc)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The document still renders, scrolls, and stays selectable and copyable (Ctrl+C,
|
|
147
|
+
right-click → Copy), and `Ctrl+F` find works. The editing chrome is hidden (no
|
|
148
|
+
ribbon or ruler; the find bar drops Replace) and **every mutation is a no-op** —
|
|
149
|
+
typing, paste, undo/redo, drag-resize, and the programmatic editing paths all
|
|
150
|
+
short-circuit. In an online session a read-only client still joins the
|
|
151
|
+
collaboration and receives live remote edits; it just can't author them.
|
|
152
|
+
|
|
153
|
+
## Notes & limitations
|
|
154
|
+
|
|
155
|
+
- **Multiple instances per page are supported** — the chrome is class-scoped under
|
|
156
|
+
a per-instance root and each instance mounts independently, so you can run several
|
|
157
|
+
editors at once. `destroy()` removes an instance's chrome and its floating panels.
|
|
158
|
+
See the [`embed-multi`](../examples/embed-multi) example.
|
|
159
|
+
- The editor injects its own stylesheet and mounts its full chrome (ribbon, ruler,
|
|
160
|
+
outline, status bar) inside the container; give it a sized element.
|
|
161
|
+
- The bundle is large on disk (~20 MB unminified, code-split) because it ships a
|
|
162
|
+
full layout engine plus DOCX/PDF tooling. The initial entry chunk is small; the
|
|
163
|
+
heavy editor app and the export/import workers are lazily loaded on demand.
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT © Bohdan Lushchyk
|
package/dist-lib/builder.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as Ve, D as Ct, a as Bt, I as pe, n as It, W as Lt, B as it, b as Mt, c as qe } from "./types-
|
|
1
|
+
import { d as Ve, D as Ct, a as Bt, I as pe, n as It, W as Lt, B as it, b as Mt, c as qe } from "./types-BaYYqH97.js";
|
|
2
2
|
function Rt(n) {
|
|
3
3
|
let e = "";
|
|
4
4
|
for (let o = 0; o < n.length; o += 32768)
|