agent-canvas 0.1.5 → 0.1.8

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 (64) hide show
  1. package/.github/workflows/ci.yml +23 -0
  2. package/.github/workflows/release.yml +36 -0
  3. package/CLAUDE.md +86 -0
  4. package/LICENSE +21 -0
  5. package/README.md +71 -0
  6. package/bin/agent-canvas.ts +26 -353
  7. package/bin/lib/commands/daemon.ts +51 -0
  8. package/bin/lib/commands/fetch.ts +20 -0
  9. package/bin/lib/commands/install.ts +48 -0
  10. package/bin/lib/commands/push.ts +67 -0
  11. package/bin/lib/commands/watch.ts +23 -0
  12. package/bin/lib/config.ts +10 -0
  13. package/bin/lib/daemon-lifecycle.ts +74 -0
  14. package/bin/lib/helpers.ts +78 -0
  15. package/bun.lock +176 -0
  16. package/daemon/build.ts +27 -61
  17. package/daemon/bunfig.toml +2 -0
  18. package/daemon/client/AnnotationProvider.tsx +2 -2
  19. package/daemon/client/AnnotationSidebar.tsx +199 -31
  20. package/daemon/client/App.tsx +39 -3
  21. package/daemon/client/FileBrowser.tsx +8 -7
  22. package/daemon/client/FileViewer.tsx +153 -16
  23. package/daemon/client/PlanRenderer.tsx +6 -3
  24. package/daemon/client/ResponsePreview.tsx +5 -116
  25. package/daemon/client/SessionSwitcher.tsx +21 -10
  26. package/daemon/client/compat/preact-all.ts +4 -0
  27. package/daemon/client/components/Markdown.tsx +44 -0
  28. package/daemon/client/components/ResponseNote.tsx +3 -8
  29. package/daemon/client/components/UserInput.tsx +2 -4
  30. package/daemon/client/components/index.ts +1 -0
  31. package/daemon/client/domDiff.ts +82 -2
  32. package/daemon/client/generateMarkdown.ts +46 -9
  33. package/daemon/client/highlightRange.ts +8 -6
  34. package/daemon/client/runtime.ts +2 -0
  35. package/daemon/client/styles.css +126 -0
  36. package/daemon/client/unifiedDiff.ts +155 -2
  37. package/daemon/client/utils.ts +10 -0
  38. package/daemon/dist/client.css +1 -1
  39. package/daemon/dist/client.js +83 -36
  40. package/daemon/dist/components.js +63 -5
  41. package/daemon/dist/index.html +5 -12
  42. package/daemon/dist/jsx-runtime.js +3 -0
  43. package/daemon/dist/preact-compat.js +1 -0
  44. package/daemon/langMap.ts +8 -0
  45. package/daemon/src/compiler.ts +28 -2
  46. package/daemon/src/handlers/api.ts +150 -0
  47. package/daemon/src/handlers/files.ts +114 -0
  48. package/daemon/src/handlers/static.ts +61 -0
  49. package/daemon/src/handlers/utils.ts +14 -0
  50. package/daemon/src/router.ts +16 -0
  51. package/daemon/src/server.ts +28 -403
  52. package/daemon/src/session.ts +43 -24
  53. package/daemon/src/websocket.ts +83 -0
  54. package/daemon/tsconfig.json +20 -0
  55. package/package.json +4 -14
  56. package/skills/canvas/SKILL.md +47 -22
  57. package/skills/canvas/components.md +31 -0
  58. package/skills/canvas/flows.md +2 -2
  59. package/tsconfig.json +14 -0
  60. package/daemon/dist/jsx-dev-runtime-shim.js +0 -17
  61. package/daemon/dist/jsx-runtime-shim.js +0 -17
  62. package/daemon/dist/react-dom-client-shim.js +0 -4
  63. package/daemon/dist/react-dom-shim.js +0 -4
  64. package/daemon/dist/react-shim.js +0 -4
@@ -0,0 +1,20 @@
1
+ import { parseArgs } from "util";
2
+ import { ensureDaemon } from "../daemon-lifecycle.ts";
3
+ import { getSessionId, consumeFeedback } from "../helpers.ts";
4
+
5
+ export async function handleFetch(args: string[]) {
6
+ const { values } = parseArgs({
7
+ args,
8
+ options: {
9
+ session: { type: "string" },
10
+ },
11
+ });
12
+
13
+ const sessionId = getSessionId(values.session);
14
+ await ensureDaemon();
15
+
16
+ const feedback = await consumeFeedback(sessionId);
17
+ if (feedback) {
18
+ process.stdout.write(feedback);
19
+ }
20
+ }
@@ -0,0 +1,48 @@
1
+ import { parseArgs } from "util";
2
+ import { existsSync, mkdirSync, cpSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { PACKAGE_ROOT } from "../config.ts";
6
+ import { readLine } from "../helpers.ts";
7
+
8
+ export async function handleInstall(args: string[]) {
9
+ const { positionals } = parseArgs({
10
+ args,
11
+ allowPositionals: true,
12
+ });
13
+
14
+ let mode = positionals[0] as "local" | "global" | undefined;
15
+
16
+ if (!mode) {
17
+ process.stdout.write("Install canvas skill for Claude Code.\n\n");
18
+ process.stdout.write(" local — install to .claude/ in current project\n");
19
+ process.stdout.write(" global — install to ~/.claude/ for all projects\n\n");
20
+ process.stdout.write("Choose [local/global]: ");
21
+
22
+ const input = await readLine();
23
+ mode = input.trim().toLowerCase() as "local" | "global";
24
+ }
25
+
26
+ if (mode !== "local" && mode !== "global") {
27
+ console.error("Error: specify 'local' or 'global'");
28
+ process.exit(1);
29
+ }
30
+
31
+ const targetBase = mode === "global"
32
+ ? join(homedir(), ".claude")
33
+ : join(process.cwd(), ".claude");
34
+
35
+ const skillTarget = join(targetBase, "skills", "canvas");
36
+ mkdirSync(skillTarget, { recursive: true });
37
+
38
+ const skillSrc = join(PACKAGE_ROOT, "skills", "canvas");
39
+ for (const file of ["SKILL.md", "components.md", "flows.md"]) {
40
+ const src = join(skillSrc, file);
41
+ if (existsSync(src)) {
42
+ cpSync(src, join(skillTarget, file));
43
+ }
44
+ }
45
+
46
+ console.log(` Skill installed to ${skillTarget}`);
47
+ console.log(`\nInstalled! The /canvas command is now available in Claude Code.`);
48
+ }
@@ -0,0 +1,67 @@
1
+ import { parseArgs } from "util";
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { resolve } from "path";
4
+ import { BASE_URL } from "../config.ts";
5
+ import { ensureDaemon } from "../daemon-lifecycle.ts";
6
+ import { getSessionId, openBrowser } from "../helpers.ts";
7
+
8
+ export async function handlePush(args: string[]) {
9
+ const { values, positionals } = parseArgs({
10
+ args,
11
+ options: {
12
+ session: { type: "string" },
13
+ label: { type: "string" },
14
+ response: { type: "string" },
15
+ },
16
+ allowPositionals: true,
17
+ });
18
+
19
+ const filePath = positionals[0];
20
+ if (!filePath) {
21
+ console.error("Error: No file specified. Usage: agent-canvas push <file.jsx>");
22
+ process.exit(1);
23
+ }
24
+
25
+ const resolvedPath = resolve(filePath);
26
+ if (!existsSync(resolvedPath)) {
27
+ console.error(`Error: File not found: ${resolvedPath}`);
28
+ process.exit(1);
29
+ }
30
+ const jsx = readFileSync(resolvedPath, "utf-8");
31
+
32
+ const sessionId = getSessionId(values.session);
33
+ const projectRoot = process.env.CANVAS_PROJECT_ROOT || process.cwd();
34
+ const label = values.label;
35
+ const autoLabel = !label ? resolvedPath.split("/").pop()?.replace(/\.jsx$/, "") : undefined;
36
+
37
+ await ensureDaemon();
38
+
39
+ const response = await fetch(`${BASE_URL}/api/session/${sessionId}/plan`, {
40
+ method: "POST",
41
+ headers: { "Content-Type": "application/json" },
42
+ body: JSON.stringify({ jsx, projectRoot, label: label || autoLabel, sourceFile: resolvedPath.split("/").pop(), ...(values.response ? { response: values.response } : {}) }),
43
+ });
44
+
45
+ const result = await response.json() as any;
46
+
47
+ if (!result.ok) {
48
+ if (result.unconsumedFeedback) {
49
+ console.error(`Error: ${result.error}`);
50
+ console.log(result.unconsumedFeedback);
51
+ } else {
52
+ console.error(`Compilation error:\n${result.error}`);
53
+ }
54
+ process.exit(1);
55
+ }
56
+
57
+ if (result.isNew) {
58
+ openBrowser(result.browserUrl);
59
+ }
60
+
61
+ console.log(JSON.stringify({
62
+ ok: true,
63
+ browserUrl: result.browserUrl,
64
+ revision: result.revision,
65
+ sessionId,
66
+ }));
67
+ }
@@ -0,0 +1,23 @@
1
+ import { parseArgs } from "util";
2
+ import { ensureDaemon } from "../daemon-lifecycle.ts";
3
+ import { getSessionId, consumeFeedback, waitForFeedback } from "../helpers.ts";
4
+
5
+ export async function handleWatch(args: string[]) {
6
+ const { values } = parseArgs({
7
+ args,
8
+ options: {
9
+ session: { type: "string" },
10
+ },
11
+ });
12
+
13
+ const sessionId = getSessionId(values.session);
14
+ await ensureDaemon();
15
+
16
+ const feedback = await consumeFeedback(sessionId);
17
+ if (feedback) {
18
+ process.stdout.write(feedback);
19
+ process.exit(0);
20
+ }
21
+
22
+ await waitForFeedback(sessionId);
23
+ }
@@ -0,0 +1,10 @@
1
+ import { resolve, join, dirname } from "path";
2
+ import { tmpdir } from "os";
3
+
4
+ export const PACKAGE_ROOT = resolve(join(dirname(import.meta.path), "../.."));
5
+ export const TEMP_DIR = join(tmpdir(), "agent-canvas");
6
+ export const DAEMON_PORT = parseInt(process.env.CANVAS_PORT || "19400", 10);
7
+ export const BASE_URL = `http://localhost:${DAEMON_PORT}`;
8
+ export const WS_URL = `ws://localhost:${DAEMON_PORT}`;
9
+ export const TIMEOUT_MS = parseInt(process.env.CANVAS_TIMEOUT || String(60 * 60 * 1000), 10);
10
+ export const PID_FILE = join(TEMP_DIR, "daemon.pid");
@@ -0,0 +1,74 @@
1
+ import { spawn } from "child_process";
2
+ import { openSync, closeSync, readFileSync, existsSync, mkdirSync, unlinkSync } from "fs";
3
+ import { join } from "path";
4
+ import { PACKAGE_ROOT, TEMP_DIR, BASE_URL, PID_FILE } from "./config.ts";
5
+
6
+ export async function isDaemonRunning(): Promise<boolean> {
7
+ try {
8
+ const res = await fetch(`${BASE_URL}/health`, { signal: AbortSignal.timeout(1000) });
9
+ const data = await res.json() as any;
10
+ return data.ok === true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ export async function startDaemon(): Promise<void> {
17
+ console.error("Starting canvas daemon...");
18
+ mkdirSync(TEMP_DIR, { recursive: true });
19
+
20
+ const daemonScript = join(PACKAGE_ROOT, "daemon", "src", "server.ts");
21
+ const stderrPath = join(TEMP_DIR, "daemon-startup.log");
22
+ const stderrFd = openSync(stderrPath, "w");
23
+
24
+ const child = spawn("bun", ["run", daemonScript], {
25
+ detached: true,
26
+ stdio: ["ignore", "ignore", stderrFd],
27
+ cwd: join(PACKAGE_ROOT, "daemon"),
28
+ });
29
+
30
+ child.unref();
31
+ closeSync(stderrFd);
32
+
33
+ for (let i = 0; i < 30; i++) {
34
+ await new Promise((r) => setTimeout(r, 100));
35
+ if (await isDaemonRunning()) {
36
+ console.error("Daemon started.");
37
+ return;
38
+ }
39
+ }
40
+
41
+ if (existsSync(stderrPath)) {
42
+ const stderr = readFileSync(stderrPath, "utf-8").trim();
43
+ if (stderr) {
44
+ console.error("Daemon stderr:\n" + stderr);
45
+ }
46
+ }
47
+
48
+ console.error("Error: Daemon failed to start within 3 seconds.");
49
+ process.exit(1);
50
+ }
51
+
52
+ export async function ensureDaemon(): Promise<void> {
53
+ if (await isDaemonRunning()) return;
54
+ await startDaemon();
55
+ }
56
+
57
+ export function stopDaemon(): boolean {
58
+ if (existsSync(PID_FILE)) {
59
+ const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10);
60
+ try {
61
+ process.kill(pid, "SIGTERM");
62
+ console.log("Daemon stopped.");
63
+ try { unlinkSync(PID_FILE); } catch {}
64
+ return true;
65
+ } catch {
66
+ console.log("Daemon was not running (stale PID file).");
67
+ try { unlinkSync(PID_FILE); } catch {}
68
+ return false;
69
+ }
70
+ } else {
71
+ console.log("No daemon PID file found.");
72
+ return false;
73
+ }
74
+ }
@@ -0,0 +1,78 @@
1
+ import { spawn } from "child_process";
2
+ import { randomUUID } from "crypto";
3
+ import { BASE_URL, WS_URL, TIMEOUT_MS } from "./config.ts";
4
+
5
+ export function getSessionId(session?: string): string {
6
+ if (session) return session;
7
+ return process.env.CANVAS_SESSION_ID || (() => {
8
+ const id = randomUUID();
9
+ console.error(`Warning: CANVAS_SESSION_ID not set, using generated ID: ${id}`);
10
+ return id;
11
+ })();
12
+ }
13
+
14
+ export async function consumeFeedback(sessionId: string): Promise<string | null> {
15
+ const res = await fetch(`${BASE_URL}/api/session/${sessionId}/feedback/consume`, { method: "POST" });
16
+ const data = await res.json() as any;
17
+ if (data.found) return data.feedback;
18
+ return null;
19
+ }
20
+
21
+ export function openBrowser(url: string) {
22
+ const candidates = process.platform === "darwin"
23
+ ? ["open"]
24
+ : ["xdg-open", "wslview", "sensible-browser"];
25
+
26
+ for (const cmd of candidates) {
27
+ const which = Bun.spawnSync(["which", cmd]);
28
+ if (which.exitCode === 0) {
29
+ spawn(cmd, [url], { detached: true, stdio: "ignore" }).unref();
30
+ return;
31
+ }
32
+ }
33
+ console.error(`Open this URL in your browser: ${url}`);
34
+ }
35
+
36
+ export async function waitForFeedback(sessionId: string): Promise<void> {
37
+ return new Promise((resolveP, rejectP) => {
38
+ const ws = new WebSocket(`${WS_URL}/ws/wait/${sessionId}`);
39
+ const timeout = setTimeout(() => {
40
+ ws.close();
41
+ console.error("Error: Timeout waiting for feedback.");
42
+ process.exit(1);
43
+ }, TIMEOUT_MS);
44
+
45
+ ws.onmessage = (event) => {
46
+ try {
47
+ const data = JSON.parse(typeof event.data === "string" ? event.data : "");
48
+ if (data.type === "submit") {
49
+ clearTimeout(timeout);
50
+ process.stdout.write(data.feedback);
51
+ ws.close();
52
+ process.exit(0);
53
+ }
54
+ } catch {}
55
+ };
56
+
57
+ ws.onerror = () => {
58
+ clearTimeout(timeout);
59
+ console.error("Error: WebSocket connection failed.");
60
+ process.exit(1);
61
+ };
62
+
63
+ ws.onclose = () => {
64
+ clearTimeout(timeout);
65
+ };
66
+ });
67
+ }
68
+
69
+ export async function readLine(): Promise<string> {
70
+ const buf: number[] = [];
71
+ for await (const chunk of Bun.stdin.stream()) {
72
+ for (const byte of chunk) {
73
+ if (byte === 10) return Buffer.from(buf).toString("utf-8");
74
+ buf.push(byte);
75
+ }
76
+ }
77
+ return Buffer.from(buf).toString("utf-8");
78
+ }
package/bun.lock ADDED
@@ -0,0 +1,176 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "agent-canvas",
7
+ "dependencies": {
8
+ "marked": "^17.0.4",
9
+ "preact": "^10.25.0",
10
+ },
11
+ "devDependencies": {
12
+ "@tailwindcss/cli": "4",
13
+ "@types/bun": "^1.3.10",
14
+ "@types/react": "^18.3.12",
15
+ "@types/react-dom": "^18.3.1",
16
+ "tailwindcss": "4",
17
+ "typescript": "^5.9.3",
18
+ },
19
+ },
20
+ },
21
+ "packages": {
22
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
23
+
24
+ "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
25
+
26
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
27
+
28
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
29
+
30
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
31
+
32
+ "@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="],
33
+
34
+ "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="],
35
+
36
+ "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="],
37
+
38
+ "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="],
39
+
40
+ "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="],
41
+
42
+ "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="],
43
+
44
+ "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="],
45
+
46
+ "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="],
47
+
48
+ "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="],
49
+
50
+ "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="],
51
+
52
+ "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="],
53
+
54
+ "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="],
55
+
56
+ "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="],
57
+
58
+ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
59
+
60
+ "@tailwindcss/cli": ["@tailwindcss/cli@4.2.1", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "enhanced-resolve": "^5.19.0", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.2.1" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-b7MGn51IA80oSG+7fuAgzfQ+7pZBgjzbqwmiv6NO7/+a1sev32cGqnwhscT7h0EcAvMa9r7gjRylqOH8Xhc4DA=="],
61
+
62
+ "@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="],
63
+
64
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="],
65
+
66
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="],
67
+
68
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="],
69
+
70
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw=="],
71
+
72
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="],
73
+
74
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw=="],
75
+
76
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ=="],
77
+
78
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="],
79
+
80
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g=="],
81
+
82
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="],
83
+
84
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="],
85
+
86
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="],
87
+
88
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="],
89
+
90
+ "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
91
+
92
+ "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
93
+
94
+ "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
95
+
96
+ "@types/react": ["@types/react@18.3.28", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw=="],
97
+
98
+ "@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
99
+
100
+ "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
101
+
102
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
103
+
104
+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
105
+
106
+ "enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
107
+
108
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
109
+
110
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
111
+
112
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
113
+
114
+ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
115
+
116
+ "lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
117
+
118
+ "lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
119
+
120
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
121
+
122
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
123
+
124
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
125
+
126
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
127
+
128
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
129
+
130
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
131
+
132
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="],
133
+
134
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
135
+
136
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
137
+
138
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
139
+
140
+ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
141
+
142
+ "marked": ["marked@17.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ=="],
143
+
144
+ "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
145
+
146
+ "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
147
+
148
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
149
+
150
+ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
151
+
152
+ "preact": ["preact@10.29.0", "", {}, "sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg=="],
153
+
154
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
155
+
156
+ "tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="],
157
+
158
+ "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
159
+
160
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
161
+
162
+ "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
163
+
164
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w=="],
165
+
166
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw=="],
167
+
168
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
169
+
170
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
171
+
172
+ "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
173
+
174
+ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
175
+ }
176
+ }
package/daemon/build.ts CHANGED
@@ -12,6 +12,26 @@ async function build() {
12
12
 
13
13
  const REACT_EXTERNALS = ["react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime"];
14
14
 
15
+ // 0. Build preact-compat bundle
16
+ console.log(" Building preact-compat...");
17
+ const preactResult = await Bun.build({
18
+ entrypoints: [join(ROOT, "client/compat/preact-all.ts")],
19
+ outdir: DIST,
20
+ format: "esm",
21
+ naming: "preact-compat.js",
22
+ minify: true,
23
+ });
24
+
25
+ if (!preactResult.success) {
26
+ console.error("Preact-compat build failed:", preactResult.logs);
27
+ process.exit(1);
28
+ }
29
+
30
+ // Write jsx-runtime shim
31
+ writeFileSync(join(DIST, "jsx-runtime.js"),
32
+ `import { jsx, jsxs, Fragment } from "./preact-compat.js";\nexport { jsx, jsxs, Fragment };\nexport const jsxDEV = jsx;\n`
33
+ );
34
+
15
35
  // 1. Build runtime (shared context between app and components)
16
36
  console.log(" Building #canvas/runtime...");
17
37
  const runtimeResult = await Bun.build({
@@ -67,11 +87,9 @@ async function build() {
67
87
  // 5. Read theme.css to inline into HTML for browser Tailwind runtime
68
88
  const themeCss = readFileSync(join(ROOT, "client/theme.css"), "utf-8");
69
89
 
70
- // 6. Create React shims for import maps
71
- // These re-export from the global React loaded via UMD
72
- console.log(" Creating React shims...");
90
+ // 6. Create index.html
91
+ console.log(" Creating index.html...");
73
92
 
74
- // Write the index.html to use CDN React
75
93
  const indexHtml = `<!DOCTYPE html>
76
94
  <html lang="en" data-theme="dark">
77
95
  <head>
@@ -91,21 +109,14 @@ ${themeCss}
91
109
  <script src="https://unpkg.com/@highlightjs/cdn-assets@11.11.1/highlight.min.js"></script>
92
110
  <script src="https://unpkg.com/mermaid@11.4.1/dist/mermaid.min.js"></script>
93
111
  <script>mermaid.initialize({ startOnLoad: false, theme: document.documentElement.dataset.theme === 'light' ? 'default' : 'dark' });</script>
94
- <script src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
95
- <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
96
- <script>
97
- // Create ESM modules from UMD globals
98
- window.__REACT_ESM__ = Object.keys(React).reduce((m, k) => { m[k] = React[k]; return m; }, { default: React, __esModule: true });
99
- window.__REACT_DOM_ESM__ = Object.keys(ReactDOM).reduce((m, k) => { m[k] = ReactDOM[k]; return m; }, { default: ReactDOM, __esModule: true });
100
- </script>
101
112
  <script type="importmap">
102
113
  {
103
114
  "imports": {
104
- "react": "/assets/react-shim.js",
105
- "react-dom": "/assets/react-dom-shim.js",
106
- "react-dom/client": "/assets/react-dom-client-shim.js",
107
- "react/jsx-runtime": "/assets/jsx-runtime-shim.js",
108
- "react/jsx-dev-runtime": "/assets/jsx-dev-runtime-shim.js",
115
+ "react": "/assets/preact-compat.js",
116
+ "react-dom": "/assets/preact-compat.js",
117
+ "react-dom/client": "/assets/preact-compat.js",
118
+ "react/jsx-runtime": "/assets/jsx-runtime.js",
119
+ "react/jsx-dev-runtime": "/assets/jsx-runtime.js",
109
120
  "#canvas/components": "/assets/components.js",
110
121
  "#canvas/runtime": "/assets/runtime.js"
111
122
  }
@@ -117,51 +128,6 @@ ${themeCss}
117
128
 
118
129
  writeFileSync(join(DIST, "index.html"), indexHtml);
119
130
 
120
- // React ESM shims
121
- writeFileSync(join(DIST, "react-shim.js"), `
122
- const R = window.React;
123
- export default R;
124
- export const { useState, useEffect, useRef, useCallback, useContext, useMemo, useReducer, createContext, createElement, Fragment, Children, cloneElement, isValidElement, memo, forwardRef, lazy, Suspense, startTransition, useTransition, useDeferredValue, useId, useSyncExternalStore, useInsertionEffect, useImperativeHandle, useLayoutEffect, useDebugValue, Component, PureComponent } = R;
125
- `);
126
-
127
- writeFileSync(join(DIST, "react-dom-shim.js"), `
128
- const RD = window.ReactDOM;
129
- export default RD;
130
- export const { createPortal, flushSync, hydrate, render, unmountComponentAtNode, findDOMNode, unstable_batchedUpdates } = RD;
131
- `);
132
-
133
- writeFileSync(join(DIST, "react-dom-client-shim.js"), `
134
- const RD = window.ReactDOM;
135
- export const { createRoot, hydrateRoot } = RD;
136
- export default { createRoot: RD.createRoot, hydrateRoot: RD.hydrateRoot };
137
- `);
138
-
139
- // jsxDEV signature: (type, props, key, isStaticChildren, source, self)
140
- // jsx/jsxs signature: (type, props, key)
141
- // createElement signature: (type, props, ...children)
142
- // We need to adapt: extract children from props and pass to createElement
143
- const jsxShimCode = `
144
- const R = window.React;
145
- export const Fragment = R.Fragment;
146
- function _jsx(type, props, key) {
147
- if (props && 'children' in props) {
148
- const { children, ...rest } = props;
149
- if (key !== undefined && key !== null) rest.key = key;
150
- return Array.isArray(children)
151
- ? R.createElement(type, rest, ...children)
152
- : R.createElement(type, rest, children);
153
- }
154
- if (key !== undefined && key !== null) props = { ...props, key };
155
- return R.createElement(type, props);
156
- }
157
- export const jsx = _jsx;
158
- export const jsxs = _jsx;
159
- export function jsxDEV(type, props, key) { return _jsx(type, props, key); }
160
- `;
161
-
162
- writeFileSync(join(DIST, "jsx-runtime-shim.js"), jsxShimCode);
163
- writeFileSync(join(DIST, "jsx-dev-runtime-shim.js"), jsxShimCode);
164
-
165
131
  console.log("Build complete! Output in dist/");
166
132
  }
167
133
 
@@ -0,0 +1,2 @@
1
+ [install]
2
+ peer = false