@kontsedal/olas-devtools 0.0.1 → 0.0.2

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/dist/index.cjs CHANGED
@@ -1319,11 +1319,11 @@ function countLiveControllers(node) {
1319
1319
  return Math.max(total - 1, 0);
1320
1320
  }
1321
1321
  function TreeView({ tree, mutations }) {
1322
+ const pending = (0, react.useMemo)(() => rollupPending(mutations), [mutations]);
1322
1323
  if (tree.children.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Empty, {
1323
1324
  title: "No controllers yet",
1324
1325
  hint: "The root hasn't constructed any controllers."
1325
1326
  });
1326
- const pending = (0, react.useMemo)(() => rollupPending(mutations), [mutations]);
1327
1327
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1328
1328
  className: "olas-devtools-tree",
1329
1329
  children: tree.children.map((child) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TreeNode, {
@@ -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 // 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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"}
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 // 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). Computed unconditionally — must run before any early\n // return so hook-order is stable across renders (rules of hooks).\n const pending = useMemo(() => rollupPending(mutations), [mutations])\n if (tree.children.length === 0) {\n return <Empty title=\"No controllers yet\" hint=\"The root hasn't constructed any controllers.\" />\n }\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/dist/index.mjs CHANGED
@@ -1318,11 +1318,11 @@ function countLiveControllers(node) {
1318
1318
  return Math.max(total - 1, 0);
1319
1319
  }
1320
1320
  function TreeView({ tree, mutations }) {
1321
+ const pending = useMemo(() => rollupPending(mutations), [mutations]);
1321
1322
  if (tree.children.length === 0) return /* @__PURE__ */ jsx(Empty, {
1322
1323
  title: "No controllers yet",
1323
1324
  hint: "The root hasn't constructed any controllers."
1324
1325
  });
1325
- const pending = useMemo(() => rollupPending(mutations), [mutations]);
1326
1326
  return /* @__PURE__ */ jsx("div", {
1327
1327
  className: "olas-devtools-tree",
1328
1328
  children: tree.children.map((child) => /* @__PURE__ */ jsx(TreeNode, {
@@ -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 // 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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"}
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 // 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). Computed unconditionally — must run before any early\n // return so hook-order is stable across renders (rules of hooks).\n const pending = useMemo(() => rollupPending(mutations), [mutations])\n if (tree.children.length === 0) {\n return <Empty title=\"No controllers yet\" hint=\"The root hasn't constructed any controllers.\" />\n }\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.2",
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-react": "^0.0.1",
50
- "@kontsedal/olas-core": "^0.0.1"
49
+ "@kontsedal/olas-core": "^0.0.2",
50
+ "@kontsedal/olas-react": "^0.0.2"
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-react": "^0.0.1",
59
- "@kontsedal/olas-core": "^0.0.1"
58
+ "@kontsedal/olas-core": "^0.0.2",
59
+ "@kontsedal/olas-react": "^0.0.2"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public"
@@ -257,13 +257,14 @@ function TreeView({
257
257
  tree: ControllerNode
258
258
  mutations: MutationEntry[]
259
259
  }): ReactElement {
260
- if (tree.children.length === 0) {
261
- return <Empty title="No controllers yet" hint="The root hasn't constructed any controllers." />
262
- }
263
260
  // Roll up pending-mutation counts per controller path. A "pending" mutation
264
261
  // is one whose last entry is `run` with no matching success/error for the
265
- // same (path, name).
262
+ // same (path, name). Computed unconditionally — must run before any early
263
+ // return so hook-order is stable across renders (rules of hooks).
266
264
  const pending = useMemo(() => rollupPending(mutations), [mutations])
265
+ if (tree.children.length === 0) {
266
+ return <Empty title="No controllers yet" hint="The root hasn't constructed any controllers." />
267
+ }
267
268
  return (
268
269
  <div className="olas-devtools-tree">
269
270
  {tree.children.map((child) => (