cozyclay 3.0.3 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/INSTALL_FOR_AGENTS.md +146 -0
  3. package/README.md +74 -192
  4. package/THIRD_PARTY_NOTICES.md +0 -21
  5. package/bin/agent/agent-routes.mjs +29 -62
  6. package/bin/agent/agent-runner.mjs +5 -3
  7. package/bin/agent/main-agent.mjs +70 -0
  8. package/bin/agent/main-prompt.mjs +17 -0
  9. package/bin/agent/plan-tool.mjs +114 -0
  10. package/bin/agent/script-tool.mjs +2 -0
  11. package/bin/agent/sculpt-sketch.mjs +190 -0
  12. package/bin/agent/session-store.mjs +4 -3
  13. package/bin/agent/sketch-code.mjs +151 -0
  14. package/bin/agent/sketch-sandbox.mjs +102 -0
  15. package/bin/agent/studio-prompt.mjs +1 -1
  16. package/bin/agent/studio-task-routes.mjs +185 -0
  17. package/bin/agent/studio-tasks.mjs +156 -0
  18. package/bin/agent/studio-tools.mjs +3 -2
  19. package/bin/agent/studio-turn-transaction.mjs +76 -0
  20. package/bin/agent/turn-frames.mjs +4 -0
  21. package/bin/cozyclay.mjs +5 -7
  22. package/dist/app/index.html +7 -6
  23. package/dist/assets/TaskBoard-ngdqiu7u.js +1 -0
  24. package/dist/assets/app-BlwOpest.css +1 -0
  25. package/dist/assets/app-DmR7jBkz.js +5144 -0
  26. package/dist/assets/compile-worker-D0pKioa0.js +5 -0
  27. package/dist/assets/landing-y3EqDgSZ.js +1 -0
  28. package/dist/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
  29. package/dist/assets/motion3-compile-BB3_1-EB.js +1 -0
  30. package/dist/assets/scene-asset-sync-CBx7XRti.css +1 -0
  31. package/dist/assets/scene-asset-sync-Cgdtr1Yw.js +13 -0
  32. package/dist/assets/task-board-ErQvfoaz.js +14 -0
  33. package/dist/assets/workflow-zQM-EKW1.js +179 -0
  34. package/dist/assets/x-bot-tpose-body-BusyR3eg.json +1 -0
  35. package/dist/assets/y-bot-tpose-body-Dy1VKChZ.json +1 -0
  36. package/dist/cozyclay-package.json +1 -1
  37. package/dist/greybox-to-video/index.html +2 -3
  38. package/dist/index.html +7 -57
  39. package/dist/llms.txt +2 -1
  40. package/dist/previs-software/index.html +2 -2
  41. package/dist/privacy/index.html +3 -7
  42. package/dist/seedance-camera-control/index.html +2 -2
  43. package/dist/sitemap.xml +5 -5
  44. package/dist/sw.js +5 -1
  45. package/dist/thumbs/blank-stage-dark.webp +0 -0
  46. package/dist/thumbs/blank-stage-light.webp +0 -0
  47. package/dist/workflow/index.html +5 -4
  48. package/mcp/mesh-file.mjs +3 -1
  49. package/mcp/server.mjs +3 -0
  50. package/mcp/tool-handlers.mjs +2 -2
  51. package/package.json +5 -3
  52. package/src/App.jsx +51 -282
  53. package/src/analytics.js +3 -148
  54. package/src/app-stage.jsx +1 -1
  55. package/src/asset-pane.jsx +5 -10
  56. package/src/auto-flat.js +10 -0
  57. package/src/bus-tasks.js +11 -5
  58. package/src/command-bus.js +1 -1
  59. package/src/commands/elements.js +4 -3
  60. package/src/commands/motion3-compile.js +51 -27
  61. package/src/commands/motion3.js +3 -3
  62. package/src/commands/objects.js +1 -1
  63. package/src/commands/project.js +1 -1
  64. package/src/commands/shot.js +16 -4
  65. package/src/controls.jsx +0 -11
  66. package/src/cuts.js +11 -0
  67. package/src/domains/cast.js +3 -2
  68. package/src/domains/motion.js +3 -10
  69. package/src/domains/playback.js +32 -0
  70. package/src/domains/scenes.js +3 -22
  71. package/src/domains/shots.js +5 -5
  72. package/src/dualview.jsx +11 -15
  73. package/src/hierarchy-panel.css +18 -4
  74. package/src/hierarchy-panel.jsx +12 -11
  75. package/src/kits/held.js +3 -2
  76. package/src/light-blockers.js +2 -2
  77. package/src/main.jsx +5 -0
  78. package/src/material-look.js +189 -0
  79. package/src/material-presets.js +78 -0
  80. package/src/material-textures.js +157 -0
  81. package/src/motion/ground.js +5 -14
  82. package/src/motion/playback-frame-store.js +79 -0
  83. package/src/motion/playback-probe.js +16 -0
  84. package/src/motion/playback-subject.js +7 -0
  85. package/src/motion/timeline.css +43 -5
  86. package/src/motion/timeline.jsx +4 -3
  87. package/src/motion3/body.js +37 -5
  88. package/src/motion3/carried.js +11 -3
  89. package/src/motion3/compile-job.js +1 -1
  90. package/src/motion3/compiler/compile.js +7 -1
  91. package/src/motion3/compiler/finger-close.js +137 -0
  92. package/src/motion3/compiler/limbs.js +18 -3
  93. package/src/motion3/compiler/pose.js +6 -0
  94. package/src/motion3/compiler/validate-objects.js +17 -2
  95. package/src/motion3/compiler/validate-parts.js +2 -1
  96. package/src/motion3/compiler/vocab.js +4 -4
  97. package/src/motion3/describe.js +2 -2
  98. package/src/motion3/fix/index.js +5 -3
  99. package/src/motion3/flight.js +38 -0
  100. package/src/motion3/free-motion.js +10 -7
  101. package/src/motion3/load.js +125 -0
  102. package/src/motion3/mass.js +23 -0
  103. package/src/motion3/object-keys.js +11 -6
  104. package/src/motion3/object-pose.js +11 -3
  105. package/src/motion3/pipeline.js +3 -0
  106. package/src/motion3/qa/explain.js +3 -1
  107. package/src/motion3/qa/grasp-checks.js +123 -0
  108. package/src/motion3/qa/hints-grasp.js +71 -0
  109. package/src/motion3/qa/index.js +3 -1
  110. package/src/motion3/qa/object-checks.js +7 -2
  111. package/src/motion3/qa/patch.js +2 -1
  112. package/src/motion3/qa/spec-checks.js +10 -1
  113. package/src/motion3/schema/fields.js +3 -3
  114. package/src/motion3/schema/topics.js +9 -8
  115. package/src/motion3/schema/types.js +4 -1
  116. package/src/motion3/seat.js +84 -0
  117. package/src/motion3/settle.js +11 -6
  118. package/src/motion3/shapes.js +70 -0
  119. package/src/motion3/skills/catch.js +71 -0
  120. package/src/motion3/skills/index.js +10 -3
  121. package/src/motion3/skills/info.js +13 -0
  122. package/src/motion3/skills/throw.js +72 -0
  123. package/src/object-catalog.jsx +0 -4
  124. package/src/object-gizmo.jsx +2 -1
  125. package/src/object-look.js +9 -5
  126. package/src/object-path.js +1 -1
  127. package/src/panels/ProjectPanel.jsx +42 -21
  128. package/src/panels/details.css +15 -4
  129. package/src/popover.js +37 -0
  130. package/src/posestudio.jsx +15 -10
  131. package/src/primitive-look.js +38 -0
  132. package/src/project-browser-art.jsx +19 -0
  133. package/src/project-browser.css +74 -121
  134. package/src/project-browser.jsx +36 -60
  135. package/src/props.jsx +24 -35
  136. package/src/render-passes.js +7 -1
  137. package/src/retired-kinds.js +37 -0
  138. package/src/scene-objects.js +5 -9
  139. package/src/scenes.js +4 -5
  140. package/src/sculpt-colours.js +65 -0
  141. package/src/sculpt-compare.js +13 -50
  142. package/src/sculpt-csg.js +112 -0
  143. package/src/sculpt-curve.js +123 -0
  144. package/src/sculpt-cutters.js +43 -0
  145. package/src/sculpt-detail.js +207 -0
  146. package/src/sculpt-geometry-loft-convex.js +100 -0
  147. package/src/sculpt-geometry-shapes.js +250 -0
  148. package/src/sculpt-geometry.js +33 -15
  149. package/src/sculpt-loft-rings.js +225 -0
  150. package/src/sculpt-noise.js +65 -0
  151. package/src/sculpt-part-fields.js +27 -0
  152. package/src/sculpt-recipe-read.js +65 -0
  153. package/src/sculpt-recipe.js +33 -75
  154. package/src/sculpt-run.js +1 -1
  155. package/src/sculpt-scatter-items.js +183 -0
  156. package/src/sculpt-scatter-shape.js +57 -0
  157. package/src/sculpt-scatter.js +124 -0
  158. package/src/sculpt-shapes-curved.js +211 -0
  159. package/src/sculpt-shapes-loft-convex.js +218 -0
  160. package/src/sculpt-shapes.js +138 -0
  161. package/src/sculpt-sketch-state.js +76 -0
  162. package/src/sculpt-sketch.jsx +124 -0
  163. package/src/sculpt-surface-fields.js +65 -0
  164. package/src/settings-menu.jsx +1 -20
  165. package/src/shell/BottomDock.jsx +9 -9
  166. package/src/shell/MenuBar.jsx +1 -9
  167. package/src/shell/PreferencesDialog.jsx +3 -4
  168. package/src/shell/ViewportToolbar.jsx +3 -3
  169. package/src/shell/add-camera-shot.js +11 -0
  170. package/src/shell/glass-regions.css +3 -2
  171. package/src/shell/glass.css +0 -5
  172. package/src/shell/lens-scrub-math.js +51 -0
  173. package/src/shell/lens-scrub.css +73 -0
  174. package/src/shell/lens-scrub.jsx +98 -0
  175. package/src/shell/mode.css +6 -5
  176. package/src/shell/preferences.css +1 -32
  177. package/src/shell/shell.css +18 -1
  178. package/src/shell/studio-shell-context.js +5 -0
  179. package/src/shot-frame.js +26 -0
  180. package/src/stage-canvas-rigs.jsx +7 -8
  181. package/src/stage-characters.jsx +26 -0
  182. package/src/stage-gizmo.js +43 -0
  183. package/src/stage-labels-ko.js +0 -6
  184. package/src/stage-startup-defaults.js +3 -9
  185. package/src/starter-scenes.js +5 -11
  186. package/src/studio-agent-commands.js +2 -2
  187. package/src/studio-agent-protocol.js +2 -7
  188. package/src/studio-app-binding.js +1 -1
  189. package/src/studio-elements.js +6 -2
  190. package/src/styles/themes.css +13 -0
  191. package/src/styles.css +6 -674
  192. package/src/text/text-object.jsx +1 -1
  193. package/src/ui.jsx +8 -16
  194. package/src/use-embed-bridge.js +3 -6
  195. package/src/use-render-activity.js +3 -1
  196. package/src/view-ghosts.jsx +5 -3
  197. package/src/viewport-visible-rect.js +37 -0
  198. package/src/workflow/AgentPanel.jsx +79 -46
  199. package/src/workflow/AgentPlanCard.jsx +41 -0
  200. package/src/workflow/AgentProgressCard.jsx +1 -1
  201. package/src/workflow/TaskBoard.jsx +102 -0
  202. package/src/workflow/TaskInline.jsx +114 -0
  203. package/src/workflow/agent-client.js +25 -36
  204. package/src/workflow/agent-panel.css +14 -1
  205. package/src/workflow/agent-plan.css +93 -0
  206. package/src/workflow/agent-plan.js +59 -0
  207. package/src/workflow/attachment-image.js +10 -3
  208. package/src/workflow/attachment-video.js +91 -0
  209. package/src/workflow/task-board.css +98 -0
  210. package/src/workflow/task-board.js +169 -0
  211. package/tools/check-eslint.mjs +2 -1
  212. package/tools/check-knip.mjs +2 -1
  213. package/tools/dev/pages/greybox-to-video.html +2 -3
  214. package/tools/dev/pages/previs-software.html +2 -2
  215. package/tools/dev/pages/privacy.html +3 -7
  216. package/tools/dev/pages/seedance-camera-control.html +2 -2
  217. package/tools/dev-full.mjs +11 -2
  218. package/tools/eslint-baseline.json +6 -6
  219. package/tools/knip-baseline.json +0 -11
  220. package/tools/loc-baseline.json +30 -31
  221. package/tools/metrics-snapshot.mjs +43 -5
  222. package/tools/render-start-thumbs.mjs +175 -0
  223. package/tools/run-tests.mjs +10 -17
  224. package/tools/sitemap.mjs +1 -1
  225. package/dist/assets/analytics-BibyE5ar.js +0 -6
  226. package/dist/assets/app-DhRJ-PBz.js +0 -5088
  227. package/dist/assets/app-f-fkV5uP.css +0 -1
  228. package/dist/assets/compile-worker-DqiA4AaN.js +0 -5
  229. package/dist/assets/first-shot-handoff-Qx7SKl38.js +0 -1
  230. package/dist/assets/landing-DYg87MDY.js +0 -1
  231. package/dist/assets/scene-asset-sync-DBYQBR0T.js +0 -20
  232. package/dist/assets/scene-asset-sync-_S8NQl5B.css +0 -1
  233. package/dist/assets/workflow-BSA22Ofr.js +0 -179
  234. package/dist/scenes/city-block.cclayproject +0 -1
  235. package/src/burn-in.js +0 -91
  236. package/src/camera-tutorial.jsx +0 -520
  237. package/src/facing-marks.js +0 -88
  238. package/src/first-shot-handoff.js +0 -44
  239. package/src/panels/motion.css +0 -197
  240. package/src/set-pieces.jsx +0 -183
  241. package/src/tutorial-analytics.js +0 -86
  242. package/src/tutorial-walk.js +0 -17
  243. package/src/use-camera-tutorial.js +0 -50
  244. package/src/use-semantic-state.js +0 -12
  245. package/tools/metrics/archive-traffic.mjs +0 -138
  246. package/tools/perf/ik-camera-drag.mjs +0 -138
@@ -0,0 +1,98 @@
1
+ /* Task mode in the chat (TaskInline.jsx): one quiet line per task, a count line under the header, the open task's
2
+ view over the chat, and the outliner mark on rows inside a running task. Studio tokens first, agent tokens after. */
3
+ .task-rows {
4
+ display: grid;
5
+ margin: 2px 0 2px 2px;
6
+ padding-left: 6px;
7
+ border-left: 2px solid var(--hairline, var(--agent-border, rgba(127, 127, 127, 0.25)));
8
+ min-width: 0;
9
+ }
10
+ .task-row {
11
+ display: flex;
12
+ align-items: center;
13
+ gap: 6px;
14
+ min-width: 0;
15
+ height: 24px;
16
+ padding: 0 6px;
17
+ border: 0;
18
+ border-radius: 6px;
19
+ background: none;
20
+ color: inherit;
21
+ font: inherit;
22
+ font-size: 12px;
23
+ text-align: left;
24
+ cursor: pointer;
25
+ }
26
+ .task-row:hover, .task-row:focus-visible { background: var(--chip-fill, rgba(127, 127, 127, 0.1)); outline: none; }
27
+ .task-row-title { flex: 0 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-weight: 550; }
28
+ .task-row-line { flex: 1 1 0; min-width: 64px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--text-2, var(--agent-text-muted)); }
29
+ .task-row[data-status="failed"] .task-row-line { color: var(--danger, var(--agent-alert)); }
30
+ .task-row[data-status="cancelled"], .task-row[data-status="done"] .task-row-title { opacity: 0.8; }
31
+
32
+ .task-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--text-3, var(--agent-text-dim)); }
33
+ [data-status="running"] > .task-dot, [data-status="starting"] > .task-dot { background: var(--select, var(--agent-accent)); animation: task-dot-pulse 1.2s ease-in-out infinite; }
34
+ [data-status="waiting"] > .task-dot, [data-status="queued"] > .task-dot { background: none; box-shadow: inset 0 0 0 1.5px var(--text-3, var(--agent-text-dim)); }
35
+ [data-status="done"] > .task-dot { background: var(--ok, var(--agent-ok)); }
36
+ [data-status="failed"] > .task-dot, [data-status="paused"] > .task-dot { background: var(--danger, var(--agent-alert)); }
37
+ @keyframes task-dot-pulse { 50% { opacity: 0.35; } }
38
+ @media (prefers-reduced-motion: reduce) { [data-status] > .task-dot { animation: none; } }
39
+
40
+ .task-strip { flex: none; border-bottom: 1px solid var(--hairline, var(--agent-border, rgba(127, 127, 127, 0.2))); padding: 0 8px; }
41
+ .task-strip-head {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 10px;
45
+ width: 100%;
46
+ height: 26px;
47
+ padding: 0 6px;
48
+ border: 0;
49
+ background: none;
50
+ color: var(--text-2, var(--agent-text-muted));
51
+ font: inherit;
52
+ font-size: 11.5px;
53
+ cursor: pointer;
54
+ }
55
+ .task-strip-count { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
56
+ .task-strip-caret { margin-left: auto; transition: transform 0.15s; }
57
+ .task-strip[data-expanded="true"] .task-strip-caret { transform: rotate(180deg); }
58
+ .task-strip-list { margin: 0 0 6px; max-height: 168px; overflow-y: auto; }
59
+
60
+ .task-detail {
61
+ position: absolute;
62
+ inset: 0;
63
+ z-index: 6;
64
+ display: flex;
65
+ flex-direction: column;
66
+ background: var(--surface-raised, var(--agent-bg));
67
+ border-radius: inherit;
68
+ }
69
+ .task-detail-head { display: flex; align-items: center; gap: 6px; height: 44px; padding: 0 10px; border-bottom: 1px solid var(--hairline, var(--agent-border)); }
70
+ .task-detail-back { display: inline-flex; align-items: center; gap: 2px; flex: none; }
71
+ .task-detail-title { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-weight: 600; }
72
+ .task-detail-body { flex: 1; min-height: 0; overflow-y: auto; display: grid; align-content: start; gap: 6px; padding: 10px 14px; font-size: 12px; }
73
+ .task-detail-meta { display: flex; align-items: center; gap: 6px; color: var(--text-2, var(--agent-text-muted)); }
74
+ .task-detail-meta .agent-ghost-button { margin-left: auto; }
75
+ .task-detail-scope, .task-detail-reason, .task-detail-note, .task-detail-summary, .task-detail-notice { margin: 0; overflow-wrap: anywhere; }
76
+ .task-detail-scope, .task-detail-reason, .task-detail-note, .task-detail-notice { color: var(--text-2, var(--agent-text-muted)); }
77
+ .task-detail-steer { padding: 8px 10px 10px; border-top: 1px solid var(--hairline, var(--agent-border)); }
78
+ .task-detail-steer input {
79
+ width: 100%;
80
+ box-sizing: border-box;
81
+ padding: 7px 10px;
82
+ border: 1px solid var(--hairline, var(--agent-border));
83
+ border-radius: 10px;
84
+ background: transparent;
85
+ color: inherit;
86
+ font: inherit;
87
+ }
88
+ .task-frames { margin: 4px 0 0; padding-left: 18px; display: grid; gap: 2px; }
89
+ .task-frames li[data-kind="error"] { color: var(--danger, var(--agent-alert)); }
90
+ .task-frames li[data-kind="reply"], .task-frames li[data-kind="said"] { list-style: none; margin-left: -18px; }
91
+ .task-frames-empty { margin: 0; color: var(--text-3, var(--agent-text-dim)); }
92
+
93
+ [data-task-scope]::after {
94
+ content: "●";
95
+ margin-left: 6px;
96
+ color: var(--select, var(--agent-accent));
97
+ font-size: 9px;
98
+ }
@@ -0,0 +1,169 @@
1
+ // The Studio task board in the agent panel (parallel tasks, P3). Task mode is a per-browser switch (the panel's Tasks
2
+ // toggle) or the server's COZYCLAY_STUDIO_TASKS flag: the chat then talks to the main agent (POST /agent/main), which
3
+ // answers at once and dispatches tasks; this board shows them. Pure parts here (the reducer, labels, the switch's
4
+ // storage); the network side is createTaskBoardClient; the view is TaskBoard.jsx.
5
+ // server events (GET /agent/main/<sessionId>/events): task.queued|waiting|started|resumed|paused|done|failed|cancelled
6
+ // carry the task's view { taskId, title, status, scope, reason?, turnId?, usage, summary? }; task.sceneChanged
7
+ // { taskId, keys, midCommand }; main.turn { turnId } (the main agent reporting on its own)
8
+ // editor events (the command bus): task.userEdit { taskId, keys } (the user changed something in its scope)
9
+ // compile activity (motion3-compile.js): { running, waiting } of { taskId, characterId }
10
+ import { ko } from "../locale.js";
11
+ import { sidecarUrl } from "../agent-image-client.js";
12
+ import { parseSseChunk } from "./agent-client.js";
13
+
14
+ const TASKS_KEY = "cozyclay.agent.tasks";
15
+ export function readStoredTaskMode(storage = globalThis.localStorage) {
16
+ try { return storage?.getItem(TASKS_KEY) === "on"; } catch { return false; }
17
+ }
18
+ export function storeTaskMode(enabled, storage = globalThis.localStorage) {
19
+ try { storage?.setItem(TASKS_KEY, enabled ? "on" : "off"); } catch { /* storage may be unavailable */ }
20
+ }
21
+
22
+ export const TASK_STATUS_COPY = {
23
+ queued: ko("Queued", "대기열"),
24
+ waiting: ko("Waiting for scope", "영역 대기"),
25
+ starting: ko("Starting", "시작 중"),
26
+ running: ko("Running", "작업 중"),
27
+ paused: ko("Paused: Studio lost", "일시정지: Studio 연결 끊김"),
28
+ done: ko("Done", "완료"),
29
+ failed: ko("Failed", "실패"),
30
+ cancelled: ko("Cancelled", "취소됨"),
31
+ };
32
+ export const ACTIVE_STATUSES = new Set(["queued", "waiting", "starting", "running", "paused"]);
33
+
34
+ export const emptyBoard = () => ({ order: [], tasks: {}, after: 0 });
35
+
36
+ /** The board after one event (server, editor or compile). Unknown events leave it as it is. */
37
+ export function reduceBoard(board, event) {
38
+ if (!event || typeof event !== "object") return board;
39
+ const after = Number.isFinite(event.eventSeq) ? Math.max(board.after, event.eventSeq) : board.after;
40
+ const patch = (taskId, change) => {
41
+ const known = board.tasks[taskId];
42
+ if (!known && !change.title) return { ...board, after };
43
+ const task = { notes: [], ...known, ...change, taskId };
44
+ return { ...board, after, order: known ? board.order : [...board.order, taskId], tasks: { ...board.tasks, [taskId]: task } };
45
+ };
46
+ const note = (taskId, text) => (board.tasks[taskId] ? patch(taskId, { notes: [...board.tasks[taskId].notes, text].slice(-4) }) : { ...board, after });
47
+ if (event.type === "task.sceneChanged") return note(event.taskId, ko(`The scene changed during a reload: ${event.keys.join(", ")}`, `새로고침 중에 장면이 바뀜: ${event.keys.join(", ")}`));
48
+ if (event.type === "task.userEdit") return note(event.taskId, ko(`You changed ${event.keys.join(", ")}; the task reads it again`, `${event.keys.join(", ")}을(를) 직접 바꿈: 작업이 다시 읽어요`));
49
+ if (event.type === "task.undone") return board.tasks[event.taskId] ? patch(event.taskId, { undone: true }) : board;
50
+ if (typeof event.type === "string" && event.type.startsWith("task.") && event.taskId && event.status) {
51
+ const { type, at, eventSeq, ...view } = event;
52
+ return patch(event.taskId, { ...view, ...(type === "task.started" || type === "task.resumed" ? { progress: "" } : {}) });
53
+ }
54
+ return { ...board, after };
55
+ }
56
+
57
+ /** A board read (GET .../board) merged under what the events already said. */
58
+ export function seedBoard(board, tasks = []) {
59
+ return tasks.reduce((acc, view) => (acc.tasks[view.taskId] ? acc : reduceBoard(acc, { type: "task.seed", ...view })), board);
60
+ }
61
+
62
+ /** The line a running card shows: the last tool it started, or the compile it waits for. */
63
+ export function progressLine(frame, labelOf = call => call.name) {
64
+ if (frame?.type === "tool.start") return labelOf(frame);
65
+ if (frame?.type === "error") return ko(`Error: ${frame.message ?? frame.code}`, `오류: ${frame.message ?? frame.code}`);
66
+ return null;
67
+ }
68
+
69
+ /** A task's wait or failure reason in the author's words: the server names the blocking task by id and title. */
70
+ export function taskReason(reason) {
71
+ // Greedy to the last ")": a title may hold parentheses of its own.
72
+ const after = /(?:waits for|held by) task \S+ \((.+)\)/.exec(String(reason ?? ""))?.[1];
73
+ if (after) return ko(`Starts after "${after}"`, `'${after}' 끝나면 시작`);
74
+ if (/scope is held/.test(String(reason ?? ""))) return ko("Starts when another task frees its part", "다른 작업이 끝나면 시작");
75
+ return reason ?? "";
76
+ }
77
+
78
+ /** The one line a task row shows after its title. */
79
+ export function taskLine(task) {
80
+ if (task.status === "running") return task.progress || ko("Working…", "작업 중…");
81
+ if (task.status === "done") return task.undone ? ko("Undone", "되돌림") : String(task.summary?.reply ?? "").split(/(?<=[.!?。])\s/)[0] || TASK_STATUS_COPY.done;
82
+ if (task.reason) return taskReason(task.reason);
83
+ return TASK_STATUS_COPY[task.status] ?? task.status;
84
+ }
85
+
86
+ const SCOPE_NAMES = { timeline: ko("timeline", "타임라인"), stage: ko("stage", "무대"), scene: ko("scene", "장면"), object: ko("object", "오브젝트"), character: ko("character", "캐릭터"), shot: ko("shot", "샷") };
87
+ /** A scope key (object:<id>, timeline, ...) as a word; the raw key stays in the tooltip. */
88
+ export const scopeLabel = key => SCOPE_NAMES[String(key).split(":")[0]] ?? String(key);
89
+
90
+ /** Rows by state for the status line: what needs a look first, then what runs, waits and ended. */
91
+ export function boardGroups(board) {
92
+ const groups = { failed: [], running: [], waiting: [], done: [] };
93
+ for (const id of board.order) {
94
+ const task = board.tasks[id];
95
+ const group = task.status === "failed" || task.status === "paused" ? "failed" : task.status === "running" || task.status === "starting" ? "running" : ACTIVE_STATUSES.has(task.status) ? "waiting" : "done";
96
+ groups[group].push(task);
97
+ }
98
+ return groups;
99
+ }
100
+
101
+ /** The task ids a turn's dispatch_task steps started, in order (a duplicate points at the task it repeats). */
102
+ export const dispatchedTaskIds = steps => (steps ?? []).filter(step => step.kind === "tool" && step.name === "dispatch_task")
103
+ .map(step => step.result?.taskId ?? step.result?.duplicateOf).filter(Boolean);
104
+
105
+ /** What the task view shares between the headless board (TaskBoard.jsx) and the rows in the chat (TaskInline.jsx). */
106
+ function createTaskViewStore() {
107
+ let state = { board: emptyBoard(), activity: null, client: null, undo: null, open: null };
108
+ const listeners = new Set();
109
+ return {
110
+ get: () => state,
111
+ set: patch => { state = { ...state, ...patch }; for (const listener of listeners) listener(); },
112
+ subscribe: listener => { listeners.add(listener); return () => listeners.delete(listener); },
113
+ };
114
+ }
115
+ // One agent panel per page, so one view.
116
+ export const taskView = createTaskViewStore();
117
+
118
+ /** "$0.12" for a task's cost, or "" when nothing is known yet. */
119
+ export const costLabel = usage => (Number.isFinite(usage?.cost) && usage.cost > 0 ? `$${usage.cost.toFixed(2)}` : "");
120
+
121
+ /** The compile a task waits for: how many of its compiles wait and how many run. */
122
+ export function compileState(activity, taskId) {
123
+ const mine = list => (list ?? []).filter(meta => meta?.taskId === taskId).length;
124
+ return { waiting: mine(activity?.waiting), running: mine(activity?.running) };
125
+ }
126
+
127
+ /** The open scopes the editor's bus reports: taskId -> keys (task.begun / task.scope / task.finished|cancelled). */
128
+ export function reduceScopes(scopes, event) {
129
+ if (event?.type === "task.begun" || event?.type === "task.scope") return { ...scopes, [event.taskId]: event.scope ?? [] };
130
+ if (event?.type === "task.finished" || event?.type === "task.cancelled") { const { [event.taskId]: _gone, ...rest } = scopes; return rest; }
131
+ return scopes;
132
+ }
133
+
134
+ /**
135
+ * The sidecar side of the board. All requests are same-origin with the Studio session's owner cookie.
136
+ * fetchImpl(url, init) -> Response; url(path) -> the sidecar URL.
137
+ */
138
+ export function createTaskBoardClient({ fetchImpl = globalThis.fetch?.bind(globalThis), url = sidecarUrl } = {}) {
139
+ const json = async (path, init) => {
140
+ const response = await fetchImpl(url(path), { credentials: "same-origin", headers: { accept: "application/json", "content-type": "application/json" }, ...init });
141
+ const body = await response.json().catch(() => ({}));
142
+ if (!response.ok) throw Object.assign(new Error(body?.error?.message ?? `${response.status}`), { status: response.status, code: body?.error?.code });
143
+ return body;
144
+ };
145
+ /** Streams an SSE route to onEvent until it ends or signal aborts. */
146
+ async function stream(path, onEvent, signal) {
147
+ const response = await fetchImpl(url(path), { credentials: "same-origin", headers: { accept: "text/event-stream" }, signal });
148
+ if (!response.ok || !response.body) throw Object.assign(new Error(`${response.status}`), { status: response.status });
149
+ const reader = response.body.getReader(), decoder = new TextDecoder();
150
+ let buffer = "";
151
+ try {
152
+ while (true) {
153
+ const { value, done } = await reader.read();
154
+ if (done) break;
155
+ const parsed = parseSseChunk(buffer + decoder.decode(value, { stream: true }));
156
+ buffer = parsed.tail;
157
+ for (const event of parsed.events) onEvent(event);
158
+ }
159
+ } finally { reader.releaseLock(); }
160
+ }
161
+ const id = value => encodeURIComponent(value);
162
+ return {
163
+ board: sessionId => json(`/agent/main/${id(sessionId)}/board`),
164
+ events: (sessionId, after, onEvent, signal) => stream(`/agent/main/${id(sessionId)}/events?after=${after}`, onEvent, signal),
165
+ frames: (turnId, onEvent, signal) => stream(`/agent/turn/${id(turnId)}/events?after=0`, onEvent, signal),
166
+ cancel: taskId => json(`/agent/task/${id(taskId)}/cancel`, { method: "POST", body: "{}" }),
167
+ steer: (taskId, text) => json(`/agent/task/${id(taskId)}/steer`, { method: "POST", body: JSON.stringify({ text }) }),
168
+ };
169
+ }
@@ -10,7 +10,8 @@ import { fileURLToPath } from "node:url";
10
10
  const root = join(dirname(fileURLToPath(import.meta.url)), "..");
11
11
  const baselinePath = join(root, "tools", "eslint-baseline.json");
12
12
 
13
- const run = spawnSync(join(root, "node_modules", ".bin", "eslint"), [".", "--format", "json"], {
13
+ // Run the package's JS entry through node: the .bin shim is a .cmd on Windows, which spawnSync cannot start without a shell.
14
+ const run = spawnSync(process.execPath, [join(root, "node_modules", "eslint", "bin", "eslint.js"), ".", "--format", "json"], {
14
15
  cwd: root,
15
16
  encoding: "utf8",
16
17
  maxBuffer: 512 * 1024 * 1024,
@@ -11,7 +11,8 @@ const root = join(dirname(fileURLToPath(import.meta.url)), '..');
11
11
  const baselinePath = join(root, 'tools', 'knip-baseline.json');
12
12
 
13
13
  // knip exits 1 when it finds issues; only a missing/unparseable report is a failure.
14
- const run = spawnSync(join(root, 'node_modules', '.bin', 'knip'), ['--reporter', 'json', '--no-progress'], {
14
+ // Run the package's JS entry through node: the .bin shim is a .cmd on Windows, which spawnSync cannot start without a shell.
15
+ const run = spawnSync(process.execPath, [join(root, 'node_modules', 'knip', 'bin', 'knip.js'), '--reporter', 'json', '--no-progress'], {
15
16
  cwd: root,
16
17
  encoding: 'utf8',
17
18
  maxBuffer: 256 * 1024 * 1024,
@@ -107,9 +107,8 @@
107
107
  <h2>Do it in the browser</h2>
108
108
  <p>
109
109
  CozyClay is open source previs that runs in a Chromium tab. It exists for exactly this loop: block,
110
- camera, export, generate. The <a href="/#try">playground on the home page</a> walks through the camera
111
- part in seven moves on the same city block you see in the reels, and <code>npx cozyclay --scene city-block</code>
112
- opens that set on your machine.
110
+ camera, export, generate. The <a href="/#try">playground on the home page</a> runs the studio in the page, and
111
+ <code>npx cozyclay</code> runs it on your machine.
113
112
  </p>
114
113
  <div class="cta">
115
114
  <a class="btn primary" href="/#try">Try it in your browser</a>
@@ -64,8 +64,8 @@
64
64
  <p>
65
65
  The honest split is time, not money. Blender can do everything on this page and will take an
66
66
  afternoon to learn just the camera. CozyClay does one thing (block, camera, cut, export) and expects
67
- you to be productive in the first ten minutes; there is a <a href="/#try">seven-step tutorial on the home page</a>
68
- that takes about that long.
67
+ you to be productive in the first ten minutes; the <a href="/#try">playground on the home page</a>
68
+ runs it in the page.
69
69
  </p>
70
70
  </section>
71
71
 
@@ -86,12 +86,11 @@ cclay telemetry on</code></pre>
86
86
  <thead><tr><th>Event</th><th>Purpose</th></tr></thead>
87
87
  <tbody>
88
88
  <tr><td>install:first_launch, app:session_started, app:session_ended</td><td>Counting installs and sessions; duration and action counts are bucketed. install:first_launch may carry heard_from (x, hn, reddit, github, friend, other, site or playground), from the first-launch prompt or a copied <code>npx cozyclay --via</code> command. app:session_ended also carries per-session edit gesture counts in five groups, pose_edit_bucket, camera_edit_bucket, object_edit_bucket, shot_edit_bucket and prompt_edit_bucket, each bucketed (0, 1-3, 4-10 or gte11); never what was edited</td></tr>
89
- <tr><td>feature:used</td><td>One signal per feature per session: a fixed feature name such as pose_edit (a real bone or pose change), pose_save (saving a pose to the library), fal_motion_open (opening the AI video A/B card), camera_fly or export_video</td></tr>
89
+ <tr><td>feature:used</td><td>One signal per feature per session: a fixed feature name such as pose_edit (a real bone or pose change), pose_save (saving a pose to the library), camera_fly or export_video</td></tr>
90
90
  <tr><td>$pageview, scene:created, scene:loaded, craft:first_action, activation:completed</td><td>Funnel and drop-off analysis</td></tr>
91
91
  <tr><td>craft:first_edit</td><td>First successful Studio authoring change per editor mount; only a fixed edit-kind label and definition version 1 (not camera navigation, playback, loading, restore, or undo/redo)</td></tr>
92
92
  <tr><td>playground:first_edit</td><td>The same versioned first-edit definition for Playground, counted separately from Studio; no prompts or scene contents</td></tr>
93
93
  <tr><td>project:saved, project:opened</td><td>Whether people keep their work (object counts and ages are bucketed; never names or paths)</td></tr>
94
- <tr><td>motion:backend_state</td><td>Session capability baseline: backend (none, local_kimodo or hosted) and boolean host_configured, never a host address</td></tr>
95
94
  <tr><td>motion:generate_requested</td><td>Explicit generation intent, not prompt-block editing: initiating surface (timeline, line_edit, trail, mcp, fal_card or agent), input_mode (prompt, pose, edit, a_to_b or still) and an ephemeral random request_id</td></tr>
96
95
  <tr><td>motion:preflight_blocked</td><td>Request readiness refusal: request_id, surface and normalized reason (unconfigured, unreachable, unsupported_route, locked or missing_input). locked means the AI video route is not enabled for the account yet; missing_input means the A/B captures were incomplete</td></tr>
97
96
  <tr><td>motion:preflight_passed</td><td>Request readiness accepted, before job start: request_id, surface and backend (none, local_kimodo, hosted or fal)</td></tr>
@@ -117,10 +116,6 @@ cclay telemetry on</code></pre>
117
116
  <tr><td>device:profile</td><td>Once per session, whether the studio can run smoothly: gpu_vendor (nvidia, amd, intel, apple, qualcomm, arm, software, other or unknown), gpu_class (discrete, integrated, software or unknown), webgl (webgl2, webgl1 or none), cpu_bucket (1-4, 5-8, 9-16, gte17 or unknown), memory_bucket (lt4, 4-7, gte8 or unknown) and frame_rate_bucket (lt20, 20-40, 40-55, gte55 or unknown) from one three-second sample. The GPU renderer string and exact hardware values never leave the browser.</td></tr>
118
117
  <tr><td>survey:use_case</td><td>An optional question shown at most once after a successful export, only while telemetry is on: use_case (animation, film, game, ad_mv, personal, other or skip) and team (team, solo or skip). Closed choices only; no free text.</td></tr>
119
118
  <tr><td>hosted:composer_viewed, hosted:login_started, hosted:ticket_created, hosted:result_opened, hosted:opened_in_studio</td><td>Hosted demo funnel</td></tr>
120
- <tr><td>tutorial:started</td><td>Camera tutorial attempts on surface (studio or playground), with numeric tutorial_version 1 and start_source (query, settings or landing). Restarting starts a new attempt; no tutorial attempt identifier or user text is sent.</td></tr>
121
- <tr><td>tutorial:step_entered, tutorial:dismissed</td><td>Entered steps and explicit dismissal of an unfinished tutorial; surface (studio or playground), numeric tutorial_version 1 and step_kind (fly, walk, dolly, orbit, shot, rail or play). Missing completion can indicate abandonment or missing telemetry; browser close does not promise a dismissal event.</td></tr>
122
- <tr><td>tutorial:step_completed</td><td>One completion per step per attempt; surface (studio or playground), numeric tutorial_version 1, step_kind (fly, walk, dolly, orbit, shot, rail or play) and elapsed_bucket (lt1s, 1-3s, 3-10s, 10-30s or gte30s) since entering that step. No raw timing, prompts, scene contents, names or paths.</td></tr>
123
- <tr><td>tutorial:completed</td><td>All seven steps completed; surface (studio or playground), numeric tutorial_version 1 and elapsed_bucket (lt1s, 1-3s, 3-10s, 10-30s or gte30s) since tutorial start. Sample loading and camera playback do not count as a user edit.</td></tr>
124
119
  </tbody>
125
120
  </table>
126
121
  </div>
@@ -132,7 +127,8 @@ cclay telemetry on</code></pre>
132
127
  <section>
133
128
  <h2>The hosted demo (Google sign-in)</h2>
134
129
  <p>
135
- The hosted motion demo asks you to sign in with Google so that the daily generation cap can be
130
+ The hosted demo is not open to the public right now. This section describes what it handled while it
131
+ was running. The hosted motion demo asks you to sign in with Google so that the daily generation cap can be
136
132
  enforced per account. It receives your Google account's email address and basic profile only, stores
137
133
  a session cookie signed by our server, and keeps your prompt and the generated result for up to 30
138
134
  days so the ticket link keeps working. Results are stored on Cloudflare R2 and deleted after that
@@ -87,8 +87,8 @@
87
87
  Any 3D tool that renders flat can make the reference. Blender works with a clay material and a
88
88
  workbench render. CozyClay, open source and in the browser, exports the flat clip directly with the
89
89
  delivery ratio and a camera timeline built in; the reel at the top of this page came out of it.
90
- The <a href="/#try">home-page playground</a> walks through blocking a shot and drawing a dolly rail in
91
- seven moves, and <code>npx cozyclay --scene city-block</code> opens that set locally.
90
+ The <a href="/#try">home-page playground</a> runs the studio in the page, and
91
+ <code>npx cozyclay</code> runs it locally.
92
92
  </p>
93
93
  <div class="cta">
94
94
  <a class="btn primary" href="/#try">Try it in your browser</a>
@@ -45,7 +45,6 @@ await new Promise((resolvePromise, reject) => {
45
45
  });
46
46
 
47
47
  const children = [];
48
- const removeSignalCleanup = installSignalCleanup(() => children, () => {});
49
48
  const agentHandler = createAgentHandler({ port: mainPort });
50
49
  const oauthServer = createServer((req, res) => {
51
50
  const path = (req.url || "").split("?")[0];
@@ -62,6 +61,16 @@ const oauthPort = configuredOAuthPort ? Number(configuredOAuthPort) : 0;
62
61
  await new Promise((resolvePromise, reject) => { oauthServer.once("error", reject); oauthServer.listen({ port: oauthPort, host: "127.0.0.1" }, resolvePromise); });
63
62
  const actualOAuthPort = oauthServer.address().port;
64
63
 
64
+ // The oauth/agent HTTP server and the agent's live hub (its own listener on
65
+ // COZYCLAY_LIVE_PORT) keep the event loop alive, so shutdown must close both
66
+ // (dropping open keep-alive/SSE sockets) or the process never exits.
67
+ const closeServers = async () => {
68
+ const httpClosed = new Promise((resolvePromise) => oauthServer.close(() => resolvePromise()));
69
+ oauthServer.closeAllConnections();
70
+ await Promise.allSettled([httpClosed, agentHandler.close()]);
71
+ };
72
+ const removeSignalCleanup = installSignalCleanup(() => children, closeServers);
73
+
65
74
  const vite = spawnOwned(process.execPath, ["node_modules/vite/bin/vite.js", ...viteArgs], {
66
75
  cwd: REPO,
67
76
  env: {
@@ -77,7 +86,7 @@ const first = await Promise.race(
77
86
  );
78
87
 
79
88
  removeSignalCleanup();
80
- await new Promise((resolvePromise) => oauthServer.close(resolvePromise));
89
+ await closeServers();
81
90
  await Promise.allSettled(
82
91
  children.filter((child) => child !== first.child).map((child) => terminateOwned(child)),
83
92
  );
@@ -7,16 +7,16 @@
7
7
  "no-regex-spaces": 2,
8
8
  "no-undef": 24,
9
9
  "no-unsafe-finally": 2,
10
- "no-unused-vars": 100,
11
- "no-useless-assignment": 10,
10
+ "no-unused-vars": 98,
11
+ "no-useless-assignment": 9,
12
12
  "no-useless-escape": 6,
13
13
  "preserve-caught-error": 6,
14
- "react-hooks/immutability": 88,
15
- "react-hooks/preserve-manual-memoization": 31,
14
+ "react-hooks/immutability": 81,
15
+ "react-hooks/preserve-manual-memoization": 28,
16
16
  "react-hooks/purity": 5,
17
- "react-hooks/refs": 111,
17
+ "react-hooks/refs": 102,
18
18
  "react-hooks/rules-of-hooks": 2,
19
- "react-hooks/set-state-in-effect": 30,
19
+ "react-hooks/set-state-in-effect": 26,
20
20
  "react-hooks/use-memo": 1,
21
21
  "require-yield": 2
22
22
  }
@@ -7,14 +7,7 @@
7
7
  "export mcp/image-file.mjs#REFERENCE_MAX_BYTES",
8
8
  "export src/asset-pane.jsx#CHARACTER_ASSETS",
9
9
  "export src/asset-pane.jsx#FolderGrid",
10
- "export src/burn-in.js#burnInCapture",
11
10
  "export src/camera-block.js#CAMERA_MODES",
12
- "export src/camera-tutorial.jsx#CAMERA_TUTORIAL_STEPS",
13
- "export src/camera-tutorial.jsx#GestureCue",
14
- "export src/camera-tutorial.jsx#TUTORIAL_BEACONS",
15
- "export src/camera-tutorial.jsx#TutorialBeacon",
16
- "export src/camera-tutorial.jsx#WALK_KEYS",
17
- "export src/camera-tutorial.jsx#default",
18
11
  "export src/domains/motion.js#createMotionDomain",
19
12
  "export src/domains/objects.js#createObjectsDomain",
20
13
  "export src/grid-view.js#STAGE_BACKGROUND_LIGHT",
@@ -23,13 +16,10 @@
23
16
  "export src/mesh-sniff.js#looksLikeObj",
24
17
  "export src/mesh-sniff.js#readFbxVersion",
25
18
  "export src/object-catalog.jsx#default",
26
- "export src/object-path.js#MAX_PATH_HEIGHT",
27
19
  "export src/project.js#storeProjectHandle",
28
20
  "export src/range-pin-panel.jsx#RANGE_PIN_TRACKS",
29
21
  "export src/scene-asset-cache.js#loadAssetTexture",
30
22
  "export src/scene-mesh-cache.js#loadMeshScene",
31
- "export src/scenes.js#CHARACTER_SCALE_MAX",
32
- "export src/scenes.js#CHARACTER_SCALE_MIN",
33
23
  "export src/scenes.js#LEGACY_FRAME_FPS",
34
24
  "export src/scenes.js#toTimelineFrame",
35
25
  "export src/stage-attach-frames.js#ATTACH_BONE_NAMES",
@@ -55,7 +45,6 @@
55
45
  "export src/stage-editor-gizmos.jsx#GizmoLabel",
56
46
  "export src/stage-editor-gizmos.jsx#prepareRailBend",
57
47
  "export src/ui-scrub.js#SCRUB_RANGE_STEPS",
58
- "export src/use-semantic-state.js#useSemanticState",
59
48
  "export src/workflow/AgentPanel.jsx#AGENT_PANEL_RAIL_WIDTH",
60
49
  "export src/workflow/AgentPanel.jsx#AGENT_STATES",
61
50
  "export src/workflow/video-contract.js#falVideoContract",
@@ -1,34 +1,33 @@
1
1
  {
2
- "bin/agent/agent-routes.mjs": 865,
3
- "bin/agent/agent-runner.mjs": 496,
4
- "bin/agent/providers.mjs": 409,
2
+ "bin/agent/agent-routes.mjs": 804,
3
+ "bin/agent/agent-runner.mjs": 495,
4
+ "bin/agent/providers.mjs": 338,
5
5
  "bin/agent/session-store.mjs": 324,
6
6
  "bin/agent/supervisor.mjs": 283,
7
7
  "bin/agent/video-adapters.mjs": 334,
8
- "bin/cozyclay.mjs": 440,
8
+ "bin/cozyclay.mjs": 438,
9
9
  "bin/live/cli.mjs": 456,
10
10
  "mcp/live-hub.mjs": 529,
11
11
  "mcp/tool-handlers.mjs": 1730,
12
- "mcp/verify-live-batch.mjs": 486,
12
+ "mcp/verify-live-batch.mjs": 485,
13
13
  "mcp/verify-live-capture.mjs": 268,
14
14
  "mcp/verify-live-controller.mjs": 340,
15
15
  "mcp/verify-live-p0.mjs": 264,
16
16
  "mcp/verify-live.mjs": 416,
17
- "src/analytics.js": 1169,
18
- "src/App.jsx": 4711,
19
- "src/asset-pane.jsx": 648,
17
+ "src/analytics.js": 1038,
18
+ "src/App.jsx": 4476,
19
+ "src/asset-pane.jsx": 643,
20
20
  "src/camera-follow.js": 505,
21
21
  "src/camera-move.js": 289,
22
- "src/camera-tutorial.jsx": 443,
23
- "src/command-bus.js": 378,
24
- "src/controls.jsx": 340,
22
+ "src/command-bus.js": 356,
23
+ "src/controls.jsx": 330,
25
24
  "src/domains/cast.js": 717,
26
- "src/domains/motion.js": 812,
25
+ "src/domains/motion.js": 806,
27
26
  "src/domains/objects.js": 907,
28
- "src/domains/scenes.js": 754,
27
+ "src/domains/scenes.js": 740,
29
28
  "src/domains/shots.js": 411,
30
- "src/dualview.jsx": 371,
31
- "src/hierarchy-panel.jsx": 779,
29
+ "src/dualview.jsx": 365,
30
+ "src/hierarchy-panel.jsx": 777,
32
31
  "src/live-control.js": 338,
33
32
  "src/matte-editor.js": 486,
34
33
  "src/matte.js": 472,
@@ -36,41 +35,41 @@
36
35
  "src/motion/fix-collisions.js": 748,
37
36
  "src/motion/ik.js": 1135,
38
37
  "src/motion/range-pin.js": 277,
39
- "src/motion/timeline.jsx": 2047,
40
- "src/object-gizmo.jsx": 824,
38
+ "src/motion/timeline.jsx": 2042,
39
+ "src/object-gizmo.jsx": 740,
41
40
  "src/object-path.js": 410,
42
41
  "src/panels/ObjectTransformPanel.jsx": 545,
43
42
  "src/panels/SculptPanel.jsx": 370,
44
43
  "src/planview.jsx": 887,
45
44
  "src/poses.js": 321,
46
45
  "src/posestudio.jsx": 1240,
47
- "src/project-browser.jsx": 401,
46
+ "src/project-browser.jsx": 371,
48
47
  "src/project.js": 603,
49
- "src/props.jsx": 417,
48
+ "src/props.jsx": 414,
50
49
  "src/range-pin-panel.jsx": 305,
51
50
  "src/scene-assets.js": 400,
52
51
  "src/scene-mesh.js": 350,
53
- "src/scene-objects.js": 1136,
54
- "src/scenes.js": 387,
52
+ "src/scene-objects.js": 1134,
53
+ "src/scenes.js": 385,
55
54
  "src/sculpt-compare.js": 357,
56
55
  "src/sculpt-recipe.js": 363,
57
56
  "src/sculpt-run.js": 309,
58
57
  "src/shell/BoardSlot.jsx": 316,
59
58
  "src/shell/BottomDock.jsx": 416,
60
59
  "src/shell/DetailsSlot.jsx": 334,
61
- "src/shell/MenuBar.jsx": 640,
62
- "src/shell/PreferencesDialog.jsx": 466,
60
+ "src/shell/MenuBar.jsx": 632,
61
+ "src/shell/PreferencesDialog.jsx": 269,
63
62
  "src/shell/ViewportToolbar.jsx": 567,
64
- "src/shot-authoring.js": 348,
65
- "src/stage-canvas-rigs.jsx": 352,
63
+ "src/shot-authoring.js": 345,
64
+ "src/stage-canvas-rigs.jsx": 350,
66
65
  "src/stage-editor-gizmos.jsx": 1116,
67
- "src/studio-agent-commands.js": 492,
68
- "src/studio-agent-protocol.js": 486,
66
+ "src/studio-agent-commands.js": 487,
67
+ "src/studio-agent-protocol.js": 480,
69
68
  "src/studio-app-binding.js": 346,
70
- "src/ui.jsx": 393,
71
- "src/workflow/agent-client.js": 1386,
72
- "src/workflow/AgentPanel.jsx": 880,
69
+ "src/ui.jsx": 385,
70
+ "src/workflow/agent-client.js": 1369,
71
+ "src/workflow/AgentPanel.jsx": 811,
73
72
  "src/workflow/WorkflowBuilder.jsx": 592,
74
73
  "tools/qa/css-rule-usage.mjs": 251,
75
- "tools/run-tests.mjs": 448
74
+ "tools/run-tests.mjs": 441
76
75
  }