cozyclay 2.1.1 → 3.0.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 (577) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +3 -3
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-ChUvB_zL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/sw.js +3 -6
  40. package/dist/workflow/index.html +5 -4
  41. package/mcp/LIVE-PROTOCOL.md +7 -47
  42. package/mcp/README.md +16 -26
  43. package/mcp/live-hub.mjs +6 -10
  44. package/mcp/package.json +1 -3
  45. package/mcp/tool-handlers.mjs +4 -168
  46. package/package.json +3 -22
  47. package/src/App.jsx +150 -2624
  48. package/src/agent-progress.js +79 -0
  49. package/src/agent-turn-mark.js +30 -0
  50. package/src/analytics.js +1 -19
  51. package/src/app-context.js +5 -0
  52. package/src/app-stage.jsx +2 -34
  53. package/src/asset-pane.jsx +6 -5
  54. package/src/camera-block.js +21 -0
  55. package/src/camera-direction.js +120 -0
  56. package/src/character-kind.js +3 -9
  57. package/src/command-bus.js +8 -6
  58. package/src/commands/cast.js +5 -34
  59. package/src/commands/elements/character.js +1 -1
  60. package/src/commands/elements/motion.js +1 -6
  61. package/src/commands/elements/object.js +6 -2
  62. package/src/commands/elements/shot.js +4 -1
  63. package/src/commands/elements.js +38 -14
  64. package/src/commands/index.js +3 -1
  65. package/src/commands/kits.js +37 -0
  66. package/src/commands/motion-timing.js +57 -0
  67. package/src/commands/motion.js +9 -152
  68. package/src/commands/motion3-compile.js +65 -0
  69. package/src/commands/motion3.js +207 -0
  70. package/src/commands/objects.js +6 -4
  71. package/src/commands/shot.js +49 -11
  72. package/src/commands/stage.js +14 -1
  73. package/src/commands/view.js +4 -1
  74. package/src/document-mirrors.js +42 -0
  75. package/src/document-store.js +4 -0
  76. package/src/domains/cast.js +19 -194
  77. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  78. package/src/domains/motion/take-source.js +51 -0
  79. package/src/domains/motion.js +60 -1838
  80. package/src/domains/scenes.js +12 -55
  81. package/src/domains/shots.js +2 -2
  82. package/src/domains/stage.js +4 -2
  83. package/src/draft-ghost.js +46 -0
  84. package/src/draft-ghost.jsx +129 -0
  85. package/src/dualview.jsx +4 -3
  86. package/src/effects/EffectsLayer.jsx +112 -0
  87. package/src/effects/effect-meshes.js +232 -0
  88. package/src/effects/effect-record.js +103 -0
  89. package/src/effects/emitter.js +59 -0
  90. package/src/effects/simulate.js +261 -0
  91. package/src/effects/state.js +59 -0
  92. package/src/execution-telemetry.js +2 -3
  93. package/src/first-success-guide.jsx +5 -5
  94. package/src/ghost-set.js +257 -0
  95. package/src/held-object-edit.jsx +113 -0
  96. package/src/held-object-pose.js +175 -0
  97. package/src/hierarchy-model.js +1 -1
  98. package/src/hierarchy-panel.jsx +1 -1
  99. package/src/imported-mesh.jsx +103 -0
  100. package/src/kits/held.js +84 -0
  101. package/src/kits/index.js +70 -0
  102. package/src/kits/smoking.js +57 -0
  103. package/src/light-blockers.js +93 -0
  104. package/src/motion/beat-board-model.js +84 -0
  105. package/src/motion/beat-board.css +124 -0
  106. package/src/motion/beat-board.jsx +59 -0
  107. package/src/motion/center-of-mass.js +66 -0
  108. package/src/motion/fix-collisions.js +850 -0
  109. package/src/motion/physics-review.js +118 -0
  110. package/src/{ardy → motion}/physics-support.js +1 -76
  111. package/src/motion/rig-snapshot.js +22 -0
  112. package/src/motion/take-segments.jsx +140 -0
  113. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  114. package/src/{ardy → motion}/timeline.css +1 -2
  115. package/src/{ardy → motion}/timeline.jsx +42 -413
  116. package/src/motion/waypoints.js +76 -0
  117. package/src/motion3/beats.js +83 -0
  118. package/src/motion3/body.js +158 -0
  119. package/src/motion3/bones.js +47 -0
  120. package/src/motion3/carried.js +47 -0
  121. package/src/motion3/compile-job.js +75 -0
  122. package/src/motion3/compile-worker.js +10 -0
  123. package/src/motion3/compiler/anchors.js +105 -0
  124. package/src/motion3/compiler/arm-orient.js +128 -0
  125. package/src/motion3/compiler/channels.js +125 -0
  126. package/src/motion3/compiler/compile.js +205 -0
  127. package/src/motion3/compiler/gait-rhythm.js +97 -0
  128. package/src/motion3/compiler/grip.js +171 -0
  129. package/src/motion3/compiler/head-steady.js +48 -0
  130. package/src/motion3/compiler/hold.js +134 -0
  131. package/src/motion3/compiler/idle.js +113 -0
  132. package/src/motion3/compiler/limbs.js +170 -0
  133. package/src/motion3/compiler/math.js +72 -0
  134. package/src/motion3/compiler/pose.js +162 -0
  135. package/src/motion3/compiler/quat.js +49 -0
  136. package/src/motion3/compiler/skeleton.js +74 -0
  137. package/src/motion3/compiler/validate-common.js +32 -0
  138. package/src/motion3/compiler/validate-goals.js +118 -0
  139. package/src/motion3/compiler/validate-objects.js +143 -0
  140. package/src/motion3/compiler/validate-parts.js +116 -0
  141. package/src/motion3/compiler/validate.js +122 -0
  142. package/src/motion3/compiler/vocab.js +73 -0
  143. package/src/motion3/compiler/walk-pelvis.js +275 -0
  144. package/src/motion3/compiler/walk.js +207 -0
  145. package/src/motion3/compiler/wrist-clamp.js +32 -0
  146. package/src/motion3/delta.js +47 -0
  147. package/src/motion3/describe.js +174 -0
  148. package/src/motion3/docs.js +27 -0
  149. package/src/motion3/edit-carry.js +21 -0
  150. package/src/motion3/fix/ease.js +16 -0
  151. package/src/motion3/fix/ground.js +37 -0
  152. package/src/motion3/fix/index.js +52 -0
  153. package/src/motion3/fix/push.js +71 -0
  154. package/src/motion3/fix/reach.js +46 -0
  155. package/src/motion3/fix/retime.js +79 -0
  156. package/src/motion3/fix/self.js +211 -0
  157. package/src/motion3/fix/slide.js +70 -0
  158. package/src/motion3/fix/turn.js +51 -0
  159. package/src/motion3/follow.js +39 -0
  160. package/src/motion3/free-motion.js +173 -0
  161. package/src/motion3/grip-choice.js +130 -0
  162. package/src/motion3/hold-plan.js +157 -0
  163. package/src/motion3/json-patch.js +77 -0
  164. package/src/motion3/key-aim.js +105 -0
  165. package/src/motion3/key-edit.js +82 -0
  166. package/src/motion3/lenient.js +127 -0
  167. package/src/motion3/object-keys.js +236 -0
  168. package/src/motion3/object-pose.js +182 -0
  169. package/src/motion3/object-tracks.js +12 -0
  170. package/src/motion3/pipeline.js +102 -0
  171. package/src/motion3/playback.js +51 -0
  172. package/src/motion3/probes.js +91 -0
  173. package/src/motion3/qa/body-checks.js +34 -0
  174. package/src/motion3/qa/check-names.js +14 -0
  175. package/src/motion3/qa/checks.js +121 -0
  176. package/src/motion3/qa/contact-checks.js +51 -0
  177. package/src/motion3/qa/explain.js +80 -0
  178. package/src/motion3/qa/fmt.js +5 -0
  179. package/src/motion3/qa/foot-checks.js +72 -0
  180. package/src/motion3/qa/geometry.js +77 -0
  181. package/src/motion3/qa/goals.js +191 -0
  182. package/src/motion3/qa/ground.js +60 -0
  183. package/src/motion3/qa/hints-contact.js +179 -0
  184. package/src/motion3/qa/hints-goal.js +160 -0
  185. package/src/motion3/qa/hints-knee.js +24 -0
  186. package/src/motion3/qa/hints-motion.js +209 -0
  187. package/src/motion3/qa/hints-object.js +91 -0
  188. package/src/motion3/qa/index.js +42 -0
  189. package/src/motion3/qa/key-checks.js +82 -0
  190. package/src/motion3/qa/near.js +96 -0
  191. package/src/motion3/qa/object-checks.js +135 -0
  192. package/src/motion3/qa/patch.js +161 -0
  193. package/src/motion3/qa/severity.js +38 -0
  194. package/src/motion3/qa/spec-checks.js +65 -0
  195. package/src/motion3/qa/style.js +218 -0
  196. package/src/motion3/qa/wrist-checks.js +69 -0
  197. package/src/motion3/qa/wrist-obs.js +60 -0
  198. package/src/motion3/refs.js +120 -0
  199. package/src/motion3/repair-tune.js +121 -0
  200. package/src/motion3/repair.js +83 -0
  201. package/src/motion3/report.js +67 -0
  202. package/src/motion3/rigs/index.js +14 -0
  203. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  204. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  205. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  207. package/src/motion3/scene.js +60 -0
  208. package/src/motion3/schema/brief.js +89 -0
  209. package/src/motion3/schema/fields.js +44 -0
  210. package/src/motion3/schema/spec-examples.js +46 -0
  211. package/src/motion3/schema/topics.js +111 -0
  212. package/src/motion3/schema/types.js +147 -0
  213. package/src/motion3/search.js +117 -0
  214. package/src/motion3/second-hand.js +112 -0
  215. package/src/motion3/skills/basic.js +82 -0
  216. package/src/motion3/skills/book.js +76 -0
  217. package/src/motion3/skills/car.js +145 -0
  218. package/src/motion3/skills/frames-auto.js +29 -0
  219. package/src/motion3/skills/index.js +83 -0
  220. package/src/motion3/skills/info.js +65 -0
  221. package/src/motion3/skills/smoke-kit.js +109 -0
  222. package/src/motion3/skills/smoke.js +160 -0
  223. package/src/motion3/spec-store.js +59 -0
  224. package/src/motion3/take-edit.js +155 -0
  225. package/src/motion3/take.js +63 -0
  226. package/src/motion3/timeline.js +127 -0
  227. package/src/motion3/views.js +121 -0
  228. package/src/motion3/walk-plan.js +119 -0
  229. package/src/object-catalog.jsx +7 -1
  230. package/src/object-colors.js +98 -0
  231. package/src/object-gizmo-parts.js +115 -0
  232. package/src/object-gizmo.jsx +37 -122
  233. package/src/object-glow.js +139 -0
  234. package/src/object-hidden.js +33 -0
  235. package/src/object-look.js +196 -0
  236. package/src/object-size.js +10 -0
  237. package/src/object-travel.js +71 -6
  238. package/src/panels/CameraPanel.jsx +7 -7
  239. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  240. package/src/panels/LightPanel.jsx +25 -4
  241. package/src/panels/ObjectLookPanel.jsx +88 -0
  242. package/src/panels/PosePanel.jsx +2 -17
  243. package/src/panels/RigControlPanel.jsx +5 -53
  244. package/src/panels/SubjectBox.jsx +4 -1
  245. package/src/panels/details.css +14 -0
  246. package/src/posestudio.jsx +4 -27
  247. package/src/project-browser.jsx +3 -4
  248. package/src/project-resources.js +18 -31
  249. package/src/project.js +13 -104
  250. package/src/props.jsx +25 -280
  251. package/src/rail-dot-drag.js +23 -0
  252. package/src/render-passes.js +10 -0
  253. package/src/resource-status.jsx +1 -0
  254. package/src/result-modal.jsx +4 -19
  255. package/src/room.jsx +20 -12
  256. package/src/scene-assets.js +8 -6
  257. package/src/scene-light-place.js +72 -0
  258. package/src/scene-lights.jsx +148 -0
  259. package/src/scene-objects.js +31 -139
  260. package/src/scenes.js +15 -105
  261. package/src/semantic-edit.js +2 -5
  262. package/src/set-pieces.jsx +183 -0
  263. package/src/settings-menu.jsx +3 -11
  264. package/src/shell/BottomDock.jsx +9 -51
  265. package/src/shell/DetailsSlot.jsx +16 -200
  266. package/src/shell/GhostsMenuGroup.jsx +74 -0
  267. package/src/shell/LibrarySlot.jsx +1 -3
  268. package/src/shell/MenuBar.jsx +3 -3
  269. package/src/shell/PreferencesDialog.jsx +15 -264
  270. package/src/shell/PreferencesSlot.jsx +2 -8
  271. package/src/shell/TopBar.jsx +5 -84
  272. package/src/shell/ViewportToolbar.jsx +10 -70
  273. package/src/shell/agent-glass.css +19 -6
  274. package/src/shell/details-selection.js +42 -0
  275. package/src/shell/ghosts-menu.css +22 -0
  276. package/src/shell/glass-regions.css +29 -3
  277. package/src/shell/preferences.css +6 -1
  278. package/src/shot-authoring.js +4 -7
  279. package/src/soft-shadows.js +37 -0
  280. package/src/stage-attach-frames.js +12 -12
  281. package/src/stage-canvas-rigs.jsx +3 -3
  282. package/src/stage-capture.jsx +42 -8
  283. package/src/stage-characters.jsx +22 -216
  284. package/src/stage-editor-gizmos.jsx +6 -6
  285. package/src/stage-environment.js +93 -0
  286. package/src/stage-hdr-builtins.js +107 -0
  287. package/src/stage-hdr-image.js +116 -0
  288. package/src/stage-hdr-library.js +70 -0
  289. package/src/stage-hdr-map.js +94 -0
  290. package/src/stage-hdr.js +130 -0
  291. package/src/stage-lighting.js +149 -0
  292. package/src/stage-occlusion.js +210 -0
  293. package/src/stage-sky.jsx +159 -0
  294. package/src/stage-startup-defaults.js +5 -9
  295. package/src/studio-actions.js +12 -14
  296. package/src/studio-agent-commands.js +19 -16
  297. package/src/studio-agent-motion.js +9 -9
  298. package/src/studio-agent-protocol.js +41 -71
  299. package/src/studio-app-binding.js +25 -45
  300. package/src/studio-asset-list.js +17 -0
  301. package/src/studio-contact-sheet.js +37 -5
  302. package/src/studio-elements.js +27 -7
  303. package/src/studio-entity-bounds.js +18 -0
  304. package/src/studio-error-codes.js +8 -0
  305. package/src/studio-export-framing.js +152 -0
  306. package/src/studio-lighting-check.js +103 -0
  307. package/src/studio-motion-visual.js +150 -0
  308. package/src/studio-schema-errors.js +80 -0
  309. package/src/studio-support-gap.js +74 -0
  310. package/src/styles.css +1 -19
  311. package/src/timeline-defaults.js +5 -0
  312. package/src/timeline-extent.js +8 -20
  313. package/src/tutorial-walk.js +17 -0
  314. package/src/ui-scrub.js +1 -1
  315. package/src/use-shot-camera-tracks.js +12 -0
  316. package/src/view-ghosts.js +97 -0
  317. package/src/view-ghosts.jsx +49 -0
  318. package/src/workflow/AgentPanel.jsx +47 -166
  319. package/src/workflow/AgentProgressCard.jsx +107 -0
  320. package/src/workflow/AgentReplyText.jsx +37 -0
  321. package/src/workflow/AgentTurnSteps.jsx +84 -0
  322. package/src/workflow/LoginPanel.jsx +140 -0
  323. package/src/workflow/WorkflowBuilder.jsx +7 -23
  324. package/src/workflow/agent-client.js +54 -51
  325. package/src/workflow/agent-panel.css +71 -1
  326. package/src/workflow/agent-progress-card.css +167 -0
  327. package/src/workflow/agent-progress-view.js +47 -0
  328. package/src/workflow/agent-turn-steps.css +113 -0
  329. package/src/workflow/agent-turn.js +191 -0
  330. package/src/workflow/login-client.js +34 -0
  331. package/src/workflow/motion-input.js +18 -31
  332. package/src/workflow/node-schema.js +1 -1
  333. package/src/workflow/reply-markdown.js +69 -0
  334. package/src/workflow/scene-asset-sync.js +8 -8
  335. package/tools/agent-session-stats.mjs +61 -0
  336. package/tools/dev-full.mjs +5 -56
  337. package/tools/eslint-baseline.json +12 -13
  338. package/tools/knip-baseline.json +0 -179
  339. package/tools/loc-baseline.json +31 -81
  340. package/tools/motion3/extract-body.mjs +103 -0
  341. package/tools/motion3/extract-rig.mjs +85 -0
  342. package/tools/process-supervisor.mjs +0 -88
  343. package/tools/run-tests.mjs +8 -124
  344. package/bin/agent/motion-runtime.mjs +0 -76
  345. package/dist/ardy/cskel27-rest.json +0 -557
  346. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  347. package/dist/assets/app-B_2XB9rw.css +0 -1
  348. package/dist/assets/app-DAuvKPP8.js +0 -4920
  349. package/dist/assets/config-Bx7pYsD9.js +0 -1
  350. package/dist/assets/config-DeoFTYzb.css +0 -1
  351. package/dist/assets/demo-CvKuBgy5.js +0 -1
  352. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  353. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  354. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  355. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  356. package/dist/demo/walk-then-stop.npz +0 -0
  357. package/mcp/ardy-prompts.mjs +0 -218
  358. package/src/ardy/auto-fix-panel.css +0 -108
  359. package/src/ardy/auto-physics.js +0 -1194
  360. package/src/ardy/client.js +0 -153
  361. package/src/ardy/convert.js +0 -313
  362. package/src/ardy/cskel27-neutral.js +0 -39
  363. package/src/ardy/cskel27.js +0 -77
  364. package/src/ardy/export.js +0 -165
  365. package/src/ardy/fix-collisions.js +0 -2257
  366. package/src/ardy/foot-lock.js +0 -230
  367. package/src/ardy/motion-calibration.js +0 -118
  368. package/src/ardy/motion-edit.js +0 -213
  369. package/src/ardy/motion-url.js +0 -55
  370. package/src/ardy/mp4-duration.js +0 -102
  371. package/src/ardy/npz.js +0 -595
  372. package/src/ardy/physics-panel.css +0 -21
  373. package/src/ardy/physics-panel.jsx +0 -89
  374. package/src/ardy/physics-refine.js +0 -111
  375. package/src/ardy/physics-review.js +0 -567
  376. package/src/ardy/physics-temporal.js +0 -54
  377. package/src/ardy/platform-fit-panel.css +0 -64
  378. package/src/ardy/platform-fit-panel.jsx +0 -41
  379. package/src/ardy/platform-fit.js +0 -326
  380. package/src/ardy/playback.js +0 -711
  381. package/src/ardy/pose-pin.js +0 -97
  382. package/src/ardy/prompt-clips.js +0 -30
  383. package/src/ardy/retime.js +0 -211
  384. package/src/ardy/root-drop.js +0 -369
  385. package/src/ardy/timeline-resize.js +0 -13
  386. package/src/ardy/to-cskel27.js +0 -225
  387. package/src/ardy/trim.js +0 -33
  388. package/src/ardy/waypoints.js +0 -369
  389. package/src/ardy-bridge-contract.js +0 -281
  390. package/src/domains/motion/motion-video-capture.js +0 -664
  391. package/src/i2v-motion-client.js +0 -77
  392. package/src/i2v-motion-studio.jsx +0 -180
  393. package/src/line-edit.js +0 -1348
  394. package/src/motion/generation.js +0 -105
  395. package/src/motion-readiness-ui.jsx +0 -67
  396. package/src/motion-readiness.js +0 -40
  397. package/src/motion-resources.js +0 -177
  398. package/src/motion-store.js +0 -65
  399. package/src/motion-trail.js +0 -601
  400. package/src/multimodel-ingest.js +0 -394
  401. package/src/panels/PromptBlocksPanel.jsx +0 -444
  402. package/src/panels/VideoCapturePanel.jsx +0 -181
  403. package/src/pose-extract/browser.js +0 -42
  404. package/src/pose-extract/detector.js +0 -100
  405. package/src/pose-extract/fit.js +0 -227
  406. package/src/pose-extract/image-frame.js +0 -51
  407. package/src/pose-extract/index.js +0 -58
  408. package/src/pose-extract/keyframes.js +0 -74
  409. package/src/pose-extract/landmarks.js +0 -162
  410. package/src/pose-extract/mirror.js +0 -153
  411. package/src/pose-extract/take.js +0 -131
  412. package/src/pose-extract/video-frames.js +0 -91
  413. package/src/take-recipe.js +0 -165
  414. package/src/trail-key-conflicts.js +0 -39
  415. package/src/trail-pick.js +0 -59
  416. package/src/use-i2v-capture-modal.js +0 -15
  417. package/tools/ardy/BRIDGE.md +0 -37
  418. package/tools/ardy/GVHMR-NPZ.md +0 -68
  419. package/tools/ardy/README.md +0 -124
  420. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  421. package/tools/ardy/artifacts.mjs +0 -34
  422. package/tools/ardy/bridge.mjs +0 -1732
  423. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  424. package/tools/ardy/cclay_constrained_generate.py +0 -561
  425. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  426. package/tools/ardy/cclay_motion_edit.py +0 -510
  427. package/tools/ardy/cclay_sequence_generate.py +0 -705
  428. package/tools/ardy/cclay_worker.py +0 -304
  429. package/tools/ardy/dump-npz.py +0 -205
  430. package/tools/ardy/extract-rest.mjs +0 -299
  431. package/tools/ardy/extract.mjs +0 -507
  432. package/tools/ardy/footage.mjs +0 -467
  433. package/tools/ardy/gvhmr-floor.mjs +0 -102
  434. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  435. package/tools/ardy/gvhmr_fastpath.py +0 -168
  436. package/tools/ardy/gvhmr_trajectory.py +0 -240
  437. package/tools/ardy/merge-text-encoder.py +0 -160
  438. package/tools/ardy/mocap-metrics.mjs +0 -200
  439. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  440. package/tools/ardy/motion-stabilize.mjs +0 -348
  441. package/tools/ardy/npz.mjs +0 -414
  442. package/tools/ardy/pose-to-npz.mjs +0 -106
  443. package/tools/ardy/prompt-limits.mjs +0 -5
  444. package/tools/ardy/run-edit-on-box.sh +0 -73
  445. package/tools/ardy/run-local.mjs +0 -233
  446. package/tools/ardy/run-on-box.sh +0 -593
  447. package/tools/ardy/run-sequence-on-box.sh +0 -177
  448. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  449. package/tools/ardy/runners/index.mjs +0 -18
  450. package/tools/ardy/runners/local.mjs +0 -455
  451. package/tools/ardy/runners/proc.mjs +0 -125
  452. package/tools/ardy/runners/remote.mjs +0 -252
  453. package/tools/ardy/setup-local.mjs +0 -189
  454. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  455. package/tools/ardy/setup-text-encoder.py +0 -306
  456. package/tools/ardy/smpl-cskel27.mjs +0 -178
  457. package/tools/ardy/visual-qa.mjs +0 -196
  458. package/tools/ardy/vq-car.mjs +0 -52
  459. package/tools/bench/EXP3.md +0 -61
  460. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  461. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  462. package/tools/bench/cclay_bench_runner.py +0 -42
  463. package/tools/bench/cube-contact.mjs +0 -162
  464. package/tools/bench/exp3.mjs +0 -152
  465. package/tools/bench/extract-bench-lib.mjs +0 -216
  466. package/tools/bench/extract-bench.mjs +0 -302
  467. package/tools/bench/fal-generate.mjs +0 -88
  468. package/tools/bench/fit/README.md +0 -189
  469. package/tools/bench/fit/camera.mjs +0 -59
  470. package/tools/bench/fit/contact.mjs +0 -419
  471. package/tools/bench/fit/footlock.mjs +0 -234
  472. package/tools/bench/fit/motion.mjs +0 -85
  473. package/tools/bench/fit/pin.mjs +0 -27
  474. package/tools/bench/fit/remote.mjs +0 -105
  475. package/tools/bench/fit-bench.mjs +0 -126
  476. package/tools/bench/fit-sanity.mjs +0 -45
  477. package/tools/bench/metrics.mjs +0 -313
  478. package/tools/bench/obs/depth.mjs +0 -258
  479. package/tools/bench/obs/extrinsics.mjs +0 -175
  480. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  481. package/tools/bench/obs/ground.mjs +0 -294
  482. package/tools/bench/obs/heading.mjs +0 -151
  483. package/tools/bench/obs/ladder.mjs +0 -555
  484. package/tools/bench/obs/mannequin-betas.json +0 -124
  485. package/tools/bench/obs/remote.mjs +0 -83
  486. package/tools/bench/obs/rest_joints.py +0 -52
  487. package/tools/bench/obs/ybot-targets.mjs +0 -49
  488. package/tools/bench/obs-bench.mjs +0 -520
  489. package/tools/bench/score.mjs +0 -343
  490. package/tools/bench/summarize.mjs +0 -86
  491. package/tools/demo-worker/README.md +0 -137
  492. package/tools/demo-worker/api-client.mjs +0 -233
  493. package/tools/demo-worker/generate.mjs +0 -301
  494. package/tools/demo-worker/index.mjs +0 -466
  495. package/tools/demo-worker/sign-core.mjs +0 -112
  496. package/tools/demo-worker/sign.mjs +0 -161
  497. package/tools/gt-render/browser.mjs +0 -182
  498. package/tools/gt-render/camera-math.mjs +0 -276
  499. package/tools/gt-render/page.mjs +0 -267
  500. package/tools/gt-render/render.mjs +0 -514
  501. package/tools/gt-render/scene-box.mjs +0 -31
  502. package/tools/gt-render/take-transform.mjs +0 -87
  503. package/tools/kimodo/compare-seam.mjs +0 -108
  504. package/tools/kimodo/constraints.mjs +0 -244
  505. package/tools/kimodo/edit.mjs +0 -259
  506. package/tools/kimodo/effector-constraints.mjs +0 -200
  507. package/tools/kimodo/generate.mjs +0 -646
  508. package/tools/kimodo/local-output.mjs +0 -265
  509. package/tools/kimodo/measure-pose.mjs +0 -65
  510. package/tools/kimodo/measure-preserve.mjs +0 -899
  511. package/tools/kimodo/measure-waypoints.mjs +0 -55
  512. package/tools/kimodo/mlx-generate.py +0 -111
  513. package/tools/kimodo/pose-constraints.mjs +0 -248
  514. package/tools/kimodo/preserve-mask.mjs +0 -543
  515. package/tools/kimodo/read-npz.mjs +0 -189
  516. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  517. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  518. package/tools/kimodo/runner.mjs +0 -284
  519. package/tools/kimodo/seam-batch.mjs +0 -77
  520. package/tools/kimodo/setup-local.mjs +0 -30
  521. package/tools/kimodo/setup-on-box.sh +0 -339
  522. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  523. package/tools/morphgs/assets/gen_truth.py +0 -39
  524. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  525. package/tools/morphgs/assets/playback-check.mjs +0 -30
  526. package/tools/morphgs/demo-gate.sh +0 -26
  527. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  528. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  529. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  530. package/tools/morphgs/setup-on-cluster.sh +0 -126
  531. package/tools/projflow/driver.py +0 -1069
  532. package/tools/projflow/generate.mjs +0 -805
  533. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  534. package/tools/projflow/line-edit-job.mjs +0 -547
  535. package/tools/projflow/replay.mjs +0 -452
  536. package/tools/projflow/runner.mjs +0 -269
  537. package/tools/projflow/service.mjs +0 -742
  538. package/tools/track/DESIGN.md +0 -513
  539. package/tools/track/backfill-provenance.mjs +0 -116
  540. package/tools/track/budget.mjs +0 -8
  541. package/tools/track/check-rig.mjs +0 -209
  542. package/tools/track/diagnostics.schema.json +0 -60
  543. package/tools/track/export-rig.mjs +0 -256
  544. package/tools/track/fallback.mjs +0 -7
  545. package/tools/track/fk-parity-fixture.mjs +0 -161
  546. package/tools/track/gate.mjs +0 -349
  547. package/tools/track/masks.mjs +0 -318
  548. package/tools/track/metrics.mjs +0 -201
  549. package/tools/track/publish-obs.mjs +0 -108
  550. package/tools/track/py/check_env.py +0 -61
  551. package/tools/track/py/eval_lr.py +0 -229
  552. package/tools/track/py/lr_viterbi.py +0 -294
  553. package/tools/track/py/masks.py +0 -405
  554. package/tools/track/py/objective.py +0 -382
  555. package/tools/track/py/rig.py +0 -508
  556. package/tools/track/py/scene.py +0 -326
  557. package/tools/track/py/test_joint_indices.py +0 -123
  558. package/tools/track/py/test_lr_viterbi.py +0 -195
  559. package/tools/track/py/test_masks.py +0 -229
  560. package/tools/track/py/test_rig.py +0 -288
  561. package/tools/track/py/test_scene.py +0 -354
  562. package/tools/track/py/test_track.py +0 -328
  563. package/tools/track/py/track.py +0 -411
  564. package/tools/track/remote.mjs +0 -140
  565. package/tools/track/rig-dump.mjs +0 -227
  566. package/tools/track/run-box-tests.mjs +0 -14
  567. package/tools/track/run-box.mjs +0 -168
  568. package/tools/track/setup-box.sh +0 -59
  569. package/tools/track/study-2d.mjs +0 -728
  570. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  571. /package/src/{ardy → motion}/ground.js +0 -0
  572. /package/src/{ardy → motion}/ik-drag.js +0 -0
  573. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  574. /package/src/{ardy → motion}/ik.js +0 -0
  575. /package/src/{ardy → motion}/physics-surface.js +0 -0
  576. /package/src/{ardy → motion}/playback-clock.js +0 -0
  577. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The per-frame half of practical lights and glow, kept free of React so the
3
+ * place pass can be exercised in Node: SceneObject calls `placeObjectLook`
4
+ * from its imperative place pass (and the recorder through it, for every
5
+ * frame it renders), and it writes the light's numbers and the glow for that
6
+ * frame only.
7
+ */
8
+
9
+ import * as THREE from "three";
10
+ import { LIGHT_INTENSITY_SCALE, flickerAt } from "./object-look.js";
11
+ import { trackLevel } from "./object-travel.js";
12
+ import { hasGlow, placeObjectGlow } from "./object-glow.js";
13
+
14
+ const DEG = Math.PI / 180;
15
+ /** A shadow camera's far plane when the light has no range of its own. */
16
+ export const SHADOW_FAR = 40;
17
+
18
+ const DOWN = new THREE.Vector3(0, -1, 0);
19
+ const scratchDir = new THREE.Vector3();
20
+ const scratchPos = new THREE.Vector3();
21
+ const scratchQuat = new THREE.Quaternion();
22
+ const scratchScale = new THREE.Vector3();
23
+
24
+ function childWith(group, key) {
25
+ for (const child of group.children) if (child.userData?.[key]) return child;
26
+ return null;
27
+ }
28
+
29
+ /** Write the light's numbers for this frame. `gain` is level × flicker. */
30
+ function placeSceneLight(group, object, gain) {
31
+ const light = childWith(group, "sceneLight");
32
+ if (!light) return;
33
+ const { color, intensity, range, angle, softness, aim } = object.light;
34
+ light.color.set(color);
35
+ light.intensity = intensity * LIGHT_INTENSITY_SCALE * gain;
36
+ light.distance = range;
37
+ if (light.castShadow && light.isPointLight) {
38
+ const far = range > 0 ? range : SHADOW_FAR;
39
+ if (light.shadow.camera.far !== far) {
40
+ light.shadow.camera.far = far;
41
+ light.shadow.camera.updateProjectionMatrix();
42
+ }
43
+ }
44
+ if (!light.isSpotLight) return;
45
+ light.angle = angle * DEG;
46
+ light.penumbra = softness;
47
+ // The target is kept outside the scene graph, in world space: an aim point
48
+ // is a place in the set, and the record's own -Y is where it hangs pointing.
49
+ group.updateWorldMatrix(true, false);
50
+ group.matrixWorld.decompose(scratchPos, scratchQuat, scratchScale);
51
+ if (aim) scratchDir.set(aim.x - scratchPos.x, aim.y - scratchPos.y, aim.z - scratchPos.z);
52
+ else scratchDir.copy(DOWN).applyQuaternion(scratchQuat);
53
+ if (scratchDir.lengthSq() < 1e-10) scratchDir.copy(DOWN).applyQuaternion(scratchQuat);
54
+ scratchDir.normalize();
55
+ light.target.position.copy(scratchPos).add(scratchDir);
56
+ light.target.updateMatrixWorld();
57
+ const cone = childWith(group, "lightMarker")?.children.find((child) => child.userData?.lightCone);
58
+ if (cone) cone.quaternion.setFromUnitVectors(DOWN, scratchDir.applyQuaternion(scratchQuat.invert()));
59
+ }
60
+
61
+ /**
62
+ * The per-frame look of one record: its light (if it is one) and its glow (if
63
+ * it has one), from the take's level at `frame` and the light's flicker. Pure
64
+ * in the frame number, so any frame can be drawn in any order.
65
+ */
66
+ export function placeObjectLook(group, object, frame, take) {
67
+ if (!object.light && !object.glow && !hasGlow(group)) return;
68
+ const level = trackLevel(object.id, frame, take);
69
+ const gain = level * (object.light ? flickerAt(object.id, frame, object.light.flicker, take?.fps) : 1);
70
+ if (object.light) placeSceneLight(group, object, gain);
71
+ placeObjectGlow(group, object, gain);
72
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Practical lights in the set: a point or spot light a record carries, and its
3
+ * editor-only marker.
4
+ *
5
+ * The light itself lives on the default layer, so the viewport, the shot
6
+ * preview, capture_frame, contact sheets and the video export all light the
7
+ * set with it. The marker (a bulb, plus the cone for a spot) is editor
8
+ * furniture on GIZMO_LAYER, exactly like an Empty's axes: drawn in the working
9
+ * views, dropped by every recorded camera, and picked through the same
10
+ * `emptyPick` volume so the user can select and move the light.
11
+ *
12
+ * Everything that changes with time (take level, flicker, a spot's aim while
13
+ * the record travels) is written by `placeObjectLook` (scene-light-place.js),
14
+ * which SceneObject calls from its imperative place pass. The recorder runs
15
+ * that pass for every frame it renders, so a scrub or an export never sees a
16
+ * stale light.
17
+ */
18
+
19
+ import { useEffect, useMemo } from "react";
20
+ import { useThree } from "@react-three/fiber";
21
+ import * as THREE from "three";
22
+ import { GIZMO_LAYER } from "./dualview.jsx";
23
+ import { EMPTY_MARKER_SIZE } from "./scene-objects.js";
24
+ import { SHADOW_FAR } from "./scene-light-place.js";
25
+
26
+ const DEG = Math.PI / 180;
27
+ const BULB_RADIUS = 0.07;
28
+ /** Drawn length of a spot's cone outline, metres (the light reaches further). */
29
+ const CONE_LENGTH = 0.7;
30
+
31
+ function makeLight(type, shadow) {
32
+ const light = type === "spot" ? new THREE.SpotLight() : new THREE.PointLight();
33
+ // three.js hangs a spot 1 m up (DEFAULT_UP) by default; a practical sits on its record's pivot.
34
+ light.position.set(0, 0, 0);
35
+ light.decay = 2;
36
+ light.castShadow = shadow;
37
+ light.userData.sceneLight = true;
38
+ if (shadow) {
39
+ const size = type === "spot" ? 1024 : 512;
40
+ light.shadow.mapSize.set(size, size);
41
+ light.shadow.bias = -0.0004;
42
+ light.shadow.normalBias = 0.02;
43
+ // A lighter a hand-width from a face must still shadow the hand.
44
+ light.shadow.camera.near = 0.03;
45
+ light.shadow.camera.far = SHADOW_FAR;
46
+ light.shadow.camera.updateProjectionMatrix();
47
+ }
48
+ return light;
49
+ }
50
+
51
+ /** Line pairs of a circle of `radius` in the plane spanned by axes a and b. */
52
+ function circle(out, radius, a, b, y = 0, segments = 24) {
53
+ for (let i = 0; i < segments; i += 1) {
54
+ for (const k of [i, i + 1]) {
55
+ const t = (k / segments) * Math.PI * 2;
56
+ const p = [0, y, 0];
57
+ p[a] += Math.cos(t) * radius;
58
+ p[b] += Math.sin(t) * radius;
59
+ out.push(...p);
60
+ }
61
+ }
62
+ return out;
63
+ }
64
+
65
+ function lineGeometry(positions) {
66
+ const geometry = new THREE.BufferGeometry();
67
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
68
+ return geometry;
69
+ }
70
+
71
+ /** The bulb: three rings and a few short rays, so it reads as a light, not a ball. */
72
+ function bulbGeometry() {
73
+ const out = [];
74
+ circle(out, BULB_RADIUS, 0, 2);
75
+ circle(out, BULB_RADIUS, 0, 1);
76
+ circle(out, BULB_RADIUS, 1, 2);
77
+ for (let i = 0; i < 8; i += 1) {
78
+ const t = (i / 8) * Math.PI * 2;
79
+ out.push(Math.cos(t) * BULB_RADIUS * 1.3, 0, Math.sin(t) * BULB_RADIUS * 1.3, Math.cos(t) * BULB_RADIUS * 2, 0, Math.sin(t) * BULB_RADIUS * 2);
80
+ }
81
+ return lineGeometry(out);
82
+ }
83
+
84
+ /** A spot's cone along local -Y: four edges and the rim at CONE_LENGTH. */
85
+ function coneGeometry(angleDeg) {
86
+ const radius = CONE_LENGTH * Math.tan(angleDeg * DEG);
87
+ const out = circle([], radius, 0, 2, -CONE_LENGTH, 32);
88
+ for (let i = 0; i < 4; i += 1) {
89
+ const t = (i / 4) * Math.PI * 2;
90
+ out.push(0, 0, 0, Math.cos(t) * radius, -CONE_LENGTH, Math.sin(t) * radius);
91
+ }
92
+ return lineGeometry(out);
93
+ }
94
+
95
+ const onGizmoLayer = (node) => {
96
+ if (node) node.layers.set(GIZMO_LAYER);
97
+ };
98
+
99
+ function LightMarker({ type, angle, color }) {
100
+ const bulb = useMemo(() => bulbGeometry(), []);
101
+ const cone = useMemo(() => (type === "spot" ? coneGeometry(angle) : null), [type, angle]);
102
+ useEffect(() => () => bulb.dispose(), [bulb]);
103
+ useEffect(() => () => cone?.dispose(), [cone]);
104
+ return (
105
+ <group userData={{ lightMarker: true }}>
106
+ <lineSegments ref={onGizmoLayer} geometry={bulb} renderOrder={997} frustumCulled={false}>
107
+ <lineBasicMaterial color={color} depthTest={false} depthWrite={false} transparent opacity={0.95} />
108
+ </lineSegments>
109
+ {cone && (
110
+ <group userData={{ lightCone: true }}>
111
+ <lineSegments ref={onGizmoLayer} geometry={cone} renderOrder={997} frustumCulled={false}>
112
+ <lineBasicMaterial color={color} depthTest={false} depthWrite={false} transparent opacity={0.55} />
113
+ </lineSegments>
114
+ </group>
115
+ )}
116
+ {/* Never drawn, still raycast: the object picker's gizmo-layer pass
117
+ finds it by `emptyPick` and selects the light's record. */}
118
+ <mesh ref={onGizmoLayer} visible={false} userData={{ emptyPick: true }}>
119
+ <sphereGeometry args={[EMPTY_MARKER_SIZE * 0.45, 12, 8]} />
120
+ <meshBasicMaterial />
121
+ </mesh>
122
+ </group>
123
+ );
124
+ }
125
+
126
+ /** A light record's content: the three.js light and its marker. Rebuilt only
127
+ * when the type or the shadow switch changes (both change the shader). */
128
+ export function SceneLight({ object }) {
129
+ const { type, shadow, angle, color } = object.light;
130
+ const light = useMemo(() => makeLight(type, shadow), [type, shadow]);
131
+ useEffect(() => () => light.dispose(), [light]);
132
+ return (
133
+ <>
134
+ <primitive object={light} />
135
+ <LightMarker type={type} angle={angle} color={color} />
136
+ </>
137
+ );
138
+ }
139
+
140
+ /** Ask for a frame when the record's light or glow changes. Both are written by
141
+ * the place pass, which a demand frame loop runs only when something asks: an
142
+ * edit that changes no three.js prop would otherwise wait for the next orbit. */
143
+ export function useLookRefresh(object) {
144
+ const invalidate = useThree((state) => state.invalidate);
145
+ useEffect(() => {
146
+ invalidate();
147
+ }, [invalidate, object.light, object.glow]);
148
+ }
@@ -22,6 +22,13 @@ import { createObjectPath, translateObjectPath } from "./object-path.js";
22
22
  import { elementByPath } from "./studio-elements.js";
23
23
  import { MESH_DEFAULT_HEIGHT, MESH_HEIGHT_MIN } from "./scene-mesh.js";
24
24
  import { normalizeSculptRecipe, sculptStandingBox } from "./sculpt-recipe.js";
25
+ import { GREY_BOX } from "./object-colors.js";
26
+ import { isEffectivelyHidden } from "./object-hidden.js";
27
+ import { objectSize } from "./object-size.js";
28
+ import { EFFECT_ENTRY, effectFields } from "./effects/effect-record.js";
29
+ import { LIGHT_KIND, LIGHT_MENU_ENTRIES, lightLibraryEntry, objectLookFields, objectLookPatch } from "./object-look.js";
30
+ export { OBJECT_COLORS, normalizeObjectColor, rememberObjectColor, readStoredObjectColors, writeStoredObjectColors } from "./object-colors.js";
31
+ export { isEffectivelyHidden };
25
32
 
26
33
  export const DEFAULT_SCENE_OBJECTS = [];
27
34
  /** The persistence contract (plan §8.1): the version lives in the key AND in
@@ -60,7 +67,6 @@ const snapTo = (value, step) => Number((step > 0 ? Math.round(value / step) * st
60
67
  * else, and a tinted maquette reads as a finished prop. The hand-built set
61
68
  * pieces keep their clay colours because they ARE the thing they depict.
62
69
  */
63
- const GREY_BOX = "#c2c6c8";
64
70
  export const OBJECT_LIBRARY = [
65
71
  { kind: "cube", label: "Cube", group: "Primitives", footprint: { width: 1, depth: 1 }, height: 1, color: GREY_BOX },
66
72
  { kind: "sphere", label: "Sphere", group: "Primitives", footprint: { width: 1, depth: 1 }, height: 1, color: GREY_BOX },
@@ -75,96 +81,6 @@ export const OBJECT_LIBRARY = [
75
81
  { kind: "small-plane", label: "Plane (aircraft)", group: "Set pieces", footprint: { width: 3.4, depth: 3.6 }, height: 1.4, color: "#7896a4" },
76
82
  ];
77
83
 
78
- /** Blockout greys first, then the clay accents, then the blocking chromatics,
79
- * for re-tinting from the inspector. The greys lead because a blockout starts
80
- * grey; the saturated four exist so a shot can say "the red ball, the blue
81
- * box" at a glance — the inspector had no way to say that at all (#88). They
82
- * are muted off pure RGB so they still read as clay under the set's lighting.
83
- * Any other colour is authored through the inspector's free colour input. */
84
- export const OBJECT_COLORS = [
85
- "#e2e5e6",
86
- GREY_BOX,
87
- "#9aa1a5",
88
- "#767d81",
89
- "#d9b18c",
90
- "#8fae9b",
91
- "#d94a4a",
92
- "#4a7bd9",
93
- "#e2c04a",
94
- "#4fa86a",
95
- ];
96
-
97
- /** How many hand-mixed colours the inspector remembers. Six is one palette
98
- * row: enough to keep a scene's custom tints reachable, short enough that the
99
- * popover never turns into a second, unruly palette. */
100
- const RECENT_OBJECT_COLORS_MAX = 6;
101
- /** Session memory of hand-mixed tints; versioned like every other key here. */
102
- const RECENT_OBJECT_COLORS_KEY = "cozyclay.objectColors.recent";
103
-
104
- const HEX_SHORT = /^#?([0-9a-f]{3})$/i;
105
- const HEX_LONG = /^#?([0-9a-f]{6})$/i;
106
-
107
- /**
108
- * What a colour input typed by a human means, or null if it means nothing.
109
- *
110
- * The hex field accepts what people actually type — `#f36`, `ff3366`, upper
111
- * case, stray spaces — and folds it to the one form the record stores, so a
112
- * colour typed by hand and the same colour picked from the native swatch are
113
- * byte-identical (and therefore compare equal against the palette). Anything
114
- * else returns null rather than throwing or guessing: a half-typed `#12` is a
115
- * keystroke on the way somewhere, not an instruction to repaint the prop.
116
- */
117
- export function normalizeObjectColor(value) {
118
- if (typeof value !== "string") return null;
119
- const text = value.trim();
120
- const short = HEX_SHORT.exec(text);
121
- if (short) return `#${short[1].toLowerCase().replace(/./g, (digit) => digit + digit)}`;
122
- const long = HEX_LONG.exec(text);
123
- return long ? `#${long[1].toLowerCase()}` : null;
124
- }
125
-
126
- /**
127
- * The recent-colours list after mixing `hex`: newest first, no duplicates,
128
- * capped, presets excluded.
129
- *
130
- * Pure so the list can be proved without a browser, and so a corrupt stored
131
- * list is repaired on the way through instead of painting a swatch with junk.
132
- * Presets are skipped because they are already one row up — remembering them
133
- * would push the author's own colours off the end with colours they never
134
- * mixed. Returns the SAME array when nothing changes, so a re-pick of an
135
- * unchanged colour cannot trigger a storage write or a re-render.
136
- */
137
- export function rememberObjectColor(list, hex) {
138
- const clean = Array.isArray(list)
139
- ? list.map(normalizeObjectColor).filter((color, index, all) => color && all.indexOf(color) === index && !OBJECT_COLORS.includes(color))
140
- : [];
141
- const color = normalizeObjectColor(hex);
142
- const next = color && !OBJECT_COLORS.includes(color) ? [color, ...clean.filter((entry) => entry !== color)] : clean;
143
- const capped = next.slice(0, RECENT_OBJECT_COLORS_MAX);
144
- const unchanged = Array.isArray(list) && capped.length === list.length && capped.every((entry, index) => entry === list[index]);
145
- return unchanged ? list : capped;
146
- }
147
-
148
- /** Recent colours as last left by this browser; storage can be unavailable
149
- * (private mode), and a corrupt body must not take the inspector down with
150
- * it — either way the palette simply starts without a memory. */
151
- export function readStoredObjectColors(storage) {
152
- try {
153
- const parsed = JSON.parse(storage?.getItem(RECENT_OBJECT_COLORS_KEY) ?? "[]");
154
- return rememberObjectColor(Array.isArray(parsed) ? parsed : [], null);
155
- } catch {
156
- return [];
157
- }
158
- }
159
-
160
- export function writeStoredObjectColors(storage, list) {
161
- try {
162
- storage?.setItem(RECENT_OBJECT_COLORS_KEY, JSON.stringify(list));
163
- } catch {
164
- // Storage can be unavailable (private mode); the colours simply stop persisting.
165
- }
166
- }
167
-
168
84
  /**
169
85
  * A cutout is a standee: an imported image standing on a card. Its size is NOT
170
86
  * library data — the height is measured by the user and the width follows the
@@ -253,14 +169,16 @@ const EMPTY_ENTRY = {
253
169
  color: "#ffffff",
254
170
  };
255
171
 
256
- /** True for a record that is only a node: no mesh, no collision, no surface. */
172
+ /** True for a record that is only a node: no mesh, no collision, no surface.
173
+ * A practical light (object-look.js) is one too: it lights, it does not stand. */
257
174
  export function isEmptyObject(object) {
258
- return object?.renderer === EMPTY_KIND;
175
+ return object?.renderer === EMPTY_KIND || object?.renderer === LIGHT_KIND;
259
176
  }
260
177
 
261
- /** The catalogue grouped for the create menu: the library plus the empty,
262
- * which is creatable but has no footprint to satisfy the library's contract. */
263
- export const OBJECT_MENU_ENTRIES = Object.freeze([EMPTY_ENTRY, ...OBJECT_LIBRARY]);
178
+ /** The catalogue grouped for the create menu: the library plus the empty, the
179
+ * two practical lights and the effect emitter, which are creatable but have no
180
+ * footprint to satisfy the library's contract. */
181
+ export const OBJECT_MENU_ENTRIES = Object.freeze([EMPTY_ENTRY, ...OBJECT_LIBRARY, ...LIGHT_MENU_ENTRIES, EFFECT_ENTRY]);
264
182
 
265
183
  /** Every kind that can exist in a scene: the catalogue you can create from,
266
184
  * plus the kinds that arrive by import and so are deliberately absent from the
@@ -271,7 +189,8 @@ function objectLibraryEntry(kind) {
271
189
  if (kind === MESH_KIND) return MESH_ENTRY;
272
190
  if (kind === SCULPT_KIND) return SCULPT_ENTRY;
273
191
  if (kind === EMPTY_KIND) return EMPTY_ENTRY;
274
- return OBJECT_LIBRARY.find((entry) => entry.kind === kind) ?? null;
192
+ if (kind === EFFECT_ENTRY.kind) return EFFECT_ENTRY;
193
+ return OBJECT_LIBRARY.find((entry) => entry.kind === kind) ?? lightLibraryEntry(kind);
275
194
  }
276
195
 
277
196
  /** Return the authored walkable surface for mocap contact staging. */
@@ -397,9 +316,10 @@ export function createSceneObject(kind, existing = [], placement = {}) {
397
316
  return {
398
317
  id,
399
318
  name,
400
- renderer: kind,
319
+ // A light kind (point-light, spot-light) is a preset of the one light renderer.
320
+ renderer: entry.renderer ?? kind,
401
321
  x: TRANSFORM_LIMITS.x(Number(placement.x) || 0),
402
- y: 0,
322
+ y: entry.placeY ?? 0,
403
323
  z: TRANSFORM_LIMITS.z(Number(placement.z) || 0),
404
324
  rot: TRANSFORM_LIMITS.rot(Number(placement.rot) || 0),
405
325
  rotX: 0,
@@ -420,6 +340,8 @@ export function createSceneObject(kind, existing = [], placement = {}) {
420
340
  footprint: { ...entry.footprint },
421
341
  height: entry.height,
422
342
  supportY: Number.isFinite(entry.supportY) ? entry.supportY : entry.height,
343
+ ...objectLookFields({}, entry),
344
+ ...effectFields({ renderer: kind }),
423
345
  };
424
346
  }
425
347
 
@@ -625,7 +547,8 @@ const TRANSFORM_LIMITS = {
625
547
  /** The patch keys updateSceneObject reads for this object; any other key is
626
548
  * silently ignored there, so callers that must not be silent check against this. */
627
549
  export function objectPatchFields(object) {
628
- const fields = [...Object.keys(TRANSFORM_LIMITS), "name", "color", "hidden", "opacity", "path"];
550
+ const fields = [...Object.keys(TRANSFORM_LIMITS), "name", "color", "hidden", "opacity", "path", "glow"];
551
+ if (object?.renderer === LIGHT_KIND) fields.push("light");
629
552
  if (object?.renderer === CUTOUT_KIND) fields.push("assetId", "sourceAssetId", "matteAssetId", "matteScale", "height", "aspect", "width", "stretch");
630
553
  if (object?.renderer === MESH_KIND) fields.push("clay", "assetId", "height");
631
554
  if (object?.renderer === SCULPT_KIND) fields.push("recipe");
@@ -653,36 +576,6 @@ export function descendantsOf(objects, id) {
653
576
  return out;
654
577
  }
655
578
 
656
- /** True when this record, a parent above it, or a character carrying any of
657
- * those records is hidden. A cycle in parent links ends the walk. When the
658
- * id is in `objects`, that stored record is what the walk reads, so a display
659
- * copy cannot disagree with the authored flag. Nothing in the lists is changed. */
660
- export function isEffectivelyHidden(entity, objects = [], characters = []) {
661
- if (!entity || typeof entity !== "object") return false;
662
- const byId = new Map();
663
- for (const object of objects) {
664
- if (object && typeof object.id === "string") byId.set(object.id, object);
665
- }
666
- const characterHidden = (id) => characters.some((item) => item && typeof item === "object" && item.id === id && item.hidden === true);
667
- const seen = new Set();
668
- let current = entity;
669
- while (current && typeof current === "object") {
670
- const id = typeof current.id === "string" ? current.id : null;
671
- if (id) {
672
- if (seen.has(id)) break;
673
- seen.add(id);
674
- if (byId.has(id)) current = byId.get(id);
675
- }
676
- if (current.hidden === true) return true;
677
- const characterId = current.attach && typeof current.attach.characterId === "string" ? current.attach.characterId : null;
678
- if (characterId && characterHidden(characterId)) return true;
679
- const parentId = typeof current.parent === "string" ? current.parent : null;
680
- if (!parentId) break;
681
- current = byId.get(parentId) ?? null;
682
- }
683
- return false;
684
- }
685
-
686
579
  /** Float dust (6e-17 where 0 belongs) is snapped away, and everything else is
687
580
  * kept to 1e-9: fine enough that hundreds of small gizmo steps cannot drift a
688
581
  * carried child visibly, coarse enough that stored values stay readable. */
@@ -963,6 +856,8 @@ export function updateSceneObject(objects, id, patch) {
963
856
  Object.assign(update, sculptStandingBox(normalized.recipe));
964
857
  }
965
858
  }
859
+ // Light and glow merge over the record's own and are repaired (object-look.js).
860
+ Object.assign(update, objectLookPatch(object, patch));
966
861
  if (!Object.keys(update).length) return object;
967
862
  changed = true;
968
863
  return { ...object, ...update };
@@ -1126,7 +1021,7 @@ export function normalizeSceneObject(record) {
1126
1021
  return {
1127
1022
  id: record.id,
1128
1023
  name: typeof record.name === "string" && record.name ? record.name : entry.label,
1129
- renderer: entry.kind,
1024
+ renderer: entry.renderer ?? entry.kind,
1130
1025
  x: TRANSFORM_LIMITS.x(pick(record.x, 0)),
1131
1026
  y: TRANSFORM_LIMITS.y(pick(record.y, 0)),
1132
1027
  z: TRANSFORM_LIMITS.z(pick(record.z, 0)),
@@ -1191,6 +1086,10 @@ export function normalizeSceneObject(record) {
1191
1086
  height: Math.max(MESH_HEIGHT_MIN, pick(record.height, entry.height)),
1192
1087
  }
1193
1088
  : { footprint: { ...entry.footprint }, height: entry.height, supportY: Number.isFinite(entry.supportY) ? entry.supportY : entry.height }),
1089
+ // A light's light, and a glow on any record (object-look.js).
1090
+ ...objectLookFields(record, entry),
1091
+ // An effect record's emitter (src/effects/effect-record.js).
1092
+ ...effectFields(record),
1194
1093
  };
1195
1094
  }
1196
1095
 
@@ -1315,14 +1214,7 @@ export function screenRotatePatch(start, viewAxis, deltaDeg, snap = ROTATE_SNAP)
1315
1214
  };
1316
1215
  }
1317
1216
 
1318
- /** The object's world size along each axis, for the gizmo and the plan board. */
1319
- export function objectSize(object) {
1320
- return {
1321
- width: (object.footprint?.width ?? 1) * (object.scaleX ?? 1),
1322
- height: (object.height ?? 1) * (object.scaleY ?? 1),
1323
- depth: (object.footprint?.depth ?? 1) * (object.scaleZ ?? 1),
1324
- };
1325
- }
1217
+ export { objectSize };
1326
1218
  /** The tolerance that keeps an edge-abutting footprint from counting as
1327
1219
  * overlap: a strict `<` against `max - EPS` makes a 0.5 - 0.5 touch false. */
1328
1220
  const OVERLAP_EPS = 1e-4;