@lamberl-lee/file-preview 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/COPYING +674 -0
  2. package/LICENSE +165 -0
  3. package/README.md +165 -0
  4. package/dist/AudioPreview.d.ts +9 -0
  5. package/dist/AudioPreview.js +29 -0
  6. package/dist/AudioPreview.js.map +1 -0
  7. package/dist/CodePreview.d.ts +10 -0
  8. package/dist/CodePreview.js +121 -0
  9. package/dist/CodePreview.js.map +1 -0
  10. package/dist/CsvPreview.d.ts +9 -0
  11. package/dist/CsvPreview.js +117 -0
  12. package/dist/CsvPreview.js.map +1 -0
  13. package/dist/DocxPreview.d.ts +11 -0
  14. package/dist/DocxPreview.js +89 -0
  15. package/dist/DocxPreview.js.map +1 -0
  16. package/dist/EpubPreview.d.ts +9 -0
  17. package/dist/EpubPreview.js +693 -0
  18. package/dist/EpubPreview.js.map +1 -0
  19. package/dist/HtmlPreview.d.ts +9 -0
  20. package/dist/HtmlPreview.js +60 -0
  21. package/dist/HtmlPreview.js.map +1 -0
  22. package/dist/ImagePreview.d.ts +9 -0
  23. package/dist/ImagePreview.js +44 -0
  24. package/dist/ImagePreview.js.map +1 -0
  25. package/dist/LargeFileGate.d.ts +12 -0
  26. package/dist/LargeFileGate.js +88 -0
  27. package/dist/LargeFileGate.js.map +1 -0
  28. package/dist/MarkdownPreview.d.ts +8 -0
  29. package/dist/MarkdownPreview.js +140 -0
  30. package/dist/MarkdownPreview.js.map +1 -0
  31. package/dist/PdfPreview.d.ts +11 -0
  32. package/dist/PdfPreview.js +206 -0
  33. package/dist/PdfPreview.js.map +1 -0
  34. package/dist/PlainTextLargePreview.d.ts +9 -0
  35. package/dist/PlainTextLargePreview.js +62 -0
  36. package/dist/PlainTextLargePreview.js.map +1 -0
  37. package/dist/PluginPreviewRenderer.d.ts +13 -0
  38. package/dist/PluginPreviewRenderer.js +89 -0
  39. package/dist/PluginPreviewRenderer.js.map +1 -0
  40. package/dist/PptxPreview.d.ts +16 -0
  41. package/dist/PptxPreview.js +376 -0
  42. package/dist/PptxPreview.js.map +1 -0
  43. package/dist/PreviewErrorBoundary.d.ts +29 -0
  44. package/dist/PreviewErrorBoundary.js +53 -0
  45. package/dist/PreviewErrorBoundary.js.map +1 -0
  46. package/dist/PreviewFallback.d.ts +18 -0
  47. package/dist/PreviewFallback.js +143 -0
  48. package/dist/PreviewFallback.js.map +1 -0
  49. package/dist/PreviewLoading.d.ts +8 -0
  50. package/dist/PreviewLoading.js +14 -0
  51. package/dist/PreviewLoading.js.map +1 -0
  52. package/dist/RtfPreview.d.ts +10 -0
  53. package/dist/RtfPreview.js +240 -0
  54. package/dist/RtfPreview.js.map +1 -0
  55. package/dist/ShikiSourceView.d.ts +11 -0
  56. package/dist/ShikiSourceView.js +112 -0
  57. package/dist/ShikiSourceView.js.map +1 -0
  58. package/dist/SvgPreview.d.ts +9 -0
  59. package/dist/SvgPreview.js +89 -0
  60. package/dist/SvgPreview.js.map +1 -0
  61. package/dist/TextPreview.d.ts +9 -0
  62. package/dist/TextPreview.js +9 -0
  63. package/dist/TextPreview.js.map +1 -0
  64. package/dist/VideoPreview.d.ts +9 -0
  65. package/dist/VideoPreview.js +18 -0
  66. package/dist/VideoPreview.js.map +1 -0
  67. package/dist/XlsxPreview.d.ts +12 -0
  68. package/dist/XlsxPreview.js +856 -0
  69. package/dist/XlsxPreview.js.map +1 -0
  70. package/dist/ZipPreview.d.ts +9 -0
  71. package/dist/ZipPreview.js +153 -0
  72. package/dist/ZipPreview.js.map +1 -0
  73. package/dist/core/binary.d.ts +10 -0
  74. package/dist/core/binary.js +27 -0
  75. package/dist/core/binary.js.map +1 -0
  76. package/dist/core/config.d.ts +17 -0
  77. package/dist/core/config.js +19 -0
  78. package/dist/core/config.js.map +1 -0
  79. package/dist/core/download.d.ts +5 -0
  80. package/dist/core/download.js +20 -0
  81. package/dist/core/download.js.map +1 -0
  82. package/dist/core/i18n.d.ts +101 -0
  83. package/dist/core/i18n.js +200 -0
  84. package/dist/core/i18n.js.map +1 -0
  85. package/dist/core/plugin.d.ts +38 -0
  86. package/dist/core/plugin.js +46 -0
  87. package/dist/core/plugin.js.map +1 -0
  88. package/dist/core/registry.d.ts +13 -0
  89. package/dist/core/registry.js +33 -0
  90. package/dist/core/registry.js.map +1 -0
  91. package/dist/core/source.d.ts +14 -0
  92. package/dist/core/source.js +131 -0
  93. package/dist/core/source.js.map +1 -0
  94. package/dist/core/types.d.ts +88 -0
  95. package/dist/core/types.js +27 -0
  96. package/dist/core/types.js.map +1 -0
  97. package/dist/hooks/useObjectUrlFromSource.d.ts +9 -0
  98. package/dist/hooks/useObjectUrlFromSource.js +50 -0
  99. package/dist/hooks/useObjectUrlFromSource.js.map +1 -0
  100. package/dist/hooks/useSourceBase64.d.ts +10 -0
  101. package/dist/hooks/useSourceBase64.js +32 -0
  102. package/dist/hooks/useSourceBase64.js.map +1 -0
  103. package/dist/hooks/useSourceText.d.ts +10 -0
  104. package/dist/hooks/useSourceText.js +32 -0
  105. package/dist/hooks/useSourceText.js.map +1 -0
  106. package/dist/icons.d.ts +44 -0
  107. package/dist/icons.js +248 -0
  108. package/dist/icons.js.map +1 -0
  109. package/dist/index.d.ts +36 -0
  110. package/dist/index.js +147 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/limits.d.ts +26 -0
  113. package/dist/limits.js +45 -0
  114. package/dist/limits.js.map +1 -0
  115. package/dist/performance-limits.d.ts +27 -0
  116. package/dist/performance-limits.js +54 -0
  117. package/dist/performance-limits.js.map +1 -0
  118. package/dist/plugins/audio-plugin.d.ts +7 -0
  119. package/dist/plugins/audio-plugin.js +11 -0
  120. package/dist/plugins/audio-plugin.js.map +1 -0
  121. package/dist/plugins/builtin-plugins.d.ts +9 -0
  122. package/dist/plugins/builtin-plugins.js +43 -0
  123. package/dist/plugins/builtin-plugins.js.map +1 -0
  124. package/dist/plugins/csv-plugin.d.ts +7 -0
  125. package/dist/plugins/csv-plugin.js +11 -0
  126. package/dist/plugins/csv-plugin.js.map +1 -0
  127. package/dist/plugins/docx-plugin.d.ts +7 -0
  128. package/dist/plugins/docx-plugin.js +15 -0
  129. package/dist/plugins/docx-plugin.js.map +1 -0
  130. package/dist/plugins/epub-plugin.d.ts +7 -0
  131. package/dist/plugins/epub-plugin.js +15 -0
  132. package/dist/plugins/epub-plugin.js.map +1 -0
  133. package/dist/plugins/html-plugin.d.ts +7 -0
  134. package/dist/plugins/html-plugin.js +11 -0
  135. package/dist/plugins/html-plugin.js.map +1 -0
  136. package/dist/plugins/image-plugin.d.ts +7 -0
  137. package/dist/plugins/image-plugin.js +11 -0
  138. package/dist/plugins/image-plugin.js.map +1 -0
  139. package/dist/plugins/markdown-plugin.d.ts +7 -0
  140. package/dist/plugins/markdown-plugin.js +11 -0
  141. package/dist/plugins/markdown-plugin.js.map +1 -0
  142. package/dist/plugins/pdf-plugin.d.ts +7 -0
  143. package/dist/plugins/pdf-plugin.js +15 -0
  144. package/dist/plugins/pdf-plugin.js.map +1 -0
  145. package/dist/plugins/pptx-plugin.d.ts +7 -0
  146. package/dist/plugins/pptx-plugin.js +15 -0
  147. package/dist/plugins/pptx-plugin.js.map +1 -0
  148. package/dist/plugins/rtf-plugin.d.ts +7 -0
  149. package/dist/plugins/rtf-plugin.js +15 -0
  150. package/dist/plugins/rtf-plugin.js.map +1 -0
  151. package/dist/plugins/source-code-plugin.d.ts +7 -0
  152. package/dist/plugins/source-code-plugin.js +11 -0
  153. package/dist/plugins/source-code-plugin.js.map +1 -0
  154. package/dist/plugins/svg-plugin.d.ts +7 -0
  155. package/dist/plugins/svg-plugin.js +11 -0
  156. package/dist/plugins/svg-plugin.js.map +1 -0
  157. package/dist/plugins/text-plugin.d.ts +7 -0
  158. package/dist/plugins/text-plugin.js +11 -0
  159. package/dist/plugins/text-plugin.js.map +1 -0
  160. package/dist/plugins/video-plugin.d.ts +7 -0
  161. package/dist/plugins/video-plugin.js +11 -0
  162. package/dist/plugins/video-plugin.js.map +1 -0
  163. package/dist/plugins/xlsx-plugin.d.ts +7 -0
  164. package/dist/plugins/xlsx-plugin.js +15 -0
  165. package/dist/plugins/xlsx-plugin.js.map +1 -0
  166. package/dist/plugins/zip-plugin.d.ts +7 -0
  167. package/dist/plugins/zip-plugin.js +15 -0
  168. package/dist/plugins/zip-plugin.js.map +1 -0
  169. package/dist/preview-adapters/AudioPreviewAdapter.d.ts +8 -0
  170. package/dist/preview-adapters/AudioPreviewAdapter.js +31 -0
  171. package/dist/preview-adapters/AudioPreviewAdapter.js.map +1 -0
  172. package/dist/preview-adapters/CsvPreviewAdapter.d.ts +8 -0
  173. package/dist/preview-adapters/CsvPreviewAdapter.js +28 -0
  174. package/dist/preview-adapters/CsvPreviewAdapter.js.map +1 -0
  175. package/dist/preview-adapters/DocxPreviewAdapter.d.ts +8 -0
  176. package/dist/preview-adapters/DocxPreviewAdapter.js +16 -0
  177. package/dist/preview-adapters/DocxPreviewAdapter.js.map +1 -0
  178. package/dist/preview-adapters/EpubPreviewAdapter.d.ts +8 -0
  179. package/dist/preview-adapters/EpubPreviewAdapter.js +28 -0
  180. package/dist/preview-adapters/EpubPreviewAdapter.js.map +1 -0
  181. package/dist/preview-adapters/HtmlPreviewAdapter.d.ts +8 -0
  182. package/dist/preview-adapters/HtmlPreviewAdapter.js +28 -0
  183. package/dist/preview-adapters/HtmlPreviewAdapter.js.map +1 -0
  184. package/dist/preview-adapters/ImagePreviewAdapter.d.ts +8 -0
  185. package/dist/preview-adapters/ImagePreviewAdapter.js +31 -0
  186. package/dist/preview-adapters/ImagePreviewAdapter.js.map +1 -0
  187. package/dist/preview-adapters/MarkdownPreviewAdapter.d.ts +8 -0
  188. package/dist/preview-adapters/MarkdownPreviewAdapter.js +28 -0
  189. package/dist/preview-adapters/MarkdownPreviewAdapter.js.map +1 -0
  190. package/dist/preview-adapters/PdfPreviewAdapter.d.ts +8 -0
  191. package/dist/preview-adapters/PdfPreviewAdapter.js +16 -0
  192. package/dist/preview-adapters/PdfPreviewAdapter.js.map +1 -0
  193. package/dist/preview-adapters/PptxPreviewAdapter.d.ts +8 -0
  194. package/dist/preview-adapters/PptxPreviewAdapter.js +16 -0
  195. package/dist/preview-adapters/PptxPreviewAdapter.js.map +1 -0
  196. package/dist/preview-adapters/RtfPreviewAdapter.d.ts +8 -0
  197. package/dist/preview-adapters/RtfPreviewAdapter.js +54 -0
  198. package/dist/preview-adapters/RtfPreviewAdapter.js.map +1 -0
  199. package/dist/preview-adapters/SourceCodePreviewAdapter.d.ts +8 -0
  200. package/dist/preview-adapters/SourceCodePreviewAdapter.js +35 -0
  201. package/dist/preview-adapters/SourceCodePreviewAdapter.js.map +1 -0
  202. package/dist/preview-adapters/SvgPreviewAdapter.d.ts +8 -0
  203. package/dist/preview-adapters/SvgPreviewAdapter.js +28 -0
  204. package/dist/preview-adapters/SvgPreviewAdapter.js.map +1 -0
  205. package/dist/preview-adapters/TextPreviewAdapter.d.ts +8 -0
  206. package/dist/preview-adapters/TextPreviewAdapter.js +28 -0
  207. package/dist/preview-adapters/TextPreviewAdapter.js.map +1 -0
  208. package/dist/preview-adapters/UnsupportedPluginPreview.d.ts +11 -0
  209. package/dist/preview-adapters/UnsupportedPluginPreview.js +34 -0
  210. package/dist/preview-adapters/UnsupportedPluginPreview.js.map +1 -0
  211. package/dist/preview-adapters/VideoPreviewAdapter.d.ts +8 -0
  212. package/dist/preview-adapters/VideoPreviewAdapter.js +31 -0
  213. package/dist/preview-adapters/VideoPreviewAdapter.js.map +1 -0
  214. package/dist/preview-adapters/XlsxPreviewAdapter.d.ts +8 -0
  215. package/dist/preview-adapters/XlsxPreviewAdapter.js +17 -0
  216. package/dist/preview-adapters/XlsxPreviewAdapter.js.map +1 -0
  217. package/dist/preview-adapters/ZipPreviewAdapter.d.ts +8 -0
  218. package/dist/preview-adapters/ZipPreviewAdapter.js +28 -0
  219. package/dist/preview-adapters/ZipPreviewAdapter.js.map +1 -0
  220. package/dist/remote-url.d.ts +20 -0
  221. package/dist/remote-url.js +478 -0
  222. package/dist/remote-url.js.map +1 -0
  223. package/dist/rtf/load-rtfjs.d.ts +42 -0
  224. package/dist/rtf/load-rtfjs.js +71 -0
  225. package/dist/rtf/load-rtfjs.js.map +1 -0
  226. package/dist/rtf/normalize-codepage.d.ts +33 -0
  227. package/dist/rtf/normalize-codepage.js +88 -0
  228. package/dist/rtf/normalize-codepage.js.map +1 -0
  229. package/dist/shiki.d.ts +35 -0
  230. package/dist/shiki.js +128 -0
  231. package/dist/shiki.js.map +1 -0
  232. package/dist/styles/AudioPreview.css +35 -0
  233. package/dist/styles/CsvPreview.css +106 -0
  234. package/dist/styles/DocxPreview.css +93 -0
  235. package/dist/styles/EpubPreview.css +509 -0
  236. package/dist/styles/HtmlPreview.css +15 -0
  237. package/dist/styles/ImagePreview.css +45 -0
  238. package/dist/styles/LargeFileGate.css +55 -0
  239. package/dist/styles/MarkdownPreview.css +291 -0
  240. package/dist/styles/PdfPreview.css +68 -0
  241. package/dist/styles/PlainTextLargePreview.css +85 -0
  242. package/dist/styles/PluginDebugBar.css +30 -0
  243. package/dist/styles/PptxPreview.css +207 -0
  244. package/dist/styles/PreviewFallback.css +88 -0
  245. package/dist/styles/PreviewLoading.css +13 -0
  246. package/dist/styles/RtfPreview.css +99 -0
  247. package/dist/styles/ShikiSourceView.css +159 -0
  248. package/dist/styles/SvgPreview.css +99 -0
  249. package/dist/styles/VideoPreview.css +19 -0
  250. package/dist/styles/ViewModeBar.css +38 -0
  251. package/dist/styles/XlsxPreview.css +361 -0
  252. package/dist/styles/ZipPreview.css +86 -0
  253. package/dist/styles/base.css +238 -0
  254. package/dist/styles/index.css +39 -0
  255. package/dist/support-status.d.ts +19 -0
  256. package/dist/support-status.js +174 -0
  257. package/dist/support-status.js.map +1 -0
  258. package/dist/utils.d.ts +17 -0
  259. package/dist/utils.js +205 -0
  260. package/dist/utils.js.map +1 -0
  261. package/package.json +125 -0
  262. package/scripts/copy-pdf-worker.mjs +31 -0
  263. package/scripts/copy-rtfjs-bundles.mjs +49 -0
@@ -0,0 +1,206 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState, useRef, useCallback } from "react";
3
+ import {
4
+ ChevronLeftIcon,
5
+ ChevronRightIcon,
6
+ ZoomInIcon,
7
+ ZoomOutIcon,
8
+ DownloadIcon,
9
+ RotateCwIcon,
10
+ AlertCircleIcon
11
+ } from "./icons";
12
+ import { readBinaryPreviewAsUint8Array } from "./core/binary";
13
+ import { resolveAssetPath } from "./core/config";
14
+ import "./styles/PdfPreview.css";
15
+ function PdfPreview({ content, source, fileName }) {
16
+ const [numPages, setNumPages] = useState(0);
17
+ const [currentPage, setCurrentPage] = useState(1);
18
+ const [scale, setScale] = useState(1.5);
19
+ const [rotation, setRotation] = useState(0);
20
+ const [loading, setLoading] = useState(true);
21
+ const [error, setError] = useState(null);
22
+ const canvasRef = useRef(null);
23
+ const pdfDocRef = useRef(null);
24
+ const renderTaskRef = useRef(null);
25
+ const renderingRef = useRef(false);
26
+ const renderPage = useCallback(async () => {
27
+ const pdfDoc = pdfDocRef.current;
28
+ if (!pdfDoc || !canvasRef.current) return;
29
+ if (renderingRef.current) {
30
+ const prevTask = renderTaskRef.current;
31
+ if (prevTask) {
32
+ try {
33
+ prevTask.cancel();
34
+ } catch {
35
+ }
36
+ }
37
+ }
38
+ renderingRef.current = true;
39
+ try {
40
+ const page = await pdfDoc.getPage(currentPage);
41
+ const viewport = page.getViewport({ scale, rotation });
42
+ const displayCanvas = canvasRef.current;
43
+ const context = displayCanvas.getContext("2d");
44
+ if (!context) return;
45
+ const offscreen = document.createElement("canvas");
46
+ offscreen.width = viewport.width;
47
+ offscreen.height = viewport.height;
48
+ const offCtx = offscreen.getContext("2d");
49
+ if (!offCtx) return;
50
+ const renderTask = page.render({
51
+ canvasContext: offCtx,
52
+ viewport
53
+ });
54
+ renderTaskRef.current = renderTask;
55
+ await renderTask.promise;
56
+ displayCanvas.width = viewport.width;
57
+ displayCanvas.height = viewport.height;
58
+ context.drawImage(offscreen, 0, 0);
59
+ } catch (err) {
60
+ const errObj = err;
61
+ if (errObj?.name === "RenderingCancelledException") return;
62
+ console.error("Error rendering PDF page:", err);
63
+ } finally {
64
+ renderingRef.current = false;
65
+ renderTaskRef.current = null;
66
+ }
67
+ }, [currentPage, scale, rotation]);
68
+ useEffect(() => {
69
+ let cancelled = false;
70
+ const loadPdf = async () => {
71
+ try {
72
+ setLoading(true);
73
+ setError(null);
74
+ const pdfjsLib = await import("pdfjs-dist");
75
+ pdfjsLib.GlobalWorkerOptions.workerSrc = resolveAssetPath(
76
+ "/vendor/pdfjs/pdf.worker.min.mjs"
77
+ );
78
+ const bytes = await readBinaryPreviewAsUint8Array({ source, content });
79
+ const loadingTask = pdfjsLib.getDocument({ data: bytes });
80
+ const pdf = await loadingTask.promise;
81
+ if (cancelled) {
82
+ await pdf.destroy?.();
83
+ return;
84
+ }
85
+ pdfDocRef.current = pdf;
86
+ setNumPages(pdf.numPages);
87
+ setCurrentPage(1);
88
+ } catch (err) {
89
+ if (!cancelled) {
90
+ console.error("Error loading PDF:", err);
91
+ setError(
92
+ err instanceof Error ? err.message : "Failed to load PDF"
93
+ );
94
+ }
95
+ } finally {
96
+ if (!cancelled) {
97
+ setLoading(false);
98
+ }
99
+ }
100
+ };
101
+ loadPdf();
102
+ return () => {
103
+ cancelled = true;
104
+ try {
105
+ renderTaskRef.current?.cancel();
106
+ } catch {
107
+ }
108
+ try {
109
+ void pdfDocRef.current?.destroy?.();
110
+ } catch {
111
+ }
112
+ renderTaskRef.current = null;
113
+ pdfDocRef.current = null;
114
+ renderingRef.current = false;
115
+ };
116
+ }, [content, source]);
117
+ useEffect(() => {
118
+ if (pdfDocRef.current && !loading) {
119
+ renderPage();
120
+ }
121
+ }, [currentPage, scale, rotation, loading, renderPage]);
122
+ const goToPage = (page) => {
123
+ setCurrentPage(Math.max(1, Math.min(page, numPages)));
124
+ };
125
+ const handleZoomIn = () => setScale((prev) => Math.min(prev + 0.25, 4));
126
+ const handleZoomOut = () => setScale((prev) => Math.max(prev - 0.25, 0.5));
127
+ const handleRotate = () => setRotation((prev) => (prev + 90) % 360);
128
+ const handleDownload = async () => {
129
+ try {
130
+ const bytes = await readBinaryPreviewAsUint8Array({ source, content });
131
+ const blob = new Blob([bytes], { type: "application/pdf" });
132
+ const url = URL.createObjectURL(blob);
133
+ const a = document.createElement("a");
134
+ a.href = url;
135
+ a.download = fileName;
136
+ a.click();
137
+ URL.revokeObjectURL(url);
138
+ } catch (err) {
139
+ console.error("Failed to download PDF:", err);
140
+ }
141
+ };
142
+ if (loading) {
143
+ return /* @__PURE__ */ jsxs("div", { className: "fv-loading", style: { minHeight: 400 }, children: [
144
+ /* @__PURE__ */ jsx("div", { className: "fv-spinner fv-spinner--lg" }),
145
+ /* @__PURE__ */ jsx("p", { className: "fv-loading__label", children: "Loading PDF..." })
146
+ ] });
147
+ }
148
+ if (error) {
149
+ return /* @__PURE__ */ jsxs("div", { className: "fv-pdf__error", children: [
150
+ /* @__PURE__ */ jsx(AlertCircleIcon, { size: 48 }),
151
+ /* @__PURE__ */ jsx("p", { className: "fv-pdf__error-title", children: "PDF Loading Failed" }),
152
+ /* @__PURE__ */ jsx("p", { className: "fv-pdf__error-msg", children: error }),
153
+ /* @__PURE__ */ jsxs("button", { onClick: handleDownload, className: "fv-btn fv-btn--primary", style: { marginTop: "0.5rem" }, children: [
154
+ /* @__PURE__ */ jsx(DownloadIcon, { size: 16 }),
155
+ " Download PDF"
156
+ ] })
157
+ ] });
158
+ }
159
+ return /* @__PURE__ */ jsxs("div", { className: "fv-pdf", children: [
160
+ /* @__PURE__ */ jsxs("div", { className: "fv-pdf__toolbar", children: [
161
+ /* @__PURE__ */ jsxs("div", { className: "fv-pdf__nav", children: [
162
+ /* @__PURE__ */ jsx(
163
+ "button",
164
+ {
165
+ onClick: () => goToPage(currentPage - 1),
166
+ disabled: currentPage <= 1,
167
+ className: "fv-btn fv-btn--icon",
168
+ title: "Previous page",
169
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, { size: 16 })
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsxs("span", { className: "fv-pdf__page-info", children: [
173
+ currentPage,
174
+ " / ",
175
+ numPages
176
+ ] }),
177
+ /* @__PURE__ */ jsx(
178
+ "button",
179
+ {
180
+ onClick: () => goToPage(currentPage + 1),
181
+ disabled: currentPage >= numPages,
182
+ className: "fv-btn fv-btn--icon",
183
+ title: "Next page",
184
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { size: 16 })
185
+ }
186
+ )
187
+ ] }),
188
+ /* @__PURE__ */ jsxs("div", { className: "fv-pdf__controls", children: [
189
+ /* @__PURE__ */ jsx("button", { onClick: handleZoomOut, className: "fv-btn fv-btn--icon", title: "Zoom Out", children: /* @__PURE__ */ jsx(ZoomOutIcon, { size: 16 }) }),
190
+ /* @__PURE__ */ jsxs("span", { className: "fv-pdf__zoom-label", children: [
191
+ Math.round(scale * 100),
192
+ "%"
193
+ ] }),
194
+ /* @__PURE__ */ jsx("button", { onClick: handleZoomIn, className: "fv-btn fv-btn--icon", title: "Zoom In", children: /* @__PURE__ */ jsx(ZoomInIcon, { size: 16 }) }),
195
+ /* @__PURE__ */ jsx("div", { className: "fv-toolbar__separator" }),
196
+ /* @__PURE__ */ jsx("button", { onClick: handleRotate, className: "fv-btn fv-btn--icon", title: "Rotate", children: /* @__PURE__ */ jsx(RotateCwIcon, { size: 16 }) }),
197
+ /* @__PURE__ */ jsx("button", { onClick: handleDownload, className: "fv-btn fv-btn--icon", title: "Download", children: /* @__PURE__ */ jsx(DownloadIcon, { size: 16 }) })
198
+ ] })
199
+ ] }),
200
+ /* @__PURE__ */ jsx("div", { className: "fv-pdf__canvas-area", children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "fv-pdf__canvas" }) })
201
+ ] });
202
+ }
203
+ export {
204
+ PdfPreview
205
+ };
206
+ //# sourceMappingURL=PdfPreview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/PdfPreview.tsx"],"sourcesContent":["import { useEffect, useState, useRef, useCallback } from \"react\";\nimport {\n ChevronLeftIcon,\n ChevronRightIcon,\n ZoomInIcon,\n ZoomOutIcon,\n DownloadIcon,\n RotateCwIcon,\n AlertCircleIcon,\n} from \"./icons\";\nimport { readBinaryPreviewAsUint8Array } from \"./core/binary\";\nimport { resolveAssetPath } from \"./core/config\";\nimport type { PreviewSource } from \"./core/types\";\nimport \"./styles/PdfPreview.css\";\n\ninterface PdfPreviewProps {\n content?: string | null;\n source?: PreviewSource;\n fileName: string;\n}\n\ninterface PdfDocumentLike {\n numPages: number;\n getPage(pageNumber: number): Promise<PdfPageLike>;\n destroy?: () => Promise<void>;\n}\n\ninterface PdfPageLike {\n getViewport(opts: { scale: number; rotation: number }): {\n width: number;\n height: number;\n };\n render(opts: {\n canvasContext: CanvasRenderingContext2D;\n viewport: { width: number; height: number };\n }): PdfRenderTaskLike;\n}\n\ninterface PdfRenderTaskLike {\n promise: Promise<void>;\n cancel: () => void;\n}\n\nexport function PdfPreview({ content, source, fileName }: PdfPreviewProps) {\n const [numPages, setNumPages] = useState(0);\n const [currentPage, setCurrentPage] = useState(1);\n const [scale, setScale] = useState(1.5);\n const [rotation, setRotation] = useState(0);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const pdfDocRef = useRef<PdfDocumentLike | null>(null);\n const renderTaskRef = useRef<PdfRenderTaskLike | null>(null);\n const renderingRef = useRef(false);\n\n const renderPage = useCallback(async () => {\n const pdfDoc = pdfDocRef.current;\n if (!pdfDoc || !canvasRef.current) return;\n\n if (renderingRef.current) {\n const prevTask = renderTaskRef.current;\n if (prevTask) {\n try { prevTask.cancel(); } catch { /* ignore */ }\n }\n }\n\n renderingRef.current = true;\n\n try {\n const page = await pdfDoc.getPage(currentPage);\n\n const viewport = page.getViewport({ scale, rotation });\n const displayCanvas = canvasRef.current;\n const context = displayCanvas.getContext(\"2d\");\n if (!context) return;\n\n const offscreen = document.createElement(\"canvas\");\n offscreen.width = viewport.width;\n offscreen.height = viewport.height;\n const offCtx = offscreen.getContext(\"2d\");\n if (!offCtx) return;\n\n const renderTask = page.render({\n canvasContext: offCtx,\n viewport,\n });\n\n renderTaskRef.current = renderTask;\n\n await renderTask.promise;\n\n displayCanvas.width = viewport.width;\n displayCanvas.height = viewport.height;\n context.drawImage(offscreen, 0, 0);\n\n } catch (err: unknown) {\n const errObj = err as { name?: string };\n if (errObj?.name === \"RenderingCancelledException\") return;\n console.error(\"Error rendering PDF page:\", err);\n } finally {\n renderingRef.current = false;\n renderTaskRef.current = null;\n }\n }, [currentPage, scale, rotation]);\n\n useEffect(() => {\n let cancelled = false;\n\n const loadPdf = async () => {\n try {\n setLoading(true);\n setError(null);\n\n const pdfjsLib = await import(\"pdfjs-dist\");\n\n pdfjsLib.GlobalWorkerOptions.workerSrc = resolveAssetPath(\n \"/vendor/pdfjs/pdf.worker.min.mjs\",\n );\n\n const bytes = await readBinaryPreviewAsUint8Array({ source, content });\n\n const loadingTask = pdfjsLib.getDocument({ data: bytes });\n const pdf = await loadingTask.promise;\n\n if (cancelled) {\n await pdf.destroy?.();\n return;\n }\n\n pdfDocRef.current = pdf;\n setNumPages(pdf.numPages);\n setCurrentPage(1);\n } catch (err) {\n if (!cancelled) {\n console.error(\"Error loading PDF:\", err);\n setError(\n err instanceof Error ? err.message : \"Failed to load PDF\"\n );\n }\n } finally {\n if (!cancelled) {\n setLoading(false);\n }\n }\n };\n\n loadPdf();\n\n return () => {\n cancelled = true;\n\n try {\n renderTaskRef.current?.cancel();\n } catch {\n // ignore\n }\n\n try {\n void pdfDocRef.current?.destroy?.();\n } catch {\n // ignore\n }\n\n renderTaskRef.current = null;\n pdfDocRef.current = null;\n renderingRef.current = false;\n };\n }, [content, source]);\n\n useEffect(() => {\n if (pdfDocRef.current && !loading) {\n renderPage();\n }\n }, [currentPage, scale, rotation, loading, renderPage]);\n\n const goToPage = (page: number) => {\n setCurrentPage(Math.max(1, Math.min(page, numPages)));\n };\n\n const handleZoomIn = () => setScale((prev) => Math.min(prev + 0.25, 4));\n const handleZoomOut = () => setScale((prev) => Math.max(prev - 0.25, 0.5));\n const handleRotate = () => setRotation((prev) => (prev + 90) % 360);\n\n const handleDownload = async () => {\n try {\n const bytes = await readBinaryPreviewAsUint8Array({ source, content });\n const blob = new Blob([bytes], { type: \"application/pdf\" });\n\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = fileName;\n a.click();\n URL.revokeObjectURL(url);\n } catch (err) {\n console.error(\"Failed to download PDF:\", err);\n }\n };\n\n if (loading) {\n return (\n <div className=\"fv-loading\" style={{ minHeight: 400 }}>\n <div className=\"fv-spinner fv-spinner--lg\" />\n <p className=\"fv-loading__label\">Loading PDF...</p>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"fv-pdf__error\">\n <AlertCircleIcon size={48} />\n <p className=\"fv-pdf__error-title\">PDF Loading Failed</p>\n <p className=\"fv-pdf__error-msg\">{error}</p>\n <button onClick={handleDownload} className=\"fv-btn fv-btn--primary\" style={{ marginTop: '0.5rem' }}>\n <DownloadIcon size={16} /> Download PDF\n </button>\n </div>\n );\n }\n\n return (\n <div className=\"fv-pdf\">\n <div className=\"fv-pdf__toolbar\">\n <div className=\"fv-pdf__nav\">\n <button\n onClick={() => goToPage(currentPage - 1)}\n disabled={currentPage <= 1}\n className=\"fv-btn fv-btn--icon\"\n title=\"Previous page\"\n >\n <ChevronLeftIcon size={16} />\n </button>\n <span className=\"fv-pdf__page-info\">\n {currentPage} / {numPages}\n </span>\n <button\n onClick={() => goToPage(currentPage + 1)}\n disabled={currentPage >= numPages}\n className=\"fv-btn fv-btn--icon\"\n title=\"Next page\"\n >\n <ChevronRightIcon size={16} />\n </button>\n </div>\n\n <div className=\"fv-pdf__controls\">\n <button onClick={handleZoomOut} className=\"fv-btn fv-btn--icon\" title=\"Zoom Out\">\n <ZoomOutIcon size={16} />\n </button>\n <span className=\"fv-pdf__zoom-label\">{Math.round(scale * 100)}%</span>\n <button onClick={handleZoomIn} className=\"fv-btn fv-btn--icon\" title=\"Zoom In\">\n <ZoomInIcon size={16} />\n </button>\n <div className=\"fv-toolbar__separator\" />\n <button onClick={handleRotate} className=\"fv-btn fv-btn--icon\" title=\"Rotate\">\n <RotateCwIcon size={16} />\n </button>\n <button onClick={handleDownload} className=\"fv-btn fv-btn--icon\" title=\"Download\">\n <DownloadIcon size={16} />\n </button>\n </div>\n </div>\n\n <div className=\"fv-pdf__canvas-area\">\n <canvas ref={canvasRef} className=\"fv-pdf__canvas\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ import * as react from 'react';
2
+
3
+ interface PlainTextLargePreviewProps {
4
+ content: string;
5
+ language: string;
6
+ }
7
+ declare function PlainTextLargePreview({ content, language }: PlainTextLargePreviewProps): react.JSX.Element;
8
+
9
+ export { PlainTextLargePreview };
@@ -0,0 +1,62 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useCallback, useMemo } from "react";
3
+ import { CopyIcon, CheckIcon, WrapTextIcon } from "./icons";
4
+ import { formatFileSize } from "./utils";
5
+ import { truncateContent } from "./limits";
6
+ import { useLocale } from "./core/i18n";
7
+ import "./styles/PlainTextLargePreview.css";
8
+ function PlainTextLargePreview({ content, language }) {
9
+ const [copied, setCopied] = useState(false);
10
+ const [wordWrap, setWordWrap] = useState(true);
11
+ const t = useLocale();
12
+ const displayContent = useMemo(() => truncateContent(content), [content]);
13
+ const lineCount = useMemo(() => content.split("\n").length, [content]);
14
+ const fileSize = useMemo(() => content.length, [content]);
15
+ const handleCopy = useCallback(async () => {
16
+ await navigator.clipboard.writeText(content);
17
+ setCopied(true);
18
+ setTimeout(() => setCopied(false), 2e3);
19
+ }, [content]);
20
+ return /* @__PURE__ */ jsxs("div", { className: "fv-plain-text", children: [
21
+ /* @__PURE__ */ jsxs("div", { className: "fv-plain-text__toolbar", children: [
22
+ /* @__PURE__ */ jsxs("div", { className: "fv-plain-text__toolbar-left", children: [
23
+ /* @__PURE__ */ jsx("span", { className: "fv-plain-text__lang-badge", children: language }),
24
+ /* @__PURE__ */ jsxs("span", { className: "fv-plain-text__line-count", children: [
25
+ lineCount.toLocaleString(),
26
+ " ",
27
+ t.lines
28
+ ] }),
29
+ /* @__PURE__ */ jsx("span", { className: "fv-plain-text__file-size", children: formatFileSize(fileSize) }),
30
+ /* @__PURE__ */ jsx("span", { className: "fv-plain-text__large-badge", children: t.largeFile })
31
+ ] }),
32
+ /* @__PURE__ */ jsxs("div", { className: "fv-plain-text__toolbar-right", children: [
33
+ /* @__PURE__ */ jsx(
34
+ "button",
35
+ {
36
+ onClick: () => setWordWrap((w) => !w),
37
+ className: `fv-btn fv-btn--icon ${wordWrap ? "fv-source__btn-active" : ""}`,
38
+ title: wordWrap ? t.wordWrapOff : t.wordWrapOn,
39
+ children: /* @__PURE__ */ jsx(WrapTextIcon, { size: 14 })
40
+ }
41
+ ),
42
+ /* @__PURE__ */ jsx(
43
+ "button",
44
+ {
45
+ onClick: handleCopy,
46
+ className: "fv-btn fv-btn--icon",
47
+ title: t.copyContent,
48
+ children: copied ? /* @__PURE__ */ jsx(CheckIcon, { size: 14 }) : /* @__PURE__ */ jsx(CopyIcon, { size: 14 })
49
+ }
50
+ )
51
+ ] })
52
+ ] }),
53
+ /* @__PURE__ */ jsx("div", { className: `fv-plain-text__content ${wordWrap ? "fv-plain-text__content--wrap" : "fv-plain-text__content--nowrap"}`, children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: displayContent.split("\n").map((line, i) => /* @__PURE__ */ jsxs("div", { className: "line", children: [
54
+ /* @__PURE__ */ jsx("span", { className: "linenumber", children: i + 1 }),
55
+ /* @__PURE__ */ jsx("span", { className: "linecontent", children: line || " " })
56
+ ] }, i)) }) }) })
57
+ ] });
58
+ }
59
+ export {
60
+ PlainTextLargePreview
61
+ };
62
+ //# sourceMappingURL=PlainTextLargePreview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/PlainTextLargePreview.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from \"react\";\nimport { CopyIcon, CheckIcon, WrapTextIcon } from \"./icons\";\nimport { formatFileSize } from \"./utils\";\nimport { truncateContent } from \"./limits\";\nimport { useLocale } from \"./core/i18n\";\nimport \"./styles/PlainTextLargePreview.css\";\n\ninterface PlainTextLargePreviewProps {\n content: string;\n language: string;\n}\n\nexport function PlainTextLargePreview({ content, language }: PlainTextLargePreviewProps) {\n const [copied, setCopied] = useState(false);\n const [wordWrap, setWordWrap] = useState(true);\n const t = useLocale();\n\n const displayContent = useMemo(() => truncateContent(content), [content]);\n const lineCount = useMemo(() => content.split(\"\\n\").length, [content]);\n const fileSize = useMemo(() => content.length, [content]);\n\n const handleCopy = useCallback(async () => {\n await navigator.clipboard.writeText(content);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }, [content]);\n\n return (\n <div className=\"fv-plain-text\">\n <div className=\"fv-plain-text__toolbar\">\n <div className=\"fv-plain-text__toolbar-left\">\n <span className=\"fv-plain-text__lang-badge\">{language}</span>\n <span className=\"fv-plain-text__line-count\">\n {lineCount.toLocaleString()} {t.lines}\n </span>\n <span className=\"fv-plain-text__file-size\">\n {formatFileSize(fileSize)}\n </span>\n <span className=\"fv-plain-text__large-badge\">{t.largeFile}</span>\n </div>\n <div className=\"fv-plain-text__toolbar-right\">\n <button\n onClick={() => setWordWrap((w) => !w)}\n className={`fv-btn fv-btn--icon ${wordWrap ? \"fv-source__btn-active\" : \"\"}`}\n title={wordWrap ? t.wordWrapOff : t.wordWrapOn}\n >\n <WrapTextIcon size={14} />\n </button>\n <button\n onClick={handleCopy}\n className=\"fv-btn fv-btn--icon\"\n title={t.copyContent}\n >\n {copied ? <CheckIcon size={14} /> : <CopyIcon size={14} />}\n </button>\n </div>\n </div>\n\n <div className={`fv-plain-text__content ${wordWrap ? \"fv-plain-text__content--wrap\" : \"fv-plain-text__content--nowrap\"}`}>\n <pre>\n <code>\n {displayContent.split(\"\\n\").map((line, i) => (\n <div key={i} className=\"line\">\n <span className=\"linenumber\">{i + 1}</span>\n <span className=\"linecontent\">{line || \" \"}</span>\n </div>\n ))}\n </code>\n </pre>\n </div>\n </div>\n );\n}\n"],"mappings":"AA+BU,cACA,YADA;AA/BV,SAAS,UAAU,aAAa,eAAe;AAC/C,SAAS,UAAU,WAAW,oBAAoB;AAClD,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAChC,SAAS,iBAAiB;AAC1B,OAAO;AAOA,SAAS,sBAAsB,EAAE,SAAS,SAAS,GAA+B;AACvF,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,IAAI;AAC7C,QAAM,IAAI,UAAU;AAEpB,QAAM,iBAAiB,QAAQ,MAAM,gBAAgB,OAAO,GAAG,CAAC,OAAO,CAAC;AACxE,QAAM,YAAY,QAAQ,MAAM,QAAQ,MAAM,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC;AACrE,QAAM,WAAW,QAAQ,MAAM,QAAQ,QAAQ,CAAC,OAAO,CAAC;AAExD,QAAM,aAAa,YAAY,YAAY;AACzC,UAAM,UAAU,UAAU,UAAU,OAAO;AAC3C,cAAU,IAAI;AACd,eAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,EACzC,GAAG,CAAC,OAAO,CAAC;AAEZ,SACE,qBAAC,SAAI,WAAU,iBACb;AAAA,yBAAC,SAAI,WAAU,0BACb;AAAA,2BAAC,SAAI,WAAU,+BACb;AAAA,4BAAC,UAAK,WAAU,6BAA6B,oBAAS;AAAA,QACtD,qBAAC,UAAK,WAAU,6BACb;AAAA,oBAAU,eAAe;AAAA,UAAE;AAAA,UAAE,EAAE;AAAA,WAClC;AAAA,QACA,oBAAC,UAAK,WAAU,4BACb,yBAAe,QAAQ,GAC1B;AAAA,QACA,oBAAC,UAAK,WAAU,8BAA8B,YAAE,WAAU;AAAA,SAC5D;AAAA,MACA,qBAAC,SAAI,WAAU,gCACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;AAAA,YACpC,WAAW,uBAAuB,WAAW,0BAA0B,EAAE;AAAA,YACzE,OAAO,WAAW,EAAE,cAAc,EAAE;AAAA,YAEpC,8BAAC,gBAAa,MAAM,IAAI;AAAA;AAAA,QAC1B;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,WAAU;AAAA,YACV,OAAO,EAAE;AAAA,YAER,mBAAS,oBAAC,aAAU,MAAM,IAAI,IAAK,oBAAC,YAAS,MAAM,IAAI;AAAA;AAAA,QAC1D;AAAA,SACF;AAAA,OACF;AAAA,IAEA,oBAAC,SAAI,WAAW,0BAA0B,WAAW,iCAAiC,gCAAgC,IACpH,8BAAC,SACC,8BAAC,UACE,yBAAe,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,MACrC,qBAAC,SAAY,WAAU,QACrB;AAAA,0BAAC,UAAK,WAAU,cAAc,cAAI,GAAE;AAAA,MACpC,oBAAC,UAAK,WAAU,eAAe,kBAAQ,KAAI;AAAA,SAFnC,CAGV,CACD,GACH,GACF,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,13 @@
1
+ import * as react from 'react';
2
+ import { FileInfo } from './core/types.js';
3
+ import { PreviewPluginRegistry } from './core/registry.js';
4
+ import './core/plugin.js';
5
+
6
+ interface PluginPreviewRendererProps {
7
+ file: FileInfo;
8
+ registry?: PreviewPluginRegistry;
9
+ showPluginDebug?: boolean;
10
+ }
11
+ declare function PluginPreviewRenderer({ file, registry, showPluginDebug, }: PluginPreviewRendererProps): react.JSX.Element;
12
+
13
+ export { PluginPreviewRenderer, type PluginPreviewRendererProps };
@@ -0,0 +1,89 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Suspense, use, useMemo, useState, useCallback } from "react";
3
+ import { createBuiltinPreviewRegistry } from "./plugins/builtin-plugins";
4
+ import { UnsupportedPluginPreview } from "./preview-adapters/UnsupportedPluginPreview";
5
+ import { getPreviewSupportMeta } from "./support-status";
6
+ import { PreviewErrorBoundary } from "./PreviewErrorBoundary";
7
+ import { PreviewLoading } from "./PreviewLoading";
8
+ import "./styles/PluginDebugBar.css";
9
+ class PreviewPluginLoadError extends Error {
10
+ constructor(pluginId, pluginName, cause) {
11
+ super(`Failed to load preview plugin: ${pluginName}`);
12
+ this.pluginId = pluginId;
13
+ this.pluginName = pluginName;
14
+ this.cause = cause;
15
+ this.name = "PreviewPluginLoadError";
16
+ }
17
+ }
18
+ const promiseCache = /* @__PURE__ */ new WeakMap();
19
+ function getPluginPromise(plugin) {
20
+ const cached = promiseCache.get(plugin);
21
+ if (cached) return cached;
22
+ const promise = plugin.load().catch((error) => {
23
+ throw new PreviewPluginLoadError(plugin.id, plugin.name, error);
24
+ });
25
+ promiseCache.set(plugin, promise);
26
+ return promise;
27
+ }
28
+ function invalidatePluginPromise(plugin) {
29
+ promiseCache.delete(plugin);
30
+ }
31
+ function PluginContent({ plugin, file }) {
32
+ const mod = use(getPluginPromise(plugin));
33
+ const Component = mod.default;
34
+ return /* @__PURE__ */ jsx(Component, { file });
35
+ }
36
+ function PluginPreviewRenderer({
37
+ file,
38
+ registry,
39
+ showPluginDebug = false
40
+ }) {
41
+ const [retryKey, setRetryKey] = useState(0);
42
+ const finalRegistry = useMemo(() => {
43
+ return registry ?? createBuiltinPreviewRegistry();
44
+ }, [registry]);
45
+ const plugin = useMemo(
46
+ () => finalRegistry.resolve(file),
47
+ [finalRegistry, file]
48
+ );
49
+ const handleRetry = useCallback(() => {
50
+ if (plugin) {
51
+ invalidatePluginPromise(plugin);
52
+ }
53
+ setRetryKey((value) => value + 1);
54
+ }, [plugin]);
55
+ if (!plugin) {
56
+ const support = getPreviewSupportMeta(file.fileType);
57
+ return /* @__PURE__ */ jsx(
58
+ UnsupportedPluginPreview,
59
+ {
60
+ file,
61
+ title: support.status === "legacy-only" ? "Not Migrated Yet" : void 0,
62
+ description: support.status === "legacy-only" ? `This file type (${file.fileType}) is currently only available in Legacy Renderer.` : support.status === "degraded" ? support.note ?? `This file type (${file.fileType}) only has degraded legacy support and is not available in Plugin Renderer.` : support.note ?? `This file type (${file.fileType}) cannot be previewed by the plugin renderer.`
63
+ }
64
+ );
65
+ }
66
+ return /* @__PURE__ */ jsxs("div", { className: "fv-plugin-renderer", children: [
67
+ showPluginDebug && /* @__PURE__ */ jsxs("div", { className: "fv-plugin-debug", children: [
68
+ /* @__PURE__ */ jsx("span", { className: "fv-plugin-debug__label", children: "Plugin Renderer" }),
69
+ /* @__PURE__ */ jsx("span", { children: "\u2192" }),
70
+ /* @__PURE__ */ jsx("span", { children: plugin.name }),
71
+ /* @__PURE__ */ jsx("span", { className: "fv-plugin-debug__id", children: plugin.id })
72
+ ] }),
73
+ /* @__PURE__ */ jsx("div", { className: "fv-plugin-renderer__content", children: /* @__PURE__ */ jsx(
74
+ PreviewErrorBoundary,
75
+ {
76
+ file,
77
+ pluginId: plugin.id,
78
+ pluginName: plugin.name,
79
+ resetKey: `${file.id}:${plugin.id}:${retryKey}`,
80
+ onRetry: handleRetry,
81
+ children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(PreviewLoading, {}), children: /* @__PURE__ */ jsx(PluginContent, { plugin, file }) })
82
+ }
83
+ ) })
84
+ ] });
85
+ }
86
+ export {
87
+ PluginPreviewRenderer
88
+ };
89
+ //# sourceMappingURL=PluginPreviewRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/PluginPreviewRenderer.tsx"],"sourcesContent":["import { Suspense, use, useMemo, useState, useCallback } from \"react\";\nimport type { ComponentType } from \"react\";\nimport type { FileInfo } from \"./utils\";\nimport type { PreviewPlugin } from \"./core/plugin\";\nimport type { PreviewPluginRegistry } from \"./core/registry\";\nimport { createBuiltinPreviewRegistry } from \"./plugins/builtin-plugins\";\nimport { UnsupportedPluginPreview } from \"./preview-adapters/UnsupportedPluginPreview\";\nimport { getPreviewSupportMeta } from \"./support-status\";\nimport { PreviewErrorBoundary } from \"./PreviewErrorBoundary\";\nimport { PreviewLoading } from \"./PreviewLoading\";\nimport \"./styles/PluginDebugBar.css\";\n\nclass PreviewPluginLoadError extends Error {\n constructor(\n public pluginId: string,\n public pluginName: string,\n public cause: unknown,\n ) {\n super(`Failed to load preview plugin: ${pluginName}`);\n this.name = \"PreviewPluginLoadError\";\n }\n}\n\ntype PluginModule = { default: ComponentType<{ file: FileInfo }> };\nconst promiseCache = new WeakMap<PreviewPlugin, Promise<PluginModule>>();\n\nfunction getPluginPromise(plugin: PreviewPlugin): Promise<PluginModule> {\n const cached = promiseCache.get(plugin);\n if (cached) return cached;\n\n const promise = plugin.load().catch((error) => {\n throw new PreviewPluginLoadError(plugin.id, plugin.name, error);\n });\n promiseCache.set(plugin, promise);\n return promise;\n}\n\nfunction invalidatePluginPromise(plugin: PreviewPlugin) {\n promiseCache.delete(plugin);\n}\n\ninterface PluginContentProps {\n plugin: PreviewPlugin;\n file: FileInfo;\n}\n\nfunction PluginContent({ plugin, file }: PluginContentProps) {\n const mod = use(getPluginPromise(plugin));\n const Component = mod.default;\n return <Component file={file} />;\n}\n\nexport interface PluginPreviewRendererProps {\n file: FileInfo;\n registry?: PreviewPluginRegistry;\n showPluginDebug?: boolean;\n}\n\nexport function PluginPreviewRenderer({\n file,\n registry,\n showPluginDebug = false,\n}: PluginPreviewRendererProps) {\n const [retryKey, setRetryKey] = useState(0);\n\n const finalRegistry = useMemo(() => {\n return registry ?? createBuiltinPreviewRegistry();\n }, [registry]);\n\n const plugin = useMemo(\n () => finalRegistry.resolve(file),\n [finalRegistry, file]\n );\n\n const handleRetry = useCallback(() => {\n if (plugin) {\n invalidatePluginPromise(plugin);\n }\n setRetryKey((value) => value + 1);\n }, [plugin]);\n\n if (!plugin) {\n const support = getPreviewSupportMeta(file.fileType);\n\n return (\n <UnsupportedPluginPreview\n file={file}\n title={support.status === \"legacy-only\" ? \"Not Migrated Yet\" : undefined}\n description={\n support.status === \"legacy-only\"\n ? `This file type (${file.fileType}) is currently only available in Legacy Renderer.`\n : support.status === \"degraded\"\n ? support.note ??\n `This file type (${file.fileType}) only has degraded legacy support and is not available in Plugin Renderer.`\n : support.note ??\n `This file type (${file.fileType}) cannot be previewed by the plugin renderer.`\n }\n />\n );\n }\n\n return (\n <div className=\"fv-plugin-renderer\">\n {showPluginDebug && (\n <div className=\"fv-plugin-debug\">\n <span className=\"fv-plugin-debug__label\">Plugin Renderer</span>\n <span>→</span>\n <span>{plugin.name}</span>\n <span className=\"fv-plugin-debug__id\">{plugin.id}</span>\n </div>\n )}\n\n <div className=\"fv-plugin-renderer__content\">\n <PreviewErrorBoundary\n file={file}\n pluginId={plugin.id}\n pluginName={plugin.name}\n resetKey={`${file.id}:${plugin.id}:${retryKey}`}\n onRetry={handleRetry}\n >\n <Suspense fallback={<PreviewLoading />}>\n <PluginContent plugin={plugin} file={file} />\n </Suspense>\n </PreviewErrorBoundary>\n </div>\n </div>\n );\n}\n"],"mappings":"AAiDS,cAuDD,YAvDC;AAjDT,SAAS,UAAU,KAAK,SAAS,UAAU,mBAAmB;AAK9D,SAAS,oCAAoC;AAC7C,SAAS,gCAAgC;AACzC,SAAS,6BAA6B;AACtC,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAC/B,OAAO;AAEP,MAAM,+BAA+B,MAAM;AAAA,EACzC,YACS,UACA,YACA,OACP;AACA,UAAM,kCAAkC,UAAU,EAAE;AAJ7C;AACA;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAGA,MAAM,eAAe,oBAAI,QAA8C;AAEvE,SAAS,iBAAiB,QAA8C;AACtE,QAAM,SAAS,aAAa,IAAI,MAAM;AACtC,MAAI,OAAQ,QAAO;AAEnB,QAAM,UAAU,OAAO,KAAK,EAAE,MAAM,CAAC,UAAU;AAC7C,UAAM,IAAI,uBAAuB,OAAO,IAAI,OAAO,MAAM,KAAK;AAAA,EAChE,CAAC;AACD,eAAa,IAAI,QAAQ,OAAO;AAChC,SAAO;AACT;AAEA,SAAS,wBAAwB,QAAuB;AACtD,eAAa,OAAO,MAAM;AAC5B;AAOA,SAAS,cAAc,EAAE,QAAQ,KAAK,GAAuB;AAC3D,QAAM,MAAM,IAAI,iBAAiB,MAAM,CAAC;AACxC,QAAM,YAAY,IAAI;AACtB,SAAO,oBAAC,aAAU,MAAY;AAChC;AAQO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAA+B;AAC7B,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,CAAC;AAE1C,QAAM,gBAAgB,QAAQ,MAAM;AAClC,WAAO,YAAY,6BAA6B;AAAA,EAClD,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,SAAS;AAAA,IACb,MAAM,cAAc,QAAQ,IAAI;AAAA,IAChC,CAAC,eAAe,IAAI;AAAA,EACtB;AAEA,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,QAAQ;AACV,8BAAwB,MAAM;AAAA,IAChC;AACA,gBAAY,CAAC,UAAU,QAAQ,CAAC;AAAA,EAClC,GAAG,CAAC,MAAM,CAAC;AAEX,MAAI,CAAC,QAAQ;AACX,UAAM,UAAU,sBAAsB,KAAK,QAAQ;AAEnD,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,OAAO,QAAQ,WAAW,gBAAgB,qBAAqB;AAAA,QAC/D,aACE,QAAQ,WAAW,gBACf,mBAAmB,KAAK,QAAQ,sDAChC,QAAQ,WAAW,aACjB,QAAQ,QACR,mBAAmB,KAAK,QAAQ,gFAChC,QAAQ,QACR,mBAAmB,KAAK,QAAQ;AAAA;AAAA,IAE1C;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,sBACZ;AAAA,uBACC,qBAAC,SAAI,WAAU,mBACb;AAAA,0BAAC,UAAK,WAAU,0BAAyB,6BAAe;AAAA,MACxD,oBAAC,UAAK,oBAAC;AAAA,MACP,oBAAC,UAAM,iBAAO,MAAK;AAAA,MACnB,oBAAC,UAAK,WAAU,uBAAuB,iBAAO,IAAG;AAAA,OACnD;AAAA,IAGF,oBAAC,SAAI,WAAU,+BACb;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU,OAAO;AAAA,QACjB,YAAY,OAAO;AAAA,QACnB,UAAU,GAAG,KAAK,EAAE,IAAI,OAAO,EAAE,IAAI,QAAQ;AAAA,QAC7C,SAAS;AAAA,QAET,8BAAC,YAAS,UAAU,oBAAC,kBAAe,GAClC,8BAAC,iBAAc,QAAgB,MAAY,GAC7C;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,16 @@
1
+ import * as react from 'react';
2
+ import { PreviewSource } from './core/types.js';
3
+
4
+ interface PptxPreviewProps {
5
+ content?: string | null;
6
+ source?: PreviewSource;
7
+ fileName: string;
8
+ }
9
+ interface PptxRenderHandle {
10
+ goToSlide: (index: number) => void;
11
+ nextSlide: () => void;
12
+ prevSlide: () => void;
13
+ }
14
+ declare function PptxPreview({ content, source, fileName }: PptxPreviewProps): react.JSX.Element;
15
+
16
+ export { PptxPreview, type PptxRenderHandle };