@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.
- package/README.md +4 -4
- package/dist/{chunk-3PHPWRB2.js → chunk-55QCOE74.js} +7 -4
- package/dist/chunk-55QCOE74.js.map +1 -0
- package/dist/{chunk-ENMRSITH.js → chunk-5OU3OWDH.js} +16 -3
- package/dist/chunk-5OU3OWDH.js.map +1 -0
- package/dist/{chunk-7LZAXQAO.js → chunk-LATXYFMO.js} +6823 -3926
- package/dist/chunk-LATXYFMO.js.map +1 -0
- package/dist/devtools.js +5 -2
- package/dist/devtools.js.map +1 -1
- package/dist/dom.js +2 -2
- package/dist/ground.js +1 -1
- package/dist/index.js +35 -1
- package/dist/r3f.js +3 -3
- package/dist/react.js +5 -3
- package/dist/types/packages/core/src/behavior/compile.d.ts +74 -0
- package/dist/types/packages/core/src/behavior/define-behavior.d.ts +56 -0
- package/dist/types/packages/core/src/behavior/differ.d.ts +91 -0
- package/dist/types/packages/core/src/behavior/harness.d.ts +65 -0
- package/dist/types/packages/core/src/behavior/migrate.d.ts +87 -0
- package/dist/types/packages/core/src/behavior/public-reads.d.ts +31 -0
- package/dist/types/packages/core/src/behavior/runtime.d.ts +102 -0
- package/dist/types/packages/core/src/behavior/types.d.ts +358 -0
- package/dist/types/packages/core/src/behavior/world-view.d.ts +36 -0
- package/dist/types/packages/core/src/engine/engine.d.ts +38 -1
- package/dist/types/packages/core/src/engine/guests.d.ts +199 -0
- package/dist/types/packages/core/src/engine/reflectors.d.ts +8 -0
- package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +26 -0
- package/dist/types/packages/core/src/guards/guarded-tx.d.ts +57 -0
- package/dist/types/packages/core/src/index.d.ts +11 -3
- package/dist/types/packages/core/src/widget/define-widget.d.ts +28 -0
- package/dist/types/packages/core/src/widget/props.d.ts +35 -1
- package/dist/types/packages/core/src/widget/spawn.d.ts +14 -0
- package/dist/types/packages/dom/src/loop.d.ts +9 -0
- package/dist/types/packages/react/src/hooks.d.ts +12 -1
- package/dist/types/packages/react/src/index.d.ts +1 -1
- package/package.json +31 -65
- package/dist/chunk-3PHPWRB2.js.map +0 -1
- package/dist/chunk-7LZAXQAO.js.map +0 -1
- 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.
|
|
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.
|
|
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/
|
|
10
|
+
"url": "git+https://github.com/vibefield/infinite-canvas-engine.git"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://
|
|
13
|
-
"bugs": "https://github.com/
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
"./
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"
|
|
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.
|
|
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
|
-
|
|
86
|
-
},
|
|
87
|
-
"
|
|
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
|
+
}
|