@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
@@ -0,0 +1,1562 @@
1
+ 'use client';
2
+ import {
3
+ useLoad
4
+ } from "./chunk-QDH7EZAF.js";
5
+ import {
6
+ useLive
7
+ } from "./chunk-OXC4PUOC.js";
8
+ import {
9
+ a4,
10
+ align,
11
+ changes,
12
+ cleanPaste,
13
+ clear,
14
+ decide,
15
+ entries,
16
+ fromMarkdown,
17
+ indent,
18
+ kit,
19
+ letter,
20
+ locate,
21
+ notes,
22
+ paging,
23
+ replace,
24
+ searching,
25
+ spacing,
26
+ suggesting,
27
+ toMarkdown,
28
+ track
29
+ } from "./chunk-Z7UNJ4ND.js";
30
+ import {
31
+ rove,
32
+ useRestore
33
+ } from "./chunk-TDP6OJET.js";
34
+ import {
35
+ mathml,
36
+ readDocx,
37
+ writeDocx
38
+ } from "./chunk-M4OQG3EN.js";
39
+ import "./chunk-ISOOUYLI.js";
40
+
41
+ // src/editor/TextEditor.tsx
42
+ import { EditorContent, useEditor, useEditorState } from "@tiptap/react";
43
+ import {
44
+ AlignCenter,
45
+ AlignJustify,
46
+ AlignLeft,
47
+ AlignRight,
48
+ Baseline,
49
+ BetweenHorizontalEnd,
50
+ BetweenHorizontalStart,
51
+ BetweenVerticalEnd,
52
+ BetweenVerticalStart,
53
+ Bold,
54
+ Check,
55
+ ChevronDown,
56
+ ChevronUp,
57
+ CloudAlert,
58
+ Code,
59
+ FilePenLine,
60
+ Highlighter,
61
+ ImagePlus,
62
+ Italic,
63
+ Link,
64
+ Link2Off,
65
+ List,
66
+ ListChecks,
67
+ ListIndentDecrease,
68
+ ListIndentIncrease,
69
+ ListOrdered,
70
+ ListTree,
71
+ Grid3x3,
72
+ Image,
73
+ MessageSquarePlus,
74
+ MessagesSquare,
75
+ PaintBucket,
76
+ PaintRoller,
77
+ Search,
78
+ Omega,
79
+ PanelLeft,
80
+ PanelRight,
81
+ Ruler,
82
+ PanelTop,
83
+ Pencil,
84
+ Plus,
85
+ Minus,
86
+ Redo2,
87
+ RemoveFormatting,
88
+ Reply,
89
+ RotateCcw,
90
+ Save,
91
+ Sigma,
92
+ Strikethrough,
93
+ Table,
94
+ Subscript,
95
+ Superscript,
96
+ TableCellsMerge,
97
+ TableColumnsSplit,
98
+ TableRowsSplit,
99
+ Trash2,
100
+ Underline,
101
+ Undo2,
102
+ UnfoldVertical,
103
+ WifiOff,
104
+ X
105
+ } from "lucide-react";
106
+ import { Fragment as Fragment2, useEffect, useRef as useRef2, useState as useState2 } from "react";
107
+ import { createPortal } from "react-dom";
108
+
109
+ // src/viewers/DocxView.tsx
110
+ import { useLayoutEffect, useRef, useState } from "react";
111
+
112
+ // src/viewers/headings.ts
113
+ var last = 0;
114
+ function collect(root, selector, level = (el) => Number(el.tagName.slice(1))) {
115
+ return [...root.querySelectorAll(selector)].flatMap((el) => {
116
+ const text = el.textContent?.trim() ?? "";
117
+ if (!text) return [];
118
+ if (!el.id) el.id = `w100n-docs-h${++last}`;
119
+ return [{ id: el.id, level: Math.min(3, Math.max(1, level(el) || 1)), text }];
120
+ });
121
+ }
122
+
123
+ // src/viewers/DocxView.tsx
124
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
125
+ function DocxView({ bytes, onOutline, failed }) {
126
+ const box = useRef(null);
127
+ const [error, setError] = useState(false);
128
+ useLayoutEffect(() => {
129
+ let live = true;
130
+ let width = 0;
131
+ const root = box.current;
132
+ const fit = () => {
133
+ const wrapper = root.querySelector(".w100n-docx-wrapper");
134
+ if (wrapper && width) wrapper.style.zoom = String(Math.min(1, root.clientWidth / width));
135
+ };
136
+ const watch = new ResizeObserver(fit);
137
+ root.replaceChildren();
138
+ setError(false);
139
+ import("docx-preview").then(({ renderAsync }) => renderAsync(bytes, root, void 0, { inWrapper: true, className: "w100n-docx", ignoreLastRenderedPageBreak: true, renderAltChunks: false })).then(() => {
140
+ if (!live) return;
141
+ width = Math.max(0, ...[...root.querySelectorAll("section.w100n-docx")].map((page) => page.offsetWidth));
142
+ fit();
143
+ watch.observe(root);
144
+ onOutline(collect(root, '[class*="heading"]', (el) => Number(/heading(\d)/i.exec(el.className)?.[1] ?? 1)));
145
+ }).catch(() => {
146
+ if (live) setError(true);
147
+ });
148
+ return () => {
149
+ live = false;
150
+ watch.disconnect();
151
+ };
152
+ }, [bytes, onOutline]);
153
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
154
+ error && /* @__PURE__ */ jsx("p", { className: "w100n-docs-note", children: failed }),
155
+ /* @__PURE__ */ jsx("div", { ref: box, className: "w100n-docs-docx" })
156
+ ] });
157
+ }
158
+
159
+ // src/editor/TextEditor.tsx
160
+ import { Fragment as Fragment3, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
161
+ var legal = { width: 816, height: 1344 };
162
+ var symbols = [
163
+ ["\xA9", "copyright"],
164
+ ["\xAE", "registered"],
165
+ ["\u2122", "trademark"],
166
+ ["\xA7", "section"],
167
+ ["\xB6", "paragraph"],
168
+ ["\u2020", "dagger"],
169
+ ["\u2021", "double dagger"],
170
+ ["\u2022", "bullet"],
171
+ ["\u2026", "ellipsis"],
172
+ ["\u2030", "per mille"],
173
+ ["\u20AC", "euro"],
174
+ ["\xA3", "pound"],
175
+ ["\xA5", "yen"],
176
+ ["\u20B4", "hryvnia"],
177
+ ["z\u0142", "zloty"],
178
+ ["\xB0", "degree"],
179
+ ["\xB1", "plus minus"],
180
+ ["\xD7", "times multiply"],
181
+ ["\xF7", "divide"],
182
+ ["\u2248", "approximately"],
183
+ ["\u2260", "not equal"],
184
+ ["\u2264", "less or equal"],
185
+ ["\u2265", "greater or equal"],
186
+ ["\u221E", "infinity"],
187
+ ["\u221A", "square root"],
188
+ ["\u2211", "sum"],
189
+ ["\u03C0", "pi"],
190
+ ["\u03A9", "omega"],
191
+ ["\xB5", "micro"],
192
+ ["\u03B1", "alpha"],
193
+ ["\u03B2", "beta"],
194
+ ["\u03B3", "gamma"],
195
+ ["\u0394", "delta"],
196
+ ["\u03BB", "lambda"],
197
+ ["\u2192", "arrow right"],
198
+ ["\u2190", "arrow left"],
199
+ ["\u2191", "arrow up"],
200
+ ["\u2193", "arrow down"],
201
+ ["\u21D2", "arrow implies"],
202
+ ["\u2713", "check tick"],
203
+ ["\u2014", "em dash"],
204
+ ["\u2013", "en dash"],
205
+ ["\xAB", "quote left"],
206
+ ["\xBB", "quote right"],
207
+ ["\u201E", "quote low"],
208
+ ["\u2116", "number"],
209
+ ["\xBD", "half"],
210
+ ["\xBC", "quarter"],
211
+ ["\xB2", "squared"],
212
+ ["\xB3", "cubed"]
213
+ ];
214
+ var helpers = [["x\xB2", "^{2}"], ["x\u2099", "_{n}"], ["\u221Ax", "\\sqrt{}"], ["a/b", "\\frac{}{}"], ["\u2211", "\\sum_{}^{}"], ["\u222B", "\\int "], ["\u03C0", "\\pi "], ["\u2264", "\\le "], ["\u2265", "\\ge "], ["\u2260", "\\ne "], ["\xB1", "\\pm "], ["\xB7", "\\cdot "]];
215
+ var cm = (px) => Math.round(px / 96 * 254) / 100;
216
+ var inks = ["#0f172a", "#475569", "#dc2626", "#ea580c", "#ca8a04", "#16a34a", "#0891b2", "#2563eb", "#7c3aed", "#db2777"];
217
+ var glows = ["#fef08a", "#bbf7d0", "#bfdbfe", "#fbcfe8", "#fed7aa", "#ddd6fe"];
218
+ var faces = ["Arial", "Calibri", "Cambria", "Courier New", "Georgia", "Inter", "Roboto", "Times New Roman", "Verdana"];
219
+ var sizes = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 72];
220
+ var uid = () => Math.random().toString(36).slice(2, 10);
221
+ function TextEditor({ kind, bytes, t, language, rights, host, together, documentId, onSaved, onOutline, onReading, onPanel, slot, head, file, guard }) {
222
+ const docx = kind === "docx";
223
+ const can = { edit: !!host?.save && !!rights?.edit, comment: !!host?.comment && !!rights && (rights.comment || rights.edit) };
224
+ const source = useLoad(async () => {
225
+ if (docx) return { ...await readDocx(bytes), md: null };
226
+ const md = fromMarkdown(new TextDecoder().decode(bytes));
227
+ return { doc: md.doc, md, styles: [], page: a4 };
228
+ }, [bytes, kind]);
229
+ const live = useLive(together ? host : void 0, documentId, kind, bytes, source.data?.doc, language, can.edit, onSaved);
230
+ const shared = live?.ready ? live.wire : null;
231
+ if (live?.readonly) can.edit = false;
232
+ const baseline = useRef2(null);
233
+ const anchors = useRef2(/* @__PURE__ */ new Map());
234
+ const scroller = useRef2(null);
235
+ const root = useRef2(null);
236
+ const [pop, setPop] = useState2(null);
237
+ const [dialog, setDialog] = useState2(null);
238
+ const field = useRef2(null);
239
+ const [more, setMore] = useState2(() => typeof localStorage !== "undefined" && localStorage.getItem("w100n-docs-more") === "1");
240
+ const [zoom, setZoom] = useState2(null);
241
+ const [layout, setLayout] = useState2(null);
242
+ const [room, setRoom] = useState2(0);
243
+ const [saving, setSaving] = useState2("idle");
244
+ const [href, setHref] = useState2("");
245
+ const [grid, setGrid] = useState2({ rows: 0, cols: 0 });
246
+ const [draft, setDraft] = useState2(null);
247
+ const [reply, setReply] = useState2(null);
248
+ const [active, setActive] = useState2(null);
249
+ const [resolved, setResolved] = useState2(false);
250
+ const [posting, setPosting] = useState2("idle");
251
+ const [added, setAdded] = useState2([]);
252
+ const [changed, setChanged] = useState2({});
253
+ const [editing, setEditing] = useState2(null);
254
+ const [dropping, setDropping] = useState2(null);
255
+ const [failed, setFailed] = useState2(false);
256
+ const [paint, setPaint] = useState2(null);
257
+ const [query, setQuery] = useState2("");
258
+ const [swap, setSwap] = useState2("");
259
+ const [matchCase, setMatchCase] = useState2(false);
260
+ const [count, setCount] = useState2({ words: 0, pages: 0 });
261
+ const [marg, setMarg] = useState2(null);
262
+ const [unsetup, setUnsetup] = useState2(false);
263
+ const [draft2, setDraft2] = useState2(null);
264
+ useRestore(pop !== null);
265
+ useRestore(editing !== null);
266
+ useRestore(dropping !== null);
267
+ const editor = useEditor({
268
+ extensions: kit(kind, t, shared),
269
+ content: shared ? null : source.data?.doc ?? null,
270
+ editable: can.edit && (!live || !!shared),
271
+ immediatelyRender: true,
272
+ shouldRerenderOnTransaction: false,
273
+ editorProps: { attributes: { class: "w100n-docs-prose w100n-text-body", "aria-label": t.documentText }, transformPastedHTML: cleanPaste },
274
+ onCreate: ({ editor: made }) => {
275
+ if (source.data && !live) baseline.current = { bytes, doc: made.state.doc, base: source.data.md && track(source.data.md, made.state.doc) };
276
+ }
277
+ }, [source.data, can.edit, shared]);
278
+ const state = useEditorState({
279
+ editor,
280
+ selector: ({ editor: e }) => {
281
+ if (!e) return null;
282
+ const { $from, empty } = e.state.selection;
283
+ const block = $from.parent;
284
+ return {
285
+ style: e.isActive("codeBlock") ? "code" : block.type.name === "heading" ? `h${block.attrs.level}` : e.isActive("blockquote") ? "quote" : "normal",
286
+ bold: e.isActive("bold"),
287
+ italic: e.isActive("italic"),
288
+ underline: e.isActive("underline"),
289
+ strike: e.isActive("strike"),
290
+ code: e.isActive("code"),
291
+ bullets: e.isActive("bulletList"),
292
+ numbers: e.isActive("orderedList"),
293
+ tasks: e.isActive("taskList"),
294
+ align: block.attrs.align ?? "left",
295
+ table: e.isActive("table"),
296
+ link: e.isActive("link"),
297
+ color: e.getAttributes("textStyle").color ?? null,
298
+ glow: e.isActive("highlight"),
299
+ font: e.getAttributes("textStyle").fontFamily ?? null,
300
+ size: e.getAttributes("textStyle").fontSize ?? null,
301
+ fill: e.getAttributes("tableCell").background ?? e.getAttributes("tableHeader").background ?? null,
302
+ bare: e.getAttributes("table").borders === "none",
303
+ sup: e.isActive("superscript"),
304
+ sub: e.isActive("subscript"),
305
+ img: e.isActive("image") ? e.getAttributes("image").wrap ?? "inline" : null,
306
+ line: block.attrs.line ?? null,
307
+ after: block.attrs.after ?? null,
308
+ undo: e.can().undo(),
309
+ redo: e.can().redo(),
310
+ empty,
311
+ toc: (() => {
312
+ let at = -1;
313
+ e.state.doc.forEach((node, pos) => {
314
+ if (at < 0 && node.type.name === "toc") at = pos;
315
+ });
316
+ return at;
317
+ })(),
318
+ find: (({ at, hits }) => `${at}/${hits.length}`)(searching.getState(e.state)),
319
+ suggest: !!suggesting.getState(e.state)?.on,
320
+ revs: docx ? changes(e.state.doc) : [],
321
+ dirty: !!baseline.current && !e.state.doc.eq(baseline.current.doc)
322
+ };
323
+ }
324
+ });
325
+ const page = layout ?? source.data?.page ?? a4;
326
+ const print = () => {
327
+ if (!editor) return;
328
+ const frame = document.createElement("iframe");
329
+ frame.style.cssText = "position:fixed;width:0;height:0;border:0;opacity:0";
330
+ document.body.append(frame);
331
+ const [mt, mr, mb, ml] = page.margins;
332
+ const size = docx ? `${page.width}px ${page.height}px` : "A4";
333
+ frame.contentDocument.open();
334
+ frame.contentDocument.write(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>${editor.state.doc.firstChild?.textContent.slice(0, 80).replace(/[<&]/g, "") ?? ""}</title><style>
335
+ @page { size: ${size}; margin: ${docx ? `${mt}px ${mr}px ${mb}px ${ml}px` : "20mm"}; }
336
+ body { margin: 0; font: 11pt/1.5 Arial, sans-serif; color: #111; }
337
+ h1 { font-size: 20pt; } h2 { font-size: 15pt; } h3 { font-size: 13pt; }
338
+ table { border-collapse: collapse; width: 100%; } td, th { border: 1px solid #94a3b8; padding: 4px 6px; vertical-align: top; } table[data-borders='none'] td, table[data-borders='none'] th { border-color: transparent; }
339
+ img { max-width: 100%; } img[data-wrap='left'] { float: left; margin: 0 12pt 6pt 0; } img[data-wrap='right'] { float: right; margin: 0 0 6pt 12pt; }
340
+ [data-type='page-break'], .page-break { break-after: page; height: 0; overflow: hidden; }
341
+ </style></head><body>${editor.getHTML()}</body></html>`);
342
+ frame.contentDocument.close();
343
+ frame.contentWindow.focus();
344
+ frame.contentWindow.print();
345
+ setTimeout(() => frame.remove(), 1e3);
346
+ };
347
+ const marginal = marg ?? (source.data && "marginal" in source.data ? source.data.marginal : null) ?? { header: "", footer: "", numbers: false };
348
+ useEffect(() => {
349
+ if (editor && docx && source.data) editor.view.dispatch(editor.state.tr.setMeta(paging, { page, marks: { header: marginal.header, footer: marginal.footer } }));
350
+ }, [editor, source.data, page, marginal.header, marginal.footer]);
351
+ useEffect(() => {
352
+ if (!shared || !docx) return;
353
+ const map = shared.doc.getMap("setup");
354
+ const sync = () => {
355
+ const v = map.toJSON();
356
+ if (v.page) setLayout(v.page);
357
+ if (v.header !== void 0 || v.footer !== void 0 || v.numbers !== void 0) setMarg({ header: v.header ?? "", footer: v.footer ?? "", numbers: !!v.numbers });
358
+ };
359
+ sync();
360
+ map.observe(sync);
361
+ return () => map.unobserve(sync);
362
+ }, [shared, docx]);
363
+ const loaded = useLoad((signal) => host?.comments ? host.comments(documentId, { signal }) : Promise.resolve([]), [host, documentId]);
364
+ const all = [...loaded.data ?? [], ...added].flatMap((c) => c.id in changed ? changed[c.id] ?? [] : [c]).filter((c) => !c.replyTo || changed[c.replyTo] !== null);
365
+ const open = all.filter((c) => !c.replyTo && !c.resolved);
366
+ const threads = all.filter((c) => !c.replyTo && (resolved || !c.resolved)).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
367
+ const openIds = open.map((c) => c.id).join();
368
+ useEffect(() => {
369
+ if (!editor || !source.data) return;
370
+ const have = new Map(notes.getState(editor.state).spots.map((s) => [s.id, s]));
371
+ const fresh = open.filter((c) => !have.has(c.id));
372
+ const found2 = locate(editor.state.doc, fresh.map((c) => ({ id: c.id, quote: c.quote, anchor: anchors.current.get(c.id) })));
373
+ editor.view.dispatch(editor.state.tr.setMeta(notes, { spots: [...open.flatMap((c) => have.get(c.id) ?? []), ...found2] }));
374
+ }, [editor, source.data, openIds]);
375
+ useEffect(() => {
376
+ if (editor && source.data) editor.view.dispatch(editor.state.tr.setMeta(notes, { active }));
377
+ }, [editor, source.data, active]);
378
+ useEffect(() => {
379
+ if (!editor || !source.data) return;
380
+ let timer = 0;
381
+ let last2 = "";
382
+ const run = () => {
383
+ const list = [];
384
+ editor.state.doc.forEach((node, offset) => {
385
+ if (node.type.name !== "heading" || !node.textContent.trim()) return;
386
+ list.push({ id: `h${offset}`, level: Math.min(3, node.attrs.level), text: node.textContent.trim(), go: () => {
387
+ editor.view.nodeDOM(offset)?.scrollIntoView({ behavior: "smooth", block: "start" });
388
+ editor.commands.setTextSelection(offset + 1);
389
+ } });
390
+ });
391
+ const text = editor.state.doc.textBetween(0, editor.state.doc.content.size, " ", " ");
392
+ setCount({ words: (text.match(/[\p{L}\p{N}]+(?:['’-][\p{L}\p{N}]+)*/gu) ?? []).length, pages: scroller.current?.querySelectorAll(".w100n-text-gap").length ?? 0 });
393
+ const key = JSON.stringify(list.map((h) => [h.id, h.level, h.text]));
394
+ if (key === last2) return;
395
+ last2 = key;
396
+ onOutline(list);
397
+ };
398
+ const later = () => {
399
+ clearTimeout(timer);
400
+ timer = window.setTimeout(run, 300);
401
+ };
402
+ run();
403
+ editor.on("update", later);
404
+ return () => {
405
+ clearTimeout(timer);
406
+ editor.off("update", later);
407
+ };
408
+ }, [editor, source.data, onOutline]);
409
+ useEffect(() => {
410
+ const el = scroller.current;
411
+ if (!el || !editor || !onReading) return;
412
+ let frame = 0;
413
+ let last2 = "";
414
+ const look = () => {
415
+ cancelAnimationFrame(frame);
416
+ frame = requestAnimationFrame(() => {
417
+ const line = el.getBoundingClientRect().top + el.clientHeight / 3;
418
+ let id = null;
419
+ editor.state.doc.forEach((node, offset) => {
420
+ const dom = node.type.name === "heading" && node.textContent.trim() ? editor.view.nodeDOM(offset) : null;
421
+ if (dom && dom.getBoundingClientRect().top <= line) id = `h${offset}`;
422
+ });
423
+ const room2 = el.scrollHeight - el.clientHeight;
424
+ const progress = room2 > 0 ? Math.round(el.scrollTop / room2 * 100) / 100 : 1;
425
+ if (`${id}:${progress}` === last2) return;
426
+ last2 = `${id}:${progress}`;
427
+ onReading({ documentId, id, progress });
428
+ });
429
+ };
430
+ look();
431
+ el.addEventListener("scroll", look, { passive: true });
432
+ return () => {
433
+ cancelAnimationFrame(frame);
434
+ el.removeEventListener("scroll", look);
435
+ };
436
+ }, [editor, source.data, onReading, documentId]);
437
+ const dirty = live ? live.pending : !!state?.dirty || unsetup;
438
+ const unsent = live ? live.unsynced && can.edit : dirty;
439
+ useEffect(() => {
440
+ if (!guard || live || !dirty || !source.data || !editor) return;
441
+ guard.current = save;
442
+ return () => {
443
+ guard.current = null;
444
+ };
445
+ });
446
+ useEffect(() => {
447
+ if (live || !dirty || !can.edit || saving === "busy" || !editor) return;
448
+ const timer = window.setTimeout(() => void save(true), 3e3);
449
+ return () => clearTimeout(timer);
450
+ });
451
+ useEffect(() => {
452
+ if (!unsent) return;
453
+ const stop = (e) => e.preventDefault();
454
+ window.addEventListener("beforeunload", stop);
455
+ return () => window.removeEventListener("beforeunload", stop);
456
+ }, [unsent]);
457
+ useEffect(() => {
458
+ const el = scroller.current;
459
+ if (!el || typeof ResizeObserver === "undefined") return;
460
+ const watch = new ResizeObserver(() => setRoom(el.clientWidth));
461
+ watch.observe(el);
462
+ return () => watch.disconnect();
463
+ }, [editor, source.data]);
464
+ useEffect(() => {
465
+ const el = root.current;
466
+ if (!el) return;
467
+ const open2 = () => {
468
+ setPop("find");
469
+ el.querySelector(".w100n-text-find input")?.focus();
470
+ };
471
+ el.addEventListener("docs-find", open2);
472
+ return () => el.removeEventListener("docs-find", open2);
473
+ }, [editor, source.data]);
474
+ useEffect(() => {
475
+ const shown = pop === "find" ? query : "";
476
+ if (editor && searching.getState(editor.state).query !== shown) editor.view.dispatch(editor.state.tr.setMeta(searching, { query: shown }));
477
+ }, [editor, pop, query]);
478
+ useEffect(() => {
479
+ const press = (e) => {
480
+ if (!(e.metaKey || e.ctrlKey) || e.altKey || e.shiftKey || e.key.toLowerCase() !== "p") return;
481
+ e.preventDefault();
482
+ e.stopPropagation();
483
+ print();
484
+ };
485
+ window.addEventListener("keydown", press, true);
486
+ return () => window.removeEventListener("keydown", press, true);
487
+ });
488
+ useEffect(() => {
489
+ if (!pop) return;
490
+ const close = (e) => {
491
+ if (!e.target.closest(".w100n-text-pop, [data-pop]")) setPop(null);
492
+ };
493
+ document.addEventListener("pointerdown", close);
494
+ return () => document.removeEventListener("pointerdown", close);
495
+ }, [pop]);
496
+ 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 });
497
+ if (!source.data || !editor || !state || live && !live.ready && !live.late && !live.denied) return /* @__PURE__ */ jsx2("div", { className: "w100n-docs-empty", children: t.loading });
498
+ const save = async (auto = false) => {
499
+ if (live) {
500
+ if (!can.edit || !live.ready || !live.online || !live.pending || saving === "busy") return false;
501
+ setSaving("busy");
502
+ const ok = await live.save();
503
+ setSaving(ok ? "done" : "failed");
504
+ return ok;
505
+ }
506
+ const was = baseline.current;
507
+ const doc = editor.state.doc;
508
+ if (!can.edit || !was || saving === "busy" || doc.eq(was.doc) && !unsetup) return false;
509
+ setSaving("busy");
510
+ try {
511
+ const md = was.base && toMarkdown(doc, was.base);
512
+ const out = md ? new TextEncoder().encode(md.text).buffer : await writeDocx(was.bytes, editor.getJSON(), void 0, unsetup ? { page, ...marginal } : void 0);
513
+ await host.save(documentId, out, auto ? t.autosavedNote : t.editedNote, auto);
514
+ baseline.current = { bytes: md ? out : was.bytes, doc, base: md ? md.base : null };
515
+ if (!md && editor.state.doc === doc) {
516
+ const fresh = (await readDocx(out)).doc;
517
+ if (editor.state.doc === doc && renew(fresh)) baseline.current = { bytes: out, doc: editor.state.doc, base: null };
518
+ }
519
+ setUnsetup(false);
520
+ setSaving("done");
521
+ onSaved?.();
522
+ return true;
523
+ } catch {
524
+ setSaving("failed");
525
+ return false;
526
+ }
527
+ };
528
+ const renew = (fresh) => {
529
+ const at = [];
530
+ const refs = [];
531
+ editor.state.doc.descendants((node, pos) => {
532
+ if (node.isBlock) at.push(pos);
533
+ });
534
+ editor.schema.nodeFromJSON(fresh).descendants((node) => {
535
+ if (node.isBlock) refs.push(node.type.name, node.attrs.ref);
536
+ });
537
+ if (refs.length !== at.length * 2 || at.some((pos, i) => editor.state.doc.nodeAt(pos).type.name !== refs[i * 2])) return false;
538
+ const tr = editor.state.tr.setMeta("addToHistory", false);
539
+ at.forEach((pos, i) => {
540
+ if (tr.doc.nodeAt(pos).attrs.ref !== refs[i * 2 + 1]) tr.setNodeAttribute(pos, "ref", refs[i * 2 + 1]);
541
+ });
542
+ editor.view.dispatch(tr);
543
+ return true;
544
+ };
545
+ const linkUp = () => {
546
+ setHref(editor.getAttributes("link").href ?? "");
547
+ setPop("link");
548
+ };
549
+ const putLink = () => {
550
+ const value = href.trim();
551
+ const chain = editor.chain().focus();
552
+ if (!value) chain.extendMarkRange("link").unsetLink().run();
553
+ else if (editor.state.selection.empty && !editor.isActive("link")) chain.insertContent({ type: "text", text: value, marks: [{ type: "link", attrs: { href: value } }] }).run();
554
+ else chain.extendMarkRange("link").setLink({ href: value }).run();
555
+ setPop(null);
556
+ };
557
+ const begin = () => {
558
+ const { from, to, empty } = editor.state.selection;
559
+ if (empty || !can.comment) return;
560
+ editor.view.dispatch(editor.state.tr.setMeta(notes, { pending: { from, to } }));
561
+ setDraft({ quote: editor.state.doc.textBetween(from, to, "\n", " "), body: "" });
562
+ setPosting("idle");
563
+ onPanel?.("comments");
564
+ };
565
+ const drop = () => {
566
+ editor.view.dispatch(editor.state.tr.setMeta(notes, { pending: null }));
567
+ setDraft(null);
568
+ };
569
+ const post = async () => {
570
+ if (!draft?.body.trim() || !host?.comment || posting === "busy") return;
571
+ setPosting("busy");
572
+ try {
573
+ const anchor = uid();
574
+ const made = await host.comment(documentId, { body: draft.body.trim(), quote: draft.quote, replyTo: null, anchor });
575
+ anchors.current.set(made.id, anchor);
576
+ const range = notes.getState(editor.state).pending;
577
+ editor.view.dispatch(editor.state.tr.setMeta(notes, { pending: null, spots: [...notes.getState(editor.state).spots, ...range ? [{ id: made.id, ...range }] : []] }));
578
+ setAdded((list) => [...list, made]);
579
+ setDraft(null);
580
+ setActive(made.id);
581
+ setPosting("idle");
582
+ } catch {
583
+ setPosting("failed");
584
+ }
585
+ };
586
+ const answer = async (parent) => {
587
+ if (!reply?.body.trim() || !host?.comment || posting === "busy") return;
588
+ setPosting("busy");
589
+ try {
590
+ const made = await host.comment(documentId, { body: reply.body.trim(), quote: parent.quote, replyTo: parent.id, anchor: anchors.current.get(parent.id) ?? "" });
591
+ setAdded((list) => [...list, made]);
592
+ setReply(null);
593
+ setPosting("idle");
594
+ } catch {
595
+ setPosting("failed");
596
+ }
597
+ };
598
+ const settle = async (c) => {
599
+ try {
600
+ await host.resolveComment(documentId, c.id, !c.resolved);
601
+ setChanged((list) => ({ ...list, [c.id]: { ...c, resolved: !c.resolved } }));
602
+ } catch {
603
+ setPosting("failed");
604
+ }
605
+ };
606
+ const change = async (id, body) => {
607
+ if (posting === "busy") return;
608
+ setPosting("busy");
609
+ setFailed(false);
610
+ try {
611
+ let made = null;
612
+ if (body === null) await host.deleteComment(documentId, id);
613
+ else made = await host.editComment(documentId, id, body);
614
+ setChanged((list) => ({ ...list, [id]: made }));
615
+ setEditing(null);
616
+ setDropping(null);
617
+ if (body === null && active === id) setActive(null);
618
+ } catch {
619
+ setFailed(true);
620
+ }
621
+ setPosting("idle");
622
+ };
623
+ const mine = (c) => c.author.id === host?.me.id;
624
+ const drops = (c) => mine(c) || c.author.id.startsWith("guest:") && !!rights?.edit;
625
+ const show = (c) => {
626
+ setActive(c.id);
627
+ const spot = notes.getState(editor.state).spots.find((s) => s.id === c.id);
628
+ if (!spot) return;
629
+ const at = editor.view.domAtPos(spot.from).node;
630
+ (at instanceof HTMLElement ? at : at.parentElement)?.scrollIntoView({ behavior: "smooth", block: "center" });
631
+ };
632
+ const suggested = [...state.revs.reduce((all2, c) => {
633
+ const key = `${c.kind}:${c.id}`;
634
+ const was = all2.get(key);
635
+ all2.set(key, was ? { ...was, text: `${was.text} ${c.text}` } : c);
636
+ return all2;
637
+ }, /* @__PURE__ */ new Map()).values()];
638
+ const jump = (at) => {
639
+ const dom = editor.view.domAtPos(at).node;
640
+ (dom instanceof HTMLElement ? dom : dom.parentElement)?.scrollIntoView({ behavior: "smooth", block: "center" });
641
+ };
642
+ const percent = zoom ?? (docx && room ? Math.max(10, Math.min(100, Math.floor((room - 24) / page.width * 100))) : 100);
643
+ const zoomBy = (d) => setZoom(Math.min(200, Math.max(30, percent + d)));
644
+ const keys = (e) => {
645
+ if (e.defaultPrevented) return;
646
+ const key = e.key.toLowerCase();
647
+ const mod2 = e.metaKey || e.ctrlKey;
648
+ if (key === "escape" && (pop || draft || paint)) {
649
+ e.stopPropagation();
650
+ if (paint) setPaint(null);
651
+ else if (pop) setPop(null);
652
+ else drop();
653
+ return;
654
+ }
655
+ if (!mod2) return;
656
+ const act = (run) => {
657
+ e.preventDefault();
658
+ run();
659
+ };
660
+ if (key === "s" && can.edit) act(() => void save());
661
+ else if (key === "k" && can.edit) act(linkUp);
662
+ else if (key === "f" && !e.altKey) act(() => setPop("find"));
663
+ else if (key === "m" && e.altKey && can.comment) act(begin);
664
+ else if (!can.edit || e.altKey) return;
665
+ else if (key === "]" || key === "[") act(() => indent(editor, key === "]" ? 1 : -1));
666
+ else if (key === "\\") act(() => clear(editor));
667
+ else if (key === "enter" && docx) act(() => editor.chain().focus().insertContent({ type: "pageBreak" }).run());
668
+ else if (e.shiftKey && docx && ["l", "e", "r", "j"].includes(key)) act(() => align(editor, { l: "left", e: "center", r: "right", j: "justify" }[key]));
669
+ };
670
+ const tool = (icon, label, run, pressed, disabled) => {
671
+ const Icon = icon;
672
+ return /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": label, title: label, "aria-pressed": pressed, disabled, onClick: () => {
673
+ run();
674
+ }, children: /* @__PURE__ */ jsx2(Icon, { "aria-hidden": true }) });
675
+ };
676
+ const gap = /* @__PURE__ */ jsx2("span", { className: "w100n-pdf-gap" });
677
+ const known = source.data.styles;
678
+ const named = (test) => {
679
+ const name = known.find(test)?.name;
680
+ return name && `${name[0].toUpperCase()}${name.slice(1)}`;
681
+ };
682
+ const levels = docx ? [1, 2, 3, 4, 5, 6].filter((level) => level <= 4 || named((s) => s.heading === level)) : [1, 2, 3, 4];
683
+ const styles = [
684
+ ["normal", t.normal, () => editor.chain().focus().clearNodes().setParagraph().run()],
685
+ ...levels.map((level) => [
686
+ `h${level}`,
687
+ named((s) => s.heading === level && !/^title$/i.test(s.name)) || `${t.heading} ${level}`,
688
+ () => editor.chain().focus().setHeading({ level }).run()
689
+ ]),
690
+ ["quote", named((s) => /quote/i.test(s.name)) || t.quote, () => editor.chain().focus().clearNodes().setParagraph().setBlockquote().run()],
691
+ ["code", t.code, () => editor.chain().focus().setCodeBlock().run()]
692
+ ];
693
+ const margins = [[t.marginsNormal, [96, 96, 96, 96]], [t.marginsNarrow, [48, 48, 48, 48]], [t.marginsWide, [96, 192, 96, 192]]];
694
+ const when = (iso) => new Intl.DateTimeFormat(language, { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));
695
+ const person = (c) => /* @__PURE__ */ jsxs2("header", { children: [
696
+ 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() }),
697
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-text-who", children: [
698
+ /* @__PURE__ */ jsxs2("b", { children: [
699
+ c.author.name,
700
+ c.author.id.startsWith("guest:") && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.guest })
701
+ ] }),
702
+ /* @__PURE__ */ jsx2("small", { children: when(c.createdAt) })
703
+ ] }),
704
+ drops(c) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-own", onClick: (e) => e.stopPropagation(), children: [
705
+ mine(c) && host.editComment && /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": t.editComment, title: t.editComment, onClick: () => {
706
+ setDropping(null);
707
+ setEditing({ id: c.id, body: c.body });
708
+ }, children: /* @__PURE__ */ jsx2(Pencil, { "aria-hidden": true }) }),
709
+ host.deleteComment && /* @__PURE__ */ jsx2("button", { type: "button", "data-tone": "red", "aria-label": t.delete, title: t.delete, onClick: () => {
710
+ setEditing(null);
711
+ setDropping(c.id);
712
+ }, children: /* @__PURE__ */ jsx2(Trash2, { "aria-hidden": true }) })
713
+ ] })
714
+ ] });
715
+ const words = (c) => editing?.id === c.id ? /* @__PURE__ */ jsxs2("div", { className: "w100n-text-reply w100n-text-edit", onClick: (e) => e.stopPropagation(), children: [
716
+ /* @__PURE__ */ jsx2(
717
+ "textarea",
718
+ {
719
+ autoFocus: true,
720
+ value: editing.body,
721
+ "aria-label": t.editComment,
722
+ onChange: (e) => setEditing({ ...editing, body: e.target.value }),
723
+ onKeyDown: (e) => {
724
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && editing.body.trim()) void change(c.id, editing.body.trim());
725
+ }
726
+ }
727
+ ),
728
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
729
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setEditing(null), children: t.cancel }),
730
+ /* @__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: [
731
+ /* @__PURE__ */ jsx2(Save, { "aria-hidden": true }),
732
+ t.save
733
+ ] })
734
+ ] })
735
+ ] }) : dropping === c.id ? /* @__PURE__ */ jsxs2("div", { className: "w100n-text-confirm", role: "group", "aria-label": t.delete, onClick: (e) => e.stopPropagation(), children: [
736
+ /* @__PURE__ */ jsx2("p", { children: c.replyTo || !all.some((r) => r.replyTo === c.id) ? t.deleteAsk : t.deleteThread }),
737
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
738
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", autoFocus: true, onClick: () => setDropping(null), children: t.cancel }),
739
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", disabled: posting === "busy", onClick: () => void change(c.id, null), children: [
740
+ /* @__PURE__ */ jsx2(Trash2, { "aria-hidden": true }),
741
+ t.delete
742
+ ] })
743
+ ] })
744
+ ] }) : /* @__PURE__ */ jsx2("p", { children: c.body });
745
+ const pick = (next) => setPop(pop === next ? null : next);
746
+ const swatches = (list, current, set, none) => /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-swatches", role: "group", children: [
747
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-text-none", onClick: () => {
748
+ set(null);
749
+ setPop(null);
750
+ }, children: none }),
751
+ list.map((c, i) => /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": c, "aria-pressed": current === c, onClick: () => {
752
+ set(c);
753
+ setPop(null);
754
+ }, children: /* @__PURE__ */ jsx2("i", { style: { background: c } }) }, i))
755
+ ] });
756
+ const mod = /Mac|iPhone|iPad/.test(navigator.platform) ? "\u2318" : "Ctrl+";
757
+ const item = (label, run, more2 = {}) => ({ label, run, ...more2 });
758
+ const insertToc = () => {
759
+ const at = editor.state.selection.$from.before(1);
760
+ editor.chain().focus().insertContentAt(at, { type: "toc", attrs: { entries: JSON.stringify(entries(editor.state.doc, !docx, at - 1)) } }).run();
761
+ };
762
+ const suggest = () => editor.view.dispatch(editor.state.tr.setMeta(suggesting, { on: !state.suggest, me: host?.me.name ?? "" }));
763
+ const aligns = [[AlignLeft, t.alignLeft, "left"], [AlignCenter, t.alignCenter, "center"], [AlignRight, t.alignRight, "right"], [AlignJustify, t.justify, "justify"]];
764
+ const lines = [[1, t.single], [1.15, "1.15"], [1.5, "1.5"], [2, t.double]];
765
+ const menus = [
766
+ ["file", t.menuFile, [
767
+ ...can.edit ? [
768
+ item(t.save, () => void save(), { keys: `${mod}S`, off: !dirty }),
769
+ item(t.print, print, { keys: `${mod}P` }),
770
+ ...docx ? ["-", item(t.pageSetup, () => setDraft2({ width: page.width, height: page.height, margins: page.margins.map(cm), ...marginal }))] : []
771
+ ] : [],
772
+ ...can.edit && (file?.formats.length || file?.copy || file?.rename) ? ["-"] : [],
773
+ ...(file?.formats ?? []).map((f) => item(`${t.downloadAs} ${f === "md" ? "Markdown" : f.toUpperCase()}`, () => file.download(f))),
774
+ !!file?.copy && item(t.makeCopy, file.copy),
775
+ !!file?.rename && item(t.rename, file.rename)
776
+ ]],
777
+ ["edit", t.menuEdit, can.edit ? [
778
+ item(t.undo, () => editor.chain().focus().undo().run(), { keys: `${mod}Z`, off: !state.undo }),
779
+ item(t.redo, () => editor.chain().focus().redo().run(), { keys: `${mod}\u21E7Z`, off: !state.redo }),
780
+ "-",
781
+ item(t.findReplace, () => setPop("find"), { keys: `${mod}F` }),
782
+ "-",
783
+ item(t.clearFormat, () => clear(editor), { keys: `${mod}\\` })
784
+ ] : []],
785
+ ["view", t.viewTab, [
786
+ item(t.zoomIn, () => zoomBy(10)),
787
+ item(t.zoomOut, () => zoomBy(-10)),
788
+ docx && item(t.fitWidth, () => setZoom(null), { on: zoom === null }),
789
+ item(t.fit, () => setZoom(100)),
790
+ "-",
791
+ item(t.outline, () => onPanel?.("outline")),
792
+ "-",
793
+ item([docx && count.pages ? `${t.pages}: ${count.pages}` : "", `${t.words}: ${count.words.toLocaleString(language)}`].filter(Boolean).join(" \xB7 "), () => {
794
+ }, { off: true })
795
+ ]],
796
+ ["insert", t.insert, can.edit ? [
797
+ item(t.table, () => setPop("table")),
798
+ item(t.image, () => {
799
+ setHref("");
800
+ setPop("image");
801
+ }),
802
+ item(t.link, linkUp, { keys: `${mod}K` }),
803
+ docx && item(t.pageBreak, () => editor.chain().focus().insertContent({ type: "pageBreak" }).run(), { keys: `${mod}\u21B5` }),
804
+ item(t.rule, () => editor.chain().focus().setHorizontalRule().run()),
805
+ "-",
806
+ state.toc < 0 ? item(t.tocInsert, insertToc) : item(t.tocRemove, () => editor.chain().focus().deleteRange({ from: state.toc, to: state.toc + editor.state.doc.nodeAt(state.toc).nodeSize }).run()),
807
+ can.comment && item(t.addComment, begin, { off: state.empty, keys: `${mod}\u2325M` }),
808
+ "-",
809
+ item(t.symbols, () => setDialog({ kind: "symbol", query: "" })),
810
+ docx && item(t.formula, () => setDialog({ kind: "formula", latex: "", at: null })),
811
+ docx && item(t.footnote, () => setDialog({ kind: "note", text: "", at: null }))
812
+ ] : []],
813
+ ["format", t.menuFormat, can.edit ? [
814
+ ...styles.map(([key, label, run]) => item(label, run, { on: state.style === key })),
815
+ "-",
816
+ item(t.bold, () => editor.chain().focus().toggleBold().run(), { on: state.bold, keys: `${mod}B` }),
817
+ item(t.italic, () => editor.chain().focus().toggleItalic().run(), { on: state.italic, keys: `${mod}I` }),
818
+ docx && item(t.underline, () => editor.chain().focus().toggleUnderline().run(), { on: state.underline, keys: `${mod}U` }),
819
+ item(t.strike, () => editor.chain().focus().toggleStrike().run(), { on: state.strike }),
820
+ docx && item(t.superscript, () => editor.chain().focus().toggleMark("superscript").run(), { on: state.sup, keys: `${mod}.` }),
821
+ docx && item(t.subscript, () => editor.chain().focus().toggleMark("subscript").run(), { on: state.sub, keys: `${mod},` }),
822
+ ...docx ? [
823
+ "-",
824
+ ...aligns.map(([, label, value]) => item(label, () => align(editor, value), { on: state.align === value })),
825
+ "-",
826
+ ...lines.map(([v, label]) => item(`${t.lineSpacing}: ${label}`, () => spacing(editor, { line: v }), { on: state.line === v }))
827
+ ] : [],
828
+ "-",
829
+ item(t.bullets, () => editor.chain().focus().toggleBulletList().run(), { on: state.bullets }),
830
+ item(t.numbers, () => editor.chain().focus().toggleOrderedList().run(), { on: state.numbers }),
831
+ item(t.tasks, () => editor.chain().focus().toggleTaskList().run(), { on: state.tasks }),
832
+ "-",
833
+ item(t.outdent, () => indent(editor, -1), { keys: `${mod}[` }),
834
+ item(t.indent, () => indent(editor, 1), { keys: `${mod}]` })
835
+ ] : []],
836
+ ["tools", t.menuTools, [
837
+ docx && can.edit && item(t.suggest, suggest, { on: state.suggest }),
838
+ docx && can.edit && state.revs.length > 0 && item(t.acceptAll, () => decide(editor, true)),
839
+ docx && can.edit && state.revs.length > 0 && item(t.rejectAll, () => decide(editor, false)),
840
+ !!host?.comments && (can.comment || open.length > 0) && item(t.comments, () => onPanel?.("comments"))
841
+ ]]
842
+ ];
843
+ const menu = ([key, label, list]) => /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
844
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-text-menu-button", "data-pop": true, "aria-haspopup": "menu", "aria-expanded": pop === key, onClick: () => pick(key), children: label }),
845
+ pop === key && /* @__PURE__ */ jsx2("span", { className: "w100n-text-pop w100n-text-menu", role: "menu", "aria-label": label, children: list.filter((x) => x !== false).map((x, i) => x === "-" ? /* @__PURE__ */ jsx2("hr", {}, i) : /* @__PURE__ */ jsxs2("button", { type: "button", role: x.on === void 0 ? "menuitem" : "menuitemcheckbox", "aria-checked": x.on, disabled: x.off, onClick: () => {
846
+ setPop(null);
847
+ x.run();
848
+ }, children: [
849
+ /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
850
+ /* @__PURE__ */ jsx2("span", { children: x.label }),
851
+ x.keys && /* @__PURE__ */ jsx2("kbd", { children: x.keys })
852
+ ] }, i)) })
853
+ ] }, key);
854
+ const seek = (next) => {
855
+ editor.view.dispatch(editor.state.tr.setMeta(searching, next));
856
+ const found2 = searching.getState(editor.state);
857
+ const hit = found2.hits[found2.at];
858
+ if (hit && next.at !== void 0) {
859
+ editor.commands.setTextSelection(hit);
860
+ editor.view.dom.querySelector(".w100n-found.is-current")?.scrollIntoView({ behavior: "smooth", block: "center" });
861
+ }
862
+ };
863
+ const found = searching.getState(editor.state);
864
+ const finder = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
865
+ /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "find", "aria-label": t.findReplace, title: t.findReplace, onClick: () => pick("find"), children: /* @__PURE__ */ jsx2(Search, { "aria-hidden": true }) }),
866
+ pop === "find" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-find", role: "search", "aria-label": t.findReplace, children: [
867
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
868
+ /* @__PURE__ */ jsx2(
869
+ "input",
870
+ {
871
+ autoFocus: true,
872
+ value: query,
873
+ placeholder: t.find,
874
+ "aria-label": t.find,
875
+ onFocus: (e) => e.currentTarget.select(),
876
+ onChange: (e) => {
877
+ setQuery(e.target.value);
878
+ seek({ query: e.target.value, at: 0 });
879
+ },
880
+ onKeyDown: (e) => {
881
+ if (e.key === "Enter") {
882
+ e.preventDefault();
883
+ seek({ at: (found?.at ?? 0) + (e.shiftKey ? -1 : 1) });
884
+ }
885
+ }
886
+ }
887
+ ),
888
+ /* @__PURE__ */ jsx2("small", { "aria-live": "polite", children: query ? found?.hits.length ? `${(found.at ?? 0) + 1} / ${found.hits.length}` : t.noHits : "" }),
889
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": t.previous, title: t.previous, disabled: !found?.hits.length, onClick: () => seek({ at: found.at - 1 }), children: /* @__PURE__ */ jsx2(ChevronUp, { "aria-hidden": true }) }),
890
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": t.next, title: t.next, disabled: !found?.hits.length, onClick: () => seek({ at: found.at + 1 }), children: /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true }) })
891
+ ] }),
892
+ can.edit && /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
893
+ /* @__PURE__ */ jsx2("input", { value: swap, placeholder: t.replaceWith, "aria-label": t.replaceWith, onChange: (e) => setSwap(e.target.value) }),
894
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", disabled: !found?.hits.length, onClick: () => {
895
+ replace(editor, swap, false);
896
+ seek({});
897
+ }, children: t.replaceOne }),
898
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-docs-action", disabled: !found?.hits.length, onClick: () => {
899
+ replace(editor, swap, true);
900
+ seek({});
901
+ }, children: t.replaceAll })
902
+ ] }),
903
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-check", children: [
904
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: matchCase, onChange: (e) => {
905
+ setMatchCase(e.target.checked);
906
+ seek({ matchCase: e.target.checked, at: 0 });
907
+ } }),
908
+ t.matchCase
909
+ ] })
910
+ ] })
911
+ ] });
912
+ const formats = ["bold", "italic", "underline", "strike", "code", "highlight", "textStyle", "superscript", "subscript"];
913
+ const take = (keep2) => {
914
+ if (paint && !keep2) return setPaint(null);
915
+ const { $from } = editor.state.selection;
916
+ const marks = (editor.state.storedMarks ?? $from.marks()).filter((m) => formats.includes(m.type.name)).map((m) => ({ type: m.type.name, attrs: m.attrs }));
917
+ setPaint({ marks, block: { align: $from.parent.attrs.align, line: $from.parent.attrs.line, after: $from.parent.attrs.after }, keep: keep2 });
918
+ };
919
+ const lay = () => {
920
+ if (!paint || editor.state.selection.empty) return;
921
+ const chain = editor.chain().focus();
922
+ for (const name of formats) chain.unsetMark(name);
923
+ for (const m of paint.marks) chain.setMark(m.type, m.attrs);
924
+ chain.run();
925
+ align(editor, paint.block.align);
926
+ spacing(editor, { line: paint.block.line, after: paint.block.after });
927
+ if (!paint.keep) setPaint(null);
928
+ };
929
+ const fontPicker = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
930
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-picker", "data-size": "mid", "data-pop": true, "aria-haspopup": "listbox", "aria-expanded": pop === "font", "aria-label": t.font, title: t.font, onClick: () => pick("font"), children: [
931
+ /* @__PURE__ */ jsx2("span", { style: { fontFamily: state.font ?? void 0 }, children: state.font ?? t.defaultFont }),
932
+ /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
933
+ ] }),
934
+ pop === "font" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.font, children: [
935
+ /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": !state.font, onClick: () => {
936
+ editor.chain().focus().unsetFontFamily().run();
937
+ setPop(null);
938
+ }, children: t.defaultFont }),
939
+ faces.map((f) => /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": state.font === f, style: { fontFamily: f }, onClick: () => {
940
+ editor.chain().focus().setFontFamily(f).run();
941
+ setPop(null);
942
+ }, children: f }, f))
943
+ ] })
944
+ ] });
945
+ const sizePicker = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
946
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-picker", "data-size": "small", "data-pop": true, "aria-haspopup": "listbox", "aria-expanded": pop === "size", "aria-label": t.fontSize, title: t.fontSize, onClick: () => pick("size"), children: [
947
+ state.size ? parseFloat(state.size) : t.defaultFont,
948
+ /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
949
+ ] }),
950
+ pop === "size" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.fontSize, children: [
951
+ /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": !state.size, onClick: () => {
952
+ editor.chain().focus().unsetFontSize().run();
953
+ setPop(null);
954
+ }, children: t.defaultFont }),
955
+ sizes.map((n) => /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": state.size !== null && parseFloat(state.size) === n, onClick: () => {
956
+ editor.chain().focus().setFontSize(`${n}pt`).run();
957
+ setPop(null);
958
+ }, children: n }, n))
959
+ ] })
960
+ ] });
961
+ const spacer = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
962
+ /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "spacing", "aria-label": t.lineSpacing, title: t.lineSpacing, onClick: () => pick("spacing"), children: /* @__PURE__ */ jsx2(UnfoldVertical, { "aria-hidden": true }) }),
963
+ pop === "spacing" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.lineSpacing, children: [
964
+ lines.map(([v, label]) => /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": state.line === v, onClick: () => {
965
+ spacing(editor, { line: v });
966
+ setPop(null);
967
+ }, children: label }, v)),
968
+ /* @__PURE__ */ jsx2("hr", {}),
969
+ /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": false, onClick: () => {
970
+ spacing(editor, { after: state.after === 0 ? 8 : 0 });
971
+ setPop(null);
972
+ }, children: state.after === 0 ? t.spaceAfter : t.noSpaceAfter })
973
+ ] })
974
+ ] });
975
+ const applySetup = () => {
976
+ const d = draft2;
977
+ const next = { width: d.width, height: d.height, margins: d.margins.map((v) => Math.round(v / 2.54 * 96 * 100) / 100) };
978
+ const marks = { header: d.header.trim(), footer: d.footer.trim(), numbers: d.numbers };
979
+ if (shared) shared.doc.transact(() => {
980
+ const map = shared.doc.getMap("setup");
981
+ map.set("page", next);
982
+ for (const [key, v] of Object.entries(marks)) map.set(key, v);
983
+ });
984
+ else {
985
+ setLayout(next);
986
+ setMarg(marks);
987
+ setUnsetup(true);
988
+ }
989
+ setDraft2(null);
990
+ };
991
+ const zooms = [50, 75, 90, 100, 125, 150, 200];
992
+ const zoomer = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
993
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-picker", "data-size": "small", "data-pop": true, "aria-haspopup": "listbox", "aria-expanded": pop === "zoom", "aria-label": t.zoom, title: t.zoom, onClick: () => pick("zoom"), children: [
994
+ percent,
995
+ "%",
996
+ /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
997
+ ] }),
998
+ pop === "zoom" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.zoom, children: [
999
+ docx && /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": zoom === null, onClick: () => {
1000
+ setZoom(null);
1001
+ setPop(null);
1002
+ }, children: t.fitWidth }),
1003
+ zooms.map((z) => /* @__PURE__ */ jsxs2("button", { type: "button", role: "option", "aria-selected": zoom === z, onClick: () => {
1004
+ setZoom(z);
1005
+ setPop(null);
1006
+ }, children: [
1007
+ z,
1008
+ "%"
1009
+ ] }, z))
1010
+ ] })
1011
+ ] });
1012
+ const Aligned = aligns.find(([, , value]) => value === state.align)?.[0] ?? AlignLeft;
1013
+ const aligner = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1014
+ /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "align", "aria-label": t.align, title: t.align, onClick: () => pick("align"), children: /* @__PURE__ */ jsx2(Aligned, { "aria-hidden": true }) }),
1015
+ pop === "align" && /* @__PURE__ */ jsx2("span", { className: "w100n-text-pop", role: "group", "aria-label": t.align, children: /* @__PURE__ */ jsx2("span", { className: "w100n-text-tools", children: aligns.map(([icon, label, value], i) => /* @__PURE__ */ jsx2(Fragment2, { children: tool(icon, label, () => {
1016
+ align(editor, value);
1017
+ setPop(null);
1018
+ }, state.align === value) }, i)) }) })
1019
+ ] });
1020
+ const widen = () => {
1021
+ localStorage.setItem("w100n-docs-more", more ? "" : "1");
1022
+ setMore(!more);
1023
+ };
1024
+ const main = can.edit ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
1025
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1026
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-picker", "data-size": "mid", "data-pop": true, "aria-haspopup": "listbox", "aria-expanded": pop === "style", "aria-label": t.paragraphStyle, title: t.paragraphStyle, onClick: () => pick("style"), children: [
1027
+ styles.find(([key]) => key === state.style)?.[1] ?? t.normal,
1028
+ /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
1029
+ ] }),
1030
+ 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(
1031
+ "button",
1032
+ {
1033
+ type: "button",
1034
+ role: "option",
1035
+ "data-style": key,
1036
+ "aria-selected": state.style === key,
1037
+ onClick: () => {
1038
+ run();
1039
+ setPop(null);
1040
+ },
1041
+ children: label
1042
+ },
1043
+ key
1044
+ )) })
1045
+ ] }),
1046
+ gap,
1047
+ tool(Bold, t.bold, () => editor.chain().focus().toggleBold().run(), state.bold),
1048
+ tool(Italic, t.italic, () => editor.chain().focus().toggleItalic().run(), state.italic),
1049
+ docx ? tool(Underline, t.underline, () => editor.chain().focus().toggleUnderline().run(), state.underline) : tool(Strikethrough, t.strike, () => editor.chain().focus().toggleStrike().run(), state.strike),
1050
+ !docx && tool(Code, t.code, () => editor.chain().focus().toggleCode().run(), state.code),
1051
+ docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1052
+ /* @__PURE__ */ jsxs2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "color", "aria-label": t.textColour, title: t.textColour, onClick: () => pick("color"), children: [
1053
+ /* @__PURE__ */ jsx2(Baseline, { "aria-hidden": true }),
1054
+ /* @__PURE__ */ jsx2("i", { className: "w100n-text-bar", style: { background: state.color ?? "currentColor" } })
1055
+ ] }),
1056
+ pop === "color" && swatches(inks, state.color, (c) => c ? editor.chain().focus().setColor(c).run() : editor.chain().focus().unsetColor().run(), t.automatic)
1057
+ ] }),
1058
+ gap,
1059
+ docx && aligner,
1060
+ tool(List, t.bullets, () => editor.chain().focus().toggleBulletList().run(), state.bullets),
1061
+ tool(ListOrdered, t.numbers, () => editor.chain().focus().toggleOrderedList().run(), state.numbers),
1062
+ !docx && tool(ListChecks, t.tasks, () => editor.chain().focus().toggleTaskList().run(), state.tasks),
1063
+ gap,
1064
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1065
+ /* @__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 }) }),
1066
+ pop === "link" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-form", children: [
1067
+ /* @__PURE__ */ jsx2("input", { autoFocus: true, value: href, placeholder: t.linkAddress, "aria-label": t.linkAddress, onChange: (e) => setHref(e.target.value), onKeyDown: (e) => {
1068
+ if (e.key === "Enter") putLink();
1069
+ } }),
1070
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
1071
+ state.link && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-pdf-link", onClick: () => {
1072
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
1073
+ setPop(null);
1074
+ }, children: [
1075
+ /* @__PURE__ */ jsx2(Link2Off, { "aria-hidden": true }),
1076
+ t.unlink
1077
+ ] }),
1078
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !href.trim(), onClick: putLink, children: [
1079
+ /* @__PURE__ */ jsx2(Link, { "aria-hidden": true }),
1080
+ t.apply
1081
+ ] })
1082
+ ] })
1083
+ ] })
1084
+ ] }),
1085
+ can.comment && tool(MessageSquarePlus, t.addComment, begin, void 0, state.empty),
1086
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1087
+ /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "image", "aria-label": t.image, title: t.image, onClick: () => {
1088
+ setHref("");
1089
+ pick("image");
1090
+ }, children: /* @__PURE__ */ jsx2(ImagePlus, { "aria-hidden": true }) }),
1091
+ pop === "image" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-form", children: [
1092
+ /* @__PURE__ */ jsx2(
1093
+ "input",
1094
+ {
1095
+ autoFocus: true,
1096
+ value: href,
1097
+ placeholder: t.imageLink,
1098
+ "aria-label": t.imageLink,
1099
+ onChange: (e) => setHref(e.target.value),
1100
+ onKeyDown: (e) => {
1101
+ if (e.key === "Enter" && href.trim()) {
1102
+ editor.chain().focus().setImage({ src: href.trim() }).run();
1103
+ setPop(null);
1104
+ }
1105
+ }
1106
+ }
1107
+ ),
1108
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
1109
+ docx && /* @__PURE__ */ jsxs2("label", { className: "w100n-pdf-link", children: [
1110
+ t.chooseFile,
1111
+ /* @__PURE__ */ jsx2("input", { type: "file", accept: "image/png,image/jpeg,image/gif", hidden: true, onChange: (e) => {
1112
+ const file2 = e.target.files?.[0];
1113
+ if (!file2) return;
1114
+ const reader = new FileReader();
1115
+ reader.onload = () => {
1116
+ editor.chain().focus().setImage({ src: String(reader.result), alt: file2.name }).run();
1117
+ setPop(null);
1118
+ };
1119
+ reader.readAsDataURL(file2);
1120
+ } })
1121
+ ] }),
1122
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !href.trim(), onClick: () => {
1123
+ editor.chain().focus().setImage({ src: href.trim() }).run();
1124
+ setPop(null);
1125
+ }, children: [
1126
+ /* @__PURE__ */ jsx2(ImagePlus, { "aria-hidden": true }),
1127
+ t.put
1128
+ ] })
1129
+ ] })
1130
+ ] })
1131
+ ] }),
1132
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1133
+ /* @__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 }) }),
1134
+ 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: [
1135
+ /* @__PURE__ */ jsx2("span", { children: Array.from({ length: 48 }, (_, i) => {
1136
+ const rows = Math.floor(i / 8) + 1;
1137
+ const cols = i % 8 + 1;
1138
+ return /* @__PURE__ */ jsx2(
1139
+ "button",
1140
+ {
1141
+ type: "button",
1142
+ "aria-label": `${rows} \xD7 ${cols}`,
1143
+ "data-on": rows <= grid.rows && cols <= grid.cols,
1144
+ onPointerEnter: () => setGrid({ rows, cols }),
1145
+ onClick: () => {
1146
+ editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
1147
+ setPop(null);
1148
+ }
1149
+ },
1150
+ i
1151
+ );
1152
+ }) }),
1153
+ /* @__PURE__ */ jsx2("small", { children: grid.rows ? `${grid.rows} \xD7 ${grid.cols}` : t.table })
1154
+ ] })
1155
+ ] }),
1156
+ gap,
1157
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-expanded": more, "aria-label": more ? t.fewerTools : t.moreTools, title: more ? t.fewerTools : t.moreTools, onClick: widen, children: more ? /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true }) : /* @__PURE__ */ jsx2(ChevronUp, { "aria-hidden": true }) })
1158
+ ] }) : /* @__PURE__ */ jsxs2(Fragment3, { children: [
1159
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => onPanel?.("outline"), children: [
1160
+ /* @__PURE__ */ jsx2(ListTree, { "aria-hidden": true }),
1161
+ t.outline
1162
+ ] }),
1163
+ can.comment && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1164
+ /* @__PURE__ */ jsx2("span", { className: "w100n-text-grow" }),
1165
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: state.empty, onClick: begin, children: [
1166
+ /* @__PURE__ */ jsx2(MessageSquarePlus, { "aria-hidden": true }),
1167
+ t.addComment
1168
+ ] })
1169
+ ] }),
1170
+ !!host?.comments && (can.comment || open.length > 0) && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => onPanel?.("comments"), children: [
1171
+ /* @__PURE__ */ jsx2(MessagesSquare, { "aria-hidden": true }),
1172
+ t.comments,
1173
+ /* @__PURE__ */ jsx2("b", { children: open.length })
1174
+ ] })
1175
+ ] });
1176
+ const extra = can.edit && more && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1177
+ docx && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1178
+ fontPicker,
1179
+ sizePicker,
1180
+ gap
1181
+ ] }),
1182
+ docx && tool(Strikethrough, t.strike, () => editor.chain().focus().toggleStrike().run(), state.strike),
1183
+ docx && tool(Superscript, t.superscript, () => editor.chain().focus().toggleMark("superscript").run(), state.sup),
1184
+ docx && tool(Subscript, t.subscript, () => editor.chain().focus().toggleMark("subscript").run(), state.sub),
1185
+ docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1186
+ /* @__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 }) }),
1187
+ pop === "mark" && swatches(glows, null, (c) => c ? editor.chain().focus().setHighlight({ color: c }).run() : editor.chain().focus().unsetHighlight().run(), t.noColour)
1188
+ ] }),
1189
+ docx && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1190
+ gap,
1191
+ spacer
1192
+ ] }),
1193
+ tool(ListIndentDecrease, t.outdent, () => indent(editor, -1)),
1194
+ tool(ListIndentIncrease, t.indent, () => indent(editor, 1)),
1195
+ docx && tool(ListChecks, t.tasks, () => editor.chain().focus().toggleTaskList().run(), state.tasks),
1196
+ gap,
1197
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": t.painter, title: paint ? t.painterHint : t.painter, "aria-pressed": !!paint, onClick: () => take(false), onDoubleClick: () => take(true), children: /* @__PURE__ */ jsx2(PaintRoller, { "aria-hidden": true }) }),
1198
+ tool(RemoveFormatting, t.clearFormat, () => clear(editor)),
1199
+ docx && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1200
+ gap,
1201
+ tool(FilePenLine, t.suggest, suggest, state.suggest)
1202
+ ] })
1203
+ ] });
1204
+ const context = can.edit && (state.table ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
1205
+ tool(BetweenHorizontalStart, t.rowAbove, () => editor.chain().focus().addRowBefore().run()),
1206
+ tool(BetweenHorizontalEnd, t.rowBelow, () => editor.chain().focus().addRowAfter().run()),
1207
+ tool(BetweenVerticalStart, t.columnLeft, () => editor.chain().focus().addColumnBefore().run()),
1208
+ tool(BetweenVerticalEnd, t.columnRight, () => editor.chain().focus().addColumnAfter().run()),
1209
+ tool(PanelTop, t.headerRow, () => editor.chain().focus().toggleHeaderRow().run()),
1210
+ docx && tool(TableCellsMerge, t.mergeCells, () => editor.chain().focus().mergeOrSplit().run()),
1211
+ docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1212
+ /* @__PURE__ */ jsxs2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "fill", "aria-label": t.cellFill, title: t.cellFill, onClick: () => pick("fill"), children: [
1213
+ /* @__PURE__ */ jsx2(PaintBucket, { "aria-hidden": true }),
1214
+ /* @__PURE__ */ jsx2("i", { className: "w100n-text-bar", style: { background: state.fill ?? "transparent" } })
1215
+ ] }),
1216
+ pop === "fill" && swatches(glows, state.fill, (c) => editor.chain().focus().setCellAttribute("background", c).run(), t.noColour)
1217
+ ] }),
1218
+ docx && tool(Grid3x3, t.tableBorders, () => editor.chain().focus().updateAttributes("table", { borders: state.bare ? null : "none" }).run(), !state.bare),
1219
+ tool(TableRowsSplit, t.deleteRow, () => editor.chain().focus().deleteRow().run()),
1220
+ tool(TableColumnsSplit, t.deleteColumn, () => editor.chain().focus().deleteColumn().run()),
1221
+ tool(Trash2, t.deleteTable, () => editor.chain().focus().deleteTable().run())
1222
+ ] }) : state.img && /* @__PURE__ */ jsx2(Fragment3, { children: [[Image, t.wrapInline, "inline"], [PanelLeft, t.wrapLeft, "left"], [PanelRight, t.wrapRight, "right"]].map(([icon, label, value]) => /* @__PURE__ */ jsx2(Fragment2, { children: tool(icon, label, () => editor.chain().focus().updateAttributes("image", { wrap: value === "inline" ? null : value }).run(), state.img === value) }, value)) }));
1223
+ const status = (can.edit || live) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-end", children: [
1224
+ live && (live.ready ? !live.online : live.late || live.denied) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-live", title: live.denied ? void 0 : t.offlineHint, children: [
1225
+ /* @__PURE__ */ jsx2(WifiOff, { "aria-hidden": true }),
1226
+ live.denied ? t.liveOff : t.offline
1227
+ ] }),
1228
+ state.suggest && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.suggesting }),
1229
+ can.edit && saving === "failed" && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-saved", title: `${t.save} (${mod}S)`, onClick: () => void save(), children: [
1230
+ /* @__PURE__ */ jsx2(CloudAlert, { "aria-hidden": true }),
1231
+ /* @__PURE__ */ jsx2("span", { children: t.saveFailed })
1232
+ ] }),
1233
+ 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.split(/\s+/).map((w) => w[0]).join("").slice(0, 2).toUpperCase() }, p.id)) })
1234
+ ] });
1235
+ const menubar = /* @__PURE__ */ jsx2("span", { className: "w100n-text-menus", role: "menubar", "aria-label": t.textTools, children: menus.filter(([, , list]) => list.some((x) => x && x !== "-")).map(menu) });
1236
+ const keep = (e) => {
1237
+ if (e.target.closest("button")) e.preventDefault();
1238
+ };
1239
+ 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;
1240
+ return /* @__PURE__ */ jsxs2("div", { ref: root, className: "w100n-text", "data-kind": kind, "data-docs-find": true, onKeyDown: keys, children: [
1241
+ head ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
1242
+ head.menus && createPortal(/* @__PURE__ */ jsx2("div", { className: "w100n-text-ribbon w100n-text-docbar", onKeyDown: rove, onPointerDown: keep, children: menubar }), head.menus),
1243
+ head.status && status && createPortal(/* @__PURE__ */ jsx2("div", { className: "w100n-text-ribbon w100n-text-docbar", onPointerDown: keep, children: status }), head.status)
1244
+ ] }) : /* @__PURE__ */ jsxs2("div", { className: "w100n-text-ribbon w100n-text-top", onKeyDown: rove, onPointerDown: keep, children: [
1245
+ menubar,
1246
+ status
1247
+ ] }),
1248
+ /* @__PURE__ */ jsx2("div", { ref: scroller, className: "w100n-text-scroll", "data-paint": !!paint, onMouseUp: lay, onClick: (e) => {
1249
+ const atom = can.edit ? e.target.closest(".w100n-math, sup[data-footnote]") : null;
1250
+ if (atom) {
1251
+ const at = editor.view.posAtDOM(atom, 0);
1252
+ const node = editor.state.doc.nodeAt(at);
1253
+ if (node?.type.name === "math") setDialog({ kind: "formula", latex: node.attrs.latex, at });
1254
+ if (node?.type.name === "footnote") setDialog({ kind: "note", text: node.attrs.text, at });
1255
+ return;
1256
+ }
1257
+ const id = e.target.closest("[data-note]")?.getAttribute("data-note");
1258
+ if (!id) return;
1259
+ setActive(id);
1260
+ onPanel?.("comments");
1261
+ }, 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 }) }) }) }),
1262
+ /* @__PURE__ */ jsx2("div", { className: "w100n-text-ribbon w100n-text-corner", "data-side": "left", onPointerDown: keep, children: /* @__PURE__ */ jsxs2("div", { className: "w100n-text-tools", role: "group", "aria-label": t.zoom, children: [
1263
+ tool(Minus, t.zoomOut, () => zoomBy(-10)),
1264
+ zoomer,
1265
+ tool(Plus, t.zoomIn, () => zoomBy(10))
1266
+ ] }) }),
1267
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-text-ribbon w100n-text-island", role: "toolbar", "aria-label": t.textTools, onKeyDown: rove, onPointerDown: keep, children: [
1268
+ context && /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": state.table ? t.table : t.image, children: context }),
1269
+ extra && /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": t.moreTools, children: extra }),
1270
+ /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": t.textTools, children: main })
1271
+ ] }),
1272
+ /* @__PURE__ */ jsx2("div", { className: "w100n-text-ribbon w100n-text-corner", "data-side": "right", onPointerDown: keep, children: /* @__PURE__ */ jsxs2("div", { className: "w100n-text-tools", role: "group", "aria-label": t.findReplace, children: [
1273
+ can.edit && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1274
+ tool(Undo2, t.undo, () => editor.chain().focus().undo().run(), void 0, !state.undo),
1275
+ tool(Redo2, t.redo, () => editor.chain().focus().redo().run(), void 0, !state.redo),
1276
+ gap
1277
+ ] }),
1278
+ finder
1279
+ ] }) }),
1280
+ dialog && /* @__PURE__ */ jsx2("div", { className: "w100n-pdf-back", onKeyDown: (e) => {
1281
+ if (e.key === "Escape") {
1282
+ e.stopPropagation();
1283
+ setDialog(null);
1284
+ }
1285
+ }, children: dialog.kind === "symbol" ? /* @__PURE__ */ jsxs2("div", { className: "w100n-pdf-dialog w100n-insert", role: "dialog", "aria-modal": true, "aria-label": t.symbols, children: [
1286
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-new-head", children: [
1287
+ /* @__PURE__ */ jsx2("span", { className: "w100n-new-tile", "data-tone": "pink", children: /* @__PURE__ */ jsx2(Omega, { "aria-hidden": true }) }),
1288
+ /* @__PURE__ */ jsx2("b", { className: "w100n-pdf-dialog-title", children: t.symbols }),
1289
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-docs-close", onClick: () => setDialog(null), "aria-label": t.close, title: t.close, children: /* @__PURE__ */ jsx2(X, { "aria-hidden": true }) })
1290
+ ] }),
1291
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-docs-field w100n-docs-search", children: [
1292
+ /* @__PURE__ */ jsx2(Search, { "aria-hidden": true }),
1293
+ /* @__PURE__ */ jsx2("input", { autoFocus: true, value: dialog.query, placeholder: t.symbolSearch, "aria-label": t.symbolSearch, onChange: (e) => setDialog({ kind: "symbol", query: e.target.value }) })
1294
+ ] }),
1295
+ /* @__PURE__ */ jsx2("div", { className: "w100n-insert-grid", role: "group", "aria-label": t.symbols, children: symbols.filter(([ch, name]) => !dialog.query.trim() || name.includes(dialog.query.trim().toLowerCase()) || ch === dialog.query.trim()).map(([ch, name]) => /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": name, title: name, onClick: () => {
1296
+ editor.chain().focus().insertContent(ch).run();
1297
+ setDialog(null);
1298
+ }, children: ch }, ch)) })
1299
+ ] }) : dialog.kind === "formula" ? /* @__PURE__ */ jsxs2("form", { className: "w100n-pdf-dialog w100n-insert", role: "dialog", "aria-modal": true, "aria-label": t.formula, onSubmit: (e) => {
1300
+ e.preventDefault();
1301
+ const latex = dialog.latex.trim();
1302
+ if (!latex) return;
1303
+ if (dialog.at !== null) editor.chain().focus().command(({ tr }) => {
1304
+ tr.setNodeMarkup(dialog.at, void 0, { latex });
1305
+ return true;
1306
+ }).run();
1307
+ else editor.chain().focus().insertContent({ type: "math", attrs: { latex } }).run();
1308
+ setDialog(null);
1309
+ }, children: [
1310
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-new-head", children: [
1311
+ /* @__PURE__ */ jsx2("span", { className: "w100n-new-tile", "data-tone": "blue", children: /* @__PURE__ */ jsx2(Sigma, { "aria-hidden": true }) }),
1312
+ /* @__PURE__ */ jsx2("b", { className: "w100n-pdf-dialog-title", children: t.formula }),
1313
+ /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.formulaHint }),
1314
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-docs-close", onClick: () => setDialog(null), "aria-label": t.close, title: t.close, children: /* @__PURE__ */ jsx2(X, { "aria-hidden": true }) })
1315
+ ] }),
1316
+ /* @__PURE__ */ jsx2("input", { ref: field, autoFocus: true, className: "w100n-insert-latex", value: dialog.latex, "aria-label": t.formula, placeholder: "F = 900 + 12 \\\\cdot n", onChange: (e) => setDialog({ ...dialog, latex: e.target.value }) }),
1317
+ /* @__PURE__ */ jsx2("div", { className: "w100n-insert-tools", role: "group", "aria-label": t.formula, children: helpers.map(([label, code]) => /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => {
1318
+ const el = field.current;
1319
+ const at = el.selectionStart ?? dialog.latex.length;
1320
+ const latex = dialog.latex.slice(0, at) + code + dialog.latex.slice(el.selectionEnd ?? at);
1321
+ setDialog({ ...dialog, latex });
1322
+ const caret = at + (code.indexOf("{}") >= 0 ? code.indexOf("{}") + 1 : code.length);
1323
+ requestAnimationFrame(() => {
1324
+ el.focus();
1325
+ el.setSelectionRange(caret, caret);
1326
+ });
1327
+ }, children: label }, label)) }),
1328
+ /* @__PURE__ */ jsx2("div", { className: "w100n-insert-preview", "aria-live": "polite", dangerouslySetInnerHTML: { __html: dialog.latex.trim() ? mathml(dialog.latex) : "" } }),
1329
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
1330
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setDialog(null), children: t.cancel }),
1331
+ /* @__PURE__ */ jsxs2("button", { type: "submit", className: "w100n-docs-action", disabled: !dialog.latex.trim(), children: [
1332
+ /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
1333
+ dialog.at !== null ? t.update : t.put
1334
+ ] })
1335
+ ] })
1336
+ ] }) : /* @__PURE__ */ jsxs2("form", { className: "w100n-pdf-dialog w100n-insert", role: "dialog", "aria-modal": true, "aria-label": t.footnote, onSubmit: (e) => {
1337
+ e.preventDefault();
1338
+ const text = dialog.text.trim();
1339
+ if (!text) return;
1340
+ if (dialog.at !== null) editor.chain().focus().command(({ tr }) => {
1341
+ tr.setNodeAttribute(dialog.at, "text", text);
1342
+ return true;
1343
+ }).run();
1344
+ else editor.chain().focus().insertContent({ type: "footnote", attrs: { id: null, text } }).run();
1345
+ setDialog(null);
1346
+ }, children: [
1347
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-new-head", children: [
1348
+ /* @__PURE__ */ jsx2("span", { className: "w100n-new-tile", "data-tone": "violet", children: /* @__PURE__ */ jsx2(Superscript, { "aria-hidden": true }) }),
1349
+ /* @__PURE__ */ jsx2("b", { className: "w100n-pdf-dialog-title", children: t.footnote }),
1350
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-docs-close", onClick: () => setDialog(null), "aria-label": t.close, title: t.close, children: /* @__PURE__ */ jsx2(X, { "aria-hidden": true }) })
1351
+ ] }),
1352
+ /* @__PURE__ */ jsx2(
1353
+ "textarea",
1354
+ {
1355
+ autoFocus: true,
1356
+ className: "w100n-insert-note",
1357
+ value: dialog.text,
1358
+ "aria-label": t.footnoteText,
1359
+ placeholder: t.footnoteText,
1360
+ onChange: (e) => setDialog({ ...dialog, text: e.target.value }),
1361
+ onKeyDown: (e) => {
1362
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) e.currentTarget.form?.requestSubmit();
1363
+ }
1364
+ }
1365
+ ),
1366
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
1367
+ dialog.at !== null && /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => {
1368
+ editor.chain().focus().deleteRange({ from: dialog.at, to: dialog.at + 1 }).run();
1369
+ setDialog(null);
1370
+ }, children: t.removeFootnote }),
1371
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setDialog(null), children: t.cancel }),
1372
+ /* @__PURE__ */ jsxs2("button", { type: "submit", className: "w100n-docs-action", disabled: !dialog.text.trim(), children: [
1373
+ /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
1374
+ dialog.at !== null ? t.update : t.put
1375
+ ] })
1376
+ ] })
1377
+ ] }) }),
1378
+ draft2 && /* @__PURE__ */ jsx2("div", { className: "w100n-pdf-back", onKeyDown: (e) => {
1379
+ if (e.key === "Escape") {
1380
+ e.stopPropagation();
1381
+ setDraft2(null);
1382
+ }
1383
+ }, children: /* @__PURE__ */ jsxs2("form", { className: "w100n-pdf-dialog w100n-setup", role: "dialog", "aria-modal": true, "aria-label": t.pageSetup, onSubmit: (e) => {
1384
+ e.preventDefault();
1385
+ applySetup();
1386
+ }, children: [
1387
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-new-head", children: [
1388
+ /* @__PURE__ */ jsx2("span", { className: "w100n-new-tile", "data-tone": "blue", children: /* @__PURE__ */ jsx2(Ruler, { "aria-hidden": true }) }),
1389
+ /* @__PURE__ */ jsx2("b", { className: "w100n-pdf-dialog-title", children: t.pageSetup }),
1390
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-docs-close", onClick: () => setDraft2(null), "aria-label": t.close, title: t.close, children: /* @__PURE__ */ jsx2(X, { "aria-hidden": true }) })
1391
+ ] }),
1392
+ /* @__PURE__ */ jsx2("p", { className: "w100n-docs-caption", children: t.pageSize }),
1393
+ /* @__PURE__ */ jsx2("div", { className: "w100n-docs-chips", role: "group", "aria-label": t.pageSize, children: [["A4", a4], ["Letter", letter], ["Legal", legal]].map(([label, size]) => {
1394
+ const land = draft2.width > draft2.height;
1395
+ const on = Math.abs(Math.min(draft2.width, draft2.height) - size.width) < 2 && Math.abs(Math.max(draft2.width, draft2.height) - size.height) < 2;
1396
+ return /* @__PURE__ */ jsx2("button", { type: "button", "aria-pressed": on, onClick: () => setDraft2({ ...draft2, width: land ? size.height : size.width, height: land ? size.width : size.height }), children: label }, label);
1397
+ }) }),
1398
+ /* @__PURE__ */ jsx2("p", { className: "w100n-docs-caption", children: t.orientation }),
1399
+ /* @__PURE__ */ jsx2("div", { className: "w100n-docs-chips", role: "group", "aria-label": t.orientation, children: [[false, t.portrait], [true, t.landscape]].map(([land, label]) => /* @__PURE__ */ jsx2(
1400
+ "button",
1401
+ {
1402
+ type: "button",
1403
+ "aria-pressed": draft2.width > draft2.height === land,
1404
+ onClick: () => {
1405
+ if (draft2.width > draft2.height !== land) setDraft2({ ...draft2, width: draft2.height, height: draft2.width });
1406
+ },
1407
+ children: label
1408
+ },
1409
+ label
1410
+ )) }),
1411
+ /* @__PURE__ */ jsx2("p", { className: "w100n-docs-caption", children: t.marginsCm }),
1412
+ /* @__PURE__ */ jsx2("div", { className: "w100n-docs-chips", role: "group", "aria-label": t.margins, children: margins.map(([label, values]) => /* @__PURE__ */ jsx2("button", { type: "button", "aria-pressed": values.every((v, n) => Math.abs(cm(v) - draft2.margins[n]) < 0.05), onClick: () => setDraft2({ ...draft2, margins: values.map(cm) }), children: label }, label)) }),
1413
+ /* @__PURE__ */ jsx2("div", { className: "w100n-setup-grid", children: [t.mTop, t.mRight, t.mBottom, t.mLeft].map((label, n) => /* @__PURE__ */ jsxs2("label", { children: [
1414
+ /* @__PURE__ */ jsx2("span", { children: label }),
1415
+ /* @__PURE__ */ jsx2(
1416
+ "input",
1417
+ {
1418
+ className: "w100n-docs-field",
1419
+ type: "number",
1420
+ min: 0,
1421
+ max: 10,
1422
+ step: 0.1,
1423
+ value: draft2.margins[n],
1424
+ onChange: (e) => setDraft2({ ...draft2, margins: draft2.margins.map((v, i) => i === n ? Math.max(0, Math.min(10, Number(e.target.value) || 0)) : v) })
1425
+ }
1426
+ )
1427
+ ] }, n)) }),
1428
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-line", children: [
1429
+ /* @__PURE__ */ jsx2("span", { children: t.headerText }),
1430
+ /* @__PURE__ */ jsx2("input", { className: "w100n-docs-field", value: draft2.header, onChange: (e) => setDraft2({ ...draft2, header: e.target.value }) })
1431
+ ] }),
1432
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-line", children: [
1433
+ /* @__PURE__ */ jsx2("span", { children: t.footerText }),
1434
+ /* @__PURE__ */ jsx2("input", { className: "w100n-docs-field", value: draft2.footer, onChange: (e) => setDraft2({ ...draft2, footer: e.target.value }) })
1435
+ ] }),
1436
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-check", children: [
1437
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: draft2.numbers, onChange: (e) => setDraft2({ ...draft2, numbers: e.target.checked }) }),
1438
+ t.pageNumbers
1439
+ ] }),
1440
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-pdf-dialog-end", children: [
1441
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setDraft2(null), children: t.cancel }),
1442
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", onClick: applySetup, children: [
1443
+ /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
1444
+ t.apply
1445
+ ] })
1446
+ ] })
1447
+ ] }) }),
1448
+ slot && createPortal(/* @__PURE__ */ jsxs2("div", { className: "w100n-text-notes", children: [
1449
+ draft && /* @__PURE__ */ jsxs2("div", { className: "w100n-text-card w100n-text-draft", children: [
1450
+ /* @__PURE__ */ jsx2("blockquote", { children: draft.quote }),
1451
+ /* @__PURE__ */ jsx2(
1452
+ "textarea",
1453
+ {
1454
+ autoFocus: true,
1455
+ value: draft.body,
1456
+ placeholder: t.writeComment,
1457
+ "aria-label": t.writeComment,
1458
+ onChange: (e) => setDraft({ ...draft, body: e.target.value }),
1459
+ onKeyDown: (e) => {
1460
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void post();
1461
+ }
1462
+ }
1463
+ ),
1464
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
1465
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: drop, children: t.cancel }),
1466
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !draft.body.trim() || posting === "busy", onClick: () => void post(), children: [
1467
+ /* @__PURE__ */ jsx2(MessageSquarePlus, { "aria-hidden": true }),
1468
+ t.post
1469
+ ] })
1470
+ ] })
1471
+ ] }),
1472
+ suggested.length > 0 && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1473
+ /* @__PURE__ */ jsxs2("p", { className: "w100n-docs-caption", children: [
1474
+ /* @__PURE__ */ jsx2(FilePenLine, { "aria-hidden": true }),
1475
+ t.suggestions,
1476
+ /* @__PURE__ */ jsx2("span", { children: suggested.length })
1477
+ ] }),
1478
+ suggested.map((c, i) => /* @__PURE__ */ jsxs2("article", { className: "w100n-text-card w100n-text-change", "data-kind": c.kind, onClick: () => jump(c.from), children: [
1479
+ /* @__PURE__ */ jsxs2("header", { children: [
1480
+ /* @__PURE__ */ jsx2("span", { className: "w100n-text-face", "aria-hidden": true, children: (c.author || "?").slice(0, 1).toUpperCase() }),
1481
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-text-who", children: [
1482
+ /* @__PURE__ */ jsx2("b", { children: c.author || t.someone }),
1483
+ c.date && /* @__PURE__ */ jsx2("small", { children: when(c.date) })
1484
+ ] })
1485
+ ] }),
1486
+ /* @__PURE__ */ jsxs2("p", { children: [
1487
+ c.kind === "insertion" ? t.added : t.removed,
1488
+ " ",
1489
+ /* @__PURE__ */ jsx2("q", { children: c.text })
1490
+ ] }),
1491
+ can.edit && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-actions", onClick: (e) => e.stopPropagation(), children: [
1492
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "green", onClick: () => decide(editor, true, c.id), children: [
1493
+ /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
1494
+ t.accept
1495
+ ] }),
1496
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", onClick: () => decide(editor, false, c.id), children: [
1497
+ /* @__PURE__ */ jsx2(X, { "aria-hidden": true }),
1498
+ t.reject
1499
+ ] })
1500
+ ] })
1501
+ ] }, i))
1502
+ ] }),
1503
+ posting === "failed" && /* @__PURE__ */ jsx2("p", { className: "w100n-docs-muted", role: "alert", children: t.commentFailed }),
1504
+ failed && /* @__PURE__ */ jsx2("p", { className: "w100n-docs-muted", role: "alert", children: t.changeFailed }),
1505
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-docs-row", children: [
1506
+ /* @__PURE__ */ jsxs2("p", { className: "w100n-docs-caption", children: [
1507
+ /* @__PURE__ */ jsx2(MessagesSquare, { "aria-hidden": true }),
1508
+ t.comments,
1509
+ /* @__PURE__ */ jsx2("span", { children: open.length })
1510
+ ] }),
1511
+ /* @__PURE__ */ jsx2("span", { className: "w100n-docs-chips", children: /* @__PURE__ */ jsx2("button", { type: "button", "aria-pressed": resolved, onClick: () => setResolved(!resolved), children: t.showResolved }) })
1512
+ ] }),
1513
+ !threads.length && !draft && /* @__PURE__ */ jsx2("p", { className: "w100n-docs-muted", children: can.comment ? t.commentHint : t.noComments }),
1514
+ threads.map((c) => /* @__PURE__ */ jsxs2("article", { className: "w100n-text-card", "aria-current": active === c.id, "data-resolved": c.resolved, onClick: () => show(c), children: [
1515
+ person(c),
1516
+ c.quote && /* @__PURE__ */ jsx2("blockquote", { children: c.quote }),
1517
+ words(c),
1518
+ all.filter((r) => r.replyTo === c.id).map((r) => /* @__PURE__ */ jsxs2("div", { className: "w100n-text-reply", children: [
1519
+ person(r),
1520
+ words(r)
1521
+ ] }, r.id)),
1522
+ reply?.id === c.id && /* @__PURE__ */ jsxs2("div", { className: "w100n-text-reply", onClick: (e) => e.stopPropagation(), children: [
1523
+ /* @__PURE__ */ jsx2(
1524
+ "textarea",
1525
+ {
1526
+ autoFocus: true,
1527
+ value: reply.body,
1528
+ placeholder: t.writeComment,
1529
+ "aria-label": t.reply,
1530
+ onChange: (e) => setReply({ ...reply, body: e.target.value }),
1531
+ onKeyDown: (e) => {
1532
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void answer(c);
1533
+ }
1534
+ }
1535
+ ),
1536
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-pdf-dialog-end", children: [
1537
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setReply(null), children: t.cancel }),
1538
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !reply.body.trim() || posting === "busy", onClick: () => void answer(c), children: [
1539
+ /* @__PURE__ */ jsx2(Reply, { "aria-hidden": true }),
1540
+ t.reply
1541
+ ] })
1542
+ ] })
1543
+ ] }),
1544
+ can.comment && reply?.id !== c.id && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-actions", onClick: (e) => e.stopPropagation(), children: [
1545
+ c.resolved && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.resolved }),
1546
+ !c.resolved && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", onClick: () => setReply({ id: c.id, body: "" }), children: [
1547
+ /* @__PURE__ */ jsx2(Reply, { "aria-hidden": true }),
1548
+ t.reply
1549
+ ] }),
1550
+ host?.resolveComment && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "green", onClick: () => void settle(c), children: [
1551
+ c.resolved ? /* @__PURE__ */ jsx2(RotateCcw, { "aria-hidden": true }) : /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
1552
+ c.resolved ? t.reopen : t.resolve
1553
+ ] })
1554
+ ] })
1555
+ ] }, c.id))
1556
+ ] }), slot)
1557
+ ] });
1558
+ }
1559
+ export {
1560
+ TextEditor as default
1561
+ };
1562
+ //# sourceMappingURL=TextEditor-NSICFE6U.js.map