sa2kit 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/UniversalFileService-CEZRJ87g.d.mts +727 -0
  2. package/dist/UniversalFileService-CEZRJ87g.d.ts +727 -0
  3. package/dist/api/index.d.mts +248 -0
  4. package/dist/api/index.d.ts +248 -0
  5. package/dist/api/index.js +294 -0
  6. package/dist/api/index.js.map +1 -0
  7. package/dist/api/index.mjs +290 -0
  8. package/dist/api/index.mjs.map +1 -0
  9. package/dist/auth/client/index.d.mts +52 -3
  10. package/dist/auth/client/index.d.ts +52 -3
  11. package/dist/auth/components/index.d.mts +149 -4
  12. package/dist/auth/components/index.d.ts +149 -4
  13. package/dist/auth/components/index.js +243 -9
  14. package/dist/auth/components/index.js.map +1 -1
  15. package/dist/auth/components/index.mjs +237 -4
  16. package/dist/auth/components/index.mjs.map +1 -1
  17. package/dist/auth/hooks/index.d.mts +31 -2
  18. package/dist/auth/hooks/index.d.ts +31 -2
  19. package/dist/auth/index.d.mts +5 -5
  20. package/dist/auth/index.d.ts +5 -5
  21. package/dist/auth/index.js +49 -17
  22. package/dist/auth/index.mjs +1 -1
  23. package/dist/auth/routes/index.d.mts +103 -5
  24. package/dist/auth/routes/index.d.ts +103 -5
  25. package/dist/auth/routes/index.js +37 -5
  26. package/dist/auth/routes/index.mjs +1 -1
  27. package/dist/chunk-42IJ7HEI.js +573 -0
  28. package/dist/chunk-42IJ7HEI.js.map +1 -0
  29. package/dist/chunk-7XLFSPDG.mjs +31 -0
  30. package/dist/chunk-7XLFSPDG.mjs.map +1 -0
  31. package/dist/chunk-GCVOKQZP.js +36 -0
  32. package/dist/chunk-GCVOKQZP.js.map +1 -0
  33. package/dist/chunk-IBLB7ARJ.mjs +560 -0
  34. package/dist/chunk-IBLB7ARJ.mjs.map +1 -0
  35. package/dist/{chunk-6FNUWAIV.js → chunk-LX4XX6W7.js} +54 -8
  36. package/dist/chunk-LX4XX6W7.js.map +1 -0
  37. package/dist/{chunk-HXFFYNIF.mjs → chunk-T5OZHYVM.mjs} +54 -8
  38. package/dist/chunk-T5OZHYVM.mjs.map +1 -0
  39. package/dist/config/server/index.d.mts +1533 -0
  40. package/dist/config/server/index.d.ts +1533 -0
  41. package/dist/config/server/index.js +1177 -0
  42. package/dist/config/server/index.js.map +1 -0
  43. package/dist/config/server/index.mjs +1138 -0
  44. package/dist/config/server/index.mjs.map +1 -0
  45. package/dist/i18n/index.d.mts +2 -1
  46. package/dist/i18n/index.d.ts +2 -1
  47. package/dist/i18n/index.js +125 -61
  48. package/dist/i18n/index.js.map +1 -1
  49. package/dist/i18n/index.mjs +126 -62
  50. package/dist/i18n/index.mjs.map +1 -1
  51. package/dist/index.js +6 -6
  52. package/dist/index.mjs +1 -1
  53. package/dist/mmd/index.d.mts +346 -0
  54. package/dist/mmd/index.d.ts +346 -0
  55. package/dist/mmd/index.js +1535 -0
  56. package/dist/mmd/index.js.map +1 -0
  57. package/dist/mmd/index.mjs +1503 -0
  58. package/dist/mmd/index.mjs.map +1 -0
  59. package/dist/storage/index.d.mts +1 -0
  60. package/dist/storage/index.d.ts +1 -0
  61. package/dist/storage/index.js +9 -9
  62. package/dist/storage/index.mjs +1 -1
  63. package/dist/{index-8VoHap_4.d.mts → types-CroexXnI.d.ts} +38 -44
  64. package/dist/{index-8VoHap_4.d.ts → types-DmsXCWvm.d.mts} +38 -44
  65. package/dist/{types-DAxQ1MeY.d.ts → types-Dt0oqeFM.d.mts} +1 -1
  66. package/dist/{types-DT8LVCvE.d.mts → types-zK6kDzDQ.d.ts} +1 -1
  67. package/dist/universalExport/index.js +17 -32
  68. package/dist/universalExport/index.js.map +1 -1
  69. package/dist/universalExport/index.mjs +2 -29
  70. package/dist/universalExport/index.mjs.map +1 -1
  71. package/dist/universalExport/server/index.d.mts +849 -8
  72. package/dist/universalExport/server/index.d.ts +849 -8
  73. package/dist/universalExport/server/index.js +1382 -2
  74. package/dist/universalExport/server/index.js.map +1 -1
  75. package/dist/universalExport/server/index.mjs +1355 -3
  76. package/dist/universalExport/server/index.mjs.map +1 -1
  77. package/dist/universalFile/index.d.mts +54 -3
  78. package/dist/universalFile/index.d.ts +54 -3
  79. package/dist/universalFile/index.js +272 -0
  80. package/dist/universalFile/index.js.map +1 -1
  81. package/dist/universalFile/index.mjs +267 -1
  82. package/dist/universalFile/index.mjs.map +1 -1
  83. package/dist/universalFile/server/index.d.mts +2541 -469
  84. package/dist/universalFile/server/index.d.ts +2541 -469
  85. package/dist/universalFile/server/index.js +830 -64
  86. package/dist/universalFile/server/index.js.map +1 -1
  87. package/dist/universalFile/server/index.mjs +803 -66
  88. package/dist/universalFile/server/index.mjs.map +1 -1
  89. package/package.json +47 -23
  90. package/dist/chunk-6FNUWAIV.js.map +0 -1
  91. package/dist/chunk-APY57REU.js +0 -300
  92. package/dist/chunk-APY57REU.js.map +0 -1
  93. package/dist/chunk-C64RY2OW.mjs +0 -295
  94. package/dist/chunk-C64RY2OW.mjs.map +0 -1
  95. package/dist/chunk-HXFFYNIF.mjs.map +0 -1
  96. package/dist/types-CoGG1rNV.d.mts +0 -258
  97. package/dist/types-CoGG1rNV.d.ts +0 -258
  98. package/dist/types-DW9qar-w.d.mts +0 -52
  99. package/dist/types-DW9qar-w.d.ts +0 -52
@@ -0,0 +1,1503 @@
1
+ import '../chunk-BJTO5JO5.mjs';
2
+ import React2, { useRef, useState, useEffect, useMemo } from 'react';
3
+ import * as THREE2 from 'three';
4
+ import { OrbitControls, OutlineEffect, MMDAnimationHelper, MMDLoader } from 'three-stdlib';
5
+
6
+ // src/mmd/utils/ammo-loader.ts
7
+ var ammoPromise = null;
8
+ var loadAmmo = (config) => {
9
+ const configKey = `${config.scriptPath}|${config.wasmBasePath}`;
10
+ const currentConfigKey = window.__AMMO_CONFIG_KEY__;
11
+ if (ammoPromise && currentConfigKey === configKey) {
12
+ return ammoPromise;
13
+ }
14
+ if (currentConfigKey && currentConfigKey !== configKey) {
15
+ ammoPromise = null;
16
+ window.Ammo = void 0;
17
+ }
18
+ ammoPromise = new Promise((resolve, reject) => {
19
+ if (typeof window === "undefined") {
20
+ resolve();
21
+ return;
22
+ }
23
+ if (window.Ammo && currentConfigKey === configKey) {
24
+ console.log("\u2705 [Ammo] \u5DF2\u52A0\u8F7D\uFF0C\u76F4\u63A5\u4F7F\u7528");
25
+ resolve();
26
+ return;
27
+ }
28
+ console.log("\u{1F4E6} [Ammo] \u5F00\u59CB\u52A0\u8F7D Ammo.js...");
29
+ console.log("\u{1F4C2} [Ammo] \u811A\u672C\u8DEF\u5F84:", config.scriptPath);
30
+ console.log("\u{1F4C2} [Ammo] WASM \u57FA\u7840\u8DEF\u5F84:", config.wasmBasePath);
31
+ window.__AMMO_CONFIG_KEY__ = configKey;
32
+ window.AMMO_PATH = config.wasmBasePath;
33
+ const script = document.createElement("script");
34
+ script.src = config.scriptPath;
35
+ script.async = true;
36
+ script.onload = () => {
37
+ console.log("\u2705 [Ammo] \u811A\u672C\u52A0\u8F7D\u5B8C\u6210\uFF0C\u7B49\u5F85\u521D\u59CB\u5316...");
38
+ const checkAmmo = () => {
39
+ if (typeof window.Ammo === "function") {
40
+ console.log("\u{1F504} [Ammo] \u5F00\u59CB\u521D\u59CB\u5316 WASM...");
41
+ window.Ammo({
42
+ locateFile: (path) => {
43
+ console.log("\u{1F4CD} [Ammo] \u5B9A\u4F4D\u6587\u4EF6:", path);
44
+ if (path.endsWith(".wasm")) {
45
+ return config.wasmBasePath + path;
46
+ }
47
+ return path;
48
+ }
49
+ }).then((AmmoLib) => {
50
+ console.log("\u2705 [Ammo] \u521D\u59CB\u5316\u5B8C\u6210\uFF01");
51
+ window.Ammo = AmmoLib;
52
+ resolve();
53
+ }).catch((err) => {
54
+ console.error("\u274C [Ammo] \u521D\u59CB\u5316\u5931\u8D25:", err);
55
+ reject(err);
56
+ });
57
+ } else {
58
+ if (window.Ammo) {
59
+ console.log("\u2705 [Ammo] \u5DF2\u521D\u59CB\u5316");
60
+ resolve();
61
+ } else {
62
+ console.log("\u23F3 [Ammo] \u7B49\u5F85\u521D\u59CB\u5316...");
63
+ setTimeout(checkAmmo, 100);
64
+ }
65
+ }
66
+ };
67
+ checkAmmo();
68
+ };
69
+ script.onerror = (e) => {
70
+ console.error("\u274C [Ammo] \u52A0\u8F7D\u5931\u8D25:", e);
71
+ reject(new Error(`Failed to load Ammo.js from ${config.scriptPath}. Please ensure the file exists.`));
72
+ ammoPromise = null;
73
+ window.__AMMO_CONFIG_KEY__ = void 0;
74
+ };
75
+ document.body.appendChild(script);
76
+ });
77
+ return ammoPromise;
78
+ };
79
+
80
+ // src/mmd/components/MMDPlayerBase.tsx
81
+ var MMDPlayerBase = ({
82
+ modelUrl,
83
+ vmdUrl,
84
+ cameraUrl,
85
+ audioUrl,
86
+ physics = true,
87
+ width = "100%",
88
+ height = "100%",
89
+ onLoad,
90
+ onProgress,
91
+ onError
92
+ }) => {
93
+ const containerRef = useRef(null);
94
+ const [loading, setLoading] = useState(true);
95
+ const [error, setError] = useState(null);
96
+ useEffect(() => {
97
+ if (!containerRef.current) return;
98
+ let scene;
99
+ let camera;
100
+ let renderer;
101
+ let effect;
102
+ let helper;
103
+ let clock;
104
+ let animationId;
105
+ const init = async () => {
106
+ try {
107
+ setLoading(true);
108
+ setError(null);
109
+ if (physics) {
110
+ await loadAmmo({
111
+ scriptPath: "/mikutalking/libs/ammo.wasm.js",
112
+ wasmBasePath: "/mikutalking/libs/"
113
+ });
114
+ }
115
+ const container = containerRef.current;
116
+ const w = container.clientWidth;
117
+ const h = container.clientHeight;
118
+ scene = new THREE2.Scene();
119
+ scene.background = new THREE2.Color(16777215);
120
+ camera = new THREE2.PerspectiveCamera(45, w / h, 1, 2e3);
121
+ camera.position.z = 30;
122
+ const ambient = new THREE2.AmbientLight(6710886);
123
+ scene.add(ambient);
124
+ const directionalLight = new THREE2.DirectionalLight(8943462);
125
+ directionalLight.position.set(-1, 1, 1).normalize();
126
+ scene.add(directionalLight);
127
+ renderer = new THREE2.WebGLRenderer({ antialias: true });
128
+ renderer.setPixelRatio(window.devicePixelRatio);
129
+ renderer.setSize(w, h);
130
+ container.appendChild(renderer.domElement);
131
+ effect = new OutlineEffect(renderer);
132
+ helper = new MMDAnimationHelper({
133
+ afterglow: 2
134
+ });
135
+ const loader = new MMDLoader();
136
+ loader.load(
137
+ modelUrl,
138
+ (mesh) => {
139
+ scene.add(mesh);
140
+ if (vmdUrl) {
141
+ loader.loadAnimation(
142
+ vmdUrl,
143
+ mesh,
144
+ (vmdObject) => {
145
+ helper.add(mesh, {
146
+ animation: vmdObject,
147
+ physics
148
+ });
149
+ },
150
+ (xhr) => {
151
+ if (onProgress) onProgress(xhr);
152
+ },
153
+ (err) => {
154
+ console.error("Error loading animation", err);
155
+ if (onError) onError(err);
156
+ }
157
+ );
158
+ } else {
159
+ helper.add(mesh, { physics });
160
+ }
161
+ if (cameraUrl) {
162
+ loader.loadAnimation(
163
+ cameraUrl,
164
+ camera,
165
+ (cameraVmdObject) => {
166
+ helper.add(camera, {
167
+ animation: cameraVmdObject
168
+ });
169
+ },
170
+ void 0,
171
+ (err) => {
172
+ console.error("Error loading camera motion", err);
173
+ }
174
+ );
175
+ }
176
+ if (audioUrl) {
177
+ new THREE2.AudioLoader().load(
178
+ audioUrl,
179
+ (buffer) => {
180
+ const listener = new THREE2.AudioListener();
181
+ camera.add(listener);
182
+ const audio = new THREE2.Audio(listener);
183
+ audio.setBuffer(buffer);
184
+ helper.add(audio);
185
+ },
186
+ void 0,
187
+ (err) => {
188
+ console.error("Error loading audio", err);
189
+ }
190
+ );
191
+ }
192
+ if (onLoad) onLoad();
193
+ setLoading(false);
194
+ },
195
+ (xhr) => {
196
+ if (onProgress) onProgress(xhr);
197
+ },
198
+ (err) => {
199
+ setError("Failed to load model");
200
+ if (onError) onError(err);
201
+ setLoading(false);
202
+ }
203
+ );
204
+ clock = new THREE2.Clock();
205
+ const animate = () => {
206
+ animationId = requestAnimationFrame(animate);
207
+ helper.update(clock.getDelta());
208
+ effect.render(scene, camera);
209
+ };
210
+ animate();
211
+ const handleResize = () => {
212
+ if (!container) return;
213
+ const width2 = container.clientWidth;
214
+ const height2 = container.clientHeight;
215
+ camera.aspect = width2 / height2;
216
+ camera.updateProjectionMatrix();
217
+ effect.setSize(width2, height2);
218
+ };
219
+ window.addEventListener("resize", handleResize);
220
+ return () => {
221
+ window.removeEventListener("resize", handleResize);
222
+ };
223
+ } catch (e) {
224
+ console.error(e);
225
+ setError("Initialization error");
226
+ setLoading(false);
227
+ }
228
+ return void 0;
229
+ };
230
+ init();
231
+ return () => {
232
+ if (animationId) cancelAnimationFrame(animationId);
233
+ if (renderer) {
234
+ renderer.dispose();
235
+ const domElement = renderer.domElement;
236
+ if (domElement && domElement.parentNode) {
237
+ domElement.parentNode.removeChild(domElement);
238
+ }
239
+ }
240
+ };
241
+ }, [modelUrl, vmdUrl, cameraUrl, audioUrl, physics]);
242
+ return /* @__PURE__ */ React2.createElement(
243
+ "div",
244
+ {
245
+ ref: containerRef,
246
+ style: { width, height, position: "relative", overflow: "hidden" }
247
+ },
248
+ loading && /* @__PURE__ */ React2.createElement("div", { style: {
249
+ position: "absolute",
250
+ top: 0,
251
+ left: 0,
252
+ width: "100%",
253
+ height: "100%",
254
+ display: "flex",
255
+ justifyContent: "center",
256
+ alignItems: "center",
257
+ background: "#f0f0f0",
258
+ color: "#666",
259
+ zIndex: 1
260
+ } }, "Loading MMD..."),
261
+ error && /* @__PURE__ */ React2.createElement("div", { style: {
262
+ position: "absolute",
263
+ top: 0,
264
+ left: 0,
265
+ width: "100%",
266
+ height: "100%",
267
+ display: "flex",
268
+ justifyContent: "center",
269
+ alignItems: "center",
270
+ background: "#ffeeee",
271
+ color: "#cc0000",
272
+ zIndex: 2
273
+ } }, error)
274
+ );
275
+ };
276
+ var MMDPlayerEnhanced = ({
277
+ resources,
278
+ resourcesList,
279
+ defaultResourceId,
280
+ resourceOptions,
281
+ defaultSelection,
282
+ stage,
283
+ autoPlay = false,
284
+ loop = false,
285
+ className = "",
286
+ style,
287
+ onLoad,
288
+ onError,
289
+ onResourceChange,
290
+ onSelectionChange,
291
+ onAudioEnded,
292
+ onAnimationEnded
293
+ }) => {
294
+ console.log("\u{1F3A8} [MMDPlayerEnhanced] \u7EC4\u4EF6\u521D\u59CB\u5316");
295
+ const [selectedResourceId, setSelectedResourceId] = useState(
296
+ defaultResourceId || resourcesList?.[0]?.id || ""
297
+ );
298
+ const [selectedModelId, setSelectedModelId] = useState(
299
+ defaultSelection?.modelId || resourceOptions?.models?.[0]?.id || ""
300
+ );
301
+ const [selectedMotionId, setSelectedMotionId] = useState(
302
+ defaultSelection?.motionId || ""
303
+ );
304
+ const [selectedAudioId, setSelectedAudioId] = useState(
305
+ defaultSelection?.audioId || ""
306
+ );
307
+ const [selectedCameraId, setSelectedCameraId] = useState(
308
+ defaultSelection?.cameraId || ""
309
+ );
310
+ const [selectedStageModelId, setSelectedStageModelId] = useState(
311
+ defaultSelection?.stageModelId || ""
312
+ );
313
+ const [selectedBackgroundId, setSelectedBackgroundId] = useState(
314
+ defaultSelection?.backgroundId || ""
315
+ );
316
+ const [showSettings, setShowSettings] = useState(false);
317
+ const [expandedSection, setExpandedSection] = useState(null);
318
+ const currentResources = useMemo(() => {
319
+ if (resourceOptions) {
320
+ const model = resourceOptions.models?.find((m) => m.id === selectedModelId);
321
+ const motion = resourceOptions.motions?.find((m) => m.id === selectedMotionId);
322
+ const audio = resourceOptions.audios?.find((a) => a.id === selectedAudioId);
323
+ const camera = resourceOptions.cameras?.find((c) => c.id === selectedCameraId);
324
+ const stageModel = resourceOptions.stageModels?.find((s) => s.id === selectedStageModelId);
325
+ const background = resourceOptions.backgrounds?.find((b) => b.id === selectedBackgroundId);
326
+ return {
327
+ modelPath: model?.path || resourceOptions.models?.[0]?.path || "",
328
+ motionPath: motion?.path,
329
+ audioPath: audio?.path,
330
+ cameraPath: camera?.path,
331
+ stageModelPath: stageModel?.path,
332
+ backgroundPath: background?.path
333
+ };
334
+ }
335
+ if (resourcesList && resourcesList.length > 0) {
336
+ const selected = resourcesList.find((r) => r.id === selectedResourceId);
337
+ const resourceItem = selected || resourcesList[0];
338
+ if (!resourceItem) {
339
+ throw new Error("\u65E0\u6CD5\u627E\u5230\u6709\u6548\u7684\u8D44\u6E90\u914D\u7F6E");
340
+ }
341
+ return resourceItem.resources;
342
+ }
343
+ if (!resources) {
344
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B resources\u3001resourcesList \u6216 resourceOptions");
345
+ }
346
+ return resources;
347
+ }, [
348
+ resources,
349
+ resourcesList,
350
+ selectedResourceId,
351
+ resourceOptions,
352
+ selectedModelId,
353
+ selectedMotionId,
354
+ selectedAudioId,
355
+ selectedCameraId,
356
+ selectedStageModelId,
357
+ selectedBackgroundId
358
+ ]);
359
+ console.log("\u{1F4C2} [MMDPlayerEnhanced] \u5F53\u524D\u8D44\u6E90\u914D\u7F6E:", currentResources);
360
+ console.log("\u{1F3AD} [MMDPlayerEnhanced] \u821E\u53F0\u914D\u7F6E:", stage);
361
+ const containerRef = useRef(null);
362
+ const rendererRef = useRef(null);
363
+ const sceneRef = useRef(null);
364
+ const cameraRef = useRef(null);
365
+ const controlsRef = useRef(null);
366
+ const helperRef = useRef(null);
367
+ const clockRef = useRef(new THREE2.Clock());
368
+ const audioRef = useRef(null);
369
+ const animationIdRef = useRef(null);
370
+ const isPlayingRef = useRef(false);
371
+ const isLoadedRef = useRef(false);
372
+ const shouldAutoPlayAfterReloadRef = useRef(false);
373
+ const vmdDataRef = useRef(null);
374
+ const animationDurationRef = useRef(0);
375
+ const hasAudioRef = useRef(false);
376
+ const animationEndedFiredRef = useRef(false);
377
+ const lastAnimationTimeRef = useRef(0);
378
+ const animationStoppedCountRef = useRef(0);
379
+ const [loading, setLoading] = useState(false);
380
+ const [loadingProgress, setLoadingProgress] = useState(0);
381
+ const [error, setError] = useState(null);
382
+ const [isPlaying, setIsPlaying] = useState(false);
383
+ const [isInitialized, setIsInitialized] = useState(false);
384
+ const [reloadTrigger, setReloadTrigger] = useState(0);
385
+ const [needReset, setNeedReset] = useState(false);
386
+ useEffect(() => {
387
+ console.log("\u{1F3D7}\uFE0F [MMDPlayerEnhanced] \u573A\u666F\u521D\u59CB\u5316 useEffect \u89E6\u53D1");
388
+ if (!containerRef.current) {
389
+ console.warn("\u26A0\uFE0F [MMDPlayerEnhanced] containerRef.current \u4E0D\u5B58\u5728");
390
+ return;
391
+ }
392
+ console.log("\u2705 [MMDPlayerEnhanced] \u5BB9\u5668\u5143\u7D20\u5B58\u5728\uFF0C\u5F00\u59CB\u521D\u59CB\u5316\u573A\u666F");
393
+ const container = containerRef.current;
394
+ if (container.children.length > 0) {
395
+ console.log("\u26A0\uFE0F [MMDPlayerEnhanced] \u573A\u666F\u5DF2\u7ECF\u521D\u59CB\u5316\uFF0C\u8DF3\u8FC7");
396
+ return;
397
+ }
398
+ const width = container.clientWidth;
399
+ const height = container.clientHeight;
400
+ const scene = new THREE2.Scene();
401
+ scene.background = new THREE2.Color(stage?.backgroundColor || "#000000");
402
+ sceneRef.current = scene;
403
+ const camera = new THREE2.PerspectiveCamera(45, width / height, 1, 2e3);
404
+ const camPos = stage?.cameraPosition || { x: 0, y: 10, z: 30 };
405
+ camera.position.set(camPos.x, camPos.y, camPos.z);
406
+ cameraRef.current = camera;
407
+ const renderer = new THREE2.WebGLRenderer({ antialias: true });
408
+ renderer.setSize(width, height);
409
+ renderer.setPixelRatio(window.devicePixelRatio);
410
+ container.appendChild(renderer.domElement);
411
+ rendererRef.current = renderer;
412
+ const ambient = new THREE2.AmbientLight(16777215, 0.6);
413
+ scene.add(ambient);
414
+ const directionalLight = new THREE2.DirectionalLight(16777215, 0.8);
415
+ directionalLight.position.set(1, 1, 1);
416
+ scene.add(directionalLight);
417
+ if (stage?.showGrid !== false) {
418
+ const gridHelper = new THREE2.PolarGridHelper(30, 10);
419
+ scene.add(gridHelper);
420
+ }
421
+ const controls = new OrbitControls(camera, renderer.domElement);
422
+ const target = stage?.cameraTarget || { x: 0, y: 10, z: 0 };
423
+ controls.target.set(target.x, target.y, target.z);
424
+ controls.update();
425
+ controlsRef.current = controls;
426
+ const handleResize = () => {
427
+ if (!container || !camera || !renderer) return;
428
+ const newWidth = container.clientWidth;
429
+ const newHeight = container.clientHeight;
430
+ camera.aspect = newWidth / newHeight;
431
+ camera.updateProjectionMatrix();
432
+ renderer.setSize(newWidth, newHeight);
433
+ };
434
+ window.addEventListener("resize", handleResize);
435
+ const animate = () => {
436
+ animationIdRef.current = requestAnimationFrame(animate);
437
+ if (helperRef.current && isPlayingRef.current) {
438
+ const delta = clockRef.current.getDelta();
439
+ helperRef.current.update(delta);
440
+ if (!hasAudioRef.current && !loop && !animationEndedFiredRef.current) {
441
+ const currentTime = clockRef.current.getElapsedTime();
442
+ if (animationDurationRef.current > 0) {
443
+ if (currentTime >= animationDurationRef.current - 0.1) {
444
+ console.log("\u{1F3AC} [MMDPlayerEnhanced] \u52A8\u753B\u64AD\u653E\u7ED3\u675F\uFF08\u65F6\u957F\u5224\u5B9A\uFF09");
445
+ animationEndedFiredRef.current = true;
446
+ isPlayingRef.current = false;
447
+ setIsPlaying(false);
448
+ onAnimationEnded?.();
449
+ }
450
+ } else {
451
+ if (Math.abs(currentTime - lastAnimationTimeRef.current) < 1e-3) {
452
+ animationStoppedCountRef.current++;
453
+ if (animationStoppedCountRef.current > 30) {
454
+ console.log("\u{1F3AC} [MMDPlayerEnhanced] \u52A8\u753B\u64AD\u653E\u7ED3\u675F\uFF08\u505C\u6B62\u68C0\u6D4B\uFF09");
455
+ animationEndedFiredRef.current = true;
456
+ isPlayingRef.current = false;
457
+ setIsPlaying(false);
458
+ onAnimationEnded?.();
459
+ }
460
+ } else {
461
+ animationStoppedCountRef.current = 0;
462
+ }
463
+ lastAnimationTimeRef.current = currentTime;
464
+ }
465
+ }
466
+ }
467
+ if (controlsRef.current) {
468
+ controlsRef.current.update();
469
+ }
470
+ if (renderer && scene && camera) {
471
+ renderer.render(scene, camera);
472
+ }
473
+ };
474
+ animate();
475
+ setIsInitialized(true);
476
+ console.log("\u2705 [MMDPlayerEnhanced] \u573A\u666F\u521D\u59CB\u5316\u5B8C\u6210");
477
+ return () => {
478
+ window.removeEventListener("resize", handleResize);
479
+ if (animationIdRef.current) {
480
+ cancelAnimationFrame(animationIdRef.current);
481
+ }
482
+ if (renderer) {
483
+ renderer.dispose();
484
+ if (renderer.domElement && renderer.domElement.parentNode) {
485
+ container.removeChild(renderer.domElement);
486
+ }
487
+ }
488
+ if (controls) {
489
+ controls.dispose();
490
+ }
491
+ };
492
+ }, [stage]);
493
+ const clearOldResources = () => {
494
+ console.log("\u{1F9F9} [MMDPlayerEnhanced] \u5F00\u59CB\u6E05\u9664\u65E7\u8D44\u6E90");
495
+ if (!sceneRef.current) return;
496
+ if (isPlayingRef.current) {
497
+ isPlayingRef.current = false;
498
+ setIsPlaying(false);
499
+ }
500
+ if (audioRef.current) {
501
+ audioRef.current.pause();
502
+ audioRef.current.currentTime = 0;
503
+ audioRef.current = null;
504
+ }
505
+ if (helperRef.current) {
506
+ helperRef.current = null;
507
+ }
508
+ const objectsToRemove = [];
509
+ sceneRef.current.traverse((child) => {
510
+ if (child.type === "SkinnedMesh" || child.isSkinnedMesh) {
511
+ objectsToRemove.push(child);
512
+ }
513
+ if (child.type === "Mesh" && child !== sceneRef.current) {
514
+ objectsToRemove.push(child);
515
+ }
516
+ });
517
+ objectsToRemove.forEach((obj) => {
518
+ if (obj.parent) {
519
+ obj.parent.remove(obj);
520
+ }
521
+ if (obj.geometry) {
522
+ obj.geometry.dispose();
523
+ }
524
+ if (obj.material) {
525
+ if (Array.isArray(obj.material)) {
526
+ obj.material.forEach((mat) => mat.dispose());
527
+ } else {
528
+ obj.material.dispose();
529
+ }
530
+ }
531
+ });
532
+ clockRef.current = new THREE2.Clock();
533
+ vmdDataRef.current = null;
534
+ console.log(`\u2705 [MMDPlayerEnhanced] \u5DF2\u6E05\u9664 ${objectsToRemove.length} \u4E2A\u65E7\u5BF9\u8C61`);
535
+ };
536
+ useEffect(() => {
537
+ console.log("\u{1F4E6} [MMDPlayerEnhanced] \u8D44\u6E90\u52A0\u8F7D useEffect \u89E6\u53D1");
538
+ console.log("\u{1F50D} [MMDPlayerEnhanced] sceneRef.current:", sceneRef.current);
539
+ console.log("\u{1F50D} [MMDPlayerEnhanced] cameraRef.current:", cameraRef.current);
540
+ console.log("\u{1F50D} [MMDPlayerEnhanced] isLoadedRef.current:", isLoadedRef.current);
541
+ if (!sceneRef.current || !cameraRef.current) {
542
+ console.warn("\u26A0\uFE0F [MMDPlayerEnhanced] \u573A\u666F\u6216\u76F8\u673A\u672A\u521D\u59CB\u5316\uFF0C\u8DF3\u8FC7\u8D44\u6E90\u52A0\u8F7D");
543
+ return;
544
+ }
545
+ if (isLoadedRef.current) {
546
+ console.log("\u26A0\uFE0F [MMDPlayerEnhanced] \u8D44\u6E90\u5DF2\u52A0\u8F7D\uFF0C\u8DF3\u8FC7\u91CD\u590D\u52A0\u8F7D");
547
+ return;
548
+ }
549
+ console.log("\u2705 [MMDPlayerEnhanced] \u573A\u666F\u548C\u76F8\u673A\u5DF2\u5C31\u7EEA\uFF0C\u5F00\u59CB\u52A0\u8F7D\u8D44\u6E90");
550
+ clearOldResources();
551
+ isLoadedRef.current = true;
552
+ const loadMMD = async () => {
553
+ try {
554
+ setLoading(true);
555
+ setLoadingProgress(0);
556
+ animationDurationRef.current = 0;
557
+ hasAudioRef.current = false;
558
+ animationEndedFiredRef.current = false;
559
+ lastAnimationTimeRef.current = 0;
560
+ animationStoppedCountRef.current = 0;
561
+ if (stage?.enablePhysics !== false) {
562
+ const ammoScriptPath = stage?.ammoPath || "/mikutalking/libs/ammo.wasm.js";
563
+ const ammoWasmPath = stage?.ammoWasmPath || "/mikutalking/libs/";
564
+ console.log("\u{1F527} [MMDPlayerEnhanced] \u68C0\u6D4B\u5230\u542F\u7528\u7269\u7406\uFF0C\u5F00\u59CB\u52A0\u8F7D Ammo.js");
565
+ console.log("\u{1F4C2} [MMDPlayerEnhanced] Ammo \u811A\u672C\u8DEF\u5F84:", ammoScriptPath);
566
+ console.log("\u{1F4C2} [MMDPlayerEnhanced] Ammo WASM \u8DEF\u5F84:", ammoWasmPath);
567
+ setLoadingProgress(5);
568
+ await loadAmmo({
569
+ scriptPath: ammoScriptPath,
570
+ wasmBasePath: ammoWasmPath
571
+ });
572
+ console.log("\u2705 [MMDPlayerEnhanced] Ammo.js \u52A0\u8F7D\u5B8C\u6210");
573
+ }
574
+ const loader = new MMDLoader();
575
+ const helper = new MMDAnimationHelper();
576
+ helperRef.current = helper;
577
+ setLoadingProgress(20);
578
+ console.log("\u{1F3AD} \u5F00\u59CB\u52A0\u8F7D\u6A21\u578B:", currentResources.modelPath);
579
+ const mesh = await new Promise((resolve, reject) => {
580
+ loader.load(
581
+ currentResources.modelPath,
582
+ (object) => {
583
+ console.log("\u2705 \u6A21\u578B\u52A0\u8F7D\u6210\u529F");
584
+ resolve(object);
585
+ },
586
+ (progress) => {
587
+ if (progress.total > 0) {
588
+ const percent = progress.loaded / progress.total * 30 + 20;
589
+ setLoadingProgress(Math.min(percent, 50));
590
+ }
591
+ },
592
+ (error2) => {
593
+ console.error("\u274C \u6A21\u578B\u52A0\u8F7D\u5931\u8D25:", error2);
594
+ reject(error2);
595
+ }
596
+ );
597
+ });
598
+ if (!sceneRef.current) {
599
+ throw new Error("\u573A\u666F\u672A\u521D\u59CB\u5316");
600
+ }
601
+ sceneRef.current.add(mesh);
602
+ if (currentResources.stageModelPath) {
603
+ console.log("\u{1F3F0} \u5F00\u59CB\u52A0\u8F7D\u573A\u666F\u6A21\u578B:", currentResources.stageModelPath);
604
+ const stageMesh = await new Promise((resolve, reject) => {
605
+ loader.load(
606
+ currentResources.stageModelPath,
607
+ (object) => {
608
+ console.log("\u2705 \u573A\u666F\u6A21\u578B\u52A0\u8F7D\u6210\u529F");
609
+ resolve(object);
610
+ },
611
+ void 0,
612
+ (error2) => {
613
+ console.error("\u274C \u573A\u666F\u6A21\u578B\u52A0\u8F7D\u5931\u8D25:", error2);
614
+ reject(error2);
615
+ }
616
+ );
617
+ });
618
+ sceneRef.current.add(stageMesh);
619
+ }
620
+ if (currentResources.backgroundPath && sceneRef.current) {
621
+ console.log("\u{1F5BC}\uFE0F \u5F00\u59CB\u52A0\u8F7D\u80CC\u666F\u56FE\u7247:", currentResources.backgroundPath);
622
+ const textureLoader = new THREE2.TextureLoader();
623
+ const backgroundTexture = await new Promise((resolve, reject) => {
624
+ textureLoader.load(
625
+ currentResources.backgroundPath,
626
+ (texture) => resolve(texture),
627
+ void 0,
628
+ (err) => reject(err)
629
+ );
630
+ });
631
+ backgroundTexture.colorSpace = THREE2.SRGBColorSpace;
632
+ if (stage?.backgroundType === "skybox") {
633
+ backgroundTexture.mapping = THREE2.EquirectangularReflectionMapping;
634
+ sceneRef.current.background = backgroundTexture;
635
+ sceneRef.current.environment = backgroundTexture;
636
+ } else if (stage?.backgroundType === "image") {
637
+ sceneRef.current.background = backgroundTexture;
638
+ } else {
639
+ sceneRef.current.background = backgroundTexture;
640
+ }
641
+ console.log("\u2705 \u80CC\u666F\u56FE\u7247\u52A0\u8F7D\u6210\u529F");
642
+ }
643
+ let vmd = null;
644
+ let cameraVmd = null;
645
+ if (currentResources.motionPath) {
646
+ setLoadingProgress(60);
647
+ console.log("\u{1F483} \u5F00\u59CB\u52A0\u8F7D\u52A8\u4F5C:", currentResources.motionPath);
648
+ vmd = await new Promise((resolve, reject) => {
649
+ loader.loadAnimation(
650
+ currentResources.motionPath,
651
+ mesh,
652
+ (vmdObject) => {
653
+ console.log("\u2705 \u52A8\u4F5C\u52A0\u8F7D\u6210\u529F");
654
+ resolve(vmdObject);
655
+ },
656
+ (progress) => {
657
+ if (progress.total > 0) {
658
+ const percent = progress.loaded / progress.total * 20 + 60;
659
+ setLoadingProgress(Math.min(percent, 80));
660
+ }
661
+ },
662
+ (error2) => {
663
+ console.error("\u274C \u52A8\u4F5C\u52A0\u8F7D\u5931\u8D25:", error2);
664
+ reject(error2);
665
+ }
666
+ );
667
+ });
668
+ helper.add(mesh, {
669
+ animation: vmd,
670
+ physics: stage?.enablePhysics !== false
671
+ });
672
+ if (vmd) {
673
+ let maxDuration = 0;
674
+ if (vmd.duration !== void 0) {
675
+ maxDuration = vmd.duration;
676
+ } else if (Array.isArray(vmd) && vmd.length > 0 && vmd[0].duration !== void 0) {
677
+ maxDuration = vmd[0].duration;
678
+ } else if (vmd.clip && vmd.clip.duration !== void 0) {
679
+ maxDuration = vmd.clip.duration;
680
+ }
681
+ if (maxDuration > 0) {
682
+ animationDurationRef.current = maxDuration;
683
+ console.log("\u23F1\uFE0F \u52A8\u753B\u65F6\u957F:", maxDuration, "\u79D2");
684
+ } else {
685
+ console.warn("\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6\u52A8\u753B\u65F6\u957F\uFF0C\u5C06\u65E0\u6CD5\u81EA\u52A8\u68C0\u6D4B\u52A8\u753B\u7ED3\u675F");
686
+ console.log("\u{1F4CB} VMD \u6570\u636E\u7ED3\u6784:", vmd);
687
+ }
688
+ }
689
+ } else {
690
+ helper.add(mesh, { physics: stage?.enablePhysics !== false });
691
+ }
692
+ if (currentResources.cameraPath && cameraRef.current) {
693
+ setLoadingProgress(80);
694
+ console.log("\u{1F4F7} \u5F00\u59CB\u52A0\u8F7D\u955C\u5934:", currentResources.cameraPath);
695
+ cameraVmd = await new Promise((resolve, reject) => {
696
+ loader.loadAnimation(
697
+ currentResources.cameraPath,
698
+ cameraRef.current,
699
+ (vmdObject) => {
700
+ console.log("\u2705 \u955C\u5934\u52A0\u8F7D\u6210\u529F");
701
+ resolve(vmdObject);
702
+ },
703
+ void 0,
704
+ (error2) => {
705
+ console.error("\u274C \u955C\u5934\u52A0\u8F7D\u5931\u8D25:", error2);
706
+ reject(error2);
707
+ }
708
+ );
709
+ });
710
+ helper.add(cameraRef.current, { animation: cameraVmd });
711
+ }
712
+ if (currentResources.audioPath) {
713
+ setLoadingProgress(90);
714
+ console.log("\u{1F3B5} \u5F00\u59CB\u52A0\u8F7D\u97F3\u9891:", currentResources.audioPath);
715
+ const audio = new Audio(currentResources.audioPath);
716
+ audio.volume = 0.5;
717
+ audio.loop = loop;
718
+ audioRef.current = audio;
719
+ hasAudioRef.current = true;
720
+ audio.onended = () => {
721
+ if (!loop) {
722
+ setIsPlaying(false);
723
+ if (helperRef.current && sceneRef.current) {
724
+ const mesh2 = sceneRef.current.children.find(
725
+ (child) => child.type === "SkinnedMesh"
726
+ );
727
+ if (mesh2) {
728
+ helperRef.current.pose(mesh2, {});
729
+ }
730
+ }
731
+ }
732
+ onAudioEnded?.();
733
+ };
734
+ console.log("\u2705 \u97F3\u9891\u52A0\u8F7D\u6210\u529F");
735
+ }
736
+ setLoadingProgress(100);
737
+ setLoading(false);
738
+ vmdDataRef.current = {
739
+ mesh,
740
+ vmd,
741
+ cameraVmd
742
+ };
743
+ console.log("\u{1F389} \u6240\u6709\u8D44\u6E90\u52A0\u8F7D\u5B8C\u6210\uFF01");
744
+ if (shouldAutoPlayAfterReloadRef.current) {
745
+ shouldAutoPlayAfterReloadRef.current = false;
746
+ setTimeout(() => play(), 500);
747
+ } else if (autoPlay) {
748
+ setTimeout(() => play(), 500);
749
+ }
750
+ onLoad?.();
751
+ } catch (err) {
752
+ console.error("\u274C MMD\u52A0\u8F7D\u5931\u8D25:", err);
753
+ setError(err.message || "\u52A0\u8F7D\u5931\u8D25");
754
+ setLoading(false);
755
+ isLoadedRef.current = false;
756
+ onError?.(err);
757
+ }
758
+ };
759
+ loadMMD();
760
+ }, [currentResources, stage?.enablePhysics, autoPlay, loop, onLoad, onError, reloadTrigger]);
761
+ const play = () => {
762
+ console.log("\u{1F3AC} [play] \u51FD\u6570\u88AB\u8C03\u7528\uFF0CneedReset =", needReset);
763
+ if (!helperRef.current) return;
764
+ if (needReset && vmdDataRef.current && sceneRef.current && cameraRef.current) {
765
+ console.log("\u{1F504} \u68C0\u6D4B\u5230\u9700\u8981\u91CD\u7F6E\uFF0C\u91CD\u65B0\u521D\u59CB\u5316 helper\uFF08\u4FDD\u7559\u6A21\u578B\uFF09");
766
+ const { mesh, vmd, cameraVmd } = vmdDataRef.current;
767
+ const newHelper = new MMDAnimationHelper();
768
+ helperRef.current = newHelper;
769
+ clockRef.current = new THREE2.Clock();
770
+ if (vmd && typeof vmd === "object") {
771
+ console.log("\u{1F4E6} \u91CD\u65B0\u6DFB\u52A0\u6A21\u578B\u52A8\u753B\uFF0Cvmd:", vmd);
772
+ try {
773
+ newHelper.add(mesh, {
774
+ animation: vmd,
775
+ physics: stage?.enablePhysics !== false
776
+ });
777
+ } catch (error2) {
778
+ console.error("\u274C \u91CD\u65B0\u6DFB\u52A0\u6A21\u578B\u52A8\u753B\u5931\u8D25:", error2);
779
+ console.log("\u{1F4CB} vmd \u6570\u636E:", vmd);
780
+ newHelper.add(mesh, { physics: stage?.enablePhysics !== false });
781
+ }
782
+ } else {
783
+ newHelper.add(mesh, { physics: stage?.enablePhysics !== false });
784
+ }
785
+ if (cameraVmd && typeof cameraVmd === "object") {
786
+ console.log("\u{1F4F7} \u91CD\u65B0\u6DFB\u52A0\u76F8\u673A\u52A8\u753B");
787
+ try {
788
+ newHelper.add(cameraRef.current, { animation: cameraVmd });
789
+ } catch (error2) {
790
+ console.error("\u274C \u91CD\u65B0\u6DFB\u52A0\u76F8\u673A\u52A8\u753B\u5931\u8D25:", error2);
791
+ }
792
+ }
793
+ if (audioRef.current) {
794
+ audioRef.current.currentTime = 0;
795
+ }
796
+ setNeedReset(false);
797
+ console.log("\u2705 Helper \u91CD\u65B0\u521D\u59CB\u5316\u5B8C\u6210\uFF0C\u51C6\u5907\u4ECE\u7B2C\u4E00\u5E27\u64AD\u653E");
798
+ }
799
+ if (audioRef.current) {
800
+ audioRef.current.play();
801
+ }
802
+ helperRef.current.enable("animation", true);
803
+ helperRef.current.enable("ik", true);
804
+ helperRef.current.enable("grant", true);
805
+ helperRef.current.enable("physics", true);
806
+ if (!isPlaying) {
807
+ clockRef.current.start();
808
+ }
809
+ animationEndedFiredRef.current = false;
810
+ lastAnimationTimeRef.current = 0;
811
+ animationStoppedCountRef.current = 0;
812
+ isPlayingRef.current = true;
813
+ setIsPlaying(true);
814
+ console.log("\u25B6\uFE0F \u5F00\u59CB\u64AD\u653E\uFF08\u5305\u62EC\u76F8\u673A\u52A8\u753B\uFF09");
815
+ };
816
+ const pause = () => {
817
+ if (!helperRef.current) return;
818
+ if (audioRef.current) {
819
+ audioRef.current.pause();
820
+ }
821
+ clockRef.current.stop();
822
+ isPlayingRef.current = false;
823
+ setIsPlaying(false);
824
+ console.log("\u23F8\uFE0F \u6682\u505C\u64AD\u653E\uFF08\u5305\u62EC\u76F8\u673A\u52A8\u753B\uFF09");
825
+ };
826
+ const stop = () => {
827
+ if (!helperRef.current || !sceneRef.current) return;
828
+ isPlayingRef.current = false;
829
+ setIsPlaying(false);
830
+ if (audioRef.current) {
831
+ audioRef.current.pause();
832
+ audioRef.current.currentTime = 0;
833
+ }
834
+ clockRef.current.stop();
835
+ clockRef.current = new THREE2.Clock();
836
+ const mesh = sceneRef.current.children.find(
837
+ (child) => child.type === "SkinnedMesh" || child.isSkinnedMesh
838
+ );
839
+ if (mesh && mesh.skeleton) {
840
+ mesh.skeleton.pose();
841
+ }
842
+ if (cameraRef.current) {
843
+ const camPos = stage?.cameraPosition || { x: 0, y: 10, z: 30 };
844
+ const camTarget = stage?.cameraTarget || { x: 0, y: 10, z: 0 };
845
+ cameraRef.current.position.set(camPos.x, camPos.y, camPos.z);
846
+ if (controlsRef.current) {
847
+ controlsRef.current.target.set(camTarget.x, camTarget.y, camTarget.z);
848
+ controlsRef.current.update();
849
+ } else {
850
+ cameraRef.current.lookAt(camTarget.x, camTarget.y, camTarget.z);
851
+ }
852
+ }
853
+ setNeedReset(true);
854
+ console.log("\u23F9\uFE0F \u505C\u6B62\u64AD\u653E\u5E76\u91CD\u7F6E\u5230\u521D\u59CB\u72B6\u6001\uFF0CneedReset = true");
855
+ };
856
+ const handleResourceChange = (resourceId) => {
857
+ console.log("\u{1F504} [MMDPlayerEnhanced] \u5207\u6362\u8D44\u6E90:", resourceId);
858
+ if (isPlayingRef.current) {
859
+ isPlayingRef.current = false;
860
+ setIsPlaying(false);
861
+ }
862
+ if (audioRef.current) {
863
+ audioRef.current.pause();
864
+ audioRef.current.currentTime = 0;
865
+ }
866
+ setSelectedResourceId(resourceId);
867
+ isLoadedRef.current = false;
868
+ setNeedReset(false);
869
+ setReloadTrigger((prev) => prev + 1);
870
+ if (onResourceChange) {
871
+ onResourceChange(resourceId);
872
+ }
873
+ setShowSettings(false);
874
+ };
875
+ const handleSelectionChange = (type, id) => {
876
+ console.log(`\u{1F504} [MMDPlayerEnhanced] \u9009\u62E9${type}:`, id);
877
+ const wasPlaying = isPlayingRef.current;
878
+ if (isPlayingRef.current) {
879
+ isPlayingRef.current = false;
880
+ setIsPlaying(false);
881
+ }
882
+ if (audioRef.current) {
883
+ audioRef.current.pause();
884
+ audioRef.current.currentTime = 0;
885
+ }
886
+ if (type === "model") setSelectedModelId(id);
887
+ if (type === "motion") setSelectedMotionId(id);
888
+ if (type === "audio") setSelectedAudioId(id);
889
+ if (type === "camera") setSelectedCameraId(id);
890
+ if (type === "stageModel") setSelectedStageModelId(id);
891
+ if (type === "background") setSelectedBackgroundId(id);
892
+ isLoadedRef.current = false;
893
+ setNeedReset(false);
894
+ if (wasPlaying || autoPlay) {
895
+ shouldAutoPlayAfterReloadRef.current = true;
896
+ }
897
+ setReloadTrigger((prev) => prev + 1);
898
+ if (onSelectionChange) {
899
+ const newSelection = {
900
+ modelId: type === "model" ? id : selectedModelId,
901
+ motionId: type === "motion" ? id : selectedMotionId,
902
+ audioId: type === "audio" ? id : selectedAudioId,
903
+ cameraId: type === "camera" ? id : selectedCameraId,
904
+ stageModelId: type === "stageModel" ? id : selectedStageModelId,
905
+ backgroundId: type === "background" ? id : selectedBackgroundId
906
+ };
907
+ onSelectionChange(newSelection);
908
+ }
909
+ };
910
+ return /* @__PURE__ */ React2.createElement("div", { className: `relative h-full w-full ${className}`, style }, /* @__PURE__ */ React2.createElement("div", { ref: containerRef, className: "h-full w-full" }), loading && /* @__PURE__ */ React2.createElement("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black text-white" }, /* @__PURE__ */ React2.createElement("div", { className: "mb-4 text-2xl" }, "\u{1F3AD} \u52A0\u8F7DMMD\u8D44\u6E90\u4E2D..."), /* @__PURE__ */ React2.createElement("div", { className: "h-4 w-3/4 max-w-md overflow-hidden rounded-full bg-gray-700" }, /* @__PURE__ */ React2.createElement(
911
+ "div",
912
+ {
913
+ className: "h-full bg-gradient-to-r from-blue-500 to-purple-500 transition-all duration-300",
914
+ style: { width: `${loadingProgress}%` }
915
+ }
916
+ )), /* @__PURE__ */ React2.createElement("div", { className: "mt-2 text-sm text-gray-400" }, Math.round(loadingProgress), "%")), isInitialized && !loading && !error && /* @__PURE__ */ React2.createElement("div", { className: "absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2 rounded-full bg-black/50 px-4 py-2 backdrop-blur-md" }, !isPlaying ? /* @__PURE__ */ React2.createElement(
917
+ "button",
918
+ {
919
+ onClick: play,
920
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-green-500 text-xl text-white transition-colors hover:bg-green-600",
921
+ title: "\u64AD\u653E"
922
+ },
923
+ "\u25B6\uFE0F"
924
+ ) : /* @__PURE__ */ React2.createElement(
925
+ "button",
926
+ {
927
+ onClick: pause,
928
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-yellow-500 text-xl text-white transition-colors hover:bg-yellow-600",
929
+ title: "\u6682\u505C"
930
+ },
931
+ "\u23F8\uFE0F"
932
+ ), /* @__PURE__ */ React2.createElement(
933
+ "button",
934
+ {
935
+ onClick: stop,
936
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-red-500 text-xl text-white transition-colors hover:bg-red-600",
937
+ title: "\u505C\u6B62"
938
+ },
939
+ "\u23F9\uFE0F"
940
+ ), (resourcesList && resourcesList.length > 1 || resourceOptions) && /* @__PURE__ */ React2.createElement(
941
+ "button",
942
+ {
943
+ onClick: () => setShowSettings(true),
944
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-purple-500 text-xl text-white transition-colors hover:bg-purple-600",
945
+ title: "\u8BBE\u7F6E"
946
+ },
947
+ "\u2699\uFE0F"
948
+ )), showSettings && resourcesList && /* @__PURE__ */ React2.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm" }, /* @__PURE__ */ React2.createElement("div", { className: "max-h-[80vh] w-full max-w-md overflow-hidden rounded-2xl bg-gradient-to-br from-gray-900 to-black shadow-2xl" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between border-b border-white/10 px-6 py-4" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-xl font-bold text-white" }, "\u9009\u62E9\u8D44\u6E90"), /* @__PURE__ */ React2.createElement(
949
+ "button",
950
+ {
951
+ onClick: () => setShowSettings(false),
952
+ className: "text-2xl text-white/60 transition-colors hover:text-white"
953
+ },
954
+ "\u2715"
955
+ )), /* @__PURE__ */ React2.createElement("div", { className: "max-h-[60vh] overflow-y-auto p-4" }, resourcesList.map((item) => /* @__PURE__ */ React2.createElement(
956
+ "button",
957
+ {
958
+ key: item.id,
959
+ onClick: () => handleResourceChange(item.id),
960
+ className: `mb-3 w-full rounded-xl p-4 text-left transition-all ${selectedResourceId === item.id ? "bg-gradient-to-r from-purple-600 to-blue-600 shadow-lg" : "bg-white/5 hover:bg-white/10"}`
961
+ },
962
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React2.createElement("h4", { className: "font-semibold text-white" }, item.name), /* @__PURE__ */ React2.createElement("div", { className: "mt-1 flex flex-wrap gap-2 text-xs text-white/60" }, item.resources.modelPath && /* @__PURE__ */ React2.createElement("span", { className: "rounded bg-white/10 px-2 py-1" }, "\u6A21\u578B"), item.resources.motionPath && /* @__PURE__ */ React2.createElement("span", { className: "rounded bg-white/10 px-2 py-1" }, "\u52A8\u4F5C"), item.resources.cameraPath && /* @__PURE__ */ React2.createElement("span", { className: "rounded bg-white/10 px-2 py-1" }, "\u76F8\u673A"), item.resources.audioPath && /* @__PURE__ */ React2.createElement("span", { className: "rounded bg-white/10 px-2 py-1" }, "\u97F3\u9891"))), selectedResourceId === item.id && /* @__PURE__ */ React2.createElement("div", { className: "ml-4 text-2xl" }, "\u2713"))
963
+ ))))), showSettings && resourceOptions && /* @__PURE__ */ React2.createElement("div", { className: "absolute top-4 right-4 z-50 w-80 rounded-xl bg-black/90 backdrop-blur-md shadow-2xl border border-white/10" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between border-b border-white/10 px-4 py-3" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-bold text-white" }, "\u8D44\u6E90\u8BBE\u7F6E"), /* @__PURE__ */ React2.createElement(
964
+ "button",
965
+ {
966
+ onClick: () => setShowSettings(false),
967
+ className: "text-lg text-white/60 transition-colors hover:text-white"
968
+ },
969
+ "\u2715"
970
+ )), /* @__PURE__ */ React2.createElement("div", { className: "max-h-[70vh] overflow-y-auto p-4 space-y-2" }, resourceOptions.models && resourceOptions.models.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "rounded-lg bg-white/5 overflow-hidden" }, /* @__PURE__ */ React2.createElement(
971
+ "button",
972
+ {
973
+ onClick: () => setExpandedSection(expandedSection === "model" ? null : "model"),
974
+ className: "w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-white/10 transition-colors"
975
+ },
976
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "text-xs font-medium text-white/70" }, "\u6A21\u578B"), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-white font-medium" }, resourceOptions.models.find((m) => m.id === selectedModelId)?.name || "\u672A\u9009\u62E9")),
977
+ /* @__PURE__ */ React2.createElement("span", { className: `text-white/60 transition-transform ${expandedSection === "model" ? "rotate-180" : ""}` }, "\u25BC")
978
+ ), expandedSection === "model" && /* @__PURE__ */ React2.createElement("div", { className: "border-t border-white/10 p-2 space-y-1 max-h-60 overflow-y-auto" }, resourceOptions.models.map((model) => /* @__PURE__ */ React2.createElement(
979
+ "button",
980
+ {
981
+ key: model.id,
982
+ onClick: () => {
983
+ handleSelectionChange("model", model.id);
984
+ setExpandedSection(null);
985
+ },
986
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedModelId === model.id ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
987
+ },
988
+ model.name
989
+ )))), resourceOptions.motions && resourceOptions.motions.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "rounded-lg bg-white/5 overflow-hidden" }, /* @__PURE__ */ React2.createElement(
990
+ "button",
991
+ {
992
+ onClick: () => setExpandedSection(expandedSection === "motion" ? null : "motion"),
993
+ className: "w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-white/10 transition-colors"
994
+ },
995
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "text-xs font-medium text-white/70" }, "\u52A8\u4F5C"), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-white font-medium" }, selectedMotionId ? resourceOptions.motions.find((m) => m.id === selectedMotionId)?.name : "\u65E0")),
996
+ /* @__PURE__ */ React2.createElement("span", { className: `text-white/60 transition-transform ${expandedSection === "motion" ? "rotate-180" : ""}` }, "\u25BC")
997
+ ), expandedSection === "motion" && /* @__PURE__ */ React2.createElement("div", { className: "border-t border-white/10 p-2 space-y-1 max-h-60 overflow-y-auto" }, /* @__PURE__ */ React2.createElement(
998
+ "button",
999
+ {
1000
+ onClick: () => {
1001
+ handleSelectionChange("motion", "");
1002
+ setExpandedSection(null);
1003
+ },
1004
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedMotionId === "" ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1005
+ },
1006
+ "\u65E0"
1007
+ ), resourceOptions.motions.map((motion) => /* @__PURE__ */ React2.createElement(
1008
+ "button",
1009
+ {
1010
+ key: motion.id,
1011
+ onClick: () => {
1012
+ handleSelectionChange("motion", motion.id);
1013
+ setExpandedSection(null);
1014
+ },
1015
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedMotionId === motion.id ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1016
+ },
1017
+ motion.name
1018
+ )))), resourceOptions.audios && resourceOptions.audios.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "rounded-lg bg-white/5 overflow-hidden" }, /* @__PURE__ */ React2.createElement(
1019
+ "button",
1020
+ {
1021
+ onClick: () => setExpandedSection(expandedSection === "audio" ? null : "audio"),
1022
+ className: "w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-white/10 transition-colors"
1023
+ },
1024
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "text-xs font-medium text-white/70" }, "\u97F3\u4E50"), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-white font-medium" }, selectedAudioId ? resourceOptions.audios.find((a) => a.id === selectedAudioId)?.name : "\u65E0")),
1025
+ /* @__PURE__ */ React2.createElement("span", { className: `text-white/60 transition-transform ${expandedSection === "audio" ? "rotate-180" : ""}` }, "\u25BC")
1026
+ ), expandedSection === "audio" && /* @__PURE__ */ React2.createElement("div", { className: "border-t border-white/10 p-2 space-y-1 max-h-60 overflow-y-auto" }, /* @__PURE__ */ React2.createElement(
1027
+ "button",
1028
+ {
1029
+ onClick: () => {
1030
+ handleSelectionChange("audio", "");
1031
+ setExpandedSection(null);
1032
+ },
1033
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedAudioId === "" ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1034
+ },
1035
+ "\u65E0"
1036
+ ), resourceOptions.audios.map((audio) => /* @__PURE__ */ React2.createElement(
1037
+ "button",
1038
+ {
1039
+ key: audio.id,
1040
+ onClick: () => {
1041
+ handleSelectionChange("audio", audio.id);
1042
+ setExpandedSection(null);
1043
+ },
1044
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedAudioId === audio.id ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1045
+ },
1046
+ audio.name
1047
+ )))), resourceOptions.cameras && resourceOptions.cameras.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "rounded-lg bg-white/5 overflow-hidden" }, /* @__PURE__ */ React2.createElement(
1048
+ "button",
1049
+ {
1050
+ onClick: () => setExpandedSection(expandedSection === "camera" ? null : "camera"),
1051
+ className: "w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-white/10 transition-colors"
1052
+ },
1053
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "text-xs font-medium text-white/70" }, "\u76F8\u673A"), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-white font-medium" }, selectedCameraId ? resourceOptions.cameras.find((c) => c.id === selectedCameraId)?.name : "\u65E0")),
1054
+ /* @__PURE__ */ React2.createElement("span", { className: `text-white/60 transition-transform ${expandedSection === "camera" ? "rotate-180" : ""}` }, "\u25BC")
1055
+ ), expandedSection === "camera" && /* @__PURE__ */ React2.createElement("div", { className: "border-t border-white/10 p-2 space-y-1 max-h-60 overflow-y-auto" }, /* @__PURE__ */ React2.createElement(
1056
+ "button",
1057
+ {
1058
+ onClick: () => {
1059
+ handleSelectionChange("camera", "");
1060
+ setExpandedSection(null);
1061
+ },
1062
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedCameraId === "" ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1063
+ },
1064
+ "\u65E0"
1065
+ ), resourceOptions.cameras.map((camera) => /* @__PURE__ */ React2.createElement(
1066
+ "button",
1067
+ {
1068
+ key: camera.id,
1069
+ onClick: () => {
1070
+ handleSelectionChange("camera", camera.id);
1071
+ setExpandedSection(null);
1072
+ },
1073
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedCameraId === camera.id ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1074
+ },
1075
+ camera.name
1076
+ )))), resourceOptions.stageModels && resourceOptions.stageModels.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "rounded-lg bg-white/5 overflow-hidden" }, /* @__PURE__ */ React2.createElement(
1077
+ "button",
1078
+ {
1079
+ onClick: () => setExpandedSection(expandedSection === "stageModel" ? null : "stageModel"),
1080
+ className: "w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-white/10 transition-colors"
1081
+ },
1082
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "text-xs font-medium text-white/70" }, "\u573A\u666F"), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-white font-medium" }, selectedStageModelId ? resourceOptions.stageModels.find((s) => s.id === selectedStageModelId)?.name : "\u65E0")),
1083
+ /* @__PURE__ */ React2.createElement("span", { className: `text-white/60 transition-transform ${expandedSection === "stageModel" ? "rotate-180" : ""}` }, "\u25BC")
1084
+ ), expandedSection === "stageModel" && /* @__PURE__ */ React2.createElement("div", { className: "border-t border-white/10 p-2 space-y-1 max-h-60 overflow-y-auto" }, /* @__PURE__ */ React2.createElement(
1085
+ "button",
1086
+ {
1087
+ onClick: () => {
1088
+ handleSelectionChange("stageModel", "");
1089
+ setExpandedSection(null);
1090
+ },
1091
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedStageModelId === "" ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1092
+ },
1093
+ "\u65E0"
1094
+ ), resourceOptions.stageModels.map((stageModel) => /* @__PURE__ */ React2.createElement(
1095
+ "button",
1096
+ {
1097
+ key: stageModel.id,
1098
+ onClick: () => {
1099
+ handleSelectionChange("stageModel", stageModel.id);
1100
+ setExpandedSection(null);
1101
+ },
1102
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedStageModelId === stageModel.id ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1103
+ },
1104
+ stageModel.name
1105
+ )))), resourceOptions.backgrounds && resourceOptions.backgrounds.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "rounded-lg bg-white/5 overflow-hidden" }, /* @__PURE__ */ React2.createElement(
1106
+ "button",
1107
+ {
1108
+ onClick: () => setExpandedSection(expandedSection === "background" ? null : "background"),
1109
+ className: "w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-white/10 transition-colors"
1110
+ },
1111
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "text-xs font-medium text-white/70" }, "\u80CC\u666F"), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-white font-medium" }, selectedBackgroundId ? resourceOptions.backgrounds.find((b) => b.id === selectedBackgroundId)?.name : "\u65E0")),
1112
+ /* @__PURE__ */ React2.createElement("span", { className: `text-white/60 transition-transform ${expandedSection === "background" ? "rotate-180" : ""}` }, "\u25BC")
1113
+ ), expandedSection === "background" && /* @__PURE__ */ React2.createElement("div", { className: "border-t border-white/10 p-2 space-y-1 max-h-60 overflow-y-auto" }, /* @__PURE__ */ React2.createElement(
1114
+ "button",
1115
+ {
1116
+ onClick: () => {
1117
+ handleSelectionChange("background", "");
1118
+ setExpandedSection(null);
1119
+ },
1120
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedBackgroundId === "" ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1121
+ },
1122
+ "\u65E0"
1123
+ ), resourceOptions.backgrounds.map((background) => /* @__PURE__ */ React2.createElement(
1124
+ "button",
1125
+ {
1126
+ key: background.id,
1127
+ onClick: () => {
1128
+ handleSelectionChange("background", background.id);
1129
+ setExpandedSection(null);
1130
+ },
1131
+ className: `w-full rounded px-3 py-2 text-left text-sm transition-all ${selectedBackgroundId === background.id ? "bg-purple-600 text-white font-medium" : "text-white/80 hover:bg-white/10"}`
1132
+ },
1133
+ background.name
1134
+ )))))));
1135
+ };
1136
+ var MMDPlaylist = ({
1137
+ playlist,
1138
+ stage,
1139
+ defaultNodeIndex = 0,
1140
+ className,
1141
+ style,
1142
+ onLoad,
1143
+ onError,
1144
+ onNodeChange,
1145
+ onPlaylistComplete
1146
+ }) => {
1147
+ console.log("\u{1F3AC} [MMDPlaylist] \u7EC4\u4EF6\u521D\u59CB\u5316");
1148
+ console.log("\u{1F4CB} [MMDPlaylist] \u64AD\u653E\u5217\u8868:", playlist.name, "\u8282\u70B9\u6570:", playlist.nodes.length);
1149
+ const [currentNodeIndex, setCurrentNodeIndex] = useState(defaultNodeIndex);
1150
+ const [showSettings, setShowSettings] = useState(false);
1151
+ const [preloadedNodes, setPreloadedNodes] = useState(/* @__PURE__ */ new Set());
1152
+ const [isPreloading, setIsPreloading] = useState(true);
1153
+ const [preloadProgress, setPreloadProgress] = useState(0);
1154
+ const [editableNodes, setEditableNodes] = useState(playlist.nodes);
1155
+ const currentNodeIndexRef = useRef(defaultNodeIndex);
1156
+ const isAutoSwitchRef = useRef(false);
1157
+ const playerRefsMap = useRef(/* @__PURE__ */ new Map());
1158
+ useEffect(() => {
1159
+ currentNodeIndexRef.current = currentNodeIndex;
1160
+ }, [currentNodeIndex]);
1161
+ const currentNode = editableNodes[currentNodeIndex];
1162
+ if (!currentNode) {
1163
+ console.error("\u274C [MMDPlaylist] \u65E0\u6548\u7684\u8282\u70B9\u7D22\u5F15:", currentNodeIndex);
1164
+ return /* @__PURE__ */ React2.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, /* @__PURE__ */ React2.createElement("p", null, "\u64AD\u653E\u5217\u8868\u8282\u70B9\u7D22\u5F15\u65E0\u6548"));
1165
+ }
1166
+ console.log("\u{1F3AF} [MMDPlaylist] \u5F53\u524D\u8282\u70B9:", currentNode.name, "\u7D22\u5F15:", currentNodeIndex);
1167
+ useEffect(() => {
1168
+ console.log(`\u{1F504} [MMDPlaylist] \u8282\u70B9\u5207\u6362: ${currentNodeIndex} - ${currentNode.name}`);
1169
+ onNodeChange?.(currentNodeIndex, currentNode);
1170
+ if (!isPreloading && (isAutoSwitchRef.current || playlist.autoPlay)) {
1171
+ console.log(`\u25B6\uFE0F [MMDPlaylist] \u51C6\u5907\u64AD\u653E\u8282\u70B9 ${currentNodeIndex}`);
1172
+ if (!preloadedNodes.has(currentNodeIndex)) {
1173
+ console.warn(`\u26A0\uFE0F [MMDPlaylist] \u8282\u70B9 ${currentNodeIndex} \u5C1A\u672A\u9884\u52A0\u8F7D\u5B8C\u6210\uFF0C\u7B49\u5F85...`);
1174
+ return;
1175
+ }
1176
+ requestAnimationFrame(() => {
1177
+ const playerElement = playerRefsMap.current.get(currentNodeIndex);
1178
+ if (playerElement) {
1179
+ const playButton = playerElement.querySelector('button[title="\u64AD\u653E"]');
1180
+ if (playButton) {
1181
+ console.log(`\u{1F3AC} [MMDPlaylist] \u89E6\u53D1\u8282\u70B9 ${currentNodeIndex} \u64AD\u653E`);
1182
+ playButton.click();
1183
+ } else {
1184
+ console.warn(`\u26A0\uFE0F [MMDPlaylist] \u672A\u627E\u5230\u8282\u70B9 ${currentNodeIndex} \u7684\u64AD\u653E\u6309\u94AE`);
1185
+ }
1186
+ } else {
1187
+ console.warn(`\u26A0\uFE0F [MMDPlaylist] \u672A\u627E\u5230\u8282\u70B9 ${currentNodeIndex} \u7684 DOM \u5143\u7D20`);
1188
+ }
1189
+ });
1190
+ }
1191
+ }, [currentNodeIndex, currentNode, onNodeChange, isPreloading, playlist.autoPlay, preloadedNodes]);
1192
+ const handleNodePreloaded = (nodeIndex) => {
1193
+ console.log(`\u2705 [MMDPlaylist] \u8282\u70B9 ${nodeIndex} \u9884\u52A0\u8F7D\u5B8C\u6210`);
1194
+ setPreloadedNodes((prev) => {
1195
+ const newSet = new Set(prev);
1196
+ newSet.add(nodeIndex);
1197
+ return newSet;
1198
+ });
1199
+ };
1200
+ useEffect(() => {
1201
+ if (preloadedNodes.size === editableNodes.length) {
1202
+ console.log("\u{1F389} [MMDPlaylist] \u6240\u6709\u8282\u70B9\u9884\u52A0\u8F7D\u5B8C\u6210");
1203
+ setIsPreloading(false);
1204
+ onLoad?.();
1205
+ } else {
1206
+ const progress = Math.round(preloadedNodes.size / editableNodes.length * 100);
1207
+ setPreloadProgress(progress);
1208
+ }
1209
+ }, [preloadedNodes, editableNodes.length, onLoad]);
1210
+ const handlePlaybackEnded = (nodeIndex) => {
1211
+ console.log(`\u{1F3B5} [MMDPlaylist] \u8282\u70B9 ${nodeIndex} \u64AD\u653E\u5B8C\u6210`);
1212
+ if (nodeIndex !== currentNodeIndexRef.current) {
1213
+ console.log(`\u26A0\uFE0F [MMDPlaylist] \u5FFD\u7565\u975E\u5F53\u524D\u8282\u70B9 ${nodeIndex} \u7684\u64AD\u653E\u7ED3\u675F\u4E8B\u4EF6\uFF08\u5F53\u524D: ${currentNodeIndexRef.current}\uFF09`);
1214
+ return;
1215
+ }
1216
+ const node = editableNodes[nodeIndex];
1217
+ if (!node) return;
1218
+ if (node.loop) {
1219
+ console.log("\u{1F501} [MMDPlaylist] \u5F53\u524D\u8282\u70B9\u5FAA\u73AF\u64AD\u653E");
1220
+ return;
1221
+ }
1222
+ const isLastNode = nodeIndex === editableNodes.length - 1;
1223
+ if (!isLastNode) {
1224
+ console.log(`\u27A1\uFE0F [MMDPlaylist] \u5207\u6362\u5230\u4E0B\u4E00\u4E2A\u8282\u70B9: ${nodeIndex + 1}`);
1225
+ isAutoSwitchRef.current = true;
1226
+ setCurrentNodeIndex(nodeIndex + 1);
1227
+ return;
1228
+ }
1229
+ if (playlist.loop) {
1230
+ console.log("\u{1F501} [MMDPlaylist] \u64AD\u653E\u5217\u8868\u5FAA\u73AF\uFF0C\u56DE\u5230\u7B2C\u4E00\u4E2A\u8282\u70B9");
1231
+ isAutoSwitchRef.current = true;
1232
+ setCurrentNodeIndex(0);
1233
+ return;
1234
+ }
1235
+ console.log("\u2705 [MMDPlaylist] \u64AD\u653E\u5217\u8868\u64AD\u653E\u5B8C\u6210");
1236
+ onPlaylistComplete?.();
1237
+ };
1238
+ const playlistPrevious = () => {
1239
+ const newIndex = currentNodeIndex > 0 ? currentNodeIndex - 1 : editableNodes.length - 1;
1240
+ console.log(`\u2B05\uFE0F [MMDPlaylist] \u4E0A\u4E00\u4E2A\u8282\u70B9: ${newIndex}`);
1241
+ isAutoSwitchRef.current = false;
1242
+ setCurrentNodeIndex(newIndex);
1243
+ };
1244
+ const playlistNext = () => {
1245
+ const newIndex = currentNodeIndex < editableNodes.length - 1 ? currentNodeIndex + 1 : 0;
1246
+ console.log(`\u27A1\uFE0F [MMDPlaylist] \u4E0B\u4E00\u4E2A\u8282\u70B9: ${newIndex}`);
1247
+ isAutoSwitchRef.current = false;
1248
+ setCurrentNodeIndex(newIndex);
1249
+ };
1250
+ const playlistJumpTo = (index) => {
1251
+ if (index < 0 || index >= editableNodes.length) return;
1252
+ console.log(`\u{1F3AF} [MMDPlaylist] \u8DF3\u8F6C\u5230\u8282\u70B9: ${index}`);
1253
+ isAutoSwitchRef.current = false;
1254
+ setCurrentNodeIndex(index);
1255
+ };
1256
+ const handleDeleteNode = (index) => {
1257
+ if (editableNodes.length <= 1) {
1258
+ alert("\u64AD\u653E\u5217\u8868\u81F3\u5C11\u9700\u8981\u4FDD\u7559\u4E00\u4E2A\u8282\u70B9");
1259
+ return;
1260
+ }
1261
+ const newNodes = editableNodes.filter((_, i) => i !== index);
1262
+ setEditableNodes(newNodes);
1263
+ if (index < currentNodeIndex) {
1264
+ setCurrentNodeIndex(currentNodeIndex - 1);
1265
+ } else if (index === currentNodeIndex) {
1266
+ const newIndex = Math.max(0, currentNodeIndex - 1);
1267
+ setCurrentNodeIndex(newIndex);
1268
+ }
1269
+ console.log(`\u{1F5D1}\uFE0F [MMDPlaylist] \u5220\u9664\u8282\u70B9 ${index}`);
1270
+ };
1271
+ const handleMoveNodeUp = (index) => {
1272
+ if (index === 0) return;
1273
+ const newNodes = [...editableNodes];
1274
+ const temp = newNodes[index - 1];
1275
+ newNodes[index - 1] = newNodes[index];
1276
+ newNodes[index] = temp;
1277
+ setEditableNodes(newNodes);
1278
+ if (currentNodeIndex === index) {
1279
+ setCurrentNodeIndex(index - 1);
1280
+ } else if (currentNodeIndex === index - 1) {
1281
+ setCurrentNodeIndex(index);
1282
+ }
1283
+ console.log(`\u2B06\uFE0F [MMDPlaylist] \u8282\u70B9 ${index} \u4E0A\u79FB`);
1284
+ };
1285
+ const handleMoveNodeDown = (index) => {
1286
+ if (index === editableNodes.length - 1) return;
1287
+ const newNodes = [...editableNodes];
1288
+ const temp = newNodes[index];
1289
+ newNodes[index] = newNodes[index + 1];
1290
+ newNodes[index + 1] = temp;
1291
+ setEditableNodes(newNodes);
1292
+ if (currentNodeIndex === index) {
1293
+ setCurrentNodeIndex(index + 1);
1294
+ } else if (currentNodeIndex === index + 1) {
1295
+ setCurrentNodeIndex(index);
1296
+ }
1297
+ console.log(`\u2B07\uFE0F [MMDPlaylist] \u8282\u70B9 ${index} \u4E0B\u79FB`);
1298
+ };
1299
+ const shouldAutoPlayInitial = playlist.autoPlay && currentNodeIndex === defaultNodeIndex && !isPreloading;
1300
+ return /* @__PURE__ */ React2.createElement("div", { className: `relative ${className || ""}`, style }, editableNodes.map((node, index) => {
1301
+ return /* @__PURE__ */ React2.createElement(
1302
+ "div",
1303
+ {
1304
+ key: `player-${node.id}-${index}`,
1305
+ ref: (el) => {
1306
+ if (el) {
1307
+ playerRefsMap.current.set(index, el);
1308
+ }
1309
+ },
1310
+ className: "absolute inset-0",
1311
+ style: {
1312
+ visibility: index === currentNodeIndex ? "visible" : "hidden",
1313
+ zIndex: index === currentNodeIndex ? 1 : 0
1314
+ }
1315
+ },
1316
+ /* @__PURE__ */ React2.createElement(
1317
+ MMDPlayerEnhanced,
1318
+ {
1319
+ resources: node.resources,
1320
+ stage,
1321
+ autoPlay: index === currentNodeIndex && shouldAutoPlayInitial,
1322
+ loop: node.loop || false,
1323
+ className: "h-full w-full",
1324
+ onLoad: () => {
1325
+ handleNodePreloaded(index);
1326
+ },
1327
+ onError: (error) => {
1328
+ console.error(`\u274C [MMDPlaylist] \u8282\u70B9 ${index} \u52A0\u8F7D\u5931\u8D25:`, error);
1329
+ if (index === currentNodeIndex) {
1330
+ onError?.(error);
1331
+ }
1332
+ },
1333
+ onAudioEnded: () => handlePlaybackEnded(index),
1334
+ onAnimationEnded: () => handlePlaybackEnded(index)
1335
+ }
1336
+ )
1337
+ );
1338
+ }), isPreloading && /* @__PURE__ */ React2.createElement("div", { className: "absolute inset-0 z-50 flex flex-col items-center justify-center bg-black/80 backdrop-blur-sm" }, /* @__PURE__ */ React2.createElement("div", { className: "text-center" }, /* @__PURE__ */ React2.createElement("div", { className: "mb-4 text-2xl font-bold text-white" }, "\u6B63\u5728\u9884\u52A0\u8F7D\u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React2.createElement("div", { className: "mb-2 text-lg text-white/80" }, preloadedNodes.size, " / ", editableNodes.length, " \u8282\u70B9"), /* @__PURE__ */ React2.createElement("div", { className: "h-2 w-64 overflow-hidden rounded-full bg-white/20" }, /* @__PURE__ */ React2.createElement(
1339
+ "div",
1340
+ {
1341
+ className: "h-full bg-gradient-to-r from-purple-500 to-blue-500 transition-all duration-300",
1342
+ style: { width: `${preloadProgress}%` }
1343
+ }
1344
+ )), /* @__PURE__ */ React2.createElement("div", { className: "mt-4 text-sm text-white/60" }, "\u9884\u52A0\u8F7D\u6240\u6709\u8D44\u6E90\u540E\uFF0C\u5207\u6362\u8282\u70B9\u5C06\u65E0\u9700\u7B49\u5F85"))), !isPreloading && /* @__PURE__ */ React2.createElement("div", { className: "absolute bottom-4 right-4 z-10 flex gap-2" }, editableNodes.length > 1 && /* @__PURE__ */ React2.createElement(
1345
+ "button",
1346
+ {
1347
+ onClick: playlistPrevious,
1348
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-blue-500/90 text-xl text-white shadow-lg backdrop-blur-md transition-all hover:bg-blue-600 hover:scale-110",
1349
+ title: "\u4E0A\u4E00\u4E2A\u8282\u70B9"
1350
+ },
1351
+ "\u23EE\uFE0F"
1352
+ ), /* @__PURE__ */ React2.createElement(
1353
+ "button",
1354
+ {
1355
+ onClick: () => setShowSettings(true),
1356
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-purple-500/90 text-xl text-white shadow-lg backdrop-blur-md transition-all hover:bg-purple-600 hover:scale-110",
1357
+ title: "\u64AD\u653E\u5217\u8868\u8BBE\u7F6E"
1358
+ },
1359
+ "\u2699\uFE0F"
1360
+ ), editableNodes.length > 1 && /* @__PURE__ */ React2.createElement(
1361
+ "button",
1362
+ {
1363
+ onClick: playlistNext,
1364
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-blue-500/90 text-xl text-white shadow-lg backdrop-blur-md transition-all hover:bg-blue-600 hover:scale-110",
1365
+ title: "\u4E0B\u4E00\u4E2A\u8282\u70B9"
1366
+ },
1367
+ "\u23ED\uFE0F"
1368
+ )), !isPreloading && /* @__PURE__ */ React2.createElement("div", { className: "absolute left-4 top-4 z-10 rounded-lg bg-black/50 px-4 py-2 backdrop-blur-md" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "text-sm font-bold text-white/60" }, currentNodeIndex + 1, "/", editableNodes.length), /* @__PURE__ */ React2.createElement("span", { className: "text-sm font-medium text-white" }, currentNode.name), currentNode.loop && /* @__PURE__ */ React2.createElement("span", { className: "rounded bg-white/20 px-2 py-0.5 text-xs text-white" }, "\u{1F501}"))), showSettings && /* @__PURE__ */ React2.createElement(
1369
+ "div",
1370
+ {
1371
+ className: "absolute inset-0 z-[100] flex items-start justify-end bg-black/40",
1372
+ onClick: () => setShowSettings(false)
1373
+ },
1374
+ /* @__PURE__ */ React2.createElement(
1375
+ "div",
1376
+ {
1377
+ className: "relative m-4 flex w-full max-w-md flex-col overflow-hidden rounded-xl bg-gradient-to-br from-gray-900 to-black shadow-2xl border border-white/20",
1378
+ style: { maxHeight: "calc(100vh - 2rem)" },
1379
+ onClick: (e) => e.stopPropagation()
1380
+ },
1381
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between border-b border-white/10 bg-gradient-to-r from-purple-900/50 to-blue-900/50 px-4 py-3 flex-shrink-0" }, /* @__PURE__ */ React2.createElement("h3", { className: "flex items-center gap-2 text-base font-bold text-white" }, "\u2699\uFE0F \u64AD\u653E\u5217\u8868\u914D\u7F6E"), /* @__PURE__ */ React2.createElement(
1382
+ "button",
1383
+ {
1384
+ onClick: () => setShowSettings(false),
1385
+ className: "text-xl text-white/60 transition-colors hover:text-white"
1386
+ },
1387
+ "\u2715"
1388
+ )),
1389
+ /* @__PURE__ */ React2.createElement("div", { className: "flex-1 overflow-y-auto p-4", style: { scrollbarWidth: "thin", scrollbarColor: "rgba(255,255,255,0.2) transparent" } }, /* @__PURE__ */ React2.createElement("div", { className: "mb-3 rounded-lg bg-gradient-to-br from-indigo-900/30 to-purple-900/30 p-3 border border-white/10" }, /* @__PURE__ */ React2.createElement("h4", { className: "text-sm font-semibold text-white mb-2 flex items-center gap-2" }, "\u{1F4CB} \u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1 text-xs" }, /* @__PURE__ */ React2.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React2.createElement("span", { className: "text-white/60" }, "\u540D\u79F0\uFF1A"), /* @__PURE__ */ React2.createElement("span", { className: "text-white font-medium" }, playlist.name)), /* @__PURE__ */ React2.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React2.createElement("span", { className: "text-white/60" }, "\u8282\u70B9\u6570\uFF1A"), /* @__PURE__ */ React2.createElement("span", { className: "text-white font-medium" }, editableNodes.length)), /* @__PURE__ */ React2.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React2.createElement("span", { className: "text-white/60" }, "\u5FAA\u73AF\uFF1A"), /* @__PURE__ */ React2.createElement("span", { className: "text-white font-medium" }, playlist.loop ? "\u662F" : "\u5426")))), /* @__PURE__ */ React2.createElement("div", { className: "mb-3 rounded-lg bg-gradient-to-br from-blue-900/30 to-cyan-900/30 p-3 border border-white/10" }, /* @__PURE__ */ React2.createElement("h4", { className: "text-sm font-semibold text-white mb-2 flex items-center gap-2" }, "\u{1F3AF} \u5F53\u524D\u8282\u70B9"), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1 text-xs" }, /* @__PURE__ */ React2.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React2.createElement("span", { className: "text-white/60" }, "\u540D\u79F0\uFF1A"), /* @__PURE__ */ React2.createElement("span", { className: "text-white font-medium truncate ml-2" }, currentNode.name)), /* @__PURE__ */ React2.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React2.createElement("span", { className: "text-white/60" }, "\u4F4D\u7F6E\uFF1A"), /* @__PURE__ */ React2.createElement("span", { className: "text-white font-medium" }, currentNodeIndex + 1, " / ", editableNodes.length)), currentNode.resources.audioPath && /* @__PURE__ */ React2.createElement("div", { className: "text-white/80 mt-1" }, "\u{1F3B5} \u6709\u97F3\u4E50"), currentNode.resources.cameraPath && /* @__PURE__ */ React2.createElement("div", { className: "text-white/80" }, "\u{1F4F7} \u6709\u76F8\u673A"))), /* @__PURE__ */ React2.createElement("div", { className: "rounded-lg bg-gradient-to-br from-gray-800/50 to-gray-900/50 border border-white/10 p-3" }, /* @__PURE__ */ React2.createElement("h4", { className: "mb-2 flex items-center gap-2 text-sm font-semibold text-white" }, "\u{1F4DD} \u8282\u70B9\u7BA1\u7406"), /* @__PURE__ */ React2.createElement("div", { className: "max-h-64 space-y-2 overflow-y-auto pr-1", style: { scrollbarWidth: "thin", scrollbarColor: "rgba(255,255,255,0.2) transparent" } }, editableNodes.map((node, index) => /* @__PURE__ */ React2.createElement(
1390
+ "div",
1391
+ {
1392
+ key: `${node.id}-${index}`,
1393
+ className: `rounded-md p-2 transition-all text-xs ${currentNodeIndex === index ? "bg-gradient-to-r from-purple-600/50 to-blue-600/50 border border-purple-400/50" : "bg-white/5 hover:bg-white/10 border border-white/10"}`
1394
+ },
1395
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-1 mb-1" }, /* @__PURE__ */ React2.createElement("span", { className: "text-xs font-bold text-white/40" }, "#", index + 1), /* @__PURE__ */ React2.createElement("h5", { className: "font-semibold text-white text-xs truncate" }, node.name), currentNodeIndex === index && /* @__PURE__ */ React2.createElement("span", { className: "rounded bg-green-500/30 px-1 py-0.5 text-[10px] text-green-300 flex-shrink-0" }, "\u25B6\uFE0F")), /* @__PURE__ */ React2.createElement("div", { className: "flex flex-wrap gap-1 text-[10px] text-white/60" }, node.resources.modelPath && /* @__PURE__ */ React2.createElement("span", null, "\u{1F464}"), node.resources.motionPath && /* @__PURE__ */ React2.createElement("span", null, "\u{1F483}"), node.resources.audioPath && /* @__PURE__ */ React2.createElement("span", null, "\u{1F3B5}"), node.resources.cameraPath && /* @__PURE__ */ React2.createElement("span", null, "\u{1F4F7}"))), /* @__PURE__ */ React2.createElement("div", { className: "flex flex-col gap-0.5 flex-shrink-0" }, index > 0 && /* @__PURE__ */ React2.createElement(
1396
+ "button",
1397
+ {
1398
+ onClick: () => handleMoveNodeUp(index),
1399
+ className: "p-0.5 rounded bg-white/10 hover:bg-white/20 text-white text-[10px] transition-colors",
1400
+ title: "\u4E0A\u79FB"
1401
+ },
1402
+ "\u2B06\uFE0F"
1403
+ ), index < editableNodes.length - 1 && /* @__PURE__ */ React2.createElement(
1404
+ "button",
1405
+ {
1406
+ onClick: () => handleMoveNodeDown(index),
1407
+ className: "p-0.5 rounded bg-white/10 hover:bg-white/20 text-white text-[10px] transition-colors",
1408
+ title: "\u4E0B\u79FB"
1409
+ },
1410
+ "\u2B07\uFE0F"
1411
+ ), /* @__PURE__ */ React2.createElement(
1412
+ "button",
1413
+ {
1414
+ onClick: () => playlistJumpTo(index),
1415
+ className: "p-0.5 rounded bg-blue-500/30 hover:bg-blue-500/50 text-white text-[10px] transition-colors",
1416
+ title: "\u8DF3\u8F6C"
1417
+ },
1418
+ "\u25B6\uFE0F"
1419
+ ), /* @__PURE__ */ React2.createElement(
1420
+ "button",
1421
+ {
1422
+ onClick: () => {
1423
+ if (confirm(`\u786E\u5B9A\u5220\u9664 "${node.name}"\uFF1F`)) {
1424
+ handleDeleteNode(index);
1425
+ }
1426
+ },
1427
+ className: "p-0.5 rounded bg-red-500/30 hover:bg-red-500/50 text-white text-[10px] transition-colors",
1428
+ title: "\u5220\u9664"
1429
+ },
1430
+ "\u{1F5D1}\uFE0F"
1431
+ )))
1432
+ )))))
1433
+ )
1434
+ ));
1435
+ };
1436
+
1437
+ // src/mmd/presets.ts
1438
+ var defaultMMDPreset = {
1439
+ id: "default",
1440
+ name: "\u9ED8\u8BA4\u6A21\u578B",
1441
+ summary: "\u4EC5\u5C55\u793A\u6A21\u578B\uFF0C\u65E0\u52A8\u4F5C\u548C\u97F3\u9891",
1442
+ badges: ["\u6A21\u578B", "\u9759\u6001"],
1443
+ resources: {
1444
+ modelPath: "/mikutalking/models/YYB_Z6SakuraMiku/miku.pmx"
1445
+ },
1446
+ stage: {
1447
+ backgroundColor: "#000000",
1448
+ cameraPosition: { x: 0, y: 10, z: 30 },
1449
+ cameraTarget: { x: 0, y: 10, z: 0 },
1450
+ enablePhysics: true,
1451
+ showGrid: true,
1452
+ ammoPath: "/mikutalking/libs/ammo.wasm.js",
1453
+ ammoWasmPath: "/mikutalking/libs/"
1454
+ }
1455
+ };
1456
+ var catchTheWavePreset = {
1457
+ id: "catch-the-wave",
1458
+ name: "Catch The Wave",
1459
+ summary: "\u5B8C\u6574\u7684MMD\u8868\u6F14\uFF1A\u6A21\u578B\u3001\u52A8\u4F5C\u3001\u76F8\u673A\u8FD0\u955C\u3001\u97F3\u9891\u540C\u6B65",
1460
+ badges: ["\u6A21\u578B", "\u52A8\u4F5C", "\u76F8\u673A", "\u97F3\u9891"],
1461
+ resources: {
1462
+ modelPath: "/mikutalking/models/YYB_Z6SakuraMiku/miku.pmx",
1463
+ motionPath: "/mikutalking/actions/CatchTheWave/mmd_CatchTheWave_motion.vmd",
1464
+ cameraPath: "/mikutalking/actions/CatchTheWave/camera.vmd",
1465
+ audioPath: "/mikutalking/actions/CatchTheWave/pv_268.wav"
1466
+ },
1467
+ stage: {
1468
+ backgroundColor: "#01030b",
1469
+ cameraPosition: { x: 0, y: 10, z: 30 },
1470
+ cameraTarget: { x: 0, y: 10, z: 0 },
1471
+ enablePhysics: true,
1472
+ showGrid: false,
1473
+ ammoPath: "/mikutalking/libs/ammo.wasm.js",
1474
+ ammoWasmPath: "/mikutalking/libs/"
1475
+ }
1476
+ };
1477
+ var simpleModelPreset = {
1478
+ id: "simple-model",
1479
+ name: "\u7B80\u5355\u6A21\u578B",
1480
+ summary: "\u8F7B\u91CF\u7EA7\u6D4B\u8BD5\u6A21\u578B",
1481
+ badges: ["\u6A21\u578B", "\u8F7B\u91CF"],
1482
+ resources: {
1483
+ modelPath: "/mikutalking/models/test/v4c5.0.pmx"
1484
+ },
1485
+ stage: {
1486
+ backgroundColor: "#ffffff",
1487
+ cameraPosition: { x: 0, y: 10, z: 30 },
1488
+ cameraTarget: { x: 0, y: 10, z: 0 },
1489
+ enablePhysics: true,
1490
+ showGrid: true,
1491
+ ammoPath: "/mikutalking/libs/ammo.wasm.js",
1492
+ ammoWasmPath: "/mikutalking/libs/"
1493
+ }
1494
+ };
1495
+ var availableMMDPresets = [
1496
+ catchTheWavePreset,
1497
+ defaultMMDPreset,
1498
+ simpleModelPreset
1499
+ ];
1500
+
1501
+ export { MMDPlayerBase, MMDPlayerEnhanced, MMDPlaylist, availableMMDPresets, catchTheWavePreset, defaultMMDPreset, loadAmmo, simpleModelPreset };
1502
+ //# sourceMappingURL=index.mjs.map
1503
+ //# sourceMappingURL=index.mjs.map