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.
Files changed (126) hide show
  1. package/CHANGELOG.md +104 -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 +16 -64
  8. package/bin/studio-routes.mjs +69 -0
  9. package/bin/update-check.mjs +53 -5
  10. package/bin/update-install.mjs +136 -0
  11. package/bin/update-routes.mjs +80 -0
  12. package/dist/ai-camera-control/index.html +1 -1
  13. package/dist/app/index.html +5 -6
  14. package/dist/assets/{TaskBoard-U1Yl-4F1.js → TaskBoard-DwXhsJXz.js} +1 -1
  15. package/dist/assets/app-BLS1Zaru.js +5144 -0
  16. package/dist/assets/app-CEP7jzXB.css +1 -0
  17. package/dist/assets/compile-worker-or7djI2v.js +6 -0
  18. package/dist/assets/{motion3-compile-DDG3wklD.js → motion3-compile-Du7UXrWn.js} +1 -1
  19. package/dist/assets/scene-asset-sync-t45pWCyq.js +13 -0
  20. package/dist/assets/task-board-CkLZGPkv.js +15 -0
  21. package/dist/assets/workflow-DJwOUzqJ.js +179 -0
  22. package/dist/cozyclay-package.json +1 -1
  23. package/dist/greybox-to-video/index.html +3 -3
  24. package/dist/index.html +533 -264
  25. package/dist/media/cozyclay-mark.png +0 -0
  26. package/dist/media/cozyclay-truck-demo-poster.jpg +0 -0
  27. package/dist/media/step-move-poster.jpg +0 -0
  28. package/dist/media/step-shoot-poster.jpg +0 -0
  29. package/dist/media/step-stage-poster.jpg +0 -0
  30. package/dist/media/truck-greybox-poster.jpg +0 -0
  31. package/dist/media/truck-seedance-poster.jpg +0 -0
  32. package/dist/previs-software/index.html +4 -4
  33. package/dist/privacy/index.html +46 -19
  34. package/dist/seedance-camera-control/index.html +3 -3
  35. package/dist/sitemap.xml +6 -6
  36. package/dist/workflow/index.html +3 -4
  37. package/mcp/project-camera.mjs +43 -0
  38. package/package.json +2 -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/tools/verify-package-tarball.mjs +29 -1
  118. package/dist/assets/app-BdRlMf4J.css +0 -1
  119. package/dist/assets/app-KXdgBC-S.js +0 -5144
  120. package/dist/assets/compile-worker-oy_P9HHh.js +0 -6
  121. package/dist/assets/landing-y3EqDgSZ.js +0 -1
  122. package/dist/assets/modulepreload-polyfill-P2Xu9kJm.js +0 -1
  123. package/dist/assets/scene-asset-sync-Bz59Ghdz.js +0 -13
  124. package/dist/assets/task-board-DFfNa0xg.js +0 -15
  125. package/dist/assets/workflow-BMJGd_gZ.js +0 -179
  126. 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="/#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-KXdgBC-S.js"></script>
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/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};