@eternalheart/react-file-preview 1.4.0 → 1.5.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/README.md +437 -60
- package/README.zh-CN.md +437 -60
- package/lib/FilePreviewContent.d.ts +1 -0
- package/lib/FilePreviewContent.d.ts.map +1 -1
- package/lib/FilePreviewEmbed.d.ts +2 -0
- package/lib/FilePreviewEmbed.d.ts.map +1 -1
- package/lib/FilePreviewModal.d.ts +2 -0
- package/lib/FilePreviewModal.d.ts.map +1 -1
- package/lib/chunks/index-2sX2d4iv.mjs +291 -0
- package/lib/chunks/index-2sX2d4iv.mjs.map +1 -0
- package/lib/chunks/index-Bdj8_B80.mjs +120 -0
- package/lib/chunks/index-Bdj8_B80.mjs.map +1 -0
- package/lib/chunks/index-CCcZzLUM.mjs +107 -0
- package/lib/chunks/index-CCcZzLUM.mjs.map +1 -0
- package/lib/chunks/{index-CzM2mxrD.mjs → index-CKdQL1Bk.mjs} +130 -128
- package/lib/chunks/{index-CzM2mxrD.mjs.map → index-CKdQL1Bk.mjs.map} +1 -1
- package/lib/chunks/index-CQYrhe7Z.mjs +275 -0
- package/lib/chunks/index-CQYrhe7Z.mjs.map +1 -0
- package/lib/chunks/{index-DuP0Tlpo.mjs → index-CRZqNMQ7.mjs} +43 -41
- package/lib/chunks/index-CRZqNMQ7.mjs.map +1 -0
- package/lib/chunks/{index-Cp68OevR.mjs → index-CTghYlSh.mjs} +1299 -1297
- package/lib/chunks/{index-Cp68OevR.mjs.map → index-CTghYlSh.mjs.map} +1 -1
- package/lib/chunks/index-CuTz7dbd.mjs +313 -0
- package/lib/chunks/index-CuTz7dbd.mjs.map +1 -0
- package/lib/chunks/index-CuWzRQZw.mjs +116 -0
- package/lib/chunks/index-CuWzRQZw.mjs.map +1 -0
- package/lib/chunks/index-Cz23v-TW.mjs +2409 -0
- package/lib/chunks/index-Cz23v-TW.mjs.map +1 -0
- package/lib/chunks/{index-kALp0tqz.mjs → index-D-Is8qvU.mjs} +22 -20
- package/lib/chunks/index-D-Is8qvU.mjs.map +1 -0
- package/lib/chunks/index-Da3FN2-3.mjs +359 -0
- package/lib/chunks/index-Da3FN2-3.mjs.map +1 -0
- package/lib/chunks/index-Dc6q1OKl.mjs +78 -0
- package/lib/chunks/index-Dc6q1OKl.mjs.map +1 -0
- package/lib/chunks/{index-10O8tfTH.mjs → index-DoGKcq9y.mjs} +194 -192
- package/lib/chunks/index-DoGKcq9y.mjs.map +1 -0
- package/lib/chunks/{index-C_BJatqr.mjs → index-DzCLf1Db.mjs} +42 -40
- package/lib/chunks/index-DzCLf1Db.mjs.map +1 -0
- package/lib/chunks/index-FomaQSaL.mjs +329 -0
- package/lib/chunks/index-FomaQSaL.mjs.map +1 -0
- package/lib/chunks/{index-DaAXRBWL.mjs → index-OXjOFggq.mjs} +864 -862
- package/lib/chunks/{index-DaAXRBWL.mjs.map → index-OXjOFggq.mjs.map} +1 -1
- package/lib/chunks/index-WLepq2g2.mjs +200 -0
- package/lib/chunks/index-WLepq2g2.mjs.map +1 -0
- package/lib/chunks/{index-DoFsoBKL.mjs → index-_B5marES.mjs} +27 -25
- package/lib/chunks/index-_B5marES.mjs.map +1 -0
- package/lib/chunks/useShikiHighlight-Bbs8Fbqs.mjs +36 -0
- package/lib/chunks/useShikiHighlight-Bbs8Fbqs.mjs.map +1 -0
- package/lib/components/preview/FilePreviewToolbar.d.ts +1 -0
- package/lib/components/preview/FilePreviewToolbar.d.ts.map +1 -1
- package/lib/components/preview/ToolbarButton.d.ts +3 -1
- package/lib/components/preview/ToolbarButton.d.ts.map +1 -1
- package/lib/hooks/index.d.ts +0 -6
- package/lib/hooks/index.d.ts.map +1 -1
- package/lib/hooks/useShikiHighlight.d.ts +3 -1
- package/lib/hooks/useShikiHighlight.d.ts.map +1 -1
- package/lib/index.cjs +32 -30
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.mjs +1 -1
- package/lib/renderers/Audio/index.d.ts +2 -1
- package/lib/renderers/Audio/index.d.ts.map +1 -1
- package/lib/renderers/Csv/index.d.ts +2 -1
- package/lib/renderers/Csv/index.d.ts.map +1 -1
- package/lib/renderers/Docx/index.d.ts +2 -1
- package/lib/renderers/Docx/index.d.ts.map +1 -1
- package/lib/renderers/Epub/index.d.ts +2 -3
- package/lib/renderers/Epub/index.d.ts.map +1 -1
- package/lib/renderers/Font/index.d.ts +2 -1
- package/lib/renderers/Font/index.d.ts.map +1 -1
- package/lib/renderers/Image/index.d.ts +6 -7
- package/lib/renderers/Image/index.d.ts.map +1 -1
- package/lib/renderers/Json/index.d.ts +2 -1
- package/lib/renderers/Json/index.d.ts.map +1 -1
- package/lib/renderers/Markdown/index.d.ts +2 -2
- package/lib/renderers/Markdown/index.d.ts.map +1 -1
- package/lib/renderers/Mobi/index.d.ts +2 -3
- package/lib/renderers/Mobi/index.d.ts.map +1 -1
- package/lib/renderers/Msg/index.d.ts +2 -1
- package/lib/renderers/Msg/index.d.ts.map +1 -1
- package/lib/renderers/Pdf/index.d.ts +4 -8
- package/lib/renderers/Pdf/index.d.ts.map +1 -1
- package/lib/renderers/Pptx/index.d.ts +2 -1
- package/lib/renderers/Pptx/index.d.ts.map +1 -1
- package/lib/renderers/Subtitle/index.d.ts +2 -1
- package/lib/renderers/Subtitle/index.d.ts.map +1 -1
- package/lib/renderers/Text/index.d.ts +2 -3
- package/lib/renderers/Text/index.d.ts.map +1 -1
- package/lib/renderers/Video/index.d.ts +2 -1
- package/lib/renderers/Video/index.d.ts.map +1 -1
- package/lib/renderers/Xlsx/index.d.ts +2 -1
- package/lib/renderers/Xlsx/index.d.ts.map +1 -1
- package/lib/renderers/Xml/index.d.ts +2 -1
- package/lib/renderers/Xml/index.d.ts.map +1 -1
- package/lib/renderers/Zip/index.d.ts +7 -2
- package/lib/renderers/Zip/index.d.ts.map +1 -1
- package/lib/renderers/base.types.d.ts +38 -0
- package/lib/renderers/base.types.d.ts.map +1 -0
- package/lib/renderers/registry.d.ts +36 -0
- package/lib/renderers/registry.d.ts.map +1 -0
- package/lib/renderers/toolbar.types.d.ts +2 -0
- package/lib/renderers/toolbar.types.d.ts.map +1 -1
- package/lib/toolbar/renderItems.d.ts.map +1 -1
- package/package.json +3 -3
- package/lib/chunks/index-0v5STX5f.mjs +0 -105
- package/lib/chunks/index-0v5STX5f.mjs.map +0 -1
- package/lib/chunks/index-10O8tfTH.mjs.map +0 -1
- package/lib/chunks/index-BCyv1HM9.mjs +0 -175
- package/lib/chunks/index-BCyv1HM9.mjs.map +0 -1
- package/lib/chunks/index-Bo90aGhy.mjs +0 -114
- package/lib/chunks/index-Bo90aGhy.mjs.map +0 -1
- package/lib/chunks/index-CEeKt7L3.mjs +0 -2808
- package/lib/chunks/index-CEeKt7L3.mjs.map +0 -1
- package/lib/chunks/index-CWKbnvW6.mjs +0 -270
- package/lib/chunks/index-CWKbnvW6.mjs.map +0 -1
- package/lib/chunks/index-C_BJatqr.mjs.map +0 -1
- package/lib/chunks/index-Cbz5Z6ZK.mjs +0 -263
- package/lib/chunks/index-Cbz5Z6ZK.mjs.map +0 -1
- package/lib/chunks/index-DTYBFuAH.mjs +0 -357
- package/lib/chunks/index-DTYBFuAH.mjs.map +0 -1
- package/lib/chunks/index-DoFsoBKL.mjs.map +0 -1
- package/lib/chunks/index-DuP0Tlpo.mjs.map +0 -1
- package/lib/chunks/index-Dv3RQz86.mjs +0 -270
- package/lib/chunks/index-Dv3RQz86.mjs.map +0 -1
- package/lib/chunks/index-QfpHck8N.mjs +0 -55
- package/lib/chunks/index-QfpHck8N.mjs.map +0 -1
- package/lib/chunks/index-gjSQeou7.mjs +0 -194
- package/lib/chunks/index-gjSQeou7.mjs.map +0 -1
- package/lib/chunks/index-kALp0tqz.mjs.map +0 -1
- package/lib/chunks/index-kCeSnFs-.mjs +0 -54
- package/lib/chunks/index-kCeSnFs-.mjs.map +0 -1
- package/lib/chunks/useShikiHighlight-BA9qgdGA.mjs +0 -23
- package/lib/chunks/useShikiHighlight-BA9qgdGA.mjs.map +0 -1
- package/lib/hooks/rendererReducer.d.ts +0 -10
- package/lib/hooks/rendererReducer.d.ts.map +0 -1
- package/lib/hooks/types.d.ts +0 -152
- package/lib/hooks/types.d.ts.map +0 -1
- package/lib/hooks/useBookRenderer.d.ts +0 -14
- package/lib/hooks/useBookRenderer.d.ts.map +0 -1
- package/lib/hooks/useFilePreviewState.d.ts +0 -10
- package/lib/hooks/useFilePreviewState.d.ts.map +0 -1
- package/lib/hooks/useImageAutoFit.d.ts +0 -13
- package/lib/hooks/useImageAutoFit.d.ts.map +0 -1
- package/lib/hooks/useToolbarConfig.d.ts +0 -25
- package/lib/hooks/useToolbarConfig.d.ts.map +0 -1
- package/lib/renderers/Epub/toolbar.d.ts +0 -13
- package/lib/renderers/Epub/toolbar.d.ts.map +0 -1
- package/lib/renderers/Image/toolbar.d.ts +0 -15
- package/lib/renderers/Image/toolbar.d.ts.map +0 -1
- package/lib/renderers/Markdown/toolbar.d.ts +0 -9
- package/lib/renderers/Markdown/toolbar.d.ts.map +0 -1
- package/lib/renderers/Mobi/toolbar.d.ts +0 -13
- package/lib/renderers/Mobi/toolbar.d.ts.map +0 -1
- package/lib/renderers/Pdf/toolbar.d.ts +0 -16
- package/lib/renderers/Pdf/toolbar.d.ts.map +0 -1
- package/lib/renderers/Text/toolbar.d.ts +0 -12
- package/lib/renderers/Text/toolbar.d.ts.map +0 -1
- package/lib/renderers/Zip/toolbar.d.ts +0 -13
- package/lib/renderers/Zip/toolbar.d.ts.map +0 -1
- package/lib/toolbar/registry.d.ts +0 -51
- package/lib/toolbar/registry.d.ts.map +0 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { jsxs as v, jsx as e, Fragment as _ } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as V, useRef as q, useState as o, useImperativeHandle as K, useEffect as k, useCallback as S, useMemo as J, Suspense as fe, lazy as ne } from "react";
|
|
3
|
+
import { createPortal as le } from "react-dom";
|
|
4
|
+
import { ChevronRight as se, FolderOpen as ie, Folder as pe, FileImage as oe, FileText as ae, FileCode as ce, File as de } from "lucide-react";
|
|
5
|
+
import { R as ue } from "./RendererError-D5i8eSpN.mjs";
|
|
6
|
+
import { u as me, a as he, c as Q, R as xe, N as be, D as we, A as ve, C as ye, F as ge } from "./index-Cz23v-TW.mjs";
|
|
7
|
+
const ee = V(({
|
|
8
|
+
left: t,
|
|
9
|
+
right: s,
|
|
10
|
+
initialLeftWidth: h = 280,
|
|
11
|
+
minLeftWidth: y = 160,
|
|
12
|
+
maxLeftWidth: x = 640,
|
|
13
|
+
minRightWidth: g = 200,
|
|
14
|
+
storageKey: a,
|
|
15
|
+
desktopMedia: b = "(min-width: 768px)",
|
|
16
|
+
className: c = "",
|
|
17
|
+
mobileTabMode: M = !1,
|
|
18
|
+
leftTabLabel: L = "文件树",
|
|
19
|
+
rightTabLabel: N = "预览"
|
|
20
|
+
}, z) => {
|
|
21
|
+
const d = q(null), [u, E] = o(() => {
|
|
22
|
+
if (a && typeof window < "u") {
|
|
23
|
+
const n = Number(window.localStorage.getItem(a));
|
|
24
|
+
if (!isNaN(n) && n > 0) return n;
|
|
25
|
+
}
|
|
26
|
+
return h;
|
|
27
|
+
}), [r, U] = o(!1), [R, j] = o(!1), [w, T] = o("left");
|
|
28
|
+
K(z, () => ({
|
|
29
|
+
switchTab: (n) => T(n)
|
|
30
|
+
}), []), k(() => {
|
|
31
|
+
if (typeof window > "u") return;
|
|
32
|
+
const n = window.matchMedia(b), p = () => j(n.matches);
|
|
33
|
+
return p(), n.addEventListener("change", p), () => n.removeEventListener("change", p);
|
|
34
|
+
}, [b]), k(() => {
|
|
35
|
+
if (!r) return;
|
|
36
|
+
const n = (O) => {
|
|
37
|
+
if (!d.current) return;
|
|
38
|
+
const I = d.current.getBoundingClientRect(), W = O.clientX - I.left, A = I.width - g - 6, B = Math.min(x, A), H = Math.max(y, Math.min(B, W));
|
|
39
|
+
E(H);
|
|
40
|
+
}, p = () => U(!1);
|
|
41
|
+
window.addEventListener("mousemove", n), window.addEventListener("mouseup", p);
|
|
42
|
+
const $ = document.body.style.cursor, C = document.body.style.userSelect;
|
|
43
|
+
return document.body.style.cursor = "col-resize", document.body.style.userSelect = "none", () => {
|
|
44
|
+
window.removeEventListener("mousemove", n), window.removeEventListener("mouseup", p), document.body.style.cursor = $, document.body.style.userSelect = C;
|
|
45
|
+
};
|
|
46
|
+
}, [r, y, x, g]), k(() => {
|
|
47
|
+
if (!(!a || r))
|
|
48
|
+
try {
|
|
49
|
+
window.localStorage.setItem(a, String(u));
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
}, [u, a, r]);
|
|
53
|
+
const D = S((n) => {
|
|
54
|
+
n.preventDefault(), U(!0);
|
|
55
|
+
}, []);
|
|
56
|
+
return M && !R ? /* @__PURE__ */ v(
|
|
57
|
+
"div",
|
|
58
|
+
{
|
|
59
|
+
ref: d,
|
|
60
|
+
className: `rfp-w-full rfp-h-full rfp-flex rfp-flex-col rfp-min-h-0 rfp-min-w-0 ${c}`,
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ v("div", { className: "rfp-flex rfp-flex-shrink-0 rfp-border-b rfp-border-line-weak rfp-bg-surface-toolbar", children: [
|
|
63
|
+
/* @__PURE__ */ e(
|
|
64
|
+
"button",
|
|
65
|
+
{
|
|
66
|
+
type: "button",
|
|
67
|
+
onClick: () => T("left"),
|
|
68
|
+
className: `rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${w === "left" ? "rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px" : "rfp-text-fg-secondary"}`,
|
|
69
|
+
children: L
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
/* @__PURE__ */ e(
|
|
73
|
+
"button",
|
|
74
|
+
{
|
|
75
|
+
type: "button",
|
|
76
|
+
onClick: () => T("right"),
|
|
77
|
+
className: `rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${w === "right" ? "rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px" : "rfp-text-fg-secondary"}`,
|
|
78
|
+
children: N
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ e(
|
|
83
|
+
"div",
|
|
84
|
+
{
|
|
85
|
+
className: "rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden",
|
|
86
|
+
style: { display: w === "left" ? void 0 : "none" },
|
|
87
|
+
children: t
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ e(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
className: "rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden",
|
|
94
|
+
style: { display: w === "right" ? void 0 : "none" },
|
|
95
|
+
children: s
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
) : /* @__PURE__ */ v(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
ref: d,
|
|
104
|
+
className: `rfp-w-full rfp-h-full rfp-flex rfp-flex-col md:rfp-flex-row rfp-min-h-0 rfp-min-w-0 ${c}`,
|
|
105
|
+
children: [
|
|
106
|
+
/* @__PURE__ */ e(
|
|
107
|
+
"div",
|
|
108
|
+
{
|
|
109
|
+
className: "rfp-min-h-0 rfp-min-w-0 rfp-flex-shrink-0 rfp-w-full rfp-max-h-60 md:rfp-h-full md:rfp-max-h-none",
|
|
110
|
+
style: R ? { width: `${u}px` } : void 0,
|
|
111
|
+
children: t
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
/* @__PURE__ */ e(
|
|
115
|
+
"div",
|
|
116
|
+
{
|
|
117
|
+
role: "separator",
|
|
118
|
+
"aria-orientation": "vertical",
|
|
119
|
+
onMouseDown: D,
|
|
120
|
+
className: `rfp-hidden md:rfp-block rfp-relative rfp-w-1.5 rfp-flex-shrink-0 rfp-cursor-col-resize rfp-transition-colors ${r ? "rfp-bg-surface-toolbar" : "rfp-bg-surface-2 hover:rfp-bg-surface-3"}`,
|
|
121
|
+
children: /* @__PURE__ */ e("span", { className: "rfp-absolute rfp-inset-y-0 -rfp-left-1 -rfp-right-1" })
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-min-w-0 rfp-min-h-0 rfp-overflow-hidden", children: s })
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
ee.displayName = "ResizableSplit";
|
|
130
|
+
const Ne = ne(
|
|
131
|
+
() => import("./index-Cz23v-TW.mjs").then((t) => t.i).then((t) => ({ default: t.FilePreviewContent }))
|
|
132
|
+
), ke = (t) => {
|
|
133
|
+
const s = ge({ name: t, type: "" });
|
|
134
|
+
return s === "image" ? oe : s === "text" || s === "markdown" || s === "json" || s === "csv" || s === "xml" || s === "subtitle" ? t.endsWith(".md") || t.endsWith(".markdown") ? ae : ce : de;
|
|
135
|
+
}, re = ({
|
|
136
|
+
node: t,
|
|
137
|
+
depth: s,
|
|
138
|
+
selectedPath: h,
|
|
139
|
+
expanded: y,
|
|
140
|
+
onToggle: x,
|
|
141
|
+
onSelect: g,
|
|
142
|
+
onHover: a,
|
|
143
|
+
onLeave: b
|
|
144
|
+
}) => {
|
|
145
|
+
var d;
|
|
146
|
+
const c = y.has(t.path), M = h === t.path, L = { paddingLeft: `${s * 14 + 10}px` }, N = (u) => {
|
|
147
|
+
const E = u.currentTarget.getBoundingClientRect();
|
|
148
|
+
a(t.name || "/", E);
|
|
149
|
+
};
|
|
150
|
+
if (t.isDir)
|
|
151
|
+
return /* @__PURE__ */ v(_, { children: [
|
|
152
|
+
/* @__PURE__ */ v(
|
|
153
|
+
"button",
|
|
154
|
+
{
|
|
155
|
+
type: "button",
|
|
156
|
+
onClick: () => x(t.path),
|
|
157
|
+
onMouseEnter: N,
|
|
158
|
+
onMouseLeave: b,
|
|
159
|
+
className: "rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-fg-secondary hover:rfp-bg-surface-1 rfp-text-sm",
|
|
160
|
+
style: L,
|
|
161
|
+
children: [
|
|
162
|
+
/* @__PURE__ */ e(
|
|
163
|
+
se,
|
|
164
|
+
{
|
|
165
|
+
className: `rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0 rfp-transition-transform ${c ? "rfp-rotate-90" : ""}`
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
c ? /* @__PURE__ */ e(ie, { className: "rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80" }) : /* @__PURE__ */ e(pe, { className: "rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80" }),
|
|
169
|
+
/* @__PURE__ */ e("span", { className: "rfp-truncate rfp-flex-1 rfp-min-w-0", children: t.name || "/" })
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
c && ((d = t.children) == null ? void 0 : d.map((u) => /* @__PURE__ */ e(
|
|
174
|
+
re,
|
|
175
|
+
{
|
|
176
|
+
node: u,
|
|
177
|
+
depth: s + 1,
|
|
178
|
+
selectedPath: h,
|
|
179
|
+
expanded: y,
|
|
180
|
+
onToggle: x,
|
|
181
|
+
onSelect: g,
|
|
182
|
+
onHover: a,
|
|
183
|
+
onLeave: b
|
|
184
|
+
},
|
|
185
|
+
u.path
|
|
186
|
+
)))
|
|
187
|
+
] });
|
|
188
|
+
const z = ke(t.name);
|
|
189
|
+
return /* @__PURE__ */ v(
|
|
190
|
+
"button",
|
|
191
|
+
{
|
|
192
|
+
type: "button",
|
|
193
|
+
onClick: () => g(t),
|
|
194
|
+
onMouseEnter: N,
|
|
195
|
+
onMouseLeave: b,
|
|
196
|
+
className: `rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-sm ${M ? "rfp-bg-surface-2 rfp-text-fg-primary" : "rfp-text-fg-secondary hover:rfp-bg-surface-1"}`,
|
|
197
|
+
style: L,
|
|
198
|
+
children: [
|
|
199
|
+
/* @__PURE__ */ e("span", { className: "rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0" }),
|
|
200
|
+
/* @__PURE__ */ e(z, { className: "rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-fg-tertiary" }),
|
|
201
|
+
/* @__PURE__ */ e("span", { className: "rfp-flex-1 rfp-truncate rfp-min-w-0", children: t.name }),
|
|
202
|
+
/* @__PURE__ */ e("span", { className: "rfp-text-xs rfp-text-fg-disabled rfp-flex-shrink-0 rfp-ml-2", children: ye(t.size) })
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
}, Fe = V(({ url: t, nestingDepth: s = 0, onStatsChange: h }, y) => {
|
|
207
|
+
var G;
|
|
208
|
+
const x = me(), g = he(), [a, b] = o(null), [c, M] = o(null), [L, N] = o(!0), [z, d] = o(null), [u, E] = o(/* @__PURE__ */ new Set([""])), [r, U] = o(null), [R, j] = o(!1), [w, T] = o(null), [D, n] = o(null), p = q(h), $ = q(null);
|
|
209
|
+
k(() => {
|
|
210
|
+
p.current = h;
|
|
211
|
+
}, [h]), k(() => {
|
|
212
|
+
if (!t) return;
|
|
213
|
+
let f = !1;
|
|
214
|
+
return (async () => {
|
|
215
|
+
try {
|
|
216
|
+
N(!0), d(null);
|
|
217
|
+
const l = await g(t);
|
|
218
|
+
if (!l.ok) throw new Error("加载失败");
|
|
219
|
+
const F = await l.arrayBuffer(), m = await be(F);
|
|
220
|
+
if (f) return;
|
|
221
|
+
const P = we(m), Z = ve(P);
|
|
222
|
+
b(m), M(Z);
|
|
223
|
+
const X = /* @__PURE__ */ new Set([""]);
|
|
224
|
+
if (Z.children)
|
|
225
|
+
for (const Y of Z.children) Y.isDir && X.add(Y.path);
|
|
226
|
+
E(X);
|
|
227
|
+
} catch (l) {
|
|
228
|
+
console.error(l), f || d(x("zip.load_failed"));
|
|
229
|
+
} finally {
|
|
230
|
+
f || N(!1);
|
|
231
|
+
}
|
|
232
|
+
})(), () => {
|
|
233
|
+
f = !0;
|
|
234
|
+
};
|
|
235
|
+
}, [t]), k(() => () => {
|
|
236
|
+
r != null && r.blobUrl && URL.revokeObjectURL(r.blobUrl);
|
|
237
|
+
}, [r]);
|
|
238
|
+
const C = J(() => {
|
|
239
|
+
if (!c) return null;
|
|
240
|
+
let f = 0, i = 0, l = 0;
|
|
241
|
+
const F = (m) => {
|
|
242
|
+
var P;
|
|
243
|
+
m.isDir ? (m.path && i++, (P = m.children) == null || P.forEach(F)) : (f++, l += m.size);
|
|
244
|
+
};
|
|
245
|
+
return F(c), { files: f, dirs: i, size: l };
|
|
246
|
+
}, [c]);
|
|
247
|
+
k(() => {
|
|
248
|
+
var f;
|
|
249
|
+
return (f = p.current) == null || f.call(p, C), () => {
|
|
250
|
+
var i;
|
|
251
|
+
(i = p.current) == null || i.call(p, null);
|
|
252
|
+
};
|
|
253
|
+
}, [C]);
|
|
254
|
+
const O = S((f) => {
|
|
255
|
+
E((i) => {
|
|
256
|
+
const l = new Set(i);
|
|
257
|
+
return l.has(f) ? l.delete(f) : l.add(f), l;
|
|
258
|
+
});
|
|
259
|
+
}, []), I = S((f, i) => {
|
|
260
|
+
n({
|
|
261
|
+
text: f,
|
|
262
|
+
x: i.right + 8,
|
|
263
|
+
y: i.top + i.height / 2
|
|
264
|
+
});
|
|
265
|
+
}, []), W = S(() => {
|
|
266
|
+
n(null);
|
|
267
|
+
}, []), A = S(
|
|
268
|
+
async (f) => {
|
|
269
|
+
var i;
|
|
270
|
+
if (!(!a || f.isDir)) {
|
|
271
|
+
r != null && r.blobUrl && URL.revokeObjectURL(r.blobUrl), j(!0), T(null);
|
|
272
|
+
try {
|
|
273
|
+
const l = Q(f.name), F = await xe(a, f.path, l !== "application/octet-stream" ? l : void 0), m = URL.createObjectURL(F);
|
|
274
|
+
U({ path: f.path, name: f.name, size: f.size, blobUrl: m }), (i = $.current) == null || i.switchTab("right");
|
|
275
|
+
} catch (l) {
|
|
276
|
+
console.error(l), T("条目读取失败");
|
|
277
|
+
} finally {
|
|
278
|
+
j(!1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
[a, r]
|
|
283
|
+
), B = J(() => r ? [{ name: r.name, url: r.blobUrl, type: Q(r.name) }] : [], [r]);
|
|
284
|
+
if (K(y, () => ({
|
|
285
|
+
getToolbarGroups: () => []
|
|
286
|
+
}), []), L)
|
|
287
|
+
return /* @__PURE__ */ e("div", { className: "rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full", children: /* @__PURE__ */ e("div", { className: "rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin" }) });
|
|
288
|
+
if (z || !c)
|
|
289
|
+
return /* @__PURE__ */ e(ue, { message: z || x("zip.parse_failed") });
|
|
290
|
+
const H = /* @__PURE__ */ e("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto", children: (G = c.children) == null ? void 0 : G.map((f) => /* @__PURE__ */ e(
|
|
291
|
+
re,
|
|
292
|
+
{
|
|
293
|
+
node: f,
|
|
294
|
+
depth: 0,
|
|
295
|
+
selectedPath: (r == null ? void 0 : r.path) ?? null,
|
|
296
|
+
expanded: u,
|
|
297
|
+
onToggle: O,
|
|
298
|
+
onSelect: A,
|
|
299
|
+
onHover: I,
|
|
300
|
+
onLeave: W
|
|
301
|
+
},
|
|
302
|
+
f.path
|
|
303
|
+
)) }), te = /* @__PURE__ */ v("div", { className: "rfp-w-full rfp-h-full rfp-flex rfp-flex-col", children: [
|
|
304
|
+
!r && /* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-muted rfp-text-sm rfp-p-6", children: "从左侧选择一个文件以预览" }),
|
|
305
|
+
r && R && /* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center", children: /* @__PURE__ */ e("div", { className: "rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin" }) }),
|
|
306
|
+
r && !R && w && /* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-secondary", children: w }),
|
|
307
|
+
r && !R && !w && /* @__PURE__ */ e(_, { children: /* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-min-h-0 rfp-overflow-hidden rfp-flex rfp-relative rfp-z-0", children: /* @__PURE__ */ e(
|
|
308
|
+
fe,
|
|
309
|
+
{
|
|
310
|
+
fallback: /* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center", children: /* @__PURE__ */ e("div", { className: "rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin" }) }),
|
|
311
|
+
children: /* @__PURE__ */ e(
|
|
312
|
+
Ne,
|
|
313
|
+
{
|
|
314
|
+
mode: "embed",
|
|
315
|
+
files: B,
|
|
316
|
+
currentIndex: 0,
|
|
317
|
+
zipNestingDepth: s + 1
|
|
318
|
+
}
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
) }) })
|
|
322
|
+
] });
|
|
323
|
+
return /* @__PURE__ */ v(_, { children: [
|
|
324
|
+
/* @__PURE__ */ e(
|
|
325
|
+
ee,
|
|
326
|
+
{
|
|
327
|
+
ref: $,
|
|
328
|
+
left: H,
|
|
329
|
+
right: te,
|
|
330
|
+
initialLeftWidth: 280,
|
|
331
|
+
minLeftWidth: 180,
|
|
332
|
+
maxLeftWidth: 560,
|
|
333
|
+
storageKey: "rfp-zip-split-left",
|
|
334
|
+
mobileTabMode: !0,
|
|
335
|
+
leftTabLabel: "文件树",
|
|
336
|
+
rightTabLabel: "预览"
|
|
337
|
+
}
|
|
338
|
+
),
|
|
339
|
+
D && typeof document < "u" && le(
|
|
340
|
+
/* @__PURE__ */ e(
|
|
341
|
+
"div",
|
|
342
|
+
{
|
|
343
|
+
className: "rfp-fixed rfp-z-[9999] rfp-pointer-events-none rfp-px-2 rfp-py-1 rfp-bg-[rgba(0,0,0,0.85)] rfp-text-fg-primary rfp-text-xs rfp-rounded rfp-whitespace-nowrap rfp-shadow-lg",
|
|
344
|
+
style: {
|
|
345
|
+
left: `${D.x}px`,
|
|
346
|
+
top: `${D.y}px`,
|
|
347
|
+
transform: "translateY(-50%)"
|
|
348
|
+
},
|
|
349
|
+
children: D.text
|
|
350
|
+
}
|
|
351
|
+
),
|
|
352
|
+
document.body
|
|
353
|
+
)
|
|
354
|
+
] });
|
|
355
|
+
});
|
|
356
|
+
export {
|
|
357
|
+
Fe as ZipRenderer
|
|
358
|
+
};
|
|
359
|
+
//# sourceMappingURL=index-Da3FN2-3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Da3FN2-3.mjs","sources":["../../src/components/ResizableSplit.tsx","../../src/renderers/Zip/index.tsx"],"sourcesContent":["import React, { useEffect, useImperativeHandle, useRef, useState, useCallback, forwardRef } from 'react';\n\nexport interface ResizableSplitProps {\n /** 左侧内容 */\n left: React.ReactNode;\n /** 右侧内容 */\n right: React.ReactNode;\n /** 左侧初始宽度(px);传入 storageKey 时会从 localStorage 读取 */\n initialLeftWidth?: number;\n /** 左侧最小宽度(px) */\n minLeftWidth?: number;\n /** 左侧最大宽度(px),同时不超过 `容器宽 - minRightWidth - 分隔线宽` */\n maxLeftWidth?: number;\n /** 右侧至少保留的宽度(px) */\n minRightWidth?: number;\n /** localStorage 持久化 key;不传则不持久化 */\n storageKey?: string;\n /** 启用横向拖动的媒体查询,默认 `(min-width: 768px)` */\n desktopMedia?: string;\n /** 容器额外类名 */\n className?: string;\n /** 移动端使用 Tab 切换而非上下堆叠 */\n mobileTabMode?: boolean;\n /** Tab 模式下左侧标题 */\n leftTabLabel?: string;\n /** Tab 模式下右侧标题 */\n rightTabLabel?: string;\n}\n\nexport interface ResizableSplitHandle {\n switchTab: (tab: 'left' | 'right') => void;\n}\n\n/**\n * 通用可拖动分隔布局:\n * - 桌面端(由 `desktopMedia` 判定)横向分两栏,中间分隔线可左右拖动调整左栏宽度\n * - 移动端默认退化为上下堆叠(不显示分隔线)\n * - 设置 `mobileTabMode` 时移动端使用 Tab 切换显示\n * - 可选 `storageKey` 将宽度持久化到 localStorage\n */\nexport const ResizableSplit = forwardRef<ResizableSplitHandle, ResizableSplitProps>(({\n left,\n right,\n initialLeftWidth = 280,\n minLeftWidth = 160,\n maxLeftWidth = 640,\n minRightWidth = 200,\n storageKey,\n desktopMedia = '(min-width: 768px)',\n className = '',\n mobileTabMode = false,\n leftTabLabel = '文件树',\n rightTabLabel = '预览',\n}, ref) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [leftWidth, setLeftWidth] = useState<number>(() => {\n if (storageKey && typeof window !== 'undefined') {\n const saved = Number(window.localStorage.getItem(storageKey));\n if (!isNaN(saved) && saved > 0) return saved;\n }\n return initialLeftWidth;\n });\n const [dragging, setDragging] = useState(false);\n const [isDesktop, setIsDesktop] = useState(false);\n const [activeTab, setActiveTab] = useState<'left' | 'right'>('left');\n\n useImperativeHandle(ref, () => ({\n switchTab: (tab) => setActiveTab(tab),\n }), []);\n\n // 响应式:监听媒体查询\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mq = window.matchMedia(desktopMedia);\n const handler = () => setIsDesktop(mq.matches);\n handler();\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, [desktopMedia]);\n\n // 拖动\n useEffect(() => {\n if (!dragging) return;\n const handleMove = (e: MouseEvent) => {\n if (!containerRef.current) return;\n const rect = containerRef.current.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const cap = rect.width - minRightWidth - 6;\n const effectiveMax = Math.min(maxLeftWidth, cap);\n const newW = Math.max(minLeftWidth, Math.min(effectiveMax, x));\n setLeftWidth(newW);\n };\n const handleUp = () => setDragging(false);\n window.addEventListener('mousemove', handleMove);\n window.addEventListener('mouseup', handleUp);\n const prevCursor = document.body.style.cursor;\n const prevSelect = document.body.style.userSelect;\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n return () => {\n window.removeEventListener('mousemove', handleMove);\n window.removeEventListener('mouseup', handleUp);\n document.body.style.cursor = prevCursor;\n document.body.style.userSelect = prevSelect;\n };\n }, [dragging, minLeftWidth, maxLeftWidth, minRightWidth]);\n\n // 持久化\n useEffect(() => {\n if (!storageKey || dragging) return;\n try {\n window.localStorage.setItem(storageKey, String(leftWidth));\n } catch {\n // ignore\n }\n }, [leftWidth, storageKey, dragging]);\n\n const handleDividerDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n setDragging(true);\n }, []);\n\n // 移动端 Tab 模式\n if (mobileTabMode && !isDesktop) {\n return (\n <div\n ref={containerRef}\n className={`rfp-w-full rfp-h-full rfp-flex rfp-flex-col rfp-min-h-0 rfp-min-w-0 ${className}`}\n >\n <div className=\"rfp-flex rfp-flex-shrink-0 rfp-border-b rfp-border-line-weak rfp-bg-surface-toolbar\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('left')}\n className={`rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${\n activeTab === 'left'\n ? 'rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px'\n : 'rfp-text-fg-secondary'\n }`}\n >\n {leftTabLabel}\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('right')}\n className={`rfp-flex-1 rfp-py-2.5 rfp-text-sm rfp-transition-colors ${\n activeTab === 'right'\n ? 'rfp-text-fg-primary rfp-border-b-2 rfp-border-fg-primary -rfp-mb-px'\n : 'rfp-text-fg-secondary'\n }`}\n >\n {rightTabLabel}\n </button>\n </div>\n <div\n className=\"rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden\"\n style={{ display: activeTab === 'left' ? undefined : 'none' }}\n >\n {left}\n </div>\n <div\n className=\"rfp-flex-1 rfp-min-h-0 rfp-min-w-0 rfp-w-full rfp-overflow-hidden\"\n style={{ display: activeTab === 'right' ? undefined : 'none' }}\n >\n {right}\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={containerRef}\n className={`rfp-w-full rfp-h-full rfp-flex rfp-flex-col md:rfp-flex-row rfp-min-h-0 rfp-min-w-0 ${className}`}\n >\n <div\n className=\"rfp-min-h-0 rfp-min-w-0 rfp-flex-shrink-0 rfp-w-full rfp-max-h-60 md:rfp-h-full md:rfp-max-h-none\"\n style={isDesktop ? { width: `${leftWidth}px` } : undefined}\n >\n {left}\n </div>\n {/* 分隔线:仅桌面显示 */}\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n onMouseDown={handleDividerDown}\n className={`rfp-hidden md:rfp-block rfp-relative rfp-w-1.5 rfp-flex-shrink-0 rfp-cursor-col-resize rfp-transition-colors ${\n dragging ? 'rfp-bg-surface-toolbar' : 'rfp-bg-surface-2 hover:rfp-bg-surface-3'\n }`}\n >\n {/* 加宽命中区,改善拖动体验 */}\n <span className=\"rfp-absolute rfp-inset-y-0 -rfp-left-1 -rfp-right-1\" />\n </div>\n <div className=\"rfp-flex-1 rfp-min-w-0 rfp-min-h-0 rfp-overflow-hidden\">{right}</div>\n </div>\n );\n});\n\nResizableSplit.displayName = 'ResizableSplit';\n","import { useState, useEffect, useMemo, useCallback, useRef, lazy, Suspense, forwardRef, useImperativeHandle } from 'react';\nimport React from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n Folder,\n FolderOpen,\n FileText,\n FileImage,\n FileCode,\n File as FileIcon,\n ChevronRight,\n} from 'lucide-react';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\nimport type JSZip from 'jszip';\nimport {\n loadZip,\n listZipEntries,\n buildZipTree,\n readZipEntryBlob,\n formatFileSize,\n getFileType,\n inferMimeType,\n type ZipTreeNode,\n} from '@eternalheart/file-preview-core';\nimport { ResizableSplit, type ResizableSplitHandle } from '../../components/ResizableSplit';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\n\nexport interface ZipToolbarStats {\n files: number;\n dirs: number;\n size: number;\n}\n\n// 懒加载 FilePreviewContent 以打破循环依赖\nconst LazyFilePreviewContent = lazy(() =>\n import('../../FilePreviewContent').then(m => ({ default: m.FilePreviewContent }))\n);\n\ninterface ZipRendererProps {\n url: string;\n /** ZIP 嵌套深度(由 FilePreviewContent 传入) */\n nestingDepth?: number;\n /** 解析完成后向外回报统计信息(files / dirs / size),供工具栏展示 */\n onStatsChange?: (stats: ZipToolbarStats | null) => void;\n}\n\ninterface SelectedPreview {\n path: string;\n name: string;\n size: number;\n blobUrl: string;\n}\n\n/** 根据文件类型返回树节点图标 */\nconst resolveIcon = (name: string) => {\n const ft = getFileType({ id: '', name, url: '', type: '' });\n if (ft === 'image') return FileImage;\n if (ft === 'text' || ft === 'markdown' || ft === 'json' || ft === 'csv' || ft === 'xml' || ft === 'subtitle') {\n return name.endsWith('.md') || name.endsWith('.markdown') ? FileText : FileCode;\n }\n return FileIcon;\n};\n\n// ---------- Tooltip via portal ----------\n\ninterface HoverTipState {\n text: string;\n x: number;\n y: number;\n}\n\n// ---------- Tree item ----------\n\ninterface TreeItemProps {\n node: ZipTreeNode;\n depth: number;\n selectedPath: string | null;\n expanded: Set<string>;\n onToggle: (path: string) => void;\n onSelect: (node: ZipTreeNode) => void;\n onHover: (text: string, rect: DOMRect) => void;\n onLeave: () => void;\n}\n\nconst TreeItem: React.FC<TreeItemProps> = ({\n node,\n depth,\n selectedPath,\n expanded,\n onToggle,\n onSelect,\n onHover,\n onLeave,\n}) => {\n const isOpen = expanded.has(node.path);\n const isSelected = selectedPath === node.path;\n const pad = { paddingLeft: `${depth * 14 + 10}px` };\n const handleEnter = (e: React.MouseEvent<HTMLElement>) => {\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n onHover(node.name || '/', rect);\n };\n\n if (node.isDir) {\n return (\n <>\n <button\n type=\"button\"\n onClick={() => onToggle(node.path)}\n onMouseEnter={handleEnter}\n onMouseLeave={onLeave}\n className=\"rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-fg-secondary hover:rfp-bg-surface-1 rfp-text-sm\"\n style={pad}\n >\n <ChevronRight\n className={`rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0 rfp-transition-transform ${\n isOpen ? 'rfp-rotate-90' : ''\n }`}\n />\n {isOpen ? (\n <FolderOpen className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80\" />\n ) : (\n <Folder className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80\" />\n )}\n <span className=\"rfp-truncate rfp-flex-1 rfp-min-w-0\">{node.name || '/'}</span>\n </button>\n {isOpen &&\n node.children?.map((child) => (\n <TreeItem\n key={child.path}\n node={child}\n depth={depth + 1}\n selectedPath={selectedPath}\n expanded={expanded}\n onToggle={onToggle}\n onSelect={onSelect}\n onHover={onHover}\n onLeave={onLeave}\n />\n ))}\n </>\n );\n }\n\n const Icon = resolveIcon(node.name);\n\n return (\n <button\n type=\"button\"\n onClick={() => onSelect(node)}\n onMouseEnter={handleEnter}\n onMouseLeave={onLeave}\n className={`rfp-w-full rfp-flex rfp-items-center rfp-gap-1.5 rfp-py-1.5 rfp-pr-2 rfp-text-left rfp-text-sm ${\n isSelected ? 'rfp-bg-surface-2 rfp-text-fg-primary' : 'rfp-text-fg-secondary hover:rfp-bg-surface-1'\n }`}\n style={pad}\n >\n <span className=\"rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0\" />\n <Icon className=\"rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-fg-tertiary\" />\n <span className=\"rfp-flex-1 rfp-truncate rfp-min-w-0\">{node.name}</span>\n <span className=\"rfp-text-xs rfp-text-fg-disabled rfp-flex-shrink-0 rfp-ml-2\">\n {formatFileSize(node.size)}\n </span>\n </button>\n );\n};\n\n// ---------- Main Zip Renderer ----------\n\nexport const ZipRenderer = forwardRef<RendererHandle, ZipRendererProps>(({ url, nestingDepth = 0, onStatsChange }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [zip, setZip] = useState<JSZip | null>(null);\n const [tree, setTree] = useState<ZipTreeNode | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [expanded, setExpanded] = useState<Set<string>>(new Set(['']));\n const [selected, setSelected] = useState<SelectedPreview | null>(null);\n const [previewLoading, setPreviewLoading] = useState(false);\n const [previewError, setPreviewError] = useState<string | null>(null);\n const [hoverTip, setHoverTip] = useState<HoverTipState | null>(null);\n const onStatsChangeRef = useRef(onStatsChange);\n const splitRef = useRef<ResizableSplitHandle>(null);\n\n useEffect(() => {\n onStatsChangeRef.current = onStatsChange;\n }, [onStatsChange]);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n let cancelled = false;\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const res = await fetcher(url);\n if (!res.ok) throw new Error('加载失败');\n const buf = await res.arrayBuffer();\n const z = await loadZip(buf);\n if (cancelled) return;\n const entries = listZipEntries(z);\n const root = buildZipTree(entries);\n setZip(z);\n setTree(root);\n const init = new Set<string>(['']);\n if (root.children) {\n for (const c of root.children) if (c.isDir) init.add(c.path);\n }\n setExpanded(init);\n } catch (err) {\n console.error(err);\n if (!cancelled) setError(t('zip.load_failed'));\n } finally {\n if (!cancelled) setLoading(false);\n }\n };\n load();\n return () => {\n cancelled = true;\n };\n }, [url]);\n\n // 切换文件时回收 blob URL\n useEffect(() => {\n return () => {\n if (selected?.blobUrl) URL.revokeObjectURL(selected.blobUrl);\n };\n }, [selected]);\n\n const totalStats = useMemo<ZipToolbarStats | null>(() => {\n if (!tree) return null;\n let files = 0;\n let dirs = 0;\n let size = 0;\n const walk = (n: ZipTreeNode) => {\n if (n.isDir) {\n if (n.path) dirs++;\n n.children?.forEach(walk);\n } else {\n files++;\n size += n.size;\n }\n };\n walk(tree);\n return { files, dirs, size };\n }, [tree]);\n\n // 向外回报 stats\n useEffect(() => {\n onStatsChangeRef.current?.(totalStats);\n return () => {\n onStatsChangeRef.current?.(null);\n };\n }, [totalStats]);\n\n const handleToggle = useCallback((path: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n }, []);\n\n const handleHover = useCallback((text: string, rect: DOMRect) => {\n setHoverTip({\n text,\n x: rect.right + 8,\n y: rect.top + rect.height / 2,\n });\n }, []);\n\n const handleLeave = useCallback(() => {\n setHoverTip(null);\n }, []);\n\n const handleSelect = useCallback(\n async (node: ZipTreeNode) => {\n if (!zip || node.isDir) return;\n if (selected?.blobUrl) URL.revokeObjectURL(selected.blobUrl);\n setPreviewLoading(true);\n setPreviewError(null);\n\n try {\n const mime = inferMimeType(node.name);\n const blob = await readZipEntryBlob(zip, node.path, mime !== 'application/octet-stream' ? mime : undefined);\n const blobUrl = URL.createObjectURL(blob);\n setSelected({ path: node.path, name: node.name, size: node.size, blobUrl });\n // 移动端切换到预览 tab\n splitRef.current?.switchTab('right');\n } catch (err) {\n console.error(err);\n setPreviewError('条目读取失败');\n } finally {\n setPreviewLoading(false);\n }\n },\n [zip, selected]\n );\n\n // Memoize files 数组以避免无限重新渲染\n const previewFiles = useMemo(() => {\n if (!selected) return [];\n return [{ name: selected.name, url: selected.blobUrl, type: inferMimeType(selected.name) }];\n }, [selected]);\n\n // 暴露接口给父组件(必须在 early return 之前调用)\n useImperativeHandle(ref, () => ({\n getToolbarGroups: () => [],\n }), []);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error || !tree) {\n return <RendererError message={error || t('zip.parse_failed')} />;\n }\n\n // 左侧:文件树\n const leftPane = (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto\">\n {tree.children?.map((child) => (\n <TreeItem\n key={child.path}\n node={child}\n depth={0}\n selectedPath={selected?.path ?? null}\n expanded={expanded}\n onToggle={handleToggle}\n onSelect={handleSelect}\n onHover={handleHover}\n onLeave={handleLeave}\n />\n ))}\n </div>\n );\n\n // 右侧:预览区\n const rightPane = (\n <div className=\"rfp-w-full rfp-h-full rfp-flex rfp-flex-col\">\n {!selected && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-muted rfp-text-sm rfp-p-6\">\n 从左侧选择一个文件以预览\n </div>\n )}\n {selected && previewLoading && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n )}\n {selected && !previewLoading && previewError && (\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-secondary\">\n {previewError}\n </div>\n )}\n {selected && !previewLoading && !previewError && (\n <>\n <div className=\"rfp-flex-1 rfp-min-h-0 rfp-overflow-hidden rfp-flex rfp-relative rfp-z-0\">\n <Suspense\n fallback={\n <div className=\"rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center\">\n <div className=\"rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n }\n >\n <LazyFilePreviewContent\n mode=\"embed\"\n files={previewFiles}\n currentIndex={0}\n zipNestingDepth={nestingDepth + 1}\n />\n </Suspense>\n </div>\n </>\n )}\n </div>\n );\n\n return (\n <>\n <ResizableSplit\n ref={splitRef}\n left={leftPane}\n right={rightPane}\n initialLeftWidth={280}\n minLeftWidth={180}\n maxLeftWidth={560}\n storageKey=\"rfp-zip-split-left\"\n mobileTabMode\n leftTabLabel=\"文件树\"\n rightTabLabel=\"预览\"\n />\n {/* 文件名 hover tooltip(portal 到 body,避免被滚动区裁剪) */}\n {hoverTip &&\n typeof document !== 'undefined' &&\n createPortal(\n <div\n className=\"rfp-fixed rfp-z-[9999] rfp-pointer-events-none rfp-px-2 rfp-py-1 rfp-bg-[rgba(0,0,0,0.85)] rfp-text-fg-primary rfp-text-xs rfp-rounded rfp-whitespace-nowrap rfp-shadow-lg\"\n style={{\n left: `${hoverTip.x}px`,\n top: `${hoverTip.y}px`,\n transform: 'translateY(-50%)',\n }}\n >\n {hoverTip.text}\n </div>,\n document.body\n )}\n </>\n );\n});\n"],"names":["ResizableSplit","forwardRef","left","right","initialLeftWidth","minLeftWidth","maxLeftWidth","minRightWidth","storageKey","desktopMedia","className","mobileTabMode","leftTabLabel","rightTabLabel","ref","containerRef","useRef","leftWidth","setLeftWidth","useState","saved","dragging","setDragging","isDesktop","setIsDesktop","activeTab","setActiveTab","useImperativeHandle","tab","useEffect","mq","handler","handleMove","e","rect","x","cap","effectiveMax","newW","handleUp","prevCursor","prevSelect","handleDividerDown","useCallback","jsxs","jsx","LazyFilePreviewContent","lazy","n","m","resolveIcon","name","ft","getFileType","FileImage","FileText","FileCode","FileIcon","TreeItem","node","depth","selectedPath","expanded","onToggle","onSelect","onHover","onLeave","isOpen","isSelected","pad","handleEnter","Fragment","ChevronRight","FolderOpen","Folder","_a","child","Icon","formatFileSize","ZipRenderer","url","nestingDepth","onStatsChange","t","useTranslator","fetcher","useFetcher","zip","setZip","tree","setTree","loading","setLoading","error","setError","setExpanded","selected","setSelected","previewLoading","setPreviewLoading","previewError","setPreviewError","hoverTip","setHoverTip","onStatsChangeRef","splitRef","cancelled","res","buf","z","loadZip","entries","listZipEntries","root","buildZipTree","init","c","err","totalStats","useMemo","files","dirs","size","walk","handleToggle","path","prev","next","handleHover","text","handleLeave","handleSelect","mime","inferMimeType","blob","readZipEntryBlob","blobUrl","previewFiles","RendererError","leftPane","rightPane","Suspense","createPortal"],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as r, jsxs as w } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as b, useState as d, useEffect as N, useImperativeHandle as y, Fragment as x } from "react";
|
|
3
|
+
import { u as v, a as z, z as S } from "./index-Cz23v-TW.mjs";
|
|
4
|
+
import { u as $ } from "./useShikiHighlight-Bbs8Fbqs.mjs";
|
|
5
|
+
import { R } from "./RendererError-D5i8eSpN.mjs";
|
|
6
|
+
const T = (t) => {
|
|
7
|
+
try {
|
|
8
|
+
const s = new DOMParser().parseFromString(t, "application/xml");
|
|
9
|
+
if (s.querySelector("parsererror")) return t;
|
|
10
|
+
const n = new XMLSerializer().serializeToString(s);
|
|
11
|
+
return j(n);
|
|
12
|
+
} catch {
|
|
13
|
+
return t;
|
|
14
|
+
}
|
|
15
|
+
}, j = (t) => {
|
|
16
|
+
const o = " ", s = /(>)(<)(\/*)/g;
|
|
17
|
+
let a = t.replace(s, `$1
|
|
18
|
+
$2$3`), e = 0;
|
|
19
|
+
return a.split(`
|
|
20
|
+
`).map((n) => {
|
|
21
|
+
let l = 0;
|
|
22
|
+
/^<\/\w/.test(n) ? e = Math.max(e - 1, 0) : /^<\w[^>]*[^/]>.*$/.test(n) && !/<.+<\/.+>$/.test(n) && (l = 1);
|
|
23
|
+
const f = o.repeat(e) + n;
|
|
24
|
+
return e += l, f;
|
|
25
|
+
}).join(`
|
|
26
|
+
`);
|
|
27
|
+
}, k = b(({ url: t }, o) => {
|
|
28
|
+
const s = v(), a = z(), [e, n] = d(""), [l, f] = d(!0), [m, u] = d(null), { lineHtmls: g } = $(e, "xml");
|
|
29
|
+
if (N(() => {
|
|
30
|
+
const c = new AbortController();
|
|
31
|
+
return (async () => {
|
|
32
|
+
try {
|
|
33
|
+
f(!0), u(null);
|
|
34
|
+
const i = await S(t, { fetcher: a, signal: c.signal });
|
|
35
|
+
n(T(i));
|
|
36
|
+
} catch (i) {
|
|
37
|
+
if (i.name === "AbortError") return;
|
|
38
|
+
console.error(i), u(s("xml.load_failed"));
|
|
39
|
+
} finally {
|
|
40
|
+
f(!1);
|
|
41
|
+
}
|
|
42
|
+
})(), () => c.abort();
|
|
43
|
+
}, [t]), y(o, () => ({
|
|
44
|
+
getToolbarGroups: () => []
|
|
45
|
+
}), []), l)
|
|
46
|
+
return /* @__PURE__ */ r("div", { className: "rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full", children: /* @__PURE__ */ r("div", { className: "rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin" }) });
|
|
47
|
+
if (m)
|
|
48
|
+
return /* @__PURE__ */ r(R, { message: m });
|
|
49
|
+
if (g.length === 0)
|
|
50
|
+
return /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg", children: /* @__PURE__ */ r("pre", { className: "rfp-py-6 rfp-px-4 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words", children: e }) });
|
|
51
|
+
const h = e.split(`
|
|
52
|
+
`);
|
|
53
|
+
return /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg", children: /* @__PURE__ */ w(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
className: "rfp-code-block with-line-numbers rfp-w-full",
|
|
57
|
+
style: { gridTemplateRows: `repeat(${h.length}, auto) minmax(1.5rem, 1fr)` },
|
|
58
|
+
children: [
|
|
59
|
+
h.map((c, p) => /* @__PURE__ */ w(x, { children: [
|
|
60
|
+
/* @__PURE__ */ r("span", { className: "rfp-code-gutter", children: p + 1 }),
|
|
61
|
+
/* @__PURE__ */ r(
|
|
62
|
+
"span",
|
|
63
|
+
{
|
|
64
|
+
className: "rfp-code-line",
|
|
65
|
+
dangerouslySetInnerHTML: { __html: g[p] ?? "" }
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
] }, p)),
|
|
69
|
+
/* @__PURE__ */ r("span", { className: "rfp-code-gutter-filler" }),
|
|
70
|
+
/* @__PURE__ */ r("span", { className: "rfp-code-line-filler" })
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
) });
|
|
74
|
+
});
|
|
75
|
+
export {
|
|
76
|
+
k as XmlRenderer
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=index-Dc6q1OKl.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Dc6q1OKl.mjs","sources":["../../src/renderers/Xml/index.tsx"],"sourcesContent":["import { useState, useEffect, Fragment, forwardRef, useImperativeHandle } from 'react';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\nimport type { RendererHandle } from '../base.types';\n\ninterface XmlRendererProps {\n url: string;\n fileName: string;\n}\n\n/**\n * 用 DOMParser 美化 XML:失败则原样返回\n */\nconst prettyPrintXml = (xml: string): string => {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'application/xml');\n // 检测解析错误\n const errNode = doc.querySelector('parsererror');\n if (errNode) return xml;\n // 使用 XSLT 或手动缩进:这里手动缩进更稳\n const serializer = new XMLSerializer();\n const serialized = serializer.serializeToString(doc);\n return indentXml(serialized);\n } catch {\n return xml;\n }\n};\n\nconst indentXml = (xml: string): string => {\n const PADDING = ' ';\n const reg = /(>)(<)(\\/*)/g;\n let formatted = xml.replace(reg, '$1\\n$2$3');\n // 自闭合和 CDATA 等不处理\n let pad = 0;\n return formatted\n .split('\\n')\n .map((line) => {\n let indent = 0;\n if (/^<\\/\\w/.test(line)) {\n pad = Math.max(pad - 1, 0);\n } else if (/^<\\w[^>]*[^/]>.*$/.test(line) && !/<.+<\\/.+>$/.test(line)) {\n indent = 1;\n }\n const padded = PADDING.repeat(pad) + line;\n pad += indent;\n return padded;\n })\n .join('\\n');\n};\n\nexport const XmlRenderer = forwardRef<RendererHandle, XmlRendererProps>(({ url }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [content, setContent] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const { lineHtmls } = useShikiHighlight(content, 'xml');\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n const raw = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(prettyPrintXml(raw));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.error(err);\n setError(t('xml.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\n\n // 暴露接口给父组件(必须在 early return 之前调用)\n useImperativeHandle(ref, () => ({\n getToolbarGroups: () => [],\n }), []);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n );\n }\n\n if (error) {\n return <RendererError message={error} />;\n }\n\n if (lineHtmls.length === 0) {\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n <pre className=\"rfp-py-6 rfp-px-4 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words\">\n {content}\n </pre>\n </div>\n );\n }\n\n const lines = content.split('\\n');\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n <div\n className=\"rfp-code-block with-line-numbers rfp-w-full\"\n style={{ gridTemplateRows: `repeat(${lines.length}, auto) minmax(1.5rem, 1fr)` }}\n >\n {lines.map((_, i) => (\n <Fragment key={i}>\n <span className=\"rfp-code-gutter\">{i + 1}</span>\n <span\n className=\"rfp-code-line\"\n dangerouslySetInnerHTML={{ __html: lineHtmls[i] ?? '' }}\n />\n </Fragment>\n ))}\n {/* 占位行:撑满剩余高度,让 gutter border 延伸到底部 */}\n <span className=\"rfp-code-gutter-filler\" />\n <span className=\"rfp-code-line-filler\" />\n </div>\n </div>\n );\n});\n"],"names":["prettyPrintXml","xml","doc","serialized","indentXml","PADDING","reg","formatted","pad","line","indent","padded","XmlRenderer","forwardRef","url","ref","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","lineHtmls","useShikiHighlight","useEffect","controller","raw","fetchTextUtf8","err","useImperativeHandle","jsx","RendererError","lines","jsxs","_","i","Fragment"],"mappings":"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"}
|