@toothfairyai/tfdesign 0.4.8 → 0.4.9-beta.1
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/apps/web/out/404/index.html +1 -1
- package/apps/web/out/404.html +1 -1
- package/apps/web/out/__next.$oc$slug.__PAGE__.txt +3 -3
- package/apps/web/out/__next.$oc$slug.txt +3 -3
- package/apps/web/out/__next._full.txt +16 -16
- package/apps/web/out/__next._head.txt +4 -4
- package/apps/web/out/__next._index.txt +6 -7
- package/apps/web/out/__next._tree.txt +3 -3
- package/apps/web/out/_next/static/chunks/0-94cql6nl0jv.css +2 -0
- package/apps/web/out/_next/static/chunks/0-_4nyrx9a0p_.js +4586 -0
- package/apps/web/out/_next/static/chunks/012el4mxdksfa.js +1 -0
- package/apps/web/out/_next/static/chunks/033hdyh92lap-.js +1 -0
- package/apps/web/out/_next/static/chunks/{0~saitcv01ulm.js → 09kj3eb8dudq..js} +1 -3
- package/apps/web/out/_next/static/chunks/0eyc01y70gfu8.js +5 -0
- package/apps/web/out/_next/static/chunks/0gfud9-ayub5k.css +1 -0
- package/apps/web/out/_next/static/chunks/0otg4bwz5am.y.css +2 -0
- package/apps/web/out/_next/static/chunks/0qgizcmqgdduz.js +1 -0
- package/apps/web/out/_next/static/chunks/0rjd9p0m-xust.js +31 -0
- package/apps/web/out/_next/static/chunks/111k.piti8qwb.js +1 -0
- package/apps/web/out/_next/static/chunks/1434omkut_t67.js +1 -0
- package/apps/web/out/_next/static/chunks/{turbopack-0qjykn58ffuft.js → turbopack-15aa1bhzoau.r.js} +1 -3
- package/apps/web/out/_not-found/__next._full.txt +23 -19
- package/apps/web/out/_not-found/__next._head.txt +4 -4
- package/apps/web/out/_not-found/__next._index.txt +6 -7
- package/apps/web/out/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/apps/web/out/_not-found/__next._not-found.txt +3 -3
- package/apps/web/out/_not-found/__next._tree.txt +3 -3
- package/apps/web/out/_not-found/index.html +1 -1
- package/apps/web/out/_not-found/index.txt +23 -19
- package/apps/web/out/align-center.png +0 -0
- package/apps/web/out/align-right.png +0 -0
- package/apps/web/out/align-text.png +0 -0
- package/apps/web/out/app-icon.png +0 -0
- package/apps/web/out/app-icon.svg +1 -1
- package/apps/web/out/bottom-alignment.png +0 -0
- package/apps/web/out/fonts/Gilroy-Regular.woff2 +0 -0
- package/apps/web/out/fonts/Gilroy-SemiBold.woff2 +0 -0
- package/apps/web/out/html2canvas.min.js +20 -0
- package/apps/web/out/icon-add-file.png +0 -0
- package/apps/web/out/icon-copy.png +0 -0
- package/apps/web/out/icon-remove.png +0 -0
- package/apps/web/out/index.html +1 -1
- package/apps/web/out/index.txt +16 -16
- package/apps/web/out/left-alignment.png +0 -0
- package/apps/web/out/logo.svg +14 -5
- package/apps/web/out/top-alignment.png +0 -0
- package/dist/{chunk-VIER63EE.js → chunk-Q2ALQSZX.js} +590 -110
- package/dist/cli.js +20 -5
- package/dist/{server-RXVSJGZG.js → server-PZVSL7GD.js} +1 -1
- package/package.json +11 -1
- package/apps/web/out/_next/static/chunks/00.vhdzyqgugx.js.map +0 -1
- package/apps/web/out/_next/static/chunks/026q~go~dh3ct.js.map +0 -1
- package/apps/web/out/_next/static/chunks/03lp76x6ml3q7.js.map +0 -1
- package/apps/web/out/_next/static/chunks/03rwb84ufo-ml.css +0 -2
- package/apps/web/out/_next/static/chunks/03~yq9q893hmn.js.map +0 -1
- package/apps/web/out/_next/static/chunks/05z1ica28h4v4.js.map +0 -1
- package/apps/web/out/_next/static/chunks/06pwisw_r78h0.js.map +0 -1
- package/apps/web/out/_next/static/chunks/07e0~_ag4wd_2.js +0 -3
- package/apps/web/out/_next/static/chunks/0856t9l8e24kl.js +0 -3459
- package/apps/web/out/_next/static/chunks/09dn2kc7uy573.css +0 -3
- package/apps/web/out/_next/static/chunks/0_h7u0wcctdvf.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0bfs9u56d4mzi.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0bnhnqs10zvm0.js +0 -3
- package/apps/web/out/_next/static/chunks/0b~oqibb-klk8.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0dlz0h-q.e8t5.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0e2qn3b70-t2i.js +0 -34
- package/apps/web/out/_next/static/chunks/0fv_wqm4-r1hb.js +0 -12
- package/apps/web/out/_next/static/chunks/0fysc_wo2kzsu.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0i-mjuoftnsa3.js +0 -3
- package/apps/web/out/_next/static/chunks/0ix3s5zdxhkjq.js +0 -12
- package/apps/web/out/_next/static/chunks/0ovz1kz3yt45_.js +0 -33
- package/apps/web/out/_next/static/chunks/0ps7zql~4nu4c.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0qans9q_q-pop.js +0 -7
- package/apps/web/out/_next/static/chunks/0t3k3fodzjqbk.js +0 -3
- package/apps/web/out/_next/static/chunks/0vh07vpip-7jr.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0weq586uhw-26.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0y1f9xlua9~_u.css +0 -2
- package/apps/web/out/_next/static/chunks/0zvsnz1lw0thi.js.map +0 -1
- package/apps/web/out/_next/static/chunks/0~ge_azerwvf9.css.map +0 -6
- package/apps/web/out/_next/static/chunks/0~wbkak703p3z.js +0 -3
- package/apps/web/out/_next/static/chunks/11uef98fc_tfp.css.map +0 -1
- package/apps/web/out/_next/static/chunks/14hojq718_2.u.js +0 -34
- package/apps/web/out/_next/static/chunks/15j8a~eyh6.eg.css.map +0 -1
- package/apps/web/out/_next/static/chunks/16cm9w~y8~hqh.js.map +0 -1
- package/apps/web/out/_next/static/chunks/17p64lujxa85m.js +0 -3
- package/apps/web/out/agent-icons/aider.png +0 -0
- package/apps/web/out/agent-icons/amr.svg +0 -8
- package/apps/web/out/agent-icons/antigravity.svg +0 -1
- package/apps/web/out/agent-icons/claude.svg +0 -1
- package/apps/web/out/agent-icons/codex.svg +0 -1
- package/apps/web/out/agent-icons/copilot.svg +0 -1
- package/apps/web/out/agent-icons/cursor-agent.svg +0 -1
- package/apps/web/out/agent-icons/deepseek.svg +0 -1
- package/apps/web/out/agent-icons/devin.png +0 -0
- package/apps/web/out/agent-icons/gemini.svg +0 -1
- package/apps/web/out/agent-icons/grok-build.svg +0 -15
- package/apps/web/out/agent-icons/hermes.svg +0 -1
- package/apps/web/out/agent-icons/kilo.svg +0 -1
- package/apps/web/out/agent-icons/kimi.svg +0 -1
- package/apps/web/out/agent-icons/kiro.svg +0 -1
- package/apps/web/out/agent-icons/mimo.svg +0 -1
- package/apps/web/out/agent-icons/opencode.svg +0 -1
- package/apps/web/out/agent-icons/pi.svg +0 -1
- package/apps/web/out/agent-icons/qoder.svg +0 -1
- package/apps/web/out/agent-icons/qwen.svg +0 -1
- package/apps/web/out/agent-icons/reasonix.svg +0 -37
- package/apps/web/out/agent-icons/trae-cli.png +0 -0
- package/apps/web/out/agent-icons/vibe.svg +0 -1
- package/apps/web/out/brand-icon.svg +0 -5
- package/apps/web/out/desktop-pet/__next._full.txt +0 -20
- package/apps/web/out/desktop-pet/__next._head.txt +0 -6
- package/apps/web/out/desktop-pet/__next._index.txt +0 -8
- package/apps/web/out/desktop-pet/__next._tree.txt +0 -3
- package/apps/web/out/desktop-pet/__next.desktop-pet.__PAGE__.txt +0 -6
- package/apps/web/out/desktop-pet/__next.desktop-pet.txt +0 -5
- package/apps/web/out/desktop-pet/index.html +0 -1
- package/apps/web/out/desktop-pet/index.txt +0 -20
- package/apps/web/out/logo.png +0 -0
- package/apps/web/out/model-icons/black-forest-labs.svg +0 -10
- package/apps/web/out/model-icons/bytedance.svg +0 -1
- package/apps/web/out/model-icons/elevenlabs.svg +0 -1
- package/apps/web/out/model-icons/fishaudio.svg +0 -1
- package/apps/web/out/model-icons/flux.svg +0 -1
- package/apps/web/out/model-icons/google-gemini.svg +0 -1
- package/apps/web/out/model-icons/minimax.svg +0 -1
- package/apps/web/out/model-icons/openai.svg +0 -1
- package/apps/web/out/model-icons/openrouter.svg +0 -1
- package/apps/web/out/model-icons/suno.svg +0 -1
- package/apps/web/out/model-icons/x.svg +0 -1
- package/apps/web/out/official_badge.svg +0 -48
- package/apps/web/out/remixicon.woff2 +0 -0
- /package/apps/web/out/_next/static/{EOfAKklAkTBpVS50QG3jX → LZdEU9ZvDchB9VDyfg54d}/_buildManifest.js +0 -0
- /package/apps/web/out/_next/static/{EOfAKklAkTBpVS50QG3jX → LZdEU9ZvDchB9VDyfg54d}/_clientMiddlewareManifest.js +0 -0
- /package/apps/web/out/_next/static/{EOfAKklAkTBpVS50QG3jX → LZdEU9ZvDchB9VDyfg54d}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
releaseAllEngines,
|
|
3
4
|
resolveDataRoot,
|
|
4
5
|
startServer
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Q2ALQSZX.js";
|
|
6
7
|
import {
|
|
7
8
|
getJson,
|
|
8
9
|
resolveProjectId
|
|
@@ -757,7 +758,9 @@ function splitOnQuestionForms(input) {
|
|
|
757
758
|
const openEnd = openStart + m[0].length;
|
|
758
759
|
const closeIdx = input.indexOf(CLOSE_TAG, openEnd);
|
|
759
760
|
if (closeIdx === -1) {
|
|
760
|
-
|
|
761
|
+
if (openStart > cursor) {
|
|
762
|
+
out.push({ kind: "text", text: input.slice(cursor, openStart) });
|
|
763
|
+
}
|
|
761
764
|
break;
|
|
762
765
|
}
|
|
763
766
|
if (openStart > cursor) {
|
|
@@ -1295,7 +1298,7 @@ function sameRoot(a, b) {
|
|
|
1295
1298
|
}
|
|
1296
1299
|
async function bootInProcess(root) {
|
|
1297
1300
|
process.env.TFDESIGN_DATA_DIR = root;
|
|
1298
|
-
const { startServer: startServer2 } = await import("./server-
|
|
1301
|
+
const { startServer: startServer2 } = await import("./server-PZVSL7GD.js");
|
|
1299
1302
|
const { url, server } = await startServer2({ port: 0, returnServer: true });
|
|
1300
1303
|
return { url, booted: true, server, dataRoot: root };
|
|
1301
1304
|
}
|
|
@@ -2008,7 +2011,7 @@ if (argv[0] === "tools" && argv[1] === "live-artifacts") {
|
|
|
2008
2011
|
process.exitCode = 1;
|
|
2009
2012
|
});
|
|
2010
2013
|
} else {
|
|
2011
|
-
let port = Number(process.env.OD_PORT)
|
|
2014
|
+
let port = process.env.OD_PORT !== void 0 && Number.isFinite(Number(process.env.OD_PORT)) ? Number(process.env.OD_PORT) : 7456;
|
|
2012
2015
|
let host = process.env.OD_BIND_HOST || "127.0.0.1";
|
|
2013
2016
|
let open = true;
|
|
2014
2017
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -2024,6 +2027,7 @@ if (argv[0] === "tools" && argv[1] === "live-artifacts") {
|
|
|
2024
2027
|
process.exit(0);
|
|
2025
2028
|
}
|
|
2026
2029
|
}
|
|
2030
|
+
installEngineCleanup();
|
|
2027
2031
|
startServer({ port, host }).then((url) => {
|
|
2028
2032
|
console.log(`[${invoked}] listening on ${url}`);
|
|
2029
2033
|
if (open) {
|
|
@@ -2034,8 +2038,18 @@ if (argv[0] === "tools" && argv[1] === "live-artifacts") {
|
|
|
2034
2038
|
}
|
|
2035
2039
|
});
|
|
2036
2040
|
}
|
|
2041
|
+
function installEngineCleanup() {
|
|
2042
|
+
const stop = () => releaseAllEngines();
|
|
2043
|
+
process.once("exit", stop);
|
|
2044
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
2045
|
+
process.once(sig, () => {
|
|
2046
|
+
stop();
|
|
2047
|
+
process.exit(0);
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2037
2051
|
async function runServe(args) {
|
|
2038
|
-
let port = Number(process.env.OD_PORT)
|
|
2052
|
+
let port = process.env.OD_PORT !== void 0 && Number.isFinite(Number(process.env.OD_PORT)) ? Number(process.env.OD_PORT) : 7456;
|
|
2039
2053
|
let host = process.env.OD_BIND_HOST || "127.0.0.1";
|
|
2040
2054
|
for (let i = 0; i < args.length; i++) {
|
|
2041
2055
|
const a = args[i];
|
|
@@ -2052,6 +2066,7 @@ Start the local daemon + web UI without opening a browser (server mode).`);
|
|
|
2052
2066
|
}
|
|
2053
2067
|
const { url } = await startServer({ port, host, returnServer: true });
|
|
2054
2068
|
console.log(`[${invoked}] listening on ${url} (server mode, no browser)`);
|
|
2069
|
+
installEngineCleanup();
|
|
2055
2070
|
await new Promise(() => {
|
|
2056
2071
|
});
|
|
2057
2072
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toothfairyai/tfdesign",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9-beta.1",
|
|
4
4
|
"description": "TF Design — the local design workspace CLI: headless design runs (decks, posters, sites) through the tfdesign daemon, delegable from tfcode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=22"
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"apps",
|
|
16
|
+
"skills",
|
|
17
|
+
"design-systems",
|
|
18
|
+
"craft",
|
|
19
|
+
"assets",
|
|
20
|
+
"prompt-templates",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
13
23
|
"dependencies": {
|
|
14
24
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
15
25
|
"better-sqlite3": "^12.9.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["turbopack:///[project]/apps/web/src/analytics/scrub.ts","turbopack:///[project]/apps/web/src/analytics/error-tracking.ts","turbopack:///[project]/apps/web/src/analytics/provider.tsx","turbopack:///[project]/packages/contracts/dist/analytics/index.mjs","turbopack:///[project]/apps/web/src/analytics/client.ts","turbopack:///[project]/apps/web/src/analytics/identity.ts"],"sourcesContent":["// Single point of privacy scrubbing for outgoing PostHog events.\n// Wired in via posthog-js's `before_send` hook so every autocapture,\n// pageview, exception, web-vital, etc. passes through this function\n// before reaching the network. Returning null drops the event.\n//\n// The masking rules here intentionally over-redact rather than rely on\n// per-element `ph-no-capture` marks scattered across the codebase. A\n// single function is easier to audit and harder to forget when a new\n// sensitive surface ships.\n\nimport type { CaptureResult } from 'posthog-js';\n\n// Tags whose text content can carry user-typed values. PostHog autocapture\n// does not capture input/textarea `value` properties by default, but it\n// does capture `$el_text` (element.textContent) — for a <textarea> with\n// typed content that becomes the prompt body. Strip eagerly.\nconst TEXT_BEARING_TAGS = new Set(['input', 'textarea']);\n\nfunction scrubElementsChain(\n elements: Array<Record<string, unknown>>,\n): Array<Record<string, unknown>> {\n return elements.map((el) => {\n const tag = typeof el.tag_name === 'string' ? el.tag_name.toLowerCase() : '';\n const contentEditable =\n typeof el.attr__contenteditable === 'string' &&\n el.attr__contenteditable !== 'false';\n const isPasswordInput =\n tag === 'input' &&\n typeof el.attr__type === 'string' &&\n el.attr__type.toLowerCase() === 'password';\n const shouldScrub =\n TEXT_BEARING_TAGS.has(tag) || contentEditable || isPasswordInput;\n if (!shouldScrub) return el;\n const cleaned: Record<string, unknown> = { ...el };\n delete cleaned.$el_text;\n delete cleaned.attr__value;\n delete cleaned.attr__placeholder;\n delete cleaned.attr__aria_label;\n delete cleaned.text;\n return cleaned;\n });\n}\n\n// Drop query-string and fragment from URLs in pageview / pageleave / nav\n// events. Pathnames are kept (they're typically `/projects/<uuid>`,\n// non-sensitive) but any `?q=…` we accidentally introduce in the future\n// won't leak.\nfunction scrubUrl(url: unknown): unknown {\n if (typeof url !== 'string') return url;\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}`;\n } catch {\n return url;\n }\n}\n\n// Rewrite absolute filesystem paths in exception stack traces. Packaged\n// builds expose `file:///Applications/Open Design.app/Contents/Resources/…`\n// which leaks both the install root and the user's home dir in homebrew /\n// custom installs. Reduce to the repo-relative tail.\n//\n// Exported so error-tracking.ts can apply the same scrub to events it\n// dispatches directly to PostHog (bypassing posthog-js's before_send).\nexport function scrubFilePath(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n // file:///abs/path/.../apps/web/src/foo.tsx → app://apps/web/src/foo.tsx\n // /Users/<user>/.../apps/web/src/foo.tsx → app://apps/web/src/foo.tsx\n //\n // The prefix uses `[^()\\n]*?` (non-greedy, no parens/newlines) so paths\n // that contain spaces — most notably the packaged macOS layout\n // `/Applications/Open Design.app/Contents/Resources/...` — get fully\n // rewritten instead of partially leaking the install directory. The\n // tail stops at whitespace or a closing paren so stack frames of shape\n // `at fn (file:///.../foo.tsx:1:2)` lose only the path portion.\n return value.replace(\n /(?:file:\\/\\/)?[^()\\n]*?\\/((?:apps|packages|tools)\\/[^\\s)]+)/g,\n 'app://$1',\n );\n}\n\n// Exported so error-tracking.ts can apply the same scrub before it sends\n// a directly-built `$exception` payload to PostHog.\nexport function scrubExceptionList(\n list: Array<Record<string, unknown>>,\n): Array<Record<string, unknown>> {\n return list.map((entry) => {\n const next = { ...entry };\n const stack = next.stacktrace as\n | { frames?: Array<Record<string, unknown>> }\n | undefined;\n if (stack?.frames && Array.isArray(stack.frames)) {\n next.stacktrace = {\n ...stack,\n frames: stack.frames.map((frame) => ({\n ...frame,\n filename: scrubFilePath(frame.filename),\n abs_path: scrubFilePath(frame.abs_path),\n })),\n };\n }\n if (typeof next.mechanism === 'object' && next.mechanism !== null) {\n // Mechanism source URL can also be a file:// — same scrub.\n const mech = next.mechanism as Record<string, unknown>;\n if (typeof mech.source === 'string') {\n mech.source = scrubFilePath(mech.source);\n }\n }\n return next;\n });\n}\n\n// Some events we don't need at all; suppressing them keeps the volume\n// below PostHog's free-tier cap and avoids capturing surfaces that don't\n// add product insight.\nconst SUPPRESSED_EVENTS = new Set<string>([\n // PostHog's $opt_in event records the act of opting in. We already\n // emit explicit consent via the toggle handler in App.tsx; the\n // duplicate is noise.\n '$opt_in',\n]);\n\nexport function scrubBeforeSend(cr: CaptureResult | null): CaptureResult | null {\n if (!cr) return null;\n if (SUPPRESSED_EVENTS.has(cr.event)) return null;\n\n const props = (cr.properties ?? {}) as Record<string, unknown>;\n\n // Autocapture / rageclick / dead-click carry $elements (legacy) or\n // $elements_chain (newer). Both shapes get the same scrub.\n const elementBearing =\n cr.event === '$autocapture' ||\n cr.event === '$rageclick' ||\n cr.event === '$dead_click' ||\n cr.event === '$copy_autocapture';\n if (elementBearing) {\n const elements = props.$elements;\n if (Array.isArray(elements)) {\n props.$elements = scrubElementsChain(elements as Array<Record<string, unknown>>);\n }\n }\n\n // URL-bearing events.\n if (typeof props.$current_url === 'string') {\n props.$current_url = scrubUrl(props.$current_url);\n }\n if (typeof props.$pathname === 'string') {\n // Pathnames in this app are routing slugs (/projects/<uuid>) — keep\n // as-is. Query strings live on $current_url, not $pathname.\n }\n if (typeof props.$referrer === 'string') {\n props.$referrer = scrubUrl(props.$referrer);\n }\n\n // Exceptions: scrub file paths in stack frames.\n if (cr.event === '$exception') {\n const list = props.$exception_list;\n if (Array.isArray(list)) {\n props.$exception_list = scrubExceptionList(list as Array<Record<string, unknown>>);\n }\n if (typeof props.$exception_source === 'string') {\n props.$exception_source = scrubFilePath(props.$exception_source);\n }\n }\n\n return { ...cr, properties: props };\n}\n","// Direct-fetch safety telemetry transport.\n//\n// Why this exists alongside posthog-js's autocapture\n// ---------------------------------------------------\n// Two design constraints make posthog-js's normal capture path insufficient\n// for safety / reliability telemetry:\n//\n// 1. **Consent gate.** `posthog.opt_out_capturing()` silences ALL captures.\n// Product policy is that *safety* telemetry — exceptions, white\n// screens, dropped chunks, long tasks, stuck runs — flows\n// unconditionally so we don't lose ground truth on stability when a\n// user opts out of general analytics. The user-facing copy in\n// Settings → Privacy must reflect this.\n//\n// 2. **Lazy-load window.** posthog-js is dynamically `import()`-ed only\n// after `/api/analytics/config` returns AND the user has consented.\n// Errors / metrics that fire during the first 1-2 seconds (React\n// hydration, early effects, route init) are lost. We hook the\n// relevant browser events synchronously at module load, before any\n// of that, and buffer until we have credentials.\n//\n// This module exposes two surfaces:\n//\n// - `reportHandledException` / `installErrorHandlers` — emit shaped\n// `$exception` events (used directly + via `window.error` /\n// `unhandledrejection`).\n// - `reportSafetyEvent(eventName, properties)` — generic transport for\n// non-exception observability events (long tasks, white screens,\n// resource errors, boot timing, etc.) that need the same\n// consent-bypass + early-buffer guarantees.\n//\n// To avoid duplicate `$exception` events, `client.ts` sets\n// `capture_exceptions: false` on the posthog-js init — this module is the\n// single source of truth for browser exception capture.\n\nimport { scrubExceptionList, scrubFilePath } from './scrub';\n\ninterface ExceptionTrackingContext {\n apiKey: string;\n host: string;\n distinctId: string;\n appVersion?: string;\n sessionId?: string;\n}\n\ninterface BufferedSafetyEvent {\n eventName: string;\n body: { properties: Record<string, unknown> };\n timestamp: string;\n}\n\n// Cap the buffer so a chain of early errors (e.g. infinite render loop\n// before posthog-js loads) cannot grow indefinitely. 50 is enough to\n// capture the burst that usually surrounds a real bug while keeping the\n// memory footprint trivial.\nconst MAX_BUFFER_SIZE = 50;\n\nlet context: ExceptionTrackingContext | null = null;\nconst buffer: BufferedSafetyEvent[] = [];\nlet installed = false;\n\nexport function setExceptionTrackingContext(next: ExceptionTrackingContext): void {\n context = next;\n if (buffer.length === 0) return;\n const drain = buffer.splice(0, buffer.length);\n for (const item of drain) {\n dispatch(item);\n }\n}\n\nexport function clearExceptionTrackingContext(): void {\n // Called when /api/analytics/config returns `key: null` (no build-time\n // POSTHOG_KEY, e.g. a fork build). The buffered events stay in memory\n // until the page unloads — no key, nowhere to send them, but also\n // nothing leaks.\n context = null;\n}\n\n// Called once at app boot. Idempotent — repeated calls are no-ops.\nexport function installErrorHandlers(): void {\n if (installed) return;\n if (typeof window === 'undefined') return;\n installed = true;\n\n window.addEventListener('error', (event) => {\n captureException(event.error, event.message ?? 'Uncaught error', {\n filename: typeof event.filename === 'string' ? event.filename : undefined,\n lineno: typeof event.lineno === 'number' ? event.lineno : undefined,\n colno: typeof event.colno === 'number' ? event.colno : undefined,\n });\n });\n\n window.addEventListener('unhandledrejection', (event) => {\n const reason = event.reason;\n const fallback =\n typeof reason === 'string' ? reason : 'Unhandled promise rejection';\n captureException(reason, fallback);\n });\n}\n\n// Public entry point for code paths that catch their own error but still\n// want it visible in PostHog (e.g. an ErrorBoundary's componentDidCatch).\n// Unhandled errors go through the window listeners above.\nexport function reportHandledException(error: unknown, message?: string): void {\n captureException(error, message ?? defaultMessage(error), { handled: true });\n}\n\ninterface CaptureMetadata {\n filename?: string;\n lineno?: number;\n colno?: number;\n handled?: boolean;\n}\n\nfunction captureException(\n error: unknown,\n fallbackMessage: string,\n metadata: CaptureMetadata = {},\n): void {\n const list = buildExceptionList(error, fallbackMessage, metadata);\n const scrubbed = scrubExceptionList(list);\n const properties: Record<string, unknown> = {\n $exception_list: scrubbed,\n $exception_type: scrubbed[0]?.type,\n $exception_message: scrubbed[0]?.value,\n $exception_source: scrubFirstFrameSource(scrubbed),\n $current_url: scrubUrl(typeof window !== 'undefined' ? window.location.href : ''),\n $insert_id: randomId(),\n capture_source: 'web/error-tracking',\n handled: metadata.handled === true,\n };\n\n enqueue('$exception', properties);\n}\n\n// Generic safety-telemetry surface for non-exception observability events\n// (long tasks, white screens, resource errors, boot timing, stuck runs,\n// visibility changes, etc.). Goes through the same buffer + direct-fetch\n// transport as `$exception` so the same consent-bypass + early-firing\n// guarantees apply. Callers should namespace event names with `client_*`\n// (or `desktop_*` / `daemon_*` for cross-process forwards) so they're\n// easy to filter against posthog-js-captured product events.\nexport function reportSafetyEvent(\n eventName: string,\n properties: Record<string, unknown> = {},\n): void {\n const merged: Record<string, unknown> = {\n ...properties,\n $current_url: scrubUrl(typeof window !== 'undefined' ? window.location.href : ''),\n $insert_id: randomId(),\n capture_source: 'web/error-tracking',\n };\n enqueue(eventName, merged);\n}\n\nfunction enqueue(eventName: string, properties: Record<string, unknown>): void {\n const timestamp = new Date().toISOString();\n const item: BufferedSafetyEvent = {\n eventName,\n body: { properties },\n timestamp,\n };\n if (context == null) {\n if (buffer.length >= MAX_BUFFER_SIZE) buffer.shift();\n buffer.push(item);\n return;\n }\n dispatch(item);\n}\n\nfunction dispatch(item: BufferedSafetyEvent): void {\n if (context == null) return;\n const payload = {\n api_key: context.apiKey,\n event: item.eventName,\n distinct_id: context.distinctId,\n properties: {\n ...item.body.properties,\n $lib: 'web/error-tracking',\n ...(context.appVersion ? { app_version: context.appVersion, ui_version: context.appVersion } : {}),\n ...(context.sessionId ? { session_id: context.sessionId } : {}),\n },\n timestamp: item.timestamp,\n };\n // `keepalive` ensures the request survives an immediate window unload —\n // important for events that fire during navigation that are followed by\n // a route change a millisecond later.\n try {\n void fetch(`${context.host.replace(/\\/+$/, '')}/i/v0/e/`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n keepalive: true,\n // No credentials — PostHog ingest uses the public `phc_` key as the\n // auth surface; cookies are irrelevant and sending them would just\n // add CORS preflight friction.\n credentials: 'omit',\n });\n } catch {\n // best-effort: safety telemetry must never propagate\n }\n}\n\nfunction buildExceptionList(\n error: unknown,\n fallbackMessage: string,\n metadata: CaptureMetadata,\n): Array<Record<string, unknown>> {\n const isError = error instanceof Error;\n const type = isError ? error.name : typeof error === 'string' ? 'Error' : 'NonError';\n const value = isError\n ? error.message\n : typeof error === 'string'\n ? error\n : fallbackMessage;\n const stack = isError && typeof error.stack === 'string' ? error.stack : '';\n const frames = parseStack(stack, metadata);\n return [\n {\n type,\n value,\n stacktrace: { type: 'raw', frames },\n mechanism: {\n type: metadata.handled === true ? 'handled' : 'generic',\n handled: metadata.handled === true,\n },\n },\n ];\n}\n\n// Minimal stack parser. Covers V8 (`at Foo (url:1:2)` and `at url:1:2`)\n// and the SpiderMonkey-style `Foo@url:1:2`. Lines we cannot parse are\n// kept as a raw line so the report stays useful even without symbolicated\n// frames.\nconst STACK_RE_V8 = /^\\s*at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/;\nconst STACK_RE_SPIDERMONKEY = /^(.*?)@(.+?):(\\d+):(\\d+)$/;\n\nfunction parseStack(stack: string, metadata: CaptureMetadata): Array<Record<string, unknown>> {\n if (!stack) {\n if (metadata.filename) {\n return [\n {\n function: '<anonymous>',\n filename: metadata.filename,\n abs_path: metadata.filename,\n lineno: metadata.lineno ?? 0,\n colno: metadata.colno ?? 0,\n in_app: true,\n },\n ];\n }\n return [];\n }\n const lines = stack.split('\\n');\n // The first line is usually the message (e.g. \"TypeError: foo is not a\n // function\") rather than a frame — skip it when it doesn't start with\n // `at` or contain `@`.\n const frameLines = lines[0]?.match(/^\\s*at\\b|@/) ? lines : lines.slice(1);\n return frameLines\n .map((line) => parseFrame(line))\n .filter((frame): frame is Record<string, unknown> => frame != null);\n}\n\nfunction parseFrame(line: string): Record<string, unknown> | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n const v8 = STACK_RE_V8.exec(trimmed);\n if (v8) {\n return {\n function: v8[1] ?? '<anonymous>',\n filename: v8[2],\n abs_path: v8[2],\n lineno: Number(v8[3]),\n colno: Number(v8[4]),\n in_app: true,\n };\n }\n const sm = STACK_RE_SPIDERMONKEY.exec(trimmed);\n if (sm) {\n return {\n function: sm[1] || '<anonymous>',\n filename: sm[2],\n abs_path: sm[2],\n lineno: Number(sm[3]),\n colno: Number(sm[4]),\n in_app: true,\n };\n }\n return { raw: trimmed, in_app: true };\n}\n\nfunction scrubFirstFrameSource(list: Array<Record<string, unknown>>): string | undefined {\n const first = list[0];\n if (!first) return undefined;\n const stacktrace = first.stacktrace as\n | { frames?: Array<{ abs_path?: unknown }> }\n | undefined;\n const frame = stacktrace?.frames?.[0];\n if (frame == null || typeof frame.abs_path !== 'string') return undefined;\n // Already scrubbed by scrubExceptionList; just narrow the type.\n return frame.abs_path;\n}\n\nfunction scrubUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}`;\n } catch {\n return url;\n }\n}\n\nfunction defaultMessage(error: unknown): string {\n if (typeof error === 'string') return error;\n if (error instanceof Error) return error.message;\n return 'Unknown error';\n}\n\nfunction randomId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback for older browsers / SSR — collision risk is negligible\n // because $insert_id only needs to dedupe within a single user-session\n // window on the PostHog ingest side.\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n// Re-exported helpers for the file-path scrub so callers that hand-build\n// frames (e.g. legacy code paths) can apply the same redaction without\n// reaching into scrub.ts directly.\nexport { scrubFilePath };\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\nimport { useI18n } from '../i18n';\nimport {\n ANALYTICS_HEADER_DEVICE_ID,\n ANALYTICS_HEADER_CLIENT_TYPE,\n ANALYTICS_HEADER_LOCALE,\n ANALYTICS_HEADER_REQUEST_ID,\n ANALYTICS_HEADER_SESSION_ID,\n} from '@open-design/contracts/analytics';\nimport {\n applyConsent,\n applyIdentity,\n bootstrapExceptionTracking,\n capture,\n getAnalyticsClient,\n getResolvedAnonymousId,\n setConfigureGlobals,\n} from './client';\nimport type { AnalyticsConfigureGlobals } from '@open-design/contracts/analytics';\nimport {\n detectClientType,\n getAnonymousId,\n getSessionId,\n} from './identity';\nimport { randomUUID } from '../utils/uuid';\n\ninterface AnalyticsContextValue {\n // The track helper accepts any event/props pair; per-event safety is\n // enforced by the typed wrappers in events.ts that consumers use.\n track: (\n event: string,\n properties: Record<string, unknown>,\n options?: { requestId?: string; insertId?: string },\n ) => void;\n // Toggle PostHog capture without unmounting the provider. App.tsx calls\n // this from a useEffect that watches `config.telemetry?.metrics` so a\n // Privacy toggle takes effect immediately, not on next reload.\n setConsent: (granted: boolean) => void;\n // Switch PostHog's distinct_id to the new installationId after a\n // Delete-my-data rotation. App.tsx watches `config.installationId` and\n // calls this whenever the daemon rotates it; PostHog's localStorage\n // state is reset() then identify()'d to the new id so the next event\n // batch is fully decoupled from the deleted identity.\n setIdentity: (installationId: string | null) => void;\n // Push the configure-state triplet (has_available_configure_cli /\n // configure_type / configure_availability) to the PostHog global\n // register so every subsequent capture inherits it. Called from\n // App.tsx whenever the user's execution-mode config changes (mode\n // switch, agent select, BYOK save, CLI rescan).\n setConfigureGlobals: (next: AnalyticsConfigureGlobals) => void;\n anonymousId: string;\n sessionId: string;\n newRequestId: () => string;\n}\n\nconst Ctx = createContext<AnalyticsContextValue | null>(null);\n\n// PR #1428 reviewer (Siri-Ray): the previous `url.includes('/api/')` check\n// matched absolute third-party URLs (https://provider.example/api/x), which\n// would leak our analytics headers outside the daemon boundary. This helper\n// is strictly same-origin + /api/ prefix and is shared by both the global\n// fetch wrapper and the per-track request_id wrapper.\nfunction isSameOriginApiCall(url: unknown): boolean {\n if (typeof url !== 'string') return false;\n if (url.startsWith('/api/')) return true;\n if (typeof window === 'undefined') return false;\n try {\n const parsed = new URL(url, window.location.origin);\n return (\n parsed.origin === window.location.origin &&\n parsed.pathname.startsWith('/api/')\n );\n } catch {\n return false;\n }\n}\n\n// App version is read from a runtime endpoint rather than at build time so\n// the same web bundle reports the daemon-pinned version even when running\n// against a newer/older daemon during dev. Falls back to '0.0.0' until the\n// fetch resolves, then the resolved value flows through state so every\n// downstream effect that depends on `appVersion` re-runs and re-registers\n// the PostHog super-property with the real version. Earlier `useRef` shape\n// silently broke this: ref writes don't trigger re-renders, so every event\n// shipped with `app_version='0.0.0'`.\nexport function useAppVersion(): string {\n const [version, setVersion] = useState('0.0.0');\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n try {\n const res = await fetch('/api/version');\n if (!res.ok) return;\n const body = (await res.json()) as { version?: { version?: string } };\n if (cancelled) return;\n const next = body?.version?.version;\n if (next) setVersion(next);\n } catch {\n // Best-effort.\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n return version;\n}\n\nexport function AnalyticsProvider({ children }: { children: ReactNode }) {\n const { locale } = useI18n();\n const appVersion = useAppVersion();\n // Identity is computed once on mount; locale flows in as a register update\n // when the user switches locales so subsequent events carry the fresh\n // value without re-initializing the PostHog client.\n const identity = useMemo(\n () => ({\n anonymousId: getAnonymousId(),\n sessionId: getSessionId(),\n clientType: detectClientType(),\n }),\n [],\n );\n\n // Once the PostHog client has talked to /api/analytics/config, the\n // installationId the daemon stamped becomes the canonical anonymous id —\n // shared with Langfuse. The fetch wrapper below picks this up so daemon\n // server-side captures end up on the same person record.\n const [resolvedAnonId, setResolvedAnonId] = useState<string | null>(null);\n useEffect(() => {\n let cancelled = false;\n // Bridge the always-on error tracker to /api/analytics/config so any\n // exceptions buffered since module load (see client-app.tsx) can flush\n // to PostHog. This runs regardless of the user's analytics consent\n // toggle — error reports are intentionally not gated by it.\n void bootstrapExceptionTracking({\n anonymousId: identity.anonymousId,\n sessionId: identity.sessionId,\n clientType: identity.clientType,\n locale,\n appVersion,\n });\n void getAnalyticsClient({\n anonymousId: identity.anonymousId,\n sessionId: identity.sessionId,\n clientType: identity.clientType,\n locale,\n appVersion,\n }).then(() => {\n if (cancelled) return;\n const resolved = getResolvedAnonymousId();\n if (resolved) setResolvedAnonId(resolved);\n });\n return () => {\n cancelled = true;\n };\n }, [identity, locale, appVersion]);\n\n // Wrap window.fetch so every same-origin /api/* request carries the\n // analytics context for the daemon to mirror result events back with the\n // matching distinct id.\n //\n // Gated on `resolvedAnonId`: PR #1428 reviewer (codex-connector,\n // lefarcen) — when Privacy → metrics is off, /api/analytics/config\n // returns enabled=false → resolvedAnonId stays null → header injection\n // never installs. That way an opted-out user can't produce daemon-side\n // PostHog events even though POSTHOG_KEY exists in the daemon env\n // (daemon's readAnalyticsContext treats the header as consent).\n useEffect(() => {\n if (typeof window === 'undefined') return;\n if (!resolvedAnonId) return;\n const original = window.fetch;\n const baseHeaders: Record<string, string> = {\n [ANALYTICS_HEADER_DEVICE_ID]: resolvedAnonId,\n [ANALYTICS_HEADER_SESSION_ID]: identity.sessionId,\n [ANALYTICS_HEADER_CLIENT_TYPE]: identity.clientType,\n };\n window.fetch = async (input, init) => {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;\n if (!isSameOriginApiCall(url)) return original(input, init);\n const merged: HeadersInit = {\n ...baseHeaders,\n [ANALYTICS_HEADER_LOCALE]: locale,\n ...(init?.headers ?? {}),\n };\n return original(input, { ...(init ?? {}), headers: merged });\n };\n return () => {\n window.fetch = original;\n };\n }, [identity, locale, resolvedAnonId]);\n\n // Update PostHog's super-properties whenever locale changes so subsequent\n // captures carry the right `locale` field without us threading it through\n // every track call site.\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n const client = await getAnalyticsClient({\n anonymousId: identity.anonymousId,\n sessionId: identity.sessionId,\n clientType: identity.clientType,\n locale: locale,\n appVersion,\n });\n if (cancelled || !client) return;\n try {\n client.register({ locale: locale, app_version: appVersion, ui_version: appVersion });\n } catch {\n // Best-effort.\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [identity, locale, appVersion]);\n\n const track = useCallback<AnalyticsContextValue['track']>(\n (event, properties, options) => {\n const insertId = options?.insertId ?? randomUUID();\n const requestId = options?.requestId ?? null;\n // Attach request_id to the in-flight fetch wrapper too, so the daemon\n // can stitch click→result pairs without the caller threading it.\n if (typeof window !== 'undefined' && requestId) {\n try {\n const baseFetch = window.fetch;\n const wrapped: typeof fetch = async (input, init) => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n if (!isSameOriginApiCall(url)) return baseFetch(input, init);\n const merged: HeadersInit = {\n [ANALYTICS_HEADER_REQUEST_ID]: requestId,\n ...(init?.headers ?? {}),\n };\n return baseFetch(input, { ...(init ?? {}), headers: merged });\n };\n // Single-shot: restore after next microtask so only the originating\n // fetch picks up the request_id header.\n window.fetch = wrapped;\n queueMicrotask(() => {\n window.fetch = baseFetch;\n });\n } catch {\n // Best-effort header injection.\n }\n }\n void (async () => {\n const client = await getAnalyticsClient({\n anonymousId: identity.anonymousId,\n sessionId: identity.sessionId,\n clientType: identity.clientType,\n locale: locale,\n appVersion,\n });\n capture(client, { event, properties, insertId, requestId });\n })();\n },\n [identity, locale, appVersion],\n );\n\n const value = useMemo<AnalyticsContextValue>(\n () => ({\n track,\n setConsent: (granted: boolean) => {\n applyConsent(granted);\n if (!granted) {\n // Clear the header-injection state so the fetch wrapper effect\n // tears down its hook on the next render. Daemon-side captures\n // will see no x-od-analytics-* headers → readAnalyticsContext\n // returns null → no events emitted, even if POSTHOG_KEY is set.\n setResolvedAnonId(null);\n } else {\n // Re-trigger client init: getAnalyticsClient's null-cache fix\n // (client.ts) allows a fresh /api/analytics/config fetch when\n // the previous response was enabled=false. Resolved id propagates\n // into the wrapper via setResolvedAnonId below.\n void getAnalyticsClient({\n anonymousId: identity.anonymousId,\n sessionId: identity.sessionId,\n clientType: identity.clientType,\n locale,\n appVersion,\n }).then(() => {\n const resolved = getResolvedAnonymousId();\n if (resolved) setResolvedAnonId(resolved);\n });\n }\n },\n setIdentity: (installationId: string | null) => {\n applyIdentity(installationId);\n // Keep the fetch wrapper's header in sync so daemon-side captures\n // start using the new id immediately, not after the next reload.\n if (installationId) setResolvedAnonId(installationId);\n },\n setConfigureGlobals: (next: AnalyticsConfigureGlobals) => {\n setConfigureGlobals(next);\n },\n anonymousId: identity.anonymousId,\n sessionId: identity.sessionId,\n newRequestId: () => randomUUID(),\n }),\n [track, identity, locale, appVersion],\n );\n\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nexport function useAnalytics(): AnalyticsContextValue {\n const value = useContext(Ctx);\n if (!value) {\n // No-op stub for unit tests / SSR / consumers rendered outside the\n // provider tree. Returning a working stub keeps every call site free of\n // null checks.\n return {\n track: () => undefined,\n setConsent: () => undefined,\n setIdentity: () => undefined,\n setConfigureGlobals: () => undefined,\n anonymousId: 'unmounted',\n sessionId: 'unmounted',\n newRequestId: () => randomUUID(),\n };\n }\n return value;\n}\n","// src/analytics/events.ts\nfunction projectKindToTracking(kind) {\n switch (kind) {\n case \"prototype\":\n return \"prototype\";\n case \"deck\":\n return \"slide_deck\";\n case \"template\":\n return \"template\";\n case \"other\":\n return \"other\";\n case \"image\":\n return \"image\";\n case \"video\":\n return \"video\";\n case \"audio\":\n return \"audio\";\n case \"live-artifact\":\n case \"live_artifact\":\n return \"live_artifact\";\n default:\n return null;\n }\n}\nfunction createTabToTracking(tab) {\n switch (tab) {\n case \"prototype\":\n return \"prototype\";\n case \"deck\":\n return \"slide_deck\";\n case \"template\":\n return \"from_template\";\n case \"live-artifact\":\n return \"live_artifact\";\n case \"image\":\n case \"video\":\n case \"audio\":\n return \"media\";\n case \"other\":\n return \"other\";\n default:\n return \"prototype\";\n }\n}\nfunction fidelityToTracking(fidelity) {\n if (fidelity === \"wireframe\") return \"wireframe\";\n if (fidelity === \"high-fidelity\") return \"high_fidelity\";\n return \"not_applicable\";\n}\nfunction executionModeToTracking(mode) {\n return mode === \"daemon\" ? \"local_cli\" : \"byok\";\n}\nfunction modelIdForTracking(model) {\n const trimmed = typeof model === \"string\" ? model.trim() : \"\";\n return trimmed.length > 0 ? trimmed : \"default\";\n}\nfunction agentIdToTracking(agentId) {\n switch (agentId) {\n case \"claude\":\n return \"claude_code\";\n case \"codex\":\n return \"codex_cli\";\n case \"devin\":\n return \"devin_for_terminal\";\n case \"gemini\":\n return \"gemini_cli\";\n case \"opencode\":\n return \"opencode\";\n case \"hermes\":\n return \"hermes\";\n case \"kimi\":\n return \"kimi_cli\";\n case \"cursor-agent\":\n return \"cursor_agent\";\n case \"qwen\":\n return \"qwen_code\";\n case \"qoder\":\n return \"qoder_cli\";\n case \"copilot\":\n return \"github_copilot_cli\";\n case \"pi\":\n return \"pi\";\n case \"kilo\":\n return \"kilo\";\n default:\n return \"other\";\n }\n}\nfunction feedbackAgentProviderIdToTracking(agentId) {\n switch (agentId) {\n case \"anthropic-api\":\n return byokProtocolToTracking(\"anthropic\") ?? \"other\";\n case \"openai-api\":\n return byokProtocolToTracking(\"openai\") ?? \"other\";\n case \"azure-openai-api\":\n return byokProtocolToTracking(\"azure\") ?? \"other\";\n case \"google-gemini-api\":\n return byokProtocolToTracking(\"google\") ?? \"other\";\n case \"ollama-cloud-api\":\n return byokProtocolToTracking(\"ollama\") ?? \"other\";\n case \"senseaudio-api\":\n return byokProtocolToTracking(\"senseaudio\") ?? \"other\";\n default:\n return agentIdToTracking(agentId);\n }\n}\nfunction byokProtocolToTracking(protocol) {\n switch (protocol) {\n case \"anthropic\":\n return \"anthropic\";\n case \"openai\":\n return \"openai\";\n case \"azure\":\n case \"azure_openai\":\n return \"azure_openai\";\n case \"google\":\n case \"google_gemini\":\n return \"google_gemini\";\n case \"ollama\":\n case \"ollama_cloud\":\n return \"ollama_cloud\";\n case \"senseaudio\":\n return \"senseaudio\";\n default:\n return null;\n }\n}\nfunction settingsSectionToTracking(section) {\n switch (section) {\n case \"execution\":\n return \"configure_execution_mode\";\n case \"instructions\":\n return \"instructions\";\n case \"media\":\n return \"media_providers\";\n case \"language\":\n return \"language\";\n case \"appearance\":\n return \"appearance\";\n case \"pet\":\n return \"pets\";\n case \"about\":\n return \"about\";\n case \"composio\":\n case \"integrations\":\n case \"connectors\":\n return \"connectors\";\n case \"mcpClient\":\n case \"mcp_server\":\n return \"mcp_server\";\n case \"orbit\":\n return \"orbit\";\n case \"skills\":\n return \"skills\";\n case \"designSystems\":\n return \"design_systems\";\n case \"projectLocations\":\n return \"project_locations\";\n case \"memory\":\n return \"memory\";\n case \"privacy\":\n return \"privacy\";\n case \"notifications\":\n return \"notifications\";\n case \"externalMcp\":\n return \"external_mcp\";\n default:\n return \"configure_execution_mode\";\n }\n}\nfunction artifactKindToTracking(args) {\n const { rendererId, fileKind } = args;\n if (rendererId === \"html\" || rendererId === \"deck-html\" || rendererId === \"react-component\") {\n return \"html\";\n }\n if (rendererId === \"markdown\") return \"markdown\";\n if (rendererId === \"svg\") return \"image\";\n if (fileKind === \"image\" || fileKind === \"sketch\") return \"image\";\n if (fileKind === \"video\") return \"video\";\n if (fileKind === \"audio\") return \"audio\";\n if (fileKind === \"pdf\" || fileKind === \"document\" || fileKind === \"presentation\" || fileKind === \"spreadsheet\") {\n return \"doc\";\n }\n return \"unknown\";\n}\nfunction fileSizeBucketToTracking(bytes) {\n const mb = bytes / (1024 * 1024);\n if (mb < 1) return \"0_1mb\";\n if (mb < 10) return \"1_10mb\";\n if (mb < 100) return \"10_100mb\";\n return \"100mb_plus\";\n}\nfunction fileTypeToTracking(args) {\n if (args.isFolder) return \"folder\";\n if (args.isZip) return \"zip\";\n const m = args.mime ?? \"\";\n if (m.startsWith(\"image/\")) return \"image\";\n if (m.startsWith(\"video/\")) return \"video\";\n if (m.startsWith(\"audio/\")) return \"audio\";\n if (m === \"application/pdf\") return \"pdf\";\n return \"other\";\n}\nfunction deriveConfigureGlobals(input) {\n const agents = input.agents ?? [];\n const hasAvailableCli = agents.some((a) => a.available === true);\n const selectedAgent = input.agentId ? agents.find((a) => a.id === input.agentId) : void 0;\n const selectedAgentAvailable = selectedAgent?.available === true;\n const byokConfigured = input.byokConfigured === true;\n let configureType;\n if (input.mode === \"daemon\") {\n configureType = byokConfigured ? \"both\" : \"local_cli\";\n } else if (input.mode === \"api\") {\n configureType = hasAvailableCli ? \"both\" : \"byok\";\n } else if (hasAvailableCli && byokConfigured) {\n configureType = \"both\";\n } else if (hasAvailableCli) {\n configureType = \"local_cli\";\n } else if (byokConfigured) {\n configureType = \"byok\";\n } else {\n configureType = \"none\";\n }\n let configureAvailability;\n if (input.mode === \"daemon\") {\n configureAvailability = selectedAgentAvailable ? \"available\" : \"unavailable\";\n } else if (input.mode === \"api\") {\n configureAvailability = byokConfigured ? \"available\" : \"unavailable\";\n } else if (hasAvailableCli || byokConfigured) {\n configureAvailability = \"available\";\n } else {\n configureAvailability = \"unknown\";\n }\n return {\n has_available_configure_cli: hasAvailableCli,\n configure_type: configureType,\n configure_availability: configureAvailability\n };\n}\nfunction normalizeCustomReason(text) {\n return (text ?? \"\").trim();\n}\nfunction designSystemLengthBucket(text) {\n const length = (text ?? \"\").trim().length;\n if (length === 0) return \"0\";\n if (length <= 50) return \"1_50\";\n if (length <= 200) return \"51_200\";\n if (length <= 500) return \"201_500\";\n return \"500_plus\";\n}\nfunction designSystemFolderCountBucket(count) {\n if (count === null || count === void 0 || !Number.isFinite(count)) {\n return \"unknown\";\n }\n if (count <= 0) return \"0\";\n if (count <= 10) return \"1_10\";\n if (count <= 50) return \"11_50\";\n if (count <= 200) return \"51_200\";\n return \"200_plus\";\n}\nfunction designSystemTotalSizeBucket(bytes) {\n if (bytes === null || bytes === void 0 || !Number.isFinite(bytes)) {\n return \"unknown\";\n }\n const mb = bytes / (1024 * 1024);\n if (mb < 1) return \"0_1mb\";\n if (mb < 10) return \"1_10mb\";\n if (mb < 50) return \"10_50mb\";\n return \"50mb_plus\";\n}\nfunction designSystemModuleSlug(header) {\n const trimmed = (header ?? \"\").trim().replace(/^#+\\s*/, \"\");\n if (!trimmed) return \"unknown\";\n return trimmed.toLowerCase().replace(/[^a-z0-9\\s-]+/g, \"\").replace(/\\s+/g, \"-\").replace(/-+/g, \"-\").replace(/^-|-$/g, \"\") || \"unknown\";\n}\nfunction designSystemModuleType(slug) {\n const s = (slug ?? \"\").toLowerCase();\n if (!s) return \"other\";\n if (/(typography|type|font)/.test(s)) return \"typography\";\n if (/(color|palette)/.test(s)) return \"colors\";\n if (/(spacing|layout|grid|radius|shadow|elevation)/.test(s)) {\n return \"spacing\";\n }\n if (/(component|button|input|form|icon|widget)/.test(s)) return \"components\";\n if (/(brand|asset|logo|image|illustration)/.test(s)) return \"brand_assets\";\n return \"other\";\n}\nfunction designSystemRepoHostFromUrl(url) {\n const raw = (url ?? \"\").trim();\n if (!raw) return \"unknown\";\n try {\n const host = new URL(raw).hostname.toLowerCase();\n if (host === \"github.com\" || host.endsWith(\".github.com\")) return \"github\";\n if (host === \"gitlab.com\" || host.endsWith(\".gitlab.com\")) return \"gitlab\";\n return \"other\";\n } catch {\n return \"unknown\";\n }\n}\n\n// src/analytics/public-params.ts\nvar EVENT_SCHEMA_VERSION = 2;\nvar ANALYTICS_HEADER_DEVICE_ID = \"x-od-analytics-device-id\";\nvar ANALYTICS_HEADER_SESSION_ID = \"x-od-analytics-session-id\";\nvar ANALYTICS_HEADER_CLIENT_TYPE = \"x-od-analytics-client-type\";\nvar ANALYTICS_HEADER_LOCALE = \"x-od-analytics-locale\";\nvar ANALYTICS_HEADER_REQUEST_ID = \"x-od-analytics-request-id\";\n\n// src/analytics/artifact-id.ts\nvar FNV_OFFSET_BASIS = 0xcbf29ce484222325n;\nvar FNV_PRIME = 0x100000001b3n;\nvar MASK_64 = 0xffffffffffffffffn;\nfunction anonymizeArtifactId(args) {\n const input = `${args.projectId}:${args.fileName}`;\n let hash = FNV_OFFSET_BASIS;\n for (let i = 0; i < input.length; i++) {\n hash ^= BigInt(input.charCodeAt(i));\n hash = hash * FNV_PRIME & MASK_64;\n }\n return hash.toString(16).padStart(16, \"0\");\n}\nexport {\n ANALYTICS_HEADER_CLIENT_TYPE,\n ANALYTICS_HEADER_DEVICE_ID,\n ANALYTICS_HEADER_LOCALE,\n ANALYTICS_HEADER_REQUEST_ID,\n ANALYTICS_HEADER_SESSION_ID,\n EVENT_SCHEMA_VERSION,\n agentIdToTracking,\n anonymizeArtifactId,\n artifactKindToTracking,\n byokProtocolToTracking,\n createTabToTracking,\n deriveConfigureGlobals,\n designSystemFolderCountBucket,\n designSystemLengthBucket,\n designSystemModuleSlug,\n designSystemModuleType,\n designSystemRepoHostFromUrl,\n designSystemTotalSizeBucket,\n executionModeToTracking,\n feedbackAgentProviderIdToTracking,\n fidelityToTracking,\n fileSizeBucketToTracking,\n fileTypeToTracking,\n modelIdForTracking,\n normalizeCustomReason,\n projectKindToTracking,\n settingsSectionToTracking\n};\n","// PostHog browser client wrapper. Lazy-loads posthog-js only after the\n// daemon /api/analytics/config response confirms a key is present, so dev\n// builds and forks impose zero runtime cost. All entry points are\n// fire-and-forget: capture failures must never propagate to product code.\n\nimport type { PostHog } from 'posthog-js';\nimport {\n EVENT_SCHEMA_VERSION,\n type AnalyticsClientType,\n type AnalyticsConfigResponse,\n type AnalyticsConfigureGlobals,\n} from '@open-design/contracts/analytics';\nimport { scrubBeforeSend } from './scrub';\nimport {\n clearExceptionTrackingContext,\n setExceptionTrackingContext,\n} from './error-tracking';\n\ninterface AnalyticsContext {\n anonymousId: string;\n sessionId: string;\n clientType: AnalyticsClientType;\n locale: string;\n appVersion: string;\n}\n\nlet client: PostHog | null = null;\nlet initPromise: Promise<PostHog | null> | null = null;\nlet resolvedDeviceId: string | null = null;\n// Latest configure-state triplet. Re-registered on the PostHog client as\n// soon as it changes so every subsequent event inherits the current values.\nlet configureGlobals: AnalyticsConfigureGlobals = {\n has_available_configure_cli: false,\n configure_type: 'unknown',\n configure_availability: 'unknown',\n};\n// Snapshot of the super-property payload sent on the most recent `loaded()`\n// init. `reset()` clears posthog-js's persisted super-properties as well as\n// the distinct_id, so privacy → metrics off → on, or a Delete-my-data\n// rotation (applyIdentity()), would otherwise resume capture without\n// `event_schema_version`, `device_id`, `session_id`, `locale`, or the\n// configure-state globals. We restash this on init and re-register it\n// after every reset()/identify() so every subsequent event keeps the\n// v2 schema contract.\nlet lastRegisterPayload: Record<string, unknown> | null = null;\n\n// Returns the installationId the daemon stamped on /api/analytics/config\n// after the user opted in via Privacy → \"Share usage data\". The provider\n// uses this in preference to its locally-generated UUID so PostHog,\n// Langfuse, and any future sink share a single anonymous identity.\n//\n// Kept under the legacy name for callers that still import it; new code\n// should prefer `getResolvedDeviceId`.\nexport function getResolvedAnonymousId(): string | null {\n return resolvedDeviceId;\n}\n\nexport function getResolvedDeviceId(): string | null {\n return resolvedDeviceId;\n}\n\n// Web-side accessor for the daemon header bridge: when the web client POSTs\n// to /api/runs the daemon needs to know what device_id to stamp on its\n// own server-side captures.\nexport function getConfigureGlobals(): AnalyticsConfigureGlobals {\n return configureGlobals;\n}\n\n// Called from the AnalyticsProvider when the configure-state triplet changes\n// (mode switch, BYOK key save, CLI rescan). The values are registered on the\n// PostHog client so every subsequent capture inherits them — no per-event\n// boilerplate needed.\nexport function setConfigureGlobals(next: AnalyticsConfigureGlobals): void {\n configureGlobals = { ...next };\n // Keep the cached register payload aligned so a future reset/identify\n // flow that calls `restoreSuperProperties()` uses the LATEST configure\n // state, not the stale snapshot captured during the initial `loaded()`.\n if (lastRegisterPayload) {\n lastRegisterPayload = {\n ...lastRegisterPayload,\n ...(configureGlobals as unknown as Record<string, unknown>),\n };\n }\n if (!client) return;\n try {\n client.register(configureGlobals as unknown as Record<string, unknown>);\n } catch {\n // best-effort — capture should never throw out of this path.\n }\n}\n\n// Fetches `/api/analytics/config` once and wires up the exception-tracking\n// module's context — independent of consent state. The error tracker\n// installs its `window.error` / `unhandledrejection` listeners at module\n// load (see `error-tracking.ts`), but cannot dispatch buffered events\n// until it has the PostHog `phc_` key + host + distinct_id. This bootstrap\n// step provides those.\n//\n// Runs in parallel with — and unrelated to — `getAnalyticsClient` above.\n// When the user has consented, both paths fetch the same endpoint once\n// each; the duplicate fetch is cheap and avoids cross-coupling the\n// (consent-gated) analytics init with the (always-on) error tracker.\nlet exceptionBootstrapPromise: Promise<void> | null = null;\nexport function bootstrapExceptionTracking(context: AnalyticsContext): Promise<void> {\n if (exceptionBootstrapPromise) return exceptionBootstrapPromise;\n exceptionBootstrapPromise = (async () => {\n try {\n const res = await fetch('/api/analytics/config');\n if (!res.ok) {\n clearExceptionTrackingContext();\n return;\n }\n const cfg = (await res.json()) as AnalyticsConfigResponse;\n if (!cfg.key || !cfg.host) {\n clearExceptionTrackingContext();\n return;\n }\n const distinctId =\n (typeof cfg.installationId === 'string' && cfg.installationId) ||\n context.anonymousId;\n setExceptionTrackingContext({\n apiKey: cfg.key,\n host: cfg.host,\n distinctId,\n appVersion: context.appVersion,\n sessionId: context.sessionId,\n });\n } catch {\n // Network failure / endpoint unavailable — leave the buffer in\n // place so a future retry could still flush, but don't crash boot.\n }\n })();\n return exceptionBootstrapPromise;\n}\n\nexport async function getAnalyticsClient(\n context: AnalyticsContext,\n): Promise<PostHog | null> {\n if (client) return client;\n if (initPromise) return initPromise;\n // PR #1428 reviewer (Siri-Ray): the first /api/analytics/config response\n // is cached forever if it resolves to null. On first launch before the\n // user accepts the privacy banner the daemon returns enabled=false, this\n // promise resolves null, and every later track() call returns the cached\n // null without re-fetching the now-enabled config. Clear initPromise\n // whenever the resolution is null so a subsequent setConsent(true) can\n // trigger a fresh init.\n const pending = (async () => {\n try {\n const res = await fetch('/api/analytics/config');\n if (!res.ok) return null;\n const cfg = (await res.json()) as AnalyticsConfigResponse;\n if (!cfg.enabled || !cfg.key || !cfg.host) return null;\n const distinctId =\n (typeof cfg.installationId === 'string' && cfg.installationId) ||\n context.anonymousId;\n resolvedDeviceId = distinctId;\n const mod = await import('posthog-js');\n const posthog = mod.default;\n const cfgKey = cfg.key;\n const cfgHost = cfg.host;\n posthog.init(cfgKey, {\n api_host: cfg.host,\n // Identify by installationId when present so daemon-side captures\n // (which also key off installationId via the analytics context\n // header) land on the same person record. Falls back to the\n // locally-generated UUID for the legacy / pre-consent path.\n bootstrap: { distinctID: distinctId },\n persistence: 'localStorage',\n // PostHog's default UA filter silently drops captures whose\n // user-agent matches its built-in bot list (HeadlessChrome,\n // various automation flags). The list also rejects some real users\n // — embedded webviews, fingerprinted browsers, e2e CI runs — which\n // is unacceptable for product analytics that needs to count every\n // session. We instead rely on the Privacy → \"Share usage data\"\n // toggle as the single consent gate and treat every UA equally.\n opt_out_useragent_filter: true,\n\n // --- Auto-capture layers --------------------------------------\n // Anonymous diagnostic features (click paths, page transitions,\n // web vitals, browser errors). The single Privacy → \"Share\n // usage data\" toggle gates ALL of these via posthog-js's global\n // opt_out_capturing() — see applyConsent() below and\n // AnalyticsProvider's setConsent wiring in App.tsx.\n autocapture: true,\n capture_pageview: 'history_change',\n capture_pageleave: 'if_capture_pageview',\n capture_dead_clicks: true,\n capture_performance: {\n web_vitals: true,\n network_timing: true,\n },\n // Exception capture is owned by `apps/web/src/analytics/error-tracking.ts`,\n // which runs unconditionally — outside this consent gate, before\n // posthog-js loads, and via a direct ingest fetch. Letting posthog-js\n // also autocapture exceptions would only produce duplicates server-side\n // (the $insert_id dedupe runs but it's still wasted ingest cost).\n capture_exceptions: false,\n\n // --- Privacy defenses -----------------------------------------\n // 1. scrub.ts runs on every outgoing event and strips $el_text\n // from input/textarea/contenteditable elements, removes\n // query strings from URLs, and rewrites absolute filesystem\n // paths in exception stack traces. Single audit point — new\n // sensitive surfaces extend the rules there, not by\n // sprinkling class names through the codebase.\n // 2. The chat composer textarea keeps a `ph-no-capture` class\n // as defense in depth: PostHog won't even generate an event\n // for clicks inside that subtree, so a future scrub regression\n // can't leak prompt content. Only the most sensitive surface\n // (prompt body) gets this treatment; everything else relies\n // on scrub.ts.\n before_send: scrubBeforeSend,\n\n // --- Explicitly disabled --------------------------------------\n // Session replay captures the user's entire screen. For a tool\n // where prompts, generated artifacts, and provider API keys are\n // all visible in DOM, this needs an extensive mask catalogue\n // before we can satisfy the CSV's no-prompt-content rule. Off\n // until a dedicated consent surface ships.\n disable_session_recording: true,\n\n loaded: (instance) => {\n lastRegisterPayload = {\n event_schema_version: EVENT_SCHEMA_VERSION,\n ui_version: context.appVersion,\n app_version: context.appVersion,\n client_type: context.clientType,\n locale: context.locale,\n session_id: context.sessionId,\n // v2 rename: was `anonymous_id`. Value is unchanged — the same\n // installationId / local-UUID fallback.\n device_id: distinctId,\n ...(configureGlobals as unknown as Record<string, unknown>),\n };\n instance.register(lastRegisterPayload);\n // Re-bridge the error-tracking context once posthog-js is fully\n // initialized. `bootstrapExceptionTracking` may have already\n // wired this up at app boot via its own fetch; this duplicate\n // assignment is harmless (same key/host) but ensures the most\n // up-to-date appVersion / sessionId metadata is attached.\n setExceptionTrackingContext({\n apiKey: cfgKey,\n host: cfgHost,\n distinctId,\n appVersion: context.appVersion,\n sessionId: context.sessionId,\n });\n },\n });\n client = posthog;\n return posthog;\n } catch {\n // Network failure, missing endpoint, third-party fork without keys —\n // all collapse to the same no-op.\n return null;\n }\n })();\n initPromise = pending;\n // Clear the cache as soon as the result is null so a later opt-in retries.\n void pending.then((result) => {\n if (!result) initPromise = null;\n });\n return pending;\n}\n\n// Called from the AnalyticsProvider when the user toggles Privacy →\n// metrics off so events stop flowing immediately, before the next\n// reload re-reads /api/analytics/config. The posthog-js client persists\n// its opt-out flag in localStorage; subsequent capture() calls become\n// no-ops until the user opts back in.\n//\n// `opt_out_capturing()` is a global gate — it halts not only explicit\n// capture() calls but also autocapture, $pageview, $pageleave,\n// $exception, web vitals, and dead clicks. One toggle covers every\n// PostHog code path.\n//\n// On opt-out we ALSO call `posthog.reset()` to clear the persisted\n// `ph_*_posthog` localStorage entry. Without this, the SDK keeps the\n// old distinct_id; if the user later clicks Delete my data (which\n// rotates installationId via the daemon) and toggles metrics back on,\n// posthog-js would still think the user is the old id and stitch the\n// new session to the deleted identity. reset() prevents that.\nexport function applyConsent(consentGranted: boolean): void {\n if (!client) return;\n try {\n if (consentGranted) {\n client.opt_in_capturing();\n // If the user previously toggled metrics off in this session, the\n // earlier opt-out path called reset() and wiped the persisted\n // super-properties. opt_in_capturing() only flips the consent flag\n // and does not re-run init(), so without this restore the next\n // capture would emit no event_schema_version / device_id /\n // session_id / locale / configure-state. See PR #2285 review\n // 2026-05-20 04:35.\n restoreSuperProperties();\n } else {\n client.opt_out_capturing();\n client.reset();\n resolvedDeviceId = null;\n }\n } catch {\n // best-effort — capture should never throw out of this path.\n }\n}\n\n// Called from the AnalyticsProvider when `config.installationId` rotates\n// (Delete my data). posthog-js's `bootstrap.distinctID` only takes\n// effect on first init; once the client is alive, identify() is the\n// only way to switch identities. We pair it with reset() first so any\n// $device_id stored under the OLD installation is wiped — the new\n// session is fully decoupled from the deleted one.\nexport function applyIdentity(installationId: string | null): void {\n if (!client || !installationId) return;\n if (resolvedDeviceId === installationId) return;\n try {\n client.reset();\n client.identify(installationId);\n resolvedDeviceId = installationId;\n // reset() also clears the persisted super-properties from\n // posthog-js's localStorage cache. Re-register them with the new\n // distinct_id so the rest of this session keeps emitting v2-schema\n // events. See PR #2285 review 2026-05-20 04:35.\n restoreSuperProperties({ device_id: installationId });\n } catch {\n // best-effort — never propagate.\n }\n}\n\n// Push the cached super-property payload back onto the PostHog client. Used\n// after reset()/identify() flows; takes an optional override patch so the\n// caller can swap fields (e.g. a rotated device_id) without re-deriving the\n// rest of the payload.\nfunction restoreSuperProperties(patch?: Record<string, unknown>): void {\n if (!client || !lastRegisterPayload) return;\n const next = patch ? { ...lastRegisterPayload, ...patch } : lastRegisterPayload;\n lastRegisterPayload = next;\n try {\n client.register(next);\n } catch {\n // best-effort.\n }\n}\n\nexport function capture(\n client: PostHog | null,\n args: {\n event: string;\n properties: Record<string, unknown>;\n insertId: string;\n requestId?: string | null;\n },\n): void {\n if (!client) return;\n try {\n client.capture(args.event, {\n ...args.properties,\n event_id: args.insertId,\n // PostHog's official dedup key. The daemon mirrors result events with\n // the same $insert_id so duplicates from the dual-side capture pattern\n // get coalesced server-side.\n $insert_id: args.insertId,\n ...(args.requestId ? { request_id: args.requestId } : {}),\n });\n } catch {\n // Swallow — analytics failures must not propagate.\n }\n}\n","// Browser-side identity bookkeeping for PostHog product analytics. Designed\n// so it stays SSR-safe: every entry point guards window/localStorage access\n// and falls back to a deterministic-enough fake id under jsdom and Next.js\n// pre-render. The daemon mirrors these values via the x-od-analytics-*\n// headers (see @open-design/contracts/analytics).\n\nimport type { AnalyticsClientType } from '@open-design/contracts/analytics';\nimport { detectOpenDesignHostClientType } from '@open-design/host';\n\nconst ANONYMOUS_ID_KEY = 'open-design:analytics.anonymous_id';\nconst SESSION_ID_KEY = 'open-design:analytics.session_id';\n\nfunction randomUuid(): string {\n // Prefer the standard crypto.randomUUID — present in every modern browser\n // and Node 19+. The Math.random fallback is for jsdom builds that ship\n // without crypto.randomUUID and for very old browsers; it does not need\n // to be cryptographically strong, only unique-enough for a session id.\n const c: Crypto | undefined =\n typeof globalThis !== 'undefined' ? globalThis.crypto : undefined;\n if (c?.randomUUID) return c.randomUUID();\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, (ch) => {\n const r = (Math.random() * 16) | 0;\n const v = ch === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport function getAnonymousId(): string {\n if (typeof window === 'undefined') return 'ssr';\n try {\n const existing = window.localStorage.getItem(ANONYMOUS_ID_KEY);\n if (existing) return existing;\n const fresh = randomUuid();\n window.localStorage.setItem(ANONYMOUS_ID_KEY, fresh);\n return fresh;\n } catch {\n // Privacy mode or quota — fall back to a per-load id; we'd rather lose\n // cross-session continuity than throw out of an analytics path.\n return randomUuid();\n }\n}\n\nexport function getSessionId(): string {\n if (typeof window === 'undefined') return 'ssr';\n try {\n const existing = window.sessionStorage.getItem(SESSION_ID_KEY);\n if (existing) return existing;\n const fresh = randomUuid();\n window.sessionStorage.setItem(SESSION_ID_KEY, fresh);\n return fresh;\n } catch {\n return randomUuid();\n }\n}\n\n// Desktop packaged builds install the Open Design host bridge so the\n// same web bundle can distinguish desktop runs from browser visits.\n// Falls back to 'web' when the host bridge isn't present.\nexport function detectClientType(): AnalyticsClientType {\n if (typeof window === 'undefined') return 'web';\n return detectOpenDesignHostClientType();\n}\n\n// Read the launch_source for app_launch. Best-effort: PerformanceNavigation\n// type 'reload' / 'back_forward' are mapped to 'reload'; deep links (paths\n// other than '/') are 'deeplink'; otherwise 'direct'. SSR returns 'unknown'.\nexport function detectLaunchSource():\n | 'direct'\n | 'deeplink'\n | 'reload'\n | 'unknown' {\n if (typeof window === 'undefined') return 'unknown';\n try {\n const entries = performance.getEntriesByType?.(\n 'navigation',\n ) as PerformanceNavigationTiming[] | undefined;\n const nav = entries?.[0];\n if (nav?.type === 'reload' || nav?.type === 'back_forward') return 'reload';\n if (window.location.pathname && window.location.pathname !== '/') {\n return 'deeplink';\n }\n return 'direct';\n } catch {\n return 'unknown';\n }\n}\n"],"names":["TEXT_BEARING_TAGS","Set","scrubElementsChain","elements","map","el","tag","tag_name","toLowerCase","contentEditable","attr__contenteditable","isPasswordInput","attr__type","shouldScrub","has","cleaned","$el_text","attr__value","attr__placeholder","attr__aria_label","text","scrubUrl","url","parsed","URL","origin","pathname","scrubFilePath","value","replace","scrubExceptionList","list","entry","next","stack","stacktrace","frames","Array","isArray","frame","filename","abs_path","mechanism","mech","source","SUPPRESSED_EVENTS","scrubBeforeSend","cr","event","props","properties","elementBearing","$elements","$current_url","$pathname","$referrer","$exception_list","$exception_source","projectKindToTracking","kind","createTabToTracking","tab","fidelityToTracking","fidelity","executionModeToTracking","mode","modelIdForTracking","model","trimmed","trim","length","agentIdToTracking","agentId","feedbackAgentProviderIdToTracking","byokProtocolToTracking","protocol","settingsSectionToTracking","section","artifactKindToTracking","args","rendererId","fileKind","fileSizeBucketToTracking","bytes","mb","fileTypeToTracking","isFolder","isZip","m","mime","startsWith","deriveConfigureGlobals","input","agents","hasAvailableCli","some","a","available","selectedAgent","find","id","selectedAgentAvailable","byokConfigured","configureType","configureAvailability","has_available_configure_cli","configure_type","configure_availability","normalizeCustomReason","designSystemLengthBucket","designSystemFolderCountBucket","count","Number","isFinite","designSystemTotalSizeBucket","designSystemModuleSlug","header","designSystemModuleType","slug","s","test","designSystemRepoHostFromUrl","raw","host","hostname","endsWith","EVENT_SCHEMA_VERSION","ANALYTICS_HEADER_DEVICE_ID","ANALYTICS_HEADER_SESSION_ID","ANALYTICS_HEADER_CLIENT_TYPE","ANALYTICS_HEADER_LOCALE","ANALYTICS_HEADER_REQUEST_ID","FNV_OFFSET_BASIS","FNV_PRIME","MASK_64","anonymizeArtifactId","projectId","fileName","hash","i","BigInt","charCodeAt","toString","padStart"],"mappings":"6IAgBA,IAAMA,EAAoB,IAAIC,IAAI,CAAC,QAAS,WAAW,EA+BvD,SAASoB,EAASC,CAAY,EAC5B,GAAmB,UAAf,OAAOA,EAAkB,OAAOA,EACpC,GAAI,CACF,IAAMC,EAAS,IAAIC,IAAIF,GACvB,MAAO,CAAA,EAAGC,EAAOE,MAAM,CAAA,EAAGF,EAAOG,QAAQ,CAAA,CAAE,AAC7C,CAAE,KAAM,CACN,OAAOJ,CACT,CACF,CASO,SAASK,EAAcC,CAAc,QACtC,AAAiB,AAArB,UAA+B,OAApBA,EAA2BA,EAU/BA,EAAMC,OAAO,CAClB,+DACA,WAEJ,CAIO,SAASC,EACdC,CAAoC,EAEpC,OAAOA,EAAK3B,GAAG,CAAE4B,AAAD,IACd,IAAMC,EAAO,CAAE,GAAGD,CAAK,AAAC,EAClBE,EAAQD,EAAKE,UAAU,CAa7B,GAVID,GAAOE,QAAUC,MAAMC,OAAO,CAACJ,EAAME,MAAM,GAAG,CAChDH,EAAKE,UAAU,CAAG,CAChB,GAAGD,CAAK,CACRE,OAAQF,EAAME,MAAM,CAAChC,GAAG,CAAC,AAACmC,IAAW,CACnC,GADkC,AAC/BA,CAAK,CACRC,SAAUb,EAAcY,EAAMC,QAAQ,EACtCC,SAAUd,EAAcY,EAAME,QAAQ,EACxC,CAAC,EACH,EAE4B,UAA1B,OAAOR,EAAKS,SAAS,EAAoC,OAAnBT,EAAKS,SAAS,CAAW,CAEjE,IAAMC,EAAOV,EAAKS,SAAS,CACA,UAAvB,AAAiC,OAA1BC,EAAKC,MAAM,GACpBD,EAAKC,MAAM,CAAGjB,EAAcgB,EAAKC,OAAM,CAE3C,CACA,OAAOX,CACT,EACF,CAKA,IAAMY,EAAoB,IAAI5C,IAAY,CAIxC,UACD,2BAEM,SAAS6C,AAAgBC,CAAwB,EACtD,GAAI,CAACA,GACDF,EAAkB/B,GAAG,CAACiC,EAAGC,KAAK,EADzB,CAC4B,MADrB,CAC4B,IAE5C,IAAMC,EAASF,EAAGG,UAAU,EAAI,CAAC,EASjC,GAJe,CAIXC,gBAJFJ,EAAGC,KAAK,EACRD,AAAa,iBAAVC,KAAK,EACRD,AAAa,kBAAVC,KAAK,EACK,sBAAbD,EAAGC,KAAK,CACU,CAClB,IAAM7C,EAAW8C,EAAMG,SAAS,CAC5Bf,MAAMC,OAAO,CAACnC,KAChB8C,EAAMG,IADqB,KACZ,CArHZjD,AAqHkCA,EArHzBC,AAqHMF,GArHH,CAAC,AAACG,IACnB,IAAMC,EAA6B,UAAvB,OAAOD,EAAGE,QAAQ,CAAgBF,EAAGE,QAAQ,CAACC,WAAW,GAAK,GACpEC,EACgC,UAApC,OAAOJ,EAAGK,qBAAqB,EAC/BL,AAA6B,YAA1BK,qBAAqB,CACpBC,EACI,UAARL,GACyB,UAAzB,OAAOD,EAAGO,UAAU,EACY,aAAhCP,EAAGO,UAAU,CAACJ,WAAW,GAG3B,GAAI,CADFR,AACGa,GADeC,GAAG,CAACR,IAAQG,GAAmBE,CAAAA,EACjC,OAAON,EACzB,IAAMU,EAAmC,CAAE,GAAGV,CAAG,AAAD,EAMhD,OALA,OAAOU,EAAQC,QAAQ,CACvB,OAAOD,EAAQE,WAAW,CAC1B,OAAOF,EAAQG,iBAAiB,CAChC,OAAOH,EAAQI,gBAAgB,CAC/B,OAAOJ,EAAQK,IAAI,CACZL,CACT,EAkGyCZ,CAEzC,CAeA,GAZkC,AAA9B,UAAwC,OAAjC8C,EAAMI,YAAY,GAC3BJ,EAAMI,YAAY,CAAGhC,EAAS4B,EAAMI,aAAY,EAEvCJ,EAAMK,SAAS,CAIK,IAJA,MAI3B,AAAqC,IAJA,GAI9BL,EAAMM,SAAS,GACxBN,EAAMM,SAAS,CAAGlC,EAAS4B,EAAMM,UAAS,EAI3B,eAAbR,EAAGC,KAAK,CAAmB,CAC7B,IAAMjB,EAAOkB,EAAMO,eAAe,CAC9BnB,MAAMC,OAAO,CAACP,KAChBkB,EADuB,AACjBO,eAAe,CAAG1B,EAAmBC,EAAAA,EAEN,UAAU,AAA7C,OAAOkB,EAAMQ,iBAAiB,GAChCR,EAAMQ,iBAAiB,CAAG9B,EAAcsB,EAAMQ,kBAAiB,CAEnE,CAEA,MAAO,CAAE,GAAGV,CAAE,CAAEG,WAAYD,CAAM,CACpC,uDC7GA,IAAI,EAA2C,KACzC,EAAgC,EAAE,CACpC,GAAY,EAuDhB,SAAS,EACP,CAAc,CACd,CAAuB,CACvB,EAA4B,CAAC,CAAC,MAuF9B,KAAc,KAIR,EAxFA,EAAW,KADe,EAsFhC,EAtFuC,EAuFvC,EAvFwD,EAkGjD,CACL,CACE,EAbqB,AAtFW,GAqFb,AAIV,GADG,aAAiB,OACV,EAAM,IAAI,CAAG,AAAiB,iBAAV,EAAqB,QAAU,WAWtE,MAVU,EACV,EAAM,OAAO,CACI,UAAjB,OAAO,EACL,EACA,EAOF,WAAY,CAAE,KAAM,MAAO,OAgBjC,AArBiB,SAqBR,AAAW,CAAa,CAAE,AArBP,CAqBgC,EAC1D,GAAI,CAAC,OAAO,EACV,AAAI,EAAS,QAAQ,CACZ,CADc,AAEnB,CACE,SAAU,cACV,SAAU,EAAS,QAAQ,CAC3B,SAAU,EAAS,QAAQ,CAC3B,OAAQ,EAAS,MAAM,EAAI,EAC3B,MAAO,EAAS,KAAK,EAAI,EACzB,QAAQ,CACV,EACD,CAEI,EAAE,CAEX,IAAM,EAAQ,EAAM,KAAK,CAAC,MAK1B,MAAO,CADY,CAAK,CAAC,EAAE,EAAE,MAAM,cAAgB,EAAQ,EAAM,KAAK,CAAC,EAAA,EAEpE,GAAG,CAAC,AAAC,GAAS,CAInB,SAAS,AAAW,CAAY,EAC9B,IAAM,EAAU,EAAK,IAAI,GACzB,GAAI,CAAC,EAAS,OAAO,KACrB,IAAM,EAAK,EAAY,IAAI,CAAC,GAC5B,GAAI,EACF,EADM,IACC,CACL,SAAU,CAAE,CAAC,EAAE,EAAI,cACnB,SAAU,CAAE,CAAC,EAAE,CACf,SAAU,CAAE,CAAC,EAAE,CACf,OAAQ,OAAO,CAAE,CAAC,EAAE,EACpB,MAAO,OAAO,CAAE,CAAC,EAAE,EACnB,QAAQ,CACV,EAEF,IAAM,EAAK,EAAsB,IAAI,CAAC,UACtC,AAAI,EACK,CACL,CAFI,QAEM,CAAE,CAAC,EAAE,EAAI,cACnB,SAAU,CAAE,CAAC,EAAE,CACf,SAAU,CAAE,CAAC,EAAE,CACf,OAAQ,OAAO,CAAE,CAAC,EAAE,EACpB,MAAO,OAAO,CAAE,CAAC,EAAE,EACnB,QAAQ,CACV,EAEK,CAAE,IAAK,EAAS,QAAQ,CAAK,EACtC,EA9B8B,IACzB,MAAM,CAAC,AAAC,GAAqD,MAAT,EACzD,EA9CgB,GAAkC,UAAvB,OAAO,EAAM,KAAK,CAAgB,EAAM,KAAK,CAAG,GACxC,EAKK,EAClC,UAAW,CACT,MAA2B,IAArB,EAAS,OAAO,CAAY,UAAY,UAC9C,SAA8B,IAArB,EAAS,OAAO,AAC3B,CACF,EACD,GA/FD,EAAQ,aAXoC,CAWtB,AAVpB,gBAAiB,EACjB,gBAAiB,CAAQ,CAAC,EAAEJ,EAAE,KAC9B,mBAAoB,CAAQ,CAAC,EAAE,EAAE,MACjC,kBAAmB,AAsKvB,SAAS,AAAsB,CAAoC,EACjE,IAAM,EAAQ,CAAI,CAAC,EAAE,CACrB,GAAI,CAAC,EAAO,OAAO,AACnB,IAAM,EAAa,EAAM,UAAU,CAG7B,EAAQ,GAAY,QAAQ,CAAC,EAAE,CACrC,GAAa,MAAT,GAA2C,UAAU,AAApC,OAAO,AAAoC,EAA9B,QAAQ,CAE1C,OAAO,EAAM,QACf,AADuB,EA/KsB,GACzC,aAAc,EAAyC,OAAO,CAAvC,OAA+C,CAAC,IAAI,EAC3E,CAD8E,UAClE,IACZ,eAAgB,qBAChB,QAAS,AAAqB,OAAZ,OAAO,AAC3B,EAGF,CAsBA,SAAS,EAAQ,CAAiB,CAAE,CAAmC,EAErE,IAAM,EAA4B,WAChC,EACA,KAAM,CAAE,YAAW,EACnB,UAJgB,IAAIxB,OAAO,WAAW,EAKxC,EACA,GAAe,MAAX,EAAiB,CACf,EAAO,MAAM,EA5GG,EA4GC,EAAiB,EAAO4B,KAAK,GAClD,EAAO,IAAInB,CAAC,GACZ,MACF,CACA,EAAS,EACX,CAEA,SAAS,EAAS,CAAyB,EACzC,GAAe,AAAX,QAAiB,OACrB,IAAM,EAAU,CACdH,QAAS,EAAQ,MAAM,CACvB,MAAO,EAAK,SAAS,CACrB,YAAauB,EAAQ,UAAU,CAC/B,WAAY,CACV,GAAG,EAAK,IAAI,CAAC,UAAU,CACvB,KAAM,qBACN,GAAI,EAAQ,UAAU,CAAG,CAAE,YAAa,EAAQ,UAAU,CAAE,WAAY,EAAQ,UAAU,AAAC,EAAI,CAAC,CAAC,CACjG,GAAI,EAAQ,SAAS,CAAG,CAAE,WAAY,EAAQ,SAAS,AAAC,EAAI,CAAC,CAAC,AAChE,EACA,UAAW,EAAK,SAAS,AAC3B,EAIA,GAAI,CACG,MAAM,CAAA,EAAG,EAAQ,IAAI,CAAC,OAAO,CAAC,OAAQ,IAAI,QAAQ,CAAC,CAAE,CACxD,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,SAAS,CAAC,GACrB,WAAW,EAIX,YAAa,MACf,EACF,CAAE,KAAM,CAER,CACF,CAiCA,IAAM,EAAc,gDACd,EAAwB,4BAoE9B,SAAS,EAAS,CAAW,EAC3B,GAAI,CACF,IAAM,EAAS,IAAI,IAAI,GACvB,MAAO,CAAA,EAAG,EAAO,MAAM,CAAA,EAAG,EAAO,QAAQ,CAAA,CAAE,AAC7C,CAAE,KAAM,CACN,OAAO,CACT,CACF,CAQA,SAAS,UACP,AAAsB,IAAlB,OAAO,QAAuD,YAA7B,AAAyC,OAAlC,OAAO,UAAU,CACpD,OAAO,UAAU,GAKnB,CAAA,EAAG,KAAK,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,GAAG,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAG,IAAA,CAC3E,AADgF,wCA/PzE,SAAS,EAKd,EAAU,IACZ,2BAGO,SAAS,EACV,IAEJ,GAAY,EAEZ,EAJe,KAIR,gBAAgB,CAAC,QAAU,AAAD,IAC/B,EAAiB,EAAM,KAAK,CAAE,EAAM,OAAO,EAAI,iBAAkB,CAC/D,SAAoC,UAA1B,OAAO,EAAM,QAAQ,CAAgB,EAAM,QAAQ,MAAG,EAChE,OAAgC,UAAxB,OAAO,EAAM,MAAM,CAAgB,EAAM,MAAM,MAAG,EAC1D,MAA8B,UAAvB,OAAO,EAAM,KAAK,CAAgB,EAAM,KAAK,MAAG,CACzD,EACF,GAEA,OAAO,gBAAgB,CAAC,qBAAsB,AAAC,IAC7C,IAAM,EAAS,EAAM,MAAM,CACrB,EACJ,AAAkB,iBAAX,EAAsB,EAAS,8BACxC,EAAiB,EAAQ,EAC3B,GACF,wBA4CO,SAAS,AACd,CAAiB,CACjB,EAAsC,CAAC,CAAC,EAQxC,EAAQ,EANgC,CACtC,GAAG,CAAU,CACb,GAIiB,UAJH,EAAyC,OAAO,CAAvC,OAA+C,CAAC,IAAI,EAC3E,CAD8E,UAClE,IACZ,eAAgB,oBAClB,EAEF,kCA5FO,SAAqC,AAA5B,CAA0D,EAExE,GADA,EAAU,EACY,GAAG,CAArB,EAAO,MAAM,CAEjB,IAAK,IAAM,KADG,EAAO,CACF,KADQ,CACD,AADE,EAAG,EAAOvB,MAAM,EAE1C,EAAS,EAEb,+DClEA,EAAA,EAAA,CAAA,CAAA,OASA,EAAA,EAAA,CAAA,CAAA,OC6CA,SAAS4C,EAAkBC,CAAO,EAChC,OAAQA,GACN,IAAK,SACH,MAAO,aACT,KAAK,QACH,MAAO,WACT,KAAK,QACH,MAAO,oBACT,KAAK,SACH,MAAO,YACT,KAAK,WACH,MAAO,UACT,KAAK,SACH,MAAO,QACT,KAAK,OACH,MAAO,UACT,KAAK,eACH,MAAO,cACT,KAAK,OACH,MAAO,WACT,KAAK,QACH,MAAO,WACT,KAAK,UACH,MAAO,oBACT,KAAK,KACH,MAAO,IACT,KAAK,OACH,MAAO,MACT,SACE,MAAO,OACX,CACF,CAmBA,SAASE,EAAuBC,CAAQ,EACtC,OAAQA,GACN,IAAK,YACH,MAAO,WACT,KAAK,SACH,MAAO,QACT,KAAK,QACL,IAAK,eACH,MAAO,cACT,KAAK,SACL,IAAK,gBACH,MAAO,eACT,KAAK,SACL,IAAK,eACH,MAAO,cACT,KAAK,aACH,MAAO,YACT,SACE,OAAO,IACX,CACF,CA+KA,IAAIoD,EAA6B,2BAC7BC,EAA8B,4BAC9BC,EAA+B,6BAC/BC,EAA0B,wBAC1BC,EAA8B,kRAMlC,SAASI,AAAoBxD,CAAI,EAC/B,IAAMa,EAAQ,CAAA,EAAGb,EAAKyD,SAAS,CAAC,CAAC,EAAEzD,EAAK0D,QAAQ,CAAA,CAAE,CAC9CC,OAAON,aALU,CAAmB,CAMxC,IAAK,IAAIO,EAAI,EAAGA,EAAI/C,EAAMtB,MAAM,CAAEqE,IAAK,AACrCD,GAAQE,OAAOhD,EAAMiD,UAAU,CAACF,IAChCD,EAPY,AAOLA,OAAOL,MAPY,MAOAC,gBANhB,CAAmB,CAQ/B,OAAOI,EAAKI,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAI,IACxC,6BArJA,SAASjE,AAAuBC,CAAI,EAClC,GAAM,CAAEC,YAAU,UAAEC,CAAQ,CAAE,CAAGF,QACjC,AAAmB,SAAfC,GAAwC,cAAfA,GAA6C,mBAAmB,CAAlCA,EAClD,OAEU,YAAY,CAA3BA,EAAkC,WACnB,OAAO,CAAtBA,GACa,UAAbC,GAAqC,UAAU,CAAvBA,EADK,IACyB,IACzC,SAAS,CAAtBA,EAA6B,QAChB,AAAbA,SAAsB,GAAO,QAChB,QAAbA,GAAmC,aAAbA,GAAwC,iBAAbA,GAA4C,eAAe,CAA5BA,EAC3E,MAEF,SACT,uDAhKA,SAASrB,AAAoBC,CAAG,EAC9B,OAAQA,GACN,IAAK,YAcL,QAbE,MAAO,WACT,KAAK,OACH,MAAO,YACT,KAAK,WACH,MAAO,eACT,KAAK,gBACH,MAAO,eACT,KAAK,QACL,IAAK,QACL,IAAK,QACH,MAAO,OACT,KAAK,QACH,MAAO,OAGX,CACF,6BA+JA,SAAS8B,AAAuBC,CAAK,EACnC,IAKIW,EAcAC,EAnBEX,EAASD,EAAMC,MAAM,EAAI,EAAE,CAC3BC,EAAkBD,EAAOE,IAAI,CAAC,AAACC,IAAsB,IAAhBA,EAAEC,SAAS,EAChDC,EAAgBN,EAAMpB,OAAO,CAAGqB,EAAOM,IAAI,CAAC,AAACH,GAAMA,EAAEI,EAAE,GAAKR,EAAMpB,OAAO,EAAI,KAAK,EAClF6B,EAAyBH,GAAeD,aAAc,EACtDK,EAA0C,AAAzBV,OAAMU,cAAc,CAyB3C,OAtBEC,EADiB,UAAU,CAAzBX,EAAM3B,IAAI,CACIqC,EAAiB,OAAS,YAClB,OAAO,CAAtBV,EAAM3B,IAAI,CACH6B,EAAkB,OAAS,OAClCA,GAAmBQ,EACZ,OACPR,EACO,KAH4B,OAInCQ,EACO,CAHU,MAKV,OAHS,AAOzBE,EADiB,UAAU,CAAzBZ,EAAM3B,IAAI,CACYoC,EAAyB,YAAc,cACvC,OAAO,CAAtBT,EAAM3B,IAAI,CACKqC,EAAiB,YAAc,cAC9CR,GAAmBQ,EACJ,YAEA,EAHoB,QAKvC,CACLG,4BAA6BX,EAC7BY,eAAgBH,EAChBI,uBAAwBH,CAC1B,CACF,oCAYA,SAASM,AAA8BC,CAAK,SAC1C,MAAIA,GAAuCC,OAA7B,AAAoCC,QAA5BF,AAAoC,CAACA,GAGvDA,GAAS,EAAU,AAH4C,CAGnD,AAHgB,GAI5BA,EAJiC,CAIxB,GAAW,CAAP,AAJyB,MAKtCA,GAAS,GAAW,CAAP,OACbA,GAAS,IAAY,CAAP,QACX,WANE,SAOX,+BAjBA,SAASF,AAAyBzF,CAAI,EACpC,IAAMkD,EAAS,CAAClD,GAAQ,EAAA,CAAE,CAAEiD,IAAI,GAAGC,MAAM,QACzC,AAAe,GAAG,CAAdA,EAAqB,IACrBA,GAAU,GAAW,CAAP,MACdA,GAAU,IAAY,CAAP,QACfA,GAAU,IAAY,CAAP,SACZ,UACT,6BAqBA,SAAS6C,AAAuBC,CAAM,EACpC,IAAMhD,EAAU,CAACgD,GAAU,EAAA,CAAE,CAAE/C,IAAI,GAAGxC,OAAO,CAAC,SAAU,WACxD,AAAKuC,GACEA,CADH,CACW5D,IADD,OAAO,AACK,GAAGqB,OAAO,CAAC,iBAAkB,IAAIA,OAAO,CAAC,OAAQ,KAAKA,OAAO,CAAC,MAAO,KAAKA,OAAO,CAAC,SAAU,KAAO,SAC/H,6BACA,SAAgCyF,AAAvBD,CAA2B,EAClC,IAAME,EAAI,CAACD,GAAQ,EAAA,CAAE,CAAE9G,WAAW,UAClC,AAAK+G,EACD,CADI,CAAJ,uBACyBC,IAAI,CAACD,GAAW,CAAP,YAClC,kBAAkBC,IAAI,CAACD,GAAW,CAAP,QAC3B,gDAAgDC,IAAI,CAACD,GAChD,CADoD,SAGzD,4CAA4CC,IAAI,CAACD,GAAW,CAAP,YACrD,wCAAwCC,IAAI,CAACD,GAAW,CAAP,cAC9C,QARQ,OASjB,kCACA,SAAqCjG,AAA5BmG,CAA+B,EACtC,IAAMC,EAAM,CAACpG,GAAO,EAAA,CAAE,CAAE+C,IAAI,GAC5B,GAAI,CAACqD,EAAK,MAAO,UACjB,GAAI,CACF,IAAMC,EAAO,IAAInG,IAAIkG,GAAKE,QAAQ,CAACpH,WAAW,GAC9C,GAAa,eAATmH,GAAyBA,EAAKE,QAAQ,CAAC,eAAgB,MAAO,SAClE,GAAa,eAATF,GAAyBA,EAAKE,QAAQ,CAAC,eAAgB,MAAO,SAClE,MAAO,OACT,CAAE,KAAM,CACN,MAAO,SACT,CACF,kCAtCA,SAASX,AAA4B/B,CAAK,EACxC,SAAIA,GAAsC,CAAC6B,MAA7B,CAAoCC,OAA5B9B,CAAoC,CAACA,GACzD,KAD8B,AAAmC,CAC1D,IAD4B,MAGrC,IAAMC,EAAKD,EAAS,MAAD,CAAQ,IAAI,IAC/B,AAAIC,EAAK,EAAU,CAAP,OACRA,EAAK,GAAW,CAAP,QACTA,EAAK,GAAW,CAAP,SACN,WACT,8BA3NA,SAASpB,AAAwBC,CAAI,EACnC,MAAOA,AAAS,aAAW,YAAc,MAC3C,wCAqCA,SAASQ,AAAkCD,CAAO,EAChD,OAAQA,GACN,IAAK,gBACH,OAAOE,EAAuB,cAAgB,OAChD,KAAK,aACH,OAAOA,EAAuB,WAAa,OAC7C,KAAK,mBACH,OAAOA,EAAuB,UAAY,OAC5C,KAAK,oBACH,OAAOA,EAAuB,WAAa,OAC7C,KAAK,mBACH,OAAOA,EAAuB,WAAa,OAC7C,KAAK,iBACH,OAAOA,EAAuB,eAAiB,OACjD,SACE,OAAOH,EAAkBC,EAC7B,CACF,yBA7DA,SAASV,AAAmBC,CAAQ,QAClC,AAAiB,aAAa,CAA1BA,EAAiC,YACpB,iBAAiB,CAA9BA,EAAqC,gBAClC,gBACT,+BAyIA,SAASmB,AAAyBC,CAAK,EACrC,IAAMC,EAAKD,EAAS,MAAD,CAAQ,IAAI,IAC/B,AAAIC,EAAK,EAAU,CAAP,OACRA,EAAK,GAAW,CAAP,QACTA,EAAK,IAAY,CAAP,UACP,YACT,yBACA,SAASC,AAAmBN,CAAI,EAC9B,GAAIA,EAAKO,QAAQ,CAAE,MAAO,SAC1B,GAAIP,EAAKQ,KAAK,CAAE,MAAO,MACvB,IAAMC,EAAIT,EAAKU,IAAI,EAAI,UACvB,AAAID,EAAEE,UAAU,CAAC,UAAkB,CAAP,OACxBF,EAAEE,UAAU,CAAC,UAAkB,CAAP,OACxBF,EAAEE,UAAU,CAAC,UAAkB,CAAP,OAClB,mBAAmB,CAAzBF,EAAgC,MAC7B,OACT,yBArJA,SAAStB,AAAmBC,CAAK,EAC/B,IAAMC,EAAU,AAAiB,iBAAVD,EAAqBA,EAAME,IAAI,GAAK,GAC3D,OAAOD,EAAQE,MAAM,CAAG,EAAIF,EAAU,SACxC,4BAuLA,SAASwC,AAAsBxF,CAAI,EACjC,MAAO,CAACA,GAAQ,EAAA,CAAE,CAAEiD,IAAI,EAC1B,4BA/OA,SAASX,AAAsBC,CAAI,EACjC,OAAQA,GACN,IAAK,YACH,MAAO,WACT,KAAK,OACH,MAAO,YACT,KAAK,WACH,MAAO,UACT,KAAK,QACH,MAAO,OACT,KAAK,QACH,MAAO,OACT,KAAK,QACH,MAAO,OACT,KAAK,QACH,MAAO,OACT,KAAK,gBACL,IAAK,gBACH,MAAO,eACT,SACE,OAAO,IACX,CACF,gCAwGA,SAASiB,AAA0BC,CAAO,EACxC,OAAQA,GACN,IAAK,YAqCL,QApCE,MAAO,0BACT,KAAK,eACH,MAAO,cACT,KAAK,QACH,MAAO,iBACT,KAAK,WACH,MAAO,UACT,KAAK,aACH,MAAO,YACT,KAAK,MACH,MAAO,MACT,KAAK,QACH,MAAO,OACT,KAAK,WACL,IAAK,eACL,IAAK,aACH,MAAO,YACT,KAAK,YACL,IAAK,aACH,MAAO,YACT,KAAK,QACH,MAAO,OACT,KAAK,SACH,MAAO,QACT,KAAK,gBACH,MAAO,gBACT,KAAK,mBACH,MAAO,mBACT,KAAK,SACH,MAAO,QACT,KAAK,UACH,MAAO,SACT,KAAK,gBACH,MAAO,eACT,KAAK,cACH,MAAO,cAGX,CACF,UC7JA,IAAA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OAaA,IAAI,EAAyB,KACzB,EAA8C,KAC9C,EAAkC,KAGlC,EAA8C,CAChD,6BAA6B,EAC7B,eAAgB,UAChB,uBAAwB,SAC1B,EASI,EAAsD,KA0DtD,EAAkD,KAiC/C,eAAemB,EACpB,CAAyBJ,EAEzB,GAAIQ,EAAQ,OAAO,EACnB,GAAI,EAAa,OAAO,EAQxBG,IAAM,EAAU,CAAC,UACf,GAAIA,CACF,IAAM,EAAM,MAAM,MAAM,yBACxB,GAAI,CAACtC,EAAI,EAAE,CAAE,OAAO,KACpB,IAAM,EAAO,MAAM,EAAI,IAAI,GAC3B,GAAI,CAAC,EAAI,OAAO,EAAI,CAAC,EAAI,GAAG,EAAI,CAAC,EAAI,IAAI,CAAE,OAAO,KAClD,IAAM,EACH,AAA8B,iBAAvB,EAAI,cAAc,EAAiB,EAAI,cAAc,EAC7D,EAAQ,WAAW,CACrB,EAAmB,EAEnB,IAAM,EAAU,CADJ,MAAA,EAAA,CAAA,CAAA,MAAA,EACQ,OAAO,CACrB,EAAS,EAAI,GAAG,CAChB,EAAU,EAAI,IAAI,CA2FxB,OA1FA,EAAQ,IAAI,CAAC,EAAQuC,CACnB,SAAU,EAAI,IAAI,CAKlB,UAAW,CAAE,WAAY,CAAW,EACpC,YAAa,eAQb,0BAA0B,EAQ1B,aAAa,EACb,iBAAkB,iBAClB,kBAAmB,sBACnB,qBAAqB,EACrB,oBAAqB,CACnB,YAAY,EACZ,gBAAgB,CAClB,EAMA,oBAAoB,EAepB,YAAa,EAAA,eAAe,CAQ5B,0BAA2B,GAE3B,OAAQ,AAAC,IACP,EAAsB,CACpB,qBD4Ee,CC5EO,CACtB,WAAY,EAAQ,UAAU,CAC9B,YAAa,EAAQ,UAAU,CAC/B,YAAa,EAAQ,UAAU,CAC/B,OAAQ,EAAQ,MAAM,CACtB,WAAY,EAAQ,SAAS,CAG7B,UAAW,EACX,GAAI,CAAgB,AACtB,EACA,EAAS,QAAQ,CAAC,GAMlB,CAAA,EAAA,EAAA,2BAAA,AAA2B,EAAC,CAC1B,OAAQ,EACR,KAAM,aACN,EACA,WAAY,EAAQ,UAAU,CAC9B,UAAW,EAAQ,SAAS,AAC9B,EACF,CACF,GACA,EAAS,EACF,CACT,CAAE,KAAM,CAGN,OAAO,IACT,EACF,CAAC,GAMD,OALA,EAAc,EAET,EAAQ,IAAI,CAAC,AAAC,IACb,AAAC,GAAQ,GAAc,IAAA,CAC7B,GACO,CACT,CAqEA,SAAS,EAAuB,CAA+B,EAC7D,GAAI,CAAC,GAAU,CAAC,EAAqB,OACrC,IAAM,EAAO,EAAQ,CAAE,GAAG,CAAmB,CAAE,GAAG,CAAM,AAAD,EAAK,EAC5D,EAAsB,EACtB,GAAI,CACF,EAAO,QAAQ,CAAC,EAClB,CAAE,KAAM,CAER,CACF,CC/UA,IAAA,EAAA,EAAA,CAAA,CAAA,OAEA,IAAM,EAAmB,qCACnB,EAAiB,mCAEvB,SAAS,IAKP,IAAM,EACkB,IAAtB,OAAO,WAA6B,WAAW,MAAM,MAAG,SACtD,AAAJ,GAAO,WAAmB,CAAP,CAAS,UAAU,GAC/B,CAAC,oCAAoC,CAAC,CAAC,OAAO,CAAC,QAAS,AAAC,IAC9D,IAAM,EAAqB,GAAhB,KAAKtC,MAAM,GAAW,EAEjC,MAAO,CADU,MAAP,EAAa,EAAS,EAAJ,EAAW,CAAA,EAC9B,QAAQ,CAAC,GACpB,EACF,CAEO,SAAS,IAEd,GAAI,CACF,IAAM,EAAW,OAAO,YAAY,CAAC,OAAO,CAAC,GAC7C,GAAI,EAAU,OAAO,EACrB,IAAM,EAAQ,IAEd,OADA,OAAO,YAAY,CAAC,OAAO,CAAC,EAAkB,GACvC,CACT,CAAE,KAAM,CAGN,OAAO,GACT,CACF,CAEO,SAAS,IAEd,GAAI,CACF,IAAM,EAAW,OAAO,cAAc,CAAC,OAAO,CAAC,GAC/C,GAAI,EAAU,OAAO,EACrB,IAAM,EAAQ,IAEd,OADA,OAAO,cAAc,CAAC,OAAO,CAACM,EAAgB,GACvC,CACT,CAAE,KAAM,CACN,OAAO,GACT,CACF,CAKO,SAAS,IAEd,MAAO,CAAA,EAAA,EAAA,8BAAA,AAA8B,GACvC,6EH3BA,IAAA,EAAA,EAAA,CAAA,CAAA,OA+BA,IAAM,EAAM,CAAA,EAAA,EAAA,aAAa,AAAb,EAA4C,MAOxD,SAAS,EAAoB,CAAY,EACvC,GAAmB,UAAf,OAAO,EAAkB,MAAO,GACpC,GAAI,EAAI,UAAU,CAAC,SAAU,OAAO,EAEpC,GAAI,CACF,IAAM,EAAS,IAAI,IAAI,EAAK,OAAO,QAAQ,CAAC,MAAM,EAClD,OACE,EAAO,MAAM,GAAK,OAAO,QAAQ,CAAC,MAAM,EACxC,EAAO,QAAQ,CAAC,UAAU,CAAC,QAE/B,CAAE,KAAM,CACN,OAAO,CACT,CACF,CAUO,SAAS,IACd,GAAM,CAAC,EAAS,EAAW,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,EAAC,SAmBvC,MAlBA,CAAA,EAAA,EAAA,SAAA,AAAS,EAAC,KACR,IAAI,GAAY,EAahB,MAZK,AAAC,WACJ,GAAI,CACF,IAAM,EAAM,MAAM,MAAM,gBACxB,GAAI,CAAC,EAAI,EAAE,CAAE,OACb,IAAM,EAAQ,MAAM,EAAI,IAAI,GAC5B,GAAI,EAAW,OACf,IAAM,EAAO,GAAM,SAAS,QACxB,GAAM,EAAW,EACvB,CAAE,KAAM,CAER,CACF,CAAC,IACM,KACL,GAAY,CACd,CACF,EAAG,EAAE,EACE,CACT,4BAEO,SAAS,AAAkB,UAAE,CAAQ,CAA2B,EACrE,GAAM,QAAE,CAAM,CAAE,CAAG,CAAA,EAAA,EAAA,OAAA,AAAO,IACpB,EAAa,IAIb,EAAW,CAAA,EAAA,EAAA,OAAA,AAAO,EACtB,IAAM,CAAC,CACL,YAAa,IACb,UAAW,IACX,WAAY,IACd,CAAC,CACD,EAAE,EAOE,CAAC,EAAgB,EAAkB,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,EAAgB,MACpE,CAAA,EAAA,EAAA,SAAS,AAAT,EAAU,WACR,IAAI,EAAY,GAuBhB,OE3DuC,EFyCP,CAC9B,IE1C8D,QF0CjD,EAAS,WAAW,CACjC,UAAW,EAAS,SAAS,CAC7BqB,WAAY,EAASO,UAAU,QAC/B,aACA,CACF,EE9CE,AAAJ,IACA,EAA4B,CAAC,UAC3B,GAAI,CACF,IAAM,EAHqB,AAGf,MAAM,CAHgB,KAGV,yBACxB,GAAI,CAAC,EAAI,EAAE,CAAE,WACX,CAAA,EAAA,EAAA,6BAAA,AAA6B,IAG/B,IAAM,EAAO,MAAM,EAAI,IAAI,GAC3B,GAAI,CAAC,EAAI,GAAG,EAAI,CAAC,EAAI,IAAI,CAAE,WACzB,CAAA,EAAA,EAAA,6BAAA,AAA6B,IAG/B,IAAM,EAC2B,UAA9B,OAAOhB,EAAI,cAAc,EAAiB,EAAI,cAAc,EAC7D,EAAQ,WAAW,CACrB,CAAA,EAAA,EAAA,2BAAA,AAA2B,EAAC,CAC1B,OAAQG,EAAI,GAAG,CACf,KAAM,EAAI,IAAI,YACd,EACA,WAAY,EAAQ,UAAU,CAC9B,UAAW,EAAQ,SAAS,AAC9B,EACF,CAAE,KAAM,CAGR,EACF,CAAC,EAAA,EFoBM,EAAmBU,CACtB,YAAa,EAAS,WAAW,CACjC,UAAW,EAAS,SAAS,CAC7B,WAAY,EAAS,UAAU,QAC/B,aACA,CACF,GAAG,IAAI,CAAC,KACN,GAAI,EAAW,OACf,IAAM,IACF,GAAU,EAAkB,EADf,AAEnB,GACO,KACL,GAAY,CACd,CACF,EAAG,CAAC,EAAUH,EAAQ,EAAW,EAYjC,CAAA,EAAA,EAAA,SAAA,AAAS,EAAC,KAER,GAAI,CAAC,EAAgB,OACrB,IAAM,EAAW,OAAO,KAAK,CACvB,EAAsC,CAC1C,CAAC,EAA2B,CAAE,EAC9B,CAAC,EAA4B,CAAE,EAAS,SAAS,CACjD,CAAC,EAA6B,CAAE,EAAS,UAAU,AACrD,EAWA,OAVA,OAAO,KAAK,CAAG,MAAO,EAAO,KAE3B,GAAI,CAAC,EADO,AAAiBiB,iBAAV,CACM,CADe,EAAQ,aAAiB,IAAM,EAAM,IAAI,CAAG,EAAME,GAAG,EAC9D,OAAO,EAAS,EAAO,GACtD,IAAM,EAAsB,CAC1B,GAAG,CAAW,CACd,CAAC,EAAwB,CAAE,EAC3B,GAAI,GAAM,SAAW,CAAC,CAAC,AACzB,EACA,OAAO,EAAS,EAAO,CAAE,GAAI,GAAQ,CAAC,CAAC,CAAG,QAAS,CAAO,EAC5D,EACO,KACL,OAAO,KAAK,CAAG,CACjB,CACF,EAAG7B,CAAC,EAAU,EAAQ,EAAeA,EAKrCf,CAAAA,EAAAA,EAAAA,SAAAA,AAAS,EAAC,KACR,IAAI,GAAY,EAgBhB,MAfK,CAAC,UACJ,IAAM7D,EAAS,MAAM,EAAmB,CACtCqB,YAAa,EAAS,WAAW,CACjC,UAAW,EAAS,SAAS,CAC7B,WAAY,EAAS,UAAU,CAC/B,OAAQ,aACR,CACF,GACA,IAAI,GAAc,EAClB,GAAI,CACF,EAFwB,AAEjB,EAFQ,MAEA,CAAC,CAAE,OAAQ,EAAQ,YAAa,EAAY,WAAY,CAAW,EACpF,CAAE,KAAM,CAER,EACF,CAAC,GACM,KACL,EAAY,EACd,CACF,EAAG,CAAC,EAAU,EAAQ,EAAW,EAEjC,IAAM,EAAQ,CAAA,EAAA,EAAA,WAAA,AAAW,EACvB,CAAC,EAAO,EAAY,KAClB,IAAM,EAAW,GAAS,UAAY,CAAA,EAAA,EAAA,UAAA,AAAU,IAC1C,EAAY,GAAS,WAAa,KAGxC,GAAqC,CAAjC,CACF,GAAI,CACF,IAAM,CAFsC,CAE1B,OAAO,KAAK,CACxB,EAAwB,MAAO,EAAO,KAC1C,IAAM,EACa,EALH,QAKd,OAAO,EACH,EACA,aAAiB,IACf,EAAM,IAAI,CACV,EAAM,GAAG,CACjB,GAAI,CAAC,EAAoB,GAAM,OAAO,EAAU,EAAO,GACvD,IAAM,EAAsB,CAC1B,CAAC,EAA4B,CAAE,EAC/B,GAAI,GAAM,SAAW,CAAC,CAAC,AACzB,EACA,OAAO,EAAU,EAAO,CAAE,GAAI,GAAQ,CAAC,CAAC,CAAG,QAAS,CAAO,EAC7DkD,EAGA,OAAO,KAAKa,CAAG,EACf,eAAe,KACb,OAAO,KAAK,CAAG,CACjB,EACF,CAAE,KAAM,CAER,CAEG,CAAC,cEsFV,EFrFqB,IEqFC,EACtB,AFtF2B,EAAmB,CACtC,CE0FP,WF1FoB,EAAS,WAAW,CACjC,UAAW,EAAS,SAAS,CAC7B,WAAY,EAAS,UAAU,CAC/B,OAAQ,aACR,CACF,KACkB,IAAO,IAAY,IAAU,EEsFrD,GAAK,CAAD,CACJ,GAAI,CACF,EAFW,AAEJ,OAAO,CAAC,EAAY,GAAP,EAClB,CACA,EAFuB,AACpB,KAAK,CACR,EAAU,CAIV,IAJe,EADG,IAKlB,EAJuB,AAIX,CACZ,GAAI,CADa,CACI,CAAE,EAAd,IADgB,IACF,CAAL,CAAiB,AAAe,EAAI,CAAC,CACzD,AAD0D,CAAhB,CAE5C,CAAE,KAAM,CAER,CAJqD,CF9FjD,CAAC,EACH,EACA,CAAC,EAAU,EAAQ,EAAW,EAG1B,EAAQ,CAAA,EAAA,EAAA,OAAA,AAAO,EACnB,IAAM,CAAC,CACL,QACA,WAAa,AAAD,IEQhB,GAAK,CAAD,CACJ,GAAI,CFRe,EEON,CAGT,EAAO,gBAAgB,GAQvB,MAEA,EAAO,iBAAiB,GACxB,EAAO,KAAK,GACZ,EAAmB,KAEvB,CAAE,KAAM,CAER,CFzBW,EAWE,EAAmB,CACtB,IAZU,QAYG,EAAS,WAAW,CACjC,UAAW,EAAS,SAAS,CAC7B,WAAY,EAAS,UAAU,QAC/B,EACA,YACF,GAAG,IAAI,CAAC,KACN,IAAM,EElPT,EFmPO,GAAU,EAAkB,EADf,AAEnB,GAfA,EAAkB,KAiBtB,EACA,YAAa,AAAC,KEWb,AFVC,SEUQ,AAAc,CAA6B,EACzD,GAAI,AAAC,GAAW,GACZ,IAAqB,AADV,EAEf,GAAI,CACF,EAAO,CAHuB,IAGlB,GAF2B,AAGvC,EAAO,QAAQ,CAAC,GAChB,EAAmB,EAKnB,EAAuB,CAAE,UAAW,CAAe,EACrD,CAAE,KAAM,CAER,CACF,EFzBsB,GAGV,GAAgB,EAAkB,EACxC,EACA,oBAAqB,AAAC,IEhO1B,GAVA,CAUI,CAVe,CAAE,GAAG,AF2OE,CAAA,AE3OG,EAIzB,CAJwB,GAK1B,EAAsB,CACpB,GAAG,CAAmB,CACtB,GAAI,CAAgB,CACtB,EAEG,EANoB,AAOzB,GAAI,CACF,EAFW,AAEJ,QAAQ,CAAC,EAClB,CAAE,KAAM,CAER,CF6NI,EACA,YAAa,EAAS,WAAW,CACjC,UAAW,EAAS,SAAS,CAC7B,aAAc,IAAM,CAAA,EAAA,EAAA,UAAA,AAAU,IAChC,CAAC,CACD,CAAC,EAAO,EAAU,EAAQ,EAAW,EAGvC,MAAO,CAAA,EAAA,EAAA,GAAA,EAAC,EAAI,QAAQ,CAAA,CAAC,MAAO,WAAQ,GACtC,mBAEO,SAAS,EACd,IAAM,EAAQ,CAAA,EAAA,EAAA,UAAA,AAAU,EAAC,UACzB,AAAK,GAII,CAJL,AAKA,GALQ,GAKD,SAAM,EACb,WAAY,SAAM,EAClB,YAAa,SAAM,EACnB,oBAAqB,SAAM,EAC3B,YAAa,YACb,UAAW,YACX,aAAc,IAAM,CAAA,EAAA,EAAA,UAAA,AAAU,GAChC,CAGJ"}
|