cozyclay 3.0.2 → 3.1.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 (221) hide show
  1. package/CHANGELOG.md +95 -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 +22 -56
  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/session-store.mjs +2 -2
  10. package/bin/agent/studio-prompt.mjs +1 -1
  11. package/bin/agent/studio-task-routes.mjs +185 -0
  12. package/bin/agent/studio-tasks.mjs +156 -0
  13. package/bin/agent/studio-tools.mjs +3 -2
  14. package/bin/agent/studio-turn-transaction.mjs +76 -0
  15. package/bin/cozyclay.mjs +5 -7
  16. package/dist/app/index.html +7 -6
  17. package/dist/assets/TaskBoard-DdDXMgZm.js +1 -0
  18. package/dist/assets/app-B2BpfCl2.js +5088 -0
  19. package/dist/assets/app-BlwOpest.css +1 -0
  20. package/dist/assets/compile-worker-D0pKioa0.js +5 -0
  21. package/dist/assets/landing-y3EqDgSZ.js +1 -0
  22. package/dist/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
  23. package/dist/assets/motion3-compile-B887n77J.js +1 -0
  24. package/dist/assets/scene-asset-sync-COGOlF5T.css +1 -0
  25. package/dist/assets/scene-asset-sync-CQzEdsqu.js +13 -0
  26. package/dist/assets/task-board-BKaM7d-9.js +14 -0
  27. package/dist/assets/workflow-B8Dhzexj.js +179 -0
  28. package/dist/assets/x-bot-tpose-body-BusyR3eg.json +1 -0
  29. package/dist/assets/y-bot-tpose-body-Dy1VKChZ.json +1 -0
  30. package/dist/cozyclay-package.json +1 -1
  31. package/dist/greybox-to-video/index.html +2 -3
  32. package/dist/index.html +7 -57
  33. package/dist/llms.txt +2 -1
  34. package/dist/previs-software/index.html +2 -2
  35. package/dist/privacy/index.html +3 -7
  36. package/dist/seedance-camera-control/index.html +2 -2
  37. package/dist/sitemap.xml +5 -5
  38. package/dist/sw.js +5 -1
  39. package/dist/thumbs/blank-stage-dark.webp +0 -0
  40. package/dist/thumbs/blank-stage-light.webp +0 -0
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/mesh-file.mjs +3 -1
  43. package/mcp/server.mjs +3 -0
  44. package/package.json +3 -3
  45. package/src/App.jsx +50 -281
  46. package/src/analytics.js +3 -148
  47. package/src/app-stage.jsx +1 -1
  48. package/src/asset-pane.jsx +5 -10
  49. package/src/auto-flat.js +10 -0
  50. package/src/bus-task-scopes.js +69 -0
  51. package/src/bus-task-targets.js +48 -0
  52. package/src/bus-tasks.js +181 -0
  53. package/src/command-bus.js +15 -40
  54. package/src/command-history.js +36 -0
  55. package/src/commands/cast.js +10 -3
  56. package/src/commands/motion.js +2 -1
  57. package/src/commands/motion3-compile.js +51 -27
  58. package/src/commands/motion3.js +13 -7
  59. package/src/commands/project.js +1 -1
  60. package/src/commands/shared.js +9 -0
  61. package/src/commands/shot.js +16 -4
  62. package/src/controls.jsx +0 -11
  63. package/src/cuts.js +11 -0
  64. package/src/domains/cast.js +3 -2
  65. package/src/domains/motion.js +3 -10
  66. package/src/domains/playback.js +32 -0
  67. package/src/domains/scenes.js +3 -22
  68. package/src/domains/shots.js +5 -5
  69. package/src/dualview.jsx +11 -15
  70. package/src/hierarchy-panel.css +18 -4
  71. package/src/hierarchy-panel.jsx +12 -11
  72. package/src/kits/held.js +3 -2
  73. package/src/light-blockers.js +2 -2
  74. package/src/main.jsx +5 -0
  75. package/src/motion/ground.js +5 -14
  76. package/src/motion/playback-frame-store.js +79 -0
  77. package/src/motion/playback-probe.js +16 -0
  78. package/src/motion/playback-subject.js +7 -0
  79. package/src/motion/timeline.css +43 -5
  80. package/src/motion/timeline.jsx +4 -3
  81. package/src/motion3/body.js +37 -5
  82. package/src/motion3/carried.js +11 -3
  83. package/src/motion3/compile-job.js +1 -1
  84. package/src/motion3/compiler/anchors.js +33 -5
  85. package/src/motion3/compiler/compile.js +27 -4
  86. package/src/motion3/compiler/finger-close.js +137 -0
  87. package/src/motion3/compiler/grip.js +9 -3
  88. package/src/motion3/compiler/limbs.js +18 -3
  89. package/src/motion3/compiler/pose.js +6 -0
  90. package/src/motion3/compiler/socket-grip.js +34 -10
  91. package/src/motion3/compiler/validate-objects.js +17 -2
  92. package/src/motion3/compiler/validate-parts.js +2 -1
  93. package/src/motion3/compiler/vocab.js +4 -4
  94. package/src/motion3/describe.js +2 -2
  95. package/src/motion3/docs.js +1 -1
  96. package/src/motion3/fix/index.js +7 -3
  97. package/src/motion3/flight.js +38 -0
  98. package/src/motion3/free-motion.js +10 -7
  99. package/src/motion3/load.js +125 -0
  100. package/src/motion3/mass.js +23 -0
  101. package/src/motion3/object-keys.js +11 -6
  102. package/src/motion3/object-pose.js +11 -3
  103. package/src/motion3/pipeline.js +5 -0
  104. package/src/motion3/qa/explain.js +3 -1
  105. package/src/motion3/qa/grasp-checks.js +123 -0
  106. package/src/motion3/qa/hints-grasp.js +71 -0
  107. package/src/motion3/qa/index.js +3 -1
  108. package/src/motion3/qa/object-checks.js +7 -2
  109. package/src/motion3/qa/patch.js +2 -1
  110. package/src/motion3/qa/spec-checks.js +21 -1
  111. package/src/motion3/schema/fields.js +3 -3
  112. package/src/motion3/schema/topics.js +11 -10
  113. package/src/motion3/schema/types.js +5 -1
  114. package/src/motion3/seat.js +84 -0
  115. package/src/motion3/settle.js +11 -6
  116. package/src/motion3/shapes.js +70 -0
  117. package/src/motion3/skills/basic.js +61 -13
  118. package/src/motion3/skills/catch.js +71 -0
  119. package/src/motion3/skills/index.js +13 -4
  120. package/src/motion3/skills/info.js +14 -1
  121. package/src/motion3/skills/reach-human.js +139 -0
  122. package/src/motion3/skills/stance.js +92 -0
  123. package/src/motion3/skills/throw.js +72 -0
  124. package/src/motion3/sockets.js +21 -4
  125. package/src/motion3/views.js +28 -5
  126. package/src/object-catalog.jsx +0 -4
  127. package/src/object-gizmo.jsx +2 -1
  128. package/src/object-path.js +1 -1
  129. package/src/panels/ProjectPanel.jsx +42 -21
  130. package/src/panels/details.css +15 -4
  131. package/src/popover.js +37 -0
  132. package/src/posestudio.jsx +15 -10
  133. package/src/project-browser-art.jsx +19 -0
  134. package/src/project-browser.css +74 -121
  135. package/src/project-browser.jsx +36 -60
  136. package/src/props.jsx +4 -7
  137. package/src/retired-kinds.js +37 -0
  138. package/src/scene-objects.js +3 -7
  139. package/src/scenes.js +4 -5
  140. package/src/settings-menu.jsx +1 -20
  141. package/src/shell/BottomDock.jsx +9 -9
  142. package/src/shell/MenuBar.jsx +1 -9
  143. package/src/shell/PreferencesDialog.jsx +3 -4
  144. package/src/shell/ViewportToolbar.jsx +3 -3
  145. package/src/shell/add-camera-shot.js +11 -0
  146. package/src/shell/glass-regions.css +3 -2
  147. package/src/shell/glass.css +0 -5
  148. package/src/shell/lens-scrub-math.js +51 -0
  149. package/src/shell/lens-scrub.css +73 -0
  150. package/src/shell/lens-scrub.jsx +98 -0
  151. package/src/shell/mode.css +6 -5
  152. package/src/shell/preferences.css +1 -32
  153. package/src/shell/shell.css +18 -1
  154. package/src/shell/studio-shell-context.js +5 -0
  155. package/src/shot-frame.js +26 -0
  156. package/src/stage-canvas-rigs.jsx +7 -8
  157. package/src/stage-characters.jsx +26 -0
  158. package/src/stage-gizmo.js +43 -0
  159. package/src/stage-labels-ko.js +0 -6
  160. package/src/stage-startup-defaults.js +3 -9
  161. package/src/starter-scenes.js +5 -11
  162. package/src/studio-agent-commands.js +2 -2
  163. package/src/studio-agent-protocol.js +2 -7
  164. package/src/studio-app-binding.js +3 -3
  165. package/src/studio-elements.js +1 -1
  166. package/src/studio-error-codes.js +1 -1
  167. package/src/studio-motion-visual.js +4 -4
  168. package/src/styles/themes.css +13 -0
  169. package/src/styles.css +6 -674
  170. package/src/text/text-object.jsx +1 -1
  171. package/src/ui.jsx +8 -16
  172. package/src/use-embed-bridge.js +3 -6
  173. package/src/use-render-activity.js +3 -1
  174. package/src/view-ghosts.jsx +5 -3
  175. package/src/viewport-visible-rect.js +37 -0
  176. package/src/workflow/AgentPanel.jsx +75 -42
  177. package/src/workflow/AgentProgressCard.jsx +1 -1
  178. package/src/workflow/TaskBoard.jsx +102 -0
  179. package/src/workflow/TaskInline.jsx +114 -0
  180. package/src/workflow/agent-client.js +11 -25
  181. package/src/workflow/agent-panel.css +14 -1
  182. package/src/workflow/attachment-image.js +10 -3
  183. package/src/workflow/attachment-video.js +91 -0
  184. package/src/workflow/task-board.css +98 -0
  185. package/src/workflow/task-board.js +169 -0
  186. package/tools/check-eslint.mjs +2 -1
  187. package/tools/check-knip.mjs +2 -1
  188. package/tools/dev/pages/greybox-to-video.html +2 -3
  189. package/tools/dev/pages/previs-software.html +2 -2
  190. package/tools/dev/pages/privacy.html +3 -7
  191. package/tools/dev/pages/seedance-camera-control.html +2 -2
  192. package/tools/dev-full.mjs +11 -2
  193. package/tools/eslint-baseline.json +6 -6
  194. package/tools/knip-baseline.json +0 -11
  195. package/tools/loc-baseline.json +30 -31
  196. package/tools/metrics-snapshot.mjs +43 -5
  197. package/tools/render-start-thumbs.mjs +175 -0
  198. package/tools/run-tests.mjs +7 -14
  199. package/tools/sitemap.mjs +1 -1
  200. package/dist/assets/analytics-DbKG0NgD.js +0 -6
  201. package/dist/assets/app-34i-iUy1.js +0 -5088
  202. package/dist/assets/app-f-fkV5uP.css +0 -1
  203. package/dist/assets/compile-worker-DYpa2nvO.js +0 -5
  204. package/dist/assets/first-shot-handoff-CrGZ3n_O.js +0 -1
  205. package/dist/assets/landing-mq5bnqFE.js +0 -1
  206. package/dist/assets/scene-asset-sync-7J-w5oNh.js +0 -20
  207. package/dist/assets/scene-asset-sync-_S8NQl5B.css +0 -1
  208. package/dist/assets/workflow-dUhurYUc.js +0 -179
  209. package/dist/scenes/city-block.cclayproject +0 -1
  210. package/src/burn-in.js +0 -91
  211. package/src/camera-tutorial.jsx +0 -520
  212. package/src/facing-marks.js +0 -88
  213. package/src/first-shot-handoff.js +0 -44
  214. package/src/panels/motion.css +0 -197
  215. package/src/set-pieces.jsx +0 -183
  216. package/src/tutorial-analytics.js +0 -86
  217. package/src/tutorial-walk.js +0 -17
  218. package/src/use-camera-tutorial.js +0 -50
  219. package/src/use-semantic-state.js +0 -12
  220. package/tools/metrics/archive-traffic.mjs +0 -138
  221. package/tools/perf/ik-camera-drag.mjs +0 -138
@@ -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
  }
@@ -1,11 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Snapshot GitHub traffic + repo stats + npm downloads into JSONL ledgers.
3
+ * Snapshot GitHub traffic, stars, referrers, repo stats and npm downloads
4
+ * into JSONL ledgers on the `metrics` branch.
4
5
  *
5
- * GitHub's traffic API only keeps 14 days, so this must run on a schedule
6
+ * GitHub's traffic API only keeps 14 days, so this runs daily
6
7
  * (.github/workflows/metrics.yml) to build a permanent time series. Rows are
7
8
  * deduped by date so overlapping 14-day windows never double-count.
8
9
  *
10
+ * Files written to <output-dir>:
11
+ * traffic-daily.jsonl one row per UTC day: views, view_uniques, clones, clone_uniques
12
+ * stars-daily.jsonl one row per UTC day: new_stars, total (rebuilt from starred_at)
13
+ * referrers.jsonl one row per run date: 14-day top referrers and paths
14
+ * repo-stats.jsonl one row per run date: stars, forks, watchers, issues, npm weekly
15
+ *
9
16
  * Usage: GITHUB_TOKEN=... node tools/metrics-snapshot.mjs <output-dir>
10
17
  */
11
18
  import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
@@ -26,9 +33,9 @@ if (!token) {
26
33
  process.exit(2);
27
34
  }
28
35
 
29
- async function github(path) {
36
+ async function github(path, accept = "application/vnd.github+json") {
30
37
  const res = await fetch(`https://api.github.com/repos/${REPO}${path}`, {
31
- headers: { authorization: `Bearer ${token}`, accept: "application/vnd.github+json" },
38
+ headers: { authorization: `Bearer ${token}`, accept },
32
39
  });
33
40
  if (!res.ok) throw new Error(`GET ${path} -> ${res.status}`);
34
41
  return res.json();
@@ -95,4 +102,35 @@ stats.set(today, {
95
102
  });
96
103
  const statsTotal = saveRows(statsFile, stats);
97
104
 
98
- console.log(`traffic-daily: +${added} new day(s), ${trafficTotal} total; repo-stats: ${statsTotal} snapshot(s)`);
105
+ // 3) Stars per UTC day, rebuilt from every stargazer's starred_at. Someone who
106
+ // later unstars drops out of the history, so totals can trail repo.stargazers_count
107
+ // by a few. Rebuilt in full each run, so it never drifts.
108
+ const starDays = new Map();
109
+ for (let page = 1; page <= 400; page++) {
110
+ const batch = await github(`/stargazers?per_page=100&page=${page}`, "application/vnd.github.star+json");
111
+ if (!Array.isArray(batch) || batch.length === 0) break;
112
+ for (const s of batch) {
113
+ const day = String(s.starred_at).slice(0, 10);
114
+ starDays.set(day, (starDays.get(day) ?? 0) + 1);
115
+ }
116
+ if (batch.length < 100) break;
117
+ }
118
+ const starRows = new Map();
119
+ let running = 0;
120
+ for (const day of [...starDays.keys()].sort()) {
121
+ running += starDays.get(day);
122
+ starRows.set(day, { date: day, new_stars: starDays.get(day), total: running });
123
+ }
124
+ const starsTotal = saveRows(join(outDir, "stars-daily.jsonl"), starRows);
125
+
126
+ // 4) 14-day referrers and paths, one snapshot per run date.
127
+ const [referrers, paths] = await Promise.all([github("/traffic/popular/referrers"), github("/traffic/popular/paths")]);
128
+ const refFile = join(outDir, "referrers.jsonl");
129
+ const refs = loadRows(refFile);
130
+ refs.set(today, { date: today, referrers, paths });
131
+ const refTotal = saveRows(refFile, refs);
132
+
133
+ console.log(
134
+ `traffic-daily: +${added} new day(s), ${trafficTotal} total; repo-stats: ${statsTotal} snapshot(s); ` +
135
+ `stars-daily: ${starsTotal} day(s), ${running} stars; referrers: ${refTotal} snapshot(s)`,
136
+ );
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Render the start-screen template thumbnails from the real studio.
4
+ *
5
+ * npm run dev -- --port 5180 # any running dev server
6
+ * node tools/render-start-thumbs.mjs [--url http://127.0.0.1:5180/app/] [--out public/thumbs]
7
+ *
8
+ * For each template (Blank Stage plus every entry in STARTER_SCENES) and each
9
+ * theme, this opens the studio in a fresh headless Chrome profile, creates the
10
+ * project the way a user does (Blank Stage: the chooser's Create button; a
11
+ * starter: `?scene=<id>`), renders one offscreen frame through the studio's own
12
+ * capture path (`window.__cozyclay.captureFraming`), and writes
13
+ * `<out>/<template-id>-<theme>.webp` at 680x383 (2x the 340px preview).
14
+ * The chooser picks the file by id and theme and falls back to a plain tile
15
+ * when it is missing, so a new starter only needs this script re-run. A
16
+ * starter with no entry in FRAMINGS is shot through its own shot camera.
17
+ *
18
+ * Options: --url, --out, --cdp-port (default 9472), --only <template-id>.
19
+ * CHROME_PATH picks the browser; THUMBS_CHROME_FLAGS replaces the GPU flags
20
+ * (default: Metal on macOS, SwiftShader elsewhere).
21
+ */
22
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
23
+ import { tmpdir } from "node:os";
24
+ import { join, resolve } from "node:path";
25
+ import { fileURLToPath } from "node:url";
26
+ import { spawnOwned, terminateOwned } from "./process-supervisor.mjs";
27
+ import { STARTER_SCENES } from "../src/starter-scenes.js";
28
+ import { THEMES, THEME_KEY } from "../src/theme.js";
29
+
30
+ const ROOT = resolve(fileURLToPath(new URL("..", import.meta.url)));
31
+ const WIDTH = 680;
32
+ const HEIGHT = 383;
33
+ const MAX_BYTES = 60 * 1024;
34
+
35
+ function arg(name, fallback) {
36
+ const argv = process.argv.slice(2);
37
+ const index = argv.indexOf(name);
38
+ return index >= 0 ? argv[index + 1] : fallback;
39
+ }
40
+ const appUrl = new URL(arg("--url", "http://127.0.0.1:5180/app/"));
41
+ const outDir = resolve(ROOT, arg("--out", "public/thumbs"));
42
+ const cdpPort = Number(arg("--cdp-port", 9472));
43
+ const only = arg("--only", null);
44
+ const chromePath = process.env.CHROME_PATH || "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
45
+ const gpuFlags = (process.env.THUMBS_CHROME_FLAGS
46
+ ?? (process.platform === "darwin" ? "--use-angle=metal --ignore-gpu-blocklist" : "--use-angle=swiftshader --enable-unsafe-swiftshader"))
47
+ .split(/\s+/).filter(Boolean);
48
+
49
+ /** A studio framing ({ pos, yaw, pitch, fovDeg }) from an eye point and a target. */
50
+ function lookAt(eye, target, fovDeg) {
51
+ const [dx, dy, dz] = target.map((value, index) => value - eye[index]);
52
+ return { pos: { x: eye[0], y: eye[1], z: eye[2] }, yaw: Math.atan2(-dx, -dz), pitch: Math.atan2(dy, Math.hypot(dx, dz)), fovDeg };
53
+ }
54
+
55
+ // Eye-level 3/4 view. Blank Stage: the default character on the floor grid.
56
+ const FRAMINGS = {
57
+ "blank-stage": lookAt([1.6, 1.55, 3.7], [0, 0.88, 0], 40),
58
+ };
59
+
60
+ const templates = [
61
+ { id: "blank-stage", url: appUrl.href, create: true },
62
+ ...STARTER_SCENES.map((scene) => ({ id: scene.id, url: `${appUrl.href}?scene=${encodeURIComponent(scene.id)}`, create: false })),
63
+ ].filter((template) => !only || template.id === only);
64
+
65
+ const sleep = (ms) => new Promise((done) => setTimeout(done, ms));
66
+
67
+ async function connect() {
68
+ const deadline = Date.now() + 30000;
69
+ while (Date.now() < deadline) {
70
+ try {
71
+ const targets = await (await fetch(`http://127.0.0.1:${cdpPort}/json`)).json();
72
+ const page = targets.find((target) => target.type === "page" && target.webSocketDebuggerUrl);
73
+ if (page) {
74
+ const ws = new WebSocket(page.webSocketDebuggerUrl);
75
+ await new Promise((done, fail) => { ws.onopen = done; ws.onerror = fail; });
76
+ return ws;
77
+ }
78
+ } catch {
79
+ // Chrome is still starting.
80
+ }
81
+ await sleep(150);
82
+ }
83
+ throw new Error("headless Chrome did not expose a page target");
84
+ }
85
+
86
+ function client(ws) {
87
+ let nextId = 1;
88
+ const pending = new Map();
89
+ ws.onmessage = (event) => {
90
+ const message = JSON.parse(event.data);
91
+ const entry = pending.get(message.id);
92
+ if (!entry) return;
93
+ pending.delete(message.id);
94
+ if (message.error) entry.reject(new Error(JSON.stringify(message.error)));
95
+ else entry.resolve(message.result);
96
+ };
97
+ const send = (method, params = {}) => new Promise((resolveSend, reject) => {
98
+ const id = nextId++;
99
+ pending.set(id, { resolve: resolveSend, reject });
100
+ ws.send(JSON.stringify({ id, method, params }));
101
+ });
102
+ const evaluate = async (expression) => {
103
+ const result = await send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true });
104
+ if (result.exceptionDetails) throw new Error(result.exceptionDetails.exception?.description || "evaluate failed");
105
+ return result.result.value;
106
+ };
107
+ const waitFor = async (label, expression, timeoutMs = 30000) => {
108
+ const deadline = Date.now() + timeoutMs;
109
+ while (Date.now() < deadline) {
110
+ if (await evaluate(`(() => { try { return Boolean(${expression}); } catch { return false; } })()`)) return;
111
+ await sleep(200);
112
+ }
113
+ throw new Error(`timed out waiting for ${label}`);
114
+ };
115
+ return { send, evaluate, waitFor };
116
+ }
117
+
118
+ // Runs in the page: render the framing at 2x, downscale, and encode WebP under the size cap.
119
+ const encodeInPage = (framing) => `(async () => {
120
+ const studio = window.__cozyclay;
121
+ const framing = ${JSON.stringify(framing)};
122
+ const output = { width: ${WIDTH * 2}, height: ${HEIGHT * 2} };
123
+ const png = framing ? studio.captureFraming(framing, output) : studio.capturePlate();
124
+ if (!png) throw new Error("studio capture returned nothing");
125
+ const image = new Image();
126
+ image.src = png;
127
+ await image.decode();
128
+ const canvas = document.createElement("canvas");
129
+ canvas.width = ${WIDTH};
130
+ canvas.height = ${HEIGHT};
131
+ const ctx = canvas.getContext("2d");
132
+ ctx.imageSmoothingQuality = "high";
133
+ const scale = Math.max(canvas.width / image.width, canvas.height / image.height);
134
+ const w = image.width * scale, h = image.height * scale;
135
+ ctx.drawImage(image, (canvas.width - w) / 2, (canvas.height - h) / 2, w, h);
136
+ for (let quality = 0.92; quality > 0.4; quality -= 0.06) {
137
+ const url = canvas.toDataURL("image/webp", quality);
138
+ if (url.length * 0.75 <= ${MAX_BYTES} || quality <= 0.46) return url;
139
+ }
140
+ })()`;
141
+
142
+ async function renderOne(template, theme) {
143
+ const profile = mkdtempSync(join(tmpdir(), "cozyclay-thumbs-"));
144
+ const chrome = spawnOwned(chromePath, [
145
+ "--headless=new", `--remote-debugging-port=${cdpPort}`, `--user-data-dir=${profile}`,
146
+ "--window-size=1440,900", "--no-first-run", "--no-default-browser-check", ...gpuFlags, "about:blank",
147
+ ], { stdio: "ignore" });
148
+ try {
149
+ const cdp = client(await connect());
150
+ await cdp.send("Page.enable");
151
+ await cdp.send("Page.addScriptToEvaluateOnNewDocument", { source: `try { localStorage.setItem(${JSON.stringify(THEME_KEY)}, ${JSON.stringify(theme)}); } catch {}` });
152
+ await cdp.send("Page.navigate", { url: template.url });
153
+ if (template.create) {
154
+ await cdp.waitFor("the start screen", `document.querySelector('[data-template-id="${template.id}"]')`);
155
+ await cdp.evaluate(`document.querySelector('[data-template-id="${template.id}"]').click()`);
156
+ await sleep(200);
157
+ await cdp.evaluate("document.querySelector('.v2-start-project-form').requestSubmit()");
158
+ }
159
+ await cdp.waitFor("the studio", "window.__cozyclay?.charA && !document.querySelector('.v2-start-screen')");
160
+ await sleep(3000); // character model, textures and shadows settle
161
+ const dataUrl = await cdp.evaluate(encodeInPage(FRAMINGS[template.id] ?? null));
162
+ const bytes = Buffer.from(dataUrl.split(",")[1], "base64");
163
+ const file = join(outDir, `${template.id}-${theme}.webp`);
164
+ writeFileSync(file, bytes);
165
+ console.log(`${file.slice(ROOT.length + 1)} ${(bytes.length / 1024).toFixed(1)} KB`);
166
+ } finally {
167
+ await terminateOwned(chrome);
168
+ rmSync(profile, { recursive: true, force: true });
169
+ }
170
+ }
171
+
172
+ mkdirSync(outDir, { recursive: true });
173
+ for (const template of templates) {
174
+ for (const theme of THEMES) await renderOne(template, theme);
175
+ }
@@ -14,7 +14,7 @@ if (!process.env.COZYCLAY_AGENT_SESSIONS_DIR) {
14
14
  }
15
15
 
16
16
  const NODE_FILES = [
17
- "test/motion3/verify-motion3-core.mjs", "test/motion3/verify-motion3-idle.mjs", "test/motion3/verify-motion3-flip.mjs", "test/motion3/verify-motion3-timing.mjs", "test/motion3/verify-motion3-self.mjs", "test/motion3/verify-motion3-commands.mjs", "test/motion3/verify-motion3-exitcar.mjs", "test/motion3/verify-motion3-agentloop.mjs", "test/motion3/verify-motion3-search.mjs", "test/motion3/verify-motion3-skills.mjs", "test/motion3/verify-motion3-carried.mjs", "test/motion3/verify-motion3-people.mjs", "test/motion3/verify-motion3-hit.mjs", "test/motion3/verify-motion3-socket.mjs", "test/motion3/verify-motion3-contracts.mjs", "test/motion3/verify-motion3-describe.mjs", "test/motion3/verify-motion3-timeline.mjs", "test/motion3/verify-motion3-probes.mjs", "test/motion3/verify-motion3-grab.mjs", "test/motion3/verify-motion3-grab-blend.mjs", "test/motion3/verify-motion3-patch.mjs", "test/motion3/verify-motion3-repair.mjs", "test/motion3/verify-motion3-specstore.mjs", "test/motion3/verify-motion3-visual.mjs", "test/motion3/verify-motion3-goals.mjs", "test/motion3/verify-motion3-prompt.mjs", "test/motion3/verify-motion3-cup.mjs", "test/verify-proxy-figure.mjs", "test/verify-text-object.mjs",
17
+ "test/motion3/verify-motion3-core.mjs", "test/motion3/verify-playback-frame-store.mjs", "test/verify-playback-render-isolation.mjs", "test/motion3/verify-motion3-idle.mjs", "test/motion3/verify-motion3-flip.mjs", "test/motion3/verify-motion3-timing.mjs", "test/motion3/verify-motion3-self.mjs", "test/motion3/verify-motion3-commands.mjs", "test/motion3/verify-motion3-exitcar.mjs", "test/motion3/verify-motion3-agentloop.mjs", "test/motion3/verify-motion3-search.mjs", "test/motion3/verify-motion3-skills.mjs", "test/motion3/verify-motion3-carried.mjs", "test/motion3/verify-motion3-people.mjs", "test/motion3/verify-motion3-hit.mjs", "test/motion3/verify-motion3-socket.mjs", "test/motion3/verify-motion3-shapes.mjs", "test/motion3/verify-motion3-grasp-fields.mjs", "test/motion3/verify-motion3-finger-contact.mjs", "test/motion3/verify-motion3-grasp-qa.mjs", "test/motion3/verify-motion3-load.mjs", "test/motion3/verify-motion3-throw.mjs", "test/motion3/verify-motion3-catch.mjs", "test/motion3/verify-motion3-grasp-hints.mjs", "test/motion3/verify-motion3-reach-human.mjs", "test/motion3/verify-motion3-stance-grasp.mjs", "test/motion3/verify-motion3-grasp-friction.mjs", "test/motion3/verify-motion3-grasp-measure.mjs", "test/motion3/verify-motion3-contracts.mjs", "test/motion3/verify-motion3-describe.mjs", "test/motion3/verify-motion3-timeline.mjs", "test/motion3/verify-motion3-probes.mjs", "test/motion3/verify-motion3-grab.mjs", "test/motion3/verify-motion3-grab-blend.mjs", "test/motion3/verify-motion3-patch.mjs", "test/motion3/verify-motion3-repair.mjs", "test/motion3/verify-motion3-specstore.mjs", "test/motion3/verify-motion3-visual.mjs", "test/motion3/verify-motion3-goals.mjs", "test/motion3/verify-motion3-prompt.mjs", "test/motion3/verify-motion3-cup.mjs", "test/verify-proxy-figure.mjs", "test/verify-text-object.mjs",
18
18
  "test/verify-proxy-figure-bounds.mjs",
19
19
  "test/verify-character-capabilities.mjs",
20
20
  "test/verify-root-path.mjs",
@@ -22,13 +22,12 @@ const NODE_FILES = [
22
22
  "test/bus/verify-view-mode-pose.mjs",
23
23
  "test/verify-studio-actions.mjs",
24
24
  "test/ardy/verify-collision-blockers.mjs",
25
- "test/verify-facing-marks.mjs",
26
25
  "test/verify-pose-fade.mjs",
27
26
  "test/verify-pose-gizmo.mjs",
28
27
  "test/ardy/verify-key-runs.mjs",
29
28
  "test/ardy/verify-playback-clock.mjs",
30
29
  "test/ardy/verify-timeline-coordinates.mjs",
31
- "test/verify-tokens.mjs",
30
+ "test/verify-tokens.mjs", "test/verify-lens-scrub.mjs",
32
31
  "test/demo/verify-api-routes.mjs",
33
32
  "test/ik/verify-collision-capsules.mjs",
34
33
  "test/ik/verify-ground.mjs",
@@ -44,7 +43,7 @@ const NODE_FILES = [
44
43
  "test/ik/verify-range-pin-ui.mjs",
45
44
  "test/ik/verify-range-pin-object-transform.mjs",
46
45
  "test/verify-ik-camera-performance.mjs",
47
- "test/verify-ik-entry-camera.mjs",
46
+ "test/verify-ik-entry-camera.mjs", "test/verify-viewport-visible-rect.mjs",
48
47
  "test/process/verify-lifecycle.mjs",
49
48
  "test/process/verify-mcp-package-isolation.mjs",
50
49
  "test/process/verify-package-telemetry.mjs",
@@ -65,7 +64,7 @@ const NODE_FILES = [
65
64
  "test/verify-schema-to-typebox.mjs",
66
65
  "test/verify-agent-sse-golden.mjs",
67
66
  "test/verify-agent-context-budget.mjs", "test/verify-agent-context-trim.mjs",
68
- "test/verify-agent-supervisor.mjs",
67
+ "test/verify-agent-supervisor.mjs", "test/verify-studio-tasks.mjs", "test/verify-task-board.mjs",
69
68
  "test/verify-studio-agent-protocol.mjs",
70
69
  "test/verify-studio-agent-sampled-framing.mjs",
71
70
  "test/verify-studio-agent-tokens.mjs",
@@ -95,17 +94,14 @@ const NODE_FILES = [
95
94
  "test/verify-asset-shelf.mjs",
96
95
  "test/verify-blocking-depth.mjs",
97
96
  "test/verify-render-passes-video.mjs",
98
- "test/verify-burn-in.mjs",
99
97
  "test/verify-camera-block.mjs", "test/verify-camera-direction.mjs",
100
98
  "test/verify-camera-follow.mjs",
101
99
  "test/verify-camera-move.mjs",
102
100
  "test/verify-camera-rail-schedule.mjs",
103
101
  "test/verify-camera-pointer-lock.mjs",
104
- "test/verify-camera-tutorial.mjs",
105
- "test/verify-first-shot-handoff.mjs",
106
102
  "test/verify-codex-client.mjs",
107
103
  "test/verify-cuts.mjs",
108
- "test/verify-shot-guides.mjs",
104
+ "test/verify-shot-guides.mjs", "test/verify-shot-frame.mjs",
109
105
  "test/verify-error-boundary.mjs",
110
106
  "test/verify-g006-css.mjs",
111
107
  "test/verify-gizmo-claim.mjs",
@@ -238,9 +234,6 @@ const BROWSER_FILES = [
238
234
  "test/verify-static-shot-export-browser.mjs",
239
235
  "test/qa-agent-view-toggle-browser.mjs",
240
236
  "test/qa-agent-scenarios-browser.mjs",
241
- "test/qa-camera-tutorial-browser.mjs",
242
- "test/qa-first-shot-handoff-browser.mjs",
243
- "test/qa-tutorial-analytics-browser.mjs",
244
237
  "test/qa-camera-pointer-lock-browser.mjs",
245
238
  "test/qa-first-edit-browser.mjs",
246
239
  "test/qa-ia-tail-browser.mjs",
@@ -262,7 +255,7 @@ const BROWSER_FILES = [
262
255
  // The inventory sweep only picks up `verify*.mjs`; a browser suite named for
263
256
  // the QA runner it needs is listed here so it still shows up in the manifest
264
257
  // (as an EXCLUDE with its reason) instead of going unmentioned.
265
- const EXTRA_INVENTORY = ["test/ik/translation-step.mjs", "test/qa-scenes-v5-migration-browser.mjs", "test/qa-agent-scenarios-browser.mjs", "test/qa-execution-outcomes-browser.mjs", "test/qa-export-recovery-browser.mjs", "test/qa-agent-view-toggle-browser.mjs", "test/qa-camera-tutorial-browser.mjs", "test/qa-first-shot-handoff-browser.mjs", "test/qa-tutorial-analytics-browser.mjs", "test/qa-camera-pointer-lock-browser.mjs", "test/qa-first-edit-browser.mjs", "test/qa-ia-tail-browser.mjs", "test/qa-keyframe-pack-browser.mjs", "test/qa-preview-browser.mjs", "test/qa-reference-slots-browser.mjs", "test/qa-scene-playback-browser.mjs", "test/qa-scene-switcher-browser.mjs", "test/qa-send-to-ai-browser.mjs", "test/qa-view-menu-browser.mjs", "test/qa-live-reconnect-browser.mjs", "test/qa-clipboard-paste-browser.mjs", "test/qa-rail-lean-browser.mjs", "test/qa-rail-reach-browser.mjs", "test/qa-rail-ring-browser.mjs", "test/qa-speed-graph-browser.mjs"];
258
+ const EXTRA_INVENTORY = ["test/ik/translation-step.mjs", "test/qa-scenes-v5-migration-browser.mjs", "test/qa-agent-scenarios-browser.mjs", "test/qa-execution-outcomes-browser.mjs", "test/qa-export-recovery-browser.mjs", "test/qa-agent-view-toggle-browser.mjs", "test/qa-camera-pointer-lock-browser.mjs", "test/qa-first-edit-browser.mjs", "test/qa-ia-tail-browser.mjs", "test/qa-keyframe-pack-browser.mjs", "test/qa-preview-browser.mjs", "test/qa-reference-slots-browser.mjs", "test/qa-scene-playback-browser.mjs", "test/qa-scene-switcher-browser.mjs", "test/qa-send-to-ai-browser.mjs", "test/qa-view-menu-browser.mjs", "test/qa-live-reconnect-browser.mjs", "test/qa-clipboard-paste-browser.mjs", "test/qa-rail-lean-browser.mjs", "test/qa-rail-reach-browser.mjs", "test/qa-rail-ring-browser.mjs", "test/qa-speed-graph-browser.mjs"];
266
259
 
267
260
  EXTRA_INVENTORY.push("test/qa-proxy-figure-browser.mjs");
268
261
  EXTRA_INVENTORY.push("test/qa-proxy-entry-browser.mjs");
@@ -275,7 +268,7 @@ function verificationFiles(directory) {
275
268
  .flatMap((entry) => {
276
269
  const path = join(directory, entry.name);
277
270
  if (entry.isDirectory()) return entry.name === "node_modules" ? [] : verificationFiles(path);
278
- return entry.isFile() && /^verify(-.*)?\.mjs$/.test(entry.name) ? [relative(".", path)] : [];
271
+ return entry.isFile() && /^verify(-.*)?\.mjs$/.test(entry.name) ? [relative(".", path).replaceAll("\\", "/")] : [];
279
272
  })
280
273
  .sort();
281
274
  }
package/tools/sitemap.mjs CHANGED
@@ -8,7 +8,7 @@ import { resolve } from "node:path";
8
8
 
9
9
  const ORIGIN = "https://cozyclay.org";
10
10
  const PAGES = [
11
- { path: "/", sources: ["index.html", "public/media", "public/scenes"] },
11
+ { path: "/", sources: ["index.html", "public/media"] },
12
12
  { path: "/greybox-to-video/", sources: ["greybox-to-video/index.html"] },
13
13
  { path: "/previs-software/", sources: ["previs-software/index.html"] },
14
14
  { path: "/seedance-camera-control/", sources: ["seedance-camera-control/index.html"] },