@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,79 @@
1
+ /**
2
+ * `defineTool` — tools are PURE CONFIGURATION (design-005 §3): a tool
3
+ * introduces no new code paths, it only parameterizes L2 spawn (which
4
+ * recognizer kinds a pointer-down mints), L4 cursor, the L3 dragRoute
5
+ * policy, and capability gates. Custom behaviors register systems via the
6
+ * engine's extension slot — same contract as built-ins (design-003 §5.0).
7
+ *
8
+ * Built-ins ship as configs of this same primitive:
9
+ * - select: tap/longPress/drag; canvas drag → marquee (pan on touch /
10
+ * space / middle-button — device conventions, honored ABOVE tool policy).
11
+ * - pan: hand cursor; every drag pans.
12
+ * - connect: crosshair; widget/port drags connect (design-003 §5.8).
13
+ * - draw(widgetType): factory — canvas drag creates ONE widget from the
14
+ * drag rect at JustEnded (one tx via the create intent; min-size floor;
15
+ * drag preview is recorded polish, not v1 — design-001 §3's full
16
+ * draft→promote protocol remains the vNext shape).
17
+ *
18
+ * Switching tools runs `cancelActiveGestures()` first (design-003 §8) —
19
+ * `ops.setTool` owns that; nothing here touches the world.
20
+ */
21
+ import type { SingleKindName } from "../systems/l2-recognize";
22
+ export type CanvasDragRoute = "marquee" | "pan" | "connect" | "draw" | "none";
23
+ export type WidgetDragRoute = "move" | "pan" | "connect" | "none";
24
+ export interface ToolRoute {
25
+ /** Drag starting on empty canvas (after device overrides: touch/space/middle → pan). */
26
+ readonly canvasDrag?: CanvasDragRoute;
27
+ /** Drag starting on a widget body. */
28
+ readonly widgetDrag?: WidgetDragRoute;
29
+ /** Drag starting on a port entity ("connect" | "none"). */
30
+ readonly portDrag?: "connect" | "none";
31
+ }
32
+ export interface ToolGates {
33
+ /** false ⇒ widget drags never route to move while this tool is active. */
34
+ readonly movable?: boolean;
35
+ /** false ⇒ resize-handle grabs are suppressed while this tool is active. */
36
+ readonly resizable?: boolean;
37
+ }
38
+ export interface ToolDef {
39
+ readonly id: string;
40
+ /** L4 cursor while active (OS cursor string; design-003 §7). */
41
+ readonly cursor?: string;
42
+ /** Recognizer kinds minted per pointer-down (design-003 §4.1). */
43
+ readonly spawnProfile?: readonly SingleKindName[];
44
+ readonly route?: ToolRoute;
45
+ readonly gates?: ToolGates;
46
+ /** Default keymap entry (the react keymap consumes; app-overridable). */
47
+ readonly shortcut?: string;
48
+ /** draw tools: the widget type the drag rect creates. */
49
+ readonly draw?: {
50
+ readonly widgetType: string;
51
+ };
52
+ }
53
+ export interface Tool extends Required<Pick<ToolDef, "id">> {
54
+ readonly cursor: string | undefined;
55
+ readonly spawnProfile: readonly SingleKindName[];
56
+ readonly route: Required<ToolRoute>;
57
+ readonly gates: Required<ToolGates>;
58
+ readonly shortcut: string | undefined;
59
+ readonly draw: {
60
+ readonly widgetType: string;
61
+ } | undefined;
62
+ }
63
+ export declare function defineTool(def: ToolDef): Tool;
64
+ export declare const tools: {
65
+ get(id: string): Tool | undefined;
66
+ all(): Tool[];
67
+ /** The active tool's policy, with select semantics for unknown ids. */
68
+ resolve(id: string): Tool;
69
+ };
70
+ /** TEST-ONLY wipe (mirrors __resetWidgetsForTests; not on the barrel). */
71
+ export declare function __resetToolsForTests(): void;
72
+ /** Idempotent — runs at module load (built-ins are the engine's canonical
73
+ * vocabulary, DEFAULT_SPAWN_PROFILES precedent); safe under HMR. */
74
+ export declare function registerBuiltinTools(): void;
75
+ /** Factory (design-005 §3): a drag on canvas creates `widgetType` from the rect. */
76
+ export declare function createDrawTool(widgetType: string, opts?: {
77
+ id?: string;
78
+ shortcut?: string;
79
+ }): Tool;
@@ -0,0 +1,124 @@
1
+ import type { Component, Tag } from "@vibecook/strata-ecs";
2
+ import { type Prefab } from "../schema/prefab";
3
+ import type { PropSpec, PropsDecl } from "./props";
4
+ export type WidgetSurface = "dom" | "gl";
5
+ export type SizeMode = "fixed" | "auto-height" | "auto";
6
+ export interface WidgetPortDecl {
7
+ readonly id: string;
8
+ readonly side: "n" | "e" | "s" | "w";
9
+ /** Slot index along the side (0-based; anchor spacing is the kernel's). */
10
+ readonly index?: number;
11
+ /** Compatibility keys (empty = connects to anything). */
12
+ readonly accepts?: readonly string[];
13
+ }
14
+ export interface WidgetInteraction {
15
+ readonly selectable?: boolean;
16
+ readonly movable?: boolean;
17
+ readonly resizable?: boolean;
18
+ readonly snap?: "source" | "target" | "both" | "none";
19
+ /** Drop-REJECTING target: widgets dropped onto this one fly back (v1 iOS-card contract). */
20
+ readonly solid?: boolean;
21
+ /** "longPress": hold-to-lift dragging (iOS home-screen model); default "press". */
22
+ readonly dragOn?: "press" | "longPress";
23
+ }
24
+ export interface WidgetDef {
25
+ readonly type: string;
26
+ readonly version?: number;
27
+ /** Top-level props: a plain record, or the DSL's `p.object(...)` result. */
28
+ readonly props?: PropsDecl | {
29
+ readonly kind: "object";
30
+ readonly fields: Readonly<Record<string, unknown>>;
31
+ };
32
+ /** Conflict groups: group name → prop names. Ungrouped props → "props". */
33
+ readonly groups?: Readonly<Record<string, readonly string[]>>;
34
+ readonly surface: WidgetSurface;
35
+ /** Framework component (opaque to core — the react package narrows it). */
36
+ readonly component: unknown;
37
+ readonly sizeMode?: SizeMode;
38
+ readonly defaultSize?: {
39
+ readonly w: number;
40
+ readonly h: number;
41
+ };
42
+ readonly minSize?: {
43
+ readonly w: number;
44
+ readonly h: number;
45
+ };
46
+ /**
47
+ * GL only: the island repaints every frame while visible (design-004 §3
48
+ * animation contract — the EXPLICIT opt-in; plain `useFrame` inside a
49
+ * portal cannot be attributed to an island, so it never drives repaints;
50
+ * content-driven animation goes through `useIslandFrame`).
51
+ */
52
+ readonly animated?: boolean;
53
+ readonly interaction?: WidgetInteraction;
54
+ /**
55
+ * Node-editor port schema (design-005 §2; design-001 §5.3): wires bind to
56
+ * widget + port ID — port ENTITIES are runtime and on-demand. `accepts` is
57
+ * the compatibility key list the connect gesture checks.
58
+ */
59
+ readonly ports?: readonly WidgetPortDecl[];
60
+ readonly container?: {
61
+ readonly accepts: readonly string[];
62
+ readonly provides?: readonly string[];
63
+ };
64
+ /**
65
+ * Provides-keys WITHOUT container semantics (nodeboard field finding): a
66
+ * leaf that only offers itself to containers must not become a Container
67
+ * (drop target + Enter affordance). Ignored when `container` is present
68
+ * (its `provides` wins).
69
+ */
70
+ readonly provides?: readonly string[];
71
+ /**
72
+ * fromVersion → idempotent absolute transform (M9's `runMigrations` runs the
73
+ * chain at open; `prev`/return are flat prop records spanning every group).
74
+ *
75
+ * No parallel `legacy` schema field is needed for field-level changes: strata
76
+ * is field-tolerant within a known component name (extra stored fields dropped,
77
+ * missing default-filled — every generated field carries a default), so a v1
78
+ * cell projects into the v2 group component of the same name and the transform
79
+ * reads the already-v2-shaped value. See doc/migrate.ts for the as-built note;
80
+ * migrating data out of a group REMOVED wholesale in v2 is vNext (design-005
81
+ * §6.4 cross-schema movement).
82
+ */
83
+ readonly migrate?: Readonly<Record<number, (prev: Record<string, unknown>) => Record<string, unknown>>>;
84
+ }
85
+ export interface WidgetGroup {
86
+ readonly name: string;
87
+ readonly component: Component;
88
+ /** prop name → spec (field order = component field order). */
89
+ readonly fields: Readonly<Record<string, PropSpec>>;
90
+ }
91
+ export interface WidgetType {
92
+ readonly type: string;
93
+ readonly version: number;
94
+ readonly prefab: Prefab;
95
+ readonly groups: readonly WidgetGroup[];
96
+ readonly propToGroup: Readonly<Record<string, string>>;
97
+ readonly surface: WidgetSurface;
98
+ readonly component: unknown;
99
+ readonly sizeMode: SizeMode;
100
+ readonly defaultSize: {
101
+ readonly w: number;
102
+ readonly h: number;
103
+ };
104
+ readonly minSize: {
105
+ readonly w: number;
106
+ readonly h: number;
107
+ };
108
+ /** GL islands: repaint every visible frame (design-004 §3). */
109
+ readonly animated: boolean;
110
+ /** Node-editor ports (empty when not a node). */
111
+ readonly ports: readonly WidgetPortDecl[];
112
+ /** Runtime capability tags the equip system stamps at projection. */
113
+ readonly capabilityTags: readonly Tag[];
114
+ readonly migrate: Readonly<Record<number, (prev: Record<string, unknown>) => Record<string, unknown>>>;
115
+ }
116
+ /** Stamped by the equip system once a projected widget carries its capability tags. */
117
+ export declare const WidgetEquipped: Tag;
118
+ export declare const widgets: {
119
+ get(type: string): WidgetType | undefined;
120
+ all(): WidgetType[];
121
+ };
122
+ export declare function defineWidget(def: WidgetDef): WidgetType;
123
+ /** TEST-ONLY wipe (mirrors __resetPrefabsForTests; not on the barrel). */
124
+ export declare function __resetWidgetsForTests(): void;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The widget equip system (design-005 §2 "capability-tag stamping recipe",
3
+ * design-001 reconciled catalog).
4
+ *
5
+ * Capability tags are RUNTIME state — a restored/remote document carries only
6
+ * durable cells, so every projected widget entity gets its type's tags stamped
7
+ * here, once (`WidgetEquipped` makes the query zero-match at steady state:
8
+ * row-filtered, nothing runs, nothing stamps). Runs in `derive`; new
9
+ * projections from this frame's sync() are visible by then.
10
+ *
11
+ * This generalizes the graybox demo's equipSceneBoxes into the engine.
12
+ */
13
+ import { type System } from "@vibecook/strata-ecs";
14
+ export declare function createWidgetEquipSystem(): System;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Widget lifecycle: cull → keep-mounted LRU → the external mount store
3
+ * (design-004 §2).
4
+ *
5
+ * - `cullSystem` (derive): viewport test over equipped widgets' EFFECTIVE size
6
+ * (MeasuredSize where present, else Size), overscan scaled by zoom; flips
7
+ * Visible/Culled CHANGE-ONLY.
8
+ * - `mountSystem` (derive, after cull): maintains the mount list — every
9
+ * Visible widget is mounted; Culled widgets stay mounted-but-hidden
10
+ * (`display:none` at the host; React state preserved) within a count LRU
11
+ * budget (default RUNTIME_BUDGETS.keepMountedWidgets); beyond it the
12
+ * least-recently-visible unmount for real. Decisions are ENGINE-side; the
13
+ * dom host reflector and the React portal hook both consume the snapshot.
14
+ * - The store implements the `useSyncExternalStore` contract: `subscribe` /
15
+ * `getSnapshot` with snapshot identity changing IFF membership or a hidden
16
+ * flag changed. Listener notification is deferred to post-notify (the
17
+ * engine flush reflector) — never from inside the tick.
18
+ */
19
+ import { type Entity, type System, type TickSystem, type World } from "@vibecook/strata-ecs";
20
+ import type { Engine } from "../engine/engine";
21
+ import type { MeasureQueue } from "../input/measure-queue";
22
+ export interface MountEntry {
23
+ readonly entity: Entity;
24
+ /** Culled-but-kept-mounted: host hides, React state survives. */
25
+ readonly hidden: boolean;
26
+ }
27
+ export interface WidgetMountStore {
28
+ subscribe(listener: () => void): () => void;
29
+ getSnapshot(): readonly MountEntry[];
30
+ }
31
+ export interface WidgetRuntime {
32
+ readonly store: WidgetMountStore;
33
+ readonly cullSystem: System;
34
+ readonly mountSystem: TickSystem;
35
+ /** Post-notify listener flush — install registers it as a reflector. */
36
+ flush(): void;
37
+ }
38
+ export declare function createWidgetRuntime(world: World, opts?: {
39
+ keepMounted?: number;
40
+ }): WidgetRuntime;
41
+ /** Install cull + mount + the post-notify listener flush on an engine. */
42
+ export declare function installWidgetRuntime(engine: Engine, opts?: {
43
+ keepMounted?: number;
44
+ measureQueue?: MeasureQueue;
45
+ }): WidgetRuntime & {
46
+ uninstall(): void;
47
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The engine props DSL (design-005 §2, amending design-001 §5.2).
3
+ *
4
+ * Standard Schema is validation-only — it exposes `validate()` but no
5
+ * introspectable shape, so arbitrary schemas cannot compile to typed
6
+ * components. This DSL is the compiler's input: it (a) maps 1:1 onto strata
7
+ * field types plus explicit `p.json` string cells, (b) implements the
8
+ * Standard Schema v1 interface so it plugs into ecosystem tooling, and
9
+ * (c) enforces the definition-time rules — every top-level field defaulted
10
+ * (json derives an empty default from its root shape), enums additive-only
11
+ * across versions (checked by the M9 migrator against pack history; recorded
12
+ * here), `p.json` as the ONLY nesting escape hatch (visibly conflict-coarse:
13
+ * one string cell, last-writer-wins whole-value).
14
+ */
15
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
16
+ readonly "~standard": {
17
+ readonly version: 1;
18
+ readonly vendor: string;
19
+ readonly validate: (value: unknown) => {
20
+ value: Output;
21
+ } | {
22
+ issues: readonly {
23
+ message: string;
24
+ }[];
25
+ };
26
+ };
27
+ }
28
+ export type PropSpec = StringSpec | NumberSpec | BooleanSpec | EnumSpec | JsonSpec;
29
+ export interface StringSpec extends StandardSchemaV1<unknown, string> {
30
+ readonly kind: "string";
31
+ readonly default?: string;
32
+ }
33
+ export interface NumberSpec extends StandardSchemaV1<unknown, number> {
34
+ readonly kind: "number";
35
+ readonly default?: number;
36
+ readonly min?: number;
37
+ readonly max?: number;
38
+ }
39
+ export interface BooleanSpec extends StandardSchemaV1<unknown, boolean> {
40
+ readonly kind: "boolean";
41
+ readonly default?: boolean;
42
+ }
43
+ export interface EnumSpec extends StandardSchemaV1<unknown, string> {
44
+ readonly kind: "enum";
45
+ readonly options: readonly string[];
46
+ readonly default?: string;
47
+ }
48
+ /** One conflict-coarse string cell; `inner` validates the PARSED value. */
49
+ export interface JsonSpec extends StandardSchemaV1<unknown, unknown> {
50
+ readonly kind: "json";
51
+ readonly inner: JsonShape;
52
+ /** Serialized default (derived from the inner root when not given). */
53
+ readonly default?: string;
54
+ }
55
+ /** Shapes legal only INSIDE p.json (validation-only, never components). */
56
+ export type JsonShape = {
57
+ readonly kind: "string";
58
+ } | {
59
+ readonly kind: "number";
60
+ } | {
61
+ readonly kind: "boolean";
62
+ } | {
63
+ readonly kind: "enum";
64
+ readonly options: readonly string[];
65
+ } | {
66
+ readonly kind: "array";
67
+ readonly item: JsonShape;
68
+ } | {
69
+ readonly kind: "object";
70
+ readonly fields: Readonly<Record<string, JsonShape>>;
71
+ };
72
+ export interface ObjectSpec {
73
+ readonly kind: "object";
74
+ readonly fields: Readonly<Record<string, PropSpec>>;
75
+ }
76
+ export declare const p: {
77
+ string(opts?: {
78
+ default?: string;
79
+ }): StringSpec;
80
+ number(opts?: {
81
+ default?: number;
82
+ min?: number;
83
+ max?: number;
84
+ }): NumberSpec;
85
+ boolean(opts?: {
86
+ default?: boolean;
87
+ }): BooleanSpec;
88
+ enum(options: readonly string[], opts?: {
89
+ default?: string;
90
+ }): EnumSpec;
91
+ json(inner: JsonShape, opts?: {
92
+ default?: unknown;
93
+ }): JsonSpec;
94
+ array(item: JsonShape): JsonShape;
95
+ object(fields: Readonly<Record<string, JsonShape>>): JsonShape;
96
+ };
97
+ /** A widget props declaration: top-level named specs (the compiler's input). */
98
+ export type PropsDecl = Readonly<Record<string, PropSpec>>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * spawnWidget — the paved-road widget creation op (app handler, design-005 §2).
3
+ *
4
+ * One guardedTransaction: spawnPrefab with Position (+Size) overrides and any
5
+ * prop overrides folded into their group components (whole-group values —
6
+ * design-001 §5.2's conflict-group granularity). Validation runs through each
7
+ * prop's Standard Schema before the write.
8
+ */
9
+ import type { Entity, World } from "@vibecook/strata-ecs";
10
+ import type { DurableStore } from "@vibecook/strata-ecs/durable";
11
+ import { type ComponentInit } from "../schema/prefab";
12
+ export interface SpawnWidgetOpts {
13
+ readonly x: number;
14
+ readonly y: number;
15
+ readonly w?: number;
16
+ readonly h?: number;
17
+ readonly props?: Readonly<Record<string, unknown>>;
18
+ /** false ⇒ the spawn never enters the local undo stack (batch seeds). */
19
+ readonly undoable?: boolean;
20
+ }
21
+ /**
22
+ * The prefab-override list for a widget spawn (Position/Size + validated
23
+ * whole-group prop values). Shared by `spawnWidget` and the doc sink's
24
+ * create-intent execution (draw tool) so the two can never diverge.
25
+ */
26
+ export declare function widgetSpawnInits(type: string, opts: SpawnWidgetOpts): {
27
+ prefab: import("../schema/prefab").Prefab;
28
+ overrides: ComponentInit[];
29
+ };
30
+ export declare function spawnWidget(store: DurableStore, world: World, type: string, opts: SpawnWidgetOpts): Entity;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @ice/devtools — the engine devtools panel (design-005 §4): pointers/recognizers,
3
+ * planes, sovereignty badges, loop telemetry. A standalone DOM panel that reads
4
+ * the world outside the tick — it is NOT a reflector and writes no ECS.
5
+ * Import wall: @ice/core (+kernel) only; nobody imports us (depcruise-enforced).
6
+ */
7
+ export declare const DEVTOOLS_VERSION = "0.0.0";
8
+ export { attachDevtools, type DevtoolsHandle, type DevtoolsOpts } from "./panel";
@@ -0,0 +1,48 @@
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;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The DOM canvas host (design-004 §1, host pipeline — M3 slice).
3
+ *
4
+ * M3 stands up ONE content plane: an absolutely-positioned div whose single CSS
5
+ * transform (written by the plane-transform reflector) applies the camera to
6
+ * every world-positioned child at once (design-002 §5 `planeTransform`; kernel
7
+ * `planeCssTransform`). The full six-plane model (background/content/overlay/
8
+ * chrome/HUD/nav + the FBO compositor) arrives in M6 — this file is deliberately
9
+ * the minimum that lets a reflector paint into a camera-transformed space.
10
+ *
11
+ * The container is styled to be a stable, gesture-clean viewport: `relative` so
12
+ * the absolute plane anchors to it, `overflow:hidden` to clip the infinite
13
+ * plane, `touch-action:none` + `user-select:none` so browser scroll/zoom and
14
+ * text selection never fight the interaction stack.
15
+ */
16
+ export interface CanvasHost {
17
+ /** The app-provided viewport element (styled, not created, by the host). */
18
+ readonly container: HTMLElement;
19
+ /** The single camera-transformed plane; world-positioned children mount here. */
20
+ readonly contentPlane: HTMLDivElement;
21
+ /** Remove the plane and clear the inline styles the host wrote. */
22
+ dispose(): void;
23
+ }
24
+ export declare function createCanvasHost(container: HTMLElement): CanvasHost;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @ice/dom — DOM planes, pointer adapters (events→queue ONLY), built-in DOM reflectors.
3
+ * Import wall: @ice/core + @ice/kernel down only — never react/three (enforced).
4
+ */
5
+ export declare const DOM_VERSION = "0.0.0";
6
+ export { createCanvasHost, type CanvasHost } from "./host";
7
+ export { createPlanes, type Planes } from "./planes";
8
+ export { createPlaneTransformReflector, type CameraPlanes } from "./reflectors/plane-transform";
9
+ export { createGrayboxReflector } from "./reflectors/graybox";
10
+ export { createDomWidgetsReflector, type DomWidgetsHost, type DomWidgetsReflector, } from "./reflectors/dom-widgets";
11
+ export { startRafLoop } from "./loop";
12
+ export { attachPointerAdapter, type GLRoute, type PointerAdapterOpts } from "./pointer-adapter";
13
+ export { createCursorReflector } from "./reflectors/cursor";
14
+ export { createRemoteCursorsReflector, type RemoteCursorsReflector } from "./reflectors/remote-cursors";
15
+ export { createGridReflector, DEFAULT_GRID_CONFIG, type GridConfig } from "./reflectors/grid";
16
+ export { createWiresReflector, DEFAULT_WIRES_CONFIG, type WirePreview, type WiresConfig, type WiresReflector, } from "./reflectors/wires";
17
+ export { createChromeReflector } from "./reflectors/chrome";
18
+ export { attachMeasureAdapter, type MeasureAdapter } from "./measure-adapter";
19
+ export { wireMeasurement, type MeasureWiringHosts, type MeasureWiringOpts } from "./measure-wiring";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The rAF frame loop (design-002 §1: core is scheduler-free — the DOM package
3
+ * owns the loop; core never touches the platform).
4
+ *
5
+ * Drives `engine.step(now)` once per animation frame, threading the rAF
6
+ * timestamp straight into the frame clock (so FrameInfo.dt is real wall time,
7
+ * clamped in core). The returned stop fn cancels the pending frame AND latches
8
+ * so a frame already in flight cannot re-schedule — calling it is idempotent.
9
+ */
10
+ import type { Engine } from "@ice/core";
11
+ export declare function startRafLoop(engine: Engine): () => void;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The measurement adapter (design-004 §2 host pipeline; measure side).
3
+ *
4
+ * Wraps a `ResizeObserver` over auto-sized widgets' CONTENT elements and pushes
5
+ * `{ entity, w, h }` samples into the engine's {@link MeasureQueue} — the
6
+ * adapter-only path (Law 2: adapters ONLY enqueue; `measureIngest` turns samples
7
+ * into `MeasuredSize` facts inside the frame contract). It resolves an entity's
8
+ * content element on demand via the caller's `hostFor` (the widget host reflector
9
+ * owns the element↔entity mapping).
10
+ *
11
+ * §2 hidden-measure rule: a `display:none` host collapses to `0×0`, and a
12
+ * full→0 sample would pass any dead-band and corrupt bounds/cull on re-entry —
13
+ * so the adapter NEVER enqueues while the host is hidden (computed `display:none`
14
+ * / no `offsetParent`), and also drops a literal `0×0`. It is the CALLER's job to
15
+ * re-`observe` (or otherwise force a remeasure) on re-show — a `ResizeObserver`
16
+ * does not re-fire for an element that was hidden and shown at the same box size.
17
+ *
18
+ * One shared `ResizeObserver` multiplexes every observed element (fewer objects,
19
+ * identical semantics to one-per-element); `element → entity` maps a callback
20
+ * entry back to its widget.
21
+ */
22
+ import type { Entity, MeasureQueue } from "@ice/core";
23
+ export interface MeasureAdapter {
24
+ /** Begin observing the entity's content element (no-op if `hostFor` yields none). */
25
+ observe(entity: Entity): void;
26
+ /** Stop observing the entity's content element. */
27
+ unobserve(entity: Entity): void;
28
+ /** Disconnect the observer and drop all mappings. */
29
+ dispose(): void;
30
+ }
31
+ export declare function attachMeasureAdapter(hostFor: (e: Entity) => HTMLElement | undefined, queue: MeasureQueue): MeasureAdapter;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Measurement wiring (design-004 §2 host pipeline; measure side, the store half).
3
+ *
4
+ * `attachMeasureAdapter` owns the `ResizeObserver` and the element→entity
5
+ * mapping; THIS module owns *when* to observe/unobserve, driven by the engine's
6
+ * widget mount store. It subscribes and reconciles each snapshot diff into four
7
+ * transitions (design-004 §2 / decision 4):
8
+ *
9
+ * - **fresh mount + visible** → `observe` (the RO fires its own first sample);
10
+ * - **visible → hidden** (culled-but-kept-mounted) → `unobserve` (disconnect on
11
+ * hide — a `display:none` host measures `0×0` and a full→0 sample corrupts
12
+ * bounds/cull on re-entry);
13
+ * - **hidden → shown** → re-`observe` AND a FORCED synchronous re-measure: a
14
+ * `ResizeObserver` does not re-fire for an element hidden and shown at the
15
+ * same box size, so the reconnect alone would leave the rider stale;
16
+ * - **unmount** (exits the snapshot) → `unobserve`.
17
+ *
18
+ * Ordering note: the mount store notifies from the widget-runtime flush
19
+ * reflector, which is registered BEFORE the `domWidgets` host reflector — so at
20
+ * notify time the host `<div>` for a just-mounted (or just-shown) widget may not
21
+ * exist / may still be `display:none`. Like the React portal hook (which defers
22
+ * via its own scheduler), this defers the reconcile to a microtask so it runs
23
+ * AFTER the whole synchronous reflector pass, by which point `domWidgets` has
24
+ * created/unhidden the host and `hostFor` + the forced re-measure see a real box.
25
+ *
26
+ * Law 2 holds: everything here only enqueues (through the adapter or the forced
27
+ * sample) — no world writes, no ECS reads.
28
+ */
29
+ import type { Entity, MeasureQueue, WidgetMountStore } from "@ice/core";
30
+ /** The element source (the `domWidgets` reflector supplies this via `hostFor`). */
31
+ export interface MeasureWiringHosts {
32
+ hostFor(entity: Entity): HTMLElement | undefined;
33
+ }
34
+ export interface MeasureWiringOpts {
35
+ /**
36
+ * Border-box read for the forced re-measure on show. Defaults to
37
+ * `getBoundingClientRect`; tests inject a deterministic stub.
38
+ */
39
+ readonly measure?: (el: HTMLElement) => {
40
+ w: number;
41
+ h: number;
42
+ };
43
+ }
44
+ /**
45
+ * Begin observing auto-sized widget hosts against `store`, pushing measurements
46
+ * into `queue` (drained by `measureIngest`). Returns a teardown that
47
+ * unsubscribes and disposes the underlying `ResizeObserver`.
48
+ */
49
+ export declare function wireMeasurement(store: WidgetMountStore, hosts: MeasureWiringHosts, queue: MeasureQueue, opts?: MeasureWiringOpts): () => void;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The lifted plane (design-004 §1, planes P1/P3 — M6 slice).
3
+ *
4
+ * M3 stood up ONE camera-transformed content plane (`host.ts`, P1). M6 adds P3,
5
+ * the **lifted** plane: a second transform-origin-0-0 div appended to the
6
+ * container AFTER the content plane, so it stacks ABOVE it (P3 over P1) with no
7
+ * z-index bookkeeping — DOM order is the strata order (§1: "stacking is
8
+ * stratified"). Both planes take the SAME single camera transform (the
9
+ * plane-transform reflector writes both), so a promoted widget re-parented
10
+ * content→lifted keeps its world position; it just paints above the content
11
+ * layer for the duration of the drag.
12
+ *
13
+ * `createCanvasHost` is intentionally untouched: the content plane it owns is
14
+ * the P1 authority. `createPlanes` is additive — it borrows that content plane
15
+ * and mints the lifted sibling next to it.
16
+ */
17
+ import type { CanvasHost } from "./host";
18
+ /** The two DOM planes M6 addresses: P1 content (host-owned) + P3 lifted. */
19
+ export interface Planes {
20
+ /** P1: the host's camera-transformed content plane; world-positioned hosts mount here. */
21
+ readonly content: HTMLDivElement;
22
+ /** P3: camera-transformed, stacked above content; promoted (dragged) hosts re-parent here. */
23
+ readonly lifted: HTMLDivElement;
24
+ /** Remove the lifted plane (the content plane is the host's — host.dispose reaps it). */
25
+ dispose(): void;
26
+ }
27
+ /**
28
+ * Mint the lifted plane next to the host's content plane. The lifted div is
29
+ * appended to the container AFTER the content plane, so it renders above it
30
+ * (P3 > P1). Returns both planes for the plane-transform reflector (writes both)
31
+ * and the dom-widgets reflector (re-parents hosts between them on drag-promote).
32
+ */
33
+ export declare function createPlanes(host: CanvasHost): Planes;