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