@w100n/documents 0.12.1 → 0.14.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 (52) 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-G3YWPH5V.js → SheetView-4TPL7DES.js} +51 -10
  4. package/dist/SheetView-4TPL7DES.js.map +1 -0
  5. package/dist/{TextEditor-SPEVB5HX.js → TextEditor-VBHWUOZR.js} +636 -225
  6. package/dist/TextEditor-VBHWUOZR.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-UWASFTDF.js → chunk-5MH4WDW4.js} +133 -20
  10. package/dist/chunk-5MH4WDW4.js.map +1 -0
  11. package/dist/{chunk-BKUFGMEA.js → chunk-DR3TTJWZ.js} +410 -29
  12. package/dist/chunk-DR3TTJWZ.js.map +1 -0
  13. package/dist/{chunk-5NT5ALOF.js → chunk-RCQODE2Z.js} +2 -2
  14. package/dist/collab.cjs +523 -46
  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 +2271 -882
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.css +1037 -189
  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 +804 -336
  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 +12 -1
  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 +13 -2
  39. package/dist/memory.js.map +1 -1
  40. package/dist/{types-B5oF25dn.d.ts → types-BTsVooZw.d.cts} +8 -1
  41. package/dist/{types-B5oF25dn.d.cts → types-BTsVooZw.d.ts} +8 -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-G3YWPH5V.js.map +0 -1
  46. package/dist/TextEditor-SPEVB5HX.js.map +0 -1
  47. package/dist/chunk-BKUFGMEA.js.map +0 -1
  48. package/dist/chunk-UWASFTDF.js.map +0 -1
  49. package/dist/grid-EI5JNZAA.js.map +0 -1
  50. package/dist/write-DaVtTJCS.d.cts +0 -6
  51. package/dist/write-DaVtTJCS.d.ts +0 -6
  52. /package/dist/{chunk-5NT5ALOF.js.map → chunk-RCQODE2Z.js.map} +0 -0
@@ -4,11 +4,12 @@ import {
4
4
  } from "./chunk-QDH7EZAF.js";
5
5
  import {
6
6
  useLive
7
- } from "./chunk-5NT5ALOF.js";
7
+ } from "./chunk-RCQODE2Z.js";
8
8
  import {
9
9
  a4,
10
10
  align,
11
11
  changes,
12
+ cleanPaste,
12
13
  clear,
13
14
  decide,
14
15
  entries,
@@ -19,10 +20,13 @@ import {
19
20
  locate,
20
21
  notes,
21
22
  paging,
23
+ replace,
24
+ searching,
25
+ spacing,
22
26
  suggesting,
23
27
  toMarkdown,
24
28
  track
25
- } from "./chunk-UWASFTDF.js";
29
+ } from "./chunk-5MH4WDW4.js";
26
30
  import {
27
31
  rove,
28
32
  useRestore
@@ -30,7 +34,7 @@ import {
30
34
  import {
31
35
  readDocx,
32
36
  writeDocx
33
- } from "./chunk-BKUFGMEA.js";
37
+ } from "./chunk-DR3TTJWZ.js";
34
38
  import "./chunk-ISOOUYLI.js";
35
39
 
36
40
  // src/editor/TextEditor.tsx
@@ -47,8 +51,9 @@ import {
47
51
  BetweenVerticalStart,
48
52
  Bold,
49
53
  Check,
50
- CheckCheck,
51
54
  ChevronDown,
55
+ ChevronUp,
56
+ CloudAlert,
52
57
  Code,
53
58
  FilePenLine,
54
59
  Highlighter,
@@ -62,29 +67,36 @@ import {
62
67
  ListIndentIncrease,
63
68
  ListOrdered,
64
69
  ListTree,
65
- Maximize2,
70
+ Grid3x3,
71
+ Image,
66
72
  MessageSquarePlus,
67
73
  MessagesSquare,
68
- Minus,
69
- MoveHorizontal,
74
+ PaintBucket,
75
+ PaintRoller,
76
+ Search,
77
+ PanelLeft,
78
+ PanelRight,
79
+ Ruler,
70
80
  PanelTop,
71
81
  Pencil,
72
82
  Plus,
83
+ Minus,
73
84
  Redo2,
74
85
  RemoveFormatting,
75
86
  Reply,
76
87
  RotateCcw,
77
88
  Save,
78
- SeparatorHorizontal,
79
89
  Strikethrough,
80
90
  Table,
91
+ Subscript,
92
+ Superscript,
81
93
  TableCellsMerge,
82
94
  TableColumnsSplit,
83
- TableOfContents,
84
95
  TableRowsSplit,
85
96
  Trash2,
86
97
  Underline,
87
98
  Undo2,
99
+ UnfoldVertical,
88
100
  WifiOff,
89
101
  X
90
102
  } from "lucide-react";
@@ -143,10 +155,14 @@ function DocxView({ bytes, onOutline, failed }) {
143
155
 
144
156
  // src/editor/TextEditor.tsx
145
157
  import { Fragment as Fragment3, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
158
+ var legal = { width: 816, height: 1344 };
159
+ var cm = (px) => Math.round(px / 96 * 254) / 100;
146
160
  var inks = ["#0f172a", "#475569", "#dc2626", "#ea580c", "#ca8a04", "#16a34a", "#0891b2", "#2563eb", "#7c3aed", "#db2777"];
147
161
  var glows = ["#fef08a", "#bbf7d0", "#bfdbfe", "#fbcfe8", "#fed7aa", "#ddd6fe"];
162
+ var faces = ["Arial", "Calibri", "Cambria", "Courier New", "Georgia", "Inter", "Roboto", "Times New Roman", "Verdana"];
163
+ var sizes = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 72];
148
164
  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 }) {
165
+ function TextEditor({ kind, bytes, t, language, rights, host, together, documentId, onSaved, onOutline, onReading, onPanel, slot, head, file, guard }) {
150
166
  const docx = kind === "docx";
151
167
  const can = { edit: !!host?.save && !!rights?.edit, comment: !!host?.comment && !!rights && (rights.comment || rights.edit) };
152
168
  const source = useLoad(async () => {
@@ -160,8 +176,9 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
160
176
  const baseline = useRef2(null);
161
177
  const anchors = useRef2(/* @__PURE__ */ new Map());
162
178
  const scroller = useRef2(null);
163
- const [tab, setTab] = useState2(can.edit ? "home" : can.comment ? "review" : "view");
179
+ const root = useRef2(null);
164
180
  const [pop, setPop] = useState2(null);
181
+ const [more, setMore] = useState2(() => typeof localStorage !== "undefined" && localStorage.getItem("w100n-docs-more") === "1");
165
182
  const [zoom, setZoom] = useState2(null);
166
183
  const [layout, setLayout] = useState2(null);
167
184
  const [room, setRoom] = useState2(0);
@@ -178,6 +195,14 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
178
195
  const [editing, setEditing] = useState2(null);
179
196
  const [dropping, setDropping] = useState2(null);
180
197
  const [failed, setFailed] = useState2(false);
198
+ const [paint, setPaint] = useState2(null);
199
+ const [query, setQuery] = useState2("");
200
+ const [swap, setSwap] = useState2("");
201
+ const [matchCase, setMatchCase] = useState2(false);
202
+ const [count, setCount] = useState2({ words: 0, pages: 0 });
203
+ const [marg, setMarg] = useState2(null);
204
+ const [unsetup, setUnsetup] = useState2(false);
205
+ const [draft2, setDraft2] = useState2(null);
181
206
  useRestore(pop !== null);
182
207
  useRestore(editing !== null);
183
208
  useRestore(dropping !== null);
@@ -187,7 +212,7 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
187
212
  editable: can.edit && (!live || !!shared),
188
213
  immediatelyRender: true,
189
214
  shouldRerenderOnTransaction: false,
190
- editorProps: { attributes: { class: "w100n-docs-prose w100n-text-body", "aria-label": t.documentText } },
215
+ editorProps: { attributes: { class: "w100n-docs-prose w100n-text-body", "aria-label": t.documentText }, transformPastedHTML: cleanPaste },
191
216
  onCreate: ({ editor: made }) => {
192
217
  if (source.data && !live) baseline.current = { bytes, doc: made.state.doc, base: source.data.md && track(source.data.md, made.state.doc) };
193
218
  }
@@ -213,6 +238,15 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
213
238
  link: e.isActive("link"),
214
239
  color: e.getAttributes("textStyle").color ?? null,
215
240
  glow: e.isActive("highlight"),
241
+ font: e.getAttributes("textStyle").fontFamily ?? null,
242
+ size: e.getAttributes("textStyle").fontSize ?? null,
243
+ fill: e.getAttributes("tableCell").background ?? e.getAttributes("tableHeader").background ?? null,
244
+ bare: e.getAttributes("table").borders === "none",
245
+ sup: e.isActive("superscript"),
246
+ sub: e.isActive("subscript"),
247
+ img: e.isActive("image") ? e.getAttributes("image").wrap ?? "inline" : null,
248
+ line: block.attrs.line ?? null,
249
+ after: block.attrs.after ?? null,
216
250
  undo: e.can().undo(),
217
251
  redo: e.can().redo(),
218
252
  empty,
@@ -223,6 +257,7 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
223
257
  });
224
258
  return at;
225
259
  })(),
260
+ find: (({ at, hits }) => `${at}/${hits.length}`)(searching.getState(e.state)),
226
261
  suggest: !!suggesting.getState(e.state)?.on,
227
262
  revs: docx ? changes(e.state.doc) : [],
228
263
  dirty: !!baseline.current && !e.state.doc.eq(baseline.current.doc)
@@ -230,9 +265,43 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
230
265
  }
231
266
  });
232
267
  const page = layout ?? source.data?.page ?? a4;
268
+ const print = () => {
269
+ if (!editor) return;
270
+ const frame = document.createElement("iframe");
271
+ frame.style.cssText = "position:fixed;width:0;height:0;border:0;opacity:0";
272
+ document.body.append(frame);
273
+ const [mt, mr, mb, ml] = page.margins;
274
+ const size = docx ? `${page.width}px ${page.height}px` : "A4";
275
+ frame.contentDocument.open();
276
+ frame.contentDocument.write(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>${editor.state.doc.firstChild?.textContent.slice(0, 80).replace(/[<&]/g, "") ?? ""}</title><style>
277
+ @page { size: ${size}; margin: ${docx ? `${mt}px ${mr}px ${mb}px ${ml}px` : "20mm"}; }
278
+ body { margin: 0; font: 11pt/1.5 Arial, sans-serif; color: #111; }
279
+ h1 { font-size: 20pt; } h2 { font-size: 15pt; } h3 { font-size: 13pt; }
280
+ 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; }
281
+ 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; }
282
+ [data-type='page-break'], .page-break { break-after: page; height: 0; overflow: hidden; }
283
+ </style></head><body>${editor.getHTML()}</body></html>`);
284
+ frame.contentDocument.close();
285
+ frame.contentWindow.focus();
286
+ frame.contentWindow.print();
287
+ setTimeout(() => frame.remove(), 1e3);
288
+ };
289
+ const marginal = marg ?? (source.data && "marginal" in source.data ? source.data.marginal : null) ?? { header: "", footer: "", numbers: false };
290
+ useEffect(() => {
291
+ if (editor && docx && source.data) editor.view.dispatch(editor.state.tr.setMeta(paging, { page, marks: { header: marginal.header, footer: marginal.footer } }));
292
+ }, [editor, source.data, page, marginal.header, marginal.footer]);
233
293
  useEffect(() => {
234
- if (editor && docx && source.data) editor.view.dispatch(editor.state.tr.setMeta(paging, { page }));
235
- }, [editor, source.data, page]);
294
+ if (!shared || !docx) return;
295
+ const map = shared.doc.getMap("setup");
296
+ const sync = () => {
297
+ const v = map.toJSON();
298
+ if (v.page) setLayout(v.page);
299
+ if (v.header !== void 0 || v.footer !== void 0 || v.numbers !== void 0) setMarg({ header: v.header ?? "", footer: v.footer ?? "", numbers: !!v.numbers });
300
+ };
301
+ sync();
302
+ map.observe(sync);
303
+ return () => map.unobserve(sync);
304
+ }, [shared, docx]);
236
305
  const loaded = useLoad((signal) => host?.comments ? host.comments(documentId, { signal }) : Promise.resolve([]), [host, documentId]);
237
306
  const all = [...loaded.data ?? [], ...added].flatMap((c) => c.id in changed ? changed[c.id] ?? [] : [c]).filter((c) => !c.replyTo || changed[c.replyTo] !== null);
238
307
  const open = all.filter((c) => !c.replyTo && !c.resolved);
@@ -242,8 +311,8 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
242
311
  if (!editor || !source.data) return;
243
312
  const have = new Map(notes.getState(editor.state).spots.map((s) => [s.id, s]));
244
313
  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] }));
314
+ const found2 = locate(editor.state.doc, fresh.map((c) => ({ id: c.id, quote: c.quote, anchor: anchors.current.get(c.id) })));
315
+ editor.view.dispatch(editor.state.tr.setMeta(notes, { spots: [...open.flatMap((c) => have.get(c.id) ?? []), ...found2] }));
247
316
  }, [editor, source.data, openIds]);
248
317
  useEffect(() => {
249
318
  if (editor && source.data) editor.view.dispatch(editor.state.tr.setMeta(notes, { active }));
@@ -261,6 +330,8 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
261
330
  editor.commands.setTextSelection(offset + 1);
262
331
  } });
263
332
  });
333
+ const text = editor.state.doc.textBetween(0, editor.state.doc.content.size, " ", " ");
334
+ setCount({ words: (text.match(/[\p{L}\p{N}]+(?:['’-][\p{L}\p{N}]+)*/gu) ?? []).length, pages: scroller.current?.querySelectorAll(".w100n-text-gap").length ?? 0 });
264
335
  const key = JSON.stringify(list.map((h) => [h.id, h.level, h.text]));
265
336
  if (key === last2) return;
266
337
  last2 = key;
@@ -277,7 +348,35 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
277
348
  editor.off("update", later);
278
349
  };
279
350
  }, [editor, source.data, onOutline]);
280
- const dirty = live ? live.pending : !!state?.dirty;
351
+ useEffect(() => {
352
+ const el = scroller.current;
353
+ if (!el || !editor || !onReading) return;
354
+ let frame = 0;
355
+ let last2 = "";
356
+ const look = () => {
357
+ cancelAnimationFrame(frame);
358
+ frame = requestAnimationFrame(() => {
359
+ const line = el.getBoundingClientRect().top + el.clientHeight / 3;
360
+ let id = null;
361
+ editor.state.doc.forEach((node, offset) => {
362
+ const dom = node.type.name === "heading" && node.textContent.trim() ? editor.view.nodeDOM(offset) : null;
363
+ if (dom && dom.getBoundingClientRect().top <= line) id = `h${offset}`;
364
+ });
365
+ const room2 = el.scrollHeight - el.clientHeight;
366
+ const progress = room2 > 0 ? Math.round(el.scrollTop / room2 * 100) / 100 : 1;
367
+ if (`${id}:${progress}` === last2) return;
368
+ last2 = `${id}:${progress}`;
369
+ onReading({ documentId, id, progress });
370
+ });
371
+ };
372
+ look();
373
+ el.addEventListener("scroll", look, { passive: true });
374
+ return () => {
375
+ cancelAnimationFrame(frame);
376
+ el.removeEventListener("scroll", look);
377
+ };
378
+ }, [editor, source.data, onReading, documentId]);
379
+ const dirty = live ? live.pending : !!state?.dirty || unsetup;
281
380
  const unsent = live ? live.unsynced && can.edit : dirty;
282
381
  useEffect(() => {
283
382
  if (!guard || live || !dirty || !source.data || !editor) return;
@@ -286,6 +385,11 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
286
385
  guard.current = null;
287
386
  };
288
387
  });
388
+ useEffect(() => {
389
+ if (live || !dirty || !can.edit || saving === "busy" || !editor) return;
390
+ const timer = window.setTimeout(() => void save(true), 3e3);
391
+ return () => clearTimeout(timer);
392
+ });
289
393
  useEffect(() => {
290
394
  if (!unsent) return;
291
395
  const stop = (e) => e.preventDefault();
@@ -299,6 +403,30 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
299
403
  watch.observe(el);
300
404
  return () => watch.disconnect();
301
405
  }, [editor, source.data]);
406
+ useEffect(() => {
407
+ const el = root.current;
408
+ if (!el) return;
409
+ const open2 = () => {
410
+ setPop("find");
411
+ el.querySelector(".w100n-text-find input")?.focus();
412
+ };
413
+ el.addEventListener("docs-find", open2);
414
+ return () => el.removeEventListener("docs-find", open2);
415
+ }, [editor, source.data]);
416
+ useEffect(() => {
417
+ const shown = pop === "find" ? query : "";
418
+ if (editor && searching.getState(editor.state).query !== shown) editor.view.dispatch(editor.state.tr.setMeta(searching, { query: shown }));
419
+ }, [editor, pop, query]);
420
+ useEffect(() => {
421
+ const press = (e) => {
422
+ if (!(e.metaKey || e.ctrlKey) || e.altKey || e.shiftKey || e.key.toLowerCase() !== "p") return;
423
+ e.preventDefault();
424
+ e.stopPropagation();
425
+ print();
426
+ };
427
+ window.addEventListener("keydown", press, true);
428
+ return () => window.removeEventListener("keydown", press, true);
429
+ });
302
430
  useEffect(() => {
303
431
  if (!pop) return;
304
432
  const close = (e) => {
@@ -309,7 +437,7 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
309
437
  }, [pop]);
310
438
  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
439
  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 () => {
440
+ const save = async (auto = false) => {
313
441
  if (live) {
314
442
  if (!can.edit || !live.ready || !live.online || !live.pending || saving === "busy") return false;
315
443
  setSaving("busy");
@@ -319,17 +447,18 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
319
447
  }
320
448
  const was = baseline.current;
321
449
  const doc = editor.state.doc;
322
- if (!can.edit || !was || saving === "busy" || doc.eq(was.doc)) return false;
450
+ if (!can.edit || !was || saving === "busy" || doc.eq(was.doc) && !unsetup) return false;
323
451
  setSaving("busy");
324
452
  try {
325
453
  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);
454
+ const out = md ? new TextEncoder().encode(md.text).buffer : await writeDocx(was.bytes, editor.getJSON(), void 0, unsetup ? { page, ...marginal } : void 0);
455
+ await host.save(documentId, out, auto ? t.autosavedNote : t.editedNote, auto);
328
456
  baseline.current = { bytes: md ? out : was.bytes, doc, base: md ? md.base : null };
329
457
  if (!md && editor.state.doc === doc) {
330
458
  const fresh = (await readDocx(out)).doc;
331
459
  if (editor.state.doc === doc && renew(fresh)) baseline.current = { bytes: out, doc: editor.state.doc, base: null };
332
460
  }
461
+ setUnsetup(false);
333
462
  setSaving("done");
334
463
  onSaved?.();
335
464
  return true;
@@ -357,7 +486,6 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
357
486
  };
358
487
  const linkUp = () => {
359
488
  setHref(editor.getAttributes("link").href ?? "");
360
- setTab("insert");
361
489
  setPop("link");
362
490
  };
363
491
  const putLink = () => {
@@ -458,20 +586,22 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
458
586
  const keys = (e) => {
459
587
  if (e.defaultPrevented) return;
460
588
  const key = e.key.toLowerCase();
461
- const mod = e.metaKey || e.ctrlKey;
462
- if (key === "escape" && (pop || draft)) {
589
+ const mod2 = e.metaKey || e.ctrlKey;
590
+ if (key === "escape" && (pop || draft || paint)) {
463
591
  e.stopPropagation();
464
- if (pop) setPop(null);
592
+ if (paint) setPaint(null);
593
+ else if (pop) setPop(null);
465
594
  else drop();
466
595
  return;
467
596
  }
468
- if (!mod) return;
597
+ if (!mod2) return;
469
598
  const act = (run) => {
470
599
  e.preventDefault();
471
600
  run();
472
601
  };
473
602
  if (key === "s" && can.edit) act(() => void save());
474
603
  else if (key === "k" && can.edit) act(linkUp);
604
+ else if (key === "f" && !e.altKey) act(() => setPop("find"));
475
605
  else if (key === "m" && e.altKey && can.comment) act(begin);
476
606
  else if (!can.edit || e.altKey) return;
477
607
  else if (key === "]" || key === "[") act(() => indent(editor, key === "]" ? 1 : -1));
@@ -502,14 +632,6 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
502
632
  ["quote", named((s) => /quote/i.test(s.name)) || t.quote, () => editor.chain().focus().clearNodes().setParagraph().setBlockquote().run()],
503
633
  ["code", t.code, () => editor.chain().focus().setCodeBlock().run()]
504
634
  ];
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
635
  const margins = [[t.marginsNormal, [96, 96, 96, 96]], [t.marginsNarrow, [48, 48, 48, 48]], [t.marginsWide, [96, 192, 96, 192]]];
514
636
  const when = (iso) => new Intl.DateTimeFormat(language, { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));
515
637
  const person = (c) => /* @__PURE__ */ jsxs2("header", { children: [
@@ -573,10 +695,273 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
573
695
  setPop(null);
574
696
  }, children: /* @__PURE__ */ jsx2("i", { style: { background: c } }) }, i))
575
697
  ] });
576
- let tools = null;
577
- if (shown === "home") tools = /* @__PURE__ */ jsxs2(Fragment3, { children: [
698
+ const mod = /Mac|iPhone|iPad/.test(navigator.platform) ? "\u2318" : "Ctrl+";
699
+ const item = (label, run, more2 = {}) => ({ label, run, ...more2 });
700
+ const insertToc = () => {
701
+ const at = editor.state.selection.$from.before(1);
702
+ editor.chain().focus().insertContentAt(at, { type: "toc", attrs: { entries: JSON.stringify(entries(editor.state.doc, !docx, at - 1)) } }).run();
703
+ };
704
+ const suggest = () => editor.view.dispatch(editor.state.tr.setMeta(suggesting, { on: !state.suggest, me: host?.me.name ?? "" }));
705
+ const aligns = [[AlignLeft, t.alignLeft, "left"], [AlignCenter, t.alignCenter, "center"], [AlignRight, t.alignRight, "right"], [AlignJustify, t.justify, "justify"]];
706
+ const lines = [[1, t.single], [1.15, "1.15"], [1.5, "1.5"], [2, t.double]];
707
+ const menus = [
708
+ ["file", t.menuFile, [
709
+ ...can.edit ? [
710
+ item(t.save, () => void save(), { keys: `${mod}S`, off: !dirty }),
711
+ item(t.print, print, { keys: `${mod}P` }),
712
+ ...docx ? ["-", item(t.pageSetup, () => setDraft2({ width: page.width, height: page.height, margins: page.margins.map(cm), ...marginal }))] : []
713
+ ] : [],
714
+ ...can.edit && (file?.formats.length || file?.copy || file?.rename) ? ["-"] : [],
715
+ ...(file?.formats ?? []).map((f) => item(`${t.downloadAs} ${f === "md" ? "Markdown" : f.toUpperCase()}`, () => file.download(f))),
716
+ !!file?.copy && item(t.makeCopy, file.copy),
717
+ !!file?.rename && item(t.rename, file.rename)
718
+ ]],
719
+ ["edit", t.menuEdit, can.edit ? [
720
+ item(t.undo, () => editor.chain().focus().undo().run(), { keys: `${mod}Z`, off: !state.undo }),
721
+ item(t.redo, () => editor.chain().focus().redo().run(), { keys: `${mod}\u21E7Z`, off: !state.redo }),
722
+ "-",
723
+ item(t.findReplace, () => setPop("find"), { keys: `${mod}F` }),
724
+ "-",
725
+ item(t.clearFormat, () => clear(editor), { keys: `${mod}\\` })
726
+ ] : []],
727
+ ["view", t.viewTab, [
728
+ item(t.zoomIn, () => zoomBy(10)),
729
+ item(t.zoomOut, () => zoomBy(-10)),
730
+ docx && item(t.fitWidth, () => setZoom(null), { on: zoom === null }),
731
+ item(t.fit, () => setZoom(100)),
732
+ "-",
733
+ item(t.outline, () => onPanel?.("outline")),
734
+ "-",
735
+ item([docx && count.pages ? `${t.pages}: ${count.pages}` : "", `${t.words}: ${count.words.toLocaleString(language)}`].filter(Boolean).join(" \xB7 "), () => {
736
+ }, { off: true })
737
+ ]],
738
+ ["insert", t.insert, can.edit ? [
739
+ item(t.table, () => setPop("table")),
740
+ item(t.image, () => {
741
+ setHref("");
742
+ setPop("image");
743
+ }),
744
+ item(t.link, linkUp, { keys: `${mod}K` }),
745
+ docx && item(t.pageBreak, () => editor.chain().focus().insertContent({ type: "pageBreak" }).run(), { keys: `${mod}\u21B5` }),
746
+ item(t.rule, () => editor.chain().focus().setHorizontalRule().run()),
747
+ "-",
748
+ 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()),
749
+ can.comment && item(t.addComment, begin, { off: state.empty, keys: `${mod}\u2325M` })
750
+ ] : []],
751
+ ["format", t.menuFormat, can.edit ? [
752
+ ...styles.map(([key, label, run]) => item(label, run, { on: state.style === key })),
753
+ "-",
754
+ item(t.bold, () => editor.chain().focus().toggleBold().run(), { on: state.bold, keys: `${mod}B` }),
755
+ item(t.italic, () => editor.chain().focus().toggleItalic().run(), { on: state.italic, keys: `${mod}I` }),
756
+ docx && item(t.underline, () => editor.chain().focus().toggleUnderline().run(), { on: state.underline, keys: `${mod}U` }),
757
+ item(t.strike, () => editor.chain().focus().toggleStrike().run(), { on: state.strike }),
758
+ docx && item(t.superscript, () => editor.chain().focus().toggleMark("superscript").run(), { on: state.sup, keys: `${mod}.` }),
759
+ docx && item(t.subscript, () => editor.chain().focus().toggleMark("subscript").run(), { on: state.sub, keys: `${mod},` }),
760
+ ...docx ? [
761
+ "-",
762
+ ...aligns.map(([, label, value]) => item(label, () => align(editor, value), { on: state.align === value })),
763
+ "-",
764
+ ...lines.map(([v, label]) => item(`${t.lineSpacing}: ${label}`, () => spacing(editor, { line: v }), { on: state.line === v }))
765
+ ] : [],
766
+ "-",
767
+ item(t.bullets, () => editor.chain().focus().toggleBulletList().run(), { on: state.bullets }),
768
+ item(t.numbers, () => editor.chain().focus().toggleOrderedList().run(), { on: state.numbers }),
769
+ item(t.tasks, () => editor.chain().focus().toggleTaskList().run(), { on: state.tasks }),
770
+ "-",
771
+ item(t.outdent, () => indent(editor, -1), { keys: `${mod}[` }),
772
+ item(t.indent, () => indent(editor, 1), { keys: `${mod}]` })
773
+ ] : []],
774
+ ["tools", t.menuTools, [
775
+ docx && can.edit && item(t.suggest, suggest, { on: state.suggest }),
776
+ docx && can.edit && state.revs.length > 0 && item(t.acceptAll, () => decide(editor, true)),
777
+ docx && can.edit && state.revs.length > 0 && item(t.rejectAll, () => decide(editor, false)),
778
+ !!host?.comments && (can.comment || open.length > 0) && item(t.comments, () => onPanel?.("comments"))
779
+ ]]
780
+ ];
781
+ const menu = ([key, label, list]) => /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
782
+ /* @__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 }),
783
+ 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: () => {
784
+ setPop(null);
785
+ x.run();
786
+ }, children: [
787
+ /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
788
+ /* @__PURE__ */ jsx2("span", { children: x.label }),
789
+ x.keys && /* @__PURE__ */ jsx2("kbd", { children: x.keys })
790
+ ] }, i)) })
791
+ ] }, key);
792
+ const seek = (next) => {
793
+ editor.view.dispatch(editor.state.tr.setMeta(searching, next));
794
+ const found2 = searching.getState(editor.state);
795
+ const hit = found2.hits[found2.at];
796
+ if (hit && next.at !== void 0) {
797
+ editor.commands.setTextSelection(hit);
798
+ editor.view.dom.querySelector(".w100n-found.is-current")?.scrollIntoView({ behavior: "smooth", block: "center" });
799
+ }
800
+ };
801
+ const found = searching.getState(editor.state);
802
+ const finder = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
803
+ /* @__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 }) }),
804
+ pop === "find" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-find", role: "search", "aria-label": t.findReplace, children: [
805
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
806
+ /* @__PURE__ */ jsx2(
807
+ "input",
808
+ {
809
+ autoFocus: true,
810
+ value: query,
811
+ placeholder: t.find,
812
+ "aria-label": t.find,
813
+ onFocus: (e) => e.currentTarget.select(),
814
+ onChange: (e) => {
815
+ setQuery(e.target.value);
816
+ seek({ query: e.target.value, at: 0 });
817
+ },
818
+ onKeyDown: (e) => {
819
+ if (e.key === "Enter") {
820
+ e.preventDefault();
821
+ seek({ at: (found?.at ?? 0) + (e.shiftKey ? -1 : 1) });
822
+ }
823
+ }
824
+ }
825
+ ),
826
+ /* @__PURE__ */ jsx2("small", { "aria-live": "polite", children: query ? found?.hits.length ? `${(found.at ?? 0) + 1} / ${found.hits.length}` : t.noHits : "" }),
827
+ /* @__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 }) }),
828
+ /* @__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 }) })
829
+ ] }),
830
+ can.edit && /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
831
+ /* @__PURE__ */ jsx2("input", { value: swap, placeholder: t.replaceWith, "aria-label": t.replaceWith, onChange: (e) => setSwap(e.target.value) }),
832
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", disabled: !found?.hits.length, onClick: () => {
833
+ replace(editor, swap, false);
834
+ seek({});
835
+ }, children: t.replaceOne }),
836
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-docs-action", disabled: !found?.hits.length, onClick: () => {
837
+ replace(editor, swap, true);
838
+ seek({});
839
+ }, children: t.replaceAll })
840
+ ] }),
841
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-check", children: [
842
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: matchCase, onChange: (e) => {
843
+ setMatchCase(e.target.checked);
844
+ seek({ matchCase: e.target.checked, at: 0 });
845
+ } }),
846
+ t.matchCase
847
+ ] })
848
+ ] })
849
+ ] });
850
+ const formats = ["bold", "italic", "underline", "strike", "code", "highlight", "textStyle", "superscript", "subscript"];
851
+ const take = (keep2) => {
852
+ if (paint && !keep2) return setPaint(null);
853
+ const { $from } = editor.state.selection;
854
+ const marks = (editor.state.storedMarks ?? $from.marks()).filter((m) => formats.includes(m.type.name)).map((m) => ({ type: m.type.name, attrs: m.attrs }));
855
+ setPaint({ marks, block: { align: $from.parent.attrs.align, line: $from.parent.attrs.line, after: $from.parent.attrs.after }, keep: keep2 });
856
+ };
857
+ const lay = () => {
858
+ if (!paint || editor.state.selection.empty) return;
859
+ const chain = editor.chain().focus();
860
+ for (const name of formats) chain.unsetMark(name);
861
+ for (const m of paint.marks) chain.setMark(m.type, m.attrs);
862
+ chain.run();
863
+ align(editor, paint.block.align);
864
+ spacing(editor, { line: paint.block.line, after: paint.block.after });
865
+ if (!paint.keep) setPaint(null);
866
+ };
867
+ const fontPicker = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
868
+ /* @__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: [
869
+ /* @__PURE__ */ jsx2("span", { style: { fontFamily: state.font ?? void 0 }, children: state.font ?? t.defaultFont }),
870
+ /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
871
+ ] }),
872
+ pop === "font" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.font, children: [
873
+ /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": !state.font, onClick: () => {
874
+ editor.chain().focus().unsetFontFamily().run();
875
+ setPop(null);
876
+ }, children: t.defaultFont }),
877
+ faces.map((f) => /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": state.font === f, style: { fontFamily: f }, onClick: () => {
878
+ editor.chain().focus().setFontFamily(f).run();
879
+ setPop(null);
880
+ }, children: f }, f))
881
+ ] })
882
+ ] });
883
+ const sizePicker = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
884
+ /* @__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: [
885
+ state.size ? parseFloat(state.size) : t.defaultFont,
886
+ /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
887
+ ] }),
888
+ pop === "size" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.fontSize, children: [
889
+ /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": !state.size, onClick: () => {
890
+ editor.chain().focus().unsetFontSize().run();
891
+ setPop(null);
892
+ }, children: t.defaultFont }),
893
+ sizes.map((n) => /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": state.size !== null && parseFloat(state.size) === n, onClick: () => {
894
+ editor.chain().focus().setFontSize(`${n}pt`).run();
895
+ setPop(null);
896
+ }, children: n }, n))
897
+ ] })
898
+ ] });
899
+ const spacer = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
900
+ /* @__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 }) }),
901
+ pop === "spacing" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.lineSpacing, children: [
902
+ lines.map(([v, label]) => /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": state.line === v, onClick: () => {
903
+ spacing(editor, { line: v });
904
+ setPop(null);
905
+ }, children: label }, v)),
906
+ /* @__PURE__ */ jsx2("hr", {}),
907
+ /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": false, onClick: () => {
908
+ spacing(editor, { after: state.after === 0 ? 8 : 0 });
909
+ setPop(null);
910
+ }, children: state.after === 0 ? t.spaceAfter : t.noSpaceAfter })
911
+ ] })
912
+ ] });
913
+ const applySetup = () => {
914
+ const d = draft2;
915
+ const next = { width: d.width, height: d.height, margins: d.margins.map((v) => Math.round(v / 2.54 * 96 * 100) / 100) };
916
+ const marks = { header: d.header.trim(), footer: d.footer.trim(), numbers: d.numbers };
917
+ if (shared) shared.doc.transact(() => {
918
+ const map = shared.doc.getMap("setup");
919
+ map.set("page", next);
920
+ for (const [key, v] of Object.entries(marks)) map.set(key, v);
921
+ });
922
+ else {
923
+ setLayout(next);
924
+ setMarg(marks);
925
+ setUnsetup(true);
926
+ }
927
+ setDraft2(null);
928
+ };
929
+ const zooms = [50, 75, 90, 100, 125, 150, 200];
930
+ const zoomer = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
931
+ /* @__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: [
932
+ percent,
933
+ "%",
934
+ /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
935
+ ] }),
936
+ pop === "zoom" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-styles", role: "listbox", "aria-label": t.zoom, children: [
937
+ docx && /* @__PURE__ */ jsx2("button", { type: "button", role: "option", "aria-selected": zoom === null, onClick: () => {
938
+ setZoom(null);
939
+ setPop(null);
940
+ }, children: t.fitWidth }),
941
+ zooms.map((z) => /* @__PURE__ */ jsxs2("button", { type: "button", role: "option", "aria-selected": zoom === z, onClick: () => {
942
+ setZoom(z);
943
+ setPop(null);
944
+ }, children: [
945
+ z,
946
+ "%"
947
+ ] }, z))
948
+ ] })
949
+ ] });
950
+ const Aligned = aligns.find(([, , value]) => value === state.align)?.[0] ?? AlignLeft;
951
+ const aligner = /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
952
+ /* @__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 }) }),
953
+ 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, () => {
954
+ align(editor, value);
955
+ setPop(null);
956
+ }, state.align === value) }, i)) }) })
957
+ ] });
958
+ const widen = () => {
959
+ localStorage.setItem("w100n-docs-more", more ? "" : "1");
960
+ setMore(!more);
961
+ };
962
+ const main = can.edit ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
578
963
  /* @__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: [
964
+ /* @__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: [
580
965
  styles.find(([key]) => key === state.style)?.[1] ?? t.normal,
581
966
  /* @__PURE__ */ jsx2(ChevronDown, { "aria-hidden": true })
582
967
  ] }),
@@ -599,8 +984,7 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
599
984
  gap,
600
985
  tool(Bold, t.bold, () => editor.chain().focus().toggleBold().run(), state.bold),
601
986
  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),
987
+ docx ? tool(Underline, t.underline, () => editor.chain().focus().toggleUnderline().run(), state.underline) : tool(Strikethrough, t.strike, () => editor.chain().focus().toggleStrike().run(), state.strike),
604
988
  !docx && tool(Code, t.code, () => editor.chain().focus().toggleCode().run(), state.code),
605
989
  docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
606
990
  /* @__PURE__ */ jsxs2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "color", "aria-label": t.textColour, title: t.textColour, onClick: () => pick("color"), children: [
@@ -609,60 +993,34 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
609
993
  ] }),
610
994
  pop === "color" && swatches(inks, state.color, (c) => c ? editor.chain().focus().setColor(c).run() : editor.chain().focus().unsetColor().run(), t.automatic)
611
995
  ] }),
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
996
  gap,
997
+ docx && aligner,
617
998
  tool(List, t.bullets, () => editor.chain().focus().toggleBulletList().run(), state.bullets),
618
999
  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
- ] }),
1000
+ !docx && tool(ListChecks, t.tasks, () => editor.chain().focus().toggleTaskList().run(), state.tasks),
624
1001
  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
1002
  /* @__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 })
1003
+ /* @__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 }) }),
1004
+ pop === "link" && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-pop w100n-text-form", children: [
1005
+ /* @__PURE__ */ jsx2("input", { autoFocus: true, value: href, placeholder: t.linkAddress, "aria-label": t.linkAddress, onChange: (e) => setHref(e.target.value), onKeyDown: (e) => {
1006
+ if (e.key === "Enter") putLink();
1007
+ } }),
1008
+ /* @__PURE__ */ jsxs2("span", { className: "w100n-docs-row", children: [
1009
+ state.link && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-pdf-link", onClick: () => {
1010
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
1011
+ setPop(null);
1012
+ }, children: [
1013
+ /* @__PURE__ */ jsx2(Link2Off, { "aria-hidden": true }),
1014
+ t.unlink
1015
+ ] }),
1016
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !href.trim(), onClick: putLink, children: [
1017
+ /* @__PURE__ */ jsx2(Link, { "aria-hidden": true }),
1018
+ t.apply
1019
+ ] })
1020
+ ] })
652
1021
  ] })
653
1022
  ] }),
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,
1023
+ can.comment && tool(MessageSquarePlus, t.addComment, begin, void 0, state.empty),
666
1024
  /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
667
1025
  /* @__PURE__ */ jsx2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "image", "aria-label": t.image, title: t.image, onClick: () => {
668
1026
  setHref("");
@@ -689,14 +1047,14 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
689
1047
  docx && /* @__PURE__ */ jsxs2("label", { className: "w100n-pdf-link", children: [
690
1048
  t.chooseFile,
691
1049
  /* @__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;
1050
+ const file2 = e.target.files?.[0];
1051
+ if (!file2) return;
694
1052
  const reader = new FileReader();
695
1053
  reader.onload = () => {
696
- editor.chain().focus().setImage({ src: String(reader.result), alt: file.name }).run();
1054
+ editor.chain().focus().setImage({ src: String(reader.result), alt: file2.name }).run();
697
1055
  setPop(null);
698
1056
  };
699
- reader.readAsDataURL(file);
1057
+ reader.readAsDataURL(file2);
700
1058
  } })
701
1059
  ] }),
702
1060
  /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: !href.trim(), onClick: () => {
@@ -710,162 +1068,215 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
710
1068
  ] })
711
1069
  ] }),
712
1070
  /* @__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
- ] })
1071
+ /* @__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 }) }),
1072
+ 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: [
1073
+ /* @__PURE__ */ jsx2("span", { children: Array.from({ length: 48 }, (_, i) => {
1074
+ const rows = Math.floor(i / 8) + 1;
1075
+ const cols = i % 8 + 1;
1076
+ return /* @__PURE__ */ jsx2(
1077
+ "button",
1078
+ {
1079
+ type: "button",
1080
+ "aria-label": `${rows} \xD7 ${cols}`,
1081
+ "data-on": rows <= grid.rows && cols <= grid.cols,
1082
+ onPointerEnter: () => setGrid({ rows, cols }),
1083
+ onClick: () => {
1084
+ editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
1085
+ setPop(null);
1086
+ }
1087
+ },
1088
+ i
1089
+ );
1090
+ }) }),
1091
+ /* @__PURE__ */ jsx2("small", { children: grid.rows ? `${grid.rows} \xD7 ${grid.cols}` : t.table })
731
1092
  ] })
732
1093
  ] }),
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
1094
  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
1095
+ /* @__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 }) })
1096
+ ] }) : /* @__PURE__ */ jsxs2(Fragment3, { children: [
1097
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => onPanel?.("outline"), children: [
1098
+ /* @__PURE__ */ jsx2(ListTree, { "aria-hidden": true }),
1099
+ t.outline
786
1100
  ] }),
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
- ] })
1101
+ can.comment && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1102
+ /* @__PURE__ */ jsx2("span", { className: "w100n-text-grow" }),
1103
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", disabled: state.empty, onClick: begin, children: [
1104
+ /* @__PURE__ */ jsx2(MessageSquarePlus, { "aria-hidden": true }),
1105
+ t.addComment
810
1106
  ] })
811
1107
  ] }),
812
- /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => onPanel?.("comments"), children: [
1108
+ !!host?.comments && (can.comment || open.length > 0) && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-word", onClick: () => onPanel?.("comments"), children: [
813
1109
  /* @__PURE__ */ jsx2(MessagesSquare, { "aria-hidden": true }),
814
1110
  t.comments,
815
1111
  /* @__PURE__ */ jsx2("b", { children: open.length })
816
1112
  ] })
817
1113
  ] });
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
- "%"
1114
+ const extra = can.edit && more && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1115
+ docx && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1116
+ fontPicker,
1117
+ sizePicker,
1118
+ gap
1119
+ ] }),
1120
+ docx && tool(Strikethrough, t.strike, () => editor.chain().focus().toggleStrike().run(), state.strike),
1121
+ docx && tool(Superscript, t.superscript, () => editor.chain().focus().toggleMark("superscript").run(), state.sup),
1122
+ docx && tool(Subscript, t.subscript, () => editor.chain().focus().toggleMark("subscript").run(), state.sub),
1123
+ docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1124
+ /* @__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 }) }),
1125
+ pop === "mark" && swatches(glows, null, (c) => c ? editor.chain().focus().setHighlight({ color: c }).run() : editor.chain().focus().unsetHighlight().run(), t.noColour)
1126
+ ] }),
1127
+ docx && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1128
+ gap,
1129
+ spacer
823
1130
  ] }),
824
- tool(Plus, t.zoomIn, () => zoomBy(10)),
825
- docx && tool(MoveHorizontal, t.fitWidth, () => setZoom(null), zoom === null),
826
- tool(Maximize2, t.fit, () => setZoom(100)),
1131
+ tool(ListIndentDecrease, t.outdent, () => indent(editor, -1)),
1132
+ tool(ListIndentIncrease, t.indent, () => indent(editor, 1)),
1133
+ docx && tool(ListChecks, t.tasks, () => editor.chain().focus().toggleTaskList().run(), state.tasks),
827
1134
  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
1135
+ /* @__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 }) }),
1136
+ tool(RemoveFormatting, t.clearFormat, () => clear(editor)),
1137
+ docx && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1138
+ gap,
1139
+ tool(FilePenLine, t.suggest, suggest, state.suggest)
831
1140
  ] })
832
1141
  ] });
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
- ] })
1142
+ const context = can.edit && (state.table ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
1143
+ tool(BetweenHorizontalStart, t.rowAbove, () => editor.chain().focus().addRowBefore().run()),
1144
+ tool(BetweenHorizontalEnd, t.rowBelow, () => editor.chain().focus().addRowAfter().run()),
1145
+ tool(BetweenVerticalStart, t.columnLeft, () => editor.chain().focus().addColumnBefore().run()),
1146
+ tool(BetweenVerticalEnd, t.columnRight, () => editor.chain().focus().addColumnAfter().run()),
1147
+ tool(PanelTop, t.headerRow, () => editor.chain().focus().toggleHeaderRow().run()),
1148
+ docx && tool(TableCellsMerge, t.mergeCells, () => editor.chain().focus().mergeOrSplit().run()),
1149
+ docx && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-anchor", children: [
1150
+ /* @__PURE__ */ jsxs2("button", { type: "button", "data-pop": true, "aria-expanded": pop === "fill", "aria-label": t.cellFill, title: t.cellFill, onClick: () => pick("fill"), children: [
1151
+ /* @__PURE__ */ jsx2(PaintBucket, { "aria-hidden": true }),
1152
+ /* @__PURE__ */ jsx2("i", { className: "w100n-text-bar", style: { background: state.fill ?? "transparent" } })
860
1153
  ] }),
861
- /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": tabs.find(([key]) => key === shown)?.[1], children: tools })
1154
+ pop === "fill" && swatches(glows, state.fill, (c) => editor.chain().focus().setCellAttribute("background", c).run(), t.noColour)
862
1155
  ] }),
863
- /* @__PURE__ */ jsx2("div", { ref: scroller, className: "w100n-text-scroll", onClick: (e) => {
1156
+ docx && tool(Grid3x3, t.tableBorders, () => editor.chain().focus().updateAttributes("table", { borders: state.bare ? null : "none" }).run(), !state.bare),
1157
+ tool(TableRowsSplit, t.deleteRow, () => editor.chain().focus().deleteRow().run()),
1158
+ tool(TableColumnsSplit, t.deleteColumn, () => editor.chain().focus().deleteColumn().run()),
1159
+ tool(Trash2, t.deleteTable, () => editor.chain().focus().deleteTable().run())
1160
+ ] }) : 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)) }));
1161
+ const status = (can.edit || live) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-end", children: [
1162
+ live && (live.ready ? !live.online : live.late || live.denied) && /* @__PURE__ */ jsxs2("span", { className: "w100n-text-live", title: live.denied ? void 0 : t.offlineHint, children: [
1163
+ /* @__PURE__ */ jsx2(WifiOff, { "aria-hidden": true }),
1164
+ live.denied ? t.liveOff : t.offline
1165
+ ] }),
1166
+ state.suggest && /* @__PURE__ */ jsx2("span", { className: "w100n-docs-badge", children: t.suggesting }),
1167
+ can.edit && saving === "failed" && /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-text-saved", title: `${t.save} (${mod}S)`, onClick: () => void save(), children: [
1168
+ /* @__PURE__ */ jsx2(CloudAlert, { "aria-hidden": true }),
1169
+ /* @__PURE__ */ jsx2("span", { children: t.saveFailed })
1170
+ ] }),
1171
+ 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)) })
1172
+ ] });
1173
+ 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) });
1174
+ const keep = (e) => {
1175
+ if (e.target.closest("button")) e.preventDefault();
1176
+ };
1177
+ 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;
1178
+ return /* @__PURE__ */ jsxs2("div", { ref: root, className: "w100n-text", "data-kind": kind, "data-docs-find": true, onKeyDown: keys, children: [
1179
+ head ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
1180
+ head.menus && createPortal(/* @__PURE__ */ jsx2("div", { className: "w100n-text-ribbon w100n-text-docbar", onKeyDown: rove, onPointerDown: keep, children: menubar }), head.menus),
1181
+ head.status && status && createPortal(/* @__PURE__ */ jsx2("div", { className: "w100n-text-ribbon w100n-text-docbar", onPointerDown: keep, children: status }), head.status)
1182
+ ] }) : /* @__PURE__ */ jsxs2("div", { className: "w100n-text-ribbon w100n-text-top", onKeyDown: rove, onPointerDown: keep, children: [
1183
+ menubar,
1184
+ status
1185
+ ] }),
1186
+ /* @__PURE__ */ jsx2("div", { ref: scroller, className: "w100n-text-scroll", "data-paint": !!paint, onMouseUp: lay, onClick: (e) => {
864
1187
  const id = e.target.closest("[data-note]")?.getAttribute("data-note");
865
1188
  if (!id) return;
866
1189
  setActive(id);
867
1190
  onPanel?.("comments");
868
1191
  }, 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 }) }) }) }),
1192
+ /* @__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: [
1193
+ tool(Minus, t.zoomOut, () => zoomBy(-10)),
1194
+ zoomer,
1195
+ tool(Plus, t.zoomIn, () => zoomBy(10))
1196
+ ] }) }),
1197
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-text-ribbon w100n-text-island", role: "toolbar", "aria-label": t.textTools, onKeyDown: rove, onPointerDown: keep, children: [
1198
+ context && /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": state.table ? t.table : t.image, children: context }),
1199
+ extra && /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": t.moreTools, children: extra }),
1200
+ /* @__PURE__ */ jsx2("div", { className: "w100n-text-tools", role: "group", "aria-label": t.textTools, children: main })
1201
+ ] }),
1202
+ /* @__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: [
1203
+ can.edit && /* @__PURE__ */ jsxs2(Fragment3, { children: [
1204
+ tool(Undo2, t.undo, () => editor.chain().focus().undo().run(), void 0, !state.undo),
1205
+ tool(Redo2, t.redo, () => editor.chain().focus().redo().run(), void 0, !state.redo),
1206
+ gap
1207
+ ] }),
1208
+ finder
1209
+ ] }) }),
1210
+ draft2 && /* @__PURE__ */ jsx2("div", { className: "w100n-pdf-back", onKeyDown: (e) => {
1211
+ if (e.key === "Escape") {
1212
+ e.stopPropagation();
1213
+ setDraft2(null);
1214
+ }
1215
+ }, children: /* @__PURE__ */ jsxs2("form", { className: "w100n-pdf-dialog w100n-setup", role: "dialog", "aria-modal": true, "aria-label": t.pageSetup, onSubmit: (e) => {
1216
+ e.preventDefault();
1217
+ applySetup();
1218
+ }, children: [
1219
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-new-head", children: [
1220
+ /* @__PURE__ */ jsx2("span", { className: "w100n-new-tile", "data-tone": "blue", children: /* @__PURE__ */ jsx2(Ruler, { "aria-hidden": true }) }),
1221
+ /* @__PURE__ */ jsx2("b", { className: "w100n-pdf-dialog-title", children: t.pageSetup }),
1222
+ /* @__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 }) })
1223
+ ] }),
1224
+ /* @__PURE__ */ jsx2("p", { className: "w100n-docs-caption", children: t.pageSize }),
1225
+ /* @__PURE__ */ jsx2("div", { className: "w100n-docs-chips", role: "group", "aria-label": t.pageSize, children: [["A4", a4], ["Letter", letter], ["Legal", legal]].map(([label, size]) => {
1226
+ const land = draft2.width > draft2.height;
1227
+ const on = Math.abs(Math.min(draft2.width, draft2.height) - size.width) < 2 && Math.abs(Math.max(draft2.width, draft2.height) - size.height) < 2;
1228
+ 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);
1229
+ }) }),
1230
+ /* @__PURE__ */ jsx2("p", { className: "w100n-docs-caption", children: t.orientation }),
1231
+ /* @__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(
1232
+ "button",
1233
+ {
1234
+ type: "button",
1235
+ "aria-pressed": draft2.width > draft2.height === land,
1236
+ onClick: () => {
1237
+ if (draft2.width > draft2.height !== land) setDraft2({ ...draft2, width: draft2.height, height: draft2.width });
1238
+ },
1239
+ children: label
1240
+ },
1241
+ label
1242
+ )) }),
1243
+ /* @__PURE__ */ jsx2("p", { className: "w100n-docs-caption", children: t.marginsCm }),
1244
+ /* @__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)) }),
1245
+ /* @__PURE__ */ jsx2("div", { className: "w100n-setup-grid", children: [t.mTop, t.mRight, t.mBottom, t.mLeft].map((label, n) => /* @__PURE__ */ jsxs2("label", { children: [
1246
+ /* @__PURE__ */ jsx2("span", { children: label }),
1247
+ /* @__PURE__ */ jsx2(
1248
+ "input",
1249
+ {
1250
+ className: "w100n-docs-field",
1251
+ type: "number",
1252
+ min: 0,
1253
+ max: 10,
1254
+ step: 0.1,
1255
+ value: draft2.margins[n],
1256
+ onChange: (e) => setDraft2({ ...draft2, margins: draft2.margins.map((v, i) => i === n ? Math.max(0, Math.min(10, Number(e.target.value) || 0)) : v) })
1257
+ }
1258
+ )
1259
+ ] }, n)) }),
1260
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-line", children: [
1261
+ /* @__PURE__ */ jsx2("span", { children: t.headerText }),
1262
+ /* @__PURE__ */ jsx2("input", { className: "w100n-docs-field", value: draft2.header, onChange: (e) => setDraft2({ ...draft2, header: e.target.value }) })
1263
+ ] }),
1264
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-line", children: [
1265
+ /* @__PURE__ */ jsx2("span", { children: t.footerText }),
1266
+ /* @__PURE__ */ jsx2("input", { className: "w100n-docs-field", value: draft2.footer, onChange: (e) => setDraft2({ ...draft2, footer: e.target.value }) })
1267
+ ] }),
1268
+ /* @__PURE__ */ jsxs2("label", { className: "w100n-setup-check", children: [
1269
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: draft2.numbers, onChange: (e) => setDraft2({ ...draft2, numbers: e.target.checked }) }),
1270
+ t.pageNumbers
1271
+ ] }),
1272
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-pdf-dialog-end", children: [
1273
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: () => setDraft2(null), children: t.cancel }),
1274
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", onClick: applySetup, children: [
1275
+ /* @__PURE__ */ jsx2(Check, { "aria-hidden": true }),
1276
+ t.apply
1277
+ ] })
1278
+ ] })
1279
+ ] }) }),
869
1280
  slot && createPortal(/* @__PURE__ */ jsxs2("div", { className: "w100n-text-notes", children: [
870
1281
  draft && /* @__PURE__ */ jsxs2("div", { className: "w100n-text-card w100n-text-draft", children: [
871
1282
  /* @__PURE__ */ jsx2("blockquote", { children: draft.quote }),
@@ -980,4 +1391,4 @@ function TextEditor({ kind, bytes, t, language, rights, host, together, document
980
1391
  export {
981
1392
  TextEditor as default
982
1393
  };
983
- //# sourceMappingURL=TextEditor-SPEVB5HX.js.map
1394
+ //# sourceMappingURL=TextEditor-VBHWUOZR.js.map