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.
Files changed (53) hide show
  1. package/dist/{UniversalFileService-C1rUWWU-.d.ts → UniversalFileService-BuHN-jrR.d.ts} +46 -2
  2. package/dist/{UniversalFileService-DrCK0-NL.d.mts → UniversalFileService-CGGzYeeF.d.mts} +46 -2
  3. package/dist/{chunk-3XG5OHFD.mjs → chunk-CIVO4R6N.mjs} +2 -2
  4. package/dist/{chunk-3XG5OHFD.mjs.map → chunk-CIVO4R6N.mjs.map} +1 -1
  5. package/dist/chunk-EV6BCVOQ.mjs +204 -0
  6. package/dist/chunk-EV6BCVOQ.mjs.map +1 -0
  7. package/dist/chunk-W35VTQAW.js +211 -0
  8. package/dist/chunk-W35VTQAW.js.map +1 -0
  9. package/dist/{chunk-HWJ34NL6.js → chunk-ZRAW3HXA.js} +2 -2
  10. package/dist/{chunk-HWJ34NL6.js.map → chunk-ZRAW3HXA.js.map} +1 -1
  11. package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
  12. package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
  13. package/dist/mmd/admin/index.d.mts +8 -1115
  14. package/dist/mmd/admin/index.d.ts +8 -1115
  15. package/dist/mmd/admin/index.js +98 -248
  16. package/dist/mmd/admin/index.js.map +1 -1
  17. package/dist/mmd/admin/index.mjs +75 -244
  18. package/dist/mmd/admin/index.mjs.map +1 -1
  19. package/dist/mmd/index.d.mts +265 -3
  20. package/dist/mmd/index.d.ts +265 -3
  21. package/dist/mmd/index.js +1266 -15
  22. package/dist/mmd/index.js.map +1 -1
  23. package/dist/mmd/index.mjs +1261 -16
  24. package/dist/mmd/index.mjs.map +1 -1
  25. package/dist/mmd/server/index.d.mts +138 -0
  26. package/dist/mmd/server/index.d.ts +138 -0
  27. package/dist/mmd/server/index.js +245 -0
  28. package/dist/mmd/server/index.js.map +1 -0
  29. package/dist/mmd/server/index.mjs +207 -0
  30. package/dist/mmd/server/index.mjs.map +1 -0
  31. package/dist/testYourself/index.d.mts +145 -0
  32. package/dist/testYourself/index.d.ts +145 -0
  33. package/dist/testYourself/index.js +1004 -0
  34. package/dist/testYourself/index.js.map +1 -0
  35. package/dist/testYourself/index.mjs +993 -0
  36. package/dist/testYourself/index.mjs.map +1 -0
  37. package/dist/{types-C2ale3d9.d.mts → types-Bc_p-zAR.d.mts} +1 -1
  38. package/dist/{types-C2ale3d9.d.ts → types-Bc_p-zAR.d.ts} +1 -1
  39. package/dist/{types-Dg-U_chI.d.mts → types-CK4We_aI.d.mts} +13 -1
  40. package/dist/{types-Dg-U_chI.d.ts → types-CK4We_aI.d.ts} +13 -1
  41. package/dist/universalFile/index.d.mts +3 -3
  42. package/dist/universalFile/index.d.ts +3 -3
  43. package/dist/universalFile/index.js +48 -10
  44. package/dist/universalFile/index.js.map +1 -1
  45. package/dist/universalFile/index.mjs +43 -5
  46. package/dist/universalFile/index.mjs.map +1 -1
  47. package/dist/universalFile/server/index.d.mts +3 -3
  48. package/dist/universalFile/server/index.d.ts +3 -3
  49. package/dist/universalFile/server/index.js +239 -7
  50. package/dist/universalFile/server/index.js.map +1 -1
  51. package/dist/universalFile/server/index.mjs +234 -2
  52. package/dist/universalFile/server/index.mjs.map +1 -1
  53. package/package.json +19 -1
@@ -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
- renderer.setPixelRatio(mobileOptimization.enabled ? mobileOptimization.pixelRatio || 1 : window.devicePixelRatio);
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
- loader.load(
407
- resources.stageModelPath,
408
- (stageMesh) => {
409
- if (checkCancelled()) return;
410
- stageMesh.castShadow = true;
411
- stageMesh.receiveShadow = true;
412
- scene.add(stageMesh);
413
- },
414
- void 0,
415
- (err) => console.error("Failed to load stage:", err)
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