@eternalheart/vue-file-preview 1.3.7 → 1.3.8

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 (39) hide show
  1. package/lib/chunks/{index-BI0dSTSM.mjs → index-5Smm2vx0.mjs} +2 -2
  2. package/lib/chunks/{index-BI0dSTSM.mjs.map → index-5Smm2vx0.mjs.map} +1 -1
  3. package/lib/chunks/{index-yMb1JUC4.mjs → index-BCkcZs4y.mjs} +2 -2
  4. package/lib/chunks/{index-yMb1JUC4.mjs.map → index-BCkcZs4y.mjs.map} +1 -1
  5. package/lib/chunks/{index-DUUsS_T4.mjs → index-BM9FaQUl.mjs} +2 -2
  6. package/lib/chunks/{index-DUUsS_T4.mjs.map → index-BM9FaQUl.mjs.map} +1 -1
  7. package/lib/chunks/{index-CcJ5NmY1.mjs → index-BYzQg-jL.mjs} +2 -2
  8. package/lib/chunks/{index-CcJ5NmY1.mjs.map → index-BYzQg-jL.mjs.map} +1 -1
  9. package/lib/chunks/{index-DTLDLOGO.mjs → index-BoL1b2w-.mjs} +2 -2
  10. package/lib/chunks/{index-DTLDLOGO.mjs.map → index-BoL1b2w-.mjs.map} +1 -1
  11. package/lib/chunks/{index-S_kGRh6H.mjs → index-Bri7VeRx.mjs} +2 -2
  12. package/lib/chunks/{index-S_kGRh6H.mjs.map → index-Bri7VeRx.mjs.map} +1 -1
  13. package/lib/chunks/{index-DpAxU2JM.mjs → index-C324a7kV.mjs} +3 -3
  14. package/lib/chunks/{index-DpAxU2JM.mjs.map → index-C324a7kV.mjs.map} +1 -1
  15. package/lib/chunks/{index-kBPb_OKm.mjs → index-C7uOM7XL.mjs} +2 -2
  16. package/lib/chunks/{index-kBPb_OKm.mjs.map → index-C7uOM7XL.mjs.map} +1 -1
  17. package/lib/chunks/{index-Doy0FvQW.mjs → index-CTsscgqY.mjs} +2 -2
  18. package/lib/chunks/{index-Doy0FvQW.mjs.map → index-CTsscgqY.mjs.map} +1 -1
  19. package/lib/chunks/{index-CBCQs1ZC.mjs → index-Ch7AEfnk.mjs} +2 -2
  20. package/lib/chunks/{index-CBCQs1ZC.mjs.map → index-Ch7AEfnk.mjs.map} +1 -1
  21. package/lib/chunks/{index-ClxBxmPR.mjs → index-D9wf6m37.mjs} +2 -2
  22. package/lib/chunks/{index-ClxBxmPR.mjs.map → index-D9wf6m37.mjs.map} +1 -1
  23. package/lib/chunks/{index-BW61ZRev.mjs → index-DAEQe-RI.mjs} +2 -2
  24. package/lib/chunks/{index-BW61ZRev.mjs.map → index-DAEQe-RI.mjs.map} +1 -1
  25. package/lib/chunks/{index-DsznjwsH.mjs → index-DAo-7spn.mjs} +2 -2
  26. package/lib/chunks/{index-DsznjwsH.mjs.map → index-DAo-7spn.mjs.map} +1 -1
  27. package/lib/chunks/{index-C8ZF84WX.mjs → index-DXlcF1Dk.mjs} +2 -2
  28. package/lib/chunks/{index-C8ZF84WX.mjs.map → index-DXlcF1Dk.mjs.map} +1 -1
  29. package/lib/chunks/{index-SAfVQyNJ.mjs → index-ELyTQZeF.mjs} +3 -3
  30. package/lib/chunks/{index-SAfVQyNJ.mjs.map → index-ELyTQZeF.mjs.map} +1 -1
  31. package/lib/chunks/{index-D9lByoJw.mjs → index-cZn4ih-x.mjs} +2 -2
  32. package/lib/chunks/{index-D9lByoJw.mjs.map → index-cZn4ih-x.mjs.map} +1 -1
  33. package/lib/chunks/{index-CB0zCxJb.mjs → index-dzB4jd9b.mjs} +2 -2
  34. package/lib/chunks/{index-CB0zCxJb.mjs.map → index-dzB4jd9b.mjs.map} +1 -1
  35. package/lib/chunks/{index-CbKSdAt_.mjs → index-lHlugUBI.mjs} +2 -2
  36. package/lib/chunks/{index-CbKSdAt_.mjs.map → index-lHlugUBI.mjs.map} +1 -1
  37. package/lib/index.cjs +1 -1
  38. package/lib/index.mjs +1 -1
  39. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import { defineComponent as I, ref as u, computed as S, onMounted as Q, onBeforeUnmount as $, watch as D, openBlock as s, createElementBlock as c, createElementVNode as p, normalizeStyle as R, renderSlot as B, normalizeClass as T, resolveComponent as Y, createVNode as W, unref as C, createBlock as M, resolveDynamicComponent as K, toDisplayString as _, Fragment as F, renderList as O, createCommentVNode as P, defineAsyncComponent as ee, withCtx as N, Teleport as te } from "vue";
2
- import { _ as V, d as ne, p as le, u as oe, e as H, F as se, v as ae, w as ie, i as re } from "./index-DpAxU2JM.mjs";
2
+ import { _ as V, d as ne, p as le, u as oe, e as H, F as se, v as ae, w as ie, i as re } from "./index-C324a7kV.mjs";
3
3
  import { FileImage as fe, FileText as ve, FileCode as de, File as pe, ChevronRight as ue, FolderOpen as ce, Folder as me } from "lucide-vue-next";
4
4
  const he = { class: "vfp-flex-1 vfp-min-w-0 vfp-min-h-0 vfp-overflow-hidden" }, we = /* @__PURE__ */ I({
5
5
  name: "ResizableSplit",
@@ -164,7 +164,7 @@ const he = { class: "vfp-flex-1 vfp-min-w-0 vfp-min-h-0 vfp-overflow-hidden" },
164
164
  emits: ["statsChange"],
165
165
  setup(a, { emit: r }) {
166
166
  const f = ee(
167
- () => import("./index-DpAxU2JM.mjs").then((t) => t.s)
167
+ () => import("./index-C324a7kV.mjs").then((t) => t.s)
168
168
  ), i = a, h = r, { t: g } = oe(), m = u(null), w = u(null), k = u(!0), x = u(null), l = u(/* @__PURE__ */ new Set([""])), e = u(null), o = u(!1), v = u(null), d = u(null), z = () => {
169
169
  var t;
170
170
  (t = e.value) != null && t.blobUrl && URL.revokeObjectURL(e.value.blobUrl);
@@ -278,4 +278,4 @@ const he = { class: "vfp-flex-1 vfp-min-w-0 vfp-min-h-0 vfp-overflow-hidden" },
278
278
  export {
279
279
  Be as default
280
280
  };
281
- //# sourceMappingURL=index-SAfVQyNJ.mjs.map
281
+ //# sourceMappingURL=index-ELyTQZeF.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-SAfVQyNJ.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}\n\nconst props = withDefaults(defineProps<Props>(), {\n initialLeftWidth: 280,\n minLeftWidth: 160,\n maxLeftWidth: 640,\n minRightWidth: 200,\n desktopMedia: '(min-width: 768px)',\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);\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</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 <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 </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';\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();\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);\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 fetch(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, load, { 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 } 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 <div v-else-if=\"error || !tree\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-text-fg-secondary vfp-text-center\"><p class=\"vfp-text-lg\">{{ error || t('zip.parse_failed') }}</p></div>\n </div>\n\n <template v-else>\n <ResizableSplit\n :initial-left-width=\"280\"\n :min-left-width=\"180\"\n :max-left-width=\"560\"\n storage-key=\"vfp-zip-split-left\"\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","mq","mqHandler","leftStyle","computed","onMove","e","rect","x","cap","effectiveMax","onUp","onDividerDown","onMounted","onBeforeUnmount","watch","w","_createElementBlock","_createElementVNode","_renderSlot","_ctx","_normalizeClass","_hoisted_1","__default__","emit","__emit","padStyle","isOpen","isSelected","fileIcon","ft","getFileType","FileImage","FileText","FileCode","FileIcon","handleEnter","handleLeave","_cache","$event","_createVNode","_unref","ChevronRight","_createBlock","_resolveDynamicComponent","FolderOpen","Folder","_hoisted_2","_toDisplayString","_openBlock","_Fragment","_renderList","child","_component_ZipTreeItem","p","n","t","r","_hoisted_3","_hoisted_4","LazyFilePreviewContent","defineAsyncComponent","useTranslator","zip","tree","loading","error","expanded","selected","previewLoading","previewError","hoverTip","revokeCurrent","_a","res","buf","z","loadZip","entries","listZipEntries","root","buildZipTree","init","c","err","totalStats","files","dirs","size","walk","s","handleToggle","path","next","handleHover","text","handleSelect","node","mime","inferMimeType","blob","readZipEntryBlob","blobUrl","previewFiles","ResizableSplit","_hoisted_5","TreeItem","_hoisted_6","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_7","_Teleport"],"mappings":"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"}
1
+ {"version":3,"file":"index-ELyTQZeF.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}\n\nconst props = withDefaults(defineProps<Props>(), {\n initialLeftWidth: 280,\n minLeftWidth: 160,\n maxLeftWidth: 640,\n minRightWidth: 200,\n desktopMedia: '(min-width: 768px)',\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);\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</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 <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 </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';\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();\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);\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 fetch(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, load, { 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 } 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 <div v-else-if=\"error || !tree\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-text-fg-secondary vfp-text-center\"><p class=\"vfp-text-lg\">{{ error || t('zip.parse_failed') }}</p></div>\n </div>\n\n <template v-else>\n <ResizableSplit\n :initial-left-width=\"280\"\n :min-left-width=\"180\"\n :max-left-width=\"560\"\n storage-key=\"vfp-zip-split-left\"\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","mq","mqHandler","leftStyle","computed","onMove","e","rect","x","cap","effectiveMax","onUp","onDividerDown","onMounted","onBeforeUnmount","watch","w","_createElementBlock","_createElementVNode","_renderSlot","_ctx","_normalizeClass","_hoisted_1","__default__","emit","__emit","padStyle","isOpen","isSelected","fileIcon","ft","getFileType","FileImage","FileText","FileCode","FileIcon","handleEnter","handleLeave","_cache","$event","_createVNode","_unref","ChevronRight","_createBlock","_resolveDynamicComponent","FolderOpen","Folder","_hoisted_2","_toDisplayString","_openBlock","_Fragment","_renderList","child","_component_ZipTreeItem","p","n","t","r","_hoisted_3","_hoisted_4","LazyFilePreviewContent","defineAsyncComponent","useTranslator","zip","tree","loading","error","expanded","selected","previewLoading","previewError","hoverTip","revokeCurrent","_a","res","buf","z","loadZip","entries","listZipEntries","root","buildZipTree","init","c","err","totalStats","files","dirs","size","walk","s","handleToggle","path","next","handleHover","text","handleSelect","node","mime","inferMimeType","blob","readZipEntryBlob","blobUrl","previewFiles","ResizableSplit","_hoisted_5","TreeItem","_hoisted_6","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_7","_Teleport"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { defineComponent as H, ref as x, onMounted as M, watch as S, onBeforeUnmount as j, openBlock as u, createElementBlock as g, createElementVNode as y, toDisplayString as b, createCommentVNode as w, withDirectives as W, vShow as q, nextTick as A } from "vue";
2
- import { S as F, u as I } from "./index-DpAxU2JM.mjs";
2
+ import { S as F, u as I } from "./index-C324a7kV.mjs";
3
3
  import * as D from "pdfjs-dist/build/pdf.mjs";
4
4
  const N = {
5
5
  key: 0,
@@ -108,4 +108,4 @@ const N = {
108
108
  export {
109
109
  X as default
110
110
  };
111
- //# sourceMappingURL=index-D9lByoJw.mjs.map
111
+ //# sourceMappingURL=index-cZn4ih-x.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-D9lByoJw.mjs","sources":["../../src/renderers/Pdf/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';\nimport { configurePdfWorker } from '@eternalheart/file-preview-core';\n// @ts-ignore - pdfjs-dist 类型路径\nimport * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';\nimport { useTranslator } from '../../composables/useTranslator';\n\n// 在模块加载时配置 PDF.js worker(默认走 CDN)\nconfigurePdfWorker(pdfjsLib);\n\ninterface PdfPageProxy {\n getViewport(opts: { scale: number }): { width: number; height: number };\n render(opts: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }): {\n promise: Promise<void>;\n };\n}\n\ninterface PdfDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<PdfPageProxy>;\n destroy(): void;\n}\n\nconst props = defineProps<{\n url: string;\n zoom: number;\n currentPage: number;\n}>();\n\nconst emit = defineEmits<{\n (e: 'pageChange', page: number): void;\n (e: 'totalPagesChange', total: number): void;\n (e: 'pageWidthChange', width: number): void;\n}>();\n\nconst { t } = useTranslator();\n\nconst numPages = ref(0);\nconst error = ref<string | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet pdfDoc: PdfDocumentProxy | null = null;\nconst pageElements: HTMLDivElement[] = [];\n\nconst renderPage = async (pageNumber: number, scale: number, container: HTMLDivElement) => {\n if (!pdfDoc) return;\n const page = await pdfDoc.getPage(pageNumber);\n const viewport = page.getViewport({ scale });\n\n const canvas = document.createElement('canvas');\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n canvas.style.maxWidth = '100%';\n canvas.style.height = 'auto';\n canvas.style.borderRadius = '0';\n canvas.style.display = 'block';\n canvas.style.boxShadow = '0 25px 50px -12px rgba(0,0,0,0.25)';\n canvas.style.filter = 'brightness(0.95) contrast(1.05)';\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n await page.render({ canvasContext: ctx, viewport }).promise;\n\n // 上报第一页原始宽度\n if (pageNumber === 1) {\n const baseViewport = page.getViewport({ scale: 1 });\n emit('pageWidthChange', baseViewport.width);\n }\n\n container.innerHTML = '';\n container.appendChild(canvas);\n\n // 页码标签\n const label = document.createElement('div');\n label.textContent = String(pageNumber);\n label.style.cssText =\n 'position:absolute;top:8px;right:8px;background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);color:white;font-size:12px;padding:4px 12px;border-radius:9999px;';\n container.appendChild(label);\n};\n\nconst renderAll = async () => {\n if (!pdfDoc || !containerRef.value) return;\n const wrapper = containerRef.value.querySelector('.pdf-pages') as HTMLDivElement | null;\n if (!wrapper) return;\n wrapper.innerHTML = '';\n pageElements.length = 0;\n\n for (let i = 1; i <= numPages.value; i++) {\n const pageDiv = document.createElement('div');\n pageDiv.style.cssText = 'position:relative;display:flex;justify-content:center;';\n wrapper.appendChild(pageDiv);\n pageElements.push(pageDiv);\n // eslint-disable-next-line no-await-in-loop\n await renderPage(i, props.zoom, pageDiv);\n }\n};\n\nconst loadPdf = async () => {\n error.value = null;\n numPages.value = 0;\n if (pdfDoc) {\n try {\n pdfDoc.destroy();\n } catch {\n // ignore\n }\n pdfDoc = null;\n }\n\n try {\n const loadingTask = pdfjsLib.getDocument({ url: props.url });\n pdfDoc = (await loadingTask.promise) as PdfDocumentProxy;\n numPages.value = pdfDoc.numPages;\n emit('totalPagesChange', pdfDoc.numPages);\n emit('pageChange', 1);\n await nextTick();\n await renderAll();\n } catch (err) {\n console.error('PDF 加载错误:', err);\n error.value = t.value('pdf.load_failed');\n }\n};\n\nconst handleScroll = () => {\n if (!containerRef.value) return;\n\n const container = containerRef.value;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const scrollCenter = scrollTop + containerHeight / 2;\n\n let currentVisiblePage = 1;\n let minDistance = Infinity;\n\n pageElements.forEach((pageEl, idx) => {\n const pageNumber = idx + 1;\n const rect = pageEl.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const pageCenter = rect.top - containerRect.top + rect.height / 2 + scrollTop;\n const distance = Math.abs(pageCenter - scrollCenter);\n\n if (distance < minDistance) {\n minDistance = distance;\n currentVisiblePage = pageNumber;\n }\n });\n\n if (currentVisiblePage !== props.currentPage) {\n emit('pageChange', currentVisiblePage);\n }\n};\n\nonMounted(() => {\n loadPdf();\n if (containerRef.value) {\n containerRef.value.addEventListener('scroll', handleScroll);\n }\n});\n\nwatch(\n () => props.url,\n () => {\n loadPdf();\n }\n);\n\nwatch(\n () => props.zoom,\n () => {\n renderAll();\n }\n);\n\nonBeforeUnmount(() => {\n if (containerRef.value) {\n containerRef.value.removeEventListener('scroll', handleScroll);\n }\n if (pdfDoc) {\n try {\n pdfDoc.destroy();\n } catch {\n // ignore\n }\n pdfDoc = null;\n }\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-flex vfp-flex-col vfp-items-center vfp-w-full vfp-h-full vfp-overflow-auto vfp-py-4 md:vfp-py-8 vfp-px-2 md:vfp-px-4\"\n >\n <div v-if=\"error\" class=\"vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error }}</p>\n </div>\n\n <div v-if=\"!error && numPages === 0\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-min-h-screen\">\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 <div v-show=\"!error && numPages > 0\" class=\"pdf-pages vfp-flex vfp-flex-col vfp-gap-4\" />\n\n <div\n v-if=\"numPages > 0\"\n class=\"vfp-sticky vfp-bottom-2 md:vfp-bottom-4 vfp-mt-4 md:vfp-mt-8 vfp-bg-surface-nav-hover vfp-backdrop-blur-xl vfp-text-fg-primary vfp-px-4 vfp-py-2 md:vfp-px-6 md:vfp-py-3 vfp-rounded-full vfp-text-xs md:vfp-text-sm vfp-font-medium vfp-shadow-2xl vfp-border vfp-border-line-weak\"\n >\n 第 {{ currentPage }} 页 / 共 {{ numPages }} 页\n </div>\n </div>\n</template>\n"],"names":["configurePdfWorker","pdfjsLib","props","__props","emit","__emit","t","useTranslator","numPages","ref","error","containerRef","pdfDoc","pageElements","renderPage","pageNumber","scale","container","page","viewport","canvas","ctx","baseViewport","label","renderAll","wrapper","i","pageDiv","loadPdf","nextTick","err","handleScroll","scrollTop","containerHeight","scrollCenter","currentVisiblePage","minDistance","pageEl","idx","rect","containerRect","pageCenter","distance","onMounted","watch","onBeforeUnmount","_createElementBlock","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","_hoisted_3","_cache","_withDirectives","_hoisted_4","_vShow","_hoisted_5"],"mappings":"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"}
1
+ {"version":3,"file":"index-cZn4ih-x.mjs","sources":["../../src/renderers/Pdf/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';\nimport { configurePdfWorker } from '@eternalheart/file-preview-core';\n// @ts-ignore - pdfjs-dist 类型路径\nimport * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';\nimport { useTranslator } from '../../composables/useTranslator';\n\n// 在模块加载时配置 PDF.js worker(默认走 CDN)\nconfigurePdfWorker(pdfjsLib);\n\ninterface PdfPageProxy {\n getViewport(opts: { scale: number }): { width: number; height: number };\n render(opts: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }): {\n promise: Promise<void>;\n };\n}\n\ninterface PdfDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<PdfPageProxy>;\n destroy(): void;\n}\n\nconst props = defineProps<{\n url: string;\n zoom: number;\n currentPage: number;\n}>();\n\nconst emit = defineEmits<{\n (e: 'pageChange', page: number): void;\n (e: 'totalPagesChange', total: number): void;\n (e: 'pageWidthChange', width: number): void;\n}>();\n\nconst { t } = useTranslator();\n\nconst numPages = ref(0);\nconst error = ref<string | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet pdfDoc: PdfDocumentProxy | null = null;\nconst pageElements: HTMLDivElement[] = [];\n\nconst renderPage = async (pageNumber: number, scale: number, container: HTMLDivElement) => {\n if (!pdfDoc) return;\n const page = await pdfDoc.getPage(pageNumber);\n const viewport = page.getViewport({ scale });\n\n const canvas = document.createElement('canvas');\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n canvas.style.maxWidth = '100%';\n canvas.style.height = 'auto';\n canvas.style.borderRadius = '0';\n canvas.style.display = 'block';\n canvas.style.boxShadow = '0 25px 50px -12px rgba(0,0,0,0.25)';\n canvas.style.filter = 'brightness(0.95) contrast(1.05)';\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n await page.render({ canvasContext: ctx, viewport }).promise;\n\n // 上报第一页原始宽度\n if (pageNumber === 1) {\n const baseViewport = page.getViewport({ scale: 1 });\n emit('pageWidthChange', baseViewport.width);\n }\n\n container.innerHTML = '';\n container.appendChild(canvas);\n\n // 页码标签\n const label = document.createElement('div');\n label.textContent = String(pageNumber);\n label.style.cssText =\n 'position:absolute;top:8px;right:8px;background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);color:white;font-size:12px;padding:4px 12px;border-radius:9999px;';\n container.appendChild(label);\n};\n\nconst renderAll = async () => {\n if (!pdfDoc || !containerRef.value) return;\n const wrapper = containerRef.value.querySelector('.pdf-pages') as HTMLDivElement | null;\n if (!wrapper) return;\n wrapper.innerHTML = '';\n pageElements.length = 0;\n\n for (let i = 1; i <= numPages.value; i++) {\n const pageDiv = document.createElement('div');\n pageDiv.style.cssText = 'position:relative;display:flex;justify-content:center;';\n wrapper.appendChild(pageDiv);\n pageElements.push(pageDiv);\n // eslint-disable-next-line no-await-in-loop\n await renderPage(i, props.zoom, pageDiv);\n }\n};\n\nconst loadPdf = async () => {\n error.value = null;\n numPages.value = 0;\n if (pdfDoc) {\n try {\n pdfDoc.destroy();\n } catch {\n // ignore\n }\n pdfDoc = null;\n }\n\n try {\n const loadingTask = pdfjsLib.getDocument({ url: props.url });\n pdfDoc = (await loadingTask.promise) as PdfDocumentProxy;\n numPages.value = pdfDoc.numPages;\n emit('totalPagesChange', pdfDoc.numPages);\n emit('pageChange', 1);\n await nextTick();\n await renderAll();\n } catch (err) {\n console.error('PDF 加载错误:', err);\n error.value = t.value('pdf.load_failed');\n }\n};\n\nconst handleScroll = () => {\n if (!containerRef.value) return;\n\n const container = containerRef.value;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const scrollCenter = scrollTop + containerHeight / 2;\n\n let currentVisiblePage = 1;\n let minDistance = Infinity;\n\n pageElements.forEach((pageEl, idx) => {\n const pageNumber = idx + 1;\n const rect = pageEl.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const pageCenter = rect.top - containerRect.top + rect.height / 2 + scrollTop;\n const distance = Math.abs(pageCenter - scrollCenter);\n\n if (distance < minDistance) {\n minDistance = distance;\n currentVisiblePage = pageNumber;\n }\n });\n\n if (currentVisiblePage !== props.currentPage) {\n emit('pageChange', currentVisiblePage);\n }\n};\n\nonMounted(() => {\n loadPdf();\n if (containerRef.value) {\n containerRef.value.addEventListener('scroll', handleScroll);\n }\n});\n\nwatch(\n () => props.url,\n () => {\n loadPdf();\n }\n);\n\nwatch(\n () => props.zoom,\n () => {\n renderAll();\n }\n);\n\nonBeforeUnmount(() => {\n if (containerRef.value) {\n containerRef.value.removeEventListener('scroll', handleScroll);\n }\n if (pdfDoc) {\n try {\n pdfDoc.destroy();\n } catch {\n // ignore\n }\n pdfDoc = null;\n }\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-flex vfp-flex-col vfp-items-center vfp-w-full vfp-h-full vfp-overflow-auto vfp-py-4 md:vfp-py-8 vfp-px-2 md:vfp-px-4\"\n >\n <div v-if=\"error\" class=\"vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error }}</p>\n </div>\n\n <div v-if=\"!error && numPages === 0\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-min-h-screen\">\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 <div v-show=\"!error && numPages > 0\" class=\"pdf-pages vfp-flex vfp-flex-col vfp-gap-4\" />\n\n <div\n v-if=\"numPages > 0\"\n class=\"vfp-sticky vfp-bottom-2 md:vfp-bottom-4 vfp-mt-4 md:vfp-mt-8 vfp-bg-surface-nav-hover vfp-backdrop-blur-xl vfp-text-fg-primary vfp-px-4 vfp-py-2 md:vfp-px-6 md:vfp-py-3 vfp-rounded-full vfp-text-xs md:vfp-text-sm vfp-font-medium vfp-shadow-2xl vfp-border vfp-border-line-weak\"\n >\n 第 {{ currentPage }} 页 / 共 {{ numPages }} 页\n </div>\n </div>\n</template>\n"],"names":["configurePdfWorker","pdfjsLib","props","__props","emit","__emit","t","useTranslator","numPages","ref","error","containerRef","pdfDoc","pageElements","renderPage","pageNumber","scale","container","page","viewport","canvas","ctx","baseViewport","label","renderAll","wrapper","i","pageDiv","loadPdf","nextTick","err","handleScroll","scrollTop","containerHeight","scrollCenter","currentVisiblePage","minDistance","pageEl","idx","rect","containerRect","pageCenter","distance","onMounted","watch","onBeforeUnmount","_createElementBlock","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","_hoisted_3","_cache","_withDirectives","_hoisted_4","_vShow","_hoisted_5"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { defineComponent as K, ref as T, watch as W, computed as l, openBlock as n, createElementBlock as o, createElementVNode as e, toDisplayString as i, unref as c, createVNode as u, createCommentVNode as r, normalizeStyle as J, Fragment as O, renderList as Q, createTextVNode as X, normalizeClass as Y } from "vue";
2
2
  import Z from "@kenjiuno/msgreader";
3
3
  import { User as ss, Users as x, Calendar as j, Clock as A, Tag as R, Mail as es, Paperclip as ts, Hash as as } from "lucide-vue-next";
4
- import { u as ns, _ as os } from "./index-DpAxU2JM.mjs";
4
+ import { u as ns, _ as os } from "./index-C324a7kV.mjs";
5
5
  const ls = {
6
6
  key: 0,
7
7
  class: "vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full"
@@ -350,4 +350,4 @@ const ls = {
350
350
  export {
351
351
  we as default
352
352
  };
353
- //# sourceMappingURL=index-CB0zCxJb.mjs.map
353
+ //# sourceMappingURL=index-dzB4jd9b.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-CB0zCxJb.mjs","sources":["../../src/renderers/Msg/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport MsgReader from '@kenjiuno/msgreader';\nimport type { FieldsData } from '@kenjiuno/msgreader';\nimport { User, Users, Paperclip, Calendar, Mail, Tag, Clock, Hash } from 'lucide-vue-next';\nimport { useTranslator } from '../../composables/useTranslator';\n\nconst props = defineProps<{\n url: string;\n}>();\n\nconst { t } = useTranslator();\n\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst fields = ref<FieldsData | null>(null);\n\nfunction formatRecipients(recipients: FieldsData[] | undefined, type: 'to' | 'cc' | 'bcc'): string {\n if (!recipients) return '';\n return recipients\n .filter((r) => r.recipType === type)\n .map((r) => {\n const name = r.name || '';\n const email = r.smtpAddress || r.email || '';\n if (name && email && name !== email) return `${name} <${email}>`;\n return name || email;\n })\n .filter(Boolean)\n .join('; ');\n}\n\nfunction formatDate(dateStr: string | undefined): string {\n if (!dateStr) return '';\n try {\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) return dateStr;\n return date.toLocaleString();\n } catch {\n return dateStr;\n }\n}\n\nfunction decodeHtmlBody(f: FieldsData, emptyBodyText: string): string {\n if (f.bodyHtml) return f.bodyHtml;\n if (f.html) {\n try {\n const decoder = new TextDecoder('utf-8');\n return decoder.decode(f.html);\n } catch {\n // fallback\n }\n }\n if (f.body) {\n return `<pre style=\"white-space: pre-wrap; word-wrap: break-word; font-family: system-ui, sans-serif;\">${f.body\n .replace(/&/g, '&amp;')\n .replace(/\\x3c/g, '&lt;')\n .replace(/>/g, '&gt;')}</pre>`;\n }\n return `<p style=\"color: #999;\">${emptyBodyText}</p>`;\n}\n\nfunction formatMessageClass(messageClass: string | undefined): string {\n if (!messageClass) return '';\n const classMap: Record<string, string> = {\n 'IPM.Note': 'Email',\n 'IPM.Note.SMIME': 'Encrypted Email',\n 'IPM.Note.SMIME.MultipartSigned': 'Signed Email',\n 'IPM.Appointment': 'Appointment',\n 'IPM.Schedule.Meeting.Request': 'Meeting Request',\n 'IPM.Schedule.Meeting.Canceled': 'Meeting Cancellation',\n 'IPM.Contact': 'Contact',\n 'IPM.Task': 'Task',\n 'IPM.StickyNote': 'Sticky Note',\n };\n return classMap[messageClass] || messageClass;\n}\n\nconst loadMsg = async () => {\n loading.value = true;\n error.value = null;\n fields.value = null;\n\n try {\n const response = await fetch(props.url);\n if (!response.ok) throw new Error('文件加载失败');\n const arrayBuffer = await response.arrayBuffer();\n const msgReader = new MsgReader(arrayBuffer);\n fields.value = msgReader.getFileData();\n } catch (err) {\n console.error('MSG 解析错误:', err);\n error.value = t.value('msg.parse_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, loadMsg, { immediate: true });\n\nconst subject = computed(() => fields.value?.subject || '(无主题)');\nconst sender = computed(() => {\n if (!fields.value) return '';\n const senderName = fields.value.senderName || '';\n const senderEmail = fields.value.senderSmtpAddress || fields.value.senderEmail || '';\n if (senderName && senderEmail && senderName !== senderEmail) {\n return `${senderName} <${senderEmail}>`;\n }\n return senderName || senderEmail;\n});\nconst toStr = computed(() => formatRecipients(fields.value?.recipients, 'to'));\nconst ccStr = computed(() => formatRecipients(fields.value?.recipients, 'cc'));\nconst bccStr = computed(() => formatRecipients(fields.value?.recipients, 'bcc'));\nconst sentDate = computed(() => formatDate(fields.value?.clientSubmitTime));\nconst receivedDate = computed(() => formatDate(fields.value?.messageDeliveryTime));\nconst createdDate = computed(() => formatDate(fields.value?.creationTime));\nconst lastModified = computed(() => formatDate(fields.value?.lastModificationTime));\nconst attachments = computed(() => (fields.value?.attachments || []).filter((a) => !a.attachmentHidden));\nconst bodyHtml = computed(() => (fields.value ? decodeHtmlBody(fields.value, t.value('msg.empty_body')) : ''));\nconst messageClass = computed(() => formatMessageClass(fields.value?.messageClass));\nconst messageId = computed(() => fields.value?.messageId || '');\n\nconst importance = computed(() => {\n const f = fields.value as (FieldsData & Record<string, unknown>) | null;\n return f && typeof f.importance === 'number' ? f.importance : undefined;\n});\nconst importanceLabel = computed(() => {\n if (importance.value === 2) return 'High';\n if (importance.value === 0) return 'Low';\n return '';\n});\n\nconst sensitivity = computed(() => {\n const f = fields.value as (FieldsData & Record<string, unknown>) | null;\n return f && typeof f.sensitivity === 'number' ? f.sensitivity : undefined;\n});\nconst sensitivityLabel = computed(() => {\n if (sensitivity.value === undefined || sensitivity.value === 0) return '';\n const labels: Record<number, string> = {\n 1: 'Personal',\n 2: 'Private',\n 3: 'Confidential',\n };\n return labels[sensitivity.value] || '';\n});\n\nconst formatAttachmentSize = (size: number | undefined) => {\n if (!size) return '';\n if (size > 1048576) return `${(size / 1048576).toFixed(1)} MB`;\n if (size > 1024) return `${(size / 1024).toFixed(0)} KB`;\n return `${size} B`;\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 <div v-else-if=\"error || !fields\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error || t('msg.parse_failed_short') }}</p>\n </div>\n </div>\n\n <div v-else class=\"vfp-w-full vfp-h-full vfp-overflow-auto\" style=\"background: white\">\n <div class=\"msg-container\">\n <!-- 邮件头部 -->\n <div class=\"msg-header\">\n <h2 class=\"msg-subject\">{{ subject }}</h2>\n\n <div class=\"msg-meta\">\n <div v-if=\"sender\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><User :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">From</span><span class=\"msg-value\">{{ sender }}</span></div>\n </div>\n\n <div v-if=\"toStr\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Users :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">To</span><span class=\"msg-value\">{{ toStr }}</span></div>\n </div>\n\n <div v-if=\"ccStr\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Users :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Cc</span><span class=\"msg-value\">{{ ccStr }}</span></div>\n </div>\n\n <div v-if=\"bccStr\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Users :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Bcc</span><span class=\"msg-value\">{{ bccStr }}</span></div>\n </div>\n\n <div v-if=\"sentDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Calendar :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Sent</span><span class=\"msg-value\">{{ sentDate }}</span></div>\n </div>\n\n <div v-if=\"receivedDate && receivedDate !== sentDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Clock :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Received</span><span class=\"msg-value\">{{ receivedDate }}</span></div>\n </div>\n\n <div v-if=\"!sentDate && !receivedDate && createdDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Calendar :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Date</span><span class=\"msg-value\">{{ createdDate }}</span></div>\n </div>\n\n <div v-if=\"importanceLabel\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Tag :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Importance</span>\n <span class=\"msg-value\" :style=\"{ color: importance === 2 ? '#dc2626' : '#2563eb', fontWeight: 500 }\">\n {{ importanceLabel }}\n </span>\n </div>\n </div>\n\n <div v-if=\"sensitivityLabel\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Tag :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Sensitivity</span><span class=\"msg-value\">{{ sensitivityLabel }}</span></div>\n </div>\n\n <div v-if=\"messageClass && messageClass !== 'Email'\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Mail :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Type</span><span class=\"msg-value\">{{ messageClass }}</span></div>\n </div>\n\n <div v-if=\"attachments.length > 0\" class=\"msg-row msg-row-bordered\">\n <span class=\"msg-icon-wrap\"><Paperclip :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Attachments</span>\n <div class=\"msg-value msg-attachments\">\n <span v-for=\"(a, i) in attachments\" :key=\"i\" class=\"msg-attachment\">\n {{ a.fileName || a.name || '未知文件' }}\n <span v-if=\"formatAttachmentSize(a.contentLength)\" class=\"msg-attachment-size\">\n ({{ formatAttachmentSize(a.contentLength) }})\n </span>\n </span>\n </div>\n </div>\n </div>\n\n <div v-if=\"messageId\" class=\"msg-row\" :class=\"{ 'msg-row-bordered': attachments.length === 0 }\">\n <span class=\"msg-icon-wrap\"><Hash :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Message-ID</span>\n <span class=\"msg-value msg-id\">{{ messageId }}</span>\n </div>\n </div>\n\n <div v-if=\"lastModified && lastModified !== sentDate && lastModified !== receivedDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Clock :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Modified</span>\n <span class=\"msg-value msg-id\">{{ lastModified }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 邮件正文 -->\n <div class=\"msg-body\" v-html=\"bodyHtml\" />\n </div>\n </div>\n</template>\n\n<style scoped>\n.msg-container {\n width: 100%;\n background: white;\n min-height: 100%;\n}\n.msg-header {\n border-bottom: 1px solid #e5e7eb;\n padding: clamp(12px, 3vw, 24px) clamp(16px, 3vw, 28px);\n background: #f9fafb;\n}\n.msg-subject {\n margin: 0 0 16px 0;\n font-size: clamp(16px, 2.5vw, 20px);\n font-weight: 600;\n color: #111827;\n line-height: 1.4;\n}\n.msg-meta {\n display: flex;\n flex-direction: column;\n font-size: clamp(12px, 1.8vw, 14px);\n color: #4b5563;\n}\n.msg-row {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 6px 0;\n}\n.msg-row-bordered {\n border-top: 1px solid #e5e7eb;\n margin-top: 4px;\n padding-top: 10px;\n}\n.msg-icon-wrap {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n height: 1.4em;\n}\n.msg-icon {\n flex-shrink: 0;\n color: #9ca3af;\n}\n.msg-row-content {\n display: flex;\n flex: 1;\n}\n.msg-label {\n flex-shrink: 0;\n color: #6b7280;\n font-weight: 500;\n margin-right: 8px;\n white-space: nowrap;\n}\n.msg-value {\n color: #111827;\n word-break: break-word;\n flex: 1;\n}\n.msg-id {\n font-size: 12px;\n color: #9ca3af;\n font-family: monospace;\n}\n.msg-attachments {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.msg-attachment {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: #f3f4f6;\n border-radius: 4px;\n font-size: 13px;\n color: #374151;\n border: 1px solid #e5e7eb;\n}\n.msg-attachment-size {\n color: #9ca3af;\n font-size: 12px;\n}\n.msg-body {\n padding: clamp(12px, 3vw, 24px) clamp(16px, 3vw, 28px);\n font-family: system-ui, -apple-system, sans-serif;\n line-height: 1.6;\n color: #333;\n overflow-x: auto;\n}\n</style>\n"],"names":["props","__props","t","useTranslator","loading","ref","error","fields","formatRecipients","recipients","type","r","name","email","formatDate","dateStr","date","decodeHtmlBody","f","emptyBodyText","formatMessageClass","messageClass","watch","response","arrayBuffer","msgReader","MsgReader","err","subject","computed","_a","sender","senderName","senderEmail","toStr","ccStr","bccStr","sentDate","receivedDate","createdDate","lastModified","attachments","a","bodyHtml","messageId","importance","importanceLabel","sensitivity","sensitivityLabel","formatAttachmentSize","size","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_toDisplayString","_unref","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_createVNode","User","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","Users","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","Calendar","_hoisted_28","_hoisted_29","_hoisted_30","_hoisted_31","Clock","_hoisted_32","_hoisted_33","_hoisted_34","_hoisted_35","_hoisted_36","_hoisted_37","_hoisted_38","_hoisted_39","Tag","_hoisted_40","_hoisted_41","_hoisted_42","_hoisted_43","_hoisted_44","_hoisted_45","_hoisted_46","Mail","_hoisted_47","_hoisted_48","_hoisted_49","_hoisted_50","Paperclip","_hoisted_51","_hoisted_52","_Fragment","_renderList","i","_createTextVNode","_hoisted_53","_normalizeClass","_hoisted_54","Hash","_hoisted_55","_hoisted_56","_hoisted_57","_hoisted_58","_hoisted_59","_hoisted_60"],"mappings":"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"}
1
+ {"version":3,"file":"index-dzB4jd9b.mjs","sources":["../../src/renderers/Msg/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport MsgReader from '@kenjiuno/msgreader';\nimport type { FieldsData } from '@kenjiuno/msgreader';\nimport { User, Users, Paperclip, Calendar, Mail, Tag, Clock, Hash } from 'lucide-vue-next';\nimport { useTranslator } from '../../composables/useTranslator';\n\nconst props = defineProps<{\n url: string;\n}>();\n\nconst { t } = useTranslator();\n\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst fields = ref<FieldsData | null>(null);\n\nfunction formatRecipients(recipients: FieldsData[] | undefined, type: 'to' | 'cc' | 'bcc'): string {\n if (!recipients) return '';\n return recipients\n .filter((r) => r.recipType === type)\n .map((r) => {\n const name = r.name || '';\n const email = r.smtpAddress || r.email || '';\n if (name && email && name !== email) return `${name} <${email}>`;\n return name || email;\n })\n .filter(Boolean)\n .join('; ');\n}\n\nfunction formatDate(dateStr: string | undefined): string {\n if (!dateStr) return '';\n try {\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) return dateStr;\n return date.toLocaleString();\n } catch {\n return dateStr;\n }\n}\n\nfunction decodeHtmlBody(f: FieldsData, emptyBodyText: string): string {\n if (f.bodyHtml) return f.bodyHtml;\n if (f.html) {\n try {\n const decoder = new TextDecoder('utf-8');\n return decoder.decode(f.html);\n } catch {\n // fallback\n }\n }\n if (f.body) {\n return `<pre style=\"white-space: pre-wrap; word-wrap: break-word; font-family: system-ui, sans-serif;\">${f.body\n .replace(/&/g, '&amp;')\n .replace(/\\x3c/g, '&lt;')\n .replace(/>/g, '&gt;')}</pre>`;\n }\n return `<p style=\"color: #999;\">${emptyBodyText}</p>`;\n}\n\nfunction formatMessageClass(messageClass: string | undefined): string {\n if (!messageClass) return '';\n const classMap: Record<string, string> = {\n 'IPM.Note': 'Email',\n 'IPM.Note.SMIME': 'Encrypted Email',\n 'IPM.Note.SMIME.MultipartSigned': 'Signed Email',\n 'IPM.Appointment': 'Appointment',\n 'IPM.Schedule.Meeting.Request': 'Meeting Request',\n 'IPM.Schedule.Meeting.Canceled': 'Meeting Cancellation',\n 'IPM.Contact': 'Contact',\n 'IPM.Task': 'Task',\n 'IPM.StickyNote': 'Sticky Note',\n };\n return classMap[messageClass] || messageClass;\n}\n\nconst loadMsg = async () => {\n loading.value = true;\n error.value = null;\n fields.value = null;\n\n try {\n const response = await fetch(props.url);\n if (!response.ok) throw new Error('文件加载失败');\n const arrayBuffer = await response.arrayBuffer();\n const msgReader = new MsgReader(arrayBuffer);\n fields.value = msgReader.getFileData();\n } catch (err) {\n console.error('MSG 解析错误:', err);\n error.value = t.value('msg.parse_failed');\n } finally {\n loading.value = false;\n }\n};\n\nwatch(() => props.url, loadMsg, { immediate: true });\n\nconst subject = computed(() => fields.value?.subject || '(无主题)');\nconst sender = computed(() => {\n if (!fields.value) return '';\n const senderName = fields.value.senderName || '';\n const senderEmail = fields.value.senderSmtpAddress || fields.value.senderEmail || '';\n if (senderName && senderEmail && senderName !== senderEmail) {\n return `${senderName} <${senderEmail}>`;\n }\n return senderName || senderEmail;\n});\nconst toStr = computed(() => formatRecipients(fields.value?.recipients, 'to'));\nconst ccStr = computed(() => formatRecipients(fields.value?.recipients, 'cc'));\nconst bccStr = computed(() => formatRecipients(fields.value?.recipients, 'bcc'));\nconst sentDate = computed(() => formatDate(fields.value?.clientSubmitTime));\nconst receivedDate = computed(() => formatDate(fields.value?.messageDeliveryTime));\nconst createdDate = computed(() => formatDate(fields.value?.creationTime));\nconst lastModified = computed(() => formatDate(fields.value?.lastModificationTime));\nconst attachments = computed(() => (fields.value?.attachments || []).filter((a) => !a.attachmentHidden));\nconst bodyHtml = computed(() => (fields.value ? decodeHtmlBody(fields.value, t.value('msg.empty_body')) : ''));\nconst messageClass = computed(() => formatMessageClass(fields.value?.messageClass));\nconst messageId = computed(() => fields.value?.messageId || '');\n\nconst importance = computed(() => {\n const f = fields.value as (FieldsData & Record<string, unknown>) | null;\n return f && typeof f.importance === 'number' ? f.importance : undefined;\n});\nconst importanceLabel = computed(() => {\n if (importance.value === 2) return 'High';\n if (importance.value === 0) return 'Low';\n return '';\n});\n\nconst sensitivity = computed(() => {\n const f = fields.value as (FieldsData & Record<string, unknown>) | null;\n return f && typeof f.sensitivity === 'number' ? f.sensitivity : undefined;\n});\nconst sensitivityLabel = computed(() => {\n if (sensitivity.value === undefined || sensitivity.value === 0) return '';\n const labels: Record<number, string> = {\n 1: 'Personal',\n 2: 'Private',\n 3: 'Confidential',\n };\n return labels[sensitivity.value] || '';\n});\n\nconst formatAttachmentSize = (size: number | undefined) => {\n if (!size) return '';\n if (size > 1048576) return `${(size / 1048576).toFixed(1)} MB`;\n if (size > 1024) return `${(size / 1024).toFixed(0)} KB`;\n return `${size} B`;\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 <div v-else-if=\"error || !fields\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error || t('msg.parse_failed_short') }}</p>\n </div>\n </div>\n\n <div v-else class=\"vfp-w-full vfp-h-full vfp-overflow-auto\" style=\"background: white\">\n <div class=\"msg-container\">\n <!-- 邮件头部 -->\n <div class=\"msg-header\">\n <h2 class=\"msg-subject\">{{ subject }}</h2>\n\n <div class=\"msg-meta\">\n <div v-if=\"sender\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><User :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">From</span><span class=\"msg-value\">{{ sender }}</span></div>\n </div>\n\n <div v-if=\"toStr\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Users :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">To</span><span class=\"msg-value\">{{ toStr }}</span></div>\n </div>\n\n <div v-if=\"ccStr\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Users :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Cc</span><span class=\"msg-value\">{{ ccStr }}</span></div>\n </div>\n\n <div v-if=\"bccStr\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Users :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Bcc</span><span class=\"msg-value\">{{ bccStr }}</span></div>\n </div>\n\n <div v-if=\"sentDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Calendar :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Sent</span><span class=\"msg-value\">{{ sentDate }}</span></div>\n </div>\n\n <div v-if=\"receivedDate && receivedDate !== sentDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Clock :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Received</span><span class=\"msg-value\">{{ receivedDate }}</span></div>\n </div>\n\n <div v-if=\"!sentDate && !receivedDate && createdDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Calendar :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Date</span><span class=\"msg-value\">{{ createdDate }}</span></div>\n </div>\n\n <div v-if=\"importanceLabel\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Tag :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Importance</span>\n <span class=\"msg-value\" :style=\"{ color: importance === 2 ? '#dc2626' : '#2563eb', fontWeight: 500 }\">\n {{ importanceLabel }}\n </span>\n </div>\n </div>\n\n <div v-if=\"sensitivityLabel\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Tag :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Sensitivity</span><span class=\"msg-value\">{{ sensitivityLabel }}</span></div>\n </div>\n\n <div v-if=\"messageClass && messageClass !== 'Email'\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Mail :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\"><span class=\"msg-label\">Type</span><span class=\"msg-value\">{{ messageClass }}</span></div>\n </div>\n\n <div v-if=\"attachments.length > 0\" class=\"msg-row msg-row-bordered\">\n <span class=\"msg-icon-wrap\"><Paperclip :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Attachments</span>\n <div class=\"msg-value msg-attachments\">\n <span v-for=\"(a, i) in attachments\" :key=\"i\" class=\"msg-attachment\">\n {{ a.fileName || a.name || '未知文件' }}\n <span v-if=\"formatAttachmentSize(a.contentLength)\" class=\"msg-attachment-size\">\n ({{ formatAttachmentSize(a.contentLength) }})\n </span>\n </span>\n </div>\n </div>\n </div>\n\n <div v-if=\"messageId\" class=\"msg-row\" :class=\"{ 'msg-row-bordered': attachments.length === 0 }\">\n <span class=\"msg-icon-wrap\"><Hash :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Message-ID</span>\n <span class=\"msg-value msg-id\">{{ messageId }}</span>\n </div>\n </div>\n\n <div v-if=\"lastModified && lastModified !== sentDate && lastModified !== receivedDate\" class=\"msg-row\">\n <span class=\"msg-icon-wrap\"><Clock :size=\"16\" class=\"msg-icon\" /></span>\n <div class=\"msg-row-content\">\n <span class=\"msg-label\">Modified</span>\n <span class=\"msg-value msg-id\">{{ lastModified }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 邮件正文 -->\n <div class=\"msg-body\" v-html=\"bodyHtml\" />\n </div>\n </div>\n</template>\n\n<style scoped>\n.msg-container {\n width: 100%;\n background: white;\n min-height: 100%;\n}\n.msg-header {\n border-bottom: 1px solid #e5e7eb;\n padding: clamp(12px, 3vw, 24px) clamp(16px, 3vw, 28px);\n background: #f9fafb;\n}\n.msg-subject {\n margin: 0 0 16px 0;\n font-size: clamp(16px, 2.5vw, 20px);\n font-weight: 600;\n color: #111827;\n line-height: 1.4;\n}\n.msg-meta {\n display: flex;\n flex-direction: column;\n font-size: clamp(12px, 1.8vw, 14px);\n color: #4b5563;\n}\n.msg-row {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 6px 0;\n}\n.msg-row-bordered {\n border-top: 1px solid #e5e7eb;\n margin-top: 4px;\n padding-top: 10px;\n}\n.msg-icon-wrap {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n height: 1.4em;\n}\n.msg-icon {\n flex-shrink: 0;\n color: #9ca3af;\n}\n.msg-row-content {\n display: flex;\n flex: 1;\n}\n.msg-label {\n flex-shrink: 0;\n color: #6b7280;\n font-weight: 500;\n margin-right: 8px;\n white-space: nowrap;\n}\n.msg-value {\n color: #111827;\n word-break: break-word;\n flex: 1;\n}\n.msg-id {\n font-size: 12px;\n color: #9ca3af;\n font-family: monospace;\n}\n.msg-attachments {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.msg-attachment {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: #f3f4f6;\n border-radius: 4px;\n font-size: 13px;\n color: #374151;\n border: 1px solid #e5e7eb;\n}\n.msg-attachment-size {\n color: #9ca3af;\n font-size: 12px;\n}\n.msg-body {\n padding: clamp(12px, 3vw, 24px) clamp(16px, 3vw, 28px);\n font-family: system-ui, -apple-system, sans-serif;\n line-height: 1.6;\n color: #333;\n overflow-x: auto;\n}\n</style>\n"],"names":["props","__props","t","useTranslator","loading","ref","error","fields","formatRecipients","recipients","type","r","name","email","formatDate","dateStr","date","decodeHtmlBody","f","emptyBodyText","formatMessageClass","messageClass","watch","response","arrayBuffer","msgReader","MsgReader","err","subject","computed","_a","sender","senderName","senderEmail","toStr","ccStr","bccStr","sentDate","receivedDate","createdDate","lastModified","attachments","a","bodyHtml","messageId","importance","importanceLabel","sensitivity","sensitivityLabel","formatAttachmentSize","size","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_toDisplayString","_unref","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_createVNode","User","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","Users","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","Calendar","_hoisted_28","_hoisted_29","_hoisted_30","_hoisted_31","Clock","_hoisted_32","_hoisted_33","_hoisted_34","_hoisted_35","_hoisted_36","_hoisted_37","_hoisted_38","_hoisted_39","Tag","_hoisted_40","_hoisted_41","_hoisted_42","_hoisted_43","_hoisted_44","_hoisted_45","_hoisted_46","Mail","_hoisted_47","_hoisted_48","_hoisted_49","_hoisted_50","Paperclip","_hoisted_51","_hoisted_52","_Fragment","_renderList","i","_createTextVNode","_hoisted_53","_normalizeClass","_hoisted_54","Hash","_hoisted_55","_hoisted_56","_hoisted_57","_hoisted_58","_hoisted_59","_hoisted_60"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { ref as p, watch as z, onUnmounted as D, defineComponent as U, toRef as O, computed as $, onMounted as q, onBeforeUnmount as H, unref as t, openBlock as b, createElementBlock as I, createElementVNode as a, toDisplayString as G, normalizeStyle as M, createStaticVNode as W, normalizeClass as A, createVNode as C, createBlock as R, resolveDynamicComponent as X, Transition as J, withCtx as K, createCommentVNode as Q } from "vue";
2
2
  import { VolumeX as Y, Volume1 as Z, Volume2 as ee, Repeat as te, SkipBack as ae, Pause as re, Play as ne, SkipForward as oe } from "lucide-vue-next";
3
- import { f as se, u as le, _ as ie } from "./index-DpAxU2JM.mjs";
3
+ import { f as se, u as le, _ as ie } from "./index-C324a7kV.mjs";
4
4
  function pe(x) {
5
5
  const f = p(null), m = p(!1), o = p(!0), h = p(0), d = p(0), y = p(1), u = p(!1), v = p(!1), c = p(null);
6
6
  let l = null;
@@ -388,4 +388,4 @@ const fe = {
388
388
  export {
389
389
  ze as default
390
390
  };
391
- //# sourceMappingURL=index-CbKSdAt_.mjs.map
391
+ //# sourceMappingURL=index-lHlugUBI.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-CbKSdAt_.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';\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 <div v-if=\"error\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error }}</p>\n </div>\n </div>\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","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_normalizeStyle","_hoisted_6","_hoisted_7","_cache","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_normalizeClass","args","_createVNode","Repeat","SkipBack","_createBlock","Pause","Play","SkipForward","_resolveDynamicComponent","_Transition","_hoisted_21","_hoisted_22","_hoisted_24"],"mappings":"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"}
1
+ {"version":3,"file":"index-lHlugUBI.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';\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 <div v-if=\"error\" class=\"vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full\">\n <div class=\"vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error }}</p>\n </div>\n </div>\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","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_normalizeStyle","_hoisted_6","_hoisted_7","_cache","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_normalizeClass","args","_createVNode","Repeat","SkipBack","_createBlock","Pause","Play","SkipForward","_resolveDynamicComponent","_Transition","_hoisted_21","_hoisted_22","_hoisted_24"],"mappings":"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"}