@vibecook/ice 0.4.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +4 -4
  2. package/dist/{chunk-3PHPWRB2.js → chunk-55QCOE74.js} +7 -4
  3. package/dist/chunk-55QCOE74.js.map +1 -0
  4. package/dist/{chunk-ENMRSITH.js → chunk-5OU3OWDH.js} +16 -3
  5. package/dist/chunk-5OU3OWDH.js.map +1 -0
  6. package/dist/{chunk-7LZAXQAO.js → chunk-LATXYFMO.js} +6823 -3926
  7. package/dist/chunk-LATXYFMO.js.map +1 -0
  8. package/dist/devtools.js +5 -2
  9. package/dist/devtools.js.map +1 -1
  10. package/dist/dom.js +2 -2
  11. package/dist/ground.js +1 -1
  12. package/dist/index.js +35 -1
  13. package/dist/r3f.js +3 -3
  14. package/dist/react.js +5 -3
  15. package/dist/types/packages/core/src/behavior/compile.d.ts +74 -0
  16. package/dist/types/packages/core/src/behavior/define-behavior.d.ts +56 -0
  17. package/dist/types/packages/core/src/behavior/differ.d.ts +91 -0
  18. package/dist/types/packages/core/src/behavior/harness.d.ts +65 -0
  19. package/dist/types/packages/core/src/behavior/migrate.d.ts +87 -0
  20. package/dist/types/packages/core/src/behavior/public-reads.d.ts +31 -0
  21. package/dist/types/packages/core/src/behavior/runtime.d.ts +102 -0
  22. package/dist/types/packages/core/src/behavior/types.d.ts +358 -0
  23. package/dist/types/packages/core/src/behavior/world-view.d.ts +36 -0
  24. package/dist/types/packages/core/src/engine/engine.d.ts +38 -1
  25. package/dist/types/packages/core/src/engine/guests.d.ts +199 -0
  26. package/dist/types/packages/core/src/engine/reflectors.d.ts +8 -0
  27. package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +26 -0
  28. package/dist/types/packages/core/src/guards/guarded-tx.d.ts +57 -0
  29. package/dist/types/packages/core/src/index.d.ts +11 -3
  30. package/dist/types/packages/core/src/widget/define-widget.d.ts +28 -0
  31. package/dist/types/packages/core/src/widget/props.d.ts +35 -1
  32. package/dist/types/packages/core/src/widget/spawn.d.ts +14 -0
  33. package/dist/types/packages/dom/src/loop.d.ts +9 -0
  34. package/dist/types/packages/react/src/hooks.d.ts +12 -1
  35. package/dist/types/packages/react/src/index.d.ts +1 -1
  36. package/package.json +31 -65
  37. package/dist/chunk-3PHPWRB2.js.map +0 -1
  38. package/dist/chunk-7LZAXQAO.js.map +0 -1
  39. package/dist/chunk-ENMRSITH.js.map +0 -1
@@ -50,6 +50,14 @@ export interface ReflectorRegistry {
50
50
  /** Every registered reflector, registration order (devtools enumeration —
51
51
  * independent of telemetry; a reflector idle since boot still lists). */
52
52
  names(): readonly string[];
53
+ /** Arm flush timing (idempotent). Costs one `performance.now()` pair per
54
+ * flushed reflector per frame, so it rides `engine.enableTelemetry()` and is
55
+ * off by default — the same opt-in posture as per-system timing. */
56
+ armTelemetry(): void;
57
+ /** Total µs of the most recent `flushAll` (0 until armed). */
58
+ lastFlushMicros(): number;
59
+ /** Per-reflector µs of the most recent `flushAll` (empty until armed). */
60
+ lastFlushDetail(): ReadonlyMap<string, number>;
53
61
  }
54
62
  export declare function createReflectorRegistry(world: World, opts?: {
55
63
  onFault?: (name: string, err: unknown) => void;
@@ -19,6 +19,8 @@
19
19
  * remain reviewed constants (RUNTIME_BUDGETS) in v1 — recorded.
20
20
  */
21
21
  import type { Entity, World } from "@vibecook/strata-ecs";
22
+ import { type BehaviorRuntime } from "../behavior/runtime";
23
+ import type { AnyBehaviorDef } from "../behavior/types";
22
24
  import { type Engine } from "../engine/engine";
23
25
  import type { FrameControl } from "../engine/frame-control";
24
26
  import { type InteractionStack } from "../interaction/install";
@@ -42,6 +44,23 @@ export interface CanvasEngineOpts {
42
44
  }[];
43
45
  /** Registered tools (definition happens at defineTool; validated). */
44
46
  readonly tools?: readonly Tool[];
47
+ /**
48
+ * Behaviors this engine runs (design-009). Definition is the import
49
+ * side-effect; listing them here COMPILES and installs them — a behavior that
50
+ * is defined but never registered costs nothing and does nothing, which is
51
+ * what makes "a plugin declared it" and "this engine runs it" separable.
52
+ */
53
+ readonly behaviors?: readonly AnyBehaviorDef[];
54
+ /** Generic guest breaker routing for hosts that use the published facade. */
55
+ readonly onGuestFault?: (id: string, error: unknown) => void;
56
+ readonly onGuestNotice?: (message: string) => void;
57
+ /** Behavior-specific routing preserves hook and entity provenance. The two
58
+ * pairs are COMPLEMENTARY, not alternatives: a fault that also strikes the
59
+ * breaker (a thenable hook; throws spanning instances) reaches BOTH the
60
+ * behavior route and the guest route — pass both, or the guest half of
61
+ * those events still lands on the console default. */
62
+ readonly onBehaviorFault?: (behavior: string, hook: string, entity: Entity | undefined, error: unknown) => void;
63
+ readonly onBehaviorLog?: (behavior: string, message: string, rest: readonly unknown[]) => void;
45
64
  readonly budgets?: {
46
65
  readonly keepMounted?: number;
47
66
  readonly fboBytes?: number;
@@ -166,6 +185,13 @@ export interface StageControl {
166
185
  export interface CanvasEngine {
167
186
  readonly world: World;
168
187
  readonly engine: Engine;
188
+ /**
189
+ * The behavior runtime (design-009 §6): attach/detach/has/read for
190
+ * runtime-class behaviors, plus `list()` for devtools and the doctor.
191
+ * DURABLE attachment is deliberately absent — it is a document op and goes
192
+ * through `tx.attach` inside a transaction, so it syncs and undoes.
193
+ */
194
+ readonly behaviors: BehaviorRuntime;
169
195
  readonly stack: InteractionStack;
170
196
  readonly runtime: WidgetRuntime & {
171
197
  uninstall(): void;
@@ -14,6 +14,7 @@
14
14
  import type { Component, Entity, OrderPlace, Relation, Resource, Tag, World } from "@vibecook/strata-ecs";
15
15
  import type { Mutator } from "@vibecook/strata-ecs/durable";
16
16
  import { type ComponentInit, type Prefab } from "../schema/prefab";
17
+ import type { LiveWriter } from "./live-writer";
17
18
  export interface GuardedTx {
18
19
  spawnPrefab(prefab: Prefab, overrides?: readonly ComponentInit[]): Entity;
19
20
  /** Raw spawn — entity is unknown to the guard (prefer spawnPrefab). */
@@ -32,6 +33,29 @@ export interface GuardedTx {
32
33
  /** Reorder within the current parent's sibling sequence — ordered relations only (petition 8). */
33
34
  moveRelation(e: Entity, r: Relation, place: OrderPlace): void;
34
35
  setResource<S>(res: Resource<S>, v: S): void;
36
+ /**
37
+ * Animation-integrated write (petition I15): ONE commit owns the capture, the
38
+ * durable final, and the glide. `from` is read AT CALL TIME (pre-commit by
39
+ * construction), the final lands in THIS transaction — inheriting its
40
+ * `undoable` — and after a successful seal the tail installs the verified
41
+ * public-surface glide: `TransformTween` (which GRANTS the divergence) then a
42
+ * live rewind to `from`. One commit, one undo entry, no double-write.
43
+ *
44
+ * `animateMs` absent/0 = snap (no tween attached at all). A `Grab`-held
45
+ * entity is SKIPPED — the gesture owns its divergence. An entity already
46
+ * tweening is RETARGETED (its `toX/toY` rewritten) rather than restarted, so
47
+ * a re-layout mid-glide re-aims instead of completing at a stale target.
48
+ *
49
+ * Requires `opts.live` (the session's LiveWriter) — without it the rewind
50
+ * could not be divergence-guarded, so the call refuses rather than writing
51
+ * around the guard.
52
+ */
53
+ move(e: Entity, to: {
54
+ x: number;
55
+ y: number;
56
+ }, opts?: {
57
+ animateMs?: number;
58
+ }): void;
35
59
  }
36
60
  interface GuardedEditor {
37
61
  set<S>(c: Component<S>, v: S): GuardedEditor;
@@ -39,6 +63,7 @@ interface GuardedEditor {
39
63
  interface TxDoc {
40
64
  transaction(fn: (tx: Mutator) => void, opts?: {
41
65
  undoable?: boolean;
66
+ meta?: Record<string, unknown>;
42
67
  }): void;
43
68
  }
44
69
  export interface GuardedTxOpts {
@@ -48,6 +73,38 @@ export interface GuardedTxOpts {
48
73
  * first-⌘Z-removes-a-seed quirk). Default true: one tx = one undo step.
49
74
  */
50
75
  readonly undoable?: boolean;
76
+ /**
77
+ * The session's live writer. Required by {@link GuardedTx.move} (its
78
+ * post-commit rewind is a live write to a doc cell, legal only under the
79
+ * tween grant) and by the post-seal retarget sweep below.
80
+ */
81
+ readonly live?: LiveWriter;
82
+ /** Optional entity → doc-key probe; when absent every entity is treated as
83
+ * durable-if-Position-eligible. Used to skip runtime-only movers' tx write. */
84
+ readonly keyOf?: (e: Entity) => string | undefined;
85
+ /**
86
+ * Per-commit PROVENANCE (strata 0.12.0, petition 9): a small record carried
87
+ * in-CRDT with the batch, so a receiving peer can say who wrote this and why.
88
+ * The behavior framework stamps `{ behavior, label }` on every `ctx.commit`
89
+ * (design-009 §4.4 — `label` is a stable machine id, never display text).
90
+ * Note that shallow saves erase provenance below the compaction boundary, so
91
+ * it is diagnostics, never a data dependency.
92
+ */
93
+ readonly meta?: Record<string, unknown>;
51
94
  }
52
95
  export declare function guardedTransaction(doc: TxDoc, world: World, run: (tx: GuardedTx) => void, opts?: GuardedTxOpts): void;
96
+ /**
97
+ * Retarget every live tween onto its DURABLE truth — the undo/redo chokepoint
98
+ * (petition I15). Undo does not pass through {@link guardedTransaction}: it is
99
+ * strata's own local batch, so a glide in flight when ⌘Z lands would otherwise
100
+ * complete at the pre-undo target and strand the cell exactly as above.
101
+ *
102
+ * `durablePositionOf` reads the committed value (the doc's, not the runtime's).
103
+ * A tween whose entity has no durable Position is left alone — nothing to
104
+ * reconcile against.
105
+ */
106
+ export declare function retargetTweensToDoc(world: World, entities: Iterable<Entity>, durablePositionOf: (e: Entity) => {
107
+ x: number;
108
+ y: number;
109
+ } | undefined): void;
53
110
  export {};
@@ -3,7 +3,7 @@
3
3
  * scene systems · doc kit · publish step · engine facade · reflector registry.
4
4
  * Import wall: strata-ecs + @ice/kernel ONLY — never react/dom/three (enforced).
5
5
  */
6
- export declare const CORE_VERSION = "0.1.0";
6
+ export declare const CORE_VERSION = "0.7.0";
7
7
  export { defineSchemaOnce, hmrInvalidateOnSchemaChange } from "./boot/hmr";
8
8
  export { All, Any, Not, Related, createWorld, defineQuery, defineSystem, defineTickSystem, enumOf, field, Local, } from "@vibecook/strata-ecs";
9
9
  export type { Batch, Component, Condition, Entity, OrderPlace, Pipeline, Query, Relation, Resource, System, SystemCtx, Tag, TickSystem, World, } from "@vibecook/strata-ecs";
@@ -15,6 +15,13 @@ export { createPipelineRegistry, PHASE_GROUPS, type AnySystem, type PhaseGroup,
15
15
  export { createReflectorRegistry, type ReflectorDef, type ReflectorObserve, type ReflectorQuerySpec, type ReflectorRegistry, } from "./engine/reflectors";
16
16
  export { createEngine, type Engine, type EngineOpts, type FrameTelemetry, type PublishHook, type SystemRunRecord, } from "./engine/engine";
17
17
  export { createFrameControl, SETTLE_CAP, type FrameControl } from "./engine/frame-control";
18
+ export { createGuestRegistry, GUEST_BUDGET, type GuestFactory, type GuestFrame, type GuestGeneration, type GuestInstance, type GuestLedgerRecord, type GuestRegistry, type GuestRegistryOpts, type GuestRun, type GuestSpec, type GuestStatus, } from "./engine/guests";
19
+ export { behaviors, defineBehavior, describeBehavior } from "./behavior/define-behavior";
20
+ export type { BehaviorRegistrationOptions } from "./behavior/runtime";
21
+ export { BEHAVIOR_DEFAULT_PHASE, BEHAVIOR_PHASES, type AnyBehaviorAttachSpec, type AnyBehaviorDef, type BehaviorAttachSpec, type BehaviorChanges, type BehaviorCtxBase, type BehaviorCtxFor, type BehaviorDescription, type BehaviorFrame, type BehaviorHandle, type BehaviorHookName, type BehaviorHooks, type BehaviorPhase, type BehaviorPropDescription, type BehaviorQuery, type BehaviorQuerySpec, type BehaviorRead, type BehaviorReadDescription, type BehaviorSchema, type BehaviorSpec, type BehaviorStore, type BehaviorTx, type BehaviorWorld, type DataOf, type DurableBehaviorCtx, type EphemeralBehaviorCtx, type PeerFacet, type RuntimeBehaviorCtx, } from "./behavior/types";
22
+ export { compileBehavior, isDurableEligible, type CompiledBehavior } from "./behavior/compile";
23
+ export { behaviorMarkerKey, classifyBehaviorVersion, docBehaviorVersion, stampBehaviorMarker, BEHAVIOR_MARKER_PREFIX, type BehaviorMeta, type BehaviorVerdict, } from "./behavior/migrate";
24
+ export { createBehaviorHarness, type BehaviorHarness, type BehaviorHarnessOpts, type HarnessCommit, } from "./behavior/harness";
18
25
  export { devGuardsEnabled, setDevGuards } from "./guards/dev";
19
26
  export { createLiveWriter, type LiveWriter, type LiveWriterOpts } from "./guards/live-writer";
20
27
  export { guardedTransaction, type GuardedTx, type GuardedTxOpts } from "./guards/guarded-tx";
@@ -22,6 +29,7 @@ export { writeRuntimeResource } from "./guards/resource-writer";
22
29
  export * from "./catalog";
23
30
  export * from "./helpers/phase-set";
24
31
  export * from "./helpers/version-stamps";
32
+ export * from "./helpers/churn-guard";
25
33
  export * from "./ops/arrange";
26
34
  export * from "./ops/selection";
27
35
  export * from "./ops/cascade";
@@ -48,9 +56,9 @@ export type { DurableUndoStatusValue } from "@vibecook/strata-ecs/durable";
48
56
  export { createDocSession, openDocSession, type DocSession, type DocSessionOpts, type OpenDocResult, } from "./doc/doc-kit";
49
57
  export { runMigrations, type MigrationCtx, type MigrationOutcome } from "./doc/migrate";
50
58
  export { p, type JsonShape, type PropSpec, type PropsDecl, type StandardSchemaV1 } from "./widget/props";
51
- export { WidgetEquipped, defineWidget, renames, widgets, type SizeMode, type WidgetDef, type WidgetGroup, type WidgetInteraction, type WidgetPortDecl, type WidgetRename, type WidgetRenameEntry, type WidgetSurface, type WidgetType, } from "./widget/define-widget";
59
+ export { WidgetEquipped, defineWidget, renames, widgets, type SizeMode, type WidgetDef, type WidgetGroup, type WidgetInteraction, type WidgetPortDecl, type WidgetBehaviorDecl, type WidgetBehaviorEntry, type WidgetRename, type WidgetRenameEntry, type WidgetSurface, type WidgetType, } from "./widget/define-widget";
52
60
  export { createWidgetEquipSystem } from "./widget/equip";
53
- export { attachSpawnParent, spawnWidget, widgetSpawnInits, type SpawnWidgetOpts } from "./widget/spawn";
61
+ export { attachSpawnBehaviors, attachSpawnParent, spawnWidget, widgetSpawnInits, type SpawnWidgetOpts } from "./widget/spawn";
54
62
  export { setWidgetProps } from "./widget/set-props";
55
63
  export { createWidgetRuntime, installWidgetRuntime, type MountEntry, type WidgetMountStore, type WidgetRuntime, } from "./widget/mount-store";
56
64
  export * from "./settings/defaults";
@@ -1,4 +1,5 @@
1
1
  import type { Component, Tag } from "@vibecook/strata-ecs";
2
+ import type { AnyBehaviorAttachSpec, AnyBehaviorDef } from "../behavior/types";
2
3
  import { type Prefab } from "../schema/prefab";
3
4
  import type { PropSpec, PropsDecl } from "./props";
4
5
  export type WidgetSurface = "dom" | "gl";
@@ -37,6 +38,16 @@ export interface WidgetRenameEntry {
37
38
  /** Legacy `<oldType>:<group>` components, index-aligned with `widget.groups`. */
38
39
  readonly legacyGroups: readonly Component[];
39
40
  }
41
+ /**
42
+ * A behavior pre-attachment on a widget type: the bare handle for defaults, or
43
+ * `Behavior.with({...})` for a starting value (design-009 §6).
44
+ */
45
+ export type WidgetBehaviorDecl = AnyBehaviorDef | AnyBehaviorAttachSpec;
46
+ /** Normalized pre-attachment (registry truth for the spawn + equip paths). */
47
+ export interface WidgetBehaviorEntry {
48
+ readonly behavior: AnyBehaviorDef;
49
+ readonly data: Readonly<Record<string, unknown>>;
50
+ }
40
51
  export interface WidgetInteraction {
41
52
  readonly selectable?: boolean;
42
53
  readonly movable?: boolean;
@@ -153,6 +164,21 @@ export interface WidgetDef {
153
164
  * read-only, and arms the live zombie sweep on every writable session.
154
165
  */
155
166
  readonly renamedFrom?: readonly WidgetRename[];
167
+ /**
168
+ * Behaviors every instance of this type carries (design-009 §6). The store
169
+ * class routes HOW, exactly as it routes everything else:
170
+ * - `durable` behaviors are attached as post-spawn `addComponent` calls in
171
+ * the SAME spawn transaction — document truth, so they sync and undo with
172
+ * the widget. (Not spawn-init overrides: those hit the prefab's
173
+ * all-builds eligibility throw in `instantiate.ts`.)
174
+ * - `runtime` behaviors are stamped at PROJECTION by the equip system,
175
+ * beside the capability tags. That is the only mechanism that also equips
176
+ * a widget arriving from a remote peer or a restored file, which a rider
177
+ * attached at spawn time would miss on every peer but the author's.
178
+ * Ephemeral behaviors are refused: an ephemeral instance IS the local
179
+ * presence peer, so it cannot ride a widget at all.
180
+ */
181
+ readonly behaviors?: readonly WidgetBehaviorDecl[];
156
182
  /**
157
183
  * fromVersion → idempotent absolute transform (M9's `runMigrations` runs the
158
184
  * chain at open; `prev`/return are flat prop records spanning every group).
@@ -203,6 +229,8 @@ export interface WidgetType {
203
229
  /** Escape ownership under an exclusive claim ("release" = engine-reserved). */
204
230
  readonly keyboardEscape: "release" | "widget";
205
231
  readonly migrate: Readonly<Record<number, (prev: Record<string, unknown>) => Record<string, unknown>>>;
232
+ /** Pre-attached behaviors, normalized (spawn tx for durable, equip for runtime). */
233
+ readonly behaviors: readonly WidgetBehaviorEntry[];
206
234
  /** Normalized author preview component (opaque; react narrows). null = none declared. */
207
235
  readonly previewComponent: unknown;
208
236
  /** Prop overrides for the default real-component preview mount (validated names). */
@@ -25,7 +25,7 @@ export interface StandardSchemaV1<Input = unknown, Output = Input> {
25
25
  };
26
26
  };
27
27
  }
28
- export type PropSpec = StringSpec | NumberSpec | BooleanSpec | EnumSpec | JsonSpec;
28
+ export type PropSpec = StringSpec | NumberSpec | BooleanSpec | EnumSpec | JsonSpec | EntityKeySpec;
29
29
  export interface StringSpec extends StandardSchemaV1<unknown, string> {
30
30
  readonly kind: "string";
31
31
  readonly default?: string;
@@ -52,6 +52,19 @@ export interface JsonSpec extends StandardSchemaV1<unknown, unknown> {
52
52
  /** Serialized default (derived from the inner root when not given). */
53
53
  readonly default?: string;
54
54
  }
55
+ /**
56
+ * A DURABLE entity reference (design-009 §4.2): the doc key of another entity,
57
+ * stored as a plain string cell. This is the ONLY legal cross-entity reference
58
+ * in behavior data — Law 7 bans `eid` from durable cells because a runtime
59
+ * handle means nothing to the peer that receives it, while a doc key survives
60
+ * the wire, the file, and a reload. Empty string = no reference; resolution is
61
+ * `ctx.entityFor(key)`, which may legitimately return undefined while the
62
+ * target is not yet projected.
63
+ */
64
+ export interface EntityKeySpec extends StandardSchemaV1<unknown, string> {
65
+ readonly kind: "entityKey";
66
+ readonly default?: string;
67
+ }
55
68
  /** Shapes legal only INSIDE p.json (validation-only, never components). */
56
69
  export type JsonShape = {
57
70
  readonly kind: "string";
@@ -91,8 +104,29 @@ export declare const p: {
91
104
  json(inner: JsonShape, opts?: {
92
105
  default?: unknown;
93
106
  }): JsonSpec;
107
+ /**
108
+ * A durable reference to another entity by DOC KEY (design-009 §4.2).
109
+ * Behavior-facing: `defineWidget` props have no use for it (a widget's
110
+ * references are relations), but nothing rejects it there either — it is an
111
+ * ordinary string cell with a declared meaning.
112
+ */
113
+ entityKey(opts?: {
114
+ default?: string;
115
+ }): EntityKeySpec;
94
116
  array(item: JsonShape): JsonShape;
95
117
  object(fields: Readonly<Record<string, JsonShape>>): JsonShape;
96
118
  };
97
119
  /** A widget props declaration: top-level named specs (the compiler's input). */
98
120
  export type PropsDecl = Readonly<Record<string, PropSpec>>;
121
+ /**
122
+ * The CELL default for a spec — the value a whole-component write fills in for
123
+ * an untouched field, and the value a generated strata field declares.
124
+ *
125
+ * One function, three callers (widget spawn defaults, `defineWidget`'s
126
+ * essential set, `defineBehavior`'s attach defaults): each used to carry its
127
+ * own else-if chain over `spec.kind`, and each chain had to be found and
128
+ * amended when `p.entityKey` was added — one of them by inspection rather than
129
+ * by a type error, since the chains end in a catch-all `else`. It lives here,
130
+ * beside the specs whose shape it reads.
131
+ */
132
+ export declare function defaultValueOf(spec: PropSpec): string | number | boolean;
@@ -8,7 +8,9 @@
8
8
  */
9
9
  import type { Entity, OrderPlace, Relation, World } from "@vibecook/strata-ecs";
10
10
  import type { DurableStore } from "@vibecook/strata-ecs/durable";
11
+ import { type GuardedTx } from "../guards/guarded-tx";
11
12
  import { type ComponentInit } from "../schema/prefab";
13
+ import { type WidgetType } from "./define-widget";
12
14
  export interface SpawnWidgetOpts {
13
15
  readonly x: number;
14
16
  readonly y: number;
@@ -47,4 +49,16 @@ export declare function widgetSpawnInits(type: string, opts: SpawnWidgetOpts): {
47
49
  prefab: import("../schema/prefab").Prefab;
48
50
  overrides: ComponentInit[];
49
51
  };
52
+ /**
53
+ * Attach a widget type's DURABLE pre-attached behaviors (design-009 §6), as
54
+ * post-spawn `addComponent` calls inside the spawn transaction.
55
+ *
56
+ * Post-spawn rather than spawn-init overrides: `instantiate` checks every
57
+ * override against the prefab's eligible set, and a behavior is eligible on
58
+ * every entity by definition (BF-D6) — it is not in any prefab's set and never
59
+ * will be. Runtime pre-attachments are NOT here: they ride projection (the
60
+ * equip system), which is the only path that also equips a widget arriving
61
+ * from a peer or a restored file.
62
+ */
63
+ export declare function attachSpawnBehaviors(tx: GuardedTx, widget: WidgetType, e: Entity): void;
50
64
  export declare function spawnWidget(store: DurableStore, world: World, type: string, opts: SpawnWidgetOpts): Entity;
@@ -17,6 +17,15 @@
17
17
  * The dt clamp is what makes resume safe: a freeze that lasted an hour hands
18
18
  * `step` an enormous `now`, and core clamps the delta (frame-info.ts), so
19
19
  * tweens advance by one ordinary frame instead of teleporting to their end.
20
+ *
21
+ * THE LOOP OUTLIVES A THROWING STEP (2026-08-15, petition I14). Until this fix
22
+ * the reschedule sat on the line AFTER `engine.step(now)`, so ONE throw from a
23
+ * publish hook (which propagate loudly by design — engine.ts) escaped the rAF
24
+ * callback, left `handle` un-renewed, and killed the canvas PERMANENTLY: with
25
+ * `parked` still false, `wake` early-returns forever, so even freeze/thaw could
26
+ * not restart it. Recovery needed a fresh `startRafLoop`. The next frame is now
27
+ * scheduled in a `finally`, so a faulting frame is loud but survivable — the
28
+ * throw still reaches the browser's error handler; only the death is gone.
20
29
  */
21
30
  import type { Engine } from "@ice/core";
22
31
  export declare function startRafLoop(engine: Engine): () => void;
@@ -13,7 +13,7 @@
13
13
  * (provided per-portal by WidgetRoot) and suspends its subscription while
14
14
  * hidden, serving the last snapshot; on show it resubscribes and refreshes.
15
15
  */
16
- import { type Component, type Entity, type World } from "@ice/core";
16
+ import { type AnyBehaviorDef, type Component, type Entity, type World } from "@ice/core";
17
17
  /** Per-portal hidden flag (WidgetRoot provides it; defaults to live). */
18
18
  export declare const WidgetHiddenContext: import("react").Context<boolean>;
19
19
  /**
@@ -30,6 +30,17 @@ export declare function useWorldComponent<S>(world: World, entity: Entity, compo
30
30
  * stable per underlying group value (memo on the raw reference).
31
31
  */
32
32
  export declare function useWidgetProps<T extends Record<string, unknown>>(world: World, entity: Entity, type: string, group?: string): T | undefined;
33
+ /**
34
+ * Live behavior data for one entity (design-009 §6, Tier-3 — the
35
+ * `useWidgetProps` pattern). `p.json` fields come back parsed; the value is
36
+ * `undefined` when the behavior is not attached, which is a legitimate render
37
+ * state and not an error.
38
+ *
39
+ * READ-ONLY by construction. A face renders behavior state; it never writes it
40
+ * (Law 10) — writes go through `ctx` inside the behavior's own hooks, or
41
+ * through `engine.behaviors.attach` / a transaction from an app handler.
42
+ */
43
+ export declare function useBehavior<T extends Record<string, unknown>>(world: World, entity: Entity, behavior: AnyBehaviorDef): T | undefined;
33
44
  /**
34
45
  * Selection membership (Tier-1 wake on Selected churn; O(1) snapshot).
35
46
  * Frozen-hidden like every widget hook: a selection change must not re-render
@@ -4,7 +4,7 @@
4
4
  * (enforced). react/react-dom are peers.
5
5
  */
6
6
  export declare const REACT_VERSION = "0.0.0";
7
- export { WidgetHiddenContext, useBreakpoint, useSelected, useWidgetProps, useWorldComponent, } from "./hooks";
7
+ export { WidgetHiddenContext, useBreakpoint, useSelected, useBehavior, useWidgetProps, useWorldComponent, } from "./hooks";
8
8
  export { WidgetRoot, type WidgetComponentProps, type WidgetHosts, type WidgetRootProps } from "./widget-root";
9
9
  export { EngineProvider, useCanvasEngine, useFrameFreeze, useOps, useStageHold, useWorld, type EngineProviderProps, } from "./engine-context";
10
10
  export { useCommit, useUndoStatus, type Commit, type UndoStatus } from "./use-commit";
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@vibecook/ice",
3
- "version": "0.4.0",
3
+ "version": "0.7.0",
4
4
  "description": "ICE — infinite canvas engine. Figma-grade infinite-canvas UX as a framework: defineWidget turns React/R3F components into canvas citizens — selectable, movable, resizable, wired into node graphs, CRDT-synced, undoable per gesture.",
5
5
  "license": "MIT",
6
6
  "author": "James Yong <me@jamesyong42.com>",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/jamesyong-42/infinite-canvas-engine.git"
10
+ "url": "git+https://github.com/vibefield/infinite-canvas-engine.git"
11
11
  },
12
- "homepage": "https://jamesyong-42.github.io/infinite-canvas-engine/",
13
- "bugs": "https://github.com/jamesyong-42/infinite-canvas-engine/issues",
12
+ "homepage": "https://vibefield.github.io/infinite-canvas-engine/",
13
+ "bugs": "https://github.com/vibefield/infinite-canvas-engine/issues",
14
14
  "keywords": [
15
15
  "infinite-canvas",
16
16
  "canvas",
@@ -26,50 +26,28 @@
26
26
  "three",
27
27
  "gestures"
28
28
  ],
29
- "files": [
30
- "dist"
31
- ],
29
+ "files": ["dist"],
32
30
  "main": "./dist/index.js",
33
31
  "types": "./dist/types/packages/ice/src/index.d.ts",
34
32
  "exports": {
35
- ".": {
36
- "types": "./dist/types/packages/ice/src/index.d.ts",
37
- "default": "./dist/index.js"
38
- },
39
- "./kernel": {
40
- "types": "./dist/types/packages/ice/src/kernel.d.ts",
41
- "default": "./dist/kernel.js"
42
- },
43
- "./dom": {
44
- "types": "./dist/types/packages/ice/src/dom.d.ts",
45
- "default": "./dist/dom.js"
46
- },
47
- "./react": {
48
- "types": "./dist/types/packages/ice/src/react.d.ts",
49
- "default": "./dist/react.js"
50
- },
51
- "./r3f": {
52
- "types": "./dist/types/packages/ice/src/r3f.d.ts",
53
- "default": "./dist/r3f.js"
54
- },
55
- "./ground": {
56
- "types": "./dist/types/packages/ice/src/ground.d.ts",
57
- "default": "./dist/ground.js"
58
- },
59
- "./devtools": {
60
- "types": "./dist/types/packages/ice/src/devtools.d.ts",
61
- "default": "./dist/devtools.js"
62
- },
33
+ ".": { "types": "./dist/types/packages/ice/src/index.d.ts", "default": "./dist/index.js" },
34
+ "./kernel": { "types": "./dist/types/packages/ice/src/kernel.d.ts", "default": "./dist/kernel.js" },
35
+ "./dom": { "types": "./dist/types/packages/ice/src/dom.d.ts", "default": "./dist/dom.js" },
36
+ "./react": { "types": "./dist/types/packages/ice/src/react.d.ts", "default": "./dist/react.js" },
37
+ "./r3f": { "types": "./dist/types/packages/ice/src/r3f.d.ts", "default": "./dist/r3f.js" },
38
+ "./ground": { "types": "./dist/types/packages/ice/src/ground.d.ts", "default": "./dist/ground.js" },
39
+ "./devtools": { "types": "./dist/types/packages/ice/src/devtools.d.ts", "default": "./dist/devtools.js" },
63
40
  "./package.json": "./package.json"
64
41
  },
65
- "engines": {
66
- "node": ">=20"
67
- },
68
- "publishConfig": {
69
- "access": "public"
42
+ "engines": { "node": ">=20" },
43
+ "publishConfig": { "access": "public" },
44
+ "scripts": {
45
+ "typecheck": "tsc --noEmit",
46
+ "build": "tsup && tsc -p tsconfig.dts.json",
47
+ "prepack": "pnpm run build"
70
48
  },
71
49
  "dependencies": {
72
- "@vibecook/strata-ecs": "0.11.0",
50
+ "@vibecook/strata-ecs": "0.12.0",
73
51
  "loro-crdt": "^1.13.8",
74
52
  "stats-gl": "^4.2.3",
75
53
  "rbush": "^3.0.1"
@@ -81,20 +59,18 @@
81
59
  "three": ">=0.160.0"
82
60
  },
83
61
  "peerDependenciesMeta": {
84
- "@react-three/fiber": {
85
- "optional": true
86
- },
87
- "react": {
88
- "optional": true
89
- },
90
- "react-dom": {
91
- "optional": true
92
- },
93
- "three": {
94
- "optional": true
95
- }
62
+ "@react-three/fiber": { "optional": true },
63
+ "react": { "optional": true },
64
+ "react-dom": { "optional": true },
65
+ "three": { "optional": true }
96
66
  },
97
67
  "devDependencies": {
68
+ "@ice/core": "workspace:*",
69
+ "@ice/devtools": "workspace:*",
70
+ "@ice/dom": "workspace:*",
71
+ "@ice/kernel": "workspace:*",
72
+ "@ice/r3f": "workspace:*",
73
+ "@ice/react": "workspace:*",
98
74
  "@react-three/fiber": "^9",
99
75
  "@types/rbush": "^3.0.3",
100
76
  "@types/react": "^19",
@@ -102,16 +78,6 @@
102
78
  "@types/three": "^0.185.0",
103
79
  "react": "^19",
104
80
  "react-dom": "^19",
105
- "three": "^0.185.0",
106
- "@ice/devtools": "0.0.0",
107
- "@ice/dom": "0.0.0",
108
- "@ice/core": "0.0.0",
109
- "@ice/react": "0.0.0",
110
- "@ice/kernel": "0.0.0",
111
- "@ice/r3f": "0.0.0"
112
- },
113
- "scripts": {
114
- "typecheck": "tsc --noEmit",
115
- "build": "tsup && tsc -p tsconfig.dts.json"
81
+ "three": "^0.185.0"
116
82
  }
117
- }
83
+ }