@vibecook/ice 0.3.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +4 -4
  2. package/dist/{chunk-2GUYATUM.js → chunk-BFT7YWS2.js} +44 -11
  3. package/dist/chunk-BFT7YWS2.js.map +1 -0
  4. package/dist/{chunk-PKK6GLSA.js → chunk-ELCBCTKY.js} +138 -13
  5. package/dist/chunk-ELCBCTKY.js.map +1 -0
  6. package/dist/{chunk-5QY7HK6G.js → chunk-JLXETATP.js} +5594 -2712
  7. package/dist/chunk-JLXETATP.js.map +1 -0
  8. package/dist/devtools.js +5 -2
  9. package/dist/devtools.js.map +1 -1
  10. package/dist/dom.js +16 -2
  11. package/dist/ground.js +1 -1
  12. package/dist/index.js +39 -1
  13. package/dist/r3f.js +3 -3
  14. package/dist/react.js +5 -3
  15. package/dist/types/packages/core/src/behavior/compile.d.ts +74 -0
  16. package/dist/types/packages/core/src/behavior/define-behavior.d.ts +55 -0
  17. package/dist/types/packages/core/src/behavior/differ.d.ts +91 -0
  18. package/dist/types/packages/core/src/behavior/harness.d.ts +65 -0
  19. package/dist/types/packages/core/src/behavior/migrate.d.ts +87 -0
  20. package/dist/types/packages/core/src/behavior/public-reads.d.ts +31 -0
  21. package/dist/types/packages/core/src/behavior/runtime.d.ts +88 -0
  22. package/dist/types/packages/core/src/behavior/types.d.ts +312 -0
  23. package/dist/types/packages/core/src/behavior/world-view.d.ts +36 -0
  24. package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
  25. package/dist/types/packages/core/src/catalog/selection-presence.d.ts +10 -0
  26. package/dist/types/packages/core/src/doc/rename-sweep.d.ts +3 -0
  27. package/dist/types/packages/core/src/doc/rename.d.ts +7 -0
  28. package/dist/types/packages/core/src/doc/version-gate.d.ts +14 -1
  29. package/dist/types/packages/core/src/engine/engine.d.ts +38 -1
  30. package/dist/types/packages/core/src/engine/guests.d.ts +196 -0
  31. package/dist/types/packages/core/src/engine/reflectors.d.ts +8 -0
  32. package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +16 -0
  33. package/dist/types/packages/core/src/guards/guarded-tx.d.ts +57 -0
  34. package/dist/types/packages/core/src/index.d.ts +10 -3
  35. package/dist/types/packages/core/src/input/queue.d.ts +9 -0
  36. package/dist/types/packages/core/src/schema/meta.d.ts +12 -0
  37. package/dist/types/packages/core/src/widget/define-widget.d.ts +89 -0
  38. package/dist/types/packages/core/src/widget/props.d.ts +35 -1
  39. package/dist/types/packages/core/src/widget/spawn.d.ts +14 -0
  40. package/dist/types/packages/dom/src/index.d.ts +2 -0
  41. package/dist/types/packages/dom/src/input-ownership.d.ts +52 -0
  42. package/dist/types/packages/dom/src/loop.d.ts +9 -0
  43. package/dist/types/packages/dom/src/pointer-adapter.d.ts +16 -3
  44. package/dist/types/packages/dom/src/widget-focus.d.ts +58 -0
  45. package/dist/types/packages/react/src/hooks.d.ts +12 -1
  46. package/dist/types/packages/react/src/index.d.ts +1 -1
  47. package/dist/types/packages/react/src/infinite-canvas.d.ts +20 -2
  48. package/dist/types/packages/react/src/keymap.d.ts +23 -4
  49. package/package.json +5 -5
  50. package/dist/chunk-2GUYATUM.js.map +0 -1
  51. package/dist/chunk-5QY7HK6G.js.map +0 -1
  52. package/dist/chunk-PKK6GLSA.js.map +0 -1
package/dist/devtools.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  WheelPan,
15
15
  WheelZoom,
16
16
  Wire
17
- } from "./chunk-5QY7HK6G.js";
17
+ } from "./chunk-JLXETATP.js";
18
18
  import "./chunk-OR5FNVUE.js";
19
19
 
20
20
  // ../devtools/src/attach.ts
@@ -505,8 +505,11 @@ function attachDevtools(engine, opts = {}) {
505
505
  if (profiler !== null) {
506
506
  core.enableTelemetry();
507
507
  removePublish = core.onPublish(() => {
508
- const us = core.lastFrame()?.phaseFlushMicros.get("reflect");
508
+ const us = core.lastFrame()?.reflectMicros;
509
509
  if (us !== void 0 && us > 0) profiler.lane("reflect", us / 1e3);
510
+ for (const g of core.guests.list()) {
511
+ if (g.status === "running" && g.lastMs > 0) profiler.lane(`guest:${g.id}`, g.lastMs);
512
+ }
510
513
  });
511
514
  }
512
515
  let glPanel = null;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../devtools/src/attach.ts","../../devtools/src/dock.ts","../../devtools/src/gl-panel.ts","../../devtools/src/index.ts"],"sourcesContent":["/**\n * @ice/devtools — strata's first-party tools, engine-flavored (rebuilt\n * 2026-07-13, superseding the M10 hand-rolled 4-tab panel: the standing rule\n * is observers/inspectors WRAP `@vibecook/strata-ecs/tools`, never\n * re-implement them).\n *\n * `attachDevtools(engine)` = strata's observer panel + FPS profiler, plus the\n * engine glue strata cannot know:\n *\n * DESCRIBE PrefabId → widget type; recognizers → kind + live phase;\n * pointers/ports/wires/peers/chrome labeled as such.\n * DURABLE TAB fed from the facade's CURRENT DocSession (docId, converged\n * snapshot, attachment baseline) — the getter re-reads per\n * poll, so it tracks docs.create/open/join/close live.\n * EPHEMERAL TAB fed from an app-provided PresenceSession getter (the\n * facade keeps presence private; collab demos pass theirs).\n * \"reflect\" LANE the engine's post-notify reflector flush cost — the one\n * frame slice strata's world hooks cannot see — reported\n * into the profiler via an onPublish hook reading\n * `lastFrame()` telemetry (one frame late by construction:\n * publish runs before this frame's reflect).\n *\n * By default all three tools mount into ONE draggable dock (dock.ts —\n * 2026-07-13, James: \"make the 3 tools into one draggable panel\"); pass\n * `dock: false` for the classic scattered corners.\n *\n * Reads only, outside the tick (verified-unrestricted); never a reflector,\n * never writes ECS. Nobody imports this package (depcruise-enforced leaf).\n */\nimport type { Entity, World } from \"@vibecook/strata-ecs\";\nimport {\n attachObserver,\n attachProfiler,\n defaultDescribe,\n type DescribeFn,\n type EntityDescription,\n type ObserverDurableSource,\n type ObserverEphemeralSource,\n type ObserverHandle,\n type ObserverOptions,\n type ProfilerHandle,\n type ProfilerOptions,\n} from \"@vibecook/strata-ecs/tools\";\nimport { createDock, type Dock, type DockOptions, type DockSlotId } from \"./dock\";\nimport { createGlPanel, type GlPanel, type GlPanelOptions, type GlPanelStats } from \"./gl-panel\";\nimport {\n CursorVisual,\n Drag,\n GesturePhases,\n GestureSuspended,\n HandleSpec,\n LongPress,\n Pinch,\n Pointer,\n Port,\n PrefabId,\n PresencePeer,\n Tap,\n WheelPan,\n WheelZoom,\n Wire,\n type CanvasEngine,\n type Engine,\n type PresenceSession,\n} from \"@ice/core\";\n\nexport interface DevtoolsOpts {\n /** Where the dock (or, with `dock: false`, each panel) mounts (default: document.body). */\n readonly container?: HTMLElement;\n /**\n * ONE draggable dock hosting all three tools (the default). `false` scatters\n * them to their classic fixed corners; an object sets initial placement.\n * The dock mounts lazily with the first tool that needs it.\n */\n readonly dock?: boolean | Pick<DockOptions, \"corner\" | \"title\" | \"width\">;\n /** Observer panel: `false` to omit, or strata passthrough options. */\n readonly observer?: boolean | Pick<ObserverOptions, \"defaultTab\" | \"tab\" | \"recorderCap\">;\n /** Profiler HUD: `false` to omit, or strata passthrough options. */\n readonly profiler?: boolean | Pick<ProfilerOptions, \"budgetMs\" | \"windowSize\" | \"corner\" | \"expanded\" | \"lanes\">;\n /** GL metrics panel: `false` to omit, or placement/budget options. Mounts lazily on the first `glStats` push. */\n readonly glPanel?: boolean | Pick<GlPanelOptions, \"corner\" | \"budgetMs\" | \"expanded\">;\n /** Collab demos: the presence session whose store feeds the ephemeral tab. */\n readonly presence?: () => PresenceSession | null | undefined;\n /** Override entity labeling (default: the engine describe below). */\n readonly describe?: DescribeFn;\n}\n\nexport interface DevtoolsHandle {\n readonly observer: ObserverHandle | null;\n readonly profiler: ProfilerHandle | null;\n /** Host-cost lane passthrough (paint, decode…); no-op when the profiler is off. */\n lane(name: string, ms: number): void;\n /**\n * Feed one GL frame's metrics (wire `<GLViews onFrameStats>` here). The GL\n * panel mounts on the first push — renderer counts, virtual-texture census,\n * LOD banding, cull population. No-op when opts.glPanel === false.\n */\n glStats(stats: GlPanelStats): void;\n /** Tear all panels + the telemetry hook down. Idempotent. */\n detach(): void;\n}\n\nconst COLOR = {\n widget: \"#3fb950\",\n gesture: \"#a371f7\",\n pointer: \"#58a6ff\",\n graph: \"#ffa657\",\n presence: \"#f778ba\",\n chrome: \"#8b949e\",\n} as const;\n\nconst PHASE_ORDER = [\"Active\", \"Possible\", \"Recognized\", \"Pending\", \"Ended\", \"Cancelled\", \"Failed\"] as const;\n\nfunction phaseOf(world: World, e: Entity): string | null {\n // Suspended is a rider OUTSIDE the phase set (a pinch freezes single-pointer\n // recognizers without ending them) — surface it first, it explains \"stuck\".\n if (world.hasTag(e, GestureSuspended)) return \"suspended\";\n for (const name of PHASE_ORDER) {\n if (world.hasTag(e, GesturePhases.tags[name])) return name.toLowerCase();\n }\n return null;\n}\n\n/**\n * Engine-aware entity labeling for the observer's lists + timeline: identity\n * off ROLE (widget type, recognizer kind + phase, pointer device), not off\n * raw component-name composition. Falls back to strata's defaultDescribe.\n */\nexport function engineDescribe(world: World, e: Entity): EntityDescription {\n const prefab = world.get(e, PrefabId)?.id;\n if (typeof prefab === \"string\") return { label: prefab, color: COLOR.widget };\n\n const kind = world.has(e, Tap)\n ? \"tap\"\n : world.has(e, LongPress)\n ? \"longpress\"\n : world.has(e, Pinch)\n ? \"pinch\"\n : world.has(e, WheelZoom)\n ? \"wheelzoom\"\n : world.has(e, WheelPan)\n ? \"wheelpan\"\n : world.has(e, Drag)\n ? \"drag\"\n : undefined;\n if (kind !== undefined) return { label: kind, color: COLOR.gesture, phase: phaseOf(world, e) };\n\n if (world.has(e, Pointer)) return { label: `pointer:${world.read(e, Pointer).device}`, color: COLOR.pointer };\n if (world.has(e, Port)) return { label: \"port\", color: COLOR.graph };\n if (world.hasTag(e, Wire)) return { label: \"wire\", color: COLOR.graph };\n if (world.hasTag(e, PresencePeer)) return { label: \"peer\", color: COLOR.presence };\n if (world.has(e, CursorVisual)) return { label: \"cursor\", color: COLOR.presence };\n if (world.has(e, HandleSpec)) return { label: \"handle\", color: COLOR.chrome };\n return defaultDescribe(world, e);\n}\n\n/** Accepts the facade (durable tab included) or a bare core engine. */\nexport type DevtoolsEngine = CanvasEngine | Engine;\n\nexport function attachDevtools(engine: DevtoolsEngine, opts: DevtoolsOpts = {}): DevtoolsHandle {\n const isFacade = \"docs\" in engine;\n const core: Engine = isFacade ? engine.engine : engine;\n const world = core.world;\n\n // Durable tab: only the facade knows the current session; the getter is\n // re-read each poll so it swaps live across doc open/close/join. The store\n // and attachment are the STRUCTURAL mirrors the observer contract documents\n // (docId + snapshot / baseline) — never a sync path.\n const docs = isFacade ? engine.docs : undefined;\n const durable =\n docs === undefined\n ? undefined\n : (): ObserverDurableSource | null => {\n const s = docs.current();\n if (s === undefined) return null;\n return {\n store: s.store as unknown as ObserverDurableSource[\"store\"],\n attachment: s.attachment as unknown as ObserverDurableSource[\"attachment\"],\n };\n };\n\n const presence = opts.presence;\n const ephemeral =\n presence === undefined\n ? undefined\n : (): ObserverEphemeralSource | null => presence()?.eph ?? null;\n\n // The dock is created with the first tool that mounts (so all-flags-off\n // attaches — and glPanel-only attaches before the first push — stay DOM-free).\n let dock: Dock | null = null;\n const mountIn = (id: DockSlotId): { container?: HTMLElement } => {\n if (opts.dock === false) return opts.container !== undefined ? { container: opts.container } : {};\n if (dock === null) {\n dock = createDock({\n ...(typeof opts.dock === \"object\" ? opts.dock : {}),\n ...(opts.container !== undefined ? { container: opts.container } : {}),\n });\n }\n return { container: dock.slot(id) };\n };\n\n const observer =\n opts.observer === false\n ? null\n : attachObserver(world, {\n ...(typeof opts.observer === \"object\" ? opts.observer : {}),\n ...mountIn(\"observer\"),\n describe: opts.describe ?? engineDescribe,\n ...(durable !== undefined ? { durable } : {}),\n ...(ephemeral !== undefined ? { ephemeral } : {}),\n });\n\n const profiler =\n opts.profiler === false\n ? null\n : attachProfiler(world, {\n ...(typeof opts.profiler === \"object\" ? opts.profiler : {}),\n ...mountIn(\"profiler\"),\n });\n\n // The \"reflect\" lane: reflector flushes run POST-notify, invisible to\n // strata's in-world hooks. Publish runs before this frame's reflect, so the\n // hook reports the PREVIOUS frame's cost — a one-frame lag a HUD can carry.\n let removePublish: (() => void) | undefined;\n if (profiler !== null) {\n core.enableTelemetry();\n removePublish = core.onPublish(() => {\n const us = core.lastFrame()?.phaseFlushMicros.get(\"reflect\");\n if (us !== undefined && us > 0) profiler.lane(\"reflect\", us / 1000);\n });\n }\n\n let glPanel: GlPanel | null = null;\n let detached = false;\n return {\n observer,\n profiler,\n lane(name, ms) {\n profiler?.lane(name, ms);\n },\n glStats(stats) {\n if (detached || opts.glPanel === false) return;\n if (glPanel === null) {\n glPanel = createGlPanel({\n ...(typeof opts.glPanel === \"object\" ? opts.glPanel : {}),\n ...mountIn(\"gl\"),\n });\n }\n glPanel.push(stats);\n },\n detach() {\n if (detached) return;\n detached = true;\n removePublish?.();\n observer?.dispose();\n profiler?.dispose();\n glPanel?.dispose();\n glPanel = null;\n dock?.dispose();\n dock = null;\n },\n };\n}\n","/**\n * The devtools dock — ONE draggable window hosting all three tools\n * (2026-07-13, James: \"make the 3 tools into one draggable panel\").\n *\n * strata's observer + profiler and our GL panel each mount `position: fixed`\n * into their own corner. The dock does NOT re-implement any of them (standing\n * rule: wrap first-party tools): it is a fixed, draggable shell with three\n * stacked slots; each tool mounts into its slot via its own `container`\n * option, and dock-scoped CSS de-windows them (`position: static`, full\n * width, no border/shadow) so they read as sections of one panel. Their own\n * behavior is untouched — the observer keeps its tabs/collapse/vertical\n * resize (its header drag goes inert: left/top are ignored at static), the\n * profiler + GL strips keep their click-to-expand.\n *\n * Slot order is fixed by the dock, not by mount order: profiler and GL are\n * one-line summary strips, the observer is the deep-dive — strips on top.\n * Layout (x/y/width/open) persists to localStorage, same as the observer:\n * schema edits full-reload the page by design; the dock must stay put.\n */\n\nexport type DockCorner = \"tl\" | \"tr\" | \"bl\" | \"br\";\n\nexport type DockSlotId = \"profiler\" | \"gl\" | \"observer\";\n\nexport interface DockOptions {\n /** Where the dock mounts (default: document.body). */\n readonly container?: HTMLElement;\n /** Initial corner when nothing is persisted yet (default \"tr\"). */\n readonly corner?: DockCorner;\n /** Header title (default \"ice devtools\"). */\n readonly title?: string;\n /** Initial width in px when nothing is persisted yet (default 420). */\n readonly width?: number;\n}\n\nexport interface Dock {\n readonly el: HTMLElement;\n /** The mount element for one tool. Empty slots are display:none via CSS. */\n slot(id: DockSlotId): HTMLElement;\n dispose(): void;\n}\n\nconst LS_KEY = \"ice-dock:layout\";\nconst STYLE_ID = \"ice-dock-style\";\nconst MARGIN = 8;\nconst KEEP = 90; // px that must stay on screen so the header is always grabbable\nconst SLOT_ORDER: readonly DockSlotId[] = [\"profiler\", \"gl\", \"observer\"];\n\ninterface Layout {\n x?: number;\n y?: number;\n w?: number;\n open?: boolean;\n}\n\nfunction loadLayout(): Layout {\n try {\n return JSON.parse(localStorage.getItem(LS_KEY) ?? \"{}\") as Layout;\n } catch {\n return {};\n }\n}\n\nfunction saveLayout(patch: Layout): void {\n try {\n localStorage.setItem(LS_KEY, JSON.stringify({ ...loadLayout(), ...patch }));\n } catch {\n /* private mode / quota — non-fatal for a dev tool */\n }\n}\n\n// strata-obs's window chrome, verbatim, plus the de-windowing overrides for\n// the hosted tools. `!important` beats both the tools' own stylesheets and\n// the observer's inline width restored from ITS localStorage layout.\nconst CSS = `\n.ice-dock { position: fixed; z-index: 99999; width: 420px; min-width: 340px; max-width: 92vw;\n background: rgba(13,17,23,.96); border: 1px solid #30363d; border-radius: 10px;\n color: #e6edf3; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;\n box-shadow: 0 8px 28px rgba(0,0,0,.45); overflow: hidden; resize: horizontal; }\n.ice-dock * { box-sizing: border-box; }\n.ice-dock.collapsed { resize: none; }\n.ice-dock.dragging { opacity: .92; cursor: grabbing; }\n.ice-dock-head { display: flex; align-items: center; gap: 7px; padding: 7px 10px; cursor: grab;\n background: rgba(22,27,34,.96); user-select: none; }\n.ice-dock-grip { color: #484f58; }\n.ice-dock-name { font-weight: 700; color: #56d4dd; }\n.ice-dock-btn { background: none; border: none; color: #8b949e; cursor: pointer; font: inherit;\n padding: 2px 6px; margin-left: auto; }\n.ice-dock-btn:hover { color: #e6edf3; }\n.ice-dock-body { max-height: calc(100vh - 96px); overflow-y: auto; }\n.ice-dock.collapsed .ice-dock-body { display: none; }\n.ice-dock-slot { border-top: 1px solid #30363d; }\n.ice-dock-slot:empty { display: none; }\n\n/* De-window the hosted tools — the dock is the window now. */\n.ice-dock .strata-obs, .ice-dock .strata-prof, .ice-dock .ice-gl {\n position: static !important; width: auto !important; min-width: 0 !important;\n border: none !important; border-radius: 0 !important; box-shadow: none !important; }\n.ice-dock .strata-obs { resize: vertical !important; }\n.ice-dock .strata-obs-head { cursor: default; }\n.ice-dock .strata-prof-body, .ice-dock .ice-gl-body { min-width: 0; }\n`;\n\nfunction injectStyle(doc: Document): void {\n if (doc.getElementById(STYLE_ID) !== null) return;\n const el = doc.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n doc.head.appendChild(el);\n}\n\nexport function createDock(opts: DockOptions = {}): Dock {\n const container = opts.container ?? document.body;\n const doc = container.ownerDocument;\n const win = doc.defaultView ?? window;\n injectStyle(doc);\n\n const clampPos = (x: number, y: number, w: number): { x: number; y: number } => {\n const maxX = win.innerWidth - KEEP;\n const minX = Math.min(MARGIN, MARGIN - (w - KEEP));\n const maxY = win.innerHeight - 34;\n return { x: Math.min(Math.max(x, minX), maxX), y: Math.min(Math.max(y, MARGIN), maxY) };\n };\n\n const root = doc.createElement(\"div\");\n root.className = \"ice-dock\";\n\n const head = doc.createElement(\"div\");\n head.className = \"ice-dock-head\";\n const grip = doc.createElement(\"span\");\n grip.className = \"ice-dock-grip\";\n grip.setAttribute(\"aria-hidden\", \"true\");\n grip.textContent = \"⠿\";\n const name = doc.createElement(\"span\");\n name.className = \"ice-dock-name\";\n name.textContent = opts.title ?? \"ice devtools\";\n const collapse = doc.createElement(\"button\");\n collapse.type = \"button\";\n collapse.className = \"ice-dock-btn\";\n collapse.title = \"Collapse\";\n head.append(grip, name, collapse);\n\n const body = doc.createElement(\"div\");\n body.className = \"ice-dock-body\";\n const slots = new Map<DockSlotId, HTMLElement>();\n for (const id of SLOT_ORDER) {\n const s = doc.createElement(\"div\");\n s.className = \"ice-dock-slot\";\n s.dataset.slot = id;\n body.appendChild(s);\n slots.set(id, s);\n }\n\n root.append(head, body);\n container.appendChild(root);\n\n // restore layout: width, then position (clamped), then open state\n const l = loadLayout();\n const w = typeof l.w === \"number\" ? l.w : (opts.width ?? 420);\n root.style.width = `${w}px`;\n const corner = opts.corner ?? \"tr\";\n const fx = corner === \"tl\" || corner === \"bl\" ? 16 : win.innerWidth - w - 16;\n const fy = corner === \"tl\" || corner === \"tr\" ? 16 : win.innerHeight - 220;\n const p = clampPos(l.x ?? fx, l.y ?? fy, w);\n root.style.left = `${p.x}px`;\n root.style.top = `${p.y}px`;\n let open = l.open ?? true;\n const applyOpen = (): void => {\n root.classList.toggle(\"collapsed\", !open);\n collapse.textContent = open ? \"▾\" : \"▸\";\n };\n applyOpen();\n collapse.addEventListener(\"click\", () => {\n open = !open;\n saveLayout({ open });\n applyOpen();\n });\n\n let drag: { id: number; sx: number; sy: number; ox: number; oy: number } | null = null;\n head.addEventListener(\"pointerdown\", (e) => {\n if ((e.target as HTMLElement).closest(\"button\") !== null) return; // let buttons be buttons\n const r = root.getBoundingClientRect();\n drag = { id: e.pointerId, sx: e.clientX, sy: e.clientY, ox: r.left, oy: r.top };\n if (typeof head.setPointerCapture === \"function\") {\n try {\n head.setPointerCapture(e.pointerId);\n } catch {\n /* synthetic events (tests) have no active pointer */\n }\n }\n root.classList.add(\"dragging\");\n e.preventDefault();\n });\n head.addEventListener(\"pointermove\", (e) => {\n if (drag?.id !== e.pointerId) return;\n const q = clampPos(drag.ox + (e.clientX - drag.sx), drag.oy + (e.clientY - drag.sy), root.offsetWidth);\n root.style.left = `${q.x}px`;\n root.style.top = `${q.y}px`;\n });\n const endDrag = (e: PointerEvent): void => {\n if (drag?.id !== e.pointerId) return;\n drag = null;\n root.classList.remove(\"dragging\");\n const r = root.getBoundingClientRect();\n saveLayout({ x: r.left, y: r.top });\n };\n head.addEventListener(\"pointerup\", endDrag);\n head.addEventListener(\"pointercancel\", endDrag);\n\n const onWinResize = (): void => {\n const r = root.getBoundingClientRect();\n const q = clampPos(r.left, r.top, root.offsetWidth);\n root.style.left = `${q.x}px`;\n root.style.top = `${q.y}px`;\n };\n win.addEventListener(\"resize\", onWinResize);\n const ro = new ResizeObserver(() => {\n saveLayout({ w: root.offsetWidth });\n });\n ro.observe(root);\n\n let disposed = false;\n return {\n el: root,\n slot(id) {\n // SLOT_ORDER covers the full DockSlotId union — the map is total.\n return slots.get(id) as HTMLElement;\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n win.removeEventListener(\"resize\", onWinResize);\n ro.disconnect();\n root.remove();\n },\n };\n}\n","/**\n * The GL metrics panel — the engine-specific renderer readout strata has no\n * counterpart for (2026-07-13, James: \"comprehensive r3f profiler ... and our\n * engine specific optimization metrics ... with nice visuals\").\n *\n * Pure DOM, strata's visual language (same palette/typography as the observer\n * + profiler HUD so the three read as one toolset). Fed by pushes of\n * `GlPanelStats` — the STRUCTURAL MIRROR of @ice/r3f's `GlFrameStats`\n * (devtools cannot import r3f under the walls; keep the shapes aligned):\n *\n * head fps · gpu ms + a gpu sparkline against the frame budget\n * frame cpu / gpu bars vs budget, with session maxima\n * render draw calls · triangles · points/lines · programs · geoms · textures\n * virtual live render targets + total resolution, FBO bytes vs budget,\n * textures the island phase census as a segmented bar, repaint/evict counters\n * lod zoom → hysteresis band → effective DPR, live targets per band\n * widgets Visible vs Culled population\n *\n * All values land via `textContent` (never innerHTML with data). Rendering is\n * throttled — pushes arrive per composited frame, the DOM refreshes at ~8 Hz.\n */\n\n/** Mirror of @ice/r3f `GlFrameStats` (structural — see the header). */\nexport interface GlPanelStats {\n readonly cpuMs: number;\n readonly gpuMs: number;\n readonly fps: number;\n readonly drawCalls: number;\n readonly triangles: number;\n readonly points: number;\n readonly lines: number;\n readonly programs: number;\n readonly geometries: number;\n readonly textures: number;\n readonly renderTargets: number;\n readonly renderMegaPixels: number;\n readonly fboBytes: number;\n readonly fboBudgetBytes: number;\n readonly islands: {\n readonly total: number;\n readonly hot: number;\n readonly warm: number;\n readonly waking: number;\n readonly cold: number;\n readonly dormant: number;\n };\n readonly bandHistogram: Readonly<Record<string, number>>;\n readonly repainted: number;\n readonly pendingPaints: number;\n readonly evicted: number;\n readonly zoom: number;\n readonly band: number;\n readonly effectiveDpr: number;\n readonly visibleWidgets: number;\n readonly culledWidgets: number;\n}\n\nexport type GlPanelCorner = \"tl\" | \"tr\" | \"bl\" | \"br\";\n\nexport interface GlPanelOptions {\n readonly container?: HTMLElement;\n readonly corner?: GlPanelCorner;\n /** Frame budget in ms — the sparkline's reference line (default 16.7). */\n readonly budgetMs?: number;\n /** Start with the detail body open (default false). */\n readonly expanded?: boolean;\n}\n\nexport interface GlPanel {\n push(stats: GlPanelStats): void;\n dispose(): void;\n}\n\nconst STYLE_ID = \"ice-gl-style\";\n\n// strata-prof's palette, verbatim — the panels must read as one toolset.\nconst CSS = `\n.ice-gl { position: fixed; z-index: 99998; background: rgba(13,17,23,.96);\n border: 1px solid #30363d; border-radius: 10px; color: #e6edf3;\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n box-shadow: 0 6px 22px rgba(0,0,0,.45); user-select: none; }\n.ice-gl * { box-sizing: border-box; }\n.ice-gl.tl { top: 10px; left: 10px; } .ice-gl.tr { top: 10px; right: 10px; }\n.ice-gl.bl { bottom: 10px; left: 10px; } .ice-gl.br { bottom: 10px; right: 10px; }\n.ice-gl-head { display: flex; align-items: center; gap: 8px; padding: 6px 9px; cursor: pointer; }\n.ice-gl-name { font-weight: 700; color: #56d4dd; }\n.ice-gl-fps { font-weight: 700; color: #a371f7; min-width: 46px; }\n.ice-gl-gpu { color: #8b949e; min-width: 74px; }\n.ice-gl-caret { color: #8b949e; margin-left: auto; }\n.ice-gl-body { display: none; border-top: 1px solid #21262d; padding: 7px 9px 8px; min-width: 300px; }\n.ice-gl.open .ice-gl-body { display: block; }\n.ice-gl-sect { color: #8b949e; font-weight: 700; padding: 5px 0 2px; }\n.ice-gl-row { display: flex; align-items: center; gap: 7px; padding: 1px 0; white-space: nowrap; }\n.ice-gl-name2 { min-width: 64px; color: #8b949e; }\n.ice-gl-bar { flex: 1; height: 5px; border-radius: 3px; background: #21262d; overflow: hidden; }\n.ice-gl-bar > i { display: block; height: 100%; background: #58a6ff; border-radius: 3px; }\n.ice-gl-stat { color: #e6edf3; margin-left: auto; }\n.ice-gl-dim { color: #8b949e; }\n.ice-gl-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px 10px; padding: 2px 0; }\n.ice-gl-cell b { display: block; color: #e6edf3; font-weight: 700; }\n.ice-gl-cell span { color: #8b949e; }\n.ice-gl-seg { display: flex; height: 7px; border-radius: 3px; overflow: hidden; background: #21262d; margin: 3px 0 2px; }\n.ice-gl-seg > i { display: block; height: 100%; }\n.ice-gl-legend { color: #8b949e; padding-bottom: 2px; }\n`;\n\nconst PHASE_COLORS: Readonly<Record<string, string>> = {\n hot: \"#ff7b72\",\n waking: \"#58a6ff\",\n warm: \"#3fb950\",\n cold: \"#8b949e\",\n dormant: \"#484f58\",\n};\n\nconst SPARK_W = 132;\nconst SPARK_H = 26;\nconst SPARK_SAMPLES = SPARK_W / 2;\n\nfunction injectStyle(doc: Document): void {\n if (doc.getElementById(STYLE_ID) !== null) return;\n const el = doc.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n doc.head.appendChild(el);\n}\n\nconst fmtInt = (n: number): string => n.toLocaleString();\nconst fmtK = (n: number): string => (n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n));\nconst fmtMs = (ms: number): string => `${ms.toFixed(2)}ms`;\nconst fmtMB = (b: number): string => `${(b / 1048576).toFixed(0)}MB`;\n\nexport function createGlPanel(opts: GlPanelOptions = {}): GlPanel {\n const container = opts.container ?? document.body;\n const doc = container.ownerDocument;\n const budgetMs = opts.budgetMs ?? 16.7;\n injectStyle(doc);\n\n const root = doc.createElement(\"div\");\n root.className = `ice-gl ${opts.corner ?? \"bl\"}${opts.expanded === true ? \" open\" : \"\"}`;\n\n const head = doc.createElement(\"div\");\n head.className = \"ice-gl-head\";\n const name = doc.createElement(\"span\");\n name.className = \"ice-gl-name\";\n name.textContent = \"ice gl\";\n const fpsEl = doc.createElement(\"span\");\n fpsEl.className = \"ice-gl-fps\";\n const gpuEl = doc.createElement(\"span\");\n gpuEl.className = \"ice-gl-gpu\";\n const spark = doc.createElement(\"canvas\");\n spark.width = SPARK_W;\n spark.height = SPARK_H;\n spark.style.display = \"block\";\n const caret = doc.createElement(\"span\");\n caret.className = \"ice-gl-caret\";\n caret.textContent = \"▾\";\n head.append(name, fpsEl, gpuEl, spark, caret);\n head.addEventListener(\"click\", () => root.classList.toggle(\"open\"));\n\n const body = doc.createElement(\"div\");\n body.className = \"ice-gl-body\";\n root.append(head, body);\n container.appendChild(root);\n\n // --- static skeleton (rebuilt values only — no per-push DOM churn) --------\n const sect = (label: string): void => {\n const s = doc.createElement(\"div\");\n s.className = \"ice-gl-sect\";\n s.textContent = label;\n body.appendChild(s);\n };\n const bar = (label: string): { fill: HTMLElement; stat: HTMLElement } => {\n const row = doc.createElement(\"div\");\n row.className = \"ice-gl-row\";\n const n = doc.createElement(\"span\");\n n.className = \"ice-gl-name2\";\n n.textContent = label;\n const b = doc.createElement(\"div\");\n b.className = \"ice-gl-bar\";\n const fill = doc.createElement(\"i\");\n b.appendChild(fill);\n const stat = doc.createElement(\"span\");\n stat.className = \"ice-gl-stat\";\n row.append(n, b, stat);\n body.appendChild(row);\n return { fill, stat };\n };\n const textRow = (): HTMLElement => {\n const row = doc.createElement(\"div\");\n row.className = \"ice-gl-row\";\n body.appendChild(row);\n return row;\n };\n\n sect(\"frame\");\n const cpuBar = bar(\"cpu\");\n const gpuBar = bar(\"gpu\");\n\n sect(\"render\");\n const grid = doc.createElement(\"div\");\n grid.className = \"ice-gl-grid\";\n body.appendChild(grid);\n const cell = (label: string): HTMLElement => {\n const c = doc.createElement(\"div\");\n c.className = \"ice-gl-cell\";\n const v = doc.createElement(\"b\");\n const l = doc.createElement(\"span\");\n l.textContent = label;\n c.append(v, l);\n grid.appendChild(c);\n return v;\n };\n const callsEl = cell(\"calls\");\n const trisEl = cell(\"triangles\");\n const ptsEl = cell(\"pts+lines\");\n const progsEl = cell(\"programs\");\n const geosEl = cell(\"geometries\");\n const texsEl = cell(\"textures\");\n\n sect(\"virtual textures\");\n const rtRow = textRow();\n const fboBar = bar(\"fbo mem\");\n const seg = doc.createElement(\"div\");\n seg.className = \"ice-gl-seg\";\n body.appendChild(seg);\n const legend = doc.createElement(\"div\");\n legend.className = \"ice-gl-legend\";\n body.appendChild(legend);\n const paintRow = textRow();\n\n sect(\"lod\");\n const lodRow = textRow();\n const bandsRow = textRow();\n\n sect(\"widgets\");\n const cullBar = bar(\"visible\");\n\n // --- push/render (throttled) ----------------------------------------------\n const samples: number[] = [];\n let latest: GlPanelStats | undefined;\n let maxCpu = 0;\n let maxGpu = 0;\n let evictedTotal = 0;\n let lastRender = 0;\n let disposed = false;\n\n const drawSpark = (): void => {\n const ctx = spark.getContext(\"2d\");\n if (ctx === null) return;\n ctx.clearRect(0, 0, SPARK_W, SPARK_H);\n const top = budgetMs * 2; // budget sits mid-height\n for (let i = 0; i < samples.length; i++) {\n const v = samples[i] ?? 0;\n const h = Math.min(SPARK_H, (v / top) * SPARK_H);\n ctx.fillStyle = v > budgetMs ? \"#ff7b72\" : \"#3fb950\";\n ctx.fillRect(i * 2, SPARK_H - h, 1.6, h);\n }\n ctx.fillStyle = \"rgba(139,148,158,.55)\";\n ctx.fillRect(0, SPARK_H / 2, SPARK_W, 1);\n };\n\n const render = (): void => {\n const s = latest;\n if (s === undefined) return;\n fpsEl.textContent = s.fps > 0 ? `${Math.round(s.fps)} fps` : \"– fps\";\n gpuEl.textContent = s.gpuMs > 0 ? `gpu ${fmtMs(s.gpuMs)}` : \"gpu –\";\n drawSpark();\n if (!root.classList.contains(\"open\")) return;\n\n cpuBar.fill.style.width = `${Math.min(100, (s.cpuMs / budgetMs) * 100)}%`;\n cpuBar.stat.textContent = `${fmtMs(s.cpuMs)} · max ${fmtMs(maxCpu)}`;\n gpuBar.fill.style.width = `${Math.min(100, (s.gpuMs / budgetMs) * 100)}%`;\n gpuBar.fill.style.background = s.gpuMs > budgetMs ? \"#ff7b72\" : \"#58a6ff\";\n gpuBar.stat.textContent = `${fmtMs(s.gpuMs)} · max ${fmtMs(maxGpu)}`;\n\n callsEl.textContent = fmtInt(s.drawCalls);\n trisEl.textContent = fmtK(s.triangles);\n ptsEl.textContent = fmtK(s.points + s.lines);\n progsEl.textContent = fmtInt(s.programs);\n geosEl.textContent = fmtInt(s.geometries);\n texsEl.textContent = fmtInt(s.textures);\n\n rtRow.textContent = `${s.renderTargets} targets · ${s.renderMegaPixels.toFixed(1)} MP total`;\n fboBar.fill.style.width = `${Math.min(100, (s.fboBytes / s.fboBudgetBytes) * 100)}%`;\n fboBar.fill.style.background = s.fboBytes > s.fboBudgetBytes * 0.85 ? \"#ffa657\" : \"#58a6ff\";\n fboBar.stat.textContent = `${fmtMB(s.fboBytes)} / ${fmtMB(s.fboBudgetBytes)}`;\n\n seg.textContent = \"\";\n const phases: Array<[string, number]> = [\n [\"hot\", s.islands.hot],\n [\"waking\", s.islands.waking],\n [\"warm\", s.islands.warm],\n [\"cold\", s.islands.cold],\n [\"dormant\", s.islands.dormant],\n ];\n const denom = Math.max(1, s.islands.total);\n const legendParts: string[] = [];\n for (const [phase, count] of phases) {\n if (count === 0) continue;\n const i = doc.createElement(\"i\");\n i.style.width = `${(count / denom) * 100}%`;\n i.style.background = PHASE_COLORS[phase] ?? \"#8b949e\";\n seg.appendChild(i);\n legendParts.push(`${count} ${phase}`);\n }\n legend.textContent = legendParts.length > 0 ? legendParts.join(\" · \") : \"no islands\";\n paintRow.textContent = `repainted ${s.repainted}/frame · pending ${s.pendingPaints} · evicted Σ${evictedTotal}`;\n\n lodRow.textContent = `zoom ${(s.zoom * 100).toFixed(0)}% → band ×${s.band} → paint dpr ${s.effectiveDpr.toFixed(2)}`;\n const bands = Object.entries(s.bandHistogram).sort((a, b) => a[0].localeCompare(b[0]));\n bandsRow.textContent =\n bands.length > 0 ? `targets by band: ${bands.map(([k, v]) => `${k}: ${v}`).join(\" · \")}` : \"no live targets\";\n\n const pop = Math.max(1, s.visibleWidgets + s.culledWidgets);\n cullBar.fill.style.width = `${(s.visibleWidgets / pop) * 100}%`;\n cullBar.fill.style.background = \"#3fb950\";\n cullBar.stat.textContent = `${s.visibleWidgets} visible · ${s.culledWidgets} culled`;\n };\n\n return {\n push(s) {\n if (disposed) return;\n latest = s;\n maxCpu = Math.max(maxCpu, s.cpuMs);\n maxGpu = Math.max(maxGpu, s.gpuMs);\n evictedTotal += s.evicted;\n samples.push(s.gpuMs > 0 ? s.gpuMs : s.cpuMs);\n if (samples.length > SPARK_SAMPLES) samples.splice(0, samples.length - SPARK_SAMPLES);\n const now = performance.now();\n if (now - lastRender >= 120) {\n lastRender = now;\n render();\n }\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n root.remove();\n },\n };\n}\n","/**\n * @ice/devtools — strata's observer panel + FPS profiler, engine-flavored\n * (describe/durable/ephemeral/reflect-lane glue). Reads the world outside the\n * tick; never a reflector, never writes ECS. Nobody imports us\n * (depcruise-enforced leaf).\n */\nexport const DEVTOOLS_VERSION = \"0.1.0\";\n\nexport {\n attachDevtools,\n engineDescribe,\n type DevtoolsEngine,\n type DevtoolsHandle,\n type DevtoolsOpts,\n} from \"./attach\";\nexport { createDock, type Dock, type DockCorner, type DockOptions, type DockSlotId } from \"./dock\";\nexport { createGlPanel, type GlPanel, type GlPanelCorner, type GlPanelOptions, type GlPanelStats } from \"./gl-panel\";\n"],"mappings":"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","names":["w","STYLE_ID","CSS","injectStyle"]}
1
+ {"version":3,"sources":["../../devtools/src/attach.ts","../../devtools/src/dock.ts","../../devtools/src/gl-panel.ts","../../devtools/src/index.ts"],"sourcesContent":["/**\n * @ice/devtools — strata's first-party tools, engine-flavored (rebuilt\n * 2026-07-13, superseding the M10 hand-rolled 4-tab panel: the standing rule\n * is observers/inspectors WRAP `@vibecook/strata-ecs/tools`, never\n * re-implement them).\n *\n * `attachDevtools(engine)` = strata's observer panel + FPS profiler, plus the\n * engine glue strata cannot know:\n *\n * DESCRIBE PrefabId → widget type; recognizers → kind + live phase;\n * pointers/ports/wires/peers/chrome labeled as such.\n * DURABLE TAB fed from the facade's CURRENT DocSession (docId, converged\n * snapshot, attachment baseline) — the getter re-reads per\n * poll, so it tracks docs.create/open/join/close live.\n * EPHEMERAL TAB fed from an app-provided PresenceSession getter (the\n * facade keeps presence private; collab demos pass theirs).\n * \"reflect\" LANE the engine's post-notify reflector flush cost — the one\n * frame slice strata's world hooks cannot see — reported\n * into the profiler via an onPublish hook reading\n * `lastFrame()` telemetry (one frame late by construction:\n * publish runs before this frame's reflect).\n *\n * By default all three tools mount into ONE draggable dock (dock.ts —\n * 2026-07-13, James: \"make the 3 tools into one draggable panel\"); pass\n * `dock: false` for the classic scattered corners.\n *\n * Reads only, outside the tick (verified-unrestricted); never a reflector,\n * never writes ECS. Nobody imports this package (depcruise-enforced leaf).\n */\nimport type { Entity, World } from \"@vibecook/strata-ecs\";\nimport {\n attachObserver,\n attachProfiler,\n defaultDescribe,\n type DescribeFn,\n type EntityDescription,\n type ObserverDurableSource,\n type ObserverEphemeralSource,\n type ObserverHandle,\n type ObserverOptions,\n type ProfilerHandle,\n type ProfilerOptions,\n} from \"@vibecook/strata-ecs/tools\";\nimport { createDock, type Dock, type DockOptions, type DockSlotId } from \"./dock\";\nimport { createGlPanel, type GlPanel, type GlPanelOptions, type GlPanelStats } from \"./gl-panel\";\nimport {\n CursorVisual,\n Drag,\n GesturePhases,\n GestureSuspended,\n HandleSpec,\n LongPress,\n Pinch,\n Pointer,\n Port,\n PrefabId,\n PresencePeer,\n Tap,\n WheelPan,\n WheelZoom,\n Wire,\n type CanvasEngine,\n type Engine,\n type PresenceSession,\n} from \"@ice/core\";\n\nexport interface DevtoolsOpts {\n /** Where the dock (or, with `dock: false`, each panel) mounts (default: document.body). */\n readonly container?: HTMLElement;\n /**\n * ONE draggable dock hosting all three tools (the default). `false` scatters\n * them to their classic fixed corners; an object sets initial placement.\n * The dock mounts lazily with the first tool that needs it.\n */\n readonly dock?: boolean | Pick<DockOptions, \"corner\" | \"title\" | \"width\">;\n /** Observer panel: `false` to omit, or strata passthrough options. */\n readonly observer?: boolean | Pick<ObserverOptions, \"defaultTab\" | \"tab\" | \"recorderCap\">;\n /** Profiler HUD: `false` to omit, or strata passthrough options. */\n readonly profiler?: boolean | Pick<ProfilerOptions, \"budgetMs\" | \"windowSize\" | \"corner\" | \"expanded\" | \"lanes\">;\n /** GL metrics panel: `false` to omit, or placement/budget options. Mounts lazily on the first `glStats` push. */\n readonly glPanel?: boolean | Pick<GlPanelOptions, \"corner\" | \"budgetMs\" | \"expanded\">;\n /** Collab demos: the presence session whose store feeds the ephemeral tab. */\n readonly presence?: () => PresenceSession | null | undefined;\n /** Override entity labeling (default: the engine describe below). */\n readonly describe?: DescribeFn;\n}\n\nexport interface DevtoolsHandle {\n readonly observer: ObserverHandle | null;\n readonly profiler: ProfilerHandle | null;\n /** Host-cost lane passthrough (paint, decode…); no-op when the profiler is off. */\n lane(name: string, ms: number): void;\n /**\n * Feed one GL frame's metrics (wire `<GLViews onFrameStats>` here). The GL\n * panel mounts on the first push — renderer counts, virtual-texture census,\n * LOD banding, cull population. No-op when opts.glPanel === false.\n */\n glStats(stats: GlPanelStats): void;\n /** Tear all panels + the telemetry hook down. Idempotent. */\n detach(): void;\n}\n\nconst COLOR = {\n widget: \"#3fb950\",\n gesture: \"#a371f7\",\n pointer: \"#58a6ff\",\n graph: \"#ffa657\",\n presence: \"#f778ba\",\n chrome: \"#8b949e\",\n} as const;\n\nconst PHASE_ORDER = [\"Active\", \"Possible\", \"Recognized\", \"Pending\", \"Ended\", \"Cancelled\", \"Failed\"] as const;\n\nfunction phaseOf(world: World, e: Entity): string | null {\n // Suspended is a rider OUTSIDE the phase set (a pinch freezes single-pointer\n // recognizers without ending them) — surface it first, it explains \"stuck\".\n if (world.hasTag(e, GestureSuspended)) return \"suspended\";\n for (const name of PHASE_ORDER) {\n if (world.hasTag(e, GesturePhases.tags[name])) return name.toLowerCase();\n }\n return null;\n}\n\n/**\n * Engine-aware entity labeling for the observer's lists + timeline: identity\n * off ROLE (widget type, recognizer kind + phase, pointer device), not off\n * raw component-name composition. Falls back to strata's defaultDescribe.\n */\nexport function engineDescribe(world: World, e: Entity): EntityDescription {\n const prefab = world.get(e, PrefabId)?.id;\n if (typeof prefab === \"string\") return { label: prefab, color: COLOR.widget };\n\n const kind = world.has(e, Tap)\n ? \"tap\"\n : world.has(e, LongPress)\n ? \"longpress\"\n : world.has(e, Pinch)\n ? \"pinch\"\n : world.has(e, WheelZoom)\n ? \"wheelzoom\"\n : world.has(e, WheelPan)\n ? \"wheelpan\"\n : world.has(e, Drag)\n ? \"drag\"\n : undefined;\n if (kind !== undefined) return { label: kind, color: COLOR.gesture, phase: phaseOf(world, e) };\n\n if (world.has(e, Pointer)) return { label: `pointer:${world.read(e, Pointer).device}`, color: COLOR.pointer };\n if (world.has(e, Port)) return { label: \"port\", color: COLOR.graph };\n if (world.hasTag(e, Wire)) return { label: \"wire\", color: COLOR.graph };\n if (world.hasTag(e, PresencePeer)) return { label: \"peer\", color: COLOR.presence };\n if (world.has(e, CursorVisual)) return { label: \"cursor\", color: COLOR.presence };\n if (world.has(e, HandleSpec)) return { label: \"handle\", color: COLOR.chrome };\n return defaultDescribe(world, e);\n}\n\n/** Accepts the facade (durable tab included) or a bare core engine. */\nexport type DevtoolsEngine = CanvasEngine | Engine;\n\nexport function attachDevtools(engine: DevtoolsEngine, opts: DevtoolsOpts = {}): DevtoolsHandle {\n const isFacade = \"docs\" in engine;\n const core: Engine = isFacade ? engine.engine : engine;\n const world = core.world;\n\n // Durable tab: only the facade knows the current session; the getter is\n // re-read each poll so it swaps live across doc open/close/join. The store\n // and attachment are the STRUCTURAL mirrors the observer contract documents\n // (docId + snapshot / baseline) — never a sync path.\n const docs = isFacade ? engine.docs : undefined;\n const durable =\n docs === undefined\n ? undefined\n : (): ObserverDurableSource | null => {\n const s = docs.current();\n if (s === undefined) return null;\n return {\n store: s.store as unknown as ObserverDurableSource[\"store\"],\n attachment: s.attachment as unknown as ObserverDurableSource[\"attachment\"],\n };\n };\n\n const presence = opts.presence;\n const ephemeral =\n presence === undefined\n ? undefined\n : (): ObserverEphemeralSource | null => presence()?.eph ?? null;\n\n // The dock is created with the first tool that mounts (so all-flags-off\n // attaches — and glPanel-only attaches before the first push — stay DOM-free).\n let dock: Dock | null = null;\n const mountIn = (id: DockSlotId): { container?: HTMLElement } => {\n if (opts.dock === false) return opts.container !== undefined ? { container: opts.container } : {};\n if (dock === null) {\n dock = createDock({\n ...(typeof opts.dock === \"object\" ? opts.dock : {}),\n ...(opts.container !== undefined ? { container: opts.container } : {}),\n });\n }\n return { container: dock.slot(id) };\n };\n\n const observer =\n opts.observer === false\n ? null\n : attachObserver(world, {\n ...(typeof opts.observer === \"object\" ? opts.observer : {}),\n ...mountIn(\"observer\"),\n describe: opts.describe ?? engineDescribe,\n ...(durable !== undefined ? { durable } : {}),\n ...(ephemeral !== undefined ? { ephemeral } : {}),\n });\n\n const profiler =\n opts.profiler === false\n ? null\n : attachProfiler(world, {\n ...(typeof opts.profiler === \"object\" ? opts.profiler : {}),\n ...mountIn(\"profiler\"),\n });\n\n // The \"reflect\" lane: reflector flushes run POST-notify, invisible to\n // strata's in-world hooks. Publish runs before this frame's reflect, so the\n // hook reports the PREVIOUS frame's cost — a one-frame lag a HUD can carry.\n //\n // FIXED 2026-08-15 (petition I14): this read `phaseFlushMicros.get(\"reflect\")`,\n // a map keyed by strata PHASE names — and reflect is not a phase, so the\n // lookup was always undefined and the lane NEVER reported since it shipped.\n // `FrameTelemetry.reflectMicros` is the real source (engine.ts arms the\n // reflector registry's own timing from `enableTelemetry`).\n //\n // The GUEST lanes (petition I14, 2026-08-15): guest work runs between the\n // tick and the publish hooks, so — like reflect — no strata in-world hook can\n // see it, and it was previously invisible to the profiler entirely. One lane\n // per guest, reporting its own last invocation; suspended guests stop\n // reporting rather than flatlining at zero, so a silent lane means \"gone\",\n // which is exactly what the doctor's row says too.\n let removePublish: (() => void) | undefined;\n if (profiler !== null) {\n core.enableTelemetry();\n removePublish = core.onPublish(() => {\n const us = core.lastFrame()?.reflectMicros;\n if (us !== undefined && us > 0) profiler.lane(\"reflect\", us / 1000);\n for (const g of core.guests.list()) {\n if (g.status === \"running\" && g.lastMs > 0) profiler.lane(`guest:${g.id}`, g.lastMs);\n }\n });\n }\n\n let glPanel: GlPanel | null = null;\n let detached = false;\n return {\n observer,\n profiler,\n lane(name, ms) {\n profiler?.lane(name, ms);\n },\n glStats(stats) {\n if (detached || opts.glPanel === false) return;\n if (glPanel === null) {\n glPanel = createGlPanel({\n ...(typeof opts.glPanel === \"object\" ? opts.glPanel : {}),\n ...mountIn(\"gl\"),\n });\n }\n glPanel.push(stats);\n },\n detach() {\n if (detached) return;\n detached = true;\n removePublish?.();\n observer?.dispose();\n profiler?.dispose();\n glPanel?.dispose();\n glPanel = null;\n dock?.dispose();\n dock = null;\n },\n };\n}\n","/**\n * The devtools dock — ONE draggable window hosting all three tools\n * (2026-07-13, James: \"make the 3 tools into one draggable panel\").\n *\n * strata's observer + profiler and our GL panel each mount `position: fixed`\n * into their own corner. The dock does NOT re-implement any of them (standing\n * rule: wrap first-party tools): it is a fixed, draggable shell with three\n * stacked slots; each tool mounts into its slot via its own `container`\n * option, and dock-scoped CSS de-windows them (`position: static`, full\n * width, no border/shadow) so they read as sections of one panel. Their own\n * behavior is untouched — the observer keeps its tabs/collapse/vertical\n * resize (its header drag goes inert: left/top are ignored at static), the\n * profiler + GL strips keep their click-to-expand.\n *\n * Slot order is fixed by the dock, not by mount order: profiler and GL are\n * one-line summary strips, the observer is the deep-dive — strips on top.\n * Layout (x/y/width/open) persists to localStorage, same as the observer:\n * schema edits full-reload the page by design; the dock must stay put.\n */\n\nexport type DockCorner = \"tl\" | \"tr\" | \"bl\" | \"br\";\n\nexport type DockSlotId = \"profiler\" | \"gl\" | \"observer\";\n\nexport interface DockOptions {\n /** Where the dock mounts (default: document.body). */\n readonly container?: HTMLElement;\n /** Initial corner when nothing is persisted yet (default \"tr\"). */\n readonly corner?: DockCorner;\n /** Header title (default \"ice devtools\"). */\n readonly title?: string;\n /** Initial width in px when nothing is persisted yet (default 420). */\n readonly width?: number;\n}\n\nexport interface Dock {\n readonly el: HTMLElement;\n /** The mount element for one tool. Empty slots are display:none via CSS. */\n slot(id: DockSlotId): HTMLElement;\n dispose(): void;\n}\n\nconst LS_KEY = \"ice-dock:layout\";\nconst STYLE_ID = \"ice-dock-style\";\nconst MARGIN = 8;\nconst KEEP = 90; // px that must stay on screen so the header is always grabbable\nconst SLOT_ORDER: readonly DockSlotId[] = [\"profiler\", \"gl\", \"observer\"];\n\ninterface Layout {\n x?: number;\n y?: number;\n w?: number;\n open?: boolean;\n}\n\nfunction loadLayout(): Layout {\n try {\n return JSON.parse(localStorage.getItem(LS_KEY) ?? \"{}\") as Layout;\n } catch {\n return {};\n }\n}\n\nfunction saveLayout(patch: Layout): void {\n try {\n localStorage.setItem(LS_KEY, JSON.stringify({ ...loadLayout(), ...patch }));\n } catch {\n /* private mode / quota — non-fatal for a dev tool */\n }\n}\n\n// strata-obs's window chrome, verbatim, plus the de-windowing overrides for\n// the hosted tools. `!important` beats both the tools' own stylesheets and\n// the observer's inline width restored from ITS localStorage layout.\nconst CSS = `\n.ice-dock { position: fixed; z-index: 99999; width: 420px; min-width: 340px; max-width: 92vw;\n background: rgba(13,17,23,.96); border: 1px solid #30363d; border-radius: 10px;\n color: #e6edf3; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;\n box-shadow: 0 8px 28px rgba(0,0,0,.45); overflow: hidden; resize: horizontal; }\n.ice-dock * { box-sizing: border-box; }\n.ice-dock.collapsed { resize: none; }\n.ice-dock.dragging { opacity: .92; cursor: grabbing; }\n.ice-dock-head { display: flex; align-items: center; gap: 7px; padding: 7px 10px; cursor: grab;\n background: rgba(22,27,34,.96); user-select: none; }\n.ice-dock-grip { color: #484f58; }\n.ice-dock-name { font-weight: 700; color: #56d4dd; }\n.ice-dock-btn { background: none; border: none; color: #8b949e; cursor: pointer; font: inherit;\n padding: 2px 6px; margin-left: auto; }\n.ice-dock-btn:hover { color: #e6edf3; }\n.ice-dock-body { max-height: calc(100vh - 96px); overflow-y: auto; }\n.ice-dock.collapsed .ice-dock-body { display: none; }\n.ice-dock-slot { border-top: 1px solid #30363d; }\n.ice-dock-slot:empty { display: none; }\n\n/* De-window the hosted tools — the dock is the window now. */\n.ice-dock .strata-obs, .ice-dock .strata-prof, .ice-dock .ice-gl {\n position: static !important; width: auto !important; min-width: 0 !important;\n border: none !important; border-radius: 0 !important; box-shadow: none !important; }\n.ice-dock .strata-obs { resize: vertical !important; }\n.ice-dock .strata-obs-head { cursor: default; }\n.ice-dock .strata-prof-body, .ice-dock .ice-gl-body { min-width: 0; }\n`;\n\nfunction injectStyle(doc: Document): void {\n if (doc.getElementById(STYLE_ID) !== null) return;\n const el = doc.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n doc.head.appendChild(el);\n}\n\nexport function createDock(opts: DockOptions = {}): Dock {\n const container = opts.container ?? document.body;\n const doc = container.ownerDocument;\n const win = doc.defaultView ?? window;\n injectStyle(doc);\n\n const clampPos = (x: number, y: number, w: number): { x: number; y: number } => {\n const maxX = win.innerWidth - KEEP;\n const minX = Math.min(MARGIN, MARGIN - (w - KEEP));\n const maxY = win.innerHeight - 34;\n return { x: Math.min(Math.max(x, minX), maxX), y: Math.min(Math.max(y, MARGIN), maxY) };\n };\n\n const root = doc.createElement(\"div\");\n root.className = \"ice-dock\";\n\n const head = doc.createElement(\"div\");\n head.className = \"ice-dock-head\";\n const grip = doc.createElement(\"span\");\n grip.className = \"ice-dock-grip\";\n grip.setAttribute(\"aria-hidden\", \"true\");\n grip.textContent = \"⠿\";\n const name = doc.createElement(\"span\");\n name.className = \"ice-dock-name\";\n name.textContent = opts.title ?? \"ice devtools\";\n const collapse = doc.createElement(\"button\");\n collapse.type = \"button\";\n collapse.className = \"ice-dock-btn\";\n collapse.title = \"Collapse\";\n head.append(grip, name, collapse);\n\n const body = doc.createElement(\"div\");\n body.className = \"ice-dock-body\";\n const slots = new Map<DockSlotId, HTMLElement>();\n for (const id of SLOT_ORDER) {\n const s = doc.createElement(\"div\");\n s.className = \"ice-dock-slot\";\n s.dataset.slot = id;\n body.appendChild(s);\n slots.set(id, s);\n }\n\n root.append(head, body);\n container.appendChild(root);\n\n // restore layout: width, then position (clamped), then open state\n const l = loadLayout();\n const w = typeof l.w === \"number\" ? l.w : (opts.width ?? 420);\n root.style.width = `${w}px`;\n const corner = opts.corner ?? \"tr\";\n const fx = corner === \"tl\" || corner === \"bl\" ? 16 : win.innerWidth - w - 16;\n const fy = corner === \"tl\" || corner === \"tr\" ? 16 : win.innerHeight - 220;\n const p = clampPos(l.x ?? fx, l.y ?? fy, w);\n root.style.left = `${p.x}px`;\n root.style.top = `${p.y}px`;\n let open = l.open ?? true;\n const applyOpen = (): void => {\n root.classList.toggle(\"collapsed\", !open);\n collapse.textContent = open ? \"▾\" : \"▸\";\n };\n applyOpen();\n collapse.addEventListener(\"click\", () => {\n open = !open;\n saveLayout({ open });\n applyOpen();\n });\n\n let drag: { id: number; sx: number; sy: number; ox: number; oy: number } | null = null;\n head.addEventListener(\"pointerdown\", (e) => {\n if ((e.target as HTMLElement).closest(\"button\") !== null) return; // let buttons be buttons\n const r = root.getBoundingClientRect();\n drag = { id: e.pointerId, sx: e.clientX, sy: e.clientY, ox: r.left, oy: r.top };\n if (typeof head.setPointerCapture === \"function\") {\n try {\n head.setPointerCapture(e.pointerId);\n } catch {\n /* synthetic events (tests) have no active pointer */\n }\n }\n root.classList.add(\"dragging\");\n e.preventDefault();\n });\n head.addEventListener(\"pointermove\", (e) => {\n if (drag?.id !== e.pointerId) return;\n const q = clampPos(drag.ox + (e.clientX - drag.sx), drag.oy + (e.clientY - drag.sy), root.offsetWidth);\n root.style.left = `${q.x}px`;\n root.style.top = `${q.y}px`;\n });\n const endDrag = (e: PointerEvent): void => {\n if (drag?.id !== e.pointerId) return;\n drag = null;\n root.classList.remove(\"dragging\");\n const r = root.getBoundingClientRect();\n saveLayout({ x: r.left, y: r.top });\n };\n head.addEventListener(\"pointerup\", endDrag);\n head.addEventListener(\"pointercancel\", endDrag);\n\n const onWinResize = (): void => {\n const r = root.getBoundingClientRect();\n const q = clampPos(r.left, r.top, root.offsetWidth);\n root.style.left = `${q.x}px`;\n root.style.top = `${q.y}px`;\n };\n win.addEventListener(\"resize\", onWinResize);\n const ro = new ResizeObserver(() => {\n saveLayout({ w: root.offsetWidth });\n });\n ro.observe(root);\n\n let disposed = false;\n return {\n el: root,\n slot(id) {\n // SLOT_ORDER covers the full DockSlotId union — the map is total.\n return slots.get(id) as HTMLElement;\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n win.removeEventListener(\"resize\", onWinResize);\n ro.disconnect();\n root.remove();\n },\n };\n}\n","/**\n * The GL metrics panel — the engine-specific renderer readout strata has no\n * counterpart for (2026-07-13, James: \"comprehensive r3f profiler ... and our\n * engine specific optimization metrics ... with nice visuals\").\n *\n * Pure DOM, strata's visual language (same palette/typography as the observer\n * + profiler HUD so the three read as one toolset). Fed by pushes of\n * `GlPanelStats` — the STRUCTURAL MIRROR of @ice/r3f's `GlFrameStats`\n * (devtools cannot import r3f under the walls; keep the shapes aligned):\n *\n * head fps · gpu ms + a gpu sparkline against the frame budget\n * frame cpu / gpu bars vs budget, with session maxima\n * render draw calls · triangles · points/lines · programs · geoms · textures\n * virtual live render targets + total resolution, FBO bytes vs budget,\n * textures the island phase census as a segmented bar, repaint/evict counters\n * lod zoom → hysteresis band → effective DPR, live targets per band\n * widgets Visible vs Culled population\n *\n * All values land via `textContent` (never innerHTML with data). Rendering is\n * throttled — pushes arrive per composited frame, the DOM refreshes at ~8 Hz.\n */\n\n/** Mirror of @ice/r3f `GlFrameStats` (structural — see the header). */\nexport interface GlPanelStats {\n readonly cpuMs: number;\n readonly gpuMs: number;\n readonly fps: number;\n readonly drawCalls: number;\n readonly triangles: number;\n readonly points: number;\n readonly lines: number;\n readonly programs: number;\n readonly geometries: number;\n readonly textures: number;\n readonly renderTargets: number;\n readonly renderMegaPixels: number;\n readonly fboBytes: number;\n readonly fboBudgetBytes: number;\n readonly islands: {\n readonly total: number;\n readonly hot: number;\n readonly warm: number;\n readonly waking: number;\n readonly cold: number;\n readonly dormant: number;\n };\n readonly bandHistogram: Readonly<Record<string, number>>;\n readonly repainted: number;\n readonly pendingPaints: number;\n readonly evicted: number;\n readonly zoom: number;\n readonly band: number;\n readonly effectiveDpr: number;\n readonly visibleWidgets: number;\n readonly culledWidgets: number;\n}\n\nexport type GlPanelCorner = \"tl\" | \"tr\" | \"bl\" | \"br\";\n\nexport interface GlPanelOptions {\n readonly container?: HTMLElement;\n readonly corner?: GlPanelCorner;\n /** Frame budget in ms — the sparkline's reference line (default 16.7). */\n readonly budgetMs?: number;\n /** Start with the detail body open (default false). */\n readonly expanded?: boolean;\n}\n\nexport interface GlPanel {\n push(stats: GlPanelStats): void;\n dispose(): void;\n}\n\nconst STYLE_ID = \"ice-gl-style\";\n\n// strata-prof's palette, verbatim — the panels must read as one toolset.\nconst CSS = `\n.ice-gl { position: fixed; z-index: 99998; background: rgba(13,17,23,.96);\n border: 1px solid #30363d; border-radius: 10px; color: #e6edf3;\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n box-shadow: 0 6px 22px rgba(0,0,0,.45); user-select: none; }\n.ice-gl * { box-sizing: border-box; }\n.ice-gl.tl { top: 10px; left: 10px; } .ice-gl.tr { top: 10px; right: 10px; }\n.ice-gl.bl { bottom: 10px; left: 10px; } .ice-gl.br { bottom: 10px; right: 10px; }\n.ice-gl-head { display: flex; align-items: center; gap: 8px; padding: 6px 9px; cursor: pointer; }\n.ice-gl-name { font-weight: 700; color: #56d4dd; }\n.ice-gl-fps { font-weight: 700; color: #a371f7; min-width: 46px; }\n.ice-gl-gpu { color: #8b949e; min-width: 74px; }\n.ice-gl-caret { color: #8b949e; margin-left: auto; }\n.ice-gl-body { display: none; border-top: 1px solid #21262d; padding: 7px 9px 8px; min-width: 300px; }\n.ice-gl.open .ice-gl-body { display: block; }\n.ice-gl-sect { color: #8b949e; font-weight: 700; padding: 5px 0 2px; }\n.ice-gl-row { display: flex; align-items: center; gap: 7px; padding: 1px 0; white-space: nowrap; }\n.ice-gl-name2 { min-width: 64px; color: #8b949e; }\n.ice-gl-bar { flex: 1; height: 5px; border-radius: 3px; background: #21262d; overflow: hidden; }\n.ice-gl-bar > i { display: block; height: 100%; background: #58a6ff; border-radius: 3px; }\n.ice-gl-stat { color: #e6edf3; margin-left: auto; }\n.ice-gl-dim { color: #8b949e; }\n.ice-gl-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px 10px; padding: 2px 0; }\n.ice-gl-cell b { display: block; color: #e6edf3; font-weight: 700; }\n.ice-gl-cell span { color: #8b949e; }\n.ice-gl-seg { display: flex; height: 7px; border-radius: 3px; overflow: hidden; background: #21262d; margin: 3px 0 2px; }\n.ice-gl-seg > i { display: block; height: 100%; }\n.ice-gl-legend { color: #8b949e; padding-bottom: 2px; }\n`;\n\nconst PHASE_COLORS: Readonly<Record<string, string>> = {\n hot: \"#ff7b72\",\n waking: \"#58a6ff\",\n warm: \"#3fb950\",\n cold: \"#8b949e\",\n dormant: \"#484f58\",\n};\n\nconst SPARK_W = 132;\nconst SPARK_H = 26;\nconst SPARK_SAMPLES = SPARK_W / 2;\n\nfunction injectStyle(doc: Document): void {\n if (doc.getElementById(STYLE_ID) !== null) return;\n const el = doc.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n doc.head.appendChild(el);\n}\n\nconst fmtInt = (n: number): string => n.toLocaleString();\nconst fmtK = (n: number): string => (n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n));\nconst fmtMs = (ms: number): string => `${ms.toFixed(2)}ms`;\nconst fmtMB = (b: number): string => `${(b / 1048576).toFixed(0)}MB`;\n\nexport function createGlPanel(opts: GlPanelOptions = {}): GlPanel {\n const container = opts.container ?? document.body;\n const doc = container.ownerDocument;\n const budgetMs = opts.budgetMs ?? 16.7;\n injectStyle(doc);\n\n const root = doc.createElement(\"div\");\n root.className = `ice-gl ${opts.corner ?? \"bl\"}${opts.expanded === true ? \" open\" : \"\"}`;\n\n const head = doc.createElement(\"div\");\n head.className = \"ice-gl-head\";\n const name = doc.createElement(\"span\");\n name.className = \"ice-gl-name\";\n name.textContent = \"ice gl\";\n const fpsEl = doc.createElement(\"span\");\n fpsEl.className = \"ice-gl-fps\";\n const gpuEl = doc.createElement(\"span\");\n gpuEl.className = \"ice-gl-gpu\";\n const spark = doc.createElement(\"canvas\");\n spark.width = SPARK_W;\n spark.height = SPARK_H;\n spark.style.display = \"block\";\n const caret = doc.createElement(\"span\");\n caret.className = \"ice-gl-caret\";\n caret.textContent = \"▾\";\n head.append(name, fpsEl, gpuEl, spark, caret);\n head.addEventListener(\"click\", () => root.classList.toggle(\"open\"));\n\n const body = doc.createElement(\"div\");\n body.className = \"ice-gl-body\";\n root.append(head, body);\n container.appendChild(root);\n\n // --- static skeleton (rebuilt values only — no per-push DOM churn) --------\n const sect = (label: string): void => {\n const s = doc.createElement(\"div\");\n s.className = \"ice-gl-sect\";\n s.textContent = label;\n body.appendChild(s);\n };\n const bar = (label: string): { fill: HTMLElement; stat: HTMLElement } => {\n const row = doc.createElement(\"div\");\n row.className = \"ice-gl-row\";\n const n = doc.createElement(\"span\");\n n.className = \"ice-gl-name2\";\n n.textContent = label;\n const b = doc.createElement(\"div\");\n b.className = \"ice-gl-bar\";\n const fill = doc.createElement(\"i\");\n b.appendChild(fill);\n const stat = doc.createElement(\"span\");\n stat.className = \"ice-gl-stat\";\n row.append(n, b, stat);\n body.appendChild(row);\n return { fill, stat };\n };\n const textRow = (): HTMLElement => {\n const row = doc.createElement(\"div\");\n row.className = \"ice-gl-row\";\n body.appendChild(row);\n return row;\n };\n\n sect(\"frame\");\n const cpuBar = bar(\"cpu\");\n const gpuBar = bar(\"gpu\");\n\n sect(\"render\");\n const grid = doc.createElement(\"div\");\n grid.className = \"ice-gl-grid\";\n body.appendChild(grid);\n const cell = (label: string): HTMLElement => {\n const c = doc.createElement(\"div\");\n c.className = \"ice-gl-cell\";\n const v = doc.createElement(\"b\");\n const l = doc.createElement(\"span\");\n l.textContent = label;\n c.append(v, l);\n grid.appendChild(c);\n return v;\n };\n const callsEl = cell(\"calls\");\n const trisEl = cell(\"triangles\");\n const ptsEl = cell(\"pts+lines\");\n const progsEl = cell(\"programs\");\n const geosEl = cell(\"geometries\");\n const texsEl = cell(\"textures\");\n\n sect(\"virtual textures\");\n const rtRow = textRow();\n const fboBar = bar(\"fbo mem\");\n const seg = doc.createElement(\"div\");\n seg.className = \"ice-gl-seg\";\n body.appendChild(seg);\n const legend = doc.createElement(\"div\");\n legend.className = \"ice-gl-legend\";\n body.appendChild(legend);\n const paintRow = textRow();\n\n sect(\"lod\");\n const lodRow = textRow();\n const bandsRow = textRow();\n\n sect(\"widgets\");\n const cullBar = bar(\"visible\");\n\n // --- push/render (throttled) ----------------------------------------------\n const samples: number[] = [];\n let latest: GlPanelStats | undefined;\n let maxCpu = 0;\n let maxGpu = 0;\n let evictedTotal = 0;\n let lastRender = 0;\n let disposed = false;\n\n const drawSpark = (): void => {\n const ctx = spark.getContext(\"2d\");\n if (ctx === null) return;\n ctx.clearRect(0, 0, SPARK_W, SPARK_H);\n const top = budgetMs * 2; // budget sits mid-height\n for (let i = 0; i < samples.length; i++) {\n const v = samples[i] ?? 0;\n const h = Math.min(SPARK_H, (v / top) * SPARK_H);\n ctx.fillStyle = v > budgetMs ? \"#ff7b72\" : \"#3fb950\";\n ctx.fillRect(i * 2, SPARK_H - h, 1.6, h);\n }\n ctx.fillStyle = \"rgba(139,148,158,.55)\";\n ctx.fillRect(0, SPARK_H / 2, SPARK_W, 1);\n };\n\n const render = (): void => {\n const s = latest;\n if (s === undefined) return;\n fpsEl.textContent = s.fps > 0 ? `${Math.round(s.fps)} fps` : \"– fps\";\n gpuEl.textContent = s.gpuMs > 0 ? `gpu ${fmtMs(s.gpuMs)}` : \"gpu –\";\n drawSpark();\n if (!root.classList.contains(\"open\")) return;\n\n cpuBar.fill.style.width = `${Math.min(100, (s.cpuMs / budgetMs) * 100)}%`;\n cpuBar.stat.textContent = `${fmtMs(s.cpuMs)} · max ${fmtMs(maxCpu)}`;\n gpuBar.fill.style.width = `${Math.min(100, (s.gpuMs / budgetMs) * 100)}%`;\n gpuBar.fill.style.background = s.gpuMs > budgetMs ? \"#ff7b72\" : \"#58a6ff\";\n gpuBar.stat.textContent = `${fmtMs(s.gpuMs)} · max ${fmtMs(maxGpu)}`;\n\n callsEl.textContent = fmtInt(s.drawCalls);\n trisEl.textContent = fmtK(s.triangles);\n ptsEl.textContent = fmtK(s.points + s.lines);\n progsEl.textContent = fmtInt(s.programs);\n geosEl.textContent = fmtInt(s.geometries);\n texsEl.textContent = fmtInt(s.textures);\n\n rtRow.textContent = `${s.renderTargets} targets · ${s.renderMegaPixels.toFixed(1)} MP total`;\n fboBar.fill.style.width = `${Math.min(100, (s.fboBytes / s.fboBudgetBytes) * 100)}%`;\n fboBar.fill.style.background = s.fboBytes > s.fboBudgetBytes * 0.85 ? \"#ffa657\" : \"#58a6ff\";\n fboBar.stat.textContent = `${fmtMB(s.fboBytes)} / ${fmtMB(s.fboBudgetBytes)}`;\n\n seg.textContent = \"\";\n const phases: Array<[string, number]> = [\n [\"hot\", s.islands.hot],\n [\"waking\", s.islands.waking],\n [\"warm\", s.islands.warm],\n [\"cold\", s.islands.cold],\n [\"dormant\", s.islands.dormant],\n ];\n const denom = Math.max(1, s.islands.total);\n const legendParts: string[] = [];\n for (const [phase, count] of phases) {\n if (count === 0) continue;\n const i = doc.createElement(\"i\");\n i.style.width = `${(count / denom) * 100}%`;\n i.style.background = PHASE_COLORS[phase] ?? \"#8b949e\";\n seg.appendChild(i);\n legendParts.push(`${count} ${phase}`);\n }\n legend.textContent = legendParts.length > 0 ? legendParts.join(\" · \") : \"no islands\";\n paintRow.textContent = `repainted ${s.repainted}/frame · pending ${s.pendingPaints} · evicted Σ${evictedTotal}`;\n\n lodRow.textContent = `zoom ${(s.zoom * 100).toFixed(0)}% → band ×${s.band} → paint dpr ${s.effectiveDpr.toFixed(2)}`;\n const bands = Object.entries(s.bandHistogram).sort((a, b) => a[0].localeCompare(b[0]));\n bandsRow.textContent =\n bands.length > 0 ? `targets by band: ${bands.map(([k, v]) => `${k}: ${v}`).join(\" · \")}` : \"no live targets\";\n\n const pop = Math.max(1, s.visibleWidgets + s.culledWidgets);\n cullBar.fill.style.width = `${(s.visibleWidgets / pop) * 100}%`;\n cullBar.fill.style.background = \"#3fb950\";\n cullBar.stat.textContent = `${s.visibleWidgets} visible · ${s.culledWidgets} culled`;\n };\n\n return {\n push(s) {\n if (disposed) return;\n latest = s;\n maxCpu = Math.max(maxCpu, s.cpuMs);\n maxGpu = Math.max(maxGpu, s.gpuMs);\n evictedTotal += s.evicted;\n samples.push(s.gpuMs > 0 ? s.gpuMs : s.cpuMs);\n if (samples.length > SPARK_SAMPLES) samples.splice(0, samples.length - SPARK_SAMPLES);\n const now = performance.now();\n if (now - lastRender >= 120) {\n lastRender = now;\n render();\n }\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n root.remove();\n },\n };\n}\n","/**\n * @ice/devtools — strata's observer panel + FPS profiler, engine-flavored\n * (describe/durable/ephemeral/reflect-lane glue). Reads the world outside the\n * tick; never a reflector, never writes ECS. Nobody imports us\n * (depcruise-enforced leaf).\n */\nexport const DEVTOOLS_VERSION = \"0.1.0\";\n\nexport {\n attachDevtools,\n engineDescribe,\n type DevtoolsEngine,\n type DevtoolsHandle,\n type DevtoolsOpts,\n} from \"./attach\";\nexport { createDock, type Dock, type DockCorner, type DockOptions, type DockSlotId } from \"./dock\";\nexport { createGlPanel, type GlPanel, type GlPanelCorner, type GlPanelOptions, type GlPanelStats } from \"./gl-panel\";\n"],"mappings":"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","names":["w","STYLE_ID","CSS","injectStyle"]}
package/dist/dom.js CHANGED
@@ -1,7 +1,11 @@
1
1
  import {
2
+ CLAIM_OWNS_ESCAPE,
2
3
  DOM_VERSION,
4
+ FOCUS_PROXY_ATTR,
5
+ KEYBOARD_CLAIM_ATTR,
3
6
  attachMeasureAdapter,
4
7
  attachPointerAdapter,
8
+ attachWidgetFocus,
5
9
  createCanvasHost,
6
10
  createChromeReflector,
7
11
  createCursorReflector,
@@ -10,15 +14,22 @@ import {
10
14
  createPlaneTransformReflector,
11
15
  createPlanes,
12
16
  createRemoteCursorsReflector,
17
+ isEditableTarget,
18
+ keyboardClaimOf,
13
19
  startRafLoop,
20
+ wheelCede,
14
21
  wireMeasurement
15
- } from "./chunk-PKK6GLSA.js";
16
- import "./chunk-5QY7HK6G.js";
22
+ } from "./chunk-ELCBCTKY.js";
23
+ import "./chunk-JLXETATP.js";
17
24
  import "./chunk-OR5FNVUE.js";
18
25
  export {
26
+ CLAIM_OWNS_ESCAPE,
19
27
  DOM_VERSION,
28
+ FOCUS_PROXY_ATTR,
29
+ KEYBOARD_CLAIM_ATTR,
20
30
  attachMeasureAdapter,
21
31
  attachPointerAdapter,
32
+ attachWidgetFocus,
22
33
  createCanvasHost,
23
34
  createChromeReflector,
24
35
  createCursorReflector,
@@ -27,7 +38,10 @@ export {
27
38
  createPlaneTransformReflector,
28
39
  createPlanes,
29
40
  createRemoteCursorsReflector,
41
+ isEditableTarget,
42
+ keyboardClaimOf,
30
43
  startRafLoop,
44
+ wheelCede,
31
45
  wireMeasurement
32
46
  };
33
47
  //# sourceMappingURL=dom.js.map
package/dist/ground.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  defineQuery,
24
24
  portSlots,
25
25
  widgets
26
- } from "./chunk-5QY7HK6G.js";
26
+ } from "./chunk-JLXETATP.js";
27
27
  import {
28
28
  portAnchor,
29
29
  tessellateCubic,
package/dist/index.js CHANGED
@@ -4,6 +4,9 @@ import {
4
4
  ActiveTool,
5
5
  All,
6
6
  Any,
7
+ BEHAVIOR_DEFAULT_PHASE,
8
+ BEHAVIOR_MARKER_PREFIX,
9
+ BEHAVIOR_PHASES,
7
10
  BoardRoot,
8
11
  CAMERA_DEFAULTS,
9
12
  CHROME_DEFAULTS,
@@ -41,6 +44,7 @@ import {
41
44
  FrameMode,
42
45
  GESTURE_DEFAULTS,
43
46
  GRAPH_PICK_DEFAULTS,
47
+ GUEST_BUDGET,
44
48
  GestureActive,
45
49
  GestureCancelled,
46
50
  GestureEnded,
@@ -62,6 +66,7 @@ import {
62
66
  HandledByWidget,
63
67
  InsertGhost,
64
68
  Keyboard,
69
+ KeyboardExclusive,
65
70
  Local,
66
71
  LocalPointer,
67
72
  LongPress,
@@ -146,6 +151,7 @@ import {
146
151
  WentCancelled,
147
152
  WentDown,
148
153
  WentUp,
154
+ WheelHandled,
149
155
  WheelPan,
150
156
  WheelZoom,
151
157
  WidgetBreakpoint,
@@ -160,15 +166,21 @@ import {
160
166
  arrangeWidgets,
161
167
  attachBroadcastRelay,
162
168
  attachPresence,
169
+ attachSpawnBehaviors,
163
170
  attachSpawnParent,
171
+ behaviorMarkerKey,
172
+ behaviors,
164
173
  broadcastChannelByteChannel,
165
174
  buildOrdinals,
166
175
  bumpVersion,
167
176
  cancelActiveGestures,
168
177
  cascadeDestroy,
178
+ classifyBehaviorVersion,
169
179
  clearSelection,
170
180
  compareStackOrder,
181
+ compileBehavior,
171
182
  createActiveMembership,
183
+ createBehaviorHarness,
172
184
  createBreakpointSystem,
173
185
  createCameraSystems,
174
186
  createCanvasEngine,
@@ -180,6 +192,7 @@ import {
180
192
  createDrawTool,
181
193
  createEngine,
182
194
  createFrameControl,
195
+ createGuestRegistry,
183
196
  createInputQueue,
184
197
  createLiveWriter,
185
198
  createMeasureIngest,
@@ -202,6 +215,7 @@ import {
202
215
  currentNavEntry,
203
216
  currentNavFrame,
204
217
  decodeEnvelope,
218
+ defineBehavior,
205
219
  defineComponent,
206
220
  definePhaseSet,
207
221
  definePrefab,
@@ -216,6 +230,7 @@ import {
216
230
  defineWidget,
217
231
  devGuardsEnabled,
218
232
  distPointToBox,
233
+ docBehaviorVersion,
219
234
  encodeEnvelope,
220
235
  ensureCanvasSurface,
221
236
  enumOf,
@@ -230,8 +245,10 @@ import {
230
245
  installPresence,
231
246
  installWidgetRuntime,
232
247
  instantiate,
248
+ isDurableEligible,
233
249
  isMidGesture,
234
250
  joinDoc,
251
+ makeChurnGuard,
235
252
  makeDefaultMayDiverge,
236
253
  makeVersionGuard,
237
254
  navFlightActive,
@@ -243,6 +260,7 @@ import {
243
260
  portSlots,
244
261
  prefabs,
245
262
  readDocVersionReport,
263
+ renames,
246
264
  restoreAutosave,
247
265
  runMigrations,
248
266
  schemaMeta,
@@ -252,6 +270,7 @@ import {
252
270
  setSelection,
253
271
  setWidgetProps,
254
272
  spawnWidget,
273
+ stampBehaviorMarker,
255
274
  stampEngineMeta,
256
275
  startAutosave,
257
276
  startNavFlight,
@@ -260,7 +279,7 @@ import {
260
279
  widgetSpawnInits,
261
280
  widgets,
262
281
  writeRuntimeResource
263
- } from "./chunk-5QY7HK6G.js";
282
+ } from "./chunk-JLXETATP.js";
264
283
  import "./chunk-OR5FNVUE.js";
265
284
  export {
266
285
  Accepts,
@@ -268,6 +287,9 @@ export {
268
287
  ActiveTool,
269
288
  All,
270
289
  Any,
290
+ BEHAVIOR_DEFAULT_PHASE,
291
+ BEHAVIOR_MARKER_PREFIX,
292
+ BEHAVIOR_PHASES,
271
293
  BoardRoot,
272
294
  CAMERA_DEFAULTS,
273
295
  CHROME_DEFAULTS,
@@ -305,6 +327,7 @@ export {
305
327
  FrameMode,
306
328
  GESTURE_DEFAULTS,
307
329
  GRAPH_PICK_DEFAULTS,
330
+ GUEST_BUDGET,
308
331
  GestureActive,
309
332
  GestureCancelled,
310
333
  GestureEnded,
@@ -326,6 +349,7 @@ export {
326
349
  HandledByWidget,
327
350
  InsertGhost,
328
351
  Keyboard,
352
+ KeyboardExclusive,
329
353
  Local,
330
354
  LocalPointer,
331
355
  LongPress,
@@ -410,6 +434,7 @@ export {
410
434
  WentCancelled,
411
435
  WentDown,
412
436
  WentUp,
437
+ WheelHandled,
413
438
  WheelPan,
414
439
  WheelZoom,
415
440
  WidgetBreakpoint,
@@ -424,15 +449,21 @@ export {
424
449
  arrangeWidgets,
425
450
  attachBroadcastRelay,
426
451
  attachPresence,
452
+ attachSpawnBehaviors,
427
453
  attachSpawnParent,
454
+ behaviorMarkerKey,
455
+ behaviors,
428
456
  broadcastChannelByteChannel,
429
457
  buildOrdinals,
430
458
  bumpVersion,
431
459
  cancelActiveGestures,
432
460
  cascadeDestroy,
461
+ classifyBehaviorVersion,
433
462
  clearSelection,
434
463
  compareStackOrder,
464
+ compileBehavior,
435
465
  createActiveMembership,
466
+ createBehaviorHarness,
436
467
  createBreakpointSystem,
437
468
  createCameraSystems,
438
469
  createCanvasEngine,
@@ -444,6 +475,7 @@ export {
444
475
  createDrawTool,
445
476
  createEngine,
446
477
  createFrameControl,
478
+ createGuestRegistry,
447
479
  createInputQueue,
448
480
  createLiveWriter,
449
481
  createMeasureIngest,
@@ -466,6 +498,7 @@ export {
466
498
  currentNavEntry,
467
499
  currentNavFrame,
468
500
  decodeEnvelope,
501
+ defineBehavior,
469
502
  defineComponent,
470
503
  definePhaseSet,
471
504
  definePrefab,
@@ -480,6 +513,7 @@ export {
480
513
  defineWidget,
481
514
  devGuardsEnabled,
482
515
  distPointToBox,
516
+ docBehaviorVersion,
483
517
  encodeEnvelope,
484
518
  ensureCanvasSurface,
485
519
  enumOf,
@@ -494,8 +528,10 @@ export {
494
528
  installPresence,
495
529
  installWidgetRuntime,
496
530
  instantiate,
531
+ isDurableEligible,
497
532
  isMidGesture,
498
533
  joinDoc,
534
+ makeChurnGuard,
499
535
  makeDefaultMayDiverge,
500
536
  makeVersionGuard,
501
537
  navFlightActive,
@@ -507,6 +543,7 @@ export {
507
543
  portSlots,
508
544
  prefabs,
509
545
  readDocVersionReport,
546
+ renames,
510
547
  restoreAutosave,
511
548
  runMigrations,
512
549
  schemaMeta,
@@ -516,6 +553,7 @@ export {
516
553
  setSelection,
517
554
  setWidgetProps,
518
555
  spawnWidget,
556
+ stampBehaviorMarker,
519
557
  stampEngineMeta,
520
558
  startAutosave,
521
559
  startNavFlight,
package/dist/r3f.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  EngineProvider,
3
3
  hasPreviewSnapshot,
4
4
  setPreviewSnapshot
5
- } from "./chunk-2GUYATUM.js";
6
- import "./chunk-PKK6GLSA.js";
5
+ } from "./chunk-BFT7YWS2.js";
6
+ import "./chunk-ELCBCTKY.js";
7
7
  import {
8
8
  Active,
9
9
  Camera,
@@ -26,7 +26,7 @@ import {
26
26
  navFlightActive,
27
27
  pickTopAt,
28
28
  widgets
29
- } from "./chunk-5QY7HK6G.js";
29
+ } from "./chunk-JLXETATP.js";
30
30
  import {
31
31
  compositeCameraFrustum,
32
32
  computeIslandPhase,
package/dist/react.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  hasPreviewSnapshot,
11
11
  setPreviewSnapshot,
12
12
  subscribePreviewSnapshots,
13
+ useBehavior,
13
14
  useBreakpoint,
14
15
  useCanvasEngine,
15
16
  useCommit,
@@ -24,9 +25,9 @@ import {
24
25
  useWidgetProps,
25
26
  useWorld,
26
27
  useWorldComponent
27
- } from "./chunk-2GUYATUM.js";
28
- import "./chunk-PKK6GLSA.js";
29
- import "./chunk-5QY7HK6G.js";
28
+ } from "./chunk-BFT7YWS2.js";
29
+ import "./chunk-ELCBCTKY.js";
30
+ import "./chunk-JLXETATP.js";
30
31
  import "./chunk-OR5FNVUE.js";
31
32
  export {
32
33
  EngineProvider,
@@ -40,6 +41,7 @@ export {
40
41
  hasPreviewSnapshot,
41
42
  setPreviewSnapshot,
42
43
  subscribePreviewSnapshots,
44
+ useBehavior,
43
45
  useBreakpoint,
44
46
  useCanvasEngine,
45
47
  useCommit,
@@ -0,0 +1,74 @@
1
+ import type { Batch, CollectOptions, Component, Relation, Resource, System, SystemAccess, SystemCtx, Tag, TickSystem } from "@vibecook/strata-ecs";
2
+ import type { AnySystem } from "../engine/pipeline";
3
+ import type { AnyBehaviorDef, BehaviorPhase } from "./types";
4
+ /**
5
+ * The bodies the runtime supplies. Compilation owns SHAPE (query, access,
6
+ * runIf, name, registration identity); the runtime owns behavior. Keeping the
7
+ * seam here means the compiler's rules — the split, the partition, the gating —
8
+ * are unit-testable without standing up a delivery loop.
9
+ */
10
+ export interface BehaviorSystemHooks {
11
+ /**
12
+ * The delivery `runIf`: drains the collector, stashes the delta for the body,
13
+ * evaluates the polls, and decides. Side-effecting BY DESIGN — there is no
14
+ * non-destructive peek on a `ChangeCollector`, so the drain has to happen
15
+ * in the gate or the gate cannot see the work (the `makeChurnGuard` idiom).
16
+ */
17
+ shouldDeliver(ctx: SystemCtx): boolean;
18
+ deliver(ctx: SystemCtx): void;
19
+ /** The tick `runIf` — cheap: instance count, suspension, freeze. */
20
+ shouldTick(ctx: SystemCtx): boolean;
21
+ tickChunk(batch: Batch, ctx: SystemCtx): void;
22
+ /**
23
+ * Run `body` inside this behavior's breaker bracket + fault domain. Returns
24
+ * false when the breaker is holding the behavior down or the body threw.
25
+ */
26
+ charge(body: () => void): boolean;
27
+ /** Breaker verdict — polled from `runIf`, so it must be cheap and never throw. */
28
+ suspended(): boolean;
29
+ /**
30
+ * A frame the behavior sat out because the breaker suspended it. The runtime
31
+ * DISCARDS its collectors here: a suspended behavior that stops draining
32
+ * still journals, so its collectors would grow for as long as the suspension
33
+ * lasts and then hand the resumed behavior a delta describing a world many
34
+ * frames old.
35
+ */
36
+ onSuspendedFrame(): void;
37
+ }
38
+ export interface CompiledBehavior {
39
+ readonly behavior: AnyBehaviorDef;
40
+ readonly phase: BehaviorPhase;
41
+ /** The framework collector's subscription (always `coarse: false`). */
42
+ readonly collect: CollectOptions;
43
+ /** `reads:` partitioned by kind — each routed to a different mechanism. */
44
+ readonly readComponents: readonly Component[];
45
+ readonly readTags: readonly Tag[];
46
+ readonly readRelations: readonly Relation[];
47
+ readonly readResources: readonly Resource[];
48
+ readonly deliveryAccess: SystemAccess;
49
+ readonly tickAccess: SystemAccess | undefined;
50
+ readonly delivery: TickSystem;
51
+ /** Present only when the declaration has an `on.tick` hook. */
52
+ readonly tick: System | undefined;
53
+ /** Registration order: delivery first, then tick. */
54
+ readonly systems: readonly AnySystem[];
55
+ }
56
+ /**
57
+ * Is `c` a cell the document can hold? Two sources: a durable prefab's
58
+ * eligible set, and (BF-D6) any durable behavior's own data component —
59
+ * registered behavior components are UNIVERSALLY durable-eligible.
60
+ *
61
+ * This is deliberately compile-time and not definition-time. At module-eval
62
+ * time the prefab registry may not yet hold the widget whose Position a
63
+ * behavior writes, so a definition-time answer would depend on import order —
64
+ * and an import-order-dependent refusal is worse than no refusal, because it
65
+ * passes on the developer's machine and fails in the bundle.
66
+ */
67
+ export declare function isDurableEligible(c: Component): boolean;
68
+ export declare function partitionReads(b: AnyBehaviorDef): {
69
+ components: Component[];
70
+ tags: Tag[];
71
+ relations: Relation[];
72
+ resources: Resource[];
73
+ };
74
+ export declare function compileBehavior(b: AnyBehaviorDef, hooks: BehaviorSystemHooks): CompiledBehavior;