@kontsedal/olas-devtools 0.0.1-rc.1 → 0.0.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/README.md +15 -3
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +6 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +16 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/store.ts +25 -0
package/README.md
CHANGED
|
@@ -49,12 +49,24 @@ The **Clear** button empties the three event logs (the tree is live state, not a
|
|
|
49
49
|
## API
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
|
+
function DevtoolsLauncher(props: {
|
|
53
|
+
root: Pick<Root<unknown>, '__debug'>
|
|
54
|
+
defaultTab?: DevtoolsTab
|
|
55
|
+
maxEntries?: number // per-log cap, oldest drop first; default 100
|
|
56
|
+
urlHashKey?: string // forwarded to the panel; persists open-tab in the URL
|
|
57
|
+
storageKey?: string // localStorage key for window position/size; default 'olas-devtools-window'
|
|
58
|
+
initial?: { x?: number; y?: number; w?: number; h?: number }
|
|
59
|
+
}): JSX.Element
|
|
60
|
+
|
|
52
61
|
function DevtoolsPanel(props: {
|
|
53
|
-
root: Root<unknown>
|
|
54
|
-
defaultTab?:
|
|
55
|
-
maxEntries?: number
|
|
62
|
+
root: Pick<Root<unknown>, '__debug'>
|
|
63
|
+
defaultTab?: DevtoolsTab
|
|
64
|
+
maxEntries?: number
|
|
65
|
+
urlHashKey?: string
|
|
56
66
|
}): JSX.Element
|
|
57
67
|
|
|
68
|
+
type DevtoolsTab = 'tree' | 'cache' | 'mutations' | 'fields' | 'events'
|
|
69
|
+
|
|
58
70
|
// Lower-level store — exported so consumers can build their own UI.
|
|
59
71
|
class DevtoolsStore {
|
|
60
72
|
readonly tree$: Signal<ControllerNode>
|
package/dist/index.cjs
CHANGED
|
@@ -266,6 +266,7 @@ var DevtoolsStore = class {
|
|
|
266
266
|
return;
|
|
267
267
|
case "controller:disposed":
|
|
268
268
|
this.tree$.set(setNodeState(this.tree$.peek(), event.path, "disposed"));
|
|
269
|
+
this.dropStartsForPath(event.path);
|
|
269
270
|
return;
|
|
270
271
|
case "cache:subscribed":
|
|
271
272
|
this.pushCache({
|
|
@@ -360,6 +361,7 @@ var DevtoolsStore = class {
|
|
|
360
361
|
this.cache$.set([]);
|
|
361
362
|
this.mutations$.set([]);
|
|
362
363
|
this.fields$.set([]);
|
|
364
|
+
this.mutationStarts.clear();
|
|
363
365
|
}
|
|
364
366
|
pushCache(entry) {
|
|
365
367
|
const full = {
|
|
@@ -392,6 +394,20 @@ var DevtoolsStore = class {
|
|
|
392
394
|
this.mutationStarts.delete(key);
|
|
393
395
|
return this.now() - startedAt;
|
|
394
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Drop every pending mutation-start record under `path` (and its
|
|
399
|
+
* descendants). Called on `controller:disposed` so a dispose mid-mutation
|
|
400
|
+
* doesn't leave a permanent entry in `mutationStarts`.
|
|
401
|
+
*/
|
|
402
|
+
dropStartsForPath(path) {
|
|
403
|
+
if (this.mutationStarts.size === 0) return;
|
|
404
|
+
const prefix = `${path.join(">")}>`;
|
|
405
|
+
const exact = path.join(">");
|
|
406
|
+
for (const key of this.mutationStarts.keys()) {
|
|
407
|
+
const beforeHash = key.split("#")[0] ?? "";
|
|
408
|
+
if (beforeHash === exact || beforeHash.startsWith(prefix)) this.mutationStarts.delete(key);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
395
411
|
};
|
|
396
412
|
function mutationKey(path, name) {
|
|
397
413
|
return `${path.join(">")}#${name ?? ""}`;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/format.ts","../src/JsonView.tsx","../src/store.ts","../src/styles.ts","../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx"],"sourcesContent":["/**\n * Render a payload (props, vars, result, etc.) as a single-line string for\n * the panel. Cuts at `maxLen` so a giant blob doesn't blow up the layout.\n */\nexport function formatPayload(value: unknown, maxLen = 200): string {\n if (value === undefined) return 'undefined'\n if (value === null) return 'null'\n if (typeof value === 'function') return '[fn]'\n let s: string\n try {\n s = JSON.stringify(value, replaceUnserializable)\n } catch {\n s = String(value)\n }\n if (s === undefined) s = String(value)\n return s.length > maxLen ? `${s.slice(0, maxLen)}…` : s\n}\n\nfunction replaceUnserializable(_key: string, value: unknown): unknown {\n if (typeof value === 'function') return '[fn]'\n if (typeof value === 'bigint') return value.toString()\n if (value instanceof Error) return { name: value.name, message: value.message }\n return value\n}\n\n/** Render an HH:MM:SS.mmm timestamp from epoch ms. */\nexport function formatTime(t: number): string {\n const d = new Date(t)\n const pad = (n: number, w = 2) => n.toString().padStart(w, '0')\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`\n}\n\n/** Render a controller path / query key as a compact string. */\nexport function formatPath(path: readonly unknown[]): string {\n if (path.length === 0) return '∅'\n return path.map((p) => String(p)).join(' › ')\n}\n","// Compact JSON renderer for the devtools panel payload column.\n//\n// Renders values inline by default. Objects/arrays show their summary\n// (\"{4}\", \"[12]\") inline; clicking the summary expands them. Each nested\n// level inherits the same expand/collapse semantics. Scoped to the\n// `olas-devtools-json-*` class prefix; styles are in `styles.ts`.\n\nimport { type ReactElement, useState } from 'react'\n\nexport function JsonView({ value, depth = 0 }: { value: unknown; depth?: number }): ReactElement {\n return <Render value={value} depth={depth} initiallyOpen={depth === 0} />\n}\n\nfunction Render({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n if (value === null) return <span className=\"olas-devtools-json-null\">null</span>\n if (value === undefined) return <span className=\"olas-devtools-json-null\">undefined</span>\n\n const t = typeof value\n if (t === 'string') return <span className=\"olas-devtools-json-string\">\"{value as string}\"</span>\n if (t === 'number') return <span className=\"olas-devtools-json-number\">{String(value)}</span>\n if (t === 'boolean') return <span className=\"olas-devtools-json-boolean\">{String(value)}</span>\n if (t === 'bigint') return <span className=\"olas-devtools-json-number\">{String(value)}n</span>\n\n // Errors render as `Error(\"message\")` so they're distinguishable from\n // plain string payloads.\n if (value instanceof Error) {\n return (\n <span className=\"olas-devtools-json-error\">\n {value.name}({JSON.stringify(value.message)})\n </span>\n )\n }\n\n if (Array.isArray(value)) {\n return <CollapsibleArray value={value} depth={depth} initiallyOpen={initiallyOpen} />\n }\n\n if (t === 'object') {\n return (\n <CollapsibleObject\n value={value as Record<string, unknown>}\n depth={depth}\n initiallyOpen={initiallyOpen}\n />\n )\n }\n\n return <span>{String(value)}</span>\n}\n\nfunction CollapsibleArray({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown[]\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const [open, setOpen] = useState(initiallyOpen && value.length <= 12)\n if (value.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">[]</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">[</span>\n <span className=\"olas-devtools-json-summary\">\n {value.length} item{value.length === 1 ? '' : 's'}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">[</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {value.map((item, idx) => (\n <span key={idx} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-index\">{idx}:</span>\n <Render value={item} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </span>\n )\n}\n\nfunction CollapsibleObject({\n value,\n depth,\n initiallyOpen,\n}: {\n value: Record<string, unknown>\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const keys = Object.keys(value)\n const [open, setOpen] = useState(initiallyOpen && keys.length <= 8)\n if (keys.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">{'{}'}</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n <span className=\"olas-devtools-json-summary\">\n {keys.slice(0, 3).join(', ')}\n {keys.length > 3 ? ` +${keys.length - 3}` : ''}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {keys.map((k) => (\n <span key={k} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-key\">{k}:</span>\n <Render value={value[k]} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </span>\n )\n}\n","import type { DebugEvent, Root } from '@kontsedal/olas-core'\nimport { type Signal, signal } from '@kontsedal/olas-core'\n\n/**\n * Per-path node in the live controller tree. `state` reflects the most\n * recently observed lifecycle event; `path` is the array reported by the\n * devtools bus.\n */\nexport type ControllerNode = {\n readonly path: readonly string[]\n state: 'active' | 'suspended' | 'disposed'\n props: unknown\n children: ControllerNode[]\n}\n\n/** One entry in the cache timeline. */\nexport type CacheEntry =\n | {\n id: number\n t: number\n kind: 'subscribed'\n queryKey: readonly unknown[]\n subscriberPath: readonly string[]\n }\n | { id: number; t: number; kind: 'fetch-start'; queryKey: readonly unknown[] }\n | {\n id: number\n t: number\n kind: 'fetch-success'\n queryKey: readonly unknown[]\n durationMs: number\n }\n | {\n id: number\n t: number\n kind: 'fetch-error'\n queryKey: readonly unknown[]\n durationMs: number\n error: unknown\n }\n | { id: number; t: number; kind: 'invalidated'; queryKey: readonly unknown[] }\n | { id: number; t: number; kind: 'gc'; queryKey: readonly unknown[] }\n\n/** One entry in the mutation log. `durationMs` is set on success/error when\n * the entry can be paired with a preceding `run` for the same path+name. */\nexport type MutationEntry =\n | { id: number; t: number; kind: 'run'; path: readonly string[]; name?: string; vars: unknown }\n | {\n id: number\n t: number\n kind: 'success'\n path: readonly string[]\n name?: string\n result: unknown\n durationMs?: number\n }\n | {\n id: number\n t: number\n kind: 'error'\n path: readonly string[]\n name?: string\n error: unknown\n durationMs?: number\n }\n | { id: number; t: number; kind: 'rollback'; path: readonly string[]; name?: string }\n\n/** One entry in the field validation log. */\nexport type FieldEntry = {\n id: number\n t: number\n path: readonly string[]\n field: string\n valid: boolean\n errors: string[]\n}\n\n/** Defaults — exported so callers can override via `new DevtoolsStore({ maxEntries: 500 })`. */\nexport const DEFAULT_MAX_ENTRIES = 100\n\nexport type DevtoolsStoreOptions = {\n /** Cap on each event log (cache, mutation, field). Oldest entries drop first. */\n maxEntries?: number\n /** Optional clock — useful for tests. Default: `() => Date.now()`. */\n now?: () => number\n}\n\n/**\n * Subscribes to a root's `__debug` bus and maintains live state for the\n * devtools panel. Exposes signals so the React layer can consume via\n * `@kontsedal/olas-react`'s `use()`.\n *\n * Pure logic — no DOM, no React. Construct one per root.\n */\nexport class DevtoolsStore {\n readonly tree$: Signal<ControllerNode> = signal(makeRoot())\n readonly cache$: Signal<CacheEntry[]> = signal([])\n readonly mutations$: Signal<MutationEntry[]> = signal([])\n readonly fields$: Signal<FieldEntry[]> = signal([])\n\n private readonly maxEntries: number\n private readonly now: () => number\n private nextId = 1\n\n /** Keyed by `path|name` so a mutation:run can be paired with its\n * success/error to compute duration. Cleared after pairing. */\n private mutationStarts = new Map<string, number>()\n\n constructor(options?: DevtoolsStoreOptions) {\n this.maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES\n this.now = options?.now ?? (() => Date.now())\n }\n\n /**\n * Subscribe to the given root's debug bus. Returns the unsubscribe. The\n * caller (typically the React component) is responsible for invoking it\n * on unmount.\n */\n attach(root: Pick<Root<unknown>, '__debug'>): () => void {\n return root.__debug.subscribe((event) => this.handle(event))\n }\n\n /** Apply one event. Exposed for tests. */\n handle(event: DebugEvent): void {\n switch (event.type) {\n case 'controller:constructed':\n this.tree$.set(insertNode(this.tree$.peek(), event.path, event.props))\n return\n case 'controller:suspended':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'suspended'))\n return\n case 'controller:resumed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'active'))\n return\n case 'controller:disposed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'disposed'))\n return\n case 'cache:subscribed':\n this.pushCache({\n kind: 'subscribed',\n queryKey: event.queryKey,\n subscriberPath: event.subscriberPath,\n })\n return\n case 'cache:fetch-start':\n this.pushCache({ kind: 'fetch-start', queryKey: event.queryKey })\n return\n case 'cache:fetch-success':\n this.pushCache({\n kind: 'fetch-success',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n })\n return\n case 'cache:fetch-error':\n this.pushCache({\n kind: 'fetch-error',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n error: event.error,\n })\n return\n case 'cache:invalidated':\n this.pushCache({ kind: 'invalidated', queryKey: event.queryKey })\n return\n case 'cache:gc':\n this.pushCache({ kind: 'gc', queryKey: event.queryKey })\n return\n case 'mutation:run': {\n this.mutationStarts.set(mutationKey(event.path, event.name), this.now())\n this.pushMutation({ kind: 'run', path: event.path, name: event.name, vars: event.vars })\n return\n }\n case 'mutation:success': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'success',\n path: event.path,\n name: event.name,\n result: event.result,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:error': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'error',\n path: event.path,\n name: event.name,\n error: event.error,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:rollback':\n this.pushMutation({ kind: 'rollback', path: event.path, name: event.name })\n return\n case 'field:validated':\n this.pushField({\n path: event.path,\n field: event.field,\n valid: event.valid,\n errors: event.errors,\n })\n return\n }\n }\n\n /** Clear every log. Tree state is preserved — the live tree is not a log. */\n clearLogs(): void {\n this.cache$.set([])\n this.mutations$.set([])\n this.fields$.set([])\n }\n\n // -----------------------------------------------------------------------\n // Internals\n // -----------------------------------------------------------------------\n\n private pushCache(entry: DistributiveOmit<CacheEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as CacheEntry\n this.cache$.set(appendBounded(this.cache$.peek(), full, this.maxEntries))\n }\n\n private pushMutation(entry: DistributiveOmit<MutationEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as MutationEntry\n this.mutations$.set(appendBounded(this.mutations$.peek(), full, this.maxEntries))\n }\n\n private pushField(entry: Omit<FieldEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as FieldEntry\n this.fields$.set(appendBounded(this.fields$.peek(), full, this.maxEntries))\n }\n\n private consumeStart(path: readonly string[], name: string | undefined): number | undefined {\n const key = mutationKey(path, name)\n const startedAt = this.mutationStarts.get(key)\n if (startedAt === undefined) return undefined\n this.mutationStarts.delete(key)\n return this.now() - startedAt\n }\n}\n\nfunction mutationKey(path: readonly string[], name: string | undefined): string {\n return `${path.join('>')}#${name ?? ''}`\n}\n\n/**\n * Distributes `Omit` over a discriminated union so each variant keeps its own\n * keys. The default `Omit<A | B, K>` collapses to the intersection of keys —\n * not what we want when constructing one variant at a time.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n// ---------------------------------------------------------------------------\n// Pure helpers — tested independently of the class.\n// ---------------------------------------------------------------------------\n\nfunction makeRoot(): ControllerNode {\n return { path: [], state: 'active', props: undefined, children: [] }\n}\n\nfunction appendBounded<T>(arr: readonly T[], item: T, max: number): T[] {\n const next = arr.length >= max ? arr.slice(arr.length - max + 1) : arr.slice()\n next.push(item)\n return next\n}\n\n/**\n * Insert (or update) a node at `path` inside the tree. Auto-creates any\n * missing intermediate ancestors as 'active' placeholders — needed if the\n * subscriber attached after the root was constructed.\n *\n * Returns a NEW tree object (immutable update).\n */\nexport function insertNode(\n root: ControllerNode,\n path: readonly string[],\n props: unknown,\n): ControllerNode {\n if (path.length === 0) {\n // The root controller's \"constructed\" event has path === ['root']\n // (one segment), not []. We never receive empty paths in practice, but\n // handle defensively.\n return { ...root, state: 'active', props }\n }\n return cloneWithUpsert(root, path, 0, props)\n}\n\nfunction cloneWithUpsert(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n props: unknown,\n): ControllerNode {\n if (depth === path.length) {\n return { ...node, state: 'active', props }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n const childPath = path.slice(0, depth + 1)\n if (idx === -1) {\n const newChild = cloneWithUpsert(\n { path: childPath, state: 'active', props: undefined, children: [] },\n path,\n depth + 1,\n props,\n )\n return { ...node, children: [...node.children, newChild] }\n }\n const existing = node.children[idx]!\n const updatedChild = cloneWithUpsert(existing, path, depth + 1, props)\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n\n/**\n * Set `state` on the node at `path`. If the node doesn't exist (out-of-order\n * event delivery), the tree is returned unchanged.\n */\nexport function setNodeState(\n root: ControllerNode,\n path: readonly string[],\n state: ControllerNode['state'],\n): ControllerNode {\n if (path.length === 0) {\n return { ...root, state }\n }\n return setStateAt(root, path, 0, state) ?? root\n}\n\nfunction setStateAt(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n state: ControllerNode['state'],\n): ControllerNode | null {\n if (depth === path.length) {\n return { ...node, state }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n if (idx === -1) return null\n const existing = node.children[idx]!\n const updatedChild = setStateAt(existing, path, depth + 1, state)\n if (updatedChild === null) return null\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n","/**\n * Inline CSS for the devtools panel. Scoped to the `.olas-devtools-*` class\n * prefix so it doesn't bleed into the host app. Honors `prefers-color-scheme`\n * and accepts host palette overrides via `--olas-*` custom properties.\n */\nexport const DEVTOOLS_CSS = `\n.olas-devtools {\n container-type: inline-size;\n --olas-bg: #ffffff;\n --olas-fg: #1f2330;\n --olas-muted: #6b7280;\n --olas-soft: #f5f6f9;\n --olas-soft-2: #eef0f4;\n --olas-accent: #4f46e5;\n --olas-accent-soft: rgba(79,70,229,0.10);\n --olas-success: #1e8a3d;\n --olas-success-soft: rgba(30,138,61,0.12);\n --olas-warn: #ad6800;\n --olas-warn-soft: rgba(173,104,0,0.12);\n --olas-error: #b8361f;\n --olas-error-soft: rgba(184,54,31,0.12);\n --olas-border: #e6e6ea;\n --olas-border-soft: #eeeef2;\n --olas-row-alt: #fafafc;\n\n /* JSON viewer colors */\n --olas-json-key: #7a4ab8;\n --olas-json-string: #1e8a3d;\n --olas-json-number: #b25400;\n --olas-json-boolean: #4f46e5;\n --olas-json-null: #9aa0aa;\n --olas-json-bracket: #6b7280;\n --olas-json-summary: #6b7280;\n\n font-family: -apple-system, BlinkMacSystemFont, \"Inter var\", \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12.5px;\n line-height: 1.55;\n color: var(--olas-fg);\n background: var(--olas-bg);\n border: 1px solid var(--olas-border);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 320px;\n overflow: hidden;\n box-sizing: border-box;\n}\n.olas-devtools *,\n.olas-devtools *::before,\n.olas-devtools *::after { box-sizing: border-box; }\n\n@media (prefers-color-scheme: dark) {\n .olas-devtools {\n --olas-bg: #15171e;\n --olas-fg: #e8ebf2;\n --olas-muted: #97a0b3;\n --olas-soft: #1d2029;\n --olas-soft-2: #232733;\n --olas-accent: #8b86f0;\n --olas-accent-soft: rgba(139,134,240,0.18);\n --olas-success: #4ade80;\n --olas-success-soft: rgba(74,222,128,0.16);\n --olas-warn: #f5b740;\n --olas-warn-soft: rgba(245,183,64,0.16);\n --olas-error: #ef6b53;\n --olas-error-soft: rgba(239,107,83,0.16);\n --olas-border: #2a2e3a;\n --olas-border-soft: #20232c;\n --olas-row-alt: #1a1d25;\n\n --olas-json-key: #c39bff;\n --olas-json-string: #7ee79d;\n --olas-json-number: #f5b740;\n --olas-json-boolean: #8b86f0;\n --olas-json-null: #7c8090;\n --olas-json-bracket: #97a0b3;\n --olas-json-summary: #97a0b3;\n }\n}\n\n/* ---- tabs ------------------------------------------------------------- */\n.olas-devtools-tabs {\n display: flex;\n align-items: center;\n gap: 1px;\n border-bottom: 1px solid var(--olas-border);\n background: var(--olas-soft);\n padding: 0 8px;\n flex-shrink: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.olas-devtools-tabs::-webkit-scrollbar { display: none; }\n.olas-devtools-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 10px 8px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n cursor: pointer;\n font: inherit;\n font-weight: 500;\n font-size: 12px;\n white-space: nowrap;\n flex-shrink: 0;\n transition: color 80ms, border-color 80ms;\n}\n.olas-devtools-tab-label-full { display: inline; }\n.olas-devtools-tab-label-short { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-tab { padding: 9px 8px 8px; font-size: 11.5px; gap: 4px; }\n .olas-devtools-tab-label-full { display: none; }\n .olas-devtools-tab-label-short { display: inline; }\n .olas-devtools-pause-text,\n .olas-devtools-clear-text { display: none; }\n}\n.olas-devtools-tab:hover { color: var(--olas-fg); }\n.olas-devtools-tab[aria-selected=\"true\"] {\n color: var(--olas-fg);\n border-bottom-color: var(--olas-accent);\n}\n.olas-devtools-tab[aria-selected=\"true\"] .olas-devtools-tab-count {\n background: var(--olas-accent-soft);\n color: var(--olas-accent);\n}\n.olas-devtools-tab-count {\n min-width: 18px;\n padding: 0 6px;\n height: 16px;\n border-radius: 999px;\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n color: var(--olas-muted);\n font-size: 10.5px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n}\n.olas-devtools-pause,\n.olas-devtools-clear {\n padding: 4px 10px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n font: inherit;\n font-size: 11.5px;\n align-self: center;\n}\n.olas-devtools-pause { margin-left: auto; }\n.olas-devtools-pause:hover,\n.olas-devtools-clear:hover {\n color: var(--olas-fg);\n background: color-mix(in oklch, var(--olas-bg) 60%, transparent);\n}\n.olas-devtools-pause-on { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-pause-on:hover { color: var(--olas-warn); }\n.olas-devtools-clear-icon { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-clear-text { display: none; }\n .olas-devtools-clear-icon { display: inline; }\n .olas-devtools-pause, .olas-devtools-clear { padding: 4px 8px; }\n}\n\n/* ---- filter ---------------------------------------------------------- */\n.olas-devtools-filter {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--olas-border-soft);\n background: var(--olas-bg);\n}\n.olas-devtools-filter input {\n flex: 1;\n padding: 5px 9px;\n border: 1px solid var(--olas-border);\n border-radius: 6px;\n background: var(--olas-soft);\n color: var(--olas-fg);\n font: inherit;\n font-size: 12px;\n outline: none;\n transition: border-color 80ms, box-shadow 80ms;\n}\n.olas-devtools-filter input:focus {\n border-color: var(--olas-accent);\n box-shadow: 0 0 0 2px var(--olas-accent-soft);\n}\n.olas-devtools-filter input::placeholder { color: var(--olas-muted); }\n.olas-devtools-filter button {\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n cursor: pointer;\n font: inherit;\n font-size: 13px;\n padding: 2px 6px;\n}\n.olas-devtools-filter button:hover { color: var(--olas-fg); }\n\n/* ---- body ------------------------------------------------------------ */\n.olas-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n/* ---- list rows ------------------------------------------------------- */\n.olas-devtools-list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.olas-devtools-list li {\n border-bottom: 1px solid var(--olas-border-soft);\n display: flex;\n flex-direction: column;\n}\n.olas-devtools-list li:last-child { border-bottom: none; }\n.olas-devtools-list li:hover { background: var(--olas-row-alt); }\n\n.olas-devtools-row-top {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n min-height: 32px;\n}\n.olas-devtools-row-clickable .olas-devtools-row-top { cursor: pointer; user-select: none; }\n\n.olas-devtools-target {\n flex: 1;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n word-break: break-word;\n min-width: 0;\n}\n.olas-devtools-target strong { font-weight: 600; color: var(--olas-accent); }\n.olas-devtools-time {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.olas-devtools-chevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--olas-muted);\n font-size: 12px;\n width: 16px;\n height: 16px;\n transition: transform 100ms;\n user-select: none;\n}\n.olas-devtools-chevron-open { transform: rotate(90deg); color: var(--olas-fg); }\n\n.olas-devtools-kind {\n color: var(--olas-accent);\n background: var(--olas-accent-soft);\n border-radius: 4px;\n padding: 1px 7px;\n font-size: 10.5px;\n font-weight: 600;\n letter-spacing: 0.02em;\n white-space: nowrap;\n line-height: 1.4;\n font-variant-numeric: tabular-nums;\n}\n.olas-devtools-kind-success { color: var(--olas-success); background: var(--olas-success-soft); }\n.olas-devtools-kind-error { color: var(--olas-error); background: var(--olas-error-soft); }\n.olas-devtools-kind-warn,\n.olas-devtools-kind-rollback { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-duration {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n background: var(--olas-soft);\n border-radius: 4px;\n padding: 1px 6px;\n white-space: nowrap;\n}\n\n.olas-devtools-payload {\n margin: 0 12px 10px;\n padding: 8px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n overflow-x: auto;\n}\n.olas-devtools-payload-inline {\n margin-top: -4px;\n padding: 4px 10px;\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n font-size: 11px;\n}\n.olas-devtools-payload-json { line-height: 1.5; }\n\n/* ---- JSON viewer ----------------------------------------------------- */\n.olas-devtools-json-row {\n display: block;\n padding-left: 14px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.olas-devtools-json-children {\n display: block;\n border-left: 1px dashed var(--olas-border);\n margin-left: 4px;\n}\n.olas-devtools-json-block {\n display: inline-flex;\n flex-direction: column;\n vertical-align: top;\n}\n.olas-devtools-json-key {\n color: var(--olas-json-key);\n margin-right: 6px;\n font-weight: 500;\n}\n.olas-devtools-json-index {\n color: var(--olas-json-bracket);\n margin-right: 6px;\n}\n.olas-devtools-json-string { color: var(--olas-json-string); }\n.olas-devtools-json-number { color: var(--olas-json-number); }\n.olas-devtools-json-boolean { color: var(--olas-json-boolean); }\n.olas-devtools-json-null { color: var(--olas-json-null); font-style: italic; }\n.olas-devtools-json-bracket { color: var(--olas-json-bracket); }\n.olas-devtools-json-error { color: var(--olas-error); }\n.olas-devtools-json-summary {\n color: var(--olas-json-summary);\n font-style: italic;\n margin: 0 4px;\n}\n.olas-devtools-json-toggle {\n display: inline-flex;\n align-items: center;\n gap: 0;\n background: transparent;\n border: 0;\n padding: 0;\n margin: 0;\n cursor: pointer;\n color: inherit;\n font: inherit;\n}\n.olas-devtools-json-toggle:hover { background: var(--olas-accent-soft); border-radius: 3px; }\n\n/* ---- empty state ---------------------------------------------------- */\n.olas-devtools-empty {\n padding: 36px 24px;\n text-align: center;\n color: var(--olas-muted);\n}\n.olas-devtools-empty-title {\n color: var(--olas-fg);\n font-weight: 600;\n font-size: 13px;\n margin-bottom: 4px;\n}\n.olas-devtools-empty-hint {\n font-size: 12px;\n max-width: 320px;\n margin: 0 auto;\n line-height: 1.55;\n}\n\n/* ---- tree ------------------------------------------------------------ */\n.olas-devtools-tree { padding: 10px 12px; }\n.olas-devtools-tree-node {\n padding: 1px 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n}\n.olas-devtools-tree-row {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 1px 0;\n}\n.olas-devtools-tree-name { color: var(--olas-fg); font-weight: 500; }\n.olas-devtools-tree-state-active,\n.olas-devtools-tree-state-suspended,\n.olas-devtools-tree-state-disposed {\n border-radius: 4px;\n padding: 0 6px;\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.olas-devtools-tree-state-active {\n color: var(--olas-success);\n background: var(--olas-success-soft);\n}\n.olas-devtools-tree-state-suspended {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n}\n.olas-devtools-tree-state-disposed {\n color: var(--olas-muted);\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n}\n.olas-devtools-tree-pending {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n border-radius: 4px;\n padding: 0 6px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.olas-devtools-tree-props-toggle {\n background: transparent;\n border: 0;\n cursor: pointer;\n font: inherit;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--olas-muted);\n padding: 0 4px;\n border-radius: 4px;\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n.olas-devtools-tree-props-toggle:hover {\n color: var(--olas-fg);\n background: var(--olas-soft);\n}\n.olas-devtools-tree-props {\n margin: 4px 0 6px 8px;\n padding: 6px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n overflow-x: auto;\n}\n.olas-devtools-tree-children {\n margin-left: 8px;\n border-left: 1px dashed var(--olas-border);\n padding-left: 10px;\n margin-top: 2px;\n}\n\n/* ---- floating window + launcher ------------------------------------- */\n.olas-devtools-launcher {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 2147483645;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 11px;\n background: #1f2330;\n color: #e8ebf2;\n border: 1px solid #2a2e3a;\n border-radius: 999px;\n box-shadow: 0 2px 6px rgba(0,0,0,0.15), 0 8px 24px rgba(0,0,0,0.18);\n cursor: pointer;\n font: inherit;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12px;\n font-weight: 500;\n}\n.olas-devtools-launcher:hover { filter: brightness(1.1); }\n.olas-devtools-launcher-active { box-shadow: 0 0 0 2px rgba(139,134,240,0.5), 0 8px 24px rgba(0,0,0,0.18); }\n.olas-devtools-launcher-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #4ade80;\n box-shadow: 0 0 6px rgba(74,222,128,0.7);\n}\n.olas-devtools-launcher-label { letter-spacing: 0.01em; }\n\n.olas-devtools-floating {\n position: fixed;\n z-index: 2147483646;\n display: flex;\n flex-direction: column;\n background: var(--olas-bg, #ffffff);\n color: var(--olas-fg, #1f2330);\n border: 1px solid var(--olas-border, #e6e6ea);\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.18), 0 24px 64px rgba(0,0,0,0.18);\n overflow: hidden;\n /* Inherit the panel's own CSS vars when DevtoolsPanel is mounted inside. */\n}\n@media (prefers-color-scheme: dark) {\n .olas-devtools-floating {\n background: #15171e;\n color: #e8ebf2;\n border-color: #2a2e3a;\n box-shadow: 0 8px 24px rgba(0,0,0,0.5), 0 24px 64px rgba(0,0,0,0.5);\n }\n}\n.olas-devtools-floating-header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 8px 0 10px;\n background: var(--olas-soft, #f5f6f9);\n border-bottom: 1px solid var(--olas-border, #e6e6ea);\n cursor: grab;\n user-select: none;\n flex-shrink: 0;\n}\n.olas-devtools-floating-header:active { cursor: grabbing; }\n.olas-devtools-floating-grip {\n color: var(--olas-muted, #6b7280);\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-title {\n flex: 1;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--olas-fg, #1f2330);\n letter-spacing: 0.01em;\n}\n.olas-devtools-floating-actions { display: inline-flex; gap: 2px; }\n.olas-devtools-floating-action {\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n border-radius: 4px;\n color: var(--olas-muted, #6b7280);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-action:hover {\n color: var(--olas-fg, #1f2330);\n background: color-mix(in oklch, currentColor 14%, transparent);\n}\n.olas-devtools-floating-body {\n flex: 1;\n min-height: 0;\n display: flex;\n}\n.olas-devtools-floating-body > .olas-devtools {\n flex: 1;\n border: 0;\n border-radius: 0;\n min-height: 0;\n}\n.olas-devtools-floating-resize {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 14px;\n height: 14px;\n cursor: nwse-resize;\n background:\n linear-gradient(135deg, transparent 0 7px, var(--olas-muted, #6b7280) 7px 8px, transparent 8px 10px,\n var(--olas-muted, #6b7280) 10px 11px, transparent 11px 100%);\n opacity: 0.6;\n}\n.olas-devtools-floating-resize:hover { opacity: 1; }\n`\n","import type { DebugCacheEntry, Root } from '@kontsedal/olas-core'\nimport { use } from '@kontsedal/olas-react'\nimport { type ReactElement, useEffect, useMemo, useRef, useState } from 'react'\nimport { formatPath, formatTime } from './format'\nimport { JsonView } from './JsonView'\nimport {\n type CacheEntry,\n type ControllerNode,\n DevtoolsStore,\n type FieldEntry,\n type MutationEntry,\n} from './store'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsTab = 'tree' | 'cache' | 'inspector' | 'mutations' | 'fields'\n\nexport type DevtoolsPanelProps = {\n /** The root to inspect. The panel subscribes to `root.__debug` on mount. */\n root: Pick<Root<unknown>, '__debug'>\n /** Initial tab. Default: `'tree'`. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. Default: 100. */\n maxEntries?: number\n /**\n * Persist filter state to the URL hash under this key. When set,\n * reloading the page restores filter + tab. Default: no persistence.\n */\n urlHashKey?: string\n /** How often (ms) to refresh the live cache inspector snapshot. Default 800. */\n inspectorPollMs?: number\n}\n\n/**\n * Drop-in devtools panel for an Olas root.\n *\n * Features:\n * - **Tree** populated from the snapshot replay on mount (no lost events).\n * - **Cache / Mutations / Fields** event logs in reverse chronological order.\n * - **Filter** field per tab — text-matches kind, path, name, payload.\n * - **Pause** toggle freezes the log without stopping ingestion.\n * - **Click a row** to expand its payload from a truncated preview to the full\n * JSON.\n * - **Mutation durations** — `run → success/error` pairing surfaces elapsed ms.\n *\n * Styled inline (no CSS import needed) and scoped to the `.olas-devtools-*`\n * class prefix. Hosts override the palette via `--olas-*` custom properties.\n * Spec §13.\n */\nexport function DevtoolsPanel(props: DevtoolsPanelProps): ReactElement {\n const { root, defaultTab = 'tree', maxEntries, urlHashKey, inspectorPollMs = 800 } = props\n const store = useMemo(\n () => new DevtoolsStore(maxEntries !== undefined ? { maxEntries } : undefined),\n [maxEntries],\n )\n useEffect(() => store.attach(root), [root, store])\n\n // Initial state read from URL hash if `urlHashKey` is set.\n const initial = useMemo(() => readUrlHash(urlHashKey, defaultTab), [urlHashKey, defaultTab])\n const [tab, setTab] = useState<DevtoolsTab>(initial.tab)\n const [paused, setPaused] = useState(false)\n // Filters are kept per-tab so switching back doesn't lose the query.\n const [filters, setFilters] = useState<Record<DevtoolsTab, string>>(initial.filters)\n const filter = filters[tab]\n const setFilter = (q: string) => setFilters((prev) => ({ ...prev, [tab]: q }))\n\n // Persist tab + filters back to the URL hash on every change.\n useEffect(() => {\n if (urlHashKey === undefined) return\n writeUrlHash(urlHashKey, { tab, filters })\n }, [urlHashKey, tab, filters])\n\n // Live cache inspector — polls `root.__debug.queryEntries()` periodically.\n // Polling is cheap (a single peek per entry) and bounded by inspectorPollMs;\n // only the Cache Inspector view reads this.\n const [cacheEntries, setCacheEntries] = useState<DebugCacheEntry[]>([])\n const rootRef = useRef(root)\n rootRef.current = root\n useEffect(() => {\n if (tab !== 'inspector') return\n const tick = () => setCacheEntries(rootRef.current.__debug.queryEntries())\n tick()\n const id = window.setInterval(tick, inspectorPollMs)\n return () => window.clearInterval(id)\n }, [tab, inspectorPollMs])\n\n const liveTree = use(store.tree$)\n const liveCache = use(store.cache$)\n const liveMutations = use(store.mutations$)\n const liveFields = use(store.fields$)\n\n // When paused, snapshot once and keep showing that frozen state.\n const [frozen, setFrozen] = useState<{\n tree: ControllerNode\n cache: CacheEntry[]\n mutations: MutationEntry[]\n fields: FieldEntry[]\n } | null>(null)\n useEffect(() => {\n if (paused) {\n setFrozen({\n tree: liveTree,\n cache: liveCache,\n mutations: liveMutations,\n fields: liveFields,\n })\n } else {\n setFrozen(null)\n }\n // We only re-snapshot when the toggle flips, not on every event.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [paused])\n\n const tree = frozen?.tree ?? liveTree\n const cache = frozen?.cache ?? liveCache\n const mutations = frozen?.mutations ?? liveMutations\n const fields = frozen?.fields ?? liveFields\n\n return (\n <div className=\"olas-devtools\" data-testid=\"olas-devtools\">\n <style>{DEVTOOLS_CSS}</style>\n <div className=\"olas-devtools-tabs\" role=\"tablist\">\n <Tab\n name=\"tree\"\n current={tab}\n setTab={setTab}\n label=\"Tree\"\n short=\"Tree\"\n count={countLiveControllers(liveTree)}\n />\n <Tab\n name=\"cache\"\n current={tab}\n setTab={setTab}\n label=\"Cache\"\n short=\"Cache\"\n count={liveCache.length}\n />\n <Tab\n name=\"inspector\"\n current={tab}\n setTab={setTab}\n label=\"Inspector\"\n short=\"Insp\"\n count={cacheEntries.length}\n />\n <Tab\n name=\"mutations\"\n current={tab}\n setTab={setTab}\n label=\"Mutations\"\n short=\"Mut\"\n count={liveMutations.length}\n />\n <Tab\n name=\"fields\"\n current={tab}\n setTab={setTab}\n label=\"Fields\"\n short=\"Fld\"\n count={liveFields.length}\n />\n <button\n type=\"button\"\n aria-pressed={paused}\n className={paused ? 'olas-devtools-pause olas-devtools-pause-on' : 'olas-devtools-pause'}\n onClick={() => setPaused(!paused)}\n title={paused ? 'Resume live updates' : 'Pause live updates'}\n >\n <span aria-hidden=\"true\">{paused ? '▶' : '⏸'}</span>\n <span className=\"olas-devtools-pause-text\">{paused ? ' Resume' : ' Pause'}</span>\n </button>\n <button\n className=\"olas-devtools-clear\"\n type=\"button\"\n onClick={() => store.clearLogs()}\n title=\"Clear logs\"\n >\n <span className=\"olas-devtools-clear-text\">Clear</span>\n <span className=\"olas-devtools-clear-icon\" aria-hidden=\"true\">\n ✕\n </span>\n </button>\n </div>\n\n {(tab === 'cache' || tab === 'inspector' || tab === 'mutations' || tab === 'fields') && (\n <div className=\"olas-devtools-filter\">\n <input\n type=\"search\"\n value={filter}\n placeholder={`Filter ${tab}…`}\n onChange={(e) => setFilter(e.target.value)}\n />\n {filter !== '' && (\n <button type=\"button\" onClick={() => setFilter('')} aria-label=\"Clear filter\">\n ✕\n </button>\n )}\n </div>\n )}\n\n <div className=\"olas-devtools-body\" role=\"tabpanel\">\n {tab === 'tree' && <TreeView tree={tree} mutations={liveMutations} />}\n {tab === 'cache' && <CacheView entries={cache} filter={filter} />}\n {tab === 'inspector' && <InspectorView entries={cacheEntries} filter={filter} />}\n {tab === 'mutations' && <MutationsView entries={mutations} filter={filter} />}\n {tab === 'fields' && <FieldsView entries={fields} filter={filter} />}\n </div>\n </div>\n )\n}\n\nfunction Tab(props: {\n name: DevtoolsTab\n current: DevtoolsTab\n setTab: (t: DevtoolsTab) => void\n label: string\n short: string\n count: number\n}): ReactElement {\n const selected = props.current === props.name\n return (\n <button\n role=\"tab\"\n type=\"button\"\n aria-selected={selected}\n title={props.label}\n className=\"olas-devtools-tab\"\n onClick={() => props.setTab(props.name)}\n >\n <span className=\"olas-devtools-tab-label-full\">{props.label}</span>\n <span className=\"olas-devtools-tab-label-short\" aria-hidden=\"true\">\n {props.short}\n </span>\n {props.count > 0 && (\n <span className=\"olas-devtools-tab-count\" aria-hidden=\"true\">\n {props.count}\n </span>\n )}\n </button>\n )\n}\n\nfunction countLiveControllers(node: ControllerNode): number {\n let total = node.state !== 'disposed' ? 1 : 0\n for (const c of node.children) total += countLiveControllers(c)\n return Math.max(total - 1, 0) // exclude the placeholder root wrapper\n}\n\n// ===========================================================================\n// Tree\n// ===========================================================================\n\nfunction TreeView({\n tree,\n mutations,\n}: {\n tree: ControllerNode\n mutations: MutationEntry[]\n}): ReactElement {\n if (tree.children.length === 0) {\n return <Empty title=\"No controllers yet\" hint=\"The root hasn't constructed any controllers.\" />\n }\n // Roll up pending-mutation counts per controller path. A \"pending\" mutation\n // is one whose last entry is `run` with no matching success/error for the\n // same (path, name).\n const pending = useMemo(() => rollupPending(mutations), [mutations])\n return (\n <div className=\"olas-devtools-tree\">\n {tree.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )\n}\n\nfunction rollupPending(entries: readonly MutationEntry[]): Map<string, number> {\n const inFlight = new Map<string, number>() // (path|name) → count\n const out = new Map<string, number>() // path → pending count\n for (const e of entries) {\n const key = `${e.path.join('>')}#${e.name ?? ''}`\n const pathKey = e.path.join('>')\n if (e.kind === 'run') {\n inFlight.set(key, (inFlight.get(key) ?? 0) + 1)\n out.set(pathKey, (out.get(pathKey) ?? 0) + 1)\n } else if (e.kind === 'success' || e.kind === 'error') {\n const n = inFlight.get(key) ?? 0\n if (n > 0) inFlight.set(key, n - 1)\n const p = out.get(pathKey) ?? 0\n if (p > 0) out.set(pathKey, p - 1)\n }\n }\n return out\n}\n\nfunction TreeNode({\n node,\n pending,\n}: {\n node: ControllerNode\n pending: Map<string, number>\n}): ReactElement {\n const name = node.path[node.path.length - 1] ?? '?'\n const stateClass =\n node.state === 'suspended'\n ? 'olas-devtools-tree-state-suspended'\n : node.state === 'disposed'\n ? 'olas-devtools-tree-state-disposed'\n : 'olas-devtools-tree-state-active'\n const pendingCount = pending.get(node.path.join('>')) ?? 0\n const propsPreview = useMemo(() => summarizeProps(node.props), [node.props])\n const [propsOpen, setPropsOpen] = useState(false)\n const canExpandProps = node.props !== undefined && node.props !== null\n\n return (\n <div className=\"olas-devtools-tree-node\">\n <span className=\"olas-devtools-tree-row\">\n <span className=\"olas-devtools-tree-name\">{name}</span>\n <span className={stateClass}>{node.state}</span>\n {pendingCount > 0 && (\n <span className=\"olas-devtools-tree-pending\" title=\"pending mutations on this controller\">\n {pendingCount} pending\n </span>\n )}\n {canExpandProps && (\n <button\n type=\"button\"\n className=\"olas-devtools-tree-props-toggle\"\n aria-expanded={propsOpen}\n onClick={() => setPropsOpen((v) => !v)}\n title={propsOpen ? 'Hide props' : 'Show full props'}\n >\n {propsPreview}\n </button>\n )}\n </span>\n {propsOpen && canExpandProps && (\n <div className=\"olas-devtools-tree-props\">\n <JsonView value={node.props} />\n </div>\n )}\n {node.children.length > 0 && (\n <div className=\"olas-devtools-tree-children\">\n {node.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )}\n </div>\n )\n}\n\n/** Build a one-line props summary for the tree row. */\nfunction summarizeProps(props: unknown): string {\n if (props === null || props === undefined) return ''\n if (typeof props === 'string') return `\"${truncate(props, 24)}\"`\n if (typeof props === 'number' || typeof props === 'boolean') return String(props)\n if (Array.isArray(props)) return `[${props.length}]`\n if (typeof props === 'object') {\n const keys = Object.keys(props as Record<string, unknown>)\n if (keys.length === 0) return '{}'\n const parts = keys.slice(0, 2).map((k) => {\n const v = (props as Record<string, unknown>)[k]\n return `${k}: ${shortValue(v)}`\n })\n return `{ ${parts.join(', ')}${keys.length > 2 ? `, +${keys.length - 2}` : ''} }`\n }\n return String(props)\n}\n\nfunction shortValue(v: unknown): string {\n if (v === null) return 'null'\n if (v === undefined) return 'undefined'\n if (typeof v === 'string') return `\"${truncate(v, 16)}\"`\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n if (Array.isArray(v)) return `[${v.length}]`\n if (typeof v === 'object') return `{${Object.keys(v as object).length}}`\n return String(v)\n}\n\nfunction truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}…`\n}\n\n// ===========================================================================\n// Cache Inspector — live state, not history\n// ===========================================================================\n\nfunction InspectorView({\n entries,\n filter,\n}: {\n entries: DebugCacheEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, inspectorHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No cache entries\"\n hint=\"Subscribe to a query somewhere in the tree to see its data.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {filtered.map((entry) => (\n <InspectorRow key={entry.key.join('|')} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction inspectorHaystack(e: DebugCacheEntry): string {\n return [...e.key.map(String), e.status, safeStringify(e.data)].join(' ')\n}\n\nfunction InspectorRow({ entry }: { entry: DebugCacheEntry }): ReactElement {\n const kindClass =\n entry.status === 'error'\n ? 'olas-devtools-kind-error'\n : entry.status === 'success'\n ? 'olas-devtools-kind-success'\n : entry.status === 'pending'\n ? 'olas-devtools-kind-warn'\n : ''\n const ageMs = entry.lastUpdatedAt != null ? Date.now() - entry.lastUpdatedAt : null\n const tags: string[] = []\n if (entry.isStale) tags.push('stale')\n if (entry.isFetching) tags.push('fetching')\n if (entry.hasPendingMutations) tags.push('optimistic')\n return (\n <Row\n kind={entry.status}\n kindClass={kindClass}\n target={formatPath(entry.key)}\n t={entry.lastUpdatedAt ?? Date.now()}\n payload={entry.error ?? entry.data}\n suffix={[ageMs != null ? `${formatAge(ageMs)} ago` : '—', ...tags].join(' · ')}\n />\n )\n}\n\nfunction safeStringify(v: unknown): string {\n try {\n return JSON.stringify(v) ?? ''\n } catch {\n return String(v)\n }\n}\n\nfunction formatAge(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`\n return `${Math.round(ms / 3_600_000)}h`\n}\n\n// ===========================================================================\n// URL-hash persistence\n// ===========================================================================\n\nfunction readUrlHash(\n key: string | undefined,\n defaultTab: DevtoolsTab,\n): { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> } {\n const empty = { tree: '', cache: '', inspector: '', mutations: '', fields: '' }\n if (key === undefined) return { tab: defaultTab, filters: empty }\n if (typeof window === 'undefined') return { tab: defaultTab, filters: empty }\n try {\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n const raw = params.get(key)\n if (raw === null) return { tab: defaultTab, filters: empty }\n const parsed = JSON.parse(decodeURIComponent(raw)) as {\n tab?: DevtoolsTab\n filters?: Partial<Record<DevtoolsTab, string>>\n }\n return {\n tab: parsed.tab ?? defaultTab,\n filters: { ...empty, ...(parsed.filters ?? {}) },\n }\n } catch {\n return { tab: defaultTab, filters: empty }\n }\n}\n\nfunction writeUrlHash(\n key: string,\n state: { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> },\n): void {\n if (typeof window === 'undefined') return\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n params.set(key, encodeURIComponent(JSON.stringify(state)))\n const next = `#${params.toString()}`\n if (next !== window.location.hash) {\n window.history.replaceState(null, '', next)\n }\n}\n\n// ===========================================================================\n// Cache\n// ===========================================================================\n\nfunction CacheView({ entries, filter }: { entries: CacheEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, cacheHaystack)\n if (entries.length === 0) {\n return (\n <Empty title=\"No cache events yet\" hint=\"Trigger a query subscription to see fetches here.\" />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <CacheRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction cacheHaystack(e: CacheEntry): string {\n const parts: string[] = [e.kind, ...e.queryKey.map((p) => String(p))]\n if (e.kind === 'fetch-error') parts.push(safeStringify(e.error))\n if (e.kind === 'subscribed') parts.push(...e.subscriberPath)\n return parts.join(' ')\n}\n\nfunction CacheRow({ entry }: { entry: CacheEntry }): ReactElement {\n const kindClass =\n entry.kind === 'fetch-error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'fetch-success'\n ? 'olas-devtools-kind-success'\n : entry.kind === 'invalidated' || entry.kind === 'gc'\n ? 'olas-devtools-kind-warn'\n : ''\n\n let inline: string | null = null\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'fetch-success') {\n suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'fetch-error') {\n suffix = `${entry.durationMs}ms`\n payload = entry.error\n } else if (entry.kind === 'subscribed') {\n inline = `from ${formatPath(entry.subscriberPath)}`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={formatPath(entry.queryKey)}\n t={entry.t}\n inline={inline}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Mutations\n// ===========================================================================\n\nfunction MutationsView({\n entries,\n filter,\n}: {\n entries: MutationEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, mutationHaystack)\n if (entries.length === 0) {\n return <Empty title=\"No mutations yet\" hint=\"Trigger a mutation to see the lifecycle here.\" />\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <MutationRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction mutationHaystack(e: MutationEntry): string {\n const parts: string[] = [e.kind, ...e.path, e.name ?? '']\n if (e.kind === 'run') parts.push(safeStringify(e.vars))\n if (e.kind === 'success') parts.push(safeStringify(e.result))\n if (e.kind === 'error') parts.push(safeStringify(e.error))\n return parts.join(' ')\n}\n\nfunction MutationRow({ entry }: { entry: MutationEntry }): ReactElement {\n const kindClass =\n entry.kind === 'error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'rollback'\n ? 'olas-devtools-kind-rollback'\n : entry.kind === 'success'\n ? 'olas-devtools-kind-success'\n : ''\n\n const target = entry.name ? `${entry.name} · ${formatPath(entry.path)}` : formatPath(entry.path)\n\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'run') payload = entry.vars\n else if (entry.kind === 'success') {\n payload = entry.result\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'error') {\n payload = entry.error\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={target}\n t={entry.t}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Fields\n// ===========================================================================\n\nfunction FieldsView({ entries, filter }: { entries: FieldEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, fieldHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No field validations yet\"\n hint=\"Type into a form bound via ctx.form(...) or ctx.field(...) — each pass lands here.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <FieldRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction fieldHaystack(e: FieldEntry): string {\n return [e.field, ...e.path, e.valid ? 'valid' : 'invalid', ...e.errors].join(' ')\n}\n\nfunction FieldRow({ entry }: { entry: FieldEntry }): ReactElement {\n const kindClass = entry.valid ? 'olas-devtools-kind-success' : 'olas-devtools-kind-error'\n return (\n <Row\n kind={entry.valid ? 'valid' : 'invalid'}\n kindClass={kindClass}\n target={`${formatPath(entry.path)} · ${entry.field}`}\n t={entry.t}\n inline={entry.errors.length > 0 ? entry.errors.join(' · ') : null}\n />\n )\n}\n\n// ===========================================================================\n// Shared row + helpers\n// ===========================================================================\n\ntype RowProps = {\n kind: string\n kindClass: string\n target: string\n t: number\n /** Either a tiny inline string (durations, urls) OR a structured payload. */\n inline?: string | null\n payload?: unknown\n suffix?: string | null\n}\n\nfunction Row(props: RowProps): ReactElement {\n const { kind, kindClass, target, t, inline, payload, suffix } = props\n const hasPayload = payload !== undefined\n const [expanded, setExpanded] = useState(false)\n const togglable = hasPayload\n\n return (\n <li className={togglable ? 'olas-devtools-row-clickable' : ''}>\n <div\n className=\"olas-devtools-row-top\"\n onClick={togglable ? () => setExpanded((v) => !v) : undefined}\n >\n <span className={`olas-devtools-kind ${kindClass}`}>{kind}</span>\n <span className=\"olas-devtools-target\">{target}</span>\n {suffix !== undefined && suffix !== null && (\n <span className=\"olas-devtools-duration\">{suffix}</span>\n )}\n <span className=\"olas-devtools-time\">{formatTime(t)}</span>\n {togglable && (\n <span\n aria-hidden=\"true\"\n className={`olas-devtools-chevron ${expanded ? 'olas-devtools-chevron-open' : ''}`}\n >\n ›\n </span>\n )}\n </div>\n {inline != null && (\n <div className=\"olas-devtools-payload olas-devtools-payload-inline\">{inline}</div>\n )}\n {hasPayload && expanded && (\n <div className=\"olas-devtools-payload olas-devtools-payload-json\">\n <JsonView value={payload} />\n </div>\n )}\n </li>\n )\n}\n\nfunction useFiltered<T>(items: readonly T[], filter: string, haystack: (item: T) => string): T[] {\n return useMemo(() => {\n if (filter.trim() === '') return [...items]\n const q = filter.toLowerCase()\n return items.filter((item) => haystack(item).toLowerCase().includes(q))\n }, [items, filter, haystack])\n}\n\nfunction Empty({ title, hint }: { title: string; hint: string }): ReactElement {\n return (\n <div className=\"olas-devtools-empty\">\n <div className=\"olas-devtools-empty-title\">{title}</div>\n <div className=\"olas-devtools-empty-hint\">{hint}</div>\n </div>\n )\n}\n","// Floating, draggable, resizable host for the DevtoolsPanel.\n//\n// Renders two things:\n// 1. A small bottom-right launcher button (always present).\n// 2. When open, a `position: fixed` window containing the panel. The header\n// is a drag handle; the south-east corner is a resize grip. Position +\n// size + open + minimized state persist to `localStorage`.\n//\n// API:\n// <DevtoolsLauncher root={root} />\n//\n// Optional props mirror DevtoolsPanel's. The launcher manages the window\n// chrome and ferries the rest through.\n\nimport type { Root } from '@kontsedal/olas-core'\nimport { type ReactElement, useCallback, useEffect, useRef, useState } from 'react'\nimport { DevtoolsPanel, type DevtoolsTab } from './DevtoolsPanel'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsLauncherProps = {\n root: Pick<Root<unknown>, '__debug'>\n /** Default panel tab. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. */\n maxEntries?: number\n /** Persist tab+filter state under this key (independent of window state). */\n urlHashKey?: string\n /** localStorage key for window state (position/size/open/minimized). */\n storageKey?: string\n /** Initial position if no persisted state. */\n initial?: { x?: number; y?: number; w?: number; h?: number }\n}\n\ntype WindowState = {\n x: number\n y: number\n w: number\n h: number\n open: boolean\n minimized: boolean\n}\n\nconst MIN_W = 360\nconst MIN_H = 280\nconst HEADER_H = 30\nconst DEFAULT_W = 520\nconst DEFAULT_H = 520\nconst MARGIN = 16\n\nexport function DevtoolsLauncher(props: DevtoolsLauncherProps): ReactElement {\n const storageKey = props.storageKey ?? 'olas-devtools-window'\n const [state, setState] = useState<WindowState>(() => loadState(storageKey, props.initial))\n\n // Persist on change.\n useEffect(() => {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(storageKey, JSON.stringify(state))\n } catch {\n /* swallow */\n }\n }, [state, storageKey])\n\n // Clamp to viewport on window resize so a previously-saved off-screen\n // position recovers gracefully.\n useEffect(() => {\n const onResize = () => setState((s) => clampToViewport(s))\n if (typeof window === 'undefined') return\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n const setOpen = useCallback((open: boolean) => setState((s) => ({ ...s, open })), [])\n const setMin = useCallback((minimized: boolean) => setState((s) => ({ ...s, minimized })), [])\n\n return (\n <>\n <style>{DEVTOOLS_CSS}</style>\n <LauncherButton open={state.open} onClick={() => setOpen(!state.open)} />\n {state.open && (\n <FloatingWindow\n state={state}\n setState={setState}\n onClose={() => setOpen(false)}\n onMinimize={() => setMin(!state.minimized)}\n >\n {!state.minimized && (\n <div className=\"olas-devtools-floating-body\">\n <DevtoolsPanel\n root={props.root}\n defaultTab={props.defaultTab}\n maxEntries={props.maxEntries}\n urlHashKey={props.urlHashKey}\n />\n </div>\n )}\n </FloatingWindow>\n )}\n </>\n )\n}\n\nfunction LauncherButton({ open, onClick }: { open: boolean; onClick: () => void }): ReactElement {\n return (\n <button\n type=\"button\"\n aria-label={open ? 'Hide Olas devtools' : 'Show Olas devtools'}\n onClick={onClick}\n className={`olas-devtools-launcher ${open ? 'olas-devtools-launcher-active' : ''}`}\n >\n <span aria-hidden=\"true\" className=\"olas-devtools-launcher-dot\" />\n <span className=\"olas-devtools-launcher-label\">Olas devtools</span>\n </button>\n )\n}\n\nfunction FloatingWindow(props: {\n state: WindowState\n setState: React.Dispatch<React.SetStateAction<WindowState>>\n onClose: () => void\n onMinimize: () => void\n children: ReactElement | ReactElement[] | false\n}): ReactElement {\n const { state, setState, onClose, onMinimize } = props\n const dragState = useRef<{\n kind: 'move' | 'resize'\n ox: number\n oy: number\n sx: number\n sy: number\n sw: number\n sh: number\n } | null>(null)\n\n const onPointerDown = (kind: 'move' | 'resize') => (e: React.PointerEvent) => {\n e.preventDefault()\n ;(e.target as Element).setPointerCapture(e.pointerId)\n dragState.current = {\n kind,\n ox: e.clientX,\n oy: e.clientY,\n sx: state.x,\n sy: state.y,\n sw: state.w,\n sh: state.h,\n }\n }\n const onPointerMove = (e: React.PointerEvent) => {\n const d = dragState.current\n if (d === null) return\n const dx = e.clientX - d.ox\n const dy = e.clientY - d.oy\n if (d.kind === 'move') {\n setState((s) => clampToViewport({ ...s, x: d.sx + dx, y: d.sy + dy }))\n } else {\n const w = Math.max(MIN_W, d.sw + dx)\n const h = Math.max(MIN_H, d.sh + dy)\n setState((s) => clampToViewport({ ...s, w, h }))\n }\n }\n const onPointerUp = () => {\n dragState.current = null\n }\n\n const minimized = state.minimized\n const height = minimized ? HEADER_H : state.h\n\n return (\n <div\n className=\"olas-devtools-floating\"\n role=\"dialog\"\n aria-label=\"Olas devtools\"\n style={{ left: state.x, top: state.y, width: state.w, height }}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"olas-devtools-floating-header\" onPointerDown={onPointerDown('move')}>\n <span className=\"olas-devtools-floating-grip\" aria-hidden=\"true\">\n ⠿\n </span>\n <span className=\"olas-devtools-floating-title\">Olas devtools</span>\n <div className=\"olas-devtools-floating-actions\">\n <button\n type=\"button\"\n aria-label={minimized ? 'Expand' : 'Minimize'}\n onClick={onMinimize}\n className=\"olas-devtools-floating-action\"\n >\n {minimized ? '▢' : '–'}\n </button>\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className=\"olas-devtools-floating-action\"\n >\n ×\n </button>\n </div>\n </div>\n {props.children}\n {!minimized && (\n <span\n className=\"olas-devtools-floating-resize\"\n onPointerDown={onPointerDown('resize')}\n aria-label=\"Resize\"\n role=\"separator\"\n />\n )}\n </div>\n )\n}\n\nfunction clampToViewport(s: WindowState): WindowState {\n if (typeof window === 'undefined') return s\n const vw = window.innerWidth\n const vh = window.innerHeight\n const w = Math.min(s.w, vw - MARGIN * 2)\n const h = Math.min(s.h, vh - MARGIN * 2)\n const x = Math.max(MARGIN, Math.min(s.x, vw - w - MARGIN))\n const y = Math.max(MARGIN, Math.min(s.y, vh - HEADER_H - MARGIN))\n return { ...s, x, y, w, h }\n}\n\nfunction loadState(\n storageKey: string,\n initial?: { x?: number; y?: number; w?: number; h?: number },\n): WindowState {\n const vw = typeof window !== 'undefined' ? window.innerWidth : 1024\n const vh = typeof window !== 'undefined' ? window.innerHeight : 768\n const w = initial?.w ?? DEFAULT_W\n const h = initial?.h ?? DEFAULT_H\n const defaults: WindowState = {\n x: initial?.x ?? Math.max(MARGIN, vw - w - MARGIN),\n y: initial?.y ?? Math.max(MARGIN, vh - h - MARGIN - 56),\n w,\n h,\n open: false,\n minimized: false,\n }\n if (typeof localStorage === 'undefined') return defaults\n try {\n const raw = localStorage.getItem(storageKey)\n if (!raw) return defaults\n const parsed = JSON.parse(raw) as Partial<WindowState>\n return clampToViewport({\n x: parsed.x ?? defaults.x,\n y: parsed.y ?? defaults.y,\n w: parsed.w ?? defaults.w,\n h: parsed.h ?? defaults.h,\n open: parsed.open ?? false,\n minimized: parsed.minimized ?? false,\n })\n } catch {\n return defaults\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/format.ts","../src/JsonView.tsx","../src/store.ts","../src/styles.ts","../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx"],"sourcesContent":["/**\n * Render a payload (props, vars, result, etc.) as a single-line string for\n * the panel. Cuts at `maxLen` so a giant blob doesn't blow up the layout.\n */\nexport function formatPayload(value: unknown, maxLen = 200): string {\n if (value === undefined) return 'undefined'\n if (value === null) return 'null'\n if (typeof value === 'function') return '[fn]'\n let s: string\n try {\n s = JSON.stringify(value, replaceUnserializable)\n } catch {\n s = String(value)\n }\n if (s === undefined) s = String(value)\n return s.length > maxLen ? `${s.slice(0, maxLen)}…` : s\n}\n\nfunction replaceUnserializable(_key: string, value: unknown): unknown {\n if (typeof value === 'function') return '[fn]'\n if (typeof value === 'bigint') return value.toString()\n if (value instanceof Error) return { name: value.name, message: value.message }\n return value\n}\n\n/** Render an HH:MM:SS.mmm timestamp from epoch ms. */\nexport function formatTime(t: number): string {\n const d = new Date(t)\n const pad = (n: number, w = 2) => n.toString().padStart(w, '0')\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`\n}\n\n/** Render a controller path / query key as a compact string. */\nexport function formatPath(path: readonly unknown[]): string {\n if (path.length === 0) return '∅'\n return path.map((p) => String(p)).join(' › ')\n}\n","// Compact JSON renderer for the devtools panel payload column.\n//\n// Renders values inline by default. Objects/arrays show their summary\n// (\"{4}\", \"[12]\") inline; clicking the summary expands them. Each nested\n// level inherits the same expand/collapse semantics. Scoped to the\n// `olas-devtools-json-*` class prefix; styles are in `styles.ts`.\n\nimport { type ReactElement, useState } from 'react'\n\nexport function JsonView({ value, depth = 0 }: { value: unknown; depth?: number }): ReactElement {\n return <Render value={value} depth={depth} initiallyOpen={depth === 0} />\n}\n\nfunction Render({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n if (value === null) return <span className=\"olas-devtools-json-null\">null</span>\n if (value === undefined) return <span className=\"olas-devtools-json-null\">undefined</span>\n\n const t = typeof value\n if (t === 'string') return <span className=\"olas-devtools-json-string\">\"{value as string}\"</span>\n if (t === 'number') return <span className=\"olas-devtools-json-number\">{String(value)}</span>\n if (t === 'boolean') return <span className=\"olas-devtools-json-boolean\">{String(value)}</span>\n if (t === 'bigint') return <span className=\"olas-devtools-json-number\">{String(value)}n</span>\n\n // Errors render as `Error(\"message\")` so they're distinguishable from\n // plain string payloads.\n if (value instanceof Error) {\n return (\n <span className=\"olas-devtools-json-error\">\n {value.name}({JSON.stringify(value.message)})\n </span>\n )\n }\n\n if (Array.isArray(value)) {\n return <CollapsibleArray value={value} depth={depth} initiallyOpen={initiallyOpen} />\n }\n\n if (t === 'object') {\n return (\n <CollapsibleObject\n value={value as Record<string, unknown>}\n depth={depth}\n initiallyOpen={initiallyOpen}\n />\n )\n }\n\n return <span>{String(value)}</span>\n}\n\nfunction CollapsibleArray({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown[]\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const [open, setOpen] = useState(initiallyOpen && value.length <= 12)\n if (value.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">[]</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">[</span>\n <span className=\"olas-devtools-json-summary\">\n {value.length} item{value.length === 1 ? '' : 's'}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">[</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {value.map((item, idx) => (\n <span key={idx} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-index\">{idx}:</span>\n <Render value={item} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </span>\n )\n}\n\nfunction CollapsibleObject({\n value,\n depth,\n initiallyOpen,\n}: {\n value: Record<string, unknown>\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const keys = Object.keys(value)\n const [open, setOpen] = useState(initiallyOpen && keys.length <= 8)\n if (keys.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">{'{}'}</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n <span className=\"olas-devtools-json-summary\">\n {keys.slice(0, 3).join(', ')}\n {keys.length > 3 ? ` +${keys.length - 3}` : ''}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {keys.map((k) => (\n <span key={k} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-key\">{k}:</span>\n <Render value={value[k]} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </span>\n )\n}\n","import type { DebugEvent, Root } from '@kontsedal/olas-core'\nimport { type Signal, signal } from '@kontsedal/olas-core'\n\n/**\n * Per-path node in the live controller tree. `state` reflects the most\n * recently observed lifecycle event; `path` is the array reported by the\n * devtools bus.\n */\nexport type ControllerNode = {\n readonly path: readonly string[]\n state: 'active' | 'suspended' | 'disposed'\n props: unknown\n children: ControllerNode[]\n}\n\n/** One entry in the cache timeline. */\nexport type CacheEntry =\n | {\n id: number\n t: number\n kind: 'subscribed'\n queryKey: readonly unknown[]\n subscriberPath: readonly string[]\n }\n | { id: number; t: number; kind: 'fetch-start'; queryKey: readonly unknown[] }\n | {\n id: number\n t: number\n kind: 'fetch-success'\n queryKey: readonly unknown[]\n durationMs: number\n }\n | {\n id: number\n t: number\n kind: 'fetch-error'\n queryKey: readonly unknown[]\n durationMs: number\n error: unknown\n }\n | { id: number; t: number; kind: 'invalidated'; queryKey: readonly unknown[] }\n | { id: number; t: number; kind: 'gc'; queryKey: readonly unknown[] }\n\n/** One entry in the mutation log. `durationMs` is set on success/error when\n * the entry can be paired with a preceding `run` for the same path+name. */\nexport type MutationEntry =\n | { id: number; t: number; kind: 'run'; path: readonly string[]; name?: string; vars: unknown }\n | {\n id: number\n t: number\n kind: 'success'\n path: readonly string[]\n name?: string\n result: unknown\n durationMs?: number\n }\n | {\n id: number\n t: number\n kind: 'error'\n path: readonly string[]\n name?: string\n error: unknown\n durationMs?: number\n }\n | { id: number; t: number; kind: 'rollback'; path: readonly string[]; name?: string }\n\n/** One entry in the field validation log. */\nexport type FieldEntry = {\n id: number\n t: number\n path: readonly string[]\n field: string\n valid: boolean\n errors: string[]\n}\n\n/** Defaults — exported so callers can override via `new DevtoolsStore({ maxEntries: 500 })`. */\nexport const DEFAULT_MAX_ENTRIES = 100\n\nexport type DevtoolsStoreOptions = {\n /** Cap on each event log (cache, mutation, field). Oldest entries drop first. */\n maxEntries?: number\n /** Optional clock — useful for tests. Default: `() => Date.now()`. */\n now?: () => number\n}\n\n/**\n * Subscribes to a root's `__debug` bus and maintains live state for the\n * devtools panel. Exposes signals so the React layer can consume via\n * `@kontsedal/olas-react`'s `use()`.\n *\n * Pure logic — no DOM, no React. Construct one per root.\n */\nexport class DevtoolsStore {\n readonly tree$: Signal<ControllerNode> = signal(makeRoot())\n readonly cache$: Signal<CacheEntry[]> = signal([])\n readonly mutations$: Signal<MutationEntry[]> = signal([])\n readonly fields$: Signal<FieldEntry[]> = signal([])\n\n private readonly maxEntries: number\n private readonly now: () => number\n private nextId = 1\n\n /** Keyed by `path|name` so a mutation:run can be paired with its\n * success/error to compute duration. Cleared after pairing. */\n private mutationStarts = new Map<string, number>()\n\n constructor(options?: DevtoolsStoreOptions) {\n this.maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES\n this.now = options?.now ?? (() => Date.now())\n }\n\n /**\n * Subscribe to the given root's debug bus. Returns the unsubscribe. The\n * caller (typically the React component) is responsible for invoking it\n * on unmount.\n */\n attach(root: Pick<Root<unknown>, '__debug'>): () => void {\n return root.__debug.subscribe((event) => this.handle(event))\n }\n\n /** Apply one event. Exposed for tests. */\n handle(event: DebugEvent): void {\n switch (event.type) {\n case 'controller:constructed':\n this.tree$.set(insertNode(this.tree$.peek(), event.path, event.props))\n return\n case 'controller:suspended':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'suspended'))\n return\n case 'controller:resumed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'active'))\n return\n case 'controller:disposed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'disposed'))\n // A controller that disposed mid-mutation (before `success`/`error`\n // ever fired) would otherwise leave its `mutation:run` start entry\n // in `mutationStarts` forever. Drop any starts under this path.\n this.dropStartsForPath(event.path)\n return\n case 'cache:subscribed':\n this.pushCache({\n kind: 'subscribed',\n queryKey: event.queryKey,\n subscriberPath: event.subscriberPath,\n })\n return\n case 'cache:fetch-start':\n this.pushCache({ kind: 'fetch-start', queryKey: event.queryKey })\n return\n case 'cache:fetch-success':\n this.pushCache({\n kind: 'fetch-success',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n })\n return\n case 'cache:fetch-error':\n this.pushCache({\n kind: 'fetch-error',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n error: event.error,\n })\n return\n case 'cache:invalidated':\n this.pushCache({ kind: 'invalidated', queryKey: event.queryKey })\n return\n case 'cache:gc':\n this.pushCache({ kind: 'gc', queryKey: event.queryKey })\n return\n case 'mutation:run': {\n this.mutationStarts.set(mutationKey(event.path, event.name), this.now())\n this.pushMutation({ kind: 'run', path: event.path, name: event.name, vars: event.vars })\n return\n }\n case 'mutation:success': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'success',\n path: event.path,\n name: event.name,\n result: event.result,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:error': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'error',\n path: event.path,\n name: event.name,\n error: event.error,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:rollback':\n this.pushMutation({ kind: 'rollback', path: event.path, name: event.name })\n return\n case 'field:validated':\n this.pushField({\n path: event.path,\n field: event.field,\n valid: event.valid,\n errors: event.errors,\n })\n return\n }\n }\n\n /** Clear every log. Tree state is preserved — the live tree is not a log. */\n clearLogs(): void {\n this.cache$.set([])\n this.mutations$.set([])\n this.fields$.set([])\n // Drop pending mutation-start timing records too — `clearLogs()` is the\n // user's \"start fresh\" gesture; any subsequent `success`/`error` for a\n // pre-clear `run` would have produced a duration anchored to noise.\n this.mutationStarts.clear()\n }\n\n // -----------------------------------------------------------------------\n // Internals\n // -----------------------------------------------------------------------\n\n private pushCache(entry: DistributiveOmit<CacheEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as CacheEntry\n this.cache$.set(appendBounded(this.cache$.peek(), full, this.maxEntries))\n }\n\n private pushMutation(entry: DistributiveOmit<MutationEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as MutationEntry\n this.mutations$.set(appendBounded(this.mutations$.peek(), full, this.maxEntries))\n }\n\n private pushField(entry: Omit<FieldEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as FieldEntry\n this.fields$.set(appendBounded(this.fields$.peek(), full, this.maxEntries))\n }\n\n private consumeStart(path: readonly string[], name: string | undefined): number | undefined {\n const key = mutationKey(path, name)\n const startedAt = this.mutationStarts.get(key)\n if (startedAt === undefined) return undefined\n this.mutationStarts.delete(key)\n return this.now() - startedAt\n }\n\n /**\n * Drop every pending mutation-start record under `path` (and its\n * descendants). Called on `controller:disposed` so a dispose mid-mutation\n * doesn't leave a permanent entry in `mutationStarts`.\n */\n private dropStartsForPath(path: readonly string[]): void {\n if (this.mutationStarts.size === 0) return\n const prefix = `${path.join('>')}>`\n const exact = path.join('>')\n for (const key of this.mutationStarts.keys()) {\n const beforeHash = key.split('#')[0] ?? ''\n if (beforeHash === exact || beforeHash.startsWith(prefix)) {\n this.mutationStarts.delete(key)\n }\n }\n }\n}\n\nfunction mutationKey(path: readonly string[], name: string | undefined): string {\n return `${path.join('>')}#${name ?? ''}`\n}\n\n/**\n * Distributes `Omit` over a discriminated union so each variant keeps its own\n * keys. The default `Omit<A | B, K>` collapses to the intersection of keys —\n * not what we want when constructing one variant at a time.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n// ---------------------------------------------------------------------------\n// Pure helpers — tested independently of the class.\n// ---------------------------------------------------------------------------\n\nfunction makeRoot(): ControllerNode {\n return { path: [], state: 'active', props: undefined, children: [] }\n}\n\nfunction appendBounded<T>(arr: readonly T[], item: T, max: number): T[] {\n const next = arr.length >= max ? arr.slice(arr.length - max + 1) : arr.slice()\n next.push(item)\n return next\n}\n\n/**\n * Insert (or update) a node at `path` inside the tree. Auto-creates any\n * missing intermediate ancestors as 'active' placeholders — needed if the\n * subscriber attached after the root was constructed.\n *\n * Returns a NEW tree object (immutable update).\n */\nexport function insertNode(\n root: ControllerNode,\n path: readonly string[],\n props: unknown,\n): ControllerNode {\n if (path.length === 0) {\n // The root controller's \"constructed\" event has path === ['root']\n // (one segment), not []. We never receive empty paths in practice, but\n // handle defensively.\n return { ...root, state: 'active', props }\n }\n return cloneWithUpsert(root, path, 0, props)\n}\n\nfunction cloneWithUpsert(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n props: unknown,\n): ControllerNode {\n if (depth === path.length) {\n return { ...node, state: 'active', props }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n const childPath = path.slice(0, depth + 1)\n if (idx === -1) {\n const newChild = cloneWithUpsert(\n { path: childPath, state: 'active', props: undefined, children: [] },\n path,\n depth + 1,\n props,\n )\n return { ...node, children: [...node.children, newChild] }\n }\n const existing = node.children[idx]!\n const updatedChild = cloneWithUpsert(existing, path, depth + 1, props)\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n\n/**\n * Set `state` on the node at `path`. If the node doesn't exist (out-of-order\n * event delivery), the tree is returned unchanged.\n */\nexport function setNodeState(\n root: ControllerNode,\n path: readonly string[],\n state: ControllerNode['state'],\n): ControllerNode {\n if (path.length === 0) {\n return { ...root, state }\n }\n return setStateAt(root, path, 0, state) ?? root\n}\n\nfunction setStateAt(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n state: ControllerNode['state'],\n): ControllerNode | null {\n if (depth === path.length) {\n return { ...node, state }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n if (idx === -1) return null\n const existing = node.children[idx]!\n const updatedChild = setStateAt(existing, path, depth + 1, state)\n if (updatedChild === null) return null\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n","/**\n * Inline CSS for the devtools panel. Scoped to the `.olas-devtools-*` class\n * prefix so it doesn't bleed into the host app. Honors `prefers-color-scheme`\n * and accepts host palette overrides via `--olas-*` custom properties.\n */\nexport const DEVTOOLS_CSS = `\n.olas-devtools {\n container-type: inline-size;\n --olas-bg: #ffffff;\n --olas-fg: #1f2330;\n --olas-muted: #6b7280;\n --olas-soft: #f5f6f9;\n --olas-soft-2: #eef0f4;\n --olas-accent: #4f46e5;\n --olas-accent-soft: rgba(79,70,229,0.10);\n --olas-success: #1e8a3d;\n --olas-success-soft: rgba(30,138,61,0.12);\n --olas-warn: #ad6800;\n --olas-warn-soft: rgba(173,104,0,0.12);\n --olas-error: #b8361f;\n --olas-error-soft: rgba(184,54,31,0.12);\n --olas-border: #e6e6ea;\n --olas-border-soft: #eeeef2;\n --olas-row-alt: #fafafc;\n\n /* JSON viewer colors */\n --olas-json-key: #7a4ab8;\n --olas-json-string: #1e8a3d;\n --olas-json-number: #b25400;\n --olas-json-boolean: #4f46e5;\n --olas-json-null: #9aa0aa;\n --olas-json-bracket: #6b7280;\n --olas-json-summary: #6b7280;\n\n font-family: -apple-system, BlinkMacSystemFont, \"Inter var\", \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12.5px;\n line-height: 1.55;\n color: var(--olas-fg);\n background: var(--olas-bg);\n border: 1px solid var(--olas-border);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 320px;\n overflow: hidden;\n box-sizing: border-box;\n}\n.olas-devtools *,\n.olas-devtools *::before,\n.olas-devtools *::after { box-sizing: border-box; }\n\n@media (prefers-color-scheme: dark) {\n .olas-devtools {\n --olas-bg: #15171e;\n --olas-fg: #e8ebf2;\n --olas-muted: #97a0b3;\n --olas-soft: #1d2029;\n --olas-soft-2: #232733;\n --olas-accent: #8b86f0;\n --olas-accent-soft: rgba(139,134,240,0.18);\n --olas-success: #4ade80;\n --olas-success-soft: rgba(74,222,128,0.16);\n --olas-warn: #f5b740;\n --olas-warn-soft: rgba(245,183,64,0.16);\n --olas-error: #ef6b53;\n --olas-error-soft: rgba(239,107,83,0.16);\n --olas-border: #2a2e3a;\n --olas-border-soft: #20232c;\n --olas-row-alt: #1a1d25;\n\n --olas-json-key: #c39bff;\n --olas-json-string: #7ee79d;\n --olas-json-number: #f5b740;\n --olas-json-boolean: #8b86f0;\n --olas-json-null: #7c8090;\n --olas-json-bracket: #97a0b3;\n --olas-json-summary: #97a0b3;\n }\n}\n\n/* ---- tabs ------------------------------------------------------------- */\n.olas-devtools-tabs {\n display: flex;\n align-items: center;\n gap: 1px;\n border-bottom: 1px solid var(--olas-border);\n background: var(--olas-soft);\n padding: 0 8px;\n flex-shrink: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.olas-devtools-tabs::-webkit-scrollbar { display: none; }\n.olas-devtools-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 10px 8px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n cursor: pointer;\n font: inherit;\n font-weight: 500;\n font-size: 12px;\n white-space: nowrap;\n flex-shrink: 0;\n transition: color 80ms, border-color 80ms;\n}\n.olas-devtools-tab-label-full { display: inline; }\n.olas-devtools-tab-label-short { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-tab { padding: 9px 8px 8px; font-size: 11.5px; gap: 4px; }\n .olas-devtools-tab-label-full { display: none; }\n .olas-devtools-tab-label-short { display: inline; }\n .olas-devtools-pause-text,\n .olas-devtools-clear-text { display: none; }\n}\n.olas-devtools-tab:hover { color: var(--olas-fg); }\n.olas-devtools-tab[aria-selected=\"true\"] {\n color: var(--olas-fg);\n border-bottom-color: var(--olas-accent);\n}\n.olas-devtools-tab[aria-selected=\"true\"] .olas-devtools-tab-count {\n background: var(--olas-accent-soft);\n color: var(--olas-accent);\n}\n.olas-devtools-tab-count {\n min-width: 18px;\n padding: 0 6px;\n height: 16px;\n border-radius: 999px;\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n color: var(--olas-muted);\n font-size: 10.5px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n}\n.olas-devtools-pause,\n.olas-devtools-clear {\n padding: 4px 10px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n font: inherit;\n font-size: 11.5px;\n align-self: center;\n}\n.olas-devtools-pause { margin-left: auto; }\n.olas-devtools-pause:hover,\n.olas-devtools-clear:hover {\n color: var(--olas-fg);\n background: color-mix(in oklch, var(--olas-bg) 60%, transparent);\n}\n.olas-devtools-pause-on { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-pause-on:hover { color: var(--olas-warn); }\n.olas-devtools-clear-icon { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-clear-text { display: none; }\n .olas-devtools-clear-icon { display: inline; }\n .olas-devtools-pause, .olas-devtools-clear { padding: 4px 8px; }\n}\n\n/* ---- filter ---------------------------------------------------------- */\n.olas-devtools-filter {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--olas-border-soft);\n background: var(--olas-bg);\n}\n.olas-devtools-filter input {\n flex: 1;\n padding: 5px 9px;\n border: 1px solid var(--olas-border);\n border-radius: 6px;\n background: var(--olas-soft);\n color: var(--olas-fg);\n font: inherit;\n font-size: 12px;\n outline: none;\n transition: border-color 80ms, box-shadow 80ms;\n}\n.olas-devtools-filter input:focus {\n border-color: var(--olas-accent);\n box-shadow: 0 0 0 2px var(--olas-accent-soft);\n}\n.olas-devtools-filter input::placeholder { color: var(--olas-muted); }\n.olas-devtools-filter button {\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n cursor: pointer;\n font: inherit;\n font-size: 13px;\n padding: 2px 6px;\n}\n.olas-devtools-filter button:hover { color: var(--olas-fg); }\n\n/* ---- body ------------------------------------------------------------ */\n.olas-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n/* ---- list rows ------------------------------------------------------- */\n.olas-devtools-list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.olas-devtools-list li {\n border-bottom: 1px solid var(--olas-border-soft);\n display: flex;\n flex-direction: column;\n}\n.olas-devtools-list li:last-child { border-bottom: none; }\n.olas-devtools-list li:hover { background: var(--olas-row-alt); }\n\n.olas-devtools-row-top {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n min-height: 32px;\n}\n.olas-devtools-row-clickable .olas-devtools-row-top { cursor: pointer; user-select: none; }\n\n.olas-devtools-target {\n flex: 1;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n word-break: break-word;\n min-width: 0;\n}\n.olas-devtools-target strong { font-weight: 600; color: var(--olas-accent); }\n.olas-devtools-time {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.olas-devtools-chevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--olas-muted);\n font-size: 12px;\n width: 16px;\n height: 16px;\n transition: transform 100ms;\n user-select: none;\n}\n.olas-devtools-chevron-open { transform: rotate(90deg); color: var(--olas-fg); }\n\n.olas-devtools-kind {\n color: var(--olas-accent);\n background: var(--olas-accent-soft);\n border-radius: 4px;\n padding: 1px 7px;\n font-size: 10.5px;\n font-weight: 600;\n letter-spacing: 0.02em;\n white-space: nowrap;\n line-height: 1.4;\n font-variant-numeric: tabular-nums;\n}\n.olas-devtools-kind-success { color: var(--olas-success); background: var(--olas-success-soft); }\n.olas-devtools-kind-error { color: var(--olas-error); background: var(--olas-error-soft); }\n.olas-devtools-kind-warn,\n.olas-devtools-kind-rollback { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-duration {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n background: var(--olas-soft);\n border-radius: 4px;\n padding: 1px 6px;\n white-space: nowrap;\n}\n\n.olas-devtools-payload {\n margin: 0 12px 10px;\n padding: 8px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n overflow-x: auto;\n}\n.olas-devtools-payload-inline {\n margin-top: -4px;\n padding: 4px 10px;\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n font-size: 11px;\n}\n.olas-devtools-payload-json { line-height: 1.5; }\n\n/* ---- JSON viewer ----------------------------------------------------- */\n.olas-devtools-json-row {\n display: block;\n padding-left: 14px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.olas-devtools-json-children {\n display: block;\n border-left: 1px dashed var(--olas-border);\n margin-left: 4px;\n}\n.olas-devtools-json-block {\n display: inline-flex;\n flex-direction: column;\n vertical-align: top;\n}\n.olas-devtools-json-key {\n color: var(--olas-json-key);\n margin-right: 6px;\n font-weight: 500;\n}\n.olas-devtools-json-index {\n color: var(--olas-json-bracket);\n margin-right: 6px;\n}\n.olas-devtools-json-string { color: var(--olas-json-string); }\n.olas-devtools-json-number { color: var(--olas-json-number); }\n.olas-devtools-json-boolean { color: var(--olas-json-boolean); }\n.olas-devtools-json-null { color: var(--olas-json-null); font-style: italic; }\n.olas-devtools-json-bracket { color: var(--olas-json-bracket); }\n.olas-devtools-json-error { color: var(--olas-error); }\n.olas-devtools-json-summary {\n color: var(--olas-json-summary);\n font-style: italic;\n margin: 0 4px;\n}\n.olas-devtools-json-toggle {\n display: inline-flex;\n align-items: center;\n gap: 0;\n background: transparent;\n border: 0;\n padding: 0;\n margin: 0;\n cursor: pointer;\n color: inherit;\n font: inherit;\n}\n.olas-devtools-json-toggle:hover { background: var(--olas-accent-soft); border-radius: 3px; }\n\n/* ---- empty state ---------------------------------------------------- */\n.olas-devtools-empty {\n padding: 36px 24px;\n text-align: center;\n color: var(--olas-muted);\n}\n.olas-devtools-empty-title {\n color: var(--olas-fg);\n font-weight: 600;\n font-size: 13px;\n margin-bottom: 4px;\n}\n.olas-devtools-empty-hint {\n font-size: 12px;\n max-width: 320px;\n margin: 0 auto;\n line-height: 1.55;\n}\n\n/* ---- tree ------------------------------------------------------------ */\n.olas-devtools-tree { padding: 10px 12px; }\n.olas-devtools-tree-node {\n padding: 1px 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n}\n.olas-devtools-tree-row {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 1px 0;\n}\n.olas-devtools-tree-name { color: var(--olas-fg); font-weight: 500; }\n.olas-devtools-tree-state-active,\n.olas-devtools-tree-state-suspended,\n.olas-devtools-tree-state-disposed {\n border-radius: 4px;\n padding: 0 6px;\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.olas-devtools-tree-state-active {\n color: var(--olas-success);\n background: var(--olas-success-soft);\n}\n.olas-devtools-tree-state-suspended {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n}\n.olas-devtools-tree-state-disposed {\n color: var(--olas-muted);\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n}\n.olas-devtools-tree-pending {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n border-radius: 4px;\n padding: 0 6px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.olas-devtools-tree-props-toggle {\n background: transparent;\n border: 0;\n cursor: pointer;\n font: inherit;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--olas-muted);\n padding: 0 4px;\n border-radius: 4px;\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n.olas-devtools-tree-props-toggle:hover {\n color: var(--olas-fg);\n background: var(--olas-soft);\n}\n.olas-devtools-tree-props {\n margin: 4px 0 6px 8px;\n padding: 6px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n overflow-x: auto;\n}\n.olas-devtools-tree-children {\n margin-left: 8px;\n border-left: 1px dashed var(--olas-border);\n padding-left: 10px;\n margin-top: 2px;\n}\n\n/* ---- floating window + launcher ------------------------------------- */\n.olas-devtools-launcher {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 2147483645;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 11px;\n background: #1f2330;\n color: #e8ebf2;\n border: 1px solid #2a2e3a;\n border-radius: 999px;\n box-shadow: 0 2px 6px rgba(0,0,0,0.15), 0 8px 24px rgba(0,0,0,0.18);\n cursor: pointer;\n font: inherit;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12px;\n font-weight: 500;\n}\n.olas-devtools-launcher:hover { filter: brightness(1.1); }\n.olas-devtools-launcher-active { box-shadow: 0 0 0 2px rgba(139,134,240,0.5), 0 8px 24px rgba(0,0,0,0.18); }\n.olas-devtools-launcher-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #4ade80;\n box-shadow: 0 0 6px rgba(74,222,128,0.7);\n}\n.olas-devtools-launcher-label { letter-spacing: 0.01em; }\n\n.olas-devtools-floating {\n position: fixed;\n z-index: 2147483646;\n display: flex;\n flex-direction: column;\n background: var(--olas-bg, #ffffff);\n color: var(--olas-fg, #1f2330);\n border: 1px solid var(--olas-border, #e6e6ea);\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.18), 0 24px 64px rgba(0,0,0,0.18);\n overflow: hidden;\n /* Inherit the panel's own CSS vars when DevtoolsPanel is mounted inside. */\n}\n@media (prefers-color-scheme: dark) {\n .olas-devtools-floating {\n background: #15171e;\n color: #e8ebf2;\n border-color: #2a2e3a;\n box-shadow: 0 8px 24px rgba(0,0,0,0.5), 0 24px 64px rgba(0,0,0,0.5);\n }\n}\n.olas-devtools-floating-header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 8px 0 10px;\n background: var(--olas-soft, #f5f6f9);\n border-bottom: 1px solid var(--olas-border, #e6e6ea);\n cursor: grab;\n user-select: none;\n flex-shrink: 0;\n}\n.olas-devtools-floating-header:active { cursor: grabbing; }\n.olas-devtools-floating-grip {\n color: var(--olas-muted, #6b7280);\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-title {\n flex: 1;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--olas-fg, #1f2330);\n letter-spacing: 0.01em;\n}\n.olas-devtools-floating-actions { display: inline-flex; gap: 2px; }\n.olas-devtools-floating-action {\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n border-radius: 4px;\n color: var(--olas-muted, #6b7280);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-action:hover {\n color: var(--olas-fg, #1f2330);\n background: color-mix(in oklch, currentColor 14%, transparent);\n}\n.olas-devtools-floating-body {\n flex: 1;\n min-height: 0;\n display: flex;\n}\n.olas-devtools-floating-body > .olas-devtools {\n flex: 1;\n border: 0;\n border-radius: 0;\n min-height: 0;\n}\n.olas-devtools-floating-resize {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 14px;\n height: 14px;\n cursor: nwse-resize;\n background:\n linear-gradient(135deg, transparent 0 7px, var(--olas-muted, #6b7280) 7px 8px, transparent 8px 10px,\n var(--olas-muted, #6b7280) 10px 11px, transparent 11px 100%);\n opacity: 0.6;\n}\n.olas-devtools-floating-resize:hover { opacity: 1; }\n`\n","import type { DebugCacheEntry, Root } from '@kontsedal/olas-core'\nimport { use } from '@kontsedal/olas-react'\nimport { type ReactElement, useEffect, useMemo, useRef, useState } from 'react'\nimport { formatPath, formatTime } from './format'\nimport { JsonView } from './JsonView'\nimport {\n type CacheEntry,\n type ControllerNode,\n DevtoolsStore,\n type FieldEntry,\n type MutationEntry,\n} from './store'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsTab = 'tree' | 'cache' | 'inspector' | 'mutations' | 'fields'\n\nexport type DevtoolsPanelProps = {\n /** The root to inspect. The panel subscribes to `root.__debug` on mount. */\n root: Pick<Root<unknown>, '__debug'>\n /** Initial tab. Default: `'tree'`. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. Default: 100. */\n maxEntries?: number\n /**\n * Persist filter state to the URL hash under this key. When set,\n * reloading the page restores filter + tab. Default: no persistence.\n */\n urlHashKey?: string\n /** How often (ms) to refresh the live cache inspector snapshot. Default 800. */\n inspectorPollMs?: number\n}\n\n/**\n * Drop-in devtools panel for an Olas root.\n *\n * Features:\n * - **Tree** populated from the snapshot replay on mount (no lost events).\n * - **Cache / Mutations / Fields** event logs in reverse chronological order.\n * - **Filter** field per tab — text-matches kind, path, name, payload.\n * - **Pause** toggle freezes the log without stopping ingestion.\n * - **Click a row** to expand its payload from a truncated preview to the full\n * JSON.\n * - **Mutation durations** — `run → success/error` pairing surfaces elapsed ms.\n *\n * Styled inline (no CSS import needed) and scoped to the `.olas-devtools-*`\n * class prefix. Hosts override the palette via `--olas-*` custom properties.\n * Spec §13.\n */\nexport function DevtoolsPanel(props: DevtoolsPanelProps): ReactElement {\n const { root, defaultTab = 'tree', maxEntries, urlHashKey, inspectorPollMs = 800 } = props\n const store = useMemo(\n () => new DevtoolsStore(maxEntries !== undefined ? { maxEntries } : undefined),\n [maxEntries],\n )\n useEffect(() => store.attach(root), [root, store])\n\n // Initial state read from URL hash if `urlHashKey` is set.\n const initial = useMemo(() => readUrlHash(urlHashKey, defaultTab), [urlHashKey, defaultTab])\n const [tab, setTab] = useState<DevtoolsTab>(initial.tab)\n const [paused, setPaused] = useState(false)\n // Filters are kept per-tab so switching back doesn't lose the query.\n const [filters, setFilters] = useState<Record<DevtoolsTab, string>>(initial.filters)\n const filter = filters[tab]\n const setFilter = (q: string) => setFilters((prev) => ({ ...prev, [tab]: q }))\n\n // Persist tab + filters back to the URL hash on every change.\n useEffect(() => {\n if (urlHashKey === undefined) return\n writeUrlHash(urlHashKey, { tab, filters })\n }, [urlHashKey, tab, filters])\n\n // Live cache inspector — polls `root.__debug.queryEntries()` periodically.\n // Polling is cheap (a single peek per entry) and bounded by inspectorPollMs;\n // only the Cache Inspector view reads this.\n const [cacheEntries, setCacheEntries] = useState<DebugCacheEntry[]>([])\n const rootRef = useRef(root)\n rootRef.current = root\n useEffect(() => {\n if (tab !== 'inspector') return\n const tick = () => setCacheEntries(rootRef.current.__debug.queryEntries())\n tick()\n const id = window.setInterval(tick, inspectorPollMs)\n return () => window.clearInterval(id)\n }, [tab, inspectorPollMs])\n\n const liveTree = use(store.tree$)\n const liveCache = use(store.cache$)\n const liveMutations = use(store.mutations$)\n const liveFields = use(store.fields$)\n\n // When paused, snapshot once and keep showing that frozen state.\n const [frozen, setFrozen] = useState<{\n tree: ControllerNode\n cache: CacheEntry[]\n mutations: MutationEntry[]\n fields: FieldEntry[]\n } | null>(null)\n useEffect(() => {\n if (paused) {\n setFrozen({\n tree: liveTree,\n cache: liveCache,\n mutations: liveMutations,\n fields: liveFields,\n })\n } else {\n setFrozen(null)\n }\n // We only re-snapshot when the toggle flips, not on every event.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [paused])\n\n const tree = frozen?.tree ?? liveTree\n const cache = frozen?.cache ?? liveCache\n const mutations = frozen?.mutations ?? liveMutations\n const fields = frozen?.fields ?? liveFields\n\n return (\n <div className=\"olas-devtools\" data-testid=\"olas-devtools\">\n <style>{DEVTOOLS_CSS}</style>\n <div className=\"olas-devtools-tabs\" role=\"tablist\">\n <Tab\n name=\"tree\"\n current={tab}\n setTab={setTab}\n label=\"Tree\"\n short=\"Tree\"\n count={countLiveControllers(liveTree)}\n />\n <Tab\n name=\"cache\"\n current={tab}\n setTab={setTab}\n label=\"Cache\"\n short=\"Cache\"\n count={liveCache.length}\n />\n <Tab\n name=\"inspector\"\n current={tab}\n setTab={setTab}\n label=\"Inspector\"\n short=\"Insp\"\n count={cacheEntries.length}\n />\n <Tab\n name=\"mutations\"\n current={tab}\n setTab={setTab}\n label=\"Mutations\"\n short=\"Mut\"\n count={liveMutations.length}\n />\n <Tab\n name=\"fields\"\n current={tab}\n setTab={setTab}\n label=\"Fields\"\n short=\"Fld\"\n count={liveFields.length}\n />\n <button\n type=\"button\"\n aria-pressed={paused}\n className={paused ? 'olas-devtools-pause olas-devtools-pause-on' : 'olas-devtools-pause'}\n onClick={() => setPaused(!paused)}\n title={paused ? 'Resume live updates' : 'Pause live updates'}\n >\n <span aria-hidden=\"true\">{paused ? '▶' : '⏸'}</span>\n <span className=\"olas-devtools-pause-text\">{paused ? ' Resume' : ' Pause'}</span>\n </button>\n <button\n className=\"olas-devtools-clear\"\n type=\"button\"\n onClick={() => store.clearLogs()}\n title=\"Clear logs\"\n >\n <span className=\"olas-devtools-clear-text\">Clear</span>\n <span className=\"olas-devtools-clear-icon\" aria-hidden=\"true\">\n ✕\n </span>\n </button>\n </div>\n\n {(tab === 'cache' || tab === 'inspector' || tab === 'mutations' || tab === 'fields') && (\n <div className=\"olas-devtools-filter\">\n <input\n type=\"search\"\n value={filter}\n placeholder={`Filter ${tab}…`}\n onChange={(e) => setFilter(e.target.value)}\n />\n {filter !== '' && (\n <button type=\"button\" onClick={() => setFilter('')} aria-label=\"Clear filter\">\n ✕\n </button>\n )}\n </div>\n )}\n\n <div className=\"olas-devtools-body\" role=\"tabpanel\">\n {tab === 'tree' && <TreeView tree={tree} mutations={liveMutations} />}\n {tab === 'cache' && <CacheView entries={cache} filter={filter} />}\n {tab === 'inspector' && <InspectorView entries={cacheEntries} filter={filter} />}\n {tab === 'mutations' && <MutationsView entries={mutations} filter={filter} />}\n {tab === 'fields' && <FieldsView entries={fields} filter={filter} />}\n </div>\n </div>\n )\n}\n\nfunction Tab(props: {\n name: DevtoolsTab\n current: DevtoolsTab\n setTab: (t: DevtoolsTab) => void\n label: string\n short: string\n count: number\n}): ReactElement {\n const selected = props.current === props.name\n return (\n <button\n role=\"tab\"\n type=\"button\"\n aria-selected={selected}\n title={props.label}\n className=\"olas-devtools-tab\"\n onClick={() => props.setTab(props.name)}\n >\n <span className=\"olas-devtools-tab-label-full\">{props.label}</span>\n <span className=\"olas-devtools-tab-label-short\" aria-hidden=\"true\">\n {props.short}\n </span>\n {props.count > 0 && (\n <span className=\"olas-devtools-tab-count\" aria-hidden=\"true\">\n {props.count}\n </span>\n )}\n </button>\n )\n}\n\nfunction countLiveControllers(node: ControllerNode): number {\n let total = node.state !== 'disposed' ? 1 : 0\n for (const c of node.children) total += countLiveControllers(c)\n return Math.max(total - 1, 0) // exclude the placeholder root wrapper\n}\n\n// ===========================================================================\n// Tree\n// ===========================================================================\n\nfunction TreeView({\n tree,\n mutations,\n}: {\n tree: ControllerNode\n mutations: MutationEntry[]\n}): ReactElement {\n if (tree.children.length === 0) {\n return <Empty title=\"No controllers yet\" hint=\"The root hasn't constructed any controllers.\" />\n }\n // Roll up pending-mutation counts per controller path. A \"pending\" mutation\n // is one whose last entry is `run` with no matching success/error for the\n // same (path, name).\n const pending = useMemo(() => rollupPending(mutations), [mutations])\n return (\n <div className=\"olas-devtools-tree\">\n {tree.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )\n}\n\nfunction rollupPending(entries: readonly MutationEntry[]): Map<string, number> {\n const inFlight = new Map<string, number>() // (path|name) → count\n const out = new Map<string, number>() // path → pending count\n for (const e of entries) {\n const key = `${e.path.join('>')}#${e.name ?? ''}`\n const pathKey = e.path.join('>')\n if (e.kind === 'run') {\n inFlight.set(key, (inFlight.get(key) ?? 0) + 1)\n out.set(pathKey, (out.get(pathKey) ?? 0) + 1)\n } else if (e.kind === 'success' || e.kind === 'error') {\n const n = inFlight.get(key) ?? 0\n if (n > 0) inFlight.set(key, n - 1)\n const p = out.get(pathKey) ?? 0\n if (p > 0) out.set(pathKey, p - 1)\n }\n }\n return out\n}\n\nfunction TreeNode({\n node,\n pending,\n}: {\n node: ControllerNode\n pending: Map<string, number>\n}): ReactElement {\n const name = node.path[node.path.length - 1] ?? '?'\n const stateClass =\n node.state === 'suspended'\n ? 'olas-devtools-tree-state-suspended'\n : node.state === 'disposed'\n ? 'olas-devtools-tree-state-disposed'\n : 'olas-devtools-tree-state-active'\n const pendingCount = pending.get(node.path.join('>')) ?? 0\n const propsPreview = useMemo(() => summarizeProps(node.props), [node.props])\n const [propsOpen, setPropsOpen] = useState(false)\n const canExpandProps = node.props !== undefined && node.props !== null\n\n return (\n <div className=\"olas-devtools-tree-node\">\n <span className=\"olas-devtools-tree-row\">\n <span className=\"olas-devtools-tree-name\">{name}</span>\n <span className={stateClass}>{node.state}</span>\n {pendingCount > 0 && (\n <span className=\"olas-devtools-tree-pending\" title=\"pending mutations on this controller\">\n {pendingCount} pending\n </span>\n )}\n {canExpandProps && (\n <button\n type=\"button\"\n className=\"olas-devtools-tree-props-toggle\"\n aria-expanded={propsOpen}\n onClick={() => setPropsOpen((v) => !v)}\n title={propsOpen ? 'Hide props' : 'Show full props'}\n >\n {propsPreview}\n </button>\n )}\n </span>\n {propsOpen && canExpandProps && (\n <div className=\"olas-devtools-tree-props\">\n <JsonView value={node.props} />\n </div>\n )}\n {node.children.length > 0 && (\n <div className=\"olas-devtools-tree-children\">\n {node.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )}\n </div>\n )\n}\n\n/** Build a one-line props summary for the tree row. */\nfunction summarizeProps(props: unknown): string {\n if (props === null || props === undefined) return ''\n if (typeof props === 'string') return `\"${truncate(props, 24)}\"`\n if (typeof props === 'number' || typeof props === 'boolean') return String(props)\n if (Array.isArray(props)) return `[${props.length}]`\n if (typeof props === 'object') {\n const keys = Object.keys(props as Record<string, unknown>)\n if (keys.length === 0) return '{}'\n const parts = keys.slice(0, 2).map((k) => {\n const v = (props as Record<string, unknown>)[k]\n return `${k}: ${shortValue(v)}`\n })\n return `{ ${parts.join(', ')}${keys.length > 2 ? `, +${keys.length - 2}` : ''} }`\n }\n return String(props)\n}\n\nfunction shortValue(v: unknown): string {\n if (v === null) return 'null'\n if (v === undefined) return 'undefined'\n if (typeof v === 'string') return `\"${truncate(v, 16)}\"`\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n if (Array.isArray(v)) return `[${v.length}]`\n if (typeof v === 'object') return `{${Object.keys(v as object).length}}`\n return String(v)\n}\n\nfunction truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}…`\n}\n\n// ===========================================================================\n// Cache Inspector — live state, not history\n// ===========================================================================\n\nfunction InspectorView({\n entries,\n filter,\n}: {\n entries: DebugCacheEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, inspectorHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No cache entries\"\n hint=\"Subscribe to a query somewhere in the tree to see its data.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {filtered.map((entry) => (\n <InspectorRow key={entry.key.join('|')} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction inspectorHaystack(e: DebugCacheEntry): string {\n return [...e.key.map(String), e.status, safeStringify(e.data)].join(' ')\n}\n\nfunction InspectorRow({ entry }: { entry: DebugCacheEntry }): ReactElement {\n const kindClass =\n entry.status === 'error'\n ? 'olas-devtools-kind-error'\n : entry.status === 'success'\n ? 'olas-devtools-kind-success'\n : entry.status === 'pending'\n ? 'olas-devtools-kind-warn'\n : ''\n const ageMs = entry.lastUpdatedAt != null ? Date.now() - entry.lastUpdatedAt : null\n const tags: string[] = []\n if (entry.isStale) tags.push('stale')\n if (entry.isFetching) tags.push('fetching')\n if (entry.hasPendingMutations) tags.push('optimistic')\n return (\n <Row\n kind={entry.status}\n kindClass={kindClass}\n target={formatPath(entry.key)}\n t={entry.lastUpdatedAt ?? Date.now()}\n payload={entry.error ?? entry.data}\n suffix={[ageMs != null ? `${formatAge(ageMs)} ago` : '—', ...tags].join(' · ')}\n />\n )\n}\n\nfunction safeStringify(v: unknown): string {\n try {\n return JSON.stringify(v) ?? ''\n } catch {\n return String(v)\n }\n}\n\nfunction formatAge(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`\n return `${Math.round(ms / 3_600_000)}h`\n}\n\n// ===========================================================================\n// URL-hash persistence\n// ===========================================================================\n\nfunction readUrlHash(\n key: string | undefined,\n defaultTab: DevtoolsTab,\n): { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> } {\n const empty = { tree: '', cache: '', inspector: '', mutations: '', fields: '' }\n if (key === undefined) return { tab: defaultTab, filters: empty }\n if (typeof window === 'undefined') return { tab: defaultTab, filters: empty }\n try {\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n const raw = params.get(key)\n if (raw === null) return { tab: defaultTab, filters: empty }\n const parsed = JSON.parse(decodeURIComponent(raw)) as {\n tab?: DevtoolsTab\n filters?: Partial<Record<DevtoolsTab, string>>\n }\n return {\n tab: parsed.tab ?? defaultTab,\n filters: { ...empty, ...(parsed.filters ?? {}) },\n }\n } catch {\n return { tab: defaultTab, filters: empty }\n }\n}\n\nfunction writeUrlHash(\n key: string,\n state: { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> },\n): void {\n if (typeof window === 'undefined') return\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n params.set(key, encodeURIComponent(JSON.stringify(state)))\n const next = `#${params.toString()}`\n if (next !== window.location.hash) {\n window.history.replaceState(null, '', next)\n }\n}\n\n// ===========================================================================\n// Cache\n// ===========================================================================\n\nfunction CacheView({ entries, filter }: { entries: CacheEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, cacheHaystack)\n if (entries.length === 0) {\n return (\n <Empty title=\"No cache events yet\" hint=\"Trigger a query subscription to see fetches here.\" />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <CacheRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction cacheHaystack(e: CacheEntry): string {\n const parts: string[] = [e.kind, ...e.queryKey.map((p) => String(p))]\n if (e.kind === 'fetch-error') parts.push(safeStringify(e.error))\n if (e.kind === 'subscribed') parts.push(...e.subscriberPath)\n return parts.join(' ')\n}\n\nfunction CacheRow({ entry }: { entry: CacheEntry }): ReactElement {\n const kindClass =\n entry.kind === 'fetch-error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'fetch-success'\n ? 'olas-devtools-kind-success'\n : entry.kind === 'invalidated' || entry.kind === 'gc'\n ? 'olas-devtools-kind-warn'\n : ''\n\n let inline: string | null = null\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'fetch-success') {\n suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'fetch-error') {\n suffix = `${entry.durationMs}ms`\n payload = entry.error\n } else if (entry.kind === 'subscribed') {\n inline = `from ${formatPath(entry.subscriberPath)}`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={formatPath(entry.queryKey)}\n t={entry.t}\n inline={inline}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Mutations\n// ===========================================================================\n\nfunction MutationsView({\n entries,\n filter,\n}: {\n entries: MutationEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, mutationHaystack)\n if (entries.length === 0) {\n return <Empty title=\"No mutations yet\" hint=\"Trigger a mutation to see the lifecycle here.\" />\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <MutationRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction mutationHaystack(e: MutationEntry): string {\n const parts: string[] = [e.kind, ...e.path, e.name ?? '']\n if (e.kind === 'run') parts.push(safeStringify(e.vars))\n if (e.kind === 'success') parts.push(safeStringify(e.result))\n if (e.kind === 'error') parts.push(safeStringify(e.error))\n return parts.join(' ')\n}\n\nfunction MutationRow({ entry }: { entry: MutationEntry }): ReactElement {\n const kindClass =\n entry.kind === 'error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'rollback'\n ? 'olas-devtools-kind-rollback'\n : entry.kind === 'success'\n ? 'olas-devtools-kind-success'\n : ''\n\n const target = entry.name ? `${entry.name} · ${formatPath(entry.path)}` : formatPath(entry.path)\n\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'run') payload = entry.vars\n else if (entry.kind === 'success') {\n payload = entry.result\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'error') {\n payload = entry.error\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={target}\n t={entry.t}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Fields\n// ===========================================================================\n\nfunction FieldsView({ entries, filter }: { entries: FieldEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, fieldHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No field validations yet\"\n hint=\"Type into a form bound via ctx.form(...) or ctx.field(...) — each pass lands here.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <FieldRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction fieldHaystack(e: FieldEntry): string {\n return [e.field, ...e.path, e.valid ? 'valid' : 'invalid', ...e.errors].join(' ')\n}\n\nfunction FieldRow({ entry }: { entry: FieldEntry }): ReactElement {\n const kindClass = entry.valid ? 'olas-devtools-kind-success' : 'olas-devtools-kind-error'\n return (\n <Row\n kind={entry.valid ? 'valid' : 'invalid'}\n kindClass={kindClass}\n target={`${formatPath(entry.path)} · ${entry.field}`}\n t={entry.t}\n inline={entry.errors.length > 0 ? entry.errors.join(' · ') : null}\n />\n )\n}\n\n// ===========================================================================\n// Shared row + helpers\n// ===========================================================================\n\ntype RowProps = {\n kind: string\n kindClass: string\n target: string\n t: number\n /** Either a tiny inline string (durations, urls) OR a structured payload. */\n inline?: string | null\n payload?: unknown\n suffix?: string | null\n}\n\nfunction Row(props: RowProps): ReactElement {\n const { kind, kindClass, target, t, inline, payload, suffix } = props\n const hasPayload = payload !== undefined\n const [expanded, setExpanded] = useState(false)\n const togglable = hasPayload\n\n return (\n <li className={togglable ? 'olas-devtools-row-clickable' : ''}>\n <div\n className=\"olas-devtools-row-top\"\n onClick={togglable ? () => setExpanded((v) => !v) : undefined}\n >\n <span className={`olas-devtools-kind ${kindClass}`}>{kind}</span>\n <span className=\"olas-devtools-target\">{target}</span>\n {suffix !== undefined && suffix !== null && (\n <span className=\"olas-devtools-duration\">{suffix}</span>\n )}\n <span className=\"olas-devtools-time\">{formatTime(t)}</span>\n {togglable && (\n <span\n aria-hidden=\"true\"\n className={`olas-devtools-chevron ${expanded ? 'olas-devtools-chevron-open' : ''}`}\n >\n ›\n </span>\n )}\n </div>\n {inline != null && (\n <div className=\"olas-devtools-payload olas-devtools-payload-inline\">{inline}</div>\n )}\n {hasPayload && expanded && (\n <div className=\"olas-devtools-payload olas-devtools-payload-json\">\n <JsonView value={payload} />\n </div>\n )}\n </li>\n )\n}\n\nfunction useFiltered<T>(items: readonly T[], filter: string, haystack: (item: T) => string): T[] {\n return useMemo(() => {\n if (filter.trim() === '') return [...items]\n const q = filter.toLowerCase()\n return items.filter((item) => haystack(item).toLowerCase().includes(q))\n }, [items, filter, haystack])\n}\n\nfunction Empty({ title, hint }: { title: string; hint: string }): ReactElement {\n return (\n <div className=\"olas-devtools-empty\">\n <div className=\"olas-devtools-empty-title\">{title}</div>\n <div className=\"olas-devtools-empty-hint\">{hint}</div>\n </div>\n )\n}\n","// Floating, draggable, resizable host for the DevtoolsPanel.\n//\n// Renders two things:\n// 1. A small bottom-right launcher button (always present).\n// 2. When open, a `position: fixed` window containing the panel. The header\n// is a drag handle; the south-east corner is a resize grip. Position +\n// size + open + minimized state persist to `localStorage`.\n//\n// API:\n// <DevtoolsLauncher root={root} />\n//\n// Optional props mirror DevtoolsPanel's. The launcher manages the window\n// chrome and ferries the rest through.\n\nimport type { Root } from '@kontsedal/olas-core'\nimport { type ReactElement, useCallback, useEffect, useRef, useState } from 'react'\nimport { DevtoolsPanel, type DevtoolsTab } from './DevtoolsPanel'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsLauncherProps = {\n root: Pick<Root<unknown>, '__debug'>\n /** Default panel tab. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. */\n maxEntries?: number\n /** Persist tab+filter state under this key (independent of window state). */\n urlHashKey?: string\n /** localStorage key for window state (position/size/open/minimized). */\n storageKey?: string\n /** Initial position if no persisted state. */\n initial?: { x?: number; y?: number; w?: number; h?: number }\n}\n\ntype WindowState = {\n x: number\n y: number\n w: number\n h: number\n open: boolean\n minimized: boolean\n}\n\nconst MIN_W = 360\nconst MIN_H = 280\nconst HEADER_H = 30\nconst DEFAULT_W = 520\nconst DEFAULT_H = 520\nconst MARGIN = 16\n\nexport function DevtoolsLauncher(props: DevtoolsLauncherProps): ReactElement {\n const storageKey = props.storageKey ?? 'olas-devtools-window'\n const [state, setState] = useState<WindowState>(() => loadState(storageKey, props.initial))\n\n // Persist on change.\n useEffect(() => {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(storageKey, JSON.stringify(state))\n } catch {\n /* swallow */\n }\n }, [state, storageKey])\n\n // Clamp to viewport on window resize so a previously-saved off-screen\n // position recovers gracefully.\n useEffect(() => {\n const onResize = () => setState((s) => clampToViewport(s))\n if (typeof window === 'undefined') return\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n const setOpen = useCallback((open: boolean) => setState((s) => ({ ...s, open })), [])\n const setMin = useCallback((minimized: boolean) => setState((s) => ({ ...s, minimized })), [])\n\n return (\n <>\n <style>{DEVTOOLS_CSS}</style>\n <LauncherButton open={state.open} onClick={() => setOpen(!state.open)} />\n {state.open && (\n <FloatingWindow\n state={state}\n setState={setState}\n onClose={() => setOpen(false)}\n onMinimize={() => setMin(!state.minimized)}\n >\n {!state.minimized && (\n <div className=\"olas-devtools-floating-body\">\n <DevtoolsPanel\n root={props.root}\n defaultTab={props.defaultTab}\n maxEntries={props.maxEntries}\n urlHashKey={props.urlHashKey}\n />\n </div>\n )}\n </FloatingWindow>\n )}\n </>\n )\n}\n\nfunction LauncherButton({ open, onClick }: { open: boolean; onClick: () => void }): ReactElement {\n return (\n <button\n type=\"button\"\n aria-label={open ? 'Hide Olas devtools' : 'Show Olas devtools'}\n onClick={onClick}\n className={`olas-devtools-launcher ${open ? 'olas-devtools-launcher-active' : ''}`}\n >\n <span aria-hidden=\"true\" className=\"olas-devtools-launcher-dot\" />\n <span className=\"olas-devtools-launcher-label\">Olas devtools</span>\n </button>\n )\n}\n\nfunction FloatingWindow(props: {\n state: WindowState\n setState: React.Dispatch<React.SetStateAction<WindowState>>\n onClose: () => void\n onMinimize: () => void\n children: ReactElement | ReactElement[] | false\n}): ReactElement {\n const { state, setState, onClose, onMinimize } = props\n const dragState = useRef<{\n kind: 'move' | 'resize'\n ox: number\n oy: number\n sx: number\n sy: number\n sw: number\n sh: number\n } | null>(null)\n\n const onPointerDown = (kind: 'move' | 'resize') => (e: React.PointerEvent) => {\n e.preventDefault()\n ;(e.target as Element).setPointerCapture(e.pointerId)\n dragState.current = {\n kind,\n ox: e.clientX,\n oy: e.clientY,\n sx: state.x,\n sy: state.y,\n sw: state.w,\n sh: state.h,\n }\n }\n const onPointerMove = (e: React.PointerEvent) => {\n const d = dragState.current\n if (d === null) return\n const dx = e.clientX - d.ox\n const dy = e.clientY - d.oy\n if (d.kind === 'move') {\n setState((s) => clampToViewport({ ...s, x: d.sx + dx, y: d.sy + dy }))\n } else {\n const w = Math.max(MIN_W, d.sw + dx)\n const h = Math.max(MIN_H, d.sh + dy)\n setState((s) => clampToViewport({ ...s, w, h }))\n }\n }\n const onPointerUp = () => {\n dragState.current = null\n }\n\n const minimized = state.minimized\n const height = minimized ? HEADER_H : state.h\n\n return (\n <div\n className=\"olas-devtools-floating\"\n role=\"dialog\"\n aria-label=\"Olas devtools\"\n style={{ left: state.x, top: state.y, width: state.w, height }}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"olas-devtools-floating-header\" onPointerDown={onPointerDown('move')}>\n <span className=\"olas-devtools-floating-grip\" aria-hidden=\"true\">\n ⠿\n </span>\n <span className=\"olas-devtools-floating-title\">Olas devtools</span>\n <div className=\"olas-devtools-floating-actions\">\n <button\n type=\"button\"\n aria-label={minimized ? 'Expand' : 'Minimize'}\n onClick={onMinimize}\n className=\"olas-devtools-floating-action\"\n >\n {minimized ? '▢' : '–'}\n </button>\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className=\"olas-devtools-floating-action\"\n >\n ×\n </button>\n </div>\n </div>\n {props.children}\n {!minimized && (\n <span\n className=\"olas-devtools-floating-resize\"\n onPointerDown={onPointerDown('resize')}\n aria-label=\"Resize\"\n role=\"separator\"\n />\n )}\n </div>\n )\n}\n\nfunction clampToViewport(s: WindowState): WindowState {\n if (typeof window === 'undefined') return s\n const vw = window.innerWidth\n const vh = window.innerHeight\n const w = Math.min(s.w, vw - MARGIN * 2)\n const h = Math.min(s.h, vh - MARGIN * 2)\n const x = Math.max(MARGIN, Math.min(s.x, vw - w - MARGIN))\n const y = Math.max(MARGIN, Math.min(s.y, vh - HEADER_H - MARGIN))\n return { ...s, x, y, w, h }\n}\n\nfunction loadState(\n storageKey: string,\n initial?: { x?: number; y?: number; w?: number; h?: number },\n): WindowState {\n const vw = typeof window !== 'undefined' ? window.innerWidth : 1024\n const vh = typeof window !== 'undefined' ? window.innerHeight : 768\n const w = initial?.w ?? DEFAULT_W\n const h = initial?.h ?? DEFAULT_H\n const defaults: WindowState = {\n x: initial?.x ?? Math.max(MARGIN, vw - w - MARGIN),\n y: initial?.y ?? Math.max(MARGIN, vh - h - MARGIN - 56),\n w,\n h,\n open: false,\n minimized: false,\n }\n if (typeof localStorage === 'undefined') return defaults\n try {\n const raw = localStorage.getItem(storageKey)\n if (!raw) return defaults\n const parsed = JSON.parse(raw) as Partial<WindowState>\n return clampToViewport({\n x: parsed.x ?? defaults.x,\n y: parsed.y ?? defaults.y,\n w: parsed.w ?? defaults.w,\n h: parsed.h ?? defaults.h,\n open: parsed.open ?? false,\n minimized: parsed.minimized ?? false,\n })\n } catch {\n return defaults\n }\n}\n"],"mappings":";;;;;;;;;;AAIA,SAAgB,cAAc,OAAgB,SAAS,KAAa;CAClE,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,OAAO,UAAU,YAAY,OAAO;CACxC,IAAI;CACJ,IAAI;EACF,IAAI,KAAK,UAAU,OAAO,qBAAqB;CACjD,QAAQ;EACN,IAAI,OAAO,KAAK;CAClB;CACA,IAAI,MAAM,KAAA,GAAW,IAAI,OAAO,KAAK;CACrC,OAAO,EAAE,SAAS,SAAS,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK;AACxD;AAEA,SAAS,sBAAsB,MAAc,OAAyB;CACpE,IAAI,OAAO,UAAU,YAAY,OAAO;CACxC,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS;CACrD,IAAI,iBAAiB,OAAO,OAAO;EAAE,MAAM,MAAM;EAAM,SAAS,MAAM;CAAQ;CAC9E,OAAO;AACT;;AAGA,SAAgB,WAAW,GAAmB;CAC5C,MAAM,IAAI,IAAI,KAAK,CAAC;CACpB,MAAM,OAAO,GAAW,IAAI,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;CAC9D,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,CAAC,EAAE,GAAG,IAAI,EAAE,gBAAgB,GAAG,CAAC;AACzG;;AAGA,SAAgB,WAAW,MAAkC;CAC3D,IAAI,KAAK,WAAW,GAAG,OAAO;CAC9B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK,KAAK;AAC9C;;;AC3BA,SAAgB,SAAS,EAAE,OAAO,QAAQ,KAAuD;CAC/F,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAe;EAAc;EAAO,eAAe,UAAU;CAAI,CAAA;AAC1E;AAEA,SAAS,OAAO,EACd,OACA,OACA,iBAKe;CACf,IAAI,UAAU,MAAM,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAM,WAAU;YAA0B;CAAU,CAAA;CAC/E,IAAI,UAAU,KAAA,GAAW,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAM,WAAU;YAA0B;CAAe,CAAA;CAEzF,MAAM,IAAI,OAAO;CACjB,IAAI,MAAM,UAAU,OAAO,iBAAA,GAAA,kBAAA,MAAC,QAAD;EAAM,WAAU;YAAhB;GAA4C;GAAE;GAAgB;EAAO;;CAChG,IAAI,MAAM,UAAU,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAM,WAAU;YAA6B,OAAO,KAAK;CAAQ,CAAA;CAC5F,IAAI,MAAM,WAAW,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAM,WAAU;YAA8B,OAAO,KAAK;CAAQ,CAAA;CAC9F,IAAI,MAAM,UAAU,OAAO,iBAAA,GAAA,kBAAA,MAAC,QAAD;EAAM,WAAU;YAAhB,CAA6C,OAAO,KAAK,GAAE,GAAO;;CAI7F,IAAI,iBAAiB,OACnB,OACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;EAAM,WAAU;YAAhB;GACG,MAAM;GAAK;GAAE,KAAK,UAAU,MAAM,OAAO;GAAE;EACxC;;CAIV,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,iBAAA,GAAA,kBAAA,KAAC,kBAAD;EAAyB;EAAc;EAAsB;CAAgB,CAAA;CAGtF,IAAI,MAAM,UACR,OACE,iBAAA,GAAA,kBAAA,KAAC,mBAAD;EACS;EACA;EACQ;CAChB,CAAA;CAIL,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD,EAAA,UAAO,OAAO,KAAK,EAAQ,CAAA;AACpC;AAEA,SAAS,iBAAiB,EACxB,OACA,OACA,iBAKe;CACf,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,UAAoB,iBAAiB,MAAM,UAAU,EAAE;CACpE,IAAI,MAAM,WAAW,GACnB,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAM,WAAU;YAA6B;CAAQ,CAAA;CAE9D,IAAI,CAAC,MACH,OACE,iBAAA,GAAA,kBAAA,MAAC,UAAD;EAAQ,MAAK;EAAS,WAAU;EAA4B,eAAe,QAAQ,IAAI;YAAvF;GACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cAA6B;GAAO,CAAA;GACpD,iBAAA,GAAA,kBAAA,MAAC,QAAD;IAAM,WAAU;cAAhB;KACG,MAAM;KAAO;KAAM,MAAM,WAAW,IAAI,KAAK;IAC1C;;GACN,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cAA6B;GAAO,CAAA;EAC9C;;CAGZ,OACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;EAAM,WAAU;YAAhB;GACE,iBAAA,GAAA,kBAAA,KAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,eAAe,QAAQ,KAAK;cAE5B,iBAAA,GAAA,kBAAA,KAAC,QAAD;KAAM,WAAU;eAA6B;IAAO,CAAA;GAC9C,CAAA;GACR,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cACb,MAAM,KAAK,MAAM,QAChB,iBAAA,GAAA,kBAAA,MAAC,QAAD;KAAgB,WAAU;eAA1B,CACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;MAAM,WAAU;gBAAhB,CAA4C,KAAI,GAAO;SACvD,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAQ,OAAO;MAAM,OAAO,QAAQ;MAAG,eAAe;KAAQ,CAAA,CAC1D;OAHK,GAGL,CACP;GACG,CAAA;GACN,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cAA6B;GAAO,CAAA;EAChD;;AAEV;AAEA,SAAS,kBAAkB,EACzB,OACA,OACA,iBAKe;CACf,MAAM,OAAO,OAAO,KAAK,KAAK;CAC9B,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,UAAoB,iBAAiB,KAAK,UAAU,CAAC;CAClE,IAAI,KAAK,WAAW,GAClB,OAAO,iBAAA,GAAA,kBAAA,KAAC,QAAD;EAAM,WAAU;YAA8B;CAAW,CAAA;CAElE,IAAI,CAAC,MACH,OACE,iBAAA,GAAA,kBAAA,MAAC,UAAD;EAAQ,MAAK;EAAS,WAAU;EAA4B,eAAe,QAAQ,IAAI;YAAvF;GACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cAA8B;GAAU,CAAA;GACxD,iBAAA,GAAA,kBAAA,MAAC,QAAD;IAAM,WAAU;cAAhB,CACG,KAAK,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,GAC1B,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,MAAM,EACxC;;GACN,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cAA8B;GAAU,CAAA;EAClD;;CAGZ,OACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;EAAM,WAAU;YAAhB;GACE,iBAAA,GAAA,kBAAA,KAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,eAAe,QAAQ,KAAK;cAE5B,iBAAA,GAAA,kBAAA,KAAC,QAAD;KAAM,WAAU;eAA8B;IAAU,CAAA;GAClD,CAAA;GACR,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cACb,KAAK,KAAK,MACT,iBAAA,GAAA,kBAAA,MAAC,QAAD;KAAc,WAAU;eAAxB,CACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;MAAM,WAAU;gBAAhB,CAA0C,GAAE,GAAO;SACnD,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAQ,OAAO,MAAM;MAAI,OAAO,QAAQ;MAAG,eAAe;KAAQ,CAAA,CAC9D;OAHK,CAGL,CACP;GACG,CAAA;GACN,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cAA8B;GAAU,CAAA;EACpD;;AAEV;;;;;;;;ACxDA,IAAa,gBAAb,MAA2B;CACzB,SAAS,GAAA,qBAAA,QAAuC,SAAS,CAAC;CAC1D,UAAS,GAAA,qBAAA,QAAsC,CAAC,CAAC;CACjD,cAAS,GAAA,qBAAA,QAA6C,CAAC,CAAC;CACxD,WAAS,GAAA,qBAAA,QAAuC,CAAC,CAAC;CAElD;CACA;CACA,SAAiB;;;CAIjB,iCAAyB,IAAI,IAAoB;CAEjD,YAAY,SAAgC;EAC1C,KAAK,aAAa,SAAS,cAAA;EAC3B,KAAK,MAAM,SAAS,cAAc,KAAK,IAAI;CAC7C;;;;;;CAOA,OAAO,MAAkD;EACvD,OAAO,KAAK,QAAQ,WAAW,UAAU,KAAK,OAAO,KAAK,CAAC;CAC7D;;CAGA,OAAO,OAAyB;EAC9B,QAAQ,MAAM,MAAd;GACE,KAAK;IACH,KAAK,MAAM,IAAI,WAAW,KAAK,MAAM,KAAK,GAAG,MAAM,MAAM,MAAM,KAAK,CAAC;IACrE;GACF,KAAK;IACH,KAAK,MAAM,IAAI,aAAa,KAAK,MAAM,KAAK,GAAG,MAAM,MAAM,WAAW,CAAC;IACvE;GACF,KAAK;IACH,KAAK,MAAM,IAAI,aAAa,KAAK,MAAM,KAAK,GAAG,MAAM,MAAM,QAAQ,CAAC;IACpE;GACF,KAAK;IACH,KAAK,MAAM,IAAI,aAAa,KAAK,MAAM,KAAK,GAAG,MAAM,MAAM,UAAU,CAAC;IAItE,KAAK,kBAAkB,MAAM,IAAI;IACjC;GACF,KAAK;IACH,KAAK,UAAU;KACb,MAAM;KACN,UAAU,MAAM;KAChB,gBAAgB,MAAM;IACxB,CAAC;IACD;GACF,KAAK;IACH,KAAK,UAAU;KAAE,MAAM;KAAe,UAAU,MAAM;IAAS,CAAC;IAChE;GACF,KAAK;IACH,KAAK,UAAU;KACb,MAAM;KACN,UAAU,MAAM;KAChB,YAAY,MAAM;IACpB,CAAC;IACD;GACF,KAAK;IACH,KAAK,UAAU;KACb,MAAM;KACN,UAAU,MAAM;KAChB,YAAY,MAAM;KAClB,OAAO,MAAM;IACf,CAAC;IACD;GACF,KAAK;IACH,KAAK,UAAU;KAAE,MAAM;KAAe,UAAU,MAAM;IAAS,CAAC;IAChE;GACF,KAAK;IACH,KAAK,UAAU;KAAE,MAAM;KAAM,UAAU,MAAM;IAAS,CAAC;IACvD;GACF,KAAK;IACH,KAAK,eAAe,IAAI,YAAY,MAAM,MAAM,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC;IACvE,KAAK,aAAa;KAAE,MAAM;KAAO,MAAM,MAAM;KAAM,MAAM,MAAM;KAAM,MAAM,MAAM;IAAK,CAAC;IACvF;GAEF,KAAK,oBAAoB;IACvB,MAAM,aAAa,KAAK,aAAa,MAAM,MAAM,MAAM,IAAI;IAC3D,KAAK,aAAa;KAChB,MAAM;KACN,MAAM,MAAM;KACZ,MAAM,MAAM;KACZ,QAAQ,MAAM;KACd,GAAI,eAAe,KAAA,IAAY,EAAE,WAAW,IAAI,CAAC;IACnD,CAAC;IACD;GACF;GACA,KAAK,kBAAkB;IACrB,MAAM,aAAa,KAAK,aAAa,MAAM,MAAM,MAAM,IAAI;IAC3D,KAAK,aAAa;KAChB,MAAM;KACN,MAAM,MAAM;KACZ,MAAM,MAAM;KACZ,OAAO,MAAM;KACb,GAAI,eAAe,KAAA,IAAY,EAAE,WAAW,IAAI,CAAC;IACnD,CAAC;IACD;GACF;GACA,KAAK;IACH,KAAK,aAAa;KAAE,MAAM;KAAY,MAAM,MAAM;KAAM,MAAM,MAAM;IAAK,CAAC;IAC1E;GACF,KAAK;IACH,KAAK,UAAU;KACb,MAAM,MAAM;KACZ,OAAO,MAAM;KACb,OAAO,MAAM;KACb,QAAQ,MAAM;IAChB,CAAC;IACD;EACJ;CACF;;CAGA,YAAkB;EAChB,KAAK,OAAO,IAAI,CAAC,CAAC;EAClB,KAAK,WAAW,IAAI,CAAC,CAAC;EACtB,KAAK,QAAQ,IAAI,CAAC,CAAC;EAInB,KAAK,eAAe,MAAM;CAC5B;CAMA,UAAkB,OAAuD;EACvE,MAAM,OAAO;GAAE,IAAI,KAAK;GAAU,GAAG,KAAK,IAAI;GAAG,GAAG;EAAM;EAC1D,KAAK,OAAO,IAAI,cAAc,KAAK,OAAO,KAAK,GAAG,MAAM,KAAK,UAAU,CAAC;CAC1E;CAEA,aAAqB,OAA0D;EAC7E,MAAM,OAAO;GAAE,IAAI,KAAK;GAAU,GAAG,KAAK,IAAI;GAAG,GAAG;EAAM;EAC1D,KAAK,WAAW,IAAI,cAAc,KAAK,WAAW,KAAK,GAAG,MAAM,KAAK,UAAU,CAAC;CAClF;CAEA,UAAkB,OAA2C;EAC3D,MAAM,OAAO;GAAE,IAAI,KAAK;GAAU,GAAG,KAAK,IAAI;GAAG,GAAG;EAAM;EAC1D,KAAK,QAAQ,IAAI,cAAc,KAAK,QAAQ,KAAK,GAAG,MAAM,KAAK,UAAU,CAAC;CAC5E;CAEA,aAAqB,MAAyB,MAA8C;EAC1F,MAAM,MAAM,YAAY,MAAM,IAAI;EAClC,MAAM,YAAY,KAAK,eAAe,IAAI,GAAG;EAC7C,IAAI,cAAc,KAAA,GAAW,OAAO,KAAA;EACpC,KAAK,eAAe,OAAO,GAAG;EAC9B,OAAO,KAAK,IAAI,IAAI;CACtB;;;;;;CAOA,kBAA0B,MAA+B;EACvD,IAAI,KAAK,eAAe,SAAS,GAAG;EACpC,MAAM,SAAS,GAAG,KAAK,KAAK,GAAG,EAAE;EACjC,MAAM,QAAQ,KAAK,KAAK,GAAG;EAC3B,KAAK,MAAM,OAAO,KAAK,eAAe,KAAK,GAAG;GAC5C,MAAM,aAAa,IAAI,MAAM,GAAG,EAAE,MAAM;GACxC,IAAI,eAAe,SAAS,WAAW,WAAW,MAAM,GACtD,KAAK,eAAe,OAAO,GAAG;EAElC;CACF;AACF;AAEA,SAAS,YAAY,MAAyB,MAAkC;CAC9E,OAAO,GAAG,KAAK,KAAK,GAAG,EAAE,GAAG,QAAQ;AACtC;AAaA,SAAS,WAA2B;CAClC,OAAO;EAAE,MAAM,CAAC;EAAG,OAAO;EAAU,OAAO,KAAA;EAAW,UAAU,CAAC;CAAE;AACrE;AAEA,SAAS,cAAiB,KAAmB,MAAS,KAAkB;CACtE,MAAM,OAAO,IAAI,UAAU,MAAM,IAAI,MAAM,IAAI,SAAS,MAAM,CAAC,IAAI,IAAI,MAAM;CAC7E,KAAK,KAAK,IAAI;CACd,OAAO;AACT;;;;;;;;AASA,SAAgB,WACd,MACA,MACA,OACgB;CAChB,IAAI,KAAK,WAAW,GAIlB,OAAO;EAAE,GAAG;EAAM,OAAO;EAAU;CAAM;CAE3C,OAAO,gBAAgB,MAAM,MAAM,GAAG,KAAK;AAC7C;AAEA,SAAS,gBACP,MACA,MACA,OACA,OACgB;CAChB,IAAI,UAAU,KAAK,QACjB,OAAO;EAAE,GAAG;EAAM,OAAO;EAAU;CAAM;CAE3C,MAAM,UAAU,KAAK;CACrB,MAAM,MAAM,KAAK,SAAS,WAAW,MAAM,EAAE,KAAK,EAAE,KAAK,SAAS,OAAO,OAAO;CAChF,MAAM,YAAY,KAAK,MAAM,GAAG,QAAQ,CAAC;CACzC,IAAI,QAAQ,IAAI;EACd,MAAM,WAAW,gBACf;GAAE,MAAM;GAAW,OAAO;GAAU,OAAO,KAAA;GAAW,UAAU,CAAC;EAAE,GACnE,MACA,QAAQ,GACR,KACF;EACA,OAAO;GAAE,GAAG;GAAM,UAAU,CAAC,GAAG,KAAK,UAAU,QAAQ;EAAE;CAC3D;CACA,MAAM,WAAW,KAAK,SAAS;CAC/B,MAAM,eAAe,gBAAgB,UAAU,MAAM,QAAQ,GAAG,KAAK;CACrE,MAAM,eAAe,KAAK,SAAS,MAAM;CACzC,aAAa,OAAO;CACpB,OAAO;EAAE,GAAG;EAAM,UAAU;CAAa;AAC3C;;;;;AAMA,SAAgB,aACd,MACA,MACA,OACgB;CAChB,IAAI,KAAK,WAAW,GAClB,OAAO;EAAE,GAAG;EAAM;CAAM;CAE1B,OAAO,WAAW,MAAM,MAAM,GAAG,KAAK,KAAK;AAC7C;AAEA,SAAS,WACP,MACA,MACA,OACA,OACuB;CACvB,IAAI,UAAU,KAAK,QACjB,OAAO;EAAE,GAAG;EAAM;CAAM;CAE1B,MAAM,UAAU,KAAK;CACrB,MAAM,MAAM,KAAK,SAAS,WAAW,MAAM,EAAE,KAAK,EAAE,KAAK,SAAS,OAAO,OAAO;CAChF,IAAI,QAAQ,IAAI,OAAO;CACvB,MAAM,WAAW,KAAK,SAAS;CAC/B,MAAM,eAAe,WAAW,UAAU,MAAM,QAAQ,GAAG,KAAK;CAChE,IAAI,iBAAiB,MAAM,OAAO;CAClC,MAAM,eAAe,KAAK,SAAS,MAAM;CACzC,aAAa,OAAO;CACpB,OAAO;EAAE,GAAG;EAAM,UAAU;CAAa;AAC3C;;;;;;;;ACnXA,MAAa,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC2C5B,SAAgB,cAAc,OAAyC;CACrE,MAAM,EAAE,MAAM,aAAa,QAAQ,YAAY,YAAY,kBAAkB,QAAQ;CACrF,MAAM,SAAA,GAAA,MAAA,eACE,IAAI,cAAc,eAAe,KAAA,IAAY,EAAE,WAAW,IAAI,KAAA,CAAS,GAC7E,CAAC,UAAU,CACb;CACA,CAAA,GAAA,MAAA,iBAAgB,MAAM,OAAO,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;CAGjD,MAAM,WAAA,GAAA,MAAA,eAAwB,YAAY,YAAY,UAAU,GAAG,CAAC,YAAY,UAAU,CAAC;CAC3F,MAAM,CAAC,KAAK,WAAA,GAAA,MAAA,UAAgC,QAAQ,GAAG;CACvD,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,UAAsB,KAAK;CAE1C,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,UAAoD,QAAQ,OAAO;CACnF,MAAM,SAAS,QAAQ;CACvB,MAAM,aAAa,MAAc,YAAY,UAAU;EAAE,GAAG;GAAO,MAAM;CAAE,EAAE;CAG7E,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,eAAe,KAAA,GAAW;EAC9B,aAAa,YAAY;GAAE;GAAK;EAAQ,CAAC;CAC3C,GAAG;EAAC;EAAY;EAAK;CAAO,CAAC;CAK7B,MAAM,CAAC,cAAc,oBAAA,GAAA,MAAA,UAA+C,CAAC,CAAC;CACtE,MAAM,WAAA,GAAA,MAAA,QAAiB,IAAI;CAC3B,QAAQ,UAAU;CAClB,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,QAAQ,aAAa;EACzB,MAAM,aAAa,gBAAgB,QAAQ,QAAQ,QAAQ,aAAa,CAAC;EACzE,KAAK;EACL,MAAM,KAAK,OAAO,YAAY,MAAM,eAAe;EACnD,aAAa,OAAO,cAAc,EAAE;CACtC,GAAG,CAAC,KAAK,eAAe,CAAC;CAEzB,MAAM,YAAA,GAAA,sBAAA,KAAe,MAAM,KAAK;CAChC,MAAM,aAAA,GAAA,sBAAA,KAAgB,MAAM,MAAM;CAClC,MAAM,iBAAA,GAAA,sBAAA,KAAoB,MAAM,UAAU;CAC1C,MAAM,cAAA,GAAA,sBAAA,KAAiB,MAAM,OAAO;CAGpC,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,UAKL,IAAI;CACd,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,QACF,UAAU;GACR,MAAM;GACN,OAAO;GACP,WAAW;GACX,QAAQ;EACV,CAAC;OAED,UAAU,IAAI;CAIlB,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,SAAS,QAAQ,UAAU;CAEjC,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,WAAU;EAAgB,eAAY;YAA3C;GACE,iBAAA,GAAA,kBAAA,KAAC,SAAD,EAAA,UAAQ,aAAoB,CAAA;GAC5B,iBAAA,GAAA,kBAAA,MAAC,OAAD;IAAK,WAAU;IAAqB,MAAK;cAAzC;KACE,iBAAA,GAAA,kBAAA,KAAC,KAAD;MACE,MAAK;MACL,SAAS;MACD;MACR,OAAM;MACN,OAAM;MACN,OAAO,qBAAqB,QAAQ;KACrC,CAAA;KACD,iBAAA,GAAA,kBAAA,KAAC,KAAD;MACE,MAAK;MACL,SAAS;MACD;MACR,OAAM;MACN,OAAM;MACN,OAAO,UAAU;KAClB,CAAA;KACD,iBAAA,GAAA,kBAAA,KAAC,KAAD;MACE,MAAK;MACL,SAAS;MACD;MACR,OAAM;MACN,OAAM;MACN,OAAO,aAAa;KACrB,CAAA;KACD,iBAAA,GAAA,kBAAA,KAAC,KAAD;MACE,MAAK;MACL,SAAS;MACD;MACR,OAAM;MACN,OAAM;MACN,OAAO,cAAc;KACtB,CAAA;KACD,iBAAA,GAAA,kBAAA,KAAC,KAAD;MACE,MAAK;MACL,SAAS;MACD;MACR,OAAM;MACN,OAAM;MACN,OAAO,WAAW;KACnB,CAAA;KACD,iBAAA,GAAA,kBAAA,MAAC,UAAD;MACE,MAAK;MACL,gBAAc;MACd,WAAW,SAAS,+CAA+C;MACnE,eAAe,UAAU,CAAC,MAAM;MAChC,OAAO,SAAS,wBAAwB;gBAL1C,CAOE,iBAAA,GAAA,kBAAA,KAAC,QAAD;OAAM,eAAY;iBAAQ,SAAS,MAAM;MAAU,CAAA,GACnD,iBAAA,GAAA,kBAAA,KAAC,QAAD;OAAM,WAAU;iBAA4B,SAAS,YAAY;MAAe,CAAA,CAC1E;;KACR,iBAAA,GAAA,kBAAA,MAAC,UAAD;MACE,WAAU;MACV,MAAK;MACL,eAAe,MAAM,UAAU;MAC/B,OAAM;gBAJR,CAME,iBAAA,GAAA,kBAAA,KAAC,QAAD;OAAM,WAAU;iBAA2B;MAAW,CAAA,GACtD,iBAAA,GAAA,kBAAA,KAAC,QAAD;OAAM,WAAU;OAA2B,eAAY;iBAAO;MAExD,CAAA,CACA;;IACL;;IAEH,QAAQ,WAAW,QAAQ,eAAe,QAAQ,eAAe,QAAQ,aACzE,iBAAA,GAAA,kBAAA,MAAC,OAAD;IAAK,WAAU;cAAf,CACE,iBAAA,GAAA,kBAAA,KAAC,SAAD;KACE,MAAK;KACL,OAAO;KACP,aAAa,UAAU,IAAI;KAC3B,WAAW,MAAM,UAAU,EAAE,OAAO,KAAK;IAC1C,CAAA,GACA,WAAW,MACV,iBAAA,GAAA,kBAAA,KAAC,UAAD;KAAQ,MAAK;KAAS,eAAe,UAAU,EAAE;KAAG,cAAW;eAAe;IAEtE,CAAA,CAEP;;GAGP,iBAAA,GAAA,kBAAA,MAAC,OAAD;IAAK,WAAU;IAAqB,MAAK;cAAzC;KACG,QAAQ,UAAU,iBAAA,GAAA,kBAAA,KAAC,UAAD;MAAgB;MAAM,WAAW;KAAgB,CAAA;KACnE,QAAQ,WAAW,iBAAA,GAAA,kBAAA,KAAC,WAAD;MAAW,SAAS;MAAe;KAAS,CAAA;KAC/D,QAAQ,eAAe,iBAAA,GAAA,kBAAA,KAAC,eAAD;MAAe,SAAS;MAAsB;KAAS,CAAA;KAC9E,QAAQ,eAAe,iBAAA,GAAA,kBAAA,KAAC,eAAD;MAAe,SAAS;MAAmB;KAAS,CAAA;KAC3E,QAAQ,YAAY,iBAAA,GAAA,kBAAA,KAAC,YAAD;MAAY,SAAS;MAAgB;KAAS,CAAA;IAChE;;EACF;;AAET;AAEA,SAAS,IAAI,OAOI;CAEf,OACE,iBAAA,GAAA,kBAAA,MAAC,UAAD;EACE,MAAK;EACL,MAAK;EACL,iBALa,MAAM,YAAY,MAAM;EAMrC,OAAO,MAAM;EACb,WAAU;EACV,eAAe,MAAM,OAAO,MAAM,IAAI;YANxC;GAQE,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;cAAgC,MAAM;GAAY,CAAA;GAClE,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;IAAgC,eAAY;cACzD,MAAM;GACH,CAAA;GACL,MAAM,QAAQ,KACb,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;IAA0B,eAAY;cACnD,MAAM;GACH,CAAA;EAEF;;AAEZ;AAEA,SAAS,qBAAqB,MAA8B;CAC1D,IAAI,QAAQ,KAAK,UAAU,aAAa,IAAI;CAC5C,KAAK,MAAM,KAAK,KAAK,UAAU,SAAS,qBAAqB,CAAC;CAC9D,OAAO,KAAK,IAAI,QAAQ,GAAG,CAAC;AAC9B;AAMA,SAAS,SAAS,EAChB,MACA,aAIe;CACf,IAAI,KAAK,SAAS,WAAW,GAC3B,OAAO,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAO,OAAM;EAAqB,MAAK;CAAgD,CAAA;CAKhG,MAAM,WAAA,GAAA,MAAA,eAAwB,cAAc,SAAS,GAAG,CAAC,SAAS,CAAC;CACnE,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAK,WAAU;YACZ,KAAK,SAAS,KAAK,UAClB,iBAAA,GAAA,kBAAA,KAAC,UAAD;GAAqC,MAAM;GAAgB;EAAU,GAAtD,MAAM,KAAK,KAAK,GAAG,CAAmC,CACtE;CACE,CAAA;AAET;AAEA,SAAS,cAAc,SAAwD;CAC7E,MAAM,2BAAW,IAAI,IAAoB;CACzC,MAAM,sBAAM,IAAI,IAAoB;CACpC,KAAK,MAAM,KAAK,SAAS;EACvB,MAAM,MAAM,GAAG,EAAE,KAAK,KAAK,GAAG,EAAE,GAAG,EAAE,QAAQ;EAC7C,MAAM,UAAU,EAAE,KAAK,KAAK,GAAG;EAC/B,IAAI,EAAE,SAAS,OAAO;GACpB,SAAS,IAAI,MAAM,SAAS,IAAI,GAAG,KAAK,KAAK,CAAC;GAC9C,IAAI,IAAI,UAAU,IAAI,IAAI,OAAO,KAAK,KAAK,CAAC;EAC9C,OAAO,IAAI,EAAE,SAAS,aAAa,EAAE,SAAS,SAAS;GACrD,MAAM,IAAI,SAAS,IAAI,GAAG,KAAK;GAC/B,IAAI,IAAI,GAAG,SAAS,IAAI,KAAK,IAAI,CAAC;GAClC,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK;GAC9B,IAAI,IAAI,GAAG,IAAI,IAAI,SAAS,IAAI,CAAC;EACnC;CACF;CACA,OAAO;AACT;AAEA,SAAS,SAAS,EAChB,MACA,WAIe;CACf,MAAM,OAAO,KAAK,KAAK,KAAK,KAAK,SAAS,MAAM;CAChD,MAAM,aACJ,KAAK,UAAU,cACX,uCACA,KAAK,UAAU,aACb,sCACA;CACR,MAAM,eAAe,QAAQ,IAAI,KAAK,KAAK,KAAK,GAAG,CAAC,KAAK;CACzD,MAAM,gBAAA,GAAA,MAAA,eAA6B,eAAe,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC;CAC3E,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,UAAyB,KAAK;CAChD,MAAM,iBAAiB,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU;CAElE,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,WAAU;YAAf;GACE,iBAAA,GAAA,kBAAA,MAAC,QAAD;IAAM,WAAU;cAAhB;KACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAU;gBAA2B;KAAW,CAAA;KACtD,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAW;gBAAa,KAAK;KAAY,CAAA;KAC9C,eAAe,KACd,iBAAA,GAAA,kBAAA,MAAC,QAAD;MAAM,WAAU;MAA6B,OAAM;gBAAnD,CACG,cAAa,UACV;;KAEP,kBACC,iBAAA,GAAA,kBAAA,KAAC,UAAD;MACE,MAAK;MACL,WAAU;MACV,iBAAe;MACf,eAAe,cAAc,MAAM,CAAC,CAAC;MACrC,OAAO,YAAY,eAAe;gBAEjC;KACK,CAAA;IAEN;;GACL,aAAa,kBACZ,iBAAA,GAAA,kBAAA,KAAC,OAAD;IAAK,WAAU;cACb,iBAAA,GAAA,kBAAA,KAAC,UAAD,EAAU,OAAO,KAAK,MAAQ,CAAA;GAC3B,CAAA;GAEN,KAAK,SAAS,SAAS,KACtB,iBAAA,GAAA,kBAAA,KAAC,OAAD;IAAK,WAAU;cACZ,KAAK,SAAS,KAAK,UAClB,iBAAA,GAAA,kBAAA,KAAC,UAAD;KAAqC,MAAM;KAAgB;IAAU,GAAtD,MAAM,KAAK,KAAK,GAAG,CAAmC,CACtE;GACE,CAAA;EAEJ;;AAET;;AAGA,SAAS,eAAe,OAAwB;CAC9C,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;CAClD,IAAI,OAAO,UAAU,UAAU,OAAO,IAAI,SAAS,OAAO,EAAE,EAAE;CAC9D,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,OAAO,OAAO,KAAK;CAChF,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,OAAO;CAClD,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,OAAO,OAAO,KAAK,KAAgC;EACzD,IAAI,KAAK,WAAW,GAAG,OAAO;EAK9B,OAAO,KAJO,KAAK,MAAM,GAAG,CAAC,EAAE,KAAK,MAAM;GACxC,MAAM,IAAK,MAAkC;GAC7C,OAAO,GAAG,EAAE,IAAI,WAAW,CAAC;EAC9B,CACgB,EAAE,KAAK,IAAI,IAAI,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,MAAM,GAAG;CAChF;CACA,OAAO,OAAO,KAAK;AACrB;AAEA,SAAS,WAAW,GAAoB;CACtC,IAAI,MAAM,MAAM,OAAO;CACvB,IAAI,MAAM,KAAA,GAAW,OAAO;CAC5B,IAAI,OAAO,MAAM,UAAU,OAAO,IAAI,SAAS,GAAG,EAAE,EAAE;CACtD,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,OAAO,OAAO,CAAC;CACpE,IAAI,MAAM,QAAQ,CAAC,GAAG,OAAO,IAAI,EAAE,OAAO;CAC1C,IAAI,OAAO,MAAM,UAAU,OAAO,IAAI,OAAO,KAAK,CAAW,EAAE,OAAO;CACtE,OAAO,OAAO,CAAC;AACjB;AAEA,SAAS,SAAS,GAAW,KAAqB;CAChD,OAAO,EAAE,UAAU,MAAM,IAAI,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE;AACtD;AAMA,SAAS,cAAc,EACrB,SACA,UAIe;CACf,MAAM,WAAW,YAAY,SAAS,QAAQ,iBAAiB;CAC/D,IAAI,QAAQ,WAAW,GACrB,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,OAAM;EACN,MAAK;CACN,CAAA;CAGL,IAAI,SAAS,WAAW,GACtB,OAAO,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAO,OAAM;EAAa,MAAM,oBAAoB,OAAO;CAAM,CAAA;CAE1E,OACE,iBAAA,GAAA,kBAAA,KAAC,MAAD;EAAI,WAAU;YACX,SAAS,KAAK,UACb,iBAAA,GAAA,kBAAA,KAAC,cAAD,EAA+C,MAAQ,GAApC,MAAM,IAAI,KAAK,GAAG,CAAkB,CACxD;CACC,CAAA;AAER;AAEA,SAAS,kBAAkB,GAA4B;CACrD,OAAO;EAAC,GAAG,EAAE,IAAI,IAAI,MAAM;EAAG,EAAE;EAAQ,cAAc,EAAE,IAAI;CAAC,EAAE,KAAK,GAAG;AACzE;AAEA,SAAS,aAAa,EAAE,SAAmD;CACzE,MAAM,YACJ,MAAM,WAAW,UACb,6BACA,MAAM,WAAW,YACf,+BACA,MAAM,WAAW,YACf,4BACA;CACV,MAAM,QAAQ,MAAM,iBAAiB,OAAO,KAAK,IAAI,IAAI,MAAM,gBAAgB;CAC/E,MAAM,OAAiB,CAAC;CACxB,IAAI,MAAM,SAAS,KAAK,KAAK,OAAO;CACpC,IAAI,MAAM,YAAY,KAAK,KAAK,UAAU;CAC1C,IAAI,MAAM,qBAAqB,KAAK,KAAK,YAAY;CACrD,OACE,iBAAA,GAAA,kBAAA,KAAC,KAAD;EACE,MAAM,MAAM;EACD;EACX,QAAQ,WAAW,MAAM,GAAG;EAC5B,GAAG,MAAM,iBAAiB,KAAK,IAAI;EACnC,SAAS,MAAM,SAAS,MAAM;EAC9B,QAAQ,CAAC,SAAS,OAAO,GAAG,UAAU,KAAK,EAAE,QAAQ,KAAK,GAAG,IAAI,EAAE,KAAK,KAAK;CAC9E,CAAA;AAEL;AAEA,SAAS,cAAc,GAAoB;CACzC,IAAI;EACF,OAAO,KAAK,UAAU,CAAC,KAAK;CAC9B,QAAQ;EACN,OAAO,OAAO,CAAC;CACjB;AACF;AAEA,SAAS,UAAU,IAAoB;CACrC,IAAI,KAAK,KAAM,OAAO,GAAG,GAAG;CAC5B,IAAI,KAAK,KAAQ,OAAO,GAAG,KAAK,MAAM,KAAK,GAAI,EAAE;CACjD,IAAI,KAAK,MAAW,OAAO,GAAG,KAAK,MAAM,KAAK,GAAM,EAAE;CACtD,OAAO,GAAG,KAAK,MAAM,KAAK,IAAS,EAAE;AACvC;AAMA,SAAS,YACP,KACA,YAC4D;CAC5D,MAAM,QAAQ;EAAE,MAAM;EAAI,OAAO;EAAI,WAAW;EAAI,WAAW;EAAI,QAAQ;CAAG;CAC9E,IAAI,QAAQ,KAAA,GAAW,OAAO;EAAE,KAAK;EAAY,SAAS;CAAM;CAChE,IAAI,OAAO,WAAW,aAAa,OAAO;EAAE,KAAK;EAAY,SAAS;CAAM;CAC5E,IAAI;EAEF,MAAM,MAAM,IADO,gBAAgB,OAAO,SAAS,KAAK,QAAQ,MAAM,EAAE,CACvD,EAAE,IAAI,GAAG;EAC1B,IAAI,QAAQ,MAAM,OAAO;GAAE,KAAK;GAAY,SAAS;EAAM;EAC3D,MAAM,SAAS,KAAK,MAAM,mBAAmB,GAAG,CAAC;EAIjD,OAAO;GACL,KAAK,OAAO,OAAO;GACnB,SAAS;IAAE,GAAG;IAAO,GAAI,OAAO,WAAW,CAAC;GAAG;EACjD;CACF,QAAQ;EACN,OAAO;GAAE,KAAK;GAAY,SAAS;EAAM;CAC3C;AACF;AAEA,SAAS,aACP,KACA,OACM;CACN,IAAI,OAAO,WAAW,aAAa;CACnC,MAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,KAAK,QAAQ,MAAM,EAAE,CAAC;CACzE,OAAO,IAAI,KAAK,mBAAmB,KAAK,UAAU,KAAK,CAAC,CAAC;CACzD,MAAM,OAAO,IAAI,OAAO,SAAS;CACjC,IAAI,SAAS,OAAO,SAAS,MAC3B,OAAO,QAAQ,aAAa,MAAM,IAAI,IAAI;AAE9C;AAMA,SAAS,UAAU,EAAE,SAAS,UAAmE;CAC/F,MAAM,WAAW,YAAY,SAAS,QAAQ,aAAa;CAC3D,IAAI,QAAQ,WAAW,GACrB,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAO,OAAM;EAAsB,MAAK;CAAqD,CAAA;CAGjG,IAAI,SAAS,WAAW,GACtB,OAAO,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAO,OAAM;EAAa,MAAM,oBAAoB,OAAO;CAAM,CAAA;CAE1E,OACE,iBAAA,GAAA,kBAAA,KAAC,MAAD;EAAI,WAAU;YACX,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,KAAK,UAC5B,iBAAA,GAAA,kBAAA,KAAC,UAAD,EAAgC,MAAQ,GAAzB,MAAM,EAAmB,CACzC;CACC,CAAA;AAER;AAEA,SAAS,cAAc,GAAuB;CAC5C,MAAM,QAAkB,CAAC,EAAE,MAAM,GAAG,EAAE,SAAS,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC;CACpE,IAAI,EAAE,SAAS,eAAe,MAAM,KAAK,cAAc,EAAE,KAAK,CAAC;CAC/D,IAAI,EAAE,SAAS,cAAc,MAAM,KAAK,GAAG,EAAE,cAAc;CAC3D,OAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,SAAS,EAAE,SAA8C;CAChE,MAAM,YACJ,MAAM,SAAS,gBACX,6BACA,MAAM,SAAS,kBACb,+BACA,MAAM,SAAS,iBAAiB,MAAM,SAAS,OAC7C,4BACA;CAEV,IAAI,SAAwB;CAC5B,IAAI;CACJ,IAAI,SAAwB;CAC5B,IAAI,MAAM,SAAS,iBACjB,SAAS,GAAG,MAAM,WAAW;MACxB,IAAI,MAAM,SAAS,eAAe;EACvC,SAAS,GAAG,MAAM,WAAW;EAC7B,UAAU,MAAM;CAClB,OAAO,IAAI,MAAM,SAAS,cACxB,SAAS,QAAQ,WAAW,MAAM,cAAc;CAGlD,OACE,iBAAA,GAAA,kBAAA,KAAC,KAAD;EACE,MAAM,MAAM;EACD;EACX,QAAQ,WAAW,MAAM,QAAQ;EACjC,GAAG,MAAM;EACD;EACC;EACD;CACT,CAAA;AAEL;AAMA,SAAS,cAAc,EACrB,SACA,UAIe;CACf,MAAM,WAAW,YAAY,SAAS,QAAQ,gBAAgB;CAC9D,IAAI,QAAQ,WAAW,GACrB,OAAO,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAO,OAAM;EAAmB,MAAK;CAAiD,CAAA;CAE/F,IAAI,SAAS,WAAW,GACtB,OAAO,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAO,OAAM;EAAa,MAAM,oBAAoB,OAAO;CAAM,CAAA;CAE1E,OACE,iBAAA,GAAA,kBAAA,KAAC,MAAD;EAAI,WAAU;YACX,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,KAAK,UAC5B,iBAAA,GAAA,kBAAA,KAAC,aAAD,EAAmC,MAAQ,GAAzB,MAAM,EAAmB,CAC5C;CACC,CAAA;AAER;AAEA,SAAS,iBAAiB,GAA0B;CAClD,MAAM,QAAkB;EAAC,EAAE;EAAM,GAAG,EAAE;EAAM,EAAE,QAAQ;CAAE;CACxD,IAAI,EAAE,SAAS,OAAO,MAAM,KAAK,cAAc,EAAE,IAAI,CAAC;CACtD,IAAI,EAAE,SAAS,WAAW,MAAM,KAAK,cAAc,EAAE,MAAM,CAAC;CAC5D,IAAI,EAAE,SAAS,SAAS,MAAM,KAAK,cAAc,EAAE,KAAK,CAAC;CACzD,OAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,YAAY,EAAE,SAAiD;CACtE,MAAM,YACJ,MAAM,SAAS,UACX,6BACA,MAAM,SAAS,aACb,gCACA,MAAM,SAAS,YACb,+BACA;CAEV,MAAM,SAAS,MAAM,OAAO,GAAG,MAAM,KAAK,KAAK,WAAW,MAAM,IAAI,MAAM,WAAW,MAAM,IAAI;CAE/F,IAAI;CACJ,IAAI,SAAwB;CAC5B,IAAI,MAAM,SAAS,OAAO,UAAU,MAAM;MACrC,IAAI,MAAM,SAAS,WAAW;EACjC,UAAU,MAAM;EAChB,IAAI,MAAM,eAAe,KAAA,GAAW,SAAS,GAAG,MAAM,WAAW;CACnE,OAAO,IAAI,MAAM,SAAS,SAAS;EACjC,UAAU,MAAM;EAChB,IAAI,MAAM,eAAe,KAAA,GAAW,SAAS,GAAG,MAAM,WAAW;CACnE;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAC,KAAD;EACE,MAAM,MAAM;EACD;EACH;EACR,GAAG,MAAM;EACA;EACD;CACT,CAAA;AAEL;AAMA,SAAS,WAAW,EAAE,SAAS,UAAmE;CAChG,MAAM,WAAW,YAAY,SAAS,QAAQ,aAAa;CAC3D,IAAI,QAAQ,WAAW,GACrB,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,OAAM;EACN,MAAK;CACN,CAAA;CAGL,IAAI,SAAS,WAAW,GACtB,OAAO,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAO,OAAM;EAAa,MAAM,oBAAoB,OAAO;CAAM,CAAA;CAE1E,OACE,iBAAA,GAAA,kBAAA,KAAC,MAAD;EAAI,WAAU;YACX,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,KAAK,UAC5B,iBAAA,GAAA,kBAAA,KAAC,UAAD,EAAgC,MAAQ,GAAzB,MAAM,EAAmB,CACzC;CACC,CAAA;AAER;AAEA,SAAS,cAAc,GAAuB;CAC5C,OAAO;EAAC,EAAE;EAAO,GAAG,EAAE;EAAM,EAAE,QAAQ,UAAU;EAAW,GAAG,EAAE;CAAM,EAAE,KAAK,GAAG;AAClF;AAEA,SAAS,SAAS,EAAE,SAA8C;CAChE,MAAM,YAAY,MAAM,QAAQ,+BAA+B;CAC/D,OACE,iBAAA,GAAA,kBAAA,KAAC,KAAD;EACE,MAAM,MAAM,QAAQ,UAAU;EACnB;EACX,QAAQ,GAAG,WAAW,MAAM,IAAI,EAAE,KAAK,MAAM;EAC7C,GAAG,MAAM;EACT,QAAQ,MAAM,OAAO,SAAS,IAAI,MAAM,OAAO,KAAK,KAAK,IAAI;CAC9D,CAAA;AAEL;AAiBA,SAAS,IAAI,OAA+B;CAC1C,MAAM,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,SAAS,WAAW;CAChE,MAAM,aAAa,YAAY,KAAA;CAC/B,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,UAAwB,KAAK;CAC9C,MAAM,YAAY;CAElB,OACE,iBAAA,GAAA,kBAAA,MAAC,MAAD;EAAI,WAAW,YAAY,gCAAgC;YAA3D;GACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;IACE,WAAU;IACV,SAAS,kBAAkB,aAAa,MAAM,CAAC,CAAC,IAAI,KAAA;cAFtD;KAIE,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAW,sBAAsB;gBAAc;KAAW,CAAA;KAChE,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAU;gBAAwB;KAAa,CAAA;KACpD,WAAW,KAAA,KAAa,WAAW,QAClC,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAU;gBAA0B;KAAa,CAAA;KAEzD,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAU;gBAAsB,WAAW,CAAC;KAAQ,CAAA;KACzD,aACC,iBAAA,GAAA,kBAAA,KAAC,QAAD;MACE,eAAY;MACZ,WAAW,yBAAyB,WAAW,+BAA+B;gBAC/E;KAEK,CAAA;IAEL;;GACJ,UAAU,QACT,iBAAA,GAAA,kBAAA,KAAC,OAAD;IAAK,WAAU;cAAsD;GAAY,CAAA;GAElF,cAAc,YACb,iBAAA,GAAA,kBAAA,KAAC,OAAD;IAAK,WAAU;cACb,iBAAA,GAAA,kBAAA,KAAC,UAAD,EAAU,OAAO,QAAU,CAAA;GACxB,CAAA;EAEL;;AAER;AAEA,SAAS,YAAe,OAAqB,QAAgB,UAAoC;CAC/F,QAAA,GAAA,MAAA,eAAqB;EACnB,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,KAAK;EAC1C,MAAM,IAAI,OAAO,YAAY;EAC7B,OAAO,MAAM,QAAQ,SAAS,SAAS,IAAI,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;CACxE,GAAG;EAAC;EAAO;EAAQ;CAAQ,CAAC;AAC9B;AAEA,SAAS,MAAM,EAAE,OAAO,QAAuD;CAC7E,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,WAAU;YAAf,CACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;GAAK,WAAU;aAA6B;EAAW,CAAA,GACvD,iBAAA,GAAA,kBAAA,KAAC,OAAD;GAAK,WAAU;aAA4B;EAAU,CAAA,CAClD;;AAET;;;AClsBA,MAAM,QAAQ;AACd,MAAM,QAAQ;AACd,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,SAAS;AAEf,SAAgB,iBAAiB,OAA4C;CAC3E,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,gBAAwC,UAAU,YAAY,MAAM,OAAO,CAAC;CAG1F,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,OAAO,iBAAiB,aAAa;EACzC,IAAI;GACF,aAAa,QAAQ,YAAY,KAAK,UAAU,KAAK,CAAC;EACxD,QAAQ,CAER;CACF,GAAG,CAAC,OAAO,UAAU,CAAC;CAItB,CAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,iBAAiB,UAAU,MAAM,gBAAgB,CAAC,CAAC;EACzD,IAAI,OAAO,WAAW,aAAa;EACnC,OAAO,iBAAiB,UAAU,QAAQ;EAC1C,aAAa,OAAO,oBAAoB,UAAU,QAAQ;CAC5D,GAAG,CAAC,CAAC;CAEL,MAAM,WAAA,GAAA,MAAA,cAAuB,SAAkB,UAAU,OAAO;EAAE,GAAG;EAAG;CAAK,EAAE,GAAG,CAAC,CAAC;CACpF,MAAM,UAAA,GAAA,MAAA,cAAsB,cAAuB,UAAU,OAAO;EAAE,GAAG;EAAG;CAAU,EAAE,GAAG,CAAC,CAAC;CAE7F,OACE,iBAAA,GAAA,kBAAA,MAAA,kBAAA,UAAA,EAAA,UAAA;EACE,iBAAA,GAAA,kBAAA,KAAC,SAAD,EAAA,UAAQ,aAAoB,CAAA;EAC5B,iBAAA,GAAA,kBAAA,KAAC,gBAAD;GAAgB,MAAM,MAAM;GAAM,eAAe,QAAQ,CAAC,MAAM,IAAI;EAAI,CAAA;EACvE,MAAM,QACL,iBAAA,GAAA,kBAAA,KAAC,gBAAD;GACS;GACG;GACV,eAAe,QAAQ,KAAK;GAC5B,kBAAkB,OAAO,CAAC,MAAM,SAAS;aAExC,CAAC,MAAM,aACN,iBAAA,GAAA,kBAAA,KAAC,OAAD;IAAK,WAAU;cACb,iBAAA,GAAA,kBAAA,KAAC,eAAD;KACE,MAAM,MAAM;KACZ,YAAY,MAAM;KAClB,YAAY,MAAM;KAClB,YAAY,MAAM;IACnB,CAAA;GACE,CAAA;EAEO,CAAA;CAElB,EAAA,CAAA;AAEN;AAEA,SAAS,eAAe,EAAE,MAAM,WAAiE;CAC/F,OACE,iBAAA,GAAA,kBAAA,MAAC,UAAD;EACE,MAAK;EACL,cAAY,OAAO,uBAAuB;EACjC;EACT,WAAW,0BAA0B,OAAO,kCAAkC;YAJhF,CAME,iBAAA,GAAA,kBAAA,KAAC,QAAD;GAAM,eAAY;GAAO,WAAU;EAA8B,CAAA,GACjE,iBAAA,GAAA,kBAAA,KAAC,QAAD;GAAM,WAAU;aAA+B;EAAmB,CAAA,CAC5D;;AAEZ;AAEA,SAAS,eAAe,OAMP;CACf,MAAM,EAAE,OAAO,UAAU,SAAS,eAAe;CACjD,MAAM,aAAA,GAAA,MAAA,QAQI,IAAI;CAEd,MAAM,iBAAiB,UAA6B,MAA0B;EAC5E,EAAE,eAAe;EAChB,EAAG,OAAmB,kBAAkB,EAAE,SAAS;EACpD,UAAU,UAAU;GAClB;GACA,IAAI,EAAE;GACN,IAAI,EAAE;GACN,IAAI,MAAM;GACV,IAAI,MAAM;GACV,IAAI,MAAM;GACV,IAAI,MAAM;EACZ;CACF;CACA,MAAM,iBAAiB,MAA0B;EAC/C,MAAM,IAAI,UAAU;EACpB,IAAI,MAAM,MAAM;EAChB,MAAM,KAAK,EAAE,UAAU,EAAE;EACzB,MAAM,KAAK,EAAE,UAAU,EAAE;EACzB,IAAI,EAAE,SAAS,QACb,UAAU,MAAM,gBAAgB;GAAE,GAAG;GAAG,GAAG,EAAE,KAAK;GAAI,GAAG,EAAE,KAAK;EAAG,CAAC,CAAC;OAChE;GACL,MAAM,IAAI,KAAK,IAAI,OAAO,EAAE,KAAK,EAAE;GACnC,MAAM,IAAI,KAAK,IAAI,OAAO,EAAE,KAAK,EAAE;GACnC,UAAU,MAAM,gBAAgB;IAAE,GAAG;IAAG;IAAG;GAAE,CAAC,CAAC;EACjD;CACF;CACA,MAAM,oBAAoB;EACxB,UAAU,UAAU;CACtB;CAEA,MAAM,YAAY,MAAM;CACxB,MAAM,SAAS,YAAY,WAAW,MAAM;CAE5C,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EACE,WAAU;EACV,MAAK;EACL,cAAW;EACX,OAAO;GAAE,MAAM,MAAM;GAAG,KAAK,MAAM;GAAG,OAAO,MAAM;GAAG;EAAO;EAC9C;EACF;EACb,iBAAiB;YAPnB;GASE,iBAAA,GAAA,kBAAA,MAAC,OAAD;IAAK,WAAU;IAAgC,eAAe,cAAc,MAAM;cAAlF;KACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAU;MAA8B,eAAY;gBAAO;KAE3D,CAAA;KACN,iBAAA,GAAA,kBAAA,KAAC,QAAD;MAAM,WAAU;gBAA+B;KAAmB,CAAA;KAClE,iBAAA,GAAA,kBAAA,MAAC,OAAD;MAAK,WAAU;gBAAf,CACE,iBAAA,GAAA,kBAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAY,YAAY,WAAW;OACnC,SAAS;OACT,WAAU;iBAET,YAAY,MAAM;MACb,CAAA,GACR,iBAAA,GAAA,kBAAA,KAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,SAAS;OACT,WAAU;iBACX;MAEO,CAAA,CACL;;IACF;;GACJ,MAAM;GACN,CAAC,aACA,iBAAA,GAAA,kBAAA,KAAC,QAAD;IACE,WAAU;IACV,eAAe,cAAc,QAAQ;IACrC,cAAW;IACX,MAAK;GACN,CAAA;EAEA;;AAET;AAEA,SAAS,gBAAgB,GAA6B;CACpD,IAAI,OAAO,WAAW,aAAa,OAAO;CAC1C,MAAM,KAAK,OAAO;CAClB,MAAM,KAAK,OAAO;CAClB,MAAM,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,SAAS,CAAC;CACvC,MAAM,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,SAAS,CAAC;CACvC,MAAM,IAAI,KAAK,IAAI,QAAQ,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,MAAM,CAAC;CACzD,MAAM,IAAI,KAAK,IAAI,QAAQ,KAAK,IAAI,EAAE,GAAG,KAAK,WAAW,MAAM,CAAC;CAChE,OAAO;EAAE,GAAG;EAAG;EAAG;EAAG;EAAG;CAAE;AAC5B;AAEA,SAAS,UACP,YACA,SACa;CACb,MAAM,KAAK,OAAO,WAAW,cAAc,OAAO,aAAa;CAC/D,MAAM,KAAK,OAAO,WAAW,cAAc,OAAO,cAAc;CAChE,MAAM,IAAI,SAAS,KAAK;CACxB,MAAM,IAAI,SAAS,KAAK;CACxB,MAAM,WAAwB;EAC5B,GAAG,SAAS,KAAK,KAAK,IAAI,QAAQ,KAAK,IAAI,MAAM;EACjD,GAAG,SAAS,KAAK,KAAK,IAAI,QAAQ,KAAK,IAAI,SAAS,EAAE;EACtD;EACA;EACA,MAAM;EACN,WAAW;CACb;CACA,IAAI,OAAO,iBAAiB,aAAa,OAAO;CAChD,IAAI;EACF,MAAM,MAAM,aAAa,QAAQ,UAAU;EAC3C,IAAI,CAAC,KAAK,OAAO;EACjB,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,OAAO,gBAAgB;GACrB,GAAG,OAAO,KAAK,SAAS;GACxB,GAAG,OAAO,KAAK,SAAS;GACxB,GAAG,OAAO,KAAK,SAAS;GACxB,GAAG,OAAO,KAAK,SAAS;GACxB,MAAM,OAAO,QAAQ;GACrB,WAAW,OAAO,aAAa;EACjC,CAAC;CACH,QAAQ;EACN,OAAO;CACT;AACF"}
|
package/dist/index.d.cts
CHANGED
|
@@ -185,6 +185,12 @@ declare class DevtoolsStore {
|
|
|
185
185
|
private pushMutation;
|
|
186
186
|
private pushField;
|
|
187
187
|
private consumeStart;
|
|
188
|
+
/**
|
|
189
|
+
* Drop every pending mutation-start record under `path` (and its
|
|
190
|
+
* descendants). Called on `controller:disposed` so a dispose mid-mutation
|
|
191
|
+
* doesn't leave a permanent entry in `mutationStarts`.
|
|
192
|
+
*/
|
|
193
|
+
private dropStartsForPath;
|
|
188
194
|
}
|
|
189
195
|
/**
|
|
190
196
|
* Insert (or update) a node at `path` inside the tree. Auto-creates any
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx","../src/format.ts","../src/store.ts"],"mappings":";;;;KAcY,WAAA;AAAA,KAEA,kBAAA;EAFA,4EAIV,IAAA,EAAM,IAAA,CAAK,IAAA;EAEX,UAAA,GAAa,WAAA,EANQ;EAQrB,UAAA;EAN4B;;;;EAW5B,UAAA,WAPwB;EASxB,eAAA;AAAA;;;;;;;;;AAAe;AAmBjB;;;;;;;iBAAgB,aAAA,CAAc,KAAA,EAAO,kBAAA,GAAqB,YAAY;;;KC7B1D,qBAAA;EACV,IAAA,EAAM,IAAA,CAAK,IAAA,uBDNU;ECQrB,UAAA,GAAa,WAAA,EDRQ;ECUrB,UAAA,WDRU;ECUV,UAAA;EAEA,UAAA,WDVM;ECYN,OAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;EAAA;AAAA;AAAA,iBAmBlC,gBAAA,CAAiB,KAAA,EAAO,qBAAA,GAAwB,YAAY;;;;;;;iBC7C5D,aAAA,CAAc,KAAA,WAAgB,MAAY;;iBAsB1C,UAAA,CAAW,CAAS;;iBAOpB,UAAA,CAAW,IAAwB;;;;;AFnBnD;;;KGNY,cAAA;EAAA,SACD,IAAA;EACT,KAAA;EACA,KAAA;EACA,QAAA,EAAU,cAAc;AAAA;;KAId,UAAA;EAEN,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,cAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAE5C,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAC5C,EAAA;EAAY,CAAA;EAAW,IAAA;EAAY,QAAA;AAAA;;;KAI7B,aAAA;EACN,EAAA;EAAY,CAAA;EAAW,IAAA;EAAa,IAAA;EAAyB,IAAA;EAAe,IAAA;AAAA;EAE5E,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,UAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAkB,IAAA;EAAyB,IAAA;AAAA;;KAG5D,UAAA;EACV,EAAA;EACA,CAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,KAMU,oBAAA;EAxEc,iFA0ExB,UAAA,WAtEwB;EAwExB,GAAG;AAAA;;;;;AAxEqB;AAI1B;;cA8Ea,aAAA;EAAA,SACF,KAAA,EAAO,MAAA,CAAO,cAAA;EAAA,SACd,MAAA,EAAQ,MAAA,CAAO,UAAA;EAAA,SACf,UAAA,EAAY,MAAA,CAAO,aAAA;EAAA,SACnB,OAAA,EAAS,MAAA,CAAO,UAAA;EAAA,iBAER,UAAA;EAAA,iBACA,GAAA;EAAA,QACT,MAAA;EA9EQ;;EAAA,QAkFR,cAAA;cAEI,OAAA,GAAU,oBAAA;EAjFlB;;;;;EA2FJ,MAAA,CAAO,IAAA,EAAM,IAAA,CAAK,IAAA;EAnFd;EAwFJ,MAAA,CAAO,KAAA,EAAO,UAAA;EAtFV;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx","../src/format.ts","../src/store.ts"],"mappings":";;;;KAcY,WAAA;AAAA,KAEA,kBAAA;EAFA,4EAIV,IAAA,EAAM,IAAA,CAAK,IAAA;EAEX,UAAA,GAAa,WAAA,EANQ;EAQrB,UAAA;EAN4B;;;;EAW5B,UAAA,WAPwB;EASxB,eAAA;AAAA;;;;;;;;;AAAe;AAmBjB;;;;;;;iBAAgB,aAAA,CAAc,KAAA,EAAO,kBAAA,GAAqB,YAAY;;;KC7B1D,qBAAA;EACV,IAAA,EAAM,IAAA,CAAK,IAAA,uBDNU;ECQrB,UAAA,GAAa,WAAA,EDRQ;ECUrB,UAAA,WDRU;ECUV,UAAA;EAEA,UAAA,WDVM;ECYN,OAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;EAAA;AAAA;AAAA,iBAmBlC,gBAAA,CAAiB,KAAA,EAAO,qBAAA,GAAwB,YAAY;;;;;;;iBC7C5D,aAAA,CAAc,KAAA,WAAgB,MAAY;;iBAsB1C,UAAA,CAAW,CAAS;;iBAOpB,UAAA,CAAW,IAAwB;;;;;AFnBnD;;;KGNY,cAAA;EAAA,SACD,IAAA;EACT,KAAA;EACA,KAAA;EACA,QAAA,EAAU,cAAc;AAAA;;KAId,UAAA;EAEN,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,cAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAE5C,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAC5C,EAAA;EAAY,CAAA;EAAW,IAAA;EAAY,QAAA;AAAA;;;KAI7B,aAAA;EACN,EAAA;EAAY,CAAA;EAAW,IAAA;EAAa,IAAA;EAAyB,IAAA;EAAe,IAAA;AAAA;EAE5E,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,UAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAkB,IAAA;EAAyB,IAAA;AAAA;;KAG5D,UAAA;EACV,EAAA;EACA,CAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,KAMU,oBAAA;EAxEc,iFA0ExB,UAAA,WAtEwB;EAwExB,GAAG;AAAA;;;;;AAxEqB;AAI1B;;cA8Ea,aAAA;EAAA,SACF,KAAA,EAAO,MAAA,CAAO,cAAA;EAAA,SACd,MAAA,EAAQ,MAAA,CAAO,UAAA;EAAA,SACf,UAAA,EAAY,MAAA,CAAO,aAAA;EAAA,SACnB,OAAA,EAAS,MAAA,CAAO,UAAA;EAAA,iBAER,UAAA;EAAA,iBACA,GAAA;EAAA,QACT,MAAA;EA9EQ;;EAAA,QAkFR,cAAA;cAEI,OAAA,GAAU,oBAAA;EAjFlB;;;;;EA2FJ,MAAA,CAAO,IAAA,EAAM,IAAA,CAAK,IAAA;EAnFd;EAwFJ,MAAA,CAAO,KAAA,EAAO,UAAA;EAtFV;EAiLJ,SAAA,CAAA;EAAA,QAcQ,SAAA;EAAA,QAKA,YAAA;EAAA,QAKA,SAAA;EAAA,QAKA,YAAA;EA1MJ;;;;;EAAA,QAuNI,iBAAA;AAAA;;;;;;;;iBA6CM,UAAA,CACd,IAAA,EAAM,cAAA,EACN,IAAA,qBACA,KAAA,YACC,cAAc;;;;;iBA0CD,YAAA,CACd,IAAA,EAAM,cAAA,EACN,IAAA,qBACA,KAAA,EAAO,cAAA,YACN,cAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -185,6 +185,12 @@ declare class DevtoolsStore {
|
|
|
185
185
|
private pushMutation;
|
|
186
186
|
private pushField;
|
|
187
187
|
private consumeStart;
|
|
188
|
+
/**
|
|
189
|
+
* Drop every pending mutation-start record under `path` (and its
|
|
190
|
+
* descendants). Called on `controller:disposed` so a dispose mid-mutation
|
|
191
|
+
* doesn't leave a permanent entry in `mutationStarts`.
|
|
192
|
+
*/
|
|
193
|
+
private dropStartsForPath;
|
|
188
194
|
}
|
|
189
195
|
/**
|
|
190
196
|
* Insert (or update) a node at `path` inside the tree. Auto-creates any
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx","../src/format.ts","../src/store.ts"],"mappings":";;;;KAcY,WAAA;AAAA,KAEA,kBAAA;EAFA,4EAIV,IAAA,EAAM,IAAA,CAAK,IAAA;EAEX,UAAA,GAAa,WAAA,EANQ;EAQrB,UAAA;EAN4B;;;;EAW5B,UAAA,WAPwB;EASxB,eAAA;AAAA;;;;;;;;;AAAe;AAmBjB;;;;;;;iBAAgB,aAAA,CAAc,KAAA,EAAO,kBAAA,GAAqB,YAAY;;;KC7B1D,qBAAA;EACV,IAAA,EAAM,IAAA,CAAK,IAAA,uBDNU;ECQrB,UAAA,GAAa,WAAA,EDRQ;ECUrB,UAAA,WDRU;ECUV,UAAA;EAEA,UAAA,WDVM;ECYN,OAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;EAAA;AAAA;AAAA,iBAmBlC,gBAAA,CAAiB,KAAA,EAAO,qBAAA,GAAwB,YAAY;;;;;;;iBC7C5D,aAAA,CAAc,KAAA,WAAgB,MAAY;;iBAsB1C,UAAA,CAAW,CAAS;;iBAOpB,UAAA,CAAW,IAAwB;;;;;AFnBnD;;;KGNY,cAAA;EAAA,SACD,IAAA;EACT,KAAA;EACA,KAAA;EACA,QAAA,EAAU,cAAc;AAAA;;KAId,UAAA;EAEN,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,cAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAE5C,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAC5C,EAAA;EAAY,CAAA;EAAW,IAAA;EAAY,QAAA;AAAA;;;KAI7B,aAAA;EACN,EAAA;EAAY,CAAA;EAAW,IAAA;EAAa,IAAA;EAAyB,IAAA;EAAe,IAAA;AAAA;EAE5E,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,UAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAkB,IAAA;EAAyB,IAAA;AAAA;;KAG5D,UAAA;EACV,EAAA;EACA,CAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,KAMU,oBAAA;EAxEc,iFA0ExB,UAAA,WAtEwB;EAwExB,GAAG;AAAA;;;;;AAxEqB;AAI1B;;cA8Ea,aAAA;EAAA,SACF,KAAA,EAAO,MAAA,CAAO,cAAA;EAAA,SACd,MAAA,EAAQ,MAAA,CAAO,UAAA;EAAA,SACf,UAAA,EAAY,MAAA,CAAO,aAAA;EAAA,SACnB,OAAA,EAAS,MAAA,CAAO,UAAA;EAAA,iBAER,UAAA;EAAA,iBACA,GAAA;EAAA,QACT,MAAA;EA9EQ;;EAAA,QAkFR,cAAA;cAEI,OAAA,GAAU,oBAAA;EAjFlB;;;;;EA2FJ,MAAA,CAAO,IAAA,EAAM,IAAA,CAAK,IAAA;EAnFd;EAwFJ,MAAA,CAAO,KAAA,EAAO,UAAA;EAtFV;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx","../src/format.ts","../src/store.ts"],"mappings":";;;;KAcY,WAAA;AAAA,KAEA,kBAAA;EAFA,4EAIV,IAAA,EAAM,IAAA,CAAK,IAAA;EAEX,UAAA,GAAa,WAAA,EANQ;EAQrB,UAAA;EAN4B;;;;EAW5B,UAAA,WAPwB;EASxB,eAAA;AAAA;;;;;;;;;AAAe;AAmBjB;;;;;;;iBAAgB,aAAA,CAAc,KAAA,EAAO,kBAAA,GAAqB,YAAY;;;KC7B1D,qBAAA;EACV,IAAA,EAAM,IAAA,CAAK,IAAA,uBDNU;ECQrB,UAAA,GAAa,WAAA,EDRQ;ECUrB,UAAA,WDRU;ECUV,UAAA;EAEA,UAAA,WDVM;ECYN,OAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;IAAY,CAAA;EAAA;AAAA;AAAA,iBAmBlC,gBAAA,CAAiB,KAAA,EAAO,qBAAA,GAAwB,YAAY;;;;;;;iBC7C5D,aAAA,CAAc,KAAA,WAAgB,MAAY;;iBAsB1C,UAAA,CAAW,CAAS;;iBAOpB,UAAA,CAAW,IAAwB;;;;;AFnBnD;;;KGNY,cAAA;EAAA,SACD,IAAA;EACT,KAAA;EACA,KAAA;EACA,QAAA,EAAU,cAAc;AAAA;;KAId,UAAA;EAEN,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,cAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAE5C,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAqB,QAAA;AAAA;EAC5C,EAAA;EAAY,CAAA;EAAW,IAAA;EAAY,QAAA;AAAA;;;KAI7B,aAAA;EACN,EAAA;EAAY,CAAA;EAAW,IAAA;EAAa,IAAA;EAAyB,IAAA;EAAe,IAAA;AAAA;EAE5E,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;EAGA,EAAA;EACA,CAAA;EACA,IAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,UAAA;AAAA;EAEA,EAAA;EAAY,CAAA;EAAW,IAAA;EAAkB,IAAA;EAAyB,IAAA;AAAA;;KAG5D,UAAA;EACV,EAAA;EACA,CAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,KAMU,oBAAA;EAxEc,iFA0ExB,UAAA,WAtEwB;EAwExB,GAAG;AAAA;;;;;AAxEqB;AAI1B;;cA8Ea,aAAA;EAAA,SACF,KAAA,EAAO,MAAA,CAAO,cAAA;EAAA,SACd,MAAA,EAAQ,MAAA,CAAO,UAAA;EAAA,SACf,UAAA,EAAY,MAAA,CAAO,aAAA;EAAA,SACnB,OAAA,EAAS,MAAA,CAAO,UAAA;EAAA,iBAER,UAAA;EAAA,iBACA,GAAA;EAAA,QACT,MAAA;EA9EQ;;EAAA,QAkFR,cAAA;cAEI,OAAA,GAAU,oBAAA;EAjFlB;;;;;EA2FJ,MAAA,CAAO,IAAA,EAAM,IAAA,CAAK,IAAA;EAnFd;EAwFJ,MAAA,CAAO,KAAA,EAAO,UAAA;EAtFV;EAiLJ,SAAA,CAAA;EAAA,QAcQ,SAAA;EAAA,QAKA,YAAA;EAAA,QAKA,SAAA;EAAA,QAKA,YAAA;EA1MJ;;;;;EAAA,QAuNI,iBAAA;AAAA;;;;;;;;iBA6CM,UAAA,CACd,IAAA,EAAM,cAAA,EACN,IAAA,qBACA,KAAA,YACC,cAAc;;;;;iBA0CD,YAAA,CACd,IAAA,EAAM,cAAA,EACN,IAAA,qBACA,KAAA,EAAO,cAAA,YACN,cAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -265,6 +265,7 @@ var DevtoolsStore = class {
|
|
|
265
265
|
return;
|
|
266
266
|
case "controller:disposed":
|
|
267
267
|
this.tree$.set(setNodeState(this.tree$.peek(), event.path, "disposed"));
|
|
268
|
+
this.dropStartsForPath(event.path);
|
|
268
269
|
return;
|
|
269
270
|
case "cache:subscribed":
|
|
270
271
|
this.pushCache({
|
|
@@ -359,6 +360,7 @@ var DevtoolsStore = class {
|
|
|
359
360
|
this.cache$.set([]);
|
|
360
361
|
this.mutations$.set([]);
|
|
361
362
|
this.fields$.set([]);
|
|
363
|
+
this.mutationStarts.clear();
|
|
362
364
|
}
|
|
363
365
|
pushCache(entry) {
|
|
364
366
|
const full = {
|
|
@@ -391,6 +393,20 @@ var DevtoolsStore = class {
|
|
|
391
393
|
this.mutationStarts.delete(key);
|
|
392
394
|
return this.now() - startedAt;
|
|
393
395
|
}
|
|
396
|
+
/**
|
|
397
|
+
* Drop every pending mutation-start record under `path` (and its
|
|
398
|
+
* descendants). Called on `controller:disposed` so a dispose mid-mutation
|
|
399
|
+
* doesn't leave a permanent entry in `mutationStarts`.
|
|
400
|
+
*/
|
|
401
|
+
dropStartsForPath(path) {
|
|
402
|
+
if (this.mutationStarts.size === 0) return;
|
|
403
|
+
const prefix = `${path.join(">")}>`;
|
|
404
|
+
const exact = path.join(">");
|
|
405
|
+
for (const key of this.mutationStarts.keys()) {
|
|
406
|
+
const beforeHash = key.split("#")[0] ?? "";
|
|
407
|
+
if (beforeHash === exact || beforeHash.startsWith(prefix)) this.mutationStarts.delete(key);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
394
410
|
};
|
|
395
411
|
function mutationKey(path, name) {
|
|
396
412
|
return `${path.join(">")}#${name ?? ""}`;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/format.ts","../src/JsonView.tsx","../src/store.ts","../src/styles.ts","../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx"],"sourcesContent":["/**\n * Render a payload (props, vars, result, etc.) as a single-line string for\n * the panel. Cuts at `maxLen` so a giant blob doesn't blow up the layout.\n */\nexport function formatPayload(value: unknown, maxLen = 200): string {\n if (value === undefined) return 'undefined'\n if (value === null) return 'null'\n if (typeof value === 'function') return '[fn]'\n let s: string\n try {\n s = JSON.stringify(value, replaceUnserializable)\n } catch {\n s = String(value)\n }\n if (s === undefined) s = String(value)\n return s.length > maxLen ? `${s.slice(0, maxLen)}…` : s\n}\n\nfunction replaceUnserializable(_key: string, value: unknown): unknown {\n if (typeof value === 'function') return '[fn]'\n if (typeof value === 'bigint') return value.toString()\n if (value instanceof Error) return { name: value.name, message: value.message }\n return value\n}\n\n/** Render an HH:MM:SS.mmm timestamp from epoch ms. */\nexport function formatTime(t: number): string {\n const d = new Date(t)\n const pad = (n: number, w = 2) => n.toString().padStart(w, '0')\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`\n}\n\n/** Render a controller path / query key as a compact string. */\nexport function formatPath(path: readonly unknown[]): string {\n if (path.length === 0) return '∅'\n return path.map((p) => String(p)).join(' › ')\n}\n","// Compact JSON renderer for the devtools panel payload column.\n//\n// Renders values inline by default. Objects/arrays show their summary\n// (\"{4}\", \"[12]\") inline; clicking the summary expands them. Each nested\n// level inherits the same expand/collapse semantics. Scoped to the\n// `olas-devtools-json-*` class prefix; styles are in `styles.ts`.\n\nimport { type ReactElement, useState } from 'react'\n\nexport function JsonView({ value, depth = 0 }: { value: unknown; depth?: number }): ReactElement {\n return <Render value={value} depth={depth} initiallyOpen={depth === 0} />\n}\n\nfunction Render({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n if (value === null) return <span className=\"olas-devtools-json-null\">null</span>\n if (value === undefined) return <span className=\"olas-devtools-json-null\">undefined</span>\n\n const t = typeof value\n if (t === 'string') return <span className=\"olas-devtools-json-string\">\"{value as string}\"</span>\n if (t === 'number') return <span className=\"olas-devtools-json-number\">{String(value)}</span>\n if (t === 'boolean') return <span className=\"olas-devtools-json-boolean\">{String(value)}</span>\n if (t === 'bigint') return <span className=\"olas-devtools-json-number\">{String(value)}n</span>\n\n // Errors render as `Error(\"message\")` so they're distinguishable from\n // plain string payloads.\n if (value instanceof Error) {\n return (\n <span className=\"olas-devtools-json-error\">\n {value.name}({JSON.stringify(value.message)})\n </span>\n )\n }\n\n if (Array.isArray(value)) {\n return <CollapsibleArray value={value} depth={depth} initiallyOpen={initiallyOpen} />\n }\n\n if (t === 'object') {\n return (\n <CollapsibleObject\n value={value as Record<string, unknown>}\n depth={depth}\n initiallyOpen={initiallyOpen}\n />\n )\n }\n\n return <span>{String(value)}</span>\n}\n\nfunction CollapsibleArray({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown[]\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const [open, setOpen] = useState(initiallyOpen && value.length <= 12)\n if (value.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">[]</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">[</span>\n <span className=\"olas-devtools-json-summary\">\n {value.length} item{value.length === 1 ? '' : 's'}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">[</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {value.map((item, idx) => (\n <span key={idx} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-index\">{idx}:</span>\n <Render value={item} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </span>\n )\n}\n\nfunction CollapsibleObject({\n value,\n depth,\n initiallyOpen,\n}: {\n value: Record<string, unknown>\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const keys = Object.keys(value)\n const [open, setOpen] = useState(initiallyOpen && keys.length <= 8)\n if (keys.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">{'{}'}</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n <span className=\"olas-devtools-json-summary\">\n {keys.slice(0, 3).join(', ')}\n {keys.length > 3 ? ` +${keys.length - 3}` : ''}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {keys.map((k) => (\n <span key={k} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-key\">{k}:</span>\n <Render value={value[k]} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </span>\n )\n}\n","import type { DebugEvent, Root } from '@kontsedal/olas-core'\nimport { type Signal, signal } from '@kontsedal/olas-core'\n\n/**\n * Per-path node in the live controller tree. `state` reflects the most\n * recently observed lifecycle event; `path` is the array reported by the\n * devtools bus.\n */\nexport type ControllerNode = {\n readonly path: readonly string[]\n state: 'active' | 'suspended' | 'disposed'\n props: unknown\n children: ControllerNode[]\n}\n\n/** One entry in the cache timeline. */\nexport type CacheEntry =\n | {\n id: number\n t: number\n kind: 'subscribed'\n queryKey: readonly unknown[]\n subscriberPath: readonly string[]\n }\n | { id: number; t: number; kind: 'fetch-start'; queryKey: readonly unknown[] }\n | {\n id: number\n t: number\n kind: 'fetch-success'\n queryKey: readonly unknown[]\n durationMs: number\n }\n | {\n id: number\n t: number\n kind: 'fetch-error'\n queryKey: readonly unknown[]\n durationMs: number\n error: unknown\n }\n | { id: number; t: number; kind: 'invalidated'; queryKey: readonly unknown[] }\n | { id: number; t: number; kind: 'gc'; queryKey: readonly unknown[] }\n\n/** One entry in the mutation log. `durationMs` is set on success/error when\n * the entry can be paired with a preceding `run` for the same path+name. */\nexport type MutationEntry =\n | { id: number; t: number; kind: 'run'; path: readonly string[]; name?: string; vars: unknown }\n | {\n id: number\n t: number\n kind: 'success'\n path: readonly string[]\n name?: string\n result: unknown\n durationMs?: number\n }\n | {\n id: number\n t: number\n kind: 'error'\n path: readonly string[]\n name?: string\n error: unknown\n durationMs?: number\n }\n | { id: number; t: number; kind: 'rollback'; path: readonly string[]; name?: string }\n\n/** One entry in the field validation log. */\nexport type FieldEntry = {\n id: number\n t: number\n path: readonly string[]\n field: string\n valid: boolean\n errors: string[]\n}\n\n/** Defaults — exported so callers can override via `new DevtoolsStore({ maxEntries: 500 })`. */\nexport const DEFAULT_MAX_ENTRIES = 100\n\nexport type DevtoolsStoreOptions = {\n /** Cap on each event log (cache, mutation, field). Oldest entries drop first. */\n maxEntries?: number\n /** Optional clock — useful for tests. Default: `() => Date.now()`. */\n now?: () => number\n}\n\n/**\n * Subscribes to a root's `__debug` bus and maintains live state for the\n * devtools panel. Exposes signals so the React layer can consume via\n * `@kontsedal/olas-react`'s `use()`.\n *\n * Pure logic — no DOM, no React. Construct one per root.\n */\nexport class DevtoolsStore {\n readonly tree$: Signal<ControllerNode> = signal(makeRoot())\n readonly cache$: Signal<CacheEntry[]> = signal([])\n readonly mutations$: Signal<MutationEntry[]> = signal([])\n readonly fields$: Signal<FieldEntry[]> = signal([])\n\n private readonly maxEntries: number\n private readonly now: () => number\n private nextId = 1\n\n /** Keyed by `path|name` so a mutation:run can be paired with its\n * success/error to compute duration. Cleared after pairing. */\n private mutationStarts = new Map<string, number>()\n\n constructor(options?: DevtoolsStoreOptions) {\n this.maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES\n this.now = options?.now ?? (() => Date.now())\n }\n\n /**\n * Subscribe to the given root's debug bus. Returns the unsubscribe. The\n * caller (typically the React component) is responsible for invoking it\n * on unmount.\n */\n attach(root: Pick<Root<unknown>, '__debug'>): () => void {\n return root.__debug.subscribe((event) => this.handle(event))\n }\n\n /** Apply one event. Exposed for tests. */\n handle(event: DebugEvent): void {\n switch (event.type) {\n case 'controller:constructed':\n this.tree$.set(insertNode(this.tree$.peek(), event.path, event.props))\n return\n case 'controller:suspended':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'suspended'))\n return\n case 'controller:resumed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'active'))\n return\n case 'controller:disposed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'disposed'))\n return\n case 'cache:subscribed':\n this.pushCache({\n kind: 'subscribed',\n queryKey: event.queryKey,\n subscriberPath: event.subscriberPath,\n })\n return\n case 'cache:fetch-start':\n this.pushCache({ kind: 'fetch-start', queryKey: event.queryKey })\n return\n case 'cache:fetch-success':\n this.pushCache({\n kind: 'fetch-success',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n })\n return\n case 'cache:fetch-error':\n this.pushCache({\n kind: 'fetch-error',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n error: event.error,\n })\n return\n case 'cache:invalidated':\n this.pushCache({ kind: 'invalidated', queryKey: event.queryKey })\n return\n case 'cache:gc':\n this.pushCache({ kind: 'gc', queryKey: event.queryKey })\n return\n case 'mutation:run': {\n this.mutationStarts.set(mutationKey(event.path, event.name), this.now())\n this.pushMutation({ kind: 'run', path: event.path, name: event.name, vars: event.vars })\n return\n }\n case 'mutation:success': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'success',\n path: event.path,\n name: event.name,\n result: event.result,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:error': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'error',\n path: event.path,\n name: event.name,\n error: event.error,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:rollback':\n this.pushMutation({ kind: 'rollback', path: event.path, name: event.name })\n return\n case 'field:validated':\n this.pushField({\n path: event.path,\n field: event.field,\n valid: event.valid,\n errors: event.errors,\n })\n return\n }\n }\n\n /** Clear every log. Tree state is preserved — the live tree is not a log. */\n clearLogs(): void {\n this.cache$.set([])\n this.mutations$.set([])\n this.fields$.set([])\n }\n\n // -----------------------------------------------------------------------\n // Internals\n // -----------------------------------------------------------------------\n\n private pushCache(entry: DistributiveOmit<CacheEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as CacheEntry\n this.cache$.set(appendBounded(this.cache$.peek(), full, this.maxEntries))\n }\n\n private pushMutation(entry: DistributiveOmit<MutationEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as MutationEntry\n this.mutations$.set(appendBounded(this.mutations$.peek(), full, this.maxEntries))\n }\n\n private pushField(entry: Omit<FieldEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as FieldEntry\n this.fields$.set(appendBounded(this.fields$.peek(), full, this.maxEntries))\n }\n\n private consumeStart(path: readonly string[], name: string | undefined): number | undefined {\n const key = mutationKey(path, name)\n const startedAt = this.mutationStarts.get(key)\n if (startedAt === undefined) return undefined\n this.mutationStarts.delete(key)\n return this.now() - startedAt\n }\n}\n\nfunction mutationKey(path: readonly string[], name: string | undefined): string {\n return `${path.join('>')}#${name ?? ''}`\n}\n\n/**\n * Distributes `Omit` over a discriminated union so each variant keeps its own\n * keys. The default `Omit<A | B, K>` collapses to the intersection of keys —\n * not what we want when constructing one variant at a time.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n// ---------------------------------------------------------------------------\n// Pure helpers — tested independently of the class.\n// ---------------------------------------------------------------------------\n\nfunction makeRoot(): ControllerNode {\n return { path: [], state: 'active', props: undefined, children: [] }\n}\n\nfunction appendBounded<T>(arr: readonly T[], item: T, max: number): T[] {\n const next = arr.length >= max ? arr.slice(arr.length - max + 1) : arr.slice()\n next.push(item)\n return next\n}\n\n/**\n * Insert (or update) a node at `path` inside the tree. Auto-creates any\n * missing intermediate ancestors as 'active' placeholders — needed if the\n * subscriber attached after the root was constructed.\n *\n * Returns a NEW tree object (immutable update).\n */\nexport function insertNode(\n root: ControllerNode,\n path: readonly string[],\n props: unknown,\n): ControllerNode {\n if (path.length === 0) {\n // The root controller's \"constructed\" event has path === ['root']\n // (one segment), not []. We never receive empty paths in practice, but\n // handle defensively.\n return { ...root, state: 'active', props }\n }\n return cloneWithUpsert(root, path, 0, props)\n}\n\nfunction cloneWithUpsert(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n props: unknown,\n): ControllerNode {\n if (depth === path.length) {\n return { ...node, state: 'active', props }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n const childPath = path.slice(0, depth + 1)\n if (idx === -1) {\n const newChild = cloneWithUpsert(\n { path: childPath, state: 'active', props: undefined, children: [] },\n path,\n depth + 1,\n props,\n )\n return { ...node, children: [...node.children, newChild] }\n }\n const existing = node.children[idx]!\n const updatedChild = cloneWithUpsert(existing, path, depth + 1, props)\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n\n/**\n * Set `state` on the node at `path`. If the node doesn't exist (out-of-order\n * event delivery), the tree is returned unchanged.\n */\nexport function setNodeState(\n root: ControllerNode,\n path: readonly string[],\n state: ControllerNode['state'],\n): ControllerNode {\n if (path.length === 0) {\n return { ...root, state }\n }\n return setStateAt(root, path, 0, state) ?? root\n}\n\nfunction setStateAt(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n state: ControllerNode['state'],\n): ControllerNode | null {\n if (depth === path.length) {\n return { ...node, state }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n if (idx === -1) return null\n const existing = node.children[idx]!\n const updatedChild = setStateAt(existing, path, depth + 1, state)\n if (updatedChild === null) return null\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n","/**\n * Inline CSS for the devtools panel. Scoped to the `.olas-devtools-*` class\n * prefix so it doesn't bleed into the host app. Honors `prefers-color-scheme`\n * and accepts host palette overrides via `--olas-*` custom properties.\n */\nexport const DEVTOOLS_CSS = `\n.olas-devtools {\n container-type: inline-size;\n --olas-bg: #ffffff;\n --olas-fg: #1f2330;\n --olas-muted: #6b7280;\n --olas-soft: #f5f6f9;\n --olas-soft-2: #eef0f4;\n --olas-accent: #4f46e5;\n --olas-accent-soft: rgba(79,70,229,0.10);\n --olas-success: #1e8a3d;\n --olas-success-soft: rgba(30,138,61,0.12);\n --olas-warn: #ad6800;\n --olas-warn-soft: rgba(173,104,0,0.12);\n --olas-error: #b8361f;\n --olas-error-soft: rgba(184,54,31,0.12);\n --olas-border: #e6e6ea;\n --olas-border-soft: #eeeef2;\n --olas-row-alt: #fafafc;\n\n /* JSON viewer colors */\n --olas-json-key: #7a4ab8;\n --olas-json-string: #1e8a3d;\n --olas-json-number: #b25400;\n --olas-json-boolean: #4f46e5;\n --olas-json-null: #9aa0aa;\n --olas-json-bracket: #6b7280;\n --olas-json-summary: #6b7280;\n\n font-family: -apple-system, BlinkMacSystemFont, \"Inter var\", \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12.5px;\n line-height: 1.55;\n color: var(--olas-fg);\n background: var(--olas-bg);\n border: 1px solid var(--olas-border);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 320px;\n overflow: hidden;\n box-sizing: border-box;\n}\n.olas-devtools *,\n.olas-devtools *::before,\n.olas-devtools *::after { box-sizing: border-box; }\n\n@media (prefers-color-scheme: dark) {\n .olas-devtools {\n --olas-bg: #15171e;\n --olas-fg: #e8ebf2;\n --olas-muted: #97a0b3;\n --olas-soft: #1d2029;\n --olas-soft-2: #232733;\n --olas-accent: #8b86f0;\n --olas-accent-soft: rgba(139,134,240,0.18);\n --olas-success: #4ade80;\n --olas-success-soft: rgba(74,222,128,0.16);\n --olas-warn: #f5b740;\n --olas-warn-soft: rgba(245,183,64,0.16);\n --olas-error: #ef6b53;\n --olas-error-soft: rgba(239,107,83,0.16);\n --olas-border: #2a2e3a;\n --olas-border-soft: #20232c;\n --olas-row-alt: #1a1d25;\n\n --olas-json-key: #c39bff;\n --olas-json-string: #7ee79d;\n --olas-json-number: #f5b740;\n --olas-json-boolean: #8b86f0;\n --olas-json-null: #7c8090;\n --olas-json-bracket: #97a0b3;\n --olas-json-summary: #97a0b3;\n }\n}\n\n/* ---- tabs ------------------------------------------------------------- */\n.olas-devtools-tabs {\n display: flex;\n align-items: center;\n gap: 1px;\n border-bottom: 1px solid var(--olas-border);\n background: var(--olas-soft);\n padding: 0 8px;\n flex-shrink: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.olas-devtools-tabs::-webkit-scrollbar { display: none; }\n.olas-devtools-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 10px 8px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n cursor: pointer;\n font: inherit;\n font-weight: 500;\n font-size: 12px;\n white-space: nowrap;\n flex-shrink: 0;\n transition: color 80ms, border-color 80ms;\n}\n.olas-devtools-tab-label-full { display: inline; }\n.olas-devtools-tab-label-short { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-tab { padding: 9px 8px 8px; font-size: 11.5px; gap: 4px; }\n .olas-devtools-tab-label-full { display: none; }\n .olas-devtools-tab-label-short { display: inline; }\n .olas-devtools-pause-text,\n .olas-devtools-clear-text { display: none; }\n}\n.olas-devtools-tab:hover { color: var(--olas-fg); }\n.olas-devtools-tab[aria-selected=\"true\"] {\n color: var(--olas-fg);\n border-bottom-color: var(--olas-accent);\n}\n.olas-devtools-tab[aria-selected=\"true\"] .olas-devtools-tab-count {\n background: var(--olas-accent-soft);\n color: var(--olas-accent);\n}\n.olas-devtools-tab-count {\n min-width: 18px;\n padding: 0 6px;\n height: 16px;\n border-radius: 999px;\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n color: var(--olas-muted);\n font-size: 10.5px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n}\n.olas-devtools-pause,\n.olas-devtools-clear {\n padding: 4px 10px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n font: inherit;\n font-size: 11.5px;\n align-self: center;\n}\n.olas-devtools-pause { margin-left: auto; }\n.olas-devtools-pause:hover,\n.olas-devtools-clear:hover {\n color: var(--olas-fg);\n background: color-mix(in oklch, var(--olas-bg) 60%, transparent);\n}\n.olas-devtools-pause-on { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-pause-on:hover { color: var(--olas-warn); }\n.olas-devtools-clear-icon { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-clear-text { display: none; }\n .olas-devtools-clear-icon { display: inline; }\n .olas-devtools-pause, .olas-devtools-clear { padding: 4px 8px; }\n}\n\n/* ---- filter ---------------------------------------------------------- */\n.olas-devtools-filter {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--olas-border-soft);\n background: var(--olas-bg);\n}\n.olas-devtools-filter input {\n flex: 1;\n padding: 5px 9px;\n border: 1px solid var(--olas-border);\n border-radius: 6px;\n background: var(--olas-soft);\n color: var(--olas-fg);\n font: inherit;\n font-size: 12px;\n outline: none;\n transition: border-color 80ms, box-shadow 80ms;\n}\n.olas-devtools-filter input:focus {\n border-color: var(--olas-accent);\n box-shadow: 0 0 0 2px var(--olas-accent-soft);\n}\n.olas-devtools-filter input::placeholder { color: var(--olas-muted); }\n.olas-devtools-filter button {\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n cursor: pointer;\n font: inherit;\n font-size: 13px;\n padding: 2px 6px;\n}\n.olas-devtools-filter button:hover { color: var(--olas-fg); }\n\n/* ---- body ------------------------------------------------------------ */\n.olas-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n/* ---- list rows ------------------------------------------------------- */\n.olas-devtools-list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.olas-devtools-list li {\n border-bottom: 1px solid var(--olas-border-soft);\n display: flex;\n flex-direction: column;\n}\n.olas-devtools-list li:last-child { border-bottom: none; }\n.olas-devtools-list li:hover { background: var(--olas-row-alt); }\n\n.olas-devtools-row-top {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n min-height: 32px;\n}\n.olas-devtools-row-clickable .olas-devtools-row-top { cursor: pointer; user-select: none; }\n\n.olas-devtools-target {\n flex: 1;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n word-break: break-word;\n min-width: 0;\n}\n.olas-devtools-target strong { font-weight: 600; color: var(--olas-accent); }\n.olas-devtools-time {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.olas-devtools-chevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--olas-muted);\n font-size: 12px;\n width: 16px;\n height: 16px;\n transition: transform 100ms;\n user-select: none;\n}\n.olas-devtools-chevron-open { transform: rotate(90deg); color: var(--olas-fg); }\n\n.olas-devtools-kind {\n color: var(--olas-accent);\n background: var(--olas-accent-soft);\n border-radius: 4px;\n padding: 1px 7px;\n font-size: 10.5px;\n font-weight: 600;\n letter-spacing: 0.02em;\n white-space: nowrap;\n line-height: 1.4;\n font-variant-numeric: tabular-nums;\n}\n.olas-devtools-kind-success { color: var(--olas-success); background: var(--olas-success-soft); }\n.olas-devtools-kind-error { color: var(--olas-error); background: var(--olas-error-soft); }\n.olas-devtools-kind-warn,\n.olas-devtools-kind-rollback { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-duration {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n background: var(--olas-soft);\n border-radius: 4px;\n padding: 1px 6px;\n white-space: nowrap;\n}\n\n.olas-devtools-payload {\n margin: 0 12px 10px;\n padding: 8px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n overflow-x: auto;\n}\n.olas-devtools-payload-inline {\n margin-top: -4px;\n padding: 4px 10px;\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n font-size: 11px;\n}\n.olas-devtools-payload-json { line-height: 1.5; }\n\n/* ---- JSON viewer ----------------------------------------------------- */\n.olas-devtools-json-row {\n display: block;\n padding-left: 14px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.olas-devtools-json-children {\n display: block;\n border-left: 1px dashed var(--olas-border);\n margin-left: 4px;\n}\n.olas-devtools-json-block {\n display: inline-flex;\n flex-direction: column;\n vertical-align: top;\n}\n.olas-devtools-json-key {\n color: var(--olas-json-key);\n margin-right: 6px;\n font-weight: 500;\n}\n.olas-devtools-json-index {\n color: var(--olas-json-bracket);\n margin-right: 6px;\n}\n.olas-devtools-json-string { color: var(--olas-json-string); }\n.olas-devtools-json-number { color: var(--olas-json-number); }\n.olas-devtools-json-boolean { color: var(--olas-json-boolean); }\n.olas-devtools-json-null { color: var(--olas-json-null); font-style: italic; }\n.olas-devtools-json-bracket { color: var(--olas-json-bracket); }\n.olas-devtools-json-error { color: var(--olas-error); }\n.olas-devtools-json-summary {\n color: var(--olas-json-summary);\n font-style: italic;\n margin: 0 4px;\n}\n.olas-devtools-json-toggle {\n display: inline-flex;\n align-items: center;\n gap: 0;\n background: transparent;\n border: 0;\n padding: 0;\n margin: 0;\n cursor: pointer;\n color: inherit;\n font: inherit;\n}\n.olas-devtools-json-toggle:hover { background: var(--olas-accent-soft); border-radius: 3px; }\n\n/* ---- empty state ---------------------------------------------------- */\n.olas-devtools-empty {\n padding: 36px 24px;\n text-align: center;\n color: var(--olas-muted);\n}\n.olas-devtools-empty-title {\n color: var(--olas-fg);\n font-weight: 600;\n font-size: 13px;\n margin-bottom: 4px;\n}\n.olas-devtools-empty-hint {\n font-size: 12px;\n max-width: 320px;\n margin: 0 auto;\n line-height: 1.55;\n}\n\n/* ---- tree ------------------------------------------------------------ */\n.olas-devtools-tree { padding: 10px 12px; }\n.olas-devtools-tree-node {\n padding: 1px 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n}\n.olas-devtools-tree-row {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 1px 0;\n}\n.olas-devtools-tree-name { color: var(--olas-fg); font-weight: 500; }\n.olas-devtools-tree-state-active,\n.olas-devtools-tree-state-suspended,\n.olas-devtools-tree-state-disposed {\n border-radius: 4px;\n padding: 0 6px;\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.olas-devtools-tree-state-active {\n color: var(--olas-success);\n background: var(--olas-success-soft);\n}\n.olas-devtools-tree-state-suspended {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n}\n.olas-devtools-tree-state-disposed {\n color: var(--olas-muted);\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n}\n.olas-devtools-tree-pending {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n border-radius: 4px;\n padding: 0 6px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.olas-devtools-tree-props-toggle {\n background: transparent;\n border: 0;\n cursor: pointer;\n font: inherit;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--olas-muted);\n padding: 0 4px;\n border-radius: 4px;\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n.olas-devtools-tree-props-toggle:hover {\n color: var(--olas-fg);\n background: var(--olas-soft);\n}\n.olas-devtools-tree-props {\n margin: 4px 0 6px 8px;\n padding: 6px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n overflow-x: auto;\n}\n.olas-devtools-tree-children {\n margin-left: 8px;\n border-left: 1px dashed var(--olas-border);\n padding-left: 10px;\n margin-top: 2px;\n}\n\n/* ---- floating window + launcher ------------------------------------- */\n.olas-devtools-launcher {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 2147483645;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 11px;\n background: #1f2330;\n color: #e8ebf2;\n border: 1px solid #2a2e3a;\n border-radius: 999px;\n box-shadow: 0 2px 6px rgba(0,0,0,0.15), 0 8px 24px rgba(0,0,0,0.18);\n cursor: pointer;\n font: inherit;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12px;\n font-weight: 500;\n}\n.olas-devtools-launcher:hover { filter: brightness(1.1); }\n.olas-devtools-launcher-active { box-shadow: 0 0 0 2px rgba(139,134,240,0.5), 0 8px 24px rgba(0,0,0,0.18); }\n.olas-devtools-launcher-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #4ade80;\n box-shadow: 0 0 6px rgba(74,222,128,0.7);\n}\n.olas-devtools-launcher-label { letter-spacing: 0.01em; }\n\n.olas-devtools-floating {\n position: fixed;\n z-index: 2147483646;\n display: flex;\n flex-direction: column;\n background: var(--olas-bg, #ffffff);\n color: var(--olas-fg, #1f2330);\n border: 1px solid var(--olas-border, #e6e6ea);\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.18), 0 24px 64px rgba(0,0,0,0.18);\n overflow: hidden;\n /* Inherit the panel's own CSS vars when DevtoolsPanel is mounted inside. */\n}\n@media (prefers-color-scheme: dark) {\n .olas-devtools-floating {\n background: #15171e;\n color: #e8ebf2;\n border-color: #2a2e3a;\n box-shadow: 0 8px 24px rgba(0,0,0,0.5), 0 24px 64px rgba(0,0,0,0.5);\n }\n}\n.olas-devtools-floating-header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 8px 0 10px;\n background: var(--olas-soft, #f5f6f9);\n border-bottom: 1px solid var(--olas-border, #e6e6ea);\n cursor: grab;\n user-select: none;\n flex-shrink: 0;\n}\n.olas-devtools-floating-header:active { cursor: grabbing; }\n.olas-devtools-floating-grip {\n color: var(--olas-muted, #6b7280);\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-title {\n flex: 1;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--olas-fg, #1f2330);\n letter-spacing: 0.01em;\n}\n.olas-devtools-floating-actions { display: inline-flex; gap: 2px; }\n.olas-devtools-floating-action {\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n border-radius: 4px;\n color: var(--olas-muted, #6b7280);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-action:hover {\n color: var(--olas-fg, #1f2330);\n background: color-mix(in oklch, currentColor 14%, transparent);\n}\n.olas-devtools-floating-body {\n flex: 1;\n min-height: 0;\n display: flex;\n}\n.olas-devtools-floating-body > .olas-devtools {\n flex: 1;\n border: 0;\n border-radius: 0;\n min-height: 0;\n}\n.olas-devtools-floating-resize {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 14px;\n height: 14px;\n cursor: nwse-resize;\n background:\n linear-gradient(135deg, transparent 0 7px, var(--olas-muted, #6b7280) 7px 8px, transparent 8px 10px,\n var(--olas-muted, #6b7280) 10px 11px, transparent 11px 100%);\n opacity: 0.6;\n}\n.olas-devtools-floating-resize:hover { opacity: 1; }\n`\n","import type { DebugCacheEntry, Root } from '@kontsedal/olas-core'\nimport { use } from '@kontsedal/olas-react'\nimport { type ReactElement, useEffect, useMemo, useRef, useState } from 'react'\nimport { formatPath, formatTime } from './format'\nimport { JsonView } from './JsonView'\nimport {\n type CacheEntry,\n type ControllerNode,\n DevtoolsStore,\n type FieldEntry,\n type MutationEntry,\n} from './store'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsTab = 'tree' | 'cache' | 'inspector' | 'mutations' | 'fields'\n\nexport type DevtoolsPanelProps = {\n /** The root to inspect. The panel subscribes to `root.__debug` on mount. */\n root: Pick<Root<unknown>, '__debug'>\n /** Initial tab. Default: `'tree'`. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. Default: 100. */\n maxEntries?: number\n /**\n * Persist filter state to the URL hash under this key. When set,\n * reloading the page restores filter + tab. Default: no persistence.\n */\n urlHashKey?: string\n /** How often (ms) to refresh the live cache inspector snapshot. Default 800. */\n inspectorPollMs?: number\n}\n\n/**\n * Drop-in devtools panel for an Olas root.\n *\n * Features:\n * - **Tree** populated from the snapshot replay on mount (no lost events).\n * - **Cache / Mutations / Fields** event logs in reverse chronological order.\n * - **Filter** field per tab — text-matches kind, path, name, payload.\n * - **Pause** toggle freezes the log without stopping ingestion.\n * - **Click a row** to expand its payload from a truncated preview to the full\n * JSON.\n * - **Mutation durations** — `run → success/error` pairing surfaces elapsed ms.\n *\n * Styled inline (no CSS import needed) and scoped to the `.olas-devtools-*`\n * class prefix. Hosts override the palette via `--olas-*` custom properties.\n * Spec §13.\n */\nexport function DevtoolsPanel(props: DevtoolsPanelProps): ReactElement {\n const { root, defaultTab = 'tree', maxEntries, urlHashKey, inspectorPollMs = 800 } = props\n const store = useMemo(\n () => new DevtoolsStore(maxEntries !== undefined ? { maxEntries } : undefined),\n [maxEntries],\n )\n useEffect(() => store.attach(root), [root, store])\n\n // Initial state read from URL hash if `urlHashKey` is set.\n const initial = useMemo(() => readUrlHash(urlHashKey, defaultTab), [urlHashKey, defaultTab])\n const [tab, setTab] = useState<DevtoolsTab>(initial.tab)\n const [paused, setPaused] = useState(false)\n // Filters are kept per-tab so switching back doesn't lose the query.\n const [filters, setFilters] = useState<Record<DevtoolsTab, string>>(initial.filters)\n const filter = filters[tab]\n const setFilter = (q: string) => setFilters((prev) => ({ ...prev, [tab]: q }))\n\n // Persist tab + filters back to the URL hash on every change.\n useEffect(() => {\n if (urlHashKey === undefined) return\n writeUrlHash(urlHashKey, { tab, filters })\n }, [urlHashKey, tab, filters])\n\n // Live cache inspector — polls `root.__debug.queryEntries()` periodically.\n // Polling is cheap (a single peek per entry) and bounded by inspectorPollMs;\n // only the Cache Inspector view reads this.\n const [cacheEntries, setCacheEntries] = useState<DebugCacheEntry[]>([])\n const rootRef = useRef(root)\n rootRef.current = root\n useEffect(() => {\n if (tab !== 'inspector') return\n const tick = () => setCacheEntries(rootRef.current.__debug.queryEntries())\n tick()\n const id = window.setInterval(tick, inspectorPollMs)\n return () => window.clearInterval(id)\n }, [tab, inspectorPollMs])\n\n const liveTree = use(store.tree$)\n const liveCache = use(store.cache$)\n const liveMutations = use(store.mutations$)\n const liveFields = use(store.fields$)\n\n // When paused, snapshot once and keep showing that frozen state.\n const [frozen, setFrozen] = useState<{\n tree: ControllerNode\n cache: CacheEntry[]\n mutations: MutationEntry[]\n fields: FieldEntry[]\n } | null>(null)\n useEffect(() => {\n if (paused) {\n setFrozen({\n tree: liveTree,\n cache: liveCache,\n mutations: liveMutations,\n fields: liveFields,\n })\n } else {\n setFrozen(null)\n }\n // We only re-snapshot when the toggle flips, not on every event.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [paused])\n\n const tree = frozen?.tree ?? liveTree\n const cache = frozen?.cache ?? liveCache\n const mutations = frozen?.mutations ?? liveMutations\n const fields = frozen?.fields ?? liveFields\n\n return (\n <div className=\"olas-devtools\" data-testid=\"olas-devtools\">\n <style>{DEVTOOLS_CSS}</style>\n <div className=\"olas-devtools-tabs\" role=\"tablist\">\n <Tab\n name=\"tree\"\n current={tab}\n setTab={setTab}\n label=\"Tree\"\n short=\"Tree\"\n count={countLiveControllers(liveTree)}\n />\n <Tab\n name=\"cache\"\n current={tab}\n setTab={setTab}\n label=\"Cache\"\n short=\"Cache\"\n count={liveCache.length}\n />\n <Tab\n name=\"inspector\"\n current={tab}\n setTab={setTab}\n label=\"Inspector\"\n short=\"Insp\"\n count={cacheEntries.length}\n />\n <Tab\n name=\"mutations\"\n current={tab}\n setTab={setTab}\n label=\"Mutations\"\n short=\"Mut\"\n count={liveMutations.length}\n />\n <Tab\n name=\"fields\"\n current={tab}\n setTab={setTab}\n label=\"Fields\"\n short=\"Fld\"\n count={liveFields.length}\n />\n <button\n type=\"button\"\n aria-pressed={paused}\n className={paused ? 'olas-devtools-pause olas-devtools-pause-on' : 'olas-devtools-pause'}\n onClick={() => setPaused(!paused)}\n title={paused ? 'Resume live updates' : 'Pause live updates'}\n >\n <span aria-hidden=\"true\">{paused ? '▶' : '⏸'}</span>\n <span className=\"olas-devtools-pause-text\">{paused ? ' Resume' : ' Pause'}</span>\n </button>\n <button\n className=\"olas-devtools-clear\"\n type=\"button\"\n onClick={() => store.clearLogs()}\n title=\"Clear logs\"\n >\n <span className=\"olas-devtools-clear-text\">Clear</span>\n <span className=\"olas-devtools-clear-icon\" aria-hidden=\"true\">\n ✕\n </span>\n </button>\n </div>\n\n {(tab === 'cache' || tab === 'inspector' || tab === 'mutations' || tab === 'fields') && (\n <div className=\"olas-devtools-filter\">\n <input\n type=\"search\"\n value={filter}\n placeholder={`Filter ${tab}…`}\n onChange={(e) => setFilter(e.target.value)}\n />\n {filter !== '' && (\n <button type=\"button\" onClick={() => setFilter('')} aria-label=\"Clear filter\">\n ✕\n </button>\n )}\n </div>\n )}\n\n <div className=\"olas-devtools-body\" role=\"tabpanel\">\n {tab === 'tree' && <TreeView tree={tree} mutations={liveMutations} />}\n {tab === 'cache' && <CacheView entries={cache} filter={filter} />}\n {tab === 'inspector' && <InspectorView entries={cacheEntries} filter={filter} />}\n {tab === 'mutations' && <MutationsView entries={mutations} filter={filter} />}\n {tab === 'fields' && <FieldsView entries={fields} filter={filter} />}\n </div>\n </div>\n )\n}\n\nfunction Tab(props: {\n name: DevtoolsTab\n current: DevtoolsTab\n setTab: (t: DevtoolsTab) => void\n label: string\n short: string\n count: number\n}): ReactElement {\n const selected = props.current === props.name\n return (\n <button\n role=\"tab\"\n type=\"button\"\n aria-selected={selected}\n title={props.label}\n className=\"olas-devtools-tab\"\n onClick={() => props.setTab(props.name)}\n >\n <span className=\"olas-devtools-tab-label-full\">{props.label}</span>\n <span className=\"olas-devtools-tab-label-short\" aria-hidden=\"true\">\n {props.short}\n </span>\n {props.count > 0 && (\n <span className=\"olas-devtools-tab-count\" aria-hidden=\"true\">\n {props.count}\n </span>\n )}\n </button>\n )\n}\n\nfunction countLiveControllers(node: ControllerNode): number {\n let total = node.state !== 'disposed' ? 1 : 0\n for (const c of node.children) total += countLiveControllers(c)\n return Math.max(total - 1, 0) // exclude the placeholder root wrapper\n}\n\n// ===========================================================================\n// Tree\n// ===========================================================================\n\nfunction TreeView({\n tree,\n mutations,\n}: {\n tree: ControllerNode\n mutations: MutationEntry[]\n}): ReactElement {\n if (tree.children.length === 0) {\n return <Empty title=\"No controllers yet\" hint=\"The root hasn't constructed any controllers.\" />\n }\n // Roll up pending-mutation counts per controller path. A \"pending\" mutation\n // is one whose last entry is `run` with no matching success/error for the\n // same (path, name).\n const pending = useMemo(() => rollupPending(mutations), [mutations])\n return (\n <div className=\"olas-devtools-tree\">\n {tree.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )\n}\n\nfunction rollupPending(entries: readonly MutationEntry[]): Map<string, number> {\n const inFlight = new Map<string, number>() // (path|name) → count\n const out = new Map<string, number>() // path → pending count\n for (const e of entries) {\n const key = `${e.path.join('>')}#${e.name ?? ''}`\n const pathKey = e.path.join('>')\n if (e.kind === 'run') {\n inFlight.set(key, (inFlight.get(key) ?? 0) + 1)\n out.set(pathKey, (out.get(pathKey) ?? 0) + 1)\n } else if (e.kind === 'success' || e.kind === 'error') {\n const n = inFlight.get(key) ?? 0\n if (n > 0) inFlight.set(key, n - 1)\n const p = out.get(pathKey) ?? 0\n if (p > 0) out.set(pathKey, p - 1)\n }\n }\n return out\n}\n\nfunction TreeNode({\n node,\n pending,\n}: {\n node: ControllerNode\n pending: Map<string, number>\n}): ReactElement {\n const name = node.path[node.path.length - 1] ?? '?'\n const stateClass =\n node.state === 'suspended'\n ? 'olas-devtools-tree-state-suspended'\n : node.state === 'disposed'\n ? 'olas-devtools-tree-state-disposed'\n : 'olas-devtools-tree-state-active'\n const pendingCount = pending.get(node.path.join('>')) ?? 0\n const propsPreview = useMemo(() => summarizeProps(node.props), [node.props])\n const [propsOpen, setPropsOpen] = useState(false)\n const canExpandProps = node.props !== undefined && node.props !== null\n\n return (\n <div className=\"olas-devtools-tree-node\">\n <span className=\"olas-devtools-tree-row\">\n <span className=\"olas-devtools-tree-name\">{name}</span>\n <span className={stateClass}>{node.state}</span>\n {pendingCount > 0 && (\n <span className=\"olas-devtools-tree-pending\" title=\"pending mutations on this controller\">\n {pendingCount} pending\n </span>\n )}\n {canExpandProps && (\n <button\n type=\"button\"\n className=\"olas-devtools-tree-props-toggle\"\n aria-expanded={propsOpen}\n onClick={() => setPropsOpen((v) => !v)}\n title={propsOpen ? 'Hide props' : 'Show full props'}\n >\n {propsPreview}\n </button>\n )}\n </span>\n {propsOpen && canExpandProps && (\n <div className=\"olas-devtools-tree-props\">\n <JsonView value={node.props} />\n </div>\n )}\n {node.children.length > 0 && (\n <div className=\"olas-devtools-tree-children\">\n {node.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )}\n </div>\n )\n}\n\n/** Build a one-line props summary for the tree row. */\nfunction summarizeProps(props: unknown): string {\n if (props === null || props === undefined) return ''\n if (typeof props === 'string') return `\"${truncate(props, 24)}\"`\n if (typeof props === 'number' || typeof props === 'boolean') return String(props)\n if (Array.isArray(props)) return `[${props.length}]`\n if (typeof props === 'object') {\n const keys = Object.keys(props as Record<string, unknown>)\n if (keys.length === 0) return '{}'\n const parts = keys.slice(0, 2).map((k) => {\n const v = (props as Record<string, unknown>)[k]\n return `${k}: ${shortValue(v)}`\n })\n return `{ ${parts.join(', ')}${keys.length > 2 ? `, +${keys.length - 2}` : ''} }`\n }\n return String(props)\n}\n\nfunction shortValue(v: unknown): string {\n if (v === null) return 'null'\n if (v === undefined) return 'undefined'\n if (typeof v === 'string') return `\"${truncate(v, 16)}\"`\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n if (Array.isArray(v)) return `[${v.length}]`\n if (typeof v === 'object') return `{${Object.keys(v as object).length}}`\n return String(v)\n}\n\nfunction truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}…`\n}\n\n// ===========================================================================\n// Cache Inspector — live state, not history\n// ===========================================================================\n\nfunction InspectorView({\n entries,\n filter,\n}: {\n entries: DebugCacheEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, inspectorHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No cache entries\"\n hint=\"Subscribe to a query somewhere in the tree to see its data.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {filtered.map((entry) => (\n <InspectorRow key={entry.key.join('|')} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction inspectorHaystack(e: DebugCacheEntry): string {\n return [...e.key.map(String), e.status, safeStringify(e.data)].join(' ')\n}\n\nfunction InspectorRow({ entry }: { entry: DebugCacheEntry }): ReactElement {\n const kindClass =\n entry.status === 'error'\n ? 'olas-devtools-kind-error'\n : entry.status === 'success'\n ? 'olas-devtools-kind-success'\n : entry.status === 'pending'\n ? 'olas-devtools-kind-warn'\n : ''\n const ageMs = entry.lastUpdatedAt != null ? Date.now() - entry.lastUpdatedAt : null\n const tags: string[] = []\n if (entry.isStale) tags.push('stale')\n if (entry.isFetching) tags.push('fetching')\n if (entry.hasPendingMutations) tags.push('optimistic')\n return (\n <Row\n kind={entry.status}\n kindClass={kindClass}\n target={formatPath(entry.key)}\n t={entry.lastUpdatedAt ?? Date.now()}\n payload={entry.error ?? entry.data}\n suffix={[ageMs != null ? `${formatAge(ageMs)} ago` : '—', ...tags].join(' · ')}\n />\n )\n}\n\nfunction safeStringify(v: unknown): string {\n try {\n return JSON.stringify(v) ?? ''\n } catch {\n return String(v)\n }\n}\n\nfunction formatAge(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`\n return `${Math.round(ms / 3_600_000)}h`\n}\n\n// ===========================================================================\n// URL-hash persistence\n// ===========================================================================\n\nfunction readUrlHash(\n key: string | undefined,\n defaultTab: DevtoolsTab,\n): { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> } {\n const empty = { tree: '', cache: '', inspector: '', mutations: '', fields: '' }\n if (key === undefined) return { tab: defaultTab, filters: empty }\n if (typeof window === 'undefined') return { tab: defaultTab, filters: empty }\n try {\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n const raw = params.get(key)\n if (raw === null) return { tab: defaultTab, filters: empty }\n const parsed = JSON.parse(decodeURIComponent(raw)) as {\n tab?: DevtoolsTab\n filters?: Partial<Record<DevtoolsTab, string>>\n }\n return {\n tab: parsed.tab ?? defaultTab,\n filters: { ...empty, ...(parsed.filters ?? {}) },\n }\n } catch {\n return { tab: defaultTab, filters: empty }\n }\n}\n\nfunction writeUrlHash(\n key: string,\n state: { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> },\n): void {\n if (typeof window === 'undefined') return\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n params.set(key, encodeURIComponent(JSON.stringify(state)))\n const next = `#${params.toString()}`\n if (next !== window.location.hash) {\n window.history.replaceState(null, '', next)\n }\n}\n\n// ===========================================================================\n// Cache\n// ===========================================================================\n\nfunction CacheView({ entries, filter }: { entries: CacheEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, cacheHaystack)\n if (entries.length === 0) {\n return (\n <Empty title=\"No cache events yet\" hint=\"Trigger a query subscription to see fetches here.\" />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <CacheRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction cacheHaystack(e: CacheEntry): string {\n const parts: string[] = [e.kind, ...e.queryKey.map((p) => String(p))]\n if (e.kind === 'fetch-error') parts.push(safeStringify(e.error))\n if (e.kind === 'subscribed') parts.push(...e.subscriberPath)\n return parts.join(' ')\n}\n\nfunction CacheRow({ entry }: { entry: CacheEntry }): ReactElement {\n const kindClass =\n entry.kind === 'fetch-error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'fetch-success'\n ? 'olas-devtools-kind-success'\n : entry.kind === 'invalidated' || entry.kind === 'gc'\n ? 'olas-devtools-kind-warn'\n : ''\n\n let inline: string | null = null\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'fetch-success') {\n suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'fetch-error') {\n suffix = `${entry.durationMs}ms`\n payload = entry.error\n } else if (entry.kind === 'subscribed') {\n inline = `from ${formatPath(entry.subscriberPath)}`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={formatPath(entry.queryKey)}\n t={entry.t}\n inline={inline}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Mutations\n// ===========================================================================\n\nfunction MutationsView({\n entries,\n filter,\n}: {\n entries: MutationEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, mutationHaystack)\n if (entries.length === 0) {\n return <Empty title=\"No mutations yet\" hint=\"Trigger a mutation to see the lifecycle here.\" />\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <MutationRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction mutationHaystack(e: MutationEntry): string {\n const parts: string[] = [e.kind, ...e.path, e.name ?? '']\n if (e.kind === 'run') parts.push(safeStringify(e.vars))\n if (e.kind === 'success') parts.push(safeStringify(e.result))\n if (e.kind === 'error') parts.push(safeStringify(e.error))\n return parts.join(' ')\n}\n\nfunction MutationRow({ entry }: { entry: MutationEntry }): ReactElement {\n const kindClass =\n entry.kind === 'error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'rollback'\n ? 'olas-devtools-kind-rollback'\n : entry.kind === 'success'\n ? 'olas-devtools-kind-success'\n : ''\n\n const target = entry.name ? `${entry.name} · ${formatPath(entry.path)}` : formatPath(entry.path)\n\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'run') payload = entry.vars\n else if (entry.kind === 'success') {\n payload = entry.result\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'error') {\n payload = entry.error\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={target}\n t={entry.t}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Fields\n// ===========================================================================\n\nfunction FieldsView({ entries, filter }: { entries: FieldEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, fieldHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No field validations yet\"\n hint=\"Type into a form bound via ctx.form(...) or ctx.field(...) — each pass lands here.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <FieldRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction fieldHaystack(e: FieldEntry): string {\n return [e.field, ...e.path, e.valid ? 'valid' : 'invalid', ...e.errors].join(' ')\n}\n\nfunction FieldRow({ entry }: { entry: FieldEntry }): ReactElement {\n const kindClass = entry.valid ? 'olas-devtools-kind-success' : 'olas-devtools-kind-error'\n return (\n <Row\n kind={entry.valid ? 'valid' : 'invalid'}\n kindClass={kindClass}\n target={`${formatPath(entry.path)} · ${entry.field}`}\n t={entry.t}\n inline={entry.errors.length > 0 ? entry.errors.join(' · ') : null}\n />\n )\n}\n\n// ===========================================================================\n// Shared row + helpers\n// ===========================================================================\n\ntype RowProps = {\n kind: string\n kindClass: string\n target: string\n t: number\n /** Either a tiny inline string (durations, urls) OR a structured payload. */\n inline?: string | null\n payload?: unknown\n suffix?: string | null\n}\n\nfunction Row(props: RowProps): ReactElement {\n const { kind, kindClass, target, t, inline, payload, suffix } = props\n const hasPayload = payload !== undefined\n const [expanded, setExpanded] = useState(false)\n const togglable = hasPayload\n\n return (\n <li className={togglable ? 'olas-devtools-row-clickable' : ''}>\n <div\n className=\"olas-devtools-row-top\"\n onClick={togglable ? () => setExpanded((v) => !v) : undefined}\n >\n <span className={`olas-devtools-kind ${kindClass}`}>{kind}</span>\n <span className=\"olas-devtools-target\">{target}</span>\n {suffix !== undefined && suffix !== null && (\n <span className=\"olas-devtools-duration\">{suffix}</span>\n )}\n <span className=\"olas-devtools-time\">{formatTime(t)}</span>\n {togglable && (\n <span\n aria-hidden=\"true\"\n className={`olas-devtools-chevron ${expanded ? 'olas-devtools-chevron-open' : ''}`}\n >\n ›\n </span>\n )}\n </div>\n {inline != null && (\n <div className=\"olas-devtools-payload olas-devtools-payload-inline\">{inline}</div>\n )}\n {hasPayload && expanded && (\n <div className=\"olas-devtools-payload olas-devtools-payload-json\">\n <JsonView value={payload} />\n </div>\n )}\n </li>\n )\n}\n\nfunction useFiltered<T>(items: readonly T[], filter: string, haystack: (item: T) => string): T[] {\n return useMemo(() => {\n if (filter.trim() === '') return [...items]\n const q = filter.toLowerCase()\n return items.filter((item) => haystack(item).toLowerCase().includes(q))\n }, [items, filter, haystack])\n}\n\nfunction Empty({ title, hint }: { title: string; hint: string }): ReactElement {\n return (\n <div className=\"olas-devtools-empty\">\n <div className=\"olas-devtools-empty-title\">{title}</div>\n <div className=\"olas-devtools-empty-hint\">{hint}</div>\n </div>\n )\n}\n","// Floating, draggable, resizable host for the DevtoolsPanel.\n//\n// Renders two things:\n// 1. A small bottom-right launcher button (always present).\n// 2. When open, a `position: fixed` window containing the panel. The header\n// is a drag handle; the south-east corner is a resize grip. Position +\n// size + open + minimized state persist to `localStorage`.\n//\n// API:\n// <DevtoolsLauncher root={root} />\n//\n// Optional props mirror DevtoolsPanel's. The launcher manages the window\n// chrome and ferries the rest through.\n\nimport type { Root } from '@kontsedal/olas-core'\nimport { type ReactElement, useCallback, useEffect, useRef, useState } from 'react'\nimport { DevtoolsPanel, type DevtoolsTab } from './DevtoolsPanel'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsLauncherProps = {\n root: Pick<Root<unknown>, '__debug'>\n /** Default panel tab. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. */\n maxEntries?: number\n /** Persist tab+filter state under this key (independent of window state). */\n urlHashKey?: string\n /** localStorage key for window state (position/size/open/minimized). */\n storageKey?: string\n /** Initial position if no persisted state. */\n initial?: { x?: number; y?: number; w?: number; h?: number }\n}\n\ntype WindowState = {\n x: number\n y: number\n w: number\n h: number\n open: boolean\n minimized: boolean\n}\n\nconst MIN_W = 360\nconst MIN_H = 280\nconst HEADER_H = 30\nconst DEFAULT_W = 520\nconst DEFAULT_H = 520\nconst MARGIN = 16\n\nexport function DevtoolsLauncher(props: DevtoolsLauncherProps): ReactElement {\n const storageKey = props.storageKey ?? 'olas-devtools-window'\n const [state, setState] = useState<WindowState>(() => loadState(storageKey, props.initial))\n\n // Persist on change.\n useEffect(() => {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(storageKey, JSON.stringify(state))\n } catch {\n /* swallow */\n }\n }, [state, storageKey])\n\n // Clamp to viewport on window resize so a previously-saved off-screen\n // position recovers gracefully.\n useEffect(() => {\n const onResize = () => setState((s) => clampToViewport(s))\n if (typeof window === 'undefined') return\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n const setOpen = useCallback((open: boolean) => setState((s) => ({ ...s, open })), [])\n const setMin = useCallback((minimized: boolean) => setState((s) => ({ ...s, minimized })), [])\n\n return (\n <>\n <style>{DEVTOOLS_CSS}</style>\n <LauncherButton open={state.open} onClick={() => setOpen(!state.open)} />\n {state.open && (\n <FloatingWindow\n state={state}\n setState={setState}\n onClose={() => setOpen(false)}\n onMinimize={() => setMin(!state.minimized)}\n >\n {!state.minimized && (\n <div className=\"olas-devtools-floating-body\">\n <DevtoolsPanel\n root={props.root}\n defaultTab={props.defaultTab}\n maxEntries={props.maxEntries}\n urlHashKey={props.urlHashKey}\n />\n </div>\n )}\n </FloatingWindow>\n )}\n </>\n )\n}\n\nfunction LauncherButton({ open, onClick }: { open: boolean; onClick: () => void }): ReactElement {\n return (\n <button\n type=\"button\"\n aria-label={open ? 'Hide Olas devtools' : 'Show Olas devtools'}\n onClick={onClick}\n className={`olas-devtools-launcher ${open ? 'olas-devtools-launcher-active' : ''}`}\n >\n <span aria-hidden=\"true\" className=\"olas-devtools-launcher-dot\" />\n <span className=\"olas-devtools-launcher-label\">Olas devtools</span>\n </button>\n )\n}\n\nfunction FloatingWindow(props: {\n state: WindowState\n setState: React.Dispatch<React.SetStateAction<WindowState>>\n onClose: () => void\n onMinimize: () => void\n children: ReactElement | ReactElement[] | false\n}): ReactElement {\n const { state, setState, onClose, onMinimize } = props\n const dragState = useRef<{\n kind: 'move' | 'resize'\n ox: number\n oy: number\n sx: number\n sy: number\n sw: number\n sh: number\n } | null>(null)\n\n const onPointerDown = (kind: 'move' | 'resize') => (e: React.PointerEvent) => {\n e.preventDefault()\n ;(e.target as Element).setPointerCapture(e.pointerId)\n dragState.current = {\n kind,\n ox: e.clientX,\n oy: e.clientY,\n sx: state.x,\n sy: state.y,\n sw: state.w,\n sh: state.h,\n }\n }\n const onPointerMove = (e: React.PointerEvent) => {\n const d = dragState.current\n if (d === null) return\n const dx = e.clientX - d.ox\n const dy = e.clientY - d.oy\n if (d.kind === 'move') {\n setState((s) => clampToViewport({ ...s, x: d.sx + dx, y: d.sy + dy }))\n } else {\n const w = Math.max(MIN_W, d.sw + dx)\n const h = Math.max(MIN_H, d.sh + dy)\n setState((s) => clampToViewport({ ...s, w, h }))\n }\n }\n const onPointerUp = () => {\n dragState.current = null\n }\n\n const minimized = state.minimized\n const height = minimized ? HEADER_H : state.h\n\n return (\n <div\n className=\"olas-devtools-floating\"\n role=\"dialog\"\n aria-label=\"Olas devtools\"\n style={{ left: state.x, top: state.y, width: state.w, height }}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"olas-devtools-floating-header\" onPointerDown={onPointerDown('move')}>\n <span className=\"olas-devtools-floating-grip\" aria-hidden=\"true\">\n ⠿\n </span>\n <span className=\"olas-devtools-floating-title\">Olas devtools</span>\n <div className=\"olas-devtools-floating-actions\">\n <button\n type=\"button\"\n aria-label={minimized ? 'Expand' : 'Minimize'}\n onClick={onMinimize}\n className=\"olas-devtools-floating-action\"\n >\n {minimized ? '▢' : '–'}\n </button>\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className=\"olas-devtools-floating-action\"\n >\n ×\n </button>\n </div>\n </div>\n {props.children}\n {!minimized && (\n <span\n className=\"olas-devtools-floating-resize\"\n onPointerDown={onPointerDown('resize')}\n aria-label=\"Resize\"\n role=\"separator\"\n />\n )}\n </div>\n )\n}\n\nfunction clampToViewport(s: WindowState): WindowState {\n if (typeof window === 'undefined') return s\n const vw = window.innerWidth\n const vh = window.innerHeight\n const w = Math.min(s.w, vw - MARGIN * 2)\n const h = Math.min(s.h, vh - MARGIN * 2)\n const x = Math.max(MARGIN, Math.min(s.x, vw - w - MARGIN))\n const y = Math.max(MARGIN, Math.min(s.y, vh - HEADER_H - MARGIN))\n return { ...s, x, y, w, h }\n}\n\nfunction loadState(\n storageKey: string,\n initial?: { x?: number; y?: number; w?: number; h?: number },\n): WindowState {\n const vw = typeof window !== 'undefined' ? window.innerWidth : 1024\n const vh = typeof window !== 'undefined' ? window.innerHeight : 768\n const w = initial?.w ?? DEFAULT_W\n const h = initial?.h ?? DEFAULT_H\n const defaults: WindowState = {\n x: initial?.x ?? Math.max(MARGIN, vw - w - MARGIN),\n y: initial?.y ?? Math.max(MARGIN, vh - h - MARGIN - 56),\n w,\n h,\n open: false,\n minimized: false,\n }\n if (typeof localStorage === 'undefined') return defaults\n try {\n const raw = localStorage.getItem(storageKey)\n if (!raw) return defaults\n const parsed = JSON.parse(raw) as Partial<WindowState>\n return clampToViewport({\n x: parsed.x ?? defaults.x,\n y: parsed.y ?? defaults.y,\n w: parsed.w ?? defaults.w,\n h: parsed.h ?? defaults.h,\n open: parsed.open ?? false,\n minimized: parsed.minimized ?? false,\n })\n } catch {\n return defaults\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/format.ts","../src/JsonView.tsx","../src/store.ts","../src/styles.ts","../src/DevtoolsPanel.tsx","../src/DevtoolsLauncher.tsx"],"sourcesContent":["/**\n * Render a payload (props, vars, result, etc.) as a single-line string for\n * the panel. Cuts at `maxLen` so a giant blob doesn't blow up the layout.\n */\nexport function formatPayload(value: unknown, maxLen = 200): string {\n if (value === undefined) return 'undefined'\n if (value === null) return 'null'\n if (typeof value === 'function') return '[fn]'\n let s: string\n try {\n s = JSON.stringify(value, replaceUnserializable)\n } catch {\n s = String(value)\n }\n if (s === undefined) s = String(value)\n return s.length > maxLen ? `${s.slice(0, maxLen)}…` : s\n}\n\nfunction replaceUnserializable(_key: string, value: unknown): unknown {\n if (typeof value === 'function') return '[fn]'\n if (typeof value === 'bigint') return value.toString()\n if (value instanceof Error) return { name: value.name, message: value.message }\n return value\n}\n\n/** Render an HH:MM:SS.mmm timestamp from epoch ms. */\nexport function formatTime(t: number): string {\n const d = new Date(t)\n const pad = (n: number, w = 2) => n.toString().padStart(w, '0')\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`\n}\n\n/** Render a controller path / query key as a compact string. */\nexport function formatPath(path: readonly unknown[]): string {\n if (path.length === 0) return '∅'\n return path.map((p) => String(p)).join(' › ')\n}\n","// Compact JSON renderer for the devtools panel payload column.\n//\n// Renders values inline by default. Objects/arrays show their summary\n// (\"{4}\", \"[12]\") inline; clicking the summary expands them. Each nested\n// level inherits the same expand/collapse semantics. Scoped to the\n// `olas-devtools-json-*` class prefix; styles are in `styles.ts`.\n\nimport { type ReactElement, useState } from 'react'\n\nexport function JsonView({ value, depth = 0 }: { value: unknown; depth?: number }): ReactElement {\n return <Render value={value} depth={depth} initiallyOpen={depth === 0} />\n}\n\nfunction Render({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n if (value === null) return <span className=\"olas-devtools-json-null\">null</span>\n if (value === undefined) return <span className=\"olas-devtools-json-null\">undefined</span>\n\n const t = typeof value\n if (t === 'string') return <span className=\"olas-devtools-json-string\">\"{value as string}\"</span>\n if (t === 'number') return <span className=\"olas-devtools-json-number\">{String(value)}</span>\n if (t === 'boolean') return <span className=\"olas-devtools-json-boolean\">{String(value)}</span>\n if (t === 'bigint') return <span className=\"olas-devtools-json-number\">{String(value)}n</span>\n\n // Errors render as `Error(\"message\")` so they're distinguishable from\n // plain string payloads.\n if (value instanceof Error) {\n return (\n <span className=\"olas-devtools-json-error\">\n {value.name}({JSON.stringify(value.message)})\n </span>\n )\n }\n\n if (Array.isArray(value)) {\n return <CollapsibleArray value={value} depth={depth} initiallyOpen={initiallyOpen} />\n }\n\n if (t === 'object') {\n return (\n <CollapsibleObject\n value={value as Record<string, unknown>}\n depth={depth}\n initiallyOpen={initiallyOpen}\n />\n )\n }\n\n return <span>{String(value)}</span>\n}\n\nfunction CollapsibleArray({\n value,\n depth,\n initiallyOpen,\n}: {\n value: unknown[]\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const [open, setOpen] = useState(initiallyOpen && value.length <= 12)\n if (value.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">[]</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">[</span>\n <span className=\"olas-devtools-json-summary\">\n {value.length} item{value.length === 1 ? '' : 's'}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">[</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {value.map((item, idx) => (\n <span key={idx} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-index\">{idx}:</span>\n <Render value={item} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">]</span>\n </span>\n )\n}\n\nfunction CollapsibleObject({\n value,\n depth,\n initiallyOpen,\n}: {\n value: Record<string, unknown>\n depth: number\n initiallyOpen: boolean\n}): ReactElement {\n const keys = Object.keys(value)\n const [open, setOpen] = useState(initiallyOpen && keys.length <= 8)\n if (keys.length === 0) {\n return <span className=\"olas-devtools-json-bracket\">{'{}'}</span>\n }\n if (!open) {\n return (\n <button type=\"button\" className=\"olas-devtools-json-toggle\" onClick={() => setOpen(true)}>\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n <span className=\"olas-devtools-json-summary\">\n {keys.slice(0, 3).join(', ')}\n {keys.length > 3 ? ` +${keys.length - 3}` : ''}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </button>\n )\n }\n return (\n <span className=\"olas-devtools-json-block\">\n <button\n type=\"button\"\n className=\"olas-devtools-json-toggle olas-devtools-json-toggle-open\"\n onClick={() => setOpen(false)}\n >\n <span className=\"olas-devtools-json-bracket\">{'{'}</span>\n </button>\n <span className=\"olas-devtools-json-children\">\n {keys.map((k) => (\n <span key={k} className=\"olas-devtools-json-row\">\n <span className=\"olas-devtools-json-key\">{k}:</span>\n <Render value={value[k]} depth={depth + 1} initiallyOpen={false} />\n </span>\n ))}\n </span>\n <span className=\"olas-devtools-json-bracket\">{'}'}</span>\n </span>\n )\n}\n","import type { DebugEvent, Root } from '@kontsedal/olas-core'\nimport { type Signal, signal } from '@kontsedal/olas-core'\n\n/**\n * Per-path node in the live controller tree. `state` reflects the most\n * recently observed lifecycle event; `path` is the array reported by the\n * devtools bus.\n */\nexport type ControllerNode = {\n readonly path: readonly string[]\n state: 'active' | 'suspended' | 'disposed'\n props: unknown\n children: ControllerNode[]\n}\n\n/** One entry in the cache timeline. */\nexport type CacheEntry =\n | {\n id: number\n t: number\n kind: 'subscribed'\n queryKey: readonly unknown[]\n subscriberPath: readonly string[]\n }\n | { id: number; t: number; kind: 'fetch-start'; queryKey: readonly unknown[] }\n | {\n id: number\n t: number\n kind: 'fetch-success'\n queryKey: readonly unknown[]\n durationMs: number\n }\n | {\n id: number\n t: number\n kind: 'fetch-error'\n queryKey: readonly unknown[]\n durationMs: number\n error: unknown\n }\n | { id: number; t: number; kind: 'invalidated'; queryKey: readonly unknown[] }\n | { id: number; t: number; kind: 'gc'; queryKey: readonly unknown[] }\n\n/** One entry in the mutation log. `durationMs` is set on success/error when\n * the entry can be paired with a preceding `run` for the same path+name. */\nexport type MutationEntry =\n | { id: number; t: number; kind: 'run'; path: readonly string[]; name?: string; vars: unknown }\n | {\n id: number\n t: number\n kind: 'success'\n path: readonly string[]\n name?: string\n result: unknown\n durationMs?: number\n }\n | {\n id: number\n t: number\n kind: 'error'\n path: readonly string[]\n name?: string\n error: unknown\n durationMs?: number\n }\n | { id: number; t: number; kind: 'rollback'; path: readonly string[]; name?: string }\n\n/** One entry in the field validation log. */\nexport type FieldEntry = {\n id: number\n t: number\n path: readonly string[]\n field: string\n valid: boolean\n errors: string[]\n}\n\n/** Defaults — exported so callers can override via `new DevtoolsStore({ maxEntries: 500 })`. */\nexport const DEFAULT_MAX_ENTRIES = 100\n\nexport type DevtoolsStoreOptions = {\n /** Cap on each event log (cache, mutation, field). Oldest entries drop first. */\n maxEntries?: number\n /** Optional clock — useful for tests. Default: `() => Date.now()`. */\n now?: () => number\n}\n\n/**\n * Subscribes to a root's `__debug` bus and maintains live state for the\n * devtools panel. Exposes signals so the React layer can consume via\n * `@kontsedal/olas-react`'s `use()`.\n *\n * Pure logic — no DOM, no React. Construct one per root.\n */\nexport class DevtoolsStore {\n readonly tree$: Signal<ControllerNode> = signal(makeRoot())\n readonly cache$: Signal<CacheEntry[]> = signal([])\n readonly mutations$: Signal<MutationEntry[]> = signal([])\n readonly fields$: Signal<FieldEntry[]> = signal([])\n\n private readonly maxEntries: number\n private readonly now: () => number\n private nextId = 1\n\n /** Keyed by `path|name` so a mutation:run can be paired with its\n * success/error to compute duration. Cleared after pairing. */\n private mutationStarts = new Map<string, number>()\n\n constructor(options?: DevtoolsStoreOptions) {\n this.maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES\n this.now = options?.now ?? (() => Date.now())\n }\n\n /**\n * Subscribe to the given root's debug bus. Returns the unsubscribe. The\n * caller (typically the React component) is responsible for invoking it\n * on unmount.\n */\n attach(root: Pick<Root<unknown>, '__debug'>): () => void {\n return root.__debug.subscribe((event) => this.handle(event))\n }\n\n /** Apply one event. Exposed for tests. */\n handle(event: DebugEvent): void {\n switch (event.type) {\n case 'controller:constructed':\n this.tree$.set(insertNode(this.tree$.peek(), event.path, event.props))\n return\n case 'controller:suspended':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'suspended'))\n return\n case 'controller:resumed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'active'))\n return\n case 'controller:disposed':\n this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'disposed'))\n // A controller that disposed mid-mutation (before `success`/`error`\n // ever fired) would otherwise leave its `mutation:run` start entry\n // in `mutationStarts` forever. Drop any starts under this path.\n this.dropStartsForPath(event.path)\n return\n case 'cache:subscribed':\n this.pushCache({\n kind: 'subscribed',\n queryKey: event.queryKey,\n subscriberPath: event.subscriberPath,\n })\n return\n case 'cache:fetch-start':\n this.pushCache({ kind: 'fetch-start', queryKey: event.queryKey })\n return\n case 'cache:fetch-success':\n this.pushCache({\n kind: 'fetch-success',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n })\n return\n case 'cache:fetch-error':\n this.pushCache({\n kind: 'fetch-error',\n queryKey: event.queryKey,\n durationMs: event.durationMs,\n error: event.error,\n })\n return\n case 'cache:invalidated':\n this.pushCache({ kind: 'invalidated', queryKey: event.queryKey })\n return\n case 'cache:gc':\n this.pushCache({ kind: 'gc', queryKey: event.queryKey })\n return\n case 'mutation:run': {\n this.mutationStarts.set(mutationKey(event.path, event.name), this.now())\n this.pushMutation({ kind: 'run', path: event.path, name: event.name, vars: event.vars })\n return\n }\n case 'mutation:success': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'success',\n path: event.path,\n name: event.name,\n result: event.result,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:error': {\n const durationMs = this.consumeStart(event.path, event.name)\n this.pushMutation({\n kind: 'error',\n path: event.path,\n name: event.name,\n error: event.error,\n ...(durationMs !== undefined ? { durationMs } : {}),\n })\n return\n }\n case 'mutation:rollback':\n this.pushMutation({ kind: 'rollback', path: event.path, name: event.name })\n return\n case 'field:validated':\n this.pushField({\n path: event.path,\n field: event.field,\n valid: event.valid,\n errors: event.errors,\n })\n return\n }\n }\n\n /** Clear every log. Tree state is preserved — the live tree is not a log. */\n clearLogs(): void {\n this.cache$.set([])\n this.mutations$.set([])\n this.fields$.set([])\n // Drop pending mutation-start timing records too — `clearLogs()` is the\n // user's \"start fresh\" gesture; any subsequent `success`/`error` for a\n // pre-clear `run` would have produced a duration anchored to noise.\n this.mutationStarts.clear()\n }\n\n // -----------------------------------------------------------------------\n // Internals\n // -----------------------------------------------------------------------\n\n private pushCache(entry: DistributiveOmit<CacheEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as CacheEntry\n this.cache$.set(appendBounded(this.cache$.peek(), full, this.maxEntries))\n }\n\n private pushMutation(entry: DistributiveOmit<MutationEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as MutationEntry\n this.mutations$.set(appendBounded(this.mutations$.peek(), full, this.maxEntries))\n }\n\n private pushField(entry: Omit<FieldEntry, 'id' | 't'>): void {\n const full = { id: this.nextId++, t: this.now(), ...entry } as FieldEntry\n this.fields$.set(appendBounded(this.fields$.peek(), full, this.maxEntries))\n }\n\n private consumeStart(path: readonly string[], name: string | undefined): number | undefined {\n const key = mutationKey(path, name)\n const startedAt = this.mutationStarts.get(key)\n if (startedAt === undefined) return undefined\n this.mutationStarts.delete(key)\n return this.now() - startedAt\n }\n\n /**\n * Drop every pending mutation-start record under `path` (and its\n * descendants). Called on `controller:disposed` so a dispose mid-mutation\n * doesn't leave a permanent entry in `mutationStarts`.\n */\n private dropStartsForPath(path: readonly string[]): void {\n if (this.mutationStarts.size === 0) return\n const prefix = `${path.join('>')}>`\n const exact = path.join('>')\n for (const key of this.mutationStarts.keys()) {\n const beforeHash = key.split('#')[0] ?? ''\n if (beforeHash === exact || beforeHash.startsWith(prefix)) {\n this.mutationStarts.delete(key)\n }\n }\n }\n}\n\nfunction mutationKey(path: readonly string[], name: string | undefined): string {\n return `${path.join('>')}#${name ?? ''}`\n}\n\n/**\n * Distributes `Omit` over a discriminated union so each variant keeps its own\n * keys. The default `Omit<A | B, K>` collapses to the intersection of keys —\n * not what we want when constructing one variant at a time.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n// ---------------------------------------------------------------------------\n// Pure helpers — tested independently of the class.\n// ---------------------------------------------------------------------------\n\nfunction makeRoot(): ControllerNode {\n return { path: [], state: 'active', props: undefined, children: [] }\n}\n\nfunction appendBounded<T>(arr: readonly T[], item: T, max: number): T[] {\n const next = arr.length >= max ? arr.slice(arr.length - max + 1) : arr.slice()\n next.push(item)\n return next\n}\n\n/**\n * Insert (or update) a node at `path` inside the tree. Auto-creates any\n * missing intermediate ancestors as 'active' placeholders — needed if the\n * subscriber attached after the root was constructed.\n *\n * Returns a NEW tree object (immutable update).\n */\nexport function insertNode(\n root: ControllerNode,\n path: readonly string[],\n props: unknown,\n): ControllerNode {\n if (path.length === 0) {\n // The root controller's \"constructed\" event has path === ['root']\n // (one segment), not []. We never receive empty paths in practice, but\n // handle defensively.\n return { ...root, state: 'active', props }\n }\n return cloneWithUpsert(root, path, 0, props)\n}\n\nfunction cloneWithUpsert(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n props: unknown,\n): ControllerNode {\n if (depth === path.length) {\n return { ...node, state: 'active', props }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n const childPath = path.slice(0, depth + 1)\n if (idx === -1) {\n const newChild = cloneWithUpsert(\n { path: childPath, state: 'active', props: undefined, children: [] },\n path,\n depth + 1,\n props,\n )\n return { ...node, children: [...node.children, newChild] }\n }\n const existing = node.children[idx]!\n const updatedChild = cloneWithUpsert(existing, path, depth + 1, props)\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n\n/**\n * Set `state` on the node at `path`. If the node doesn't exist (out-of-order\n * event delivery), the tree is returned unchanged.\n */\nexport function setNodeState(\n root: ControllerNode,\n path: readonly string[],\n state: ControllerNode['state'],\n): ControllerNode {\n if (path.length === 0) {\n return { ...root, state }\n }\n return setStateAt(root, path, 0, state) ?? root\n}\n\nfunction setStateAt(\n node: ControllerNode,\n path: readonly string[],\n depth: number,\n state: ControllerNode['state'],\n): ControllerNode | null {\n if (depth === path.length) {\n return { ...node, state }\n }\n const segment = path[depth] as string\n const idx = node.children.findIndex((c) => c.path[c.path.length - 1] === segment)\n if (idx === -1) return null\n const existing = node.children[idx]!\n const updatedChild = setStateAt(existing, path, depth + 1, state)\n if (updatedChild === null) return null\n const nextChildren = node.children.slice()\n nextChildren[idx] = updatedChild\n return { ...node, children: nextChildren }\n}\n","/**\n * Inline CSS for the devtools panel. Scoped to the `.olas-devtools-*` class\n * prefix so it doesn't bleed into the host app. Honors `prefers-color-scheme`\n * and accepts host palette overrides via `--olas-*` custom properties.\n */\nexport const DEVTOOLS_CSS = `\n.olas-devtools {\n container-type: inline-size;\n --olas-bg: #ffffff;\n --olas-fg: #1f2330;\n --olas-muted: #6b7280;\n --olas-soft: #f5f6f9;\n --olas-soft-2: #eef0f4;\n --olas-accent: #4f46e5;\n --olas-accent-soft: rgba(79,70,229,0.10);\n --olas-success: #1e8a3d;\n --olas-success-soft: rgba(30,138,61,0.12);\n --olas-warn: #ad6800;\n --olas-warn-soft: rgba(173,104,0,0.12);\n --olas-error: #b8361f;\n --olas-error-soft: rgba(184,54,31,0.12);\n --olas-border: #e6e6ea;\n --olas-border-soft: #eeeef2;\n --olas-row-alt: #fafafc;\n\n /* JSON viewer colors */\n --olas-json-key: #7a4ab8;\n --olas-json-string: #1e8a3d;\n --olas-json-number: #b25400;\n --olas-json-boolean: #4f46e5;\n --olas-json-null: #9aa0aa;\n --olas-json-bracket: #6b7280;\n --olas-json-summary: #6b7280;\n\n font-family: -apple-system, BlinkMacSystemFont, \"Inter var\", \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12.5px;\n line-height: 1.55;\n color: var(--olas-fg);\n background: var(--olas-bg);\n border: 1px solid var(--olas-border);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 320px;\n overflow: hidden;\n box-sizing: border-box;\n}\n.olas-devtools *,\n.olas-devtools *::before,\n.olas-devtools *::after { box-sizing: border-box; }\n\n@media (prefers-color-scheme: dark) {\n .olas-devtools {\n --olas-bg: #15171e;\n --olas-fg: #e8ebf2;\n --olas-muted: #97a0b3;\n --olas-soft: #1d2029;\n --olas-soft-2: #232733;\n --olas-accent: #8b86f0;\n --olas-accent-soft: rgba(139,134,240,0.18);\n --olas-success: #4ade80;\n --olas-success-soft: rgba(74,222,128,0.16);\n --olas-warn: #f5b740;\n --olas-warn-soft: rgba(245,183,64,0.16);\n --olas-error: #ef6b53;\n --olas-error-soft: rgba(239,107,83,0.16);\n --olas-border: #2a2e3a;\n --olas-border-soft: #20232c;\n --olas-row-alt: #1a1d25;\n\n --olas-json-key: #c39bff;\n --olas-json-string: #7ee79d;\n --olas-json-number: #f5b740;\n --olas-json-boolean: #8b86f0;\n --olas-json-null: #7c8090;\n --olas-json-bracket: #97a0b3;\n --olas-json-summary: #97a0b3;\n }\n}\n\n/* ---- tabs ------------------------------------------------------------- */\n.olas-devtools-tabs {\n display: flex;\n align-items: center;\n gap: 1px;\n border-bottom: 1px solid var(--olas-border);\n background: var(--olas-soft);\n padding: 0 8px;\n flex-shrink: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.olas-devtools-tabs::-webkit-scrollbar { display: none; }\n.olas-devtools-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 10px 8px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n cursor: pointer;\n font: inherit;\n font-weight: 500;\n font-size: 12px;\n white-space: nowrap;\n flex-shrink: 0;\n transition: color 80ms, border-color 80ms;\n}\n.olas-devtools-tab-label-full { display: inline; }\n.olas-devtools-tab-label-short { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-tab { padding: 9px 8px 8px; font-size: 11.5px; gap: 4px; }\n .olas-devtools-tab-label-full { display: none; }\n .olas-devtools-tab-label-short { display: inline; }\n .olas-devtools-pause-text,\n .olas-devtools-clear-text { display: none; }\n}\n.olas-devtools-tab:hover { color: var(--olas-fg); }\n.olas-devtools-tab[aria-selected=\"true\"] {\n color: var(--olas-fg);\n border-bottom-color: var(--olas-accent);\n}\n.olas-devtools-tab[aria-selected=\"true\"] .olas-devtools-tab-count {\n background: var(--olas-accent-soft);\n color: var(--olas-accent);\n}\n.olas-devtools-tab-count {\n min-width: 18px;\n padding: 0 6px;\n height: 16px;\n border-radius: 999px;\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n color: var(--olas-muted);\n font-size: 10.5px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n}\n.olas-devtools-pause,\n.olas-devtools-clear {\n padding: 4px 10px;\n background: transparent;\n color: var(--olas-muted);\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n font: inherit;\n font-size: 11.5px;\n align-self: center;\n}\n.olas-devtools-pause { margin-left: auto; }\n.olas-devtools-pause:hover,\n.olas-devtools-clear:hover {\n color: var(--olas-fg);\n background: color-mix(in oklch, var(--olas-bg) 60%, transparent);\n}\n.olas-devtools-pause-on { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-pause-on:hover { color: var(--olas-warn); }\n.olas-devtools-clear-icon { display: none; }\n@container (max-width: 480px) {\n .olas-devtools-clear-text { display: none; }\n .olas-devtools-clear-icon { display: inline; }\n .olas-devtools-pause, .olas-devtools-clear { padding: 4px 8px; }\n}\n\n/* ---- filter ---------------------------------------------------------- */\n.olas-devtools-filter {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--olas-border-soft);\n background: var(--olas-bg);\n}\n.olas-devtools-filter input {\n flex: 1;\n padding: 5px 9px;\n border: 1px solid var(--olas-border);\n border-radius: 6px;\n background: var(--olas-soft);\n color: var(--olas-fg);\n font: inherit;\n font-size: 12px;\n outline: none;\n transition: border-color 80ms, box-shadow 80ms;\n}\n.olas-devtools-filter input:focus {\n border-color: var(--olas-accent);\n box-shadow: 0 0 0 2px var(--olas-accent-soft);\n}\n.olas-devtools-filter input::placeholder { color: var(--olas-muted); }\n.olas-devtools-filter button {\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n cursor: pointer;\n font: inherit;\n font-size: 13px;\n padding: 2px 6px;\n}\n.olas-devtools-filter button:hover { color: var(--olas-fg); }\n\n/* ---- body ------------------------------------------------------------ */\n.olas-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n/* ---- list rows ------------------------------------------------------- */\n.olas-devtools-list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.olas-devtools-list li {\n border-bottom: 1px solid var(--olas-border-soft);\n display: flex;\n flex-direction: column;\n}\n.olas-devtools-list li:last-child { border-bottom: none; }\n.olas-devtools-list li:hover { background: var(--olas-row-alt); }\n\n.olas-devtools-row-top {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n min-height: 32px;\n}\n.olas-devtools-row-clickable .olas-devtools-row-top { cursor: pointer; user-select: none; }\n\n.olas-devtools-target {\n flex: 1;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n word-break: break-word;\n min-width: 0;\n}\n.olas-devtools-target strong { font-weight: 600; color: var(--olas-accent); }\n.olas-devtools-time {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.olas-devtools-chevron {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--olas-muted);\n font-size: 12px;\n width: 16px;\n height: 16px;\n transition: transform 100ms;\n user-select: none;\n}\n.olas-devtools-chevron-open { transform: rotate(90deg); color: var(--olas-fg); }\n\n.olas-devtools-kind {\n color: var(--olas-accent);\n background: var(--olas-accent-soft);\n border-radius: 4px;\n padding: 1px 7px;\n font-size: 10.5px;\n font-weight: 600;\n letter-spacing: 0.02em;\n white-space: nowrap;\n line-height: 1.4;\n font-variant-numeric: tabular-nums;\n}\n.olas-devtools-kind-success { color: var(--olas-success); background: var(--olas-success-soft); }\n.olas-devtools-kind-error { color: var(--olas-error); background: var(--olas-error-soft); }\n.olas-devtools-kind-warn,\n.olas-devtools-kind-rollback { color: var(--olas-warn); background: var(--olas-warn-soft); }\n.olas-devtools-duration {\n color: var(--olas-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n background: var(--olas-soft);\n border-radius: 4px;\n padding: 1px 6px;\n white-space: nowrap;\n}\n\n.olas-devtools-payload {\n margin: 0 12px 10px;\n padding: 8px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n color: var(--olas-fg);\n overflow-x: auto;\n}\n.olas-devtools-payload-inline {\n margin-top: -4px;\n padding: 4px 10px;\n background: transparent;\n border: 0;\n color: var(--olas-muted);\n font-size: 11px;\n}\n.olas-devtools-payload-json { line-height: 1.5; }\n\n/* ---- JSON viewer ----------------------------------------------------- */\n.olas-devtools-json-row {\n display: block;\n padding-left: 14px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.olas-devtools-json-children {\n display: block;\n border-left: 1px dashed var(--olas-border);\n margin-left: 4px;\n}\n.olas-devtools-json-block {\n display: inline-flex;\n flex-direction: column;\n vertical-align: top;\n}\n.olas-devtools-json-key {\n color: var(--olas-json-key);\n margin-right: 6px;\n font-weight: 500;\n}\n.olas-devtools-json-index {\n color: var(--olas-json-bracket);\n margin-right: 6px;\n}\n.olas-devtools-json-string { color: var(--olas-json-string); }\n.olas-devtools-json-number { color: var(--olas-json-number); }\n.olas-devtools-json-boolean { color: var(--olas-json-boolean); }\n.olas-devtools-json-null { color: var(--olas-json-null); font-style: italic; }\n.olas-devtools-json-bracket { color: var(--olas-json-bracket); }\n.olas-devtools-json-error { color: var(--olas-error); }\n.olas-devtools-json-summary {\n color: var(--olas-json-summary);\n font-style: italic;\n margin: 0 4px;\n}\n.olas-devtools-json-toggle {\n display: inline-flex;\n align-items: center;\n gap: 0;\n background: transparent;\n border: 0;\n padding: 0;\n margin: 0;\n cursor: pointer;\n color: inherit;\n font: inherit;\n}\n.olas-devtools-json-toggle:hover { background: var(--olas-accent-soft); border-radius: 3px; }\n\n/* ---- empty state ---------------------------------------------------- */\n.olas-devtools-empty {\n padding: 36px 24px;\n text-align: center;\n color: var(--olas-muted);\n}\n.olas-devtools-empty-title {\n color: var(--olas-fg);\n font-weight: 600;\n font-size: 13px;\n margin-bottom: 4px;\n}\n.olas-devtools-empty-hint {\n font-size: 12px;\n max-width: 320px;\n margin: 0 auto;\n line-height: 1.55;\n}\n\n/* ---- tree ------------------------------------------------------------ */\n.olas-devtools-tree { padding: 10px 12px; }\n.olas-devtools-tree-node {\n padding: 1px 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n}\n.olas-devtools-tree-row {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 1px 0;\n}\n.olas-devtools-tree-name { color: var(--olas-fg); font-weight: 500; }\n.olas-devtools-tree-state-active,\n.olas-devtools-tree-state-suspended,\n.olas-devtools-tree-state-disposed {\n border-radius: 4px;\n padding: 0 6px;\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.olas-devtools-tree-state-active {\n color: var(--olas-success);\n background: var(--olas-success-soft);\n}\n.olas-devtools-tree-state-suspended {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n}\n.olas-devtools-tree-state-disposed {\n color: var(--olas-muted);\n background: color-mix(in oklch, var(--olas-fg) 8%, transparent);\n}\n.olas-devtools-tree-pending {\n color: var(--olas-warn);\n background: var(--olas-warn-soft);\n border-radius: 4px;\n padding: 0 6px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.olas-devtools-tree-props-toggle {\n background: transparent;\n border: 0;\n cursor: pointer;\n font: inherit;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--olas-muted);\n padding: 0 4px;\n border-radius: 4px;\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n.olas-devtools-tree-props-toggle:hover {\n color: var(--olas-fg);\n background: var(--olas-soft);\n}\n.olas-devtools-tree-props {\n margin: 4px 0 6px 8px;\n padding: 6px 10px;\n background: var(--olas-soft);\n border: 1px solid var(--olas-border-soft);\n border-radius: 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n overflow-x: auto;\n}\n.olas-devtools-tree-children {\n margin-left: 8px;\n border-left: 1px dashed var(--olas-border);\n padding-left: 10px;\n margin-top: 2px;\n}\n\n/* ---- floating window + launcher ------------------------------------- */\n.olas-devtools-launcher {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 2147483645;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 11px;\n background: #1f2330;\n color: #e8ebf2;\n border: 1px solid #2a2e3a;\n border-radius: 999px;\n box-shadow: 0 2px 6px rgba(0,0,0,0.15), 0 8px 24px rgba(0,0,0,0.18);\n cursor: pointer;\n font: inherit;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 12px;\n font-weight: 500;\n}\n.olas-devtools-launcher:hover { filter: brightness(1.1); }\n.olas-devtools-launcher-active { box-shadow: 0 0 0 2px rgba(139,134,240,0.5), 0 8px 24px rgba(0,0,0,0.18); }\n.olas-devtools-launcher-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #4ade80;\n box-shadow: 0 0 6px rgba(74,222,128,0.7);\n}\n.olas-devtools-launcher-label { letter-spacing: 0.01em; }\n\n.olas-devtools-floating {\n position: fixed;\n z-index: 2147483646;\n display: flex;\n flex-direction: column;\n background: var(--olas-bg, #ffffff);\n color: var(--olas-fg, #1f2330);\n border: 1px solid var(--olas-border, #e6e6ea);\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.18), 0 24px 64px rgba(0,0,0,0.18);\n overflow: hidden;\n /* Inherit the panel's own CSS vars when DevtoolsPanel is mounted inside. */\n}\n@media (prefers-color-scheme: dark) {\n .olas-devtools-floating {\n background: #15171e;\n color: #e8ebf2;\n border-color: #2a2e3a;\n box-shadow: 0 8px 24px rgba(0,0,0,0.5), 0 24px 64px rgba(0,0,0,0.5);\n }\n}\n.olas-devtools-floating-header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 8px 0 10px;\n background: var(--olas-soft, #f5f6f9);\n border-bottom: 1px solid var(--olas-border, #e6e6ea);\n cursor: grab;\n user-select: none;\n flex-shrink: 0;\n}\n.olas-devtools-floating-header:active { cursor: grabbing; }\n.olas-devtools-floating-grip {\n color: var(--olas-muted, #6b7280);\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-title {\n flex: 1;\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--olas-fg, #1f2330);\n letter-spacing: 0.01em;\n}\n.olas-devtools-floating-actions { display: inline-flex; gap: 2px; }\n.olas-devtools-floating-action {\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n border-radius: 4px;\n color: var(--olas-muted, #6b7280);\n cursor: pointer;\n font: inherit;\n font-size: 14px;\n line-height: 1;\n}\n.olas-devtools-floating-action:hover {\n color: var(--olas-fg, #1f2330);\n background: color-mix(in oklch, currentColor 14%, transparent);\n}\n.olas-devtools-floating-body {\n flex: 1;\n min-height: 0;\n display: flex;\n}\n.olas-devtools-floating-body > .olas-devtools {\n flex: 1;\n border: 0;\n border-radius: 0;\n min-height: 0;\n}\n.olas-devtools-floating-resize {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 14px;\n height: 14px;\n cursor: nwse-resize;\n background:\n linear-gradient(135deg, transparent 0 7px, var(--olas-muted, #6b7280) 7px 8px, transparent 8px 10px,\n var(--olas-muted, #6b7280) 10px 11px, transparent 11px 100%);\n opacity: 0.6;\n}\n.olas-devtools-floating-resize:hover { opacity: 1; }\n`\n","import type { DebugCacheEntry, Root } from '@kontsedal/olas-core'\nimport { use } from '@kontsedal/olas-react'\nimport { type ReactElement, useEffect, useMemo, useRef, useState } from 'react'\nimport { formatPath, formatTime } from './format'\nimport { JsonView } from './JsonView'\nimport {\n type CacheEntry,\n type ControllerNode,\n DevtoolsStore,\n type FieldEntry,\n type MutationEntry,\n} from './store'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsTab = 'tree' | 'cache' | 'inspector' | 'mutations' | 'fields'\n\nexport type DevtoolsPanelProps = {\n /** The root to inspect. The panel subscribes to `root.__debug` on mount. */\n root: Pick<Root<unknown>, '__debug'>\n /** Initial tab. Default: `'tree'`. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. Default: 100. */\n maxEntries?: number\n /**\n * Persist filter state to the URL hash under this key. When set,\n * reloading the page restores filter + tab. Default: no persistence.\n */\n urlHashKey?: string\n /** How often (ms) to refresh the live cache inspector snapshot. Default 800. */\n inspectorPollMs?: number\n}\n\n/**\n * Drop-in devtools panel for an Olas root.\n *\n * Features:\n * - **Tree** populated from the snapshot replay on mount (no lost events).\n * - **Cache / Mutations / Fields** event logs in reverse chronological order.\n * - **Filter** field per tab — text-matches kind, path, name, payload.\n * - **Pause** toggle freezes the log without stopping ingestion.\n * - **Click a row** to expand its payload from a truncated preview to the full\n * JSON.\n * - **Mutation durations** — `run → success/error` pairing surfaces elapsed ms.\n *\n * Styled inline (no CSS import needed) and scoped to the `.olas-devtools-*`\n * class prefix. Hosts override the palette via `--olas-*` custom properties.\n * Spec §13.\n */\nexport function DevtoolsPanel(props: DevtoolsPanelProps): ReactElement {\n const { root, defaultTab = 'tree', maxEntries, urlHashKey, inspectorPollMs = 800 } = props\n const store = useMemo(\n () => new DevtoolsStore(maxEntries !== undefined ? { maxEntries } : undefined),\n [maxEntries],\n )\n useEffect(() => store.attach(root), [root, store])\n\n // Initial state read from URL hash if `urlHashKey` is set.\n const initial = useMemo(() => readUrlHash(urlHashKey, defaultTab), [urlHashKey, defaultTab])\n const [tab, setTab] = useState<DevtoolsTab>(initial.tab)\n const [paused, setPaused] = useState(false)\n // Filters are kept per-tab so switching back doesn't lose the query.\n const [filters, setFilters] = useState<Record<DevtoolsTab, string>>(initial.filters)\n const filter = filters[tab]\n const setFilter = (q: string) => setFilters((prev) => ({ ...prev, [tab]: q }))\n\n // Persist tab + filters back to the URL hash on every change.\n useEffect(() => {\n if (urlHashKey === undefined) return\n writeUrlHash(urlHashKey, { tab, filters })\n }, [urlHashKey, tab, filters])\n\n // Live cache inspector — polls `root.__debug.queryEntries()` periodically.\n // Polling is cheap (a single peek per entry) and bounded by inspectorPollMs;\n // only the Cache Inspector view reads this.\n const [cacheEntries, setCacheEntries] = useState<DebugCacheEntry[]>([])\n const rootRef = useRef(root)\n rootRef.current = root\n useEffect(() => {\n if (tab !== 'inspector') return\n const tick = () => setCacheEntries(rootRef.current.__debug.queryEntries())\n tick()\n const id = window.setInterval(tick, inspectorPollMs)\n return () => window.clearInterval(id)\n }, [tab, inspectorPollMs])\n\n const liveTree = use(store.tree$)\n const liveCache = use(store.cache$)\n const liveMutations = use(store.mutations$)\n const liveFields = use(store.fields$)\n\n // When paused, snapshot once and keep showing that frozen state.\n const [frozen, setFrozen] = useState<{\n tree: ControllerNode\n cache: CacheEntry[]\n mutations: MutationEntry[]\n fields: FieldEntry[]\n } | null>(null)\n useEffect(() => {\n if (paused) {\n setFrozen({\n tree: liveTree,\n cache: liveCache,\n mutations: liveMutations,\n fields: liveFields,\n })\n } else {\n setFrozen(null)\n }\n // We only re-snapshot when the toggle flips, not on every event.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [paused])\n\n const tree = frozen?.tree ?? liveTree\n const cache = frozen?.cache ?? liveCache\n const mutations = frozen?.mutations ?? liveMutations\n const fields = frozen?.fields ?? liveFields\n\n return (\n <div className=\"olas-devtools\" data-testid=\"olas-devtools\">\n <style>{DEVTOOLS_CSS}</style>\n <div className=\"olas-devtools-tabs\" role=\"tablist\">\n <Tab\n name=\"tree\"\n current={tab}\n setTab={setTab}\n label=\"Tree\"\n short=\"Tree\"\n count={countLiveControllers(liveTree)}\n />\n <Tab\n name=\"cache\"\n current={tab}\n setTab={setTab}\n label=\"Cache\"\n short=\"Cache\"\n count={liveCache.length}\n />\n <Tab\n name=\"inspector\"\n current={tab}\n setTab={setTab}\n label=\"Inspector\"\n short=\"Insp\"\n count={cacheEntries.length}\n />\n <Tab\n name=\"mutations\"\n current={tab}\n setTab={setTab}\n label=\"Mutations\"\n short=\"Mut\"\n count={liveMutations.length}\n />\n <Tab\n name=\"fields\"\n current={tab}\n setTab={setTab}\n label=\"Fields\"\n short=\"Fld\"\n count={liveFields.length}\n />\n <button\n type=\"button\"\n aria-pressed={paused}\n className={paused ? 'olas-devtools-pause olas-devtools-pause-on' : 'olas-devtools-pause'}\n onClick={() => setPaused(!paused)}\n title={paused ? 'Resume live updates' : 'Pause live updates'}\n >\n <span aria-hidden=\"true\">{paused ? '▶' : '⏸'}</span>\n <span className=\"olas-devtools-pause-text\">{paused ? ' Resume' : ' Pause'}</span>\n </button>\n <button\n className=\"olas-devtools-clear\"\n type=\"button\"\n onClick={() => store.clearLogs()}\n title=\"Clear logs\"\n >\n <span className=\"olas-devtools-clear-text\">Clear</span>\n <span className=\"olas-devtools-clear-icon\" aria-hidden=\"true\">\n ✕\n </span>\n </button>\n </div>\n\n {(tab === 'cache' || tab === 'inspector' || tab === 'mutations' || tab === 'fields') && (\n <div className=\"olas-devtools-filter\">\n <input\n type=\"search\"\n value={filter}\n placeholder={`Filter ${tab}…`}\n onChange={(e) => setFilter(e.target.value)}\n />\n {filter !== '' && (\n <button type=\"button\" onClick={() => setFilter('')} aria-label=\"Clear filter\">\n ✕\n </button>\n )}\n </div>\n )}\n\n <div className=\"olas-devtools-body\" role=\"tabpanel\">\n {tab === 'tree' && <TreeView tree={tree} mutations={liveMutations} />}\n {tab === 'cache' && <CacheView entries={cache} filter={filter} />}\n {tab === 'inspector' && <InspectorView entries={cacheEntries} filter={filter} />}\n {tab === 'mutations' && <MutationsView entries={mutations} filter={filter} />}\n {tab === 'fields' && <FieldsView entries={fields} filter={filter} />}\n </div>\n </div>\n )\n}\n\nfunction Tab(props: {\n name: DevtoolsTab\n current: DevtoolsTab\n setTab: (t: DevtoolsTab) => void\n label: string\n short: string\n count: number\n}): ReactElement {\n const selected = props.current === props.name\n return (\n <button\n role=\"tab\"\n type=\"button\"\n aria-selected={selected}\n title={props.label}\n className=\"olas-devtools-tab\"\n onClick={() => props.setTab(props.name)}\n >\n <span className=\"olas-devtools-tab-label-full\">{props.label}</span>\n <span className=\"olas-devtools-tab-label-short\" aria-hidden=\"true\">\n {props.short}\n </span>\n {props.count > 0 && (\n <span className=\"olas-devtools-tab-count\" aria-hidden=\"true\">\n {props.count}\n </span>\n )}\n </button>\n )\n}\n\nfunction countLiveControllers(node: ControllerNode): number {\n let total = node.state !== 'disposed' ? 1 : 0\n for (const c of node.children) total += countLiveControllers(c)\n return Math.max(total - 1, 0) // exclude the placeholder root wrapper\n}\n\n// ===========================================================================\n// Tree\n// ===========================================================================\n\nfunction TreeView({\n tree,\n mutations,\n}: {\n tree: ControllerNode\n mutations: MutationEntry[]\n}): ReactElement {\n if (tree.children.length === 0) {\n return <Empty title=\"No controllers yet\" hint=\"The root hasn't constructed any controllers.\" />\n }\n // Roll up pending-mutation counts per controller path. A \"pending\" mutation\n // is one whose last entry is `run` with no matching success/error for the\n // same (path, name).\n const pending = useMemo(() => rollupPending(mutations), [mutations])\n return (\n <div className=\"olas-devtools-tree\">\n {tree.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )\n}\n\nfunction rollupPending(entries: readonly MutationEntry[]): Map<string, number> {\n const inFlight = new Map<string, number>() // (path|name) → count\n const out = new Map<string, number>() // path → pending count\n for (const e of entries) {\n const key = `${e.path.join('>')}#${e.name ?? ''}`\n const pathKey = e.path.join('>')\n if (e.kind === 'run') {\n inFlight.set(key, (inFlight.get(key) ?? 0) + 1)\n out.set(pathKey, (out.get(pathKey) ?? 0) + 1)\n } else if (e.kind === 'success' || e.kind === 'error') {\n const n = inFlight.get(key) ?? 0\n if (n > 0) inFlight.set(key, n - 1)\n const p = out.get(pathKey) ?? 0\n if (p > 0) out.set(pathKey, p - 1)\n }\n }\n return out\n}\n\nfunction TreeNode({\n node,\n pending,\n}: {\n node: ControllerNode\n pending: Map<string, number>\n}): ReactElement {\n const name = node.path[node.path.length - 1] ?? '?'\n const stateClass =\n node.state === 'suspended'\n ? 'olas-devtools-tree-state-suspended'\n : node.state === 'disposed'\n ? 'olas-devtools-tree-state-disposed'\n : 'olas-devtools-tree-state-active'\n const pendingCount = pending.get(node.path.join('>')) ?? 0\n const propsPreview = useMemo(() => summarizeProps(node.props), [node.props])\n const [propsOpen, setPropsOpen] = useState(false)\n const canExpandProps = node.props !== undefined && node.props !== null\n\n return (\n <div className=\"olas-devtools-tree-node\">\n <span className=\"olas-devtools-tree-row\">\n <span className=\"olas-devtools-tree-name\">{name}</span>\n <span className={stateClass}>{node.state}</span>\n {pendingCount > 0 && (\n <span className=\"olas-devtools-tree-pending\" title=\"pending mutations on this controller\">\n {pendingCount} pending\n </span>\n )}\n {canExpandProps && (\n <button\n type=\"button\"\n className=\"olas-devtools-tree-props-toggle\"\n aria-expanded={propsOpen}\n onClick={() => setPropsOpen((v) => !v)}\n title={propsOpen ? 'Hide props' : 'Show full props'}\n >\n {propsPreview}\n </button>\n )}\n </span>\n {propsOpen && canExpandProps && (\n <div className=\"olas-devtools-tree-props\">\n <JsonView value={node.props} />\n </div>\n )}\n {node.children.length > 0 && (\n <div className=\"olas-devtools-tree-children\">\n {node.children.map((child) => (\n <TreeNode key={child.path.join('/')} node={child} pending={pending} />\n ))}\n </div>\n )}\n </div>\n )\n}\n\n/** Build a one-line props summary for the tree row. */\nfunction summarizeProps(props: unknown): string {\n if (props === null || props === undefined) return ''\n if (typeof props === 'string') return `\"${truncate(props, 24)}\"`\n if (typeof props === 'number' || typeof props === 'boolean') return String(props)\n if (Array.isArray(props)) return `[${props.length}]`\n if (typeof props === 'object') {\n const keys = Object.keys(props as Record<string, unknown>)\n if (keys.length === 0) return '{}'\n const parts = keys.slice(0, 2).map((k) => {\n const v = (props as Record<string, unknown>)[k]\n return `${k}: ${shortValue(v)}`\n })\n return `{ ${parts.join(', ')}${keys.length > 2 ? `, +${keys.length - 2}` : ''} }`\n }\n return String(props)\n}\n\nfunction shortValue(v: unknown): string {\n if (v === null) return 'null'\n if (v === undefined) return 'undefined'\n if (typeof v === 'string') return `\"${truncate(v, 16)}\"`\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n if (Array.isArray(v)) return `[${v.length}]`\n if (typeof v === 'object') return `{${Object.keys(v as object).length}}`\n return String(v)\n}\n\nfunction truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}…`\n}\n\n// ===========================================================================\n// Cache Inspector — live state, not history\n// ===========================================================================\n\nfunction InspectorView({\n entries,\n filter,\n}: {\n entries: DebugCacheEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, inspectorHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No cache entries\"\n hint=\"Subscribe to a query somewhere in the tree to see its data.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {filtered.map((entry) => (\n <InspectorRow key={entry.key.join('|')} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction inspectorHaystack(e: DebugCacheEntry): string {\n return [...e.key.map(String), e.status, safeStringify(e.data)].join(' ')\n}\n\nfunction InspectorRow({ entry }: { entry: DebugCacheEntry }): ReactElement {\n const kindClass =\n entry.status === 'error'\n ? 'olas-devtools-kind-error'\n : entry.status === 'success'\n ? 'olas-devtools-kind-success'\n : entry.status === 'pending'\n ? 'olas-devtools-kind-warn'\n : ''\n const ageMs = entry.lastUpdatedAt != null ? Date.now() - entry.lastUpdatedAt : null\n const tags: string[] = []\n if (entry.isStale) tags.push('stale')\n if (entry.isFetching) tags.push('fetching')\n if (entry.hasPendingMutations) tags.push('optimistic')\n return (\n <Row\n kind={entry.status}\n kindClass={kindClass}\n target={formatPath(entry.key)}\n t={entry.lastUpdatedAt ?? Date.now()}\n payload={entry.error ?? entry.data}\n suffix={[ageMs != null ? `${formatAge(ageMs)} ago` : '—', ...tags].join(' · ')}\n />\n )\n}\n\nfunction safeStringify(v: unknown): string {\n try {\n return JSON.stringify(v) ?? ''\n } catch {\n return String(v)\n }\n}\n\nfunction formatAge(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`\n return `${Math.round(ms / 3_600_000)}h`\n}\n\n// ===========================================================================\n// URL-hash persistence\n// ===========================================================================\n\nfunction readUrlHash(\n key: string | undefined,\n defaultTab: DevtoolsTab,\n): { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> } {\n const empty = { tree: '', cache: '', inspector: '', mutations: '', fields: '' }\n if (key === undefined) return { tab: defaultTab, filters: empty }\n if (typeof window === 'undefined') return { tab: defaultTab, filters: empty }\n try {\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n const raw = params.get(key)\n if (raw === null) return { tab: defaultTab, filters: empty }\n const parsed = JSON.parse(decodeURIComponent(raw)) as {\n tab?: DevtoolsTab\n filters?: Partial<Record<DevtoolsTab, string>>\n }\n return {\n tab: parsed.tab ?? defaultTab,\n filters: { ...empty, ...(parsed.filters ?? {}) },\n }\n } catch {\n return { tab: defaultTab, filters: empty }\n }\n}\n\nfunction writeUrlHash(\n key: string,\n state: { tab: DevtoolsTab; filters: Record<DevtoolsTab, string> },\n): void {\n if (typeof window === 'undefined') return\n const params = new URLSearchParams(window.location.hash.replace(/^#/, ''))\n params.set(key, encodeURIComponent(JSON.stringify(state)))\n const next = `#${params.toString()}`\n if (next !== window.location.hash) {\n window.history.replaceState(null, '', next)\n }\n}\n\n// ===========================================================================\n// Cache\n// ===========================================================================\n\nfunction CacheView({ entries, filter }: { entries: CacheEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, cacheHaystack)\n if (entries.length === 0) {\n return (\n <Empty title=\"No cache events yet\" hint=\"Trigger a query subscription to see fetches here.\" />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <CacheRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction cacheHaystack(e: CacheEntry): string {\n const parts: string[] = [e.kind, ...e.queryKey.map((p) => String(p))]\n if (e.kind === 'fetch-error') parts.push(safeStringify(e.error))\n if (e.kind === 'subscribed') parts.push(...e.subscriberPath)\n return parts.join(' ')\n}\n\nfunction CacheRow({ entry }: { entry: CacheEntry }): ReactElement {\n const kindClass =\n entry.kind === 'fetch-error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'fetch-success'\n ? 'olas-devtools-kind-success'\n : entry.kind === 'invalidated' || entry.kind === 'gc'\n ? 'olas-devtools-kind-warn'\n : ''\n\n let inline: string | null = null\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'fetch-success') {\n suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'fetch-error') {\n suffix = `${entry.durationMs}ms`\n payload = entry.error\n } else if (entry.kind === 'subscribed') {\n inline = `from ${formatPath(entry.subscriberPath)}`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={formatPath(entry.queryKey)}\n t={entry.t}\n inline={inline}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Mutations\n// ===========================================================================\n\nfunction MutationsView({\n entries,\n filter,\n}: {\n entries: MutationEntry[]\n filter: string\n}): ReactElement {\n const filtered = useFiltered(entries, filter, mutationHaystack)\n if (entries.length === 0) {\n return <Empty title=\"No mutations yet\" hint=\"Trigger a mutation to see the lifecycle here.\" />\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <MutationRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction mutationHaystack(e: MutationEntry): string {\n const parts: string[] = [e.kind, ...e.path, e.name ?? '']\n if (e.kind === 'run') parts.push(safeStringify(e.vars))\n if (e.kind === 'success') parts.push(safeStringify(e.result))\n if (e.kind === 'error') parts.push(safeStringify(e.error))\n return parts.join(' ')\n}\n\nfunction MutationRow({ entry }: { entry: MutationEntry }): ReactElement {\n const kindClass =\n entry.kind === 'error'\n ? 'olas-devtools-kind-error'\n : entry.kind === 'rollback'\n ? 'olas-devtools-kind-rollback'\n : entry.kind === 'success'\n ? 'olas-devtools-kind-success'\n : ''\n\n const target = entry.name ? `${entry.name} · ${formatPath(entry.path)}` : formatPath(entry.path)\n\n let payload: unknown | undefined\n let suffix: string | null = null\n if (entry.kind === 'run') payload = entry.vars\n else if (entry.kind === 'success') {\n payload = entry.result\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n } else if (entry.kind === 'error') {\n payload = entry.error\n if (entry.durationMs !== undefined) suffix = `${entry.durationMs}ms`\n }\n\n return (\n <Row\n kind={entry.kind}\n kindClass={kindClass}\n target={target}\n t={entry.t}\n payload={payload}\n suffix={suffix}\n />\n )\n}\n\n// ===========================================================================\n// Fields\n// ===========================================================================\n\nfunction FieldsView({ entries, filter }: { entries: FieldEntry[]; filter: string }): ReactElement {\n const filtered = useFiltered(entries, filter, fieldHaystack)\n if (entries.length === 0) {\n return (\n <Empty\n title=\"No field validations yet\"\n hint=\"Type into a form bound via ctx.form(...) or ctx.field(...) — each pass lands here.\"\n />\n )\n }\n if (filtered.length === 0) {\n return <Empty title=\"No matches\" hint={`Nothing matches “${filter}”.`} />\n }\n return (\n <ul className=\"olas-devtools-list\">\n {[...filtered].reverse().map((entry) => (\n <FieldRow key={entry.id} entry={entry} />\n ))}\n </ul>\n )\n}\n\nfunction fieldHaystack(e: FieldEntry): string {\n return [e.field, ...e.path, e.valid ? 'valid' : 'invalid', ...e.errors].join(' ')\n}\n\nfunction FieldRow({ entry }: { entry: FieldEntry }): ReactElement {\n const kindClass = entry.valid ? 'olas-devtools-kind-success' : 'olas-devtools-kind-error'\n return (\n <Row\n kind={entry.valid ? 'valid' : 'invalid'}\n kindClass={kindClass}\n target={`${formatPath(entry.path)} · ${entry.field}`}\n t={entry.t}\n inline={entry.errors.length > 0 ? entry.errors.join(' · ') : null}\n />\n )\n}\n\n// ===========================================================================\n// Shared row + helpers\n// ===========================================================================\n\ntype RowProps = {\n kind: string\n kindClass: string\n target: string\n t: number\n /** Either a tiny inline string (durations, urls) OR a structured payload. */\n inline?: string | null\n payload?: unknown\n suffix?: string | null\n}\n\nfunction Row(props: RowProps): ReactElement {\n const { kind, kindClass, target, t, inline, payload, suffix } = props\n const hasPayload = payload !== undefined\n const [expanded, setExpanded] = useState(false)\n const togglable = hasPayload\n\n return (\n <li className={togglable ? 'olas-devtools-row-clickable' : ''}>\n <div\n className=\"olas-devtools-row-top\"\n onClick={togglable ? () => setExpanded((v) => !v) : undefined}\n >\n <span className={`olas-devtools-kind ${kindClass}`}>{kind}</span>\n <span className=\"olas-devtools-target\">{target}</span>\n {suffix !== undefined && suffix !== null && (\n <span className=\"olas-devtools-duration\">{suffix}</span>\n )}\n <span className=\"olas-devtools-time\">{formatTime(t)}</span>\n {togglable && (\n <span\n aria-hidden=\"true\"\n className={`olas-devtools-chevron ${expanded ? 'olas-devtools-chevron-open' : ''}`}\n >\n ›\n </span>\n )}\n </div>\n {inline != null && (\n <div className=\"olas-devtools-payload olas-devtools-payload-inline\">{inline}</div>\n )}\n {hasPayload && expanded && (\n <div className=\"olas-devtools-payload olas-devtools-payload-json\">\n <JsonView value={payload} />\n </div>\n )}\n </li>\n )\n}\n\nfunction useFiltered<T>(items: readonly T[], filter: string, haystack: (item: T) => string): T[] {\n return useMemo(() => {\n if (filter.trim() === '') return [...items]\n const q = filter.toLowerCase()\n return items.filter((item) => haystack(item).toLowerCase().includes(q))\n }, [items, filter, haystack])\n}\n\nfunction Empty({ title, hint }: { title: string; hint: string }): ReactElement {\n return (\n <div className=\"olas-devtools-empty\">\n <div className=\"olas-devtools-empty-title\">{title}</div>\n <div className=\"olas-devtools-empty-hint\">{hint}</div>\n </div>\n )\n}\n","// Floating, draggable, resizable host for the DevtoolsPanel.\n//\n// Renders two things:\n// 1. A small bottom-right launcher button (always present).\n// 2. When open, a `position: fixed` window containing the panel. The header\n// is a drag handle; the south-east corner is a resize grip. Position +\n// size + open + minimized state persist to `localStorage`.\n//\n// API:\n// <DevtoolsLauncher root={root} />\n//\n// Optional props mirror DevtoolsPanel's. The launcher manages the window\n// chrome and ferries the rest through.\n\nimport type { Root } from '@kontsedal/olas-core'\nimport { type ReactElement, useCallback, useEffect, useRef, useState } from 'react'\nimport { DevtoolsPanel, type DevtoolsTab } from './DevtoolsPanel'\nimport { DEVTOOLS_CSS } from './styles'\n\nexport type DevtoolsLauncherProps = {\n root: Pick<Root<unknown>, '__debug'>\n /** Default panel tab. */\n defaultTab?: DevtoolsTab\n /** Cap on each event log. */\n maxEntries?: number\n /** Persist tab+filter state under this key (independent of window state). */\n urlHashKey?: string\n /** localStorage key for window state (position/size/open/minimized). */\n storageKey?: string\n /** Initial position if no persisted state. */\n initial?: { x?: number; y?: number; w?: number; h?: number }\n}\n\ntype WindowState = {\n x: number\n y: number\n w: number\n h: number\n open: boolean\n minimized: boolean\n}\n\nconst MIN_W = 360\nconst MIN_H = 280\nconst HEADER_H = 30\nconst DEFAULT_W = 520\nconst DEFAULT_H = 520\nconst MARGIN = 16\n\nexport function DevtoolsLauncher(props: DevtoolsLauncherProps): ReactElement {\n const storageKey = props.storageKey ?? 'olas-devtools-window'\n const [state, setState] = useState<WindowState>(() => loadState(storageKey, props.initial))\n\n // Persist on change.\n useEffect(() => {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(storageKey, JSON.stringify(state))\n } catch {\n /* swallow */\n }\n }, [state, storageKey])\n\n // Clamp to viewport on window resize so a previously-saved off-screen\n // position recovers gracefully.\n useEffect(() => {\n const onResize = () => setState((s) => clampToViewport(s))\n if (typeof window === 'undefined') return\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n const setOpen = useCallback((open: boolean) => setState((s) => ({ ...s, open })), [])\n const setMin = useCallback((minimized: boolean) => setState((s) => ({ ...s, minimized })), [])\n\n return (\n <>\n <style>{DEVTOOLS_CSS}</style>\n <LauncherButton open={state.open} onClick={() => setOpen(!state.open)} />\n {state.open && (\n <FloatingWindow\n state={state}\n setState={setState}\n onClose={() => setOpen(false)}\n onMinimize={() => setMin(!state.minimized)}\n >\n {!state.minimized && (\n <div className=\"olas-devtools-floating-body\">\n <DevtoolsPanel\n root={props.root}\n defaultTab={props.defaultTab}\n maxEntries={props.maxEntries}\n urlHashKey={props.urlHashKey}\n />\n </div>\n )}\n </FloatingWindow>\n )}\n </>\n )\n}\n\nfunction LauncherButton({ open, onClick }: { open: boolean; onClick: () => void }): ReactElement {\n return (\n <button\n type=\"button\"\n aria-label={open ? 'Hide Olas devtools' : 'Show Olas devtools'}\n onClick={onClick}\n className={`olas-devtools-launcher ${open ? 'olas-devtools-launcher-active' : ''}`}\n >\n <span aria-hidden=\"true\" className=\"olas-devtools-launcher-dot\" />\n <span className=\"olas-devtools-launcher-label\">Olas devtools</span>\n </button>\n )\n}\n\nfunction FloatingWindow(props: {\n state: WindowState\n setState: React.Dispatch<React.SetStateAction<WindowState>>\n onClose: () => void\n onMinimize: () => void\n children: ReactElement | ReactElement[] | false\n}): ReactElement {\n const { state, setState, onClose, onMinimize } = props\n const dragState = useRef<{\n kind: 'move' | 'resize'\n ox: number\n oy: number\n sx: number\n sy: number\n sw: number\n sh: number\n } | null>(null)\n\n const onPointerDown = (kind: 'move' | 'resize') => (e: React.PointerEvent) => {\n e.preventDefault()\n ;(e.target as Element).setPointerCapture(e.pointerId)\n dragState.current = {\n kind,\n ox: e.clientX,\n oy: e.clientY,\n sx: state.x,\n sy: state.y,\n sw: state.w,\n sh: state.h,\n }\n }\n const onPointerMove = (e: React.PointerEvent) => {\n const d = dragState.current\n if (d === null) return\n const dx = e.clientX - d.ox\n const dy = e.clientY - d.oy\n if (d.kind === 'move') {\n setState((s) => clampToViewport({ ...s, x: d.sx + dx, y: d.sy + dy }))\n } else {\n const w = Math.max(MIN_W, d.sw + dx)\n const h = Math.max(MIN_H, d.sh + dy)\n setState((s) => clampToViewport({ ...s, w, h }))\n }\n }\n const onPointerUp = () => {\n dragState.current = null\n }\n\n const minimized = state.minimized\n const height = minimized ? HEADER_H : state.h\n\n return (\n <div\n className=\"olas-devtools-floating\"\n role=\"dialog\"\n aria-label=\"Olas devtools\"\n style={{ left: state.x, top: state.y, width: state.w, height }}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"olas-devtools-floating-header\" onPointerDown={onPointerDown('move')}>\n <span className=\"olas-devtools-floating-grip\" aria-hidden=\"true\">\n ⠿\n </span>\n <span className=\"olas-devtools-floating-title\">Olas devtools</span>\n <div className=\"olas-devtools-floating-actions\">\n <button\n type=\"button\"\n aria-label={minimized ? 'Expand' : 'Minimize'}\n onClick={onMinimize}\n className=\"olas-devtools-floating-action\"\n >\n {minimized ? '▢' : '–'}\n </button>\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className=\"olas-devtools-floating-action\"\n >\n ×\n </button>\n </div>\n </div>\n {props.children}\n {!minimized && (\n <span\n className=\"olas-devtools-floating-resize\"\n onPointerDown={onPointerDown('resize')}\n aria-label=\"Resize\"\n role=\"separator\"\n />\n )}\n </div>\n )\n}\n\nfunction clampToViewport(s: WindowState): WindowState {\n if (typeof window === 'undefined') return s\n const vw = window.innerWidth\n const vh = window.innerHeight\n const w = Math.min(s.w, vw - MARGIN * 2)\n const h = Math.min(s.h, vh - MARGIN * 2)\n const x = Math.max(MARGIN, Math.min(s.x, vw - w - MARGIN))\n const y = Math.max(MARGIN, Math.min(s.y, vh - HEADER_H - MARGIN))\n return { ...s, x, y, w, h }\n}\n\nfunction loadState(\n storageKey: string,\n initial?: { x?: number; y?: number; w?: number; h?: number },\n): WindowState {\n const vw = typeof window !== 'undefined' ? window.innerWidth : 1024\n const vh = typeof window !== 'undefined' ? window.innerHeight : 768\n const w = initial?.w ?? DEFAULT_W\n const h = initial?.h ?? DEFAULT_H\n const defaults: WindowState = {\n x: initial?.x ?? Math.max(MARGIN, vw - w - MARGIN),\n y: initial?.y ?? Math.max(MARGIN, vh - h - MARGIN - 56),\n w,\n h,\n open: false,\n minimized: false,\n }\n if (typeof localStorage === 'undefined') return defaults\n try {\n const raw = localStorage.getItem(storageKey)\n if (!raw) return defaults\n const parsed = JSON.parse(raw) as Partial<WindowState>\n return clampToViewport({\n x: parsed.x ?? defaults.x,\n y: parsed.y ?? defaults.y,\n w: parsed.w ?? defaults.w,\n h: parsed.h ?? defaults.h,\n open: parsed.open ?? false,\n minimized: parsed.minimized ?? false,\n })\n } catch {\n return defaults\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontsedal/olas-devtools",
|
|
3
|
-
"version": "0.0.1
|
|
3
|
+
"version": "0.0.1",
|
|
4
4
|
"description": "Olas in-app devtools — controller tree inspector, cache timeline, mutation log, signal graph.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"olas",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"sideEffects": false,
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"react": ">=18",
|
|
49
|
-
"@kontsedal/olas-
|
|
50
|
-
"@kontsedal/olas-
|
|
49
|
+
"@kontsedal/olas-react": "^0.0.1",
|
|
50
|
+
"@kontsedal/olas-core": "^0.0.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@testing-library/react": "^16.3.2",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"@types/react-dom": "^19.2.3",
|
|
56
56
|
"react": "^19.2.6",
|
|
57
57
|
"react-dom": "^19.2.6",
|
|
58
|
-
"@kontsedal/olas-
|
|
59
|
-
"@kontsedal/olas-
|
|
58
|
+
"@kontsedal/olas-react": "^0.0.1",
|
|
59
|
+
"@kontsedal/olas-core": "^0.0.1"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
package/src/store.ts
CHANGED
|
@@ -134,6 +134,10 @@ export class DevtoolsStore {
|
|
|
134
134
|
return
|
|
135
135
|
case 'controller:disposed':
|
|
136
136
|
this.tree$.set(setNodeState(this.tree$.peek(), event.path, 'disposed'))
|
|
137
|
+
// A controller that disposed mid-mutation (before `success`/`error`
|
|
138
|
+
// ever fired) would otherwise leave its `mutation:run` start entry
|
|
139
|
+
// in `mutationStarts` forever. Drop any starts under this path.
|
|
140
|
+
this.dropStartsForPath(event.path)
|
|
137
141
|
return
|
|
138
142
|
case 'cache:subscribed':
|
|
139
143
|
this.pushCache({
|
|
@@ -212,6 +216,10 @@ export class DevtoolsStore {
|
|
|
212
216
|
this.cache$.set([])
|
|
213
217
|
this.mutations$.set([])
|
|
214
218
|
this.fields$.set([])
|
|
219
|
+
// Drop pending mutation-start timing records too — `clearLogs()` is the
|
|
220
|
+
// user's "start fresh" gesture; any subsequent `success`/`error` for a
|
|
221
|
+
// pre-clear `run` would have produced a duration anchored to noise.
|
|
222
|
+
this.mutationStarts.clear()
|
|
215
223
|
}
|
|
216
224
|
|
|
217
225
|
// -----------------------------------------------------------------------
|
|
@@ -240,6 +248,23 @@ export class DevtoolsStore {
|
|
|
240
248
|
this.mutationStarts.delete(key)
|
|
241
249
|
return this.now() - startedAt
|
|
242
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Drop every pending mutation-start record under `path` (and its
|
|
254
|
+
* descendants). Called on `controller:disposed` so a dispose mid-mutation
|
|
255
|
+
* doesn't leave a permanent entry in `mutationStarts`.
|
|
256
|
+
*/
|
|
257
|
+
private dropStartsForPath(path: readonly string[]): void {
|
|
258
|
+
if (this.mutationStarts.size === 0) return
|
|
259
|
+
const prefix = `${path.join('>')}>`
|
|
260
|
+
const exact = path.join('>')
|
|
261
|
+
for (const key of this.mutationStarts.keys()) {
|
|
262
|
+
const beforeHash = key.split('#')[0] ?? ''
|
|
263
|
+
if (beforeHash === exact || beforeHash.startsWith(prefix)) {
|
|
264
|
+
this.mutationStarts.delete(key)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
243
268
|
}
|
|
244
269
|
|
|
245
270
|
function mutationKey(path: readonly string[], name: string | undefined): string {
|