@eternalheart/vue-file-preview 1.3.13 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/FilePreviewContent.vue.d.ts +3 -0
- package/lib/FilePreviewContent.vue.d.ts.map +1 -1
- package/lib/FilePreviewEmbed.vue.d.ts +5 -0
- package/lib/FilePreviewEmbed.vue.d.ts.map +1 -1
- package/lib/FilePreviewModal.vue.d.ts.map +1 -1
- package/lib/chunks/{RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs → RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs} +9 -9
- package/lib/chunks/RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs.map +1 -0
- package/lib/chunks/index-8ETACZ6T.mjs +267 -0
- package/lib/chunks/index-8ETACZ6T.mjs.map +1 -0
- package/lib/chunks/{index-DyipSoKh.mjs → index-B-LW-GJO.mjs} +24 -21
- package/lib/chunks/index-B-LW-GJO.mjs.map +1 -0
- package/lib/chunks/{index-Cjnw238n.mjs → index-BT9hXA6r.mjs} +3 -3
- package/lib/chunks/{index-Cjnw238n.mjs.map → index-BT9hXA6r.mjs.map} +1 -1
- package/lib/chunks/{index-CyrFxdIJ.mjs → index-BcKI75RW.mjs} +4 -4
- package/lib/chunks/{index-CyrFxdIJ.mjs.map → index-BcKI75RW.mjs.map} +1 -1
- package/lib/chunks/{index-Ctd0JQ6K.mjs → index-By6vGdnK.mjs} +3 -3
- package/lib/chunks/{index-Ctd0JQ6K.mjs.map → index-By6vGdnK.mjs.map} +1 -1
- package/lib/chunks/{index-BHi1WRQZ.mjs → index-C-Mu0fvH.mjs} +3 -3
- package/lib/chunks/{index-BHi1WRQZ.mjs.map → index-C-Mu0fvH.mjs.map} +1 -1
- package/lib/chunks/{index-DT9dPz8I.mjs → index-CGSq7-Q-.mjs} +3 -3
- package/lib/chunks/{index-DT9dPz8I.mjs.map → index-CGSq7-Q-.mjs.map} +1 -1
- package/lib/chunks/{index-DTtHrdBl.mjs → index-CWpU8prZ.mjs} +11 -11
- package/lib/chunks/index-CWpU8prZ.mjs.map +1 -0
- package/lib/chunks/index-CYa_XneH.mjs +303 -0
- package/lib/chunks/index-CYa_XneH.mjs.map +1 -0
- package/lib/chunks/{index-CYt6tpQC.mjs → index-CbJkkdEB.mjs} +9 -9
- package/lib/chunks/index-CbJkkdEB.mjs.map +1 -0
- package/lib/chunks/{index-DEoR7aDN.mjs → index-CoITLoYL.mjs} +3 -3
- package/lib/chunks/{index-DEoR7aDN.mjs.map → index-CoITLoYL.mjs.map} +1 -1
- package/lib/chunks/{index-Oxwizt6-.mjs → index-D9QQL7U6.mjs} +605 -557
- package/lib/chunks/index-D9QQL7U6.mjs.map +1 -0
- package/lib/chunks/{index-BvegaxQF.mjs → index-DAdO2lkI.mjs} +32 -32
- package/lib/chunks/index-DAdO2lkI.mjs.map +1 -0
- package/lib/chunks/{index-GGGDPwxX.mjs → index-DGqDJMot.mjs} +3 -3
- package/lib/chunks/{index-GGGDPwxX.mjs.map → index-DGqDJMot.mjs.map} +1 -1
- package/lib/chunks/{index-DIe0vHlt.mjs → index-DTyG1Wzp.mjs} +8 -8
- package/lib/chunks/index-DTyG1Wzp.mjs.map +1 -0
- package/lib/chunks/{index-Dw1U4YqT.mjs → index-DglMeKJM.mjs} +36 -38
- package/lib/chunks/{index-Dw1U4YqT.mjs.map → index-DglMeKJM.mjs.map} +1 -1
- package/lib/chunks/{index-B2lqsdp7.mjs → index-OOoaYpkl.mjs} +3 -3
- package/lib/chunks/{index-B2lqsdp7.mjs.map → index-OOoaYpkl.mjs.map} +1 -1
- package/lib/chunks/{index-DBw8tvyb.mjs → index-jcgLzyDR.mjs} +4 -4
- package/lib/chunks/{index-DBw8tvyb.mjs.map → index-jcgLzyDR.mjs.map} +1 -1
- package/lib/chunks/{index-BTW1xwB9.mjs → index-nFIiu1lA.mjs} +3 -3
- package/lib/chunks/{index-BTW1xwB9.mjs.map → index-nFIiu1lA.mjs.map} +1 -1
- package/lib/index.cjs +40 -32
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.mjs +1 -1
- package/lib/renderers/Audio/index.vue.d.ts.map +1 -1
- package/lib/renderers/Docx/index.vue.d.ts.map +1 -1
- package/lib/renderers/Epub/index.vue.d.ts.map +1 -1
- package/lib/renderers/Markdown/index.vue.d.ts.map +1 -1
- package/lib/renderers/Pdf/index.vue.d.ts +3 -0
- package/lib/renderers/Pdf/index.vue.d.ts.map +1 -1
- package/lib/renderers/Pdf/toolbar.d.ts +5 -0
- package/lib/renderers/Pdf/toolbar.d.ts.map +1 -1
- package/lib/renderers/RendererError.vue.d.ts.map +1 -1
- package/lib/renderers/Subtitle/index.vue.d.ts.map +1 -1
- package/lib/renderers/Text/index.vue.d.ts.map +1 -1
- package/lib/renderers/Xml/index.vue.d.ts.map +1 -1
- package/package.json +1 -1
- package/lib/chunks/RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs.map +0 -1
- package/lib/chunks/index-BvegaxQF.mjs.map +0 -1
- package/lib/chunks/index-CYt6tpQC.mjs.map +0 -1
- package/lib/chunks/index-DIe0vHlt.mjs.map +0 -1
- package/lib/chunks/index-DTtHrdBl.mjs.map +0 -1
- package/lib/chunks/index-DX_NBPzS.mjs +0 -248
- package/lib/chunks/index-DX_NBPzS.mjs.map +0 -1
- package/lib/chunks/index-DyipSoKh.mjs.map +0 -1
- package/lib/chunks/index-Oxwizt6-.mjs.map +0 -1
- package/lib/chunks/index-Zwl5J-Uw.mjs +0 -166
- package/lib/chunks/index-Zwl5J-Uw.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-Cjnw238n.mjs","sources":["../../src/renderers/Pptx/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount } from 'vue';\nimport { init } from 'pptx-preview';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\nconst props = withDefaults(\n defineProps<{\n url: string;\n tiled?: boolean;\n }>(),\n { tiled: true }\n);\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst slideCount = ref(0);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet previewer: ReturnType<typeof init> | null = null;\nlet resizeObserver: ResizeObserver | null = null;\nlet arrayBufferRef: ArrayBuffer | null = null;\nlet resizeTimeout: number | null = null;\nlet lastDimensions = { width: 0, height: 0 };\n\nconst calculateDimensions = () => {\n if (!containerRef.value) return { width: 960, height: 540 };\n const rawWidth = containerRef.value.clientWidth;\n const parentWidth = containerRef.value.parentElement?.clientWidth || 0;\n const containerWidth = rawWidth > 100 ? rawWidth : parentWidth > 100 ? parentWidth : 300;\n const height = Math.floor((containerWidth * 9) / 16);\n return { width: containerWidth, height };\n};\n\nconst reinitializePreviewer = async () => {\n if (!containerRef.value || !arrayBufferRef || slideCount.value === 0) return;\n\n try {\n if (previewer) {\n try {\n previewer.destroy();\n } catch {\n // ignore\n }\n }\n\n containerRef.value.innerHTML = '';\n\n const currentDimensions = calculateDimensions();\n previewer = init(containerRef.value, {\n width: currentDimensions.width,\n height: props.tiled ? currentDimensions.height * slideCount.value : currentDimensions.height,\n mode: props.tiled ? 'list' : 'slide',\n });\n\n await previewer.preview(arrayBufferRef);\n } catch {\n // 静默处理\n }\n};\n\nconst loadPptx = async () => {\n if (!containerRef.value) return;\n\n loading.value = true;\n error.value = null;\n\n let timeoutId: number | null = window.setTimeout(() => {\n error.value = t.value('pptx.timeout');\n loading.value = false;\n }, 30000);\n\n try {\n const response = await fetcher.value(props.url, {\n mode: 'cors',\n credentials: 'omit',\n redirect: 'follow',\n });\n\n if (!response.ok) {\n if (response.status === 404) throw new Error(t.value('pptx.not_found'));\n if (response.status === 403) throw new Error('无权限访问此文件');\n if (response.status >= 500) throw new Error('服务器错误,请稍后重试');\n throw new Error(`文件加载失败 (${response.status})`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n if (arrayBuffer.byteLength === 0) throw new Error('文件为空');\n\n arrayBufferRef = arrayBuffer;\n\n // 步骤 1: 创建隐藏容器获取 slideCount\n const hiddenContainer = document.createElement('div');\n hiddenContainer.style.cssText = 'position:absolute;left:-9999px;top:-9999px;visibility:hidden';\n document.body.appendChild(hiddenContainer);\n\n try {\n const tempPreviewer = init(hiddenContainer, {\n width: 100,\n height: 100,\n mode: 'slide',\n });\n\n try {\n await tempPreviewer.preview(arrayBuffer);\n } catch {\n throw new Error(t.value('pptx.invalid_format'));\n }\n\n const count = tempPreviewer.slideCount;\n if (!count || count === 0) throw new Error(t.value('pptx.no_pages'));\n\n tempPreviewer.destroy();\n\n slideCount.value = count;\n\n if (containerRef.value) {\n containerRef.value.innerHTML = '';\n }\n\n const currentDimensions = calculateDimensions();\n previewer = init(containerRef.value, {\n width: currentDimensions.width,\n height: props.tiled ? currentDimensions.height * count : currentDimensions.height,\n mode: props.tiled ? 'list' : 'slide',\n });\n\n await previewer.preview(arrayBuffer);\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n loading.value = false;\n } finally {\n if (document.body.contains(hiddenContainer)) {\n document.body.removeChild(hiddenContainer);\n }\n }\n } catch (err) {\n if (timeoutId !== null) clearTimeout(timeoutId);\n let errorMsg = t.value('pptx.parse_failed');\n if (err instanceof Error) errorMsg = err.message;\n else if (typeof err === 'string') errorMsg = err;\n error.value = errorMsg;\n loading.value = false;\n }\n};\n\nonMounted(() => {\n if (!containerRef.value) return;\n\n let isInitialRender = true;\n\n resizeObserver = new ResizeObserver(() => {\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensions = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n if (widthDiff < 10 && heightDiff < 10) return;\n\n lastDimensions = newDimensions;\n\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n\n resizeTimeout = window.setTimeout(() => {\n if (previewer && arrayBufferRef) reinitializePreviewer();\n }, 800);\n });\n\n resizeObserver.observe(containerRef.value);\n\n setTimeout(() => {\n requestAnimationFrame(() => requestAnimationFrame(() => loadPptx()));\n }, 150);\n});\n\nwatch(\n () => props.url,\n (newUrl) => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (newUrl) {\n loadPptx();\n }\n }\n);\n\nonBeforeUnmount(() => {\n if (resizeObserver) resizeObserver.disconnect();\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n arrayBufferRef = null;\n slideCount.value = 0;\n if (previewer) {\n try {\n previewer.destroy();\n } catch {\n // ignore\n }\n }\n previewer = null;\n});\n</script>\n\n<template>\n <div class=\"vfp-relative vfp-flex vfp-flex-col vfp-items-center vfp-w-full vfp-h-full\">\n <div\n v-if=\"loading\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-bg-surface-toolbar vfp-backdrop-blur-sm vfp-z-10\"\n >\n <div class=\"vfp-text-center\">\n <div\n class=\"vfp-w-10 vfp-h-10 md:vfp-w-12 md:vfp-h-12 vfp-mx-auto vfp-mb-3 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n <p class=\"vfp-text-xs md:vfp-text-sm vfp-text-fg-secondary vfp-font-medium\">{{ t('pptx.loading') }}</p>\n </div>\n </div>\n\n <RendererError\n v-if=\"error && !loading\"\n :message=\"t('pptx.load_failed')\"\n :detail=\"error\"\n class=\"vfp-absolute vfp-inset-0 vfp-bg-surface-toolbar vfp-backdrop-blur-sm vfp-z-10\"\n />\n\n <div\n v-if=\"!error\"\n ref=\"containerRef\"\n class=\"pptx-wrapper vfp-w-full vfp-max-w-full md:vfp-max-w-6xl\"\n :style=\"{ opacity: loading ? 0 : 1 }\"\n />\n </div>\n</template>\n"],"names":["props","__props","t","useTranslator","fetcher","useFetcher","loading","ref","error","slideCount","containerRef","previewer","resizeObserver","arrayBufferRef","resizeTimeout","lastDimensions","calculateDimensions","rawWidth","parentWidth","_a","containerWidth","height","reinitializePreviewer","currentDimensions","init","loadPptx","timeoutId","response","arrayBuffer","hiddenContainer","tempPreviewer","count","err","errorMsg","onMounted","isInitialRender","newDimensions","widthDiff","heightDiff","watch","newUrl","onBeforeUnmount","_openBlock","_createElementBlock","_hoisted_1","_hoisted_2","_createElementVNode","_hoisted_3","_hoisted_4","_toDisplayString","_unref","_createBlock","RendererError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-BT9hXA6r.mjs","sources":["../../src/renderers/Pptx/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount } from 'vue';\nimport { init } from 'pptx-preview';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\nconst props = withDefaults(\n defineProps<{\n url: string;\n tiled?: boolean;\n }>(),\n { tiled: true }\n);\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst slideCount = ref(0);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet previewer: ReturnType<typeof init> | null = null;\nlet resizeObserver: ResizeObserver | null = null;\nlet arrayBufferRef: ArrayBuffer | null = null;\nlet resizeTimeout: number | null = null;\nlet lastDimensions = { width: 0, height: 0 };\n\nconst calculateDimensions = () => {\n if (!containerRef.value) return { width: 960, height: 540 };\n const rawWidth = containerRef.value.clientWidth;\n const parentWidth = containerRef.value.parentElement?.clientWidth || 0;\n const containerWidth = rawWidth > 100 ? rawWidth : parentWidth > 100 ? parentWidth : 300;\n const height = Math.floor((containerWidth * 9) / 16);\n return { width: containerWidth, height };\n};\n\nconst reinitializePreviewer = async () => {\n if (!containerRef.value || !arrayBufferRef || slideCount.value === 0) return;\n\n try {\n if (previewer) {\n try {\n previewer.destroy();\n } catch {\n // ignore\n }\n }\n\n containerRef.value.innerHTML = '';\n\n const currentDimensions = calculateDimensions();\n previewer = init(containerRef.value, {\n width: currentDimensions.width,\n height: props.tiled ? currentDimensions.height * slideCount.value : currentDimensions.height,\n mode: props.tiled ? 'list' : 'slide',\n });\n\n await previewer.preview(arrayBufferRef);\n } catch {\n // 静默处理\n }\n};\n\nconst loadPptx = async () => {\n if (!containerRef.value) return;\n\n loading.value = true;\n error.value = null;\n\n let timeoutId: number | null = window.setTimeout(() => {\n error.value = t.value('pptx.timeout');\n loading.value = false;\n }, 30000);\n\n try {\n const response = await fetcher.value(props.url, {\n mode: 'cors',\n credentials: 'omit',\n redirect: 'follow',\n });\n\n if (!response.ok) {\n if (response.status === 404) throw new Error(t.value('pptx.not_found'));\n if (response.status === 403) throw new Error('无权限访问此文件');\n if (response.status >= 500) throw new Error('服务器错误,请稍后重试');\n throw new Error(`文件加载失败 (${response.status})`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n if (arrayBuffer.byteLength === 0) throw new Error('文件为空');\n\n arrayBufferRef = arrayBuffer;\n\n // 步骤 1: 创建隐藏容器获取 slideCount\n const hiddenContainer = document.createElement('div');\n hiddenContainer.style.cssText = 'position:absolute;left:-9999px;top:-9999px;visibility:hidden';\n document.body.appendChild(hiddenContainer);\n\n try {\n const tempPreviewer = init(hiddenContainer, {\n width: 100,\n height: 100,\n mode: 'slide',\n });\n\n try {\n await tempPreviewer.preview(arrayBuffer);\n } catch {\n throw new Error(t.value('pptx.invalid_format'));\n }\n\n const count = tempPreviewer.slideCount;\n if (!count || count === 0) throw new Error(t.value('pptx.no_pages'));\n\n tempPreviewer.destroy();\n\n slideCount.value = count;\n\n if (containerRef.value) {\n containerRef.value.innerHTML = '';\n }\n\n const currentDimensions = calculateDimensions();\n previewer = init(containerRef.value, {\n width: currentDimensions.width,\n height: props.tiled ? currentDimensions.height * count : currentDimensions.height,\n mode: props.tiled ? 'list' : 'slide',\n });\n\n await previewer.preview(arrayBuffer);\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n loading.value = false;\n } finally {\n if (document.body.contains(hiddenContainer)) {\n document.body.removeChild(hiddenContainer);\n }\n }\n } catch (err) {\n if (timeoutId !== null) clearTimeout(timeoutId);\n let errorMsg = t.value('pptx.parse_failed');\n if (err instanceof Error) errorMsg = err.message;\n else if (typeof err === 'string') errorMsg = err;\n error.value = errorMsg;\n loading.value = false;\n }\n};\n\nonMounted(() => {\n if (!containerRef.value) return;\n\n let isInitialRender = true;\n\n resizeObserver = new ResizeObserver(() => {\n if (isInitialRender) {\n isInitialRender = false;\n lastDimensions = calculateDimensions();\n return;\n }\n\n const newDimensions = calculateDimensions();\n const widthDiff = Math.abs(lastDimensions.width - newDimensions.width);\n const heightDiff = Math.abs(lastDimensions.height - newDimensions.height);\n\n if (widthDiff < 10 && heightDiff < 10) return;\n\n lastDimensions = newDimensions;\n\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n\n resizeTimeout = window.setTimeout(() => {\n if (previewer && arrayBufferRef) reinitializePreviewer();\n }, 800);\n });\n\n resizeObserver.observe(containerRef.value);\n\n setTimeout(() => {\n requestAnimationFrame(() => requestAnimationFrame(() => loadPptx()));\n }, 150);\n});\n\nwatch(\n () => props.url,\n (newUrl) => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (newUrl) {\n loadPptx();\n }\n }\n);\n\nonBeforeUnmount(() => {\n if (resizeObserver) resizeObserver.disconnect();\n if (resizeTimeout !== null) clearTimeout(resizeTimeout);\n arrayBufferRef = null;\n slideCount.value = 0;\n if (previewer) {\n try {\n previewer.destroy();\n } catch {\n // ignore\n }\n }\n previewer = null;\n});\n</script>\n\n<template>\n <div class=\"vfp-relative vfp-flex vfp-flex-col vfp-items-center vfp-w-full vfp-h-full\">\n <div\n v-if=\"loading\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-bg-surface-toolbar vfp-backdrop-blur-sm vfp-z-10\"\n >\n <div class=\"vfp-text-center\">\n <div\n class=\"vfp-w-10 vfp-h-10 md:vfp-w-12 md:vfp-h-12 vfp-mx-auto vfp-mb-3 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n <p class=\"vfp-text-xs md:vfp-text-sm vfp-text-fg-secondary vfp-font-medium\">{{ t('pptx.loading') }}</p>\n </div>\n </div>\n\n <RendererError\n v-if=\"error && !loading\"\n :message=\"t('pptx.load_failed')\"\n :detail=\"error\"\n class=\"vfp-absolute vfp-inset-0 vfp-bg-surface-toolbar vfp-backdrop-blur-sm vfp-z-10\"\n />\n\n <div\n v-if=\"!error\"\n ref=\"containerRef\"\n class=\"pptx-wrapper vfp-w-full vfp-max-w-full md:vfp-max-w-6xl\"\n :style=\"{ opacity: loading ? 0 : 1 }\"\n />\n </div>\n</template>\n"],"names":["props","__props","t","useTranslator","fetcher","useFetcher","loading","ref","error","slideCount","containerRef","previewer","resizeObserver","arrayBufferRef","resizeTimeout","lastDimensions","calculateDimensions","rawWidth","parentWidth","_a","containerWidth","height","reinitializePreviewer","currentDimensions","init","loadPptx","timeoutId","response","arrayBuffer","hiddenContainer","tempPreviewer","count","err","errorMsg","onMounted","isInitialRender","newDimensions","widthDiff","heightDiff","watch","newUrl","onBeforeUnmount","_openBlock","_createElementBlock","_hoisted_1","_hoisted_2","_createElementVNode","_hoisted_3","_hoisted_4","_toDisplayString","_unref","_createBlock","RendererError"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent as N, ref as u, computed as C, onMounted as ne, onBeforeUnmount as _, watch as B, openBlock as a, createElementBlock as c, Fragment as F, createElementVNode as i, normalizeClass as E, toDisplayString as $, withDirectives as O, renderSlot as W, vShow as P, normalizeStyle as I, resolveComponent as le, createVNode as K, unref as T, createBlock as R, resolveDynamicComponent as A, renderList as q, createCommentVNode as X, defineAsyncComponent as oe, withCtx as V, Teleport as se } from "vue";
|
|
2
|
-
import { _ as G, F as ae, C as ie, u as re, a as fe, d as Z, j as ve, D as de, A as pe, R as ue } from "./index-
|
|
2
|
+
import { _ as G, F as ae, C as ie, u as re, a as fe, d as Z, j as ve, D as de, A as pe, R as ue } from "./index-D9QQL7U6.mjs";
|
|
3
3
|
import { FileImage as ce, FileText as me, FileCode as he, File as we, ChevronRight as ye, FolderOpen as ge, Folder as xe } from "lucide-vue-next";
|
|
4
|
-
import { _ as be } from "./RendererError.vue_vue_type_script_setup_true_lang-
|
|
4
|
+
import { _ as be } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
5
5
|
const ke = { class: "vfp-flex vfp-flex-shrink-0 vfp-border-b vfp-border-line-weak vfp-bg-surface-toolbar" }, Le = { class: "vfp-flex-1 vfp-min-h-0 vfp-min-w-0 vfp-w-full vfp-overflow-hidden" }, Se = { class: "vfp-flex-1 vfp-min-h-0 vfp-min-w-0 vfp-w-full vfp-overflow-hidden" }, ze = { class: "vfp-flex-1 vfp-min-w-0 vfp-min-h-0 vfp-overflow-hidden" }, Ce = /* @__PURE__ */ N({
|
|
6
6
|
name: "ResizableSplit",
|
|
7
7
|
__name: "ResizableSplit",
|
|
@@ -194,7 +194,7 @@ const ke = { class: "vfp-flex vfp-flex-shrink-0 vfp-border-b vfp-border-line-wea
|
|
|
194
194
|
emits: ["statsChange"],
|
|
195
195
|
setup(s, { emit: M }) {
|
|
196
196
|
const l = oe(
|
|
197
|
-
() => import("./index-
|
|
197
|
+
() => import("./index-D9QQL7U6.mjs").then((e) => e.i)
|
|
198
198
|
), v = s, g = M, { t: h } = re(), S = fe(), y = u(null), d = u(null), x = u(!0), r = u(null), t = u(/* @__PURE__ */ new Set([""])), f = u(null), b = u(!1), p = u(null), n = u(null), w = u(null), z = () => {
|
|
199
199
|
var e;
|
|
200
200
|
(e = f.value) != null && e.blobUrl && URL.revokeObjectURL(f.value.blobUrl);
|
|
@@ -315,4 +315,4 @@ const ke = { class: "vfp-flex vfp-flex-shrink-0 vfp-border-b vfp-border-line-wea
|
|
|
315
315
|
export {
|
|
316
316
|
Ve as default
|
|
317
317
|
};
|
|
318
|
-
//# sourceMappingURL=index-
|
|
318
|
+
//# sourceMappingURL=index-BcKI75RW.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-CyrFxdIJ.mjs","sources":["../../src/components/ResizableSplit.vue","../../src/renderers/Zip/TreeItem.vue","../../src/renderers/Zip/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount, onMounted, watch } from 'vue';\n\ninterface Props {\n /** 左侧初始宽度(px);传入 storageKey 时会从 localStorage 读取 */\n initialLeftWidth?: number;\n /** 左侧最小宽度(px) */\n minLeftWidth?: number;\n /** 左侧最大宽度(px) */\n maxLeftWidth?: number;\n /** 右侧至少保留的宽度(px) */\n minRightWidth?: number;\n /** localStorage 持久化 key */\n storageKey?: string;\n /** 启用横向拖动的媒体查询 */\n desktopMedia?: string;\n /** 移动端使用 Tab 切换而非上下堆叠 */\n mobileTabMode?: boolean;\n /** Tab 模式下左侧标题 */\n leftTabLabel?: string;\n /** Tab 模式下右侧标题 */\n rightTabLabel?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n initialLeftWidth: 280,\n minLeftWidth: 160,\n maxLeftWidth: 640,\n minRightWidth: 200,\n desktopMedia: '(min-width: 768px)',\n mobileTabMode: false,\n leftTabLabel: '文件树',\n rightTabLabel: '预览',\n});\n\ndefineOptions({ name: 'ResizableSplit' });\n\nconst containerRef = ref<HTMLDivElement | null>(null);\nconst leftWidth = ref<number>(\n (() => {\n if (props.storageKey && typeof window !== 'undefined') {\n const saved = Number(window.localStorage.getItem(props.storageKey));\n if (!isNaN(saved) && saved > 0) return saved;\n }\n return props.initialLeftWidth;\n })()\n);\nconst dragging = ref(false);\nconst isDesktop = ref(false);\nconst activeTab = ref<'left' | 'right'>('left');\n\nlet mq: MediaQueryList | null = null;\nconst mqHandler = () => {\n if (mq) isDesktop.value = mq.matches;\n};\n\nconst leftStyle = computed(() =>\n isDesktop.value ? { width: `${leftWidth.value}px` } : undefined\n);\n\nconst onMove = (e: MouseEvent) => {\n if (!containerRef.value) return;\n const rect = containerRef.value.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const cap = rect.width - props.minRightWidth - 6;\n const effectiveMax = Math.min(props.maxLeftWidth, cap);\n leftWidth.value = Math.max(\n props.minLeftWidth,\n Math.min(effectiveMax, x)\n );\n};\n\nconst onUp = () => {\n if (!dragging.value) return;\n dragging.value = false;\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n if (props.storageKey) {\n try {\n window.localStorage.setItem(props.storageKey, String(leftWidth.value));\n } catch {\n // ignore\n }\n }\n};\n\nconst onDividerDown = (e: MouseEvent) => {\n e.preventDefault();\n dragging.value = true;\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n};\n\nonMounted(() => {\n if (typeof window !== 'undefined') {\n mq = window.matchMedia(props.desktopMedia);\n mqHandler();\n mq.addEventListener('change', mqHandler);\n }\n});\n\nonBeforeUnmount(() => {\n if (mq) mq.removeEventListener('change', mqHandler);\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n if (dragging.value) {\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n }\n});\n\nwatch(\n () => props.initialLeftWidth,\n (w) => {\n if (!props.storageKey) leftWidth.value = w;\n }\n);\n\nconst switchTab = (tab: 'left' | 'right') => {\n activeTab.value = tab;\n};\n\ndefineExpose({ switchTab });\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-w-full vfp-h-full vfp-flex vfp-flex-col md:vfp-flex-row vfp-min-h-0 vfp-min-w-0\"\n >\n <!-- 移动端 Tab 模式 -->\n <template v-if=\"mobileTabMode && !isDesktop\">\n <div class=\"vfp-flex vfp-flex-shrink-0 vfp-border-b vfp-border-line-weak vfp-bg-surface-toolbar\">\n <button\n type=\"button\"\n class=\"vfp-flex-1 vfp-py-2.5 vfp-text-sm vfp-transition-colors\"\n :class=\"activeTab === 'left'\n ? 'vfp-text-fg-primary vfp-border-b-2 vfp-border-fg-primary -vfp-mb-px'\n : 'vfp-text-fg-secondary'\"\n @click=\"switchTab('left')\"\n >\n {{ leftTabLabel }}\n </button>\n <button\n type=\"button\"\n class=\"vfp-flex-1 vfp-py-2.5 vfp-text-sm vfp-transition-colors\"\n :class=\"activeTab === 'right'\n ? 'vfp-text-fg-primary vfp-border-b-2 vfp-border-fg-primary -vfp-mb-px'\n : 'vfp-text-fg-secondary'\"\n @click=\"switchTab('right')\"\n >\n {{ rightTabLabel }}\n </button>\n </div>\n <div v-show=\"activeTab === 'left'\" class=\"vfp-flex-1 vfp-min-h-0 vfp-min-w-0 vfp-w-full vfp-overflow-hidden\">\n <slot name=\"left\" />\n </div>\n <div v-show=\"activeTab === 'right'\" class=\"vfp-flex-1 vfp-min-h-0 vfp-min-w-0 vfp-w-full vfp-overflow-hidden\">\n <slot name=\"right\" />\n </div>\n </template>\n\n <!-- 桌面端 / 移动端默认堆叠模式 -->\n <template v-else>\n <div\n class=\"vfp-min-h-0 vfp-min-w-0 vfp-flex-shrink-0 vfp-w-full vfp-max-h-60 md:vfp-h-full md:vfp-max-h-none\"\n :style=\"leftStyle\"\n >\n <slot name=\"left\" />\n </div>\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n class=\"split-divider vfp-hidden md:vfp-block vfp-relative vfp-w-1.5 vfp-flex-shrink-0 vfp-cursor-col-resize vfp-transition-colors\"\n :class=\"dragging ? 'dragging' : ''\"\n @mousedown=\"onDividerDown\"\n >\n <span class=\"vfp-absolute vfp-inset-y-0 hit-area\" />\n </div>\n <div class=\"vfp-flex-1 vfp-min-w-0 vfp-min-h-0 vfp-overflow-hidden\">\n <slot name=\"right\" />\n </div>\n </template>\n </div>\n</template>\n\n<style scoped>\n.split-divider {\n background: rgba(255, 255, 255, 0.1);\n}\n.split-divider:hover {\n background: rgba(255, 255, 255, 0.3);\n}\n.split-divider.dragging {\n background: rgba(255, 255, 255, 0.4);\n}\n.hit-area {\n left: -4px;\n right: -4px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport {\n Folder,\n FolderOpen,\n FileText,\n FileImage,\n FileCode,\n File as FileIcon,\n ChevronRight,\n} from 'lucide-vue-next';\nimport { formatFileSize, getFileType, type ZipTreeNode } from '@eternalheart/file-preview-core';\n\nconst props = defineProps<{\n node: ZipTreeNode;\n depth: number;\n selectedPath: string | null;\n expanded: Set<string>;\n}>();\n\nconst emit = defineEmits<{\n (e: 'toggle', path: string): void;\n (e: 'select', node: ZipTreeNode): void;\n (e: 'hover', text: string, rect: DOMRect): void;\n (e: 'leave'): void;\n}>();\n\nconst padStyle = computed(() => ({ paddingLeft: `${props.depth * 14 + 10}px` }));\nconst isOpen = computed(() => props.expanded.has(props.node.path));\nconst isSelected = computed(() => props.selectedPath === props.node.path);\n\nconst fileIcon = computed(() => {\n const ft = getFileType({ id: '', name: props.node.name, url: '', type: '' });\n if (ft === 'image') return FileImage;\n if (ft === 'text' || ft === 'markdown' || ft === 'json' || ft === 'csv' || ft === 'xml' || ft === 'subtitle') {\n return (props.node.name.endsWith('.md') || props.node.name.endsWith('.markdown')) ? FileText : FileCode;\n }\n return FileIcon;\n});\n\nconst handleEnter = (e: MouseEvent) => {\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n emit('hover', props.node.name || '/', rect);\n};\n\nconst handleLeave = () => emit('leave');\n</script>\n\n<template>\n <div v-if=\"node.isDir\">\n <button\n type=\"button\"\n class=\"tree-row dir-row\"\n :style=\"padStyle\"\n @click=\"emit('toggle', node.path)\"\n @mouseenter=\"handleEnter\"\n @mouseleave=\"handleLeave\"\n >\n <ChevronRight\n class=\"vfp-w-3.5 vfp-h-3.5 vfp-flex-shrink-0 vfp-transition-transform\"\n :class=\"{ 'vfp-rotate-90': isOpen }\"\n />\n <component\n :is=\"isOpen ? FolderOpen : Folder\"\n class=\"vfp-w-4 vfp-h-4 vfp-flex-shrink-0 vfp-text-amber-300/80\"\n />\n <span class=\"vfp-truncate vfp-flex-1 vfp-min-w-0\">{{ node.name || '/' }}</span>\n </button>\n <template v-if=\"isOpen && node.children\">\n <ZipTreeItem\n v-for=\"child in node.children\"\n :key=\"child.path\"\n :node=\"child\"\n :depth=\"depth + 1\"\n :selected-path=\"selectedPath\"\n :expanded=\"expanded\"\n @toggle=\"(p) => emit('toggle', p)\"\n @select=\"(n) => emit('select', n)\"\n @hover=\"(t, r) => emit('hover', t, r)\"\n @leave=\"emit('leave')\"\n />\n </template>\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tree-row file-row\"\n :class=\"{ selected: isSelected }\"\n :style=\"padStyle\"\n @click=\"emit('select', node)\"\n @mouseenter=\"handleEnter\"\n @mouseleave=\"handleLeave\"\n >\n <span class=\"vfp-w-3.5 vfp-h-3.5 vfp-flex-shrink-0\" />\n <component :is=\"fileIcon\" class=\"vfp-w-4 vfp-h-4 vfp-flex-shrink-0 vfp-text-fg-tertiary\" />\n <span class=\"vfp-flex-1 vfp-truncate vfp-min-w-0\">{{ node.name }}</span>\n <span class=\"vfp-text-xs vfp-text-fg-disabled vfp-flex-shrink-0 vfp-ml-2\">{{ formatFileSize(node.size) }}</span>\n </button>\n</template>\n\n<script lang=\"ts\">\nexport default { name: 'ZipTreeItem' };\n</script>\n\n<style scoped>\n.tree-row {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n padding-right: 0.5rem;\n text-align: left;\n font-size: 0.875rem;\n background: transparent;\n border: 0;\n cursor: pointer;\n}\n.dir-row {\n color: var(--fp-fg-secondary);\n}\n.file-row {\n color: var(--fp-fg-secondary);\n}\n.tree-row:hover {\n background: var(--fp-surface-1);\n}\n.file-row.selected {\n background: var(--fp-line);\n color: #fff;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, onBeforeUnmount, defineAsyncComponent } from 'vue';\nimport type JSZip from 'jszip';\nimport {\n loadZip,\n listZipEntries,\n buildZipTree,\n readZipEntryBlob,\n inferMimeType,\n type ZipTreeNode,\n} from '@eternalheart/file-preview-core';\nimport ResizableSplit from '../../components/ResizableSplit.vue';\nimport TreeItem from './TreeItem.vue';\nimport type { ZipToolbarStats } from './toolbar';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\n// 懒加载 FilePreviewContent 以打破循环依赖\nconst LazyFilePreviewContent = defineAsyncComponent(\n () => import('../../FilePreviewContent.vue')\n);\n\nconst props = withDefaults(defineProps<{\n url: string;\n /** ZIP 嵌套深度(由 FilePreviewContent 传入) */\n nestingDepth?: number;\n}>(), {\n nestingDepth: 0,\n});\n\nconst emit = defineEmits<{\n (e: 'statsChange', stats: ZipToolbarStats | null): void;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\ninterface SelectedPreview {\n path: string;\n name: string;\n size: number;\n blobUrl: string;\n}\n\ninterface HoverTipState {\n text: string;\n x: number;\n y: number;\n}\n\nconst zip = ref<JSZip | null>(null);\nconst tree = ref<ZipTreeNode | null>(null);\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst expanded = ref<Set<string>>(new Set(['']));\nconst selected = ref<SelectedPreview | null>(null);\nconst previewLoading = ref(false);\nconst previewError = ref<string | null>(null);\nconst hoverTip = ref<HoverTipState | null>(null);\nconst splitRef = ref<InstanceType<typeof ResizableSplit> | null>(null);\n\nconst revokeCurrent = () => {\n if (selected.value?.blobUrl) URL.revokeObjectURL(selected.value.blobUrl);\n};\n\nconst load = async () => {\n revokeCurrent();\n selected.value = null;\n loading.value = true;\n error.value = null;\n try {\n const res = await fetcher.value(props.url);\n if (!res.ok) throw new Error('加载失败');\n const buf = await res.arrayBuffer();\n const z = await loadZip(buf);\n const entries = listZipEntries(z);\n const root = buildZipTree(entries);\n zip.value = z;\n tree.value = root;\n const init = new Set<string>(['']);\n if (root.children) for (const c of root.children) if (c.isDir) init.add(c.path);\n expanded.value = init;\n } catch (err) {\n console.error(err);\n error.value = t.value('zip.load_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, (newUrl) => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (newUrl) load();\n}, { immediate: true });\nonBeforeUnmount(() => { revokeCurrent(); });\n\nconst totalStats = computed<ZipToolbarStats | null>(() => {\n if (!tree.value) return null;\n let files = 0, dirs = 0, size = 0;\n const walk = (n: ZipTreeNode) => {\n if (n.isDir) { if (n.path) dirs++; n.children?.forEach(walk); }\n else { files++; size += n.size; }\n };\n walk(tree.value);\n return { files, dirs, size };\n});\n\nwatch(totalStats, (s) => emit('statsChange', s), { immediate: true });\nonBeforeUnmount(() => emit('statsChange', null));\n\nconst handleToggle = (path: string) => {\n const next = new Set(expanded.value);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n expanded.value = next;\n};\n\nconst handleHover = (text: string, rect: DOMRect) => {\n hoverTip.value = { text, x: rect.right + 8, y: rect.top + rect.height / 2 };\n};\nconst handleLeave = () => { hoverTip.value = null; };\n\nconst handleSelect = async (node: ZipTreeNode) => {\n if (!zip.value || node.isDir) return;\n revokeCurrent();\n previewLoading.value = true;\n previewError.value = null;\n\n try {\n const mime = inferMimeType(node.name);\n const blob = await readZipEntryBlob(zip.value, node.path, mime !== 'application/octet-stream' ? mime : undefined);\n const blobUrl = URL.createObjectURL(blob);\n selected.value = { path: node.path, name: node.name, size: node.size, blobUrl };\n // 移动端切换到预览 tab\n splitRef.value?.switchTab('right');\n } catch (err) {\n console.error(err);\n previewError.value = '条目读取失败';\n } finally {\n previewLoading.value = false;\n }\n};\n\n/** 为嵌入的 FilePreviewContent 构建 files 数组 */\nconst previewFiles = computed(() => {\n if (!selected.value) return [];\n return [{ name: selected.value.name, url: selected.value.blobUrl, type: inferMimeType(selected.value.name) }];\n});\n</script>\n\n<template>\n <div v-if=\"loading\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\" />\n </div>\n\n <RendererError v-else-if=\"error || !tree\" :message=\"error || t('zip.parse_failed')\" />\n\n <template v-else>\n <ResizableSplit\n ref=\"splitRef\"\n :initial-left-width=\"280\"\n :min-left-width=\"180\"\n :max-left-width=\"560\"\n storage-key=\"vfp-zip-split-left\"\n mobile-tab-mode\n left-tab-label=\"文件树\"\n right-tab-label=\"预览\"\n >\n <template #left>\n <div class=\"vfp-w-full vfp-h-full vfp-overflow-auto\">\n <TreeItem\n v-for=\"child in tree.children || []\"\n :key=\"child.path\"\n :node=\"child\"\n :depth=\"0\"\n :selected-path=\"selected?.path ?? null\"\n :expanded=\"expanded\"\n @toggle=\"handleToggle\"\n @select=\"handleSelect\"\n @hover=\"handleHover\"\n @leave=\"handleLeave\"\n />\n </div>\n </template>\n\n <template #right>\n <div class=\"vfp-w-full vfp-h-full vfp-flex vfp-flex-col\">\n <div v-if=\"!selected\" class=\"vfp-flex-1 vfp-flex vfp-items-center vfp-justify-center vfp-text-fg-muted vfp-text-sm vfp-p-6\">\n 从左侧选择一个文件以预览\n </div>\n <div v-else-if=\"previewLoading\" class=\"vfp-flex-1 vfp-flex vfp-items-center vfp-justify-center\">\n <div class=\"vfp-w-8 vfp-h-8 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\" />\n </div>\n <div v-else-if=\"previewError\" class=\"vfp-flex-1 vfp-flex vfp-items-center vfp-justify-center vfp-text-fg-secondary\">{{ previewError }}</div>\n <template v-else>\n <div class=\"vfp-flex-1 vfp-min-h-0 vfp-overflow-hidden vfp-flex vfp-relative vfp-z-0\">\n <LazyFilePreviewContent\n mode=\"embed\"\n :files=\"previewFiles\"\n :current-index=\"0\"\n :zip-nesting-depth=\"nestingDepth + 1\"\n />\n </div>\n </template>\n </div>\n </template>\n </ResizableSplit>\n\n <!-- 文件名 hover tooltip (teleport 到 body 避免被滚动区裁剪) -->\n <Teleport to=\"body\">\n <div\n v-if=\"hoverTip\"\n class=\"vfp-zip-tip\"\n :style=\"{ left: hoverTip.x + 'px', top: hoverTip.y + 'px' }\"\n >\n {{ hoverTip.text }}\n </div>\n </Teleport>\n </template>\n</template>\n\n<style>\n/* 全局 tooltip(不能 scoped,因 Teleport 到 body) */\n.vfp-zip-tip {\n position: fixed;\n z-index: 9999;\n pointer-events: none;\n transform: translateY(-50%);\n padding: 4px 8px;\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 4px;\n white-space: nowrap;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n</style>\n"],"names":["props","__props","containerRef","ref","leftWidth","saved","dragging","isDesktop","activeTab","mq","mqHandler","leftStyle","computed","onMove","e","rect","x","cap","effectiveMax","onUp","onDividerDown","onMounted","onBeforeUnmount","watch","w","switchTab","tab","__expose","_createElementBlock","_Fragment","_createElementVNode","_hoisted_1","_normalizeClass","_withDirectives","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_hoisted_4","__default__","emit","__emit","padStyle","isOpen","isSelected","fileIcon","ft","getFileType","FileImage","FileText","FileCode","FileIcon","handleEnter","handleLeave","_cache","$event","_createVNode","_unref","ChevronRight","_createBlock","_resolveDynamicComponent","FolderOpen","Folder","_toDisplayString","_openBlock","_renderList","child","_component_ZipTreeItem","n","t","r","LazyFilePreviewContent","defineAsyncComponent","useTranslator","fetcher","useFetcher","zip","tree","loading","error","expanded","selected","previewLoading","previewError","hoverTip","splitRef","revokeCurrent","_a","load","res","buf","z","loadZip","entries","listZipEntries","root","buildZipTree","init","c","err","newUrl","totalStats","files","dirs","size","walk","s","handleToggle","path","next","handleHover","text","handleSelect","node","mime","inferMimeType","blob","readZipEntryBlob","blobUrl","previewFiles","RendererError","ResizableSplit","TreeItem","_hoisted_5","_hoisted_6","_hoisted_7","_Teleport"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-BcKI75RW.mjs","sources":["../../src/components/ResizableSplit.vue","../../src/renderers/Zip/TreeItem.vue","../../src/renderers/Zip/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onBeforeUnmount, onMounted, watch } from 'vue';\n\ninterface Props {\n /** 左侧初始宽度(px);传入 storageKey 时会从 localStorage 读取 */\n initialLeftWidth?: number;\n /** 左侧最小宽度(px) */\n minLeftWidth?: number;\n /** 左侧最大宽度(px) */\n maxLeftWidth?: number;\n /** 右侧至少保留的宽度(px) */\n minRightWidth?: number;\n /** localStorage 持久化 key */\n storageKey?: string;\n /** 启用横向拖动的媒体查询 */\n desktopMedia?: string;\n /** 移动端使用 Tab 切换而非上下堆叠 */\n mobileTabMode?: boolean;\n /** Tab 模式下左侧标题 */\n leftTabLabel?: string;\n /** Tab 模式下右侧标题 */\n rightTabLabel?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n initialLeftWidth: 280,\n minLeftWidth: 160,\n maxLeftWidth: 640,\n minRightWidth: 200,\n desktopMedia: '(min-width: 768px)',\n mobileTabMode: false,\n leftTabLabel: '文件树',\n rightTabLabel: '预览',\n});\n\ndefineOptions({ name: 'ResizableSplit' });\n\nconst containerRef = ref<HTMLDivElement | null>(null);\nconst leftWidth = ref<number>(\n (() => {\n if (props.storageKey && typeof window !== 'undefined') {\n const saved = Number(window.localStorage.getItem(props.storageKey));\n if (!isNaN(saved) && saved > 0) return saved;\n }\n return props.initialLeftWidth;\n })()\n);\nconst dragging = ref(false);\nconst isDesktop = ref(false);\nconst activeTab = ref<'left' | 'right'>('left');\n\nlet mq: MediaQueryList | null = null;\nconst mqHandler = () => {\n if (mq) isDesktop.value = mq.matches;\n};\n\nconst leftStyle = computed(() =>\n isDesktop.value ? { width: `${leftWidth.value}px` } : undefined\n);\n\nconst onMove = (e: MouseEvent) => {\n if (!containerRef.value) return;\n const rect = containerRef.value.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const cap = rect.width - props.minRightWidth - 6;\n const effectiveMax = Math.min(props.maxLeftWidth, cap);\n leftWidth.value = Math.max(\n props.minLeftWidth,\n Math.min(effectiveMax, x)\n );\n};\n\nconst onUp = () => {\n if (!dragging.value) return;\n dragging.value = false;\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n if (props.storageKey) {\n try {\n window.localStorage.setItem(props.storageKey, String(leftWidth.value));\n } catch {\n // ignore\n }\n }\n};\n\nconst onDividerDown = (e: MouseEvent) => {\n e.preventDefault();\n dragging.value = true;\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n};\n\nonMounted(() => {\n if (typeof window !== 'undefined') {\n mq = window.matchMedia(props.desktopMedia);\n mqHandler();\n mq.addEventListener('change', mqHandler);\n }\n});\n\nonBeforeUnmount(() => {\n if (mq) mq.removeEventListener('change', mqHandler);\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n if (dragging.value) {\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n }\n});\n\nwatch(\n () => props.initialLeftWidth,\n (w) => {\n if (!props.storageKey) leftWidth.value = w;\n }\n);\n\nconst switchTab = (tab: 'left' | 'right') => {\n activeTab.value = tab;\n};\n\ndefineExpose({ switchTab });\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-w-full vfp-h-full vfp-flex vfp-flex-col md:vfp-flex-row vfp-min-h-0 vfp-min-w-0\"\n >\n <!-- 移动端 Tab 模式 -->\n <template v-if=\"mobileTabMode && !isDesktop\">\n <div class=\"vfp-flex vfp-flex-shrink-0 vfp-border-b vfp-border-line-weak vfp-bg-surface-toolbar\">\n <button\n type=\"button\"\n class=\"vfp-flex-1 vfp-py-2.5 vfp-text-sm vfp-transition-colors\"\n :class=\"activeTab === 'left'\n ? 'vfp-text-fg-primary vfp-border-b-2 vfp-border-fg-primary -vfp-mb-px'\n : 'vfp-text-fg-secondary'\"\n @click=\"switchTab('left')\"\n >\n {{ leftTabLabel }}\n </button>\n <button\n type=\"button\"\n class=\"vfp-flex-1 vfp-py-2.5 vfp-text-sm vfp-transition-colors\"\n :class=\"activeTab === 'right'\n ? 'vfp-text-fg-primary vfp-border-b-2 vfp-border-fg-primary -vfp-mb-px'\n : 'vfp-text-fg-secondary'\"\n @click=\"switchTab('right')\"\n >\n {{ rightTabLabel }}\n </button>\n </div>\n <div v-show=\"activeTab === 'left'\" class=\"vfp-flex-1 vfp-min-h-0 vfp-min-w-0 vfp-w-full vfp-overflow-hidden\">\n <slot name=\"left\" />\n </div>\n <div v-show=\"activeTab === 'right'\" class=\"vfp-flex-1 vfp-min-h-0 vfp-min-w-0 vfp-w-full vfp-overflow-hidden\">\n <slot name=\"right\" />\n </div>\n </template>\n\n <!-- 桌面端 / 移动端默认堆叠模式 -->\n <template v-else>\n <div\n class=\"vfp-min-h-0 vfp-min-w-0 vfp-flex-shrink-0 vfp-w-full vfp-max-h-60 md:vfp-h-full md:vfp-max-h-none\"\n :style=\"leftStyle\"\n >\n <slot name=\"left\" />\n </div>\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n class=\"split-divider vfp-hidden md:vfp-block vfp-relative vfp-w-1.5 vfp-flex-shrink-0 vfp-cursor-col-resize vfp-transition-colors\"\n :class=\"dragging ? 'dragging' : ''\"\n @mousedown=\"onDividerDown\"\n >\n <span class=\"vfp-absolute vfp-inset-y-0 hit-area\" />\n </div>\n <div class=\"vfp-flex-1 vfp-min-w-0 vfp-min-h-0 vfp-overflow-hidden\">\n <slot name=\"right\" />\n </div>\n </template>\n </div>\n</template>\n\n<style scoped>\n.split-divider {\n background: rgba(255, 255, 255, 0.1);\n}\n.split-divider:hover {\n background: rgba(255, 255, 255, 0.3);\n}\n.split-divider.dragging {\n background: rgba(255, 255, 255, 0.4);\n}\n.hit-area {\n left: -4px;\n right: -4px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport {\n Folder,\n FolderOpen,\n FileText,\n FileImage,\n FileCode,\n File as FileIcon,\n ChevronRight,\n} from 'lucide-vue-next';\nimport { formatFileSize, getFileType, type ZipTreeNode } from '@eternalheart/file-preview-core';\n\nconst props = defineProps<{\n node: ZipTreeNode;\n depth: number;\n selectedPath: string | null;\n expanded: Set<string>;\n}>();\n\nconst emit = defineEmits<{\n (e: 'toggle', path: string): void;\n (e: 'select', node: ZipTreeNode): void;\n (e: 'hover', text: string, rect: DOMRect): void;\n (e: 'leave'): void;\n}>();\n\nconst padStyle = computed(() => ({ paddingLeft: `${props.depth * 14 + 10}px` }));\nconst isOpen = computed(() => props.expanded.has(props.node.path));\nconst isSelected = computed(() => props.selectedPath === props.node.path);\n\nconst fileIcon = computed(() => {\n const ft = getFileType({ id: '', name: props.node.name, url: '', type: '' });\n if (ft === 'image') return FileImage;\n if (ft === 'text' || ft === 'markdown' || ft === 'json' || ft === 'csv' || ft === 'xml' || ft === 'subtitle') {\n return (props.node.name.endsWith('.md') || props.node.name.endsWith('.markdown')) ? FileText : FileCode;\n }\n return FileIcon;\n});\n\nconst handleEnter = (e: MouseEvent) => {\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n emit('hover', props.node.name || '/', rect);\n};\n\nconst handleLeave = () => emit('leave');\n</script>\n\n<template>\n <div v-if=\"node.isDir\">\n <button\n type=\"button\"\n class=\"tree-row dir-row\"\n :style=\"padStyle\"\n @click=\"emit('toggle', node.path)\"\n @mouseenter=\"handleEnter\"\n @mouseleave=\"handleLeave\"\n >\n <ChevronRight\n class=\"vfp-w-3.5 vfp-h-3.5 vfp-flex-shrink-0 vfp-transition-transform\"\n :class=\"{ 'vfp-rotate-90': isOpen }\"\n />\n <component\n :is=\"isOpen ? FolderOpen : Folder\"\n class=\"vfp-w-4 vfp-h-4 vfp-flex-shrink-0 vfp-text-amber-300/80\"\n />\n <span class=\"vfp-truncate vfp-flex-1 vfp-min-w-0\">{{ node.name || '/' }}</span>\n </button>\n <template v-if=\"isOpen && node.children\">\n <ZipTreeItem\n v-for=\"child in node.children\"\n :key=\"child.path\"\n :node=\"child\"\n :depth=\"depth + 1\"\n :selected-path=\"selectedPath\"\n :expanded=\"expanded\"\n @toggle=\"(p) => emit('toggle', p)\"\n @select=\"(n) => emit('select', n)\"\n @hover=\"(t, r) => emit('hover', t, r)\"\n @leave=\"emit('leave')\"\n />\n </template>\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tree-row file-row\"\n :class=\"{ selected: isSelected }\"\n :style=\"padStyle\"\n @click=\"emit('select', node)\"\n @mouseenter=\"handleEnter\"\n @mouseleave=\"handleLeave\"\n >\n <span class=\"vfp-w-3.5 vfp-h-3.5 vfp-flex-shrink-0\" />\n <component :is=\"fileIcon\" class=\"vfp-w-4 vfp-h-4 vfp-flex-shrink-0 vfp-text-fg-tertiary\" />\n <span class=\"vfp-flex-1 vfp-truncate vfp-min-w-0\">{{ node.name }}</span>\n <span class=\"vfp-text-xs vfp-text-fg-disabled vfp-flex-shrink-0 vfp-ml-2\">{{ formatFileSize(node.size) }}</span>\n </button>\n</template>\n\n<script lang=\"ts\">\nexport default { name: 'ZipTreeItem' };\n</script>\n\n<style scoped>\n.tree-row {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n padding-right: 0.5rem;\n text-align: left;\n font-size: 0.875rem;\n background: transparent;\n border: 0;\n cursor: pointer;\n}\n.dir-row {\n color: var(--fp-fg-secondary);\n}\n.file-row {\n color: var(--fp-fg-secondary);\n}\n.tree-row:hover {\n background: var(--fp-surface-1);\n}\n.file-row.selected {\n background: var(--fp-line);\n color: #fff;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, onBeforeUnmount, defineAsyncComponent } from 'vue';\nimport type JSZip from 'jszip';\nimport {\n loadZip,\n listZipEntries,\n buildZipTree,\n readZipEntryBlob,\n inferMimeType,\n type ZipTreeNode,\n} from '@eternalheart/file-preview-core';\nimport ResizableSplit from '../../components/ResizableSplit.vue';\nimport TreeItem from './TreeItem.vue';\nimport type { ZipToolbarStats } from './toolbar';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\n// 懒加载 FilePreviewContent 以打破循环依赖\nconst LazyFilePreviewContent = defineAsyncComponent(\n () => import('../../FilePreviewContent.vue')\n);\n\nconst props = withDefaults(defineProps<{\n url: string;\n /** ZIP 嵌套深度(由 FilePreviewContent 传入) */\n nestingDepth?: number;\n}>(), {\n nestingDepth: 0,\n});\n\nconst emit = defineEmits<{\n (e: 'statsChange', stats: ZipToolbarStats | null): void;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\ninterface SelectedPreview {\n path: string;\n name: string;\n size: number;\n blobUrl: string;\n}\n\ninterface HoverTipState {\n text: string;\n x: number;\n y: number;\n}\n\nconst zip = ref<JSZip | null>(null);\nconst tree = ref<ZipTreeNode | null>(null);\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst expanded = ref<Set<string>>(new Set(['']));\nconst selected = ref<SelectedPreview | null>(null);\nconst previewLoading = ref(false);\nconst previewError = ref<string | null>(null);\nconst hoverTip = ref<HoverTipState | null>(null);\nconst splitRef = ref<InstanceType<typeof ResizableSplit> | null>(null);\n\nconst revokeCurrent = () => {\n if (selected.value?.blobUrl) URL.revokeObjectURL(selected.value.blobUrl);\n};\n\nconst load = async () => {\n revokeCurrent();\n selected.value = null;\n loading.value = true;\n error.value = null;\n try {\n const res = await fetcher.value(props.url);\n if (!res.ok) throw new Error('加载失败');\n const buf = await res.arrayBuffer();\n const z = await loadZip(buf);\n const entries = listZipEntries(z);\n const root = buildZipTree(entries);\n zip.value = z;\n tree.value = root;\n const init = new Set<string>(['']);\n if (root.children) for (const c of root.children) if (c.isDir) init.add(c.path);\n expanded.value = init;\n } catch (err) {\n console.error(err);\n error.value = t.value('zip.load_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, (newUrl) => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (newUrl) load();\n}, { immediate: true });\nonBeforeUnmount(() => { revokeCurrent(); });\n\nconst totalStats = computed<ZipToolbarStats | null>(() => {\n if (!tree.value) return null;\n let files = 0, dirs = 0, size = 0;\n const walk = (n: ZipTreeNode) => {\n if (n.isDir) { if (n.path) dirs++; n.children?.forEach(walk); }\n else { files++; size += n.size; }\n };\n walk(tree.value);\n return { files, dirs, size };\n});\n\nwatch(totalStats, (s) => emit('statsChange', s), { immediate: true });\nonBeforeUnmount(() => emit('statsChange', null));\n\nconst handleToggle = (path: string) => {\n const next = new Set(expanded.value);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n expanded.value = next;\n};\n\nconst handleHover = (text: string, rect: DOMRect) => {\n hoverTip.value = { text, x: rect.right + 8, y: rect.top + rect.height / 2 };\n};\nconst handleLeave = () => { hoverTip.value = null; };\n\nconst handleSelect = async (node: ZipTreeNode) => {\n if (!zip.value || node.isDir) return;\n revokeCurrent();\n previewLoading.value = true;\n previewError.value = null;\n\n try {\n const mime = inferMimeType(node.name);\n const blob = await readZipEntryBlob(zip.value, node.path, mime !== 'application/octet-stream' ? mime : undefined);\n const blobUrl = URL.createObjectURL(blob);\n selected.value = { path: node.path, name: node.name, size: node.size, blobUrl };\n // 移动端切换到预览 tab\n splitRef.value?.switchTab('right');\n } catch (err) {\n console.error(err);\n previewError.value = '条目读取失败';\n } finally {\n previewLoading.value = false;\n }\n};\n\n/** 为嵌入的 FilePreviewContent 构建 files 数组 */\nconst previewFiles = computed(() => {\n if (!selected.value) return [];\n return [{ name: selected.value.name, url: selected.value.blobUrl, type: inferMimeType(selected.value.name) }];\n});\n</script>\n\n<template>\n <div v-if=\"loading\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\" />\n </div>\n\n <RendererError v-else-if=\"error || !tree\" :message=\"error || t('zip.parse_failed')\" />\n\n <template v-else>\n <ResizableSplit\n ref=\"splitRef\"\n :initial-left-width=\"280\"\n :min-left-width=\"180\"\n :max-left-width=\"560\"\n storage-key=\"vfp-zip-split-left\"\n mobile-tab-mode\n left-tab-label=\"文件树\"\n right-tab-label=\"预览\"\n >\n <template #left>\n <div class=\"vfp-w-full vfp-h-full vfp-overflow-auto\">\n <TreeItem\n v-for=\"child in tree.children || []\"\n :key=\"child.path\"\n :node=\"child\"\n :depth=\"0\"\n :selected-path=\"selected?.path ?? null\"\n :expanded=\"expanded\"\n @toggle=\"handleToggle\"\n @select=\"handleSelect\"\n @hover=\"handleHover\"\n @leave=\"handleLeave\"\n />\n </div>\n </template>\n\n <template #right>\n <div class=\"vfp-w-full vfp-h-full vfp-flex vfp-flex-col\">\n <div v-if=\"!selected\" class=\"vfp-flex-1 vfp-flex vfp-items-center vfp-justify-center vfp-text-fg-muted vfp-text-sm vfp-p-6\">\n 从左侧选择一个文件以预览\n </div>\n <div v-else-if=\"previewLoading\" class=\"vfp-flex-1 vfp-flex vfp-items-center vfp-justify-center\">\n <div class=\"vfp-w-8 vfp-h-8 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\" />\n </div>\n <div v-else-if=\"previewError\" class=\"vfp-flex-1 vfp-flex vfp-items-center vfp-justify-center vfp-text-fg-secondary\">{{ previewError }}</div>\n <template v-else>\n <div class=\"vfp-flex-1 vfp-min-h-0 vfp-overflow-hidden vfp-flex vfp-relative vfp-z-0\">\n <LazyFilePreviewContent\n mode=\"embed\"\n :files=\"previewFiles\"\n :current-index=\"0\"\n :zip-nesting-depth=\"nestingDepth + 1\"\n />\n </div>\n </template>\n </div>\n </template>\n </ResizableSplit>\n\n <!-- 文件名 hover tooltip (teleport 到 body 避免被滚动区裁剪) -->\n <Teleport to=\"body\">\n <div\n v-if=\"hoverTip\"\n class=\"vfp-zip-tip\"\n :style=\"{ left: hoverTip.x + 'px', top: hoverTip.y + 'px' }\"\n >\n {{ hoverTip.text }}\n </div>\n </Teleport>\n </template>\n</template>\n\n<style>\n/* 全局 tooltip(不能 scoped,因 Teleport 到 body) */\n.vfp-zip-tip {\n position: fixed;\n z-index: 9999;\n pointer-events: none;\n transform: translateY(-50%);\n padding: 4px 8px;\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 4px;\n white-space: nowrap;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n</style>\n"],"names":["props","__props","containerRef","ref","leftWidth","saved","dragging","isDesktop","activeTab","mq","mqHandler","leftStyle","computed","onMove","e","rect","x","cap","effectiveMax","onUp","onDividerDown","onMounted","onBeforeUnmount","watch","w","switchTab","tab","__expose","_createElementBlock","_Fragment","_createElementVNode","_hoisted_1","_normalizeClass","_withDirectives","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_hoisted_4","__default__","emit","__emit","padStyle","isOpen","isSelected","fileIcon","ft","getFileType","FileImage","FileText","FileCode","FileIcon","handleEnter","handleLeave","_cache","$event","_createVNode","_unref","ChevronRight","_createBlock","_resolveDynamicComponent","FolderOpen","Folder","_toDisplayString","_openBlock","_renderList","child","_component_ZipTreeItem","n","t","r","LazyFilePreviewContent","defineAsyncComponent","useTranslator","fetcher","useFetcher","zip","tree","loading","error","expanded","selected","previewLoading","previewError","hoverTip","splitRef","revokeCurrent","_a","load","res","buf","z","loadZip","entries","listZipEntries","root","buildZipTree","init","c","err","newUrl","totalStats","files","dirs","size","walk","s","handleToggle","path","next","handleHover","text","handleSelect","node","mime","inferMimeType","blob","readZipEntryBlob","blobUrl","previewFiles","RendererError","ResizableSplit","TreeItem","_hoisted_5","_hoisted_6","_hoisted_7","_Teleport"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent as k, ref as d, onMounted as x, watch as b, onBeforeUnmount as w, openBlock as f, createBlock as T, createElementBlock as m, createElementVNode as n, toDisplayString as E, unref as C, createCommentVNode as D } from "vue";
|
|
2
2
|
import j from "video.js";
|
|
3
|
-
import { u as B, b as _ } from "./index-
|
|
4
|
-
import { _ as N } from "./RendererError.vue_vue_type_script_setup_true_lang-
|
|
3
|
+
import { u as B, b as _ } from "./index-D9QQL7U6.mjs";
|
|
4
|
+
import { _ as N } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
5
5
|
const P = {
|
|
6
6
|
key: 1,
|
|
7
7
|
class: "vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full"
|
|
@@ -114,4 +114,4 @@ const P = {
|
|
|
114
114
|
export {
|
|
115
115
|
F as default
|
|
116
116
|
};
|
|
117
|
-
//# sourceMappingURL=index-
|
|
117
|
+
//# sourceMappingURL=index-By6vGdnK.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-By6vGdnK.mjs","sources":["../../src/renderers/Video/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, onMounted, onBeforeUnmount, watch } from 'vue';\nimport videojs from 'video.js';\nimport 'video.js/dist/video-js.css';\nimport { getVideoMimeType } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\n\ntype VideoJsPlayer = ReturnType<typeof videojs>;\n\nconst props = defineProps<{\n url: string;\n fileName?: string;\n}>();\n\nconst { t } = useTranslator();\n\ninterface ErrorState {\n title: string;\n detail: string;\n}\n\n// 浏览器原生不支持的视频容器(无论编码,<video> 都无法播放)\nconst BROWSER_UNSUPPORTED_EXTS = new Set(['avi', 'wmv', 'flv']);\n\nconst error = ref<ErrorState | null>(null);\nconst isLoading = ref(true);\nconst videoContainerRef = ref<HTMLDivElement | null>(null);\nlet player: VideoJsPlayer | null = null;\n\nconst getVideoExt = (url: string, fileName?: string): string => {\n const source = fileName || url;\n return source.split('.').pop()?.toLowerCase().split('?')[0] || '';\n};\n\nconst initPlayer = () => {\n if (!videoContainerRef.value || player) return;\n\n const videoExt = getVideoExt(props.url, props.fileName);\n\n // 已知浏览器不支持的容器:跳过 videojs 初始化,直接展示友好提示\n if (BROWSER_UNSUPPORTED_EXTS.has(videoExt)) {\n error.value = {\n title: t.value('video.unsupported_title'),\n detail: t.value('video.unsupported_detail', { format: videoExt.toUpperCase() }),\n };\n isLoading.value = false;\n return;\n }\n\n const videoElement = document.createElement('video-js');\n videoElement.classList.add('vjs-big-play-centered', 'vjs-theme-apple');\n videoContainerRef.value.appendChild(videoElement);\n\n const videoType = getVideoMimeType(props.url);\n\n // 为特定格式提供多个 MIME 类型作为备用\n let sources: Array<{ src: string; type: string }>;\n\n if (videoType === 'video/quicktime') {\n // MOV 格式 fallback\n sources = [\n { src: props.url, type: 'video/quicktime' },\n { src: props.url, type: 'video/mp4' }\n ];\n } else {\n sources = [{ src: props.url, type: videoType }];\n }\n\n player = videojs(videoElement, {\n controls: true,\n fill: true,\n preload: 'auto',\n controlBar: {\n children: [\n 'playToggle',\n 'volumePanel',\n 'currentTimeDisplay',\n 'timeDivider',\n 'durationDisplay',\n 'progressControl',\n 'remainingTimeDisplay',\n 'fullscreenToggle',\n ],\n volumePanel: {\n inline: false,\n },\n },\n html5: {\n vhs: {\n overrideNative: true,\n },\n nativeVideoTracks: false,\n nativeAudioTracks: false,\n nativeTextTracks: false,\n },\n sources,\n });\n\n const videoEl = player.el().querySelector('video');\n if (videoEl) {\n (videoEl as HTMLVideoElement).style.objectFit = 'contain';\n }\n\n player.on('loadeddata', () => {\n isLoading.value = false;\n });\n\n player.on('error', () => {\n const err = player?.error();\n console.warn('[VideoRenderer] Video playback error:', err?.message || 'Unknown error');\n\n // MEDIA_ERR_SRC_NOT_SUPPORTED(code=4):编码或容器层面浏览器解不了\n if (err?.code === 4) {\n error.value = {\n title: t.value('video.unsupported_title'),\n detail: t.value('video.unsupported_detail', {\n format: videoExt ? videoExt.toUpperCase() : t.value('common.unknown_error'),\n }),\n };\n } else {\n error.value = {\n title: t.value('video.load_failed'),\n detail: err?.message || t.value('common.unknown_error'),\n };\n }\n isLoading.value = false;\n });\n};\n\nonMounted(initPlayer);\n\nwatch(\n () => props.url,\n () => {\n if (player && !player.isDisposed()) {\n player.src({ src: props.url, type: getVideoMimeType(props.url) });\n }\n }\n);\n\nonBeforeUnmount(() => {\n if (player && !player.isDisposed()) {\n player.dispose();\n player = null;\n }\n});\n</script>\n\n<template>\n <RendererError v-if=\"error\" :message=\"error.title\" :detail=\"error.detail\" />\n\n <div v-else class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-w-full vfp-h-full vfp-relative\">\n <div\n v-if=\"isLoading\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-bg-surface-3 vfp-backdrop-blur-sm vfp-z-10\"\n >\n <div class=\"vfp-text-center\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-mx-auto vfp-mb-3 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n <p class=\"vfp-text-sm vfp-text-fg-secondary vfp-font-medium\">{{ t('video.loading') }}</p>\n </div>\n </div>\n\n <div\n ref=\"videoContainerRef\"\n class=\"vfp-overflow-hidden vfp-w-full vfp-h-full\"\n style=\"box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05)\"\n />\n </div>\n </div>\n</template>\n"],"names":["props","__props","t","useTranslator","BROWSER_UNSUPPORTED_EXTS","error","ref","isLoading","videoContainerRef","player","getVideoExt","url","fileName","_a","onMounted","videoExt","videoElement","videoType","getVideoMimeType","sources","videojs","videoEl","err","watch","onBeforeUnmount","_createBlock","RendererError","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_toDisplayString","_unref"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineComponent as U, ref as f, watch as te, onBeforeUnmount as oe, openBlock as m, createElementBlock as _, createBlock as ae, createCommentVNode as C, createElementVNode as p, normalizeStyle as S, toDisplayString as re, unref as $, createVNode as P, h } from "vue";
|
|
2
2
|
import { X as le } from "lucide-vue-next";
|
|
3
3
|
import "foliate-js/view.js";
|
|
4
|
-
import { u as ne, a as se, _ as ie } from "./index-
|
|
5
|
-
import { _ as fe } from "./RendererError.vue_vue_type_script_setup_true_lang-
|
|
4
|
+
import { u as ne, a as se, _ as ie } from "./index-D9QQL7U6.mjs";
|
|
5
|
+
import { _ as fe } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
6
6
|
const pe = { class: "vfp-relative vfp-w-full vfp-h-full vfp-flex vfp-justify-center vfp-bg-surface-1 vfp-overflow-hidden" }, ce = {
|
|
7
7
|
key: 1,
|
|
8
8
|
class: "vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-z-10"
|
|
@@ -199,4 +199,4 @@ const pe = { class: "vfp-relative vfp-w-full vfp-h-full vfp-flex vfp-justify-cen
|
|
|
199
199
|
export {
|
|
200
200
|
ke as default
|
|
201
201
|
};
|
|
202
|
-
//# sourceMappingURL=index-
|
|
202
|
+
//# sourceMappingURL=index-C-Mu0fvH.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-BHi1WRQZ.mjs","sources":["../../src/renderers/Mobi/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onBeforeUnmount, defineComponent, h, type PropType } from 'vue';\nimport { X } from 'lucide-vue-next';\nimport 'foliate-js/view.js';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\ninterface TocItem {\n label: string;\n href?: string;\n subitems?: TocItem[];\n}\n\ninterface FoliateView extends HTMLElement {\n book: {\n sections: unknown[];\n toc?: TocItem[];\n destroy?: () => void;\n } | null;\n renderer: HTMLElement & {\n setStyles?: (css: string) => void;\n next?: () => Promise<void>;\n page?: number;\n pages?: number;\n };\n open(target: string | Blob | File | ArrayBuffer): Promise<void>;\n goTo(target: number | string): Promise<void>;\n prev(distance?: number): Promise<void>;\n next(distance?: number): Promise<void>;\n}\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\nconst props = defineProps<{ url: string }>();\nconst emit = defineEmits<{\n (e: 'chapterChange', current: number, total: number): void;\n (e: 'fullWidthChange', isFullWidth: boolean): void;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\nconst hostRef = ref<HTMLDivElement | null>(null);\nlet viewInstance: FoliateView | null = null;\nlet totalLocations = 1;\n\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst toc = ref<TocItem[]>([]);\nconst showToc = ref(false);\nconst activeTocHref = ref('');\nconst isFullWidth = ref(false);\n\nconst reportProgress = (current: number, total: number) => {\n if (total > 0) totalLocations = total;\n emit('chapterChange', Math.max(1, current + 1), totalLocations);\n};\n\nconst prevPage = () => { viewInstance?.prev().catch(() => {}); };\nconst nextPage = () => { viewInstance?.next().catch(() => {}); };\nconst toggleToc = () => { showToc.value = !showToc.value; };\nconst toggleFullWidth = () => {\n isFullWidth.value = !isFullWidth.value;\n emit('fullWidthChange', isFullWidth.value);\n if (viewInstance?.renderer) {\n viewInstance.renderer.setAttribute('max-inline-size', isFullWidth.value ? '9999' : '720');\n }\n};\n\nconst handleTocClick = (href: string) => {\n activeTocHref.value = href;\n showToc.value = false;\n viewInstance?.goTo(href).catch(() => {});\n};\n\ndefineExpose({ prevPage, nextPage, toggleFullWidth, toggleToc });\n\nconst load = async () => {\n const host = hostRef.value;\n if (!host) return;\n loading.value = true;\n error.value = null;\n toc.value = [];\n showToc.value = false;\n activeTocHref.value = '';\n host.replaceChildren();\n viewInstance = null;\n\n try {\n const view = document.createElement('foliate-view') as FoliateView;\n host.appendChild(view);\n viewInstance = view;\n\n // 先注册事件\n view.addEventListener('relocate', (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (!detail) return;\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 const sections = viewInstance?.book?.sections ?? [];\n const idx = detail.index ?? 0;\n const frac = detail.fraction ?? 0;\n const total = Math.max(sections.length, 1);\n reportProgress(Math.round((idx + frac) / total * total), total);\n }\n const tocItem = detail.tocItem as { href?: string } | undefined;\n if (tocItem?.href) activeTocHref.value = tocItem.href;\n });\n\n const res = await fetcher.value(props.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(props.url, window.location.href);\n const base = u.pathname.split('/').pop();\n if (base) name = decodeURIComponent(base);\n } catch { /* blob: URL */ }\n\n await view.open(new File([blob], name));\n\n // 配置 paginator:paginated 模式 + 动画\n const renderer = view.renderer;\n if (renderer) {\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 await renderer.next?.();\n }\n\n toc.value = (view.book?.toc ?? []) as TocItem[];\n loading.value = false;\n reportProgress(0, view.book?.sections.length ?? 1);\n } catch (err) {\n console.error('MOBI/AZW3 加载错误:', err);\n error.value = t.value('mobi.load_failed');\n loading.value = false;\n }\n};\n\nwatch(() => props.url, (newUrl) => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (newUrl) load();\n}, { immediate: true });\nonBeforeUnmount(() => {\n try { viewInstance?.book?.destroy?.(); } catch { /* ignore */ }\n viewInstance = null;\n hostRef.value?.replaceChildren();\n});\n</script>\n\n<template>\n <div\n class=\"vfp-relative vfp-w-full vfp-h-full vfp-flex vfp-justify-center vfp-bg-surface-1 vfp-overflow-hidden\"\n >\n <RendererError v-if=\"error\" :message=\"error\" class=\"vfp-absolute vfp-inset-0\" />\n\n <div\n v-if=\"loading && !error\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-z-10\"\n >\n <div class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\" />\n </div>\n\n <!-- 目录侧栏 -->\n <div\n v-if=\"toc.length > 0\"\n class=\"vfp-absolute vfp-inset-0 vfp-z-20 vfp-flex vfp-transition-opacity vfp-duration-300\"\n :style=\"{ opacity: showToc ? 1 : 0, pointerEvents: showToc ? 'auto' : 'none' }\"\n >\n <div\n class=\"vfp-w-72 vfp-max-w-[80%] vfp-h-full vfp-bg-surface-overlay vfp-backdrop-blur-xl vfp-border-r vfp-border-line-weak vfp-flex vfp-flex-col vfp-shadow-2xl vfp-transition-transform vfp-duration-300\"\n :style=\"{ transform: showToc ? 'translateX(0)' : 'translateX(-100%)' }\"\n >\n <div class=\"vfp-flex vfp-items-center vfp-justify-between vfp-px-4 vfp-py-3 vfp-border-b vfp-border-line-weak vfp-flex-shrink-0\">\n <span class=\"vfp-text-fg-primary vfp-font-medium vfp-text-sm\">{{ t('toolbar.toc') }}</span>\n <button class=\"toc-close-btn\" @click=\"showToc = false\">\n <X class=\"vfp-w-4 vfp-h-4\" />\n </button>\n </div>\n <div class=\"vfp-flex-1 vfp-overflow-y-auto vfp-py-4 vfp-px-1\">\n <MobiTocList :items=\"toc\" :active-href=\"activeTocHref\" @select=\"handleTocClick\" />\n </div>\n </div>\n <div\n class=\"vfp-flex-1 vfp-transition-opacity vfp-duration-300\"\n :style=\"{ background: showToc ? 'rgba(0,0,0,0.3)' : 'transparent' }\"\n @click=\"showToc = false\"\n />\n </div>\n\n <div\n v-if=\"!error\"\n ref=\"hostRef\"\n class=\"vfp-h-full vfp-bg-surface-toolbar vfp-shadow-lg\"\n :style=\"{ width: isFullWidth ? '100%' : A4_WIDTH + 'px', maxWidth: '100%', transition: 'width 0.3s ease' }\"\n />\n </div>\n</template>\n\n<script lang=\"ts\">\ninterface TocItemLocal {\n label: string;\n href?: string;\n subitems?: TocItemLocal[];\n}\n\nconst MobiTocList = defineComponent({\n name: 'MobiTocList',\n props: {\n items: { type: Array as PropType<TocItemLocal[]>, required: true },\n activeHref: { type: String, default: '' },\n depth: { type: Number, default: 0 },\n },\n emits: ['select'],\n render(): ReturnType<typeof h> {\n return h(\n 'ul',\n { style: { listStyle: 'none', padding: 0, margin: this.depth > 0 ? '0 0 0 16px' : 0 } },\n this.items.map((item: TocItemLocal, i: number) =>\n h('li', { key: `${item.href ?? item.label}-${i}` }, [\n item.href\n ? h(\n 'button',\n {\n onClick: () => this.$emit('select', item.href),\n class: [\n 'vfp-w-full vfp-text-left vfp-py-2 vfp-px-3 vfp-text-sm vfp-rounded vfp-transition-all vfp-truncate',\n this.activeHref === item.href\n ? 'vfp-text-fg-primary vfp-bg-surface-3 vfp-font-medium'\n : 'vfp-text-fg-secondary hover:vfp-text-fg-primary hover:vfp-bg-surface-2',\n ],\n title: item.label,\n style: 'background: none; border: none; cursor: pointer',\n },\n item.label?.trim()\n )\n : h(\n 'div',\n { class: 'vfp-w-full vfp-py-2 vfp-px-3 vfp-text-sm vfp-text-fg-tertiary vfp-truncate' },\n item.label?.trim()\n ),\n item.subitems?.length\n ? h(MobiTocList, {\n items: item.subitems,\n activeHref: this.activeHref,\n depth: (this.depth as number) + 1,\n onSelect: (href: string) => this.$emit('select', href),\n })\n : null,\n ])\n )\n );\n },\n});\n\nexport default { name: 'MobiRenderer' };\n</script>\n\n<style scoped>\n.toc-close-btn {\n color: var(--fp-fg-tertiary);\n background: none;\n border: none;\n cursor: pointer;\n transition: color 0.15s;\n}\n.toc-close-btn:hover { color: #fff; }\n</style>\n"],"names":["MobiTocList","defineComponent","item","i","_a","_b","_c","href","__default__","READER_CSS","props","__props","emit","__emit","t","useTranslator","fetcher","useFetcher","hostRef","ref","viewInstance","totalLocations","loading","error","toc","showToc","activeTocHref","isFullWidth","reportProgress","current","total","prevPage","nextPage","toggleToc","toggleFullWidth","handleTocClick","__expose","load","host","view","e","detail","loc","sections","idx","frac","tocItem","res","blob","name","base","renderer","_d","err","watch","newUrl","onBeforeUnmount","_openBlock","_createElementBlock","_hoisted_1","_createBlock","RendererError","_hoisted_2","_cache","_createElementVNode","_normalizeStyle","_hoisted_3","_hoisted_4","_toDisplayString","_unref","_createVNode","X","_hoisted_5"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-C-Mu0fvH.mjs","sources":["../../src/renderers/Mobi/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onBeforeUnmount, defineComponent, h, type PropType } from 'vue';\nimport { X } from 'lucide-vue-next';\nimport 'foliate-js/view.js';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport RendererError from '../RendererError.vue';\n\ninterface TocItem {\n label: string;\n href?: string;\n subitems?: TocItem[];\n}\n\ninterface FoliateView extends HTMLElement {\n book: {\n sections: unknown[];\n toc?: TocItem[];\n destroy?: () => void;\n } | null;\n renderer: HTMLElement & {\n setStyles?: (css: string) => void;\n next?: () => Promise<void>;\n page?: number;\n pages?: number;\n };\n open(target: string | Blob | File | ArrayBuffer): Promise<void>;\n goTo(target: number | string): Promise<void>;\n prev(distance?: number): Promise<void>;\n next(distance?: number): Promise<void>;\n}\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\nconst props = defineProps<{ url: string }>();\nconst emit = defineEmits<{\n (e: 'chapterChange', current: number, total: number): void;\n (e: 'fullWidthChange', isFullWidth: boolean): void;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\nconst hostRef = ref<HTMLDivElement | null>(null);\nlet viewInstance: FoliateView | null = null;\nlet totalLocations = 1;\n\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst toc = ref<TocItem[]>([]);\nconst showToc = ref(false);\nconst activeTocHref = ref('');\nconst isFullWidth = ref(false);\n\nconst reportProgress = (current: number, total: number) => {\n if (total > 0) totalLocations = total;\n emit('chapterChange', Math.max(1, current + 1), totalLocations);\n};\n\nconst prevPage = () => { viewInstance?.prev().catch(() => {}); };\nconst nextPage = () => { viewInstance?.next().catch(() => {}); };\nconst toggleToc = () => { showToc.value = !showToc.value; };\nconst toggleFullWidth = () => {\n isFullWidth.value = !isFullWidth.value;\n emit('fullWidthChange', isFullWidth.value);\n if (viewInstance?.renderer) {\n viewInstance.renderer.setAttribute('max-inline-size', isFullWidth.value ? '9999' : '720');\n }\n};\n\nconst handleTocClick = (href: string) => {\n activeTocHref.value = href;\n showToc.value = false;\n viewInstance?.goTo(href).catch(() => {});\n};\n\ndefineExpose({ prevPage, nextPage, toggleFullWidth, toggleToc });\n\nconst load = async () => {\n const host = hostRef.value;\n if (!host) return;\n loading.value = true;\n error.value = null;\n toc.value = [];\n showToc.value = false;\n activeTocHref.value = '';\n host.replaceChildren();\n viewInstance = null;\n\n try {\n const view = document.createElement('foliate-view') as FoliateView;\n host.appendChild(view);\n viewInstance = view;\n\n // 先注册事件\n view.addEventListener('relocate', (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (!detail) return;\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 const sections = viewInstance?.book?.sections ?? [];\n const idx = detail.index ?? 0;\n const frac = detail.fraction ?? 0;\n const total = Math.max(sections.length, 1);\n reportProgress(Math.round((idx + frac) / total * total), total);\n }\n const tocItem = detail.tocItem as { href?: string } | undefined;\n if (tocItem?.href) activeTocHref.value = tocItem.href;\n });\n\n const res = await fetcher.value(props.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(props.url, window.location.href);\n const base = u.pathname.split('/').pop();\n if (base) name = decodeURIComponent(base);\n } catch { /* blob: URL */ }\n\n await view.open(new File([blob], name));\n\n // 配置 paginator:paginated 模式 + 动画\n const renderer = view.renderer;\n if (renderer) {\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 await renderer.next?.();\n }\n\n toc.value = (view.book?.toc ?? []) as TocItem[];\n loading.value = false;\n reportProgress(0, view.book?.sections.length ?? 1);\n } catch (err) {\n console.error('MOBI/AZW3 加载错误:', err);\n error.value = t.value('mobi.load_failed');\n loading.value = false;\n }\n};\n\nwatch(() => props.url, (newUrl) => {\n // 只有 URL 有效时才加载(避免空字符串或已 revoke 的 blob URL)\n if (newUrl) load();\n}, { immediate: true });\nonBeforeUnmount(() => {\n try { viewInstance?.book?.destroy?.(); } catch { /* ignore */ }\n viewInstance = null;\n hostRef.value?.replaceChildren();\n});\n</script>\n\n<template>\n <div\n class=\"vfp-relative vfp-w-full vfp-h-full vfp-flex vfp-justify-center vfp-bg-surface-1 vfp-overflow-hidden\"\n >\n <RendererError v-if=\"error\" :message=\"error\" class=\"vfp-absolute vfp-inset-0\" />\n\n <div\n v-if=\"loading && !error\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-z-10\"\n >\n <div class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\" />\n </div>\n\n <!-- 目录侧栏 -->\n <div\n v-if=\"toc.length > 0\"\n class=\"vfp-absolute vfp-inset-0 vfp-z-20 vfp-flex vfp-transition-opacity vfp-duration-300\"\n :style=\"{ opacity: showToc ? 1 : 0, pointerEvents: showToc ? 'auto' : 'none' }\"\n >\n <div\n class=\"vfp-w-72 vfp-max-w-[80%] vfp-h-full vfp-bg-surface-overlay vfp-backdrop-blur-xl vfp-border-r vfp-border-line-weak vfp-flex vfp-flex-col vfp-shadow-2xl vfp-transition-transform vfp-duration-300\"\n :style=\"{ transform: showToc ? 'translateX(0)' : 'translateX(-100%)' }\"\n >\n <div class=\"vfp-flex vfp-items-center vfp-justify-between vfp-px-4 vfp-py-3 vfp-border-b vfp-border-line-weak vfp-flex-shrink-0\">\n <span class=\"vfp-text-fg-primary vfp-font-medium vfp-text-sm\">{{ t('toolbar.toc') }}</span>\n <button class=\"toc-close-btn\" @click=\"showToc = false\">\n <X class=\"vfp-w-4 vfp-h-4\" />\n </button>\n </div>\n <div class=\"vfp-flex-1 vfp-overflow-y-auto vfp-py-4 vfp-px-1\">\n <MobiTocList :items=\"toc\" :active-href=\"activeTocHref\" @select=\"handleTocClick\" />\n </div>\n </div>\n <div\n class=\"vfp-flex-1 vfp-transition-opacity vfp-duration-300\"\n :style=\"{ background: showToc ? 'rgba(0,0,0,0.3)' : 'transparent' }\"\n @click=\"showToc = false\"\n />\n </div>\n\n <div\n v-if=\"!error\"\n ref=\"hostRef\"\n class=\"vfp-h-full vfp-bg-surface-toolbar vfp-shadow-lg\"\n :style=\"{ width: isFullWidth ? '100%' : A4_WIDTH + 'px', maxWidth: '100%', transition: 'width 0.3s ease' }\"\n />\n </div>\n</template>\n\n<script lang=\"ts\">\ninterface TocItemLocal {\n label: string;\n href?: string;\n subitems?: TocItemLocal[];\n}\n\nconst MobiTocList = defineComponent({\n name: 'MobiTocList',\n props: {\n items: { type: Array as PropType<TocItemLocal[]>, required: true },\n activeHref: { type: String, default: '' },\n depth: { type: Number, default: 0 },\n },\n emits: ['select'],\n render(): ReturnType<typeof h> {\n return h(\n 'ul',\n { style: { listStyle: 'none', padding: 0, margin: this.depth > 0 ? '0 0 0 16px' : 0 } },\n this.items.map((item: TocItemLocal, i: number) =>\n h('li', { key: `${item.href ?? item.label}-${i}` }, [\n item.href\n ? h(\n 'button',\n {\n onClick: () => this.$emit('select', item.href),\n class: [\n 'vfp-w-full vfp-text-left vfp-py-2 vfp-px-3 vfp-text-sm vfp-rounded vfp-transition-all vfp-truncate',\n this.activeHref === item.href\n ? 'vfp-text-fg-primary vfp-bg-surface-3 vfp-font-medium'\n : 'vfp-text-fg-secondary hover:vfp-text-fg-primary hover:vfp-bg-surface-2',\n ],\n title: item.label,\n style: 'background: none; border: none; cursor: pointer',\n },\n item.label?.trim()\n )\n : h(\n 'div',\n { class: 'vfp-w-full vfp-py-2 vfp-px-3 vfp-text-sm vfp-text-fg-tertiary vfp-truncate' },\n item.label?.trim()\n ),\n item.subitems?.length\n ? h(MobiTocList, {\n items: item.subitems,\n activeHref: this.activeHref,\n depth: (this.depth as number) + 1,\n onSelect: (href: string) => this.$emit('select', href),\n })\n : null,\n ])\n )\n );\n },\n});\n\nexport default { name: 'MobiRenderer' };\n</script>\n\n<style scoped>\n.toc-close-btn {\n color: var(--fp-fg-tertiary);\n background: none;\n border: none;\n cursor: pointer;\n transition: color 0.15s;\n}\n.toc-close-btn:hover { color: #fff; }\n</style>\n"],"names":["MobiTocList","defineComponent","item","i","_a","_b","_c","href","__default__","READER_CSS","props","__props","emit","__emit","t","useTranslator","fetcher","useFetcher","hostRef","ref","viewInstance","totalLocations","loading","error","toc","showToc","activeTocHref","isFullWidth","reportProgress","current","total","prevPage","nextPage","toggleToc","toggleFullWidth","handleTocClick","__expose","load","host","view","e","detail","loc","sections","idx","frac","tocItem","res","blob","name","base","renderer","_d","err","watch","newUrl","onBeforeUnmount","_openBlock","_createElementBlock","_hoisted_1","_createBlock","RendererError","_hoisted_2","_cache","_createElementVNode","_normalizeStyle","_hoisted_3","_hoisted_4","_toDisplayString","_unref","_createVNode","X","_hoisted_5"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent as de, ref as r, watch as H, onBeforeUnmount as Q, onMounted as pe, computed as I, openBlock as m, createElementBlock as _, normalizeStyle as J, createVNode as he, unref as ee, createElementVNode as S, createTextVNode as me, toDisplayString as Y, createCommentVNode as w, createBlock as te, normalizeClass as ge } from "vue";
|
|
2
2
|
import { Loader2 as ye } from "lucide-vue-next";
|
|
3
|
-
import { u as be, G as xe, q as we, C as Le } from "./index-
|
|
4
|
-
import { _ as ae } from "./RendererError.vue_vue_type_script_setup_true_lang-
|
|
3
|
+
import { u as be, G as xe, q as we, C as Le } from "./index-D9QQL7U6.mjs";
|
|
4
|
+
import { _ as ae } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
5
5
|
const Ce = {
|
|
6
6
|
key: 0,
|
|
7
7
|
class: "vfp-absolute vfp-inset-0 vfp-flex vfp-flex-col vfp-items-center vfp-justify-center vfp-bg-surface-1/80 vfp-z-10"
|
|
@@ -276,4 +276,4 @@ const Ce = {
|
|
|
276
276
|
export {
|
|
277
277
|
je as default
|
|
278
278
|
};
|
|
279
|
-
//# sourceMappingURL=index-
|
|
279
|
+
//# sourceMappingURL=index-CGSq7-Q-.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-DT9dPz8I.mjs","sources":["../../src/renderers/Image/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue';\nimport { Loader2 } from 'lucide-vue-next';\nimport { formatFileSize, detectImageFormat, getLoaderForMimeType } from '@eternalheart/file-preview-core';\nimport type { PreviewFile } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n zoom: number;\n rotation: number;\n resetKey?: number;\n fileSize?: number;\n file?: PreviewFile | File;\n}>();\n\nconst emit = defineEmits<{\n (e: 'zoomChange', zoom: number): void;\n (e: 'naturalWidthChange', width: number): void;\n (e: 'naturalHeightChange', height: number): void;\n}>();\n\nconst { t } = useTranslator();\n\nconst loaded = ref(false);\nconst error = ref<string | null>(null);\nconst decoding = ref(false);\nconst decodeProgress = ref(0);\nconst decodeError = ref<string | null>(null);\nconst imageSrc = ref<string>('');\nconst currentPage = ref(1);\nconst totalPages = ref(1);\nconst isRawThumbnail = ref(false);\nconst position = ref({ x: 0, y: 0 });\nconst isDragging = ref(false);\nlet dragStart = { x: 0, y: 0 };\nconst internalZoom = ref(1);\nconst naturalSize = ref({ width: 0, height: 0 });\n\nconst imgRef = ref<HTMLImageElement | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet blobUrl: string | null = null;\nlet fileBlobCache: Blob | null = null;\nlet loaderCache: any = null;\nconst pageCache = new Map<number, string>();\n\nlet isTouchDevice = false;\nlet touchStartDistance = 0;\nlet touchStartZoom = 1;\nlet touchStartPos = { x: 0, y: 0 };\nlet lastTapTime = 0;\n\n// 解码逻辑\nwatch(\n () => [props.url, props.file] as const,\n async () => {\n // 重置状态:清空 src 以避免上一张图片的 onLoad/onError 误触发到新文件\n imageSrc.value = '';\n loaded.value = false;\n error.value = null;\n decoding.value = false;\n decodeError.value = null;\n decodeProgress.value = 0;\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n currentPage.value = 1;\n totalPages.value = 1;\n isRawThumbnail.value = false;\n\n // 清理旧的 blob URL 与缓存\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n blobUrl = null;\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n fileBlobCache = null;\n loaderCache = null;\n\n // 如果没有 file 对象,直接使用 url\n if (!props.file) {\n imageSrc.value = props.url;\n return;\n }\n\n try {\n // 检测图片格式\n const mimeType = await detectImageFormat(props.file);\n const loader = await getLoaderForMimeType(mimeType);\n\n // 如果不需要解码,直接使用原 URL\n if (!loader || !(await loader.needsDecode(mimeType))) {\n imageSrc.value = props.url;\n return;\n }\n\n // 需要解码\n decoding.value = true;\n\n // 获取文件 Blob\n let fileBlob: Blob;\n if (props.file instanceof Blob) {\n fileBlob = props.file;\n } else {\n const response = await fetch(props.url);\n if (!response.ok) throw new Error('Failed to fetch file');\n fileBlob = await response.blob();\n }\n\n // 缓存 Blob 与 loader\n fileBlobCache = fileBlob;\n loaderCache = loader;\n\n // 标记 RAW 缩略图模式\n const isRaw = mimeType.startsWith('image/x-');\n if (isRaw) {\n isRawThumbnail.value = true;\n }\n\n // 获取元数据(用于检测多页 TIFF)\n if (loader.getMetadata) {\n try {\n const metadata = await loader.getMetadata(fileBlob);\n if (metadata.pageCount && metadata.pageCount > 1) {\n totalPages.value = metadata.pageCount;\n }\n } catch {\n // 忽略元数据获取失败\n }\n }\n\n // 调用 loader 解码(第 1 页 / 缩略图模式)\n const decodedBlob = await loader.decode(fileBlob, {\n page: 1,\n fullQuality: false,\n onProgress: (percent: number) => {\n decodeProgress.value = percent;\n },\n });\n\n // 生成 blob URL\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n blobUrl = url;\n pageCache.set(1, url);\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '解码失败';\n decoding.value = false;\n }\n },\n { immediate: true }\n);\n\n// 多页 TIFF 翻页\nconst handlePageChange = async (page: number) => {\n if (!fileBlobCache || !loaderCache) return;\n if (page < 1 || page > totalPages.value) return;\n\n // 命中缓存:直接切换\n const cached = pageCache.get(page);\n if (cached) {\n currentPage.value = page;\n imageSrc.value = cached;\n return;\n }\n\n // 解码新页面\n decoding.value = true;\n try {\n const decodedBlob = await loaderCache.decode(fileBlobCache, { page });\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n // LRU:缓存超过 10 页时删除最早的\n if (pageCache.size >= 10) {\n const firstKey = pageCache.keys().next().value;\n if (firstKey !== undefined) {\n const oldUrl = pageCache.get(firstKey);\n if (oldUrl) URL.revokeObjectURL(oldUrl);\n pageCache.delete(firstKey);\n }\n }\n\n pageCache.set(page, url);\n currentPage.value = page;\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '翻页解码失败';\n decoding.value = false;\n }\n};\n\n// Cleanup on unmount\nonBeforeUnmount(() => {\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n }\n});\n\nwatch(\n () => props.zoom,\n (z) => {\n internalZoom.value = z;\n }\n);\n\nwatch(\n () => props.resetKey,\n () => {\n position.value = { x: 0, y: 0 };\n }\n);\n\nconst clampPosition = (pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.value;\n if (!container || naturalSize.value.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.value.width * currentZoom;\n const imgH = naturalSize.value.height * currentZoom;\n\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n};\n\nconst handleLoad = (e: Event) => {\n loaded.value = true;\n const img = e.currentTarget as HTMLImageElement;\n naturalSize.value = { width: img.naturalWidth, height: img.naturalHeight };\n emit('naturalWidthChange', img.naturalWidth);\n emit('naturalHeightChange', img.naturalHeight);\n};\n\nconst handleError = () => {\n error.value = t.value('image.load_failed');\n loaded.value = true;\n};\n\nconst handleDoubleClick = () => {\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n emit('zoomChange', 1);\n};\n\n// 鼠标滚轮缩放\nconst handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const container = containerRef.value;\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n const prev = internalZoom.value;\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n position.value = clampPosition(\n {\n x: mouseX - scale * (mouseX - position.value.x),\n y: mouseY - scale * (mouseY - position.value.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n emit('zoomChange', newZoom);\n};\n\nonMounted(() => {\n const container = containerRef.value;\n if (container) {\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nconst handleMouseDown = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (e.button !== 0) return;\n isDragging.value = true;\n dragStart = {\n x: e.clientX - position.value.x,\n y: e.clientY - position.value.y,\n };\n};\n\nconst handleMouseMove = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (!isDragging.value) return;\n position.value = clampPosition(\n {\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n },\n internalZoom.value\n );\n};\n\nconst handleMouseUp = () => {\n if (isTouchDevice) return;\n isDragging.value = false;\n};\n\n// 触屏事件处理\nconst handleTouchStart = (e: TouchEvent) => {\n isTouchDevice = true;\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1) {\n // 单指拖拽\n isDragging.value = true;\n dragStart = {\n x: touches[0].clientX - position.value.x,\n y: touches[0].clientY - position.value.y,\n };\n\n // 双击检测\n const now = Date.now();\n if (now - lastTapTime < 300) {\n // 双击复原:居中 + 缩放100%\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n emit('zoomChange', 1);\n }\n lastTapTime = now;\n } else if (touches.length === 2) {\n // 双指缩放初始化\n isDragging.value = false;\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n touchStartDistance = distance;\n touchStartZoom = internalZoom.value;\n touchStartPos = { ...position.value };\n }\n};\n\nconst handleTouchMove = (e: TouchEvent) => {\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1 && isDragging.value) {\n // 单指拖拽\n position.value = clampPosition(\n {\n x: touches[0].clientX - dragStart.x,\n y: touches[0].clientY - dragStart.y,\n },\n internalZoom.value\n );\n } else if (touches.length === 2) {\n // 双指缩放\n const container = containerRef.value;\n if (!container) return;\n\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n\n // 最小距离变化阈值,防止抖动\n if (Math.abs(distance - touchStartDistance) < 5) return;\n\n const scale = distance / touchStartDistance;\n const newZoom = Math.max(0.01, Math.min(10, touchStartZoom * scale));\n\n // 双指中心点作为缩放原点\n const rect = container.getBoundingClientRect();\n const centerX = (touches[0].clientX + touches[1].clientX) / 2 - rect.left - rect.width / 2;\n const centerY = (touches[0].clientY + touches[1].clientY) / 2 - rect.top - rect.height / 2;\n\n const zoomScale = newZoom / internalZoom.value;\n position.value = clampPosition(\n {\n x: centerX - zoomScale * (centerX - touchStartPos.x),\n y: centerY - zoomScale * (centerY - touchStartPos.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n emit('zoomChange', newZoom);\n }\n};\n\nconst handleTouchEnd = () => {\n isDragging.value = false;\n touchStartDistance = 0;\n};\n\nconst transformStyle = computed(() => ({\n transform: `translate(${position.value.x}px, ${position.value.y}px) scale(${internalZoom.value}) rotate(${props.rotation}deg)`,\n transformOrigin: 'center',\n transition: isDragging.value ? 'none' : 'transform 0.3s ease-out',\n opacity: loaded.value && !error.value && !decodeError.value ? 1 : 0,\n}));\n\nconst sizeText = computed(() => {\n if (props.fileSize == null) return '';\n return ` · ${formatFileSize(props.fileSize)}`;\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-relative vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-overflow-hidden\"\n :style=\"{ cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none' }\"\n @mousedown=\"handleMouseDown\"\n @mousemove=\"handleMouseMove\"\n @mouseup=\"handleMouseUp\"\n @mouseleave=\"handleMouseUp\"\n >\n <!-- 解码中 -->\n <div\n v-if=\"decoding\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-flex-col vfp-items-center vfp-justify-center vfp-bg-surface-1/80 vfp-z-10\"\n >\n <Loader2 class=\"vfp-w-12 vfp-h-12 vfp-text-fg-primary vfp-animate-spin\" />\n <p class=\"vfp-mt-4 vfp-text-fg-secondary\">\n 正在解码... <span v-if=\"decodeProgress > 0\">{{ Math.round(decodeProgress) }}%</span>\n </p>\n </div>\n\n <!-- 解码错误 -->\n <RendererError v-if=\"decodeError\" :message=\"t('image.decode_failed')\" :detail=\"decodeError\" />\n\n <div v-if=\"!loaded && !error && !decoding && !decodeError\" class=\"vfp-flex vfp-items-center vfp-justify-center\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <RendererError v-if=\"error\" :message=\"error\" />\n\n <img\n v-if=\"imageSrc\"\n ref=\"imgRef\"\n :src=\"imageSrc\"\n alt=\"Preview\"\n :class=\"['vfp-max-w-none vfp-select-none', (!loaded || error || decodeError) && 'vfp-hidden']\"\n :style=\"transformStyle\"\n :draggable=\"false\"\n @load=\"handleLoad\"\n @error=\"handleError\"\n @dblclick=\"handleDoubleClick\"\n />\n\n <div\n v-if=\"loaded && !error && naturalSize.width > 0\"\n class=\"vfp-absolute vfp-bottom-2 vfp-right-3 vfp-text-[10px] vfp-text-fg-disabled hover:vfp-text-fg-secondary vfp-transition-colors vfp-pointer-events-auto vfp-select-none vfp-cursor-default\"\n >\n {{ naturalSize.width }} × {{ naturalSize.height }}{{ sizeText }}\n </div>\n\n <!-- 多页 TIFF 翻页器 -->\n <div\n v-if=\"totalPages > 1\"\n class=\"vfp-absolute vfp-bottom-2 vfp-left-1/2 -vfp-translate-x-1/2 vfp-flex vfp-items-center vfp-gap-2 vfp-px-3 vfp-py-1.5 vfp-bg-surface-toolbar vfp-border vfp-border-line vfp-rounded-lg vfp-text-sm vfp-text-fg-primary vfp-shadow-md\"\n >\n <button\n type=\"button\"\n :disabled=\"currentPage <= 1 || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage - 1)\"\n >\n 上一页\n </button>\n <span class=\"vfp-text-fg-secondary vfp-tabular-nums\">\n {{ currentPage }} / {{ totalPages }}\n </span>\n <button\n type=\"button\"\n :disabled=\"currentPage >= totalPages || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage + 1)\"\n >\n 下一页\n </button>\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","t","useTranslator","loaded","ref","error","decoding","decodeProgress","decodeError","imageSrc","currentPage","totalPages","isRawThumbnail","position","isDragging","dragStart","internalZoom","naturalSize","imgRef","containerRef","blobUrl","fileBlobCache","loaderCache","pageCache","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","watch","url","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","err","handlePageChange","page","cached","firstKey","oldUrl","onBeforeUnmount","container","handleWheelNative","z","clampPosition","pos","currentZoom","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleLoad","img","handleError","handleDoubleClick","rect","mouseX","mouseY","delta","prev","newZoom","scale","onMounted","handleTouchStart","handleTouchMove","handleTouchEnd","handleMouseDown","handleMouseMove","handleMouseUp","touches","now","distance","centerX","centerY","zoomScale","transformStyle","computed","sizeText","formatFileSize","_createElementBlock","_openBlock","_hoisted_1","_createVNode","_unref","Loader2","_createElementVNode","_hoisted_2","_createBlock","RendererError","_hoisted_4","_cache","_normalizeClass","_hoisted_6","_toDisplayString","_hoisted_7","$event","_hoisted_8","_hoisted_9","_hoisted_10"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-CGSq7-Q-.mjs","sources":["../../src/renderers/Image/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue';\nimport { Loader2 } from 'lucide-vue-next';\nimport { formatFileSize, detectImageFormat, getLoaderForMimeType } from '@eternalheart/file-preview-core';\nimport type { PreviewFile } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n zoom: number;\n rotation: number;\n resetKey?: number;\n fileSize?: number;\n file?: PreviewFile | File;\n}>();\n\nconst emit = defineEmits<{\n (e: 'zoomChange', zoom: number): void;\n (e: 'naturalWidthChange', width: number): void;\n (e: 'naturalHeightChange', height: number): void;\n}>();\n\nconst { t } = useTranslator();\n\nconst loaded = ref(false);\nconst error = ref<string | null>(null);\nconst decoding = ref(false);\nconst decodeProgress = ref(0);\nconst decodeError = ref<string | null>(null);\nconst imageSrc = ref<string>('');\nconst currentPage = ref(1);\nconst totalPages = ref(1);\nconst isRawThumbnail = ref(false);\nconst position = ref({ x: 0, y: 0 });\nconst isDragging = ref(false);\nlet dragStart = { x: 0, y: 0 };\nconst internalZoom = ref(1);\nconst naturalSize = ref({ width: 0, height: 0 });\n\nconst imgRef = ref<HTMLImageElement | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet blobUrl: string | null = null;\nlet fileBlobCache: Blob | null = null;\nlet loaderCache: any = null;\nconst pageCache = new Map<number, string>();\n\nlet isTouchDevice = false;\nlet touchStartDistance = 0;\nlet touchStartZoom = 1;\nlet touchStartPos = { x: 0, y: 0 };\nlet lastTapTime = 0;\n\n// 解码逻辑\nwatch(\n () => [props.url, props.file] as const,\n async () => {\n // 重置状态:清空 src 以避免上一张图片的 onLoad/onError 误触发到新文件\n imageSrc.value = '';\n loaded.value = false;\n error.value = null;\n decoding.value = false;\n decodeError.value = null;\n decodeProgress.value = 0;\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n currentPage.value = 1;\n totalPages.value = 1;\n isRawThumbnail.value = false;\n\n // 清理旧的 blob URL 与缓存\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n blobUrl = null;\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n fileBlobCache = null;\n loaderCache = null;\n\n // 如果没有 file 对象,直接使用 url\n if (!props.file) {\n imageSrc.value = props.url;\n return;\n }\n\n try {\n // 检测图片格式\n const mimeType = await detectImageFormat(props.file);\n const loader = await getLoaderForMimeType(mimeType);\n\n // 如果不需要解码,直接使用原 URL\n if (!loader || !(await loader.needsDecode(mimeType))) {\n imageSrc.value = props.url;\n return;\n }\n\n // 需要解码\n decoding.value = true;\n\n // 获取文件 Blob\n let fileBlob: Blob;\n if (props.file instanceof Blob) {\n fileBlob = props.file;\n } else {\n const response = await fetch(props.url);\n if (!response.ok) throw new Error('Failed to fetch file');\n fileBlob = await response.blob();\n }\n\n // 缓存 Blob 与 loader\n fileBlobCache = fileBlob;\n loaderCache = loader;\n\n // 标记 RAW 缩略图模式\n const isRaw = mimeType.startsWith('image/x-');\n if (isRaw) {\n isRawThumbnail.value = true;\n }\n\n // 获取元数据(用于检测多页 TIFF)\n if (loader.getMetadata) {\n try {\n const metadata = await loader.getMetadata(fileBlob);\n if (metadata.pageCount && metadata.pageCount > 1) {\n totalPages.value = metadata.pageCount;\n }\n } catch {\n // 忽略元数据获取失败\n }\n }\n\n // 调用 loader 解码(第 1 页 / 缩略图模式)\n const decodedBlob = await loader.decode(fileBlob, {\n page: 1,\n fullQuality: false,\n onProgress: (percent: number) => {\n decodeProgress.value = percent;\n },\n });\n\n // 生成 blob URL\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n blobUrl = url;\n pageCache.set(1, url);\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '解码失败';\n decoding.value = false;\n }\n },\n { immediate: true }\n);\n\n// 多页 TIFF 翻页\nconst handlePageChange = async (page: number) => {\n if (!fileBlobCache || !loaderCache) return;\n if (page < 1 || page > totalPages.value) return;\n\n // 命中缓存:直接切换\n const cached = pageCache.get(page);\n if (cached) {\n currentPage.value = page;\n imageSrc.value = cached;\n return;\n }\n\n // 解码新页面\n decoding.value = true;\n try {\n const decodedBlob = await loaderCache.decode(fileBlobCache, { page });\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n // LRU:缓存超过 10 页时删除最早的\n if (pageCache.size >= 10) {\n const firstKey = pageCache.keys().next().value;\n if (firstKey !== undefined) {\n const oldUrl = pageCache.get(firstKey);\n if (oldUrl) URL.revokeObjectURL(oldUrl);\n pageCache.delete(firstKey);\n }\n }\n\n pageCache.set(page, url);\n currentPage.value = page;\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '翻页解码失败';\n decoding.value = false;\n }\n};\n\n// Cleanup on unmount\nonBeforeUnmount(() => {\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n }\n});\n\nwatch(\n () => props.zoom,\n (z) => {\n internalZoom.value = z;\n }\n);\n\nwatch(\n () => props.resetKey,\n () => {\n position.value = { x: 0, y: 0 };\n }\n);\n\nconst clampPosition = (pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.value;\n if (!container || naturalSize.value.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.value.width * currentZoom;\n const imgH = naturalSize.value.height * currentZoom;\n\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n};\n\nconst handleLoad = (e: Event) => {\n loaded.value = true;\n const img = e.currentTarget as HTMLImageElement;\n naturalSize.value = { width: img.naturalWidth, height: img.naturalHeight };\n emit('naturalWidthChange', img.naturalWidth);\n emit('naturalHeightChange', img.naturalHeight);\n};\n\nconst handleError = () => {\n error.value = t.value('image.load_failed');\n loaded.value = true;\n};\n\nconst handleDoubleClick = () => {\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n emit('zoomChange', 1);\n};\n\n// 鼠标滚轮缩放\nconst handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const container = containerRef.value;\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n const prev = internalZoom.value;\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n position.value = clampPosition(\n {\n x: mouseX - scale * (mouseX - position.value.x),\n y: mouseY - scale * (mouseY - position.value.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n emit('zoomChange', newZoom);\n};\n\nonMounted(() => {\n const container = containerRef.value;\n if (container) {\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nconst handleMouseDown = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (e.button !== 0) return;\n isDragging.value = true;\n dragStart = {\n x: e.clientX - position.value.x,\n y: e.clientY - position.value.y,\n };\n};\n\nconst handleMouseMove = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (!isDragging.value) return;\n position.value = clampPosition(\n {\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n },\n internalZoom.value\n );\n};\n\nconst handleMouseUp = () => {\n if (isTouchDevice) return;\n isDragging.value = false;\n};\n\n// 触屏事件处理\nconst handleTouchStart = (e: TouchEvent) => {\n isTouchDevice = true;\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1) {\n // 单指拖拽\n isDragging.value = true;\n dragStart = {\n x: touches[0].clientX - position.value.x,\n y: touches[0].clientY - position.value.y,\n };\n\n // 双击检测\n const now = Date.now();\n if (now - lastTapTime < 300) {\n // 双击复原:居中 + 缩放100%\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n emit('zoomChange', 1);\n }\n lastTapTime = now;\n } else if (touches.length === 2) {\n // 双指缩放初始化\n isDragging.value = false;\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n touchStartDistance = distance;\n touchStartZoom = internalZoom.value;\n touchStartPos = { ...position.value };\n }\n};\n\nconst handleTouchMove = (e: TouchEvent) => {\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1 && isDragging.value) {\n // 单指拖拽\n position.value = clampPosition(\n {\n x: touches[0].clientX - dragStart.x,\n y: touches[0].clientY - dragStart.y,\n },\n internalZoom.value\n );\n } else if (touches.length === 2) {\n // 双指缩放\n const container = containerRef.value;\n if (!container) return;\n\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n\n // 最小距离变化阈值,防止抖动\n if (Math.abs(distance - touchStartDistance) < 5) return;\n\n const scale = distance / touchStartDistance;\n const newZoom = Math.max(0.01, Math.min(10, touchStartZoom * scale));\n\n // 双指中心点作为缩放原点\n const rect = container.getBoundingClientRect();\n const centerX = (touches[0].clientX + touches[1].clientX) / 2 - rect.left - rect.width / 2;\n const centerY = (touches[0].clientY + touches[1].clientY) / 2 - rect.top - rect.height / 2;\n\n const zoomScale = newZoom / internalZoom.value;\n position.value = clampPosition(\n {\n x: centerX - zoomScale * (centerX - touchStartPos.x),\n y: centerY - zoomScale * (centerY - touchStartPos.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n emit('zoomChange', newZoom);\n }\n};\n\nconst handleTouchEnd = () => {\n isDragging.value = false;\n touchStartDistance = 0;\n};\n\nconst transformStyle = computed(() => ({\n transform: `translate(${position.value.x}px, ${position.value.y}px) scale(${internalZoom.value}) rotate(${props.rotation}deg)`,\n transformOrigin: 'center',\n transition: isDragging.value ? 'none' : 'transform 0.3s ease-out',\n opacity: loaded.value && !error.value && !decodeError.value ? 1 : 0,\n}));\n\nconst sizeText = computed(() => {\n if (props.fileSize == null) return '';\n return ` · ${formatFileSize(props.fileSize)}`;\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-relative vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-overflow-hidden\"\n :style=\"{ cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none' }\"\n @mousedown=\"handleMouseDown\"\n @mousemove=\"handleMouseMove\"\n @mouseup=\"handleMouseUp\"\n @mouseleave=\"handleMouseUp\"\n >\n <!-- 解码中 -->\n <div\n v-if=\"decoding\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-flex-col vfp-items-center vfp-justify-center vfp-bg-surface-1/80 vfp-z-10\"\n >\n <Loader2 class=\"vfp-w-12 vfp-h-12 vfp-text-fg-primary vfp-animate-spin\" />\n <p class=\"vfp-mt-4 vfp-text-fg-secondary\">\n 正在解码... <span v-if=\"decodeProgress > 0\">{{ Math.round(decodeProgress) }}%</span>\n </p>\n </div>\n\n <!-- 解码错误 -->\n <RendererError v-if=\"decodeError\" :message=\"t('image.decode_failed')\" :detail=\"decodeError\" />\n\n <div v-if=\"!loaded && !error && !decoding && !decodeError\" class=\"vfp-flex vfp-items-center vfp-justify-center\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <RendererError v-if=\"error\" :message=\"error\" />\n\n <img\n v-if=\"imageSrc\"\n ref=\"imgRef\"\n :src=\"imageSrc\"\n alt=\"Preview\"\n :class=\"['vfp-max-w-none vfp-select-none', (!loaded || error || decodeError) && 'vfp-hidden']\"\n :style=\"transformStyle\"\n :draggable=\"false\"\n @load=\"handleLoad\"\n @error=\"handleError\"\n @dblclick=\"handleDoubleClick\"\n />\n\n <div\n v-if=\"loaded && !error && naturalSize.width > 0\"\n class=\"vfp-absolute vfp-bottom-2 vfp-right-3 vfp-text-[10px] vfp-text-fg-disabled hover:vfp-text-fg-secondary vfp-transition-colors vfp-pointer-events-auto vfp-select-none vfp-cursor-default\"\n >\n {{ naturalSize.width }} × {{ naturalSize.height }}{{ sizeText }}\n </div>\n\n <!-- 多页 TIFF 翻页器 -->\n <div\n v-if=\"totalPages > 1\"\n class=\"vfp-absolute vfp-bottom-2 vfp-left-1/2 -vfp-translate-x-1/2 vfp-flex vfp-items-center vfp-gap-2 vfp-px-3 vfp-py-1.5 vfp-bg-surface-toolbar vfp-border vfp-border-line vfp-rounded-lg vfp-text-sm vfp-text-fg-primary vfp-shadow-md\"\n >\n <button\n type=\"button\"\n :disabled=\"currentPage <= 1 || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage - 1)\"\n >\n 上一页\n </button>\n <span class=\"vfp-text-fg-secondary vfp-tabular-nums\">\n {{ currentPage }} / {{ totalPages }}\n </span>\n <button\n type=\"button\"\n :disabled=\"currentPage >= totalPages || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage + 1)\"\n >\n 下一页\n </button>\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","t","useTranslator","loaded","ref","error","decoding","decodeProgress","decodeError","imageSrc","currentPage","totalPages","isRawThumbnail","position","isDragging","dragStart","internalZoom","naturalSize","imgRef","containerRef","blobUrl","fileBlobCache","loaderCache","pageCache","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","watch","url","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","err","handlePageChange","page","cached","firstKey","oldUrl","onBeforeUnmount","container","handleWheelNative","z","clampPosition","pos","currentZoom","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleLoad","img","handleError","handleDoubleClick","rect","mouseX","mouseY","delta","prev","newZoom","scale","onMounted","handleTouchStart","handleTouchMove","handleTouchEnd","handleMouseDown","handleMouseMove","handleMouseUp","touches","now","distance","centerX","centerY","zoomScale","transformStyle","computed","sizeText","formatFileSize","_createElementBlock","_openBlock","_hoisted_1","_createVNode","_unref","Loader2","_createElementVNode","_hoisted_2","_createBlock","RendererError","_hoisted_4","_cache","_normalizeClass","_hoisted_6","_toDisplayString","_hoisted_7","$event","_hoisted_8","_hoisted_9","_hoisted_10"],"mappings":"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"}
|