cozyclay 2.1.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (585) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +43 -116
  4. package/bin/agent/agent-runner.mjs +14 -15
  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/session-store.mjs +2 -2
  14. package/bin/agent/studio-context-budget.mjs +93 -0
  15. package/bin/agent/studio-motion-reference.mjs +23 -0
  16. package/bin/agent/studio-prompt.mjs +1 -1
  17. package/bin/agent/studio-rebase-text.mjs +32 -0
  18. package/bin/agent/studio-rebase.mjs +26 -39
  19. package/bin/agent/studio-tools.mjs +9 -28
  20. package/bin/agent/tool-arguments.mjs +49 -0
  21. package/bin/agent/turn-frames.mjs +66 -0
  22. package/bin/codex-auth.mjs +9 -2
  23. package/bin/cozyclay.mjs +10 -116
  24. package/bin/live/cli.mjs +3 -3
  25. package/bin/mcp-runtime.mjs +2 -12
  26. package/bin/package-signature.mjs +0 -4
  27. package/dist/app/index.html +7 -6
  28. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  29. package/dist/assets/analytics-BWCqdgWA.js +1 -0
  30. package/dist/assets/app-DfTCxaKk.js +5087 -0
  31. package/dist/assets/app-f-fkV5uP.css +1 -0
  32. package/dist/assets/compile-worker-BxKXh07s.js +5 -0
  33. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  34. package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-DNO8kv_N.js} +1 -1
  35. package/dist/assets/{landing-ChUvB_zL.js → landing-yfZaJkWc.js} +1 -1
  36. package/dist/assets/scene-asset-sync-U3kS2rvK.js +20 -0
  37. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  38. package/dist/assets/workflow-D78eiKTa.js +179 -0
  39. package/dist/cozyclay-package.json +1 -1
  40. package/dist/index.html +3 -3
  41. package/dist/sw.js +3 -6
  42. package/dist/workflow/index.html +5 -4
  43. package/mcp/LIVE-PROTOCOL.md +7 -47
  44. package/mcp/README.md +16 -26
  45. package/mcp/live-hub.mjs +6 -10
  46. package/mcp/package.json +1 -3
  47. package/mcp/tool-handlers.mjs +4 -168
  48. package/package.json +3 -22
  49. package/src/App.jsx +151 -2625
  50. package/src/agent-progress.js +80 -0
  51. package/src/agent-turn-mark.js +30 -0
  52. package/src/analytics.js +1 -19
  53. package/src/app-context.js +12 -2
  54. package/src/app-stage.jsx +2 -34
  55. package/src/asset-pane.jsx +6 -5
  56. package/src/camera-block.js +21 -0
  57. package/src/camera-direction.js +120 -0
  58. package/src/character-kind.js +3 -9
  59. package/src/command-bus.js +10 -8
  60. package/src/commands/cast.js +5 -34
  61. package/src/commands/elements/character.js +1 -1
  62. package/src/commands/elements/motion.js +1 -6
  63. package/src/commands/elements/object.js +6 -2
  64. package/src/commands/elements/shot.js +4 -1
  65. package/src/commands/elements.js +38 -14
  66. package/src/commands/index.js +3 -1
  67. package/src/commands/kits.js +37 -0
  68. package/src/commands/motion-timing.js +57 -0
  69. package/src/commands/motion.js +9 -152
  70. package/src/commands/motion3-compile.js +65 -0
  71. package/src/commands/motion3.js +214 -0
  72. package/src/commands/objects.js +6 -4
  73. package/src/commands/shot.js +55 -13
  74. package/src/commands/stage.js +14 -1
  75. package/src/commands/view.js +4 -1
  76. package/src/document-mirrors.js +42 -0
  77. package/src/document-store.js +4 -0
  78. package/src/domains/cast.js +19 -194
  79. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  80. package/src/domains/motion/take-source.js +53 -0
  81. package/src/domains/motion.js +60 -1838
  82. package/src/domains/scenes.js +12 -55
  83. package/src/domains/shots.js +2 -2
  84. package/src/domains/stage.js +4 -2
  85. package/src/draft-ghost.js +47 -0
  86. package/src/draft-ghost.jsx +129 -0
  87. package/src/dualview.jsx +4 -3
  88. package/src/effects/EffectsLayer.jsx +112 -0
  89. package/src/effects/effect-meshes.js +232 -0
  90. package/src/effects/effect-record.js +103 -0
  91. package/src/effects/emitter.js +59 -0
  92. package/src/effects/simulate.js +261 -0
  93. package/src/effects/state.js +59 -0
  94. package/src/execution-telemetry.js +2 -3
  95. package/src/first-success-guide.jsx +5 -5
  96. package/src/ghost-set.js +257 -0
  97. package/src/held-object-edit.jsx +113 -0
  98. package/src/held-object-pose.js +175 -0
  99. package/src/hierarchy-model.js +1 -1
  100. package/src/hierarchy-panel.jsx +1 -1
  101. package/src/imported-mesh.jsx +103 -0
  102. package/src/kits/held.js +84 -0
  103. package/src/kits/index.js +70 -0
  104. package/src/kits/smoking.js +57 -0
  105. package/src/light-blockers.js +93 -0
  106. package/src/motion/beat-board-model.js +84 -0
  107. package/src/motion/beat-board.css +124 -0
  108. package/src/motion/beat-board.jsx +60 -0
  109. package/src/motion/center-of-mass.js +66 -0
  110. package/src/motion/fix-collisions.js +850 -0
  111. package/src/motion/physics-review.js +118 -0
  112. package/src/{ardy → motion}/physics-support.js +1 -76
  113. package/src/motion/rig-snapshot.js +22 -0
  114. package/src/motion/take-segments.jsx +140 -0
  115. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  116. package/src/{ardy → motion}/timeline.css +1 -2
  117. package/src/{ardy → motion}/timeline.jsx +42 -413
  118. package/src/motion/waypoints.js +76 -0
  119. package/src/motion3/beats.js +83 -0
  120. package/src/motion3/body.js +158 -0
  121. package/src/motion3/bones.js +47 -0
  122. package/src/motion3/carried.js +47 -0
  123. package/src/motion3/compile-job.js +79 -0
  124. package/src/motion3/compile-worker.js +10 -0
  125. package/src/motion3/compiler/anchors.js +113 -0
  126. package/src/motion3/compiler/arm-orient.js +128 -0
  127. package/src/motion3/compiler/channels.js +125 -0
  128. package/src/motion3/compiler/compile.js +205 -0
  129. package/src/motion3/compiler/gait-rhythm.js +97 -0
  130. package/src/motion3/compiler/grip.js +171 -0
  131. package/src/motion3/compiler/head-steady.js +48 -0
  132. package/src/motion3/compiler/hold.js +134 -0
  133. package/src/motion3/compiler/idle.js +113 -0
  134. package/src/motion3/compiler/limbs.js +197 -0
  135. package/src/motion3/compiler/math.js +72 -0
  136. package/src/motion3/compiler/pose.js +177 -0
  137. package/src/motion3/compiler/quat.js +49 -0
  138. package/src/motion3/compiler/skeleton.js +74 -0
  139. package/src/motion3/compiler/validate-common.js +32 -0
  140. package/src/motion3/compiler/validate-goals.js +118 -0
  141. package/src/motion3/compiler/validate-objects.js +144 -0
  142. package/src/motion3/compiler/validate-parts.js +118 -0
  143. package/src/motion3/compiler/validate.js +122 -0
  144. package/src/motion3/compiler/vocab.js +74 -0
  145. package/src/motion3/compiler/walk-pelvis.js +275 -0
  146. package/src/motion3/compiler/walk.js +207 -0
  147. package/src/motion3/compiler/wrist-clamp.js +32 -0
  148. package/src/motion3/delta.js +47 -0
  149. package/src/motion3/describe.js +177 -0
  150. package/src/motion3/docs.js +27 -0
  151. package/src/motion3/edit-carry.js +21 -0
  152. package/src/motion3/fix/ease.js +16 -0
  153. package/src/motion3/fix/ground.js +37 -0
  154. package/src/motion3/fix/index.js +52 -0
  155. package/src/motion3/fix/push.js +71 -0
  156. package/src/motion3/fix/reach.js +46 -0
  157. package/src/motion3/fix/retime.js +79 -0
  158. package/src/motion3/fix/self.js +211 -0
  159. package/src/motion3/fix/slide.js +70 -0
  160. package/src/motion3/fix/turn.js +51 -0
  161. package/src/motion3/follow.js +39 -0
  162. package/src/motion3/free-motion.js +204 -0
  163. package/src/motion3/grip-choice.js +130 -0
  164. package/src/motion3/hold-plan.js +157 -0
  165. package/src/motion3/json-patch.js +77 -0
  166. package/src/motion3/key-aim.js +105 -0
  167. package/src/motion3/key-edit.js +82 -0
  168. package/src/motion3/lenient.js +127 -0
  169. package/src/motion3/object-keys.js +236 -0
  170. package/src/motion3/object-pose.js +182 -0
  171. package/src/motion3/object-tracks.js +12 -0
  172. package/src/motion3/people.js +82 -0
  173. package/src/motion3/pipeline.js +102 -0
  174. package/src/motion3/playback.js +51 -0
  175. package/src/motion3/probes.js +91 -0
  176. package/src/motion3/qa/body-checks.js +34 -0
  177. package/src/motion3/qa/check-names.js +14 -0
  178. package/src/motion3/qa/checks.js +125 -0
  179. package/src/motion3/qa/contact-checks.js +51 -0
  180. package/src/motion3/qa/explain.js +80 -0
  181. package/src/motion3/qa/fmt.js +5 -0
  182. package/src/motion3/qa/foot-checks.js +72 -0
  183. package/src/motion3/qa/geometry.js +77 -0
  184. package/src/motion3/qa/goals.js +191 -0
  185. package/src/motion3/qa/ground.js +60 -0
  186. package/src/motion3/qa/hints-contact.js +195 -0
  187. package/src/motion3/qa/hints-goal.js +160 -0
  188. package/src/motion3/qa/hints-knee.js +24 -0
  189. package/src/motion3/qa/hints-motion.js +209 -0
  190. package/src/motion3/qa/hints-object.js +91 -0
  191. package/src/motion3/qa/index.js +44 -0
  192. package/src/motion3/qa/key-checks.js +82 -0
  193. package/src/motion3/qa/near.js +96 -0
  194. package/src/motion3/qa/object-checks.js +135 -0
  195. package/src/motion3/qa/patch.js +161 -0
  196. package/src/motion3/qa/people-checks.js +37 -0
  197. package/src/motion3/qa/severity.js +38 -0
  198. package/src/motion3/qa/spec-checks.js +65 -0
  199. package/src/motion3/qa/style.js +228 -0
  200. package/src/motion3/qa/wrist-checks.js +69 -0
  201. package/src/motion3/qa/wrist-obs.js +60 -0
  202. package/src/motion3/refs.js +129 -0
  203. package/src/motion3/repair-tune.js +121 -0
  204. package/src/motion3/repair.js +83 -0
  205. package/src/motion3/report.js +67 -0
  206. package/src/motion3/rigs/index.js +14 -0
  207. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  208. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  209. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  210. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  211. package/src/motion3/scene.js +60 -0
  212. package/src/motion3/schema/brief.js +89 -0
  213. package/src/motion3/schema/fields.js +44 -0
  214. package/src/motion3/schema/spec-examples.js +46 -0
  215. package/src/motion3/schema/topics.js +113 -0
  216. package/src/motion3/schema/types.js +149 -0
  217. package/src/motion3/search.js +117 -0
  218. package/src/motion3/second-hand.js +112 -0
  219. package/src/motion3/settle.js +136 -0
  220. package/src/motion3/skills/basic.js +83 -0
  221. package/src/motion3/skills/book.js +76 -0
  222. package/src/motion3/skills/car.js +145 -0
  223. package/src/motion3/skills/flip.js +200 -0
  224. package/src/motion3/skills/frames-auto.js +29 -0
  225. package/src/motion3/skills/impact.js +109 -0
  226. package/src/motion3/skills/index.js +85 -0
  227. package/src/motion3/skills/info.js +80 -0
  228. package/src/motion3/skills/smoke-kit.js +109 -0
  229. package/src/motion3/skills/smoke.js +160 -0
  230. package/src/motion3/spec-store.js +59 -0
  231. package/src/motion3/take-edit.js +155 -0
  232. package/src/motion3/take.js +63 -0
  233. package/src/motion3/timeline.js +127 -0
  234. package/src/motion3/views.js +121 -0
  235. package/src/motion3/walk-plan.js +119 -0
  236. package/src/object-catalog.jsx +7 -1
  237. package/src/object-colors.js +98 -0
  238. package/src/object-gizmo-parts.js +115 -0
  239. package/src/object-gizmo.jsx +37 -122
  240. package/src/object-glow.js +139 -0
  241. package/src/object-hidden.js +33 -0
  242. package/src/object-look.js +196 -0
  243. package/src/object-size.js +10 -0
  244. package/src/object-travel.js +71 -6
  245. package/src/panels/CameraPanel.jsx +7 -7
  246. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  247. package/src/panels/LightPanel.jsx +25 -4
  248. package/src/panels/ObjectLookPanel.jsx +88 -0
  249. package/src/panels/PosePanel.jsx +2 -17
  250. package/src/panels/RigControlPanel.jsx +5 -53
  251. package/src/panels/SubjectBox.jsx +4 -1
  252. package/src/panels/details.css +14 -0
  253. package/src/posestudio.jsx +4 -27
  254. package/src/project-browser.jsx +3 -4
  255. package/src/project-resources.js +18 -31
  256. package/src/project.js +13 -104
  257. package/src/props.jsx +25 -280
  258. package/src/qa-label.js +25 -0
  259. package/src/rail-dot-drag.js +23 -0
  260. package/src/render-passes.js +10 -0
  261. package/src/resource-status.jsx +1 -0
  262. package/src/result-modal.jsx +4 -19
  263. package/src/room.jsx +20 -12
  264. package/src/scene-assets.js +8 -6
  265. package/src/scene-light-place.js +72 -0
  266. package/src/scene-lights.jsx +148 -0
  267. package/src/scene-objects.js +31 -139
  268. package/src/scenes.js +15 -105
  269. package/src/semantic-edit.js +2 -5
  270. package/src/set-pieces.jsx +183 -0
  271. package/src/settings-menu.jsx +3 -11
  272. package/src/shell/BottomDock.jsx +9 -51
  273. package/src/shell/DetailsSlot.jsx +16 -200
  274. package/src/shell/GhostsMenuGroup.jsx +74 -0
  275. package/src/shell/LibrarySlot.jsx +1 -3
  276. package/src/shell/MenuBar.jsx +3 -3
  277. package/src/shell/PreferencesDialog.jsx +15 -264
  278. package/src/shell/PreferencesSlot.jsx +2 -8
  279. package/src/shell/TopBar.jsx +5 -84
  280. package/src/shell/ViewportToolbar.jsx +10 -70
  281. package/src/shell/agent-glass.css +19 -6
  282. package/src/shell/details-selection.js +42 -0
  283. package/src/shell/ghosts-menu.css +22 -0
  284. package/src/shell/glass-regions.css +29 -3
  285. package/src/shell/preferences.css +6 -1
  286. package/src/shot-authoring.js +4 -7
  287. package/src/soft-shadows.js +37 -0
  288. package/src/stage-attach-frames.js +12 -12
  289. package/src/stage-canvas-rigs.jsx +3 -3
  290. package/src/stage-capture.jsx +42 -8
  291. package/src/stage-characters.jsx +22 -216
  292. package/src/stage-editor-gizmos.jsx +6 -6
  293. package/src/stage-environment.js +93 -0
  294. package/src/stage-hdr-builtins.js +107 -0
  295. package/src/stage-hdr-image.js +116 -0
  296. package/src/stage-hdr-library.js +70 -0
  297. package/src/stage-hdr-map.js +94 -0
  298. package/src/stage-hdr.js +130 -0
  299. package/src/stage-lighting.js +149 -0
  300. package/src/stage-occlusion.js +210 -0
  301. package/src/stage-sky.jsx +159 -0
  302. package/src/stage-startup-defaults.js +5 -9
  303. package/src/studio-actions.js +16 -14
  304. package/src/studio-agent-commands.js +19 -16
  305. package/src/studio-agent-motion.js +9 -9
  306. package/src/studio-agent-protocol.js +47 -77
  307. package/src/studio-app-binding.js +27 -47
  308. package/src/studio-asset-list.js +17 -0
  309. package/src/studio-contact-sheet.js +37 -5
  310. package/src/studio-elements.js +27 -7
  311. package/src/studio-entity-bounds.js +18 -0
  312. package/src/studio-error-codes.js +15 -0
  313. package/src/studio-export-framing.js +152 -0
  314. package/src/studio-lighting-check.js +103 -0
  315. package/src/studio-motion-visual.js +150 -0
  316. package/src/studio-schema-errors.js +80 -0
  317. package/src/studio-support-gap.js +74 -0
  318. package/src/styles.css +1 -19
  319. package/src/timeline-defaults.js +5 -0
  320. package/src/timeline-extent.js +8 -20
  321. package/src/tutorial-walk.js +17 -0
  322. package/src/ui-scrub.js +1 -1
  323. package/src/use-shot-camera-tracks.js +12 -0
  324. package/src/view-ghosts.js +97 -0
  325. package/src/view-ghosts.jsx +49 -0
  326. package/src/workflow/AgentPanel.jsx +47 -166
  327. package/src/workflow/AgentProgressCard.jsx +109 -0
  328. package/src/workflow/AgentReplyText.jsx +37 -0
  329. package/src/workflow/AgentTurnSteps.jsx +84 -0
  330. package/src/workflow/LoginPanel.jsx +140 -0
  331. package/src/workflow/WorkflowBuilder.jsx +7 -23
  332. package/src/workflow/agent-client.js +54 -51
  333. package/src/workflow/agent-panel.css +71 -1
  334. package/src/workflow/agent-progress-card.css +167 -0
  335. package/src/workflow/agent-progress-view.js +47 -0
  336. package/src/workflow/agent-turn-steps.css +113 -0
  337. package/src/workflow/agent-turn.js +191 -0
  338. package/src/workflow/login-client.js +34 -0
  339. package/src/workflow/motion-input.js +18 -31
  340. package/src/workflow/node-schema.js +1 -1
  341. package/src/workflow/reply-markdown.js +69 -0
  342. package/src/workflow/scene-asset-sync.js +8 -8
  343. package/tools/agent-session-stats.mjs +61 -0
  344. package/tools/dev-full.mjs +5 -56
  345. package/tools/eslint-baseline.json +12 -13
  346. package/tools/knip-baseline.json +0 -179
  347. package/tools/loc-baseline.json +31 -81
  348. package/tools/motion3/extract-body.mjs +103 -0
  349. package/tools/motion3/extract-rig.mjs +85 -0
  350. package/tools/process-supervisor.mjs +0 -88
  351. package/tools/run-tests.mjs +9 -125
  352. package/bin/agent/motion-runtime.mjs +0 -76
  353. package/dist/ardy/cskel27-rest.json +0 -557
  354. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  355. package/dist/assets/app-B_2XB9rw.css +0 -1
  356. package/dist/assets/app-DAuvKPP8.js +0 -4920
  357. package/dist/assets/config-Bx7pYsD9.js +0 -1
  358. package/dist/assets/config-DeoFTYzb.css +0 -1
  359. package/dist/assets/demo-CvKuBgy5.js +0 -1
  360. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  361. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  362. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  363. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  364. package/dist/demo/walk-then-stop.npz +0 -0
  365. package/mcp/ardy-prompts.mjs +0 -218
  366. package/src/ardy/auto-fix-panel.css +0 -108
  367. package/src/ardy/auto-physics.js +0 -1194
  368. package/src/ardy/client.js +0 -153
  369. package/src/ardy/convert.js +0 -313
  370. package/src/ardy/cskel27-neutral.js +0 -39
  371. package/src/ardy/cskel27.js +0 -77
  372. package/src/ardy/export.js +0 -165
  373. package/src/ardy/fix-collisions.js +0 -2257
  374. package/src/ardy/foot-lock.js +0 -230
  375. package/src/ardy/motion-calibration.js +0 -118
  376. package/src/ardy/motion-edit.js +0 -213
  377. package/src/ardy/motion-url.js +0 -55
  378. package/src/ardy/mp4-duration.js +0 -102
  379. package/src/ardy/npz.js +0 -595
  380. package/src/ardy/physics-panel.css +0 -21
  381. package/src/ardy/physics-panel.jsx +0 -89
  382. package/src/ardy/physics-refine.js +0 -111
  383. package/src/ardy/physics-review.js +0 -567
  384. package/src/ardy/physics-temporal.js +0 -54
  385. package/src/ardy/platform-fit-panel.css +0 -64
  386. package/src/ardy/platform-fit-panel.jsx +0 -41
  387. package/src/ardy/platform-fit.js +0 -326
  388. package/src/ardy/playback.js +0 -711
  389. package/src/ardy/pose-pin.js +0 -97
  390. package/src/ardy/prompt-clips.js +0 -30
  391. package/src/ardy/retime.js +0 -211
  392. package/src/ardy/root-drop.js +0 -369
  393. package/src/ardy/timeline-resize.js +0 -13
  394. package/src/ardy/to-cskel27.js +0 -225
  395. package/src/ardy/trim.js +0 -33
  396. package/src/ardy/waypoints.js +0 -369
  397. package/src/ardy-bridge-contract.js +0 -281
  398. package/src/domains/motion/motion-video-capture.js +0 -664
  399. package/src/i2v-motion-client.js +0 -77
  400. package/src/i2v-motion-studio.jsx +0 -180
  401. package/src/line-edit.js +0 -1348
  402. package/src/motion/generation.js +0 -105
  403. package/src/motion-readiness-ui.jsx +0 -67
  404. package/src/motion-readiness.js +0 -40
  405. package/src/motion-resources.js +0 -177
  406. package/src/motion-store.js +0 -65
  407. package/src/motion-trail.js +0 -601
  408. package/src/multimodel-ingest.js +0 -394
  409. package/src/panels/PromptBlocksPanel.jsx +0 -444
  410. package/src/panels/VideoCapturePanel.jsx +0 -181
  411. package/src/pose-extract/browser.js +0 -42
  412. package/src/pose-extract/detector.js +0 -100
  413. package/src/pose-extract/fit.js +0 -227
  414. package/src/pose-extract/image-frame.js +0 -51
  415. package/src/pose-extract/index.js +0 -58
  416. package/src/pose-extract/keyframes.js +0 -74
  417. package/src/pose-extract/landmarks.js +0 -162
  418. package/src/pose-extract/mirror.js +0 -153
  419. package/src/pose-extract/take.js +0 -131
  420. package/src/pose-extract/video-frames.js +0 -91
  421. package/src/take-recipe.js +0 -165
  422. package/src/trail-key-conflicts.js +0 -39
  423. package/src/trail-pick.js +0 -59
  424. package/src/use-i2v-capture-modal.js +0 -15
  425. package/tools/ardy/BRIDGE.md +0 -37
  426. package/tools/ardy/GVHMR-NPZ.md +0 -68
  427. package/tools/ardy/README.md +0 -124
  428. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  429. package/tools/ardy/artifacts.mjs +0 -34
  430. package/tools/ardy/bridge.mjs +0 -1732
  431. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  432. package/tools/ardy/cclay_constrained_generate.py +0 -561
  433. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  434. package/tools/ardy/cclay_motion_edit.py +0 -510
  435. package/tools/ardy/cclay_sequence_generate.py +0 -705
  436. package/tools/ardy/cclay_worker.py +0 -304
  437. package/tools/ardy/dump-npz.py +0 -205
  438. package/tools/ardy/extract-rest.mjs +0 -299
  439. package/tools/ardy/extract.mjs +0 -507
  440. package/tools/ardy/footage.mjs +0 -467
  441. package/tools/ardy/gvhmr-floor.mjs +0 -102
  442. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  443. package/tools/ardy/gvhmr_fastpath.py +0 -168
  444. package/tools/ardy/gvhmr_trajectory.py +0 -240
  445. package/tools/ardy/merge-text-encoder.py +0 -160
  446. package/tools/ardy/mocap-metrics.mjs +0 -200
  447. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  448. package/tools/ardy/motion-stabilize.mjs +0 -348
  449. package/tools/ardy/npz.mjs +0 -414
  450. package/tools/ardy/pose-to-npz.mjs +0 -106
  451. package/tools/ardy/prompt-limits.mjs +0 -5
  452. package/tools/ardy/run-edit-on-box.sh +0 -73
  453. package/tools/ardy/run-local.mjs +0 -233
  454. package/tools/ardy/run-on-box.sh +0 -593
  455. package/tools/ardy/run-sequence-on-box.sh +0 -177
  456. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  457. package/tools/ardy/runners/index.mjs +0 -18
  458. package/tools/ardy/runners/local.mjs +0 -455
  459. package/tools/ardy/runners/proc.mjs +0 -125
  460. package/tools/ardy/runners/remote.mjs +0 -252
  461. package/tools/ardy/setup-local.mjs +0 -189
  462. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  463. package/tools/ardy/setup-text-encoder.py +0 -306
  464. package/tools/ardy/smpl-cskel27.mjs +0 -178
  465. package/tools/ardy/visual-qa.mjs +0 -196
  466. package/tools/ardy/vq-car.mjs +0 -52
  467. package/tools/bench/EXP3.md +0 -61
  468. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  469. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  470. package/tools/bench/cclay_bench_runner.py +0 -42
  471. package/tools/bench/cube-contact.mjs +0 -162
  472. package/tools/bench/exp3.mjs +0 -152
  473. package/tools/bench/extract-bench-lib.mjs +0 -216
  474. package/tools/bench/extract-bench.mjs +0 -302
  475. package/tools/bench/fal-generate.mjs +0 -88
  476. package/tools/bench/fit/README.md +0 -189
  477. package/tools/bench/fit/camera.mjs +0 -59
  478. package/tools/bench/fit/contact.mjs +0 -419
  479. package/tools/bench/fit/footlock.mjs +0 -234
  480. package/tools/bench/fit/motion.mjs +0 -85
  481. package/tools/bench/fit/pin.mjs +0 -27
  482. package/tools/bench/fit/remote.mjs +0 -105
  483. package/tools/bench/fit-bench.mjs +0 -126
  484. package/tools/bench/fit-sanity.mjs +0 -45
  485. package/tools/bench/metrics.mjs +0 -313
  486. package/tools/bench/obs/depth.mjs +0 -258
  487. package/tools/bench/obs/extrinsics.mjs +0 -175
  488. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  489. package/tools/bench/obs/ground.mjs +0 -294
  490. package/tools/bench/obs/heading.mjs +0 -151
  491. package/tools/bench/obs/ladder.mjs +0 -555
  492. package/tools/bench/obs/mannequin-betas.json +0 -124
  493. package/tools/bench/obs/remote.mjs +0 -83
  494. package/tools/bench/obs/rest_joints.py +0 -52
  495. package/tools/bench/obs/ybot-targets.mjs +0 -49
  496. package/tools/bench/obs-bench.mjs +0 -520
  497. package/tools/bench/score.mjs +0 -343
  498. package/tools/bench/summarize.mjs +0 -86
  499. package/tools/demo-worker/README.md +0 -137
  500. package/tools/demo-worker/api-client.mjs +0 -233
  501. package/tools/demo-worker/generate.mjs +0 -301
  502. package/tools/demo-worker/index.mjs +0 -466
  503. package/tools/demo-worker/sign-core.mjs +0 -112
  504. package/tools/demo-worker/sign.mjs +0 -161
  505. package/tools/gt-render/browser.mjs +0 -182
  506. package/tools/gt-render/camera-math.mjs +0 -276
  507. package/tools/gt-render/page.mjs +0 -267
  508. package/tools/gt-render/render.mjs +0 -514
  509. package/tools/gt-render/scene-box.mjs +0 -31
  510. package/tools/gt-render/take-transform.mjs +0 -87
  511. package/tools/kimodo/compare-seam.mjs +0 -108
  512. package/tools/kimodo/constraints.mjs +0 -244
  513. package/tools/kimodo/edit.mjs +0 -259
  514. package/tools/kimodo/effector-constraints.mjs +0 -200
  515. package/tools/kimodo/generate.mjs +0 -646
  516. package/tools/kimodo/local-output.mjs +0 -265
  517. package/tools/kimodo/measure-pose.mjs +0 -65
  518. package/tools/kimodo/measure-preserve.mjs +0 -899
  519. package/tools/kimodo/measure-waypoints.mjs +0 -55
  520. package/tools/kimodo/mlx-generate.py +0 -111
  521. package/tools/kimodo/pose-constraints.mjs +0 -248
  522. package/tools/kimodo/preserve-mask.mjs +0 -543
  523. package/tools/kimodo/read-npz.mjs +0 -189
  524. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  525. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  526. package/tools/kimodo/runner.mjs +0 -284
  527. package/tools/kimodo/seam-batch.mjs +0 -77
  528. package/tools/kimodo/setup-local.mjs +0 -30
  529. package/tools/kimodo/setup-on-box.sh +0 -339
  530. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  531. package/tools/morphgs/assets/gen_truth.py +0 -39
  532. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  533. package/tools/morphgs/assets/playback-check.mjs +0 -30
  534. package/tools/morphgs/demo-gate.sh +0 -26
  535. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  536. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  537. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  538. package/tools/morphgs/setup-on-cluster.sh +0 -126
  539. package/tools/projflow/driver.py +0 -1069
  540. package/tools/projflow/generate.mjs +0 -805
  541. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  542. package/tools/projflow/line-edit-job.mjs +0 -547
  543. package/tools/projflow/replay.mjs +0 -452
  544. package/tools/projflow/runner.mjs +0 -269
  545. package/tools/projflow/service.mjs +0 -742
  546. package/tools/track/DESIGN.md +0 -513
  547. package/tools/track/backfill-provenance.mjs +0 -116
  548. package/tools/track/budget.mjs +0 -8
  549. package/tools/track/check-rig.mjs +0 -209
  550. package/tools/track/diagnostics.schema.json +0 -60
  551. package/tools/track/export-rig.mjs +0 -256
  552. package/tools/track/fallback.mjs +0 -7
  553. package/tools/track/fk-parity-fixture.mjs +0 -161
  554. package/tools/track/gate.mjs +0 -349
  555. package/tools/track/masks.mjs +0 -318
  556. package/tools/track/metrics.mjs +0 -201
  557. package/tools/track/publish-obs.mjs +0 -108
  558. package/tools/track/py/check_env.py +0 -61
  559. package/tools/track/py/eval_lr.py +0 -229
  560. package/tools/track/py/lr_viterbi.py +0 -294
  561. package/tools/track/py/masks.py +0 -405
  562. package/tools/track/py/objective.py +0 -382
  563. package/tools/track/py/rig.py +0 -508
  564. package/tools/track/py/scene.py +0 -326
  565. package/tools/track/py/test_joint_indices.py +0 -123
  566. package/tools/track/py/test_lr_viterbi.py +0 -195
  567. package/tools/track/py/test_masks.py +0 -229
  568. package/tools/track/py/test_rig.py +0 -288
  569. package/tools/track/py/test_scene.py +0 -354
  570. package/tools/track/py/test_track.py +0 -328
  571. package/tools/track/py/track.py +0 -411
  572. package/tools/track/remote.mjs +0 -140
  573. package/tools/track/rig-dump.mjs +0 -227
  574. package/tools/track/run-box-tests.mjs +0 -14
  575. package/tools/track/run-box.mjs +0 -168
  576. package/tools/track/setup-box.sh +0 -59
  577. package/tools/track/study-2d.mjs +0 -728
  578. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  579. /package/src/{ardy → motion}/ground.js +0 -0
  580. /package/src/{ardy → motion}/ik-drag.js +0 -0
  581. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  582. /package/src/{ardy → motion}/ik.js +0 -0
  583. /package/src/{ardy → motion}/physics-surface.js +0 -0
  584. /package/src/{ardy → motion}/playback-clock.js +0 -0
  585. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -1,369 +0,0 @@
1
- const cleanZero = (value) => (Object.is(value, -0) || Math.abs(value) < 1e-9 ? 0 : value);
2
- import { isKo } from "../locale.js";
3
- import { TIMELINE_FRAME_FPS } from "../timeline-fps.js";
4
-
5
- const DEFAULT_WAYPOINT_SPACING_FRAMES = Math.round(0.4 * TIMELINE_FRAME_FPS);
6
-
7
- /**
8
- * ARDY root constraints live in the actor's clip-local coordinates. CozyClay's
9
- * plan uses scene-world coordinates and rotates Subject 1 as a scene object, so
10
- * both translation and actor yaw must be removed before generation. Playback
11
- * applies the exact inverse transform when it places the generated root.
12
- */
13
- export function toArdyWaypoints(waypoints, actorRotationDeg = 0) {
14
- if (!waypoints.length) return [];
15
- const origin = waypoints[0];
16
- const yaw = (actorRotationDeg * Math.PI) / 180;
17
- const cos = Math.cos(yaw);
18
- const sin = Math.sin(yaw);
19
- return waypoints.map((waypoint) => {
20
- const worldX = waypoint.x - origin.x;
21
- const worldZ = waypoint.z - origin.z;
22
- return {
23
- frame: waypoint.frame,
24
- x: cleanZero(cos * worldX - sin * worldZ),
25
- z: cleanZero(sin * worldX + cos * worldZ),
26
- heading: waypoint.heading ?? null,
27
- };
28
- });
29
- }
30
- /**
31
- * Densify an authored root path for ARDY motion generation: rebase to
32
- * clip-local coordinates exactly like toArdyWaypoints, interpolate on the
33
- * authored polyline, and emit one sample roughly every `spacing` frames
34
- * (capped at maxPoints). Authored frames are always kept, the first
35
- * sample is always frame 0, and frames are strictly ascending without
36
- * duplicates. The spacing is deliberately loose: a sample every 2-3
37
- * frames rails the root so hard the model gives up its gait cadence and
38
- * ice-skates; ~0.4 s between position pins leaves room for real steps
39
- * while the interpolated headings still steer the facing.
40
- * Each sample heading is the path tangent (central difference between
41
- * neighbors; adjacent-segment direction at the ends), carried forward
42
- * through stalls shorter than 1e-6 and 0 when the whole path is
43
- * stationary; headings are always finite numbers in [-2π, 2π].
44
- */
45
- export function densifyArdyWaypoints(waypoints, actorRotationDeg = 0, maxPoints = 32, spacing = DEFAULT_WAYPOINT_SPACING_FRAMES) {
46
- if (waypoints.length < 2) return [];
47
- const local = toArdyWaypoints(waypoints, actorRotationDeg);
48
-
49
- // Authored frames and positions, deduplicated: these are the
50
- // interpolation endpoints every sample set must contain.
51
- const authored = [];
52
- for (const point of local) {
53
- if (authored.length === 0 || authored[authored.length - 1].frame !== point.frame) {
54
- authored.push({ frame: point.frame, x: point.x, z: point.z });
55
- }
56
- }
57
- if (authored.length === 1) {
58
- return [{ frame: authored[0].frame, x: authored[0].x, z: authored[0].z, heading: 0 }];
59
- }
60
-
61
- const denseCount = authored[authored.length - 1].frame - authored[0].frame + 1;
62
- const limit = Math.max(0, Math.floor(maxPoints));
63
- const gapSpacing = Math.max(1, Math.floor(spacing));
64
- // One filler per started `spacing` chunk beyond the first, per gap.
65
- let spacingExtra = 0;
66
- for (let i = 0; i < authored.length - 1; i += 1) {
67
- spacingExtra += Math.max(0, Math.ceil((authored[i + 1].frame - authored[i].frame) / gapSpacing) - 1);
68
- }
69
- const extra = Math.max(0, Math.min(limit, denseCount, authored.length + spacingExtra) - authored.length);
70
-
71
- // Distribute the extra samples across the gaps between authored frames
72
- // in proportion to gap length (largest remainder), so spacing stays
73
- // uniform while authored frames are always kept.
74
- const gaps = [];
75
- for (let i = 0; i < authored.length - 1; i += 1) {
76
- gaps.push(authored[i + 1].frame - authored[i].frame);
77
- }
78
- const gapSizes = gaps.map(() => 0);
79
- if (extra > 0) {
80
- const total = gaps.reduce((sum, size) => sum + size, 0);
81
- const raw = gaps.map((size) => (extra * size) / total);
82
- const base = raw.map((value) => Math.floor(value));
83
- const order = raw
84
- .map((value, i) => [value - base[i], i])
85
- .sort((a, b) => b[0] - a[0]);
86
- let used = base.reduce((sum, value) => sum + value, 0);
87
- for (const [, i] of order) {
88
- if (used >= extra) break;
89
- const add = Math.min(extra - used, gaps[i] - 1 - base[i]);
90
- base[i] += add;
91
- used += add;
92
- }
93
- for (let i = 0; i < gaps.length; i += 1) gapSizes[i] = base[i];
94
- }
95
-
96
- const frames = [];
97
- for (let i = 0; i < authored.length; i += 1) {
98
- if (i > 0) {
99
- const count = gapSizes[i - 1];
100
- if (count > 0) {
101
- const step = gaps[i - 1] / (count + 1);
102
- for (let j = 0; j < count; j += 1) {
103
- frames.push(authored[i - 1].frame + Math.round((j + 1) * step));
104
- }
105
- }
106
- }
107
- frames.push(authored[i].frame);
108
- }
109
-
110
- // C1 resampling: cubic Hermite through the authored pins, with each pin's
111
- // velocity as the finite-difference average of its two segment velocities
112
- // (one-sided at the ends). A C0 polyline pins an instantaneous direction
113
- // flip at every interior waypoint — and, when authored frames imply
114
- // different paces, an instantaneous speed jump — which the generator can
115
- // only satisfy by braking and pivoting on the pin: the hitch you see as
116
- // the root crosses waypoint 2. Hermite keeps authored pins exact, keeps a
117
- // uniform straight path exactly linear, and turns corners into arcs where
118
- // both travel direction and speed ramp continuously. (A near-180° reversal
119
- // will swing wide of the pin — that wide swing is the C1 answer to an
120
- // about-face, not an error.)
121
- const segmentVelocity = (a, b) => ({
122
- x: (b.x - a.x) / (b.frame - a.frame),
123
- z: (b.z - a.z) / (b.frame - a.frame),
124
- });
125
- const stalledSegment = (a, b) => (b.x - a.x) * (b.x - a.x) + (b.z - a.z) * (b.z - a.z) < 1e-12;
126
- const velocity = authored.map((point, i) => {
127
- const prev = authored[i - 1];
128
- const next = authored[i + 1];
129
- // A knot beside a stall comes to a full stop: leaking a neighbour's
130
- // velocity into a hold would make the "stationary" span drift, and a
131
- // walker entering a hold decelerates to zero anyway.
132
- if (!prev) return stalledSegment(point, next) ? { x: 0, z: 0 } : segmentVelocity(point, next);
133
- if (!next) return stalledSegment(prev, point) ? { x: 0, z: 0 } : segmentVelocity(prev, point);
134
- if (stalledSegment(prev, point) || stalledSegment(point, next)) return { x: 0, z: 0 };
135
- const before = segmentVelocity(prev, point);
136
- const after = segmentVelocity(point, next);
137
- return { x: (before.x + after.x) / 2, z: (before.z + after.z) / 2 };
138
- });
139
- const samples = [];
140
- let seg = 0;
141
- for (const frame of frames) {
142
- while (frame > authored[seg + 1].frame) seg += 1;
143
- const start = authored[seg];
144
- const end = authored[seg + 1];
145
- const span = end.frame - start.frame;
146
- const t = (frame - start.frame) / span;
147
- const h00 = (1 + 2 * t) * (1 - t) * (1 - t);
148
- const h10 = t * (1 - t) * (1 - t);
149
- const h01 = t * t * (3 - 2 * t);
150
- const h11 = t * t * (t - 1);
151
- const v0 = velocity[seg];
152
- const v1 = velocity[seg + 1];
153
- samples.push({
154
- frame,
155
- x: cleanZero(h00 * start.x + h10 * span * v0.x + h01 * end.x + h11 * span * v1.x),
156
- z: cleanZero(h00 * start.z + h10 * span * v0.z + h01 * end.z + h11 * span * v1.z),
157
- heading: 0,
158
- });
159
- }
160
-
161
- // Path-tangent headings. Interior samples use the central difference
162
- // between neighbors, the ends use their adjacent segment; displacements
163
- // under 1e-6 (stalls) carry the previous heading, 0 when stationary.
164
- // Sign convention measured against the deployed generator, not assumed.
165
- // With the path aligned so heading[0] = 0 matches the model's forced
166
- // frame-0 facing, the generated hips reproduce the SENT angle exactly
167
- // (a probe sending mirrored headings walked the second segment facing
168
- // +77.7° while travelling at −77.7°). Earlier mirror-looking results
169
- // came from clips whose frame-0 facing conflicted with heading[0] and
170
- // corrupted the whole track. Model heading = atan2(dx, dz) of the
171
- // facing/travel direction; 0 faces +Z.
172
- let previousHeading = 0;
173
- for (let i = 0; i < samples.length; i += 1) {
174
- let dx = 0;
175
- let dz = 0;
176
- if (samples.length > 1) {
177
- if (i === 0) {
178
- dx = samples[1].x - samples[0].x;
179
- dz = samples[1].z - samples[0].z;
180
- } else if (i === samples.length - 1) {
181
- dx = samples[i].x - samples[i - 1].x;
182
- dz = samples[i].z - samples[i - 1].z;
183
- } else {
184
- dx = samples[i + 1].x - samples[i - 1].x;
185
- dz = samples[i + 1].z - samples[i - 1].z;
186
- }
187
- }
188
- if (dx * dx + dz * dz >= 1e-12) previousHeading = Math.atan2(dx, dz);
189
- samples[i].heading = cleanZero(previousHeading);
190
- }
191
- return samples;
192
- }
193
- /**
194
- * Align an authored root path for ARDY generation: compute the clip-local
195
- * first travel tangent (first non-zero authored displacement, squared
196
- * length >= 1e-12) and fold it into the total rotation so the densified
197
- * path starts with heading 0, the model's forced frame-0 +Z facing. The
198
- * returned rotationDeg is the scene->clip total rotation; handing it to
199
- * toSceneRootOffset at playback restores scene coordinates exactly.
200
- */
201
- export function alignArdyPath(waypoints, actorRotationDeg = 0, maxPoints = 32) {
202
- if (waypoints.length < 2) return { waypoints: [], rotationDeg: actorRotationDeg };
203
- // Fold the first *sampled* travel direction into the rotation, not the
204
- // authored chord: the C1 resampler bends inside the first segment, so only
205
- // the dense samples know the true first step. Rotation is rigid, so
206
- // densifying again with the folded rotation rotates those samples exactly
207
- // and heading[0] lands on 0 by construction.
208
- const probe = densifyArdyWaypoints(waypoints, actorRotationDeg, maxPoints);
209
- let phi = 0;
210
- for (let i = 0; i < probe.length - 1; i += 1) {
211
- const dx = probe[i + 1].x - probe[i].x;
212
- const dz = probe[i + 1].z - probe[i].z;
213
- if (dx * dx + dz * dz >= 1e-12) {
214
- phi = Math.atan2(dx, dz);
215
- break;
216
- }
217
- }
218
- const rotationDeg = actorRotationDeg + (phi * 180) / Math.PI;
219
- return {
220
- waypoints: densifyArdyWaypoints(waypoints, rotationDeg, maxPoints),
221
- rotationDeg,
222
- };
223
- }
224
-
225
- /** Rotate an ARDY clip-local root offset back into CozyClay scene space. */
226
- export function toSceneRootOffset(x, z, actorRotationDeg = 0) {
227
- const yaw = (actorRotationDeg * Math.PI) / 180;
228
- const cos = Math.cos(yaw);
229
- const sin = Math.sin(yaw);
230
- return {
231
- x: cleanZero(cos * x + sin * z),
232
- z: cleanZero(-sin * x + cos * z),
233
- };
234
- }
235
-
236
- /**
237
- * A full-body pose also constrains the root. On a root path it must share the
238
- * frame-zero origin; otherwise the two constraints can pull the clip apart.
239
- */
240
- export function poseConstraintFrame(requestedFrame, clipFrames, hasRootPath) {
241
- if (hasRootPath) return 0;
242
- return Math.max(0, Math.min(Math.round(requestedFrame) || 0, clipFrames - 1));
243
- }
244
-
245
- // What the generator can actually express, learned the hard way: waypoints
246
- // are inpainting observations ARDY cannot refuse, so a request outside the
247
- // training distribution collapses into foot-sliding instead of erroring.
248
- // These limits keep authored paths inside that distribution.
249
- export const PATH_LIMITS = Object.freeze({
250
- speedMinMps: 0.5, // below this, "walking" cannot cycle a gait — feet slide
251
- speedMaxMps: 3.0, // above this is out of the locomotion band entirely
252
- holdEpsM: 0.05, // a displacement this small is a deliberate hold, any duration
253
- minPinGapFrames: 8, // authored pins closer than this over-rail the root
254
- speedRatioWarn: 2, // adjacent legs differing more than 2x force a gait change
255
- turnRateWarnDegPerS: 120, // sharper turns need wider arcs than the path gives
256
- tailMaxS: 2, // unconstrained tail after the last pin inherits its history
257
- clipMaxS: 10, // ARDY's trained window; one-shot constrained calls cannot exceed it
258
- });
259
-
260
- const NATURAL_WALK_MPS = 1.4;
261
-
262
- /**
263
- * Judge the segment a candidate pin would create after `last`. Returns
264
- * { ok, error?, warnings: [] } — `error` blocks the placement and always
265
- * names the fix (a workable frame or distance), `warnings` accompany a
266
- * placement that is legal but will read oddly.
267
- * @param {{frame:number,x:number,z:number}} last the previous pin (or the frame-0 start)
268
- * @param {{frame:number,x:number,z:number}} candidate the pin being authored
269
- * @param {number} fps
270
- * @param {{frame:number,x:number,z:number}|null} prev the pin before `last`, for ratio/turn warnings
271
- */
272
- export function judgeNextWaypoint(last, candidate, fps, prev = null) {
273
- const gap = candidate.frame - last.frame;
274
- if (gap < PATH_LIMITS.minPinGapFrames) {
275
- return {
276
- ok: false,
277
- error: isKo ? `핀 사이에는 최소 ${PATH_LIMITS.minPinGapFrames}프레임(${(PATH_LIMITS.minPinGapFrames / fps).toFixed(1)}초)이 필요해요 — ${last.frame + PATH_LIMITS.minPinGapFrames}프레임 이후로 이동해 주세요` : `pins need at least ${PATH_LIMITS.minPinGapFrames} frames between them (${(PATH_LIMITS.minPinGapFrames / fps).toFixed(1)} s) — scrub past frame ${last.frame + PATH_LIMITS.minPinGapFrames}`,
278
- warnings: [],
279
- };
280
- }
281
- const dist = Math.hypot(candidate.x - last.x, candidate.z - last.z);
282
- if (dist <= PATH_LIMITS.holdEpsM) return { ok: true, warnings: [] }; // a hold: legal at any length
283
- const dt = gap / fps;
284
- const speed = dist / dt;
285
- if (speed > PATH_LIMITS.speedMaxMps) {
286
- const neededFrames = Math.ceil((dist / PATH_LIMITS.speedMaxMps) * fps);
287
- return {
288
- ok: false,
289
- error: isKo ? `${speed.toFixed(1)} m/s는 사람이 움직이기엔 너무 빨라요(최대 ${PATH_LIMITS.speedMaxMps}) — ${last.frame + neededFrames}프레임 이후에 핀을 찍거나 더 가까운 곳을 눌러 주세요` : `${speed.toFixed(1)} m/s is faster than anyone moves (max ${PATH_LIMITS.speedMaxMps}) — pin frame ${last.frame + neededFrames} or later, or click closer`,
290
- warnings: [],
291
- };
292
- }
293
- if (speed < PATH_LIMITS.speedMinMps) {
294
- const walkFrame = last.frame + Math.max(PATH_LIMITS.minPinGapFrames, Math.round((dist / NATURAL_WALK_MPS) * fps));
295
- const neededDist = (PATH_LIMITS.speedMinMps * dt).toFixed(1);
296
- return {
297
- ok: false,
298
- error: isKo ? `${speed.toFixed(2)} m/s는 자연스럽게 걷기엔 너무 느려요(최소 ${PATH_LIMITS.speedMinMps}) — 대신 약 ${walkFrame}프레임에 핀을 찍거나 최소 ${neededDist} m 이상 떨어진 곳을 눌러 주세요` : `${speed.toFixed(2)} m/s is below a sustainable walk (min ${PATH_LIMITS.speedMinMps}) — pin frame ~${walkFrame} instead, or click at least ${neededDist} m away`,
299
- warnings: [],
300
- };
301
- }
302
- const warnings = [];
303
- if (prev) {
304
- const prevDist = Math.hypot(last.x - prev.x, last.z - prev.z);
305
- if (prevDist > PATH_LIMITS.holdEpsM) {
306
- const prevSpeed = prevDist / ((last.frame - prev.frame) / fps);
307
- const ratio = Math.max(speed, prevSpeed) / Math.max(Math.min(speed, prevSpeed), 1e-6);
308
- if (ratio > PATH_LIMITS.speedRatioWarn) {
309
- warnings.push(isKo ? `이전 구간보다 속도가 ${ratio.toFixed(1)}배 달라요 — 걸음 변화가 눈에 보일 수 있어요` : `${ratio.toFixed(1)}x speed change from the previous leg — expect a visible gait shift`);
310
- }
311
- const before = Math.atan2(last.x - prev.x, last.z - prev.z);
312
- const after = Math.atan2(candidate.x - last.x, candidate.z - last.z);
313
- let turn = Math.abs(after - before);
314
- if (turn > Math.PI) turn = 2 * Math.PI - turn;
315
- const turnRate = (turn * 180) / Math.PI / dt;
316
- if (turnRate > PATH_LIMITS.turnRateWarnDegPerS) {
317
- warnings.push(isKo ? `${dt.toFixed(1)}초 동안 ${Math.round((turn * 180) / Math.PI)}° 회전은 걷는 동선치고 너무 급해요 — 프레임이나 거리를 더 주세요` : `${Math.round((turn * 180) / Math.PI)}° turn in ${dt.toFixed(1)} s is sharper than a walking arc — give it more frames or distance`);
318
- }
319
- }
320
- }
321
- return { ok: true, warnings };
322
- }
323
-
324
- /**
325
- * Re-judge a whole authored path (frame-0 start included) before generation:
326
- * placement-time checks can be invalidated later by removing a middle pin.
327
- * @returns {{errors: string[], warnings: string[]}}
328
- */
329
- export function judgeAuthoredPath(rootPath, fps, clipFrames, { chained = false } = {}) {
330
- const errors = [];
331
- const warnings = [];
332
- const ordered = [...rootPath].sort((a, b) => a.frame - b.frame);
333
- for (let i = 1; i < ordered.length; i += 1) {
334
- const verdict = judgeNextWaypoint(ordered[i - 1], ordered[i], fps, i >= 2 ? ordered[i - 2] : null);
335
- if (!verdict.ok) errors.push(isKo ? `구간 ${i} (${ordered[i - 1].frame}->${ordered[i].frame}프레임): ${verdict.error}` : `leg ${i} (frame ${ordered[i - 1].frame}->${ordered[i].frame}): ${verdict.error}`);
336
- else for (const warning of verdict.warnings) warnings.push(isKo ? `구간 ${i}: ${warning}` : `leg ${i}: ${warning}`);
337
- }
338
- // The trained window binds ONE model call. A chained rollout (a prompt
339
- // schedule) makes a call per block, so the whole clip may exceed it —
340
- // each block is capped separately by the bridge and the box generator.
341
- if (!chained && clipFrames / fps > PATH_LIMITS.clipMaxS) {
342
- errors.push(
343
- isKo
344
- ? `이동 경로는 전체 클립을 한 번의 모델 호출로 만들어요. 모션 모델이 학습한 길이는 ${PATH_LIMITS.clipMaxS}초까지예요 — 클립을 ${PATH_LIMITS.clipMaxS}초로 줄이거나 프롬프트를 블록으로 나눠 이어 만들기를 사용해 주세요`
345
- : `a root path generates the whole clip in one model call, and the motion model's trained window is ${PATH_LIMITS.clipMaxS} s — shorten the clip to ${PATH_LIMITS.clipMaxS} s, or split the prompt into blocks so the path rides a chained rollout`,
346
- );
347
- }
348
- const tail = (clipFrames - 1 - (ordered[ordered.length - 1]?.frame ?? 0)) / fps;
349
- if (ordered.length > 1 && tail > PATH_LIMITS.tailMaxS) {
350
- warnings.push(
351
- isKo
352
- ? `마지막 핀 뒤에 클립이 ${tail.toFixed(1)}초 남아 있어요 — 제약이 없는 끝부분은 경로가 끝난 상태를 그대로 이어가요`
353
- : `${tail.toFixed(1)} s of clip remain after the last pin — the unconstrained tail continues whatever the path ends in`,
354
- );
355
- }
356
- return { errors, warnings };
357
- }
358
-
359
- /** Place a newly-authored keyframe where it is immediately visible and draggable. */
360
- export function defaultWaypointPosition(waypoints, subject) {
361
- if (!waypoints.length) return { x: subject.x, z: subject.z };
362
- const last = waypoints[waypoints.length - 1];
363
- if (waypoints.length > 1) {
364
- const previous = waypoints[waypoints.length - 2];
365
- return { x: last.x + (last.x - previous.x), z: last.z + (last.z - previous.z) };
366
- }
367
- const yaw = (subject.rot * Math.PI) / 180;
368
- return { x: last.x + Math.sin(yaw), z: last.z + Math.cos(yaw) };
369
- }
@@ -1,281 +0,0 @@
1
- import { ko, isKo } from "./locale.js";
2
- import { LINE_EDIT_TRACK_IDS, PINNED_CURVE_ENDS } from "./line-edit.js";
3
- import { FK_TRACKS, IK_TRACKS, MID_TRACKS } from "./ardy/ik.js";
4
-
5
- // Two clocks, one boundary. The app timeline runs the production 24 fps —
6
- // the rate the reference footage and the recorded export are counted in.
7
- // ARDY Core generates on its trained 20 fps clock and that cannot move, so
8
- // takes are RETIMED inbound (bridge → app, retimeMotion) and frame numbers
9
- // are CONVERTED outbound (app → bridge, toArdyFrame). Nothing between the
10
- // boundaries may mix the clocks.
11
- export const TIMELINE_FPS = 24;
12
- // The motion bridge uses the Studio's fixed 24 fps production clock.
13
- export const ARDY_FPS = 24;
14
- export const toArdyFrame = (frame) => Math.round((frame * ARDY_FPS) / TIMELINE_FPS);
15
-
16
- // Outbound converters: timeline-frame entries → strictly-ascending bridge
17
- // frames. Rounding can land two timeline frames on one bridge frame; the
18
- // first wins — the bridge refuses non-ascending lists outright.
19
- /** Where a placed pose lands, in TIMELINE frames, for a clip of `clipFrames`. */
20
- export function posePlacementFrame(placement, clipFrames, playheadFrame) {
21
- const last = Math.max(0, clipFrames - 1);
22
- if (placement === "end") return last;
23
- if (placement === "middle") return Math.round(last / 2);
24
- if (placement === "playhead") return Math.max(0, Math.min(last, playheadFrame));
25
- return 0;
26
- }
27
-
28
- export function toArdyFrameEntries(entries) {
29
- const out = [];
30
- for (const entry of entries) {
31
- const frame = toArdyFrame(entry.frame);
32
- if (out.length && frame <= out[out.length - 1].frame) continue;
33
- out.push({ ...entry, frame });
34
- }
35
- return out;
36
- }
37
-
38
- export function toArdySegments(segments) {
39
- const out = [];
40
- for (const segment of segments) {
41
- const startFrame = toArdyFrame(segment.startFrame);
42
- const endFrame = toArdyFrame(segment.endFrame);
43
- if (endFrame <= startFrame) continue; // a zero-length rounding remnant covers nothing
44
- out.push({ ...segment, startFrame, endFrame });
45
- }
46
- return out;
47
- }
48
-
49
- export const DEFAULT_DURATION_S = 15; // pre-motion timeline duration; shown as duration × TIMELINE_FPS frames
50
- export const DEFAULT_PLAYBACK_SPEED = 1;
51
- export const BRIDGE_RECHECK_MS = 3000;
52
- export const ARDY_PROMPT_HORIZON_FRAMES = 2 * TIMELINE_FPS; // core model horizon: 2 seconds, counted on the timeline clock
53
- export const MAX_WAYPOINTS = 32; // ARDY bridge contract: a root path holds 2..32 distinct waypoint frames
54
- export const ARDY_PROMPT_MAX = 500; // bridge contract: prompt must be non-empty, capped at 500 chars
55
- export const ARDY_DURATION_MIN = 1; // the UI works in whole seconds; the bridge floor is 0.15 s
56
- export const ARDY_DURATION_MAX = 1200; // bridge contract: duration capped at 1200 s
57
- export const ARDY_SEED_MAX = 2 ** 31 - 1; // bridge contract: optional seed, integer in 0..2**31-1
58
- // Scheduled inpainting (contract C3): how hard a regeneration holds onto the
59
- // take that is already loaded. 0 turns preserving OFF entirely; 0.5 is the
60
- // paper's recommended setting. The app ships this RAW number and nothing else:
61
- // the strength -> denoising-schedule mapping (sigma_s = round(1000 * strength),
62
- // sigma_e = min(50, sigma_s), diffusion time 0..1000) lives on the box side, and
63
- // duplicating it here is exactly how the two would silently drift apart.
64
- export const ARDY_PRESERVE_DEFAULT = 0.5;
65
- export const DEFAULT_PROMPT_CLIPS = [];
66
-
67
- /** The prompt-block schedule: every authored block in frame order, with the gaps
68
- * between them filled by the base prompt so the bridge always receives one
69
- * contiguous 0..clipFrames sequence. Blocks shorter than 3 frames are dropped —
70
- * they cannot carry a generation.
71
- * Pure and shared on purpose: the request assembly decides which blocks count as
72
- * "edited", and the preserve panel has to name the SAME blocks. Re-deriving them
73
- * approximately beside the slider is how the UI starts promising a locality the
74
- * wire does not carry. */
75
- export function buildPromptSchedule(sourcePromptClips, clipFrames, prompt) {
76
- const segments = [];
77
- let cursor = 0;
78
- for (const clip of sourcePromptClips) {
79
- const startFrame = Math.max(cursor, Math.min(clipFrames, clip.startFrame));
80
- const endFrame = Math.max(startFrame, Math.min(clipFrames, clip.endFrame));
81
- if (startFrame > cursor) segments.push({ startFrame: cursor, endFrame: startFrame, prompt });
82
- if (endFrame - startFrame >= 3) segments.push({ startFrame, endFrame, prompt: clip.text.trim() || prompt });
83
- cursor = Math.max(cursor, endFrame);
84
- if (cursor >= clipFrames) break;
85
- }
86
- if (cursor < clipFrames) segments.push({ startFrame: cursor, endFrame: clipFrames, prompt });
87
- if (segments.length === 0) segments.push({ startFrame: 0, endFrame: clipFrames, prompt });
88
- return segments;
89
- }
90
-
91
- /** The DISTINCT ik track ids keyed inside the half-open timeline span
92
- * [startFrame, endFrame). `ikState.keys` is Map(frame -> Map(trackId -> pose)),
93
- * so a frame with several dragged effectors contributes all of them. */
94
- export function ikTracksInRange(ikState, ikFrames, startFrame, endFrame) {
95
- const tracks = new Set();
96
- for (const frame of ikFrames) {
97
- if (frame < startFrame || frame >= endFrame) continue;
98
- for (const trackId of ikState?.keys?.get(frame)?.keys() ?? []) tracks.add(trackId);
99
- }
100
- return [...tracks];
101
- }
102
-
103
- /* Which limb a keyed IK track frees once the preserve mask is grouped
104
- (contract C3v2). The single source of truth for track -> mask group is
105
- TRACK_GROUPS in tools/kimodo/preserve-mask.mjs; nothing under tools/ may be
106
- imported into src/, so the rule is transcribed here — as USER-FACING LABELS
107
- only, never as a second copy of the mapping the wire depends on:
108
- leftHand, leftElbow -> leftArm rightHand, rightElbow -> rightArm
109
- leftFoot, leftKnee -> leftLeg rightFoot, rightKnee -> rightLeg
110
- head, neck -> head spine, chest, leftShoulder,
111
- hips -> torso AND root rightShoulder -> torso
112
- The track ids are the same namespace on both sides (src/ardy/ik.js owns
113
- them), so the app ships whatever ids it has and the mask builder maps them.
114
- Torso and pelvis tracks are worded as the whole body: the pelvis carries the
115
- global transform as well as a bone, and naming a limb beside it would promise
116
- a locality the mask does not have. */
117
- export const PRESERVE_TRACK_LIMBS = {
118
- leftHand: "leftArm",
119
- leftElbow: "leftArm",
120
- rightHand: "rightArm",
121
- rightElbow: "rightArm",
122
- leftFoot: "leftLeg",
123
- leftKnee: "leftLeg",
124
- rightFoot: "rightLeg",
125
- rightKnee: "rightLeg",
126
- head: "head",
127
- neck: "head",
128
- spine: "body",
129
- chest: "body",
130
- leftShoulder: "body",
131
- rightShoulder: "body",
132
- hips: "body",
133
- };
134
- // A fixed reading order, so the same edit always reads back the same way.
135
- export const PRESERVE_LIMB_ORDER = ["head", "leftArm", "rightArm", "leftLeg", "rightLeg"];
136
- export const preserveLimbLabel = (limb) => ({
137
- head: ko("head", "머리"),
138
- leftArm: ko("left arm", "왼팔"),
139
- rightArm: ko("right arm", "오른팔"),
140
- leftLeg: ko("left leg", "왼발"),
141
- rightLeg: ko("right leg", "오른발"),
142
- }[limb]);
143
-
144
- /** One muted line naming what a grouped preserve run will regenerate, or "" when
145
- * no track can be attributed — then the request carries no `tracks` either and
146
- * the panel's existing whole-take wording is already the truth. */
147
- export function preserveTracksSummary(tracks) {
148
- const limbs = new Set();
149
- for (const trackId of tracks) {
150
- const limb = PRESERVE_TRACK_LIMBS[trackId];
151
- if (limb) limbs.add(limb);
152
- }
153
- if (limbs.size === 0) return "";
154
- if (limbs.has("body")) return ko("regenerates the whole body", "몸 전체를 다시 생성");
155
- const names = PRESERVE_LIMB_ORDER.filter((limb) => limbs.has(limb)).map(preserveLimbLabel);
156
- return isKo ? `${names.join("·")}만 다시 생성` : `regenerates ${names.join(" · ")} only`;
157
- }
158
-
159
- /* --------------------------- line editing (C6) ---------------------------
160
- * "Grab the joint's motion path and pull it; the joint follows the pulled path
161
- * exactly" (ProjFlow). The arithmetic lives in ./line-edit.js; what stays here
162
- * is the panel's own vocabulary and the one thing App owns that the pure module
163
- * cannot — the mapping from a track id to a HUMAN LABEL, which is read straight
164
- * out of ik.js's three tables so the picker can never drift from the pose
165
- * studio's own naming. An id in LINE_EDIT_TRACK_IDS with no entry in those
166
- * tables is a bug, and falling back to the raw id makes it visible instead of
167
- * silent. */
168
- export const LINE_EDIT_TRACK_OPTIONS = LINE_EDIT_TRACK_IDS.map((id) => ({
169
- value: id,
170
- label: [...IK_TRACKS, ...MID_TRACKS, ...FK_TRACKS].find((track) => track.id === id)?.label ?? id,
171
- }));
172
- /** The same human label, by id — what a version-strip chip says a refinement
173
- * touched ("Refine · Left Hand"). */
174
- export const lineTrackLabel = (id) => LINE_EDIT_TRACK_OPTIONS.find((option) => option.value === id)?.label ?? id;
175
- /** The default edited track: a wrist is the joint the paper's own demo draws
176
- * with, and the one whose screen trajectory a user can actually see. */
177
- export const LINE_EDIT_DEFAULT_TRACK = "leftHand";
178
- /** Draw a grab handle every Nth frame. Fewer would leave the curve looking
179
- * un-grabbable in the gaps; more turns a 200-frame range into a solid bead of
180
- * dots. The hit test tolerates CURVE_GRAB_RADIUS_PX regardless, so the handles
181
- * are an affordance rather than the actual geometry. */
182
- export const LINE_CURVE_MARKER_STRIDE = 4;
183
- /** The shortest curve with anything draggable in it: both ends are hard-pinned
184
- * to the original trajectory, so a range needs more than 2 x PINNED_CURVE_ENDS
185
- * frames before the middle can move at all. */
186
- export const MIN_CURVE_POINTS = PINNED_CURVE_ENDS * 2 + 1;
187
- /** How long a released drag has to sit still before it costs a preview. Long
188
- * enough that pull-pull-pull is one request instead of three, short enough that
189
- * the loop still feels like it answers the gesture — a preview round trip is
190
- * ~1 s on the warm service, so 150 ms is noise against it. */
191
- export const LINE_PREVIEW_DEBOUNCE_MS = 150;
192
- /** How often the line-edit capability is re-probed while the bridge is up and
193
- * the answer is still no.
194
- *
195
- * The bridge answers `capabilities.lineEdit` from a LAZY ssh probe of the
196
- * ProjFlow box behind a 5 s cache, so the very first /ardy/health of a session
197
- * can land on a cold cache and come back false — a box that is perfectly fine
198
- * two seconds later. Asking once per bridge-up transition therefore made the
199
- * whole mode dead until a reload (first boot broken, second boot fine, which is
200
- * exactly how it was reported). A capability is an ADVERTISEMENT, so it is
201
- * allowed to arrive late; what is not allowed is to assume it. 4 s sits just
202
- * under the bridge's own 5 s TTL, so a retry can actually see a new answer
203
- * rather than re-reading the same cached false. */
204
- export const LINE_CAPABILITY_RETRY_MS = 4000;
205
- /** Why a validateLineEdit code refused, said once, in the user's language.
206
- * Keyed by the pure module's stable codes so the copy and the check can never
207
- * disagree about which field was wrong. */
208
- export const LINE_EDIT_REFUSALS = {
209
- sourceMotion: ["The current take has no bridge source — generate it once before editing a path", "현재 테이크에 브리지 원본이 없어요 — 궤적을 편집하기 전에 한 번 생성하세요"],
210
- track: ["Pick a joint to edit first", "먼저 편집할 관절을 고르세요"],
211
- frameRange: ["The frame range must sit inside the clip and span at least 2 frames", "프레임 구간은 클립 안에 있어야 하고 최소 2프레임이어야 해요"],
212
- points: ["Pull a longer stretch of the path — a single point is not a path", "궤적을 좀 더 길게 잡아당겨 주세요 — 점 하나는 경로가 아니에요"],
213
- camera: ["The camera could not be captured — nudge the view and try again", "카메라를 캡처하지 못했어요 — 뷰를 조금 움직인 뒤 다시 시도하세요"],
214
- prompt: ["The motion prompt is not usable for a line edit", "모션 프롬프트를 라인 편집에 쓸 수 없어요"],
215
- pins: ["The pinned moments could not be sent — try clearing them and pinning again", "찍은 순간을 보낼 수 없어요 — 지우고 다시 찍어 보세요"],
216
- shape: ["The line edit could not be assembled", "라인 편집을 구성하지 못했어요"],
217
- };
218
- /** Why curveToPoints2d refused. Same shape, different stage: these are about
219
- * the CURVE, before there is a payload to validate. */
220
- export const LINE_CURVE_REFUSALS = {
221
- empty: ["This joint is not visible over the chosen frames — frame it in view first", "선택한 구간에서 이 관절이 보이지 않아요 — 먼저 화면 안에 들어오도록 잡아 주세요"],
222
- offscreen: ["The pulled path leaves the frame — keep it inside the dashed border", "잡아당긴 궤적이 화면을 벗어났어요 — 점선 테두리 안에 두세요"],
223
- };
224
-
225
- // Named ingest failures, in both locales. A reason the user cannot act on is
226
- // not a message: each line says what was wrong with THIS source.
227
- export const MULTIMODEL_REASONS = {
228
- "url-empty": ["Enter a video URL first", "영상 URL을 먼저 입력하세요"],
229
- "url-protocol-relative": ["Use a full https:// address", "https:// 로 시작하는 전체 주소를 쓰세요"],
230
- "url-scheme-unsupported": ["Only http(s) or a local /path is accepted", "http(s) 또는 로컬 /경로만 사용할 수 있어요"],
231
- "url-platform-page": [
232
- "YouTube/Vimeo pages are players, not video files, and block browser downloads. Start the dev bridge to use the URL directly, or download the clip and use Choose video.",
233
- "유튜브·비메오 주소는 영상 파일이 아니라 플레이어 페이지이고 브라우저 다운로드를 막습니다. 개발 브리지를 켜면 URL을 그대로 쓸 수 있고, 아니면 영상을 내려받아 '영상 선택'을 쓰세요.",
234
- ],
235
- "url-malformed": ["That address could not be parsed", "주소를 해석할 수 없어요"],
236
- "fetch-failed": ["The host refused a browser download (CORS or offline)", "호스트가 브라우저 다운로드를 거부했어요(CORS 또는 오프라인)"],
237
- "not-a-video": ["That address did not return a video (check the path)", "그 주소는 영상을 반환하지 않았어요(경로를 확인하세요)"],
238
- "decode-failed": ["This browser cannot decode that video", "이 브라우저가 디코딩할 수 없는 영상이에요"],
239
- "duration-unreadable": ["The clip length could not be read", "클립 길이를 읽지 못했어요"],
240
- "dimensions-unreadable": ["The frame size could not be read", "프레임 크기를 읽지 못했어요"],
241
- "probe-timeout": ["The video never reported metadata", "영상이 메타데이터를 보내지 않았어요"],
242
- "fetch-unavailable": ["This browser cannot download files", "이 브라우저는 파일을 내려받을 수 없어요"],
243
- "footage-load-timeout": ["The clip never became seekable", "클립이 탐색 가능한 상태가 되지 않았어요"],
244
- "seek-timeout": ["Seeking inside the clip stalled", "클립 내부 탐색이 멈췄어요"],
245
- "pose-runtime-unavailable": ["The pose engine is missing from this build", "이 빌드에 포즈 엔진이 없어요"],
246
- "pose-model-download-failed": ["The pose model could not be downloaded (offline?)", "포즈 모델을 내려받지 못했어요(오프라인인가요?)"],
247
- "pose-engine-download-failed": ["The pose engine could not be downloaded (offline?)", "포즈 엔진을 내려받지 못했어요(오프라인인가요?)"],
248
- "pose-engine-init-failed": ["The pose engine failed to start on this device", "이 기기에서 포즈 엔진을 시작하지 못했어요"],
249
- "rest-unavailable": ["The rig rest data (/ardy/cskel27-rest.json) could not be loaded", "리그 레스트 데이터(/ardy/cskel27-rest.json)를 불러오지 못했어요"],
250
- "no-person-found": ["No person was detected in the footage", "영상에서 사람을 감지하지 못했어요"],
251
- "no-person-in-photo": ["No person was detected in that photograph", "사진에서 사람을 감지하지 못했어요"],
252
- "image-load-timeout": ["The photograph never finished decoding", "사진 디코딩이 끝나지 않았어요"],
253
- "pose-partly-occluded": ["Shoulders and hips must both be visible in the photograph", "사진에 어깨와 골반이 모두 보여야 해요"],
254
- "no-usable-pose": ["A person was seen, but no stable pose could be fitted", "사람은 보였지만 안정적인 포즈를 만들지 못했어요"],
255
- "rig-not-loaded": ["Subject 1's rig is not loaded yet", "인물 1 리그가 아직 로드되지 않았어요"],
256
- "bridge-unreachable": ["The dev bridge did not answer", "개발 브리지가 응답하지 않아요"],
257
- "bridge-footage-incomplete": ["The bridge stream ended without footage", "브리지 전송이 영상 없이 끝났어요"],
258
- "footage-url-invalid": ["That address could not be used for a bridge download", "이 주소는 브리지 다운로드에 쓸 수 없어요"],
259
- "footage-probe-failed": ["The bridge could not read that page's video info", "브리지가 그 페이지의 영상 정보를 읽지 못했어요"],
260
- "footage-live-unsupported": ["Live streams have no fixed length and cannot be ingested", "라이브 스트림은 길이가 없어 인제스트할 수 없어요"],
261
- "footage-too-long": ["That video is over 15 minutes — trim or pick a shorter one", "15분이 넘는 영상이에요 — 잘라내거나 더 짧은 걸 골라 주세요"],
262
- "footage-download-failed": ["The bridge could not download that video", "브리지가 영상을 내려받지 못했어요"],
263
- "footage-normalize-failed": ["The bridge could not convert that video for extraction", "브리지가 영상을 추출용으로 변환하지 못했어요"],
264
- "footage-timeout": ["The bridge download took too long and was stopped", "브리지 다운로드가 너무 오래 걸려 중단됐어요"],
265
- "bridge-extract-incomplete": ["The bridge stream ended without a take", "브리지 전송이 테이크 없이 끝났어요"],
266
- "extract-host-missing": ["The bridge has no GPU box configured (CCLAY_EXTRACT_HOST)", "브리지에 GPU 박스가 설정돼 있지 않아요(CCLAY_EXTRACT_HOST)"],
267
- "extract-bridge-required": ["GVHMR extraction requires the local GPU bridge", "GVHMR 추출에는 로컬 GPU 브리지가 필요해요"],
268
- "extract-backend-unsupported": ["This bridge is not configured for GVHMR", "이 브리지가 GVHMR로 설정되지 않았어요"],
269
- "extract-upload-failed": ["The footage could not be copied to the GPU box", "영상을 GPU 박스로 복사하지 못했어요"],
270
- "extract-upload-too-large": ["That clip is too large to upload for extraction (300 MB cap)", "추출 업로드 한도(300MB)를 넘는 영상이에요"],
271
- "extract-upload-empty": ["No video bytes arrived at the bridge", "브리지에 영상 데이터가 도착하지 않았어요"],
272
- "extract-footage-unknown": ["The bridge no longer holds that download — re-ingest the URL", "브리지에 그 다운로드가 더 이상 없어요 — URL을 다시 넣어 주세요"],
273
- "extract-run-failed": ["GVHMR failed on the GPU box (see the bridge log)", "GPU 박스에서 GVHMR 실행이 실패했어요(브리지 로그 확인)"],
274
- "extract-no-person": ["The GPU box tracked no person in that footage", "GPU 박스가 영상에서 사람을 추적하지 못했어요"],
275
- "extract-convert-failed": ["The extracted take could not be converted for the timeline", "추출된 테이크를 타임라인용으로 변환하지 못했어요"],
276
- "extract-timeout": ["GPU extraction took too long and was stopped", "GPU 추출이 너무 오래 걸려 중단됐어요"],
277
- };
278
-
279
- // Extraction samples and bakes straight onto the production clock — an
280
- // extracted take never touches ARDY, so it never needs retiming either.
281
- export const MULTIMODEL_SAMPLE_FPS = TIMELINE_FPS;