@w100n/documents 0.4.0 → 0.11.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 (61) hide show
  1. package/dist/SheetView-3XTEV2VT.css +319 -0
  2. package/dist/SheetView-3XTEV2VT.css.map +1 -0
  3. package/dist/SheetView-TXHS5WJC.js +928 -0
  4. package/dist/SheetView-TXHS5WJC.js.map +1 -0
  5. package/dist/TextEditor-4YETNX4T.js +983 -0
  6. package/dist/TextEditor-4YETNX4T.js.map +1 -0
  7. package/dist/caveat-cyrillic-XVO43JCM.woff2 +0 -0
  8. package/dist/caveat-latin-IUBKKMHQ.woff2 +0 -0
  9. package/dist/caveat-latin-ext-5S7R5SZN.woff2 +0 -0
  10. package/dist/chunk-EM4U6OC5.js +3447 -0
  11. package/dist/chunk-EM4U6OC5.js.map +1 -0
  12. package/dist/chunk-FPGPEIW5.js +196 -0
  13. package/dist/chunk-FPGPEIW5.js.map +1 -0
  14. package/dist/chunk-ISOOUYLI.js +150 -0
  15. package/dist/chunk-ISOOUYLI.js.map +1 -0
  16. package/dist/{chunk-KEV4R7W5.js → chunk-L765DZPO.js} +9 -1
  17. package/dist/chunk-L765DZPO.js.map +1 -0
  18. package/dist/chunk-QDH7EZAF.js +27 -0
  19. package/dist/chunk-QDH7EZAF.js.map +1 -0
  20. package/dist/chunk-TDP6OJET.js +31 -0
  21. package/dist/chunk-TDP6OJET.js.map +1 -0
  22. package/dist/chunk-TNSN4ZQ5.js +2909 -0
  23. package/dist/chunk-TNSN4ZQ5.js.map +1 -0
  24. package/dist/collab.cjs +6312 -0
  25. package/dist/collab.cjs.map +1 -0
  26. package/dist/collab.d.cts +39 -0
  27. package/dist/collab.d.ts +39 -0
  28. package/dist/collab.js +30 -0
  29. package/dist/collab.js.map +1 -0
  30. package/dist/grid-EI5JNZAA.js +88 -0
  31. package/dist/grid-EI5JNZAA.js.map +1 -0
  32. package/dist/index.cjs +12191 -486
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.css +3632 -728
  35. package/dist/index.css.map +1 -1
  36. package/dist/index.d.cts +144 -11
  37. package/dist/index.d.ts +144 -11
  38. package/dist/index.js +3389 -439
  39. package/dist/index.js.map +1 -1
  40. package/dist/kinds.cjs +71 -0
  41. package/dist/kinds.cjs.map +1 -0
  42. package/dist/kinds.d.cts +11 -0
  43. package/dist/kinds.d.ts +11 -0
  44. package/dist/kinds.js +14 -0
  45. package/dist/kinds.js.map +1 -0
  46. package/dist/memory.cjs +59 -7
  47. package/dist/memory.cjs.map +1 -1
  48. package/dist/memory.d.cts +7 -2
  49. package/dist/memory.d.ts +7 -2
  50. package/dist/memory.js +57 -8
  51. package/dist/memory.js.map +1 -1
  52. package/dist/pdfWrite-EM6GJDBF.js +76 -0
  53. package/dist/pdfWrite-EM6GJDBF.js.map +1 -0
  54. package/dist/types-B5oF25dn.d.cts +165 -0
  55. package/dist/types-B5oF25dn.d.ts +165 -0
  56. package/dist/write-DaVtTJCS.d.cts +6 -0
  57. package/dist/write-DaVtTJCS.d.ts +6 -0
  58. package/package.json +50 -3
  59. package/dist/chunk-KEV4R7W5.js.map +0 -1
  60. package/dist/types-DshXPnWA.d.cts +0 -62
  61. package/dist/types-DshXPnWA.d.ts +0 -62
package/dist/index.js CHANGED
@@ -2,156 +2,2795 @@
2
2
  import {
3
3
  kindOf,
4
4
  kinds,
5
- matches
6
- } from "./chunk-KEV4R7W5.js";
5
+ matches,
6
+ readable
7
+ } from "./chunk-L765DZPO.js";
8
+ import {
9
+ useLoad
10
+ } from "./chunk-QDH7EZAF.js";
11
+ import {
12
+ rove,
13
+ useRestore
14
+ } from "./chunk-TDP6OJET.js";
15
+ import {
16
+ readDocx,
17
+ texts,
18
+ writeDocx
19
+ } from "./chunk-TNSN4ZQ5.js";
20
+
21
+ // src/DocumentOverlay.tsx
22
+ import { Download, ExternalLink, FileSpreadsheet, FileText, FileType, Files, Presentation, X as X4 } from "lucide-react";
23
+ import { lazy, Suspense, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
24
+ import { createPortal as createPortal2 } from "react-dom";
25
+
26
+ // src/slides/SlidesView.tsx
27
+ import { ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Copy, Maximize, Pause, Play, Projector, Redo2, RotateCcw, Save, StickyNote, Timer, Trash2, Undo2, X } from "lucide-react";
28
+ import { useEffect, useRef, useState } from "react";
29
+ import { createPortal } from "react-dom";
30
+
31
+ // src/slides/html.ts
32
+ var voids = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
33
+ var raws = /* @__PURE__ */ new Set(["script", "style", "textarea", "title", "xmp", "iframe", "noembed", "noframes", "noscript"]);
34
+ var breaks = /* @__PURE__ */ new Set([
35
+ "address",
36
+ "article",
37
+ "aside",
38
+ "blockquote",
39
+ "details",
40
+ "dialog",
41
+ "div",
42
+ "dl",
43
+ "fieldset",
44
+ "figcaption",
45
+ "figure",
46
+ "footer",
47
+ "form",
48
+ "h1",
49
+ "h2",
50
+ "h3",
51
+ "h4",
52
+ "h5",
53
+ "h6",
54
+ "header",
55
+ "hgroup",
56
+ "hr",
57
+ "li",
58
+ "main",
59
+ "menu",
60
+ "nav",
61
+ "ol",
62
+ "p",
63
+ "pre",
64
+ "section",
65
+ "table",
66
+ "ul"
67
+ ]);
68
+ var fences = /* @__PURE__ */ new Set(["button", "table", "td", "th", "caption", "template", "object", "marquee", "applet", "html"]);
69
+ var peers = {
70
+ li: [["li"], ["ul", "ol"]],
71
+ dt: [["dt", "dd"], ["dl"]],
72
+ dd: [["dt", "dd"], ["dl"]],
73
+ option: [["option"], ["select", "datalist", "optgroup"]],
74
+ tr: [["tr", "td", "th"], ["table", "thead", "tbody", "tfoot"]],
75
+ td: [["td", "th"], ["tr", "table"]],
76
+ th: [["td", "th"], ["tr", "table"]],
77
+ thead: [["thead", "tbody", "tfoot"], ["table"]],
78
+ tbody: [["thead", "tbody", "tfoot"], ["table"]],
79
+ tfoot: [["thead", "tbody", "tfoot"], ["table"]]
80
+ };
81
+ var names = {
82
+ amp: "&",
83
+ lt: "<",
84
+ gt: ">",
85
+ quot: '"',
86
+ apos: "'",
87
+ nbsp: "\xA0",
88
+ mdash: "\u2014",
89
+ ndash: "\u2013",
90
+ hellip: "\u2026",
91
+ rsquo: "\u2019",
92
+ lsquo: "\u2018",
93
+ rdquo: "\u201D",
94
+ ldquo: "\u201C",
95
+ laquo: "\xAB",
96
+ raquo: "\xBB",
97
+ copy: "\xA9",
98
+ reg: "\xAE",
99
+ trade: "\u2122",
100
+ middot: "\xB7",
101
+ bull: "\u2022",
102
+ euro: "\u20AC",
103
+ times: "\xD7"
104
+ };
105
+ var decode = (s) => s.replace(/&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]*);?/gi, (all, e) => {
106
+ if (e[0] !== "#") return names[e.toLowerCase()] ?? all;
107
+ const code = e[1] === "x" || e[1] === "X" ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10);
108
+ return code > 0 && code < 1114112 ? String.fromCodePoint(code) : all;
109
+ });
110
+ var tagName = /<([a-zA-Z][^\s/>]*)/y;
111
+ var endTag = /<\/([a-zA-Z][^\s/>]*)[^>]*>/y;
112
+ var attrName = /[^\s/>=]+/y;
113
+ var bare = /[^\s>]*/y;
114
+ var sticky = (re, s, at) => {
115
+ re.lastIndex = at;
116
+ return re.exec(s);
117
+ };
118
+ function parse(html) {
119
+ const len = html.length;
120
+ const root = { tag: "#root", attrs: /* @__PURE__ */ new Map(), start: 0, open: 0, close: len, end: len, children: [], parent: null };
121
+ const stack = [root];
122
+ let foreign = 0;
123
+ const top = () => stack[stack.length - 1];
124
+ const text = (start, end) => {
125
+ if (end > start) top().children.push({ text: true, start, end });
126
+ };
127
+ const shut = (k, at, end) => {
128
+ for (let i2 = stack.length - 1; i2 > k; i2--) stack[i2].close = stack[i2].end = at;
129
+ stack[k].close = at;
130
+ stack[k].end = end;
131
+ for (let i2 = stack.length - 1; i2 >= k; i2--) if (stack[i2].tag === "svg" || stack[i2].tag === "math") foreign--;
132
+ stack.length = k;
133
+ };
134
+ const seek = (tags, stop) => {
135
+ for (let k = stack.length - 1; k > 0; k--) {
136
+ if (tags.includes(stack[k].tag)) return k;
137
+ if (Array.isArray(stop) ? stop.includes(stack[k].tag) : stop.has(stack[k].tag)) return -1;
138
+ }
139
+ return -1;
140
+ };
141
+ let i = 0;
142
+ let from = 0;
143
+ while (i < len) {
144
+ const lt = html.indexOf("<", i);
145
+ if (lt < 0) break;
146
+ const next = html[lt + 1];
147
+ if (html.startsWith("<!--", lt)) {
148
+ text(from, lt);
149
+ const e = html.indexOf("-->", lt + 4);
150
+ i = from = e < 0 ? len : e + 3;
151
+ continue;
152
+ }
153
+ if (next === "!" || next === "?") {
154
+ text(from, lt);
155
+ const e = html.indexOf(">", lt);
156
+ i = from = e < 0 ? len : e + 1;
157
+ continue;
158
+ }
159
+ if (next === "/") {
160
+ const m2 = sticky(endTag, html, lt);
161
+ if (!m2) {
162
+ i = lt + 1;
163
+ continue;
164
+ }
165
+ text(from, lt);
166
+ const k = seek([m2[1].toLowerCase()], []);
167
+ if (k > 0) shut(k, lt, lt + m2[0].length);
168
+ i = from = lt + m2[0].length;
169
+ continue;
170
+ }
171
+ const m = sticky(tagName, html, lt);
172
+ if (!m) {
173
+ i = lt + 1;
174
+ continue;
175
+ }
176
+ text(from, lt);
177
+ const tag = m[1].toLowerCase();
178
+ const attrs = /* @__PURE__ */ new Map();
179
+ let j = lt + m[0].length;
180
+ let closing = false;
181
+ while (j < len) {
182
+ while (j < len && /[\s/]/.test(html[j])) {
183
+ closing = html[j] === "/";
184
+ j++;
185
+ }
186
+ if (j >= len) break;
187
+ if (html[j] === ">") {
188
+ j++;
189
+ break;
190
+ }
191
+ closing = false;
192
+ const n = sticky(attrName, html, j) ?? [html[j]];
193
+ j += n[0].length;
194
+ while (j < len && /\s/.test(html[j])) j++;
195
+ let value = "";
196
+ let at = null;
197
+ if (html[j] === "=") {
198
+ j++;
199
+ while (j < len && /\s/.test(html[j])) j++;
200
+ const q = html[j];
201
+ if (q === '"' || q === "'") {
202
+ const e = html.indexOf(q, j + 1);
203
+ at = [j + 1, e < 0 ? len : e];
204
+ j = e < 0 ? len : e + 1;
205
+ } else {
206
+ const v = sticky(bare, html, j);
207
+ at = [j, j + v[0].length];
208
+ j += v[0].length;
209
+ }
210
+ value = decode(html.slice(at[0], at[1]));
211
+ }
212
+ const key = n[0].toLowerCase();
213
+ if (!attrs.has(key)) attrs.set(key, { value, at });
214
+ }
215
+ if (breaks.has(tag)) {
216
+ const k = seek(["p"], fences);
217
+ if (k > 0) shut(k, lt, lt);
218
+ }
219
+ if (peers[tag]) {
220
+ const k = seek(peers[tag][0], peers[tag][1]);
221
+ if (k > 0) shut(k, lt, lt);
222
+ }
223
+ const el = { tag, attrs, start: lt, open: j, close: j, end: j, children: [], parent: top() };
224
+ top().children.push(el);
225
+ i = from = j;
226
+ if (voids.has(tag) || closing && (foreign > 0 || tag === "svg" || tag === "math")) continue;
227
+ if (raws.has(tag) && !foreign) {
228
+ const re = new RegExp(`</${tag}[\\s/>]`, "gi");
229
+ re.lastIndex = j;
230
+ const hit = re.exec(html);
231
+ const e = hit ? hit.index : len;
232
+ el.children.push({ text: true, start: j, end: e });
233
+ const gt = hit ? html.indexOf(">", e) : -1;
234
+ el.close = e;
235
+ el.end = gt < 0 ? len : gt + 1;
236
+ i = from = el.end;
237
+ continue;
238
+ }
239
+ if (tag === "svg" || tag === "math") foreign++;
240
+ stack.push(el);
241
+ }
242
+ text(from, len);
243
+ for (let k = stack.length - 1; k > 0; k--) stack[k].close = stack[k].end = len;
244
+ return root;
245
+ }
246
+ function elements(root) {
247
+ const out = [];
248
+ const walk = (el) => {
249
+ for (const part of el.children) if (!part.text) {
250
+ out.push(part);
251
+ walk(part);
252
+ }
253
+ };
254
+ walk(root);
255
+ return out;
256
+ }
257
+ var classes = (el) => (el.attrs.get("class")?.value ?? "").split(/\s+/).filter(Boolean);
258
+ var under = (el, test) => {
259
+ for (let up = el.parent; up; up = up.parent) if (test(up)) return true;
260
+ return false;
261
+ };
262
+
263
+ // src/slides/deck.ts
264
+ var inline = /* @__PURE__ */ new Set([
265
+ "a",
266
+ "abbr",
267
+ "b",
268
+ "bdi",
269
+ "bdo",
270
+ "br",
271
+ "cite",
272
+ "code",
273
+ "data",
274
+ "del",
275
+ "dfn",
276
+ "em",
277
+ "font",
278
+ "i",
279
+ "ins",
280
+ "kbd",
281
+ "mark",
282
+ "q",
283
+ "s",
284
+ "samp",
285
+ "small",
286
+ "span",
287
+ "strong",
288
+ "sub",
289
+ "sup",
290
+ "time",
291
+ "u",
292
+ "var",
293
+ "wbr"
294
+ ]);
295
+ var skip = /* @__PURE__ */ new Set([
296
+ "script",
297
+ "style",
298
+ "template",
299
+ "svg",
300
+ "math",
301
+ "iframe",
302
+ "object",
303
+ "embed",
304
+ "video",
305
+ "audio",
306
+ "canvas",
307
+ "select",
308
+ "textarea",
309
+ "noscript",
310
+ "img",
311
+ "picture",
312
+ "input",
313
+ "button"
314
+ ]);
315
+ var boxes = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "td", "th", "dt", "dd", "figcaption", "caption", "blockquote", "pre"]);
316
+ var most = (list) => {
317
+ const count = /* @__PURE__ */ new Map();
318
+ for (const el of list) count.set(el.parent, (count.get(el.parent) ?? 0) + 1);
319
+ return [...count].sort((a, b) => b[1] - a[1])[0][0];
320
+ };
321
+ var flat = (el, html) => {
322
+ let text = false;
323
+ const visit = (e) => e.children.every((part) => {
324
+ if (part.text) {
325
+ text || (text = /\S/.test(html.slice(part.start, part.end)));
326
+ return true;
327
+ }
328
+ return inline.has(part.tag) && visit(part);
329
+ });
330
+ return visit(el) && (text || boxes.has(el.tag));
331
+ };
332
+ var blocksOf = (el, html, notes) => {
333
+ if (flat(el, html)) return [el];
334
+ const out = [];
335
+ const walk = (e) => {
336
+ for (const part of e.children) {
337
+ if (part.text || part === notes || skip.has(part.tag) || part.attrs.get("contenteditable")?.value === "false") continue;
338
+ if (flat(part, html)) out.push(part);
339
+ else walk(part);
340
+ }
341
+ };
342
+ walk(el);
343
+ return out;
344
+ };
345
+ var notesOf = (el) => elements(el).find((e) => e.tag === "aside" && classes(e).includes("notes")) ?? null;
346
+ var toText = (src) => decode(src.replace(/<!--[\s\S]*?-->/g, "").replace(/\s+/g, " ").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|blockquote|pre|tr)>/gi, "\n").replace(/<[^>]*>/g, "")).split("\n").map((line) => line.trim()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
347
+ var titled = (src) => toText(src).replace(/\s+/g, " ").slice(0, 80);
348
+ var escape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
349
+ var quote = (s) => escape(s).replace(/"/g, "&quot;");
350
+ var notesHtml = (text) => escape(text).split("\n").join("<br>\n");
351
+ var sizeOf = (html, kind, scripts, styles, marks) => {
352
+ if (kind === "reveal") {
353
+ const code = scripts.map((s) => html.slice(s.open, s.close)).join("\n");
354
+ const at = code.search(/Reveal\.initialize\s*\(/);
355
+ const init = at < 0 ? "" : code.slice(at, at + 4e3);
356
+ const width = Number(/\bwidth\s*:\s*['"]?(\d+)/.exec(init)?.[1] ?? 960);
357
+ const height = Number(/\bheight\s*:\s*['"]?(\d+)/.exec(init)?.[1] ?? 700);
358
+ return { width, height, center: !/\bcenter\s*:\s*false/.test(init) };
359
+ }
360
+ for (const el of marks) {
361
+ const width = Number(el.attrs.get("data-width")?.value);
362
+ const height = Number(el.attrs.get("data-height")?.value);
363
+ if (width > 0 && height > 0) return { width, height, center: false };
364
+ }
365
+ const css2 = styles.map((s) => html.slice(s.open, s.close)).join("\n").replace(/\/\*[\s\S]*?\*\//g, "");
366
+ const target = kind === "marked" ? /(^|[\s>])\[data-slide[^\]]*\](\.[\w-]+)*$/ : /(^|[\s>])(section|\.slide)(\.[\w-]+)*$/;
367
+ for (const rule of css2.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
368
+ if (!rule[1].split(",").some((sel) => target.test(sel.trim()))) continue;
369
+ const width = Number(/(?:^|;|\s)width\s*:\s*(\d+)px/.exec(rule[2])?.[1]);
370
+ const height = Number(/(?:^|;|\s)height\s*:\s*(\d+)px/.exec(rule[2])?.[1]);
371
+ if (width >= 320 && height >= 180) return { width, height, center: false };
372
+ }
373
+ return { width: 1280, height: 720, center: false };
374
+ };
375
+ function shape(html) {
376
+ const root = parse(html);
377
+ const all = elements(root);
378
+ const scripts = all.filter((el) => el.tag === "script");
379
+ const styles = all.filter((el) => el.tag === "style");
380
+ const refresh = all.filter((el) => el.tag === "meta" && el.attrs.get("http-equiv")?.value.toLowerCase() === "refresh");
381
+ const head = all.find((el) => el.tag === "head");
382
+ const outer = all.find((el) => el.tag === "html");
383
+ const body = all.find((el) => el.tag === "body") ?? null;
384
+ const doctype = /^\s*<!doctype[^>]*>/i.exec(html);
385
+ const at = head ? head.open : outer ? outer.open : doctype ? doctype[0].length : 0;
386
+ const reveal = all.find((el) => classes(el).includes("reveal")) ?? null;
387
+ const box = reveal && all.find((el) => classes(el).includes("slides") && under(el, (up) => up === reveal));
388
+ const spots = [];
389
+ const stacks = [];
390
+ const spot2 = (el, stack) => {
391
+ const notes = notesOf(el);
392
+ spots.push({ el, stack, notes, attr: notes ? null : el.attrs.get("data-notes") ?? null, blocks: blocksOf(el, html, notes) });
393
+ };
394
+ let kind;
395
+ let units;
396
+ let container;
397
+ if (box && box.children.some((part) => !part.text && part.tag === "section")) {
398
+ kind = "reveal";
399
+ container = box;
400
+ units = box.children.filter((part) => !part.text && part.tag === "section");
401
+ for (const unit of units) {
402
+ const inner = unit.children.filter((part) => !part.text && part.tag === "section");
403
+ if (!inner.length) spot2(unit, null);
404
+ else {
405
+ stacks.push(unit);
406
+ for (const el of inner) spot2(el, stacks.length - 1);
407
+ }
408
+ }
409
+ } else {
410
+ const marked = all.filter((el) => el.attrs.has("data-slide"));
411
+ const tops = marked.filter((el) => !under(el, (up) => up.attrs.has("data-slide")));
412
+ const sections = all.filter((el) => el.tag === "section" && !under(el, (up) => up.tag === "section"));
413
+ const list = tops.length ? tops : sections;
414
+ kind = tops.length ? "marked" : sections.length ? "sections" : "page";
415
+ if (list.length) {
416
+ container = most(list);
417
+ units = list.filter((el) => el.parent === container);
418
+ } else {
419
+ const page = body ?? root;
420
+ container = page.parent ?? root;
421
+ units = [page];
422
+ }
423
+ for (const unit of units) spot2(unit, null);
424
+ }
425
+ const size = sizeOf(html, kind, scripts, styles, [container, ...body ? [body] : [], ...outer ? [outer] : []]);
426
+ return { kind, ...size, root, box: container, units, stacks, spots, scripts, refresh, head: at, body };
427
+ }
428
+ var titleOf = (html, s) => {
429
+ const heading = elements(s.el).find((el2) => /^h[1-6]$/.test(el2.tag) && !(s.notes && under(el2, (up) => up === s.notes)));
430
+ const el = heading ?? s.blocks[0];
431
+ const head = el ? s.blocks.indexOf(el) : -1;
432
+ return { title: el ? titled(html.slice(el.open, el.close)) : "", head: head < 0 ? null : head };
433
+ };
434
+ var notesText = (html, s) => s.notes ? toText(html.slice(s.notes.open, s.notes.close)) : s.attr ? s.attr.value.trim() : "";
435
+ function readDeck(html, known) {
436
+ const s = known ?? shape(html);
437
+ return {
438
+ kind: s.kind,
439
+ width: s.width,
440
+ height: s.height,
441
+ center: s.center,
442
+ slides: s.spots.map((spot2, i) => ({
443
+ key: `s${i}`,
444
+ from: i,
445
+ stack: spot2.stack,
446
+ ...titleOf(html, spot2),
447
+ notes: notesText(html, spot2),
448
+ blocks: spot2.blocks.map((el, id) => ({ id, html: html.slice(el.open, el.close) }))
449
+ }))
450
+ };
451
+ }
452
+ function cuts(html, s, slide, stamp) {
453
+ const out = [];
454
+ s.blocks.forEach((el, i) => {
455
+ if (stamp && el !== s.el) out.push([el.start + 1 + el.tag.length, el.start + 1 + el.tag.length, ` data-w100n-id="${i}"`]);
456
+ const now = slide.blocks[i]?.html;
457
+ if (now !== void 0 && now !== html.slice(el.open, el.close)) out.push([el.open, el.close, now]);
458
+ });
459
+ if (slide.notes !== notesText(html, s)) {
460
+ if (s.notes) out.push([s.notes.open, s.notes.close, notesHtml(slide.notes)]);
461
+ else if (s.attr?.at) {
462
+ const quoted = /["']/.test(html[s.attr.at[0] - 1] ?? "");
463
+ out.push([s.attr.at[0], s.attr.at[1], quoted ? quote(slide.notes) : `"${quote(slide.notes)}"`]);
464
+ } else if (slide.notes.trim()) {
465
+ const inner = html.slice(s.el.open, s.el.close);
466
+ const lead = /^\s*/.exec(inner)[0];
467
+ const tail = /\s*$/.exec(inner)[0];
468
+ const indent = lead.includes("\n") ? lead.slice(lead.lastIndexOf("\n")) : "";
469
+ const at = Math.max(s.el.open, s.el.close - tail.length);
470
+ out.push([at, at, `${indent}<aside class="notes">${notesHtml(slide.notes)}</aside>`]);
471
+ }
472
+ }
473
+ return out;
474
+ }
475
+ function apply(html, from, to, list) {
476
+ const sorted = [...list].sort((a, b) => a[0] - b[0] || a[1] - b[1]);
477
+ const out = [];
478
+ let at = from;
479
+ for (const [start, end, put] of sorted) {
480
+ if (start < at || end > to) continue;
481
+ out.push(html.slice(at, start), put);
482
+ at = end;
483
+ }
484
+ out.push(html.slice(at, to));
485
+ return out.join("");
486
+ }
487
+ var blank = (s) => !/\S/.test(s);
488
+ var join = (html, box, els, parts) => {
489
+ const head = html.slice(box.open, els[0].start);
490
+ const pre = els.map((el, i) => i ? html.slice(els[i - 1].end, el.start) : "");
491
+ const fill = pre.find((gap, i) => i && blank(gap)) ?? (/\s*$/.exec(head)[0] || "\n");
492
+ const used = /* @__PURE__ */ new Set();
493
+ const body = parts.map(([from, part], k) => {
494
+ const first = !used.has(from);
495
+ used.add(from);
496
+ if (k) return (first && from ? pre[from] : fill) + part;
497
+ return (first && !blank(pre[from]) ? pre[from].trimStart() : "") + part;
498
+ });
499
+ const lost = pre.filter((gap, i) => !used.has(i) && !blank(gap)).map((gap) => gap.trimEnd());
500
+ return head + body.join("") + lost.join("") + html.slice(els[els.length - 1].end, box.close);
501
+ };
502
+ function writeDeck(original, deck, known) {
503
+ const s = known ?? shape(original);
504
+ const piece = (slide) => {
505
+ const spot2 = s.spots[slide.from];
506
+ return apply(original, spot2.el.start, spot2.el.end, cuts(original, spot2, slide, false));
507
+ };
508
+ const units = [];
509
+ for (let i = 0; i < deck.slides.length; ) {
510
+ const stack = deck.slides[i].stack;
511
+ if (stack === null) {
512
+ const slide = deck.slides[i++];
513
+ units.push([s.units.indexOf(s.spots[slide.from].el), piece(slide)]);
514
+ continue;
515
+ }
516
+ let j = i;
517
+ while (j < deck.slides.length && deck.slides[j].stack === stack) j++;
518
+ const el = s.stacks[stack];
519
+ const kids = s.spots.filter((spot2) => spot2.stack === stack).map((spot2) => spot2.el);
520
+ const inner = join(original, el, kids, deck.slides.slice(i, j).map((slide) => [kids.indexOf(s.spots[slide.from].el), piece(slide)]));
521
+ units.push([s.units.indexOf(el), original.slice(el.start, el.open) + inner + original.slice(el.close, el.end)]);
522
+ i = j;
523
+ }
524
+ return original.slice(0, s.box.open) + join(original, s.box, s.units, units) + original.slice(s.box.close);
525
+ }
526
+ function moveSlide(deck, from, to) {
527
+ const slide = deck.slides[from];
528
+ const rest = deck.slides.filter((_, i) => i !== from);
529
+ let at = Math.max(0, Math.min(rest.length, to));
530
+ const run = rest.flatMap((other, i) => slide.stack !== null && other.stack === slide.stack ? [i] : []);
531
+ if (run.length) at = Math.max(run[0], Math.min(run[run.length - 1] + 1, at));
532
+ else if (at > 0 && at < rest.length && rest[at].stack !== null && rest[at - 1].stack === rest[at].stack) {
533
+ let end = at;
534
+ while (end < rest.length && rest[end].stack === rest[at].stack) end++;
535
+ at = to > from ? end : rest.findIndex((other) => other.stack === rest[at].stack);
536
+ }
537
+ return { ...deck, slides: [...rest.slice(0, at), slide, ...rest.slice(at)] };
538
+ }
539
+ var duplicateSlide = (deck, i, key) => ({ ...deck, slides: [...deck.slides.slice(0, i + 1), { ...deck.slides[i], key }, ...deck.slides.slice(i + 1)] });
540
+ var deleteSlide = (deck, i) => ({ ...deck, slides: deck.slides.filter((_, k) => k !== i) });
541
+
542
+ // src/slides/frame.ts
543
+ var bridge = "(function(){var me=document.currentScript,d=me.dataset,key=d.key,root=document.documentElement,edit=d.edit==='1',H=+d.h;root.setAttribute('data-w100n-mode',d.mode);if(edit)root.setAttribute('data-w100n-edit','');function send(m){m.key=key;parent.postMessage(m,'*')}var slide=null,groups=[],on=null,snap='',last='',timer=0;function center(){if(!slide||d.center!=='1')return;slide.style.top='0px';slide.style.top=Math.max(0,(H-slide.scrollHeight)/2)+'px'}function collect(){var keyed={},order=[],loose=[];[].slice.call(slide.querySelectorAll('.fragment')).forEach(function(el){var i=el.getAttribute('data-fragment-index');if(i===null||isNaN(+i))loose.push([el]);else{if(!keyed[i]){keyed[i]=[];order.push(+i)}keyed[i].push(el)}});order.sort(function(a,b){return a-b});groups=order.map(function(i){return keyed[i]}).concat(loose)}function step(n){groups.forEach(function(g,i){g.forEach(function(el){el.classList.toggle('visible',i<n);el.classList.toggle('current-fragment',i===n-1)})})}function report(){clearTimeout(timer);if(on&&on.innerHTML!==last){last=on.innerHTML;send({w100nSlides:'text',id:+on.getAttribute('data-w100n-id'),html:last});center()}}function begin(el){if(on)end();on=el;snap=last=el.innerHTML;el.setAttribute('contenteditable','true')}function end(){var el=on;if(!el)return;report();el.removeAttribute('contenteditable');on=null}function start(){slide=document.querySelector('[data-w100n-slide]')||document.body;collect();center();if(window.ResizeObserver)new ResizeObserver(center).observe(slide);send({w100nSlides:'ready',fragments:groups.length})}if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start);else start();addEventListener('load',center);if(document.fonts)document.fonts.ready.then(center);addEventListener('message',function(e){var m=e.source===parent&&e.data;if(m&&m.w100nSlides==='step')step(+m.step||0)});addEventListener('click',function(e){if(e.target.closest&&e.target.closest('a'))e.preventDefault()},true);addEventListener('pointerdown',function(e){var el=edit&&e.target.closest&&e.target.closest('[data-w100n-id]');if(el&&el!==on)begin(el)},true);addEventListener('focusout',function(e){if(on&&e.target===on)end()});addEventListener('input',function(){clearTimeout(timer);timer=setTimeout(report,250)});addEventListener('paste',function(e){if(!on)return;e.preventDefault();document.execCommand('insertText',false,e.clipboardData?e.clipboardData.getData('text/plain'):'')},true);addEventListener('drop',function(e){if(on)e.preventDefault()},true);addEventListener('keydown',function(e){var k=e.key,mod=e.metaKey||e.ctrlKey;if(on){if(k==='Enter'&&!e.shiftKey){e.preventDefault();on.blur()}else if(k==='Enter'){e.preventDefault();document.execCommand('insertLineBreak')}else if(k==='Escape'){e.preventDefault();on.innerHTML=snap;report();on.blur()}else if(mod&&k.toLowerCase()==='s'){e.preventDefault();report();send({w100nSlides:'key',press:'s',mod:true,shift:false,alt:false})}return}if(/^(Arrow|Page|Home$|End$|Escape$| $)/.test(k)||mod&&/^[szy]$/i.test(k)){e.preventDefault();send({w100nSlides:'key',press:k,mod:mod,shift:e.shiftKey,alt:e.altKey})}},true)})()";
544
+ var retag = (el, add, extra) => {
545
+ const list = new Map([...el.attrs].map(([name, attr]) => [name, attr.value]));
546
+ if (extra.length) list.set("class", [...(list.get("class") ?? "").split(/\s+/).filter(Boolean), ...extra].join(" "));
547
+ for (const [name, value] of Object.entries(add)) list.set(name, value);
548
+ return `<${el.tag}${[...list].map(([name, value]) => ` ${name}="${quote(value)}"`).join("")}>`;
549
+ };
550
+ var folder = (url) => {
551
+ if (!url) return "";
552
+ try {
553
+ const u = new URL(url);
554
+ return /^https?:$/.test(u.protocol) ? (u.origin + u.pathname.replace(/[^/]*$/, "")).replace(/[;,'"\s]/g, encodeURIComponent) : "";
555
+ } catch {
556
+ return "";
557
+ }
558
+ };
559
+ var policy = (nonce, scripts, base) => {
560
+ const b = base ? ` ${base}` : "";
561
+ return [
562
+ "default-src 'none'",
563
+ `script-src ${scripts ? `'unsafe-inline' 'unsafe-eval'${b}` : `'nonce-${nonce}'`}`,
564
+ `style-src 'unsafe-inline'${b}`,
565
+ `img-src data: blob:${b}`,
566
+ `font-src data:${b}`,
567
+ `media-src data: blob:${b}`,
568
+ `connect-src ${scripts && base ? base : "'none'"}`,
569
+ "frame-src 'none'",
570
+ "object-src 'none'",
571
+ "form-action 'none'",
572
+ `base-uri ${base || "'none'"}`
573
+ ].join("; ");
574
+ };
575
+ var css = (value) => value.replace(/[<>{};\\"\n\r]/g, "");
576
+ var backdrop = (els) => {
577
+ for (const el of els) {
578
+ const get = (name) => el.attrs.get(name)?.value.trim();
579
+ const plain = get("data-background");
580
+ const image = get("data-background-image") ?? (plain && (/^(https?:|file:|\/\/)/i.test(plain) || /\.(svg|png|jpe?g|gif|bmp|webp)([?#\s]|$)/i.test(plain)) ? plain : void 0);
581
+ const color = get("data-background-color") ?? (plain && !image ? plain : void 0);
582
+ if (!image && !color) continue;
583
+ return `.reveal{${color ? `background-color:${css(color)}!important;` : ""}${image ? `background-image:url("${css(image)}")!important;background-size:${css(get("data-background-size") ?? "cover")};background-position:${css(get("data-background-position") ?? "center")};background-repeat:${css(get("data-background-repeat") ?? "no-repeat")};` : ""}}`;
584
+ }
585
+ return "";
586
+ };
587
+ var layout = (s, els) => {
588
+ const { width: w, height: h } = s;
589
+ const page = s.kind === "page" ? "" : `html,body{margin:0!important;width:${w}px!important;height:${h}px!important;overflow:hidden!important}`;
590
+ const reveal = s.kind !== "reveal" ? "" : `.reveal{position:relative!important;width:${w}px!important;height:${h}px!important;overflow:hidden!important}.reveal .slides{position:absolute!important;left:0!important;top:0!important;right:auto!important;bottom:auto!important;width:${w}px!important;height:${h}px!important;margin:0!important;transform:none!important;zoom:1!important;pointer-events:auto!important;text-align:center}.reveal .slides>section,.reveal .slides>section>section{position:absolute;left:0;width:100%;box-sizing:border-box}[data-w100n-stack]{top:0!important;height:100%}.reveal .backgrounds,.reveal .controls,.reveal .progress,.reveal .slide-number{display:none!important}[data-w100n-slide],[data-w100n-stack]{display:block!important;transform:none!important}` + backdrop(els);
591
+ return page + reveal + "[data-w100n-slide],[data-w100n-stack]{visibility:visible!important;opacity:1!important}aside.notes{display:none!important}a[href]{pointer-events:none!important}[data-w100n-edit] [data-w100n-id]{cursor:text}[data-w100n-edit] [data-w100n-id]:hover{outline:1.5px dashed #8b5cf6aa;outline-offset:3px}[data-w100n-id][contenteditable=true]{outline:2px solid #8b5cf6!important;outline-offset:3px}[data-w100n-mode=present] .fragment{transition:opacity .25s}[data-w100n-mode=present] .fragment:not(.visible){opacity:0;visibility:hidden}html:not([data-w100n-mode=present]) .fragment{opacity:1!important;visibility:inherit!important}";
592
+ };
593
+ function framePage(html, s, slide, o) {
594
+ const spot2 = s.spots[slide.from];
595
+ const unit = spot2.stack === null ? spot2.el : s.stacks[spot2.stack];
596
+ const reveal = s.kind === "reveal";
597
+ const list = [];
598
+ for (const el of s.units) if (el !== unit) list.push([el.start, el.end, ""]);
599
+ if (spot2.stack !== null) {
600
+ for (const other of s.spots) if (other.stack === spot2.stack && other !== spot2) list.push([other.el.start, other.el.end, ""]);
601
+ list.push([unit.start, unit.open, retag(unit, { "data-w100n-stack": "" }, reveal ? ["stack", "present"] : [])]);
602
+ }
603
+ list.push(...cuts(html, spot2, slide, true));
604
+ if (spot2.el.tag !== "#root") list.push([spot2.el.start, spot2.el.open, retag(spot2.el, { "data-w100n-slide": "", ...spot2.blocks[0] === spot2.el ? { "data-w100n-id": "0" } : {} }, reveal ? ["present"] : ["active", "current"])]);
605
+ if (reveal && s.body) list.push([s.body.start, s.body.open, retag(s.body, {}, ["reveal-viewport"])]);
606
+ for (const el of s.refresh) list.push([el.start, el.end, ""]);
607
+ for (const el of s.scripts) list.push(o.scripts ? [el.start + 7, el.start + 7, o.nonce ? ` nonce="${quote(o.nonce)}"` : ""] : [el.start, el.end, ""]);
608
+ const base = folder(o.base);
609
+ const tag = `<script nonce="${quote(o.nonce)}" data-key="${quote(o.id)}" data-mode="${o.mode}" data-edit="${o.edit ? 1 : 0}" data-h="${s.height}" data-center="${reveal && s.center ? 1 : 0}">`;
610
+ list.push([s.head, s.head, `<meta http-equiv="Content-Security-Policy" content="${quote(policy(o.nonce, !!o.scripts, base))}">${base ? `<base href="${quote(base)}">` : ""}<style>${layout(s, [spot2.el, ...spot2.stack === null ? [] : [unit]])}</style>${tag}${bridge}</script>`]);
611
+ return apply(html, 0, html.length, list);
612
+ }
613
+ var drop = /* @__PURE__ */ new Set([
614
+ "script",
615
+ "style",
616
+ "iframe",
617
+ "object",
618
+ "embed",
619
+ "template",
620
+ "svg",
621
+ "math",
622
+ "link",
623
+ "meta",
624
+ "base",
625
+ "form",
626
+ "input",
627
+ "button",
628
+ "textarea",
629
+ "select",
630
+ "img",
631
+ "picture",
632
+ "video",
633
+ "audio",
634
+ "canvas",
635
+ "noscript",
636
+ "title"
637
+ ]);
638
+ function clean(html) {
639
+ const box = document.createElement("template");
640
+ box.innerHTML = html;
641
+ const walk = (node) => {
642
+ for (const child of [...node.childNodes]) {
643
+ if (child.nodeType === Node.COMMENT_NODE) child.remove();
644
+ if (child.nodeType !== Node.ELEMENT_NODE) continue;
645
+ const el = child;
646
+ if (drop.has(el.localName)) {
647
+ el.remove();
648
+ continue;
649
+ }
650
+ walk(el);
651
+ if (!inline.has(el.localName)) {
652
+ el.replaceWith(...[...el.childNodes]);
653
+ continue;
654
+ }
655
+ for (const { name, value } of [...el.attributes]) {
656
+ if (/^on/i.test(name) || name === "contenteditable" || name.startsWith("data-w100n") || /^(href|src|xlink:href|action|formaction)$/i.test(name) && /^\s*(javascript|vbscript|data):/i.test(value)) el.removeAttribute(name);
657
+ }
658
+ }
659
+ };
660
+ walk(box.content);
661
+ return box.content.textContent?.trim() ? box.innerHTML : "";
662
+ }
663
+ var nonceOf = (doc) => doc.querySelector("script[nonce]")?.nonce || Math.random().toString(36).slice(2) + Date.now().toString(36);
664
+
665
+ // src/slides/text.ts
666
+ var en = {
667
+ slides: "Slides",
668
+ slide: "Slide",
669
+ tools: "Slide tools",
670
+ present: "Present",
671
+ presenter: "Presenter view",
672
+ duplicate: "Duplicate slide",
673
+ remove: "Delete slide",
674
+ earlier: "Move up",
675
+ later: "Move down",
676
+ undo: "Undo",
677
+ redo: "Redo",
678
+ save: "Save",
679
+ saving: "Saving",
680
+ saved: "Saved",
681
+ saveFailed: "Could not save",
682
+ notes: "Speaker notes",
683
+ notesHint: "What to say on this slide",
684
+ noNotes: "No notes for this slide",
685
+ editHint: "Click any text on the slide to change it",
686
+ next: "Next slide",
687
+ previous: "Previous slide",
688
+ leave: "End the show",
689
+ fullscreen: "Full screen",
690
+ upNext: "Up next",
691
+ last: "End of the deck",
692
+ elapsed: "Time",
693
+ pause: "Pause the timer",
694
+ resume: "Resume the timer",
695
+ restart: "Start the timer again",
696
+ now: "Now",
697
+ popup: "Allow pop-ups for this site to open the presenter view",
698
+ savedNote: "Slides edited"
699
+ };
700
+ var pl = {
701
+ slides: "Slajdy",
702
+ slide: "Slajd",
703
+ tools: "Narz\u0119dzia slajd\xF3w",
704
+ present: "Prezentuj",
705
+ presenter: "Widok prezentera",
706
+ duplicate: "Powiel slajd",
707
+ remove: "Usu\u0144 slajd",
708
+ earlier: "Przesu\u0144 wy\u017Cej",
709
+ later: "Przesu\u0144 ni\u017Cej",
710
+ undo: "Cofnij",
711
+ redo: "Pon\xF3w",
712
+ save: "Zapisz",
713
+ saving: "Zapisywanie",
714
+ saved: "Zapisano",
715
+ saveFailed: "Nie uda\u0142o si\u0119 zapisa\u0107",
716
+ notes: "Notatki prelegenta",
717
+ notesHint: "Co powiedzie\u0107 przy tym slajdzie",
718
+ noNotes: "Brak notatek do tego slajdu",
719
+ editHint: "Kliknij dowolny tekst na slajdzie, aby go zmieni\u0107",
720
+ next: "Nast\u0119pny slajd",
721
+ previous: "Poprzedni slajd",
722
+ leave: "Zako\u0144cz pokaz",
723
+ fullscreen: "Pe\u0142ny ekran",
724
+ upNext: "Nast\u0119pny",
725
+ last: "Koniec prezentacji",
726
+ elapsed: "Czas",
727
+ pause: "Wstrzymaj stoper",
728
+ resume: "Wzn\xF3w stoper",
729
+ restart: "Uruchom stoper od nowa",
730
+ now: "Teraz",
731
+ popup: "Zezw\xF3l tej stronie na wyskakuj\u0105ce okna, aby otworzy\u0107 widok prezentera",
732
+ savedNote: "Edycja slajd\xF3w"
733
+ };
734
+ var uk = {
735
+ slides: "\u0421\u043B\u0430\u0439\u0434\u0438",
736
+ slide: "\u0421\u043B\u0430\u0439\u0434",
737
+ tools: "\u0406\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0438 \u0441\u043B\u0430\u0439\u0434\u0456\u0432",
738
+ present: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u0438",
739
+ presenter: "\u0420\u0435\u0436\u0438\u043C \u0434\u043E\u043F\u043E\u0432\u0456\u0434\u0430\u0447\u0430",
740
+ duplicate: "\u0414\u0443\u0431\u043B\u044E\u0432\u0430\u0442\u0438 \u0441\u043B\u0430\u0439\u0434",
741
+ remove: "\u0412\u0438\u0434\u0430\u043B\u0438\u0442\u0438 \u0441\u043B\u0430\u0439\u0434",
742
+ earlier: "\u041F\u0435\u0440\u0435\u043C\u0456\u0441\u0442\u0438\u0442\u0438 \u0432\u0438\u0449\u0435",
743
+ later: "\u041F\u0435\u0440\u0435\u043C\u0456\u0441\u0442\u0438\u0442\u0438 \u043D\u0438\u0436\u0447\u0435",
744
+ undo: "\u0412\u0456\u0434\u043C\u0456\u043D\u0438\u0442\u0438",
745
+ redo: "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0438",
746
+ save: "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438",
747
+ saving: "\u0417\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u043D\u044F",
748
+ saved: "\u0417\u0431\u0435\u0440\u0435\u0436\u0435\u043D\u043E",
749
+ saveFailed: "\u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438",
750
+ notes: "\u041D\u043E\u0442\u0430\u0442\u043A\u0438 \u0434\u043E\u043F\u043E\u0432\u0456\u0434\u0430\u0447\u0430",
751
+ notesHint: "\u0429\u043E \u0441\u043A\u0430\u0437\u0430\u0442\u0438 \u043D\u0430 \u0446\u044C\u043E\u043C\u0443 \u0441\u043B\u0430\u0439\u0434\u0456",
752
+ noNotes: "\u0414\u043E \u0446\u044C\u043E\u0433\u043E \u0441\u043B\u0430\u0439\u0434\u0430 \u043D\u0435\u043C\u0430\u0454 \u043D\u043E\u0442\u0430\u0442\u043E\u043A",
753
+ editHint: "\u041A\u043B\u0430\u0446\u043D\u0456\u0442\u044C \u0431\u0443\u0434\u044C-\u044F\u043A\u0438\u0439 \u0442\u0435\u043A\u0441\u0442 \u043D\u0430 \u0441\u043B\u0430\u0439\u0434\u0456, \u0449\u043E\u0431 \u0437\u043C\u0456\u043D\u0438\u0442\u0438 \u0439\u043E\u0433\u043E",
754
+ next: "\u041D\u0430\u0441\u0442\u0443\u043F\u043D\u0438\u0439 \u0441\u043B\u0430\u0439\u0434",
755
+ previous: "\u041F\u043E\u043F\u0435\u0440\u0435\u0434\u043D\u0456\u0439 \u0441\u043B\u0430\u0439\u0434",
756
+ leave: "\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438 \u043F\u043E\u043A\u0430\u0437",
757
+ fullscreen: "\u041D\u0430 \u0432\u0435\u0441\u044C \u0435\u043A\u0440\u0430\u043D",
758
+ upNext: "\u0414\u0430\u043B\u0456",
759
+ last: "\u041A\u0456\u043D\u0435\u0446\u044C \u043F\u0440\u0435\u0437\u0435\u043D\u0442\u0430\u0446\u0456\u0457",
760
+ elapsed: "\u0427\u0430\u0441",
761
+ pause: "\u041F\u0440\u0438\u0437\u0443\u043F\u0438\u043D\u0438\u0442\u0438 \u0442\u0430\u0439\u043C\u0435\u0440",
762
+ resume: "\u041F\u0440\u043E\u0434\u043E\u0432\u0436\u0438\u0442\u0438 \u0442\u0430\u0439\u043C\u0435\u0440",
763
+ restart: "\u0417\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0438 \u0442\u0430\u0439\u043C\u0435\u0440 \u0437\u043D\u043E\u0432\u0443",
764
+ now: "\u0417\u0430\u0440\u0430\u0437",
765
+ popup: "\u0414\u043E\u0437\u0432\u043E\u043B\u044C\u0442\u0435 \u0441\u043F\u043B\u0438\u0432\u043D\u0456 \u0432\u0456\u043A\u043D\u0430 \u0434\u043B\u044F \u0446\u044C\u043E\u0433\u043E \u0441\u0430\u0439\u0442\u0443, \u0449\u043E\u0431 \u0432\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0440\u0435\u0436\u0438\u043C \u0434\u043E\u043F\u043E\u0432\u0456\u0434\u0430\u0447\u0430",
766
+ savedNote: "\u041F\u0440\u0430\u0432\u043A\u0430 \u0441\u043B\u0430\u0439\u0434\u0456\u0432"
767
+ };
768
+ var slidesTexts = { en, pl, uk };
769
+
770
+ // src/slides/SlidesView.tsx
771
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
772
+ var load = (html) => {
773
+ const form = shape(html);
774
+ return { html, form, deck: readDeck(html, form) };
775
+ };
776
+ var record = (s, deck, group) => ({ ...s, deck, past: group && group === s.group ? s.past : [...s.past.slice(-99), s.deck], future: [], group });
777
+ function reduce(s, a) {
778
+ if (a.type === "set") return record(s, a.deck, null);
779
+ if (a.type === "undo") return s.past.length ? { deck: s.past[s.past.length - 1], past: s.past.slice(0, -1), future: [s.deck, ...s.future], group: null, epoch: s.epoch + 1 } : s;
780
+ if (a.type === "redo") return s.future.length ? { deck: s.future[0], past: [...s.past, s.deck], future: s.future.slice(1), group: null, epoch: s.epoch + 1 } : s;
781
+ const slide = s.deck.slides.find((x) => x.key === a.key);
782
+ if (!slide) return s;
783
+ let next;
784
+ if (a.type === "notes") {
785
+ if (slide.notes === a.text) return s;
786
+ next = { ...slide, notes: a.text };
787
+ } else {
788
+ const block = slide.blocks.find((b) => b.id === a.id);
789
+ if (!block || block.html === a.html) return s;
790
+ next = { ...slide, blocks: slide.blocks.map((b) => b === block ? { ...b, html: a.html } : b), title: slide.head === a.id ? titled(a.html) : slide.title };
791
+ }
792
+ return record(s, { ...s.deck, slides: s.deck.slides.map((x) => x === slide ? next : x) }, `${a.type}:${a.key}:${a.type === "text" ? a.id : ""}`);
793
+ }
794
+ var differs = (a, b) => a.slides.length !== b.slides.length || a.slides.some((s, i) => s.from !== b.slides[i].from || s.stack !== b.slides[i].stack || s.notes !== b.slides[i].notes || s.blocks.some((x, k) => x.html !== b.slides[i].blocks[k].html));
795
+ function useBox() {
796
+ const [el, setEl] = useState(null);
797
+ const [box, setBox] = useState({ w: 0, h: 0 });
798
+ useEffect(() => {
799
+ const Watch = el?.ownerDocument.defaultView?.ResizeObserver;
800
+ if (!el || !Watch) return;
801
+ const watch = new Watch(([entry]) => setBox({ w: entry.contentRect.width, h: entry.contentRect.height }));
802
+ watch.observe(el);
803
+ return () => watch.disconnect();
804
+ }, [el]);
805
+ return [setEl, box];
806
+ }
807
+ function SlidesView({ html, name, rights, language = "en", theme = "light", baseUrl, scripts = false, onChange, onSaveRequest, onOutline, guard }) {
808
+ const t = slidesTexts[language];
809
+ const [source, setSource] = useState(() => load(html));
810
+ const [state, setState] = useState(() => ({ deck: source.deck, past: [], future: [], group: null, epoch: 0 }));
811
+ const live = useRef(state);
812
+ const [saved, setSaved] = useState({ html, deck: source.deck });
813
+ const [prop, setProp] = useState(html);
814
+ const [at, setAt] = useState(0);
815
+ if (html !== prop) {
816
+ setProp(html);
817
+ if (html !== saved.html) {
818
+ const next = load(html);
819
+ live.current = { deck: next.deck, past: [], future: [], group: null, epoch: state.epoch + 1 };
820
+ setSource(next);
821
+ setState(live.current);
822
+ setSaved({ html, deck: next.deck });
823
+ setAt(0);
824
+ }
825
+ }
826
+ const act = (a) => {
827
+ live.current = reduce(live.current, a);
828
+ setState(live.current);
829
+ };
830
+ const { deck, epoch } = state;
831
+ const count = deck.slides.length;
832
+ const index = Math.min(at, count - 1);
833
+ const slide = deck.slides[index];
834
+ const can = rights.edit && !!onSaveRequest;
835
+ const movable = can && deck.kind !== "page";
836
+ const dirty = differs(saved.deck, deck);
837
+ const [saving, setSaving] = useState("idle");
838
+ const [nonce] = useState(() => typeof document === "undefined" ? "" : nonceOf(document));
839
+ const made = useRef(0);
840
+ const frames = useRef(/* @__PURE__ */ new Map());
841
+ const pages = useRef(/* @__PURE__ */ new Map());
842
+ const root = useRef(null);
843
+ const rail = useRef(null);
844
+ const full = useRef(false);
845
+ const channel = useRef(null);
846
+ const [near, setNear] = useState(() => typeof IntersectionObserver === "undefined" ? null : /* @__PURE__ */ new Set());
847
+ const [drag, setDrag] = useState(null);
848
+ const [drop2, setDrop] = useState(null);
849
+ const [presenting, setPresenting] = useState(false);
850
+ const [popup, setPopup] = useState(null);
851
+ const [blocked, setBlocked] = useState(false);
852
+ const [nav, setNav] = useState({ at: 0, step: 0 });
853
+ const [steps2, setSteps] = useState({});
854
+ const [timer, setTimer] = useState({ from: 0, stop: null });
855
+ const [now, setNow] = useState(() => Date.now());
856
+ const [stageRef, stageBox] = useBox();
857
+ const [showRef, showBox] = useBox();
858
+ const [nowRef, nowBox] = useBox();
859
+ const [nextRef, nextBox] = useBox();
860
+ const build = (s, id, mode) => framePage(source.html, source.form, s, { id, mode, edit: mode === "edit" && can, nonce, base: baseUrl, scripts });
861
+ const rendered = [];
862
+ const frozen = (s, id, mode) => {
863
+ rendered.push(id);
864
+ const page = pages.current.get(id) ?? build(s, id, mode);
865
+ pages.current.set(id, page);
866
+ return page;
867
+ };
868
+ const fit = (box, pad = 0) => box.w > pad && box.h > pad ? Math.min((box.w - pad) / deck.width, (box.h - pad) / deck.height) : 0.5;
869
+ const byKey = (a, b) => deck.slides[a].key < deck.slides[b].key ? -1 : 1;
870
+ const around = (i) => [i - 1, i, i + 1].filter((k) => k >= 0 && k < count).sort(byKey);
871
+ const caption = (i) => `${t.slide} ${i + 1}${deck.slides[i].title ? `: ${deck.slides[i].title}` : ""}`;
872
+ const post = (id, msg) => frames.current.get(id)?.contentWindow?.postMessage(msg, "*");
873
+ const frame = (id, page, scale, title, hidden = false, still = false) => /* @__PURE__ */ jsx("span", { className: "w100n-slides-frame", "data-hidden": hidden || void 0, style: { width: Math.round(deck.width * scale), height: Math.round(deck.height * scale) }, children: /* @__PURE__ */ jsx(
874
+ "iframe",
875
+ {
876
+ ref: (el) => {
877
+ if (el) frames.current.set(id, el);
878
+ else frames.current.delete(id);
879
+ },
880
+ title,
881
+ sandbox: "allow-scripts",
882
+ referrerPolicy: "no-referrer",
883
+ srcDoc: page,
884
+ tabIndex: hidden || still ? -1 : void 0,
885
+ "aria-hidden": hidden || still || void 0,
886
+ style: { width: deck.width, height: deck.height, transform: `scale(${scale})` }
887
+ }
888
+ ) }, id);
889
+ const moveTo = (from, to) => {
890
+ const d = live.current.deck;
891
+ const next = moveSlide(d, from, to);
892
+ if (next.slides.every((s, i) => s === d.slides[i])) return;
893
+ act({ type: "set", deck: next });
894
+ setAt(next.slides.indexOf(d.slides[from]));
895
+ };
896
+ const duplicate = () => {
897
+ act({ type: "set", deck: duplicateSlide(live.current.deck, index, `d${++made.current}`) });
898
+ setAt(index + 1);
899
+ };
900
+ const remove = () => {
901
+ if (count < 2) return;
902
+ act({ type: "set", deck: deleteSlide(live.current.deck, index) });
903
+ setAt(Math.min(index, count - 2));
904
+ };
905
+ const save = async () => {
906
+ const d = live.current.deck;
907
+ if (!can || saving === "busy" || !differs(saved.deck, d)) return false;
908
+ setSaving("busy");
909
+ const out = writeDeck(source.html, d, source.form);
910
+ try {
911
+ await onSaveRequest(new TextEncoder().encode(out).buffer, t.savedNote);
912
+ setSaved({ html: out, deck: d });
913
+ setSaving("done");
914
+ return true;
915
+ } catch {
916
+ setSaving("failed");
917
+ return false;
918
+ }
919
+ };
920
+ const press = (k, typing = false) => {
921
+ const key = k.press.toLowerCase();
922
+ if (k.mod && key === "s") {
923
+ void save();
924
+ return true;
925
+ }
926
+ if (typing) return false;
927
+ if (k.mod && (key === "z" || key === "y")) {
928
+ if (can) act(key === "y" || k.shift ? { type: "redo" } : { type: "undo" });
929
+ return true;
930
+ }
931
+ if (k.mod && key === "d" && movable) {
932
+ duplicate();
933
+ return true;
934
+ }
935
+ const step = /^(arrowdown|arrowright|pagedown)$/.test(key) ? 1 : /^(arrowup|arrowleft|pageup)$/.test(key) ? -1 : 0;
936
+ if (step && k.alt) {
937
+ if (movable) moveTo(index, index + step);
938
+ return true;
939
+ }
940
+ if (step || key === "home" || key === "end") {
941
+ setAt(key === "home" ? 0 : key === "end" ? count - 1 : Math.max(0, Math.min(count - 1, index + step)));
942
+ return true;
943
+ }
944
+ return false;
945
+ };
946
+ const go = (to, step) => {
947
+ const next = { at: Math.max(0, Math.min(count - 1, to)), step: Math.max(0, step) };
948
+ setNav(next);
949
+ channel.current?.postMessage({ w100nSlides: "go", ...next });
950
+ };
951
+ const forward = () => {
952
+ if (nav.step < (steps2[deck.slides[nav.at].key] ?? 0)) go(nav.at, nav.step + 1);
953
+ else if (nav.at < count - 1) go(nav.at + 1, 0);
954
+ };
955
+ const back = () => {
956
+ if (nav.step > 0) go(nav.at, nav.step - 1);
957
+ else if (nav.at > 0) go(nav.at - 1, steps2[deck.slides[nav.at - 1].key] ?? 0);
958
+ };
959
+ const fullscreen = () => {
960
+ if (document.fullscreenElement) {
961
+ full.current = false;
962
+ void document.exitFullscreen();
963
+ } else root.current?.requestFullscreen?.().then(() => {
964
+ full.current = true;
965
+ }, () => {
966
+ });
967
+ };
968
+ const start = () => {
969
+ setNav({ at: index, step: 0 });
970
+ setPresenting(true);
971
+ fullscreen();
972
+ };
973
+ const stop = () => {
974
+ setPresenting(false);
975
+ setAt(nav.at);
976
+ if (document.fullscreenElement) {
977
+ full.current = false;
978
+ void document.exitFullscreen();
979
+ }
980
+ popup?.close();
981
+ setPopup(null);
982
+ };
983
+ const presenter = () => {
984
+ if (popup && !popup.closed) {
985
+ popup.focus();
986
+ return;
987
+ }
988
+ const win = window.open("", "w100n-presenter", "popup,width=1280,height=780");
989
+ setBlocked(!win);
990
+ if (!win) return;
991
+ win.document.head.replaceChildren(...[...document.querySelectorAll('style, link[rel="stylesheet"]')].map((node) => node.cloneNode(true)));
992
+ win.document.title = `${t.presenter} \xB7 ${name}`;
993
+ win.document.body.replaceChildren();
994
+ Object.assign(win.document.body.style, { margin: "0", height: "100vh" });
995
+ win.addEventListener("pagehide", () => setPopup(null));
996
+ setPopup(win);
997
+ setTimer({ from: Date.now(), stop: null });
998
+ if (!presenting) {
999
+ setNav({ at: index, step: 0 });
1000
+ setPresenting(true);
1001
+ }
1002
+ };
1003
+ useEffect(() => {
1004
+ const listen = (e) => {
1005
+ const m = e.data;
1006
+ if (!m || typeof m !== "object" || typeof m.w100nSlides !== "string" || typeof m.key !== "string") return;
1007
+ const el = frames.current.get(m.key);
1008
+ if (!el || !e.source || el.contentWindow !== e.source) return;
1009
+ const [kind, key] = m.key.split(":");
1010
+ if (m.w100nSlides === "text" && kind === "m" && can && typeof m.id === "number" && typeof m.html === "string") act({ type: "text", key, id: m.id, html: clean(m.html) });
1011
+ if (m.w100nSlides === "key" && kind === "m" && typeof m.press === "string") press({ press: m.press, mod: !!m.mod, shift: !!m.shift, alt: !!m.alt });
1012
+ if (m.w100nSlides === "ready" && typeof m.fragments === "number") {
1013
+ const n = m.fragments;
1014
+ setSteps((old) => old[key] === n ? old : { ...old, [key]: n });
1015
+ if ((kind === "p" || kind === "q") && deck.slides[nav.at]?.key === key) post(m.key, { w100nSlides: "step", step: nav.step });
1016
+ }
1017
+ };
1018
+ window.addEventListener("message", listen);
1019
+ popup?.addEventListener("message", listen);
1020
+ return () => {
1021
+ window.removeEventListener("message", listen);
1022
+ popup?.removeEventListener("message", listen);
1023
+ };
1024
+ });
1025
+ useEffect(() => {
1026
+ for (const id of [...pages.current.keys()]) if (!rendered.includes(id)) pages.current.delete(id);
1027
+ });
1028
+ useEffect(() => {
1029
+ onChange?.(dirty);
1030
+ }, [dirty]);
1031
+ useEffect(() => {
1032
+ if (!guard || !dirty || !can) return;
1033
+ guard.current = save;
1034
+ return () => {
1035
+ guard.current = null;
1036
+ };
1037
+ });
1038
+ useRestore(presenting);
1039
+ useEffect(() => {
1040
+ if (!dirty) return;
1041
+ const stay = (e) => e.preventDefault();
1042
+ window.addEventListener("beforeunload", stay);
1043
+ return () => window.removeEventListener("beforeunload", stay);
1044
+ }, [dirty]);
1045
+ const order = deck.slides.map((s) => s.key).join();
1046
+ const titles = deck.slides.map((s) => s.title).join("\n");
1047
+ useEffect(() => {
1048
+ onOutline?.(deck.slides.map((s, i) => ({ id: s.key, level: s.stack !== null && deck.slides[i - 1]?.stack === s.stack ? 2 : 1, text: `${i + 1}. ${s.title || t.slide}`, go: () => setAt(i) })));
1049
+ }, [order, titles, language]);
1050
+ useEffect(() => {
1051
+ const list = rail.current;
1052
+ if (!list || typeof IntersectionObserver === "undefined") return;
1053
+ const watch = new IntersectionObserver((entries) => setNear((old) => {
1054
+ const next = new Set(old ?? []);
1055
+ for (const entry of entries) {
1056
+ const key = entry.target.dataset.key;
1057
+ if (entry.isIntersecting) next.add(key);
1058
+ else next.delete(key);
1059
+ }
1060
+ return next;
1061
+ }), { root: list, rootMargin: "320px" });
1062
+ for (const li of list.querySelectorAll("li")) watch.observe(li);
1063
+ return () => watch.disconnect();
1064
+ }, [order]);
1065
+ useEffect(() => {
1066
+ rail.current?.querySelector('[aria-current="true"]')?.scrollIntoView?.({ block: "nearest" });
1067
+ }, [index]);
1068
+ useEffect(() => {
1069
+ if (!presenting && !popup) return;
1070
+ const key = deck.slides[nav.at]?.key;
1071
+ for (const id of frames.current.keys()) if (/^[pq]:/.test(id) && id.split(":")[1] === key) post(id, { w100nSlides: "step", step: nav.step });
1072
+ }, [nav, presenting, popup, epoch]);
1073
+ useEffect(() => {
1074
+ if (!presenting && !popup) return;
1075
+ const keys = (e) => {
1076
+ const k = e.key;
1077
+ if (/^(ArrowRight|ArrowDown|PageDown| |Enter|n|N)$/.test(k)) forward();
1078
+ else if (/^(ArrowLeft|ArrowUp|PageUp|Backspace|p|P)$/.test(k)) back();
1079
+ else if (k === "Home" || k === "End") go(k === "Home" ? 0 : count - 1, 0);
1080
+ else if (k === "Escape") stop();
1081
+ else if (k === "f" || k === "F") fullscreen();
1082
+ else return;
1083
+ e.preventDefault();
1084
+ e.stopPropagation();
1085
+ };
1086
+ if (presenting) window.addEventListener("keydown", keys, true);
1087
+ popup?.addEventListener("keydown", keys, true);
1088
+ const change = () => {
1089
+ if (document.fullscreenElement || !full.current) return;
1090
+ full.current = false;
1091
+ stop();
1092
+ };
1093
+ document.addEventListener("fullscreenchange", change);
1094
+ return () => {
1095
+ window.removeEventListener("keydown", keys, true);
1096
+ popup?.removeEventListener("keydown", keys, true);
1097
+ document.removeEventListener("fullscreenchange", change);
1098
+ };
1099
+ });
1100
+ useEffect(() => {
1101
+ if (!presenting && !popup || typeof BroadcastChannel === "undefined") return;
1102
+ const ch = new BroadcastChannel(`w100n-slides:${name}`);
1103
+ ch.onmessage = (e) => {
1104
+ const m = e.data;
1105
+ if (m?.w100nSlides === "go" && typeof m.at === "number" && typeof m.step === "number") setNav({ at: Math.max(0, Math.min(count - 1, m.at)), step: Math.max(0, m.step) });
1106
+ };
1107
+ channel.current = ch;
1108
+ return () => {
1109
+ ch.close();
1110
+ channel.current = null;
1111
+ };
1112
+ }, [presenting, popup, name, count]);
1113
+ useEffect(() => {
1114
+ if (!popup) return;
1115
+ const tick = setInterval(() => setNow(Date.now()), 1e3);
1116
+ return () => {
1117
+ clearInterval(tick);
1118
+ popup.close();
1119
+ };
1120
+ }, [popup]);
1121
+ const tool = (Icon, text, run, disabled = false) => /* @__PURE__ */ jsx("button", { type: "button", "aria-label": text, title: text, disabled, onClick: run, children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": true }) });
1122
+ const thumb = 148;
1123
+ const seconds = Math.floor(Math.max(0, (timer.stop ?? now) - timer.from) / 1e3);
1124
+ const elapsed = `${seconds >= 3600 ? `${Math.floor(seconds / 3600)}:` : ""}${String(Math.floor(seconds / 60) % 60).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}`;
1125
+ const shown = deck.slides[Math.min(nav.at, count - 1)];
1126
+ const first = nav.at === 0 && nav.step === 0;
1127
+ const end = nav.at === count - 1 && nav.step >= (steps2[shown.key] ?? 0);
1128
+ return /* @__PURE__ */ jsxs("div", { ref: root, className: "w100n-slides", onKeyDown: (e) => {
1129
+ if (press({ press: e.key, mod: e.metaKey || e.ctrlKey, shift: e.shiftKey, alt: e.altKey }, /^(TEXTAREA|INPUT|SELECT)$/.test(e.target.tagName))) e.preventDefault();
1130
+ }, children: [
1131
+ /* @__PURE__ */ jsxs("div", { className: "w100n-pdf-bar w100n-slides-bar", role: "toolbar", "aria-label": t.tools, onKeyDown: rove, children: [
1132
+ /* @__PURE__ */ jsxs("button", { type: "button", className: "w100n-docs-action", onClick: start, children: [
1133
+ /* @__PURE__ */ jsx(Play, { "aria-hidden": true }),
1134
+ t.present
1135
+ ] }),
1136
+ tool(Projector, t.presenter, presenter),
1137
+ movable && /* @__PURE__ */ jsxs(Fragment, { children: [
1138
+ /* @__PURE__ */ jsx("span", { className: "w100n-pdf-gap" }),
1139
+ tool(Copy, t.duplicate, duplicate),
1140
+ tool(Trash2, t.remove, remove, count < 2),
1141
+ tool(ArrowUp, t.earlier, () => moveTo(index, index - 1), index === 0),
1142
+ tool(ArrowDown, t.later, () => moveTo(index, index + 1), index === count - 1)
1143
+ ] }),
1144
+ can && /* @__PURE__ */ jsxs(Fragment, { children: [
1145
+ /* @__PURE__ */ jsx("span", { className: "w100n-pdf-gap" }),
1146
+ tool(Undo2, t.undo, () => act({ type: "undo" }), !state.past.length),
1147
+ tool(Redo2, t.redo, () => act({ type: "redo" }), !state.future.length)
1148
+ ] }),
1149
+ /* @__PURE__ */ jsxs("span", { className: "w100n-pdf-pageno", children: [
1150
+ index + 1,
1151
+ " / ",
1152
+ count
1153
+ ] }),
1154
+ blocked && /* @__PURE__ */ jsx("span", { className: "w100n-docs-muted", children: t.popup }),
1155
+ can && saving === "failed" && /* @__PURE__ */ jsx("span", { className: "w100n-docs-muted", children: t.saveFailed }),
1156
+ can && /* @__PURE__ */ jsxs("button", { type: "button", className: "w100n-docs-action", disabled: !dirty || saving === "busy", onClick: () => void save(), children: [
1157
+ /* @__PURE__ */ jsx(Save, { "aria-hidden": true }),
1158
+ saving === "busy" ? t.saving : !dirty && saving === "done" ? t.saved : t.save
1159
+ ] })
1160
+ ] }),
1161
+ /* @__PURE__ */ jsxs("div", { className: "w100n-slides-body", children: [
1162
+ /* @__PURE__ */ jsx("nav", { ref: rail, className: "w100n-slides-rail", "aria-label": t.slides, children: /* @__PURE__ */ jsx("ol", { children: deck.slides.map((s, i) => /* @__PURE__ */ jsxs(
1163
+ "li",
1164
+ {
1165
+ "data-key": s.key,
1166
+ "data-stack": s.stack !== null && deck.slides[i - 1]?.stack === s.stack ? "in" : void 0,
1167
+ "data-drop": drop2 === i ? "before" : drop2 === i + 1 && i === count - 1 ? "after" : void 0,
1168
+ children: [
1169
+ /* @__PURE__ */ jsx(
1170
+ "button",
1171
+ {
1172
+ type: "button",
1173
+ className: "w100n-slides-thumb",
1174
+ "aria-current": i === index,
1175
+ "aria-label": caption(i),
1176
+ draggable: movable,
1177
+ "data-dragging": drag === i || void 0,
1178
+ onClick: () => setAt(i),
1179
+ onDragStart: (e) => {
1180
+ e.dataTransfer.effectAllowed = "move";
1181
+ e.dataTransfer.setData("text/plain", String(i));
1182
+ setDrag(i);
1183
+ },
1184
+ onDragOver: (e) => {
1185
+ if (drag === null) return;
1186
+ e.preventDefault();
1187
+ const box = e.currentTarget.getBoundingClientRect();
1188
+ setDrop(e.clientY < box.top + box.height / 2 ? i : i + 1);
1189
+ },
1190
+ onDrop: (e) => {
1191
+ e.preventDefault();
1192
+ if (drag !== null && drop2 !== null) moveTo(drag, drop2 > drag ? drop2 - 1 : drop2);
1193
+ setDrag(null);
1194
+ setDrop(null);
1195
+ },
1196
+ onDragEnd: () => {
1197
+ setDrag(null);
1198
+ setDrop(null);
1199
+ },
1200
+ children: i + 1
1201
+ }
1202
+ ),
1203
+ /* @__PURE__ */ jsx("span", { className: "w100n-slides-mini", style: { width: thumb, height: Math.round(thumb * deck.height / deck.width) }, children: (!near || near.has(s.key)) && frame(`t:${s.key}`, build(s, `t:${s.key}`, "thumb"), thumb / deck.width, caption(i), false, true) })
1204
+ ]
1205
+ },
1206
+ s.key
1207
+ )) }) }),
1208
+ /* @__PURE__ */ jsxs("div", { className: "w100n-slides-main", children: [
1209
+ /* @__PURE__ */ jsx("div", { ref: stageRef, className: "w100n-slides-stage", style: { "--w100n-ratio": `${deck.width} / ${deck.height}` }, children: around(index).map((i) => {
1210
+ const id = `m:${deck.slides[i].key}:${epoch}`;
1211
+ return frame(id, frozen(deck.slides[i], id, "edit"), fit(stageBox, 32), caption(i), i !== index);
1212
+ }) }),
1213
+ /* @__PURE__ */ jsxs("section", { className: "w100n-slides-notes", "aria-label": t.notes, children: [
1214
+ /* @__PURE__ */ jsxs("header", { children: [
1215
+ /* @__PURE__ */ jsx(StickyNote, { "aria-hidden": true }),
1216
+ t.notes,
1217
+ can && /* @__PURE__ */ jsx("small", { children: t.editHint })
1218
+ ] }),
1219
+ can ? /* @__PURE__ */ jsx("textarea", { value: slide.notes, placeholder: t.notesHint, "aria-label": t.notes, onChange: (e) => act({ type: "notes", key: slide.key, text: e.target.value }) }) : /* @__PURE__ */ jsx("p", { children: slide.notes || t.noNotes })
1220
+ ] })
1221
+ ] })
1222
+ ] }),
1223
+ presenting && /* @__PURE__ */ jsxs(
1224
+ "div",
1225
+ {
1226
+ ref: showRef,
1227
+ className: "w100n-slides-show",
1228
+ role: "dialog",
1229
+ "aria-label": `${t.present} \xB7 ${name}`,
1230
+ onClick: forward,
1231
+ onContextMenu: (e) => {
1232
+ e.preventDefault();
1233
+ back();
1234
+ },
1235
+ children: [
1236
+ around(nav.at).map((i) => {
1237
+ const id = `p:${deck.slides[i].key}:${epoch}`;
1238
+ return frame(id, frozen(deck.slides[i], id, "present"), fit(showBox), caption(i), i !== nav.at, true);
1239
+ }),
1240
+ /* @__PURE__ */ jsxs("span", { className: "w100n-slides-count", children: [
1241
+ nav.at + 1,
1242
+ " / ",
1243
+ count
1244
+ ] }),
1245
+ /* @__PURE__ */ jsx("div", { className: "w100n-slides-foot", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs("div", { className: "w100n-slides-controls", role: "toolbar", "aria-label": t.tools, onKeyDown: rove, children: [
1246
+ tool(ChevronLeft, t.previous, back, first),
1247
+ tool(ChevronRight, t.next, forward, end),
1248
+ tool(Projector, t.presenter, presenter),
1249
+ tool(Maximize, t.fullscreen, fullscreen),
1250
+ tool(X, t.leave, stop)
1251
+ ] }) })
1252
+ ]
1253
+ }
1254
+ ),
1255
+ popup && createPortal(/* @__PURE__ */ jsxs("div", { className: "w100n-docs w100n-slides-presenter", "data-theme": theme, children: [
1256
+ /* @__PURE__ */ jsxs("header", { className: "w100n-slides-head", children: [
1257
+ /* @__PURE__ */ jsx("span", { className: "w100n-docs-tile", children: /* @__PURE__ */ jsx(Timer, { "aria-hidden": true }) }),
1258
+ /* @__PURE__ */ jsx("b", { className: "w100n-slides-time", title: t.elapsed, children: elapsed }),
1259
+ tool(
1260
+ timer.stop === null ? Pause : Play,
1261
+ timer.stop === null ? t.pause : t.resume,
1262
+ () => setTimer(timer.stop === null ? { ...timer, stop: Date.now() } : { from: timer.from + Date.now() - timer.stop, stop: null })
1263
+ ),
1264
+ tool(RotateCcw, t.restart, () => setTimer({ from: Date.now(), stop: timer.stop === null ? null : Date.now() })),
1265
+ /* @__PURE__ */ jsxs("span", { className: "w100n-docs-muted", children: [
1266
+ t.now,
1267
+ " ",
1268
+ new Intl.DateTimeFormat(language, { hour: "2-digit", minute: "2-digit" }).format(now)
1269
+ ] }),
1270
+ /* @__PURE__ */ jsxs("span", { className: "w100n-slides-end", children: [
1271
+ /* @__PURE__ */ jsxs("span", { className: "w100n-pdf-pageno", children: [
1272
+ nav.at + 1,
1273
+ " / ",
1274
+ count
1275
+ ] }),
1276
+ tool(ChevronLeft, t.previous, back, first),
1277
+ tool(ChevronRight, t.next, forward, end),
1278
+ /* @__PURE__ */ jsxs("button", { type: "button", className: "w100n-docs-action", onClick: stop, children: [
1279
+ /* @__PURE__ */ jsx(X, { "aria-hidden": true }),
1280
+ t.leave
1281
+ ] })
1282
+ ] })
1283
+ ] }),
1284
+ /* @__PURE__ */ jsxs("div", { className: "w100n-slides-desk", children: [
1285
+ /* @__PURE__ */ jsx("div", { ref: nowRef, className: "w100n-slides-now", children: frame(`q:${shown.key}:${epoch}`, frozen(shown, `q:${shown.key}:${epoch}`, "present"), fit(nowBox, 16), caption(nav.at), false, true) }),
1286
+ /* @__PURE__ */ jsxs("aside", { className: "w100n-slides-side", children: [
1287
+ /* @__PURE__ */ jsx("p", { className: "w100n-docs-caption", children: t.upNext }),
1288
+ /* @__PURE__ */ jsx("div", { ref: nextRef, className: "w100n-slides-next", children: nav.at < count - 1 ? frame(
1289
+ `n:${deck.slides[nav.at + 1].key}:${epoch}`,
1290
+ frozen(deck.slides[nav.at + 1], `n:${deck.slides[nav.at + 1].key}:${epoch}`, "thumb"),
1291
+ (nextBox.w || 320) / deck.width,
1292
+ caption(nav.at + 1),
1293
+ false,
1294
+ true
1295
+ ) : /* @__PURE__ */ jsx("p", { className: "w100n-docs-muted", children: t.last }) }),
1296
+ /* @__PURE__ */ jsxs("p", { className: "w100n-docs-caption", children: [
1297
+ /* @__PURE__ */ jsx(StickyNote, { "aria-hidden": true }),
1298
+ t.notes
1299
+ ] }),
1300
+ /* @__PURE__ */ jsx("div", { className: "w100n-slides-speech", children: shown.notes || t.noNotes })
1301
+ ] })
1302
+ ] })
1303
+ ] }), popup.document.body)
1304
+ ] });
1305
+ }
1306
+
1307
+ // src/Unsaved.tsx
1308
+ import { Save as Save2, Trash2 as Trash22 } from "lucide-react";
1309
+ import { useId, useState as useState2 } from "react";
1310
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1311
+ function useUnsaved(guard, t) {
1312
+ const id = useId();
1313
+ const [asking, setAsking] = useState2(null);
1314
+ useRestore(!!asking);
1315
+ const ask = (then, stay) => guard.current ? setAsking({ then, stay, state: "idle" }) : then();
1316
+ const cancel = () => {
1317
+ asking?.stay?.();
1318
+ setAsking(null);
1319
+ };
1320
+ const leave = (save) => async () => {
1321
+ const was = asking;
1322
+ if (save && guard.current) {
1323
+ setAsking({ ...was, state: "busy" });
1324
+ if (!await guard.current()) return setAsking({ ...was, state: "failed" });
1325
+ }
1326
+ setAsking(null);
1327
+ was.then();
1328
+ };
1329
+ const dialog = asking && /* @__PURE__ */ jsx2("div", { className: "w100n-pdf-back w100n-docs-ask", onKeyDown: (e) => {
1330
+ if (e.key === "Escape") {
1331
+ e.stopPropagation();
1332
+ cancel();
1333
+ }
1334
+ }, children: /* @__PURE__ */ jsxs2("div", { className: "w100n-pdf-dialog", role: "alertdialog", "aria-modal": true, "aria-labelledby": `${id}t`, "aria-describedby": `${id}d`, children: [
1335
+ /* @__PURE__ */ jsx2("b", { id: `${id}t`, className: "w100n-pdf-dialog-title", children: t.unsavedTitle }),
1336
+ /* @__PURE__ */ jsx2("p", { id: `${id}d`, className: "w100n-docs-muted", role: asking.state === "failed" ? "alert" : void 0, children: asking.state === "failed" ? t.saveFailed : t.unsavedHint }),
1337
+ /* @__PURE__ */ jsxs2("div", { className: "w100n-pdf-dialog-end", children: [
1338
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "w100n-pdf-link", onClick: cancel, children: t.cancel }),
1339
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", disabled: asking.state === "busy", onClick: leave(false), children: [
1340
+ /* @__PURE__ */ jsx2(Trash22, { "aria-hidden": true }),
1341
+ t.discard
1342
+ ] }),
1343
+ /* @__PURE__ */ jsxs2("button", { type: "button", className: "w100n-docs-action", autoFocus: true, disabled: asking.state === "busy", onClick: leave(true), children: [
1344
+ /* @__PURE__ */ jsx2(Save2, { "aria-hidden": true }),
1345
+ asking.state === "busy" ? t.saving : t.save
1346
+ ] })
1347
+ ] })
1348
+ ] }) });
1349
+ return { ask, dialog };
1350
+ }
1351
+
1352
+ // src/viewers/PdfView.tsx
1353
+ import {
1354
+ ChevronDown,
1355
+ ChevronUp,
1356
+ Ellipsis,
1357
+ Eraser,
1358
+ Highlighter,
1359
+ ImageUp,
1360
+ Maximize as Maximize2,
1361
+ Minus,
1362
+ MousePointer2,
1363
+ MoveHorizontal,
1364
+ PanelLeft,
1365
+ PenLine,
1366
+ Plus,
1367
+ Redo2 as Redo22,
1368
+ Save as Save3,
1369
+ Search,
1370
+ Signature as Sign,
1371
+ StickyNote as StickyNote3,
1372
+ Strikethrough,
1373
+ Type,
1374
+ Underline,
1375
+ Undo2 as Undo22,
1376
+ X as X3
1377
+ } from "lucide-react";
1378
+ import { Fragment as Fragment3, useEffect as useEffect3, useId as useId2, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
1379
+
1380
+ // src/viewers/PdfPage.tsx
1381
+ import { StickyNote as StickyNote2, Trash2 as Trash23, X as X2 } from "lucide-react";
1382
+ import { useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
1383
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1384
+ var pct = (v) => `${v * 100}%`;
1385
+ var same = (a, b) => !!b && a.page === b.page && a.start === b.start;
1386
+ var spot = (b, size) => ({ left: pct(b.x / size.w), top: pct(b.y / size.h), width: pct(b.w / size.w), height: pct(b.h / size.h) });
1387
+ function PdfPage({ pdf, n, size, scale, live, id, layers }) {
1388
+ const paint = useRef2(null);
1389
+ const words = useRef2(null);
1390
+ const forms = useRef2(null);
1391
+ const focus = useRef2(null);
1392
+ const rubbed = useRef2(null);
1393
+ const [ready, setReady] = useState3(0);
1394
+ const [boxes2, setBoxes] = useState3([]);
1395
+ const [stroke, setStroke] = useState3(null);
1396
+ const [drag, setDrag] = useState3(null);
1397
+ const fill = !!layers?.forms;
1398
+ const hitKey = layers ? layers.hits.map((hit) => `${hit.start}:${hit.end}`).join() : "";
1399
+ useEffect2(() => {
1400
+ if (!live) return;
1401
+ const holders = [paint, words, forms].flatMap((ref) => ref.current ? [ref.current] : []);
1402
+ return () => {
1403
+ for (const holder of holders) {
1404
+ for (const canvas of holder.querySelectorAll("canvas")) {
1405
+ canvas.width = 0;
1406
+ canvas.height = 0;
1407
+ }
1408
+ holder.replaceChildren();
1409
+ }
1410
+ };
1411
+ }, [live]);
1412
+ useEffect2(() => {
1413
+ if (!live || !pdf) return;
1414
+ let stop = false;
1415
+ let task;
1416
+ const canvas = document.createElement("canvas");
1417
+ const timer = setTimeout(() => {
1418
+ void Promise.all([pdf.getPage(n), import("pdfjs-dist")]).then(async ([page, { AnnotationMode }]) => {
1419
+ if (stop) return;
1420
+ const viewport = page.getViewport({ scale });
1421
+ const ratio = Math.min(window.devicePixelRatio || 1, Math.sqrt(16777216 / (viewport.width * viewport.height)));
1422
+ canvas.width = Math.floor(viewport.width * ratio);
1423
+ canvas.height = Math.floor(viewport.height * ratio);
1424
+ task = page.render({
1425
+ canvas,
1426
+ canvasContext: canvas.getContext("2d"),
1427
+ viewport,
1428
+ transform: [ratio, 0, 0, ratio, 0, 0],
1429
+ annotationMode: fill ? AnnotationMode.ENABLE_FORMS : AnnotationMode.ENABLE
1430
+ });
1431
+ await task.promise;
1432
+ if (stop) return;
1433
+ for (const old of paint.current.querySelectorAll("canvas")) {
1434
+ old.width = 0;
1435
+ old.height = 0;
1436
+ }
1437
+ paint.current.replaceChildren(canvas);
1438
+ }).catch(() => {
1439
+ });
1440
+ }, paint.current.firstChild ? 120 : 0);
1441
+ return () => {
1442
+ stop = true;
1443
+ clearTimeout(timer);
1444
+ task?.cancel();
1445
+ if (!canvas.isConnected) {
1446
+ canvas.width = 0;
1447
+ canvas.height = 0;
1448
+ }
1449
+ };
1450
+ }, [pdf, n, scale, live, fill]);
1451
+ useEffect2(() => {
1452
+ if (!live || !pdf || !layers) return;
1453
+ let stop = false;
1454
+ let text;
1455
+ const links = {
1456
+ externalLinkEnabled: true,
1457
+ addLinkAttributes: (link, url) => {
1458
+ link.href = url;
1459
+ link.target = "_blank";
1460
+ link.rel = "noopener noreferrer nofollow";
1461
+ },
1462
+ getDestinationHash: () => "#",
1463
+ getAnchorUrl: () => "#",
1464
+ executeNamedAction: () => {
1465
+ },
1466
+ executeSetOCGState: () => {
1467
+ },
1468
+ goToDestination: async (dest) => {
1469
+ const target = typeof dest === "string" ? await pdf.getDestination(dest) : dest;
1470
+ const ref = Array.isArray(target) ? target[0] : null;
1471
+ const index = typeof ref === "number" ? ref : ref && typeof ref === "object" ? await pdf.getPageIndex(ref) : null;
1472
+ if (index !== null) paint.current?.closest(".w100n-pdf-pages")?.querySelector(`[data-n="${index + 1}"]`)?.scrollIntoView({ block: "start" });
1473
+ }
1474
+ };
1475
+ void Promise.all([pdf.getPage(n), import("pdfjs-dist")]).then(async ([page, pdfjs]) => {
1476
+ if (stop) return;
1477
+ const viewport = page.getViewport({ scale: 1 });
1478
+ const layer = document.createElement("div");
1479
+ layer.className = "textLayer";
1480
+ text = new pdfjs.TextLayer({ textContentSource: page.streamTextContent(), container: layer, viewport });
1481
+ await text.render();
1482
+ if (stop) return;
1483
+ const end = document.createElement("div");
1484
+ end.className = "endOfContent";
1485
+ layer.append(end);
1486
+ words.current.replaceChildren(layer);
1487
+ const annotations = await page.getAnnotations();
1488
+ for (const a of annotations) {
1489
+ if (!a.fieldName || a.pushButton || !["Tx", "Btn", "Ch"].includes(a.fieldType)) continue;
1490
+ const [x1, y1] = viewport.convertToViewportPoint(a.rect[0], a.rect[1]);
1491
+ const [x2, y2] = viewport.convertToViewportPoint(a.rect[2], a.rect[3]);
1492
+ const options = a.fieldType === "Ch" ? a.options : void 0;
1493
+ const value = a.checkBox ? a.fieldValue === a.exportValue : a.radioButton ? a.fieldValue === a.buttonValue : options ? [a.fieldValue].flat().filter(Boolean).map((v) => options.find((o) => o.exportValue === v)?.displayValue ?? v) : a.fieldValue ?? "";
1494
+ layers.widgets.set(a.id, { page: n, name: a.fieldName, box: { x: Math.min(x1, x2), y: Math.min(y1, y2), w: Math.abs(x2 - x1), h: Math.abs(y2 - y1) }, value, options });
1495
+ }
1496
+ const div = document.createElement("div");
1497
+ div.className = "annotationLayer";
1498
+ const flat2 = viewport.clone({ dontFlip: true });
1499
+ await new pdfjs.AnnotationLayer({
1500
+ div,
1501
+ page,
1502
+ viewport: flat2,
1503
+ linkService: links,
1504
+ annotationStorage: pdf.annotationStorage,
1505
+ accessibilityManager: null,
1506
+ annotationCanvasMap: null,
1507
+ annotationEditorUIManager: null,
1508
+ structTreeLayer: null,
1509
+ commentManager: null
1510
+ }).render({ annotations, div, page, viewport: flat2, linkService: links, renderForms: fill, annotationStorage: pdf.annotationStorage, enableScripting: false, hasJSActions: false });
1511
+ if (stop) return;
1512
+ forms.current.replaceChildren(div);
1513
+ setReady((count) => count + 1);
1514
+ }).catch(() => {
1515
+ });
1516
+ return () => {
1517
+ stop = true;
1518
+ text?.cancel();
1519
+ };
1520
+ }, [pdf, n, live, fill]);
1521
+ useLayoutEffect(() => {
1522
+ const layer = words.current?.firstElementChild;
1523
+ if (!layers || !layer || !hitKey) {
1524
+ setBoxes([]);
1525
+ return;
1526
+ }
1527
+ const frame = layer.getBoundingClientRect();
1528
+ const k = frame.width ? size.w / frame.width : 1 / scale;
1529
+ const parts = [];
1530
+ let at2 = 0;
1531
+ const walk = document.createTreeWalker(layer, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT);
1532
+ for (let node = walk.nextNode(); node; node = walk.nextNode()) {
1533
+ if (node.nodeType === Node.TEXT_NODE) {
1534
+ parts.push({ node, start: at2 });
1535
+ at2 += node.length;
1536
+ } else if (node.nodeName === "BR") at2 += 1;
1537
+ }
1538
+ setBoxes(layers.hits.flatMap((hit) => parts.flatMap(({ node, start }) => {
1539
+ const from = Math.max(hit.start, start);
1540
+ const to = Math.min(hit.end, start + node.length);
1541
+ if (from >= to) return [];
1542
+ const range = document.createRange();
1543
+ range.setStart(node, from - start);
1544
+ range.setEnd(node, to - start);
1545
+ return [...range.getClientRects()].map((r) => ({ x: (r.left - frame.left) * k, y: (r.top - frame.top) * k, w: r.width * k, h: r.height * k, hit }));
1546
+ })));
1547
+ }, [ready, hitKey]);
1548
+ useEffect2(() => {
1549
+ const el = focus.current;
1550
+ const scroller = el?.closest(".w100n-pdf-scroll");
1551
+ if (!el || !scroller) return;
1552
+ const r = el.getBoundingClientRect();
1553
+ const view = scroller.getBoundingClientRect();
1554
+ if (r.top < view.top + 40 || r.bottom > view.bottom - 40) scroller.scrollBy({ top: r.top - view.top - view.height / 2 });
1555
+ }, [layers?.active, boxes2]);
1556
+ const style = { width: size.w * scale, height: size.h * scale, "--total-scale-factor": scale, "--scale-round-x": "1px", "--scale-round-y": "1px" };
1557
+ if (!layers) return /* @__PURE__ */ jsx3("div", { className: "w100n-pdf-page", "data-n": n, style, children: /* @__PURE__ */ jsx3("div", { ref: paint, className: "w100n-pdf-paint" }) });
1558
+ const { tool, marks, act, t, open } = layers;
1559
+ const first = boxes2.findIndex((b) => same(b.hit, layers.active));
1560
+ const at = (e) => {
1561
+ const r = e.currentTarget.closest(".w100n-pdf-page").getBoundingClientRect();
1562
+ return { x: (e.clientX - r.left) / scale, y: (e.clientY - r.top) / scale };
1563
+ };
1564
+ const line = (points) => points.map((p, i) => `${i ? "L" : "M"}${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ");
1565
+ const down = (e) => {
1566
+ const p = at(e);
1567
+ if (tool === "note" || tool === "place") {
1568
+ e.preventDefault();
1569
+ if (tool === "note") act.note(n, p);
1570
+ else act.place(n, p);
1571
+ } else {
1572
+ e.currentTarget.setPointerCapture(e.pointerId);
1573
+ if (tool === "ink") setStroke([p]);
1574
+ else {
1575
+ rubbed.current = p;
1576
+ act.erase(n, p);
1577
+ }
1578
+ }
1579
+ };
1580
+ const move = (e) => {
1581
+ if (!e.buttons) return;
1582
+ const p = at(e);
1583
+ if (stroke) {
1584
+ const last = stroke[stroke.length - 1];
1585
+ if (Math.hypot(p.x - last.x, p.y - last.y) > 0.8) setStroke([...stroke, p]);
1586
+ } else if (tool === "eraser" && rubbed.current) {
1587
+ const from = rubbed.current;
1588
+ const steps2 = Math.ceil(Math.hypot(p.x - from.x, p.y - from.y) / 3);
1589
+ for (let i = 1; i <= steps2; i++) act.erase(n, { x: from.x + (p.x - from.x) * i / steps2, y: from.y + (p.y - from.y) * i / steps2 });
1590
+ rubbed.current = p;
1591
+ }
1592
+ };
1593
+ const grab = (e, mark, resize) => {
1594
+ e.stopPropagation();
1595
+ e.currentTarget.setPointerCapture(e.pointerId);
1596
+ act.open(mark.id);
1597
+ setDrag({ id: mark.id, from: at(e), start: mark.box, box: mark.box, resize });
1598
+ };
1599
+ const pull = (e) => {
1600
+ if (!drag) return;
1601
+ const p = at(e);
1602
+ const dx = p.x - drag.from.x;
1603
+ const { start } = drag;
1604
+ if (drag.resize) {
1605
+ const w = Math.max(24, Math.min(size.w - start.x, start.w + dx));
1606
+ setDrag({ ...drag, box: { ...start, w, h: w * start.h / start.w } });
1607
+ } else {
1608
+ setDrag({ ...drag, box: { ...start, x: Math.max(0, Math.min(size.w - start.w, start.x + dx)), y: Math.max(0, Math.min(size.h - start.h, start.y + p.y - drag.from.y)) } });
1609
+ }
1610
+ };
1611
+ const drop2 = () => {
1612
+ if (drag && (drag.box.x !== drag.start.x || drag.box.y !== drag.start.y || drag.box.w !== drag.start.w)) act.move(drag.id, drag.box);
1613
+ setDrag(null);
1614
+ };
1615
+ return /* @__PURE__ */ jsxs3("div", { className: "w100n-pdf-page", id, "data-n": n, style, children: [
1616
+ /* @__PURE__ */ jsx3("div", { ref: paint, className: "w100n-pdf-paint" }),
1617
+ /* @__PURE__ */ jsx3("div", { ref: words, className: "w100n-pdf-words" }),
1618
+ /* @__PURE__ */ jsx3("div", { ref: forms, className: "w100n-pdf-forms" }),
1619
+ /* @__PURE__ */ jsxs3("svg", { className: "w100n-pdf-marks", viewBox: `0 0 ${size.w} ${size.h}`, preserveAspectRatio: "none", "aria-hidden": true, children: [
1620
+ marks.map((mark) => {
1621
+ if (mark.kind === "ink") return /* @__PURE__ */ jsx3("path", { d: mark.lines.map(line).join(" "), stroke: mark.color, strokeWidth: mark.width, children: /* @__PURE__ */ jsx3("title", { children: `${mark.author} \xB7 ${layers.when(mark.at)}` }) }, mark.id);
1622
+ if (mark.kind === "highlight") return /* @__PURE__ */ jsx3("g", { className: "w100n-pdf-glow", children: mark.rects.map((r, i) => /* @__PURE__ */ jsx3("rect", { x: r.x, y: r.y, width: r.w, height: r.h, fill: mark.color }, i)) }, mark.id);
1623
+ if (mark.kind === "underline" || mark.kind === "strike") {
1624
+ return /* @__PURE__ */ jsx3("g", { children: mark.rects.map((r, i) => {
1625
+ const y = mark.kind === "underline" ? r.y + r.h * 0.94 : r.y + r.h * 0.55;
1626
+ return /* @__PURE__ */ jsx3("line", { x1: r.x, x2: r.x + r.w, y1: y, y2: y, stroke: mark.color, strokeWidth: Math.max(0.8, r.h * 0.08) }, i);
1627
+ }) }, mark.id);
1628
+ }
1629
+ return null;
1630
+ }),
1631
+ stroke && /* @__PURE__ */ jsx3("path", { d: line(stroke), stroke: layers.pen.color, strokeWidth: layers.pen.width })
1632
+ ] }),
1633
+ boxes2.map((b, i) => /* @__PURE__ */ jsx3(
1634
+ "div",
1635
+ {
1636
+ ref: i === first ? focus : void 0,
1637
+ className: "w100n-pdf-hit",
1638
+ "data-active": same(b.hit, layers.active),
1639
+ style: spot(b, size)
1640
+ },
1641
+ i
1642
+ )),
1643
+ ["ink", "eraser", "note", "place"].includes(tool) && /* @__PURE__ */ jsx3(
1644
+ "div",
1645
+ {
1646
+ className: "w100n-pdf-input",
1647
+ "data-tool": tool,
1648
+ onPointerDown: down,
1649
+ onPointerMove: move,
1650
+ onPointerUp: () => {
1651
+ if (stroke) act.ink(n, stroke);
1652
+ setStroke(null);
1653
+ }
1654
+ }
1655
+ ),
1656
+ marks.map((mark) => {
1657
+ if (mark.kind === "note") {
1658
+ return /* @__PURE__ */ jsxs3(
1659
+ "div",
1660
+ {
1661
+ className: "w100n-pdf-note",
1662
+ "data-side": `${mark.spot.x > size.w / 2 ? "left" : "right"} ${mark.spot.y > size.h * 0.7 ? "up" : "down"}`,
1663
+ style: { left: pct(mark.spot.x / size.w), top: pct(mark.spot.y / size.h) },
1664
+ children: [
1665
+ /* @__PURE__ */ jsx3("button", { type: "button", className: "w100n-pdf-pin", "aria-label": t.note, "aria-expanded": open === mark.id, onClick: () => act.open(open === mark.id ? null : mark.id), children: /* @__PURE__ */ jsx3(StickyNote2, { "aria-hidden": true }) }),
1666
+ open === mark.id && /* @__PURE__ */ jsxs3("div", { className: "w100n-pdf-card", role: "dialog", "aria-label": t.note, children: [
1667
+ /* @__PURE__ */ jsxs3("div", { className: "w100n-pdf-card-head", children: [
1668
+ /* @__PURE__ */ jsxs3("span", { children: [
1669
+ /* @__PURE__ */ jsx3("b", { children: mark.author }),
1670
+ /* @__PURE__ */ jsx3("small", { children: layers.when(mark.at) })
1671
+ ] }),
1672
+ /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => act.remove(mark.id), "aria-label": t.remove, title: t.remove, children: /* @__PURE__ */ jsx3(Trash23, { "aria-hidden": true }) })
1673
+ ] }),
1674
+ /* @__PURE__ */ jsx3("textarea", { autoFocus: true, defaultValue: mark.text, placeholder: t.notePlaceholder, "aria-label": t.note, onBlur: (e) => act.text(mark.id, e.target.value) })
1675
+ ] })
1676
+ ]
1677
+ },
1678
+ mark.id
1679
+ );
1680
+ }
1681
+ if (mark.kind !== "sign") return null;
1682
+ return /* @__PURE__ */ jsxs3(
1683
+ "div",
1684
+ {
1685
+ className: "w100n-pdf-sign",
1686
+ "aria-selected": open === mark.id,
1687
+ style: spot(drag?.id === mark.id ? drag.box : mark.box, size),
1688
+ title: `${mark.author} \xB7 ${layers.when(mark.at)}`,
1689
+ onPointerDown: (e) => grab(e, mark, false),
1690
+ onPointerMove: pull,
1691
+ onPointerUp: drop2,
1692
+ children: [
1693
+ /* @__PURE__ */ jsx3("img", { src: mark.image, alt: t.signature, draggable: false }),
1694
+ open === mark.id && /* @__PURE__ */ jsxs3(Fragment2, { children: [
1695
+ /* @__PURE__ */ jsx3("span", { className: "w100n-pdf-grip", onPointerDown: (e) => grab(e, mark, true) }),
1696
+ /* @__PURE__ */ jsx3("button", { type: "button", className: "w100n-pdf-drop", onPointerDown: (e) => e.stopPropagation(), onClick: () => act.remove(mark.id), "aria-label": t.remove, title: t.remove, children: /* @__PURE__ */ jsx3(X2, { "aria-hidden": true }) })
1697
+ ] })
1698
+ ]
1699
+ },
1700
+ mark.id
1701
+ );
1702
+ })
1703
+ ] });
1704
+ }
1705
+
1706
+ // src/viewers/PdfView.tsx
1707
+ import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1708
+ var steps = [25, 33, 50, 67, 75, 90, 100, 110, 125, 150, 175, 200, 250, 300, 400];
1709
+ var uid = () => Math.random().toString(36).slice(2, 10);
1710
+ var dist = (p, a, b) => {
1711
+ const dx = b.x - a.x;
1712
+ const dy = b.y - a.y;
1713
+ const k = Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / (dx * dx + dy * dy || 1)));
1714
+ return Math.hypot(p.x - a.x - k * dx, p.y - a.y - k * dy);
1715
+ };
1716
+ var touches = (m, p) => {
1717
+ const inside = (b) => p.x >= b.x - 2 && p.x <= b.x + b.w + 2 && p.y >= b.y - 2 && p.y <= b.y + b.h + 2;
1718
+ if (m.kind === "note") return inside({ ...m.spot, w: 20, h: 20 });
1719
+ if (m.kind === "sign") return inside(m.box);
1720
+ if (m.kind === "ink") return m.lines.some((line) => line.some((q, i) => dist(p, line[Math.max(0, i - 1)], q) < m.width / 2 + 4));
1721
+ return m.rects.some(inside);
1722
+ };
1723
+ function PdfView({ bytes, t, language, worker, rights, host, documentId, onSaved, onOutline, guard }) {
1724
+ const prefix = useId2();
1725
+ const root = useRef3(null);
1726
+ const bar = useRef3(null);
1727
+ const scroller = useRef3(null);
1728
+ const railBox = useRef3(null);
1729
+ const finder = useRef3(null);
1730
+ const pad = useRef3(null);
1731
+ const inked = useRef3(null);
1732
+ const widgets = useRef3(/* @__PURE__ */ new Map());
1733
+ const words = useRef3(/* @__PURE__ */ new Map());
1734
+ const anchor = useRef3({ scale: 0, page: 0, frac: 0 });
1735
+ const [pdf, setPdf] = useState4(null);
1736
+ const [sizes, setSizes] = useState4([]);
1737
+ const [error, setError] = useState4(false);
1738
+ const [box, setBox] = useState4({ w: 0, h: 0 });
1739
+ const [top, setTop] = useState4(0);
1740
+ const [railTop, setRailTop] = useState4(0);
1741
+ const [fit, setFit] = useState4("auto");
1742
+ const [zoom, setZoom] = useState4(100);
1743
+ const [rail, setRail] = useState4(false);
1744
+ const [typed, setTyped] = useState4(null);
1745
+ const [query, setQuery] = useState4(null);
1746
+ const [hits, setHits] = useState4({ list: [], done: true });
1747
+ const [at, setAt] = useState4(0);
1748
+ const [tool, setTool] = useState4("select");
1749
+ const [pen, setPen] = useState4({ highlight: "#facc15", line: "#dc2626", ink: "#2563eb", width: 2 });
1750
+ const [palette, setPalette] = useState4(false);
1751
+ const [more, setMore] = useState4(false);
1752
+ const [level, setLevel] = useState4(0);
1753
+ const [history, setHistory] = useState4({ marks: [], past: [], future: [] });
1754
+ const [clean2, setClean] = useState4(null);
1755
+ const [filled, setFilled] = useState4(false);
1756
+ const [open, setOpen] = useState4(null);
1757
+ const [signing, setSigning] = useState4(null);
1758
+ const [pending, setPending] = useState4(null);
1759
+ const [saving, setSaving] = useState4("idle");
1760
+ const store = host?.save && documentId ? host.save : null;
1761
+ const can = { mark: !!store && !!rights && (rights.comment || rights.edit), edit: !!store && !!rights?.edit };
1762
+ const marks = history.marks;
1763
+ const dirty = marks.length > 0 && marks !== clean2 || filled;
1764
+ const me = host?.me.name ?? "";
1765
+ useEffect3(() => {
1766
+ let stop = false;
1767
+ let close = () => {
1768
+ };
1769
+ setPdf(null);
1770
+ setError(false);
1771
+ setHistory({ marks: [], past: [], future: [] });
1772
+ setFilled(false);
1773
+ setOpen(null);
1774
+ widgets.current = /* @__PURE__ */ new Map();
1775
+ words.current = /* @__PURE__ */ new Map();
1776
+ (async () => {
1777
+ var _a;
1778
+ const pdfjs = await import("pdfjs-dist");
1779
+ if (worker) (_a = pdfjs.GlobalWorkerOptions).workerPort ?? (_a.workerPort = worker());
1780
+ const task = pdfjs.getDocument({ data: new Uint8Array(bytes.slice(0)) });
1781
+ close = () => {
1782
+ void task.destroy();
1783
+ };
1784
+ const doc = await task.promise;
1785
+ const first = (await doc.getPage(1)).getViewport({ scale: 1 });
1786
+ if (stop) return;
1787
+ Object.assign(doc.annotationStorage, { onSetModified: () => setFilled(true) });
1788
+ setSizes(Array.from({ length: doc.numPages }, () => ({ w: first.width, h: first.height })));
1789
+ setPdf(doc);
1790
+ const flat2 = async (entries2, level2) => (await Promise.all(entries2.map(async (entry) => {
1791
+ const dest = typeof entry.dest === "string" ? await doc.getDestination(entry.dest) : entry.dest;
1792
+ const ref = Array.isArray(dest) ? dest[0] : null;
1793
+ const index = typeof ref === "number" ? ref : ref && typeof ref === "object" ? await doc.getPageIndex(ref) : null;
1794
+ const here = index === null ? [] : [{ id: `${prefix}-${index + 1}`, level: level2, text: entry.title }];
1795
+ return [...here, ...level2 < 3 ? await flat2(entry.items ?? [], level2 + 1) : []];
1796
+ }))).flat();
1797
+ const outline = await flat2(await doc.getOutline() ?? [], 1);
1798
+ if (!stop) onOutline?.(outline);
1799
+ const all = [];
1800
+ for (let n = 1; n <= doc.numPages && !stop; n += 64) {
1801
+ all.push(...await Promise.all(Array.from({ length: Math.min(64, doc.numPages - n + 1) }, async (_, i) => {
1802
+ const v = (await doc.getPage(n + i)).getViewport({ scale: 1 });
1803
+ return { w: v.width, h: v.height };
1804
+ })));
1805
+ }
1806
+ if (!stop && all.some((s) => s.w !== first.width || s.h !== first.height)) setSizes(all);
1807
+ })().catch(() => {
1808
+ if (!stop) setError(true);
1809
+ });
1810
+ return () => {
1811
+ stop = true;
1812
+ close();
1813
+ };
1814
+ }, [bytes, worker, onOutline]);
1815
+ useLayoutEffect2(() => {
1816
+ const el = scroller.current;
1817
+ let width = root.current.clientWidth;
1818
+ const measure = () => {
1819
+ setBox({ w: el.clientWidth, h: el.clientHeight });
1820
+ if (root.current.clientWidth !== width) setLevel(0);
1821
+ width = root.current.clientWidth;
1822
+ };
1823
+ measure();
1824
+ const watch = new ResizeObserver(measure);
1825
+ watch.observe(el);
1826
+ watch.observe(root.current);
1827
+ return () => watch.disconnect();
1828
+ }, []);
1829
+ useLayoutEffect2(() => {
1830
+ const items = bar.current.children;
1831
+ if (level < 2 && items[items.length - 1].offsetTop - items[0].offsetTop > 16) setLevel(level + 1);
1832
+ });
1833
+ useRestore(palette);
1834
+ useRestore(more);
1835
+ useRestore(signing !== null);
1836
+ useEffect3(() => {
1837
+ if (!more && !palette) return;
1838
+ const close = (e) => {
1839
+ if (e.target.closest(".w100n-pdf-palette")) return;
1840
+ setMore(false);
1841
+ setPalette(false);
1842
+ };
1843
+ document.addEventListener("pointerdown", close);
1844
+ return () => document.removeEventListener("pointerdown", close);
1845
+ }, [more, palette]);
1846
+ const widest = Math.max(1, ...sizes.map((s) => s.w));
1847
+ const tallest = Math.max(1, ...sizes.map((s) => s.h));
1848
+ const room = { w: Math.max(120, box.w - 32), h: Math.max(120, box.h - 32) };
1849
+ const scale = fit === "auto" ? Math.min(room.w / widest, 5 / 3) : fit === "width" ? room.w / widest : fit === "page" ? Math.min(room.w / widest, room.h / tallest) : zoom / 75;
1850
+ const percent = Math.round(scale * 75);
1851
+ const tops = [];
1852
+ let end = 16;
1853
+ for (const s of sizes) {
1854
+ tops.push(end);
1855
+ end += s.h * scale + 16;
1856
+ }
1857
+ let page = 1;
1858
+ while (page < tops.length && tops[page] <= top + box.h / 3) page++;
1859
+ const live = (i) => !!pdf && tops[i] < top + box.h * 2 && tops[i] + sizes[i].h * scale > top - box.h;
1860
+ const thumbTops = [];
1861
+ let thumbEnd = 10;
1862
+ for (const s of sizes) {
1863
+ thumbTops.push(thumbEnd);
1864
+ thumbEnd += s.h * 96 / s.w + 34;
1865
+ }
1866
+ const thumbLive = (i) => thumbTops[i] < railTop + box.h * 2 && thumbTops[i] + 160 > railTop - box.h;
1867
+ const centre = (y) => {
1868
+ let i = 0;
1869
+ while (i + 1 < tops.length && tops[i + 1] <= y + box.h / 2) i++;
1870
+ return { page: i, frac: sizes[i] ? (y + box.h / 2 - tops[i]) / (sizes[i].h * scale) : 0 };
1871
+ };
1872
+ useLayoutEffect2(() => {
1873
+ const was = anchor.current;
1874
+ if (was.scale && was.scale !== scale && sizes[was.page]) scroller.current.scrollTop = tops[was.page] + was.frac * sizes[was.page].h * scale - box.h / 2;
1875
+ anchor.current = { ...was, scale };
1876
+ }, [scale]);
1877
+ useEffect3(() => {
1878
+ const el = scroller.current;
1879
+ const wheel = (e) => {
1880
+ if (!e.ctrlKey && !e.metaKey) return;
1881
+ e.preventDefault();
1882
+ setFit(null);
1883
+ setZoom(Math.min(400, Math.max(25, Math.round(percent * Math.exp(-e.deltaY / 300)))));
1884
+ };
1885
+ el.addEventListener("wheel", wheel, { passive: false });
1886
+ return () => el.removeEventListener("wheel", wheel);
1887
+ }, [percent]);
1888
+ useEffect3(() => {
1889
+ if (!pdf || !query?.trim()) {
1890
+ setHits({ list: [], done: true });
1891
+ return;
1892
+ }
1893
+ let stop = false;
1894
+ const needle = query.trim().toLowerCase();
1895
+ setHits({ list: [], done: false });
1896
+ setAt(0);
1897
+ const timer = setTimeout(async () => {
1898
+ const found = [];
1899
+ for (let n = 1; n <= pdf.numPages && !stop; n++) {
1900
+ let text = words.current.get(n);
1901
+ if (text === void 0) {
1902
+ const content = await (await pdf.getPage(n)).getTextContent();
1903
+ text = content.items.map((piece) => "str" in piece ? piece.str + (piece.hasEOL ? " " : "") : "").join("").toLowerCase();
1904
+ words.current.set(n, text);
1905
+ }
1906
+ for (let i = text.indexOf(needle); i >= 0; i = text.indexOf(needle, i + needle.length)) found.push({ page: n, start: i, end: i + needle.length });
1907
+ if (!stop && (n % 20 === 0 || n === pdf.numPages)) setHits({ list: [...found], done: n === pdf.numPages });
1908
+ }
1909
+ }, 200);
1910
+ return () => {
1911
+ stop = true;
1912
+ clearTimeout(timer);
1913
+ };
1914
+ }, [pdf, query]);
1915
+ useEffect3(() => {
1916
+ if (rail) railBox.current?.querySelector('[aria-current="true"]')?.scrollIntoView({ block: "nearest" });
1917
+ }, [rail, page]);
1918
+ useEffect3(() => {
1919
+ if (!dirty) return;
1920
+ const stop = (e) => e.preventDefault();
1921
+ window.addEventListener("beforeunload", stop);
1922
+ return () => window.removeEventListener("beforeunload", stop);
1923
+ }, [dirty]);
1924
+ useLayoutEffect2(() => {
1925
+ const canvas = pad.current;
1926
+ if (!canvas) return;
1927
+ const ratio = window.devicePixelRatio || 1;
1928
+ canvas.width = canvas.clientWidth * ratio;
1929
+ canvas.height = canvas.clientHeight * ratio;
1930
+ const ctx = canvas.getContext("2d");
1931
+ ctx?.scale(ratio, ratio);
1932
+ inked.current = null;
1933
+ }, [signing?.tab]);
1934
+ const go = (n) => scroller.current.scrollTo({ top: tops[Math.min(Math.max(n, 1), tops.length) - 1] - 8 });
1935
+ const change = (next) => setHistory((h) => {
1936
+ const marks2 = next(h.marks);
1937
+ return marks2 === h.marks ? h : { marks: marks2, past: [...h.past, h.marks], future: [] };
1938
+ });
1939
+ const undo = () => setHistory((h) => h.past.length ? { marks: h.past[h.past.length - 1], past: h.past.slice(0, -1), future: [h.marks, ...h.future] } : h);
1940
+ const redo = () => setHistory((h) => h.future.length ? { marks: h.future[0], past: [...h.past, h.marks], future: h.future.slice(1) } : h);
1941
+ const stamp = () => ({ id: uid(), author: me, at: (/* @__PURE__ */ new Date()).toISOString() });
1942
+ const zoomBy = (d) => {
1943
+ setFit(null);
1944
+ setZoom(d > 0 ? steps.find((s) => s > percent + 1) ?? 400 : [...steps].reverse().find((s) => s < percent - 1) ?? 25);
1945
+ };
1946
+ const step = (d) => {
1947
+ if (!hits.list.length) return;
1948
+ const next = (at + d + hits.list.length) % hits.list.length;
1949
+ setAt(next);
1950
+ const n = hits.list[next].page;
1951
+ if (tops[n - 1] + sizes[n - 1].h * scale < top || tops[n - 1] > top + box.h) go(n);
1952
+ };
1953
+ const markText = (kind) => {
1954
+ const picked = document.getSelection();
1955
+ if (!picked || picked.isCollapsed || !picked.rangeCount || !scroller.current.contains(picked.anchorNode)) return false;
1956
+ const range = picked.getRangeAt(0);
1957
+ const made = [];
1958
+ for (const el of scroller.current.querySelectorAll(".w100n-pdf-page")) {
1959
+ if (!range.intersectsNode(el)) continue;
1960
+ const frame = el.getBoundingClientRect();
1961
+ const rects = [];
1962
+ let text = "";
1963
+ const walk = document.createTreeWalker(el.querySelector(".w100n-pdf-words"), NodeFilter.SHOW_TEXT);
1964
+ for (let node = walk.nextNode(); node; node = walk.nextNode()) {
1965
+ if (!range.intersectsNode(node)) continue;
1966
+ const part = document.createRange();
1967
+ part.selectNodeContents(node);
1968
+ if (node === range.startContainer) part.setStart(node, range.startOffset);
1969
+ if (node === range.endContainer) part.setEnd(node, range.endOffset);
1970
+ text += part.toString();
1971
+ for (const r of part.getClientRects()) if (r.width > 0.5) rects.push({ x: (r.left - frame.left) / scale, y: (r.top - frame.top) / scale, w: r.width / scale, h: r.height / scale });
1972
+ }
1973
+ const lines = rects.sort((a, b) => a.y - b.y || a.x - b.x).reduce((all, r) => {
1974
+ const line = all.find((l) => Math.abs(l.y + l.h / 2 - r.y - r.h / 2) < Math.min(l.h, r.h) / 2 && r.x <= l.x + l.w + r.h);
1975
+ if (!line) return [...all, { ...r }];
1976
+ const x = Math.min(line.x, r.x);
1977
+ const y = Math.min(line.y, r.y);
1978
+ Object.assign(line, { x, y, w: Math.max(line.x + line.w, r.x + r.w) - x, h: Math.max(line.y + line.h, r.y + r.h) - y });
1979
+ return all;
1980
+ }, []);
1981
+ if (lines.length) made.push({ ...stamp(), page: Number(el.dataset.n), kind, rects: lines, text: text.trim(), color: kind === "highlight" ? pen.highlight : pen.line });
1982
+ }
1983
+ picked.removeAllRanges();
1984
+ if (made.length) change((all) => [...all, ...made]);
1985
+ return made.length > 0;
1986
+ };
1987
+ const pick = (next) => {
1988
+ setPalette(false);
1989
+ if ((next === "highlight" || next === "underline" || next === "strike") && markText(next)) return;
1990
+ setTool(tool === next ? "select" : next);
1991
+ };
1992
+ const act = {
1993
+ note: (n, p) => {
1994
+ const mark = { ...stamp(), page: n, kind: "note", spot: { x: p.x - 10, y: p.y - 10 }, text: "", color: "#f59e0b" };
1995
+ change((all) => [...all, mark]);
1996
+ setOpen(mark.id);
1997
+ setTool("select");
1998
+ },
1999
+ text: (id, text) => change((all) => text.trim() ? all.map((m) => m.id === id && m.kind === "note" && m.text !== text ? { ...m, text } : m) : all.filter((m) => m.id !== id)),
2000
+ ink: (n, line) => change((all) => [...all, { ...stamp(), page: n, kind: "ink", lines: [line], color: pen.ink, width: pen.width }]),
2001
+ erase: (n, p) => change((all) => {
2002
+ const keep = all.filter((m) => m.page !== n || !touches(m, p));
2003
+ return keep.length === all.length ? all : keep;
2004
+ }),
2005
+ place: (n, p) => {
2006
+ if (!pending) return;
2007
+ const size = sizes[n - 1];
2008
+ const w = Math.min(180, size.w / 2);
2009
+ const h = w / pending.ratio;
2010
+ const mark = {
2011
+ ...stamp(),
2012
+ page: n,
2013
+ kind: "sign",
2014
+ method: pending.method,
2015
+ image: pending.image,
2016
+ box: { x: Math.max(0, Math.min(size.w - w, p.x - w / 2)), y: Math.max(0, Math.min(size.h - h, p.y - h / 2)), w, h }
2017
+ };
2018
+ change((all) => [...all, mark]);
2019
+ setPending(null);
2020
+ setTool("select");
2021
+ setOpen(mark.id);
2022
+ },
2023
+ move: (id, b) => change((all) => all.map((m) => m.id === id && m.kind === "sign" ? { ...m, box: b } : m)),
2024
+ remove: (id) => change((all) => all.filter((m) => m.id !== id)),
2025
+ open: setOpen
2026
+ };
2027
+ const fields = () => {
2028
+ const out = [];
2029
+ for (const [id, stored] of pdf?.annotationStorage.serializable.map ?? []) {
2030
+ const w = widgets.current.get(id);
2031
+ if (!w || !stored || typeof stored !== "object" || !("value" in stored)) continue;
2032
+ const value = w.options ? [stored.value].flat().filter(Boolean).map((v) => w.options.find((o) => o.exportValue === v)?.displayValue ?? v) : stored.value;
2033
+ if (JSON.stringify(value) !== JSON.stringify(w.value)) out.push({ page: w.page, name: w.name, box: w.box, value });
2034
+ }
2035
+ return out;
2036
+ };
2037
+ const save = async () => {
2038
+ if (!store || !documentId) return false;
2039
+ setSaving("busy");
2040
+ try {
2041
+ const { write } = await import("./pdfWrite-EM6GJDBF.js");
2042
+ const changed = fields();
2043
+ const signs = marks.flatMap((m) => m.kind === "sign" ? [m] : []);
2044
+ const out = await write(bytes, marks, changed);
2045
+ const note = [marks.length > signs.length && `${t.marksNote} ${marks.length - signs.length}`, changed.length > 0 && t.formNote, signs.length > 0 && t.signedNote].filter(Boolean).join(", ");
2046
+ const { versionId } = await store(documentId, out, note);
2047
+ for (const s of signs) await host.sign?.(documentId, { versionId, page: s.page, method: s.method });
2048
+ setClean(marks);
2049
+ setFilled(false);
2050
+ pdf.annotationStorage.resetModified();
2051
+ setSaving("done");
2052
+ onSaved?.(out);
2053
+ return true;
2054
+ } catch {
2055
+ setSaving("failed");
2056
+ return false;
2057
+ }
2058
+ };
2059
+ useEffect3(() => {
2060
+ if (!guard || !dirty || !can.mark) return;
2061
+ guard.current = save;
2062
+ return () => {
2063
+ guard.current = null;
2064
+ };
2065
+ });
2066
+ const adopt = async () => {
2067
+ const s = signing;
2068
+ const canvas = document.createElement("canvas");
2069
+ const ctx = canvas.getContext("2d");
2070
+ if (s.tab === "typed") {
2071
+ const font = '64px "W100n Sign", "Segoe Script", "Bradley Hand", cursive';
2072
+ await document.fonts.load(font, s.name);
2073
+ ctx.font = font;
2074
+ canvas.width = Math.ceil(ctx.measureText(s.name).width) + 24;
2075
+ canvas.height = 96;
2076
+ ctx.font = font;
2077
+ ctx.fillStyle = "#1e3a8a";
2078
+ ctx.textBaseline = "middle";
2079
+ ctx.fillText(s.name, 12, 50);
2080
+ } else if (s.tab === "drawn") {
2081
+ const b = inked.current;
2082
+ const ratio = pad.current.width / pad.current.clientWidth;
2083
+ canvas.width = Math.ceil((b.w + 16) * ratio);
2084
+ canvas.height = Math.ceil((b.h + 16) * ratio);
2085
+ ctx.drawImage(pad.current, (b.x - 8) * ratio, (b.y - 8) * ratio, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
2086
+ } else {
2087
+ const img = new Image();
2088
+ img.src = s.image;
2089
+ await img.decode();
2090
+ const k = Math.min(1, 1200 / img.naturalWidth);
2091
+ canvas.width = Math.round(img.naturalWidth * k);
2092
+ canvas.height = Math.round(img.naturalHeight * k);
2093
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
2094
+ }
2095
+ setPending({ image: canvas.toDataURL("image/png"), ratio: canvas.width / canvas.height, method: s.tab });
2096
+ setSigning(null);
2097
+ setTool("place");
2098
+ };
2099
+ const draw = (e) => {
2100
+ const r = e.currentTarget.getBoundingClientRect();
2101
+ const p = { x: e.clientX - r.left, y: e.clientY - r.top };
2102
+ const ctx = e.currentTarget.getContext("2d");
2103
+ if (e.type === "pointerdown") {
2104
+ e.currentTarget.setPointerCapture(e.pointerId);
2105
+ ctx.lineWidth = 2.6;
2106
+ ctx.lineCap = "round";
2107
+ ctx.lineJoin = "round";
2108
+ ctx.strokeStyle = "#1e3a8a";
2109
+ ctx.beginPath();
2110
+ ctx.moveTo(p.x, p.y);
2111
+ } else if (e.buttons) {
2112
+ ctx.lineTo(p.x, p.y);
2113
+ ctx.stroke();
2114
+ } else return;
2115
+ const b = inked.current ?? { x: p.x, y: p.y, w: 0, h: 0 };
2116
+ const x = Math.min(b.x, p.x);
2117
+ const y = Math.min(b.y, p.y);
2118
+ inked.current = { x, y, w: Math.max(b.x + b.w, p.x) - x, h: Math.max(b.y + b.h, p.y) - y };
2119
+ if (!signing.drawn && e.type === "pointermove") setSigning({ ...signing, drawn: true });
2120
+ };
2121
+ const keys = (e) => {
2122
+ const key = e.key.toLowerCase();
2123
+ const mod = e.metaKey || e.ctrlKey;
2124
+ const typing = e.target.closest('input, textarea, select, [contenteditable="true"]');
2125
+ if (mod && key === "f") {
2126
+ e.preventDefault();
2127
+ if (query === null) setQuery("");
2128
+ else finder.current?.focus();
2129
+ } else if (key === "escape" && (signing || palette || more || pending || tool !== "select" || query !== null)) {
2130
+ e.stopPropagation();
2131
+ if (signing) setSigning(null);
2132
+ else if (palette) setPalette(false);
2133
+ else if (more) setMore(false);
2134
+ else if (tool !== "select") {
2135
+ setTool("select");
2136
+ setPending(null);
2137
+ } else setQuery(null);
2138
+ } else if (can.mark && !typing && mod && (key === "z" || key === "y")) {
2139
+ e.preventDefault();
2140
+ if (key === "y" || e.shiftKey) redo();
2141
+ else undo();
2142
+ }
2143
+ };
2144
+ const byPage = /* @__PURE__ */ new Map();
2145
+ for (const m of marks) byPage.set(m.page, [...byPage.get(m.page) ?? [], m]);
2146
+ const hitsBy = /* @__PURE__ */ new Map();
2147
+ for (const h of hits.list) hitsBy.set(h.page, [...hitsBy.get(h.page) ?? [], h]);
2148
+ const when = (iso) => new Intl.DateTimeFormat(language, { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));
2149
+ const tools = [
2150
+ ["select", MousePointer2, t.selectText, true, 2],
2151
+ ["highlight", Highlighter, t.highlight, true, 0],
2152
+ ["underline", Underline, t.underline, true, 1],
2153
+ ["strike", Strikethrough, t.strike, true, 1],
2154
+ ["note", StickyNote3, t.note, true, 0],
2155
+ ["ink", PenLine, t.draw, can.edit, 2],
2156
+ ["eraser", Eraser, t.eraser, true, 1]
2157
+ ];
2158
+ const inkish = tool === "ink";
2159
+ const family = inkish ? "ink" : tool === "underline" || tool === "strike" ? "line" : "highlight";
2160
+ const swatch = pen[family];
2161
+ const gap = /* @__PURE__ */ jsx4("span", { className: "w100n-pdf-gap" });
2162
+ const entries = [
2163
+ { key: "rail", at: 2, Icon: PanelLeft, label: t.pages, run: () => setRail(!rail), pressed: rail },
2164
+ { key: "page", at: 0, node: /* @__PURE__ */ jsxs4("label", { className: "w100n-pdf-pageno", children: [
2165
+ /* @__PURE__ */ jsx4(
2166
+ "input",
2167
+ {
2168
+ value: typed ?? String(page),
2169
+ inputMode: "numeric",
2170
+ "aria-label": t.page,
2171
+ onFocus: (e) => {
2172
+ setTyped(String(page));
2173
+ e.currentTarget.select();
2174
+ },
2175
+ onBlur: () => setTyped(null),
2176
+ onChange: (e) => setTyped(e.target.value.replace(/\D/g, "")),
2177
+ onKeyDown: (e) => {
2178
+ if (e.key === "Enter") {
2179
+ go(Number(typed) || 1);
2180
+ e.currentTarget.blur();
2181
+ }
2182
+ }
2183
+ }
2184
+ ),
2185
+ /* @__PURE__ */ jsxs4("span", { children: [
2186
+ "/ ",
2187
+ sizes.length || ""
2188
+ ] })
2189
+ ] }) },
2190
+ { key: "g1", at: 2, node: gap },
2191
+ { key: "out", at: 2, Icon: Minus, label: t.zoomOut, run: () => zoomBy(-1), stay: true },
2192
+ { key: "zoom", at: 2, node: /* @__PURE__ */ jsxs4("span", { className: "w100n-pdf-zoom", children: [
2193
+ percent,
2194
+ "%"
2195
+ ] }) },
2196
+ { key: "in", at: 2, Icon: Plus, label: t.zoomIn, run: () => zoomBy(1), stay: true },
2197
+ { key: "width", at: 1, Icon: MoveHorizontal, label: t.fitWidth, run: () => setFit("width"), pressed: fit === "width" },
2198
+ { key: "fit", at: 1, Icon: Maximize2, label: t.fitPage, run: () => setFit("page"), pressed: fit === "page" },
2199
+ { key: "g2", at: 2, node: gap },
2200
+ { key: "find", at: 0, Icon: Search, label: t.find, run: () => setQuery(query === null ? "" : null), pressed: query !== null },
2201
+ ...can.mark ? [
2202
+ { key: "g3", at: 2, node: gap },
2203
+ ...tools.filter(([, , , shown]) => shown).map(([key, Icon, label, , at2]) => ({ key, at: at2, Icon, label, run: () => pick(key), pressed: tool === key })),
2204
+ ...can.edit ? [{ key: "sign", at: 2, Icon: Sign, label: t.sign, run: () => setSigning({ tab: "typed", name: me, image: null, drawn: false }), pressed: tool === "place" || signing !== null }] : [],
2205
+ { key: "colour", at: 0, node: /* @__PURE__ */ jsxs4("span", { className: "w100n-pdf-palette", children: [
2206
+ /* @__PURE__ */ jsx4("button", { type: "button", "aria-expanded": palette, onClick: () => {
2207
+ setMore(false);
2208
+ setPalette(!palette);
2209
+ }, "aria-label": t.colour, title: t.colour, children: /* @__PURE__ */ jsx4("i", { style: { background: swatch } }) }),
2210
+ palette && /* @__PURE__ */ jsxs4("span", { className: "w100n-pdf-pop", role: "group", "aria-label": t.colour, children: [
2211
+ ["#facc15", "#4ade80", "#60a5fa", "#f472b6", "#f87171", "#2563eb", "#0f172a"].map((c, i) => /* @__PURE__ */ jsx4(
2212
+ "button",
2213
+ {
2214
+ type: "button",
2215
+ "aria-pressed": swatch === c,
2216
+ "aria-label": c,
2217
+ autoFocus: i === 0,
2218
+ onClick: () => setPen({ ...pen, [family]: c }),
2219
+ children: /* @__PURE__ */ jsx4("i", { style: { background: c } })
2220
+ },
2221
+ c
2222
+ )),
2223
+ inkish && [1, 2, 4].map((w) => /* @__PURE__ */ jsx4("button", { type: "button", "aria-pressed": pen.width === w, "aria-label": `${t.lineWidth} ${w}`, onClick: () => setPen({ ...pen, width: w }), children: /* @__PURE__ */ jsx4("b", { style: { height: w + 1 } }) }, w))
2224
+ ] })
2225
+ ] }) },
2226
+ { key: "undo", at: 2, Icon: Undo22, label: t.undo, run: undo, disabled: !history.past.length, stay: true },
2227
+ { key: "redo", at: 1, Icon: Redo22, label: t.redo, run: redo, disabled: !history.future.length, stay: true }
2228
+ ] : []
2229
+ ];
2230
+ const moved = entries.filter((e) => e.at && e.at <= level && e.Icon);
2231
+ const button = (e, listed, first = false) => {
2232
+ const Icon = e.Icon;
2233
+ return /* @__PURE__ */ jsxs4(
2234
+ "button",
2235
+ {
2236
+ type: "button",
2237
+ "aria-pressed": e.pressed,
2238
+ disabled: e.disabled,
2239
+ autoFocus: first,
2240
+ "aria-label": listed ? void 0 : e.label,
2241
+ title: listed ? void 0 : e.label,
2242
+ onClick: () => {
2243
+ e.run();
2244
+ if (listed && !e.stay) setMore(false);
2245
+ },
2246
+ children: [
2247
+ /* @__PURE__ */ jsx4(Icon, { "aria-hidden": true }),
2248
+ listed && e.label
2249
+ ]
2250
+ },
2251
+ e.key
2252
+ );
2253
+ };
2254
+ return /* @__PURE__ */ jsxs4("div", { ref: root, className: "w100n-pdf", onKeyDown: keys, children: [
2255
+ /* @__PURE__ */ jsxs4("div", { ref: bar, className: "w100n-pdf-bar", role: "toolbar", "aria-label": t.pdfTools, onKeyDown: rove, onPointerDown: (e) => {
2256
+ if (e.target.closest("button")) e.preventDefault();
2257
+ }, children: [
2258
+ entries.filter((e) => !e.at || e.at > level).map((e) => e.Icon ? button(e, false) : /* @__PURE__ */ jsx4(Fragment3, { children: e.node }, e.key)),
2259
+ moved.length > 0 && /* @__PURE__ */ jsxs4("span", { className: "w100n-pdf-palette", children: [
2260
+ /* @__PURE__ */ jsx4("button", { type: "button", "aria-expanded": more, "aria-label": t.more, title: t.more, onClick: () => {
2261
+ setPalette(false);
2262
+ setMore(!more);
2263
+ }, children: /* @__PURE__ */ jsx4(Ellipsis, { "aria-hidden": true }) }),
2264
+ more && /* @__PURE__ */ jsx4("span", { className: "w100n-pdf-pop w100n-pdf-more", role: "group", "aria-label": t.more, children: moved.map((e, i) => button(e, true, i === 0)) })
2265
+ ] }),
2266
+ can.mark && /* @__PURE__ */ jsxs4(Fragment4, { children: [
2267
+ /* @__PURE__ */ jsxs4("button", { type: "button", className: "w100n-docs-action", disabled: !dirty || saving === "busy", onClick: () => void save(), children: [
2268
+ /* @__PURE__ */ jsx4(Save3, { "aria-hidden": true }),
2269
+ saving === "busy" ? t.saving : !dirty && saving === "done" ? t.saved : t.save
2270
+ ] }),
2271
+ saving === "failed" && /* @__PURE__ */ jsx4("span", { className: "w100n-docs-muted", role: "alert", children: t.saveFailed })
2272
+ ] })
2273
+ ] }),
2274
+ query !== null && /* @__PURE__ */ jsxs4("div", { className: "w100n-pdf-find", role: "search", children: [
2275
+ /* @__PURE__ */ jsx4(Search, { "aria-hidden": true }),
2276
+ /* @__PURE__ */ jsx4(
2277
+ "input",
2278
+ {
2279
+ ref: finder,
2280
+ autoFocus: true,
2281
+ value: query,
2282
+ placeholder: t.find,
2283
+ "aria-label": t.find,
2284
+ onChange: (e) => setQuery(e.target.value),
2285
+ onKeyDown: (e) => {
2286
+ if (e.key === "Enter") step(e.shiftKey ? -1 : 1);
2287
+ }
2288
+ }
2289
+ ),
2290
+ /* @__PURE__ */ jsx4("span", { "aria-live": "polite", children: hits.list.length ? `${Math.min(at + 1, hits.list.length)} / ${hits.list.length}` : query.trim() ? hits.done ? t.noHits : t.searching : "" }),
2291
+ /* @__PURE__ */ jsx4("button", { type: "button", disabled: !hits.list.length, onClick: () => step(-1), "aria-label": t.previous, title: t.previous, children: /* @__PURE__ */ jsx4(ChevronUp, { "aria-hidden": true }) }),
2292
+ /* @__PURE__ */ jsx4("button", { type: "button", disabled: !hits.list.length, onClick: () => step(1), "aria-label": t.next, title: t.next, children: /* @__PURE__ */ jsx4(ChevronDown, { "aria-hidden": true }) }),
2293
+ /* @__PURE__ */ jsx4("button", { type: "button", onClick: () => setQuery(null), "aria-label": t.close, title: t.close, children: /* @__PURE__ */ jsx4(X3, { "aria-hidden": true }) })
2294
+ ] }),
2295
+ tool === "place" && /* @__PURE__ */ jsxs4("div", { className: "w100n-pdf-hint", children: [
2296
+ t.placeHint,
2297
+ /* @__PURE__ */ jsx4("button", { type: "button", onClick: () => {
2298
+ setTool("select");
2299
+ setPending(null);
2300
+ }, children: t.cancel })
2301
+ ] }),
2302
+ /* @__PURE__ */ jsxs4("div", { className: "w100n-pdf-body", children: [
2303
+ rail && /* @__PURE__ */ jsx4("nav", { ref: railBox, className: "w100n-pdf-rail", "aria-label": t.pages, onScroll: (e) => setRailTop(Math.round(e.currentTarget.scrollTop / 80) * 80), children: sizes.map((s, i) => /* @__PURE__ */ jsxs4("button", { type: "button", className: "w100n-pdf-thumb", "aria-current": page === i + 1, onClick: () => go(i + 1), "aria-label": `${t.page} ${i + 1}`, children: [
2304
+ /* @__PURE__ */ jsx4(PdfPage, { pdf, n: i + 1, size: s, scale: 96 / s.w, live: !!pdf && thumbLive(i) }),
2305
+ /* @__PURE__ */ jsx4("span", { children: i + 1 })
2306
+ ] }, i)) }),
2307
+ /* @__PURE__ */ jsxs4(
2308
+ "div",
2309
+ {
2310
+ ref: scroller,
2311
+ className: "w100n-pdf-scroll",
2312
+ tabIndex: 0,
2313
+ "data-tool": tool,
2314
+ onScroll: (e) => {
2315
+ const y = e.currentTarget.scrollTop;
2316
+ anchor.current = { scale, ...centre(y) };
2317
+ setTop(Math.round(y / 80) * 80);
2318
+ },
2319
+ onPointerDown: (e) => {
2320
+ const target = e.target;
2321
+ target.closest(".textLayer")?.classList.add("selecting");
2322
+ if (open && !target.closest(".w100n-pdf-note, .w100n-pdf-sign")) setOpen(null);
2323
+ },
2324
+ onPointerUp: () => {
2325
+ for (const el of scroller.current.querySelectorAll(".selecting")) el.classList.remove("selecting");
2326
+ if (tool === "highlight" || tool === "underline" || tool === "strike") markText(tool);
2327
+ },
2328
+ children: [
2329
+ error && /* @__PURE__ */ jsx4("p", { className: "w100n-docs-note", children: t.failed }),
2330
+ /* @__PURE__ */ jsx4("div", { className: "w100n-pdf-pages", children: sizes.map((s, i) => /* @__PURE__ */ jsx4(PdfPage, { id: `${prefix}-${i + 1}`, pdf, n: i + 1, size: s, scale, live: live(i), layers: {
2331
+ forms: can.edit,
2332
+ tool,
2333
+ marks: byPage.get(i + 1) ?? [],
2334
+ hits: hitsBy.get(i + 1) ?? [],
2335
+ active: hits.list[at],
2336
+ open,
2337
+ pen: { color: pen.ink, width: pen.width },
2338
+ widgets: widgets.current,
2339
+ t,
2340
+ when,
2341
+ act
2342
+ } }, i)) })
2343
+ ]
2344
+ }
2345
+ )
2346
+ ] }),
2347
+ signing && /* @__PURE__ */ jsx4("div", { className: "w100n-pdf-back", onPointerDown: (e) => {
2348
+ if (e.target === e.currentTarget) setSigning(null);
2349
+ }, children: /* @__PURE__ */ jsxs4("div", { className: "w100n-pdf-dialog", role: "dialog", "aria-modal": true, "aria-label": t.signTitle, children: [
2350
+ /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-row", children: [
2351
+ /* @__PURE__ */ jsx4("b", { className: "w100n-pdf-dialog-title", children: t.signTitle }),
2352
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "w100n-docs-close", onClick: () => setSigning(null), "aria-label": t.close, title: t.close, children: /* @__PURE__ */ jsx4(X3, { "aria-hidden": true }) })
2353
+ ] }),
2354
+ /* @__PURE__ */ jsx4("div", { className: "w100n-docs-tabs", role: "tablist", "aria-label": t.signTitle, onKeyDown: rove, children: [["typed", Type, t.typed], ["drawn", PenLine, t.drawn], ["image", ImageUp, t.image]].map(([key, Icon, label]) => /* @__PURE__ */ jsxs4("button", { type: "button", role: "tab", "aria-selected": signing.tab === key, tabIndex: signing.tab === key ? 0 : -1, onClick: () => setSigning({ ...signing, tab: key }), children: [
2355
+ /* @__PURE__ */ jsx4(Icon, { "aria-hidden": true }),
2356
+ label
2357
+ ] }, key)) }),
2358
+ signing.tab === "typed" && /* @__PURE__ */ jsxs4(Fragment4, { children: [
2359
+ /* @__PURE__ */ jsx4("input", { className: "w100n-pdf-name", autoFocus: true, value: signing.name, onChange: (e) => setSigning({ ...signing, name: e.target.value }), "aria-label": t.yourName, placeholder: t.yourName }),
2360
+ /* @__PURE__ */ jsx4("p", { className: "w100n-pdf-script", style: { fontSize: Math.min(52, Math.floor(680 / Math.max(1, (signing.name || t.yourName).length))) }, children: signing.name || t.yourName })
2361
+ ] }),
2362
+ signing.tab === "drawn" && /* @__PURE__ */ jsxs4(Fragment4, { children: [
2363
+ /* @__PURE__ */ jsx4("canvas", { ref: pad, className: "w100n-pdf-pad", "aria-label": t.drawHere, onPointerDown: draw, onPointerMove: draw }),
2364
+ /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-row", children: [
2365
+ /* @__PURE__ */ jsx4("small", { className: "w100n-docs-muted", children: t.drawHere }),
2366
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "w100n-pdf-link", onClick: () => {
2367
+ const canvas = pad.current;
2368
+ canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
2369
+ inked.current = null;
2370
+ setSigning({ ...signing, drawn: false });
2371
+ }, children: t.clear })
2372
+ ] })
2373
+ ] }),
2374
+ signing.tab === "image" && /* @__PURE__ */ jsxs4("label", { className: "w100n-pdf-upload", children: [
2375
+ signing.image ? /* @__PURE__ */ jsx4("img", { src: signing.image, alt: "" }) : /* @__PURE__ */ jsxs4(Fragment4, { children: [
2376
+ /* @__PURE__ */ jsx4(ImageUp, { "aria-hidden": true }),
2377
+ t.pickImage
2378
+ ] }),
2379
+ /* @__PURE__ */ jsx4("input", { type: "file", accept: "image/png,image/jpeg", onChange: (e) => {
2380
+ const file = e.target.files?.[0];
2381
+ if (!file) return;
2382
+ const reader = new FileReader();
2383
+ reader.onload = () => setSigning((current) => current && { ...current, image: String(reader.result) });
2384
+ reader.readAsDataURL(file);
2385
+ } })
2386
+ ] }),
2387
+ /* @__PURE__ */ jsxs4("div", { className: "w100n-pdf-dialog-end", children: [
2388
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "w100n-pdf-link", onClick: () => setSigning(null), children: t.cancel }),
2389
+ /* @__PURE__ */ jsxs4(
2390
+ "button",
2391
+ {
2392
+ type: "button",
2393
+ className: "w100n-docs-action",
2394
+ onClick: () => void adopt(),
2395
+ disabled: signing.tab === "typed" ? !signing.name.trim() : signing.tab === "drawn" ? !signing.drawn : !signing.image,
2396
+ children: [
2397
+ /* @__PURE__ */ jsx4(Sign, { "aria-hidden": true }),
2398
+ t.useSign
2399
+ ]
2400
+ }
2401
+ )
2402
+ ] })
2403
+ ] }) })
2404
+ ] });
2405
+ }
2406
+
2407
+ // src/DocumentOverlay.tsx
2408
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2409
+ var TextEditor = lazy(() => import("./TextEditor-4YETNX4T.js"));
2410
+ var SheetView = lazy(() => import("./SheetView-TXHS5WJC.js"));
2411
+ var icons = { docx: FileText, markdown: FileType, pdf: Files, sheet: FileSpreadsheet, slides: Presentation };
2412
+ var read = { view: true, comment: false, edit: false };
2413
+ var quiet = () => {
2414
+ };
2415
+ function DocumentOverlay({ source, name, mimeType, info, download, actions, onClose, onOpen, language = "en", theme = "light", worker, host, documentId }) {
2416
+ const t = texts[language];
2417
+ const kind = kindOf(name, mimeType) ?? "pdf";
2418
+ const Kind = icons[kind];
2419
+ const [saved, setSaved] = useState5(null);
2420
+ const loaded = useLoad(async (signal) => {
2421
+ if (source instanceof ArrayBuffer) return source;
2422
+ if (source instanceof Blob) return source.arrayBuffer();
2423
+ const response = await fetch(source, { signal });
2424
+ if (!response.ok) throw new Error(`${response.status}`);
2425
+ return response.arrayBuffer();
2426
+ }, [source]);
2427
+ const doc = useLoad((signal) => host && documentId ? host.document(documentId, { signal }) : Promise.resolve(null), [host, documentId]);
2428
+ const bytes = saved ?? loaded.data;
2429
+ const guard = useRef4(null);
2430
+ const box = useRef4(null);
2431
+ const { ask, dialog } = useUnsaved(guard, t);
2432
+ const close = () => ask(onClose);
2433
+ useRestore(true);
2434
+ useEffect4(() => {
2435
+ box.current.focus();
2436
+ }, []);
2437
+ useEffect4(() => {
2438
+ const escape2 = (e) => {
2439
+ if (e.key !== "Escape") return;
2440
+ e.stopPropagation();
2441
+ close();
2442
+ };
2443
+ const overflow = document.body.style.overflow;
2444
+ document.body.style.overflow = "hidden";
2445
+ window.addEventListener("keydown", escape2, true);
2446
+ return () => {
2447
+ window.removeEventListener("keydown", escape2, true);
2448
+ document.body.style.overflow = overflow;
2449
+ };
2450
+ });
2451
+ const save = () => {
2452
+ const url = URL.createObjectURL(new Blob([bytes], { type: "application/pdf" }));
2453
+ const link = document.createElement("a");
2454
+ link.href = url;
2455
+ link.download = name;
2456
+ link.click();
2457
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
2458
+ };
2459
+ const sheet = readable(name, mimeType) !== "sheet" ? null : /\.csv$/i.test(name) || mimeType === "text/csv" ? "csv" : "xlsx";
2460
+ const wait = /* @__PURE__ */ jsx5("div", { className: "w100n-docs-empty", children: t.loading });
2461
+ let stage = null;
2462
+ if (bytes && !doc.loading) {
2463
+ if (kind === "pdf") stage = /* @__PURE__ */ jsx5(
2464
+ PdfView,
2465
+ {
2466
+ bytes,
2467
+ t,
2468
+ language,
2469
+ worker: worker ?? host?.pdfWorker,
2470
+ rights: doc.data?.rights,
2471
+ host: doc.data ? host : void 0,
2472
+ documentId,
2473
+ onSaved: setSaved,
2474
+ guard
2475
+ }
2476
+ );
2477
+ else if (kind === "docx" || kind === "markdown") stage = /* @__PURE__ */ jsx5(Suspense, { fallback: wait, children: /* @__PURE__ */ jsx5(TextEditor, { kind, bytes, t, language, rights: read, documentId: documentId ?? name, onOutline: quiet, slot: null }) });
2478
+ else if (kind === "slides") stage = /* @__PURE__ */ jsx5(SlidesView, { html: new TextDecoder().decode(bytes), name, rights: read, language, theme, onOutline: quiet });
2479
+ else if (sheet) stage = /* @__PURE__ */ jsx5(Suspense, { fallback: wait, children: /* @__PURE__ */ jsx5(SheetView, { bytes, name, kind: sheet, rights: read, language, theme, documentId: documentId ?? name }) });
2480
+ else stage = /* @__PURE__ */ jsx5("div", { className: "w100n-docs-empty", children: t.unsupported });
2481
+ }
2482
+ return createPortal2(/* @__PURE__ */ jsxs5("div", { ref: box, className: "w100n-docs w100n-docs-overlay", "data-theme": theme, role: "dialog", "aria-modal": true, "aria-label": name, tabIndex: -1, children: [
2483
+ /* @__PURE__ */ jsxs5("header", { className: "w100n-docs-bar", children: [
2484
+ /* @__PURE__ */ jsx5("span", { className: "w100n-docs-tile", "data-kind": kind, children: /* @__PURE__ */ jsx5(Kind, { "aria-hidden": true }) }),
2485
+ /* @__PURE__ */ jsxs5("div", { className: "w100n-docs-heading", children: [
2486
+ /* @__PURE__ */ jsx5("strong", { className: "w100n-docs-title", children: name }),
2487
+ info && /* @__PURE__ */ jsx5("span", { className: "w100n-docs-crumbs", children: info })
2488
+ ] }),
2489
+ /* @__PURE__ */ jsxs5("div", { className: "w100n-docs-end", children: [
2490
+ actions,
2491
+ download && !saved || !bytes ? /* @__PURE__ */ jsxs5("a", { className: "w100n-docs-action", href: download ?? (typeof source === "string" ? source : void 0), download: name, "aria-label": t.download, children: [
2492
+ /* @__PURE__ */ jsx5(Download, { "aria-hidden": true }),
2493
+ /* @__PURE__ */ jsx5("span", { children: t.download })
2494
+ ] }) : /* @__PURE__ */ jsxs5("button", { type: "button", className: "w100n-docs-action", onClick: save, "aria-label": t.download, children: [
2495
+ /* @__PURE__ */ jsx5(Download, { "aria-hidden": true }),
2496
+ /* @__PURE__ */ jsx5("span", { children: t.download })
2497
+ ] }),
2498
+ onOpen && /* @__PURE__ */ jsxs5("button", { type: "button", className: "w100n-docs-action", onClick: () => ask(onOpen), "aria-label": t.openInDocuments, children: [
2499
+ /* @__PURE__ */ jsx5(ExternalLink, { "aria-hidden": true }),
2500
+ /* @__PURE__ */ jsx5("span", { children: t.openInDocuments })
2501
+ ] }),
2502
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "w100n-docs-close", onClick: close, "aria-label": t.close, title: t.close, children: /* @__PURE__ */ jsx5(X4, { "aria-hidden": true }) })
2503
+ ] })
2504
+ ] }),
2505
+ /* @__PURE__ */ jsx5("div", { className: "w100n-docs-body", children: /* @__PURE__ */ jsx5("main", { className: "w100n-docs-stage", children: loaded.error ? /* @__PURE__ */ jsxs5("div", { className: "w100n-docs-empty", children: [
2506
+ t.failed,
2507
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "w100n-pdf-link", onClick: loaded.retry, children: t.retry })
2508
+ ] }) : stage ?? wait }) }),
2509
+ dialog
2510
+ ] }), document.body);
2511
+ }
7
2512
 
8
2513
  // src/DocumentsWorkspace.tsx
9
- import { useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState5 } from "react";
2514
+ import { lazy as lazy2, Suspense as Suspense2, useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef6, useState as useState8 } from "react";
2515
+ import { flushSync } from "react-dom";
10
2516
  import {
11
2517
  Check,
12
- ChevronDown,
13
- ChevronRight,
14
- FileSpreadsheet,
15
- FileText,
16
- FileType,
17
- Files,
2518
+ ChevronDown as ChevronDown2,
2519
+ ChevronRight as ChevronRight2,
2520
+ FileDown,
2521
+ FileSpreadsheet as FileSpreadsheet2,
2522
+ FileText as FileText2,
2523
+ FileType as FileType2,
2524
+ Files as Files2,
18
2525
  History,
19
2526
  Info,
20
2527
  LayoutGrid,
21
2528
  ListTree,
22
- Maximize2,
23
- Minus,
2529
+ MessagesSquare,
2530
+ PanelLeftClose,
24
2531
  PanelLeftOpen,
2532
+ PanelRightClose,
25
2533
  PanelRightOpen,
26
- Plus,
27
- Presentation,
28
- RotateCcw,
29
- Search,
30
- X
2534
+ Presentation as Presentation2,
2535
+ RotateCcw as RotateCcw2,
2536
+ Search as Search2,
2537
+ Share2,
2538
+ Signature,
2539
+ X as X5
31
2540
  } from "lucide-react";
32
2541
 
33
- // src/text.ts
34
- var en = {
35
- documents: "Documents",
36
- loose: "Without a folder",
37
- search: "Search documents",
38
- all: "All",
39
- empty: "No documents here yet",
40
- nothing: "Nothing matches the search",
41
- space: "Project",
42
- pick: "Choose a document on the left",
43
- closeList: "Hide the list",
44
- openList: "Show the list",
45
- outline: "Outline",
46
- versions: "Versions",
47
- details: "Details",
48
- noOutline: "This document has no headings",
49
- current: "Current",
50
- size: "Size",
51
- updated: "Updated",
52
- by: "By",
53
- kind: "Format",
54
- rights: "You can",
55
- view: "View",
56
- comment: "Comment",
57
- edit: "Edit",
58
- failed: "Could not load",
59
- retry: "Try again",
60
- loading: "Loading",
61
- sheet: "Sheet",
62
- unsupported: "This file cannot be shown yet",
63
- restore: "Restore this version",
64
- restoreFailed: "Could not restore",
65
- allProjects: "All projects",
66
- closePanel: "Hide the panel",
67
- openPanel: "Show the panel",
68
- zoomIn: "Zoom in",
69
- zoomOut: "Zoom out",
70
- fit: "Actual size",
71
- kinds: { docx: "DOCX", markdown: "Markdown", pdf: "PDF", sheet: "Sheets", slides: "Slides" }
72
- };
73
- var pl = {
74
- documents: "Dokumenty",
75
- loose: "Bez folderu",
76
- search: "Szukaj dokument\xF3w",
77
- all: "Wszystkie",
78
- empty: "Nie ma tu jeszcze dokument\xF3w",
79
- nothing: "Nic nie pasuje do wyszukiwania",
80
- space: "Projekt",
81
- pick: "Wybierz dokument po lewej",
82
- closeList: "Ukryj list\u0119",
83
- openList: "Poka\u017C list\u0119",
84
- outline: "Spis tre\u015Bci",
85
- versions: "Wersje",
86
- details: "Szczeg\xF3\u0142y",
87
- noOutline: "Ten dokument nie ma nag\u0142\xF3wk\xF3w",
88
- current: "Bie\u017C\u0105ca",
89
- size: "Rozmiar",
90
- updated: "Zmieniono",
91
- by: "Autor",
92
- kind: "Format",
93
- rights: "Mo\u017Cesz",
94
- view: "Ogl\u0105da\u0107",
95
- comment: "Komentowa\u0107",
96
- edit: "Edytowa\u0107",
97
- failed: "Nie uda\u0142o si\u0119 wczyta\u0107",
98
- retry: "Spr\xF3buj ponownie",
99
- loading: "Wczytywanie",
100
- sheet: "Arkusz",
101
- unsupported: "Tego pliku nie da si\u0119 jeszcze pokaza\u0107",
102
- restore: "Przywr\xF3\u0107 t\u0119 wersj\u0119",
103
- restoreFailed: "Nie uda\u0142o si\u0119 przywr\xF3ci\u0107",
104
- allProjects: "Wszystkie projekty",
105
- closePanel: "Ukryj panel",
106
- openPanel: "Poka\u017C panel",
107
- zoomIn: "Powi\u0119ksz",
108
- zoomOut: "Pomniejsz",
109
- fit: "Rzeczywisty rozmiar",
110
- kinds: { docx: "DOCX", markdown: "Markdown", pdf: "PDF", sheet: "Arkusze", slides: "Slajdy" }
111
- };
112
- var uk = {
113
- documents: "\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0438",
114
- loose: "\u0411\u0435\u0437 \u0442\u0435\u043A\u0438",
115
- search: "\u041F\u043E\u0448\u0443\u043A \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0456\u0432",
116
- all: "\u0423\u0441\u0456",
117
- empty: "\u0422\u0443\u0442 \u0449\u0435 \u043D\u0435\u043C\u0430\u0454 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0456\u0432",
118
- nothing: "\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E",
119
- space: "\u041F\u0440\u043E\u0454\u043A\u0442",
120
- pick: "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044C \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442 \u043B\u0456\u0432\u043E\u0440\u0443\u0447",
121
- closeList: "\u0421\u0445\u043E\u0432\u0430\u0442\u0438 \u0441\u043F\u0438\u0441\u043E\u043A",
122
- openList: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u0438 \u0441\u043F\u0438\u0441\u043E\u043A",
123
- outline: "\u0417\u043C\u0456\u0441\u0442",
124
- versions: "\u0412\u0435\u0440\u0441\u0456\u0457",
125
- details: "\u0414\u0435\u0442\u0430\u043B\u0456",
126
- noOutline: "\u0423 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0456 \u043D\u0435\u043C\u0430\u0454 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0456\u0432",
127
- current: "\u041F\u043E\u0442\u043E\u0447\u043D\u0430",
128
- size: "\u0420\u043E\u0437\u043C\u0456\u0440",
129
- updated: "\u0417\u043C\u0456\u043D\u0435\u043D\u043E",
130
- by: "\u0410\u0432\u0442\u043E\u0440",
131
- kind: "\u0424\u043E\u0440\u043C\u0430\u0442",
132
- rights: "\u0412\u0438 \u043C\u043E\u0436\u0435\u0442\u0435",
133
- view: "\u0414\u0438\u0432\u0438\u0442\u0438\u0441\u044F",
134
- comment: "\u041A\u043E\u043C\u0435\u043D\u0442\u0443\u0432\u0430\u0442\u0438",
135
- edit: "\u041F\u0440\u0430\u0432\u0438\u0442\u0438",
136
- failed: "\u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438",
137
- retry: "\u0421\u043F\u0440\u043E\u0431\u0443\u0432\u0430\u0442\u0438 \u0449\u0435",
138
- loading: "\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F",
139
- sheet: "\u0410\u0440\u043A\u0443\u0448",
140
- unsupported: "\u0426\u0435\u0439 \u0444\u0430\u0439\u043B \u043F\u043E\u043A\u0438 \u043D\u0435 \u043F\u043E\u043A\u0430\u0437\u0443\u0454\u0442\u044C\u0441\u044F",
141
- restore: "\u0412\u0456\u0434\u043D\u043E\u0432\u0438\u0442\u0438 \u0446\u044E \u0432\u0435\u0440\u0441\u0456\u044E",
142
- restoreFailed: "\u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0432\u0456\u0434\u043D\u043E\u0432\u0438\u0442\u0438",
143
- allProjects: "\u0423\u0441\u0456 \u043F\u0440\u043E\u0454\u043A\u0442\u0438",
144
- closePanel: "\u0421\u0445\u043E\u0432\u0430\u0442\u0438 \u043F\u0430\u043D\u0435\u043B\u044C",
145
- openPanel: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u0438 \u043F\u0430\u043D\u0435\u043B\u044C",
146
- zoomIn: "\u0417\u0431\u0456\u043B\u044C\u0448\u0438\u0442\u0438",
147
- zoomOut: "\u0417\u043C\u0435\u043D\u0448\u0438\u0442\u0438",
148
- fit: "\u0421\u043F\u0440\u0430\u0432\u0436\u043D\u0456\u0439 \u0440\u043E\u0437\u043C\u0456\u0440",
149
- kinds: { docx: "DOCX", markdown: "Markdown", pdf: "PDF", sheet: "\u0422\u0430\u0431\u043B\u0438\u0446\u0456", slides: "\u0421\u043B\u0430\u0439\u0434\u0438" }
2542
+ // src/Sharing.tsx
2543
+ import { useState as useState6 } from "react";
2544
+ import { Ban, CalendarDays, Clock, Copy as Copy2, Download as Download2, Eye, Link, Lock, MessageSquarePlus, Plus as Plus2, Users } from "lucide-react";
2545
+ import { Fragment as Fragment5, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2546
+ var blank2 = { id: null, comment: false, download: false, askName: true, works: "30", day: "", password: false, secret: "", had: false };
2547
+ var local = (iso) => {
2548
+ const at = new Date(iso);
2549
+ return `${at.getFullYear()}-${String(at.getMonth() + 1).padStart(2, "0")}-${String(at.getDate()).padStart(2, "0")}`;
150
2550
  };
151
- var texts = { en, pl, uk };
2551
+ function Sharing({ host, doc, t, language, team }) {
2552
+ const links = useLoad((signal) => host.links(doc.id, { signal }), [host, doc.id]);
2553
+ const [form, setForm] = useState6(null);
2554
+ const [state, setState] = useState6("idle");
2555
+ const [copied, setCopied] = useState6(null);
2556
+ const text = doc.kind === "docx" || doc.kind === "markdown";
2557
+ const rules = new Intl.PluralRules(language);
2558
+ const count = (n, forms) => `${n} ${forms[rules.select(n)] ?? forms.other}`;
2559
+ const day = (iso) => new Intl.DateTimeFormat(language, { day: "numeric", month: "short" }).format(new Date(iso));
2560
+ const ago = (iso) => {
2561
+ const minutes = Math.round((Date.parse(iso) - Date.now()) / 6e4);
2562
+ const rtf = new Intl.RelativeTimeFormat(language, { numeric: "auto", style: "short" });
2563
+ if (Math.abs(minutes) < 60) return rtf.format(minutes, "minute");
2564
+ if (Math.abs(minutes) < 1440) return rtf.format(Math.round(minutes / 60), "hour");
2565
+ return rtf.format(Math.round(minutes / 1440), "day");
2566
+ };
2567
+ const edit = (l) => setForm({
2568
+ id: l.id,
2569
+ comment: l.comment,
2570
+ download: l.download,
2571
+ askName: l.askName,
2572
+ works: l.endsAt ? "day" : "off",
2573
+ day: l.endsAt ? local(l.endsAt) : "",
2574
+ password: l.password,
2575
+ secret: "",
2576
+ had: l.password
2577
+ });
2578
+ const ends = (f) => {
2579
+ if (f.works === "off") return null;
2580
+ if (f.works === "day") return (/* @__PURE__ */ new Date(`${f.day}T23:59:59`)).toISOString();
2581
+ return new Date(Date.now() + Number(f.works) * 864e5).toISOString();
2582
+ };
2583
+ const ready = form && (form.works !== "day" || !!form.day && /* @__PURE__ */ new Date(`${form.day}T23:59:59`) > /* @__PURE__ */ new Date()) && (!form.password || form.had || form.secret.length >= 4);
2584
+ const submit = async () => {
2585
+ if (!form || !ready) return;
2586
+ setState("busy");
2587
+ const password = form.password ? form.secret || (form.had ? null : "") : form.had ? "" : null;
2588
+ const entry = { comment: text && form.comment, download: form.download, askName: form.askName, endsAt: ends(form), password };
2589
+ try {
2590
+ await (form.id ? host.relink(form.id, entry) : host.share(doc.id, entry));
2591
+ setForm(null);
2592
+ setState("idle");
2593
+ links.retry();
2594
+ } catch {
2595
+ setState("failed");
2596
+ }
2597
+ };
2598
+ const off = async (l) => {
2599
+ setState("busy");
2600
+ try {
2601
+ await host.unshare(l.id);
2602
+ setState("idle");
2603
+ links.retry();
2604
+ } catch {
2605
+ setState("failed");
2606
+ }
2607
+ };
2608
+ const copy = async (l) => {
2609
+ try {
2610
+ await navigator.clipboard.writeText(l.url);
2611
+ setCopied(l.id);
2612
+ setTimeout(() => setCopied((now) => now === l.id ? null : now), 2e3);
2613
+ } catch {
2614
+ setCopied(null);
2615
+ }
2616
+ };
2617
+ if (form) return /* @__PURE__ */ jsxs6("div", { className: "w100n-share", children: [
2618
+ /* @__PURE__ */ jsxs6("p", { className: "w100n-docs-caption", children: [
2619
+ /* @__PURE__ */ jsx6(Link, { "aria-hidden": true }),
2620
+ form.id ? t.changeLink : t.newLink
2621
+ ] }),
2622
+ text && /* @__PURE__ */ jsxs6("div", { className: "w100n-share-field", children: [
2623
+ /* @__PURE__ */ jsx6("span", { children: t.guestsCan }),
2624
+ /* @__PURE__ */ jsxs6("div", { className: "w100n-docs-chips", children: [
2625
+ /* @__PURE__ */ jsxs6("button", { type: "button", "aria-pressed": !form.comment, onClick: () => setForm({ ...form, comment: false }), children: [
2626
+ /* @__PURE__ */ jsx6(Eye, { "aria-hidden": true }),
2627
+ t.canRead
2628
+ ] }),
2629
+ /* @__PURE__ */ jsxs6("button", { type: "button", "aria-pressed": form.comment, onClick: () => setForm({ ...form, comment: true }), children: [
2630
+ /* @__PURE__ */ jsx6(MessageSquarePlus, { "aria-hidden": true }),
2631
+ t.canComment
2632
+ ] })
2633
+ ] })
2634
+ ] }),
2635
+ /* @__PURE__ */ jsxs6("div", { className: "w100n-share-field", children: [
2636
+ /* @__PURE__ */ jsx6("span", { children: t.linkWorks }),
2637
+ /* @__PURE__ */ jsx6("div", { className: "w100n-docs-chips", children: [["7", t.days7], ["30", t.days30], ["off", t.untilTurnOff], ["day", t.pickDay]].map(([key, label]) => /* @__PURE__ */ jsxs6("button", { type: "button", "aria-pressed": form.works === key, onClick: () => setForm({ ...form, works: key }), children: [
2638
+ key === "day" && /* @__PURE__ */ jsx6(CalendarDays, { "aria-hidden": true }),
2639
+ label
2640
+ ] }, key)) }),
2641
+ form.works === "day" && /* @__PURE__ */ jsx6(
2642
+ "input",
2643
+ {
2644
+ type: "date",
2645
+ className: "w100n-share-input",
2646
+ value: form.day,
2647
+ min: local((/* @__PURE__ */ new Date()).toISOString()),
2648
+ "aria-label": t.pickDay,
2649
+ onChange: (e) => setForm({ ...form, day: e.target.value })
2650
+ }
2651
+ )
2652
+ ] }),
2653
+ /* @__PURE__ */ jsxs6("label", { className: "w100n-share-toggle", children: [
2654
+ /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: form.password, onChange: (e) => setForm({ ...form, password: e.target.checked }) }),
2655
+ /* @__PURE__ */ jsxs6("span", { children: [
2656
+ /* @__PURE__ */ jsx6("b", { children: t.passwordOn }),
2657
+ /* @__PURE__ */ jsx6("small", { children: t.passwordHint })
2658
+ ] })
2659
+ ] }),
2660
+ form.password && /* @__PURE__ */ jsx6(
2661
+ "input",
2662
+ {
2663
+ type: "password",
2664
+ className: "w100n-share-input",
2665
+ autoComplete: "new-password",
2666
+ value: form.secret,
2667
+ "aria-label": t.passwordOn,
2668
+ placeholder: form.had ? t.keepPassword : t.passwordPlaceholder,
2669
+ onChange: (e) => setForm({ ...form, secret: e.target.value })
2670
+ }
2671
+ ),
2672
+ /* @__PURE__ */ jsxs6("label", { className: "w100n-share-toggle", children: [
2673
+ /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: form.download, onChange: (e) => setForm({ ...form, download: e.target.checked }) }),
2674
+ /* @__PURE__ */ jsxs6("span", { children: [
2675
+ /* @__PURE__ */ jsx6("b", { children: t.downloadFile }),
2676
+ /* @__PURE__ */ jsx6("small", { children: t.downloadHint })
2677
+ ] })
2678
+ ] }),
2679
+ /* @__PURE__ */ jsxs6("label", { className: "w100n-share-toggle", children: [
2680
+ /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: form.askName, onChange: (e) => setForm({ ...form, askName: e.target.checked }) }),
2681
+ /* @__PURE__ */ jsxs6("span", { children: [
2682
+ /* @__PURE__ */ jsx6("b", { children: t.askName }),
2683
+ /* @__PURE__ */ jsx6("small", { children: t.askNameHint })
2684
+ ] })
2685
+ ] }),
2686
+ /* @__PURE__ */ jsx6("p", { className: "w100n-docs-muted", children: t.lastSaved }),
2687
+ state === "failed" && /* @__PURE__ */ jsx6("p", { className: "w100n-docs-muted", role: "alert", children: t.shareFailed }),
2688
+ /* @__PURE__ */ jsxs6("span", { className: "w100n-pdf-dialog-end", children: [
2689
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "w100n-pdf-link", onClick: () => {
2690
+ setForm(null);
2691
+ setState("idle");
2692
+ }, children: t.cancel }),
2693
+ /* @__PURE__ */ jsxs6("button", { type: "button", className: "w100n-docs-action", disabled: !ready || state === "busy", onClick: () => void submit(), children: [
2694
+ /* @__PURE__ */ jsx6(Link, { "aria-hidden": true }),
2695
+ form.id ? t.saveLink : t.makeLink
2696
+ ] })
2697
+ ] })
2698
+ ] });
2699
+ const on = links.data ?? [];
2700
+ return /* @__PURE__ */ jsxs6("div", { className: "w100n-share", children: [
2701
+ /* @__PURE__ */ jsxs6("p", { className: "w100n-docs-caption", children: [
2702
+ /* @__PURE__ */ jsx6(Link, { "aria-hidden": true }),
2703
+ t.guestLinks,
2704
+ on.length > 0 && /* @__PURE__ */ jsx6("span", { children: on.filter((l) => !l.ended).length })
2705
+ ] }),
2706
+ links.error && /* @__PURE__ */ jsxs6("div", { className: "w100n-docs-note", children: [
2707
+ t.failed,
2708
+ /* @__PURE__ */ jsx6("button", { type: "button", onClick: links.retry, children: t.retry })
2709
+ ] }),
2710
+ links.data && !on.length && /* @__PURE__ */ jsx6("p", { className: "w100n-docs-muted", children: t.noLinks }),
2711
+ state === "failed" && /* @__PURE__ */ jsx6("p", { className: "w100n-docs-muted", role: "alert", children: t.shareFailed }),
2712
+ on.map((l) => /* @__PURE__ */ jsxs6("article", { className: "w100n-share-link", "data-ended": l.ended, children: [
2713
+ /* @__PURE__ */ jsxs6("header", { children: [
2714
+ /* @__PURE__ */ jsx6("span", { className: "w100n-share-tile", "data-tone": l.comment ? "amber" : "blue", children: l.comment ? /* @__PURE__ */ jsx6(MessageSquarePlus, { "aria-hidden": true }) : /* @__PURE__ */ jsx6(Eye, { "aria-hidden": true }) }),
2715
+ /* @__PURE__ */ jsxs6("span", { children: [
2716
+ /* @__PURE__ */ jsx6("b", { children: l.comment ? t.commentLink : t.viewLink }),
2717
+ /* @__PURE__ */ jsxs6("small", { children: [
2718
+ t.madeBy.replace("{name}", l.createdBy.name),
2719
+ " \xB7 ",
2720
+ day(l.createdAt)
2721
+ ] })
2722
+ ] })
2723
+ ] }),
2724
+ /* @__PURE__ */ jsxs6("div", { className: "w100n-share-chips", children: [
2725
+ /* @__PURE__ */ jsxs6("span", { children: [
2726
+ /* @__PURE__ */ jsx6(Clock, { "aria-hidden": true }),
2727
+ l.ended ? t.linkEnded : l.endsAt ? t.endsOn.replace("{date}", day(l.endsAt)) : t.untilOff
2728
+ ] }),
2729
+ l.download && /* @__PURE__ */ jsxs6("span", { children: [
2730
+ /* @__PURE__ */ jsx6(Download2, { "aria-hidden": true }),
2731
+ t.downloadOn
2732
+ ] }),
2733
+ l.password && /* @__PURE__ */ jsxs6("span", { children: [
2734
+ /* @__PURE__ */ jsx6(Lock, { "aria-hidden": true }),
2735
+ t.passwordOn
2736
+ ] }),
2737
+ /* @__PURE__ */ jsxs6("span", { children: [
2738
+ /* @__PURE__ */ jsx6(Eye, { "aria-hidden": true }),
2739
+ count(l.guests.reduce((n, g) => n + g.opens, 0), t.openForms)
2740
+ ] })
2741
+ ] }),
2742
+ /* @__PURE__ */ jsxs6("div", { className: "w100n-share-url", children: [
2743
+ /* @__PURE__ */ jsx6("span", { children: l.url.replace(/^https?:\/\//, "") }),
2744
+ /* @__PURE__ */ jsxs6("button", { type: "button", className: "w100n-docs-action", "data-tone": "blue", onClick: () => void copy(l), children: [
2745
+ /* @__PURE__ */ jsx6(Copy2, { "aria-hidden": true }),
2746
+ copied === l.id ? t.copied : t.copy
2747
+ ] })
2748
+ ] }),
2749
+ l.guests.length > 0 && /* @__PURE__ */ jsx6("div", { className: "w100n-share-who", children: l.guests.map((g, i) => /* @__PURE__ */ jsxs6("div", { children: [
2750
+ /* @__PURE__ */ jsx6("span", { className: "w100n-text-face", "aria-hidden": true, children: (g.name || "?").slice(0, 1).toUpperCase() }),
2751
+ /* @__PURE__ */ jsx6("b", { children: g.name || t.unnamed }),
2752
+ /* @__PURE__ */ jsxs6("small", { children: [
2753
+ g.comments ? count(g.comments, t.commentForms) : count(g.opens, t.openForms),
2754
+ " \xB7 ",
2755
+ ago(g.lastAt)
2756
+ ] })
2757
+ ] }, i)) }),
2758
+ /* @__PURE__ */ jsxs6("div", { className: "w100n-share-tools", children: [
2759
+ /* @__PURE__ */ jsxs6("button", { type: "button", className: "w100n-docs-action", "data-tone": "slate", disabled: state === "busy", onClick: () => edit(l), children: [
2760
+ /* @__PURE__ */ jsx6(Clock, { "aria-hidden": true }),
2761
+ t.changeLink
2762
+ ] }),
2763
+ /* @__PURE__ */ jsxs6("button", { type: "button", className: "w100n-docs-action", "data-tone": "red", disabled: state === "busy", onClick: () => void off(l), children: [
2764
+ /* @__PURE__ */ jsx6(Ban, { "aria-hidden": true }),
2765
+ t.turnOff
2766
+ ] })
2767
+ ] })
2768
+ ] }, l.id)),
2769
+ /* @__PURE__ */ jsxs6("button", { type: "button", className: "w100n-docs-action w100n-share-new", onClick: () => {
2770
+ setState("idle");
2771
+ setForm(blank2);
2772
+ }, children: [
2773
+ /* @__PURE__ */ jsx6(Plus2, { "aria-hidden": true }),
2774
+ t.newLink
2775
+ ] }),
2776
+ team && /* @__PURE__ */ jsxs6(Fragment5, { children: [
2777
+ /* @__PURE__ */ jsxs6("p", { className: "w100n-docs-caption", children: [
2778
+ /* @__PURE__ */ jsx6(Users, { "aria-hidden": true }),
2779
+ t.inTeam
2780
+ ] }),
2781
+ /* @__PURE__ */ jsxs6("div", { className: "w100n-share-team", children: [
2782
+ /* @__PURE__ */ jsx6("span", { className: "w100n-share-tile", children: /* @__PURE__ */ jsx6(Users, { "aria-hidden": true }) }),
2783
+ /* @__PURE__ */ jsxs6("span", { children: [
2784
+ /* @__PURE__ */ jsx6("b", { children: t.peopleOf.replace("{space}", team) }),
2785
+ /* @__PURE__ */ jsx6("small", { children: t.teamHint })
2786
+ ] })
2787
+ ] })
2788
+ ] })
2789
+ ] });
2790
+ }
152
2791
 
153
2792
  // src/Thumb.tsx
154
- import { useEffect, useRef, useState } from "react";
2793
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState7 } from "react";
155
2794
 
156
2795
  // src/preview.ts
157
2796
  var cache = /* @__PURE__ */ new Map();
@@ -204,12 +2843,12 @@ function preview(host, doc) {
204
2843
  }
205
2844
 
206
2845
  // src/Thumb.tsx
207
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2846
+ import { Fragment as Fragment6, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
208
2847
  function Thumb({ host, item, label }) {
209
- const box = useRef(null);
210
- const [seen, setSeen] = useState(false);
211
- const [shot, setShot] = useState(null);
212
- useEffect(() => {
2848
+ const box = useRef5(null);
2849
+ const [seen, setSeen] = useState7(false);
2850
+ const [shot, setShot] = useState7(null);
2851
+ useEffect5(() => {
213
2852
  if (item.thumbnailUrl || item.excerpt || typeof IntersectionObserver === "undefined") return;
214
2853
  const watch = new IntersectionObserver((entries) => {
215
2854
  if (entries.some((entry) => entry.isIntersecting)) setSeen(true);
@@ -217,7 +2856,7 @@ function Thumb({ host, item, label }) {
217
2856
  watch.observe(box.current);
218
2857
  return () => watch.disconnect();
219
2858
  }, [item.thumbnailUrl, item.excerpt]);
220
- useEffect(() => {
2859
+ useEffect5(() => {
221
2860
  if (!seen) return;
222
2861
  let live = true;
223
2862
  void preview(host, item).then((found) => {
@@ -230,200 +2869,80 @@ function Thumb({ host, item, label }) {
230
2869
  const text = item.excerpt ?? shot?.text ?? "";
231
2870
  const lines = text.split("\n").filter((line) => !line.trim().startsWith("```")).map((line) => line.replace(/^#+\s*/, "").replace(/[*_`>|]/g, "").trim()).filter(Boolean);
232
2871
  const image = item.thumbnailUrl ?? shot?.image;
233
- return /* @__PURE__ */ jsxs("span", { ref: box, className: "w100n-docs-thumb", "data-kind": item.kind, children: [
234
- image ? /* @__PURE__ */ jsx("img", { src: image, alt: "" }) : /* @__PURE__ */ jsx("span", { className: "w100n-docs-paper", "aria-hidden": true, children: lines.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
235
- /* @__PURE__ */ jsx("b", { children: lines[0] }),
2872
+ return /* @__PURE__ */ jsxs7("span", { ref: box, className: "w100n-docs-thumb", "data-kind": item.kind, children: [
2873
+ image ? /* @__PURE__ */ jsx7("img", { src: image, alt: "" }) : /* @__PURE__ */ jsx7("span", { className: "w100n-docs-paper", "aria-hidden": true, children: lines.length ? /* @__PURE__ */ jsxs7(Fragment6, { children: [
2874
+ /* @__PURE__ */ jsx7("b", { children: lines[0] }),
236
2875
  lines.slice(1, 14).join(" ")
237
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
238
- /* @__PURE__ */ jsx("i", {}),
239
- /* @__PURE__ */ jsx("i", {}),
240
- /* @__PURE__ */ jsx("i", {}),
241
- /* @__PURE__ */ jsx("i", {}),
242
- /* @__PURE__ */ jsx("i", {})
2876
+ ] }) : /* @__PURE__ */ jsxs7(Fragment6, { children: [
2877
+ /* @__PURE__ */ jsx7("i", {}),
2878
+ /* @__PURE__ */ jsx7("i", {}),
2879
+ /* @__PURE__ */ jsx7("i", {}),
2880
+ /* @__PURE__ */ jsx7("i", {}),
2881
+ /* @__PURE__ */ jsx7("i", {})
243
2882
  ] }) }),
244
- /* @__PURE__ */ jsx("span", { className: "w100n-docs-kind", "data-kind": item.kind, children: label })
245
- ] });
246
- }
247
-
248
- // src/useLoad.ts
249
- import { useEffect as useEffect2, useState as useState2 } from "react";
250
- function useLoad(load, key) {
251
- const [state, setState] = useState2({ data: null, error: null, loading: true });
252
- const [attempt, setAttempt] = useState2(0);
253
- useEffect2(() => {
254
- const controller = new AbortController();
255
- setState((current) => ({ ...current, loading: true, error: null }));
256
- load(controller.signal).then(
257
- (data) => {
258
- if (!controller.signal.aborted) setState({ data, error: null, loading: false });
259
- },
260
- (error) => {
261
- if (!controller.signal.aborted) setState({ data: null, error: error instanceof Error ? error : new Error(String(error)), loading: false });
262
- }
263
- );
264
- return () => controller.abort();
265
- }, [...key, attempt]);
266
- return { ...state, retry: () => setAttempt((n) => n + 1) };
267
- }
268
-
269
- // src/viewers/DocxView.tsx
270
- import { useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
271
-
272
- // src/viewers/headings.ts
273
- var last = 0;
274
- function collect(root, selector, level = (el) => Number(el.tagName.slice(1))) {
275
- return [...root.querySelectorAll(selector)].flatMap((el) => {
276
- const text = el.textContent?.trim() ?? "";
277
- if (!text) return [];
278
- if (!el.id) el.id = `w100n-docs-h${++last}`;
279
- return [{ id: el.id, level: Math.min(3, Math.max(1, level(el) || 1)), text }];
280
- });
281
- }
282
-
283
- // src/viewers/DocxView.tsx
284
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
285
- function DocxView({ bytes, onOutline, failed }) {
286
- const box = useRef2(null);
287
- const [error, setError] = useState3(false);
288
- useLayoutEffect(() => {
289
- let live = true;
290
- let width = 0;
291
- const root = box.current;
292
- const fit = () => {
293
- const wrapper = root.querySelector(".w100n-docx-wrapper");
294
- if (wrapper && width) wrapper.style.zoom = String(Math.min(1, root.clientWidth / width));
295
- };
296
- const watch = new ResizeObserver(fit);
297
- root.replaceChildren();
298
- setError(false);
299
- import("docx-preview").then(({ renderAsync }) => renderAsync(bytes, root, void 0, { inWrapper: true, className: "w100n-docx", ignoreLastRenderedPageBreak: true })).then(() => {
300
- if (!live) return;
301
- width = Math.max(0, ...[...root.querySelectorAll("section.w100n-docx")].map((page) => page.offsetWidth));
302
- fit();
303
- watch.observe(root);
304
- onOutline(collect(root, '[class*="heading"]', (el) => Number(/heading(\d)/i.exec(el.className)?.[1] ?? 1)));
305
- }).catch(() => {
306
- if (live) setError(true);
307
- });
308
- return () => {
309
- live = false;
310
- watch.disconnect();
311
- };
312
- }, [bytes, onOutline]);
313
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
314
- error && /* @__PURE__ */ jsx2("p", { className: "w100n-docs-note", children: failed }),
315
- /* @__PURE__ */ jsx2("div", { ref: box, className: "w100n-docs-docx" })
316
- ] });
317
- }
318
-
319
- // src/viewers/MarkdownView.tsx
320
- import DOMPurify from "dompurify";
321
- import { marked } from "marked";
322
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef3 } from "react";
323
- import { jsx as jsx3 } from "react/jsx-runtime";
324
- function MarkdownView({ bytes, onOutline }) {
325
- const box = useRef3(null);
326
- useLayoutEffect2(() => {
327
- const root = box.current;
328
- root.innerHTML = DOMPurify.sanitize(marked.parse(new TextDecoder().decode(bytes), { async: false }));
329
- onOutline(collect(root, "h1, h2, h3"));
330
- }, [bytes, onOutline]);
331
- return /* @__PURE__ */ jsx3("article", { ref: box, className: "w100n-docs-page w100n-docs-prose" });
332
- }
333
-
334
- // src/viewers/PdfView.tsx
335
- import { useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
336
- import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
337
- var last2 = 0;
338
- function PdfView({ bytes, worker, onOutline, failed }) {
339
- const box = useRef4(null);
340
- const [error, setError] = useState4(false);
341
- useEffect3(() => {
342
- const root = box.current;
343
- const prefix = `w100n-docs-p${++last2}`;
344
- let stop = false;
345
- let close = () => {
346
- };
347
- root.replaceChildren();
348
- setError(false);
349
- (async () => {
350
- var _a;
351
- const pdfjs = await import("pdfjs-dist");
352
- if (worker) (_a = pdfjs.GlobalWorkerOptions).workerPort ?? (_a.workerPort = worker());
353
- const task = pdfjs.getDocument({ data: new Uint8Array(bytes.slice(0)) });
354
- close = () => {
355
- void task.destroy();
356
- };
357
- const pdf = await task.promise;
358
- const width = Math.max(320, root.clientWidth - 48);
359
- for (let n = 1; n <= pdf.numPages && !stop; n++) {
360
- const page = await pdf.getPage(n);
361
- const scale = Math.min(2, width / page.getViewport({ scale: 1 }).width);
362
- const viewport = page.getViewport({ scale: scale * window.devicePixelRatio });
363
- const canvas = document.createElement("canvas");
364
- canvas.id = `${prefix}-${n}`;
365
- canvas.className = "w100n-docs-sheet";
366
- canvas.width = viewport.width;
367
- canvas.height = viewport.height;
368
- canvas.style.width = `${viewport.width / window.devicePixelRatio}px`;
369
- root.append(canvas);
370
- await page.render({ canvas, canvasContext: canvas.getContext("2d"), viewport }).promise;
371
- }
372
- const flat = async (items, level) => (await Promise.all(items.map(async (item) => {
373
- const dest = typeof item.dest === "string" ? await pdf.getDestination(item.dest) : item.dest;
374
- const ref = Array.isArray(dest) ? dest[0] : null;
375
- const index = ref && typeof ref === "object" ? await pdf.getPageIndex(ref) : null;
376
- const here = index === null ? [] : [{ id: `${prefix}-${index + 1}`, level, text: item.title }];
377
- return [...here, ...level < 3 ? await flat(item.items ?? [], level + 1) : []];
378
- }))).flat();
379
- if (!stop) onOutline(await flat(await pdf.getOutline() ?? [], 1));
380
- })().catch(() => {
381
- if (!stop) setError(true);
382
- });
383
- return () => {
384
- stop = true;
385
- close();
386
- };
387
- }, [bytes, worker, onOutline]);
388
- return /* @__PURE__ */ jsxs3(Fragment3, { children: [
389
- error && /* @__PURE__ */ jsx4("p", { className: "w100n-docs-note", children: failed }),
390
- /* @__PURE__ */ jsx4("div", { ref: box, className: "w100n-docs-pdf" })
2883
+ /* @__PURE__ */ jsx7("span", { className: "w100n-docs-kind", "data-kind": item.kind, children: label })
391
2884
  ] });
392
2885
  }
393
2886
 
394
- // src/viewers/SlidesView.tsx
395
- import { jsx as jsx5 } from "react/jsx-runtime";
396
- function SlidesView({ bytes, title }) {
397
- return /* @__PURE__ */ jsx5("iframe", { className: "w100n-docs-slides", sandbox: "allow-scripts", title, srcDoc: new TextDecoder().decode(bytes) });
398
- }
399
-
400
2887
  // src/DocumentsWorkspace.tsx
401
- import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
402
- var icons = { docx: FileText, markdown: FileType, pdf: Files, sheet: FileSpreadsheet, slides: Presentation };
2888
+ import { Fragment as Fragment7, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2889
+ var TextEditor2 = lazy2(() => import("./TextEditor-4YETNX4T.js"));
2890
+ var SheetView2 = lazy2(() => import("./SheetView-TXHS5WJC.js"));
2891
+ var icons2 = { docx: FileText2, markdown: FileType2, pdf: Files2, sheet: FileSpreadsheet2, slides: Presentation2 };
403
2892
  function DocumentsWorkspace({ host, language = "en", theme = "light", place, onPlace, start, end, aside, actions }) {
404
2893
  const t = texts[language];
405
- const [own, setOwn] = useState5({ spaceId: null, documentId: null });
2894
+ const [own, setOwn] = useState8({ spaceId: null, documentId: null });
406
2895
  const here = place ?? own;
407
2896
  const go = (next) => {
408
2897
  setOwn(next);
409
2898
  onPlace?.(next);
410
2899
  };
411
- const [query, setQuery] = useState5("");
412
- const [kind, setKind] = useState5("all");
413
- const [listOpen, setListOpen] = useState5(true);
414
- const [panelOpen, setPanelOpen] = useState5(true);
415
- const root = useRef5(null);
416
- const narrow = () => root.current !== null && getComputedStyle(root.current).getPropertyValue("--docs-narrow").trim() === "1";
2900
+ const [query, setQuery] = useState8("");
2901
+ const [kind, setKind] = useState8("all");
2902
+ const [listOpen, setListOpen] = useState8(true);
2903
+ const [panelOpen, setPanelOpen] = useState8(true);
2904
+ const [narrow, setNarrow] = useState8(false);
2905
+ const body = useRef6(null);
2906
+ const listButton = useRef6(null);
2907
+ const panelButton = useRef6(null);
2908
+ const listClose = useRef6(null);
2909
+ const panel = useRef6(null);
2910
+ const guard = useRef6(null);
417
2911
  useLayoutEffect3(() => {
418
- if (here.documentId && narrow()) setListOpen(false);
419
- }, []);
420
- const [picking, setPicking] = useState5(false);
421
- const [tab, setTab] = useState5("outline");
422
- const [zoom, setZoom] = useState5(100);
423
- const [outline, setOutline] = useState5([]);
424
- const [version, setVersion] = useState5({ documentId: null, id: null });
2912
+ const el = body.current;
2913
+ let was = false;
2914
+ const check = () => {
2915
+ const now = getComputedStyle(el).getPropertyValue("--docs-narrow").trim() === "1";
2916
+ if (now && !was) {
2917
+ setPanelOpen(false);
2918
+ if (here.documentId) setListOpen(false);
2919
+ }
2920
+ was = now;
2921
+ setNarrow(now);
2922
+ };
2923
+ check();
2924
+ if (typeof ResizeObserver === "undefined") return;
2925
+ const watch = new ResizeObserver(check);
2926
+ watch.observe(el);
2927
+ return () => watch.disconnect();
2928
+ }, [here.documentId]);
2929
+ const [picking, setPicking] = useState8(false);
2930
+ useEffect6(() => {
2931
+ if (!picking) return;
2932
+ const close = (e) => {
2933
+ if (!e.target.closest(".w100n-docs-picker")) setPicking(false);
2934
+ };
2935
+ document.addEventListener("pointerdown", close);
2936
+ return () => document.removeEventListener("pointerdown", close);
2937
+ }, [picking]);
2938
+ const [tab, setTab] = useState8("outline");
2939
+ const [outline, setOutline] = useState8([]);
2940
+ const [version, setVersion] = useState8({ documentId: null, id: null });
425
2941
  const versionId = version.documentId === here.documentId ? version.id : null;
426
- const [restoring, setRestoring] = useState5("idle");
2942
+ const [restoring, setRestoring] = useState8("idle");
2943
+ const [exporting, setExporting] = useState8("idle");
2944
+ const [copying, setCopying] = useState8("idle");
2945
+ const [box, setBox] = useState8(null);
427
2946
  const spaces = useLoad((signal) => host.spaces({ signal }), [host]);
428
2947
  const single = spaces.data?.length === 1 ? spaces.data[0].id : null;
429
2948
  const spaceId = here.spaceId ?? single;
@@ -437,11 +2956,14 @@ function DocumentsWorkspace({ host, language = "en", theme = "light", place, onP
437
2956
  const doc = useLoad((signal) => here.documentId ? host.document(here.documentId, { signal }) : Promise.resolve(null), [host, here.documentId]);
438
2957
  const versions = useLoad((signal) => here.documentId ? host.versions(here.documentId, { signal }) : Promise.resolve([]), [host, here.documentId]);
439
2958
  const content = useLoad((signal) => here.documentId ? host.content(here.documentId, versionId, { signal }) : Promise.resolve(null), [host, here.documentId, versionId]);
2959
+ const pdf = doc.data?.kind === "pdf";
2960
+ const text = doc.data?.kind === "markdown" || doc.data?.kind === "docx";
2961
+ const signatures = useLoad((signal) => here.documentId && pdf && host.signatures ? host.signatures(here.documentId, { signal }) : Promise.resolve([]), [host, here.documentId, pdf]);
440
2962
  const all = list.data ?? [];
441
2963
  const shown = all.filter((item) => matches(item, query, kind));
442
2964
  const present = kinds.filter((item) => all.some((doc2) => doc2.kind === item));
443
- const folders = spaceId ? [.../* @__PURE__ */ new Set(["", ...shown.map((item) => item.folder ?? "")])].filter((folder) => shown.some((item) => (item.folder ?? "") === folder)) : [""];
444
- const names = new Map((spaces.data ?? []).map((item) => [item.id, item.name]));
2965
+ const folders = spaceId ? [.../* @__PURE__ */ new Set(["", ...shown.map((item) => item.folder ?? "")])].filter((folder2) => shown.some((item) => (item.folder ?? "") === folder2)) : [""];
2966
+ const names2 = new Map((spaces.data ?? []).map((item) => [item.id, item.name]));
445
2967
  const counts = /* @__PURE__ */ new Map();
446
2968
  for (const item of all) counts.set(item.spaceId, (counts.get(item.spaceId) ?? 0) + 1);
447
2969
  const groups = [...new Set((spaces.data ?? []).map((item) => item.group ?? ""))];
@@ -455,21 +2977,47 @@ function DocumentsWorkspace({ host, language = "en", theme = "light", place, onP
455
2977
  return new Intl.DateTimeFormat(language, { day: "numeric", month: "short", year: "numeric" }).format(new Date(iso));
456
2978
  };
457
2979
  const size = (bytes) => bytes < 1024 ? `${bytes} B` : bytes < 1048576 ? `${(bytes / 1024).toFixed(0)} KB` : `${(bytes / 1048576).toFixed(1)} MB`;
458
- const failed = (retry) => /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-note", children: [
2980
+ const failed = (retry) => /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-note", children: [
459
2981
  t.failed,
460
- /* @__PURE__ */ jsx6("button", { type: "button", onClick: retry, children: t.retry })
2982
+ /* @__PURE__ */ jsx8("button", { type: "button", onClick: retry, children: t.retry })
461
2983
  ] });
2984
+ const { ask, dialog } = useUnsaved(guard, t);
2985
+ const side = (which, open2) => {
2986
+ flushSync(() => {
2987
+ if (which === "list") setListOpen(open2);
2988
+ else setPanelOpen(open2);
2989
+ if (open2 && narrow) (which === "list" ? setPanelOpen : setListOpen)(false);
2990
+ });
2991
+ if (which === "list") (open2 ? listClose : listButton).current?.focus();
2992
+ else (open2 ? panel.current?.querySelector('[aria-selected="true"]') : panelButton.current)?.focus();
2993
+ };
462
2994
  const open = (item) => {
463
- setOutline([]);
464
- setTab("outline");
465
- go({ spaceId: here.spaceId, documentId: item.id });
466
- if (narrow()) setListOpen(false);
2995
+ if (item.id === here.documentId) {
2996
+ if (narrow) setListOpen(false);
2997
+ return;
2998
+ }
2999
+ ask(() => {
3000
+ setOutline([]);
3001
+ setTab("outline");
3002
+ go({ spaceId: here.spaceId, documentId: item.id });
3003
+ if (narrow) setListOpen(false);
3004
+ });
467
3005
  };
468
3006
  const pick = (next) => {
469
3007
  setPicking(false);
470
- setKind("all");
471
- go({ spaceId: next, documentId: null });
3008
+ ask(() => {
3009
+ setKind("all");
3010
+ go({ spaceId: next, documentId: null });
3011
+ });
3012
+ };
3013
+ const view = (id) => {
3014
+ if (id === versionId) return;
3015
+ ask(() => {
3016
+ setRestoring("idle");
3017
+ setVersion({ documentId: here.documentId, id });
3018
+ });
472
3019
  };
3020
+ const slot = (part) => typeof part === "function" ? part(ask) : part;
473
3021
  const restore = async (documentId, versionId2) => {
474
3022
  setRestoring("busy");
475
3023
  try {
@@ -484,8 +3032,33 @@ function DocumentsWorkspace({ host, language = "en", theme = "light", place, onP
484
3032
  doc.retry();
485
3033
  list.retry();
486
3034
  };
487
- let stage = /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-empty", children: [
488
- /* @__PURE__ */ jsx6(FileText, { "aria-hidden": true }),
3035
+ const exportAs = async (file, format) => {
3036
+ setExporting("busy");
3037
+ try {
3038
+ const url = URL.createObjectURL(await host.exportAs(file.id, format));
3039
+ const link = document.createElement("a");
3040
+ link.href = url;
3041
+ link.download = `${file.name.replace(/\.[^.]+$/, "")}.${format}`;
3042
+ link.click();
3043
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
3044
+ setExporting("idle");
3045
+ } catch {
3046
+ setExporting("failed");
3047
+ }
3048
+ };
3049
+ const copyAs = async (file) => {
3050
+ setCopying("busy");
3051
+ try {
3052
+ const made = await host.copyAs(file.id, "xlsx");
3053
+ setCopying("idle");
3054
+ list.retry();
3055
+ open(made);
3056
+ } catch {
3057
+ setCopying("failed");
3058
+ }
3059
+ };
3060
+ let stage = /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-empty", children: [
3061
+ /* @__PURE__ */ jsx8(FileText2, { "aria-hidden": true }),
489
3062
  t.pick
490
3063
  ] });
491
3064
  if (here.documentId) {
@@ -493,166 +3066,543 @@ function DocumentsWorkspace({ host, language = "en", theme = "light", place, onP
493
3066
  doc.retry();
494
3067
  content.retry();
495
3068
  });
496
- else if (!doc.data || !content.data) stage = /* @__PURE__ */ jsx6("div", { className: "w100n-docs-empty", children: t.loading });
497
- else if (doc.data.kind === "markdown") stage = /* @__PURE__ */ jsx6(MarkdownView, { bytes: content.data, onOutline: setOutline });
498
- else if (doc.data.kind === "docx") stage = /* @__PURE__ */ jsx6(DocxView, { bytes: content.data, onOutline: setOutline, failed: t.failed });
499
- else if (doc.data.kind === "pdf") stage = /* @__PURE__ */ jsx6(PdfView, { bytes: content.data, worker: host.pdfWorker, onOutline: setOutline, failed: t.failed });
500
- else if (doc.data.kind === "slides") stage = /* @__PURE__ */ jsx6(SlidesView, { bytes: content.data, title: doc.data.name });
501
- else stage = /* @__PURE__ */ jsx6("div", { className: "w100n-docs-empty", children: t.unsupported });
3069
+ else if (!doc.data || !content.data) stage = /* @__PURE__ */ jsx8("div", { className: "w100n-docs-empty", children: t.loading });
3070
+ else if (doc.data.kind === "markdown" || doc.data.kind === "docx") stage = /* @__PURE__ */ jsx8(Suspense2, { fallback: /* @__PURE__ */ jsx8("div", { className: "w100n-docs-empty", children: t.loading }), children: /* @__PURE__ */ jsx8(
3071
+ TextEditor2,
3072
+ {
3073
+ kind: doc.data.kind,
3074
+ bytes: content.data,
3075
+ t,
3076
+ language,
3077
+ rights: doc.data.rights,
3078
+ host: versionId ? void 0 : host,
3079
+ together: doc.data.live,
3080
+ documentId: doc.data.id,
3081
+ onOutline: setOutline,
3082
+ onPanel: (next) => {
3083
+ setPanelOpen(true);
3084
+ setTab(next);
3085
+ if (narrow) setListOpen(false);
3086
+ },
3087
+ slot: tab === "comments" && panelOpen ? box : null,
3088
+ guard,
3089
+ onSaved: () => {
3090
+ versions.retry();
3091
+ doc.retry();
3092
+ list.retry();
3093
+ }
3094
+ },
3095
+ doc.data.id
3096
+ ) });
3097
+ else if (doc.data.kind === "pdf") stage = /* @__PURE__ */ jsx8(
3098
+ PdfView,
3099
+ {
3100
+ bytes: content.data,
3101
+ t,
3102
+ language,
3103
+ worker: host.pdfWorker,
3104
+ rights: doc.data.rights,
3105
+ host: versionId ? void 0 : host,
3106
+ documentId: doc.data.id,
3107
+ onOutline: setOutline,
3108
+ guard,
3109
+ onSaved: () => {
3110
+ content.retry();
3111
+ versions.retry();
3112
+ doc.retry();
3113
+ list.retry();
3114
+ signatures.retry();
3115
+ }
3116
+ },
3117
+ `${doc.data.id}:${versionId ?? ""}`
3118
+ );
3119
+ else if (doc.data.kind === "slides") stage = /* @__PURE__ */ jsx8(
3120
+ SlidesView,
3121
+ {
3122
+ html: new TextDecoder().decode(content.data),
3123
+ name: doc.data.name,
3124
+ rights: doc.data.rights,
3125
+ language,
3126
+ theme,
3127
+ onOutline: setOutline,
3128
+ guard,
3129
+ onSaveRequest: host.save && !versionId ? async (bytes, note) => {
3130
+ await host.save(doc.data.id, bytes, note);
3131
+ versions.retry();
3132
+ doc.retry();
3133
+ list.retry();
3134
+ } : void 0
3135
+ },
3136
+ doc.data.id
3137
+ );
3138
+ else if (doc.data.kind === "sheet") stage = /* @__PURE__ */ jsx8(Suspense2, { fallback: /* @__PURE__ */ jsx8("div", { className: "w100n-docs-empty", children: t.loading }), children: /* @__PURE__ */ jsx8(
3139
+ SheetView2,
3140
+ {
3141
+ bytes: content.data,
3142
+ name: doc.data.name,
3143
+ kind: /\.csv$/i.test(doc.data.name) || doc.data.mimeType === "text/csv" ? "csv" : "xlsx",
3144
+ rights: doc.data.rights,
3145
+ language,
3146
+ theme,
3147
+ guard,
3148
+ host: versionId ? void 0 : host,
3149
+ together: doc.data.live,
3150
+ documentId: doc.data.id,
3151
+ onSaved: () => {
3152
+ versions.retry();
3153
+ doc.retry();
3154
+ list.retry();
3155
+ },
3156
+ onSaveRequest: versionId || !host.save ? void 0 : async (bytes, note) => {
3157
+ await host.save(doc.data.id, bytes, note);
3158
+ versions.retry();
3159
+ doc.retry();
3160
+ list.retry();
3161
+ }
3162
+ },
3163
+ `${doc.data.id}:${versionId ?? ""}`
3164
+ ) });
3165
+ else stage = /* @__PURE__ */ jsx8("div", { className: "w100n-docs-empty", children: t.unsupported });
502
3166
  }
503
- const Kind = doc.data ? icons[doc.data.kind] : FileText;
504
- const crumbs = doc.data ? [names.get(doc.data.spaceId), doc.data.folder].filter(Boolean) : space ? [space.name] : [];
3167
+ const Kind = doc.data ? icons2[doc.data.kind] : FileText2;
3168
+ const crumbs = doc.data ? [names2.get(doc.data.spaceId), doc.data.folder].filter(Boolean) : space ? [space.name] : [];
505
3169
  const panelTabs = [["outline", ListTree, t.outline], ["versions", History, t.versions], ["details", Info, t.details]];
506
- return /* @__PURE__ */ jsxs4("div", { ref: root, className: "w100n-docs", "data-theme": theme, children: [
507
- /* @__PURE__ */ jsxs4("header", { className: "w100n-docs-bar", children: [
508
- start,
509
- /* @__PURE__ */ jsx6("span", { className: "w100n-docs-tile", "data-kind": doc.data?.kind, children: /* @__PURE__ */ jsx6(Kind, { "aria-hidden": true }) }),
510
- /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-heading", children: [
511
- /* @__PURE__ */ jsx6("strong", { className: "w100n-docs-title", children: doc.data?.name ?? t.documents }),
512
- crumbs.length > 0 && /* @__PURE__ */ jsx6("span", { className: "w100n-docs-crumbs", children: crumbs.map((item, i) => /* @__PURE__ */ jsxs4("span", { children: [
513
- i > 0 && /* @__PURE__ */ jsx6(ChevronRight, { "aria-hidden": true }),
3170
+ if (pdf && host.signatures) panelTabs.push(["signatures", Signature, t.signatures]);
3171
+ if (text && host.comments && !versionId) panelTabs.push(["comments", MessagesSquare, t.comments]);
3172
+ const sharing = !!host.links && !!doc.data?.rights.edit && !versionId;
3173
+ if (sharing) panelTabs.push(["sharing", Share2, t.sharing]);
3174
+ const listToggle = /* @__PURE__ */ jsx8(
3175
+ "button",
3176
+ {
3177
+ ref: listButton,
3178
+ type: "button",
3179
+ className: narrow ? "w100n-docs-close" : "w100n-docs-reopen",
3180
+ "aria-expanded": listOpen,
3181
+ onClick: () => side("list", !listOpen),
3182
+ "aria-label": listOpen ? t.closeList : t.openList,
3183
+ title: listOpen ? t.closeList : t.openList,
3184
+ children: listOpen ? /* @__PURE__ */ jsx8(PanelLeftClose, { "aria-hidden": true }) : /* @__PURE__ */ jsx8(PanelLeftOpen, { "aria-hidden": true })
3185
+ }
3186
+ );
3187
+ const panelToggle = doc.data && /* @__PURE__ */ jsx8(
3188
+ "button",
3189
+ {
3190
+ ref: panelButton,
3191
+ type: "button",
3192
+ className: narrow ? "w100n-docs-close" : "w100n-docs-reopen w100n-docs-reopen-right",
3193
+ "aria-expanded": panelOpen,
3194
+ onClick: () => side("panel", !panelOpen),
3195
+ "aria-label": panelOpen ? t.closePanel : t.openPanel,
3196
+ title: panelOpen ? t.closePanel : t.openPanel,
3197
+ children: panelOpen ? /* @__PURE__ */ jsx8(PanelRightClose, { "aria-hidden": true }) : /* @__PURE__ */ jsx8(PanelRightOpen, { "aria-hidden": true })
3198
+ }
3199
+ );
3200
+ return /* @__PURE__ */ jsxs8("div", { className: "w100n-docs", "data-theme": theme, children: [
3201
+ /* @__PURE__ */ jsxs8("header", { className: "w100n-docs-bar", children: [
3202
+ slot(start),
3203
+ narrow && listToggle,
3204
+ /* @__PURE__ */ jsx8("span", { className: "w100n-docs-tile", "data-kind": doc.data?.kind, children: /* @__PURE__ */ jsx8(Kind, { "aria-hidden": true }) }),
3205
+ /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-heading", children: [
3206
+ /* @__PURE__ */ jsx8("strong", { className: "w100n-docs-title", children: doc.data?.name ?? t.documents }),
3207
+ crumbs.length > 0 && /* @__PURE__ */ jsx8("span", { className: "w100n-docs-crumbs", children: crumbs.map((item, i) => /* @__PURE__ */ jsxs8("span", { children: [
3208
+ i > 0 && /* @__PURE__ */ jsx8(ChevronRight2, { "aria-hidden": true }),
514
3209
  item
515
3210
  ] }, i)) })
516
3211
  ] }),
517
- doc.data && /* @__PURE__ */ jsx6("span", { className: "w100n-docs-badge", "data-kind": doc.data.kind, children: t.kinds[doc.data.kind] }),
518
- versionId && /* @__PURE__ */ jsx6("span", { className: "w100n-docs-badge", children: versions.data?.find((v) => v.id === versionId) && date(versions.data.find((v) => v.id === versionId).createdAt) }),
519
- versionId && here.documentId && host.restore && doc.data?.rights.edit && /* @__PURE__ */ jsxs4("button", { type: "button", className: "w100n-docs-action", disabled: restoring === "busy", onClick: () => void restore(here.documentId, versionId), children: [
520
- /* @__PURE__ */ jsx6(RotateCcw, { "aria-hidden": true }),
3212
+ doc.data && /* @__PURE__ */ jsx8("span", { className: "w100n-docs-badge", "data-kind": doc.data.kind, children: t.kinds[doc.data.kind] }),
3213
+ versionId && /* @__PURE__ */ jsx8("span", { className: "w100n-docs-badge", children: versions.data?.find((v) => v.id === versionId) && date(versions.data.find((v) => v.id === versionId).createdAt) }),
3214
+ versionId && here.documentId && host.restore && doc.data?.rights.edit && /* @__PURE__ */ jsxs8("button", { type: "button", className: "w100n-docs-action", disabled: restoring === "busy", onClick: () => void restore(here.documentId, versionId), children: [
3215
+ /* @__PURE__ */ jsx8(RotateCcw2, { "aria-hidden": true }),
521
3216
  t.restore
522
3217
  ] }),
523
- restoring === "failed" && /* @__PURE__ */ jsx6("span", { className: "w100n-docs-muted", children: t.restoreFailed }),
3218
+ restoring === "failed" && /* @__PURE__ */ jsx8("span", { className: "w100n-docs-muted", children: t.restoreFailed }),
3219
+ doc.data?.convert && host.copyAs && !versionId && /* @__PURE__ */ jsxs8("button", { type: "button", className: "w100n-docs-action", "data-tone": "green", disabled: copying === "busy", onClick: () => void copyAs(doc.data), children: [
3220
+ /* @__PURE__ */ jsx8(FileSpreadsheet2, { "aria-hidden": true }),
3221
+ t.copyAs
3222
+ ] }),
3223
+ copying === "failed" && /* @__PURE__ */ jsx8("span", { className: "w100n-docs-muted", children: t.copyFailed }),
3224
+ sharing && /* @__PURE__ */ jsxs8("button", { type: "button", className: "w100n-docs-action", "aria-label": t.share, title: t.share, onClick: () => {
3225
+ setTab("sharing");
3226
+ side("panel", true);
3227
+ }, children: [
3228
+ /* @__PURE__ */ jsx8(Share2, { "aria-hidden": true }),
3229
+ /* @__PURE__ */ jsx8("span", { children: t.share })
3230
+ ] }),
524
3231
  doc.data && !versionId && actions?.(doc.data),
525
- end && /* @__PURE__ */ jsx6("div", { className: "w100n-docs-end", children: end })
3232
+ narrow && panelToggle,
3233
+ end && /* @__PURE__ */ jsx8("div", { className: "w100n-docs-end", children: slot(end) })
526
3234
  ] }),
527
- /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-body", children: [
528
- listOpen ? /* @__PURE__ */ jsxs4("aside", { className: "w100n-docs-side w100n-docs-list", "aria-label": t.documents, children: [
529
- /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-head", children: [
530
- /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-row", children: [
531
- /* @__PURE__ */ jsxs4("p", { className: "w100n-docs-caption", children: [
532
- /* @__PURE__ */ jsx6(Files, { "aria-hidden": true }),
3235
+ /* @__PURE__ */ jsxs8("div", { ref: body, className: "w100n-docs-body", children: [
3236
+ listOpen && /* @__PURE__ */ jsxs8("aside", { className: "w100n-docs-side w100n-docs-list", "aria-label": t.documents, children: [
3237
+ /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-head", children: [
3238
+ /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-row", children: [
3239
+ /* @__PURE__ */ jsxs8("p", { className: "w100n-docs-caption", children: [
3240
+ /* @__PURE__ */ jsx8(Files2, { "aria-hidden": true }),
533
3241
  t.documents,
534
- /* @__PURE__ */ jsx6("span", { children: all.length })
3242
+ /* @__PURE__ */ jsx8("span", { children: all.length })
535
3243
  ] }),
536
- /* @__PURE__ */ jsx6("button", { type: "button", className: "w100n-docs-close", onClick: () => setListOpen(false), "aria-label": t.closeList, title: t.closeList, children: /* @__PURE__ */ jsx6(X, { "aria-hidden": true }) })
3244
+ /* @__PURE__ */ jsx8("button", { ref: listClose, type: "button", className: "w100n-docs-close", onClick: () => side("list", false), "aria-label": t.closeList, title: t.closeList, children: /* @__PURE__ */ jsx8(X5, { "aria-hidden": true }) })
537
3245
  ] }),
538
- (spaces.data?.length ?? 0) > 1 && /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-picker", children: [
539
- /* @__PURE__ */ jsxs4("button", { type: "button", className: "w100n-docs-field", "aria-haspopup": "listbox", "aria-expanded": picking, onClick: () => setPicking(!picking), children: [
540
- space ? /* @__PURE__ */ jsx6("span", { className: "w100n-docs-dot", style: { background: space.color ?? void 0 } }) : /* @__PURE__ */ jsx6("span", { className: "w100n-docs-all", children: /* @__PURE__ */ jsx6(LayoutGrid, { "aria-hidden": true }) }),
541
- /* @__PURE__ */ jsx6("b", { children: space?.name ?? t.allProjects }),
542
- !space && /* @__PURE__ */ jsx6("span", { className: "w100n-docs-count", children: all.length }),
543
- /* @__PURE__ */ jsx6(ChevronDown, { "aria-hidden": true })
3246
+ (spaces.data?.length ?? 0) > 1 && /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-picker", onKeyDown: (e) => {
3247
+ if (e.key !== "Escape" || !picking) return;
3248
+ e.stopPropagation();
3249
+ setPicking(false);
3250
+ e.currentTarget.querySelector("button").focus();
3251
+ }, children: [
3252
+ /* @__PURE__ */ jsxs8("button", { type: "button", className: "w100n-docs-field", "aria-haspopup": "listbox", "aria-expanded": picking, onClick: () => setPicking(!picking), children: [
3253
+ space ? /* @__PURE__ */ jsx8("span", { className: "w100n-docs-dot", style: { background: space.color ?? void 0 } }) : /* @__PURE__ */ jsx8("span", { className: "w100n-docs-all", children: /* @__PURE__ */ jsx8(LayoutGrid, { "aria-hidden": true }) }),
3254
+ /* @__PURE__ */ jsx8("b", { children: space?.name ?? t.allProjects }),
3255
+ !space && /* @__PURE__ */ jsx8("span", { className: "w100n-docs-count", children: all.length }),
3256
+ /* @__PURE__ */ jsx8(ChevronDown2, { "aria-hidden": true })
544
3257
  ] }),
545
- picking && /* @__PURE__ */ jsxs4("div", { role: "listbox", className: "w100n-docs-menu", children: [
546
- /* @__PURE__ */ jsxs4("button", { type: "button", role: "option", "aria-selected": !spaceId, onClick: () => pick(null), children: [
547
- /* @__PURE__ */ jsx6("span", { className: "w100n-docs-all", children: /* @__PURE__ */ jsx6(LayoutGrid, { "aria-hidden": true }) }),
548
- /* @__PURE__ */ jsx6("b", { children: t.allProjects }),
549
- !spaceId && /* @__PURE__ */ jsx6(Check, { "aria-hidden": true })
3258
+ picking && /* @__PURE__ */ jsxs8("div", { role: "listbox", className: "w100n-docs-menu", children: [
3259
+ /* @__PURE__ */ jsxs8("button", { type: "button", role: "option", "aria-selected": !spaceId, onClick: () => pick(null), children: [
3260
+ /* @__PURE__ */ jsx8("span", { className: "w100n-docs-all", children: /* @__PURE__ */ jsx8(LayoutGrid, { "aria-hidden": true }) }),
3261
+ /* @__PURE__ */ jsx8("b", { children: t.allProjects }),
3262
+ !spaceId && /* @__PURE__ */ jsx8(Check, { "aria-hidden": true })
550
3263
  ] }),
551
- groups.map((group) => /* @__PURE__ */ jsxs4("div", { role: "group", "aria-label": group || void 0, children: [
552
- group && /* @__PURE__ */ jsx6("p", { className: "w100n-docs-menu-caption", children: group }),
553
- (spaces.data ?? []).filter((item) => (item.group ?? "") === group).map((item) => /* @__PURE__ */ jsxs4("button", { type: "button", role: "option", "aria-selected": item.id === spaceId, onClick: () => pick(item.id), children: [
554
- /* @__PURE__ */ jsx6("span", { className: "w100n-docs-dot", style: { background: item.color ?? void 0 } }),
555
- /* @__PURE__ */ jsx6("b", { children: item.name }),
556
- !spaceId && counts.get(item.id) ? /* @__PURE__ */ jsx6("span", { className: "w100n-docs-count", children: counts.get(item.id) }) : null,
557
- item.id === spaceId && /* @__PURE__ */ jsx6(Check, { "aria-hidden": true })
3264
+ groups.map((group) => /* @__PURE__ */ jsxs8("div", { role: "group", "aria-label": group || void 0, children: [
3265
+ group && /* @__PURE__ */ jsx8("p", { className: "w100n-docs-menu-caption", children: group }),
3266
+ (spaces.data ?? []).filter((item) => (item.group ?? "") === group).map((item) => /* @__PURE__ */ jsxs8("button", { type: "button", role: "option", "aria-selected": item.id === spaceId, onClick: () => pick(item.id), children: [
3267
+ /* @__PURE__ */ jsx8("span", { className: "w100n-docs-dot", style: { background: item.color ?? void 0 } }),
3268
+ /* @__PURE__ */ jsx8("b", { children: item.name }),
3269
+ !spaceId && counts.get(item.id) ? /* @__PURE__ */ jsx8("span", { className: "w100n-docs-count", children: counts.get(item.id) }) : null,
3270
+ item.id === spaceId && /* @__PURE__ */ jsx8(Check, { "aria-hidden": true })
558
3271
  ] }, item.id))
559
3272
  ] }, group))
560
3273
  ] })
561
3274
  ] }),
562
- /* @__PURE__ */ jsxs4("label", { className: "w100n-docs-field w100n-docs-search", children: [
563
- /* @__PURE__ */ jsx6(Search, { "aria-hidden": true }),
564
- /* @__PURE__ */ jsx6("input", { value: query, onChange: (event) => setQuery(event.target.value), placeholder: t.search, "aria-label": t.search })
3275
+ /* @__PURE__ */ jsxs8("label", { className: "w100n-docs-field w100n-docs-search", children: [
3276
+ /* @__PURE__ */ jsx8(Search2, { "aria-hidden": true }),
3277
+ /* @__PURE__ */ jsx8("input", { value: query, onChange: (event) => setQuery(event.target.value), placeholder: t.search, "aria-label": t.search })
565
3278
  ] }),
566
- present.length > 1 && /* @__PURE__ */ jsx6("div", { className: "w100n-docs-chips", role: "group", children: ["all", ...present].map((item) => /* @__PURE__ */ jsxs4("button", { type: "button", "aria-pressed": kind === item, onClick: () => setKind(item), children: [
3279
+ present.length > 1 && /* @__PURE__ */ jsx8("div", { className: "w100n-docs-chips", role: "group", "aria-label": t.kind, children: ["all", ...present].map((item) => /* @__PURE__ */ jsxs8("button", { type: "button", "aria-pressed": kind === item, onClick: () => setKind(item), children: [
567
3280
  item === "all" ? t.all : t.kinds[item],
568
- /* @__PURE__ */ jsx6("span", { "aria-hidden": true, children: item === "all" ? all.length : all.filter((doc2) => doc2.kind === item).length })
3281
+ /* @__PURE__ */ jsx8("span", { "aria-hidden": true, children: item === "all" ? all.length : all.filter((doc2) => doc2.kind === item).length })
569
3282
  ] }, item)) })
570
3283
  ] }),
571
- /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-cards", children: [
3284
+ /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-cards", children: [
572
3285
  list.error && failed(list.retry),
573
- list.loading && !list.data && /* @__PURE__ */ jsx6("p", { className: "w100n-docs-muted", children: t.loading }),
574
- list.data && !shown.length && /* @__PURE__ */ jsx6("p", { className: "w100n-docs-muted", children: all.length ? t.nothing : t.empty }),
575
- folders.map((folder) => /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-folder", role: "group", "aria-label": folder || t.loose, children: [
576
- folders.length > 1 && /* @__PURE__ */ jsx6("p", { className: "w100n-docs-group", children: folder || t.loose }),
577
- /* @__PURE__ */ jsx6("div", { className: "w100n-docs-grid", children: shown.filter((item) => !spaceId || (item.folder ?? "") === folder).map((item) => /* @__PURE__ */ jsxs4("button", { type: "button", className: "w100n-docs-card", "aria-current": item.id === here.documentId, onClick: () => open(item), title: item.name, children: [
578
- /* @__PURE__ */ jsx6(Thumb, { host, item, label: t.kinds[item.kind] }),
579
- /* @__PURE__ */ jsxs4("span", { className: "w100n-docs-card-text", children: [
580
- /* @__PURE__ */ jsx6("b", { children: item.name }),
581
- /* @__PURE__ */ jsx6("small", { children: [!spaceId && names.get(item.spaceId), ago(item.updatedAt)].filter(Boolean).join(" \xB7 ") })
3286
+ list.loading && !list.data && /* @__PURE__ */ jsx8("p", { className: "w100n-docs-muted", children: t.loading }),
3287
+ list.data && !shown.length && /* @__PURE__ */ jsx8("p", { className: "w100n-docs-muted", children: all.length ? t.nothing : t.empty }),
3288
+ folders.map((folder2) => /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-folder", role: "group", "aria-label": folder2 || t.loose, children: [
3289
+ folders.length > 1 && /* @__PURE__ */ jsx8("p", { className: "w100n-docs-group", children: folder2 || t.loose }),
3290
+ /* @__PURE__ */ jsx8("div", { className: "w100n-docs-grid", children: shown.filter((item) => !spaceId || (item.folder ?? "") === folder2).map((item) => /* @__PURE__ */ jsxs8("button", { type: "button", className: "w100n-docs-card", "aria-current": item.id === here.documentId, onClick: () => open(item), title: item.name, children: [
3291
+ /* @__PURE__ */ jsx8(Thumb, { host, item, label: t.kinds[item.kind] }),
3292
+ /* @__PURE__ */ jsxs8("span", { className: "w100n-docs-card-text", children: [
3293
+ /* @__PURE__ */ jsx8("b", { children: item.name }),
3294
+ /* @__PURE__ */ jsx8("small", { children: [!spaceId && names2.get(item.spaceId), ago(item.updatedAt)].filter(Boolean).join(" \xB7 ") })
582
3295
  ] })
583
3296
  ] }, item.id)) })
584
- ] }, folder))
3297
+ ] }, folder2))
585
3298
  ] })
586
- ] }) : /* @__PURE__ */ jsx6("button", { type: "button", className: "w100n-docs-reopen", onClick: () => setListOpen(true), "aria-label": t.openList, title: t.openList, children: /* @__PURE__ */ jsx6(PanelLeftOpen, { "aria-hidden": true }) }),
587
- /* @__PURE__ */ jsxs4("main", { className: "w100n-docs-stage", children: [
588
- doc.data && /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-zoom", role: "group", children: [
589
- /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => setZoom(Math.max(50, zoom - 10)), "aria-label": t.zoomOut, title: t.zoomOut, children: /* @__PURE__ */ jsx6(Minus, { "aria-hidden": true }) }),
590
- /* @__PURE__ */ jsxs4("span", { children: [
591
- zoom,
592
- "%"
593
- ] }),
594
- /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => setZoom(Math.min(200, zoom + 10)), "aria-label": t.zoomIn, title: t.zoomIn, children: /* @__PURE__ */ jsx6(Plus, { "aria-hidden": true }) }),
595
- /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => setZoom(100), "aria-label": t.fit, title: t.fit, children: /* @__PURE__ */ jsx6(Maximize2, { "aria-hidden": true }) })
596
- ] }),
597
- doc.data && !panelOpen && /* @__PURE__ */ jsx6("button", { type: "button", className: "w100n-docs-reopen w100n-docs-reopen-right", onClick: () => setPanelOpen(true), "aria-label": t.openPanel, title: t.openPanel, children: /* @__PURE__ */ jsx6(PanelRightOpen, { "aria-hidden": true }) }),
598
- /* @__PURE__ */ jsx6("div", { className: "w100n-docs-scroll", children: /* @__PURE__ */ jsx6("div", { className: "w100n-docs-zoomed", style: { zoom: zoom / 100 }, children: stage }) })
599
3299
  ] }),
600
- doc.data && panelOpen && /* @__PURE__ */ jsxs4("aside", { className: "w100n-docs-side w100n-docs-panel", "aria-label": t.details, children: [
601
- /* @__PURE__ */ jsx6("div", { className: "w100n-docs-head", children: /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-row", children: [
602
- /* @__PURE__ */ jsx6("div", { className: "w100n-docs-tabs", role: "tablist", children: panelTabs.map(([key, Icon, label]) => /* @__PURE__ */ jsxs4("button", { type: "button", role: "tab", "aria-selected": tab === key, onClick: () => setTab(key), children: [
603
- /* @__PURE__ */ jsx6(Icon, { "aria-hidden": true }),
604
- label
3300
+ !narrow && !listOpen && listToggle,
3301
+ /* @__PURE__ */ jsxs8("main", { className: "w100n-docs-stage", children: [
3302
+ !narrow && !panelOpen && panelToggle,
3303
+ /* @__PURE__ */ jsx8("div", { className: "w100n-docs-bare", children: stage })
3304
+ ] }),
3305
+ doc.data && panelOpen && /* @__PURE__ */ jsxs8("aside", { ref: panel, className: "w100n-docs-side w100n-docs-panel", "aria-label": t.details, children: [
3306
+ /* @__PURE__ */ jsx8("div", { className: "w100n-docs-head", children: /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-row", children: [
3307
+ /* @__PURE__ */ jsx8("div", { className: "w100n-docs-tabs", role: "tablist", "aria-label": t.details, "data-compact": panelTabs.length > 3, onKeyDown: rove, children: panelTabs.map(([key, Icon, label]) => /* @__PURE__ */ jsxs8("button", { type: "button", role: "tab", "aria-selected": tab === key, tabIndex: tab === key ? 0 : -1, "aria-label": label, title: label, onClick: () => setTab(key), children: [
3308
+ /* @__PURE__ */ jsx8(Icon, { "aria-hidden": true }),
3309
+ (panelTabs.length < 4 || tab === key) && label
605
3310
  ] }, key)) }),
606
- /* @__PURE__ */ jsx6("button", { type: "button", className: "w100n-docs-close", onClick: () => setPanelOpen(false), "aria-label": t.closePanel, title: t.closePanel, children: /* @__PURE__ */ jsx6(X, { "aria-hidden": true }) })
3311
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "w100n-docs-close", onClick: () => side("panel", false), "aria-label": t.closePanel, title: t.closePanel, children: /* @__PURE__ */ jsx8(X5, { "aria-hidden": true }) })
607
3312
  ] }) }),
608
- /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-panel-body", children: [
609
- tab === "outline" && (outline.length ? /* @__PURE__ */ jsx6("nav", { className: "w100n-docs-outline", children: outline.map((item) => /* @__PURE__ */ jsx6("button", { type: "button", "data-level": item.level, onClick: () => document.getElementById(item.id)?.scrollIntoView({ behavior: "smooth", block: "start" }), children: item.text }, item.id)) }) : /* @__PURE__ */ jsx6("p", { className: "w100n-docs-muted", children: t.noOutline })),
610
- tab === "versions" && /* @__PURE__ */ jsxs4("div", { className: "w100n-docs-versions", children: [
3313
+ /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-panel-body", role: "tabpanel", "aria-label": panelTabs.find(([key]) => key === tab)?.[2], children: [
3314
+ tab === "outline" && (outline.length ? /* @__PURE__ */ jsx8("nav", { className: "w100n-docs-outline", children: outline.map((item) => /* @__PURE__ */ jsx8("button", { type: "button", "data-level": item.level, onClick: () => item.go ? item.go() : document.getElementById(item.id)?.scrollIntoView({ behavior: "smooth", block: "start" }), children: item.text }, item.id)) }) : /* @__PURE__ */ jsx8("p", { className: "w100n-docs-muted", children: t.noOutline })),
3315
+ tab === "versions" && /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-versions", children: [
611
3316
  versions.error && failed(versions.retry),
612
- (versions.data ?? []).map((item) => /* @__PURE__ */ jsxs4(
3317
+ (versions.data ?? []).map((item) => /* @__PURE__ */ jsxs8(
613
3318
  "button",
614
3319
  {
615
3320
  type: "button",
616
3321
  "aria-pressed": (versionId ?? "current") === item.id || !versionId && item.current,
617
- onClick: () => {
618
- setRestoring("idle");
619
- setVersion({ documentId: here.documentId, id: item.current ? null : item.id });
620
- },
3322
+ onClick: () => view(item.current ? null : item.id),
621
3323
  children: [
622
- /* @__PURE__ */ jsxs4("b", { children: [
3324
+ /* @__PURE__ */ jsxs8("b", { children: [
623
3325
  date(item.createdAt),
624
- item.current && /* @__PURE__ */ jsx6("span", { className: "w100n-docs-badge", children: t.current })
3326
+ item.current && /* @__PURE__ */ jsx8("span", { className: "w100n-docs-badge", children: t.current })
625
3327
  ] }),
626
- /* @__PURE__ */ jsx6("small", { children: [item.author?.name, size(item.size), item.note].filter(Boolean).join(" \xB7 ") })
3328
+ /* @__PURE__ */ jsx8("small", { children: [item.author?.name, size(item.size), item.note].filter(Boolean).join(" \xB7 ") })
627
3329
  ]
628
3330
  },
629
3331
  item.id
630
3332
  ))
631
3333
  ] }),
632
- tab === "details" && /* @__PURE__ */ jsxs4("dl", { className: "w100n-docs-details", children: [
633
- /* @__PURE__ */ jsx6("dt", { children: t.kind }),
634
- /* @__PURE__ */ jsx6("dd", { children: t.kinds[doc.data.kind] }),
635
- /* @__PURE__ */ jsx6("dt", { children: t.size }),
636
- /* @__PURE__ */ jsx6("dd", { children: size(doc.data.size) }),
637
- /* @__PURE__ */ jsx6("dt", { children: t.updated }),
638
- /* @__PURE__ */ jsx6("dd", { children: date(doc.data.updatedAt) }),
639
- doc.data.updatedBy && /* @__PURE__ */ jsxs4(Fragment4, { children: [
640
- /* @__PURE__ */ jsx6("dt", { children: t.by }),
641
- /* @__PURE__ */ jsx6("dd", { children: doc.data.updatedBy.name })
3334
+ tab === "details" && /* @__PURE__ */ jsxs8("dl", { className: "w100n-docs-details", children: [
3335
+ /* @__PURE__ */ jsx8("dt", { children: t.kind }),
3336
+ /* @__PURE__ */ jsx8("dd", { children: t.kinds[doc.data.kind] }),
3337
+ /* @__PURE__ */ jsx8("dt", { children: t.size }),
3338
+ /* @__PURE__ */ jsx8("dd", { children: size(doc.data.size) }),
3339
+ /* @__PURE__ */ jsx8("dt", { children: t.updated }),
3340
+ /* @__PURE__ */ jsx8("dd", { children: date(doc.data.updatedAt) }),
3341
+ doc.data.updatedBy && /* @__PURE__ */ jsxs8(Fragment7, { children: [
3342
+ /* @__PURE__ */ jsx8("dt", { children: t.by }),
3343
+ /* @__PURE__ */ jsx8("dd", { children: doc.data.updatedBy.name })
3344
+ ] }),
3345
+ /* @__PURE__ */ jsx8("dt", { children: t.rights }),
3346
+ /* @__PURE__ */ jsx8("dd", { children: [doc.data.rights.view && t.view, doc.data.rights.comment && t.comment, doc.data.rights.edit && t.edit].filter(Boolean).join(", ") })
3347
+ ] }),
3348
+ tab === "details" && host.exportAs && !!doc.data.exports?.length && /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-about", children: [
3349
+ /* @__PURE__ */ jsxs8("p", { className: "w100n-docs-caption", children: [
3350
+ /* @__PURE__ */ jsx8(FileDown, { "aria-hidden": true }),
3351
+ t.exportAs
642
3352
  ] }),
643
- /* @__PURE__ */ jsx6("dt", { children: t.rights }),
644
- /* @__PURE__ */ jsx6("dd", { children: [doc.data.rights.view && t.view, doc.data.rights.comment && t.comment, doc.data.rights.edit && t.edit].filter(Boolean).join(", ") })
3353
+ /* @__PURE__ */ jsx8("div", { className: "w100n-docs-chips", children: doc.data.exports.map((format) => /* @__PURE__ */ jsxs8(
3354
+ "button",
3355
+ {
3356
+ type: "button",
3357
+ className: "w100n-docs-action",
3358
+ disabled: exporting === "busy",
3359
+ onClick: () => void exportAs(doc.data, format),
3360
+ children: [
3361
+ /* @__PURE__ */ jsx8(FileDown, { "aria-hidden": true }),
3362
+ format.toUpperCase()
3363
+ ]
3364
+ },
3365
+ format
3366
+ )) }),
3367
+ exporting === "failed" && /* @__PURE__ */ jsx8("p", { className: "w100n-docs-muted", children: t.exportFailed })
3368
+ ] }),
3369
+ tab === "comments" && /* @__PURE__ */ jsx8("div", { ref: setBox }),
3370
+ tab === "sharing" && sharing && /* @__PURE__ */ jsx8(Sharing, { host, doc: doc.data, t, language, team: names2.get(doc.data.spaceId) }),
3371
+ tab === "signatures" && /* @__PURE__ */ jsxs8("div", { className: "w100n-docs-versions", children: [
3372
+ signatures.error && failed(signatures.retry),
3373
+ signatures.data?.length === 0 && /* @__PURE__ */ jsx8("p", { className: "w100n-docs-muted", children: t.noSignatures }),
3374
+ (signatures.data ?? []).map((entry) => {
3375
+ const signed = versions.data?.find((v) => v.id === entry.versionId);
3376
+ return /* @__PURE__ */ jsxs8(
3377
+ "button",
3378
+ {
3379
+ type: "button",
3380
+ disabled: !signed,
3381
+ "aria-pressed": !!signed && (versionId ?? versions.data?.find((v) => v.current)?.id) === signed.id,
3382
+ onClick: () => view(signed.current ? null : signed.id),
3383
+ children: [
3384
+ /* @__PURE__ */ jsxs8("b", { children: [
3385
+ entry.signer.name,
3386
+ /* @__PURE__ */ jsxs8("span", { className: "w100n-docs-badge", children: [
3387
+ t.page,
3388
+ " ",
3389
+ entry.page
3390
+ ] })
3391
+ ] }),
3392
+ /* @__PURE__ */ jsx8("small", { children: [date(entry.signedAt), t.methods[entry.method], signed && (signed.current ? t.current : `${t.version} ${date(signed.createdAt)}`)].filter(Boolean).join(" \xB7 ") })
3393
+ ]
3394
+ },
3395
+ entry.id
3396
+ );
3397
+ })
645
3398
  ] }),
646
3399
  aside?.(doc.data)
647
3400
  ] })
648
3401
  ] })
3402
+ ] }),
3403
+ dialog
3404
+ ] });
3405
+ }
3406
+
3407
+ // src/GuestDocument.tsx
3408
+ import { lazy as lazy3, Suspense as Suspense3, useRef as useRef7, useState as useState9 } from "react";
3409
+ import { Ban as Ban2, Download as Download3, Eye as Eye2, FileDown as FileDown2, FileSpreadsheet as FileSpreadsheet3, FileText as FileText3, FileType as FileType3, Files as Files3, Lock as Lock2, Presentation as Presentation3, UserRound } from "lucide-react";
3410
+ import { Fragment as Fragment8, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3411
+ var TextEditor3 = lazy3(() => import("./TextEditor-4YETNX4T.js"));
3412
+ var SheetView3 = lazy3(() => import("./SheetView-TXHS5WJC.js"));
3413
+ var icons3 = { docx: FileText3, markdown: FileType3, pdf: Files3, sheet: FileSpreadsheet3, slides: Presentation3 };
3414
+ var GuestRefused = class extends Error {
3415
+ constructor(reason, file = "", sharedBy = "") {
3416
+ super(reason);
3417
+ this.reason = reason;
3418
+ this.file = file;
3419
+ this.sharedBy = sharedBy;
3420
+ }
3421
+ };
3422
+ var remembered = () => {
3423
+ try {
3424
+ return localStorage.getItem("w100n-guest-name") ?? "";
3425
+ } catch {
3426
+ return "";
3427
+ }
3428
+ };
3429
+ var remember = (name) => {
3430
+ try {
3431
+ localStorage.setItem("w100n-guest-name", name);
3432
+ } catch {
3433
+ return;
3434
+ }
3435
+ };
3436
+ function GuestDocument({ host, language = "en", theme = "light", brand }) {
3437
+ const t = texts[language];
3438
+ const [ask, setAsk] = useState9(() => ({ name: remembered() || void 0 }));
3439
+ const [typed, setTyped] = useState9("");
3440
+ const [name, setName] = useState9(remembered);
3441
+ const named = useRef7(name);
3442
+ const [slot, setSlot] = useState9(null);
3443
+ const [saving, setSaving] = useState9("idle");
3444
+ const opened = useLoad((signal) => host.open(ask, { signal }), [host, ask]);
3445
+ const [notes] = useState9(() => ({
3446
+ me: { id: "", name: "" },
3447
+ spaces: async () => [],
3448
+ documents: async () => [],
3449
+ document: () => Promise.reject(new Error("A guest opens one document")),
3450
+ versions: async () => [],
3451
+ content: () => Promise.reject(new Error("A guest opens one document")),
3452
+ comments: (_id, opts) => host.comments(opts),
3453
+ comment: (_id, entry) => named.current.trim() ? host.comment({ ...entry, name: named.current.trim() }) : Promise.reject(new Error("A guest comments under a name"))
3454
+ }));
3455
+ const typeName = (value) => {
3456
+ named.current = value;
3457
+ setName(value);
3458
+ remember(value.trim());
3459
+ };
3460
+ const download = async (format) => {
3461
+ setSaving("busy");
3462
+ try {
3463
+ await host.download(format);
3464
+ setSaving("idle");
3465
+ } catch {
3466
+ setSaving("failed");
3467
+ }
3468
+ };
3469
+ const top = (file2, badge) => {
3470
+ const kind2 = file2 ? kindOf(file2.name, file2.mimeType) : null;
3471
+ const Kind = kind2 ? icons3[kind2] : FileText3;
3472
+ return /* @__PURE__ */ jsxs9("header", { className: "w100n-docs-bar w100n-guest-bar", children: [
3473
+ brand && /* @__PURE__ */ jsx9("span", { className: "w100n-guest-brand", children: brand }),
3474
+ file2 && /* @__PURE__ */ jsxs9(Fragment8, { children: [
3475
+ /* @__PURE__ */ jsx9("span", { className: "w100n-docs-tile", "data-kind": kind2, children: /* @__PURE__ */ jsx9(Kind, { "aria-hidden": true }) }),
3476
+ /* @__PURE__ */ jsxs9("div", { className: "w100n-docs-heading", children: [
3477
+ /* @__PURE__ */ jsx9("strong", { className: "w100n-docs-title", children: file2.name }),
3478
+ file2.sharedBy && /* @__PURE__ */ jsx9("span", { className: "w100n-docs-crumbs", children: t.sharedBy.replace("{name}", file2.sharedBy) })
3479
+ ] }),
3480
+ badge && /* @__PURE__ */ jsx9("span", { className: "w100n-docs-badge", "data-kind": kind2, children: badge }),
3481
+ file2.download && /* @__PURE__ */ jsxs9("div", { className: "w100n-docs-end", children: [
3482
+ (kind2 === "docx" || kind2 === "markdown" || kind2 === "sheet" || kind2 === "slides") && /* @__PURE__ */ jsxs9(
3483
+ "button",
3484
+ {
3485
+ type: "button",
3486
+ className: "w100n-docs-action",
3487
+ "data-tone": "slate",
3488
+ disabled: saving === "busy",
3489
+ onClick: () => void download("pdf"),
3490
+ "aria-label": t.pdfCopy,
3491
+ title: t.pdfCopy,
3492
+ children: [
3493
+ /* @__PURE__ */ jsx9(FileDown2, { "aria-hidden": true }),
3494
+ /* @__PURE__ */ jsx9("span", { children: t.pdfCopy })
3495
+ ]
3496
+ }
3497
+ ),
3498
+ /* @__PURE__ */ jsxs9("button", { type: "button", className: "w100n-docs-action", "data-tone": "blue", disabled: saving === "busy", onClick: () => void download("file"), "aria-label": t.download, title: t.download, children: [
3499
+ /* @__PURE__ */ jsx9(Download3, { "aria-hidden": true }),
3500
+ /* @__PURE__ */ jsx9("span", { children: t.download })
3501
+ ] })
3502
+ ] })
3503
+ ] })
3504
+ ] });
3505
+ };
3506
+ const gate = (tone, Icon, title, body, form) => /* @__PURE__ */ jsxs9("div", { className: "w100n-docs w100n-guest", "data-theme": theme, children: [
3507
+ top(null, null),
3508
+ /* @__PURE__ */ jsx9("div", { className: "w100n-guest-gate", children: /* @__PURE__ */ jsxs9("div", { className: "w100n-guest-card", children: [
3509
+ /* @__PURE__ */ jsx9("span", { className: "w100n-share-tile", "data-tone": tone, children: /* @__PURE__ */ jsx9(Icon, { "aria-hidden": true }) }),
3510
+ /* @__PURE__ */ jsx9("h2", { children: title }),
3511
+ /* @__PURE__ */ jsx9("p", { children: body }),
3512
+ form
3513
+ ] }) })
3514
+ ] });
3515
+ if (opened.error) {
3516
+ const refused = opened.error instanceof GuestRefused ? opened.error : null;
3517
+ if (refused?.reason === "ended") return gate("slate", Ban2, t.endedTitle, t.endedText);
3518
+ if (refused?.reason === "missing") return gate("slate", Ban2, t.missingTitle, t.missingText);
3519
+ if (refused?.reason === "password" || refused?.reason === "wrong" || refused?.reason === "name") {
3520
+ const password = refused.reason !== "name";
3521
+ const shared = refused.sharedBy ? `${t.sharedBy.replace("{name}", refused.sharedBy)}. ` : "";
3522
+ return gate(
3523
+ "violet",
3524
+ password ? Lock2 : UserRound,
3525
+ refused.file || t.documents,
3526
+ shared + (password ? t.passwordAsk : t.nameAsk),
3527
+ /* @__PURE__ */ jsxs9("form", { className: "w100n-guest-form", onSubmit: (e) => {
3528
+ e.preventDefault();
3529
+ if (!typed.trim()) return;
3530
+ if (password) setAsk({ ...ask, password: typed });
3531
+ else {
3532
+ typeName(typed);
3533
+ setAsk({ ...ask, name: typed.trim() });
3534
+ }
3535
+ setTyped("");
3536
+ }, children: [
3537
+ /* @__PURE__ */ jsx9(
3538
+ "input",
3539
+ {
3540
+ className: "w100n-share-input",
3541
+ type: password ? "password" : "text",
3542
+ autoFocus: true,
3543
+ value: typed,
3544
+ autoComplete: password ? "current-password" : "name",
3545
+ placeholder: password ? t.passwordPlaceholder : t.yourName,
3546
+ "aria-label": password ? t.passwordOn : t.yourName,
3547
+ onChange: (e) => setTyped(e.target.value)
3548
+ }
3549
+ ),
3550
+ refused.reason === "wrong" && /* @__PURE__ */ jsx9("p", { className: "w100n-docs-muted", role: "alert", children: t.wrongPassword }),
3551
+ /* @__PURE__ */ jsxs9("button", { type: "submit", className: "w100n-docs-action", disabled: !typed.trim(), children: [
3552
+ /* @__PURE__ */ jsx9(Eye2, { "aria-hidden": true }),
3553
+ t.openDoc
3554
+ ] })
3555
+ ] })
3556
+ );
3557
+ }
3558
+ if (refused?.reason === "busy") return gate("slate", Ban2, t.failed, t.tooMany);
3559
+ return gate("slate", Ban2, t.failed, "", /* @__PURE__ */ jsx9("button", { type: "button", className: "w100n-docs-action", onClick: opened.retry, children: t.retry }));
3560
+ }
3561
+ const file = opened.data;
3562
+ if (!file) return /* @__PURE__ */ jsxs9("div", { className: "w100n-docs w100n-guest", "data-theme": theme, children: [
3563
+ top(null, null),
3564
+ /* @__PURE__ */ jsx9("div", { className: "w100n-docs-empty", children: t.loading })
3565
+ ] });
3566
+ const kind = kindOf(file.name, file.mimeType);
3567
+ const text = kind === "docx" || kind === "markdown";
3568
+ const talk = text && file.comment;
3569
+ const rights = { view: true, comment: talk, edit: false };
3570
+ const quiet2 = () => {
3571
+ };
3572
+ let stage = /* @__PURE__ */ jsx9("div", { className: "w100n-docs-empty", children: t.unsupported });
3573
+ if (text) stage = /* @__PURE__ */ jsx9(Suspense3, { fallback: /* @__PURE__ */ jsx9("div", { className: "w100n-docs-empty", children: t.loading }), children: /* @__PURE__ */ jsx9(TextEditor3, { kind, bytes: file.bytes, t, language, rights, host: talk ? notes : void 0, documentId: file.id, onOutline: quiet2, slot: talk ? slot : null }) });
3574
+ else if (kind === "pdf") stage = /* @__PURE__ */ jsx9(PdfView, { bytes: file.bytes, t, language, worker: host.pdfWorker, rights, documentId: file.id, onOutline: quiet2 });
3575
+ else if (kind === "slides") stage = /* @__PURE__ */ jsx9(SlidesView, { html: new TextDecoder().decode(file.bytes), name: file.name, rights, language, theme, onOutline: quiet2 });
3576
+ else if (kind === "sheet") stage = /* @__PURE__ */ jsx9(Suspense3, { fallback: /* @__PURE__ */ jsx9("div", { className: "w100n-docs-empty", children: t.loading }), children: /* @__PURE__ */ jsx9(SheetView3, { bytes: file.bytes, name: file.name, kind: /\.csv$/i.test(file.name) || file.mimeType === "text/csv" ? "csv" : "xlsx", rights, language, theme, documentId: file.id }) });
3577
+ return /* @__PURE__ */ jsxs9("div", { className: "w100n-docs w100n-guest", "data-theme": theme, children: [
3578
+ top(file, talk ? t.badgeComment : t.badgeRead),
3579
+ saving === "failed" && /* @__PURE__ */ jsx9("p", { className: "w100n-docs-muted w100n-guest-note", role: "alert", children: t.exportFailed }),
3580
+ /* @__PURE__ */ jsxs9("div", { className: "w100n-docs-body w100n-guest-body", children: [
3581
+ /* @__PURE__ */ jsx9("main", { className: "w100n-docs-stage", children: /* @__PURE__ */ jsx9("div", { className: "w100n-docs-bare", children: stage }) }),
3582
+ talk && /* @__PURE__ */ jsx9("aside", { className: "w100n-docs-side w100n-docs-panel w100n-guest-panel", "aria-label": t.comments, children: /* @__PURE__ */ jsxs9("div", { className: "w100n-docs-panel-body", children: [
3583
+ /* @__PURE__ */ jsxs9("label", { className: "w100n-share-field", children: [
3584
+ /* @__PURE__ */ jsx9("span", { children: t.yourName }),
3585
+ /* @__PURE__ */ jsx9("input", { className: "w100n-share-input", value: name, autoComplete: "name", placeholder: t.yourName, onChange: (e) => typeName(e.target.value) })
3586
+ ] }),
3587
+ /* @__PURE__ */ jsx9("p", { className: "w100n-docs-muted", children: name.trim() ? t.nameHint : t.nameNeeded }),
3588
+ /* @__PURE__ */ jsx9("div", { ref: setSlot })
3589
+ ] }) })
649
3590
  ] })
650
3591
  ] });
651
3592
  }
652
3593
  export {
3594
+ DocumentOverlay,
653
3595
  DocumentsWorkspace,
3596
+ GuestDocument,
3597
+ GuestRefused,
3598
+ SlidesView,
654
3599
  kindOf,
655
3600
  kinds,
656
- matches
3601
+ matches,
3602
+ readDeck,
3603
+ readDocx,
3604
+ readable,
3605
+ writeDeck,
3606
+ writeDocx
657
3607
  };
658
3608
  //# sourceMappingURL=index.js.map