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.
Files changed (286) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/bin/agent/agent-routes.mjs +74 -70
  3. package/bin/agent/agent-runner.mjs +10 -3
  4. package/bin/agent/motion-runtime.mjs +32 -260
  5. package/bin/agent/providers.mjs +46 -3
  6. package/bin/agent/session-store.mjs +41 -5
  7. package/bin/agent/studio-prompt.mjs +1 -1
  8. package/bin/agent/studio-tools.mjs +48 -11
  9. package/bin/cozyclay.mjs +26 -8
  10. package/bin/live/cli.mjs +22 -3
  11. package/bin/mcp-runtime.mjs +35 -20
  12. package/bin/telemetry-state.mjs +5 -2
  13. package/bin/update-check.mjs +15 -0
  14. package/dist/app/index.html +6 -6
  15. package/dist/assets/analytics-MVZOEW8O.js +1 -0
  16. package/dist/assets/app-C1sdVbg4.css +1 -0
  17. package/dist/assets/app-CA55tcI3.js +4876 -0
  18. package/dist/assets/{demo-B-eg1CT8.js → demo-ByeYp-dY.js} +1 -1
  19. package/dist/assets/{first-shot-handoff-CGgnvNu8.js → first-shot-handoff-DgT28ZiV.js} +1 -1
  20. package/dist/assets/{landing-IbRp3FnQ.js → landing-BaXkpFip.js} +1 -1
  21. package/dist/assets/shot-prompt-CbqWcWYi.css +1 -0
  22. package/dist/assets/shot-prompt-Yrt99wZx.js +17 -0
  23. package/dist/assets/{ticket-BgIzOBOf.js → ticket-BcmsnHn5.js} +1 -1
  24. package/dist/assets/workflow-DRdwoDBT.css +1 -0
  25. package/dist/assets/workflow-dNPGY9xz.js +179 -0
  26. package/dist/cozyclay-package.json +1 -1
  27. package/dist/fonts/IBM-Plex-Sans-OFL.txt +92 -0
  28. package/dist/fonts/JetBrains-Mono-OFL.txt +93 -0
  29. package/dist/fonts/README.md +13 -9
  30. package/dist/fonts/ibm-plex-sans-400-latin.woff2 +0 -0
  31. package/dist/fonts/ibm-plex-sans-500-latin.woff2 +0 -0
  32. package/dist/fonts/ibm-plex-sans-600-latin.woff2 +0 -0
  33. package/dist/fonts/jetbrains-mono-400-latin.woff2 +0 -0
  34. package/dist/fonts/jetbrains-mono-500-latin.woff2 +0 -0
  35. package/dist/index.html +7 -7
  36. package/dist/privacy/index.html +13 -8
  37. package/dist/sitemap.xml +6 -6
  38. package/dist/workflow/index.html +5 -5
  39. package/mcp/LIVE-PROTOCOL.md +1 -1
  40. package/mcp/live-hub.mjs +35 -93
  41. package/mcp/mesh-file.mjs +48 -0
  42. package/mcp/server.mjs +6 -77
  43. package/mcp/tool-handlers.mjs +224 -377
  44. package/package.json +2 -1
  45. package/src/App.jsx +1273 -9075
  46. package/src/analytics.js +393 -9
  47. package/src/app-context.js +211 -0
  48. package/src/app-stage.jsx +25 -24
  49. package/src/ardy/auto-fix-panel.css +108 -0
  50. package/src/ardy/collision-blockers.js +9 -3
  51. package/src/ardy/fix-collisions.js +10 -66
  52. package/src/ardy/ground.js +4 -4
  53. package/src/ardy/ik-drag.js +101 -0
  54. package/src/ardy/ik-key-json.js +43 -0
  55. package/src/ardy/ik.js +122 -76
  56. package/src/ardy/physics-panel.css +2 -0
  57. package/src/ardy/physics-panel.jsx +17 -5
  58. package/src/ardy/platform-fit-panel.css +64 -0
  59. package/src/ardy/platform-fit-panel.jsx +40 -0
  60. package/src/ardy/platform-fit.js +326 -0
  61. package/src/ardy/playback.js +166 -20
  62. package/src/ardy/range-pin.js +299 -0
  63. package/src/ardy/timeline-coordinates.js +26 -0
  64. package/src/ardy/timeline.css +1199 -0
  65. package/src/ardy/timeline.jsx +149 -41
  66. package/src/ardy/waypoints.js +2 -2
  67. package/src/asset-pane.css +462 -0
  68. package/src/asset-pane.jsx +461 -232
  69. package/src/command-bus.js +349 -0
  70. package/src/commands/ai.js +31 -0
  71. package/src/commands/cast.js +140 -0
  72. package/src/commands/elements/character.js +13 -0
  73. package/src/commands/elements/motion.js +14 -0
  74. package/src/commands/elements/object.js +22 -0
  75. package/src/commands/elements/scene.js +16 -0
  76. package/src/commands/elements/shot.js +9 -0
  77. package/src/commands/elements/stage.js +11 -0
  78. package/src/commands/elements.js +155 -0
  79. package/src/commands/export.js +20 -0
  80. package/src/commands/index.js +40 -0
  81. package/src/commands/motion.js +231 -0
  82. package/src/commands/objects.js +174 -0
  83. package/src/commands/project.js +53 -0
  84. package/src/commands/scene.js +81 -0
  85. package/src/commands/shared.js +19 -0
  86. package/src/commands/shot.js +103 -0
  87. package/src/commands/stage.js +17 -0
  88. package/src/commands/view.js +42 -0
  89. package/src/document-store.js +144 -0
  90. package/src/domains/cast.js +1007 -0
  91. package/src/domains/motion.js +3219 -0
  92. package/src/domains/objects.js +932 -0
  93. package/src/domains/scenes.js +817 -0
  94. package/src/domains/shots.js +404 -0
  95. package/src/domains/stage.js +93 -0
  96. package/src/dualview.jsx +113 -57
  97. package/src/facing-marks.js +3 -0
  98. package/src/first-success-guide.jsx +14 -14
  99. package/src/grid-view.js +18 -5
  100. package/src/hierarchy-model.js +24 -16
  101. package/src/hierarchy-panel.css +386 -0
  102. package/src/hierarchy-panel.jsx +150 -21
  103. package/src/{fal-motion-client.js → i2v-motion-client.js} +13 -13
  104. package/src/i2v-motion-studio.jsx +180 -0
  105. package/src/ik-camera.js +3 -0
  106. package/src/main.jsx +6 -0
  107. package/src/motion/generation.js +105 -0
  108. package/src/motion-readiness-ui.jsx +4 -4
  109. package/src/motion-readiness.js +11 -0
  110. package/src/motion-trail.js +366 -9
  111. package/src/object-gizmo.jsx +7 -1
  112. package/src/otio.js +2 -1
  113. package/src/panels/CameraPanel.jsx +60 -0
  114. package/src/panels/CharacterTransformPanel.jsx +48 -0
  115. package/src/panels/EnvironmentPanel.jsx +40 -0
  116. package/src/panels/Foldout.jsx +32 -0
  117. package/src/panels/LightPanel.jsx +24 -0
  118. package/src/panels/ObjectTransformPanel.jsx +554 -0
  119. package/src/panels/PosePanel.jsx +97 -0
  120. package/src/panels/ProjectPanel.jsx +33 -0
  121. package/src/panels/PromptBlocksPanel.jsx +442 -0
  122. package/src/panels/PropsPanel.jsx +70 -0
  123. package/src/panels/ReferenceImageField.jsx +91 -0
  124. package/src/panels/RigControlPanel.jsx +78 -0
  125. package/src/panels/RigPanel.jsx +37 -0
  126. package/src/panels/SubjectBox.jsx +47 -0
  127. package/src/panels/SubjectsPanel.jsx +39 -0
  128. package/src/panels/VideoCapturePanel.jsx +180 -0
  129. package/src/panels/details.css +1171 -0
  130. package/src/panels/motion.css +197 -0
  131. package/src/panels/pose.css +39 -0
  132. package/src/planview.jsx +60 -31
  133. package/src/posestudio.jsx +59 -4
  134. package/src/project-browser.css +1073 -0
  135. package/src/project-browser.jsx +284 -123
  136. package/src/range-pin-object-transform.js +19 -0
  137. package/src/range-pin-panel.css +518 -0
  138. package/src/range-pin-panel.jsx +344 -0
  139. package/src/result-modal.jsx +5 -5
  140. package/src/room.jsx +79 -51
  141. package/src/scene-objects.js +37 -1
  142. package/src/scenes.js +5 -0
  143. package/src/semantic-edit.js +2 -0
  144. package/src/settings-menu.jsx +39 -105
  145. package/src/shell/BottomDock.jsx +450 -0
  146. package/src/shell/DetailsSlot.jsx +513 -0
  147. package/src/shell/LibrarySlot.jsx +86 -0
  148. package/src/shell/MenuBar.jsx +601 -0
  149. package/src/shell/OutlinerSlot.jsx +50 -0
  150. package/src/shell/PreferencesDialog.jsx +538 -0
  151. package/src/shell/PreferencesSlot.jsx +40 -0
  152. package/src/shell/StatusBar.jsx +125 -0
  153. package/src/shell/StudioShell.jsx +92 -0
  154. package/src/shell/TopBar.jsx +129 -0
  155. package/src/shell/ViewportToolbar.jsx +656 -0
  156. package/src/shell/agent-glass.css +296 -0
  157. package/src/shell/dock.css +93 -0
  158. package/src/shell/glass-regions.css +985 -0
  159. package/src/shell/glass.css +687 -0
  160. package/src/shell/log-store.js +52 -0
  161. package/src/shell/mode.css +9 -0
  162. package/src/shell/preferences.css +651 -0
  163. package/src/shell/shell.css +282 -0
  164. package/src/shell/studio-shell-context.js +19 -0
  165. package/src/shell/topbar.css +435 -0
  166. package/src/shell/viewport.css +451 -0
  167. package/src/store/authored-intent.js +22 -0
  168. package/src/store/runtime-adapters.js +92 -0
  169. package/src/store/scene-stage.js +21 -0
  170. package/src/store/use-document-store.js +14 -0
  171. package/src/studio-actions.js +210 -0
  172. package/src/studio-agent-commands.js +41 -271
  173. package/src/studio-agent-context.js +39 -10
  174. package/src/studio-agent-motion.js +122 -373
  175. package/src/studio-agent-protocol.js +67 -31
  176. package/src/studio-app-binding.js +379 -0
  177. package/src/studio-contact-sheet.js +73 -0
  178. package/src/studio-elements.js +58 -51
  179. package/src/styles/themes.css +200 -0
  180. package/src/styles/tokens.css +69 -0
  181. package/src/styles.css +460 -1742
  182. package/src/theme.js +44 -0
  183. package/src/timeline-extent.js +16 -0
  184. package/src/trail-key-conflicts.js +39 -0
  185. package/src/trail-pick.js +59 -0
  186. package/src/ui.jsx +8 -8
  187. package/src/use-case-question.jsx +66 -0
  188. package/src/workflow/AgentPanel.jsx +82 -69
  189. package/src/workflow/agent-client.js +12 -2
  190. package/src/workflow/agent-panel.css +34 -32
  191. package/src/workflow/cozy-scene-node.css +2 -0
  192. package/src/workflow/workflow.css +2 -0
  193. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  194. package/tools/ardy/bridge.mjs +164 -146
  195. package/tools/ardy/visual-qa.mjs +5 -5
  196. package/tools/bench/EXP3.md +61 -0
  197. package/tools/bench/cclay_bench_extract_incam.py +125 -0
  198. package/tools/bench/cclay_bench_extract_obs.py +204 -0
  199. package/tools/bench/cclay_bench_runner.py +42 -0
  200. package/tools/bench/cube-contact.mjs +162 -0
  201. package/tools/bench/exp3.mjs +152 -0
  202. package/tools/bench/extract-bench-lib.mjs +216 -0
  203. package/tools/bench/extract-bench.mjs +302 -0
  204. package/tools/bench/fal-generate.mjs +88 -0
  205. package/tools/bench/fit/README.md +189 -0
  206. package/tools/bench/fit/camera.mjs +59 -0
  207. package/tools/bench/fit/contact.mjs +419 -0
  208. package/tools/bench/fit/footlock.mjs +234 -0
  209. package/tools/bench/fit/motion.mjs +85 -0
  210. package/tools/bench/fit/pin.mjs +27 -0
  211. package/tools/bench/fit/remote.mjs +105 -0
  212. package/tools/bench/fit-bench.mjs +126 -0
  213. package/tools/bench/fit-sanity.mjs +45 -0
  214. package/tools/bench/metrics.mjs +313 -0
  215. package/tools/bench/obs/depth.mjs +258 -0
  216. package/tools/bench/obs/extrinsics.mjs +175 -0
  217. package/tools/bench/obs/fit_mannequin_betas.py +340 -0
  218. package/tools/bench/obs/ground.mjs +294 -0
  219. package/tools/bench/obs/heading.mjs +151 -0
  220. package/tools/bench/obs/ladder.mjs +555 -0
  221. package/tools/bench/obs/mannequin-betas.json +124 -0
  222. package/tools/bench/obs/remote.mjs +83 -0
  223. package/tools/bench/obs/rest_joints.py +52 -0
  224. package/tools/bench/obs/ybot-targets.mjs +49 -0
  225. package/tools/bench/obs-bench.mjs +520 -0
  226. package/tools/bench/score.mjs +343 -0
  227. package/tools/bench/summarize.mjs +86 -0
  228. package/tools/dev/pages/privacy.html +13 -8
  229. package/tools/gt-render/browser.mjs +159 -0
  230. package/tools/gt-render/camera-math.mjs +276 -0
  231. package/tools/gt-render/page.mjs +267 -0
  232. package/tools/gt-render/render.mjs +514 -0
  233. package/tools/gt-render/scene-box.mjs +31 -0
  234. package/tools/gt-render/take-transform.mjs +87 -0
  235. package/tools/morphgs/assets/gen_truth.py +39 -0
  236. package/tools/morphgs/assets/mesh_ori_rig.txt +27 -0
  237. package/tools/morphgs/assets/playback-check.mjs +30 -0
  238. package/tools/morphgs/demo-gate.sh +26 -0
  239. package/tools/morphgs/fbx2morphgs.mjs +68 -0
  240. package/tools/morphgs/morphgs-to-cskel27.mjs +105 -0
  241. package/tools/morphgs/patches/preprocess_src-none-mode.patch +76 -0
  242. package/tools/morphgs/setup-on-cluster.sh +126 -0
  243. package/tools/qa/css-rule-usage.mjs +283 -0
  244. package/tools/qa/studio-control-count.mjs +70 -32
  245. package/tools/run-tests.mjs +134 -12
  246. package/tools/track/DESIGN.md +513 -0
  247. package/tools/track/backfill-provenance.mjs +116 -0
  248. package/tools/track/budget.mjs +8 -0
  249. package/tools/track/check-rig.mjs +209 -0
  250. package/tools/track/diagnostics.schema.json +60 -0
  251. package/tools/track/export-rig.mjs +256 -0
  252. package/tools/track/fallback.mjs +7 -0
  253. package/tools/track/fk-parity-fixture.mjs +161 -0
  254. package/tools/track/gate.mjs +349 -0
  255. package/tools/track/masks.mjs +318 -0
  256. package/tools/track/metrics.mjs +201 -0
  257. package/tools/track/publish-obs.mjs +108 -0
  258. package/tools/track/py/check_env.py +61 -0
  259. package/tools/track/py/eval_lr.py +229 -0
  260. package/tools/track/py/lr_viterbi.py +294 -0
  261. package/tools/track/py/masks.py +405 -0
  262. package/tools/track/py/objective.py +382 -0
  263. package/tools/track/py/rig.py +508 -0
  264. package/tools/track/py/scene.py +326 -0
  265. package/tools/track/py/test_joint_indices.py +123 -0
  266. package/tools/track/py/test_lr_viterbi.py +195 -0
  267. package/tools/track/py/test_masks.py +229 -0
  268. package/tools/track/py/test_rig.py +288 -0
  269. package/tools/track/py/test_scene.py +354 -0
  270. package/tools/track/py/test_track.py +328 -0
  271. package/tools/track/py/track.py +411 -0
  272. package/tools/track/remote.mjs +140 -0
  273. package/tools/track/rig-dump.mjs +227 -0
  274. package/tools/track/run-box-tests.mjs +14 -0
  275. package/tools/track/run-box.mjs +168 -0
  276. package/tools/track/setup-box.sh +59 -0
  277. package/tools/track/study-2d.mjs +728 -0
  278. package/dist/assets/analytics-B1hnH66c.js +0 -1
  279. package/dist/assets/app-BWusbqgO.js +0 -4861
  280. package/dist/assets/app-qKDo4PBX.css +0 -1
  281. package/dist/assets/shot-prompt-CHUtw6af.js +0 -17
  282. package/dist/assets/shot-prompt-C_g2BHVc.css +0 -1
  283. package/dist/assets/workflow-CVwzMhz2.js +0 -179
  284. package/dist/assets/workflow-DsudxKHF.css +0 -1
  285. package/src/fal-motion-studio.jsx +0 -180
  286. package/src/scene-history.js +0 -129
@@ -0,0 +1,513 @@
1
+ import { useStudioShell } from "./studio-shell-context.js";
2
+ import { ko } from "../locale.js";
3
+ import { sceneObjectNameDisplayKo, HIERARCHY_INSPECTOR_TITLES, CHARACTER_MODEL_LABELS } from "../app-stage.jsx";
4
+ import { CUTOUT_KIND } from "../scene-objects.js";
5
+ import LightPanel from "../panels/LightPanel.jsx";
6
+ import CameraPanel from "../panels/CameraPanel.jsx";
7
+ import SubjectsPanel from "../panels/SubjectsPanel.jsx";
8
+ import CharacterTransformPanel from "../panels/CharacterTransformPanel.jsx";
9
+ import RigPanel from "../panels/RigPanel.jsx";
10
+ import PosePanel from "../panels/PosePanel.jsx";
11
+ import VideoCapturePanel from "../panels/VideoCapturePanel.jsx";
12
+ import PromptBlocksPanel from "../panels/PromptBlocksPanel.jsx";
13
+ import RigControlPanel from "../panels/RigControlPanel.jsx";
14
+ import EnvironmentPanel from "../panels/EnvironmentPanel.jsx";
15
+ import PropsPanel from "../panels/PropsPanel.jsx";
16
+ import ObjectTransformPanel from "../panels/ObjectTransformPanel.jsx";
17
+ import { ASSET_IMAGE_TYPES } from "../scene-assets.js";
18
+ import { PoseStudioPanel } from "../posestudio.jsx";
19
+ import { DEFAULT_POSE } from "../poses.js";
20
+ import "../panels/details.css";
21
+
22
+ const characterIndexForRow = (rowId, characters) => rowId === "characterA" ? 0
23
+ : rowId === "characterB" ? 1
24
+ : characters.findIndex((entry) => `character:${entry.id}` === rowId);
25
+ const characterTitle = (index) => ko(`Character ${index + 1}`, `인물 ${index + 1}`);
26
+
27
+ /** The 2a selection header: the selected item's name and a "Kind · detail"
28
+ * type line. Null when nothing that owns settings is selected. */
29
+ function detailsSelection({ selectedHierarchyId: id, selectedSceneObject, rigSelection, characters, shot, inspectorHasContent }) {
30
+ if (!inspectorHasContent) return null;
31
+ if (selectedSceneObject) {
32
+ const kind = selectedSceneObject.renderer === CUTOUT_KIND ? ko("Cutout", "컷아웃") : ko("Mesh", "메시");
33
+ return { name: sceneObjectNameDisplayKo(selectedSceneObject.name), type: `${ko("Prop", "소품")} · ${kind}` };
34
+ }
35
+ if (rigSelection) {
36
+ const index = characterIndexForRow(rigSelection.rowId, characters);
37
+ const kind = rigSelection.token === "rig" ? ko("Rig", "리그") : ko("Bone", "본");
38
+ return { name: HIERARCHY_INSPECTOR_TITLES[rigSelection.token] ?? kind, type: index >= 0 ? `${kind} · ${characterTitle(index)}` : kind };
39
+ }
40
+ if (id === "camera") return { name: HIERARCHY_INSPECTOR_TITLES.camera, type: `${ko("Camera", "카메라")} · ${shot.focalMm}mm` };
41
+ if (id === "characterA" || id === "characterB" || id.startsWith("character:")) {
42
+ const index = characterIndexForRow(id, characters);
43
+ const model = CHARACTER_MODEL_LABELS[characters[index]?.model];
44
+ return { name: characterTitle(Math.max(index, 0)), type: model ? `${ko("Character", "인물")} · ${model}` : ko("Character", "인물") };
45
+ }
46
+ const types = {
47
+ shot: ko("Scene", "장면"),
48
+ light: ko("Light", "조명"),
49
+ characters: ko("Folder", "폴더"),
50
+ environment: ko("Environment", "환경"),
51
+ props: ko("Folder", "폴더"),
52
+ };
53
+ return { name: HIERARCHY_INSPECTOR_TITLES[id] ?? ko("Selection", "선택 항목"), type: types[id] ?? "" };
54
+ }
55
+
56
+ export default function DetailsSlot() {
57
+ const {
58
+ selectedHierarchyId, sceneSaveError, studioAgentError, embedMode, studioAgentMode,
59
+ setStudioAgentMode, AgentPanel, setAgentCollapsed, scenes, activeSceneId,
60
+ buildStudioAgentContext, highlightAgentTargets, generateI2vMotionFromUi, selectedSceneObject, rigSelection,
61
+ inspectorActionsOpen, setInspectorActionsOpen, runStudioAction, deleteSelectedSceneObject, inspectorHasContent,
62
+ keyLightSelected, keyLight, isCameraSelection,
63
+ shot, moveSequence, cameraKeys, activeShot,
64
+ isCharacterSelection, showB, characters, openStudio,
65
+ posing, workflowMode, activeChar,
66
+ activeCharIndex, i2vMotionModel, i2vMotionActions, setI2vMotionStudioOpen,
67
+ selectablePoses, ikMode, ikApplyPoseAsKey, motion, setStudioPick,
68
+ appContext, removePose, setPhotoPoseError, photoPoseFileRef, photoPoseState,
69
+ photoPoseError, activeRig, saveCurrentPose, multiModelStatus, multiModelStage,
70
+ multiModelFileRef, chooseMultiModelFile, multiModelSource, multiModelUrl, setMultiModelUrl,
71
+ useMultiModelUrl, pasteMultiModelUrl, multiModelProgress, multiModelError, multiModelFootage,
72
+ extractMultiModelMotion, multiModelExtract, multiModelTake, multiModelExtractProgress, multiModelExtractError,
73
+ bridge, promptBlocksReveal, promptClips, selectedPromptId, setSelectedPromptId,
74
+ setArdyPrompt, setTlFrame, tlFrameCount, ardySeed,
75
+ changeArdySeed, lineEditMode, toggleLineEditMode, linePreviewUrl, lineCurve,
76
+ lineDrifted, lineTrack, setLineTrack, linePinMode, setLinePinMode,
77
+ linePins, lineClipFrames, lineEditRange, setLineRange, lineRadius,
78
+ changeLineRadius, lineCurveDirty, lineEditFrom, lineEditTo, lineCurvePointCount,
79
+ lineDriftHint, lineCurveHidden, linePreviewBusy, linePreviewMs, linePreviewError,
80
+ generationBusy, bridgeChecking, lineReadinessState, runLineEdit, openMotionSetup,
81
+ recheckMotionHealth, resetLineCurve, exitLineEditMode, readinessState, ardyRunning,
82
+ cancelArdy, ardyStatus, ardyOutcome, tlFrame,
83
+ isRigSelection, ikChains, ikFocus, footSnap, collisionCleanupSupported,
84
+ autoPhysicsRunning, physicsProgress, physicsPreview,
85
+ physicsShow, physicsOptions, platformFitRunning, platformFitProgress, platformFitLast,
86
+ platformFitApplied, changePhysicsOptions, runAutoPhysics, showPhysicsPreview, applyPhysicsPreview,
87
+ cancelPhysicsPreview, ikEditTool, setIkEditTool, showTrails, setShowTrails,
88
+ trailFalloffS, setTrailFalloffS, trailEdit, runTrailRegeneration, trailReadinessState,
89
+ rangePins, rangePinResiduals, rangePinSelection, rangePinPartPick, rangePinPreview,
90
+ sceneObjects, setRangePinSelection, setRangePinPartPick, previewRangePinDraft, applyRangePinDraft,
91
+ deleteRangePin, hasEnvSheet, environment, style, environmentImage,
92
+ inspectorDrop, cutoutInputRef, meshInputRef, importCutout,
93
+ importMesh, selectHierarchy, snapEnabled, setSnapEnabled,
94
+ attachTargetLabel, beginSceneTransaction, endSceneTransaction,
95
+ matteCanvasRef, matteStats, matteMode, setMatteMode, matteEditorRef,
96
+ matteTolerance, setMatteTolerance, matteBrush, setMatteBrush, matteShrink,
97
+ setMatteShrink, matteFeather, setMatteFeather, matteBusy,
98
+ autoColor, recentObjectColors, rememberSceneObjectColor, objectColorDraft, setObjectColorDraft,
99
+ posePhotoFile, posingIndex, posingChar, charA, allPoses,
100
+ studioPick, posingClosing, closeStudio, castDomain, setToast,
101
+ savePose, beginWorkspaceResize,
102
+ } = useStudioShell();
103
+ const selection = detailsSelection({ selectedHierarchyId, selectedSceneObject, rigSelection, characters, shot, inspectorHasContent });
104
+ return (
105
+ <aside className="panel hierarchy-sidebar inspector-sidebar" data-inspector={selectedHierarchyId}>
106
+ {/* Save failures live above the tab content, not inside the Props
107
+ card: that card is hidden whenever any hierarchy node is
108
+ selected, and saves fire exactly while objects are being
109
+ edited — the one case where a failure line inside it is
110
+ invisible. As a sibling of the tab panes this line stays
111
+ on screen for every selection and every tab until the
112
+ next successful write clears it (plan §8.4); the one-shot
113
+ toast still announces each failure episode. */}
114
+ {sceneSaveError && (
115
+ <p className="scene-save-error" role="status">
116
+ {sceneSaveError}
117
+ </p>
118
+ )}
119
+ {studioAgentError && <p className="scene-save-error" role="alert">{studioAgentError}</p>}
120
+ {/* #570: the Agent no longer replaces Details. It docks under it in
121
+ the right column, and Window › Agent folds it away again. */}
122
+ <section className="inspector-pane">
123
+ <div className="inspector-heading details-panel-head">
124
+ {/* #570: the selection is the panel's title; "Details" only
125
+ names an empty panel. */}
126
+ {selection ? (
127
+ <div className="details-selection" data-testid="details-selection">
128
+ <span className="details-selection-name inspector-heading-selection">{selection.name}</span>
129
+ {selection.type && <span className="details-selection-type">{selection.type}</span>}
130
+ </div>
131
+ ) : <strong className="details-panel-title">{ko("Details", "세부 정보")}</strong>}
132
+ {selectedSceneObject && (
133
+ <div className="inspector-actions-wrap">
134
+ <button
135
+ type="button"
136
+ className="inspector-actions-trigger"
137
+ aria-label={ko("Object actions", "오브젝트 작업")}
138
+ aria-expanded={inspectorActionsOpen}
139
+ onClick={() => setInspectorActionsOpen((open) => !open)}
140
+ >
141
+ ⋮
142
+ </button>
143
+ {inspectorActionsOpen && (
144
+ <div className="inspector-actions-menu" role="menu">
145
+ <button type="button" role="menuitem" onClick={() => { runStudioAction("object.duplicate"); setInspectorActionsOpen(false); }}>
146
+ {ko("Duplicate", "복제")}
147
+ </button>
148
+ <button type="button" role="menuitem" onClick={() => { deleteSelectedSceneObject(); setInspectorActionsOpen(false); }}>
149
+ {ko("Delete", "삭제")}
150
+ </button>
151
+ </div>
152
+ )}
153
+ </div>
154
+ )}
155
+ </div>
156
+ <div className="inspector-scroll">
157
+ {/* Nothing is selected that owns settings — say so rather than
158
+ showing an empty column the user has to interpret. */}
159
+ {!inspectorHasContent && (
160
+ <p className="inspector-empty" data-inspector-empty role="status">
161
+ {ko(
162
+ "Select something in the hierarchy — the scene, the camera, a character, the environment or a prop — and its settings appear here.",
163
+ "계층에서 항목을 고르면 — 씨, 카메라, 캐릭터, 환경, 소품 — 그 설정이 여기 나타납니다.",
164
+ )}
165
+ </p>
166
+ )}
167
+ {/* Shot TYPE presets live in the viewport toolbar dropdown — not
168
+ duplicated here. */}
169
+
170
+ {/* Camera animation is authored against the same playhead as motion,
171
+ so keep its controls beside the Motion tools as well as Shot setup. */}
172
+ <LightPanel keyLightSelected={keyLightSelected} keyLight={keyLight} />
173
+ {/* Lens, Recenter and Record used to live here as well as in the
174
+ viewport camera bar and the topbar Export menu. One home each
175
+ (#193, R1): framing is the bar's job, delivery is Export's, and
176
+ selecting the camera now switches to Camera mode so the bar's
177
+ controls are on screen when this panel opens. */}
178
+ <CameraPanel isCameraSelection={isCameraSelection} shot={shot} moveSequence={moveSequence} cameraKeys={cameraKeys} activeShot={activeShot} />
179
+
180
+ <SubjectsPanel
181
+ isCharacterSelection={isCharacterSelection}
182
+ showB={showB}
183
+ characters={characters}
184
+ openStudio={openStudio}
185
+ posing={posing}
186
+ />
187
+
188
+ {/* Scene mode: the viewport gizmo and Move/Rotate/Scale are the primary
189
+ path, so the numeric form starts folded (R5). Motion mode hides
190
+ those tools, so the same foldout becomes the open Placement row —
191
+ where the body stands on stage, which is all Motion can restage.
192
+ Foldout reads defaultOpen once, so the key remounts it per mode. */}
193
+ <CharacterTransformPanel
194
+ workflowMode={workflowMode}
195
+ isCharacterSelection={isCharacterSelection}
196
+ activeChar={activeChar}
197
+ />
198
+
199
+ {/* Rig and Pose are chosen once when a character is cast and then left
200
+ alone, so they open on demand — Subject and Prompt are the panels
201
+ you actually work in. */}
202
+ <RigPanel
203
+ isCharacterSelection={isCharacterSelection}
204
+ activeChar={activeChar}
205
+ />
206
+
207
+ <PosePanel
208
+ isCharacterSelection={isCharacterSelection}
209
+ activeCharIndex={activeCharIndex}
210
+ i2vMotionModel={i2vMotionModel}
211
+ i2vMotionActions={i2vMotionActions}
212
+ setI2vMotionStudioOpen={setI2vMotionStudioOpen}
213
+ selectablePoses={selectablePoses}
214
+ activeChar={activeChar}
215
+ ikMode={ikMode}
216
+ ikApplyPoseAsKey={ikApplyPoseAsKey}
217
+ motion={motion}
218
+ setStudioPick={setStudioPick}
219
+ setToast={appContext.notify}
220
+ removePose={removePose}
221
+ setPhotoPoseError={setPhotoPoseError}
222
+ photoPoseFileRef={photoPoseFileRef}
223
+ photoPoseState={photoPoseState}
224
+ photoPoseError={photoPoseError}
225
+ activeRig={activeRig}
226
+ saveCurrentPose={saveCurrentPose}
227
+ />
228
+
229
+ <VideoCapturePanel
230
+ isCharacterSelection={isCharacterSelection}
231
+ multiModelStatus={multiModelStatus}
232
+ multiModelStage={multiModelStage}
233
+ multiModelFileRef={multiModelFileRef}
234
+ chooseMultiModelFile={chooseMultiModelFile}
235
+ multiModelSource={multiModelSource}
236
+ multiModelUrl={multiModelUrl}
237
+ setMultiModelUrl={setMultiModelUrl}
238
+ useMultiModelUrl={useMultiModelUrl}
239
+ pasteMultiModelUrl={pasteMultiModelUrl}
240
+ multiModelProgress={multiModelProgress}
241
+ multiModelError={multiModelError}
242
+ multiModelFootage={multiModelFootage}
243
+ extractMultiModelMotion={extractMultiModelMotion}
244
+ multiModelExtract={multiModelExtract}
245
+ multiModelTake={multiModelTake}
246
+ multiModelExtractProgress={multiModelExtractProgress}
247
+ multiModelExtractError={multiModelExtractError}
248
+ activeChar={activeChar}
249
+ bridge={bridge}
250
+ />
251
+ <PromptBlocksPanel
252
+ isCharacterSelection={isCharacterSelection}
253
+ promptBlocksReveal={promptBlocksReveal}
254
+ promptClips={promptClips}
255
+ selectedPromptId={selectedPromptId}
256
+ setSelectedPromptId={setSelectedPromptId}
257
+ setArdyPrompt={setArdyPrompt}
258
+ setTlFrame={setTlFrame}
259
+ tlFrameCount={tlFrameCount}
260
+ ardySeed={ardySeed}
261
+ changeArdySeed={changeArdySeed}
262
+ motion={motion}
263
+ lineEditMode={lineEditMode}
264
+ toggleLineEditMode={toggleLineEditMode}
265
+ linePreviewUrl={linePreviewUrl}
266
+ lineCurve={lineCurve}
267
+ lineDrifted={lineDrifted}
268
+ lineTrack={lineTrack}
269
+ setLineTrack={setLineTrack}
270
+ linePinMode={linePinMode}
271
+ setLinePinMode={setLinePinMode}
272
+ linePins={linePins}
273
+ lineClipFrames={lineClipFrames}
274
+ lineEditRange={lineEditRange}
275
+ setLineRange={setLineRange}
276
+ lineRadius={lineRadius}
277
+ changeLineRadius={changeLineRadius}
278
+ lineCurveDirty={lineCurveDirty}
279
+ lineEditFrom={lineEditFrom}
280
+ lineEditTo={lineEditTo}
281
+ lineCurvePointCount={lineCurvePointCount}
282
+ lineDriftHint={lineDriftHint}
283
+ lineCurveHidden={lineCurveHidden}
284
+ linePreviewBusy={linePreviewBusy}
285
+ linePreviewMs={linePreviewMs}
286
+ linePreviewError={linePreviewError}
287
+ generationBusy={generationBusy}
288
+ bridgeChecking={bridgeChecking}
289
+ bridge={bridge}
290
+ lineReadinessState={lineReadinessState}
291
+ runLineEdit={runLineEdit}
292
+ openMotionSetup={openMotionSetup}
293
+ recheckMotionHealth={recheckMotionHealth}
294
+ resetLineCurve={resetLineCurve}
295
+ exitLineEditMode={exitLineEditMode}
296
+ readinessState={readinessState}
297
+ ardyRunning={ardyRunning}
298
+ cancelArdy={cancelArdy}
299
+ ardyStatus={ardyStatus}
300
+ ardyOutcome={ardyOutcome}
301
+ />
302
+
303
+ <RigControlPanel
304
+ isRigSelection={isRigSelection}
305
+ rigSelection={rigSelection}
306
+ ikChains={ikChains}
307
+ ikFocus={ikFocus}
308
+ footSnap={footSnap}
309
+ ikMode={ikMode}
310
+ collisionCleanupSupported={collisionCleanupSupported}
311
+ motion={motion}
312
+ autoPhysicsRunning={autoPhysicsRunning}
313
+ physicsProgress={physicsProgress}
314
+ physicsPreview={physicsPreview}
315
+ physicsShow={physicsShow}
316
+ physicsOptions={physicsOptions}
317
+ platformFitRunning={platformFitRunning}
318
+ platformFitProgress={platformFitProgress}
319
+ platformFitLast={platformFitLast}
320
+ platformFitApplied={platformFitApplied}
321
+ tlFrame={tlFrame}
322
+ changePhysicsOptions={changePhysicsOptions}
323
+ runAutoPhysics={runAutoPhysics}
324
+ showPhysicsPreview={showPhysicsPreview}
325
+ applyPhysicsPreview={applyPhysicsPreview}
326
+ cancelPhysicsPreview={cancelPhysicsPreview}
327
+ setTlFrame={setTlFrame}
328
+ ikEditTool={ikEditTool}
329
+ setIkEditTool={setIkEditTool}
330
+ showTrails={showTrails}
331
+ setShowTrails={setShowTrails}
332
+ trailFalloffS={trailFalloffS}
333
+ setTrailFalloffS={setTrailFalloffS}
334
+ trailEdit={trailEdit}
335
+ generationBusy={generationBusy}
336
+ bridgeChecking={bridgeChecking}
337
+ bridge={bridge}
338
+ runTrailRegeneration={runTrailRegeneration}
339
+ trailReadinessState={trailReadinessState}
340
+ openMotionSetup={openMotionSetup}
341
+ recheckMotionHealth={recheckMotionHealth}
342
+ rangePins={rangePins}
343
+ rangePinResiduals={rangePinResiduals}
344
+ rangePinSelection={rangePinSelection}
345
+ rangePinPartPick={rangePinPartPick}
346
+ rangePinPreview={rangePinPreview}
347
+ objects={sceneObjects}
348
+ setRangePinSelection={setRangePinSelection}
349
+ setRangePinPartPick={setRangePinPartPick}
350
+ previewRangePinDraft={previewRangePinDraft}
351
+ applyRangePinDraft={applyRangePinDraft}
352
+ deleteRangePin={deleteRangePin}
353
+ />
354
+
355
+ <EnvironmentPanel
356
+ selectedHierarchyId={selectedHierarchyId}
357
+ hasEnvSheet={hasEnvSheet}
358
+ environment={environment}
359
+ style={style}
360
+ environmentImage={environmentImage}
361
+ setToast={appContext.notify}
362
+ />
363
+
364
+ <PropsPanel
365
+ selectedHierarchyId={selectedHierarchyId}
366
+ inspectorDrop={inspectorDrop}
367
+ cutoutInputRef={cutoutInputRef}
368
+ meshInputRef={meshInputRef}
369
+ importCutout={importCutout}
370
+ importMesh={importMesh}
371
+ sceneObjects={sceneObjects}
372
+ selectHierarchy={selectHierarchy}
373
+ />
374
+
375
+ <ObjectTransformPanel
376
+ selectedSceneObject={selectedSceneObject}
377
+ snapEnabled={snapEnabled}
378
+ setSnapEnabled={setSnapEnabled}
379
+ attachTargetLabel={attachTargetLabel}
380
+ sceneObjects={sceneObjects}
381
+ beginSceneTransaction={beginSceneTransaction}
382
+ endSceneTransaction={endSceneTransaction}
383
+ matteCanvasRef={matteCanvasRef}
384
+ matteStats={matteStats}
385
+ matteMode={matteMode}
386
+ setMatteMode={setMatteMode}
387
+ matteEditorRef={matteEditorRef}
388
+ matteTolerance={matteTolerance}
389
+ setMatteTolerance={setMatteTolerance}
390
+ matteBrush={matteBrush}
391
+ setMatteBrush={setMatteBrush}
392
+ matteShrink={matteShrink}
393
+ setMatteShrink={setMatteShrink}
394
+ matteFeather={matteFeather}
395
+ setMatteFeather={setMatteFeather}
396
+ matteBusy={matteBusy}
397
+ autoColor={autoColor}
398
+ recentObjectColors={recentObjectColors}
399
+ rememberSceneObjectColor={rememberSceneObjectColor}
400
+ objectColorDraft={objectColorDraft}
401
+ setObjectColorDraft={setObjectColorDraft}
402
+ />
403
+ </div>
404
+ {selectedSceneObject && (
405
+ <div className="inspector-footer">
406
+ <span>{ko("Delete or Backspace to remove", "Delete 또는 Backspace로 삭제")}</span>
407
+ </div>
408
+ )}
409
+ </section>
410
+ {/* #570: the Agent always sits at the foot of the right column.
411
+ Collapsed it is one 44px bar; Window › Agent, Cmd/Ctrl+B and
412
+ the bar's own chevron fold it open and shut. */}
413
+ {!embedMode && !studioAgentMode && (
414
+ <button
415
+ type="button"
416
+ className="studio-agent-bar"
417
+ data-testid="studio-agent-bar"
418
+ aria-expanded="false"
419
+ title={ko("Open Agent (Cmd/Ctrl+B)", "에이전트 열기 (Cmd/Ctrl+B)")}
420
+ onClick={() => setStudioAgentMode(true)}
421
+ >
422
+ <svg className="studio-agent-spark" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 2.25c.4 2.9 1.85 4.35 4.75 4.75-2.9.4-4.35 1.85-4.75 4.75-.4-2.9-1.85-4.35-4.75-4.75 2.9-.4 4.35-1.85 4.75-4.75z M12.75 11.5v2.5 M11.5 12.75H14" /></svg>
423
+ <span className="studio-agent-bar-title">{ko("Agent", "에이전트")}</span>
424
+ <span className="studio-agent-bar-hint">{ko("Ask the agent…", "에이전트에게 요청하기…")}</span>
425
+ <svg viewBox="0 0 10 10" aria-hidden="true"><path d="m2.5 6 2.5-2.5L7.5 6" /></svg>
426
+ </button>
427
+ )}
428
+ {!embedMode && <div className="studio-agent-inspector" hidden={!studioAgentMode}>
429
+ <div
430
+ className="workspace-splitter shell-splitter shell-agent-splitter"
431
+ role="separator"
432
+ aria-orientation="horizontal"
433
+ aria-label={ko("Resize Details and Agent", "세부 정보와 에이전트 크기 조절")}
434
+ onPointerDown={(event) => beginWorkspaceResize("agent", event)}
435
+ />
436
+ <button
437
+ type="button"
438
+ className="studio-agent-collapse"
439
+ aria-expanded="true"
440
+ aria-label={ko("Collapse Agent", "에이전트 접기")}
441
+ title={ko("Collapse Agent (Cmd/Ctrl+B)", "에이전트 접기 (Cmd/Ctrl+B)")}
442
+ onClick={() => setStudioAgentMode(false)}
443
+ >
444
+ <svg viewBox="0 0 10 10" aria-hidden="true"><path d="m2.5 4 2.5 2.5L7.5 4" /></svg>
445
+ </button>
446
+ <AgentPanel embedded hidden={!studioAgentMode} surface="studio" defaultCollapsed onCollapsedChange={setAgentCollapsed}
447
+ sceneName={scenes.find((entry) => entry.id === activeSceneId)?.name ?? ko("Untitled Scene", "제목 없는 씬")}
448
+ buildContext={buildStudioAgentContext} onReceipt={highlightAgentTargets}
449
+ onI2vAction={(instruction) => void generateI2vMotionFromUi(instruction)} />
450
+ </div>}
451
+ {/* The reference-photo picker sits outside the panel so re-mounting
452
+ the studio cannot cancel an in-flight read. */}
453
+ <input
454
+ ref={photoPoseFileRef}
455
+ className="multimodel-file-input"
456
+ type="file"
457
+ accept={ASSET_IMAGE_TYPES.join(",")}
458
+ data-pose-photo-input
459
+ onChange={(event) => {
460
+ const file = event.target.files?.[0];
461
+ event.target.value = ""; // the same photo must be re-pickable after an error
462
+ if (file) posePhotoFile(file);
463
+ }}
464
+ />
465
+ {/* Pose Studio docks under the inspector instead of floating over
466
+ the shot: the viewport keeps the posed character unobstructed. */}
467
+ {posing && (
468
+ <PoseStudioPanel
469
+ docked
470
+ subject={posingIndex >= 0 ? posingIndex + 1 : 1}
471
+ model={posingChar?.model ?? charA.model}
472
+ poses={allPoses}
473
+ selectedId={studioPick}
474
+ closing={posingClosing}
475
+ motionActive={Boolean(motion)}
476
+ ikCorrection={ikMode && Boolean(motion) && posingChar?.id === activeChar.id}
477
+ onSelect={setStudioPick}
478
+ onApply={(selectedPoseId) => {
479
+ const pose = selectablePoses.find((p) => p.id === selectedPoseId);
480
+ if (pose) {
481
+ // IK mode over a take, on the active character: key the
482
+ // pose as a correction instead of erasing the motion.
483
+ if (ikMode && posingChar?.id === activeChar.id && ikApplyPoseAsKey(pose)) {
484
+ closeStudio();
485
+ return;
486
+ }
487
+ const hadMotion = Boolean(motion);
488
+ if (posingChar) castDomain.run('character.setPose', { characterId: posingChar.id, pose: pose.id, clearMotion: hadMotion });
489
+ closeStudio();
490
+ setToast(hadMotion ? ko("Cleared the current motion and applied the pose", "현재 모션을 지우고 포즈를 적용했어요") : ko("Pose applied", "포즈를 적용했어요"));
491
+ } else {
492
+ setToast(ko("Couldn't find the selected pose — pick again", "선택한 포즈를 찾지 못했어요. 다시 골라 주세요"));
493
+ }
494
+ }}
495
+ onReset={() => {
496
+ if (posingChar) castDomain.run('character.setPose', { characterId: posingChar.id, pose: DEFAULT_POSE.id, clearMotion: Boolean(motion) });
497
+ setStudioPick(DEFAULT_POSE.id);
498
+ setToast(ko("Back to the default pose", "기본 포즈로 돌아왔어요"));
499
+ }}
500
+ onSave={savePose}
501
+ onPhoto={() => {
502
+ setPhotoPoseError("");
503
+ photoPoseFileRef.current?.click();
504
+ }}
505
+ photoState={photoPoseState}
506
+ photoError={photoPoseError}
507
+ onDelete={removePose}
508
+ onClose={closeStudio}
509
+ />
510
+ )}
511
+ </aside>
512
+ );
513
+ }
@@ -0,0 +1,86 @@
1
+ import { useEffect, useState } from "react";
2
+ import { useStudioShell } from "./studio-shell-context.js";
3
+ import { ko } from "../locale.js";
4
+ import AssetPane from "../asset-pane.jsx";
5
+
6
+ const CONTENT_COLLAPSED_KEY = "cozyclay.dock.content-collapsed.v1";
7
+
8
+ function readContentCollapsed() {
9
+ try {
10
+ return globalThis.localStorage?.getItem(CONTENT_COLLAPSED_KEY) === "1";
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ function writeContentCollapsed(value) {
17
+ try {
18
+ globalThis.localStorage?.setItem(CONTENT_COLLAPSED_KEY, value ? "1" : "0");
19
+ } catch (error) {
20
+ console.warn(`[cozyclay] could not store ${CONTENT_COLLAPSED_KEY}`, error);
21
+ }
22
+ }
23
+
24
+ // #570: the object library (Content | Log) is the dock's Assets tab. It stays
25
+ // mounted while the Animation tab is up, hidden.
26
+ export default function LibrarySlot({ active = true }) {
27
+ const {
28
+ setBottomTab, beginAssetDrag, shelfImageIds, shelfMeshIds,
29
+ manageAssetStorage, setManageAssetStorage, unusedAssetIds, usedAssetIds, usageCounts,
30
+ projectAssetGraphSignature, assetTrash, deleteUnusedAsset, undoDeletedAsset, deletingAssetId,
31
+ projectManifest, takeVersions, loadTakeVersion, shots, selectTimelineShot, allPoses,
32
+ spawnCharacter, addSceneObject, runStudioAction,
33
+ } = useStudioShell();
34
+ const [collapsed, setCollapsed] = useState(readContentCollapsed);
35
+ const [shelfShown, setShelfShown] = useState(false);
36
+
37
+ // App scans imported assets only while they are on screen.
38
+ useEffect(() => {
39
+ const visible = active && shelfShown;
40
+ setBottomTab(visible ? "assets" : "timeline");
41
+ }, [active, shelfShown, setBottomTab]);
42
+
43
+ // Double-click in Content: the same payloads the drag carries, placed at
44
+ // the origin through the named domain actions.
45
+ function placeAssetAtOrigin(payload) {
46
+ const origin = { x: 0, z: 0 };
47
+ if (payload.kind === "character") spawnCharacter(payload.id, origin.x, origin.z);
48
+ else if (payload.kind === "object") addSceneObject(payload.objectKind, origin);
49
+ else if (payload.kind === "image" || payload.kind === "mesh") {
50
+ runStudioAction("asset.import", { assetId: payload.assetId, placeAs: payload.kind === "mesh" ? "mesh" : "cutout", placement: origin });
51
+ }
52
+ }
53
+
54
+ return (
55
+ <section className="assets-pane studio-library" aria-label={ko("Library", "라이브러리")} data-content-collapsed={collapsed || undefined} hidden={!active}>
56
+ <AssetPane
57
+ onAssetGrab={beginAssetDrag}
58
+ onAssetPlace={placeAssetAtOrigin}
59
+ imageAssetIds={shelfImageIds}
60
+ meshAssetIds={shelfMeshIds}
61
+ manageStorage={manageAssetStorage}
62
+ onManageStorageToggle={() => setManageAssetStorage((current) => !current)}
63
+ unusedAssetIds={unusedAssetIds}
64
+ usedAssetIds={usedAssetIds}
65
+ usageCounts={usageCounts}
66
+ graphSignature={projectAssetGraphSignature}
67
+ trashCount={assetTrash.length}
68
+ onDeleteUnusedAsset={deleteUnusedAsset}
69
+ onUndoDelete={undoDeletedAsset}
70
+ deletingAssetId={deletingAssetId}
71
+ resourceManifest={projectManifest}
72
+ shots={shots}
73
+ takeVersions={takeVersions}
74
+ poses={allPoses}
75
+ onShotOpen={selectTimelineShot}
76
+ onTakeOpen={loadTakeVersion}
77
+ collapsed={collapsed}
78
+ onCollapsedChange={(next) => {
79
+ setCollapsed(next);
80
+ writeContentCollapsed(next);
81
+ }}
82
+ onShelfVisibleChange={setShelfShown}
83
+ />
84
+ </section>
85
+ );
86
+ }