cozyclay 3.1.4 → 3.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +104 -0
- package/INSTALL_FOR_AGENTS.md +3 -0
- package/README.md +5 -0
- package/bin/agent/agent-failure-codes.mjs +31 -0
- package/bin/agent/agent-routes.mjs +5 -10
- package/bin/agent/agent-runner.mjs +5 -3
- package/bin/cozyclay.mjs +16 -64
- package/bin/studio-routes.mjs +69 -0
- package/bin/update-check.mjs +53 -5
- package/bin/update-install.mjs +136 -0
- package/bin/update-routes.mjs +80 -0
- package/dist/ai-camera-control/index.html +1 -1
- package/dist/app/index.html +5 -6
- package/dist/assets/{TaskBoard-U1Yl-4F1.js → TaskBoard-DwXhsJXz.js} +1 -1
- package/dist/assets/app-BLS1Zaru.js +5144 -0
- package/dist/assets/app-CEP7jzXB.css +1 -0
- package/dist/assets/compile-worker-or7djI2v.js +6 -0
- package/dist/assets/{motion3-compile-DDG3wklD.js → motion3-compile-Du7UXrWn.js} +1 -1
- package/dist/assets/scene-asset-sync-t45pWCyq.js +13 -0
- package/dist/assets/task-board-CkLZGPkv.js +15 -0
- package/dist/assets/workflow-DJwOUzqJ.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/greybox-to-video/index.html +3 -3
- package/dist/index.html +533 -264
- package/dist/media/cozyclay-mark.png +0 -0
- package/dist/media/cozyclay-truck-demo-poster.jpg +0 -0
- package/dist/media/step-move-poster.jpg +0 -0
- package/dist/media/step-shoot-poster.jpg +0 -0
- package/dist/media/step-stage-poster.jpg +0 -0
- package/dist/media/truck-greybox-poster.jpg +0 -0
- package/dist/media/truck-seedance-poster.jpg +0 -0
- package/dist/previs-software/index.html +4 -4
- package/dist/privacy/index.html +46 -19
- package/dist/seedance-camera-control/index.html +3 -3
- package/dist/sitemap.xml +6 -6
- package/dist/workflow/index.html +3 -4
- package/mcp/project-camera.mjs +43 -0
- package/package.json +2 -1
- package/src/App.jsx +59 -70
- package/src/analytics-controls.js +17 -0
- package/src/analytics-friction.js +124 -0
- package/src/analytics-strict.js +139 -0
- package/src/analytics-values.js +49 -0
- package/src/analytics.js +37 -133
- package/src/asset-pane.jsx +4 -5
- package/src/click-friction.js +101 -0
- package/src/command-bus-telemetry.js +155 -0
- package/src/command-bus.js +10 -8
- package/src/commands/motion3.js +7 -2
- package/src/controls.jsx +3 -2
- package/src/crash-telemetry.js +45 -0
- package/src/domains/cast.js +2 -11
- package/src/domains/motion.js +49 -38
- package/src/domains/objects.js +3 -0
- package/src/domains/scene-undo.js +27 -0
- package/src/domains/scenes.js +1 -1
- package/src/error-boundary.jsx +2 -0
- package/src/export-attempt.js +100 -0
- package/src/export-workspace.js +12 -1
- package/src/first-success-guide.jsx +11 -7
- package/src/guide-telemetry.js +60 -0
- package/src/hierarchy-panel.jsx +2 -1
- package/src/live-control.js +2 -9
- package/src/locale-zh/domains.js +1 -0
- package/src/main.jsx +5 -0
- package/src/motion/timeline.jsx +9 -5
- package/src/motion-draft-telemetry.js +62 -0
- package/src/motion3/compiler/channels.js +7 -5
- package/src/motion3/compiler/compile.js +6 -2
- package/src/motion3/compiler/finger-close.js +84 -7
- package/src/motion3/compiler/head-lead.js +70 -0
- package/src/motion3/compiler/head-steady.js +1 -1
- package/src/motion3/compiler/limbs.js +10 -3
- package/src/motion3/compiler/pose.js +1 -1
- package/src/motion3/compiler/validate.js +5 -3
- package/src/motion3/pipeline.js +4 -0
- package/src/motion3/qa/explain.js +2 -1
- package/src/motion3/qa/finger-checks.js +80 -0
- package/src/motion3/qa/gait-observe.js +88 -0
- package/src/motion3/qa/grasp-checks.js +3 -0
- package/src/motion3/qa/hints-grasp.js +8 -0
- package/src/motion3/qa/index.js +2 -1
- package/src/motion3/qa/motion-life.js +26 -3
- package/src/motion3/schema/topics.js +1 -1
- package/src/playback-telemetry.js +107 -0
- package/src/post-turn-edits.js +62 -0
- package/src/search-friction.js +49 -0
- package/src/semantic-edit.js +19 -6
- package/src/shell/BoardSlot.jsx +3 -3
- package/src/shell/BottomDock.jsx +1 -1
- package/src/shell/DetailsSlot.jsx +1 -1
- package/src/shell/MenuBar.jsx +13 -13
- package/src/shell/StudioShell.jsx +7 -1
- package/src/shell/TopBar.jsx +2 -0
- package/src/shell/ViewportToolbar.jsx +2 -2
- package/src/shortcut-friction.js +122 -0
- package/src/stage-canvas-rigs.jsx +3 -4
- package/src/studio-telemetry.js +97 -0
- package/src/thrash-friction.js +90 -0
- package/src/update/UpdateChip.jsx +86 -0
- package/src/update/UpdateDialog.jsx +147 -0
- package/src/update/update-client.js +188 -0
- package/src/update/update-telemetry.js +78 -0
- package/src/update/update.css +94 -0
- package/src/use-webgl-available.js +35 -0
- package/src/workflow/AgentPanel.jsx +29 -30
- package/src/workflow/CozySceneNode.jsx +1 -1
- package/src/workflow/LoginPanel.jsx +15 -5
- package/src/workflow/agent-client.js +10 -91
- package/src/workflow/agent-failure-codes.js +47 -0
- package/src/workflow/agent-turn-telemetry.js +222 -0
- package/src/workflow/connect-telemetry.js +86 -0
- package/src/workflow/prompt-telemetry.js +105 -0
- package/tools/dev/pages/privacy.html +44 -17
- package/tools/loc-baseline.json +10 -10
- package/tools/run-tests.mjs +8 -8
- package/tools/verify-package-tarball.mjs +29 -1
- package/dist/assets/app-BdRlMf4J.css +0 -1
- package/dist/assets/app-KXdgBC-S.js +0 -5144
- package/dist/assets/compile-worker-oy_P9HHh.js +0 -6
- package/dist/assets/landing-y3EqDgSZ.js +0 -1
- package/dist/assets/modulepreload-polyfill-P2Xu9kJm.js +0 -1
- package/dist/assets/scene-asset-sync-Bz59Ghdz.js +0 -13
- package/dist/assets/task-board-DFfNa0xg.js +0 -15
- package/dist/assets/workflow-BMJGd_gZ.js +0 -179
- package/dist/media/playground-poster.jpg +0 -0
|
@@ -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="
|
|
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>
|
package/dist/app/index.html
CHANGED
|
@@ -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-
|
|
37
|
+
<script type="module" crossorigin src="/assets/app-BLS1Zaru.js"></script>
|
|
38
38
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
39
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
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-
|
|
43
|
-
<link rel="modulepreload" crossorigin href="/assets/motion3-compile-
|
|
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-
|
|
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{
|
|
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};
|