sa2kit 1.0.9 → 1.1.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.
@@ -264,7 +264,7 @@ declare const MMDPlayerBase: React__default.FC<MMDPlayerBaseProps>;
264
264
  * 支持通过 resources 和 stage 配置快速使用
265
265
  * 所有资源均从 public 目录加载,无需额外配置
266
266
  */
267
- declare const MMDPlayerEnhanced: React__default.FC<MMDPlayerEnhancedProps>;
267
+ declare const MMDPlayerEnhanced: React__default.ForwardRefExoticComponent<MMDPlayerEnhancedProps & React__default.RefAttributes<any>>;
268
268
 
269
269
  /**
270
270
  * MMD 播放列表组件(预加载版本)
@@ -264,7 +264,7 @@ declare const MMDPlayerBase: React__default.FC<MMDPlayerBaseProps>;
264
264
  * 支持通过 resources 和 stage 配置快速使用
265
265
  * 所有资源均从 public 目录加载,无需额外配置
266
266
  */
267
- declare const MMDPlayerEnhanced: React__default.FC<MMDPlayerEnhancedProps>;
267
+ declare const MMDPlayerEnhanced: React__default.ForwardRefExoticComponent<MMDPlayerEnhancedProps & React__default.RefAttributes<any>>;
268
268
 
269
269
  /**
270
270
  * MMD 播放列表组件(预加载版本)
package/dist/mmd/index.js CHANGED
@@ -298,7 +298,7 @@ var MMDPlayerBase = ({
298
298
  } }, error)
299
299
  );
300
300
  };
301
- var MMDPlayerEnhanced = ({
301
+ var MMDPlayerEnhanced = React2.forwardRef(({
302
302
  resources,
303
303
  resourcesList,
304
304
  defaultResourceId,
@@ -315,82 +315,8 @@ var MMDPlayerEnhanced = ({
315
315
  onSelectionChange,
316
316
  onAudioEnded,
317
317
  onAnimationEnded
318
- }) => {
318
+ }, ref) => {
319
319
  console.log("\u{1F3A8} [MMDPlayerEnhanced] \u7EC4\u4EF6\u521D\u59CB\u5316");
320
- React2.useEffect(() => {
321
- return () => {
322
- console.log("\u{1F9F9} [MMDPlayerEnhanced] \u7EC4\u4EF6\u5378\u8F7D\uFF0C\u6267\u884C\u5B8C\u6574\u6E05\u7406");
323
- if (animationIdRef.current) {
324
- cancelAnimationFrame(animationIdRef.current);
325
- animationIdRef.current = null;
326
- }
327
- if (audioRef.current) {
328
- audioRef.current.pause();
329
- audioRef.current.src = "";
330
- audioRef.current.load();
331
- audioRef.current = null;
332
- }
333
- if (helperRef.current) {
334
- try {
335
- helperRef.current.enable("animation", false);
336
- helperRef.current.enable("ik", false);
337
- helperRef.current.enable("grant", false);
338
- helperRef.current.enable("physics", false);
339
- const helperObjects = helperRef.current.objects;
340
- if (helperObjects && Array.isArray(helperObjects)) {
341
- const physicsWorldsToDestroy = /* @__PURE__ */ new Set();
342
- helperObjects.forEach((obj) => {
343
- if (obj.physics) {
344
- const physics = obj.physics;
345
- if (physics.world) physicsWorldsToDestroy.add(physics.world);
346
- if (physics.bodies) physics.bodies.length = 0;
347
- if (physics.constraints) physics.constraints.length = 0;
348
- obj.physics = null;
349
- }
350
- });
351
- physicsWorldsToDestroy.forEach((world) => {
352
- try {
353
- while (world.getNumCollisionObjects() > 0) {
354
- const obj = world.getCollisionObjectArray().at(0);
355
- world.removeCollisionObject(obj);
356
- if (obj && obj.destroy) obj.destroy();
357
- }
358
- if (world.destroy) world.destroy();
359
- } catch (e) {
360
- }
361
- });
362
- helperObjects.length = 0;
363
- }
364
- } catch (e) {
365
- }
366
- helperRef.current = null;
367
- }
368
- if (sceneRef.current) {
369
- sceneRef.current.clear();
370
- sceneRef.current = null;
371
- }
372
- if (rendererRef.current) {
373
- rendererRef.current.dispose();
374
- rendererRef.current = null;
375
- }
376
- if (controlsRef.current) {
377
- controlsRef.current.dispose();
378
- controlsRef.current = null;
379
- }
380
- cameraRef.current = null;
381
- if (clockRef.current) {
382
- clockRef.current = new THREE2__namespace.Clock();
383
- }
384
- vmdDataRef.current = null;
385
- if (window.gc) {
386
- try {
387
- window.gc();
388
- } catch (e) {
389
- }
390
- }
391
- console.log("\u2705 [MMDPlayerEnhanced] \u7EC4\u4EF6\u5378\u8F7D\u6E05\u7406\u5B8C\u6210");
392
- };
393
- }, []);
394
320
  const [selectedResourceId, setSelectedResourceId] = React2.useState(
395
321
  defaultResourceId || resourcesList?.[0]?.id || ""
396
322
  );
@@ -482,6 +408,55 @@ var MMDPlayerEnhanced = ({
482
408
  const [isInitialized, setIsInitialized] = React2.useState(false);
483
409
  const [reloadTrigger, setReloadTrigger] = React2.useState(0);
484
410
  const [needReset, setNeedReset] = React2.useState(false);
411
+ React2.useImperativeHandle(ref, () => ({
412
+ clearResources: () => {
413
+ console.log("\u{1F9F9} [MMDPlayerEnhanced] \u5916\u90E8\u89E6\u53D1\u8D44\u6E90\u6E05\u7406");
414
+ clearOldResources();
415
+ },
416
+ getIsPlaying: () => isPlayingRef.current,
417
+ getIsLoaded: () => isLoadedRef.current,
418
+ stopCompletely: () => {
419
+ console.log("\u23F9\uFE0F [MMDPlayerEnhanced] \u5B8C\u5168\u505C\u6B62");
420
+ stopCompletely();
421
+ }
422
+ }));
423
+ const stopCompletely = () => {
424
+ isPlayingRef.current = false;
425
+ setIsPlaying(false);
426
+ if (audioRef.current) {
427
+ audioRef.current.pause();
428
+ audioRef.current.currentTime = 0;
429
+ }
430
+ if (helperRef.current) {
431
+ helperRef.current.enable("physics", false);
432
+ }
433
+ animationEndedFiredRef.current = false;
434
+ lastAnimationTimeRef.current = 0;
435
+ animationStoppedCountRef.current = 0;
436
+ console.log("\u2705 [MMDPlayerEnhanced] \u5B8C\u5168\u505C\u6B62\u5B8C\u6210");
437
+ };
438
+ React2.useEffect(() => {
439
+ const container = containerRef.current;
440
+ if (!container) return;
441
+ const handleCleanupResources = () => {
442
+ console.log("\u{1F9F9} [MMDPlayerEnhanced] \u6536\u5230\u6E05\u7406\u8D44\u6E90\u4E8B\u4EF6");
443
+ if (!isPlayingRef.current) {
444
+ clearOldResources();
445
+ } else {
446
+ console.warn("\u26A0\uFE0F [MMDPlayerEnhanced] \u64AD\u653E\u4E2D\uFF0C\u8DF3\u8FC7\u8D44\u6E90\u6E05\u7406");
447
+ }
448
+ };
449
+ const handleStopCompletely = () => {
450
+ console.log("\u23F9\uFE0F [MMDPlayerEnhanced] \u6536\u5230\u5B8C\u5168\u505C\u6B62\u4E8B\u4EF6");
451
+ stopCompletely();
452
+ };
453
+ container.addEventListener("cleanupResources", handleCleanupResources);
454
+ container.addEventListener("stopCompletely", handleStopCompletely);
455
+ return () => {
456
+ container.removeEventListener("cleanupResources", handleCleanupResources);
457
+ container.removeEventListener("stopCompletely", handleStopCompletely);
458
+ };
459
+ }, []);
485
460
  React2.useEffect(() => {
486
461
  console.log("\u{1F3D7}\uFE0F [MMDPlayerEnhanced] \u573A\u666F\u521D\u59CB\u5316 useEffect \u89E6\u53D1");
487
462
  if (!containerRef.current) {
@@ -747,7 +722,6 @@ var MMDPlayerEnhanced = ({
747
722
  if (window.gc) {
748
723
  try {
749
724
  window.gc();
750
- console.log("\u267B\uFE0F \u5DF2\u89E6\u53D1\u5783\u573E\u56DE\u6536");
751
725
  } catch (e) {
752
726
  }
753
727
  }
@@ -916,75 +890,13 @@ var MMDPlayerEnhanced = ({
916
890
  loadMMD();
917
891
  }, [currentResources, stage?.enablePhysics, autoPlay, loop, onLoad, onError, reloadTrigger]);
918
892
  const play = () => {
893
+ if (!helperRef.current && !needReset) return;
919
894
  if (needReset && vmdDataRef.current && sceneRef.current && cameraRef.current) {
920
- console.log("\u{1F504} \u68C0\u6D4B\u5230\u9700\u8981\u91CD\u7F6E\uFF0C\u5F00\u59CB\u5F3A\u5236\u6E05\u7406...");
921
- if (animationIdRef.current) {
922
- cancelAnimationFrame(animationIdRef.current);
923
- animationIdRef.current = null;
924
- }
895
+ const { mesh, vmd, cameraVmd } = vmdDataRef.current;
925
896
  if (helperRef.current) {
926
897
  try {
927
- helperRef.current.enable("animation", false);
928
- helperRef.current.enable("ik", false);
929
- helperRef.current.enable("grant", false);
930
- helperRef.current.enable("physics", false);
931
898
  const helperObjects = helperRef.current.objects;
932
899
  if (helperObjects && Array.isArray(helperObjects)) {
933
- const physicsWorldsToDestroy = /* @__PURE__ */ new Set();
934
- helperObjects.forEach((obj) => {
935
- if (obj.physics) {
936
- try {
937
- const physics = obj.physics;
938
- if (physics.world) {
939
- physicsWorldsToDestroy.add(physics.world);
940
- }
941
- if (physics.bodies && Array.isArray(physics.bodies)) {
942
- physics.bodies.forEach((body) => {
943
- if (physics.world && body) {
944
- try {
945
- physics.world.removeRigidBody(body);
946
- if (window.Ammo && body.destroy) {
947
- body.destroy();
948
- }
949
- } catch (e) {
950
- }
951
- }
952
- });
953
- physics.bodies.length = 0;
954
- physics.bodies = null;
955
- }
956
- if (physics.constraints && Array.isArray(physics.constraints)) {
957
- physics.constraints.forEach((constraint) => {
958
- if (physics.world && constraint) {
959
- try {
960
- physics.world.removeConstraint(constraint);
961
- if (window.Ammo && constraint.destroy) {
962
- constraint.destroy();
963
- }
964
- } catch (e) {
965
- }
966
- }
967
- });
968
- physics.constraints.length = 0;
969
- physics.constraints = null;
970
- }
971
- physics.world = null;
972
- obj.physics = null;
973
- } catch (e) {
974
- }
975
- }
976
- });
977
- physicsWorldsToDestroy.forEach((world) => {
978
- try {
979
- while (world.getNumCollisionObjects() > 0) {
980
- const obj = world.getCollisionObjectArray().at(0);
981
- world.removeCollisionObject(obj);
982
- if (obj && obj.destroy) obj.destroy();
983
- }
984
- if (world.destroy) world.destroy();
985
- } catch (e) {
986
- }
987
- });
988
900
  helperObjects.length = 0;
989
901
  }
990
902
  } catch (error2) {
@@ -993,7 +905,6 @@ var MMDPlayerEnhanced = ({
993
905
  const newHelper = new threeStdlib.MMDAnimationHelper();
994
906
  helperRef.current = newHelper;
995
907
  clockRef.current = new THREE2__namespace.Clock();
996
- const { mesh, vmd, cameraVmd } = vmdDataRef.current;
997
908
  if (vmd && typeof vmd === "object") {
998
909
  try {
999
910
  newHelper.add(mesh, {
@@ -1022,7 +933,6 @@ var MMDPlayerEnhanced = ({
1022
933
  audioRef.current.currentTime = 0;
1023
934
  }
1024
935
  setNeedReset(false);
1025
- console.log("\u2705 \u5F3A\u5236\u6E05\u7406\u5B8C\u6210\uFF0C\u5F00\u59CB\u64AD\u653E");
1026
936
  }
1027
937
  if (!helperRef.current) {
1028
938
  console.error("\u274C [play] helper \u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u64AD\u653E");
@@ -1057,7 +967,6 @@ var MMDPlayerEnhanced = ({
1057
967
  };
1058
968
  const stop = () => {
1059
969
  if (!helperRef.current || !sceneRef.current) return;
1060
- console.log("\u23F9\uFE0F \u505C\u6B62\u64AD\u653E\uFF0C\u5F00\u59CB\u6E05\u7406\u7269\u7406\u7CFB\u7EDF...");
1061
970
  isPlayingRef.current = false;
1062
971
  setIsPlaying(false);
1063
972
  if (audioRef.current) {
@@ -1066,73 +975,6 @@ var MMDPlayerEnhanced = ({
1066
975
  }
1067
976
  clockRef.current.stop();
1068
977
  clockRef.current = new THREE2__namespace.Clock();
1069
- try {
1070
- helperRef.current.enable("animation", false);
1071
- helperRef.current.enable("ik", false);
1072
- helperRef.current.enable("grant", false);
1073
- helperRef.current.enable("physics", false);
1074
- const helperObjects = helperRef.current.objects;
1075
- if (helperObjects && Array.isArray(helperObjects)) {
1076
- const physicsWorldsToDestroy = /* @__PURE__ */ new Set();
1077
- helperObjects.forEach((obj) => {
1078
- if (obj.physics) {
1079
- try {
1080
- const physics = obj.physics;
1081
- if (physics.world) {
1082
- physicsWorldsToDestroy.add(physics.world);
1083
- }
1084
- if (physics.bodies && Array.isArray(physics.bodies)) {
1085
- physics.bodies.forEach((body) => {
1086
- if (physics.world && body) {
1087
- try {
1088
- physics.world.removeRigidBody(body);
1089
- if (window.Ammo && body.destroy) {
1090
- body.destroy();
1091
- }
1092
- } catch (e) {
1093
- }
1094
- }
1095
- });
1096
- physics.bodies.length = 0;
1097
- physics.bodies = null;
1098
- }
1099
- if (physics.constraints && Array.isArray(physics.constraints)) {
1100
- physics.constraints.forEach((constraint) => {
1101
- if (physics.world && constraint) {
1102
- try {
1103
- physics.world.removeConstraint(constraint);
1104
- if (window.Ammo && constraint.destroy) {
1105
- constraint.destroy();
1106
- }
1107
- } catch (e) {
1108
- }
1109
- }
1110
- });
1111
- physics.constraints.length = 0;
1112
- physics.constraints = null;
1113
- }
1114
- physics.world = null;
1115
- obj.physics = null;
1116
- } catch (e) {
1117
- }
1118
- }
1119
- });
1120
- physicsWorldsToDestroy.forEach((world) => {
1121
- try {
1122
- while (world.getNumCollisionObjects() > 0) {
1123
- const obj = world.getCollisionObjectArray().at(0);
1124
- world.removeCollisionObject(obj);
1125
- if (obj && obj.destroy) obj.destroy();
1126
- }
1127
- if (world.destroy) world.destroy();
1128
- } catch (e) {
1129
- }
1130
- });
1131
- helperObjects.length = 0;
1132
- }
1133
- } catch (error2) {
1134
- console.warn("\u505C\u6B62\u65F6\u6E05\u7406\u7269\u7406\u7CFB\u7EDF\u5931\u8D25:", error2);
1135
- }
1136
978
  const mesh = sceneRef.current.children.find(
1137
979
  (child) => child.type === "SkinnedMesh" || child.isSkinnedMesh
1138
980
  );
@@ -1151,7 +993,7 @@ var MMDPlayerEnhanced = ({
1151
993
  }
1152
994
  }
1153
995
  setNeedReset(true);
1154
- console.log("\u2705 \u505C\u6B62\u64AD\u653E\u5E76\u6E05\u7406\u5B8C\u6210\uFF0CneedReset = true");
996
+ console.log("\u23F9\uFE0F \u505C\u6B62\u64AD\u653E\u5E76\u91CD\u7F6E\u5230\u521D\u59CB\u72B6\u6001\uFF0CneedReset = true");
1155
997
  };
1156
998
  const handleResourceChange = (resourceId) => {
1157
999
  console.log("\u{1F504} [MMDPlayerEnhanced] \u5207\u6362\u8D44\u6E90:", resourceId);
@@ -1432,7 +1274,8 @@ var MMDPlayerEnhanced = ({
1432
1274
  },
1433
1275
  background.name
1434
1276
  )))))));
1435
- };
1277
+ });
1278
+ MMDPlayerEnhanced.displayName = "MMDPlayerEnhanced";
1436
1279
  var MMDPlaylist = ({
1437
1280
  playlist,
1438
1281
  stage,
@@ -1455,6 +1298,8 @@ var MMDPlaylist = ({
1455
1298
  const currentNodeIndexRef = React2.useRef(defaultNodeIndex);
1456
1299
  const isAutoSwitchRef = React2.useRef(false);
1457
1300
  const playerRefsMap = React2.useRef(/* @__PURE__ */ new Map());
1301
+ const playerComponentRefs = React2.useRef(/* @__PURE__ */ new Map());
1302
+ const [memoryUsage, setMemoryUsage] = React2.useState(0);
1458
1303
  React2.useEffect(() => {
1459
1304
  currentNodeIndexRef.current = currentNodeIndex;
1460
1305
  }, [currentNodeIndex]);
@@ -1465,24 +1310,62 @@ var MMDPlaylist = ({
1465
1310
  }
1466
1311
  console.log("\u{1F3AF} [MMDPlaylist] \u5F53\u524D\u8282\u70B9:", currentNode.name, "\u7D22\u5F15:", currentNodeIndex);
1467
1312
  const stopNode = (nodeIndex) => {
1468
- const playerElement = playerRefsMap.current.get(nodeIndex);
1469
- if (!playerElement) return;
1470
- console.log(`\u23F9\uFE0F [MMDPlaylist] \u505C\u6B62\u8282\u70B9 ${nodeIndex}`);
1471
- const audioElement = playerElement.querySelector("audio");
1472
- if (audioElement) {
1473
- audioElement.pause();
1474
- audioElement.currentTime = 0;
1475
- console.log(` \u{1F507} \u505C\u6B62\u97F3\u9891`);
1313
+ const playerComponent = playerComponentRefs.current.get(nodeIndex);
1314
+ if (playerComponent && playerComponent.stopCompletely) {
1315
+ console.log(`\u23F9\uFE0F [MMDPlaylist] \u505C\u6B62\u8282\u70B9 ${nodeIndex}`);
1316
+ playerComponent.stopCompletely();
1317
+ } else {
1318
+ const playerElement = playerRefsMap.current.get(nodeIndex);
1319
+ if (!playerElement) return;
1320
+ console.log(`\u23F9\uFE0F [MMDPlaylist] \u505C\u6B62\u8282\u70B9 ${nodeIndex} (DOM\u65B9\u5F0F)`);
1321
+ const audioElement = playerElement.querySelector("audio");
1322
+ if (audioElement) {
1323
+ audioElement.pause();
1324
+ audioElement.currentTime = 0;
1325
+ console.log(` \u{1F507} \u505C\u6B62\u97F3\u9891`);
1326
+ }
1327
+ const stopEvent = new CustomEvent("stopCompletely");
1328
+ playerElement.dispatchEvent(stopEvent);
1329
+ console.log(` \u{1F4E1} \u53D1\u9001\u505C\u6B62\u4E8B\u4EF6`);
1476
1330
  }
1477
- const stopButton = playerElement.querySelector('button[title="\u505C\u6B62"]');
1478
- if (stopButton) {
1479
- stopButton.click();
1480
- console.log(` \u23F9\uFE0F \u70B9\u51FB\u505C\u6B62\u6309\u94AE`);
1331
+ };
1332
+ const clearNodeResources = (nodeIndex, excludeCurrent = true) => {
1333
+ if (excludeCurrent && nodeIndex === currentNodeIndex) {
1334
+ console.log(`\u26A0\uFE0F [MMDPlaylist] \u8DF3\u8FC7\u6E05\u7406\u5F53\u524D\u64AD\u653E\u8282\u70B9 ${nodeIndex}`);
1335
+ return;
1336
+ }
1337
+ const playerComponent = playerComponentRefs.current.get(nodeIndex);
1338
+ if (playerComponent && playerComponent.clearResources) {
1339
+ console.log(`\u{1F9F9} [MMDPlaylist] \u6E05\u7406\u8282\u70B9 ${nodeIndex} \u8D44\u6E90`);
1340
+ playerComponent.clearResources();
1481
1341
  } else {
1482
- const pauseButton = playerElement.querySelector('button[title="\u6682\u505C"]');
1483
- if (pauseButton) {
1484
- pauseButton.click();
1485
- console.log(` \u23F8\uFE0F \u70B9\u51FB\u6682\u505C\u6309\u94AE`);
1342
+ const playerElement = playerRefsMap.current.get(nodeIndex);
1343
+ if (playerElement) {
1344
+ const cleanupEvent = new CustomEvent("cleanupResources");
1345
+ playerElement.dispatchEvent(cleanupEvent);
1346
+ }
1347
+ }
1348
+ };
1349
+ const emergencyMemoryCleanup = () => {
1350
+ if (window.performance?.memory) {
1351
+ const memInfo = window.performance.memory;
1352
+ const usage = memInfo.usedJSHeapSize / memInfo.totalJSHeapSize;
1353
+ if (usage > 0.9) {
1354
+ console.error(`\u{1F6A8} [MMDPlaylist] \u5185\u5B58\u4F7F\u7528\u4E25\u91CD\u8FC7\u9AD8 (${(usage * 100).toFixed(1)}%)\uFF0C\u7D27\u6025\u6E05\u7406`);
1355
+ const nodesToClean = editableNodes.map((_, index) => ({
1356
+ index,
1357
+ distance: Math.abs(index - currentNodeIndex)
1358
+ })).filter((node) => node.distance > 2).sort((a, b) => b.distance - a.distance).slice(0, 2);
1359
+ nodesToClean.forEach(({ index }) => {
1360
+ console.warn(`\u{1F9F9} [MMDPlaylist] \u7D27\u6025\u6E05\u7406\u8282\u70B9 ${index}`);
1361
+ clearNodeResources(index, false);
1362
+ });
1363
+ if (window.gc) {
1364
+ try {
1365
+ window.gc();
1366
+ } catch (e) {
1367
+ }
1368
+ }
1486
1369
  }
1487
1370
  }
1488
1371
  };
@@ -1493,6 +1376,19 @@ var MMDPlaylist = ({
1493
1376
  stopNode(index);
1494
1377
  }
1495
1378
  });
1379
+ const nodesToKeep = /* @__PURE__ */ new Set();
1380
+ nodesToKeep.add(currentNodeIndex);
1381
+ if (playlist.loop && currentNodeIndex > 0) {
1382
+ nodesToKeep.add(currentNodeIndex - 1);
1383
+ }
1384
+ if (currentNodeIndex < editableNodes.length - 1) {
1385
+ nodesToKeep.add(currentNodeIndex + 1);
1386
+ }
1387
+ editableNodes.forEach((_, index) => {
1388
+ if (!nodesToKeep.has(index)) {
1389
+ clearNodeResources(index, false);
1390
+ }
1391
+ });
1496
1392
  onNodeChange?.(currentNodeIndex, currentNode);
1497
1393
  if (!isPreloading && (isAutoSwitchRef.current || playlist.autoPlay)) {
1498
1394
  console.log(`\u25B6\uFE0F [MMDPlaylist] \u51C6\u5907\u64AD\u653E\u8282\u70B9 ${currentNodeIndex}`);
@@ -1515,7 +1411,7 @@ var MMDPlaylist = ({
1515
1411
  }
1516
1412
  });
1517
1413
  }
1518
- }, [currentNodeIndex, currentNode, onNodeChange, isPreloading, playlist.autoPlay, preloadedNodes, editableNodes]);
1414
+ }, [currentNodeIndex, currentNode, onNodeChange, isPreloading, playlist.autoPlay, playlist.loop, preloadedNodes, editableNodes]);
1519
1415
  const handleNodePreloaded = (nodeIndex) => {
1520
1416
  console.log(`\u2705 [MMDPlaylist] \u8282\u70B9 ${nodeIndex} \u9884\u52A0\u8F7D\u5B8C\u6210`);
1521
1417
  setPreloadedNodes((prev) => {
@@ -1534,6 +1430,20 @@ var MMDPlaylist = ({
1534
1430
  setPreloadProgress(progress);
1535
1431
  }
1536
1432
  }, [preloadedNodes, editableNodes.length, onLoad]);
1433
+ React2.useEffect(() => {
1434
+ const checkMemory = () => {
1435
+ if (window.performance?.memory) {
1436
+ const memInfo = window.performance.memory;
1437
+ const usage = memInfo.usedJSHeapSize / memInfo.totalJSHeapSize;
1438
+ setMemoryUsage(usage);
1439
+ if (usage > 0.9) {
1440
+ emergencyMemoryCleanup();
1441
+ }
1442
+ }
1443
+ };
1444
+ const interval = setInterval(checkMemory, 15e3);
1445
+ return () => clearInterval(interval);
1446
+ }, [currentNodeIndex, editableNodes]);
1537
1447
  const handlePlaybackEnded = (nodeIndex) => {
1538
1448
  console.log(`\u{1F3B5} [MMDPlaylist] \u8282\u70B9 ${nodeIndex} \u64AD\u653E\u5B8C\u6210`);
1539
1449
  if (nodeIndex !== currentNodeIndexRef.current) {
@@ -1643,6 +1553,11 @@ var MMDPlaylist = ({
1643
1553
  /* @__PURE__ */ React2__default.default.createElement(
1644
1554
  MMDPlayerEnhanced,
1645
1555
  {
1556
+ ref: (componentRef) => {
1557
+ if (componentRef) {
1558
+ playerComponentRefs.current.set(index, componentRef);
1559
+ }
1560
+ },
1646
1561
  resources: node.resources,
1647
1562
  stage,
1648
1563
  autoPlay: index === currentNodeIndex && shouldAutoPlayInitial,