@vibecook/ice 0.1.0 → 0.2.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 +37 -2
- package/dist/chunk-5SJYHQI4.js +636 -0
- package/dist/chunk-5SJYHQI4.js.map +1 -0
- package/dist/{chunk-5S7GSXJG.js → chunk-DXU3RZZS.js} +2913 -1180
- package/dist/chunk-DXU3RZZS.js.map +1 -0
- package/dist/{chunk-FIAXPNNY.js → chunk-IASTQMU5.js} +130 -551
- package/dist/chunk-IASTQMU5.js.map +1 -0
- package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
- package/dist/chunk-OR5FNVUE.js.map +1 -0
- package/dist/devtools.js +508 -372
- package/dist/devtools.js.map +1 -1
- package/dist/dom.js +3 -11
- package/dist/ground.js +715 -0
- package/dist/ground.js.map +1 -0
- package/dist/index.js +70 -2
- package/dist/kernel.js +31 -1
- package/dist/r3f.js +599 -62
- package/dist/r3f.js.map +1 -1
- package/dist/react.js +34 -418
- package/dist/react.js.map +1 -1
- package/dist/types/packages/core/src/catalog/camera-derived.d.ts +41 -3
- package/dist/types/packages/core/src/catalog/gesture.d.ts +39 -3
- package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
- package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
- package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
- package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
- package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
- package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
- package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
- package/dist/types/packages/core/src/doc/autosave.d.ts +15 -2
- package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
- package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
- package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
- package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
- package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
- package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
- package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
- package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
- package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
- package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
- package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +70 -6
- package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
- package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
- package/dist/types/packages/core/src/index.d.ts +13 -7
- package/dist/types/packages/core/src/input/queue.d.ts +11 -0
- package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
- package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
- package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
- package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
- package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
- package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
- package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
- package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
- package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
- package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
- package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
- package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
- package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
- package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
- package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
- package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
- package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
- package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
- package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
- package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
- package/dist/types/packages/devtools/src/attach.d.ts +77 -0
- package/dist/types/packages/devtools/src/dock.d.ts +38 -0
- package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
- package/dist/types/packages/devtools/src/index.d.ts +8 -6
- package/dist/types/packages/dom/src/index.d.ts +1 -3
- package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
- package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
- package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
- package/dist/types/packages/ground/src/index.d.ts +52 -0
- package/dist/types/packages/ground/src/layer.d.ts +40 -0
- package/dist/types/packages/ground/src/pass.d.ts +41 -0
- package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
- package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
- package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
- package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
- package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
- package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
- package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
- package/dist/types/packages/ground/src/renderer.d.ts +15 -0
- package/dist/types/packages/ice/src/ground.d.ts +1 -0
- package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
- package/dist/types/packages/kernel/src/coords.d.ts +15 -0
- package/dist/types/packages/kernel/src/easing.d.ts +11 -0
- package/dist/types/packages/kernel/src/index.d.ts +3 -0
- package/dist/types/packages/kernel/src/layout.d.ts +118 -0
- package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
- package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
- package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
- package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
- package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
- package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
- package/dist/types/packages/r3f/src/index.d.ts +3 -2
- package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
- package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
- package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
- package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
- package/dist/types/packages/react/src/engine-context.d.ts +8 -0
- package/dist/types/packages/react/src/index.d.ts +4 -2
- package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
- package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
- package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
- package/package.json +13 -8
- package/dist/chunk-5S7GSXJG.js.map +0 -1
- package/dist/chunk-BCJ47SX7.js.map +0 -1
- package/dist/chunk-FIAXPNNY.js.map +0 -1
- package/dist/types/packages/devtools/src/panel.d.ts +0 -48
- package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
- package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
import type { Entity, World } from "@vibecook/strata-ecs";
|
|
22
22
|
import { type Engine } from "../engine/engine";
|
|
23
23
|
import { type InteractionStack } from "../interaction/install";
|
|
24
|
-
import { type NestedCanvas } from "../nav/nested-canvas";
|
|
24
|
+
import { type NavOpts, type NestedCanvas } from "../nav/nested-canvas";
|
|
25
|
+
import { type ArrangeOpts } from "../ops/arrange";
|
|
26
|
+
import { type InsertByDragOpts } from "../ops/insert";
|
|
25
27
|
import { type WidgetRuntime } from "../widget/mount-store";
|
|
26
28
|
import { type SpawnWidgetOpts } from "../widget/spawn";
|
|
27
29
|
import { type Tool } from "../tools/define-tool";
|
|
@@ -29,6 +31,7 @@ import { type DocSession, type DocSessionOpts, type OpenDocResult } from "../doc
|
|
|
29
31
|
import { type JoinDocOpts, type JoinResult } from "../doc/bootstrap";
|
|
30
32
|
import type { ByteChannel } from "../doc/channels";
|
|
31
33
|
import { type Autosave, type AutosaveOpts, type AutosaveStorageWrite } from "../doc/autosave";
|
|
34
|
+
import { type PresenceSession } from "../presence/presence-kit";
|
|
32
35
|
import type { MeasureQueue } from "../input/measure-queue";
|
|
33
36
|
import { GESTURE_DEFAULTS, POINTER_DEFAULTS } from "../settings/defaults";
|
|
34
37
|
export interface CanvasEngineOpts {
|
|
@@ -53,6 +56,10 @@ export interface CanvasEngineOpts {
|
|
|
53
56
|
readonly enabled?: boolean;
|
|
54
57
|
readonly thresholdPx?: number;
|
|
55
58
|
};
|
|
59
|
+
/** Selection-chrome knobs: liftScale = the app's visual drag-lift scale (union box wraps it). */
|
|
60
|
+
readonly chrome?: {
|
|
61
|
+
readonly liftScale?: number;
|
|
62
|
+
};
|
|
56
63
|
};
|
|
57
64
|
readonly policy?: {
|
|
58
65
|
/** The gate verdict a "migrate"-classified doc downgrades to when migration is off/fails. */
|
|
@@ -65,40 +72,96 @@ export interface CanvasOps {
|
|
|
65
72
|
/** cancel active gestures → switch (design-003 §8; the strata-example lesson). */
|
|
66
73
|
setTool(id: string): void;
|
|
67
74
|
spawnWidget(type: string, opts: SpawnWidgetOpts): Entity;
|
|
75
|
+
/**
|
|
76
|
+
* Tray adoption (2026-07-19): DRAFT-spawn a runtime ghost of `type` centered
|
|
77
|
+
* under the pressing pointer and enqueue one synthetic down — the ordinary
|
|
78
|
+
* drag stack takes over (lift, snap, drop targets, folder consume). Release
|
|
79
|
+
* promotes it through ONE `create` tx (selected, one undo step); cancel or a
|
|
80
|
+
* rejected drop flies it back to the tray press point and despawns it with
|
|
81
|
+
* zero undo footprint. Call from the tray tile's `pointerdown` AFTER
|
|
82
|
+
* `stopPropagation()` (the down must not double-land via the adapter).
|
|
83
|
+
*/
|
|
84
|
+
insertByDrag(type: string, opts: InsertByDragOpts): Entity;
|
|
85
|
+
/** Validated prop update: Standard-Schema-checked, json serialized, ONE tx (2026-07-13 review). */
|
|
86
|
+
setWidgetProps(entity: Entity, props: Readonly<Record<string, unknown>>): void;
|
|
68
87
|
deleteSelection(): void;
|
|
69
88
|
/** Twin spawns offset +16/+16; the clones become the selection. */
|
|
70
89
|
duplicateSelection(): Entity[];
|
|
71
90
|
setSelection(ids: readonly Entity[], mode?: "replace" | "add" | "toggle"): void;
|
|
72
91
|
clearSelection(): void;
|
|
73
92
|
selectAll(): void;
|
|
74
|
-
/**
|
|
93
|
+
/** Sibling-sequence sweep, one tx: the ids to the frame top/bottom (petition 8). */
|
|
75
94
|
reorder(ids: readonly Entity[], mode: "top" | "bottom"): void;
|
|
95
|
+
/**
|
|
96
|
+
* Desktop-style Clean Up (kernel packLayout): tidy reading-order rows, one
|
|
97
|
+
* undo step, 240ms glide (durationMs: 0 snaps). Scope: ids > selection ≥2 >
|
|
98
|
+
* current nav frame. Returns the movers (empty = already tidy).
|
|
99
|
+
*/
|
|
100
|
+
arrange(opts?: ArrangeOpts): Entity[];
|
|
76
101
|
zoomToFit(ids?: readonly Entity[]): void;
|
|
102
|
+
/**
|
|
103
|
+
* The natural DEFAULT framing (2026-07-18): zoom-to-fit the current
|
|
104
|
+
* frame's widgets inside the FIT_DEFAULTS band (never past 100%, never
|
|
105
|
+
* below 50%, ∩ settings.zoom) — same band as the folder arrival camera.
|
|
106
|
+
* Returns false (camera untouched) until a real viewport AND content
|
|
107
|
+
* exist, so boot code can retry. zoomToFit stays the uncapped explicit
|
|
108
|
+
* "show me everything".
|
|
109
|
+
*/
|
|
110
|
+
frameContent(): boolean;
|
|
77
111
|
zoomTo(zoom: number, anchor?: {
|
|
78
112
|
x: number;
|
|
79
113
|
y: number;
|
|
80
114
|
}): void;
|
|
81
115
|
panTo(x: number, y: number): void;
|
|
82
|
-
enterContainer(container: Entity): void;
|
|
83
|
-
exitContainer(): void;
|
|
116
|
+
enterContainer(container: Entity, opts?: NavOpts): void;
|
|
117
|
+
exitContainer(opts?: NavOpts): void;
|
|
118
|
+
/** Pop several levels in ONE transition (breadcrumb jumps; design-006 §5). */
|
|
119
|
+
exitTo(targetDepth: number, opts?: NavOpts): void;
|
|
84
120
|
cancelActiveGestures(): void;
|
|
85
121
|
}
|
|
86
122
|
export interface CanvasDocs {
|
|
87
123
|
create(opts?: DocSessionOpts): DocSession;
|
|
88
124
|
open(bytes: Uint8Array, opts?: DocSessionOpts): OpenDocResult;
|
|
89
|
-
/**
|
|
90
|
-
|
|
125
|
+
/**
|
|
126
|
+
* §6.5 bootstrap + optional presence sugar over the same channel. `signal`/
|
|
127
|
+
* `onSession` are facade-owned (supersede + re-bootstrap wiring) — track the
|
|
128
|
+
* live session through `docs.current()`. Rejects if another document is
|
|
129
|
+
* opened while the join is in flight.
|
|
130
|
+
*/
|
|
131
|
+
join(channel: ByteChannel, opts?: Omit<JoinDocOpts, "presence" | "signal" | "onSession"> & {
|
|
91
132
|
readonly presence?: {
|
|
92
133
|
name: string;
|
|
93
134
|
color: string;
|
|
94
135
|
};
|
|
95
136
|
}): Promise<JoinResult>;
|
|
96
137
|
current(): DocSession | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* The live presence session (the `join({presence})` sugar's attachment), or
|
|
140
|
+
* undefined when doc-less / presence-less. An INSPECTION seam — the devtools
|
|
141
|
+
* dock's ephemeral tab reads `presence().eph` (DevtoolsOpts.presence) —
|
|
142
|
+
* never a sync path; same contract as `DocSession.attachment`. Swaps with
|
|
143
|
+
* the doc lifecycle exactly like `current()`: set by join, cleared by close.
|
|
144
|
+
*/
|
|
145
|
+
presence(): PresenceSession | undefined;
|
|
97
146
|
close(): void;
|
|
98
147
|
undo(): boolean;
|
|
99
148
|
redo(): boolean;
|
|
100
149
|
autosave(storage: AutosaveStorageWrite, opts?: Omit<AutosaveOpts, "storage" | "world">): Autosave;
|
|
101
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Stage presentation control (StageMode resource, 2026-07-19): app chrome
|
|
153
|
+
* that covers/recedes the canvas takes a refcounted, NAMED background hold —
|
|
154
|
+
* the disposer pattern makes overlay lifecycles leak-proof (release on
|
|
155
|
+
* unmount), the name makes "why isn't the canvas animating?" answerable.
|
|
156
|
+
* Rendering policy only — input/interactability stays the overlay's concern.
|
|
157
|
+
*/
|
|
158
|
+
export interface StageControl {
|
|
159
|
+
/** Take a named background hold. Returns an IDEMPOTENT release. */
|
|
160
|
+
background(name: string): () => void;
|
|
161
|
+
isBackgrounded(): boolean;
|
|
162
|
+
/** Live hold names, in take order (debug/devtools). */
|
|
163
|
+
holds(): readonly string[];
|
|
164
|
+
}
|
|
102
165
|
export interface CanvasEngine {
|
|
103
166
|
readonly world: World;
|
|
104
167
|
readonly engine: Engine;
|
|
@@ -109,6 +172,7 @@ export interface CanvasEngine {
|
|
|
109
172
|
readonly nav: NestedCanvas;
|
|
110
173
|
readonly ops: CanvasOps;
|
|
111
174
|
readonly docs: CanvasDocs;
|
|
175
|
+
readonly stage: StageControl;
|
|
112
176
|
readonly budgets: {
|
|
113
177
|
readonly keepMounted: number;
|
|
114
178
|
readonly fboBytes: number;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Tags are not eligibility-checked in M2 (essential tags spawn with the
|
|
12
12
|
* prefab; optional-tag modeling is future work — documented).
|
|
13
13
|
*/
|
|
14
|
-
import type { Component, Entity, Relation, Resource, Tag, World } from "@vibecook/strata-ecs";
|
|
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
17
|
export interface GuardedTx {
|
|
@@ -26,9 +26,11 @@ export interface GuardedTx {
|
|
|
26
26
|
};
|
|
27
27
|
addTag(e: Entity, t: Tag): void;
|
|
28
28
|
removeTag(e: Entity, t: Tag): void;
|
|
29
|
-
setRelation(e: Entity, r: Relation, target: Entity): void;
|
|
29
|
+
setRelation(e: Entity, r: Relation, target: Entity, place?: OrderPlace): void;
|
|
30
30
|
addRelation(e: Entity, r: Relation, target: Entity): void;
|
|
31
31
|
removeRelation(e: Entity, r: Relation, target?: Entity): void;
|
|
32
|
+
/** Reorder within the current parent's sibling sequence — ordered relations only (petition 8). */
|
|
33
|
+
moveRelation(e: Entity, r: Relation, place: OrderPlace): void;
|
|
32
34
|
setResource<S>(res: Resource<S>, v: S): void;
|
|
33
35
|
}
|
|
34
36
|
interface GuardedEditor {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Churn guard — the `runIf` half of the petition-7 gating pattern
|
|
3
|
+
* (design-002 §4 "guards live in runIf, never the body" × strata 0.7.0
|
|
4
|
+
* `world.changes`).
|
|
5
|
+
*
|
|
6
|
+
* `activeMembership` gates by draining a collector INSIDE its tick body
|
|
7
|
+
* (legal there: it declares no writes, so an early-out stamps nothing).
|
|
8
|
+
* Systems that DECLARE `access.write` cannot use that shape — strata
|
|
9
|
+
* blanket-stamps declared writes for any system that RUNS, even one that
|
|
10
|
+
* early-outs (see helpers/version-stamps.ts) — so their gate must be a real
|
|
11
|
+
* `runIf`. But a naive collector-in-runIf would LOSE the drained delta when
|
|
12
|
+
* the body runs: this guard drains in `runIf`, stashes the delta, and hands
|
|
13
|
+
* it to the body via `take()` in the same frame.
|
|
14
|
+
*
|
|
15
|
+
* Contract:
|
|
16
|
+
* - `runIf` drains the collector every frame (collector creation is lazy —
|
|
17
|
+
* a guard whose system is never installed never journals) and evaluates
|
|
18
|
+
* the optional `extra` trigger (a value-compare closure, e.g. "camera
|
|
19
|
+
* zoom changed"; it is called every frame so it can keep its own cache
|
|
20
|
+
* current). Returns true iff there is work.
|
|
21
|
+
* - `take()` (body, same frame) returns the work: `full` means rebuild
|
|
22
|
+
* everything (first run, `world.reset`, coarse writes, or `extra` fired);
|
|
23
|
+
* otherwise `changed`/`removed` are the journaled entities. The arrays
|
|
24
|
+
* alias the collector's internal buffers — valid until the next drain,
|
|
25
|
+
* i.e. for the body that runs this frame; do not retain them.
|
|
26
|
+
*/
|
|
27
|
+
import type { CollectOptions, Entity, World } from "@vibecook/strata-ecs";
|
|
28
|
+
export interface ChurnWork {
|
|
29
|
+
/** Rebuild everything: first run, reset, coarse writes, or `extra` fired. */
|
|
30
|
+
readonly full: boolean;
|
|
31
|
+
readonly changed: readonly Entity[];
|
|
32
|
+
readonly removed: readonly Entity[];
|
|
33
|
+
}
|
|
34
|
+
export interface ChurnGuard {
|
|
35
|
+
/** Drop into a system's `runIf`. */
|
|
36
|
+
readonly runIf: () => boolean;
|
|
37
|
+
/** The work stashed by this frame's `runIf` (undefined if it returned false). */
|
|
38
|
+
take(): ChurnWork | undefined;
|
|
39
|
+
}
|
|
40
|
+
export declare function makeChurnGuard(world: World, collect: CollectOptions, extra?: () => boolean): ChurnGuard;
|
|
@@ -3,10 +3,10 @@
|
|
|
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.1.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
|
-
export type { Batch, Component, Condition, Entity, Pipeline, Query, Relation, Resource, System, SystemCtx, Tag, TickSystem, World, } from "@vibecook/strata-ecs";
|
|
9
|
+
export type { Batch, Component, Condition, Entity, OrderPlace, Pipeline, Query, Relation, Resource, System, SystemCtx, Tag, TickSystem, World, } from "@vibecook/strata-ecs";
|
|
10
10
|
export { defineComponent, defineRelation, defineResource, defineTag, schemaMeta, type ComponentMeta, type RawSchema, type RelationMeta, type ResourceMeta, } from "./schema/meta";
|
|
11
11
|
export { definePrefab, init, PrefabId, prefabs, type ComponentInit, type FieldWrite, type Prefab, type PrefabClass, type PrefabDef, } from "./schema/prefab";
|
|
12
12
|
export { instantiate, type EphSpawner, type SpawnTarget } from "./engine/instantiate";
|
|
@@ -21,13 +21,16 @@ export { writeRuntimeResource } from "./guards/resource-writer";
|
|
|
21
21
|
export * from "./catalog";
|
|
22
22
|
export * from "./helpers/phase-set";
|
|
23
23
|
export * from "./helpers/version-stamps";
|
|
24
|
+
export * from "./ops/arrange";
|
|
24
25
|
export * from "./ops/selection";
|
|
25
26
|
export * from "./ops/cascade";
|
|
26
27
|
export * from "./ops/claims";
|
|
27
28
|
export * from "./ops/gestures";
|
|
28
29
|
export * from "./ops/point-pick";
|
|
30
|
+
export * from "./ops/sibling-order";
|
|
31
|
+
export { portSlots, portSlotOf } from "./ops/port-geometry";
|
|
29
32
|
export { createInputQueue, NO_MODS, type InputEvent, type InputEventKind, type InputMods, type InputQueue } from "./input/queue";
|
|
30
|
-
export { createRecordingCommitSink, type CommitIntent, type CommitCreate, type CommitReparent, type CommitSink, type CommitWrite, } from "./engine/commit-sink";
|
|
33
|
+
export { createRecordingCommitSink, type CommitIntent, type CommitCreate, type CommitOrder, type CommitReparent, type CommitSink, type CommitWrite, } from "./engine/commit-sink";
|
|
31
34
|
export { ensureCanvasSurface, installInteractionCore, installInteractionStack, type InteractionCore, type InteractionCoreOpts, type InteractionStack, } from "./interaction/install";
|
|
32
35
|
export type { MarqueeBuffer } from "./systems/l3-marquee";
|
|
33
36
|
export { DEFAULT_SPAWN_PROFILES, type SingleKindName, type SpawnProfiles } from "./systems/l2-recognize";
|
|
@@ -45,19 +48,22 @@ export { runMigrations, type MigrationCtx, type MigrationOutcome } from "./doc/m
|
|
|
45
48
|
export { p, type JsonShape, type PropSpec, type PropsDecl, type StandardSchemaV1 } from "./widget/props";
|
|
46
49
|
export { WidgetEquipped, defineWidget, widgets, type SizeMode, type WidgetDef, type WidgetGroup, type WidgetInteraction, type WidgetPortDecl, type WidgetSurface, type WidgetType, } from "./widget/define-widget";
|
|
47
50
|
export { createWidgetEquipSystem } from "./widget/equip";
|
|
48
|
-
export { spawnWidget, widgetSpawnInits, type SpawnWidgetOpts } from "./widget/spawn";
|
|
51
|
+
export { attachSpawnParent, spawnWidget, widgetSpawnInits, type SpawnWidgetOpts } from "./widget/spawn";
|
|
52
|
+
export { setWidgetProps } from "./widget/set-props";
|
|
49
53
|
export { createWidgetRuntime, installWidgetRuntime, type MountEntry, type WidgetMountStore, type WidgetRuntime, } from "./widget/mount-store";
|
|
50
54
|
export * from "./settings/defaults";
|
|
55
|
+
export * from "./settings/ground-config";
|
|
51
56
|
export { CameraInertia, createCameraSystems, type CameraSystems } from "./systems/camera-sim";
|
|
52
57
|
export { restoreAutosave, startAutosave, type Autosave, type AutosaveOpts, type AutosaveState, type AutosaveStatus, type AutosaveStorageRead, type AutosaveStorageWrite, type CancelScheduled, type RestoreResult, } from "./doc/autosave";
|
|
53
58
|
export { attachBroadcastRelay, type BroadcastRelay, type BroadcastRelayOpts, type RelayChannel, } from "./doc/broadcast-relay";
|
|
54
59
|
export { createMeasureQueue, type MeasureEvent, type MeasureQueue } from "./input/measure-queue";
|
|
55
60
|
export { createMeasureIngest } from "./systems/measure-ingest";
|
|
56
61
|
export { createBreakpointSystem, createSelectionChromeSystem } from "./systems/chrome";
|
|
57
|
-
export { createNestedCanvas, currentNavEntry, currentNavFrame, type NestedCanvas, type NestedCanvasOpts, } from "./nav/nested-canvas";
|
|
62
|
+
export { createActiveMembership, createNestedCanvas, currentNavEntry, currentNavFrame, type NavOpts, type NestedCanvas, type NestedCanvasOpts, } from "./nav/nested-canvas";
|
|
63
|
+
export { abortNavFlight, createNavFlight, navFlightActive, NavTransition, startNavFlight, } from "./systems/nav-flight";
|
|
58
64
|
export { attachPresence, createPresencePublish, createRemoteCursorsSystem, DEFAULT_PRESENCE_TTL_MS, installPresence, type InstallPresenceOpts, type PresenceOpts, type PresencePublishOpts, type PresenceSession, } from "./presence";
|
|
59
|
-
export { joinDoc, type BootstrapClock, type JoinDocOpts, type JoinResult, } from "./doc/bootstrap";
|
|
65
|
+
export { joinDoc, offerBase, type BootstrapClock, type JoinDocOpts, type JoinResult, } from "./doc/bootstrap";
|
|
60
66
|
export { broadcastChannelByteChannel, webSocketByteChannel, type ByteChannel, type OwnedByteChannel, type WebSocketLike, } from "./doc/channels";
|
|
61
67
|
export { createDrawTool, defineTool, tools, type CanvasDragRoute, type Tool, type ToolDef, type ToolGates, type ToolRoute, type WidgetDragRoute, } from "./tools/define-tool";
|
|
62
|
-
export { createCanvasEngine, type CanvasDocs, type CanvasEngine, type CanvasEngineOpts, type CanvasOps, } from "./facade/create-canvas-engine";
|
|
68
|
+
export { createCanvasEngine, type CanvasDocs, type CanvasEngine, type CanvasEngineOpts, type StageControl, type CanvasOps, } from "./facade/create-canvas-engine";
|
|
63
69
|
export { createDrawBehavior } from "./systems/l3-draw";
|
|
@@ -40,6 +40,17 @@ export interface InputEvent {
|
|
|
40
40
|
};
|
|
41
41
|
/** GL-island opt-out (pinned widget-event contract, design-002 §8): fact still lands, recognizers skip. */
|
|
42
42
|
readonly surfaceHandled?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Hover-time widget-boundary fact (design-002 §8 amendment, 2026-07-18): the
|
|
45
|
+
* pointer is currently over content that would opt a down out — a native
|
|
46
|
+
* interactive / `[data-canvas-interactive]` (DOM) or claim-capable island
|
|
47
|
+
* content (GL). Stamped on down/move by adapters that track it; ABSENT means
|
|
48
|
+
* "no hover information this event" (wheel, key, blur-cancel) — ingest keeps
|
|
49
|
+
* the pointer's `OverInteractive` tag unchanged, it never treats absent as
|
|
50
|
+
* false. Presentation-grade truth (cursor affordances), never a gesture gate:
|
|
51
|
+
* recognizers keep reading `surfaceHandled`/`HandledByWidget` only.
|
|
52
|
+
*/
|
|
53
|
+
readonly overInteractive?: boolean;
|
|
43
54
|
/**
|
|
44
55
|
* Event timestamp (DOMHighResTimeStamp — the same clock rAF hands
|
|
45
56
|
* `engine.step`). Gesture timing anchors on THIS, not the processing frame:
|
|
@@ -7,10 +7,25 @@
|
|
|
7
7
|
* (at root: no container ancestor at all). Children of a deeper container
|
|
8
8
|
* are that container's content — not Active until you enter it.
|
|
9
9
|
* - `activeMembership` (derive tick, before cull): recomputes membership and
|
|
10
|
-
* flips Active/Visible/Culled CHANGE-ONLY
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* flips Active/Visible/Culled CHANGE-ONLY, gated per design-004 §7
|
|
11
|
+
* (`runIf: nav-change ∨ ChildOf churn`) as a drain-and-early-return tick
|
|
12
|
+
* body — the spatialSync shape (petition 7 ChangeCollector; a `runIf`
|
|
13
|
+
* cannot drain, and this system declares no writes so an early-out
|
|
14
|
+
* blanket-stamps nothing). Idle frames cost one drain + one nav compare.
|
|
15
|
+
* Full resweeps run on seed / reset / coarse / NAV CHANGE / container
|
|
16
|
+
* despawn; everything else is delta-driven: a journaled entity whose
|
|
17
|
+
* membership INPUTS (ChildOf edge, Container-ness) are unchanged is
|
|
18
|
+
* skipped (drag frames journal Position every tick — O(1) each), a real
|
|
19
|
+
* reparent/container-flip reclassifies its re-anchored subtree.
|
|
20
|
+
* MEMBERSHIP-CHURN SIGNAL (why Position is the reparent proxy): relations
|
|
21
|
+
* are not collector-journalable (strata CollectOptions = components +
|
|
22
|
+
* tags), but coordinates are FRAME-LOCAL (design-001 §5.1) — a reparent
|
|
23
|
+
* that does not rewrite Position into the new frame's space is already a
|
|
24
|
+
* bug, and every real path co-writes: the doc sink commits reparent +
|
|
25
|
+
* Position in ONE tx (doc-commit-sink.ts), and undo/redo/remote imports
|
|
26
|
+
* project both rows of that tx. A bare runtime `world.setRelation(e,
|
|
27
|
+
* ChildOf, …)` with no Position write on a pre-existing entity is OUTSIDE
|
|
28
|
+
* the contract and goes stale until the next resweep.
|
|
14
29
|
* - Nav stack = SESSION entities: `NavEntry` rows carry NavDepth + NavCamera
|
|
15
30
|
* (the camera to restore on exit) + NavFrame(entry→container). Root =
|
|
16
31
|
* empty stack. Current frame = deepest live entry's NavFrame target.
|
|
@@ -23,11 +38,20 @@
|
|
|
23
38
|
* container died (undo/remote — NavFrame edge auto-cleared), pop to the
|
|
24
39
|
* nearest LIVE ancestor entry (skipping dead ones) or root, restore that
|
|
25
40
|
* camera, rebuild. The recognizer-integrity pattern applied to nav.
|
|
26
|
-
* - Camera
|
|
27
|
-
*
|
|
28
|
-
* zoom-to-fit over the frame's content, else
|
|
29
|
-
*
|
|
30
|
-
*
|
|
41
|
+
* - Camera policy (design-006, amending design-004 §7; James 2026-07-15):
|
|
42
|
+
* enter saves the live camera on the pushed entry (NavCamera) and ALWAYS
|
|
43
|
+
* lands on the DEFAULT framing — zoom-to-fit over the frame's content, else
|
|
44
|
+
* identity. The `ContainerCamera` rider is neither read nor written by the
|
|
45
|
+
* engine anymore (per-canvas view memory made re-entry unpredictable); the
|
|
46
|
+
* component stays in the catalog for userland. Exit restores the entry's
|
|
47
|
+
* NavCamera — "back where you came from" is the nav stack, not view memory.
|
|
48
|
+
* - Transitions (design-006 T1): with a live Viewport and `transition` not
|
|
49
|
+
* "none", enter/exit CUT exactly as before and then FLY — the camera snaps
|
|
50
|
+
* to the continuity-solved start and `navFlight` springs it to the arrival
|
|
51
|
+
* (systems/nav-flight.ts). `exitTo(depth)` pops multi-level in ONE flight
|
|
52
|
+
* through the composed portals (rebuilt from CURRENT container rects — a
|
|
53
|
+
* collaborator may have moved a card). Headless (no Viewport) or opted-out
|
|
54
|
+
* calls snap, preserving the pre-T1 behavior exactly.
|
|
31
55
|
*/
|
|
32
56
|
import type { Entity, TickSystem, World } from "@vibecook/strata-ecs";
|
|
33
57
|
import type { SpatialIndex } from "@ice/kernel";
|
|
@@ -35,11 +59,17 @@ import type { SpatialIndex } from "@ice/kernel";
|
|
|
35
59
|
export declare function currentNavEntry(world: World): Entity | undefined;
|
|
36
60
|
/** The container the user is inside, or undefined at root. */
|
|
37
61
|
export declare function currentNavFrame(world: World): Entity | undefined;
|
|
62
|
+
/** Per-op transition choice (design-006 §3.1); default is the portal zoom. */
|
|
63
|
+
export interface NavOpts {
|
|
64
|
+
readonly transition?: "zoom" | "none";
|
|
65
|
+
}
|
|
38
66
|
export interface NestedCanvas {
|
|
39
67
|
readonly activeMembership: TickSystem;
|
|
40
68
|
readonly navIntegrity: TickSystem;
|
|
41
|
-
enterContainer(container: Entity): void;
|
|
42
|
-
exitContainer(): void;
|
|
69
|
+
enterContainer(container: Entity, opts?: NavOpts): void;
|
|
70
|
+
exitContainer(opts?: NavOpts): void;
|
|
71
|
+
/** Pop to `targetDepth` in ONE transition (breadcrumb jumps; design-006 §5). */
|
|
72
|
+
exitTo(targetDepth: number, opts?: NavOpts): void;
|
|
43
73
|
/** Depth of the nav stack (0 = root). */
|
|
44
74
|
depth(): number;
|
|
45
75
|
}
|
|
@@ -57,6 +87,12 @@ export interface NestedCanvasOpts {
|
|
|
57
87
|
* The membership stamper alone — installed by `installWidgetRuntime` (before
|
|
58
88
|
* cull) so EVERY app partitions correctly, containers or not: a board with no
|
|
59
89
|
* containers converges to all-Active on the first pass and never writes again.
|
|
90
|
+
*
|
|
91
|
+
* Gated per design-004 §7 (see the file header): the tick body drains a
|
|
92
|
+
* petition-7 collector and early-returns on idle frames; membership work runs
|
|
93
|
+
* only on nav change or membership-input churn. Baseline before this gate
|
|
94
|
+
* (bench/membership-scale.test.ts, 2026-07-15): the full scan cost 19.0 ms
|
|
95
|
+
* PER IDLE FRAME at 100k widgets nested 8 deep.
|
|
60
96
|
*/
|
|
61
97
|
export declare function createActiveMembership(world: World): TickSystem;
|
|
62
98
|
export declare function createNestedCanvas(world: World, opts: NestedCanvasOpts): NestedCanvas;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Entity, World } from "@vibecook/strata-ecs";
|
|
2
|
+
import type { DurableStore } from "@vibecook/strata-ecs/durable";
|
|
3
|
+
import type { LiveWriter } from "../guards/live-writer";
|
|
4
|
+
export interface ArrangeOpts {
|
|
5
|
+
/** Explicit scope; wins over selection/frame resolution. */
|
|
6
|
+
readonly ids?: readonly Entity[];
|
|
7
|
+
/** Min separation between packed widgets (default 24). */
|
|
8
|
+
readonly gutter?: number;
|
|
9
|
+
/** Row wrap width; default = packLayout's near-square derivation. */
|
|
10
|
+
readonly maxWidth?: number;
|
|
11
|
+
/** Glide duration; 0 snaps instantly (tests/headless). Default 240ms. */
|
|
12
|
+
readonly durationMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Returns the entities that actually moved (empty = already tidy). */
|
|
15
|
+
export declare function arrangeWidgets(store: DurableStore, liveWriter: LiveWriter, world: World, opts?: ArrangeOpts): Entity[];
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* insertByDrag — the tray-adoption op (2026-07-19, James: the widget tray).
|
|
3
|
+
*
|
|
4
|
+
* Called from the app's tray-tile `pointerdown` (which `stopPropagation`s the
|
|
5
|
+
* native down — the sanctioned widget-content boundary, design-002 §8). Two
|
|
6
|
+
* event-time moves, then the ENGINE owns everything:
|
|
7
|
+
*
|
|
8
|
+
* 1. DRAFT-spawn the ghost (design-001 §3; instantiate `{ draft: true }`) —
|
|
9
|
+
* a runtime instance of the widget prefab centered under the pointer,
|
|
10
|
+
* equipped + Active + top-of-frame IMMEDIATELY (the equip system runs in
|
|
11
|
+
* `derive`, AFTER this tick's pick — the down below must find the ghost
|
|
12
|
+
* indexed and topmost in the SAME tick, so the op stamps what equip
|
|
13
|
+
* would). Two capability tags are deliberately withheld:
|
|
14
|
+
* - `LongPressDrag`: a tray insert is always instant-drag — nobody holds
|
|
15
|
+
* 500ms over a palette;
|
|
16
|
+
* - `SweepsContained`: a comment ghost at the tray's world point must not
|
|
17
|
+
* sweep the widgets resting under the tray into the insert.
|
|
18
|
+
* The projected twin gets the FULL tag set from the equip system.
|
|
19
|
+
*
|
|
20
|
+
* 2. Enqueue ONE synthetic un-flagged `down` (Law 2 — this op is an adapter
|
|
21
|
+
* here, it only enqueues; the GL router precedent). Next tick: spatialSync
|
|
22
|
+
* indexes the ghost, targeting resolves it topmost, the Drag recognizer
|
|
23
|
+
* captures it — and the ordinary L0–L4 stack runs the whole drag: lift,
|
|
24
|
+
* snap, drop targets, folder consume. moveBehavior's ghost terminals
|
|
25
|
+
* promote (one `create` tx) or fly the ghost home and reap it.
|
|
26
|
+
*/
|
|
27
|
+
import type { Entity, World } from "@vibecook/strata-ecs";
|
|
28
|
+
import { type InputQueue } from "../input/queue";
|
|
29
|
+
export interface InsertByDragOpts {
|
|
30
|
+
/** The press point in canvas-container CSS px (the kernel screen space). */
|
|
31
|
+
readonly screenX: number;
|
|
32
|
+
readonly screenY: number;
|
|
33
|
+
/** Prop overrides for the eventual create (validated by widgetSpawnInits). */
|
|
34
|
+
readonly props?: Readonly<Record<string, unknown>>;
|
|
35
|
+
/** Adapter pointer id of the pressing pointer (default "mouse"). */
|
|
36
|
+
readonly pointerId?: string;
|
|
37
|
+
readonly device?: "mouse" | "touch" | "pen";
|
|
38
|
+
/** PointerEvent.buttons at the press (default 1 = primary). */
|
|
39
|
+
readonly buttons?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Fly-back HOME in screen px (default: the press point). Apps that adopt
|
|
42
|
+
* the drag AWAY from the tray (the deferred-spawn proxy pattern — the ghost
|
|
43
|
+
* spawns at the tray-EXIT point) aim the cancel/reject return at the
|
|
44
|
+
* persistent chrome (the toolbar) instead of a spot in open canvas.
|
|
45
|
+
*/
|
|
46
|
+
readonly home?: {
|
|
47
|
+
readonly x: number;
|
|
48
|
+
readonly y: number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Proportional GRAB POINT (0–1 within the widget rect; default center).
|
|
52
|
+
* The grab point is the drag's INVARIANT (2026-07-19 polish): the spot
|
|
53
|
+
* under the finger in the tray tile stays the same spot of the spawned
|
|
54
|
+
* widget — the proxy→widget morph then composes as a pure scale about the
|
|
55
|
+
* cursor, with no re-anchoring snap.
|
|
56
|
+
*/
|
|
57
|
+
readonly anchor?: {
|
|
58
|
+
readonly u: number;
|
|
59
|
+
readonly v: number;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export declare function insertByDrag(world: World, queue: InputQueue, type: string, opts: InsertByDragOpts): Entity;
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
* the tick are verified-unrestricted). One implementation so the two can
|
|
7
7
|
* never disagree on ordering.
|
|
8
8
|
*
|
|
9
|
-
* Priority: HandleSpec chrome first (nearest edge), then widgets by
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Priority: HandleSpec chrome first (nearest edge), then widgets topmost by
|
|
10
|
+
* SIBLING ORDER (petition 8: the frame parent's ChildOf sequence, legacy
|
|
11
|
+
* (StackZ, entity) fallback for edge-less entities — compareStackOrder is the
|
|
12
|
+
* ONE comparator paint and pick share). Deliberately NOT plane-stratified:
|
|
13
|
+
* cross-plane z is stratified visually (P2 GL composites above P1 DOM), but
|
|
14
|
+
* pick order is sibling order across all surfaces — design-004 §1 names the
|
|
15
|
+
* mixed-overlap consequence and accepts it; do not "fix" it here.
|
|
14
16
|
*/
|
|
15
17
|
import type { Component, Entity } from "@vibecook/strata-ecs";
|
|
16
18
|
import type { Cubic, SpatialIndex } from "@ice/kernel";
|
|
@@ -33,10 +35,12 @@ export declare function distPointToBox(px: number, py: number, minX: number, min
|
|
|
33
35
|
* Top pick under a world point within `rWorld` (point-in-box when 0).
|
|
34
36
|
* Tier order (design-003 §3, amended for M8): HandleSpec chrome → PORT
|
|
35
37
|
* entities (interaction affordances sit atop their widget edge) → widgets by
|
|
36
|
-
*
|
|
38
|
+
* sibling order (topmost = compareStackOrder max; `ordinals` is the caller's
|
|
39
|
+
* frame ordinal map — SiblingOrderIndex — so paint and pick share one order)
|
|
40
|
+
* → WIRES (render in P0 under content; pick below widgets, above
|
|
37
41
|
* canvas — render order and pick order agree). The canvas fallback stays the
|
|
38
42
|
* caller's. Wires narrow-phase against their cached cubic (`wires` source;
|
|
39
43
|
* without one, wire entries are skipped — coarse AABB hits would make fat
|
|
40
44
|
* bounding boxes steal canvas taps).
|
|
41
45
|
*/
|
|
42
|
-
export declare function pickTopAt(reader: PickReader, index: SpatialIndex<Entity>, wx: number, wy: number, rWorld: number, wires?: WirePickSource): Entity | undefined;
|
|
46
|
+
export declare function pickTopAt(reader: PickReader, index: SpatialIndex<Entity>, wx: number, wy: number, rWorld: number, wires?: WirePickSource, ordinals?: ReadonlyMap<Entity, number>): Entity | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sibling order — THE per-frame stacking source (petition 8; design-004 §1
|
|
3
|
+
* amendment). Within a frame, paint/pick/drop order IS the frame parent's
|
|
4
|
+
* ordered `ChildOf` sibling sequence: index 0 paints first (bottom), the tail
|
|
5
|
+
* paints last (top). The frame parent is `currentNavFrame(world) ?? BoardRoot`
|
|
6
|
+
* — every renderer/pick consumer shares this module so the orders can never
|
|
7
|
+
* diverge (the point-pick lesson, extended to the comparator).
|
|
8
|
+
*
|
|
9
|
+
* LEGACY FALLBACK (must keep working): when the `BoardRoot` resource is absent
|
|
10
|
+
* (a pre-schema-2 doc opened read-only, or a doc-less runtime world) OR an
|
|
11
|
+
* entity carries no `ChildOf` edge (bare runtime spawns), stacking falls back
|
|
12
|
+
* to the legacy `(StackZ.z asc, entity asc)` comparator. This module is the
|
|
13
|
+
* ONLY sanctioned StackZ reader outside the schema migration.
|
|
14
|
+
*
|
|
15
|
+
* Mixed frames — some entities ordinal-mapped, some fallback — are nominally
|
|
16
|
+
* impossible (the 1→2 migration hangs an edge on every durable widget, and a
|
|
17
|
+
* pre-schema-2 read-only doc has no BoardRoot at all, so it is all-fallback).
|
|
18
|
+
* The comparator still totals them: ordinal-mapped entities sort BELOW the
|
|
19
|
+
* fallback set (ordinals first in paint order), each group internally ordered
|
|
20
|
+
* by its own rule — documented so a stray bare spawn lands somewhere stable
|
|
21
|
+
* rather than somewhere random.
|
|
22
|
+
*
|
|
23
|
+
* Invalidation is PULL-ONLY, the strata 0.8.0 contract: `world.orderStamp
|
|
24
|
+
* (parent, ChildOf)` is a monotonic per-parent version consumers poll after a
|
|
25
|
+
* reactive wake; the wake itself is an `observeQuery` whose grammar names the
|
|
26
|
+
* relation (`Related(ChildOf)` — pure reorders turn it over).
|
|
27
|
+
*/
|
|
28
|
+
import type { Component, Entity, World } from "@vibecook/strata-ecs";
|
|
29
|
+
/** Structural read both `World` and `SystemCtx` provide — all the comparator needs. */
|
|
30
|
+
export interface StackOrderReader {
|
|
31
|
+
get<S>(e: Entity, c: Component<S>): S | undefined;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The frame parent — the entity whose `ChildOf` sibling sequence is the
|
|
35
|
+
* current frame's stacking order: the nav frame when inside a container, else
|
|
36
|
+
* the document's board root. `undefined` = a legacy world (pre-schema-2
|
|
37
|
+
* read-only doc / doc-less runtime) — everything is fallback-ordered.
|
|
38
|
+
*/
|
|
39
|
+
export declare function frameParent(world: World): Entity | undefined;
|
|
40
|
+
/** `parent`'s sibling sequence as entity → position (the renderers' sort key). */
|
|
41
|
+
export declare function buildOrdinals(world: World, parent: Entity): Map<Entity, number>;
|
|
42
|
+
/**
|
|
43
|
+
* THE stacking comparator (ascending = paint order; the pick tier takes the
|
|
44
|
+
* max). Ordinal-mapped entities order by sibling position; entities missing
|
|
45
|
+
* from the map use the legacy `(StackZ asc, entity asc)` fallback among
|
|
46
|
+
* themselves and sort ABOVE the ordinal-mapped set (see the header note on
|
|
47
|
+
* nominally-impossible mixed frames).
|
|
48
|
+
*/
|
|
49
|
+
export declare function compareStackOrder(reader: StackOrderReader, ordinals: ReadonlyMap<Entity, number>, a: Entity, b: Entity): number;
|
|
50
|
+
export interface SiblingOrderIndex {
|
|
51
|
+
/** The frame parent backing the current ordinal map (undefined = legacy world). */
|
|
52
|
+
parent(): Entity | undefined;
|
|
53
|
+
/** The stamp-checked ordinal map — rebuilt only when the frame parent or its `orderStamp` moved. */
|
|
54
|
+
ordinals(): ReadonlyMap<Entity, number>;
|
|
55
|
+
/** True when the next `ordinals()` would rebuild (cheap pull; no rebuild). */
|
|
56
|
+
stale(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Arm the reorder wake: an `observeQuery` naming `ChildOf` (`Related`
|
|
59
|
+
* grammar — pure reorders and edge turnover both fire it). Callers pair it
|
|
60
|
+
* with `stale()`/`ordinals()` so other-frame churn stays a no-op.
|
|
61
|
+
*/
|
|
62
|
+
observe(cb: () => void): () => void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A pull-based ordinal cache over the CURRENT frame parent's sibling sequence.
|
|
66
|
+
* Consumers each hold their own instance (the cache is a cheap `getReverse`
|
|
67
|
+
* walk); correctness is shared through {@link compareStackOrder} and the
|
|
68
|
+
* stamp, never through instance identity.
|
|
69
|
+
*/
|
|
70
|
+
export declare function createSiblingOrderIndex(world: World): SiblingOrderIndex;
|
|
@@ -31,6 +31,7 @@ export declare function defineComponent<const Sch extends RawSchema>(name: strin
|
|
|
31
31
|
export declare function defineTag(name: string): Tag;
|
|
32
32
|
export declare function defineRelation(name: string, opts?: {
|
|
33
33
|
arity?: "one" | "many";
|
|
34
|
+
ordered?: boolean;
|
|
34
35
|
}): Relation;
|
|
35
36
|
export declare function defineResource<const Sch extends RawSchema>(name: string, schema: Sch, opts?: {
|
|
36
37
|
durable?: boolean;
|
|
@@ -15,6 +15,8 @@ export declare const GESTURE_DEFAULTS: {
|
|
|
15
15
|
readonly inertiaDecayMs: 325;
|
|
16
16
|
readonly multiTapWindowMs: 280;
|
|
17
17
|
readonly multiTapSlopPx: 20;
|
|
18
|
+
readonly wheelZoomSensitivity: 0.01;
|
|
19
|
+
readonly wheelZoomMaxStep: 10;
|
|
18
20
|
};
|
|
19
21
|
/** Pick radii + retarget dead-band (design-003 §3 L1 targeting). */
|
|
20
22
|
export declare const POINTER_DEFAULTS: {
|
|
@@ -39,12 +41,55 @@ export declare const SNAP_DEFAULTS: {
|
|
|
39
41
|
readonly enabled: true;
|
|
40
42
|
readonly thresholdPx: 5;
|
|
41
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Selection-chrome knobs (2026-07-17). `liftScale` mirrors the app's visual
|
|
46
|
+
* drag-lift scale (widgetlab CardShell: 1.05) so the union box can inflate a
|
|
47
|
+
* grabbed member's rect and keep WRAPPING the card it sees; 1 = no lift
|
|
48
|
+
* visual, no inflation.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CHROME_DEFAULTS: {
|
|
51
|
+
readonly liftScale: 1;
|
|
52
|
+
};
|
|
42
53
|
/** Zoom clamp (design-003 §5 item 9 cameraControl) and frame dt clamp (design-002 §1). */
|
|
43
54
|
export declare const CAMERA_DEFAULTS: {
|
|
44
55
|
readonly minZoom: 0.1;
|
|
45
56
|
readonly maxZoom: 5;
|
|
46
57
|
readonly dtClampMs: 64;
|
|
47
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* The natural auto-framing band (2026-07-18, James: "not super zoomed in
|
|
61
|
+
* nor super zoomed out, should feel natural"): every DEFAULT framing —
|
|
62
|
+
* `ops.frameContent` at boot, the folder arrival camera, and the folder
|
|
63
|
+
* minis' portal affine (widgetlab keeps its preview in lockstep) — fits
|
|
64
|
+
* content via kernel `fitCamera` but never lands outside
|
|
65
|
+
* [fitMinZoom, fitMaxZoom], intersected with the session's hard
|
|
66
|
+
* CameraLimits. `ops.zoomToFit` stays UNCAPPED: an explicit "show me
|
|
67
|
+
* everything" beats natural.
|
|
68
|
+
*/
|
|
69
|
+
export declare const FIT_DEFAULTS: {
|
|
70
|
+
readonly pad: 80;
|
|
71
|
+
readonly minZoom: 0.5;
|
|
72
|
+
readonly maxZoom: 1;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Nav-transition motion constants (design-006 §4/§5, tuned in the approved
|
|
76
|
+
* 2026-07-15 mock). `responseMs` is the critically-damped spring response for
|
|
77
|
+
* ENTER; exit runs at `exitResponseFactor ×` (returning is lighter). Response
|
|
78
|
+
* scales `durationPerOctave` per zoom-octave beyond `baseOctaves` (Apple's
|
|
79
|
+
* duration-with-distance). Beyond `freezeOctaves` a flight cannot be presented
|
|
80
|
+
* geometrically (Chromium raster-scale swing — design-006 §5): the start depth
|
|
81
|
+
* caps at `capFactor ×` arrival and the transition presents as a crossfade.
|
|
82
|
+
*/
|
|
83
|
+
export declare const NAV_TRANSITION_DEFAULTS: {
|
|
84
|
+
readonly responseMs: 420;
|
|
85
|
+
readonly exitResponseFactor: 0.8;
|
|
86
|
+
readonly durationPerOctave: 0.12;
|
|
87
|
+
readonly baseOctaves: 3.5;
|
|
88
|
+
readonly freezeOctaves: 4.2;
|
|
89
|
+
readonly capFactor: 10;
|
|
90
|
+
readonly settleP: 0.999;
|
|
91
|
+
readonly settleV: 0.02;
|
|
92
|
+
};
|
|
48
93
|
/** Widget/FBO/port budgets (design-004 §2 host pipeline, §3 FBO pool, §6 port materialization; ported into design-005 §4 engine facade). */
|
|
49
94
|
export declare const RUNTIME_BUDGETS: {
|
|
50
95
|
readonly keepMountedWidgets: 256;
|