@eternalheart/react-file-preview 1.3.12 → 1.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +27 -0
  2. package/README.zh-CN.md +27 -0
  3. package/lib/FilePreviewEmbed.d.ts +2 -0
  4. package/lib/FilePreviewEmbed.d.ts.map +1 -1
  5. package/lib/chunks/{index-BCbSb9Ob.mjs → index--lXiT1Y_.mjs} +77 -75
  6. package/lib/chunks/index--lXiT1Y_.mjs.map +1 -0
  7. package/lib/chunks/index-B05UpMZC.mjs +270 -0
  8. package/lib/chunks/index-B05UpMZC.mjs.map +1 -0
  9. package/lib/chunks/{index-DJZWizxK.mjs → index-BG3Idu38.mjs} +2 -2
  10. package/lib/chunks/{index-DJZWizxK.mjs.map → index-BG3Idu38.mjs.map} +1 -1
  11. package/lib/chunks/{index-DCTwUpKS.mjs → index-BTLV1YqJ.mjs} +2 -2
  12. package/lib/chunks/{index-DCTwUpKS.mjs.map → index-BTLV1YqJ.mjs.map} +1 -1
  13. package/lib/chunks/{index-BBvL23cc.mjs → index-B_7NPlPG.mjs} +2 -2
  14. package/lib/chunks/{index-BBvL23cc.mjs.map → index-B_7NPlPG.mjs.map} +1 -1
  15. package/lib/chunks/{index-DRCA7PhQ.mjs → index-BaU-yih3.mjs} +2 -2
  16. package/lib/chunks/{index-DRCA7PhQ.mjs.map → index-BaU-yih3.mjs.map} +1 -1
  17. package/lib/chunks/{index-qEUWJYQ5.mjs → index-BfzV7KIz.mjs} +2 -2
  18. package/lib/chunks/{index-qEUWJYQ5.mjs.map → index-BfzV7KIz.mjs.map} +1 -1
  19. package/lib/chunks/{index-D2t64h6I.mjs → index-Br8WHz8e.mjs} +2 -2
  20. package/lib/chunks/{index-D2t64h6I.mjs.map → index-Br8WHz8e.mjs.map} +1 -1
  21. package/lib/chunks/{index-DrgP7cc7.mjs → index-CaobN7Im.mjs} +3 -3
  22. package/lib/chunks/{index-DrgP7cc7.mjs.map → index-CaobN7Im.mjs.map} +1 -1
  23. package/lib/chunks/{index-CA8OvqPT.mjs → index-Ch7DqyC4.mjs} +3 -3
  24. package/lib/chunks/{index-CA8OvqPT.mjs.map → index-Ch7DqyC4.mjs.map} +1 -1
  25. package/lib/chunks/{index-BPsJtP6e.mjs → index-DCGk-moA.mjs} +2 -2
  26. package/lib/chunks/{index-BPsJtP6e.mjs.map → index-DCGk-moA.mjs.map} +1 -1
  27. package/lib/chunks/{index-Dx8aLIDX.mjs → index-DEzF8C7L.mjs} +2 -2
  28. package/lib/chunks/{index-Dx8aLIDX.mjs.map → index-DEzF8C7L.mjs.map} +1 -1
  29. package/lib/chunks/{index-vaILKWGV.mjs → index-DKwN-YU-.mjs} +3 -3
  30. package/lib/chunks/{index-vaILKWGV.mjs.map → index-DKwN-YU-.mjs.map} +1 -1
  31. package/lib/chunks/index-DMmb2rBE.mjs +357 -0
  32. package/lib/chunks/index-DMmb2rBE.mjs.map +1 -0
  33. package/lib/chunks/{index-DdkkEzw3.mjs → index-D_cBflBv.mjs} +2 -2
  34. package/lib/chunks/{index-DdkkEzw3.mjs.map → index-D_cBflBv.mjs.map} +1 -1
  35. package/lib/chunks/{index-DlptjfMf.mjs → index-Dq-90KbM.mjs} +2 -2
  36. package/lib/chunks/{index-DlptjfMf.mjs.map → index-Dq-90KbM.mjs.map} +1 -1
  37. package/lib/chunks/{index-CJGtdAy7.mjs → index-Go2oJfny.mjs} +2 -2
  38. package/lib/chunks/{index-CJGtdAy7.mjs.map → index-Go2oJfny.mjs.map} +1 -1
  39. package/lib/chunks/{index-Cxf4CLJZ.mjs → index-d8Bt4gIX.mjs} +2 -2
  40. package/lib/chunks/{index-Cxf4CLJZ.mjs.map → index-d8Bt4gIX.mjs.map} +1 -1
  41. package/lib/chunks/{index-DAo4n3Mq.mjs → index-jHf5E4be.mjs} +2 -2
  42. package/lib/chunks/{index-DAo4n3Mq.mjs.map → index-jHf5E4be.mjs.map} +1 -1
  43. package/lib/chunks/{useShikiHighlight-Cq02e63J.mjs → useShikiHighlight-DoY3TBPT.mjs} +2 -2
  44. package/lib/chunks/{useShikiHighlight-Cq02e63J.mjs.map → useShikiHighlight-DoY3TBPT.mjs.map} +1 -1
  45. package/lib/components/ResizableSplit.d.ts +12 -2
  46. package/lib/components/ResizableSplit.d.ts.map +1 -1
  47. package/lib/index.cjs +30 -30
  48. package/lib/index.cjs.map +1 -1
  49. package/lib/index.css +1 -1
  50. package/lib/index.mjs +1 -1
  51. package/lib/renderers/Image/index.d.ts.map +1 -1
  52. package/lib/renderers/Zip/index.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/lib/chunks/index-BCbSb9Ob.mjs.map +0 -1
  55. package/lib/chunks/index-CeQf1qNC.mjs +0 -301
  56. package/lib/chunks/index-CeQf1qNC.mjs.map +0 -1
  57. package/lib/chunks/index-fhiaa9rv.mjs +0 -222
  58. package/lib/chunks/index-fhiaa9rv.mjs.map +0 -1
@@ -1,301 +0,0 @@
1
- import { jsxs as j, jsx as e, Fragment as O } from "react/jsx-runtime";
2
- import { useRef as Z, useState as o, useEffect as z, useCallback as F, useMemo as J, Suspense as Q, lazy as V } from "react";
3
- import { createPortal as K } from "react-dom";
4
- import { ChevronRight as ee, FolderOpen as re, Folder as te, FileImage as ne, FileText as fe, FileCode as se, File as le } from "lucide-react";
5
- import { R as ie } from "./RendererError-BH6fzLrN.mjs";
6
- import { u as pe, a as oe, c as H, R as ae, j as ce, D as de, A as ue, C as me, F as he } from "./index-BCbSb9Ob.mjs";
7
- const xe = ({
8
- left: t,
9
- right: l,
10
- initialLeftWidth: w = 280,
11
- minLeftWidth: v = 160,
12
- maxLeftWidth: b = 640,
13
- minRightWidth: d = 200,
14
- storageKey: a,
15
- desktopMedia: p = "(min-width: 768px)",
16
- className: g = ""
17
- }) => {
18
- const y = Z(null), [u, N] = o(() => {
19
- if (a && typeof window < "u") {
20
- const s = Number(window.localStorage.getItem(a));
21
- if (!isNaN(s) && s > 0) return s;
22
- }
23
- return w;
24
- }), [c, L] = o(!1), [m, n] = o(!1);
25
- z(() => {
26
- if (typeof window > "u") return;
27
- const s = window.matchMedia(p), h = () => n(s.matches);
28
- return h(), s.addEventListener("change", h), () => s.removeEventListener("change", h);
29
- }, [p]), z(() => {
30
- if (!c) return;
31
- const s = (E) => {
32
- if (!y.current) return;
33
- const U = y.current.getBoundingClientRect(), x = E.clientX - U.left, S = U.width - d - 6, $ = Math.min(b, S), C = Math.max(v, Math.min($, x));
34
- N(C);
35
- }, h = () => L(!1);
36
- window.addEventListener("mousemove", s), window.addEventListener("mouseup", h);
37
- const R = document.body.style.cursor, M = document.body.style.userSelect;
38
- return document.body.style.cursor = "col-resize", document.body.style.userSelect = "none", () => {
39
- window.removeEventListener("mousemove", s), window.removeEventListener("mouseup", h), document.body.style.cursor = R, document.body.style.userSelect = M;
40
- };
41
- }, [c, v, b, d]), z(() => {
42
- if (!(!a || c))
43
- try {
44
- window.localStorage.setItem(a, String(u));
45
- } catch {
46
- }
47
- }, [u, a, c]);
48
- const T = F((s) => {
49
- s.preventDefault(), L(!0);
50
- }, []);
51
- return /* @__PURE__ */ j(
52
- "div",
53
- {
54
- ref: y,
55
- className: `rfp-w-full rfp-h-full rfp-flex rfp-flex-col md:rfp-flex-row rfp-min-h-0 rfp-min-w-0 ${g}`,
56
- children: [
57
- /* @__PURE__ */ e(
58
- "div",
59
- {
60
- 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",
61
- style: m ? { width: `${u}px` } : void 0,
62
- children: t
63
- }
64
- ),
65
- /* @__PURE__ */ e(
66
- "div",
67
- {
68
- role: "separator",
69
- "aria-orientation": "vertical",
70
- onMouseDown: T,
71
- className: `rfp-hidden md:rfp-block rfp-relative rfp-w-1.5 rfp-flex-shrink-0 rfp-cursor-col-resize rfp-transition-colors ${c ? "rfp-bg-surface-toolbar" : "rfp-bg-surface-2 hover:rfp-bg-surface-3"}`,
72
- children: /* @__PURE__ */ e("span", { className: "rfp-absolute rfp-inset-y-0 -rfp-left-1 -rfp-right-1" })
73
- }
74
- ),
75
- /* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-min-w-0 rfp-min-h-0 rfp-overflow-hidden", children: l })
76
- ]
77
- }
78
- );
79
- }, we = V(
80
- () => import("./index-BCbSb9Ob.mjs").then((t) => t.i).then((t) => ({ default: t.FilePreviewContent }))
81
- ), ve = (t) => {
82
- const l = he({ name: t, type: "" });
83
- return l === "image" ? ne : l === "text" || l === "markdown" || l === "json" || l === "csv" || l === "xml" || l === "subtitle" ? t.endsWith(".md") || t.endsWith(".markdown") ? fe : se : le;
84
- }, _ = ({
85
- node: t,
86
- depth: l,
87
- selectedPath: w,
88
- expanded: v,
89
- onToggle: b,
90
- onSelect: d,
91
- onHover: a,
92
- onLeave: p
93
- }) => {
94
- var L;
95
- const g = v.has(t.path), y = w === t.path, u = { paddingLeft: `${l * 14 + 10}px` }, N = (m) => {
96
- const n = m.currentTarget.getBoundingClientRect();
97
- a(t.name || "/", n);
98
- };
99
- if (t.isDir)
100
- return /* @__PURE__ */ j(O, { children: [
101
- /* @__PURE__ */ j(
102
- "button",
103
- {
104
- type: "button",
105
- onClick: () => b(t.path),
106
- onMouseEnter: N,
107
- onMouseLeave: p,
108
- 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",
109
- style: u,
110
- children: [
111
- /* @__PURE__ */ e(
112
- ee,
113
- {
114
- className: `rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0 rfp-transition-transform ${g ? "rfp-rotate-90" : ""}`
115
- }
116
- ),
117
- g ? /* @__PURE__ */ e(re, { className: "rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80" }) : /* @__PURE__ */ e(te, { className: "rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-amber-300/80" }),
118
- /* @__PURE__ */ e("span", { className: "rfp-truncate rfp-flex-1 rfp-min-w-0", children: t.name || "/" })
119
- ]
120
- }
121
- ),
122
- g && ((L = t.children) == null ? void 0 : L.map((m) => /* @__PURE__ */ e(
123
- _,
124
- {
125
- node: m,
126
- depth: l + 1,
127
- selectedPath: w,
128
- expanded: v,
129
- onToggle: b,
130
- onSelect: d,
131
- onHover: a,
132
- onLeave: p
133
- },
134
- m.path
135
- )))
136
- ] });
137
- const c = ve(t.name);
138
- return /* @__PURE__ */ j(
139
- "button",
140
- {
141
- type: "button",
142
- onClick: () => d(t),
143
- onMouseEnter: N,
144
- onMouseLeave: p,
145
- 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 ${y ? "rfp-bg-surface-2 rfp-text-fg-primary" : "rfp-text-fg-secondary hover:rfp-bg-surface-1"}`,
146
- style: u,
147
- children: [
148
- /* @__PURE__ */ e("span", { className: "rfp-w-3.5 rfp-h-3.5 rfp-flex-shrink-0" }),
149
- /* @__PURE__ */ e(c, { className: "rfp-w-4 rfp-h-4 rfp-flex-shrink-0 rfp-text-fg-tertiary" }),
150
- /* @__PURE__ */ e("span", { className: "rfp-flex-1 rfp-truncate rfp-min-w-0", children: t.name }),
151
- /* @__PURE__ */ e("span", { className: "rfp-text-xs rfp-text-fg-disabled rfp-flex-shrink-0 rfp-ml-2", children: me(t.size) })
152
- ]
153
- }
154
- );
155
- }, ze = ({ url: t, nestingDepth: l = 0, onStatsChange: w }) => {
156
- var W;
157
- const v = pe(), b = oe(), [d, a] = o(null), [p, g] = o(null), [y, u] = o(!0), [N, c] = o(null), [L, m] = o(/* @__PURE__ */ new Set([""])), [n, T] = o(null), [s, h] = o(!1), [R, M] = o(null), [E, U] = o(null), x = Z(w);
158
- z(() => {
159
- x.current = w;
160
- }, [w]), z(() => {
161
- if (!t) return;
162
- let r = !1;
163
- return (async () => {
164
- try {
165
- u(!0), c(null);
166
- const i = await b(t);
167
- if (!i.ok) throw new Error("加载失败");
168
- const D = await i.arrayBuffer(), k = await ce(D);
169
- if (r) return;
170
- const I = de(k), P = ue(I);
171
- a(k), g(P);
172
- const B = /* @__PURE__ */ new Set([""]);
173
- if (P.children)
174
- for (const A of P.children) A.isDir && B.add(A.path);
175
- m(B);
176
- } catch (i) {
177
- console.error(i), r || c(v("zip.load_failed"));
178
- } finally {
179
- r || u(!1);
180
- }
181
- })(), () => {
182
- r = !0;
183
- };
184
- }, [t]), z(() => () => {
185
- n != null && n.blobUrl && URL.revokeObjectURL(n.blobUrl);
186
- }, [n]);
187
- const S = J(() => {
188
- if (!p) return null;
189
- let r = 0, f = 0, i = 0;
190
- const D = (k) => {
191
- var I;
192
- k.isDir ? (k.path && f++, (I = k.children) == null || I.forEach(D)) : (r++, i += k.size);
193
- };
194
- return D(p), { files: r, dirs: f, size: i };
195
- }, [p]);
196
- z(() => {
197
- var r;
198
- return (r = x.current) == null || r.call(x, S), () => {
199
- var f;
200
- (f = x.current) == null || f.call(x, null);
201
- };
202
- }, [S]);
203
- const $ = F((r) => {
204
- m((f) => {
205
- const i = new Set(f);
206
- return i.has(r) ? i.delete(r) : i.add(r), i;
207
- });
208
- }, []), C = F((r, f) => {
209
- U({
210
- text: r,
211
- x: f.right + 8,
212
- y: f.top + f.height / 2
213
- });
214
- }, []), q = F(() => {
215
- U(null);
216
- }, []), X = F(
217
- async (r) => {
218
- if (!(!d || r.isDir)) {
219
- n != null && n.blobUrl && URL.revokeObjectURL(n.blobUrl), h(!0), M(null);
220
- try {
221
- const f = H(r.name), i = await ae(d, r.path, f !== "application/octet-stream" ? f : void 0), D = URL.createObjectURL(i);
222
- T({ path: r.path, name: r.name, size: r.size, blobUrl: D });
223
- } catch (f) {
224
- console.error(f), M("条目读取失败");
225
- } finally {
226
- h(!1);
227
- }
228
- }
229
- },
230
- [d, n]
231
- );
232
- if (y)
233
- 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" }) });
234
- if (N || !p)
235
- return /* @__PURE__ */ e(ie, { message: N || v("zip.parse_failed") });
236
- const Y = /* @__PURE__ */ e("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto", children: (W = p.children) == null ? void 0 : W.map((r) => /* @__PURE__ */ e(
237
- _,
238
- {
239
- node: r,
240
- depth: 0,
241
- selectedPath: (n == null ? void 0 : n.path) ?? null,
242
- expanded: L,
243
- onToggle: $,
244
- onSelect: X,
245
- onHover: C,
246
- onLeave: q
247
- },
248
- r.path
249
- )) }), G = /* @__PURE__ */ j("div", { className: "rfp-w-full rfp-h-full rfp-flex rfp-flex-col", children: [
250
- !n && /* @__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: "从左侧选择一个文件以预览" }),
251
- n && s && /* @__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" }) }),
252
- n && !s && R && /* @__PURE__ */ e("div", { className: "rfp-flex-1 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-secondary", children: R }),
253
- n && !s && !R && /* @__PURE__ */ e(O, { 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(
254
- Q,
255
- {
256
- 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" }) }),
257
- children: /* @__PURE__ */ e(
258
- we,
259
- {
260
- mode: "embed",
261
- files: [{ name: n.name, url: n.blobUrl, type: H(n.name) }],
262
- currentIndex: 0,
263
- zipNestingDepth: l + 1
264
- }
265
- )
266
- }
267
- ) }) })
268
- ] });
269
- return /* @__PURE__ */ j(O, { children: [
270
- /* @__PURE__ */ e(
271
- xe,
272
- {
273
- left: Y,
274
- right: G,
275
- initialLeftWidth: 280,
276
- minLeftWidth: 180,
277
- maxLeftWidth: 560,
278
- storageKey: "rfp-zip-split-left"
279
- }
280
- ),
281
- E && typeof document < "u" && K(
282
- /* @__PURE__ */ e(
283
- "div",
284
- {
285
- 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",
286
- style: {
287
- left: `${E.x}px`,
288
- top: `${E.y}px`,
289
- transform: "translateY(-50%)"
290
- },
291
- children: E.text
292
- }
293
- ),
294
- document.body
295
- )
296
- ] });
297
- };
298
- export {
299
- ze as ZipRenderer
300
- };
301
- //# sourceMappingURL=index-CeQf1qNC.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-CeQf1qNC.mjs","sources":["../../src/components/ResizableSplit.tsx","../../src/renderers/Zip/index.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState, useCallback } 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}\n\n/**\n * 通用可拖动分隔布局:\n * - 桌面端(由 `desktopMedia` 判定)横向分两栏,中间分隔线可左右拖动调整左栏宽度\n * - 移动端退化为上下堆叠,不显示分隔线\n * - 可选 `storageKey` 将宽度持久化到 localStorage\n */\nexport const ResizableSplit: React.FC<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}) => {\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\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 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","import { useState, useEffect, useMemo, useCallback, useRef, lazy, Suspense } 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 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 } from '../../components/ResizableSplit';\nimport type { ZipToolbarStats } from './toolbar';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\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: React.FC<ZipRendererProps> = ({ url, nestingDepth = 0, onStatsChange }) => {\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\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 } catch (err) {\n console.error(err);\n setPreviewError('条目读取失败');\n } finally {\n setPreviewLoading(false);\n }\n },\n [zip, selected]\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={[{ name: selected.name, url: selected.blobUrl, type: inferMimeType(selected.name) }]}\n currentIndex={0}\n zipNestingDepth={nestingDepth + 1}\n />\n </Suspense>\n </div>\n </>\n )}\n </div>\n );\n\n return (\n <>\n <ResizableSplit\n left={leftPane}\n right={rightPane}\n initialLeftWidth={280}\n minLeftWidth={180}\n maxLeftWidth={560}\n storageKey=\"rfp-zip-split-left\"\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","left","right","initialLeftWidth","minLeftWidth","maxLeftWidth","minRightWidth","storageKey","desktopMedia","className","containerRef","useRef","leftWidth","setLeftWidth","useState","saved","dragging","setDragging","isDesktop","setIsDesktop","useEffect","mq","handler","handleMove","e","rect","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","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","RendererError","leftPane","rightPane","Suspense","createPortal"],"mappings":"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"}
@@ -1,222 +0,0 @@
1
- import { jsxs as v, jsx as i } from "react/jsx-runtime";
2
- import { useState as c, useRef as w, useEffect as P, useCallback as $ } from "react";
3
- import { motion as me } from "framer-motion";
4
- import { Loader2 as be } from "lucide-react";
5
- import { u as ge, G as he, q as xe } from "./index-BCbSb9Ob.mjs";
6
- import { R as se } from "./RendererError-BH6fzLrN.mjs";
7
- const Ue = ({
8
- url: B,
9
- zoom: K,
10
- rotation: ae,
11
- resetKey: Q,
12
- fileSize: b,
13
- file: R,
14
- onZoomChange: d,
15
- onNaturalWidthChange: T,
16
- onNaturalHeightChange: F
17
- }) => {
18
- const A = ge(), [N, q] = c(!1), [g, J] = c(null), [O, u] = c(!1), [V, z] = c(0), [L, G] = c(null), [C, h] = c(""), [M, S] = c(1), [U, Z] = c(1), [j, k] = c({ x: 0, y: 0 }), [I, ee] = c(!1), [W, ce] = c({ x: 0, y: 0 }), [_, Y] = c(1), [p, oe] = c({ width: 0, height: 0 }), fe = w(null), H = w(null), x = w(null), D = w(null), E = w(null), o = w(/* @__PURE__ */ new Map());
19
- P(() => {
20
- let e = !1;
21
- return (async () => {
22
- if (h(""), q(!1), J(null), u(!1), G(null), z(0), k({ x: 0, y: 0 }), Y(1), S(1), Z(1), x.current && (URL.revokeObjectURL(x.current), x.current = null), o.current.forEach((r) => URL.revokeObjectURL(r)), o.current.clear(), D.current = null, E.current = null, !R) {
23
- e || h(B);
24
- return;
25
- }
26
- try {
27
- const r = await he(R), t = await xe(r);
28
- if (!t || !await t.needsDecode(r)) {
29
- e || h(B);
30
- return;
31
- }
32
- u(!0);
33
- let n;
34
- if (R instanceof Blob)
35
- n = R;
36
- else {
37
- const a = await fetch(B);
38
- if (!a.ok) throw new Error("Failed to fetch file");
39
- n = await a.blob();
40
- }
41
- if (e) return;
42
- if (D.current = n, E.current = t, D.current = n, E.current = t, t.getMetadata)
43
- try {
44
- const a = await t.getMetadata(n);
45
- !e && a.pageCount && a.pageCount > 1 && Z(a.pageCount);
46
- } catch {
47
- }
48
- const f = await t.decode(n, {
49
- page: 1,
50
- fullQuality: !1,
51
- onProgress: (a) => {
52
- e || z(a);
53
- }
54
- });
55
- if (e) return;
56
- const m = typeof f == "string" ? f : URL.createObjectURL(f);
57
- x.current = m, o.current.set(1, m), h(m), u(!1);
58
- } catch (r) {
59
- e || (G((r == null ? void 0 : r.message) || "解码失败"), u(!1));
60
- }
61
- })(), () => {
62
- e = !0;
63
- };
64
- }, [B, R]);
65
- const re = $(async (e) => {
66
- if (!D.current || !E.current || e < 1 || e > U) return;
67
- const s = o.current.get(e);
68
- if (s) {
69
- S(e), h(s);
70
- return;
71
- }
72
- u(!0);
73
- try {
74
- const r = await E.current.decode(D.current, { page: e }), t = typeof r == "string" ? r : URL.createObjectURL(r);
75
- if (o.current.size >= 10) {
76
- const n = o.current.keys().next().value;
77
- if (n !== void 0) {
78
- const f = o.current.get(n);
79
- f && URL.revokeObjectURL(f), o.current.delete(n);
80
- }
81
- }
82
- o.current.set(e, t), S(e), h(t), u(!1);
83
- } catch (r) {
84
- G((r == null ? void 0 : r.message) || "翻页解码失败"), u(!1);
85
- }
86
- }, [U]);
87
- P(() => () => {
88
- x.current && URL.revokeObjectURL(x.current), o.current.forEach((e) => URL.revokeObjectURL(e)), o.current.clear();
89
- }, []), P(() => {
90
- Y(K);
91
- }, [K]), P(() => {
92
- Q !== void 0 && k({ x: 0, y: 0 });
93
- }, [Q]);
94
- const ie = (e) => {
95
- q(!0);
96
- const s = e.currentTarget;
97
- oe({ width: s.naturalWidth, height: s.naturalHeight }), T == null || T(s.naturalWidth), F == null || F(s.naturalHeight);
98
- }, X = $((e, s) => {
99
- const r = H.current;
100
- if (!r || p.width === 0) return e;
101
- const t = r.clientWidth, n = r.clientHeight, f = p.width * s, m = p.height * s, a = Math.min(80, t * 0.15, n * 0.15), l = (t + f) / 2 - a, y = (n + m) / 2 - a;
102
- return {
103
- x: l > 0 ? Math.max(-l, Math.min(l, e.x)) : 0,
104
- y: y > 0 ? Math.max(-y, Math.min(y, e.y)) : 0
105
- };
106
- }, [p]), le = () => {
107
- J(A("image.load_failed")), q(!0);
108
- }, de = () => {
109
- k({ x: 0, y: 0 }), Y(1), d == null || d(1);
110
- };
111
- P(() => {
112
- const e = H.current;
113
- if (!e) return;
114
- const s = (r) => {
115
- r.preventDefault(), r.stopPropagation();
116
- const t = e.getBoundingClientRect(), n = r.clientX - t.left - t.width / 2, f = r.clientY - t.top - t.height / 2, m = r.deltaY > 0 ? -0.05 : 0.05;
117
- Y((a) => {
118
- const l = Math.max(0.01, Math.min(10, a + m)), y = l / a;
119
- return k((ne) => X({
120
- x: n - y * (n - ne.x),
121
- y: f - y * (f - ne.y)
122
- }, l)), d == null || d(l), l;
123
- });
124
- };
125
- return e.addEventListener("wheel", s, { passive: !1 }), () => e.removeEventListener("wheel", s);
126
- }, [d, X]);
127
- const ue = $((e) => {
128
- e.button === 0 && (ee(!0), ce({
129
- x: e.clientX - j.x,
130
- y: e.clientY - j.y
131
- }));
132
- }, [j]), pe = $((e) => {
133
- I && k(X({
134
- x: e.clientX - W.x,
135
- y: e.clientY - W.y
136
- }, _));
137
- }, [I, W, _, X]), te = $(() => {
138
- ee(!1);
139
- }, []);
140
- return /* @__PURE__ */ v(
141
- "div",
142
- {
143
- ref: H,
144
- className: "rfp-relative rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-overflow-hidden",
145
- onMouseDown: ue,
146
- onMouseMove: pe,
147
- onMouseUp: te,
148
- onMouseLeave: te,
149
- style: { cursor: I ? "grabbing" : "grab" },
150
- children: [
151
- O && /* @__PURE__ */ v("div", { className: "rfp-absolute rfp-inset-0 rfp-flex rfp-flex-col rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10", children: [
152
- /* @__PURE__ */ i(be, { className: "rfp-w-12 rfp-h-12 rfp-text-fg-primary rfp-animate-spin" }),
153
- /* @__PURE__ */ v("p", { className: "rfp-mt-4 rfp-text-fg-secondary", children: [
154
- "正在解码... ",
155
- V > 0 && `${Math.round(V)}%`
156
- ] })
157
- ] }),
158
- L && /* @__PURE__ */ i("div", { className: "rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10", children: /* @__PURE__ */ i(se, { message: A("image.decode_failed"), detail: L }) }),
159
- !N && !g && !O && !L && /* @__PURE__ */ i("div", { className: "rfp-flex rfp-items-center rfp-justify-center", children: /* @__PURE__ */ i("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" }) }),
160
- g && /* @__PURE__ */ i(se, { message: g }),
161
- C && /* @__PURE__ */ i(
162
- me.img,
163
- {
164
- ref: fe,
165
- src: C,
166
- alt: "Preview",
167
- className: `rfp-max-w-none rfp-select-none ${!N || g || L ? "rfp-hidden" : ""}`,
168
- style: {
169
- transform: `translate(${j.x}px, ${j.y}px) scale(${_}) rotate(${ae}deg)`,
170
- transformOrigin: "center",
171
- transition: I ? "none" : "transform 0.3s ease-out"
172
- },
173
- onLoad: ie,
174
- onError: le,
175
- onDoubleClick: de,
176
- initial: { opacity: 0 },
177
- animate: { opacity: N && !g && !L ? 1 : 0 },
178
- transition: { duration: 0.3 },
179
- draggable: !1
180
- }
181
- ),
182
- N && !g && p.width > 0 && /* @__PURE__ */ v("div", { className: "rfp-absolute rfp-bottom-2 rfp-right-3 rfp-text-[10px] rfp-text-fg-disabled hover:rfp-text-fg-secondary rfp-transition-colors rfp-pointer-events-auto rfp-select-none rfp-cursor-default", children: [
183
- p.width,
184
- " × ",
185
- p.height,
186
- b != null && ` · ${b < 1024 ? `${b} B` : b < 1024 * 1024 ? `${(b / 1024).toFixed(1)} KB` : `${(b / (1024 * 1024)).toFixed(1)} MB`}`
187
- ] }),
188
- U > 1 && /* @__PURE__ */ v("div", { className: "rfp-absolute rfp-bottom-2 rfp-left-1/2 -rfp-translate-x-1/2 rfp-flex rfp-items-center rfp-gap-2 rfp-px-3 rfp-py-1.5 rfp-bg-surface-toolbar rfp-border rfp-border-line rfp-rounded-lg rfp-text-sm rfp-text-fg-primary rfp-shadow-md", children: [
189
- /* @__PURE__ */ i(
190
- "button",
191
- {
192
- type: "button",
193
- onClick: () => re(M - 1),
194
- disabled: M <= 1 || O,
195
- className: "rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed",
196
- children: "上一页"
197
- }
198
- ),
199
- /* @__PURE__ */ v("span", { className: "rfp-text-fg-secondary rfp-tabular-nums", children: [
200
- M,
201
- " / ",
202
- U
203
- ] }),
204
- /* @__PURE__ */ i(
205
- "button",
206
- {
207
- type: "button",
208
- onClick: () => re(M + 1),
209
- disabled: M >= U || O,
210
- className: "rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed",
211
- children: "下一页"
212
- }
213
- )
214
- ] })
215
- ]
216
- }
217
- );
218
- };
219
- export {
220
- Ue as ImageRenderer
221
- };
222
- //# sourceMappingURL=index-fhiaa9rv.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-fhiaa9rv.mjs","sources":["../../src/renderers/Image/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { motion } from 'framer-motion';\nimport { Loader2 } from 'lucide-react';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { detectImageFormat, getLoaderForMimeType } from '@eternalheart/file-preview-core';\nimport type { PreviewFile } from '@eternalheart/file-preview-core';\nimport { RendererError } from '../RendererError';\n\ninterface ImageRendererProps {\n url: string;\n zoom: number;\n rotation: number;\n resetKey?: number;\n fileSize?: number;\n file?: PreviewFile | File;\n onZoomChange?: (zoom: number) => void;\n onNaturalWidthChange?: (width: number) => void;\n onNaturalHeightChange?: (height: number) => void;\n}\n\nexport const ImageRenderer: React.FC<ImageRendererProps> = ({\n url,\n zoom,\n rotation,\n resetKey,\n fileSize,\n file,\n onZoomChange,\n onNaturalWidthChange,\n onNaturalHeightChange\n}) => {\n const t = useTranslator();\n const [loaded, setLoaded] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [decoding, setDecoding] = useState(false);\n const [decodeProgress, setDecodeProgress] = useState(0);\n const [decodeError, setDecodeError] = useState<string | null>(null);\n const [imageSrc, setImageSrc] = useState<string>('');\n const [currentPage, setCurrentPage] = useState(1);\n const [totalPages, setTotalPages] = useState(1);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const [dragStart, setDragStart] = useState({ x: 0, y: 0 });\n const [internalZoom, setInternalZoom] = useState(1);\n const [naturalSize, setNaturalSize] = useState({ width: 0, height: 0 });\n const imgRef = useRef<HTMLImageElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const blobUrlRef = useRef<string | null>(null);\n const fileBlobRef = useRef<Blob | null>(null);\n const loaderRef = useRef<any>(null);\n const pageCacheRef = useRef<Map<number, string>>(new Map());\n\n // 解码逻辑:检测格式并按需解码\n useEffect(() => {\n let cancelled = false;\n\n const decodeIfNeeded = async () => {\n // 重置状态:清空 src 以避免上一张图片的 onLoad/onError 误触发到新文件\n setImageSrc('');\n setLoaded(false);\n setError(null);\n setDecoding(false);\n setDecodeError(null);\n setDecodeProgress(0);\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n setCurrentPage(1);\n setTotalPages(1);\n\n // 清理旧的 blob URL 与缓存\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n blobUrlRef.current = null;\n }\n pageCacheRef.current.forEach((url) => URL.revokeObjectURL(url));\n pageCacheRef.current.clear();\n fileBlobRef.current = null;\n loaderRef.current = null;\n\n // 如果没有 file 对象,直接使用 url\n if (!file) {\n if (!cancelled) setImageSrc(url);\n return;\n }\n\n try {\n // 检测图片格式\n const mimeType = await detectImageFormat(file);\n const loader = await getLoaderForMimeType(mimeType);\n\n // 如果不需要解码,直接使用原 URL\n if (!loader || !(await loader.needsDecode(mimeType))) {\n if (!cancelled) setImageSrc(url);\n return;\n }\n\n // 需要解码\n setDecoding(true);\n\n // 获取文件 Blob\n let fileBlob: Blob;\n if (file instanceof Blob) {\n fileBlob = file;\n } else {\n const response = await fetch(url);\n if (!response.ok) throw new Error('Failed to fetch file');\n fileBlob = await response.blob();\n }\n\n if (cancelled) return;\n\n // 缓存 Blob 与 loader 以便后续翻页\n fileBlobRef.current = fileBlob;\n loaderRef.current = loader;\n\n // 缓存 Blob 与 loader 以便后续翻页\n fileBlobRef.current = fileBlob;\n loaderRef.current = loader;\n\n // 获取元数据(用于检测多页 TIFF)\n if (loader.getMetadata) {\n try {\n const metadata = await loader.getMetadata(fileBlob);\n if (!cancelled && metadata.pageCount && metadata.pageCount > 1) {\n setTotalPages(metadata.pageCount);\n }\n } catch {\n // 忽略元数据获取失败\n }\n }\n\n // 调用 loader 解码(第 1 页 / 缩略图模式)\n const decodedBlob = await loader.decode(fileBlob, {\n page: 1,\n fullQuality: false,\n onProgress: (percent: number) => {\n if (!cancelled) {\n setDecodeProgress(percent);\n }\n },\n });\n\n if (cancelled) return;\n\n // 生成 blob URL\n const blobUrl = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n blobUrlRef.current = blobUrl;\n pageCacheRef.current.set(1, blobUrl);\n setImageSrc(blobUrl);\n setDecoding(false);\n } catch (err: any) {\n if (!cancelled) {\n setDecodeError(err?.message || '解码失败');\n setDecoding(false);\n }\n }\n };\n\n decodeIfNeeded();\n\n return () => {\n cancelled = true;\n };\n }, [url, file]);\n\n // 多页 TIFF 翻页:切换页码时重新解码\n const handlePageChange = useCallback(async (page: number) => {\n if (!fileBlobRef.current || !loaderRef.current) return;\n if (page < 1 || page > totalPages) return;\n\n // 命中缓存:直接切换\n const cached = pageCacheRef.current.get(page);\n if (cached) {\n setCurrentPage(page);\n setImageSrc(cached);\n return;\n }\n\n // 解码新页面\n setDecoding(true);\n try {\n const decodedBlob = await loaderRef.current.decode(fileBlobRef.current, { page });\n const blobUrl = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n // LRU:缓存超过 10 页时删除最早的\n if (pageCacheRef.current.size >= 10) {\n const firstKey = pageCacheRef.current.keys().next().value;\n if (firstKey !== undefined) {\n const oldUrl = pageCacheRef.current.get(firstKey);\n if (oldUrl) URL.revokeObjectURL(oldUrl);\n pageCacheRef.current.delete(firstKey);\n }\n }\n\n pageCacheRef.current.set(page, blobUrl);\n setCurrentPage(page);\n setImageSrc(blobUrl);\n setDecoding(false);\n } catch (err: any) {\n setDecodeError(err?.message || '翻页解码失败');\n setDecoding(false);\n }\n }, [totalPages]);\n\n // Cleanup blob URL on unmount\n useEffect(() => {\n return () => {\n if (blobUrlRef.current) {\n URL.revokeObjectURL(blobUrlRef.current);\n }\n pageCacheRef.current.forEach((url) => URL.revokeObjectURL(url));\n pageCacheRef.current.clear();\n };\n }, []);\n\n // 当外部 zoom 改变时,同步内部 zoom\n useEffect(() => {\n setInternalZoom(zoom);\n }, [zoom]);\n\n // 适应窗口/原始尺寸等操作时重置位置居中\n useEffect(() => {\n if (resetKey !== undefined) {\n setPosition({ x: 0, y: 0 });\n }\n }, [resetKey]);\n\n const handleLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n setLoaded(true);\n const img = e.currentTarget;\n setNaturalSize({ width: img.naturalWidth, height: img.naturalHeight });\n onNaturalWidthChange?.(img.naturalWidth);\n onNaturalHeightChange?.(img.naturalHeight);\n };\n\n // 边界限制:确保图片至少有一部分可见\n const clampPosition = useCallback((pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.current;\n if (!container || naturalSize.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.width * currentZoom;\n const imgH = naturalSize.height * currentZoom;\n\n // 至少保留 margin px 的图片在视口内\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n }, [naturalSize]);\n\n const handleError = () => {\n setError(t('image.load_failed'));\n setLoaded(true);\n };\n\n // 双击复原:居中 + 缩放100%\n const handleDoubleClick = () => {\n setPosition({ x: 0, y: 0 });\n setInternalZoom(1);\n onZoomChange?.(1);\n };\n\n // 鼠标滚轮缩放 —— 以鼠标位置为缩放原点\n // 使用原生事件 + passive: false,确保 preventDefault 生效,\n // 避免滚轮事件冒泡触发外层(如嵌入模式下的页面滚动)\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n\n setInternalZoom(prev => {\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n setPosition(pos => clampPosition({\n x: mouseX - scale * (mouseX - pos.x),\n y: mouseY - scale * (mouseY - pos.y),\n }, newZoom));\n\n onZoomChange?.(newZoom);\n return newZoom;\n });\n };\n\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n return () => container.removeEventListener('wheel', handleWheelNative);\n }, [onZoomChange, clampPosition]);\n\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n if (e.button !== 0) return;\n setIsDragging(true);\n setDragStart({\n x: e.clientX - position.x,\n y: e.clientY - position.y,\n });\n }, [position]);\n\n const handleMouseMove = useCallback((e: React.MouseEvent) => {\n if (!isDragging) return;\n setPosition(clampPosition({\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n }, internalZoom));\n }, [isDragging, dragStart, internalZoom, clampPosition]);\n\n const handleMouseUp = useCallback(() => {\n setIsDragging(false);\n }, []);\n\n return (\n <div\n ref={containerRef}\n className=\"rfp-relative rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-overflow-hidden\"\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n style={{ cursor: isDragging ? 'grabbing' : 'grab' }}\n >\n {/* 解码中 */}\n {decoding && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-flex-col rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10\">\n <Loader2 className=\"rfp-w-12 rfp-h-12 rfp-text-fg-primary rfp-animate-spin\" />\n <p className=\"rfp-mt-4 rfp-text-fg-secondary\">\n 正在解码... {decodeProgress > 0 && `${Math.round(decodeProgress)}%`}\n </p>\n </div>\n )}\n\n {/* 解码错误 */}\n {decodeError && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-1/80 rfp-z-10\">\n <RendererError message={t('image.decode_failed')} detail={decodeError} />\n </div>\n )}\n\n {!loaded && !error && !decoding && !decodeError && (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center\">\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 {error && (\n <RendererError message={error} />\n )}\n\n {imageSrc && (\n <motion.img\n ref={imgRef}\n src={imageSrc}\n alt=\"Preview\"\n className={`rfp-max-w-none rfp-select-none ${!loaded || error || decodeError ? 'rfp-hidden' : ''}`}\n style={{\n transform: `translate(${position.x}px, ${position.y}px) scale(${internalZoom}) rotate(${rotation}deg)`,\n transformOrigin: 'center',\n transition: isDragging ? 'none' : 'transform 0.3s ease-out',\n }}\n onLoad={handleLoad}\n onError={handleError}\n onDoubleClick={handleDoubleClick}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded && !error && !decodeError ? 1 : 0 }}\n transition={{ duration: 0.3 }}\n draggable={false}\n />\n )}\n\n {/* 右下角分辨率 */}\n {loaded && !error && naturalSize.width > 0 && (\n <div className=\"rfp-absolute rfp-bottom-2 rfp-right-3 rfp-text-[10px] rfp-text-fg-disabled hover:rfp-text-fg-secondary rfp-transition-colors rfp-pointer-events-auto rfp-select-none rfp-cursor-default\">\n {naturalSize.width} × {naturalSize.height}{fileSize != null && ` · ${fileSize < 1024 ? `${fileSize} B` : fileSize < 1024 * 1024 ? `${(fileSize / 1024).toFixed(1)} KB` : `${(fileSize / (1024 * 1024)).toFixed(1)} MB`}`}\n </div>\n )}\n\n {/* 多页 TIFF 翻页器 */}\n {totalPages > 1 && (\n <div className=\"rfp-absolute rfp-bottom-2 rfp-left-1/2 -rfp-translate-x-1/2 rfp-flex rfp-items-center rfp-gap-2 rfp-px-3 rfp-py-1.5 rfp-bg-surface-toolbar rfp-border rfp-border-line rfp-rounded-lg rfp-text-sm rfp-text-fg-primary rfp-shadow-md\">\n <button\n type=\"button\"\n onClick={() => handlePageChange(currentPage - 1)}\n disabled={currentPage <= 1 || decoding}\n className=\"rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed\"\n >\n 上一页\n </button>\n <span className=\"rfp-text-fg-secondary rfp-tabular-nums\">\n {currentPage} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => handlePageChange(currentPage + 1)}\n disabled={currentPage >= totalPages || decoding}\n className=\"rfp-px-2 rfp-py-0.5 rfp-rounded hover:rfp-bg-surface-nav-hover disabled:rfp-opacity-40 disabled:rfp-cursor-not-allowed\"\n >\n 下一页\n </button>\n </div>\n )}\n </div>\n );\n};\n"],"names":["ImageRenderer","url","zoom","rotation","resetKey","fileSize","file","onZoomChange","onNaturalWidthChange","onNaturalHeightChange","t","useTranslator","loaded","setLoaded","useState","error","setError","decoding","setDecoding","decodeProgress","setDecodeProgress","decodeError","setDecodeError","imageSrc","setImageSrc","currentPage","setCurrentPage","totalPages","setTotalPages","position","setPosition","isDragging","setIsDragging","dragStart","setDragStart","internalZoom","setInternalZoom","naturalSize","setNaturalSize","imgRef","useRef","containerRef","blobUrlRef","fileBlobRef","loaderRef","pageCacheRef","useEffect","cancelled","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","blobUrl","err","handlePageChange","useCallback","page","cached","firstKey","oldUrl","handleLoad","img","clampPosition","pos","currentZoom","container","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleError","handleDoubleClick","handleWheelNative","e","rect","mouseX","mouseY","delta","prev","newZoom","scale","handleMouseDown","handleMouseMove","handleMouseUp","jsxs","jsx","Loader2","RendererError","motion"],"mappings":"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"}