@relevaince/document-viewer 0.1.1 → 0.2.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.
- package/dist/index.d.mts +12 -8
- package/dist/index.d.ts +12 -8
- package/dist/index.js +560 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +545 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __esm = (fn, res) => function __init() {
|
|
7
9
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
@@ -18,6 +20,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
20
|
}
|
|
19
21
|
return to;
|
|
20
22
|
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
21
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
32
|
|
|
23
33
|
// src/components/renderers/PdfRenderer.tsx
|
|
@@ -50,7 +60,7 @@ function bboxToHighlight(target) {
|
|
|
50
60
|
}
|
|
51
61
|
function CitationHighlightRenderer() {
|
|
52
62
|
const { highlight, isScrolledTo } = (0, import_react_pdf_highlighter_extended.useHighlightContainerContext)();
|
|
53
|
-
return /* @__PURE__ */ (0,
|
|
63
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
54
64
|
import_react_pdf_highlighter_extended.AreaHighlight,
|
|
55
65
|
{
|
|
56
66
|
highlight,
|
|
@@ -72,28 +82,28 @@ function PdfRenderer({
|
|
|
72
82
|
onLoadSuccess,
|
|
73
83
|
onLoadError
|
|
74
84
|
}) {
|
|
75
|
-
const highlighterRef = (0,
|
|
76
|
-
const loadNotifiedRef = (0,
|
|
77
|
-
const highlights = (0,
|
|
85
|
+
const highlighterRef = (0, import_react3.useRef)(null);
|
|
86
|
+
const loadNotifiedRef = (0, import_react3.useRef)(false);
|
|
87
|
+
const highlights = (0, import_react3.useMemo)(() => {
|
|
78
88
|
const h = bboxToHighlight(target);
|
|
79
89
|
return h ? [h] : [];
|
|
80
90
|
}, [target]);
|
|
81
|
-
(0,
|
|
91
|
+
(0, import_react3.useEffect)(() => {
|
|
82
92
|
if (highlights.length === 0) return;
|
|
83
93
|
const timer = setTimeout(() => {
|
|
84
94
|
highlighterRef.current?.scrollToHighlight(highlights[0]);
|
|
85
95
|
}, 400);
|
|
86
96
|
return () => clearTimeout(timer);
|
|
87
97
|
}, [highlights]);
|
|
88
|
-
const handleUtilsRef = (0,
|
|
98
|
+
const handleUtilsRef = (0, import_react3.useCallback)((utils) => {
|
|
89
99
|
highlighterRef.current = utils;
|
|
90
100
|
}, []);
|
|
91
|
-
return /* @__PURE__ */ (0,
|
|
101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "100%", height: "100%", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
92
102
|
import_react_pdf_highlighter_extended.PdfLoader,
|
|
93
103
|
{
|
|
94
104
|
document: target.url,
|
|
95
105
|
workerSrc: workerUrl ?? DEFAULT_WORKER_URL,
|
|
96
|
-
beforeLoad: () => loadingComponent ?? /* @__PURE__ */ (0,
|
|
106
|
+
beforeLoad: () => loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
97
107
|
"div",
|
|
98
108
|
{
|
|
99
109
|
style: {
|
|
@@ -111,7 +121,7 @@ function PdfRenderer({
|
|
|
111
121
|
),
|
|
112
122
|
errorMessage: (error) => {
|
|
113
123
|
onLoadError?.(error);
|
|
114
|
-
return errorComponent ?? /* @__PURE__ */ (0,
|
|
124
|
+
return errorComponent ?? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
115
125
|
"div",
|
|
116
126
|
{
|
|
117
127
|
style: {
|
|
@@ -133,7 +143,7 @@ function PdfRenderer({
|
|
|
133
143
|
loadNotifiedRef.current = true;
|
|
134
144
|
onLoadSuccess?.();
|
|
135
145
|
}
|
|
136
|
-
return /* @__PURE__ */ (0,
|
|
146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
137
147
|
import_react_pdf_highlighter_extended.PdfHighlighter,
|
|
138
148
|
{
|
|
139
149
|
pdfDocument,
|
|
@@ -143,25 +153,137 @@ function PdfRenderer({
|
|
|
143
153
|
width: "100%",
|
|
144
154
|
height: "100%"
|
|
145
155
|
},
|
|
146
|
-
children: /* @__PURE__ */ (0,
|
|
156
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CitationHighlightRenderer, {})
|
|
147
157
|
}
|
|
148
158
|
);
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
161
|
) });
|
|
152
162
|
}
|
|
153
|
-
var
|
|
163
|
+
var import_react3, import_react_pdf_highlighter_extended, import_jsx_runtime4, CITATION_ID, DEFAULT_WORKER_URL;
|
|
154
164
|
var init_PdfRenderer = __esm({
|
|
155
165
|
"src/components/renderers/PdfRenderer.tsx"() {
|
|
156
166
|
"use strict";
|
|
157
|
-
|
|
167
|
+
import_react3 = require("react");
|
|
158
168
|
import_react_pdf_highlighter_extended = require("react-pdf-highlighter-extended");
|
|
159
|
-
|
|
169
|
+
import_jsx_runtime4 = require("react/jsx-runtime");
|
|
160
170
|
CITATION_ID = "citation-highlight";
|
|
161
171
|
DEFAULT_WORKER_URL = "https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.min.mjs";
|
|
162
172
|
}
|
|
163
173
|
});
|
|
164
174
|
|
|
175
|
+
// src/components/renderers/ImageRenderer.tsx
|
|
176
|
+
var ImageRenderer_exports = {};
|
|
177
|
+
__export(ImageRenderer_exports, {
|
|
178
|
+
ImageRenderer: () => ImageRenderer
|
|
179
|
+
});
|
|
180
|
+
function ImageRenderer({
|
|
181
|
+
target,
|
|
182
|
+
loadingComponent,
|
|
183
|
+
errorComponent,
|
|
184
|
+
onLoadSuccess,
|
|
185
|
+
onLoadError
|
|
186
|
+
}) {
|
|
187
|
+
const [status, setStatus] = (0, import_react4.useState)("loading");
|
|
188
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
189
|
+
const handleLoad = (0, import_react4.useCallback)(() => {
|
|
190
|
+
setStatus("success");
|
|
191
|
+
onLoadSuccess?.();
|
|
192
|
+
}, [onLoadSuccess]);
|
|
193
|
+
const handleError = (0, import_react4.useCallback)(() => {
|
|
194
|
+
const err = new Error("Failed to load image");
|
|
195
|
+
setError(err);
|
|
196
|
+
setStatus("error");
|
|
197
|
+
onLoadError?.(err);
|
|
198
|
+
}, [onLoadError]);
|
|
199
|
+
if (status === "error") {
|
|
200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: containerStyle3, children: errorComponent ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
201
|
+
"div",
|
|
202
|
+
{
|
|
203
|
+
style: {
|
|
204
|
+
color: "#ef4444",
|
|
205
|
+
fontSize: 14,
|
|
206
|
+
fontFamily: 'system-ui, "Segoe UI", Roboto, sans-serif'
|
|
207
|
+
},
|
|
208
|
+
children: "Failed to load image"
|
|
209
|
+
}
|
|
210
|
+
) });
|
|
211
|
+
}
|
|
212
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: containerStyle3, children: [
|
|
213
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
214
|
+
import_react_zoom_pan_pinch.TransformWrapper,
|
|
215
|
+
{
|
|
216
|
+
initialScale: 1,
|
|
217
|
+
minScale: 0.5,
|
|
218
|
+
maxScale: 8,
|
|
219
|
+
centerOnInit: true,
|
|
220
|
+
limitToBounds: false,
|
|
221
|
+
panning: { velocityDisabled: true },
|
|
222
|
+
doubleClick: { mode: "reset" },
|
|
223
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
224
|
+
import_react_zoom_pan_pinch.TransformComponent,
|
|
225
|
+
{
|
|
226
|
+
wrapperStyle: { width: "100%", height: "100%", cursor: "grab" },
|
|
227
|
+
contentStyle: containerStyle3,
|
|
228
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
229
|
+
"img",
|
|
230
|
+
{
|
|
231
|
+
src: target.url,
|
|
232
|
+
alt: target.title ?? "Document",
|
|
233
|
+
style: imageStyle,
|
|
234
|
+
onLoad: handleLoad,
|
|
235
|
+
onError: handleError,
|
|
236
|
+
draggable: false
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
),
|
|
243
|
+
status === "loading" && (loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
244
|
+
"div",
|
|
245
|
+
{
|
|
246
|
+
style: {
|
|
247
|
+
position: "absolute",
|
|
248
|
+
inset: 0,
|
|
249
|
+
display: "flex",
|
|
250
|
+
alignItems: "center",
|
|
251
|
+
justifyContent: "center",
|
|
252
|
+
color: "#888",
|
|
253
|
+
fontSize: 14,
|
|
254
|
+
fontFamily: 'system-ui, "Segoe UI", Roboto, sans-serif'
|
|
255
|
+
},
|
|
256
|
+
children: "Loading image\u2026"
|
|
257
|
+
}
|
|
258
|
+
))
|
|
259
|
+
] });
|
|
260
|
+
}
|
|
261
|
+
var import_react4, import_react_zoom_pan_pinch, import_jsx_runtime5, containerStyle3, imageStyle;
|
|
262
|
+
var init_ImageRenderer = __esm({
|
|
263
|
+
"src/components/renderers/ImageRenderer.tsx"() {
|
|
264
|
+
"use strict";
|
|
265
|
+
import_react4 = require("react");
|
|
266
|
+
import_react_zoom_pan_pinch = require("react-zoom-pan-pinch");
|
|
267
|
+
import_jsx_runtime5 = require("react/jsx-runtime");
|
|
268
|
+
containerStyle3 = {
|
|
269
|
+
position: "relative",
|
|
270
|
+
width: "100%",
|
|
271
|
+
height: "100%",
|
|
272
|
+
display: "flex",
|
|
273
|
+
alignItems: "center",
|
|
274
|
+
justifyContent: "center",
|
|
275
|
+
overflow: "hidden",
|
|
276
|
+
background: "#1a1a1a"
|
|
277
|
+
};
|
|
278
|
+
imageStyle = {
|
|
279
|
+
maxWidth: "100%",
|
|
280
|
+
maxHeight: "100%",
|
|
281
|
+
objectFit: "contain",
|
|
282
|
+
display: "block"
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
165
287
|
// src/index.ts
|
|
166
288
|
var index_exports = {};
|
|
167
289
|
__export(index_exports, {
|
|
@@ -171,13 +293,19 @@ __export(index_exports, {
|
|
|
171
293
|
module.exports = __toCommonJS(index_exports);
|
|
172
294
|
|
|
173
295
|
// src/components/DocumentViewer.tsx
|
|
174
|
-
var
|
|
296
|
+
var import_react5 = require("react");
|
|
175
297
|
|
|
176
298
|
// src/utils/getFileType.ts
|
|
299
|
+
var IMAGE_EXT = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".avif", ".svg"];
|
|
300
|
+
var JSON_EXT = [".json"];
|
|
301
|
+
var EML_EXT = [".eml"];
|
|
177
302
|
function getFileType(url) {
|
|
178
303
|
const clean = url.split("?")[0].split("#")[0].toLowerCase();
|
|
179
304
|
if (clean.endsWith(".pdf")) return "pdf";
|
|
180
305
|
if (clean.endsWith(".docx") || clean.endsWith(".doc")) return "docx";
|
|
306
|
+
if (IMAGE_EXT.some((e) => clean.endsWith(e))) return "image";
|
|
307
|
+
if (JSON_EXT.some((e) => clean.endsWith(e))) return "json";
|
|
308
|
+
if (EML_EXT.some((e) => clean.endsWith(e))) return "eml";
|
|
181
309
|
return "unknown";
|
|
182
310
|
}
|
|
183
311
|
|
|
@@ -203,21 +331,397 @@ function DocxRenderer({ target }) {
|
|
|
203
331
|
);
|
|
204
332
|
}
|
|
205
333
|
|
|
206
|
-
// src/components/
|
|
334
|
+
// src/components/renderers/JsonRenderer.tsx
|
|
335
|
+
var import_react = require("react");
|
|
336
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
337
|
+
var containerStyle = {
|
|
338
|
+
width: "100%",
|
|
339
|
+
height: "100%",
|
|
340
|
+
overflow: "auto",
|
|
341
|
+
background: "#0d1117",
|
|
342
|
+
padding: 16,
|
|
343
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
344
|
+
fontSize: 13,
|
|
345
|
+
lineHeight: 1.5
|
|
346
|
+
};
|
|
347
|
+
function formatKey(key) {
|
|
348
|
+
return JSON.stringify(key);
|
|
349
|
+
}
|
|
350
|
+
function JsonTree({ node, depth = 0 }) {
|
|
351
|
+
const [open, setOpen] = (0, import_react.useState)(
|
|
352
|
+
node.type === "array" || node.type === "object" ? node.open : true
|
|
353
|
+
);
|
|
354
|
+
const isOpen = node.type === "array" || node.type === "object" ? open : true;
|
|
355
|
+
const indent = depth * 18;
|
|
356
|
+
if (node.type === "string") {
|
|
357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
358
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#79c0ff" }, children: formatKey(node.key) }),
|
|
359
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6e7681" }, children: ": " }),
|
|
360
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#a5d6ff" }, children: JSON.stringify(node.value) })
|
|
361
|
+
] });
|
|
362
|
+
}
|
|
363
|
+
if (node.type === "number") {
|
|
364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
365
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#79c0ff" }, children: formatKey(node.key) }),
|
|
366
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6e7681" }, children: ": " }),
|
|
367
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#79c0ff" }, children: node.value })
|
|
368
|
+
] });
|
|
369
|
+
}
|
|
370
|
+
if (node.type === "boolean") {
|
|
371
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
372
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#79c0ff" }, children: formatKey(node.key) }),
|
|
373
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6e7681" }, children: ": " }),
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#ff7b72" }, children: String(node.value) })
|
|
375
|
+
] });
|
|
376
|
+
}
|
|
377
|
+
if (node.type === "null") {
|
|
378
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
379
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#79c0ff" }, children: formatKey(node.key) }),
|
|
380
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6e7681" }, children: ": " }),
|
|
381
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6e7681" }, children: "null" })
|
|
382
|
+
] });
|
|
383
|
+
}
|
|
384
|
+
if (node.type === "array") {
|
|
385
|
+
const bracket = isOpen ? "[" : "[ \u2026 ]";
|
|
386
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginLeft: indent }, children: [
|
|
387
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
388
|
+
"span",
|
|
389
|
+
{
|
|
390
|
+
onClick: () => node.children.length > 0 ? setOpen(!open) : null,
|
|
391
|
+
style: {
|
|
392
|
+
cursor: node.children.length > 0 ? "pointer" : "default",
|
|
393
|
+
color: "#ffa657"
|
|
394
|
+
},
|
|
395
|
+
children: [
|
|
396
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
397
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#79c0ff" }, children: formatKey(node.key) }),
|
|
398
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6e7681" }, children: ": " })
|
|
399
|
+
] }),
|
|
400
|
+
bracket
|
|
401
|
+
]
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
isOpen && node.children.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
405
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JsonTree, { node: child, depth: depth + 1 }),
|
|
406
|
+
i < node.children.length - 1 && ","
|
|
407
|
+
] }, i)),
|
|
408
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#ffa657" }, children: "]" })
|
|
409
|
+
] });
|
|
410
|
+
}
|
|
411
|
+
if (node.type === "object") {
|
|
412
|
+
const brace = isOpen ? "{" : "{ \u2026 }";
|
|
413
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginLeft: indent }, children: [
|
|
414
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
415
|
+
"span",
|
|
416
|
+
{
|
|
417
|
+
onClick: () => node.children.length > 0 ? setOpen(!open) : null,
|
|
418
|
+
style: {
|
|
419
|
+
cursor: node.children.length > 0 ? "pointer" : "default",
|
|
420
|
+
color: "#ffa657"
|
|
421
|
+
},
|
|
422
|
+
children: [
|
|
423
|
+
node.key != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
424
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#79c0ff" }, children: formatKey(node.key) }),
|
|
425
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6e7681" }, children: ": " })
|
|
426
|
+
] }),
|
|
427
|
+
brace
|
|
428
|
+
]
|
|
429
|
+
}
|
|
430
|
+
),
|
|
431
|
+
isOpen && node.children.map(({ key, value }, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
432
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JsonTree, { node: { ...value, key }, depth: depth + 1 }),
|
|
433
|
+
i < node.children.length - 1 && ","
|
|
434
|
+
] }, key)),
|
|
435
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#ffa657" }, children: "}" })
|
|
436
|
+
] });
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
function parseToTree(value) {
|
|
441
|
+
if (value === null) return { type: "null" };
|
|
442
|
+
if (typeof value === "string") return { type: "string", value };
|
|
443
|
+
if (typeof value === "number") return { type: "number", value };
|
|
444
|
+
if (typeof value === "boolean") return { type: "boolean", value };
|
|
445
|
+
if (Array.isArray(value)) {
|
|
446
|
+
return {
|
|
447
|
+
type: "array",
|
|
448
|
+
children: value.map((v) => parseToTree(v)),
|
|
449
|
+
open: value.length <= 3
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
if (typeof value === "object" && value !== null) {
|
|
453
|
+
const obj = value;
|
|
454
|
+
return {
|
|
455
|
+
type: "object",
|
|
456
|
+
children: Object.entries(obj).map(([k, v]) => ({ key: k, value: parseToTree(v) })),
|
|
457
|
+
open: Object.keys(obj).length <= 5
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return { type: "null" };
|
|
461
|
+
}
|
|
462
|
+
function JsonRenderer({
|
|
463
|
+
target,
|
|
464
|
+
loadingComponent,
|
|
465
|
+
errorComponent,
|
|
466
|
+
onLoadSuccess,
|
|
467
|
+
onLoadError
|
|
468
|
+
}) {
|
|
469
|
+
const [state, setState] = (0, import_react.useState)({ status: "loading" });
|
|
470
|
+
const load = (0, import_react.useCallback)(async () => {
|
|
471
|
+
try {
|
|
472
|
+
const res = await fetch(target.url, { credentials: "omit" });
|
|
473
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
474
|
+
const raw = await res.text();
|
|
475
|
+
const data = JSON.parse(raw);
|
|
476
|
+
setState({ status: "success", tree: parseToTree(data) });
|
|
477
|
+
onLoadSuccess?.();
|
|
478
|
+
} catch (err) {
|
|
479
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
480
|
+
setState({ status: "error", error });
|
|
481
|
+
onLoadError?.(error);
|
|
482
|
+
}
|
|
483
|
+
}, [target.url, onLoadSuccess, onLoadError]);
|
|
484
|
+
(0, import_react.useEffect)(() => {
|
|
485
|
+
load();
|
|
486
|
+
}, [load]);
|
|
487
|
+
if (state.status === "loading") {
|
|
488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: containerStyle, children: loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#8b949e" }, children: "Loading JSON\u2026" }) });
|
|
489
|
+
}
|
|
490
|
+
if (state.status === "error") {
|
|
491
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: containerStyle, children: errorComponent ?? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { color: "#f85149" }, children: [
|
|
492
|
+
"Failed to load JSON: ",
|
|
493
|
+
state.error.message
|
|
494
|
+
] }) });
|
|
495
|
+
}
|
|
496
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JsonTree, { node: state.tree }) });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/components/renderers/EmlRenderer.tsx
|
|
500
|
+
var import_react2 = require("react");
|
|
501
|
+
var import_dompurify = __toESM(require("dompurify"));
|
|
207
502
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
208
|
-
var
|
|
503
|
+
var containerStyle2 = {
|
|
504
|
+
width: "100%",
|
|
505
|
+
height: "100%",
|
|
506
|
+
overflow: "auto",
|
|
507
|
+
background: "#fff",
|
|
508
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
509
|
+
fontSize: 14,
|
|
510
|
+
lineHeight: 1.5
|
|
511
|
+
};
|
|
512
|
+
function parseEml(raw) {
|
|
513
|
+
const lines = raw.split(/\r?\n/);
|
|
514
|
+
const headers = [];
|
|
515
|
+
let i = 0;
|
|
516
|
+
let currentKey = null;
|
|
517
|
+
let currentValue = [];
|
|
518
|
+
while (i < lines.length) {
|
|
519
|
+
const line = lines[i];
|
|
520
|
+
if (line === "") {
|
|
521
|
+
i++;
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
525
|
+
if (match) {
|
|
526
|
+
if (currentKey) {
|
|
527
|
+
headers.push({ key: currentKey, value: currentValue.join(" ").trim() });
|
|
528
|
+
}
|
|
529
|
+
currentKey = match[1];
|
|
530
|
+
currentValue = [match[2]];
|
|
531
|
+
} else if (currentKey && /^\s/.test(line)) {
|
|
532
|
+
currentValue.push(line.trim());
|
|
533
|
+
}
|
|
534
|
+
i++;
|
|
535
|
+
}
|
|
536
|
+
if (currentKey) {
|
|
537
|
+
headers.push({ key: currentKey, value: currentValue.join(" ").trim() });
|
|
538
|
+
}
|
|
539
|
+
const rest = lines.slice(i).join("\n");
|
|
540
|
+
const contentType = headers.find(
|
|
541
|
+
(h) => h.key.toLowerCase() === "content-type"
|
|
542
|
+
)?.value ?? "text/plain";
|
|
543
|
+
const isMultipart = contentType.toLowerCase().includes("multipart");
|
|
544
|
+
let bodyText = null;
|
|
545
|
+
let bodyHtml = null;
|
|
546
|
+
if (!isMultipart) {
|
|
547
|
+
bodyText = rest.trim() || null;
|
|
548
|
+
if (contentType.toLowerCase().includes("text/html")) {
|
|
549
|
+
bodyHtml = bodyText;
|
|
550
|
+
bodyText = null;
|
|
551
|
+
}
|
|
552
|
+
return { headers, bodyText, bodyHtml };
|
|
553
|
+
}
|
|
554
|
+
const boundaryMatch = contentType.match(/boundary\s*=\s*["']?([^"'\s;]+)["']?/i);
|
|
555
|
+
const boundary = boundaryMatch?.[1]?.trim();
|
|
556
|
+
if (!boundary) {
|
|
557
|
+
bodyText = rest.trim() || null;
|
|
558
|
+
return { headers, bodyText, bodyHtml: null };
|
|
559
|
+
}
|
|
560
|
+
const parts = rest.split(new RegExp(`\\r?\\n--${escapeRegExp(boundary)}(?:\\r?\\n|--)`));
|
|
561
|
+
for (const part of parts) {
|
|
562
|
+
if (!part.trim()) continue;
|
|
563
|
+
const [head, ...bodyLines] = part.split(/\r?\n\r?\n/);
|
|
564
|
+
const body = bodyLines.join("\n\n").trim();
|
|
565
|
+
const partType = (head.match(/Content-Type:\s*([^;\s]+)/i) ?? [])[1]?.toLowerCase();
|
|
566
|
+
if (partType?.includes("text/plain") && !bodyText) bodyText = body;
|
|
567
|
+
if (partType?.includes("text/html") && !bodyHtml) bodyHtml = body;
|
|
568
|
+
}
|
|
569
|
+
if (!bodyText && !bodyHtml && parts[1]) {
|
|
570
|
+
const [head, ...bodyLines] = parts[1].split(/\r?\n\r?\n/);
|
|
571
|
+
const body = bodyLines.join("\n\n").trim();
|
|
572
|
+
const partType = (head.match(/Content-Type:\s*([^;\s]+)/i) ?? [])[1]?.toLowerCase();
|
|
573
|
+
if (partType?.includes("text/html")) bodyHtml = body;
|
|
574
|
+
else bodyText = body;
|
|
575
|
+
}
|
|
576
|
+
return { headers, bodyText, bodyHtml };
|
|
577
|
+
}
|
|
578
|
+
function escapeRegExp(s) {
|
|
579
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
580
|
+
}
|
|
581
|
+
var HEADER_KEYS = ["from", "to", "cc", "bcc", "subject", "date", "reply-to"];
|
|
582
|
+
function EmlRenderer({
|
|
583
|
+
target,
|
|
584
|
+
loadingComponent,
|
|
585
|
+
errorComponent,
|
|
586
|
+
onLoadSuccess,
|
|
587
|
+
onLoadError
|
|
588
|
+
}) {
|
|
589
|
+
const [state, setState] = (0, import_react2.useState)({ status: "loading" });
|
|
590
|
+
const load = (0, import_react2.useCallback)(async () => {
|
|
591
|
+
try {
|
|
592
|
+
const res = await fetch(target.url, { credentials: "omit" });
|
|
593
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
594
|
+
const raw = await res.text();
|
|
595
|
+
const data = parseEml(raw);
|
|
596
|
+
setState({ status: "success", data });
|
|
597
|
+
onLoadSuccess?.();
|
|
598
|
+
} catch (err) {
|
|
599
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
600
|
+
setState({ status: "error", error });
|
|
601
|
+
onLoadError?.(error);
|
|
602
|
+
}
|
|
603
|
+
}, [target.url, onLoadSuccess, onLoadError]);
|
|
604
|
+
(0, import_react2.useEffect)(() => {
|
|
605
|
+
load();
|
|
606
|
+
}, [load]);
|
|
607
|
+
if (state.status === "loading") {
|
|
608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: containerStyle2, children: loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: 24, color: "#666" }, children: "Loading email\u2026" }) });
|
|
609
|
+
}
|
|
610
|
+
if (state.status === "error") {
|
|
611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: containerStyle2, children: errorComponent ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: 24, color: "#dc2626" }, children: [
|
|
612
|
+
"Failed to load email: ",
|
|
613
|
+
state.error.message
|
|
614
|
+
] }) });
|
|
615
|
+
}
|
|
616
|
+
const { headers, bodyText, bodyHtml } = state.data;
|
|
617
|
+
const preferredHeaders = HEADER_KEYS.filter(
|
|
618
|
+
(k) => headers.some((h) => h.key.toLowerCase() === k)
|
|
619
|
+
);
|
|
620
|
+
const otherHeaders = headers.filter(
|
|
621
|
+
(h) => !HEADER_KEYS.includes(h.key.toLowerCase())
|
|
622
|
+
);
|
|
623
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: containerStyle2, children: [
|
|
624
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: 24, borderBottom: "1px solid #e5e7eb" }, children: [
|
|
625
|
+
preferredHeaders.map((key) => {
|
|
626
|
+
const h = headers.find((x) => x.key.toLowerCase() === key);
|
|
627
|
+
if (!h) return null;
|
|
628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: 8 }, children: [
|
|
629
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { color: "#6b7280", fontWeight: 600, marginRight: 8 }, children: [
|
|
630
|
+
h.key,
|
|
631
|
+
":"
|
|
632
|
+
] }),
|
|
633
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#111" }, children: h.value })
|
|
634
|
+
] }, h.key);
|
|
635
|
+
}),
|
|
636
|
+
otherHeaders.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("details", { style: { marginTop: 12 }, children: [
|
|
637
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("summary", { style: { color: "#6b7280", cursor: "pointer", fontSize: 13 }, children: "Other headers" }),
|
|
638
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: 8 }, children: otherHeaders.map((h) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: 4, fontSize: 13 }, children: [
|
|
639
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { color: "#6b7280" }, children: [
|
|
640
|
+
h.key,
|
|
641
|
+
": "
|
|
642
|
+
] }),
|
|
643
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#374151" }, children: h.value })
|
|
644
|
+
] }, h.key)) })
|
|
645
|
+
] })
|
|
646
|
+
] }),
|
|
647
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: 24 }, children: bodyHtml ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
648
|
+
"div",
|
|
649
|
+
{
|
|
650
|
+
className: "document-viewer-eml-body",
|
|
651
|
+
style: { maxWidth: 720, overflow: "auto" },
|
|
652
|
+
dangerouslySetInnerHTML: {
|
|
653
|
+
__html: import_dompurify.default.sanitize(bodyHtml, {
|
|
654
|
+
ALLOWED_TAGS: [
|
|
655
|
+
"p",
|
|
656
|
+
"br",
|
|
657
|
+
"div",
|
|
658
|
+
"span",
|
|
659
|
+
"a",
|
|
660
|
+
"strong",
|
|
661
|
+
"em",
|
|
662
|
+
"u",
|
|
663
|
+
"b",
|
|
664
|
+
"i",
|
|
665
|
+
"ul",
|
|
666
|
+
"ol",
|
|
667
|
+
"li",
|
|
668
|
+
"h1",
|
|
669
|
+
"h2",
|
|
670
|
+
"h3",
|
|
671
|
+
"h4",
|
|
672
|
+
"table",
|
|
673
|
+
"tr",
|
|
674
|
+
"td",
|
|
675
|
+
"th",
|
|
676
|
+
"tbody",
|
|
677
|
+
"thead",
|
|
678
|
+
"img",
|
|
679
|
+
"blockquote",
|
|
680
|
+
"hr",
|
|
681
|
+
"pre",
|
|
682
|
+
"code"
|
|
683
|
+
],
|
|
684
|
+
ALLOWED_ATTR: ["href", "src", "alt", "title", "target"]
|
|
685
|
+
})
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
) : bodyText ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
689
|
+
"pre",
|
|
690
|
+
{
|
|
691
|
+
style: {
|
|
692
|
+
margin: 0,
|
|
693
|
+
whiteSpace: "pre-wrap",
|
|
694
|
+
wordBreak: "break-word",
|
|
695
|
+
color: "#374151",
|
|
696
|
+
fontFamily: "inherit",
|
|
697
|
+
fontSize: "inherit"
|
|
698
|
+
},
|
|
699
|
+
children: bodyText
|
|
700
|
+
}
|
|
701
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "#6b7280" }, children: "No body content" }) })
|
|
702
|
+
] });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// src/components/DocumentViewer.tsx
|
|
706
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
707
|
+
var LazyPdfRenderer = (0, import_react5.lazy)(
|
|
209
708
|
() => Promise.resolve().then(() => (init_PdfRenderer(), PdfRenderer_exports)).then((mod) => ({
|
|
210
709
|
default: mod.PdfRenderer
|
|
211
710
|
}))
|
|
212
711
|
);
|
|
712
|
+
var LazyImageRenderer = (0, import_react5.lazy)(
|
|
713
|
+
() => Promise.resolve().then(() => (init_ImageRenderer(), ImageRenderer_exports)).then((mod) => ({
|
|
714
|
+
default: mod.ImageRenderer
|
|
715
|
+
}))
|
|
716
|
+
);
|
|
213
717
|
function ClientOnly({
|
|
214
718
|
children,
|
|
215
719
|
fallback
|
|
216
720
|
}) {
|
|
217
|
-
const [mounted, setMounted] = (0,
|
|
218
|
-
(0,
|
|
219
|
-
if (!mounted) return /* @__PURE__ */ (0,
|
|
220
|
-
return /* @__PURE__ */ (0,
|
|
721
|
+
const [mounted, setMounted] = (0, import_react5.useState)(false);
|
|
722
|
+
(0, import_react5.useEffect)(() => setMounted(true), []);
|
|
723
|
+
if (!mounted) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: fallback ?? null });
|
|
724
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children });
|
|
221
725
|
}
|
|
222
726
|
function DocumentViewer({
|
|
223
727
|
target,
|
|
@@ -239,7 +743,7 @@ function DocumentViewer({
|
|
|
239
743
|
borderRadius: 8,
|
|
240
744
|
background: "#fafafa"
|
|
241
745
|
};
|
|
242
|
-
const loadingFallback = loadingComponent ?? /* @__PURE__ */ (0,
|
|
746
|
+
const loadingFallback = loadingComponent ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
243
747
|
"div",
|
|
244
748
|
{
|
|
245
749
|
style: {
|
|
@@ -255,8 +759,8 @@ function DocumentViewer({
|
|
|
255
759
|
children: "Loading\u2026"
|
|
256
760
|
}
|
|
257
761
|
);
|
|
258
|
-
return /* @__PURE__ */ (0,
|
|
259
|
-
type === "pdf" && /* @__PURE__ */ (0,
|
|
762
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className, style: wrapperStyle, children: [
|
|
763
|
+
type === "pdf" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ClientOnly, { fallback: loadingFallback, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react5.Suspense, { fallback: loadingFallback, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
260
764
|
LazyPdfRenderer,
|
|
261
765
|
{
|
|
262
766
|
target,
|
|
@@ -267,8 +771,38 @@ function DocumentViewer({
|
|
|
267
771
|
onLoadError
|
|
268
772
|
}
|
|
269
773
|
) }) }),
|
|
270
|
-
type === "docx" && /* @__PURE__ */ (0,
|
|
271
|
-
type === "
|
|
774
|
+
type === "docx" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DocxRenderer, { target }),
|
|
775
|
+
type === "image" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ClientOnly, { fallback: loadingFallback, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react5.Suspense, { fallback: loadingFallback, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
776
|
+
LazyImageRenderer,
|
|
777
|
+
{
|
|
778
|
+
target,
|
|
779
|
+
loadingComponent,
|
|
780
|
+
errorComponent,
|
|
781
|
+
onLoadSuccess,
|
|
782
|
+
onLoadError
|
|
783
|
+
}
|
|
784
|
+
) }) }),
|
|
785
|
+
type === "json" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
786
|
+
JsonRenderer,
|
|
787
|
+
{
|
|
788
|
+
target,
|
|
789
|
+
loadingComponent,
|
|
790
|
+
errorComponent,
|
|
791
|
+
onLoadSuccess,
|
|
792
|
+
onLoadError
|
|
793
|
+
}
|
|
794
|
+
),
|
|
795
|
+
type === "eml" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
796
|
+
EmlRenderer,
|
|
797
|
+
{
|
|
798
|
+
target,
|
|
799
|
+
loadingComponent,
|
|
800
|
+
errorComponent,
|
|
801
|
+
onLoadSuccess,
|
|
802
|
+
onLoadError
|
|
803
|
+
}
|
|
804
|
+
),
|
|
805
|
+
type === "unknown" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
272
806
|
"div",
|
|
273
807
|
{
|
|
274
808
|
style: {
|