@zerohive/hive-viewer 0.2.7 → 1.0.1
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.
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +1372 -993
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1345 -987
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +309 -676
- package/package.json +60 -59
- package/dist/index.cjs +0 -1225
- package/dist/index.cjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
// src/components/DocumentViewer.tsx
|
|
2
|
-
import { useEffect as
|
|
2
|
+
import { useEffect as useEffect7, useState as useState7 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/editors/RichTextEditor.tsx
|
|
5
|
-
import html2canvas from "html2canvas";
|
|
6
5
|
import mammoth from "mammoth";
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
7
|
import MarkdownIt from "markdown-it";
|
|
8
|
-
import {
|
|
9
|
-
forwardRef,
|
|
10
|
-
useEffect,
|
|
11
|
-
useImperativeHandle,
|
|
12
|
-
useMemo,
|
|
13
|
-
useRef
|
|
14
|
-
} from "react";
|
|
15
8
|
|
|
16
9
|
// src/utils/sanitize.ts
|
|
17
10
|
import DOMPurify from "dompurify";
|
|
@@ -23,129 +16,796 @@ function sanitizeHtml(html) {
|
|
|
23
16
|
}
|
|
24
17
|
|
|
25
18
|
// src/editors/RichTextEditor.tsx
|
|
26
|
-
import { jsx
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (initialized.current) return;
|
|
41
|
-
(async () => {
|
|
42
|
-
if (props.mode === "create") {
|
|
43
|
-
editorRef.current.innerHTML = "<p><br/></p>";
|
|
44
|
-
initialized.current = true;
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
if (!props.arrayBuffer) return;
|
|
48
|
-
if (props.fileType === "docx") {
|
|
49
|
-
const res = await mammoth.convertToHtml({
|
|
19
|
+
import { jsx } from "react/jsx-runtime";
|
|
20
|
+
function RichTextEditor(props) {
|
|
21
|
+
const editorRef = useRef(null);
|
|
22
|
+
const [contentHtml, setContentHtml] = useState("");
|
|
23
|
+
const [loading, setLoading] = useState(false);
|
|
24
|
+
const mdParser = useRef(new MarkdownIt({ html: true, linkify: true }));
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const loadDoc = async () => {
|
|
27
|
+
if (!props.arrayBuffer) return;
|
|
28
|
+
setLoading(true);
|
|
29
|
+
try {
|
|
30
|
+
if (props.fileName.endsWith(".docx") || props.fileType === "docx") {
|
|
31
|
+
const result = await mammoth.convertToHtml({
|
|
50
32
|
arrayBuffer: props.arrayBuffer
|
|
51
33
|
});
|
|
52
|
-
|
|
53
|
-
res.value || "<p><br/></p>"
|
|
54
|
-
);
|
|
34
|
+
setContentHtml(sanitizeHtml(result.value));
|
|
55
35
|
} else {
|
|
56
|
-
const
|
|
57
|
-
|
|
36
|
+
const decoder = new TextDecoder("utf-8");
|
|
37
|
+
const text = decoder.decode(props.arrayBuffer);
|
|
38
|
+
if (props.fileName.endsWith(".md") || props.fileType === "md") {
|
|
39
|
+
const html = mdParser.current.render(text);
|
|
40
|
+
setContentHtml(sanitizeHtml(html));
|
|
41
|
+
} else {
|
|
42
|
+
setContentHtml(
|
|
43
|
+
`<pre style="white-space: pre-wrap; font-family: monospace;">${text}</pre>`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
58
46
|
}
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const el = scrollerRef.current;
|
|
64
|
-
if (!el) return;
|
|
65
|
-
const recompute = () => props.onPageCount(Math.max(1, Math.ceil(el.scrollHeight / PAGE_H)));
|
|
66
|
-
recompute();
|
|
67
|
-
const ro = new ResizeObserver(recompute);
|
|
68
|
-
ro.observe(el);
|
|
69
|
-
return () => ro.disconnect();
|
|
70
|
-
}, [props.headerFooterEnabled]);
|
|
71
|
-
function exec(cmd) {
|
|
72
|
-
if (readOnly) return;
|
|
73
|
-
document.execCommand(cmd);
|
|
74
|
-
editorRef.current?.focus();
|
|
75
|
-
}
|
|
76
|
-
function onClickPage(e) {
|
|
77
|
-
if (!props.armedSignatureUrl) return;
|
|
78
|
-
const scroller = scrollerRef.current;
|
|
79
|
-
if (!scroller) return;
|
|
80
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
81
|
-
const absY = scroller.scrollTop + (e.clientY - rect.top);
|
|
82
|
-
const page = Math.floor(absY / PAGE_H) + 1;
|
|
83
|
-
props.onPlaceSignature({
|
|
84
|
-
page,
|
|
85
|
-
x: (e.clientX - rect.left) / rect.width,
|
|
86
|
-
y: absY % PAGE_H / PAGE_H,
|
|
87
|
-
w: 0.25,
|
|
88
|
-
h: 0.1
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
async function requestThumbnail(index) {
|
|
92
|
-
const scroller = scrollerRef.current;
|
|
93
|
-
const capture = captureRef.current;
|
|
94
|
-
if (!scroller || !capture) return;
|
|
95
|
-
const old = scroller.scrollTop;
|
|
96
|
-
scroller.scrollTop = index * PAGE_H;
|
|
97
|
-
await new Promise((r) => requestAnimationFrame(r));
|
|
98
|
-
try {
|
|
99
|
-
const canvas = await html2canvas(capture, {
|
|
100
|
-
scale: 0.25,
|
|
101
|
-
useCORS: true
|
|
102
|
-
});
|
|
103
|
-
return canvas.toDataURL("image/png");
|
|
47
|
+
props.onPageCount(1);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error("Doc Conversion failed", err);
|
|
50
|
+
setContentHtml("<p style='color:red'>Error parsing document.</p>");
|
|
104
51
|
} finally {
|
|
105
|
-
|
|
52
|
+
setLoading(false);
|
|
106
53
|
}
|
|
54
|
+
};
|
|
55
|
+
loadDoc();
|
|
56
|
+
}, [props.arrayBuffer, props.fileName, props.fileType]);
|
|
57
|
+
return /* @__PURE__ */ jsx("div", { className: "hv-view-single", children: /* @__PURE__ */ jsx("div", { className: "hv-page-container", children: loading ? /* @__PURE__ */ jsx("div", { className: "p-12 text-center text-gray-400", children: "Processing text..." }) : /* @__PURE__ */ jsx(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
ref: editorRef,
|
|
61
|
+
className: "hv-docx-content",
|
|
62
|
+
contentEditable: props.mode === "edit" || props.mode === "create",
|
|
63
|
+
dangerouslySetInnerHTML: { __html: contentHtml },
|
|
64
|
+
suppressContentEditableWarning: true
|
|
107
65
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
66
|
+
) }) });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/editors/SpreadsheetEditor.tsx
|
|
70
|
+
import {
|
|
71
|
+
forwardRef,
|
|
72
|
+
useEffect as useEffect2,
|
|
73
|
+
useImperativeHandle,
|
|
74
|
+
useState as useState2
|
|
75
|
+
} from "react";
|
|
76
|
+
import * as XLSX from "xlsx";
|
|
77
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
78
|
+
var SpreadsheetEditor = forwardRef(function SpreadsheetEditor2(props, ref) {
|
|
79
|
+
const readonly = props.mode === "view";
|
|
80
|
+
const [data, setData] = useState2([]);
|
|
81
|
+
const [cols, setCols] = useState2([]);
|
|
82
|
+
useEffect2(() => {
|
|
83
|
+
if (!props.arrayBuffer) return;
|
|
84
|
+
try {
|
|
85
|
+
const wb = XLSX.read(props.arrayBuffer, { type: "array" });
|
|
86
|
+
const wsName = wb.SheetNames[0];
|
|
87
|
+
const ws = wb.Sheets[wsName];
|
|
88
|
+
const jsonData = XLSX.utils.sheet_to_json(ws, {
|
|
89
|
+
header: 1,
|
|
90
|
+
defval: ""
|
|
91
|
+
});
|
|
92
|
+
const minRows = 40;
|
|
93
|
+
const minCols = 15;
|
|
94
|
+
const rows = Math.max(minRows, jsonData.length);
|
|
95
|
+
const colCount = Math.max(minCols, jsonData[0]?.length || 0);
|
|
96
|
+
const normalized = Array.from({ length: rows }, (_, r) => {
|
|
97
|
+
const row = jsonData[r] || [];
|
|
98
|
+
return Array.from(
|
|
99
|
+
{ length: colCount },
|
|
100
|
+
(_2, c) => row[c] !== void 0 && row[c] !== null ? String(row[c]) : ""
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
setData(normalized);
|
|
104
|
+
const headers = Array.from({ length: colCount }, (_, i) => {
|
|
105
|
+
let letter = "";
|
|
106
|
+
let temp = i;
|
|
107
|
+
while (temp >= 0) {
|
|
108
|
+
letter = String.fromCharCode(temp % 26 + 65) + letter;
|
|
109
|
+
temp = Math.floor(temp / 26) - 1;
|
|
110
|
+
}
|
|
111
|
+
return letter;
|
|
117
112
|
});
|
|
113
|
+
setCols(headers);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.error("Spreadsheet load error:", e);
|
|
116
|
+
}
|
|
117
|
+
}, [props.arrayBuffer]);
|
|
118
|
+
async function save(exportPdf) {
|
|
119
|
+
if (!props.onSave) return;
|
|
120
|
+
const ws = XLSX.utils.aoa_to_sheet(data);
|
|
121
|
+
const wb = XLSX.utils.book_new();
|
|
122
|
+
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
|
123
|
+
const out = XLSX.write(wb, { type: "array", bookType: "xlsx" });
|
|
124
|
+
const bytes = new Uint8Array(out);
|
|
125
|
+
let binary = "";
|
|
126
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
127
|
+
binary += String.fromCharCode(bytes[i]);
|
|
118
128
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
props.onSave(btoa(binary), {
|
|
130
|
+
fileName: props.fileName,
|
|
131
|
+
fileType: "xlsx",
|
|
132
|
+
exportedAsPdf: !!exportPdf
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
useImperativeHandle(ref, () => ({ save }));
|
|
136
|
+
return /* @__PURE__ */ jsx2("div", { className: "hv-view-single", children: /* @__PURE__ */ jsxs(
|
|
137
|
+
"div",
|
|
138
|
+
{
|
|
139
|
+
className: "hv-page-container",
|
|
140
|
+
style: {
|
|
141
|
+
width: "auto",
|
|
142
|
+
maxWidth: "95vw",
|
|
143
|
+
minWidth: "800px",
|
|
144
|
+
padding: 0,
|
|
145
|
+
overflow: "hidden",
|
|
146
|
+
display: "flex",
|
|
147
|
+
flexDirection: "column"
|
|
148
|
+
},
|
|
149
|
+
children: [
|
|
150
|
+
/* @__PURE__ */ jsxs(
|
|
151
|
+
"div",
|
|
152
|
+
{
|
|
153
|
+
style: {
|
|
154
|
+
background: "#f8f9fa",
|
|
155
|
+
borderBottom: "1px solid #e2e8f0",
|
|
156
|
+
padding: "8px 16px",
|
|
157
|
+
fontSize: "12px",
|
|
158
|
+
color: "#64748b",
|
|
159
|
+
display: "flex",
|
|
160
|
+
gap: "12px",
|
|
161
|
+
flexShrink: 0
|
|
162
|
+
},
|
|
163
|
+
children: [
|
|
164
|
+
/* @__PURE__ */ jsx2("span", { children: "fx" }),
|
|
165
|
+
/* @__PURE__ */ jsx2(
|
|
166
|
+
"div",
|
|
167
|
+
{
|
|
168
|
+
style: {
|
|
169
|
+
background: "white",
|
|
170
|
+
border: "1px solid #cbd5e1",
|
|
171
|
+
flex: 1,
|
|
172
|
+
height: "18px",
|
|
173
|
+
borderRadius: "2px"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
/* @__PURE__ */ jsx2(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
style: {
|
|
184
|
+
overflow: "auto",
|
|
185
|
+
// FIX: Dynamic height ensures bottom isn't cut off on small screens
|
|
186
|
+
maxHeight: "calc(100vh - 140px)",
|
|
187
|
+
minHeight: "400px",
|
|
188
|
+
position: "relative",
|
|
189
|
+
// FIX: Padding prevents border clipping (first row/col edges)
|
|
190
|
+
padding: "1px"
|
|
191
|
+
},
|
|
192
|
+
children: /* @__PURE__ */ jsxs(
|
|
193
|
+
"table",
|
|
194
|
+
{
|
|
195
|
+
style: {
|
|
196
|
+
borderCollapse: "collapse",
|
|
197
|
+
minWidth: "100%",
|
|
198
|
+
tableLayout: "fixed"
|
|
199
|
+
},
|
|
200
|
+
children: [
|
|
201
|
+
/* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
202
|
+
/* @__PURE__ */ jsx2(
|
|
203
|
+
"th",
|
|
204
|
+
{
|
|
205
|
+
style: {
|
|
206
|
+
width: "40px",
|
|
207
|
+
background: "#f1f5f9",
|
|
208
|
+
border: "1px solid #cbd5e1",
|
|
209
|
+
position: "sticky",
|
|
210
|
+
top: 0,
|
|
211
|
+
left: 0,
|
|
212
|
+
zIndex: 30
|
|
213
|
+
// Increased zIndex
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
cols.map((col, i) => /* @__PURE__ */ jsx2(
|
|
218
|
+
"th",
|
|
219
|
+
{
|
|
220
|
+
style: {
|
|
221
|
+
minWidth: "100px",
|
|
222
|
+
background: "#f1f5f9",
|
|
223
|
+
border: "1px solid #cbd5e1",
|
|
224
|
+
color: "#475569",
|
|
225
|
+
fontSize: "11px",
|
|
226
|
+
fontWeight: 600,
|
|
227
|
+
padding: "4px",
|
|
228
|
+
position: "sticky",
|
|
229
|
+
top: 0,
|
|
230
|
+
zIndex: 20
|
|
231
|
+
// Increased zIndex
|
|
232
|
+
},
|
|
233
|
+
children: col
|
|
234
|
+
},
|
|
235
|
+
i
|
|
236
|
+
))
|
|
237
|
+
] }) }),
|
|
238
|
+
/* @__PURE__ */ jsx2("tbody", { children: data.map((row, r) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
239
|
+
/* @__PURE__ */ jsx2(
|
|
240
|
+
"td",
|
|
241
|
+
{
|
|
242
|
+
style: {
|
|
243
|
+
background: "#f1f5f9",
|
|
244
|
+
border: "1px solid #cbd5e1",
|
|
245
|
+
textAlign: "center",
|
|
246
|
+
fontSize: "11px",
|
|
247
|
+
color: "#64748b",
|
|
248
|
+
position: "sticky",
|
|
249
|
+
left: 0,
|
|
250
|
+
zIndex: 20
|
|
251
|
+
// Increased zIndex to stay above cells
|
|
252
|
+
},
|
|
253
|
+
children: r + 1
|
|
254
|
+
}
|
|
255
|
+
),
|
|
256
|
+
row.map((cell, c) => /* @__PURE__ */ jsx2(
|
|
257
|
+
"td",
|
|
258
|
+
{
|
|
259
|
+
contentEditable: !readonly,
|
|
260
|
+
suppressContentEditableWarning: true,
|
|
261
|
+
onBlur: (e) => {
|
|
262
|
+
const val = e.currentTarget.innerText;
|
|
263
|
+
setData((prev) => {
|
|
264
|
+
const next = [...prev];
|
|
265
|
+
next[r] = [...next[r]];
|
|
266
|
+
next[r][c] = val;
|
|
267
|
+
return next;
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
style: {
|
|
271
|
+
border: "1px solid #e2e8f0",
|
|
272
|
+
padding: "4px 8px",
|
|
273
|
+
fontSize: "13px",
|
|
274
|
+
color: "#1e293b",
|
|
275
|
+
minWidth: "100px",
|
|
276
|
+
whiteSpace: "nowrap",
|
|
277
|
+
overflow: "hidden",
|
|
278
|
+
outline: "none",
|
|
279
|
+
zIndex: 1
|
|
280
|
+
},
|
|
281
|
+
children: cell
|
|
282
|
+
},
|
|
283
|
+
c
|
|
284
|
+
))
|
|
285
|
+
] }, r)) })
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
) });
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// src/renderers/ImageRenderer.tsx
|
|
297
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useState as useState3 } from "react";
|
|
298
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
299
|
+
function ImageRenderer({
|
|
300
|
+
arrayBuffer,
|
|
301
|
+
fileType,
|
|
302
|
+
fileName
|
|
303
|
+
}) {
|
|
304
|
+
const [zoom, setZoom] = useState3(1);
|
|
305
|
+
const url = useMemo2(() => {
|
|
306
|
+
if (!arrayBuffer) {
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
const mime = fileType === "svg" ? "image/svg+xml" : fileType === "png" ? "image/png" : "image/jpeg";
|
|
310
|
+
return URL.createObjectURL(new Blob([arrayBuffer], { type: mime }));
|
|
311
|
+
}, [arrayBuffer, fileType]);
|
|
312
|
+
useEffect3(() => {
|
|
313
|
+
return () => {
|
|
314
|
+
if (url) {
|
|
315
|
+
URL.revokeObjectURL(url);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}, [url]);
|
|
319
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col h-full bg-gray-50", children: [
|
|
320
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between px-6 py-4 bg-white border-b border-gray-200", children: [
|
|
321
|
+
/* @__PURE__ */ jsx3("h2", { className: "text-sm font-medium text-gray-700 truncate max-w-md", children: fileName }),
|
|
322
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 bg-gray-100 rounded-lg p-1", children: [
|
|
323
|
+
/* @__PURE__ */ jsx3(
|
|
324
|
+
"button",
|
|
325
|
+
{
|
|
326
|
+
type: "button",
|
|
327
|
+
onClick: () => setZoom((z) => Math.max(0.25, z - 0.25)),
|
|
328
|
+
className: "w-9 h-9 flex items-center justify-center rounded-md bg-white hover:bg-gray-50 text-gray-700 transition-all shadow-sm hover:shadow",
|
|
329
|
+
"aria-label": "Zoom out",
|
|
330
|
+
children: /* @__PURE__ */ jsx3(
|
|
331
|
+
"svg",
|
|
332
|
+
{
|
|
333
|
+
className: "w-4 h-4",
|
|
334
|
+
fill: "none",
|
|
335
|
+
viewBox: "0 0 24 24",
|
|
336
|
+
stroke: "currentColor",
|
|
337
|
+
children: /* @__PURE__ */ jsx3(
|
|
338
|
+
"path",
|
|
339
|
+
{
|
|
340
|
+
strokeLinecap: "round",
|
|
341
|
+
strokeLinejoin: "round",
|
|
342
|
+
strokeWidth: 2,
|
|
343
|
+
d: "M20 12H4"
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
),
|
|
350
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-sm font-semibold text-gray-700 min-w-[3.5rem] text-center px-2", children: [
|
|
351
|
+
Math.round(zoom * 100),
|
|
352
|
+
"%"
|
|
353
|
+
] }),
|
|
354
|
+
/* @__PURE__ */ jsx3(
|
|
355
|
+
"button",
|
|
356
|
+
{
|
|
357
|
+
type: "button",
|
|
358
|
+
onClick: () => setZoom((z) => Math.min(4, z + 0.25)),
|
|
359
|
+
className: "w-9 h-9 flex items-center justify-center rounded-md bg-white hover:bg-gray-50 text-gray-700 transition-all shadow-sm hover:shadow",
|
|
360
|
+
"aria-label": "Zoom in",
|
|
361
|
+
children: /* @__PURE__ */ jsx3(
|
|
362
|
+
"svg",
|
|
363
|
+
{
|
|
364
|
+
className: "w-4 h-4",
|
|
365
|
+
fill: "none",
|
|
366
|
+
viewBox: "0 0 24 24",
|
|
367
|
+
stroke: "currentColor",
|
|
368
|
+
children: /* @__PURE__ */ jsx3(
|
|
369
|
+
"path",
|
|
370
|
+
{
|
|
371
|
+
strokeLinecap: "round",
|
|
372
|
+
strokeLinejoin: "round",
|
|
373
|
+
strokeWidth: 2,
|
|
374
|
+
d: "M12 4v16m8-8H4"
|
|
375
|
+
}
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
] })
|
|
382
|
+
] }),
|
|
383
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 overflow-auto flex items-center justify-center p-8", children: [
|
|
384
|
+
!arrayBuffer && /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
385
|
+
/* @__PURE__ */ jsx3("div", { className: "w-16 h-16 mx-auto mb-3 rounded-full bg-gray-200 flex items-center justify-center", children: /* @__PURE__ */ jsx3(
|
|
386
|
+
"svg",
|
|
387
|
+
{
|
|
388
|
+
className: "w-8 h-8 text-gray-400",
|
|
389
|
+
fill: "none",
|
|
390
|
+
viewBox: "0 0 24 24",
|
|
391
|
+
stroke: "currentColor",
|
|
392
|
+
children: /* @__PURE__ */ jsx3(
|
|
393
|
+
"path",
|
|
394
|
+
{
|
|
395
|
+
strokeLinecap: "round",
|
|
396
|
+
strokeLinejoin: "round",
|
|
397
|
+
strokeWidth: 2,
|
|
398
|
+
d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
399
|
+
}
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
) }),
|
|
403
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-500", children: "No image data provided" })
|
|
129
404
|
] }),
|
|
130
|
-
|
|
405
|
+
arrayBuffer && !url && /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
406
|
+
/* @__PURE__ */ jsx3("div", { className: "w-16 h-16 mx-auto mb-3 rounded-full bg-red-100 flex items-center justify-center", children: /* @__PURE__ */ jsx3(
|
|
407
|
+
"svg",
|
|
408
|
+
{
|
|
409
|
+
className: "w-8 h-8 text-red-500",
|
|
410
|
+
fill: "none",
|
|
411
|
+
viewBox: "0 0 24 24",
|
|
412
|
+
stroke: "currentColor",
|
|
413
|
+
children: /* @__PURE__ */ jsx3(
|
|
414
|
+
"path",
|
|
415
|
+
{
|
|
416
|
+
strokeLinecap: "round",
|
|
417
|
+
strokeLinejoin: "round",
|
|
418
|
+
strokeWidth: 2,
|
|
419
|
+
d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
420
|
+
}
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
) }),
|
|
424
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-600", children: "Failed to load image" })
|
|
425
|
+
] }),
|
|
426
|
+
url && /* @__PURE__ */ jsx3(
|
|
131
427
|
"div",
|
|
132
428
|
{
|
|
133
|
-
|
|
134
|
-
className:
|
|
135
|
-
|
|
136
|
-
|
|
429
|
+
style: { transform: `scale(${zoom})` },
|
|
430
|
+
className: "transition-transform duration-200 origin-center",
|
|
431
|
+
children: /* @__PURE__ */ jsx3(
|
|
432
|
+
"img",
|
|
433
|
+
{
|
|
434
|
+
src: url,
|
|
435
|
+
alt: fileName,
|
|
436
|
+
style: { transform: `scale(${zoom})` },
|
|
437
|
+
className: "max-w-full h-auto rounded-lg shadow-lg transition-transform duration-200"
|
|
438
|
+
}
|
|
439
|
+
)
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
] })
|
|
443
|
+
] });
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// src/renderers/PdfRenderer.tsx
|
|
447
|
+
import {
|
|
448
|
+
getDocument,
|
|
449
|
+
GlobalWorkerOptions
|
|
450
|
+
} from "pdfjs-dist";
|
|
451
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef2, useState as useState4 } from "react";
|
|
452
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
453
|
+
var PDF_WORKER_URL = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.10.38/pdf.worker.min.mjs";
|
|
454
|
+
function PdfRenderer(props) {
|
|
455
|
+
const { url, arrayBuffer, layout, currentPage } = props;
|
|
456
|
+
const [doc, setDoc] = useState4(null);
|
|
457
|
+
const [error, setError] = useState4(null);
|
|
458
|
+
useEffect4(() => {
|
|
459
|
+
if (!GlobalWorkerOptions.workerSrc) {
|
|
460
|
+
GlobalWorkerOptions.workerSrc = PDF_WORKER_URL;
|
|
461
|
+
}
|
|
462
|
+
let active = true;
|
|
463
|
+
const loadPdf = async () => {
|
|
464
|
+
if (!url && !arrayBuffer) return;
|
|
465
|
+
setError(null);
|
|
466
|
+
try {
|
|
467
|
+
const dataSource = arrayBuffer ? { data: arrayBuffer.slice(0) } : { url };
|
|
468
|
+
const loadingTask = getDocument(dataSource);
|
|
469
|
+
const pdf = await loadingTask.promise;
|
|
470
|
+
if (active) {
|
|
471
|
+
setDoc(pdf);
|
|
472
|
+
props.onPageCount(pdf.numPages);
|
|
473
|
+
generateThumbnails(pdf);
|
|
474
|
+
}
|
|
475
|
+
} catch (err) {
|
|
476
|
+
console.error("PDF Load Error:", err);
|
|
477
|
+
if (active) setError(err.message || "Failed to load PDF");
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
loadPdf();
|
|
481
|
+
return () => {
|
|
482
|
+
active = false;
|
|
483
|
+
};
|
|
484
|
+
}, [url, arrayBuffer]);
|
|
485
|
+
const generateThumbnails = async (pdf) => {
|
|
486
|
+
try {
|
|
487
|
+
const thumbs = [];
|
|
488
|
+
const num = Math.min(pdf.numPages, 5);
|
|
489
|
+
for (let i = 1; i <= num; i++) {
|
|
490
|
+
const page = await pdf.getPage(i);
|
|
491
|
+
const viewport = page.getViewport({ scale: 0.2 });
|
|
492
|
+
const canvas = document.createElement("canvas");
|
|
493
|
+
canvas.width = viewport.width;
|
|
494
|
+
canvas.height = viewport.height;
|
|
495
|
+
const ctx = canvas.getContext("2d");
|
|
496
|
+
if (ctx) {
|
|
497
|
+
await page.render({ canvasContext: ctx, viewport }).promise;
|
|
498
|
+
thumbs.push(canvas.toDataURL());
|
|
137
499
|
}
|
|
138
|
-
|
|
139
|
-
|
|
500
|
+
}
|
|
501
|
+
props.onThumbs(thumbs);
|
|
502
|
+
} catch (e) {
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const pagesToRender = useMemo3(() => {
|
|
506
|
+
if (!doc) return [];
|
|
507
|
+
const p = Math.max(1, Math.min(currentPage, doc.numPages));
|
|
508
|
+
if (layout === "side-by-side" && doc.numPages > 1) {
|
|
509
|
+
if (p === 1) return [1];
|
|
510
|
+
const left = p % 2 === 0 ? p : p - 1;
|
|
511
|
+
return left + 1 <= doc.numPages ? [left, left + 1] : [left];
|
|
512
|
+
}
|
|
513
|
+
return [p];
|
|
514
|
+
}, [doc, currentPage, layout]);
|
|
515
|
+
if (error) {
|
|
516
|
+
return /* @__PURE__ */ jsxs3(
|
|
517
|
+
"div",
|
|
518
|
+
{
|
|
519
|
+
className: "hv-page-container",
|
|
520
|
+
style: { padding: "32px", textAlign: "center", color: "#dc2626" },
|
|
521
|
+
children: [
|
|
522
|
+
/* @__PURE__ */ jsx4("strong", { children: "Error loading PDF" }),
|
|
523
|
+
/* @__PURE__ */ jsx4("p", { className: "text-sm mt-2", children: error })
|
|
524
|
+
]
|
|
525
|
+
}
|
|
526
|
+
);
|
|
140
527
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
528
|
+
return /* @__PURE__ */ jsx4(
|
|
529
|
+
"div",
|
|
530
|
+
{
|
|
531
|
+
className: `hv-doc-scroll ${layout === "side-by-side" ? "hv-view-double" : "hv-view-single"}`,
|
|
532
|
+
children: pagesToRender.map((page) => /* @__PURE__ */ jsx4(PdfPage, { doc, pageNum: page }, page))
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
function PdfPage({
|
|
537
|
+
doc,
|
|
538
|
+
pageNum
|
|
539
|
+
}) {
|
|
540
|
+
const canvasRef = useRef2(null);
|
|
541
|
+
useEffect4(() => {
|
|
542
|
+
if (!doc || !canvasRef.current) return;
|
|
543
|
+
let active = true;
|
|
544
|
+
const render = async () => {
|
|
545
|
+
try {
|
|
546
|
+
const page = await doc.getPage(pageNum);
|
|
547
|
+
if (!active) return;
|
|
548
|
+
const scale = 1.5;
|
|
549
|
+
const viewport = page.getViewport({ scale });
|
|
550
|
+
const canvas = canvasRef.current;
|
|
551
|
+
const ctx = canvas.getContext("2d");
|
|
552
|
+
if (!ctx) return;
|
|
553
|
+
canvas.width = viewport.width;
|
|
554
|
+
canvas.height = viewport.height;
|
|
555
|
+
canvas.style.width = `${viewport.width / scale}px`;
|
|
556
|
+
canvas.style.height = `${viewport.height / scale}px`;
|
|
557
|
+
await page.render({ canvasContext: ctx, viewport }).promise;
|
|
558
|
+
} catch (e) {
|
|
559
|
+
console.error("Page render error:", e);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
render();
|
|
563
|
+
return () => {
|
|
564
|
+
active = false;
|
|
565
|
+
};
|
|
566
|
+
}, [doc, pageNum]);
|
|
567
|
+
return /* @__PURE__ */ jsx4(
|
|
568
|
+
"div",
|
|
569
|
+
{
|
|
570
|
+
className: "hv-page-container",
|
|
571
|
+
style: {
|
|
572
|
+
minHeight: "600px",
|
|
573
|
+
display: "flex",
|
|
574
|
+
alignItems: "center",
|
|
575
|
+
justifyContent: "center"
|
|
576
|
+
},
|
|
577
|
+
children: /* @__PURE__ */ jsx4("canvas", { ref: canvasRef, className: "hv-pdf-canvas" })
|
|
578
|
+
}
|
|
579
|
+
);
|
|
144
580
|
}
|
|
145
581
|
|
|
146
|
-
// src/
|
|
147
|
-
import {
|
|
148
|
-
import
|
|
582
|
+
// src/renderers/PptxRenderer.tsx
|
|
583
|
+
import { useEffect as useEffect5, useMemo as useMemo4, useState as useState5 } from "react";
|
|
584
|
+
import JSZip from "jszip";
|
|
585
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
586
|
+
var NS_P = "http://schemas.openxmlformats.org/presentationml/2006/main";
|
|
587
|
+
var NS_A = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
588
|
+
function parseColor(solidFill) {
|
|
589
|
+
if (!solidFill) return void 0;
|
|
590
|
+
const srgbClr = solidFill.getElementsByTagNameNS(NS_A, "srgbClr")[0];
|
|
591
|
+
if (srgbClr) {
|
|
592
|
+
const val = srgbClr.getAttribute("val");
|
|
593
|
+
return val ? `#${val}` : void 0;
|
|
594
|
+
}
|
|
595
|
+
const schemeClr = solidFill.getElementsByTagNameNS(NS_A, "schemeClr")[0];
|
|
596
|
+
if (schemeClr) {
|
|
597
|
+
const val = schemeClr.getAttribute("val");
|
|
598
|
+
const colorMap = {
|
|
599
|
+
tx1: "#000000",
|
|
600
|
+
bg1: "#FFFFFF",
|
|
601
|
+
tx2: "#1F1F1F",
|
|
602
|
+
accent1: "#4472C4",
|
|
603
|
+
accent2: "#ED7D31",
|
|
604
|
+
accent3: "#A5A5A5",
|
|
605
|
+
accent4: "#FFC000",
|
|
606
|
+
accent5: "#5B9BD5",
|
|
607
|
+
accent6: "#70AD47"
|
|
608
|
+
};
|
|
609
|
+
return colorMap[val || ""] || "#000000";
|
|
610
|
+
}
|
|
611
|
+
return void 0;
|
|
612
|
+
}
|
|
613
|
+
function parseSlideXml(xml) {
|
|
614
|
+
const parser = new DOMParser();
|
|
615
|
+
const doc = parser.parseFromString(xml, "application/xml");
|
|
616
|
+
let title;
|
|
617
|
+
let titleColor;
|
|
618
|
+
let bgColor;
|
|
619
|
+
const body = [];
|
|
620
|
+
const bgElements = doc.getElementsByTagNameNS(NS_P, "bg");
|
|
621
|
+
if (bgElements.length > 0) {
|
|
622
|
+
const bgPr = bgElements[0].getElementsByTagNameNS(NS_P, "bgPr")[0];
|
|
623
|
+
if (bgPr) {
|
|
624
|
+
const solidFill = bgPr.getElementsByTagNameNS(NS_A, "solidFill")[0];
|
|
625
|
+
bgColor = parseColor(solidFill);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
const shapes = Array.from(doc.getElementsByTagNameNS(NS_P, "sp"));
|
|
629
|
+
shapes.forEach((shape) => {
|
|
630
|
+
const nvSpPr = shape.getElementsByTagNameNS(NS_P, "nvSpPr")[0];
|
|
631
|
+
const cNvPr = nvSpPr?.getElementsByTagNameNS(NS_P, "cNvPr")[0];
|
|
632
|
+
const name = cNvPr?.getAttribute("name")?.toLowerCase() || "";
|
|
633
|
+
const isTitle = name.includes("title") || name.includes("header");
|
|
634
|
+
const txBody = shape.getElementsByTagNameNS(NS_P, "txBody")[0];
|
|
635
|
+
if (!txBody) return;
|
|
636
|
+
const paragraphs = Array.from(txBody.getElementsByTagNameNS(NS_A, "p"));
|
|
637
|
+
paragraphs.forEach((p) => {
|
|
638
|
+
const runs = Array.from(p.getElementsByTagNameNS(NS_A, "r"));
|
|
639
|
+
runs.forEach((run) => {
|
|
640
|
+
const textEl = run.getElementsByTagNameNS(NS_A, "t")[0];
|
|
641
|
+
const text = textEl?.textContent?.trim() || "";
|
|
642
|
+
if (!text) return;
|
|
643
|
+
const rPr = run.getElementsByTagNameNS(NS_A, "rPr")[0];
|
|
644
|
+
let color;
|
|
645
|
+
let isBold = false;
|
|
646
|
+
let isItalic = false;
|
|
647
|
+
if (rPr) {
|
|
648
|
+
isBold = rPr.getAttribute("b") === "1";
|
|
649
|
+
isItalic = rPr.getAttribute("i") === "1";
|
|
650
|
+
color = parseColor(rPr.getElementsByTagNameNS(NS_A, "solidFill")[0]);
|
|
651
|
+
}
|
|
652
|
+
if (isTitle && !title) {
|
|
653
|
+
title = text;
|
|
654
|
+
titleColor = color;
|
|
655
|
+
} else {
|
|
656
|
+
body.push({ text, color, isBold, isItalic });
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
return { title, titleColor, body, bgColor };
|
|
662
|
+
}
|
|
663
|
+
function PptxRenderer(props) {
|
|
664
|
+
const [slides, setSlides] = useState5([]);
|
|
665
|
+
const [error, setError] = useState5(null);
|
|
666
|
+
useEffect5(() => {
|
|
667
|
+
if (!props.arrayBuffer) return;
|
|
668
|
+
const loadPptx = async () => {
|
|
669
|
+
try {
|
|
670
|
+
const zip = await JSZip.loadAsync(props.arrayBuffer);
|
|
671
|
+
const slidePaths = Object.keys(zip.files).filter((p) => /^ppt\/slides\/slide\d+\.xml$/.test(p)).sort((a, b) => {
|
|
672
|
+
const numA = parseInt(a.match(/\d+/)[0]);
|
|
673
|
+
const numB = parseInt(b.match(/\d+/)[0]);
|
|
674
|
+
return numA - numB;
|
|
675
|
+
});
|
|
676
|
+
const parsedSlides = [];
|
|
677
|
+
for (const path of slidePaths) {
|
|
678
|
+
const xml = await zip.files[path].async("string");
|
|
679
|
+
parsedSlides.push(parseSlideXml(xml));
|
|
680
|
+
}
|
|
681
|
+
setSlides(parsedSlides);
|
|
682
|
+
props.onPageCount(parsedSlides.length);
|
|
683
|
+
const thumbs = parsedSlides.map(
|
|
684
|
+
(_, i) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(
|
|
685
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="100" height="56"><rect width="100%" height="100%" fill="#4f46e5"/><text x="50%" y="50%" fill="white" font-size="20" text-anchor="middle" dy=".3em">${i + 1}</text></svg>`
|
|
686
|
+
)}`
|
|
687
|
+
);
|
|
688
|
+
props.onThumbs(thumbs);
|
|
689
|
+
} catch (err) {
|
|
690
|
+
setError(err.message || "Failed to parse PPTX");
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
loadPptx();
|
|
694
|
+
}, [props.arrayBuffer]);
|
|
695
|
+
const pagesToShow = useMemo4(() => {
|
|
696
|
+
if (slides.length === 0) return [];
|
|
697
|
+
const validPage = Math.max(1, Math.min(props.currentPage, slides.length));
|
|
698
|
+
if (props.layout === "side-by-side" && slides.length > 1) {
|
|
699
|
+
if (validPage === 1) return [1];
|
|
700
|
+
const left = validPage % 2 === 0 ? validPage : validPage - 1;
|
|
701
|
+
return [left, left + 1].filter((p) => p <= slides.length);
|
|
702
|
+
}
|
|
703
|
+
return [validPage];
|
|
704
|
+
}, [slides.length, props.currentPage, props.layout]);
|
|
705
|
+
if (error) return /* @__PURE__ */ jsx5("div", { className: "text-red-500 p-8 text-center", children: error });
|
|
706
|
+
return /* @__PURE__ */ jsx5(
|
|
707
|
+
"div",
|
|
708
|
+
{
|
|
709
|
+
className: props.layout === "side-by-side" ? "hv-view-double" : "hv-view-single",
|
|
710
|
+
children: pagesToShow.map((p) => {
|
|
711
|
+
const slide = slides[p - 1];
|
|
712
|
+
if (!slide) return null;
|
|
713
|
+
return /* @__PURE__ */ jsxs4(
|
|
714
|
+
"div",
|
|
715
|
+
{
|
|
716
|
+
className: "hv-page-container",
|
|
717
|
+
style: {
|
|
718
|
+
width: "960px",
|
|
719
|
+
// Standard HD slide width
|
|
720
|
+
aspectRatio: "16/9",
|
|
721
|
+
padding: "48px",
|
|
722
|
+
backgroundColor: slide.bgColor || "#ffffff",
|
|
723
|
+
display: "flex",
|
|
724
|
+
flexDirection: "column",
|
|
725
|
+
justifyContent: "center",
|
|
726
|
+
position: "relative"
|
|
727
|
+
// For absolute positioning if we added it later
|
|
728
|
+
},
|
|
729
|
+
children: [
|
|
730
|
+
slide.title && /* @__PURE__ */ jsx5(
|
|
731
|
+
"h1",
|
|
732
|
+
{
|
|
733
|
+
style: {
|
|
734
|
+
fontSize: "42px",
|
|
735
|
+
marginBottom: "32px",
|
|
736
|
+
fontWeight: "bold",
|
|
737
|
+
color: slide.titleColor || "#1a202c",
|
|
738
|
+
lineHeight: 1.2
|
|
739
|
+
},
|
|
740
|
+
children: slide.title
|
|
741
|
+
}
|
|
742
|
+
),
|
|
743
|
+
/* @__PURE__ */ jsx5(
|
|
744
|
+
"div",
|
|
745
|
+
{
|
|
746
|
+
style: { display: "flex", flexDirection: "column", gap: "16px" },
|
|
747
|
+
children: slide.body.map((item, i) => /* @__PURE__ */ jsxs4(
|
|
748
|
+
"div",
|
|
749
|
+
{
|
|
750
|
+
style: {
|
|
751
|
+
display: "flex",
|
|
752
|
+
alignItems: "flex-start",
|
|
753
|
+
gap: "12px"
|
|
754
|
+
},
|
|
755
|
+
children: [
|
|
756
|
+
/* @__PURE__ */ jsx5(
|
|
757
|
+
"div",
|
|
758
|
+
{
|
|
759
|
+
style: {
|
|
760
|
+
width: "8px",
|
|
761
|
+
height: "8px",
|
|
762
|
+
background: item.color || "#cbd5e1",
|
|
763
|
+
borderRadius: "50%",
|
|
764
|
+
marginTop: "12px",
|
|
765
|
+
flexShrink: 0
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
),
|
|
769
|
+
/* @__PURE__ */ jsx5(
|
|
770
|
+
"p",
|
|
771
|
+
{
|
|
772
|
+
style: {
|
|
773
|
+
fontSize: "24px",
|
|
774
|
+
color: item.color || "#4a5568",
|
|
775
|
+
fontWeight: item.isBold ? "bold" : "normal",
|
|
776
|
+
fontStyle: item.isItalic ? "italic" : "normal",
|
|
777
|
+
margin: 0
|
|
778
|
+
},
|
|
779
|
+
children: item.text
|
|
780
|
+
}
|
|
781
|
+
)
|
|
782
|
+
]
|
|
783
|
+
},
|
|
784
|
+
i
|
|
785
|
+
))
|
|
786
|
+
}
|
|
787
|
+
),
|
|
788
|
+
/* @__PURE__ */ jsx5(
|
|
789
|
+
"div",
|
|
790
|
+
{
|
|
791
|
+
style: {
|
|
792
|
+
position: "absolute",
|
|
793
|
+
bottom: "20px",
|
|
794
|
+
right: "30px",
|
|
795
|
+
color: "#cbd5e1",
|
|
796
|
+
fontWeight: "bold"
|
|
797
|
+
},
|
|
798
|
+
children: p
|
|
799
|
+
}
|
|
800
|
+
)
|
|
801
|
+
]
|
|
802
|
+
},
|
|
803
|
+
p
|
|
804
|
+
);
|
|
805
|
+
})
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
}
|
|
149
809
|
|
|
150
810
|
// src/utils/fileSource.ts
|
|
151
811
|
function guessFileType(name, explicit) {
|
|
@@ -166,15 +826,6 @@ function guessFileType(name, explicit) {
|
|
|
166
826
|
];
|
|
167
827
|
return allowed.includes(ext) ? ext : "txt";
|
|
168
828
|
}
|
|
169
|
-
function arrayBufferToBase64(buf) {
|
|
170
|
-
const bytes = new Uint8Array(buf);
|
|
171
|
-
let binary = "";
|
|
172
|
-
const chunk = 32768;
|
|
173
|
-
for (let i = 0; i < bytes.length; i += chunk) {
|
|
174
|
-
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
|
175
|
-
}
|
|
176
|
-
return btoa(binary);
|
|
177
|
-
}
|
|
178
829
|
async function base64ToArrayBuffer(b64) {
|
|
179
830
|
const bin = atob(b64);
|
|
180
831
|
const len = bin.length;
|
|
@@ -232,965 +883,672 @@ async function resolveSource(args) {
|
|
|
232
883
|
return { fileType, fileName, arrayBuffer: out.buffer, url: args.fileUrl };
|
|
233
884
|
}
|
|
234
885
|
|
|
235
|
-
// src/
|
|
236
|
-
import {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const ws = wb.Sheets[name];
|
|
248
|
-
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, raw: true });
|
|
249
|
-
const rows = Math.max(30, aoa.length);
|
|
250
|
-
const cols2 = Math.max(12, Math.max(...aoa.map((r) => r?.length ?? 0), 0));
|
|
251
|
-
const next = Array.from({ length: rows }, (_, r) => Array.from({ length: cols2 }, (_2, c) => {
|
|
252
|
-
const v = aoa[r]?.[c];
|
|
253
|
-
return v == null ? "" : String(v);
|
|
254
|
-
}));
|
|
255
|
-
setGrid(next);
|
|
256
|
-
} catch {
|
|
886
|
+
// src/components/ThumbnailsSidebar.tsx
|
|
887
|
+
import { useEffect as useEffect6, useRef as useRef3 } from "react";
|
|
888
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
889
|
+
function ThumbnailsSidebar(props) {
|
|
890
|
+
const { isOpen, thumbnails, currentPage, onSelectPage } = props;
|
|
891
|
+
const activeRef = useRef3(null);
|
|
892
|
+
useEffect6(() => {
|
|
893
|
+
if (activeRef.current) {
|
|
894
|
+
activeRef.current.scrollIntoView({
|
|
895
|
+
behavior: "smooth",
|
|
896
|
+
block: "nearest"
|
|
897
|
+
});
|
|
257
898
|
}
|
|
258
|
-
}, [
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const text = e.currentTarget.textContent ?? "";
|
|
293
|
-
setGrid((prev) => {
|
|
294
|
-
const next = prev.map((rr) => rr.slice());
|
|
295
|
-
next[r][c] = text;
|
|
296
|
-
return next;
|
|
297
|
-
});
|
|
298
|
-
},
|
|
299
|
-
children: val
|
|
300
|
-
},
|
|
301
|
-
c
|
|
302
|
-
))
|
|
303
|
-
] }, r))
|
|
304
|
-
] })
|
|
305
|
-
] });
|
|
306
|
-
});
|
|
307
|
-
function ensureExt(name, ext) {
|
|
308
|
-
const base = name.includes(".") ? name.slice(0, name.lastIndexOf(".")) : name;
|
|
309
|
-
return `${base}.${ext}`;
|
|
899
|
+
}, [currentPage, isOpen]);
|
|
900
|
+
return /* @__PURE__ */ jsx6("div", { className: `hv-sidebar ${!isOpen ? "collapsed" : ""}`, children: /* @__PURE__ */ jsxs5("div", { className: "hv-thumb-list", children: [
|
|
901
|
+
thumbnails.map((src, index) => {
|
|
902
|
+
const pageNum = index + 1;
|
|
903
|
+
const isActive = pageNum === currentPage;
|
|
904
|
+
return /* @__PURE__ */ jsxs5(
|
|
905
|
+
"div",
|
|
906
|
+
{
|
|
907
|
+
ref: isActive ? activeRef : null,
|
|
908
|
+
className: `hv-thumb-item ${isActive ? "active" : ""}`,
|
|
909
|
+
onClick: () => onSelectPage(pageNum),
|
|
910
|
+
children: [
|
|
911
|
+
/* @__PURE__ */ jsx6("div", { className: "hv-thumb-preview", children: src ? /* @__PURE__ */ jsx6(
|
|
912
|
+
"img",
|
|
913
|
+
{
|
|
914
|
+
src,
|
|
915
|
+
alt: `Page ${pageNum}`,
|
|
916
|
+
className: "hv-thumb-img"
|
|
917
|
+
}
|
|
918
|
+
) : (
|
|
919
|
+
// Skeleton loader state for thumbnail
|
|
920
|
+
/* @__PURE__ */ jsx6("div", { className: "w-full h-full bg-gray-100 animate-pulse flex items-center justify-center", children: /* @__PURE__ */ jsx6("span", { className: "text-xs text-gray-300", children: "..." }) })
|
|
921
|
+
) }),
|
|
922
|
+
/* @__PURE__ */ jsxs5("span", { className: "hv-thumb-label", children: [
|
|
923
|
+
"Page ",
|
|
924
|
+
pageNum
|
|
925
|
+
] })
|
|
926
|
+
]
|
|
927
|
+
},
|
|
928
|
+
pageNum
|
|
929
|
+
);
|
|
930
|
+
}),
|
|
931
|
+
thumbnails.length === 0 && /* @__PURE__ */ jsx6("div", { className: "text-center p-4 text-xs text-gray-400", children: "No previews available" })
|
|
932
|
+
] }) });
|
|
310
933
|
}
|
|
311
934
|
|
|
312
|
-
// src/
|
|
313
|
-
import {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
935
|
+
// src/components/Toolbar.tsx
|
|
936
|
+
import {
|
|
937
|
+
ChevronLeft,
|
|
938
|
+
ChevronRight,
|
|
939
|
+
Grid2X2,
|
|
940
|
+
LayoutTemplate,
|
|
941
|
+
PanelLeftClose,
|
|
942
|
+
PanelLeftOpen,
|
|
943
|
+
PenLine
|
|
944
|
+
} from "lucide-react";
|
|
945
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
946
|
+
function Toolbar(props) {
|
|
947
|
+
const {
|
|
948
|
+
fileName,
|
|
949
|
+
pageCount,
|
|
950
|
+
currentPage,
|
|
951
|
+
onPageChange,
|
|
952
|
+
layout,
|
|
953
|
+
onLayoutChange
|
|
954
|
+
} = props;
|
|
955
|
+
const handlePrev = () => {
|
|
956
|
+
if (currentPage > 1) onPageChange(currentPage - 1);
|
|
957
|
+
};
|
|
958
|
+
const handleNext = () => {
|
|
959
|
+
if (currentPage < pageCount) onPageChange(currentPage + 1);
|
|
960
|
+
};
|
|
961
|
+
const handleInput = (e) => {
|
|
962
|
+
const val = parseInt(e.target.value);
|
|
963
|
+
if (!isNaN(val) && val >= 1 && val <= pageCount) {
|
|
964
|
+
onPageChange(val);
|
|
324
965
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
/* @__PURE__ */
|
|
338
|
-
|
|
339
|
-
|
|
966
|
+
};
|
|
967
|
+
return /* @__PURE__ */ jsxs6("div", { className: "hv-toolbar", children: [
|
|
968
|
+
/* @__PURE__ */ jsxs6("div", { className: "hv-toolbar-group", children: [
|
|
969
|
+
/* @__PURE__ */ jsx7(
|
|
970
|
+
"button",
|
|
971
|
+
{
|
|
972
|
+
className: `hv-btn ${props.showThumbnails ? "hv-btn-active" : ""}`,
|
|
973
|
+
onClick: props.onToggleThumbnails,
|
|
974
|
+
title: "Toggle Thumbnails",
|
|
975
|
+
children: props.showThumbnails ? /* @__PURE__ */ jsx7(PanelLeftClose, { size: 20 }) : /* @__PURE__ */ jsx7(PanelLeftOpen, { size: 20 })
|
|
976
|
+
}
|
|
977
|
+
),
|
|
978
|
+
/* @__PURE__ */ jsx7(
|
|
979
|
+
"div",
|
|
980
|
+
{
|
|
981
|
+
className: "hv-sep",
|
|
982
|
+
style: {
|
|
983
|
+
width: 1,
|
|
984
|
+
height: 24,
|
|
985
|
+
background: "var(--hv-border)",
|
|
986
|
+
margin: "0 8px"
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
),
|
|
990
|
+
/* @__PURE__ */ jsx7(
|
|
991
|
+
"span",
|
|
992
|
+
{
|
|
993
|
+
style: {
|
|
994
|
+
fontWeight: 600,
|
|
995
|
+
fontSize: "14px",
|
|
996
|
+
maxWidth: 200,
|
|
997
|
+
overflow: "hidden",
|
|
998
|
+
textOverflow: "ellipsis",
|
|
999
|
+
whiteSpace: "nowrap"
|
|
1000
|
+
},
|
|
1001
|
+
children: fileName || "Document"
|
|
1002
|
+
}
|
|
1003
|
+
)
|
|
1004
|
+
] }),
|
|
1005
|
+
/* @__PURE__ */ jsxs6("div", { className: "hv-toolbar-group", children: [
|
|
1006
|
+
/* @__PURE__ */ jsx7(
|
|
340
1007
|
"button",
|
|
341
1008
|
{
|
|
342
|
-
type: "button",
|
|
343
1009
|
className: "hv-btn",
|
|
344
|
-
|
|
345
|
-
|
|
1010
|
+
disabled: currentPage <= 1,
|
|
1011
|
+
onClick: handlePrev,
|
|
1012
|
+
children: /* @__PURE__ */ jsx7(ChevronLeft, { size: 20 })
|
|
346
1013
|
}
|
|
347
1014
|
),
|
|
348
|
-
/* @__PURE__ */
|
|
349
|
-
|
|
350
|
-
|
|
1015
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-600", children: [
|
|
1016
|
+
/* @__PURE__ */ jsx7(
|
|
1017
|
+
"input",
|
|
1018
|
+
{
|
|
1019
|
+
type: "number",
|
|
1020
|
+
className: "w-12 text-center border rounded py-1 bg-gray-50 focus:bg-white focus:ring-2 focus:ring-indigo-500 outline-none transition-all",
|
|
1021
|
+
value: currentPage,
|
|
1022
|
+
onChange: handleInput,
|
|
1023
|
+
min: 1,
|
|
1024
|
+
max: pageCount
|
|
1025
|
+
}
|
|
1026
|
+
),
|
|
1027
|
+
/* @__PURE__ */ jsx7("span", { className: "text-gray-400", children: "/" }),
|
|
1028
|
+
/* @__PURE__ */ jsx7("span", { children: pageCount })
|
|
351
1029
|
] }),
|
|
352
|
-
/* @__PURE__ */
|
|
1030
|
+
/* @__PURE__ */ jsx7(
|
|
353
1031
|
"button",
|
|
354
1032
|
{
|
|
355
|
-
type: "button",
|
|
356
1033
|
className: "hv-btn",
|
|
357
|
-
|
|
358
|
-
|
|
1034
|
+
disabled: currentPage >= pageCount,
|
|
1035
|
+
onClick: handleNext,
|
|
1036
|
+
children: /* @__PURE__ */ jsx7(ChevronRight, { size: 20 })
|
|
359
1037
|
}
|
|
360
1038
|
)
|
|
361
1039
|
] }),
|
|
362
|
-
/* @__PURE__ */
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
url && /* @__PURE__ */ jsx3(
|
|
366
|
-
"img",
|
|
1040
|
+
/* @__PURE__ */ jsxs6("div", { className: "hv-toolbar-group", children: [
|
|
1041
|
+
/* @__PURE__ */ jsx7(
|
|
1042
|
+
"button",
|
|
367
1043
|
{
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
1044
|
+
className: `hv-btn ${layout === "single" ? "hv-btn-active text-indigo-600 bg-indigo-50" : ""}`,
|
|
1045
|
+
onClick: () => onLayoutChange("single"),
|
|
1046
|
+
title: "Single Page View",
|
|
1047
|
+
children: /* @__PURE__ */ jsx7(LayoutTemplate, { size: 18 })
|
|
1048
|
+
}
|
|
1049
|
+
),
|
|
1050
|
+
/* @__PURE__ */ jsx7(
|
|
1051
|
+
"button",
|
|
1052
|
+
{
|
|
1053
|
+
className: `hv-btn ${layout === "side-by-side" ? "hv-btn-active text-indigo-600 bg-indigo-50" : ""}`,
|
|
1054
|
+
onClick: () => onLayoutChange("side-by-side"),
|
|
1055
|
+
title: "Two Page View",
|
|
1056
|
+
children: /* @__PURE__ */ jsx7(Grid2X2, { size: 18 })
|
|
1057
|
+
}
|
|
1058
|
+
),
|
|
1059
|
+
/* @__PURE__ */ jsx7(
|
|
1060
|
+
"div",
|
|
1061
|
+
{
|
|
1062
|
+
className: "hv-sep",
|
|
1063
|
+
style: {
|
|
1064
|
+
width: 1,
|
|
1065
|
+
height: 24,
|
|
1066
|
+
background: "var(--hv-border)",
|
|
1067
|
+
margin: "0 8px"
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
),
|
|
1071
|
+
!props.disableSigning && /* @__PURE__ */ jsxs6(
|
|
1072
|
+
"button",
|
|
1073
|
+
{
|
|
1074
|
+
className: `hv-btn hv-btn-primary ${props.showSignatures ? "ring-2 ring-indigo-300" : ""}`,
|
|
1075
|
+
onClick: props.onToggleSignatures,
|
|
1076
|
+
title: "Sign Document",
|
|
1077
|
+
children: [
|
|
1078
|
+
/* @__PURE__ */ jsx7(PenLine, { size: 18, className: "mr-2" }),
|
|
1079
|
+
/* @__PURE__ */ jsx7("span", { className: "hidden sm:inline", children: "Sign" })
|
|
1080
|
+
]
|
|
372
1081
|
}
|
|
373
1082
|
)
|
|
374
1083
|
] })
|
|
375
1084
|
] });
|
|
376
1085
|
}
|
|
377
1086
|
|
|
378
|
-
// src/
|
|
379
|
-
import {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
);
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
const [
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
useEffect4(() => {
|
|
398
|
-
try {
|
|
399
|
-
GlobalWorkerOptions.workerSrc = new URL(
|
|
400
|
-
"pdfjs-dist/build/pdf.worker.min.mjs",
|
|
401
|
-
import.meta.url
|
|
402
|
-
).toString();
|
|
403
|
-
} catch {
|
|
404
|
-
}
|
|
405
|
-
}, []);
|
|
406
|
-
useEffect4(() => {
|
|
407
|
-
let cancel = false;
|
|
408
|
-
setError(null);
|
|
409
|
-
setLoading(true);
|
|
410
|
-
(async () => {
|
|
411
|
-
setDoc(null);
|
|
412
|
-
setRendered(/* @__PURE__ */ new Map());
|
|
413
|
-
setThumbs([]);
|
|
414
|
-
if (!url && !arrayBuffer) {
|
|
415
|
-
setError("No PDF source provided.");
|
|
416
|
-
setLoading(false);
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
1087
|
+
// src/components/SignaturePanel.tsx
|
|
1088
|
+
import { Plus, Trash2, X } from "lucide-react";
|
|
1089
|
+
import { useState as useState6, useRef as useRef4 } from "react";
|
|
1090
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1091
|
+
function SignaturePanel(props) {
|
|
1092
|
+
const {
|
|
1093
|
+
isOpen,
|
|
1094
|
+
onClose,
|
|
1095
|
+
onSelectSignature,
|
|
1096
|
+
externalSignatures = [],
|
|
1097
|
+
onSignRequest
|
|
1098
|
+
} = props;
|
|
1099
|
+
const [localSignatures, setLocalSignatures] = useState6([]);
|
|
1100
|
+
const [showModal, setShowModal] = useState6(false);
|
|
1101
|
+
const signatures = [...externalSignatures, ...localSignatures];
|
|
1102
|
+
const canvasRef = useRef4(null);
|
|
1103
|
+
const [isDrawing, setIsDrawing] = useState6(false);
|
|
1104
|
+
const handleCreateClick = async () => {
|
|
1105
|
+
if (onSignRequest) {
|
|
419
1106
|
try {
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (cancel) {
|
|
425
|
-
return;
|
|
1107
|
+
const newSig = await onSignRequest();
|
|
1108
|
+
if (newSig) {
|
|
1109
|
+
setLocalSignatures((prev) => [...prev, newSig]);
|
|
1110
|
+
onSelectSignature(newSig);
|
|
426
1111
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
props.onPageCount(pdf.numPages);
|
|
430
|
-
const p1 = await pdf.getPage(1);
|
|
431
|
-
const base = p1.getViewport({ scale: 1 });
|
|
432
|
-
const w = Math.min(980, Math.max(640, base.width));
|
|
433
|
-
const s = w / base.width;
|
|
434
|
-
const vp = p1.getViewport({ scale: s });
|
|
435
|
-
setSize({ w: Math.round(vp.width), h: Math.round(vp.height) });
|
|
436
|
-
const thumbWidth = 56;
|
|
437
|
-
const thumbsArr = [];
|
|
438
|
-
for (let i = 1; i <= pdf.numPages; i++) {
|
|
439
|
-
const page = await pdf.getPage(i);
|
|
440
|
-
const pageBase = page.getViewport({ scale: 1 });
|
|
441
|
-
const thumbScale = thumbWidth / pageBase.width;
|
|
442
|
-
const thumbVp = page.getViewport({ scale: thumbScale });
|
|
443
|
-
const thumbCanvas = document.createElement("canvas");
|
|
444
|
-
thumbCanvas.width = Math.round(thumbVp.width);
|
|
445
|
-
thumbCanvas.height = Math.round(thumbVp.height);
|
|
446
|
-
const thumbCtx = thumbCanvas.getContext("2d", { alpha: false });
|
|
447
|
-
if (thumbCtx) {
|
|
448
|
-
await page.render({ canvasContext: thumbCtx, viewport: thumbVp }).promise;
|
|
449
|
-
thumbsArr.push(thumbCanvas.toDataURL("image/png"));
|
|
450
|
-
} else {
|
|
451
|
-
thumbsArr.push(void 0);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
setThumbs(thumbsArr);
|
|
455
|
-
} catch (e) {
|
|
456
|
-
setError(
|
|
457
|
-
"Failed to load PDF. " + (e instanceof Error ? e.message : "")
|
|
458
|
-
);
|
|
459
|
-
} finally {
|
|
460
|
-
setLoading(false);
|
|
1112
|
+
} catch (err) {
|
|
1113
|
+
console.error("Custom sign request failed", err);
|
|
461
1114
|
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
cancel = true;
|
|
465
|
-
};
|
|
466
|
-
}, [url, arrayBuffer]);
|
|
467
|
-
useEffect4(() => {
|
|
468
|
-
props.onThumbs(thumbs);
|
|
469
|
-
}, [thumbs]);
|
|
470
|
-
const pagesToShow = useMemo4(() => {
|
|
471
|
-
if (props.layout === "side-by-side" && pageCount > 1) {
|
|
472
|
-
const left = Math.max(1, Math.min(props.currentPage, pageCount));
|
|
473
|
-
const right = Math.max(1, Math.min(left + 1, pageCount));
|
|
474
|
-
return left === right ? [left] : [left, right];
|
|
1115
|
+
} else {
|
|
1116
|
+
openCreateModal();
|
|
475
1117
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
for (const p of pagesToShow) {
|
|
485
|
-
if (rendered.has(p)) {
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
try {
|
|
489
|
-
const page = await doc.getPage(p);
|
|
490
|
-
if (cancel) {
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
const base = page.getViewport({ scale: 1 });
|
|
494
|
-
const vp = page.getViewport({ scale: size.w / base.width });
|
|
495
|
-
const canvas = document.createElement("canvas");
|
|
496
|
-
canvas.width = Math.round(vp.width);
|
|
497
|
-
canvas.height = Math.round(vp.height);
|
|
498
|
-
const ctx = canvas.getContext("2d", { alpha: false });
|
|
499
|
-
if (!ctx) {
|
|
500
|
-
continue;
|
|
501
|
-
}
|
|
502
|
-
await page.render({ canvasContext: ctx, viewport: vp }).promise;
|
|
503
|
-
if (cancel) {
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
setRendered((prev) => {
|
|
507
|
-
const next = new Map(prev);
|
|
508
|
-
next.set(p, canvas);
|
|
509
|
-
return next;
|
|
510
|
-
});
|
|
511
|
-
} catch {
|
|
512
|
-
}
|
|
1118
|
+
};
|
|
1119
|
+
const openCreateModal = () => {
|
|
1120
|
+
setShowModal(true);
|
|
1121
|
+
setTimeout(() => {
|
|
1122
|
+
const canvas = canvasRef.current;
|
|
1123
|
+
if (canvas) {
|
|
1124
|
+
const ctx = canvas.getContext("2d");
|
|
1125
|
+
ctx?.clearRect(0, 0, canvas.width, canvas.height);
|
|
513
1126
|
}
|
|
514
|
-
})
|
|
515
|
-
|
|
516
|
-
|
|
1127
|
+
}, 100);
|
|
1128
|
+
};
|
|
1129
|
+
const saveSignature = () => {
|
|
1130
|
+
const canvas = canvasRef.current;
|
|
1131
|
+
if (!canvas) return;
|
|
1132
|
+
const newSig = {
|
|
1133
|
+
id: Date.now().toString(),
|
|
1134
|
+
signatureImageUrl: canvas.toDataURL("image/png"),
|
|
1135
|
+
signedBy: "Me",
|
|
1136
|
+
dateSigned: (/* @__PURE__ */ new Date()).toISOString()
|
|
517
1137
|
};
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
);
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
return /* @__PURE__ */
|
|
545
|
-
|
|
546
|
-
doc ? /* @__PURE__ */ jsx4(
|
|
1138
|
+
setLocalSignatures([...localSignatures, newSig]);
|
|
1139
|
+
setShowModal(false);
|
|
1140
|
+
onSelectSignature(newSig);
|
|
1141
|
+
};
|
|
1142
|
+
const startDraw = (e) => {
|
|
1143
|
+
setIsDrawing(true);
|
|
1144
|
+
draw(e);
|
|
1145
|
+
};
|
|
1146
|
+
const stopDraw = () => setIsDrawing(false);
|
|
1147
|
+
const draw = (e) => {
|
|
1148
|
+
if (!isDrawing || !canvasRef.current) return;
|
|
1149
|
+
const canvas = canvasRef.current;
|
|
1150
|
+
const ctx = canvas.getContext("2d");
|
|
1151
|
+
if (!ctx) return;
|
|
1152
|
+
const rect = canvas.getBoundingClientRect();
|
|
1153
|
+
const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
|
|
1154
|
+
const clientY = "touches" in e ? e.touches[0].clientY : e.clientY;
|
|
1155
|
+
const x = clientX - rect.left;
|
|
1156
|
+
const y = clientY - rect.top;
|
|
1157
|
+
ctx.lineWidth = 2;
|
|
1158
|
+
ctx.lineCap = "round";
|
|
1159
|
+
ctx.lineTo(x, y);
|
|
1160
|
+
ctx.stroke();
|
|
1161
|
+
ctx.beginPath();
|
|
1162
|
+
ctx.moveTo(x, y);
|
|
1163
|
+
};
|
|
1164
|
+
return /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
1165
|
+
/* @__PURE__ */ jsxs7(
|
|
547
1166
|
"div",
|
|
548
1167
|
{
|
|
549
|
-
className:
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
1168
|
+
className: `hv-sidebar hv-sidebar-right ${!isOpen ? "collapsed" : ""}`,
|
|
1169
|
+
style: { width: isOpen ? "280px" : "0" },
|
|
1170
|
+
children: [
|
|
1171
|
+
/* @__PURE__ */ jsxs7(
|
|
553
1172
|
"div",
|
|
554
1173
|
{
|
|
555
|
-
className: "hv-
|
|
556
|
-
style: {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
const ctx = node.getContext("2d");
|
|
569
|
-
if (ctx) {
|
|
570
|
-
ctx.drawImage(c, 0, 0);
|
|
571
|
-
}
|
|
1174
|
+
className: "hv-sidebar-header",
|
|
1175
|
+
style: { justifyContent: "space-between", padding: "12px 16px" },
|
|
1176
|
+
children: [
|
|
1177
|
+
/* @__PURE__ */ jsx8("h3", { style: { margin: 0, fontSize: "15px", fontWeight: 600 }, children: "Signatures" }),
|
|
1178
|
+
/* @__PURE__ */ jsx8(
|
|
1179
|
+
"button",
|
|
1180
|
+
{
|
|
1181
|
+
onClick: onClose,
|
|
1182
|
+
className: "hv-btn",
|
|
1183
|
+
style: { padding: "4px", border: "none" },
|
|
1184
|
+
children: /* @__PURE__ */ jsx8(X, { size: 18 })
|
|
572
1185
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
1186
|
+
)
|
|
1187
|
+
]
|
|
1188
|
+
}
|
|
1189
|
+
),
|
|
1190
|
+
/* @__PURE__ */ jsxs7("div", { className: "hv-thumb-list", children: [
|
|
1191
|
+
/* @__PURE__ */ jsxs7(
|
|
1192
|
+
"button",
|
|
1193
|
+
{
|
|
1194
|
+
onClick: handleCreateClick,
|
|
1195
|
+
className: "hv-btn",
|
|
1196
|
+
style: {
|
|
1197
|
+
width: "100%",
|
|
1198
|
+
justifyContent: "center",
|
|
1199
|
+
border: "2px dashed var(--hv-border)",
|
|
1200
|
+
marginBottom: "16px",
|
|
1201
|
+
color: "var(--hv-primary)"
|
|
1202
|
+
},
|
|
1203
|
+
children: [
|
|
1204
|
+
/* @__PURE__ */ jsx8(Plus, { size: 18, style: { marginRight: "8px" } }),
|
|
1205
|
+
"New Signature"
|
|
1206
|
+
]
|
|
1207
|
+
}
|
|
1208
|
+
),
|
|
1209
|
+
signatures.map((sig, idx) => {
|
|
1210
|
+
const isLocal = localSignatures.some((s) => s.id === sig.id);
|
|
1211
|
+
const showDelete = isLocal;
|
|
1212
|
+
return /* @__PURE__ */ jsxs7(
|
|
1213
|
+
"div",
|
|
1214
|
+
{
|
|
1215
|
+
className: "hv-thumb-item",
|
|
1216
|
+
style: {
|
|
1217
|
+
position: "relative",
|
|
1218
|
+
padding: "12px",
|
|
1219
|
+
background: "var(--hv-bg)",
|
|
1220
|
+
borderRadius: "8px",
|
|
1221
|
+
border: "1px solid var(--hv-border)"
|
|
1222
|
+
},
|
|
1223
|
+
onClick: () => onSelectSignature(sig),
|
|
1224
|
+
children: [
|
|
1225
|
+
/* @__PURE__ */ jsx8(
|
|
1226
|
+
"img",
|
|
1227
|
+
{
|
|
1228
|
+
src: sig.signatureImageUrl,
|
|
1229
|
+
alt: "Signature",
|
|
1230
|
+
style: { height: "40px", objectFit: "contain" }
|
|
1231
|
+
}
|
|
1232
|
+
),
|
|
1233
|
+
/* @__PURE__ */ jsxs7(
|
|
1234
|
+
"div",
|
|
1235
|
+
{
|
|
1236
|
+
style: {
|
|
1237
|
+
fontSize: "11px",
|
|
1238
|
+
color: "var(--hv-muted)",
|
|
1239
|
+
marginTop: "4px",
|
|
1240
|
+
textAlign: "center"
|
|
1241
|
+
},
|
|
1242
|
+
children: [
|
|
1243
|
+
sig.signedBy || "User",
|
|
1244
|
+
" \u2022",
|
|
1245
|
+
" ",
|
|
1246
|
+
new Date(sig.dateSigned).toLocaleDateString(),
|
|
1247
|
+
sig.comment && /* @__PURE__ */ jsx8("div", { className: "text-xs text-gray-500 mt-1 italic", children: sig.comment })
|
|
1248
|
+
]
|
|
1249
|
+
}
|
|
1250
|
+
),
|
|
1251
|
+
showDelete && /* @__PURE__ */ jsx8(
|
|
1252
|
+
"button",
|
|
1253
|
+
{
|
|
1254
|
+
onClick: (e) => {
|
|
1255
|
+
e.stopPropagation();
|
|
1256
|
+
setLocalSignatures(
|
|
1257
|
+
localSignatures.filter((s) => s.id !== sig.id)
|
|
1258
|
+
);
|
|
1259
|
+
},
|
|
1260
|
+
className: "hv-btn",
|
|
1261
|
+
style: {
|
|
1262
|
+
position: "absolute",
|
|
1263
|
+
top: "4px",
|
|
1264
|
+
right: "4px",
|
|
1265
|
+
padding: "4px",
|
|
1266
|
+
color: "#ef4444",
|
|
1267
|
+
border: "none",
|
|
1268
|
+
background: "white"
|
|
1269
|
+
},
|
|
1270
|
+
children: /* @__PURE__ */ jsx8(Trash2, { size: 12 })
|
|
1271
|
+
}
|
|
1272
|
+
)
|
|
1273
|
+
]
|
|
1274
|
+
},
|
|
1275
|
+
sig.id || idx
|
|
1276
|
+
);
|
|
1277
|
+
})
|
|
1278
|
+
] })
|
|
1279
|
+
]
|
|
642
1280
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
cancel = true;
|
|
646
|
-
};
|
|
647
|
-
}, [props.arrayBuffer]);
|
|
648
|
-
useEffect5(() => {
|
|
649
|
-
props.onThumbs(thumbs);
|
|
650
|
-
}, [thumbs]);
|
|
651
|
-
const pagesToShow = useMemo5(() => {
|
|
652
|
-
const total = slides.length;
|
|
653
|
-
if (props.layout === "side-by-side" && total > 1) {
|
|
654
|
-
const left = Math.max(1, Math.min(props.currentPage, total));
|
|
655
|
-
const right = Math.max(1, Math.min(left + 1, total));
|
|
656
|
-
return left === right ? [left] : [left, right];
|
|
657
|
-
}
|
|
658
|
-
return [Math.max(1, Math.min(props.currentPage, total))];
|
|
659
|
-
}, [props.currentPage, props.layout, slides.length]);
|
|
660
|
-
return /* @__PURE__ */ jsxs5("div", { className: "hv-doc", children: [
|
|
661
|
-
loading && /* @__PURE__ */ jsx5("div", { className: "hv-loading", children: "Loading PPTX\u2026" }),
|
|
662
|
-
error && /* @__PURE__ */ jsx5("div", { className: "hv-error", children: error }),
|
|
663
|
-
!loading && !error && (!slides || slides.length === 0) && /* @__PURE__ */ jsx5("div", { className: "hv-error", children: "No slides to display." }),
|
|
664
|
-
!error && slides && slides.length > 0 && /* @__PURE__ */ jsx5(
|
|
1281
|
+
),
|
|
1282
|
+
showModal && /* @__PURE__ */ jsx8("div", { className: "hv-modal-overlay", children: /* @__PURE__ */ jsxs7(
|
|
665
1283
|
"div",
|
|
666
1284
|
{
|
|
667
|
-
className:
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
1285
|
+
className: "hv-modal",
|
|
1286
|
+
style: { width: "450px", maxWidth: "90vw" },
|
|
1287
|
+
children: [
|
|
1288
|
+
/* @__PURE__ */ jsxs7(
|
|
671
1289
|
"div",
|
|
672
1290
|
{
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
1291
|
+
style: {
|
|
1292
|
+
padding: "16px 24px",
|
|
1293
|
+
borderBottom: "1px solid var(--hv-border)",
|
|
1294
|
+
display: "flex",
|
|
1295
|
+
justifyContent: "space-between",
|
|
1296
|
+
alignItems: "center"
|
|
1297
|
+
},
|
|
676
1298
|
children: [
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
1299
|
+
/* @__PURE__ */ jsx8("h3", { style: { margin: 0, fontSize: "18px", fontWeight: 600 }, children: "Draw Signature" }),
|
|
1300
|
+
/* @__PURE__ */ jsx8(
|
|
1301
|
+
"button",
|
|
1302
|
+
{
|
|
1303
|
+
onClick: () => setShowModal(false),
|
|
1304
|
+
className: "hv-btn",
|
|
1305
|
+
style: { border: "none" },
|
|
1306
|
+
children: /* @__PURE__ */ jsx8(X, { size: 20 })
|
|
1307
|
+
}
|
|
1308
|
+
)
|
|
682
1309
|
]
|
|
683
|
-
},
|
|
684
|
-
p
|
|
685
|
-
);
|
|
686
|
-
})
|
|
687
|
-
}
|
|
688
|
-
)
|
|
689
|
-
] });
|
|
690
|
-
}
|
|
691
|
-
function svgThumb(n) {
|
|
692
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="180" height="100"><rect width="100%" height="100%" rx="12" fill="#111827"/><text x="50%" y="54%" font-size="18" fill="#e5e7eb" text-anchor="middle">${n}</text></svg>`;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// src/utils/locale.ts
|
|
696
|
-
var defaultLocale = {
|
|
697
|
-
"loading": "Loading\u2026",
|
|
698
|
-
"error.title": "Error",
|
|
699
|
-
"toolbar.layout.single": "Single page",
|
|
700
|
-
"toolbar.layout.two": "Side-by-side",
|
|
701
|
-
"toolbar.thumbs": "Thumbnails",
|
|
702
|
-
"toolbar.signatures": "Signatures",
|
|
703
|
-
"toolbar.sign": "Sign Document",
|
|
704
|
-
"toolbar.save": "Save",
|
|
705
|
-
"toolbar.exportPdf": "Export as PDF",
|
|
706
|
-
"thumbnails.title": "Thumbnails",
|
|
707
|
-
"thumbnails.page": "Page",
|
|
708
|
-
"signatures.title": "Signatures",
|
|
709
|
-
"signatures.empty": "No signatures",
|
|
710
|
-
"signatures.placeHint": "Click on the document to place the signature.",
|
|
711
|
-
"a11y.viewer": "Document viewer",
|
|
712
|
-
"a11y.ribbon": "Ribbon",
|
|
713
|
-
"a11y.editor": "Document editor"
|
|
714
|
-
};
|
|
715
|
-
|
|
716
|
-
// src/components/SignaturePanel.tsx
|
|
717
|
-
import React6 from "react";
|
|
718
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
719
|
-
function SignaturePanel(props) {
|
|
720
|
-
const title = props.locale["signatures.title"] ?? "Signatures";
|
|
721
|
-
const deduped = React6.useMemo(() => {
|
|
722
|
-
const seen = /* @__PURE__ */ new Set();
|
|
723
|
-
return props.signatures.filter((s) => {
|
|
724
|
-
const key = `${s.signedBy}|${s.dateSigned}|${s.signatureImageUrl}`;
|
|
725
|
-
if (seen.has(key)) return false;
|
|
726
|
-
seen.add(key);
|
|
727
|
-
return true;
|
|
728
|
-
});
|
|
729
|
-
}, [props.signatures]);
|
|
730
|
-
return /* @__PURE__ */ jsxs6(
|
|
731
|
-
"aside",
|
|
732
|
-
{
|
|
733
|
-
className: props.collapsed ? "hv-side hv-side--collapsed" : "hv-side",
|
|
734
|
-
"aria-label": title,
|
|
735
|
-
children: [
|
|
736
|
-
/* @__PURE__ */ jsxs6("div", { className: "hv-sidebar-header", children: [
|
|
737
|
-
/* @__PURE__ */ jsx6(
|
|
738
|
-
"button",
|
|
739
|
-
{
|
|
740
|
-
type: "button",
|
|
741
|
-
className: "hv-icon",
|
|
742
|
-
onClick: props.onToggle,
|
|
743
|
-
"aria-label": props.locale["toolbar.signatures"] ?? "Signatures",
|
|
744
|
-
children: /* @__PURE__ */ jsx6("span", { "aria-hidden": true, children: "\u270D" })
|
|
745
1310
|
}
|
|
746
1311
|
),
|
|
747
|
-
/* @__PURE__ */
|
|
748
|
-
] }),
|
|
749
|
-
/* @__PURE__ */ jsxs6("div", { className: "hv-sidebar-body", children: [
|
|
750
|
-
deduped.length === 0 && /* @__PURE__ */ jsx6("div", { className: "hv-signature-empty", "aria-live": "polite", children: props.locale["signatures.empty"] ?? "No signatures yet." }),
|
|
751
|
-
deduped.map((s, idx) => /* @__PURE__ */ jsxs6(
|
|
1312
|
+
/* @__PURE__ */ jsxs7(
|
|
752
1313
|
"div",
|
|
753
1314
|
{
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1315
|
+
style: {
|
|
1316
|
+
padding: "24px",
|
|
1317
|
+
background: "#f9fafb",
|
|
1318
|
+
display: "flex",
|
|
1319
|
+
flexDirection: "column",
|
|
1320
|
+
alignItems: "center"
|
|
1321
|
+
},
|
|
757
1322
|
children: [
|
|
758
|
-
/* @__PURE__ */
|
|
759
|
-
"
|
|
1323
|
+
/* @__PURE__ */ jsx8(
|
|
1324
|
+
"div",
|
|
760
1325
|
{
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
"
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1326
|
+
style: {
|
|
1327
|
+
background: "white",
|
|
1328
|
+
border: "1px solid #e5e7eb",
|
|
1329
|
+
borderRadius: "8px",
|
|
1330
|
+
overflow: "hidden",
|
|
1331
|
+
boxShadow: "inset 0 2px 4px 0 rgba(0,0,0,0.05)"
|
|
1332
|
+
},
|
|
1333
|
+
children: /* @__PURE__ */ jsx8(
|
|
1334
|
+
"canvas",
|
|
1335
|
+
{
|
|
1336
|
+
ref: canvasRef,
|
|
1337
|
+
width: 400,
|
|
1338
|
+
height: 200,
|
|
1339
|
+
style: {
|
|
1340
|
+
display: "block",
|
|
1341
|
+
cursor: "crosshair",
|
|
1342
|
+
touchAction: "none"
|
|
1343
|
+
},
|
|
1344
|
+
onMouseDown: startDraw,
|
|
1345
|
+
onMouseUp: stopDraw,
|
|
1346
|
+
onMouseMove: draw,
|
|
1347
|
+
onTouchStart: startDraw,
|
|
1348
|
+
onTouchEnd: stopDraw,
|
|
1349
|
+
onTouchMove: draw
|
|
1350
|
+
}
|
|
1351
|
+
)
|
|
767
1352
|
}
|
|
768
1353
|
),
|
|
769
|
-
/* @__PURE__ */
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1354
|
+
/* @__PURE__ */ jsx8(
|
|
1355
|
+
"p",
|
|
1356
|
+
{
|
|
1357
|
+
style: {
|
|
1358
|
+
fontSize: "12px",
|
|
1359
|
+
color: "var(--hv-muted)",
|
|
1360
|
+
marginTop: "8px"
|
|
1361
|
+
},
|
|
1362
|
+
children: "Sign above using your mouse or finger"
|
|
1363
|
+
}
|
|
1364
|
+
)
|
|
774
1365
|
]
|
|
775
|
-
},
|
|
776
|
-
`${s.signedBy}-${s.dateSigned}-${s.signatureImageUrl}`
|
|
777
|
-
))
|
|
778
|
-
] })
|
|
779
|
-
]
|
|
780
|
-
}
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// src/components/ThumbnailsSidebar.tsx
|
|
785
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
786
|
-
function ThumbnailsSidebar(props) {
|
|
787
|
-
const t = props.locale["thumbnails.title"] ?? "Thumbnails";
|
|
788
|
-
return /* @__PURE__ */ jsxs7(
|
|
789
|
-
"aside",
|
|
790
|
-
{
|
|
791
|
-
className: props.collapsed ? "hv-thumbs hv-thumbs--collapsed" : "hv-thumbs",
|
|
792
|
-
"aria-label": t,
|
|
793
|
-
children: [
|
|
794
|
-
/* @__PURE__ */ jsxs7("div", { className: "hv-thumbs-header", children: [
|
|
795
|
-
/* @__PURE__ */ jsx7(
|
|
796
|
-
"button",
|
|
797
|
-
{
|
|
798
|
-
type: "button",
|
|
799
|
-
className: "hv-thumbs-toggle",
|
|
800
|
-
onClick: props.onToggle,
|
|
801
|
-
"aria-label": props.collapsed ? props.locale["thumbnails.open"] ?? "Open thumbnails" : props.locale["thumbnails.close"] ?? "Close thumbnails",
|
|
802
|
-
children: /* @__PURE__ */ jsx7("span", { className: "hv-thumbs-toggle-icon", children: props.collapsed ? "\u25B8" : "\u25BE" })
|
|
803
1366
|
}
|
|
804
1367
|
),
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
!props.collapsed && /* @__PURE__ */ jsx7("div", { className: "hv-thumbs-list", role: "list", children: props.thumbnails.map((th, idx) => {
|
|
808
|
-
const p = idx + 1;
|
|
809
|
-
const active = p === props.currentPage;
|
|
810
|
-
return /* @__PURE__ */ jsxs7(
|
|
811
|
-
"button",
|
|
1368
|
+
/* @__PURE__ */ jsxs7(
|
|
1369
|
+
"div",
|
|
812
1370
|
{
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1371
|
+
style: {
|
|
1372
|
+
padding: "16px 24px",
|
|
1373
|
+
borderTop: "1px solid var(--hv-border)",
|
|
1374
|
+
display: "flex",
|
|
1375
|
+
justifyContent: "flex-end",
|
|
1376
|
+
gap: "12px"
|
|
1377
|
+
},
|
|
819
1378
|
children: [
|
|
820
|
-
/* @__PURE__ */
|
|
821
|
-
|
|
1379
|
+
/* @__PURE__ */ jsx8(
|
|
1380
|
+
"button",
|
|
1381
|
+
{
|
|
1382
|
+
onClick: () => {
|
|
1383
|
+
const ctx = canvasRef.current?.getContext("2d");
|
|
1384
|
+
ctx?.clearRect(0, 0, 400, 200);
|
|
1385
|
+
},
|
|
1386
|
+
className: "hv-btn",
|
|
1387
|
+
children: "Clear"
|
|
1388
|
+
}
|
|
1389
|
+
),
|
|
1390
|
+
/* @__PURE__ */ jsx8("button", { onClick: saveSignature, className: "hv-btn hv-btn-primary", children: "Create & Use" })
|
|
822
1391
|
]
|
|
823
|
-
},
|
|
824
|
-
th.id
|
|
825
|
-
);
|
|
826
|
-
}) })
|
|
827
|
-
]
|
|
828
|
-
}
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// src/components/Toolbar.tsx
|
|
833
|
-
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
834
|
-
function Toolbar(props) {
|
|
835
|
-
const t = (k, fallback) => props.locale[k] ?? fallback;
|
|
836
|
-
return /* @__PURE__ */ jsxs8(
|
|
837
|
-
"div",
|
|
838
|
-
{
|
|
839
|
-
className: "hv-toolbar",
|
|
840
|
-
role: "toolbar",
|
|
841
|
-
"aria-label": t("a11y.toolbar", "Document toolbar"),
|
|
842
|
-
children: [
|
|
843
|
-
/* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__group", children: [
|
|
844
|
-
/* @__PURE__ */ jsx8(
|
|
845
|
-
"button",
|
|
846
|
-
{
|
|
847
|
-
className: `hv-btn ${props.showThumbnails ? "hv-btn--active" : ""}`,
|
|
848
|
-
onClick: props.onToggleThumbnails,
|
|
849
|
-
"aria-pressed": props.showThumbnails,
|
|
850
|
-
children: "Thumbnails"
|
|
851
|
-
}
|
|
852
|
-
),
|
|
853
|
-
props.mode !== "create" && /* @__PURE__ */ jsx8(
|
|
854
|
-
"button",
|
|
855
|
-
{
|
|
856
|
-
className: `hv-btn ${props.showSignatures ? "hv-btn--active" : ""}`,
|
|
857
|
-
onClick: props.onToggleSignatures,
|
|
858
|
-
"aria-pressed": props.showSignatures,
|
|
859
|
-
children: "Signatures"
|
|
860
1392
|
}
|
|
861
1393
|
)
|
|
862
|
-
]
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
{
|
|
867
|
-
className: `hv-btn ${props.layout === "single" ? "hv-btn--active" : ""}`,
|
|
868
|
-
onClick: () => props.onChangeLayout("single"),
|
|
869
|
-
children: "Single page"
|
|
870
|
-
}
|
|
871
|
-
),
|
|
872
|
-
/* @__PURE__ */ jsx8(
|
|
873
|
-
"button",
|
|
874
|
-
{
|
|
875
|
-
className: `hv-btn ${props.layout === "side-by-side" ? "hv-btn--active" : ""}`,
|
|
876
|
-
onClick: () => props.onChangeLayout("side-by-side"),
|
|
877
|
-
children: "Side-by-side"
|
|
878
|
-
}
|
|
879
|
-
)
|
|
880
|
-
] }),
|
|
881
|
-
/* @__PURE__ */ jsxs8("div", { className: "hv-toolbar__group hv-toolbar__actions", children: [
|
|
882
|
-
props.showHeaderFooterToggle && /* @__PURE__ */ jsxs8("label", { className: "hv-switch", children: [
|
|
883
|
-
/* @__PURE__ */ jsx8(
|
|
884
|
-
"input",
|
|
885
|
-
{
|
|
886
|
-
type: "checkbox",
|
|
887
|
-
checked: props.headerFooterEnabled,
|
|
888
|
-
onChange: props.onToggleHeaderFooter
|
|
889
|
-
}
|
|
890
|
-
),
|
|
891
|
-
/* @__PURE__ */ jsx8("span", { className: "hv-switch__slider" }),
|
|
892
|
-
/* @__PURE__ */ jsx8("span", { className: "hv-switch__label", children: t("toolbar.letterhead", "Letterhead") })
|
|
893
|
-
] }),
|
|
894
|
-
props.allowSigning && /* @__PURE__ */ jsx8(
|
|
895
|
-
"button",
|
|
896
|
-
{
|
|
897
|
-
className: "hv-btn hv-btn--primary",
|
|
898
|
-
onClick: props.onSign,
|
|
899
|
-
disabled: props.signingDisabled,
|
|
900
|
-
children: "Sign document"
|
|
901
|
-
}
|
|
902
|
-
),
|
|
903
|
-
props.canExportPdf && /* @__PURE__ */ jsx8("button", { className: "hv-btn", onClick: props.onExportPdf, children: "Export PDF" }),
|
|
904
|
-
props.canSave && /* @__PURE__ */ jsx8("button", { className: "hv-btn hv-btn--primary", onClick: props.onSave, children: "Save" })
|
|
905
|
-
] })
|
|
906
|
-
]
|
|
907
|
-
}
|
|
908
|
-
);
|
|
1394
|
+
]
|
|
1395
|
+
}
|
|
1396
|
+
) })
|
|
1397
|
+
] });
|
|
909
1398
|
}
|
|
910
1399
|
|
|
911
1400
|
// src/components/DocumentViewer.tsx
|
|
912
|
-
import { jsx as jsx9, jsxs as
|
|
1401
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
913
1402
|
function DocumentViewer(props) {
|
|
914
1403
|
const mode = props.mode ?? "view";
|
|
915
1404
|
const theme = props.theme ?? "light";
|
|
916
|
-
const
|
|
917
|
-
() => ({ ...defaultLocale, ...props.locale ?? {} }),
|
|
918
|
-
[props.locale]
|
|
919
|
-
);
|
|
920
|
-
const [layout, setLayout] = useState6(
|
|
1405
|
+
const [layout, setLayout] = useState7(
|
|
921
1406
|
props.defaultLayout ?? "single"
|
|
922
1407
|
);
|
|
923
|
-
const [showThumbnails, setShowThumbnails] =
|
|
924
|
-
|
|
925
|
-
const [headerFooterEnabled, setHeaderFooterEnabled] = useState6(true);
|
|
926
|
-
const allowSigning = props.allowSigning ?? false;
|
|
927
|
-
const [signingBusy, setSigningBusy] = useState6(false);
|
|
928
|
-
const [resolved, setResolved] = useState6(null);
|
|
929
|
-
const [error, setError] = useState6("");
|
|
930
|
-
const [pageCount, setPageCount] = useState6(1);
|
|
931
|
-
const [currentPage, setCurrentPage] = useState6(1);
|
|
932
|
-
const [thumbs, setThumbs] = useState6([]);
|
|
933
|
-
const [localSignatures, setLocalSignatures] = useState6(
|
|
934
|
-
props.signatures ?? []
|
|
1408
|
+
const [showThumbnails, setShowThumbnails] = useState7(
|
|
1409
|
+
props.defaultShowThumbnails ?? true
|
|
935
1410
|
);
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
);
|
|
940
|
-
const [
|
|
941
|
-
const [
|
|
942
|
-
|
|
943
|
-
)
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
(async () => {
|
|
1411
|
+
const [showSignatures, setShowSignatures] = useState7(false);
|
|
1412
|
+
const [resolved, setResolved] = useState7(null);
|
|
1413
|
+
const [loading, setLoading] = useState7(true);
|
|
1414
|
+
const [error, setError] = useState7("");
|
|
1415
|
+
const [pageCount, setPageCount] = useState7(1);
|
|
1416
|
+
const [currentPage, setCurrentPage] = useState7(1);
|
|
1417
|
+
const [thumbnails, setThumbnails] = useState7([]);
|
|
1418
|
+
useEffect7(() => {
|
|
1419
|
+
let active = true;
|
|
1420
|
+
const loadFile = async () => {
|
|
1421
|
+
setLoading(true);
|
|
948
1422
|
setError("");
|
|
949
1423
|
setResolved(null);
|
|
950
|
-
setThumbs([]);
|
|
951
|
-
setPageCount(1);
|
|
952
|
-
setCurrentPage(1);
|
|
953
|
-
setSigPlacements([]);
|
|
954
|
-
setArmedSignatureUrl(null);
|
|
955
|
-
if (mode === "create") {
|
|
956
|
-
const ft = props.fileType ?? "docx";
|
|
957
|
-
setResolved({
|
|
958
|
-
fileType: ft,
|
|
959
|
-
fileName: props.fileName ?? `Untitled.${ft}`
|
|
960
|
-
});
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
1424
|
try {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
arrayBuffer: res.arrayBuffer
|
|
979
|
-
});
|
|
980
|
-
} catch (e) {
|
|
981
|
-
if (cancelled) {
|
|
982
|
-
return;
|
|
1425
|
+
if (mode === "create") {
|
|
1426
|
+
setResolved({
|
|
1427
|
+
fileType: props.fileType ?? "docx",
|
|
1428
|
+
fileName: props.fileName ?? "Untitled"
|
|
1429
|
+
});
|
|
1430
|
+
} else {
|
|
1431
|
+
const res = await resolveSource({
|
|
1432
|
+
fileUrl: props.fileUrl,
|
|
1433
|
+
base64: props.base64,
|
|
1434
|
+
blob: props.blob,
|
|
1435
|
+
fileName: props.fileName,
|
|
1436
|
+
fileType: props.fileType
|
|
1437
|
+
});
|
|
1438
|
+
if (active) setResolved(res);
|
|
983
1439
|
}
|
|
984
|
-
|
|
1440
|
+
} catch (err) {
|
|
1441
|
+
if (active) setError(err.message || "Failed to load document");
|
|
1442
|
+
} finally {
|
|
1443
|
+
if (active) setLoading(false);
|
|
985
1444
|
}
|
|
986
|
-
}
|
|
1445
|
+
};
|
|
1446
|
+
loadFile();
|
|
987
1447
|
return () => {
|
|
988
|
-
|
|
1448
|
+
active = false;
|
|
989
1449
|
};
|
|
990
|
-
}, [
|
|
991
|
-
|
|
992
|
-
props.
|
|
993
|
-
|
|
994
|
-
props.blob,
|
|
995
|
-
props.fileName,
|
|
996
|
-
props.fileType
|
|
997
|
-
]);
|
|
998
|
-
const thumbnails = useMemo6(() => {
|
|
999
|
-
const n = Math.max(1, pageCount);
|
|
1000
|
-
return Array.from({ length: n }, (_, i) => ({
|
|
1001
|
-
id: `p-${i + 1}`,
|
|
1002
|
-
label: `${locale["thumbnails.page"] ?? "Page"} ${i + 1}`,
|
|
1003
|
-
dataUrl: thumbs[i]
|
|
1004
|
-
}));
|
|
1005
|
-
}, [pageCount, thumbs, locale]);
|
|
1006
|
-
async function handleSignRequest() {
|
|
1007
|
-
if (!allowSigning || signingBusy || !props.onSignRequest) {
|
|
1008
|
-
return;
|
|
1450
|
+
}, [props.fileUrl, props.base64, props.blob, mode]);
|
|
1451
|
+
const handleSignatureSelect = (sig) => {
|
|
1452
|
+
if (props.onSign) {
|
|
1453
|
+
props.onSign(sig);
|
|
1009
1454
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1455
|
+
console.log("Signature selected:", sig);
|
|
1456
|
+
};
|
|
1457
|
+
const renderContent = () => {
|
|
1458
|
+
if (error) {
|
|
1459
|
+
return /* @__PURE__ */ jsxs8("div", { className: "hv-error-banner", children: [
|
|
1460
|
+
/* @__PURE__ */ jsx9("strong", { children: "Error loading document" }),
|
|
1461
|
+
/* @__PURE__ */ jsx9("p", { children: error })
|
|
1462
|
+
] });
|
|
1017
1463
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
setSigPlacements((prev) => [
|
|
1024
|
-
...prev,
|
|
1025
|
-
{ ...p, signatureImageUrl: armedSignatureUrl }
|
|
1026
|
-
]);
|
|
1027
|
-
setArmedSignatureUrl(null);
|
|
1028
|
-
}
|
|
1029
|
-
async function handleSave(exportPdf) {
|
|
1030
|
-
if (editorRef.current) {
|
|
1031
|
-
await editorRef.current.save(!!exportPdf);
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
if (!resolved?.arrayBuffer) {
|
|
1035
|
-
return;
|
|
1464
|
+
if (loading || !resolved) {
|
|
1465
|
+
return /* @__PURE__ */ jsxs8("div", { className: "hv-loader", children: [
|
|
1466
|
+
/* @__PURE__ */ jsx9("div", { className: "hv-spinner" }),
|
|
1467
|
+
/* @__PURE__ */ jsx9("span", { children: "Loading Document..." })
|
|
1468
|
+
] });
|
|
1036
1469
|
}
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1470
|
+
const commonProps = {
|
|
1471
|
+
arrayBuffer: resolved.arrayBuffer,
|
|
1039
1472
|
fileName: resolved.fileName,
|
|
1040
1473
|
fileType: resolved.fileType,
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1474
|
+
layout,
|
|
1475
|
+
currentPage,
|
|
1476
|
+
onPageCount: setPageCount,
|
|
1477
|
+
onCurrentPageChange: setCurrentPage,
|
|
1478
|
+
onThumbs: setThumbnails
|
|
1479
|
+
};
|
|
1480
|
+
switch (resolved.fileType) {
|
|
1481
|
+
case "pdf":
|
|
1482
|
+
return /* @__PURE__ */ jsx9(PdfRenderer, { url: resolved.url, ...commonProps });
|
|
1483
|
+
case "docx":
|
|
1484
|
+
case "doc":
|
|
1485
|
+
case "rtf":
|
|
1486
|
+
case "txt":
|
|
1487
|
+
case "md":
|
|
1488
|
+
return /* @__PURE__ */ jsx9(RichTextEditor, { mode, ...commonProps });
|
|
1489
|
+
case "xlsx":
|
|
1490
|
+
case "csv":
|
|
1491
|
+
case "xls":
|
|
1492
|
+
return /* @__PURE__ */ jsx9(SpreadsheetEditor, { mode, ...commonProps });
|
|
1493
|
+
case "pptx":
|
|
1494
|
+
case "ppt":
|
|
1495
|
+
return /* @__PURE__ */ jsx9(PptxRenderer, { ...commonProps });
|
|
1496
|
+
case "jpg":
|
|
1497
|
+
case "jpeg":
|
|
1498
|
+
case "png":
|
|
1499
|
+
case "gif":
|
|
1500
|
+
case "bmp":
|
|
1501
|
+
case "svg":
|
|
1502
|
+
return /* @__PURE__ */ jsx9(ImageRenderer, { ...commonProps, fileType: resolved.fileType });
|
|
1503
|
+
default:
|
|
1504
|
+
return /* @__PURE__ */ jsxs8("div", { className: "hv-error-banner", children: [
|
|
1505
|
+
"Unsupported file type: ",
|
|
1506
|
+
resolved.fileType
|
|
1507
|
+
] });
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
return /* @__PURE__ */ jsxs8("div", { className: "hv-root", "data-hv-theme": theme, children: [
|
|
1047
1511
|
/* @__PURE__ */ jsx9(
|
|
1048
1512
|
Toolbar,
|
|
1049
1513
|
{
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1514
|
+
fileName: resolved?.fileName,
|
|
1515
|
+
pageCount,
|
|
1516
|
+
currentPage,
|
|
1517
|
+
onPageChange: setCurrentPage,
|
|
1053
1518
|
layout,
|
|
1054
|
-
|
|
1519
|
+
onLayoutChange: setLayout,
|
|
1055
1520
|
showThumbnails,
|
|
1056
|
-
onToggleThumbnails: () => setShowThumbnails(
|
|
1521
|
+
onToggleThumbnails: () => setShowThumbnails(!showThumbnails),
|
|
1057
1522
|
showSignatures,
|
|
1058
|
-
onToggleSignatures: () => setShowSignatures(
|
|
1059
|
-
|
|
1060
|
-
allowSigning,
|
|
1061
|
-
signingDisabled: signingBusy || !props.onSignRequest,
|
|
1062
|
-
canSave,
|
|
1063
|
-
onSave: () => void handleSave(false),
|
|
1064
|
-
canExportPdf,
|
|
1065
|
-
onExportPdf: () => void handleSave(true),
|
|
1066
|
-
headerFooterEnabled,
|
|
1067
|
-
showHeaderFooterToggle: (props.enableHeaderFooterToggle ?? true) && mode === "create",
|
|
1068
|
-
onToggleHeaderFooter: () => setHeaderFooterEnabled((v) => !v)
|
|
1523
|
+
onToggleSignatures: () => setShowSignatures(!showSignatures),
|
|
1524
|
+
disableSigning: props.disableSigning
|
|
1069
1525
|
}
|
|
1070
1526
|
),
|
|
1071
|
-
|
|
1072
|
-
/* @__PURE__ */ jsx9(
|
|
1073
|
-
/* @__PURE__ */ jsx9("div", { className: "hv-error-body", children: error })
|
|
1074
|
-
] }) : null,
|
|
1075
|
-
!resolved && !error ? /* @__PURE__ */ jsx9("div", { className: "hv-loading", "aria-busy": "true", children: locale.loading ?? "Loading\u2026" }) : null,
|
|
1076
|
-
resolved ? /* @__PURE__ */ jsxs9("div", { className: "hv-shell", children: [
|
|
1077
|
-
mode !== "create" ? /* @__PURE__ */ jsx9(
|
|
1527
|
+
/* @__PURE__ */ jsxs8("div", { className: "hv-shell", children: [
|
|
1528
|
+
/* @__PURE__ */ jsx9(
|
|
1078
1529
|
ThumbnailsSidebar,
|
|
1079
1530
|
{
|
|
1080
|
-
|
|
1531
|
+
isOpen: showThumbnails,
|
|
1081
1532
|
thumbnails,
|
|
1082
1533
|
currentPage,
|
|
1083
|
-
collapsed: !showThumbnails,
|
|
1084
|
-
onToggle: () => setShowThumbnails((v) => !v),
|
|
1085
1534
|
onSelectPage: setCurrentPage
|
|
1086
1535
|
}
|
|
1087
|
-
)
|
|
1088
|
-
/* @__PURE__ */
|
|
1089
|
-
|
|
1090
|
-
PdfRenderer,
|
|
1091
|
-
{
|
|
1092
|
-
url: resolved.url,
|
|
1093
|
-
arrayBuffer: resolved.arrayBuffer,
|
|
1094
|
-
layout,
|
|
1095
|
-
currentPage,
|
|
1096
|
-
onCurrentPageChange: setCurrentPage,
|
|
1097
|
-
onPageCount: (n) => {
|
|
1098
|
-
setPageCount(n);
|
|
1099
|
-
setThumbs(
|
|
1100
|
-
(prev) => prev.length === n ? prev : Array.from({ length: n }, (_, i) => prev[i])
|
|
1101
|
-
);
|
|
1102
|
-
},
|
|
1103
|
-
onThumbs: (t) => setThumbs(t),
|
|
1104
|
-
signatureStamp: armedSignatureUrl ? {
|
|
1105
|
-
imageUrl: armedSignatureUrl,
|
|
1106
|
-
armed: true,
|
|
1107
|
-
onPlaced: placeSignature
|
|
1108
|
-
} : void 0
|
|
1109
|
-
}
|
|
1110
|
-
) : null,
|
|
1111
|
-
resolved.fileType === "docx" || resolved.fileType === "md" || resolved.fileType === "txt" ? /* @__PURE__ */ jsx9(
|
|
1112
|
-
RichTextEditor,
|
|
1113
|
-
{
|
|
1114
|
-
ref: editorRef,
|
|
1115
|
-
mode,
|
|
1116
|
-
fileType: resolved.fileType,
|
|
1117
|
-
fileName: resolved.fileName,
|
|
1118
|
-
arrayBuffer: resolved.arrayBuffer,
|
|
1119
|
-
headerComponent: props.headerComponent,
|
|
1120
|
-
footerComponent: props.footerComponent,
|
|
1121
|
-
headerFooterEnabled,
|
|
1122
|
-
locale,
|
|
1123
|
-
signatures: localSignatures,
|
|
1124
|
-
signaturePlacements: sigPlacements,
|
|
1125
|
-
onPageCount: (n) => {
|
|
1126
|
-
setPageCount(n);
|
|
1127
|
-
setThumbs(
|
|
1128
|
-
(prev) => prev.length === n ? prev : Array.from({ length: n }, (_, i) => prev[i])
|
|
1129
|
-
);
|
|
1130
|
-
},
|
|
1131
|
-
onSave: (b64, meta) => props.onSave?.(b64, meta),
|
|
1132
|
-
armedSignatureUrl,
|
|
1133
|
-
onPlaceSignature: placeSignature
|
|
1134
|
-
}
|
|
1135
|
-
) : null,
|
|
1136
|
-
resolved.fileType === "xlsx" ? /* @__PURE__ */ jsx9(
|
|
1137
|
-
SpreadsheetEditor,
|
|
1138
|
-
{
|
|
1139
|
-
ref: editorRef,
|
|
1140
|
-
mode,
|
|
1141
|
-
fileName: resolved.fileName,
|
|
1142
|
-
arrayBuffer: resolved.arrayBuffer,
|
|
1143
|
-
locale,
|
|
1144
|
-
onSave: (b64, meta) => props.onSave?.(b64, meta)
|
|
1145
|
-
}
|
|
1146
|
-
) : null,
|
|
1147
|
-
resolved.fileType === "pptx" ? /* @__PURE__ */ jsx9(
|
|
1148
|
-
PptxRenderer,
|
|
1149
|
-
{
|
|
1150
|
-
arrayBuffer: resolved.arrayBuffer,
|
|
1151
|
-
layout,
|
|
1152
|
-
currentPage,
|
|
1153
|
-
onCurrentPageChange: setCurrentPage,
|
|
1154
|
-
onSlideCount: (n) => {
|
|
1155
|
-
setPageCount(n);
|
|
1156
|
-
setThumbs(
|
|
1157
|
-
(prev) => prev.length === n ? prev : Array.from({ length: n }, (_, i) => prev[i])
|
|
1158
|
-
);
|
|
1159
|
-
},
|
|
1160
|
-
onThumbs: (t) => setThumbs(t)
|
|
1161
|
-
}
|
|
1162
|
-
) : null,
|
|
1163
|
-
resolved.fileType === "png" || resolved.fileType === "jpg" || resolved.fileType === "svg" ? /* @__PURE__ */ jsx9(
|
|
1164
|
-
ImageRenderer,
|
|
1165
|
-
{
|
|
1166
|
-
arrayBuffer: resolved.arrayBuffer,
|
|
1167
|
-
fileType: resolved.fileType,
|
|
1168
|
-
fileName: resolved.fileName
|
|
1169
|
-
}
|
|
1170
|
-
) : null
|
|
1171
|
-
] }),
|
|
1172
|
-
mode !== "create" && localSignatures.length ? /* @__PURE__ */ jsx9(
|
|
1536
|
+
),
|
|
1537
|
+
/* @__PURE__ */ jsx9("main", { className: "hv-main", children: renderContent() }),
|
|
1538
|
+
/* @__PURE__ */ jsx9(
|
|
1173
1539
|
SignaturePanel,
|
|
1174
1540
|
{
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1541
|
+
isOpen: showSignatures,
|
|
1542
|
+
onClose: () => setShowSignatures(false),
|
|
1543
|
+
onSelectSignature: handleSignatureSelect,
|
|
1544
|
+
externalSignatures: props.signatures,
|
|
1545
|
+
onSignRequest: props.onSignRequest
|
|
1179
1546
|
}
|
|
1180
|
-
)
|
|
1181
|
-
] })
|
|
1547
|
+
)
|
|
1548
|
+
] })
|
|
1182
1549
|
] });
|
|
1183
1550
|
}
|
|
1184
|
-
function arrayBufferToBase642(ab) {
|
|
1185
|
-
const bytes = new Uint8Array(ab);
|
|
1186
|
-
let binary = "";
|
|
1187
|
-
const chunk = 32768;
|
|
1188
|
-
for (let i = 0; i < bytes.length; i += chunk) {
|
|
1189
|
-
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
|
1190
|
-
}
|
|
1191
|
-
return btoa(binary);
|
|
1192
|
-
}
|
|
1193
1551
|
export {
|
|
1194
1552
|
DocumentViewer
|
|
1195
1553
|
};
|
|
1196
|
-
//# sourceMappingURL=index.
|
|
1554
|
+
//# sourceMappingURL=index.mjs.map
|