@eternalheart/vue-file-preview 1.3.6 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/FilePreviewContent.vue.d.ts.map +1 -1
- package/lib/FilePreviewEmbed.vue.d.ts.map +1 -1
- package/lib/FilePreviewModal.vue.d.ts.map +1 -1
- package/lib/chunks/index-BI0dSTSM.mjs +77 -0
- package/lib/chunks/index-BI0dSTSM.mjs.map +1 -0
- package/lib/chunks/index-BW61ZRev.mjs +111 -0
- package/lib/chunks/index-BW61ZRev.mjs.map +1 -0
- package/lib/chunks/index-C8ZF84WX.mjs +113 -0
- package/lib/chunks/index-C8ZF84WX.mjs.map +1 -0
- package/lib/chunks/index-CB0zCxJb.mjs +353 -0
- package/lib/chunks/index-CB0zCxJb.mjs.map +1 -0
- package/lib/chunks/index-CBCQs1ZC.mjs +116 -0
- package/lib/chunks/index-CBCQs1ZC.mjs.map +1 -0
- package/lib/chunks/index-CbKSdAt_.mjs +391 -0
- package/lib/chunks/index-CbKSdAt_.mjs.map +1 -0
- package/lib/chunks/index-CcJ5NmY1.mjs +152 -0
- package/lib/chunks/index-CcJ5NmY1.mjs.map +1 -0
- package/lib/chunks/index-ClxBxmPR.mjs +249 -0
- package/lib/chunks/index-ClxBxmPR.mjs.map +1 -0
- package/lib/chunks/index-D9lByoJw.mjs +111 -0
- package/lib/chunks/index-D9lByoJw.mjs.map +1 -0
- package/lib/chunks/index-DTLDLOGO.mjs +129 -0
- package/lib/chunks/index-DTLDLOGO.mjs.map +1 -0
- package/lib/chunks/index-DUUsS_T4.mjs +134 -0
- package/lib/chunks/index-DUUsS_T4.mjs.map +1 -0
- package/lib/chunks/index-Doy0FvQW.mjs +136 -0
- package/lib/chunks/index-Doy0FvQW.mjs.map +1 -0
- package/lib/chunks/index-DpAxU2JM.mjs +2017 -0
- package/lib/chunks/index-DpAxU2JM.mjs.map +1 -0
- package/lib/chunks/index-DsznjwsH.mjs +103 -0
- package/lib/chunks/index-DsznjwsH.mjs.map +1 -0
- package/lib/chunks/index-SAfVQyNJ.mjs +281 -0
- package/lib/chunks/index-SAfVQyNJ.mjs.map +1 -0
- package/lib/chunks/index-S_kGRh6H.mjs +203 -0
- package/lib/chunks/index-S_kGRh6H.mjs.map +1 -0
- package/lib/chunks/index-kBPb_OKm.mjs +139 -0
- package/lib/chunks/index-kBPb_OKm.mjs.map +1 -0
- package/lib/chunks/index-yMb1JUC4.mjs +84 -0
- package/lib/chunks/index-yMb1JUC4.mjs.map +1 -0
- package/lib/composables/useResolvedTheme.d.ts +15 -0
- package/lib/composables/useResolvedTheme.d.ts.map +1 -0
- package/lib/index.cjs +24 -499
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.mjs +12 -44597
- package/lib/index.mjs.map +1 -1
- package/lib/renderers/Audio/index.vue.d.ts.map +1 -1
- package/lib/renderers/Csv/index.vue.d.ts.map +1 -1
- package/lib/renderers/Json/index.vue.d.ts.map +1 -1
- package/lib/renderers/Markdown/index.vue.d.ts.map +1 -1
- package/lib/renderers/Pptx/index.vue.d.ts.map +1 -1
- package/lib/renderers/RendererLoading.vue.d.ts +3 -0
- package/lib/renderers/RendererLoading.vue.d.ts.map +1 -0
- package/lib/renderers/Text/index.vue.d.ts.map +1 -1
- package/lib/renderers/Video/index.vue.d.ts.map +1 -1
- package/lib/renderers/Xlsx/index.vue.d.ts.map +1 -1
- package/lib/renderers/Xml/index.vue.d.ts.map +1 -1
- package/lib/renderers/Zip/index.vue.d.ts.map +1 -1
- package/lib/renderers/lazy.d.ts +19 -0
- package/lib/renderers/lazy.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { defineComponent as U, ref as o, watch as z, onMounted as V, onBeforeUnmount as O, computed as E, openBlock as x, createElementBlock as y, normalizeStyle as Y, createElementVNode as C, createCommentVNode as k, toDisplayString as w, normalizeClass as Z } from "vue";
|
|
2
|
+
import { u as q, p as A } from "./index-DpAxU2JM.mjs";
|
|
3
|
+
const F = {
|
|
4
|
+
key: 0,
|
|
5
|
+
class: "vfp-flex vfp-items-center vfp-justify-center"
|
|
6
|
+
}, G = {
|
|
7
|
+
key: 1,
|
|
8
|
+
class: "vfp-text-fg-secondary vfp-text-center"
|
|
9
|
+
}, I = { class: "vfp-text-lg" }, J = ["src"], Q = {
|
|
10
|
+
key: 2,
|
|
11
|
+
class: "vfp-absolute vfp-bottom-2 vfp-right-3 vfp-text-[10px] vfp-text-fg-disabled hover:vfp-text-fg-secondary vfp-transition-colors vfp-pointer-events-auto vfp-select-none vfp-cursor-default"
|
|
12
|
+
}, ne = /* @__PURE__ */ U({
|
|
13
|
+
__name: "index",
|
|
14
|
+
props: {
|
|
15
|
+
url: {},
|
|
16
|
+
zoom: {},
|
|
17
|
+
rotation: {},
|
|
18
|
+
resetKey: {},
|
|
19
|
+
fileSize: {}
|
|
20
|
+
},
|
|
21
|
+
emits: ["zoomChange", "naturalWidthChange", "naturalHeightChange"],
|
|
22
|
+
setup(S, { emit: R }) {
|
|
23
|
+
const u = S, m = R, { t: X } = q(), l = o(!1), v = o(null), n = o({ x: 0, y: 0 }), f = o(!1);
|
|
24
|
+
let M = { x: 0, y: 0 };
|
|
25
|
+
const r = o(1), s = o({ width: 0, height: 0 }), $ = o(null), d = o(null);
|
|
26
|
+
z(
|
|
27
|
+
() => u.url,
|
|
28
|
+
() => {
|
|
29
|
+
l.value = !1, v.value = null, n.value = { x: 0, y: 0 }, r.value = 1;
|
|
30
|
+
}
|
|
31
|
+
), z(
|
|
32
|
+
() => u.zoom,
|
|
33
|
+
(e) => {
|
|
34
|
+
r.value = e;
|
|
35
|
+
}
|
|
36
|
+
), z(
|
|
37
|
+
() => u.resetKey,
|
|
38
|
+
() => {
|
|
39
|
+
n.value = { x: 0, y: 0 };
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
const W = (e, t) => {
|
|
43
|
+
const a = d.value;
|
|
44
|
+
if (!a || s.value.width === 0) return e;
|
|
45
|
+
const h = a.clientWidth, p = a.clientHeight, _ = s.value.width * t, g = s.value.height * t, i = Math.min(80, h * 0.15, p * 0.15), c = (h + _) / 2 - i, b = (p + g) / 2 - i;
|
|
46
|
+
return {
|
|
47
|
+
x: c > 0 ? Math.max(-c, Math.min(c, e.x)) : 0,
|
|
48
|
+
y: b > 0 ? Math.max(-b, Math.min(b, e.y)) : 0
|
|
49
|
+
};
|
|
50
|
+
}, B = (e) => {
|
|
51
|
+
l.value = !0;
|
|
52
|
+
const t = e.currentTarget;
|
|
53
|
+
s.value = { width: t.naturalWidth, height: t.naturalHeight }, m("naturalWidthChange", t.naturalWidth), m("naturalHeightChange", t.naturalHeight);
|
|
54
|
+
}, L = () => {
|
|
55
|
+
v.value = X.value("image.load_failed"), l.value = !0;
|
|
56
|
+
}, N = () => {
|
|
57
|
+
n.value = { x: 0, y: 0 }, r.value = 1, m("zoomChange", 1);
|
|
58
|
+
}, H = (e) => {
|
|
59
|
+
e.preventDefault(), e.stopPropagation();
|
|
60
|
+
const t = d.value;
|
|
61
|
+
if (!t) return;
|
|
62
|
+
const a = t.getBoundingClientRect(), h = e.clientX - a.left - a.width / 2, p = e.clientY - a.top - a.height / 2, _ = e.deltaY > 0 ? -0.05 : 0.05, g = r.value, i = Math.max(0.01, Math.min(10, g + _)), c = i / g;
|
|
63
|
+
n.value = W(
|
|
64
|
+
{
|
|
65
|
+
x: h - c * (h - n.value.x),
|
|
66
|
+
y: p - c * (p - n.value.y)
|
|
67
|
+
},
|
|
68
|
+
i
|
|
69
|
+
), r.value = i, m("zoomChange", i);
|
|
70
|
+
};
|
|
71
|
+
V(() => {
|
|
72
|
+
const e = d.value;
|
|
73
|
+
e && e.addEventListener("wheel", H, { passive: !1 });
|
|
74
|
+
}), O(() => {
|
|
75
|
+
const e = d.value;
|
|
76
|
+
e && e.removeEventListener("wheel", H);
|
|
77
|
+
});
|
|
78
|
+
const P = (e) => {
|
|
79
|
+
e.button === 0 && (f.value = !0, M = {
|
|
80
|
+
x: e.clientX - n.value.x,
|
|
81
|
+
y: e.clientY - n.value.y
|
|
82
|
+
});
|
|
83
|
+
}, T = (e) => {
|
|
84
|
+
f.value && (n.value = W(
|
|
85
|
+
{
|
|
86
|
+
x: e.clientX - M.x,
|
|
87
|
+
y: e.clientY - M.y
|
|
88
|
+
},
|
|
89
|
+
r.value
|
|
90
|
+
));
|
|
91
|
+
}, D = () => {
|
|
92
|
+
f.value = !1;
|
|
93
|
+
}, j = E(() => ({
|
|
94
|
+
transform: `translate(${n.value.x}px, ${n.value.y}px) scale(${r.value}) rotate(${u.rotation}deg)`,
|
|
95
|
+
transformOrigin: "center",
|
|
96
|
+
transition: f.value ? "none" : "transform 0.3s ease-out",
|
|
97
|
+
opacity: l.value ? 1 : 0
|
|
98
|
+
})), K = E(() => u.fileSize == null ? "" : ` · ${A(u.fileSize)}`);
|
|
99
|
+
return (e, t) => (x(), y("div", {
|
|
100
|
+
ref_key: "containerRef",
|
|
101
|
+
ref: d,
|
|
102
|
+
class: "vfp-relative vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-overflow-hidden",
|
|
103
|
+
style: Y({ cursor: f.value ? "grabbing" : "grab" }),
|
|
104
|
+
onMousedown: P,
|
|
105
|
+
onMousemove: T,
|
|
106
|
+
onMouseup: D,
|
|
107
|
+
onMouseleave: D
|
|
108
|
+
}, [
|
|
109
|
+
!l.value && !v.value ? (x(), y("div", F, [...t[0] || (t[0] = [
|
|
110
|
+
C("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" }, null, -1)
|
|
111
|
+
])])) : k("", !0),
|
|
112
|
+
v.value ? (x(), y("div", G, [
|
|
113
|
+
C("p", I, w(v.value), 1)
|
|
114
|
+
])) : k("", !0),
|
|
115
|
+
C("img", {
|
|
116
|
+
ref_key: "imgRef",
|
|
117
|
+
ref: $,
|
|
118
|
+
src: S.url,
|
|
119
|
+
alt: "Preview",
|
|
120
|
+
class: Z(["vfp-max-w-none vfp-select-none", !l.value && "vfp-hidden"]),
|
|
121
|
+
style: Y(j.value),
|
|
122
|
+
draggable: !1,
|
|
123
|
+
onLoad: B,
|
|
124
|
+
onError: L,
|
|
125
|
+
onDblclick: N
|
|
126
|
+
}, null, 46, J),
|
|
127
|
+
l.value && !v.value && s.value.width > 0 ? (x(), y("div", Q, w(s.value.width) + " × " + w(s.value.height) + w(K.value), 1)) : k("", !0)
|
|
128
|
+
], 36));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
export {
|
|
132
|
+
ne as default
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=index-DUUsS_T4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DUUsS_T4.mjs","sources":["../../src/renderers/Image/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue';\nimport { formatFileSize } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\n\nconst props = defineProps<{\n url: string;\n zoom: number;\n rotation: number;\n resetKey?: number;\n fileSize?: number;\n}>();\n\nconst emit = defineEmits<{\n (e: 'zoomChange', zoom: number): void;\n (e: 'naturalWidthChange', width: number): void;\n (e: 'naturalHeightChange', height: number): void;\n}>();\n\nconst { t } = useTranslator();\n\nconst loaded = ref(false);\nconst error = ref<string | null>(null);\nconst position = ref({ x: 0, y: 0 });\nconst isDragging = ref(false);\nlet dragStart = { x: 0, y: 0 };\nconst internalZoom = ref(1);\nconst naturalSize = ref({ width: 0, height: 0 });\n\nconst imgRef = ref<HTMLImageElement | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\n\nwatch(\n () => props.url,\n () => {\n loaded.value = false;\n error.value = null;\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n }\n);\n\nwatch(\n () => props.zoom,\n (z) => {\n internalZoom.value = z;\n }\n);\n\nwatch(\n () => props.resetKey,\n () => {\n position.value = { x: 0, y: 0 };\n }\n);\n\nconst clampPosition = (pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.value;\n if (!container || naturalSize.value.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.value.width * currentZoom;\n const imgH = naturalSize.value.height * currentZoom;\n\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n};\n\nconst handleLoad = (e: Event) => {\n loaded.value = true;\n const img = e.currentTarget as HTMLImageElement;\n naturalSize.value = { width: img.naturalWidth, height: img.naturalHeight };\n emit('naturalWidthChange', img.naturalWidth);\n emit('naturalHeightChange', img.naturalHeight);\n};\n\nconst handleError = () => {\n error.value = t.value('image.load_failed');\n loaded.value = true;\n};\n\nconst handleDoubleClick = () => {\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n emit('zoomChange', 1);\n};\n\n// 鼠标滚轮缩放\nconst handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const container = containerRef.value;\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n const prev = internalZoom.value;\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n position.value = clampPosition(\n {\n x: mouseX - scale * (mouseX - position.value.x),\n y: mouseY - scale * (mouseY - position.value.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n emit('zoomChange', newZoom);\n};\n\nonMounted(() => {\n const container = containerRef.value;\n if (container) {\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n }\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n }\n});\n\nconst handleMouseDown = (e: MouseEvent) => {\n if (e.button !== 0) return;\n isDragging.value = true;\n dragStart = {\n x: e.clientX - position.value.x,\n y: e.clientY - position.value.y,\n };\n};\n\nconst handleMouseMove = (e: MouseEvent) => {\n if (!isDragging.value) return;\n position.value = clampPosition(\n {\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n },\n internalZoom.value\n );\n};\n\nconst handleMouseUp = () => {\n isDragging.value = false;\n};\n\nconst transformStyle = computed(() => ({\n transform: `translate(${position.value.x}px, ${position.value.y}px) scale(${internalZoom.value}) rotate(${props.rotation}deg)`,\n transformOrigin: 'center',\n transition: isDragging.value ? 'none' : 'transform 0.3s ease-out',\n opacity: loaded.value ? 1 : 0,\n}));\n\nconst sizeText = computed(() => {\n if (props.fileSize == null) return '';\n return ` · ${formatFileSize(props.fileSize)}`;\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-relative vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-overflow-hidden\"\n :style=\"{ cursor: isDragging ? 'grabbing' : 'grab' }\"\n @mousedown=\"handleMouseDown\"\n @mousemove=\"handleMouseMove\"\n @mouseup=\"handleMouseUp\"\n @mouseleave=\"handleMouseUp\"\n >\n <div v-if=\"!loaded && !error\" class=\"vfp-flex vfp-items-center vfp-justify-center\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <div v-if=\"error\" class=\"vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error }}</p>\n </div>\n\n <img\n ref=\"imgRef\"\n :src=\"url\"\n alt=\"Preview\"\n :class=\"['vfp-max-w-none vfp-select-none', !loaded && 'vfp-hidden']\"\n :style=\"transformStyle\"\n :draggable=\"false\"\n @load=\"handleLoad\"\n @error=\"handleError\"\n @dblclick=\"handleDoubleClick\"\n />\n\n <div\n v-if=\"loaded && !error && naturalSize.width > 0\"\n class=\"vfp-absolute vfp-bottom-2 vfp-right-3 vfp-text-[10px] vfp-text-fg-disabled hover:vfp-text-fg-secondary vfp-transition-colors vfp-pointer-events-auto vfp-select-none vfp-cursor-default\"\n >\n {{ naturalSize.width }} × {{ naturalSize.height }}{{ sizeText }}\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","t","useTranslator","loaded","ref","error","position","isDragging","dragStart","internalZoom","naturalSize","imgRef","containerRef","watch","z","clampPosition","pos","currentZoom","container","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleLoad","img","handleError","handleDoubleClick","handleWheelNative","rect","mouseX","mouseY","delta","prev","newZoom","scale","onMounted","onBeforeUnmount","handleMouseDown","handleMouseMove","handleMouseUp","transformStyle","computed","sizeText","formatFileSize","_createElementBlock","_openBlock","_hoisted_1","_cache","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_hoisted_5"],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { defineComponent as $, computed as G, ref as c, watch as _, nextTick as I, openBlock as a, createElementBlock as d, createElementVNode as i, toDisplayString as H } from "vue";
|
|
2
|
+
import P from "markdown-it";
|
|
3
|
+
import D from "@traptitech/markdown-it-katex";
|
|
4
|
+
import { codeToHtml as E } from "shiki";
|
|
5
|
+
import { u as K, b as O, h as R, _ as N } from "./index-DpAxU2JM.mjs";
|
|
6
|
+
import "katex/dist/katex.min.css";
|
|
7
|
+
const Y = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full"
|
|
10
|
+
}, W = {
|
|
11
|
+
key: 1,
|
|
12
|
+
class: "vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full"
|
|
13
|
+
}, z = { class: "vfp-text-fg-secondary vfp-text-center" }, F = { class: "vfp-text-lg" }, J = {
|
|
14
|
+
key: 2,
|
|
15
|
+
class: "vfp-w-full vfp-h-full vfp-overflow-auto",
|
|
16
|
+
style: { background: "var(--fp-code-bg)" }
|
|
17
|
+
}, Q = {
|
|
18
|
+
key: 0,
|
|
19
|
+
class: "vfp-p-6 vfp-text-fg-primary vfp-font-mono vfp-text-sm vfp-whitespace-pre-wrap vfp-break-words"
|
|
20
|
+
}, U = ["innerHTML"], X = {
|
|
21
|
+
key: 3,
|
|
22
|
+
class: "vfp-w-full vfp-h-full vfp-overflow-auto vfp-p-6 md:vfp-p-10"
|
|
23
|
+
}, Z = { class: "vfp-max-w-full md:vfp-max-w-4xl vfp-mx-auto" }, ee = ["innerHTML"], L = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>', B = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>', te = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>', oe = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>', re = /* @__PURE__ */ $({
|
|
24
|
+
__name: "index",
|
|
25
|
+
props: {
|
|
26
|
+
url: {},
|
|
27
|
+
viewMode: {}
|
|
28
|
+
},
|
|
29
|
+
setup(b) {
|
|
30
|
+
const x = b, { t: p } = K(), j = O(), g = G(() => j.value === "light" ? "github-light" : "github-dark"), y = c(""), u = c(""), v = c(""), f = c(!0), h = c(null), m = c(null), l = new P({
|
|
31
|
+
html: !0,
|
|
32
|
+
linkify: !0,
|
|
33
|
+
typographer: !1,
|
|
34
|
+
breaks: !1
|
|
35
|
+
});
|
|
36
|
+
l.use(D), l.renderer.rules.fence = (o, r) => {
|
|
37
|
+
const e = o[r], n = e.info ? e.info.trim() : "", t = e.content;
|
|
38
|
+
return n ? `<div class="code-block-wrapper">
|
|
39
|
+
<div class="code-block-header">
|
|
40
|
+
<span>${n}</span>
|
|
41
|
+
</div>
|
|
42
|
+
<pre data-shiki-pending="1" data-lang="${n}"><code>${l.utils.escapeHtml(t)}</code></pre>
|
|
43
|
+
</div>` : `<div class="code-block-wrapper">
|
|
44
|
+
<pre class="no-lang-pre"><code>${l.utils.escapeHtml(t)}</code></pre>
|
|
45
|
+
</div>`;
|
|
46
|
+
}, l.renderer.rules.code_block = (o, r) => {
|
|
47
|
+
const e = o[r].content;
|
|
48
|
+
return `<div class="code-block-wrapper">
|
|
49
|
+
<pre class="no-lang-pre"><code>${l.utils.escapeHtml(e)}</code></pre>
|
|
50
|
+
</div>`;
|
|
51
|
+
};
|
|
52
|
+
const q = l.renderer.rules.table_open || ((o, r, e, n, t) => t.renderToken(o, r, e));
|
|
53
|
+
l.renderer.rules.table_open = (o, r, e, n, t) => '<div class="table-wrapper">' + q(o, r, e, n, t);
|
|
54
|
+
const V = l.renderer.rules.table_close || ((o, r, e, n, t) => t.renderToken(o, r, e));
|
|
55
|
+
l.renderer.rules.table_close = (o, r, e, n, t) => V(o, r, e, n, t) + "</div>";
|
|
56
|
+
const C = async () => {
|
|
57
|
+
f.value = !0, h.value = null;
|
|
58
|
+
try {
|
|
59
|
+
const o = await R(x.url);
|
|
60
|
+
y.value = o, u.value = l.render(o), f.value = !1;
|
|
61
|
+
try {
|
|
62
|
+
v.value = await E(o, { lang: "markdown", theme: g.value });
|
|
63
|
+
} catch {
|
|
64
|
+
v.value = "";
|
|
65
|
+
}
|
|
66
|
+
} catch (o) {
|
|
67
|
+
console.error(o), h.value = p.value("markdown.load_failed"), f.value = !1;
|
|
68
|
+
}
|
|
69
|
+
}, M = (o, r) => {
|
|
70
|
+
const e = document.createElement("button");
|
|
71
|
+
return e.type = "button", e.className = r ? "code-copy-btn" : "code-copy-btn code-copy-float", e.title = p.value("markdown.copy_code"), e.innerHTML = r ? L : B, e.addEventListener("click", async (n) => {
|
|
72
|
+
n.stopPropagation();
|
|
73
|
+
try {
|
|
74
|
+
await navigator.clipboard.writeText(o);
|
|
75
|
+
} catch {
|
|
76
|
+
const t = document.createElement("textarea");
|
|
77
|
+
t.value = o, document.body.appendChild(t), t.select(), document.execCommand("copy"), document.body.removeChild(t);
|
|
78
|
+
}
|
|
79
|
+
e.innerHTML = r ? te : oe, e.title = p.value("markdown.copied"), setTimeout(() => {
|
|
80
|
+
e.innerHTML = r ? L : B, e.title = p.value("markdown.copy_code");
|
|
81
|
+
}, 2e3);
|
|
82
|
+
}), e;
|
|
83
|
+
}, A = async () => {
|
|
84
|
+
var e, n;
|
|
85
|
+
if (!m.value) return;
|
|
86
|
+
const o = m.value.querySelectorAll('pre[data-shiki-pending="1"]');
|
|
87
|
+
for (const t of o) {
|
|
88
|
+
const w = t.getAttribute("data-lang") || "text", s = ((e = t.querySelector("code")) == null ? void 0 : e.textContent) || "";
|
|
89
|
+
try {
|
|
90
|
+
const k = await E(s, { lang: w, theme: g.value }), T = document.createElement("div");
|
|
91
|
+
T.innerHTML = k;
|
|
92
|
+
const S = T.firstElementChild;
|
|
93
|
+
S && t.replaceWith(S);
|
|
94
|
+
} catch {
|
|
95
|
+
t.style.backgroundColor = "var(--fp-code-bg)", t.removeAttribute("data-shiki-pending");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const r = m.value.querySelectorAll(".code-block-wrapper");
|
|
99
|
+
for (const t of r) {
|
|
100
|
+
if (t.querySelector(".code-copy-btn")) continue;
|
|
101
|
+
const w = t.querySelector(".code-block-header"), s = t.querySelector("pre"), k = ((n = s == null ? void 0 : s.querySelector("code")) == null ? void 0 : n.textContent) ?? (s == null ? void 0 : s.textContent) ?? "";
|
|
102
|
+
w ? w.appendChild(M(k, !0)) : t.appendChild(M(k, !1));
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
return _(() => x.url, C, { immediate: !0 }), _(u, async () => {
|
|
106
|
+
u.value && (await I(), A());
|
|
107
|
+
}), _(g, () => {
|
|
108
|
+
y.value && C();
|
|
109
|
+
}), (o, r) => f.value ? (a(), d("div", Y, [...r[0] || (r[0] = [
|
|
110
|
+
i("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" }, null, -1)
|
|
111
|
+
])])) : h.value ? (a(), d("div", W, [
|
|
112
|
+
i("div", z, [
|
|
113
|
+
i("p", F, H(h.value), 1)
|
|
114
|
+
])
|
|
115
|
+
])) : b.viewMode === "source" ? (a(), d("div", J, [
|
|
116
|
+
v.value ? (a(), d("div", {
|
|
117
|
+
key: 1,
|
|
118
|
+
class: "shiki-wrapper",
|
|
119
|
+
innerHTML: v.value
|
|
120
|
+
}, null, 8, U)) : (a(), d("pre", Q, H(y.value), 1))
|
|
121
|
+
])) : (a(), d("div", X, [
|
|
122
|
+
i("div", Z, [
|
|
123
|
+
i("div", {
|
|
124
|
+
ref_key: "containerRef",
|
|
125
|
+
ref: m,
|
|
126
|
+
class: "markdown-body",
|
|
127
|
+
innerHTML: u.value
|
|
128
|
+
}, null, 8, ee)
|
|
129
|
+
])
|
|
130
|
+
]));
|
|
131
|
+
}
|
|
132
|
+
}), ie = /* @__PURE__ */ N(re, [["__scopeId", "data-v-b709ebf4"]]);
|
|
133
|
+
export {
|
|
134
|
+
ie as default
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=index-Doy0FvQW.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Doy0FvQW.mjs","sources":["../../src/renderers/Markdown/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch, nextTick } from 'vue';\nimport MarkdownIt from 'markdown-it';\nimport MarkdownItKatex from '@traptitech/markdown-it-katex';\nimport { codeToHtml } from 'shiki';\nimport { fetchTextUtf8 } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useResolvedTheme } from '../../composables/useResolvedTheme';\nimport 'katex/dist/katex.min.css';\n\nconst props = defineProps<{\n url: string;\n viewMode?: 'preview' | 'source';\n}>();\n\nconst { t } = useTranslator();\nconst resolvedTheme = useResolvedTheme();\nconst shikiTheme = computed(() => (resolvedTheme.value === 'light' ? 'github-light' : 'github-dark'));\n\nconst content = ref('');\nconst html = ref('');\nconst highlightedSource = ref('');\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\n\n// 创建 markdown-it 实例(支持 GFM 表格、删除线、任务列表、HTML、数学公式)\nconst md = new MarkdownIt({\n html: true,\n linkify: true,\n typographer: false,\n breaks: false,\n});\nmd.use(MarkdownItKatex);\n\n// 自定义代码块渲染:包裹 code-block-wrapper + header(不含复制按钮,由 JS 动态注入)\nmd.renderer.rules.fence = (tokens, idx) => {\n const token = tokens[idx];\n const info = token.info ? token.info.trim() : '';\n const code = token.content;\n\n if (!info) {\n return `<div class=\"code-block-wrapper\">\n <pre class=\"no-lang-pre\"><code>${md.utils.escapeHtml(code)}</code></pre>\n </div>`;\n }\n\n return `<div class=\"code-block-wrapper\">\n <div class=\"code-block-header\">\n <span>${info}</span>\n </div>\n <pre data-shiki-pending=\"1\" data-lang=\"${info}\"><code>${md.utils.escapeHtml(code)}</code></pre>\n </div>`;\n};\n\n// 4-space 缩进代码块:同样包裹 code-block-wrapper(无头,注入浮动复制按钮)\nmd.renderer.rules.code_block = (tokens, idx) => {\n const code = tokens[idx].content;\n return `<div class=\"code-block-wrapper\">\n <pre class=\"no-lang-pre\"><code>${md.utils.escapeHtml(code)}</code></pre>\n </div>`;\n};\n\n// 表格添加包裹层(用于横向滚动)\nconst defaultTableOpen =\n md.renderer.rules.table_open ||\n ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));\nmd.renderer.rules.table_open = (tokens, idx, options, env, self) => {\n return '<div class=\"table-wrapper\">' + defaultTableOpen(tokens, idx, options, env, self);\n};\nconst defaultTableClose =\n md.renderer.rules.table_close ||\n ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options));\nmd.renderer.rules.table_close = (tokens, idx, options, env, self) => {\n return defaultTableClose(tokens, idx, options, env, self) + '</div>';\n};\n\nconst loadMarkdown = async () => {\n loading.value = true;\n error.value = null;\n try {\n const text = await fetchTextUtf8(props.url);\n content.value = text;\n html.value = md.render(text);\n loading.value = false;\n // 异步生成源码高亮(不阻塞预览渲染)\n try {\n highlightedSource.value = await codeToHtml(text, { lang: 'markdown', theme: shikiTheme.value });\n } catch {\n highlightedSource.value = '';\n }\n } catch (err) {\n console.error(err);\n error.value = t.value('markdown.load_failed');\n loading.value = false;\n }\n};\n\nconst COPY_SVG_SM = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg>`;\nconst COPY_SVG_MD = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg>`;\nconst CHECK_SVG_SM = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"/></svg>`;\nconst CHECK_SVG_MD = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"/></svg>`;\n\nconst createCopyButton = (code: string, inline: boolean): HTMLButtonElement => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = inline ? 'code-copy-btn' : 'code-copy-btn code-copy-float';\n btn.title = t.value('markdown.copy_code');\n btn.innerHTML = inline ? COPY_SVG_SM : COPY_SVG_MD;\n btn.addEventListener('click', async (e) => {\n e.stopPropagation();\n try {\n await navigator.clipboard.writeText(code);\n } catch {\n const ta = document.createElement('textarea');\n ta.value = code;\n document.body.appendChild(ta);\n ta.select();\n document.execCommand('copy');\n document.body.removeChild(ta);\n }\n btn.innerHTML = inline ? CHECK_SVG_SM : CHECK_SVG_MD;\n btn.title = t.value('markdown.copied');\n setTimeout(() => {\n btn.innerHTML = inline ? COPY_SVG_SM : COPY_SVG_MD;\n btn.title = t.value('markdown.copy_code');\n }, 2000);\n });\n return btn;\n};\n\nconst highlightAndInjectCopyButtons = async () => {\n if (!containerRef.value) return;\n\n // 1. shiki 高亮有语言的代码块\n const pending = containerRef.value.querySelectorAll<HTMLPreElement>('pre[data-shiki-pending=\"1\"]');\n for (const pre of pending) {\n const lang = pre.getAttribute('data-lang') || 'text';\n const code = pre.querySelector('code')?.textContent || '';\n try {\n const highlighted = await codeToHtml(code, { lang, theme: shikiTheme.value });\n const tmp = document.createElement('div');\n tmp.innerHTML = highlighted;\n const newPre = tmp.firstElementChild as HTMLElement;\n if (newPre) pre.replaceWith(newPre);\n } catch {\n pre.style.backgroundColor = 'var(--fp-code-bg)';\n pre.removeAttribute('data-shiki-pending');\n }\n }\n\n // 2. 为所有代码块注入复制按钮(避免重复注入)\n const wrappers = containerRef.value.querySelectorAll<HTMLElement>('.code-block-wrapper');\n for (const wrapper of wrappers) {\n if (wrapper.querySelector('.code-copy-btn')) continue;\n const header = wrapper.querySelector('.code-block-header');\n const pre = wrapper.querySelector('pre');\n const code = pre?.querySelector('code')?.textContent ?? pre?.textContent ?? '';\n if (header) {\n // 有语言:按钮放入 header 右侧\n header.appendChild(createCopyButton(code, true));\n } else {\n // 无语言:浮动按钮\n wrapper.appendChild(createCopyButton(code, false));\n }\n }\n};\n\nwatch(() => props.url, loadMarkdown, { immediate: true });\n\nwatch(html, async () => {\n if (!html.value) return;\n await nextTick();\n highlightAndInjectCopyButtons();\n});\n\n// 主题切换时重新加载(让 shiki 用新主题重新高亮)\nwatch(shikiTheme, () => {\n if (content.value) loadMarkdown();\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\" 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 <!-- 源码视图 -->\n <div v-else-if=\"viewMode === 'source'\" class=\"vfp-w-full vfp-h-full vfp-overflow-auto\" style=\"background: var(--fp-code-bg);\">\n <pre\n v-if=\"!highlightedSource\"\n class=\"vfp-p-6 vfp-text-fg-primary vfp-font-mono vfp-text-sm vfp-whitespace-pre-wrap vfp-break-words\"\n >{{ content }}</pre\n >\n <div v-else class=\"shiki-wrapper\" v-html=\"highlightedSource\" />\n </div>\n\n <!-- 预览视图 -->\n <div v-else class=\"vfp-w-full vfp-h-full vfp-overflow-auto vfp-p-6 md:vfp-p-10\">\n <div class=\"vfp-max-w-full md:vfp-max-w-4xl vfp-mx-auto\">\n <div ref=\"containerRef\" class=\"markdown-body\" v-html=\"html\" />\n </div>\n </div>\n</template>\n\n<style scoped>\n.markdown-body :deep(.table-wrapper) {\n overflow-x: auto;\n margin: 1rem 0;\n border-radius: 0.375rem;\n border: 1px solid var(--fp-surface-3);\n}\n.markdown-body :deep(.table-wrapper > table) {\n margin: 0;\n border: 0;\n border-radius: 0;\n}\n/* shiki 生成的 pre:修正在 code-block-wrapper 内的边距与圆角 */\n.markdown-body :deep(.code-block-wrapper pre.shiki) {\n margin: 0;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-top: 0;\n}\n/* 无语言代码块:pre 不裁剪,让浮动按钮可见 */\n.markdown-body :deep(.code-block-wrapper) {\n overflow: visible;\n}\n.markdown-body :deep(.code-block-wrapper pre) {\n overflow: visible;\n}\n/* 无语言代码块纯文本样式 */\n.markdown-body :deep(.no-lang-pre) {\n margin: 0;\n padding: 1rem;\n background: var(--fp-code-bg);\n border-radius: 0.375rem;\n border: 1px solid var(--fp-line);\n}\n.markdown-body :deep(.no-lang-pre code) {\n display: block;\n font-size: 0.8125rem;\n line-height: 1.5;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n color: var(--fp-fg-primary);\n white-space: pre;\n overflow-x: auto;\n}\n/* shiki 生成的 code:补充 padding,使代码有适当内边距 */\n.markdown-body :deep(pre.shiki code) {\n display: block;\n padding: 1rem;\n font-size: 0.8125rem;\n line-height: 1.5;\n overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n}\n/* KaTeX 公式颜色适配暗色背景 */\n.markdown-body :deep(.katex) {\n color: var(--fp-fg-primary);\n}\n.markdown-body :deep(.katex-display) {\n margin: 1.25rem 0;\n overflow-x: auto;\n overflow-y: hidden;\n}\n/* 源码视图 shiki */\n.shiki-wrapper :deep(pre) {\n margin: 0;\n padding: 1.5rem;\n background: transparent !important;\n font-size: 0.875rem;\n overflow-x: auto;\n}\n.shiki-wrapper :deep(code) {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n}\n</style>\n"],"names":["COPY_SVG_SM","COPY_SVG_MD","CHECK_SVG_SM","CHECK_SVG_MD","props","__props","t","useTranslator","resolvedTheme","useResolvedTheme","shikiTheme","computed","content","ref","html","highlightedSource","loading","error","containerRef","md","MarkdownIt","MarkdownItKatex","tokens","idx","token","info","code","defaultTableOpen","options","_env","self","env","defaultTableClose","loadMarkdown","text","fetchTextUtf8","codeToHtml","err","createCopyButton","inline","btn","e","ta","highlightAndInjectCopyButtons","pending","pre","lang","_a","highlighted","tmp","newPre","wrappers","wrapper","header","_b","watch","nextTick","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_toDisplayString","_hoisted_5","_hoisted_6","_hoisted_8","_hoisted_9"],"mappings":"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"}
|