@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,30 @@
1
+ /**
2
+ * The measurement queue — the ONE ingest path for auto-sized widget sizes
3
+ * (design-004 §2 host pipeline). Mirrors {@link ../input/queue.ts} exactly:
4
+ * pre-ingest TRANSPORT, not world state. A DOM `ResizeObserver` adapter (the
5
+ * `measure-adapter` in @ice/dom) enqueues normalized `{ entity, w, h }` samples
6
+ * as content boxes resize; `measureIngest` drains once per tick and folds them
7
+ * into the `MeasuredSize` session rider. No system reads it, nothing observes
8
+ * it, it holds nothing across a drain.
9
+ *
10
+ * Measurements are content-box sizes in CSS px (screen == world here — the
11
+ * host's content plane carries the camera transform, so a widget's content is
12
+ * laid out in world units). The adapter NEVER enqueues a `0×0` sample (from a
13
+ * `display:none` host) and ingest skips it a second time — the §2 double wall
14
+ * against a full→0 collapse corrupting bounds/cull on re-entry.
15
+ */
16
+ import type { Entity } from "@vibecook/strata-ecs";
17
+ export interface MeasureEvent {
18
+ /** The widget entity whose content element resized (a runtime handle). */
19
+ readonly entity: Entity;
20
+ readonly w: number;
21
+ readonly h: number;
22
+ }
23
+ export interface MeasureQueue {
24
+ enqueue(event: MeasureEvent): void;
25
+ /** Drain in arrival order. Called ONLY by `measureIngest`, once per tick. */
26
+ drain(): readonly MeasureEvent[];
27
+ /** Samples waiting (telemetry / the ingest `runIf` guard). */
28
+ size(): number;
29
+ }
30
+ export declare function createMeasureQueue(): MeasureQueue;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The input queue — the ONE input path's front door (Law 2, design-003 §2).
3
+ *
4
+ * Adapters (DOM pointer/wheel/keyboard, R3F router verdicts, test drivers) ONLY
5
+ * enqueue normalized {@link InputEvent}s; `pointerIngest` drains the queue once
6
+ * per tick and turns events into world facts. Handlers run mid-frame at
7
+ * arbitrary times — the queue is what keeps every world write inside the frame
8
+ * contract (design-002 §3). It is pre-ingest TRANSPORT, not world state: no
9
+ * system reads it, nothing observes it, it holds nothing across a drain.
10
+ *
11
+ * Pointer identity: `pointerId` is the adapter's stable string for a device
12
+ * pointer — `"mouse"` for the one mouse, `"touch:<n>"` per touch contact,
13
+ * `"pen"` for a stylus. Ingest keys pointer entities off it (`Pointer.id`).
14
+ */
15
+ export type InputEventKind = "down" | "move" | "up" | "cancel" | "wheel" | "key";
16
+ export interface InputMods {
17
+ readonly shift: boolean;
18
+ readonly ctrl: boolean;
19
+ readonly alt: boolean;
20
+ readonly meta: boolean;
21
+ /** Space-held (pan gesture modifier, design-003 §4.4) — not a PointerMods field. */
22
+ readonly space: boolean;
23
+ }
24
+ export interface InputEvent {
25
+ readonly kind: InputEventKind;
26
+ /** Stable adapter pointer id ("mouse" | "touch:<n>" | "pen"). Empty for kind:"key". */
27
+ readonly pointerId: string;
28
+ readonly device: "mouse" | "touch" | "pen";
29
+ /** Canvas-container-relative CSS px (the kernel screen space). */
30
+ readonly screenX: number;
31
+ readonly screenY: number;
32
+ /** Button bitmask as of this event (PointerEvent.buttons semantics). */
33
+ readonly buttons: number;
34
+ readonly mods: InputMods;
35
+ /** Wheel deltas for kind:"wheel" (`pinch` = ctrl-wheel / trackpad zoom signal). */
36
+ readonly wheel?: {
37
+ readonly dx: number;
38
+ readonly dy: number;
39
+ readonly pinch: number;
40
+ };
41
+ /** GL-island opt-out (pinned widget-event contract, design-002 §8): fact still lands, recognizers skip. */
42
+ readonly surfaceHandled?: boolean;
43
+ /**
44
+ * Event timestamp (DOMHighResTimeStamp — the same clock rAF hands
45
+ * `engine.step`). Gesture timing anchors on THIS, not the processing frame:
46
+ * a stalled main thread (GC, cold paints) otherwise eats into hold windows —
47
+ * a 640 ms stall at pointerdown made long-press unreachable (2026-07-12
48
+ * field debugging). Absent ⇒ the processing frame's now.
49
+ */
50
+ readonly tMs?: number;
51
+ }
52
+ export declare const NO_MODS: InputMods;
53
+ export interface InputQueue {
54
+ enqueue(event: InputEvent): void;
55
+ /** Drain in arrival order. Called ONLY by `pointerIngest`, once per tick. */
56
+ drain(): readonly InputEvent[];
57
+ /** Events waiting (telemetry/tests). */
58
+ size(): number;
59
+ }
60
+ export declare function createInputQueue(): InputQueue;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Interaction-stack installation (design-002 §2 phase layout × design-003).
3
+ *
4
+ * Composable: `installInteractionCore` wires the M4 spine (L0 ingest, L2
5
+ * lifecycle + kinds + arbitration, ctl:claim, select/move behaviors, cleanup).
6
+ * Later slices (picking, snap, drop, resize, marquee, camera) install through
7
+ * their own module functions into the same phase groups — group order is the
8
+ * only ordering contract between them; within-group order is the design-003 §5
9
+ * in-phase order, so later installers pass explicit `before`-free appends in
10
+ * the documented sequence.
11
+ *
12
+ * The canvas-surface anchor entity is guaranteed here (design-001 §4: picking
13
+ * always resolves to SOMETHING; L0 ingest schedules on it).
14
+ */
15
+ import type { Entity, World } from "@vibecook/strata-ecs";
16
+ import { SpatialIndex } from "@ice/kernel";
17
+ import { type CommitSink } from "../engine/commit-sink";
18
+ import type { Engine } from "../engine/engine";
19
+ import type { InputQueue } from "../input/queue";
20
+ import { type SpawnProfiles } from "../systems/l2-recognize";
21
+ import { type WirePreviewBuffer } from "../systems/l3-connect";
22
+ import { type MarqueeBuffer } from "../systems/l3-marquee";
23
+ /** Ensure the guaranteed pick-fallback entity exists (idempotent; outside tick). */
24
+ export declare function ensureCanvasSurface(world: World): Entity;
25
+ export interface InteractionCoreOpts {
26
+ readonly queue?: InputQueue;
27
+ readonly sink?: CommitSink;
28
+ readonly profiles?: SpawnProfiles;
29
+ }
30
+ export interface InteractionCore {
31
+ readonly queue: InputQueue;
32
+ readonly sink: CommitSink;
33
+ readonly canvasSurface: Entity;
34
+ /** Remove every installed system (HMR / doc switch). */
35
+ uninstall(): void;
36
+ }
37
+ export declare function installInteractionCore(engine: Engine, opts?: InteractionCoreOpts): InteractionCore;
38
+ export interface InteractionStack extends InteractionCore {
39
+ /** The one spatial index (picking/snap/drop/marquee/wires/ports share it). */
40
+ readonly index: SpatialIndex<Entity>;
41
+ /** Marquee preview render buffer (out-of-ECS by design — design-003 §5.7). */
42
+ readonly marqueeBuffer: MarqueeBuffer;
43
+ /** Connect preview render buffer (out-of-ECS — design-004 §6; M8). */
44
+ readonly wirePreview: WirePreviewBuffer;
45
+ /** Nav-op seam (design-004 §7): forget spatialSync's last-known AABBs. */
46
+ clearCaches(): void;
47
+ /** L4 cursor readout for the DOM cursor reflector. */
48
+ readCursor(): string;
49
+ }
50
+ /**
51
+ * The FULL M4 stack. Not a wrapper over `installInteractionCore`: within-group
52
+ * order is registration order and design-003 §5's in-phase sequence
53
+ * (select → snap → move → drop → resize → marquee → camera) interleaves spine
54
+ * and non-spine systems, so the full installer registers everything itself in
55
+ * the documented order. `installInteractionCore` remains the spine-only slice
56
+ * (what the spine traces run against).
57
+ */
58
+ export declare function installInteractionStack(engine: Engine, opts?: InteractionCoreOpts): InteractionStack;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Nested canvas (design-004 §7 rev 2 — "the missing systems"; design-001
3
+ * §5.7/§5.8 catalog).
4
+ *
5
+ * - `Active` = derived membership tag: a widget is Active iff the FIRST
6
+ * container ancestor on its `ChildOf` chain is the current nav frame
7
+ * (at root: no container ancestor at all). Children of a deeper container
8
+ * are that container's content — not Active until you enter it.
9
+ * - `activeMembership` (derive tick, before cull): recomputes membership and
10
+ * flips Active/Visible/Culled CHANGE-ONLY. A nav switch is O(frame
11
+ * content) tag flips — bounded, rare, migration-free. Boards without
12
+ * containers converge to "everything Active" on the first pass and never
13
+ * write again (M3–M7 demos unaffected).
14
+ * - Nav stack = SESSION entities: `NavEntry` rows carry NavDepth + NavCamera
15
+ * (the camera to restore on exit) + NavFrame(entry→container). Root =
16
+ * empty stack. Current frame = deepest live entry's NavFrame target.
17
+ * - `enterContainer`/`exitContainer` ops own the SPATIAL INDEX REBUILD:
18
+ * membership flips don't move Transforms, so the incremental spatialSync
19
+ * cache never fires on nav — the ops clear the index + caches and bump
20
+ * `SpatialVersion`; spatialSync repopulates from the new Active set on the
21
+ * next tick (its sweep-by-`seen` semantics make clear+repopulate exact).
22
+ * - `navIntegrity` (react tick, before picking): if the current frame's
23
+ * container died (undo/remote — NavFrame edge auto-cleared), pop to the
24
+ * nearest LIVE ancestor entry (skipping dead ones) or root, restore that
25
+ * camera, rebuild. The recognizer-integrity pattern applied to nav.
26
+ * - Camera memory: enter saves the live camera on the pushed entry
27
+ * (NavCamera) and applies the container's `ContainerCamera` rider (else
28
+ * zoom-to-fit over the frame's content, else identity); exit writes the
29
+ * rider back (per-user view memory; lost on undo-despawn — accepted) and
30
+ * restores the entry's NavCamera.
31
+ */
32
+ import type { Entity, TickSystem, World } from "@vibecook/strata-ecs";
33
+ import type { SpatialIndex } from "@ice/kernel";
34
+ /** Deepest live nav entry, or undefined at root. */
35
+ export declare function currentNavEntry(world: World): Entity | undefined;
36
+ /** The container the user is inside, or undefined at root. */
37
+ export declare function currentNavFrame(world: World): Entity | undefined;
38
+ export interface NestedCanvas {
39
+ readonly activeMembership: TickSystem;
40
+ readonly navIntegrity: TickSystem;
41
+ enterContainer(container: Entity): void;
42
+ exitContainer(): void;
43
+ /** Depth of the nav stack (0 = root). */
44
+ depth(): number;
45
+ }
46
+ export interface NestedCanvasOpts {
47
+ /** THE spatial index (nav ops clear it; spatialSync repopulates). */
48
+ readonly index: SpatialIndex<Entity>;
49
+ /**
50
+ * Clear spatialSync's private last-known cache alongside the index (the
51
+ * install seam wires this; without it the compare-and-skip would believe
52
+ * stale entries still exist).
53
+ */
54
+ readonly clearSpatialCaches: () => void;
55
+ }
56
+ /**
57
+ * The membership stamper alone — installed by `installWidgetRuntime` (before
58
+ * cull) so EVERY app partitions correctly, containers or not: a board with no
59
+ * containers converges to all-Active on the first pass and never writes again.
60
+ */
61
+ export declare function createActiveMembership(world: World): TickSystem;
62
+ export declare function createNestedCanvas(world: World, opts: NestedCanvasOpts): NestedCanvas;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Engine-owned destroy cascade (design-001 §5.1, §5.3, design-003 §5).
3
+ *
4
+ * strata clears an entity's edges on `tx.destroy` but does NOT recurse, so the
5
+ * engine walks containment itself: depth-first over `getReverse(e, ChildOf)`,
6
+ * destroying descendants before their parent, then the root. Wires are reified
7
+ * edge entities, so a wire whose `WireFrom`/`WireTo` endpoint dies is destroyed
8
+ * with it (reverse-index lookup, same transaction).
9
+ *
10
+ * The walk reads runtime world state via `getReverse`; `tx.destroy` only RECORDS
11
+ * (applied at seal / projection), so the reverse indices stay whole for the whole
12
+ * walk. A `visited` set guards against cycles and double-destroying a wire that
13
+ * touches two doomed endpoints.
14
+ */
15
+ import type { Entity, World } from "@vibecook/strata-ecs";
16
+ import type { Mutator } from "@vibecook/strata-ecs/durable";
17
+ /** Depth-first destroy `root` and its `ChildOf` subtree + any wires bound to a destroyed entity. */
18
+ export declare function cascadeDestroy(tx: Mutator, world: World, root: Entity): void;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The DEFAULT divergence predicate for the live-write guard (design-001 §3):
3
+ * an entity's doc cells may diverge iff
4
+ * - some recognizer in `GestureActive` currently `Captures` or `Drags` it, or
5
+ * - it carries a live `TransformTween` (fly-back holds the claim after the
6
+ * recognizer is reaped — design-003 review M3 / design-001 §3 amendment).
7
+ *
8
+ * This replaces the "inject a stub" posture (review finding A2): engines wire
9
+ * `createLiveWriter(world, { keyOf, mayDiverge: makeDefaultMayDiverge(world), … })`
10
+ * by default; tests and special hosts may still inject their own predicate.
11
+ * Discrete claims (`GestureRecognized`) deliberately do NOT grant divergence —
12
+ * design-001 §3 live-writes happen only under continuous Active gestures.
13
+ */
14
+ import type { Entity, World } from "@vibecook/strata-ecs";
15
+ export declare function makeDefaultMayDiverge(world: World): (e: Entity) => boolean;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Gesture ops — app-handler write paths (design-003 §2/§8).
3
+ *
4
+ * `cancelActiveGestures` sets the ONE-TICK CancelRequest resource: escape, tool
5
+ * switch, and doc detach all converge here; the ctl:spawn sweep transitions
6
+ * every non-terminal recognizer → Cancelled next tick, behaviors restore from
7
+ * riders, and cleanup clears the flag.
8
+ */
9
+ import type { World } from "@vibecook/strata-ecs";
10
+ export declare function cancelActiveGestures(world: World): void;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * THE point-pick (Law 15: the spatial index is the only hit path; this
3
+ * function is its only narrow-phase). Shared verbatim by the in-tick
4
+ * `picking` system (L1, via SystemCtx) and the pointer router's synchronous
5
+ * event-time pick (design-004 §4 GL path, via World — world reads outside
6
+ * the tick are verified-unrestricted). One implementation so the two can
7
+ * never disagree on ordering.
8
+ *
9
+ * Priority: HandleSpec chrome first (nearest edge), then widgets by StackZ
10
+ * descending. Deliberately NOT plane-stratified: cross-plane z is stratified
11
+ * visually (P2 GL composites above P1 DOM), but pick order is StackZ across
12
+ * all surfaces — design-004 §1 names the mixed-overlap consequence and
13
+ * accepts it; do not "fix" it here.
14
+ */
15
+ import type { Component, Entity } from "@vibecook/strata-ecs";
16
+ import type { Cubic, SpatialIndex } from "@ice/kernel";
17
+ /** Structural reads both `World` and `SystemCtx` provide. */
18
+ export interface PickReader {
19
+ isAlive(e: Entity): boolean;
20
+ has(e: Entity, c: Component): boolean;
21
+ hasTag(e: Entity, t: import("@vibecook/strata-ecs").Tag): boolean;
22
+ get<S>(e: Entity, c: Component<S>): S | undefined;
23
+ }
24
+ /** Wire narrow-phase source: cached cubics from wireSync (M8). */
25
+ export interface WirePickSource {
26
+ cubicOf(e: Entity): Cubic | undefined;
27
+ /** Pick slop in WORLD units (screen slop / zoom, caller-converted). */
28
+ readonly slopWorld: number;
29
+ }
30
+ /** Distance from a point to the nearest edge of an AABB (0 iff inside). */
31
+ export declare function distPointToBox(px: number, py: number, minX: number, minY: number, maxX: number, maxY: number): number;
32
+ /**
33
+ * Top pick under a world point within `rWorld` (point-in-box when 0).
34
+ * Tier order (design-003 §3, amended for M8): HandleSpec chrome → PORT
35
+ * entities (interaction affordances sit atop their widget edge) → widgets by
36
+ * StackZ → WIRES (render in P0 under content; pick below widgets, above
37
+ * canvas — render order and pick order agree). The canvas fallback stays the
38
+ * caller's. Wires narrow-phase against their cached cubic (`wires` source;
39
+ * without one, wire entries are skipped — coarse AABB hits would make fat
40
+ * bounding boxes steal canvas taps).
41
+ */
42
+ export declare function pickTopAt(reader: PickReader, index: SpatialIndex<Entity>, wx: number, wy: number, rWorld: number, wires?: WirePickSource): Entity | undefined;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Port-slot resolution: a widget's port DECLARATIONS (from the widget registry,
3
+ * design-005 §2) → kernel `PortSlot`s (side + even-distribution index/count).
4
+ *
5
+ * The single source of a port's world anchor is the kernel's pure
6
+ * `portAnchor(widgetRect, slot)` — geometry NEVER reads a port ENTITY (design-001
7
+ * §5.3), so wires, the connect preview, and materialized-port anchors all agree
8
+ * by construction and culled endpoints resolve for free. Ports sharing a side are
9
+ * numbered by their declared `index` when given, else by declaration order; the
10
+ * side's `count` drives the even spacing.
11
+ */
12
+ import type { PortSlot } from "@ice/kernel";
13
+ import type { WidgetPortDecl } from "../widget/define-widget";
14
+ /** Map every declared port id → its `PortSlot` (side/index/count for even spacing). */
15
+ export declare function portSlots(decls: readonly WidgetPortDecl[]): Map<string, PortSlot>;
16
+ /** The `PortSlot` for one port id (undefined if the widget declares no such port). */
17
+ export declare function portSlotOf(decls: readonly WidgetPortDecl[], portId: string): PortSlot | undefined;
@@ -0,0 +1,14 @@
1
+ import type { Entity, World } from "@vibecook/strata-ecs";
2
+ export type SelectionMode = "replace" | "toggle" | "add";
3
+ /** All entities currently carrying the `Selected` rider. */
4
+ export declare function selectedEntities(world: World): Entity[];
5
+ /**
6
+ * Apply a selection.
7
+ * - `replace`: exactly `entities` end up selected (others cleared).
8
+ * - `toggle`: flip each of `entities`.
9
+ * - `add`: union `entities` into the current selection.
10
+ * `onChange` fires once, only if a tag actually flipped.
11
+ */
12
+ export declare function setSelection(world: World, entities: Iterable<Entity>, mode: SelectionMode, onChange?: () => void): void;
13
+ /** Clear the whole selection. `onChange` fires once, only if something was selected. */
14
+ export declare function clearSelection(world: World, onChange?: () => void): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @ice/core presence layer (design-005 §6.5, design-001 §5.6) — attach, publish,
3
+ * remote-cursor projection, and the install seam.
4
+ */
5
+ export { attachPresence, DEFAULT_PRESENCE_TTL_MS, type PresenceOpts, type PresenceSession, } from "./presence-kit";
6
+ export { createPresencePublish, type PresencePublishOpts } from "./publish";
7
+ export { createRemoteCursorsSystem, installPresence, type InstallPresenceOpts, } from "./remote-cursors";
@@ -0,0 +1,39 @@
1
+ import type { Entity, World } from "@vibecook/strata-ecs";
2
+ import { type EphemeralSource, type EphemeralStore } from "@vibecook/strata-ecs/ephemeral";
3
+ /** Default per-entry TTL — a presence entity self-expires this long after its last keepalive. */
4
+ export declare const DEFAULT_PRESENCE_TTL_MS = 5000;
5
+ export interface PresenceOpts {
6
+ /** SESSION-unique peer id (006 B5). Defaults to `crypto.randomUUID()`. */
7
+ readonly peerId?: string;
8
+ /** Display name — must be real (bare, no default). */
9
+ readonly name: string;
10
+ /** Display color — must be real (bare, no default). */
11
+ readonly color: string;
12
+ /** Per-entry TTL (design §15.3), default {@link DEFAULT_PRESENCE_TTL_MS}. */
13
+ readonly ttlMs?: number;
14
+ }
15
+ export interface PresenceSession {
16
+ /** This session's peer id (the ephemeral partition prefix). */
17
+ readonly peerId: string;
18
+ /** The writer-partitioned Mutator for this peer's own presence entities. */
19
+ readonly eph: EphemeralStore;
20
+ /** The local peer entity (facets: PresenceInfo + PresencePeer + Local; cursor/selection added by publish). */
21
+ readonly localPeer: Entity;
22
+ /**
23
+ * The LoroEphemeralSnapshot surface. INBOUND presence bytes go through
24
+ * `wire.apply(bytes)`; the encoders (`encodeChanged`/`encodeKeys`/…) are
25
+ * driven by the binding's own timers — do not call them externally.
26
+ */
27
+ readonly wire: EphemeralSource;
28
+ /** Subscribe to the binding's OUTBOUND presence bytes (its two timers). Returns an unsubscribe. */
29
+ onOutbound(fn: (bytes: Uint8Array) => void): () => void;
30
+ /** Leave (ship tombstones) then tear down the binding + destroy the owned Loro store. Idempotent. */
31
+ detach(): void;
32
+ }
33
+ /**
34
+ * Attach an ephemeral presence layer to `world`. The returned session owns the
35
+ * Loro store; the app (or the bootstrap kit) wires `wire.apply` / `onOutbound`
36
+ * to a byte channel and installs the publish hook + remote-cursor system via
37
+ * {@link installPresence}.
38
+ */
39
+ export declare function attachPresence(world: World, opts: PresenceOpts): PresenceSession;
@@ -0,0 +1,14 @@
1
+ import type { Entity, World } from "@vibecook/strata-ecs";
2
+ import type { PublishHook } from "../engine/engine";
3
+ import type { PresenceSession } from "./presence-kit";
4
+ export interface PresencePublishOpts {
5
+ /** Durable key for a selected entity — the doc session's `store.keyOf`. Absent ⇒ no keys carried. */
6
+ readonly keyOf?: (e: Entity) => string | undefined;
7
+ /** Max selection keys carried (default {@link SELECTION_KEY_CAP}). */
8
+ readonly maxKeys?: number;
9
+ }
10
+ /**
11
+ * Build the publish hook that maintains `session.localPeer`'s cursor + selection
12
+ * facets. Register it with `engine.onPublish` (installPresence does this).
13
+ */
14
+ export declare function createPresencePublish(world: World, session: PresenceSession, opts?: PresencePublishOpts): PublishHook;
@@ -0,0 +1,18 @@
1
+ import type { TickSystem, World } from "@vibecook/strata-ecs";
2
+ import type { Engine } from "../engine/engine";
3
+ import { type PresencePublishOpts } from "./publish";
4
+ import type { PresenceSession } from "./presence-kit";
5
+ /**
6
+ * The derive tick system that pools + reaps remote cursor entities. `world` is
7
+ * unused directly (iteration goes through `ctx`), kept for factory symmetry with
8
+ * the other `create*System(world)` builders.
9
+ */
10
+ export declare function createRemoteCursorsSystem(_world: World): TickSystem;
11
+ export type InstallPresenceOpts = PresencePublishOpts;
12
+ /**
13
+ * Wire a presence session into an engine: the publish hook (outbound facet
14
+ * derivation) + the remote-cursor derive system. Returns an uninstall that
15
+ * removes both. Does NOT own the session's lifecycle — call `session.detach()`
16
+ * separately.
17
+ */
18
+ export declare function installPresence(engine: Engine, session: PresenceSession, opts?: InstallPresenceOpts): () => void;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Schema metadata registry (design-001 §2, design-005 §1).
3
+ *
4
+ * Components/tags/relations stay PURE (rev-2 sovereignty) — but prefab
5
+ * validation (eid ban, required-field defaults) and devtools badges need to
6
+ * know what a component's fields look like. strata does not expose schema
7
+ * introspection publicly, so ALL engine + userland definitions go through
8
+ * these thin wrappers: they record the raw schema in a side registry, then
9
+ * delegate to strata unchanged. The returned handles are strata's own.
10
+ */
11
+ import { defineComponent as strataDefineComponent, defineResource as strataDefineResource, type Component, type FieldInput, type Relation, type Resource, type Tag } from "@vibecook/strata-ecs";
12
+ export type RawSchema = Record<string, FieldInput>;
13
+ export interface ComponentMeta {
14
+ name: string;
15
+ schema: RawSchema;
16
+ /** Field names whose type is `eid` (runtime-local handles — banned in durable cells). */
17
+ eidFields: string[];
18
+ /** Field names with no default (strata requires them at spawn). */
19
+ requiredFields: string[];
20
+ }
21
+ export interface RelationMeta {
22
+ name: string;
23
+ arity: "one" | "many";
24
+ }
25
+ export interface ResourceMeta {
26
+ name: string;
27
+ /** design-001 §2 rule 7: resources declare durable|runtime at definition. */
28
+ durable: boolean;
29
+ }
30
+ export declare function defineComponent<const Sch extends RawSchema>(name: string, schema: Sch): ReturnType<typeof strataDefineComponent<Sch>>;
31
+ export declare function defineTag(name: string): Tag;
32
+ export declare function defineRelation(name: string, opts?: {
33
+ arity?: "one" | "many";
34
+ }): Relation;
35
+ export declare function defineResource<const Sch extends RawSchema>(name: string, schema: Sch, opts?: {
36
+ durable?: boolean;
37
+ }): ReturnType<typeof strataDefineResource<Sch>>;
38
+ export declare const schemaMeta: {
39
+ component(c: Component): ComponentMeta | undefined;
40
+ tagName(t: Tag): string | undefined;
41
+ relation(r: Relation): RelationMeta | undefined;
42
+ resource(r: Resource): ResourceMeta | undefined;
43
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Prefabs — sovereignty at the entity level (design-001 §2, rev 2).
3
+ *
4
+ * A prefab declares an entity kind's ESSENTIAL component set, its
5
+ * durable-ELIGIBLE extras, and its class; the class IS the spawn path:
6
+ * durable → tx.spawn · runtime → world/ctx.spawn · ephemeral → eph.spawn
7
+ *
8
+ * Validation set (design-005 §1, all DEV-throw at definition time):
9
+ * - eligible components eid-free (durable prefabs)
10
+ * - every essential component's required fields supplied by its init value
11
+ * - ephemeral prefabs: components + tags only (no relations)
12
+ * - owner:"derive" only on runtime prefabs
13
+ * - prefab ids unique
14
+ */
15
+ import type { Component, Entity, EntityKey, Relation, Tag } from "@vibecook/strata-ecs";
16
+ /** Durable identity stamp (design-001 §2 rule 5) — subsumes WidgetType. */
17
+ export declare const PrefabId: Component<import("@vibecook/strata-ecs").ValueOf<{
18
+ readonly id: "string";
19
+ }>, {
20
+ readonly id: "string";
21
+ }>;
22
+ export type PrefabClass = "durable" | "runtime" | "ephemeral";
23
+ /** Scalar shapes strata accepts as field writes at spawn. */
24
+ export type FieldWrite = string | number | boolean | Entity | EntityKey | null;
25
+ export type ComponentInit = readonly [Component, Record<string, FieldWrite>];
26
+ /**
27
+ * Typed component-init pair: `init(Position, { x: 0, y: 0 })` gets full
28
+ * compile-time field checking (missing/extra/mistyped fields error) before
29
+ * erasing to the registry's loose `ComponentInit`. Prefer this in every
30
+ * prefab definition and instantiate override; raw pairs remain legal for
31
+ * dynamic construction (review finding B4).
32
+ */
33
+ export declare function init<S extends Record<string, FieldWrite>>(c: Component<S>, v: S): ComponentInit;
34
+ export interface PrefabDef {
35
+ store: PrefabClass;
36
+ /** Essential set: spawned with, committed for durable prefabs. */
37
+ components: readonly ComponentInit[];
38
+ tags?: readonly Tag[];
39
+ /** Durable-eligible beyond essential (attachable later via tx); facets for ephemeral. */
40
+ optional?: readonly Component[];
41
+ /** Eligible outgoing relations (durable prefabs; runtime relations are unrestricted). */
42
+ relations?: readonly Relation[];
43
+ /** Write-ownership badge (devtools) — runtime prefabs only. */
44
+ owner?: "derive";
45
+ /**
46
+ * Pack version for the document version gate (design-001 §5.2, design-005
47
+ * §6.3): stamped as an `engine.pack.<id>.<v>` marker key at doc creation and
48
+ * compared at open. Bump when the prefab's durable shape changes. Default 1.
49
+ */
50
+ version?: number;
51
+ }
52
+ export interface Prefab extends PrefabDef {
53
+ id: string;
54
+ /** Eligible component set = essential ∪ optional ∪ {PrefabId}. */
55
+ eligible: ReadonlySet<Component>;
56
+ eligibleRelations: ReadonlySet<Relation>;
57
+ }
58
+ export declare function definePrefab(id: string, def: PrefabDef): Prefab;
59
+ export declare const prefabs: {
60
+ get(id: string): Prefab | undefined;
61
+ /** Every registered prefab (the version gate stamps/compares the full set). */
62
+ all(): Prefab[];
63
+ };
64
+ /**
65
+ * TEST-ONLY registry wipe. Deliberately a standalone export that the public
66
+ * barrel does NOT re-export (review finding A8) — tests import it from this
67
+ * module path directly.
68
+ */
69
+ export declare function __resetPrefabsForTests(): void;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Runtime default constants transcribed from the reviewed design docs.
3
+ * Not exported from the package barrel yet (design-005 §4 engine facade wires these in later).
4
+ */
5
+ /** Recognizer timing/slop defaults (design-003 §4.2 kind table; inertia constants from §5 item 9; multi-tap from v2 gesture.ts). */
6
+ export declare const GESTURE_DEFAULTS: {
7
+ readonly tapMaxMs: 250;
8
+ readonly tapSlopPx: 8;
9
+ readonly longPressMs: 500;
10
+ readonly longPressSlopPx: 10;
11
+ readonly dragSlopPx: 10;
12
+ readonly pinchSlopPx: 6;
13
+ readonly wheelEndSilenceMs: 150;
14
+ readonly inertiaMinVelocityPxPerS: 120;
15
+ readonly inertiaDecayMs: 325;
16
+ readonly multiTapWindowMs: 280;
17
+ readonly multiTapSlopPx: 20;
18
+ };
19
+ /** Pick radii + retarget dead-band (design-003 §3 L1 targeting). */
20
+ export declare const POINTER_DEFAULTS: {
21
+ readonly radiusMousePx: 0;
22
+ readonly radiusTouchPx: 12;
23
+ readonly radiusPenPx: 4;
24
+ readonly hoverReleaseDeadBandPx: 4;
25
+ };
26
+ /**
27
+ * Wire/port picking geometry (design-003 §3 wires-below-widgets; design-004 §6).
28
+ * `wirePickSlopPx` is added to the pointer radius (screen px, `/zoom` at use) so a
29
+ * thin bezier stays grab-able; `portIndexHalfWorld` is the half-extent of the tiny
30
+ * world AABB a materialized port occupies in the spatial index (design-004 §6 —
31
+ * ports are pickable but carry no Position/Size, so spatialSync never sees them).
32
+ */
33
+ export declare const GRAPH_PICK_DEFAULTS: {
34
+ readonly wirePickSlopPx: 8;
35
+ readonly portIndexHalfWorld: 6;
36
+ };
37
+ /** SnapConfig resource defaults (design-005 §4 engine facade). */
38
+ export declare const SNAP_DEFAULTS: {
39
+ readonly enabled: true;
40
+ readonly thresholdPx: 5;
41
+ };
42
+ /** Zoom clamp (design-003 §5 item 9 cameraControl) and frame dt clamp (design-002 §1). */
43
+ export declare const CAMERA_DEFAULTS: {
44
+ readonly minZoom: 0.1;
45
+ readonly maxZoom: 5;
46
+ readonly dtClampMs: 64;
47
+ };
48
+ /** Widget/FBO/port budgets (design-004 §2 host pipeline, §3 FBO pool, §6 port materialization; ported into design-005 §4 engine facade). */
49
+ export declare const RUNTIME_BUDGETS: {
50
+ readonly keepMountedWidgets: 256;
51
+ readonly fboBytes: 268435456;
52
+ readonly portSpawnPerFrame: 64;
53
+ readonly portLightUpRadiusPx: 600;
54
+ readonly portReapGraceMs: 1000;
55
+ readonly cullOverscanWorldPerZoom: 200;
56
+ };
@@ -0,0 +1,19 @@
1
+ import type { System, TickSystem, World } from "@vibecook/strata-ecs";
2
+ /**
3
+ * Runtime pan-inertia velocity (screen px/s), seeded by `cameraControl` at pan
4
+ * release and decayed by `cameraInertia`. A resource (not closure state) so
5
+ * devtools sees it and there is no hidden singleton (design-003 §9).
6
+ */
7
+ export declare const CameraInertia: import("@vibecook/strata-ecs").Resource<import("@vibecook/strata-ecs").ValueOf<{
8
+ readonly vx: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
9
+ readonly vy: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
10
+ }>, {
11
+ readonly vx: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
12
+ readonly vy: import("@vibecook/strata-ecs").FieldSpec<"f32", true>;
13
+ }>;
14
+ export interface CameraSystems {
15
+ cameraControl: TickSystem;
16
+ cameraInertia: TickSystem;
17
+ tweenSystem: System;
18
+ }
19
+ export declare function createCameraSystems(world: World): CameraSystems;