@arbidocs/blocks 0.4.20 → 0.4.22
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/dist/agents/definitions/AgentCard.d.ts +4 -1
- package/dist/agents/index.cjs +211 -211
- package/dist/agents/index.js +5 -5
- package/dist/chat/ChatPage.d.ts +1 -1
- package/dist/chat/chat-page-types.d.ts +2 -0
- package/dist/chat/index.cjs +169 -168
- package/dist/chat/index.js +3 -2
- package/dist/chunk-5TRALRWN.js +42 -0
- package/dist/chunk-5TRALRWN.js.map +1 -0
- package/dist/chunk-7UTB4PHM.cjs +300 -0
- package/dist/chunk-7UTB4PHM.cjs.map +1 -0
- package/dist/chunk-AA3PT6KR.js +286 -0
- package/dist/chunk-AA3PT6KR.js.map +1 -0
- package/dist/{chunk-QJEJBLHI.js → chunk-CQ6CYPV4.js} +7 -281
- package/dist/chunk-CQ6CYPV4.js.map +1 -0
- package/dist/chunk-EUOGPUMJ.cjs +59 -0
- package/dist/chunk-EUOGPUMJ.cjs.map +1 -0
- package/dist/{chunk-PRVKJ5XL.cjs → chunk-J22DXUJY.cjs} +4 -3
- package/dist/chunk-J22DXUJY.cjs.map +1 -0
- package/dist/{chunk-ZPOLKYR2.cjs → chunk-JCVZKCXC.cjs} +22 -69
- package/dist/chunk-JCVZKCXC.cjs.map +1 -0
- package/dist/{chunk-557EU3G4.js → chunk-JLOYWXRB.js} +3 -3
- package/dist/{chunk-557EU3G4.js.map → chunk-JLOYWXRB.js.map} +1 -1
- package/dist/{chunk-UVIOQUJA.js → chunk-JLPYOPCX.js} +4 -3
- package/dist/chunk-JLPYOPCX.js.map +1 -0
- package/dist/{chunk-4TBZRZFC.js → chunk-MLJDOH3B.js} +23 -6
- package/dist/chunk-MLJDOH3B.js.map +1 -0
- package/dist/chunk-NNH7N62N.cjs +876 -0
- package/dist/chunk-NNH7N62N.cjs.map +1 -0
- package/dist/{chunk-7Q6RBXIV.cjs → chunk-OEQXYWUO.cjs} +22 -5
- package/dist/chunk-OEQXYWUO.cjs.map +1 -0
- package/dist/{chunk-J4AOAFZF.cjs → chunk-TL2DHSC5.cjs} +11 -297
- package/dist/chunk-TL2DHSC5.cjs.map +1 -0
- package/dist/chunk-UOLMFC5K.js +846 -0
- package/dist/chunk-UOLMFC5K.js.map +1 -0
- package/dist/{chunk-DCEI37AX.cjs → chunk-YVIOCITQ.cjs} +3 -3
- package/dist/{chunk-DCEI37AX.cjs.map → chunk-YVIOCITQ.cjs.map} +1 -1
- package/dist/{chunk-S74WZNK7.js → chunk-ZEIOREJY.js} +5 -36
- package/dist/chunk-ZEIOREJY.js.map +1 -0
- package/dist/connect/docx.cjs +86 -0
- package/dist/connect/docx.cjs.map +1 -0
- package/dist/connect/docx.d.ts +1 -0
- package/dist/connect/docx.js +84 -0
- package/dist/connect/docx.js.map +1 -0
- package/dist/connect/index.cjs +136 -69
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.ts +1 -0
- package/dist/connect/index.js +91 -25
- package/dist/connect/index.js.map +1 -1
- package/dist/connection/ConnectedNativeDocxEditor.d.ts +5 -0
- package/dist/connection/useConnectedDocxNotifications.d.ts +9 -0
- package/dist/docx/DocxMentions.d.ts +17 -0
- package/dist/docx/NativeDocxComparison.d.ts +10 -0
- package/dist/docx/NativeDocxEditor.d.ts +50 -0
- package/dist/docx/export.d.ts +2 -0
- package/dist/docx/fonts.d.ts +2 -0
- package/dist/docx/index.cjs +116 -0
- package/dist/docx/index.cjs.map +1 -0
- package/dist/docx/index.d.ts +10 -0
- package/dist/docx/index.js +63 -0
- package/dist/docx/index.js.map +1 -0
- package/dist/docx/mentions.d.ts +8 -0
- package/dist/facts/index.cjs +1 -1
- package/dist/facts/index.js +1 -1
- package/dist/files/index.cjs +5 -5
- package/dist/files/index.js +6 -6
- package/dist/index.cjs +795 -794
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +17 -16
- package/dist/index.js.map +1 -1
- package/dist/settings/index.cjs +41 -41
- package/dist/settings/index.js +4 -4
- package/dist/shell/index.cjs +9 -9
- package/dist/shell/index.js +1 -1
- package/dist/site/index.cjs +13 -13
- package/dist/site/index.js +1 -1
- package/dist/studio/index.cjs +6 -5
- package/dist/studio/index.cjs.map +1 -1
- package/dist/studio/index.js +4 -3
- package/dist/studio/index.js.map +1 -1
- package/dist/workspaces/index.cjs +2 -2
- package/dist/workspaces/index.js +2 -2
- package/package.json +48 -7
- package/src/docx/README.md +68 -0
- package/src/docx/styles.css +36 -0
- package/dist/chunk-4TBZRZFC.js.map +0 -1
- package/dist/chunk-7Q6RBXIV.cjs.map +0 -1
- package/dist/chunk-J4AOAFZF.cjs.map +0 -1
- package/dist/chunk-PRVKJ5XL.cjs.map +0 -1
- package/dist/chunk-QJEJBLHI.js.map +0 -1
- package/dist/chunk-S74WZNK7.js.map +0 -1
- package/dist/chunk-UVIOQUJA.js.map +0 -1
- package/dist/chunk-ZPOLKYR2.cjs.map +0 -1
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { filterMentionCandidates, MentionMenu, findMentionQuery, extractMentions } from './chunk-AA3PT6KR.js';
|
|
3
|
+
import { unzipSync, strFromU8, strToU8, zipSync } from 'fflate';
|
|
4
|
+
import React2, { useRef, useEffect, useState, useLayoutEffect } from 'react';
|
|
5
|
+
import { useReview, DocxEditorReview, reviewModule } from '@docx-editor.dev/pro/react';
|
|
6
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
import * as Y from 'yjs';
|
|
8
|
+
import { useChromeTranslate, DocxEditor, createFontSource } from '@docx-editor.dev/react';
|
|
9
|
+
import { collaborationModule } from '@docx-editor.dev/pro';
|
|
10
|
+
import { DOCUMENT_COLLABORATION_VERSIONS } from '@docx-editor.dev/pro/collaboration';
|
|
11
|
+
import { createHocuspocusCollaboration } from '@docx-editor.dev/pro/collaboration/hocuspocus';
|
|
12
|
+
import { Type, Ellipsis, WifiOff, Check, Download, Save, RefreshCw } from 'lucide-react';
|
|
13
|
+
|
|
14
|
+
function normalizeDocxExport(data) {
|
|
15
|
+
const files = unzipSync(new Uint8Array(data));
|
|
16
|
+
const part = "[Content_Types].xml";
|
|
17
|
+
const xml = new DOMParser().parseFromString(strFromU8(files[part]), "application/xml");
|
|
18
|
+
if (xml.getElementsByTagName("parsererror").length) throw new Error("Invalid DOCX content types.");
|
|
19
|
+
const namespace = "http://schemas.openxmlformats.org/package/2006/content-types";
|
|
20
|
+
const types = {
|
|
21
|
+
"word/comments.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml",
|
|
22
|
+
"word/commentsExtended.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml",
|
|
23
|
+
"word/commentsIds.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml",
|
|
24
|
+
"word/commentsExtensible.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml"
|
|
25
|
+
};
|
|
26
|
+
let changed = false;
|
|
27
|
+
for (const [name, type] of Object.entries(types)) {
|
|
28
|
+
if (!files[name]) continue;
|
|
29
|
+
let override = Array.from(xml.getElementsByTagNameNS(namespace, "Override")).find(
|
|
30
|
+
(node) => node.getAttribute("PartName") === "/" + name
|
|
31
|
+
);
|
|
32
|
+
if (!override) {
|
|
33
|
+
override = xml.createElementNS(namespace, "Override");
|
|
34
|
+
override.setAttribute("PartName", "/" + name);
|
|
35
|
+
xml.documentElement.appendChild(override);
|
|
36
|
+
}
|
|
37
|
+
if (override.getAttribute("ContentType") !== type) {
|
|
38
|
+
override.setAttribute("ContentType", type);
|
|
39
|
+
changed = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (changed) files[part] = strToU8(new XMLSerializer().serializeToString(xml));
|
|
43
|
+
const word = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
44
|
+
for (const name of Object.keys(files)) {
|
|
45
|
+
if (!/^word\/(document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/.test(name))
|
|
46
|
+
continue;
|
|
47
|
+
const document2 = new DOMParser().parseFromString(strFromU8(files[name]), "application/xml");
|
|
48
|
+
if (document2.getElementsByTagName("parsererror").length)
|
|
49
|
+
throw new Error("Invalid Word document XML.");
|
|
50
|
+
const elements = Array.from(document2.getElementsByTagName("*"));
|
|
51
|
+
const used = new Set(
|
|
52
|
+
elements.map((element) => element.getAttributeNS(word, "id")).filter(Boolean)
|
|
53
|
+
);
|
|
54
|
+
const seen = /* @__PURE__ */ new Set();
|
|
55
|
+
let next = 1, repaired = false;
|
|
56
|
+
for (const element of elements) {
|
|
57
|
+
if (element.namespaceURI !== word || !["ins", "del", "moveFrom", "moveTo"].includes(element.localName))
|
|
58
|
+
continue;
|
|
59
|
+
const id = element.getAttributeNS(word, "id");
|
|
60
|
+
if (!id) continue;
|
|
61
|
+
if (seen.has(id)) {
|
|
62
|
+
while (used.has(String(next))) next++;
|
|
63
|
+
const unique = String(next++);
|
|
64
|
+
const attribute = element.getAttributeNodeNS(word, "id");
|
|
65
|
+
element.setAttributeNS(word, attribute.name, unique);
|
|
66
|
+
used.add(unique);
|
|
67
|
+
repaired = true;
|
|
68
|
+
} else seen.add(id);
|
|
69
|
+
}
|
|
70
|
+
if (repaired) {
|
|
71
|
+
files[name] = strToU8(new XMLSerializer().serializeToString(document2));
|
|
72
|
+
changed = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!changed) return data;
|
|
76
|
+
return zipSync(files).slice().buffer;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/docx/mentions.ts
|
|
80
|
+
function mentionCandidates(mentions) {
|
|
81
|
+
return [
|
|
82
|
+
{ id: "arbi", label: "ARBI", kind: "assistant" },
|
|
83
|
+
...mentions.filter((person) => person.name.trim()).map((person) => ({
|
|
84
|
+
id: person.id,
|
|
85
|
+
label: person.name,
|
|
86
|
+
kind: person.kind === "agent" ? "assistant" : "user"
|
|
87
|
+
}))
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
function mentionedDocxRecipients(text, mentions) {
|
|
91
|
+
return extractMentions(text, mentionCandidates(mentions)).map((person) => person.id);
|
|
92
|
+
}
|
|
93
|
+
function DocxMentions({
|
|
94
|
+
children,
|
|
95
|
+
mentions = [],
|
|
96
|
+
initialCommentId,
|
|
97
|
+
requestAgent,
|
|
98
|
+
notify
|
|
99
|
+
}) {
|
|
100
|
+
const review = useReview();
|
|
101
|
+
const activated = useRef(void 0);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (!initialCommentId || activated.current === initialCommentId || !review.ready) return;
|
|
104
|
+
const target = review.items.find(
|
|
105
|
+
(item) => item.kind === "comment" && item.item.id === initialCommentId
|
|
106
|
+
);
|
|
107
|
+
if (!target) return;
|
|
108
|
+
review.setPaneOpen(true);
|
|
109
|
+
if (review.setActive(target.key)) activated.current = initialCommentId;
|
|
110
|
+
}, [initialCommentId, review]);
|
|
111
|
+
const input = useRef(null);
|
|
112
|
+
const replyTarget = useRef(null);
|
|
113
|
+
const pending = useRef(null);
|
|
114
|
+
const [suggestions, setSuggestions] = useState(null);
|
|
115
|
+
const [selected, setSelected] = useState(0);
|
|
116
|
+
const candidates = filterMentionCandidates(
|
|
117
|
+
mentionCandidates(mentions),
|
|
118
|
+
suggestions?.query || ""
|
|
119
|
+
).slice(0, 8);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
const submission = pending.current;
|
|
122
|
+
if (!submission || !requestAgent) return;
|
|
123
|
+
const created = review.items.find(
|
|
124
|
+
(item) => item.kind === "comment" && !submission.ids.has(item.item.id) && item.text.trim() === submission.text
|
|
125
|
+
);
|
|
126
|
+
if (!created || created.kind !== "comment") return;
|
|
127
|
+
pending.current = null;
|
|
128
|
+
const commentId = created.parentId || created.item.id;
|
|
129
|
+
void requestAgent({
|
|
130
|
+
commentId,
|
|
131
|
+
instruction: submission.text,
|
|
132
|
+
requestId: submission.requestId,
|
|
133
|
+
recipientIds: submission.recipientIds
|
|
134
|
+
}).catch((error) => {
|
|
135
|
+
notify?.(
|
|
136
|
+
error instanceof Error ? error.message : "Could not start the document agent.",
|
|
137
|
+
"error"
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
}, [review.items, requestAgent, notify]);
|
|
141
|
+
const choose = (name) => {
|
|
142
|
+
const field = input.current;
|
|
143
|
+
if (!field || !suggestions) return;
|
|
144
|
+
const end = field.selectionStart ?? field.value.length;
|
|
145
|
+
const value = field.value.slice(0, suggestions.from) + "@" + name + " " + field.value.slice(end);
|
|
146
|
+
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set?.call(field, value);
|
|
147
|
+
field.dispatchEvent(new Event("input", { bubbles: true }));
|
|
148
|
+
field.focus();
|
|
149
|
+
field.setSelectionRange(suggestions.from + name.length + 2, suggestions.from + name.length + 2);
|
|
150
|
+
setSuggestions(null);
|
|
151
|
+
};
|
|
152
|
+
const captureSubmission = (target) => {
|
|
153
|
+
const form = target.closest("form");
|
|
154
|
+
const field = form?.querySelector(
|
|
155
|
+
'[data-testid="review-draft-input"], [data-testid="review-reply-input"]'
|
|
156
|
+
);
|
|
157
|
+
if (!field || field.readOnly || !requestAgent) return false;
|
|
158
|
+
const recipientIds = mentionedDocxRecipients(field.value, mentions);
|
|
159
|
+
if (!recipientIds.length) return false;
|
|
160
|
+
pending.current = {
|
|
161
|
+
ids: new Set(review.items.filter((i) => i.kind === "comment").map((i) => i.item.id)),
|
|
162
|
+
text: field.value.trim(),
|
|
163
|
+
requestId: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`,
|
|
164
|
+
recipientIds
|
|
165
|
+
};
|
|
166
|
+
const pinned = replyTarget.current;
|
|
167
|
+
const thread = pinned ? review.items.find((item) => item.key === pinned.key && item.kind === "comment") : void 0;
|
|
168
|
+
if (field.dataset.testid === "review-reply-input" && pinned && !thread) {
|
|
169
|
+
pending.current = null;
|
|
170
|
+
notify?.("This comment was removed. Open a current thread before replying.", "error");
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
if (field.dataset.testid === "review-reply-input" && thread) {
|
|
174
|
+
if (!review.reply(thread, field.value.trim())) {
|
|
175
|
+
pending.current = null;
|
|
176
|
+
notify?.("Could not post this reply. The comment may have been removed.", "error");
|
|
177
|
+
} else {
|
|
178
|
+
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set?.call(field, "");
|
|
179
|
+
field.dispatchEvent(new Event("input", { bubbles: true }));
|
|
180
|
+
replyTarget.current = null;
|
|
181
|
+
review.setActive(thread.key, { reveal: false });
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
};
|
|
187
|
+
return /* @__PURE__ */ jsxs(
|
|
188
|
+
"div",
|
|
189
|
+
{
|
|
190
|
+
className: "native-docx-mentions",
|
|
191
|
+
onInputCapture: (event) => {
|
|
192
|
+
const field = event.target;
|
|
193
|
+
if (!(field instanceof HTMLInputElement) || !field.dataset.testid?.startsWith("review-"))
|
|
194
|
+
return;
|
|
195
|
+
if (!replyTarget.current && review.activeKey)
|
|
196
|
+
replyTarget.current = { field, key: review.activeKey };
|
|
197
|
+
input.current = field;
|
|
198
|
+
const before = field.value.slice(0, field.selectionStart ?? field.value.length);
|
|
199
|
+
const match = findMentionQuery(before, before.length);
|
|
200
|
+
if (!match) return setSuggestions(null);
|
|
201
|
+
const rect = field.getBoundingClientRect();
|
|
202
|
+
setSelected(0);
|
|
203
|
+
setSuggestions({
|
|
204
|
+
query: match.query,
|
|
205
|
+
from: match.start,
|
|
206
|
+
left: rect.left,
|
|
207
|
+
top: rect.bottom
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
onPointerDownCapture: (event) => {
|
|
211
|
+
const target = event.target;
|
|
212
|
+
if (target instanceof HTMLElement && target.closest('[data-testid="review-marker"], [data-testid="review-reply-cancel"]'))
|
|
213
|
+
replyTarget.current = null;
|
|
214
|
+
},
|
|
215
|
+
onSubmitCapture: (event) => {
|
|
216
|
+
if (captureSubmission(event.target)) {
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
event.stopPropagation();
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
onKeyDownCapture: (event) => {
|
|
222
|
+
if (suggestions && candidates.length) {
|
|
223
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
224
|
+
event.preventDefault();
|
|
225
|
+
event.stopPropagation();
|
|
226
|
+
setSelected(
|
|
227
|
+
(s) => (s + (event.key === "ArrowDown" ? 1 : candidates.length - 1)) % candidates.length
|
|
228
|
+
);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (event.key === "Enter" || event.key === "Tab") {
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
event.stopPropagation();
|
|
234
|
+
choose(candidates[selected]?.label || candidates[0].label);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (event.key === "Escape") {
|
|
238
|
+
event.preventDefault();
|
|
239
|
+
event.stopPropagation();
|
|
240
|
+
setSuggestions(null);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (event.key === "Enter" && captureSubmission(event.target)) {
|
|
245
|
+
event.preventDefault();
|
|
246
|
+
event.stopPropagation();
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
children: [
|
|
250
|
+
children,
|
|
251
|
+
suggestions && candidates.length > 0 && /* @__PURE__ */ jsx(
|
|
252
|
+
"div",
|
|
253
|
+
{
|
|
254
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
255
|
+
onMouseDown: (event) => {
|
|
256
|
+
event.preventDefault();
|
|
257
|
+
event.stopPropagation();
|
|
258
|
+
},
|
|
259
|
+
onClick: (event) => event.stopPropagation(),
|
|
260
|
+
style: {
|
|
261
|
+
position: "fixed",
|
|
262
|
+
left: suggestions.left,
|
|
263
|
+
top: suggestions.top,
|
|
264
|
+
width: 280,
|
|
265
|
+
zIndex: 1e3
|
|
266
|
+
},
|
|
267
|
+
children: /* @__PURE__ */ jsx(
|
|
268
|
+
MentionMenu,
|
|
269
|
+
{
|
|
270
|
+
open: true,
|
|
271
|
+
placement: "below",
|
|
272
|
+
items: candidates,
|
|
273
|
+
selectedIndex: selected,
|
|
274
|
+
setSelectedIndex: setSelected,
|
|
275
|
+
onSelect: (person) => choose(person.label),
|
|
276
|
+
labels: { list: "Mention workspace member" }
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
)
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
var drafts = /* @__PURE__ */ new Map();
|
|
286
|
+
var stateBytes = (room) => Y.encodeStateAsUpdate(room.ydoc);
|
|
287
|
+
var encodeState = (room) => {
|
|
288
|
+
let binary = "";
|
|
289
|
+
for (const byte of stateBytes(room)) binary += String.fromCharCode(byte);
|
|
290
|
+
return btoa(binary);
|
|
291
|
+
};
|
|
292
|
+
function covered(room, vector, receipt) {
|
|
293
|
+
const decode = (value) => Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
|
|
294
|
+
const server = Y.decodeStateVector(decode(vector)), local = Y.decodeStateVector(Y.encodeStateVector(room.ydoc));
|
|
295
|
+
for (const [client, clock] of local) if ((server.get(client) || 0) < clock) return false;
|
|
296
|
+
const deleted = Y.decodeUpdate(stateBytes(room)).ds.clients;
|
|
297
|
+
const saved = Y.decodeUpdate(decode(receipt)).ds.clients;
|
|
298
|
+
for (const [client, ranges] of deleted)
|
|
299
|
+
for (const range of ranges) {
|
|
300
|
+
if (!(saved.get(client) || []).some(
|
|
301
|
+
(s) => s.clock <= range.clock && s.clock + s.len >= range.clock + range.len
|
|
302
|
+
))
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
var EditorBoundary = class extends React2.Component {
|
|
308
|
+
state = {};
|
|
309
|
+
static getDerivedStateFromError(error) {
|
|
310
|
+
return { error };
|
|
311
|
+
}
|
|
312
|
+
render() {
|
|
313
|
+
return this.state.error ? /* @__PURE__ */ jsxs("div", { role: "alert", className: "native-docx-error", children: [
|
|
314
|
+
"Could not render this Word document: ",
|
|
315
|
+
this.state.error.message
|
|
316
|
+
] }) : this.props.children;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
function DocxParticipants({ session }) {
|
|
320
|
+
const [people, setPeople] = useState([]);
|
|
321
|
+
useEffect(() => {
|
|
322
|
+
if (!session) return;
|
|
323
|
+
setPeople(session.participants());
|
|
324
|
+
return session.subscribeParticipants(setPeople);
|
|
325
|
+
}, [session]);
|
|
326
|
+
return /* @__PURE__ */ jsx("div", { className: "native-docx-presence", "aria-label": "People viewing this document", children: people.map((p) => /* @__PURE__ */ jsx("span", { className: "native-docx-avatar", title: p.name, children: p.name.split(" ").map((s) => s[0]).slice(0, 2).join("") }, p.actorId)) });
|
|
327
|
+
}
|
|
328
|
+
function RibbonMenu({
|
|
329
|
+
label,
|
|
330
|
+
icon,
|
|
331
|
+
children
|
|
332
|
+
}) {
|
|
333
|
+
const [open, setOpen] = useState(false);
|
|
334
|
+
const element = useRef(null);
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
if (!open) return;
|
|
337
|
+
const dismiss = (event) => {
|
|
338
|
+
if (!element.current?.contains(event.target)) setOpen(false);
|
|
339
|
+
};
|
|
340
|
+
const escape = (event) => {
|
|
341
|
+
if (event.key === "Escape") setOpen(false);
|
|
342
|
+
};
|
|
343
|
+
document.addEventListener("pointerdown", dismiss);
|
|
344
|
+
document.addEventListener("keydown", escape);
|
|
345
|
+
return () => {
|
|
346
|
+
document.removeEventListener("pointerdown", dismiss);
|
|
347
|
+
document.removeEventListener("keydown", escape);
|
|
348
|
+
};
|
|
349
|
+
}, [open]);
|
|
350
|
+
return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
|
|
351
|
+
/* @__PURE__ */ jsx(
|
|
352
|
+
"button",
|
|
353
|
+
{
|
|
354
|
+
type: "button",
|
|
355
|
+
className: "native-docx-ribbon-trigger",
|
|
356
|
+
"aria-label": label,
|
|
357
|
+
title: label,
|
|
358
|
+
"aria-expanded": open,
|
|
359
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
360
|
+
onClick: () => setOpen((value) => !value),
|
|
361
|
+
children: icon
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
open && /* @__PURE__ */ jsx(
|
|
365
|
+
"div",
|
|
366
|
+
{
|
|
367
|
+
className: "native-docx-ribbon-popover",
|
|
368
|
+
role: "group",
|
|
369
|
+
"aria-label": label,
|
|
370
|
+
onMouseDown: (event) => {
|
|
371
|
+
if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
|
|
372
|
+
event.preventDefault();
|
|
373
|
+
},
|
|
374
|
+
children
|
|
375
|
+
}
|
|
376
|
+
)
|
|
377
|
+
] });
|
|
378
|
+
}
|
|
379
|
+
function DocxRibbon({ onSave }) {
|
|
380
|
+
const [tab, setTab] = useState("Home");
|
|
381
|
+
const t = useChromeTranslate();
|
|
382
|
+
return /* @__PURE__ */ jsxs("div", { className: "native-docx-ribbon", children: [
|
|
383
|
+
/* @__PURE__ */ jsxs("select", { "aria-label": "Ribbon tab", value: tab, onChange: (event) => setTab(event.target.value), children: [
|
|
384
|
+
/* @__PURE__ */ jsx("option", { children: "Home" }),
|
|
385
|
+
/* @__PURE__ */ jsx("option", { children: "Insert" }),
|
|
386
|
+
/* @__PURE__ */ jsx("option", { children: "Review" })
|
|
387
|
+
] }),
|
|
388
|
+
/* @__PURE__ */ jsxs(DocxEditor.Toolbar, { t, preset: false, onSave, children: [
|
|
389
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Undo, {}),
|
|
390
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Redo, {}),
|
|
391
|
+
tab === "Home" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
392
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Bold, {}),
|
|
393
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Italic, {}),
|
|
394
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Underline, {}),
|
|
395
|
+
/* @__PURE__ */ jsxs(RibbonMenu, { label: "Font", icon: /* @__PURE__ */ jsx(Type, { size: 16 }), children: [
|
|
396
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.FontFamily, {}),
|
|
397
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.FontSize, {}),
|
|
398
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.FontColor, {}),
|
|
399
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Highlight, {})
|
|
400
|
+
] }),
|
|
401
|
+
/* @__PURE__ */ jsxs(RibbonMenu, { label: "More formatting", icon: /* @__PURE__ */ jsx(Ellipsis, { size: 16 }), children: [
|
|
402
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.StylePicker, {}),
|
|
403
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Alignment, {}),
|
|
404
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.BulletList, {}),
|
|
405
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.NumberedList, {}),
|
|
406
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.LineSpacing, {}),
|
|
407
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Indent, {}),
|
|
408
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Outdent, {}),
|
|
409
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Strike, {}),
|
|
410
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.ClearFormatting, {})
|
|
411
|
+
] })
|
|
412
|
+
] }),
|
|
413
|
+
tab === "Insert" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
414
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.TableInsert, {}),
|
|
415
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageInsert, {}),
|
|
416
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Link, {})
|
|
417
|
+
] }),
|
|
418
|
+
tab === "Review" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
419
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.EditingMode, {}),
|
|
420
|
+
/* @__PURE__ */ jsx(DocxEditor.Toolbar.Comments, {}),
|
|
421
|
+
/* @__PURE__ */ jsx(RibbonMenu, { label: "Review options", icon: /* @__PURE__ */ jsx(Ellipsis, { size: 16 }), children: /* @__PURE__ */ jsx(DocxEditor.Toolbar.Reviewers, {}) })
|
|
422
|
+
] })
|
|
423
|
+
] })
|
|
424
|
+
] });
|
|
425
|
+
}
|
|
426
|
+
var fixedZoomMode = { type: "fixed" };
|
|
427
|
+
function NativeDocxEditor({
|
|
428
|
+
documentId,
|
|
429
|
+
title,
|
|
430
|
+
draftContent,
|
|
431
|
+
initialCommentId,
|
|
432
|
+
snapshot = false,
|
|
433
|
+
onSaved,
|
|
434
|
+
adapter
|
|
435
|
+
}) {
|
|
436
|
+
const [ready, setReady] = useState(), [error, setError] = useState(""), [retry, setRetry] = useState(0);
|
|
437
|
+
const [status, setStatus] = useState("Connecting\u2026"), [dirty, setDirty] = useState(false), [saving, setSaving] = useState(false), [offline, setOffline] = useState(false);
|
|
438
|
+
const revision = useRef(0);
|
|
439
|
+
const stageRef = useRef(null), fitCleanup = useRef(void 0);
|
|
440
|
+
const ref = useRef(null), dirtyRef = useRef(false), draftTimer = useRef(void 0);
|
|
441
|
+
const isDraft = draftContent !== void 0;
|
|
442
|
+
useEffect(() => {
|
|
443
|
+
let cancelled = false;
|
|
444
|
+
let room;
|
|
445
|
+
const cleanups = [];
|
|
446
|
+
setReady(void 0);
|
|
447
|
+
setError("");
|
|
448
|
+
setStatus("Connecting\u2026");
|
|
449
|
+
setDirty(false);
|
|
450
|
+
dirtyRef.current = false;
|
|
451
|
+
async function start() {
|
|
452
|
+
const fonts = await adapter.loadFonts();
|
|
453
|
+
if (isDraft || snapshot) {
|
|
454
|
+
const data = isDraft ? drafts.get(documentId) || await (await adapter.request("/prepare", {
|
|
455
|
+
method: "POST",
|
|
456
|
+
headers: { "Content-Type": "application/json" },
|
|
457
|
+
body: JSON.stringify({ content: draftContent })
|
|
458
|
+
})).arrayBuffer() : await (await adapter.request(`/${documentId}/content`)).arrayBuffer();
|
|
459
|
+
if (!cancelled) {
|
|
460
|
+
setReady({
|
|
461
|
+
document: new Uint8Array(data),
|
|
462
|
+
fonts,
|
|
463
|
+
modules: [reviewModule()],
|
|
464
|
+
canEdit: !snapshot,
|
|
465
|
+
author: adapter.author || "You",
|
|
466
|
+
filename: title
|
|
467
|
+
});
|
|
468
|
+
setStatus(snapshot ? "Saved version \xB7 read only" : "Unsaved note");
|
|
469
|
+
}
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const session = await adapter.open(documentId);
|
|
473
|
+
const ws = new URL(session.websocket_path, adapter.baseUrl || location.origin);
|
|
474
|
+
ws.protocol = ws.protocol === "https:" ? "wss:" : "ws:";
|
|
475
|
+
let initial = true;
|
|
476
|
+
room = await createHocuspocusCollaboration({
|
|
477
|
+
url: ws.toString(),
|
|
478
|
+
roomId: session.room_id,
|
|
479
|
+
identity: session.identity,
|
|
480
|
+
bootstrap: { kind: "join" },
|
|
481
|
+
offlineEditing: true,
|
|
482
|
+
token: async () => {
|
|
483
|
+
const next = initial ? session : await adapter.open(documentId);
|
|
484
|
+
initial = false;
|
|
485
|
+
if (!cancelled)
|
|
486
|
+
setReady((previous) => previous ? { ...previous, canEdit: next.can_edit } : previous);
|
|
487
|
+
return JSON.stringify({
|
|
488
|
+
capability: next.token,
|
|
489
|
+
versions: DOCUMENT_COLLABORATION_VERSIONS
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
if (cancelled) {
|
|
494
|
+
room.destroy();
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const current = room;
|
|
498
|
+
cleanups.push(
|
|
499
|
+
current.session.subscribeStatus((s, _reason, detail) => {
|
|
500
|
+
const disconnected = s === "disconnected";
|
|
501
|
+
setOffline(disconnected);
|
|
502
|
+
if (s === "error") {
|
|
503
|
+
setError(
|
|
504
|
+
detail || "The collaboration session failed. Export your local document before reconnecting."
|
|
505
|
+
);
|
|
506
|
+
setStatus("Connection failed");
|
|
507
|
+
} else if (disconnected) setStatus("Offline \xB7 edits buffered locally");
|
|
508
|
+
else if (s === "ready") {
|
|
509
|
+
setError("");
|
|
510
|
+
setStatus(dirtyRef.current ? "Saving changes\u2026" : "Connected");
|
|
511
|
+
}
|
|
512
|
+
})
|
|
513
|
+
);
|
|
514
|
+
const stateless = ({ payload }) => {
|
|
515
|
+
try {
|
|
516
|
+
const message = JSON.parse(payload);
|
|
517
|
+
if (message.type === "persisted") {
|
|
518
|
+
if (!cancelled && message.vector && message.receipt && covered(current, message.vector, message.receipt)) {
|
|
519
|
+
dirtyRef.current = false;
|
|
520
|
+
setDirty(false);
|
|
521
|
+
setStatus("All changes saved");
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (message.type === "pending") setStatus("Saving changes\u2026");
|
|
525
|
+
if (message.type === "save-error") setStatus(message.message || "Autosave failed");
|
|
526
|
+
} catch {
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
current.provider.on("stateless", stateless);
|
|
530
|
+
cleanups.push(() => current.provider.off("stateless", stateless));
|
|
531
|
+
if (adapter.subscribeTokenChanges)
|
|
532
|
+
cleanups.push(
|
|
533
|
+
adapter.subscribeTokenChanges(() => {
|
|
534
|
+
void current.provider.sendToken().catch(() => setStatus("Could not renew the collaboration session"));
|
|
535
|
+
})
|
|
536
|
+
);
|
|
537
|
+
const refresh = setInterval(
|
|
538
|
+
() => {
|
|
539
|
+
void current.provider.sendToken().catch(() => setStatus("Could not renew the collaboration session"));
|
|
540
|
+
},
|
|
541
|
+
5 * 60 * 1e3
|
|
542
|
+
);
|
|
543
|
+
cleanups.push(() => clearInterval(refresh));
|
|
544
|
+
setReady({
|
|
545
|
+
document: current.document,
|
|
546
|
+
fonts,
|
|
547
|
+
modules: [reviewModule(), collaborationModule({ session: current.session })],
|
|
548
|
+
room: current,
|
|
549
|
+
canEdit: session.can_edit,
|
|
550
|
+
author: session.identity.name,
|
|
551
|
+
filename: session.filename
|
|
552
|
+
});
|
|
553
|
+
setStatus(session.can_edit ? "All changes saved" : "Read only \xB7 live");
|
|
554
|
+
}
|
|
555
|
+
void start().catch((e) => {
|
|
556
|
+
if (!cancelled) setError(e instanceof Error ? e.message : String(e));
|
|
557
|
+
});
|
|
558
|
+
return () => {
|
|
559
|
+
cancelled = true;
|
|
560
|
+
cleanups.forEach((f) => f());
|
|
561
|
+
clearTimeout(draftTimer.current);
|
|
562
|
+
fitCleanup.current?.();
|
|
563
|
+
room?.destroy();
|
|
564
|
+
};
|
|
565
|
+
}, [documentId, isDraft, snapshot, retry]);
|
|
566
|
+
useEffect(() => {
|
|
567
|
+
adapter.onDirtyChange?.(dirty);
|
|
568
|
+
const beforeUnload = (e) => {
|
|
569
|
+
if (dirtyRef.current) e.preventDefault();
|
|
570
|
+
};
|
|
571
|
+
window.addEventListener("beforeunload", beforeUnload);
|
|
572
|
+
return () => {
|
|
573
|
+
window.removeEventListener("beforeunload", beforeUnload);
|
|
574
|
+
adapter.onDirtyChange?.(false);
|
|
575
|
+
};
|
|
576
|
+
}, [dirty]);
|
|
577
|
+
async function exportDocument() {
|
|
578
|
+
const data = await ref.current?.save();
|
|
579
|
+
if (!data) return;
|
|
580
|
+
const url = URL.createObjectURL(
|
|
581
|
+
new Blob([normalizeDocxExport(data)], {
|
|
582
|
+
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
583
|
+
})
|
|
584
|
+
);
|
|
585
|
+
const a = document.createElement("a");
|
|
586
|
+
a.href = url;
|
|
587
|
+
a.download = title.replace(/\.[^.]+$/, "") + ".docx";
|
|
588
|
+
a.click();
|
|
589
|
+
setTimeout(() => URL.revokeObjectURL(url), 5e3);
|
|
590
|
+
}
|
|
591
|
+
useLayoutEffect(() => {
|
|
592
|
+
const editor = ref.current;
|
|
593
|
+
return () => {
|
|
594
|
+
if (isDraft && editor && dirtyRef.current)
|
|
595
|
+
void editor.save().then((bytes) => {
|
|
596
|
+
if (bytes) drafts.set(documentId, bytes);
|
|
597
|
+
}).catch(() => {
|
|
598
|
+
});
|
|
599
|
+
};
|
|
600
|
+
}, [documentId, isDraft, ready]);
|
|
601
|
+
async function saveVersion() {
|
|
602
|
+
if (!ready || saving) return;
|
|
603
|
+
setSaving(true);
|
|
604
|
+
const atRevision = revision.current;
|
|
605
|
+
try {
|
|
606
|
+
const result = isDraft ? await (async () => {
|
|
607
|
+
const bytes = await ref.current?.save();
|
|
608
|
+
if (!bytes) throw new Error("The editor is not ready.");
|
|
609
|
+
return (await adapter.request(`/notes?${new URLSearchParams({ title })}`, {
|
|
610
|
+
method: "POST",
|
|
611
|
+
headers: {
|
|
612
|
+
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
613
|
+
},
|
|
614
|
+
body: bytes
|
|
615
|
+
})).json();
|
|
616
|
+
})() : await adapter.save(documentId, ready.room ? encodeState(ready.room) : void 0);
|
|
617
|
+
if (revision.current === atRevision) {
|
|
618
|
+
dirtyRef.current = false;
|
|
619
|
+
setDirty(false);
|
|
620
|
+
drafts.delete(documentId);
|
|
621
|
+
}
|
|
622
|
+
await Promise.resolve(adapter.onVersionSaved?.(result.document_ext_id)).catch(() => {
|
|
623
|
+
});
|
|
624
|
+
onSaved?.(result.document_ext_id);
|
|
625
|
+
adapter.notify?.(isDraft ? "Word note saved" : "DOCX version saved", "success");
|
|
626
|
+
} catch (e) {
|
|
627
|
+
adapter.notify?.(e instanceof Error ? e.message : "Could not save this document.", "error");
|
|
628
|
+
setStatus("Save failed \xB7 your draft is retained");
|
|
629
|
+
} finally {
|
|
630
|
+
setSaving(false);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function editorReady(editor) {
|
|
634
|
+
ref.current = editor;
|
|
635
|
+
fitCleanup.current?.();
|
|
636
|
+
const viewport = stageRef.current?.querySelector(".native-docx-viewport");
|
|
637
|
+
if (!viewport) return;
|
|
638
|
+
const fit = () => {
|
|
639
|
+
const pageWidth = editor.getPageGeometry()[0]?.box.width;
|
|
640
|
+
if (!pageWidth) return;
|
|
641
|
+
const style = getComputedStyle(viewport);
|
|
642
|
+
const available = viewport.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
|
|
643
|
+
const zoom = Math.max(0.1, Math.min(2.5, available / pageWidth));
|
|
644
|
+
if (Math.abs(editor.getZoom() - zoom) > 1e-3) editor.setZoom(zoom);
|
|
645
|
+
};
|
|
646
|
+
const resize = new ResizeObserver(fit);
|
|
647
|
+
const chrome = new MutationObserver(fit);
|
|
648
|
+
resize.observe(viewport);
|
|
649
|
+
chrome.observe(viewport, { attributes: true, attributeFilter: ["style", "data-review-pane"] });
|
|
650
|
+
fit();
|
|
651
|
+
fitCleanup.current = () => {
|
|
652
|
+
resize.disconnect();
|
|
653
|
+
chrome.disconnect();
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function changed() {
|
|
657
|
+
if (snapshot) return;
|
|
658
|
+
revision.current += 1;
|
|
659
|
+
dirtyRef.current = true;
|
|
660
|
+
setDirty(true);
|
|
661
|
+
setStatus(
|
|
662
|
+
isDraft ? "Unsaved note" : offline ? "Offline \xB7 edits buffered locally" : "Saving changes\u2026"
|
|
663
|
+
);
|
|
664
|
+
if (isDraft) {
|
|
665
|
+
clearTimeout(draftTimer.current);
|
|
666
|
+
draftTimer.current = setTimeout(() => {
|
|
667
|
+
void ref.current?.save().then((bytes) => {
|
|
668
|
+
if (bytes) drafts.set(documentId, bytes);
|
|
669
|
+
});
|
|
670
|
+
}, 200);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return /* @__PURE__ */ jsxs("section", { className: "native-docx-viewer", "data-testid": "native-docx-editor", children: [
|
|
674
|
+
/* @__PURE__ */ jsxs("div", { className: "native-docx-actions", children: [
|
|
675
|
+
/* @__PURE__ */ jsxs("span", { className: "native-docx-status", role: "status", children: [
|
|
676
|
+
offline ? /* @__PURE__ */ jsx(WifiOff, { size: 14 }) : !dirty && ready ? /* @__PURE__ */ jsx(Check, { size: 14 }) : null,
|
|
677
|
+
status
|
|
678
|
+
] }),
|
|
679
|
+
/* @__PURE__ */ jsx(DocxParticipants, { session: ready?.room?.session }),
|
|
680
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => void exportDocument(), disabled: !ready, "aria-label": "Download DOCX", children: [
|
|
681
|
+
/* @__PURE__ */ jsx(Download, { size: 14 }),
|
|
682
|
+
"DOCX"
|
|
683
|
+
] }),
|
|
684
|
+
!snapshot && ready?.canEdit && /* @__PURE__ */ jsxs(
|
|
685
|
+
"button",
|
|
686
|
+
{
|
|
687
|
+
className: "native-docx-save",
|
|
688
|
+
onClick: () => void saveVersion(),
|
|
689
|
+
disabled: !ready || saving || offline,
|
|
690
|
+
"data-testid": "native-docx-save",
|
|
691
|
+
children: [
|
|
692
|
+
/* @__PURE__ */ jsx(Save, { size: 14 }),
|
|
693
|
+
saving ? "Saving\u2026" : isDraft ? "Save note" : "Save version"
|
|
694
|
+
]
|
|
695
|
+
}
|
|
696
|
+
)
|
|
697
|
+
] }),
|
|
698
|
+
offline && /* @__PURE__ */ jsx("div", { className: "native-docx-notice", children: "Changes are buffered in this browser. Keep this tab open until the connection returns, or download your DOCX." }),
|
|
699
|
+
error && /* @__PURE__ */ jsxs("div", { className: "native-docx-notice", "data-error": "true", role: "alert", children: [
|
|
700
|
+
error,
|
|
701
|
+
" ",
|
|
702
|
+
/* @__PURE__ */ jsxs(
|
|
703
|
+
"button",
|
|
704
|
+
{
|
|
705
|
+
disabled: dirty,
|
|
706
|
+
title: dirty ? "Download your unsaved DOCX before reopening the editor" : void 0,
|
|
707
|
+
onClick: () => setRetry((x) => x + 1),
|
|
708
|
+
children: [
|
|
709
|
+
/* @__PURE__ */ jsx(RefreshCw, { size: 12 }),
|
|
710
|
+
"Retry"
|
|
711
|
+
]
|
|
712
|
+
}
|
|
713
|
+
)
|
|
714
|
+
] }),
|
|
715
|
+
!ready && !error && /* @__PURE__ */ jsx("div", { className: "native-docx-error", role: "status", children: "Opening Word document\u2026" }),
|
|
716
|
+
ready && /* @__PURE__ */ jsx("div", { className: "native-docx-body docx-editor", children: /* @__PURE__ */ jsx(EditorBoundary, { children: /* @__PURE__ */ jsxs(
|
|
717
|
+
DocxEditor.Root,
|
|
718
|
+
{
|
|
719
|
+
document: ready.document,
|
|
720
|
+
modules: ready.modules,
|
|
721
|
+
fonts: ready.fonts,
|
|
722
|
+
mode: !ready.canEdit || snapshot ? "view" : "suggesting",
|
|
723
|
+
reviewDisplayMode: "all-markup",
|
|
724
|
+
zoomMode: fixedZoomMode,
|
|
725
|
+
author: ready.author,
|
|
726
|
+
onReady: editorReady,
|
|
727
|
+
onChange: (change) => {
|
|
728
|
+
if (change.source !== "load") changed();
|
|
729
|
+
},
|
|
730
|
+
onFontError: (e) => setError(e.message),
|
|
731
|
+
children: [
|
|
732
|
+
/* @__PURE__ */ jsx(
|
|
733
|
+
DocxEditor.RevisionMarkup,
|
|
734
|
+
{
|
|
735
|
+
insertions: { mark: "underline", color: "blue" },
|
|
736
|
+
deletions: { mark: "strikethrough", color: "red" },
|
|
737
|
+
movedFrom: { mark: "doubleStrikethrough", color: "green" },
|
|
738
|
+
movedTo: { mark: "doubleUnderline", color: "green" },
|
|
739
|
+
trackMoves: true
|
|
740
|
+
}
|
|
741
|
+
),
|
|
742
|
+
/* @__PURE__ */ jsx(DocxRibbon, { onSave: () => void saveVersion() }),
|
|
743
|
+
/* @__PURE__ */ jsxs(
|
|
744
|
+
"div",
|
|
745
|
+
{
|
|
746
|
+
className: "native-docx-stage",
|
|
747
|
+
ref: stageRef,
|
|
748
|
+
onMouseDownCapture: (event) => {
|
|
749
|
+
if (event.button === 2 && ref.current?.getSelectionPlacement())
|
|
750
|
+
event.preventDefault();
|
|
751
|
+
},
|
|
752
|
+
children: [
|
|
753
|
+
/* @__PURE__ */ jsxs(DocxEditor.Viewport, { className: "native-docx-viewport", children: [
|
|
754
|
+
/* @__PURE__ */ jsx(DocxEditor.Content, {}),
|
|
755
|
+
/* @__PURE__ */ jsx(
|
|
756
|
+
DocxMentions,
|
|
757
|
+
{
|
|
758
|
+
initialCommentId,
|
|
759
|
+
mentions: adapter.mentions,
|
|
760
|
+
notify: adapter.notify,
|
|
761
|
+
requestAgent: adapter.requestAgent ? (request) => adapter.requestAgent(documentId, request) : void 0,
|
|
762
|
+
children: /* @__PURE__ */ jsx(DocxEditorReview, {})
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
/* @__PURE__ */ jsx(DocxEditor.HeaderFooterChrome, {}),
|
|
766
|
+
/* @__PURE__ */ jsx(DocxEditor.NotesChrome, {}),
|
|
767
|
+
/* @__PURE__ */ jsx(DocxEditor.Equation, {}),
|
|
768
|
+
/* @__PURE__ */ jsx(DocxEditor.HyperLink, {}),
|
|
769
|
+
/* @__PURE__ */ jsx(DocxEditor.ContextMenu, {})
|
|
770
|
+
] }),
|
|
771
|
+
/* @__PURE__ */ jsx(DocxEditor.PageNumber, {})
|
|
772
|
+
]
|
|
773
|
+
}
|
|
774
|
+
)
|
|
775
|
+
]
|
|
776
|
+
}
|
|
777
|
+
) }, retry) })
|
|
778
|
+
] });
|
|
779
|
+
}
|
|
780
|
+
var loaded = /* @__PURE__ */ new Map();
|
|
781
|
+
function loadDocxFonts(baseUrl = "/native-docx/fonts") {
|
|
782
|
+
let promise = loaded.get(baseUrl);
|
|
783
|
+
if (!promise) {
|
|
784
|
+
promise = loadFonts(baseUrl).catch((error) => {
|
|
785
|
+
loaded.delete(baseUrl);
|
|
786
|
+
throw error;
|
|
787
|
+
});
|
|
788
|
+
loaded.set(baseUrl, promise);
|
|
789
|
+
}
|
|
790
|
+
return promise;
|
|
791
|
+
}
|
|
792
|
+
async function loadFonts(baseUrl) {
|
|
793
|
+
const families = ["Liberation Serif", "Liberation Sans", "Liberation Mono"];
|
|
794
|
+
const variants = [
|
|
795
|
+
{ suffix: "Regular", weight: 400, style: "normal" },
|
|
796
|
+
{ suffix: "Bold", weight: 700, style: "normal" },
|
|
797
|
+
{ suffix: "Italic", weight: 400, style: "italic" },
|
|
798
|
+
{ suffix: "BoldItalic", weight: 700, style: "italic" }
|
|
799
|
+
];
|
|
800
|
+
const sources = await Promise.all(
|
|
801
|
+
families.flatMap(
|
|
802
|
+
(family) => variants.map(async (face) => {
|
|
803
|
+
const r = await fetch(
|
|
804
|
+
`${baseUrl.replace(/\/$/, "")}/${family.replace(" ", "")}-${face.suffix}.ttf`
|
|
805
|
+
);
|
|
806
|
+
if (!r.ok) throw new Error(`Could not load the document font ${family}.`);
|
|
807
|
+
const result = createFontSource(new Uint8Array(await r.arrayBuffer()), {
|
|
808
|
+
family,
|
|
809
|
+
weight: face.weight,
|
|
810
|
+
style: face.style
|
|
811
|
+
});
|
|
812
|
+
if ("failure" in result)
|
|
813
|
+
throw new Error(`Could not initialize the document font ${family}.`);
|
|
814
|
+
return result.source;
|
|
815
|
+
})
|
|
816
|
+
)
|
|
817
|
+
);
|
|
818
|
+
const aliases = {
|
|
819
|
+
"Times New Roman": "Liberation Serif",
|
|
820
|
+
Cambria: "Liberation Serif",
|
|
821
|
+
"DejaVu Serif": "Liberation Serif",
|
|
822
|
+
Calibri: "Liberation Sans",
|
|
823
|
+
Aptos: "Liberation Sans",
|
|
824
|
+
Arial: "Liberation Sans",
|
|
825
|
+
Helvetica: "Liberation Sans",
|
|
826
|
+
"Courier New": "Liberation Mono",
|
|
827
|
+
"DejaVu Sans Mono": "Liberation Mono"
|
|
828
|
+
};
|
|
829
|
+
return {
|
|
830
|
+
epoch: 1,
|
|
831
|
+
maxFontBytes: 12 * 1024 * 1024,
|
|
832
|
+
sources,
|
|
833
|
+
defaultFont: { family: "Liberation Serif", sizeHalfPoints: 22 },
|
|
834
|
+
supportedFamilies: [...families, ...Object.keys(aliases)],
|
|
835
|
+
substitutions: Object.entries(aliases).flatMap(
|
|
836
|
+
([from, to]) => variants.map((face) => ({
|
|
837
|
+
from: { family: from, weight: face.weight, style: face.style },
|
|
838
|
+
to: { family: to, weight: face.weight, style: face.style }
|
|
839
|
+
}))
|
|
840
|
+
)
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
export { DocxMentions, DocxParticipants, DocxRibbon, NativeDocxEditor, loadDocxFonts, normalizeDocxExport };
|
|
845
|
+
//# sourceMappingURL=chunk-UOLMFC5K.js.map
|
|
846
|
+
//# sourceMappingURL=chunk-UOLMFC5K.js.map
|