cozyclay 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (583) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +22 -6
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -1,8 +1,7 @@
1
1
  import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
2
- import { frameFromClientX, groupKeyRuns, KEY_RUN_MIN, motionTrimRange, promptMoveStartFrame, rangePinBandGeometry, shotBlockGeometry } from "./timeline-coordinates.js";
3
- import { motionSegmentSpeedForFrames } from "./motion-edit.js";
2
+ import { frameFromClientX, groupKeyRuns, KEY_RUN_MIN, rangePinBandGeometry, shotBlockGeometry } from "./timeline-coordinates.js";
4
3
  import { createPlaybackClock } from "./playback-clock.js";
5
- import { promptResizeFrame } from "./timeline-resize.js";
4
+
6
5
  import { ko, isKo } from "../locale.js";
7
6
  import { isImeComposing } from "../ime.js";
8
7
  import { buildRail, craneHeightAt } from "../camera-follow.js";
@@ -10,7 +9,9 @@ import { insertPathMark, pathMarks, pathMetrics, pathProgressAt, MAX_PATH_LEAN }
10
9
  import { NumberField } from "../ui.jsx";
11
10
  import { flatTiming, timingIsFlat, envelopeDrag, insertCut, removeCut, CUT_MIN_GAP } from "../speed-envelope.js";
12
11
  import { checkShotAgainstPreset, presetById } from "../model-presets.js";
12
+ import { TakeSegmentClips, TakeSegmentTools } from "./take-segments.jsx"; import { BeatBoard } from "./beat-board.jsx";
13
13
  import "./timeline.css";
14
+ export { timelineTake } from "./take-segments.jsx";
14
15
 
15
16
  /**
16
17
  * ARDY Viser-style animation timeline — the live motion workspace.
@@ -43,13 +44,12 @@ const WHEEL_STEP_PX = 50;
43
44
  const MAX_WHEEL_STEPS = 3;
44
45
 
45
46
  const TRACKS = [
46
- "Prompts",
47
47
  "Full-Body",
48
48
  "2D Root",
49
49
  "Shots",
50
50
  ];
51
51
  const TRACK_LABELS_KO = {
52
- Prompts: ko("Prompt", "프롬프트"),
52
+
53
53
  "Full-Body": ko("Body", "바디"),
54
54
  "2D Root": ko("Root path", "루트 경로"),
55
55
  Shots: ko("Camera Cuts", "카메라 컷"),
@@ -60,7 +60,7 @@ const TRACK_LABELS_KO = {
60
60
  const IK_LANE = "Full-Body";
61
61
  const SHOTS_LANE = "Shots";
62
62
  // Ruler labels and lane gridlines share one 10-frame cadence, so authored
63
- // elements (40-frame prompt blocks, camera key dots) always land on visible
63
+ // elements (camera key dots, root pins) always land on visible
64
64
  // lines. Label density adapts to zoom in 10-based steps.
65
65
  const GRID_STEP_FRAMES = 10;
66
66
  const LABEL_STEPS = [10, 20, 50, 100, 200, 500, 1000];
@@ -899,7 +899,7 @@ function CameraBlockEditor({
899
899
  </label>
900
900
  <label title={ko("Cap dolly travel speed", "돌리의 최고 이동 속도를 제한합니다")}>
901
901
  <span>{ko("Speed", "속도")}</span>
902
- <input type="number" min="0.2" max="8" step="0.1" value={follow.maxDollySpeed} onChange={(event) => patchFollow({ maxDollySpeed: numberValue(event) })} />
902
+ <input type="number" min="0.2" max="40" step="0.1" value={follow.maxDollySpeed} onChange={(event) => patchFollow({ maxDollySpeed: numberValue(event) })} />
903
903
  <small>m/s</small>
904
904
  </label>
905
905
  {!(mode === "rail" && crane) && (
@@ -1005,30 +1005,25 @@ export default function Timeline({
1005
1005
  playing,
1006
1006
  // Which cast member's animation layer these tracks edit ("S2", …).
1007
1007
  trackOwner = null,
1008
- // Read-only previews of the OTHER cast members' layers: their prompt
1009
- // blocks and root pins render dimmed with an owner chip, so the whole
1010
- // cast's schedule is visible while only the active layer is editable.
1011
- ghostLayers = [], // [{ owner, promptClips: [], waypointFrames: [] }]
1008
+ // Read-only previews of the OTHER cast members' layers: their root pins
1009
+ // render dimmed with an owner chip, so the whole cast's schedule is
1010
+ // visible while only the active layer is editable.
1011
+ ghostLayers = [], // [{ owner, waypointFrames: [] }]
1012
1012
  waypointMode,
1013
1013
  // Which department the studio is in ("scene" | "camera" | "motion"). The
1014
- // motion-layer track tools — Prompts +, the Full-Body Cut, the trim and
1015
- // retime grips — only mean something while motion is the job, so they are
1016
- // not rendered anywhere else. The Root path (Waypoint) toggle stays in
1017
- // every mode: blocking uses it to stage where a subject walks.
1014
+ // camera-cut tools (Cut, Draw rail, + Track) belong to Scene and Camera.
1015
+ // The Root path (Waypoint) toggle stays in every mode: blocking uses it to
1016
+ // stage where a subject walks.
1018
1017
  workflowMode = "motion",
1019
- // A storyboard works in panels: it has no Prompts lane, chips or + Block.
1020
- previsMode = null,
1021
1018
  waypoints = [],
1022
1019
  pathSpeed = null, // { min, max, warn } in m/s, shown on the 2D Root label
1023
- badge,
1024
- promptClips = [],
1025
- selectedPromptId,
1020
+ badge,
1026
1021
  pendingWaypointFrame = null,
1027
1022
  ikMode = false,
1028
1023
  ikDisabled = false, // a loaded motion owns the rig
1029
1024
  // The ACTIVE cast member's loaded take, drawn as a passive strip on the
1030
1025
  // Full-Body lane. Frames are already on the timeline's 24 fps clock.
1031
- motion = null, // { frames, label } | null
1026
+ motion = null, // { frames, label, segments, cut, speed, remove, trim, reset } (take-segments.jsx timelineTake) | null
1032
1027
  ikFrames = [], // sorted full-body key frames
1033
1028
  rangePins = [], // active character's inclusive pin ranges
1034
1029
  previewRanges = [], // amber preview spans shown under the Body track
@@ -1052,12 +1047,7 @@ export default function Timeline({
1052
1047
  onMarkerSelect,
1053
1048
  onMarkerRemove,
1054
1049
  onRootKeyframeAdd,
1055
- onPromptAdd,
1056
- onPromptSelect,
1057
- onPromptChange,
1058
- onPromptResize,
1059
- onPromptMove,
1060
- onPromptRemove,
1050
+
1061
1051
  onClearMotion,
1062
1052
  onIkKeyframeAdd,
1063
1053
  onIkKeyframeRemove,
@@ -1102,11 +1092,7 @@ export default function Timeline({
1102
1092
  onShotCut,
1103
1093
  onShotSplit,
1104
1094
  onShotMove,
1105
- onMotionTrim,
1106
- onMotionTrimReset,
1107
- onMotionCut,
1108
- onMotionSpeedChange,
1109
- onMotionSegmentRemove,
1095
+
1110
1096
  // One undo entry per editing GESTURE. Fired once when a continuous drag
1111
1097
  // (or a keyboard nudge, or a text-editing session) begins, BEFORE the
1112
1098
  // first mutation lands, so App can snapshot the pre-gesture state exactly
@@ -1115,7 +1101,7 @@ export default function Timeline({
1115
1101
  }) {
1116
1102
  const [expanded, setExpanded] = useState(true);
1117
1103
  const [zoom, setZoom] = useState(ZOOM_DEFAULT);
1118
- const [movingPromptId, setMovingPromptId] = useState(null);
1104
+
1119
1105
  const [renamingShotId, setRenamingShotId] = useState(null);
1120
1106
  const [movingShotId, setMovingShotId] = useState(null);
1121
1107
  const [localCameraBlockIdx, setLocalCameraBlockIdx] = useState(null);
@@ -1132,7 +1118,7 @@ export default function Timeline({
1132
1118
  // The window key/interval handlers register once; the latest callbacks
1133
1119
  // are read through a ref so they never go stale mid-playback.
1134
1120
  const handlers = useRef({});
1135
- handlers.current = { onScrub, onAdvance, onStep, onPlayToggle, onWaypointToggle, onMarkerSelect, onMarkerRemove, onRootKeyframeAdd, onPromptAdd, onPromptSelect, onPromptChange, onPromptResize, onPromptMove, onPromptRemove, onIkKeyframeAdd, onIkKeyframeRemove, onPinSelect, onFootSnapToggle, onBodyContactToggle, onCameraMoveSelect, onCameraKeyframeAdd, onCameraKeyframeMove, onCameraKeyframeRemove, onCameraBlockSelect, onCameraBlockChange, onCameraPreview, onCameraRailDrawToggle, onCameraRailDelete, onObjectPathDrawToggle, onObjectPathChange, onObjectPathClear, onObjectTimingGestureStart, onObjectTimingGestureEnd, onObjectMarkAdd, onObjectMarkLean, onObjectMarkScrubStart, onObjectMarkScrubEnd, onShotSelect, onShotBoundaryMove, onShotRename, onShotRemove, onShotDuplicate, onShotCut, onShotSplit, onShotMove, onMotionTrim, onMotionTrimReset, onMotionCut, onMotionSpeedChange, onMotionSegmentRemove, onEditGestureStart };
1121
+ handlers.current = { onScrub, onAdvance, onStep, onPlayToggle, onWaypointToggle, onMarkerSelect, onMarkerRemove, onRootKeyframeAdd, onIkKeyframeAdd, onIkKeyframeRemove, onPinSelect, onFootSnapToggle, onBodyContactToggle, onCameraMoveSelect, onCameraKeyframeAdd, onCameraKeyframeMove, onCameraKeyframeRemove, onCameraBlockSelect, onCameraBlockChange, onCameraPreview, onCameraRailDrawToggle, onCameraRailDelete, onObjectPathDrawToggle, onObjectPathChange, onObjectPathClear, onObjectTimingGestureStart, onObjectTimingGestureEnd, onObjectMarkAdd, onObjectMarkLean, onObjectMarkScrubStart, onObjectMarkScrubEnd, onShotSelect, onShotBoundaryMove, onShotRename, onShotRemove, onShotDuplicate, onShotCut, onShotSplit, onShotMove, onEditGestureStart };
1136
1122
 
1137
1123
  // Trackpad/wheel zoom over the FRAME ruler lane only. React registers
1138
1124
  // onWheel as passive, so a synthetic onWheel could never preventDefault —
@@ -1284,187 +1270,13 @@ export default function Timeline({
1284
1270
  pin,
1285
1271
  geometry: rangePinBandGeometry(pin, frameCount, displayFrameCount),
1286
1272
  })).filter((entry) => entry.geometry), [displayFrameCount, frameCount, rangePins]);
1287
- const moveRef = useRef(null);
1288
- const suppressPromptClickRef = useRef(false);
1289
- const resizeRef = useRef(null);
1290
- const camDragRef = useRef(null);
1273
+ const camDragRef = useRef(null);
1291
1274
  const camSuppressClickRef = useRef(false);
1292
1275
  const shotBoundaryRef = useRef(null);
1293
1276
  const shotMoveRef = useRef(null);
1294
1277
  const shotSuppressClickRef = useRef(false);
1295
1278
 
1296
- function beginPromptMove(e, clip) {
1297
- if (e.button !== 0 || e.target.closest(".tl-chip-handle")) return;
1298
- const lane = e.currentTarget.closest(".tl-lane");
1299
- if (!lane) return;
1300
- const rect = lane.getBoundingClientRect();
1301
- moveRef.current = {
1302
- id: clip.id,
1303
- pointerId: e.pointerId,
1304
- startClientX: e.clientX,
1305
- startFrame: clip.startFrame,
1306
- laneWidth: rect.width,
1307
- displayFrameCount,
1308
- moving: false,
1309
- };
1310
- handlers.current.onEditGestureStart?.("prompt-move");
1311
- handlers.current.onPromptSelect?.(clip.id);
1312
- }
1313
-
1314
- function movePrompt(e) {
1315
- const active = moveRef.current;
1316
- if (!active || e.pointerId !== active.pointerId) return;
1317
- if (!active.moving) {
1318
- if (Math.abs(e.clientX - active.startClientX) < 4) return;
1319
- active.moving = true;
1320
- e.currentTarget.setPointerCapture(e.pointerId);
1321
- setMovingPromptId(active.id);
1322
- document.activeElement?.blur();
1323
- }
1324
- e.preventDefault();
1325
- e.stopPropagation();
1326
- const rawStart = promptMoveStartFrame(
1327
- active.startFrame,
1328
- active.startClientX,
1329
- e.clientX,
1330
- active.laneWidth,
1331
- active.displayFrameCount
1332
- );
1333
- handlers.current.onPromptMove?.(active.id, rawStart);
1334
- }
1335
-
1336
- function endPromptMove(e) {
1337
- const active = moveRef.current;
1338
- if (!active || e.pointerId !== active.pointerId) return;
1339
- if (active.moving) {
1340
- e.preventDefault();
1341
- e.stopPropagation();
1342
- suppressPromptClickRef.current = true;
1343
- if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId);
1344
- queueMicrotask(() => { suppressPromptClickRef.current = false; });
1345
- }
1346
- moveRef.current = null;
1347
- setMovingPromptId(null);
1348
- }
1349
-
1350
- function blockPromptClick(e) {
1351
- if (!suppressPromptClickRef.current) return;
1352
- e.preventDefault();
1353
- e.stopPropagation();
1354
- }
1355
-
1356
- function beginPromptResize(e, clip, edge) {
1357
- if (e.button !== 0) return;
1358
- e.preventDefault();
1359
- e.stopPropagation();
1360
- e.currentTarget.setPointerCapture?.(e.pointerId);
1361
- resizeRef.current = {
1362
- id: clip.id,
1363
- edge,
1364
- startClientX: e.clientX,
1365
- startFrame: edge === "start" ? clip.startFrame : clip.endFrame,
1366
- lastFrame: edge === "start" ? clip.startFrame : clip.endFrame,
1367
- };
1368
- handlers.current.onEditGestureStart?.("prompt-resize");
1369
- handlers.current.onPromptSelect?.(clip.id);
1370
- }
1371
-
1372
- function movePromptResize(e) {
1373
- const active = resizeRef.current;
1374
- if (!active) return;
1375
- const nextFrame = promptResizeFrame(active.startFrame, active.startClientX, e.clientX);
1376
- if (nextFrame === active.lastFrame) return;
1377
- active.lastFrame = nextFrame;
1378
- handlers.current.onPromptResize?.(active.id, active.edge, nextFrame);
1379
- }
1380
-
1381
- function endPromptResize(e) {
1382
- if (!resizeRef.current) return;
1383
- resizeRef.current = null;
1384
- e.currentTarget.releasePointerCapture?.(e.pointerId);
1385
- }
1386
-
1387
- // Motion trim: frame-accurate drag of the loaded take's in/out point on
1388
- // the Full-Body strip. No block snapping — a cut lands on the exact frame
1389
- // the hand releases. The preview lives here; the CUT itself is App's.
1390
- const motionTrimRef = useRef(null);
1391
- const [trimPreview, setTrimPreview] = useState(null);
1392
-
1393
- function beginMotionTrim(e, edge) {
1394
- if (e.button !== 0 || !motion) return;
1395
- e.preventDefault();
1396
- e.stopPropagation();
1397
- e.currentTarget.setPointerCapture?.(e.pointerId);
1398
- const rect = e.currentTarget.closest(".tl-lane")?.getBoundingClientRect();
1399
- if (!rect || rect.width < 2) return;
1400
- const max = Math.min(motion.frames, displayFrameCount) - 1;
1401
- // The preview always restarts from the FULL take: App composes the cut
1402
- // as trimOffset + start, so an end-only drag must still report start 0.
1403
- motionTrimRef.current = { edge, rect, max, displayFrameCount, preview: { start: 0, end: max } };
1404
- setTrimPreview({ start: 0, end: max });
1405
- }
1406
-
1407
- function moveMotionTrim(e) {
1408
- const active = motionTrimRef.current;
1409
- if (!active) return;
1410
- // Same pixel→frame transform as every other lane, off the pointer-down
1411
- // geometry, so the handle stays under the cursor at any zoom.
1412
- const frame = Math.min(active.max, frameFromClientX(e.clientX, active.rect.left, active.rect.width, active.displayFrameCount, frameCount));
1413
- const next = motionTrimRange(active.edge, frame, active.preview, active.max);
1414
- active.preview = next;
1415
- setTrimPreview(next);
1416
- }
1417
-
1418
- function endMotionTrim(e) {
1419
- const active = motionTrimRef.current;
1420
- motionTrimRef.current = null;
1421
- e.currentTarget.releasePointerCapture?.(e.pointerId);
1422
- setTrimPreview(null);
1423
- if (active && (active.preview.start > 0 || active.preview.end < active.max)) {
1424
- handlers.current.onMotionTrim?.(active.preview.start, active.preview.end);
1425
- }
1426
- }
1427
-
1428
- // Segment speed by stretch: dragging a segment's right-edge grip resizes it
1429
- // on the strip, and the width IS the playback rate — wider is slower. The
1430
- // preview lives here; the retime itself is App's, committed once on release.
1431
- const motionSpeedRef = useRef(null);
1432
- const [speedPreview, setSpeedPreview] = useState(null);
1433
-
1434
- function beginMotionSpeed(e, segment) {
1435
- if (e.button !== 0 || !motion) return;
1436
- e.preventDefault();
1437
- e.stopPropagation();
1438
- e.currentTarget.setPointerCapture?.(e.pointerId);
1439
- const rect = e.currentTarget.closest(".tl-lane")?.getBoundingClientRect();
1440
- if (!rect || rect.width < 2) return;
1441
- motionSpeedRef.current = { segment, rect, displayFrameCount, preview: null };
1442
- }
1443
-
1444
- function moveMotionSpeed(e) {
1445
- const active = motionSpeedRef.current;
1446
- if (!active) return;
1447
- const pointerFrame = frameFromClientX(e.clientX, active.rect.left, active.rect.width, active.displayFrameCount, frameCount);
1448
- const frames = Math.max(1, Math.round(pointerFrame) - active.segment.timelineStart + 1);
1449
- const speed = motionSegmentSpeedForFrames(active.segment, frames);
1450
- const sourceFrames = active.segment.sourceEnd - active.segment.sourceStart + 1;
1451
- const next = { id: active.segment.id, timelineFrames: Math.max(1, Math.round(sourceFrames / speed)), speed };
1452
- if (active.preview?.speed === next.speed && active.preview?.timelineFrames === next.timelineFrames) return;
1453
- active.preview = next;
1454
- setSpeedPreview(next);
1455
- }
1456
-
1457
- function endMotionSpeed(e) {
1458
- const active = motionSpeedRef.current;
1459
- motionSpeedRef.current = null;
1460
- e.currentTarget.releasePointerCapture?.(e.pointerId);
1461
- setSpeedPreview(null);
1462
- if (active?.preview && active.preview.speed !== active.segment.speed) {
1463
- handlers.current.onMotionSpeedChange?.(active.segment.id, active.preview.speed);
1464
- }
1465
- }
1466
-
1467
- // Camera key dots re-time by dragging, like prompt clips move: the frame
1279
+ // Camera key dots re-time by dragging: the frame
1468
1280
  // delta comes from the pointer-down geometry so a growing timeline cannot
1469
1281
  // feed back into the next pointermove.
1470
1282
  function beginCameraKeyDrag(e, key, shotIndex) {
@@ -1756,53 +1568,11 @@ export default function Timeline({
1756
1568
  onReset: () => commitDolly(flatTiming()),
1757
1569
  }
1758
1570
  : null;
1759
- const motionSegments = motion?.segments ?? [];
1760
- const selectedMotionSegment = motionSegments.find((segment) => frame >= segment.timelineStart && frame <= segment.timelineEnd) ?? motionSegments[0] ?? null;
1761
- const visibleMotionSegments = trimPreview
1762
- ? [{
1763
- id: "motion-trim-preview",
1764
- timelineStart: trimPreview.start,
1765
- timelineEnd: trimPreview.end,
1766
- speed: null,
1767
- preview: true,
1768
- }]
1769
- : motionSegments;
1770
- // While a speed grip is held, the dragged segment shows its would-be width
1771
- // and everything after it slides by the delta — the same reflow the commit
1772
- // will produce, so the release is never a surprise.
1773
- const displayMotionSegments = (() => {
1774
- if (!speedPreview || trimPreview) return visibleMotionSegments;
1775
- let shift = 0;
1776
- return visibleMotionSegments.map((segment) => {
1777
- if (segment.id === speedPreview.id) {
1778
- const shown = {
1779
- ...segment,
1780
- timelineStart: segment.timelineStart + shift,
1781
- timelineEnd: segment.timelineStart + shift + speedPreview.timelineFrames - 1,
1782
- previewSpeed: speedPreview.speed,
1783
- };
1784
- shift += speedPreview.timelineFrames - (segment.timelineEnd - segment.timelineStart + 1);
1785
- return shown;
1786
- }
1787
- return shift === 0 ? segment : { ...segment, timelineStart: segment.timelineStart + shift, timelineEnd: segment.timelineEnd + shift };
1788
- });
1789
- })();
1790
- // The motion-layer track tools (Prompts +, the Full-Body Cut, the trim and
1791
- // retime grips) edit the take, so Scene and Camera never render them.
1571
+ // Motion and Pose own the character layer; the camera-cut tools stay out of them.
1792
1572
  const motionTools = workflowMode === "motion";
1793
- const promptBlocksHidden = previsMode === "storyboard";
1794
- const tracks = promptBlocksHidden ? TRACKS.filter((name) => name !== "Prompts") : TRACKS;
1573
+ const tracks = TRACKS;
1795
1574
  const activeShotForCut = shots[activeShotIdx] ?? null;
1796
1575
  const canCutShot = Boolean(activeShotForCut && frame > activeShotForCut.startFrame && frame <= activeShotForCut.endFrame);
1797
- // A trim or retime grip edits ONE segment: it belongs to the segment the
1798
- // playhead selects. The trim preview clip carries the live gesture, so it
1799
- // keeps its grips while the pointer is down.
1800
- const segmentTools = (segment) => motionTools && (segment.preview === true || selectedMotionSegment?.id === segment.id);
1801
- const changeSelectedMotionSpeed = (speed) => {
1802
- if (!selectedMotionSegment || !Number.isFinite(speed)) return;
1803
- handlers.current.onMotionSpeedChange?.(selectedMotionSegment.id, Math.max(0.1, Math.min(4, speed)));
1804
- };
1805
-
1806
1576
  return (
1807
1577
  <section className={"timeline v2-sequencer" + (expanded ? "" : " collapsed") + (!shots.length ? " empty-shots" : "")} data-testid="sequencer" aria-label={ko("Animation timeline", "애니메이션 타임라인")}>
1808
1578
  {expanded ? (
@@ -1842,38 +1612,8 @@ export default function Timeline({
1842
1612
  </span>
1843
1613
  </div>
1844
1614
  <div className="tl-sequencer-actions" role="group" aria-label={ko("Sequencer actions", "시퀀서 작업")}>
1845
- {motionTools ? (<>
1846
- <button
1847
- type="button"
1848
- className="tl-seq-action cut"
1849
- data-testid="sequencer-cut"
1850
- disabled={!selectedMotionSegment || frame <= 0 || frame >= motion.frames}
1851
- title={ko("Cut the Body clip at the playhead", "재생 헤드에서 바디 클립 자르기")}
1852
- onClick={() => handlers.current.onMotionCut?.()}
1853
- >
1854
- {ko("Cut", "컷")}
1855
- </button>
1856
- {!promptBlocksHidden && <button
1857
- type="button"
1858
- className="tl-seq-action"
1859
- data-testid="sequencer-add-block"
1860
- title={ko("Add a prompt block at the playhead", "재생 헤드에 프롬프트 블록 추가")}
1861
- onClick={() => handlers.current.onPromptAdd?.(frame)}
1862
- >
1863
- {ko("+ Block", "+ 블록")}
1864
- </button>}
1865
- </>) : workflowMode === "pose" || ikMode ? (
1866
- <button
1867
- type="button"
1868
- className="tl-seq-action cut"
1869
- data-testid="sequencer-cut"
1870
- disabled={!selectedMotionSegment || frame <= 0 || frame >= (motion?.frames ?? 0)}
1871
- onClick={() => handlers.current.onMotionCut?.()}
1872
- >
1873
- {ko("Cut", "컷")}
1874
- </button>
1875
- ) : (<>
1876
- <button
1615
+ {motionTools || workflowMode === "pose" || ikMode ? null : (<>
1616
+ {shots.length > 0 && <button
1877
1617
  type="button"
1878
1618
  className="tl-seq-action cut"
1879
1619
  data-testid="sequencer-cut"
@@ -1882,8 +1622,8 @@ export default function Timeline({
1882
1622
  onClick={() => activeShotForCut && handlers.current.onShotSplit?.(activeShotForCut.id)}
1883
1623
  >
1884
1624
  {ko("Cut", "컷")}
1885
- </button>
1886
- <button
1625
+ </button>}
1626
+ {cameraBlockIdx != null && <button
1887
1627
  type="button"
1888
1628
  className="tl-seq-action rail"
1889
1629
  data-testid="sequencer-draw-rail"
@@ -1894,8 +1634,8 @@ export default function Timeline({
1894
1634
  onClick={() => handlers.current.onCameraRailDrawToggle?.()}
1895
1635
  >
1896
1636
  {ko("Draw rail", "레일 그리기")}
1897
- </button>
1898
- <button
1637
+ </button>}
1638
+ {!shotCutDisabled && <button
1899
1639
  type="button"
1900
1640
  className="tl-seq-action"
1901
1641
  data-testid="sequencer-add-track"
@@ -1904,7 +1644,7 @@ export default function Timeline({
1904
1644
  onClick={() => handlers.current.onShotCut?.()}
1905
1645
  >
1906
1646
  {ko("+ Track", "+ 트랙")}
1907
- </button>
1647
+ </button>}
1908
1648
  </>) }
1909
1649
  </div>
1910
1650
  <div className="tl-head-group tl-view-tools" role="group" aria-label={ko("Timeline view tools", "타임라인 보기 도구")}>
@@ -1961,43 +1701,18 @@ export default function Timeline({
1961
1701
  </>)}
1962
1702
  </>)}
1963
1703
  </div>
1964
- {(selectedMotionSegment || onClearMotion) && (
1704
+ {onClearMotion && (
1965
1705
  <div className="tl-head-group tl-motion-tools" role="group" aria-label={ko("Motion controls", "모션 컨트롤")}>
1966
- {selectedMotionSegment && (
1967
- <label className="tl-motion-speed-editor">
1968
- <span>{ko(`Segment ${motionSegments.indexOf(selectedMotionSegment) + 1} speed`, `구간 ${motionSegments.indexOf(selectedMotionSegment) + 1} 배율`)}</span>
1969
- <input
1970
- type="range"
1971
- min="0.1"
1972
- max="4"
1973
- step="0.1"
1974
- value={selectedMotionSegment.speed}
1975
- aria-label={ko("Selected Full-Body segment speed", "선택한 전신 구간 배율")}
1976
- onChange={(event) => changeSelectedMotionSpeed(event.currentTarget.valueAsNumber)}
1977
- />
1978
- <input
1979
- type="number"
1980
- min="0.1"
1981
- max="4"
1982
- step="0.1"
1983
- value={selectedMotionSegment.speed}
1984
- aria-label={ko("Selected Full-Body segment speed value", "선택한 전신 구간 배율 값")}
1985
- onChange={(event) => changeSelectedMotionSpeed(event.currentTarget.valueAsNumber)}
1986
- />
1987
- <small>×</small>
1988
- </label>
1989
- )}
1990
- {onClearMotion && (
1991
- <button
1992
- type="button"
1993
- className="tl-btn clear"
1994
- aria-label={ko("Clear loaded motion", "불러온 모션 지우기")}
1995
- title={ko("Clear motion and prompt blocks, then restore the blocking pose", "모션과 프롬프트 블록을 지우고 블로킹 포즈로 되돌리기")}
1996
- onClick={onClearMotion}
1997
- >
1998
- ✕ {ko("Clear motion", "모션 지우기")}
1999
- </button>
2000
- )}
1706
+ {motion?.segments && motionTools && <TakeSegmentTools motion={motion} frame={frame} />}
1707
+ <button
1708
+ type="button"
1709
+ className="tl-btn clear"
1710
+ aria-label={ko("Clear loaded motion", "불러온 모션 지우기")}
1711
+ title={ko("Clear the compiled take, then restore the blocking pose", "컴파일된 테이크를 지우고 블로킹 포즈로 되돌리기")}
1712
+ onClick={onClearMotion}
1713
+ >
1714
+ ✕ {ko("Clear motion", "모션 지우기")}
1715
+ </button>
2001
1716
  </div>
2002
1717
  )}
2003
1718
  {waypointMode && (
@@ -2109,7 +1824,7 @@ export default function Timeline({
2109
1824
  onMarkScrubEnd={(...args) => handlers.current.onObjectMarkScrubEnd?.(...args)}
2110
1825
  />
2111
1826
  ) : tracks.map((name) => (
2112
- <div className={"tl-track" + (name === "Prompts" ? " prompts" : "") + (name === IK_LANE ? " ik" : "") + (name === SHOTS_LANE ? " shots" : "")} data-track-id={name} key={name}>
1827
+ <div className={"tl-track" + (name === IK_LANE ? " ik" : "") + (name === SHOTS_LANE ? " shots" : "")} data-track-id={name} key={name}>
2113
1828
  <span className="tl-track-label tl-track-tree">
2114
1829
  {name === IK_LANE ? (
2115
1830
  <span className="tl-track-label-stack">
@@ -2124,9 +1839,9 @@ export default function Timeline({
2124
1839
  <span className="tl-track-subrow indent"><span className="tl-track-chip crane" data-lane-label="Crane">{ko("Crane", "크레인")}</span></span>
2125
1840
  </span>
2126
1841
  ) : (
2127
- <span className="tl-track-subrow"><span className={"tl-track-chip " + (name === "2D Root" ? "root" : "prompt")} data-lane-label={TRACK_LABELS_KO[name]}>{TRACK_LABELS_KO[name]}</span></span>
1842
+ <span className="tl-track-subrow"><span className="tl-track-chip root" data-lane-label={TRACK_LABELS_KO[name]}>{TRACK_LABELS_KO[name]}</span></span>
2128
1843
  )}
2129
- {trackOwner && (name === "Prompts" || name === "2D Root") && <em className="tl-track-owner">{trackOwner}</em>}
1844
+ {trackOwner && name === "2D Root" && <em className="tl-track-owner">{trackOwner}</em>}
2130
1845
  {name === "2D Root" && pathSpeed && (
2131
1846
  <em
2132
1847
  className={"tl-path-speed" + (pathSpeed.warn ? " warn" : "")}
@@ -2137,7 +1852,7 @@ export default function Timeline({
2137
1852
  : `${pathSpeed.min.toFixed(1)}–${pathSpeed.max.toFixed(1)} m/s`}
2138
1853
  </em>
2139
1854
  )}
2140
- {motionTools && name === "Prompts" && <button className="tl-track-add" type="button" title={ko("Add a 2–4 second prompt clip — one action per block", "2–4초 프롬프트 클립 추가 — 한 블록에 한 동작")} onClick={() => handlers.current.onPromptAdd?.(frame)}>+</button>}
1855
+
2141
1856
  {name === SHOTS_LANE && (
2142
1857
  <button
2143
1858
  type="button"
@@ -2159,20 +1874,7 @@ export default function Timeline({
2159
1874
  +
2160
1875
  </button>
2161
1876
  )}
2162
- {/* Cutting the take addresses the selected segment, so it is
2163
- offered only where that selection means something (R2). */}
2164
- {name === IK_LANE && motion && motionTools && selectedMotionSegment && (
2165
- <button
2166
- className="tl-track-add motion-cut"
2167
- type="button"
2168
- disabled={frame <= 0 || frame >= motion.frames}
2169
- title={ko("Cut the Full-Body clip at the playhead", "재생 헤드에서 전신 클립 컷")}
2170
- onClick={() => handlers.current.onMotionCut?.()}
2171
- >
2172
- {ko("Cut", "컷")}
2173
- </button>
2174
- )}
2175
- </span>
1877
+ </span>
2176
1878
  <div
2177
1879
  className={"tl-lane" + (name === "2D Root" ? " root" : "") + (name === SHOTS_LANE ? " shots" : "")}
2178
1880
  onPointerDown={
@@ -2373,26 +2075,6 @@ export default function Timeline({
2373
2075
  </div>
2374
2076
  );
2375
2077
  })}
2376
- {name === "Prompts" && ghostLayers.map((layer) => layer.promptClips.map((clip) => (
2377
- <div
2378
- key={`${layer.owner}:${clip.id}`}
2379
- className="tl-chip ghost"
2380
- style={{ "--tl-f-start": clipPct(clip.startFrame), "--tl-f-end": clipPct(clip.endFrame) }}
2381
- title={`${layer.owner} · ${clip.text}`}
2382
- >
2383
- <span className="tl-chip-ghost-label">{layer.owner} · {clip.text || "…"}</span>
2384
- </div>
2385
- )))}
2386
- {name === "Prompts" && promptClips.map((clip) => {
2387
- const duration = ((clip.endFrame - clip.startFrame) / Math.max(1, fps)).toFixed(1);
2388
- return (
2389
- <div key={clip.id} className={"tl-chip" + (selectedPromptId === clip.id ? " selected" : "") + (movingPromptId === clip.id ? " moving" : "")} style={{ "--tl-f-start": clipPct(clip.startFrame), "--tl-f-end": clipPct(clip.endFrame) }} title={ko("Drag to move · edge handles resize · right-click removes", "드래그로 이동 · 가장자리 핸들로 길이 조절 · 오른쪽 클릭으로 삭제")} onPointerDown={(e) => beginPromptMove(e, clip)} onPointerMove={movePrompt} onPointerUp={endPromptMove} onPointerCancel={endPromptMove} onClick={blockPromptClick} onContextMenu={(e) => { e.preventDefault(); handlers.current.onPromptRemove?.(clip.id); }}>
2390
- <button className="tl-chip-handle start" type="button" aria-label={ko("Resize prompt start", "프롬프트 시작점 조절")} onPointerDown={(e) => beginPromptResize(e, clip, "start")} onPointerMove={movePromptResize} onPointerUp={endPromptResize} onPointerCancel={endPromptResize} />
2391
- <input className="tl-chip-input" value={clip.text} placeholder={isKo ? `${duration}초 · 모션 프롬프트` : `${duration}s · motion prompt`} maxLength={500} onFocus={() => handlers.current.onEditGestureStart?.("prompt-text", clip.id)} onChange={(e) => handlers.current.onPromptChange?.(clip.id, e.target.value)} />
2392
- <button className="tl-chip-handle end" type="button" aria-label={ko("Resize prompt end", "프롬프트 끝점 조절")} onPointerDown={(e) => beginPromptResize(e, clip, "end")} onPointerMove={movePromptResize} onPointerUp={endPromptResize} onPointerCancel={endPromptResize} />
2393
- </div>
2394
- );
2395
- })}
2396
2078
  {name === IK_LANE && pinBands.map(({ pin, geometry }) => {
2397
2079
  return (
2398
2080
  <button
@@ -2428,60 +2110,7 @@ export default function Timeline({
2428
2110
  aria-label={ko(`Pending pin range ${geometry.startFrame}–${geometry.endFrame}`, `대기 중인 고정 범위 ${geometry.startFrame}–${geometry.endFrame}`)}
2429
2111
  />;
2430
2112
  })()}
2431
- {name === IK_LANE && displayMotionSegments.map((segment, index) => (
2432
- <div
2433
- key={segment.id}
2434
- className={"tl-motion-clip" + (trimPreview ? " trimming" : "") + (segment.previewSpeed !== undefined ? " retiming" : "") + (selectedMotionSegment?.id === segment.id ? " selected" : "")}
2435
- style={{
2436
- "--tl-f-start": clipPct(trimPreview ? trimPreview.start : Math.min(segment.timelineStart, displayFrameCount)),
2437
- "--tl-f-end": clipPct(trimPreview ? trimPreview.end + 1 : Math.min(segment.timelineEnd + 1, displayFrameCount)),
2438
- }}
2439
- title={segment.preview ? undefined : isKo
2440
- ? `전신 구간 ${index + 1} — ${segment.speed}×. 컷은 재생 헤드에서 · 오른쪽 그립을 끌어 배속 조절 · 우클릭으로 구간 삭제`
2441
- : `Full-Body segment ${index + 1} — ${segment.speed}×. Cut at the playhead; drag the right grip to retime; right-click to delete`}
2442
- onContextMenu={segment.preview ? undefined : (e) => {
2443
- e.preventDefault();
2444
- e.stopPropagation();
2445
- handlers.current.onMotionSegmentRemove?.(segment.id);
2446
- }}
2447
- >
2448
- {index === 0 && segmentTools(segment) && <button
2449
- className="tl-motion-clip-handle start"
2450
- type="button"
2451
- aria-label={ko("Trim take start", "테이크 시작점 자르기")}
2452
- onPointerDown={(e) => beginMotionTrim(e, "start")}
2453
- onPointerMove={moveMotionTrim}
2454
- onPointerUp={endMotionTrim}
2455
- onPointerCancel={endMotionTrim}
2456
- onContextMenu={(e) => { e.preventDefault(); handlers.current.onMotionTrimReset?.(); }}
2457
- />}
2458
- <span className="tl-motion-clip-label">
2459
- {segment.preview
2460
- ? `${trimPreview.start}–${trimPreview.end} (${trimPreview.end - trimPreview.start + 1}f)`
2461
- : `${index + 1} · ${segment.previewSpeed ?? segment.speed}×`}
2462
- </span>
2463
- {!segment.preview && segmentTools(segment) && <button
2464
- className="tl-motion-clip-handle speed"
2465
- type="button"
2466
- aria-label={ko("Retime segment by stretch", "드래그로 구간 배속 조절")}
2467
- title={ko("Drag — wider is slower, narrower is faster", "드래그 — 늘리면 느리게, 줄이면 빠르게")}
2468
- onPointerDown={(e) => beginMotionSpeed(e, segment)}
2469
- onPointerMove={moveMotionSpeed}
2470
- onPointerUp={endMotionSpeed}
2471
- onPointerCancel={endMotionSpeed}
2472
- />}
2473
- {index === displayMotionSegments.length - 1 && segmentTools(segment) && <button
2474
- className="tl-motion-clip-handle end"
2475
- type="button"
2476
- aria-label={ko("Trim take end", "테이크 끝점 자르기")}
2477
- onPointerDown={(e) => beginMotionTrim(e, "end")}
2478
- onPointerMove={moveMotionTrim}
2479
- onPointerUp={endMotionTrim}
2480
- onPointerCancel={endMotionTrim}
2481
- onContextMenu={(e) => { e.preventDefault(); handlers.current.onMotionTrimReset?.(); }}
2482
- />}
2483
- </div>
2484
- ))}
2113
+ {name === IK_LANE && motion?.segments && <TakeSegmentClips motion={motion} frame={frame} frameCount={frameCount} displayFrameCount={displayFrameCount} clipPct={clipPct} tools={motionTools} />}{name === IK_LANE && <BeatBoard motion={motion} frame={frame} displayFrameCount={displayFrameCount} clipPct={clipPct} onJump={(f) => handlers.current.onScrub?.(f)} />}
2485
2114
 
2486
2115
  {name === IK_LANE &&
2487
2116
  ikRuns.flatMap((run) => (run.length < KEY_RUN_MIN