@dreamerbird/camera-studio 0.0.0-stage → 0.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.
Files changed (830) hide show
  1. package/assets/models/Y Bot.glb +0 -0
  2. package/assets/poseThumbs/qbx-hands-01-base-base-l.png +0 -0
  3. package/assets/poseThumbs/qbx-hands-01-base-base-r.png +0 -0
  4. package/assets/poseThumbs/qbx-hands-01-base-point-relaxed-l.png +0 -0
  5. package/assets/poseThumbs/qbx-hands-01-base-point-relaxed-r.png +0 -0
  6. package/assets/poseThumbs/qbx-hands-01-base-point-straight-l.png +0 -0
  7. package/assets/poseThumbs/qbx-hands-01-base-point-straight-r.png +0 -0
  8. package/assets/poseThumbs/qbx-hands-01-base-relaxed-01-l.png +0 -0
  9. package/assets/poseThumbs/qbx-hands-01-base-relaxed-01-r.png +0 -0
  10. package/assets/poseThumbs/qbx-hands-01-base-relaxed-02-l.png +0 -0
  11. package/assets/poseThumbs/qbx-hands-01-base-relaxed-02-r.png +0 -0
  12. package/assets/poseThumbs/qbx-hands-01-base-relaxed-03-l.png +0 -0
  13. package/assets/poseThumbs/qbx-hands-01-base-relaxed-03-r.png +0 -0
  14. package/assets/poseThumbs/qbx-hands-01-base-relaxed-04-l.png +0 -0
  15. package/assets/poseThumbs/qbx-hands-01-base-relaxed-04-r.png +0 -0
  16. package/assets/poseThumbs/qbx-hands-01-base-relaxed-05-l.png +0 -0
  17. package/assets/poseThumbs/qbx-hands-01-base-relaxed-05-r.png +0 -0
  18. package/assets/poseThumbs/qbx-hands-01-base-relaxed-06-l.png +0 -0
  19. package/assets/poseThumbs/qbx-hands-01-base-relaxed-06-r.png +0 -0
  20. package/assets/poseThumbs/qbx-hands-02-other-bowl-l.png +0 -0
  21. package/assets/poseThumbs/qbx-hands-02-other-bowl-r.png +0 -0
  22. package/assets/poseThumbs/qbx-hands-02-other-card-l.png +0 -0
  23. package/assets/poseThumbs/qbx-hands-02-other-card-r.png +0 -0
  24. package/assets/poseThumbs/qbx-hands-02-other-claw-l.png +0 -0
  25. package/assets/poseThumbs/qbx-hands-02-other-claw-r.png +0 -0
  26. package/assets/poseThumbs/qbx-hands-02-other-fist-l.png +0 -0
  27. package/assets/poseThumbs/qbx-hands-02-other-fist-r.png +0 -0
  28. package/assets/poseThumbs/qbx-hands-02-other-grip-l.png +0 -0
  29. package/assets/poseThumbs/qbx-hands-02-other-grip-r.png +0 -0
  30. package/assets/poseThumbs/qbx-hands-02-other-ko-l.png +0 -0
  31. package/assets/poseThumbs/qbx-hands-02-other-ko-r.png +0 -0
  32. package/assets/poseThumbs/qbx-hands-02-other-nail-check-01-l.png +0 -0
  33. package/assets/poseThumbs/qbx-hands-02-other-nail-check-01-r.png +0 -0
  34. package/assets/poseThumbs/qbx-hands-02-other-nail-check-02-l.png +0 -0
  35. package/assets/poseThumbs/qbx-hands-02-other-nail-check-02-r.png +0 -0
  36. package/assets/poseThumbs/qbx-hands-02-other-offer-l.png +0 -0
  37. package/assets/poseThumbs/qbx-hands-02-other-offer-r.png +0 -0
  38. package/assets/poseThumbs/qbx-hands-02-other-push-l.png +0 -0
  39. package/assets/poseThumbs/qbx-hands-02-other-push-r.png +0 -0
  40. package/assets/poseThumbs/qbx-hands-02-other-push-the-button-l.png +0 -0
  41. package/assets/poseThumbs/qbx-hands-02-other-push-the-button-r.png +0 -0
  42. package/assets/poseThumbs/qbx-hands-02-other-scratch-l.png +0 -0
  43. package/assets/poseThumbs/qbx-hands-02-other-scratch-r.png +0 -0
  44. package/assets/poseThumbs/qbx-hands-02-other-spread-l.png +0 -0
  45. package/assets/poseThumbs/qbx-hands-02-other-spread-r.png +0 -0
  46. package/assets/poseThumbs/qbx-hands-02-other-write-l.png +0 -0
  47. package/assets/poseThumbs/qbx-hands-02-other-write-r.png +0 -0
  48. package/assets/poseThumbs/qbx-hands-03-signs-cobblepot-l.png +0 -0
  49. package/assets/poseThumbs/qbx-hands-03-signs-cobblepot-r.png +0 -0
  50. package/assets/poseThumbs/qbx-hands-03-signs-fingers-crossed-l.png +0 -0
  51. package/assets/poseThumbs/qbx-hands-03-signs-fingers-crossed-r.png +0 -0
  52. package/assets/poseThumbs/qbx-hands-03-signs-five-l.png +0 -0
  53. package/assets/poseThumbs/qbx-hands-03-signs-five-r.png +0 -0
  54. package/assets/poseThumbs/qbx-hands-03-signs-four-l.png +0 -0
  55. package/assets/poseThumbs/qbx-hands-03-signs-four-r.png +0 -0
  56. package/assets/poseThumbs/qbx-hands-03-signs-four-ring-l.png +0 -0
  57. package/assets/poseThumbs/qbx-hands-03-signs-four-ring-r.png +0 -0
  58. package/assets/poseThumbs/qbx-hands-03-signs-fu-l.png +0 -0
  59. package/assets/poseThumbs/qbx-hands-03-signs-fu-r.png +0 -0
  60. package/assets/poseThumbs/qbx-hands-03-signs-index-l.png +0 -0
  61. package/assets/poseThumbs/qbx-hands-03-signs-index-r.png +0 -0
  62. package/assets/poseThumbs/qbx-hands-03-signs-martian-reactor-switch-l.png +0 -0
  63. package/assets/poseThumbs/qbx-hands-03-signs-martian-reactor-switch-r.png +0 -0
  64. package/assets/poseThumbs/qbx-hands-03-signs-one-does-not-simply-l.png +0 -0
  65. package/assets/poseThumbs/qbx-hands-03-signs-one-does-not-simply-r.png +0 -0
  66. package/assets/poseThumbs/qbx-hands-03-signs-peace-flipped-r.png +0 -0
  67. package/assets/poseThumbs/qbx-hands-03-signs-peace-flipped.png +0 -0
  68. package/assets/poseThumbs/qbx-hands-03-signs-peace-l.png +0 -0
  69. package/assets/poseThumbs/qbx-hands-03-signs-peace-r.png +0 -0
  70. package/assets/poseThumbs/qbx-hands-03-signs-perfect-l.png +0 -0
  71. package/assets/poseThumbs/qbx-hands-03-signs-perfect-r.png +0 -0
  72. package/assets/poseThumbs/qbx-hands-03-signs-pinky-swear-l.png +0 -0
  73. package/assets/poseThumbs/qbx-hands-03-signs-pinky-swear-r.png +0 -0
  74. package/assets/poseThumbs/qbx-hands-03-signs-rock-out-l.png +0 -0
  75. package/assets/poseThumbs/qbx-hands-03-signs-rock-out-r.png +0 -0
  76. package/assets/poseThumbs/qbx-hands-03-signs-scout-salute-l.png +0 -0
  77. package/assets/poseThumbs/qbx-hands-03-signs-scout-salute-r.png +0 -0
  78. package/assets/poseThumbs/qbx-hands-03-signs-spock-l.png +0 -0
  79. package/assets/poseThumbs/qbx-hands-03-signs-spock-r.png +0 -0
  80. package/assets/poseThumbs/qbx-hands-03-signs-stop-l.png +0 -0
  81. package/assets/poseThumbs/qbx-hands-03-signs-stop-r.png +0 -0
  82. package/assets/poseThumbs/qbx-hands-03-signs-surf-s-up-flipped-l.png +0 -0
  83. package/assets/poseThumbs/qbx-hands-03-signs-surf-s-up-flipped-r.png +0 -0
  84. package/assets/poseThumbs/qbx-hands-03-signs-surf-s-up-l.png +0 -0
  85. package/assets/poseThumbs/qbx-hands-03-signs-surf-s-up-r.png +0 -0
  86. package/assets/poseThumbs/qbx-hands-03-signs-three-hicox-l.png +0 -0
  87. package/assets/poseThumbs/qbx-hands-03-signs-three-hicox-r.png +0 -0
  88. package/assets/poseThumbs/qbx-hands-03-signs-three-l.png +0 -0
  89. package/assets/poseThumbs/qbx-hands-03-signs-three-r.png +0 -0
  90. package/assets/poseThumbs/qbx-hands-03-signs-three-ring-l.png +0 -0
  91. package/assets/poseThumbs/qbx-hands-03-signs-three-ring-r.png +0 -0
  92. package/assets/poseThumbs/qbx-hands-03-signs-thumbs-up-l.png +0 -0
  93. package/assets/poseThumbs/qbx-hands-03-signs-thumbs-up-r.png +0 -0
  94. package/assets/poseThumbs/qbx-hands-04-weapons-detonator-l.png +0 -0
  95. package/assets/poseThumbs/qbx-hands-04-weapons-detonator-r.png +0 -0
  96. package/assets/poseThumbs/qbx-hands-04-weapons-grip-sword-l.png +0 -0
  97. package/assets/poseThumbs/qbx-hands-04-weapons-grip-sword-r.png +0 -0
  98. package/assets/poseThumbs/qbx-hands-04-weapons-gun-01-l.png +0 -0
  99. package/assets/poseThumbs/qbx-hands-04-weapons-gun-01-r.png +0 -0
  100. package/assets/poseThumbs/qbx-hands-04-weapons-gun-02-l.png +0 -0
  101. package/assets/poseThumbs/qbx-hands-04-weapons-gun-02-r.png +0 -0
  102. package/assets/poseThumbs/qbx-hands-04-weapons-gun-03-l-cock-the-hammer.png +0 -0
  103. package/assets/poseThumbs/qbx-hands-04-weapons-gun-03-r-cock-the-hammer.png +0 -0
  104. package/assets/poseThumbs/qbx-hands-04-weapons-hadoken-l.png +0 -0
  105. package/assets/poseThumbs/qbx-hands-04-weapons-hadoken-r.png +0 -0
  106. package/assets/poseThumbs/qbx-hands-04-weapons-pistol-double-l.png +0 -0
  107. package/assets/poseThumbs/qbx-hands-04-weapons-pistol-double-r.png +0 -0
  108. package/assets/poseThumbs/qbx-hands-04-weapons-pistol-l.png +0 -0
  109. package/assets/poseThumbs/qbx-hands-04-weapons-pistol-r.png +0 -0
  110. package/assets/poseThumbs/qbx-hands-04-weapons-throw-l.png +0 -0
  111. package/assets/poseThumbs/qbx-hands-04-weapons-throw-r.png +0 -0
  112. package/dist/Anchors.d.ts +11 -0
  113. package/dist/Anchors.d.ts.map +1 -0
  114. package/dist/Anchors.js +315 -0
  115. package/dist/App.hostFlows.test.d.ts +2 -0
  116. package/dist/App.hostFlows.test.d.ts.map +1 -0
  117. package/dist/App.hostFlows.test.js +48 -0
  118. package/dist/CameraPath.d.ts +130 -0
  119. package/dist/CameraPath.d.ts.map +1 -0
  120. package/dist/CameraPath.js +920 -0
  121. package/dist/CameraScene.d.ts +45 -0
  122. package/dist/CameraScene.d.ts.map +1 -0
  123. package/dist/CameraScene.js +1716 -0
  124. package/dist/CameraScene.test.d.ts +2 -0
  125. package/dist/CameraScene.test.d.ts.map +1 -0
  126. package/dist/CameraScene.test.js +259 -0
  127. package/dist/PathPresetDropRig.d.ts +27 -0
  128. package/dist/PathPresetDropRig.d.ts.map +1 -0
  129. package/dist/PathPresetDropRig.js +192 -0
  130. package/dist/PathPresetThumb.d.ts +11 -0
  131. package/dist/PathPresetThumb.d.ts.map +1 -0
  132. package/dist/PathPresetThumb.js +85 -0
  133. package/dist/PipCameraSelector.d.ts +15 -0
  134. package/dist/PipCameraSelector.d.ts.map +1 -0
  135. package/dist/PipCameraSelector.js +92 -0
  136. package/dist/PlacementManager.d.ts +252 -0
  137. package/dist/PlacementManager.d.ts.map +1 -0
  138. package/dist/PlacementManager.js +1525 -0
  139. package/dist/ShotCamera.d.ts +21 -0
  140. package/dist/ShotCamera.d.ts.map +1 -0
  141. package/dist/ShotCamera.js +568 -0
  142. package/dist/TopToolbar.d.ts +43 -0
  143. package/dist/TopToolbar.d.ts.map +1 -0
  144. package/dist/TopToolbar.js +167 -0
  145. package/dist/TopToolbar.test.d.ts +2 -0
  146. package/dist/TopToolbar.test.d.ts.map +1 -0
  147. package/dist/TopToolbar.test.js +107 -0
  148. package/dist/agent/api.d.ts +95 -0
  149. package/dist/agent/api.d.ts.map +1 -0
  150. package/dist/agent/api.js +337 -0
  151. package/dist/agent/api.test.d.ts +2 -0
  152. package/dist/agent/api.test.d.ts.map +1 -0
  153. package/dist/agent/api.test.js +907 -0
  154. package/dist/agent/bridgeClient.d.ts +62 -0
  155. package/dist/agent/bridgeClient.d.ts.map +1 -0
  156. package/dist/agent/bridgeClient.js +277 -0
  157. package/dist/agent/bridgeClient.test.d.ts +2 -0
  158. package/dist/agent/bridgeClient.test.d.ts.map +1 -0
  159. package/dist/agent/bridgeClient.test.js +413 -0
  160. package/dist/agent/bridgeFiles.d.ts +33 -0
  161. package/dist/agent/bridgeFiles.d.ts.map +1 -0
  162. package/dist/agent/bridgeFiles.js +193 -0
  163. package/dist/agent/bridgeFiles.test.d.ts +2 -0
  164. package/dist/agent/bridgeFiles.test.d.ts.map +1 -0
  165. package/dist/agent/bridgeFiles.test.js +220 -0
  166. package/dist/agent/commands/anchors.d.ts +43 -0
  167. package/dist/agent/commands/anchors.d.ts.map +1 -0
  168. package/dist/agent/commands/anchors.js +331 -0
  169. package/dist/agent/commands/anchors.test.d.ts +2 -0
  170. package/dist/agent/commands/anchors.test.d.ts.map +1 -0
  171. package/dist/agent/commands/anchors.test.js +447 -0
  172. package/dist/agent/commands/animations.d.ts +47 -0
  173. package/dist/agent/commands/animations.d.ts.map +1 -0
  174. package/dist/agent/commands/animations.js +245 -0
  175. package/dist/agent/commands/animations.test.d.ts +2 -0
  176. package/dist/agent/commands/animations.test.d.ts.map +1 -0
  177. package/dist/agent/commands/animations.test.js +426 -0
  178. package/dist/agent/commands/assets.d.ts +34 -0
  179. package/dist/agent/commands/assets.d.ts.map +1 -0
  180. package/dist/agent/commands/assets.js +367 -0
  181. package/dist/agent/commands/assets.test.d.ts +2 -0
  182. package/dist/agent/commands/assets.test.d.ts.map +1 -0
  183. package/dist/agent/commands/assets.test.js +405 -0
  184. package/dist/agent/commands/bindings.realStudio.test.d.ts +21 -0
  185. package/dist/agent/commands/bindings.realStudio.test.d.ts.map +1 -0
  186. package/dist/agent/commands/bindings.realStudio.test.js +281 -0
  187. package/dist/agent/commands/cameras.d.ts +65 -0
  188. package/dist/agent/commands/cameras.d.ts.map +1 -0
  189. package/dist/agent/commands/cameras.js +498 -0
  190. package/dist/agent/commands/cameras.test.d.ts +2 -0
  191. package/dist/agent/commands/cameras.test.d.ts.map +1 -0
  192. package/dist/agent/commands/cameras.test.js +560 -0
  193. package/dist/agent/commands/delete.test.d.ts +2 -0
  194. package/dist/agent/commands/delete.test.d.ts.map +1 -0
  195. package/dist/agent/commands/delete.test.js +518 -0
  196. package/dist/agent/commands/exports.d.ts +12 -0
  197. package/dist/agent/commands/exports.d.ts.map +1 -0
  198. package/dist/agent/commands/exports.js +132 -0
  199. package/dist/agent/commands/exports.test.d.ts +2 -0
  200. package/dist/agent/commands/exports.test.d.ts.map +1 -0
  201. package/dist/agent/commands/exports.test.js +402 -0
  202. package/dist/agent/commands/history.d.ts +17 -0
  203. package/dist/agent/commands/history.d.ts.map +1 -0
  204. package/dist/agent/commands/history.js +96 -0
  205. package/dist/agent/commands/history.test.d.ts +2 -0
  206. package/dist/agent/commands/history.test.d.ts.map +1 -0
  207. package/dist/agent/commands/history.test.js +140 -0
  208. package/dist/agent/commands/jobs.d.ts +23 -0
  209. package/dist/agent/commands/jobs.d.ts.map +1 -0
  210. package/dist/agent/commands/jobs.js +66 -0
  211. package/dist/agent/commands/keyframes.d.ts +42 -0
  212. package/dist/agent/commands/keyframes.d.ts.map +1 -0
  213. package/dist/agent/commands/keyframes.js +310 -0
  214. package/dist/agent/commands/keyframes.realStudio.test.d.ts +11 -0
  215. package/dist/agent/commands/keyframes.realStudio.test.d.ts.map +1 -0
  216. package/dist/agent/commands/keyframes.realStudio.test.js +95 -0
  217. package/dist/agent/commands/keyframes.test.d.ts +2 -0
  218. package/dist/agent/commands/keyframes.test.d.ts.map +1 -0
  219. package/dist/agent/commands/keyframes.test.js +474 -0
  220. package/dist/agent/commands/models.d.ts +34 -0
  221. package/dist/agent/commands/models.d.ts.map +1 -0
  222. package/dist/agent/commands/models.js +137 -0
  223. package/dist/agent/commands/models.test.d.ts +2 -0
  224. package/dist/agent/commands/models.test.d.ts.map +1 -0
  225. package/dist/agent/commands/models.test.js +268 -0
  226. package/dist/agent/commands/objects.d.ts +4 -0
  227. package/dist/agent/commands/objects.d.ts.map +1 -0
  228. package/dist/agent/commands/objects.js +182 -0
  229. package/dist/agent/commands/objects.test.d.ts +2 -0
  230. package/dist/agent/commands/objects.test.d.ts.map +1 -0
  231. package/dist/agent/commands/objects.test.js +316 -0
  232. package/dist/agent/commands/paths.d.ts +111 -0
  233. package/dist/agent/commands/paths.d.ts.map +1 -0
  234. package/dist/agent/commands/paths.js +576 -0
  235. package/dist/agent/commands/paths.test.d.ts +2 -0
  236. package/dist/agent/commands/paths.test.d.ts.map +1 -0
  237. package/dist/agent/commands/paths.test.js +773 -0
  238. package/dist/agent/commands/poses.d.ts +101 -0
  239. package/dist/agent/commands/poses.d.ts.map +1 -0
  240. package/dist/agent/commands/poses.js +500 -0
  241. package/dist/agent/commands/poses.realStudio.test.d.ts +15 -0
  242. package/dist/agent/commands/poses.realStudio.test.d.ts.map +1 -0
  243. package/dist/agent/commands/poses.realStudio.test.js +161 -0
  244. package/dist/agent/commands/poses.test.d.ts +2 -0
  245. package/dist/agent/commands/poses.test.d.ts.map +1 -0
  246. package/dist/agent/commands/poses.test.js +735 -0
  247. package/dist/agent/commands/project.d.ts +12 -0
  248. package/dist/agent/commands/project.d.ts.map +1 -0
  249. package/dist/agent/commands/project.js +42 -0
  250. package/dist/agent/commands/project.test.d.ts +2 -0
  251. package/dist/agent/commands/project.test.d.ts.map +1 -0
  252. package/dist/agent/commands/project.test.js +125 -0
  253. package/dist/agent/commands/revision.realStudio.test.d.ts +14 -0
  254. package/dist/agent/commands/revision.realStudio.test.d.ts.map +1 -0
  255. package/dist/agent/commands/revision.realStudio.test.js +129 -0
  256. package/dist/agent/commands/scene.d.ts +32 -0
  257. package/dist/agent/commands/scene.d.ts.map +1 -0
  258. package/dist/agent/commands/scene.js +100 -0
  259. package/dist/agent/commands/scene.test.d.ts +2 -0
  260. package/dist/agent/commands/scene.test.d.ts.map +1 -0
  261. package/dist/agent/commands/scene.test.js +147 -0
  262. package/dist/agent/commands/selection.d.ts +21 -0
  263. package/dist/agent/commands/selection.d.ts.map +1 -0
  264. package/dist/agent/commands/selection.js +101 -0
  265. package/dist/agent/commands/selection.test.d.ts +2 -0
  266. package/dist/agent/commands/selection.test.d.ts.map +1 -0
  267. package/dist/agent/commands/selection.test.js +173 -0
  268. package/dist/agent/commands/sequence.d.ts +25 -0
  269. package/dist/agent/commands/sequence.d.ts.map +1 -0
  270. package/dist/agent/commands/sequence.js +174 -0
  271. package/dist/agent/commands/sequence.test.d.ts +2 -0
  272. package/dist/agent/commands/sequence.test.d.ts.map +1 -0
  273. package/dist/agent/commands/sequence.test.js +264 -0
  274. package/dist/agent/commands/ui.d.ts +40 -0
  275. package/dist/agent/commands/ui.d.ts.map +1 -0
  276. package/dist/agent/commands/ui.js +166 -0
  277. package/dist/agent/commands/view.d.ts +61 -0
  278. package/dist/agent/commands/view.d.ts.map +1 -0
  279. package/dist/agent/commands/view.js +229 -0
  280. package/dist/agent/commands/view.test.d.ts +2 -0
  281. package/dist/agent/commands/view.test.d.ts.map +1 -0
  282. package/dist/agent/commands/view.test.js +347 -0
  283. package/dist/agent/contractCoverage.test.d.ts +2 -0
  284. package/dist/agent/contractCoverage.test.d.ts.map +1 -0
  285. package/dist/agent/contractCoverage.test.js +87 -0
  286. package/dist/agent/deleteHandlers.d.ts +61 -0
  287. package/dist/agent/deleteHandlers.d.ts.map +1 -0
  288. package/dist/agent/deleteHandlers.js +41 -0
  289. package/dist/agent/errors.d.ts +24 -0
  290. package/dist/agent/errors.d.ts.map +1 -0
  291. package/dist/agent/errors.js +59 -0
  292. package/dist/agent/errors.test.d.ts +2 -0
  293. package/dist/agent/errors.test.d.ts.map +1 -0
  294. package/dist/agent/errors.test.js +25 -0
  295. package/dist/agent/expectedCommands.d.ts +9 -0
  296. package/dist/agent/expectedCommands.d.ts.map +1 -0
  297. package/dist/agent/expectedCommands.js +79 -0
  298. package/dist/agent/jobs.d.ts +52 -0
  299. package/dist/agent/jobs.d.ts.map +1 -0
  300. package/dist/agent/jobs.js +175 -0
  301. package/dist/agent/jobs.test.d.ts +2 -0
  302. package/dist/agent/jobs.test.d.ts.map +1 -0
  303. package/dist/agent/jobs.test.js +281 -0
  304. package/dist/agent/keyframeTracks.d.ts +57 -0
  305. package/dist/agent/keyframeTracks.d.ts.map +1 -0
  306. package/dist/agent/keyframeTracks.js +127 -0
  307. package/dist/agent/objectIds.d.ts +7 -0
  308. package/dist/agent/objectIds.d.ts.map +1 -0
  309. package/dist/agent/objectIds.js +20 -0
  310. package/dist/agent/objectIds.test.d.ts +2 -0
  311. package/dist/agent/objectIds.test.d.ts.map +1 -0
  312. package/dist/agent/objectIds.test.js +10 -0
  313. package/dist/agent/objectResolver.d.ts +34 -0
  314. package/dist/agent/objectResolver.d.ts.map +1 -0
  315. package/dist/agent/objectResolver.js +112 -0
  316. package/dist/agent/pathCommands.d.ts +78 -0
  317. package/dist/agent/pathCommands.d.ts.map +1 -0
  318. package/dist/agent/pathCommands.js +240 -0
  319. package/dist/agent/propAccess.d.ts +42 -0
  320. package/dist/agent/propAccess.d.ts.map +1 -0
  321. package/dist/agent/propAccess.js +181 -0
  322. package/dist/agent/registerCommands.d.ts +9 -0
  323. package/dist/agent/registerCommands.d.ts.map +1 -0
  324. package/dist/agent/registerCommands.js +77 -0
  325. package/dist/agent/registry.d.ts +33 -0
  326. package/dist/agent/registry.d.ts.map +1 -0
  327. package/dist/agent/registry.js +55 -0
  328. package/dist/agent/registry.test.d.ts +2 -0
  329. package/dist/agent/registry.test.d.ts.map +1 -0
  330. package/dist/agent/registry.test.js +106 -0
  331. package/dist/agent/runtime/animationStatus.d.ts +25 -0
  332. package/dist/agent/runtime/animationStatus.d.ts.map +1 -0
  333. package/dist/agent/runtime/animationStatus.js +13 -0
  334. package/dist/agent/runtime/cameraLifecycle.d.ts +37 -0
  335. package/dist/agent/runtime/cameraLifecycle.d.ts.map +1 -0
  336. package/dist/agent/runtime/cameraLifecycle.js +112 -0
  337. package/dist/agent/runtime/directorView.d.ts +42 -0
  338. package/dist/agent/runtime/directorView.d.ts.map +1 -0
  339. package/dist/agent/runtime/directorView.js +27 -0
  340. package/dist/agent/runtime/exportRuntime.d.ts +95 -0
  341. package/dist/agent/runtime/exportRuntime.d.ts.map +1 -0
  342. package/dist/agent/runtime/exportRuntime.js +227 -0
  343. package/dist/agent/runtime/objectRuntime.d.ts +26 -0
  344. package/dist/agent/runtime/objectRuntime.d.ts.map +1 -0
  345. package/dist/agent/runtime/objectRuntime.js +29 -0
  346. package/dist/agent/runtime/pathRuntime.d.ts +21 -0
  347. package/dist/agent/runtime/pathRuntime.d.ts.map +1 -0
  348. package/dist/agent/runtime/pathRuntime.js +13 -0
  349. package/dist/agent/runtime/placementLifecycle.d.ts +34 -0
  350. package/dist/agent/runtime/placementLifecycle.d.ts.map +1 -0
  351. package/dist/agent/runtime/placementLifecycle.js +63 -0
  352. package/dist/agent/runtime/placementLifecycle.test.d.ts +2 -0
  353. package/dist/agent/runtime/placementLifecycle.test.d.ts.map +1 -0
  354. package/dist/agent/runtime/placementLifecycle.test.js +68 -0
  355. package/dist/agent/runtime/poseSessionRuntime.d.ts +17 -0
  356. package/dist/agent/runtime/poseSessionRuntime.d.ts.map +1 -0
  357. package/dist/agent/runtime/poseSessionRuntime.js +64 -0
  358. package/dist/agent/runtime.d.ts +67 -0
  359. package/dist/agent/runtime.d.ts.map +1 -0
  360. package/dist/agent/runtime.js +214 -0
  361. package/dist/agent/runtime.test.d.ts +2 -0
  362. package/dist/agent/runtime.test.d.ts.map +1 -0
  363. package/dist/agent/runtime.test.js +142 -0
  364. package/dist/agent/sequenceLength.d.ts +20 -0
  365. package/dist/agent/sequenceLength.d.ts.map +1 -0
  366. package/dist/agent/sequenceLength.js +18 -0
  367. package/dist/agent/types.d.ts +70 -0
  368. package/dist/agent/types.d.ts.map +1 -0
  369. package/dist/agent/types.js +6 -0
  370. package/dist/agent/validation.d.ts +7 -0
  371. package/dist/agent/validation.d.ts.map +1 -0
  372. package/dist/agent/validation.js +39 -0
  373. package/dist/aimTargets.d.ts +128 -0
  374. package/dist/aimTargets.d.ts.map +1 -0
  375. package/dist/aimTargets.js +367 -0
  376. package/dist/aimTargets.test.d.ts +2 -0
  377. package/dist/aimTargets.test.d.ts.map +1 -0
  378. package/dist/aimTargets.test.js +84 -0
  379. package/dist/anchorParenting.test.d.ts +2 -0
  380. package/dist/anchorParenting.test.d.ts.map +1 -0
  381. package/dist/anchorParenting.test.js +87 -0
  382. package/dist/anchorsRegistry.d.ts +45 -0
  383. package/dist/anchorsRegistry.d.ts.map +1 -0
  384. package/dist/anchorsRegistry.js +101 -0
  385. package/dist/anchorsRegistry.test.d.ts +2 -0
  386. package/dist/anchorsRegistry.test.d.ts.map +1 -0
  387. package/dist/anchorsRegistry.test.js +29 -0
  388. package/dist/assetLibrary/AssetLibraryPanel.d.ts +35 -0
  389. package/dist/assetLibrary/AssetLibraryPanel.d.ts.map +1 -0
  390. package/dist/assetLibrary/AssetLibraryPanel.js +544 -0
  391. package/dist/assetLibrary/AssetLibraryPanel.viewer.test.d.ts +2 -0
  392. package/dist/assetLibrary/AssetLibraryPanel.viewer.test.d.ts.map +1 -0
  393. package/dist/assetLibrary/AssetLibraryPanel.viewer.test.js +133 -0
  394. package/dist/assetLibrary/ModelPickerSlot.d.ts +24 -0
  395. package/dist/assetLibrary/ModelPickerSlot.d.ts.map +1 -0
  396. package/dist/assetLibrary/ModelPickerSlot.js +32 -0
  397. package/dist/assetLibrary/catalogThumbs.d.ts +7 -0
  398. package/dist/assetLibrary/catalogThumbs.d.ts.map +1 -0
  399. package/dist/assetLibrary/catalogThumbs.js +75 -0
  400. package/dist/assetLibrary/classify.d.ts +14 -0
  401. package/dist/assetLibrary/classify.d.ts.map +1 -0
  402. package/dist/assetLibrary/classify.js +43 -0
  403. package/dist/assetLibrary/classify.test.d.ts +2 -0
  404. package/dist/assetLibrary/classify.test.d.ts.map +1 -0
  405. package/dist/assetLibrary/classify.test.js +44 -0
  406. package/dist/assetLibrary/contract.indexeddb.test.d.ts +2 -0
  407. package/dist/assetLibrary/contract.indexeddb.test.d.ts.map +1 -0
  408. package/dist/assetLibrary/contract.indexeddb.test.js +10 -0
  409. package/dist/assetLibrary/contractTests.d.ts +12 -0
  410. package/dist/assetLibrary/contractTests.d.ts.map +1 -0
  411. package/dist/assetLibrary/contractTests.js +51 -0
  412. package/dist/assetLibrary/dispose.d.ts +4 -0
  413. package/dist/assetLibrary/dispose.d.ts.map +1 -0
  414. package/dist/assetLibrary/dispose.js +21 -0
  415. package/dist/assetLibrary/draco.d.ts +6 -0
  416. package/dist/assetLibrary/draco.d.ts.map +1 -0
  417. package/dist/assetLibrary/draco.js +45 -0
  418. package/dist/assetLibrary/draco.test.d.ts +2 -0
  419. package/dist/assetLibrary/draco.test.d.ts.map +1 -0
  420. package/dist/assetLibrary/draco.test.js +93 -0
  421. package/dist/assetLibrary/exportGLB.d.ts +20 -0
  422. package/dist/assetLibrary/exportGLB.d.ts.map +1 -0
  423. package/dist/assetLibrary/exportGLB.js +83 -0
  424. package/dist/assetLibrary/exportGLB.test.d.ts +2 -0
  425. package/dist/assetLibrary/exportGLB.test.d.ts.map +1 -0
  426. package/dist/assetLibrary/exportGLB.test.js +73 -0
  427. package/dist/assetLibrary/gltfLoader.d.ts +8 -0
  428. package/dist/assetLibrary/gltfLoader.d.ts.map +1 -0
  429. package/dist/assetLibrary/gltfLoader.js +11 -0
  430. package/dist/assetLibrary/hash.d.ts +7 -0
  431. package/dist/assetLibrary/hash.d.ts.map +1 -0
  432. package/dist/assetLibrary/hash.js +17 -0
  433. package/dist/assetLibrary/importModelFile.d.ts +5 -0
  434. package/dist/assetLibrary/importModelFile.d.ts.map +1 -0
  435. package/dist/assetLibrary/importModelFile.js +19 -0
  436. package/dist/assetLibrary/importModelFile.test.d.ts +2 -0
  437. package/dist/assetLibrary/importModelFile.test.d.ts.map +1 -0
  438. package/dist/assetLibrary/importModelFile.test.js +77 -0
  439. package/dist/assetLibrary/importPipeline.d.ts +61 -0
  440. package/dist/assetLibrary/importPipeline.d.ts.map +1 -0
  441. package/dist/assetLibrary/importPipeline.js +253 -0
  442. package/dist/assetLibrary/importPipeline.test.d.ts +2 -0
  443. package/dist/assetLibrary/importPipeline.test.d.ts.map +1 -0
  444. package/dist/assetLibrary/importPipeline.test.js +188 -0
  445. package/dist/assetLibrary/indexedDBBackend.d.ts +31 -0
  446. package/dist/assetLibrary/indexedDBBackend.d.ts.map +1 -0
  447. package/dist/assetLibrary/indexedDBBackend.js +134 -0
  448. package/dist/assetLibrary/indexedDBBackend.test.d.ts +2 -0
  449. package/dist/assetLibrary/indexedDBBackend.test.d.ts.map +1 -0
  450. package/dist/assetLibrary/indexedDBBackend.test.js +148 -0
  451. package/dist/assetLibrary/legacySeedCleanup.d.ts +19 -0
  452. package/dist/assetLibrary/legacySeedCleanup.d.ts.map +1 -0
  453. package/dist/assetLibrary/legacySeedCleanup.js +50 -0
  454. package/dist/assetLibrary/legacySeedCleanup.test.d.ts +2 -0
  455. package/dist/assetLibrary/legacySeedCleanup.test.d.ts.map +1 -0
  456. package/dist/assetLibrary/legacySeedCleanup.test.js +74 -0
  457. package/dist/assetLibrary/library.d.ts +122 -0
  458. package/dist/assetLibrary/library.d.ts.map +1 -0
  459. package/dist/assetLibrary/library.js +424 -0
  460. package/dist/assetLibrary/library.test.d.ts +2 -0
  461. package/dist/assetLibrary/library.test.d.ts.map +1 -0
  462. package/dist/assetLibrary/library.test.js +520 -0
  463. package/dist/assetLibrary/options.d.ts +9 -0
  464. package/dist/assetLibrary/options.d.ts.map +1 -0
  465. package/dist/assetLibrary/options.js +20 -0
  466. package/dist/assetLibrary/options.test.d.ts +2 -0
  467. package/dist/assetLibrary/options.test.d.ts.map +1 -0
  468. package/dist/assetLibrary/options.test.js +70 -0
  469. package/dist/assetLibrary/pose.d.ts +15 -0
  470. package/dist/assetLibrary/pose.d.ts.map +1 -0
  471. package/dist/assetLibrary/pose.js +41 -0
  472. package/dist/assetLibrary/pose.test.d.ts +2 -0
  473. package/dist/assetLibrary/pose.test.d.ts.map +1 -0
  474. package/dist/assetLibrary/pose.test.js +45 -0
  475. package/dist/assetLibrary/poseThumbnail.d.ts +30 -0
  476. package/dist/assetLibrary/poseThumbnail.d.ts.map +1 -0
  477. package/dist/assetLibrary/poseThumbnail.js +144 -0
  478. package/dist/assetLibrary/poseThumbnail.test.d.ts +2 -0
  479. package/dist/assetLibrary/poseThumbnail.test.d.ts.map +1 -0
  480. package/dist/assetLibrary/poseThumbnail.test.js +111 -0
  481. package/dist/assetLibrary/rig.d.ts +14 -0
  482. package/dist/assetLibrary/rig.d.ts.map +1 -0
  483. package/dist/assetLibrary/rig.js +33 -0
  484. package/dist/assetLibrary/rig.test.d.ts +2 -0
  485. package/dist/assetLibrary/rig.test.d.ts.map +1 -0
  486. package/dist/assetLibrary/rig.test.js +55 -0
  487. package/dist/assetLibrary/seedAssetsManifest.d.ts +24 -0
  488. package/dist/assetLibrary/seedAssetsManifest.d.ts.map +1 -0
  489. package/dist/assetLibrary/seedAssetsManifest.js +347 -0
  490. package/dist/assetLibrary/seedBuiltinPoses.d.ts +29 -0
  491. package/dist/assetLibrary/seedBuiltinPoses.d.ts.map +1 -0
  492. package/dist/assetLibrary/seedBuiltinPoses.js +114 -0
  493. package/dist/assetLibrary/seedBuiltinPoses.test.d.ts +2 -0
  494. package/dist/assetLibrary/seedBuiltinPoses.test.d.ts.map +1 -0
  495. package/dist/assetLibrary/seedBuiltinPoses.test.js +176 -0
  496. package/dist/assetLibrary/seedPoses.d.ts +8 -0
  497. package/dist/assetLibrary/seedPoses.d.ts.map +1 -0
  498. package/dist/assetLibrary/seedPoses.js +2414 -0
  499. package/dist/assetLibrary/seedPosesDpl.d.ts +8 -0
  500. package/dist/assetLibrary/seedPosesDpl.d.ts.map +1 -0
  501. package/dist/assetLibrary/seedPosesDpl.js +11660 -0
  502. package/dist/assetLibrary/seedPosesQbx.d.ts +8 -0
  503. package/dist/assetLibrary/seedPosesQbx.d.ts.map +1 -0
  504. package/dist/assetLibrary/seedPosesQbx.js +5612 -0
  505. package/dist/assetLibrary/seedPosesQbx.test.d.ts +2 -0
  506. package/dist/assetLibrary/seedPosesQbx.test.d.ts.map +1 -0
  507. package/dist/assetLibrary/seedPosesQbx.test.js +181 -0
  508. package/dist/assetLibrary/source.d.ts +45 -0
  509. package/dist/assetLibrary/source.d.ts.map +1 -0
  510. package/dist/assetLibrary/source.js +11 -0
  511. package/dist/assetLibrary/source.test.d.ts +2 -0
  512. package/dist/assetLibrary/source.test.d.ts.map +1 -0
  513. package/dist/assetLibrary/source.test.js +28 -0
  514. package/dist/assetLibrary/staticCatalogSource.d.ts +48 -0
  515. package/dist/assetLibrary/staticCatalogSource.d.ts.map +1 -0
  516. package/dist/assetLibrary/staticCatalogSource.js +140 -0
  517. package/dist/assetLibrary/staticCatalogSource.test.d.ts +2 -0
  518. package/dist/assetLibrary/staticCatalogSource.test.d.ts.map +1 -0
  519. package/dist/assetLibrary/staticCatalogSource.test.js +154 -0
  520. package/dist/assetLibrary/thumbnail.d.ts +14 -0
  521. package/dist/assetLibrary/thumbnail.d.ts.map +1 -0
  522. package/dist/assetLibrary/thumbnail.js +70 -0
  523. package/dist/assetLibrary/types.d.ts +54 -0
  524. package/dist/assetLibrary/types.d.ts.map +1 -0
  525. package/dist/assetLibrary/types.js +1 -0
  526. package/dist/assetLibrary/useModelThumbUrl.d.ts +10 -0
  527. package/dist/assetLibrary/useModelThumbUrl.d.ts.map +1 -0
  528. package/dist/assetLibrary/useModelThumbUrl.js +56 -0
  529. package/dist/boneAnchorWiring.test.d.ts +2 -0
  530. package/dist/boneAnchorWiring.test.d.ts.map +1 -0
  531. package/dist/boneAnchorWiring.test.js +173 -0
  532. package/dist/boneAnchors.d.ts +30 -0
  533. package/dist/boneAnchors.d.ts.map +1 -0
  534. package/dist/boneAnchors.js +111 -0
  535. package/dist/boneAnchors.test.d.ts +2 -0
  536. package/dist/boneAnchors.test.d.ts.map +1 -0
  537. package/dist/boneAnchors.test.js +98 -0
  538. package/dist/bootstrap.d.ts +37 -0
  539. package/dist/bootstrap.d.ts.map +1 -0
  540. package/dist/bootstrap.js +394 -0
  541. package/dist/bootstrap.test.d.ts +2 -0
  542. package/dist/bootstrap.test.d.ts.map +1 -0
  543. package/dist/bootstrap.test.js +647 -0
  544. package/dist/cameraRegistry.d.ts +65 -0
  545. package/dist/cameraRegistry.d.ts.map +1 -0
  546. package/dist/cameraRegistry.js +151 -0
  547. package/dist/cameraRegistry.test.d.ts +2 -0
  548. package/dist/cameraRegistry.test.d.ts.map +1 -0
  549. package/dist/cameraRegistry.test.js +68 -0
  550. package/dist/config.d.ts +92 -0
  551. package/dist/config.d.ts.map +1 -0
  552. package/dist/config.js +1 -0
  553. package/dist/contextMenu.d.ts +16 -0
  554. package/dist/contextMenu.d.ts.map +1 -0
  555. package/dist/contextMenu.js +10 -0
  556. package/dist/dropPoint.d.ts +61 -0
  557. package/dist/dropPoint.d.ts.map +1 -0
  558. package/dist/dropPoint.js +97 -0
  559. package/dist/dropPoint.test.d.ts +2 -0
  560. package/dist/dropPoint.test.d.ts.map +1 -0
  561. package/dist/dropPoint.test.js +174 -0
  562. package/dist/editorOnly.d.ts +6 -0
  563. package/dist/editorOnly.d.ts.map +1 -0
  564. package/dist/editorOnly.js +32 -0
  565. package/dist/errors.d.ts +24 -0
  566. package/dist/errors.d.ts.map +1 -0
  567. package/dist/errors.js +13 -0
  568. package/dist/gestureProps.d.ts +99 -0
  569. package/dist/gestureProps.d.ts.map +1 -0
  570. package/dist/gestureProps.js +118 -0
  571. package/dist/gestureProps.test.d.ts +2 -0
  572. package/dist/gestureProps.test.d.ts.map +1 -0
  573. package/dist/gestureProps.test.js +182 -0
  574. package/dist/gesturePropsEditor.d.ts +27 -0
  575. package/dist/gesturePropsEditor.d.ts.map +1 -0
  576. package/dist/gesturePropsEditor.js +121 -0
  577. package/dist/getDeep.d.ts +8 -0
  578. package/dist/getDeep.d.ts.map +1 -0
  579. package/dist/getDeep.js +15 -0
  580. package/dist/gizmoRegistry.test.d.ts +2 -0
  581. package/dist/gizmoRegistry.test.d.ts.map +1 -0
  582. package/dist/gizmoRegistry.test.js +40 -0
  583. package/dist/hostFlows.d.ts +55 -0
  584. package/dist/hostFlows.d.ts.map +1 -0
  585. package/dist/hostFlows.js +77 -0
  586. package/dist/hostFlows.test.d.ts +2 -0
  587. package/dist/hostFlows.test.d.ts.map +1 -0
  588. package/dist/hostFlows.test.js +191 -0
  589. package/dist/i18n.d.ts +26 -0
  590. package/dist/i18n.d.ts.map +1 -0
  591. package/dist/i18n.js +271 -0
  592. package/dist/i18n.test.d.ts +2 -0
  593. package/dist/i18n.test.d.ts.map +1 -0
  594. package/dist/i18n.test.js +41 -0
  595. package/dist/index.d.ts +42 -0
  596. package/dist/index.d.ts.map +1 -0
  597. package/dist/index.js +11 -0
  598. package/dist/menuModel.d.ts +51 -0
  599. package/dist/menuModel.d.ts.map +1 -0
  600. package/dist/menuModel.js +111 -0
  601. package/dist/menuModel.test.d.ts +2 -0
  602. package/dist/menuModel.test.d.ts.map +1 -0
  603. package/dist/menuModel.test.js +197 -0
  604. package/dist/modalGrab.d.ts +29 -0
  605. package/dist/modalGrab.d.ts.map +1 -0
  606. package/dist/modalGrab.js +127 -0
  607. package/dist/outlineObjectMenu.d.ts +41 -0
  608. package/dist/outlineObjectMenu.d.ts.map +1 -0
  609. package/dist/outlineObjectMenu.js +58 -0
  610. package/dist/outlineObjectMenu.test.d.ts +2 -0
  611. package/dist/outlineObjectMenu.test.d.ts.map +1 -0
  612. package/dist/outlineObjectMenu.test.js +83 -0
  613. package/dist/pathBinding.d.ts +16 -0
  614. package/dist/pathBinding.d.ts.map +1 -0
  615. package/dist/pathBinding.js +19 -0
  616. package/dist/pathBinding.test.d.ts +2 -0
  617. package/dist/pathBinding.test.d.ts.map +1 -0
  618. package/dist/pathBinding.test.js +14 -0
  619. package/dist/pathModel.d.ts +123 -0
  620. package/dist/pathModel.d.ts.map +1 -0
  621. package/dist/pathModel.js +343 -0
  622. package/dist/pathModel.test.d.ts +2 -0
  623. package/dist/pathModel.test.d.ts.map +1 -0
  624. package/dist/pathModel.test.js +394 -0
  625. package/dist/pathPresets.d.ts +38 -0
  626. package/dist/pathPresets.d.ts.map +1 -0
  627. package/dist/pathPresets.js +180 -0
  628. package/dist/pathPresets.test.d.ts +2 -0
  629. package/dist/pathPresets.test.d.ts.map +1 -0
  630. package/dist/pathPresets.test.js +154 -0
  631. package/dist/pathRegistry.d.ts +46 -0
  632. package/dist/pathRegistry.d.ts.map +1 -0
  633. package/dist/pathRegistry.js +105 -0
  634. package/dist/pathRegistry.test.d.ts +2 -0
  635. package/dist/pathRegistry.test.d.ts.map +1 -0
  636. package/dist/pathRegistry.test.js +23 -0
  637. package/dist/pickerRegistry.d.ts +49 -0
  638. package/dist/pickerRegistry.d.ts.map +1 -0
  639. package/dist/pickerRegistry.js +17 -0
  640. package/dist/pickerRegistry.test.d.ts +2 -0
  641. package/dist/pickerRegistry.test.d.ts.map +1 -0
  642. package/dist/pickerRegistry.test.js +31 -0
  643. package/dist/pipLayout.d.ts +7 -0
  644. package/dist/pipLayout.d.ts.map +1 -0
  645. package/dist/pipLayout.js +58 -0
  646. package/dist/placementKey.test.d.ts +2 -0
  647. package/dist/placementKey.test.d.ts.map +1 -0
  648. package/dist/placementKey.test.js +32 -0
  649. package/dist/placementPoseProps.d.ts +29 -0
  650. package/dist/placementPoseProps.d.ts.map +1 -0
  651. package/dist/placementPoseProps.js +43 -0
  652. package/dist/placementPoseProps.test.d.ts +2 -0
  653. package/dist/placementPoseProps.test.d.ts.map +1 -0
  654. package/dist/placementPoseProps.test.js +35 -0
  655. package/dist/poseMode/PoseModeBar.d.ts +18 -0
  656. package/dist/poseMode/PoseModeBar.d.ts.map +1 -0
  657. package/dist/poseMode/PoseModeBar.js +127 -0
  658. package/dist/poseMode/PoseModeRig.d.ts +9 -0
  659. package/dist/poseMode/PoseModeRig.d.ts.map +1 -0
  660. package/dist/poseMode/PoseModeRig.js +300 -0
  661. package/dist/poseMode/PosePickerFrame.d.ts +42 -0
  662. package/dist/poseMode/PosePickerFrame.d.ts.map +1 -0
  663. package/dist/poseMode/PosePickerFrame.js +164 -0
  664. package/dist/poseMode/PosePropEditor.d.ts +30 -0
  665. package/dist/poseMode/PosePropEditor.d.ts.map +1 -0
  666. package/dist/poseMode/PosePropEditor.js +139 -0
  667. package/dist/poseMode/controlSpec.d.ts +26 -0
  668. package/dist/poseMode/controlSpec.d.ts.map +1 -0
  669. package/dist/poseMode/controlSpec.js +157 -0
  670. package/dist/poseMode/controlSpec.test.d.ts +2 -0
  671. package/dist/poseMode/controlSpec.test.d.ts.map +1 -0
  672. package/dist/poseMode/controlSpec.test.js +196 -0
  673. package/dist/poseMode/dragState.d.ts +16 -0
  674. package/dist/poseMode/dragState.d.ts.map +1 -0
  675. package/dist/poseMode/dragState.js +15 -0
  676. package/dist/poseMode/dragState.test.d.ts +2 -0
  677. package/dist/poseMode/dragState.test.d.ts.map +1 -0
  678. package/dist/poseMode/dragState.test.js +23 -0
  679. package/dist/poseMode/poseDisplayText.d.ts +18 -0
  680. package/dist/poseMode/poseDisplayText.d.ts.map +1 -0
  681. package/dist/poseMode/poseDisplayText.js +13 -0
  682. package/dist/poseMode/poseDisplayText.test.d.ts +2 -0
  683. package/dist/poseMode/poseDisplayText.test.d.ts.map +1 -0
  684. package/dist/poseMode/poseDisplayText.test.js +35 -0
  685. package/dist/poseMode/poseExitGuard.d.ts +8 -0
  686. package/dist/poseMode/poseExitGuard.d.ts.map +1 -0
  687. package/dist/poseMode/poseExitGuard.js +13 -0
  688. package/dist/poseMode/poseExitGuard.test.d.ts +2 -0
  689. package/dist/poseMode/poseExitGuard.test.d.ts.map +1 -0
  690. package/dist/poseMode/poseExitGuard.test.js +41 -0
  691. package/dist/poseMode/poseFrame.d.ts +11 -0
  692. package/dist/poseMode/poseFrame.d.ts.map +1 -0
  693. package/dist/poseMode/poseFrame.js +7 -0
  694. package/dist/poseMode/poseFrame.test.d.ts +2 -0
  695. package/dist/poseMode/poseFrame.test.d.ts.map +1 -0
  696. package/dist/poseMode/poseFrame.test.js +25 -0
  697. package/dist/poseMode/poseInterpolate.d.ts +4 -0
  698. package/dist/poseMode/poseInterpolate.d.ts.map +1 -0
  699. package/dist/poseMode/poseInterpolate.js +75 -0
  700. package/dist/poseMode/poseInterpolate.test.d.ts +2 -0
  701. package/dist/poseMode/poseInterpolate.test.d.ts.map +1 -0
  702. package/dist/poseMode/poseInterpolate.test.js +61 -0
  703. package/dist/poseMode/poseLibResolve.d.ts +9 -0
  704. package/dist/poseMode/poseLibResolve.d.ts.map +1 -0
  705. package/dist/poseMode/poseLibResolve.js +49 -0
  706. package/dist/poseMode/poseLibResolve.test.d.ts +2 -0
  707. package/dist/poseMode/poseLibResolve.test.d.ts.map +1 -0
  708. package/dist/poseMode/poseLibResolve.test.js +59 -0
  709. package/dist/poseMode/poseMigration.d.ts +5 -0
  710. package/dist/poseMode/poseMigration.d.ts.map +1 -0
  711. package/dist/poseMode/poseMigration.js +16 -0
  712. package/dist/poseMode/poseMigration.test.d.ts +2 -0
  713. package/dist/poseMode/poseMigration.test.d.ts.map +1 -0
  714. package/dist/poseMode/poseMigration.test.js +25 -0
  715. package/dist/poseMode/poseOptions.d.ts +45 -0
  716. package/dist/poseMode/poseOptions.d.ts.map +1 -0
  717. package/dist/poseMode/poseOptions.js +39 -0
  718. package/dist/poseMode/poseOptions.test.d.ts +2 -0
  719. package/dist/poseMode/poseOptions.test.d.ts.map +1 -0
  720. package/dist/poseMode/poseOptions.test.js +83 -0
  721. package/dist/poseMode/poseOverride.d.ts +13 -0
  722. package/dist/poseMode/poseOverride.d.ts.map +1 -0
  723. package/dist/poseMode/poseOverride.js +26 -0
  724. package/dist/poseMode/poseOverride.test.d.ts +2 -0
  725. package/dist/poseMode/poseOverride.test.d.ts.map +1 -0
  726. package/dist/poseMode/poseOverride.test.js +23 -0
  727. package/dist/poseMode/poseRetarget.d.ts +17 -0
  728. package/dist/poseMode/poseRetarget.d.ts.map +1 -0
  729. package/dist/poseMode/poseRetarget.js +53 -0
  730. package/dist/poseMode/poseRetarget.test.d.ts +2 -0
  731. package/dist/poseMode/poseRetarget.test.d.ts.map +1 -0
  732. package/dist/poseMode/poseRetarget.test.js +88 -0
  733. package/dist/poseMode/poseSession.d.ts +58 -0
  734. package/dist/poseMode/poseSession.d.ts.map +1 -0
  735. package/dist/poseMode/poseSession.js +183 -0
  736. package/dist/poseMode/poseSession.test.d.ts +2 -0
  737. package/dist/poseMode/poseSession.test.d.ts.map +1 -0
  738. package/dist/poseMode/poseSession.test.js +159 -0
  739. package/dist/poseMode/poseSessionHooks.test.d.ts +2 -0
  740. package/dist/poseMode/poseSessionHooks.test.d.ts.map +1 -0
  741. package/dist/poseMode/poseSessionHooks.test.js +49 -0
  742. package/dist/poseMode/poseTrack.d.ts +30 -0
  743. package/dist/poseMode/poseTrack.d.ts.map +1 -0
  744. package/dist/poseMode/poseTrack.js +155 -0
  745. package/dist/poseMode/poseTrack.test.d.ts +2 -0
  746. package/dist/poseMode/poseTrack.test.d.ts.map +1 -0
  747. package/dist/poseMode/poseTrack.test.js +84 -0
  748. package/dist/poseMode/poseValue.d.ts +22 -0
  749. package/dist/poseMode/poseValue.d.ts.map +1 -0
  750. package/dist/poseMode/poseValue.js +73 -0
  751. package/dist/poseMode/poseValue.test.d.ts +2 -0
  752. package/dist/poseMode/poseValue.test.d.ts.map +1 -0
  753. package/dist/poseMode/poseValue.test.js +62 -0
  754. package/dist/poseMode/savePose.d.ts +23 -0
  755. package/dist/poseMode/savePose.d.ts.map +1 -0
  756. package/dist/poseMode/savePose.js +29 -0
  757. package/dist/poseMode/selectionModes.d.ts +14 -0
  758. package/dist/poseMode/selectionModes.d.ts.map +1 -0
  759. package/dist/poseMode/selectionModes.js +39 -0
  760. package/dist/poseMode/selectionModes.test.d.ts +2 -0
  761. package/dist/poseMode/selectionModes.test.d.ts.map +1 -0
  762. package/dist/poseMode/selectionModes.test.js +91 -0
  763. package/dist/poseNameMessages.d.ts +13 -0
  764. package/dist/poseNameMessages.d.ts.map +1 -0
  765. package/dist/poseNameMessages.js +370 -0
  766. package/dist/poseNames.d.ts +14 -0
  767. package/dist/poseNames.d.ts.map +1 -0
  768. package/dist/poseNames.js +21 -0
  769. package/dist/poseNames.test.d.ts +2 -0
  770. package/dist/poseNames.test.d.ts.map +1 -0
  771. package/dist/poseNames.test.js +41 -0
  772. package/dist/poseStatusText.d.ts +2 -0
  773. package/dist/poseStatusText.d.ts.map +1 -0
  774. package/dist/poseStatusText.js +11 -0
  775. package/dist/poseStatusText.test.d.ts +2 -0
  776. package/dist/poseStatusText.test.d.ts.map +1 -0
  777. package/dist/poseStatusText.test.js +21 -0
  778. package/dist/presetThumbGeometry.d.ts +26 -0
  779. package/dist/presetThumbGeometry.d.ts.map +1 -0
  780. package/dist/presetThumbGeometry.js +103 -0
  781. package/dist/presetThumbGeometry.test.d.ts +2 -0
  782. package/dist/presetThumbGeometry.test.d.ts.map +1 -0
  783. package/dist/presetThumbGeometry.test.js +83 -0
  784. package/dist/selectionOutline.d.ts +42 -0
  785. package/dist/selectionOutline.d.ts.map +1 -0
  786. package/dist/selectionOutline.js +174 -0
  787. package/dist/selectionOutline.test.d.ts +2 -0
  788. package/dist/selectionOutline.test.d.ts.map +1 -0
  789. package/dist/selectionOutline.test.js +58 -0
  790. package/dist/smoke.test.d.ts +1 -0
  791. package/dist/smoke.test.d.ts.map +1 -0
  792. package/dist/smoke.test.js +17 -0
  793. package/dist/uiConfig.d.ts +16 -0
  794. package/dist/uiConfig.d.ts.map +1 -0
  795. package/dist/uiConfig.js +13 -0
  796. package/dist/uiConfig.test.d.ts +2 -0
  797. package/dist/uiConfig.test.d.ts.map +1 -0
  798. package/dist/uiConfig.test.js +98 -0
  799. package/dist/undoShortcuts.d.ts +28 -0
  800. package/dist/undoShortcuts.d.ts.map +1 -0
  801. package/dist/undoShortcuts.js +59 -0
  802. package/dist/undoShortcuts.test.d.ts +2 -0
  803. package/dist/undoShortcuts.test.d.ts.map +1 -0
  804. package/dist/undoShortcuts.test.js +37 -0
  805. package/dist/useCameraStudio.d.ts +3 -0
  806. package/dist/useCameraStudio.d.ts.map +1 -0
  807. package/dist/useCameraStudio.js +86 -0
  808. package/dist/version.d.ts +2 -0
  809. package/dist/version.d.ts.map +1 -0
  810. package/dist/version.js +11 -0
  811. package/dist/videoExport/BlobCollectSink.d.ts +25 -0
  812. package/dist/videoExport/BlobCollectSink.d.ts.map +1 -0
  813. package/dist/videoExport/BlobCollectSink.js +56 -0
  814. package/dist/videoExport/BlobCollectSink.test.d.ts +2 -0
  815. package/dist/videoExport/BlobCollectSink.test.d.ts.map +1 -0
  816. package/dist/videoExport/BlobCollectSink.test.js +78 -0
  817. package/dist/videoExport/encoder.d.ts +34 -0
  818. package/dist/videoExport/encoder.d.ts.map +1 -0
  819. package/dist/videoExport/encoder.js +61 -0
  820. package/dist/videoExport/encoder.test.d.ts +2 -0
  821. package/dist/videoExport/encoder.test.d.ts.map +1 -0
  822. package/dist/videoExport/encoder.test.js +21 -0
  823. package/dist/videoExport/exporter.d.ts +24 -0
  824. package/dist/videoExport/exporter.d.ts.map +1 -0
  825. package/dist/videoExport/exporter.js +74 -0
  826. package/dist/videoExport/sinks.d.ts +55 -0
  827. package/dist/videoExport/sinks.d.ts.map +1 -0
  828. package/dist/videoExport/sinks.js +69 -0
  829. package/package.json +59 -3
  830. package/README.md +0 -3
@@ -0,0 +1,1525 @@
1
+ import { editable as e } from '@dreamerbird/r3f';
2
+ import { getStudioSync, onChange, types as t, val } from '@dreamerbird/core';
3
+ import { privateAPI } from '@dreamerbird/core/privateAPIs';
4
+ import { useVal } from '@dreamerbird/react';
5
+ import { translate } from '@dreamerbird/studio/i18n';
6
+ // 内部 Studio 通道:删除合并事务需要 stateEditors.forgetObject
7
+ // (按地址清持久数据,不要求对象已挂载)——与 keyframeTracks 同先例
8
+ import getStudio from '@dreamerbird/studio/getStudio';
9
+ import { TransformControls, useFBX } from '@react-three/drei';
10
+ import { useFrame, useThree } from '@react-three/fiber';
11
+ import React, { Suspense, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
12
+ import { AnimationMixer, Box3, BoxGeometry, Mesh, MeshBasicMaterial, Raycaster, Vector3, } from 'three';
13
+ import { SkeletonUtils } from 'three-stdlib';
14
+ import { registerBoneAnchors, unregisterBoneAnchors } from './boneAnchors';
15
+ import { notifyTargetRegistryChange, sheetObjectExists } from './aimTargets';
16
+ import { createGLTFLoader } from './assetLibrary/gltfLoader';
17
+ import { getLibrary } from './assetLibrary/library';
18
+ import { LibraryError } from './assetLibrary/source';
19
+ import { entryOptions } from './assetLibrary/options';
20
+ import { computeDropPoint } from './dropPoint';
21
+ import { registerEditorOnly } from './editorOnly';
22
+ import { createSelectionOutlineRenderer, } from './selectionOutline';
23
+ import { useModalGrab } from './modalGrab';
24
+ import { disposeObjectTree } from './assetLibrary/dispose';
25
+ import { applyPose, captureBindPose } from './assetLibrary/pose';
26
+ import { isPoseEntryUsed, posePropConfigShape, shouldApplyPoseDuringPlayback, shouldApplyPoseWhenIdle, } from './placementPoseProps';
27
+ import { PoseModeRig } from './poseMode/PoseModeRig';
28
+ import { PoseModeBar } from './poseMode/PoseModeBar';
29
+ import { enterPoseSession, getPoseSession, noteModelDoubleClick, usePoseSessionKey, } from './poseMode/poseSession';
30
+ import { resolveControlSpec } from './poseMode/controlSpec';
31
+ import { prefetchLibPoses, resolvePoseBones } from './poseMode/poseLibResolve';
32
+ import { collectLibPoseIdsForProps, getPoseTrackId, getTrackIdForProp, } from './poseMode/poseTrack';
33
+ import { gesturePropConfigShape, resolveGestureBones } from './gestureProps';
34
+ import { computeLegacyPoseMigration } from './poseMode/poseMigration';
35
+ import { parsePoseValue } from './poseMode/poseValue';
36
+ import { formatSceneObjectId } from './agent/objectIds';
37
+ import { registerSceneSheetObject, unregisterSceneSheetObject, } from './agent/runtime/objectRuntime';
38
+ import { createPlacementWaiter, rejectPlacement, resolvePlacementPersisted, } from './agent/runtime/placementLifecycle';
39
+ import { clearAnimationLoadStatus, setAnimationLoadStatus, } from './agent/runtime/animationStatus';
40
+ /** 注册表对象的 theatre key(大纲顶层 "Placements" 行;Agent resolver 只读探测用) */
41
+ export const PLACEMENTS_REGISTRY_KEY = 'Placements';
42
+ function assetStorageOf(project) {
43
+ const internal = privateAPI(project);
44
+ return internal?.assetStorage;
45
+ }
46
+ /**
47
+ * 每个资产 id 的 file 属性配置,模块级缓存。
48
+ * t.file/t.number 每次调用都会生成新的闭包(interpolate 等),而 sheet.object
49
+ * 对同一个 key 的重复调用要求配置 fast-deep-equal(函数按引用比)——
50
+ * 只有缓存成同一个对象引用,命令式创建和 <e.group additionalProps>
51
+ * 两条路径的配置才能相等。
52
+ */
53
+ const fileConfigCache = new Map();
54
+ /**
55
+ * 动作/姿势下拉配置,按 (属性, 模型库条目 id) 缓存。
56
+ * 和上面 fileConfigCache 同一个原因:sheet.object 对同一 key 的重复调用
57
+ * 要求配置 fast-deep-equal(函数按引用比),所以闭包不能每次新建。
58
+ * 选项函数本身不闭包骨架族,而是闭包模型条目 id,调用时才从库快照解析
59
+ * rig——配置从首次渲染起引用稳定,快照就绪后筛选自动生效。
60
+ */
61
+ const literalConfigCache = new Map();
62
+ function literalConfigFor(kind, modelEntryId) {
63
+ const cacheKey = `${kind}:${modelEntryId ?? ''}`;
64
+ let config = literalConfigCache.get(cacheKey);
65
+ if (!config) {
66
+ config = t.stringLiteral('', () => entryOptions(getLibrary().getEntriesSnapshot(), kind, modelEntryId),
67
+ // label 用函数形式:studio 每次渲染现调(resolvePropLabel),
68
+ // 语言切换热改;箭头在缓存首次创建时定义,引用稳定
69
+ {
70
+ label: () => translate('pg.props.animation'),
71
+ as: 'menu',
72
+ });
73
+ literalConfigCache.set(cacheKey, config);
74
+ }
75
+ return config;
76
+ }
77
+ /** 已放置模型的 theatre 对象 → {PlacementEntry, 场景实例} 映射,
78
+ * App 通过 selection 反查 rig,Task 6 用 instance 抽帧。
79
+ */
80
+ const placementObjects = new Map();
81
+ export function getPlacementEntryForObject(obj) {
82
+ return placementObjects.get(obj)?.entry;
83
+ }
84
+ /** 按 theatre 对象取已放置实例的 entry 与场景 Group,供 App 抽帧/查 rig 用 */
85
+ export function getPlacementInstanceForObject(obj) {
86
+ return placementObjects.get(obj);
87
+ }
88
+ /** 统计场景里引用某库条目的已放置实例数(模型本体/动作/姿势三种引用) */
89
+ export function countLibraryEntryUsage(entryId) {
90
+ let n = 0;
91
+ for (const [obj, { entry }] of placementObjects.entries()) {
92
+ if (entry.libraryEntryId === entryId)
93
+ n++;
94
+ if (val(obj.props.animation) === entryId)
95
+ n++;
96
+ if (isPoseEntryUsed(val(obj.props.pose) ?? '', entryId))
97
+ n++;
98
+ }
99
+ return n;
100
+ }
101
+ function fileConfigFor(assetId) {
102
+ let config = fileConfigCache.get(assetId);
103
+ if (!config) {
104
+ // label 函数形式:面板每次渲染现调,语言切换热改(对齐
105
+ // literalConfigFor);缓存在首次创建时固化引用,满足 deep-equal
106
+ config = t.file(assetId, {
107
+ label: () => translate('pg.props.source'),
108
+ });
109
+ fileConfigCache.set(assetId, config);
110
+ }
111
+ return config;
112
+ }
113
+ function getRegistry(sheet) {
114
+ // 注册表只是存数据的对象,不出现在大纲树
115
+ return sheet.object(PLACEMENTS_REGISTRY_KEY, { list: t.string('[]') }, { hideFromOutline: true });
116
+ }
117
+ /**
118
+ * 放置时射线检测的目标清单(模块级登记表)。
119
+ * 只有登记过的分组才参与落点检测——ghost 本身、星星、gizmo、相机子树
120
+ * 都不在表里,检测时直接跳过,不用测完再过滤。模型面数越高省得越多。
121
+ * 想让人能把模型摞在已有模型上,就把对应分组登记进来。
122
+ */
123
+ const placementSurfaces = new Set();
124
+ /** 把 ref 指向的分组登记为落点表面,卸载时自动移除 */
125
+ export function usePlacementSurface(ref) {
126
+ useEffect(() => {
127
+ const obj = ref.current;
128
+ if (!obj)
129
+ return;
130
+ placementSurfaces.add(obj);
131
+ return () => {
132
+ placementSurfaces.delete(obj);
133
+ };
134
+ }, [ref]);
135
+ }
136
+ /** 落点表面登记表的只读视图(路径预设落点标记也用这套射线目标) */
137
+ export function getPlacementSurfaces() {
138
+ return placementSurfaces;
139
+ }
140
+ /**
141
+ * 「放置的模型」子集:placementSurfaces 里属于库放置实例的根(背景
142
+ * 场景模型不在此列)。路径预设拖拽吸附用——拖到放置的模型上对到它的
143
+ * 中心,拖到背景/地面保持精确落点
144
+ */
145
+ const placedModelRoots = new Set();
146
+ /** 放置实例登记自己的场景根,卸载时自动移除 */
147
+ function usePlacedModelRoot(ref) {
148
+ useEffect(() => {
149
+ const obj = ref.current;
150
+ if (!obj)
151
+ return;
152
+ placedModelRoots.add(obj);
153
+ return () => {
154
+ placedModelRoots.delete(obj);
155
+ };
156
+ }, [ref]);
157
+ }
158
+ /** 放置模型根集合的只读视图 */
159
+ export function getPlacedModelRoots() {
160
+ return placedModelRoots;
161
+ }
162
+ /** 放置实例登记表:placementKey → 场景实例根(父级绑定的 model: 解析用) */
163
+ const placedInstances = new Map();
164
+ /**
165
+ * 登记/注销放置实例并广播目标登记表变更:持久化的瞄准/父级绑定在
166
+ * 异步实例(模型加载)挂载瞬间即可解析,ShotCamera/Anchors 无需等
167
+ * 无关重渲染(对齐 registerAnchorObject 的广播语义;注销同理——
168
+ * 被瞄准的模型删除后 tracking/看护立即回退,不卡死)。usePlacedInstance
169
+ * 的 hook 语义拆成命令式对,测试可直接断言广播。
170
+ */
171
+ export function registerPlacedInstance(key, instance) {
172
+ placedInstances.set(key, instance);
173
+ notifyTargetRegistryChange();
174
+ }
175
+ export function unregisterPlacedInstance(key) {
176
+ placedInstances.delete(key);
177
+ notifyTargetRegistryChange();
178
+ }
179
+ /** 放置实例登记/注销的便利 hook(PlacedModelCore 用) */
180
+ function usePlacedInstance(key, instance) {
181
+ useEffect(() => {
182
+ if (!instance)
183
+ return;
184
+ registerPlacedInstance(key, instance);
185
+ return () => {
186
+ unregisterPlacedInstance(key);
187
+ };
188
+ }, [key, instance]);
189
+ }
190
+ export function getPlacedInstances() {
191
+ return placedInstances;
192
+ }
193
+ const selectionGizmoTargets = new Map();
194
+ /**
195
+ * 登记表变更通知:SelectionGizmo 只在选中变化时重算附着目标,
196
+ * 但"登记被注销"也要立刻反映(比如路径驱动启用时,正选中的相机
197
+ * 箭头要马上收起来)——登记/注销都通过这里广播
198
+ */
199
+ const gizmoRegistryListeners = new Set();
200
+ /** 订阅登记表变更,返回退订函数 */
201
+ export function onGizmoRegistryChange(listener) {
202
+ gizmoRegistryListeners.add(listener);
203
+ return () => {
204
+ gizmoRegistryListeners.delete(listener);
205
+ };
206
+ }
207
+ function notifyGizmoRegistryChange() {
208
+ gizmoRegistryListeners.forEach((l) => l());
209
+ }
210
+ /** 查某个 theatre 对象的登记项(SelectionGizmo 取附着目标用) */
211
+ export function getGizmoTargetEntry(obj) {
212
+ return selectionGizmoTargets.get(obj);
213
+ }
214
+ /** 登记/注销选中 gizmo 目标;boxHighlight 默认开,返回注销函数 */
215
+ export function registerGizmoTarget(obj, node, opts) {
216
+ selectionGizmoTargets.set(obj, {
217
+ node,
218
+ boxHighlight: opts?.boxHighlight ?? true,
219
+ });
220
+ notifyGizmoRegistryChange();
221
+ return () => {
222
+ selectionGizmoTargets.delete(obj);
223
+ notifyGizmoRegistryChange();
224
+ };
225
+ }
226
+ /**
227
+ * 把 ref 指向的节点登记为选中 gizmo 的附着目标,卸载时自动移除。
228
+ * enabled=false 时不登记:驱动被路径接管的对象(沿路径的相机/瞄准点)
229
+ * 拖了也不生效,箭头干脆不出现(对齐 Blender 约束激活时物体拖不动)
230
+ */
231
+ export function useSelectionGizmoTarget(objRef, nodeRef, enabled = true, opts) {
232
+ useEffect(() => {
233
+ if (!enabled)
234
+ return;
235
+ const obj = objRef.current;
236
+ const node = nodeRef.current;
237
+ if (!obj || !node)
238
+ return;
239
+ return registerGizmoTarget(obj, node, opts);
240
+ // 两个 ref 都是挂载后不再变的;enabled 翻转时注销/重新登记
241
+ }, [enabled]);
242
+ }
243
+ /**
244
+ * 点 gizmo 箭头时射线会穿过箭头打到后面的模型,松手那一下会被误当成
245
+ * "点击选中别的模型"。拖 gizmo 期间和松手后一小段时间内,点选一律忽略。
246
+ */
247
+ let suppressSelectUntil = 0;
248
+ /** 当前这次点击是否该被 gizmo 吞掉(场景里所有点选入口共用一个判断) */
249
+ export function isSelectSuppressed() {
250
+ return performance.now() < suppressSelectUntil;
251
+ }
252
+ /** 拖 gizmo 期间/松手后短暂屏蔽场景点选(路径控制点的手柄也在用) */
253
+ export function suppressSelectBriefly() {
254
+ suppressSelectUntil = performance.now() + 200;
255
+ }
256
+ /** 解析放置清单 JSON;坏数据一律回退空(注册表是底账,勿手改)。
257
+ * 导出给 Agent resolver:无副作用查询在注册表对象已存在时复用同一解析 */
258
+ export function readRegistry(registry) {
259
+ try {
260
+ const list = JSON.parse(registry.value.list);
261
+ return Array.isArray(list) ? list : [];
262
+ }
263
+ catch {
264
+ return [];
265
+ }
266
+ }
267
+ /** 所有已放置模型的落点(给 findFreeSpot 算默认位用) */
268
+ export function readPlacementPositions(sheet) {
269
+ return readRegistry(getRegistry(sheet)).map((e) => e.position);
270
+ }
271
+ /** 读放置清单全部 entry(App 判右键对象是否库来源用)。registry 对象由
272
+ * PlacedModels 挂载时已创建,同 key 同配置的 sheet.object 是安全的重复调用 */
273
+ export function readPlacementEntries(sheet) {
274
+ return readRegistry(getRegistry(sheet));
275
+ }
276
+ /**
277
+ * 放置模型的 theatreKey:顶层扁平化(不再套 "Models /" 文件夹),名字取自
278
+ * 文件名(去扩展名,不安全字符换下划线)。撞名才追加 -2/-3 序号,平时保持
279
+ * 干净(theatreKey 必须唯一,同名模型放两次需要区分)。防撞查两处:放置
280
+ * 清单(注册表已写、对象尚未挂载的空窗) + 全场景路径探测(顶层名字可能与
281
+ * 相机/灯光/演示模型撞,如 "Camera.fbx"、"Thingy.fbx",只查不建)
282
+ */
283
+ export function uniquePlacedModelKey(sheet, label) {
284
+ const base = label.replace(/\.[^.]+$/, '').replace(/[^\w-]/g, '_') || 'model';
285
+ let taken;
286
+ try {
287
+ taken = new Set(readPlacementEntries(sheet).map((e) => e.key));
288
+ }
289
+ catch {
290
+ // 注册表对象尚未创建(理论外场景):按空清单处理,探测兜底防重
291
+ taken = new Set();
292
+ }
293
+ let key = base;
294
+ for (let i = 2; taken.has(key) || sheetObjectExists(sheet, key); i++) {
295
+ key = `${base}-${i}`;
296
+ }
297
+ return key;
298
+ }
299
+ /**
300
+ * 等资产存储就绪:attach 完成前 project.assetStorage 还是占位实现,
301
+ * getAssetUrl 只会返回 "undefined/<id>" 假地址(theatre 官方也要求
302
+ * project.ready 之后再取资产地址)。就绪前渲染已放置模型必然 404。
303
+ */
304
+ function useProjectReady(project) {
305
+ const [ready, setReady] = useState(() => project.isReady);
306
+ useEffect(() => {
307
+ if (ready)
308
+ return;
309
+ let cancelled = false;
310
+ void project.ready.then(() => {
311
+ if (!cancelled)
312
+ setReady(true);
313
+ });
314
+ return () => {
315
+ cancelled = true;
316
+ };
317
+ }, [project, ready]);
318
+ return ready;
319
+ }
320
+ /** 响应式读放置清单:scrub 提交/撤销/同步都会触发 onValuesChange */
321
+ function usePlacementsList(sheet) {
322
+ const registry = useMemo(() => getRegistry(sheet), [sheet]);
323
+ const [list, setList] = useState(() => readRegistry(registry));
324
+ useEffect(() => {
325
+ setList(readRegistry(registry));
326
+ return registry.onValuesChange(() => setList(readRegistry(registry)));
327
+ }, [registry]);
328
+ return list;
329
+ }
330
+ /** 归一化:任意 FBX 单位差异很大,统一缩放到最大边 5 个世界单位,底部对齐落点 */
331
+ function useNormalizedModel(fbx) {
332
+ return useMemo(() => {
333
+ const box = new Box3().setFromObject(fbx);
334
+ const size = box.getSize(new Vector3());
335
+ const maxDim = Math.max(size.x, size.y, size.z);
336
+ return {
337
+ scale: maxDim > 0 ? 5 / maxDim : 1,
338
+ // lift 是模型局部单位,放进 scale 过的组里后底部正好落在 y=0
339
+ lift: -box.min.y,
340
+ };
341
+ }, [fbx]);
342
+ }
343
+ /** ghost:克隆一份(SkinnedMesh 安全),材质全部换成半透明 */
344
+ function makeGhost(fbx) {
345
+ const ghost = SkeletonUtils.clone(fbx);
346
+ ghost.traverse((obj) => {
347
+ const mesh = obj;
348
+ if (!mesh.isMesh)
349
+ return;
350
+ const materials = Array.isArray(mesh.material)
351
+ ? mesh.material
352
+ : [mesh.material];
353
+ const ghosted = materials.map((m) => {
354
+ const c = m.clone();
355
+ c.transparent = true;
356
+ c.opacity = 0.4;
357
+ c.depthWrite = false;
358
+ return c;
359
+ });
360
+ mesh.material = Array.isArray(mesh.material) ? ghosted : ghosted[0];
361
+ });
362
+ return ghost;
363
+ }
364
+ /**
365
+ * 放置状态的控制器:选文件 → 存资产 + 进放置模式;确认 → 先放入 pending
366
+ * (模型立刻渲染),持久化由 PlacedModel 挂载后一次性提交(清单 + source
367
+ * 静态覆盖同一个 scrub,一步撤销);取消/确认后释放 blob URL。
368
+ */
369
+ export function usePlacementController(project, sheet) {
370
+ const [placing, setPlacing] = useState(null);
371
+ const [pendingEntry, setPendingEntry] = useState(null);
372
+ const startPlacing = useCallback(async (file) => {
373
+ // 等 attach 完成:占位存储的 createAsset 会直接抛错
374
+ await project.ready;
375
+ const storage = assetStorageOf(project);
376
+ if (!storage) {
377
+ console.warn('[placement] 没有 studio 资产存储,无法放置');
378
+ return;
379
+ }
380
+ // 重名且内容不同时 createAsset 会弹窗让用户改名;返回 null 即用户取消或失败
381
+ let assetId = null;
382
+ try {
383
+ assetId = await storage.createAsset(file);
384
+ }
385
+ catch (err) {
386
+ console.warn('[placement] 创建资产失败', err);
387
+ return;
388
+ }
389
+ if (!assetId)
390
+ return;
391
+ setPlacing({
392
+ url: URL.createObjectURL(file),
393
+ label: file.name,
394
+ source: { type: 'local', file, assetId },
395
+ });
396
+ }, [project]);
397
+ /** 从资产库选模型进入放置模式:不创建 theatre 项目资产 */
398
+ const libraryGenRef = useRef(0);
399
+ const startPlacingFromLibrary = useCallback(async (entry) => {
400
+ const gen = ++libraryGenRef.current;
401
+ const url = await getLibrary().getBlobUrl(entry.blobId);
402
+ // 用户快速切换选择时,旧加载结果直接丢弃
403
+ if (gen !== libraryGenRef.current)
404
+ return;
405
+ setPlacing({
406
+ url,
407
+ label: entry.name,
408
+ source: { type: 'library', entryId: entry.id },
409
+ });
410
+ }, []);
411
+ const cancelPlacing = useCallback(() => {
412
+ setPlacing((p) => {
413
+ // 本地文件是自己创建的 blob URL,库 URL 由 backend 管理生命周期
414
+ if (p && p.source.type === 'local')
415
+ URL.revokeObjectURL(p.url);
416
+ return null;
417
+ });
418
+ }, []);
419
+ const confirmPlacing = useCallback((point) => {
420
+ if (!placing)
421
+ return;
422
+ const key = uniquePlacedModelKey(sheet, placing.label);
423
+ if (placing.source.type === 'local') {
424
+ setPendingEntry({
425
+ key,
426
+ assetId: placing.source.assetId,
427
+ position: point,
428
+ pending: true,
429
+ });
430
+ }
431
+ else {
432
+ setPendingEntry({
433
+ key,
434
+ libraryEntryId: placing.source.entryId,
435
+ position: point,
436
+ pending: true,
437
+ });
438
+ }
439
+ if (placing.source.type === 'local')
440
+ URL.revokeObjectURL(placing.url);
441
+ setPlacing(null);
442
+ }, [placing, sheet]);
443
+ /**
444
+ * 点击库卡片直接落点:跳过幽灵预览,走与 confirmPlacing 相同的落地管线。
445
+ * 返回 {key, persisted}(2026-10-17,Task 8):Agent models.create 靠
446
+ * persisted 等挂载完成(completion:mounted);waiter 必须先于
447
+ * setPendingEntry 创建,挂载链路的 resolve/reject 都按 key 找它。
448
+ * UI 调用方不 await persisted —— 这里挂 noop catch 兜底,失败告警由
449
+ * PlacedModelErrorBoundary 负责,不产生 unhandled rejection。
450
+ */
451
+ const placeLibraryEntryAt = useCallback((entry, point) => {
452
+ const key = uniquePlacedModelKey(sheet, entry.name);
453
+ const persisted = createPlacementWaiter(key);
454
+ persisted.catch(() => { });
455
+ setPendingEntry({
456
+ key,
457
+ libraryEntryId: entry.id,
458
+ position: point,
459
+ pending: true,
460
+ });
461
+ return { key, persisted };
462
+ }, [sheet]);
463
+ const clearPending = useCallback(() => setPendingEntry(null), []);
464
+ return {
465
+ placing,
466
+ pendingEntry,
467
+ startPlacing,
468
+ startPlacingFromLibrary,
469
+ cancelPlacing,
470
+ confirmPlacing,
471
+ placeLibraryEntryAt,
472
+ clearPending,
473
+ };
474
+ }
475
+ /**
476
+ * 放置模式下的 Canvas 内容:半透明 ghost 跟随光标。
477
+ * 事件走 canvas 的 DOM 监听 + 手动射线检测,不用不可见网格接事件——
478
+ * 视线与地面平行时网格永远打不中,事件根本不触发,看起来就是"没反应"。
479
+ * 落点规则:物体表面优先;y=0 地面(数学平面,无限大)兜底;视线与地面
480
+ * 平行或朝天时再退到"前方 30 个单位、压到地面",保证任何视角都有落点预览。
481
+ */
482
+ export function PlacementRig(props) {
483
+ return props.placing.source.type === 'local' ? (React.createElement(PlacementRigFBX, { ...props })) : (React.createElement(PlacementRigGLTF, { ...props }));
484
+ }
485
+ /** 本地 FBX 预览:用 drei 的 useFBX,加载完交给公共的 Rig 逻辑 */
486
+ function PlacementRigFBX({ placing, onConfirm, onCancel, confirmOnPointerUp, }) {
487
+ const fbx = useFBX(placing.url);
488
+ return (React.createElement(PlacementRigCore, { model: fbx, onConfirm: onConfirm, onCancel: onCancel, confirmOnPointerUp: confirmOnPointerUp }));
489
+ }
490
+ /** 资产库 GLB 预览:用 GLTFLoader 异步加载,失败时交给错误边界 */
491
+ function PlacementRigGLTF({ placing, onConfirm, onCancel, confirmOnPointerUp, }) {
492
+ const [model, setModel] = useState(null);
493
+ const [failed, setFailed] = useState(false);
494
+ useEffect(() => {
495
+ let cancelled = false;
496
+ const loader = createGLTFLoader();
497
+ loader
498
+ .loadAsync(placing.url)
499
+ .then((gltf) => {
500
+ if (!cancelled)
501
+ setModel(gltf.scene);
502
+ })
503
+ .catch((err) => {
504
+ console.warn('[placement] 库模型预览加载失败', err);
505
+ // 资产 URL 不可达/解析失败:上告库 onError 通道(toast 条),
506
+ // 本地状态照走错误边界取消放置,不崩画布
507
+ getLibrary().reportError(new LibraryError('network', '库模型预览加载失败', err));
508
+ if (!cancelled)
509
+ setFailed(true);
510
+ });
511
+ return () => {
512
+ cancelled = true;
513
+ };
514
+ }, [placing.url]);
515
+ // 卸载或 URL 变化时释放模型 GPU 资源
516
+ useEffect(() => {
517
+ return () => {
518
+ if (model)
519
+ disposeObjectTree(model);
520
+ };
521
+ }, [model]);
522
+ if (failed)
523
+ throw new Error('[placement] 库模型预览加载失败');
524
+ if (!model)
525
+ return null;
526
+ return (React.createElement(PlacementRigCore, { model: model, onConfirm: onConfirm, onCancel: onCancel, confirmOnPointerUp: confirmOnPointerUp }));
527
+ }
528
+ /** 放置模式下的 Canvas 内容:半透明 ghost 跟随光标。
529
+ * 事件走 canvas 的 DOM 监听 + 手动射线检测,不用不可见网格接事件——
530
+ * 视线与地面平行时网格永远打不中,事件根本不触发,看起来就是"没反应"。
531
+ * 落点规则:物体表面优先;y=0 地面(数学平面,无限大)兜底;视线与地面
532
+ * 平行或朝天时再退到"前方 30 个单位、压到地面",保证任何视角都有落点预览。 */
533
+ function PlacementRigCore({ model, onConfirm, onCancel, confirmOnPointerUp, }) {
534
+ const invalidate = useThree((s) => s.invalidate);
535
+ const gl = useThree((s) => s.gl);
536
+ const camera = useThree((s) => s.camera);
537
+ const ghost = useMemo(() => makeGhost(model), [model]);
538
+ const { scale, lift } = useNormalizedModel(model);
539
+ const ghostRef = useRef(null);
540
+ const raycaster = useMemo(() => new Raycaster(), []);
541
+ const computePoint = useCallback((clientX, clientY) => computeDropPoint(raycaster, camera, placementSurfaces, gl.domElement.getBoundingClientRect(), clientX, clientY), [gl, camera, raycaster]);
542
+ // 鼠标事件只记坐标并申请一帧,真正的落点计算在帧回调里做一次——
543
+ // pointermove 一秒能来上百次(每次都全量射线检测会很卡),渲染帧一秒最多 60 次
544
+ const pendingMoveRef = useRef(null);
545
+ useFrame(() => {
546
+ const pending = pendingMoveRef.current;
547
+ if (!pending)
548
+ return;
549
+ pendingMoveRef.current = null;
550
+ const p = computePoint(pending.x, pending.y);
551
+ if (ghostRef.current) {
552
+ ghostRef.current.visible = true;
553
+ ghostRef.current.position.copy(p);
554
+ }
555
+ });
556
+ useEffect(() => {
557
+ const el = gl.domElement;
558
+ let downPos = null;
559
+ const onMove = (ev) => {
560
+ pendingMoveRef.current = { x: ev.clientX, y: ev.clientY };
561
+ invalidate();
562
+ };
563
+ const onDown = (ev) => {
564
+ downPos = { x: ev.clientX, y: ev.clientY };
565
+ };
566
+ const onClick = (ev) => {
567
+ if (ev.button !== 0)
568
+ return;
569
+ // 拖拽模式下画布点击不确认,确认只走松手
570
+ if (confirmOnPointerUp)
571
+ return;
572
+ // 拖动后的松手不算点击
573
+ if (downPos &&
574
+ Math.hypot(ev.clientX - downPos.x, ev.clientY - downPos.y) > 2)
575
+ return;
576
+ const p = computePoint(ev.clientX, ev.clientY);
577
+ // 太远的落点直接忽略,留在放置模式里(防止误放到天边)
578
+ if (p.distanceTo(camera.position) > 300)
579
+ return;
580
+ onConfirm({ x: p.x, y: p.y, z: p.z });
581
+ };
582
+ const onKey = (ev) => {
583
+ if (ev.key === 'Escape')
584
+ onCancel();
585
+ };
586
+ const onContextMenu = (ev) => {
587
+ ev.preventDefault();
588
+ onCancel();
589
+ };
590
+ el.addEventListener('pointermove', onMove);
591
+ el.addEventListener('pointerdown', onDown);
592
+ el.addEventListener('click', onClick);
593
+ window.addEventListener('keydown', onKey);
594
+ window.addEventListener('contextmenu', onContextMenu);
595
+ return () => {
596
+ el.removeEventListener('pointermove', onMove);
597
+ el.removeEventListener('pointerdown', onDown);
598
+ el.removeEventListener('click', onClick);
599
+ window.removeEventListener('keydown', onKey);
600
+ window.removeEventListener('contextmenu', onContextMenu);
601
+ };
602
+ }, [
603
+ gl,
604
+ computePoint,
605
+ camera,
606
+ invalidate,
607
+ onConfirm,
608
+ onCancel,
609
+ confirmOnPointerUp,
610
+ ]);
611
+ // 拖拽模式:按下发生在面板卡片上,松手才是确认。松手在画布外(面板区域)
612
+ // = 取消;太远(>300)也取消,防止误放到天边
613
+ useEffect(() => {
614
+ if (!confirmOnPointerUp)
615
+ return;
616
+ const el = gl.domElement;
617
+ const onUp = (ev) => {
618
+ if (ev.button !== 0)
619
+ return;
620
+ // 画布铺满全窗口、垫在面板之下:松手在面板/工具栏等 DOM 上时 target 不是画布,
621
+ // 必须取消(spec:松手在面板区域 = 静默取消)。提示条 pointerEvents:none 不挡
622
+ if (ev.target !== el) {
623
+ onCancel();
624
+ return;
625
+ }
626
+ const p = computePoint(ev.clientX, ev.clientY);
627
+ if (p.distanceTo(camera.position) > 300) {
628
+ onCancel();
629
+ return;
630
+ }
631
+ onConfirm({ x: p.x, y: p.y, z: p.z });
632
+ };
633
+ window.addEventListener('pointerup', onUp);
634
+ return () => window.removeEventListener('pointerup', onUp);
635
+ }, [confirmOnPointerUp, gl, camera, computePoint, onConfirm, onCancel]);
636
+ return (
637
+ // 首次 pointermove 之前不显示,避免在原地闪一下
638
+ React.createElement("group", { ref: ghostRef, scale: scale, visible: false },
639
+ React.createElement("primitive", { object: ghost, position: [0, lift, 0] })));
640
+ }
641
+ /** FBX 解析失败(文件损坏/格式太老)时取消放置,而不是崩掉画布、卡住按钮 */
642
+ export class PlacementRigErrorBoundary extends React.Component {
643
+ constructor() {
644
+ super(...arguments);
645
+ this.state = { failed: false };
646
+ }
647
+ static getDerivedStateFromError() {
648
+ return { failed: true };
649
+ }
650
+ componentDidCatch(err) {
651
+ console.warn('[placement] 模型文件解析失败,已取消放置', err);
652
+ this.props.onCancel();
653
+ }
654
+ render() {
655
+ return this.state.failed ? null : this.props.children;
656
+ }
657
+ }
658
+ /** 已放置的模型们:按清单渲染(外加刚确认、待提交的那条),每个是独立的 theatre 可编辑对象 */
659
+ export function PlacedModels({ sheet, project, pendingEntry, onPersisted, selectLocked, onObjectMenu, }) {
660
+ const list = usePlacementsList(sheet);
661
+ const ready = useProjectReady(project);
662
+ // pending 提交进清单(list)到 clearPending 之间有一瞬两边都渲染,
663
+ // React 报 duplicate key;已在清单里的 pending 副本直接跳过
664
+ const entries = pendingEntry && !list.some((e) => e.key === pendingEntry.key)
665
+ ? [...list, pendingEntry]
666
+ : list;
667
+ if (!ready)
668
+ return null;
669
+ return (React.createElement(React.Fragment, null, entries.map((entry) => (React.createElement(Suspense, { key: entry.key, fallback: null },
670
+ React.createElement(PlacedModelErrorBoundary, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }))))));
671
+ }
672
+ function PlacedModel({ entry, project, sheet, onPersisted, selectLocked, onObjectMenu, }) {
673
+ if (entry.libraryEntryId) {
674
+ return (React.createElement(LibraryPlacedModel, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
675
+ }
676
+ return (React.createElement(FBXPlacedModel, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
677
+ }
678
+ /** 旧本地 FBX 路径:通过 theatre assetStorage 取 URL,再用 useFBX 加载 */
679
+ function FBXPlacedModel({ entry, project, sheet, onPersisted, selectLocked, onObjectMenu, }) {
680
+ const url = useMemo(() => {
681
+ const storage = assetStorageOf(project);
682
+ if (!storage || !entry.assetId)
683
+ throw new Error('asset storage unavailable');
684
+ return storage.getAssetUrl(entry.assetId);
685
+ }, [project, entry.assetId]);
686
+ const fbx = useFBX(url);
687
+ return (React.createElement(PlacedModelCore, { entry: entry, sheet: sheet, group: fbx, animations: fbx.animations, sourceType: "local", onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
688
+ }
689
+ /**
690
+ * 库模型加载:条目解析 → blob URL → GLTFLoader。fetch/解析失败统一包成
691
+ * LibraryError('network') 并经库的 onError 通道上告(宿主错误条提示),
692
+ * 条目缺失是数据问题报 not-found;两种都继续抛给调用方走错误边界,
693
+ * 选择框/画布照常。
694
+ */
695
+ export async function loadLibraryModel(entryId, lib = getLibrary()) {
696
+ try {
697
+ const libEntry = await lib.getEntry(entryId);
698
+ if (!libEntry || libEntry.kind !== 'model') {
699
+ throw new LibraryError('not-found', `库模型条目不存在: ${entryId}`);
700
+ }
701
+ const url = await lib.getBlobUrl(libEntry.blobId);
702
+ const gltf = await createGLTFLoader().loadAsync(url);
703
+ return { group: gltf.scene, animations: gltf.animations };
704
+ }
705
+ catch (err) {
706
+ const wrapped = err instanceof LibraryError
707
+ ? err
708
+ : new LibraryError('network', `库模型加载失败: ${entryId}`, err);
709
+ lib.reportError(wrapped);
710
+ throw wrapped;
711
+ }
712
+ }
713
+ /** 资产库 GLB 路径:解析条目、取 blob URL、用 GLTFLoader 异步加载 */
714
+ function LibraryPlacedModel({ entry, project, sheet, onPersisted, selectLocked, onObjectMenu, }) {
715
+ const [loaded, setLoaded] = useState(null);
716
+ const [failed, setFailed] = useState(false);
717
+ useEffect(() => {
718
+ let cancelled = false;
719
+ loadLibraryModel(entry.libraryEntryId)
720
+ .then((result) => {
721
+ if (!cancelled)
722
+ setLoaded(result);
723
+ })
724
+ .catch((err) => {
725
+ console.warn('[placement] 库模型加载失败', err);
726
+ if (!cancelled)
727
+ setFailed(true);
728
+ });
729
+ return () => {
730
+ cancelled = true;
731
+ };
732
+ }, [entry.libraryEntryId]);
733
+ // 卸载或条目变化时释放已加载模型
734
+ useEffect(() => {
735
+ return () => {
736
+ if (loaded?.group)
737
+ disposeObjectTree(loaded.group);
738
+ };
739
+ }, [loaded?.group]);
740
+ if (failed)
741
+ throw new Error('[placement] 库模型加载失败');
742
+ if (!loaded)
743
+ return null;
744
+ return (React.createElement(PlacedModelCore, { entry: entry, sheet: sheet, group: loaded.group, animations: loaded.animations, sourceType: "library", onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
745
+ }
746
+ /** 已加载 Group 之后的公共逻辑:克隆、归一化、动画 mixer、点选菜单、持久化 */
747
+ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersisted, selectLocked, onObjectMenu, }) {
748
+ const additionalProps = useMemo(() => {
749
+ const modelEntryId = sourceType === 'library' ? entry.libraryEntryId : undefined;
750
+ const props = {
751
+ animation: literalConfigFor('animation', modelEntryId),
752
+ // 姿势 = 时间轴单属性(spec §6.1 + 2026-10-01 属性行化):值
753
+ // JSON(lib 引用|custom 快照),面板显示为属性行,值区域走注册表
754
+ // 的 pose 编辑器(弹框选预设);自定义插值器让它可打帧且平滑混合
755
+ pose: t.string('', posePropConfigShape().opts),
756
+ // 手势(2026-10-05):姿势的纯选择叠加层,左右手各一行;值是库手势
757
+ // 引用(或 ''=无手势),应用只写本侧手指骨,可打帧(同姿势插值器)
758
+ gestureL: t.string('', gesturePropConfigShape('left').opts),
759
+ gestureR: t.string('', gesturePropConfigShape('right').opts),
760
+ // 迁移过渡(spec §9):读取/清理旧值用,面板隐藏;下个大版本删 key
761
+ poseOverride: t.string('', {
762
+ label: () => translate('pg.props.poseOverride'),
763
+ hidden: true,
764
+ }),
765
+ };
766
+ if (sourceType === 'local' && entry.assetId) {
767
+ props.source = fileConfigFor(entry.assetId);
768
+ }
769
+ return props;
770
+ }, [sourceType, entry.assetId, entry.libraryEntryId]);
771
+ const { scale, lift } = useNormalizedModel(group);
772
+ const objRef = useRef(undefined);
773
+ const [sheetObject, setSheetObject] = useState(undefined);
774
+ // Agent 运行时登记(2026-10-17,Task 8):objRef 填充即「运行时 SheetObject
775
+ // 已挂载」信号,models.create 的 resolver 复核与 scene/objects 查询都读
776
+ // 这份登记;清空/卸载时按 id + 同一对象清理,防过期 cleanup 误删新代次
777
+ const setObjRef = useCallback((obj) => {
778
+ const id = formatSceneObjectId('model', entry.key);
779
+ const prev = objRef.current;
780
+ objRef.current = obj ?? undefined;
781
+ setSheetObject(obj ?? undefined);
782
+ if (obj) {
783
+ registerSceneSheetObject(id, obj);
784
+ }
785
+ else {
786
+ unregisterSceneSheetObject(id, prev);
787
+ }
788
+ }, [entry.key]);
789
+ // 登记为落点表面:之后的放置可以把模型摞到这个模型上
790
+ const groupRef = useRef(null);
791
+ usePlacementSurface(groupRef);
792
+ // 同时登记进「放置的模型」子集:路径拖拽吸附只认放置实例,不认背景
793
+ usePlacedModelRoot(groupRef);
794
+ // 登记为选中 gizmo 的附着目标:被选中时身上挂三轴移动箭头
795
+ useSelectionGizmoTarget(objRef, groupRef);
796
+ const invalidate = useThree((s) => s.invalidate);
797
+ // 主画面点选:和 App 里 Model 的 selectThingy 同款。放置模式下点击是
798
+ // 确认落点,不选中、不拦截;点 gizmo 箭头穿透过来的误点也要吞掉。
799
+ const selectSelf = (event) => {
800
+ if (selectLocked || isSelectSuppressed())
801
+ return;
802
+ // 摆姿势期间单击(自己或别的模型)都不改选中;stopPropagation 保持
803
+ // "点击被模型处理"的语义,onPointerMissed 不会触发
804
+ if (getPoseSession()) {
805
+ event.stopPropagation();
806
+ return;
807
+ }
808
+ event.stopPropagation();
809
+ const studio = getStudioSync();
810
+ if (studio && objRef.current) {
811
+ studio.setSelection([objRef.current]);
812
+ }
813
+ };
814
+ // 右键:选中自己 + 弹操作菜单(切换手柄模式/删除)。
815
+ // 放置模式下右键是取消放置(PlacementRig 的监听),这里不抢。
816
+ const openMenu = (event) => {
817
+ if (selectLocked || !onObjectMenu)
818
+ return;
819
+ event.stopPropagation();
820
+ event.nativeEvent.preventDefault();
821
+ const studio = getStudioSync();
822
+ if (studio && objRef.current) {
823
+ studio.setSelection([objRef.current]);
824
+ }
825
+ onObjectMenu(entry.key, event.nativeEvent.clientX, event.nativeEvent.clientY);
826
+ };
827
+ // 双击已选中的人形模型进入姿势模式。第一件事记录 timeStamp:
828
+ // App 的"双击空白退出"靠它区分落在模型上的双击。
829
+ const enterPoseViaDoubleClick = (event) => {
830
+ noteModelDoubleClick(event.nativeEvent.timeStamp);
831
+ if (selectLocked || isSelectSuppressed())
832
+ return;
833
+ const studio = getStudioSync();
834
+ if (!studio || !objRef.current)
835
+ return;
836
+ // 未选中不进:双击的第一击已由 selectSelf 选中,第二击这里自然满足
837
+ if (!studio.selection.includes(objRef.current))
838
+ return;
839
+ if (!resolveControlSpec(instance))
840
+ return; // 骨架不支持:静默不进
841
+ event.stopPropagation();
842
+ enterPoseSession(entry.key);
843
+ };
844
+ // 骨架动画:加载结果按 URL 缓存,同一文件放多个会共享同一份骨架,
845
+ // 互相打架(多个 <primitive> 挂同一个 Object3D 也只有最后一个能显示)。
846
+ // 所以每个实例克隆一份(SkinnedMesh 安全),配独立的 AnimationMixer。
847
+ const instance = useMemo(() => SkeletonUtils.clone(group), [group]);
848
+ const mixer = useMemo(() => new AnimationMixer(instance), [instance]);
849
+ // 登记进 placedInstances 表:锚点父级绑定的 model: 解析用
850
+ usePlacedInstance(entry.key, instance);
851
+ const bindPoseRef = useRef({});
852
+ // 实例刚克隆出来时同步捕获绑定姿势,确保后续姿势应用 effect 运行前已就位
853
+ useLayoutEffect(() => {
854
+ bindPoseRef.current = captureBindPose(instance);
855
+ }, [instance]);
856
+ const animationId = useVal(sheetObject?.props.animation) ?? '';
857
+ const poseJson = useVal(sheetObject?.props.pose) ?? '';
858
+ const gestureLJson = useVal(sheetObject?.props.gestureL) ?? '';
859
+ const gestureRJson = useVal(sheetObject?.props.gestureR) ?? '';
860
+ const poseSessionKey = usePoseSessionKey();
861
+ // 本模型正处于姿势模式会话:期间骨骼归 IK 骨架每帧重解算
862
+ const poseModeActive = poseSessionKey === entry.key;
863
+ const [clip, setClip] = useState(null);
864
+ const currentActionRef = useRef(null);
865
+ const pendingUncachesRef = useRef([]);
866
+ // 登记当前 theatre 对象与 PlacementEntry 的映射,App 用 selection 反查 rig;
867
+ // 同时保留场景实例,供资产库面板统计引用与 Task 6 抽帧。
868
+ // 骨骼锚点:按实例骨架登记头/胸口/双手/双腿(运行时,非 theatre 对象),
869
+ // 登记/注销都广播目标登记表——持久化的瞄准/父级绑定在模型加载完、
870
+ // 或模型被删的瞬间立即生效/回退,ShotCamera 与 Anchors 订阅见各自组件。
871
+ // deps 只挂 entry.key:注册表 list 任何增删都会 parse 出全新 entry 身份,
872
+ // 挂 entry 本体等于"增删任一模型重建全部骨骼锚点节点",已绑在骨骼锚点
873
+ // 上的锚点网格会随之脱离场景树;entry 内容放置后不可变,同 key 内容恒等。
874
+ useEffect(() => {
875
+ if (!sheetObject)
876
+ return;
877
+ placementObjects.set(sheetObject, { entry, instance });
878
+ registerBoneAnchors(entry.key, instance);
879
+ notifyTargetRegistryChange();
880
+ return () => {
881
+ placementObjects.delete(sheetObject);
882
+ unregisterBoneAnchors(entry.key);
883
+ notifyTargetRegistryChange();
884
+ };
885
+ }, [sheetObject, entry.key, instance]);
886
+ // animation 属性变化时加载库动画 GLB。加载状态登记进
887
+ // agent/runtime/animationStatus(animations.getStatus 的唯一数据源,
888
+ // 不从 props 猜测):开始=loaded:false、成功=loaded:true、失败=error;
889
+ // cleanup(卸载或 animationId 切换)必须删 key,不留陈旧 loaded。
890
+ useEffect(() => {
891
+ if (!animationId) {
892
+ setClip(null);
893
+ return;
894
+ }
895
+ let cancelled = false;
896
+ setAnimationLoadStatus(entry.key, { assetId: animationId, loaded: false });
897
+ const load = async () => {
898
+ const animEntry = await getLibrary().getEntry(animationId);
899
+ if (!animEntry || animEntry.kind !== 'animation') {
900
+ if (!cancelled) {
901
+ setClip(null);
902
+ setAnimationLoadStatus(entry.key, {
903
+ assetId: animationId,
904
+ loaded: false,
905
+ error: {
906
+ code: 'RESOURCE_LOAD_FAILED',
907
+ message: `动画资产不存在或类型不符: ${animationId}`,
908
+ retryable: false,
909
+ },
910
+ });
911
+ }
912
+ return;
913
+ }
914
+ const url = await getLibrary().getBlobUrl(animEntry.blobId);
915
+ const gltf = await createGLTFLoader().loadAsync(url);
916
+ if (!cancelled) {
917
+ setClip(gltf.animations[0] ?? null);
918
+ setAnimationLoadStatus(entry.key, { assetId: animationId, loaded: true });
919
+ }
920
+ };
921
+ load().catch((err) => {
922
+ console.warn('[placement] 动画加载失败', err);
923
+ if (!cancelled) {
924
+ setClip(null);
925
+ setAnimationLoadStatus(entry.key, {
926
+ assetId: animationId,
927
+ loaded: false,
928
+ error: {
929
+ code: 'RESOURCE_LOAD_FAILED',
930
+ message: err instanceof Error ? err.message : String(err),
931
+ retryable: true,
932
+ },
933
+ });
934
+ }
935
+ });
936
+ return () => {
937
+ cancelled = true;
938
+ clearAnimationLoadStatus(entry.key);
939
+ };
940
+ }, [animationId, entry.key]);
941
+ const [libResolveTick, setLibResolveTick] = useState(0);
942
+ // 旧两字段 → 新 pose 值迁移(spec §9):一个事务(可撤销、经 saaz 同步),
943
+ // 幂等——新值已存在或旧值双空即不触发;迁移后旧 override unset 即天然标记
944
+ useEffect(() => {
945
+ if (!sheetObject)
946
+ return;
947
+ const legacyPose = val(sheetObject.props.pose) ?? '';
948
+ const legacyOverride = val(sheetObject.props.poseOverride) ?? '';
949
+ const migrated = computeLegacyPoseMigration({
950
+ pose: legacyPose,
951
+ poseOverride: legacyOverride,
952
+ });
953
+ // 至多一次(spec §9.2):override 空=天然标记;裸 id 迁移靠「结果与现值相同」
954
+ // 判定——已迁移过的对象两个开关都假,不再开事务(否则每次挂载重复同值写,
955
+ // 空撤销步/saaz 噪音)
956
+ const needsSet = migrated !== null && migrated !== legacyPose;
957
+ const needsUnset = !!legacyOverride.trim();
958
+ if (!needsSet && !needsUnset)
959
+ return;
960
+ const studio = getStudioSync();
961
+ if (!studio)
962
+ return;
963
+ studio.transaction(({ set, unset }) => {
964
+ if (needsSet && migrated !== null)
965
+ set(sheetObject.props.pose, migrated);
966
+ if (needsUnset)
967
+ unset(sheetObject.props.poseOverride);
968
+ });
969
+ }, [sheetObject]);
970
+ const poseBones = useMemo(() => resolvePoseBones(parsePoseValue(poseJson)),
971
+ // libResolveTick:库引用异步解析到手后 onReady 自增,静止态下逼 memo 重算
972
+ // (r3f invalidate 只排帧不重渲染 React,spec §5 的「重求值」需要这一跳)
973
+ [poseJson, libResolveTick]);
974
+ // 手势层(叠加):本侧手指骨;解析不出 = null(跳过应用,保持姿势层)
975
+ const gestureLBones = useMemo(() => resolveGestureBones(gestureLJson, 'left'), [gestureLJson, libResolveTick]);
976
+ const gestureRBones = useMemo(() => resolveGestureBones(gestureRJson, 'right'), [gestureRJson, libResolveTick]);
977
+ // 库姿势引用预取(spec §5):当前值与轨道全部帧值里的 lib id 异步进缓存,
978
+ // 到手 invalidate 触发重求值,阶跃变平滑
979
+ useEffect(() => {
980
+ if (!sheetObject)
981
+ return;
982
+ const ids = collectLibPoseIdsForProps(sheetObject, [
983
+ 'pose',
984
+ 'gestureL',
985
+ 'gestureR',
986
+ ]);
987
+ if (ids.length === 0)
988
+ return;
989
+ let cancelled = false;
990
+ prefetchLibPoses(ids, () => {
991
+ if (cancelled)
992
+ return;
993
+ setLibResolveTick((t) => t + 1);
994
+ invalidate();
995
+ });
996
+ return () => {
997
+ cancelled = true;
998
+ };
999
+ }, [sheetObject, poseJson, gestureLJson, gestureRJson, invalidate]);
1000
+ // 动画片段切换:0.2s 淡入淡出
1001
+ useEffect(() => {
1002
+ if (!clip) {
1003
+ const oldAction = currentActionRef.current;
1004
+ if (oldAction) {
1005
+ const oldClip = oldAction.getClip();
1006
+ oldAction.fadeOut(0.2);
1007
+ // 淡出结束再 uncache;立即 uncache 会中断淡出插值、骨骼瞬间回弹
1008
+ pendingUncachesRef.current.push(setTimeout(() => {
1009
+ const act = mixer.existingAction(oldClip, instance);
1010
+ if (act && act.isRunning())
1011
+ return;
1012
+ mixer.uncacheAction(oldClip, instance);
1013
+ }, 250));
1014
+ currentActionRef.current = null;
1015
+ }
1016
+ return;
1017
+ }
1018
+ const oldAction = currentActionRef.current;
1019
+ const oldClip = oldAction?.getClip();
1020
+ const action = mixer.clipAction(clip);
1021
+ action.reset().fadeIn(0.2).play();
1022
+ if (oldAction && oldAction !== action) {
1023
+ oldAction.fadeOut(0.2);
1024
+ if (oldClip) {
1025
+ // 同上:等淡出完成再清缓存,避免打断交叉淡化
1026
+ pendingUncachesRef.current.push(setTimeout(() => {
1027
+ const act = mixer.existingAction(oldClip, instance);
1028
+ if (act && act.isRunning())
1029
+ return;
1030
+ mixer.uncacheAction(oldClip, instance);
1031
+ }, 250));
1032
+ }
1033
+ }
1034
+ currentActionRef.current = action;
1035
+ }, [clip, mixer, instance]);
1036
+ useEffect(() => {
1037
+ return () => {
1038
+ for (const t of pendingUncachesRef.current)
1039
+ clearTimeout(t);
1040
+ pendingUncachesRef.current = [];
1041
+ mixer.stopAllAction();
1042
+ mixer.uncacheRoot(instance);
1043
+ disposeObjectTree(instance);
1044
+ };
1045
+ }, [mixer, instance]);
1046
+ // 只在 theatre 序列播放时驱动动画:编辑/摆姿势时模型保持静止。
1047
+ // demand 帧循环下没有持续帧,播放状态翻成 true 时主动起一帧,
1048
+ // 之后 useFrame 里边播边续帧;暂停的那帧起不再 invalidate,画面静止。
1049
+ // 序列停止时把当前姿势(或绑定姿势)写回骨骼;播放时由 clip effect + useFrame 驱动,
1050
+ // animation 为空时 mixer 不更新,骨骼保持停止前的姿势,无需额外处理。
1051
+ // 旧的 action fadeOut 残余权重在停止后直接由 applyPose 覆盖,不必 stopAllAction。
1052
+ useEffect(() => {
1053
+ return onChange(sheet.sequence.pointer.playing, (playing) => {
1054
+ if (playing) {
1055
+ invalidate();
1056
+ }
1057
+ else {
1058
+ applyPose(instance, poseBones ?? bindPoseRef.current);
1059
+ // 手势叠加层:姿势层整写之后,只补本侧手指骨(顺序不能反)
1060
+ if (gestureLBones)
1061
+ applyPose(instance, gestureLBones);
1062
+ if (gestureRBones)
1063
+ applyPose(instance, gestureRBones);
1064
+ invalidate();
1065
+ }
1066
+ });
1067
+ }, [sheet, invalidate, instance, poseBones, gestureLBones, gestureRBones]);
1068
+ // pose 数据变化且当前未播放时立即应用,覆盖 pose 值切换与撤销/重做路径。
1069
+ // poseModeActive 进 deps:姿势模式期间不抢骨骼(IK 骨架每帧重解算,应用了
1070
+ // 也会被压回);会话退出(true→false)时本 effect 重跑,把当前值补刷回骨骼——
1071
+ // 修「会话期间值被弹框/撤销/同步改掉,Esc 后模型停在旧姿势、再点同值因
1072
+ // 值没变不重刷而永久卡死」的脱节。
1073
+ useEffect(() => {
1074
+ if (!shouldApplyPoseWhenIdle(val(sheet.sequence.pointer.playing), poseModeActive))
1075
+ return;
1076
+ applyPose(instance, poseBones ?? bindPoseRef.current);
1077
+ // 手势叠加层:姿势层整写之后,只补本侧手指骨(顺序不能反)
1078
+ if (gestureLBones)
1079
+ applyPose(instance, gestureLBones);
1080
+ if (gestureRBones)
1081
+ applyPose(instance, gestureRBones);
1082
+ invalidate();
1083
+ }, [
1084
+ instance,
1085
+ poseBones,
1086
+ gestureLBones,
1087
+ gestureRBones,
1088
+ invalidate,
1089
+ sheet,
1090
+ poseModeActive,
1091
+ ]);
1092
+ useFrame((_, delta) => {
1093
+ if (!val(sheet.sequence.pointer.playing))
1094
+ return;
1095
+ if (!sheetObject)
1096
+ return;
1097
+ mixer.update(delta);
1098
+ // 有姿势/手势轨道:求值姿势逐骨压过动作片段(spec §7.1);
1099
+ // 每帧重读求值值(拖针/播放求值产物),resolvePoseBones 有 LRU/缓存兜底。
1100
+ // 轨道有无也现场读——两步撤销首帧(撤关键帧→撤空轨)删轨不改求值值,
1101
+ // 缓存的布尔会滞留 true 让播放被绑定姿势盖住(终审 F2)
1102
+ const hasPoseTrack = getPoseTrackId(sheetObject) !== undefined;
1103
+ const hasGestureLTrack = getTrackIdForProp(sheetObject, 'gestureL') !== undefined;
1104
+ const hasGestureRTrack = getTrackIdForProp(sheetObject, 'gestureR') !== undefined;
1105
+ if (shouldApplyPoseDuringPlayback(hasPoseTrack || hasGestureLTrack || hasGestureRTrack)) {
1106
+ if (hasPoseTrack) {
1107
+ const evaluated = val(sheetObject.props.pose) ?? '';
1108
+ applyPose(instance, resolvePoseBones(parsePoseValue(evaluated)) ?? bindPoseRef.current);
1109
+ }
1110
+ // 手势轨道:求值值裁到本侧手指骨压上;无姿势轨道时身体仍归 mixer
1111
+ if (hasGestureLTrack) {
1112
+ const g = resolveGestureBones(val(sheetObject.props.gestureL) ?? '', 'left');
1113
+ if (g)
1114
+ applyPose(instance, g);
1115
+ }
1116
+ if (hasGestureRTrack) {
1117
+ const g = resolveGestureBones(val(sheetObject.props.gestureR) ?? '', 'right');
1118
+ if (g)
1119
+ applyPose(instance, g);
1120
+ }
1121
+ }
1122
+ invalidate();
1123
+ });
1124
+ // pending 条目:对象已由 e.group 建好后,一次性提交清单。
1125
+ // 本地来源额外写 source 静态覆盖,让 theatre 项目状态引用到资产(避免被清理);
1126
+ // 库来源不写 source,清单里的 libraryEntryId 足够恢复。
1127
+ const isPending = 'pending' in entry && entry.pending;
1128
+ useEffect(() => {
1129
+ if (!isPending)
1130
+ return;
1131
+ const obj = objRef.current;
1132
+ const studio = getStudioSync();
1133
+ if (obj && studio) {
1134
+ const registry = getRegistry(sheet);
1135
+ const list = readRegistry(registry);
1136
+ // 已在清单里 = 重做/同步/StrictMode 二次触发,不重复提交
1137
+ if (!list.some((e) => e.key === entry.key)) {
1138
+ const scrub = studio.scrub();
1139
+ scrub.capture(({ set }) => {
1140
+ const { pending: _pending, ...rest } = entry;
1141
+ set(registry.props.list, JSON.stringify([...list, rest]));
1142
+ if (sourceType === 'local' && entry.assetId) {
1143
+ set(obj.props.source, {
1144
+ type: 'file',
1145
+ id: entry.assetId,
1146
+ });
1147
+ }
1148
+ });
1149
+ scrub.commit();
1150
+ onPersisted();
1151
+ // Agent models.create 的 mounted 信号(Task 8):注册表提交 +
1152
+ // pending 清理之后才兑现;waiter 不存在(UI 路径)时是 no-op
1153
+ resolvePlacementPersisted(entry.key);
1154
+ }
1155
+ }
1156
+ // pending 条目未持久化即卸载(场景拆解等):拒绝 waiter,Agent 侧
1157
+ // 不悬挂;正常路径 waiter 已 settle 并从 Map 删除,这里是 no-op
1158
+ return () => {
1159
+ rejectPlacement(entry.key, new Error(`[placement] 模型 ${entry.key} 未持久化即卸载`));
1160
+ };
1161
+ }, []);
1162
+ return (React.createElement(e.group, { ref: groupRef, theatreKey: entry.key, objRef: setObjRef, additionalProps: additionalProps, position: [entry.position.x, entry.position.y, entry.position.z], scale: scale, onClick: selectSelf, onContextMenu: openMenu, onDoubleClick: enterPoseViaDoubleClick },
1163
+ React.createElement("primitive", { object: instance, position: [0, lift, 0] }),
1164
+ sheetObject && poseModeActive && (React.createElement(React.Fragment, null,
1165
+ React.createElement(PoseModeRig, { instance: instance, sheetObject: sheetObject, placementKey: entry.key, sheet: sheet }),
1166
+ React.createElement(PoseModeBar, { instance: instance })))));
1167
+ }
1168
+ /** 本地缺资产(另一个客户端同步来的清单)或 FBX 解析失败时,跳过该模型而不是崩掉画布 */
1169
+ class PlacedModelErrorBoundary extends React.Component {
1170
+ constructor() {
1171
+ super(...arguments);
1172
+ this.state = { failed: false };
1173
+ }
1174
+ static getDerivedStateFromError() {
1175
+ return { failed: true };
1176
+ }
1177
+ componentDidCatch(err) {
1178
+ console.warn(`[placement] 模型 ${this.props.entry.key} 加载失败`, err);
1179
+ // Agent models.create 正等着挂载完成(Task 8):失败必须 reject waiter,
1180
+ // 不能悬挂;UI 路径没有 waiter,rejectPlacement 是 no-op
1181
+ rejectPlacement(this.props.entry.key, err);
1182
+ }
1183
+ render() {
1184
+ return this.state.failed ? null : React.createElement(PlacedModel, { ...this.props });
1185
+ }
1186
+ }
1187
+ /**
1188
+ * 快捷键切换 gizmo 模式(对齐 Blender):W 选择 / G 移动 / R 旋转 / S 缩放。
1189
+ * 有选中对象时才响应;焦点在输入框时不抢按键(和 studio 快捷键同一套避让)。
1190
+ * G 另有模态抓取(选中对象跟随鼠标,见 SelectionGizmo);W/R/S 只切模式。
1191
+ * 返回 [当前模式, 设置模式]——顶栏模式组和右键菜单都用设置接口切模式。
1192
+ */
1193
+ export function useGizmoMode() {
1194
+ const [mode, setMode] = useState('select');
1195
+ useEffect(() => {
1196
+ const onKey = (ev) => {
1197
+ const target = ev.target;
1198
+ if (target &&
1199
+ (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA'))
1200
+ return;
1201
+ // 姿势模式期间 W/E 归 threeIK 操纵器,G/R/S 不响应
1202
+ if (getPoseSession())
1203
+ return;
1204
+ const next = ev.code === 'KeyW'
1205
+ ? 'select'
1206
+ : ev.code === 'KeyG'
1207
+ ? 'translate'
1208
+ : ev.code === 'KeyR'
1209
+ ? 'rotate'
1210
+ : ev.code === 'KeyS'
1211
+ ? 'scale'
1212
+ : null;
1213
+ if (!next)
1214
+ return;
1215
+ const studio = getStudioSync();
1216
+ if (!studio || studio.selection.length === 0)
1217
+ return;
1218
+ setMode(next);
1219
+ };
1220
+ window.addEventListener('keydown', onKey);
1221
+ return () => window.removeEventListener('keydown', onKey);
1222
+ }, []);
1223
+ return [mode, setMode];
1224
+ }
1225
+ /**
1226
+ * 从放置清单里删除一个模型:单个事务完成 forgetObject + 清单移除 =
1227
+ * 一步撤销(2026-10-19,Task 14 合并;原先只退清单)。
1228
+ * forgetObject 按地址清持久数据(变换/姿势/动画覆盖与轨道,同步到所有
1229
+ * 客户端),撤销一步把清单行和覆盖一起还原;不再残留"同名重放复活旧
1230
+ * 变换"的幽灵数据。theatre 对象本身删不掉(公开 API 没有删除),会留个
1231
+ * 空壳在大纲里,刷新后消失——和撤销放置的已知行为一样。FBX 文件本体
1232
+ * 留在 IndexedDB。
1233
+ */
1234
+ export function deletePlacement(sheet, key) {
1235
+ const studio = getStudio();
1236
+ if (!studio)
1237
+ return;
1238
+ const registry = getRegistry(sheet);
1239
+ const list = readRegistry(registry).filter((e) => e.key !== key);
1240
+ studio.transaction(({ set, stateEditors }) => {
1241
+ stateEditors.coreByProject.historic.sheetsById.forgetObject({
1242
+ projectId: sheet.address.projectId,
1243
+ sheetId: sheet.address.sheetId,
1244
+ // 业务 key 跨适配层按不透明字符串传递,此处回到 core 的名义类型
1245
+ objectKey: key,
1246
+ });
1247
+ set(registry.props.list, JSON.stringify(list));
1248
+ });
1249
+ }
1250
+ /**
1251
+ * 给 three-stdlib 版缩放手柄补一个居中的等比缩放块。drei 封装的这个
1252
+ * 旧版手柄只有单轴/双轴和轴尖白块(XYZX/Y/Z,太小发现不了);它的拾取
1253
+ * (axis = 拾取块 name)和拖动(axis 含 'XYZ' 即三轴同系数)逻辑原生支持
1254
+ * 等比,所以往 gizmo/picker 的 scale 分组各加一个 name='XYZ' 的块即可,
1255
+ * 拖动写回/撤销仍走 SelectionGizmo 现有链路。ref 回调每次渲染重挂,
1256
+ * 这里按 name 查重保证幂等。依赖 three-stdlib 内部分组结构,升级
1257
+ * drei/three-stdlib 时需复核。
1258
+ */
1259
+ function ensureUniformScaleHandle(controls) {
1260
+ // controls.gizmo 是 TransformControlsGizmo,可视/拾取分组挂在它下面
1261
+ const gizmoScale = controls?.gizmo?.gizmo?.scale;
1262
+ const pickerScale = controls?.gizmo?.picker?.scale;
1263
+ if (!gizmoScale || !pickerScale)
1264
+ return;
1265
+ if (gizmoScale.children.some((child) => child.name === 'XYZ'))
1266
+ return;
1267
+ const visual = new Mesh(new BoxGeometry(0.16, 0.16, 0.16), new MeshBasicMaterial({
1268
+ color: 0xffffff,
1269
+ transparent: true,
1270
+ opacity: 0.35,
1271
+ depthTest: false,
1272
+ depthWrite: false,
1273
+ toneMapped: false,
1274
+ }));
1275
+ visual.name = 'XYZ';
1276
+ visual.renderOrder = Infinity;
1277
+ const hit = new Mesh(new BoxGeometry(0.26, 0.26, 0.26), new MeshBasicMaterial({ visible: false }));
1278
+ hit.name = 'XYZ';
1279
+ gizmoScale.add(visual);
1280
+ pickerScale.add(hit);
1281
+ }
1282
+ /**
1283
+ * 选中对象的操作手柄:studio 选中状态里出现登记过的对象时,
1284
+ * 在它的节点上挂手柄(移动三轴/旋转圆环/缩放方块,由 mode 决定);
1285
+ * 取消选中或选中别的,手柄消失。
1286
+ * (登记见 useSelectionGizmoTarget:已放置的模型、Thingy 都在表里)
1287
+ *
1288
+ * 拖动写回和拖相机同一套:按下开一个 scrub,拖动中逐帧 capture
1289
+ * (theatre 状态实时更新,属性面板/同步方都能看到),松手一次 commit
1290
+ * ——一次拖动 = 一步撤销。轨道控制器是 makeDefault,drei 的
1291
+ * TransformControls 拖动期间会自动禁用它,视角不会跟着转。
1292
+ */
1293
+ export function SelectionGizmo({ hidden, mode, }) {
1294
+ const [target, setTarget] = useState(null);
1295
+ const invalidate = useThree((s) => s.invalidate);
1296
+ const camera = useThree((s) => s.camera);
1297
+ const gl = useThree((s) => s.gl);
1298
+ const scrubRef = useRef(undefined);
1299
+ const controlsRef = useRef(null);
1300
+ // 中心等比块的拖动修正:three-stdlib 对 axis==='XYZ' 用
1301
+ // |pointEnd|/|pointStart| 算系数,抓点离手柄中心越近分母越小,轻轻一拖
1302
+ // 就是几百倍。这里在写回前改用屏幕空间径向映射:以抓手处相对手柄中心
1303
+ // 屏幕点的方向为轴,沿轴拖 250px ≈ 2 倍,可预期、不炸。
1304
+ const uniDragRef = useRef(null);
1305
+ const lastPointerRef = useRef({ x: 0, y: 0 });
1306
+ useEffect(() => {
1307
+ const onMove = (ev) => {
1308
+ lastPointerRef.current = { x: ev.clientX, y: ev.clientY };
1309
+ };
1310
+ window.addEventListener('pointermove', onMove);
1311
+ return () => window.removeEventListener('pointermove', onMove);
1312
+ }, []);
1313
+ // G 键模态抓取(Blender):选中对象跟随鼠标(过起点、正对相机的平面),
1314
+ // X/Y/Z 锁轴,左键/回车确认,右键/Esc 废弃弹回;和路径控制点共用一套
1315
+ const { start: startGrab, active: grabActive } = useModalGrab();
1316
+ const poseKey = usePoseSessionKey();
1317
+ // 姿势模式期间:被摆模型的三轴手柄让位给 threeIK 操纵器(描边保留——只取
1318
+ // 模型子树,控制点不在其中,互不干扰)
1319
+ const isPosingTarget = !!poseKey &&
1320
+ !!target &&
1321
+ getPlacementEntryForObject(target.obj)?.key === poseKey;
1322
+ useEffect(() => {
1323
+ const studio = getStudioSync();
1324
+ if (!studio)
1325
+ return;
1326
+ const sync = () => {
1327
+ const selected = studio.selection.find((item) => getGizmoTargetEntry(item));
1328
+ const entry = selected && getGizmoTargetEntry(selected);
1329
+ setTarget(selected && entry
1330
+ ? { obj: selected, node: entry.node, boxHighlight: entry.boxHighlight }
1331
+ : null);
1332
+ invalidate();
1333
+ };
1334
+ sync();
1335
+ // 选中变化、登记表变化(路径驱动启用把相机注销了等)都重算附着目标
1336
+ const offRegistry = onGizmoRegistryChange(sync);
1337
+ const untapSelection = studio.onSelectionChange(sync);
1338
+ return () => {
1339
+ offRegistry();
1340
+ untapSelection();
1341
+ };
1342
+ }, [invalidate]);
1343
+ // G:对当前选中对象起模态抓取(写回 position;旋转/缩放的模态版暂无,
1344
+ // R/S 只切模式)。抓取平面/写回都是世界坐标,落属性时换算回节点本地
1345
+ useEffect(() => {
1346
+ if (hidden || !target)
1347
+ return;
1348
+ const onKey = (ev) => {
1349
+ if (ev.code !== 'KeyG')
1350
+ return;
1351
+ if (getPoseSession())
1352
+ return;
1353
+ const el = ev.target;
1354
+ if (el &&
1355
+ (el.tagName === 'INPUT' ||
1356
+ el.tagName === 'TEXTAREA' ||
1357
+ el.isContentEditable))
1358
+ return;
1359
+ // 箭头拖动中途不抢
1360
+ if (scrubRef.current)
1361
+ return;
1362
+ const { obj, node } = target;
1363
+ startGrab({
1364
+ startPos: node.getWorldPosition(new Vector3()),
1365
+ dragging: false,
1366
+ capture: (scrub, worldPos) => {
1367
+ const local = worldPos.clone();
1368
+ const parent = node.parent;
1369
+ if (parent) {
1370
+ parent.updateWorldMatrix(true, false);
1371
+ parent.worldToLocal(local);
1372
+ }
1373
+ scrub.capture(({ set }) => set(obj.props.position, { x: local.x, y: local.y, z: local.z }));
1374
+ },
1375
+ });
1376
+ };
1377
+ window.addEventListener('keydown', onKey);
1378
+ return () => window.removeEventListener('keydown', onKey);
1379
+ }, [hidden, target, startGrab]);
1380
+ // 附着节点被移出场景(对象被删/卸载)时立刻摘掉手柄:否则 React
1381
+ // 重渲染前的空窗期里,helper 每帧更新都会撞 three 的
1382
+ // "The attached 3D object must be a part of the scene graph" 报错
1383
+ // (删选中的相机必现一次)。three 的 'removed' 事件和节点移除同步,
1384
+ // 比等选中通知→setState→重渲染快一个帧
1385
+ useEffect(() => {
1386
+ if (!target)
1387
+ return;
1388
+ const node = target.node;
1389
+ const detach = () => {
1390
+ controlsRef.current?.detach?.();
1391
+ setTarget((t) => (t && t.node === node ? null : t));
1392
+ };
1393
+ node.addEventListener('removed', detach);
1394
+ return () => node.removeEventListener('removed', detach);
1395
+ }, [target]);
1396
+ // 卸载兜底:对象被撤销掉(节点已不在场景里)时不挂手柄;
1397
+ // 模态抓取期间也不挂:两套拖动不撞车(抓取结束后自动回来);
1398
+ // 选择模式(W)只点选不挂手柄(Blender 的 Select 工具)
1399
+ const showControls = !hidden &&
1400
+ !!target &&
1401
+ !!target.node.parent &&
1402
+ !grabActive &&
1403
+ mode !== 'select' &&
1404
+ !isPosingTarget;
1405
+ // 选中轮廓:模式无关(选择模式下它是唯一的选中提示),抓取期间也跟随
1406
+ const showOutline = !hidden && !!target && !!target.node.parent && target.boxHighlight;
1407
+ if (!target)
1408
+ return null;
1409
+ return (React.createElement(React.Fragment, null,
1410
+ showOutline && React.createElement(SelectionOutlineHighlight, { node: target.node }),
1411
+ showControls && (React.createElement(TransformControls, { ref: (controls) => {
1412
+ controlsRef.current = controls;
1413
+ // 箭头本体登记为"仅编辑视角":画中画/镜头视角渲染时不入画。
1414
+ // three 0.185 的 TransformControls 不是 Object3D,可视部分在 getHelper()
1415
+ if (!controls)
1416
+ return;
1417
+ ensureUniformScaleHandle(controls);
1418
+ const root = typeof controls.getHelper === 'function'
1419
+ ? controls.getHelper()
1420
+ : controls;
1421
+ return registerEditorOnly(root);
1422
+ }, object: target.node, mode: mode, onMouseDown: () => {
1423
+ suppressSelectBriefly();
1424
+ scrubRef.current = getStudioSync()?.scrub();
1425
+ invalidate();
1426
+ const axis = controlsRef.current?.axis;
1427
+ if (mode === 'scale' && axis === 'XYZ') {
1428
+ const ndc = target.node
1429
+ .getWorldPosition(new Vector3())
1430
+ .project(camera);
1431
+ const rect = gl.domElement.getBoundingClientRect();
1432
+ const cx = rect.left + ((ndc.x + 1) / 2) * rect.width;
1433
+ const cy = rect.top + ((1 - ndc.y) / 2) * rect.height;
1434
+ const p = lastPointerRef.current;
1435
+ let dx = p.x - cx;
1436
+ let dy = p.y - cy;
1437
+ const len = Math.hypot(dx, dy);
1438
+ // 抓在正中心时没有方向,兜底向屏幕下方拉=放大
1439
+ if (len < 4) {
1440
+ dx = 0;
1441
+ dy = 1;
1442
+ }
1443
+ else {
1444
+ dx /= len;
1445
+ dy /= len;
1446
+ }
1447
+ uniDragRef.current = {
1448
+ startScale: target.node.scale.clone(),
1449
+ startPointer: { ...p },
1450
+ dir: { x: dx, y: dy },
1451
+ };
1452
+ }
1453
+ else {
1454
+ uniDragRef.current = null;
1455
+ }
1456
+ }, onObjectChange: () => {
1457
+ suppressSelectBriefly();
1458
+ invalidate();
1459
+ const scrub = scrubRef.current;
1460
+ if (!scrub)
1461
+ return;
1462
+ const node = target.node;
1463
+ scrub.capture(({ set }) => {
1464
+ // 旋转/缩放写回的是节点本地值:theatre 的 rotation/scale 属性
1465
+ // 本来就是本地空间,和面板里看到的数字一致
1466
+ if (mode === 'rotate') {
1467
+ const r = node.rotation;
1468
+ set(target.obj.props.rotation, { x: r.x, y: r.y, z: r.z });
1469
+ }
1470
+ else if (mode === 'scale') {
1471
+ const uni = uniDragRef.current;
1472
+ if (uni && controlsRef.current?.axis === 'XYZ') {
1473
+ // 等比块:弃用 three-stdlib 的距离比(抓点近中心会炸),
1474
+ // 改屏幕空间径向映射,见 uniDragRef 注释
1475
+ const p = lastPointerRef.current;
1476
+ const along = (p.x - uni.startPointer.x) * uni.dir.x +
1477
+ (p.y - uni.startPointer.y) * uni.dir.y;
1478
+ const factor = Math.max(0.01, 1 + along / 250);
1479
+ node.scale.copy(uni.startScale).multiplyScalar(factor);
1480
+ }
1481
+ const s = node.scale;
1482
+ set(target.obj.props.scale, { x: s.x, y: s.y, z: s.z });
1483
+ }
1484
+ else {
1485
+ const p = node.position;
1486
+ set(target.obj.props.position, { x: p.x, y: p.y, z: p.z });
1487
+ }
1488
+ });
1489
+ }, onMouseUp: () => {
1490
+ suppressSelectBriefly();
1491
+ scrubRef.current?.commit();
1492
+ scrubRef.current = undefined;
1493
+ uniDragRef.current = null;
1494
+ } }))));
1495
+ }
1496
+ /**
1497
+ * 选中对象的轮廓描边(Blender 式,蒙版+边缘检测):没有自带选中高亮的
1498
+ * 对象(放置的模型等)选中后由它提示选中态——模式无关,选择模式下它是
1499
+ * 唯一的选中反馈。屏幕空间描边,不往场景挂任何网格;画中画/镜头取景/
1500
+ * 导出不入画(镜头视角下 SelectionGizmo 整体不挂,这里随之缺席)。
1501
+ * useFrame 优先级 1.5:在 drei Hud(优先级 1,主视角画面)之后、画中画
1502
+ * (优先级 2)之前画,描边只叠在编辑主视角上。
1503
+ */
1504
+ function SelectionOutlineHighlight({ node }) {
1505
+ const invalidate = useThree((s) => s.invalidate);
1506
+ const rendererRef = useRef(null);
1507
+ useEffect(() => {
1508
+ const renderer = createSelectionOutlineRenderer();
1509
+ rendererRef.current = renderer;
1510
+ return () => {
1511
+ renderer.dispose();
1512
+ rendererRef.current = null;
1513
+ };
1514
+ }, []);
1515
+ // 选中切换/卸载都要触发重画:demand 模式下不 invalidate,
1516
+ // 画面上会留着上一帧的描边残影
1517
+ useEffect(() => {
1518
+ invalidate();
1519
+ return () => invalidate();
1520
+ }, [node, invalidate]);
1521
+ useFrame(({ gl, scene, camera, size, viewport }) => {
1522
+ rendererRef.current?.render(gl, scene, camera, node, size, viewport.dpr);
1523
+ }, 1.5);
1524
+ return null;
1525
+ }