@eternalheart/vue-file-preview 1.3.7 → 1.3.9
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 +13 -1
- package/lib/FilePreviewContent.vue.d.ts.map +1 -1
- package/lib/FilePreviewEmbed.vue.d.ts +13 -1
- package/lib/FilePreviewEmbed.vue.d.ts.map +1 -1
- package/lib/FilePreviewModal.vue.d.ts +13 -1
- package/lib/FilePreviewModal.vue.d.ts.map +1 -1
- package/lib/chunks/{index-CcJ5NmY1.mjs → index-1khRVlUa.mjs} +25 -25
- package/lib/chunks/index-1khRVlUa.mjs.map +1 -0
- package/lib/chunks/{index-C8ZF84WX.mjs → index-B9Xz5bUB.mjs} +2 -2
- package/lib/chunks/{index-C8ZF84WX.mjs.map → index-B9Xz5bUB.mjs.map} +1 -1
- package/lib/chunks/{index-DpAxU2JM.mjs → index-BA-bP8P6.mjs} +789 -649
- package/lib/chunks/index-BA-bP8P6.mjs.map +1 -0
- package/lib/chunks/{index-DTLDLOGO.mjs → index-BP_8GjEV.mjs} +28 -28
- package/lib/chunks/index-BP_8GjEV.mjs.map +1 -0
- package/lib/chunks/{index-Doy0FvQW.mjs → index-BVdw1Cc8.mjs} +32 -32
- package/lib/chunks/index-BVdw1Cc8.mjs.map +1 -0
- package/lib/chunks/{index-yMb1JUC4.mjs → index-BqSPk90T.mjs} +30 -30
- package/lib/chunks/index-BqSPk90T.mjs.map +1 -0
- package/lib/chunks/index-BrCSk7CK.mjs +116 -0
- package/lib/chunks/index-BrCSk7CK.mjs.map +1 -0
- package/lib/chunks/{index-BI0dSTSM.mjs → index-Bu8GH1aq.mjs} +27 -27
- package/lib/chunks/index-Bu8GH1aq.mjs.map +1 -0
- package/lib/chunks/{index-ClxBxmPR.mjs → index-CYvPRxHK.mjs} +53 -53
- package/lib/chunks/index-CYvPRxHK.mjs.map +1 -0
- package/lib/chunks/{index-CbKSdAt_.mjs → index-Cfm88ItA.mjs} +2 -2
- package/lib/chunks/{index-CbKSdAt_.mjs.map → index-Cfm88ItA.mjs.map} +1 -1
- package/lib/chunks/{index-kBPb_OKm.mjs → index-CxKVonec.mjs} +52 -52
- package/lib/chunks/index-CxKVonec.mjs.map +1 -0
- package/lib/chunks/index-D6RuC1es.mjs +111 -0
- package/lib/chunks/index-D6RuC1es.mjs.map +1 -0
- package/lib/chunks/{index-CB0zCxJb.mjs → index-DEyjEp-i.mjs} +106 -106
- package/lib/chunks/index-DEyjEp-i.mjs.map +1 -0
- package/lib/chunks/{index-DsznjwsH.mjs → index-DFEd3JaR.mjs} +35 -35
- package/lib/chunks/index-DFEd3JaR.mjs.map +1 -0
- package/lib/chunks/index-DjvWRo53.mjs +281 -0
- package/lib/chunks/index-DjvWRo53.mjs.map +1 -0
- package/lib/chunks/{index-DUUsS_T4.mjs → index-H-QlBdbJ.mjs} +2 -2
- package/lib/chunks/{index-DUUsS_T4.mjs.map → index-H-QlBdbJ.mjs.map} +1 -1
- package/lib/chunks/{index-S_kGRh6H.mjs → index-_llF_WTY.mjs} +46 -46
- package/lib/chunks/index-_llF_WTY.mjs.map +1 -0
- package/lib/chunks/{index-D9lByoJw.mjs → index-oQRFKvtz.mjs} +13 -13
- package/lib/chunks/{index-D9lByoJw.mjs.map → index-oQRFKvtz.mjs.map} +1 -1
- package/lib/composables/useRequest.d.ts +24 -0
- package/lib/composables/useRequest.d.ts.map +1 -0
- package/lib/index.cjs +23 -23
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.mjs +6 -6
- package/lib/renderers/Csv/index.vue.d.ts.map +1 -1
- package/lib/renderers/Docx/index.vue.d.ts.map +1 -1
- package/lib/renderers/Epub/index.vue.d.ts.map +1 -1
- package/lib/renderers/Json/index.vue.d.ts.map +1 -1
- package/lib/renderers/Markdown/index.vue.d.ts.map +1 -1
- package/lib/renderers/Mobi/index.vue.d.ts.map +1 -1
- package/lib/renderers/Msg/index.vue.d.ts.map +1 -1
- package/lib/renderers/Pptx/index.vue.d.ts.map +1 -1
- package/lib/renderers/Subtitle/index.vue.d.ts.map +1 -1
- package/lib/renderers/Text/index.vue.d.ts.map +1 -1
- package/lib/renderers/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/types.d.ts +1 -1
- package/lib/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/lib/chunks/index-BI0dSTSM.mjs.map +0 -1
- package/lib/chunks/index-BW61ZRev.mjs +0 -111
- package/lib/chunks/index-BW61ZRev.mjs.map +0 -1
- package/lib/chunks/index-CB0zCxJb.mjs.map +0 -1
- package/lib/chunks/index-CBCQs1ZC.mjs +0 -116
- package/lib/chunks/index-CBCQs1ZC.mjs.map +0 -1
- package/lib/chunks/index-CcJ5NmY1.mjs.map +0 -1
- package/lib/chunks/index-ClxBxmPR.mjs.map +0 -1
- package/lib/chunks/index-DTLDLOGO.mjs.map +0 -1
- package/lib/chunks/index-Doy0FvQW.mjs.map +0 -1
- package/lib/chunks/index-DpAxU2JM.mjs.map +0 -1
- package/lib/chunks/index-DsznjwsH.mjs.map +0 -1
- package/lib/chunks/index-SAfVQyNJ.mjs +0 -281
- package/lib/chunks/index-SAfVQyNJ.mjs.map +0 -1
- package/lib/chunks/index-S_kGRh6H.mjs.map +0 -1
- package/lib/chunks/index-kBPb_OKm.mjs.map +0 -1
- package/lib/chunks/index-yMb1JUC4.mjs.map +0 -1
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import
|
|
3
|
-
import { X as
|
|
4
|
-
import { u as
|
|
5
|
-
const
|
|
1
|
+
import { defineComponent as ee, ref as h, onMounted as te, watch as oe, onBeforeUnmount as re, openBlock as c, createElementBlock as p, createElementVNode as v, toDisplayString as E, createCommentVNode as y, normalizeStyle as b, unref as X, createVNode as ae, Fragment as $, renderList as D } from "vue";
|
|
2
|
+
import ne from "@likecoin/epub-ts";
|
|
3
|
+
import { X as le } from "lucide-vue-next";
|
|
4
|
+
import { u as se, a as ie } from "./index-BA-bP8P6.mjs";
|
|
5
|
+
const fe = {
|
|
6
6
|
class: "vfp-relative vfp-w-full vfp-h-full vfp-flex vfp-justify-center vfp-overflow-hidden",
|
|
7
7
|
style: { background: "#f5f5f0" }
|
|
8
|
-
},
|
|
8
|
+
}, ue = {
|
|
9
9
|
key: 0,
|
|
10
10
|
class: "vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-text-fg-secondary vfp-text-center"
|
|
11
|
-
},
|
|
11
|
+
}, ce = { class: "vfp-text-lg" }, pe = {
|
|
12
12
|
key: 1,
|
|
13
13
|
class: "vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-z-10"
|
|
14
|
-
},
|
|
14
|
+
}, ve = {
|
|
15
15
|
class: "vfp-flex vfp-items-center vfp-justify-between vfp-flex-shrink-0",
|
|
16
16
|
style: { padding: "12px 16px", "border-bottom": "1px solid rgba(255,255,255,0.1)" }
|
|
17
|
-
},
|
|
17
|
+
}, de = { style: { color: "white", "font-weight": "500", "font-size": "14px" } }, he = {
|
|
18
18
|
class: "vfp-flex-1 vfp-overflow-y-auto",
|
|
19
19
|
style: { padding: "8px 4px" }
|
|
20
|
-
},
|
|
20
|
+
}, ge = ["title", "onClick", "onMouseenter", "onMouseleave"], me = ["title", "onClick", "onMouseenter", "onMouseleave"], be = 794, _e = /* @__PURE__ */ ee({
|
|
21
21
|
__name: "index",
|
|
22
22
|
props: {
|
|
23
23
|
url: {}
|
|
24
24
|
},
|
|
25
25
|
emits: ["chapterChange", "fullWidthChange"],
|
|
26
26
|
setup(I, { expose: O, emit: U }) {
|
|
27
|
-
const w = I, { t:
|
|
27
|
+
const w = I, { t: F } = se(), G = ie(), k = U, l = h(null), _ = h(!0), g = h(null), C = h(!1), x = h([]), f = h(!1), L = h("");
|
|
28
28
|
let s = null, e = null, m = 0, S = null;
|
|
29
|
-
const
|
|
29
|
+
const R = () => {
|
|
30
30
|
var o;
|
|
31
31
|
try {
|
|
32
32
|
(o = e == null ? void 0 : e.destroy) == null || o.call(e);
|
|
@@ -37,26 +37,26 @@ const se = {
|
|
|
37
37
|
} catch {
|
|
38
38
|
}
|
|
39
39
|
e = null, s = null, m = 0, S = null;
|
|
40
|
-
}, G = () => {
|
|
41
|
-
e == null || e.prev();
|
|
42
40
|
}, J = () => {
|
|
43
|
-
e == null || e.
|
|
41
|
+
e == null || e.prev();
|
|
44
42
|
}, K = () => {
|
|
43
|
+
e == null || e.next();
|
|
44
|
+
}, Q = () => {
|
|
45
45
|
C.value = !C.value, k("fullWidthChange", C.value), setTimeout(() => {
|
|
46
46
|
l.value && e && (e.resize(l.value.offsetWidth, l.value.offsetHeight), S && e.display(S), P());
|
|
47
47
|
}, 350);
|
|
48
|
-
},
|
|
48
|
+
}, Y = () => {
|
|
49
49
|
f.value = !f.value;
|
|
50
50
|
}, B = (o) => {
|
|
51
51
|
L.value = o, e == null || e.display(o), f.value = !1;
|
|
52
|
-
},
|
|
53
|
-
O({ prevPage:
|
|
52
|
+
}, u = (o) => o === L.value;
|
|
53
|
+
O({ prevPage: J, nextPage: K, toggleFullWidth: Q, toggleToc: Y });
|
|
54
54
|
const N = async () => {
|
|
55
55
|
if (l.value) {
|
|
56
|
-
_.value = !0, g.value = null, x.value = [], f.value = !1, l.value.innerHTML = "",
|
|
56
|
+
_.value = !0, g.value = null, x.value = [], f.value = !1, l.value.innerHTML = "", R();
|
|
57
57
|
try {
|
|
58
58
|
let o = w.url;
|
|
59
|
-
w.url.startsWith("blob:") && (o = await (await
|
|
59
|
+
w.url.startsWith("blob:") && (o = await (await G.value(w.url)).arrayBuffer()), s = ne(o), e = s.renderTo(l.value, {
|
|
60
60
|
manager: "continuous",
|
|
61
61
|
flow: "scrolled",
|
|
62
62
|
width: "100%",
|
|
@@ -93,8 +93,8 @@ const se = {
|
|
|
93
93
|
var W, d, q;
|
|
94
94
|
const n = t;
|
|
95
95
|
if ((W = n == null ? void 0 : n.start) != null && W.cfi && (S = n.start.cfi), (d = n == null ? void 0 : n.start) != null && d.href) {
|
|
96
|
-
const A = n.start.href, H = [], V = (
|
|
97
|
-
for (const z of
|
|
96
|
+
const A = n.start.href, H = [], V = (Z) => {
|
|
97
|
+
for (const z of Z) {
|
|
98
98
|
const T = z.href.split("#")[0];
|
|
99
99
|
T && (A === T || A.endsWith("/" + T) || A.endsWith(T)) && H.push(z.href), z.subitems && V(z.subitems);
|
|
100
100
|
}
|
|
@@ -105,7 +105,7 @@ const se = {
|
|
|
105
105
|
typeof r == "number" && m > 0 && k("chapterChange", r + 1, m);
|
|
106
106
|
});
|
|
107
107
|
} catch (o) {
|
|
108
|
-
console.error("EPUB 加载错误:", o), g.value =
|
|
108
|
+
console.error("EPUB 加载错误:", o), g.value = F.value("epub.load_failed"), _.value = !1;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
}, j = () => {
|
|
@@ -130,18 +130,18 @@ const se = {
|
|
|
130
130
|
};
|
|
131
131
|
requestAnimationFrame(o);
|
|
132
132
|
};
|
|
133
|
-
return
|
|
133
|
+
return te(() => {
|
|
134
134
|
N(), window.addEventListener("resize", j), P();
|
|
135
|
-
}),
|
|
136
|
-
window.removeEventListener("resize", j), i == null || i.removeEventListener("scroll", M),
|
|
137
|
-
}), (o, a) => (
|
|
138
|
-
g.value ? (
|
|
139
|
-
v("p",
|
|
135
|
+
}), oe(() => w.url, () => N()), re(() => {
|
|
136
|
+
window.removeEventListener("resize", j), i == null || i.removeEventListener("scroll", M), R();
|
|
137
|
+
}), (o, a) => (c(), p("div", fe, [
|
|
138
|
+
g.value ? (c(), p("div", ue, [
|
|
139
|
+
v("p", ce, E(g.value), 1)
|
|
140
140
|
])) : y("", !0),
|
|
141
|
-
_.value && !g.value ? (
|
|
141
|
+
_.value && !g.value ? (c(), p("div", pe, [...a[2] || (a[2] = [
|
|
142
142
|
v("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)
|
|
143
143
|
])])) : y("", !0),
|
|
144
|
-
x.value.length > 0 ? (
|
|
144
|
+
x.value.length > 0 ? (c(), p("div", {
|
|
145
145
|
key: 2,
|
|
146
146
|
class: "vfp-absolute vfp-inset-0 vfp-z-20 vfp-flex",
|
|
147
147
|
style: b({ opacity: f.value ? 1 : 0, pointerEvents: f.value ? "auto" : "none", transition: "opacity 0.3s" })
|
|
@@ -158,17 +158,17 @@ const se = {
|
|
|
158
158
|
transition: "transform 0.3s ease"
|
|
159
159
|
})
|
|
160
160
|
}, [
|
|
161
|
-
v("div",
|
|
162
|
-
v("span",
|
|
161
|
+
v("div", ve, [
|
|
162
|
+
v("span", de, E(X(F)("toolbar.toc")), 1),
|
|
163
163
|
v("button", {
|
|
164
164
|
style: { color: "rgba(255,255,255,0.6)", background: "none", border: "none", cursor: "pointer", padding: "4px" },
|
|
165
165
|
onClick: a[0] || (a[0] = (t) => f.value = !1)
|
|
166
166
|
}, [
|
|
167
|
-
|
|
167
|
+
ae(X(le), { style: { width: "16px", height: "16px" } })
|
|
168
168
|
])
|
|
169
169
|
]),
|
|
170
|
-
v("div",
|
|
171
|
-
(
|
|
170
|
+
v("div", he, [
|
|
171
|
+
(c(!0), p($, null, D(x.value, (t, n) => (c(), p($, {
|
|
172
172
|
key: `${t.href}-${n}`
|
|
173
173
|
}, [
|
|
174
174
|
v("button", {
|
|
@@ -177,9 +177,9 @@ const se = {
|
|
|
177
177
|
textAlign: "left",
|
|
178
178
|
padding: "8px 12px",
|
|
179
179
|
fontSize: "14px",
|
|
180
|
-
color:
|
|
181
|
-
fontWeight:
|
|
182
|
-
background:
|
|
180
|
+
color: u(t.href) ? "white" : "rgba(255,255,255,0.7)",
|
|
181
|
+
fontWeight: u(t.href) ? "500" : "400",
|
|
182
|
+
background: u(t.href) ? "rgba(255,255,255,0.15)" : "none",
|
|
183
183
|
border: "none",
|
|
184
184
|
cursor: "pointer",
|
|
185
185
|
borderRadius: "4px",
|
|
@@ -191,19 +191,19 @@ const se = {
|
|
|
191
191
|
}),
|
|
192
192
|
title: t.label,
|
|
193
193
|
onClick: (r) => B(t.href),
|
|
194
|
-
onMouseenter: (r) => !
|
|
195
|
-
onMouseleave: (r) => !
|
|
196
|
-
}, E(t.label.trim()), 45,
|
|
197
|
-
t.subitems && t.subitems.length > 0 ? (
|
|
194
|
+
onMouseenter: (r) => !u(t.href) && (r.target.style.background = "rgba(255,255,255,0.1)"),
|
|
195
|
+
onMouseleave: (r) => !u(t.href) && (r.target.style.background = "none")
|
|
196
|
+
}, E(t.label.trim()), 45, ge),
|
|
197
|
+
t.subitems && t.subitems.length > 0 ? (c(!0), p($, { key: 0 }, D(t.subitems, (r, W) => (c(), p("button", {
|
|
198
198
|
key: `${r.href}-${W}`,
|
|
199
199
|
style: b({
|
|
200
200
|
width: "100%",
|
|
201
201
|
textAlign: "left",
|
|
202
202
|
padding: "8px 12px 8px 28px",
|
|
203
203
|
fontSize: "13px",
|
|
204
|
-
color:
|
|
205
|
-
fontWeight:
|
|
206
|
-
background:
|
|
204
|
+
color: u(r.href) ? "white" : "rgba(255,255,255,0.6)",
|
|
205
|
+
fontWeight: u(r.href) ? "500" : "400",
|
|
206
|
+
background: u(r.href) ? "rgba(255,255,255,0.15)" : "none",
|
|
207
207
|
border: "none",
|
|
208
208
|
cursor: "pointer",
|
|
209
209
|
borderRadius: "4px",
|
|
@@ -215,9 +215,9 @@ const se = {
|
|
|
215
215
|
}),
|
|
216
216
|
title: r.label,
|
|
217
217
|
onClick: (d) => B(r.href),
|
|
218
|
-
onMouseenter: (d) => !
|
|
219
|
-
onMouseleave: (d) => !
|
|
220
|
-
}, E(r.label.trim()), 45,
|
|
218
|
+
onMouseenter: (d) => !u(r.href) && (d.target.style.background = "rgba(255,255,255,0.1)"),
|
|
219
|
+
onMouseleave: (d) => !u(r.href) && (d.target.style.background = "none")
|
|
220
|
+
}, E(r.label.trim()), 45, me))), 128)) : y("", !0)
|
|
221
221
|
], 64))), 128))
|
|
222
222
|
])
|
|
223
223
|
], 4),
|
|
@@ -227,13 +227,13 @@ const se = {
|
|
|
227
227
|
onClick: a[1] || (a[1] = (t) => f.value = !1)
|
|
228
228
|
}, null, 4)
|
|
229
229
|
], 4)) : y("", !0),
|
|
230
|
-
g.value ? y("", !0) : (
|
|
230
|
+
g.value ? y("", !0) : (c(), p("div", {
|
|
231
231
|
key: 3,
|
|
232
232
|
ref_key: "viewerRef",
|
|
233
233
|
ref: l,
|
|
234
234
|
class: "vfp-h-full vfp-bg-surface-toolbar",
|
|
235
235
|
style: b({
|
|
236
|
-
width: C.value ? "100%" : `${
|
|
236
|
+
width: C.value ? "100%" : `${be}px`,
|
|
237
237
|
maxWidth: "100%",
|
|
238
238
|
transition: "width 0.3s ease",
|
|
239
239
|
boxShadow: "0 1px 3px rgba(0,0,0,0.12)",
|
|
@@ -244,6 +244,6 @@ const se = {
|
|
|
244
244
|
}
|
|
245
245
|
});
|
|
246
246
|
export {
|
|
247
|
-
|
|
247
|
+
_e as default
|
|
248
248
|
};
|
|
249
|
-
//# sourceMappingURL=index-
|
|
249
|
+
//# sourceMappingURL=index-CYvPRxHK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CYvPRxHK.mjs","sources":["../../src/renderers/Epub/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount } from 'vue';\nimport ePub from '@likecoin/epub-ts';\nimport { X } from 'lucide-vue-next';\nimport { useTranslator } from '../../composables/useTranslator';\nimport { useFetcher } from '../../composables/useRequest';\n\ninterface TocItem {\n label: string;\n href: string;\n subitems?: TocItem[];\n}\n\ninterface RenditionLike {\n display: (target?: string) => Promise<unknown>;\n next: () => Promise<unknown>;\n prev: () => Promise<unknown>;\n on: (event: string, cb: (...args: unknown[]) => void) => void;\n resize: (width: number, height: number) => void;\n currentLocation: () => unknown;\n destroy?: () => void;\n themes: {\n register: (name: string, styles: Record<string, unknown>) => void;\n select: (name: string) => void;\n };\n}\n\ninterface BookLike {\n ready: Promise<unknown>;\n loaded: { navigation: Promise<unknown> };\n locations: {\n generate: (chars: number) => Promise<string[]>;\n length: () => number;\n locationFromCfi: (cfi: string) => number;\n };\n renderTo: (el: HTMLElement, opts: Record<string, unknown>) => RenditionLike;\n destroy: () => void;\n}\n\nconst A4_WIDTH = 794;\n\nconst props = defineProps<{ url: string }>();\n\nconst { t } = useTranslator();\nconst fetcher = useFetcher();\n\nconst emit = defineEmits<{\n (e: 'chapterChange', current: number, total: number): void;\n (e: 'fullWidthChange', isFullWidth: boolean): void;\n}>();\n\nconst viewerRef = ref<HTMLDivElement | null>(null);\nconst loading = ref(true);\nconst error = ref<string | null>(null);\nconst isFullWidth = ref(false);\nconst toc = ref<TocItem[]>([]);\nconst showToc = ref(false);\nconst activeTocHref = ref('');\n\nlet book: BookLike | null = null;\nlet rendition: RenditionLike | null = null;\nlet totalLocations = 0;\nlet lastCfi: string | null = null;\n\nconst cleanup = () => {\n try { rendition?.destroy?.(); } catch { /* ignore */ }\n try { book?.destroy(); } catch { /* ignore */ }\n rendition = null;\n book = null;\n totalLocations = 0;\n lastCfi = null;\n};\n\nconst prevPage = () => { rendition?.prev(); };\nconst nextPage = () => { rendition?.next(); };\nconst toggleFullWidth = () => {\n isFullWidth.value = !isFullWidth.value;\n emit('fullWidthChange', isFullWidth.value);\n setTimeout(() => {\n if (viewerRef.value && rendition) {\n rendition.resize(viewerRef.value.offsetWidth, viewerRef.value.offsetHeight);\n if (lastCfi) {\n rendition.display(lastCfi);\n }\n // resize/display 可能重建 .epub-container,需要重新绑定滚动监听\n reattachScrollListener();\n }\n }, 350);\n};\nconst toggleToc = () => { showToc.value = !showToc.value; };\nconst handleTocClick = (href: string) => {\n activeTocHref.value = href;\n rendition?.display(href);\n showToc.value = false;\n};\n\nconst isActive = (href: string) => {\n return href === activeTocHref.value;\n};\n\ndefineExpose({ prevPage, nextPage, toggleFullWidth, toggleToc });\n\nconst loadEpub = async () => {\n if (!viewerRef.value) return;\n\n loading.value = true;\n error.value = null;\n toc.value = [];\n showToc.value = false;\n viewerRef.value.innerHTML = '';\n cleanup();\n\n try {\n let bookInput: string | ArrayBuffer = props.url;\n if (props.url.startsWith('blob:')) {\n const resp = await fetcher.value(props.url);\n bookInput = await resp.arrayBuffer();\n }\n\n book = ePub(bookInput) as unknown as BookLike;\n\n rendition = book.renderTo(viewerRef.value, {\n manager: 'continuous',\n flow: 'scrolled',\n width: '100%',\n height: '100%',\n });\n\n rendition.themes.register('default', {\n body: {\n background: '#ffffff !important',\n color: '#1a1a1a !important',\n 'font-family': '\"Noto Serif SC\", \"Source Han Serif SC\", Georgia, \"Times New Roman\", serif !important',\n 'font-size': '16px !important',\n 'line-height': '2 !important',\n padding: '40px 60px !important',\n 'max-width': '100% !important',\n 'box-sizing': 'border-box !important',\n 'word-break': 'break-word !important',\n 'overflow-wrap': 'break-word !important',\n },\n p: { 'text-indent': '2em !important', margin: '0.8em 0 !important' },\n h1: { 'text-align': 'center !important', margin: '1.5em 0 1em !important' },\n h2: { margin: '1.2em 0 0.8em !important' },\n h3: { margin: '1em 0 0.6em !important' },\n img: { 'max-width': '100% !important', height: 'auto !important' },\n a: { color: '#2563eb !important', 'text-decoration': 'none !important' },\n });\n rendition.themes.select('default');\n\n await book.ready;\n\n // 异步生成 locations 索引\n book.locations.generate(1024).then(() => {\n if (!book) return;\n totalLocations = book.locations.length();\n const loc = rendition?.currentLocation() as { start?: { location?: number } } | undefined;\n const cur = loc?.start?.location ?? 0;\n emit('chapterChange', cur + 1, totalLocations);\n }).catch(() => { /* ignore */ });\n\n const nav = await book.loaded.navigation as { toc?: TocItem[] };\n if (Array.isArray(nav?.toc)) {\n toc.value = nav.toc;\n }\n\n await rendition.display();\n\n loading.value = false;\n emit('chapterChange', 1, totalLocations || 1);\n\n rendition.on('relocated', (location: unknown) => {\n const loc = location as { start?: { cfi?: string; location?: number; href?: string } };\n if (loc?.start?.cfi) {\n lastCfi = loc.start.cfi;\n }\n if (loc?.start?.href) {\n // 根据 spine href 查找匹配的 TOC 项\n const spineHref = loc.start.href;\n const matches: string[] = [];\n const collect = (items: TocItem[]) => {\n for (const item of items) {\n const base = item.href.split('#')[0];\n if (base && (spineHref === base || spineHref.endsWith('/' + base) || spineHref.endsWith(base))) {\n matches.push(item.href);\n }\n if (item.subitems) collect(item.subitems);\n }\n };\n collect(toc.value);\n if (matches.length === 1) {\n activeTocHref.value = matches[0];\n }\n // 多个匹配(同一文件不同 anchor)时保持当前选中(由点击设置)\n }\n const cur = loc?.start?.location;\n if (typeof cur === 'number' && totalLocations > 0) {\n emit('chapterChange', cur + 1, totalLocations);\n }\n });\n } catch (err) {\n console.error('EPUB 加载错误:', err);\n error.value = t.value('epub.load_failed');\n loading.value = false;\n }\n};\n\nconst onResize = () => {\n if (!viewerRef.value || !rendition) return;\n rendition.resize(viewerRef.value.offsetWidth, viewerRef.value.offsetHeight);\n};\n\n// 监听 epub-container 滚动,接近底部时强制加载后续 section\nlet scrollContainer: Element | null = null;\nconst onContainerScroll = () => {\n if (!scrollContainer) return;\n const el = scrollContainer as HTMLElement;\n if (el.scrollTop + el.clientHeight >= el.scrollHeight - 200) {\n try {\n const mgr = (rendition as unknown as { manager?: { check?: (t?: number, e?: number) => Promise<unknown> } })?.manager;\n // 传递垂直偏移量参数,让 check() 在接近底部时就触发加载下一节\n mgr?.check?.(500, 500);\n } catch { /* ignore */ }\n }\n};\n\nconst reattachScrollListener = () => {\n // 清理旧监听\n if (scrollContainer) {\n scrollContainer.removeEventListener('scroll', onContainerScroll);\n scrollContainer = null;\n }\n const tryAttach = () => {\n scrollContainer = viewerRef.value?.querySelector('.epub-container') ?? null;\n if (scrollContainer) {\n scrollContainer.addEventListener('scroll', onContainerScroll, { passive: true });\n } else {\n requestAnimationFrame(tryAttach);\n }\n };\n requestAnimationFrame(tryAttach);\n};\n\nonMounted(() => {\n loadEpub();\n window.addEventListener('resize', onResize);\n reattachScrollListener();\n});\n\nwatch(() => props.url, () => loadEpub());\n\nonBeforeUnmount(() => {\n window.removeEventListener('resize', onResize);\n scrollContainer?.removeEventListener('scroll', onContainerScroll);\n cleanup();\n});\n</script>\n\n<template>\n <div class=\"vfp-relative vfp-w-full vfp-h-full vfp-flex vfp-justify-center vfp-overflow-hidden\" style=\"background: #f5f5f0\">\n <div v-if=\"error\" class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-text-fg-secondary vfp-text-center\">\n <p class=\"vfp-text-lg\">{{ error }}</p>\n </div>\n\n <div\n v-if=\"loading && !error\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-items-center vfp-justify-center vfp-z-10\"\n >\n <div class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\" />\n </div>\n\n <!-- 目录侧栏 - 滑入动画 -->\n <div\n v-if=\"toc.length > 0\"\n class=\"vfp-absolute vfp-inset-0 vfp-z-20 vfp-flex\"\n :style=\"{ opacity: showToc ? 1 : 0, pointerEvents: showToc ? 'auto' : 'none', transition: 'opacity 0.3s' }\"\n >\n <div\n class=\"vfp-w-72 vfp-h-full vfp-flex vfp-flex-col\"\n :style=\"{\n maxWidth: '80%',\n background: 'rgba(0,0,0,0.9)',\n backdropFilter: 'blur(12px)',\n borderRight: '1px solid rgba(255,255,255,0.1)',\n boxShadow: '0 25px 50px -12px rgba(0,0,0,0.25)',\n transform: showToc ? 'translateX(0)' : 'translateX(-100%)',\n transition: 'transform 0.3s ease',\n }\"\n >\n <div class=\"vfp-flex vfp-items-center vfp-justify-between vfp-flex-shrink-0\" style=\"padding: 12px 16px; border-bottom: 1px solid rgba(255,255,255,0.1)\">\n <span style=\"color: white; font-weight: 500; font-size: 14px\">{{ t('toolbar.toc') }}</span>\n <button style=\"color: rgba(255,255,255,0.6); background: none; border: none; cursor: pointer; padding: 4px\" @click=\"showToc = false\">\n <X style=\"width: 16px; height: 16px\" />\n </button>\n </div>\n <div class=\"vfp-flex-1 vfp-overflow-y-auto\" style=\"padding: 8px 4px\">\n <template v-for=\"(item, i) in toc\" :key=\"`${item.href}-${i}`\">\n <button\n :style=\"{\n width: '100%', textAlign: 'left', padding: '8px 12px', fontSize: '14px',\n color: isActive(item.href) ? 'white' : 'rgba(255,255,255,0.7)',\n fontWeight: isActive(item.href) ? '500' : '400',\n background: isActive(item.href) ? 'rgba(255,255,255,0.15)' : 'none',\n border: 'none', cursor: 'pointer', borderRadius: '4px',\n overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block',\n transition: 'all 0.15s',\n }\"\n :title=\"item.label\"\n @click=\"handleTocClick(item.href)\"\n @mouseenter=\"!isActive(item.href) && (($event.target as HTMLElement).style.background = 'rgba(255,255,255,0.1)')\"\n @mouseleave=\"!isActive(item.href) && (($event.target as HTMLElement).style.background = 'none')\"\n >\n {{ item.label.trim() }}\n </button>\n <template v-if=\"item.subitems && item.subitems.length > 0\">\n <button\n v-for=\"(sub, j) in item.subitems\"\n :key=\"`${sub.href}-${j}`\"\n :style=\"{\n width: '100%', textAlign: 'left', padding: '8px 12px 8px 28px', fontSize: '13px',\n color: isActive(sub.href) ? 'white' : 'rgba(255,255,255,0.6)',\n fontWeight: isActive(sub.href) ? '500' : '400',\n background: isActive(sub.href) ? 'rgba(255,255,255,0.15)' : 'none',\n border: 'none', cursor: 'pointer', borderRadius: '4px',\n overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block',\n transition: 'all 0.15s',\n }\"\n :title=\"sub.label\"\n @click=\"handleTocClick(sub.href)\"\n @mouseenter=\"!isActive(sub.href) && (($event.target as HTMLElement).style.background = 'rgba(255,255,255,0.1)')\"\n @mouseleave=\"!isActive(sub.href) && (($event.target as HTMLElement).style.background = 'none')\"\n >\n {{ sub.label.trim() }}\n </button>\n </template>\n </template>\n </div>\n </div>\n <div\n class=\"vfp-flex-1\"\n :style=\"{ background: showToc ? 'rgba(0,0,0,0.3)' : 'transparent', transition: 'background 0.3s' }\"\n @click=\"showToc = false\"\n />\n </div>\n\n <div\n v-if=\"!error\"\n ref=\"viewerRef\"\n class=\"vfp-h-full vfp-bg-surface-toolbar\"\n :style=\"{\n width: isFullWidth ? '100%' : `${A4_WIDTH}px`,\n maxWidth: '100%',\n transition: 'width 0.3s ease',\n boxShadow: '0 1px 3px rgba(0,0,0,0.12)',\n overflow: 'hidden',\n }\"\n />\n </div>\n</template>\n\n<style>\n.epub-container { overflow-y: auto !important; scrollbar-width: thin; }\n.epub-container::-webkit-scrollbar { width: 8px; }\n.epub-container::-webkit-scrollbar-track { background: transparent; }\n.epub-container::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 4px; }\n.epub-container::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.3); }\n.epub-view > iframe { background: white; }\n</style>\n"],"names":["A4_WIDTH","props","__props","t","useTranslator","fetcher","useFetcher","emit","__emit","viewerRef","ref","loading","error","isFullWidth","toc","showToc","activeTocHref","book","rendition","totalLocations","lastCfi","cleanup","_a","prevPage","nextPage","toggleFullWidth","reattachScrollListener","toggleToc","handleTocClick","href","isActive","__expose","loadEpub","bookInput","ePub","loc","cur","nav","location","_b","spineHref","matches","collect","items","item","base","_c","err","onResize","scrollContainer","onContainerScroll","el","mgr","tryAttach","onMounted","watch","onBeforeUnmount","_openBlock","_createElementBlock","_hoisted_1","_hoisted_2","_createElementVNode","_hoisted_3","_toDisplayString","_hoisted_4","_cache","_normalizeStyle","_hoisted_5","_hoisted_6","_unref","_createVNode","X","_hoisted_7","_Fragment","_renderList","i","$event","_hoisted_8","sub","j","_hoisted_9"],"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 {
|
|
3
|
+
import { h as se, u as le, _ as ie } from "./index-BA-bP8P6.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-
|
|
391
|
+
//# sourceMappingURL=index-Cfm88ItA.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-Cfm88ItA.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":";;;AAOO,SAASA,GAAeC,GAAkB;AAC/C,QAAMC,IAAWC,EAA6B,IAAI,GAC5CC,IAAYD,EAAI,EAAK,GACrBE,IAAYF,EAAI,EAAI,GACpBG,IAAcH,EAAI,CAAC,GACnBI,IAAWJ,EAAI,CAAC,GAChBK,IAASL,EAAI,CAAC,GACdM,IAAUN,EAAI,EAAK,GACnBO,IAASP,EAAI,EAAK,GAClBQ,IAAQR,EAAmB,IAAI;AAErC,MAAIS,IAA+B;AAEnC,QAAMC,IAAa,MAAM;AACvB,UAAMC,IAAQZ,EAAS;AACvB,QAAI,CAACY,EAAO;AAEZ,IAAAT,EAAU,QAAQ,IAClBM,EAAM,QAAQ;AAEd,UAAMI,IAAe,MAAM;AACzB,MAAK,MAAMD,EAAM,WAAW,MAC1BR,EAAY,QAAQQ,EAAM;AAAA,IAE9B,GAEME,IAAmB,MAAM;AAC7B,MAAI,CAAC,MAAMF,EAAM,QAAQ,KAAK,SAASA,EAAM,QAAQ,MACnDP,EAAS,QAAQO,EAAM;AAAA,IAE3B,GAEMG,IAAY,MAAM;AACtB,MAAAZ,EAAU,QAAQ,IAClBW,EAAA;AAAA,IACF,GAEME,IAAY,MAAM;AACtB,MAAAb,EAAU,QAAQ;AAAA,IACpB,GACMc,IAAY,MAAM;AACtB,MAAAd,EAAU,QAAQ,IAClBD,EAAU,QAAQ;AAAA,IACpB,GAEMgB,IAAS,MAAM;AACnB,MAAAhB,EAAU,QAAQ;AAAA,IACpB,GACMiB,IAAU,MAAM;AACpB,MAAAjB,EAAU,QAAQ;AAAA,IACpB,GACMkB,IAAU,MAAM;AACpB,MAAAlB,EAAU,QAAQ;AAAA,IACpB,GAEMmB,IAAU,MAAM;AACpB,MAAAZ,EAAM,QAAQ,UACdN,EAAU,QAAQ;AAAA,IACpB;AAEA,IAAAS,EAAM,iBAAiB,cAAcC,CAAY,GACjDD,EAAM,iBAAiB,kBAAkBE,CAAgB,GACzDF,EAAM,iBAAiB,kBAAkBE,CAAgB,GACzDF,EAAM,iBAAiB,WAAWG,CAAS,GAC3CH,EAAM,iBAAiB,WAAWI,CAAS,GAC3CJ,EAAM,iBAAiB,WAAWK,CAAS,GAC3CL,EAAM,iBAAiB,QAAQM,CAAM,GACrCN,EAAM,iBAAiB,SAASO,CAAO,GACvCP,EAAM,iBAAiB,SAASQ,CAAO,GACvCR,EAAM,iBAAiB,SAASS,CAAO,GAEnCT,EAAM,cAAc,KACtBT,EAAU,QAAQ,IAClBW,EAAA,KACSF,EAAM,cAAc,KAC7BE,EAAA,GAGFJ,IAAU,MAAM;AACd,MAAAE,EAAM,oBAAoB,cAAcC,CAAY,GACpDD,EAAM,oBAAoB,kBAAkBE,CAAgB,GAC5DF,EAAM,oBAAoB,kBAAkBE,CAAgB,GAC5DF,EAAM,oBAAoB,WAAWG,CAAS,GAC9CH,EAAM,oBAAoB,WAAWI,CAAS,GAC9CJ,EAAM,oBAAoB,WAAWK,CAAS,GAC9CL,EAAM,oBAAoB,QAAQM,CAAM,GACxCN,EAAM,oBAAoB,SAASO,CAAO,GAC1CP,EAAM,oBAAoB,SAASQ,CAAO,GAC1CR,EAAM,oBAAoB,SAASS,CAAO;AAAA,IAC5C;AAAA,EACF;AAGA,SAAAC;AAAA,IACEvB;AAAA,IACA,MAAM;AACJ,MAAAW,KAAA,QAAAA,KACAA,IAAU,MAEV,sBAAsB,MAAMC,GAAY;AAAA,IAC1C;AAAA,IACA,EAAE,OAAO,OAAA;AAAA,EAAO,GAIlBW;AAAA,IACEtB;AAAA,IACA,CAACuB,MAAO;AACN,MAAIA,KAAIZ,EAAA;AAAA,IACV;AAAA,IACA,EAAE,OAAO,OAAA;AAAA,EAAO,GAGlBa,EAAY,MAAM;AAChB,IAAAd,KAAA,QAAAA,KACAA,IAAU;AAAA,EACZ,CAAC,GAuDM;AAAA,IACL,UAAAV;AAAA,IACA,WAAAE;AAAA,IACA,WAAAC;AAAA,IACA,QAAAK;AAAA,IACA,aAAAJ;AAAA,IACA,UAAAC;AAAA,IACA,QAAAC;AAAA,IACA,SAAAC;AAAA,IACA,OAAAE;AAAA,IACA,YA/DiB,MAAM;AACvB,YAAMG,IAAQZ,EAAS;AACvB,MAAKY,MACDA,EAAM,SACRA,EAAM,OAAO,MAAM,MAAM;AAAA,MAAC,CAAC,IAE3BA,EAAM,MAAA;AAAA,IAEV;AAAA,IAwDE,MAtDW,CAACa,MAAiB;AAC7B,YAAMb,IAAQZ,EAAS;AACvB,MAAKY,MACLA,EAAM,cAAca,GACpBrB,EAAY,QAAQqB;AAAA,IACtB;AAAA,IAkDE,MAhDW,CAACC,MAAoB;AAChC,YAAMd,IAAQZ,EAAS;AACvB,MAAKY,MACLA,EAAM,cAAc,KAAK;AAAA,QACvB;AAAA,QACA,KAAK,IAAIA,EAAM,cAAcc,GAASd,EAAM,YAAY,KAAQ;AAAA,MAAA;AAAA,IAEpE;AAAA,IA0CE,WAxCgB,CAACe,MAAgB;AACjC,YAAMf,IAAQZ,EAAS;AACvB,UAAI,CAACY,EAAO;AACZ,YAAMgB,IAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGD,CAAG,CAAC;AAC5C,MAAAf,EAAM,SAASgB,GACftB,EAAO,QAAQsB,GACXA,IAAU,MACZhB,EAAM,QAAQ,IACdL,EAAQ,QAAQ;AAAA,IAEpB;AAAA,IA+BE,YA7BiB,MAAM;AACvB,YAAMK,IAAQZ,EAAS;AACvB,MAAKY,MACLA,EAAM,QAAQ,CAACA,EAAM,OACrBL,EAAQ,QAAQK,EAAM;AAAA,IACxB;AAAA,IAyBE,YAvBiB,MAAM;AACvB,YAAMA,IAAQZ,EAAS;AACvB,UAAI,CAACY,EAAO;AACZ,YAAMiB,IAAO,CAACjB,EAAM;AACpB,MAAAA,EAAM,OAAOiB,GACbrB,EAAO,QAAQqB;AAAA,IACjB;AAAA,IAiBE,YACAC;AAAAA,EAAA;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;ACzLA,UAAMC,IAASC,EALDC,GAKc,KAAK,GAE3B,EAAE,GAAAC,EAAA,IAAMC,GAAA,GAER;AAAA,MACJ,UAAAnC;AAAA,MACA,WAAAE;AAAA,MACA,QAAAM;AAAA,MACA,aAAAJ;AAAA,MACA,UAAAC;AAAA,MACA,QAAAC;AAAA,MACA,SAAAC;AAAA,MACA,OAAAE;AAAA,MACA,YAAA2B;AAAA,MACA,MAAAC;AAAA,MACA,MAAAC;AAAA,MACA,WAAAC;AAAA,MACA,YAAAC;AAAA,MACA,YAAAC;AAAA,MACA,YAAAX;AAAA,IAAA,IACEhC,GAAeiC,CAAM,GAEnBW,IAAazC,EAAI,EAAK;AAC5B,QAAI0C,IAAiC;AACrC,UAAMC,IAAY3C,EAA2B,IAAI,GAE3C4C,IAAWC,EAAS,MAAOzC,EAAS,QAAQ,IAAID,EAAY,QAAQC,EAAS,QAAQ,CAAE,GAEvF0C,IAAaD,EAAS,MACtBvC,EAAQ,SAASD,EAAO,UAAU,IAAU0C,IAC5C1C,EAAO,QAAQ,MAAY2C,IACxBC,EACR,GAEKC,IAAqB,CAACC,MAAkB;AAC5C,MAAIR,EAAU,SAAS,CAACA,EAAU,MAAM,SAASQ,EAAE,MAAc,MAC/DV,EAAW,QAAQ;AAAA,IAEvB;AAEA,IAAAW,EAAU,MAAM;AACd,eAAS,iBAAiB,aAAaF,CAAkB;AAAA,IAC3D,CAAC,GAEDG,EAAgB,MAAM;AACpB,eAAS,oBAAoB,aAAaH,CAAkB,GACxDR,MAAoB,QAAM,aAAaA,CAAe;AAAA,IAC5D,CAAC;AAED,UAAMY,IAAoB,MAAM;AAC9B,MAAIZ,MAAoB,QAAM,aAAaA,CAAe,GAC1DD,EAAW,QAAQ;AAAA,IACrB,GAEMc,IAAoB,MAAM;AAC9B,MAAAb,IAAkB,OAAO,WAAW,MAAM;AACxC,QAAAD,EAAW,QAAQ;AAAA,MACrB,GAAG,GAAG;AAAA,IACR;qBAIae,EAAAhD,CAAA,KAAXiD,KAAAC,EAIM,OAJNC,IAIM;AAAA,MAHJC,EAEM,OAFNC,IAEM;AAAA,QADJD,EAAsC,KAAtCE,IAAsCC,EAAZP,EAAAhD,CAAA,CAAK,GAAA,CAAA;AAAA,MAAA;WAInCiD,EAAA,GAAAC,EA8RM,OA9RNM,IA8RM;AAAA,MAzRJJ,EAsHM,OAtHNK,IAsHM;AAAA,QApHJL,EAWE,OAAA;AAAA,UAVA,OAAM;AAAA,UACL,OAAKM,EAAA;AAAA;;;;;qBAAuNV,EAAAvD,CAAA,IAAS,MAAA;AAAA;;;QAYxO2D,EAkDM,OAAA;AAAA,UAjDJ,OAAM;AAAA,UACL,OAAKM,EAAA;AAAA;;;;;;;;;;;;;;;;uBAAixCV,EAAAvD,CAAA;;gCAAyRuD,EAAAvD,CAAA,IAAS,YAAA;AAAA,UAAA;;UAyBzjD2D,EAsBM,OAAA;AAAA,YArBJ,OAAM;AAAA,YACL,OAAO;AAAA;;;;;;;UAOP;YAEDA,EAUE,OAAA;AAAA,cATA,OAAM;AAAA,cACL,OAAO;AAAA;;;;;;;YAOP;;;QAMPA,EAgDM,OAAA;AAAA,UA/CJ,OAAM;AAAA,UACL,OAAKM,EAAA;AAAA;;;;;;uBAA0LV,EAAAvD,CAAA,IAAS,kBAAA;AAAA;;;;;;MAkD7M2D,EAKM,OALNO,IAKM;AAAA,QAJJP,EAEM,OAFNQ,IAEML,EADD/B,EAAA,QAAQ,GAAA,CAAA;AAAA,QAEbqC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAAT,EAAkF,KAAA,EAA/E,OAAM,mEAAgE,SAAK,EAAA;AAAA,MAAA;MAIhFA,EAqJM,OArJNU,IAqJM;AAAA,QAhJJV,EAgCM,OAhCNW,IAgCM;AAAA,UA/BJX,EAwBM,OAxBNY,IAwBM;AAAA,8BAvBJZ,EAEE,OAAA,EADA,OAAM,wEAAA,GAAuE,MAAA,EAAA;AAAA,YAE/EA,EAQE,OAAA;AAAA,cAPA,OAAM;AAAA,cACL,OAAKM,EAAA;AAAA,0BAA4BtB,EAAA,QAAQ,GAAA;AAAA;2BAAkIY,EAAAvD,CAAA,IAAS,kCAAA;AAAA;;;YAOvL2D,EAUE,SAAA;AAAA,cATA,MAAK;AAAA,cACL,KAAI;AAAA,cACH,KAAKJ,EAAApD,CAAA,IAAQ,IAAOoD,EAAApD,CAAA,IAAWoD,EAAArD,CAAA,KAAW;AAAA,cAC3C,MAAK;AAAA,cACJ,OAAOqD,EAAArD,CAAA;AAAA,cACP,UAAUqD,EAAApD,CAAA,KAAQ;AAAA,cAClB,cAAYoD,EAAAvB,CAAA,EAAC,qBAAA;AAAA,cACd,OAAM;AAAA,cACL,SAAKoC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAGlB,MAAMK,EAAApB,CAAA,EAAK,WAAYe,EAAE,OAA4B,KAAK,CAAA;AAAA,YAAA;;UAGvES,EAKM,OALNa,IAKM;AAAA,YAFJb,EAAqF,QAArFc,IAAqFX,EAAjCP,EAAA3B,CAAA,EAAW2B,EAAArD,CAAA,CAAW,CAAA,GAAA,CAAA;AAAA,YAC1EyD,EAA2G,QAA3Ge,IAA2GZ,EAAvDP,OAAQ,IAAOA,EAAA3B,CAAA,EAAW2B,EAAApD,CAAA,CAAQ,IAAA,OAAA,GAAA,CAAA;AAAA,UAAA;;QAK1FwD,EA4GM,OA5GNgB,IA4GM;AAAA,UA1GJhB,EASS,UAAA;AAAA,YARN,OAAKiB,EAAA;AAAA;cAAkJrB,EAAAjD,CAAA,IAAM,uCAAA;AAAA,YAAA;YAI7J,cAAYiD,EAAAjD,CAAA,IAASiD,EAAAvB,CAAA,2BAA2BuB,EAAAvB,CAAA,EAAC,oBAAA;AAAA,YACjD,SAAKoC,EAAA,CAAA,MAAAA,EAAA,CAAA;AAAA,sBAAEb,EAAAhB,CAAA,KAAAgB,EAAAhB,CAAA,EAAA,GAAAsC,CAAA;AAAA,UAAA;YAERC,EAAkCvB,EAAAwB,EAAA,GAAA,EAA1B,OAAM,mBAAiB;AAAA,UAAA;UAIjCpB,EAMS,UAAA;AAAA,YALP,OAAM;AAAA,YACL,cAAYJ,EAAAvB,CAAA,EAAC,wBAAA;AAAA,YACb,gCAAOuB,EAAAnB,CAAA,EAAI,GAAA;AAAA,UAAA;YAEZ0C,EAA8CvB,EAAAyB,EAAA,GAAA,EAApC,OAAM,6BAA2B;AAAA,UAAA;UAI7CrB,EAYS,UAAA;AAAA,YAXP,OAAM;AAAA,YACL,OAAO;AAAA;;;;YAKP,cAAYJ,EAAAvD,CAAA,IAAYuD,EAAAvB,CAAA,wBAAwBuB,EAAAvB,CAAA,EAAC,iBAAA;AAAA,YACjD,SAAKoC,EAAA,CAAA,MAAAA,EAAA,CAAA;AAAA,sBAAEb,EAAArB,CAAA,KAAAqB,EAAArB,CAAA,EAAA,GAAA2C,CAAA;AAAA,UAAA;YAEKtB,EAAAvD,CAAA,UAAbiF,EAAkD1B,EAAA2B,EAAA,GAAA;AAAA;cAA1B,OAAM;AAAA,YAAA,YAC9BD,EAAkD1B,EAAA4B,EAAA,GAAA;AAAA;cAArC,OAAM;AAAA,YAAA;;UAIrBxB,EAMS,UAAA;AAAA,YALP,OAAM;AAAA,YACL,cAAYJ,EAAAvB,CAAA,EAAC,uBAAA;AAAA,YACb,gCAAOuB,EAAAnB,CAAA,EAAI,EAAA;AAAA,UAAA;YAEZ0C,EAAiDvB,EAAA6B,EAAA,GAAA,EAApC,OAAM,6BAA2B;AAAA,UAAA;UAIhDzB,EA4DM,OAAA;AAAA,qBA5DG;AAAA,YAAJ,KAAIjB;AAAA,YAAY,OAAM;AAAA,YAAgB,cAAYW;AAAA,YAAoB,cAAYC;AAAA,UAAA;YACrFK,EASS,UAAA;AAAA,cARN,OAAKiB,EAAA;AAAA;gBAAsJpC,EAAA,QAAU,uCAAA;AAAA,cAAA;cAIrK,cAAYe,EAAAlD,CAAA,IAAUkD,EAAAvB,CAAA,yBAAyBuB,EAAAvB,CAAA,EAAC,iBAAA;AAAA,cAChD,SAAKoC,EAAA,CAAA,MAAAA,EAAA,CAAA;AAAA,wBAAEb,EAAAjB,CAAA,KAAAiB,EAAAjB,CAAA,EAAA,GAAAuC,CAAA;AAAA,YAAA;eAERrB,EAAA,GAAAyB,EAAsDI,EAAtCxC,EAAA,KAAU,GAAA,EAAE,OAAM,mBAAiB;AAAA,YAAA;YAGrDiC,EA+CaQ,GAAA,EA/CD,MAAK,cAAU;AAAA,yBACzB,MA6CM;AAAA,gBA5CE9C,EAAA,cADRiB,EA6CM,OAAA;AAAA;kBA3CJ,OAAM;AAAA,kBACL,OAAO;AAAA;;;;kBAKP,cAAYJ;AAAA,kBACZ,cAAYC;AAAA,gBAAA;kBAEbK,EAiCM,OAjCN4B,IAiCM;AAAA,oBAhCJ5B,EA4BM,OA5BN6B,IA4BM;AAAA,wCAxBJ7B,EAGE,OAAA;AAAA,wBAFA,OAAM;AAAA,wBACN,OAAA,EAAA,OAAA,OAAA,QAAA,OAAA;AAAA,sBAAA;sBAEFA,EAQE,OAAA;AAAA,wBAPA,OAAM;AAAA,wBACL,OAAKM,EAAA;AAAA;0BAA0E,QAAA,IAAAV,EAAAlD,CAAA,QAAckD,EAAAnD,CAAA,KAAM,GAAA;AAAA;;;;sBAOtGuD,EAUE,SAAA;AAAA,wBATA,MAAK;AAAA,wBACL,KAAI;AAAA,wBACJ,KAAI;AAAA,wBACJ,MAAK;AAAA,wBACJ,OAAOJ,EAAAlD,CAAA,IAAO,IAAOkD,EAAAnD,CAAA;AAAA,wBACrB,cAAYmD,EAAAvB,CAAA,EAAC,mBAAA;AAAA,wBACd,OAAM;AAAA,wBACN,OAAA,EAAA,OAAA,QAAA,QAAA,QAAA,WAAA,kBAAA,oBAAA,gBAAA;AAAA,wBACC,SAAKoC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAGlB,MAAMK,EAAAlB,CAAA,EAAU,WAAYa,EAAE,OAA4B,KAAK,CAAA;AAAA,sBAAA;;oBAG5ES,EAEO,QAFP8B,IAEO3B,EADF,KAAK,OAAOP,EAAAlD,CAAA,IAAO,IAAOkD,EAAAnD,CAAA,KAAM,GAAA,CAAA,GAAA,CAAA;AAAA,kBAAA;;;;;;;;MASjDuD,EAAsD,SAAA;AAAA,iBAA3C;AAAA,QAAJ,KAAI7D;AAAA,QAAY,KAAKiC,EAAA;AAAA,QAAK,OAAM;AAAA,MAAA;;;;"}
|