@vibecook/ice 0.1.0 → 0.2.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 +37 -2
- package/dist/chunk-5SJYHQI4.js +636 -0
- package/dist/chunk-5SJYHQI4.js.map +1 -0
- package/dist/{chunk-5S7GSXJG.js → chunk-DXU3RZZS.js} +2913 -1180
- package/dist/chunk-DXU3RZZS.js.map +1 -0
- package/dist/{chunk-FIAXPNNY.js → chunk-IASTQMU5.js} +130 -551
- package/dist/chunk-IASTQMU5.js.map +1 -0
- package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
- package/dist/chunk-OR5FNVUE.js.map +1 -0
- package/dist/devtools.js +508 -372
- package/dist/devtools.js.map +1 -1
- package/dist/dom.js +3 -11
- package/dist/ground.js +715 -0
- package/dist/ground.js.map +1 -0
- package/dist/index.js +70 -2
- package/dist/kernel.js +31 -1
- package/dist/r3f.js +599 -62
- package/dist/r3f.js.map +1 -1
- package/dist/react.js +34 -418
- package/dist/react.js.map +1 -1
- package/dist/types/packages/core/src/catalog/camera-derived.d.ts +41 -3
- package/dist/types/packages/core/src/catalog/gesture.d.ts +39 -3
- package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
- package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
- package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
- package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
- package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
- package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
- package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
- package/dist/types/packages/core/src/doc/autosave.d.ts +15 -2
- package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
- package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
- package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
- package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
- package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
- package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
- package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
- package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
- package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
- package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
- package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +70 -6
- package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
- package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
- package/dist/types/packages/core/src/index.d.ts +13 -7
- package/dist/types/packages/core/src/input/queue.d.ts +11 -0
- package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
- package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
- package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
- package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
- package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
- package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
- package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
- package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
- package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
- package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
- package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
- package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
- package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
- package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
- package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
- package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
- package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
- package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
- package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
- package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
- package/dist/types/packages/devtools/src/attach.d.ts +77 -0
- package/dist/types/packages/devtools/src/dock.d.ts +38 -0
- package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
- package/dist/types/packages/devtools/src/index.d.ts +8 -6
- package/dist/types/packages/dom/src/index.d.ts +1 -3
- package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
- package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
- package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
- package/dist/types/packages/ground/src/index.d.ts +52 -0
- package/dist/types/packages/ground/src/layer.d.ts +40 -0
- package/dist/types/packages/ground/src/pass.d.ts +41 -0
- package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
- package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
- package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
- package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
- package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
- package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
- package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
- package/dist/types/packages/ground/src/renderer.d.ts +15 -0
- package/dist/types/packages/ice/src/ground.d.ts +1 -0
- package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
- package/dist/types/packages/kernel/src/coords.d.ts +15 -0
- package/dist/types/packages/kernel/src/easing.d.ts +11 -0
- package/dist/types/packages/kernel/src/index.d.ts +3 -0
- package/dist/types/packages/kernel/src/layout.d.ts +118 -0
- package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
- package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
- package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
- package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
- package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
- package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
- package/dist/types/packages/r3f/src/index.d.ts +3 -2
- package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
- package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
- package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
- package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
- package/dist/types/packages/react/src/engine-context.d.ts +8 -0
- package/dist/types/packages/react/src/index.d.ts +4 -2
- package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
- package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
- package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
- package/package.json +13 -8
- package/dist/chunk-5S7GSXJG.js.map +0 -1
- package/dist/chunk-BCJ47SX7.js.map +0 -1
- package/dist/chunk-FIAXPNNY.js.map +0 -1
- package/dist/types/packages/devtools/src/panel.d.ts +0 -48
- package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
- package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../kernel/src/shapes.ts","../../kernel/src/coords.ts","../../kernel/src/snap.ts","../../kernel/src/spatial-index.ts","../../kernel/src/zoom-bands.ts","../../kernel/src/eviction.ts","../../kernel/src/anchors.ts","../../kernel/src/bezier.ts","../../kernel/src/index.ts"],"sourcesContent":["/** Plain geometric structs shared across the kernel. Ported from v1 `ecs/math.ts`. */\n\nexport interface Vec2 {\n x: number;\n y: number;\n}\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface AABB {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\nexport function rectToAABB(r: Rect): AABB {\n return { minX: r.x, minY: r.y, maxX: r.x + r.width, maxY: r.y + r.height };\n}\n\nexport function aabbToRect(a: AABB): Rect {\n return { x: a.minX, y: a.minY, width: a.maxX - a.minX, height: a.maxY - a.minY };\n}\n\nexport function intersectsAABB(a: AABB, b: AABB): boolean {\n return a.maxX >= b.minX && a.minX <= b.maxX && a.maxY >= b.minY && a.minY <= b.maxY;\n}\n\nexport function pointInAABB(px: number, py: number, a: AABB): boolean {\n return px >= a.minX && px <= a.maxX && py >= a.minY && py <= a.maxY;\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n","/**\n * THE coordinate seam (Law 13): every conversion between screen, world, and\n * island (widget-local GL) space lives here — including the ONE Y-flip.\n * v1 duplicated this math in three places; nothing outside this module may\n * re-derive it.\n *\n * Conventions:\n * - screen: CSS px, canvas-container-relative, Y-down. DPR never enters world\n * math (it exists only in GL uniforms / FBO sizing — see zoom-bands).\n * - world: world units, Y-down. `Camera.{x,y}` is the WORLD point at the\n * viewport's top-left; `zoom` is screen px per world unit.\n * - island: widget-local GL space — CENTER origin, Y-UP, world-unit scale.\n * (v1 VirtualWidget convention; the only Y-flip in the codebase.)\n */\nimport type { Rect, Vec2 } from \"./shapes\";\n\nexport interface CameraState {\n x: number;\n y: number;\n zoom: number;\n}\n\nexport function screenToWorld(screenX: number, screenY: number, camera: CameraState): Vec2 {\n return {\n x: screenX / camera.zoom + camera.x,\n y: screenY / camera.zoom + camera.y,\n };\n}\n\nexport function worldToScreen(worldX: number, worldY: number, camera: CameraState): Vec2 {\n return {\n x: (worldX - camera.x) * camera.zoom,\n y: (worldY - camera.y) * camera.zoom,\n };\n}\n\n/**\n * Zoom about a fixed screen point (v1 `zoomAtPoint`): the world point under\n * `(screenX, screenY)` stays under it after the zoom change.\n * Returns the new camera (does not mutate). Clamping is the caller's policy.\n */\nexport function zoomAtPoint(\n camera: CameraState,\n screenX: number,\n screenY: number,\n newZoom: number,\n): CameraState {\n const anchor = screenToWorld(screenX, screenY, camera);\n return {\n x: anchor.x - screenX / newZoom,\n y: anchor.y - screenY / newZoom,\n zoom: newZoom,\n };\n}\n\n/**\n * Camera → the ONE per-plane CSS transform (design-002 §5 `planeTransform`).\n * Children are laid out in WORLD units inside the plane (left/top = world x/y),\n * the plane carries `transform: translate(tx px, ty px) scale(scale)` with\n * `transform-origin: 0 0` — so a child at world point p lands at\n * `p·zoom + t = (p − camera)·zoom`, i.e. exactly `worldToScreen(p)`.\n */\nexport function planeCssTransform(camera: CameraState): { tx: number; ty: number; scale: number } {\n return {\n tx: -camera.x * camera.zoom,\n ty: -camera.y * camera.zoom,\n scale: camera.zoom,\n };\n}\n\n/** World → island (center-origin, Y-up). THE Y-flip, direction one. */\nexport function worldToIsland(worldX: number, worldY: number, widget: Rect): Vec2 {\n return {\n x: worldX - (widget.x + widget.width / 2),\n y: -(worldY - (widget.y + widget.height / 2)),\n };\n}\n\n/** Island → world. THE Y-flip, direction two. */\nexport function islandToWorld(islandX: number, islandY: number, widget: Rect): Vec2 {\n return {\n x: widget.x + widget.width / 2 + islandX,\n y: widget.y + widget.height / 2 - islandY,\n };\n}\n\n// --- P2 composite scene (design-004 §1/§3) -------------------------------\n// The GL-views plane composites island quads in a Three scene whose axes are\n// world-unit X-right / Y-UP (Three convention). Same flip as island space,\n// applied at WORLD scale — these two helpers are the only place it exists.\n\n/**\n * Camera → the P2 composite ortho frustum + camera position (world units,\n * Y-up scene). `left/top` pin the viewport's top-left to `Camera.{x,y}`\n * exactly like `planeCssTransform` does for the DOM planes: the frustum spans\n * `viewport / zoom` world units and the camera sits at the world origin\n * offset, so a quad at `worldRectToComposite(rect)` lands on the same screen\n * pixels as a DOM host at `rect` under the plane transform.\n */\nexport function compositeCameraFrustum(\n camera: CameraState,\n viewportW: number,\n viewportH: number,\n): { left: number; right: number; top: number; bottom: number; x: number; y: number } {\n return {\n left: 0,\n right: viewportW / camera.zoom,\n top: 0,\n bottom: -(viewportH / camera.zoom),\n x: camera.x,\n y: -camera.y,\n };\n}\n\n/** World rect → composite-quad center + scale in the Y-up P2 scene. */\nexport function worldRectToComposite(rect: Rect): { x: number; y: number; sx: number; sy: number } {\n return {\n x: rect.x + rect.width / 2,\n y: -(rect.y + rect.height / 2),\n sx: rect.width,\n sy: rect.height,\n };\n}\n","/**\n * Snap guide computation (Figma-style), ported verbatim from v1\n * `ecs/spatial/snap.ts` (audited CLEAN-PORT):\n * Phase 1 — edge/center alignment (7 X + 7 Y candidate pairs per reference,\n * per-axis nearest-wins, ties within 0.01 accumulate guides)\n * Phase 2 — equal spacing (between-two-neighbors midpoint; extend-a-pattern),\n * same-perpendicular-band filter\n * Merge — alignment takes priority per axis; equal spacing fills the other;\n * indicators recomputed post-snap at threshold × 0.5.\n * Pure function over plain bounds. Candidate GATHERING (spatial index, tags,\n * Active scoping) is the caller's job — design-003 §5.2/§6.\n */\n\nexport interface SnapGuide {\n axis: \"x\" | \"y\";\n /** World-space coordinate of the alignment line */\n position: number;\n type: \"edge\" | \"center\";\n}\n\nexport interface EqualSpacingIndicator {\n axis: \"x\" | \"y\";\n /** The equal gap value (world units) */\n gap: number;\n /** Pairs of (from, to) marking each equal gap segment */\n segments: { from: number; to: number }[];\n /** Position on the perpendicular axis (for rendering) */\n perpPosition: number;\n}\n\nexport interface SnapResult {\n /** Snap-corrected delta (world units). Apply to entity position. */\n snapDx: number;\n snapDy: number;\n guides: SnapGuide[];\n spacings: EqualSpacingIndicator[];\n}\n\nexport interface EntityBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport function computeSnapGuides(\n dragged: EntityBounds,\n references: EntityBounds[],\n threshold: number,\n): SnapResult {\n const guides: SnapGuide[] = [];\n const spacings: EqualSpacingIndicator[] = [];\n let snapDx = 0;\n let snapDy = 0;\n\n const dLeft = dragged.x;\n const dRight = dragged.x + dragged.width;\n const dCenterX = dragged.x + dragged.width / 2;\n const dTop = dragged.y;\n const dBottom = dragged.y + dragged.height;\n const dCenterY = dragged.y + dragged.height / 2;\n\n let bestSnapX = Number.POSITIVE_INFINITY;\n let bestSnapY = Number.POSITIVE_INFINITY;\n let bestDx = 0;\n let bestDy = 0;\n const xGuides: SnapGuide[] = [];\n const yGuides: SnapGuide[] = [];\n\n // --- Phase 1: edge/center alignment ---\n for (const ref of references) {\n const rLeft = ref.x;\n const rRight = ref.x + ref.width;\n const rCenterX = ref.x + ref.width / 2;\n const rTop = ref.y;\n const rBottom = ref.y + ref.height;\n const rCenterY = ref.y + ref.height / 2;\n\n const xPairs: [number, number, \"edge\" | \"center\"][] = [\n [dLeft, rLeft, \"edge\"],\n [dLeft, rRight, \"edge\"],\n [dRight, rLeft, \"edge\"],\n [dRight, rRight, \"edge\"],\n [dCenterX, rCenterX, \"center\"],\n [dLeft, rCenterX, \"edge\"],\n [dRight, rCenterX, \"edge\"],\n ];\n for (const [dVal, rVal, type] of xPairs) {\n const dist = Math.abs(dVal - rVal);\n if (dist <= threshold) {\n const dx = rVal - dVal;\n if (dist < bestSnapX) {\n bestSnapX = dist;\n bestDx = dx;\n xGuides.length = 0;\n }\n if (dist <= bestSnapX + 0.01) {\n xGuides.push({ axis: \"x\", position: rVal, type });\n }\n }\n }\n\n const yPairs: [number, number, \"edge\" | \"center\"][] = [\n [dTop, rTop, \"edge\"],\n [dTop, rBottom, \"edge\"],\n [dBottom, rTop, \"edge\"],\n [dBottom, rBottom, \"edge\"],\n [dCenterY, rCenterY, \"center\"],\n [dTop, rCenterY, \"edge\"],\n [dBottom, rCenterY, \"edge\"],\n ];\n for (const [dVal, rVal, type] of yPairs) {\n const dist = Math.abs(dVal - rVal);\n if (dist <= threshold) {\n const dy = rVal - dVal;\n if (dist < bestSnapY) {\n bestSnapY = dist;\n bestDy = dy;\n yGuides.length = 0;\n }\n if (dist <= bestSnapY + 0.01) {\n yGuides.push({ axis: \"y\", position: rVal, type });\n }\n }\n }\n }\n\n // --- Phase 2: equal spacing ---\n const eqResult = computeEqualSpacing(dragged, references, threshold);\n\n // Merge: alignment snap takes priority; equal spacing fills the other axis.\n if (bestSnapX <= threshold) {\n snapDx = bestDx;\n } else if (eqResult.snapDx !== undefined) {\n snapDx = eqResult.snapDx;\n }\n if (bestSnapY <= threshold) {\n snapDy = bestDy;\n } else if (eqResult.snapDy !== undefined) {\n snapDy = eqResult.snapDy;\n }\n\n if (bestSnapX <= threshold) {\n const seen = new Set<number>();\n for (const g of xGuides) {\n if (!seen.has(g.position)) {\n seen.add(g.position);\n guides.push(g);\n }\n }\n }\n if (bestSnapY <= threshold) {\n const seen = new Set<number>();\n for (const g of yGuides) {\n if (!seen.has(g.position)) {\n seen.add(g.position);\n guides.push(g);\n }\n }\n }\n\n // Indicators recomputed against the SNAPPED bounds at half threshold.\n const snappedBounds: EntityBounds = {\n x: dragged.x + snapDx,\n y: dragged.y + snapDy,\n width: dragged.width,\n height: dragged.height,\n };\n const eqFinal = computeEqualSpacing(snappedBounds, references, threshold * 0.5);\n spacings.push(...eqFinal.indicators);\n\n return { snapDx, snapDy, guides, spacings };\n}\n\ninterface EqualSpacingResult {\n snapDx: number | undefined;\n snapDy: number | undefined;\n indicators: EqualSpacingIndicator[];\n}\n\nfunction computeEqualSpacing(\n dragged: EntityBounds,\n references: EntityBounds[],\n threshold: number,\n): EqualSpacingResult {\n const indicators: EqualSpacingIndicator[] = [];\n let snapDx: number | undefined;\n let snapDy: number | undefined;\n\n const xResult = checkAxisSpacing(dragged, references, threshold, \"x\");\n if (xResult) {\n snapDx = xResult.snap;\n indicators.push(...xResult.indicators);\n }\n const yResult = checkAxisSpacing(dragged, references, threshold, \"y\");\n if (yResult) {\n snapDy = yResult.snap;\n indicators.push(...yResult.indicators);\n }\n return { snapDx, snapDy, indicators };\n}\n\nfunction checkAxisSpacing(\n dragged: EntityBounds,\n references: EntityBounds[],\n threshold: number,\n axis: \"x\" | \"y\",\n): { snap: number; indicators: EqualSpacingIndicator[] } | null {\n const isX = axis === \"x\";\n const pos = (b: EntityBounds) => (isX ? b.x : b.y);\n const size = (b: EntityBounds) => (isX ? b.width : b.height);\n const perpPos = (b: EntityBounds) => (isX ? b.y : b.x);\n const perpSize = (b: EntityBounds) => (isX ? b.height : b.width);\n const end = (b: EntityBounds) => pos(b) + size(b);\n\n // Same-perpendicular-band filter.\n const neighbors = references.filter(\n (ref) =>\n perpPos(ref) < perpPos(dragged) + perpSize(dragged) &&\n perpPos(ref) + perpSize(ref) > perpPos(dragged),\n );\n if (neighbors.length < 1) return null;\n\n const sorted = [...neighbors].sort((a, b) => pos(a) - pos(b));\n\n // Existing gaps between consecutive references.\n const refGaps: { from: EntityBounds; to: EntityBounds; gap: number }[] = [];\n for (let i = 0; i < sorted.length - 1; i++) {\n const a = sorted[i];\n const b = sorted[i + 1];\n if (a === undefined || b === undefined) continue;\n const gap = pos(b) - end(a);\n if (gap > 0.1) refGaps.push({ from: a, to: b, gap });\n }\n\n let bestSnap: number | null = null;\n let bestIndicators: EqualSpacingIndicator[] = [];\n let bestDiff = Number.POSITIVE_INFINITY;\n\n // Case 1: between two neighbors — equalize left/right gaps.\n let leftN: EntityBounds | null = null;\n let rightN: EntityBounds | null = null;\n for (const ref of sorted) {\n if (end(ref) <= pos(dragged) + threshold) {\n if (!leftN || end(ref) > end(leftN)) leftN = ref;\n }\n if (pos(ref) >= end(dragged) - threshold) {\n if (!rightN || pos(ref) < pos(rightN)) rightN = ref;\n }\n }\n if (leftN && rightN) {\n const lGap = pos(dragged) - end(leftN);\n const rGap = pos(rightN) - end(dragged);\n const diff = Math.abs(lGap - rGap);\n if (diff <= threshold && diff < bestDiff) {\n const idealPos = (end(leftN) + pos(rightN) - size(dragged)) / 2;\n const snap = idealPos - pos(dragged);\n const equalGap = (pos(rightN) - end(leftN) - size(dragged)) / 2;\n if (equalGap > 0.1) {\n const perpY = computePerpCenter(dragged, [leftN, rightN], isX);\n bestSnap = snap;\n bestDiff = diff;\n bestIndicators = [\n {\n axis,\n gap: equalGap,\n segments: [\n { from: end(leftN), to: idealPos },\n { from: idealPos + size(dragged), to: pos(rightN) },\n ],\n perpPosition: perpY,\n },\n ];\n }\n }\n }\n\n // Case 2: extend an existing gap pattern (append right / prepend left).\n for (const refGap of refGaps) {\n const patternGap = refGap.gap;\n\n if (rightN === null || pos(refGap.to) >= end(dragged) - threshold * 2) {\n const chainEnd = refGap.to;\n const dragGap = pos(dragged) - end(chainEnd);\n const diff = Math.abs(dragGap - patternGap);\n if (diff <= threshold && diff < bestDiff) {\n const idealPos = end(chainEnd) + patternGap;\n const snap = idealPos - pos(dragged);\n const perpY = computePerpCenter(dragged, [refGap.from, refGap.to], isX);\n bestSnap = snap;\n bestDiff = diff;\n bestIndicators = [\n {\n axis,\n gap: patternGap,\n segments: [\n { from: end(refGap.from), to: pos(refGap.to) },\n { from: end(chainEnd), to: idealPos },\n ],\n perpPosition: perpY,\n },\n ];\n }\n }\n\n if (leftN === null || end(refGap.from) <= pos(dragged) + threshold * 2) {\n const chainStart = refGap.from;\n const dragGap = pos(chainStart) - end(dragged);\n const diff = Math.abs(dragGap - patternGap);\n if (diff <= threshold && diff < bestDiff) {\n const idealPos = pos(chainStart) - patternGap - size(dragged);\n const snap = idealPos - pos(dragged);\n const perpY = computePerpCenter(dragged, [refGap.from, refGap.to], isX);\n bestSnap = snap;\n bestDiff = diff;\n bestIndicators = [\n {\n axis,\n gap: patternGap,\n segments: [\n { from: idealPos + size(dragged), to: pos(chainStart) },\n { from: end(refGap.from), to: pos(refGap.to) },\n ],\n perpPosition: perpY,\n },\n ];\n }\n }\n }\n\n if (bestSnap !== null) return { snap: bestSnap, indicators: bestIndicators };\n return null;\n}\n\nfunction computePerpCenter(dragged: EntityBounds, refs: EntityBounds[], isX: boolean): number {\n const perpPos = (b: EntityBounds) => (isX ? b.y : b.x);\n const perpSize = (b: EntityBounds) => (isX ? b.height : b.width);\n const allBounds = [dragged, ...refs];\n const maxStart = Math.max(...allBounds.map(perpPos));\n const minEnd = Math.min(...allBounds.map((b) => perpPos(b) + perpSize(b)));\n if (minEnd < maxStart) {\n return perpPos(dragged) + perpSize(dragged) / 2;\n }\n return maxStart + (minEnd - maxStart) / 2;\n}\n","/**\n * Spatial index backed by an R-tree (rbush) — ported from v1 (audited CLEAN-PORT).\n * World-space AABBs for culling, hit-testing, and snap-candidate gathering\n * (Law 15: the ONLY hit path). The id↔entry map gives O(log n) removal by\n * passing rbush the exact stored reference (v1 \"Fix #8\").\n *\n * `rbush` is the kernel's single, named dependency exception: a zero-dep pure\n * data structure (see .dependency-cruiser.cjs).\n */\nimport RBushImport from \"rbush\";\nimport type { AABB } from \"./shapes\";\n\n// CJS/ESM interop — rbush exports differently depending on context (kept from v1).\ntype RBushModule = typeof RBushImport & { default?: typeof RBushImport };\nconst rbushModule = RBushImport as RBushModule;\nconst RBush = (\n typeof rbushModule.default === \"function\" ? rbushModule.default : RBushImport\n) as typeof RBushImport;\n\nexport interface SpatialEntry<Id> extends AABB {\n id: Id;\n}\n\nexport class SpatialIndex<Id> {\n private tree = new RBush<SpatialEntry<Id>>();\n private entries = new Map<Id, SpatialEntry<Id>>();\n\n upsert(id: Id, bounds: AABB): void {\n const existing = this.entries.get(id);\n if (existing) this.tree.remove(existing);\n const entry: SpatialEntry<Id> = { ...bounds, id };\n this.entries.set(id, entry);\n this.tree.insert(entry);\n }\n\n remove(id: Id): void {\n const existing = this.entries.get(id);\n if (existing) {\n this.tree.remove(existing);\n this.entries.delete(id);\n }\n }\n\n /** All entries intersecting the given AABB. */\n search(bounds: AABB): SpatialEntry<Id>[] {\n return this.tree.search(bounds);\n }\n\n /** Entries within `tolerance` of a point (z-priority is the caller's job). */\n searchPoint(x: number, y: number, tolerance = 0): SpatialEntry<Id>[] {\n return this.tree.search({\n minX: x - tolerance,\n minY: y - tolerance,\n maxX: x + tolerance,\n maxY: y + tolerance,\n });\n }\n\n clear(): void {\n this.tree.clear();\n this.entries.clear();\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n","/**\n * Hysteresis-banded zoom-resolution policy — ported from v1 `ZoomBands.ts`\n * (RFC-002). An island FBO is allocated at `widget bounds × dpr × band(zoom)`;\n * while zoom stays within `[band × 0.5, band × 2]` no repaint is needed.\n * Bands are powers of 2 (each covers a 4× display range).\n */\nexport const ZOOM_BANDS = [0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16] as const;\nconst MIN_BAND = 0.0625;\nconst MAX_BAND = 16;\n\n/** Smallest band ≥ zoom (clamped to the ladder). */\nexport function selectBand(zoom: number): number {\n if (zoom <= MIN_BAND) return MIN_BAND;\n if (zoom >= MAX_BAND) return MAX_BAND;\n for (const b of ZOOM_BANDS) {\n if (zoom <= b) return b;\n }\n return MAX_BAND;\n}\n\n/**\n * True when zoom left the `[band × 0.5, band × 2]` window of the band the\n * island was painted at. `paintedBand ≤ 0` = never painted (handled elsewhere).\n */\nexport function isOutOfBand(currentZoom: number, paintedBand: number): boolean {\n if (paintedBand <= 0) return false;\n const ratio = currentZoom / paintedBand;\n return ratio > 2 || ratio < 0.5;\n}\n\n/** FBO pixel size for a widget at a band (v1: `bounds × dpr × band`, min 1px). */\nexport function fboPixelSize(\n worldWidth: number,\n worldHeight: number,\n dpr: number,\n band: number,\n): { width: number; height: number } {\n return {\n width: Math.max(1, Math.round(worldWidth * dpr * band)),\n height: Math.max(1, Math.round(worldHeight * dpr * band)),\n };\n}\n","/**\n * FBO-pool eviction policy — ported from v1 `eviction.ts` (pure part of the\n * pool). Priority: Cold(0) < Warm(1) < Dormant(2); LRU within a phase;\n * Hot/Waking are never evicted even under an impossible budget.\n */\nexport type IslandPhase = \"Hot\" | \"Warm\" | \"Waking\" | \"Cold\" | \"Dormant\";\n\nexport interface EvictionCandidate<Id> {\n id: Id;\n phase: IslandPhase;\n bytes: number;\n lastUsedMs: number;\n}\n\nconst PHASE_PRIORITY: Record<IslandPhase, number> = {\n Cold: 0,\n Warm: 1,\n Dormant: 2,\n Waking: Number.POSITIVE_INFINITY,\n Hot: Number.POSITIVE_INFINITY,\n};\n\n/**\n * The island phase truth table (design-004 §3; v1 `computePhase` verbatim —\n * RFC-002 pinned it with tests). `active` is the nested-canvas membership\n * signal (always true until M8); `hasFbo` splits Warm (retained texture,\n * composite-ready) from Waking (must paint before compositing) — after an\n * eviction a re-entering island comes back as Waking, after a plain cull\n * round-trip it comes back Warm from the retained texture.\n */\nexport function computeIslandPhase(\n active: boolean,\n visible: boolean,\n animating: boolean,\n hasFbo: boolean,\n): IslandPhase {\n if (!active) return \"Dormant\";\n if (visible) {\n if (animating) return \"Hot\";\n return hasFbo ? \"Warm\" : \"Waking\";\n }\n // Culled OR not yet culled-classified: treat as Cold — never paint a widget\n // the engine hasn't placed in the viewport yet (v1 rule, kept).\n return \"Cold\";\n}\n\n/** Ordered ids to release so total bytes ≤ budget. Pure. */\nexport function selectEvictions<Id>(\n candidates: EvictionCandidate<Id>[],\n totalBytes: number,\n maxBytes: number,\n): Id[] {\n if (totalBytes <= maxBytes) return [];\n\n const eligible = candidates.filter((c) => Number.isFinite(PHASE_PRIORITY[c.phase]));\n eligible.sort((a, b) => {\n const p = PHASE_PRIORITY[a.phase] - PHASE_PRIORITY[b.phase];\n if (p !== 0) return p;\n return a.lastUsedMs - b.lastUsedMs;\n });\n\n const toEvict: Id[] = [];\n let remaining = totalBytes;\n for (const c of eligible) {\n if (remaining <= maxBytes) break;\n toEvict.push(c.id);\n remaining -= c.bytes;\n }\n return toEvict;\n}\n","/**\n * Port-anchor math (design-001 §5.3): wire geometry NEVER reads port entities —\n * anchors are pure functions over the widget rect + the port declaration, so\n * culled endpoints work by construction.\n */\nimport type { Rect, Vec2 } from \"./shapes\";\n\nexport type PortSide = \"n\" | \"e\" | \"s\" | \"w\";\n\nexport interface PortSlot {\n side: PortSide;\n /** 0-based position among the ports sharing this side. */\n index: number;\n /** Total ports on this side. */\n count: number;\n}\n\n/** World-space anchor point: ports distribute evenly along their side. */\nexport function portAnchor(widget: Rect, slot: PortSlot): Vec2 {\n const f = (slot.index + 1) / (slot.count + 1);\n switch (slot.side) {\n case \"n\":\n return { x: widget.x + widget.width * f, y: widget.y };\n case \"s\":\n return { x: widget.x + widget.width * f, y: widget.y + widget.height };\n case \"w\":\n return { x: widget.x, y: widget.y + widget.height * f };\n case \"e\":\n return { x: widget.x + widget.width, y: widget.y + widget.height * f };\n }\n}\n\n/** Outward unit direction of a side (wire tangent stubs, Y-down world). */\nexport function sideDirection(side: PortSide): Vec2 {\n switch (side) {\n case \"n\":\n return { x: 0, y: -1 };\n case \"s\":\n return { x: 0, y: 1 };\n case \"w\":\n return { x: -1, y: 0 };\n case \"e\":\n return { x: 1, y: 0 };\n }\n}\n","/**\n * Cubic-bezier wire geometry: construction from port anchors, conservative\n * AABB (for the spatial index), and sampled distance (for picking — wires rank\n * below widgets, design-003 §3).\n */\nimport { sideDirection, type PortSide } from \"./anchors\";\nimport type { AABB, Vec2 } from \"./shapes\";\n\nexport interface Cubic {\n x0: number;\n y0: number;\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n x3: number;\n y3: number;\n}\n\n/**\n * Wire curve between two anchors: tangent stubs leave each port along its\n * side's outward direction, scaled with distance (min stub keeps short wires\n * readable).\n */\nexport function wireCubic(\n from: Vec2,\n fromSide: PortSide,\n to: Vec2,\n toSide: PortSide,\n stubScale = 0.4,\n minStub = 24,\n): Cubic {\n const dist = Math.hypot(to.x - from.x, to.y - from.y);\n const stub = Math.max(minStub, dist * stubScale);\n const df = sideDirection(fromSide);\n const dt = sideDirection(toSide);\n return {\n x0: from.x,\n y0: from.y,\n x1: from.x + df.x * stub,\n y1: from.y + df.y * stub,\n x2: to.x + dt.x * stub,\n y2: to.y + dt.y * stub,\n x3: to.x,\n y3: to.y,\n };\n}\n\n/** Point on the curve at t ∈ [0, 1] (Bernstein form). */\nexport function cubicPoint(c: Cubic, t: number): Vec2 {\n const u = 1 - t;\n const b0 = u * u * u;\n const b1 = 3 * u * u * t;\n const b2 = 3 * u * t * t;\n const b3 = t * t * t;\n return {\n x: b0 * c.x0 + b1 * c.x1 + b2 * c.x2 + b3 * c.x3,\n y: b0 * c.y0 + b1 * c.y1 + b2 * c.y2 + b3 * c.y3,\n };\n}\n\n/**\n * Conservative bounds: the control-point hull contains the curve (convex-hull\n * property). Cheap and index-friendly; exact extrema are unnecessary for\n * culling/picking prefilters.\n */\nexport function cubicAABB(c: Cubic): AABB {\n return {\n minX: Math.min(c.x0, c.x1, c.x2, c.x3),\n minY: Math.min(c.y0, c.y1, c.y2, c.y3),\n maxX: Math.max(c.x0, c.x1, c.x2, c.x3),\n maxY: Math.max(c.y0, c.y1, c.y2, c.y3),\n };\n}\n\n/** Distance from a point to a line segment. */\nexport function distanceToSegment(\n px: number,\n py: number,\n ax: number,\n ay: number,\n bx: number,\n by: number,\n): number {\n const dx = bx - ax;\n const dy = by - ay;\n const lenSq = dx * dx + dy * dy;\n let t = 0;\n if (lenSq > 0) {\n t = ((px - ax) * dx + (py - ay) * dy) / lenSq;\n t = Math.max(0, Math.min(1, t));\n }\n const cx = ax + t * dx;\n const cy = ay + t * dy;\n return Math.hypot(px - cx, py - cy);\n}\n\n/**\n * Min distance from a point to the curve via polyline sampling. 32 samples is\n * ample for hit thresholds of a few px; error shrinks with curve flatness.\n */\nexport function distanceToCubic(px: number, py: number, c: Cubic, samples = 32): number {\n let best = Number.POSITIVE_INFINITY;\n let prev = cubicPoint(c, 0);\n for (let i = 1; i <= samples; i++) {\n const cur = cubicPoint(c, i / samples);\n const d = distanceToSegment(px, py, prev.x, prev.y, cur.x, cur.y);\n if (d < best) best = d;\n prev = cur;\n }\n return best;\n}\n","/**\n * @ice/kernel — pure math. Plain structs in, plain structs out.\n * Import wall: nothing but `rbush` (named exception — zero-dep R-tree).\n */\nexport const KERNEL_VERSION = \"0.0.0\";\n\nexport * from \"./shapes\";\nexport * from \"./coords\";\nexport * from \"./snap\";\nexport * from \"./spatial-index\";\nexport * from \"./zoom-bands\";\nexport * from \"./eviction\";\nexport * from \"./anchors\";\nexport * from \"./bezier\";\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../dom/src/host.ts","../../dom/src/planes.ts","../../dom/src/reflectors/plane-transform.ts","../../dom/src/reflectors/graybox.ts","../../dom/src/reflectors/dom-widgets.ts","../../dom/src/loop.ts","../../dom/src/pointer-adapter.ts","../../dom/src/reflectors/cursor.ts","../../dom/src/reflectors/remote-cursors.ts","../../dom/src/reflectors/grid.ts","../../dom/src/reflectors/wires.ts","../../dom/src/reflectors/chrome.ts","../../dom/src/measure-adapter.ts","../../dom/src/measure-wiring.ts","../../dom/src/index.ts"],"sourcesContent":["/**\n * The DOM canvas host (design-004 §1, host pipeline — M3 slice).\n *\n * M3 stands up ONE content plane: an absolutely-positioned div whose single CSS\n * transform (written by the plane-transform reflector) applies the camera to\n * every world-positioned child at once (design-002 §5 `planeTransform`; kernel\n * `planeCssTransform`). The full six-plane model (background/content/overlay/\n * chrome/HUD/nav + the FBO compositor) arrives in M6 — this file is deliberately\n * the minimum that lets a reflector paint into a camera-transformed space.\n *\n * The container is styled to be a stable, gesture-clean viewport: `relative` so\n * the absolute plane anchors to it, `overflow:hidden` to clip the infinite\n * plane, `touch-action:none` + `user-select:none` so browser scroll/zoom and\n * text selection never fight the interaction stack.\n */\n\nexport interface CanvasHost {\n /** The app-provided viewport element (styled, not created, by the host). */\n readonly container: HTMLElement;\n /** The single camera-transformed plane; world-positioned children mount here. */\n readonly contentPlane: HTMLDivElement;\n /** Remove the plane and clear the inline styles the host wrote. */\n dispose(): void;\n}\n\n/** The container styles the host owns unconditionally (cleared on dispose). */\nconst CONTAINER_STYLE: Readonly<Record<string, string>> = {\n overflow: \"hidden\",\n touchAction: \"none\",\n userSelect: \"none\",\n};\n\n/** The content-plane styles: origin-anchored, transform-ready (design-002 §5). */\nconst PLANE_STYLE: Readonly<Record<string, string>> = {\n position: \"absolute\",\n left: \"0\",\n top: \"0\",\n transformOrigin: \"0 0\",\n willChange: \"transform\",\n};\n\nexport function createCanvasHost(container: HTMLElement): CanvasHost {\n // The plane needs the container to be a POSITIONED containing block — but\n // `absolute`/`fixed` already qualify, so only promote a `static` container\n // to `relative`. Stomping an app's `position: absolute` with inline\n // `relative` collapses the common `#app { position: absolute; inset: 0 }`\n // sizing pattern to zero height (inset offsets a relative box, it does not\n // size it), and the host's own overflow:hidden then clips everything.\n // Inline style first (authoritative and environment-independent), computed\n // second (catches stylesheet rules like `#app { position: absolute }`).\n const view = container.ownerDocument.defaultView;\n const position =\n container.style.position !== \"\"\n ? container.style.position\n : (view?.getComputedStyle(container).position ?? \"\");\n const promoteToRelative = position === \"\" || position === \"static\";\n if (promoteToRelative) container.style.position = \"relative\";\n Object.assign(container.style, CONTAINER_STYLE);\n\n const contentPlane = container.ownerDocument.createElement(\"div\");\n Object.assign(contentPlane.style, PLANE_STYLE);\n container.appendChild(contentPlane);\n\n return {\n container,\n contentPlane,\n dispose() {\n contentPlane.remove();\n if (promoteToRelative) container.style.removeProperty(\"position\");\n for (const prop of Object.keys(CONTAINER_STYLE)) {\n container.style.removeProperty(\n // camelCase → kebab-case for removeProperty (touchAction → touch-action).\n prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`),\n );\n }\n },\n };\n}\n","/**\n * The lifted plane (design-004 §1, planes P1/P3 — M6 slice).\n *\n * M3 stood up ONE camera-transformed content plane (`host.ts`, P1). M6 adds P3,\n * the **lifted** plane: a second transform-origin-0-0 div appended to the\n * container AFTER the content plane, so it stacks ABOVE it (P3 over P1) with no\n * z-index bookkeeping — DOM order is the strata order (§1: \"stacking is\n * stratified\"). Both planes take the SAME single camera transform (the\n * plane-transform reflector writes both), so a promoted widget re-parented\n * content→lifted keeps its world position; it just paints above the content\n * layer for the duration of the drag.\n *\n * `createCanvasHost` is intentionally untouched: the content plane it owns is\n * the P1 authority. `createPlanes` is additive — it borrows that content plane\n * and mints the lifted sibling next to it.\n */\nimport type { CanvasHost } from \"./host\";\n\n/** The two DOM planes M6 addresses: P1 content (host-owned) + P3 lifted. */\nexport interface Planes {\n /** P1: the host's camera-transformed content plane; world-positioned hosts mount here. */\n readonly content: HTMLDivElement;\n /** P3: camera-transformed, stacked above content; promoted (dragged) hosts re-parent here. */\n readonly lifted: HTMLDivElement;\n /** Remove the lifted plane (the content plane is the host's — host.dispose reaps it). */\n dispose(): void;\n}\n\n/**\n * The lifted-plane styles — identical to the content plane's (origin-anchored,\n * transform-ready). Re-declared here rather than reaching into `host.ts` so that\n * file stays the sole owner of the content plane and nothing it exports moves.\n */\nconst LIFTED_PLANE_STYLE: Readonly<Record<string, string>> = {\n position: \"absolute\",\n left: \"0\",\n top: \"0\",\n transformOrigin: \"0 0\",\n willChange: \"transform\",\n};\n\n/**\n * Mint the lifted plane next to the host's content plane. The lifted div is\n * appended to the container AFTER the content plane, so it renders above it\n * (P3 > P1). Returns both planes for the plane-transform reflector (writes both)\n * and the dom-widgets reflector (re-parents hosts between them on drag-promote).\n */\nexport function createPlanes(host: CanvasHost): Planes {\n const lifted = host.container.ownerDocument.createElement(\"div\");\n Object.assign(lifted.style, LIFTED_PLANE_STYLE);\n host.container.appendChild(lifted); // after contentPlane ⇒ P3 stacks above P1\n return {\n content: host.contentPlane,\n lifted,\n // Without this a React StrictMode remount stacks a second P3\n // (2026-07-11 reflector-teardown sweep, with the grid-canvas fix).\n dispose() {\n lifted.remove();\n },\n };\n}\n","/**\n * The plane-transform reflector (design-002 §5 `planeTransform`; M3 exit gate,\n * widened for the M6 lifted plane).\n *\n * Observes the `Camera` RESOURCE and nothing else. Each flush reads the camera,\n * derives the ONE per-plane CSS transform via kernel `planeCssTransform` (the\n * sole owner of that math — Law 13), and writes it to EVERY camera-transformed\n * DOM plane in a SINGLE `style.transform` assignment each. Panning the whole\n * canvas is therefore O(1) in the DOM: one transform write per plane per moving\n * frame, independent of node count — the property that makes an infinite canvas\n * of 10k gray boxes pan at frame rate.\n *\n * The input is widened to `{ contentPlane; liftedPlane? }` (design-004 §1: P1 +\n * P3 share the one camera). A `CanvasHost` still satisfies it structurally, so\n * M3 callers are unchanged; M6 passes both planes and the lifted plane (P3)\n * tracks the content plane (P1) exactly, so a promoted widget keeps its world\n * position across the content→lifted re-parent.\n *\n * `transformWrites()` is the M3 instrument: because the reflector is dirty-\n * driven on the Camera resource (equality-suppressed — design-002 §5), a static\n * camera flushes zero times and the counter stays flat; every distinct camera\n * value bumps it exactly once. The frame-contract tests assert both halves.\n *\n * Law 10: reflectors run post-notify, write output only, and NEVER read layout\n * or write ECS — this flush touches only `host.contentPlane.style`.\n */\nimport { Camera, type ReflectorDef, type World } from \"@ice/core\";\nimport { planeCssTransform } from \"@ice/kernel\";\n\n/**\n * The camera-transformed DOM plane(s). A `CanvasHost` satisfies this\n * structurally (M3 backward compat); M6 passes `liftedPlane` too so P3 tracks P1.\n */\nexport interface CameraPlanes {\n readonly contentPlane: HTMLElement;\n readonly liftedPlane?: HTMLElement;\n}\n\nexport function createPlaneTransformReflector(\n host: CameraPlanes,\n): ReflectorDef & { transformWrites(): number } {\n let writes = 0;\n return {\n name: \"plane-transform\",\n observe: { resources: [Camera] },\n flush(world: World) {\n const camera = world.getResource(Camera);\n if (camera === undefined) return;\n const { tx, ty, scale } = planeCssTransform(camera);\n const transform = `translate(${tx}px, ${ty}px) scale(${scale})`;\n host.contentPlane.style.transform = transform;\n if (host.liftedPlane !== undefined) host.liftedPlane.style.transform = transform;\n writes++; // one flush per distinct camera value, regardless of plane count\n },\n transformWrites: () => writes,\n };\n}\n","/**\n * The gray-box reflector (design-002 §5 reflector doctrine; M3 exit gate).\n *\n * A minimal enter/update/exit reconciler: it mirrors every `Position` + `Size`\n * entity to an absolutely-positioned div inside the content plane, laying each\n * out in WORLD units (`left/top/width/height` in world coordinates) — the\n * plane's single camera transform (see plane-transform.ts) does the screen\n * mapping, so this reflector never touches the camera and never reads layout\n * (Law 10). It is the stand-in for real widget rendering until the widget\n * runtime lands (M4+); \"gray box\" = deterministic per-entity fill, no content.\n *\n * Reconciliation, dirty-driven on the `Position`/`Size` query (design-002 §5):\n * - enter: create a div (fill hashed from the entity handle), write geometry,\n * stage it in a DocumentFragment (one plane append per flush — 10k mounts do\n * not thrash layout);\n * - update: compare against a per-entity geometry cache and rewrite ONLY the\n * entities whose world geometry actually changed;\n * - exit: remove divs for entities absent from this flush.\n *\n * `styleWrites()` counts entities whose geometry was (re)written this flush (one\n * per enter, one per changed entity, zero for unchanged) — the churn instrument\n * the M3 tests assert: change one entity ⇒ exactly one style write. `nodeCount()`\n * is the live div count. Because the reflector is dirty-driven, a static frame\n * does not flush at all, so neither counter moves.\n */\nimport { defineQuery, type Entity, Position, type ReflectorDef, Size, type World } from \"@ice/core\";\nimport type { CanvasHost } from \"../host\";\n\nconst grayboxQuery = defineQuery([Position, Size]);\n\ninterface Geom {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/** Deterministic fill from the entity handle (a packed u32) — stable across flushes. */\nfunction fillFor(entity: Entity): string {\n const n: number = entity;\n const hash = (Math.imul(n, 2654435761) >>> 0) % 360;\n return `hsl(${hash}, 55%, 55%)`;\n}\n\nfunction writeGeom(div: HTMLDivElement, x: number, y: number, w: number, h: number): void {\n div.style.left = `${x}px`;\n div.style.top = `${y}px`;\n div.style.width = `${w}px`;\n div.style.height = `${h}px`;\n}\n\nexport function createGrayboxReflector(\n host: CanvasHost,\n): ReflectorDef & { styleWrites(): number; nodeCount(): number } {\n const divs = new Map<Entity, HTMLDivElement>();\n const geom = new Map<Entity, Geom>();\n const seen = new Set<Entity>();\n const doc = host.container.ownerDocument;\n let styleWrites = 0;\n\n return {\n name: \"graybox\",\n observe: { queries: [{ q: grayboxQuery, cols: [Position, Size] }] },\n flush(world: World) {\n seen.clear();\n let fragment: DocumentFragment | null = null;\n\n world.query(grayboxQuery).each((batch) => {\n const px = batch.col(Position).x;\n const py = batch.col(Position).y;\n const sw = batch.col(Size).w;\n const sh = batch.col(Size).h;\n for (const r of batch) {\n const entity = batch.entity(r);\n seen.add(entity);\n const x = px[r] as number;\n const y = py[r] as number;\n const w = sw[r] as number;\n const h = sh[r] as number;\n\n const existing = divs.get(entity);\n if (existing === undefined) {\n const div = doc.createElement(\"div\");\n div.style.position = \"absolute\";\n div.style.background = fillFor(entity);\n writeGeom(div, x, y, w, h);\n styleWrites++;\n divs.set(entity, div);\n geom.set(entity, { x, y, w, h });\n if (fragment === null) fragment = doc.createDocumentFragment();\n fragment.appendChild(div);\n continue;\n }\n\n const prev = geom.get(entity);\n if (prev === undefined) {\n writeGeom(existing, x, y, w, h);\n styleWrites++;\n geom.set(entity, { x, y, w, h });\n } else if (prev.x !== x || prev.y !== y || prev.w !== w || prev.h !== h) {\n writeGeom(existing, x, y, w, h);\n styleWrites++;\n prev.x = x;\n prev.y = y;\n prev.w = w;\n prev.h = h;\n }\n }\n });\n\n if (fragment !== null) host.contentPlane.appendChild(fragment);\n\n for (const [entity, div] of divs) {\n if (seen.has(entity)) continue;\n div.remove();\n divs.delete(entity);\n geom.delete(entity);\n }\n },\n styleWrites: () => styleWrites,\n nodeCount: () => divs.size,\n };\n}\n","/**\n * The DOM widget HOST reflector (design-004 §2 host pipeline; design-002 §5\n * `domWidgets`).\n *\n * This is the reflector-owned HALF of the widget runtime: it reconciles a host\n * `<div>` per mounted widget against the engine's mount store and paints its\n * world-unit geometry. The OTHER half — the React portal list — is owned by a\n * hook subscribed to the SAME store (design-004 §2, decision 3); this file never\n * touches React. Each host carries an inner `data-ice-content` element that the\n * portal targets, so the two halves meet only at that node.\n *\n * Store, not query: membership (enter/exit) and the culled-but-kept-mounted\n * `hidden` flag are ENGINE-side decisions (cull + keep-mounted LRU, mount-store.ts)\n * published through a `useSyncExternalStore` snapshot. That store has NO ECS stamp\n * for a reflector `observe` to watch, so — like the cursor reflector — this one is\n * `always: true` and does its own cheap dirt detection:\n * - **membership**: `store.getSnapshot()` identity is O(1); the snapshot changes\n * IFF membership or a hidden flag changed, so an unchanged snapshot skips the\n * reconcile entirely;\n * - **geometry**: a private observer on `[Position, Size]` sets a dirty flag, so\n * the (change-only) geometry pass runs only when a widget actually moved/resized\n * — a still scene touches no DOM. Effective size is `MeasuredSize` where present\n * and non-zero, else `Size` (design-004 §2). (MeasuredSize changes ride a\n * dedicated observer; the note below is historical:\n * picked up on the next Position/Size stamp or membership change; the ResizeObserver\n * measurement path — a later slice — will arm its own dirt.)\n * - **drag-promote** (P3): a private observer on `[Grab]` membership sets a dirty\n * flag; a host whose entity holds `Grab` re-parents content→lifted plane and back\n * on release. Re-parenting MOVES the host node (with its content child), which\n * PRESERVES the React portal — the portal targets the content node, and moving a\n * portal's container node does not remount it, so widget React state survives the\n * lift. While ANY host is lifted, every host's content element is inerted\n * (`pointer-events: none`, the pinned inert-during-drag contract, design-004 §4).\n *\n * Law 10: reflectors run post-notify, write output only, never read layout or\n * write ECS — this flush touches only host `<div>` style/attributes/parentage.\n */\nimport {\n Grab,\n MeasuredSize,\n Position,\n Size,\n defineQuery,\n type Entity,\n type MountEntry,\n type ReflectorDef,\n type WidgetMountStore,\n type World,\n} from \"@ice/core\";\n\n/** The two planes a host can live in (design-004 §1: P1 content, P3 lifted). */\nexport interface DomWidgetsHost {\n readonly contentPlane: HTMLElement;\n readonly liftedPlane: HTMLElement;\n}\n\ninterface HostRec {\n /** The absolute-positioned, world-unit host div; re-parents content↔lifted on promote. */\n readonly host: HTMLDivElement;\n /** The `data-ice-content` portal target (React mounts INTO this). */\n readonly content: HTMLDivElement;\n // Geometry cache — the change-only guard (graybox pattern).\n x: number;\n y: number;\n w: number;\n h: number;\n /** Last-applied `display:none` (culled-but-kept-mounted) state. */\n hidden: boolean;\n /** Last-applied parent: true = lifted plane, false = content plane. */\n lifted: boolean;\n}\n\ninterface Geom {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nconst geometryQuery = defineQuery([Position, Size]);\nconst measuredQuery = defineQuery([MeasuredSize]);\nconst grabQuery = defineQuery([Grab]);\n\nfunction writeGeom(el: HTMLDivElement, x: number, y: number, w: number, h: number): void {\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n el.style.width = `${w}px`;\n el.style.height = `${h}px`;\n}\n\nexport interface DomWidgetsReflector extends ReflectorDef {\n /** The entity's content element (the `data-ice-content` portal target), or undefined if not hosted. */\n hostFor(entity: Entity): HTMLElement | undefined;\n /** Live host count (= mounted widgets, visible + kept-mounted). */\n hostCount(): number;\n /** Geometry writes so far — one per enter, one per changed host, zero for unchanged (churn instrument). */\n geometryWrites(): number;\n /** Tear down the private observers (call when unregistering the reflector before the world dies). */\n dispose(): void;\n}\n\nexport function createDomWidgetsReflector(\n host: DomWidgetsHost,\n world: World,\n store: WidgetMountStore,\n): DomWidgetsReflector {\n const doc = host.contentPlane.ownerDocument;\n const hosts = new Map<Entity, HostRec>();\n let lastSnapshot: readonly MountEntry[] | undefined;\n let geometryWrites = 0;\n /** True while any host is lifted → all content is inerted (pinned drag contract). */\n let inert = false;\n\n // Private dirt flags — the store carries no ECS stamp, so geometry/promote work\n // is gated by observers rather than the registry's (unused under `always`) dirt.\n let geometryDirty = false;\n let promoteDirty = false;\n const unsubs: Array<() => void> = [\n world.reactive.observeQuery(geometryQuery, () => { geometryDirty = true; }, { cols: [Position, Size] }),\n // MeasuredSize rides its own observer: adding it to geometryQuery's cols\n // would require it in the query and drop widgets without the rider.\n world.reactive.observeQuery(measuredQuery, () => { geometryDirty = true; }, { cols: [MeasuredSize] }),\n world.reactive.observeQuery(grabQuery, () => { promoteDirty = true; }, { cols: [] }),\n ];\n\n function readGeom(e: Entity): Geom {\n const p = world.get(e, Position);\n const measured = world.get(e, MeasuredSize);\n const s = measured !== undefined && measured.w > 0 ? measured : world.get(e, Size);\n return { x: p?.x ?? 0, y: p?.y ?? 0, w: s?.w ?? 0, h: s?.h ?? 0 };\n }\n\n function createHost(e: Entity): HostRec {\n const el = doc.createElement(\"div\");\n el.style.position = \"absolute\";\n el.setAttribute(\"data-ice-entity\", String(e));\n\n const content = doc.createElement(\"div\");\n content.setAttribute(\"data-ice-content\", \"\");\n content.style.width = \"100%\";\n content.style.height = \"100%\";\n el.appendChild(content);\n\n const g = readGeom(e);\n writeGeom(el, g.x, g.y, g.w, g.h);\n geometryWrites++;\n const lifted = world.has(e, Grab);\n (lifted ? host.liftedPlane : host.contentPlane).appendChild(el);\n return { host: el, content, x: g.x, y: g.y, w: g.w, h: g.h, hidden: false, lifted };\n }\n\n /** Enter/exit/hidden reconcile against the store snapshot. Returns whether membership changed. */\n function reconcile(snapshot: readonly MountEntry[]): boolean {\n let membershipChanged = false;\n const present = new Set<Entity>();\n for (const entry of snapshot) {\n present.add(entry.entity);\n let rec = hosts.get(entry.entity);\n if (rec === undefined) {\n rec = createHost(entry.entity);\n hosts.set(entry.entity, rec);\n membershipChanged = true;\n }\n if (entry.hidden !== rec.hidden) {\n rec.hidden = entry.hidden;\n rec.host.style.display = entry.hidden ? \"none\" : \"\"; // cull ≠ unmount\n }\n }\n for (const [e, rec] of hosts) {\n if (present.has(e)) continue;\n rec.host.remove(); // React unmounts via the store; the host div goes too\n hosts.delete(e);\n membershipChanged = true;\n }\n return membershipChanged;\n }\n\n /** Change-only geometry rewrite over the live hosts (graybox pattern). */\n function updateGeometry(): void {\n for (const [e, rec] of hosts) {\n const g = readGeom(e);\n if (g.x !== rec.x || g.y !== rec.y || g.w !== rec.w || g.h !== rec.h) {\n writeGeom(rec.host, g.x, g.y, g.w, g.h);\n rec.x = g.x;\n rec.y = g.y;\n rec.w = g.w;\n rec.h = g.h;\n geometryWrites++;\n }\n }\n }\n\n /** Re-parent promoted hosts content↔lifted and toggle the global inert state. */\n function updatePromote(): void {\n let anyLifted = false;\n for (const [e, rec] of hosts) {\n const shouldLift = world.has(e, Grab);\n if (shouldLift) anyLifted = true;\n if (shouldLift !== rec.lifted) {\n rec.lifted = shouldLift;\n // appendChild MOVES the existing node between planes — the React portal\n // (targeting the content child) survives the move.\n (shouldLift ? host.liftedPlane : host.contentPlane).appendChild(rec.host);\n }\n }\n if (anyLifted !== inert) {\n inert = anyLifted;\n // PLANE-LEVEL inert (review finding — design-004 §1/§4 contract): two\n // writes cover every widget, including the lifted one and any host that\n // enters mid-drag; events fall through to the container, so canvas\n // facts keep flowing while content handlers go quiet.\n host.contentPlane.style.pointerEvents = inert ? \"none\" : \"\";\n host.liftedPlane.style.pointerEvents = inert ? \"none\" : \"\";\n }\n }\n\n return {\n name: \"domWidgets\",\n always: true, // store membership has no ECS stamp; the flush self-gates cheaply\n flush(_world: World) {\n const snapshot = store.getSnapshot();\n let membershipChanged = false;\n if (snapshot !== lastSnapshot) {\n lastSnapshot = snapshot;\n membershipChanged = reconcile(snapshot);\n }\n if (geometryDirty) {\n geometryDirty = false;\n updateGeometry();\n }\n // Membership changes can add/remove a lifted host → recompute inert even\n // without a Grab stamp (e.g. a lifted widget despawns mid-drag).\n if (promoteDirty || membershipChanged) {\n promoteDirty = false;\n updatePromote();\n }\n },\n hostFor: (entity) => hosts.get(entity)?.content,\n hostCount: () => hosts.size,\n geometryWrites: () => geometryWrites,\n dispose() {\n for (const u of unsubs) u();\n unsubs.length = 0;\n },\n };\n}\n","/**\n * The rAF frame loop (design-002 §1: core is scheduler-free — the DOM package\n * owns the loop; core never touches the platform).\n *\n * Drives `engine.step(now)` once per animation frame, threading the rAF\n * timestamp straight into the frame clock (so FrameInfo.dt is real wall time,\n * clamped in core). The returned stop fn cancels the pending frame AND latches\n * so a frame already in flight cannot re-schedule — calling it is idempotent.\n */\nimport type { Engine } from \"@ice/core\";\n\nexport function startRafLoop(engine: Engine): () => void {\n let handle = 0;\n let stopped = false;\n\n const tick = (now: number): void => {\n if (stopped) return;\n engine.step(now);\n handle = requestAnimationFrame(tick);\n };\n\n handle = requestAnimationFrame(tick);\n\n return () => {\n stopped = true;\n cancelAnimationFrame(handle);\n };\n}\n","/**\n * The real L0 pointer adapter (design-003 §2–§3; replaces the M3 demo-input scaffold).\n *\n * Translates DOM pointer / wheel / keyboard events into normalized `InputEvent`s\n * and ONLY enqueues them (Law 2: adapters enqueue, they never touch the world —\n * this file imports no ECS state and writes nothing). `pointerIngest` drains the\n * queue once per tick and turns the facts into world state.\n *\n * Details that matter:\n * - stable pointer ids: `\"mouse\"` for the one mouse, `\"touch:<pointerId>\"` per\n * contact, `\"pen\"` — ingest keys pointer entities off these (design-003 §2);\n * - container-relative CSS px (the kernel screen space) via getBoundingClientRect;\n * - `setPointerCapture` on down so a drag keeps delivering outside the element;\n * - Space is the pan modifier (design-003 §4.4): tracked here and threaded onto\n * every event's `mods.space`; keydown/keyup emit a `key` fact only when the\n * modifier tuple actually changes, and Space `preventDefault`s to stop page scroll;\n * - wheel `preventDefault`s: a ctrl-wheel / trackpad pinch becomes `wheel.pinch`\n * (the zoom signal), a plain wheel becomes dx/dy (deltaMode lines → px ×16);\n * - window blur enqueues a synthetic `cancel` for every pointer still down\n * (design-003 §8) — the browser will not send the pointerups.\n *\n * Widget opt-out (the pinned widget-event contract, design-002 §8 / design-004 §4):\n * on `pointerdown` ONLY, if the event's target chain (up to the container) crosses\n * a native interactive (input/textarea/select/button/a[href]/contenteditable/\n * media[controls]) or an explicit `[data-canvas-interactive]`, the down fact is\n * flagged `surfaceHandled: true` — the fact still lands (the one input path is\n * absolute), but recognizer-spawn/arbitration skip it (`HandledByWidget` flows\n * through L0). `stopPropagation()` inside widget content is the OTHER boundary:\n * a stopped event never reaches the container listener, so it never becomes a\n * canvas fact at all — that is the design, not a gap.\n */\nimport { NO_MODS, type InputEvent, type InputMods, type InputQueue } from \"@ice/core\";\nimport type { CanvasHost } from \"./host\";\n\n/**\n * The router GL path's adapter seam (design-004 §4). The GL plane is\n * `pointer-events: none` — its events land HERE, and the injected router\n * (built by @ice/r3f; the adapter stays ECS-free) performs the synchronous\n * point-pick + island raycast + synthetic dispatch at event time. Returning\n * `true` means island content claimed the event (stopPropagation'd or held\n * under island capture): the fact still lands — flagged `surfaceHandled`, so\n * recognizers skip it via `HandledByWidget` — the same contract as the\n * native-interactive opt-out.\n */\nexport type GLRoute = (\n kind: \"down\" | \"move\" | \"up\" | \"cancel\",\n screenX: number,\n screenY: number,\n native: PointerEvent,\n) => boolean;\n\nexport interface PointerAdapterOpts {\n readonly glRoute?: GLRoute;\n}\n\n/** DOM_DELTA_LINE → px (one wheel \"line\" ≈ 16px; matches typical browser mapping). */\nconst WHEEL_LINE_PX = 16;\n\n/**\n * Elements whose `pointerdown` is the widget's, not the canvas's (design-002 §8).\n * `contenteditable=\"\"` (bare attribute) and `=\"true\"` are editable; `=\"false\"` is\n * not. Media only opts out WITH visible controls.\n */\nconst INTERACTIVE_SELECTOR =\n 'input, textarea, select, button, a[href], [contenteditable=\"\"], [contenteditable=\"true\"], audio[controls], video[controls], [data-canvas-interactive]';\n\n/** True if `target`'s ancestor chain, up to but excluding `container`, holds an interactive surface. */\nfunction crossesInteractive(target: EventTarget | null, container: HTMLElement): boolean {\n if (!(target instanceof Element)) return false;\n const match = target.closest(INTERACTIVE_SELECTOR);\n // `closest` walks to the document root; bound it to inside the canvas so an\n // interactive ANCESTOR of the container (unusual, but legal) does not count.\n return match !== null && container.contains(match);\n}\n\nfunction pointerIdOf(e: PointerEvent): string {\n if (e.pointerType === \"touch\") return `touch:${e.pointerId}`;\n if (e.pointerType === \"pen\") return \"pen\";\n return \"mouse\";\n}\n\nfunction deviceOf(e: PointerEvent): InputEvent[\"device\"] {\n if (e.pointerType === \"touch\") return \"touch\";\n if (e.pointerType === \"pen\") return \"pen\";\n return \"mouse\";\n}\n\nexport function attachPointerAdapter(\n host: CanvasHost,\n queue: InputQueue,\n opts: PointerAdapterOpts = {},\n): () => void {\n const { container } = host;\n const view = container.ownerDocument.defaultView;\n const { glRoute } = opts;\n\n let spaceHeld = false;\n // Pointers currently down — blur-cancel sweep + DEFERRED-CAPTURE state.\n // Capture timing (field report 2026-07-12, v1's click-vs-drag discrimination):\n // capturing on DOWN retargets the derived `click`/`dblclick` to the container,\n // killing every widget-body handler (todo rows, folder double-click). So:\n // - GL-claimed downs capture IMMEDIATELY (the router synthesizes its own\n // clicks; island drags must keep delivering outside the element);\n // - native-interactive downs NEVER capture (the engine ignores them);\n // - everything else captures on the FIRST held-button move past a 4px slop\n // — below every recognizer dead zone, so a drag is always captured\n // before it can activate, while a clean tap never captures at all.\n const live = new Map<\n string,\n {\n device: InputEvent[\"device\"];\n x: number;\n y: number;\n downX: number;\n downY: number;\n native: boolean;\n captured: boolean;\n }\n >();\n const CAPTURE_SLOP_PX = 4;\n\n const capture = (pointerId: number): void => {\n if (typeof container.setPointerCapture !== \"function\") return;\n try {\n container.setPointerCapture(pointerId);\n } catch {\n // capture is best-effort — a detached or unsupported target must not throw here.\n }\n };\n // Last emitted keyboard tuple — emit a `key` fact only when it actually changes.\n let lastMods = { shift: false, ctrl: false, alt: false, meta: false, space: false };\n\n const relative = (clientX: number, clientY: number): { x: number; y: number } => {\n const rect = container.getBoundingClientRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n };\n\n const pointerMods = (e: PointerEvent | WheelEvent): InputMods => ({\n shift: e.shiftKey,\n ctrl: e.ctrlKey,\n alt: e.altKey,\n meta: e.metaKey,\n space: spaceHeld,\n });\n\n const onPointerDown = (e: PointerEvent): void => {\n const id = pointerIdOf(e);\n const device = deviceOf(e);\n const { x, y } = relative(e.clientX, e.clientY);\n // Widget opt-out: down on a native interactive / [data-canvas-interactive]\n // flags the fact so recognizers skip it (design-002 §8). The GL router is\n // the same boundary for island content — its synchronous pick + synthetic\n // dispatch happens HERE, at event time (design-004 §4).\n const native = crossesInteractive(e.target, container);\n const glClaimed = glRoute?.(\"down\", x, y, e) === true;\n live.set(id, { device, x, y, downX: x, downY: y, native, captured: glClaimed });\n if (glClaimed) capture(e.pointerId); // island capture semantics need it NOW\n const surfaceHandled = native || glClaimed;\n queue.enqueue({\n kind: \"down\",\n pointerId: id,\n device,\n screenX: x,\n screenY: y,\n buttons: e.buttons,\n mods: pointerMods(e),\n tMs: e.timeStamp,\n ...(surfaceHandled ? { surfaceHandled: true } : {}),\n });\n };\n\n const onPointerMove = (e: PointerEvent): void => {\n const id = pointerIdOf(e);\n const device = deviceOf(e);\n const { x, y } = relative(e.clientX, e.clientY);\n const seen = live.get(id);\n if (seen !== undefined) {\n seen.x = x;\n seen.y = y;\n // Deferred capture: a real drag is forming — take it before any\n // recognizer dead zone (>=8px) can activate.\n if (\n !seen.captured &&\n !seen.native &&\n e.buttons !== 0 &&\n Math.hypot(x - seen.downX, y - seen.downY) > CAPTURE_SLOP_PX\n ) {\n capture(e.pointerId);\n seen.captured = true;\n }\n }\n const glHandled = glRoute?.(\"move\", x, y, e) === true; // island capture / hover synth\n queue.enqueue({\n kind: \"move\",\n pointerId: id,\n device,\n screenX: x,\n screenY: y,\n buttons: e.buttons,\n mods: pointerMods(e),\n tMs: e.timeStamp,\n ...(glHandled ? { surfaceHandled: true } : {}),\n });\n };\n\n const endPointer =\n (kind: \"up\" | \"cancel\") =>\n (e: PointerEvent): void => {\n const id = pointerIdOf(e);\n const device = deviceOf(e);\n const { x, y } = relative(e.clientX, e.clientY);\n live.delete(id);\n if (typeof container.releasePointerCapture === \"function\") {\n try {\n container.releasePointerCapture(e.pointerId);\n } catch {\n // release is best-effort (capture may already be gone).\n }\n }\n const glHandled = glRoute?.(kind, x, y, e) === true; // releases island capture\n queue.enqueue({\n kind,\n pointerId: id,\n device,\n screenX: x,\n screenY: y,\n buttons: e.buttons,\n mods: pointerMods(e),\n ...(glHandled ? { surfaceHandled: true } : {}),\n });\n };\n const onPointerUp = endPointer(\"up\");\n const onPointerCancel = endPointer(\"cancel\");\n\n const onWheel = (e: WheelEvent): void => {\n e.preventDefault(); // own zoom/pan — never let the page scroll\n const { x, y } = relative(e.clientX, e.clientY);\n const scale = e.deltaMode === 1 ? WHEEL_LINE_PX : 1; // DOM_DELTA_LINE → px\n const wheel = e.ctrlKey\n ? { dx: 0, dy: 0, pinch: e.deltaY } // ctrl-wheel / trackpad pinch = zoom signal\n : { dx: e.deltaX * scale, dy: e.deltaY * scale, pinch: 0 };\n queue.enqueue({ kind: \"wheel\", pointerId: \"mouse\", device: \"mouse\", screenX: x, screenY: y, buttons: e.buttons, mods: pointerMods(e), wheel });\n };\n\n /** Emit a `key` fact iff the modifier tuple changed (design-003 §2 minimal keyboard). */\n const emitKeyIfChanged = (e: KeyboardEvent): void => {\n const mods: InputMods = { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey, meta: e.metaKey, space: spaceHeld };\n if (\n mods.shift === lastMods.shift &&\n mods.ctrl === lastMods.ctrl &&\n mods.alt === lastMods.alt &&\n mods.meta === lastMods.meta &&\n mods.space === lastMods.space\n ) {\n return;\n }\n lastMods = { ...mods };\n queue.enqueue({ kind: \"key\", pointerId: \"\", device: \"mouse\", screenX: 0, screenY: 0, buttons: 0, mods });\n };\n\n const isSpace = (e: KeyboardEvent): boolean => e.key === \" \" || e.code === \"Space\";\n\n const onKeyDown = (e: KeyboardEvent): void => {\n if (isSpace(e)) {\n e.preventDefault(); // Space would otherwise page-scroll\n spaceHeld = true;\n }\n emitKeyIfChanged(e);\n };\n const onKeyUp = (e: KeyboardEvent): void => {\n if (isSpace(e)) {\n e.preventDefault();\n spaceHeld = false;\n }\n emitKeyIfChanged(e);\n };\n\n const onBlur = (): void => {\n // The browser will not deliver pointerups once we lose focus — cancel every\n // still-down pointer so no gesture is stranded Active (design-003 §8).\n for (const [id, s] of live) {\n queue.enqueue({ kind: \"cancel\", pointerId: id, device: s.device, screenX: s.x, screenY: s.y, buttons: 0, mods: { ...NO_MODS, space: spaceHeld } });\n }\n live.clear();\n // Modifier keyups are also lost on blur — ENQUEUE the clearing fact, or\n // the world's Keyboard resource stays latched (space-stuck: every canvas\n // drag pans forever). Clearing only local state was the bug: lastMods\n // already saying \"space up\" suppressed the next emitKeyIfChanged too.\n spaceHeld = false;\n if (lastMods.space || lastMods.shift || lastMods.ctrl || lastMods.alt || lastMods.meta) {\n lastMods = { ...NO_MODS };\n queue.enqueue({\n kind: \"key\",\n pointerId: \"\",\n device: \"mouse\",\n screenX: 0,\n screenY: 0,\n buttons: 0,\n mods: { ...NO_MODS },\n });\n }\n };\n\n container.addEventListener(\"pointerdown\", onPointerDown);\n container.addEventListener(\"pointermove\", onPointerMove);\n container.addEventListener(\"pointerup\", onPointerUp);\n container.addEventListener(\"pointercancel\", onPointerCancel);\n container.addEventListener(\"wheel\", onWheel, { passive: false });\n view?.addEventListener(\"keydown\", onKeyDown);\n view?.addEventListener(\"keyup\", onKeyUp);\n view?.addEventListener(\"blur\", onBlur);\n\n return () => {\n container.removeEventListener(\"pointerdown\", onPointerDown);\n container.removeEventListener(\"pointermove\", onPointerMove);\n container.removeEventListener(\"pointerup\", onPointerUp);\n container.removeEventListener(\"pointercancel\", onPointerCancel);\n container.removeEventListener(\"wheel\", onWheel);\n view?.removeEventListener(\"keydown\", onKeyDown);\n view?.removeEventListener(\"keyup\", onKeyUp);\n view?.removeEventListener(\"blur\", onBlur);\n };\n}\n","/**\n * The cursor reflector (design-003 §7 L4 cursor projection; v1 approach:\n * local cursor = OS cursor via one `style.cursor` write).\n *\n * `createCursorSync` resolves the cursor string into closure state every\n * `derive` tick with no observable ECS stamp (a plain string, not a\n * component/resource write), so there is nothing for `observe` to watch —\n * hence `always: true` with an internal last-value cache, mirroring the\n * \"cheap-overlay style\" carve-out `ReflectorDef.always` exists for. The cache\n * is what keeps the write ONE-per-change: `flush` runs every frame, but\n * `host.container.style.cursor` is only touched when `readCursor()` differs\n * from the last-applied value.\n *\n * Law 10: reflectors run post-notify, write output only, never read layout\n * or write ECS — this flush touches only `host.container.style`.\n */\nimport type { ReflectorDef, World } from \"@ice/core\";\nimport type { CanvasHost } from \"../host\";\n\nexport function createCursorReflector(\n host: CanvasHost,\n readCursor: () => string,\n): ReflectorDef & { writes(): number } {\n let writes = 0;\n let last: string | undefined;\n return {\n name: \"cursor\",\n always: true,\n flush(_world: World) {\n const next = readCursor();\n if (next === last) return;\n last = next;\n host.container.style.cursor = next;\n writes++;\n },\n writes: () => writes,\n };\n}\n","/**\n * P5 — remote cursors (design-004 §1: screen-space DOM, \"remote cursors are\n * always custom nodes\"; design-003 §7).\n *\n * Renders one pooled node per REMOTE cursor entity (`CursorVisual` kind\n * \"remote\" + world `Position`, derived by core's remote-cursors system from\n * presence peers; `Follows` names the peer whose `PresenceInfo` supplies\n * color + label). Screen-space by design: an acknowledged small-N\n * camera→position consumer (design-002 §5 rescope), recomputed on camera or\n * cursor dirt only. The plane mounts LAST in the container, above chrome;\n * `pointer-events: none` throughout — cursors are never hit-testable.\n */\nimport {\n Camera,\n CursorVisual,\n Follows,\n Position,\n PresenceInfo,\n defineQuery,\n type Entity,\n type ReflectorDef,\n type World,\n} from \"@ice/core\";\nimport { worldToScreen } from \"@ice/kernel\";\nimport type { CanvasHost } from \"../host\";\n\nconst remoteCursorQ = defineQuery([Position, CursorVisual]);\n\nexport interface RemoteCursorsReflector {\n readonly reflector: ReflectorDef;\n /** Remove the plane + pool (doc close / HMR). */\n destroy(): void;\n}\n\nexport function createRemoteCursorsReflector(host: CanvasHost, world: World): RemoteCursorsReflector {\n const doc = host.container.ownerDocument;\n const plane = doc.createElement(\"div\");\n Object.assign(plane.style, {\n position: \"absolute\",\n inset: \"0\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n } as Partial<CSSStyleDeclaration>);\n host.container.appendChild(plane); // last child ⇒ topmost (P5 over P4)\n\n interface Node {\n root: HTMLDivElement;\n label: HTMLDivElement;\n color: string;\n name: string;\n x: number;\n y: number;\n }\n const pool = new Map<Entity, Node>();\n\n const ensure = (e: Entity): Node => {\n let n = pool.get(e);\n if (n !== undefined) return n;\n const root = doc.createElement(\"div\");\n Object.assign(root.style, {\n position: \"absolute\",\n left: \"0\",\n top: \"0\",\n willChange: \"transform\",\n pointerEvents: \"none\",\n } as Partial<CSSStyleDeclaration>);\n // Arrow glyph + name chip (color applied on first style pass).\n const arrow = doc.createElement(\"div\");\n Object.assign(arrow.style, {\n width: \"0\",\n height: \"0\",\n borderLeft: \"6px solid transparent\",\n borderRight: \"6px solid transparent\",\n borderBottom: \"14px solid currentColor\",\n transform: \"rotate(-32deg)\",\n } as Partial<CSSStyleDeclaration>);\n const label = doc.createElement(\"div\");\n Object.assign(label.style, {\n marginTop: \"2px\",\n marginLeft: \"8px\",\n padding: \"1px 6px\",\n borderRadius: \"8px\",\n font: \"11px system-ui, sans-serif\",\n color: \"#fff\",\n whiteSpace: \"nowrap\",\n } as Partial<CSSStyleDeclaration>);\n root.appendChild(arrow);\n root.appendChild(label);\n plane.appendChild(root);\n n = { root, label, color: \"\", name: \"\", x: Number.NaN, y: Number.NaN };\n pool.set(e, n);\n return n;\n };\n\n const reflector: ReflectorDef = {\n name: \"remoteCursors\",\n observe: {\n resources: [Camera],\n queries: [{ q: remoteCursorQ, cols: [Position, CursorVisual] }],\n },\n flush: (w) => {\n const cam = w.getResource(Camera) ?? { x: 0, y: 0, zoom: 1 };\n const seen = new Set<Entity>();\n w.query(remoteCursorQ).each((b) => {\n for (const r of b) {\n const e = b.entity(r);\n if (w.get(e, CursorVisual)?.kind !== \"remote\") continue;\n seen.add(e);\n const p = w.read(e, Position);\n const s = worldToScreen(p.x, p.y, cam);\n const n = ensure(e);\n if (n.x !== s.x || n.y !== s.y) {\n n.root.style.transform = `translate(${s.x}px, ${s.y}px)`;\n n.x = s.x;\n n.y = s.y;\n }\n const peer = w.getRelation(e, Follows);\n const info = peer !== undefined ? w.get(peer, PresenceInfo) : undefined;\n const color = info?.color ?? \"#7a8aa0\";\n const name = info?.name ?? \"\";\n if (n.color !== color) {\n n.root.style.color = color;\n n.label.style.background = color;\n n.color = color;\n }\n if (n.name !== name) {\n n.label.textContent = name;\n n.label.style.display = name === \"\" ? \"none\" : \"block\";\n n.name = name;\n }\n }\n });\n for (const [e, n] of pool) {\n if (!seen.has(e)) {\n n.root.remove();\n pool.delete(e);\n }\n }\n },\n };\n\n return {\n reflector,\n destroy() {\n pool.clear();\n plane.remove();\n },\n };\n}\n","/**\n * The grid reflector (design-004 §1 P0 ground plane; M6).\n *\n * Ports v1's dot-grid shader VERBATIM from\n * `../../../infinite-canvas/packages/infinite-canvas/src/webgl/renderers/GridRenderer.ts`:\n * the fragment shader body (world reconstruction from `gl_FragCoord`, three\n * fade-staged spacing levels, max-composited anti-aliased dots) is copied\n * unchanged from GridRenderer.ts:45-115, and `DEFAULT_GRID_CONFIG` from\n * GridRenderer.ts:27-35. v1 drove this through three.js (`ShaderMaterial` +\n * `WebGLRenderer`), but `@ice/dom` may not import three (the `dom-only-core-\n * kernel` cruiser rule: core + kernel only) — so this reflector talks to a raw\n * WebGL2/WebGL1 context directly. Same GLSL, same uniforms, same math; only the\n * plumbing that fed them (three's abstractions) is gone. The vertex shader is\n * simplified from a vec3 attribute (three's default) to vec2 — geometry only,\n * no shading logic, so \"verbatim\" is unaffected.\n *\n * P0 is a WebGL `<canvas>` mounted as the FIRST child of the host container\n * (design-004 §1: \"ground\" sits under P1's content-DOM). It is sized to the\n * container's CSS box × devicePixelRatio via a ResizeObserver — its own,\n * independent hit-path; the reflector's `flush` never reads layout (Law 10),\n * only the RO callback does, and only in response to the browser's own resize\n * notification. Camera uniforms (world top-left + zoom) come from the `Camera`\n * RESOURCE, mirroring plane-transform.ts's `observe` pattern; unlike P1's\n * single CSS transform, this plane recomputes per-pixel in the shader — the\n * world is regenerated from `gl_FragCoord` every draw, so there are no seams\n * at any zoom/pan.\n *\n * Fault isolation, locally too: `getContext` returns `null` in headless/happy-\n * dom (jsdom/happy-dom implement `<canvas>` but not WebGL) — `available()`\n * reports that, and `flush()` is a permanent, silent no-op. GL setup and every\n * draw call are wrapped so a driver/context-loss failure degrades to\n * `available() === false` rather than a throwing reflector (the registry has\n * its own fault isolation — a skipped frame on throw — but a reflector that\n * throws every single frame is still noise, design-002 §5).\n */\nimport { Camera, type ReflectorDef, type World } from \"@ice/core\";\n\n// === Public config (mirrors v1 `GridConfig` — GridRenderer.ts:5-20 — field for field) ===\n\nexport interface GridConfig {\n /** World-unit spacings for up to 3 grid levels [fine, medium, coarse]. */\n spacings: [number, number, number];\n /** Dot RGB color as [r, g, b] in 0-1 range. */\n dotColor: [number, number, number];\n /** Base dot opacity multiplier (0-1). */\n dotAlpha: number;\n /** CSS-pixel range where a grid level fades in: [start, end]. */\n fadeIn: [number, number];\n /** CSS-pixel range where a grid level fades out: [start, end]. */\n fadeOut: [number, number];\n /** Dot radius range in CSS pixels [min, max]. Scaled by DPR internally. */\n dotRadius: [number, number];\n /** Per-level opacity weight: level i gets (base + i * step). */\n levelWeight: [number, number];\n}\n\n/** Verbatim from GridRenderer.ts:27-35 (tuned to match Apple Freeform / FigJam). */\nexport const DEFAULT_GRID_CONFIG: GridConfig = {\n spacings: [20, 100, 500],\n dotColor: [0.75, 0.77, 0.8],\n dotAlpha: 1.0,\n fadeIn: [8, 16],\n fadeOut: [120, 200],\n dotRadius: [0.75, 0.75],\n levelWeight: [1.0, 0.0],\n};\n\ninterface GridHost {\n readonly container: HTMLElement;\n}\n\n// === Shader source (fragment body verbatim from GridRenderer.ts:45-115) ===\n\nconst VERTEX_SHADER = /* glsl */ `\nattribute vec2 position;\nvoid main() {\n\tgl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst FRAGMENT_SHADER = /* glsl */ `\nprecision highp float;\n\nuniform vec2 u_resolution; // device pixels\nuniform vec2 u_camera; // world-space top-left\nuniform float u_zoom; // CSS zoom\nuniform float u_dpr; // device pixel ratio\nuniform vec3 u_spacings; // world-unit grid spacings\nuniform vec3 u_dotColor; // dot RGB\nuniform float u_dotAlpha; // dot base alpha\nuniform vec2 u_fadeIn; // CSS-px [start, end]\nuniform vec2 u_fadeOut; // CSS-px [start, end]\nuniform vec2 u_dotRadius; // CSS-px [min, max]\nuniform vec2 u_levelWeight; // [base, step]\n\nvoid main() {\n\tvec2 devicePos = gl_FragCoord.xy;\n\tdevicePos.y = u_resolution.y - devicePos.y;\n\n\tfloat effectiveZoom = u_zoom * u_dpr;\n\tvec2 worldPos = devicePos / effectiveZoom + u_camera;\n\n\tfloat totalAlpha = 0.0;\n\n\tfor (int i = 0; i < 3; i++) {\n\t\tfloat spacing;\n\t\tif (i == 0) spacing = u_spacings.x;\n\t\telse if (i == 1) spacing = u_spacings.y;\n\t\telse spacing = u_spacings.z;\n\n\t\t// Screen spacing in CSS pixels (DPR-independent for consistent fading)\n\t\tfloat cssSpacing = spacing * u_zoom;\n\n\t\t// Fade curve\n\t\tfloat opacity = 0.0;\n\t\tif (cssSpacing >= u_fadeIn.x && cssSpacing < u_fadeIn.y) {\n\t\t\topacity = (cssSpacing - u_fadeIn.x) / (u_fadeIn.y - u_fadeIn.x);\n\t\t} else if (cssSpacing >= u_fadeIn.y && cssSpacing < u_fadeOut.x) {\n\t\t\topacity = 1.0;\n\t\t} else if (cssSpacing >= u_fadeOut.x && cssSpacing < u_fadeOut.y) {\n\t\t\topacity = 1.0 - (cssSpacing - u_fadeOut.x) / (u_fadeOut.y - u_fadeOut.x);\n\t\t}\n\t\tif (opacity <= 0.001) continue;\n\n\t\t// Distance to nearest grid intersection in device pixels\n\t\tvec2 f = fract(worldPos / spacing + 0.5) - 0.5;\n\t\tfloat dist = length(f) * spacing * effectiveZoom;\n\n\t\t// Dot radius in device pixels — optionally grows for sparser levels\n\t\t// (set u_dotRadius.x == u_dotRadius.y for Freeform/FigJam-style\n\t\t// constant-size dots)\n\t\tfloat t = clamp((cssSpacing - u_fadeIn.x) / 40.0, 0.0, 1.0);\n\t\tfloat radius = mix(u_dotRadius.x, u_dotRadius.y, t) * u_dpr;\n\n\t\t// Anti-aliased dot (0.5 device pixel smoothstep)\n\t\tfloat dot = 1.0 - smoothstep(radius - 0.5, radius + 0.5, dist);\n\n\t\t// Per-level weight: base + i * step. Step=0 keeps all levels at equal\n\t\t// intensity; positive step emphasizes coarser levels (CAD feel).\n\t\tfloat weight = u_levelWeight.x + float(i) * u_levelWeight.y;\n\n\t\t// Composite with max, not sum. Additive compositing causes anti-\n\t\t// aliased dot rims to stack at joint intersections (every N-th dot\n\t\t// visibly fatter — a CAD tell). max() guarantees a joint intersection\n\t\t// looks identical to a single-level dot, matching Freeform / FigJam.\n\t\ttotalAlpha = max(totalAlpha, dot * opacity * weight);\n\t}\n\n\tgl_FragColor = vec4(u_dotColor, clamp(totalAlpha * u_dotAlpha, 0.0, 1.0));\n}\n`;\n\nconst FULLSCREEN_TRIANGLE = new Float32Array([-1, -1, 3, -1, -1, 3]);\n\nconst UNIFORM_NAMES = [\n \"u_resolution\",\n \"u_camera\",\n \"u_zoom\",\n \"u_dpr\",\n \"u_spacings\",\n \"u_dotColor\",\n \"u_dotAlpha\",\n \"u_fadeIn\",\n \"u_fadeOut\",\n \"u_dotRadius\",\n \"u_levelWeight\",\n] as const;\n\ntype UniformName = (typeof UNIFORM_NAMES)[number];\ntype GLContext = WebGLRenderingContext | WebGL2RenderingContext;\n\nfunction compileShader(gl: GLContext, type: number, source: string): WebGLShader | null {\n const shader = gl.createShader(type);\n if (shader === null) return null;\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) !== true) {\n gl.deleteShader(shader);\n return null;\n }\n return shader;\n}\n\nfunction linkProgram(gl: GLContext): WebGLProgram | null {\n const vs = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER);\n const fs = compileShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER);\n if (vs === null || fs === null) return null;\n const program = gl.createProgram();\n if (program === null) return null;\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n const linked = gl.getProgramParameter(program, gl.LINK_STATUS) === true;\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n if (!linked) {\n gl.deleteProgram(program);\n return null;\n }\n return program;\n}\n\n/** Fullscreen-triangle vertex buffer, bound to the `position` attribute. Returns false on any setup failure. */\nfunction setupGeometry(gl: GLContext, program: WebGLProgram): boolean {\n const buffer = gl.createBuffer();\n if (buffer === null) return false;\n const loc = gl.getAttribLocation(program, \"position\");\n if (loc < 0) return false;\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n gl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_TRIANGLE, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(loc);\n gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);\n return true;\n}\n\nfunction collectUniforms(gl: GLContext, program: WebGLProgram): Record<UniformName, WebGLUniformLocation | null> {\n const out = {} as Record<UniformName, WebGLUniformLocation | null>;\n for (const name of UNIFORM_NAMES) out[name] = gl.getUniformLocation(program, name);\n return out;\n}\n\nexport function createGridReflector(\n host: GridHost,\n opts: Partial<GridConfig> = {},\n): ReflectorDef & { available(): boolean; dispose(): void; configure(next: Partial<GridConfig>): void } {\n const config: GridConfig = { ...DEFAULT_GRID_CONFIG, ...opts };\n const doc = host.container.ownerDocument;\n\n const canvas = doc.createElement(\"canvas\");\n canvas.style.position = \"absolute\";\n canvas.style.left = \"0\";\n canvas.style.top = \"0\";\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.display = \"block\";\n // P0 is never a DOM hit target — the router does its own spatial-index pick\n // (design-004 §4); this plane must not shadow the L0 adapter's listeners.\n canvas.style.pointerEvents = \"none\";\n host.container.insertBefore(canvas, host.container.firstChild);\n\n let gl: GLContext | null = null;\n let program: WebGLProgram | null = null;\n let uniforms: Record<UniformName, WebGLUniformLocation | null> | null = null;\n let dpr = 1;\n let lastCamera = { x: 0, y: 0, zoom: 1 };\n\n try {\n const attrs: WebGLContextAttributes = { alpha: true, antialias: false, premultipliedAlpha: false };\n gl =\n (canvas.getContext(\"webgl2\", attrs) as WebGL2RenderingContext | null) ??\n (canvas.getContext(\"webgl\", attrs) as WebGLRenderingContext | null);\n if (gl !== null) {\n const prog = linkProgram(gl);\n if (prog !== null && setupGeometry(gl, prog)) {\n program = prog;\n uniforms = collectUniforms(gl, prog);\n } else {\n gl = null;\n }\n }\n } catch {\n gl = null;\n program = null;\n uniforms = null;\n }\n\n function draw(camera: { x: number; y: number; zoom: number }): void {\n if (gl === null || program === null || uniforms === null) return;\n lastCamera = camera;\n gl.viewport(0, 0, canvas.width, canvas.height);\n gl.useProgram(program);\n gl.uniform2f(uniforms.u_resolution, canvas.width, canvas.height);\n gl.uniform2f(uniforms.u_camera, camera.x, camera.y);\n gl.uniform1f(uniforms.u_zoom, camera.zoom);\n gl.uniform1f(uniforms.u_dpr, dpr);\n gl.uniform3f(uniforms.u_spacings, ...config.spacings);\n gl.uniform3f(uniforms.u_dotColor, ...config.dotColor);\n gl.uniform1f(uniforms.u_dotAlpha, config.dotAlpha);\n gl.uniform2f(uniforms.u_fadeIn, ...config.fadeIn);\n gl.uniform2f(uniforms.u_fadeOut, ...config.fadeOut);\n gl.uniform2f(uniforms.u_dotRadius, ...config.dotRadius);\n gl.uniform2f(uniforms.u_levelWeight, ...config.levelWeight);\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n }\n\n /** Resize the backing buffer to `cssWidth/cssHeight × dpr` and repaint from the last known camera. */\n function applySize(cssWidth: number, cssHeight: number): void {\n dpr = doc.defaultView?.devicePixelRatio ?? 1;\n const bufW = Math.max(1, Math.round(cssWidth * dpr));\n const bufH = Math.max(1, Math.round(cssHeight * dpr));\n if (canvas.width !== bufW) canvas.width = bufW;\n if (canvas.height !== bufH) canvas.height = bufH;\n try {\n draw(lastCamera);\n } catch {\n gl = null;\n program = null;\n uniforms = null;\n }\n }\n\n // Best-effort initial size (real browsers have a laid-out box synchronously\n // on creation; headless DOMs without a layout engine report 0×0 here, which\n // is harmless — the canvas just waits for the observer's first real tick).\n const initialRect = host.container.getBoundingClientRect();\n applySize(initialRect.width, initialRect.height);\n\n let ro: ResizeObserver | undefined;\n try {\n ro = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (entry === undefined) return;\n applySize(entry.contentRect.width, entry.contentRect.height);\n });\n ro.observe(host.container);\n } catch {\n // No ResizeObserver in this environment — the canvas keeps its initial\n // (possibly 0×0) size. Never fatal: `available()` is orthogonal to sizing.\n }\n\n return {\n name: \"grid\",\n observe: { resources: [Camera] },\n flush(world: World) {\n if (gl === null) return;\n const camera = world.getResource(Camera);\n if (camera === undefined) return;\n try {\n draw(camera);\n } catch {\n gl = null;\n program = null;\n uniforms = null;\n }\n },\n available: () => gl !== null,\n // Live re-tune (theme switches, settings panels): draw() uploads every\n // config uniform per call, so a merge + one redraw at the last camera is\n // the whole story. Outside the frame contract by design — config is host\n // input, not world state (same class as the RO resize path).\n configure(next: Partial<GridConfig>) {\n Object.assign(config, next);\n if (gl !== null) {\n try {\n draw(lastCamera);\n } catch {\n gl = null;\n program = null;\n uniforms = null;\n }\n }\n },\n // Unregistering a reflector only stops its flushes — the canvas this\n // factory inserted (and its ResizeObserver) must be torn down explicitly,\n // or a React StrictMode remount stacks a second frozen grid under the\n // live one (field report 2026-07-11).\n dispose() {\n ro?.disconnect();\n gl = null;\n program = null;\n uniforms = null;\n canvas.remove();\n },\n };\n}\n","/**\n * The wires reflector (design-004 §1 P0 \"WIRES\" pass; design-001 §5.3).\n *\n * P0 is the ground stratum: the grid canvas paints first, the wires canvas next\n * (this file), the content plane on top. Wires therefore render UNDER widget\n * content — a node graph's edges sit behind its nodes — with no z-index\n * bookkeeping: the wires canvas is inserted immediately before the content plane\n * (after the grid canvas), so DOM order is the strata order (§1: stacking is\n * stratified). Like the grid, it is a SCREEN-space canvas covering the full\n * viewport; the camera is applied per-draw via the kernel `worldToScreen` seam\n * rather than by a CSS plane transform, so panning re-strokes the curves at any\n * zoom with no seams.\n *\n * Wire geometry NEVER reads a port ENTITY (design-001 §5.3): endpoints resolve\n * from the wire's `WireFrom`/`WireTo` widgets (durable `Position`/`Size`) and the\n * two `WirePorts` ids looked up against the widget type's static port schema\n * (`PrefabId` -> `widgets.get(type).ports`); the kernel `portAnchor` places each\n * anchor on the widget rect and `wireCubic` builds the curve. Because that path\n * touches only durable geometry + the type registry — never a runtime port\n * entity — a wire whose endpoint is CULLED (off-viewport, no DOM host, no\n * materialized port) still draws by construction. This mirrors core's audited\n * `systems/l1-wires.ts` endpoint resolver, so pick and render agree. The connect-\n * drag preview and the materialized port dots share this one canvas.\n *\n * Change-only (design-002 §5): the reflector is `always: true` (the connect\n * preview is an out-of-ECS buffer with no ECS stamp, exactly like the marquee\n * buffer the chrome reflector consumes), but a full re-stroke happens only when\n * something visible changed — private observers on Camera / [Position,Size] /\n * [Wire] / [Selected] / ports / [RoutedConnect] arm a dirty flag, and the preview\n * buffer is diffed by value each frame. A static scene with an idle preview\n * re-strokes zero times after first paint.\n *\n * Fault isolation mirrors the grid: `getContext(\"2d\")` returns `null` in happy-\n * dom (and on context loss), so `available()` reports false and `flush` is a\n * permanent silent no-op; every draw is wrapped so a driver failure degrades to\n * unavailable rather than a throwing reflector (design-002 §5).\n *\n * Law 10: reflectors run post-notify, write output only, never read layout or\n * write ECS — this flush touches only the wires canvas 2D context.\n */\nimport {\n Camera,\n defineQuery,\n type Entity,\n MeasuredSize,\n Port,\n PortAnchor,\n Position,\n PrefabId,\n type ReflectorDef,\n RoutedConnect,\n Selected,\n Size,\n widgets,\n type WidgetType,\n Wire,\n WireFrom,\n WirePorts,\n WireTo,\n type World,\n} from \"@ice/core\";\nimport {\n type CameraState,\n type Cubic,\n type PortSide,\n type PortSlot,\n portAnchor,\n type Rect,\n wireCubic,\n worldToScreen,\n} from \"@ice/kernel\";\nimport type { CanvasHost } from \"../host\";\n\n/**\n * The connect-drag preview buffer (structural mirror of the core\n * `WirePreviewBuffer` the graph-systems slice produces in\n * `core/src/systems/l3-connect.ts`, exposed on `installInteractionStack` as\n * `wirePreview`). Coords are WORLD-space; `active` gates drawing; `compatible`\n * picks solid (snapped to a compatible port) vs dashed (free / incompatible).\n *\n * TODO(integration): once core exports its buffer type, import it here and drop\n * this local — the lead reconciles field names if they diverge.\n */\nexport interface WirePreview {\n /** Source anchor (world). */\n sx: number;\n sy: number;\n /** Target / cursor (world). */\n tx: number;\n ty: number;\n active: boolean;\n compatible: boolean;\n}\n\nconst INACTIVE_PREVIEW: WirePreview = { sx: 0, sy: 0, tx: 0, ty: 0, active: false, compatible: false };\n\n/** Minimal-neutral wire styling (design-004 §6 chrome is subdued; wires defer to content). */\nexport interface WiresConfig {\n /** Default wire stroke (CSS color). */\n wireColor: string;\n /** Accent stroke for a Selected wire. */\n selectedColor: string;\n wireWidth: number;\n selectedWidth: number;\n /** Materialized-port dot radius (CSS px) + colors (idle vs during a connect drag). */\n portRadius: number;\n portColor: string;\n portActiveColor: string;\n}\n\nexport const DEFAULT_WIRES_CONFIG: WiresConfig = {\n wireColor: \"rgba(120, 132, 145, 0.9)\",\n selectedColor: \"#4a90d9\",\n wireWidth: 1.5,\n selectedWidth: 2,\n portRadius: 4,\n portColor: \"rgba(120, 132, 145, 0.9)\",\n portActiveColor: \"#4a90d9\",\n};\n\nconst wireQ = defineQuery([Wire]);\nconst geometryQ = defineQuery([Position, Size]);\nconst selectedQ = defineQuery([Selected]);\nconst portQ = defineQuery([Port, PortAnchor]);\nconst routedConnectQ = defineQuery([RoutedConnect]);\n\nexport interface WiresReflector extends ReflectorDef {\n /** False under happy-dom / on context loss — flush is then a silent no-op. */\n available(): boolean;\n /** Full re-stroke count (the churn instrument): one per frame that actually redrew, zero on a static idle scene. */\n redraws(): number;\n /** Detach the canvas + tear down the private observers and ResizeObserver. */\n dispose(): void;\n}\n\n/**\n * Effective widget rect (design-004 §2): `MeasuredSize` where present and non-\n * zero, else `Size` — so wire anchors track the visually-rendered box of an\n * auto-sized node. Field names map to the kernel `Rect` (width/height).\n */\nfunction readRect(world: World, e: Entity): Rect | undefined {\n const p = world.get(e, Position);\n if (p === undefined) return undefined;\n const measured = world.get(e, MeasuredSize);\n const s = measured !== undefined && measured.w > 0 ? measured : world.get(e, Size);\n if (s === undefined) return undefined;\n return { x: p.x, y: p.y, width: s.w, height: s.h };\n}\n\n/**\n * Resolve a widget type's ports to kernel `PortSlot`s (side + even-distribution\n * index/count), cached per type since the schema is immutable. Mirrors core's\n * `ops/port-geometry.ts` `portSlots` EXACTLY (ports on a side numbered by explicit\n * `index` else declaration order; side `count` drives the even spacing), so the\n * rendered anchor agrees with the picked one by construction.\n *\n * TODO(integration): swap for the core `portSlotOf` export once the graph-systems\n * slice adds it to the `@ice/core` barrel (the import wall requires the package root).\n */\nfunction makePortSlotResolver(): (wt: WidgetType, portId: string) => PortSlot | undefined {\n const cache = new Map<string, Map<string, PortSlot>>();\n const build = (wt: WidgetType): Map<string, PortSlot> => {\n const bySide = new Map<PortSide, WidgetType[\"ports\"][number][]>();\n for (const d of wt.ports) {\n let list = bySide.get(d.side);\n if (list === undefined) {\n list = [];\n bySide.set(d.side, list);\n }\n list.push(d);\n }\n const out = new Map<string, PortSlot>();\n for (const [side, list] of bySide) {\n for (let i = 0; i < list.length; i++) {\n const d = list[i];\n if (d === undefined) continue;\n out.set(d.id, { side, index: d.index ?? i, count: list.length });\n }\n }\n return out;\n };\n return (wt, portId) => {\n let slots = cache.get(wt.type);\n if (slots === undefined) {\n slots = build(wt);\n cache.set(wt.type, slots);\n }\n return slots.get(portId);\n };\n}\n\n/** Screen-space control points for a world-space cubic (worldToScreen x4). */\nfunction cubicToScreen(c: Cubic, cam: CameraState): Cubic {\n const p0 = worldToScreen(c.x0, c.y0, cam);\n const p1 = worldToScreen(c.x1, c.y1, cam);\n const p2 = worldToScreen(c.x2, c.y2, cam);\n const p3 = worldToScreen(c.x3, c.y3, cam);\n return { x0: p0.x, y0: p0.y, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y, x3: p3.x, y3: p3.y };\n}\n\nexport function createWiresReflector(\n host: CanvasHost,\n world: World,\n opts: { readPreview?: () => WirePreview; config?: Partial<WiresConfig> } = {},\n): WiresReflector {\n const config: WiresConfig = { ...DEFAULT_WIRES_CONFIG, ...opts.config };\n const readPreview = opts.readPreview ?? (() => INACTIVE_PREVIEW);\n const doc = host.container.ownerDocument;\n\n const canvas = doc.createElement(\"canvas\");\n canvas.style.position = \"absolute\";\n canvas.style.left = \"0\";\n canvas.style.top = \"0\";\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.style.display = \"block\";\n // P0 is never a DOM hit target (the router does its own spatial-index pick,\n // design-004 §4) — the wire hit-path is the kernel curve-distance test, not DOM.\n canvas.style.pointerEvents = \"none\";\n // Immediately before the content plane => after the grid canvas => wires paint\n // above the grid, below content (P0 ground stratum).\n host.container.insertBefore(canvas, host.contentPlane);\n\n let ctx: CanvasRenderingContext2D | null = null;\n try {\n ctx = canvas.getContext(\"2d\");\n } catch {\n ctx = null;\n }\n\n let dpr = 1;\n const slotOf = makePortSlotResolver();\n\n // Private dirt: the preview buffer carries no ECS stamp (chrome/dom-widgets\n // pattern), so this reflector is `always` and self-gates. Any change that\n // moves a stroke arms `dirty`; the preview is diffed by value each frame.\n let dirty = true;\n let lastPreviewKey = \"\";\n let redraws = 0;\n const wake = () => {\n dirty = true;\n };\n const unsubs: Array<() => void> = [\n world.reactive.observeResource(Camera, wake),\n world.reactive.observeQuery(wireQ, wake, { cols: [] }),\n world.reactive.observeQuery(geometryQ, wake, { cols: [Position, Size] }),\n world.reactive.observeQuery(selectedQ, wake, { cols: [] }),\n world.reactive.observeQuery(portQ, wake, { cols: [PortAnchor] }),\n world.reactive.observeQuery(routedConnectQ, wake, { cols: [] }),\n ];\n\n function applySize(cssWidth: number, cssHeight: number): void {\n dpr = doc.defaultView?.devicePixelRatio ?? 1;\n const bufW = Math.max(1, Math.round(cssWidth * dpr));\n const bufH = Math.max(1, Math.round(cssHeight * dpr));\n if (canvas.width !== bufW) canvas.width = bufW;\n if (canvas.height !== bufH) canvas.height = bufH;\n // Resizing a canvas resets its context state; a re-stroke is owed.\n dirty = true;\n }\n\n const initialRect = host.container.getBoundingClientRect();\n applySize(initialRect.width, initialRect.height);\n\n let ro: ResizeObserver | null = null;\n try {\n ro = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (entry === undefined) return;\n applySize(entry.contentRect.width, entry.contentRect.height);\n });\n ro.observe(host.container);\n } catch {\n ro = null;\n }\n\n /** Stroke one screen-space cubic. */\n function strokeCubic(c2d: CanvasRenderingContext2D, s: Cubic): void {\n c2d.beginPath();\n c2d.moveTo(s.x0, s.y0);\n c2d.bezierCurveTo(s.x1, s.y1, s.x2, s.y2, s.x3, s.y3);\n c2d.stroke();\n }\n\n /**\n * Resolve a wire endpoint's anchor rect + port slot from its endpoint widget +\n * port id (mirrors core `l1-wires` `endpointAnchor` null-handling: a `string`\n * PrefabId, a registered type, a declared port — anything missing => undefined,\n * and the caller skips the wire).\n */\n function endpointAnchor(widget: Entity | undefined, portId: string): { anchor: Rect; slot: PortSlot } | undefined {\n if (widget === undefined) return undefined;\n const rect = readRect(world, widget);\n if (rect === undefined) return undefined;\n const type = world.get(widget, PrefabId)?.id;\n if (typeof type !== \"string\") return undefined;\n const wt = widgets.get(type);\n if (wt === undefined) return undefined;\n const slot = slotOf(wt, portId);\n if (slot === undefined) return undefined;\n return { anchor: rect, slot };\n }\n\n function drawWires(c2d: CanvasRenderingContext2D, cam: CameraState): void {\n world.query(wireQ).each((batch) => {\n for (const r of batch) {\n const wire = batch.entity(r);\n const ports = world.get(wire, WirePorts);\n if (ports === undefined) continue;\n const from = endpointAnchor(world.getRelation(wire, WireFrom), ports.from ?? \"\");\n const to = endpointAnchor(world.getRelation(wire, WireTo), ports.to ?? \"\");\n if (from === undefined || to === undefined) continue;\n const a0 = portAnchor(from.anchor, from.slot);\n const a1 = portAnchor(to.anchor, to.slot);\n const cubic = wireCubic(a0, from.slot.side, a1, to.slot.side);\n const selected = world.hasTag(wire, Selected);\n c2d.strokeStyle = selected ? config.selectedColor : config.wireColor;\n c2d.lineWidth = selected ? config.selectedWidth : config.wireWidth;\n c2d.setLineDash([]);\n strokeCubic(c2d, cubicToScreen(cubic, cam));\n }\n });\n }\n\n function drawPreview(c2d: CanvasRenderingContext2D, cam: CameraState, preview: WirePreview): void {\n if (!preview.active) return;\n // The preview buffer carries no port sides (a free drag), so build a smooth\n // horizontal-stub cubic between the two world points — a readable stand-in\n // until the wire commits and gets its real port sides.\n const stub = Math.max(24, Math.abs(preview.tx - preview.sx) * 0.5);\n const cubic: Cubic = {\n x0: preview.sx,\n y0: preview.sy,\n x1: preview.sx + stub,\n y1: preview.sy,\n x2: preview.tx - stub,\n y2: preview.ty,\n x3: preview.tx,\n y3: preview.ty,\n };\n c2d.strokeStyle = preview.compatible ? config.selectedColor : config.wireColor;\n c2d.lineWidth = config.wireWidth;\n c2d.setLineDash(preview.compatible ? [] : [6, 4]);\n strokeCubic(c2d, cubicToScreen(cubic, cam));\n c2d.setLineDash([]);\n }\n\n function drawPorts(c2d: CanvasRenderingContext2D, cam: CameraState): void {\n const connecting = world.firstOf(routedConnectQ) !== undefined;\n c2d.fillStyle = connecting ? config.portActiveColor : config.portColor;\n world.query(portQ).each((batch) => {\n const ax = batch.col(PortAnchor).x;\n const ay = batch.col(PortAnchor).y;\n for (const r of batch) {\n const s = worldToScreen(ax[r] as number, ay[r] as number, cam);\n c2d.beginPath();\n c2d.arc(s.x, s.y, config.portRadius, 0, Math.PI * 2);\n c2d.fill();\n }\n });\n }\n\n function redraw(c2d: CanvasRenderingContext2D, cam: CameraState, preview: WirePreview): void {\n // Clear the full backing buffer (identity), then draw in CSS px (dpr scale).\n c2d.setTransform(1, 0, 0, 1, 0, 0);\n c2d.clearRect(0, 0, canvas.width, canvas.height);\n c2d.setTransform(dpr, 0, 0, dpr, 0, 0);\n redraws++;\n drawWires(c2d, cam);\n drawPreview(c2d, cam, preview);\n drawPorts(c2d, cam);\n }\n\n return {\n name: \"wires\",\n // The preview buffer has no ECS stamp; self-gate on a private dirty flag\n // (chrome/dom-widgets pattern) rather than the registry's observe dirt.\n always: true,\n flush(w: World) {\n if (ctx === null) return;\n const cam = w.getResource(Camera);\n if (cam === undefined) return;\n const preview = readPreview();\n const previewKey = preview.active\n ? `${preview.sx},${preview.sy},${preview.tx},${preview.ty},${preview.compatible ? 1 : 0}`\n : \"\";\n if (previewKey !== lastPreviewKey) {\n lastPreviewKey = previewKey;\n dirty = true;\n }\n if (!dirty) return;\n dirty = false;\n try {\n redraw(ctx, cam, preview);\n } catch {\n ctx = null; // context loss => degrade to a silent no-op, never a throwing reflector\n }\n },\n available: () => ctx !== null,\n redraws: () => redraws,\n dispose() {\n for (const u of unsubs) u();\n unsubs.length = 0;\n ro?.disconnect();\n ro = null;\n canvas.remove();\n },\n };\n}\n","/**\n * The chrome reflector (design-004 §5 chrome plane P4; post-notify, SCREEN-space).\n *\n * Draws the engine's pooled chrome entities plus the out-of-ECS marquee buffer\n * into a dedicated, non-transformed overlay plane above the content plane:\n * - each `SelectionBox` entity → a 1px outline rect (its world x,y,w,h mapped to\n * screen via `worldToScreen` × zoom);\n * - each `HandleSpec` entity → an 8×8 screen-px square CENTERED on the handle's\n * world AABB center (the handle's world size is zoom-scaled for picking; the\n * drawn size is screen-constant);\n * - the `marqueeBuffer.rect` (world) → a rect while non-null.\n *\n * P4 is screen-space by design (design-004 §2 rescope): unlike the content plane\n * (one camera transform), chrome is recomputed per camera change — small N. This\n * reflector therefore reads the `Camera` resource directly (an acknowledged\n * small-N screen-space camera consumer).\n *\n * `always: true`, not observer-driven: the marquee buffer has no ECS stamp to\n * observe, so the reflector must get a chance every frame. A cheap early-out\n * keeps it idle-free — it returns immediately when there is nothing to draw AND\n * nothing was drawn last frame (so a cleared selection still gets one flush to\n * remove its nodes). Style writes are change-only (per-node geometry cache) and\n * counted by `writes()` — the churn instrument, mirroring the other reflectors.\n *\n * Law 10: reflectors write output only, never read layout or write ECS — this\n * flush touches only the overlay plane's node styles.\n */\nimport {\n Camera,\n defineQuery,\n type Entity,\n HandleSpec,\n type MarqueeBuffer,\n Position,\n type ReflectorDef,\n SelectionBox,\n Size,\n type World,\n} from \"@ice/core\";\nimport { worldToScreen } from \"@ice/kernel\";\nimport type { CanvasHost } from \"../host\";\n\n/** On-screen handle square edge (px) — the hit target (world size) is larger. */\nconst HANDLE_DRAW_PX = 8;\n\nconst boxQ = defineQuery([SelectionBox]);\nconst handleQ = defineQuery([HandleSpec, Position, Size]);\n\nfunction makeNode(doc: Document, extra: Record<string, string>): HTMLDivElement {\n const node = doc.createElement(\"div\");\n Object.assign(node.style, { position: \"absolute\", boxSizing: \"border-box\", pointerEvents: \"none\" }, extra);\n return node;\n}\n\nexport function createChromeReflector(\n host: CanvasHost,\n // Signature carries `world` (design-004 chrome install shape); the flush is\n // handed the same world by the registry, so we read it there, not from closure.\n _world: World,\n marqueeBuffer: MarqueeBuffer,\n): ReflectorDef & { writes(): number; dispose(): void } {\n const doc = host.container.ownerDocument;\n // The screen-space overlay plane — appended after the content plane, so it\n // paints above it (P4 over P1) without a camera transform of its own.\n const plane = makeNode(doc, { left: \"0\", top: \"0\", width: \"0\", height: \"0\" });\n host.container.appendChild(plane);\n\n const boxNodes = new Map<Entity, HTMLDivElement>();\n const handleNodes = new Map<Entity, HTMLDivElement>();\n let marqueeNode: HTMLDivElement | null = null;\n\n const boxCache = new Map<Entity, string>();\n const handleCache = new Map<Entity, string>();\n let marqueeCache: string | null = null;\n let drewLast = false;\n\n const seenBox = new Set<Entity>();\n const seenHandle = new Set<Entity>();\n\n let writes = 0;\n\n const writeRect = (node: HTMLDivElement, left: number, top: number, w: number, h: number): void => {\n node.style.left = `${left}px`;\n node.style.top = `${top}px`;\n node.style.width = `${w}px`;\n node.style.height = `${h}px`;\n writes++;\n };\n\n return {\n name: \"chrome\",\n always: true,\n observe: {\n queries: [\n { q: boxQ, cols: [SelectionBox] },\n { q: handleQ, cols: [Position, Size] },\n ],\n resources: [Camera],\n },\n flush(w: World) {\n const cam = w.getResource(Camera);\n if (cam === undefined) return;\n\n const marqueeRect = marqueeBuffer.rect;\n // Cheap early-out: nothing to draw and nothing drawn last frame ⇒ skip the\n // full reconcile. `firstOf` is O(1)-ish (first matching archetype).\n const hasContent =\n marqueeRect !== null ||\n w.firstOf(boxQ) !== undefined ||\n w.firstOf(handleQ) !== undefined;\n if (!hasContent && !drewLast) return;\n\n seenBox.clear();\n seenHandle.clear();\n\n // Selection boxes: world rect → screen outline.\n w.query(boxQ).each((batch) => {\n const bx = batch.col(SelectionBox).x;\n const by = batch.col(SelectionBox).y;\n const bw = batch.col(SelectionBox).w;\n const bh = batch.col(SelectionBox).h;\n for (const r of batch) {\n const e = batch.entity(r);\n seenBox.add(e);\n const tl = worldToScreen(bx[r] as number, by[r] as number, cam);\n const sw = (bw[r] as number) * cam.zoom;\n const sh = (bh[r] as number) * cam.zoom;\n const key = `${tl.x}:${tl.y}:${sw}:${sh}`;\n let node = boxNodes.get(e);\n if (node === undefined) {\n node = makeNode(doc, { border: \"1px solid #4a90d9\" });\n node.dataset.chrome = \"box\";\n boxNodes.set(e, node);\n plane.appendChild(node);\n }\n if (boxCache.get(e) !== key) {\n writeRect(node, tl.x, tl.y, sw, sh);\n boxCache.set(e, key);\n }\n }\n });\n for (const [e, node] of boxNodes) {\n if (seenBox.has(e)) continue;\n node.remove();\n boxNodes.delete(e);\n boxCache.delete(e);\n }\n\n // Handles: world AABB center → screen, draw a fixed 8×8 square around it.\n w.query(handleQ).each((batch) => {\n const px = batch.col(Position).x;\n const py = batch.col(Position).y;\n const sw = batch.col(Size).w;\n const sh = batch.col(Size).h;\n for (const r of batch) {\n const e = batch.entity(r);\n seenHandle.add(e);\n const cx = (px[r] as number) + (sw[r] as number) / 2;\n const cy = (py[r] as number) + (sh[r] as number) / 2;\n const sc = worldToScreen(cx, cy, cam);\n const left = sc.x - HANDLE_DRAW_PX / 2;\n const top = sc.y - HANDLE_DRAW_PX / 2;\n const key = `${left}:${top}`;\n let node = handleNodes.get(e);\n if (node === undefined) {\n node = makeNode(doc, {\n width: `${HANDLE_DRAW_PX}px`,\n height: `${HANDLE_DRAW_PX}px`,\n background: \"#fff\",\n border: \"1px solid #4a90d9\",\n });\n node.dataset.chrome = \"handle\";\n handleNodes.set(e, node);\n plane.appendChild(node);\n }\n if (handleCache.get(e) !== key) {\n node.style.left = `${left}px`;\n node.style.top = `${top}px`;\n writes++;\n handleCache.set(e, key);\n }\n }\n });\n for (const [e, node] of handleNodes) {\n if (seenHandle.has(e)) continue;\n node.remove();\n handleNodes.delete(e);\n handleCache.delete(e);\n }\n\n // Marquee: from the out-of-ECS buffer, drawn while non-null.\n if (marqueeRect !== null) {\n const tl = worldToScreen(marqueeRect.x, marqueeRect.y, cam);\n const mw = marqueeRect.w * cam.zoom;\n const mh = marqueeRect.h * cam.zoom;\n const key = `${tl.x}:${tl.y}:${mw}:${mh}`;\n if (marqueeNode === null) {\n marqueeNode = makeNode(doc, { border: \"1px dashed #4a90d9\", background: \"rgba(74,144,217,0.1)\" });\n marqueeNode.dataset.chrome = \"marquee\";\n plane.appendChild(marqueeNode);\n }\n if (marqueeCache !== key) {\n writeRect(marqueeNode, tl.x, tl.y, mw, mh);\n marqueeCache = key;\n }\n } else if (marqueeNode !== null) {\n marqueeNode.remove();\n marqueeNode = null;\n marqueeCache = null;\n }\n\n drewLast = boxNodes.size > 0 || handleNodes.size > 0 || marqueeNode !== null;\n },\n writes: () => writes,\n // The P4 overlay plane is this factory's insertion — unregistering the\n // reflector alone leaves it behind (2026-07-11 teardown sweep).\n dispose() {\n plane.remove();\n },\n };\n}\n","/**\n * The measurement adapter (design-004 §2 host pipeline; measure side).\n *\n * Wraps a `ResizeObserver` over auto-sized widgets' CONTENT elements and pushes\n * `{ entity, w, h }` samples into the engine's {@link MeasureQueue} — the\n * adapter-only path (Law 2: adapters ONLY enqueue; `measureIngest` turns samples\n * into `MeasuredSize` facts inside the frame contract). It resolves an entity's\n * content element on demand via the caller's `hostFor` (the widget host reflector\n * owns the element↔entity mapping).\n *\n * §2 hidden-measure rule: a `display:none` host collapses to `0×0`, and a\n * full→0 sample would pass any dead-band and corrupt bounds/cull on re-entry —\n * so the adapter NEVER enqueues while the host is hidden (computed `display:none`\n * / no `offsetParent`), and also drops a literal `0×0`. It is the CALLER's job to\n * re-`observe` (or otherwise force a remeasure) on re-show — a `ResizeObserver`\n * does not re-fire for an element that was hidden and shown at the same box size.\n *\n * One shared `ResizeObserver` multiplexes every observed element (fewer objects,\n * identical semantics to one-per-element); `element → entity` maps a callback\n * entry back to its widget.\n */\nimport type { Entity, MeasureQueue } from \"@ice/core\";\n\nexport interface MeasureAdapter {\n /** Begin observing the entity's content element (no-op if `hostFor` yields none). */\n observe(entity: Entity): void;\n /** Stop observing the entity's content element. */\n unobserve(entity: Entity): void;\n /** Disconnect the observer and drop all mappings. */\n dispose(): void;\n}\n\n/** True when the element is `display:none` (collapsed to 0×0) and must not be measured. */\nfunction isHidden(el: HTMLElement): boolean {\n // offsetParent is null for display:none — but also for position:fixed and the\n // <body>, so a null offsetParent is only conclusive with a computed-display check.\n if (el.offsetParent !== null) return false;\n const display = el.ownerDocument.defaultView?.getComputedStyle(el).display;\n return display === \"none\";\n}\n\nexport function attachMeasureAdapter(\n hostFor: (e: Entity) => HTMLElement | undefined,\n queue: MeasureQueue,\n): MeasureAdapter {\n const byElement = new Map<Element, Entity>();\n const byEntity = new Map<Entity, Element>();\n\n const ro = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n const entity = byElement.get(el);\n if (entity === undefined) continue;\n if (isHidden(el)) continue; // never ingest from a hidden host (§2)\n const box = entry.borderBoxSize?.[0];\n const w = box !== undefined ? box.inlineSize : entry.contentRect.width;\n const h = box !== undefined ? box.blockSize : entry.contentRect.height;\n if (w === 0 && h === 0) continue; // double wall with ingest's 0×0 skip\n queue.enqueue({ entity, w, h });\n }\n });\n\n return {\n observe(entity) {\n const el = hostFor(entity);\n if (el === undefined) return;\n const prev = byEntity.get(entity);\n if (prev !== undefined && prev !== el) {\n ro.unobserve(prev);\n byElement.delete(prev);\n }\n byElement.set(el, entity);\n byEntity.set(entity, el);\n ro.observe(el);\n },\n unobserve(entity) {\n const el = byEntity.get(entity);\n if (el === undefined) return;\n ro.unobserve(el);\n byElement.delete(el);\n byEntity.delete(entity);\n },\n dispose() {\n ro.disconnect();\n byElement.clear();\n byEntity.clear();\n },\n };\n}\n","/**\n * Measurement wiring (design-004 §2 host pipeline; measure side, the store half).\n *\n * `attachMeasureAdapter` owns the `ResizeObserver` and the element→entity\n * mapping; THIS module owns *when* to observe/unobserve, driven by the engine's\n * widget mount store. It subscribes and reconciles each snapshot diff into four\n * transitions (design-004 §2 / decision 4):\n *\n * - **fresh mount + visible** → `observe` (the RO fires its own first sample);\n * - **visible → hidden** (culled-but-kept-mounted) → `unobserve` (disconnect on\n * hide — a `display:none` host measures `0×0` and a full→0 sample corrupts\n * bounds/cull on re-entry);\n * - **hidden → shown** → re-`observe` AND a FORCED synchronous re-measure: a\n * `ResizeObserver` does not re-fire for an element hidden and shown at the\n * same box size, so the reconnect alone would leave the rider stale;\n * - **unmount** (exits the snapshot) → `unobserve`.\n *\n * Ordering note: the mount store notifies from the widget-runtime flush\n * reflector, which is registered BEFORE the `domWidgets` host reflector — so at\n * notify time the host `<div>` for a just-mounted (or just-shown) widget may not\n * exist / may still be `display:none`. Like the React portal hook (which defers\n * via its own scheduler), this defers the reconcile to a microtask so it runs\n * AFTER the whole synchronous reflector pass, by which point `domWidgets` has\n * created/unhidden the host and `hostFor` + the forced re-measure see a real box.\n *\n * Law 2 holds: everything here only enqueues (through the adapter or the forced\n * sample) — no world writes, no ECS reads.\n */\nimport type { Entity, MeasureQueue, WidgetMountStore } from \"@ice/core\";\nimport { attachMeasureAdapter } from \"./measure-adapter\";\n\n/** The element source (the `domWidgets` reflector supplies this via `hostFor`). */\nexport interface MeasureWiringHosts {\n hostFor(entity: Entity): HTMLElement | undefined;\n}\n\nexport interface MeasureWiringOpts {\n /**\n * Border-box read for the forced re-measure on show. Defaults to\n * `getBoundingClientRect`; tests inject a deterministic stub.\n */\n readonly measure?: (el: HTMLElement) => { w: number; h: number };\n}\n\nfunction defaultMeasure(el: HTMLElement): { w: number; h: number } {\n const rect = el.getBoundingClientRect();\n return { w: rect.width, h: rect.height };\n}\n\n/**\n * Begin observing auto-sized widget hosts against `store`, pushing measurements\n * into `queue` (drained by `measureIngest`). Returns a teardown that\n * unsubscribes and disposes the underlying `ResizeObserver`.\n */\nexport function wireMeasurement(\n store: WidgetMountStore,\n hosts: MeasureWiringHosts,\n queue: MeasureQueue,\n opts: MeasureWiringOpts = {},\n): () => void {\n const adapter = attachMeasureAdapter((e) => hosts.hostFor(e), queue);\n const measure = opts.measure ?? defaultMeasure;\n // Last-applied per-entity state — the diff basis. Absent = not mounted.\n const state = new Map<Entity, \"visible\" | \"hidden\">();\n let scheduled = false;\n let disposed = false;\n\n /** Reconnect leaves the rider stale for an unchanged box — force one sample (§2). */\n const forceRemeasure = (e: Entity): void => {\n const el = hosts.hostFor(e);\n if (el === undefined) return;\n const { w, h } = measure(el);\n if (w <= 0 && h <= 0) return; // never enqueue a hidden / 0×0 sample (§2)\n queue.enqueue({ entity: e, w, h });\n };\n\n const reconcile = (): void => {\n if (disposed) return;\n const snapshot = store.getSnapshot();\n const present = new Set<Entity>();\n for (const entry of snapshot) {\n present.add(entry.entity);\n const prev = state.get(entry.entity);\n if (entry.hidden) {\n if (prev === \"visible\") adapter.unobserve(entry.entity); // visible → hidden: disconnect\n state.set(entry.entity, \"hidden\");\n } else {\n if (prev === undefined) {\n adapter.observe(entry.entity); // fresh mount: the RO fires its own first sample\n } else if (prev === \"hidden\") {\n adapter.observe(entry.entity); // reconnect on show…\n forceRemeasure(entry.entity); // …and force it (the RO won't re-fire same-box)\n }\n state.set(entry.entity, \"visible\");\n }\n }\n for (const [e, s] of [...state]) {\n if (present.has(e)) continue;\n if (s === \"visible\") adapter.unobserve(e); // unmount from visible: disconnect\n state.delete(e);\n }\n };\n\n // Defer to a microtask so the reconcile runs AFTER the synchronous reflector\n // pass (the host reflector creates/unhides the host later this frame).\n const onStoreChange = (): void => {\n if (scheduled || disposed) return;\n scheduled = true;\n queueMicrotask(() => {\n scheduled = false;\n reconcile();\n });\n };\n\n const unsub = store.subscribe(onStoreChange);\n onStoreChange(); // pick up any entries already present at wire time\n\n return () => {\n disposed = true;\n unsub();\n adapter.dispose();\n state.clear();\n };\n}\n","/**\n * @ice/dom — DOM planes, pointer adapters (events→queue ONLY), built-in DOM reflectors.\n * Import wall: @ice/core + @ice/kernel down only — never react/three (enforced).\n */\nexport const DOM_VERSION = \"0.0.0\";\n\n// The canvas host (design-004 §1; M3: one content plane).\nexport { createCanvasHost, type CanvasHost } from \"./host\";\n\n// The lifted plane (design-004 §1, P3 — M6): host + lifted for drag-promote.\nexport { createPlanes, type Planes } from \"./planes\";\n\n// Built-in DOM reflectors (design-002 §5, post-notify output only).\nexport { createPlaneTransformReflector, type CameraPlanes } from \"./reflectors/plane-transform\";\nexport { createGrayboxReflector } from \"./reflectors/graybox\";\n\n// The DOM widget host layer (design-004 §2): host divs reconciled against the\n// engine mount store; React portals target each host's content element.\nexport {\n createDomWidgetsReflector,\n type DomWidgetsHost,\n type DomWidgetsReflector,\n} from \"./reflectors/dom-widgets\";\n\n// The rAF frame loop (design-002 §1: the platform owns the loop).\nexport { startRafLoop } from \"./loop\";\n\n// The L0 pointer adapter (design-003 §2–§3): DOM events → InputQueue, nothing more.\nexport { attachPointerAdapter, type GLRoute, type PointerAdapterOpts } from \"./pointer-adapter\";\n\n// L4 cursor projection output (design-003 §7: local cursor = OS cursor, one write).\nexport { createCursorReflector } from \"./reflectors/cursor\";\n// P5 remote cursors (design-004 §1: screen-space pooled nodes; M9 presence).\nexport { createRemoteCursorsReflector, type RemoteCursorsReflector } from \"./reflectors/remote-cursors\";\n\n// P0 ground-plane grid (design-004 §1: WebGL canvas, camera uniforms, fullscreen-triangle shader).\nexport { createGridReflector, DEFAULT_GRID_CONFIG, type GridConfig } from \"./reflectors/grid\";\n\n// P0 wires pass (design-004 §1): screen-space canvas under content — bezier wires\n// (geometry from kernel anchors, culled endpoints included), connect preview, port dots.\nexport {\n createWiresReflector,\n DEFAULT_WIRES_CONFIG,\n type WirePreview,\n type WiresConfig,\n type WiresReflector,\n} from \"./reflectors/wires\";\n\n// M6 chrome plane reflector (P4) + measurement adapter (design-004 §2 measure, §5 chrome).\nexport { createChromeReflector } from \"./reflectors/chrome\";\nexport { attachMeasureAdapter, type MeasureAdapter } from \"./measure-adapter\";\n// M6 measurement wiring: mount-store → observe/unobserve + reconnect-on-show (design-004 §2).\nexport { wireMeasurement, type MeasureWiringHosts, type MeasureWiringOpts } from \"./measure-wiring\";\n"],"mappings":"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","names":["writeGeom"]}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The engine devtools panel (design-005 §4, M10).
|
|
3
|
-
*
|
|
4
|
-
* A standalone, dependency-free DOM panel that reads the world OUTSIDE the tick
|
|
5
|
-
* — a verified-unrestricted read (reads are never gated; only writes are) — on a
|
|
6
|
-
* fixed interval (250ms default). It is deliberately NOT a reflector: it never
|
|
7
|
-
* registers with the reflector registry, never writes ECS, and refreshes on its
|
|
8
|
-
* own timer rather than post-notify. Four engine tabs (design-005 §4):
|
|
9
|
-
*
|
|
10
|
-
* POINTERS live L0 pointers + L2 recognizers (kind, phase, watches, claims, routes)
|
|
11
|
-
* PLANES reflectors flushed last frame + key entity-class counts
|
|
12
|
-
* SOVEREIGNTY per-prefab store class + live count; a selected type's per-component badges
|
|
13
|
-
* LOOP per-system run/skip/µs from engine telemetry, sorted by cost
|
|
14
|
-
*
|
|
15
|
-
* Telemetry: attach arms `engine.enableTelemetry()` by default (idempotent). The
|
|
16
|
-
* LOOP tab and the PLANES reflector-flushed list both read `engine.lastFrame()`,
|
|
17
|
-
* which is only populated once telemetry is armed and one step has run. Pass
|
|
18
|
-
* `{ telemetry: false }` to leave it off — those two feeds then read as empty.
|
|
19
|
-
*
|
|
20
|
-
* Rendering is intentionally un-optimized: each refresh rebuilds the active tab's
|
|
21
|
-
* content from scratch (via createElement + textContent — never innerHTML, since
|
|
22
|
-
* pointer/prefab ids and component names are user/pack strings) once per 250ms.
|
|
23
|
-
* That churn is a non-issue for a dev panel and keeps the reconcile trivial.
|
|
24
|
-
*
|
|
25
|
-
* Doc-sovereignty seams: the panel never imports doc internals. `keyOf` maps an
|
|
26
|
-
* entity to its durable key (shown in the sovereignty detail header) and
|
|
27
|
-
* `cellInDoc` reports whether a given component cell is actually committed to the
|
|
28
|
-
* doc (badge "doc"); without them the badge falls back to the prefab registry's
|
|
29
|
-
* static eligible set ("doc?" vs "rt").
|
|
30
|
-
*/
|
|
31
|
-
import { type Component, type Engine, type Entity } from "@ice/core";
|
|
32
|
-
export interface DevtoolsOpts {
|
|
33
|
-
/** Where to mount the panel (default: document.body). */
|
|
34
|
-
readonly container?: HTMLElement;
|
|
35
|
-
/** Refresh cadence in ms (default: 250). */
|
|
36
|
-
readonly intervalMs?: number;
|
|
37
|
-
/** Entity → durable key, for the sovereignty detail header (no doc import needed). */
|
|
38
|
-
readonly keyOf?: (e: Entity) => unknown;
|
|
39
|
-
/** True when component `c`'s cell on `e` is actually committed to the doc (badge "doc"). */
|
|
40
|
-
readonly cellInDoc?: (e: Entity, c: Component) => boolean;
|
|
41
|
-
/** Arm engine telemetry on attach (default: true — the LOOP/PLANES feeds need it). */
|
|
42
|
-
readonly telemetry?: boolean;
|
|
43
|
-
}
|
|
44
|
-
export interface DevtoolsHandle {
|
|
45
|
-
/** Remove the panel from the DOM and stop the refresh timer. */
|
|
46
|
-
detach(): void;
|
|
47
|
-
}
|
|
48
|
-
export declare function attachDevtools(engine: Engine, opts?: DevtoolsOpts): DevtoolsHandle;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The grid reflector (design-004 §1 P0 ground plane; M6).
|
|
3
|
-
*
|
|
4
|
-
* Ports v1's dot-grid shader VERBATIM from
|
|
5
|
-
* `../../../infinite-canvas/packages/infinite-canvas/src/webgl/renderers/GridRenderer.ts`:
|
|
6
|
-
* the fragment shader body (world reconstruction from `gl_FragCoord`, three
|
|
7
|
-
* fade-staged spacing levels, max-composited anti-aliased dots) is copied
|
|
8
|
-
* unchanged from GridRenderer.ts:45-115, and `DEFAULT_GRID_CONFIG` from
|
|
9
|
-
* GridRenderer.ts:27-35. v1 drove this through three.js (`ShaderMaterial` +
|
|
10
|
-
* `WebGLRenderer`), but `@ice/dom` may not import three (the `dom-only-core-
|
|
11
|
-
* kernel` cruiser rule: core + kernel only) — so this reflector talks to a raw
|
|
12
|
-
* WebGL2/WebGL1 context directly. Same GLSL, same uniforms, same math; only the
|
|
13
|
-
* plumbing that fed them (three's abstractions) is gone. The vertex shader is
|
|
14
|
-
* simplified from a vec3 attribute (three's default) to vec2 — geometry only,
|
|
15
|
-
* no shading logic, so "verbatim" is unaffected.
|
|
16
|
-
*
|
|
17
|
-
* P0 is a WebGL `<canvas>` mounted as the FIRST child of the host container
|
|
18
|
-
* (design-004 §1: "ground" sits under P1's content-DOM). It is sized to the
|
|
19
|
-
* container's CSS box × devicePixelRatio via a ResizeObserver — its own,
|
|
20
|
-
* independent hit-path; the reflector's `flush` never reads layout (Law 10),
|
|
21
|
-
* only the RO callback does, and only in response to the browser's own resize
|
|
22
|
-
* notification. Camera uniforms (world top-left + zoom) come from the `Camera`
|
|
23
|
-
* RESOURCE, mirroring plane-transform.ts's `observe` pattern; unlike P1's
|
|
24
|
-
* single CSS transform, this plane recomputes per-pixel in the shader — the
|
|
25
|
-
* world is regenerated from `gl_FragCoord` every draw, so there are no seams
|
|
26
|
-
* at any zoom/pan.
|
|
27
|
-
*
|
|
28
|
-
* Fault isolation, locally too: `getContext` returns `null` in headless/happy-
|
|
29
|
-
* dom (jsdom/happy-dom implement `<canvas>` but not WebGL) — `available()`
|
|
30
|
-
* reports that, and `flush()` is a permanent, silent no-op. GL setup and every
|
|
31
|
-
* draw call are wrapped so a driver/context-loss failure degrades to
|
|
32
|
-
* `available() === false` rather than a throwing reflector (the registry has
|
|
33
|
-
* its own fault isolation — a skipped frame on throw — but a reflector that
|
|
34
|
-
* throws every single frame is still noise, design-002 §5).
|
|
35
|
-
*/
|
|
36
|
-
import { type ReflectorDef } from "@ice/core";
|
|
37
|
-
export interface GridConfig {
|
|
38
|
-
/** World-unit spacings for up to 3 grid levels [fine, medium, coarse]. */
|
|
39
|
-
spacings: [number, number, number];
|
|
40
|
-
/** Dot RGB color as [r, g, b] in 0-1 range. */
|
|
41
|
-
dotColor: [number, number, number];
|
|
42
|
-
/** Base dot opacity multiplier (0-1). */
|
|
43
|
-
dotAlpha: number;
|
|
44
|
-
/** CSS-pixel range where a grid level fades in: [start, end]. */
|
|
45
|
-
fadeIn: [number, number];
|
|
46
|
-
/** CSS-pixel range where a grid level fades out: [start, end]. */
|
|
47
|
-
fadeOut: [number, number];
|
|
48
|
-
/** Dot radius range in CSS pixels [min, max]. Scaled by DPR internally. */
|
|
49
|
-
dotRadius: [number, number];
|
|
50
|
-
/** Per-level opacity weight: level i gets (base + i * step). */
|
|
51
|
-
levelWeight: [number, number];
|
|
52
|
-
}
|
|
53
|
-
/** Verbatim from GridRenderer.ts:27-35 (tuned to match Apple Freeform / FigJam). */
|
|
54
|
-
export declare const DEFAULT_GRID_CONFIG: GridConfig;
|
|
55
|
-
interface GridHost {
|
|
56
|
-
readonly container: HTMLElement;
|
|
57
|
-
}
|
|
58
|
-
export declare function createGridReflector(host: GridHost, opts?: Partial<GridConfig>): ReflectorDef & {
|
|
59
|
-
available(): boolean;
|
|
60
|
-
dispose(): void;
|
|
61
|
-
configure(next: Partial<GridConfig>): void;
|
|
62
|
-
};
|
|
63
|
-
export {};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The wires reflector (design-004 §1 P0 "WIRES" pass; design-001 §5.3).
|
|
3
|
-
*
|
|
4
|
-
* P0 is the ground stratum: the grid canvas paints first, the wires canvas next
|
|
5
|
-
* (this file), the content plane on top. Wires therefore render UNDER widget
|
|
6
|
-
* content — a node graph's edges sit behind its nodes — with no z-index
|
|
7
|
-
* bookkeeping: the wires canvas is inserted immediately before the content plane
|
|
8
|
-
* (after the grid canvas), so DOM order is the strata order (§1: stacking is
|
|
9
|
-
* stratified). Like the grid, it is a SCREEN-space canvas covering the full
|
|
10
|
-
* viewport; the camera is applied per-draw via the kernel `worldToScreen` seam
|
|
11
|
-
* rather than by a CSS plane transform, so panning re-strokes the curves at any
|
|
12
|
-
* zoom with no seams.
|
|
13
|
-
*
|
|
14
|
-
* Wire geometry NEVER reads a port ENTITY (design-001 §5.3): endpoints resolve
|
|
15
|
-
* from the wire's `WireFrom`/`WireTo` widgets (durable `Position`/`Size`) and the
|
|
16
|
-
* two `WirePorts` ids looked up against the widget type's static port schema
|
|
17
|
-
* (`PrefabId` -> `widgets.get(type).ports`); the kernel `portAnchor` places each
|
|
18
|
-
* anchor on the widget rect and `wireCubic` builds the curve. Because that path
|
|
19
|
-
* touches only durable geometry + the type registry — never a runtime port
|
|
20
|
-
* entity — a wire whose endpoint is CULLED (off-viewport, no DOM host, no
|
|
21
|
-
* materialized port) still draws by construction. This mirrors core's audited
|
|
22
|
-
* `systems/l1-wires.ts` endpoint resolver, so pick and render agree. The connect-
|
|
23
|
-
* drag preview and the materialized port dots share this one canvas.
|
|
24
|
-
*
|
|
25
|
-
* Change-only (design-002 §5): the reflector is `always: true` (the connect
|
|
26
|
-
* preview is an out-of-ECS buffer with no ECS stamp, exactly like the marquee
|
|
27
|
-
* buffer the chrome reflector consumes), but a full re-stroke happens only when
|
|
28
|
-
* something visible changed — private observers on Camera / [Position,Size] /
|
|
29
|
-
* [Wire] / [Selected] / ports / [RoutedConnect] arm a dirty flag, and the preview
|
|
30
|
-
* buffer is diffed by value each frame. A static scene with an idle preview
|
|
31
|
-
* re-strokes zero times after first paint.
|
|
32
|
-
*
|
|
33
|
-
* Fault isolation mirrors the grid: `getContext("2d")` returns `null` in happy-
|
|
34
|
-
* dom (and on context loss), so `available()` reports false and `flush` is a
|
|
35
|
-
* permanent silent no-op; every draw is wrapped so a driver failure degrades to
|
|
36
|
-
* unavailable rather than a throwing reflector (design-002 §5).
|
|
37
|
-
*
|
|
38
|
-
* Law 10: reflectors run post-notify, write output only, never read layout or
|
|
39
|
-
* write ECS — this flush touches only the wires canvas 2D context.
|
|
40
|
-
*/
|
|
41
|
-
import { type ReflectorDef, type World } from "@ice/core";
|
|
42
|
-
import type { CanvasHost } from "../host";
|
|
43
|
-
/**
|
|
44
|
-
* The connect-drag preview buffer (structural mirror of the core
|
|
45
|
-
* `WirePreviewBuffer` the graph-systems slice produces in
|
|
46
|
-
* `core/src/systems/l3-connect.ts`, exposed on `installInteractionStack` as
|
|
47
|
-
* `wirePreview`). Coords are WORLD-space; `active` gates drawing; `compatible`
|
|
48
|
-
* picks solid (snapped to a compatible port) vs dashed (free / incompatible).
|
|
49
|
-
*
|
|
50
|
-
* TODO(integration): once core exports its buffer type, import it here and drop
|
|
51
|
-
* this local — the lead reconciles field names if they diverge.
|
|
52
|
-
*/
|
|
53
|
-
export interface WirePreview {
|
|
54
|
-
/** Source anchor (world). */
|
|
55
|
-
sx: number;
|
|
56
|
-
sy: number;
|
|
57
|
-
/** Target / cursor (world). */
|
|
58
|
-
tx: number;
|
|
59
|
-
ty: number;
|
|
60
|
-
active: boolean;
|
|
61
|
-
compatible: boolean;
|
|
62
|
-
}
|
|
63
|
-
/** Minimal-neutral wire styling (design-004 §6 chrome is subdued; wires defer to content). */
|
|
64
|
-
export interface WiresConfig {
|
|
65
|
-
/** Default wire stroke (CSS color). */
|
|
66
|
-
wireColor: string;
|
|
67
|
-
/** Accent stroke for a Selected wire. */
|
|
68
|
-
selectedColor: string;
|
|
69
|
-
wireWidth: number;
|
|
70
|
-
selectedWidth: number;
|
|
71
|
-
/** Materialized-port dot radius (CSS px) + colors (idle vs during a connect drag). */
|
|
72
|
-
portRadius: number;
|
|
73
|
-
portColor: string;
|
|
74
|
-
portActiveColor: string;
|
|
75
|
-
}
|
|
76
|
-
export declare const DEFAULT_WIRES_CONFIG: WiresConfig;
|
|
77
|
-
export interface WiresReflector extends ReflectorDef {
|
|
78
|
-
/** False under happy-dom / on context loss — flush is then a silent no-op. */
|
|
79
|
-
available(): boolean;
|
|
80
|
-
/** Full re-stroke count (the churn instrument): one per frame that actually redrew, zero on a static idle scene. */
|
|
81
|
-
redraws(): number;
|
|
82
|
-
/** Detach the canvas + tear down the private observers and ResizeObserver. */
|
|
83
|
-
dispose(): void;
|
|
84
|
-
}
|
|
85
|
-
export declare function createWiresReflector(host: CanvasHost, world: World, opts?: {
|
|
86
|
-
readPreview?: () => WirePreview;
|
|
87
|
-
config?: Partial<WiresConfig>;
|
|
88
|
-
}): WiresReflector;
|