sa2kit 1.2.1 → 1.4.0
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/{UniversalFileService-C1rUWWU-.d.ts → UniversalFileService-BuHN-jrR.d.ts} +46 -2
- package/dist/{UniversalFileService-DrCK0-NL.d.mts → UniversalFileService-CGGzYeeF.d.mts} +46 -2
- package/dist/{chunk-3XG5OHFD.mjs → chunk-CIVO4R6N.mjs} +2 -2
- package/dist/{chunk-3XG5OHFD.mjs.map → chunk-CIVO4R6N.mjs.map} +1 -1
- package/dist/chunk-EV6BCVOQ.mjs +204 -0
- package/dist/chunk-EV6BCVOQ.mjs.map +1 -0
- package/dist/chunk-W35VTQAW.js +211 -0
- package/dist/chunk-W35VTQAW.js.map +1 -0
- package/dist/{chunk-HWJ34NL6.js → chunk-ZRAW3HXA.js} +2 -2
- package/dist/{chunk-HWJ34NL6.js.map → chunk-ZRAW3HXA.js.map} +1 -1
- package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
- package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
- package/dist/mmd/admin/index.d.mts +8 -1115
- package/dist/mmd/admin/index.d.ts +8 -1115
- package/dist/mmd/admin/index.js +98 -248
- package/dist/mmd/admin/index.js.map +1 -1
- package/dist/mmd/admin/index.mjs +75 -244
- package/dist/mmd/admin/index.mjs.map +1 -1
- package/dist/mmd/index.d.mts +265 -3
- package/dist/mmd/index.d.ts +265 -3
- package/dist/mmd/index.js +1266 -15
- package/dist/mmd/index.js.map +1 -1
- package/dist/mmd/index.mjs +1261 -16
- package/dist/mmd/index.mjs.map +1 -1
- package/dist/mmd/server/index.d.mts +138 -0
- package/dist/mmd/server/index.d.ts +138 -0
- package/dist/mmd/server/index.js +245 -0
- package/dist/mmd/server/index.js.map +1 -0
- package/dist/mmd/server/index.mjs +207 -0
- package/dist/mmd/server/index.mjs.map +1 -0
- package/dist/testYourself/index.d.mts +145 -0
- package/dist/testYourself/index.d.ts +145 -0
- package/dist/testYourself/index.js +1004 -0
- package/dist/testYourself/index.js.map +1 -0
- package/dist/testYourself/index.mjs +993 -0
- package/dist/testYourself/index.mjs.map +1 -0
- package/dist/{types-C2ale3d9.d.mts → types-Bc_p-zAR.d.mts} +1 -1
- package/dist/{types-C2ale3d9.d.ts → types-Bc_p-zAR.d.ts} +1 -1
- package/dist/{types-Dg-U_chI.d.mts → types-CK4We_aI.d.mts} +13 -1
- package/dist/{types-Dg-U_chI.d.ts → types-CK4We_aI.d.ts} +13 -1
- package/dist/universalFile/index.d.mts +3 -3
- package/dist/universalFile/index.d.ts +3 -3
- package/dist/universalFile/index.js +48 -10
- package/dist/universalFile/index.js.map +1 -1
- package/dist/universalFile/index.mjs +43 -5
- package/dist/universalFile/index.mjs.map +1 -1
- package/dist/universalFile/server/index.d.mts +3 -3
- package/dist/universalFile/server/index.d.ts +3 -3
- package/dist/universalFile/server/index.js +239 -7
- package/dist/universalFile/server/index.js.map +1 -1
- package/dist/universalFile/server/index.mjs +234 -2
- package/dist/universalFile/server/index.mjs.map +1 -1
- package/package.json +19 -1
package/dist/mmd/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import React6, { forwardRef, useRef, useImperativeHandle, useEffect, useState, u
|
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { OrbitControls, MMDLoader, MMDAnimationHelper } from 'three-stdlib';
|
|
5
5
|
import { SkipBack, Pause, Play, SkipForward, Repeat, Repeat1, Grid3x3, Settings, Minimize, Maximize, X, Video, Check, User, Image, Music } from 'lucide-react';
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
6
7
|
|
|
7
8
|
// src/mmd/utils/ammo-loader.ts
|
|
8
9
|
var ammoPromise = null;
|
|
@@ -46,6 +47,102 @@ var loadAmmo = (path = "/libs/ammo.wasm.js") => {
|
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
// src/mmd/components/MMDPlayerBase.tsx
|
|
50
|
+
async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
51
|
+
const textures = [];
|
|
52
|
+
let meshCount = 0;
|
|
53
|
+
object.traverse((obj) => {
|
|
54
|
+
if (obj instanceof THREE.Mesh || obj instanceof THREE.SkinnedMesh) {
|
|
55
|
+
meshCount++;
|
|
56
|
+
const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
57
|
+
materials.forEach((material) => {
|
|
58
|
+
if (material instanceof THREE.Material) {
|
|
59
|
+
const textureProps = [
|
|
60
|
+
"map",
|
|
61
|
+
"lightMap",
|
|
62
|
+
"bumpMap",
|
|
63
|
+
"normalMap",
|
|
64
|
+
"specularMap",
|
|
65
|
+
"envMap",
|
|
66
|
+
"alphaMap",
|
|
67
|
+
"emissiveMap",
|
|
68
|
+
"displacementMap",
|
|
69
|
+
"roughnessMap",
|
|
70
|
+
"metalnessMap",
|
|
71
|
+
"aoMap",
|
|
72
|
+
// MMD 特有纹理
|
|
73
|
+
"gradientMap",
|
|
74
|
+
"toonMap",
|
|
75
|
+
"sphereMap",
|
|
76
|
+
"matcap"
|
|
77
|
+
];
|
|
78
|
+
textureProps.forEach((prop) => {
|
|
79
|
+
const texture = material[prop];
|
|
80
|
+
if (texture instanceof THREE.Texture && !textures.includes(texture)) {
|
|
81
|
+
textures.push(texture);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
console.log(`[MMDPlayerBase] Found ${meshCount} meshes and ${textures.length} unique textures`);
|
|
89
|
+
const texturePromises = textures.map((texture, index) => {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
const image = texture.image;
|
|
92
|
+
if (!image) {
|
|
93
|
+
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: No image`);
|
|
94
|
+
resolve();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (image instanceof HTMLImageElement) {
|
|
98
|
+
if (image.complete && image.naturalWidth > 0) {
|
|
99
|
+
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Already loaded`);
|
|
100
|
+
resolve();
|
|
101
|
+
} else {
|
|
102
|
+
const onLoad = () => {
|
|
103
|
+
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Loaded`);
|
|
104
|
+
image.removeEventListener("load", onLoad);
|
|
105
|
+
image.removeEventListener("error", onError);
|
|
106
|
+
resolve();
|
|
107
|
+
};
|
|
108
|
+
const onError = (e) => {
|
|
109
|
+
console.warn(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Failed to load`, e);
|
|
110
|
+
image.removeEventListener("load", onLoad);
|
|
111
|
+
image.removeEventListener("error", onError);
|
|
112
|
+
resolve();
|
|
113
|
+
};
|
|
114
|
+
image.addEventListener("load", onLoad);
|
|
115
|
+
image.addEventListener("error", onError);
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
image.removeEventListener("load", onLoad);
|
|
118
|
+
image.removeEventListener("error", onError);
|
|
119
|
+
console.warn(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Timeout`);
|
|
120
|
+
resolve();
|
|
121
|
+
}, 5e3);
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Non-image type`);
|
|
125
|
+
resolve();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
await Promise.all(texturePromises);
|
|
130
|
+
console.log("[MMDPlayerBase] All texture images loaded");
|
|
131
|
+
textures.forEach((texture) => {
|
|
132
|
+
texture.needsUpdate = true;
|
|
133
|
+
});
|
|
134
|
+
console.log("[MMDPlayerBase] Warming up renderer...");
|
|
135
|
+
for (let i = 0; i < 3; i++) {
|
|
136
|
+
await new Promise((resolve) => {
|
|
137
|
+
requestAnimationFrame(() => {
|
|
138
|
+
renderer.render(scene, camera);
|
|
139
|
+
console.log(`[MMDPlayerBase] Warmup render ${i + 1}/3`);
|
|
140
|
+
resolve();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
console.log("[MMDPlayerBase] All materials and textures fully ready");
|
|
145
|
+
}
|
|
49
146
|
var MMDPlayerBase = forwardRef((props, ref) => {
|
|
50
147
|
const {
|
|
51
148
|
resources,
|
|
@@ -231,7 +328,9 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
231
328
|
preserveDrawingBuffer: true
|
|
232
329
|
});
|
|
233
330
|
renderer.setSize(width, height);
|
|
234
|
-
|
|
331
|
+
const pixelRatio = mobileOptimization.enabled ? mobileOptimization.pixelRatio || Math.min(window.devicePixelRatio, 2) : window.devicePixelRatio;
|
|
332
|
+
renderer.setPixelRatio(pixelRatio);
|
|
333
|
+
console.log("[MMDPlayerBase] Pixel ratio set to:", pixelRatio);
|
|
235
334
|
if (checkCancelled()) {
|
|
236
335
|
renderer.dispose();
|
|
237
336
|
return;
|
|
@@ -241,6 +340,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
241
340
|
renderer.domElement.style.width = "100%";
|
|
242
341
|
renderer.domElement.style.height = "100%";
|
|
243
342
|
renderer.domElement.style.outline = "none";
|
|
343
|
+
renderer.domElement.style.position = "relative";
|
|
244
344
|
if (stage.enableShadow !== false && !mobileOptimization.reduceShadowQuality) {
|
|
245
345
|
renderer.shadowMap.enabled = true;
|
|
246
346
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
@@ -287,6 +387,8 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
287
387
|
resizeObserver.observe(container);
|
|
288
388
|
resizeObserverRef.current = resizeObserver;
|
|
289
389
|
onResize();
|
|
390
|
+
console.log("[MMDPlayerBase] Starting render loop (animation paused)");
|
|
391
|
+
animate();
|
|
290
392
|
console.log("[MMDPlayerBase] Start loading resources...", resources);
|
|
291
393
|
const loader = new MMDLoader();
|
|
292
394
|
const helper = new MMDAnimationHelper({
|
|
@@ -335,6 +437,15 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
335
437
|
durationRef.current = animation.duration;
|
|
336
438
|
console.log("[MMDPlayerBase] Animation duration:", animation.duration);
|
|
337
439
|
}
|
|
440
|
+
mesh.castShadow = true;
|
|
441
|
+
mesh.receiveShadow = true;
|
|
442
|
+
console.log("[MMDPlayerBase] Waiting for all materials and textures to load...");
|
|
443
|
+
const tempScene = new THREE.Scene();
|
|
444
|
+
tempScene.add(mesh);
|
|
445
|
+
await waitForMaterialsReady(mesh, renderer, tempScene, camera);
|
|
446
|
+
if (checkCancelled()) return;
|
|
447
|
+
console.log("[MMDPlayerBase] \u2705 All materials and textures loaded");
|
|
448
|
+
tempScene.remove(mesh);
|
|
338
449
|
const box = new THREE.Box3().setFromObject(mesh);
|
|
339
450
|
if (!box.isEmpty()) {
|
|
340
451
|
const center = box.getCenter(new THREE.Vector3());
|
|
@@ -358,14 +469,13 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
358
469
|
controls.update();
|
|
359
470
|
}
|
|
360
471
|
}
|
|
361
|
-
mesh.castShadow = true;
|
|
362
|
-
mesh.receiveShadow = true;
|
|
363
472
|
const enablePhysics = stage.enablePhysics !== false && !mobileOptimization.disablePhysics;
|
|
364
473
|
helper.add(mesh, {
|
|
365
474
|
animation,
|
|
366
475
|
physics: enablePhysics
|
|
367
476
|
});
|
|
368
477
|
scene.add(mesh);
|
|
478
|
+
console.log("[MMDPlayerBase] \u2705 Model added to scene (fully loaded)");
|
|
369
479
|
if (resources.cameraPath) {
|
|
370
480
|
loader.loadAnimation(
|
|
371
481
|
resources.cameraPath,
|
|
@@ -402,31 +512,56 @@ var MMDPlayerBase = forwardRef((props, ref) => {
|
|
|
402
512
|
(err) => console.error("Failed to load audio:", err)
|
|
403
513
|
);
|
|
404
514
|
}
|
|
515
|
+
let stageMesh = null;
|
|
405
516
|
if (resources.stageModelPath) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
(
|
|
416
|
-
|
|
517
|
+
try {
|
|
518
|
+
stageMesh = await new Promise((resolve, reject) => {
|
|
519
|
+
loader.load(
|
|
520
|
+
resources.stageModelPath,
|
|
521
|
+
(mesh2) => resolve(mesh2),
|
|
522
|
+
void 0,
|
|
523
|
+
(err) => reject(err)
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
if (checkCancelled()) return;
|
|
527
|
+
console.log("[MMDPlayerBase] Stage model loaded:", stageMesh);
|
|
528
|
+
stageMesh.castShadow = true;
|
|
529
|
+
stageMesh.receiveShadow = true;
|
|
530
|
+
console.log("[MMDPlayerBase] Waiting for stage materials and textures...");
|
|
531
|
+
const tempStageScene = new THREE.Scene();
|
|
532
|
+
tempStageScene.add(stageMesh);
|
|
533
|
+
await waitForMaterialsReady(stageMesh, renderer, tempStageScene, camera);
|
|
534
|
+
tempStageScene.remove(stageMesh);
|
|
535
|
+
if (checkCancelled()) return;
|
|
536
|
+
console.log("[MMDPlayerBase] \u2705 Stage materials and textures loaded");
|
|
537
|
+
scene.add(stageMesh);
|
|
538
|
+
console.log("[MMDPlayerBase] \u2705 Stage added to scene (fully loaded)");
|
|
539
|
+
} catch (err) {
|
|
540
|
+
console.error("Failed to load stage:", err);
|
|
541
|
+
}
|
|
417
542
|
}
|
|
418
543
|
if (checkCancelled()) return;
|
|
419
544
|
isReadyRef.current = true;
|
|
545
|
+
console.log("[MMDPlayerBase] \u{1F389} All resources fully loaded and ready!");
|
|
546
|
+
console.log("[MMDPlayerBase] \u{1F4CA} Summary:");
|
|
547
|
+
console.log(`[MMDPlayerBase] - Model: \u2705 Fully loaded with all textures`);
|
|
548
|
+
if (resources.stageModelPath) {
|
|
549
|
+
console.log(`[MMDPlayerBase] - Stage: \u2705 Fully loaded with all textures`);
|
|
550
|
+
}
|
|
551
|
+
if (animation) {
|
|
552
|
+
console.log(`[MMDPlayerBase] - Animation: \u2705 Ready (${animation.duration.toFixed(2)}s)`);
|
|
553
|
+
}
|
|
554
|
+
console.log("[MMDPlayerBase] \u{1F514} Triggering onLoad callback");
|
|
420
555
|
onLoad?.();
|
|
421
556
|
if (autoPlay) {
|
|
422
557
|
setTimeout(() => {
|
|
423
558
|
if (checkCancelled()) return;
|
|
559
|
+
console.log("[MMDPlayerBase] \u{1F3AC} Starting animation playback (after materials fully loaded)");
|
|
424
560
|
isPlayingRef.current = true;
|
|
425
561
|
if (!clockRef.current.running) clockRef.current.start();
|
|
426
562
|
onPlay?.();
|
|
427
563
|
}, 100);
|
|
428
564
|
}
|
|
429
|
-
animate();
|
|
430
565
|
} catch (error) {
|
|
431
566
|
if (checkCancelled()) return;
|
|
432
567
|
console.error("MMDPlayerBase initialization failed:", error);
|
|
@@ -875,6 +1010,7 @@ ${errorMessage}
|
|
|
875
1010
|
height: "100%",
|
|
876
1011
|
overflow: "hidden",
|
|
877
1012
|
position: "relative",
|
|
1013
|
+
// 恢复 relative,作为 canvas 的定位容器
|
|
878
1014
|
backgroundColor: stage.backgroundColor || "#000",
|
|
879
1015
|
...style
|
|
880
1016
|
}
|
|
@@ -1588,7 +1724,1116 @@ var MMDPlaylist = ({
|
|
|
1588
1724
|
))
|
|
1589
1725
|
);
|
|
1590
1726
|
};
|
|
1727
|
+
if (typeof document !== "undefined" && !document.getElementById("dialogue-box-animations")) {
|
|
1728
|
+
const style = document.createElement("style");
|
|
1729
|
+
style.id = "dialogue-box-animations";
|
|
1730
|
+
style.textContent = `
|
|
1731
|
+
@keyframes gradientShift {
|
|
1732
|
+
0%, 100% { background-position: 0% 50%; }
|
|
1733
|
+
50% { background-position: 100% 50%; }
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
@keyframes shimmer {
|
|
1737
|
+
0% { background-position: -200% 0; }
|
|
1738
|
+
100% { background-position: 200% 0; }
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
@keyframes cursorBlink {
|
|
1742
|
+
0%, 100% { opacity: 1; transform: scaleY(1); }
|
|
1743
|
+
50% { opacity: 0.3; transform: scaleY(0.8); }
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
@keyframes textFadeIn {
|
|
1747
|
+
from { opacity: 0; transform: translateY(2px); }
|
|
1748
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
@keyframes bounce {
|
|
1752
|
+
0%, 100% { transform: translateY(0); }
|
|
1753
|
+
50% { transform: translateY(-4px); }
|
|
1754
|
+
}
|
|
1755
|
+
`;
|
|
1756
|
+
document.head.appendChild(style);
|
|
1757
|
+
}
|
|
1758
|
+
var defaultTheme = {
|
|
1759
|
+
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
|
1760
|
+
borderColor: "rgba(255, 255, 255, 0.25)",
|
|
1761
|
+
textColor: "#ffffff",
|
|
1762
|
+
speakerBgColor: "rgba(255, 255, 255, 0.25)",
|
|
1763
|
+
speakerTextColor: "#ffffff",
|
|
1764
|
+
opacity: 0.98,
|
|
1765
|
+
blur: "24px",
|
|
1766
|
+
continueHint: "\u70B9\u51FB\u7EE7\u7EED \u25BC",
|
|
1767
|
+
showContinueHint: true
|
|
1768
|
+
};
|
|
1769
|
+
var DialogueBox = ({
|
|
1770
|
+
dialogue,
|
|
1771
|
+
theme: userTheme,
|
|
1772
|
+
isTyping = false,
|
|
1773
|
+
isAutoMode = false,
|
|
1774
|
+
onClick,
|
|
1775
|
+
onSkipTyping,
|
|
1776
|
+
onToggleAuto,
|
|
1777
|
+
onOpenHistory,
|
|
1778
|
+
onSkip,
|
|
1779
|
+
showControls = true,
|
|
1780
|
+
showSkipButton = true,
|
|
1781
|
+
showAutoButton = true,
|
|
1782
|
+
showHistoryButton = true,
|
|
1783
|
+
className
|
|
1784
|
+
}) => {
|
|
1785
|
+
const theme = { ...defaultTheme, ...userTheme };
|
|
1786
|
+
const [displayedText, setDisplayedText] = useState("");
|
|
1787
|
+
const [isComplete, setIsComplete] = useState(false);
|
|
1788
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
1789
|
+
const typingRef = useRef(null);
|
|
1790
|
+
const currentTextRef = useRef("");
|
|
1791
|
+
useEffect(() => {
|
|
1792
|
+
setIsMounted(true);
|
|
1793
|
+
}, []);
|
|
1794
|
+
useEffect(() => {
|
|
1795
|
+
if (!dialogue) {
|
|
1796
|
+
setDisplayedText("");
|
|
1797
|
+
setIsComplete(false);
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
const text = dialogue.text;
|
|
1801
|
+
const speed = dialogue.typeSpeed ?? 50;
|
|
1802
|
+
if (typingRef.current) {
|
|
1803
|
+
clearTimeout(typingRef.current);
|
|
1804
|
+
}
|
|
1805
|
+
setDisplayedText("");
|
|
1806
|
+
setIsComplete(false);
|
|
1807
|
+
currentTextRef.current = text;
|
|
1808
|
+
let index = 0;
|
|
1809
|
+
const typeNext = () => {
|
|
1810
|
+
if (index < text.length && currentTextRef.current === text) {
|
|
1811
|
+
setDisplayedText(text.slice(0, index + 1));
|
|
1812
|
+
index++;
|
|
1813
|
+
typingRef.current = setTimeout(typeNext, speed);
|
|
1814
|
+
} else if (currentTextRef.current === text) {
|
|
1815
|
+
setIsComplete(true);
|
|
1816
|
+
}
|
|
1817
|
+
};
|
|
1818
|
+
typingRef.current = setTimeout(typeNext, speed);
|
|
1819
|
+
return () => {
|
|
1820
|
+
if (typingRef.current) {
|
|
1821
|
+
clearTimeout(typingRef.current);
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
}, [dialogue]);
|
|
1825
|
+
const handleSkipTyping = useCallback(() => {
|
|
1826
|
+
if (typingRef.current) {
|
|
1827
|
+
clearTimeout(typingRef.current);
|
|
1828
|
+
}
|
|
1829
|
+
if (dialogue) {
|
|
1830
|
+
setDisplayedText(dialogue.text);
|
|
1831
|
+
setIsComplete(true);
|
|
1832
|
+
}
|
|
1833
|
+
onSkipTyping?.();
|
|
1834
|
+
}, [dialogue, onSkipTyping]);
|
|
1835
|
+
const handleClick = useCallback(() => {
|
|
1836
|
+
if (!isComplete) {
|
|
1837
|
+
handleSkipTyping();
|
|
1838
|
+
} else {
|
|
1839
|
+
onClick?.();
|
|
1840
|
+
}
|
|
1841
|
+
}, [isComplete, handleSkipTyping, onClick]);
|
|
1842
|
+
if (!dialogue) {
|
|
1843
|
+
return null;
|
|
1844
|
+
}
|
|
1845
|
+
dialogue.speakerColor || theme.speakerBgColor;
|
|
1846
|
+
console.log("[DialogueBox] Rendering:", {
|
|
1847
|
+
speaker: dialogue.speaker,
|
|
1848
|
+
text: dialogue.text,
|
|
1849
|
+
displayedText,
|
|
1850
|
+
isComplete
|
|
1851
|
+
});
|
|
1852
|
+
const dialogueContent = /* @__PURE__ */ React6.createElement(
|
|
1853
|
+
"div",
|
|
1854
|
+
{
|
|
1855
|
+
className: `${className || ""}`,
|
|
1856
|
+
style: {
|
|
1857
|
+
position: "fixed",
|
|
1858
|
+
bottom: 0,
|
|
1859
|
+
left: 0,
|
|
1860
|
+
right: 0,
|
|
1861
|
+
height: "30vh",
|
|
1862
|
+
minHeight: "200px",
|
|
1863
|
+
maxHeight: "30vh",
|
|
1864
|
+
zIndex: 1,
|
|
1865
|
+
pointerEvents: "auto"
|
|
1866
|
+
}
|
|
1867
|
+
},
|
|
1868
|
+
/* @__PURE__ */ React6.createElement(
|
|
1869
|
+
"div",
|
|
1870
|
+
{
|
|
1871
|
+
className: "w-full h-full rounded-t-3xl border cursor-pointer select-none transition-all hover:border-white/50 hover:shadow-2xl flex flex-col relative overflow-hidden",
|
|
1872
|
+
onClick: handleClick,
|
|
1873
|
+
style: {
|
|
1874
|
+
borderColor: theme.borderColor,
|
|
1875
|
+
backdropFilter: `blur(${theme.blur}) saturate(200%)`,
|
|
1876
|
+
WebkitBackdropFilter: `blur(${theme.blur}) saturate(200%)`,
|
|
1877
|
+
opacity: theme.opacity,
|
|
1878
|
+
pointerEvents: "auto",
|
|
1879
|
+
position: "relative",
|
|
1880
|
+
display: "flex",
|
|
1881
|
+
background: `linear-gradient(135deg,
|
|
1882
|
+
rgba(255, 255, 255, 0.15) 0%,
|
|
1883
|
+
rgba(255, 255, 255, 0.12) 50%,
|
|
1884
|
+
rgba(255, 255, 255, 0.1) 100%)`,
|
|
1885
|
+
boxShadow: `
|
|
1886
|
+
0 -8px 40px rgba(255, 255, 255, 0.1),
|
|
1887
|
+
0 -4px 16px rgba(255, 255, 255, 0.05),
|
|
1888
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
|
1889
|
+
inset 0 -1px 0 rgba(255, 255, 255, 0.1)
|
|
1890
|
+
`
|
|
1891
|
+
}
|
|
1892
|
+
},
|
|
1893
|
+
/* @__PURE__ */ React6.createElement(
|
|
1894
|
+
"div",
|
|
1895
|
+
{
|
|
1896
|
+
className: "absolute inset-0 opacity-20 pointer-events-none",
|
|
1897
|
+
style: {
|
|
1898
|
+
background: `linear-gradient(45deg,
|
|
1899
|
+
rgba(255, 182, 193, 0.2) 0%,
|
|
1900
|
+
rgba(173, 216, 230, 0.2) 25%,
|
|
1901
|
+
rgba(221, 160, 221, 0.2) 50%,
|
|
1902
|
+
rgba(255, 218, 185, 0.2) 75%,
|
|
1903
|
+
rgba(255, 182, 193, 0.2) 100%)`,
|
|
1904
|
+
backgroundSize: "400% 400%",
|
|
1905
|
+
animation: "gradientShift 15s ease infinite"
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
),
|
|
1909
|
+
/* @__PURE__ */ React6.createElement(
|
|
1910
|
+
"div",
|
|
1911
|
+
{
|
|
1912
|
+
className: "absolute top-0 left-0 right-0 h-1",
|
|
1913
|
+
style: {
|
|
1914
|
+
background: "linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent)"
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
),
|
|
1918
|
+
/* @__PURE__ */ React6.createElement(
|
|
1919
|
+
"div",
|
|
1920
|
+
{
|
|
1921
|
+
className: "absolute bottom-0 left-0 right-0 h-px",
|
|
1922
|
+
style: {
|
|
1923
|
+
background: "linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent)"
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
),
|
|
1927
|
+
showControls && /* @__PURE__ */ React6.createElement("div", { className: "flex justify-end gap-3 px-6 pt-4 pb-2 flex-shrink-0 relative z-10" }, showHistoryButton && /* @__PURE__ */ React6.createElement(
|
|
1928
|
+
"button",
|
|
1929
|
+
{
|
|
1930
|
+
onClick: (e) => {
|
|
1931
|
+
e.stopPropagation();
|
|
1932
|
+
onOpenHistory?.();
|
|
1933
|
+
},
|
|
1934
|
+
className: "px-4 py-2 text-xs rounded-xl text-white font-medium hover:text-white transition-all backdrop-blur-lg border border-white/30 hover:border-white/50 hover:scale-105 active:scale-95 shadow-lg",
|
|
1935
|
+
style: {
|
|
1936
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
1937
|
+
boxShadow: "0 4px 16px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
|
|
1938
|
+
},
|
|
1939
|
+
title: "\u5386\u53F2\u8BB0\u5F55"
|
|
1940
|
+
},
|
|
1941
|
+
"\u{1F4DC} \u5386\u53F2"
|
|
1942
|
+
), showAutoButton && /* @__PURE__ */ React6.createElement(
|
|
1943
|
+
"button",
|
|
1944
|
+
{
|
|
1945
|
+
onClick: (e) => {
|
|
1946
|
+
e.stopPropagation();
|
|
1947
|
+
onToggleAuto?.();
|
|
1948
|
+
},
|
|
1949
|
+
className: `px-4 py-2 text-xs rounded-xl font-medium transition-all backdrop-blur-lg border hover:scale-105 active:scale-95 shadow-lg ${isAutoMode ? "border-white/50 text-white" : "border-white/30 hover:border-white/50 text-white"}`,
|
|
1950
|
+
style: {
|
|
1951
|
+
background: isAutoMode ? "linear-gradient(135deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0.25))" : "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
1952
|
+
boxShadow: isAutoMode ? "0 4px 20px rgba(255, 255, 255, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.4)" : "0 4px 16px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
|
|
1953
|
+
},
|
|
1954
|
+
title: "\u81EA\u52A8\u64AD\u653E"
|
|
1955
|
+
},
|
|
1956
|
+
"\u25B6 \u81EA\u52A8"
|
|
1957
|
+
), showSkipButton && /* @__PURE__ */ React6.createElement(
|
|
1958
|
+
"button",
|
|
1959
|
+
{
|
|
1960
|
+
onClick: (e) => {
|
|
1961
|
+
e.stopPropagation();
|
|
1962
|
+
onSkip?.();
|
|
1963
|
+
},
|
|
1964
|
+
className: "px-4 py-2 text-xs rounded-xl text-white font-medium hover:text-white transition-all backdrop-blur-lg border border-white/30 hover:border-white/50 hover:scale-105 active:scale-95 shadow-lg",
|
|
1965
|
+
style: {
|
|
1966
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
1967
|
+
boxShadow: "0 4px 16px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
|
|
1968
|
+
},
|
|
1969
|
+
title: "\u5FEB\u8FDB"
|
|
1970
|
+
},
|
|
1971
|
+
"\u23E9 \u5FEB\u8FDB"
|
|
1972
|
+
)),
|
|
1973
|
+
/* @__PURE__ */ React6.createElement("div", { className: "px-8 pb-6 flex-1 flex flex-col justify-center overflow-y-auto relative z-10" }, dialogue.speaker && /* @__PURE__ */ React6.createElement(
|
|
1974
|
+
"div",
|
|
1975
|
+
{
|
|
1976
|
+
className: "inline-block px-6 py-2.5 rounded-2xl mb-4 text-sm font-bold shadow-2xl self-start relative overflow-hidden transition-all hover:scale-105",
|
|
1977
|
+
style: {
|
|
1978
|
+
color: "#ffffff",
|
|
1979
|
+
background: `linear-gradient(135deg,
|
|
1980
|
+
rgba(255, 255, 255, 0.3) 0%,
|
|
1981
|
+
rgba(255, 255, 255, 0.2) 100%)`,
|
|
1982
|
+
backdropFilter: "blur(20px)",
|
|
1983
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
1984
|
+
boxShadow: "0 8px 32px rgba(255, 255, 255, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.4)",
|
|
1985
|
+
border: "1px solid rgba(255, 255, 255, 0.3)"
|
|
1986
|
+
}
|
|
1987
|
+
},
|
|
1988
|
+
/* @__PURE__ */ React6.createElement(
|
|
1989
|
+
"div",
|
|
1990
|
+
{
|
|
1991
|
+
className: "absolute inset-0 opacity-30",
|
|
1992
|
+
style: {
|
|
1993
|
+
background: "linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.5) 50%, transparent 70%)",
|
|
1994
|
+
backgroundSize: "200% 200%",
|
|
1995
|
+
animation: "shimmer 3s infinite"
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
),
|
|
1999
|
+
/* @__PURE__ */ React6.createElement("span", { className: "relative z-10 drop-shadow-lg" }, dialogue.speaker)
|
|
2000
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2001
|
+
"div",
|
|
2002
|
+
{
|
|
2003
|
+
className: "text-lg leading-relaxed relative",
|
|
2004
|
+
style: {
|
|
2005
|
+
color: theme.textColor,
|
|
2006
|
+
textShadow: "0 2px 8px rgba(0, 0, 0, 0.5)"
|
|
2007
|
+
}
|
|
2008
|
+
},
|
|
2009
|
+
/* @__PURE__ */ React6.createElement("span", { className: "inline-block", style: {
|
|
2010
|
+
animation: !isComplete ? "textFadeIn 0.3s ease-out" : "none"
|
|
2011
|
+
} }, displayedText),
|
|
2012
|
+
!isComplete && /* @__PURE__ */ React6.createElement(
|
|
2013
|
+
"span",
|
|
2014
|
+
{
|
|
2015
|
+
className: "inline-block w-1 h-6 ml-1 align-middle",
|
|
2016
|
+
style: {
|
|
2017
|
+
background: "linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.6))",
|
|
2018
|
+
animation: "cursorBlink 1s ease-in-out infinite",
|
|
2019
|
+
boxShadow: "0 0 12px rgba(255, 255, 255, 0.8)"
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
)
|
|
2023
|
+
), isComplete && theme.showContinueHint && /* @__PURE__ */ React6.createElement("div", { className: "flex justify-end mt-4" }, /* @__PURE__ */ React6.createElement(
|
|
2024
|
+
"span",
|
|
2025
|
+
{
|
|
2026
|
+
className: "text-sm px-4 py-2 rounded-full backdrop-blur-lg font-medium",
|
|
2027
|
+
style: {
|
|
2028
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.15))",
|
|
2029
|
+
color: "rgba(255, 255, 255, 0.9)",
|
|
2030
|
+
animation: "bounce 2s ease-in-out infinite",
|
|
2031
|
+
boxShadow: "0 4px 16px rgba(255, 255, 255, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
|
|
2032
|
+
border: "1px solid rgba(255, 255, 255, 0.3)"
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
theme.continueHint
|
|
2036
|
+
)))
|
|
2037
|
+
)
|
|
2038
|
+
);
|
|
2039
|
+
if (!isMounted) {
|
|
2040
|
+
return null;
|
|
2041
|
+
}
|
|
2042
|
+
let portalContainer = document.getElementById("dialogue-portal-root");
|
|
2043
|
+
if (!portalContainer) {
|
|
2044
|
+
portalContainer = document.createElement("div");
|
|
2045
|
+
portalContainer.id = "dialogue-portal-root";
|
|
2046
|
+
portalContainer.style.cssText = "position: fixed; inset: 0; pointer-events: none; z-index: 999999;";
|
|
2047
|
+
document.body.appendChild(portalContainer);
|
|
2048
|
+
}
|
|
2049
|
+
return createPortal(dialogueContent, portalContainer);
|
|
2050
|
+
};
|
|
2051
|
+
var HistoryPanel = ({
|
|
2052
|
+
history,
|
|
2053
|
+
theme,
|
|
2054
|
+
onClose,
|
|
2055
|
+
className
|
|
2056
|
+
}) => {
|
|
2057
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
2058
|
+
useEffect(() => {
|
|
2059
|
+
setIsMounted(true);
|
|
2060
|
+
}, []);
|
|
2061
|
+
const historyContent = /* @__PURE__ */ React6.createElement(
|
|
2062
|
+
"div",
|
|
2063
|
+
{
|
|
2064
|
+
className: `fixed inset-0 flex flex-col ${className}`,
|
|
2065
|
+
style: {
|
|
2066
|
+
zIndex: 1,
|
|
2067
|
+
pointerEvents: "auto",
|
|
2068
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.05))"
|
|
2069
|
+
},
|
|
2070
|
+
onClick: onClose
|
|
2071
|
+
},
|
|
2072
|
+
/* @__PURE__ */ React6.createElement(
|
|
2073
|
+
"div",
|
|
2074
|
+
{
|
|
2075
|
+
className: "absolute inset-0 opacity-25 pointer-events-none",
|
|
2076
|
+
style: {
|
|
2077
|
+
background: `linear-gradient(45deg,
|
|
2078
|
+
rgba(255, 182, 193, 0.15) 0%,
|
|
2079
|
+
rgba(173, 216, 230, 0.15) 25%,
|
|
2080
|
+
rgba(221, 160, 221, 0.15) 50%,
|
|
2081
|
+
rgba(255, 218, 185, 0.15) 75%,
|
|
2082
|
+
rgba(255, 182, 193, 0.15) 100%)`,
|
|
2083
|
+
backgroundSize: "400% 400%",
|
|
2084
|
+
animation: "gradientShift 15s ease infinite"
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
),
|
|
2088
|
+
/* @__PURE__ */ React6.createElement(
|
|
2089
|
+
"div",
|
|
2090
|
+
{
|
|
2091
|
+
className: "absolute inset-0 pointer-events-none",
|
|
2092
|
+
style: {
|
|
2093
|
+
backdropFilter: "blur(32px) saturate(200%)",
|
|
2094
|
+
WebkitBackdropFilter: "blur(32px) saturate(200%)"
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
),
|
|
2098
|
+
/* @__PURE__ */ React6.createElement("div", { className: "relative z-10 flex flex-col h-full", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React6.createElement(
|
|
2099
|
+
"div",
|
|
2100
|
+
{
|
|
2101
|
+
className: "flex items-center justify-between px-8 py-6 border-b relative",
|
|
2102
|
+
style: {
|
|
2103
|
+
borderColor: "rgba(255, 255, 255, 0.25)",
|
|
2104
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
2105
|
+
backdropFilter: "blur(20px)",
|
|
2106
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
2107
|
+
boxShadow: "0 4px 24px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
|
|
2108
|
+
}
|
|
2109
|
+
},
|
|
2110
|
+
/* @__PURE__ */ React6.createElement("h2", { className: "text-2xl font-bold text-white flex items-center gap-3" }, /* @__PURE__ */ React6.createElement("span", { className: "text-3xl" }, "\u{1F4DC}"), /* @__PURE__ */ React6.createElement("span", { style: { textShadow: "0 2px 12px rgba(255, 255, 255, 0.3)" } }, "\u5BF9\u8BDD\u5386\u53F2")),
|
|
2111
|
+
/* @__PURE__ */ React6.createElement(
|
|
2112
|
+
"button",
|
|
2113
|
+
{
|
|
2114
|
+
onClick: onClose,
|
|
2115
|
+
className: "p-3 hover:bg-white/20 rounded-2xl transition-all text-white hover:text-white hover:scale-110 active:scale-95",
|
|
2116
|
+
style: {
|
|
2117
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
2118
|
+
backdropFilter: "blur(12px)",
|
|
2119
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
2120
|
+
boxShadow: "0 4px 16px rgba(255, 255, 255, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
|
|
2121
|
+
border: "1px solid rgba(255, 255, 255, 0.25)"
|
|
2122
|
+
},
|
|
2123
|
+
"aria-label": "\u5173\u95ED"
|
|
2124
|
+
},
|
|
2125
|
+
/* @__PURE__ */ React6.createElement("svg", { className: "w-6 h-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" }, /* @__PURE__ */ React6.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))
|
|
2126
|
+
)
|
|
2127
|
+
), /* @__PURE__ */ React6.createElement("div", { className: "flex-1 overflow-y-auto p-6 space-y-4" }, history.length === 0 ? /* @__PURE__ */ React6.createElement("div", { className: "text-center text-white/70 py-20 text-lg font-medium", style: { textShadow: "0 2px 8px rgba(0, 0, 0, 0.3)" } }, "\u6682\u65E0\u5BF9\u8BDD\u5386\u53F2") : history.map((item, index) => /* @__PURE__ */ React6.createElement(
|
|
2128
|
+
"div",
|
|
2129
|
+
{
|
|
2130
|
+
key: `${item.nodeIndex}-${item.dialogueIndex}-${index}`,
|
|
2131
|
+
className: "p-6 rounded-2xl transition-all hover:scale-[1.01] relative overflow-hidden cursor-pointer",
|
|
2132
|
+
style: {
|
|
2133
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.1))",
|
|
2134
|
+
backdropFilter: "blur(16px)",
|
|
2135
|
+
WebkitBackdropFilter: "blur(16px)",
|
|
2136
|
+
border: "1px solid rgba(255, 255, 255, 0.2)",
|
|
2137
|
+
boxShadow: "0 8px 24px rgba(255, 255, 255, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.25)"
|
|
2138
|
+
}
|
|
2139
|
+
},
|
|
2140
|
+
/* @__PURE__ */ React6.createElement(
|
|
2141
|
+
"div",
|
|
2142
|
+
{
|
|
2143
|
+
className: "absolute top-0 left-0 right-0 h-0.5",
|
|
2144
|
+
style: {
|
|
2145
|
+
background: "linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent)"
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
),
|
|
2149
|
+
item.speaker && /* @__PURE__ */ React6.createElement(
|
|
2150
|
+
"div",
|
|
2151
|
+
{
|
|
2152
|
+
className: "inline-block px-5 py-2 rounded-xl mb-3 text-sm font-bold shadow-lg relative overflow-hidden",
|
|
2153
|
+
style: {
|
|
2154
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2))",
|
|
2155
|
+
backdropFilter: "blur(12px)",
|
|
2156
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
2157
|
+
color: "#ffffff",
|
|
2158
|
+
boxShadow: "0 4px 12px rgba(255, 255, 255, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
|
|
2159
|
+
border: "1px solid rgba(255, 255, 255, 0.25)"
|
|
2160
|
+
}
|
|
2161
|
+
},
|
|
2162
|
+
/* @__PURE__ */ React6.createElement(
|
|
2163
|
+
"div",
|
|
2164
|
+
{
|
|
2165
|
+
className: "absolute inset-0 opacity-30",
|
|
2166
|
+
style: {
|
|
2167
|
+
background: "linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.4) 50%, transparent 70%)",
|
|
2168
|
+
backgroundSize: "200% 200%",
|
|
2169
|
+
animation: "shimmer 3s infinite"
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
),
|
|
2173
|
+
/* @__PURE__ */ React6.createElement("span", { className: "relative z-10 drop-shadow-lg" }, item.speaker)
|
|
2174
|
+
),
|
|
2175
|
+
/* @__PURE__ */ React6.createElement(
|
|
2176
|
+
"p",
|
|
2177
|
+
{
|
|
2178
|
+
className: "text-base leading-relaxed",
|
|
2179
|
+
style: {
|
|
2180
|
+
color: "#ffffff",
|
|
2181
|
+
textShadow: "0 2px 8px rgba(0, 0, 0, 0.3)"
|
|
2182
|
+
}
|
|
2183
|
+
},
|
|
2184
|
+
item.text
|
|
2185
|
+
)
|
|
2186
|
+
))), /* @__PURE__ */ React6.createElement(
|
|
2187
|
+
"div",
|
|
2188
|
+
{
|
|
2189
|
+
className: "px-8 py-4 border-t text-center",
|
|
2190
|
+
style: {
|
|
2191
|
+
borderColor: "rgba(255, 255, 255, 0.2)",
|
|
2192
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.1))",
|
|
2193
|
+
backdropFilter: "blur(20px)",
|
|
2194
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
2195
|
+
boxShadow: "inset 0 1px 0 rgba(255, 255, 255, 0.2)"
|
|
2196
|
+
}
|
|
2197
|
+
},
|
|
2198
|
+
/* @__PURE__ */ React6.createElement(
|
|
2199
|
+
"span",
|
|
2200
|
+
{
|
|
2201
|
+
className: "text-sm px-5 py-2.5 rounded-full inline-block font-medium",
|
|
2202
|
+
style: {
|
|
2203
|
+
color: "rgba(255, 255, 255, 0.9)",
|
|
2204
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
2205
|
+
backdropFilter: "blur(12px)",
|
|
2206
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
2207
|
+
boxShadow: "0 4px 16px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
|
|
2208
|
+
border: "1px solid rgba(255, 255, 255, 0.25)",
|
|
2209
|
+
textShadow: "0 2px 8px rgba(0, 0, 0, 0.3)"
|
|
2210
|
+
}
|
|
2211
|
+
},
|
|
2212
|
+
"\u70B9\u51FB\u80CC\u666F\u5173\u95ED"
|
|
2213
|
+
)
|
|
2214
|
+
))
|
|
2215
|
+
);
|
|
2216
|
+
if (!isMounted) {
|
|
2217
|
+
return null;
|
|
2218
|
+
}
|
|
2219
|
+
let portalContainer = document.getElementById("history-portal-root");
|
|
2220
|
+
if (!portalContainer) {
|
|
2221
|
+
portalContainer = document.createElement("div");
|
|
2222
|
+
portalContainer.id = "history-portal-root";
|
|
2223
|
+
portalContainer.style.cssText = "position: fixed; inset: 0; pointer-events: none; z-index: 999999;";
|
|
2224
|
+
document.body.appendChild(portalContainer);
|
|
2225
|
+
}
|
|
2226
|
+
return createPortal(historyContent, portalContainer);
|
|
2227
|
+
};
|
|
2228
|
+
var LoadingScreen = ({
|
|
2229
|
+
isLoading = true,
|
|
2230
|
+
loadingText = "\u6B63\u5728\u51C6\u5907\u573A\u666F\u4E2D...",
|
|
2231
|
+
className = ""
|
|
2232
|
+
}) => {
|
|
2233
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
2234
|
+
useEffect(() => {
|
|
2235
|
+
setIsMounted(true);
|
|
2236
|
+
}, []);
|
|
2237
|
+
console.log("[LoadingScreen] Render state:", {
|
|
2238
|
+
isLoading,
|
|
2239
|
+
isMounted
|
|
2240
|
+
});
|
|
2241
|
+
if (!isMounted) {
|
|
2242
|
+
return null;
|
|
2243
|
+
}
|
|
2244
|
+
if (!isLoading) {
|
|
2245
|
+
console.log("[LoadingScreen] Not showing, returning null");
|
|
2246
|
+
return null;
|
|
2247
|
+
}
|
|
2248
|
+
const content = /* @__PURE__ */ React6.createElement(
|
|
2249
|
+
"div",
|
|
2250
|
+
{
|
|
2251
|
+
className: `fixed inset-0 w-screen h-screen flex items-center justify-center ${className}`,
|
|
2252
|
+
style: {
|
|
2253
|
+
zIndex: 999998,
|
|
2254
|
+
pointerEvents: "auto",
|
|
2255
|
+
backgroundColor: "rgba(0, 0, 0, 0.4)",
|
|
2256
|
+
margin: 0,
|
|
2257
|
+
padding: 0
|
|
2258
|
+
}
|
|
2259
|
+
},
|
|
2260
|
+
/* @__PURE__ */ React6.createElement(
|
|
2261
|
+
"div",
|
|
2262
|
+
{
|
|
2263
|
+
className: "absolute inset-0 w-full h-full pointer-events-none",
|
|
2264
|
+
style: {
|
|
2265
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.1))",
|
|
2266
|
+
backdropFilter: "blur(32px) saturate(200%)",
|
|
2267
|
+
WebkitBackdropFilter: "blur(32px) saturate(200%)"
|
|
2268
|
+
}
|
|
2269
|
+
},
|
|
2270
|
+
/* @__PURE__ */ React6.createElement(
|
|
2271
|
+
"div",
|
|
2272
|
+
{
|
|
2273
|
+
className: "flex items-center justify-center flex-col inset-0 w-full h-full opacity-25 pointer-events-none",
|
|
2274
|
+
style: {
|
|
2275
|
+
background: `linear-gradient(45deg,
|
|
2276
|
+
rgba(255, 182, 193, 0.2) 0%,
|
|
2277
|
+
rgba(173, 216, 230, 0.2) 25%,
|
|
2278
|
+
rgba(221, 160, 221, 0.2) 50%,
|
|
2279
|
+
rgba(255, 218, 185, 0.2) 75%,
|
|
2280
|
+
rgba(255, 182, 193, 0.2) 100%)`,
|
|
2281
|
+
backgroundSize: "400% 400%",
|
|
2282
|
+
animation: "gradientShift 15s ease infinite"
|
|
2283
|
+
}
|
|
2284
|
+
},
|
|
2285
|
+
/* @__PURE__ */ React6.createElement(
|
|
2286
|
+
"div",
|
|
2287
|
+
{
|
|
2288
|
+
className: "text-lg font-medium text-white text-center px-6 py-3 rounded-2xl"
|
|
2289
|
+
},
|
|
2290
|
+
loadingText
|
|
2291
|
+
)
|
|
2292
|
+
)
|
|
2293
|
+
)
|
|
2294
|
+
);
|
|
2295
|
+
let portalContainer = document.getElementById("loading-screen-portal-root");
|
|
2296
|
+
if (!portalContainer) {
|
|
2297
|
+
portalContainer = document.createElement("div");
|
|
2298
|
+
portalContainer.id = "loading-screen-portal-root";
|
|
2299
|
+
document.body.appendChild(portalContainer);
|
|
2300
|
+
}
|
|
2301
|
+
portalContainer.style.cssText = `
|
|
2302
|
+
position: fixed;
|
|
2303
|
+
top: 0;
|
|
2304
|
+
left: 0;
|
|
2305
|
+
right: 0;
|
|
2306
|
+
bottom: 0;
|
|
2307
|
+
width: 100vw;
|
|
2308
|
+
height: 100vh;
|
|
2309
|
+
margin: 0;
|
|
2310
|
+
padding: 0;
|
|
2311
|
+
overflow: hidden;
|
|
2312
|
+
z-index: 999998;
|
|
2313
|
+
pointer-events: none;
|
|
2314
|
+
`.replace(/\s+/g, " ").trim();
|
|
2315
|
+
return createPortal(content, portalContainer);
|
|
2316
|
+
};
|
|
2317
|
+
LoadingScreen.displayName = "LoadingScreen";
|
|
2318
|
+
var StartScreen = ({
|
|
2319
|
+
showStartScreen = false,
|
|
2320
|
+
scriptName = "",
|
|
2321
|
+
startText = "\u70B9\u51FB\u5F00\u59CB",
|
|
2322
|
+
onStart,
|
|
2323
|
+
className = ""
|
|
2324
|
+
}) => {
|
|
2325
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
2326
|
+
useEffect(() => {
|
|
2327
|
+
setIsMounted(true);
|
|
2328
|
+
}, []);
|
|
2329
|
+
console.log("[StartScreen] Render state:", {
|
|
2330
|
+
showStartScreen,
|
|
2331
|
+
scriptName,
|
|
2332
|
+
isMounted
|
|
2333
|
+
});
|
|
2334
|
+
if (!isMounted) {
|
|
2335
|
+
return null;
|
|
2336
|
+
}
|
|
2337
|
+
if (!showStartScreen) {
|
|
2338
|
+
console.log("[StartScreen] Not showing, returning null");
|
|
2339
|
+
return null;
|
|
2340
|
+
}
|
|
2341
|
+
const content = /* @__PURE__ */ React6.createElement(
|
|
2342
|
+
"div",
|
|
2343
|
+
{
|
|
2344
|
+
className: `fixed inset-0 w-screen h-screen flex items-center justify-center cursor-pointer ${className}`,
|
|
2345
|
+
style: {
|
|
2346
|
+
zIndex: 999999,
|
|
2347
|
+
pointerEvents: "auto",
|
|
2348
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
2349
|
+
margin: 0,
|
|
2350
|
+
padding: 0
|
|
2351
|
+
},
|
|
2352
|
+
onClick: onStart
|
|
2353
|
+
},
|
|
2354
|
+
/* @__PURE__ */ React6.createElement(
|
|
2355
|
+
"div",
|
|
2356
|
+
{
|
|
2357
|
+
className: "absolute inset-0 w-full h-full pointer-events-none",
|
|
2358
|
+
style: {
|
|
2359
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
2360
|
+
backdropFilter: "blur(40px) saturate(200%)",
|
|
2361
|
+
WebkitBackdropFilter: "blur(40px) saturate(200%)"
|
|
2362
|
+
}
|
|
2363
|
+
},
|
|
2364
|
+
/* @__PURE__ */ React6.createElement(
|
|
2365
|
+
"div",
|
|
2366
|
+
{
|
|
2367
|
+
className: "flex items-center justify-center flex-col inset-0 w-full h-full opacity-30 pointer-events-none",
|
|
2368
|
+
style: {
|
|
2369
|
+
background: `linear-gradient(45deg,
|
|
2370
|
+
rgba(255, 182, 193, 0.25) 0%,
|
|
2371
|
+
rgba(173, 216, 230, 0.25) 25%,
|
|
2372
|
+
rgba(221, 160, 221, 0.25) 50%,
|
|
2373
|
+
rgba(255, 218, 185, 0.25) 75%,
|
|
2374
|
+
rgba(255, 182, 193, 0.25) 100%)`,
|
|
2375
|
+
backgroundSize: "400% 400%",
|
|
2376
|
+
animation: "gradientShift 15s ease infinite"
|
|
2377
|
+
}
|
|
2378
|
+
},
|
|
2379
|
+
/* @__PURE__ */ React6.createElement(
|
|
2380
|
+
"div",
|
|
2381
|
+
{
|
|
2382
|
+
className: "relative z-10 text-center px-16 py-14 rounded-3xl max-w-2xl mx-auto transform transition-all hover:scale-105",
|
|
2383
|
+
style: {
|
|
2384
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.15))",
|
|
2385
|
+
backdropFilter: "blur(24px)",
|
|
2386
|
+
WebkitBackdropFilter: "blur(24px)",
|
|
2387
|
+
border: "2px solid rgba(255, 255, 255, 0.3)",
|
|
2388
|
+
boxShadow: `
|
|
2389
|
+
0 12px 48px rgba(255, 255, 255, 0.15),
|
|
2390
|
+
0 4px 16px rgba(255, 255, 255, 0.1),
|
|
2391
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.4)
|
|
2392
|
+
`
|
|
2393
|
+
}
|
|
2394
|
+
},
|
|
2395
|
+
/* @__PURE__ */ React6.createElement(
|
|
2396
|
+
"div",
|
|
2397
|
+
{
|
|
2398
|
+
className: "absolute top-0 left-0 right-0 h-1 margin-auto",
|
|
2399
|
+
style: {
|
|
2400
|
+
background: "linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent)"
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
),
|
|
2404
|
+
/* @__PURE__ */ React6.createElement(
|
|
2405
|
+
"h1",
|
|
2406
|
+
{
|
|
2407
|
+
className: "text-5xl font-bold text-white mb-8 relative",
|
|
2408
|
+
style: {
|
|
2409
|
+
textShadow: "0 4px 16px rgba(255, 255, 255, 0.3), 0 2px 8px rgba(0, 0, 0, 0.5)"
|
|
2410
|
+
}
|
|
2411
|
+
},
|
|
2412
|
+
scriptName,
|
|
2413
|
+
/* @__PURE__ */ React6.createElement(
|
|
2414
|
+
"div",
|
|
2415
|
+
{
|
|
2416
|
+
className: "absolute -bottom-3 left-1/2 transform -translate-x-1/2 h-1 rounded-full",
|
|
2417
|
+
style: {
|
|
2418
|
+
width: "60%",
|
|
2419
|
+
background: "linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent)"
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
)
|
|
2423
|
+
),
|
|
2424
|
+
/* @__PURE__ */ React6.createElement(
|
|
2425
|
+
"div",
|
|
2426
|
+
{
|
|
2427
|
+
className: "inline-block px-10 py-4 rounded-2xl font-bold text-xl text-white transition-all hover:scale-110 active:scale-95",
|
|
2428
|
+
style: {
|
|
2429
|
+
background: "linear-gradient(135deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0.25))",
|
|
2430
|
+
backdropFilter: "blur(16px)",
|
|
2431
|
+
WebkitBackdropFilter: "blur(16px)",
|
|
2432
|
+
border: "2px solid rgba(255, 255, 255, 0.4)",
|
|
2433
|
+
boxShadow: `
|
|
2434
|
+
0 8px 32px rgba(255, 255, 255, 0.2),
|
|
2435
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.5)
|
|
2436
|
+
`,
|
|
2437
|
+
textShadow: "0 2px 8px rgba(0, 0, 0, 0.3)",
|
|
2438
|
+
animation: "pulse 2s ease-in-out infinite"
|
|
2439
|
+
}
|
|
2440
|
+
},
|
|
2441
|
+
/* @__PURE__ */ React6.createElement("span", { className: "relative z-10" }, startText)
|
|
2442
|
+
),
|
|
2443
|
+
/* @__PURE__ */ React6.createElement(
|
|
2444
|
+
"div",
|
|
2445
|
+
{
|
|
2446
|
+
className: "absolute bottom-0 left-0 right-0 h-px",
|
|
2447
|
+
style: {
|
|
2448
|
+
background: "linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent)"
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
)
|
|
2452
|
+
)
|
|
2453
|
+
)
|
|
2454
|
+
)
|
|
2455
|
+
);
|
|
2456
|
+
let portalContainer = document.getElementById("start-screen-portal-root");
|
|
2457
|
+
if (!portalContainer) {
|
|
2458
|
+
portalContainer = document.createElement("div");
|
|
2459
|
+
portalContainer.id = "start-screen-portal-root";
|
|
2460
|
+
document.body.appendChild(portalContainer);
|
|
2461
|
+
}
|
|
2462
|
+
portalContainer.style.cssText = `
|
|
2463
|
+
position: fixed;
|
|
2464
|
+
top: 0;
|
|
2465
|
+
left: 0;
|
|
2466
|
+
right: 0;
|
|
2467
|
+
bottom: 0;
|
|
2468
|
+
width: 100vw;
|
|
2469
|
+
height: 100vh;
|
|
2470
|
+
margin: 0;
|
|
2471
|
+
padding: 0;
|
|
2472
|
+
overflow: hidden;
|
|
2473
|
+
z-index: 999999;
|
|
2474
|
+
pointer-events: none;
|
|
2475
|
+
`.replace(/\s+/g, " ").trim();
|
|
2476
|
+
return createPortal(content, portalContainer);
|
|
2477
|
+
};
|
|
2478
|
+
StartScreen.displayName = "StartScreen";
|
|
2479
|
+
|
|
2480
|
+
// src/mmd/visual-novel/LoadingOverlay.tsx
|
|
2481
|
+
if (typeof document !== "undefined" && !document.getElementById("loading-overlay-animations")) {
|
|
2482
|
+
const style = document.createElement("style");
|
|
2483
|
+
style.id = "loading-overlay-animations";
|
|
2484
|
+
style.textContent = `
|
|
2485
|
+
@keyframes gradientShift {
|
|
2486
|
+
0%, 100% { background-position: 0% 50%; }
|
|
2487
|
+
50% { background-position: 100% 50%; }
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
@keyframes pulse {
|
|
2491
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
2492
|
+
50% { opacity: 0.9; transform: scale(1.05); }
|
|
2493
|
+
}
|
|
2494
|
+
`;
|
|
2495
|
+
document.head.appendChild(style);
|
|
2496
|
+
}
|
|
2497
|
+
var LoadingOverlay = ({
|
|
2498
|
+
isLoading = true,
|
|
2499
|
+
showStartScreen = false,
|
|
2500
|
+
scriptName = "",
|
|
2501
|
+
loadingText = "\u6B63\u5728\u51C6\u5907\u573A\u666F\u4E2D...",
|
|
2502
|
+
startText = "\u70B9\u51FB\u5F00\u59CB",
|
|
2503
|
+
onStart,
|
|
2504
|
+
className = ""
|
|
2505
|
+
}) => {
|
|
2506
|
+
return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
|
|
2507
|
+
LoadingScreen,
|
|
2508
|
+
{
|
|
2509
|
+
isLoading,
|
|
2510
|
+
loadingText,
|
|
2511
|
+
className
|
|
2512
|
+
}
|
|
2513
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2514
|
+
StartScreen,
|
|
2515
|
+
{
|
|
2516
|
+
showStartScreen,
|
|
2517
|
+
scriptName,
|
|
2518
|
+
startText,
|
|
2519
|
+
onStart,
|
|
2520
|
+
className
|
|
2521
|
+
}
|
|
2522
|
+
));
|
|
2523
|
+
};
|
|
2524
|
+
LoadingOverlay.displayName = "LoadingOverlay";
|
|
2525
|
+
|
|
2526
|
+
// src/mmd/visual-novel/MMDVisualNovel.tsx
|
|
2527
|
+
var MMDVisualNovel = forwardRef(
|
|
2528
|
+
({
|
|
2529
|
+
script,
|
|
2530
|
+
stage,
|
|
2531
|
+
mobileOptimization,
|
|
2532
|
+
dialogueTheme,
|
|
2533
|
+
autoStart = false,
|
|
2534
|
+
initialNodeIndex = 0,
|
|
2535
|
+
initialDialogueIndex = 0,
|
|
2536
|
+
onNodeChange,
|
|
2537
|
+
onDialogueChange,
|
|
2538
|
+
onScriptComplete,
|
|
2539
|
+
onError,
|
|
2540
|
+
showDebugInfo = false,
|
|
2541
|
+
showSkipButton = true,
|
|
2542
|
+
showAutoButton = true,
|
|
2543
|
+
showHistoryButton = true,
|
|
2544
|
+
className,
|
|
2545
|
+
style
|
|
2546
|
+
}, ref) => {
|
|
2547
|
+
const { nodes, loop = false } = script;
|
|
2548
|
+
const [currentNodeIndex, setCurrentNodeIndex] = useState(initialNodeIndex);
|
|
2549
|
+
const [currentDialogueIndex, setCurrentDialogueIndex] = useState(initialDialogueIndex);
|
|
2550
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
2551
|
+
const [isAnimationPlaying, setIsAnimationPlaying] = useState(false);
|
|
2552
|
+
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
2553
|
+
const [isAutoMode, setIsAutoMode] = useState(false);
|
|
2554
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
2555
|
+
const [showHistory, setShowHistory] = useState(false);
|
|
2556
|
+
const [history, setHistory] = useState([]);
|
|
2557
|
+
const [isStarted, setIsStarted] = useState(autoStart);
|
|
2558
|
+
const playerRef = useRef(null);
|
|
2559
|
+
const containerRef = useRef(null);
|
|
2560
|
+
const autoTimerRef = useRef(null);
|
|
2561
|
+
const typingCompleteRef = useRef(false);
|
|
2562
|
+
const isStartedRef = useRef(autoStart);
|
|
2563
|
+
const currentNode = nodes[currentNodeIndex];
|
|
2564
|
+
const currentDialogue = currentNode?.dialogues[currentDialogueIndex] || null;
|
|
2565
|
+
const addToHistory = useCallback((dialogue, nodeIndex, dialogueIndex) => {
|
|
2566
|
+
setHistory((prev) => [
|
|
2567
|
+
...prev,
|
|
2568
|
+
{
|
|
2569
|
+
nodeIndex,
|
|
2570
|
+
dialogueIndex,
|
|
2571
|
+
speaker: dialogue.speaker,
|
|
2572
|
+
text: dialogue.text,
|
|
2573
|
+
timestamp: Date.now()
|
|
2574
|
+
}
|
|
2575
|
+
]);
|
|
2576
|
+
}, []);
|
|
2577
|
+
const goToNextDialogue = useCallback(() => {
|
|
2578
|
+
if (!currentNode) return;
|
|
2579
|
+
if (autoTimerRef.current) {
|
|
2580
|
+
clearTimeout(autoTimerRef.current);
|
|
2581
|
+
autoTimerRef.current = null;
|
|
2582
|
+
}
|
|
2583
|
+
const nextDialogueIndex = currentDialogueIndex + 1;
|
|
2584
|
+
if (nextDialogueIndex < currentNode.dialogues.length && currentNode?.dialogues[nextDialogueIndex] !== void 0) {
|
|
2585
|
+
const nextDialogue = currentNode.dialogues[nextDialogueIndex];
|
|
2586
|
+
setCurrentDialogueIndex(nextDialogueIndex);
|
|
2587
|
+
addToHistory(nextDialogue, currentNodeIndex, nextDialogueIndex);
|
|
2588
|
+
onDialogueChange?.(nextDialogue, nextDialogueIndex, currentNodeIndex);
|
|
2589
|
+
typingCompleteRef.current = false;
|
|
2590
|
+
} else {
|
|
2591
|
+
const nextNodeIndex = currentNodeIndex + 1;
|
|
2592
|
+
if (nextNodeIndex < nodes.length) {
|
|
2593
|
+
goToNode(nextNodeIndex);
|
|
2594
|
+
} else if (loop) {
|
|
2595
|
+
goToNode(0);
|
|
2596
|
+
} else {
|
|
2597
|
+
onScriptComplete?.();
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}, [currentNode, currentDialogueIndex, currentNodeIndex, nodes.length, loop, addToHistory, onDialogueChange, onScriptComplete]);
|
|
2601
|
+
const goToNode = useCallback(
|
|
2602
|
+
(nodeIndex) => {
|
|
2603
|
+
if (nodeIndex < 0 || nodeIndex >= nodes.length) return;
|
|
2604
|
+
if (isTransitioning) return;
|
|
2605
|
+
const node = nodes[nodeIndex];
|
|
2606
|
+
if (!node) return;
|
|
2607
|
+
console.log(`[MMDVisualNovel] Transitioning to node ${nodeIndex}`);
|
|
2608
|
+
setIsTransitioning(true);
|
|
2609
|
+
setIsLoading(true);
|
|
2610
|
+
setIsAnimationPlaying(false);
|
|
2611
|
+
setTimeout(() => {
|
|
2612
|
+
setCurrentNodeIndex(nodeIndex);
|
|
2613
|
+
setCurrentDialogueIndex(0);
|
|
2614
|
+
typingCompleteRef.current = false;
|
|
2615
|
+
if (node.dialogues.length > 0 && node?.dialogues[0] !== void 0) {
|
|
2616
|
+
addToHistory(node.dialogues[0], nodeIndex, 0);
|
|
2617
|
+
}
|
|
2618
|
+
onNodeChange?.(node, nodeIndex);
|
|
2619
|
+
if (node.dialogues.length > 0 && node?.dialogues[0] !== void 0) {
|
|
2620
|
+
onDialogueChange?.(node.dialogues[0], 0, nodeIndex);
|
|
2621
|
+
}
|
|
2622
|
+
setTimeout(() => {
|
|
2623
|
+
setIsTransitioning(false);
|
|
2624
|
+
console.log(`[MMDVisualNovel] Transition to node ${nodeIndex} completed, waiting for model load`);
|
|
2625
|
+
}, 100);
|
|
2626
|
+
}, 300);
|
|
2627
|
+
},
|
|
2628
|
+
[nodes, isTransitioning, addToHistory, onNodeChange, onDialogueChange]
|
|
2629
|
+
);
|
|
2630
|
+
const goToDialogue = useCallback(
|
|
2631
|
+
(dialogueIndex) => {
|
|
2632
|
+
if (!currentNode) return;
|
|
2633
|
+
const dialogue = currentNode.dialogues[dialogueIndex];
|
|
2634
|
+
if (dialogueIndex < 0 || dialogueIndex >= currentNode.dialogues.length || dialogue === void 0) return;
|
|
2635
|
+
setCurrentDialogueIndex(dialogueIndex);
|
|
2636
|
+
addToHistory(dialogue, currentNodeIndex, dialogueIndex);
|
|
2637
|
+
onDialogueChange?.(dialogue, dialogueIndex, currentNodeIndex);
|
|
2638
|
+
typingCompleteRef.current = false;
|
|
2639
|
+
},
|
|
2640
|
+
[currentNode, currentNodeIndex, addToHistory, onDialogueChange]
|
|
2641
|
+
);
|
|
2642
|
+
const handleDialogueClick = useCallback(() => {
|
|
2643
|
+
if (!typingCompleteRef.current) {
|
|
2644
|
+
typingCompleteRef.current = true;
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
goToNextDialogue();
|
|
2648
|
+
}, [goToNextDialogue]);
|
|
2649
|
+
const handleTypingComplete = useCallback(() => {
|
|
2650
|
+
typingCompleteRef.current = true;
|
|
2651
|
+
setIsTyping(false);
|
|
2652
|
+
if (isAutoMode || currentDialogue?.waitForClick === false) {
|
|
2653
|
+
const delay = currentDialogue?.autoDelay ?? 2e3;
|
|
2654
|
+
autoTimerRef.current = setTimeout(() => {
|
|
2655
|
+
goToNextDialogue();
|
|
2656
|
+
}, delay);
|
|
2657
|
+
}
|
|
2658
|
+
}, [isAutoMode, currentDialogue, goToNextDialogue]);
|
|
2659
|
+
useEffect(() => {
|
|
2660
|
+
if (currentDialogue) {
|
|
2661
|
+
setIsTyping(true);
|
|
2662
|
+
typingCompleteRef.current = false;
|
|
2663
|
+
const text = currentDialogue.text;
|
|
2664
|
+
const speed = currentDialogue.typeSpeed ?? 50;
|
|
2665
|
+
const typingDuration = text.length * speed;
|
|
2666
|
+
const timer = setTimeout(() => {
|
|
2667
|
+
handleTypingComplete();
|
|
2668
|
+
}, typingDuration);
|
|
2669
|
+
return () => clearTimeout(timer);
|
|
2670
|
+
}
|
|
2671
|
+
return void 0;
|
|
2672
|
+
}, [currentDialogue, handleTypingComplete]);
|
|
2673
|
+
const toggleAutoMode = useCallback(() => {
|
|
2674
|
+
setIsAutoMode((prev) => !prev);
|
|
2675
|
+
}, []);
|
|
2676
|
+
const handleSkip = useCallback(() => {
|
|
2677
|
+
const nextNodeIndex = currentNodeIndex + 1;
|
|
2678
|
+
if (nextNodeIndex < nodes.length) {
|
|
2679
|
+
goToNode(nextNodeIndex);
|
|
2680
|
+
} else if (loop) {
|
|
2681
|
+
goToNode(0);
|
|
2682
|
+
} else {
|
|
2683
|
+
onScriptComplete?.();
|
|
2684
|
+
}
|
|
2685
|
+
}, [currentNodeIndex, nodes.length, loop, goToNode, onScriptComplete]);
|
|
2686
|
+
const handleStart = useCallback(() => {
|
|
2687
|
+
setIsStarted(true);
|
|
2688
|
+
isStartedRef.current = true;
|
|
2689
|
+
if (currentNode && currentNode.dialogues.length > 0 && currentNode?.dialogues[0] !== void 0) {
|
|
2690
|
+
addToHistory(currentNode?.dialogues[0], currentNodeIndex, 0);
|
|
2691
|
+
}
|
|
2692
|
+
setTimeout(() => {
|
|
2693
|
+
playerRef.current?.play();
|
|
2694
|
+
}, 100);
|
|
2695
|
+
}, [currentNode, currentNodeIndex, addToHistory]);
|
|
2696
|
+
useImperativeHandle(
|
|
2697
|
+
ref,
|
|
2698
|
+
() => ({
|
|
2699
|
+
goToNode,
|
|
2700
|
+
goToDialogue,
|
|
2701
|
+
getCurrentNodeIndex: () => currentNodeIndex,
|
|
2702
|
+
getCurrentDialogueIndex: () => currentDialogueIndex,
|
|
2703
|
+
getHistory: () => history,
|
|
2704
|
+
setAutoMode: setIsAutoMode,
|
|
2705
|
+
skipTyping: () => {
|
|
2706
|
+
typingCompleteRef.current = true;
|
|
2707
|
+
}
|
|
2708
|
+
}),
|
|
2709
|
+
[goToNode, goToDialogue, currentNodeIndex, currentDialogueIndex, history]
|
|
2710
|
+
);
|
|
2711
|
+
useEffect(() => {
|
|
2712
|
+
if (autoStart && currentNode && currentNode.dialogues.length > 0 && history.length === 0 && currentNode?.dialogues[0] !== void 0) {
|
|
2713
|
+
addToHistory(currentNode?.dialogues[0], currentNodeIndex, 0);
|
|
2714
|
+
}
|
|
2715
|
+
}, [autoStart, currentNode, currentNodeIndex, history.length, addToHistory]);
|
|
2716
|
+
useEffect(() => {
|
|
2717
|
+
return () => {
|
|
2718
|
+
if (autoTimerRef.current) {
|
|
2719
|
+
clearTimeout(autoTimerRef.current);
|
|
2720
|
+
}
|
|
2721
|
+
};
|
|
2722
|
+
}, []);
|
|
2723
|
+
if (!currentNode) {
|
|
2724
|
+
return /* @__PURE__ */ React6.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u5267\u672C\u4E3A\u7A7A");
|
|
2725
|
+
}
|
|
2726
|
+
return /* @__PURE__ */ React6.createElement(
|
|
2727
|
+
"div",
|
|
2728
|
+
{
|
|
2729
|
+
ref: containerRef,
|
|
2730
|
+
className: `relative bg-black ${className}`,
|
|
2731
|
+
style: { width: "100%", height: "100%", overflow: "hidden", ...style }
|
|
2732
|
+
},
|
|
2733
|
+
/* @__PURE__ */ React6.createElement(
|
|
2734
|
+
"div",
|
|
2735
|
+
{
|
|
2736
|
+
className: "absolute inset-0 w-full h-full",
|
|
2737
|
+
style: {
|
|
2738
|
+
zIndex: 0,
|
|
2739
|
+
// 在加载期间隐藏,避免看到模型加载过程
|
|
2740
|
+
opacity: isLoading || isTransitioning || !isAnimationPlaying ? 0 : 1,
|
|
2741
|
+
transition: "opacity 0.3s ease-in-out"
|
|
2742
|
+
}
|
|
2743
|
+
},
|
|
2744
|
+
!isTransitioning && /* @__PURE__ */ React6.createElement(
|
|
2745
|
+
MMDPlayerBase,
|
|
2746
|
+
{
|
|
2747
|
+
key: currentNode.id,
|
|
2748
|
+
ref: playerRef,
|
|
2749
|
+
resources: currentNode.resources,
|
|
2750
|
+
stage,
|
|
2751
|
+
autoPlay: isStarted,
|
|
2752
|
+
loop: currentNode.loopAnimation === true,
|
|
2753
|
+
mobileOptimization,
|
|
2754
|
+
onLoad: () => {
|
|
2755
|
+
console.log("[MMDVisualNovel] MMDPlayerBase onLoad called");
|
|
2756
|
+
setIsLoading(false);
|
|
2757
|
+
if (isStartedRef.current) {
|
|
2758
|
+
console.log("[MMDVisualNovel] Game already started, triggering play");
|
|
2759
|
+
setTimeout(() => {
|
|
2760
|
+
playerRef.current?.play();
|
|
2761
|
+
}, 100);
|
|
2762
|
+
}
|
|
2763
|
+
},
|
|
2764
|
+
onPlay: () => {
|
|
2765
|
+
console.log("[MMDVisualNovel] MMDPlayerBase onPlay called");
|
|
2766
|
+
setIsAnimationPlaying(true);
|
|
2767
|
+
},
|
|
2768
|
+
onError
|
|
2769
|
+
}
|
|
2770
|
+
)
|
|
2771
|
+
),
|
|
2772
|
+
/* @__PURE__ */ React6.createElement(
|
|
2773
|
+
LoadingOverlay,
|
|
2774
|
+
{
|
|
2775
|
+
isLoading: (() => {
|
|
2776
|
+
const shouldShowLoading = (isLoading || isTransitioning || !isAnimationPlaying) && isStarted;
|
|
2777
|
+
console.log("[MMDVisualNovel] LoadingOverlay conditions:", {
|
|
2778
|
+
isLoading,
|
|
2779
|
+
isTransitioning,
|
|
2780
|
+
isAnimationPlaying,
|
|
2781
|
+
isStarted,
|
|
2782
|
+
shouldShowLoading
|
|
2783
|
+
});
|
|
2784
|
+
return shouldShowLoading;
|
|
2785
|
+
})(),
|
|
2786
|
+
showStartScreen: !isStarted,
|
|
2787
|
+
scriptName: script.name,
|
|
2788
|
+
loadingText: "\u6B63\u5728\u51C6\u5907\u573A\u666F\u4E2D...",
|
|
2789
|
+
startText: "\u70B9\u51FB\u5F00\u59CB",
|
|
2790
|
+
onStart: handleStart
|
|
2791
|
+
}
|
|
2792
|
+
),
|
|
2793
|
+
(() => {
|
|
2794
|
+
const shouldShow = isStarted && isAnimationPlaying && currentDialogue && !showHistory;
|
|
2795
|
+
console.log("[MMDVisualNovel] DialogueBox render condition:", {
|
|
2796
|
+
isStarted,
|
|
2797
|
+
isAnimationPlaying,
|
|
2798
|
+
hasDialogue: !!currentDialogue,
|
|
2799
|
+
showHistory,
|
|
2800
|
+
shouldShow,
|
|
2801
|
+
dialogue: currentDialogue
|
|
2802
|
+
});
|
|
2803
|
+
return shouldShow ? /* @__PURE__ */ React6.createElement(
|
|
2804
|
+
DialogueBox,
|
|
2805
|
+
{
|
|
2806
|
+
dialogue: currentDialogue,
|
|
2807
|
+
theme: dialogueTheme,
|
|
2808
|
+
isTyping,
|
|
2809
|
+
isAutoMode,
|
|
2810
|
+
onClick: handleDialogueClick,
|
|
2811
|
+
onSkipTyping: () => {
|
|
2812
|
+
typingCompleteRef.current = true;
|
|
2813
|
+
},
|
|
2814
|
+
onToggleAuto: toggleAutoMode,
|
|
2815
|
+
onOpenHistory: () => setShowHistory(true),
|
|
2816
|
+
onSkip: handleSkip,
|
|
2817
|
+
showControls: true,
|
|
2818
|
+
showSkipButton,
|
|
2819
|
+
showAutoButton,
|
|
2820
|
+
showHistoryButton
|
|
2821
|
+
}
|
|
2822
|
+
) : null;
|
|
2823
|
+
})(),
|
|
2824
|
+
showHistory && /* @__PURE__ */ React6.createElement(
|
|
2825
|
+
HistoryPanel,
|
|
2826
|
+
{
|
|
2827
|
+
history,
|
|
2828
|
+
theme: dialogueTheme,
|
|
2829
|
+
onClose: () => setShowHistory(false)
|
|
2830
|
+
}
|
|
2831
|
+
)
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2834
|
+
);
|
|
2835
|
+
MMDVisualNovel.displayName = "MMDVisualNovel";
|
|
1591
2836
|
|
|
1592
|
-
export { MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, loadAmmo };
|
|
2837
|
+
export { DialogueBox, HistoryPanel, LoadingOverlay, LoadingScreen, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, StartScreen, loadAmmo };
|
|
1593
2838
|
//# sourceMappingURL=index.mjs.map
|
|
1594
2839
|
//# sourceMappingURL=index.mjs.map
|