@nice2dev/ui-3d 1.1.0 → 1.1.1

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 (206) hide show
  1. package/dist/cjs/collaborative/collaborativeScene.js +16 -8
  2. package/dist/cjs/collaborative/collaborativeScene.js.map +1 -1
  3. package/dist/cjs/core/i18n.js +3 -3
  4. package/dist/cjs/dance/DanceBridge.js +65 -30
  5. package/dist/cjs/dance/DanceBridge.js.map +1 -1
  6. package/dist/cjs/dance/DanceScoreEngine.js +37 -23
  7. package/dist/cjs/dance/DanceScoreEngine.js.map +1 -1
  8. package/dist/cjs/dance/PoseDetector.js +92 -49
  9. package/dist/cjs/dance/PoseDetector.js.map +1 -1
  10. package/dist/cjs/material/NiceMaterialEditor.js +134 -91
  11. package/dist/cjs/material/NiceMaterialEditor.js.map +1 -1
  12. package/dist/cjs/material/materialEditorUtils.js +86 -55
  13. package/dist/cjs/material/materialEditorUtils.js.map +1 -1
  14. package/dist/cjs/model/ModelEditor.js +16 -14
  15. package/dist/cjs/model/ModelEditor.js.map +1 -1
  16. package/dist/cjs/model/ModelEditorLeftPanel.js +21 -15
  17. package/dist/cjs/model/ModelEditorLeftPanel.js.map +1 -1
  18. package/dist/cjs/model/ModelEditorMenuBar.js +23 -10
  19. package/dist/cjs/model/ModelEditorMenuBar.js.map +1 -1
  20. package/dist/cjs/model/ModelEditorRightPanel.js +32 -27
  21. package/dist/cjs/model/ModelEditorRightPanel.js.map +1 -1
  22. package/dist/cjs/model/ModelEditorSubComponents.js +20 -20
  23. package/dist/cjs/model/ModelEditorSubComponents.js.map +1 -1
  24. package/dist/cjs/model/ModelEditorTimeline.js +12 -11
  25. package/dist/cjs/model/ModelEditorTimeline.js.map +1 -1
  26. package/dist/cjs/model/ModelEditorToolbar.js +5 -5
  27. package/dist/cjs/model/ModelEditorToolbar.js.map +1 -1
  28. package/dist/cjs/model/ModelEditorViewport.js +12 -8
  29. package/dist/cjs/model/ModelEditorViewport.js.map +1 -1
  30. package/dist/cjs/model/ModelViewer.js +16 -15
  31. package/dist/cjs/model/ModelViewer.js.map +1 -1
  32. package/dist/cjs/model/NiceArmatureEditor.js +53 -30
  33. package/dist/cjs/model/NiceArmatureEditor.js.map +1 -1
  34. package/dist/cjs/model/NiceMorphTargetEditor.js +51 -30
  35. package/dist/cjs/model/NiceMorphTargetEditor.js.map +1 -1
  36. package/dist/cjs/model/NiceOctree.js +62 -37
  37. package/dist/cjs/model/NiceOctree.js.map +1 -1
  38. package/dist/cjs/model/NicePhysicsSimulation.js +74 -36
  39. package/dist/cjs/model/NicePhysicsSimulation.js.map +1 -1
  40. package/dist/cjs/model/NiceProceduralGeometry.js +101 -31
  41. package/dist/cjs/model/NiceProceduralGeometry.js.map +1 -1
  42. package/dist/cjs/model/NiceTerrainEditor.js +88 -38
  43. package/dist/cjs/model/NiceTerrainEditor.js.map +1 -1
  44. package/dist/cjs/model/NiceWeightPainter.js +46 -24
  45. package/dist/cjs/model/NiceWeightPainter.js.map +1 -1
  46. package/dist/cjs/model/NiceXRPreview.js +89 -43
  47. package/dist/cjs/model/NiceXRPreview.js.map +1 -1
  48. package/dist/cjs/model/cadModeUtils.js +2 -2
  49. package/dist/cjs/model/cadModeUtils.js.map +1 -1
  50. package/dist/cjs/model/editorShortcuts.js +69 -35
  51. package/dist/cjs/model/editorShortcuts.js.map +1 -1
  52. package/dist/cjs/model/modelEditorTypes.js +56 -52
  53. package/dist/cjs/model/modelEditorTypes.js.map +1 -1
  54. package/dist/cjs/model/modelEditorUtils.js +356 -131
  55. package/dist/cjs/model/modelEditorUtils.js.map +1 -1
  56. package/dist/cjs/model/simsModeUtils.js +819 -74
  57. package/dist/cjs/model/simsModeUtils.js.map +1 -1
  58. package/dist/cjs/model/useModelEditor.js +501 -394
  59. package/dist/cjs/model/useModelEditor.js.map +1 -1
  60. package/dist/cjs/model/useModelViewer.js +117 -86
  61. package/dist/cjs/model/useModelViewer.js.map +1 -1
  62. package/dist/cjs/particle/NiceParticleEditor.js +126 -69
  63. package/dist/cjs/particle/NiceParticleEditor.js.map +1 -1
  64. package/dist/cjs/particle/particleEditorUtils.js +64 -27
  65. package/dist/cjs/particle/particleEditorUtils.js.map +1 -1
  66. package/dist/cjs/rendering/NiceCascadedShadows.js +35 -23
  67. package/dist/cjs/rendering/NiceCascadedShadows.js.map +1 -1
  68. package/dist/cjs/rendering/NiceRenderExport.js +26 -12
  69. package/dist/cjs/rendering/NiceRenderExport.js.map +1 -1
  70. package/dist/cjs/rendering/NiceSSAO.js +28 -23
  71. package/dist/cjs/rendering/NiceSSAO.js.map +1 -1
  72. package/dist/cjs/rendering/NiceSSR.js +22 -18
  73. package/dist/cjs/rendering/NiceSSR.js.map +1 -1
  74. package/dist/cjs/rendering/NiceWebGPURenderer.js +60 -37
  75. package/dist/cjs/rendering/NiceWebGPURenderer.js.map +1 -1
  76. package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js +572 -0
  77. package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
  78. package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js +4979 -0
  79. package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
  80. package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js +98 -0
  81. package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
  82. package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5244 -0
  83. package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
  84. package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js +174 -0
  85. package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
  86. package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js +10459 -0
  87. package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
  88. package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6313 -0
  89. package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
  90. package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js +1639 -0
  91. package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
  92. package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js +807 -0
  93. package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
  94. package/dist/cjs/ui/dist/charts-BAS21jkC.js +3833 -0
  95. package/dist/cjs/ui/dist/charts-BAS21jkC.js.map +1 -0
  96. package/dist/cjs/ui/dist/core-Da43q-pO.js +11683 -0
  97. package/dist/cjs/ui/dist/core-Da43q-pO.js.map +1 -0
  98. package/dist/cjs/ui/dist/index-pAuGTbuB.js +28876 -0
  99. package/dist/cjs/ui/dist/index-pAuGTbuB.js.map +1 -0
  100. package/dist/cjs/uv/NiceUVEditor.js +115 -86
  101. package/dist/cjs/uv/NiceUVEditor.js.map +1 -1
  102. package/dist/cjs/uv/uvEditorUtils.js +41 -23
  103. package/dist/cjs/uv/uvEditorUtils.js.map +1 -1
  104. package/dist/esm/collaborative/collaborativeScene.js +16 -8
  105. package/dist/esm/collaborative/collaborativeScene.js.map +1 -1
  106. package/dist/esm/dance/DanceBridge.js +55 -20
  107. package/dist/esm/dance/DanceBridge.js.map +1 -1
  108. package/dist/esm/dance/DanceScoreEngine.js +29 -15
  109. package/dist/esm/dance/DanceScoreEngine.js.map +1 -1
  110. package/dist/esm/dance/PoseDetector.js +73 -30
  111. package/dist/esm/dance/PoseDetector.js.map +1 -1
  112. package/dist/esm/material/NiceMaterialEditor.js +115 -72
  113. package/dist/esm/material/NiceMaterialEditor.js.map +1 -1
  114. package/dist/esm/material/materialEditorUtils.js +86 -55
  115. package/dist/esm/material/materialEditorUtils.js.map +1 -1
  116. package/dist/esm/model/ModelEditor.js +16 -14
  117. package/dist/esm/model/ModelEditor.js.map +1 -1
  118. package/dist/esm/model/ModelEditorLeftPanel.js +21 -15
  119. package/dist/esm/model/ModelEditorLeftPanel.js.map +1 -1
  120. package/dist/esm/model/ModelEditorMenuBar.js +23 -10
  121. package/dist/esm/model/ModelEditorMenuBar.js.map +1 -1
  122. package/dist/esm/model/ModelEditorRightPanel.js +32 -27
  123. package/dist/esm/model/ModelEditorRightPanel.js.map +1 -1
  124. package/dist/esm/model/ModelEditorSubComponents.js +17 -17
  125. package/dist/esm/model/ModelEditorSubComponents.js.map +1 -1
  126. package/dist/esm/model/ModelEditorTimeline.js +12 -11
  127. package/dist/esm/model/ModelEditorTimeline.js.map +1 -1
  128. package/dist/esm/model/ModelEditorToolbar.js +5 -5
  129. package/dist/esm/model/ModelEditorToolbar.js.map +1 -1
  130. package/dist/esm/model/ModelEditorViewport.js +12 -8
  131. package/dist/esm/model/ModelEditorViewport.js.map +1 -1
  132. package/dist/esm/model/ModelViewer.js +8 -7
  133. package/dist/esm/model/ModelViewer.js.map +1 -1
  134. package/dist/esm/model/NiceArmatureEditor.js +36 -13
  135. package/dist/esm/model/NiceArmatureEditor.js.map +1 -1
  136. package/dist/esm/model/NiceMorphTargetEditor.js +34 -13
  137. package/dist/esm/model/NiceMorphTargetEditor.js.map +1 -1
  138. package/dist/esm/model/NiceOctree.js +47 -22
  139. package/dist/esm/model/NiceOctree.js.map +1 -1
  140. package/dist/esm/model/NicePhysicsSimulation.js +51 -13
  141. package/dist/esm/model/NicePhysicsSimulation.js.map +1 -1
  142. package/dist/esm/model/NiceProceduralGeometry.js +94 -24
  143. package/dist/esm/model/NiceProceduralGeometry.js.map +1 -1
  144. package/dist/esm/model/NiceTerrainEditor.js +70 -20
  145. package/dist/esm/model/NiceTerrainEditor.js.map +1 -1
  146. package/dist/esm/model/NiceWeightPainter.js +33 -11
  147. package/dist/esm/model/NiceWeightPainter.js.map +1 -1
  148. package/dist/esm/model/NiceXRPreview.js +72 -26
  149. package/dist/esm/model/NiceXRPreview.js.map +1 -1
  150. package/dist/esm/model/cadModeUtils.js +2 -2
  151. package/dist/esm/model/cadModeUtils.js.map +1 -1
  152. package/dist/esm/model/editorShortcuts.js +69 -35
  153. package/dist/esm/model/editorShortcuts.js.map +1 -1
  154. package/dist/esm/model/modelEditorTypes.js +56 -52
  155. package/dist/esm/model/modelEditorTypes.js.map +1 -1
  156. package/dist/esm/model/modelEditorUtils.js +356 -131
  157. package/dist/esm/model/modelEditorUtils.js.map +1 -1
  158. package/dist/esm/model/simsModeUtils.js +819 -74
  159. package/dist/esm/model/simsModeUtils.js.map +1 -1
  160. package/dist/esm/model/useModelEditor.js +384 -277
  161. package/dist/esm/model/useModelEditor.js.map +1 -1
  162. package/dist/esm/model/useModelViewer.js +56 -25
  163. package/dist/esm/model/useModelViewer.js.map +1 -1
  164. package/dist/esm/particle/NiceParticleEditor.js +116 -59
  165. package/dist/esm/particle/NiceParticleEditor.js.map +1 -1
  166. package/dist/esm/particle/particleEditorUtils.js +64 -27
  167. package/dist/esm/particle/particleEditorUtils.js.map +1 -1
  168. package/dist/esm/rendering/NiceCascadedShadows.js +24 -12
  169. package/dist/esm/rendering/NiceCascadedShadows.js.map +1 -1
  170. package/dist/esm/rendering/NiceRenderExport.js +22 -8
  171. package/dist/esm/rendering/NiceRenderExport.js.map +1 -1
  172. package/dist/esm/rendering/NiceSSAO.js +11 -6
  173. package/dist/esm/rendering/NiceSSAO.js.map +1 -1
  174. package/dist/esm/rendering/NiceSSR.js +9 -5
  175. package/dist/esm/rendering/NiceSSR.js.map +1 -1
  176. package/dist/esm/rendering/NiceWebGPURenderer.js +40 -17
  177. package/dist/esm/rendering/NiceWebGPURenderer.js.map +1 -1
  178. package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js +563 -0
  179. package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
  180. package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js +4960 -0
  181. package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
  182. package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js +96 -0
  183. package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
  184. package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5201 -0
  185. package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
  186. package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js +172 -0
  187. package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
  188. package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js +10408 -0
  189. package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
  190. package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6296 -0
  191. package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
  192. package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js +1630 -0
  193. package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
  194. package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js +796 -0
  195. package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
  196. package/dist/esm/ui/dist/charts-BAS21jkC.js +3809 -0
  197. package/dist/esm/ui/dist/charts-BAS21jkC.js.map +1 -0
  198. package/dist/esm/ui/dist/core-Da43q-pO.js +11502 -0
  199. package/dist/esm/ui/dist/core-Da43q-pO.js.map +1 -0
  200. package/dist/esm/ui/dist/index-pAuGTbuB.js +28792 -0
  201. package/dist/esm/ui/dist/index-pAuGTbuB.js.map +1 -0
  202. package/dist/esm/uv/NiceUVEditor.js +91 -62
  203. package/dist/esm/uv/NiceUVEditor.js.map +1 -1
  204. package/dist/esm/uv/uvEditorUtils.js +41 -23
  205. package/dist/esm/uv/uvEditorUtils.js.map +1 -1
  206. package/package.json +2 -2
@@ -1,28 +1,28 @@
1
1
  'use strict';
2
2
 
3
- var ke = require('react');
3
+ var _e = require('react');
4
4
  var THREE = require('three');
5
5
  var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
6
6
  var TransformControls_js = require('three/examples/jsm/controls/TransformControls.js');
7
- var FBXLoader_js = require('three/examples/jsm/loaders/FBXLoader.js');
8
- var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
9
7
  var GLTFExporter_js = require('three/examples/jsm/exporters/GLTFExporter.js');
10
8
  var OBJExporter_js = require('three/examples/jsm/exporters/OBJExporter.js');
11
- var STLExporter_js = require('three/examples/jsm/exporters/STLExporter.js');
12
9
  var PLYExporter_js = require('three/examples/jsm/exporters/PLYExporter.js');
10
+ var STLExporter_js = require('three/examples/jsm/exporters/STLExporter.js');
13
11
  var USDZExporter_js = require('three/examples/jsm/exporters/USDZExporter.js');
14
- var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
15
- var ColladaLoader_js = require('three/examples/jsm/loaders/ColladaLoader.js');
16
- var STLLoader_js = require('three/examples/jsm/loaders/STLLoader.js');
17
- var PLYLoader_js = require('three/examples/jsm/loaders/PLYLoader.js');
18
- var TDSLoader_js = require('three/examples/jsm/loaders/TDSLoader.js');
19
12
  var _3MFLoader_js = require('three/examples/jsm/loaders/3MFLoader.js');
20
13
  var AMFLoader_js = require('three/examples/jsm/loaders/AMFLoader.js');
21
- var PCDLoader_js = require('three/examples/jsm/loaders/PCDLoader.js');
22
- var VTKLoader_js = require('three/examples/jsm/loaders/VTKLoader.js');
23
- var VRMLLoader_js = require('three/examples/jsm/loaders/VRMLLoader.js');
14
+ var ColladaLoader_js = require('three/examples/jsm/loaders/ColladaLoader.js');
15
+ var FBXLoader_js = require('three/examples/jsm/loaders/FBXLoader.js');
24
16
  var GCodeLoader_js = require('three/examples/jsm/loaders/GCodeLoader.js');
17
+ var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
18
+ var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
19
+ var PCDLoader_js = require('three/examples/jsm/loaders/PCDLoader.js');
20
+ var PLYLoader_js = require('three/examples/jsm/loaders/PLYLoader.js');
21
+ var STLLoader_js = require('three/examples/jsm/loaders/STLLoader.js');
25
22
  var SVGLoader_js = require('three/examples/jsm/loaders/SVGLoader.js');
23
+ var TDSLoader_js = require('three/examples/jsm/loaders/TDSLoader.js');
24
+ var VRMLLoader_js = require('three/examples/jsm/loaders/VRMLLoader.js');
25
+ var VTKLoader_js = require('three/examples/jsm/loaders/VTKLoader.js');
26
26
  var modelEditorTypes = require('./modelEditorTypes.js');
27
27
  var modelEditorUtils = require('./modelEditorUtils.js');
28
28
 
@@ -59,11 +59,13 @@ const nid = () => `n${++_nid}`;
59
59
  /* ── Pure helper — extracted outside the hook for stable identity ── */
60
60
  function findNodeById(nodes, id) {
61
61
  for (const n of nodes) {
62
- if (n.id === id)
62
+ if (n.id === id) {
63
63
  return n;
64
+ }
64
65
  const found = findNodeById(n.children, id);
65
- if (found)
66
+ if (found) {
66
67
  return found;
68
+ }
67
69
  }
68
70
  return null;
69
71
  }
@@ -72,85 +74,86 @@ function findNodeById(nodes, id) {
72
74
  ═══════════════════════════════════════════ */
73
75
  function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
74
76
  /* ── refs ── */
75
- const mountRef = ke.useRef(null);
76
- const rendererRef = ke.useRef(null);
77
- const sceneRef = ke.useRef(new THREE__namespace.Scene());
78
- const cameraRef = ke.useRef(new THREE__namespace.PerspectiveCamera(50, 1, 0.01, 10000));
79
- const orbitRef = ke.useRef(null);
80
- const transformRef = ke.useRef(null);
81
- const clockRef = ke.useRef(new THREE__namespace.Clock());
82
- const mixerRef = ke.useRef(null);
83
- const rafRef = ke.useRef(0);
84
- const fileInputRef = ke.useRef(null);
85
- const mergeInputRef = ke.useRef(null);
86
- const videoInputRef = ke.useRef(null);
87
- const raycasterRef = ke.useRef(new THREE__namespace.Raycaster());
88
- const skeletonHelperRef = ke.useRef(null);
77
+ const mountRef = _e.useRef(null);
78
+ const rendererRef = _e.useRef(null);
79
+ const sceneRef = _e.useRef(new THREE__namespace.Scene());
80
+ const cameraRef = _e.useRef(new THREE__namespace.PerspectiveCamera(50, 1, 0.01, 10000));
81
+ const orbitRef = _e.useRef(null);
82
+ const transformRef = _e.useRef(null);
83
+ const clockRef = _e.useRef(new THREE__namespace.Clock());
84
+ const mixerRef = _e.useRef(null);
85
+ const rafRef = _e.useRef(0);
86
+ const fileInputRef = _e.useRef(null);
87
+ const mergeInputRef = _e.useRef(null);
88
+ const videoInputRef = _e.useRef(null);
89
+ const raycasterRef = _e.useRef(new THREE__namespace.Raycaster());
90
+ const skeletonHelperRef = _e.useRef(null);
89
91
  /* ── state ── */
90
- const [sceneTree, setSceneTree] = ke.useState([]);
91
- const [selectedId, setSelectedId] = ke.useState(null);
92
- const [transformMode, setTransformMode] = ke.useState("translate");
93
- const [animations, setAnimations] = ke.useState([]);
94
- const [activeAnimIdx, setActiveAnimIdx] = ke.useState(-1);
95
- const [isPlaying, setIsPlaying] = ke.useState(false);
96
- const [animTime, setAnimTime] = ke.useState(0);
97
- const [animDuration, setAnimDuration] = ke.useState(0);
98
- const [animSpeed, setAnimSpeed] = ke.useState(1);
99
- const [loopAnim, setLoopAnim] = ke.useState(true);
100
- const [bottomCollapsed, setBottomCollapsed] = ke.useState(false);
101
- const [showGrid, setShowGrid] = ke.useState(true);
102
- const [showAxes, setShowAxes] = ke.useState(true);
103
- const [wireframe, setWireframe] = ke.useState(false);
104
- const [bgColor, setBgColor] = ke.useState("#111122");
105
- const [statusText, setStatusText] = ke.useState("Ready");
106
- const [dragOver, setDragOver] = ke.useState(false);
107
- const [polyCount, setPolyCount] = ke.useState(0);
108
- const [meshCount, setMeshCount] = ke.useState(0);
109
- const [boneCount, setBoneCount] = ke.useState(0);
92
+ const [sceneTree, setSceneTree] = _e.useState([]);
93
+ const [selectedId, setSelectedId] = _e.useState(null);
94
+ const [transformMode, setTransformMode] = _e.useState('translate');
95
+ const [animations, setAnimations] = _e.useState([]);
96
+ const [activeAnimIdx, setActiveAnimIdx] = _e.useState(-1);
97
+ const [isPlaying, setIsPlaying] = _e.useState(false);
98
+ const [animTime, setAnimTime] = _e.useState(0);
99
+ const [animDuration, setAnimDuration] = _e.useState(0);
100
+ const [animSpeed, setAnimSpeed] = _e.useState(1);
101
+ const [loopAnim, setLoopAnim] = _e.useState(true);
102
+ const [bottomCollapsed, setBottomCollapsed] = _e.useState(false);
103
+ const [showGrid, setShowGrid] = _e.useState(true);
104
+ const [showAxes, setShowAxes] = _e.useState(true);
105
+ const [wireframe, setWireframe] = _e.useState(false);
106
+ const [bgColor, setBgColor] = _e.useState('#111122');
107
+ const [statusText, setStatusText] = _e.useState('Ready');
108
+ const [dragOver, setDragOver] = _e.useState(false);
109
+ const [polyCount, setPolyCount] = _e.useState(0);
110
+ const [meshCount, setMeshCount] = _e.useState(0);
111
+ const [boneCount, setBoneCount] = _e.useState(0);
110
112
  // Material editor
111
- const [selMaterialIdx, setSelMaterialIdx] = ke.useState(0);
112
- const [matRefresh, setMatRefresh] = ke.useState(0);
113
+ const [selMaterialIdx, setSelMaterialIdx] = _e.useState(0);
114
+ const [matRefresh, setMatRefresh] = _e.useState(0);
113
115
  // AI panel
114
- const [aiStatus, setAiStatus] = ke.useState("");
115
- const [aiBusy, setAiBusy] = ke.useState(false);
116
+ const [aiStatus, setAiStatus] = _e.useState('');
117
+ const [aiBusy, setAiBusy] = _e.useState(false);
116
118
  // Blender 2.0 — viewport & editor
117
- const [shadingMode, setShadingMode] = ke.useState("solid");
118
- const [editorMode, setEditorMode] = ke.useState("object");
119
- const [contextMenu, setContextMenu] = ke.useState(null);
120
- const [outlinerSearch, setOutlinerSearch] = ke.useState("");
121
- const [snapEnabled, setSnapEnabled] = ke.useState(false);
122
- const [snapGrid, setSnapGrid] = ke.useState(1);
123
- const [showSkeleton, setShowSkeleton] = ke.useState(true);
124
- const [addMenuOpen, setAddMenuOpen] = ke.useState(null);
125
- const [gizmoSpace, setGizmoSpace] = ke.useState("world");
126
- const [propTab, setPropTab] = ke.useState("object");
127
- const [fogEnabled, setFogEnabled] = ke.useState(false);
128
- const [fogColor, setFogColor] = ke.useState("#111122");
129
- const [fogNear, setFogNear] = ke.useState(10);
130
- const [fogFar, setFogFar] = ke.useState(100);
131
- const [showLightHelpers, setShowLightHelpers] = ke.useState(true);
119
+ const [shadingMode, setShadingMode] = _e.useState('solid');
120
+ const [editorMode, setEditorMode] = _e.useState('object');
121
+ const [contextMenu, setContextMenu] = _e.useState(null);
122
+ const [outlinerSearch, setOutlinerSearch] = _e.useState('');
123
+ const [snapEnabled, setSnapEnabled] = _e.useState(false);
124
+ const [snapGrid, setSnapGrid] = _e.useState(1);
125
+ const [showSkeleton, setShowSkeleton] = _e.useState(true);
126
+ const [addMenuOpen, setAddMenuOpen] = _e.useState(null);
127
+ const [gizmoSpace, setGizmoSpace] = _e.useState('world');
128
+ const [propTab, setPropTab] = _e.useState('object');
129
+ const [fogEnabled, setFogEnabled] = _e.useState(false);
130
+ const [fogColor, setFogColor] = _e.useState('#111122');
131
+ const [fogNear, setFogNear] = _e.useState(10);
132
+ const [fogFar, setFogFar] = _e.useState(100);
133
+ const [showLightHelpers, setShowLightHelpers] = _e.useState(true);
132
134
  // The root object that was loaded (mesh/character)
133
- const rootObjectRef = ke.useRef(null);
135
+ const rootObjectRef = _e.useRef(null);
134
136
  // Active animation action
135
- const activeActionRef = ke.useRef(null);
137
+ const activeActionRef = _e.useRef(null);
136
138
  // All loaded animation clips (for the current model)
137
- const allClipsRef = ke.useRef([]);
139
+ const allClipsRef = _e.useRef([]);
138
140
  /* keep refs current */
139
- const animTimeRef = ke.useRef(animTime);
141
+ const animTimeRef = _e.useRef(animTime);
140
142
  animTimeRef.current = animTime;
141
- const isPlayingRef = ke.useRef(isPlaying);
143
+ const isPlayingRef = _e.useRef(isPlaying);
142
144
  isPlayingRef.current = isPlaying;
143
- const animSpeedRef = ke.useRef(animSpeed);
145
+ const animSpeedRef = _e.useRef(animSpeed);
144
146
  animSpeedRef.current = animSpeed;
145
- const loopAnimRef = ke.useRef(loopAnim);
147
+ const loopAnimRef = _e.useRef(loopAnim);
146
148
  loopAnimRef.current = loopAnim;
147
149
  /* ─────────────────────────────────────────
148
150
  Scene setup
149
151
  ───────────────────────────────────────── */
150
- ke.useEffect(() => {
152
+ _e.useEffect(() => {
151
153
  const container = mountRef.current;
152
- if (!container)
154
+ if (!container) {
153
155
  return;
156
+ }
154
157
  const scene = sceneRef.current;
155
158
  const camera = cameraRef.current;
156
159
  // Renderer
@@ -167,13 +170,13 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
167
170
  camera.position.set(3, 2, 5);
168
171
  camera.lookAt(0, 0, 0);
169
172
  // Scene background
170
- scene.background = new THREE__namespace.Color("#111122");
173
+ scene.background = new THREE__namespace.Color('#111122');
171
174
  // Lights
172
175
  const ambientLight = new THREE__namespace.AmbientLight(0xffffff, 0.5);
173
- ambientLight.name = "__ambient";
176
+ ambientLight.name = '__ambient';
174
177
  scene.add(ambientLight);
175
178
  const dirLight = new THREE__namespace.DirectionalLight(0xffffff, 1.2);
176
- dirLight.name = "__dirLight";
179
+ dirLight.name = '__dirLight';
177
180
  dirLight.position.set(5, 8, 5);
178
181
  dirLight.castShadow = true;
179
182
  dirLight.shadow.mapSize.set(2048, 2048);
@@ -185,21 +188,21 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
185
188
  dirLight.shadow.camera.bottom = -10;
186
189
  scene.add(dirLight);
187
190
  const hemiLight = new THREE__namespace.HemisphereLight(0x8888ff, 0x444422, 0.4);
188
- hemiLight.name = "__hemiLight";
191
+ hemiLight.name = '__hemiLight';
189
192
  scene.add(hemiLight);
190
193
  // Ground plane
191
194
  const ground = new THREE__namespace.Mesh(new THREE__namespace.PlaneGeometry(100, 100), new THREE__namespace.ShadowMaterial({ opacity: 0.15 }));
192
- ground.name = "__ground";
195
+ ground.name = '__ground';
193
196
  ground.rotation.x = -Math.PI / 2;
194
197
  ground.receiveShadow = true;
195
198
  scene.add(ground);
196
199
  // Grid
197
200
  const grid = new THREE__namespace.GridHelper(20, 20, 0x333355, 0x22223a);
198
- grid.name = "__grid";
201
+ grid.name = '__grid';
199
202
  scene.add(grid);
200
203
  // Axes
201
204
  const axes = new THREE__namespace.AxesHelper(2);
202
- axes.name = "__axes";
205
+ axes.name = '__axes';
203
206
  scene.add(axes);
204
207
  // Orbit controls
205
208
  const orbit = new OrbitControls_js.OrbitControls(camera, renderer.domElement);
@@ -211,9 +214,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
211
214
  orbitRef.current = orbit;
212
215
  // Transform controls
213
216
  const tc = new TransformControls_js.TransformControls(camera, renderer.domElement);
214
- tc.name = "__transformControls";
217
+ tc.name = '__transformControls';
215
218
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
216
- tc.addEventListener("dragging-changed", ((e) => {
219
+ tc.addEventListener('dragging-changed', ((e) => {
217
220
  orbit.enabled = !e.value;
218
221
  }));
219
222
  scene.add(tc);
@@ -222,8 +225,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
222
225
  const onResize = () => {
223
226
  const w = container.clientWidth;
224
227
  const h = container.clientHeight;
225
- if (w === 0 || h === 0)
228
+ if (w === 0 || h === 0) {
226
229
  return;
230
+ }
227
231
  camera.aspect = w / h;
228
232
  camera.updateProjectionMatrix();
229
233
  renderer.setSize(w, h);
@@ -256,8 +260,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
256
260
  ro.disconnect();
257
261
  const scn = sceneRef.current;
258
262
  scn.traverse((obj) => {
259
- if (obj.geometry)
263
+ if (obj.geometry) {
260
264
  obj.geometry.dispose();
265
+ }
261
266
  if (obj.material) {
262
267
  const mats = Array.isArray(obj.material)
263
268
  ? obj.material
@@ -275,27 +280,29 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
275
280
  container.removeChild(renderer.domElement);
276
281
  }
277
282
  };
278
- // eslint-disable-next-line react-hooks/exhaustive-deps
279
283
  }, []);
280
284
  /* ─────────────────────────────────────────
281
285
  Rebuild scene tree from Three.js scene
282
286
  ───────────────────────────────────────── */
283
- const rebuildTree = ke.useCallback(() => {
287
+ const rebuildTree = _e.useCallback(() => {
284
288
  const scene = sceneRef.current;
285
289
  let polys = 0;
286
290
  let meshes = 0;
287
291
  let bones = 0;
288
292
  const walk = (obj, depth) => {
289
293
  var _a, _b;
290
- if (obj.name.startsWith("__"))
294
+ if (obj.name.startsWith('__')) {
291
295
  return null;
292
- if (obj.type === "TransformControlsPlane" || obj.type === "TransformControlsGizmo")
296
+ }
297
+ if (obj.type === 'TransformControlsPlane' || obj.type === 'TransformControlsGizmo') {
293
298
  return null;
294
- if ("isTransformControls" in obj)
299
+ }
300
+ if ('isTransformControls' in obj) {
295
301
  return null;
296
- let nodeType = "group";
302
+ }
303
+ let nodeType = 'group';
297
304
  if (obj instanceof THREE__namespace.Mesh) {
298
- nodeType = "mesh";
305
+ nodeType = 'mesh';
299
306
  meshes++;
300
307
  const geo = obj.geometry;
301
308
  if (geo) {
@@ -304,27 +311,28 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
304
311
  }
305
312
  }
306
313
  else if (obj instanceof THREE__namespace.Bone) {
307
- nodeType = "bone";
314
+ nodeType = 'bone';
308
315
  bones++;
309
316
  }
310
317
  else if (obj instanceof THREE__namespace.Light) {
311
- nodeType = "light";
318
+ nodeType = 'light';
312
319
  }
313
320
  else if (obj instanceof THREE__namespace.Camera) {
314
- nodeType = "camera";
321
+ nodeType = 'camera';
315
322
  }
316
- else if (obj instanceof THREE__namespace.GridHelper ||
317
- obj instanceof THREE__namespace.AxesHelper) {
318
- nodeType = "helper";
323
+ else if (obj instanceof THREE__namespace.GridHelper || obj instanceof THREE__namespace.AxesHelper) {
324
+ nodeType = 'helper';
319
325
  }
320
326
  const children = [];
321
327
  for (const c of obj.children) {
322
328
  const cn = walk(c, depth + 1);
323
- if (cn)
329
+ if (cn) {
324
330
  children.push(cn);
331
+ }
325
332
  }
326
333
  return {
327
- id: obj.userData.__nodeId || ((obj.userData.__nodeId = nid()), obj.userData.__nodeId),
334
+ id: obj.userData.__nodeId ||
335
+ ((obj.userData.__nodeId = nid()), obj.userData.__nodeId),
328
336
  name: obj.name || obj.type,
329
337
  type: nodeType,
330
338
  object: obj,
@@ -336,8 +344,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
336
344
  const roots = [];
337
345
  for (const c of scene.children) {
338
346
  const n = walk(c, 0);
339
- if (n)
347
+ if (n) {
340
348
  roots.push(n);
349
+ }
341
350
  }
342
351
  setSceneTree(roots);
343
352
  setPolyCount(Math.round(polys));
@@ -347,9 +356,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
347
356
  /* ─────────────────────────────────────────
348
357
  File loading
349
358
  ───────────────────────────────────────── */
350
- const playClipRef = ke.useRef(() => { });
351
- const focusOnObjectRef = ke.useRef(() => { });
352
- const addObjectToScene = ke.useCallback((obj, clips, filename) => {
359
+ const playClipRef = _e.useRef(() => { });
360
+ const focusOnObjectRef = _e.useRef(() => { });
361
+ const addObjectToScene = _e.useCallback((obj, clips, filename) => {
353
362
  const scene = sceneRef.current;
354
363
  obj.traverse((child) => {
355
364
  if (child instanceof THREE__namespace.Mesh) {
@@ -378,7 +387,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
378
387
  const mixer = new THREE__namespace.AnimationMixer(obj);
379
388
  mixerRef.current = mixer;
380
389
  const newAnims = clips.map((c) => ({
381
- name: c.name || "Animation",
390
+ name: c.name || 'Animation',
382
391
  clip: c,
383
392
  duration: c.duration,
384
393
  source: filename,
@@ -396,73 +405,81 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
396
405
  rebuildTree();
397
406
  setStatusText(`Loaded: ${filename} (${clips.length} animation(s))`);
398
407
  }, [rebuildTree]);
399
- const loadFile = ke.useCallback(async (file) => {
408
+ const loadFile = _e.useCallback(async (file) => {
400
409
  var _a, _b, _c, _d, _e, _f;
401
410
  const name = file.name.toLowerCase();
402
- const ext = "." + name.split(".").pop();
411
+ const ext = '.' + name.split('.').pop();
403
412
  const url = URL.createObjectURL(file);
404
413
  setStatusText(`Loading ${file.name}...`);
405
414
  try {
406
- if (ext === ".fbx") {
415
+ if (ext === '.fbx') {
407
416
  const loader = new FBXLoader_js.FBXLoader();
408
417
  const obj = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
409
418
  addObjectToScene(obj, (_a = obj.animations) !== null && _a !== void 0 ? _a : [], file.name);
410
419
  }
411
- else if (ext === ".glb" || ext === ".gltf") {
420
+ else if (ext === '.glb' || ext === '.gltf') {
412
421
  const loader = new GLTFLoader_js.GLTFLoader();
413
422
  const gltf = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
414
423
  addObjectToScene(gltf.scene, (_b = gltf.animations) !== null && _b !== void 0 ? _b : [], file.name);
415
424
  }
416
- else if (ext === ".obj") {
425
+ else if (ext === '.obj') {
417
426
  const loader = new OBJLoader_js.OBJLoader();
418
427
  const obj = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
419
428
  addObjectToScene(obj, [], file.name);
420
429
  }
421
- else if (ext === ".dae") {
430
+ else if (ext === '.dae') {
422
431
  const loader = new ColladaLoader_js.ColladaLoader();
423
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
424
- const collada = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
432
+ const collada = await new Promise((res, rej) =>
433
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ColladaLoader callback type widens beyond { scene: Group }
434
+ loader.load(url, res, undefined, rej));
425
435
  addObjectToScene(collada.scene, (_d = (_c = collada.scene) === null || _c === void 0 ? void 0 : _c.animations) !== null && _d !== void 0 ? _d : [], file.name);
426
436
  }
427
- else if (ext === ".stl") {
437
+ else if (ext === '.stl') {
428
438
  const loader = new STLLoader_js.STLLoader();
429
439
  const geo = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
430
- const mat = new THREE__namespace.MeshStandardMaterial({ color: 0x888888, metalness: 0.3, roughness: 0.6 });
440
+ const mat = new THREE__namespace.MeshStandardMaterial({
441
+ color: 0x888888,
442
+ metalness: 0.3,
443
+ roughness: 0.6,
444
+ });
431
445
  const mesh = new THREE__namespace.Mesh(geo, mat);
432
446
  mesh.name = file.name;
433
447
  addObjectToScene(mesh, [], file.name);
434
448
  }
435
- else if (ext === ".ply") {
449
+ else if (ext === '.ply') {
436
450
  const loader = new PLYLoader_js.PLYLoader();
437
451
  const geo = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
438
452
  geo.computeVertexNormals();
439
- const mat = new THREE__namespace.MeshStandardMaterial({ color: 0x888888, vertexColors: geo.hasAttribute("color") });
453
+ const mat = new THREE__namespace.MeshStandardMaterial({
454
+ color: 0x888888,
455
+ vertexColors: geo.hasAttribute('color'),
456
+ });
440
457
  const mesh = new THREE__namespace.Mesh(geo, mat);
441
458
  mesh.name = file.name;
442
459
  addObjectToScene(mesh, [], file.name);
443
460
  }
444
- else if (ext === ".3ds") {
461
+ else if (ext === '.3ds') {
445
462
  const loader = new TDSLoader_js.TDSLoader();
446
463
  const obj = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
447
464
  addObjectToScene(obj, [], file.name);
448
465
  }
449
- else if (ext === ".3mf") {
466
+ else if (ext === '.3mf') {
450
467
  const loader = new _3MFLoader_js.ThreeMFLoader();
451
468
  const obj = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
452
469
  addObjectToScene(obj, [], file.name);
453
470
  }
454
- else if (ext === ".amf") {
471
+ else if (ext === '.amf') {
455
472
  const loader = new AMFLoader_js.AMFLoader();
456
473
  const obj = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
457
474
  addObjectToScene(obj, [], file.name);
458
475
  }
459
- else if (ext === ".pcd") {
476
+ else if (ext === '.pcd') {
460
477
  const loader = new PCDLoader_js.PCDLoader();
461
478
  const points = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
462
479
  points.name = file.name;
463
480
  addObjectToScene(points, [], file.name);
464
481
  }
465
- else if (ext === ".vtk" || ext === ".vtp") {
482
+ else if (ext === '.vtk' || ext === '.vtp') {
466
483
  const loader = new VTKLoader_js.VTKLoader();
467
484
  const geo = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
468
485
  geo.computeVertexNormals();
@@ -471,31 +488,36 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
471
488
  mesh.name = file.name;
472
489
  addObjectToScene(mesh, [], file.name);
473
490
  }
474
- else if (ext === ".wrl" || ext === ".vrml") {
491
+ else if (ext === '.wrl' || ext === '.vrml') {
475
492
  const loader = new VRMLLoader_js.VRMLLoader();
476
493
  const scn = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
477
494
  const group = new THREE__namespace.Group();
478
495
  group.name = file.name;
479
- while (scn.children.length)
496
+ while (scn.children.length) {
480
497
  group.add(scn.children[0]);
498
+ }
481
499
  addObjectToScene(group, [], file.name);
482
500
  }
483
- else if (ext === ".gcode") {
501
+ else if (ext === '.gcode') {
484
502
  const loader = new GCodeLoader_js.GCodeLoader();
485
503
  const obj = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
486
504
  addObjectToScene(obj, [], file.name);
487
505
  }
488
- else if (ext === ".svg") {
506
+ else if (ext === '.svg') {
489
507
  const loader = new SVGLoader_js.SVGLoader();
490
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
491
- const data = await new Promise((res, rej) => loader.load(url, res, undefined, rej));
508
+ const data = await new Promise((res, rej) =>
509
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- SVGLoader callback type widens beyond { paths: SVGResultPaths[] }
510
+ loader.load(url, res, undefined, rej));
492
511
  const group = new THREE__namespace.Group();
493
512
  group.name = file.name;
494
513
  for (const path of data.paths) {
495
514
  const shapes2 = SVGLoader_js.SVGLoader.createShapes(path);
496
515
  for (const shape of shapes2) {
497
516
  const geo = new THREE__namespace.ExtrudeGeometry(shape, { depth: 2, bevelEnabled: false });
498
- const mat = new THREE__namespace.MeshStandardMaterial({ color: ((_f = (_e = path.color) === null || _e === void 0 ? void 0 : _e.getHex) === null || _f === void 0 ? void 0 : _f.call(_e)) || 0x888888, side: THREE__namespace.DoubleSide });
517
+ const mat = new THREE__namespace.MeshStandardMaterial({
518
+ color: ((_f = (_e = path.color) === null || _e === void 0 ? void 0 : _e.getHex) === null || _f === void 0 ? void 0 : _f.call(_e)) || 0x888888,
519
+ side: THREE__namespace.DoubleSide,
520
+ });
499
521
  const mesh = new THREE__namespace.Mesh(geo, mat);
500
522
  group.add(mesh);
501
523
  }
@@ -508,14 +530,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
508
530
  }
509
531
  }
510
532
  catch (err) {
511
- log.error("Load error:", err);
533
+ log.error('Load error:', err);
512
534
  setStatusText(`Error loading ${file.name}: ${String(err)}`);
513
535
  }
514
536
  finally {
515
537
  URL.revokeObjectURL(url);
516
538
  }
517
539
  }, [addObjectToScene]);
518
- const mergeAnimationFBX = ke.useCallback(async (file) => {
540
+ const mergeAnimationFBX = _e.useCallback(async (file) => {
519
541
  var _a;
520
542
  const url = URL.createObjectURL(file);
521
543
  setStatusText(`Merging animations from ${file.name}...`);
@@ -529,14 +551,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
529
551
  }
530
552
  const root = rootObjectRef.current;
531
553
  if (!root) {
532
- setStatusText("No model loaded — load a base model first");
554
+ setStatusText('No model loaded — load a base model first');
533
555
  return;
534
556
  }
535
557
  if (!mixerRef.current) {
536
558
  mixerRef.current = new THREE__namespace.AnimationMixer(root);
537
559
  }
538
560
  const newAnims = clips.map((c) => ({
539
- name: c.name || file.name.replace(/\.[^.]+$/, ""),
561
+ name: c.name || file.name.replace(/\.[^.]+$/, ''),
540
562
  clip: c,
541
563
  duration: c.duration,
542
564
  source: file.name,
@@ -546,7 +568,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
546
568
  setStatusText(`Merged ${clips.length} animation(s) from ${file.name}`);
547
569
  }
548
570
  catch (err) {
549
- log.error("Merge error:", err);
571
+ log.error('Merge error:', err);
550
572
  setStatusText(`Error merging ${file.name}: ${String(err)}`);
551
573
  }
552
574
  finally {
@@ -556,10 +578,11 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
556
578
  /* ─────────────────────────────────────────
557
579
  Animation controls
558
580
  ───────────────────────────────────────── */
559
- const playClip = ke.useCallback((index) => {
581
+ const playClip = _e.useCallback((index) => {
560
582
  const mixer = mixerRef.current;
561
- if (!mixer || index < 0 || index >= allClipsRef.current.length)
583
+ if (!mixer || index < 0 || index >= allClipsRef.current.length) {
562
584
  return;
585
+ }
563
586
  if (activeActionRef.current) {
564
587
  activeActionRef.current.stop();
565
588
  }
@@ -576,14 +599,15 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
576
599
  setIsPlaying(true);
577
600
  }, []);
578
601
  playClipRef.current = playClip;
579
- const togglePlay = ke.useCallback(() => {
602
+ const togglePlay = _e.useCallback(() => {
580
603
  if (activeAnimIdx < 0 && allClipsRef.current.length > 0) {
581
604
  playClip(0);
582
605
  return;
583
606
  }
584
607
  const action = activeActionRef.current;
585
- if (!action)
608
+ if (!action) {
586
609
  return;
610
+ }
587
611
  if (isPlayingRef.current) {
588
612
  action.paused = true;
589
613
  setIsPlaying(false);
@@ -593,7 +617,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
593
617
  setIsPlaying(true);
594
618
  }
595
619
  }, [activeAnimIdx, playClip]);
596
- const stopAnim = ke.useCallback(() => {
620
+ const stopAnim = _e.useCallback(() => {
597
621
  const action = activeActionRef.current;
598
622
  if (action) {
599
623
  action.stop();
@@ -602,11 +626,12 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
602
626
  setIsPlaying(false);
603
627
  setAnimTime(0);
604
628
  }, []);
605
- const seekAnim = ke.useCallback((t) => {
629
+ const seekAnim = _e.useCallback((t) => {
606
630
  var _a;
607
631
  const action = activeActionRef.current;
608
- if (!action)
632
+ if (!action) {
609
633
  return;
634
+ }
610
635
  action.time = t;
611
636
  action.paused = true;
612
637
  setAnimTime(t);
@@ -616,7 +641,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
616
641
  /* ─────────────────────────────────────────
617
642
  Camera helpers
618
643
  ───────────────────────────────────────── */
619
- const focusOnObject = ke.useCallback((obj) => {
644
+ const focusOnObject = _e.useCallback((obj) => {
620
645
  const box = new THREE__namespace.Box3().setFromObject(obj);
621
646
  const center = box.getCenter(new THREE__namespace.Vector3());
622
647
  const size = box.getSize(new THREE__namespace.Vector3());
@@ -630,33 +655,35 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
630
655
  }
631
656
  }, []);
632
657
  focusOnObjectRef.current = focusOnObject;
633
- const setCameraPreset = ke.useCallback((preset) => {
658
+ const setCameraPreset = _e.useCallback((preset) => {
634
659
  const orbit = orbitRef.current;
635
- if (!orbit)
660
+ if (!orbit) {
636
661
  return;
662
+ }
637
663
  const target = orbit.target.clone();
638
664
  const d = cameraRef.current.position.distanceTo(target);
639
665
  const pos = new THREE__namespace.Vector3();
640
666
  switch (preset) {
641
- case "front":
667
+ case 'front':
642
668
  pos.set(0, 0, d);
643
669
  break;
644
- case "back":
670
+ case 'back':
645
671
  pos.set(0, 0, -d);
646
672
  break;
647
- case "left":
673
+ case 'left':
648
674
  pos.set(-d, 0, 0);
649
675
  break;
650
- case "right":
676
+ case 'right':
651
677
  pos.set(d, 0, 0);
652
678
  break;
653
- case "top":
679
+ case 'top':
654
680
  pos.set(0, d, 0.001);
655
681
  break;
656
- case "bottom":
682
+ case 'bottom':
657
683
  pos.set(0, -d, 0.001);
658
684
  break;
659
- default: return;
685
+ default:
686
+ return;
660
687
  }
661
688
  cameraRef.current.position.copy(target.clone().add(pos));
662
689
  orbit.update();
@@ -664,13 +691,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
664
691
  /* ─────────────────────────────────────────
665
692
  Selection & transform
666
693
  ───────────────────────────────────────── */
667
- const selectObject = ke.useCallback((node) => {
694
+ const selectObject = _e.useCallback((node) => {
668
695
  var _a;
669
696
  setSelectedId((_a = node === null || node === void 0 ? void 0 : node.id) !== null && _a !== void 0 ? _a : null);
670
697
  const tc = transformRef.current;
671
- if (!tc)
698
+ if (!tc) {
672
699
  return;
673
- if (node && node.type !== "helper") {
700
+ }
701
+ if (node && node.type !== 'helper') {
674
702
  tc.attach(node.object);
675
703
  tc.setMode(transformMode);
676
704
  }
@@ -678,40 +706,42 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
678
706
  tc.detach();
679
707
  }
680
708
  }, [transformMode]);
681
- ke.useEffect(() => {
709
+ _e.useEffect(() => {
682
710
  var _a;
683
711
  (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.setMode(transformMode);
684
712
  }, [transformMode]);
685
713
  /* ─────────────────────────────────────────
686
714
  Toggle helpers
687
715
  ───────────────────────────────────────── */
688
- ke.useEffect(() => {
716
+ _e.useEffect(() => {
689
717
  const scene = sceneRef.current;
690
- const grid = scene.getObjectByName("__grid");
691
- if (grid)
718
+ const grid = scene.getObjectByName('__grid');
719
+ if (grid) {
692
720
  grid.visible = showGrid;
721
+ }
693
722
  }, [showGrid]);
694
- ke.useEffect(() => {
723
+ _e.useEffect(() => {
695
724
  const scene = sceneRef.current;
696
- const axes = scene.getObjectByName("__axes");
697
- if (axes)
725
+ const axes = scene.getObjectByName('__axes');
726
+ if (axes) {
698
727
  axes.visible = showAxes;
728
+ }
699
729
  }, [showAxes]);
700
- ke.useEffect(() => {
730
+ _e.useEffect(() => {
701
731
  sceneRef.current.background = new THREE__namespace.Color(bgColor);
702
732
  }, [bgColor]);
703
- ke.useEffect(() => {
733
+ _e.useEffect(() => {
704
734
  const root = rootObjectRef.current;
705
- if (!root)
735
+ if (!root) {
706
736
  return;
737
+ }
707
738
  root.traverse((child) => {
708
739
  if (child instanceof THREE__namespace.Mesh) {
709
- const mats = Array.isArray(child.material)
710
- ? child.material
711
- : [child.material];
740
+ const mats = Array.isArray(child.material) ? child.material : [child.material];
712
741
  mats.forEach((m) => {
713
- if ("wireframe" in m)
742
+ if ('wireframe' in m) {
714
743
  m.wireframe = wireframe;
744
+ }
715
745
  });
716
746
  }
717
747
  });
@@ -719,51 +749,64 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
719
749
  /* ─────────────────────────────────────────
720
750
  Viewport shading mode
721
751
  ───────────────────────────────────────── */
722
- ke.useEffect(() => {
752
+ _e.useEffect(() => {
723
753
  const renderer = rendererRef.current;
724
- if (!renderer)
754
+ if (!renderer) {
725
755
  return;
756
+ }
726
757
  const scene = sceneRef.current;
727
758
  switch (shadingMode) {
728
- case "wireframe":
759
+ case 'wireframe':
729
760
  scene.traverse((obj) => {
730
- if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith("__")) {
761
+ if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith('__')) {
731
762
  const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
732
- mats.forEach((m) => { if ("wireframe" in m)
733
- m.wireframe = true; });
763
+ mats.forEach((m) => {
764
+ if ('wireframe' in m) {
765
+ m.wireframe = true;
766
+ }
767
+ });
734
768
  }
735
769
  });
736
770
  renderer.toneMapping = THREE__namespace.NoToneMapping;
737
771
  renderer.toneMappingExposure = 1;
738
772
  break;
739
- case "solid":
773
+ case 'solid':
740
774
  scene.traverse((obj) => {
741
- if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith("__")) {
775
+ if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith('__')) {
742
776
  const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
743
- mats.forEach((m) => { if ("wireframe" in m)
744
- m.wireframe = false; });
777
+ mats.forEach((m) => {
778
+ if ('wireframe' in m) {
779
+ m.wireframe = false;
780
+ }
781
+ });
745
782
  }
746
783
  });
747
784
  renderer.toneMapping = THREE__namespace.NoToneMapping;
748
785
  renderer.toneMappingExposure = 1;
749
786
  break;
750
- case "material":
787
+ case 'material':
751
788
  scene.traverse((obj) => {
752
- if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith("__")) {
789
+ if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith('__')) {
753
790
  const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
754
- mats.forEach((m) => { if ("wireframe" in m)
755
- m.wireframe = false; });
791
+ mats.forEach((m) => {
792
+ if ('wireframe' in m) {
793
+ m.wireframe = false;
794
+ }
795
+ });
756
796
  }
757
797
  });
758
798
  renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping;
759
799
  renderer.toneMappingExposure = 1.2;
760
800
  break;
761
- case "rendered":
801
+ case 'rendered':
762
802
  scene.traverse((obj) => {
763
- if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith("__")) {
803
+ if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith('__')) {
764
804
  const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
765
- mats.forEach((m) => { if ("wireframe" in m)
766
- m.wireframe = false; });
805
+ mats.forEach((m) => {
806
+ if ('wireframe' in m) {
807
+ m.wireframe = false;
808
+ }
809
+ });
767
810
  }
768
811
  });
769
812
  renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping;
@@ -772,15 +815,16 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
772
815
  }
773
816
  }, [shadingMode]);
774
817
  /* Gizmo space */
775
- ke.useEffect(() => {
818
+ _e.useEffect(() => {
776
819
  var _a;
777
820
  (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.setSpace(gizmoSpace);
778
821
  }, [gizmoSpace]);
779
822
  /* Snap settings */
780
- ke.useEffect(() => {
823
+ _e.useEffect(() => {
781
824
  const tc = transformRef.current;
782
- if (!tc)
825
+ if (!tc) {
783
826
  return;
827
+ }
784
828
  if (snapEnabled) {
785
829
  tc.setTranslationSnap(snapGrid);
786
830
  tc.setRotationSnap(THREE__namespace.MathUtils.degToRad(15));
@@ -793,28 +837,30 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
793
837
  }
794
838
  }, [snapEnabled, snapGrid]);
795
839
  /* Skeleton helper */
796
- ke.useEffect(() => {
840
+ _e.useEffect(() => {
797
841
  const scene = sceneRef.current;
798
842
  if (skeletonHelperRef.current) {
799
843
  scene.remove(skeletonHelperRef.current);
800
844
  skeletonHelperRef.current = null;
801
845
  }
802
- if (!showSkeleton || !rootObjectRef.current)
846
+ if (!showSkeleton || !rootObjectRef.current) {
803
847
  return;
848
+ }
804
849
  let hasRig = false;
805
850
  rootObjectRef.current.traverse((obj) => {
806
- if (obj instanceof THREE__namespace.SkinnedMesh)
851
+ if (obj instanceof THREE__namespace.SkinnedMesh) {
807
852
  hasRig = true;
853
+ }
808
854
  });
809
855
  if (hasRig) {
810
856
  const helper = new THREE__namespace.SkeletonHelper(rootObjectRef.current);
811
- helper.name = "__skeletonHelper";
857
+ helper.name = '__skeletonHelper';
812
858
  scene.add(helper);
813
859
  skeletonHelperRef.current = helper;
814
860
  }
815
861
  }, [showSkeleton, sceneTree]);
816
862
  /* Fog */
817
- ke.useEffect(() => {
863
+ _e.useEffect(() => {
818
864
  if (fogEnabled) {
819
865
  sceneRef.current.fog = new THREE__namespace.Fog(fogColor, fogNear, fogFar);
820
866
  }
@@ -823,9 +869,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
823
869
  }
824
870
  }, [fogEnabled, fogColor, fogNear, fogFar]);
825
871
  /* Light helpers visibility */
826
- ke.useEffect(() => {
872
+ _e.useEffect(() => {
827
873
  sceneRef.current.traverse((obj) => {
828
- if (obj.name.startsWith("__helper_")) {
874
+ if (obj.name.startsWith('__helper_')) {
829
875
  obj.visible = showLightHelpers;
830
876
  }
831
877
  });
@@ -833,22 +879,22 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
833
879
  /* ─────────────────────────────────────────
834
880
  Drag-and-drop
835
881
  ───────────────────────────────────────── */
836
- const handleDragOver = ke.useCallback((e) => {
882
+ const handleDragOver = _e.useCallback((e) => {
837
883
  e.preventDefault();
838
884
  e.stopPropagation();
839
885
  setDragOver(true);
840
886
  }, []);
841
- const handleDragLeave = ke.useCallback((e) => {
887
+ const handleDragLeave = _e.useCallback((e) => {
842
888
  e.preventDefault();
843
889
  setDragOver(false);
844
890
  }, []);
845
- const handleDrop = ke.useCallback((e) => {
891
+ const handleDrop = _e.useCallback((e) => {
846
892
  e.preventDefault();
847
893
  e.stopPropagation();
848
894
  setDragOver(false);
849
895
  const files = Array.from(e.dataTransfer.files);
850
896
  files.forEach((f) => {
851
- const ext = "." + f.name.toLowerCase().split(".").pop();
897
+ const ext = '.' + f.name.toLowerCase().split('.').pop();
852
898
  if (modelEditorTypes.SUPPORTED_EXTENSIONS.includes(ext)) {
853
899
  loadFile(f);
854
900
  }
@@ -857,27 +903,30 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
857
903
  /* ─────────────────────────────────────────
858
904
  Helpers
859
905
  ───────────────────────────────────────── */
860
- const selectedNode = ke.useMemo(() => (selectedId ? findNodeById(sceneTree, selectedId) : null), [selectedId, sceneTree]);
861
- const selectedMaterials = ke.useMemo(() => {
862
- if (!selectedNode || !(selectedNode.object instanceof THREE__namespace.Mesh))
906
+ const selectedNode = _e.useMemo(() => (selectedId ? findNodeById(sceneTree, selectedId) : null), [selectedId, sceneTree]);
907
+ const selectedMaterials = _e.useMemo(() => {
908
+ if (!selectedNode || !(selectedNode.object instanceof THREE__namespace.Mesh)) {
863
909
  return [];
910
+ }
864
911
  const m = selectedNode.object.material;
865
912
  return Array.isArray(m) ? m : [m];
866
913
  }, [selectedNode, matRefresh]);
867
- const deleteSelected = ke.useCallback(() => {
914
+ const deleteSelected = _e.useCallback(() => {
868
915
  var _a;
869
- if (!selectedNode)
916
+ if (!selectedNode) {
870
917
  return;
918
+ }
871
919
  selectedNode.object.removeFromParent();
872
920
  (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.detach();
873
921
  setSelectedId(null);
874
922
  rebuildTree();
875
923
  }, [selectedNode, rebuildTree]);
876
- const duplicateSelected = ke.useCallback(() => {
877
- if (!selectedNode)
924
+ const duplicateSelected = _e.useCallback(() => {
925
+ if (!selectedNode) {
878
926
  return;
927
+ }
879
928
  const clone = selectedNode.object.clone(true);
880
- clone.name = selectedNode.object.name + ".001";
929
+ clone.name = selectedNode.object.name + '.001';
881
930
  clone.position.x += 1;
882
931
  sceneRef.current.add(clone);
883
932
  rebuildTree();
@@ -886,32 +935,35 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
886
935
  /* ─────────────────────────────────────────
887
936
  Keyboard shortcuts
888
937
  ───────────────────────────────────────── */
889
- ke.useEffect(() => {
938
+ _e.useEffect(() => {
890
939
  const handleKey = (e) => {
891
940
  var _a, _b, _c;
892
941
  const tag = (_a = e.target) === null || _a === void 0 ? void 0 : _a.tagName;
893
- if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT")
942
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') {
894
943
  return;
944
+ }
895
945
  switch (e.key.toLowerCase()) {
896
- case "g":
897
- setTransformMode("translate");
946
+ case 'g':
947
+ setTransformMode('translate');
898
948
  break;
899
- case "r":
900
- setTransformMode("rotate");
949
+ case 'r':
950
+ setTransformMode('rotate');
901
951
  break;
902
- case "s":
903
- if (!e.ctrlKey)
904
- setTransformMode("scale");
952
+ case 's':
953
+ if (!e.ctrlKey) {
954
+ setTransformMode('scale');
955
+ }
905
956
  break;
906
- case " ":
957
+ case ' ':
907
958
  e.preventDefault();
908
959
  togglePlay();
909
960
  break;
910
- case "f":
911
- if (rootObjectRef.current)
961
+ case 'f':
962
+ if (rootObjectRef.current) {
912
963
  focusOnObject(rootObjectRef.current);
964
+ }
913
965
  break;
914
- case "delete":
966
+ case 'delete':
915
967
  if (selectedId) {
916
968
  const sel = findNodeById(sceneTree, selectedId);
917
969
  if (sel) {
@@ -922,7 +974,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
922
974
  }
923
975
  }
924
976
  break;
925
- case "x":
977
+ case 'x':
926
978
  if (e.ctrlKey && selectedId) {
927
979
  const sel = findNodeById(sceneTree, selectedId);
928
980
  if (sel) {
@@ -933,55 +985,62 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
933
985
  }
934
986
  }
935
987
  break;
936
- case "d":
988
+ case 'd':
937
989
  if (e.shiftKey) {
938
990
  e.preventDefault();
939
991
  duplicateSelected();
940
992
  }
941
993
  break;
942
- case "tab":
994
+ case 'tab':
943
995
  e.preventDefault();
944
- setEditorMode((m) => m === "object" ? "edit" : m === "edit" ? "pose" : "object");
996
+ setEditorMode((m) => (m === 'object' ? 'edit' : m === 'edit' ? 'pose' : 'object'));
945
997
  break;
946
- case "n":
998
+ case 'n':
947
999
  if (!e.ctrlKey) ;
948
1000
  break;
949
- case "1":
950
- if (e.code.startsWith("Numpad"))
951
- setCameraPreset("front");
1001
+ case '1':
1002
+ if (e.code.startsWith('Numpad')) {
1003
+ setCameraPreset('front');
1004
+ }
952
1005
  break;
953
- case "3":
954
- if (e.code.startsWith("Numpad"))
955
- setCameraPreset("right");
1006
+ case '3':
1007
+ if (e.code.startsWith('Numpad')) {
1008
+ setCameraPreset('right');
1009
+ }
956
1010
  break;
957
- case "7":
958
- if (e.code.startsWith("Numpad"))
959
- setCameraPreset("top");
1011
+ case '7':
1012
+ if (e.code.startsWith('Numpad')) {
1013
+ setCameraPreset('top');
1014
+ }
960
1015
  break;
961
- case "4":
962
- if (e.code.startsWith("Numpad"))
963
- setCameraPreset("left");
1016
+ case '4':
1017
+ if (e.code.startsWith('Numpad')) {
1018
+ setCameraPreset('left');
1019
+ }
964
1020
  break;
965
- case "6":
966
- if (e.code.startsWith("Numpad"))
967
- setCameraPreset("right");
1021
+ case '6':
1022
+ if (e.code.startsWith('Numpad')) {
1023
+ setCameraPreset('right');
1024
+ }
968
1025
  break;
969
- case "9":
970
- if (e.code.startsWith("Numpad"))
971
- setCameraPreset("back");
1026
+ case '9':
1027
+ if (e.code.startsWith('Numpad')) {
1028
+ setCameraPreset('back');
1029
+ }
972
1030
  break;
973
- case "2":
974
- if (e.code.startsWith("Numpad"))
975
- setCameraPreset("bottom");
1031
+ case '2':
1032
+ if (e.code.startsWith('Numpad')) {
1033
+ setCameraPreset('bottom');
1034
+ }
976
1035
  break;
977
1036
  }
978
- if (e.key === "Escape") {
1037
+ if (e.key === 'Escape') {
979
1038
  setAddMenuOpen(null);
980
1039
  setContextMenu(null);
981
1040
  }
982
1041
  };
983
- window.addEventListener("keydown", handleKey);
984
- return () => window.removeEventListener("keydown", handleKey);
1042
+ window.addEventListener('keydown', handleKey);
1043
+ return () => window.removeEventListener('keydown', handleKey);
985
1044
  }, [
986
1045
  togglePlay,
987
1046
  focusOnObject,
@@ -994,92 +1053,97 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
994
1053
  /* ─────────────────────────────────────────
995
1054
  File input handlers
996
1055
  ───────────────────────────────────────── */
997
- const handleFileInput = ke.useCallback((e) => {
1056
+ const handleFileInput = _e.useCallback((e) => {
998
1057
  const files = e.target.files;
999
- if (!files)
1058
+ if (!files) {
1000
1059
  return;
1060
+ }
1001
1061
  Array.from(files).forEach(loadFile);
1002
- e.target.value = "";
1062
+ e.target.value = '';
1003
1063
  }, [loadFile]);
1004
- const handleMergeInput = ke.useCallback((e) => {
1064
+ const handleMergeInput = _e.useCallback((e) => {
1005
1065
  const files = e.target.files;
1006
- if (!files)
1066
+ if (!files) {
1007
1067
  return;
1068
+ }
1008
1069
  Array.from(files).forEach(mergeAnimationFBX);
1009
- e.target.value = "";
1070
+ e.target.value = '';
1010
1071
  }, [mergeAnimationFBX]);
1011
1072
  /* ─────────────────────────────────────────
1012
1073
  Export functions
1013
1074
  ───────────────────────────────────────── */
1014
- const exportGLTF = ke.useCallback((binary) => {
1075
+ const exportGLTF = _e.useCallback((binary) => {
1015
1076
  const root = rootObjectRef.current;
1016
1077
  if (!root) {
1017
- setStatusText("Nothing to export");
1078
+ setStatusText('Nothing to export');
1018
1079
  return;
1019
1080
  }
1020
1081
  const exporter = new GLTFExporter_js.GLTFExporter();
1021
- const options = { binary, animations: allClipsRef.current.map((a) => a.clip) };
1082
+ const options = {
1083
+ binary,
1084
+ animations: allClipsRef.current.map((a) => a.clip),
1085
+ };
1022
1086
  exporter.parse(root, (result) => {
1023
1087
  let blob;
1024
1088
  if (binary) {
1025
- blob = new Blob([result], { type: "application/octet-stream" });
1089
+ blob = new Blob([result], { type: 'application/octet-stream' });
1026
1090
  }
1027
1091
  else {
1028
- blob = new Blob([JSON.stringify(result, null, 2)], { type: "application/json" });
1092
+ blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' });
1029
1093
  }
1030
- const a = document.createElement("a");
1094
+ const a = document.createElement('a');
1031
1095
  a.href = URL.createObjectURL(blob);
1032
- a.download = `export.${binary ? "glb" : "gltf"}`;
1096
+ a.download = `export.${binary ? 'glb' : 'gltf'}`;
1033
1097
  a.click();
1034
1098
  URL.revokeObjectURL(a.href);
1035
- setStatusText(`Exported ${binary ? "GLB" : "GLTF"} successfully`);
1099
+ setStatusText(`Exported ${binary ? 'GLB' : 'GLTF'} successfully`);
1036
1100
  }, (err) => {
1037
- log.error("Export error:", err);
1101
+ log.error('Export error:', err);
1038
1102
  setStatusText(`Export error: ${String(err)}`);
1039
1103
  }, options);
1040
1104
  }, []);
1041
- const exportOBJ = ke.useCallback(() => {
1105
+ const exportOBJ = _e.useCallback(() => {
1042
1106
  const root = rootObjectRef.current;
1043
1107
  if (!root) {
1044
- setStatusText("Nothing to export");
1108
+ setStatusText('Nothing to export');
1045
1109
  return;
1046
1110
  }
1047
1111
  const exporter = new OBJExporter_js.OBJExporter();
1048
1112
  const result = exporter.parse(root);
1049
- const blob = new Blob([result], { type: "text/plain" });
1050
- const a = document.createElement("a");
1113
+ const blob = new Blob([result], { type: 'text/plain' });
1114
+ const a = document.createElement('a');
1051
1115
  a.href = URL.createObjectURL(blob);
1052
- a.download = "export.obj";
1116
+ a.download = 'export.obj';
1053
1117
  a.click();
1054
1118
  URL.revokeObjectURL(a.href);
1055
- setStatusText("Exported OBJ successfully");
1119
+ setStatusText('Exported OBJ successfully');
1056
1120
  }, []);
1057
- const exportSTL = ke.useCallback((binary) => {
1121
+ const exportSTL = _e.useCallback((binary) => {
1058
1122
  const root = rootObjectRef.current;
1059
1123
  if (!root) {
1060
- setStatusText("Nothing to export");
1124
+ setStatusText('Nothing to export');
1061
1125
  return;
1062
1126
  }
1063
1127
  const exporter = new STLExporter_js.STLExporter();
1064
1128
  const result = exporter.parse(root, { binary });
1065
1129
  let blob;
1066
1130
  if (binary) {
1067
- blob = new Blob([result], { type: "application/octet-stream" });
1131
+ blob = new Blob([result], { type: 'application/octet-stream' });
1068
1132
  }
1069
1133
  else {
1070
- blob = new Blob([result], { type: "text/plain" });
1134
+ blob = new Blob([result], { type: 'text/plain' });
1071
1135
  }
1072
- const a = document.createElement("a");
1136
+ const a = document.createElement('a');
1073
1137
  a.href = URL.createObjectURL(blob);
1074
- a.download = "export.stl";
1138
+ a.download = 'export.stl';
1075
1139
  a.click();
1076
1140
  URL.revokeObjectURL(a.href);
1077
- setStatusText(`Exported STL${binary ? " (binary)" : ""} successfully`);
1141
+ setStatusText(`Exported STL${binary ? ' (binary)' : ''} successfully`);
1078
1142
  }, []);
1079
- const exportPLY = ke.useCallback((binary) => {
1143
+ const exportPLY = _e.useCallback((binary) => {
1080
1144
  const root = rootObjectRef.current;
1081
1145
  if (!root) {
1082
- setStatusText("Nothing to export");
1146
+ setStatusText('Nothing to export');
1083
1147
  return;
1084
1148
  }
1085
1149
  const exporter = new PLYExporter_js.PLYExporter();
@@ -1087,48 +1151,48 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1087
1151
  exporter.parse(root, (result) => {
1088
1152
  let blob;
1089
1153
  if (binary) {
1090
- blob = new Blob([result], { type: "application/octet-stream" });
1154
+ blob = new Blob([result], { type: 'application/octet-stream' });
1091
1155
  }
1092
1156
  else {
1093
- blob = new Blob([result], { type: "text/plain" });
1157
+ blob = new Blob([result], { type: 'text/plain' });
1094
1158
  }
1095
- const a = document.createElement("a");
1159
+ const a = document.createElement('a');
1096
1160
  a.href = URL.createObjectURL(blob);
1097
- a.download = "export.ply";
1161
+ a.download = 'export.ply';
1098
1162
  a.click();
1099
1163
  URL.revokeObjectURL(a.href);
1100
- setStatusText(`Exported PLY${binary ? " (binary)" : ""} successfully`);
1164
+ setStatusText(`Exported PLY${binary ? ' (binary)' : ''} successfully`);
1101
1165
  }, { binary });
1102
1166
  }, []);
1103
- const exportUSDZ = ke.useCallback(async () => {
1167
+ const exportUSDZ = _e.useCallback(async () => {
1104
1168
  const root = rootObjectRef.current;
1105
1169
  if (!root) {
1106
- setStatusText("Nothing to export");
1170
+ setStatusText('Nothing to export');
1107
1171
  return;
1108
1172
  }
1109
1173
  try {
1110
1174
  const exporter = new USDZExporter_js.USDZExporter();
1111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1112
1175
  const result = await new Promise((resolve) => {
1176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- USDZExporter.parse callback signature varies between three.js minor versions
1113
1177
  exporter.parse(root, resolve);
1114
1178
  });
1115
- const blob = new Blob([result.buffer], { type: "application/octet-stream" });
1116
- const a = document.createElement("a");
1179
+ const blob = new Blob([result.buffer], { type: 'application/octet-stream' });
1180
+ const a = document.createElement('a');
1117
1181
  a.href = URL.createObjectURL(blob);
1118
- a.download = "export.usdz";
1182
+ a.download = 'export.usdz';
1119
1183
  a.click();
1120
1184
  URL.revokeObjectURL(a.href);
1121
- setStatusText("Exported USDZ successfully");
1185
+ setStatusText('Exported USDZ successfully');
1122
1186
  }
1123
1187
  catch (err) {
1124
- log.error("USDZ export error:", err);
1188
+ log.error('USDZ export error:', err);
1125
1189
  setStatusText(`USDZ export error: ${String(err)}`);
1126
1190
  }
1127
1191
  }, []);
1128
1192
  /* ─────────────────────────────────────────
1129
1193
  Dispose & clear
1130
1194
  ───────────────────────────────────────── */
1131
- const disposeObject = ke.useCallback((obj) => {
1195
+ const disposeObject = _e.useCallback((obj) => {
1132
1196
  obj.traverse((child) => {
1133
1197
  if (child.geometry) {
1134
1198
  child.geometry.dispose();
@@ -1139,23 +1203,38 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1139
1203
  : [child.material];
1140
1204
  mats.forEach((mat) => {
1141
1205
  const m = mat;
1142
- ["map", "normalMap", "roughnessMap", "metalnessMap", "emissiveMap", "aoMap", "alphaMap", "envMap", "lightMap", "bumpMap", "displacementMap", "specularMap"].forEach((prop) => {
1143
- if (m[prop] && typeof m[prop].dispose === "function")
1206
+ [
1207
+ 'map',
1208
+ 'normalMap',
1209
+ 'roughnessMap',
1210
+ 'metalnessMap',
1211
+ 'emissiveMap',
1212
+ 'aoMap',
1213
+ 'alphaMap',
1214
+ 'envMap',
1215
+ 'lightMap',
1216
+ 'bumpMap',
1217
+ 'displacementMap',
1218
+ 'specularMap',
1219
+ ].forEach((prop) => {
1220
+ if (m[prop] && typeof m[prop].dispose === 'function') {
1144
1221
  m[prop].dispose();
1222
+ }
1145
1223
  });
1146
1224
  mat.dispose();
1147
1225
  });
1148
1226
  }
1149
1227
  });
1150
1228
  }, []);
1151
- const clearScene = ke.useCallback(() => {
1229
+ const clearScene = _e.useCallback(() => {
1152
1230
  var _a;
1153
- if (!window.confirm("Clear the entire scene? This cannot be undone."))
1231
+ if (!window.confirm('Clear the entire scene? This cannot be undone.')) {
1154
1232
  return;
1233
+ }
1155
1234
  const scene = sceneRef.current;
1156
1235
  const toRemove = [];
1157
1236
  scene.traverse((obj) => {
1158
- if (!obj.name.startsWith("__") && obj !== scene && obj.parent === scene) {
1237
+ if (!obj.name.startsWith('__') && obj !== scene && obj.parent === scene) {
1159
1238
  toRemove.push(obj);
1160
1239
  }
1161
1240
  });
@@ -1181,89 +1260,96 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1181
1260
  setAnimDuration(0);
1182
1261
  setSelectedId(null);
1183
1262
  rebuildTree();
1184
- setStatusText("Scene cleared");
1263
+ setStatusText('Scene cleared');
1185
1264
  }, [rebuildTree, disposeObject]);
1186
1265
  /* ─────────────────────────────────────────
1187
1266
  Save to Library
1188
1267
  ───────────────────────────────────────── */
1189
- const saveToLibrary = ke.useCallback(() => {
1190
- if (!onSaveToLibrary)
1268
+ const saveToLibrary = _e.useCallback(() => {
1269
+ if (!onSaveToLibrary) {
1191
1270
  return;
1271
+ }
1192
1272
  const root = rootObjectRef.current;
1193
1273
  if (!root) {
1194
- setStatusText("Nothing to save");
1274
+ setStatusText('Nothing to save');
1195
1275
  return;
1196
1276
  }
1197
1277
  const exporter = new GLTFExporter_js.GLTFExporter();
1198
1278
  exporter.parse(root, (result) => {
1199
- const blob = new Blob([result], { type: "model/gltf-binary" });
1279
+ const blob = new Blob([result], { type: 'model/gltf-binary' });
1200
1280
  const reader = new FileReader();
1201
1281
  reader.onload = () => {
1202
- const defaultName = "model.glb";
1203
- const name = window.prompt("Save to library as:", defaultName);
1204
- if (!name || !name.trim())
1282
+ const defaultName = 'model.glb';
1283
+ const name = window.prompt('Save to library as:', defaultName);
1284
+ if (!name || !name.trim()) {
1205
1285
  return;
1206
- onSaveToLibrary(reader.result, name.trim(), "model/gltf-binary");
1286
+ }
1287
+ onSaveToLibrary(reader.result, name.trim(), 'model/gltf-binary');
1207
1288
  };
1208
1289
  reader.readAsDataURL(blob);
1209
1290
  }, (err) => {
1210
- log.error("Save to library error:", err);
1291
+ log.error('Save to library error:', err);
1211
1292
  setStatusText(`Save error: ${String(err)}`);
1212
1293
  }, { binary: true, animations: allClipsRef.current.map((a) => a.clip) });
1213
1294
  }, [onSaveToLibrary]);
1214
1295
  /* ─────────────────────────────────────────
1215
1296
  Add Mesh / Light / Camera / Empty
1216
1297
  ───────────────────────────────────────── */
1217
- const addPrimitive = ke.useCallback((type) => {
1298
+ const addPrimitive = _e.useCallback((type) => {
1218
1299
  let geo;
1219
1300
  switch (type) {
1220
- case "cube":
1301
+ case 'cube':
1221
1302
  geo = new THREE__namespace.BoxGeometry(1, 1, 1);
1222
1303
  break;
1223
- case "sphere":
1304
+ case 'sphere':
1224
1305
  geo = new THREE__namespace.SphereGeometry(0.5, 32, 32);
1225
1306
  break;
1226
- case "cylinder":
1307
+ case 'cylinder':
1227
1308
  geo = new THREE__namespace.CylinderGeometry(0.5, 0.5, 1, 32);
1228
1309
  break;
1229
- case "cone":
1310
+ case 'cone':
1230
1311
  geo = new THREE__namespace.ConeGeometry(0.5, 1, 32);
1231
1312
  break;
1232
- case "torus":
1313
+ case 'torus':
1233
1314
  geo = new THREE__namespace.TorusGeometry(0.5, 0.2, 16, 32);
1234
1315
  break;
1235
- case "plane":
1316
+ case 'plane':
1236
1317
  geo = new THREE__namespace.PlaneGeometry(2, 2);
1237
1318
  break;
1238
- case "circle":
1319
+ case 'circle':
1239
1320
  geo = new THREE__namespace.CircleGeometry(0.5, 32);
1240
1321
  break;
1241
- case "ring":
1322
+ case 'ring':
1242
1323
  geo = new THREE__namespace.RingGeometry(0.3, 0.5, 32);
1243
1324
  break;
1244
- case "dodecahedron":
1325
+ case 'dodecahedron':
1245
1326
  geo = new THREE__namespace.DodecahedronGeometry(0.5);
1246
1327
  break;
1247
- case "icosahedron":
1328
+ case 'icosahedron':
1248
1329
  geo = new THREE__namespace.IcosahedronGeometry(0.5);
1249
1330
  break;
1250
- case "octahedron":
1331
+ case 'octahedron':
1251
1332
  geo = new THREE__namespace.OctahedronGeometry(0.5);
1252
1333
  break;
1253
- case "tetrahedron":
1334
+ case 'tetrahedron':
1254
1335
  geo = new THREE__namespace.TetrahedronGeometry(0.5);
1255
1336
  break;
1256
- case "torusKnot":
1337
+ case 'torusKnot':
1257
1338
  geo = new THREE__namespace.TorusKnotGeometry(0.4, 0.15, 64, 16);
1258
1339
  break;
1259
- default: return;
1340
+ default:
1341
+ return;
1260
1342
  }
1261
- const mat = new THREE__namespace.MeshStandardMaterial({ color: 0x888888, metalness: 0.1, roughness: 0.7 });
1343
+ const mat = new THREE__namespace.MeshStandardMaterial({
1344
+ color: 0x888888,
1345
+ metalness: 0.1,
1346
+ roughness: 0.7,
1347
+ });
1262
1348
  const mesh = new THREE__namespace.Mesh(geo, mat);
1263
1349
  mesh.name = type.charAt(0).toUpperCase() + type.slice(1);
1264
1350
  mesh.castShadow = true;
1265
1351
  mesh.receiveShadow = true;
1266
- if (type === "plane" || type === "circle") {
1352
+ if (type === 'plane' || type === 'circle') {
1267
1353
  mesh.rotation.x = -Math.PI / 2;
1268
1354
  mesh.position.y = 0.001;
1269
1355
  }
@@ -1275,37 +1361,38 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1275
1361
  setStatusText(`Added ${mesh.name}`);
1276
1362
  setAddMenuOpen(null);
1277
1363
  }, [rebuildTree]);
1278
- const addSceneLight = ke.useCallback((type) => {
1364
+ const addSceneLight = _e.useCallback((type) => {
1279
1365
  let light;
1280
1366
  switch (type) {
1281
- case "point": {
1367
+ case 'point': {
1282
1368
  const l = new THREE__namespace.PointLight(0xffffff, 1, 20);
1283
1369
  l.position.set(0, 3, 0);
1284
1370
  l.castShadow = true;
1285
1371
  light = l;
1286
1372
  break;
1287
1373
  }
1288
- case "spot": {
1374
+ case 'spot': {
1289
1375
  const l = new THREE__namespace.SpotLight(0xffffff, 1, 20, Math.PI / 6, 0.3);
1290
1376
  l.position.set(0, 4, 2);
1291
1377
  l.castShadow = true;
1292
1378
  light = l;
1293
1379
  break;
1294
1380
  }
1295
- case "directional": {
1381
+ case 'directional': {
1296
1382
  const l = new THREE__namespace.DirectionalLight(0xffffff, 1);
1297
1383
  l.position.set(3, 5, 3);
1298
1384
  l.castShadow = true;
1299
1385
  light = l;
1300
1386
  break;
1301
1387
  }
1302
- case "hemisphere": {
1388
+ case 'hemisphere': {
1303
1389
  light = new THREE__namespace.HemisphereLight(0x8888ff, 0x442200, 0.5);
1304
1390
  break;
1305
1391
  }
1306
- default: return;
1392
+ default:
1393
+ return;
1307
1394
  }
1308
- light.name = type.charAt(0).toUpperCase() + type.slice(1) + "Light";
1395
+ light.name = type.charAt(0).toUpperCase() + type.slice(1) + 'Light';
1309
1396
  sceneRef.current.add(light);
1310
1397
  let helper = null;
1311
1398
  if (light instanceof THREE__namespace.PointLight) {
@@ -1328,26 +1415,27 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1328
1415
  setStatusText(`Added ${light.name}`);
1329
1416
  setAddMenuOpen(null);
1330
1417
  }, [rebuildTree]);
1331
- const addCameraObject = ke.useCallback(() => {
1418
+ const addCameraObject = _e.useCallback(() => {
1332
1419
  const cam = new THREE__namespace.PerspectiveCamera(50, 16 / 9, 0.1, 1000);
1333
- cam.name = "Camera";
1420
+ cam.name = 'Camera';
1334
1421
  cam.position.set(5, 3, 5);
1335
1422
  cam.lookAt(0, 0, 0);
1336
1423
  sceneRef.current.add(cam);
1337
1424
  const helper = new THREE__namespace.CameraHelper(cam);
1338
- helper.name = "__helper_Camera";
1425
+ helper.name = '__helper_Camera';
1339
1426
  sceneRef.current.add(helper);
1340
1427
  rebuildTree();
1341
- setStatusText("Added Camera");
1428
+ setStatusText('Added Camera');
1342
1429
  setAddMenuOpen(null);
1343
1430
  }, [rebuildTree]);
1344
- const addEmpty = ke.useCallback((type) => {
1431
+ const addEmpty = _e.useCallback((type) => {
1345
1432
  const obj = new THREE__namespace.Object3D();
1346
- obj.name = type === "arrows" ? "Empty_Arrows" : type === "cube_empty" ? "Empty_Cube" : "Empty";
1433
+ obj.name =
1434
+ type === 'arrows' ? 'Empty_Arrows' : type === 'cube_empty' ? 'Empty_Cube' : 'Empty';
1347
1435
  sceneRef.current.add(obj);
1348
- if (type === "arrows") {
1436
+ if (type === 'arrows') {
1349
1437
  const h = new THREE__namespace.AxesHelper(0.5);
1350
- h.name = "__emptyAxes";
1438
+ h.name = '__emptyAxes';
1351
1439
  obj.add(h);
1352
1440
  }
1353
1441
  rebuildTree();
@@ -1357,21 +1445,25 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1357
1445
  /* ─────────────────────────────────────────
1358
1446
  Raycaster click-to-select
1359
1447
  ───────────────────────────────────────── */
1360
- const handleViewportClick = ke.useCallback((e) => {
1361
- if (editorMode !== "object")
1448
+ const handleViewportClick = _e.useCallback((e) => {
1449
+ if (editorMode !== 'object') {
1362
1450
  return;
1363
- if (!mountRef.current || !rendererRef.current || !cameraRef.current)
1451
+ }
1452
+ if (!mountRef.current || !rendererRef.current || !cameraRef.current) {
1364
1453
  return;
1365
- if (e.target !== rendererRef.current.domElement)
1454
+ }
1455
+ if (e.target !== rendererRef.current.domElement) {
1366
1456
  return;
1457
+ }
1367
1458
  const rect = mountRef.current.getBoundingClientRect();
1368
1459
  const x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
1369
1460
  const y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
1370
1461
  raycasterRef.current.setFromCamera(new THREE__namespace.Vector2(x, y), cameraRef.current);
1371
1462
  const meshes = [];
1372
1463
  sceneRef.current.traverse((obj) => {
1373
- if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith("__"))
1464
+ if (obj instanceof THREE__namespace.Mesh && !obj.name.startsWith('__')) {
1374
1465
  meshes.push(obj);
1466
+ }
1375
1467
  });
1376
1468
  const intersects = raycasterRef.current.intersectObjects(meshes, false);
1377
1469
  if (intersects.length > 0) {
@@ -1393,33 +1485,35 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1393
1485
  /* ─────────────────────────────────────────
1394
1486
  Context menu
1395
1487
  ───────────────────────────────────────── */
1396
- const handleContextMenu = ke.useCallback((e) => {
1488
+ const handleContextMenu = _e.useCallback((e) => {
1397
1489
  e.preventDefault();
1398
1490
  setContextMenu({ x: e.clientX, y: e.clientY });
1399
1491
  }, []);
1400
- const closeContextMenu = ke.useCallback(() => setContextMenu(null), []);
1401
- ke.useEffect(() => {
1402
- if (!contextMenu)
1492
+ const closeContextMenu = _e.useCallback(() => setContextMenu(null), []);
1493
+ _e.useEffect(() => {
1494
+ if (!contextMenu) {
1403
1495
  return;
1496
+ }
1404
1497
  const close = () => setContextMenu(null);
1405
- window.addEventListener("click", close);
1406
- return () => window.removeEventListener("click", close);
1498
+ window.addEventListener('click', close);
1499
+ return () => window.removeEventListener('click', close);
1407
1500
  }, [contextMenu]);
1408
1501
  /* ─────────────────────────────────────────
1409
1502
  AI animation from video
1410
1503
  ───────────────────────────────────────── */
1411
- const handleAIVideo = ke.useCallback(async (e) => {
1504
+ const handleAIVideo = _e.useCallback(async (e) => {
1412
1505
  var _a;
1413
1506
  const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
1414
- if (!file)
1507
+ if (!file) {
1415
1508
  return;
1416
- e.target.value = "";
1509
+ }
1510
+ e.target.value = '';
1417
1511
  if (!onAIPoseVideo) {
1418
- setAiStatus("AI pose callback not configured.");
1512
+ setAiStatus('AI pose callback not configured.');
1419
1513
  return;
1420
1514
  }
1421
1515
  setAiBusy(true);
1422
- setAiStatus("Uploading video for AI pose estimation...");
1516
+ setAiStatus('Uploading video for AI pose estimation...');
1423
1517
  try {
1424
1518
  const result = await onAIPoseVideo(file);
1425
1519
  setAiStatus(`Got ${result.frame_count} frames at ${result.fps} fps. Creating animation clip...`);
@@ -1437,14 +1531,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1437
1531
  allClipsRef.current = [...allClipsRef.current, newAnim];
1438
1532
  setAnimations([...allClipsRef.current]);
1439
1533
  playClip(allClipsRef.current.length - 1);
1440
- setAiStatus("Animation created successfully!");
1534
+ setAiStatus('Animation created successfully!');
1441
1535
  }
1442
1536
  else {
1443
- setAiStatus("Could not create animation — load a rigged model first.");
1537
+ setAiStatus('Could not create animation — load a rigged model first.');
1444
1538
  }
1445
1539
  }
1446
1540
  catch (err) {
1447
- log.error("AI pose error:", err);
1541
+ log.error('AI pose error:', err);
1448
1542
  setAiStatus(`Error: ${String(err)}`);
1449
1543
  }
1450
1544
  finally {
@@ -1454,24 +1548,24 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1454
1548
  /* ─────────────────────────────────────────
1455
1549
  Node helpers
1456
1550
  ───────────────────────────────────────── */
1457
- const toggleNodeVisibility = ke.useCallback((node) => {
1551
+ const toggleNodeVisibility = _e.useCallback((node) => {
1458
1552
  node.object.visible = !node.object.visible;
1459
1553
  rebuildTree();
1460
1554
  }, [rebuildTree]);
1461
- const toggleNodeExpanded = ke.useCallback((node) => {
1555
+ const toggleNodeExpanded = _e.useCallback((node) => {
1462
1556
  node.expanded = !node.expanded;
1463
1557
  setSceneTree((prev) => [...prev]);
1464
1558
  }, []);
1465
1559
  /* ─────────────────────────────────────────
1466
1560
  Undo / Redo
1467
1561
  ───────────────────────────────────────── */
1468
- const undoRedoRef = ke.useRef(new modelEditorUtils.UndoRedoStack());
1469
- const [undoRedoVer, setUndoRedoVer] = ke.useState(0);
1470
- const pushUndo = ke.useCallback((action) => {
1562
+ const undoRedoRef = _e.useRef(new modelEditorUtils.UndoRedoStack());
1563
+ const [undoRedoVer, setUndoRedoVer] = _e.useState(0);
1564
+ const pushUndo = _e.useCallback((action) => {
1471
1565
  undoRedoRef.current.push(action);
1472
1566
  setUndoRedoVer((v) => v + 1);
1473
1567
  }, []);
1474
- const undo = ke.useCallback(() => {
1568
+ const undo = _e.useCallback(() => {
1475
1569
  const label = undoRedoRef.current.undo();
1476
1570
  if (label) {
1477
1571
  setStatusText(`Undo: ${label}`);
@@ -1479,7 +1573,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1479
1573
  }
1480
1574
  setUndoRedoVer((v) => v + 1);
1481
1575
  }, [rebuildTree]);
1482
- const redo = ke.useCallback(() => {
1576
+ const redo = _e.useCallback(() => {
1483
1577
  const label = undoRedoRef.current.redo();
1484
1578
  if (label) {
1485
1579
  setStatusText(`Redo: ${label}`);
@@ -1492,30 +1586,30 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1492
1586
  const undoLabel = undoRedoRef.current.undoLabel;
1493
1587
  const redoLabel = undoRedoRef.current.redoLabel;
1494
1588
  // Keyboard shortcuts for undo/redo
1495
- ke.useEffect(() => {
1589
+ _e.useEffect(() => {
1496
1590
  const handleKeyDown = (e) => {
1497
- if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
1591
+ if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
1498
1592
  e.preventDefault();
1499
1593
  undo();
1500
1594
  }
1501
- else if ((e.ctrlKey || e.metaKey) && (e.key === "y" || (e.key === "z" && e.shiftKey))) {
1595
+ else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
1502
1596
  e.preventDefault();
1503
1597
  redo();
1504
1598
  }
1505
1599
  };
1506
- window.addEventListener("keydown", handleKeyDown);
1507
- return () => window.removeEventListener("keydown", handleKeyDown);
1600
+ window.addEventListener('keydown', handleKeyDown);
1601
+ return () => window.removeEventListener('keydown', handleKeyDown);
1508
1602
  }, [undo, redo]);
1509
1603
  /* ─────────────────────────────────────────
1510
1604
  Batch Import
1511
1605
  ───────────────────────────────────────── */
1512
- const loadFiles = ke.useCallback(async (files) => {
1606
+ const loadFiles = _e.useCallback(async (files) => {
1513
1607
  const fileArray = Array.from(files).filter((f) => {
1514
- const ext = f.name.substring(f.name.lastIndexOf(".")).toLowerCase();
1608
+ const ext = f.name.substring(f.name.lastIndexOf('.')).toLowerCase();
1515
1609
  return modelEditorTypes.SUPPORTED_EXTENSIONS.includes(ext);
1516
1610
  });
1517
1611
  if (fileArray.length === 0) {
1518
- setStatusText("No supported files found");
1612
+ setStatusText('No supported files found');
1519
1613
  return;
1520
1614
  }
1521
1615
  setStatusText(`Importing ${fileArray.length} file(s)...`);
@@ -1535,28 +1629,34 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1535
1629
  /* ─────────────────────────────────────────
1536
1630
  PBR Presets
1537
1631
  ───────────────────────────────────────── */
1538
- const applyPreset = ke.useCallback((preset) => {
1632
+ const applyPreset = _e.useCallback((preset) => {
1539
1633
  const node = selectedNode;
1540
1634
  if (!node || !(node.object instanceof THREE__namespace.Mesh)) {
1541
- setStatusText("Select a mesh to apply material preset");
1635
+ setStatusText('Select a mesh to apply material preset');
1542
1636
  return;
1543
1637
  }
1544
1638
  const mesh = node.object;
1545
- const oldMaterial = Array.isArray(mesh.material) ? mesh.material[selMaterialIdx] : mesh.material;
1546
- if (!oldMaterial)
1639
+ const oldMaterial = Array.isArray(mesh.material)
1640
+ ? mesh.material[selMaterialIdx]
1641
+ : mesh.material;
1642
+ if (!oldMaterial) {
1547
1643
  return;
1644
+ }
1548
1645
  // Save for undo
1549
1646
  const oldClone = oldMaterial.clone();
1550
1647
  const materialIdx = selMaterialIdx;
1551
1648
  const newMaterial = modelEditorUtils.createMaterialFromPreset(preset);
1552
1649
  // Copy existing textures if present
1553
1650
  if (oldMaterial instanceof THREE__namespace.MeshStandardMaterial) {
1554
- if (oldMaterial.map)
1651
+ if (oldMaterial.map) {
1555
1652
  newMaterial.map = oldMaterial.map;
1556
- if (oldMaterial.normalMap)
1653
+ }
1654
+ if (oldMaterial.normalMap) {
1557
1655
  newMaterial.normalMap = oldMaterial.normalMap;
1558
- if (oldMaterial.aoMap)
1656
+ }
1657
+ if (oldMaterial.aoMap) {
1559
1658
  newMaterial.aoMap = oldMaterial.aoMap;
1659
+ }
1560
1660
  }
1561
1661
  if (Array.isArray(mesh.material)) {
1562
1662
  mesh.material[materialIdx] = newMaterial;
@@ -1589,10 +1689,10 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1589
1689
  /* ─────────────────────────────────────────
1590
1690
  LOD Generation
1591
1691
  ───────────────────────────────────────── */
1592
- const generateSelectedLOD = ke.useCallback(() => {
1692
+ const generateSelectedLOD = _e.useCallback(() => {
1593
1693
  const node = selectedNode;
1594
1694
  if (!node || !(node.object instanceof THREE__namespace.Mesh)) {
1595
- setStatusText("Select a mesh to generate LOD");
1695
+ setStatusText('Select a mesh to generate LOD');
1596
1696
  return;
1597
1697
  }
1598
1698
  const mesh = node.object;
@@ -1602,8 +1702,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1602
1702
  parent.add(lod);
1603
1703
  pushUndo({
1604
1704
  label: `Generate LOD for ${mesh.name}`,
1605
- undo: () => { parent.remove(lod); parent.add(mesh); },
1606
- redo: () => { parent.remove(mesh); parent.add(lod); },
1705
+ undo: () => {
1706
+ parent.remove(lod);
1707
+ parent.add(mesh);
1708
+ },
1709
+ redo: () => {
1710
+ parent.remove(mesh);
1711
+ parent.add(lod);
1712
+ },
1607
1713
  });
1608
1714
  rebuildTree();
1609
1715
  setStatusText(`Generated LOD for ${mesh.name} (4 levels)`);
@@ -1611,21 +1717,22 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1611
1717
  /* ─────────────────────────────────────────
1612
1718
  Instancing
1613
1719
  ───────────────────────────────────────── */
1614
- const convertSelectedToInstanced = ke.useCallback(() => {
1720
+ const convertSelectedToInstanced = _e.useCallback(() => {
1615
1721
  const root = rootObjectRef.current || sceneRef.current;
1616
1722
  const result = modelEditorUtils.convertToInstanced(root);
1617
1723
  if (result.created === 0) {
1618
- setStatusText("No duplicate meshes found to instance");
1724
+ setStatusText('No duplicate meshes found to instance');
1619
1725
  return;
1620
1726
  }
1621
1727
  rebuildTree();
1622
1728
  setStatusText(`Created ${result.created} instanced meshes, removed ${result.removed} duplicates`);
1623
1729
  }, [rebuildTree]);
1624
1730
  /* ── HDRI Environment ── */
1625
- const applyHDRI = ke.useCallback((preset) => {
1731
+ const applyHDRI = _e.useCallback((preset) => {
1626
1732
  const renderer = rendererRef.current;
1627
- if (!renderer)
1733
+ if (!renderer) {
1628
1734
  return;
1735
+ }
1629
1736
  modelEditorUtils.applyHDRIPreset(sceneRef.current, renderer, preset);
1630
1737
  setBgColor(`#${new THREE__namespace.Color(preset.bgColor).getHexString()}`);
1631
1738
  if (preset.fog) {
@@ -1640,9 +1747,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
1640
1747
  setStatusText(`Environment: ${preset.name}`);
1641
1748
  }, []);
1642
1749
  /* ── Snap ── */
1643
- const snapPoint = ke.useCallback((point) => {
1750
+ const snapPoint = _e.useCallback((point) => {
1644
1751
  const root = rootObjectRef.current || sceneRef.current;
1645
- const modes = snapEnabled ? ["vertex", "edge", "grid"] : ["grid"];
1752
+ const modes = snapEnabled ? ['vertex', 'edge', 'grid'] : ['grid'];
1646
1753
  return modelEditorUtils.snapMulti(point, root, snapGrid, modes, 0.5);
1647
1754
  }, [snapEnabled, snapGrid]);
1648
1755
  /* ═══════════════════════════════════════════