sa2kit 1.6.14 → 1.6.15

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 (43) hide show
  1. package/dist/audioDetection/index.js.map +1 -1
  2. package/dist/audioDetection/index.mjs.map +1 -1
  3. package/dist/auth/index.js +3 -3
  4. package/dist/auth/index.mjs +1 -1
  5. package/dist/{chunk-GGGTJETD.mjs → chunk-3WOAPLEG.mjs} +3 -3
  6. package/dist/{chunk-GGGTJETD.mjs.map → chunk-3WOAPLEG.mjs.map} +1 -1
  7. package/dist/{chunk-5D7ZFZIM.js → chunk-5YQ5B7IZ.js} +2 -2
  8. package/dist/{chunk-5D7ZFZIM.js.map → chunk-5YQ5B7IZ.js.map} +1 -1
  9. package/dist/{chunk-VZFHU553.mjs → chunk-6MQUBPKB.mjs} +2 -2
  10. package/dist/{chunk-VZFHU553.mjs.map → chunk-6MQUBPKB.mjs.map} +1 -1
  11. package/dist/{chunk-LFM5QSFW.js → chunk-A3UP56MS.js} +2 -2
  12. package/dist/{chunk-LFM5QSFW.js.map → chunk-A3UP56MS.js.map} +1 -1
  13. package/dist/{chunk-66EHKQVS.js → chunk-CLKKZSPZ.js} +2 -2
  14. package/dist/{chunk-66EHKQVS.js.map → chunk-CLKKZSPZ.js.map} +1 -1
  15. package/dist/{chunk-MRGFYQTC.mjs → chunk-CNTILN5J.mjs} +2 -2
  16. package/dist/{chunk-MRGFYQTC.mjs.map → chunk-CNTILN5J.mjs.map} +1 -1
  17. package/dist/{chunk-UUM5BIOU.js → chunk-E7RGBAYJ.js} +7 -7
  18. package/dist/{chunk-UUM5BIOU.js.map → chunk-E7RGBAYJ.js.map} +1 -1
  19. package/dist/{chunk-TGL6BATG.mjs → chunk-MW4BCIZC.mjs} +2 -2
  20. package/dist/{chunk-TGL6BATG.mjs.map → chunk-MW4BCIZC.mjs.map} +1 -1
  21. package/dist/imageCrop/index.js.map +1 -1
  22. package/dist/imageCrop/index.mjs.map +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mjs +1 -1
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/mmd/index.d.mts +169 -5
  28. package/dist/mmd/index.d.ts +169 -5
  29. package/dist/mmd/index.js +463 -67
  30. package/dist/mmd/index.js.map +1 -1
  31. package/dist/mmd/index.mjs +463 -69
  32. package/dist/mmd/index.mjs.map +1 -1
  33. package/dist/music/index.js +16 -16
  34. package/dist/music/index.mjs +2 -2
  35. package/dist/music/server/index.js +8 -8
  36. package/dist/music/server/index.mjs +1 -1
  37. package/dist/testYourself/admin/index.js +3 -3
  38. package/dist/testYourself/admin/index.mjs +1 -1
  39. package/dist/testYourself/index.js +7 -7
  40. package/dist/testYourself/index.js.map +1 -1
  41. package/dist/testYourself/index.mjs +2 -2
  42. package/dist/testYourself/index.mjs.map +1 -1
  43. package/package.json +17 -19
@@ -1,12 +1,12 @@
1
- import { useMusic } from '../chunk-GGGTJETD.mjs';
2
- import '../chunk-TGL6BATG.mjs';
1
+ import { useMusic } from '../chunk-3WOAPLEG.mjs';
2
+ import '../chunk-MW4BCIZC.mjs';
3
3
  import { PMXParser } from '../chunk-VRTRSEEH.mjs';
4
4
  export { PMXParser } from '../chunk-VRTRSEEH.mjs';
5
5
  import '../chunk-BJTO5JO5.mjs';
6
- import React10, { forwardRef, useRef, useEffect, useImperativeHandle, useState, useCallback } from 'react';
6
+ import React10, { forwardRef, useRef, useState, useEffect, useImperativeHandle, useCallback, useMemo } from 'react';
7
7
  import * as THREE2 from 'three';
8
8
  import { OutlineEffect, OrbitControls, MMDLoader, MMDAnimationHelper } from 'three-stdlib';
9
- import { AlertCircle, Camera, Settings, RefreshCw, CameraOff, X, Check, SkipBack, Pause, Play, SkipForward, Repeat, Repeat1, Shuffle, ListMusic, Music, Search, Loader2, Grid3x3, Minimize, Maximize, Video, User, Image as Image$1 } from 'lucide-react';
9
+ import { Sparkles, AlertCircle, Camera, Settings, RefreshCw, CameraOff, X, RotateCcw, SkipBack, Pause, Play, SkipForward, Repeat, Repeat1, Shuffle, ListMusic, Music, Search, Loader2, ChevronDown, Grid3x3, Minimize, Maximize, Video, Check, User, Image as Image$1 } from 'lucide-react';
10
10
  import { createPortal } from 'react-dom';
11
11
 
12
12
  // src/mmd/pmx/editor/PMXEditor.ts
@@ -1153,6 +1153,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
1153
1153
  const audioRef = useRef(null);
1154
1154
  const audioListenerRef = useRef(null);
1155
1155
  const audioLoaderRef = useRef(new THREE2.AudioLoader());
1156
+ const [isAudioSystemReady, setIsAudioSystemReady] = useState(false);
1156
1157
  const latestCallbacks = useRef({ onPlay, onPause, onEnded, onTimeUpdate });
1157
1158
  useEffect(() => {
1158
1159
  latestCallbacks.current = { onPlay, onPause, onEnded, onTimeUpdate };
@@ -1170,6 +1171,10 @@ var MMDPlayerBase = forwardRef((props, ref) => {
1170
1171
  useImperativeHandle(ref, () => ({
1171
1172
  play: () => {
1172
1173
  if (!isReadyRef.current) return;
1174
+ if (isPlayingRef.current) {
1175
+ console.log("[MMDPlayerBase] play() called but already playing, skipping");
1176
+ return;
1177
+ }
1173
1178
  console.log("[MMDPlayerBase] play() called, audioRef:", !!audioRef.current, "isPlaying:", audioRef.current?.isPlaying);
1174
1179
  isPlayingRef.current = true;
1175
1180
  if (!clockRef.current.running) clockRef.current.start();
@@ -1412,6 +1417,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
1412
1417
  afterglow: 2
1413
1418
  });
1414
1419
  helperRef.current = helper;
1420
+ setIsAudioSystemReady(true);
1415
1421
  const loadModelPromise = new Promise((resolve, reject) => {
1416
1422
  if (resources.motionPath) {
1417
1423
  loader.loadWithAnimation(
@@ -1829,6 +1835,7 @@ ${errorMessage}
1829
1835
  console.warn("[MMDPlayerBase] Error cleaning up AnimationHelper:", e);
1830
1836
  }
1831
1837
  helperRef.current = null;
1838
+ setIsAudioSystemReady(false);
1832
1839
  }
1833
1840
  animationClipRef.current = null;
1834
1841
  if (axesHelperRef.current) {
@@ -1985,9 +1992,19 @@ ${errorMessage}
1985
1992
  };
1986
1993
  }, [resources.modelPath, resources.motionPath, resources.stageModelPath, stage.enablePhysics, stage.physicsPath]);
1987
1994
  useEffect(() => {
1988
- if (!audioListenerRef.current || !helperRef.current || !resources.audioPath) return;
1995
+ if (!isAudioSystemReady || !audioListenerRef.current || !helperRef.current || !resources.audioPath) {
1996
+ console.log("[MMDPlayerBase] \u{1F3B5} Audio loading skipped:", {
1997
+ isAudioSystemReady,
1998
+ hasListener: !!audioListenerRef.current,
1999
+ hasHelper: !!helperRef.current,
2000
+ audioPath: resources.audioPath
2001
+ });
2002
+ return;
2003
+ }
1989
2004
  const listener = audioListenerRef.current;
1990
2005
  const helper = helperRef.current;
2006
+ let retryTimer = null;
2007
+ console.log("[MMDPlayerBase] \u{1F3B5} Starting audio load for:", resources.audioPath);
1991
2008
  if (audioRef.current) {
1992
2009
  const oldSound = audioRef.current;
1993
2010
  if (oldSound.isPlaying) oldSound.stop();
@@ -2007,14 +2024,29 @@ ${errorMessage}
2007
2024
  delay: 0,
2008
2025
  duration: buffer.duration
2009
2026
  });
2027
+ console.log("[MMDPlayerBase] \u{1F3B5} Audio loaded, isPlayingRef:", isPlayingRef.current, "autoPlay:", autoPlay, "isReadyRef:", isReadyRef.current);
2010
2028
  if (isPlayingRef.current) {
2029
+ console.log("[MMDPlayerBase] \u{1F3B5} Playing audio immediately (isPlayingRef=true)");
2011
2030
  sound.play();
2031
+ } else if (autoPlay) {
2032
+ console.log("[MMDPlayerBase] \u{1F3B5} Audio loaded before autoPlay triggered, waiting for play state...");
2033
+ retryTimer = setTimeout(() => {
2034
+ if (isPlayingRef.current && audioRef.current && !audioRef.current.isPlaying) {
2035
+ console.log("[MMDPlayerBase] \u{1F3B5} Playing audio after delay (autoPlay=true)");
2036
+ audioRef.current.play();
2037
+ }
2038
+ }, 150);
2012
2039
  }
2013
2040
  },
2014
2041
  void 0,
2015
2042
  (err) => console.error("[MMDPlayerBase] Failed to load audio track:", err)
2016
2043
  );
2017
- }, [resources.audioPath, volume]);
2044
+ return () => {
2045
+ if (retryTimer) {
2046
+ clearTimeout(retryTimer);
2047
+ }
2048
+ };
2049
+ }, [resources.audioPath, volume, autoPlay, isAudioSystemReady]);
2018
2050
  useEffect(() => {
2019
2051
  if (!sceneRef.current) return;
2020
2052
  if (showAxes && !axesHelperRef.current) {
@@ -4414,7 +4446,9 @@ var StartScreen = ({
4414
4446
  settingsText = "\u6E38\u620F\u8BBE\u7F6E",
4415
4447
  aboutText = "\u5173\u4E8E\u4F5C\u54C1",
4416
4448
  onStart,
4417
- className = ""
4449
+ className = "",
4450
+ customSettingsContent,
4451
+ customAboutContent
4418
4452
  }) => {
4419
4453
  const [isMounted, setIsMounted] = useState(false);
4420
4454
  const [showSettings, setShowSettings] = useState(false);
@@ -4519,7 +4553,7 @@ var StartScreen = ({
4519
4553
  background: "rgba(248, 250, 252, 0.8)",
4520
4554
  borderColor: "rgba(203, 213, 225, 0.3)"
4521
4555
  } }, /* @__PURE__ */ React10.createElement("span", { className: "text-[8px] sm:text-[10px] md:text-xs tracking-[0.3em] sm:tracking-[0.5em] font-light uppercase", style: { color: "rgba(100, 116, 139, 0.5)" } }, "Ver 1.6.2 \u2014 ENGINE POWERED BY SA2KIT"))),
4522
- /* @__PURE__ */ React10.createElement(VNModal, { title: settingsText, show: showSettings, onClose: () => setShowSettings(false) }, /* @__PURE__ */ React10.createElement("div", { className: "space-y-6 sm:space-y-8 py-2 sm:py-4" }, /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-center text-xs sm:text-sm font-bold tracking-wider sm:tracking-widest", style: { color: "#64748b" } }, /* @__PURE__ */ React10.createElement("span", null, "MUSIC VOLUME"), /* @__PURE__ */ React10.createElement("span", null, "80%")), /* @__PURE__ */ React10.createElement("div", { className: "h-2.5 sm:h-3 rounded-full p-0.5 border", style: { background: "rgba(241, 245, 249, 0.5)", borderColor: "rgba(203, 213, 225, 0.4)" } }, /* @__PURE__ */ React10.createElement("div", { className: "h-full w-[80%] rounded-full", style: { background: "linear-gradient(to right, #22c55e, #4ade80)", boxShadow: "0 0 15px rgba(34, 197, 94, 0.5)" } }))), /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-center text-xs sm:text-sm font-bold tracking-wider sm:tracking-widest", style: { color: "#64748b" } }, /* @__PURE__ */ React10.createElement("span", null, "TEXT SPEED"), /* @__PURE__ */ React10.createElement("span", null, "NORMAL")), /* @__PURE__ */ React10.createElement("div", { className: "flex gap-2 sm:gap-3" }, ["SLOW", "NORMAL", "FAST"].map((s, i) => /* @__PURE__ */ React10.createElement(
4556
+ /* @__PURE__ */ React10.createElement(VNModal, { title: settingsText, show: showSettings, onClose: () => setShowSettings(false) }, customSettingsContent ? /* @__PURE__ */ React10.createElement("div", { className: "py-2 sm:py-4" }, customSettingsContent) : /* @__PURE__ */ React10.createElement("div", { className: "space-y-6 sm:space-y-8 py-2 sm:py-4" }, /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-center text-xs sm:text-sm font-bold tracking-wider sm:tracking-widest", style: { color: "#64748b" } }, /* @__PURE__ */ React10.createElement("span", null, "MUSIC VOLUME"), /* @__PURE__ */ React10.createElement("span", null, "80%")), /* @__PURE__ */ React10.createElement("div", { className: "h-2.5 sm:h-3 rounded-full p-0.5 border", style: { background: "rgba(241, 245, 249, 0.5)", borderColor: "rgba(203, 213, 225, 0.4)" } }, /* @__PURE__ */ React10.createElement("div", { className: "h-full w-[80%] rounded-full", style: { background: "linear-gradient(to right, #22c55e, #4ade80)", boxShadow: "0 0 15px rgba(34, 197, 94, 0.5)" } }))), /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-center text-xs sm:text-sm font-bold tracking-wider sm:tracking-widest", style: { color: "#64748b" } }, /* @__PURE__ */ React10.createElement("span", null, "TEXT SPEED"), /* @__PURE__ */ React10.createElement("span", null, "NORMAL")), /* @__PURE__ */ React10.createElement("div", { className: "flex gap-2 sm:gap-3" }, ["SLOW", "NORMAL", "FAST"].map((s, i) => /* @__PURE__ */ React10.createElement(
4523
4557
  "div",
4524
4558
  {
4525
4559
  key: s,
@@ -4536,7 +4570,7 @@ var StartScreen = ({
4536
4570
  },
4537
4571
  s
4538
4572
  )))), /* @__PURE__ */ React10.createElement("div", { className: "pt-3 sm:pt-4 flex flex-col sm:flex-row items-center justify-between gap-2 sm:gap-0 opacity-50 italic text-[10px] sm:text-xs border-t", style: { borderColor: "rgba(203, 213, 225, 0.3)" } }, /* @__PURE__ */ React10.createElement("span", null, "Auto Save Enabled"), /* @__PURE__ */ React10.createElement("span", null, "Cloud Sync Active")))),
4539
- /* @__PURE__ */ React10.createElement(VNModal, { title: aboutText, show: showAbout, onClose: () => setShowAbout(false) }, /* @__PURE__ */ React10.createElement("div", { className: "space-y-6 sm:space-y-8 py-2 sm:py-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center gap-3 sm:gap-6 p-4 sm:p-6 rounded-2xl sm:rounded-3xl border", style: { background: "rgba(241, 245, 249, 0.6)", borderColor: "rgba(203, 213, 225, 0.4)" } }, /* @__PURE__ */ React10.createElement("div", { className: "w-14 h-14 sm:w-20 sm:h-20 rounded-xl sm:rounded-2xl flex items-center justify-center text-2xl sm:text-3xl font-black text-white shadow-lg shrink-0", style: { background: "linear-gradient(to bottom right, #22c55e, #4ade80)" } }, "S2"), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("h3", { className: "text-lg sm:text-2xl font-black tracking-tight", style: { color: "#22c55e" } }, scriptName || "Project SA2"), /* @__PURE__ */ React10.createElement("p", { className: "text-[10px] sm:text-xs font-bold tracking-wider sm:tracking-widest mt-1 uppercase", style: { color: "rgba(100, 116, 139, 0.6)" } }, "Visual Novel Experience"))), /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4 px-1 sm:px-2" }, /* @__PURE__ */ React10.createElement("p", { className: "font-medium leading-relaxed text-sm sm:text-base", style: { color: "#475569" } }, "\u91C7\u7528 sa2kit \u5F15\u64CE\u6784\u5EFA\u7684\u65B0\u4E00\u4EE3\u5B9E\u65F6 3D \u89C6\u89C9\u5C0F\u8BF4\u3002\u7ED3\u5408\u4E86 MMD \u5B9E\u65F6\u6E32\u67D3\u6280\u672F\u4E0E\u4EA4\u4E92\u5F0F\u5267\u60C5\u5206\u652F\u7CFB\u7EDF\uFF0C\u81F4\u529B\u4E8E\u6253\u9020\u6781\u81F4\u7684\u6C89\u6D78\u5F0F\u53D9\u4E8B\u4F53\u9A8C\u3002")), /* @__PURE__ */ React10.createElement("div", { className: "grid grid-cols-2 gap-3 sm:gap-4 pt-4 sm:pt-6 border-t", style: { borderColor: "rgba(203, 213, 225, 0.3)" } }, /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React10.createElement("span", { className: "text-[9px] sm:text-[10px] font-bold tracking-wider sm:tracking-widest", style: { color: "rgba(100, 116, 139, 0.5)" } }, "DEVELOPER"), /* @__PURE__ */ React10.createElement("span", { className: "text-xs font-bold", style: { color: "#64748b" } }, "SA2KIT TEAM")), /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-1 text-right" }, /* @__PURE__ */ React10.createElement("span", { className: "text-[9px] sm:text-[10px] font-bold tracking-wider sm:tracking-widest", style: { color: "rgba(100, 116, 139, 0.5)" } }, "ENGINE"), /* @__PURE__ */ React10.createElement("span", { className: "text-xs font-bold", style: { color: "#64748b" } }, "THREE.JS / REACT"))))),
4573
+ /* @__PURE__ */ React10.createElement(VNModal, { title: aboutText, show: showAbout, onClose: () => setShowAbout(false) }, customAboutContent ? /* @__PURE__ */ React10.createElement("div", { className: "py-2 sm:py-4" }, customAboutContent) : /* @__PURE__ */ React10.createElement("div", { className: "space-y-6 sm:space-y-8 py-2 sm:py-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center gap-3 sm:gap-6 p-4 sm:p-6 rounded-2xl sm:rounded-3xl border", style: { background: "rgba(241, 245, 249, 0.6)", borderColor: "rgba(203, 213, 225, 0.4)" } }, /* @__PURE__ */ React10.createElement("div", { className: "w-14 h-14 sm:w-20 sm:h-20 rounded-xl sm:rounded-2xl flex items-center justify-center text-2xl sm:text-3xl font-black text-white shadow-lg shrink-0", style: { background: "linear-gradient(to bottom right, #22c55e, #4ade80)" } }, "S2"), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("h3", { className: "text-lg sm:text-2xl font-black tracking-tight", style: { color: "#22c55e" } }, scriptName || "Project SA2"), /* @__PURE__ */ React10.createElement("p", { className: "text-[10px] sm:text-xs font-bold tracking-wider sm:tracking-widest mt-1 uppercase", style: { color: "rgba(100, 116, 139, 0.6)" } }, "Visual Novel Experience"))), /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4 px-1 sm:px-2" }, /* @__PURE__ */ React10.createElement("p", { className: "font-medium leading-relaxed text-sm sm:text-base", style: { color: "#475569" } }, "\u91C7\u7528 sa2kit \u5F15\u64CE\u6784\u5EFA\u7684\u65B0\u4E00\u4EE3\u5B9E\u65F6 3D \u89C6\u89C9\u5C0F\u8BF4\u3002\u7ED3\u5408\u4E86 MMD \u5B9E\u65F6\u6E32\u67D3\u6280\u672F\u4E0E\u4EA4\u4E92\u5F0F\u5267\u60C5\u5206\u652F\u7CFB\u7EDF\uFF0C\u81F4\u529B\u4E8E\u6253\u9020\u6781\u81F4\u7684\u6C89\u6D78\u5F0F\u53D9\u4E8B\u4F53\u9A8C\u3002")), /* @__PURE__ */ React10.createElement("div", { className: "grid grid-cols-2 gap-3 sm:gap-4 pt-4 sm:pt-6 border-t", style: { borderColor: "rgba(203, 213, 225, 0.3)" } }, /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React10.createElement("span", { className: "text-[9px] sm:text-[10px] font-bold tracking-wider sm:tracking-widest", style: { color: "rgba(100, 116, 139, 0.5)" } }, "DEVELOPER"), /* @__PURE__ */ React10.createElement("span", { className: "text-xs font-bold", style: { color: "#64748b" } }, "SA2KIT TEAM")), /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-1 text-right" }, /* @__PURE__ */ React10.createElement("span", { className: "text-[9px] sm:text-[10px] font-bold tracking-wider sm:tracking-widest", style: { color: "rgba(100, 116, 139, 0.5)" } }, "ENGINE"), /* @__PURE__ */ React10.createElement("span", { className: "text-xs font-bold", style: { color: "#64748b" } }, "THREE.JS / REACT"))))),
4540
4574
  /* @__PURE__ */ React10.createElement("style", null, `
4541
4575
  @keyframes floatParticle {
4542
4576
  from { transform: translateY(0) translateX(0); opacity: 0; }
@@ -4620,7 +4654,9 @@ var LoadingOverlay = ({
4620
4654
  settingsText = "\u6E38\u620F\u8BBE\u7F6E",
4621
4655
  aboutText = "\u5173\u4E8E\u4F5C\u54C1",
4622
4656
  onStart,
4623
- className = ""
4657
+ className = "",
4658
+ customSettingsContent,
4659
+ customAboutContent
4624
4660
  }) => {
4625
4661
  return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
4626
4662
  LoadingScreen,
@@ -4638,7 +4674,9 @@ var LoadingOverlay = ({
4638
4674
  settingsText,
4639
4675
  aboutText,
4640
4676
  onStart,
4641
- className
4677
+ className,
4678
+ customSettingsContent,
4679
+ customAboutContent
4642
4680
  }
4643
4681
  ));
4644
4682
  };
@@ -5045,6 +5083,8 @@ var MMDVisualNovel = forwardRef(
5045
5083
  showHistoryButton = true,
5046
5084
  showLightingDebugPanel = false,
5047
5085
  lightingDebugInitialParams,
5086
+ customSettingsContent,
5087
+ customAboutContent,
5048
5088
  className,
5049
5089
  style
5050
5090
  }, ref) => {
@@ -5443,7 +5483,9 @@ var MMDVisualNovel = forwardRef(
5443
5483
  startText,
5444
5484
  settingsText,
5445
5485
  aboutText,
5446
- onStart: handleStart
5486
+ onStart: handleStart,
5487
+ customSettingsContent,
5488
+ customAboutContent
5447
5489
  }
5448
5490
  ),
5449
5491
  (() => {
@@ -5564,6 +5606,224 @@ var MMDVisualNovel = forwardRef(
5564
5606
  }
5565
5607
  );
5566
5608
  MMDVisualNovel.displayName = "MMDVisualNovel";
5609
+ var ModelSelectorSettings = ({
5610
+ characterModels,
5611
+ stageModels,
5612
+ selectedCharacterId,
5613
+ selectedStageId,
5614
+ onCharacterChange,
5615
+ onStageChange,
5616
+ characterLabel = "CHARACTER MODEL",
5617
+ stageLabel = "STAGE MODEL"
5618
+ }) => {
5619
+ return /* @__PURE__ */ React10.createElement("div", { className: "space-y-6 sm:space-y-8" }, /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-center text-xs sm:text-sm font-bold tracking-wider sm:tracking-widest", style: { color: "#64748b" } }, /* @__PURE__ */ React10.createElement("span", null, characterLabel), /* @__PURE__ */ React10.createElement("span", { className: "text-[10px] sm:text-xs opacity-60" }, characterModels.find((m) => m.id === selectedCharacterId)?.name || "\u672A\u9009\u62E9")), /* @__PURE__ */ React10.createElement("div", { className: "relative" }, /* @__PURE__ */ React10.createElement(
5620
+ "select",
5621
+ {
5622
+ value: selectedCharacterId,
5623
+ onChange: (e) => onCharacterChange(e.target.value),
5624
+ className: "w-full h-12 sm:h-14 px-4 sm:px-6 rounded-xl sm:rounded-2xl border appearance-none cursor-pointer font-medium text-sm sm:text-base transition-all focus:outline-none focus:ring-2 focus:ring-green-500/30 touch-manipulation",
5625
+ style: {
5626
+ background: "linear-gradient(135deg, rgba(248, 250, 252, 0.98), rgba(226, 232, 240, 0.95))",
5627
+ borderColor: "rgba(203, 213, 225, 0.6)",
5628
+ color: "#475569",
5629
+ boxShadow: "0 2px 8px rgba(100, 116, 139, 0.1)"
5630
+ }
5631
+ },
5632
+ characterModels.map((model) => /* @__PURE__ */ React10.createElement("option", { key: model.id, value: model.id }, model.name))
5633
+ ), /* @__PURE__ */ React10.createElement("div", { className: "absolute right-4 sm:right-6 top-1/2 -translate-y-1/2 pointer-events-none" }, /* @__PURE__ */ React10.createElement(
5634
+ "svg",
5635
+ {
5636
+ className: "w-4 h-4 sm:w-5 sm:h-5",
5637
+ fill: "none",
5638
+ stroke: "currentColor",
5639
+ viewBox: "0 0 24 24",
5640
+ style: { color: "#64748b" }
5641
+ },
5642
+ /* @__PURE__ */ React10.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })
5643
+ ))), characterModels.find((m) => m.id === selectedCharacterId)?.thumbnail && /* @__PURE__ */ React10.createElement("div", { className: "mt-2 flex justify-center" }, /* @__PURE__ */ React10.createElement(
5644
+ "img",
5645
+ {
5646
+ src: characterModels.find((m) => m.id === selectedCharacterId)?.thumbnail,
5647
+ alt: "Character preview",
5648
+ className: "h-20 sm:h-24 rounded-lg border object-cover",
5649
+ style: { borderColor: "rgba(203, 213, 225, 0.4)" }
5650
+ }
5651
+ ))), /* @__PURE__ */ React10.createElement("div", { className: "space-y-3 sm:space-y-4" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-center text-xs sm:text-sm font-bold tracking-wider sm:tracking-widest", style: { color: "#64748b" } }, /* @__PURE__ */ React10.createElement("span", null, stageLabel), /* @__PURE__ */ React10.createElement("span", { className: "text-[10px] sm:text-xs opacity-60" }, stageModels.find((m) => m.id === selectedStageId)?.name || "\u672A\u9009\u62E9")), /* @__PURE__ */ React10.createElement("div", { className: "relative" }, /* @__PURE__ */ React10.createElement(
5652
+ "select",
5653
+ {
5654
+ value: selectedStageId,
5655
+ onChange: (e) => onStageChange(e.target.value),
5656
+ className: "w-full h-12 sm:h-14 px-4 sm:px-6 rounded-xl sm:rounded-2xl border appearance-none cursor-pointer font-medium text-sm sm:text-base transition-all focus:outline-none focus:ring-2 focus:ring-green-500/30 touch-manipulation",
5657
+ style: {
5658
+ background: "linear-gradient(135deg, rgba(248, 250, 252, 0.98), rgba(226, 232, 240, 0.95))",
5659
+ borderColor: "rgba(203, 213, 225, 0.6)",
5660
+ color: "#475569",
5661
+ boxShadow: "0 2px 8px rgba(100, 116, 139, 0.1)"
5662
+ }
5663
+ },
5664
+ stageModels.map((model) => /* @__PURE__ */ React10.createElement("option", { key: model.id, value: model.id }, model.name))
5665
+ ), /* @__PURE__ */ React10.createElement("div", { className: "absolute right-4 sm:right-6 top-1/2 -translate-y-1/2 pointer-events-none" }, /* @__PURE__ */ React10.createElement(
5666
+ "svg",
5667
+ {
5668
+ className: "w-4 h-4 sm:w-5 sm:h-5",
5669
+ fill: "none",
5670
+ stroke: "currentColor",
5671
+ viewBox: "0 0 24 24",
5672
+ style: { color: "#64748b" }
5673
+ },
5674
+ /* @__PURE__ */ React10.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })
5675
+ ))), stageModels.find((m) => m.id === selectedStageId)?.thumbnail && /* @__PURE__ */ React10.createElement("div", { className: "mt-2 flex justify-center" }, /* @__PURE__ */ React10.createElement(
5676
+ "img",
5677
+ {
5678
+ src: stageModels.find((m) => m.id === selectedStageId)?.thumbnail,
5679
+ alt: "Stage preview",
5680
+ className: "h-20 sm:h-24 rounded-lg border object-cover",
5681
+ style: { borderColor: "rgba(203, 213, 225, 0.4)" }
5682
+ }
5683
+ ))), /* @__PURE__ */ React10.createElement("div", { className: "pt-3 sm:pt-4 flex items-center justify-center gap-2 opacity-50 italic text-[10px] sm:text-xs border-t", style: { borderColor: "rgba(203, 213, 225, 0.3)" } }, /* @__PURE__ */ React10.createElement("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React10.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" })), /* @__PURE__ */ React10.createElement("span", null, "\u9009\u62E9\u7684\u6A21\u578B\u5C06\u5E94\u7528\u5230\u6240\u6709\u573A\u666F")));
5684
+ };
5685
+ ModelSelectorSettings.displayName = "ModelSelectorSettings";
5686
+
5687
+ // src/mmd/visual-novel/MMDVisualNovelWithSelector.tsx
5688
+ var MMDVisualNovelWithSelector = forwardRef((props, ref) => {
5689
+ const {
5690
+ script,
5691
+ modelSelector,
5692
+ onModelSelectionChange,
5693
+ ...restProps
5694
+ } = props;
5695
+ const {
5696
+ characterModels,
5697
+ stageModels,
5698
+ defaultCharacterId,
5699
+ defaultStageId,
5700
+ characterLabel,
5701
+ stageLabel
5702
+ } = modelSelector;
5703
+ const visualNovelRef = useRef(null);
5704
+ const [selectedCharacterId, setSelectedCharacterId] = useState(
5705
+ defaultCharacterId || characterModels[0]?.id || ""
5706
+ );
5707
+ const [selectedStageId, setSelectedStageId] = useState(
5708
+ defaultStageId || stageModels[0]?.id || ""
5709
+ );
5710
+ const selectedCharacterPath = useMemo(() => {
5711
+ const model = characterModels.find((m) => m.id === selectedCharacterId);
5712
+ return model?.path || characterModels[0]?.path || "";
5713
+ }, [characterModels, selectedCharacterId]);
5714
+ const selectedStagePath = useMemo(() => {
5715
+ const model = stageModels.find((m) => m.id === selectedStageId);
5716
+ return model?.path || stageModels[0]?.path || "";
5717
+ }, [stageModels, selectedStageId]);
5718
+ const modifiedScript = useMemo(() => {
5719
+ return {
5720
+ ...script,
5721
+ nodes: script.nodes.map((node) => ({
5722
+ ...node,
5723
+ resources: {
5724
+ ...node.resources,
5725
+ // 覆盖人物模型路径
5726
+ modelPath: selectedCharacterPath,
5727
+ // 覆盖场景模型路径
5728
+ stageModelPath: selectedStagePath
5729
+ }
5730
+ }))
5731
+ };
5732
+ }, [script, selectedCharacterPath, selectedStagePath]);
5733
+ const handleCharacterChange = useCallback((id) => {
5734
+ setSelectedCharacterId(id);
5735
+ onModelSelectionChange?.(id, selectedStageId);
5736
+ }, [selectedStageId, onModelSelectionChange]);
5737
+ const handleStageChange = useCallback((id) => {
5738
+ setSelectedStageId(id);
5739
+ onModelSelectionChange?.(selectedCharacterId, id);
5740
+ }, [selectedCharacterId, onModelSelectionChange]);
5741
+ const customSettingsContent = useMemo(() => /* @__PURE__ */ React10.createElement(
5742
+ ModelSelectorSettings,
5743
+ {
5744
+ characterModels,
5745
+ stageModels,
5746
+ selectedCharacterId,
5747
+ selectedStageId,
5748
+ onCharacterChange: handleCharacterChange,
5749
+ onStageChange: handleStageChange,
5750
+ characterLabel,
5751
+ stageLabel
5752
+ }
5753
+ ), [
5754
+ characterModels,
5755
+ stageModels,
5756
+ selectedCharacterId,
5757
+ selectedStageId,
5758
+ handleCharacterChange,
5759
+ handleStageChange,
5760
+ characterLabel,
5761
+ stageLabel
5762
+ ]);
5763
+ useImperativeHandle(ref, () => ({
5764
+ // 继承 MMDVisualNovelRef 的所有方法
5765
+ goToNode: (nodeIndex, force) => {
5766
+ visualNovelRef.current?.goToNode(nodeIndex, force);
5767
+ },
5768
+ goToDialogue: (dialogueIndex) => {
5769
+ visualNovelRef.current?.goToDialogue(dialogueIndex);
5770
+ },
5771
+ getCurrentNodeIndex: () => {
5772
+ return visualNovelRef.current?.getCurrentNodeIndex() ?? 0;
5773
+ },
5774
+ getCurrentDialogueIndex: () => {
5775
+ return visualNovelRef.current?.getCurrentDialogueIndex() ?? 0;
5776
+ },
5777
+ getHistory: () => {
5778
+ return visualNovelRef.current?.getHistory() ?? [];
5779
+ },
5780
+ getVariables: () => {
5781
+ return visualNovelRef.current?.getVariables() ?? {};
5782
+ },
5783
+ setVariable: (key, value) => {
5784
+ visualNovelRef.current?.setVariable(key, value);
5785
+ },
5786
+ setAutoMode: (enabled) => {
5787
+ visualNovelRef.current?.setAutoMode(enabled);
5788
+ },
5789
+ skipTyping: () => {
5790
+ visualNovelRef.current?.skipTyping();
5791
+ },
5792
+ triggerEffect: (effect) => {
5793
+ visualNovelRef.current?.triggerEffect?.(effect);
5794
+ },
5795
+ // 新增的模型选择方法
5796
+ getSelectedCharacterId: () => selectedCharacterId,
5797
+ getSelectedStageId: () => selectedStageId,
5798
+ setCharacterModel: (id) => {
5799
+ if (characterModels.some((m) => m.id === id)) {
5800
+ handleCharacterChange(id);
5801
+ }
5802
+ },
5803
+ setStageModel: (id) => {
5804
+ if (stageModels.some((m) => m.id === id)) {
5805
+ handleStageChange(id);
5806
+ }
5807
+ }
5808
+ }), [
5809
+ selectedCharacterId,
5810
+ selectedStageId,
5811
+ characterModels,
5812
+ stageModels,
5813
+ handleCharacterChange,
5814
+ handleStageChange
5815
+ ]);
5816
+ return /* @__PURE__ */ React10.createElement(
5817
+ MMDVisualNovel,
5818
+ {
5819
+ ref: visualNovelRef,
5820
+ script: modifiedScript,
5821
+ customSettingsContent,
5822
+ ...restProps
5823
+ }
5824
+ );
5825
+ });
5826
+ MMDVisualNovelWithSelector.displayName = "MMDVisualNovelWithSelector";
5567
5827
  var MusicControls = ({
5568
5828
  isPlaying,
5569
5829
  currentTime,
@@ -6052,41 +6312,129 @@ var MMDMusicPlayer = forwardRef(
6052
6312
  }
6053
6313
  );
6054
6314
  MMDMusicPlayer.displayName = "MMDMusicPlayer";
6315
+ function Select({
6316
+ label,
6317
+ options,
6318
+ value,
6319
+ onChange,
6320
+ placeholder = "\u8BF7\u9009\u62E9...",
6321
+ allowEmpty = false,
6322
+ emptyLabel = "\u65E0"
6323
+ }) {
6324
+ const selectedOption = options.find((opt) => opt.id === value);
6325
+ const showPlaceholder = !selectedOption && !allowEmpty && value !== "";
6326
+ return /* @__PURE__ */ React10.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React10.createElement("label", { className: "block text-xs font-medium text-white/50 ml-1 uppercase tracking-wider" }, label), /* @__PURE__ */ React10.createElement("div", { className: "relative" }, /* @__PURE__ */ React10.createElement(
6327
+ "select",
6328
+ {
6329
+ value,
6330
+ onChange: (e) => onChange(e.target.value),
6331
+ className: "w-full appearance-none bg-white/5 border border-white/10 rounded-xl px-4 py-3 pr-10 text-sm text-white focus:outline-none focus:border-cyan-500/50 transition-colors cursor-pointer hover:bg-white/10"
6332
+ },
6333
+ showPlaceholder && /* @__PURE__ */ React10.createElement("option", { value: "", disabled: true, className: "bg-gray-900 text-white/50" }, placeholder),
6334
+ allowEmpty && /* @__PURE__ */ React10.createElement("option", { value: "", className: "bg-gray-900 text-white/60" }, emptyLabel),
6335
+ options.map((option) => /* @__PURE__ */ React10.createElement(
6336
+ "option",
6337
+ {
6338
+ key: option.id,
6339
+ value: option.id,
6340
+ className: "bg-gray-900 text-white"
6341
+ },
6342
+ option.name
6343
+ ))
6344
+ ), /* @__PURE__ */ React10.createElement(ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/40 pointer-events-none" })));
6345
+ }
6055
6346
  var MMDARPlayer = forwardRef((props, ref) => {
6056
6347
  const {
6057
- resources,
6058
6348
  stage = {},
6059
6349
  mobileOptimization,
6060
6350
  cameraConfig = { facingMode: "user" },
6061
6351
  mirrored,
6062
6352
  showSettings = true,
6353
+ modelPresets,
6354
+ motionPresets,
6355
+ audioPresets = [],
6356
+ defaultModelId,
6357
+ defaultMotionId,
6358
+ defaultAudioId,
6359
+ initialModelVisible = false,
6360
+ placementText = "TOUCH!",
6063
6361
  autoPlay = true,
6064
6362
  loop = true,
6065
6363
  onCameraReady,
6066
6364
  onCameraError,
6067
6365
  onResourcesChange,
6366
+ onModelPlaced,
6068
6367
  onLoad,
6069
6368
  onError,
6070
6369
  className,
6071
6370
  style
6072
6371
  } = props;
6372
+ const initialModelId = defaultModelId || modelPresets[0]?.id || "";
6373
+ const initialMotionId = defaultMotionId || motionPresets[0]?.id || "";
6374
+ const initialAudioId = defaultAudioId || audioPresets[0]?.id || "";
6073
6375
  const videoRef = useRef(null);
6074
6376
  const playerRef = useRef(null);
6075
6377
  const streamRef = useRef(null);
6378
+ const containerRef = useRef(null);
6076
6379
  const [isCameraStarted, setIsCameraStarted] = useState(false);
6077
6380
  const [cameraError, setCameraError] = useState(null);
6078
6381
  const [facingMode, setFacingMode] = useState(cameraConfig.facingMode || "user");
6079
- const [isLoading, setIsLoading] = useState(true);
6382
+ const [isLoading, setIsLoading] = useState(false);
6080
6383
  const [isSettingsOpen, setIsSettingsOpen] = useState(false);
6081
- const [currentResources, setCurrentResources] = useState(resources);
6082
- const [tempResources, setTempResources] = useState(resources);
6083
- useEffect(() => {
6084
- if (!isSettingsOpen) {
6085
- setCurrentResources(resources);
6086
- setTempResources(resources);
6087
- }
6088
- }, [resources, isSettingsOpen]);
6384
+ const [selectedModelId, setSelectedModelId] = useState(initialModelId);
6385
+ const [selectedMotionId, setSelectedMotionId] = useState(initialMotionId);
6386
+ const [selectedAudioId, setSelectedAudioId] = useState(initialAudioId);
6387
+ const [isModelPlaced, setIsModelPlaced] = useState(initialModelVisible);
6388
+ const [placementAnimation, setPlacementAnimation] = useState(false);
6389
+ const currentResources = useMemo(() => {
6390
+ const model = modelPresets.find((m) => m.id === selectedModelId);
6391
+ const motion = motionPresets.find((m) => m.id === selectedMotionId);
6392
+ const audio = audioPresets.find((a) => a.id === selectedAudioId);
6393
+ return {
6394
+ modelPath: model?.modelPath || modelPresets[0]?.modelPath || "",
6395
+ motionPath: motion?.motionPath || motionPresets[0]?.motionPath || "",
6396
+ audioPath: audio?.audioPath
6397
+ };
6398
+ }, [selectedModelId, selectedMotionId, selectedAudioId, modelPresets, motionPresets, audioPresets]);
6089
6399
  const shouldMirror = mirrored !== void 0 ? mirrored : facingMode === "user";
6400
+ const placeModel = useCallback(() => {
6401
+ if (isModelPlaced) return;
6402
+ setPlacementAnimation(true);
6403
+ setIsLoading(true);
6404
+ setTimeout(() => {
6405
+ setIsModelPlaced(true);
6406
+ setPlacementAnimation(false);
6407
+ onModelPlaced?.();
6408
+ }, 300);
6409
+ }, [isModelPlaced, onModelPlaced]);
6410
+ const removeModel = useCallback(() => {
6411
+ setIsModelPlaced(false);
6412
+ setIsLoading(false);
6413
+ }, []);
6414
+ const switchModel = useCallback((newResources) => {
6415
+ const matchedModel = modelPresets.find((m) => m.modelPath === newResources.modelPath);
6416
+ const matchedMotion = motionPresets.find((m) => m.motionPath === newResources.motionPath);
6417
+ const matchedAudio = audioPresets.find((a) => a.audioPath === newResources.audioPath);
6418
+ if (matchedModel) setSelectedModelId(matchedModel.id);
6419
+ if (matchedMotion) setSelectedMotionId(matchedMotion.id);
6420
+ if (matchedAudio) setSelectedAudioId(matchedAudio.id);
6421
+ onResourcesChange?.(newResources);
6422
+ if (isModelPlaced) {
6423
+ setIsLoading(true);
6424
+ }
6425
+ }, [isModelPlaced, onResourcesChange, modelPresets, motionPresets, audioPresets]);
6426
+ const applySettings = useCallback(() => {
6427
+ onResourcesChange?.(currentResources);
6428
+ setIsSettingsOpen(false);
6429
+ if (isModelPlaced) {
6430
+ setIsLoading(true);
6431
+ }
6432
+ }, [currentResources, isModelPlaced, onResourcesChange]);
6433
+ const resetPosition = useCallback(() => {
6434
+ setIsModelPlaced(false);
6435
+ setIsLoading(false);
6436
+ setIsSettingsOpen(false);
6437
+ }, []);
6090
6438
  const startCamera = useCallback(async (mode = facingMode) => {
6091
6439
  try {
6092
6440
  setCameraError(null);
@@ -6147,22 +6495,27 @@ var MMDARPlayer = forwardRef((props, ref) => {
6147
6495
  if (shouldMirror) {
6148
6496
  ctx.setTransform(1, 0, 0, 1, 0, 0);
6149
6497
  }
6150
- const mmdBase64 = playerRef.current.snapshot();
6151
- const mmdImg = new Image();
6152
- mmdImg.src = mmdBase64;
6153
- await new Promise((resolve) => {
6154
- mmdImg.onload = () => {
6155
- ctx.drawImage(mmdImg, 0, 0, canvas.width, canvas.height);
6156
- resolve(null);
6157
- };
6158
- });
6498
+ if (isModelPlaced) {
6499
+ const mmdBase64 = playerRef.current.snapshot();
6500
+ const mmdImg = new Image();
6501
+ mmdImg.src = mmdBase64;
6502
+ await new Promise((resolve) => {
6503
+ mmdImg.onload = () => {
6504
+ ctx.drawImage(mmdImg, 0, 0, canvas.width, canvas.height);
6505
+ resolve(null);
6506
+ };
6507
+ });
6508
+ }
6159
6509
  return canvas.toDataURL("image/png");
6160
- }, [shouldMirror]);
6510
+ }, [shouldMirror, isModelPlaced]);
6161
6511
  useImperativeHandle(ref, () => ({
6162
6512
  startCamera,
6163
6513
  stopCamera,
6164
6514
  switchCamera,
6165
- snapshot
6515
+ snapshot,
6516
+ placeModel,
6517
+ removeModel,
6518
+ switchModel
6166
6519
  }));
6167
6520
  useEffect(() => {
6168
6521
  if (autoPlay) {
@@ -6170,9 +6523,13 @@ var MMDARPlayer = forwardRef((props, ref) => {
6170
6523
  }
6171
6524
  return () => stopCamera();
6172
6525
  }, [autoPlay, startCamera, stopCamera]);
6526
+ useEffect(() => {
6527
+ onResourcesChange?.(currentResources);
6528
+ }, [currentResources, onResourcesChange]);
6173
6529
  return /* @__PURE__ */ React10.createElement(
6174
6530
  "div",
6175
6531
  {
6532
+ ref: containerRef,
6176
6533
  className: `relative w-full h-full bg-black overflow-hidden ${className}`,
6177
6534
  style
6178
6535
  },
@@ -6187,26 +6544,25 @@ var MMDARPlayer = forwardRef((props, ref) => {
6187
6544
  style: { zIndex: 0 }
6188
6545
  }
6189
6546
  ),
6190
- /* @__PURE__ */ React10.createElement(
6547
+ isModelPlaced && /* @__PURE__ */ React10.createElement(
6191
6548
  "div",
6192
6549
  {
6193
- className: "absolute inset-0 w-full h-full",
6550
+ className: `absolute inset-0 w-full h-full transition-all duration-500 ${placementAnimation ? "scale-110 opacity-0" : "scale-100 opacity-100"}`,
6194
6551
  style: { zIndex: 1 }
6195
6552
  },
6196
6553
  /* @__PURE__ */ React10.createElement(
6197
6554
  MMDPlayerBase,
6198
6555
  {
6556
+ key: `${selectedModelId}-${selectedMotionId}-${selectedAudioId}`,
6199
6557
  ref: playerRef,
6200
6558
  resources: currentResources,
6201
6559
  stage: {
6202
6560
  ...stage,
6203
6561
  backgroundColor: "transparent",
6204
- // 强制透明背景
6205
6562
  cameraPosition: stage.cameraPosition || { x: 0, y: 15, z: 40 }
6206
- // 针对 AR 场景稍调高相机
6207
6563
  },
6208
6564
  mobileOptimization,
6209
- autoPlay,
6565
+ autoPlay: true,
6210
6566
  loop,
6211
6567
  onLoad: () => {
6212
6568
  setIsLoading(false);
@@ -6216,6 +6572,30 @@ var MMDARPlayer = forwardRef((props, ref) => {
6216
6572
  }
6217
6573
  )
6218
6574
  ),
6575
+ !isModelPlaced && isCameraStarted && /* @__PURE__ */ React10.createElement(
6576
+ "div",
6577
+ {
6578
+ className: "absolute inset-0 z-5 flex items-center justify-center",
6579
+ onClick: placeModel
6580
+ },
6581
+ /* @__PURE__ */ React10.createElement(
6582
+ "button",
6583
+ {
6584
+ onClick: placeModel,
6585
+ className: `
6586
+ relative group cursor-pointer
6587
+ transition-all duration-300 ease-out
6588
+ hover:scale-110 active:scale-95
6589
+ ${placementAnimation ? "scale-150 opacity-0" : "scale-100 opacity-100"}
6590
+ `
6591
+ },
6592
+ /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 -m-4 rounded-2xl bg-cyan-400/20 animate-ping" }),
6593
+ /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 -m-2 rounded-xl bg-cyan-400/30 animate-pulse" }),
6594
+ /* @__PURE__ */ React10.createElement("div", { className: "relative bg-gradient-to-br from-cyan-400 via-blue-500 to-purple-600 p-1 rounded-2xl shadow-2xl shadow-cyan-500/50" }, /* @__PURE__ */ React10.createElement("div", { className: "bg-black/80 backdrop-blur-xl px-8 py-6 rounded-xl flex flex-col items-center gap-3" }, /* @__PURE__ */ React10.createElement("div", { className: "relative" }, /* @__PURE__ */ React10.createElement(Sparkles, { className: "w-10 h-10 text-cyan-400 animate-pulse" }), /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 w-10 h-10 bg-cyan-400/30 blur-xl" })), /* @__PURE__ */ React10.createElement("span", { className: "text-2xl font-black text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-purple-400 tracking-widest" }, placementText), /* @__PURE__ */ React10.createElement("span", { className: "text-xs text-white/50 font-medium" }, "\u70B9\u51FB\u53EC\u5524 Miku \u2728"))),
6595
+ /* @__PURE__ */ React10.createElement("div", { className: "absolute -top-2 -right-2 w-4 h-4 bg-yellow-400 rounded-full animate-bounce shadow-lg shadow-yellow-400/50" }),
6596
+ /* @__PURE__ */ React10.createElement("div", { className: "absolute -bottom-1 -left-1 w-3 h-3 bg-pink-400 rounded-full animate-bounce delay-100 shadow-lg shadow-pink-400/50" })
6597
+ )
6598
+ ),
6219
6599
  /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 z-10 pointer-events-none flex flex-col justify-between p-6" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-start pointer-events-auto" }, cameraError ? /* @__PURE__ */ React10.createElement("div", { className: "bg-red-500/80 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React10.createElement(AlertCircle, { className: "w-4 h-4" }), cameraError, /* @__PURE__ */ React10.createElement(
6220
6600
  "button",
6221
6601
  {
@@ -6223,12 +6603,12 @@ var MMDARPlayer = forwardRef((props, ref) => {
6223
6603
  className: "ml-2 underline"
6224
6604
  },
6225
6605
  "\u91CD\u8BD5"
6226
- )) : /* @__PURE__ */ React10.createElement("div", { className: "bg-black/40 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React10.createElement(Camera, { className: "w-4 h-4 text-green-400" }), isCameraStarted ? "\u5B9E\u666F AR \u6A21\u5F0F\u5DF2\u5F00\u542F" : "\u7B49\u5F85\u6444\u50CF\u5934..."), /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-2" }, showSettings && /* @__PURE__ */ React10.createElement(
6606
+ )) : /* @__PURE__ */ React10.createElement("div", { className: "bg-black/40 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React10.createElement(Camera, { className: "w-4 h-4 text-green-400" }), isCameraStarted ? isModelPlaced ? "\u5B9E\u666F AR \u6A21\u5F0F" : "\u70B9\u51FB\u653E\u7F6E\u6A21\u578B" : "\u7B49\u5F85\u6444\u50CF\u5934..."), /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-2" }, showSettings && /* @__PURE__ */ React10.createElement(
6227
6607
  "button",
6228
6608
  {
6229
6609
  onClick: () => setIsSettingsOpen(!isSettingsOpen),
6230
- className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto ${isSettingsOpen ? "bg-blue-500" : "bg-white/10 hover:bg-white/20"}`,
6231
- title: "\u8BBE\u7F6E\u6A21\u578B\u4E0E\u52A8\u4F5C"
6610
+ className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto ${isSettingsOpen ? "bg-cyan-500" : "bg-white/10 hover:bg-white/20"}`,
6611
+ title: "\u8BBE\u7F6E"
6232
6612
  },
6233
6613
  /* @__PURE__ */ React10.createElement(Settings, { className: "w-5 h-5" })
6234
6614
  ), /* @__PURE__ */ React10.createElement(
@@ -6247,45 +6627,59 @@ var MMDARPlayer = forwardRef((props, ref) => {
6247
6627
  title: isCameraStarted ? "\u5173\u95ED\u6444\u50CF\u5934" : "\u5F00\u542F\u6444\u50CF\u5934"
6248
6628
  },
6249
6629
  isCameraStarted ? /* @__PURE__ */ React10.createElement(CameraOff, { className: "w-5 h-5" }) : /* @__PURE__ */ React10.createElement(Camera, { className: "w-5 h-5" })
6250
- ))), isSettingsOpen && /* @__PURE__ */ React10.createElement("div", { className: "absolute top-20 right-6 w-80 bg-black/80 backdrop-blur-xl border border-white/10 rounded-2xl p-6 pointer-events-auto shadow-2xl animate-in slide-in-from-right-4 duration-300" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React10.createElement("h3", { className: "text-white font-bold flex items-center gap-2" }, /* @__PURE__ */ React10.createElement(Settings, { className: "w-4 h-4" }), "\u8D44\u6E90\u8BBE\u7F6E"), /* @__PURE__ */ React10.createElement(
6630
+ ))), isSettingsOpen && /* @__PURE__ */ React10.createElement("div", { className: "absolute top-20 right-6 w-72 bg-black/90 backdrop-blur-xl border border-white/10 rounded-2xl p-5 pointer-events-auto shadow-2xl animate-in slide-in-from-right-4 duration-300" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center justify-between mb-5" }, /* @__PURE__ */ React10.createElement("h3", { className: "text-white font-bold flex items-center gap-2" }, /* @__PURE__ */ React10.createElement(Settings, { className: "w-4 h-4 text-cyan-400" }), "AR \u8BBE\u7F6E"), /* @__PURE__ */ React10.createElement(
6251
6631
  "button",
6252
6632
  {
6253
6633
  onClick: () => setIsSettingsOpen(false),
6254
- className: "p-1 hover:bg-white/10 rounded-full transition-colors text-white/60"
6634
+ className: "p-1.5 hover:bg-white/10 rounded-full transition-colors text-white/60"
6255
6635
  },
6256
6636
  /* @__PURE__ */ React10.createElement(X, { className: "w-4 h-4" })
6257
- )), /* @__PURE__ */ React10.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u6A21\u578B\u8DEF\u5F84 (.pmx)"), /* @__PURE__ */ React10.createElement(
6258
- "input",
6637
+ )), /* @__PURE__ */ React10.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React10.createElement(
6638
+ Select,
6259
6639
  {
6260
- type: "text",
6261
- value: tempResources.modelPath,
6262
- onChange: (e) => setTempResources({ ...tempResources, modelPath: e.target.value }),
6263
- placeholder: "\u8BF7\u8F93\u5165\u6A21\u578B URL...",
6264
- className: "w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-white placeholder:text-white/10 focus:outline-none focus:border-blue-500/50 transition-colors"
6640
+ label: "\u9009\u62E9\u6A21\u578B",
6641
+ options: modelPresets,
6642
+ value: selectedModelId,
6643
+ onChange: setSelectedModelId,
6644
+ placeholder: "\u8BF7\u9009\u62E9\u6A21\u578B..."
6265
6645
  }
6266
- )), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u52A8\u4F5C\u8DEF\u5F84 (.vmd)"), /* @__PURE__ */ React10.createElement(
6267
- "input",
6646
+ ), /* @__PURE__ */ React10.createElement(
6647
+ Select,
6268
6648
  {
6269
- type: "text",
6270
- value: tempResources.motionPath || "",
6271
- onChange: (e) => setTempResources({ ...tempResources, motionPath: e.target.value }),
6272
- placeholder: "\u8BF7\u8F93\u5165\u52A8\u4F5C URL...",
6273
- className: "w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-white placeholder:text-white/10 focus:outline-none focus:border-blue-500/50 transition-colors"
6274
- }
6275
- )), /* @__PURE__ */ React10.createElement("div", { className: "pt-2" }, /* @__PURE__ */ React10.createElement(
6649
+ label: "\u9009\u62E9\u52A8\u4F5C",
6650
+ options: motionPresets,
6651
+ value: selectedMotionId,
6652
+ onChange: setSelectedMotionId,
6653
+ placeholder: "\u8BF7\u9009\u62E9\u52A8\u4F5C..."
6654
+ }
6655
+ ), audioPresets.length > 0 && /* @__PURE__ */ React10.createElement(
6656
+ Select,
6657
+ {
6658
+ label: "\u9009\u62E9\u97F3\u4E50",
6659
+ options: audioPresets,
6660
+ value: selectedAudioId,
6661
+ onChange: setSelectedAudioId,
6662
+ placeholder: "\u8BF7\u9009\u62E9\u97F3\u4E50...",
6663
+ allowEmpty: true,
6664
+ emptyLabel: "\u{1F507} \u4E0D\u64AD\u653E\u97F3\u4E50"
6665
+ }
6666
+ ), /* @__PURE__ */ React10.createElement("div", { className: "pt-3 space-y-2" }, isModelPlaced && /* @__PURE__ */ React10.createElement(
6276
6667
  "button",
6277
6668
  {
6278
- onClick: () => {
6279
- setCurrentResources(tempResources);
6280
- onResourcesChange?.(tempResources);
6281
- setIsSettingsOpen(false);
6282
- setIsLoading(true);
6283
- },
6284
- className: "w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 rounded-xl flex items-center justify-center gap-2 transition-all active:scale-95 shadow-lg shadow-blue-500/20"
6669
+ onClick: applySettings,
6670
+ className: "w-full bg-cyan-500 hover:bg-cyan-600 text-white font-bold py-3 rounded-xl flex items-center justify-center gap-2 transition-all active:scale-95 shadow-lg shadow-cyan-500/20"
6285
6671
  },
6286
- /* @__PURE__ */ React10.createElement(Check, { className: "w-4 h-4" }),
6672
+ /* @__PURE__ */ React10.createElement(Sparkles, { className: "w-4 h-4" }),
6287
6673
  "\u5E94\u7528\u66F4\u6539"
6288
- )))), isLoading && /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none" }, /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React10.createElement("div", { className: "w-12 h-12 border-4 border-blue-500/30 border-t-blue-500 rounded-full animate-spin" }), /* @__PURE__ */ React10.createElement("div", { className: "text-white text-sm font-medium bg-black/40 px-3 py-1 rounded-full backdrop-blur-sm" }, "\u6B63\u5728\u52A0\u8F7D Miku \u8D44\u6E90..."))))
6674
+ ), /* @__PURE__ */ React10.createElement(
6675
+ "button",
6676
+ {
6677
+ onClick: resetPosition,
6678
+ className: `w-full ${isModelPlaced ? "bg-white/5 hover:bg-white/10 text-white/70" : "bg-cyan-500 hover:bg-cyan-600 text-white shadow-lg shadow-cyan-500/20"} font-medium py-3 rounded-xl flex items-center justify-center gap-2 transition-all active:scale-95 border border-white/10`
6679
+ },
6680
+ /* @__PURE__ */ React10.createElement(RotateCcw, { className: "w-4 h-4" }),
6681
+ isModelPlaced ? "\u91CD\u7F6E\u4F4D\u7F6E" : "\u5F00\u59CB\u653E\u7F6E"
6682
+ )))), isLoading && isModelPlaced && /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none" }, /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React10.createElement("div", { className: "w-12 h-12 border-4 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin" }), /* @__PURE__ */ React10.createElement("div", { className: "text-white text-sm font-medium bg-black/40 px-3 py-1 rounded-full backdrop-blur-sm" }, "\u6B63\u5728\u53EC\u5524 Miku..."))))
6289
6683
  );
6290
6684
  });
6291
6685
  MMDARPlayer.displayName = "MMDARPlayer";
@@ -8378,6 +8772,6 @@ var FXThreePreview = ({
8378
8772
  };
8379
8773
  FXThreePreview.displayName = "FXThreePreview";
8380
8774
 
8381
- export { CheerButton, CheerParticles, ChoiceMenu, DialogueBox, FXParser, FXThreePreview, FXToThreeAdapter, FXViewer, HLSLToGLSLConverter, HistoryPanel, LoadingOverlay, LoadingScreen, LoopConfirmDialog, MMDARPlayer, MMDLightingDebugPanel, MMDMusicPlayer, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, MultiFXAdapter, MusicControls, PMXEditor2 as PMXEditor, PMXEditor as PMXEditorCore, PMXExporter, PMXViewer, PlaylistPanel, StartScreen, TrackInfo, addDefaultSphereTextures, checkModelSphereDefinition, checkSphereTextures, compareFXEffects, configureMaterialsForMMD, configureRendererForMMD, createMMDLights, diagnoseMaterialsMMD, exportFXToJSON, exportFXToMarkdown, extractTexturePaths, filterDefinesByPrefix, fullSphereDiagnostic, generateAllToonTextures, generateToonTexture, getColorParameters, getConfigSummaryText, getFeatureFlags, getTextureDefines, hasFeature, inspectMaterial, listAllMaterials, loadAmmo, printDiagnosticReport, printModelSphereInfo, printSphereDiagnostic, quickDiagnose, validateFXEffect };
8775
+ export { CheerButton, CheerParticles, ChoiceMenu, DialogueBox, FXParser, FXThreePreview, FXToThreeAdapter, FXViewer, HLSLToGLSLConverter, HistoryPanel, LoadingOverlay, LoadingScreen, LoopConfirmDialog, MMDARPlayer, MMDLightingDebugPanel, MMDMusicPlayer, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, MMDVisualNovelWithSelector, ModelSelectorSettings, MultiFXAdapter, MusicControls, PMXEditor2 as PMXEditor, PMXEditor as PMXEditorCore, PMXExporter, PMXViewer, PlaylistPanel, StartScreen, TrackInfo, addDefaultSphereTextures, checkModelSphereDefinition, checkSphereTextures, compareFXEffects, configureMaterialsForMMD, configureRendererForMMD, createMMDLights, diagnoseMaterialsMMD, exportFXToJSON, exportFXToMarkdown, extractTexturePaths, filterDefinesByPrefix, fullSphereDiagnostic, generateAllToonTextures, generateToonTexture, getColorParameters, getConfigSummaryText, getFeatureFlags, getTextureDefines, hasFeature, inspectMaterial, listAllMaterials, loadAmmo, printDiagnosticReport, printModelSphereInfo, printSphereDiagnostic, quickDiagnose, validateFXEffect };
8382
8776
  //# sourceMappingURL=index.mjs.map
8383
8777
  //# sourceMappingURL=index.mjs.map