@vibecook/ice 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/dist/chunk-5S7GSXJG.js +6003 -0
  4. package/dist/chunk-5S7GSXJG.js.map +1 -0
  5. package/dist/chunk-BCJ47SX7.js +550 -0
  6. package/dist/chunk-BCJ47SX7.js.map +1 -0
  7. package/dist/chunk-FIAXPNNY.js +1446 -0
  8. package/dist/chunk-FIAXPNNY.js.map +1 -0
  9. package/dist/devtools.js +417 -0
  10. package/dist/devtools.js.map +1 -0
  11. package/dist/dom.js +41 -0
  12. package/dist/dom.js.map +1 -0
  13. package/dist/index.js +450 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/kernel.js +63 -0
  16. package/dist/kernel.js.map +1 -0
  17. package/dist/r3f.js +1101 -0
  18. package/dist/r3f.js.map +1 -0
  19. package/dist/react.js +440 -0
  20. package/dist/react.js.map +1 -0
  21. package/dist/types/packages/core/src/boot/hmr.d.ts +12 -0
  22. package/dist/types/packages/core/src/catalog/camera-derived.d.ts +144 -0
  23. package/dist/types/packages/core/src/catalog/gesture.d.ts +254 -0
  24. package/dist/types/packages/core/src/catalog/graph.d.ts +55 -0
  25. package/dist/types/packages/core/src/catalog/index.d.ts +32 -0
  26. package/dist/types/packages/core/src/catalog/pointer.d.ts +111 -0
  27. package/dist/types/packages/core/src/catalog/scene.d.ts +34 -0
  28. package/dist/types/packages/core/src/catalog/selection-presence.d.ts +53 -0
  29. package/dist/types/packages/core/src/catalog/settings-resources.d.ts +46 -0
  30. package/dist/types/packages/core/src/doc/autosave.d.ts +69 -0
  31. package/dist/types/packages/core/src/doc/bootstrap.d.ts +74 -0
  32. package/dist/types/packages/core/src/doc/broadcast-relay.d.ts +52 -0
  33. package/dist/types/packages/core/src/doc/channels.d.ts +49 -0
  34. package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +22 -0
  35. package/dist/types/packages/core/src/doc/doc-kit.d.ts +50 -0
  36. package/dist/types/packages/core/src/doc/envelope.d.ts +36 -0
  37. package/dist/types/packages/core/src/doc/migrate.d.ts +69 -0
  38. package/dist/types/packages/core/src/doc/version-gate.d.ts +49 -0
  39. package/dist/types/packages/core/src/engine/commit-sink.d.ts +61 -0
  40. package/dist/types/packages/core/src/engine/engine.d.ts +62 -0
  41. package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -0
  42. package/dist/types/packages/core/src/engine/instantiate.d.ts +45 -0
  43. package/dist/types/packages/core/src/engine/pipeline.d.ts +17 -0
  44. package/dist/types/packages/core/src/engine/reflectors.d.ts +56 -0
  45. package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +120 -0
  46. package/dist/types/packages/core/src/guards/dev.d.ts +2 -0
  47. package/dist/types/packages/core/src/guards/guarded-tx.d.ts +51 -0
  48. package/dist/types/packages/core/src/guards/live-writer.d.ts +33 -0
  49. package/dist/types/packages/core/src/guards/resource-writer.d.ts +17 -0
  50. package/dist/types/packages/core/src/helpers/phase-set.d.ts +70 -0
  51. package/dist/types/packages/core/src/helpers/version-stamps.d.ts +55 -0
  52. package/dist/types/packages/core/src/index.d.ts +63 -0
  53. package/dist/types/packages/core/src/input/measure-queue.d.ts +30 -0
  54. package/dist/types/packages/core/src/input/queue.d.ts +60 -0
  55. package/dist/types/packages/core/src/interaction/install.d.ts +58 -0
  56. package/dist/types/packages/core/src/nav/nested-canvas.d.ts +62 -0
  57. package/dist/types/packages/core/src/ops/cascade.d.ts +18 -0
  58. package/dist/types/packages/core/src/ops/claims.d.ts +15 -0
  59. package/dist/types/packages/core/src/ops/gestures.d.ts +10 -0
  60. package/dist/types/packages/core/src/ops/point-pick.d.ts +42 -0
  61. package/dist/types/packages/core/src/ops/port-geometry.d.ts +17 -0
  62. package/dist/types/packages/core/src/ops/selection.d.ts +14 -0
  63. package/dist/types/packages/core/src/presence/index.d.ts +7 -0
  64. package/dist/types/packages/core/src/presence/presence-kit.d.ts +39 -0
  65. package/dist/types/packages/core/src/presence/publish.d.ts +14 -0
  66. package/dist/types/packages/core/src/presence/remote-cursors.d.ts +18 -0
  67. package/dist/types/packages/core/src/schema/meta.d.ts +43 -0
  68. package/dist/types/packages/core/src/schema/prefab.d.ts +69 -0
  69. package/dist/types/packages/core/src/settings/defaults.d.ts +56 -0
  70. package/dist/types/packages/core/src/systems/camera-sim.d.ts +19 -0
  71. package/dist/types/packages/core/src/systems/chrome.d.ts +48 -0
  72. package/dist/types/packages/core/src/systems/cleanup.d.ts +19 -0
  73. package/dist/types/packages/core/src/systems/l0-input.d.ts +31 -0
  74. package/dist/types/packages/core/src/systems/l1-pick.d.ts +44 -0
  75. package/dist/types/packages/core/src/systems/l1-wires.d.ts +30 -0
  76. package/dist/types/packages/core/src/systems/l2-arbitrate.d.ts +40 -0
  77. package/dist/types/packages/core/src/systems/l2-recognize.d.ts +45 -0
  78. package/dist/types/packages/core/src/systems/l3-behave.d.ts +24 -0
  79. package/dist/types/packages/core/src/systems/l3-claim.d.ts +26 -0
  80. package/dist/types/packages/core/src/systems/l3-connect.d.ts +57 -0
  81. package/dist/types/packages/core/src/systems/l3-draw.d.ts +21 -0
  82. package/dist/types/packages/core/src/systems/l3-drop.d.ts +21 -0
  83. package/dist/types/packages/core/src/systems/l3-marquee.d.ts +35 -0
  84. package/dist/types/packages/core/src/systems/l3-ports.d.ts +30 -0
  85. package/dist/types/packages/core/src/systems/l3-resize.d.ts +17 -0
  86. package/dist/types/packages/core/src/systems/l3-snap.d.ts +19 -0
  87. package/dist/types/packages/core/src/systems/l4-cursor.d.ts +24 -0
  88. package/dist/types/packages/core/src/systems/measure-ingest.d.ts +26 -0
  89. package/dist/types/packages/core/src/tools/define-tool.d.ts +79 -0
  90. package/dist/types/packages/core/src/widget/define-widget.d.ts +124 -0
  91. package/dist/types/packages/core/src/widget/equip.d.ts +14 -0
  92. package/dist/types/packages/core/src/widget/mount-store.d.ts +47 -0
  93. package/dist/types/packages/core/src/widget/props.d.ts +98 -0
  94. package/dist/types/packages/core/src/widget/spawn.d.ts +30 -0
  95. package/dist/types/packages/devtools/src/index.d.ts +8 -0
  96. package/dist/types/packages/devtools/src/panel.d.ts +48 -0
  97. package/dist/types/packages/dom/src/host.d.ts +24 -0
  98. package/dist/types/packages/dom/src/index.d.ts +19 -0
  99. package/dist/types/packages/dom/src/loop.d.ts +11 -0
  100. package/dist/types/packages/dom/src/measure-adapter.d.ts +31 -0
  101. package/dist/types/packages/dom/src/measure-wiring.d.ts +49 -0
  102. package/dist/types/packages/dom/src/planes.d.ts +33 -0
  103. package/dist/types/packages/dom/src/pointer-adapter.d.ts +48 -0
  104. package/dist/types/packages/dom/src/reflectors/chrome.d.ts +33 -0
  105. package/dist/types/packages/dom/src/reflectors/cursor.d.ts +21 -0
  106. package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +54 -0
  107. package/dist/types/packages/dom/src/reflectors/graybox.d.ts +31 -0
  108. package/dist/types/packages/dom/src/reflectors/grid.d.ts +63 -0
  109. package/dist/types/packages/dom/src/reflectors/plane-transform.d.ts +38 -0
  110. package/dist/types/packages/dom/src/reflectors/remote-cursors.d.ts +20 -0
  111. package/dist/types/packages/dom/src/reflectors/wires.d.ts +88 -0
  112. package/dist/types/packages/ice/src/devtools.d.ts +1 -0
  113. package/dist/types/packages/ice/src/dom.d.ts +1 -0
  114. package/dist/types/packages/ice/src/index.d.ts +1 -0
  115. package/dist/types/packages/ice/src/kernel.d.ts +1 -0
  116. package/dist/types/packages/ice/src/r3f.d.ts +1 -0
  117. package/dist/types/packages/ice/src/react.d.ts +1 -0
  118. package/dist/types/packages/kernel/src/anchors.d.ts +18 -0
  119. package/dist/types/packages/kernel/src/bezier.d.ts +38 -0
  120. package/dist/types/packages/kernel/src/coords.d.ts +67 -0
  121. package/dist/types/packages/kernel/src/eviction.d.ts +23 -0
  122. package/dist/types/packages/kernel/src/index.d.ts +13 -0
  123. package/dist/types/packages/kernel/src/shapes.d.ts +22 -0
  124. package/dist/types/packages/kernel/src/snap.d.ts +44 -0
  125. package/dist/types/packages/kernel/src/spatial-index.d.ts +16 -0
  126. package/dist/types/packages/kernel/src/zoom-bands.d.ts +19 -0
  127. package/dist/types/packages/r3f/src/bridge.d.ts +68 -0
  128. package/dist/types/packages/r3f/src/composite-material.d.ts +17 -0
  129. package/dist/types/packages/r3f/src/compositor-pass.d.ts +83 -0
  130. package/dist/types/packages/r3f/src/dev-write-trap.d.ts +30 -0
  131. package/dist/types/packages/r3f/src/gl-root.d.ts +21 -0
  132. package/dist/types/packages/r3f/src/gl-router.d.ts +60 -0
  133. package/dist/types/packages/r3f/src/index.d.ts +16 -0
  134. package/dist/types/packages/r3f/src/island-state.d.ts +68 -0
  135. package/dist/types/packages/r3f/src/island.d.ts +13 -0
  136. package/dist/types/packages/r3f/src/pool.d.ts +56 -0
  137. package/dist/types/packages/r3f/src/resource-registry.d.ts +30 -0
  138. package/dist/types/packages/r3f/src/use-island-frame.d.ts +24 -0
  139. package/dist/types/packages/react/src/engine-context.d.ts +31 -0
  140. package/dist/types/packages/react/src/hooks.d.ts +40 -0
  141. package/dist/types/packages/react/src/index.d.ts +14 -0
  142. package/dist/types/packages/react/src/infinite-canvas.d.ts +71 -0
  143. package/dist/types/packages/react/src/keymap.d.ts +42 -0
  144. package/dist/types/packages/react/src/use-commit.d.ts +35 -0
  145. package/dist/types/packages/react/src/use-presence.d.ts +23 -0
  146. package/dist/types/packages/react/src/use-tool.d.ts +4 -0
  147. package/dist/types/packages/react/src/widget-root.d.ts +33 -0
  148. package/package.json +112 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The real L0 pointer adapter (design-003 §2–§3; replaces the M3 demo-input scaffold).
3
+ *
4
+ * Translates DOM pointer / wheel / keyboard events into normalized `InputEvent`s
5
+ * and ONLY enqueues them (Law 2: adapters enqueue, they never touch the world —
6
+ * this file imports no ECS state and writes nothing). `pointerIngest` drains the
7
+ * queue once per tick and turns the facts into world state.
8
+ *
9
+ * Details that matter:
10
+ * - stable pointer ids: `"mouse"` for the one mouse, `"touch:<pointerId>"` per
11
+ * contact, `"pen"` — ingest keys pointer entities off these (design-003 §2);
12
+ * - container-relative CSS px (the kernel screen space) via getBoundingClientRect;
13
+ * - `setPointerCapture` on down so a drag keeps delivering outside the element;
14
+ * - Space is the pan modifier (design-003 §4.4): tracked here and threaded onto
15
+ * every event's `mods.space`; keydown/keyup emit a `key` fact only when the
16
+ * modifier tuple actually changes, and Space `preventDefault`s to stop page scroll;
17
+ * - wheel `preventDefault`s: a ctrl-wheel / trackpad pinch becomes `wheel.pinch`
18
+ * (the zoom signal), a plain wheel becomes dx/dy (deltaMode lines → px ×16);
19
+ * - window blur enqueues a synthetic `cancel` for every pointer still down
20
+ * (design-003 §8) — the browser will not send the pointerups.
21
+ *
22
+ * Widget opt-out (the pinned widget-event contract, design-002 §8 / design-004 §4):
23
+ * on `pointerdown` ONLY, if the event's target chain (up to the container) crosses
24
+ * a native interactive (input/textarea/select/button/a[href]/contenteditable/
25
+ * media[controls]) or an explicit `[data-canvas-interactive]`, the down fact is
26
+ * flagged `surfaceHandled: true` — the fact still lands (the one input path is
27
+ * absolute), but recognizer-spawn/arbitration skip it (`HandledByWidget` flows
28
+ * through L0). `stopPropagation()` inside widget content is the OTHER boundary:
29
+ * a stopped event never reaches the container listener, so it never becomes a
30
+ * canvas fact at all — that is the design, not a gap.
31
+ */
32
+ import { type InputQueue } from "@ice/core";
33
+ import type { CanvasHost } from "./host";
34
+ /**
35
+ * The router GL path's adapter seam (design-004 §4). The GL plane is
36
+ * `pointer-events: none` — its events land HERE, and the injected router
37
+ * (built by @ice/r3f; the adapter stays ECS-free) performs the synchronous
38
+ * point-pick + island raycast + synthetic dispatch at event time. Returning
39
+ * `true` means island content claimed the event (stopPropagation'd or held
40
+ * under island capture): the fact still lands — flagged `surfaceHandled`, so
41
+ * recognizers skip it via `HandledByWidget` — the same contract as the
42
+ * native-interactive opt-out.
43
+ */
44
+ export type GLRoute = (kind: "down" | "move" | "up" | "cancel", screenX: number, screenY: number, native: PointerEvent) => boolean;
45
+ export interface PointerAdapterOpts {
46
+ readonly glRoute?: GLRoute;
47
+ }
48
+ export declare function attachPointerAdapter(host: CanvasHost, queue: InputQueue, opts?: PointerAdapterOpts): () => void;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The chrome reflector (design-004 §5 chrome plane P4; post-notify, SCREEN-space).
3
+ *
4
+ * Draws the engine's pooled chrome entities plus the out-of-ECS marquee buffer
5
+ * into a dedicated, non-transformed overlay plane above the content plane:
6
+ * - each `SelectionBox` entity → a 1px outline rect (its world x,y,w,h mapped to
7
+ * screen via `worldToScreen` × zoom);
8
+ * - each `HandleSpec` entity → an 8×8 screen-px square CENTERED on the handle's
9
+ * world AABB center (the handle's world size is zoom-scaled for picking; the
10
+ * drawn size is screen-constant);
11
+ * - the `marqueeBuffer.rect` (world) → a rect while non-null.
12
+ *
13
+ * P4 is screen-space by design (design-004 §2 rescope): unlike the content plane
14
+ * (one camera transform), chrome is recomputed per camera change — small N. This
15
+ * reflector therefore reads the `Camera` resource directly (an acknowledged
16
+ * small-N screen-space camera consumer).
17
+ *
18
+ * `always: true`, not observer-driven: the marquee buffer has no ECS stamp to
19
+ * observe, so the reflector must get a chance every frame. A cheap early-out
20
+ * keeps it idle-free — it returns immediately when there is nothing to draw AND
21
+ * nothing was drawn last frame (so a cleared selection still gets one flush to
22
+ * remove its nodes). Style writes are change-only (per-node geometry cache) and
23
+ * counted by `writes()` — the churn instrument, mirroring the other reflectors.
24
+ *
25
+ * Law 10: reflectors write output only, never read layout or write ECS — this
26
+ * flush touches only the overlay plane's node styles.
27
+ */
28
+ import { type MarqueeBuffer, type ReflectorDef, type World } from "@ice/core";
29
+ import type { CanvasHost } from "../host";
30
+ export declare function createChromeReflector(host: CanvasHost, _world: World, marqueeBuffer: MarqueeBuffer): ReflectorDef & {
31
+ writes(): number;
32
+ dispose(): void;
33
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The cursor reflector (design-003 §7 L4 cursor projection; v1 approach:
3
+ * local cursor = OS cursor via one `style.cursor` write).
4
+ *
5
+ * `createCursorSync` resolves the cursor string into closure state every
6
+ * `derive` tick with no observable ECS stamp (a plain string, not a
7
+ * component/resource write), so there is nothing for `observe` to watch —
8
+ * hence `always: true` with an internal last-value cache, mirroring the
9
+ * "cheap-overlay style" carve-out `ReflectorDef.always` exists for. The cache
10
+ * is what keeps the write ONE-per-change: `flush` runs every frame, but
11
+ * `host.container.style.cursor` is only touched when `readCursor()` differs
12
+ * from the last-applied value.
13
+ *
14
+ * Law 10: reflectors run post-notify, write output only, never read layout
15
+ * or write ECS — this flush touches only `host.container.style`.
16
+ */
17
+ import type { ReflectorDef } from "@ice/core";
18
+ import type { CanvasHost } from "../host";
19
+ export declare function createCursorReflector(host: CanvasHost, readCursor: () => string): ReflectorDef & {
20
+ writes(): number;
21
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The DOM widget HOST reflector (design-004 §2 host pipeline; design-002 §5
3
+ * `domWidgets`).
4
+ *
5
+ * This is the reflector-owned HALF of the widget runtime: it reconciles a host
6
+ * `<div>` per mounted widget against the engine's mount store and paints its
7
+ * world-unit geometry. The OTHER half — the React portal list — is owned by a
8
+ * hook subscribed to the SAME store (design-004 §2, decision 3); this file never
9
+ * touches React. Each host carries an inner `data-ice-content` element that the
10
+ * portal targets, so the two halves meet only at that node.
11
+ *
12
+ * Store, not query: membership (enter/exit) and the culled-but-kept-mounted
13
+ * `hidden` flag are ENGINE-side decisions (cull + keep-mounted LRU, mount-store.ts)
14
+ * published through a `useSyncExternalStore` snapshot. That store has NO ECS stamp
15
+ * for a reflector `observe` to watch, so — like the cursor reflector — this one is
16
+ * `always: true` and does its own cheap dirt detection:
17
+ * - **membership**: `store.getSnapshot()` identity is O(1); the snapshot changes
18
+ * IFF membership or a hidden flag changed, so an unchanged snapshot skips the
19
+ * reconcile entirely;
20
+ * - **geometry**: a private observer on `[Position, Size]` sets a dirty flag, so
21
+ * the (change-only) geometry pass runs only when a widget actually moved/resized
22
+ * — a still scene touches no DOM. Effective size is `MeasuredSize` where present
23
+ * and non-zero, else `Size` (design-004 §2). (MeasuredSize changes ride a
24
+ * dedicated observer; the note below is historical:
25
+ * picked up on the next Position/Size stamp or membership change; the ResizeObserver
26
+ * measurement path — a later slice — will arm its own dirt.)
27
+ * - **drag-promote** (P3): a private observer on `[Grab]` membership sets a dirty
28
+ * flag; a host whose entity holds `Grab` re-parents content→lifted plane and back
29
+ * on release. Re-parenting MOVES the host node (with its content child), which
30
+ * PRESERVES the React portal — the portal targets the content node, and moving a
31
+ * portal's container node does not remount it, so widget React state survives the
32
+ * lift. While ANY host is lifted, every host's content element is inerted
33
+ * (`pointer-events: none`, the pinned inert-during-drag contract, design-004 §4).
34
+ *
35
+ * Law 10: reflectors run post-notify, write output only, never read layout or
36
+ * write ECS — this flush touches only host `<div>` style/attributes/parentage.
37
+ */
38
+ import { type Entity, type ReflectorDef, type WidgetMountStore, type World } from "@ice/core";
39
+ /** The two planes a host can live in (design-004 §1: P1 content, P3 lifted). */
40
+ export interface DomWidgetsHost {
41
+ readonly contentPlane: HTMLElement;
42
+ readonly liftedPlane: HTMLElement;
43
+ }
44
+ export interface DomWidgetsReflector extends ReflectorDef {
45
+ /** The entity's content element (the `data-ice-content` portal target), or undefined if not hosted. */
46
+ hostFor(entity: Entity): HTMLElement | undefined;
47
+ /** Live host count (= mounted widgets, visible + kept-mounted). */
48
+ hostCount(): number;
49
+ /** Geometry writes so far — one per enter, one per changed host, zero for unchanged (churn instrument). */
50
+ geometryWrites(): number;
51
+ /** Tear down the private observers (call when unregistering the reflector before the world dies). */
52
+ dispose(): void;
53
+ }
54
+ export declare function createDomWidgetsReflector(host: DomWidgetsHost, world: World, store: WidgetMountStore): DomWidgetsReflector;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The gray-box reflector (design-002 §5 reflector doctrine; M3 exit gate).
3
+ *
4
+ * A minimal enter/update/exit reconciler: it mirrors every `Position` + `Size`
5
+ * entity to an absolutely-positioned div inside the content plane, laying each
6
+ * out in WORLD units (`left/top/width/height` in world coordinates) — the
7
+ * plane's single camera transform (see plane-transform.ts) does the screen
8
+ * mapping, so this reflector never touches the camera and never reads layout
9
+ * (Law 10). It is the stand-in for real widget rendering until the widget
10
+ * runtime lands (M4+); "gray box" = deterministic per-entity fill, no content.
11
+ *
12
+ * Reconciliation, dirty-driven on the `Position`/`Size` query (design-002 §5):
13
+ * - enter: create a div (fill hashed from the entity handle), write geometry,
14
+ * stage it in a DocumentFragment (one plane append per flush — 10k mounts do
15
+ * not thrash layout);
16
+ * - update: compare against a per-entity geometry cache and rewrite ONLY the
17
+ * entities whose world geometry actually changed;
18
+ * - exit: remove divs for entities absent from this flush.
19
+ *
20
+ * `styleWrites()` counts entities whose geometry was (re)written this flush (one
21
+ * per enter, one per changed entity, zero for unchanged) — the churn instrument
22
+ * the M3 tests assert: change one entity ⇒ exactly one style write. `nodeCount()`
23
+ * is the live div count. Because the reflector is dirty-driven, a static frame
24
+ * does not flush at all, so neither counter moves.
25
+ */
26
+ import { type ReflectorDef } from "@ice/core";
27
+ import type { CanvasHost } from "../host";
28
+ export declare function createGrayboxReflector(host: CanvasHost): ReflectorDef & {
29
+ styleWrites(): number;
30
+ nodeCount(): number;
31
+ };
@@ -0,0 +1,63 @@
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 {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The plane-transform reflector (design-002 §5 `planeTransform`; M3 exit gate,
3
+ * widened for the M6 lifted plane).
4
+ *
5
+ * Observes the `Camera` RESOURCE and nothing else. Each flush reads the camera,
6
+ * derives the ONE per-plane CSS transform via kernel `planeCssTransform` (the
7
+ * sole owner of that math — Law 13), and writes it to EVERY camera-transformed
8
+ * DOM plane in a SINGLE `style.transform` assignment each. Panning the whole
9
+ * canvas is therefore O(1) in the DOM: one transform write per plane per moving
10
+ * frame, independent of node count — the property that makes an infinite canvas
11
+ * of 10k gray boxes pan at frame rate.
12
+ *
13
+ * The input is widened to `{ contentPlane; liftedPlane? }` (design-004 §1: P1 +
14
+ * P3 share the one camera). A `CanvasHost` still satisfies it structurally, so
15
+ * M3 callers are unchanged; M6 passes both planes and the lifted plane (P3)
16
+ * tracks the content plane (P1) exactly, so a promoted widget keeps its world
17
+ * position across the content→lifted re-parent.
18
+ *
19
+ * `transformWrites()` is the M3 instrument: because the reflector is dirty-
20
+ * driven on the Camera resource (equality-suppressed — design-002 §5), a static
21
+ * camera flushes zero times and the counter stays flat; every distinct camera
22
+ * value bumps it exactly once. The frame-contract tests assert both halves.
23
+ *
24
+ * Law 10: reflectors run post-notify, write output only, and NEVER read layout
25
+ * or write ECS — this flush touches only `host.contentPlane.style`.
26
+ */
27
+ import { type ReflectorDef } from "@ice/core";
28
+ /**
29
+ * The camera-transformed DOM plane(s). A `CanvasHost` satisfies this
30
+ * structurally (M3 backward compat); M6 passes `liftedPlane` too so P3 tracks P1.
31
+ */
32
+ export interface CameraPlanes {
33
+ readonly contentPlane: HTMLElement;
34
+ readonly liftedPlane?: HTMLElement;
35
+ }
36
+ export declare function createPlaneTransformReflector(host: CameraPlanes): ReflectorDef & {
37
+ transformWrites(): number;
38
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * P5 — remote cursors (design-004 §1: screen-space DOM, "remote cursors are
3
+ * always custom nodes"; design-003 §7).
4
+ *
5
+ * Renders one pooled node per REMOTE cursor entity (`CursorVisual` kind
6
+ * "remote" + world `Position`, derived by core's remote-cursors system from
7
+ * presence peers; `Follows` names the peer whose `PresenceInfo` supplies
8
+ * color + label). Screen-space by design: an acknowledged small-N
9
+ * camera→position consumer (design-002 §5 rescope), recomputed on camera or
10
+ * cursor dirt only. The plane mounts LAST in the container, above chrome;
11
+ * `pointer-events: none` throughout — cursors are never hit-testable.
12
+ */
13
+ import { type ReflectorDef, type World } from "@ice/core";
14
+ import type { CanvasHost } from "../host";
15
+ export interface RemoteCursorsReflector {
16
+ readonly reflector: ReflectorDef;
17
+ /** Remove the plane + pool (doc close / HMR). */
18
+ destroy(): void;
19
+ }
20
+ export declare function createRemoteCursorsReflector(host: CanvasHost, world: World): RemoteCursorsReflector;
@@ -0,0 +1,88 @@
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;
@@ -0,0 +1 @@
1
+ export * from "../../devtools/src/index";
@@ -0,0 +1 @@
1
+ export * from "../../dom/src/index";
@@ -0,0 +1 @@
1
+ export * from "../../core/src/index";
@@ -0,0 +1 @@
1
+ export * from "../../kernel/src/index";
@@ -0,0 +1 @@
1
+ export * from "../../r3f/src/index";
@@ -0,0 +1 @@
1
+ export * from "../../react/src/index";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Port-anchor math (design-001 §5.3): wire geometry NEVER reads port entities —
3
+ * anchors are pure functions over the widget rect + the port declaration, so
4
+ * culled endpoints work by construction.
5
+ */
6
+ import type { Rect, Vec2 } from "./shapes";
7
+ export type PortSide = "n" | "e" | "s" | "w";
8
+ export interface PortSlot {
9
+ side: PortSide;
10
+ /** 0-based position among the ports sharing this side. */
11
+ index: number;
12
+ /** Total ports on this side. */
13
+ count: number;
14
+ }
15
+ /** World-space anchor point: ports distribute evenly along their side. */
16
+ export declare function portAnchor(widget: Rect, slot: PortSlot): Vec2;
17
+ /** Outward unit direction of a side (wire tangent stubs, Y-down world). */
18
+ export declare function sideDirection(side: PortSide): Vec2;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cubic-bezier wire geometry: construction from port anchors, conservative
3
+ * AABB (for the spatial index), and sampled distance (for picking — wires rank
4
+ * below widgets, design-003 §3).
5
+ */
6
+ import { type PortSide } from "./anchors";
7
+ import type { AABB, Vec2 } from "./shapes";
8
+ export interface Cubic {
9
+ x0: number;
10
+ y0: number;
11
+ x1: number;
12
+ y1: number;
13
+ x2: number;
14
+ y2: number;
15
+ x3: number;
16
+ y3: number;
17
+ }
18
+ /**
19
+ * Wire curve between two anchors: tangent stubs leave each port along its
20
+ * side's outward direction, scaled with distance (min stub keeps short wires
21
+ * readable).
22
+ */
23
+ export declare function wireCubic(from: Vec2, fromSide: PortSide, to: Vec2, toSide: PortSide, stubScale?: number, minStub?: number): Cubic;
24
+ /** Point on the curve at t ∈ [0, 1] (Bernstein form). */
25
+ export declare function cubicPoint(c: Cubic, t: number): Vec2;
26
+ /**
27
+ * Conservative bounds: the control-point hull contains the curve (convex-hull
28
+ * property). Cheap and index-friendly; exact extrema are unnecessary for
29
+ * culling/picking prefilters.
30
+ */
31
+ export declare function cubicAABB(c: Cubic): AABB;
32
+ /** Distance from a point to a line segment. */
33
+ export declare function distanceToSegment(px: number, py: number, ax: number, ay: number, bx: number, by: number): number;
34
+ /**
35
+ * Min distance from a point to the curve via polyline sampling. 32 samples is
36
+ * ample for hit thresholds of a few px; error shrinks with curve flatness.
37
+ */
38
+ export declare function distanceToCubic(px: number, py: number, c: Cubic, samples?: number): number;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * THE coordinate seam (Law 13): every conversion between screen, world, and
3
+ * island (widget-local GL) space lives here — including the ONE Y-flip.
4
+ * v1 duplicated this math in three places; nothing outside this module may
5
+ * re-derive it.
6
+ *
7
+ * Conventions:
8
+ * - screen: CSS px, canvas-container-relative, Y-down. DPR never enters world
9
+ * math (it exists only in GL uniforms / FBO sizing — see zoom-bands).
10
+ * - world: world units, Y-down. `Camera.{x,y}` is the WORLD point at the
11
+ * viewport's top-left; `zoom` is screen px per world unit.
12
+ * - island: widget-local GL space — CENTER origin, Y-UP, world-unit scale.
13
+ * (v1 VirtualWidget convention; the only Y-flip in the codebase.)
14
+ */
15
+ import type { Rect, Vec2 } from "./shapes";
16
+ export interface CameraState {
17
+ x: number;
18
+ y: number;
19
+ zoom: number;
20
+ }
21
+ export declare function screenToWorld(screenX: number, screenY: number, camera: CameraState): Vec2;
22
+ export declare function worldToScreen(worldX: number, worldY: number, camera: CameraState): Vec2;
23
+ /**
24
+ * Zoom about a fixed screen point (v1 `zoomAtPoint`): the world point under
25
+ * `(screenX, screenY)` stays under it after the zoom change.
26
+ * Returns the new camera (does not mutate). Clamping is the caller's policy.
27
+ */
28
+ export declare function zoomAtPoint(camera: CameraState, screenX: number, screenY: number, newZoom: number): CameraState;
29
+ /**
30
+ * Camera → the ONE per-plane CSS transform (design-002 §5 `planeTransform`).
31
+ * Children are laid out in WORLD units inside the plane (left/top = world x/y),
32
+ * the plane carries `transform: translate(tx px, ty px) scale(scale)` with
33
+ * `transform-origin: 0 0` — so a child at world point p lands at
34
+ * `p·zoom + t = (p − camera)·zoom`, i.e. exactly `worldToScreen(p)`.
35
+ */
36
+ export declare function planeCssTransform(camera: CameraState): {
37
+ tx: number;
38
+ ty: number;
39
+ scale: number;
40
+ };
41
+ /** World → island (center-origin, Y-up). THE Y-flip, direction one. */
42
+ export declare function worldToIsland(worldX: number, worldY: number, widget: Rect): Vec2;
43
+ /** Island → world. THE Y-flip, direction two. */
44
+ export declare function islandToWorld(islandX: number, islandY: number, widget: Rect): Vec2;
45
+ /**
46
+ * Camera → the P2 composite ortho frustum + camera position (world units,
47
+ * Y-up scene). `left/top` pin the viewport's top-left to `Camera.{x,y}`
48
+ * exactly like `planeCssTransform` does for the DOM planes: the frustum spans
49
+ * `viewport / zoom` world units and the camera sits at the world origin
50
+ * offset, so a quad at `worldRectToComposite(rect)` lands on the same screen
51
+ * pixels as a DOM host at `rect` under the plane transform.
52
+ */
53
+ export declare function compositeCameraFrustum(camera: CameraState, viewportW: number, viewportH: number): {
54
+ left: number;
55
+ right: number;
56
+ top: number;
57
+ bottom: number;
58
+ x: number;
59
+ y: number;
60
+ };
61
+ /** World rect → composite-quad center + scale in the Y-up P2 scene. */
62
+ export declare function worldRectToComposite(rect: Rect): {
63
+ x: number;
64
+ y: number;
65
+ sx: number;
66
+ sy: number;
67
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * FBO-pool eviction policy — ported from v1 `eviction.ts` (pure part of the
3
+ * pool). Priority: Cold(0) < Warm(1) < Dormant(2); LRU within a phase;
4
+ * Hot/Waking are never evicted even under an impossible budget.
5
+ */
6
+ export type IslandPhase = "Hot" | "Warm" | "Waking" | "Cold" | "Dormant";
7
+ export interface EvictionCandidate<Id> {
8
+ id: Id;
9
+ phase: IslandPhase;
10
+ bytes: number;
11
+ lastUsedMs: number;
12
+ }
13
+ /**
14
+ * The island phase truth table (design-004 §3; v1 `computePhase` verbatim —
15
+ * RFC-002 pinned it with tests). `active` is the nested-canvas membership
16
+ * signal (always true until M8); `hasFbo` splits Warm (retained texture,
17
+ * composite-ready) from Waking (must paint before compositing) — after an
18
+ * eviction a re-entering island comes back as Waking, after a plain cull
19
+ * round-trip it comes back Warm from the retained texture.
20
+ */
21
+ export declare function computeIslandPhase(active: boolean, visible: boolean, animating: boolean, hasFbo: boolean): IslandPhase;
22
+ /** Ordered ids to release so total bytes ≤ budget. Pure. */
23
+ export declare function selectEvictions<Id>(candidates: EvictionCandidate<Id>[], totalBytes: number, maxBytes: number): Id[];
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @ice/kernel — pure math. Plain structs in, plain structs out.
3
+ * Import wall: nothing but `rbush` (named exception — zero-dep R-tree).
4
+ */
5
+ export declare const KERNEL_VERSION = "0.0.0";
6
+ export * from "./shapes";
7
+ export * from "./coords";
8
+ export * from "./snap";
9
+ export * from "./spatial-index";
10
+ export * from "./zoom-bands";
11
+ export * from "./eviction";
12
+ export * from "./anchors";
13
+ export * from "./bezier";
@@ -0,0 +1,22 @@
1
+ /** Plain geometric structs shared across the kernel. Ported from v1 `ecs/math.ts`. */
2
+ export interface Vec2 {
3
+ x: number;
4
+ y: number;
5
+ }
6
+ export interface Rect {
7
+ x: number;
8
+ y: number;
9
+ width: number;
10
+ height: number;
11
+ }
12
+ export interface AABB {
13
+ minX: number;
14
+ minY: number;
15
+ maxX: number;
16
+ maxY: number;
17
+ }
18
+ export declare function rectToAABB(r: Rect): AABB;
19
+ export declare function aabbToRect(a: AABB): Rect;
20
+ export declare function intersectsAABB(a: AABB, b: AABB): boolean;
21
+ export declare function pointInAABB(px: number, py: number, a: AABB): boolean;
22
+ export declare function clamp(value: number, min: number, max: number): number;