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
package/src/timeline-extent.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Return the current content-driven length of the shared production clock.
|
|
3
3
|
*
|
|
4
4
|
* The active character is represented by the editing-buffer motion; inactive
|
|
5
|
-
* characters keep their committed sessionMotion.
|
|
6
|
-
*
|
|
5
|
+
* characters keep their committed sessionMotion (motion.frames is an exclusive
|
|
6
|
+
* timeline count).
|
|
7
7
|
*
|
|
8
8
|
* A zero result is intentional: an empty scene keeps its existing authored
|
|
9
9
|
* duration instead of collapsing the pre-generation clock to one frame.
|
|
@@ -11,28 +11,16 @@
|
|
|
11
11
|
export function timelineContentExtent(
|
|
12
12
|
characters = [],
|
|
13
13
|
activeCharacterId = null,
|
|
14
|
-
|
|
15
|
-
promptClips = [],
|
|
14
|
+
activeMotion = null,
|
|
16
15
|
extraFrames = 0,
|
|
17
16
|
) {
|
|
18
|
-
const
|
|
17
|
+
const authoredExtent = characters.reduce((max, entry) => {
|
|
19
18
|
const clip = entry.id === activeCharacterId ? activeMotion : entry.sessionMotion;
|
|
20
|
-
|
|
21
|
-
? 0
|
|
22
|
-
: (entry.layer?.promptClips ?? []).reduce(
|
|
23
|
-
(layerMax, clip) => Math.max(layerMax, Number.isFinite(clip?.endFrame) ? clip.endFrame : 0),
|
|
24
|
-
0,
|
|
25
|
-
);
|
|
26
|
-
return Math.max(max, Number.isFinite(clip?.frames) ? clip.frames : 0, layerPromptEnd);
|
|
19
|
+
return Math.max(max, Number.isFinite(clip?.frames) ? clip.frames : 0);
|
|
27
20
|
}, 0);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
32
|
-
const authoredExtent = Math.max(longestMotion, promptEnd);
|
|
33
|
-
// Ingested footage owns the clock only before a cast take or prompt
|
|
34
|
-
// schedule exists. Once a 136-frame take is installed, an older 192-frame
|
|
35
|
-
// source video must not recreate the frozen tail this helper prevents.
|
|
21
|
+
// Ingested footage owns the clock only before a cast take exists. Once a
|
|
22
|
+
// 136-frame take is installed, an older 192-frame source video must not
|
|
23
|
+
// recreate the frozen tail this helper prevents.
|
|
36
24
|
return authoredExtent > 0
|
|
37
25
|
? authoredExtent
|
|
38
26
|
: Number.isFinite(extraFrames) ? extraFrames : 0;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// The camera tutorial's set dressing: a short compiled walk (take v3) for the starter scene's
|
|
2
|
+
// character, so the Shot / Rail / Play steps have somebody moving through the set. Built from
|
|
3
|
+
// the character's own bind skeleton and placement; null for a body motion3 cannot drive.
|
|
4
|
+
import { skeletonFor } from "./motion3/rigs/index.js";
|
|
5
|
+
import { add, apply, ry } from "./motion3/compiler/math.js";
|
|
6
|
+
|
|
7
|
+
const TUTORIAL_WALK_LABEL = "A person walks forward and stops.";
|
|
8
|
+
|
|
9
|
+
export function tutorialWalkSpec(character) {
|
|
10
|
+
const sk = skeletonFor(character?.model);
|
|
11
|
+
if (!sk) return null;
|
|
12
|
+
const I = sk.index, P = sk.restWorldPos, ank = P[I.LeftFoot][1], yaw = character.rot ?? 0;
|
|
13
|
+
const at = (offset, y) => add([character.x ?? 0, character.y ?? 0, character.z ?? 0], apply(ry(yaw), [offset[0], y, offset[1]]));
|
|
14
|
+
return { schema: "cozyclay.motion-spec.v1", fps: 24, frames: 216, label: TUTORIAL_WALK_LABEL,
|
|
15
|
+
keys: [{ f: 0, set: { root: at([0, 0], P[I.Hips][1] - 0.01), yaw, foot_l: at([0.09, -0.03], ank), foot_r: at([-0.09, 0.03], ank), fyaw_l: yaw, fyaw_r: yaw } }],
|
|
16
|
+
generators: [{ type: "walk", f0: 6, steps: 8, heading: [yaw, yaw] }] };
|
|
17
|
+
}
|
package/src/ui-scrub.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* a pixel buy?". They live here so a Node test can prove them without a DOM.
|
|
8
8
|
*
|
|
9
9
|
* Sensitivity is a RATE over a fixed travel, the same rule the timeline's
|
|
10
|
-
* curve editors follow (src/
|
|
10
|
+
* curve editors follow (src/motion/timeline.jsx DRAG_TRAVEL_PX): dragging the
|
|
11
11
|
* hand across `SCRUB_TRAVEL_PX` sweeps one meaningful span of the field. The
|
|
12
12
|
* old `pixels * step` mapping tied the feel to the field's precision, so a
|
|
13
13
|
* step 0.05 field needed 100 px to move 5 units while a step 1 field bolted.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { buildShotCameraTracks, castAimPoint, followSubjectTrack } from "./camera-direction.js";
|
|
3
|
+
|
|
4
|
+
/** The per-frame follow/rail camera of every shot (camera-direction.js), rebuilt
|
|
5
|
+
* when the shots, the subject's path or the cast a rail aims at change. */
|
|
6
|
+
export function useShotCameraTracks({ shots, subjectTrack, subjectId, fps, yawDeg, filmback, characters, objects, frameCount, activeId, motion }) {
|
|
7
|
+
return useMemo(() => {
|
|
8
|
+
if (!subjectTrack) return null;
|
|
9
|
+
const cast = { characters, objects, shots, frameCount, fps, clipOf: (entry) => (entry.id === activeId ? motion : entry.sessionMotion) };
|
|
10
|
+
return buildShotCameraTracks({ shots, subjectTrack: followSubjectTrack(subjectTrack, subjectId, cast), fps, yawDeg, filmback, aimAt: (id, frame) => castAimPoint(id, frame, cast) });
|
|
11
|
+
}, [shots, subjectTrack, subjectId, fps, yawDeg, filmback, characters, objects, frameCount, activeId, motion]);
|
|
12
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Onion-skin ghosts: translucent poses of the active character's take before and after the
|
|
2
|
+
// playhead, an editor overlay (ghost-set.js draws them on GIZMO_LAYER). This module holds the
|
|
3
|
+
// viewer setting (transient like the View menu: never saved, never undone) and the pure choices:
|
|
4
|
+
// which frames get a ghost and how each one looks.
|
|
5
|
+
import { useSyncExternalStore } from "react";
|
|
6
|
+
import { expandUse } from "./motion3/skills/index.js";
|
|
7
|
+
import { skeletonFor } from "./motion3/rigs/index.js";
|
|
8
|
+
import { motionScene } from "./motion3/scene.js";
|
|
9
|
+
|
|
10
|
+
/** off; step: every `step` frames; keys: the take spec's key frames nearest the playhead. */
|
|
11
|
+
export const GHOST_MODES = Object.freeze(["off", "step", "keys"]);
|
|
12
|
+
export const GHOST_COUNT = Object.freeze({ min: 1, max: 6 });
|
|
13
|
+
export const GHOST_STEP = Object.freeze({ min: 1, max: 24 });
|
|
14
|
+
const GHOST_DEFAULTS = Object.freeze({ mode: "off", count: 3, step: 4 });
|
|
15
|
+
/** Past poses cool, future poses warm. */
|
|
16
|
+
export const GHOST_COLORS = Object.freeze({ before: "#4d9cff", after: "#ff8a3d" });
|
|
17
|
+
const NEAR_OPACITY = 0.46, FAR_OPACITY = 0.14;
|
|
18
|
+
|
|
19
|
+
const clampInt = (value, { min, max }, fallback) => (Number.isFinite(value) ? Math.max(min, Math.min(max, Math.round(value))) : fallback);
|
|
20
|
+
|
|
21
|
+
/** A full, valid setting from a partial one (unknown modes keep the current mode). */
|
|
22
|
+
export function normalizeGhostSettings(patch = {}, base = GHOST_DEFAULTS) {
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
mode: GHOST_MODES.includes(patch.mode) ? patch.mode : base.mode,
|
|
25
|
+
count: clampInt(patch.count, GHOST_COUNT, base.count),
|
|
26
|
+
step: clampInt(patch.step, GHOST_STEP, base.step),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let current = GHOST_DEFAULTS;
|
|
31
|
+
const listeners = new Set();
|
|
32
|
+
export const ghostSettings = () => current;
|
|
33
|
+
export function setGhostSettings(patch) {
|
|
34
|
+
const next = normalizeGhostSettings(patch, current);
|
|
35
|
+
if (next.mode === current.mode && next.count === current.count && next.step === current.step) return current;
|
|
36
|
+
current = next;
|
|
37
|
+
for (const listener of listeners) listener();
|
|
38
|
+
return current;
|
|
39
|
+
}
|
|
40
|
+
/** What a views contact sheet draws (verify_result ghosts): the viewport's setting while it is on, else 3 each side every 4 frames. */
|
|
41
|
+
export const sheetGhostSettings = () => (current.mode === "off" ? { ...GHOST_DEFAULTS, mode: "step" } : current);
|
|
42
|
+
const subscribe = (listener) => { listeners.add(listener); return () => listeners.delete(listener); };
|
|
43
|
+
export const useGhostSettings = () => useSyncExternalStore(subscribe, ghostSettings, ghostSettings);
|
|
44
|
+
|
|
45
|
+
/** One sentence for receipts and tooltips. */
|
|
46
|
+
export function describeGhosts({ mode, count, step }) {
|
|
47
|
+
if (mode === "off") return "Ghosts off.";
|
|
48
|
+
return `Ghosts: ${count} before and ${count} after the playhead, ${mode === "keys" ? "at the spec's key frames" : `every ${step} frame${step === 1 ? "" : "s"}`}.`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The frames that get a ghost around `frame` within 0..last: `count` on each side, every `step`
|
|
53
|
+
* frames, or the nearest of `keyFrames` (mode keys). rank 1 is the nearest; the playhead's own
|
|
54
|
+
* frame never gets one.
|
|
55
|
+
*/
|
|
56
|
+
export function ghostFrames({ frame, last, mode, count, step, keyFrames = [] }) {
|
|
57
|
+
if (mode === "off" || !(last >= 0)) return [];
|
|
58
|
+
const at = Math.max(0, Math.min(last, Math.round(frame) || 0)), out = [];
|
|
59
|
+
const keys = mode === "keys" ? [...new Set(keyFrames.map(Math.round))].filter((f) => f >= 0 && f <= last).sort((x, y) => x - y) : null;
|
|
60
|
+
const before = keys ? keys.filter((f) => f < at).reverse() : null, after = keys ? keys.filter((f) => f > at) : null;
|
|
61
|
+
for (let rank = 1; rank <= count; rank++) {
|
|
62
|
+
const early = keys ? before[rank - 1] : at - rank * step, late = keys ? after[rank - 1] : at + rank * step;
|
|
63
|
+
if (early >= 0) out.push({ f: early, side: "before", rank });
|
|
64
|
+
if (late <= last) out.push({ f: late, side: "after", rank });
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Colour and opacity of a ghost: its side's hue, fading with its distance in time. */
|
|
70
|
+
export function ghostLook(side, rank, count) {
|
|
71
|
+
const t = count > 1 ? (rank - 1) / (count - 1) : 0;
|
|
72
|
+
return { color: GHOST_COLORS[side], opacity: +(NEAR_OPACITY + (FAR_OPACITY - NEAR_OPACITY) * t).toFixed(3) };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const keyCache = new WeakMap();
|
|
76
|
+
/**
|
|
77
|
+
* The key frames of an installed take v3: its spec's keys with the skills it uses expanded
|
|
78
|
+
* (expandUse, lenient), the held-object keys and each generator's start, ascending. A retimed
|
|
79
|
+
* take (fix.retime) is not remapped: these are the spec's frames. [] for anything else.
|
|
80
|
+
*/
|
|
81
|
+
export function takeKeyFrames(take, objects = []) {
|
|
82
|
+
if (take?.schema !== "cozyclay.take.v3" || !take.spec) return [];
|
|
83
|
+
if (keyCache.has(take)) return keyCache.get(take);
|
|
84
|
+
let spec = take.spec;
|
|
85
|
+
try {
|
|
86
|
+
const sk = skeletonFor(take.rig);
|
|
87
|
+
if (sk && spec.use?.length) spec = expandUse(spec, { sk, scene: motionScene(objects), lenient: true }).spec;
|
|
88
|
+
} catch { spec = take.spec; }
|
|
89
|
+
const frames = [
|
|
90
|
+
...(spec.keys ?? []).map((k) => k?.f),
|
|
91
|
+
...Object.values(spec.objects ?? {}).flatMap((o) => (Array.isArray(o?.keys) ? o.keys.map((k) => k?.f) : [])),
|
|
92
|
+
...(spec.generators ?? []).map((g) => g?.f0),
|
|
93
|
+
].filter(Number.isFinite);
|
|
94
|
+
const keys = [...new Set(frames.map(Math.round))].sort((a, b) => a - b);
|
|
95
|
+
keyCache.set(take, keys);
|
|
96
|
+
return keys;
|
|
97
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// The viewport's onion-skin ghosts (ghost-set.js) of the active character's take v3. They live on
|
|
2
|
+
// GIZMO_LAYER: the working views draw them, while the shot preview card, PlayView, captures,
|
|
3
|
+
// contact sheets and exports never do. Off by default; view.setGhosts and the View menu's Ghosts
|
|
4
|
+
// group turn them on (view-ghosts.js holds that setting).
|
|
5
|
+
import { useEffect, useMemo } from "react";
|
|
6
|
+
import { useFrame, useThree } from "@react-three/fiber";
|
|
7
|
+
import { GIZMO_LAYER } from "./dualview.jsx";
|
|
8
|
+
import { GhostSet } from "./ghost-set.js";
|
|
9
|
+
import { isTakeV3 } from "./motion3/playback.js";
|
|
10
|
+
import { ghostFrames, takeKeyFrames, useGhostSettings } from "./view-ghosts.js";
|
|
11
|
+
|
|
12
|
+
// Kit parts mount in the same commit as a take or record change, sometimes a frame later: the
|
|
13
|
+
// carried meshes are collected again on the next frames too.
|
|
14
|
+
const SOURCE_PASSES = 3;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* rig: the active character's live rig; take: its installed motion; frame: the playhead;
|
|
18
|
+
* objects: the authored records; propTake: { frameCount, fps, objectTracks } as SetProps gets it.
|
|
19
|
+
*/
|
|
20
|
+
export function ViewGhosts({ rig, take, frame, objects, propTake }) {
|
|
21
|
+
const settings = useGhostSettings();
|
|
22
|
+
const scene = useThree((state) => state.scene), gl = useThree((state) => state.gl), invalidate = useThree((state) => state.invalidate);
|
|
23
|
+
const set = useMemo(() => new GhostSet({ layer: GIZMO_LAYER }), []);
|
|
24
|
+
const on = settings.mode !== "off" && !!rig && isTakeV3(take);
|
|
25
|
+
const keyFrames = useMemo(() => (on && settings.mode === "keys" ? takeKeyFrames(take, objects) : []), [on, settings.mode, take, objects]);
|
|
26
|
+
const entries = useMemo(() => (on ? ghostFrames({ frame, last: take.frameCount - 1, ...settings, keyFrames }) : []),
|
|
27
|
+
[on, frame, take, settings, keyFrames]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!on) return undefined;
|
|
30
|
+
scene.add(set.object);
|
|
31
|
+
return () => { scene.remove(set.object); };
|
|
32
|
+
}, [on, scene, set]);
|
|
33
|
+
useEffect(() => () => set.dispose(), [set]);
|
|
34
|
+
// The canvas may render on demand: a new ghost frame set is a reason to draw.
|
|
35
|
+
useEffect(() => { invalidate(); }, [invalidate, on, entries]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
// QA hook (test/qa-view-ghosts-browser.mjs): the live set and the renderer it draws with.
|
|
38
|
+
if (typeof window !== "undefined") window.__cozyclayGhosts = { set, gl, scene, entries };
|
|
39
|
+
}, [set, gl, scene, entries]);
|
|
40
|
+
useFrame(() => {
|
|
41
|
+
// A rig that left the stage (a hidden cast member) keeps no ghosts on it.
|
|
42
|
+
if (!set.show(on && !!rig.parent)) return;
|
|
43
|
+
set.setRig(rig);
|
|
44
|
+
set.syncSources(scene, objects, take, SOURCE_PASSES);
|
|
45
|
+
rig.parent.updateWorldMatrix(true, false);
|
|
46
|
+
set.update({ entries, take, placement: rig.parent.matrixWorld, propTake, objects, count: settings.count });
|
|
47
|
+
});
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
-
import { FiAlertTriangle, FiArrowUp, FiCheck, FiChevronRight, FiClock, FiDownload,
|
|
3
|
-
import {
|
|
4
|
-
ATTACHMENT_FAILED_NOTICE,
|
|
5
|
-
ATTACHMENT_LIMIT_NOTICE,
|
|
6
|
-
ATTACHMENT_TYPE_NOTICE,
|
|
7
|
-
attachmentFilesFromTransfer,
|
|
8
|
-
attachmentFromFile,
|
|
9
|
-
transferCarriesFiles,
|
|
10
|
-
} from "./attachment-image.js";
|
|
1
|
+
import { Fragment, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { FiAlertTriangle, FiArrowUp, FiCheck, FiChevronRight, FiClock, FiDownload, FiImage, FiMoreHorizontal, FiPaperclip, FiPlus, FiRotateCw, FiX } from "react-icons/fi";
|
|
3
|
+
import { ATTACHMENT_FAILED_NOTICE, ATTACHMENT_LIMIT_NOTICE, ATTACHMENT_TYPE_NOTICE, attachmentFilesFromTransfer, attachmentFromFile, transferCarriesFiles } from "./attachment-image.js";
|
|
11
4
|
import { isImeComposing } from "../ime.js";
|
|
12
5
|
import { useStudioShell } from "../shell/studio-shell-context.js";
|
|
13
6
|
import { ko } from "../locale.js";
|
|
@@ -31,10 +24,8 @@ import {
|
|
|
31
24
|
clampPanelWidth,
|
|
32
25
|
createAgentTransport,
|
|
33
26
|
panelPresentation,
|
|
34
|
-
providerEnvLabel,
|
|
35
27
|
requestHostImageAction,
|
|
36
|
-
|
|
37
|
-
formatElapsed,
|
|
28
|
+
formatTurnClock,
|
|
38
29
|
formatResetIn,
|
|
39
30
|
readStoredPanelWidth,
|
|
40
31
|
readStoredSupervisor,
|
|
@@ -47,6 +38,11 @@ import {
|
|
|
47
38
|
supervisorFollowUp,
|
|
48
39
|
} from "./agent-client.js";
|
|
49
40
|
import "./agent-panel.css";
|
|
41
|
+
import { AgentProgressCard } from "./AgentProgressCard.jsx";
|
|
42
|
+
import { LoginPanel } from "./LoginPanel.jsx";
|
|
43
|
+
import { TurnSteps } from "./AgentTurnSteps.jsx";
|
|
44
|
+
import { ReplyText } from "./AgentReplyText.jsx";
|
|
45
|
+
import { stepLabel, turnLayout } from "./agent-turn.js";
|
|
50
46
|
|
|
51
47
|
function relativeTime(value, now = Date.now()) {
|
|
52
48
|
const at = Date.parse(value || "");
|
|
@@ -65,32 +61,6 @@ function StatusDot({ tone, title }) {
|
|
|
65
61
|
return <span className={`agent-status-dot ${tone}`} title={title} aria-hidden="true" />;
|
|
66
62
|
}
|
|
67
63
|
|
|
68
|
-
function ToolCallCard({ call, presentation, onRetry }) {
|
|
69
|
-
const tone = call.status === "running" ? "busy" : ["failed", "cancelled"].includes(call.status) ? "alert" : "ok";
|
|
70
|
-
// The badge marks a call that edits the host surface, so it is drawn exactly
|
|
71
|
-
// for the families that surface names.
|
|
72
|
-
const hostTool = Object.hasOwn(presentation.toolLabels, call.name);
|
|
73
|
-
const detail = call.error ? `error: ${call.error}` : JSON.stringify(call.result ?? call.args ?? {}, null, 2);
|
|
74
|
-
return <div className={`agent-card agent-tool-card${call.status === "failed" ? " failed" : ""}`} data-tool-status={call.status} data-tool-name={call.name}>
|
|
75
|
-
<details>
|
|
76
|
-
<summary>
|
|
77
|
-
<StatusDot tone={tone} title={call.status} />
|
|
78
|
-
<span className="agent-tool-label">{resolveToolLabel(call, presentation.toolLabels)}</span>{hostTool && <span className="agent-tool-badge">{presentation.toolBadge}</span>}
|
|
79
|
-
<span className="agent-tool-elapsed">{call.status === "running" ? "running…" : call.status === "cancelled" ? (call.result?.status === "not_applied" ? "not applied" : "result unknown") : formatElapsed(call.elapsedMs)}</span>
|
|
80
|
-
<FiChevronRight size={12} aria-hidden="true" />
|
|
81
|
-
</summary>
|
|
82
|
-
<pre className="agent-tool-detail">{detail}</pre>
|
|
83
|
-
</details>
|
|
84
|
-
{call.failure && <div className="agent-error" role="alert">
|
|
85
|
-
<span>{call.failure.message || ERROR_COPY[call.failure.code] || "The turn failed."}</span>
|
|
86
|
-
<div className="agent-error-actions">
|
|
87
|
-
<button type="button" className="agent-ghost-button agent-error-retry" onClick={onRetry}>Retry</button>
|
|
88
|
-
<button type="button" className="agent-ghost-button agent-error-details" onClick={(event) => { const card = event.currentTarget.closest(".agent-tool-card"); const details = card?.querySelector("details"); if (details) details.open = !details.open; }}>Details</button>
|
|
89
|
-
</div>
|
|
90
|
-
</div>}
|
|
91
|
-
</div>;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
64
|
function ImageResultCard({ image, onUse, onUndo, onRegenerate, onOpen }) {
|
|
95
65
|
const applying = image.apply?.status === "applying";
|
|
96
66
|
const failed = image.apply?.status === "failed";
|
|
@@ -250,69 +220,15 @@ const RECOVERY_COPY = {
|
|
|
250
220
|
new_intent: "Tell the agent what to do with the changed target.",
|
|
251
221
|
reconcile: "The result is unknown; reconcile before changing this target.",
|
|
252
222
|
sign_in: "Sign in again to continue.",
|
|
223
|
+
fix_key: "Log in again under Connect a model, then retry.",
|
|
253
224
|
};
|
|
254
225
|
|
|
255
|
-
function FailureCard({ failure, onRetry }) {
|
|
226
|
+
function FailureCard({ failure, onRetry, onFixKey }) {
|
|
256
227
|
return <div className="agent-card agent-failure-card" data-failure-code={failure.code} role="alert">
|
|
257
228
|
<div className="agent-failure-head"><FiAlertTriangle size={12} aria-hidden="true" /><span className="agent-failure-code">{failure.code}</span></div>
|
|
258
229
|
<p className="agent-failure-message">{failure.message || "The command did not complete."}</p>
|
|
259
230
|
<p className="agent-failure-recovery">{RECOVERY_COPY[failure.recovery?.action] || RECOVERY_COPY.none}{failure.preserved?.authoredState === "unchanged" ? " Nothing in the scene changed." : failure.preserved?.authoredState === "unknown" ? " Whether the scene changed is unknown." : ""}</p>
|
|
260
|
-
{failure.recovery?.retryAllowed && <div className="agent-error-actions"
|
|
261
|
-
</div>;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// One provider's credential row. The key lives in this component and nowhere
|
|
265
|
-
// else: it is typed into a password field, handed to the transport once and
|
|
266
|
-
// dropped the moment the sidecar accepts it, so no store, no transcript and no
|
|
267
|
-
// screenshot can ever carry it. The sidecar answers with the SOURCE of a key
|
|
268
|
-
// (env / file), never with a key, which is what the row reports.
|
|
269
|
-
function ProviderKeyRow({ provider, onSave, onRemove }) {
|
|
270
|
-
const [draft, setDraft] = useState("");
|
|
271
|
-
const [busy, setBusy] = useState(false);
|
|
272
|
-
const [error, setError] = useState("");
|
|
273
|
-
const fromEnv = provider.authSource === "env";
|
|
274
|
-
const fromFile = provider.authSource === "file";
|
|
275
|
-
const envLabel = `set by ${providerEnvLabel(provider.id)}`;
|
|
276
|
-
const inputId = `agent-key-${provider.id}`;
|
|
277
|
-
const run = async (action) => {
|
|
278
|
-
setBusy(true);
|
|
279
|
-
setError("");
|
|
280
|
-
try {
|
|
281
|
-
await action();
|
|
282
|
-
// The key is gone from the page the instant it is stored.
|
|
283
|
-
setDraft("");
|
|
284
|
-
} catch (failure) {
|
|
285
|
-
// The sidecar's own refusal, which never quotes key material.
|
|
286
|
-
setError(failure?.message || "The sidecar refused that key.");
|
|
287
|
-
} finally {
|
|
288
|
-
setBusy(false);
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
return <div className="agent-key-row" data-provider={provider.id} data-provider-source={provider.authSource || "none"} data-provider-signed-in={provider.signedIn ? "true" : "false"}>
|
|
292
|
-
<div className="agent-key-head">
|
|
293
|
-
<StatusDot tone={provider.signedIn ? "ok" : ""} title={provider.signedIn ? "key configured" : "no key"} />
|
|
294
|
-
<label className="agent-key-label" htmlFor={inputId}>{provider.label}</label>
|
|
295
|
-
{/* The head states WHERE the key lives in two words; the variable's own
|
|
296
|
-
name belongs on the input it replaces, where there is room for it. */}
|
|
297
|
-
<span className="agent-key-source">{fromEnv ? "environment" : fromFile ? "saved on this machine" : "no key"}</span>
|
|
298
|
-
</div>
|
|
299
|
-
<div className="agent-key-controls">
|
|
300
|
-
<input
|
|
301
|
-
id={inputId}
|
|
302
|
-
className="agent-key-input"
|
|
303
|
-
type="password"
|
|
304
|
-
autoComplete="off"
|
|
305
|
-
spellCheck="false"
|
|
306
|
-
aria-label={`${provider.label} API key`}
|
|
307
|
-
placeholder={fromEnv ? envLabel : provider.signedIn ? "Replace key" : "Paste API key"}
|
|
308
|
-
disabled={fromEnv || busy}
|
|
309
|
-
value={draft}
|
|
310
|
-
onChange={(event) => { setDraft(event.target.value); setError(""); }}
|
|
311
|
-
/>
|
|
312
|
-
<button type="button" className="agent-key-save" disabled={fromEnv || busy || !draft.trim()} onClick={() => run(() => onSave(provider.id, draft))}>Save</button>
|
|
313
|
-
{fromFile && <button type="button" className="agent-ghost-button agent-key-remove" disabled={busy} onClick={() => run(() => onRemove(provider.id))}>Remove</button>}
|
|
314
|
-
</div>
|
|
315
|
-
{error && <p className="agent-key-error" role="alert">{error}</p>}
|
|
231
|
+
{failure.recovery?.retryAllowed && <div className="agent-error-actions">{failure.recovery.action === "fix_key" && onFixKey && <button type="button" className="agent-ghost-button agent-error-keys" onClick={onFixKey}>Log in again</button>}<button type="button" className="agent-ghost-button agent-error-retry" onClick={onRetry}>Retry</button></div>}
|
|
316
232
|
</div>;
|
|
317
233
|
}
|
|
318
234
|
|
|
@@ -336,7 +252,6 @@ export default function AgentPanel({
|
|
|
336
252
|
surface = "workflow",
|
|
337
253
|
buildContext = null,
|
|
338
254
|
onImageAction = null,
|
|
339
|
-
onI2vAction = null,
|
|
340
255
|
onReceipt = null,
|
|
341
256
|
}) {
|
|
342
257
|
// What this host shows around the shared conversation: labels, chips, the
|
|
@@ -405,10 +320,8 @@ export default function AgentPanel({
|
|
|
405
320
|
const [lightbox, setLightbox] = useState(null);
|
|
406
321
|
const [overlay, setOverlay] = useState(() => !embedded && (globalThis.innerWidth || 1440) < AGENT_PANEL_OVERLAY_BREAKPOINT);
|
|
407
322
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
408
|
-
//
|
|
323
|
+
// Connect a model (pi's login list, LoginPanel.jsx) reads its own list from the sidecar while it is open.
|
|
409
324
|
const [keysOpen, setKeysOpen] = useState(false);
|
|
410
|
-
const [providers, setProviders] = useState([]);
|
|
411
|
-
const [providersState, setProvidersState] = useState("loading");
|
|
412
325
|
const [historySessions, setHistorySessions] = useState([]);
|
|
413
326
|
const [restoreNotice, setRestoreNotice] = useState("");
|
|
414
327
|
// Why the last steer did not reach the running turn. It reads in the same
|
|
@@ -427,8 +340,6 @@ export default function AgentPanel({
|
|
|
427
340
|
buildContextRef.current = buildContext;
|
|
428
341
|
const imageActionRef = useRef(onImageAction);
|
|
429
342
|
imageActionRef.current = onImageAction;
|
|
430
|
-
const i2vActionRef = useRef(onI2vAction);
|
|
431
|
-
i2vActionRef.current = onI2vAction;
|
|
432
343
|
const receiptRef = useRef(onReceipt);
|
|
433
344
|
receiptRef.current = onReceipt;
|
|
434
345
|
|
|
@@ -479,10 +390,15 @@ export default function AgentPanel({
|
|
|
479
390
|
// is live and holding the turn's outcome for a few seconds after it ends.
|
|
480
391
|
const [activityNow, setActivityNow] = useState(() => Date.now());
|
|
481
392
|
const activity = model
|
|
482
|
-
? describeActivity(chat, { now: activityNow, toolLabel: (call) =>
|
|
393
|
+
? describeActivity(chat, { now: activityNow, toolLabel: (call) => stepLabel(call, presentation.toolLabels) })
|
|
483
394
|
: modelsState === "failed"
|
|
484
395
|
? { phase: "no-model", kind: "terminal", tone: "alert", text: "No model available — the agent service did not answer with one", ticking: false }
|
|
485
396
|
: { phase: "models", kind: "live", tone: "busy", text: "Loading models…", ticking: false };
|
|
397
|
+
// The transcript grouped per turn, and the motion progress card (above the latest turn's steps, so the live
|
|
398
|
+
// step stays at the bottom where the transcript scrolls) limited to that turn's drafts.
|
|
399
|
+
const layout = useMemo(() => turnLayout(items, { streaming }), [items, streaming]);
|
|
400
|
+
const turnSince = streaming ? chat.turnStartedAt : chat.lastTurn ? chat.lastTurn.endedAt - chat.lastTurn.durationMs : null;
|
|
401
|
+
const progressCard = <AgentProgressCard items={items} since={turnSince} live={streaming} />;
|
|
486
402
|
useEffect(() => {
|
|
487
403
|
if (!activity.ticking) return;
|
|
488
404
|
const timer = setInterval(() => setActivityNow(Date.now()), 500);
|
|
@@ -510,21 +426,6 @@ export default function AgentPanel({
|
|
|
510
426
|
return null;
|
|
511
427
|
}
|
|
512
428
|
}, [sessionState, transport]);
|
|
513
|
-
// The sidecar owns the credentials; this reads which providers have one and
|
|
514
|
-
// where it came from. The list is read when the keys section opens and again
|
|
515
|
-
// after every write — a key that just landed (or left) changes which models
|
|
516
|
-
// the composer may offer, so the model list is re-read with it.
|
|
517
|
-
const readProviders = useCallback(async () => {
|
|
518
|
-
try {
|
|
519
|
-
const list = await transport.providers?.();
|
|
520
|
-
if (!Array.isArray(list)) throw new Error("no provider list");
|
|
521
|
-
setProviders(list);
|
|
522
|
-
setProvidersState("ready");
|
|
523
|
-
} catch {
|
|
524
|
-
setProviders([]);
|
|
525
|
-
setProvidersState("failed");
|
|
526
|
-
}
|
|
527
|
-
}, [transport]);
|
|
528
429
|
// Everything an authentication change moves, read back once: WHO the session
|
|
529
430
|
// is (readiness is `signedIn || providersConfigured > 0`), which providers
|
|
530
431
|
// hold a credential, and WHICH models this session may therefore send to. A
|
|
@@ -535,13 +436,11 @@ export default function AgentPanel({
|
|
|
535
436
|
// into a composer without reloading the page, and what takes it away again
|
|
536
437
|
// when the last way in goes. No timer is involved.
|
|
537
438
|
const refreshProviderState = useCallback(async () => {
|
|
538
|
-
|
|
539
|
-
await readProviders();
|
|
540
|
-
await session;
|
|
439
|
+
await readAccount();
|
|
541
440
|
try {
|
|
542
441
|
applyModelList(await transport.models());
|
|
543
442
|
} catch { /* the composer keeps the list it was last advertised */ }
|
|
544
|
-
}, [applyModelList, readAccount,
|
|
443
|
+
}, [applyModelList, readAccount, transport]);
|
|
545
444
|
|
|
546
445
|
useEffect(() => {
|
|
547
446
|
let cancelled = false;
|
|
@@ -798,20 +697,13 @@ export default function AgentPanel({
|
|
|
798
697
|
// refresh above, because a key changes the session, not just a row.
|
|
799
698
|
const toggleProviderKeys = useCallback(() => {
|
|
800
699
|
setMenuOpen(false);
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
await transport.setProviderKey(id, key);
|
|
809
|
-
await refreshProviderState();
|
|
810
|
-
}, [refreshProviderState, transport]);
|
|
811
|
-
const removeProviderKey = useCallback(async (id) => {
|
|
812
|
-
await transport.removeProviderKey(id);
|
|
813
|
-
await refreshProviderState();
|
|
814
|
-
}, [refreshProviderState, transport]);
|
|
700
|
+
setKeysOpen(!keysOpen);
|
|
701
|
+
}, [keysOpen]);
|
|
702
|
+
// Opens (never toggles) Connect a model, pi's login list: the signed-out card and a refused key both lead here.
|
|
703
|
+
const openProviderKeys = useCallback(() => {
|
|
704
|
+
setMenuOpen(false);
|
|
705
|
+
setKeysOpen(true);
|
|
706
|
+
}, []);
|
|
815
707
|
|
|
816
708
|
// Clearing the transcript also retires the session, so what the author sees
|
|
817
709
|
// and what the model remembers cannot diverge.
|
|
@@ -927,8 +819,8 @@ export default function AgentPanel({
|
|
|
927
819
|
{resetLabel && <span className="agent-account-reset">resets in {resetLabel}</span>}
|
|
928
820
|
</div>}
|
|
929
821
|
<button type="button" role="menuitem" onClick={clearContext}>Clear context</button>
|
|
930
|
-
<button type="button" role="menuitem" className="agent-menu-keys" aria-expanded={keysOpen} onClick={toggleProviderKeys}>
|
|
931
|
-
<button type="button" role="menuitem" onClick={signOut}>Sign out</button>
|
|
822
|
+
<button type="button" role="menuitem" className="agent-menu-keys" aria-expanded={keysOpen} onClick={toggleProviderKeys}>Connect a model…</button>
|
|
823
|
+
{account?.signedIn && <button type="button" role="menuitem" onClick={signOut}>Sign out</button>}
|
|
932
824
|
</div>}
|
|
933
825
|
</span>
|
|
934
826
|
{!embedded && <button type="button" className="agent-icon-button agent-collapse" onClick={() => setCollapsed(true)} aria-label="Collapse agent panel" title="Collapse agent panel (Cmd/Ctrl+B)"><FiChevronRight size={13} /></button>}
|
|
@@ -939,28 +831,21 @@ export default function AgentPanel({
|
|
|
939
831
|
|
|
940
832
|
{/* An inline section, not a modal: the panel stays the one place the
|
|
941
833
|
conversation and the credentials it runs on are managed. */}
|
|
942
|
-
{keysOpen && <
|
|
943
|
-
<div className="agent-keys-head">
|
|
944
|
-
<h3 className="agent-keys-title">Provider keys</h3>
|
|
945
|
-
<button type="button" className="agent-icon-button agent-keys-close" aria-label="Close provider keys" onClick={() => setKeysOpen(false)}><FiX size={11} aria-hidden="true" /></button>
|
|
946
|
-
</div>
|
|
947
|
-
<p className="agent-keys-hint">Keys are stored by the local CozyClay service and are never shown again.</p>
|
|
948
|
-
{providersState === "loading" && <p className="agent-keys-note">Loading providers…</p>}
|
|
949
|
-
{providersState === "failed" && <p className="agent-keys-note alert" role="alert">The provider list is unavailable.</p>}
|
|
950
|
-
{providers.filter((entry) => entry.id !== "openai-codex").map((entry) => <ProviderKeyRow key={entry.id} provider={entry} onSave={saveProviderKey} onRemove={removeProviderKey} />)}
|
|
951
|
-
</section>}
|
|
834
|
+
{keysOpen && <LoginPanel onClose={() => setKeysOpen(false)} onChatGPT={signIn} onChanged={refreshProviderState} />}
|
|
952
835
|
|
|
953
836
|
<div className="agent-transcript" ref={transcriptRef} aria-live="polite" aria-label="Conversation" data-agent-transcript="true">
|
|
954
837
|
{authState === "signed-out" && <div className="agent-state-card" data-agent-card="signed-out">
|
|
955
|
-
<h3>
|
|
956
|
-
<p>
|
|
838
|
+
<h3>Connect a model</h3>
|
|
839
|
+
<p>Sign in with ChatGPT, or log in to another provider with a subscription, an API key, or your own proxy server. Sign-in happens in your browser — no token is ever stored in this page.</p>
|
|
957
840
|
<button type="button" className="agent-primary-button agent-signin" onClick={signIn}>Sign in with ChatGPT</button>
|
|
841
|
+
<button type="button" className="agent-ghost-button agent-use-key" onClick={openProviderKeys}>Log in with another provider</button>
|
|
958
842
|
</div>}
|
|
959
843
|
|
|
960
844
|
{authState === "signing-in" && <div className="agent-state-card" data-agent-card="signing-in">
|
|
961
845
|
<span className="agent-spinner" aria-hidden="true" />
|
|
962
846
|
<h3>Waiting for your browser…</h3>
|
|
963
|
-
<p>Finish the ChatGPT sign-in in the
|
|
847
|
+
<p>Finish the ChatGPT sign-in in the browser page that just opened. This panel picks up the session when you come back.</p>
|
|
848
|
+
{account?.authorizeUrl && <a className="agent-ghost-button agent-signin-link" href={account.authorizeUrl} target="_blank" rel="noopener noreferrer">Open the sign-in page again</a>}
|
|
964
849
|
</div>}
|
|
965
850
|
|
|
966
851
|
{authState === "no-entitlement" && <div className="agent-state-card" data-agent-card="no-entitlement">
|
|
@@ -977,22 +862,28 @@ export default function AgentPanel({
|
|
|
977
862
|
</div>
|
|
978
863
|
</div>}
|
|
979
864
|
|
|
980
|
-
{
|
|
865
|
+
{/* One step list per turn (AgentTurnSteps.jsx); the motion progress card sits just above the latest one. */}
|
|
866
|
+
{layout.map((entry) => {
|
|
867
|
+
if (entry.type === "steps") return <Fragment key={entry.id}>
|
|
868
|
+
{entry.last && progressCard}
|
|
869
|
+
<div className="agent-row"><TurnSteps block={entry} now={activityNow} toolLabels={presentation.toolLabels} formatClock={formatTurnClock} onRetry={() => runTurn(chat.lastPrompt)} /></div>
|
|
870
|
+
</Fragment>;
|
|
871
|
+
const { item } = entry;
|
|
981
872
|
if (item.kind === "user") return <div className="agent-row user" key={item.id}>
|
|
982
873
|
{item.attachments?.length > 0 && <div className="agent-bubble-attachments">
|
|
983
874
|
{item.attachments.map((attachment, index) => <img key={`${item.id}-${index}`} src={attachment.dataUrl} alt={attachment.name || "Attached image"} onClick={() => setLightbox({ dataUrl: attachment.dataUrl, prompt: attachment.name })} />)}
|
|
984
875
|
</div>}
|
|
985
876
|
<div className="agent-bubble">{item.text}</div>
|
|
986
877
|
</div>;
|
|
987
|
-
if (item.kind === "assistant") return <div className="agent-row assistant" key={item.id}><div className="agent-assistant-text"
|
|
988
|
-
if (item.kind === "tool") return <div className="agent-row" key={item.id}><ToolCallCard call={item} presentation={presentation} onRetry={() => runTurn(chat.lastPrompt)} /></div>;
|
|
878
|
+
if (item.kind === "assistant") return <div className="agent-row assistant" key={item.id}><div className="agent-assistant-text"><ReplyText text={item.text} tail={streaming && <span className="agent-caret">▌</span>} /></div></div>;
|
|
989
879
|
if (item.kind === "job") return <div className="agent-row" key={item.id}><JobCard job={item} onStop={stopTurn} onAccept={(job) => store.acceptJob(job.jobId)} /></div>;
|
|
990
880
|
if (item.kind === "receipt") return <div className="agent-row" key={item.id}><ReceiptCard item={item} /></div>;
|
|
991
|
-
if (item.kind === "failure") return <div className="agent-row" key={item.id}><FailureCard failure={item.failure} onRetry={() => runTurn(chat.lastPrompt)} /></div>;
|
|
881
|
+
if (item.kind === "failure") return <div className="agent-row" key={item.id}><FailureCard failure={item.failure} onRetry={() => runTurn(chat.lastPrompt)} onFixKey={openProviderKeys} /></div>;
|
|
992
882
|
if (item.kind === "supervisor") return <div className="agent-row" key={item.id}><SupervisorCard note={item} onSend={(text) => { store.setDraft(text); runTurn(text); }} /></div>;
|
|
993
883
|
return <div className="agent-row" key={item.id}><ImageResultCard image={item} onUse={(image) => store.applyImage(image.id)} onUndo={(image) => store.undoImage(image.id)} onRegenerate={() => runTurn(chat.lastPrompt)} onOpen={setLightbox} /></div>;
|
|
994
884
|
})}
|
|
995
885
|
|
|
886
|
+
{!layout.some((entry) => entry.type === "steps" && entry.last) && progressCard}
|
|
996
887
|
{rateLimit && <PausedCard resetAt={rateLimit.resetAt} onRetry={() => { store.clearRateLimit(); runTurn(chat.lastPrompt); }} onSwitchModel={switchModel} />}
|
|
997
888
|
</div>
|
|
998
889
|
|
|
@@ -1001,6 +892,7 @@ export default function AgentPanel({
|
|
|
1001
892
|
{authenticated && <div className="agent-activity" data-agent-activity={activity.phase} data-agent-activity-kind={activity.kind} role="status" aria-live="polite">
|
|
1002
893
|
<span className={`agent-activity-indicator ${activity.tone}`} aria-hidden="true" />
|
|
1003
894
|
<span className="agent-activity-text">{activity.text}</span>
|
|
895
|
+
{activity.detail && <span className="agent-activity-detail">{activity.detail}</span>}
|
|
1004
896
|
</div>}
|
|
1005
897
|
|
|
1006
898
|
{authenticated && <div
|
|
@@ -1062,17 +954,6 @@ export default function AgentPanel({
|
|
|
1062
954
|
</button>
|
|
1063
955
|
)}
|
|
1064
956
|
<span className="agent-composer-spacer" aria-hidden="true" />
|
|
1065
|
-
{surface === "studio" && i2vActionRef.current && <button
|
|
1066
|
-
type="button"
|
|
1067
|
-
className="agent-attach-chip agent-i2v-action"
|
|
1068
|
-
data-testid="i2v-agent-action"
|
|
1069
|
-
aria-label="Generate motion"
|
|
1070
|
-
title="Generate motion from this message"
|
|
1071
|
-
disabled={composerDisabled || !draft.trim() || streaming}
|
|
1072
|
-
onClick={() => { const instruction = draft.trim(); store.setDraft(""); i2vActionRef.current?.(instruction); }}
|
|
1073
|
-
>
|
|
1074
|
-
<FiFilm size={13} aria-hidden="true" />
|
|
1075
|
-
</button>}
|
|
1076
957
|
{/* While a turn runs, Stop is still the way out of it; on a surface
|
|
1077
958
|
that can be steered, Send becomes the way INTO it. */}
|
|
1078
959
|
{streaming
|