@needle-tools/engine 5.1.0-canary.db0c38f → 5.1.0-canary.e6680fa

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 (381) hide show
  1. package/.needle/generated/needle-bindings.gen.d.ts +5 -0
  2. package/CHANGELOG.md +133 -1
  3. package/SKILL.md +4 -1
  4. package/components.needle.json +1 -1
  5. package/dist/needle-engine.bundle-Bl_hyH5G.umd.cjs +1734 -0
  6. package/dist/needle-engine.bundle-Cduc1gj6.min.js +1734 -0
  7. package/dist/{needle-engine.bundle-B29kieh0.js → needle-engine.bundle-DNcqT8nJ.js} +13770 -12741
  8. package/dist/needle-engine.d.ts +1628 -402
  9. package/dist/needle-engine.js +591 -586
  10. package/dist/needle-engine.min.js +1 -1
  11. package/dist/needle-engine.umd.cjs +1 -1
  12. package/dist/three.js +1 -0
  13. package/dist/three.min.js +21 -21
  14. package/dist/three.umd.cjs +16 -16
  15. package/lib/engine/api.d.ts +9 -2
  16. package/lib/engine/api.js +7 -1
  17. package/lib/engine/api.js.map +1 -1
  18. package/lib/engine/codegen/register_types.js +10 -18
  19. package/lib/engine/codegen/register_types.js.map +1 -1
  20. package/lib/engine/debug/debug_spatial_console.d.ts +2 -0
  21. package/lib/engine/debug/debug_spatial_console.js +10 -7
  22. package/lib/engine/debug/debug_spatial_console.js.map +1 -1
  23. package/lib/engine/engine_addressables.d.ts +2 -0
  24. package/lib/engine/engine_addressables.js +6 -3
  25. package/lib/engine/engine_addressables.js.map +1 -1
  26. package/lib/engine/engine_audio.d.ts +68 -0
  27. package/lib/engine/engine_audio.js +172 -0
  28. package/lib/engine/engine_audio.js.map +1 -1
  29. package/lib/engine/engine_camera.fit.js +16 -4
  30. package/lib/engine/engine_camera.fit.js.map +1 -1
  31. package/lib/engine/engine_components.js +1 -1
  32. package/lib/engine/engine_components.js.map +1 -1
  33. package/lib/engine/engine_context.d.ts +41 -27
  34. package/lib/engine/engine_context.js +71 -30
  35. package/lib/engine/engine_context.js.map +1 -1
  36. package/lib/engine/engine_context_eventbus.d.ts +47 -0
  37. package/lib/engine/engine_context_eventbus.js +47 -0
  38. package/lib/engine/engine_context_eventbus.js.map +1 -0
  39. package/lib/engine/engine_disposable.d.ts +172 -0
  40. package/lib/engine/engine_disposable.js +136 -0
  41. package/lib/engine/engine_disposable.js.map +1 -0
  42. package/lib/engine/engine_gameobject.d.ts +1 -10
  43. package/lib/engine/engine_gameobject.js +22 -120
  44. package/lib/engine/engine_gameobject.js.map +1 -1
  45. package/lib/engine/engine_gltf_builtin_components.js +7 -69
  46. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  47. package/lib/engine/engine_init.js +16 -1
  48. package/lib/engine/engine_init.js.map +1 -1
  49. package/lib/engine/engine_input.d.ts +24 -5
  50. package/lib/engine/engine_input.js +3 -2
  51. package/lib/engine/engine_input.js.map +1 -1
  52. package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
  53. package/lib/engine/engine_instantiate_resolve.js +372 -0
  54. package/lib/engine/engine_instantiate_resolve.js.map +1 -0
  55. package/lib/engine/engine_license.d.ts +8 -6
  56. package/lib/engine/engine_license.js +195 -59
  57. package/lib/engine/engine_license.js.map +1 -1
  58. package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
  59. package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
  60. package/lib/engine/engine_mainloop_utils.js +7 -4
  61. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  62. package/lib/engine/engine_networking.d.ts +51 -37
  63. package/lib/engine/engine_networking.js +132 -82
  64. package/lib/engine/engine_networking.js.map +1 -1
  65. package/lib/engine/engine_networking.transport.websocket.d.ts +15 -0
  66. package/lib/engine/engine_networking.transport.websocket.js +38 -0
  67. package/lib/engine/engine_networking.transport.websocket.js.map +1 -0
  68. package/lib/engine/engine_networking_blob.js +4 -4
  69. package/lib/engine/engine_networking_blob.js.map +1 -1
  70. package/lib/engine/engine_networking_instantiate.js +2 -2
  71. package/lib/engine/engine_networking_instantiate.js.map +1 -1
  72. package/lib/engine/engine_networking_types.d.ts +39 -1
  73. package/lib/engine/engine_networking_types.js +7 -0
  74. package/lib/engine/engine_networking_types.js.map +1 -1
  75. package/lib/engine/engine_physics_rapier.d.ts +21 -3
  76. package/lib/engine/engine_physics_rapier.js +94 -25
  77. package/lib/engine/engine_physics_rapier.js.map +1 -1
  78. package/lib/engine/engine_pmrem.js +53 -5
  79. package/lib/engine/engine_pmrem.js.map +1 -1
  80. package/lib/engine/engine_scenedata.d.ts +13 -17
  81. package/lib/engine/engine_scenedata.js +58 -31
  82. package/lib/engine/engine_scenedata.js.map +1 -1
  83. package/lib/engine/engine_serialization_builtin_serializer.d.ts +10 -16
  84. package/lib/engine/engine_serialization_builtin_serializer.js +56 -46
  85. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  86. package/lib/engine/engine_serialization_core.d.ts +1 -0
  87. package/lib/engine/engine_serialization_core.js +7 -0
  88. package/lib/engine/engine_serialization_core.js.map +1 -1
  89. package/lib/engine/engine_ssr.d.ts +2 -0
  90. package/lib/engine/engine_ssr.js +20 -0
  91. package/lib/engine/engine_ssr.js.map +1 -1
  92. package/lib/engine/engine_types.d.ts +31 -11
  93. package/lib/engine/engine_types.js +1 -1
  94. package/lib/engine/engine_types.js.map +1 -1
  95. package/lib/engine/engine_util_decorator.js +7 -2
  96. package/lib/engine/engine_util_decorator.js.map +1 -1
  97. package/lib/engine/engine_utils.d.ts +1 -1
  98. package/lib/engine/engine_utils.js +19 -5
  99. package/lib/engine/engine_utils.js.map +1 -1
  100. package/lib/engine/engine_utils_format.js +20 -14
  101. package/lib/engine/engine_utils_format.js.map +1 -1
  102. package/lib/engine/engine_utils_qrcode.js +2 -2
  103. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  104. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  105. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  106. package/lib/engine/webcomponents/jsx.d.ts +51 -0
  107. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  108. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  109. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  110. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +3 -4
  111. package/lib/engine/webcomponents/needle menu/needle-menu.js +6 -6
  112. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  113. package/lib/engine/webcomponents/needle-button.js.map +1 -1
  114. package/lib/engine/webcomponents/needle-engine.d.ts +10 -4
  115. package/lib/engine/webcomponents/needle-engine.js +3 -3
  116. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  117. package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
  118. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  119. package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
  120. package/lib/engine/xr/NeedleXRSession.js +50 -14
  121. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  122. package/lib/engine/xr/TempXRContext.js +2 -2
  123. package/lib/engine/xr/TempXRContext.js.map +1 -1
  124. package/lib/engine/xr/events.d.ts +1 -1
  125. package/lib/engine/xr/events.js.map +1 -1
  126. package/lib/engine-components/Animation.js +17 -16
  127. package/lib/engine-components/Animation.js.map +1 -1
  128. package/lib/engine-components/AnimationBuilder.d.ts +158 -0
  129. package/lib/engine-components/AnimationBuilder.js +305 -0
  130. package/lib/engine-components/AnimationBuilder.js.map +1 -0
  131. package/lib/engine-components/Animator.d.ts +6 -0
  132. package/lib/engine-components/Animator.js +23 -13
  133. package/lib/engine-components/Animator.js.map +1 -1
  134. package/lib/engine-components/AnimatorController.builder.d.ts +191 -0
  135. package/lib/engine-components/AnimatorController.builder.js +263 -0
  136. package/lib/engine-components/AnimatorController.builder.js.map +1 -0
  137. package/lib/engine-components/AnimatorController.d.ts +4 -119
  138. package/lib/engine-components/AnimatorController.js +37 -233
  139. package/lib/engine-components/AnimatorController.js.map +1 -1
  140. package/lib/engine-components/AudioSource.d.ts +19 -3
  141. package/lib/engine-components/AudioSource.js +121 -68
  142. package/lib/engine-components/AudioSource.js.map +1 -1
  143. package/lib/engine-components/Camera.d.ts +6 -1
  144. package/lib/engine-components/Camera.js +16 -3
  145. package/lib/engine-components/Camera.js.map +1 -1
  146. package/lib/engine-components/CameraUtils.js +14 -6
  147. package/lib/engine-components/CameraUtils.js.map +1 -1
  148. package/lib/engine-components/Collider.d.ts +18 -9
  149. package/lib/engine-components/Collider.js +61 -14
  150. package/lib/engine-components/Collider.js.map +1 -1
  151. package/lib/engine-components/Component.d.ts +72 -9
  152. package/lib/engine-components/Component.js +114 -10
  153. package/lib/engine-components/Component.js.map +1 -1
  154. package/lib/engine-components/ContactShadows.d.ts +1 -0
  155. package/lib/engine-components/ContactShadows.js +14 -1
  156. package/lib/engine-components/ContactShadows.js.map +1 -1
  157. package/lib/engine-components/DragControls.d.ts +7 -0
  158. package/lib/engine-components/DragControls.js +19 -7
  159. package/lib/engine-components/DragControls.js.map +1 -1
  160. package/lib/engine-components/DropListener.js +4 -0
  161. package/lib/engine-components/DropListener.js.map +1 -1
  162. package/lib/engine-components/EventList.d.ts +31 -9
  163. package/lib/engine-components/EventList.js +37 -76
  164. package/lib/engine-components/EventList.js.map +1 -1
  165. package/lib/engine-components/Joints.d.ts +4 -2
  166. package/lib/engine-components/Joints.js +19 -3
  167. package/lib/engine-components/Joints.js.map +1 -1
  168. package/lib/engine-components/Light.d.ts +6 -8
  169. package/lib/engine-components/Light.js +48 -27
  170. package/lib/engine-components/Light.js.map +1 -1
  171. package/lib/engine-components/Networking.d.ts +1 -1
  172. package/lib/engine-components/Networking.js +1 -1
  173. package/lib/engine-components/OrbitControls.d.ts +1 -2
  174. package/lib/engine-components/OrbitControls.js +37 -14
  175. package/lib/engine-components/OrbitControls.js.map +1 -1
  176. package/lib/engine-components/ReflectionProbe.js +2 -0
  177. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  178. package/lib/engine-components/RigidBody.d.ts +12 -4
  179. package/lib/engine-components/RigidBody.js +18 -4
  180. package/lib/engine-components/RigidBody.js.map +1 -1
  181. package/lib/engine-components/SceneSwitcher.js +3 -0
  182. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  183. package/lib/engine-components/SeeThrough.js +2 -2
  184. package/lib/engine-components/SeeThrough.js.map +1 -1
  185. package/lib/engine-components/VideoPlayer.d.ts +8 -2
  186. package/lib/engine-components/VideoPlayer.js +42 -19
  187. package/lib/engine-components/VideoPlayer.js.map +1 -1
  188. package/lib/engine-components/Voip.d.ts +16 -7
  189. package/lib/engine-components/Voip.js +90 -53
  190. package/lib/engine-components/Voip.js.map +1 -1
  191. package/lib/engine-components/api.d.ts +3 -1
  192. package/lib/engine-components/api.js +3 -1
  193. package/lib/engine-components/api.js.map +1 -1
  194. package/lib/engine-components/codegen/components.d.ts +7 -13
  195. package/lib/engine-components/codegen/components.js +7 -13
  196. package/lib/engine-components/codegen/components.js.map +1 -1
  197. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  198. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  199. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  200. package/lib/engine-components/postprocessing/VolumeParameter.d.ts +2 -0
  201. package/lib/engine-components/postprocessing/VolumeParameter.js +4 -1
  202. package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
  203. package/lib/engine-components/timeline/PlayableDirector.d.ts +21 -11
  204. package/lib/engine-components/timeline/PlayableDirector.js +75 -67
  205. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  206. package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
  207. package/lib/engine-components/timeline/SignalAsset.js +1 -0
  208. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  209. package/lib/engine-components/timeline/TimelineBuilder.d.ts +413 -0
  210. package/lib/engine-components/timeline/TimelineBuilder.js +506 -0
  211. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
  212. package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
  213. package/lib/engine-components/timeline/TimelineModels.js +3 -0
  214. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  215. package/lib/engine-components/timeline/TimelineTracks.d.ts +37 -6
  216. package/lib/engine-components/timeline/TimelineTracks.js +92 -26
  217. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  218. package/lib/engine-components/timeline/index.d.ts +2 -1
  219. package/lib/engine-components/timeline/index.js +2 -0
  220. package/lib/engine-components/timeline/index.js.map +1 -1
  221. package/lib/engine-components/ui/Canvas.d.ts +1 -1
  222. package/lib/engine-components/ui/Canvas.js +2 -8
  223. package/lib/engine-components/ui/Canvas.js.map +1 -1
  224. package/lib/engine-components/ui/Text.d.ts +1 -0
  225. package/lib/engine-components/ui/Text.js +10 -7
  226. package/lib/engine-components/ui/Text.js.map +1 -1
  227. package/lib/engine-components/web/CursorFollow.d.ts +0 -1
  228. package/lib/engine-components/web/CursorFollow.js +21 -13
  229. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  230. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +62 -1
  231. package/lib/engine-components/webxr/WebXRImageTracking.js +59 -2
  232. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  233. package/lib/needle-engine.d.ts +2 -0
  234. package/lib/needle-engine.js +2 -0
  235. package/lib/needle-engine.js.map +1 -1
  236. package/package.json +4 -84
  237. package/plugins/common/cloud.js +6 -1
  238. package/plugins/common/license.js +55 -12
  239. package/plugins/common/worker.js +9 -4
  240. package/plugins/dts-generator/dts.codegen.js +255 -50
  241. package/plugins/dts-generator/dts.scan.js +37 -9
  242. package/plugins/dts-generator/dts.writer.js +1 -1
  243. package/plugins/dts-generator/glb.discovery.js +140 -23
  244. package/plugins/dts-generator/glb.extractor.js +48 -8
  245. package/plugins/dts-generator/glb.reader.js +80 -27
  246. package/plugins/dts-generator/index.js +1 -1
  247. package/plugins/types/needle-bindings.d.ts +25 -14
  248. package/plugins/types/userconfig.d.ts +16 -1
  249. package/plugins/vite/asap.js +18 -9
  250. package/plugins/vite/build-pipeline.js +57 -20
  251. package/plugins/vite/dependencies.js +29 -10
  252. package/plugins/vite/dependency-watcher.d.ts +2 -2
  253. package/plugins/vite/dependency-watcher.js +5 -6
  254. package/plugins/vite/drop.d.ts +2 -2
  255. package/plugins/vite/drop.js +3 -4
  256. package/plugins/vite/dts-generator.d.ts +2 -2
  257. package/plugins/vite/dts-generator.js +43 -9
  258. package/plugins/vite/editor-connection.js +3 -3
  259. package/plugins/vite/index.d.ts +9 -3
  260. package/plugins/vite/index.js +23 -10
  261. package/plugins/vite/license.js +42 -7
  262. package/plugins/vite/local-files-core.js +3 -3
  263. package/plugins/vite/local-files-utils.d.ts +3 -1
  264. package/plugins/vite/local-files-utils.js +29 -5
  265. package/plugins/vite/meta.js +4 -2
  266. package/plugins/vite/poster.d.ts +2 -2
  267. package/plugins/vite/poster.js +3 -5
  268. package/plugins/vite/reload.d.ts +2 -2
  269. package/plugins/vite/reload.js +23 -23
  270. package/plugins/vite/server.js +2 -1
  271. package/src/engine/api.ts +12 -2
  272. package/src/engine/codegen/register_types.ts +10 -18
  273. package/src/engine/debug/debug_spatial_console.ts +10 -7
  274. package/src/engine/engine_addressables.ts +6 -3
  275. package/src/engine/engine_audio.ts +184 -0
  276. package/src/engine/engine_camera.fit.ts +15 -4
  277. package/src/engine/engine_components.ts +1 -1
  278. package/src/engine/engine_context.ts +85 -38
  279. package/src/engine/engine_context_eventbus.ts +73 -0
  280. package/src/engine/engine_disposable.ts +214 -0
  281. package/src/engine/engine_gameobject.ts +54 -159
  282. package/src/engine/engine_gltf_builtin_components.ts +7 -76
  283. package/src/engine/engine_init.ts +15 -1
  284. package/src/engine/engine_input.ts +28 -7
  285. package/src/engine/engine_instantiate_resolve.ts +407 -0
  286. package/src/engine/engine_license.ts +209 -61
  287. package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
  288. package/src/engine/engine_mainloop_utils.ts +7 -4
  289. package/src/engine/engine_networking.transport.websocket.ts +45 -0
  290. package/src/engine/engine_networking.ts +161 -137
  291. package/src/engine/engine_networking_blob.ts +4 -4
  292. package/src/engine/engine_networking_instantiate.ts +2 -2
  293. package/src/engine/engine_networking_types.ts +41 -1
  294. package/src/engine/engine_physics_rapier.ts +102 -33
  295. package/src/engine/engine_pmrem.ts +56 -6
  296. package/src/engine/engine_scenedata.ts +56 -30
  297. package/src/engine/engine_serialization_builtin_serializer.ts +64 -52
  298. package/src/engine/engine_serialization_core.ts +9 -0
  299. package/src/engine/engine_ssr.ts +29 -3
  300. package/src/engine/engine_types.ts +48 -27
  301. package/src/engine/engine_util_decorator.ts +7 -2
  302. package/src/engine/engine_utils.ts +16 -5
  303. package/src/engine/engine_utils_format.ts +20 -14
  304. package/src/engine/engine_utils_qrcode.ts +2 -2
  305. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  306. package/src/engine/webcomponents/jsx.d.ts +51 -0
  307. package/src/engine/webcomponents/logo-element.ts +1 -0
  308. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  309. package/src/engine/webcomponents/needle menu/needle-menu.ts +8 -7
  310. package/src/engine/webcomponents/needle-button.ts +1 -0
  311. package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
  312. package/src/engine/webcomponents/needle-engine.ts +13 -6
  313. package/src/engine/xr/NeedleXRSession.ts +48 -13
  314. package/src/engine/xr/TempXRContext.ts +2 -2
  315. package/src/engine/xr/events.ts +1 -1
  316. package/src/engine-components/Animation.ts +19 -16
  317. package/src/engine-components/AnimationBuilder.ts +472 -0
  318. package/src/engine-components/Animator.ts +24 -12
  319. package/src/engine-components/AnimatorController.builder.ts +387 -0
  320. package/src/engine-components/AnimatorController.ts +24 -292
  321. package/src/engine-components/AudioSource.ts +130 -79
  322. package/src/engine-components/Camera.ts +16 -3
  323. package/src/engine-components/CameraUtils.ts +12 -5
  324. package/src/engine-components/Collider.ts +66 -18
  325. package/src/engine-components/Component.ts +118 -20
  326. package/src/engine-components/ContactShadows.ts +15 -1
  327. package/src/engine-components/DragControls.ts +18 -11
  328. package/src/engine-components/DropListener.ts +4 -0
  329. package/src/engine-components/EventList.ts +45 -83
  330. package/src/engine-components/Joints.ts +20 -4
  331. package/src/engine-components/Light.ts +49 -27
  332. package/src/engine-components/Networking.ts +1 -1
  333. package/src/engine-components/OrbitControls.ts +42 -16
  334. package/src/engine-components/ReflectionProbe.ts +2 -0
  335. package/src/engine-components/RigidBody.ts +18 -4
  336. package/src/engine-components/SceneSwitcher.ts +3 -0
  337. package/src/engine-components/SeeThrough.ts +2 -2
  338. package/src/engine-components/VideoPlayer.ts +40 -17
  339. package/src/engine-components/Voip.ts +88 -53
  340. package/src/engine-components/api.ts +3 -1
  341. package/src/engine-components/codegen/components.ts +7 -13
  342. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  343. package/src/engine-components/postprocessing/VolumeParameter.ts +4 -1
  344. package/src/engine-components/timeline/PlayableDirector.ts +83 -81
  345. package/src/engine-components/timeline/SignalAsset.ts +4 -1
  346. package/src/engine-components/timeline/TimelineBuilder.ts +824 -0
  347. package/src/engine-components/timeline/TimelineModels.ts +5 -1
  348. package/src/engine-components/timeline/TimelineTracks.ts +96 -27
  349. package/src/engine-components/timeline/index.ts +2 -1
  350. package/src/engine-components/ui/Canvas.ts +2 -8
  351. package/src/engine-components/ui/Text.ts +12 -8
  352. package/src/engine-components/web/CursorFollow.ts +21 -14
  353. package/src/engine-components/webxr/WebXRImageTracking.ts +79 -7
  354. package/src/needle-engine.ts +3 -0
  355. package/dist/needle-engine.bundle-Dq0Ly8fW.umd.cjs +0 -1732
  356. package/dist/needle-engine.bundle-YnpzzOPL.min.js +0 -1732
  357. package/lib/engine-components/AvatarLoader.d.ts +0 -80
  358. package/lib/engine-components/AvatarLoader.js +0 -232
  359. package/lib/engine-components/AvatarLoader.js.map +0 -1
  360. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
  361. package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
  362. package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
  363. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
  364. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
  365. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
  366. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
  367. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
  368. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
  369. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
  370. package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
  371. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
  372. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
  373. package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
  374. package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
  375. package/src/engine-components/AvatarLoader.ts +0 -264
  376. package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
  377. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
  378. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
  379. package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
  380. package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
  381. package/src/vite-env.d.ts +0 -16
@@ -8,7 +8,63 @@
8
8
  */
9
9
 
10
10
  import { existsSync, readFileSync, readdirSync } from 'fs';
11
- import { join, resolve } from 'path';
11
+ import { join, basename, extname } from 'path';
12
+
13
+ /**
14
+ * Generic URL path segments that carry no useful identity — fall back to
15
+ * the previous segment when the last segment matches one of these.
16
+ */
17
+ const GENERIC_SEGMENTS = new Set(["file", "index", "scene", "assets", "glb", "gltf", "model", "download"]);
18
+
19
+ /**
20
+ * Derive a short, human-friendly identifier from a GLB URL or local path.
21
+ *
22
+ * Rules (in priority order):
23
+ * 1. If `contentDispositionFilename` is provided → strip extension, use it.
24
+ * 2. Remote URL → walk path segments right-to-left, skip generic ones,
25
+ * strip extension from first useful segment.
26
+ * 3. Local path → basename without extension.
27
+ *
28
+ * Result is identifier-safe: non-alphanumeric chars replaced with `_`,
29
+ * leading digits prefixed with `_`.
30
+ *
31
+ * @param {string} pathOrUrl
32
+ * @param {string | null} [contentDispositionFilename]
33
+ * @returns {string}
34
+ */
35
+ export function glbFriendlyName(pathOrUrl, contentDispositionFilename) {
36
+ let raw = "";
37
+
38
+ if (contentDispositionFilename) {
39
+ raw = basename(contentDispositionFilename, extname(contentDispositionFilename));
40
+ } else if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
41
+ try {
42
+ const segments = new URL(pathOrUrl).pathname.split("/").filter(Boolean);
43
+ // Walk right-to-left, skip generic segments
44
+ for (let i = segments.length - 1; i >= 0; i--) {
45
+ const seg = segments[i];
46
+ const withoutExt = seg.replace(/\.(glb|gltf)$/i, "");
47
+ if (!GENERIC_SEGMENTS.has(withoutExt.toLowerCase())) {
48
+ raw = withoutExt;
49
+ break;
50
+ }
51
+ }
52
+ if (!raw) raw = segments[segments.length - 1] ?? "scene";
53
+ } catch (_e) {
54
+ raw = "scene";
55
+ }
56
+ } else {
57
+ raw = basename(pathOrUrl, extname(pathOrUrl));
58
+ }
59
+
60
+ // Make identifier-safe
61
+ let id = raw.replace(/[^a-zA-Z0-9]/g, "_");
62
+ if (/^\d/.test(id)) id = "_" + id;
63
+ return id || "scene";
64
+ }
65
+
66
+ /** Source file extensions that may contain `<needle-engine src="...">` markup. */
67
+ const SOURCE_EXTENSIONS = /\.(html|svelte|tsx|jsx|vue)$/i;
12
68
 
13
69
  /**
14
70
  * Parse `<needle-engine src="...">` from HTML.
@@ -17,7 +73,7 @@ import { join, resolve } from 'path';
17
73
  * @returns {string[]}
18
74
  */
19
75
  function parseSrcAttribute(html) {
20
- const re = /<needle-engine[^>]*\ssrc=["']([^"']+)["']/gi;
76
+ const re = /<needle-engine[\s\S]*?\ssrc=["']([^"']+)["']/gi;
21
77
  /** @type {string[]} */
22
78
  const out = [];
23
79
  let m;
@@ -54,19 +110,26 @@ function parseGenJs(src) {
54
110
  /**
55
111
  * Resolve a list of (possibly relative) GLB path strings to absolute paths.
56
112
  * Remote URLs (http/https) are passed through as-is.
113
+ * The `key` field preserves the original src value (relative path or URL)
114
+ * for use as the SceneMap key in the generated .d.ts.
57
115
  *
58
- * @param {string[]} paths
116
+ * @param {Array<{glbPath: string, sourceFile: string | null}>} pathEntries
59
117
  * @param {string} projectRoot
60
118
  * @param {string} assetsDir
61
- * @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean}>}
119
+ * @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean, key: string, sourceFiles: string[]}>}
62
120
  */
63
- function resolveGlbPaths(paths, projectRoot, assetsDir) {
64
- /** @type {Array<{path: string, type: "glb"|"gltf", remote?: boolean}>} */
65
- const out = [];
66
- for (const p of paths) {
121
+ function resolveGlbPaths(pathEntries, projectRoot, assetsDir) {
122
+ /** @type {Map<string, {path: string, type: "glb"|"gltf", remote?: boolean, key: string, sourceFiles: string[]}>} */
123
+ const byKey = new Map();
124
+ for (const { glbPath: p, sourceFile } of pathEntries) {
67
125
  if (p.startsWith("http://") || p.startsWith("https://")) {
68
126
  const type = p.toLowerCase().endsWith(".gltf") ? "gltf" : "glb";
69
- out.push({ path: p, type, remote: true });
127
+ const existing = byKey.get(p);
128
+ if (existing) {
129
+ if (sourceFile) existing.sourceFiles.push(sourceFile);
130
+ } else {
131
+ byKey.set(p, { path: p, type, remote: true, key: p, sourceFiles: sourceFile ? [sourceFile] : [] });
132
+ }
70
133
  continue;
71
134
  }
72
135
  const clean = p.replace(/^\.\//, "").replace(/^\//, "");
@@ -77,52 +140,106 @@ function resolveGlbPaths(paths, projectRoot, assetsDir) {
77
140
  for (const candidate of candidates) {
78
141
  if (existsSync(candidate)) {
79
142
  const type = candidate.toLowerCase().endsWith(".gltf") ? "gltf" : "glb";
80
- out.push({ path: candidate, type });
143
+ const existing = byKey.get(clean);
144
+ if (existing) {
145
+ if (sourceFile) existing.sourceFiles.push(sourceFile);
146
+ } else {
147
+ byKey.set(clean, { path: candidate, type, key: clean, sourceFiles: sourceFile ? [sourceFile] : [] });
148
+ }
81
149
  break;
82
150
  }
83
151
  }
84
152
  }
153
+ return Array.from(byKey.values());
154
+ }
155
+
156
+ /**
157
+ * Walk `src/` (or the project root) for source files that may contain
158
+ * `<needle-engine src="...">` and return GLB path + source file pairs.
159
+ *
160
+ * @param {string} projectRoot
161
+ * @returns {Array<{glbPath: string, sourceFile: string}>}
162
+ */
163
+ function scanSourceFilesForGlbs(projectRoot) {
164
+ /** @type {Array<{glbPath: string, sourceFile: string}>} */
165
+ const out = [];
166
+ const srcDir = join(projectRoot, "src");
167
+ const searchRoot = existsSync(srcDir) ? srcDir : projectRoot;
168
+
169
+ /** @param {string} dir */
170
+ function walk(dir) {
171
+ let entries;
172
+ try { entries = readdirSync(dir, { withFileTypes: true }); } catch (_e) { return; }
173
+ for (const entry of entries) {
174
+ const fullPath = join(dir, entry.name);
175
+ if (entry.isDirectory()) {
176
+ // Skip node_modules and hidden dirs
177
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
178
+ walk(fullPath);
179
+ } else if (SOURCE_EXTENSIONS.test(entry.name)) {
180
+ try {
181
+ const content = readFileSync(fullPath, "utf8");
182
+ const relPath = fullPath.replace(projectRoot + "/", "").replace(projectRoot + "\\", "");
183
+ for (const glbPath of parseSrcAttribute(content)) {
184
+ out.push({ glbPath, sourceFile: relPath });
185
+ }
186
+ } catch (_e) { /* ignore unreadable files */ }
187
+ }
188
+ }
189
+ }
190
+
191
+ walk(searchRoot);
85
192
  return out;
86
193
  }
87
194
 
88
195
  /**
89
196
  * Resolve the entrypoint GLB file paths for a project.
90
197
  *
91
- * Priority:
92
- * 1. `<needle-engine src="...">` attribute in `index.html`
198
+ * Sources (all merged, deduplicated by key):
199
+ * 1. `<needle-engine src="...">` in `index.html`
93
200
  * 2. `needle_exported_files.push("...")` lines in `{codegenDir}/gen.js`
94
- * 3. Returns `null` caller should fall back to `collectSceneFiles`.
201
+ * 3. `<needle-engine src="...">` in any `.svelte`, `.tsx`, `.jsx`, `.vue`, `.html` under `src/`
202
+ *
203
+ * Returns `null` if nothing found — caller should fall back to `collectSceneFiles`.
95
204
  *
96
205
  * @param {string} projectRoot Absolute path to the project root
97
206
  * @param {string} assetsDir Absolute path to the assets directory
98
207
  * @param {string} [codegenDir] Absolute path to the codegen directory
99
- * @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean}> | null}
208
+ * @returns {Array<{path: string, type: "glb"|"gltf", remote?: boolean, key: string, sourceFiles: string[]}> | null}
100
209
  */
101
210
  export function resolveEntrypointGlbs(projectRoot, assetsDir, codegenDir) {
211
+ /** @type {Array<{glbPath: string, sourceFile: string | null}>} */
212
+ const allEntries = [];
213
+
214
+ // 1. index.html
102
215
  const htmlPath = join(projectRoot, "index.html");
103
216
  if (existsSync(htmlPath)) {
104
217
  try {
105
- const srcPaths = parseSrcAttribute(readFileSync(htmlPath, "utf8"));
106
- if (srcPaths.length > 0) {
107
- const resolved = resolveGlbPaths(srcPaths, projectRoot, assetsDir);
108
- if (resolved.length > 0) return resolved;
218
+ for (const glbPath of parseSrcAttribute(readFileSync(htmlPath, "utf8"))) {
219
+ allEntries.push({ glbPath, sourceFile: "index.html" });
109
220
  }
110
221
  } catch (_e) { /* ignore */ }
111
222
  }
112
223
 
224
+ // 2. gen.js
113
225
  const genDir = codegenDir ?? join(projectRoot, "src", "generated");
114
226
  const genPath = join(genDir, "gen.js");
115
227
  if (existsSync(genPath)) {
116
228
  try {
117
- const genPaths = parseGenJs(readFileSync(genPath, "utf8"));
118
- if (genPaths.length > 0) {
119
- const resolved = resolveGlbPaths(genPaths, projectRoot, assetsDir);
120
- if (resolved.length > 0) return resolved;
229
+ const relGenPath = genPath.replace(projectRoot + "/", "").replace(projectRoot + "\\", "");
230
+ for (const glbPath of parseGenJs(readFileSync(genPath, "utf8"))) {
231
+ allEntries.push({ glbPath, sourceFile: relGenPath });
121
232
  }
122
233
  } catch (_e) { /* ignore */ }
123
234
  }
124
235
 
125
- return null;
236
+ // 3. Source files (SvelteKit, React, Vue, etc.)
237
+ allEntries.push(...scanSourceFilesForGlbs(projectRoot));
238
+
239
+ if (allEntries.length === 0) return null;
240
+
241
+ const resolved = resolveGlbPaths(allEntries, projectRoot, assetsDir);
242
+ return resolved.length > 0 ? resolved : null;
126
243
  }
127
244
 
128
245
  /**
@@ -29,17 +29,44 @@ export function sanitizeNodeName(rawName, namesUsed) {
29
29
 
30
30
  /**
31
31
  * Infer the Three.js runtime type of a glTF node from its JSON properties.
32
- * - `mesh` present → `import("three").Mesh`
33
- * - `camera` present → `import("three").Camera`
34
- * - KHR_lights_punctual → `import("three").Light`
35
- * - otherwise → `import("three").Object3D`
32
+ * - `mesh` present with skinning → `import("three").SkinnedMesh`
33
+ * - `mesh` present → `import("three").Mesh`
34
+ * - `camera` present, perspective → `import("three").PerspectiveCamera`
35
+ * - `camera` present, orthographic→ `import("three").OrthographicCamera`
36
+ * - KHR_lights_punctual → `import("three").Light`
37
+ * - otherwise → `import("three").Object3D`
36
38
  *
37
39
  * @param {Record<string, unknown>} node
40
+ * @param {Array<Record<string, unknown>>} cameras Raw glTF cameras array
41
+ * @param {Array<Record<string, unknown>>} meshes Raw glTF meshes array
38
42
  * @returns {string}
39
43
  */
40
- export function inferNodeThreeType(node) {
41
- if ("mesh" in node) return `import("three").Mesh`;
42
- if ("camera" in node) return `import("three").Camera`;
44
+ export function inferNodeThreeType(node, cameras, meshes) {
45
+ if ("mesh" in node) {
46
+ const hasSkin = "skin" in node;
47
+ if (!hasSkin) {
48
+ const meshIdx = /** @type {number} */ (node.mesh);
49
+ const mesh = meshes[meshIdx];
50
+ const primitives = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(mesh?.primitives) ? mesh.primitives : []);
51
+ const isSkinned = primitives.some(p => {
52
+ const attrs = /** @type {Record<string, unknown>} */ (p?.attributes ?? {});
53
+ return "WEIGHTS_0" in attrs;
54
+ });
55
+ if (isSkinned) return `import("three").SkinnedMesh`;
56
+ } else {
57
+ return `import("three").SkinnedMesh`;
58
+ }
59
+ return `import("three").Mesh`;
60
+ }
61
+ if ("camera" in node) {
62
+ const camIdx = /** @type {number} */ (node.camera);
63
+ const cam = cameras[camIdx];
64
+ if (cam && typeof cam === "object") {
65
+ if (cam.type === "perspective") return `import("three").PerspectiveCamera`;
66
+ if (cam.type === "orthographic") return `import("three").OrthographicCamera`;
67
+ }
68
+ return `import("three").Camera`;
69
+ }
43
70
  if (node.extensions && /** @type {any} */ (node.extensions)["KHR_lights_punctual"] != null) return `import("three").Light`;
44
71
  return `import("three").Object3D`;
45
72
  }
@@ -123,6 +150,17 @@ function buildNodePaths(nodes, namesUsed) {
123
150
  */
124
151
  export function extractComponentBindings(json) {
125
152
  const nodes = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.nodes) ? json.nodes : []);
153
+ const cameras = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.cameras) ? json.cameras : []);
154
+ const meshes = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.meshes) ? json.meshes : []);
155
+
156
+ // Collect scene root node indices — these map to THREE.Scene at runtime
157
+ const scenes = /** @type {Array<Record<string, unknown>>} */ (Array.isArray(json.scenes) ? json.scenes : []);
158
+ /** @type {Set<number>} */
159
+ const sceneRootNodeIndices = new Set();
160
+ for (const scene of scenes) {
161
+ const sceneNodes = Array.isArray(scene.nodes) ? /** @type {number[]} */ (scene.nodes) : [];
162
+ for (const idx of sceneNodes) sceneRootNodeIndices.add(idx);
163
+ }
126
164
  /** @type {Array<{nodeName: string, nodePath: string, componentName: string, fields: Record<string, unknown>, nodeThreeType: string}>} */
127
165
  const results = [];
128
166
  /** @type {Record<string, number>} */
@@ -138,7 +176,9 @@ export function extractComponentBindings(json) {
138
176
  if (!nodeName) continue;
139
177
 
140
178
  const nodePath = pathMap.get(i) ?? nodeName;
141
- const nodeThreeType = inferNodeThreeType(/** @type {Record<string, unknown>} */ (node));
179
+ const nodeThreeType = sceneRootNodeIndices.has(i)
180
+ ? `import("three").Scene`
181
+ : inferNodeThreeType(/** @type {Record<string, unknown>} */ (node), cameras, meshes);
142
182
  const ext = node.extensions?.[NEEDLE_COMPONENTS_EXTENSION];
143
183
 
144
184
  if (!ext || typeof ext !== "object") {
@@ -7,9 +7,9 @@
7
7
  */
8
8
 
9
9
  import { openSync, readSync, closeSync, readFileSync } from 'fs';
10
+ import { needleLog } from '../vite/logging.js';
10
11
 
11
- /** @type {Map<string, { lastModified: string, json: Record<string, unknown> }>} */
12
- const _remoteGlbCache = new Map();
12
+ const PLUGIN = "needle:dts-generator";
13
13
 
14
14
  /**
15
15
  * Read the JSON chunk from a binary GLB file without loading the binary blob.
@@ -56,12 +56,39 @@ export function readGltfJsonFile(filePath) {
56
56
  }
57
57
 
58
58
  /**
59
- * Fetch only the JSON chunk of a remote GLB using two HTTP Range requests.
59
+ * Parse the filename from a `Content-Disposition` header value.
60
+ * Handles `filename="foo.glb"` and `filename*=UTF-8''foo.glb` forms.
61
+ * Returns null if not present or not parseable.
62
+ * @param {string | null} header
63
+ * @returns {string | null}
64
+ */
65
+ export function parseContentDispositionFilename(header) {
66
+ if (!header) return null;
67
+ // RFC 5987 extended form: filename*=UTF-8''foo.glb
68
+ const extMatch = header.match(/filename\*\s*=\s*(?:[^']*'')?([^;]+)/i);
69
+ if (extMatch) {
70
+ try { return decodeURIComponent(extMatch[1].trim()); } catch (_e) { /* fall through */ }
71
+ }
72
+ // Plain form: filename="foo.glb" or filename=foo.glb
73
+ const plainMatch = header.match(/filename\s*=\s*"?([^";]+)"?/i);
74
+ if (plainMatch) return plainMatch[1].trim();
75
+ return null;
76
+ }
77
+
78
+ /** @type {Map<string, { lastModified: string, json: Record<string, unknown>, filename: string | null }>} */
79
+ const _remoteGlbCache = new Map();
80
+
81
+ /**
82
+ * Fetch the JSON chunk of a remote GLB.
83
+ * Tries an initial Range request for the header bytes; if the server already returned
84
+ * enough data (200 + full body, or 206 with sufficient bytes) the JSON chunk is
85
+ * extracted directly. Otherwise a second Range request fetches the JSON chunk.
60
86
  * Uses `Last-Modified` header for caching — avoids re-parsing if unchanged.
61
- * Returns `null` on any network/parse error or if Range is not supported.
87
+ * Also captures `Content-Disposition` filename for friendly key generation.
88
+ * Returns `null` on any network or parse error.
62
89
  *
63
90
  * @param {string} url
64
- * @returns {Promise<Record<string, unknown> | null>}
91
+ * @returns {Promise<{ json: Record<string, unknown>, filename: string | null } | null>}
65
92
  */
66
93
  export async function readRemoteGlbJsonChunk(url) {
67
94
  try {
@@ -69,46 +96,72 @@ export async function readRemoteGlbJsonChunk(url) {
69
96
 
70
97
  const headerRes = await fetch(url, { headers: { Range: "bytes=0-19" } });
71
98
  if (!headerRes.ok) {
72
- console.warn(`[needle:dts-generator] Remote GLB fetch failed (${headerRes.status}): ${url}`);
99
+ needleLog(PLUGIN, `Remote GLB fetch failed (${headerRes.status}): ${url}`, "warn");
73
100
  return null;
74
101
  }
75
102
 
76
103
  const lastModified = headerRes.headers.get("Last-Modified") ?? "";
104
+ const filename = parseContentDispositionFilename(headerRes.headers.get("Content-Disposition"));
105
+
77
106
  if (cached && lastModified && cached.lastModified === lastModified) {
78
- return cached.json;
107
+ // Prefer the filename we resolved during the full fetch (may have come from the JSON chunk
108
+ // response). Only fall back to the fresh header value if the cache has nothing.
109
+ return { json: cached.json, filename: cached.filename ?? filename };
79
110
  }
80
111
 
81
- const rangeSupported = headerRes.status === 206;
82
- const headerBytes = Buffer.from(await headerRes.arrayBuffer());
83
- if (headerBytes.length < 20) return null;
112
+ const firstBytes = Buffer.from(await headerRes.arrayBuffer());
113
+ if (firstBytes.length < 20) return null;
84
114
 
85
- const magic = headerBytes.readUInt32LE(0);
86
- const version = headerBytes.readUInt32LE(4);
87
- const chunkLength = headerBytes.readUInt32LE(12);
88
- const chunkType = headerBytes.readUInt32LE(16);
115
+ const magic = firstBytes.readUInt32LE(0);
116
+ const version = firstBytes.readUInt32LE(4);
117
+ const chunkLength = firstBytes.readUInt32LE(12);
118
+ const chunkType = firstBytes.readUInt32LE(16);
89
119
 
90
120
  if (magic !== 0x46546c67 || version !== 2) return null; // not a GLB
91
121
  if (chunkType !== 0x4E4F534A) return null; // chunk0 not JSON
92
122
 
93
- if (!rangeSupported) {
94
- console.warn(`[needle:dts-generator] Remote GLB server does not support Range requests, skipping: ${url}`);
95
- return null;
123
+ let jsonBytes;
124
+ let resolvedFilename = filename;
125
+
126
+ if (firstBytes.length >= 20 + chunkLength) {
127
+ // The first response already contained the full JSON chunk (server ignored Range or returned 200).
128
+ jsonBytes = firstBytes.slice(20, 20 + chunkLength);
129
+ } else {
130
+ // Try a second Range request for just the JSON chunk bytes.
131
+ const jsonEnd = 20 + chunkLength - 1;
132
+ const jsonRes = await fetch(url, { headers: { Range: `bytes=20-${jsonEnd}` } });
133
+ if (!jsonRes.ok) {
134
+ needleLog(PLUGIN, `Remote GLB JSON chunk fetch failed (${jsonRes.status}): ${url}`, "warn");
135
+ return null;
136
+ }
137
+ const jsonResBytes = Buffer.from(await jsonRes.arrayBuffer());
138
+ // Server may return 200 + full body even for a Range request — slice out our window.
139
+ if (jsonResBytes.length >= 20 + chunkLength) {
140
+ jsonBytes = jsonResBytes.slice(20, 20 + chunkLength);
141
+ } else if (jsonResBytes.length >= chunkLength) {
142
+ jsonBytes = jsonResBytes.slice(0, chunkLength);
143
+ } else {
144
+ needleLog(PLUGIN, `Remote GLB unexpected response (${jsonRes.status}, ${jsonResBytes.length} bytes, expected ${chunkLength}): ${url}`, "warn");
145
+ return null;
146
+ }
147
+
148
+ const filenameFromJsonRes = parseContentDispositionFilename(jsonRes.headers.get("Content-Disposition"));
149
+ resolvedFilename = filename ?? filenameFromJsonRes;
96
150
  }
97
151
 
98
- const jsonEnd = 20 + chunkLength - 1;
99
- const jsonRes = await fetch(url, { headers: { Range: `bytes=20-${jsonEnd}` } });
100
- if (!jsonRes.ok) {
101
- console.warn(`[needle:dts-generator] Remote GLB JSON chunk fetch failed (${jsonRes.status}): ${url}`);
102
- return null;
152
+ if (resolvedFilename === null) {
153
+ try {
154
+ const headRes = await fetch(url, { method: "HEAD" });
155
+ resolvedFilename = parseContentDispositionFilename(headRes.headers.get("Content-Disposition"));
156
+ } catch (_e) { /* ignore — fall back to URL-based name */ }
103
157
  }
104
-
105
158
  const json = /** @type {Record<string, unknown>} */ (
106
- JSON.parse(Buffer.from(await jsonRes.arrayBuffer()).toString("utf8").replace(/\u0000+$/g, ""))
159
+ JSON.parse(jsonBytes.toString("utf8").replace(/\u0000+$/g, ""))
107
160
  );
108
- _remoteGlbCache.set(url, { lastModified, json });
109
- return json;
161
+ _remoteGlbCache.set(url, { lastModified, json, filename: resolvedFilename });
162
+ return { json, filename: resolvedFilename };
110
163
  } catch (e) {
111
- console.warn(`[needle:dts-generator] Failed to fetch remote GLB: ${url} —`, /** @type {any} */ (e)?.message ?? e);
164
+ needleLog(PLUGIN, `Failed to fetch remote GLB: ${url} ${/** @type {any} */ (e)?.message ?? e}`, "warn");
112
165
  return null;
113
166
  }
114
167
  }
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Typical generated output:
10
10
  *
11
- * declare module "needle:bindings" {
11
+ * declare module "needle-bindings" {
12
12
  * interface SceneData {
13
13
  * Sphere: {
14
14
  * MyBall: { speed: number; label: string; };
@@ -1,19 +1,30 @@
1
1
  /**
2
- * Ambient module declaration for `needle:bindings`.
2
+ * Ambient module declaration for `needle-bindings`.
3
3
  *
4
- * The `SceneData` interface is empty by default.
5
- * When the `needle:dts-generator` Vite plugin is active it writes
6
- * `src/generated/needle-bindings.d.ts` into the user's project, which
7
- * augments this interface with the actual component bindings extracted
8
- * from the project's GLB files at build time.
4
+ * `SceneData` is keyed by GLB friendly name, then by the node hierarchy:
5
+ * ctx.sceneData.Minimal.Camera.$object // THREE.Camera
6
+ * ctx.sceneData.Minimal.Camera.$components.OrbitControls.autoRotate = true;
7
+ * ctx.sceneData.Minimal.UI.Button.$components.Button
8
+ *
9
+ * Each node entry has:
10
+ * $object — the Three.js Object3D (typed precisely, e.g. Mesh, Camera, Light)
11
+ * $components — Needle components attached to this node
12
+ * [childName] — child nodes, recursively typed
9
13
  *
10
- * @example
11
- * import type { SceneData } from "@needle-tools/engine";
12
- * type MyBallData = SceneData["RedBall"]["MyBall"];
14
+ * When the `needle:dts-generator` Vite plugin is active it writes
15
+ * `.needle/generated/needle-bindings.gen.d.ts` next to the installed package,
16
+ * which augments this interface with the actual bindings extracted from
17
+ * the project's GLB files at build time.
13
18
  */
14
- declare module "needle:bindings" {
15
- interface SceneData {
16
- /** Fallback for nodes not present in the generated bindings — no type info, but no crash. */
17
- [nodeName: string]: Record<string, unknown> | undefined;
18
- }
19
+
20
+ // Pull in the project-local generated augmentation (written by needle:dts-generator).
21
+ /// <reference path="../../.needle/generated/needle-bindings.gen.d.ts" />
22
+
23
+ declare module "needle-bindings" {
24
+ /**
25
+ * Scene data keyed by GLB friendly name, then by node hierarchy.
26
+ * Fallback index signature allows unknown names without crashing.
27
+ */
28
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
29
+ interface SceneData {}
19
30
  }
@@ -82,7 +82,10 @@ export type userSettings = {
82
82
  */
83
83
  version?: string;
84
84
 
85
- /** If defined the access token will be used to run compression on Needle Cloud */
85
+ /** If defined the access token will be used to run compression on Needle Cloud.
86
+ *
87
+ * Expected to be a Needle Cloud access token (created in the Needle Cloud UI),
88
+ * NOT a JWT. Do not pass a licensing JWT here. */
86
89
  accessToken?: string | undefined;
87
90
 
88
91
  /**
@@ -176,4 +179,16 @@ export type userSettings = {
176
179
 
177
180
  /** Set to true to disable the plugin that ensures VSCode workspace settings for custom-elements.json data */
178
181
  noCustomElementData?: boolean;
182
+
183
+ /**
184
+ * Generate Typescript declaration files for references 3D assets in your project.
185
+ * These will be available via `context.sceneData` in your code.
186
+ * @default enabled
187
+ */
188
+ dts?: {
189
+ /** When set to false, disables the generation of TypeScript declaration files.
190
+ * @default true
191
+ */
192
+ enabled?: boolean;
193
+ }
179
194
  }
@@ -22,7 +22,7 @@ export async function needleAsap(command, config, userSettings) {
22
22
 
23
23
  fixMainTs();
24
24
 
25
- if (command != "build") {
25
+ if (command === "serve") {
26
26
  return null;
27
27
  }
28
28
 
@@ -50,7 +50,7 @@ export async function needleAsap(command, config, userSettings) {
50
50
  const tags = [];
51
51
 
52
52
  try {
53
- generateGltfPreloadLinks(config, html, tags);
53
+ generateGltfPreloadLinks(config, html, tags, viteConfig?.base);
54
54
  }
55
55
  catch (err) {
56
56
  console.error("Error generating gltf preload links", err);
@@ -173,18 +173,21 @@ function fixMainTs() {
173
173
  * @param {import('vite').ResolvedConfig} _config
174
174
  * @param {import('vite').HtmlTagDescriptor[]} tags
175
175
  */
176
- function generateScriptPreloadLinks(_config, tags) {
176
+ function generateScriptPreloadLinks(config, tags) {
177
177
  try {
178
+ const base = config.base || '/';
178
179
  const chunks = preloadScriptPaths;
179
180
  // console.log("ASAP", chunks)
180
181
  if (chunks.length > 0) {
181
182
  for (const chunk of chunks) {
183
+ // Apply base path so preload hrefs resolve correctly under SPA routing
184
+ const href = chunk.startsWith('./') ? base + chunk.slice(2) : chunk;
182
185
  tags.push({
183
186
  tag: 'link',
184
187
  attrs: {
185
188
  rel: "modulepreload",
186
189
  as: "script",
187
- href: chunk,
190
+ href: href,
188
191
  }
189
192
  });
190
193
  }
@@ -203,15 +206,16 @@ const codegenRegex = /\"(?<gltf>.+(.glb|.gltf)(\?.*)?)\"/gm;
203
206
  /**
204
207
  * @param {import('../types').needleConfig} config
205
208
  * @param {string} html
206
- * @param {import('vite').HtmlTagDescriptor[]} tags
209
+ * @param {import('vite').HtmlTagDescriptor[]} tags
210
+ * @param {string} [base]
207
211
  **/
208
- function generateGltfPreloadLinks(config, html, tags) {
212
+ function generateGltfPreloadLinks(config, html, tags, base) {
209
213
 
210
214
  // TODO: try to get the <needle-engine src> element src attribute and preload that
211
215
  const needleEngineMatches = tryParseNeedleEngineSrcAttributeFromHtml(html);
212
216
  if (needleEngineMatches?.length) {
213
217
  for (const item of needleEngineMatches) {
214
- insertPreloadLink(tags, item, "model/gltf+json");
218
+ insertPreloadLink(tags, item, "model/gltf+json", base);
215
219
  }
216
220
  }
217
221
 
@@ -246,7 +250,7 @@ function generateGltfPreloadLinks(config, html, tags) {
246
250
  }
247
251
  }
248
252
  needleLog("needle:asap", `Insert glTF preload link: ${value}`);
249
- insertPreloadLink(tags, value, "model/gltf+json");
253
+ insertPreloadLink(tags, value, "model/gltf+json", base);
250
254
  }
251
255
  }
252
256
  }
@@ -258,9 +262,14 @@ function generateGltfPreloadLinks(config, html, tags) {
258
262
  * @param {import('vite').HtmlTagDescriptor[]} tags
259
263
  * @param {string} href
260
264
  * @param {string} type
265
+ * @param {string} [base]
261
266
  */
262
- function insertPreloadLink(tags, href, type) {
267
+ function insertPreloadLink(tags, href, type, base) {
263
268
  if (!href) return;
269
+ // Apply base path so preload hrefs resolve correctly under SPA routing
270
+ if (base && !href.startsWith('http') && !href.startsWith('/')) {
271
+ href = base + (href.startsWith('./') ? href.slice(2) : href);
272
+ }
264
273
  tags.push({
265
274
  tag: 'link',
266
275
  attrs: {