cozyclay 2.1.1 → 3.0.0

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