@forevka/wordcanvas 0.2.0 → 0.3.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 +4 -2
- package/dist-lib/assets/{engine-DZ0JqUhP.js → engine-BVd9_fny.js} +1126 -1125
- package/dist-lib/assets/{worker-DL4Rxrg-.js → worker-C35TK5Ee.js} +1 -1
- package/dist-lib/builder.js +1 -1
- package/dist-lib/{editorApp-Dl_Glngl.js → editorApp-DYckquXo.js} +5464 -5158
- package/dist-lib/{types-C2taeCu4.js → types-5-egHV_r.js} +5 -5
- package/dist-lib/wordcanvas.js +65 -2
- package/package.json +7 -16
- package/dist-lib/wordcanvas-CgZ_uuf9.js +0 -373
|
@@ -921,11 +921,11 @@ class We {
|
|
|
921
921
|
}
|
|
922
922
|
}
|
|
923
923
|
export {
|
|
924
|
-
|
|
924
|
+
Ae as A,
|
|
925
925
|
$ as B,
|
|
926
|
-
|
|
926
|
+
te as C,
|
|
927
927
|
oe as D,
|
|
928
|
-
|
|
928
|
+
h as E,
|
|
929
929
|
ye as F,
|
|
930
930
|
D as G,
|
|
931
931
|
_ as H,
|
|
@@ -967,6 +967,6 @@ export {
|
|
|
967
967
|
Ne as v,
|
|
968
968
|
K as w,
|
|
969
969
|
E as x,
|
|
970
|
-
|
|
971
|
-
|
|
970
|
+
Ie as y,
|
|
971
|
+
ee as z
|
|
972
972
|
};
|
package/dist-lib/wordcanvas.js
CHANGED
|
@@ -1,4 +1,67 @@
|
|
|
1
|
-
|
|
1
|
+
class s {
|
|
2
|
+
handle = null;
|
|
3
|
+
ready;
|
|
4
|
+
handlers = /* @__PURE__ */ new Map();
|
|
5
|
+
constructor(e) {
|
|
6
|
+
this.ready = new Promise((t) => {
|
|
7
|
+
const a = {
|
|
8
|
+
container: e.container,
|
|
9
|
+
backendUrl: e.backendUrl,
|
|
10
|
+
// `docId` is the canonical name; fall back to the deprecated `collabId`.
|
|
11
|
+
collabId: e.docId ?? e.collabId,
|
|
12
|
+
user: e.user,
|
|
13
|
+
onShareLink: e.onShareLink,
|
|
14
|
+
onReady: (n) => {
|
|
15
|
+
this.handle = n, t(n);
|
|
16
|
+
},
|
|
17
|
+
onEvent: (n) => this.emit(n)
|
|
18
|
+
};
|
|
19
|
+
import("./editorApp-DYckquXo.js").then((n) => n.mountEditorApp(a));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Subscribe to a collaboration event. Returns an unsubscribe function. */
|
|
23
|
+
on(e, t) {
|
|
24
|
+
let a = this.handlers.get(e);
|
|
25
|
+
return a || this.handlers.set(e, a = /* @__PURE__ */ new Set()), a.add(t), () => this.off(e, t);
|
|
26
|
+
}
|
|
27
|
+
/** Unsubscribe a handler previously passed to on(). */
|
|
28
|
+
off(e, t) {
|
|
29
|
+
this.handlers.get(e)?.delete(t);
|
|
30
|
+
}
|
|
31
|
+
emit(e) {
|
|
32
|
+
const { type: t, ...a } = e;
|
|
33
|
+
for (const n of this.handlers.get(t) ?? []) n(a);
|
|
34
|
+
}
|
|
35
|
+
/** Resolves once the editor is mounted and ready. */
|
|
36
|
+
whenReady() {
|
|
37
|
+
return this.ready;
|
|
38
|
+
}
|
|
39
|
+
/** Open a .docx. When online, auto-publishes it and surfaces a share link. */
|
|
40
|
+
async openDocx(e) {
|
|
41
|
+
return (await this.ready).openDocx(e);
|
|
42
|
+
}
|
|
43
|
+
/** Replace the open document with a programmatically-built one (e.g. a
|
|
44
|
+
* DocumentBuilder result). The input is cloned. Like openDocx, this starts
|
|
45
|
+
* a NEW document: undo history and any live collab session are dropped
|
|
46
|
+
* (the next share() forks). Zoom and scroll position are preserved, so
|
|
47
|
+
* calling this on every data change gives a stable live preview. */
|
|
48
|
+
async setDocument(e) {
|
|
49
|
+
(await this.ready).setDocument(e);
|
|
50
|
+
}
|
|
51
|
+
/** Publish the current document and resolve its shareable link (online only). */
|
|
52
|
+
async share() {
|
|
53
|
+
return (await this.ready).share();
|
|
54
|
+
}
|
|
55
|
+
getDocId() {
|
|
56
|
+
return this.handle?.getDocId() ?? null;
|
|
57
|
+
}
|
|
58
|
+
getShareLink() {
|
|
59
|
+
return this.handle?.getShareLink() ?? null;
|
|
60
|
+
}
|
|
61
|
+
destroy() {
|
|
62
|
+
this.handle?.destroy(), this.handle = null, this.handlers.clear();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
2
65
|
export {
|
|
3
|
-
|
|
66
|
+
s as WordCanvas
|
|
4
67
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forevka/wordcanvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Embeddable canvas-rendered, Word-compatible document editor with live collaboration and DOCX/PDF import & export. Self-contained, zero runtime dependencies.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,28 +27,19 @@
|
|
|
27
27
|
],
|
|
28
28
|
"module": "./dist-lib/wordcanvas.js",
|
|
29
29
|
"types": "./types/wordcanvas.d.ts",
|
|
30
|
+
"//exports": "Only the surface that is actually shipped (files: dist-lib + types). The frontend's import/export pipelines are consumed in-workspace by the backend via its tsconfig paths + vitest aliases, NOT through this map, so they are intentionally not published. Note: npm's publishConfig does NOT override `exports`, so this committed map is what ships — keep it pointing at dist, not src.",
|
|
30
31
|
"exports": {
|
|
31
32
|
".": {
|
|
32
33
|
"types": "./types/wordcanvas.d.ts",
|
|
33
34
|
"import": "./dist-lib/wordcanvas.js"
|
|
34
35
|
},
|
|
35
|
-
"./builder":
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
"./builder": {
|
|
37
|
+
"types": "./types/builder.d.ts",
|
|
38
|
+
"import": "./dist-lib/builder.js"
|
|
39
|
+
}
|
|
39
40
|
},
|
|
40
41
|
"publishConfig": {
|
|
41
|
-
"access": "public"
|
|
42
|
-
"exports": {
|
|
43
|
-
".": {
|
|
44
|
-
"types": "./types/wordcanvas.d.ts",
|
|
45
|
-
"import": "./dist-lib/wordcanvas.js"
|
|
46
|
-
},
|
|
47
|
-
"./builder": {
|
|
48
|
-
"types": "./types/builder.d.ts",
|
|
49
|
-
"import": "./dist-lib/builder.js"
|
|
50
|
-
}
|
|
51
|
-
}
|
|
42
|
+
"access": "public"
|
|
52
43
|
},
|
|
53
44
|
"files": ["dist-lib", "types"],
|
|
54
45
|
"scripts": {
|
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
let n = null;
|
|
2
|
-
function p(r) {
|
|
3
|
-
n = r;
|
|
4
|
-
}
|
|
5
|
-
function b() {
|
|
6
|
-
if (!n) throw new Error("WordCanvas runtime not configured (set it before importing editorApp)");
|
|
7
|
-
return n;
|
|
8
|
-
}
|
|
9
|
-
const a = "wordcanvas-styles", l = `
|
|
10
|
-
.wordcanvas-root {
|
|
11
|
-
display: flex; flex-direction: column; height: 100%; min-height: 0; overflow-x: hidden;
|
|
12
|
-
font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
13
|
-
color: #323130;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/* ===== Word-style ribbon ============================================ */
|
|
17
|
-
#toolbar {
|
|
18
|
-
flex: 0 0 auto; display: flex; flex-direction: column;
|
|
19
|
-
background: #f3f2f1; border-bottom: 1px solid #e1dfdd; user-select: none;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/* --- tab strip --- */
|
|
23
|
-
.rib-tabs {
|
|
24
|
-
display: flex; align-items: flex-end; gap: 1px; height: 32px;
|
|
25
|
-
padding: 4px 8px 0; background: #f3f2f1;
|
|
26
|
-
}
|
|
27
|
-
.rib-tab {
|
|
28
|
-
border: none; background: transparent; cursor: pointer;
|
|
29
|
-
font: inherit; font-size: 13px; color: #323130;
|
|
30
|
-
padding: 5px 11px 6px; border-radius: 4px 4px 0 0; position: relative;
|
|
31
|
-
}
|
|
32
|
-
.rib-tab:hover { background: #eceae9; }
|
|
33
|
-
.rib-tab.active {
|
|
34
|
-
background: #fff; color: #2b579a; font-weight: 600;
|
|
35
|
-
box-shadow: 0 -1px 2px rgba(0,0,0,0.05);
|
|
36
|
-
}
|
|
37
|
-
.rib-tab.active::after {
|
|
38
|
-
content: ""; position: absolute; left: 8px; right: 8px; bottom: 0;
|
|
39
|
-
height: 2px; background: #2b579a;
|
|
40
|
-
}
|
|
41
|
-
.rib-tab.file { background: #2b579a; color: #fff; font-weight: 600; }
|
|
42
|
-
.rib-tab.file:hover { background: #21457e; }
|
|
43
|
-
|
|
44
|
-
/* --- ribbon body: one panel visible at a time --- */
|
|
45
|
-
.rib-bodies {
|
|
46
|
-
background: #fff; border-top: 1px solid #e1dfdd; border-bottom: 1px solid #e1dfdd;
|
|
47
|
-
}
|
|
48
|
-
.rib-panel { display: none; align-items: stretch; min-height: 94px; padding: 0 2px; }
|
|
49
|
-
.rib-panel.active { display: flex; }
|
|
50
|
-
|
|
51
|
-
/* --- group: stacked controls + caption, divider on the right --- */
|
|
52
|
-
.rib-group {
|
|
53
|
-
display: flex; flex-direction: column; align-items: center;
|
|
54
|
-
padding: 5px 7px 3px; position: relative;
|
|
55
|
-
}
|
|
56
|
-
.rib-group + .rib-group::before {
|
|
57
|
-
content: ""; position: absolute; left: 0; top: 8px; bottom: 8px;
|
|
58
|
-
width: 1px; background: #e1dfdd;
|
|
59
|
-
}
|
|
60
|
-
.rib-controls { display: flex; align-items: center; gap: 2px; flex: 1; }
|
|
61
|
-
/* two stacked rows (Font, Paragraph) */
|
|
62
|
-
.rib-rows { display: flex; flex-direction: column; gap: 3px; flex: 1; justify-content: center; }
|
|
63
|
-
.rib-row { display: flex; align-items: center; gap: 2px; }
|
|
64
|
-
.rib-label {
|
|
65
|
-
font-size: 11px; color: #605e5c; padding-top: 3px; white-space: nowrap;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/* --- buttons --- */
|
|
69
|
-
#toolbar button.rib-btn {
|
|
70
|
-
min-width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
|
|
71
|
-
border: 1px solid transparent; border-radius: 4px; background: transparent; cursor: pointer;
|
|
72
|
-
color: #323130; font-size: 13px; padding: 0 4px; gap: 1px;
|
|
73
|
-
}
|
|
74
|
-
#toolbar button.rib-btn:hover { background: #e1dfdd; }
|
|
75
|
-
#toolbar button.rib-btn:active { background: #d2d0ce; }
|
|
76
|
-
#toolbar button.rib-btn.active { background: #cfe3fb; color: #0b57d0; border-color: #b3d3f5; }
|
|
77
|
-
#toolbar button.rib-btn.active:hover { background: #bcd8fa; }
|
|
78
|
-
#toolbar button.rib-btn.active svg { color: #0b57d0; }
|
|
79
|
-
#toolbar button.rib-btn svg { width: 16px; height: 16px; display: block; }
|
|
80
|
-
#toolbar button.rib-btn .caret { width: 8px; height: 8px; }
|
|
81
|
-
|
|
82
|
-
/* large button (Paste): icon over caption */
|
|
83
|
-
#toolbar button.rib-big {
|
|
84
|
-
flex-direction: column; height: 100%; min-width: 48px; padding: 4px 6px; gap: 2px; justify-content: center;
|
|
85
|
-
}
|
|
86
|
-
#toolbar button.rib-big svg { width: 26px; height: 26px; }
|
|
87
|
-
#toolbar button.rib-big .big-cap { font-size: 11px; display: flex; align-items: center; gap: 1px; }
|
|
88
|
-
|
|
89
|
-
/* swatch button: icon row + colour underline */
|
|
90
|
-
#toolbar button.rib-swatch { flex-direction: column; gap: 0; padding: 2px 3px; }
|
|
91
|
-
#toolbar button.rib-swatch .row { display: flex; align-items: center; gap: 1px; }
|
|
92
|
-
#toolbar button.rib-swatch .bar { width: 18px; height: 3px; margin-top: 1px; border-radius: 1px; }
|
|
93
|
-
|
|
94
|
-
/* disabled stub (feature not supported by the engine yet) */
|
|
95
|
-
#toolbar button.rib-btn:disabled { opacity: 0.38; cursor: default; }
|
|
96
|
-
#toolbar button.rib-btn:disabled:hover { background: transparent; }
|
|
97
|
-
|
|
98
|
-
/* form controls */
|
|
99
|
-
#toolbar select, #toolbar input[type="number"] {
|
|
100
|
-
height: 24px; border: 1px solid #c8c6c4; border-radius: 3px; background: #fff;
|
|
101
|
-
font: inherit; font-size: 13px; color: #323130;
|
|
102
|
-
}
|
|
103
|
-
#toolbar select:hover, #toolbar input[type="number"]:hover { border-color: #8a8886; }
|
|
104
|
-
|
|
105
|
-
/* styles gallery */
|
|
106
|
-
.rib-gallery {
|
|
107
|
-
display: flex; align-items: center; gap: 4px; height: 64px; padding: 0 4px;
|
|
108
|
-
border: 1px solid #c8c6c4; border-radius: 4px; background: #fff;
|
|
109
|
-
overflow-x: auto; max-width: 340px;
|
|
110
|
-
}
|
|
111
|
-
.rib-gallery::-webkit-scrollbar { height: 8px; }
|
|
112
|
-
.rib-gallery::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 4px; }
|
|
113
|
-
.style-card {
|
|
114
|
-
flex: 0 0 auto; width: 76px; height: 50px; border: 1px solid #e1dfdd; border-radius: 2px;
|
|
115
|
-
background: #fff; cursor: pointer; display: flex; flex-direction: column;
|
|
116
|
-
align-items: center; justify-content: center; gap: 3px; padding: 2px;
|
|
117
|
-
}
|
|
118
|
-
.style-card:hover { border-color: #2b579a; }
|
|
119
|
-
.style-card.active { border-color: #2b579a; box-shadow: inset 0 0 0 1px #2b579a; background: #eef3fb; }
|
|
120
|
-
.style-card .preview { font-size: 13px; line-height: 1; color: #323130; }
|
|
121
|
-
.style-card .name { font-size: 10px; color: #605e5c; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 72px; }
|
|
122
|
-
|
|
123
|
-
/* work area: optional outline drawer + ruler + the scrolling page canvas */
|
|
124
|
-
#workarea { flex: 1 1 auto; min-height: 0; display: flex; }
|
|
125
|
-
#editorpane { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
|
|
126
|
-
/* touch-action: keep one/two-finger PAN (scroll) but disable the browser's
|
|
127
|
-
native pinch-zoom and double-tap-zoom on the document, so our own
|
|
128
|
-
pinch-to-zoom handler (index.ts) owns those gestures. The toolbar/status bar
|
|
129
|
-
keep default touch-action so the ribbon scrolls and taps normally. */
|
|
130
|
-
#app { flex: 1 1 auto; min-height: 0; min-width: 0; overflow: auto; background: #e8eaed; position: relative; touch-action: pan-x pan-y; }
|
|
131
|
-
|
|
132
|
-
/* horizontal ruler (inch ticks, margin shading, draggable indent markers) */
|
|
133
|
-
#ruler { flex: 0 0 22px; height: 22px; position: relative; background: #e8eaed; border-bottom: 1px solid #d2d0ce; overflow: hidden; }
|
|
134
|
-
#ruler.hidden { display: none; }
|
|
135
|
-
#ruler canvas { position: absolute; inset: 0; }
|
|
136
|
-
#ruler .ruler-marker { position: absolute; width: 0; height: 0; cursor: ew-resize; z-index: 2; }
|
|
137
|
-
/* left-indent: bottom-pointing triangle sitting on the baseline */
|
|
138
|
-
#ruler .ruler-left { bottom: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 7px solid #5b6b8c; }
|
|
139
|
-
/* first-line-indent: top-pointing triangle */
|
|
140
|
-
#ruler .ruler-first { top: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 7px solid #5b6b8c; }
|
|
141
|
-
|
|
142
|
-
/* ===== Outline / Navigation drawer ================================== */
|
|
143
|
-
#outline {
|
|
144
|
-
flex: 0 0 264px; width: 264px; min-height: 0; overflow-y: auto;
|
|
145
|
-
background: #fff; border-right: 1px solid #e1dfdd; display: none;
|
|
146
|
-
}
|
|
147
|
-
#outline.open { display: block; }
|
|
148
|
-
#outline .outline-head {
|
|
149
|
-
position: sticky; top: 0; background: #fff; z-index: 1;
|
|
150
|
-
display: flex; align-items: center; justify-content: space-between;
|
|
151
|
-
padding: 9px 8px 9px 14px; border-bottom: 1px solid #e1dfdd;
|
|
152
|
-
font-size: 13px; font-weight: 600; color: #323130;
|
|
153
|
-
}
|
|
154
|
-
#outline .outline-head button {
|
|
155
|
-
border: none; background: transparent; cursor: pointer; color: #605e5c;
|
|
156
|
-
width: 24px; height: 24px; border-radius: 4px; font-size: 17px; line-height: 1;
|
|
157
|
-
}
|
|
158
|
-
#outline .outline-head button:hover { background: #f3f2f1; }
|
|
159
|
-
#outline-list { padding: 4px 0 12px; }
|
|
160
|
-
.outline-item {
|
|
161
|
-
display: block; width: 100%; box-sizing: border-box; text-align: left;
|
|
162
|
-
border: none; border-left: 3px solid transparent; background: transparent; cursor: pointer;
|
|
163
|
-
padding: 4px 12px; color: #323130; font-size: 13px; line-height: 1.35;
|
|
164
|
-
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
165
|
-
}
|
|
166
|
-
.outline-item:hover { background: #f3f2f1; }
|
|
167
|
-
.outline-item.active { background: #eef3fb; border-left-color: #2b579a; color: #2b579a; font-weight: 600; }
|
|
168
|
-
.outline-empty { padding: 14px; color: #80868b; font-size: 12px; line-height: 1.4; }
|
|
169
|
-
|
|
170
|
-
/* collapsed ribbon: keep the tab strip, hide the body */
|
|
171
|
-
#toolbar.collapsed .rib-bodies { display: none; }
|
|
172
|
-
|
|
173
|
-
/* ===== Status bar ================================================== */
|
|
174
|
-
#statusbar {
|
|
175
|
-
flex: 0 0 auto; height: 24px; display: flex; align-items: center; justify-content: space-between;
|
|
176
|
-
background: #2b579a; color: #fff; font-size: 12px; padding: 0 10px; user-select: none;
|
|
177
|
-
}
|
|
178
|
-
#statusbar .sb-left, #statusbar .sb-right { display: flex; align-items: center; gap: 16px; }
|
|
179
|
-
#statusbar .sb-right { gap: 8px; }
|
|
180
|
-
#statusbar .sb-item { white-space: nowrap; }
|
|
181
|
-
#statusbar .sb-sep { width: 1px; height: 14px; background: rgba(255,255,255,0.35); }
|
|
182
|
-
#statusbar button.sb-btn {
|
|
183
|
-
background: transparent; border: none; color: #fff; cursor: pointer; font-size: 15px;
|
|
184
|
-
width: 22px; height: 20px; border-radius: 3px; line-height: 1; padding: 0;
|
|
185
|
-
}
|
|
186
|
-
#statusbar button.sb-btn:hover { background: rgba(255,255,255,0.18); }
|
|
187
|
-
#statusbar input[type="range"] { width: 120px; cursor: pointer; accent-color: #fff; }
|
|
188
|
-
#statusbar .sb-zoom { min-width: 38px; text-align: right; }
|
|
189
|
-
|
|
190
|
-
/* ===== Popovers (palettes, menus, pickers, dialogs) ================ */
|
|
191
|
-
.cw-pop {
|
|
192
|
-
position: fixed; background: #fff; border: 1px solid #c8c6c4; border-radius: 6px;
|
|
193
|
-
box-shadow: 0 4px 18px rgba(0,0,0,0.18); z-index: 50; font-size: 13px; padding: 4px;
|
|
194
|
-
}
|
|
195
|
-
.cw-menu { display: flex; flex-direction: column; min-width: 168px; }
|
|
196
|
-
.cw-menu button {
|
|
197
|
-
display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
|
|
198
|
-
border: none; background: transparent; cursor: pointer; padding: 7px 10px; border-radius: 4px;
|
|
199
|
-
font: inherit; color: #323130;
|
|
200
|
-
}
|
|
201
|
-
.cw-menu button:hover { background: #f3f2f1; }
|
|
202
|
-
.cw-menu .check { width: 14px; color: #2b579a; }
|
|
203
|
-
.cw-menu .sample { color: #605e5c; }
|
|
204
|
-
.cw-pop-title { font-size: 11px; color: #605e5c; padding: 4px 6px 2px; }
|
|
205
|
-
.cw-swatches { display: grid; grid-template-columns: repeat(6, 22px); gap: 4px; padding: 4px 6px; }
|
|
206
|
-
.cw-swatches button { width: 22px; height: 22px; border: 1px solid rgba(0,0,0,0.18); border-radius: 3px; cursor: pointer; padding: 0; }
|
|
207
|
-
.cw-swatches button:hover { outline: 2px solid #2b579a; outline-offset: 1px; }
|
|
208
|
-
.cw-pop .pop-action {
|
|
209
|
-
display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; border: none;
|
|
210
|
-
background: transparent; cursor: pointer; padding: 7px 8px; border-radius: 4px; font: inherit; color: #323130;
|
|
211
|
-
border-top: 1px solid #edebe9; margin-top: 4px;
|
|
212
|
-
}
|
|
213
|
-
.cw-pop .pop-action:hover { background: #f3f2f1; }
|
|
214
|
-
.cw-grid { display: grid; gap: 2px; padding: 8px 8px 2px; }
|
|
215
|
-
.cw-grid .cell { width: 15px; height: 15px; border: 1px solid #c8c6c4; background: #fff; }
|
|
216
|
-
.cw-grid .cell.on { background: #cfe3fb; border-color: #2b579a; }
|
|
217
|
-
.cw-grid-label { text-align: center; font-size: 12px; color: #605e5c; padding: 4px 0 6px; }
|
|
218
|
-
.cw-dialog { display: flex; flex-direction: column; gap: 8px; padding: 10px; min-width: 248px; }
|
|
219
|
-
.cw-dialog label { font-size: 12px; color: #605e5c; display: flex; flex-direction: column; gap: 3px; }
|
|
220
|
-
.cw-dialog input { height: 28px; border: 1px solid #c8c6c4; border-radius: 4px; padding: 0 8px; font: inherit; font-size: 13px; }
|
|
221
|
-
.cw-dialog .row { display: flex; justify-content: flex-end; gap: 6px; margin-top: 2px; }
|
|
222
|
-
.cw-dialog button { height: 28px; border: 1px solid #c8c6c4; background: #fff; border-radius: 4px; cursor: pointer; padding: 0 12px; font: inherit; font-size: 13px; }
|
|
223
|
-
.cw-dialog button.primary { background: #2b579a; color: #fff; border-color: #2b579a; }
|
|
224
|
-
.cw-dialog button.danger { color: #a4262c; }
|
|
225
|
-
|
|
226
|
-
/* ===== Busy overlay (docx import / join / publish) ================= */
|
|
227
|
-
.cw-loading-overlay {
|
|
228
|
-
position: fixed; inset: 0; z-index: 70; background: rgba(0,0,0,0.25);
|
|
229
|
-
display: flex; align-items: center; justify-content: center;
|
|
230
|
-
}
|
|
231
|
-
.cw-loading-card {
|
|
232
|
-
display: flex; flex-direction: column; align-items: center; gap: 12px;
|
|
233
|
-
background: #fff; border: 1px solid #c8c6c4; border-radius: 8px;
|
|
234
|
-
box-shadow: 0 8px 30px rgba(0,0,0,0.25); padding: 22px 28px; min-width: 200px;
|
|
235
|
-
}
|
|
236
|
-
.cw-spinner {
|
|
237
|
-
width: 30px; height: 30px; border-radius: 50%;
|
|
238
|
-
border: 3px solid #e1dfdd; border-top-color: #2b579a;
|
|
239
|
-
animation: cw-spin 0.8s linear infinite;
|
|
240
|
-
}
|
|
241
|
-
@keyframes cw-spin { to { transform: rotate(360deg); } }
|
|
242
|
-
.cw-loading-label { font-size: 13px; color: #323130; text-align: center; }
|
|
243
|
-
.cw-progress { width: 180px; height: 4px; background: #e1dfdd; border-radius: 2px; overflow: hidden; }
|
|
244
|
-
.cw-progress-bar { height: 100%; width: 0%; background: #2b579a; transition: width 0.15s ease; }
|
|
245
|
-
|
|
246
|
-
/* floating mini-toolbar shown above a selected image */
|
|
247
|
-
#img-toolbar {
|
|
248
|
-
position: fixed; display: none; align-items: center; gap: 2px; z-index: 40;
|
|
249
|
-
background: #fff; border: 1px solid #c8c6c4; border-radius: 6px; padding: 3px;
|
|
250
|
-
box-shadow: 0 3px 12px rgba(0,0,0,0.18);
|
|
251
|
-
}
|
|
252
|
-
#img-toolbar button {
|
|
253
|
-
width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
|
|
254
|
-
border: 1px solid transparent; border-radius: 4px; background: transparent; cursor: pointer; color: #323130;
|
|
255
|
-
}
|
|
256
|
-
#img-toolbar button:hover { background: #e1dfdd; }
|
|
257
|
-
#img-toolbar button.danger:hover { background: #fde7e9; color: #a4262c; }
|
|
258
|
-
#img-toolbar button svg { width: 16px; height: 16px; }
|
|
259
|
-
#img-toolbar .sep { width: 1px; height: 18px; background: #e1dfdd; margin: 0 2px; }
|
|
260
|
-
|
|
261
|
-
/* ===== Mobile / touch responsive layer ============================== */
|
|
262
|
-
/* Activates on touch devices (coarse primary pointer) OR narrow screens.
|
|
263
|
-
Desktop is untouched outside this block. Strategy: collapse the ribbon to one
|
|
264
|
-
horizontally-scrollable row, hide group captions, grow touch targets to ~40px,
|
|
265
|
-
turn the 264px outline into an overlay drawer, and clamp floating panels to
|
|
266
|
-
the viewport (a bottom sheet) so they never render off-screen. */
|
|
267
|
-
@media (pointer: coarse), (max-width: 760px) {
|
|
268
|
-
/* Ribbon body: single scrollable row instead of a tall multi-group block. */
|
|
269
|
-
.rib-panel { min-height: 0; overflow-x: auto; overflow-y: hidden; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; }
|
|
270
|
-
.rib-label { display: none; }
|
|
271
|
-
.rib-group { padding: 4px 6px; }
|
|
272
|
-
/* Touch targets (Apple/Material guideline is ~40-48px). */
|
|
273
|
-
#toolbar button.rib-btn { min-width: 40px; height: 40px; }
|
|
274
|
-
#toolbar button.rib-btn svg { width: 18px; height: 18px; }
|
|
275
|
-
.rib-tab { padding: 9px 14px; font-size: 14px; }
|
|
276
|
-
#toolbar select, #toolbar input[type="number"] { height: 36px; font-size: 14px; }
|
|
277
|
-
|
|
278
|
-
/* Outline: overlay the page instead of stealing 264px of a ~360px screen. */
|
|
279
|
-
#workarea { position: relative; }
|
|
280
|
-
#outline { position: absolute; left: 0; top: 0; bottom: 0; height: auto; width: min(264px, 80vw); z-index: 30; box-shadow: 2px 0 16px rgba(0,0,0,0.18); }
|
|
281
|
-
|
|
282
|
-
/* Status bar: tappable zoom controls. */
|
|
283
|
-
#statusbar { height: 36px; }
|
|
284
|
-
#statusbar button.sb-btn { width: 32px; height: 30px; font-size: 17px; }
|
|
285
|
-
#statusbar input[type="range"] { width: 96px; }
|
|
286
|
-
|
|
287
|
-
/* Floating panels (Page Setup, Find) → bottom sheet; Activity → full-width.
|
|
288
|
-
!important overrides the inline position/size set in editorApp.ts. */
|
|
289
|
-
.cw-float-panel { left: 8px !important; right: 8px !important; top: auto !important; bottom: 8px !important; width: auto !important; max-width: none !important; max-height: 60vh; overflow: auto; }
|
|
290
|
-
.cw-float-drawer { width: 100% !important; }
|
|
291
|
-
#img-toolbar button { width: 34px; height: 34px; }
|
|
292
|
-
|
|
293
|
-
/* Image resize handles: 8px dots are unhittable with a finger — an invisible
|
|
294
|
-
::before pads the touch target to ~24px without changing the visual size. */
|
|
295
|
-
.cw-obj-handle::before { content: ""; position: absolute; inset: -8px; }
|
|
296
|
-
}
|
|
297
|
-
`;
|
|
298
|
-
function s() {
|
|
299
|
-
if (document.getElementById(a)) return;
|
|
300
|
-
const r = document.createElement("style");
|
|
301
|
-
r.id = a, r.textContent = l, document.head.appendChild(r);
|
|
302
|
-
}
|
|
303
|
-
let i = !1;
|
|
304
|
-
class c {
|
|
305
|
-
handle = null;
|
|
306
|
-
ready;
|
|
307
|
-
handlers = /* @__PURE__ */ new Map();
|
|
308
|
-
constructor(e) {
|
|
309
|
-
if (i)
|
|
310
|
-
throw new Error("WordCanvas: only one instance per page is supported in this version");
|
|
311
|
-
i = !0, s(), this.ready = new Promise((o) => {
|
|
312
|
-
p({
|
|
313
|
-
container: e.container,
|
|
314
|
-
backendUrl: e.backendUrl,
|
|
315
|
-
// `docId` is the canonical name; fall back to the deprecated `collabId`.
|
|
316
|
-
collabId: e.docId ?? e.collabId,
|
|
317
|
-
user: e.user,
|
|
318
|
-
onShareLink: e.onShareLink,
|
|
319
|
-
onReady: (t) => {
|
|
320
|
-
this.handle = t, o(t);
|
|
321
|
-
},
|
|
322
|
-
onEvent: (t) => this.emit(t)
|
|
323
|
-
}), import("./editorApp-Dl_Glngl.js");
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
/** Subscribe to a collaboration event. Returns an unsubscribe function. */
|
|
327
|
-
on(e, o) {
|
|
328
|
-
let t = this.handlers.get(e);
|
|
329
|
-
return t || this.handlers.set(e, t = /* @__PURE__ */ new Set()), t.add(o), () => this.off(e, o);
|
|
330
|
-
}
|
|
331
|
-
/** Unsubscribe a handler previously passed to on(). */
|
|
332
|
-
off(e, o) {
|
|
333
|
-
this.handlers.get(e)?.delete(o);
|
|
334
|
-
}
|
|
335
|
-
emit(e) {
|
|
336
|
-
const { type: o, ...t } = e;
|
|
337
|
-
for (const d of this.handlers.get(o) ?? []) d(t);
|
|
338
|
-
}
|
|
339
|
-
/** Resolves once the editor is mounted and ready. */
|
|
340
|
-
whenReady() {
|
|
341
|
-
return this.ready;
|
|
342
|
-
}
|
|
343
|
-
/** Open a .docx. When online, auto-publishes it and surfaces a share link. */
|
|
344
|
-
async openDocx(e) {
|
|
345
|
-
return (await this.ready).openDocx(e);
|
|
346
|
-
}
|
|
347
|
-
/** Replace the open document with a programmatically-built one (e.g. a
|
|
348
|
-
* DocumentBuilder result). The input is cloned. Like openDocx, this starts
|
|
349
|
-
* a NEW document: undo history and any live collab session are dropped
|
|
350
|
-
* (the next share() forks). Zoom and scroll position are preserved, so
|
|
351
|
-
* calling this on every data change gives a stable live preview. */
|
|
352
|
-
async setDocument(e) {
|
|
353
|
-
(await this.ready).setDocument(e);
|
|
354
|
-
}
|
|
355
|
-
/** Publish the current document and resolve its shareable link (online only). */
|
|
356
|
-
async share() {
|
|
357
|
-
return (await this.ready).share();
|
|
358
|
-
}
|
|
359
|
-
getDocId() {
|
|
360
|
-
return this.handle?.getDocId() ?? null;
|
|
361
|
-
}
|
|
362
|
-
getShareLink() {
|
|
363
|
-
return this.handle?.getShareLink() ?? null;
|
|
364
|
-
}
|
|
365
|
-
destroy() {
|
|
366
|
-
this.handle?.destroy(), this.handle = null, this.handlers.clear(), i = !1;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
export {
|
|
370
|
-
c as W,
|
|
371
|
-
s as e,
|
|
372
|
-
b as g
|
|
373
|
-
};
|