cozyclay 3.1.3 → 3.1.5

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 (125) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/INSTALL_FOR_AGENTS.md +3 -0
  3. package/README.md +5 -0
  4. package/bin/agent/agent-failure-codes.mjs +31 -0
  5. package/bin/agent/agent-routes.mjs +5 -10
  6. package/bin/agent/agent-runner.mjs +5 -3
  7. package/bin/cozyclay.mjs +18 -112
  8. package/bin/star-prompt.mjs +123 -0
  9. package/bin/studio-routes.mjs +69 -0
  10. package/bin/update-check.mjs +53 -5
  11. package/bin/update-install.mjs +136 -0
  12. package/bin/update-routes.mjs +80 -0
  13. package/dist/ai-camera-control/index.html +1 -1
  14. package/dist/app/index.html +5 -6
  15. package/dist/assets/{TaskBoard-U1Yl-4F1.js → TaskBoard-DwXhsJXz.js} +1 -1
  16. package/dist/assets/app-CEP7jzXB.css +1 -0
  17. package/dist/assets/app-CTKcyPM-.js +5144 -0
  18. package/dist/assets/compile-worker-or7djI2v.js +6 -0
  19. package/dist/assets/{motion3-compile-DDG3wklD.js → motion3-compile-Du7UXrWn.js} +1 -1
  20. package/dist/assets/scene-asset-sync-t45pWCyq.js +13 -0
  21. package/dist/assets/task-board-CkLZGPkv.js +15 -0
  22. package/dist/assets/workflow-DJwOUzqJ.js +179 -0
  23. package/dist/cozyclay-package.json +1 -1
  24. package/dist/greybox-to-video/index.html +3 -3
  25. package/dist/index.html +533 -264
  26. package/dist/media/cozyclay-mark.png +0 -0
  27. package/dist/media/cozyclay-truck-demo-poster.jpg +0 -0
  28. package/dist/media/step-move-poster.jpg +0 -0
  29. package/dist/media/step-shoot-poster.jpg +0 -0
  30. package/dist/media/step-stage-poster.jpg +0 -0
  31. package/dist/media/truck-greybox-poster.jpg +0 -0
  32. package/dist/media/truck-seedance-poster.jpg +0 -0
  33. package/dist/previs-software/index.html +4 -4
  34. package/dist/privacy/index.html +46 -19
  35. package/dist/seedance-camera-control/index.html +3 -3
  36. package/dist/sitemap.xml +6 -6
  37. package/dist/workflow/index.html +3 -4
  38. package/package.json +1 -1
  39. package/src/App.jsx +59 -70
  40. package/src/analytics-controls.js +17 -0
  41. package/src/analytics-friction.js +124 -0
  42. package/src/analytics-strict.js +139 -0
  43. package/src/analytics-values.js +49 -0
  44. package/src/analytics.js +37 -133
  45. package/src/asset-pane.jsx +4 -5
  46. package/src/click-friction.js +101 -0
  47. package/src/command-bus-telemetry.js +155 -0
  48. package/src/command-bus.js +10 -8
  49. package/src/commands/motion3.js +7 -2
  50. package/src/controls.jsx +3 -2
  51. package/src/crash-telemetry.js +45 -0
  52. package/src/domains/cast.js +2 -11
  53. package/src/domains/motion.js +49 -38
  54. package/src/domains/objects.js +3 -0
  55. package/src/domains/scene-undo.js +27 -0
  56. package/src/domains/scenes.js +1 -1
  57. package/src/error-boundary.jsx +2 -0
  58. package/src/export-attempt.js +100 -0
  59. package/src/export-workspace.js +12 -1
  60. package/src/first-success-guide.jsx +11 -7
  61. package/src/guide-telemetry.js +60 -0
  62. package/src/hierarchy-panel.jsx +2 -1
  63. package/src/live-control.js +2 -9
  64. package/src/locale-zh/domains.js +1 -0
  65. package/src/main.jsx +5 -0
  66. package/src/motion/timeline.jsx +9 -5
  67. package/src/motion-draft-telemetry.js +62 -0
  68. package/src/motion3/compiler/channels.js +7 -5
  69. package/src/motion3/compiler/compile.js +6 -2
  70. package/src/motion3/compiler/finger-close.js +84 -7
  71. package/src/motion3/compiler/head-lead.js +70 -0
  72. package/src/motion3/compiler/head-steady.js +1 -1
  73. package/src/motion3/compiler/limbs.js +10 -3
  74. package/src/motion3/compiler/pose.js +1 -1
  75. package/src/motion3/compiler/validate.js +5 -3
  76. package/src/motion3/pipeline.js +4 -0
  77. package/src/motion3/qa/explain.js +2 -1
  78. package/src/motion3/qa/finger-checks.js +80 -0
  79. package/src/motion3/qa/gait-observe.js +88 -0
  80. package/src/motion3/qa/grasp-checks.js +3 -0
  81. package/src/motion3/qa/hints-grasp.js +8 -0
  82. package/src/motion3/qa/index.js +2 -1
  83. package/src/motion3/qa/motion-life.js +26 -3
  84. package/src/motion3/schema/topics.js +1 -1
  85. package/src/playback-telemetry.js +107 -0
  86. package/src/post-turn-edits.js +62 -0
  87. package/src/search-friction.js +49 -0
  88. package/src/semantic-edit.js +19 -6
  89. package/src/shell/BoardSlot.jsx +3 -3
  90. package/src/shell/BottomDock.jsx +1 -1
  91. package/src/shell/DetailsSlot.jsx +1 -1
  92. package/src/shell/MenuBar.jsx +13 -13
  93. package/src/shell/StudioShell.jsx +7 -1
  94. package/src/shell/TopBar.jsx +2 -0
  95. package/src/shell/ViewportToolbar.jsx +2 -2
  96. package/src/shortcut-friction.js +122 -0
  97. package/src/stage-canvas-rigs.jsx +3 -4
  98. package/src/studio-telemetry.js +97 -0
  99. package/src/thrash-friction.js +90 -0
  100. package/src/update/UpdateChip.jsx +86 -0
  101. package/src/update/UpdateDialog.jsx +147 -0
  102. package/src/update/update-client.js +188 -0
  103. package/src/update/update-telemetry.js +78 -0
  104. package/src/update/update.css +94 -0
  105. package/src/use-webgl-available.js +35 -0
  106. package/src/workflow/AgentPanel.jsx +29 -30
  107. package/src/workflow/CozySceneNode.jsx +1 -1
  108. package/src/workflow/LoginPanel.jsx +15 -5
  109. package/src/workflow/agent-client.js +10 -91
  110. package/src/workflow/agent-failure-codes.js +47 -0
  111. package/src/workflow/agent-turn-telemetry.js +222 -0
  112. package/src/workflow/connect-telemetry.js +86 -0
  113. package/src/workflow/prompt-telemetry.js +105 -0
  114. package/tools/dev/pages/privacy.html +44 -17
  115. package/tools/loc-baseline.json +10 -10
  116. package/tools/run-tests.mjs +8 -8
  117. package/dist/assets/app-BdRlMf4J.css +0 -1
  118. package/dist/assets/app-DwETmoQZ.js +0 -5144
  119. package/dist/assets/compile-worker-oy_P9HHh.js +0 -6
  120. package/dist/assets/landing-y3EqDgSZ.js +0 -1
  121. package/dist/assets/modulepreload-polyfill-P2Xu9kJm.js +0 -1
  122. package/dist/assets/scene-asset-sync-Bz59Ghdz.js +0 -13
  123. package/dist/assets/task-board-DFfNa0xg.js +0 -15
  124. package/dist/assets/workflow-BMJGd_gZ.js +0 -179
  125. package/dist/media/playground-poster.jpg +0 -0
@@ -52,20 +52,32 @@ export function compareVersions(a, b) {
52
52
  return 0;
53
53
  }
54
54
 
55
+ // A registry answer whose cache write failed (a read-only home), by cache file.
56
+ // The write is best effort, so a good fetch must not depend on it.
57
+ const fetchedInProcess = new Map();
58
+
59
+ // The newer of the cache file and this process's own last fetch.
55
60
  function readCache(cacheFile) {
61
+ let disk = null;
56
62
  try {
57
- return JSON.parse(readFileSync(cacheFile, "utf8"));
63
+ disk = JSON.parse(readFileSync(cacheFile, "utf8"));
58
64
  } catch {
59
- return null;
65
+ /* missing or unreadable: the in-process answer, if any, stands */
60
66
  }
67
+ const memory = fetchedInProcess.get(cacheFile) ?? null;
68
+ if (!disk) return memory;
69
+ return memory && !(memory.checkedAt <= disk.checkedAt) ? memory : disk;
61
70
  }
62
71
 
63
72
  function writeCache(cacheFile, cacheDir, latest) {
73
+ const entry = { latest, checkedAt: Date.now() };
64
74
  try {
65
75
  mkdirSync(cacheDir, { recursive: true });
66
- writeFileSync(cacheFile, JSON.stringify({ latest, checkedAt: Date.now() }, null, "\t"));
76
+ writeFileSync(cacheFile, JSON.stringify(entry, null, "\t"));
77
+ fetchedInProcess.delete(cacheFile);
67
78
  } catch {
68
- /* a read-only home is not a reason to fail a local dev server */
79
+ // A read-only home is not a reason to fail a local dev server, nor to forget the answer.
80
+ fetchedInProcess.set(cacheFile, entry);
69
81
  }
70
82
  }
71
83
 
@@ -134,11 +146,47 @@ export function readUpdateStatus(currentVersion, stateDir) {
134
146
  }
135
147
  }
136
148
 
149
+ // `--no-update-check`, COZYCLAY_NO_UPDATE_CHECK and CI each turn the notice,
150
+ // the Studio chip and the one-click install off.
151
+ export function updateChecksOn(flag, env = process.env) {
152
+ return Boolean(flag && !env.CI && !env.COZYCLAY_NO_UPDATE_CHECK);
153
+ }
154
+
155
+ /**
156
+ * The registry's latest x.y.z with this launch's status: the shape behind
157
+ * GET /__cozyclay/update and `cclay update --check`. The cache rules are
158
+ * checkForUpdate's; `fresh` asks the registry even inside the TTL, and a dead
159
+ * registry still falls back to the cached answer.
160
+ */
161
+ export async function lookupLatest(currentVersion, stateDir, { fresh = false } = {}) {
162
+ const cacheFile = join(stateDir, "update-check.json");
163
+ if (fresh) {
164
+ const fetched = await fetchLatest();
165
+ if (fetched) writeCache(cacheFile, stateDir, fetched);
166
+ } else {
167
+ await checkForUpdate(currentVersion, stateDir);
168
+ }
169
+ const latest = readCache(cacheFile)?.latest;
170
+ const known = typeof latest === "string" && /^\d+\.\d+\.\d+/.test(latest);
171
+ return { latest: known ? latest : null, status: readUpdateStatus(currentVersion, stateDir) };
172
+ }
173
+
174
+ /**
175
+ * `cclay update --check`: print, install nothing, and exit 0 when this is the
176
+ * newest release, 10 when a newer one exists, 11 when the registry could not
177
+ * answer and nothing is cached. Scripts and agents branch on the code.
178
+ */
179
+ export async function runUpdateCheck({ currentVersion, kind, stateDir }) {
180
+ const { latest, status } = await lookupLatest(currentVersion, stateDir, { fresh: true });
181
+ console.log(`current: ${currentVersion}\nlatest: ${latest ?? "unknown"}\nkind: ${kind}\nstatus: ${status}`);
182
+ return { latest: 0, outdated: 10 }[status] ?? 11;
183
+ }
184
+
137
185
  // npx/bunx runs from a temp dir, so there is no local install to upgrade in
138
186
  // place; `npm install -g` is the one command that is right either way.
139
187
  export function runUpdate() {
140
188
  const npm = process.platform === "win32" ? "npm.cmd" : "npm";
141
- const child = spawn(npm, ["install", "-g", "cozyclay@latest"], { stdio: "inherit", shell: process.platform === "win32" });
189
+ const child = spawn(npm, ["install", "-g", "cozyclay@latest", "--engine-strict"], { stdio: "inherit", shell: process.platform === "win32" });
142
190
  child.on("error", (err) => {
143
191
  console.error(`cozyclay: could not run npm: ${err.message}`);
144
192
  process.exit(1);
@@ -0,0 +1,136 @@
1
+ /**
2
+ * The one-click global update behind POST /__cozyclay/update.
3
+ *
4
+ * Two questions live here: may this launcher update itself at all
5
+ * (canSelfUpdate), and what happened when it tried (an in-memory install
6
+ * state the Studio polls). Nothing a request carries ever reaches npm: the
7
+ * only argument that varies is a version that has passed an exact x.y.z test.
8
+ */
9
+ import { execFile, spawn } from "node:child_process";
10
+ import { constants, accessSync, realpathSync } from "node:fs";
11
+ import { join } from "node:path";
12
+
13
+ const VERSION = /^\d+\.\d+\.\d+$/;
14
+ const ROOT_TIMEOUT_MS = 2000;
15
+ const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
16
+ const STDERR_CAP = 64 * 1024;
17
+
18
+ const ERROR_CODES = [
19
+ ["node_version", ["EBADENGINE"]],
20
+ ["permission", ["EACCES", "EPERM"]],
21
+ ["network", ["ENOTFOUND", "ETIMEDOUT", "ECONNREFUSED", "ECONNRESET", "EAI_AGAIN"]],
22
+ ];
23
+
24
+ // COZYCLAY_NPM_BIN lets a test put a fake npm in front of both calls; win32
25
+ // keeps npm.cmd the way runUpdate spawns it.
26
+ function npmBinary(platform = process.platform) {
27
+ return process.env.COZYCLAY_NPM_BIN || (platform === "win32" ? "npm.cmd" : "npm");
28
+ }
29
+
30
+ function npmRootGlobal(npmBin, platform) {
31
+ return new Promise((resolve) => {
32
+ execFile(npmBin, ["root", "-g"], { timeout: ROOT_TIMEOUT_MS, shell: platform === "win32", encoding: "utf8" }, (error, stdout) => {
33
+ resolve(error ? null : String(stdout).trim() || null);
34
+ });
35
+ });
36
+ }
37
+
38
+ const selfUpdateCache = new Map();
39
+
40
+ /**
41
+ * True only when all three hold: not Windows (U1 scope), this package's real
42
+ * path is `<npm root -g>/cozyclay`, and that folder and its parent are
43
+ * writable. Any doubt answers false, which falls back to a copied command.
44
+ * Computed once per (pkgRoot, platform, npm) and kept in memory.
45
+ */
46
+ export function canSelfUpdate({ pkgRoot, platform = process.platform, npmBin = npmBinary(platform) }) {
47
+ const key = JSON.stringify([pkgRoot, platform, npmBin]);
48
+ if (!selfUpdateCache.has(key)) selfUpdateCache.set(key, computeSelfUpdate(pkgRoot, platform, npmBin));
49
+ return selfUpdateCache.get(key);
50
+ }
51
+
52
+ async function computeSelfUpdate(pkgRoot, platform, npmBin) {
53
+ if (platform === "win32") return false;
54
+ const root = await npmRootGlobal(npmBin, platform);
55
+ if (!root) return false;
56
+ try {
57
+ const installed = realpathSync(join(root, "cozyclay"));
58
+ if (realpathSync(pkgRoot) !== installed) return false;
59
+ // npm swaps the folder, so the parent has to take writes as well.
60
+ accessSync(installed, constants.W_OK);
61
+ accessSync(realpathSync(root), constants.W_OK);
62
+ return true;
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+
68
+ function classOf(code) {
69
+ return ERROR_CODES.find(([, codes]) => codes.includes(code))?.[0] ?? null;
70
+ }
71
+
72
+ /**
73
+ * permission | network | node_version | npm_missing | other, from npm's
74
+ * stderr and the spawn error (if any). The `npm error code X` line wins;
75
+ * otherwise the first known code anywhere in the text decides.
76
+ */
77
+ export function classifyNpmError(stderr = "", spawnError = null) {
78
+ if (spawnError?.code === "ENOENT") return "npm_missing";
79
+ const text = String(stderr ?? "");
80
+ const lineCode = /npm (?:error|ERR!) code (\S+)/.exec(text)?.[1];
81
+ if (lineCode && classOf(lineCode)) return classOf(lineCode);
82
+ if (/Not compatible with your version of node/i.test(text)) return "node_version";
83
+ for (const token of text.match(/\bE[A-Z_]{2,}\b/g) ?? []) {
84
+ if (classOf(token)) return classOf(token);
85
+ }
86
+ return classOf(spawnError?.code) ?? "other";
87
+ }
88
+
89
+ /**
90
+ * The launcher's install state: { phase, errorClass, startedAt, durationMs }.
91
+ * startInstall is the lock: while a run is updating or has finished and waits
92
+ * for a restart, it starts nothing and returns false.
93
+ */
94
+ export function createInstaller({ timeoutMs = INSTALL_TIMEOUT_MS } = {}) {
95
+ const state = { phase: "idle", errorClass: null, startedAt: null, durationMs: null };
96
+ let timer = null;
97
+ function finish(phase, errorClass) {
98
+ if (state.phase !== "updating") return;
99
+ clearTimeout(timer);
100
+ Object.assign(state, { phase, errorClass, durationMs: Date.now() - state.startedAt });
101
+ }
102
+ function startInstall(version) {
103
+ if (!VERSION.test(String(version))) throw new TypeError("cozyclay: refusing to install a version that is not x.y.z");
104
+ if (state.phase === "updating" || state.phase === "restart_needed") return false;
105
+ Object.assign(state, { phase: "updating", errorClass: null, startedAt: Date.now(), durationMs: null });
106
+ const platform = process.platform;
107
+ let child;
108
+ try {
109
+ child = spawn(npmBinary(platform), ["install", "-g", `cozyclay@${version}`, "--engine-strict"], {
110
+ stdio: ["ignore", "pipe", "pipe"],
111
+ shell: platform === "win32",
112
+ });
113
+ } catch (error) {
114
+ finish("failed", classifyNpmError("", error));
115
+ return true;
116
+ }
117
+ let stderr = "";
118
+ child.stdout.resume();
119
+ child.stderr.setEncoding("utf8");
120
+ child.stderr.on("data", (chunk) => {
121
+ if (stderr.length < STDERR_CAP) stderr += chunk.slice(0, STDERR_CAP - stderr.length);
122
+ });
123
+ timer = setTimeout(() => {
124
+ child.kill("SIGTERM");
125
+ finish("failed", "other");
126
+ }, timeoutMs);
127
+ timer.unref?.();
128
+ child.on("error", (error) => finish("failed", classifyNpmError(stderr, error)));
129
+ child.on("close", (code, signal) => {
130
+ if (code === 0 && !signal) finish("restart_needed", null);
131
+ else finish("failed", classifyNpmError(stderr));
132
+ });
133
+ return true;
134
+ }
135
+ return { startInstall, getState: () => ({ ...state }) };
136
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * GET and POST /__cozyclay/update: what the Studio's update chip reads, and
3
+ * the one click that installs. Every input is injected so the route runs
4
+ * under a test with a mock registry and a fake npm.
5
+ *
6
+ * POST reads no body. The version it installs is the registry's latest after
7
+ * an exact x.y.z test, never anything the request says.
8
+ */
9
+ const VERSION = /^\d+\.\d+\.\d+$/;
10
+ const NOTES_URL = "https://github.com/NomaDamas/CozyClay/releases/tag/v";
11
+
12
+ function sendJson(res, status, body, headers = {}) {
13
+ res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store", ...headers });
14
+ res.end(JSON.stringify(body));
15
+ }
16
+
17
+ /**
18
+ * @param {object} options
19
+ * @param {string} options.currentVersion the running package version
20
+ * @param {() => Promise<{latest: string|null, status: string}>} options.checkLatest
21
+ * @param {"global"|"npx"|"clone"} options.kind
22
+ * @param {boolean} options.enabled false when update checks are off
23
+ * @param {number} options.port the launcher's loopback port
24
+ * @param {() => Promise<boolean>} options.canSelfUpdate
25
+ * @param {{startInstall(version: string): boolean, getState(): object}} options.installer
26
+ */
27
+ export function createUpdateRoutes({ currentVersion, checkLatest, kind, enabled, port, canSelfUpdate, installer }) {
28
+ const command = kind === "npx" ? "npx cozyclay" : "npm install -g cozyclay@latest";
29
+ const hosts = new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
30
+ // Spelled in two parts so the G014 loopback invariant does not read it as a client URL.
31
+ const origins = new Set([`http://127.0.0.1:${port}`, `http://${"local" + "host"}:${port}`]);
32
+
33
+ async function info() {
34
+ const base = { enabled, current: currentVersion, latest: null, status: "unknown", kind, canSelfUpdate: false, phase: "idle", errorClass: null, durationMs: null, notesUrl: null, command };
35
+ if (!enabled) return base;
36
+ const [{ latest, status }, selfUpdate] = await Promise.all([
37
+ checkLatest().catch(() => ({ latest: null, status: "unknown" })),
38
+ kind === "global" ? canSelfUpdate().catch(() => false) : false,
39
+ ]);
40
+ const { phase, errorClass, durationMs } = installer.getState();
41
+ const finished = phase === "restart_needed" || phase === "failed";
42
+ return {
43
+ ...base,
44
+ latest: latest ?? null,
45
+ status: latest ? status : "unknown",
46
+ canSelfUpdate: selfUpdate === true,
47
+ phase,
48
+ errorClass: phase === "failed" ? errorClass : null,
49
+ durationMs: finished ? durationMs : null,
50
+ notesUrl: latest ? `${NOTES_URL}${latest}` : null,
51
+ };
52
+ }
53
+
54
+ async function handlePost(req, res) {
55
+ if (req.headers.origin !== `http://127.0.0.1:${port}`) return sendJson(res, 403, { error: "forbidden origin" });
56
+ if (!enabled) return sendJson(res, 403, { error: "update checks are disabled" });
57
+ const { phase } = installer.getState();
58
+ if (phase === "updating" || phase === "restart_needed") return sendJson(res, 202, await info());
59
+ const current = await info();
60
+ if (!(kind === "global" && current.canSelfUpdate && current.status === "outdated" && VERSION.test(current.latest ?? ""))) {
61
+ return sendJson(res, 409, { error: "self-update unavailable" });
62
+ }
63
+ installer.startInstall(current.latest);
64
+ return sendJson(res, 202, await info());
65
+ }
66
+
67
+ return async function handleUpdateRoute(req, res) {
68
+ try {
69
+ if (req.method === "GET") {
70
+ const origin = req.headers.origin;
71
+ if (!(origins.has(origin) || (origin === undefined && hosts.has(req.headers.host)))) return sendJson(res, 403, { error: "forbidden origin" });
72
+ return sendJson(res, 200, await info());
73
+ }
74
+ if (req.method === "POST") return await handlePost(req, res);
75
+ return sendJson(res, 405, { error: "method not allowed" }, { allow: "GET, POST" });
76
+ } catch {
77
+ if (!res.headersSent) sendJson(res, 500, { error: "update unavailable" });
78
+ }
79
+ };
80
+ }
@@ -168,7 +168,7 @@
168
168
  <div class="wrap">
169
169
  <a class="brand" href="/">CozyClay</a>
170
170
  <nav>
171
- <a href="/#try">Try it</a>
171
+ <a href="/app/">Try it</a>
172
172
  <a href="/greybox-to-video/">Greybox to video</a>
173
173
  <a href="/previs-software/">Previs software</a>
174
174
  <a href="/seedance-camera-control/">Seedance</a>
@@ -34,15 +34,14 @@
34
34
  <title>CozyClay Studio — 3D staging and previs in your browser</title>
35
35
 
36
36
  <script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token": "6a4384d1375d4ffc8a30a6346a8ad1a0"}'></script>
37
- <script type="module" crossorigin src="/assets/app-DwETmoQZ.js"></script>
37
+ <script type="module" crossorigin src="/assets/app-CTKcyPM-.js"></script>
38
38
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
39
- <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-P2Xu9kJm.js">
40
- <link rel="modulepreload" crossorigin href="/assets/task-board-DFfNa0xg.js">
39
+ <link rel="modulepreload" crossorigin href="/assets/task-board-CkLZGPkv.js">
41
40
  <link rel="modulepreload" crossorigin href="/assets/fflate.module-KbGj5-H6.js">
42
- <link rel="modulepreload" crossorigin href="/assets/scene-asset-sync-Bz59Ghdz.js">
43
- <link rel="modulepreload" crossorigin href="/assets/motion3-compile-DDG3wklD.js">
41
+ <link rel="modulepreload" crossorigin href="/assets/scene-asset-sync-t45pWCyq.js">
42
+ <link rel="modulepreload" crossorigin href="/assets/motion3-compile-Du7UXrWn.js">
44
43
  <link rel="stylesheet" crossorigin href="/assets/scene-asset-sync-BEjjhNEo.css">
45
- <link rel="stylesheet" crossorigin href="/assets/app-BdRlMf4J.css">
44
+ <link rel="stylesheet" crossorigin href="/assets/app-CEP7jzXB.css">
46
45
  </head>
47
46
  <body>
48
47
  <div id="root">
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-hePW80VL.js";import{Ti as t,c as n,d as r,f as i,fa as a,l as o,m as s,o as c,v as l}from"./task-board-DFfNa0xg.js";import{o as u,r as d,t as f}from"./motion3-compile-DDG3wklD.js";var p=e(a(),1),m=2e3,h=(e,t)=>new Promise(n=>{let r=setTimeout(n,e);t.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})});function g(e,n){(0,p.useEffect)(()=>{let r=new Map;for(let[t,i]of Object.entries(e))for(let e of i)r.set(e,n[t]??t);let i=()=>{for(let e of document.querySelectorAll(`[data-node-id]`)){let n=r.get(e.getAttribute(`data-node-id`));n?(e.setAttribute(`data-task-scope`,n),e.setAttribute(`title`,t(`In task: ${n}`,`작업 중: ${n}`,`任务中:${n}`))):e.hasAttribute(`data-task-scope`)&&(e.removeAttribute(`data-task-scope`),e.removeAttribute(`title`))}};i();let a=new MutationObserver(i);return a.observe(document.body,{childList:!0,subtree:!0}),()=>{a.disconnect(),r.clear(),i()}},[e,n])}function _({sessionId:e,pulse:t,labelOf:a,onMainNote:_}){let v=(0,p.useMemo)(()=>c(),[]),y=u(),[b,x]=(0,p.useReducer)((e,t)=>t.seed?s(e,t.seed):t.reset?n():r(e,t.event),void 0,n),[S,C]=(0,p.useState)({}),[w,T]=(0,p.useState)(f),E=(0,p.useRef)(0),D=(0,p.useRef)(_);(0,p.useEffect)(()=>{E.current=b.after,D.current=_},[b.after,_]),(0,p.useEffect)(()=>{x({reset:!0}),E.current=0},[e]),(0,p.useEffect)(()=>d(T),[]),(0,p.useEffect)(()=>y.subscribe(e=>{typeof e?.type!=`string`||!e.type.startsWith(`task.`)||(C(t=>i(t,e)),(e.type===`task.userEdit`||e.type===`task.undone`)&&x({event:e}))}),[y]),(0,p.useEffect)(()=>{if(!e)return;let t=new AbortController;return(async()=>{for(;!t.signal.aborted;){try{let{tasks:n}=await v.board(e);x({seed:n}),await v.events(e,E.current,e=>{if(e.type===`main.turn`&&e.turnId){let n=``;v.frames(e.turnId,e=>{e.type===`text.delta`&&(n+=e.text??``),e.type===`tool.start`&&(n=``)},t.signal).then(()=>D.current?.(n),()=>{})}x({event:e})},t.signal)}catch{}await h(m,t.signal)}})(),()=>t.abort()},[v,e,t]);let O=b.order.filter(e=>b.tasks[e]?.status===`running`&&b.tasks[e]?.turnId),k=O.map(e=>`${e}:${b.tasks[e].turnId}`).join();return(0,p.useEffect)(()=>{let e=O.map(e=>{let t=new AbortController,{turnId:n}=b.tasks[e];return v.frames(n,t=>{let n=o(t,a);n&&x({event:{type:`task.progress`,taskId:e,status:`running`,progress:n}})},t.signal).catch(()=>{}),t});return()=>e.forEach(e=>e.abort())},[v,k,a]),g(S,(0,p.useMemo)(()=>Object.fromEntries(b.order.map(e=>[e,b.tasks[e].title])),[b])),(0,p.useEffect)(()=>{l.set({client:v,labelOf:a,undo:async e=>{let t=await y.run(`task.undo`,{taskId:e});if(t?.ok===!1)throw Error(t.message??t.code);x({event:{type:`task.undone`,taskId:e}})}})},[y,v,a]),(0,p.useEffect)(()=>{l.set({board:b,activity:w})},[b,w]),(0,p.useEffect)(()=>()=>l.set({board:n(),open:null}),[e]),null}export{_ as default};
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{Fa as t,Ji as n,c as r,d as i,f as a,l as o,m as s,o as c,v as l}from"./task-board-CkLZGPkv.js";import{o as u,r as d,t as f}from"./motion3-compile-Du7UXrWn.js";var p=e(t(),1),m=2e3,h=(e,t)=>new Promise(n=>{let r=setTimeout(n,e);t.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})});function g(e,t){(0,p.useEffect)(()=>{let r=new Map;for(let[n,i]of Object.entries(e))for(let e of i)r.set(e,t[n]??n);let i=()=>{for(let e of document.querySelectorAll(`[data-node-id]`)){let t=r.get(e.getAttribute(`data-node-id`));t?(e.setAttribute(`data-task-scope`,t),e.setAttribute(`title`,n(`In task: ${t}`,`작업 중: ${t}`,`任务中:${t}`))):e.hasAttribute(`data-task-scope`)&&(e.removeAttribute(`data-task-scope`),e.removeAttribute(`title`))}};i();let a=new MutationObserver(i);return a.observe(document.body,{childList:!0,subtree:!0}),()=>{a.disconnect(),r.clear(),i()}},[e,t])}function _({sessionId:e,pulse:t,labelOf:n,onMainNote:_}){let v=(0,p.useMemo)(()=>c(),[]),y=u(),[b,x]=(0,p.useReducer)((e,t)=>t.seed?s(e,t.seed):t.reset?r():i(e,t.event),void 0,r),[S,C]=(0,p.useState)({}),[w,T]=(0,p.useState)(f),E=(0,p.useRef)(0),D=(0,p.useRef)(_);(0,p.useEffect)(()=>{E.current=b.after,D.current=_},[b.after,_]),(0,p.useEffect)(()=>{x({reset:!0}),E.current=0},[e]),(0,p.useEffect)(()=>d(T),[]),(0,p.useEffect)(()=>y.subscribe(e=>{typeof e?.type!=`string`||!e.type.startsWith(`task.`)||(C(t=>a(t,e)),(e.type===`task.userEdit`||e.type===`task.undone`)&&x({event:e}))}),[y]),(0,p.useEffect)(()=>{if(!e)return;let t=new AbortController;return(async()=>{for(;!t.signal.aborted;){try{let{tasks:n}=await v.board(e);x({seed:n}),await v.events(e,E.current,e=>{if(e.type===`main.turn`&&e.turnId){let n=``;v.frames(e.turnId,e=>{e.type===`text.delta`&&(n+=e.text??``),e.type===`tool.start`&&(n=``)},t.signal).then(()=>D.current?.(n),()=>{})}x({event:e})},t.signal)}catch{}await h(m,t.signal)}})(),()=>t.abort()},[v,e,t]);let O=b.order.filter(e=>b.tasks[e]?.status===`running`&&b.tasks[e]?.turnId),k=O.map(e=>`${e}:${b.tasks[e].turnId}`).join();return(0,p.useEffect)(()=>{let e=O.map(e=>{let t=new AbortController,{turnId:r}=b.tasks[e];return v.frames(r,t=>{let r=o(t,n);r&&x({event:{type:`task.progress`,taskId:e,status:`running`,progress:r}})},t.signal).catch(()=>{}),t});return()=>e.forEach(e=>e.abort())},[v,k,n]),g(S,(0,p.useMemo)(()=>Object.fromEntries(b.order.map(e=>[e,b.tasks[e].title])),[b])),(0,p.useEffect)(()=>{l.set({client:v,labelOf:n,undo:async e=>{let t=await y.run(`task.undo`,{taskId:e});if(t?.ok===!1)throw Error(t.message??t.code);x({event:{type:`task.undone`,taskId:e}})}})},[y,v,n]),(0,p.useEffect)(()=>{l.set({board:b,activity:w})},[b,w]),(0,p.useEffect)(()=>()=>l.set({board:r(),open:null}),[e]),null}export{_ as default};