@vibecook/ice 0.4.0 → 0.7.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.
- package/README.md +4 -4
- package/dist/{chunk-3PHPWRB2.js → chunk-55QCOE74.js} +7 -4
- package/dist/chunk-55QCOE74.js.map +1 -0
- package/dist/{chunk-ENMRSITH.js → chunk-5OU3OWDH.js} +16 -3
- package/dist/chunk-5OU3OWDH.js.map +1 -0
- package/dist/{chunk-7LZAXQAO.js → chunk-LATXYFMO.js} +6823 -3926
- package/dist/chunk-LATXYFMO.js.map +1 -0
- package/dist/devtools.js +5 -2
- package/dist/devtools.js.map +1 -1
- package/dist/dom.js +2 -2
- package/dist/ground.js +1 -1
- package/dist/index.js +35 -1
- package/dist/r3f.js +3 -3
- package/dist/react.js +5 -3
- package/dist/types/packages/core/src/behavior/compile.d.ts +74 -0
- package/dist/types/packages/core/src/behavior/define-behavior.d.ts +56 -0
- package/dist/types/packages/core/src/behavior/differ.d.ts +91 -0
- package/dist/types/packages/core/src/behavior/harness.d.ts +65 -0
- package/dist/types/packages/core/src/behavior/migrate.d.ts +87 -0
- package/dist/types/packages/core/src/behavior/public-reads.d.ts +31 -0
- package/dist/types/packages/core/src/behavior/runtime.d.ts +102 -0
- package/dist/types/packages/core/src/behavior/types.d.ts +358 -0
- package/dist/types/packages/core/src/behavior/world-view.d.ts +36 -0
- package/dist/types/packages/core/src/engine/engine.d.ts +38 -1
- package/dist/types/packages/core/src/engine/guests.d.ts +199 -0
- package/dist/types/packages/core/src/engine/reflectors.d.ts +8 -0
- package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +26 -0
- package/dist/types/packages/core/src/guards/guarded-tx.d.ts +57 -0
- package/dist/types/packages/core/src/index.d.ts +11 -3
- package/dist/types/packages/core/src/widget/define-widget.d.ts +28 -0
- package/dist/types/packages/core/src/widget/props.d.ts +35 -1
- package/dist/types/packages/core/src/widget/spawn.d.ts +14 -0
- package/dist/types/packages/dom/src/loop.d.ts +9 -0
- package/dist/types/packages/react/src/hooks.d.ts +12 -1
- package/dist/types/packages/react/src/index.d.ts +1 -1
- package/package.json +31 -65
- package/dist/chunk-3PHPWRB2.js.map +0 -1
- package/dist/chunk-7LZAXQAO.js.map +0 -1
- package/dist/chunk-ENMRSITH.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-
|
|
17
|
+
} from "./chunk-LATXYFMO.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()?.
|
|
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;
|
package/dist/devtools.js.map
CHANGED
|
@@ -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
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
startRafLoop,
|
|
20
20
|
wheelCede,
|
|
21
21
|
wireMeasurement
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
22
|
+
} from "./chunk-55QCOE74.js";
|
|
23
|
+
import "./chunk-LATXYFMO.js";
|
|
24
24
|
import "./chunk-OR5FNVUE.js";
|
|
25
25
|
export {
|
|
26
26
|
CLAIM_OWNS_ESCAPE,
|
package/dist/ground.js
CHANGED
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,
|
|
@@ -162,15 +166,21 @@ import {
|
|
|
162
166
|
arrangeWidgets,
|
|
163
167
|
attachBroadcastRelay,
|
|
164
168
|
attachPresence,
|
|
169
|
+
attachSpawnBehaviors,
|
|
165
170
|
attachSpawnParent,
|
|
171
|
+
behaviorMarkerKey,
|
|
172
|
+
behaviors,
|
|
166
173
|
broadcastChannelByteChannel,
|
|
167
174
|
buildOrdinals,
|
|
168
175
|
bumpVersion,
|
|
169
176
|
cancelActiveGestures,
|
|
170
177
|
cascadeDestroy,
|
|
178
|
+
classifyBehaviorVersion,
|
|
171
179
|
clearSelection,
|
|
172
180
|
compareStackOrder,
|
|
181
|
+
compileBehavior,
|
|
173
182
|
createActiveMembership,
|
|
183
|
+
createBehaviorHarness,
|
|
174
184
|
createBreakpointSystem,
|
|
175
185
|
createCameraSystems,
|
|
176
186
|
createCanvasEngine,
|
|
@@ -182,6 +192,7 @@ import {
|
|
|
182
192
|
createDrawTool,
|
|
183
193
|
createEngine,
|
|
184
194
|
createFrameControl,
|
|
195
|
+
createGuestRegistry,
|
|
185
196
|
createInputQueue,
|
|
186
197
|
createLiveWriter,
|
|
187
198
|
createMeasureIngest,
|
|
@@ -204,6 +215,7 @@ import {
|
|
|
204
215
|
currentNavEntry,
|
|
205
216
|
currentNavFrame,
|
|
206
217
|
decodeEnvelope,
|
|
218
|
+
defineBehavior,
|
|
207
219
|
defineComponent,
|
|
208
220
|
definePhaseSet,
|
|
209
221
|
definePrefab,
|
|
@@ -216,8 +228,10 @@ import {
|
|
|
216
228
|
defineTickSystem,
|
|
217
229
|
defineTool,
|
|
218
230
|
defineWidget,
|
|
231
|
+
describeBehavior,
|
|
219
232
|
devGuardsEnabled,
|
|
220
233
|
distPointToBox,
|
|
234
|
+
docBehaviorVersion,
|
|
221
235
|
encodeEnvelope,
|
|
222
236
|
ensureCanvasSurface,
|
|
223
237
|
enumOf,
|
|
@@ -232,8 +246,10 @@ import {
|
|
|
232
246
|
installPresence,
|
|
233
247
|
installWidgetRuntime,
|
|
234
248
|
instantiate,
|
|
249
|
+
isDurableEligible,
|
|
235
250
|
isMidGesture,
|
|
236
251
|
joinDoc,
|
|
252
|
+
makeChurnGuard,
|
|
237
253
|
makeDefaultMayDiverge,
|
|
238
254
|
makeVersionGuard,
|
|
239
255
|
navFlightActive,
|
|
@@ -255,6 +271,7 @@ import {
|
|
|
255
271
|
setSelection,
|
|
256
272
|
setWidgetProps,
|
|
257
273
|
spawnWidget,
|
|
274
|
+
stampBehaviorMarker,
|
|
258
275
|
stampEngineMeta,
|
|
259
276
|
startAutosave,
|
|
260
277
|
startNavFlight,
|
|
@@ -263,7 +280,7 @@ import {
|
|
|
263
280
|
widgetSpawnInits,
|
|
264
281
|
widgets,
|
|
265
282
|
writeRuntimeResource
|
|
266
|
-
} from "./chunk-
|
|
283
|
+
} from "./chunk-LATXYFMO.js";
|
|
267
284
|
import "./chunk-OR5FNVUE.js";
|
|
268
285
|
export {
|
|
269
286
|
Accepts,
|
|
@@ -271,6 +288,9 @@ export {
|
|
|
271
288
|
ActiveTool,
|
|
272
289
|
All,
|
|
273
290
|
Any,
|
|
291
|
+
BEHAVIOR_DEFAULT_PHASE,
|
|
292
|
+
BEHAVIOR_MARKER_PREFIX,
|
|
293
|
+
BEHAVIOR_PHASES,
|
|
274
294
|
BoardRoot,
|
|
275
295
|
CAMERA_DEFAULTS,
|
|
276
296
|
CHROME_DEFAULTS,
|
|
@@ -308,6 +328,7 @@ export {
|
|
|
308
328
|
FrameMode,
|
|
309
329
|
GESTURE_DEFAULTS,
|
|
310
330
|
GRAPH_PICK_DEFAULTS,
|
|
331
|
+
GUEST_BUDGET,
|
|
311
332
|
GestureActive,
|
|
312
333
|
GestureCancelled,
|
|
313
334
|
GestureEnded,
|
|
@@ -429,15 +450,21 @@ export {
|
|
|
429
450
|
arrangeWidgets,
|
|
430
451
|
attachBroadcastRelay,
|
|
431
452
|
attachPresence,
|
|
453
|
+
attachSpawnBehaviors,
|
|
432
454
|
attachSpawnParent,
|
|
455
|
+
behaviorMarkerKey,
|
|
456
|
+
behaviors,
|
|
433
457
|
broadcastChannelByteChannel,
|
|
434
458
|
buildOrdinals,
|
|
435
459
|
bumpVersion,
|
|
436
460
|
cancelActiveGestures,
|
|
437
461
|
cascadeDestroy,
|
|
462
|
+
classifyBehaviorVersion,
|
|
438
463
|
clearSelection,
|
|
439
464
|
compareStackOrder,
|
|
465
|
+
compileBehavior,
|
|
440
466
|
createActiveMembership,
|
|
467
|
+
createBehaviorHarness,
|
|
441
468
|
createBreakpointSystem,
|
|
442
469
|
createCameraSystems,
|
|
443
470
|
createCanvasEngine,
|
|
@@ -449,6 +476,7 @@ export {
|
|
|
449
476
|
createDrawTool,
|
|
450
477
|
createEngine,
|
|
451
478
|
createFrameControl,
|
|
479
|
+
createGuestRegistry,
|
|
452
480
|
createInputQueue,
|
|
453
481
|
createLiveWriter,
|
|
454
482
|
createMeasureIngest,
|
|
@@ -471,6 +499,7 @@ export {
|
|
|
471
499
|
currentNavEntry,
|
|
472
500
|
currentNavFrame,
|
|
473
501
|
decodeEnvelope,
|
|
502
|
+
defineBehavior,
|
|
474
503
|
defineComponent,
|
|
475
504
|
definePhaseSet,
|
|
476
505
|
definePrefab,
|
|
@@ -483,8 +512,10 @@ export {
|
|
|
483
512
|
defineTickSystem,
|
|
484
513
|
defineTool,
|
|
485
514
|
defineWidget,
|
|
515
|
+
describeBehavior,
|
|
486
516
|
devGuardsEnabled,
|
|
487
517
|
distPointToBox,
|
|
518
|
+
docBehaviorVersion,
|
|
488
519
|
encodeEnvelope,
|
|
489
520
|
ensureCanvasSurface,
|
|
490
521
|
enumOf,
|
|
@@ -499,8 +530,10 @@ export {
|
|
|
499
530
|
installPresence,
|
|
500
531
|
installWidgetRuntime,
|
|
501
532
|
instantiate,
|
|
533
|
+
isDurableEligible,
|
|
502
534
|
isMidGesture,
|
|
503
535
|
joinDoc,
|
|
536
|
+
makeChurnGuard,
|
|
504
537
|
makeDefaultMayDiverge,
|
|
505
538
|
makeVersionGuard,
|
|
506
539
|
navFlightActive,
|
|
@@ -522,6 +555,7 @@ export {
|
|
|
522
555
|
setSelection,
|
|
523
556
|
setWidgetProps,
|
|
524
557
|
spawnWidget,
|
|
558
|
+
stampBehaviorMarker,
|
|
525
559
|
stampEngineMeta,
|
|
526
560
|
startAutosave,
|
|
527
561
|
startNavFlight,
|
package/dist/r3f.js
CHANGED
|
@@ -2,8 +2,8 @@ import {
|
|
|
2
2
|
EngineProvider,
|
|
3
3
|
hasPreviewSnapshot,
|
|
4
4
|
setPreviewSnapshot
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-5OU3OWDH.js";
|
|
6
|
+
import "./chunk-55QCOE74.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-
|
|
29
|
+
} from "./chunk-LATXYFMO.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-
|
|
28
|
-
import "./chunk-
|
|
29
|
-
import "./chunk-
|
|
28
|
+
} from "./chunk-5OU3OWDH.js";
|
|
29
|
+
import "./chunk-55QCOE74.js";
|
|
30
|
+
import "./chunk-LATXYFMO.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;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineBehavior` — definition, validation, and the process-global registry
|
|
3
|
+
* (design-009 §4.1).
|
|
4
|
+
*
|
|
5
|
+
* Compile output is one generated component per behavior (`behavior:<name>`),
|
|
6
|
+
* which is also the CONFLICT UNIT: one behavior = one component = one
|
|
7
|
+
* committer-wins cell. The guidance line for authors, worth repeating here
|
|
8
|
+
* because it is the rule that keeps collaboration sane: *would two peers
|
|
9
|
+
* plausibly edit these fields concurrently? Then they are two behaviors.*
|
|
10
|
+
*
|
|
11
|
+
* Everything in this file is definition-time. The compiler (compile.ts) owns
|
|
12
|
+
* the checks that need a populated world — above all the durable-eligibility
|
|
13
|
+
* refusal, which cannot run here: at module-eval time the prefab registry may
|
|
14
|
+
* not yet hold the widget whose Position the behavior writes, and a check that
|
|
15
|
+
* depends on import order is a check that reports differently on a good day.
|
|
16
|
+
*/
|
|
17
|
+
import type { Component } from "@vibecook/strata-ecs";
|
|
18
|
+
import { type AnyBehaviorDef, type BehaviorDescription, type BehaviorHandle, type BehaviorRead, type BehaviorSchema, type BehaviorSpec, type BehaviorStore, type DataOf } from "./types";
|
|
19
|
+
/**
|
|
20
|
+
* The generated component's name. Prefixed so a behavior can never collide
|
|
21
|
+
* with `defineWidget`'s `<type>:<group>` components — and, because this string
|
|
22
|
+
* is the DURABLE key that projection is driven by, it is a permanent wire
|
|
23
|
+
* format decision, not an implementation detail. A collision anywhere is loud
|
|
24
|
+
* (`ensureComponent` throws on same-name/different-shape), never silent.
|
|
25
|
+
*/
|
|
26
|
+
export declare function behaviorComponentName(name: string): string;
|
|
27
|
+
export declare const behaviors: {
|
|
28
|
+
get(name: string): AnyBehaviorDef | undefined;
|
|
29
|
+
all(): AnyBehaviorDef[];
|
|
30
|
+
/** The behavior that owns a generated data component, if any. */
|
|
31
|
+
forComponent(c: Component): AnyBehaviorDef | undefined;
|
|
32
|
+
/** Is `c` a behavior's own data component? (the BF-D6 eligibility branch). */
|
|
33
|
+
isBehaviorComponent(c: Component): boolean;
|
|
34
|
+
};
|
|
35
|
+
export type ReadKind = "component" | "tag" | "relation" | "resource" | "behavior";
|
|
36
|
+
/**
|
|
37
|
+
* Classify a `reads:` entry. Handles are opaque and structurally ambiguous
|
|
38
|
+
* (Component and Resource have IDENTICAL shapes), so classification goes
|
|
39
|
+
* through ICE's own schema registries — which doubles as the §4.1 rule that
|
|
40
|
+
* reads must name REGISTERED schema: anything defined outside the `@ice/core`
|
|
41
|
+
* wrappers is unclassifiable here and is rejected rather than guessed at.
|
|
42
|
+
*/
|
|
43
|
+
export declare function classifyRead(r: BehaviorRead): {
|
|
44
|
+
kind: ReadKind;
|
|
45
|
+
name: string;
|
|
46
|
+
} | undefined;
|
|
47
|
+
/** The read component a `reads:` entry contributes, if it contributes one. */
|
|
48
|
+
export declare function readComponentOf(r: BehaviorRead): Component | undefined;
|
|
49
|
+
export declare function describeBehavior(behavior: AnyBehaviorDef): BehaviorDescription;
|
|
50
|
+
export declare function defineBehavior<const S extends BehaviorStore, const Sch extends BehaviorSchema = BehaviorSchema>(name: string, spec: BehaviorSpec<S, Sch>): BehaviorHandle<DataOf<Sch>>;
|
|
51
|
+
/**
|
|
52
|
+
* TEST-ONLY registry wipe (mirrors `__resetPrefabsForTests`; deliberately NOT
|
|
53
|
+
* on the barrel). strata's schema registry has no reset, so a test that wipes
|
|
54
|
+
* this must still use file-unique behavior names.
|
|
55
|
+
*/
|
|
56
|
+
export declare function __resetBehaviorsForTests(): void;
|