@eternalheart/react-file-preview 1.3.5 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/FilePreviewContent.d.ts +3 -0
- package/lib/FilePreviewContent.d.ts.map +1 -1
- package/lib/FilePreviewEmbed.d.ts +3 -1
- package/lib/FilePreviewEmbed.d.ts.map +1 -1
- package/lib/FilePreviewModal.d.ts +3 -1
- package/lib/FilePreviewModal.d.ts.map +1 -1
- package/lib/ThemeContext.d.ts +18 -0
- package/lib/ThemeContext.d.ts.map +1 -0
- package/lib/chunks/index--zZy1XpK.mjs +299 -0
- package/lib/chunks/index--zZy1XpK.mjs.map +1 -0
- package/lib/chunks/index-B5tBeF0g.mjs +51 -0
- package/lib/chunks/index-B5tBeF0g.mjs.map +1 -0
- package/lib/chunks/index-BEolw_uv.mjs +528 -0
- package/lib/chunks/index-BEolw_uv.mjs.map +1 -0
- package/lib/chunks/index-BIg3vHQf.mjs +110 -0
- package/lib/chunks/index-BIg3vHQf.mjs.map +1 -0
- package/lib/chunks/index-BOEtlHD3.mjs +257 -0
- package/lib/chunks/index-BOEtlHD3.mjs.map +1 -0
- package/lib/chunks/index-BWbuffRN.mjs +152 -0
- package/lib/chunks/index-BWbuffRN.mjs.map +1 -0
- package/lib/chunks/index-BsSx9pGx.mjs +128 -0
- package/lib/chunks/index-BsSx9pGx.mjs.map +1 -0
- package/lib/chunks/index-CQABwGVP.mjs +99 -0
- package/lib/chunks/index-CQABwGVP.mjs.map +1 -0
- package/lib/chunks/index-DOMMMe9f.mjs +96 -0
- package/lib/chunks/index-DOMMMe9f.mjs.map +1 -0
- package/lib/chunks/index-DTH1IvOG.mjs +239 -0
- package/lib/chunks/index-DTH1IvOG.mjs.map +1 -0
- package/lib/chunks/index-D_8IHm6o.mjs +98 -0
- package/lib/chunks/index-D_8IHm6o.mjs.map +1 -0
- package/lib/chunks/index-DinKO2op.mjs +116 -0
- package/lib/chunks/index-DinKO2op.mjs.map +1 -0
- package/lib/chunks/index-LNXbKjrI.mjs +1900 -0
- package/lib/chunks/index-LNXbKjrI.mjs.map +1 -0
- package/lib/chunks/index-Yp36heK8.mjs +193 -0
- package/lib/chunks/index-Yp36heK8.mjs.map +1 -0
- package/lib/chunks/index-qxvk-6P6.mjs +172 -0
- package/lib/chunks/index-qxvk-6P6.mjs.map +1 -0
- package/lib/chunks/index-w0tt7Myw.mjs +96 -0
- package/lib/chunks/index-w0tt7Myw.mjs.map +1 -0
- package/lib/chunks/index-xTq9b3vw.mjs +52 -0
- package/lib/chunks/index-xTq9b3vw.mjs.map +1 -0
- package/lib/chunks/index-zDEwNk3h.mjs +103 -0
- package/lib/chunks/index-zDEwNk3h.mjs.map +1 -0
- package/lib/chunks/useShikiHighlight-DtWg9b8y.mjs +23 -0
- package/lib/chunks/useShikiHighlight-DtWg9b8y.mjs.map +1 -0
- package/lib/chunks/xspreadsheet-CyBXARuf.mjs +5215 -0
- package/lib/chunks/xspreadsheet-CyBXARuf.mjs.map +1 -0
- package/lib/hooks/useShikiHighlight.d.ts +18 -0
- package/lib/hooks/useShikiHighlight.d.ts.map +1 -0
- package/lib/index.cjs +28 -508
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.mjs +13 -59140
- package/lib/index.mjs.map +1 -1
- package/lib/renderers/Audio/index.d.ts.map +1 -1
- package/lib/renderers/Json/index.d.ts.map +1 -1
- package/lib/renderers/Markdown/index.d.ts.map +1 -1
- package/lib/renderers/RendererLoading.d.ts +3 -0
- package/lib/renderers/RendererLoading.d.ts.map +1 -0
- package/lib/renderers/Text/index.d.ts.map +1 -1
- package/lib/renderers/Xml/index.d.ts.map +1 -1
- package/lib/renderers/lazy.d.ts +38 -0
- package/lib/renderers/lazy.d.ts.map +1 -0
- package/lib/renderers/toolbar.types.d.ts.map +1 -1
- package/lib/types.d.ts +27 -3
- package/lib/types.d.ts.map +1 -1
- package/package.json +4 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Yp36heK8.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';\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 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 if (!host) 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 fetch(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 console.error('MOBI/AZW3 加载错误:', 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-[#f5f5f0] rfp-overflow-hidden\">\n {error && (\n <div className=\"rfp-absolute rfp-inset-0 rfp-flex rfp-items-center rfp-justify-center rfp-text-fg-secondary rfp-text-center rfp-p-6\">\n <p className=\"rfp-text-lg\">{error}</p>\n </div>\n )}\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","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","X"],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { jsx as r, jsxs as d } from "react/jsx-runtime";
|
|
2
|
+
import { useState as l, useEffect as S, useCallback as _ } from "react";
|
|
3
|
+
import z from "react-markdown";
|
|
4
|
+
import H from "remark-gfm";
|
|
5
|
+
import M from "remark-math";
|
|
6
|
+
import j from "rehype-katex";
|
|
7
|
+
import T from "rehype-raw";
|
|
8
|
+
import { Check as y, Copy as N } from "lucide-react";
|
|
9
|
+
import { u as c, h as B } from "./index-LNXbKjrI.mjs";
|
|
10
|
+
import { u as w } from "./useShikiHighlight-DtWg9b8y.mjs";
|
|
11
|
+
import "katex/dist/katex.min.css";
|
|
12
|
+
const v = (t) => {
|
|
13
|
+
const [p, f] = l(!1), a = _(async () => {
|
|
14
|
+
try {
|
|
15
|
+
await navigator.clipboard.writeText(t);
|
|
16
|
+
} catch {
|
|
17
|
+
const s = document.createElement("textarea");
|
|
18
|
+
s.value = t, document.body.appendChild(s), s.select(), document.execCommand("copy"), document.body.removeChild(s);
|
|
19
|
+
}
|
|
20
|
+
f(!0), setTimeout(() => f(!1), 2e3);
|
|
21
|
+
}, [t]);
|
|
22
|
+
return { copied: p, handleCopy: a };
|
|
23
|
+
}, E = ({ text: t }) => {
|
|
24
|
+
const p = c(), { copied: f, handleCopy: a } = v(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: p(f ? "markdown.copied" : "markdown.copy_code"),
|
|
31
|
+
children: f ? /* @__PURE__ */ r(y, { size: 13 }) : /* @__PURE__ */ r(N, { size: 13 })
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}, I = ({ text: t }) => {
|
|
35
|
+
const p = c(), { copied: f, handleCopy: a } = v(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: p(f ? "markdown.copied" : "markdown.copy_code"),
|
|
42
|
+
children: f ? /* @__PURE__ */ r(y, { size: 14 }) : /* @__PURE__ */ r(N, { size: 14 })
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}, L = ({ code: t, lang: p }) => {
|
|
46
|
+
const { html: f } = w(t, p);
|
|
47
|
+
return /* @__PURE__ */ d("div", { className: "rfp-relative rfp-group rfp-my-4", children: [
|
|
48
|
+
/* @__PURE__ */ d("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: p }),
|
|
50
|
+
/* @__PURE__ */ r(E, { text: t })
|
|
51
|
+
] }),
|
|
52
|
+
f ? /* @__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: f }
|
|
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
|
+
}, Q = ({ url: t, viewMode: p = "preview" }) => {
|
|
68
|
+
const f = c(), [a, s] = l(""), [k, m] = l(!0), [u, h] = l(null), { html: x } = w(
|
|
69
|
+
p === "source" ? a : "",
|
|
70
|
+
"markdown"
|
|
71
|
+
);
|
|
72
|
+
return S(() => {
|
|
73
|
+
(async () => {
|
|
74
|
+
try {
|
|
75
|
+
m(!0), h(null);
|
|
76
|
+
const o = await B(t);
|
|
77
|
+
s(o);
|
|
78
|
+
} catch (o) {
|
|
79
|
+
h(f("markdown.load_failed")), console.error(o);
|
|
80
|
+
} finally {
|
|
81
|
+
m(!1);
|
|
82
|
+
}
|
|
83
|
+
})();
|
|
84
|
+
}, [t]), k ? /* @__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" }) }) : u ? /* @__PURE__ */ r("div", { className: "rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full", children: /* @__PURE__ */ r("div", { className: "rfp-text-fg-secondary rfp-text-center", children: /* @__PURE__ */ r("p", { className: "rfp-text-lg", children: u }) }) }) : p === "source" ? /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg", children: x ? /* @__PURE__ */ r(
|
|
85
|
+
"div",
|
|
86
|
+
{
|
|
87
|
+
className: "rfp-shiki-wrapper with-line-numbers",
|
|
88
|
+
dangerouslySetInnerHTML: { __html: x }
|
|
89
|
+
}
|
|
90
|
+
) : /* @__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: a }) }) : /* @__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(
|
|
91
|
+
z,
|
|
92
|
+
{
|
|
93
|
+
remarkPlugins: [H, M],
|
|
94
|
+
rehypePlugins: [T, j],
|
|
95
|
+
components: {
|
|
96
|
+
code({ node: e, inline: o, className: n, children: i, ...C }) {
|
|
97
|
+
const b = /language-(\w+)/.exec(n || ""), g = String(i).replace(/\n$/, "");
|
|
98
|
+
return !o && b ? /* @__PURE__ */ r(L, { code: g, lang: b[1] }) : o ? /* @__PURE__ */ r(
|
|
99
|
+
"code",
|
|
100
|
+
{
|
|
101
|
+
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",
|
|
102
|
+
...C,
|
|
103
|
+
children: i
|
|
104
|
+
}
|
|
105
|
+
) : /* @__PURE__ */ d("div", { className: "rfp-relative rfp-group rfp-my-4", children: [
|
|
106
|
+
/* @__PURE__ */ r(I, { text: g }),
|
|
107
|
+
/* @__PURE__ */ r(
|
|
108
|
+
"pre",
|
|
109
|
+
{
|
|
110
|
+
className: "rfp-m-0 rfp-rounded-md rfp-border rfp-border-line-weak rfp-overflow-x-auto rfp-p-4 rfp-bg-code-bg",
|
|
111
|
+
style: { fontSize: "13px", lineHeight: "1.5" },
|
|
112
|
+
children: /* @__PURE__ */ r("code", { className: "rfp-font-mono rfp-text-code-fg rfp-text-sm", children: i })
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
] });
|
|
116
|
+
},
|
|
117
|
+
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 }),
|
|
118
|
+
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 }),
|
|
119
|
+
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 }),
|
|
120
|
+
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 }),
|
|
121
|
+
p: ({ children: e }) => /* @__PURE__ */ r("p", { className: "rfp-text-fg-secondary rfp-mb-4 rfp-leading-7 rfp-text-base", children: e }),
|
|
122
|
+
a: ({ href: e, children: o }) => /* @__PURE__ */ r(
|
|
123
|
+
"a",
|
|
124
|
+
{
|
|
125
|
+
href: e,
|
|
126
|
+
className: "rfp-text-indigo-400 hover:rfp-text-indigo-300 rfp-underline rfp-decoration-indigo-600 hover:rfp-decoration-indigo-400",
|
|
127
|
+
target: "_blank",
|
|
128
|
+
rel: "noopener noreferrer",
|
|
129
|
+
children: o
|
|
130
|
+
}
|
|
131
|
+
),
|
|
132
|
+
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 }),
|
|
133
|
+
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 }),
|
|
134
|
+
li: ({ children: e }) => /* @__PURE__ */ r("li", { className: "rfp-leading-7", children: e }),
|
|
135
|
+
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 }),
|
|
136
|
+
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 }) }),
|
|
137
|
+
thead: ({ children: e }) => /* @__PURE__ */ r("thead", { className: "rfp-bg-surface-1", children: e }),
|
|
138
|
+
tbody: ({ children: e }) => /* @__PURE__ */ r("tbody", { className: "rfp-divide-y rfp-divide-divide rfp-bg-transparent", children: e }),
|
|
139
|
+
tr: ({ children: e }) => /* @__PURE__ */ r("tr", { className: "hover:rfp-bg-surface-1 rfp-transition-colors", children: e }),
|
|
140
|
+
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 }),
|
|
141
|
+
td: ({ children: e }) => /* @__PURE__ */ r("td", { className: "rfp-px-4 rfp-py-3 rfp-text-sm rfp-text-fg-secondary", children: e }),
|
|
142
|
+
hr: () => /* @__PURE__ */ r("hr", { className: "rfp-border-line rfp-my-6" }),
|
|
143
|
+
img: ({ src: e, alt: o }) => /* @__PURE__ */ r(
|
|
144
|
+
"img",
|
|
145
|
+
{
|
|
146
|
+
src: e,
|
|
147
|
+
alt: o,
|
|
148
|
+
className: "rfp-rounded-md rfp-max-w-full rfp-h-auto rfp-my-4 rfp-mx-auto rfp-block rfp-shadow-sm"
|
|
149
|
+
}
|
|
150
|
+
),
|
|
151
|
+
input: ({ type: e, checked: o, ...n }) => e === "checkbox" ? /* @__PURE__ */ r(
|
|
152
|
+
"input",
|
|
153
|
+
{
|
|
154
|
+
type: "checkbox",
|
|
155
|
+
checked: o,
|
|
156
|
+
readOnly: !0,
|
|
157
|
+
className: "rfp-mr-2 rfp-rounded rfp-border-line",
|
|
158
|
+
...n
|
|
159
|
+
}
|
|
160
|
+
) : /* @__PURE__ */ r("input", { type: e, ...n }),
|
|
161
|
+
strong: ({ children: e }) => /* @__PURE__ */ r("strong", { className: "rfp-font-semibold rfp-text-fg-primary", children: e }),
|
|
162
|
+
em: ({ children: e }) => /* @__PURE__ */ r("em", { className: "rfp-italic", children: e }),
|
|
163
|
+
del: ({ children: e }) => /* @__PURE__ */ r("del", { className: "rfp-text-fg-muted rfp-line-through", children: e })
|
|
164
|
+
},
|
|
165
|
+
children: a
|
|
166
|
+
}
|
|
167
|
+
) }) });
|
|
168
|
+
};
|
|
169
|
+
export {
|
|
170
|
+
Q as MarkdownRenderer
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=index-qxvk-6P6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-qxvk-6P6.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 { useShikiHighlight } from '../../hooks/useShikiHighlight';\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 [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 loadMarkdown = async () => {\n try {\n setLoading(true);\n setError(null);\n const text = await fetchTextUtf8(url);\n setContent(text);\n } catch (err) {\n setError(t('markdown.load_failed'));\n console.error(err);\n } finally {\n setLoading(false);\n }\n };\n\n loadMarkdown();\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 (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-text-fg-secondary rfp-text-center\">\n <p className=\"rfp-text-lg\">{error}</p>\n </div>\n </div>\n );\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 if (!inline && match) {\n // 有语言标注的代码块 - shiki 高亮\n return <ShikiCodeBlock code={codeString} lang={match[1]} />;\n }\n if (!inline) {\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 // 行内代码\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 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","content","setContent","loading","setLoading","error","setError","sourceHtml","useEffect","fetchTextUtf8","err","ReactMarkdown","remarkGfm","remarkMath","rehypeRaw","rehypeKatex","node","inline","className","children","props","match","codeString","href","src","alt","type","checked"],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsxs as m, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useState as N, useRef as w, useEffect as R, useCallback as L } from "react";
|
|
3
|
+
import { Document as H, Page as M } from "react-pdf";
|
|
4
|
+
import { u as P } from "./index-LNXbKjrI.mjs";
|
|
5
|
+
const q = ({
|
|
6
|
+
url: u,
|
|
7
|
+
zoom: x,
|
|
8
|
+
currentPage: s,
|
|
9
|
+
onPageChange: o,
|
|
10
|
+
onTotalPagesChange: E,
|
|
11
|
+
onPageWidthChange: h
|
|
12
|
+
}) => {
|
|
13
|
+
const k = P(), [c, D] = N(0), [l, b] = N(null), p = w(null), a = w(/* @__PURE__ */ new Map());
|
|
14
|
+
R(() => {
|
|
15
|
+
b(null);
|
|
16
|
+
}, [u]);
|
|
17
|
+
const S = ({ numPages: r }) => {
|
|
18
|
+
D(r), E(r), o(1);
|
|
19
|
+
}, j = (r) => {
|
|
20
|
+
console.error("PDF 加载错误:", r), b(k("pdf.load_failed"));
|
|
21
|
+
}, i = L(() => {
|
|
22
|
+
if (!p.current) return;
|
|
23
|
+
const r = p.current, t = r.scrollTop, f = r.clientHeight, n = t + f / 2;
|
|
24
|
+
let d = 1, g = 1 / 0;
|
|
25
|
+
a.current.forEach((A, _) => {
|
|
26
|
+
const v = A.getBoundingClientRect(), B = r.getBoundingClientRect(), C = v.top - B.top + v.height / 2 + t, y = Math.abs(C - n);
|
|
27
|
+
y < g && (g = y, d = _);
|
|
28
|
+
}), d !== s && o(d);
|
|
29
|
+
}, [s, o]);
|
|
30
|
+
R(() => {
|
|
31
|
+
const r = p.current;
|
|
32
|
+
if (r)
|
|
33
|
+
return r.addEventListener("scroll", i), () => r.removeEventListener("scroll", i);
|
|
34
|
+
}, [i]);
|
|
35
|
+
const T = L((r, t) => {
|
|
36
|
+
t ? a.current.set(r, t) : a.current.delete(r);
|
|
37
|
+
}, []);
|
|
38
|
+
return /* @__PURE__ */ m(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
ref: p,
|
|
42
|
+
className: "rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full rfp-overflow-auto rfp-py-4 md:rfp-py-8 rfp-px-2 md:rfp-px-4",
|
|
43
|
+
children: [
|
|
44
|
+
l && /* @__PURE__ */ e("div", { className: "rfp-text-fg-secondary rfp-text-center", children: /* @__PURE__ */ e("p", { className: "rfp-text-lg", children: l }) }),
|
|
45
|
+
!l && /* @__PURE__ */ e(
|
|
46
|
+
H,
|
|
47
|
+
{
|
|
48
|
+
file: u,
|
|
49
|
+
onLoadSuccess: S,
|
|
50
|
+
onLoadError: j,
|
|
51
|
+
loading: /* @__PURE__ */ e("div", { className: "rfp-flex rfp-items-center rfp-justify-center rfp-min-h-screen", children: /* @__PURE__ */ e("div", { className: "rfp-w-12 rfp-h-12 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin" }) }),
|
|
52
|
+
children: /* @__PURE__ */ e("div", { className: "rfp-flex rfp-flex-col rfp-gap-4", children: Array.from(new Array(c), (r, t) => {
|
|
53
|
+
const f = t + 1;
|
|
54
|
+
return /* @__PURE__ */ m(
|
|
55
|
+
"div",
|
|
56
|
+
{
|
|
57
|
+
ref: (n) => T(f, n),
|
|
58
|
+
className: "rfp-relative",
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ e(
|
|
61
|
+
M,
|
|
62
|
+
{
|
|
63
|
+
pageNumber: f,
|
|
64
|
+
scale: x,
|
|
65
|
+
loading: /* @__PURE__ */ e("div", { className: "rfp-flex rfp-items-center rfp-justify-center rfp-p-8 rfp-bg-surface-1 rfp-rounded-lg rfp-min-h-[600px]", children: /* @__PURE__ */ e("div", { className: "rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin" }) }),
|
|
66
|
+
renderTextLayer: !1,
|
|
67
|
+
renderAnnotationLayer: !1,
|
|
68
|
+
className: "rfp-shadow-2xl",
|
|
69
|
+
onRenderSuccess: (n) => {
|
|
70
|
+
f === 1 && h && h(n.originalWidth || n.width / x);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
),
|
|
74
|
+
/* @__PURE__ */ e("div", { className: "rfp-absolute rfp-top-2 rfp-right-2 rfp-bg-surface-nav-hover rfp-backdrop-blur-sm rfp-text-fg-primary rfp-text-xs rfp-px-3 rfp-py-1 rfp-rounded-full", children: f })
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
`page_${f}`
|
|
78
|
+
);
|
|
79
|
+
}) })
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
c > 0 && /* @__PURE__ */ m("div", { className: "rfp-sticky rfp-bottom-2 md:rfp-bottom-4 rfp-mt-4 md:rfp-mt-8 rfp-bg-surface-nav-hover rfp-backdrop-blur-xl rfp-text-fg-primary rfp-px-4 rfp-py-2 md:rfp-px-6 md:rfp-py-3 rfp-rounded-full rfp-text-xs md:rfp-text-sm rfp-font-medium rfp-shadow-2xl rfp-border rfp-border-line-weak", children: [
|
|
83
|
+
"第 ",
|
|
84
|
+
s,
|
|
85
|
+
" 页 / 共 ",
|
|
86
|
+
c,
|
|
87
|
+
" 页"
|
|
88
|
+
] })
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
export {
|
|
94
|
+
q as PdfRenderer
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=index-w0tt7Myw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-w0tt7Myw.mjs","sources":["../../src/renderers/Pdf/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport { Document, Page } from 'react-pdf';\nimport { useTranslator } from '../../i18n/LocaleContext';\n\n// 导入 PDF.js 配置\nimport '../../utils/pdfConfig';\n\ninterface PdfRendererProps {\n url: string;\n zoom: number;\n currentPage: number;\n onPageChange: (page: number) => void;\n onTotalPagesChange: (total: number) => void;\n onPageWidthChange?: (width: number) => void;\n}\n\nexport const PdfRenderer: React.FC<PdfRendererProps> = ({\n url,\n zoom,\n currentPage,\n onPageChange,\n onTotalPagesChange,\n onPageWidthChange,\n}) => {\n const t = useTranslator();\n const [numPages, setNumPages] = useState<number>(0);\n const [error, setError] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const pageRefs = useRef<Map<number, HTMLDivElement>>(new Map());\n\n useEffect(() => {\n setError(null);\n }, [url]);\n\n const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {\n setNumPages(numPages);\n onTotalPagesChange(numPages);\n onPageChange(1);\n };\n\n const onDocumentLoadError = (error: Error) => {\n console.error('PDF 加载错误:', error);\n setError(t('pdf.load_failed'));\n };\n\n // 滚动时更新当前页码\n const handleScroll = useCallback(() => {\n if (!containerRef.current) return;\n\n const container = containerRef.current;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const scrollCenter = scrollTop + containerHeight / 2;\n\n // 找到当前可见的页面\n let currentVisiblePage = 1;\n let minDistance = Infinity;\n\n pageRefs.current.forEach((pageElement, pageNumber) => {\n const rect = pageElement.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const pageCenter = rect.top - containerRect.top + rect.height / 2 + scrollTop;\n const distance = Math.abs(pageCenter - scrollCenter);\n\n if (distance < minDistance) {\n minDistance = distance;\n currentVisiblePage = pageNumber;\n }\n });\n\n if (currentVisiblePage !== currentPage) {\n onPageChange(currentVisiblePage);\n }\n }, [currentPage, onPageChange]);\n\n // 监听滚动事件\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener('scroll', handleScroll);\n return () => container.removeEventListener('scroll', handleScroll);\n }, [handleScroll]);\n\n // 设置页面引用\n const setPageRef = useCallback((pageNumber: number, element: HTMLDivElement | null) => {\n if (element) {\n pageRefs.current.set(pageNumber, element);\n } else {\n pageRefs.current.delete(pageNumber);\n }\n }, []);\n\n return (\n <div\n ref={containerRef}\n className=\"rfp-flex rfp-flex-col rfp-items-center rfp-w-full rfp-h-full rfp-overflow-auto rfp-py-4 md:rfp-py-8 rfp-px-2 md:rfp-px-4\"\n >\n {error && (\n <div className=\"rfp-text-fg-secondary rfp-text-center\">\n <p className=\"rfp-text-lg\">{error}</p>\n </div>\n )}\n\n {!error && (\n <Document\n file={url}\n onLoadSuccess={onDocumentLoadSuccess}\n onLoadError={onDocumentLoadError}\n loading={\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-min-h-screen\">\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 <div className=\"rfp-flex rfp-flex-col rfp-gap-4\">\n {Array.from(new Array(numPages), (_, index) => {\n const pageNumber = index + 1;\n return (\n <div\n key={`page_${pageNumber}`}\n ref={(el) => setPageRef(pageNumber, el)}\n className=\"rfp-relative\"\n >\n <Page\n pageNumber={pageNumber}\n scale={zoom}\n loading={\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-p-8 rfp-bg-surface-1 rfp-rounded-lg rfp-min-h-[600px]\">\n <div className=\"rfp-w-8 rfp-h-8 rfp-border-4 rfp-border-line-strong rfp-border-t-spinner-head rfp-rounded-full rfp-animate-spin\" />\n </div>\n }\n renderTextLayer={false}\n renderAnnotationLayer={false}\n className=\"rfp-shadow-2xl\"\n onRenderSuccess={(page) => {\n if (pageNumber === 1 && onPageWidthChange) {\n // 上报 scale=1 时的原始页面宽度\n onPageWidthChange(page.originalWidth || page.width / zoom);\n }\n }}\n />\n {/* 页码标签 */}\n <div className=\"rfp-absolute rfp-top-2 rfp-right-2 rfp-bg-surface-nav-hover rfp-backdrop-blur-sm rfp-text-fg-primary rfp-text-xs rfp-px-3 rfp-py-1 rfp-rounded-full\">\n {pageNumber}\n </div>\n </div>\n );\n })}\n </div>\n </Document>\n )}\n\n {/* 底部页码指示器 */}\n {numPages > 0 && (\n <div className=\"rfp-sticky rfp-bottom-2 md:rfp-bottom-4 rfp-mt-4 md:rfp-mt-8 rfp-bg-surface-nav-hover rfp-backdrop-blur-xl rfp-text-fg-primary rfp-px-4 rfp-py-2 md:rfp-px-6 md:rfp-py-3 rfp-rounded-full rfp-text-xs md:rfp-text-sm rfp-font-medium rfp-shadow-2xl rfp-border rfp-border-line-weak\">\n 第 {currentPage} 页 / 共 {numPages} 页\n </div>\n )}\n </div>\n );\n};\n"],"names":["PdfRenderer","url","zoom","currentPage","onPageChange","onTotalPagesChange","onPageWidthChange","t","useTranslator","numPages","setNumPages","useState","error","setError","containerRef","useRef","pageRefs","useEffect","onDocumentLoadSuccess","onDocumentLoadError","handleScroll","useCallback","container","scrollTop","containerHeight","scrollCenter","currentVisiblePage","minDistance","pageElement","pageNumber","rect","containerRect","pageCenter","distance","setPageRef","element","jsxs","jsx","Document","_","index","el","Page","page"],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { useState as l, useEffect as g } from "react";
|
|
3
|
+
import { u as x, e as w, h as b } from "./index-LNXbKjrI.mjs";
|
|
4
|
+
import { u as N } from "./useShikiHighlight-DtWg9b8y.mjs";
|
|
5
|
+
const S = ({
|
|
6
|
+
url: s,
|
|
7
|
+
fileName: a,
|
|
8
|
+
wordWrap: n = !0,
|
|
9
|
+
htmlPreview: d = !1
|
|
10
|
+
}) => {
|
|
11
|
+
const u = x(), [t, h] = l(""), [m, p] = l(!0), [o, i] = l(null), e = w(a), { html: c } = N(
|
|
12
|
+
e !== "text" ? t : "",
|
|
13
|
+
e
|
|
14
|
+
);
|
|
15
|
+
return g(() => {
|
|
16
|
+
(async () => {
|
|
17
|
+
try {
|
|
18
|
+
p(!0), i(null);
|
|
19
|
+
const f = await b(s);
|
|
20
|
+
h(f);
|
|
21
|
+
} catch (f) {
|
|
22
|
+
i(u("text.load_failed")), console.error(f);
|
|
23
|
+
} finally {
|
|
24
|
+
p(!1);
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
}, [s]), m ? /* @__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" }) }) : o ? /* @__PURE__ */ r("div", { className: "rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full", children: /* @__PURE__ */ r("div", { className: "rfp-text-fg-secondary rfp-text-center", children: /* @__PURE__ */ r("p", { className: "rfp-text-lg", children: o }) }) }) : d && e === "html" ? /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-bg-surface-toolbar", children: /* @__PURE__ */ r(
|
|
28
|
+
"iframe",
|
|
29
|
+
{
|
|
30
|
+
srcDoc: t,
|
|
31
|
+
sandbox: "allow-same-origin",
|
|
32
|
+
className: "rfp-w-full rfp-h-full rfp-border-0",
|
|
33
|
+
title: a
|
|
34
|
+
}
|
|
35
|
+
) }) : /* @__PURE__ */ r("div", { className: "rfp-w-full rfp-h-full rfp-overflow-auto rfp-bg-code-bg", children: e === "text" || !c ? /* @__PURE__ */ r(
|
|
36
|
+
"pre",
|
|
37
|
+
{
|
|
38
|
+
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"}`,
|
|
39
|
+
children: t
|
|
40
|
+
}
|
|
41
|
+
) : /* @__PURE__ */ r(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
className: `rfp-shiki-wrapper with-line-numbers ${n ? "" : "no-wrap"}`,
|
|
45
|
+
dangerouslySetInnerHTML: { __html: c }
|
|
46
|
+
}
|
|
47
|
+
) });
|
|
48
|
+
};
|
|
49
|
+
export {
|
|
50
|
+
S as TextRenderer
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=index-xTq9b3vw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-xTq9b3vw.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 { useShikiHighlight } from '../../hooks/useShikiHighlight';\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 [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 loadText = async () => {\n try {\n setLoading(true);\n setError(null);\n const text = await fetchTextUtf8(url);\n setContent(text);\n } catch (err) {\n setError(t('text.load_failed'));\n console.error(err);\n } finally {\n setLoading(false);\n }\n };\n\n loadText();\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 (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-text-fg-secondary rfp-text-center\">\n <p className=\"rfp-text-lg\">{error}</p>\n </div>\n </div>\n );\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","content","setContent","useState","loading","setLoading","error","setError","language","getLanguageFromFileName","highlighted","useShikiHighlight","useEffect","text","fetchTextUtf8","err","jsx"],"mappings":";;;;AAYO,MAAMA,IAA4C,CAAC;AAAA,EACxD,KAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAChB,MAAM;AACJ,QAAMC,IAAIC,EAAA,GACJ,CAACC,GAASC,CAAU,IAAIC,EAAiB,EAAE,GAC3C,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI,GACrC,CAACG,GAAOC,CAAQ,IAAIJ,EAAwB,IAAI,GAChDK,IAAWC,EAAwBb,CAAQ,GAC3C,EAAE,MAAMc,EAAA,IAAgBC;AAAA,IAC5BH,MAAa,SAASP,IAAU;AAAA,IAChCO;AAAA,EAAA;AAqBF,SAlBAI,EAAU,MAAM;AAed,KAdiB,YAAY;AAC3B,UAAI;AACF,QAAAP,EAAW,EAAI,GACfE,EAAS,IAAI;AACb,cAAMM,IAAO,MAAMC,EAAcnB,CAAG;AACpC,QAAAO,EAAWW,CAAI;AAAA,MACjB,SAASE,GAAK;AACZ,QAAAR,EAASR,EAAE,kBAAkB,CAAC,GAC9B,QAAQ,MAAMgB,CAAG;AAAA,MACnB,UAAA;AACE,QAAAV,EAAW,EAAK;AAAA,MAClB;AAAA,IACF,GAEA;AAAA,EACF,GAAG,CAACV,CAAG,CAAC,GAEJS,IAEA,gBAAAY,EAAC,SAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,qHAAoH,EAAA,CACrI,IAIAV,IAEA,gBAAAU,EAAC,OAAA,EAAI,WAAU,sEACb,4BAAC,OAAA,EAAI,WAAU,yCACb,UAAA,gBAAAA,EAAC,KAAA,EAAE,WAAU,eAAe,UAAAV,EAAA,CAAM,GACpC,GACF,IAKAR,KAAgBU,MAAa,SAE7B,gBAAAQ,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA,gBAAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,QAAQf;AAAA,MACR,SAAQ;AAAA,MACR,WAAU;AAAA,MACV,OAAOL;AAAA,IAAA;AAAA,EAAA,GAEX,sBAMD,OAAA,EAAI,WAAU,0DACZ,UAAAY,MAAa,UAAU,CAACE,IACvB,gBAAAM;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,yDACTnB,IAAW,4CAA4C,oBACzD;AAAA,MAEC,UAAAI;AAAA,IAAA;AAAA,EAAA,IAGH,gBAAAe;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,uCAAuCnB,IAAW,KAAK,SAAS;AAAA,MAC3E,yBAAyB,EAAE,QAAQa,EAAA;AAAA,IAAY;AAAA,EAAA,GAGrD;AAEJ;"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as r, jsxs as N } from "react/jsx-runtime";
|
|
2
|
+
import { useState as f, useRef as A, useEffect as y, useCallback as G } from "react";
|
|
3
|
+
import P from "mammoth";
|
|
4
|
+
import { u as T } from "./index-LNXbKjrI.mjs";
|
|
5
|
+
const _ = 1123, E = 60, v = 50, D = _ - E * 2, I = 24, w = {
|
|
6
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
7
|
+
lineHeight: "1.8",
|
|
8
|
+
color: "#333"
|
|
9
|
+
}, L = ({ url: a }) => {
|
|
10
|
+
const H = T(), [n, c] = f(""), [b, p] = f(!0), [d, u] = f(null), [h, m] = f([]), i = A(null);
|
|
11
|
+
y(() => {
|
|
12
|
+
(async () => {
|
|
13
|
+
p(!0), u(null), c("");
|
|
14
|
+
try {
|
|
15
|
+
const e = await fetch(a);
|
|
16
|
+
if (!e.ok)
|
|
17
|
+
throw new Error("文件加载失败");
|
|
18
|
+
const t = await e.arrayBuffer(), s = await P.convertToHtml({ arrayBuffer: t });
|
|
19
|
+
c(s.value);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error("Docx 解析错误:", e), u(H("docx.parse_failed"));
|
|
22
|
+
} finally {
|
|
23
|
+
p(!1);
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
}, [a]);
|
|
27
|
+
const x = G(() => {
|
|
28
|
+
const o = i.current;
|
|
29
|
+
if (!o || !n) return;
|
|
30
|
+
const e = Array.from(o.children);
|
|
31
|
+
if (e.length === 0) {
|
|
32
|
+
m([n]);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const t = [[]];
|
|
36
|
+
let s = 0;
|
|
37
|
+
for (const l of e) {
|
|
38
|
+
const g = l.offsetHeight;
|
|
39
|
+
s > 0 && s + g > D && (t.push([]), s = 0), t[t.length - 1].push(l.outerHTML), s += g;
|
|
40
|
+
}
|
|
41
|
+
t.length === 0 && t.push([]), m(t.map((l) => l.join("")));
|
|
42
|
+
}, [n]);
|
|
43
|
+
return y(() => {
|
|
44
|
+
!n || !i.current || requestAnimationFrame(() => {
|
|
45
|
+
x();
|
|
46
|
+
});
|
|
47
|
+
}, [n, x]), b ? /* @__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" }) }) : d ? /* @__PURE__ */ r("div", { className: "rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full", children: /* @__PURE__ */ r("div", { className: "rfp-text-fg-secondary rfp-text-center", children: /* @__PURE__ */ r("p", { className: "rfp-text-lg", children: d }) }) }) : /* @__PURE__ */ N(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
className: "rfp-w-full rfp-h-full rfp-overflow-auto",
|
|
51
|
+
style: { background: "rgba(0, 0, 0, 0.15)" },
|
|
52
|
+
children: [
|
|
53
|
+
/* @__PURE__ */ r(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
ref: i,
|
|
57
|
+
dangerouslySetInnerHTML: { __html: n },
|
|
58
|
+
style: {
|
|
59
|
+
...w,
|
|
60
|
+
position: "absolute",
|
|
61
|
+
visibility: "hidden",
|
|
62
|
+
width: `${794 - v * 2}px`,
|
|
63
|
+
pointerEvents: "none"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
/* @__PURE__ */ r(
|
|
68
|
+
"div",
|
|
69
|
+
{
|
|
70
|
+
className: "rfp-py-6 md:rfp-py-10 rfp-flex rfp-flex-col rfp-items-center",
|
|
71
|
+
style: { gap: `${I}px` },
|
|
72
|
+
children: (h.length > 0 ? h : [""]).map((o, e) => /* @__PURE__ */ r(
|
|
73
|
+
"div",
|
|
74
|
+
{
|
|
75
|
+
style: {
|
|
76
|
+
width: "100%",
|
|
77
|
+
maxWidth: "794px",
|
|
78
|
+
minHeight: `${_}px`,
|
|
79
|
+
background: "white",
|
|
80
|
+
boxShadow: "0 2px 12px rgba(0, 0, 0, 0.15)",
|
|
81
|
+
flexShrink: 0,
|
|
82
|
+
padding: `${E}px ${v}px`
|
|
83
|
+
},
|
|
84
|
+
children: /* @__PURE__ */ r(
|
|
85
|
+
"div",
|
|
86
|
+
{
|
|
87
|
+
dangerouslySetInnerHTML: { __html: o },
|
|
88
|
+
style: w
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
},
|
|
92
|
+
e
|
|
93
|
+
))
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
export {
|
|
101
|
+
L as DocxRenderer
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=index-zDEwNk3h.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-zDEwNk3h.mjs","sources":["../../src/renderers/Docx/index.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport mammoth from 'mammoth';\nimport { useTranslator } from '../../i18n/LocaleContext';\n\ninterface DocxRendererProps {\n url: string;\n}\n\n// A4 page dimensions (96dpi)\nconst PAGE_HEIGHT = 1123;\nconst PAGE_PADDING_Y = 60;\nconst PAGE_PADDING_X = 50;\nconst PAGE_CONTENT_HEIGHT = PAGE_HEIGHT - PAGE_PADDING_Y * 2;\nconst PAGE_GAP = 24;\n\nconst contentStyle: React.CSSProperties = {\n fontFamily: 'system-ui, -apple-system, sans-serif',\n lineHeight: '1.8',\n color: '#333',\n};\n\nexport const DocxRenderer: React.FC<DocxRendererProps> = ({ url }) => {\n const t = useTranslator();\n const [html, setHtml] = useState<string>('');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [pages, setPages] = useState<string[]>([]);\n const measureRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const loadDocx = async () => {\n setLoading(true);\n setError(null);\n setHtml('');\n\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error('文件加载失败');\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const result = await mammoth.convertToHtml({ arrayBuffer });\n setHtml(result.value);\n } catch (err) {\n console.error('Docx 解析错误:', err);\n setError(t('docx.parse_failed'));\n } finally {\n setLoading(false);\n }\n };\n\n loadDocx();\n }, [url]);\n\n const paginate = useCallback(() => {\n const container = measureRef.current;\n if (!container || !html) return;\n\n const children = Array.from(container.children) as HTMLElement[];\n if (children.length === 0) {\n setPages([html]);\n return;\n }\n\n const result: string[][] = [[]];\n let currentPageUsed = 0;\n\n for (const child of children) {\n const h = child.offsetHeight;\n\n // If adding this block would exceed page content area and page isn't empty,\n // start a new page\n if (currentPageUsed > 0 && currentPageUsed + h > PAGE_CONTENT_HEIGHT) {\n result.push([]);\n currentPageUsed = 0;\n }\n\n result[result.length - 1].push(child.outerHTML);\n currentPageUsed += h;\n }\n\n // At least one page\n if (result.length === 0) result.push([]);\n\n setPages(result.map(blocks => blocks.join('')));\n }, [html]);\n\n useEffect(() => {\n if (!html || !measureRef.current) return;\n requestAnimationFrame(() => {\n paginate();\n });\n }, [html, paginate]);\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 (\n <div className=\"rfp-flex rfp-items-center rfp-justify-center rfp-w-full rfp-h-full\">\n <div className=\"rfp-text-fg-secondary rfp-text-center\">\n <p className=\"rfp-text-lg\">{error}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className=\"rfp-w-full rfp-h-full rfp-overflow-auto\"\n style={{ background: 'rgba(0, 0, 0, 0.15)' }}\n >\n {/* Hidden measurement div — same width as page content area */}\n <div\n ref={measureRef}\n dangerouslySetInnerHTML={{ __html: html }}\n style={{\n ...contentStyle,\n position: 'absolute',\n visibility: 'hidden',\n width: `${794 - PAGE_PADDING_X * 2}px`,\n pointerEvents: 'none',\n }}\n />\n\n {/* Visible pages */}\n <div\n className=\"rfp-py-6 md:rfp-py-10 rfp-flex rfp-flex-col rfp-items-center\"\n style={{ gap: `${PAGE_GAP}px` }}\n >\n {(pages.length > 0 ? pages : ['']).map((pageHtml, i) => (\n <div\n key={i}\n style={{\n width: '100%',\n maxWidth: '794px',\n minHeight: `${PAGE_HEIGHT}px`,\n background: 'white',\n boxShadow: '0 2px 12px rgba(0, 0, 0, 0.15)',\n flexShrink: 0,\n padding: `${PAGE_PADDING_Y}px ${PAGE_PADDING_X}px`,\n }}\n >\n <div\n dangerouslySetInnerHTML={{ __html: pageHtml }}\n style={contentStyle}\n />\n </div>\n ))}\n </div>\n </div>\n );\n};\n"],"names":["PAGE_HEIGHT","PAGE_PADDING_Y","PAGE_PADDING_X","PAGE_CONTENT_HEIGHT","PAGE_GAP","contentStyle","DocxRenderer","url","t","useTranslator","html","setHtml","useState","loading","setLoading","error","setError","pages","setPages","measureRef","useRef","useEffect","response","arrayBuffer","result","mammoth","err","paginate","useCallback","container","children","currentPageUsed","child","h","blocks","jsx","jsxs","pageHtml","i"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useState as l, useEffect as a } from "react";
|
|
2
|
+
import { codeToHtml as f } from "shiki";
|
|
3
|
+
import { a as n } from "./index-LNXbKjrI.mjs";
|
|
4
|
+
function p(s, o) {
|
|
5
|
+
const r = n(), [u, i] = l(""), [h, e] = l(!0);
|
|
6
|
+
return a(() => {
|
|
7
|
+
let t = !1;
|
|
8
|
+
return e(!0), f(s, {
|
|
9
|
+
lang: o,
|
|
10
|
+
theme: r === "light" ? "github-light" : "dark-plus"
|
|
11
|
+
}).then((m) => {
|
|
12
|
+
t || (i(m), e(!1));
|
|
13
|
+
}).catch(() => {
|
|
14
|
+
t || (i(""), e(!1));
|
|
15
|
+
}), () => {
|
|
16
|
+
t = !0;
|
|
17
|
+
};
|
|
18
|
+
}, [s, o, r]), { html: u, loading: h };
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
p as u
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=useShikiHighlight-DtWg9b8y.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useShikiHighlight-DtWg9b8y.mjs","sources":["../../src/hooks/useShikiHighlight.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { codeToHtml } from 'shiki';\nimport { useResolvedTheme } from '../ThemeContext';\n\n/**\n * 用 shiki 把代码高亮成 HTML(与 vue-file-preview 同引擎、同主题,保证两端视觉一致)。\n *\n * - dark 主题用 `dark-plus`(VSCode Dark Plus)\n * - light 主题用 `github-light`(GitHub Light)\n *\n * shiki 输出的 <pre> 自带 inline 背景/前景色,主题切换时必须重新高亮,\n * 因此 resolvedTheme 进入依赖数组。\n *\n * @returns\n * - `html`: 高亮后的 HTML 字符串(失败或加载中为 '')\n * - `loading`: 是否正在高亮\n */\nexport function useShikiHighlight(code: string, lang: string): { html: string; loading: boolean } {\n const resolvedTheme = useResolvedTheme();\n const [html, setHtml] = useState('');\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n codeToHtml(code, {\n lang,\n theme: resolvedTheme === 'light' ? 'github-light' : 'dark-plus',\n })\n .then((out) => {\n if (!cancelled) {\n setHtml(out);\n setLoading(false);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setHtml('');\n setLoading(false);\n }\n });\n return () => {\n cancelled = true;\n };\n }, [code, lang, resolvedTheme]);\n\n return { html, loading };\n}\n"],"names":["useShikiHighlight","code","lang","resolvedTheme","useResolvedTheme","html","setHtml","useState","loading","setLoading","useEffect","cancelled","codeToHtml","out"],"mappings":";;;AAiBO,SAASA,EAAkBC,GAAcC,GAAkD;AAChG,QAAMC,IAAgBC,EAAA,GAChB,CAACC,GAAMC,CAAO,IAAIC,EAAS,EAAE,GAC7B,CAACC,GAASC,CAAU,IAAIF,EAAS,EAAI;AAE3C,SAAAG,EAAU,MAAM;AACd,QAAIC,IAAY;AAChB,WAAAF,EAAW,EAAI,GACfG,EAAWX,GAAM;AAAA,MACf,MAAAC;AAAA,MACA,OAAOC,MAAkB,UAAU,iBAAiB;AAAA,IAAA,CACrD,EACE,KAAK,CAACU,MAAQ;AACb,MAAKF,MACHL,EAAQO,CAAG,GACXJ,EAAW,EAAK;AAAA,IAEpB,CAAC,EACA,MAAM,MAAM;AACX,MAAKE,MACHL,EAAQ,EAAE,GACVG,EAAW,EAAK;AAAA,IAEpB,CAAC,GACI,MAAM;AACX,MAAAE,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACV,GAAMC,GAAMC,CAAa,CAAC,GAEvB,EAAE,MAAAE,GAAM,SAAAG,EAAA;AACjB;"}
|