@eternalheart/react-file-preview 1.3.14 → 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.
Files changed (166) hide show
  1. package/README.md +437 -60
  2. package/README.zh-CN.md +437 -60
  3. package/lib/FilePreviewContent.d.ts +4 -17
  4. package/lib/FilePreviewContent.d.ts.map +1 -1
  5. package/lib/FilePreviewEmbed.d.ts +2 -0
  6. package/lib/FilePreviewEmbed.d.ts.map +1 -1
  7. package/lib/FilePreviewModal.d.ts +2 -0
  8. package/lib/FilePreviewModal.d.ts.map +1 -1
  9. package/lib/chunks/RendererError-D5i8eSpN.mjs +15 -0
  10. package/lib/chunks/RendererError-D5i8eSpN.mjs.map +1 -0
  11. package/lib/chunks/index-2sX2d4iv.mjs +291 -0
  12. package/lib/chunks/index-2sX2d4iv.mjs.map +1 -0
  13. package/lib/chunks/index-Bdj8_B80.mjs +120 -0
  14. package/lib/chunks/index-Bdj8_B80.mjs.map +1 -0
  15. package/lib/chunks/index-CCcZzLUM.mjs +107 -0
  16. package/lib/chunks/index-CCcZzLUM.mjs.map +1 -0
  17. package/lib/chunks/{index-BfzV7KIz.mjs → index-CKdQL1Bk.mjs} +130 -128
  18. package/lib/chunks/{index-BfzV7KIz.mjs.map → index-CKdQL1Bk.mjs.map} +1 -1
  19. package/lib/chunks/index-CQYrhe7Z.mjs +275 -0
  20. package/lib/chunks/index-CQYrhe7Z.mjs.map +1 -0
  21. package/lib/chunks/{index-DEzF8C7L.mjs → index-CRZqNMQ7.mjs} +43 -41
  22. package/lib/chunks/index-CRZqNMQ7.mjs.map +1 -0
  23. package/lib/chunks/{index-D_cBflBv.mjs → index-CTghYlSh.mjs} +1299 -1297
  24. package/lib/chunks/{index-D_cBflBv.mjs.map → index-CTghYlSh.mjs.map} +1 -1
  25. package/lib/chunks/index-CuTz7dbd.mjs +313 -0
  26. package/lib/chunks/index-CuTz7dbd.mjs.map +1 -0
  27. package/lib/chunks/index-CuWzRQZw.mjs +116 -0
  28. package/lib/chunks/index-CuWzRQZw.mjs.map +1 -0
  29. package/lib/chunks/index-Cz23v-TW.mjs +2409 -0
  30. package/lib/chunks/index-Cz23v-TW.mjs.map +1 -0
  31. package/lib/chunks/{index-Br8WHz8e.mjs → index-D-Is8qvU.mjs} +22 -20
  32. package/lib/chunks/index-D-Is8qvU.mjs.map +1 -0
  33. package/lib/chunks/index-Da3FN2-3.mjs +359 -0
  34. package/lib/chunks/index-Da3FN2-3.mjs.map +1 -0
  35. package/lib/chunks/index-Dc6q1OKl.mjs +78 -0
  36. package/lib/chunks/index-Dc6q1OKl.mjs.map +1 -0
  37. package/lib/chunks/{index-d8Bt4gIX.mjs → index-DoGKcq9y.mjs} +196 -194
  38. package/lib/chunks/index-DoGKcq9y.mjs.map +1 -0
  39. package/lib/chunks/{index-DCGk-moA.mjs → index-DzCLf1Db.mjs} +47 -45
  40. package/lib/chunks/index-DzCLf1Db.mjs.map +1 -0
  41. package/lib/chunks/index-FomaQSaL.mjs +329 -0
  42. package/lib/chunks/index-FomaQSaL.mjs.map +1 -0
  43. package/lib/chunks/{index-BTLV1YqJ.mjs → index-OXjOFggq.mjs} +864 -862
  44. package/lib/chunks/{index-BTLV1YqJ.mjs.map → index-OXjOFggq.mjs.map} +1 -1
  45. package/lib/chunks/index-WLepq2g2.mjs +200 -0
  46. package/lib/chunks/index-WLepq2g2.mjs.map +1 -0
  47. package/lib/chunks/{index-BG3Idu38.mjs → index-_B5marES.mjs} +27 -25
  48. package/lib/chunks/index-_B5marES.mjs.map +1 -0
  49. package/lib/chunks/useShikiHighlight-Bbs8Fbqs.mjs +36 -0
  50. package/lib/chunks/useShikiHighlight-Bbs8Fbqs.mjs.map +1 -0
  51. package/lib/components/preview/FilePreviewRenderer.d.ts +18 -0
  52. package/lib/components/preview/FilePreviewRenderer.d.ts.map +1 -0
  53. package/lib/components/preview/FilePreviewToolbar.d.ts +20 -0
  54. package/lib/components/preview/FilePreviewToolbar.d.ts.map +1 -0
  55. package/lib/components/preview/NavArrows.d.ts +18 -0
  56. package/lib/components/preview/NavArrows.d.ts.map +1 -0
  57. package/lib/components/preview/RendererError.d.ts +15 -0
  58. package/lib/components/preview/RendererError.d.ts.map +1 -0
  59. package/lib/components/preview/RendererErrorBoundary.d.ts +23 -0
  60. package/lib/components/preview/RendererErrorBoundary.d.ts.map +1 -0
  61. package/lib/components/preview/ToolbarButton.d.ts +16 -0
  62. package/lib/components/preview/ToolbarButton.d.ts.map +1 -0
  63. package/lib/components/preview/index.d.ts +7 -0
  64. package/lib/components/preview/index.d.ts.map +1 -0
  65. package/lib/hooks/index.d.ts +3 -0
  66. package/lib/hooks/index.d.ts.map +1 -0
  67. package/lib/hooks/useKeyboardNavigation.d.ts +15 -0
  68. package/lib/hooks/useKeyboardNavigation.d.ts.map +1 -0
  69. package/lib/hooks/useShikiHighlight.d.ts +3 -1
  70. package/lib/hooks/useShikiHighlight.d.ts.map +1 -1
  71. package/lib/hooks/useThemeMode.d.ts +7 -0
  72. package/lib/hooks/useThemeMode.d.ts.map +1 -0
  73. package/lib/index.cjs +27 -25
  74. package/lib/index.cjs.map +1 -1
  75. package/lib/index.css +1 -1
  76. package/lib/index.mjs +1 -1
  77. package/lib/renderers/Audio/index.d.ts +2 -1
  78. package/lib/renderers/Audio/index.d.ts.map +1 -1
  79. package/lib/renderers/Csv/index.d.ts +2 -1
  80. package/lib/renderers/Csv/index.d.ts.map +1 -1
  81. package/lib/renderers/Docx/index.d.ts +2 -1
  82. package/lib/renderers/Docx/index.d.ts.map +1 -1
  83. package/lib/renderers/Epub/index.d.ts +2 -3
  84. package/lib/renderers/Epub/index.d.ts.map +1 -1
  85. package/lib/renderers/Font/index.d.ts +2 -1
  86. package/lib/renderers/Font/index.d.ts.map +1 -1
  87. package/lib/renderers/Image/index.d.ts +6 -7
  88. package/lib/renderers/Image/index.d.ts.map +1 -1
  89. package/lib/renderers/Json/index.d.ts +2 -1
  90. package/lib/renderers/Json/index.d.ts.map +1 -1
  91. package/lib/renderers/Markdown/index.d.ts +2 -2
  92. package/lib/renderers/Markdown/index.d.ts.map +1 -1
  93. package/lib/renderers/Mobi/index.d.ts +2 -3
  94. package/lib/renderers/Mobi/index.d.ts.map +1 -1
  95. package/lib/renderers/Msg/index.d.ts +2 -1
  96. package/lib/renderers/Msg/index.d.ts.map +1 -1
  97. package/lib/renderers/Pdf/index.d.ts +4 -6
  98. package/lib/renderers/Pdf/index.d.ts.map +1 -1
  99. package/lib/renderers/Pptx/index.d.ts +2 -1
  100. package/lib/renderers/Pptx/index.d.ts.map +1 -1
  101. package/lib/renderers/Subtitle/index.d.ts +2 -1
  102. package/lib/renderers/Subtitle/index.d.ts.map +1 -1
  103. package/lib/renderers/Text/index.d.ts +2 -3
  104. package/lib/renderers/Text/index.d.ts.map +1 -1
  105. package/lib/renderers/Video/index.d.ts +2 -1
  106. package/lib/renderers/Video/index.d.ts.map +1 -1
  107. package/lib/renderers/Xlsx/index.d.ts +2 -1
  108. package/lib/renderers/Xlsx/index.d.ts.map +1 -1
  109. package/lib/renderers/Xml/index.d.ts +2 -1
  110. package/lib/renderers/Xml/index.d.ts.map +1 -1
  111. package/lib/renderers/Zip/index.d.ts +7 -2
  112. package/lib/renderers/Zip/index.d.ts.map +1 -1
  113. package/lib/renderers/base.types.d.ts +38 -0
  114. package/lib/renderers/base.types.d.ts.map +1 -0
  115. package/lib/renderers/registry.d.ts +36 -0
  116. package/lib/renderers/registry.d.ts.map +1 -0
  117. package/lib/renderers/toolbar.types.d.ts +3 -0
  118. package/lib/renderers/toolbar.types.d.ts.map +1 -1
  119. package/lib/toolbar/renderItems.d.ts +8 -0
  120. package/lib/toolbar/renderItems.d.ts.map +1 -0
  121. package/package.json +3 -3
  122. package/lib/chunks/RendererError-BH6fzLrN.mjs +0 -15
  123. package/lib/chunks/RendererError-BH6fzLrN.mjs.map +0 -1
  124. package/lib/chunks/index--lXiT1Y_.mjs +0 -2325
  125. package/lib/chunks/index--lXiT1Y_.mjs.map +0 -1
  126. package/lib/chunks/index-B05UpMZC.mjs +0 -270
  127. package/lib/chunks/index-B05UpMZC.mjs.map +0 -1
  128. package/lib/chunks/index-BG3Idu38.mjs.map +0 -1
  129. package/lib/chunks/index-B_7NPlPG.mjs +0 -105
  130. package/lib/chunks/index-B_7NPlPG.mjs.map +0 -1
  131. package/lib/chunks/index-BaU-yih3.mjs +0 -194
  132. package/lib/chunks/index-BaU-yih3.mjs.map +0 -1
  133. package/lib/chunks/index-Br8WHz8e.mjs.map +0 -1
  134. package/lib/chunks/index-CaobN7Im.mjs +0 -175
  135. package/lib/chunks/index-CaobN7Im.mjs.map +0 -1
  136. package/lib/chunks/index-Ch7DqyC4.mjs +0 -55
  137. package/lib/chunks/index-Ch7DqyC4.mjs.map +0 -1
  138. package/lib/chunks/index-DCGk-moA.mjs.map +0 -1
  139. package/lib/chunks/index-DEzF8C7L.mjs.map +0 -1
  140. package/lib/chunks/index-DKwN-YU-.mjs +0 -54
  141. package/lib/chunks/index-DKwN-YU-.mjs.map +0 -1
  142. package/lib/chunks/index-DMmb2rBE.mjs +0 -357
  143. package/lib/chunks/index-DMmb2rBE.mjs.map +0 -1
  144. package/lib/chunks/index-Dq-90KbM.mjs +0 -240
  145. package/lib/chunks/index-Dq-90KbM.mjs.map +0 -1
  146. package/lib/chunks/index-Go2oJfny.mjs +0 -114
  147. package/lib/chunks/index-Go2oJfny.mjs.map +0 -1
  148. package/lib/chunks/index-d8Bt4gIX.mjs.map +0 -1
  149. package/lib/chunks/index-jHf5E4be.mjs +0 -161
  150. package/lib/chunks/index-jHf5E4be.mjs.map +0 -1
  151. package/lib/chunks/useShikiHighlight-DoY3TBPT.mjs +0 -23
  152. package/lib/chunks/useShikiHighlight-DoY3TBPT.mjs.map +0 -1
  153. package/lib/renderers/Epub/toolbar.d.ts +0 -13
  154. package/lib/renderers/Epub/toolbar.d.ts.map +0 -1
  155. package/lib/renderers/Image/toolbar.d.ts +0 -15
  156. package/lib/renderers/Image/toolbar.d.ts.map +0 -1
  157. package/lib/renderers/Markdown/toolbar.d.ts +0 -9
  158. package/lib/renderers/Markdown/toolbar.d.ts.map +0 -1
  159. package/lib/renderers/Mobi/toolbar.d.ts +0 -13
  160. package/lib/renderers/Mobi/toolbar.d.ts.map +0 -1
  161. package/lib/renderers/Pdf/toolbar.d.ts +0 -11
  162. package/lib/renderers/Pdf/toolbar.d.ts.map +0 -1
  163. package/lib/renderers/Text/toolbar.d.ts +0 -12
  164. package/lib/renderers/Text/toolbar.d.ts.map +0 -1
  165. package/lib/renderers/Zip/toolbar.d.ts +0 -13
  166. package/lib/renderers/Zip/toolbar.d.ts.map +0 -1
@@ -1,194 +0,0 @@
1
- import { jsxs as x, jsx as o } from "react/jsx-runtime";
2
- import { forwardRef as lr, useRef as p, useState as d, useCallback as c, useImperativeHandle as cr, useEffect as pr } from "react";
3
- import { X as dr } from "lucide-react";
4
- import "foliate-js/view.js";
5
- import { u as ur, a as mr } from "./index--lXiT1Y_.mjs";
6
- import { R as hr } from "./RendererError-BH6fzLrN.mjs";
7
- const br = `
8
- @namespace epub "http://www.idpf.org/2007/ops";
9
- html { color-scheme: light; }
10
- body {
11
- background: #ffffff !important;
12
- color: #1a1a1a !important;
13
- font-family: "Noto Serif SC", "Source Han Serif SC", Georgia, "Times New Roman", serif !important;
14
- font-size: 16px !important;
15
- line-height: 2 !important;
16
- max-width: 100% !important;
17
- box-sizing: border-box !important;
18
- word-break: break-word !important;
19
- overflow-wrap: break-word !important;
20
- }
21
- p, li, blockquote, dd { line-height: 2; text-align: justify; }
22
- p { text-indent: 2em; margin: 0.8em 0; }
23
- h1 { text-align: center; margin: 1.5em 0 1em; }
24
- h2 { margin: 1.2em 0 0.8em; }
25
- h3 { margin: 1em 0 0.6em; }
26
- img { max-width: 100% !important; height: auto !important; }
27
- a { color: #2563eb; text-decoration: none; }
28
- pre { white-space: pre-wrap !important; }
29
- `, xr = 794, wr = lr(
30
- ({ url: w, onChapterChange: A, onFullWidthChange: j }, Q) => {
31
- const z = ur(), Y = mr(), C = p(null), i = p(null), g = p(A), y = p(j);
32
- g.current = A, y.current = j;
33
- const M = p(1), [Z, S] = d(!0), [v, F] = d(null), [H, I] = d([]), [k, u] = d(!1), [$, T] = d(""), [L, rr] = d(!1), P = p(!1);
34
- P.current = L;
35
- const N = c((e, t) => {
36
- var r;
37
- t > 0 && (M.current = t), (r = g.current) == null || r.call(g, Math.max(1, e + 1), M.current);
38
- }, []), W = c(() => {
39
- const e = i.current;
40
- e && e.prev().catch(() => {
41
- });
42
- }, []), X = c(() => {
43
- const e = i.current;
44
- e && e.next().catch(() => {
45
- });
46
- }, []), _ = c(() => u((e) => !e), []), D = c(() => {
47
- var m;
48
- const e = !P.current;
49
- rr(e), (m = y.current) == null || m.call(y, e);
50
- const t = i.current;
51
- if (!t) return;
52
- const r = t.renderer;
53
- r && r.setAttribute("max-inline-size", e ? "9999" : "720");
54
- }, []), er = c((e) => {
55
- var t;
56
- T(e), u(!1), (t = i.current) == null || t.goTo(e).catch(() => {
57
- });
58
- }, []);
59
- cr(Q, () => ({
60
- prevPage: W,
61
- nextPage: X,
62
- toggleFullWidth: D,
63
- toggleToc: _
64
- }), [W, X, D, _]), pr(() => {
65
- const e = C.current;
66
- if (!e || !w) return;
67
- S(!0), F(null), I([]), u(!1), T(""), e.replaceChildren();
68
- let t = !1, r = null;
69
- return (async () => {
70
- var a, s, h, q, G, V;
71
- try {
72
- if (t) return;
73
- r = document.createElement("foliate-view"), e.appendChild(r), i.current = r, r.addEventListener("relocate", (J) => {
74
- var K, O;
75
- const f = J.detail;
76
- if (!f) return;
77
- const b = f.location;
78
- if (b && typeof b.current == "number" && typeof b.total == "number")
79
- N(b.current, b.total);
80
- else {
81
- const ar = ((O = (K = i.current) == null ? void 0 : K.book) == null ? void 0 : O.sections) ?? [], sr = f.index ?? 0, ir = f.fraction ?? 0, E = Math.max(ar.length, 1), fr = Math.round((sr + ir) / E * E);
82
- N(fr, E);
83
- }
84
- const R = f.tocItem;
85
- R != null && R.href && T(R.href);
86
- });
87
- const l = await Y(w);
88
- if (!l.ok) throw new Error(`请求失败: ${l.status}`);
89
- const or = await l.blob();
90
- let B = "book.mobi";
91
- try {
92
- const f = new URL(w, window.location.href).pathname.split("/").pop();
93
- f && (B = decodeURIComponent(f));
94
- } catch {
95
- }
96
- const nr = new File([or], B);
97
- if (await r.open(nr), t) {
98
- (s = (a = r.book) == null ? void 0 : a.destroy) == null || s.call(a);
99
- return;
100
- }
101
- const n = r.renderer;
102
- n && (n.setAttribute("animated", ""), n.setAttribute("max-inline-size", "720"), n.setAttribute("margin", "48"), n.setAttribute("gap", "5%"), (h = n.setStyles) == null || h.call(n, br), await ((q = n.next) == null ? void 0 : q.call(n))), I(((G = r.book) == null ? void 0 : G.toc) ?? []), S(!1), N(0, ((V = r.book) == null ? void 0 : V.sections.length) ?? 1);
103
- } catch (l) {
104
- console.warn("[MobiRenderer] Failed to load ebook:", l instanceof Error ? l.message : String(l)), t || (F(z("mobi.load_failed")), S(!1));
105
- }
106
- })(), () => {
107
- var a, s, h;
108
- t = !0;
109
- try {
110
- (h = (s = (a = i.current) == null ? void 0 : a.book) == null ? void 0 : s.destroy) == null || h.call(s);
111
- } catch {
112
- }
113
- i.current = null, e.replaceChildren();
114
- };
115
- }, [w, N]);
116
- const tr = c(
117
- (e) => !!e && e === $,
118
- [$]
119
- ), U = (e, t = 0) => /* @__PURE__ */ o("ul", { style: { listStyle: "none", padding: 0, margin: t > 0 ? "0 0 0 16px" : 0 }, children: e.map((r, m) => {
120
- var a, s;
121
- return /* @__PURE__ */ x("li", { children: [
122
- r.href ? /* @__PURE__ */ o(
123
- "button",
124
- {
125
- onClick: () => er(r.href),
126
- className: `rfp-w-full rfp-text-left rfp-py-2 rfp-px-3 rfp-text-sm rfp-rounded rfp-transition-all rfp-truncate ${tr(r.href) ? "rfp-text-fg-primary rfp-bg-surface-3 rfp-font-medium" : "rfp-text-fg-secondary hover:rfp-text-fg-primary hover:rfp-bg-surface-2"}`,
127
- title: r.label,
128
- children: (a = r.label) == null ? void 0 : a.trim()
129
- }
130
- ) : /* @__PURE__ */ o("div", { className: "rfp-w-full rfp-py-2 rfp-px-3 rfp-text-sm rfp-text-fg-tertiary rfp-truncate", children: (s = r.label) == null ? void 0 : s.trim() }),
131
- r.subitems && r.subitems.length > 0 && U(r.subitems, t + 1)
132
- ] }, `${r.href ?? r.label}-${m}`);
133
- }) });
134
- return /* @__PURE__ */ x("div", { className: "rfp-relative rfp-w-full rfp-h-full rfp-flex rfp-justify-center rfp-bg-surface-1 rfp-overflow-hidden", children: [
135
- v && /* @__PURE__ */ o(hr, { message: v }),
136
- Z && !v && /* @__PURE__ */ o("div", { className: "rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-z-10", children: /* @__PURE__ */ o("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" }) }),
137
- H.length > 0 && /* @__PURE__ */ x(
138
- "div",
139
- {
140
- className: "rfp-absolute rfp-inset-0 rfp-z-20 rfp-flex rfp-transition-opacity rfp-duration-300",
141
- style: { opacity: k ? 1 : 0, pointerEvents: k ? "auto" : "none" },
142
- children: [
143
- /* @__PURE__ */ x(
144
- "div",
145
- {
146
- className: "rfp-w-72 rfp-max-w-[80%] rfp-h-full rfp-bg-surface-overlay rfp-backdrop-blur-xl rfp-border-r rfp-border-line-weak rfp-flex rfp-flex-col rfp-shadow-2xl rfp-transition-transform rfp-duration-300",
147
- style: { transform: k ? "translateX(0)" : "translateX(-100%)" },
148
- children: [
149
- /* @__PURE__ */ x("div", { className: "rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-3 rfp-border-b rfp-border-line-weak rfp-flex-shrink-0", children: [
150
- /* @__PURE__ */ o("span", { className: "rfp-text-fg-primary rfp-font-medium rfp-text-sm", children: z("toolbar.toc") }),
151
- /* @__PURE__ */ o(
152
- "button",
153
- {
154
- onClick: () => u(!1),
155
- className: "rfp-text-fg-tertiary hover:rfp-text-fg-primary rfp-transition-colors",
156
- children: /* @__PURE__ */ o(dr, { className: "rfp-w-4 rfp-h-4" })
157
- }
158
- )
159
- ] }),
160
- /* @__PURE__ */ o("div", { className: "rfp-flex-1 rfp-overflow-y-auto rfp-py-4 rfp-px-1", children: U(H) })
161
- ]
162
- }
163
- ),
164
- /* @__PURE__ */ o(
165
- "div",
166
- {
167
- className: "rfp-flex-1 rfp-transition-opacity rfp-duration-300",
168
- style: { background: k ? "rgba(0,0,0,0.3)" : "transparent" },
169
- onClick: () => u(!1)
170
- }
171
- )
172
- ]
173
- }
174
- ),
175
- !v && /* @__PURE__ */ o(
176
- "div",
177
- {
178
- ref: C,
179
- className: "rfp-h-full rfp-bg-surface-toolbar rfp-shadow-lg",
180
- style: {
181
- width: L ? "100%" : `${xr}px`,
182
- maxWidth: "100%",
183
- transition: "width 0.3s ease"
184
- }
185
- }
186
- )
187
- ] });
188
- }
189
- );
190
- wr.displayName = "MobiRenderer";
191
- export {
192
- wr as MobiRenderer
193
- };
194
- //# sourceMappingURL=index-BaU-yih3.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-BaU-yih3.mjs","sources":["../../src/renderers/Mobi/index.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n useCallback,\n useImperativeHandle,\n forwardRef,\n} from 'react';\nimport { X } from 'lucide-react';\nimport 'foliate-js/view.js';\nimport type { FoliateView, TocItem } from 'foliate-js/view.js';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\nconst READER_CSS = `\n @namespace epub \"http://www.idpf.org/2007/ops\";\n html { color-scheme: light; }\n body {\n background: #ffffff !important;\n color: #1a1a1a !important;\n font-family: \"Noto Serif SC\", \"Source Han Serif SC\", Georgia, \"Times New Roman\", serif !important;\n font-size: 16px !important;\n line-height: 2 !important;\n max-width: 100% !important;\n box-sizing: border-box !important;\n word-break: break-word !important;\n overflow-wrap: break-word !important;\n }\n p, li, blockquote, dd { line-height: 2; text-align: justify; }\n p { text-indent: 2em; margin: 0.8em 0; }\n h1 { text-align: center; margin: 1.5em 0 1em; }\n h2 { margin: 1.2em 0 0.8em; }\n h3 { margin: 1em 0 0.6em; }\n img { max-width: 100% !important; height: auto !important; }\n a { color: #2563eb; text-decoration: none; }\n pre { white-space: pre-wrap !important; }\n`;\n\nconst A4_WIDTH = 794;\n\nexport interface MobiRendererHandle {\n prevPage: () => void;\n nextPage: () => void;\n toggleFullWidth: () => void;\n toggleToc: () => void;\n}\n\ninterface MobiRendererProps {\n url: string;\n onChapterChange?: (current: number, total: number) => void;\n onFullWidthChange?: (isFullWidth: boolean) => void;\n}\n\nexport const MobiRenderer = forwardRef<MobiRendererHandle, MobiRendererProps>(\n ({ url, onChapterChange, onFullWidthChange }, ref) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const hostRef = useRef<HTMLDivElement>(null);\n const viewRef = useRef<FoliateView | null>(null);\n const onChapterChangeRef = useRef(onChapterChange);\n const onFullWidthChangeRef = useRef(onFullWidthChange);\n onChapterChangeRef.current = onChapterChange;\n onFullWidthChangeRef.current = onFullWidthChange;\n\n // 模拟 epub.js 的 locations 显示\n const totalLocationsRef = useRef(1);\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [toc, setToc] = useState<TocItem[]>([]);\n const [showToc, setShowToc] = useState(false);\n const [activeTocHref, setActiveTocHref] = useState<string>('');\n const [isFullWidth, setIsFullWidth] = useState(false);\n const isFullWidthRef = useRef(false);\n isFullWidthRef.current = isFullWidth;\n\n const reportProgress = useCallback((current: number, total: number) => {\n if (total > 0) totalLocationsRef.current = total;\n onChapterChangeRef.current?.(Math.max(1, current + 1), totalLocationsRef.current);\n }, []);\n\n const handlePrev = useCallback(() => {\n const view = viewRef.current;\n if (!view) return;\n view.prev().catch(() => {});\n }, []);\n\n const handleNext = useCallback(() => {\n const view = viewRef.current;\n if (!view) return;\n view.next().catch(() => {});\n }, []);\n\n const toggleToc = useCallback(() => setShowToc((prev) => !prev), []);\n\n const toggleFullWidth = useCallback(() => {\n const newVal = !isFullWidthRef.current;\n setIsFullWidth(newVal);\n onFullWidthChangeRef.current?.(newVal);\n // 宽度改变后 paginator 需要重新分页\n const view = viewRef.current;\n if (!view) return;\n const renderer = (view as unknown as { renderer?: HTMLElement }).renderer;\n if (renderer) {\n renderer.setAttribute('max-inline-size', newVal ? '9999' : '720');\n }\n }, []);\n\n const handleTocClick = useCallback((href: string) => {\n setActiveTocHref(href);\n setShowToc(false);\n viewRef.current?.goTo(href).catch(() => {});\n }, []);\n\n useImperativeHandle(ref, () => ({\n prevPage: handlePrev,\n nextPage: handleNext,\n toggleFullWidth,\n toggleToc,\n }), [handlePrev, handleNext, toggleFullWidth, toggleToc]);\n\n useEffect(() => {\n const host = hostRef.current;\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!host || !url) return;\n\n setLoading(true);\n setError(null);\n setToc([]);\n setShowToc(false);\n setActiveTocHref('');\n host.replaceChildren();\n\n let cancelled = false;\n let view: FoliateView | null = null;\n\n const load = async () => {\n try {\n if (cancelled) return;\n\n view = document.createElement('foliate-view') as FoliateView;\n host.appendChild(view);\n viewRef.current = view;\n\n // 先注册事件\n view.addEventListener('relocate', (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (!detail) return;\n // SectionProgress 返回的 location 对象: { current, next, total }\n const loc = detail.location as { current?: number; total?: number } | undefined;\n if (loc && typeof loc.current === 'number' && typeof loc.total === 'number') {\n reportProgress(loc.current, loc.total);\n } else {\n // fallback:用 section 级别估算\n const sections = viewRef.current?.book?.sections ?? [];\n const idx = detail.index ?? 0;\n const frac = detail.fraction ?? 0;\n const total = Math.max(sections.length, 1);\n const current = Math.round((idx + frac) / total * total);\n reportProgress(current, total);\n }\n const tocItem = detail.tocItem as { href?: string } | undefined;\n if (tocItem?.href) {\n setActiveTocHref(tocItem.href);\n }\n });\n\n const res = await fetcher(url);\n if (!res.ok) throw new Error(`请求失败: ${res.status}`);\n const blob = await res.blob();\n let name = 'book.mobi';\n try {\n const u = new URL(url, window.location.href);\n const base = u.pathname.split('/').pop();\n if (base) name = decodeURIComponent(base);\n } catch { /* blob: URL */ }\n const file = new File([blob], name);\n\n await view.open(file);\n if (cancelled) { view.book?.destroy?.(); return; }\n\n // 配置 paginator:paginated 模式(默认),带动画\n const renderer = (view as unknown as { renderer: HTMLElement & {\n setStyles?: (css: string) => void;\n next?: () => Promise<void>;\n } }).renderer;\n\n if (renderer) {\n // flow=\"paginated\" 是默认值,不需要显式设置\n renderer.setAttribute('animated', '');\n renderer.setAttribute('max-inline-size', '720');\n renderer.setAttribute('margin', '48');\n renderer.setAttribute('gap', '5%');\n renderer.setStyles?.(READER_CSS);\n // 必须调 next() 渲染首页\n await renderer.next?.();\n }\n\n setToc(view.book?.toc ?? []);\n setLoading(false);\n reportProgress(0, view.book?.sections.length ?? 1);\n } catch (err) {\n // MOBI/EPUB 加载错误通常是文件损坏或 DRM 保护,用 warn 级别记录\n console.warn('[MobiRenderer] Failed to load ebook:', err instanceof Error ? err.message : String(err));\n if (!cancelled) {\n setError(t('mobi.load_failed'));\n setLoading(false);\n }\n }\n };\n\n load();\n\n return () => {\n cancelled = true;\n try { viewRef.current?.book?.destroy?.(); } catch { /* ignore */ }\n viewRef.current = null;\n host.replaceChildren();\n };\n }, [url, reportProgress]);\n\n const isActive = useCallback(\n (href: string | undefined) => !!href && href === activeTocHref,\n [activeTocHref]\n );\n\n const renderTocItems = (items: TocItem[], depth = 0) => (\n <ul style={{ listStyle: 'none', padding: 0, margin: depth > 0 ? '0 0 0 16px' : 0 }}>\n {items.map((item, i) => (\n <li key={`${item.href ?? item.label}-${i}`}>\n {item.href ? (\n <button\n onClick={() => handleTocClick(item.href!)}\n className={`rfp-w-full rfp-text-left rfp-py-2 rfp-px-3 rfp-text-sm rfp-rounded rfp-transition-all rfp-truncate ${\n isActive(item.href)\n ? 'rfp-text-fg-primary rfp-bg-surface-3 rfp-font-medium'\n : 'rfp-text-fg-secondary hover:rfp-text-fg-primary hover:rfp-bg-surface-2'\n }`}\n title={item.label}\n >\n {item.label?.trim()}\n </button>\n ) : (\n <div className=\"rfp-w-full rfp-py-2 rfp-px-3 rfp-text-sm rfp-text-fg-tertiary rfp-truncate\">\n {item.label?.trim()}\n </div>\n )}\n {item.subitems && item.subitems.length > 0 && renderTocItems(item.subitems, depth + 1)}\n </li>\n ))}\n </ul>\n );\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-flex rfp-justify-center rfp-bg-surface-1 rfp-overflow-hidden\">\n {error && <RendererError message={error} />}\n\n {loading && !error && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-z-10\">\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 {toc.length > 0 && (\n <div\n className=\"rfp-absolute rfp-inset-0 rfp-z-20 rfp-flex rfp-transition-opacity rfp-duration-300\"\n style={{ opacity: showToc ? 1 : 0, pointerEvents: showToc ? 'auto' : 'none' }}\n >\n <div\n className=\"rfp-w-72 rfp-max-w-[80%] rfp-h-full rfp-bg-surface-overlay rfp-backdrop-blur-xl rfp-border-r rfp-border-line-weak rfp-flex rfp-flex-col rfp-shadow-2xl rfp-transition-transform rfp-duration-300\"\n style={{ transform: showToc ? 'translateX(0)' : 'translateX(-100%)' }}\n >\n <div className=\"rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-3 rfp-border-b rfp-border-line-weak rfp-flex-shrink-0\">\n <span className=\"rfp-text-fg-primary rfp-font-medium rfp-text-sm\">{t('toolbar.toc')}</span>\n <button\n onClick={() => setShowToc(false)}\n className=\"rfp-text-fg-tertiary hover:rfp-text-fg-primary rfp-transition-colors\"\n >\n <X className=\"rfp-w-4 rfp-h-4\" />\n </button>\n </div>\n <div className=\"rfp-flex-1 rfp-overflow-y-auto rfp-py-4 rfp-px-1\">\n {renderTocItems(toc)}\n </div>\n </div>\n <div\n className=\"rfp-flex-1 rfp-transition-opacity rfp-duration-300\"\n style={{ background: showToc ? 'rgba(0,0,0,0.3)' : 'transparent' }}\n onClick={() => setShowToc(false)}\n />\n </div>\n )}\n\n {!error && (\n <div\n ref={hostRef}\n className=\"rfp-h-full rfp-bg-surface-toolbar rfp-shadow-lg\"\n style={{\n width: isFullWidth ? '100%' : `${A4_WIDTH}px`,\n maxWidth: '100%',\n transition: 'width 0.3s ease',\n }}\n />\n )}\n </div>\n );\n }\n);\n\nMobiRenderer.displayName = 'MobiRenderer';\n"],"names":["READER_CSS","A4_WIDTH","MobiRenderer","forwardRef","url","onChapterChange","onFullWidthChange","ref","t","useTranslator","fetcher","useFetcher","hostRef","useRef","viewRef","onChapterChangeRef","onFullWidthChangeRef","totalLocationsRef","loading","setLoading","useState","error","setError","toc","setToc","showToc","setShowToc","activeTocHref","setActiveTocHref","isFullWidth","setIsFullWidth","isFullWidthRef","reportProgress","useCallback","current","total","_a","handlePrev","view","handleNext","toggleToc","prev","toggleFullWidth","newVal","renderer","handleTocClick","href","useImperativeHandle","useEffect","host","cancelled","e","detail","loc","sections","_b","idx","frac","tocItem","res","blob","name","base","file","_c","_d","_e","_f","err","isActive","renderTocItems","items","depth","item","i","jsxs","jsx","RendererError","X"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-Br8WHz8e.mjs","sources":["../../src/renderers/Csv/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport Spreadsheet from 'x-data-spreadsheet';\nimport 'x-data-spreadsheet/dist/xspreadsheet.css';\nimport {\n parseCsv,\n guessCsvDelimiter,\n fetchTextUtf8,\n convertCsvToSpreadsheetData,\n} from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface CsvRendererProps {\n url: string;\n fileName: string;\n}\n\nexport const CsvRenderer: React.FC<CsvRendererProps> = ({ url, fileName }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const spreadsheetRef = useRef<Spreadsheet | null>(null);\n const sheetDataRef = useRef<Record<string, unknown>[] | null>(null);\n const resizeObserverRef = useRef<ResizeObserver | null>(null);\n const resizeTimeoutRef = useRef<number | null>(null);\n const lastDimensionsRef = useRef({ width: 0, height: 0 });\n\n const calculateDimensions = useCallback(() => {\n if (!containerRef.current) return { width: 800, height: 600 };\n const rawWidth = containerRef.current.clientWidth;\n const rawHeight = containerRef.current.clientHeight;\n const width = rawWidth > 100 ? rawWidth : 800;\n const height = rawHeight > 100 ? rawHeight : 600;\n return { width, height };\n }, []);\n\n const mountSpreadsheet = useCallback(() => {\n if (!containerRef.current || !sheetDataRef.current) return;\n\n containerRef.current.innerHTML = '';\n spreadsheetRef.current = null;\n\n const { width, height } = calculateDimensions();\n const isMobile = width < 640;\n\n const s = new Spreadsheet(containerRef.current, {\n mode: 'read',\n showToolbar: false,\n showContextmenu: false,\n showGrid: true,\n row: {\n len: 100,\n height: 25,\n },\n col: {\n len: 26,\n width: isMobile ? 80 : 100,\n indexWidth: isMobile ? 40 : 60,\n minWidth: isMobile ? 40 : 60,\n },\n view: {\n height: () => height,\n width: () => width,\n },\n });\n\n s.loadData(sheetDataRef.current as unknown as Record<string, unknown>);\n spreadsheetRef.current = s;\n }, [calculateDimensions]);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n let isInitialRender = true;\n\n const updateDimensions = () => {\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensionsRef.current = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\n const lastDimensions = lastDimensionsRef.current;\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n if (widthDiff < 10 && heightDiff < 10) return;\n\n lastDimensionsRef.current = newDimensions;\n\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n\n resizeTimeoutRef.current = window.setTimeout(() => {\n if (sheetDataRef.current) {\n mountSpreadsheet();\n }\n }, 500);\n };\n\n resizeObserverRef.current = new ResizeObserver(() => {\n updateDimensions();\n });\n\n resizeObserverRef.current.observe(containerRef.current);\n\n return () => {\n if (resizeObserverRef.current) {\n resizeObserverRef.current.disconnect();\n }\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n };\n }, [calculateDimensions, mountSpreadsheet]);\n\n useEffect(() => {\n let isMounted = true;\n const controller = new AbortController();\n\n const loadCsv = async () => {\n if (!containerRef.current) return;\n\n setLoading(true);\n setError(null);\n\n try {\n const text = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n const parsed = parseCsv(text, { delimiter: guessCsvDelimiter(fileName) });\n const sheetData = convertCsvToSpreadsheetData(parsed.header, parsed.rows, fileName);\n\n if (!isMounted) return;\n\n sheetDataRef.current = sheetData as unknown as Record<string, unknown>[];\n mountSpreadsheet();\n setLoading(false);\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n if (isMounted) {\n console.error('CSV 解析错误:', err);\n setError(t('csv.load_failed'));\n setLoading(false);\n }\n }\n };\n\n const timer = setTimeout(() => {\n requestAnimationFrame(() => {\n loadCsv();\n });\n }, 100);\n\n return () => {\n isMounted = false;\n controller.abort();\n clearTimeout(timer);\n sheetDataRef.current = null;\n if (containerRef.current) {\n containerRef.current.innerHTML = '';\n }\n spreadsheetRef.current = null;\n };\n }, [url, fileName, mountSpreadsheet]);\n\n return (\n <div className=\"rfp-relative rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full\">\n {loading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-toolbar rfp-backdrop-blur-sm rfp-z-10\">\n <div className=\"rfp-text-center\">\n <div className=\"rfp-w-10 rfp-h-10 md:rfp-w-12 md:rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n <p className=\"rfp-text-xs md:rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('csv.loading')}</p>\n </div>\n </div>\n )}\n\n {error && !loading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-toolbar rfp-backdrop-blur-sm rfp-z-10\">\n <RendererError message={error} />\n </div>\n )}\n\n {!error && (\n <div\n ref={containerRef}\n className=\"xlsx-spreadsheet-container rfp-w-full rfp-h-full\"\n style={{ opacity: loading ? 0 : 1 }}\n />\n )}\n </div>\n );\n};\n"],"names":["CsvRenderer","url","fileName","t","useTranslator","fetcher","useFetcher","loading","setLoading","useState","error","setError","containerRef","useRef","spreadsheetRef","sheetDataRef","resizeObserverRef","resizeTimeoutRef","lastDimensionsRef","calculateDimensions","useCallback","rawWidth","rawHeight","width","height","mountSpreadsheet","isMobile","Spreadsheet","useEffect","isInitialRender","updateDimensions","newDimensions","lastDimensions","widthDiff","heightDiff","isMounted","controller","loadCsv","text","fetchTextUtf8","parsed","parseCsv","guessCsvDelimiter","sheetData","convertCsvToSpreadsheetData","err","timer","jsxs","jsx","RendererError"],"mappings":"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"}
@@ -1,175 +0,0 @@
1
- import { jsx as r, jsxs as m } from "react/jsx-runtime";
2
- import { useState as l, useEffect as _, useCallback as E } from "react";
3
- import z from "react-markdown";
4
- import H from "remark-gfm";
5
- import M from "remark-math";
6
- import I from "rehype-katex";
7
- import R from "rehype-raw";
8
- import { Check as y, Copy as w } from "lucide-react";
9
- import { u, a as T, E as j } from "./index--lXiT1Y_.mjs";
10
- import { u as N } from "./useShikiHighlight-DoY3TBPT.mjs";
11
- import { R as B } from "./RendererError-BH6fzLrN.mjs";
12
- const k = (t) => {
13
- const [f, p] = l(!1), a = E(async () => {
14
- try {
15
- await navigator.clipboard.writeText(t);
16
- } catch {
17
- const n = document.createElement("textarea");
18
- n.value = t, document.body.appendChild(n), n.select(), document.execCommand("copy"), document.body.removeChild(n);
19
- }
20
- p(!0), setTimeout(() => p(!1), 2e3);
21
- }, [t]);
22
- return { copied: f, handleCopy: a };
23
- }, L = ({ text: t }) => {
24
- const f = u(), { copied: p, handleCopy: a } = k(t);
25
- return /* @__PURE__ */ r(
26
- "button",
27
- {
28
- onClick: a,
29
- className: "rfp-p-1 rfp-rounded rfp-text-fg-muted hover:rfp-text-fg-secondary rfp-transition-colors rfp-flex rfp-items-center rfp-gap-1",
30
- title: f(p ? "markdown.copied" : "markdown.copy_code"),
31
- children: p ? /* @__PURE__ */ r(y, { size: 13 }) : /* @__PURE__ */ r(w, { size: 13 })
32
- }
33
- );
34
- }, q = ({ text: t }) => {
35
- const f = u(), { copied: p, handleCopy: a } = k(t);
36
- return /* @__PURE__ */ r(
37
- "button",
38
- {
39
- onClick: a,
40
- className: "rfp-absolute rfp-top-2 rfp-right-2 rfp-p-1.5 rfp-rounded-md rfp-bg-surface-2 hover:rfp-bg-surface-3 rfp-text-fg-tertiary hover:rfp-text-fg-secondary rfp-transition-colors rfp-opacity-0 group-hover:rfp-opacity-100 rfp-border rfp-border-line",
41
- title: f(p ? "markdown.copied" : "markdown.copy_code"),
42
- children: p ? /* @__PURE__ */ r(y, { size: 14 }) : /* @__PURE__ */ r(w, { size: 14 })
43
- }
44
- );
45
- }, A = ({ code: t, lang: f }) => {
46
- const { html: p } = N(t, f);
47
- return /* @__PURE__ */ m("div", { className: "rfp-relative rfp-group rfp-my-4", children: [
48
- /* @__PURE__ */ m("div", { className: "rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-1.5 rfp-bg-surface-1 rfp-border rfp-border-line-weak rfp-rounded-t-md rfp-border-b-0", children: [
49
- /* @__PURE__ */ r("span", { className: "rfp-text-xs rfp-text-fg-secondary rfp-font-mono rfp-select-none", children: f }),
50
- /* @__PURE__ */ r(L, { text: t })
51
- ] }),
52
- p ? /* @__PURE__ */ r(
53
- "div",
54
- {
55
- className: "rfp-shiki-wrapper rfp-rounded-b-md rfp-border rfp-border-line-weak rfp-border-t-0 rfp-overflow-x-auto",
56
- dangerouslySetInnerHTML: { __html: p }
57
- }
58
- ) : /* @__PURE__ */ r(
59
- "pre",
60
- {
61
- className: "rfp-m-0 rfp-rounded-b-md rfp-border rfp-border-line-weak rfp-border-t-0 rfp-overflow-x-auto rfp-p-4 rfp-bg-code-bg",
62
- style: { fontSize: "13px", lineHeight: "1.5" },
63
- children: /* @__PURE__ */ r("code", { className: "rfp-font-mono rfp-text-code-fg rfp-text-sm", children: t })
64
- }
65
- )
66
- ] });
67
- }, X = ({ url: t, viewMode: f = "preview" }) => {
68
- const p = u(), a = T(), [n, v] = l(""), [C, b] = l(!0), [h, g] = l(null), { html: x } = N(
69
- f === "source" ? n : "",
70
- "markdown"
71
- );
72
- return _(() => {
73
- const e = new AbortController();
74
- return (async () => {
75
- try {
76
- b(!0), g(null);
77
- const o = await j(t, { fetcher: a, signal: e.signal });
78
- v(o);
79
- } catch (o) {
80
- if (o.name === "AbortError") return;
81
- g(p("markdown.load_failed")), console.error(o);
82
- } finally {
83
- b(!1);
84
- }
85
- })(), () => e.abort();
86
- }, [t, a, p]), C ? /* @__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" }) }) : h ? /* @__PURE__ */ r(B, { message: h }) : f === "source" ? /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg", children: x ? /* @__PURE__ */ r(
87
- "div",
88
- {
89
- className: "rfp-shiki-wrapper with-line-numbers",
90
- dangerouslySetInnerHTML: { __html: x }
91
- }
92
- ) : /* @__PURE__ */ r("pre", { className: "rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words", children: n }) }) : /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-p-6 md:rfp-p-10", children: /* @__PURE__ */ r("div", { className: "rfp-max-w-full md:rfp-max-w-4xl rfp-mx-auto", children: /* @__PURE__ */ r(
93
- z,
94
- {
95
- remarkPlugins: [H, M],
96
- rehypePlugins: [R, I],
97
- components: {
98
- code({ node: e, inline: s, className: o, children: i, ...S }) {
99
- const d = /language-(\w+)/.exec(o || ""), c = String(i).replace(/\n$/, "");
100
- return s ?? (!d && !c.includes(`
101
- `)) ? /* @__PURE__ */ r(
102
- "code",
103
- {
104
- className: "rfp-bg-surface-2 rfp-px-1.5 rfp-py-0.5 rfp-rounded rfp-text-sm rfp-font-mono rfp-text-fg-primary rfp-border rfp-border-line-weak",
105
- ...S,
106
- children: i
107
- }
108
- ) : d ? /* @__PURE__ */ r(A, { code: c, lang: d[1] }) : /* @__PURE__ */ m("div", { className: "rfp-relative rfp-group rfp-my-4", children: [
109
- /* @__PURE__ */ r(q, { text: c }),
110
- /* @__PURE__ */ r(
111
- "pre",
112
- {
113
- className: "rfp-m-0 rfp-rounded-md rfp-border rfp-border-line-weak rfp-overflow-x-auto rfp-p-4 rfp-bg-code-bg",
114
- style: { fontSize: "13px", lineHeight: "1.5" },
115
- children: /* @__PURE__ */ r("code", { className: "rfp-font-mono rfp-text-code-fg rfp-text-sm", children: i })
116
- }
117
- )
118
- ] });
119
- },
120
- h1: ({ children: e }) => /* @__PURE__ */ r("h1", { className: "rfp-text-3xl rfp-font-semibold rfp-mb-4 rfp-mt-6 rfp-text-fg-primary first:rfp-mt-0", children: e }),
121
- h2: ({ children: e }) => /* @__PURE__ */ r("h2", { className: "rfp-text-2xl rfp-font-semibold rfp-mb-3 rfp-mt-8 rfp-text-fg-primary", children: e }),
122
- h3: ({ children: e }) => /* @__PURE__ */ r("h3", { className: "rfp-text-xl rfp-font-semibold rfp-mb-2 rfp-mt-6 rfp-text-fg-primary", children: e }),
123
- h4: ({ children: e }) => /* @__PURE__ */ r("h4", { className: "rfp-text-lg rfp-font-semibold rfp-mb-2 rfp-mt-4 rfp-text-fg-primary", children: e }),
124
- p: ({ children: e }) => /* @__PURE__ */ r("p", { className: "rfp-text-fg-secondary rfp-mb-4 rfp-leading-7 rfp-text-base", children: e }),
125
- a: ({ href: e, children: s }) => /* @__PURE__ */ r(
126
- "a",
127
- {
128
- href: e,
129
- className: "rfp-text-indigo-400 hover:rfp-text-indigo-300 rfp-underline rfp-decoration-indigo-600 hover:rfp-decoration-indigo-400",
130
- target: "_blank",
131
- rel: "noopener noreferrer",
132
- children: s
133
- }
134
- ),
135
- ul: ({ children: e }) => /* @__PURE__ */ r("ul", { className: "rfp-list-disc rfp-pl-6 rfp-mb-4 rfp-text-fg-secondary rfp-space-y-1", children: e }),
136
- ol: ({ children: e }) => /* @__PURE__ */ r("ol", { className: "rfp-list-decimal rfp-pl-6 rfp-mb-4 rfp-text-fg-secondary rfp-space-y-1", children: e }),
137
- li: ({ children: e }) => /* @__PURE__ */ r("li", { className: "rfp-leading-7", children: e }),
138
- blockquote: ({ children: e }) => /* @__PURE__ */ r("blockquote", { className: "rfp-border-l-4 rfp-border-line-strong rfp-pl-4 rfp-text-fg-tertiary rfp-my-4 rfp-italic", children: e }),
139
- table: ({ children: e }) => /* @__PURE__ */ r("div", { className: "rfp-overflow-x-auto rfp-my-4 rfp-rounded-md rfp-border rfp-border-line", children: /* @__PURE__ */ r("table", { className: "rfp-min-w-full rfp-divide-y rfp-divide-divide", children: e }) }),
140
- thead: ({ children: e }) => /* @__PURE__ */ r("thead", { className: "rfp-bg-surface-1", children: e }),
141
- tbody: ({ children: e }) => /* @__PURE__ */ r("tbody", { className: "rfp-divide-y rfp-divide-divide rfp-bg-transparent", children: e }),
142
- tr: ({ children: e }) => /* @__PURE__ */ r("tr", { className: "hover:rfp-bg-surface-1 rfp-transition-colors", children: e }),
143
- th: ({ children: e }) => /* @__PURE__ */ r("th", { className: "rfp-px-4 rfp-py-3 rfp-text-left rfp-text-xs rfp-font-semibold rfp-text-fg-tertiary rfp-uppercase rfp-tracking-wider", children: e }),
144
- td: ({ children: e }) => /* @__PURE__ */ r("td", { className: "rfp-px-4 rfp-py-3 rfp-text-sm rfp-text-fg-secondary", children: e }),
145
- hr: () => /* @__PURE__ */ r("hr", { className: "rfp-border-line rfp-my-6" }),
146
- img: ({ src: e, alt: s }) => /* @__PURE__ */ r(
147
- "img",
148
- {
149
- src: e,
150
- alt: s,
151
- className: "rfp-rounded-md rfp-max-w-full rfp-h-auto rfp-my-4 rfp-mx-auto rfp-block rfp-shadow-sm"
152
- }
153
- ),
154
- input: ({ type: e, checked: s, ...o }) => e === "checkbox" ? /* @__PURE__ */ r(
155
- "input",
156
- {
157
- type: "checkbox",
158
- checked: s,
159
- readOnly: !0,
160
- className: "rfp-mr-2 rfp-rounded rfp-border-line",
161
- ...o
162
- }
163
- ) : /* @__PURE__ */ r("input", { type: e, ...o }),
164
- strong: ({ children: e }) => /* @__PURE__ */ r("strong", { className: "rfp-font-semibold rfp-text-fg-primary", children: e }),
165
- em: ({ children: e }) => /* @__PURE__ */ r("em", { className: "rfp-italic", children: e }),
166
- del: ({ children: e }) => /* @__PURE__ */ r("del", { className: "rfp-text-fg-muted rfp-line-through", children: e })
167
- },
168
- children: n
169
- }
170
- ) }) });
171
- };
172
- export {
173
- X as MarkdownRenderer
174
- };
175
- //# sourceMappingURL=index-CaobN7Im.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-CaobN7Im.mjs","sources":["../../src/renderers/Markdown/index.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport rehypeKatex from 'rehype-katex';\nimport rehypeRaw from 'rehype-raw';\nimport { Copy, Check } from 'lucide-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 'katex/dist/katex.min.css';\n\ninterface MarkdownRendererProps {\n url: string;\n viewMode?: 'preview' | 'source';\n}\n\nconst useCopy = (text: string) => {\n const [copied, setCopied] = useState(false);\n const handleCopy = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n const textarea = document.createElement('textarea');\n textarea.value = text;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n }\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }, [text]);\n return { copied, handleCopy };\n};\n\n/** 内联版复制按钮:放在代码块 header 行内,始终可见 */\nconst InlineCopyButton = ({ text }: { text: string }) => {\n const t = useTranslator();\n const { copied, handleCopy } = useCopy(text);\n return (\n <button\n onClick={handleCopy}\n className=\"rfp-p-1 rfp-rounded rfp-text-fg-muted hover:rfp-text-fg-secondary rfp-transition-colors rfp-flex rfp-items-center rfp-gap-1\"\n title={copied ? t('markdown.copied') : t('markdown.copy_code')}\n >\n {copied ? <Check size={13} /> : <Copy size={13} />}\n </button>\n );\n};\n\n/** 浮动版复制按钮:无 header 时绝对定位于代码块右上角(hover 显示) */\nconst FloatingCopyButton = ({ text }: { text: string }) => {\n const t = useTranslator();\n const { copied, handleCopy } = useCopy(text);\n return (\n <button\n onClick={handleCopy}\n className=\"rfp-absolute rfp-top-2 rfp-right-2 rfp-p-1.5 rfp-rounded-md rfp-bg-surface-2 hover:rfp-bg-surface-3 rfp-text-fg-tertiary hover:rfp-text-fg-secondary rfp-transition-colors rfp-opacity-0 group-hover:rfp-opacity-100 rfp-border rfp-border-line\"\n title={copied ? t('markdown.copied') : t('markdown.copy_code')}\n >\n {copied ? <Check size={14} /> : <Copy size={14} />}\n </button>\n );\n};\n\n/** 带语言标注的代码块:shiki 高亮 + header + 复制按钮 */\nconst ShikiCodeBlock = ({ code, lang }: { code: string; lang: string }) => {\n const { html } = useShikiHighlight(code, lang);\n return (\n <div className=\"rfp-relative rfp-group rfp-my-4\">\n <div className=\"rfp-flex rfp-items-center rfp-justify-between rfp-px-4 rfp-py-1.5 rfp-bg-surface-1 rfp-border rfp-border-line-weak rfp-rounded-t-md rfp-border-b-0\">\n <span className=\"rfp-text-xs rfp-text-fg-secondary rfp-font-mono rfp-select-none\">{lang}</span>\n <InlineCopyButton text={code} />\n </div>\n {html ? (\n <div\n className=\"rfp-shiki-wrapper rfp-rounded-b-md rfp-border rfp-border-line-weak rfp-border-t-0 rfp-overflow-x-auto\"\n dangerouslySetInnerHTML={{ __html: html }}\n />\n ) : (\n <pre\n className=\"rfp-m-0 rfp-rounded-b-md rfp-border rfp-border-line-weak rfp-border-t-0 rfp-overflow-x-auto rfp-p-4 rfp-bg-code-bg\"\n style={{ fontSize: '13px', lineHeight: '1.5' }}\n >\n <code className=\"rfp-font-mono rfp-text-code-fg rfp-text-sm\">{code}</code>\n </pre>\n )}\n </div>\n );\n};\n\nexport const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ url, viewMode = 'preview' }) => {\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 { html: sourceHtml } = useShikiHighlight(\n viewMode === 'source' ? content : '',\n 'markdown',\n );\n\n useEffect(() => {\n const controller = new AbortController();\n const loadMarkdown = async () => {\n try {\n setLoading(true);\n setError(null);\n const text = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(text);\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n setError(t('markdown.load_failed'));\n console.error(err);\n } finally {\n setLoading(false);\n }\n };\n\n loadMarkdown();\n return () => controller.abort();\n }, [url, fetcher, t]);\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 // 源码视图\n if (viewMode === 'source') {\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {sourceHtml ? (\n <div\n className=\"rfp-shiki-wrapper with-line-numbers\"\n dangerouslySetInnerHTML={{ __html: sourceHtml }}\n />\n ) : (\n <pre className=\"rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm rfp-whitespace-pre-wrap rfp-break-words\">\n {content}\n </pre>\n )}\n </div>\n );\n }\n\n // 预览视图\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-p-6 md:rfp-p-10\">\n <div className=\"rfp-max-w-full md:rfp-max-w-4xl rfp-mx-auto\">\n <ReactMarkdown\n remarkPlugins={[remarkGfm, remarkMath]}\n rehypePlugins={[rehypeRaw, rehypeKatex]}\n components={{\n code({ node, inline, className, children, ...props }: any) {\n const match = /language-(\\w+)/.exec(className || '');\n const codeString = String(children).replace(/\\n$/, '');\n // react-markdown v9 不再传 inline,需要兜底判断:\n // 无语言 className 且不含换行视为内联代码\n const isInline = inline ?? (!match && !codeString.includes('\\n'));\n\n // 行内代码 - 返回纯 <code>\n if (isInline) {\n return (\n <code\n className=\"rfp-bg-surface-2 rfp-px-1.5 rfp-py-0.5 rfp-rounded rfp-text-sm rfp-font-mono rfp-text-fg-primary rfp-border rfp-border-line-weak\"\n {...props}\n >\n {children}\n </code>\n );\n }\n\n // 代码块 - 有语言标注\n if (match) {\n return <ShikiCodeBlock code={codeString} lang={match[1]} />;\n }\n\n // 代码块 - 无语言标注\n return (\n <div className=\"rfp-relative rfp-group rfp-my-4\">\n <FloatingCopyButton text={codeString} />\n <pre\n className=\"rfp-m-0 rfp-rounded-md rfp-border rfp-border-line-weak rfp-overflow-x-auto rfp-p-4 rfp-bg-code-bg\"\n style={{ fontSize: '13px', lineHeight: '1.5' }}\n >\n <code className=\"rfp-font-mono rfp-text-code-fg rfp-text-sm\">{children}</code>\n </pre>\n </div>\n );\n },\n h1: ({ children }) => (\n <h1 className=\"rfp-text-3xl rfp-font-semibold rfp-mb-4 rfp-mt-6 rfp-text-fg-primary first:rfp-mt-0\">\n {children}\n </h1>\n ),\n h2: ({ children }) => (\n <h2 className=\"rfp-text-2xl rfp-font-semibold rfp-mb-3 rfp-mt-8 rfp-text-fg-primary\">\n {children}\n </h2>\n ),\n h3: ({ children }) => (\n <h3 className=\"rfp-text-xl rfp-font-semibold rfp-mb-2 rfp-mt-6 rfp-text-fg-primary\">{children}</h3>\n ),\n h4: ({ children }) => (\n <h4 className=\"rfp-text-lg rfp-font-semibold rfp-mb-2 rfp-mt-4 rfp-text-fg-primary\">{children}</h4>\n ),\n p: ({ children }) => (\n <p className=\"rfp-text-fg-secondary rfp-mb-4 rfp-leading-7 rfp-text-base\">{children}</p>\n ),\n a: ({ href, children }) => (\n <a\n href={href}\n className=\"rfp-text-indigo-400 hover:rfp-text-indigo-300 rfp-underline rfp-decoration-indigo-600 hover:rfp-decoration-indigo-400\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {children}\n </a>\n ),\n ul: ({ children }) => (\n <ul className=\"rfp-list-disc rfp-pl-6 rfp-mb-4 rfp-text-fg-secondary rfp-space-y-1\">{children}</ul>\n ),\n ol: ({ children }) => (\n <ol className=\"rfp-list-decimal rfp-pl-6 rfp-mb-4 rfp-text-fg-secondary rfp-space-y-1\">{children}</ol>\n ),\n li: ({ children }) => <li className=\"rfp-leading-7\">{children}</li>,\n blockquote: ({ children }) => (\n <blockquote className=\"rfp-border-l-4 rfp-border-line-strong rfp-pl-4 rfp-text-fg-tertiary rfp-my-4 rfp-italic\">\n {children}\n </blockquote>\n ),\n table: ({ children }) => (\n <div className=\"rfp-overflow-x-auto rfp-my-4 rfp-rounded-md rfp-border rfp-border-line\">\n <table className=\"rfp-min-w-full rfp-divide-y rfp-divide-divide\">{children}</table>\n </div>\n ),\n thead: ({ children }) => <thead className=\"rfp-bg-surface-1\">{children}</thead>,\n tbody: ({ children }) => (\n <tbody className=\"rfp-divide-y rfp-divide-divide rfp-bg-transparent\">{children}</tbody>\n ),\n tr: ({ children }) => (\n <tr className=\"hover:rfp-bg-surface-1 rfp-transition-colors\">{children}</tr>\n ),\n th: ({ children }) => (\n <th className=\"rfp-px-4 rfp-py-3 rfp-text-left rfp-text-xs rfp-font-semibold rfp-text-fg-tertiary rfp-uppercase rfp-tracking-wider\">\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td className=\"rfp-px-4 rfp-py-3 rfp-text-sm rfp-text-fg-secondary\">{children}</td>\n ),\n hr: () => <hr className=\"rfp-border-line rfp-my-6\" />,\n img: ({ src, alt }) => (\n <img\n src={src}\n alt={alt}\n className=\"rfp-rounded-md rfp-max-w-full rfp-h-auto rfp-my-4 rfp-mx-auto rfp-block rfp-shadow-sm\"\n />\n ),\n input: ({ type, checked, ...props }) => {\n if (type === 'checkbox') {\n return (\n <input\n type=\"checkbox\"\n checked={checked}\n readOnly\n className=\"rfp-mr-2 rfp-rounded rfp-border-line\"\n {...props}\n />\n );\n }\n return <input type={type} {...props} />;\n },\n strong: ({ children }) => (\n <strong className=\"rfp-font-semibold rfp-text-fg-primary\">{children}</strong>\n ),\n em: ({ children }) => <em className=\"rfp-italic\">{children}</em>,\n del: ({ children }) => (\n <del className=\"rfp-text-fg-muted rfp-line-through\">{children}</del>\n ),\n }}\n >\n {content}\n </ReactMarkdown>\n </div>\n </div>\n );\n};\n"],"names":["useCopy","text","copied","setCopied","useState","handleCopy","useCallback","textarea","InlineCopyButton","t","useTranslator","jsx","Check","Copy","FloatingCopyButton","ShikiCodeBlock","code","lang","html","useShikiHighlight","jsxs","MarkdownRenderer","url","viewMode","fetcher","useFetcher","content","setContent","loading","setLoading","error","setError","sourceHtml","useEffect","controller","fetchTextUtf8","err","RendererError","ReactMarkdown","remarkGfm","remarkMath","rehypeRaw","rehypeKatex","node","inline","className","children","props","match","codeString","href","src","alt","type","checked"],"mappings":"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"}
@@ -1,55 +0,0 @@
1
- import { jsx as r } from "react/jsx-runtime";
2
- import { useState as s, useEffect as b } from "react";
3
- import { u as x, a as y, y as v, E } from "./index--lXiT1Y_.mjs";
4
- import { u as N } from "./useShikiHighlight-DoY3TBPT.mjs";
5
- import { R as T } from "./RendererError-BH6fzLrN.mjs";
6
- const C = ({
7
- url: l,
8
- fileName: f,
9
- wordWrap: n = !0,
10
- htmlPreview: m = !1
11
- }) => {
12
- const d = x(), h = y(), [o, g] = s(""), [w, a] = s(!0), [p, i] = s(null), e = v(f), { html: c } = N(
13
- e !== "text" ? o : "",
14
- e
15
- );
16
- return b(() => {
17
- const u = new AbortController();
18
- return (async () => {
19
- try {
20
- a(!0), i(null);
21
- const t = await E(l, { fetcher: h, signal: u.signal });
22
- g(t);
23
- } catch (t) {
24
- if (t.name === "AbortError") return;
25
- i(d("text.load_failed")), console.error(t);
26
- } finally {
27
- a(!1);
28
- }
29
- })(), () => u.abort();
30
- }, [l]), w ? /* @__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" }) }) : p ? /* @__PURE__ */ r(T, { message: p }) : m && e === "html" ? /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-bg-surface-toolbar", children: /* @__PURE__ */ r(
31
- "iframe",
32
- {
33
- srcDoc: o,
34
- sandbox: "allow-same-origin",
35
- className: "rfp-w-full rfp-h-full rfp-border-0",
36
- title: f
37
- }
38
- ) }) : /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg", children: e === "text" || !c ? /* @__PURE__ */ r(
39
- "pre",
40
- {
41
- className: `rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm ${n ? "rfp-whitespace-pre-wrap rfp-break-words" : "rfp-whitespace-pre"}`,
42
- children: o
43
- }
44
- ) : /* @__PURE__ */ r(
45
- "div",
46
- {
47
- className: `rfp-shiki-wrapper with-line-numbers ${n ? "" : "no-wrap"}`,
48
- dangerouslySetInnerHTML: { __html: c }
49
- }
50
- ) });
51
- };
52
- export {
53
- C as TextRenderer
54
- };
55
- //# sourceMappingURL=index-Ch7DqyC4.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-Ch7DqyC4.mjs","sources":["../../src/renderers/Text/index.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { fetchTextUtf8, getLanguageFromFileName } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { useShikiHighlight } from '../../hooks/useShikiHighlight';\nimport { RendererError } from '../RendererError';\n\ninterface TextRendererProps {\n url: string;\n fileName: string;\n wordWrap?: boolean;\n htmlPreview?: boolean;\n}\n\nexport const TextRenderer: React.FC<TextRendererProps> = ({\n url,\n fileName,\n wordWrap = true,\n htmlPreview = false,\n}) => {\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 language = getLanguageFromFileName(fileName);\n const { html: highlighted } = useShikiHighlight(\n language !== 'text' ? content : '',\n language,\n );\n\n useEffect(() => {\n const controller = new AbortController();\n const loadText = async () => {\n try {\n setLoading(true);\n setError(null);\n const text = await fetchTextUtf8(url, { fetcher, signal: controller.signal });\n setContent(text);\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n setError(t('text.load_failed'));\n console.error(err);\n } finally {\n setLoading(false);\n }\n };\n\n loadText();\n return () => controller.abort();\n }, [url]);\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 // HTML 预览模式\n if (htmlPreview && (language === 'html')) {\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-bg-surface-toolbar\">\n <iframe\n srcDoc={content}\n sandbox=\"allow-same-origin\"\n className=\"rfp-w-full rfp-h-full rfp-border-0\"\n title={fileName}\n />\n </div>\n );\n }\n\n // 源码模式\n return (\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg\">\n {language === 'text' || !highlighted ? (\n <pre\n className={`rfp-p-6 rfp-text-fg-primary rfp-font-mono rfp-text-sm ${\n wordWrap ? 'rfp-whitespace-pre-wrap rfp-break-words' : 'rfp-whitespace-pre'\n }`}\n >\n {content}\n </pre>\n ) : (\n <div\n className={`rfp-shiki-wrapper with-line-numbers ${wordWrap ? '' : 'no-wrap'}`}\n dangerouslySetInnerHTML={{ __html: highlighted }}\n />\n )}\n </div>\n );\n};\n"],"names":["TextRenderer","url","fileName","wordWrap","htmlPreview","t","useTranslator","fetcher","useFetcher","content","setContent","useState","loading","setLoading","error","setError","language","getLanguageFromFileName","highlighted","useShikiHighlight","useEffect","controller","text","fetchTextUtf8","err","jsx","RendererError"],"mappings":";;;;;AAcO,MAAMA,IAA4C,CAAC;AAAA,EACxD,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAChB,MAAM;AACJ,QAAMC,IAAIC,EAAA,GACJC,IAAUC,EAAA,GACV,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChDK,IAAWC,EAAwBf,CAAQ,GAC3C,EAAE,MAAMgB,EAAA,IAAgBC;AAAA,IAC5BH,MAAa,SAASP,IAAU;AAAA,IAChCO;AAAA,EAAA;AAwBF,SArBAI,EAAU,MAAM;AACd,UAAMC,IAAa,IAAI,gBAAA;AAgBvB,YAfiB,YAAY;AAC3B,UAAI;AACF,QAAAR,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMO,IAAO,MAAMC,EAActB,GAAK,EAAE,SAAAM,GAAS,QAAQc,EAAW,QAAQ;AAC5E,QAAAX,EAAWY,CAAI;AAAA,MACjB,SAASE,GAAU;AACjB,YAAIA,EAAI,SAAS,aAAc;AAC/B,QAAAT,EAASV,EAAE,kBAAkB,CAAC,GAC9B,QAAQ,MAAMmB,CAAG;AAAA,MACnB,UAAA;AACE,QAAAX,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GAEA,GACO,MAAMQ,EAAW,MAAA;AAAA,EAC1B,GAAG,CAACpB,CAAG,CAAC,GAEJW,IAEA,gBAAAa,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAX,IACK,gBAAAW,EAACC,GAAA,EAAc,SAASZ,EAAA,CAAO,IAIpCV,KAAgBY,MAAa,SAE7B,gBAAAS,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA,gBAAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,QAAQhB;AAAA,MACR,SAAQ;AAAA,MACR,WAAU;AAAA,MACV,OAAOP;AAAA,IAAA;AAAA,EAAA,GAEX,sBAMD,OAAA,EAAI,WAAU,0DACZ,UAAAc,MAAa,UAAU,CAACE,IACvB,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,yDACTtB,IAAW,4CAA4C,oBACzD;AAAA,MAEC,UAAAM;AAAA,IAAA;AAAA,EAAA,IAGH,gBAAAgB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,uCAAuCtB,IAAW,KAAK,SAAS;AAAA,MAC3E,yBAAyB,EAAE,QAAQe,EAAA;AAAA,IAAY;AAAA,EAAA,GAGrD;AAEJ;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-DCGk-moA.mjs","sources":["../../src/renderers/Subtitle/index.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport {\n parseSubtitle,\n formatSubtitleTime,\n fetchTextUtf8,\n type SubtitleParseResult,\n type SubtitleFormat,\n} from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface SubtitleRendererProps {\n url: string;\n fileName: string;\n}\n\nconst FORMAT_BY_EXT: Record<string, SubtitleFormat> = {\n srt: 'srt',\n vtt: 'vtt',\n lrc: 'lrc',\n elrc: 'elrc',\n ass: 'ass',\n ssa: 'ssa',\n ttml: 'ttml',\n dfxp: 'ttml',\n};\n\nconst getFormat = (fileName: string): SubtitleFormat | undefined => {\n const ext = fileName.split('.').pop()?.toLowerCase() || '';\n return FORMAT_BY_EXT[ext];\n};\n\nexport const SubtitleRenderer: React.FC<SubtitleRendererProps> = ({ url, fileName }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [text, setText] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const load = async () => {\n try {\n setLoading(true);\n setError(null);\n setText(await fetchTextUtf8(url, { fetcher, signal: controller.signal }));\n } catch (err: any) {\n if (err.name === 'AbortError') return;\n console.warn('[SubtitleRenderer] Failed to load subtitle:', err instanceof Error ? err.message : String(err));\n setError(t('subtitle.load_failed'));\n } finally {\n setLoading(false);\n }\n };\n load();\n return () => controller.abort();\n }, [url]);\n\n const parsed: SubtitleParseResult | null = useMemo(() => {\n if (!text) return null;\n try {\n return parseSubtitle(text, getFormat(fileName));\n } catch (err) {\n // 字幕解析失败通常是格式不支持或文件损坏,用 warn 级别记录\n console.warn('[SubtitleRenderer] Failed to parse subtitle:', err instanceof Error ? err.message : String(err));\n return null;\n }\n }, [text, fileName]);\n\n if (loading) {\n return (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\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 || !parsed) {\n return <RendererError message={error || t('subtitle.parse_failed')} />;\n }\n\n const isLyric = parsed.format === 'lrc' || parsed.format === 'elrc';\n const meta = parsed.metadata ?? {};\n const dotHover = isLyric ? 'group-hover:rfp-bg-violet-400' : 'group-hover:rfp-bg-sky-400';\n\n return (\n <div className=\"rfp-relative rfp-w-full rfp-h-full rfp-bg-[#0f0f12]\">\n {/* 内容滚动区 */}\n <div className=\"rfp-w-full rfp-h-full rfp-overflow-auto rfp-px-6 md:rfp-px-10 rfp-pt-6 rfp-pb-16 md:rfp-pb-20\">\n <div className=\"rfp-relative rfp-max-w-5xl rfp-mx-auto\">\n {/* vertical line */}\n <div className=\"rfp-absolute rfp-left-[5px] md:rfp-left-[7px] rfp-top-2 rfp-bottom-2 rfp-w-px rfp-bg-surface-1\" />\n\n <ol className=\"rfp-space-y-5 md:rfp-space-y-6\">\n {parsed.cues.map((cue, i) => (\n <li key={`cue-${i}`} className=\"rfp-relative rfp-pl-6 md:rfp-pl-8 rfp-group\">\n {/* dot */}\n <div\n className={`rfp-absolute rfp-left-0 rfp-top-2 rfp-w-3 rfp-h-3 rfp-rounded-full rfp-bg-surface-3 rfp-border-2 rfp-border-[#0f0f12] rfp-transition-colors ${dotHover}`}\n />\n\n <div className=\"rfp-flex rfp-flex-wrap rfp-items-baseline rfp-gap-x-3 rfp-gap-y-1 rfp-mb-1.5\">\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.start)}\n </span>\n <span className=\"rfp-text-[11px] rfp-text-fg-disabled\">→</span>\n <span className=\"rfp-text-[11px] rfp-font-mono rfp-text-fg-muted rfp-tabular-nums\">\n {formatSubtitleTime(cue.end)}\n </span>\n <span className=\"rfp-text-[10px] rfp-font-mono rfp-text-fg-disabled rfp-tabular-nums\">\n #{cue.id ?? i + 1}\n </span>\n {cue.style && (\n <span className=\"rfp-text-[9px] rfp-uppercase rfp-tracking-widest rfp-text-fg-tertiary rfp-px-1.5 rfp-py-0.5 rfp-rounded rfp-bg-surface-1 rfp-border rfp-border-line-weak\">\n {cue.style}\n </span>\n )}\n </div>\n\n {cue.words && cue.words.length > 0 ? (\n <div className=\"rfp-flex rfp-flex-wrap rfp-gap-x-1.5 rfp-gap-y-1 rfp-text-base md:rfp-text-lg rfp-text-fg-primary rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors\">\n {cue.words.map((word, wi) => (\n <span\n key={`w-${wi}`}\n className=\"rfp-inline-flex rfp-flex-col rfp-items-start\"\n title={formatSubtitleTime(word.start)}\n >\n <span className=\"rfp-text-[9px] rfp-text-fg-disabled rfp-font-mono rfp-leading-none rfp-tabular-nums\">\n {formatSubtitleTime(word.start).slice(3, 8)}\n </span>\n <span className=\"rfp-leading-snug\">{word.text}</span>\n </span>\n ))}\n </div>\n ) : (\n <p\n className={`rfp-whitespace-pre-wrap rfp-break-words rfp-leading-relaxed group-hover:rfp-text-fg-primary rfp-transition-colors rfp-text-fg-primary ${\n isLyric ? 'rfp-text-base md:rfp-text-xl rfp-font-medium' : 'rfp-text-sm md:rfp-text-base'\n }`}\n >\n {cue.text}\n </p>\n )}\n </li>\n ))}\n </ol>\n </div>\n </div>\n\n {/* 底部状态栏 */}\n <div className=\"rfp-pointer-events-none rfp-absolute rfp-bottom-3 rfp-right-3 md:rfp-bottom-4 md:rfp-right-4 rfp-flex rfp-items-center rfp-gap-2 rfp-px-2.5 rfp-py-1 rfp-rounded-full rfp-bg-surface-nav rfp-backdrop-blur rfp-border rfp-border-line-weak rfp-text-[10px] rfp-text-fg-tertiary rfp-font-mono rfp-tabular-nums\">\n <span>{parsed.cues.length} {isLyric ? t('subtitle.lines') : t('subtitle.cues')}</span>\n {meta.length && (\n <>\n <span className=\"rfp-text-fg-disabled\">·</span>\n <span>{meta.length}</span>\n </>\n )}\n </div>\n </div>\n );\n};\n"],"names":["FORMAT_BY_EXT","getFormat","fileName","ext","_a","SubtitleRenderer","url","t","useTranslator","fetcher","useFetcher","text","setText","useState","loading","setLoading","error","setError","useEffect","controller","fetchTextUtf8","err","parsed","useMemo","parseSubtitle","jsx","RendererError","isLyric","meta","dotHover","jsxs","cue","i","formatSubtitleTime","word","wi","Fragment"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-DEzF8C7L.mjs","sources":["../../src/renderers/Xlsx/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport ExcelJS from 'exceljs';\nimport Spreadsheet from 'x-data-spreadsheet';\nimport 'x-data-spreadsheet/dist/xspreadsheet.css';\nimport { convertWorkbookToSpreadsheetData } from '../../utils/excelDataConverter';\nimport { useTranslator } from '../../i18n/LocaleContext';\nimport { useFetcher } from '../../RequestContext';\nimport { RendererError } from '../RendererError';\n\ninterface XlsxRendererProps {\n url: string;\n}\n\nexport const XlsxRenderer: React.FC<XlsxRendererProps> = ({ url }) => {\n const t = useTranslator();\n const fetcher = useFetcher();\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const spreadsheetRef = useRef<Spreadsheet | null>(null);\n const sheetDataRef = useRef<Record<string, unknown>[] | null>(null);\n const resizeObserverRef = useRef<ResizeObserver | null>(null);\n const resizeTimeoutRef = useRef<number | null>(null);\n const lastDimensionsRef = useRef({ width: 0, height: 0 });\n\n const calculateDimensions = useCallback(() => {\n if (!containerRef.current) return { width: 800, height: 600 };\n const rawWidth = containerRef.current.clientWidth;\n const rawHeight = containerRef.current.clientHeight;\n const width = rawWidth > 100 ? rawWidth : 800;\n const height = rawHeight > 100 ? rawHeight : 600;\n return { width, height };\n }, []);\n\n const mountSpreadsheet = useCallback(() => {\n if (!containerRef.current || !sheetDataRef.current) return;\n\n // 清空容器\n containerRef.current.innerHTML = '';\n spreadsheetRef.current = null;\n\n const { width, height } = calculateDimensions();\n const isMobile = width < 640;\n\n const s = new Spreadsheet(containerRef.current, {\n mode: 'read',\n showToolbar: false,\n showContextmenu: false,\n showGrid: true,\n row: {\n len: 100,\n height: 25,\n },\n col: {\n len: 26,\n width: isMobile ? 80 : 100,\n indexWidth: isMobile ? 40 : 60,\n minWidth: isMobile ? 40 : 60,\n },\n view: {\n height: () => height,\n width: () => width,\n },\n });\n\n s.loadData(sheetDataRef.current as unknown as Record<string, unknown>);\n spreadsheetRef.current = s;\n }, [calculateDimensions]);\n\n // 监听容器尺寸变化\n useEffect(() => {\n if (!containerRef.current) return;\n\n let isInitialRender = true;\n\n const updateDimensions = () => {\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensionsRef.current = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\n const lastDimensions = lastDimensionsRef.current;\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n if (widthDiff < 10 && heightDiff < 10) return;\n\n lastDimensionsRef.current = newDimensions;\n\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n\n resizeTimeoutRef.current = window.setTimeout(() => {\n if (sheetDataRef.current) {\n mountSpreadsheet();\n }\n }, 500);\n };\n\n resizeObserverRef.current = new ResizeObserver(() => {\n updateDimensions();\n });\n\n resizeObserverRef.current.observe(containerRef.current);\n\n return () => {\n if (resizeObserverRef.current) {\n resizeObserverRef.current.disconnect();\n }\n if (resizeTimeoutRef.current) {\n clearTimeout(resizeTimeoutRef.current);\n }\n };\n }, [calculateDimensions, mountSpreadsheet]);\n\n useEffect(() => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (!url) return;\n\n let isMounted = true;\n\n const loadExcel = async () => {\n if (!containerRef.current) return;\n\n setLoading(true);\n setError(null);\n\n try {\n const response = await fetcher(url, {\n mode: 'cors',\n credentials: 'omit',\n redirect: 'follow',\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(t('xlsx.not_found'));\n } else if (response.status === 403) {\n throw new Error('无权限访问此文件');\n } else {\n throw new Error(`文件加载失败 (${response.status})`);\n }\n }\n\n const arrayBuffer = await response.arrayBuffer();\n\n if (arrayBuffer.byteLength === 0) {\n throw new Error('文件为空');\n }\n\n // 使用 exceljs 解析\n const workbook = new ExcelJS.Workbook();\n await workbook.xlsx.load(arrayBuffer);\n\n // 转换为 x-data-spreadsheet 数据格式\n const sheetData = convertWorkbookToSpreadsheetData(workbook);\n\n if (!isMounted) return;\n\n sheetDataRef.current = sheetData as unknown as Record<string, unknown>[];\n\n // 挂载 x-data-spreadsheet\n mountSpreadsheet();\n\n setLoading(false);\n } catch (err) {\n if (isMounted) {\n console.error('Excel 解析错误:', err);\n let errorMsg = t('xlsx.parse_failed');\n if (err instanceof Error) {\n errorMsg = err.message;\n }\n setError(errorMsg);\n setLoading(false);\n }\n }\n };\n\n const timer = setTimeout(() => {\n requestAnimationFrame(() => {\n loadExcel();\n });\n }, 100);\n\n return () => {\n isMounted = false;\n clearTimeout(timer);\n sheetDataRef.current = null;\n if (containerRef.current) {\n containerRef.current.innerHTML = '';\n }\n spreadsheetRef.current = null;\n };\n }, [url, mountSpreadsheet]);\n\n return (\n <div className=\"rfp-relative rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full\">\n {/* 加载状态 */}\n {loading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-toolbar rfp-backdrop-blur-sm rfp-z-10\">\n <div className=\"rfp-text-center\">\n <div className=\"rfp-w-10 rfp-h-10 md:rfp-w-12 md:rfp-h-12 rfp-mx-auto rfp-mb-3 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n <p className=\"rfp-text-xs md:rfp-text-sm rfp-text-fg-secondary rfp-font-medium\">{t('xlsx.loading')}</p>\n </div>\n </div>\n )}\n\n {/* 错误状态 */}\n {error && !loading && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-bg-surface-toolbar rfp-backdrop-blur-sm rfp-z-10\">\n <RendererError message={t('xlsx.load_failed')} detail={error} />\n </div>\n )}\n\n {/* Spreadsheet 容器 */}\n {!error && (\n <div\n ref={containerRef}\n className=\"xlsx-spreadsheet-container rfp-w-full rfp-h-full\"\n style={{ opacity: loading ? 0 : 1 }}\n />\n )}\n </div>\n );\n};\n"],"names":["XlsxRenderer","url","t","useTranslator","fetcher","useFetcher","loading","setLoading","useState","error","setError","containerRef","useRef","spreadsheetRef","sheetDataRef","resizeObserverRef","resizeTimeoutRef","lastDimensionsRef","calculateDimensions","useCallback","rawWidth","rawHeight","width","height","mountSpreadsheet","isMobile","s","Spreadsheet","useEffect","isInitialRender","updateDimensions","newDimensions","lastDimensions","widthDiff","heightDiff","isMounted","loadExcel","response","arrayBuffer","workbook","ExcelJS","sheetData","convertWorkbookToSpreadsheetData","err","errorMsg","timer","jsxs","jsx","RendererError"],"mappings":"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"}