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.
- package/CHANGELOG.md +60 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +40 -113
- package/bin/agent/agent-runner.mjs +9 -10
- package/bin/agent/cliproxy-provider.mjs +135 -0
- package/bin/agent/credential-store.mjs +30 -7
- package/bin/agent/pi-tools.mjs +4 -1
- package/bin/agent/provider-keys.mjs +32 -0
- package/bin/agent/provider-login.mjs +110 -0
- package/bin/agent/provider-routes.mjs +99 -0
- package/bin/agent/providers.mjs +19 -97
- package/bin/agent/script-tool.mjs +17 -4
- package/bin/agent/studio-motion-reference.mjs +23 -0
- package/bin/agent/studio-prompt.mjs +1 -1
- package/bin/agent/studio-rebase-text.mjs +32 -0
- package/bin/agent/studio-rebase.mjs +26 -39
- package/bin/agent/studio-tools.mjs +9 -28
- package/bin/agent/tool-arguments.mjs +49 -0
- package/bin/agent/turn-frames.mjs +66 -0
- package/bin/codex-auth.mjs +9 -2
- package/bin/cozyclay.mjs +10 -116
- package/bin/live/cli.mjs +3 -3
- package/bin/mcp-runtime.mjs +2 -12
- package/bin/package-signature.mjs +0 -4
- package/dist/app/index.html +7 -6
- package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
- package/dist/assets/analytics-C9sUnndd.js +1 -0
- package/dist/assets/app-CCh--1Hc.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.js → landing-DjHD8u7k.js} +1 -1
- package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-C4yKH1p4.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +3 -3
- package/dist/sw.js +3 -6
- package/dist/workflow/index.html +5 -4
- package/mcp/LIVE-PROTOCOL.md +7 -47
- package/mcp/README.md +16 -26
- package/mcp/live-hub.mjs +6 -10
- package/mcp/package.json +1 -3
- package/mcp/tool-handlers.mjs +4 -168
- package/package.json +3 -22
- package/src/App.jsx +150 -2624
- package/src/agent-progress.js +79 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +5 -0
- package/src/app-stage.jsx +2 -34
- package/src/asset-pane.jsx +6 -5
- package/src/camera-block.js +21 -0
- package/src/camera-direction.js +120 -0
- package/src/character-kind.js +3 -9
- package/src/command-bus.js +8 -6
- package/src/commands/cast.js +5 -34
- package/src/commands/elements/character.js +1 -1
- package/src/commands/elements/motion.js +1 -6
- package/src/commands/elements/object.js +6 -2
- package/src/commands/elements/shot.js +4 -1
- package/src/commands/elements.js +38 -14
- package/src/commands/index.js +3 -1
- package/src/commands/kits.js +37 -0
- package/src/commands/motion-timing.js +57 -0
- package/src/commands/motion.js +9 -152
- package/src/commands/motion3-compile.js +65 -0
- package/src/commands/motion3.js +207 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +49 -11
- package/src/commands/stage.js +14 -1
- package/src/commands/view.js +4 -1
- package/src/document-mirrors.js +42 -0
- package/src/document-store.js +4 -0
- package/src/domains/cast.js +19 -194
- package/src/domains/motion/motion-range-pin-rules.js +1 -1
- package/src/domains/motion/take-source.js +51 -0
- package/src/domains/motion.js +60 -1838
- package/src/domains/scenes.js +12 -55
- package/src/domains/shots.js +2 -2
- package/src/domains/stage.js +4 -2
- package/src/draft-ghost.js +46 -0
- package/src/draft-ghost.jsx +129 -0
- package/src/dualview.jsx +4 -3
- package/src/effects/EffectsLayer.jsx +112 -0
- package/src/effects/effect-meshes.js +232 -0
- package/src/effects/effect-record.js +103 -0
- package/src/effects/emitter.js +59 -0
- package/src/effects/simulate.js +261 -0
- package/src/effects/state.js +59 -0
- package/src/execution-telemetry.js +2 -3
- package/src/first-success-guide.jsx +5 -5
- package/src/ghost-set.js +257 -0
- package/src/held-object-edit.jsx +113 -0
- package/src/held-object-pose.js +175 -0
- package/src/hierarchy-model.js +1 -1
- package/src/hierarchy-panel.jsx +1 -1
- package/src/imported-mesh.jsx +103 -0
- package/src/kits/held.js +84 -0
- package/src/kits/index.js +70 -0
- package/src/kits/smoking.js +57 -0
- package/src/light-blockers.js +93 -0
- package/src/motion/beat-board-model.js +84 -0
- package/src/motion/beat-board.css +124 -0
- package/src/motion/beat-board.jsx +59 -0
- package/src/motion/center-of-mass.js +66 -0
- package/src/motion/fix-collisions.js +850 -0
- package/src/motion/physics-review.js +118 -0
- package/src/{ardy → motion}/physics-support.js +1 -76
- package/src/motion/rig-snapshot.js +22 -0
- package/src/motion/take-segments.jsx +140 -0
- package/src/{ardy → motion}/timeline-coordinates.js +0 -21
- package/src/{ardy → motion}/timeline.css +1 -2
- package/src/{ardy → motion}/timeline.jsx +42 -413
- package/src/motion/waypoints.js +76 -0
- package/src/motion3/beats.js +83 -0
- package/src/motion3/body.js +158 -0
- package/src/motion3/bones.js +47 -0
- package/src/motion3/carried.js +47 -0
- package/src/motion3/compile-job.js +75 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +105 -0
- package/src/motion3/compiler/arm-orient.js +128 -0
- package/src/motion3/compiler/channels.js +125 -0
- package/src/motion3/compiler/compile.js +205 -0
- package/src/motion3/compiler/gait-rhythm.js +97 -0
- package/src/motion3/compiler/grip.js +171 -0
- package/src/motion3/compiler/head-steady.js +48 -0
- package/src/motion3/compiler/hold.js +134 -0
- package/src/motion3/compiler/idle.js +113 -0
- package/src/motion3/compiler/limbs.js +170 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +162 -0
- package/src/motion3/compiler/quat.js +49 -0
- package/src/motion3/compiler/skeleton.js +74 -0
- package/src/motion3/compiler/validate-common.js +32 -0
- package/src/motion3/compiler/validate-goals.js +118 -0
- package/src/motion3/compiler/validate-objects.js +143 -0
- package/src/motion3/compiler/validate-parts.js +116 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +73 -0
- package/src/motion3/compiler/walk-pelvis.js +275 -0
- package/src/motion3/compiler/walk.js +207 -0
- package/src/motion3/compiler/wrist-clamp.js +32 -0
- package/src/motion3/delta.js +47 -0
- package/src/motion3/describe.js +174 -0
- package/src/motion3/docs.js +27 -0
- package/src/motion3/edit-carry.js +21 -0
- package/src/motion3/fix/ease.js +16 -0
- package/src/motion3/fix/ground.js +37 -0
- package/src/motion3/fix/index.js +52 -0
- package/src/motion3/fix/push.js +71 -0
- package/src/motion3/fix/reach.js +46 -0
- package/src/motion3/fix/retime.js +79 -0
- package/src/motion3/fix/self.js +211 -0
- package/src/motion3/fix/slide.js +70 -0
- package/src/motion3/fix/turn.js +51 -0
- package/src/motion3/follow.js +39 -0
- package/src/motion3/free-motion.js +173 -0
- package/src/motion3/grip-choice.js +130 -0
- package/src/motion3/hold-plan.js +157 -0
- package/src/motion3/json-patch.js +77 -0
- package/src/motion3/key-aim.js +105 -0
- package/src/motion3/key-edit.js +82 -0
- package/src/motion3/lenient.js +127 -0
- package/src/motion3/object-keys.js +236 -0
- package/src/motion3/object-pose.js +182 -0
- package/src/motion3/object-tracks.js +12 -0
- package/src/motion3/pipeline.js +102 -0
- package/src/motion3/playback.js +51 -0
- package/src/motion3/probes.js +91 -0
- package/src/motion3/qa/body-checks.js +34 -0
- package/src/motion3/qa/check-names.js +14 -0
- package/src/motion3/qa/checks.js +121 -0
- package/src/motion3/qa/contact-checks.js +51 -0
- package/src/motion3/qa/explain.js +80 -0
- package/src/motion3/qa/fmt.js +5 -0
- package/src/motion3/qa/foot-checks.js +72 -0
- package/src/motion3/qa/geometry.js +77 -0
- package/src/motion3/qa/goals.js +191 -0
- package/src/motion3/qa/ground.js +60 -0
- package/src/motion3/qa/hints-contact.js +179 -0
- package/src/motion3/qa/hints-goal.js +160 -0
- package/src/motion3/qa/hints-knee.js +24 -0
- package/src/motion3/qa/hints-motion.js +209 -0
- package/src/motion3/qa/hints-object.js +91 -0
- package/src/motion3/qa/index.js +42 -0
- package/src/motion3/qa/key-checks.js +82 -0
- package/src/motion3/qa/near.js +96 -0
- package/src/motion3/qa/object-checks.js +135 -0
- package/src/motion3/qa/patch.js +161 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +218 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +120 -0
- package/src/motion3/repair-tune.js +121 -0
- package/src/motion3/repair.js +83 -0
- package/src/motion3/report.js +67 -0
- package/src/motion3/rigs/index.js +14 -0
- package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/x-bot-tpose.json +1 -0
- package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/y-bot-tpose.json +1 -0
- package/src/motion3/scene.js +60 -0
- package/src/motion3/schema/brief.js +89 -0
- package/src/motion3/schema/fields.js +44 -0
- package/src/motion3/schema/spec-examples.js +46 -0
- package/src/motion3/schema/topics.js +111 -0
- package/src/motion3/schema/types.js +147 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/skills/basic.js +82 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/index.js +83 -0
- package/src/motion3/skills/info.js +65 -0
- package/src/motion3/skills/smoke-kit.js +109 -0
- package/src/motion3/skills/smoke.js +160 -0
- package/src/motion3/spec-store.js +59 -0
- package/src/motion3/take-edit.js +155 -0
- package/src/motion3/take.js +63 -0
- package/src/motion3/timeline.js +127 -0
- package/src/motion3/views.js +121 -0
- package/src/motion3/walk-plan.js +119 -0
- package/src/object-catalog.jsx +7 -1
- package/src/object-colors.js +98 -0
- package/src/object-gizmo-parts.js +115 -0
- package/src/object-gizmo.jsx +37 -122
- package/src/object-glow.js +139 -0
- package/src/object-hidden.js +33 -0
- package/src/object-look.js +196 -0
- package/src/object-size.js +10 -0
- package/src/object-travel.js +71 -6
- package/src/panels/CameraPanel.jsx +7 -7
- package/src/panels/HdrEnvironmentControls.jsx +59 -0
- package/src/panels/LightPanel.jsx +25 -4
- package/src/panels/ObjectLookPanel.jsx +88 -0
- package/src/panels/PosePanel.jsx +2 -17
- package/src/panels/RigControlPanel.jsx +5 -53
- package/src/panels/SubjectBox.jsx +4 -1
- package/src/panels/details.css +14 -0
- package/src/posestudio.jsx +4 -27
- package/src/project-browser.jsx +3 -4
- package/src/project-resources.js +18 -31
- package/src/project.js +13 -104
- package/src/props.jsx +25 -280
- package/src/rail-dot-drag.js +23 -0
- package/src/render-passes.js +10 -0
- package/src/resource-status.jsx +1 -0
- package/src/result-modal.jsx +4 -19
- package/src/room.jsx +20 -12
- package/src/scene-assets.js +8 -6
- package/src/scene-light-place.js +72 -0
- package/src/scene-lights.jsx +148 -0
- package/src/scene-objects.js +31 -139
- package/src/scenes.js +15 -105
- package/src/semantic-edit.js +2 -5
- package/src/set-pieces.jsx +183 -0
- package/src/settings-menu.jsx +3 -11
- package/src/shell/BottomDock.jsx +9 -51
- package/src/shell/DetailsSlot.jsx +16 -200
- package/src/shell/GhostsMenuGroup.jsx +74 -0
- package/src/shell/LibrarySlot.jsx +1 -3
- package/src/shell/MenuBar.jsx +3 -3
- package/src/shell/PreferencesDialog.jsx +15 -264
- package/src/shell/PreferencesSlot.jsx +2 -8
- package/src/shell/TopBar.jsx +5 -84
- package/src/shell/ViewportToolbar.jsx +10 -70
- package/src/shell/agent-glass.css +19 -6
- package/src/shell/details-selection.js +42 -0
- package/src/shell/ghosts-menu.css +22 -0
- package/src/shell/glass-regions.css +29 -3
- package/src/shell/preferences.css +6 -1
- package/src/shot-authoring.js +4 -7
- package/src/soft-shadows.js +37 -0
- package/src/stage-attach-frames.js +12 -12
- package/src/stage-canvas-rigs.jsx +3 -3
- package/src/stage-capture.jsx +42 -8
- package/src/stage-characters.jsx +22 -216
- package/src/stage-editor-gizmos.jsx +6 -6
- package/src/stage-environment.js +93 -0
- package/src/stage-hdr-builtins.js +107 -0
- package/src/stage-hdr-image.js +116 -0
- package/src/stage-hdr-library.js +70 -0
- package/src/stage-hdr-map.js +94 -0
- package/src/stage-hdr.js +130 -0
- package/src/stage-lighting.js +149 -0
- package/src/stage-occlusion.js +210 -0
- package/src/stage-sky.jsx +159 -0
- package/src/stage-startup-defaults.js +5 -9
- package/src/studio-actions.js +12 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +41 -71
- package/src/studio-app-binding.js +25 -45
- package/src/studio-asset-list.js +17 -0
- package/src/studio-contact-sheet.js +37 -5
- package/src/studio-elements.js +27 -7
- package/src/studio-entity-bounds.js +18 -0
- package/src/studio-error-codes.js +8 -0
- package/src/studio-export-framing.js +152 -0
- package/src/studio-lighting-check.js +103 -0
- package/src/studio-motion-visual.js +150 -0
- package/src/studio-schema-errors.js +80 -0
- package/src/studio-support-gap.js +74 -0
- package/src/styles.css +1 -19
- package/src/timeline-defaults.js +5 -0
- package/src/timeline-extent.js +8 -20
- package/src/tutorial-walk.js +17 -0
- package/src/ui-scrub.js +1 -1
- package/src/use-shot-camera-tracks.js +12 -0
- package/src/view-ghosts.js +97 -0
- package/src/view-ghosts.jsx +49 -0
- package/src/workflow/AgentPanel.jsx +47 -166
- package/src/workflow/AgentProgressCard.jsx +107 -0
- package/src/workflow/AgentReplyText.jsx +37 -0
- package/src/workflow/AgentTurnSteps.jsx +84 -0
- package/src/workflow/LoginPanel.jsx +140 -0
- package/src/workflow/WorkflowBuilder.jsx +7 -23
- package/src/workflow/agent-client.js +54 -51
- package/src/workflow/agent-panel.css +71 -1
- package/src/workflow/agent-progress-card.css +167 -0
- package/src/workflow/agent-progress-view.js +47 -0
- package/src/workflow/agent-turn-steps.css +113 -0
- package/src/workflow/agent-turn.js +191 -0
- package/src/workflow/login-client.js +34 -0
- package/src/workflow/motion-input.js +18 -31
- package/src/workflow/node-schema.js +1 -1
- package/src/workflow/reply-markdown.js +69 -0
- package/src/workflow/scene-asset-sync.js +8 -8
- package/tools/agent-session-stats.mjs +61 -0
- package/tools/dev-full.mjs +5 -56
- package/tools/eslint-baseline.json +12 -13
- package/tools/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +31 -81
- package/tools/motion3/extract-body.mjs +103 -0
- package/tools/motion3/extract-rig.mjs +85 -0
- package/tools/process-supervisor.mjs +0 -88
- package/tools/run-tests.mjs +8 -124
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-C9pMpW7s.js +0 -1
- package/dist/assets/app-B_2XB9rw.css +0 -1
- package/dist/assets/app-DAuvKPP8.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-CvKuBgy5.js +0 -1
- package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-BPk2Ni95.js +0 -1
- package/dist/assets/workflow-jJRBR94Z.js +0 -179
- package/dist/demo/walk-then-stop.npz +0 -0
- package/mcp/ardy-prompts.mjs +0 -218
- package/src/ardy/auto-fix-panel.css +0 -108
- package/src/ardy/auto-physics.js +0 -1194
- package/src/ardy/client.js +0 -153
- package/src/ardy/convert.js +0 -313
- package/src/ardy/cskel27-neutral.js +0 -39
- package/src/ardy/cskel27.js +0 -77
- package/src/ardy/export.js +0 -165
- package/src/ardy/fix-collisions.js +0 -2257
- package/src/ardy/foot-lock.js +0 -230
- package/src/ardy/motion-calibration.js +0 -118
- package/src/ardy/motion-edit.js +0 -213
- package/src/ardy/motion-url.js +0 -55
- package/src/ardy/mp4-duration.js +0 -102
- package/src/ardy/npz.js +0 -595
- package/src/ardy/physics-panel.css +0 -21
- package/src/ardy/physics-panel.jsx +0 -89
- package/src/ardy/physics-refine.js +0 -111
- package/src/ardy/physics-review.js +0 -567
- package/src/ardy/physics-temporal.js +0 -54
- package/src/ardy/platform-fit-panel.css +0 -64
- package/src/ardy/platform-fit-panel.jsx +0 -41
- package/src/ardy/platform-fit.js +0 -326
- package/src/ardy/playback.js +0 -711
- package/src/ardy/pose-pin.js +0 -97
- package/src/ardy/prompt-clips.js +0 -30
- package/src/ardy/retime.js +0 -211
- package/src/ardy/root-drop.js +0 -369
- package/src/ardy/timeline-resize.js +0 -13
- package/src/ardy/to-cskel27.js +0 -225
- package/src/ardy/trim.js +0 -33
- package/src/ardy/waypoints.js +0 -369
- package/src/ardy-bridge-contract.js +0 -281
- package/src/domains/motion/motion-video-capture.js +0 -664
- package/src/i2v-motion-client.js +0 -77
- package/src/i2v-motion-studio.jsx +0 -180
- package/src/line-edit.js +0 -1348
- package/src/motion/generation.js +0 -105
- package/src/motion-readiness-ui.jsx +0 -67
- package/src/motion-readiness.js +0 -40
- package/src/motion-resources.js +0 -177
- package/src/motion-store.js +0 -65
- package/src/motion-trail.js +0 -601
- package/src/multimodel-ingest.js +0 -394
- package/src/panels/PromptBlocksPanel.jsx +0 -444
- package/src/panels/VideoCapturePanel.jsx +0 -181
- package/src/pose-extract/browser.js +0 -42
- package/src/pose-extract/detector.js +0 -100
- package/src/pose-extract/fit.js +0 -227
- package/src/pose-extract/image-frame.js +0 -51
- package/src/pose-extract/index.js +0 -58
- package/src/pose-extract/keyframes.js +0 -74
- package/src/pose-extract/landmarks.js +0 -162
- package/src/pose-extract/mirror.js +0 -153
- package/src/pose-extract/take.js +0 -131
- package/src/pose-extract/video-frames.js +0 -91
- package/src/take-recipe.js +0 -165
- package/src/trail-key-conflicts.js +0 -39
- package/src/trail-pick.js +0 -59
- package/src/use-i2v-capture-modal.js +0 -15
- package/tools/ardy/BRIDGE.md +0 -37
- package/tools/ardy/GVHMR-NPZ.md +0 -68
- package/tools/ardy/README.md +0 -124
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/ardy/artifacts.mjs +0 -34
- package/tools/ardy/bridge.mjs +0 -1732
- package/tools/ardy/bvh-cskel27.mjs +0 -1265
- package/tools/ardy/cclay_constrained_generate.py +0 -561
- package/tools/ardy/cclay_gvhmr_worker.py +0 -264
- package/tools/ardy/cclay_motion_edit.py +0 -510
- package/tools/ardy/cclay_sequence_generate.py +0 -705
- package/tools/ardy/cclay_worker.py +0 -304
- package/tools/ardy/dump-npz.py +0 -205
- package/tools/ardy/extract-rest.mjs +0 -299
- package/tools/ardy/extract.mjs +0 -507
- package/tools/ardy/footage.mjs +0 -467
- package/tools/ardy/gvhmr-floor.mjs +0 -102
- package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
- package/tools/ardy/gvhmr_fastpath.py +0 -168
- package/tools/ardy/gvhmr_trajectory.py +0 -240
- package/tools/ardy/merge-text-encoder.py +0 -160
- package/tools/ardy/mocap-metrics.mjs +0 -200
- package/tools/ardy/mocap-quality-gate.mjs +0 -110
- package/tools/ardy/motion-stabilize.mjs +0 -348
- package/tools/ardy/npz.mjs +0 -414
- package/tools/ardy/pose-to-npz.mjs +0 -106
- package/tools/ardy/prompt-limits.mjs +0 -5
- package/tools/ardy/run-edit-on-box.sh +0 -73
- package/tools/ardy/run-local.mjs +0 -233
- package/tools/ardy/run-on-box.sh +0 -593
- package/tools/ardy/run-sequence-on-box.sh +0 -177
- package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
- package/tools/ardy/runners/index.mjs +0 -18
- package/tools/ardy/runners/local.mjs +0 -455
- package/tools/ardy/runners/proc.mjs +0 -125
- package/tools/ardy/runners/remote.mjs +0 -252
- package/tools/ardy/setup-local.mjs +0 -189
- package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
- package/tools/ardy/setup-text-encoder.py +0 -306
- package/tools/ardy/smpl-cskel27.mjs +0 -178
- package/tools/ardy/visual-qa.mjs +0 -196
- package/tools/ardy/vq-car.mjs +0 -52
- package/tools/bench/EXP3.md +0 -61
- package/tools/bench/cclay_bench_extract_incam.py +0 -125
- package/tools/bench/cclay_bench_extract_obs.py +0 -204
- package/tools/bench/cclay_bench_runner.py +0 -42
- package/tools/bench/cube-contact.mjs +0 -162
- package/tools/bench/exp3.mjs +0 -152
- package/tools/bench/extract-bench-lib.mjs +0 -216
- package/tools/bench/extract-bench.mjs +0 -302
- package/tools/bench/fal-generate.mjs +0 -88
- package/tools/bench/fit/README.md +0 -189
- package/tools/bench/fit/camera.mjs +0 -59
- package/tools/bench/fit/contact.mjs +0 -419
- package/tools/bench/fit/footlock.mjs +0 -234
- package/tools/bench/fit/motion.mjs +0 -85
- package/tools/bench/fit/pin.mjs +0 -27
- package/tools/bench/fit/remote.mjs +0 -105
- package/tools/bench/fit-bench.mjs +0 -126
- package/tools/bench/fit-sanity.mjs +0 -45
- package/tools/bench/metrics.mjs +0 -313
- package/tools/bench/obs/depth.mjs +0 -258
- package/tools/bench/obs/extrinsics.mjs +0 -175
- package/tools/bench/obs/fit_mannequin_betas.py +0 -340
- package/tools/bench/obs/ground.mjs +0 -294
- package/tools/bench/obs/heading.mjs +0 -151
- package/tools/bench/obs/ladder.mjs +0 -555
- package/tools/bench/obs/mannequin-betas.json +0 -124
- package/tools/bench/obs/remote.mjs +0 -83
- package/tools/bench/obs/rest_joints.py +0 -52
- package/tools/bench/obs/ybot-targets.mjs +0 -49
- package/tools/bench/obs-bench.mjs +0 -520
- package/tools/bench/score.mjs +0 -343
- package/tools/bench/summarize.mjs +0 -86
- package/tools/demo-worker/README.md +0 -137
- package/tools/demo-worker/api-client.mjs +0 -233
- package/tools/demo-worker/generate.mjs +0 -301
- package/tools/demo-worker/index.mjs +0 -466
- package/tools/demo-worker/sign-core.mjs +0 -112
- package/tools/demo-worker/sign.mjs +0 -161
- package/tools/gt-render/browser.mjs +0 -182
- package/tools/gt-render/camera-math.mjs +0 -276
- package/tools/gt-render/page.mjs +0 -267
- package/tools/gt-render/render.mjs +0 -514
- package/tools/gt-render/scene-box.mjs +0 -31
- package/tools/gt-render/take-transform.mjs +0 -87
- package/tools/kimodo/compare-seam.mjs +0 -108
- package/tools/kimodo/constraints.mjs +0 -244
- package/tools/kimodo/edit.mjs +0 -259
- package/tools/kimodo/effector-constraints.mjs +0 -200
- package/tools/kimodo/generate.mjs +0 -646
- package/tools/kimodo/local-output.mjs +0 -265
- package/tools/kimodo/measure-pose.mjs +0 -65
- package/tools/kimodo/measure-preserve.mjs +0 -899
- package/tools/kimodo/measure-waypoints.mjs +0 -55
- package/tools/kimodo/mlx-generate.py +0 -111
- package/tools/kimodo/pose-constraints.mjs +0 -248
- package/tools/kimodo/preserve-mask.mjs +0 -543
- package/tools/kimodo/read-npz.mjs +0 -189
- package/tools/kimodo/run-edit-on-box.mjs +0 -155
- package/tools/kimodo/run-sequence-on-box.mjs +0 -178
- package/tools/kimodo/runner.mjs +0 -284
- package/tools/kimodo/seam-batch.mjs +0 -77
- package/tools/kimodo/setup-local.mjs +0 -30
- package/tools/kimodo/setup-on-box.sh +0 -339
- package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
- package/tools/morphgs/assets/gen_truth.py +0 -39
- package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
- package/tools/morphgs/assets/playback-check.mjs +0 -30
- package/tools/morphgs/demo-gate.sh +0 -26
- package/tools/morphgs/fbx2morphgs.mjs +0 -68
- package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
- package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
- package/tools/morphgs/setup-on-cluster.sh +0 -126
- package/tools/projflow/driver.py +0 -1069
- package/tools/projflow/generate.mjs +0 -805
- package/tools/projflow/hml22-to-cskel27.mjs +0 -735
- package/tools/projflow/line-edit-job.mjs +0 -547
- package/tools/projflow/replay.mjs +0 -452
- package/tools/projflow/runner.mjs +0 -269
- package/tools/projflow/service.mjs +0 -742
- package/tools/track/DESIGN.md +0 -513
- package/tools/track/backfill-provenance.mjs +0 -116
- package/tools/track/budget.mjs +0 -8
- package/tools/track/check-rig.mjs +0 -209
- package/tools/track/diagnostics.schema.json +0 -60
- package/tools/track/export-rig.mjs +0 -256
- package/tools/track/fallback.mjs +0 -7
- package/tools/track/fk-parity-fixture.mjs +0 -161
- package/tools/track/gate.mjs +0 -349
- package/tools/track/masks.mjs +0 -318
- package/tools/track/metrics.mjs +0 -201
- package/tools/track/publish-obs.mjs +0 -108
- package/tools/track/py/check_env.py +0 -61
- package/tools/track/py/eval_lr.py +0 -229
- package/tools/track/py/lr_viterbi.py +0 -294
- package/tools/track/py/masks.py +0 -405
- package/tools/track/py/objective.py +0 -382
- package/tools/track/py/rig.py +0 -508
- package/tools/track/py/scene.py +0 -326
- package/tools/track/py/test_joint_indices.py +0 -123
- package/tools/track/py/test_lr_viterbi.py +0 -195
- package/tools/track/py/test_masks.py +0 -229
- package/tools/track/py/test_rig.py +0 -288
- package/tools/track/py/test_scene.py +0 -354
- package/tools/track/py/test_track.py +0 -328
- package/tools/track/py/track.py +0 -411
- package/tools/track/remote.mjs +0 -140
- package/tools/track/rig-dump.mjs +0 -227
- package/tools/track/run-box-tests.mjs +0 -14
- package/tools/track/run-box.mjs +0 -168
- package/tools/track/setup-box.sh +0 -59
- package/tools/track/study-2d.mjs +0 -728
- /package/src/{ardy → motion}/collision-blockers.js +0 -0
- /package/src/{ardy → motion}/ground.js +0 -0
- /package/src/{ardy → motion}/ik-drag.js +0 -0
- /package/src/{ardy → motion}/ik-key-json.js +0 -0
- /package/src/{ardy → motion}/ik.js +0 -0
- /package/src/{ardy → motion}/physics-surface.js +0 -0
- /package/src/{ardy → motion}/playback-clock.js +0 -0
- /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,
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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="
|
|
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
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
ghostLayers = [], // [{ owner,
|
|
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
|
-
//
|
|
1015
|
-
//
|
|
1016
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,37 +1612,7 @@ 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
|
-
selectedMotionSegment ? <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> : null
|
|
1875
|
-
) : (<>
|
|
1615
|
+
{motionTools || workflowMode === "pose" || ikMode ? null : (<>
|
|
1876
1616
|
{shots.length > 0 && <button
|
|
1877
1617
|
type="button"
|
|
1878
1618
|
className="tl-seq-action cut"
|
|
@@ -1961,43 +1701,18 @@ export default function Timeline({
|
|
|
1961
1701
|
</>)}
|
|
1962
1702
|
</>)}
|
|
1963
1703
|
</div>
|
|
1964
|
-
{
|
|
1704
|
+
{onClearMotion && (
|
|
1965
1705
|
<div className="tl-head-group tl-motion-tools" role="group" aria-label={ko("Motion controls", "모션 컨트롤")}>
|
|
1966
|
-
{
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
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 ===
|
|
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=
|
|
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 &&
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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
|