cozyclay 1.9.0 → 2.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 +171 -0
- package/bin/agent/agent-routes.mjs +74 -70
- package/bin/agent/agent-runner.mjs +10 -3
- package/bin/agent/motion-runtime.mjs +32 -260
- package/bin/agent/providers.mjs +46 -3
- package/bin/agent/session-store.mjs +41 -5
- package/bin/agent/studio-prompt.mjs +1 -1
- package/bin/agent/studio-tools.mjs +48 -11
- package/bin/cozyclay.mjs +26 -8
- package/bin/live/cli.mjs +22 -3
- package/bin/mcp-runtime.mjs +35 -20
- package/bin/telemetry-state.mjs +5 -2
- package/bin/update-check.mjs +15 -0
- package/dist/app/index.html +6 -6
- package/dist/assets/analytics-MVZOEW8O.js +1 -0
- package/dist/assets/app-C1sdVbg4.css +1 -0
- package/dist/assets/app-CA55tcI3.js +4876 -0
- package/dist/assets/{demo-B-eg1CT8.js → demo-ByeYp-dY.js} +1 -1
- package/dist/assets/{first-shot-handoff-CGgnvNu8.js → first-shot-handoff-DgT28ZiV.js} +1 -1
- package/dist/assets/{landing-IbRp3FnQ.js → landing-BaXkpFip.js} +1 -1
- package/dist/assets/shot-prompt-CbqWcWYi.css +1 -0
- package/dist/assets/shot-prompt-Yrt99wZx.js +17 -0
- package/dist/assets/{ticket-BgIzOBOf.js → ticket-BcmsnHn5.js} +1 -1
- package/dist/assets/workflow-DRdwoDBT.css +1 -0
- package/dist/assets/workflow-dNPGY9xz.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/fonts/IBM-Plex-Sans-OFL.txt +92 -0
- package/dist/fonts/JetBrains-Mono-OFL.txt +93 -0
- package/dist/fonts/README.md +13 -9
- package/dist/fonts/ibm-plex-sans-400-latin.woff2 +0 -0
- package/dist/fonts/ibm-plex-sans-500-latin.woff2 +0 -0
- package/dist/fonts/ibm-plex-sans-600-latin.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-400-latin.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-500-latin.woff2 +0 -0
- package/dist/index.html +7 -7
- package/dist/privacy/index.html +13 -8
- package/dist/sitemap.xml +6 -6
- package/dist/workflow/index.html +5 -5
- package/mcp/LIVE-PROTOCOL.md +1 -1
- package/mcp/live-hub.mjs +35 -93
- package/mcp/mesh-file.mjs +48 -0
- package/mcp/server.mjs +6 -77
- package/mcp/tool-handlers.mjs +224 -377
- package/package.json +2 -1
- package/src/App.jsx +1273 -9075
- package/src/analytics.js +393 -9
- package/src/app-context.js +211 -0
- package/src/app-stage.jsx +25 -24
- package/src/ardy/auto-fix-panel.css +108 -0
- package/src/ardy/collision-blockers.js +9 -3
- package/src/ardy/fix-collisions.js +10 -66
- package/src/ardy/ground.js +4 -4
- package/src/ardy/ik-drag.js +101 -0
- package/src/ardy/ik-key-json.js +43 -0
- package/src/ardy/ik.js +122 -76
- package/src/ardy/physics-panel.css +2 -0
- package/src/ardy/physics-panel.jsx +17 -5
- package/src/ardy/platform-fit-panel.css +64 -0
- package/src/ardy/platform-fit-panel.jsx +40 -0
- package/src/ardy/platform-fit.js +326 -0
- package/src/ardy/playback.js +166 -20
- package/src/ardy/range-pin.js +299 -0
- package/src/ardy/timeline-coordinates.js +26 -0
- package/src/ardy/timeline.css +1199 -0
- package/src/ardy/timeline.jsx +149 -41
- package/src/ardy/waypoints.js +2 -2
- package/src/asset-pane.css +462 -0
- package/src/asset-pane.jsx +461 -232
- package/src/command-bus.js +349 -0
- package/src/commands/ai.js +31 -0
- package/src/commands/cast.js +140 -0
- package/src/commands/elements/character.js +13 -0
- package/src/commands/elements/motion.js +14 -0
- package/src/commands/elements/object.js +22 -0
- package/src/commands/elements/scene.js +16 -0
- package/src/commands/elements/shot.js +9 -0
- package/src/commands/elements/stage.js +11 -0
- package/src/commands/elements.js +155 -0
- package/src/commands/export.js +20 -0
- package/src/commands/index.js +40 -0
- package/src/commands/motion.js +231 -0
- package/src/commands/objects.js +174 -0
- package/src/commands/project.js +53 -0
- package/src/commands/scene.js +81 -0
- package/src/commands/shared.js +19 -0
- package/src/commands/shot.js +103 -0
- package/src/commands/stage.js +17 -0
- package/src/commands/view.js +42 -0
- package/src/document-store.js +144 -0
- package/src/domains/cast.js +1007 -0
- package/src/domains/motion.js +3219 -0
- package/src/domains/objects.js +932 -0
- package/src/domains/scenes.js +817 -0
- package/src/domains/shots.js +404 -0
- package/src/domains/stage.js +93 -0
- package/src/dualview.jsx +113 -57
- package/src/facing-marks.js +3 -0
- package/src/first-success-guide.jsx +14 -14
- package/src/grid-view.js +18 -5
- package/src/hierarchy-model.js +24 -16
- package/src/hierarchy-panel.css +386 -0
- package/src/hierarchy-panel.jsx +150 -21
- package/src/{fal-motion-client.js → i2v-motion-client.js} +13 -13
- package/src/i2v-motion-studio.jsx +180 -0
- package/src/ik-camera.js +3 -0
- package/src/main.jsx +6 -0
- package/src/motion/generation.js +105 -0
- package/src/motion-readiness-ui.jsx +4 -4
- package/src/motion-readiness.js +11 -0
- package/src/motion-trail.js +366 -9
- package/src/object-gizmo.jsx +7 -1
- package/src/otio.js +2 -1
- package/src/panels/CameraPanel.jsx +60 -0
- package/src/panels/CharacterTransformPanel.jsx +48 -0
- package/src/panels/EnvironmentPanel.jsx +40 -0
- package/src/panels/Foldout.jsx +32 -0
- package/src/panels/LightPanel.jsx +24 -0
- package/src/panels/ObjectTransformPanel.jsx +554 -0
- package/src/panels/PosePanel.jsx +97 -0
- package/src/panels/ProjectPanel.jsx +33 -0
- package/src/panels/PromptBlocksPanel.jsx +442 -0
- package/src/panels/PropsPanel.jsx +70 -0
- package/src/panels/ReferenceImageField.jsx +91 -0
- package/src/panels/RigControlPanel.jsx +78 -0
- package/src/panels/RigPanel.jsx +37 -0
- package/src/panels/SubjectBox.jsx +47 -0
- package/src/panels/SubjectsPanel.jsx +39 -0
- package/src/panels/VideoCapturePanel.jsx +180 -0
- package/src/panels/details.css +1171 -0
- package/src/panels/motion.css +197 -0
- package/src/panels/pose.css +39 -0
- package/src/planview.jsx +60 -31
- package/src/posestudio.jsx +59 -4
- package/src/project-browser.css +1073 -0
- package/src/project-browser.jsx +284 -123
- package/src/range-pin-object-transform.js +19 -0
- package/src/range-pin-panel.css +518 -0
- package/src/range-pin-panel.jsx +344 -0
- package/src/result-modal.jsx +5 -5
- package/src/room.jsx +79 -51
- package/src/scene-objects.js +37 -1
- package/src/scenes.js +5 -0
- package/src/semantic-edit.js +2 -0
- package/src/settings-menu.jsx +39 -105
- package/src/shell/BottomDock.jsx +450 -0
- package/src/shell/DetailsSlot.jsx +513 -0
- package/src/shell/LibrarySlot.jsx +86 -0
- package/src/shell/MenuBar.jsx +601 -0
- package/src/shell/OutlinerSlot.jsx +50 -0
- package/src/shell/PreferencesDialog.jsx +538 -0
- package/src/shell/PreferencesSlot.jsx +40 -0
- package/src/shell/StatusBar.jsx +125 -0
- package/src/shell/StudioShell.jsx +92 -0
- package/src/shell/TopBar.jsx +129 -0
- package/src/shell/ViewportToolbar.jsx +656 -0
- package/src/shell/agent-glass.css +296 -0
- package/src/shell/dock.css +93 -0
- package/src/shell/glass-regions.css +985 -0
- package/src/shell/glass.css +687 -0
- package/src/shell/log-store.js +52 -0
- package/src/shell/mode.css +9 -0
- package/src/shell/preferences.css +651 -0
- package/src/shell/shell.css +282 -0
- package/src/shell/studio-shell-context.js +19 -0
- package/src/shell/topbar.css +435 -0
- package/src/shell/viewport.css +451 -0
- package/src/store/authored-intent.js +22 -0
- package/src/store/runtime-adapters.js +92 -0
- package/src/store/scene-stage.js +21 -0
- package/src/store/use-document-store.js +14 -0
- package/src/studio-actions.js +210 -0
- package/src/studio-agent-commands.js +41 -271
- package/src/studio-agent-context.js +39 -10
- package/src/studio-agent-motion.js +122 -373
- package/src/studio-agent-protocol.js +67 -31
- package/src/studio-app-binding.js +379 -0
- package/src/studio-contact-sheet.js +73 -0
- package/src/studio-elements.js +58 -51
- package/src/styles/themes.css +200 -0
- package/src/styles/tokens.css +69 -0
- package/src/styles.css +460 -1742
- package/src/theme.js +44 -0
- package/src/timeline-extent.js +16 -0
- package/src/trail-key-conflicts.js +39 -0
- package/src/trail-pick.js +59 -0
- package/src/ui.jsx +8 -8
- package/src/use-case-question.jsx +66 -0
- package/src/workflow/AgentPanel.jsx +82 -69
- package/src/workflow/agent-client.js +12 -2
- package/src/workflow/agent-panel.css +34 -32
- package/src/workflow/cozy-scene-node.css +2 -0
- package/src/workflow/workflow.css +2 -0
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/ardy/bridge.mjs +164 -146
- package/tools/ardy/visual-qa.mjs +5 -5
- package/tools/bench/EXP3.md +61 -0
- package/tools/bench/cclay_bench_extract_incam.py +125 -0
- package/tools/bench/cclay_bench_extract_obs.py +204 -0
- package/tools/bench/cclay_bench_runner.py +42 -0
- package/tools/bench/cube-contact.mjs +162 -0
- package/tools/bench/exp3.mjs +152 -0
- package/tools/bench/extract-bench-lib.mjs +216 -0
- package/tools/bench/extract-bench.mjs +302 -0
- package/tools/bench/fal-generate.mjs +88 -0
- package/tools/bench/fit/README.md +189 -0
- package/tools/bench/fit/camera.mjs +59 -0
- package/tools/bench/fit/contact.mjs +419 -0
- package/tools/bench/fit/footlock.mjs +234 -0
- package/tools/bench/fit/motion.mjs +85 -0
- package/tools/bench/fit/pin.mjs +27 -0
- package/tools/bench/fit/remote.mjs +105 -0
- package/tools/bench/fit-bench.mjs +126 -0
- package/tools/bench/fit-sanity.mjs +45 -0
- package/tools/bench/metrics.mjs +313 -0
- package/tools/bench/obs/depth.mjs +258 -0
- package/tools/bench/obs/extrinsics.mjs +175 -0
- package/tools/bench/obs/fit_mannequin_betas.py +340 -0
- package/tools/bench/obs/ground.mjs +294 -0
- package/tools/bench/obs/heading.mjs +151 -0
- package/tools/bench/obs/ladder.mjs +555 -0
- package/tools/bench/obs/mannequin-betas.json +124 -0
- package/tools/bench/obs/remote.mjs +83 -0
- package/tools/bench/obs/rest_joints.py +52 -0
- package/tools/bench/obs/ybot-targets.mjs +49 -0
- package/tools/bench/obs-bench.mjs +520 -0
- package/tools/bench/score.mjs +343 -0
- package/tools/bench/summarize.mjs +86 -0
- package/tools/dev/pages/privacy.html +13 -8
- package/tools/gt-render/browser.mjs +159 -0
- package/tools/gt-render/camera-math.mjs +276 -0
- package/tools/gt-render/page.mjs +267 -0
- package/tools/gt-render/render.mjs +514 -0
- package/tools/gt-render/scene-box.mjs +31 -0
- package/tools/gt-render/take-transform.mjs +87 -0
- package/tools/morphgs/assets/gen_truth.py +39 -0
- package/tools/morphgs/assets/mesh_ori_rig.txt +27 -0
- package/tools/morphgs/assets/playback-check.mjs +30 -0
- package/tools/morphgs/demo-gate.sh +26 -0
- package/tools/morphgs/fbx2morphgs.mjs +68 -0
- package/tools/morphgs/morphgs-to-cskel27.mjs +105 -0
- package/tools/morphgs/patches/preprocess_src-none-mode.patch +76 -0
- package/tools/morphgs/setup-on-cluster.sh +126 -0
- package/tools/qa/css-rule-usage.mjs +283 -0
- package/tools/qa/studio-control-count.mjs +70 -32
- package/tools/run-tests.mjs +134 -12
- package/tools/track/DESIGN.md +513 -0
- package/tools/track/backfill-provenance.mjs +116 -0
- package/tools/track/budget.mjs +8 -0
- package/tools/track/check-rig.mjs +209 -0
- package/tools/track/diagnostics.schema.json +60 -0
- package/tools/track/export-rig.mjs +256 -0
- package/tools/track/fallback.mjs +7 -0
- package/tools/track/fk-parity-fixture.mjs +161 -0
- package/tools/track/gate.mjs +349 -0
- package/tools/track/masks.mjs +318 -0
- package/tools/track/metrics.mjs +201 -0
- package/tools/track/publish-obs.mjs +108 -0
- package/tools/track/py/check_env.py +61 -0
- package/tools/track/py/eval_lr.py +229 -0
- package/tools/track/py/lr_viterbi.py +294 -0
- package/tools/track/py/masks.py +405 -0
- package/tools/track/py/objective.py +382 -0
- package/tools/track/py/rig.py +508 -0
- package/tools/track/py/scene.py +326 -0
- package/tools/track/py/test_joint_indices.py +123 -0
- package/tools/track/py/test_lr_viterbi.py +195 -0
- package/tools/track/py/test_masks.py +229 -0
- package/tools/track/py/test_rig.py +288 -0
- package/tools/track/py/test_scene.py +354 -0
- package/tools/track/py/test_track.py +328 -0
- package/tools/track/py/track.py +411 -0
- package/tools/track/remote.mjs +140 -0
- package/tools/track/rig-dump.mjs +227 -0
- package/tools/track/run-box-tests.mjs +14 -0
- package/tools/track/run-box.mjs +168 -0
- package/tools/track/setup-box.sh +59 -0
- package/tools/track/study-2d.mjs +728 -0
- package/dist/assets/analytics-B1hnH66c.js +0 -1
- package/dist/assets/app-BWusbqgO.js +0 -4861
- package/dist/assets/app-qKDo4PBX.css +0 -1
- package/dist/assets/shot-prompt-CHUtw6af.js +0 -17
- package/dist/assets/shot-prompt-C_g2BHVc.css +0 -1
- package/dist/assets/workflow-CVwzMhz2.js +0 -179
- package/dist/assets/workflow-DsudxKHF.css +0 -1
- package/src/fal-motion-studio.jsx +0 -180
- package/src/scene-history.js +0 -129
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/* v2 Motion Details (G6): prompt blocks, line editing, Motion › Advanced and
|
|
2
|
+
the Takes section. The generate actions live in the top bar (R1). Rooted like details.css so these rules beat
|
|
3
|
+
the legacy inspector material without editing src/styles.css. */
|
|
4
|
+
|
|
5
|
+
/* The selected block's range, a read-only value box in the Field grid. */
|
|
6
|
+
.v2-details-section.v2-details-section .motion-block-range {
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
height: var(--h-row, 24px);
|
|
11
|
+
padding: 0 var(--sp-1, 8px);
|
|
12
|
+
border-radius: var(--r-1, 4px);
|
|
13
|
+
background: var(--field, #0f0f11);
|
|
14
|
+
color: var(--text-1, #e4e4e7);
|
|
15
|
+
font-family: var(--mono, 'JetBrains Mono');
|
|
16
|
+
font-size: var(--details-mono-size, 11px);
|
|
17
|
+
font-variant-numeric: tabular-nums;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* A full-width button that is a Field's control sits on the label's row. */
|
|
21
|
+
/* biome-ignore lint/style/noDescendingSpecificity: the repeated region root is intentional to beat legacy inspector rules */
|
|
22
|
+
.v2-details-section.v2-details-section .field > .btn.full {
|
|
23
|
+
margin-top: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Readiness sits inline under the Prompt Blocks controls in the same quiet hint voice. */
|
|
27
|
+
.v2-details-section.v2-details-section .motion-readiness {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: space-between;
|
|
31
|
+
gap: var(--sp-1, 8px);
|
|
32
|
+
min-height: var(--h-row, 24px);
|
|
33
|
+
margin: var(--details-gap, 4px) 0 0;
|
|
34
|
+
padding: 0;
|
|
35
|
+
border: 0;
|
|
36
|
+
background: transparent;
|
|
37
|
+
color: var(--text-2, #9b9ba1);
|
|
38
|
+
font-size: var(--details-secondary-size, 11px);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* biome-ignore lint/style/noDescendingSpecificity: the repeated region root is intentional to beat legacy inspector rules */
|
|
42
|
+
.v2-details-section.v2-details-section .motion-readiness .btn {
|
|
43
|
+
flex: none;
|
|
44
|
+
width: auto;
|
|
45
|
+
height: var(--h-row, 24px);
|
|
46
|
+
min-height: var(--h-row, 24px);
|
|
47
|
+
line-height: var(--h-row, 24px);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.v2-details-section.v2-details-section .ardy-status {
|
|
51
|
+
margin: 0;
|
|
52
|
+
color: var(--text-2, #9b9ba1);
|
|
53
|
+
font-size: var(--details-secondary-size, 11px);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Motion › Advanced: a collapsed subsection with the same arrow grammar as a
|
|
57
|
+
section head, one step indented by living inside the section body. */
|
|
58
|
+
.v2-details-section.v2-details-section .motion-advanced {
|
|
59
|
+
margin-top: var(--sp-1, 8px);
|
|
60
|
+
border-top: var(--details-border-width, 1px) solid var(--row-divider, #1d1d20);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.v2-details-section.v2-details-section .motion-advanced-head {
|
|
64
|
+
box-sizing: border-box;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: var(--sp-1, 8px);
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: var(--h-row, 24px);
|
|
70
|
+
margin: 0;
|
|
71
|
+
padding: 0;
|
|
72
|
+
border: 0;
|
|
73
|
+
background: transparent;
|
|
74
|
+
color: var(--text-3, #636369);
|
|
75
|
+
font-family: var(--sans, 'IBM Plex Sans');
|
|
76
|
+
font-size: var(--details-secondary-size, 11px);
|
|
77
|
+
font-weight: 500;
|
|
78
|
+
text-align: left;
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.v2-details-section.v2-details-section .motion-advanced-head:hover,
|
|
83
|
+
.v2-details-section.v2-details-section .motion-advanced-head:focus-visible {
|
|
84
|
+
color: var(--text-1, #e4e4e7);
|
|
85
|
+
outline: none;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.v2-details-section.v2-details-section .motion-advanced-head:focus-visible {
|
|
89
|
+
box-shadow: inset 0 0 0 var(--details-focus-width, 1px) var(--select, #e8a33d);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.v2-details-section.v2-details-section .motion-advanced-body {
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
gap: var(--details-gap, 4px);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.v2-details-section.v2-details-section .motion-preserve-scale {
|
|
99
|
+
display: flex;
|
|
100
|
+
justify-content: space-between;
|
|
101
|
+
gap: var(--sp-1, 8px);
|
|
102
|
+
padding-left: calc(var(--details-label-width, 100px) + var(--sp-1, 8px));
|
|
103
|
+
font-size: var(--details-meta-size, 10px);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Takes: one 24 px row per version. The current take wears the amber
|
|
107
|
+
selection tint, the same one the Outliner uses for a selected row. */
|
|
108
|
+
.v2-details-section.v2-details-section .motion-takes {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
gap: var(--details-gap, 4px);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.v2-details-section.v2-details-section .motion-take-list {
|
|
115
|
+
display: grid;
|
|
116
|
+
gap: var(--details-group-gap, 2px);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.v2-details-section.v2-details-section .motion-take-row {
|
|
120
|
+
box-sizing: border-box;
|
|
121
|
+
display: grid;
|
|
122
|
+
grid-template-columns: var(--motion-take-index-width, 28px) minmax(0, 1fr) auto;
|
|
123
|
+
align-items: center;
|
|
124
|
+
column-gap: var(--sp-1, 8px);
|
|
125
|
+
width: 100%;
|
|
126
|
+
height: var(--h-row, 24px);
|
|
127
|
+
min-height: var(--h-row, 24px);
|
|
128
|
+
margin: 0;
|
|
129
|
+
padding: 0 var(--sp-1, 8px);
|
|
130
|
+
border: 0;
|
|
131
|
+
border-radius: var(--r-1, 4px);
|
|
132
|
+
background: transparent;
|
|
133
|
+
color: var(--text-row, #c9c9ce);
|
|
134
|
+
font-family: var(--sans, 'IBM Plex Sans');
|
|
135
|
+
font-size: var(--details-secondary-size, 11px);
|
|
136
|
+
line-height: var(--h-row, 24px);
|
|
137
|
+
text-align: left;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.v2-details-section.v2-details-section .motion-take-row:hover,
|
|
142
|
+
.v2-details-section.v2-details-section .motion-take-row:focus-visible {
|
|
143
|
+
background: var(--surface-raised, #1e1e21);
|
|
144
|
+
color: var(--text-1, #e4e4e7);
|
|
145
|
+
outline: none;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.v2-details-section.v2-details-section .motion-take-row:focus-visible {
|
|
149
|
+
box-shadow: inset 0 0 0 var(--details-focus-width, 1px) var(--select, #e8a33d);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.v2-details-section.v2-details-section .motion-take-row.current {
|
|
153
|
+
background: var(--select-tint, rgba(232,163,61,.14));
|
|
154
|
+
color: var(--text-selected, #fff);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.v2-details-section.v2-details-section .motion-take-index {
|
|
158
|
+
color: var(--text-2, #9b9ba1);
|
|
159
|
+
font-family: var(--mono, 'JetBrains Mono');
|
|
160
|
+
font-size: var(--details-mono-size, 11px);
|
|
161
|
+
font-variant-numeric: tabular-nums;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.v2-details-section.v2-details-section .motion-take-row.current .motion-take-index {
|
|
165
|
+
color: var(--select, #e8a33d);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.v2-details-section.v2-details-section .motion-take-label {
|
|
169
|
+
min-width: 0;
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
text-overflow: ellipsis;
|
|
172
|
+
white-space: nowrap;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.v2-details-section.v2-details-section .motion-take-time {
|
|
176
|
+
color: var(--text-3, #636369);
|
|
177
|
+
font-family: var(--mono, 'JetBrains Mono');
|
|
178
|
+
font-size: var(--details-meta-size, 10px);
|
|
179
|
+
font-variant-numeric: tabular-nums;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.v2-details-section.v2-details-section .replay-notice {
|
|
183
|
+
color: var(--select, #e8a33d);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* The recipe summary is the section's footer line. */
|
|
187
|
+
.v2-details-section.v2-details-section .motion-takes-footer {
|
|
188
|
+
box-sizing: border-box;
|
|
189
|
+
margin: 0;
|
|
190
|
+
padding: var(--details-gap, 4px) 0 0;
|
|
191
|
+
border-top: var(--details-border-width, 1px) solid var(--row-divider, #1d1d20);
|
|
192
|
+
color: var(--text-3, #636369);
|
|
193
|
+
font-family: var(--mono, 'JetBrains Mono');
|
|
194
|
+
font-size: var(--details-meta-size, 10px);
|
|
195
|
+
font-variant-numeric: tabular-nums;
|
|
196
|
+
line-height: var(--details-hint-line-height, 1.4);
|
|
197
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@layer regions {
|
|
2
|
+
.pose-details {
|
|
3
|
+
display: grid;
|
|
4
|
+
gap: 8px;
|
|
5
|
+
padding: 8px 12px 12px;
|
|
6
|
+
color: var(--text-1, #e4e4e7);
|
|
7
|
+
}
|
|
8
|
+
.pose-details .pose-section {
|
|
9
|
+
display: grid;
|
|
10
|
+
gap: 4px;
|
|
11
|
+
padding: 8px;
|
|
12
|
+
background: var(--surface-raised, #1e1e21);
|
|
13
|
+
border: 1px solid var(--row-divider, #2a2a2e);
|
|
14
|
+
border-radius: var(--r-1, 4px);
|
|
15
|
+
}
|
|
16
|
+
.pose-details .pose-section > h4 {
|
|
17
|
+
margin: 0;
|
|
18
|
+
color: var(--text-2, #9b9ba1);
|
|
19
|
+
font-size: 11px;
|
|
20
|
+
font-weight: 500;
|
|
21
|
+
}
|
|
22
|
+
.pose-details .pose-row { min-height: 24px; display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 8px; font-size: 12px; }
|
|
23
|
+
.pose-details .pose-row + .pose-row { border-top: 1px solid var(--row-divider, #2a2a2e); }
|
|
24
|
+
.pose-details .pose-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px; }
|
|
25
|
+
.pose-details .pose-preview {
|
|
26
|
+
display: grid;
|
|
27
|
+
gap: 4px;
|
|
28
|
+
padding: 8px;
|
|
29
|
+
border: 1px solid color-mix(in srgb, var(--select, #e8a33d) 65%, transparent);
|
|
30
|
+
border-radius: var(--r-1, 4px);
|
|
31
|
+
}
|
|
32
|
+
.pose-details .pose-preview-title { color: var(--select, #e8a33d); font-weight: 600; }
|
|
33
|
+
.pose-details .pose-preview-summary { color: var(--text-2, #9b9ba1); font-size: 11px; }
|
|
34
|
+
.pose-details .pose-results { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
35
|
+
.pose-details .pose-results li { min-height: 24px; display: flex; align-items: center; color: var(--text-2, #9b9ba1); font-size: 11px; }
|
|
36
|
+
.pose-details .pose-results li > .btn { width: 100%; justify-content: flex-start; text-align: left; padding: 0 4px; color: inherit; }
|
|
37
|
+
.pose-details .pose-results li[data-status="tooHigh"], .pose-details .pose-results li[data-status="wall"] { color: var(--danger, #e5484d); }
|
|
38
|
+
.pose-details .pose-keyhint { color: var(--text-3, #636369); font-size: 10px; }
|
|
39
|
+
}
|
package/src/planview.jsx
CHANGED
|
@@ -18,8 +18,14 @@ const HANDLE_GRAB = 0.5;
|
|
|
18
18
|
const CAMERA_COLOR = "#007f9e";
|
|
19
19
|
const SUBJECT_ONE_COLOR = "#2457d6";
|
|
20
20
|
const SUBJECT_TWO_COLOR = "#d63b55";
|
|
21
|
-
|
|
21
|
+
// Props are the plan's backdrop: a pale wash, a hairline edge and a muted
|
|
22
|
+
// name, so the camera, the cast and the rail are what reads first.
|
|
23
|
+
const OBJECT_COLOR = "#f3f0ea";
|
|
24
|
+
const PROP_EDGE_COLOR = "#a19b91";
|
|
25
|
+
const PROP_LABEL_COLOR = "#8a847b";
|
|
22
26
|
const SELECTED_COLOR = "#b77900";
|
|
27
|
+
const CAST_RING_INNER = 0.62;
|
|
28
|
+
const CAST_RING_OUTER = 0.8;
|
|
23
29
|
|
|
24
30
|
/** yaw that makes the character's local +Z face the given direction */
|
|
25
31
|
const yawToward = (dx, dz) => Math.atan2(dx, dz);
|
|
@@ -149,19 +155,19 @@ function Puck({ color, dragging, turning, showBody = true, handleDist = HANDLE_D
|
|
|
149
155
|
* Short by design: full names ran into each other the moment two actors stood
|
|
150
156
|
* closer than the width of the word, which in a two-shot is always.
|
|
151
157
|
*/
|
|
152
|
-
function PlanLabel({ text, color, offset = -0.72 }) {
|
|
158
|
+
function PlanLabel({ text, color, offset = -0.72, muted = false }) {
|
|
153
159
|
return (
|
|
154
160
|
<Text
|
|
155
161
|
position={[0, 0.05, offset]}
|
|
156
162
|
rotation={[-Math.PI / 2, 0, 0]}
|
|
157
|
-
fontSize={0.32}
|
|
163
|
+
fontSize={muted ? 0.26 : 0.32}
|
|
158
164
|
color={color}
|
|
159
165
|
anchorX="center"
|
|
160
166
|
anchorY="middle"
|
|
161
|
-
outlineWidth={0.045}
|
|
167
|
+
outlineWidth={muted ? 0 : 0.045}
|
|
162
168
|
outlineColor="#0e0d10"
|
|
163
169
|
outlineOpacity={0.92}
|
|
164
|
-
renderOrder={12}
|
|
170
|
+
renderOrder={muted ? 8 : 12}
|
|
165
171
|
depthOffset={-1}
|
|
166
172
|
>
|
|
167
173
|
{text}
|
|
@@ -169,36 +175,58 @@ function PlanLabel({ text, color, offset = -0.72 }) {
|
|
|
169
175
|
);
|
|
170
176
|
}
|
|
171
177
|
|
|
178
|
+
/** A ring around a character's real mesh in its role colour: the cast reads
|
|
179
|
+
* first on a busy plan without the mesh itself being covered. */
|
|
180
|
+
function CastRing({ color }) {
|
|
181
|
+
return (
|
|
182
|
+
<mesh position={[0, 0.05, 0]} rotation={[-Math.PI / 2, 0, 0]} renderOrder={11}>
|
|
183
|
+
<ringGeometry args={[CAST_RING_INNER, CAST_RING_OUTER, 40]} />
|
|
184
|
+
<meshBasicMaterial color={color} transparent opacity={0.9} depthWrite={false} depthTest={false} />
|
|
185
|
+
</mesh>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
172
189
|
function SceneObjectFootprint({ object, selected, dragging, turning }) {
|
|
173
|
-
const { width, depth } = objectSize(object);
|
|
190
|
+
const { width, height, depth } = objectSize(object);
|
|
174
191
|
const objectLabel = displayObjectLabel(object.name).slice(0, 8);
|
|
175
192
|
const handleDist = depth / 2 + 0.65;
|
|
176
193
|
const rotation = (object.rot * Math.PI) / 180;
|
|
194
|
+
// An unselected prop is washed where it stands: a pale cap just above its
|
|
195
|
+
// top, depth-tested, so its own colours fade while a character beside or on
|
|
196
|
+
// it still shows through. Selection keeps the loud amber footprint.
|
|
197
|
+
const capY = (object.y ?? 0) + height + 0.02;
|
|
198
|
+
const edge = useMemo(() => {
|
|
199
|
+
const x = width / 2;
|
|
200
|
+
const z = depth / 2;
|
|
201
|
+
return [[-x, capY, -z], [x, capY, -z], [x, capY, z], [-x, capY, z], [-x, capY, -z]];
|
|
202
|
+
}, [width, depth, capY]);
|
|
177
203
|
return (
|
|
178
204
|
<group position={[object.x, 0, object.z]} rotation={[0, rotation, 0]}>
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
{selected ? (
|
|
206
|
+
<>
|
|
207
|
+
<mesh position={[0, 0.032, 0]} rotation={[-Math.PI / 2, 0, 0]} renderOrder={10}>
|
|
208
|
+
<planeGeometry args={[width, depth]} />
|
|
209
|
+
<meshBasicMaterial color={SELECTED_COLOR} transparent opacity={0.58} depthWrite={false} depthTest={false} />
|
|
210
|
+
</mesh>
|
|
211
|
+
<mesh position={[0, 0.04, 0]} renderOrder={11}>
|
|
212
|
+
<boxGeometry args={[width, 0.02, depth]} />
|
|
213
|
+
<meshBasicMaterial color={SELECTED_COLOR} wireframe depthTest={false} />
|
|
214
|
+
</mesh>
|
|
215
|
+
</>
|
|
216
|
+
) : (
|
|
217
|
+
<>
|
|
218
|
+
{/* Auto-color mode stamps `autoColor` on displayed objects; the board
|
|
219
|
+
agrees with the viewport so "the teal box" means the same thing in
|
|
220
|
+
both panes. */}
|
|
221
|
+
<mesh position={[0, capY, 0]} rotation={[-Math.PI / 2, 0, 0]} renderOrder={6}>
|
|
222
|
+
<planeGeometry args={[width, depth]} />
|
|
223
|
+
<meshBasicMaterial color={object.autoColor ?? OBJECT_COLOR} transparent opacity={0.72} depthWrite={false} />
|
|
224
|
+
</mesh>
|
|
225
|
+
<Line points={edge} color={PROP_EDGE_COLOR} lineWidth={1} transparent opacity={0.7} depthWrite={false} renderOrder={7} />
|
|
226
|
+
</>
|
|
227
|
+
)}
|
|
196
228
|
<group position={[0, 0, depth / 2 + 0.45]} rotation={[0, -rotation, 0]}>
|
|
197
|
-
<PlanLabel
|
|
198
|
-
text={objectLabel}
|
|
199
|
-
color={selected ? SELECTED_COLOR : OBJECT_COLOR}
|
|
200
|
-
offset={0}
|
|
201
|
-
/>
|
|
229
|
+
<PlanLabel text={objectLabel} color={selected ? SELECTED_COLOR : PROP_LABEL_COLOR} offset={0} muted={!selected} />
|
|
202
230
|
</group>
|
|
203
231
|
{selected && <Puck color={SELECTED_COLOR} showBody={false} handleDist={handleDist} dragging={dragging} turning={turning} />}
|
|
204
232
|
</group>
|
|
@@ -253,8 +281,8 @@ function SubjectMovementGuide({ track }) {
|
|
|
253
281
|
</mesh>
|
|
254
282
|
)}
|
|
255
283
|
{[
|
|
256
|
-
{ point: guide.start, label: ko("
|
|
257
|
-
{ point: guide.end, label: ko("
|
|
284
|
+
{ point: guide.start, label: ko("PATH START", "경로 시작") },
|
|
285
|
+
{ point: guide.end, label: ko("PATH END", "경로 끝") },
|
|
258
286
|
].map(({ point, label }) => (
|
|
259
287
|
<group key={label} position={[point.x, 0, point.z]}>
|
|
260
288
|
<mesh position={[0, 0.055, 0]} rotation={[-Math.PI / 2, 0, 0]} renderOrder={11}>
|
|
@@ -874,7 +902,8 @@ export function PlanBoard({ hostRef, planCamRef, shotCamRef, look, fovDeg, chara
|
|
|
874
902
|
const color = listIndex === 0 ? SUBJECT_ONE_COLOR : SUBJECT_TWO_COLOR;
|
|
875
903
|
return (
|
|
876
904
|
<group key={entry.id} position={[entry.x, 0, entry.z]}>
|
|
877
|
-
<PlanLabel text={ko(`S${listIndex + 1}`, `인물 ${listIndex + 1}`)} color={color} />
|
|
905
|
+
<PlanLabel text={ko(`S${listIndex + 1}`, `인물 ${listIndex + 1}`)} color={color} offset={-(CAST_RING_OUTER + 0.3)} />
|
|
906
|
+
<CastRing color={color} />
|
|
878
907
|
<group rotation={[0, (entry.rot * Math.PI) / 180, 0]}>
|
|
879
908
|
{/* The real character mesh already renders in Top-View. Keep only
|
|
880
909
|
its facing stem/handle instead of covering it with a hex puck. */}
|
package/src/posestudio.jsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { useFrame, useThree } from "@react-three/fiber";
|
|
3
|
+
import { Line } from "@react-three/drei";
|
|
3
4
|
import * as THREE from "three";
|
|
4
5
|
import { POSE_BONES, normalizeBoneName, primeBindPose } from "./poses.js";
|
|
5
6
|
import { poseThumbnail, warmThumbnailModels } from "./pose-thumbs.js";
|
|
@@ -282,7 +283,7 @@ const FK_ROTATE_SPEED = 5;
|
|
|
282
283
|
* Every drag move calls `onSolve(kind, trackId, targetWorld)`; the drag end
|
|
283
284
|
* calls `onDragEnd(trackId)` so the caller can key it.
|
|
284
285
|
*/
|
|
285
|
-
export function IkHandles({ chains, fkJoints, ikState, enabled, focus, onFocus, onSolve, onDragEnd }) {
|
|
286
|
+
export function IkHandles({ chains, fkJoints, ikState, enabled, focus, onFocus, onSolve, onDragEnd, selectionOnly = false, onPartPick }) {
|
|
286
287
|
const { camera, gl, raycaster, scene } = useThree();
|
|
287
288
|
const handleRefs = useRef({}); // id -> mesh (all sphere handles)
|
|
288
289
|
const handleMetaRef = useRef(new Map()); // id -> { track, radius }
|
|
@@ -304,6 +305,8 @@ export function IkHandles({ chains, fkJoints, ikState, enabled, focus, onFocus,
|
|
|
304
305
|
const solveRef = useRef(onSolve);
|
|
305
306
|
const endRef = useRef(onDragEnd);
|
|
306
307
|
const focusRef = useRef(onFocus);
|
|
308
|
+
const partPickRef = useRef(onPartPick);
|
|
309
|
+
partPickRef.current = onPartPick;
|
|
307
310
|
// The pointerdown handler filters picks through the CURRENT focus: while
|
|
308
311
|
// a joint is focused, only that joint (sphere + its gizmo arrows) is
|
|
309
312
|
// interactive — unfocused handles are inert, so they can never shadow a
|
|
@@ -887,6 +890,12 @@ const CLICK_PX = 4;
|
|
|
887
890
|
if (kind === "body") dragRef.current.startLocalPos = fkJoints.get(track.id).bone.position.clone();
|
|
888
891
|
}
|
|
889
892
|
focusRef.current?.(track.id);
|
|
893
|
+
if (selectionOnly) {
|
|
894
|
+
partPickRef.current?.(track.id);
|
|
895
|
+
dragRef.current = null;
|
|
896
|
+
gl.domElement.style.cursor = "";
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
890
899
|
gl.domElement.style.cursor = "grabbing";
|
|
891
900
|
window.addEventListener("pointermove", handlersRef.current.onMove);
|
|
892
901
|
window.addEventListener("pointerup", handlersRef.current.onUp);
|
|
@@ -911,8 +920,9 @@ const CLICK_PX = 4;
|
|
|
911
920
|
handleRefs.current[p.track.id]?.visible &&
|
|
912
921
|
handleRefs.current[p.track.id]?.userData.ikExposed !== false
|
|
913
922
|
);
|
|
914
|
-
if (focused) picks = picks.filter((p) => p.track.id === focused);
|
|
915
|
-
|
|
923
|
+
if (focused && !selectionOnly) picks = picks.filter((p) => p.track.id === focused);
|
|
924
|
+
else picks = picks.filter((p) => !p.axisDir); // spheres only
|
|
925
|
+
if (selectionOnly) picks = picks.filter((p) => !p.axisDir);
|
|
916
926
|
if (!picks.length) {
|
|
917
927
|
if (focused) beginDrag("empty", { id: "empty" }, null, null, downXY);
|
|
918
928
|
return;
|
|
@@ -1101,6 +1111,51 @@ const CLICK_PX = 4;
|
|
|
1101
1111
|
);
|
|
1102
1112
|
}
|
|
1103
1113
|
|
|
1114
|
+
/** A poser-only target marker for the active range pin. The target is supplied
|
|
1115
|
+
* in world space; the line is rebuilt every render frame so object travel and
|
|
1116
|
+
* the effector's keyed pose stay visually connected while the playhead moves. */
|
|
1117
|
+
export function RangePinMarker({ chains, track, target = null, enabled = false }) {
|
|
1118
|
+
const markerRef = useRef(null);
|
|
1119
|
+
const lineRef = useRef(null);
|
|
1120
|
+
const effector = useRef(new THREE.Vector3()).current;
|
|
1121
|
+
const targetVector = useRef(new THREE.Vector3()).current;
|
|
1122
|
+
const markerColor = track?.endsWith("Hand") ? "#ff8a3d" : "#4dd2ff";
|
|
1123
|
+
|
|
1124
|
+
useFrame(() => {
|
|
1125
|
+
if (!enabled || !track || !target) return;
|
|
1126
|
+
const chain = chains?.get(track);
|
|
1127
|
+
if (!chain || !markerRef.current || !lineRef.current) return;
|
|
1128
|
+
targetVector.fromArray(target);
|
|
1129
|
+
markerRef.current.position.copy(targetVector);
|
|
1130
|
+
chain.bones[2].getWorldPosition(effector);
|
|
1131
|
+
lineRef.current.geometry.setPositions([...effector.toArray(), ...targetVector.toArray()]);
|
|
1132
|
+
});
|
|
1133
|
+
|
|
1134
|
+
return (
|
|
1135
|
+
<group visible={enabled} renderOrder={1001}>
|
|
1136
|
+
<mesh ref={(mesh) => {
|
|
1137
|
+
markerRef.current = mesh;
|
|
1138
|
+
if (mesh) mesh.layers.set(POSER_LAYER);
|
|
1139
|
+
}}>
|
|
1140
|
+
<sphereGeometry args={[0.035, 12, 8]} />
|
|
1141
|
+
<meshBasicMaterial color={markerColor} transparent opacity={0.95} depthTest={false} depthWrite={false} />
|
|
1142
|
+
</mesh>
|
|
1143
|
+
<Line
|
|
1144
|
+
ref={(line) => {
|
|
1145
|
+
lineRef.current = line;
|
|
1146
|
+
if (line) line.layers.set(POSER_LAYER);
|
|
1147
|
+
}}
|
|
1148
|
+
points={[[0, 0, 0], [0, 0, 0]]}
|
|
1149
|
+
color={markerColor}
|
|
1150
|
+
lineWidth={1.2}
|
|
1151
|
+
transparent
|
|
1152
|
+
opacity={0.72}
|
|
1153
|
+
depthTest={false}
|
|
1154
|
+
/>
|
|
1155
|
+
</group>
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1104
1159
|
/**
|
|
1105
1160
|
* Pose Studio overlay panel. `poses` is the merged list (built-ins + custom)
|
|
1106
1161
|
* the caller owns; every pose has `{ id, label, bones }` and custom poses add
|
|
@@ -1195,7 +1250,7 @@ export function PoseStudioPanel({ subject, model, poses, selectedId, onSelect, o
|
|
|
1195
1250
|
{motionActive && (
|
|
1196
1251
|
<p className="studio-hint" data-pose-motion-warning role="status">
|
|
1197
1252
|
{ikCorrection
|
|
1198
|
-
? ko("
|
|
1253
|
+
? ko("Pose mode is on — applying keys the pose as a full-body correction at the current frame; the motion stays.", "포즈 모드가 켜져 있어요 — 적용하면 현재 프레임에 전신 보정 키로 들어가고, 모션은 그대로 유지됩니다.")
|
|
1199
1254
|
: ko("A sample motion is moving the character — applying a pose clears it and returns to the blocking pose.", "현재 샘플 모션이 캐릭터를 움직이고 있어요. 포즈를 눈앞에 적용하려면 샘플 모션을 지우고 블로킹 포즈로 전환합니다.")}
|
|
1200
1255
|
</p>
|
|
1201
1256
|
)}
|