cozyclay 2.1.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +43 -116
- package/bin/agent/agent-runner.mjs +14 -15
- 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/session-store.mjs +2 -2
- package/bin/agent/studio-context-budget.mjs +93 -0
- 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-BWCqdgWA.js +1 -0
- package/dist/assets/app-DfTCxaKk.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-BxKXh07s.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-DNO8kv_N.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.js → landing-yfZaJkWc.js} +1 -1
- package/dist/assets/scene-asset-sync-U3kS2rvK.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-D78eiKTa.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 +151 -2625
- package/src/agent-progress.js +80 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +12 -2
- 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 +10 -8
- 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 +214 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +55 -13
- 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 +53 -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 +47 -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 +60 -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 +79 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +113 -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 +197 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +177 -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 +144 -0
- package/src/motion3/compiler/validate-parts.js +118 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +74 -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 +177 -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 +204 -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/people.js +82 -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 +125 -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 +195 -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 +44 -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/people-checks.js +37 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +228 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +129 -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 +113 -0
- package/src/motion3/schema/types.js +149 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/settle.js +136 -0
- package/src/motion3/skills/basic.js +83 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/flip.js +200 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/impact.js +109 -0
- package/src/motion3/skills/index.js +85 -0
- package/src/motion3/skills/info.js +80 -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/qa-label.js +25 -0
- 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 +16 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +47 -77
- package/src/studio-app-binding.js +27 -47
- 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 +15 -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 +109 -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 +9 -125
- 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
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// What the agent's motion work looks like while it runs, for the user's eyes. Every motion.compile publishes its
|
|
2
|
+
// draft here, installed or not: the take it baked, its QA (total, groups, goals) and its beats (timeline). The
|
|
3
|
+
// images the agent asks to see (verify_result) are published here too. The viewport plays the latest draft as a
|
|
4
|
+
// ghost, the timeline lays out its beats, and the agent panel draws the QA trend, the goals and the images.
|
|
5
|
+
// In-page only: nothing here is saved with the project, undone, or sent to the agent. Each character keeps its
|
|
6
|
+
// last KEEP drafts; the images keep the last KEEP. A new agent turn starts clean (clearProgress).
|
|
7
|
+
|
|
8
|
+
import { qaProblems } from "./qa-label.js";
|
|
9
|
+
const KEEP = 12;
|
|
10
|
+
const drafts = new Map(), sheets = [], listeners = new Set();
|
|
11
|
+
let version = 0, counter = 0;
|
|
12
|
+
|
|
13
|
+
function emit() {
|
|
14
|
+
version++;
|
|
15
|
+
for (const listener of [...listeners]) {
|
|
16
|
+
try { listener(); } catch { /* a broken view never stops the agent */ }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const pick = (row, keys) => Object.fromEntries(keys.filter(k => row?.[k] !== undefined).map(k => [k, row[k]]));
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* publishDraft(characterId, { take, installed, specId, label, qa, timeline }) -> the stored draft:
|
|
24
|
+
* { n (1, 2, ... per character), at (ms), characterId, take, frames, installed, specId, label,
|
|
25
|
+
* qa: { ok, total, problems, groups: [{ kind, subject, count, first, last, worst }], goals: [{ name, kind, ok, value, need, at }] },
|
|
26
|
+
* timeline: the compile's beats (pipeline.js timelineOf) }.
|
|
27
|
+
*/
|
|
28
|
+
export function publishDraft(characterId, { take, installed = false, specId = null, label = null, qa = null, timeline = [] }) {
|
|
29
|
+
const list = drafts.get(characterId) ?? [];
|
|
30
|
+
const draft = Object.freeze({
|
|
31
|
+
n: (list.at(-1)?.n ?? 0) + 1, at: Date.now(), seq: ++counter, characterId, take, frames: take?.frameCount ?? 0,
|
|
32
|
+
installed: !!installed, specId, label,
|
|
33
|
+
qa: { ok: !!qa?.ok, total: qa?.total ?? 0, problems: qaProblems(qa),
|
|
34
|
+
groups: (qa?.groups ?? []).map(g => pick(g, ["kind", "subject", "count", "first", "last", "worst"])),
|
|
35
|
+
goals: (qa?.goals ?? []).map(g => pick(g, ["name", "kind", "ok", "value", "need", "at"])) },
|
|
36
|
+
timeline: timeline ?? [],
|
|
37
|
+
});
|
|
38
|
+
drafts.set(characterId, [...list, draft].slice(-KEEP));
|
|
39
|
+
emit();
|
|
40
|
+
return draft;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The drafts of a character, oldest first (at most KEEP). */
|
|
44
|
+
export const draftsOf = characterId => drafts.get(characterId) ?? [];
|
|
45
|
+
|
|
46
|
+
/** The newest draft of a character, or of any character when none is named; null when there is none. */
|
|
47
|
+
export function latestDraft(characterId) {
|
|
48
|
+
if (characterId !== undefined) return draftsOf(characterId).at(-1) ?? null;
|
|
49
|
+
let best = null;
|
|
50
|
+
for (const list of drafts.values()) if (list.length && (!best || list.at(-1).seq > best.seq)) best = list.at(-1);
|
|
51
|
+
return best;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** publishSheet({ characterId, dataUrl, width, height, caption, frames }) -> the stored image record. */
|
|
55
|
+
export function publishSheet({ characterId = null, dataUrl, width = 0, height = 0, caption = "", frames = [] }) {
|
|
56
|
+
const sheet = Object.freeze({ at: Date.now(), seq: ++counter, characterId, dataUrl, width, height, caption, frames });
|
|
57
|
+
sheets.push(sheet);
|
|
58
|
+
sheets.splice(0, Math.max(0, sheets.length - KEEP));
|
|
59
|
+
emit();
|
|
60
|
+
return sheet;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The published images, oldest first (at most KEEP). */
|
|
64
|
+
export const progressSheets = () => [...sheets];
|
|
65
|
+
|
|
66
|
+
/** Forget every draft and image (a new agent turn). */
|
|
67
|
+
export function clearProgress() {
|
|
68
|
+
drafts.clear();
|
|
69
|
+
sheets.length = 0;
|
|
70
|
+
emit();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** subscribeProgress(listener) -> unsubscribe; with progressVersion it fits React's useSyncExternalStore. */
|
|
74
|
+
export function subscribeProgress(listener) {
|
|
75
|
+
listeners.add(listener);
|
|
76
|
+
return () => { listeners.delete(listener); };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** A number that changes on every publish or clear. */
|
|
80
|
+
export const progressVersion = () => version;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// The agent turn's controls, and its mark. One user message is one editor transaction
|
|
2
|
+
// (#702, #715): every edit of the turn lands in one composed session, so the message is one
|
|
3
|
+
// undo step. A script runs inside that turn, after edits the same message already made (an
|
|
4
|
+
// installed take, a placed set). When the script fails, only its own edits roll back:
|
|
5
|
+
// agent.turn.mark records where it starts, and agent.turn.rollback restores exactly what came
|
|
6
|
+
// after the mark and leaves the turn open, so the earlier edits still land with the turn.
|
|
7
|
+
import { StudioProtocolError } from './studio-agent-protocol.js';
|
|
8
|
+
|
|
9
|
+
const TURN_CONTROLS = ['begin', 'mark', 'rollback', 'finish', 'cancel'];
|
|
10
|
+
/** The wire schema of each agent.turn.* control: the owning turn's id, nothing else. */
|
|
11
|
+
export const agentTurnControls = (object, identifier) => Object.fromEntries(TURN_CONTROLS.map(verb => [`agent.turn.${verb}`, object({ turnId: identifier })]));
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* agent.turn.mark or agent.turn.rollback on the open turn. Answers the transaction its receipt
|
|
15
|
+
* describes: a mark affects nothing, a rollback names the ids the rolled-back edits touched.
|
|
16
|
+
* The bus adds each later edit's ids to `turn.mark.since`.
|
|
17
|
+
*/
|
|
18
|
+
export function markAgentTurn(id, turn) {
|
|
19
|
+
if (id === 'agent.turn.mark') {
|
|
20
|
+
turn.mark = { point: turn.session?.savepoint() ?? null, affectedIds: new Set(turn.affectedIds), since: new Set() };
|
|
21
|
+
return { txId: turn.txId, affectedIds: [], toasts: [] };
|
|
22
|
+
}
|
|
23
|
+
const { mark } = turn;
|
|
24
|
+
if (!mark) throw new StudioProtocolError('STALE_TARGET', 'The agent turn has no mark to roll back to.');
|
|
25
|
+
turn.mark = null;
|
|
26
|
+
// No session at the mark: everything the turn authored came after it.
|
|
27
|
+
turn.session?.rollback(mark.point ?? undefined);
|
|
28
|
+
turn.affectedIds = mark.affectedIds;
|
|
29
|
+
return { txId: turn.txId, affectedIds: [...mark.since], toasts: [] };
|
|
30
|
+
}
|
package/src/analytics.js
CHANGED
|
@@ -905,7 +905,7 @@ function runtimeConfig() {
|
|
|
905
905
|
function isLoopbackOrigin(origin) {
|
|
906
906
|
try {
|
|
907
907
|
const parsed = new URL(origin);
|
|
908
|
-
return parsed.protocol === "http:" &&
|
|
908
|
+
return parsed.protocol === "http:" && ["127.0.0.1", "localhost"].includes(parsed.hostname);
|
|
909
909
|
} catch {
|
|
910
910
|
return false;
|
|
911
911
|
}
|
|
@@ -1051,17 +1051,10 @@ export async function initAnalytics() {
|
|
|
1051
1051
|
void recordDeviceProfile();
|
|
1052
1052
|
if (resolved.distribution === "npm") {
|
|
1053
1053
|
track("app:session_started");
|
|
1054
|
-
// This follows the session marker so the two events form one
|
|
1055
|
-
// capability baseline in funnel queries.
|
|
1056
|
-
void recordMotionBackendState();
|
|
1057
1054
|
if (resolved.firstLaunch) {
|
|
1058
1055
|
const heardFrom = resolved.firstLaunchHeardFrom;
|
|
1059
1056
|
track("install:first_launch", heardFrom ? { heard_from: heardFrom } : {});
|
|
1060
1057
|
}
|
|
1061
|
-
} else {
|
|
1062
|
-
// Hosted sessions have PostHog's native session marker rather than
|
|
1063
|
-
// the npm-only custom event above.
|
|
1064
|
-
void recordMotionBackendState();
|
|
1065
1058
|
}
|
|
1066
1059
|
} catch {
|
|
1067
1060
|
console.info("[analytics] initialization failed");
|
|
@@ -1070,17 +1063,6 @@ export async function initAnalytics() {
|
|
|
1070
1063
|
await initPromise;
|
|
1071
1064
|
}
|
|
1072
1065
|
|
|
1073
|
-
async function recordMotionBackendState() {
|
|
1074
|
-
let health = null;
|
|
1075
|
-
try {
|
|
1076
|
-
const response = await fetch("/ardy/health", { signal: AbortSignal.timeout(5000) });
|
|
1077
|
-
if (response.ok) health = await response.json();
|
|
1078
|
-
} catch {
|
|
1079
|
-
// No bridge is a normal hosted/demo state.
|
|
1080
|
-
}
|
|
1081
|
-
track("motion:backend_state", motionBackendState(health));
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
1066
|
export function track(event, props = {}) {
|
|
1085
1067
|
if (!initialized || !enabled || !posthog) return;
|
|
1086
1068
|
try {
|
package/src/app-context.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { createContext, useContext } from 'react';
|
|
1
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
2
2
|
import { StudioProtocolError } from './studio-agent-protocol.js';
|
|
3
3
|
|
|
4
4
|
export const AppContext = createContext(null);
|
|
5
|
-
|
|
5
|
+
// Reading `bus` refreshes the studio binding (it serializes the whole scene), so a panel must not read it while it
|
|
6
|
+
// renders: playback re-renders panels every frame. The hook hands back a stable handle that opens the bus per call.
|
|
7
|
+
export function useBus() {
|
|
8
|
+
const app = useContext(AppContext);
|
|
9
|
+
return useMemo(() => ({ run: (...args) => app.bus.run(...args), subscribe: (...args) => app.bus.subscribe(...args) }), [app]);
|
|
10
|
+
}
|
|
6
11
|
|
|
7
12
|
const ref = current => ({ current });
|
|
8
13
|
|
|
@@ -98,6 +103,11 @@ export function createAppContext({
|
|
|
98
103
|
closed = true; activeComposition = null;
|
|
99
104
|
return remember(changed);
|
|
100
105
|
},
|
|
106
|
+
// A savepoint inside the open composition (a script inside an agent turn): rollback(point)
|
|
107
|
+
// restores each owner to it, and an owner first touched after it to where it began. The
|
|
108
|
+
// composition stays open, so edits made before the savepoint still land with it.
|
|
109
|
+
savepoint() { check(); return new Map([...members].map(([handle, row]) => [handle, row.session.mark()])); },
|
|
110
|
+
rollback(point = new Map()) { check(); for (const [handle, row] of [...members].reverse()) row.session.rollback(point.get(handle)); },
|
|
101
111
|
cancel(options) {
|
|
102
112
|
if (closed) return false;
|
|
103
113
|
closed = true; activeComposition = null;
|
package/src/app-stage.jsx
CHANGED
|
@@ -22,8 +22,6 @@ export {
|
|
|
22
22
|
characterModelUrl,
|
|
23
23
|
CHARACTER_MODEL_LABELS,
|
|
24
24
|
nextCharacterId,
|
|
25
|
-
DEMO_MOTION_URL,
|
|
26
|
-
DEMO_MOTION_PROMPT,
|
|
27
25
|
OBJECT_DELETE_UNDO_MS,
|
|
28
26
|
ASSET_DELETE_UNDO_MS,
|
|
29
27
|
CLAY,
|
|
@@ -64,7 +62,7 @@ export {
|
|
|
64
62
|
|
|
65
63
|
export * from "./stage-attach-frames.js";
|
|
66
64
|
|
|
67
|
-
export { Character
|
|
65
|
+
export { Character } from "./stage-characters.jsx";
|
|
68
66
|
|
|
69
67
|
export {
|
|
70
68
|
ShotRig,
|
|
@@ -105,40 +103,10 @@ export {
|
|
|
105
103
|
} from "./stage-capture.jsx";
|
|
106
104
|
export {
|
|
107
105
|
TIMELINE_FPS,
|
|
108
|
-
ARDY_FPS,
|
|
109
|
-
toArdyFrame,
|
|
110
|
-
posePlacementFrame,
|
|
111
|
-
toArdyFrameEntries,
|
|
112
|
-
toArdySegments,
|
|
113
106
|
DEFAULT_DURATION_S,
|
|
114
107
|
DEFAULT_PLAYBACK_SPEED,
|
|
115
|
-
BRIDGE_RECHECK_MS,
|
|
116
|
-
ARDY_PROMPT_HORIZON_FRAMES,
|
|
117
108
|
MAX_WAYPOINTS,
|
|
118
|
-
|
|
119
|
-
ARDY_DURATION_MIN,
|
|
120
|
-
ARDY_DURATION_MAX,
|
|
121
|
-
ARDY_SEED_MAX,
|
|
122
|
-
ARDY_PRESERVE_DEFAULT,
|
|
123
|
-
DEFAULT_PROMPT_CLIPS,
|
|
124
|
-
buildPromptSchedule,
|
|
125
|
-
ikTracksInRange,
|
|
126
|
-
PRESERVE_TRACK_LIMBS,
|
|
127
|
-
PRESERVE_LIMB_ORDER,
|
|
128
|
-
preserveLimbLabel,
|
|
129
|
-
preserveTracksSummary,
|
|
130
|
-
LINE_EDIT_TRACK_OPTIONS,
|
|
131
|
-
lineTrackLabel,
|
|
132
|
-
LINE_EDIT_DEFAULT_TRACK,
|
|
133
|
-
LINE_CURVE_MARKER_STRIDE,
|
|
134
|
-
MIN_CURVE_POINTS,
|
|
135
|
-
LINE_PREVIEW_DEBOUNCE_MS,
|
|
136
|
-
LINE_CAPABILITY_RETRY_MS,
|
|
137
|
-
LINE_EDIT_REFUSALS,
|
|
138
|
-
LINE_CURVE_REFUSALS,
|
|
139
|
-
MULTIMODEL_REASONS,
|
|
140
|
-
MULTIMODEL_SAMPLE_FPS,
|
|
141
|
-
} from "./ardy-bridge-contract.js";
|
|
109
|
+
} from "./timeline-defaults.js";
|
|
142
110
|
|
|
143
111
|
/* ------------------------------------------------------------------ 3D --- */
|
|
144
112
|
|
package/src/asset-pane.jsx
CHANGED
|
@@ -3,7 +3,7 @@ import { ko } from "./locale.js";
|
|
|
3
3
|
import { CHARACTER_MODEL_IDS, PROXY_FIGURE_MODEL } from "./scenes.js";
|
|
4
4
|
import { OBJECT_LIBRARY } from "./scene-objects.js";
|
|
5
5
|
import { displayObjectLabel } from "./object-catalog.jsx";
|
|
6
|
-
import { assetAspect, isMeshAssetId, isSupportedMeshType } from "./scene-assets.js";
|
|
6
|
+
import { assetAspect, isImageAssetId, isMeshAssetId, isSupportedMeshType } from "./scene-assets.js";
|
|
7
7
|
import { assetKind, formatAssetBytes } from "./asset-shelf.js";
|
|
8
8
|
import { assetRecord } from "./scene-asset-cache.js";
|
|
9
9
|
import ResourceStatus from "./resource-status.jsx";
|
|
@@ -99,14 +99,15 @@ function loadThumb(id) {
|
|
|
99
99
|
// card hides. A present record whose bytes fail below resolves null
|
|
100
100
|
// instead, and the card stays visible so it can be deleted.
|
|
101
101
|
if (!record) return undefined;
|
|
102
|
-
|
|
102
|
+
// An HDR environment file (hdr- id) is no picture either: it gets the same generic card.
|
|
103
|
+
if (!isImageAssetId(record.id) || isSupportedMeshType(record.type)) {
|
|
103
104
|
// A GLB is not a picture. Decoding it as one would mark every
|
|
104
105
|
// model "unreadable" in Manage storage — show a generic cube
|
|
105
106
|
// instead, using the same card preview the catalogue already has.
|
|
106
107
|
return {
|
|
107
108
|
name: record.name,
|
|
108
109
|
bytesLabel: formatAssetBytes(record.bytes.byteLength),
|
|
109
|
-
kind: "mesh",
|
|
110
|
+
kind: isMeshAssetId(record.id) || isSupportedMeshType(record.type) ? "mesh" : "environment",
|
|
110
111
|
mesh: true,
|
|
111
112
|
};
|
|
112
113
|
}
|
|
@@ -167,7 +168,7 @@ function StorageAssetRow({ id, onDelete, deleting, usageCount = 0, graphSignatur
|
|
|
167
168
|
const usageLabel = ko(`Used by ${usageCount} scene object${usageCount === 1 ? "" : "s"}`, `${usageCount}개 씬 오브젝트에서 사용 중`);
|
|
168
169
|
const deleteLabel = ko(`Delete ${name} from storage`, `${name}을(를) 저장소에서 삭제`);
|
|
169
170
|
const confirmationId = `asset-storage-warning-${id}`;
|
|
170
|
-
const kindLabel = thumb?.kind === "mesh" ? ko("Model", "모델") : thumb?.kind === "matte" ? ko("Matte", "매트") : ko("Image", "이미지");
|
|
171
|
+
const kindLabel = thumb?.kind === "mesh" ? ko("Model", "모델") : thumb?.kind === "environment" ? ko("HDR environment", "HDR 환경") : thumb?.kind === "matte" ? ko("Matte", "매트") : ko("Image", "이미지");
|
|
171
172
|
return (
|
|
172
173
|
<li className="asset-storage-row">
|
|
173
174
|
{mesh ? (
|
|
@@ -486,7 +487,7 @@ export function FolderGrid({ folder, query, ...props }) {
|
|
|
486
487
|
/>
|
|
487
488
|
);
|
|
488
489
|
});
|
|
489
|
-
if (!takeVersions.length) empty = ko("No takes for this character yet.
|
|
490
|
+
if (!takeVersions.length) empty = ko("No takes for this character yet. Ask the agent to compile one.", "이 인물의 테이크가 아직 없어요. 에이전트에게 모션 컴파일을 요청하세요.");
|
|
490
491
|
} else if (folder === "poses") {
|
|
491
492
|
tiles = poses.map((pose, index) => ({ pose, index, label: pose?.name || pose?.label || ko(`Pose ${index + 1}`, `포즈 ${index + 1}`) }))
|
|
492
493
|
.filter(({ label }) => matchesQuery(label, query))
|
package/src/camera-block.js
CHANGED
|
@@ -59,6 +59,26 @@ function cloneRailFollow(value) {
|
|
|
59
59
|
return null;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* What a rail shot's head holds: up to 4 cast ids (their middle, frame by
|
|
64
|
+
* frame, at their chest or at a world height) or one world point. Null = the follow subject (every older shot).
|
|
65
|
+
* Like the crane, an aim and a lens of its own (focalMm) ride the rail and
|
|
66
|
+
* are left out of the block without one, so untouched shots stay byte-stable.
|
|
67
|
+
*/
|
|
68
|
+
function cloneRailAim(value) {
|
|
69
|
+
const ids = Array.isArray(value?.ids) ? value.ids.filter((id) => typeof id === "string" && id).slice(0, 4) : [];
|
|
70
|
+
if (ids.length) return Number.isFinite(value.height) ? { ids, height: value.height } : { ids };
|
|
71
|
+
const point = value?.point;
|
|
72
|
+
return point && [point.x, point.y, point.z].every(Number.isFinite) ? { point: { x: point.x, y: point.y, z: point.z } } : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The aim and lens fields a block with a rail carries (none without one). */
|
|
76
|
+
export function railShotFields(value, cameraRail) {
|
|
77
|
+
const aim = cameraRail ? cloneRailAim(value.aim) : null;
|
|
78
|
+
const focalMm = cameraRail && Number.isFinite(value.focalMm) && value.focalMm > 0 ? value.focalMm : null;
|
|
79
|
+
return { ...(aim ? { aim } : {}), ...(focalMm ? { focalMm } : {}) };
|
|
80
|
+
}
|
|
81
|
+
|
|
62
82
|
/** Build a complete block from a partial or stored camera value. */
|
|
63
83
|
export function createCameraBlock(input = {}) {
|
|
64
84
|
const value = input && typeof input === "object" ? input : {};
|
|
@@ -82,6 +102,7 @@ export function createCameraBlock(input = {}) {
|
|
|
82
102
|
railFollow: cloneRailFollow(value.railFollow),
|
|
83
103
|
craneHeight,
|
|
84
104
|
dollyTiming: timingIsFlat(dollyTiming) ? null : dollyTiming,
|
|
105
|
+
...railShotFields(value, cameraRail),
|
|
85
106
|
};
|
|
86
107
|
}
|
|
87
108
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Pure camera direction helpers shared by playback, export and the agent's
|
|
2
|
+
// receipts: the per-frame follow/rail camera of every shot, where a rail
|
|
3
|
+
// shot's head aims, and the stops a chain of camera keys makes.
|
|
4
|
+
|
|
5
|
+
import { createCameraBlock } from "./camera-block.js";
|
|
6
|
+
import { buildFollowTrack, buildRail, buildRailFollowTrack } from "./camera-follow.js";
|
|
7
|
+
import { shortestArc } from "./camera-move.js";
|
|
8
|
+
import { RAIL_SCHEDULE_LEGACY, RAIL_SCHEDULE_RANGE, resolveRailSchedule } from "./camera-rail-schedule.js";
|
|
9
|
+
import { shotAtFrame } from "./cuts.js";
|
|
10
|
+
import { objectTransformAt } from "./object-path.js";
|
|
11
|
+
import { placementAt } from "./root-path.js";
|
|
12
|
+
import { sampleAt } from "./sample-at.js";
|
|
13
|
+
import { SUBJECT_HEIGHT_M, focalMmToFov } from "./shot.js";
|
|
14
|
+
import { takeHips } from "./motion3/take.js";
|
|
15
|
+
|
|
16
|
+
const DEG = Math.PI / 180;
|
|
17
|
+
/** where a character's aim sits on its body: chest height, as a share of its stature, or above a take's hips */
|
|
18
|
+
const CHEST = 0.75, HIPS_TO_CHEST = 0.35;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The follow and rail cameras of every shot, one sample per timeline frame
|
|
22
|
+
* (null where a shot keys its camera or its rail is off). A rail shot with
|
|
23
|
+
* an aim holds that aim instead of the follow subject and runs its rail on
|
|
24
|
+
* schedule (no distance pull, no pitch offset), with its own lens when the
|
|
25
|
+
* block names one. aimAt(id, frame) answers one cast member's aim point.
|
|
26
|
+
*/
|
|
27
|
+
export function buildShotCameraTracks({ shots, subjectTrack, fps, yawDeg = 0, aimAt = () => null, filmback = {} }) {
|
|
28
|
+
const combined = new Array(subjectTrack.length).fill(null);
|
|
29
|
+
const initialDir = { x: Math.sin(yawDeg * DEG), z: Math.cos(yawDeg * DEG) };
|
|
30
|
+
for (const shot of shots) {
|
|
31
|
+
const camera = createCameraBlock(shot.camera);
|
|
32
|
+
if (camera.mode === "keys") continue;
|
|
33
|
+
const start = shot.startFrame, subjectSlice = subjectTrack.slice(start, Math.min(subjectTrack.length, shot.endFrame + 1));
|
|
34
|
+
const params = { ...camera.followCam, craneHeight: camera.craneHeight, dollyTiming: camera.dollyTiming, initialDir };
|
|
35
|
+
const rail = camera.mode === "rail" ? buildRail(camera.cameraRail) : null;
|
|
36
|
+
if (!rail) {
|
|
37
|
+
buildFollowTrack(subjectSlice, fps, params).forEach((sample, offset) => { combined[start + offset] = sample; });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const schedule = resolveRailSchedule({ railFollow: camera.railFollow, cameraRail: camera.cameraRail, frameCount: subjectSlice.length });
|
|
41
|
+
if (schedule.kind !== RAIL_SCHEDULE_RANGE && schedule.kind !== RAIL_SCHEDULE_LEGACY) continue;
|
|
42
|
+
const from = start + schedule.startFrame, length = schedule.endFrame - schedule.startFrame + 1;
|
|
43
|
+
const aim = camera.aim ? Array.from({ length }, (_, offset) => railAimPoint(camera.aim, from + offset, aimAt)) : null;
|
|
44
|
+
const aimed = aim?.every(Boolean) ? aim : null;
|
|
45
|
+
const track = aimed
|
|
46
|
+
? buildRailFollowTrack(aimed, fps, rail, { ...params, aimHeight: aimed.reduce((sum, point) => sum + point.y, 0) / length, distanceInfluence: 0, pitchOffsetDeg: 0 })
|
|
47
|
+
: buildRailFollowTrack(subjectSlice.slice(schedule.startFrame, schedule.endFrame + 1), fps, rail, params);
|
|
48
|
+
const fovDeg = camera.focalMm ? focalMmToFov(camera.focalMm, filmback.sensorId, filmback.aspectRatio) / DEG : null;
|
|
49
|
+
track.forEach((sample, offset) => { combined[from + offset] = fovDeg ? { ...sample, fovDeg } : sample; });
|
|
50
|
+
}
|
|
51
|
+
return combined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A rail aim at one frame: its world point, or the middle of its cast (null when one is missing). */
|
|
55
|
+
function railAimPoint(aim, frame, aimAt) {
|
|
56
|
+
if (aim.point) return aim.point;
|
|
57
|
+
const points = aim.ids.map((id) => aimAt(id, frame));
|
|
58
|
+
if (points.some((point) => !point)) return null;
|
|
59
|
+
const mean = (axis) => points.reduce((sum, point) => sum + point[axis], 0) / points.length;
|
|
60
|
+
return { x: mean("x"), y: aim.height ?? mean("y"), z: mean("z") };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* One cast member's aim point at a frame: a character's chest where its take
|
|
65
|
+
* (or placement) puts it, an object's middle where its route (or placement)
|
|
66
|
+
* puts it. clipOf(character) is the take the character plays.
|
|
67
|
+
*/
|
|
68
|
+
export function castAimPoint(id, frame, { characters = [], objects = [], shots = [], clipOf = () => null, frameCount, fps }) {
|
|
69
|
+
const character = characters.find((entry) => entry.id === id);
|
|
70
|
+
if (character) {
|
|
71
|
+
const clip = clipOf(character), scale = character.scale ?? 1, yaw = (character.rot ?? 0) * DEG;
|
|
72
|
+
// A take v3 moves the hips in the character's placement frame (motion3/take.js), not its root.
|
|
73
|
+
if (clip?.hips) {
|
|
74
|
+
const [x, y, z] = takeHips(clip, frame);
|
|
75
|
+
return { x: character.x + (x * Math.cos(yaw) + z * Math.sin(yaw)) * scale, y: (character.y ?? 0) + (y + HIPS_TO_CHEST) * scale, z: character.z + (-x * Math.sin(yaw) + z * Math.cos(yaw)) * scale };
|
|
76
|
+
}
|
|
77
|
+
const at = placementAt(character, frame, { shotAt: (at) => shotAtFrame(shots, at),
|
|
78
|
+
takeRoot: clip ? sampleAt({ frameCount: clip.frames, motion: clip }, null, frame).subject : null });
|
|
79
|
+
return { x: at.x, y: (character.y ?? 0) + CHEST * SUBJECT_HEIGHT_M * (character.scale ?? 1), z: at.z };
|
|
80
|
+
}
|
|
81
|
+
const object = objects.find((entry) => entry.id === id);
|
|
82
|
+
if (!object) return null;
|
|
83
|
+
const at = objectTransformAt(object, frame, { frameCount, fps }) ?? object;
|
|
84
|
+
return { x: at.x, y: at.y + ((object.height ?? 0) * (object.scaleY ?? 1)) / 2, z: at.z };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The follow subject's path, where its take v3 moves its body rather than its root (a follow or
|
|
88
|
+
* aimless rail camera keeps up with the walk); any other subject keeps the path it has. */
|
|
89
|
+
export function followSubjectTrack(subjectTrack, subjectId, cast) {
|
|
90
|
+
const character = cast.characters?.find((entry) => entry.id === subjectId);
|
|
91
|
+
if (!subjectTrack || !character || !cast.clipOf(character)?.hips) return subjectTrack;
|
|
92
|
+
return subjectTrack.map((point, frame) => castAimPoint(subjectId, frame, cast) ?? point);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The frames of the keys a camera halts at for no reason: an interior key the
|
|
97
|
+
* move runs on through in the same direction. Every key eases in and out, so
|
|
98
|
+
* the camera slows to a stop there; a hold, a reversal or a new direction is
|
|
99
|
+
* a stop the shot means and is not listed.
|
|
100
|
+
*/
|
|
101
|
+
export function cameraKeyStops(keys = []) {
|
|
102
|
+
const steps = keys.slice(1).map((key, index) => {
|
|
103
|
+
const a = keys[index].framing, b = key.framing;
|
|
104
|
+
return [b.pos.x - a.pos.x, b.pos.y - a.pos.y, b.pos.z - a.pos.z, shortestArc(a.yaw, b.yaw), b.pitch - a.pitch, (b.fovDeg - a.fovDeg) * DEG];
|
|
105
|
+
});
|
|
106
|
+
const length = (step) => Math.hypot(...step);
|
|
107
|
+
return keys.slice(1, -1).filter((_, index) => {
|
|
108
|
+
const u = steps[index], v = steps[index + 1], dot = u.reduce((sum, value, axis) => sum + value * v[axis], 0);
|
|
109
|
+
return length(u) > 0.02 && length(v) > 0.02 && dot > 0.5 * length(u) * length(v);
|
|
110
|
+
}).map((key) => key.frame);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A receipt warning for a shot whose camera keys halt a move (none when they do not). */
|
|
114
|
+
export function cameraKeyWarnings(shot) {
|
|
115
|
+
const stops = cameraKeyStops(shot.cameraKeys);
|
|
116
|
+
if (!stops.length) return [];
|
|
117
|
+
const listed = `${stops.slice(0, 4).join(", ")}${stops.length > 4 ? ` +${stops.length - 4}` : ""}`;
|
|
118
|
+
return [{ code: "CAMERA_KEY_STOPS", id: shot.id, count: stops.length,
|
|
119
|
+
message: `Camera stops at keys ${listed} (keys ease in/out); use a rail (shot.setCameraRail) or 2 keys.`.slice(0, 120) }];
|
|
120
|
+
}
|
package/src/character-kind.js
CHANGED
|
@@ -6,7 +6,7 @@ import { isProxyFigure } from "./scenes.js";
|
|
|
6
6
|
import { isKo } from "./locale.js";
|
|
7
7
|
import { StudioProtocolError } from "./studio-agent-protocol.js";
|
|
8
8
|
|
|
9
|
-
export const CHARACTER_CAPABILITIES = Object.freeze(["rig", "ik", "pose", "motion"
|
|
9
|
+
export const CHARACTER_CAPABILITIES = Object.freeze(["rig", "ik", "pose", "motion"]);
|
|
10
10
|
const PROXY_BODY_RADIUS = 0.22;
|
|
11
11
|
const PROXY_BODY_HEIGHT = 1.45;
|
|
12
12
|
const PROXY_HEAD_RADIUS = 0.12;
|
|
@@ -36,7 +36,7 @@ export function proxyFigureBounds(character, placement = character) {
|
|
|
36
36
|
return { min, max };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/** `{ rig, ik, pose, motion
|
|
39
|
+
/** `{ rig, ik, pose, motion }` - all false
|
|
40
40
|
* for a capsule figure, all true for a rigged character. */
|
|
41
41
|
export function characterCapabilities(character) {
|
|
42
42
|
const rigged = !isProxyFigure(character);
|
|
@@ -48,13 +48,7 @@ const FEATURE_LABELS = {
|
|
|
48
48
|
rig: ["the rig", "리그"],
|
|
49
49
|
ik: ["IK", "IK"],
|
|
50
50
|
pose: ["Pose mode", "포즈 모드"],
|
|
51
|
-
motion: ["
|
|
52
|
-
mocap: ["Motion capture", "모션 캡처"],
|
|
53
|
-
photoPose: ["Photo pose", "사진 포즈"],
|
|
54
|
-
physics: ["Physics", "물리 보정"],
|
|
55
|
-
collision: ["Collision cleanup", "충돌 정리"],
|
|
56
|
-
trails: ["Motion trails", "궤적선"],
|
|
57
|
-
lineEdit: ["Line edit", "라인 편집"],
|
|
51
|
+
motion: ["Compiled motion", "모션 컴파일"],
|
|
58
52
|
take: ["Loading a take", "테이크 불러오기"],
|
|
59
53
|
};
|
|
60
54
|
|
package/src/command-bus.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// the editor ports own native state/history, and the existing journal owns
|
|
3
3
|
// idempotency. Synchronous UI actions stay synchronous (including user gestures).
|
|
4
4
|
import { HISTORY_LIMIT } from './history.js';
|
|
5
|
-
import { StudioProtocolError, StudioSchemas, validateStudioSchema, validateReceipt, validateStudioIdentity } from './studio-agent-protocol.js';
|
|
5
|
+
import { StudioProtocolError, StudioSchemas, validateStudioSchema, validateReceipt, validateStudioIdentity, refusalCode } from './studio-agent-protocol.js';
|
|
6
|
+
import { agentTurnControls, markAgentTurn } from './agent-turn-mark.js';
|
|
6
7
|
|
|
7
8
|
// A batching/retention adapter over the existing object history owner. The
|
|
8
9
|
// native store still owns every snapshot and transition; this tracks only the
|
|
@@ -62,9 +63,7 @@ export function createCommandBus({ registry, ports }) {
|
|
|
62
63
|
const object = properties => ({ type: 'object', properties, required: Object.keys(properties), additionalProperties: false });
|
|
63
64
|
const argsSchema = { type: 'object', properties: {}, required: [], additionalProperties: true };
|
|
64
65
|
const controls = {
|
|
65
|
-
|
|
66
|
-
'agent.turn.finish': object({ turnId: identifier }),
|
|
67
|
-
'agent.turn.cancel': object({ turnId: identifier }),
|
|
66
|
+
...agentTurnControls(object, identifier),
|
|
68
67
|
'run.begin': object({ id: identifier, args: argsSchema }),
|
|
69
68
|
'run.update': object({ txId: identifier, args: argsSchema }),
|
|
70
69
|
'run.commit': object({ txId: identifier }),
|
|
@@ -94,6 +93,8 @@ export function createCommandBus({ registry, ports }) {
|
|
|
94
93
|
}
|
|
95
94
|
if (agentTurn?.txId !== args.turnId) fail('STALE_TARGET', 'The agent turn is no longer open.');
|
|
96
95
|
const turn = agentTurn;
|
|
96
|
+
// A script's mark, and the rollback to it that keeps the turn open (agent-turn-mark.js).
|
|
97
|
+
if (id === 'agent.turn.mark' || id === 'agent.turn.rollback') return transactionReceipt(id, markAgentTurn(id, turn), request, before);
|
|
97
98
|
// A rolled-back turn restores every owner it touched and leaves no entry.
|
|
98
99
|
if (id === 'agent.turn.cancel') { agentTurn = null; turn.session?.cancel(); return transactionReceipt(id, turn, request, before); }
|
|
99
100
|
try { turn.historyEntryId = turn.session?.commit().historyEntryId; }
|
|
@@ -202,7 +203,7 @@ export function createCommandBus({ registry, ports }) {
|
|
|
202
203
|
function refusal(request, before, error, mutated) {
|
|
203
204
|
const changed = mutated ?? (ports.read().revision !== before.revision);
|
|
204
205
|
return validateReceipt({ ok: false, commandId: request.commandId, host: before.host,
|
|
205
|
-
code: error
|
|
206
|
+
code: refusalCode(error), phase: changed ? 'commit' : 'admission', affectedIds: [], expectedTargets: [], currentTargets: [],
|
|
206
207
|
mutated: changed, preserved: { authoredState: changed ? 'changed' : 'unchanged' }, recovery: { action: 'inspect', retryAllowed: false },
|
|
207
208
|
message: [...String(error.message || error)].slice(0, 500).join('') });
|
|
208
209
|
}
|
|
@@ -219,7 +220,7 @@ export function createCommandBus({ registry, ports }) {
|
|
|
219
220
|
if (entry.kind === 'mutation' && changed && !historyEntryId && !turn) fail('UNCERTAIN_APPLY', `${entry.id} changed the scene without one undoable entry.`);
|
|
220
221
|
const completed = entry.kind === 'job' || entry.kind === 'document' || (entry.kind === 'mutation' && changed && (turn || after.revision > before.revision + 1));
|
|
221
222
|
const ids = completed || changed ? result.affectedIds : entry.kind === 'transient' ? [before.host.sceneId] : [];
|
|
222
|
-
if (turn && changed) for (const id of ids) turn.affectedIds.add(id);
|
|
223
|
+
if (turn && changed) for (const id of ids) { turn.affectedIds.add(id); turn.mark?.since.add(id); }
|
|
223
224
|
const build = level => validateReceipt({ ok: true, commandId: request.commandId, receiptId: crypto.randomUUID(), host: before.host,
|
|
224
225
|
action: entry.id, summary: result.summary, status: completed ? 'completed' : entry.kind === 'transient' ? 'transient' : changed ? 'applied' : 'noop',
|
|
225
226
|
authored: changed, ...(completed ? { kind: entry.kind, ...(result.output === undefined ? {} : { output: result.output }), ...(same(before.host, after.host) ? {} : { nextHost: after.host }) } : entry.kind === 'transient' ? { view: { before: before.viewRevision ?? 0, after: after.viewRevision ?? 0 } } : { mutated: changed }),
|
|
@@ -292,8 +293,9 @@ export function createCommandBus({ registry, ports }) {
|
|
|
292
293
|
// Job controls observe/cancel an admitted identity; completion itself
|
|
293
294
|
// can advance the revision while their wire request is in transit.
|
|
294
295
|
// Document identity and the job's own publication fence still apply.
|
|
295
|
-
|
|
296
|
-
if (
|
|
296
|
+
// A readOnly action (declared data, e.g. motion.qa) only measures: no revision or gesture fence, the host check above still holds.
|
|
297
|
+
if (!entry?.readOnly && !['job.await', 'job.cancel', 'agent.turn.mark', 'agent.turn.rollback', 'agent.turn.finish', 'agent.turn.cancel'].includes(id) && request.expectedRevision !== before.revision) fail('STALE_SCENE', 'Authored state changed; obtain fresh intent.');
|
|
298
|
+
if (!entry?.readOnly && before.busy && !turn && !transactions.has(validated.txId) && !id.startsWith('job.')) fail('TARGET_BUSY', 'Finish the current editor gesture first.');
|
|
297
299
|
}
|
|
298
300
|
if (controls[id]) return mapResult(control(id, validated, request, before), remember, rejected);
|
|
299
301
|
if (transactions.size) fail('TARGET_BUSY', 'Finish or cancel the open command transaction first.');
|
package/src/commands/cast.js
CHANGED
|
@@ -9,8 +9,7 @@ import { createCharacterEntry, CHARACTER_KIND_IDS, POSTURES, PROXY_FIGURE_MODEL,
|
|
|
9
9
|
import { KIND_FIXED_REFUSAL } from '../character-kind.js';
|
|
10
10
|
import { track } from '../analytics.js';
|
|
11
11
|
import { DEFAULT_POSE } from '../poses.js';
|
|
12
|
-
import { createStableItemId
|
|
13
|
-
import { movePromptClipFrames } from '../ardy/prompt-clips.js';
|
|
12
|
+
import { createStableItemId } from '../stable-items.js';
|
|
14
13
|
import { STUDIO_TOOL_SCHEMAS, StudioSchemas } from '../studio-agent-protocol.js';
|
|
15
14
|
|
|
16
15
|
const id = StudioSchemas.TargetGuard.properties.targetId;
|
|
@@ -21,7 +20,7 @@ const record = { type: 'object', properties: {}, additionalProperties: true };
|
|
|
21
20
|
const newCharacter = { ...record, properties: { model: { type: 'string', enum: [...CHARACTER_KIND_IDS] }, posture: { type: 'string', enum: [...POSTURES] } }, required: [] };
|
|
22
21
|
const characterPatch = { ...record, properties: { posture: { type: 'string', description: `One of ${POSTURES.join(', ')}; anything else stands.` } }, required: [] };
|
|
23
22
|
const mutation = (id, label, schema, exposure = 'open') => ({ id, label, description: label, kind: 'mutation', undoDomain: 'cast', input: schema, exposure });
|
|
24
|
-
|
|
23
|
+
|
|
25
24
|
const semantic = [
|
|
26
25
|
mutation('character.set', 'Set character fields', elementSetSchema('character')),
|
|
27
26
|
mutation('character.add', 'Add character', input({ character: newCharacter })),
|
|
@@ -29,7 +28,7 @@ const semantic = [
|
|
|
29
28
|
mutation('character.update', 'Update character', input({ characterId: id, patch: characterPatch })),
|
|
30
29
|
studioActionDeclaration('character.move'),
|
|
31
30
|
mutation('character.setPose', 'Set character pose', input({ characterId: id, pose: { oneOf: [id, record, { type: 'null' }] }, clearMotion: { type: 'boolean' } }, ['characterId', 'pose'])),
|
|
32
|
-
|
|
31
|
+
|
|
33
32
|
mutation('characters.arrange', 'Arrange characters', STUDIO_TOOL_SCHEMAS.arrange_characters),
|
|
34
33
|
mutation('cast.replace', 'Replace cast', input({ characters: { type: 'array', items: record, minItems: 1 } }), 'ui-only'),
|
|
35
34
|
mutation('cast.setCustomPoses', 'Set pose library', input({ poses: { type: 'array', items: record } }), 'ui-only'),
|
|
@@ -37,9 +36,6 @@ const semantic = [
|
|
|
37
36
|
mutation('cast.removePose', 'Remove saved pose', input({ id }), 'ui-only'),
|
|
38
37
|
mutation('cast.showExtras', 'Show extra subjects', input({ show: { type: 'boolean' } }), 'ui-only'),
|
|
39
38
|
mutation('cast.setLayer', 'Set character layer', input({ characterId: id, layer: record }), 'ui-only'),
|
|
40
|
-
...['add', 'move', 'resize', 'change', 'remove'].map(verb => mutation(`character.${verb}PromptBlock`, `${verb} prompt block`,
|
|
41
|
-
input({ characterId: id, id, frame: { type: 'number' }, edge: { type: 'string', enum: ['start', 'end'] }, text: { type: 'string', maxLength: 2000 } },
|
|
42
|
-
verb === 'add' ? ['characterId', 'frame'] : verb === 'remove' ? ['characterId', 'id'] : verb === 'change' ? ['characterId', 'id', 'text'] : verb === 'resize' ? ['characterId', 'id', 'edge', 'frame'] : ['characterId', 'id', 'frame']), 'ui-only')),
|
|
43
39
|
];
|
|
44
40
|
export const declarations = Object.freeze([...semantic, ...["character.addWaypoint", "character.moveWaypoint", "character.removeWaypoint", "character.clearWaypoints"].map(studioActionDeclaration)]);
|
|
45
41
|
|
|
@@ -56,29 +52,6 @@ export function register(registry, ports) {
|
|
|
56
52
|
const object = ports.state().objects.find(row => row.id === value) ?? fail('STALE_TARGET', `Object ${value} is not in this scene.`);
|
|
57
53
|
if (object.attach) fail('INVALID_ARGUMENT', 'A character cannot be grouped under an object that a character carries.');
|
|
58
54
|
};
|
|
59
|
-
function promptEdit(command, args) {
|
|
60
|
-
const before = character(args.characterId), clips = before.layer.promptClips;
|
|
61
|
-
let next;
|
|
62
|
-
if (command === 'character.addPromptBlock') {
|
|
63
|
-
const snapped = Math.max(0, Math.round(args.frame / 48) * 48);
|
|
64
|
-
const blocked = clips.some(clip => snapped < clip.endFrame && snapped + 48 > clip.startFrame);
|
|
65
|
-
const startFrame = blocked ? Math.max(snapped, ...clips.map(clip => clip.endFrame)) : snapped;
|
|
66
|
-
next = [...clips, { id: createStableItemId('prompt-clip'), startFrame, endFrame: startFrame + 48, text: '' }];
|
|
67
|
-
} else if (command === 'character.removePromptBlock') next = removeStableItem(clips, args.id, 'promptClips');
|
|
68
|
-
else if (command === 'character.changePromptBlock') next = updateStableItem(clips, args.id, clip => ({ ...clip, text: args.text }), 'promptClips');
|
|
69
|
-
else if (command === 'character.movePromptBlock') next = movePromptClipFrames(clips, args.id, args.frame, 48);
|
|
70
|
-
else {
|
|
71
|
-
next = updateStableItem(clips, args.id, clip => {
|
|
72
|
-
const snapped = Math.max(0, Math.round(args.frame / 48) * 48);
|
|
73
|
-
return args.edge === 'start' ? { ...clip, startFrame: Math.min(Math.max(snapped, clip.endFrame - 120), clip.endFrame - 48) }
|
|
74
|
-
: { ...clip, endFrame: Math.min(Math.max(clip.startFrame + 48, snapped), clip.startFrame + 120) };
|
|
75
|
-
}, 'promptClips');
|
|
76
|
-
const resized = next.find(clip => clip.id === args.id);
|
|
77
|
-
if (next.some(clip => clip.id !== args.id && resized.startFrame < clip.endFrame && resized.endFrame > clip.startFrame)) return;
|
|
78
|
-
}
|
|
79
|
-
patch(args.characterId, { layer: { ...before.layer, promptClips: next } });
|
|
80
|
-
owner().extendTimeline(next.reduce((end, clip) => Math.max(end, clip.endFrame), 24));
|
|
81
|
-
}
|
|
82
55
|
const methods = {
|
|
83
56
|
'character.add': ({ character: value }) => {
|
|
84
57
|
const row = createCharacterEntry({ ...value, id: value.id ?? createStableItemId('character') }, owner().read().length);
|
|
@@ -94,9 +67,7 @@ export function register(registry, ports) {
|
|
|
94
67
|
},
|
|
95
68
|
'character.move': ({ characterId, ...value }) => patch(characterId, value),
|
|
96
69
|
'character.setPose': ({ characterId, pose: value, clearMotion }) => { character(characterId); owner().applyPose(characterId, pose(value), clearMotion); },
|
|
97
|
-
|
|
98
|
-
const entry = character(characterId); patch(characterId, { layer: { ...entry.layer, promptClips: blocks } });
|
|
99
|
-
},
|
|
70
|
+
|
|
100
71
|
'cast.replace': ({ characters }) => owner().write(characters),
|
|
101
72
|
'cast.setCustomPoses': ({ poses }) => owner().writeState(before => ({ ...before, customPoses: poses })),
|
|
102
73
|
'cast.savePose': ({ pose, characterId, clearMotion }) => {
|
|
@@ -127,7 +98,7 @@ export function register(registry, ports) {
|
|
|
127
98
|
for (const declaration of semantic.slice(1)) registry.register({ ...declaration, available: mounted, run(args, context) {
|
|
128
99
|
if (declaration.id === 'characters.arrange') { const plan = owner().arrange(args); return { affectedIds: plan.affectedIds, summary: 'Arranged characters.', checks: plan.checks, warnings: plan.warnings }; }
|
|
129
100
|
const before = owner().read();
|
|
130
|
-
|
|
101
|
+
methods[declaration.id](args);
|
|
131
102
|
// The Assets tile and the MCP live path report themselves; this is the agent and
|
|
132
103
|
// any other non-UI bus caller adding a capsule figure.
|
|
133
104
|
if (declaration.id === 'character.add' && context?.origin && context.origin !== 'ui' && isProxyFigure(owner().read().find(row => !before.some(prior => prior.id === row.id)))) track('cast:proxy_added', { surface: 'agent' });
|
|
@@ -5,7 +5,7 @@ import { registerElementKind } from '../elements.js';
|
|
|
5
5
|
const elements = STUDIO_ELEMENTS.filter(row => row.path.startsWith('character.')).map(row => {
|
|
6
6
|
const { min, max, gizmo, ...element } = row;
|
|
7
7
|
if (row.path === 'character.position') return { ...element, documentPath: { x: 'x', y: 'y', z: 'z' } };
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
if (row.path === 'character.pose') return { ...element, readback: item => ({ text: item?.pose?.id ?? null }) };
|
|
10
10
|
return element;
|
|
11
11
|
});
|