@w100n/documents 0.13.0 → 0.15.0

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.
Files changed (53) hide show
  1. package/dist/{SheetView-3XTEV2VT.css → SheetView-43MPR64N.css} +5 -4
  2. package/dist/{SheetView-3XTEV2VT.css.map → SheetView-43MPR64N.css.map} +1 -1
  3. package/dist/{SheetView-3UAFXGZS.js → SheetView-72KXS4DM.js} +51 -10
  4. package/dist/SheetView-72KXS4DM.js.map +1 -0
  5. package/dist/TextEditor-NSICFE6U.js +1562 -0
  6. package/dist/TextEditor-NSICFE6U.js.map +1 -0
  7. package/dist/{chunk-L765DZPO.js → chunk-2CA3HQTI.js} +3 -3
  8. package/dist/{chunk-L765DZPO.js.map → chunk-2CA3HQTI.js.map} +1 -1
  9. package/dist/{chunk-WR3MT7KP.js → chunk-M4OQG3EN.js} +754 -57
  10. package/dist/chunk-M4OQG3EN.js.map +1 -0
  11. package/dist/{chunk-T7XCGDE5.js → chunk-OXC4PUOC.js} +2 -2
  12. package/dist/{chunk-MRP3EURG.js → chunk-Z7UNJ4ND.js} +194 -25
  13. package/dist/chunk-Z7UNJ4ND.js.map +1 -0
  14. package/dist/collab.cjs +927 -80
  15. package/dist/collab.cjs.map +1 -1
  16. package/dist/collab.d.cts +2 -2
  17. package/dist/collab.d.ts +2 -2
  18. package/dist/collab.js +2 -2
  19. package/dist/{grid-EI5JNZAA.js → grid-Q4XBPQFY.js} +3 -3
  20. package/dist/grid-Q4XBPQFY.js.map +1 -0
  21. package/dist/index.cjs +2136 -420
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.css +1047 -171
  24. package/dist/index.css.map +1 -1
  25. package/dist/index.d.cts +9 -4
  26. package/dist/index.d.ts +9 -4
  27. package/dist/index.js +283 -33
  28. package/dist/index.js.map +1 -1
  29. package/dist/kinds.cjs +2 -2
  30. package/dist/kinds.cjs.map +1 -1
  31. package/dist/kinds.d.cts +2 -2
  32. package/dist/kinds.d.ts +2 -2
  33. package/dist/kinds.js +1 -1
  34. package/dist/memory.cjs +3 -0
  35. package/dist/memory.cjs.map +1 -1
  36. package/dist/memory.d.cts +1 -1
  37. package/dist/memory.d.ts +1 -1
  38. package/dist/memory.js +4 -1
  39. package/dist/memory.js.map +1 -1
  40. package/dist/{types-Er60M5kL.d.ts → types-BTsVooZw.d.cts} +2 -1
  41. package/dist/{types-Er60M5kL.d.cts → types-BTsVooZw.d.ts} +2 -1
  42. package/dist/write-BCI8yool.d.cts +16 -0
  43. package/dist/write-BCI8yool.d.ts +16 -0
  44. package/package.json +1 -1
  45. package/dist/SheetView-3UAFXGZS.js.map +0 -1
  46. package/dist/TextEditor-OLBLWC6F.js +0 -983
  47. package/dist/TextEditor-OLBLWC6F.js.map +0 -1
  48. package/dist/chunk-MRP3EURG.js.map +0 -1
  49. package/dist/chunk-WR3MT7KP.js.map +0 -1
  50. package/dist/grid-EI5JNZAA.js.map +0 -1
  51. package/dist/write-DaVtTJCS.d.cts +0 -6
  52. package/dist/write-DaVtTJCS.d.ts +0 -6
  53. /package/dist/{chunk-T7XCGDE5.js.map → chunk-OXC4PUOC.js.map} +0 -0
@@ -1,983 +0,0 @@
1
- 'use client';
2
- import {
3
- useLoad
4
- } from "./chunk-QDH7EZAF.js";
5
- import {
6
- useLive
7
- } from "./chunk-T7XCGDE5.js";
8
- import {
9
- a4,
10
- align,
11
- changes,
12
- clear,
13
- decide,
14
- entries,
15
- fromMarkdown,
16
- indent,
17
- kit,
18
- letter,
19
- locate,
20
- notes,
21
- paging,
22
- suggesting,
23
- toMarkdown,
24
- track
25
- } from "./chunk-MRP3EURG.js";
26
- import {
27
- rove,
28
- useRestore
29
- } from "./chunk-TDP6OJET.js";
30
- import {
31
- readDocx,
32
- writeDocx
33
- } from "./chunk-WR3MT7KP.js";
34
- import "./chunk-ISOOUYLI.js";
35
-
36
- // src/editor/TextEditor.tsx
37
- import { EditorContent, useEditor, useEditorState } from "@tiptap/react";
38
- import {
39
- AlignCenter,
40
- AlignJustify,
41
- AlignLeft,
42
- AlignRight,
43
- Baseline,
44
- BetweenHorizontalEnd,
45
- BetweenHorizontalStart,
46
- BetweenVerticalEnd,
47
- BetweenVerticalStart,
48
- Bold,
49
- Check,
50
- CheckCheck,
51
- ChevronDown,
52
- Code,
53
- FilePenLine,
54
- Highlighter,
55
- ImagePlus,
56
- Italic,
57
- Link,
58
- Link2Off,
59
- List,
60
- ListChecks,
61
- ListIndentDecrease,
62
- ListIndentIncrease,
63
- ListOrdered,
64
- ListTree,
65
- Maximize2,
66
- MessageSquarePlus,
67
- MessagesSquare,
68
- Minus,
69
- MoveHorizontal,
70
- PanelTop,
71
- Pencil,
72
- Plus,
73
- Redo2,
74
- RemoveFormatting,
75
- Reply,
76
- RotateCcw,
77
- Save,
78
- SeparatorHorizontal,
79
- Strikethrough,
80
- Table,
81
- TableCellsMerge,
82
- TableColumnsSplit,
83
- TableOfContents,
84
- TableRowsSplit,
85
- Trash2,
86
- Underline,
87
- Undo2,
88
- WifiOff,
89
- X
90
- } from "lucide-react";
91
- import { Fragment as Fragment2, useEffect, useRef as useRef2, useState as useState2 } from "react";
92
- import { createPortal } from "react-dom";
93
-
94
- // src/viewers/DocxView.tsx
95
- import { useLayoutEffect, useRef, useState } from "react";
96
-
97
- // src/viewers/headings.ts
98
- var last = 0;
99
- function collect(root, selector, level = (el) => Number(el.tagName.slice(1))) {
100
- return [...root.querySelectorAll(selector)].flatMap((el) => {
101
- const text = el.textContent?.trim() ?? "";
102
- if (!text) return [];
103
- if (!el.id) el.id = `w100n-docs-h${++last}`;
104
- return [{ id: el.id, level: Math.min(3, Math.max(1, level(el) || 1)), text }];
105
- });
106
- }
107
-
108
- // src/viewers/DocxView.tsx
109
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
110
- function DocxView({ bytes, onOutline, failed }) {
111
- const box = useRef(null);
112
- const [error, setError] = useState(false);
113
- useLayoutEffect(() => {
114
- let live = true;
115
- let width = 0;
116
- const root = box.current;
117
- const fit = () => {
118
- const wrapper = root.querySelector(".w100n-docx-wrapper");
119
- if (wrapper && width) wrapper.style.zoom = String(Math.min(1, root.clientWidth / width));
120
- };
121
- const watch = new ResizeObserver(fit);
122
- root.replaceChildren();
123
- setError(false);
124
- import("docx-preview").then(({ renderAsync }) => renderAsync(bytes, root, void 0, { inWrapper: true, className: "w100n-docx", ignoreLastRenderedPageBreak: true, renderAltChunks: false })).then(() => {
125
- if (!live) return;
126
- width = Math.max(0, ...[...root.querySelectorAll("section.w100n-docx")].map((page) => page.offsetWidth));
127
- fit();
128
- watch.observe(root);
129
- onOutline(collect(root, '[class*="heading"]', (el) => Number(/heading(\d)/i.exec(el.className)?.[1] ?? 1)));
130
- }).catch(() => {
131
- if (live) setError(true);
132
- });
133
- return () => {
134
- live = false;
135
- watch.disconnect();
136
- };
137
- }, [bytes, onOutline]);
138
- return /* @__PURE__ */ jsxs(Fragment, { children: [
139
- error && /* @__PURE__ */ jsx("p", { className: "w100n-docs-note", children: failed }),
140
- /* @__PURE__ */ jsx("div", { ref: box, className: "w100n-docs-docx" })
141
- ] });
142
- }
143
-
144
- // src/editor/TextEditor.tsx
145
- import { Fragment as Fragment3, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
146
- var inks = ["#0f172a", "#475569", "#dc2626", "#ea580c", "#ca8a04", "#16a34a", "#0891b2", "#2563eb", "#7c3aed", "#db2777"];
147
- var glows = ["#fef08a", "#bbf7d0", "#bfdbfe", "#fbcfe8", "#fed7aa", "#ddd6fe"];
148
- var uid = () => Math.random().toString(36).slice(2, 10);
149
- function TextEditor({ kind, bytes, t, language, rights, host, together, documentId, onSaved, onOutline, onPanel, slot, guard }) {
150
- const docx = kind === "docx";
151
- const can = { edit: !!host?.save && !!rights?.edit, comment: !!host?.comment && !!rights && (rights.comment || rights.edit) };
152
- const source = useLoad(async () => {
153
- if (docx) return { ...await readDocx(bytes), md: null };
154
- const md = fromMarkdown(new TextDecoder().decode(bytes));
155
- return { doc: md.doc, md, styles: [], page: a4 };
156
- }, [bytes, kind]);
157
- const live = useLive(together ? host : void 0, documentId, kind, bytes, source.data?.doc, language, can.edit, onSaved);
158
- const shared = live?.ready ? live.wire : null;
159
- if (live?.readonly) can.edit = false;
160
- const baseline = useRef2(null);
161
- const anchors = useRef2(/* @__PURE__ */ new Map());
162
- const scroller = useRef2(null);
163
- const [tab, setTab] = useState2(can.edit ? "home" : can.comment ? "review" : "view");
164
- const [pop, setPop] = useState2(null);
165
- const [zoom, setZoom] = useState2(null);
166
- const [layout, setLayout] = useState2(null);
167
- const [room, setRoom] = useState2(0);
168
- const [saving, setSaving] = useState2("idle");
169
- const [href, setHref] = useState2("");
170
- const [grid, setGrid] = useState2({ rows: 0, cols: 0 });
171
- const [draft, setDraft] = useState2(null);
172
- const [reply, setReply] = useState2(null);
173
- const [active, setActive] = useState2(null);
174
- const [resolved, setResolved] = useState2(false);
175
- const [posting, setPosting] = useState2("idle");
176
- const [added, setAdded] = useState2([]);
177
- const [changed, setChanged] = useState2({});
178
- const [editing, setEditing] = useState2(null);
179
- const [dropping, setDropping] = useState2(null);
180
- const [failed, setFailed] = useState2(false);
181
- useRestore(pop !== null);
182
- useRestore(editing !== null);
183
- useRestore(dropping !== null);
184
- const editor = useEditor({
185
- extensions: kit(kind, t, shared),
186
- content: shared ? null : source.data?.doc ?? null,
187
- editable: can.edit && (!live || !!shared),
188
- immediatelyRender: true,
189
- shouldRerenderOnTransaction: false,
190
- editorProps: { attributes: { class: "w100n-docs-prose w100n-text-body", "aria-label": t.documentText } },
191
- onCreate: ({ editor: made }) => {
192
- if (source.data && !live) baseline.current = { bytes, doc: made.state.doc, base: source.data.md && track(source.data.md, made.state.doc) };
193
- }
194
- }, [source.data, can.edit, shared]);
195
- const state = useEditorState({
196
- editor,
197
- selector: ({ editor: e }) => {
198
- if (!e) return null;
199
- const { $from, empty } = e.state.selection;
200
- const block = $from.parent;
201
- return {
202
- style: e.isActive("codeBlock") ? "code" : block.type.name === "heading" ? `h${block.attrs.level}` : e.isActive("blockquote") ? "quote" : "normal",
203
- bold: e.isActive("bold"),
204
- italic: e.isActive("italic"),
205
- underline: e.isActive("underline"),
206
- strike: e.isActive("strike"),
207
- code: e.isActive("code"),
208
- bullets: e.isActive("bulletList"),
209
- numbers: e.isActive("orderedList"),
210
- tasks: e.isActive("taskList"),
211
- align: block.attrs.align ?? "left",
212
- table: e.isActive("table"),
213
- link: e.isActive("link"),
214
- color: e.getAttributes("textStyle").color ?? null,
215
- glow: e.isActive("highlight"),
216
- undo: e.can().undo(),
217
- redo: e.can().redo(),
218
- empty,
219
- toc: (() => {
220
- let at = -1;
221
- e.state.doc.forEach((node, pos) => {
222
- if (at < 0 && node.type.name === "toc") at = pos;
223
- });
224
- return at;
225
- })(),
226
- suggest: !!suggesting.getState(e.state)?.on,
227
- revs: docx ? changes(e.state.doc) : [],
228
- dirty: !!baseline.current && !e.state.doc.eq(baseline.current.doc)
229
- };
230
- }
231
- });
232
- const page = layout ?? source.data?.page ?? a4;
233
- useEffect(() => {
234
- if (editor && docx && source.data) editor.view.dispatch(editor.state.tr.setMeta(paging, { page }));
235
- }, [editor, source.data, page]);
236
- const loaded = useLoad((signal) => host?.comments ? host.comments(documentId, { signal }) : Promise.resolve([]), [host, documentId]);
237
- const all = [...loaded.data ?? [], ...added].flatMap((c) => c.id in changed ? changed[c.id] ?? [] : [c]).filter((c) => !c.replyTo || changed[c.replyTo] !== null);
238
- const open = all.filter((c) => !c.replyTo && !c.resolved);
239
- const threads = all.filter((c) => !c.replyTo && (resolved || !c.resolved)).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
240
- const openIds = open.map((c) => c.id).join();
241
- useEffect(() => {
242
- if (!editor || !source.data) return;
243
- const have = new Map(notes.getState(editor.state).spots.map((s) => [s.id, s]));
244
- const fresh = open.filter((c) => !have.has(c.id));
245
- const found = locate(editor.state.doc, fresh.map((c) => ({ id: c.id, quote: c.quote, anchor: anchors.current.get(c.id) })));
246
- editor.view.dispatch(editor.state.tr.setMeta(notes, { spots: [...open.flatMap((c) => have.get(c.id) ?? []), ...found] }));
247
- }, [editor, source.data, openIds]);
248
- useEffect(() => {
249
- if (editor && source.data) editor.view.dispatch(editor.state.tr.setMeta(notes, { active }));
250
- }, [editor, source.data, active]);
251
- useEffect(() => {
252
- if (!editor || !source.data) return;
253
- let timer = 0;
254
- let last2 = "";
255
- const run = () => {
256
- const list = [];
257
- editor.state.doc.forEach((node, offset) => {
258
- if (node.type.name !== "heading" || !node.textContent.trim()) return;
259
- list.push({ id: `h${offset}`, level: Math.min(3, node.attrs.level), text: node.textContent.trim(), go: () => {
260
- editor.view.nodeDOM(offset)?.scrollIntoView({ behavior: "smooth", block: "start" });
261
- editor.commands.setTextSelection(offset + 1);
262
- } });
263
- });
264
- const key = JSON.stringify(list.map((h) => [h.id, h.level, h.text]));
265
- if (key === last2) return;
266
- last2 = key;
267
- onOutline(list);
268
- };
269
- const later = () => {
270
- clearTimeout(timer);
271
- timer = window.setTimeout(run, 300);
272
- };
273
- run();
274
- editor.on("update", later);
275
- return () => {
276
- clearTimeout(timer);
277
- editor.off("update", later);
278
- };
279
- }, [editor, source.data, onOutline]);
280
- const dirty = live ? live.pending : !!state?.dirty;
281
- const unsent = live ? live.unsynced && can.edit : dirty;
282
- useEffect(() => {
283
- if (!guard || live || !dirty || !source.data || !editor) return;
284
- guard.current = save;
285
- return () => {
286
- guard.current = null;
287
- };
288
- });
289
- useEffect(() => {
290
- if (!unsent) return;
291
- const stop = (e) => e.preventDefault();
292
- window.addEventListener("beforeunload", stop);
293
- return () => window.removeEventListener("beforeunload", stop);
294
- }, [unsent]);
295
- useEffect(() => {
296
- const el = scroller.current;
297
- if (!el || typeof ResizeObserver === "undefined") return;
298
- const watch = new ResizeObserver(() => setRoom(el.clientWidth));
299
- watch.observe(el);
300
- return () => watch.disconnect();
301
- }, [editor, source.data]);
302
- useEffect(() => {
303
- if (!pop) return;
304
- const close = (e) => {
305
- if (!e.target.closest(".w100n-text-pop, [data-pop]")) setPop(null);
306
- };
307
- document.addEventListener("pointerdown", close);
308
- return () => document.removeEventListener("pointerdown", close);
309
- }, [pop]);
310
- if (source.error) return docx ? /* @__PURE__ */ jsx2("div", { className: "w100n-docs-scroll", children: /* @__PURE__ */ jsx2(DocxView, { bytes, onOutline, failed: t.failed }) }) : /* @__PURE__ */ jsx2("div", { className: "w100n-docs-empty", children: t.failed });
311
- if (!source.data || !editor || !state || live && !live.ready && !live.late && !live.denied) return /* @__PURE__ */ jsx2("div", { className: "w100n-docs-empty", children: t.loading });
312
- const save = async () => {
313
- if (live) {
314
- if (!can.edit || !live.ready || !live.online || !live.pending || saving === "busy") return false;
315
- setSaving("busy");
316
- const ok = await live.save();
317
- setSaving(ok ? "done" : "failed");
318
- return ok;
319
- }
320
- const was = baseline.current;
321
- const doc = editor.state.doc;
322
- if (!can.edit || !was || saving === "busy" || doc.eq(was.doc)) return false;
323
- setSaving("busy");
324
- try {
325
- const md = was.base && toMarkdown(doc, was.base);
326
- const out = md ? new TextEncoder().encode(md.text).buffer : await writeDocx(was.bytes, editor.getJSON());
327
- await host.save(documentId, out, t.editedNote);
328
- baseline.current = { bytes: md ? out : was.bytes, doc, base: md ? md.base : null };
329
- if (!md && editor.state.doc === doc) {
330
- const fresh = (await readDocx(out)).doc;
331
- if (editor.state.doc === doc && renew(fresh)) baseline.current = { bytes: out, doc: editor.state.doc, base: null };
332
- }
333
- setSaving("done");
334
- onSaved?.();
335
- return true;
336
- } catch {
337
- setSaving("failed");
338
- return false;
339
- }
340
- };
341
- const renew = (fresh) => {
342
- const at = [];
343
- const refs = [];
344
- editor.state.doc.descendants((node, pos) => {
345
- if (node.isBlock) at.push(pos);
346
- });
347
- editor.schema.nodeFromJSON(fresh).descendants((node) => {
348
- if (node.isBlock) refs.push(node.type.name, node.attrs.ref);
349
- });
350
- if (refs.length !== at.length * 2 || at.some((pos, i) => editor.state.doc.nodeAt(pos).type.name !== refs[i * 2])) return false;
351
- const tr = editor.state.tr.setMeta("addToHistory", false);
352
- at.forEach((pos, i) => {
353
- if (tr.doc.nodeAt(pos).attrs.ref !== refs[i * 2 + 1]) tr.setNodeAttribute(pos, "ref", refs[i * 2 + 1]);
354
- });
355
- editor.view.dispatch(tr);
356
- return true;
357
- };
358
- const linkUp = () => {
359
- setHref(editor.getAttributes("link").href ?? "");
360
- setTab("insert");
361
- setPop("link");
362
- };
363
- const putLink = () => {
364
- const value = href.trim();
365
- const chain = editor.chain().focus();
366
- if (!value) chain.extendMarkRange("link").unsetLink().run();
367
- else if (editor.state.selection.empty && !editor.isActive("link")) chain.insertContent({ type: "text", text: value, marks: [{ type: "link", attrs: { href: value } }] }).run();
368
- else chain.extendMarkRange("link").setLink({ href: value }).run();
369
- setPop(null);
370
- };
371
- const begin = () => {
372
- const { from, to, empty } = editor.state.selection;
373
- if (empty || !can.comment) return;
374
- editor.view.dispatch(editor.state.tr.setMeta(notes, { pending: { from, to } }));
375
- setDraft({ quote: editor.state.doc.textBetween(from, to, "\n", " "), body: "" });
376
- setPosting("idle");
377
- onPanel?.("comments");
378
- };
379
- const drop = () => {
380
- editor.view.dispatch(editor.state.tr.setMeta(notes, { pending: null }));
381
- setDraft(null);
382
- };
383
- const post = async () => {
384
- if (!draft?.body.trim() || !host?.comment || posting === "busy") return;
385
- setPosting("busy");
386
- try {
387
- const anchor = uid();
388
- const made = await host.comment(documentId, { body: draft.body.trim(), quote: draft.quote, replyTo: null, anchor });
389
- anchors.current.set(made.id, anchor);
390
- const range = notes.getState(editor.state).pending;
391
- editor.view.dispatch(editor.state.tr.setMeta(notes, { pending: null, spots: [...notes.getState(editor.state).spots, ...range ? [{ id: made.id, ...range }] : []] }));
392
- setAdded((list) => [...list, made]);
393
- setDraft(null);
394
- setActive(made.id);
395
- setPosting("idle");
396
- } catch {
397
- setPosting("failed");
398
- }
399
- };
400
- const answer = async (parent) => {
401
- if (!reply?.body.trim() || !host?.comment || posting === "busy") return;
402
- setPosting("busy");
403
- try {
404
- const made = await host.comment(documentId, { body: reply.body.trim(), quote: parent.quote, replyTo: parent.id, anchor: anchors.current.get(parent.id) ?? "" });
405
- setAdded((list) => [...list, made]);
406
- setReply(null);
407
- setPosting("idle");
408
- } catch {
409
- setPosting("failed");
410
- }
411
- };
412
- const settle = async (c) => {
413
- try {
414
- await host.resolveComment(documentId, c.id, !c.resolved);
415
- setChanged((list) => ({ ...list, [c.id]: { ...c, resolved: !c.resolved } }));
416
- } catch {
417
- setPosting("failed");
418
- }
419
- };
420
- const change = async (id, body) => {
421
- if (posting === "busy") return;
422
- setPosting("busy");
423
- setFailed(false);
424
- try {
425
- let made = null;
426
- if (body === null) await host.deleteComment(documentId, id);
427
- else made = await host.editComment(documentId, id, body);
428
- setChanged((list) => ({ ...list, [id]: made }));
429
- setEditing(null);
430
- setDropping(null);
431
- if (body === null && active === id) setActive(null);
432
- } catch {
433
- setFailed(true);
434
- }
435
- setPosting("idle");
436
- };
437
- const mine = (c) => c.author.id === host?.me.id;
438
- const drops = (c) => mine(c) || c.author.id.startsWith("guest:") && !!rights?.edit;
439
- const show = (c) => {
440
- setActive(c.id);
441
- const spot = notes.getState(editor.state).spots.find((s) => s.id === c.id);
442
- if (!spot) return;
443
- const at = editor.view.domAtPos(spot.from).node;
444
- (at instanceof HTMLElement ? at : at.parentElement)?.scrollIntoView({ behavior: "smooth", block: "center" });
445
- };
446
- const suggested = [...state.revs.reduce((all2, c) => {
447
- const key = `${c.kind}:${c.id}`;
448
- const was = all2.get(key);
449
- all2.set(key, was ? { ...was, text: `${was.text} ${c.text}` } : c);
450
- return all2;
451
- }, /* @__PURE__ */ new Map()).values()];
452
- const jump = (at) => {
453
- const dom = editor.view.domAtPos(at).node;
454
- (dom instanceof HTMLElement ? dom : dom.parentElement)?.scrollIntoView({ behavior: "smooth", block: "center" });
455
- };
456
- const percent = zoom ?? (docx && room ? Math.max(10, Math.min(100, Math.floor((room - 24) / page.width * 100))) : 100);
457
- const zoomBy = (d) => setZoom(Math.min(200, Math.max(30, percent + d)));
458
- const keys = (e) => {
459
- if (e.defaultPrevented) return;
460
- const key = e.key.toLowerCase();
461
- const mod = e.metaKey || e.ctrlKey;
462
- if (key === "escape" && (pop || draft)) {
463
- e.stopPropagation();
464
- if (pop) setPop(null);
465
- else drop();
466
- return;
467
- }
468
- if (!mod) return;
469
- const act = (run) => {
470
- e.preventDefault();
471
- run();
472
- };
473
- if (key === "s" && can.edit) act(() => void save());
474
- else if (key === "k" && can.edit) act(linkUp);
475
- else if (key === "m" && e.altKey && can.comment) act(begin);
476
- else if (!can.edit || e.altKey) return;
477
- else if (key === "]" || key === "[") act(() => indent(editor, key === "]" ? 1 : -1));
478
- else if (key === "\\") act(() => clear(editor));
479
- else if (key === "enter" && docx) act(() => editor.chain().focus().insertContent({ type: "pageBreak" }).run());
480
- else if (e.shiftKey && docx && ["l", "e", "r", "j"].includes(key)) act(() => align(editor, { l: "left", e: "center", r: "right", j: "justify" }[key]));
481
- };
482
- const tool = (icon, label, run, pressed, disabled) => {
483
- const Icon = icon;
484
- return /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": label, title: label, "aria-pressed": pressed, disabled, onClick: () => {
485
- run();
486
- }, children: /* @__PURE__ */ jsx2(Icon, { "aria-hidden": true }) });
487
- };
488
- const gap = /* @__PURE__ */ jsx2("span", { className: "w100n-pdf-gap" });
489
- const known = source.data.styles;
490
- const named = (test) => {
491
- const name = known.find(test)?.name;
492
- return name && `${name[0].toUpperCase()}${name.slice(1)}`;
493
- };
494
- const levels = docx ? [1, 2, 3, 4, 5, 6].filter((level) => level <= 4 || named((s) => s.heading === level)) : [1, 2, 3, 4];
495
- const styles = [
496
- ["normal", t.normal, () => editor.chain().focus().clearNodes().setParagraph().run()],
497
- ...levels.map((level) => [
498
- `h${level}`,
499
- named((s) => s.heading === level && !/^title$/i.test(s.name)) || `${t.heading} ${level}`,
500
- () => editor.chain().focus().setHeading({ level }).run()
501
- ]),
502
- ["quote", named((s) => /quote/i.test(s.name)) || t.quote, () => editor.chain().focus().clearNodes().setParagraph().setBlockquote().run()],
503
- ["code", t.code, () => editor.chain().focus().setCodeBlock().run()]
504
- ];
505
- const tabs = [
506
- ...can.edit ? [["home", t.home], ["insert", t.insert]] : [],
507
- ...can.edit && docx ? [["layout", t.layout]] : [],
508
- ...can.edit ? [["refs", t.references]] : [],
509
- ...host?.comments && (can.comment || open.length > 0) ? [["review", t.review]] : [],
510
- ["view", t.viewTab]
511
- ];
512
- const shown = tabs.some(([key]) => key === tab) ? tab : "view";
513
- const margins = [[t.marginsNormal, [96, 96, 96, 96]], [t.marginsNarrow, [48, 48, 48, 48]], [t.marginsWide, [96, 192, 96, 192]]];
514
- const when = (iso) => new Intl.DateTimeFormat(language, { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));
515
- const person = (c) => /* @__PURE__ */ jsxs2("header", { children: [
516
- c.author.avatarUrl ? /* @__PURE__ */ jsx2("img", { className: "w100n-text-face", src: c.author.avatarUrl, alt: "" }) : /* @__PURE__ */ jsx2("span", { className: "w100n-text-face", "aria-hidden": true, children: c.author.name.slice(0, 1).toUpperCase() }),
517
- /* @__PURE__ */ jsxs2("span", { className: "w100n-text-who", children: [
518
- /* @__PURE__ */ jsxs2("b", { children: [
519
- c.author.name,
520
- c.author.id.startsWith("guest:") && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.guest })
521
- ] }),
522
- /* @__PURE__ */ jsx2("small", { children: when(c.createdAt) })
523
- ] }),
524
- drops(c) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-own", onClick: (e) => e.stopPropagation(), children: [
525
- mine(c) && host.editComment && /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": t.editComment, title: t.editComment, onClick: () => {
526
- setDropping(null);
527
- setEditing({ id: c.id, body: c.body });
528
- }, children: /* @__PURE__ */ jsx2(Pencil, { "aria-hidden": true }) }),
529
- host.deleteComment && /* @__PURE__ */ jsx2("button", { type: "button", "data-tone": "red", "aria-label": t.delete, title: t.delete, onClick: () => {
530
- setEditing(null);
531
- setDropping(c.id);
532
- }, children: /* @__PURE__ */ jsx2(Trash2, { "aria-hidden": true }) })
533
- ] })
534
- ] });
535
- const words = (c) => editing?.id === c.id ? /* @__PURE__ */ jsxs2("div", { className: "w100n-text-reply w100n-text-edit", onClick: (e) => e.stopPropagation(), children: [
536
- /* @__PURE__ */ jsx2(
537
- "textarea",
538
- {
539
- autoFocus: true,
540
- value: editing.body,
541
- "aria-label": t.editComment,
542
- onChange: (e) => setEditing({ ...editing, body: e.target.value }),
543
- onKeyDown: (e) => {
544
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && editing.body.trim()) void change(c.id, editing.body.trim());
545
- }
546
- }
547
- ),
548
- /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
549
- /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setEditing(null), children: t.cancel }),
550
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !editing.body.trim() || editing.body.trim() === c.body || posting === "busy", onClick: () => void change(c.id, editing.body.trim()), children: [
551
- /* @__PURE__ */ jsx2(Save, { "aria-hidden": true }),
552
- t.save
553
- ] })
554
- ] })
555
- ] }) : dropping === c.id ? /* @__PURE__ */ jsxs2("div", { className: "w100n-text-confirm", role: "group", "aria-label": t.delete, onClick: (e) => e.stopPropagation(), children: [
556
- /* @__PURE__ */ jsx2("p", { children: c.replyTo || !all.some((r) => r.replyTo === c.id) ? t.deleteAsk : t.deleteThread }),
557
- /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
558
- /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", autoFocus: true, onClick: () => setDropping(null), children: t.cancel }),
559
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", disabled: posting === "busy", onClick: () => void change(c.id, null), children: [
560
- /* @__PURE__ */ jsx2(Trash2, { "aria-hidden": true }),
561
- t.delete
562
- ] })
563
- ] })
564
- ] }) : /* @__PURE__ */ jsx2("p", { children: c.body });
565
- const pick = (next) => setPop(pop === next ? null : next);
566
- const swatches = (list, current, set, none) => /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-swatches", role: "group", children: [
567
- /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-text-none", onClick: () => {
568
- set(null);
569
- setPop(null);
570
- }, children: none }),
571
- list.map((c, i) => /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": c, "aria-pressed": current === c, onClick: () => {
572
- set(c);
573
- setPop(null);
574
- }, children: /* @__PURE__ */ jsx2("i", { style: { background: c } }) }, i))
575
- ] });
576
- let tools = null;
577
- if (shown === "home") tools = /* @__PURE__ */ jsxs2(Fragment3, { children: [
578
- /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
579
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-picker", "data-pop": true, "aria-haspopup": "listbox", "aria-expanded": pop === "style", "aria-label": t.paragraphStyle, title: t.paragraphStyle, onClick: () => pick("style"), children: [
580
- styles.find(([key]) => key === state.style)?.[1] ?? t.normal,
581
- /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
582
- ] }),
583
- pop === "style" && /* @__PURE__ */ jsx2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.paragraphStyle, children: styles.map(([key, label, run]) => /* @__PURE__ */ jsx2(
584
- "button",
585
- {
586
- type: "button",
587
- role: "option",
588
- "data-style": key,
589
- "aria-selected": state.style === key,
590
- onClick: () => {
591
- run();
592
- setPop(null);
593
- },
594
- children: label
595
- },
596
- key
597
- )) })
598
- ] }),
599
- gap,
600
- tool(Bold, t.bold, () => editor.chain().focus().toggleBold().run(), state.bold),
601
- tool(Italic, t.italic, () => editor.chain().focus().toggleItalic().run(), state.italic),
602
- docx && tool(Underline, t.underline, () => editor.chain().focus().toggleUnderline().run(), state.underline),
603
- tool(Strikethrough, t.strike, () => editor.chain().focus().toggleStrike().run(), state.strike),
604
- !docx && tool(Code, t.code, () => editor.chain().focus().toggleCode().run(), state.code),
605
- docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
606
- /* @__PURE__ */ jsxs2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "color", "aria-label": t.textColour, title: t.textColour, onClick: () => pick("color"), children: [
607
- /* @__PURE__ */ jsx2(Baseline, { "aria-hidden": true }),
608
- /* @__PURE__ */ jsx2("i", { className: "w100n-text-bar", style: { background: state.color ?? "currentColor" } })
609
- ] }),
610
- pop === "color" && swatches(inks, state.color, (c) => c ? editor.chain().focus().setColor(c).run() : editor.chain().focus().unsetColor().run(), t.automatic)
611
- ] }),
612
- docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
613
- /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "mark", "aria-pressed": state.glow, "aria-label": t.highlight, title: t.highlight, onClick: () => pick("mark"), children: /* @__PURE__ */ jsx2(Highlighter, { "aria-hidden": true }) }),
614
- pop === "mark" && swatches(glows, null, (c) => c ? editor.chain().focus().setHighlight({ color: c }).run() : editor.chain().focus().unsetHighlight().run(), t.noColour)
615
- ] }),
616
- gap,
617
- tool(List, t.bullets, () => editor.chain().focus().toggleBulletList().run(), state.bullets),
618
- tool(ListOrdered, t.numbers, () => editor.chain().focus().toggleOrderedList().run(), state.numbers),
619
- tool(ListChecks, t.tasks, () => editor.chain().focus().toggleTaskList().run(), state.tasks),
620
- docx && /* @__PURE__ */ jsxs2(Fragment3, { children: [
621
- gap,
622
- [[AlignLeft, t.alignLeft, "left"], [AlignCenter, t.alignCenter, "center"], [AlignRight, t.alignRight, "right"], [AlignJustify, t.justify, "justify"]].map(([icon, label, value], i) => /* @__PURE__ */ jsx2(Fragment2, { children: tool(icon, label, () => align(editor, value), state.align === value) }, i))
623
- ] }),
624
- gap,
625
- tool(ListIndentDecrease, t.outdent, () => indent(editor, -1)),
626
- tool(ListIndentIncrease, t.indent, () => indent(editor, 1)),
627
- tool(RemoveFormatting, t.clearFormat, () => clear(editor))
628
- ] });
629
- else if (shown === "insert") tools = /* @__PURE__ */ jsxs2(Fragment3, { children: [
630
- /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
631
- /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "table", "aria-label": t.table, title: t.table, onClick: () => pick("table"), children: /* @__PURE__ */ jsx2(Table, { "aria-hidden": true }) }),
632
- pop === "table" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-grid", role: "group", "aria-label": t.table, onPointerLeave: () => setGrid({ rows: 0, cols: 0 }), children: [
633
- /* @__PURE__ */ jsx2("span", { children: Array.from({ length: 48 }, (_, i) => {
634
- const rows = Math.floor(i / 8) + 1;
635
- const cols = i % 8 + 1;
636
- return /* @__PURE__ */ jsx2(
637
- "button",
638
- {
639
- type: "button",
640
- "aria-label": `${rows} \xD7 ${cols}`,
641
- "data-on": rows <= grid.rows && cols <= grid.cols,
642
- onPointerEnter: () => setGrid({ rows, cols }),
643
- onClick: () => {
644
- editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
645
- setPop(null);
646
- }
647
- },
648
- i
649
- );
650
- }) }),
651
- /* @__PURE__ */ jsx2("small", { children: grid.rows ? `${grid.rows} \xD7 ${grid.cols}` : t.table })
652
- ] })
653
- ] }),
654
- state.table && /* @__PURE__ */ jsxs2(Fragment3, { children: [
655
- tool(BetweenHorizontalStart, t.rowAbove, () => editor.chain().focus().addRowBefore().run()),
656
- tool(BetweenHorizontalEnd, t.rowBelow, () => editor.chain().focus().addRowAfter().run()),
657
- tool(BetweenVerticalStart, t.columnLeft, () => editor.chain().focus().addColumnBefore().run()),
658
- tool(BetweenVerticalEnd, t.columnRight, () => editor.chain().focus().addColumnAfter().run()),
659
- tool(PanelTop, t.headerRow, () => editor.chain().focus().toggleHeaderRow().run()),
660
- docx && tool(TableCellsMerge, t.mergeCells, () => editor.chain().focus().mergeOrSplit().run()),
661
- tool(TableRowsSplit, t.deleteRow, () => editor.chain().focus().deleteRow().run()),
662
- tool(TableColumnsSplit, t.deleteColumn, () => editor.chain().focus().deleteColumn().run()),
663
- tool(Trash2, t.deleteTable, () => editor.chain().focus().deleteTable().run())
664
- ] }),
665
- gap,
666
- /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
667
- /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "image", "aria-label": t.image, title: t.image, onClick: () => {
668
- setHref("");
669
- pick("image");
670
- }, children: /* @__PURE__ */ jsx2(ImagePlus, { "aria-hidden": true }) }),
671
- pop === "image" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-form", children: [
672
- /* @__PURE__ */ jsx2(
673
- "input",
674
- {
675
- autoFocus: true,
676
- value: href,
677
- placeholder: t.imageLink,
678
- "aria-label": t.imageLink,
679
- onChange: (e) => setHref(e.target.value),
680
- onKeyDown: (e) => {
681
- if (e.key === "Enter" && href.trim()) {
682
- editor.chain().focus().setImage({ src: href.trim() }).run();
683
- setPop(null);
684
- }
685
- }
686
- }
687
- ),
688
- /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
689
- docx && /* @__PURE__ */ jsxs2("label", { className: "w100n-pdf-link", children: [
690
- t.chooseFile,
691
- /* @__PURE__ */ jsx2("input", { type: "file", accept: "image/png,image/jpeg,image/gif", hidden: true, onChange: (e) => {
692
- const file = e.target.files?.[0];
693
- if (!file) return;
694
- const reader = new FileReader();
695
- reader.onload = () => {
696
- editor.chain().focus().setImage({ src: String(reader.result), alt: file.name }).run();
697
- setPop(null);
698
- };
699
- reader.readAsDataURL(file);
700
- } })
701
- ] }),
702
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !href.trim(), onClick: () => {
703
- editor.chain().focus().setImage({ src: href.trim() }).run();
704
- setPop(null);
705
- }, children: [
706
- /* @__PURE__ */ jsx2(ImagePlus, { "aria-hidden": true }),
707
- t.put
708
- ] })
709
- ] })
710
- ] })
711
- ] }),
712
- /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
713
- /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "link", "aria-pressed": state.link, "aria-label": t.link, title: t.link, onClick: () => pop === "link" ? setPop(null) : linkUp(), children: /* @__PURE__ */ jsx2(Link, { "aria-hidden": true }) }),
714
- pop === "link" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-form", children: [
715
- /* @__PURE__ */ jsx2("input", { autoFocus: true, value: href, placeholder: t.linkAddress, "aria-label": t.linkAddress, onChange: (e) => setHref(e.target.value), onKeyDown: (e) => {
716
- if (e.key === "Enter") putLink();
717
- } }),
718
- /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
719
- state.link && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-pdf-link", onClick: () => {
720
- editor.chain().focus().extendMarkRange("link").unsetLink().run();
721
- setPop(null);
722
- }, children: [
723
- /* @__PURE__ */ jsx2(Link2Off, { "aria-hidden": true }),
724
- t.unlink
725
- ] }),
726
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !href.trim(), onClick: putLink, children: [
727
- /* @__PURE__ */ jsx2(Link, { "aria-hidden": true }),
728
- t.apply
729
- ] })
730
- ] })
731
- ] })
732
- ] }),
733
- docx && tool(SeparatorHorizontal, t.pageBreak, () => editor.chain().focus().insertContent({ type: "pageBreak" }).run()),
734
- tool(Minus, t.rule, () => editor.chain().focus().setHorizontalRule().run())
735
- ] });
736
- else if (shown === "layout") tools = /* @__PURE__ */ jsxs2(Fragment3, { children: [
737
- /* @__PURE__ */ jsx2("span", { className: "w100n-text-label", children: t.pageSize }),
738
- [["A4", a4], ["Letter", letter]].map(([label, size], i) => /* @__PURE__ */ jsx2(
739
- "button",
740
- {
741
- type: "button",
742
- className: "w100n-text-word",
743
- "aria-pressed": Math.abs(page.width - size.width) < 2 && Math.abs(page.height - size.height) < 2,
744
- onClick: () => setLayout({ ...page, width: size.width, height: size.height }),
745
- children: label
746
- },
747
- i
748
- )),
749
- gap,
750
- /* @__PURE__ */ jsx2("span", { className: "w100n-text-label", children: t.margins }),
751
- margins.map(([label, values], i) => /* @__PURE__ */ jsx2(
752
- "button",
753
- {
754
- type: "button",
755
- className: "w100n-text-word",
756
- "aria-pressed": values.every((v, n) => Math.abs(page.margins[n] - v) < 2),
757
- onClick: () => setLayout({ ...page, margins: values }),
758
- children: label
759
- },
760
- i
761
- ))
762
- ] });
763
- else if (shown === "refs") tools = /* @__PURE__ */ jsxs2(Fragment3, { children: [
764
- state.toc < 0 ? /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", onClick: () => {
765
- const at = editor.state.selection.$from.before(1);
766
- editor.chain().focus().insertContentAt(at, { type: "toc", attrs: { entries: JSON.stringify(entries(editor.state.doc, !docx, at - 1)) } }).run();
767
- }, children: [
768
- /* @__PURE__ */ jsx2(TableOfContents, { "aria-hidden": true }),
769
- t.tocInsert
770
- ] }) : /* @__PURE__ */ jsxs2(Fragment3, { children: [
771
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => editor.view.nodeDOM(state.toc)?.scrollIntoView({ behavior: "smooth", block: "start" }), children: [
772
- /* @__PURE__ */ jsx2(TableOfContents, { "aria-hidden": true }),
773
- t.tocGo
774
- ] }),
775
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", onClick: () => editor.chain().focus().deleteRange({ from: state.toc, to: state.toc + editor.state.doc.nodeAt(state.toc).nodeSize }).run(), children: [
776
- /* @__PURE__ */ jsx2(Trash2, { "aria-hidden": true }),
777
- t.tocRemove
778
- ] })
779
- ] }),
780
- /* @__PURE__ */ jsx2("span", { className: "w100n-text-label", children: t.tocHint })
781
- ] });
782
- else if (shown === "review") tools = /* @__PURE__ */ jsxs2(Fragment3, { children: [
783
- can.comment && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: state.empty, onClick: begin, children: [
784
- /* @__PURE__ */ jsx2(MessageSquarePlus, { "aria-hidden": true }),
785
- t.addComment
786
- ] }),
787
- docx && can.edit && /* @__PURE__ */ jsxs2(Fragment3, { children: [
788
- /* @__PURE__ */ jsxs2(
789
- "button",
790
- {
791
- type: "button",
792
- className: "w100n-text-word",
793
- "aria-pressed": state.suggest,
794
- onClick: () => editor.view.dispatch(editor.state.tr.setMeta(suggesting, { on: !state.suggest, me: host?.me.name ?? "" })),
795
- children: [
796
- /* @__PURE__ */ jsx2(FilePenLine, { "aria-hidden": true }),
797
- t.suggest
798
- ]
799
- }
800
- ),
801
- state.revs.length > 0 && /* @__PURE__ */ jsxs2(Fragment3, { children: [
802
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "green", onClick: () => decide(editor, true), children: [
803
- /* @__PURE__ */ jsx2(CheckCheck, { "aria-hidden": true }),
804
- t.acceptAll
805
- ] }),
806
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", onClick: () => decide(editor, false), children: [
807
- /* @__PURE__ */ jsx2(X, { "aria-hidden": true }),
808
- t.rejectAll
809
- ] })
810
- ] })
811
- ] }),
812
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => onPanel?.("comments"), children: [
813
- /* @__PURE__ */ jsx2(MessagesSquare, { "aria-hidden": true }),
814
- t.comments,
815
- /* @__PURE__ */ jsx2("b", { children: open.length })
816
- ] })
817
- ] });
818
- else tools = /* @__PURE__ */ jsxs2(Fragment3, { children: [
819
- tool(Minus, t.zoomOut, () => zoomBy(-10)),
820
- /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-zoom", children: [
821
- percent,
822
- "%"
823
- ] }),
824
- tool(Plus, t.zoomIn, () => zoomBy(10)),
825
- docx && tool(MoveHorizontal, t.fitWidth, () => setZoom(null), zoom === null),
826
- tool(Maximize2, t.fit, () => setZoom(100)),
827
- gap,
828
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => onPanel?.("outline"), children: [
829
- /* @__PURE__ */ jsx2(ListTree, { "aria-hidden": true }),
830
- t.outline
831
- ] })
832
- ] });
833
- const sheet = docx ? { "--page-w": `${page.width}px`, "--page-h": `${page.height}px`, "--mt": `${page.margins[0]}px`, "--mr": `${page.margins[1]}px`, "--mb": `${page.margins[2]}px`, "--ml": `${page.margins[3]}px` } : void 0;
834
- return /* @__PURE__ */ jsxs2("div", { className: "w100n-text", "data-kind": kind, onKeyDown: keys, children: [
835
- /* @__PURE__ */ jsxs2("div", { className: "w100n-text-ribbon", role: "toolbar", "aria-label": t.textTools, onKeyDown: rove, onPointerDown: (e) => {
836
- if (e.target.closest("button")) e.preventDefault();
837
- }, children: [
838
- (tabs.length > 1 || live) && /* @__PURE__ */ jsxs2("div", { className: "w100n-text-tabs", children: [
839
- /* @__PURE__ */ jsx2("span", { role: "tablist", "aria-label": t.textTools, children: tabs.length > 1 && tabs.map(([key, label]) => /* @__PURE__ */ jsx2("button", { type: "button", role: "tab", "aria-selected": shown === key, onClick: () => {
840
- setTab(key);
841
- setPop(null);
842
- }, children: label }, key)) }),
843
- (can.edit || live) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-end", children: [
844
- live && (live.ready ? !live.online : live.late || live.denied) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-live", title: live.denied ? void 0 : t.offlineHint, children: [
845
- /* @__PURE__ */ jsx2(WifiOff, { "aria-hidden": true }),
846
- live.denied ? t.liveOff : t.offline
847
- ] }),
848
- live && live.people.length > 0 && /* @__PURE__ */ jsx2("span", { className: "w100n-text-people", role: "group", "aria-label": t.people, children: live.people.map((p) => /* @__PURE__ */ jsx2("span", { className: "w100n-text-face", title: p.me ? `${p.name} (${t.you})` : p.name, style: { "--face": p.color }, children: p.avatarUrl ? /* @__PURE__ */ jsx2("img", { src: p.avatarUrl, alt: "" }) : p.name.slice(0, 1).toUpperCase() }, p.id)) }),
849
- state.suggest && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.suggesting }),
850
- can.edit && /* @__PURE__ */ jsxs2(Fragment3, { children: [
851
- tool(Undo2, t.undo, () => editor.chain().focus().undo().run(), void 0, !state.undo),
852
- tool(Redo2, t.redo, () => editor.chain().focus().redo().run(), void 0, !state.redo),
853
- saving === "failed" && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-muted", children: t.saveFailed }),
854
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !dirty || saving === "busy" || !!live && !live.online, onClick: () => void save(), children: [
855
- /* @__PURE__ */ jsx2(Save, { "aria-hidden": true }),
856
- saving === "busy" ? t.saving : !dirty && saving === "done" ? t.saved : t.save
857
- ] })
858
- ] })
859
- ] })
860
- ] }),
861
- /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": tabs.find(([key]) => key === shown)?.[1], children: tools })
862
- ] }),
863
- /* @__PURE__ */ jsx2("div", { ref: scroller, className: "w100n-text-scroll", onClick: (e) => {
864
- const id = e.target.closest("[data-note]")?.getAttribute("data-note");
865
- if (!id) return;
866
- setActive(id);
867
- onPanel?.("comments");
868
- }, children: /* @__PURE__ */ jsx2("div", { className: "w100n-text-zoomed", style: { zoom: percent / 100 }, children: /* @__PURE__ */ jsx2("div", { className: docx ? "w100n-text-sheet" : "w100n-docs-page w100n-text-column", style: sheet, children: /* @__PURE__ */ jsx2(EditorContent, { editor }) }) }) }),
869
- slot && createPortal(/* @__PURE__ */ jsxs2("div", { className: "w100n-text-notes", children: [
870
- draft && /* @__PURE__ */ jsxs2("div", { className: "w100n-text-card w100n-text-draft", children: [
871
- /* @__PURE__ */ jsx2("blockquote", { children: draft.quote }),
872
- /* @__PURE__ */ jsx2(
873
- "textarea",
874
- {
875
- autoFocus: true,
876
- value: draft.body,
877
- placeholder: t.writeComment,
878
- "aria-label": t.writeComment,
879
- onChange: (e) => setDraft({ ...draft, body: e.target.value }),
880
- onKeyDown: (e) => {
881
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void post();
882
- }
883
- }
884
- ),
885
- /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
886
- /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: drop, children: t.cancel }),
887
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !draft.body.trim() || posting === "busy", onClick: () => void post(), children: [
888
- /* @__PURE__ */ jsx2(MessageSquarePlus, { "aria-hidden": true }),
889
- t.post
890
- ] })
891
- ] })
892
- ] }),
893
- suggested.length > 0 && /* @__PURE__ */ jsxs2(Fragment3, { children: [
894
- /* @__PURE__ */ jsxs2("p", { className: "w100n-docs-caption", children: [
895
- /* @__PURE__ */ jsx2(FilePenLine, { "aria-hidden": true }),
896
- t.suggestions,
897
- /* @__PURE__ */ jsx2("span", { children: suggested.length })
898
- ] }),
899
- suggested.map((c, i) => /* @__PURE__ */ jsxs2("article", { className: "w100n-text-card w100n-text-change", "data-kind": c.kind, onClick: () => jump(c.from), children: [
900
- /* @__PURE__ */ jsxs2("header", { children: [
901
- /* @__PURE__ */ jsx2("span", { className: "w100n-text-face", "aria-hidden": true, children: (c.author || "?").slice(0, 1).toUpperCase() }),
902
- /* @__PURE__ */ jsxs2("span", { className: "w100n-text-who", children: [
903
- /* @__PURE__ */ jsx2("b", { children: c.author || t.someone }),
904
- c.date && /* @__PURE__ */ jsx2("small", { children: when(c.date) })
905
- ] })
906
- ] }),
907
- /* @__PURE__ */ jsxs2("p", { children: [
908
- c.kind === "insertion" ? t.added : t.removed,
909
- " ",
910
- /* @__PURE__ */ jsx2("q", { children: c.text })
911
- ] }),
912
- can.edit && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-actions", onClick: (e) => e.stopPropagation(), children: [
913
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "green", onClick: () => decide(editor, true, c.id), children: [
914
- /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
915
- t.accept
916
- ] }),
917
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", onClick: () => decide(editor, false, c.id), children: [
918
- /* @__PURE__ */ jsx2(X, { "aria-hidden": true }),
919
- t.reject
920
- ] })
921
- ] })
922
- ] }, i))
923
- ] }),
924
- posting === "failed" && /* @__PURE__ */ jsx2("p", { className: "w100n-docs-muted", role: "alert", children: t.commentFailed }),
925
- failed && /* @__PURE__ */ jsx2("p", { className: "w100n-docs-muted", role: "alert", children: t.changeFailed }),
926
- /* @__PURE__ */ jsxs2("div", { className: "w100n-docs-row", children: [
927
- /* @__PURE__ */ jsxs2("p", { className: "w100n-docs-caption", children: [
928
- /* @__PURE__ */ jsx2(MessagesSquare, { "aria-hidden": true }),
929
- t.comments,
930
- /* @__PURE__ */ jsx2("span", { children: open.length })
931
- ] }),
932
- /* @__PURE__ */ jsx2("span", { className: "w100n-docs-chips", children: /* @__PURE__ */ jsx2("button", { type: "button", "aria-pressed": resolved, onClick: () => setResolved(!resolved), children: t.showResolved }) })
933
- ] }),
934
- !threads.length && !draft && /* @__PURE__ */ jsx2("p", { className: "w100n-docs-muted", children: can.comment ? t.commentHint : t.noComments }),
935
- threads.map((c) => /* @__PURE__ */ jsxs2("article", { className: "w100n-text-card", "aria-current": active === c.id, "data-resolved": c.resolved, onClick: () => show(c), children: [
936
- person(c),
937
- c.quote && /* @__PURE__ */ jsx2("blockquote", { children: c.quote }),
938
- words(c),
939
- all.filter((r) => r.replyTo === c.id).map((r) => /* @__PURE__ */ jsxs2("div", { className: "w100n-text-reply", children: [
940
- person(r),
941
- words(r)
942
- ] }, r.id)),
943
- reply?.id === c.id && /* @__PURE__ */ jsxs2("div", { className: "w100n-text-reply", onClick: (e) => e.stopPropagation(), children: [
944
- /* @__PURE__ */ jsx2(
945
- "textarea",
946
- {
947
- autoFocus: true,
948
- value: reply.body,
949
- placeholder: t.writeComment,
950
- "aria-label": t.reply,
951
- onChange: (e) => setReply({ ...reply, body: e.target.value }),
952
- onKeyDown: (e) => {
953
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void answer(c);
954
- }
955
- }
956
- ),
957
- /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
958
- /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setReply(null), children: t.cancel }),
959
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !reply.body.trim() || posting === "busy", onClick: () => void answer(c), children: [
960
- /* @__PURE__ */ jsx2(Reply, { "aria-hidden": true }),
961
- t.reply
962
- ] })
963
- ] })
964
- ] }),
965
- can.comment && reply?.id !== c.id && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-actions", onClick: (e) => e.stopPropagation(), children: [
966
- c.resolved && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.resolved }),
967
- !c.resolved && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", onClick: () => setReply({ id: c.id, body: "" }), children: [
968
- /* @__PURE__ */ jsx2(Reply, { "aria-hidden": true }),
969
- t.reply
970
- ] }),
971
- host?.resolveComment && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "green", onClick: () => void settle(c), children: [
972
- c.resolved ? /* @__PURE__ */ jsx2(RotateCcw, { "aria-hidden": true }) : /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
973
- c.resolved ? t.reopen : t.resolve
974
- ] })
975
- ] })
976
- ] }, c.id))
977
- ] }), slot)
978
- ] });
979
- }
980
- export {
981
- TextEditor as default
982
- };
983
- //# sourceMappingURL=TextEditor-OLBLWC6F.js.map