sa2kit 1.4.2 → 1.5.1
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/dist/ConfigService-7MEZXKJ5.js +21 -0
- package/dist/ConfigService-7MEZXKJ5.js.map +1 -0
- package/dist/ConfigService-BV57YYFW.mjs +4 -0
- package/dist/ConfigService-BV57YYFW.mjs.map +1 -0
- package/dist/ConfigService-BxK06xP6.d.mts +262 -0
- package/dist/ConfigService-BxK06xP6.d.ts +262 -0
- package/dist/audioDetection/index.d.mts +449 -0
- package/dist/audioDetection/index.d.ts +449 -0
- package/dist/audioDetection/index.js +1244 -0
- package/dist/audioDetection/index.js.map +1 -0
- package/dist/audioDetection/index.mjs +1227 -0
- package/dist/audioDetection/index.mjs.map +1 -0
- package/dist/chunk-5XUE72Y3.mjs +1001 -0
- package/dist/chunk-5XUE72Y3.mjs.map +1 -0
- package/dist/chunk-DQVPZTVC.js +1009 -0
- package/dist/chunk-DQVPZTVC.js.map +1 -0
- package/dist/chunk-NEPD75MX.mjs +467 -0
- package/dist/chunk-NEPD75MX.mjs.map +1 -0
- package/dist/chunk-OEDY7GI4.js +473 -0
- package/dist/chunk-OEDY7GI4.js.map +1 -0
- package/dist/chunk-TFQF2HDO.mjs +354 -0
- package/dist/chunk-TFQF2HDO.mjs.map +1 -0
- package/dist/chunk-TOC5FSHP.js +358 -0
- package/dist/chunk-TOC5FSHP.js.map +1 -0
- package/dist/imageCrop/index.d.mts +165 -0
- package/dist/imageCrop/index.d.ts +165 -0
- package/dist/imageCrop/index.js +559 -0
- package/dist/imageCrop/index.js.map +1 -0
- package/dist/imageCrop/index.mjs +540 -0
- package/dist/imageCrop/index.mjs.map +1 -0
- package/dist/index.d.mts +139 -0
- package/dist/index.d.ts +139 -0
- package/dist/index.js +670 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +662 -0
- package/dist/index.mjs.map +1 -1
- package/dist/mmd/index.d.mts +113 -2
- package/dist/mmd/index.d.ts +113 -2
- package/dist/mmd/index.js +484 -2
- package/dist/mmd/index.js.map +1 -1
- package/dist/mmd/index.mjs +482 -4
- package/dist/mmd/index.mjs.map +1 -1
- package/dist/testYourself/admin/index.d.mts +58 -0
- package/dist/testYourself/admin/index.d.ts +58 -0
- package/dist/testYourself/admin/index.js +17 -0
- package/dist/testYourself/admin/index.js.map +1 -0
- package/dist/testYourself/admin/index.mjs +4 -0
- package/dist/testYourself/admin/index.mjs.map +1 -0
- package/dist/testYourself/index.d.mts +6 -98
- package/dist/testYourself/index.d.ts +6 -98
- package/dist/testYourself/index.js +90 -334
- package/dist/testYourself/index.js.map +1 -1
- package/dist/testYourself/index.mjs +47 -333
- package/dist/testYourself/index.mjs.map +1 -1
- package/dist/testYourself/server/index.d.mts +1029 -0
- package/dist/testYourself/server/index.d.ts +1029 -0
- package/dist/testYourself/server/index.js +42 -0
- package/dist/testYourself/server/index.js.map +1 -0
- package/dist/testYourself/server/index.mjs +5 -0
- package/dist/testYourself/server/index.mjs.map +1 -0
- package/dist/universalFile/server/index.js +5 -5
- package/dist/universalFile/server/index.mjs +1 -1
- package/package.json +62 -20
package/dist/mmd/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import '../chunk-BJTO5JO5.mjs';
|
|
|
2
2
|
import React6, { forwardRef, useRef, useImperativeHandle, useEffect, useState, useCallback } from 'react';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { OrbitControls, MMDLoader, MMDAnimationHelper } from 'three-stdlib';
|
|
5
|
-
import { SkipBack, Pause, Play, SkipForward, Repeat, Repeat1, Grid3x3, Settings, Minimize, Maximize,
|
|
5
|
+
import { SkipBack, Pause, Play, SkipForward, Repeat, Repeat1, Shuffle, ListMusic, Music, X, Grid3x3, Settings, Minimize, Maximize, Video, Check, User, Image } from 'lucide-react';
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
7
7
|
|
|
8
8
|
// src/mmd/utils/ammo-loader.ts
|
|
@@ -47,6 +47,9 @@ var loadAmmo = (path = "/libs/ammo.wasm.js") => {
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
// src/mmd/components/MMDPlayerBase.tsx
|
|
50
|
+
if (typeof window !== "undefined") {
|
|
51
|
+
THREE.Cache.enabled = true;
|
|
52
|
+
}
|
|
50
53
|
async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
51
54
|
const textures = [];
|
|
52
55
|
let meshCount = 0;
|
|
@@ -2572,6 +2575,61 @@ var LoadingOverlay = ({
|
|
|
2572
2575
|
));
|
|
2573
2576
|
};
|
|
2574
2577
|
LoadingOverlay.displayName = "LoadingOverlay";
|
|
2578
|
+
var SkipConfirmDialog = ({
|
|
2579
|
+
onConfirm,
|
|
2580
|
+
onCancel
|
|
2581
|
+
}) => {
|
|
2582
|
+
const [isMounted, setIsMounted] = React6.useState(false);
|
|
2583
|
+
React6.useEffect(() => {
|
|
2584
|
+
setIsMounted(true);
|
|
2585
|
+
}, []);
|
|
2586
|
+
if (!isMounted) return null;
|
|
2587
|
+
const content = /* @__PURE__ */ React6.createElement(
|
|
2588
|
+
"div",
|
|
2589
|
+
{
|
|
2590
|
+
className: "fixed inset-0 flex items-center justify-center bg-black/40 backdrop-blur-sm pointer-events-auto",
|
|
2591
|
+
style: { zIndex: 999999 }
|
|
2592
|
+
},
|
|
2593
|
+
/* @__PURE__ */ React6.createElement(
|
|
2594
|
+
"div",
|
|
2595
|
+
{
|
|
2596
|
+
className: "p-8 rounded-3xl border border-white/30 shadow-2xl max-w-sm w-full mx-4 overflow-hidden relative",
|
|
2597
|
+
style: {
|
|
2598
|
+
background: `linear-gradient(135deg,
|
|
2599
|
+
rgba(255, 255, 255, 0.2) 0%,
|
|
2600
|
+
rgba(255, 255, 255, 0.1) 100%)`,
|
|
2601
|
+
backdropFilter: "blur(32px) saturate(200%)",
|
|
2602
|
+
WebkitBackdropFilter: "blur(32px) saturate(200%)"
|
|
2603
|
+
}
|
|
2604
|
+
},
|
|
2605
|
+
/* @__PURE__ */ React6.createElement("h3", { className: "text-xl font-bold text-white mb-4 drop-shadow-md" }, "\u52A8\u753B\u5C1A\u672A\u64AD\u653E\u5B8C\u6210"),
|
|
2606
|
+
/* @__PURE__ */ React6.createElement("p", { className: "text-white/90 mb-8 leading-relaxed drop-shadow-sm" }, "\u5F53\u524D\u573A\u666F\u7684\u52A8\u753B\u8FD8\u6CA1\u6709\u5B8C\u6574\u64AD\u653E\u4E00\u904D\uFF0C\u662F\u5426\u76F4\u63A5\u8DF3\u8F6C\u5230\u4E0B\u4E00\u4E2A\u573A\u666F\uFF1F"),
|
|
2607
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex justify-end gap-4" }, /* @__PURE__ */ React6.createElement(
|
|
2608
|
+
"button",
|
|
2609
|
+
{
|
|
2610
|
+
onClick: onCancel,
|
|
2611
|
+
className: "px-6 py-2.5 rounded-2xl text-white/80 font-medium hover:text-white hover:bg-white/10 transition-all border border-white/10 hover:border-white/30"
|
|
2612
|
+
},
|
|
2613
|
+
"\u53D6\u6D88"
|
|
2614
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2615
|
+
"button",
|
|
2616
|
+
{
|
|
2617
|
+
onClick: onConfirm,
|
|
2618
|
+
className: "px-6 py-2.5 rounded-2xl bg-white/20 hover:bg-white/30 text-white font-bold transition-all border border-white/40 shadow-lg hover:scale-105 active:scale-95"
|
|
2619
|
+
},
|
|
2620
|
+
"\u76F4\u63A5\u8DF3\u8F6C"
|
|
2621
|
+
))
|
|
2622
|
+
)
|
|
2623
|
+
);
|
|
2624
|
+
let portalContainer = document.getElementById("dialogue-portal-root");
|
|
2625
|
+
if (!portalContainer) {
|
|
2626
|
+
portalContainer = document.createElement("div");
|
|
2627
|
+
portalContainer.id = "dialogue-portal-root";
|
|
2628
|
+
portalContainer.style.cssText = "position: fixed; inset: 0; pointer-events: none; z-index: 999999;";
|
|
2629
|
+
document.body.appendChild(portalContainer);
|
|
2630
|
+
}
|
|
2631
|
+
return createPortal(content, portalContainer);
|
|
2632
|
+
};
|
|
2575
2633
|
|
|
2576
2634
|
// src/mmd/visual-novel/MMDVisualNovel.tsx
|
|
2577
2635
|
var MMDVisualNovel = forwardRef(
|
|
@@ -2605,11 +2663,14 @@ var MMDVisualNovel = forwardRef(
|
|
|
2605
2663
|
const [showHistory, setShowHistory] = useState(false);
|
|
2606
2664
|
const [history, setHistory] = useState([]);
|
|
2607
2665
|
const [isStarted, setIsStarted] = useState(autoStart);
|
|
2666
|
+
const [isVmdFinished, setIsVmdFinished] = useState(false);
|
|
2667
|
+
const [pendingNodeIndex, setPendingNodeIndex] = useState(null);
|
|
2608
2668
|
const playerRef = useRef(null);
|
|
2609
2669
|
const containerRef = useRef(null);
|
|
2610
2670
|
const autoTimerRef = useRef(null);
|
|
2611
2671
|
const typingCompleteRef = useRef(false);
|
|
2612
2672
|
const isStartedRef = useRef(autoStart);
|
|
2673
|
+
const lastAnimationTimeRef = useRef(0);
|
|
2613
2674
|
const currentNode = nodes[currentNodeIndex];
|
|
2614
2675
|
const currentDialogue = currentNode?.dialogues[currentDialogueIndex] || null;
|
|
2615
2676
|
const addToHistory = useCallback((dialogue, nodeIndex, dialogueIndex) => {
|
|
@@ -2649,15 +2710,24 @@ var MMDVisualNovel = forwardRef(
|
|
|
2649
2710
|
}
|
|
2650
2711
|
}, [currentNode, currentDialogueIndex, currentNodeIndex, nodes.length, loop, addToHistory, onDialogueChange, onScriptComplete]);
|
|
2651
2712
|
const goToNode = useCallback(
|
|
2652
|
-
(nodeIndex) => {
|
|
2713
|
+
(nodeIndex, force = false) => {
|
|
2653
2714
|
if (nodeIndex < 0 || nodeIndex >= nodes.length) return;
|
|
2654
2715
|
if (isTransitioning) return;
|
|
2655
2716
|
const node = nodes[nodeIndex];
|
|
2656
2717
|
if (!node) return;
|
|
2718
|
+
const currentResources = nodes[currentNodeIndex]?.resources;
|
|
2719
|
+
if (!force && currentResources?.motionPath && !isVmdFinished) {
|
|
2720
|
+
console.log("[MMDVisualNovel] VMD not finished, showing confirmation");
|
|
2721
|
+
setPendingNodeIndex(nodeIndex);
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2657
2724
|
console.log(`[MMDVisualNovel] Transitioning to node ${nodeIndex}`);
|
|
2658
2725
|
setIsTransitioning(true);
|
|
2659
2726
|
setIsLoading(true);
|
|
2660
2727
|
setIsAnimationPlaying(false);
|
|
2728
|
+
setIsVmdFinished(false);
|
|
2729
|
+
setPendingNodeIndex(null);
|
|
2730
|
+
lastAnimationTimeRef.current = 0;
|
|
2661
2731
|
setTimeout(() => {
|
|
2662
2732
|
setCurrentNodeIndex(nodeIndex);
|
|
2663
2733
|
setCurrentDialogueIndex(0);
|
|
@@ -2675,7 +2745,7 @@ var MMDVisualNovel = forwardRef(
|
|
|
2675
2745
|
}, 100);
|
|
2676
2746
|
}, 300);
|
|
2677
2747
|
},
|
|
2678
|
-
[nodes, isTransitioning, addToHistory, onNodeChange, onDialogueChange]
|
|
2748
|
+
[nodes, isTransitioning, addToHistory, onNodeChange, onDialogueChange, currentNodeIndex, isVmdFinished]
|
|
2679
2749
|
);
|
|
2680
2750
|
const goToDialogue = useCallback(
|
|
2681
2751
|
(dialogueIndex) => {
|
|
@@ -2815,6 +2885,19 @@ var MMDVisualNovel = forwardRef(
|
|
|
2815
2885
|
console.log("[MMDVisualNovel] MMDPlayerBase onPlay called");
|
|
2816
2886
|
setIsAnimationPlaying(true);
|
|
2817
2887
|
},
|
|
2888
|
+
onTimeUpdate: (time) => {
|
|
2889
|
+
if (time < lastAnimationTimeRef.current && lastAnimationTimeRef.current > 0) {
|
|
2890
|
+
if (!isVmdFinished) {
|
|
2891
|
+
console.log("[MMDVisualNovel] VMD loop detected, marking as finished");
|
|
2892
|
+
setIsVmdFinished(true);
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
lastAnimationTimeRef.current = time;
|
|
2896
|
+
},
|
|
2897
|
+
onEnded: () => {
|
|
2898
|
+
console.log("[MMDVisualNovel] VMD ended, marking as finished");
|
|
2899
|
+
setIsVmdFinished(true);
|
|
2900
|
+
},
|
|
2818
2901
|
onError
|
|
2819
2902
|
}
|
|
2820
2903
|
)
|
|
@@ -2871,6 +2954,19 @@ var MMDVisualNovel = forwardRef(
|
|
|
2871
2954
|
}
|
|
2872
2955
|
) : null;
|
|
2873
2956
|
})(),
|
|
2957
|
+
pendingNodeIndex !== null && /* @__PURE__ */ React6.createElement(
|
|
2958
|
+
SkipConfirmDialog,
|
|
2959
|
+
{
|
|
2960
|
+
onConfirm: () => {
|
|
2961
|
+
if (pendingNodeIndex !== null) {
|
|
2962
|
+
goToNode(pendingNodeIndex, true);
|
|
2963
|
+
}
|
|
2964
|
+
},
|
|
2965
|
+
onCancel: () => {
|
|
2966
|
+
setPendingNodeIndex(null);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
),
|
|
2874
2970
|
showHistory && /* @__PURE__ */ React6.createElement(
|
|
2875
2971
|
HistoryPanel,
|
|
2876
2972
|
{
|
|
@@ -2883,7 +2979,389 @@ var MMDVisualNovel = forwardRef(
|
|
|
2883
2979
|
}
|
|
2884
2980
|
);
|
|
2885
2981
|
MMDVisualNovel.displayName = "MMDVisualNovel";
|
|
2982
|
+
var MusicControls = ({
|
|
2983
|
+
isPlaying,
|
|
2984
|
+
currentTime,
|
|
2985
|
+
duration,
|
|
2986
|
+
loopMode,
|
|
2987
|
+
onPlayPause,
|
|
2988
|
+
onNext,
|
|
2989
|
+
onPrevious,
|
|
2990
|
+
onSeek,
|
|
2991
|
+
onToggleLoop,
|
|
2992
|
+
onTogglePlaylist,
|
|
2993
|
+
className = ""
|
|
2994
|
+
}) => {
|
|
2995
|
+
const formatTime = (seconds) => {
|
|
2996
|
+
const mins = Math.floor(seconds / 60);
|
|
2997
|
+
const secs = Math.floor(seconds % 60);
|
|
2998
|
+
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
2999
|
+
};
|
|
3000
|
+
const progress = duration > 0 ? currentTime / duration * 100 : 0;
|
|
3001
|
+
return /* @__PURE__ */ React6.createElement(
|
|
3002
|
+
"div",
|
|
3003
|
+
{
|
|
3004
|
+
className: `w-full max-w-4xl mx-auto px-6 py-4 bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl shadow-2xl pointer-events-auto transition-all group ${className}`
|
|
3005
|
+
},
|
|
3006
|
+
/* @__PURE__ */ React6.createElement("div", { className: "relative w-full h-1.5 bg-white/20 rounded-full mb-4 cursor-pointer group/progress overflow-hidden" }, /* @__PURE__ */ React6.createElement(
|
|
3007
|
+
"div",
|
|
3008
|
+
{
|
|
3009
|
+
className: "absolute h-full bg-blue-500 rounded-full transition-all duration-300",
|
|
3010
|
+
style: { width: `${progress}%` }
|
|
3011
|
+
}
|
|
3012
|
+
), /* @__PURE__ */ React6.createElement(
|
|
3013
|
+
"input",
|
|
3014
|
+
{
|
|
3015
|
+
type: "range",
|
|
3016
|
+
min: 0,
|
|
3017
|
+
max: duration || 100,
|
|
3018
|
+
value: currentTime,
|
|
3019
|
+
onChange: (e) => onSeek(Number(e.target.value)),
|
|
3020
|
+
className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
3021
|
+
}
|
|
3022
|
+
)),
|
|
3023
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-2 text-xs font-mono text-white/60 w-32" }, /* @__PURE__ */ React6.createElement("span", null, formatTime(currentTime)), /* @__PURE__ */ React6.createElement("span", null, "/"), /* @__PURE__ */ React6.createElement("span", null, formatTime(duration))), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React6.createElement(
|
|
3024
|
+
"button",
|
|
3025
|
+
{
|
|
3026
|
+
onClick: onPrevious,
|
|
3027
|
+
className: "text-white/80 hover:text-white transition-colors"
|
|
3028
|
+
},
|
|
3029
|
+
/* @__PURE__ */ React6.createElement(SkipBack, { className: "w-6 h-6 fill-current" })
|
|
3030
|
+
), /* @__PURE__ */ React6.createElement(
|
|
3031
|
+
"button",
|
|
3032
|
+
{
|
|
3033
|
+
onClick: onPlayPause,
|
|
3034
|
+
className: "w-12 h-12 flex items-center justify-center bg-blue-500 hover:bg-blue-400 text-white rounded-full shadow-lg shadow-blue-500/20 transition-all hover:scale-105 active:scale-95"
|
|
3035
|
+
},
|
|
3036
|
+
isPlaying ? /* @__PURE__ */ React6.createElement(Pause, { className: "w-6 h-6 fill-current" }) : /* @__PURE__ */ React6.createElement(Play, { className: "w-6 h-6 fill-current ml-1" })
|
|
3037
|
+
), /* @__PURE__ */ React6.createElement(
|
|
3038
|
+
"button",
|
|
3039
|
+
{
|
|
3040
|
+
onClick: onNext,
|
|
3041
|
+
className: "text-white/80 hover:text-white transition-colors"
|
|
3042
|
+
},
|
|
3043
|
+
/* @__PURE__ */ React6.createElement(SkipForward, { className: "w-6 h-6 fill-current" })
|
|
3044
|
+
)), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-4 w-32 justify-end" }, /* @__PURE__ */ React6.createElement(
|
|
3045
|
+
"button",
|
|
3046
|
+
{
|
|
3047
|
+
onClick: onToggleLoop,
|
|
3048
|
+
className: "text-white/60 hover:text-white transition-colors",
|
|
3049
|
+
title: loopMode
|
|
3050
|
+
},
|
|
3051
|
+
loopMode === "list" && /* @__PURE__ */ React6.createElement(Repeat, { className: "w-5 h-5" }),
|
|
3052
|
+
loopMode === "single" && /* @__PURE__ */ React6.createElement(Repeat1, { className: "w-5 h-5 text-blue-400" }),
|
|
3053
|
+
loopMode === "shuffle" && /* @__PURE__ */ React6.createElement(Shuffle, { className: "w-5 h-5 text-orange-400" })
|
|
3054
|
+
), /* @__PURE__ */ React6.createElement(
|
|
3055
|
+
"button",
|
|
3056
|
+
{
|
|
3057
|
+
onClick: onTogglePlaylist,
|
|
3058
|
+
className: "text-white/60 hover:text-white transition-colors"
|
|
3059
|
+
},
|
|
3060
|
+
/* @__PURE__ */ React6.createElement(ListMusic, { className: "w-5 h-5" })
|
|
3061
|
+
)))
|
|
3062
|
+
);
|
|
3063
|
+
};
|
|
3064
|
+
var PlaylistPanel = ({
|
|
3065
|
+
tracks,
|
|
3066
|
+
currentIndex,
|
|
3067
|
+
isOpen,
|
|
3068
|
+
onClose,
|
|
3069
|
+
onSelectTrack,
|
|
3070
|
+
className = ""
|
|
3071
|
+
}) => {
|
|
3072
|
+
if (!isOpen) return null;
|
|
3073
|
+
return /* @__PURE__ */ React6.createElement(
|
|
3074
|
+
"div",
|
|
3075
|
+
{
|
|
3076
|
+
className: `fixed inset-y-0 right-0 w-80 bg-gray-900/90 backdrop-blur-2xl border-l border-white/10 shadow-2xl z-50 flex flex-col pointer-events-auto transform transition-transform duration-500 ease-out ${isOpen ? "translate-x-0" : "translate-x-full"} ${className}`
|
|
3077
|
+
},
|
|
3078
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between p-6 border-b border-white/10" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React6.createElement(Music, { className: "w-5 h-5 text-blue-400" }), /* @__PURE__ */ React6.createElement("h3", { className: "text-lg font-bold text-white" }, "\u64AD\u653E\u5217\u8868")), /* @__PURE__ */ React6.createElement(
|
|
3079
|
+
"button",
|
|
3080
|
+
{
|
|
3081
|
+
onClick: onClose,
|
|
3082
|
+
className: "p-2 text-white/60 hover:text-white transition-colors"
|
|
3083
|
+
},
|
|
3084
|
+
/* @__PURE__ */ React6.createElement(X, { className: "w-5 h-5" })
|
|
3085
|
+
)),
|
|
3086
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex-1 overflow-y-auto p-4 space-y-2 custom-scrollbar" }, tracks.map((track, index) => {
|
|
3087
|
+
const isActive = index === currentIndex;
|
|
3088
|
+
return /* @__PURE__ */ React6.createElement(
|
|
3089
|
+
"button",
|
|
3090
|
+
{
|
|
3091
|
+
key: track.id,
|
|
3092
|
+
onClick: () => onSelectTrack(index),
|
|
3093
|
+
className: `w-full flex items-center gap-4 p-3 rounded-xl transition-all group ${isActive ? "bg-blue-500/20 border border-blue-500/30" : "hover:bg-white/5 border border-transparent"}`
|
|
3094
|
+
},
|
|
3095
|
+
/* @__PURE__ */ React6.createElement("div", { className: "relative w-12 h-12 flex-shrink-0 rounded-lg overflow-hidden bg-gray-800" }, track.coverUrl ? /* @__PURE__ */ React6.createElement("img", { src: track.coverUrl, alt: track.title, className: "w-full h-full object-cover" }) : /* @__PURE__ */ React6.createElement("div", { className: "w-full h-full flex items-center justify-center text-white/20" }, /* @__PURE__ */ React6.createElement(Music, { className: "w-6 h-6" })), isActive && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 bg-blue-500/40 flex items-center justify-center" }, /* @__PURE__ */ React6.createElement("div", { className: "flex gap-1 items-end h-4" }, /* @__PURE__ */ React6.createElement("div", { className: "w-1 bg-white animate-music-bar-1" }), /* @__PURE__ */ React6.createElement("div", { className: "w-1 bg-white animate-music-bar-2" }), /* @__PURE__ */ React6.createElement("div", { className: "w-1 bg-white animate-music-bar-3" })))),
|
|
3096
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex-1 text-left min-w-0" }, /* @__PURE__ */ React6.createElement("h4", { className: `text-sm font-bold truncate ${isActive ? "text-blue-400" : "text-white/90"}` }, track.title), /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-white/40 truncate mt-0.5" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6")),
|
|
3097
|
+
!isActive && /* @__PURE__ */ React6.createElement("div", { className: "opacity-0 group-hover:opacity-100 transition-opacity" }, /* @__PURE__ */ React6.createElement(Play, { className: "w-4 h-4 text-white/40 fill-current" }))
|
|
3098
|
+
);
|
|
3099
|
+
})),
|
|
3100
|
+
/* @__PURE__ */ React6.createElement("div", { className: "p-6 border-t border-white/10" }, /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-gray-500 text-center" }, "\u5171 ", tracks.length, " \u9996\u66F2\u76EE")),
|
|
3101
|
+
/* @__PURE__ */ React6.createElement("style", { dangerouslySetInnerHTML: { __html: `
|
|
3102
|
+
.custom-scrollbar::-webkit-scrollbar {
|
|
3103
|
+
width: 4px;
|
|
3104
|
+
}
|
|
3105
|
+
.custom-scrollbar::-webkit-scrollbar-track {
|
|
3106
|
+
background: transparent;
|
|
3107
|
+
}
|
|
3108
|
+
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
3109
|
+
background: rgba(255, 255, 255, 0.1);
|
|
3110
|
+
border-radius: 10px;
|
|
3111
|
+
}
|
|
3112
|
+
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
3113
|
+
background: rgba(255, 255, 255, 0.2);
|
|
3114
|
+
}
|
|
3115
|
+
@keyframes music-bar-1 {
|
|
3116
|
+
0%, 100% { height: 4px; }
|
|
3117
|
+
50% { height: 12px; }
|
|
3118
|
+
}
|
|
3119
|
+
@keyframes music-bar-2 {
|
|
3120
|
+
0%, 100% { height: 8px; }
|
|
3121
|
+
50% { height: 16px; }
|
|
3122
|
+
}
|
|
3123
|
+
@keyframes music-bar-3 {
|
|
3124
|
+
0%, 100% { height: 6px; }
|
|
3125
|
+
50% { height: 14px; }
|
|
3126
|
+
}
|
|
3127
|
+
.animate-music-bar-1 { animation: music-bar-1 0.6s infinite; }
|
|
3128
|
+
.animate-music-bar-2 { animation: music-bar-2 0.8s infinite; }
|
|
3129
|
+
.animate-music-bar-3 { animation: music-bar-3 0.7s infinite; }
|
|
3130
|
+
` } })
|
|
3131
|
+
);
|
|
3132
|
+
};
|
|
3133
|
+
var TrackInfo = ({ track, className = "" }) => {
|
|
3134
|
+
return /* @__PURE__ */ React6.createElement("div", { className: `flex flex-col items-center text-center gap-2 ${className}` }, /* @__PURE__ */ React6.createElement("div", { className: "px-4 py-1.5 bg-black/40 backdrop-blur-md rounded-full border border-white/10 shadow-lg" }, /* @__PURE__ */ React6.createElement("h2", { className: "text-lg font-bold text-white tracking-wider truncate max-w-md" }, track.title)), /* @__PURE__ */ React6.createElement("p", { className: "text-sm font-medium text-white/60 drop-shadow-md" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6"));
|
|
3135
|
+
};
|
|
3136
|
+
|
|
3137
|
+
// src/mmd/music-player/MMDMusicPlayer.tsx
|
|
3138
|
+
var MMDMusicPlayer = forwardRef(
|
|
3139
|
+
({
|
|
3140
|
+
config,
|
|
3141
|
+
stage,
|
|
3142
|
+
mobileOptimization,
|
|
3143
|
+
initialTrackIndex = 0,
|
|
3144
|
+
onTrackChange,
|
|
3145
|
+
onPlayPause,
|
|
3146
|
+
onProgress,
|
|
3147
|
+
onError,
|
|
3148
|
+
className,
|
|
3149
|
+
style
|
|
3150
|
+
}, ref) => {
|
|
3151
|
+
const { tracks, autoPlay = false, defaultLoopMode = "list" } = config;
|
|
3152
|
+
const [currentIndex, setCurrentIndex] = useState(initialTrackIndex);
|
|
3153
|
+
const [isPlaying, setIsPlaying] = useState(autoPlay);
|
|
3154
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
3155
|
+
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
3156
|
+
const [currentTime, setCurrentTime] = useState(0);
|
|
3157
|
+
const [duration, setDuration] = useState(0);
|
|
3158
|
+
const [loopMode, setLoopMode] = useState(defaultLoopMode);
|
|
3159
|
+
const [showPlaylist, setShowPlaylist] = useState(false);
|
|
3160
|
+
const [isUIVisible, setIsUIVisible] = useState(true);
|
|
3161
|
+
const playerRef = useRef(null);
|
|
3162
|
+
const containerRef = useRef(null);
|
|
3163
|
+
const isStartedRef = useRef(autoPlay);
|
|
3164
|
+
const uiTimeoutRef = useRef(null);
|
|
3165
|
+
const currentTrack = tracks[currentIndex];
|
|
3166
|
+
const goToTrack = useCallback(
|
|
3167
|
+
(index) => {
|
|
3168
|
+
if (index < 0 || index >= tracks.length) return;
|
|
3169
|
+
if (isTransitioning) return;
|
|
3170
|
+
const track = tracks[index];
|
|
3171
|
+
if (!track) return;
|
|
3172
|
+
console.log(`[MMDMusicPlayer] Transitioning to track ${index}: ${track.title}`);
|
|
3173
|
+
setIsTransitioning(true);
|
|
3174
|
+
setIsLoading(true);
|
|
3175
|
+
const wasPlaying = isPlaying;
|
|
3176
|
+
setIsPlaying(false);
|
|
3177
|
+
setTimeout(() => {
|
|
3178
|
+
setCurrentIndex(index);
|
|
3179
|
+
setCurrentTime(0);
|
|
3180
|
+
setDuration(0);
|
|
3181
|
+
onTrackChange?.(track, index);
|
|
3182
|
+
setTimeout(() => {
|
|
3183
|
+
setIsTransitioning(false);
|
|
3184
|
+
if (wasPlaying) {
|
|
3185
|
+
isStartedRef.current = true;
|
|
3186
|
+
}
|
|
3187
|
+
}, 100);
|
|
3188
|
+
}, 300);
|
|
3189
|
+
},
|
|
3190
|
+
[tracks, isTransitioning, isPlaying, onTrackChange]
|
|
3191
|
+
);
|
|
3192
|
+
const next = useCallback(() => {
|
|
3193
|
+
let nextIndex = currentIndex + 1;
|
|
3194
|
+
if (loopMode === "shuffle") {
|
|
3195
|
+
nextIndex = Math.floor(Math.random() * tracks.length);
|
|
3196
|
+
} else if (nextIndex >= tracks.length) {
|
|
3197
|
+
nextIndex = 0;
|
|
3198
|
+
}
|
|
3199
|
+
goToTrack(nextIndex);
|
|
3200
|
+
}, [currentIndex, tracks.length, loopMode, goToTrack]);
|
|
3201
|
+
const previous = useCallback(() => {
|
|
3202
|
+
let prevIndex = currentIndex - 1;
|
|
3203
|
+
if (prevIndex < 0) {
|
|
3204
|
+
prevIndex = tracks.length - 1;
|
|
3205
|
+
}
|
|
3206
|
+
goToTrack(prevIndex);
|
|
3207
|
+
}, [currentIndex, tracks.length, goToTrack]);
|
|
3208
|
+
useImperativeHandle(
|
|
3209
|
+
ref,
|
|
3210
|
+
() => ({
|
|
3211
|
+
play: () => {
|
|
3212
|
+
setIsPlaying(true);
|
|
3213
|
+
isStartedRef.current = true;
|
|
3214
|
+
playerRef.current?.play();
|
|
3215
|
+
},
|
|
3216
|
+
pause: () => {
|
|
3217
|
+
setIsPlaying(false);
|
|
3218
|
+
isStartedRef.current = false;
|
|
3219
|
+
playerRef.current?.pause();
|
|
3220
|
+
},
|
|
3221
|
+
next,
|
|
3222
|
+
previous,
|
|
3223
|
+
goToTrack,
|
|
3224
|
+
setLoopMode,
|
|
3225
|
+
getState: () => ({
|
|
3226
|
+
currentIndex,
|
|
3227
|
+
isPlaying,
|
|
3228
|
+
currentTime,
|
|
3229
|
+
duration,
|
|
3230
|
+
loopMode
|
|
3231
|
+
})
|
|
3232
|
+
}),
|
|
3233
|
+
[next, previous, goToTrack, currentIndex, isPlaying, currentTime, duration, loopMode]
|
|
3234
|
+
);
|
|
3235
|
+
const handleEnded = useCallback(() => {
|
|
3236
|
+
if (loopMode === "single") {
|
|
3237
|
+
playerRef.current?.seek(0);
|
|
3238
|
+
playerRef.current?.play();
|
|
3239
|
+
} else {
|
|
3240
|
+
next();
|
|
3241
|
+
}
|
|
3242
|
+
}, [loopMode, next]);
|
|
3243
|
+
const handleTimeUpdate = useCallback((time) => {
|
|
3244
|
+
setCurrentTime(time);
|
|
3245
|
+
if (playerRef.current) {
|
|
3246
|
+
const total = playerRef.current.getDuration();
|
|
3247
|
+
setDuration(total);
|
|
3248
|
+
onProgress?.(time, total);
|
|
3249
|
+
}
|
|
3250
|
+
}, [onProgress]);
|
|
3251
|
+
const resetUITimeout = useCallback(() => {
|
|
3252
|
+
setIsUIVisible(true);
|
|
3253
|
+
if (uiTimeoutRef.current) clearTimeout(uiTimeoutRef.current);
|
|
3254
|
+
if (isPlaying) {
|
|
3255
|
+
uiTimeoutRef.current = setTimeout(() => {
|
|
3256
|
+
if (!showPlaylist) setIsUIVisible(false);
|
|
3257
|
+
}, 5e3);
|
|
3258
|
+
}
|
|
3259
|
+
}, [isPlaying, showPlaylist]);
|
|
3260
|
+
useEffect(() => {
|
|
3261
|
+
resetUITimeout();
|
|
3262
|
+
return () => {
|
|
3263
|
+
if (uiTimeoutRef.current) clearTimeout(uiTimeoutRef.current);
|
|
3264
|
+
};
|
|
3265
|
+
}, [resetUITimeout]);
|
|
3266
|
+
if (!currentTrack) {
|
|
3267
|
+
return /* @__PURE__ */ React6.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u64AD\u653E\u5217\u8868\u4E3A\u7A7A");
|
|
3268
|
+
}
|
|
3269
|
+
return /* @__PURE__ */ React6.createElement(
|
|
3270
|
+
"div",
|
|
3271
|
+
{
|
|
3272
|
+
ref: containerRef,
|
|
3273
|
+
className: `relative bg-black group ${className}`,
|
|
3274
|
+
style: { width: "100%", height: "100%", overflow: "hidden", ...style },
|
|
3275
|
+
onMouseMove: resetUITimeout,
|
|
3276
|
+
onClick: resetUITimeout
|
|
3277
|
+
},
|
|
3278
|
+
/* @__PURE__ */ React6.createElement(
|
|
3279
|
+
"div",
|
|
3280
|
+
{
|
|
3281
|
+
className: "absolute inset-0 w-full h-full",
|
|
3282
|
+
style: {
|
|
3283
|
+
zIndex: 0,
|
|
3284
|
+
opacity: isLoading || isTransitioning ? 0 : 1,
|
|
3285
|
+
transition: "opacity 0.5s ease-in-out"
|
|
3286
|
+
}
|
|
3287
|
+
},
|
|
3288
|
+
!isTransitioning && /* @__PURE__ */ React6.createElement(
|
|
3289
|
+
MMDPlayerBase,
|
|
3290
|
+
{
|
|
3291
|
+
key: currentTrack.id,
|
|
3292
|
+
ref: playerRef,
|
|
3293
|
+
resources: currentTrack.resources,
|
|
3294
|
+
stage,
|
|
3295
|
+
autoPlay: isStartedRef.current,
|
|
3296
|
+
loop: loopMode === "single",
|
|
3297
|
+
mobileOptimization,
|
|
3298
|
+
onLoad: () => {
|
|
3299
|
+
console.log("[MMDMusicPlayer] Track loaded");
|
|
3300
|
+
setIsLoading(false);
|
|
3301
|
+
if (isStartedRef.current) {
|
|
3302
|
+
playerRef.current?.play();
|
|
3303
|
+
setIsPlaying(true);
|
|
3304
|
+
}
|
|
3305
|
+
},
|
|
3306
|
+
onPlay: () => {
|
|
3307
|
+
setIsPlaying(true);
|
|
3308
|
+
onPlayPause?.(true);
|
|
3309
|
+
},
|
|
3310
|
+
onPause: () => {
|
|
3311
|
+
setIsPlaying(false);
|
|
3312
|
+
onPlayPause?.(false);
|
|
3313
|
+
},
|
|
3314
|
+
onTimeUpdate: handleTimeUpdate,
|
|
3315
|
+
onEnded: handleEnded,
|
|
3316
|
+
onError
|
|
3317
|
+
}
|
|
3318
|
+
)
|
|
3319
|
+
),
|
|
3320
|
+
(isLoading || isTransitioning) && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" }, /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col items-center gap-4" }, /* @__PURE__ */ React6.createElement("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" }), /* @__PURE__ */ React6.createElement("div", { className: "text-white font-medium" }, isTransitioning ? "\u51C6\u5907\u4E0B\u4E00\u9996..." : "\u6B63\u5728\u52A0\u8F7D\u821E\u53F0..."))),
|
|
3321
|
+
/* @__PURE__ */ React6.createElement(
|
|
3322
|
+
"div",
|
|
3323
|
+
{
|
|
3324
|
+
className: `absolute inset-0 z-10 flex flex-col justify-between transition-opacity duration-700 pointer-events-none ${isUIVisible ? "opacity-100" : "opacity-0"}`
|
|
3325
|
+
},
|
|
3326
|
+
/* @__PURE__ */ React6.createElement("div", { className: "pt-12 px-8 flex justify-center" }, /* @__PURE__ */ React6.createElement(TrackInfo, { track: currentTrack })),
|
|
3327
|
+
/* @__PURE__ */ React6.createElement("div", { className: "pb-12 px-8" }, /* @__PURE__ */ React6.createElement(
|
|
3328
|
+
MusicControls,
|
|
3329
|
+
{
|
|
3330
|
+
isPlaying,
|
|
3331
|
+
currentTime,
|
|
3332
|
+
duration,
|
|
3333
|
+
loopMode,
|
|
3334
|
+
onPlayPause: () => isPlaying ? playerRef.current?.pause() : playerRef.current?.play(),
|
|
3335
|
+
onNext: next,
|
|
3336
|
+
onPrevious: previous,
|
|
3337
|
+
onSeek: (time) => playerRef.current?.seek(time),
|
|
3338
|
+
onToggleLoop: () => {
|
|
3339
|
+
const modes = ["list", "single", "shuffle"];
|
|
3340
|
+
const nextMode = modes[(modes.indexOf(loopMode) + 1) % modes.length];
|
|
3341
|
+
setLoopMode(nextMode);
|
|
3342
|
+
},
|
|
3343
|
+
onTogglePlaylist: () => setShowPlaylist(!showPlaylist)
|
|
3344
|
+
}
|
|
3345
|
+
))
|
|
3346
|
+
),
|
|
3347
|
+
/* @__PURE__ */ React6.createElement(
|
|
3348
|
+
PlaylistPanel,
|
|
3349
|
+
{
|
|
3350
|
+
tracks,
|
|
3351
|
+
currentIndex,
|
|
3352
|
+
isOpen: showPlaylist,
|
|
3353
|
+
onClose: () => setShowPlaylist(false),
|
|
3354
|
+
onSelectTrack: (index) => {
|
|
3355
|
+
goToTrack(index);
|
|
3356
|
+
setShowPlaylist(false);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
)
|
|
3360
|
+
);
|
|
3361
|
+
}
|
|
3362
|
+
);
|
|
3363
|
+
MMDMusicPlayer.displayName = "MMDMusicPlayer";
|
|
2886
3364
|
|
|
2887
|
-
export { DialogueBox, HistoryPanel, LoadingOverlay, LoadingScreen, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, StartScreen, loadAmmo };
|
|
3365
|
+
export { DialogueBox, HistoryPanel, LoadingOverlay, LoadingScreen, MMDMusicPlayer, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, MusicControls, PlaylistPanel, StartScreen, TrackInfo, loadAmmo };
|
|
2888
3366
|
//# sourceMappingURL=index.mjs.map
|
|
2889
3367
|
//# sourceMappingURL=index.mjs.map
|