cozyclay 2.1.0 → 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 (583) hide show
  1. package/CHANGELOG.md +65 -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 +22 -6
  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-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.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/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /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;