@eternalheart/vue-file-preview 1.3.13 → 1.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/lib/FilePreviewContent.vue.d.ts +3 -0
  2. package/lib/FilePreviewContent.vue.d.ts.map +1 -1
  3. package/lib/FilePreviewEmbed.vue.d.ts +5 -0
  4. package/lib/FilePreviewEmbed.vue.d.ts.map +1 -1
  5. package/lib/FilePreviewModal.vue.d.ts.map +1 -1
  6. package/lib/chunks/{index-BHi1WRQZ.mjs → index-5hGtGeD4.mjs} +2 -2
  7. package/lib/chunks/{index-BHi1WRQZ.mjs.map → index-5hGtGeD4.mjs.map} +1 -1
  8. package/lib/chunks/{index-DBw8tvyb.mjs → index-B-f41wjl.mjs} +2 -2
  9. package/lib/chunks/{index-DBw8tvyb.mjs.map → index-B-f41wjl.mjs.map} +1 -1
  10. package/lib/chunks/{index-CYt6tpQC.mjs → index-BI32fnT-.mjs} +2 -2
  11. package/lib/chunks/{index-CYt6tpQC.mjs.map → index-BI32fnT-.mjs.map} +1 -1
  12. package/lib/chunks/{index-DTtHrdBl.mjs → index-BgdBpGYv.mjs} +2 -2
  13. package/lib/chunks/{index-DTtHrdBl.mjs.map → index-BgdBpGYv.mjs.map} +1 -1
  14. package/lib/chunks/{index-DT9dPz8I.mjs → index-BkMYaea2.mjs} +2 -2
  15. package/lib/chunks/{index-DT9dPz8I.mjs.map → index-BkMYaea2.mjs.map} +1 -1
  16. package/lib/chunks/{index-BTW1xwB9.mjs → index-BlD2p_KC.mjs} +2 -2
  17. package/lib/chunks/{index-BTW1xwB9.mjs.map → index-BlD2p_KC.mjs.map} +1 -1
  18. package/lib/chunks/{index-B2lqsdp7.mjs → index-Bz9nKaz5.mjs} +2 -2
  19. package/lib/chunks/{index-B2lqsdp7.mjs.map → index-Bz9nKaz5.mjs.map} +1 -1
  20. package/lib/chunks/{index-GGGDPwxX.mjs → index-C0OWrmU8.mjs} +2 -2
  21. package/lib/chunks/{index-GGGDPwxX.mjs.map → index-C0OWrmU8.mjs.map} +1 -1
  22. package/lib/chunks/{index-Oxwizt6-.mjs → index-CFaWV6cC.mjs} +87 -82
  23. package/lib/chunks/index-CFaWV6cC.mjs.map +1 -0
  24. package/lib/chunks/{index-BvegaxQF.mjs → index-CJ86PaaF.mjs} +2 -2
  25. package/lib/chunks/{index-BvegaxQF.mjs.map → index-CJ86PaaF.mjs.map} +1 -1
  26. package/lib/chunks/{index-DIe0vHlt.mjs → index-CMKtCKpo.mjs} +2 -2
  27. package/lib/chunks/{index-DIe0vHlt.mjs.map → index-CMKtCKpo.mjs.map} +1 -1
  28. package/lib/chunks/{index-Cjnw238n.mjs → index-CUA_z4GJ.mjs} +2 -2
  29. package/lib/chunks/{index-Cjnw238n.mjs.map → index-CUA_z4GJ.mjs.map} +1 -1
  30. package/lib/chunks/{index-CyrFxdIJ.mjs → index-Cr0Z36a0.mjs} +3 -3
  31. package/lib/chunks/{index-CyrFxdIJ.mjs.map → index-Cr0Z36a0.mjs.map} +1 -1
  32. package/lib/chunks/{index-Ctd0JQ6K.mjs → index-DVJfd09T.mjs} +2 -2
  33. package/lib/chunks/{index-Ctd0JQ6K.mjs.map → index-DVJfd09T.mjs.map} +1 -1
  34. package/lib/chunks/{index-DyipSoKh.mjs → index-DYrRDrZL.mjs} +2 -2
  35. package/lib/chunks/{index-DyipSoKh.mjs.map → index-DYrRDrZL.mjs.map} +1 -1
  36. package/lib/chunks/{index-Zwl5J-Uw.mjs → index-GcvjVFMf.mjs} +2 -2
  37. package/lib/chunks/{index-Zwl5J-Uw.mjs.map → index-GcvjVFMf.mjs.map} +1 -1
  38. package/lib/chunks/{index-DX_NBPzS.mjs → index-P8naV3DU.mjs} +2 -2
  39. package/lib/chunks/{index-DX_NBPzS.mjs.map → index-P8naV3DU.mjs.map} +1 -1
  40. package/lib/chunks/{index-Dw1U4YqT.mjs → index-VTsO_yR7.mjs} +2 -2
  41. package/lib/chunks/{index-Dw1U4YqT.mjs.map → index-VTsO_yR7.mjs.map} +1 -1
  42. package/lib/chunks/{index-DEoR7aDN.mjs → index-dG-p4BPD.mjs} +2 -2
  43. package/lib/chunks/{index-DEoR7aDN.mjs.map → index-dG-p4BPD.mjs.map} +1 -1
  44. package/lib/index.cjs +20 -20
  45. package/lib/index.cjs.map +1 -1
  46. package/lib/index.css +1 -1
  47. package/lib/index.mjs +1 -1
  48. package/package.json +1 -1
  49. package/lib/chunks/index-Oxwizt6-.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index-BvegaxQF.mjs","sources":["../../src/composables/useAudioPlayer.ts","../../src/renderers/Audio/index.vue"],"sourcesContent":["import { ref, watch, onUnmounted, type Ref } from 'vue';\nimport { formatTime } from '@eternalheart/file-preview-core';\n\n/**\n * Vue 3 版本的音频播放器 composable\n * 与 React 版的 useAudioPlayer 行为对齐\n */\nexport function useAudioPlayer(url: Ref<string>) {\n const audioRef = ref<HTMLAudioElement | null>(null);\n const isPlaying = ref(false);\n const isLoading = ref(true);\n const currentTime = ref(0);\n const duration = ref(0);\n const volume = ref(1);\n const isMuted = ref(false);\n const isLoop = ref(false);\n const error = ref<string | null>(null);\n\n let cleanup: (() => void) | null = null;\n\n const bindEvents = () => {\n const audio = audioRef.value;\n if (!audio) return;\n\n isLoading.value = true;\n error.value = null;\n\n const onTimeUpdate = () => {\n if (!isNaN(audio.currentTime)) {\n currentTime.value = audio.currentTime;\n }\n };\n\n const onDurationChange = () => {\n if (!isNaN(audio.duration) && isFinite(audio.duration)) {\n duration.value = audio.duration;\n }\n };\n\n const onCanPlay = () => {\n isLoading.value = false;\n onDurationChange();\n };\n\n const onWaiting = () => {\n isLoading.value = true;\n };\n const onPlaying = () => {\n isLoading.value = false;\n isPlaying.value = true;\n };\n\n const onPlay = () => {\n isPlaying.value = true;\n };\n const onPause = () => {\n isPlaying.value = false;\n };\n const onEnded = () => {\n isPlaying.value = false;\n };\n\n const onError = () => {\n error.value = '音频加载失败';\n isLoading.value = false;\n };\n\n audio.addEventListener('timeupdate', onTimeUpdate);\n audio.addEventListener('loadedmetadata', onDurationChange);\n audio.addEventListener('durationchange', onDurationChange);\n audio.addEventListener('canplay', onCanPlay);\n audio.addEventListener('waiting', onWaiting);\n audio.addEventListener('playing', onPlaying);\n audio.addEventListener('play', onPlay);\n audio.addEventListener('pause', onPause);\n audio.addEventListener('ended', onEnded);\n audio.addEventListener('error', onError);\n\n if (audio.readyState >= 3) {\n isLoading.value = false;\n onDurationChange();\n } else if (audio.readyState >= 1) {\n onDurationChange();\n }\n\n cleanup = () => {\n audio.removeEventListener('timeupdate', onTimeUpdate);\n audio.removeEventListener('loadedmetadata', onDurationChange);\n audio.removeEventListener('durationchange', onDurationChange);\n audio.removeEventListener('canplay', onCanPlay);\n audio.removeEventListener('waiting', onWaiting);\n audio.removeEventListener('playing', onPlaying);\n audio.removeEventListener('play', onPlay);\n audio.removeEventListener('pause', onPause);\n audio.removeEventListener('ended', onEnded);\n audio.removeEventListener('error', onError);\n };\n };\n\n // url 变化时重新绑定\n watch(\n url,\n () => {\n cleanup?.();\n cleanup = null;\n // 等待 DOM 更新(audio src 变化)\n requestAnimationFrame(() => bindEvents());\n },\n { flush: 'post' }\n );\n\n // 在 audioRef 挂载后首次绑定\n watch(\n audioRef,\n (el) => {\n if (el) bindEvents();\n },\n { flush: 'post' }\n );\n\n onUnmounted(() => {\n cleanup?.();\n cleanup = null;\n });\n\n const togglePlay = () => {\n const audio = audioRef.value;\n if (!audio) return;\n if (audio.paused) {\n audio.play().catch(() => {});\n } else {\n audio.pause();\n }\n };\n\n const seek = (time: number) => {\n const audio = audioRef.value;\n if (!audio) return;\n audio.currentTime = time;\n currentTime.value = time;\n };\n\n const skip = (seconds: number) => {\n const audio = audioRef.value;\n if (!audio) return;\n audio.currentTime = Math.max(\n 0,\n Math.min(audio.currentTime + seconds, audio.duration || Infinity)\n );\n };\n\n const setVolume = (vol: number) => {\n const audio = audioRef.value;\n if (!audio) return;\n const clamped = Math.max(0, Math.min(1, vol));\n audio.volume = clamped;\n volume.value = clamped;\n if (clamped > 0) {\n audio.muted = false;\n isMuted.value = false;\n }\n };\n\n const toggleMute = () => {\n const audio = audioRef.value;\n if (!audio) return;\n audio.muted = !audio.muted;\n isMuted.value = audio.muted;\n };\n\n const toggleLoop = () => {\n const audio = audioRef.value;\n if (!audio) return;\n const next = !audio.loop;\n audio.loop = next;\n isLoop.value = next;\n };\n\n return {\n audioRef,\n isPlaying,\n isLoading,\n isLoop,\n currentTime,\n duration,\n volume,\n isMuted,\n error,\n togglePlay,\n seek,\n skip,\n setVolume,\n toggleMute,\n toggleLoop,\n formatTime,\n };\n}\n","<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onBeforeUnmount, toRef } from 'vue';\nimport { Play, Pause, Volume2, VolumeX, Volume1, SkipBack, SkipForward, Repeat } from 'lucide-vue-next';\nimport { useAudioPlayer } from '../../composables/useAudioPlayer';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n fileName: string;\n}>();\n\nconst urlRef = toRef(props, 'url');\n\nconst { t } = useTranslator();\n\nconst {\n audioRef,\n isPlaying,\n isLoop,\n currentTime,\n duration,\n volume,\n isMuted,\n error,\n togglePlay,\n seek,\n skip,\n setVolume,\n toggleMute,\n toggleLoop,\n formatTime,\n} = useAudioPlayer(urlRef);\n\nconst showVolume = ref(false);\nlet volumeHideTimer: number | null = null;\nconst volumeRef = ref<HTMLDivElement | null>(null);\n\nconst progress = computed(() => (duration.value > 0 ? currentTime.value / duration.value : 0));\n\nconst VolumeIcon = computed(() => {\n if (isMuted.value || volume.value === 0) return VolumeX;\n if (volume.value < 0.5) return Volume1;\n return Volume2;\n});\n\nconst handleClickOutside = (e: MouseEvent) => {\n if (volumeRef.value && !volumeRef.value.contains(e.target as Node)) {\n showVolume.value = false;\n }\n};\n\nonMounted(() => {\n document.addEventListener('mousedown', handleClickOutside);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('mousedown', handleClickOutside);\n if (volumeHideTimer !== null) clearTimeout(volumeHideTimer);\n});\n\nconst handleVolumeEnter = () => {\n if (volumeHideTimer !== null) clearTimeout(volumeHideTimer);\n showVolume.value = true;\n};\n\nconst handleVolumeLeave = () => {\n volumeHideTimer = window.setTimeout(() => {\n showVolume.value = false;\n }, 300);\n};\n</script>\n\n<template>\n <RendererError v-if=\"error\" :message=\"error\" />\n\n <div\n v-else\n class=\"vfp-flex vfp-flex-col vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-p-4 md:vfp-p-8 vfp-gap-5 md:vfp-gap-8 vfp-select-none\"\n >\n <!-- 唱片机 -->\n <div class=\"vfp-relative\" style=\"width: 260px; height: 240px\">\n <!-- 外圈光晕 -->\n <div\n class=\"vfp-absolute vfp-rounded-full\"\n :style=\"{\n width: '220px',\n height: '220px',\n top: '18px',\n left: '8px',\n background: 'radial-gradient(circle, rgba(129,140,248,0.12) 0%, transparent 70%)',\n opacity: isPlaying ? 0.7 : 0.2,\n transition: 'opacity 0.5s',\n }\"\n />\n\n <!-- 唱片主体 -->\n <div\n class=\"vfp-absolute vfp-rounded-full vfp-overflow-hidden\"\n :style=\"{\n width: '200px',\n height: '200px',\n top: '28px',\n left: '18px',\n background: `\n radial-gradient(circle at center, transparent 95%, rgba(30,30,30,0.8) 95.5%, #111 97%),\n radial-gradient(circle at center, transparent 38%, rgba(50,50,50,0.5) 38.15%, transparent 38.4%),\n radial-gradient(circle at center, transparent 45%, rgba(50,50,50,0.3) 45.15%, transparent 45.4%),\n radial-gradient(circle at center, transparent 52%, rgba(50,50,50,0.5) 52.15%, transparent 52.4%),\n radial-gradient(circle at center, transparent 59%, rgba(50,50,50,0.3) 59.15%, transparent 59.4%),\n radial-gradient(circle at center, transparent 66%, rgba(50,50,50,0.5) 66.15%, transparent 66.4%),\n radial-gradient(circle at center, transparent 73%, rgba(50,50,50,0.3) 73.15%, transparent 73.4%),\n radial-gradient(circle at center, transparent 80%, rgba(50,50,50,0.4) 80.15%, transparent 80.4%),\n radial-gradient(circle at center, transparent 87%, rgba(50,50,50,0.3) 87.15%, transparent 87.4%),\n conic-gradient(from 0deg, #1c1c1c, #232323, #1a1a1a, #262626, #1c1c1c, #212121, #1a1a1a, #252525, #1c1c1c, #232323, #1a1a1a, #262626, #1c1c1c)\n `,\n boxShadow: isPlaying\n ? '0 0 36px rgba(129,140,248,0.1), 0 8px 32px rgba(0,0,0,0.4), inset 0 0 20px rgba(0,0,0,0.4)'\n : '0 8px 32px rgba(0,0,0,0.4), inset 0 0 20px rgba(0,0,0,0.4)',\n animation: 'vfp-vinyl-spin 8s linear infinite',\n animationPlayState: isPlaying ? 'running' : 'paused',\n }\"\n >\n <!-- 中心标签 -->\n <div\n class=\"vfp-absolute vfp-rounded-full\"\n :style=\"{\n width: '34%',\n height: '34%',\n top: '33%',\n left: '33%',\n background: 'radial-gradient(circle at 40% 38%, #818cf8, #6366f1, #4f46e5, #4338ca)',\n boxShadow: 'inset 0 1px 3px rgba(255,255,255,0.25), inset 0 -1px 3px rgba(0,0,0,0.3), 0 0 8px rgba(0,0,0,0.3)',\n }\"\n >\n <div\n class=\"vfp-absolute vfp-rounded-full\"\n :style=\"{\n width: '14%',\n height: '14%',\n top: '43%',\n left: '43%',\n background: 'radial-gradient(circle at 40% 40%, #333, #0d0d0d)',\n boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.9), 0 0 2px rgba(0,0,0,0.5)',\n }\"\n />\n </div>\n </div>\n\n <!-- 唱臂 -->\n <div\n class=\"vfp-absolute\"\n :style=\"{\n top: '-6px',\n right: '2px',\n width: '100px',\n height: '120px',\n transformOrigin: '76px 16px',\n zIndex: 5,\n transform: isPlaying ? 'rotate(16deg)' : 'rotate(0deg)',\n transition: 'transform 0.8s cubic-bezier(0.4, 0, 0.2, 1)',\n }\"\n >\n <svg width=\"100\" height=\"120\" viewBox=\"0 0 100 120\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"76\" cy=\"16\" r=\"13\" fill=\"rgba(0,0,0,0.3)\" />\n <circle cx=\"76\" cy=\"16\" r=\"11\" fill=\"url(#vfp-baseGrad)\" />\n <circle cx=\"76\" cy=\"16\" r=\"6\" fill=\"url(#vfp-baseInnerGrad)\" />\n <circle cx=\"76\" cy=\"16\" r=\"2.5\" fill=\"#222\" stroke=\"#555\" stroke-width=\"0.5\" />\n <path d=\"M74 22 L56 88\" stroke=\"url(#vfp-armGrad)\" stroke-width=\"3.5\" stroke-linecap=\"round\" />\n <rect x=\"50\" y=\"86\" width=\"12\" height=\"7\" rx=\"1.5\" fill=\"url(#vfp-headGrad)\" />\n <rect x=\"52.5\" y=\"92\" width=\"7\" height=\"9\" rx=\"1\" fill=\"url(#vfp-cartridgeGrad)\" />\n <line x1=\"56\" y1=\"101\" x2=\"56\" y2=\"105\" stroke=\"#bbb\" stroke-width=\"1.2\" stroke-linecap=\"round\" />\n <circle cx=\"56\" cy=\"105.5\" r=\"0.8\" fill=\"#ddd\" />\n\n <defs>\n <radialGradient id=\"vfp-baseGrad\" cx=\"40%\" cy=\"35%\">\n <stop offset=\"0%\" stop-color=\"#555\" />\n <stop offset=\"100%\" stop-color=\"#1a1a1a\" />\n </radialGradient>\n <radialGradient id=\"vfp-baseInnerGrad\" cx=\"40%\" cy=\"35%\">\n <stop offset=\"0%\" stop-color=\"#666\" />\n <stop offset=\"100%\" stop-color=\"#333\" />\n </radialGradient>\n <linearGradient id=\"vfp-armGrad\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"#555\" />\n <stop offset=\"50%\" stop-color=\"#444\" />\n <stop offset=\"100%\" stop-color=\"#333\" />\n </linearGradient>\n <linearGradient id=\"vfp-headGrad\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"#555\" />\n <stop offset=\"100%\" stop-color=\"#333\" />\n </linearGradient>\n <linearGradient id=\"vfp-cartridgeGrad\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"#444\" />\n <stop offset=\"100%\" stop-color=\"#222\" />\n </linearGradient>\n </defs>\n </svg>\n </div>\n </div>\n\n <!-- 文件名 -->\n <div class=\"vfp-text-center vfp-max-w-sm md:vfp-max-w-md vfp-px-4\">\n <div class=\"vfp-text-lg md:vfp-text-xl vfp-font-medium vfp-mb-1 vfp-truncate vfp-text-fg-primary\">\n {{ fileName }}\n </div>\n <p class=\"vfp-text-xs vfp-tracking-widest vfp-uppercase vfp-text-accent\">Audio</p>\n </div>\n\n <!-- 控制面板 -->\n <div\n class=\"vfp-w-full vfp-max-w-sm md:vfp-max-w-md vfp-rounded-2xl vfp-p-4 md:vfp-p-6 vfp-border vfp-bg-surface-1 vfp-border-line-weak\"\n :style=\"{ backdropFilter: 'blur(16px)' }\"\n >\n <!-- 进度条 -->\n <div class=\"vfp-mb-5\">\n <div class=\"vfp-relative vfp-h-4 vfp-flex vfp-items-center\">\n <div\n class=\"vfp-absolute vfp-w-full vfp-h-[5px] vfp-rounded-full vfp-bg-surface-2\"\n />\n <div\n class=\"vfp-absolute vfp-h-[5px] vfp-rounded-full vfp-pointer-events-none\"\n :style=\"{\n width: `${progress * 100}%`,\n background: 'linear-gradient(90deg, var(--fp-accent), var(--fp-accent-hover))',\n boxShadow: isPlaying ? '0 0 8px rgba(129,140,248,0.4)' : 'none',\n transition: 'width 0.1s linear',\n }\"\n />\n <input\n type=\"range\"\n min=\"0\"\n :max=\"duration > 0 ? duration : currentTime || 100\"\n step=\"any\"\n :value=\"currentTime\"\n :disabled=\"duration <= 0\"\n :aria-label=\"t('audio.aria.progress')\"\n class=\"audio-slider vfp-absolute vfp-w-full\"\n @input=\"(e) => seek(parseFloat((e.target as HTMLInputElement).value))\"\n />\n </div>\n <div\n class=\"vfp-flex vfp-justify-between vfp-text-xs vfp-mt-2.5 vfp-text-fg-tertiary\"\n >\n <span style=\"font-variant-numeric: tabular-nums\">{{ formatTime(currentTime) }}</span>\n <span style=\"font-variant-numeric: tabular-nums\">{{ duration > 0 ? formatTime(duration) : '--:--' }}</span>\n </div>\n </div>\n\n <!-- 控制按钮 -->\n <div class=\"vfp-flex vfp-items-center vfp-justify-center vfp-gap-3\">\n <!-- 循环 -->\n <button\n :class=\"[\n 'vfp-w-9 vfp-h-9 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn',\n isLoop ? 'vfp-bg-accent-soft vfp-text-accent' : 'vfp-bg-surface-2 vfp-text-fg-tertiary',\n ]\"\n :aria-label=\"isLoop ? t('audio.aria.loop_off') : t('audio.aria.loop_on')\"\n @click=\"toggleLoop\"\n >\n <Repeat class=\"vfp-w-4 vfp-h-4\" />\n </button>\n\n <!-- 后退 -->\n <button\n class=\"vfp-w-10 vfp-h-10 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn vfp-bg-surface-2 vfp-text-fg-secondary\"\n :aria-label=\"t('audio.aria.backward_10')\"\n @click=\"skip(-10)\"\n >\n <SkipBack class=\"vfp-w-[18px] vfp-h-[18px]\" />\n </button>\n\n <!-- 播放/暂停 -->\n <button\n class=\"vfp-w-14 vfp-h-14 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center audio-ctrl-btn\"\n :style=\"{\n background: 'linear-gradient(135deg, var(--fp-accent-hover), var(--fp-accent))',\n color: '#fff',\n boxShadow: '0 4px 20px rgba(99,102,241,0.35)',\n }\"\n :aria-label=\"isPlaying ? t('audio.aria.pause') : t('audio.aria.play')\"\n @click=\"togglePlay\"\n >\n <Pause v-if=\"isPlaying\" class=\"vfp-w-6 vfp-h-6\" />\n <Play v-else class=\"vfp-w-6 vfp-h-6 vfp-ml-0.5\" />\n </button>\n\n <!-- 前进 -->\n <button\n class=\"vfp-w-10 vfp-h-10 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn vfp-bg-surface-2 vfp-text-fg-secondary\"\n :aria-label=\"t('audio.aria.forward_10')\"\n @click=\"skip(10)\"\n >\n <SkipForward class=\"vfp-w-[18px] vfp-h-[18px]\" />\n </button>\n\n <!-- 音量 -->\n <div ref=\"volumeRef\" class=\"vfp-relative\" @mouseenter=\"handleVolumeEnter\" @mouseleave=\"handleVolumeLeave\">\n <button\n :class=\"[\n 'vfp-w-9 vfp-h-9 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn',\n showVolume ? 'vfp-bg-accent-soft vfp-text-accent' : 'vfp-bg-surface-2 vfp-text-fg-secondary',\n ]\"\n :aria-label=\"isMuted ? t('audio.aria.unmute') : t('audio.aria.mute')\"\n @click=\"toggleMute\"\n >\n <component :is=\"VolumeIcon\" class=\"vfp-w-4 vfp-h-4\" />\n </button>\n\n <Transition name=\"vfp-fade\">\n <div\n v-if=\"showVolume\"\n class=\"vfp-absolute vfp-bottom-full vfp-mb-2 vfp-rounded-xl vfp-p-3 vfp-border vfp-bg-surface-3 vfp-border-line\"\n :style=\"{\n left: '50%',\n marginLeft: '-27px',\n backdropFilter: 'blur(16px)',\n }\"\n @mouseenter=\"handleVolumeEnter\"\n @mouseleave=\"handleVolumeLeave\"\n >\n <div class=\"vfp-flex vfp-flex-col vfp-items-center vfp-gap-2\" style=\"height: 100px\">\n <div\n class=\"vfp-relative vfp-flex vfp-items-center vfp-justify-center\"\n style=\"width: 24px; height: 80px\"\n >\n <div\n class=\"vfp-absolute vfp-rounded-full vfp-bg-surface-2\"\n style=\"width: 3px; height: 100%\"\n />\n <div\n class=\"vfp-absolute vfp-bottom-0 vfp-rounded-full vfp-pointer-events-none\"\n :style=\"{\n width: '3px',\n height: `${(isMuted ? 0 : volume) * 100}%`,\n background: 'var(--fp-accent-hover)',\n transition: 'height 0.1s linear',\n }\"\n />\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n :value=\"isMuted ? 0 : volume\"\n :aria-label=\"t('audio.aria.volume')\"\n class=\"volume-slider-vertical vfp-absolute\"\n style=\"width: 80px; height: 24px; transform: rotate(-90deg); transform-origin: center center\"\n @input=\"(e) => setVolume(parseFloat((e.target as HTMLInputElement).value))\"\n />\n </div>\n <span class=\"vfp-text-[10px] vfp-tabular-nums vfp-text-fg-tertiary\">\n {{ Math.round((isMuted ? 0 : volume) * 100) }}\n </span>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n\n <audio ref=\"audioRef\" :src=\"url\" class=\"vfp-hidden\" />\n </div>\n</template>\n\n<style scoped>\n.audio-ctrl-btn {\n border: 0;\n cursor: pointer;\n}\n</style>\n"],"names":["useAudioPlayer","url","audioRef","ref","isPlaying","isLoading","currentTime","duration","volume","isMuted","isLoop","error","cleanup","bindEvents","audio","onTimeUpdate","onDurationChange","onCanPlay","onWaiting","onPlaying","onPlay","onPause","onEnded","onError","watch","el","onUnmounted","time","seconds","vol","clamped","next","formatTime","urlRef","toRef","__props","t","useTranslator","togglePlay","seek","skip","setVolume","toggleMute","toggleLoop","showVolume","volumeHideTimer","volumeRef","progress","computed","VolumeIcon","VolumeX","Volume1","Volume2","handleClickOutside","e","onMounted","onBeforeUnmount","handleVolumeEnter","handleVolumeLeave","_unref","_createBlock","RendererError","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_normalizeStyle","_hoisted_3","_hoisted_4","_toDisplayString","_cache","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_normalizeClass","args","_createVNode","Repeat","SkipBack","Pause","Play","SkipForward","_resolveDynamicComponent","_Transition","_hoisted_18","_hoisted_19","_hoisted_21"],"mappings":"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"}
1
+ {"version":3,"file":"index-CJ86PaaF.mjs","sources":["../../src/composables/useAudioPlayer.ts","../../src/renderers/Audio/index.vue"],"sourcesContent":["import { ref, watch, onUnmounted, type Ref } from 'vue';\nimport { formatTime } from '@eternalheart/file-preview-core';\n\n/**\n * Vue 3 版本的音频播放器 composable\n * 与 React 版的 useAudioPlayer 行为对齐\n */\nexport function useAudioPlayer(url: Ref<string>) {\n const audioRef = ref<HTMLAudioElement | null>(null);\n const isPlaying = ref(false);\n const isLoading = ref(true);\n const currentTime = ref(0);\n const duration = ref(0);\n const volume = ref(1);\n const isMuted = ref(false);\n const isLoop = ref(false);\n const error = ref<string | null>(null);\n\n let cleanup: (() => void) | null = null;\n\n const bindEvents = () => {\n const audio = audioRef.value;\n if (!audio) return;\n\n isLoading.value = true;\n error.value = null;\n\n const onTimeUpdate = () => {\n if (!isNaN(audio.currentTime)) {\n currentTime.value = audio.currentTime;\n }\n };\n\n const onDurationChange = () => {\n if (!isNaN(audio.duration) && isFinite(audio.duration)) {\n duration.value = audio.duration;\n }\n };\n\n const onCanPlay = () => {\n isLoading.value = false;\n onDurationChange();\n };\n\n const onWaiting = () => {\n isLoading.value = true;\n };\n const onPlaying = () => {\n isLoading.value = false;\n isPlaying.value = true;\n };\n\n const onPlay = () => {\n isPlaying.value = true;\n };\n const onPause = () => {\n isPlaying.value = false;\n };\n const onEnded = () => {\n isPlaying.value = false;\n };\n\n const onError = () => {\n error.value = '音频加载失败';\n isLoading.value = false;\n };\n\n audio.addEventListener('timeupdate', onTimeUpdate);\n audio.addEventListener('loadedmetadata', onDurationChange);\n audio.addEventListener('durationchange', onDurationChange);\n audio.addEventListener('canplay', onCanPlay);\n audio.addEventListener('waiting', onWaiting);\n audio.addEventListener('playing', onPlaying);\n audio.addEventListener('play', onPlay);\n audio.addEventListener('pause', onPause);\n audio.addEventListener('ended', onEnded);\n audio.addEventListener('error', onError);\n\n if (audio.readyState >= 3) {\n isLoading.value = false;\n onDurationChange();\n } else if (audio.readyState >= 1) {\n onDurationChange();\n }\n\n cleanup = () => {\n audio.removeEventListener('timeupdate', onTimeUpdate);\n audio.removeEventListener('loadedmetadata', onDurationChange);\n audio.removeEventListener('durationchange', onDurationChange);\n audio.removeEventListener('canplay', onCanPlay);\n audio.removeEventListener('waiting', onWaiting);\n audio.removeEventListener('playing', onPlaying);\n audio.removeEventListener('play', onPlay);\n audio.removeEventListener('pause', onPause);\n audio.removeEventListener('ended', onEnded);\n audio.removeEventListener('error', onError);\n };\n };\n\n // url 变化时重新绑定\n watch(\n url,\n () => {\n cleanup?.();\n cleanup = null;\n // 等待 DOM 更新(audio src 变化)\n requestAnimationFrame(() => bindEvents());\n },\n { flush: 'post' }\n );\n\n // 在 audioRef 挂载后首次绑定\n watch(\n audioRef,\n (el) => {\n if (el) bindEvents();\n },\n { flush: 'post' }\n );\n\n onUnmounted(() => {\n cleanup?.();\n cleanup = null;\n });\n\n const togglePlay = () => {\n const audio = audioRef.value;\n if (!audio) return;\n if (audio.paused) {\n audio.play().catch(() => {});\n } else {\n audio.pause();\n }\n };\n\n const seek = (time: number) => {\n const audio = audioRef.value;\n if (!audio) return;\n audio.currentTime = time;\n currentTime.value = time;\n };\n\n const skip = (seconds: number) => {\n const audio = audioRef.value;\n if (!audio) return;\n audio.currentTime = Math.max(\n 0,\n Math.min(audio.currentTime + seconds, audio.duration || Infinity)\n );\n };\n\n const setVolume = (vol: number) => {\n const audio = audioRef.value;\n if (!audio) return;\n const clamped = Math.max(0, Math.min(1, vol));\n audio.volume = clamped;\n volume.value = clamped;\n if (clamped > 0) {\n audio.muted = false;\n isMuted.value = false;\n }\n };\n\n const toggleMute = () => {\n const audio = audioRef.value;\n if (!audio) return;\n audio.muted = !audio.muted;\n isMuted.value = audio.muted;\n };\n\n const toggleLoop = () => {\n const audio = audioRef.value;\n if (!audio) return;\n const next = !audio.loop;\n audio.loop = next;\n isLoop.value = next;\n };\n\n return {\n audioRef,\n isPlaying,\n isLoading,\n isLoop,\n currentTime,\n duration,\n volume,\n isMuted,\n error,\n togglePlay,\n seek,\n skip,\n setVolume,\n toggleMute,\n toggleLoop,\n formatTime,\n };\n}\n","<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onBeforeUnmount, toRef } from 'vue';\nimport { Play, Pause, Volume2, VolumeX, Volume1, SkipBack, SkipForward, Repeat } from 'lucide-vue-next';\nimport { useAudioPlayer } from '../../composables/useAudioPlayer';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n fileName: string;\n}>();\n\nconst urlRef = toRef(props, 'url');\n\nconst { t } = useTranslator();\n\nconst {\n audioRef,\n isPlaying,\n isLoop,\n currentTime,\n duration,\n volume,\n isMuted,\n error,\n togglePlay,\n seek,\n skip,\n setVolume,\n toggleMute,\n toggleLoop,\n formatTime,\n} = useAudioPlayer(urlRef);\n\nconst showVolume = ref(false);\nlet volumeHideTimer: number | null = null;\nconst volumeRef = ref<HTMLDivElement | null>(null);\n\nconst progress = computed(() => (duration.value > 0 ? currentTime.value / duration.value : 0));\n\nconst VolumeIcon = computed(() => {\n if (isMuted.value || volume.value === 0) return VolumeX;\n if (volume.value < 0.5) return Volume1;\n return Volume2;\n});\n\nconst handleClickOutside = (e: MouseEvent) => {\n if (volumeRef.value && !volumeRef.value.contains(e.target as Node)) {\n showVolume.value = false;\n }\n};\n\nonMounted(() => {\n document.addEventListener('mousedown', handleClickOutside);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('mousedown', handleClickOutside);\n if (volumeHideTimer !== null) clearTimeout(volumeHideTimer);\n});\n\nconst handleVolumeEnter = () => {\n if (volumeHideTimer !== null) clearTimeout(volumeHideTimer);\n showVolume.value = true;\n};\n\nconst handleVolumeLeave = () => {\n volumeHideTimer = window.setTimeout(() => {\n showVolume.value = false;\n }, 300);\n};\n</script>\n\n<template>\n <RendererError v-if=\"error\" :message=\"error\" />\n\n <div\n v-else\n class=\"vfp-flex vfp-flex-col vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-p-4 md:vfp-p-8 vfp-gap-5 md:vfp-gap-8 vfp-select-none\"\n >\n <!-- 唱片机 -->\n <div class=\"vfp-relative\" style=\"width: 260px; height: 240px\">\n <!-- 外圈光晕 -->\n <div\n class=\"vfp-absolute vfp-rounded-full\"\n :style=\"{\n width: '220px',\n height: '220px',\n top: '18px',\n left: '8px',\n background: 'radial-gradient(circle, rgba(129,140,248,0.12) 0%, transparent 70%)',\n opacity: isPlaying ? 0.7 : 0.2,\n transition: 'opacity 0.5s',\n }\"\n />\n\n <!-- 唱片主体 -->\n <div\n class=\"vfp-absolute vfp-rounded-full vfp-overflow-hidden\"\n :style=\"{\n width: '200px',\n height: '200px',\n top: '28px',\n left: '18px',\n background: `\n radial-gradient(circle at center, transparent 95%, rgba(30,30,30,0.8) 95.5%, #111 97%),\n radial-gradient(circle at center, transparent 38%, rgba(50,50,50,0.5) 38.15%, transparent 38.4%),\n radial-gradient(circle at center, transparent 45%, rgba(50,50,50,0.3) 45.15%, transparent 45.4%),\n radial-gradient(circle at center, transparent 52%, rgba(50,50,50,0.5) 52.15%, transparent 52.4%),\n radial-gradient(circle at center, transparent 59%, rgba(50,50,50,0.3) 59.15%, transparent 59.4%),\n radial-gradient(circle at center, transparent 66%, rgba(50,50,50,0.5) 66.15%, transparent 66.4%),\n radial-gradient(circle at center, transparent 73%, rgba(50,50,50,0.3) 73.15%, transparent 73.4%),\n radial-gradient(circle at center, transparent 80%, rgba(50,50,50,0.4) 80.15%, transparent 80.4%),\n radial-gradient(circle at center, transparent 87%, rgba(50,50,50,0.3) 87.15%, transparent 87.4%),\n conic-gradient(from 0deg, #1c1c1c, #232323, #1a1a1a, #262626, #1c1c1c, #212121, #1a1a1a, #252525, #1c1c1c, #232323, #1a1a1a, #262626, #1c1c1c)\n `,\n boxShadow: isPlaying\n ? '0 0 36px rgba(129,140,248,0.1), 0 8px 32px rgba(0,0,0,0.4), inset 0 0 20px rgba(0,0,0,0.4)'\n : '0 8px 32px rgba(0,0,0,0.4), inset 0 0 20px rgba(0,0,0,0.4)',\n animation: 'vfp-vinyl-spin 8s linear infinite',\n animationPlayState: isPlaying ? 'running' : 'paused',\n }\"\n >\n <!-- 中心标签 -->\n <div\n class=\"vfp-absolute vfp-rounded-full\"\n :style=\"{\n width: '34%',\n height: '34%',\n top: '33%',\n left: '33%',\n background: 'radial-gradient(circle at 40% 38%, #818cf8, #6366f1, #4f46e5, #4338ca)',\n boxShadow: 'inset 0 1px 3px rgba(255,255,255,0.25), inset 0 -1px 3px rgba(0,0,0,0.3), 0 0 8px rgba(0,0,0,0.3)',\n }\"\n >\n <div\n class=\"vfp-absolute vfp-rounded-full\"\n :style=\"{\n width: '14%',\n height: '14%',\n top: '43%',\n left: '43%',\n background: 'radial-gradient(circle at 40% 40%, #333, #0d0d0d)',\n boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.9), 0 0 2px rgba(0,0,0,0.5)',\n }\"\n />\n </div>\n </div>\n\n <!-- 唱臂 -->\n <div\n class=\"vfp-absolute\"\n :style=\"{\n top: '-6px',\n right: '2px',\n width: '100px',\n height: '120px',\n transformOrigin: '76px 16px',\n zIndex: 5,\n transform: isPlaying ? 'rotate(16deg)' : 'rotate(0deg)',\n transition: 'transform 0.8s cubic-bezier(0.4, 0, 0.2, 1)',\n }\"\n >\n <svg width=\"100\" height=\"120\" viewBox=\"0 0 100 120\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"76\" cy=\"16\" r=\"13\" fill=\"rgba(0,0,0,0.3)\" />\n <circle cx=\"76\" cy=\"16\" r=\"11\" fill=\"url(#vfp-baseGrad)\" />\n <circle cx=\"76\" cy=\"16\" r=\"6\" fill=\"url(#vfp-baseInnerGrad)\" />\n <circle cx=\"76\" cy=\"16\" r=\"2.5\" fill=\"#222\" stroke=\"#555\" stroke-width=\"0.5\" />\n <path d=\"M74 22 L56 88\" stroke=\"url(#vfp-armGrad)\" stroke-width=\"3.5\" stroke-linecap=\"round\" />\n <rect x=\"50\" y=\"86\" width=\"12\" height=\"7\" rx=\"1.5\" fill=\"url(#vfp-headGrad)\" />\n <rect x=\"52.5\" y=\"92\" width=\"7\" height=\"9\" rx=\"1\" fill=\"url(#vfp-cartridgeGrad)\" />\n <line x1=\"56\" y1=\"101\" x2=\"56\" y2=\"105\" stroke=\"#bbb\" stroke-width=\"1.2\" stroke-linecap=\"round\" />\n <circle cx=\"56\" cy=\"105.5\" r=\"0.8\" fill=\"#ddd\" />\n\n <defs>\n <radialGradient id=\"vfp-baseGrad\" cx=\"40%\" cy=\"35%\">\n <stop offset=\"0%\" stop-color=\"#555\" />\n <stop offset=\"100%\" stop-color=\"#1a1a1a\" />\n </radialGradient>\n <radialGradient id=\"vfp-baseInnerGrad\" cx=\"40%\" cy=\"35%\">\n <stop offset=\"0%\" stop-color=\"#666\" />\n <stop offset=\"100%\" stop-color=\"#333\" />\n </radialGradient>\n <linearGradient id=\"vfp-armGrad\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"#555\" />\n <stop offset=\"50%\" stop-color=\"#444\" />\n <stop offset=\"100%\" stop-color=\"#333\" />\n </linearGradient>\n <linearGradient id=\"vfp-headGrad\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"#555\" />\n <stop offset=\"100%\" stop-color=\"#333\" />\n </linearGradient>\n <linearGradient id=\"vfp-cartridgeGrad\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"#444\" />\n <stop offset=\"100%\" stop-color=\"#222\" />\n </linearGradient>\n </defs>\n </svg>\n </div>\n </div>\n\n <!-- 文件名 -->\n <div class=\"vfp-text-center vfp-max-w-sm md:vfp-max-w-md vfp-px-4\">\n <div class=\"vfp-text-lg md:vfp-text-xl vfp-font-medium vfp-mb-1 vfp-truncate vfp-text-fg-primary\">\n {{ fileName }}\n </div>\n <p class=\"vfp-text-xs vfp-tracking-widest vfp-uppercase vfp-text-accent\">Audio</p>\n </div>\n\n <!-- 控制面板 -->\n <div\n class=\"vfp-w-full vfp-max-w-sm md:vfp-max-w-md vfp-rounded-2xl vfp-p-4 md:vfp-p-6 vfp-border vfp-bg-surface-1 vfp-border-line-weak\"\n :style=\"{ backdropFilter: 'blur(16px)' }\"\n >\n <!-- 进度条 -->\n <div class=\"vfp-mb-5\">\n <div class=\"vfp-relative vfp-h-4 vfp-flex vfp-items-center\">\n <div\n class=\"vfp-absolute vfp-w-full vfp-h-[5px] vfp-rounded-full vfp-bg-surface-2\"\n />\n <div\n class=\"vfp-absolute vfp-h-[5px] vfp-rounded-full vfp-pointer-events-none\"\n :style=\"{\n width: `${progress * 100}%`,\n background: 'linear-gradient(90deg, var(--fp-accent), var(--fp-accent-hover))',\n boxShadow: isPlaying ? '0 0 8px rgba(129,140,248,0.4)' : 'none',\n transition: 'width 0.1s linear',\n }\"\n />\n <input\n type=\"range\"\n min=\"0\"\n :max=\"duration > 0 ? duration : currentTime || 100\"\n step=\"any\"\n :value=\"currentTime\"\n :disabled=\"duration <= 0\"\n :aria-label=\"t('audio.aria.progress')\"\n class=\"audio-slider vfp-absolute vfp-w-full\"\n @input=\"(e) => seek(parseFloat((e.target as HTMLInputElement).value))\"\n />\n </div>\n <div\n class=\"vfp-flex vfp-justify-between vfp-text-xs vfp-mt-2.5 vfp-text-fg-tertiary\"\n >\n <span style=\"font-variant-numeric: tabular-nums\">{{ formatTime(currentTime) }}</span>\n <span style=\"font-variant-numeric: tabular-nums\">{{ duration > 0 ? formatTime(duration) : '--:--' }}</span>\n </div>\n </div>\n\n <!-- 控制按钮 -->\n <div class=\"vfp-flex vfp-items-center vfp-justify-center vfp-gap-3\">\n <!-- 循环 -->\n <button\n :class=\"[\n 'vfp-w-9 vfp-h-9 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn',\n isLoop ? 'vfp-bg-accent-soft vfp-text-accent' : 'vfp-bg-surface-2 vfp-text-fg-tertiary',\n ]\"\n :aria-label=\"isLoop ? t('audio.aria.loop_off') : t('audio.aria.loop_on')\"\n @click=\"toggleLoop\"\n >\n <Repeat class=\"vfp-w-4 vfp-h-4\" />\n </button>\n\n <!-- 后退 -->\n <button\n class=\"vfp-w-10 vfp-h-10 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn vfp-bg-surface-2 vfp-text-fg-secondary\"\n :aria-label=\"t('audio.aria.backward_10')\"\n @click=\"skip(-10)\"\n >\n <SkipBack class=\"vfp-w-[18px] vfp-h-[18px]\" />\n </button>\n\n <!-- 播放/暂停 -->\n <button\n class=\"vfp-w-14 vfp-h-14 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center audio-ctrl-btn\"\n :style=\"{\n background: 'linear-gradient(135deg, var(--fp-accent-hover), var(--fp-accent))',\n color: '#fff',\n boxShadow: '0 4px 20px rgba(99,102,241,0.35)',\n }\"\n :aria-label=\"isPlaying ? t('audio.aria.pause') : t('audio.aria.play')\"\n @click=\"togglePlay\"\n >\n <Pause v-if=\"isPlaying\" class=\"vfp-w-6 vfp-h-6\" />\n <Play v-else class=\"vfp-w-6 vfp-h-6 vfp-ml-0.5\" />\n </button>\n\n <!-- 前进 -->\n <button\n class=\"vfp-w-10 vfp-h-10 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn vfp-bg-surface-2 vfp-text-fg-secondary\"\n :aria-label=\"t('audio.aria.forward_10')\"\n @click=\"skip(10)\"\n >\n <SkipForward class=\"vfp-w-[18px] vfp-h-[18px]\" />\n </button>\n\n <!-- 音量 -->\n <div ref=\"volumeRef\" class=\"vfp-relative\" @mouseenter=\"handleVolumeEnter\" @mouseleave=\"handleVolumeLeave\">\n <button\n :class=\"[\n 'vfp-w-9 vfp-h-9 vfp-rounded-full vfp-flex vfp-items-center vfp-justify-center vfp-transition-colors audio-ctrl-btn',\n showVolume ? 'vfp-bg-accent-soft vfp-text-accent' : 'vfp-bg-surface-2 vfp-text-fg-secondary',\n ]\"\n :aria-label=\"isMuted ? t('audio.aria.unmute') : t('audio.aria.mute')\"\n @click=\"toggleMute\"\n >\n <component :is=\"VolumeIcon\" class=\"vfp-w-4 vfp-h-4\" />\n </button>\n\n <Transition name=\"vfp-fade\">\n <div\n v-if=\"showVolume\"\n class=\"vfp-absolute vfp-bottom-full vfp-mb-2 vfp-rounded-xl vfp-p-3 vfp-border vfp-bg-surface-3 vfp-border-line\"\n :style=\"{\n left: '50%',\n marginLeft: '-27px',\n backdropFilter: 'blur(16px)',\n }\"\n @mouseenter=\"handleVolumeEnter\"\n @mouseleave=\"handleVolumeLeave\"\n >\n <div class=\"vfp-flex vfp-flex-col vfp-items-center vfp-gap-2\" style=\"height: 100px\">\n <div\n class=\"vfp-relative vfp-flex vfp-items-center vfp-justify-center\"\n style=\"width: 24px; height: 80px\"\n >\n <div\n class=\"vfp-absolute vfp-rounded-full vfp-bg-surface-2\"\n style=\"width: 3px; height: 100%\"\n />\n <div\n class=\"vfp-absolute vfp-bottom-0 vfp-rounded-full vfp-pointer-events-none\"\n :style=\"{\n width: '3px',\n height: `${(isMuted ? 0 : volume) * 100}%`,\n background: 'var(--fp-accent-hover)',\n transition: 'height 0.1s linear',\n }\"\n />\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n :value=\"isMuted ? 0 : volume\"\n :aria-label=\"t('audio.aria.volume')\"\n class=\"volume-slider-vertical vfp-absolute\"\n style=\"width: 80px; height: 24px; transform: rotate(-90deg); transform-origin: center center\"\n @input=\"(e) => setVolume(parseFloat((e.target as HTMLInputElement).value))\"\n />\n </div>\n <span class=\"vfp-text-[10px] vfp-tabular-nums vfp-text-fg-tertiary\">\n {{ Math.round((isMuted ? 0 : volume) * 100) }}\n </span>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n\n <audio ref=\"audioRef\" :src=\"url\" class=\"vfp-hidden\" />\n </div>\n</template>\n\n<style scoped>\n.audio-ctrl-btn {\n border: 0;\n cursor: pointer;\n}\n</style>\n"],"names":["useAudioPlayer","url","audioRef","ref","isPlaying","isLoading","currentTime","duration","volume","isMuted","isLoop","error","cleanup","bindEvents","audio","onTimeUpdate","onDurationChange","onCanPlay","onWaiting","onPlaying","onPlay","onPause","onEnded","onError","watch","el","onUnmounted","time","seconds","vol","clamped","next","formatTime","urlRef","toRef","__props","t","useTranslator","togglePlay","seek","skip","setVolume","toggleMute","toggleLoop","showVolume","volumeHideTimer","volumeRef","progress","computed","VolumeIcon","VolumeX","Volume1","Volume2","handleClickOutside","e","onMounted","onBeforeUnmount","handleVolumeEnter","handleVolumeLeave","_unref","_createBlock","RendererError","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_normalizeStyle","_hoisted_3","_hoisted_4","_toDisplayString","_cache","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_normalizeClass","args","_createVNode","Repeat","SkipBack","Pause","Play","SkipForward","_resolveDynamicComponent","_Transition","_hoisted_18","_hoisted_19","_hoisted_21"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { defineComponent as T, ref as i, watch as _, openBlock as a, createElementBlock as p, createElementVNode as $, createBlock as z, toDisplayString as M } from "vue";
2
- import { u as S, a as D, c as E, E as B, _ as H } from "./index-Oxwizt6-.mjs";
2
+ import { u as S, a as D, c as E, E as B, _ as H } from "./index-CFaWV6cC.mjs";
3
3
  import { codeToHtml as L } from "shiki";
4
4
  import { _ as N } from "./RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs";
5
5
  const P = {
@@ -78,4 +78,4 @@ $2$3`);
78
78
  export {
79
79
  O as default
80
80
  };
81
- //# sourceMappingURL=index-DIe0vHlt.mjs.map
81
+ //# sourceMappingURL=index-CMKtCKpo.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-DIe0vHlt.mjs","sources":["../../src/renderers/Xml/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch } from 'vue';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { codeToHtml } from 'shiki';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport { useResolvedTheme } from '../../composables/useResolvedTheme';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n fileName: string;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\nconst resolvedTheme = useResolvedTheme();\n\nconst content = ref<string>('');\nconst highlighted = ref<string>('');\nconst loading = ref(true);\nconst error = ref<string | null>(null);\n\nconst indentXml = (xml: string): string => {\n const PADDING = ' ';\n const reg = /(>)(<)(\\/*)/g;\n const formatted = xml.replace(reg, '$1\\n$2$3');\n let pad = 0;\n return formatted\n .split('\\n')\n .map((line) => {\n let indent = 0;\n if (/^<\\/\\w/.test(line)) {\n pad = Math.max(pad - 1, 0);\n } else if (/^<\\w[^>]*[^/]>.*$/.test(line) && !/<.+<\\/.+>$/.test(line)) {\n indent = 1;\n }\n const padded = PADDING.repeat(pad) + line;\n pad += indent;\n return padded;\n })\n .join('\\n');\n};\n\nconst prettyPrintXml = (xml: string): string => {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'application/xml');\n if (doc.querySelector('parsererror')) return xml;\n const serializer = new XMLSerializer();\n const serialized = serializer.serializeToString(doc);\n return indentXml(serialized);\n } catch {\n return xml;\n }\n};\n\nconst load = async () => {\n loading.value = true;\n error.value = null;\n try {\n const raw = await fetchTextUtf8(props.url, { fetcher: fetcher.value });\n content.value = prettyPrintXml(raw);\n try {\n highlighted.value = await codeToHtml(content.value, {\n lang: 'xml',\n theme: resolvedTheme.value === 'light' ? 'github-light' : 'dark-plus',\n });\n } catch {\n highlighted.value = '';\n }\n } catch (err) {\n console.error(err);\n error.value = t.value('xml.load_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, load, { immediate: true });\nwatch(resolvedTheme, () => {\n if (content.value) load();\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\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-else-if=\"error\" :message=\"error\" />\n\n <div v-else class=\"vfp-w-full vfp-h-full vfp-overflow-auto\" style=\"background: var(--fp-code-bg);\">\n <pre\n v-if=\"!highlighted\"\n class=\"vfp-p-6 vfp-text-fg-primary vfp-font-mono vfp-text-sm vfp-whitespace-pre-wrap vfp-break-words\"\n >{{ content }}</pre\n >\n <div v-else class=\"shiki-wrapper\" v-html=\"highlighted\" />\n </div>\n</template>\n\n<style scoped>\n.shiki-wrapper :deep(pre) {\n margin: 0;\n padding: 1.5rem 1.5rem 1.5rem 0;\n background: transparent !important;\n font-size: 0.875rem;\n overflow-x: auto;\n}\n.shiki-wrapper :deep(code) {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n counter-reset: line;\n}\n.shiki-wrapper :deep(code .line) {\n counter-increment: line;\n display: inline-block;\n width: 100%;\n padding-left: 4.5em;\n text-indent: -4.5em;\n}\n.shiki-wrapper :deep(code .line::before) {\n content: counter(line);\n display: inline-block;\n width: 3em;\n padding-right: 1em;\n margin-right: 0.5em;\n text-align: right;\n color: var(--fp-fg-disabled);\n user-select: none;\n border-right: 1px solid var(--fp-line);\n}\n</style>\n"],"names":["props","__props","t","useTranslator","fetcher","useFetcher","resolvedTheme","useResolvedTheme","content","ref","highlighted","loading","error","indentXml","xml","PADDING","reg","formatted","pad","line","indent","padded","prettyPrintXml","doc","serialized","load","raw","fetchTextUtf8","codeToHtml","err","watch","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_createBlock","RendererError","_hoisted_2","_hoisted_3","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASA,UAAMA,IAAQC,GAKR,EAAE,GAAAC,EAAA,IAAMC,EAAA,GACRC,IAAUC,EAAA,GACVC,IAAgBC,EAAA,GAEhBC,IAAUC,EAAY,EAAE,GACxBC,IAAcD,EAAY,EAAE,GAC5BE,IAAUF,EAAI,EAAI,GAClBG,IAAQH,EAAmB,IAAI,GAE/BI,IAAY,CAACC,MAAwB;AACzC,YAAMC,IAAU,MACVC,IAAM,gBACNC,IAAYH,EAAI,QAAQE,GAAK;AAAA,KAAU;AAC7C,UAAIE,IAAM;AACV,aAAOD,EACJ,MAAM;AAAA,CAAI,EACV,IAAI,CAACE,MAAS;AACb,YAAIC,IAAS;AACb,QAAI,SAAS,KAAKD,CAAI,IACpBD,IAAM,KAAK,IAAIA,IAAM,GAAG,CAAC,IAChB,oBAAoB,KAAKC,CAAI,KAAK,CAAC,aAAa,KAAKA,CAAI,MAClEC,IAAS;AAEX,cAAMC,IAASN,EAAQ,OAAOG,CAAG,IAAIC;AACrC,eAAAD,KAAOE,GACAC;AAAA,MACT,CAAC,EACA,KAAK;AAAA,CAAI;AAAA,IACd,GAEMC,IAAiB,CAACR,MAAwB;AAC9C,UAAI;AAEF,cAAMS,IADS,IAAI,UAAA,EACA,gBAAgBT,GAAK,iBAAiB;AACzD,YAAIS,EAAI,cAAc,aAAa,EAAG,QAAOT;AAE7C,cAAMU,IADa,IAAI,cAAA,EACO,kBAAkBD,CAAG;AACnD,eAAOV,EAAUW,CAAU;AAAA,MAC7B,QAAQ;AACN,eAAOV;AAAA,MACT;AAAA,IACF,GAEMW,IAAO,YAAY;AACvB,MAAAd,EAAQ,QAAQ,IAChBC,EAAM,QAAQ;AACd,UAAI;AACF,cAAMc,IAAM,MAAMC,EAAc3B,EAAM,KAAK,EAAE,SAASI,EAAQ,OAAO;AACrE,QAAAI,EAAQ,QAAQc,EAAeI,CAAG;AAClC,YAAI;AACF,UAAAhB,EAAY,QAAQ,MAAMkB,EAAWpB,EAAQ,OAAO;AAAA,YAClD,MAAM;AAAA,YACN,OAAOF,EAAc,UAAU,UAAU,iBAAiB;AAAA,UAAA,CAC3D;AAAA,QACH,QAAQ;AACN,UAAAI,EAAY,QAAQ;AAAA,QACtB;AAAA,MACF,SAASmB,GAAK;AACZ,gBAAQ,MAAMA,CAAG,GACjBjB,EAAM,QAAQV,EAAE,MAAM,iBAAiB;AAAA,MACzC,UAAA;AACE,QAAAS,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAEA,WAAAmB,EAAM,MAAM9B,EAAM,KAAKyB,GAAM,EAAE,WAAW,IAAM,GAChDK,EAAMxB,GAAe,MAAM;AACzB,MAAIE,EAAQ,SAAOiB,EAAA;AAAA,IACrB,CAAC,aAIYd,EAAA,SAAXoB,EAAA,GAAAC,EAIM,OAJNC,GAIM,CAAA,GAAAC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA;AAAA,MAHJC,EAEE,OAAA,EADA,OAAM,oHAAA,GAAmH,MAAA,EAAA;AAAA,IAAA,QAInGvB,EAAA,cAA1BwB,EAAoDC,GAAA;AAAA;MAAlB,SAASzB,EAAA;AAAA,IAAA,6BAE3CmB,KAAAC,EAOM,OAPNM,GAOM;AAAA,MALK5B,EAAA,cAITsB,EAAyD,OAAA;AAAA;QAA7C,OAAM;AAAA,QAAgB,WAAQtB,EAAA;AAAA,MAAA,wBAL1CsB,EAIC,OAJDO,GAICC,EADKhC,EAAA,KAAO,GAAA,CAAA;AAAA;;;"}
1
+ {"version":3,"file":"index-CMKtCKpo.mjs","sources":["../../src/renderers/Xml/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch } from 'vue';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { codeToHtml } from 'shiki';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\nimport { useResolvedTheme } from '../../composables/useResolvedTheme';\nimport RendererError from '../RendererError.vue';\n\nconst props = defineProps<{\n url: string;\n fileName: string;\n}>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\nconst resolvedTheme = useResolvedTheme();\n\nconst content = ref<string>('');\nconst highlighted = ref<string>('');\nconst loading = ref(true);\nconst error = ref<string | null>(null);\n\nconst indentXml = (xml: string): string => {\n const PADDING = ' ';\n const reg = /(>)(<)(\\/*)/g;\n const formatted = xml.replace(reg, '$1\\n$2$3');\n let pad = 0;\n return formatted\n .split('\\n')\n .map((line) => {\n let indent = 0;\n if (/^<\\/\\w/.test(line)) {\n pad = Math.max(pad - 1, 0);\n } else if (/^<\\w[^>]*[^/]>.*$/.test(line) && !/<.+<\\/.+>$/.test(line)) {\n indent = 1;\n }\n const padded = PADDING.repeat(pad) + line;\n pad += indent;\n return padded;\n })\n .join('\\n');\n};\n\nconst prettyPrintXml = (xml: string): string => {\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'application/xml');\n if (doc.querySelector('parsererror')) return xml;\n const serializer = new XMLSerializer();\n const serialized = serializer.serializeToString(doc);\n return indentXml(serialized);\n } catch {\n return xml;\n }\n};\n\nconst load = async () => {\n loading.value = true;\n error.value = null;\n try {\n const raw = await fetchTextUtf8(props.url, { fetcher: fetcher.value });\n content.value = prettyPrintXml(raw);\n try {\n highlighted.value = await codeToHtml(content.value, {\n lang: 'xml',\n theme: resolvedTheme.value === 'light' ? 'github-light' : 'dark-plus',\n });\n } catch {\n highlighted.value = '';\n }\n } catch (err) {\n console.error(err);\n error.value = t.value('xml.load_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, load, { immediate: true });\nwatch(resolvedTheme, () => {\n if (content.value) load();\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\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-else-if=\"error\" :message=\"error\" />\n\n <div v-else class=\"vfp-w-full vfp-h-full vfp-overflow-auto\" style=\"background: var(--fp-code-bg);\">\n <pre\n v-if=\"!highlighted\"\n class=\"vfp-p-6 vfp-text-fg-primary vfp-font-mono vfp-text-sm vfp-whitespace-pre-wrap vfp-break-words\"\n >{{ content }}</pre\n >\n <div v-else class=\"shiki-wrapper\" v-html=\"highlighted\" />\n </div>\n</template>\n\n<style scoped>\n.shiki-wrapper :deep(pre) {\n margin: 0;\n padding: 1.5rem 1.5rem 1.5rem 0;\n background: transparent !important;\n font-size: 0.875rem;\n overflow-x: auto;\n}\n.shiki-wrapper :deep(code) {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n counter-reset: line;\n}\n.shiki-wrapper :deep(code .line) {\n counter-increment: line;\n display: inline-block;\n width: 100%;\n padding-left: 4.5em;\n text-indent: -4.5em;\n}\n.shiki-wrapper :deep(code .line::before) {\n content: counter(line);\n display: inline-block;\n width: 3em;\n padding-right: 1em;\n margin-right: 0.5em;\n text-align: right;\n color: var(--fp-fg-disabled);\n user-select: none;\n border-right: 1px solid var(--fp-line);\n}\n</style>\n"],"names":["props","__props","t","useTranslator","fetcher","useFetcher","resolvedTheme","useResolvedTheme","content","ref","highlighted","loading","error","indentXml","xml","PADDING","reg","formatted","pad","line","indent","padded","prettyPrintXml","doc","serialized","load","raw","fetchTextUtf8","codeToHtml","err","watch","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_createBlock","RendererError","_hoisted_2","_hoisted_3","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASA,UAAMA,IAAQC,GAKR,EAAE,GAAAC,EAAA,IAAMC,EAAA,GACRC,IAAUC,EAAA,GACVC,IAAgBC,EAAA,GAEhBC,IAAUC,EAAY,EAAE,GACxBC,IAAcD,EAAY,EAAE,GAC5BE,IAAUF,EAAI,EAAI,GAClBG,IAAQH,EAAmB,IAAI,GAE/BI,IAAY,CAACC,MAAwB;AACzC,YAAMC,IAAU,MACVC,IAAM,gBACNC,IAAYH,EAAI,QAAQE,GAAK;AAAA,KAAU;AAC7C,UAAIE,IAAM;AACV,aAAOD,EACJ,MAAM;AAAA,CAAI,EACV,IAAI,CAACE,MAAS;AACb,YAAIC,IAAS;AACb,QAAI,SAAS,KAAKD,CAAI,IACpBD,IAAM,KAAK,IAAIA,IAAM,GAAG,CAAC,IAChB,oBAAoB,KAAKC,CAAI,KAAK,CAAC,aAAa,KAAKA,CAAI,MAClEC,IAAS;AAEX,cAAMC,IAASN,EAAQ,OAAOG,CAAG,IAAIC;AACrC,eAAAD,KAAOE,GACAC;AAAA,MACT,CAAC,EACA,KAAK;AAAA,CAAI;AAAA,IACd,GAEMC,IAAiB,CAACR,MAAwB;AAC9C,UAAI;AAEF,cAAMS,IADS,IAAI,UAAA,EACA,gBAAgBT,GAAK,iBAAiB;AACzD,YAAIS,EAAI,cAAc,aAAa,EAAG,QAAOT;AAE7C,cAAMU,IADa,IAAI,cAAA,EACO,kBAAkBD,CAAG;AACnD,eAAOV,EAAUW,CAAU;AAAA,MAC7B,QAAQ;AACN,eAAOV;AAAA,MACT;AAAA,IACF,GAEMW,IAAO,YAAY;AACvB,MAAAd,EAAQ,QAAQ,IAChBC,EAAM,QAAQ;AACd,UAAI;AACF,cAAMc,IAAM,MAAMC,EAAc3B,EAAM,KAAK,EAAE,SAASI,EAAQ,OAAO;AACrE,QAAAI,EAAQ,QAAQc,EAAeI,CAAG;AAClC,YAAI;AACF,UAAAhB,EAAY,QAAQ,MAAMkB,EAAWpB,EAAQ,OAAO;AAAA,YAClD,MAAM;AAAA,YACN,OAAOF,EAAc,UAAU,UAAU,iBAAiB;AAAA,UAAA,CAC3D;AAAA,QACH,QAAQ;AACN,UAAAI,EAAY,QAAQ;AAAA,QACtB;AAAA,MACF,SAASmB,GAAK;AACZ,gBAAQ,MAAMA,CAAG,GACjBjB,EAAM,QAAQV,EAAE,MAAM,iBAAiB;AAAA,MACzC,UAAA;AACE,QAAAS,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAEA,WAAAmB,EAAM,MAAM9B,EAAM,KAAKyB,GAAM,EAAE,WAAW,IAAM,GAChDK,EAAMxB,GAAe,MAAM;AACzB,MAAIE,EAAQ,SAAOiB,EAAA;AAAA,IACrB,CAAC,aAIYd,EAAA,SAAXoB,EAAA,GAAAC,EAIM,OAJNC,GAIM,CAAA,GAAAC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA;AAAA,MAHJC,EAEE,OAAA,EADA,OAAM,oHAAA,GAAmH,MAAA,EAAA;AAAA,IAAA,QAInGvB,EAAA,cAA1BwB,EAAoDC,GAAA;AAAA;MAAlB,SAASzB,EAAA;AAAA,IAAA,6BAE3CmB,KAAAC,EAOM,OAPNM,GAOM;AAAA,MALK5B,EAAA,cAITsB,EAAyD,OAAA;AAAA;QAA7C,OAAM;AAAA,QAAgB,WAAQtB,EAAA;AAAA,MAAA,wBAL1CsB,EAIC,OAJDO,GAICC,EADKhC,EAAA,KAAO,GAAA,CAAA;AAAA;;;"}
@@ -1,6 +1,6 @@
1
1
  import { defineComponent as M, ref as y, onMounted as R, watch as W, onBeforeUnmount as F, openBlock as g, createElementBlock as b, createElementVNode as _, toDisplayString as L, unref as B, createCommentVNode as E, createBlock as P, normalizeStyle as q } from "vue";
2
2
  import { init as T } from "pptx-preview";
3
- import { u as A, a as H } from "./index-Oxwizt6-.mjs";
3
+ import { u as A, a as H } from "./index-CFaWV6cC.mjs";
4
4
  import { _ as I } from "./RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs";
5
5
  const N = { class: "vfp-relative vfp-flex vfp-flex-col vfp-items-center vfp-w-full vfp-h-full" }, O = {
6
6
  key: 0,
@@ -137,4 +137,4 @@ const N = { class: "vfp-relative vfp-flex vfp-flex-col vfp-items-center vfp-w-fu
137
137
  export {
138
138
  J as default
139
139
  };
140
- //# sourceMappingURL=index-Cjnw238n.mjs.map
140
+ //# sourceMappingURL=index-CUA_z4GJ.mjs.map
@@ -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-CUA_z4GJ.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,5 +1,5 @@
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-Oxwizt6-.mjs";
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-CFaWV6cC.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
4
  import { _ as be } from "./RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.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({
@@ -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-Oxwizt6-.mjs").then((e) => e.i)
197
+ () => import("./index-CFaWV6cC.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-CyrFxdIJ.mjs.map
318
+ //# sourceMappingURL=index-Cr0Z36a0.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-Cr0Z36a0.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,6 +1,6 @@
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-Oxwizt6-.mjs";
3
+ import { u as B, b as _ } from "./index-CFaWV6cC.mjs";
4
4
  import { _ as N } from "./RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs";
5
5
  const P = {
6
6
  key: 1,
@@ -114,4 +114,4 @@ const P = {
114
114
  export {
115
115
  F as default
116
116
  };
117
- //# sourceMappingURL=index-Ctd0JQ6K.mjs.map
117
+ //# sourceMappingURL=index-DVJfd09T.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-Ctd0JQ6K.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
+ {"version":3,"file":"index-DVJfd09T.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,6 +1,6 @@
1
1
  import { defineComponent as P, ref as s, watch as y, nextTick as D, openBlock as a, createElementBlock as c, createElementVNode as f, createBlock as L, normalizeStyle as S, Fragment as $, renderList as B } from "vue";
2
2
  import M from "mammoth";
3
- import { u as N, a as F } from "./index-Oxwizt6-.mjs";
3
+ import { u as N, a as F } from "./index-CFaWV6cC.mjs";
4
4
  import { _ as I } from "./RendererError.vue_vue_type_script_setup_true_lang-CbgZBSDr.mjs";
5
5
  const j = {
6
6
  key: 0,
@@ -99,4 +99,4 @@ const j = {
99
99
  export {
100
100
  X as default
101
101
  };
102
- //# sourceMappingURL=index-DyipSoKh.mjs.map
102
+ //# sourceMappingURL=index-DYrRDrZL.mjs.map