@jamesyong42/infinite-canvas 1.2.0 → 1.3.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 +65 -0
- package/dist/advanced.cjs +61 -24
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +180 -64
- package/dist/advanced.d.cts.map +1 -1
- package/dist/advanced.d.mts +180 -64
- package/dist/advanced.d.mts.map +1 -1
- package/dist/advanced.mjs +29 -12
- package/dist/advanced.mjs.map +1 -1
- package/dist/devtools.cjs +22 -22
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.d.cts +2 -2
- package/dist/devtools.d.cts.map +1 -1
- package/dist/devtools.d.mts +2 -2
- package/dist/devtools.d.mts.map +1 -1
- package/dist/devtools.mjs +2 -2
- package/dist/devtools.mjs.map +1 -1
- package/dist/{hooks-BwY7rRHg.mjs → ecs-3kimUV5Z.mjs} +238 -74
- package/dist/ecs-3kimUV5Z.mjs.map +1 -0
- package/dist/{hooks-DHShH86C.cjs → ecs-B4QrqfvQ.cjs} +320 -108
- package/dist/ecs-B4QrqfvQ.cjs.map +1 -0
- package/dist/hooks-CtP02JNt.cjs +3762 -0
- package/dist/hooks-CtP02JNt.cjs.map +1 -0
- package/dist/hooks-gsQDDE56.mjs +3494 -0
- package/dist/hooks-gsQDDE56.mjs.map +1 -0
- package/dist/index-3GY7T8JM.d.mts +480 -0
- package/dist/index-3GY7T8JM.d.mts.map +1 -0
- package/dist/index-B7B1tRPl.d.cts +480 -0
- package/dist/index-B7B1tRPl.d.cts.map +1 -0
- package/dist/index-DSdbSQ_t.d.cts +1451 -0
- package/dist/index-DSdbSQ_t.d.cts.map +1 -0
- package/dist/index-Dj9odADH.d.mts +1451 -0
- package/dist/index-Dj9odADH.d.mts.map +1 -0
- package/dist/index.cjs +3865 -643
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +315 -138
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +315 -138
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3767 -571
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/SelectionRenderer-CR2PBQwx.d.cts +0 -105
- package/dist/SelectionRenderer-CR2PBQwx.d.cts.map +0 -1
- package/dist/SelectionRenderer-DlsBstAq.d.mts +0 -105
- package/dist/SelectionRenderer-DlsBstAq.d.mts.map +0 -1
- package/dist/WebGLWidgetLayer-BBMuwzHq.cjs +0 -3560
- package/dist/WebGLWidgetLayer-BBMuwzHq.cjs.map +0 -1
- package/dist/WebGLWidgetLayer-C3p1tnpm.mjs +0 -3375
- package/dist/WebGLWidgetLayer-C3p1tnpm.mjs.map +0 -1
- package/dist/engine-BfbvWXSk.d.mts +0 -982
- package/dist/engine-BfbvWXSk.d.mts.map +0 -1
- package/dist/engine-CCjuFMC-.d.cts +0 -982
- package/dist/engine-CCjuFMC-.d.cts.map +0 -1
- package/dist/hooks-BwY7rRHg.mjs.map +0 -1
- package/dist/hooks-DHShH86C.cjs.map +0 -1
|
@@ -0,0 +1,1451 @@
|
|
|
1
|
+
import * as _$_jamesyong42_reactive_ecs0 from "@jamesyong42/reactive-ecs";
|
|
2
|
+
import { ComponentInit, ComponentType, EntityId, SystemDef, TagType, Unsubscribe, World } from "@jamesyong42/reactive-ecs";
|
|
3
|
+
|
|
4
|
+
//#region src/ecs/components.d.ts
|
|
5
|
+
/** Position, size, and rotation of an entity in local coordinates (world units). */
|
|
6
|
+
declare const Transform2D: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
rotation: number;
|
|
12
|
+
}>;
|
|
13
|
+
/** Rendering and hit-test ordering. Higher values render on top. */
|
|
14
|
+
declare const ZIndex: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
15
|
+
value: number;
|
|
16
|
+
}>;
|
|
17
|
+
/** Easing curves supported by `transformTweenSystem` (RFC-004 § Phase 2). */
|
|
18
|
+
type TweenEasing = 'linear' | 'ease-out' | 'ease-in-out' | 'spring';
|
|
19
|
+
/**
|
|
20
|
+
* Generic animated transition of `Transform2D.x` / `.y` over time.
|
|
21
|
+
* Runtime-only: the tween component is auto-removed on completion,
|
|
22
|
+
* and in-flight tweens are not serialized (the destination Transform2D
|
|
23
|
+
* is what survives a save/load).
|
|
24
|
+
*
|
|
25
|
+
* Introduced for RFC-004 Phase 4 fly-back but designed to be reusable
|
|
26
|
+
* for any future position-animation need (snap, drop-in, consume-pop).
|
|
27
|
+
* Starting a new tween on an entity that already has one overwrites.
|
|
28
|
+
*/
|
|
29
|
+
declare const TransformTween: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
30
|
+
fromX: number;
|
|
31
|
+
fromY: number;
|
|
32
|
+
toX: number;
|
|
33
|
+
toY: number; /** `performance.now()`-ish timestamp captured at tween start. */
|
|
34
|
+
startMs: number; /** Total duration in ms. */
|
|
35
|
+
durationMs: number;
|
|
36
|
+
easing: TweenEasing;
|
|
37
|
+
/**
|
|
38
|
+
* Discriminator so downstream systems can react per kind
|
|
39
|
+
* (e.g. `'flyback'`, `'snap'`, `'spawn'`). The tween system
|
|
40
|
+
* itself is kind-agnostic.
|
|
41
|
+
*/
|
|
42
|
+
kind: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Container-hierarchy parenthood: the entity lives inside another
|
|
46
|
+
* entity's sub-canvas. Read by `navigationFilterSystem` to filter the
|
|
47
|
+
* active widget set by the current navigation frame.
|
|
48
|
+
*
|
|
49
|
+
* Container children have their own `Transform2D` in the container's
|
|
50
|
+
* local coord space — **no coord accumulation** through this reference.
|
|
51
|
+
* Replaces the old `Parent` component's container-hierarchy usage
|
|
52
|
+
* (RFC-005).
|
|
53
|
+
*/
|
|
54
|
+
declare const ParentFrame: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
55
|
+
id: EntityId;
|
|
56
|
+
}>;
|
|
57
|
+
/** Child entity IDs. Used for nested containers and handle sync. */
|
|
58
|
+
declare const Children: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
59
|
+
ids: EntityId[];
|
|
60
|
+
}>;
|
|
61
|
+
/**
|
|
62
|
+
* Ordered list of the entities a container owns (RFC-004 § Phase 5).
|
|
63
|
+
* Redundant with `ParentFrame` (the inverse relation) but materialised
|
|
64
|
+
* so UI / compositor paths can cheaply read "give me this container's
|
|
65
|
+
* children" without a reverse-index scan. `applyMutation` /
|
|
66
|
+
* `revertMutation` keep the two in sync. Serialized; IDs are remapped
|
|
67
|
+
* alongside `ParentFrame` on load.
|
|
68
|
+
*/
|
|
69
|
+
declare const ContainerChildren: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
70
|
+
ids: EntityId[];
|
|
71
|
+
}>;
|
|
72
|
+
/** Marks an entity as a renderable widget with a type identifier and rendering surface. */
|
|
73
|
+
declare const Widget$1: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
74
|
+
surface: "dom" | "webgl" | "webview";
|
|
75
|
+
type: string;
|
|
76
|
+
}>;
|
|
77
|
+
/** Arbitrary application data attached to a widget entity. Access via useWidgetData(). */
|
|
78
|
+
declare const WidgetData: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
79
|
+
data: Record<string, unknown>;
|
|
80
|
+
}>;
|
|
81
|
+
/** Computed responsive breakpoint based on screen-space size. Read-only. */
|
|
82
|
+
declare const WidgetBreakpoint: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
83
|
+
current: "micro" | "compact" | "normal" | "expanded" | "detailed";
|
|
84
|
+
screenWidth: number;
|
|
85
|
+
screenHeight: number;
|
|
86
|
+
}>;
|
|
87
|
+
/** iOS-style card size presets. Actual dimensions live in CardPresetsResource. */
|
|
88
|
+
type CardPreset = 'small' | 'medium' | 'large' | 'xl';
|
|
89
|
+
/**
|
|
90
|
+
* Marks an entity as an iOS-style card with a fixed preset size, AND
|
|
91
|
+
* opts the entity into the full card-shaped behavior bundle:
|
|
92
|
+
*
|
|
93
|
+
* - DOM `<CardChrome>` slot (rounded body, hairline ring, shadow,
|
|
94
|
+
* CSS lift transition on Dragging)
|
|
95
|
+
* - drag-promote to the 'overlay' layer for DOM cards (so dragged
|
|
96
|
+
* DOM cards visually pop above the R3F canvas)
|
|
97
|
+
* - composition discard rect for R3F cards (so other R3F widgets
|
|
98
|
+
* are clipped out of the dragged card's screen rect — defends
|
|
99
|
+
* the chrome from being painted over)
|
|
100
|
+
* - drop-to-consume contracts (`accepts` / `provides`) — RFC-004.
|
|
101
|
+
*
|
|
102
|
+
* Widgets *without* `Card` get none of this — they render bare (no
|
|
103
|
+
* chrome, no lift transition), and on drag they only get the
|
|
104
|
+
* compositor's renderOrder bump if they're R3F (so they stack on
|
|
105
|
+
* top of other R3F widgets they overlap).
|
|
106
|
+
*
|
|
107
|
+
* The `cardSystem` reconciles `Transform2D.width/height` from the
|
|
108
|
+
* preset each tick, so cards cannot be resized freely — change
|
|
109
|
+
* `preset` instead.
|
|
110
|
+
*/
|
|
111
|
+
declare const Card: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
112
|
+
preset: CardPreset;
|
|
113
|
+
/**
|
|
114
|
+
* CSS background for the chrome's surface (any valid background value;
|
|
115
|
+
* defaults to the dark iOS card colour). Picked up by the
|
|
116
|
+
* `<CardChrome>` component rendered for this entity.
|
|
117
|
+
*/
|
|
118
|
+
background: string;
|
|
119
|
+
/**
|
|
120
|
+
* Drop-to-consume contract — what this card accepts as a *parent*.
|
|
121
|
+
* An incoming dragged card's `provides` must intersect this list
|
|
122
|
+
* for the consume mechanic to fire (RFC-004 § Phase 3). Empty
|
|
123
|
+
* array = card never consumes anything.
|
|
124
|
+
*/
|
|
125
|
+
accepts: readonly string[];
|
|
126
|
+
/**
|
|
127
|
+
* Drop-to-consume contract — what this card offers when dropped
|
|
128
|
+
* as a *child*. Empty array = card is never consumed by anything.
|
|
129
|
+
*/
|
|
130
|
+
provides: readonly string[];
|
|
131
|
+
}>;
|
|
132
|
+
/**
|
|
133
|
+
* Transient — set by the card-overlap pass on every card whose AABB
|
|
134
|
+
* intersects the dragged card's AABB during drag. Layer 1 of the
|
|
135
|
+
* two-layer overlap visual state (RFC-004 § Phase 3). Cleared on drag
|
|
136
|
+
* end. Runtime-only — not serialized.
|
|
137
|
+
*/
|
|
138
|
+
declare const OverlapCandidate: _$_jamesyong42_reactive_ecs0.TagType;
|
|
139
|
+
/**
|
|
140
|
+
* Transient — set on the single primary candidate (closest centre
|
|
141
|
+
* distance) iff its `accepts` contract intersects the dragged card's
|
|
142
|
+
* `provides` contract AND the optional `canAccept` gate passes.
|
|
143
|
+
* Layer 2 of the two-layer overlap visual state. Cleared on drag end
|
|
144
|
+
* or when match becomes false. Runtime-only — not serialized.
|
|
145
|
+
*/
|
|
146
|
+
declare const OverlapTarget: _$_jamesyong42_reactive_ecs0.TagType;
|
|
147
|
+
/**
|
|
148
|
+
* Per-candidate "hot point" for the position-dependent radial glow
|
|
149
|
+
* (RFC-004 § Phase 3). `x` and `y` are normalized [0..1] local coords
|
|
150
|
+
* within the overlapped card, pointing at the intersection centroid.
|
|
151
|
+
* `strength` ramps between 0 and 1 for the fade-in / fade-out.
|
|
152
|
+
* Runtime-only — not serialized.
|
|
153
|
+
*/
|
|
154
|
+
declare const CardOverlapHotPoint: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
155
|
+
x: number;
|
|
156
|
+
y: number;
|
|
157
|
+
strength: number;
|
|
158
|
+
}>;
|
|
159
|
+
/** Marks an entity as an enterable container (double-click/double-tap to enter). */
|
|
160
|
+
declare const Container: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
161
|
+
enterable: boolean;
|
|
162
|
+
}>;
|
|
163
|
+
/**
|
|
164
|
+
* Per-container persistent camera state. When the user navigates out of
|
|
165
|
+
* a container, their current pan/zoom is snapshotted here; when they
|
|
166
|
+
* navigate back in, it's restored. Serialized — containers remember
|
|
167
|
+
* their view across save/load (RFC-004 § Phase 0c).
|
|
168
|
+
*/
|
|
169
|
+
declare const ContainerCamera: _$_jamesyong42_reactive_ecs0.ComponentType<{
|
|
170
|
+
x: number;
|
|
171
|
+
y: number;
|
|
172
|
+
zoom: number;
|
|
173
|
+
}>;
|
|
174
|
+
/** Resize handle positions — 4 edges + 4 corners. */
|
|
175
|
+
type ResizeHandlePos = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';
|
|
176
|
+
/** Discriminated union of interaction roles an entity can fulfil. */
|
|
177
|
+
type InteractionRoleType = {
|
|
178
|
+
type: 'drag';
|
|
179
|
+
} | {
|
|
180
|
+
type: 'select';
|
|
181
|
+
} | {
|
|
182
|
+
type: 'resize';
|
|
183
|
+
handle: ResizeHandlePos;
|
|
184
|
+
} | {
|
|
185
|
+
type: 'rotate';
|
|
186
|
+
} | {
|
|
187
|
+
type: 'connect';
|
|
188
|
+
} | {
|
|
189
|
+
type: 'canvas';
|
|
190
|
+
};
|
|
191
|
+
type InteractionRoleData = {
|
|
192
|
+
/** Hit-test priority — higher wins when multiple entities contain the point. */layer: number; /** Discriminated role + role-specific data. */
|
|
193
|
+
role: InteractionRoleType;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Declares what happens when this entity is hit, plus its hit-test priority.
|
|
197
|
+
* Canonical layers: 0=canvas, 5=widget body, 20=reserved.
|
|
198
|
+
*
|
|
199
|
+
* Resize corner/edge roles are emitted inline by `interaction.ts` from the
|
|
200
|
+
* selected `Resizable` widget's Transform2D; they are NOT stored as
|
|
201
|
+
* per-handle entities (RFC-005).
|
|
202
|
+
*/
|
|
203
|
+
declare const InteractionRole: _$_jamesyong42_reactive_ecs0.ComponentType<InteractionRoleData>;
|
|
204
|
+
/** CSS cursor values the canvas may request. */
|
|
205
|
+
type CSSCursor = 'default' | 'grab' | 'grabbing' | 'crosshair' | 'n-resize' | 's-resize' | 'e-resize' | 'w-resize' | 'ne-resize' | 'nw-resize' | 'se-resize' | 'sw-resize';
|
|
206
|
+
type CursorHintData = {
|
|
207
|
+
/** Cursor when this entity is hovered in idle state. */hover: CSSCursor; /** Cursor while this entity is being dragged/resized. */
|
|
208
|
+
active: CSSCursor;
|
|
209
|
+
};
|
|
210
|
+
/** Declares the cursor this entity requests when hovered and when active. */
|
|
211
|
+
declare const CursorHint: _$_jamesyong42_reactive_ecs0.ComponentType<CursorHintData>;
|
|
212
|
+
/** Marks an entity as selectable by click or marquee. */
|
|
213
|
+
declare const Selectable: _$_jamesyong42_reactive_ecs0.TagType;
|
|
214
|
+
/** Marks an entity as draggable via pointer interaction. */
|
|
215
|
+
declare const Draggable: _$_jamesyong42_reactive_ecs0.TagType;
|
|
216
|
+
/** Marks an entity as resizable via edge/corner handles. */
|
|
217
|
+
declare const Resizable: _$_jamesyong42_reactive_ecs0.TagType;
|
|
218
|
+
/**
|
|
219
|
+
* Marks an entity that, when dragged, runs alignment-snap math against
|
|
220
|
+
* the set of `SnapTarget` entities. Without this tag, dragging proceeds
|
|
221
|
+
* with raw pointer deltas (no snapping). Independent of `SnapTarget`:
|
|
222
|
+
* an entity can be a source without being a target (rare) or a target
|
|
223
|
+
* without being a source (e.g. cards — they participate as references
|
|
224
|
+
* for other widgets but never snap themselves).
|
|
225
|
+
*
|
|
226
|
+
* Multi-select drag: only the first selected entity's bounds drive the
|
|
227
|
+
* snap calculation; followers receive the same correction delta. Tag a
|
|
228
|
+
* follower-only entity with `SnapSource` if you also want it to lead a
|
|
229
|
+
* single-entity drag, but be aware its geometry is ignored when it is
|
|
230
|
+
* being dragged as part of a multi-select group.
|
|
231
|
+
*/
|
|
232
|
+
declare const SnapSource: _$_jamesyong42_reactive_ecs0.TagType;
|
|
233
|
+
/**
|
|
234
|
+
* Marks an entity whose bounds are usable as a snap reference when
|
|
235
|
+
* another `SnapSource` entity is being dragged. References are further
|
|
236
|
+
* filtered by `Active`, so only entities in the current navigation
|
|
237
|
+
* frame can pull a drag — a `SnapTarget` inside a closed container
|
|
238
|
+
* does not leak into a root-level drag. Visibility of the resulting
|
|
239
|
+
* guide lines is controlled separately by the engine's
|
|
240
|
+
* `snap.guidesVisible` config — this tag only governs participation
|
|
241
|
+
* in the math.
|
|
242
|
+
*/
|
|
243
|
+
declare const SnapTarget: _$_jamesyong42_reactive_ecs0.TagType;
|
|
244
|
+
/** Prevents an entity from being moved or resized. */
|
|
245
|
+
declare const Locked: _$_jamesyong42_reactive_ecs0.TagType;
|
|
246
|
+
/** Indicates the entity is currently selected. */
|
|
247
|
+
declare const Selected: _$_jamesyong42_reactive_ecs0.TagType;
|
|
248
|
+
/**
|
|
249
|
+
* Indicates the entity is currently being dragged by the user.
|
|
250
|
+
* Added after the drag dead-zone is crossed; removed on pointer up/cancel.
|
|
251
|
+
* Renderers read this to apply transient drag affordances (e.g. scale/shadow lift).
|
|
252
|
+
*/
|
|
253
|
+
declare const Dragging: _$_jamesyong42_reactive_ecs0.TagType;
|
|
254
|
+
/**
|
|
255
|
+
* Entities with this tag get the engine-drawn selection + hover outline frame.
|
|
256
|
+
* Granted automatically to Selectable entities unless explicitly disabled via
|
|
257
|
+
* `Archetype.interactive.selectionFrame: false`. Widgets that render their own
|
|
258
|
+
* selected/hover chrome (e.g. iOS-style cards) opt out.
|
|
259
|
+
*/
|
|
260
|
+
declare const SelectionFrame: _$_jamesyong42_reactive_ecs0.TagType;
|
|
261
|
+
/** Indicates the entity is currently being interacted with (drag, resize). */
|
|
262
|
+
declare const Active: _$_jamesyong42_reactive_ecs0.TagType;
|
|
263
|
+
/** Indicates the entity is within the visible viewport. Set by the cull system. */
|
|
264
|
+
declare const Visible: _$_jamesyong42_reactive_ecs0.TagType;
|
|
265
|
+
/**
|
|
266
|
+
* Indicates the entity is `Active` but **outside** the visible viewport
|
|
267
|
+
* (+overscan). The complement of `Visible` for Active entities — the cull
|
|
268
|
+
* system maintains the invariant that every Active entity carries exactly
|
|
269
|
+
* one of `Visible` or `Culled`.
|
|
270
|
+
*
|
|
271
|
+
* Render layers consume this to keep state cached without rendering: DOM
|
|
272
|
+
* widgets may stay mounted-but-hidden for fast re-reveal, and the R3F
|
|
273
|
+
* compositor (RFC-002) holds widget render targets in its Cold pool.
|
|
274
|
+
*/
|
|
275
|
+
declare const Culled: _$_jamesyong42_reactive_ecs0.TagType;
|
|
276
|
+
/**
|
|
277
|
+
* Named DOM stacking layer a widget renders into. Three layers are
|
|
278
|
+
* rendered out of the box by `<InfiniteCanvas>`:
|
|
279
|
+
*
|
|
280
|
+
* `'background'` — DOM widgets behind everything user-content.
|
|
281
|
+
* `'base'` — default; DOM widgets and R3F card chrome.
|
|
282
|
+
* `'overlay'` — DOM widgets and R3F chrome promoted above the R3F
|
|
283
|
+
* canvas (e.g. dragged widget, future tooltips).
|
|
284
|
+
*
|
|
285
|
+
* Per-widget `ZIndex` continues to control intra-layer ordering;
|
|
286
|
+
* `Layer.name` picks which layer container the widget mounts into.
|
|
287
|
+
*
|
|
288
|
+
* R3F widgets always render through the R3F canvas regardless of layer
|
|
289
|
+
* — `Layer.name` only controls where their CSS chrome / interaction
|
|
290
|
+
* surface mounts.
|
|
291
|
+
*/
|
|
292
|
+
type LayerName = 'background' | 'base' | 'overlay';
|
|
293
|
+
type LayerData = {
|
|
294
|
+
name: LayerName;
|
|
295
|
+
};
|
|
296
|
+
declare const Layer: _$_jamesyong42_reactive_ecs0.ComponentType<LayerData>;
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/ecs/math.d.ts
|
|
299
|
+
interface Vec2 {
|
|
300
|
+
x: number;
|
|
301
|
+
y: number;
|
|
302
|
+
}
|
|
303
|
+
interface Rect {
|
|
304
|
+
x: number;
|
|
305
|
+
y: number;
|
|
306
|
+
width: number;
|
|
307
|
+
height: number;
|
|
308
|
+
}
|
|
309
|
+
interface AABB {
|
|
310
|
+
minX: number;
|
|
311
|
+
minY: number;
|
|
312
|
+
maxX: number;
|
|
313
|
+
maxY: number;
|
|
314
|
+
}
|
|
315
|
+
/** Convert a Rect to AABB */
|
|
316
|
+
declare function rectToAABB(r: Rect): AABB;
|
|
317
|
+
/** Convert AABB to Rect */
|
|
318
|
+
declare function aabbToRect(a: AABB): Rect;
|
|
319
|
+
/** Test if two AABBs overlap */
|
|
320
|
+
declare function intersectsAABB(a: AABB, b: AABB): boolean;
|
|
321
|
+
/** Test if a point is inside an AABB */
|
|
322
|
+
declare function pointInAABB(px: number, py: number, a: AABB): boolean;
|
|
323
|
+
/** Convert screen coordinates to world coordinates */
|
|
324
|
+
declare function screenToWorld(screenX: number, screenY: number, camera: {
|
|
325
|
+
x: number;
|
|
326
|
+
y: number;
|
|
327
|
+
zoom: number;
|
|
328
|
+
}): Vec2;
|
|
329
|
+
/** Convert world coordinates to screen coordinates */
|
|
330
|
+
declare function worldToScreen(worldX: number, worldY: number, camera: {
|
|
331
|
+
x: number;
|
|
332
|
+
y: number;
|
|
333
|
+
zoom: number;
|
|
334
|
+
}): Vec2;
|
|
335
|
+
/** Clamp a value between min and max */
|
|
336
|
+
declare function clamp(value: number, min: number, max: number): number;
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/ecs/spatial/SpatialIndex.d.ts
|
|
339
|
+
interface SpatialEntry extends AABB {
|
|
340
|
+
entityId: EntityId;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Spatial index backed by an R-tree (rbush).
|
|
344
|
+
* Stores world-space AABBs for fast viewport culling and hit testing.
|
|
345
|
+
*/
|
|
346
|
+
declare class SpatialIndex {
|
|
347
|
+
private tree;
|
|
348
|
+
private entries;
|
|
349
|
+
upsert(entityId: EntityId, bounds: AABB): void;
|
|
350
|
+
remove(entityId: EntityId): void;
|
|
351
|
+
/** Query all entries intersecting the given AABB */
|
|
352
|
+
search(bounds: AABB): SpatialEntry[];
|
|
353
|
+
/** Find the topmost entity at a point (by z-order — caller sorts) */
|
|
354
|
+
searchPoint(x: number, y: number, tolerance?: number): SpatialEntry[];
|
|
355
|
+
clear(): void;
|
|
356
|
+
get size(): number;
|
|
357
|
+
}
|
|
358
|
+
//#endregion
|
|
359
|
+
//#region src/ecs/resources.d.ts
|
|
360
|
+
/**
|
|
361
|
+
* A single frame in the navigation stack. `containerId === null` is the
|
|
362
|
+
* root canvas; any other value is the entity id of the container whose
|
|
363
|
+
* sub-canvas the user is currently inside. Camera state lives on a
|
|
364
|
+
* `ContainerCamera` component per container (or `RootCameraResource`
|
|
365
|
+
* for the root frame), not in the stack itself — see RFC-004 § Phase 0c.
|
|
366
|
+
*/
|
|
367
|
+
interface NavigationFrame {
|
|
368
|
+
containerId: EntityId | null;
|
|
369
|
+
}
|
|
370
|
+
/** Data shape for the CursorResource. */
|
|
371
|
+
type CursorResourceData = {
|
|
372
|
+
cursor: CSSCursor;
|
|
373
|
+
};
|
|
374
|
+
/**
|
|
375
|
+
* Output sink for the cursor system. Written by cursorSystem each tick;
|
|
376
|
+
* read by the RAF loop to apply style.cursor on the root container div.
|
|
377
|
+
*/
|
|
378
|
+
declare const CursorResource: _$_jamesyong42_reactive_ecs0.ResourceType<CursorResourceData>;
|
|
379
|
+
/**
|
|
380
|
+
* Camera state: world-space position (x, y) and zoom level.
|
|
381
|
+
*
|
|
382
|
+
* `gesturing` is true while the user is actively manipulating the camera
|
|
383
|
+
* (continuous wheel zoom, pinch, two-finger pan). Set/cleared by gesture
|
|
384
|
+
* handlers via {@link LayoutEngine.setGesturing}; render layers can use it
|
|
385
|
+
* to defer expensive work (e.g. the R3F compositor skips zoom-band
|
|
386
|
+
* repaints while gesturing so a continuous pinch doesn't trigger a
|
|
387
|
+
* repaint storm across every visible widget).
|
|
388
|
+
*/
|
|
389
|
+
declare const CameraResource: _$_jamesyong42_reactive_ecs0.ResourceType<{
|
|
390
|
+
x: number;
|
|
391
|
+
y: number;
|
|
392
|
+
zoom: number;
|
|
393
|
+
gesturing: boolean;
|
|
394
|
+
}>;
|
|
395
|
+
/** Viewport dimensions in CSS pixels and device pixel ratio. Updated on resize. */
|
|
396
|
+
declare const ViewportResource: _$_jamesyong42_reactive_ecs0.ResourceType<{
|
|
397
|
+
width: number;
|
|
398
|
+
height: number;
|
|
399
|
+
dpr: number;
|
|
400
|
+
}>;
|
|
401
|
+
/** Minimum and maximum zoom levels. */
|
|
402
|
+
declare const ZoomConfigResource: _$_jamesyong42_reactive_ecs0.ResourceType<{
|
|
403
|
+
min: number;
|
|
404
|
+
max: number;
|
|
405
|
+
}>;
|
|
406
|
+
/** Screen-space pixel thresholds for responsive breakpoints (micro/compact/normal/expanded/detailed). */
|
|
407
|
+
declare const BreakpointConfigResource: _$_jamesyong42_reactive_ecs0.ResourceType<{
|
|
408
|
+
micro: number;
|
|
409
|
+
compact: number;
|
|
410
|
+
normal: number;
|
|
411
|
+
expanded: number;
|
|
412
|
+
}>;
|
|
413
|
+
/**
|
|
414
|
+
* Navigation stack for hierarchical container traversal. Always has at
|
|
415
|
+
* least one frame (the root). The last element is the current frame;
|
|
416
|
+
* `containerId === null` means the user is at the root canvas.
|
|
417
|
+
*
|
|
418
|
+
* Runtime-only view state — deliberately not serialized, so reloading a
|
|
419
|
+
* saved canvas always drops the user at the root frame (RFC-004 § Phase 0c).
|
|
420
|
+
*/
|
|
421
|
+
declare const NavigationStackResource: _$_jamesyong42_reactive_ecs0.ResourceType<{
|
|
422
|
+
frames: NavigationFrame[];
|
|
423
|
+
changed: boolean;
|
|
424
|
+
}>;
|
|
425
|
+
/** Shape shared by `ContainerCamera` components and `RootCameraResource`. */
|
|
426
|
+
type FrameCameraState = {
|
|
427
|
+
x: number;
|
|
428
|
+
y: number;
|
|
429
|
+
zoom: number;
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Camera state for the root canvas. Persisted (serialized) so the root
|
|
433
|
+
* view returns to its previous pan/zoom across navigation push/pop and
|
|
434
|
+
* across save/load. Container frames use the `ContainerCamera` component
|
|
435
|
+
* on the container entity instead (RFC-004 § Phase 0c).
|
|
436
|
+
*/
|
|
437
|
+
declare const RootCameraResource: _$_jamesyong42_reactive_ecs0.ResourceType<FrameCameraState>;
|
|
438
|
+
/** Responsive breakpoint name derived from a widget's screen-space size. */
|
|
439
|
+
type Breakpoint = 'micro' | 'compact' | 'normal' | 'expanded' | 'detailed';
|
|
440
|
+
/**
|
|
441
|
+
* iOS-style card preset size map. Lookup happens by `Card.preset`; the
|
|
442
|
+
* `cardSystem` stamps `Transform2D.width/height` from the resolved size.
|
|
443
|
+
*
|
|
444
|
+
* Override at `createLayoutEngine({ cardPresets })` for tablet-scale or
|
|
445
|
+
* custom design systems.
|
|
446
|
+
*/
|
|
447
|
+
declare const CardPresetsResource: _$_jamesyong42_reactive_ecs0.ResourceType<{
|
|
448
|
+
presets: {
|
|
449
|
+
small: {
|
|
450
|
+
width: number;
|
|
451
|
+
height: number;
|
|
452
|
+
};
|
|
453
|
+
medium: {
|
|
454
|
+
width: number;
|
|
455
|
+
height: number;
|
|
456
|
+
};
|
|
457
|
+
large: {
|
|
458
|
+
width: number;
|
|
459
|
+
height: number;
|
|
460
|
+
};
|
|
461
|
+
xl: {
|
|
462
|
+
width: number;
|
|
463
|
+
height: number;
|
|
464
|
+
};
|
|
465
|
+
}; /** Gap between adjacent tiles (future tile-snap system reads this). */
|
|
466
|
+
gap: number;
|
|
467
|
+
}>;
|
|
468
|
+
/** ECS resource holding the SpatialIndex instance for viewport culling and hit testing. */
|
|
469
|
+
declare const SpatialIndexResource: _$_jamesyong42_reactive_ecs0.ResourceType<{
|
|
470
|
+
instance: SpatialIndex | null;
|
|
471
|
+
}>;
|
|
472
|
+
/**
|
|
473
|
+
* Render-layer order, low → high. `<InfiniteCanvas>` mounts a DOM
|
|
474
|
+
* container for each entry; widgets render into the container that
|
|
475
|
+
* matches their `Layer.name`. Per-widget `ZIndex` controls intra-layer
|
|
476
|
+
* ordering. RFC-003.
|
|
477
|
+
*
|
|
478
|
+
* Default: `['background', 'base', 'overlay']`. Out-of-the-box the
|
|
479
|
+
* three names map to fixed DOM positions in `<InfiniteCanvas>`'s
|
|
480
|
+
* stacking sandwich: background and base sit beneath the R3F canvas
|
|
481
|
+
* (zIndex < 1), overlay sits above it (zIndex 2). Custom layer names
|
|
482
|
+
* are not yet rendered by the default `<InfiniteCanvas>`.
|
|
483
|
+
*/
|
|
484
|
+
type LayerOrderData = {
|
|
485
|
+
layers: LayerName[];
|
|
486
|
+
};
|
|
487
|
+
declare const LayerOrderResource: _$_jamesyong42_reactive_ecs0.ResourceType<LayerOrderData>;
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region src/ecs/spatial/snap.d.ts
|
|
490
|
+
/**
|
|
491
|
+
* Snap guide computation for alignment during drag operations.
|
|
492
|
+
* Implements Figma-style snapping:
|
|
493
|
+
* 1. Edge/center alignment guides
|
|
494
|
+
* 2. Equal spacing snap + indicators
|
|
495
|
+
*/
|
|
496
|
+
interface SnapGuide {
|
|
497
|
+
/** Axis this guide aligns on */
|
|
498
|
+
axis: 'x' | 'y';
|
|
499
|
+
/** World-space coordinate of the alignment line */
|
|
500
|
+
position: number;
|
|
501
|
+
/** What kind of alignment */
|
|
502
|
+
type: 'edge' | 'center';
|
|
503
|
+
}
|
|
504
|
+
interface EqualSpacingIndicator {
|
|
505
|
+
/** Axis along which the equal gaps run */
|
|
506
|
+
axis: 'x' | 'y';
|
|
507
|
+
/** The equal gap value (world units) */
|
|
508
|
+
gap: number;
|
|
509
|
+
/** Pairs of (from, to) marking each equal gap segment */
|
|
510
|
+
segments: {
|
|
511
|
+
from: number;
|
|
512
|
+
to: number;
|
|
513
|
+
}[];
|
|
514
|
+
/** Position on the perpendicular axis (for rendering) */
|
|
515
|
+
perpPosition: number;
|
|
516
|
+
}
|
|
517
|
+
interface SnapResult {
|
|
518
|
+
/** Snap-corrected delta (world units). Apply to entity position. */
|
|
519
|
+
snapDx: number;
|
|
520
|
+
snapDy: number;
|
|
521
|
+
/** Active alignment guide lines to render */
|
|
522
|
+
guides: SnapGuide[];
|
|
523
|
+
/** Equal spacing indicators */
|
|
524
|
+
spacings: EqualSpacingIndicator[];
|
|
525
|
+
}
|
|
526
|
+
interface EntityBounds {
|
|
527
|
+
x: number;
|
|
528
|
+
y: number;
|
|
529
|
+
width: number;
|
|
530
|
+
height: number;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Compute snap guides for a dragged entity against reference entities.
|
|
534
|
+
*/
|
|
535
|
+
declare function computeSnapGuides(dragged: EntityBounds, references: EntityBounds[], threshold: number): SnapResult;
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/profiler/Profiler.d.ts
|
|
538
|
+
/**
|
|
539
|
+
* Multi-layer performance profiler.
|
|
540
|
+
*
|
|
541
|
+
* Tracks three independent rendering concerns:
|
|
542
|
+
*
|
|
543
|
+
* 1. ECS tick — systems, visibility, entity counts. Driven by `engine.tick()`.
|
|
544
|
+
* 2. WebGL engine pass — the library's grid + selection renderers. Runs
|
|
545
|
+
* inside the rAF loop immediately after each ECS tick, so samples share
|
|
546
|
+
* the ECS ring and carry matched tick ids.
|
|
547
|
+
* 3. R3F canvas — the user's 3D widgets inside the shared `<Canvas>`. Runs
|
|
548
|
+
* continuously at rAF cadence regardless of engine ticks, so it has its
|
|
549
|
+
* own ring.
|
|
550
|
+
*
|
|
551
|
+
* All methods are no-ops when disabled — zero cost for production builds.
|
|
552
|
+
* User Timing API marks are emitted when enabled so traces line up with
|
|
553
|
+
* Chrome DevTools' Performance panel.
|
|
554
|
+
*/
|
|
555
|
+
interface TickSample {
|
|
556
|
+
tick: number;
|
|
557
|
+
timestamp: number;
|
|
558
|
+
/** Total tick duration (ms) = ECS tick + WebGL engine pass. */
|
|
559
|
+
totalMs: number;
|
|
560
|
+
ecs: {
|
|
561
|
+
/** Per-system durations (ms). */systems: Record<string, number>; /** Visible entity computation (ms). */
|
|
562
|
+
visibilityMs: number; /** Entity counts at this frame. */
|
|
563
|
+
entityCount: number;
|
|
564
|
+
visibleCount: number;
|
|
565
|
+
};
|
|
566
|
+
webgl: {
|
|
567
|
+
/** Grid renderer pass duration (ms). */gridMs: number; /** Selection + hover + snap-guide render pass duration (ms). */
|
|
568
|
+
selectionMs: number; /** Total `renderer.info.render.calls` across all passes this tick. */
|
|
569
|
+
drawCalls: number; /** Total `renderer.info.render.triangles` across all passes this tick. */
|
|
570
|
+
triangles: number; /** Selection frames drawn this tick. */
|
|
571
|
+
selectionFrames: number; /** Snap guides drawn this tick. */
|
|
572
|
+
snapGuides: number; /** Equal-spacing indicators drawn this tick. */
|
|
573
|
+
spacingIndicators: number; /** DOM slot.transform writes this tick (from changes.positionsChanged). */
|
|
574
|
+
domPositionsUpdated: number;
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
/** Per-phase widget count snapshot for the compositor state machine (RFC-002). */
|
|
578
|
+
interface R3FPhaseHistogram {
|
|
579
|
+
hot: number;
|
|
580
|
+
warm: number;
|
|
581
|
+
cold: number;
|
|
582
|
+
waking: number;
|
|
583
|
+
dormant: number;
|
|
584
|
+
}
|
|
585
|
+
interface R3FSample {
|
|
586
|
+
timestamp: number;
|
|
587
|
+
/** Delta since the previous R3F frame (ms). */
|
|
588
|
+
dtMs: number;
|
|
589
|
+
/** three.js renderer.info snapshot. */
|
|
590
|
+
drawCalls: number;
|
|
591
|
+
triangles: number;
|
|
592
|
+
points: number;
|
|
593
|
+
lines: number;
|
|
594
|
+
programs: number;
|
|
595
|
+
geometries: number;
|
|
596
|
+
textures: number;
|
|
597
|
+
activeWidgets: number;
|
|
598
|
+
/**
|
|
599
|
+
* Number of per-widget FBO repaints this frame. Zero before the compositor
|
|
600
|
+
* lands (RFC-002 Phase 4); zero on idle frames once it has.
|
|
601
|
+
*/
|
|
602
|
+
widgetsRepainted: number;
|
|
603
|
+
/** Total bytes consumed by the widget render-target pool. */
|
|
604
|
+
fboBytes: number;
|
|
605
|
+
/** Compositor state-machine phase histogram across all R3F widgets. */
|
|
606
|
+
phases: R3FPhaseHistogram;
|
|
607
|
+
/** GPU time spent on per-widget paints this frame (ms). Optional — populated only when timestamp queries are available (WebGL `WEBGL_timer_query` or WebGPU). */
|
|
608
|
+
gpuPaintMs?: number;
|
|
609
|
+
/** GPU time spent on the composition pass this frame (ms). Optional — same caveat as `gpuPaintMs`. */
|
|
610
|
+
gpuCompositeMs?: number;
|
|
611
|
+
}
|
|
612
|
+
interface FrameTimeStats {
|
|
613
|
+
avg: number;
|
|
614
|
+
p50: number;
|
|
615
|
+
p95: number;
|
|
616
|
+
p99: number;
|
|
617
|
+
max: number;
|
|
618
|
+
}
|
|
619
|
+
interface EcsStats {
|
|
620
|
+
fps: number;
|
|
621
|
+
frameTime: FrameTimeStats;
|
|
622
|
+
systemAvg: Record<string, number>;
|
|
623
|
+
systemP95: Record<string, number>;
|
|
624
|
+
/** Avg frame time as % of 16.67ms (60 fps). */
|
|
625
|
+
budgetUsed: number;
|
|
626
|
+
sampleCount: number;
|
|
627
|
+
}
|
|
628
|
+
interface WebGLStats {
|
|
629
|
+
/** Tick cadence — matches ECS since both fire together. */
|
|
630
|
+
fps: number;
|
|
631
|
+
/** Avg combined pass time (ms) and percentile breakdown. */
|
|
632
|
+
frameTime: FrameTimeStats;
|
|
633
|
+
/** Avg combined pass time as % of 16.67ms budget. */
|
|
634
|
+
budgetUsed: number;
|
|
635
|
+
gridAvg: number;
|
|
636
|
+
gridP95: number;
|
|
637
|
+
selectionAvg: number;
|
|
638
|
+
selectionP95: number;
|
|
639
|
+
avgDrawCalls: number;
|
|
640
|
+
avgTriangles: number;
|
|
641
|
+
avgSelectionFrames: number;
|
|
642
|
+
avgSnapGuides: number;
|
|
643
|
+
avgDomUpdates: number;
|
|
644
|
+
sampleCount: number;
|
|
645
|
+
}
|
|
646
|
+
interface R3FStats {
|
|
647
|
+
fps: number;
|
|
648
|
+
frameTime: FrameTimeStats;
|
|
649
|
+
avgDrawCalls: number;
|
|
650
|
+
avgTriangles: number;
|
|
651
|
+
/** Latest snapshots — these don't average meaningfully. */
|
|
652
|
+
programs: number;
|
|
653
|
+
geometries: number;
|
|
654
|
+
textures: number;
|
|
655
|
+
activeWidgets: number;
|
|
656
|
+
/** Avg per-widget repaints per frame across the sample window. */
|
|
657
|
+
avgWidgetsRepainted: number;
|
|
658
|
+
/** Latest FBO pool memory usage (bytes). */
|
|
659
|
+
fboBytes: number;
|
|
660
|
+
/** Latest phase histogram across the R3F widget set. */
|
|
661
|
+
phases: R3FPhaseHistogram;
|
|
662
|
+
/** Avg GPU paint time per frame (ms). Undefined if no samples carried timestamps. */
|
|
663
|
+
avgGpuPaintMs?: number;
|
|
664
|
+
/** Avg GPU composition time per frame (ms). */
|
|
665
|
+
avgGpuCompositeMs?: number;
|
|
666
|
+
sampleCount: number;
|
|
667
|
+
}
|
|
668
|
+
interface ProfilerStats {
|
|
669
|
+
ecs: EcsStats;
|
|
670
|
+
webgl: WebGLStats;
|
|
671
|
+
r3f: R3FStats;
|
|
672
|
+
}
|
|
673
|
+
/** What's being timed inside the library's WebGL engine pass. */
|
|
674
|
+
type WebGLPass = 'grid' | 'selection' | 'snap-guides';
|
|
675
|
+
declare class Profiler {
|
|
676
|
+
private enabled;
|
|
677
|
+
private tickRing;
|
|
678
|
+
private tickWrite;
|
|
679
|
+
private tickFilled;
|
|
680
|
+
private r3fRing;
|
|
681
|
+
private r3fWrite;
|
|
682
|
+
private r3fFilled;
|
|
683
|
+
private frameStart;
|
|
684
|
+
private currentSystems;
|
|
685
|
+
private visibilityMs;
|
|
686
|
+
private currentTick;
|
|
687
|
+
/** Enable/disable profiling. When disabled, all methods are no-ops. */
|
|
688
|
+
setEnabled(on: boolean): void;
|
|
689
|
+
isEnabled(): boolean;
|
|
690
|
+
/** Call at the start of engine.tick(). */
|
|
691
|
+
beginFrame(tick: number): void;
|
|
692
|
+
/** Call around each ECS system execution. */
|
|
693
|
+
beginSystem(name: string): void;
|
|
694
|
+
endSystem(name: string): void;
|
|
695
|
+
beginVisibility(): void;
|
|
696
|
+
endVisibility(): void;
|
|
697
|
+
/** Call right before the named engine WebGL pass renders. */
|
|
698
|
+
beginWebGL(pass: WebGLPass): void;
|
|
699
|
+
/**
|
|
700
|
+
* Call right after the named engine WebGL pass renders.
|
|
701
|
+
*
|
|
702
|
+
* The WebGL pass runs AFTER endFrame has flushed the tick sample to the
|
|
703
|
+
* ring, so we mutate the most-recent ring entry in place instead of
|
|
704
|
+
* writing to scratch state (which would be wiped by the next beginFrame
|
|
705
|
+
* before ever being read).
|
|
706
|
+
*/
|
|
707
|
+
endWebGL(pass: WebGLPass): void;
|
|
708
|
+
/**
|
|
709
|
+
* Record WebGL engine pass counters for the current tick. `drawCalls` and
|
|
710
|
+
* `triangles` should be the totals from `renderer.info.render` accumulated
|
|
711
|
+
* across all engine passes in this tick (grid + selection). Callers must
|
|
712
|
+
* reset `renderer.info` at the start of the tick (with `autoReset=false`)
|
|
713
|
+
* so these values cover both passes.
|
|
714
|
+
*
|
|
715
|
+
* Called from the rAF loop AFTER engine.tick() / endFrame, so it targets
|
|
716
|
+
* the most-recent ring sample directly (see {@link endWebGL}).
|
|
717
|
+
*/
|
|
718
|
+
recordWebGLStats(stats: {
|
|
719
|
+
drawCalls: number;
|
|
720
|
+
triangles: number;
|
|
721
|
+
selectionFrames: number;
|
|
722
|
+
snapGuides: number;
|
|
723
|
+
spacingIndicators: number;
|
|
724
|
+
domPositionsUpdated: number;
|
|
725
|
+
}): void;
|
|
726
|
+
/** Returns the most recently pushed tick sample, or null if the ring is empty. */
|
|
727
|
+
private getMostRecentTickSample;
|
|
728
|
+
/** Call at the end of engine.tick() — flushes a TickSample to the ring. */
|
|
729
|
+
endFrame(entityCount: number, visibleCount: number): void;
|
|
730
|
+
/**
|
|
731
|
+
* Push one R3F frame sample. Called from the R3F canvas via a probe
|
|
732
|
+
* component that has access to `useThree`.
|
|
733
|
+
*/
|
|
734
|
+
recordR3FFrame(sample: Omit<R3FSample, 'timestamp'>): void;
|
|
735
|
+
/** Get the last N tick samples (newest first). */
|
|
736
|
+
getSamples(count?: number): TickSample[];
|
|
737
|
+
/** Get the last N R3F samples (newest first). */
|
|
738
|
+
getR3FSamples(count?: number): R3FSample[];
|
|
739
|
+
/** Compute rolling statistics across all three layers. */
|
|
740
|
+
getStats(): ProfilerStats;
|
|
741
|
+
private getEcsStats;
|
|
742
|
+
private getWebGLStats;
|
|
743
|
+
private getR3FStats;
|
|
744
|
+
/** Clear all collected data. */
|
|
745
|
+
clear(): void;
|
|
746
|
+
}
|
|
747
|
+
//#endregion
|
|
748
|
+
//#region src/ecs/schema.d.ts
|
|
749
|
+
/**
|
|
750
|
+
* Minimal Standard Schema v1 type definition.
|
|
751
|
+
*
|
|
752
|
+
* Compatible with Zod 3.24+, Valibot, ArkType, and any other library that
|
|
753
|
+
* implements the spec at https://standardschema.dev. We do not depend on any
|
|
754
|
+
* validator — users bring their own.
|
|
755
|
+
*
|
|
756
|
+
* Today we only carry the type parameter for `T`. Runtime validation via
|
|
757
|
+
* `validate()` is wired in by features that need it (inspector, serialization).
|
|
758
|
+
*/
|
|
759
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
760
|
+
readonly '~standard': StandardSchemaV1.Props<Input, Output>;
|
|
761
|
+
}
|
|
762
|
+
declare namespace StandardSchemaV1 {
|
|
763
|
+
interface Props<Input = unknown, Output = Input> {
|
|
764
|
+
readonly version: 1;
|
|
765
|
+
readonly vendor: string;
|
|
766
|
+
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
|
|
767
|
+
readonly types?: Types<Input, Output>;
|
|
768
|
+
}
|
|
769
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
770
|
+
interface SuccessResult<Output> {
|
|
771
|
+
readonly value: Output;
|
|
772
|
+
readonly issues?: undefined;
|
|
773
|
+
}
|
|
774
|
+
interface FailureResult {
|
|
775
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
776
|
+
}
|
|
777
|
+
interface Issue {
|
|
778
|
+
readonly message: string;
|
|
779
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
780
|
+
}
|
|
781
|
+
interface PathSegment {
|
|
782
|
+
readonly key: PropertyKey;
|
|
783
|
+
}
|
|
784
|
+
interface Types<Input = unknown, Output = Input> {
|
|
785
|
+
readonly input: Input;
|
|
786
|
+
readonly output: Output;
|
|
787
|
+
}
|
|
788
|
+
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
|
|
789
|
+
}
|
|
790
|
+
//#endregion
|
|
791
|
+
//#region src/ecs/engine/widget-binding.d.ts
|
|
792
|
+
/** Rendering surface for a widget. */
|
|
793
|
+
type WidgetSurface = 'dom' | 'webgl';
|
|
794
|
+
/**
|
|
795
|
+
* Drop-to-consume interaction handlers for a widget type (RFC-004 § Phase 1).
|
|
796
|
+
*
|
|
797
|
+
* All fields are optional. A widget that sets `accepts` on its `Card`
|
|
798
|
+
* component should typically also supply `onReceiveChild` so it can
|
|
799
|
+
* actually do something on consume; a widget that sets `provides`
|
|
800
|
+
* rarely needs `onDroppedOnParent` unless it wants to veto or run
|
|
801
|
+
* side effects.
|
|
802
|
+
*
|
|
803
|
+
* Handlers live on the widget *type* (not the entity) so `Card` data
|
|
804
|
+
* stays serializable and one handler triple drives every instance of
|
|
805
|
+
* the widget type.
|
|
806
|
+
*/
|
|
807
|
+
interface WidgetInteractionHandlers {
|
|
808
|
+
/**
|
|
809
|
+
* Parent-side: called on drop when a child lands on this widget.
|
|
810
|
+
* Return `{ consume: true }` to accept the drop; the optional
|
|
811
|
+
* `mutation` payload is passed to `applyMutation` / `revertMutation`
|
|
812
|
+
* so undo can be symmetric.
|
|
813
|
+
*/
|
|
814
|
+
onReceiveChild?(ctx: {
|
|
815
|
+
parent: EntityId;
|
|
816
|
+
child: EntityId;
|
|
817
|
+
world: World;
|
|
818
|
+
}): {
|
|
819
|
+
consume: boolean;
|
|
820
|
+
mutation?: unknown;
|
|
821
|
+
};
|
|
822
|
+
/**
|
|
823
|
+
* Child-side: called on drop when this widget is being dropped
|
|
824
|
+
* onto a parent. Optional side effects and optional veto.
|
|
825
|
+
*/
|
|
826
|
+
onDroppedOnParent?(ctx: {
|
|
827
|
+
child: EntityId;
|
|
828
|
+
parent: EntityId;
|
|
829
|
+
world: World;
|
|
830
|
+
}): {
|
|
831
|
+
veto: boolean;
|
|
832
|
+
} | undefined;
|
|
833
|
+
/**
|
|
834
|
+
* Parent-side runtime gate on top of the static `accepts` /
|
|
835
|
+
* `provides` intersection — e.g. "container is full, refuse further
|
|
836
|
+
* children." Returning `false` blocks the consume before
|
|
837
|
+
* `onReceiveChild` is called.
|
|
838
|
+
*/
|
|
839
|
+
canAccept?(ctx: {
|
|
840
|
+
parent: EntityId;
|
|
841
|
+
child: EntityId;
|
|
842
|
+
world: World;
|
|
843
|
+
}): boolean;
|
|
844
|
+
/**
|
|
845
|
+
* Apply the mutation returned by `onReceiveChild` (forward path).
|
|
846
|
+
* Called by `ConsumeCommand.execute`.
|
|
847
|
+
*/
|
|
848
|
+
applyMutation?(world: World, mutation: unknown): void;
|
|
849
|
+
/**
|
|
850
|
+
* Reverse the mutation (undo path). Called by `ConsumeCommand.undo`.
|
|
851
|
+
*/
|
|
852
|
+
revertMutation?(world: World, mutation: unknown): void;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Framework-free widget contract used by the engine.
|
|
856
|
+
*
|
|
857
|
+
* The React layer (see `react/widgets/registry.ts`) extends this with a
|
|
858
|
+
* `component` field to carry the rendered React component. The engine never
|
|
859
|
+
* reads that field — it only needs the metadata here — which keeps the
|
|
860
|
+
* `ecs/` layer free of React imports.
|
|
861
|
+
*/
|
|
862
|
+
interface WidgetBinding<T = Record<string, unknown>> {
|
|
863
|
+
/** Unique widget type id. Matches `Widget { type }` on spawned entities. */
|
|
864
|
+
type: string;
|
|
865
|
+
/** Rendering surface; defaults to `'dom'`. */
|
|
866
|
+
surface?: WidgetSurface;
|
|
867
|
+
/**
|
|
868
|
+
* Standard Schema v1-compatible schema for the widget's data.
|
|
869
|
+
* Use Zod 3.24+, Valibot, ArkType, or any other conforming validator.
|
|
870
|
+
* The schema's output type drives the widget's data type.
|
|
871
|
+
*/
|
|
872
|
+
schema: StandardSchemaV1<any, T>;
|
|
873
|
+
/** Default data shape for new instances. Merged with user-supplied data at spawn. */
|
|
874
|
+
defaultData: T;
|
|
875
|
+
/** Default world-space size at spawn. */
|
|
876
|
+
defaultSize: {
|
|
877
|
+
width: number;
|
|
878
|
+
height: number;
|
|
879
|
+
};
|
|
880
|
+
/** Minimum world-space size when resizing. */
|
|
881
|
+
minSize?: {
|
|
882
|
+
width: number;
|
|
883
|
+
height: number;
|
|
884
|
+
};
|
|
885
|
+
/**
|
|
886
|
+
* Optional drop-to-consume handlers. Only meaningful for `Card`-tagged
|
|
887
|
+
* widgets; non-card widgets don't participate in the consume mechanic
|
|
888
|
+
* so any handlers here are dead code for them.
|
|
889
|
+
*/
|
|
890
|
+
interaction?: WidgetInteractionHandlers;
|
|
891
|
+
}
|
|
892
|
+
/** Simple in-memory registry of widget bindings. */
|
|
893
|
+
interface WidgetRegistry<W extends WidgetBinding = WidgetBinding> {
|
|
894
|
+
register(def: W): void;
|
|
895
|
+
get(type: string): W | null;
|
|
896
|
+
getAll(): W[];
|
|
897
|
+
}
|
|
898
|
+
declare function createWidgetRegistry<W extends WidgetBinding = WidgetBinding>(defs?: W[]): WidgetRegistry<W>;
|
|
899
|
+
//#endregion
|
|
900
|
+
//#region src/react/widgets/registry.d.ts
|
|
901
|
+
/**
|
|
902
|
+
* Props passed to every DOM widget component. The component is mounted
|
|
903
|
+
* inside a sized `WidgetSlot` div — size via CSS or layout — and renders
|
|
904
|
+
* normal React/HTML.
|
|
905
|
+
*
|
|
906
|
+
* Pointer events work natively (RFC-006): `onClick`, `onPointerOver`,
|
|
907
|
+
* focus, native form inputs, contenteditable, drag-and-drop API — all
|
|
908
|
+
* dispatched by the browser's event path and reach widget children
|
|
909
|
+
* before they bubble to the canvas-level `PointerEventBus`.
|
|
910
|
+
*
|
|
911
|
+
* To opt out of engine routing (drag / select / resize) for an event,
|
|
912
|
+
* call `e.stopPropagation()` from inside the widget. `<button>`,
|
|
913
|
+
* `<input>`, `<textarea>`, `<select>`, and `[contenteditable]` opt out
|
|
914
|
+
* automatically — the bus skips engine routing when the event target
|
|
915
|
+
* matches one of those native interactive selectors.
|
|
916
|
+
*/
|
|
917
|
+
interface DomWidgetProps {
|
|
918
|
+
entityId: EntityId;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Props passed to every R3F widget component. Rendered in widget-local
|
|
922
|
+
* coords — the origin is the widget centre, X right, Y up — and the
|
|
923
|
+
* component declares its own Three.js scene through R3F primitives.
|
|
924
|
+
*
|
|
925
|
+
* Pointer events on `<mesh>` / `<group>` work natively (RFC-006):
|
|
926
|
+
* `onClick`, `onPointerOver`, `onPointerOut`, `onPointerEnter`,
|
|
927
|
+
* `onPointerLeave`, `onPointerMove`, `onPointerDown`, `onPointerUp`,
|
|
928
|
+
* `onPointerMissed`, `event.point` / `event.uv` / `event.intersections`,
|
|
929
|
+
* R3F's `event.stopPropagation()` and `setPointerCapture` — all
|
|
930
|
+
* dispatched by a custom EventManager that raycasts the widget's
|
|
931
|
+
* own scene with widget-local coords.
|
|
932
|
+
*
|
|
933
|
+
* To opt out of engine routing (drag / select / resize) for an event,
|
|
934
|
+
* additionally call `event.nativeEvent.stopPropagation()` to prevent
|
|
935
|
+
* the canvas-container `PointerEventBus` from receiving it.
|
|
936
|
+
*/
|
|
937
|
+
interface R3FWidgetProps {
|
|
938
|
+
entityId: EntityId;
|
|
939
|
+
/** Widget width in world units. */
|
|
940
|
+
width: number;
|
|
941
|
+
/** Widget height in world units. */
|
|
942
|
+
height: number;
|
|
943
|
+
}
|
|
944
|
+
/** A DOM-rendered widget. The component is wrapped in a sized div — size via CSS. */
|
|
945
|
+
interface DomWidget<T = Record<string, unknown>> extends WidgetBinding<T> {
|
|
946
|
+
surface?: 'dom';
|
|
947
|
+
component: React.ComponentType<DomWidgetProps>;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* An R3F (React Three Fiber) widget. The component receives local-space
|
|
951
|
+
* width/height. Card-shaped chrome / lift / drag-promote / compositor
|
|
952
|
+
* discard are all opted in via the `Card` ECS component on the spawned
|
|
953
|
+
* entity (typically declared in the widget's archetype) — see
|
|
954
|
+
* `createGeometryCardWidget` for the convenience wrapper.
|
|
955
|
+
*/
|
|
956
|
+
interface R3FWidget<T = Record<string, unknown>> extends WidgetBinding<T> {
|
|
957
|
+
surface: 'webgl';
|
|
958
|
+
component: React.ComponentType<R3FWidgetProps>;
|
|
959
|
+
}
|
|
960
|
+
/** Either kind of widget. */
|
|
961
|
+
type Widget<T = Record<string, unknown>> = DomWidget<T> | R3FWidget<T>;
|
|
962
|
+
/** Narrows to the R3F variant. */
|
|
963
|
+
declare function isR3FWidget<T>(widget: Widget<T>): widget is R3FWidget<T>;
|
|
964
|
+
//#endregion
|
|
965
|
+
//#region src/ecs/archetype.d.ts
|
|
966
|
+
/**
|
|
967
|
+
* An archetype is a recipe for creating an entity: it declares which components
|
|
968
|
+
* and tags the entity should have on spawn. Archetypes may reference a widget
|
|
969
|
+
* type (for visible entities) or stand alone (for logic-only entities).
|
|
970
|
+
*
|
|
971
|
+
* The engine auto-generates a default archetype for every registered widget
|
|
972
|
+
* that does not have an explicit one — so simple widgets don't need to ship
|
|
973
|
+
* an archetype at all. Write an archetype only when you need to bundle extra
|
|
974
|
+
* behaviour (Container, Locked, custom components) with a widget.
|
|
975
|
+
*/
|
|
976
|
+
interface Archetype {
|
|
977
|
+
/** Unique archetype id. Pass this to `engine.spawn(id, ...)`. */
|
|
978
|
+
id: string;
|
|
979
|
+
/**
|
|
980
|
+
* The widget type this archetype renders as. Required for visible entities;
|
|
981
|
+
* omit for logic-only entities that have no view.
|
|
982
|
+
*/
|
|
983
|
+
widget?: string;
|
|
984
|
+
/** Extra components added on spawn, beyond Transform2D / Widget / WidgetData / ZIndex. */
|
|
985
|
+
components?: ComponentInit[];
|
|
986
|
+
/** Extra tags added on spawn, beyond the interactive defaults. */
|
|
987
|
+
tags?: TagType[];
|
|
988
|
+
/**
|
|
989
|
+
* Which interaction capabilities to grant on spawn.
|
|
990
|
+
*
|
|
991
|
+
* - `true` (default) / `undefined`: add Selectable, Draggable, Resizable,
|
|
992
|
+
* SelectionFrame, SnapSource, and SnapTarget — the full bundle.
|
|
993
|
+
* - `false`: add none (backdrops, decorations, locked entities).
|
|
994
|
+
* - object form: pick and choose — e.g. iOS-style cards use
|
|
995
|
+
* `{ selectable: true, draggable: true, resizable: false,
|
|
996
|
+
* selectionFrame: false, snapSource: false, snapTarget: true }`
|
|
997
|
+
* so they can be moved and selected but never resized, render their
|
|
998
|
+
* own chrome instead of the engine-drawn frame, don't snap to
|
|
999
|
+
* neighbours when dragged, but do serve as references that other
|
|
1000
|
+
* widgets snap to.
|
|
1001
|
+
*
|
|
1002
|
+
* Omitted interaction keys default to `false`. `selectionFrame` is an
|
|
1003
|
+
* exception: if omitted, it follows `selectable` (an entity you can
|
|
1004
|
+
* select gets a frame unless you explicitly opt out).
|
|
1005
|
+
*
|
|
1006
|
+
* NOTE for upgraders from earlier versions: `interactive: true` now
|
|
1007
|
+
* additionally grants `SnapSource` and `SnapTarget`, so widgets using
|
|
1008
|
+
* the default bundle will start participating in alignment-snap math
|
|
1009
|
+
* during drag. If you previously relied on snap being a no-op for
|
|
1010
|
+
* these widgets, switch to the object form and set both to `false`.
|
|
1011
|
+
*/
|
|
1012
|
+
interactive?: boolean | {
|
|
1013
|
+
selectable?: boolean;
|
|
1014
|
+
draggable?: boolean;
|
|
1015
|
+
resizable?: boolean;
|
|
1016
|
+
selectionFrame?: boolean; /** Dragging this entity invokes alignment-snap math. Default false in the object form. */
|
|
1017
|
+
snapSource?: boolean; /** Bounds participate as references for other entities' snap math. Default false in the object form. */
|
|
1018
|
+
snapTarget?: boolean;
|
|
1019
|
+
};
|
|
1020
|
+
/** Overrides the widget's defaultSize. */
|
|
1021
|
+
defaultSize?: {
|
|
1022
|
+
width: number;
|
|
1023
|
+
height: number;
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Options for `engine.spawn(archetypeId, opts)`.
|
|
1028
|
+
* All fields are optional — defaults come from the archetype + widget.
|
|
1029
|
+
*/
|
|
1030
|
+
interface SpawnOptions {
|
|
1031
|
+
/** World-space position. Defaults to { x: 0, y: 0 }. */
|
|
1032
|
+
at?: {
|
|
1033
|
+
x: number;
|
|
1034
|
+
y: number;
|
|
1035
|
+
};
|
|
1036
|
+
/** World-space size. Falls back to archetype.defaultSize → widget.defaultSize. */
|
|
1037
|
+
size?: {
|
|
1038
|
+
width: number;
|
|
1039
|
+
height: number;
|
|
1040
|
+
};
|
|
1041
|
+
/** Initial rotation in radians. Default 0. */
|
|
1042
|
+
rotation?: number;
|
|
1043
|
+
/** Data patch merged into the widget's defaultData. */
|
|
1044
|
+
data?: Record<string, unknown>;
|
|
1045
|
+
/** Z-order. Default 0. */
|
|
1046
|
+
zIndex?: number;
|
|
1047
|
+
/** Parent container entity — writes `ParentFrame` on spawn so the child
|
|
1048
|
+
* lives in the container's sub-canvas frame. */
|
|
1049
|
+
parent?: EntityId;
|
|
1050
|
+
}
|
|
1051
|
+
/** Simple in-memory archetype registry. */
|
|
1052
|
+
interface ArchetypeRegistry {
|
|
1053
|
+
register(archetype: Archetype): void;
|
|
1054
|
+
get(id: string): Archetype | null;
|
|
1055
|
+
getAll(): Archetype[];
|
|
1056
|
+
}
|
|
1057
|
+
declare function createArchetypeRegistry(archetypes?: Archetype[]): ArchetypeRegistry;
|
|
1058
|
+
//#endregion
|
|
1059
|
+
//#region src/ecs/commands.d.ts
|
|
1060
|
+
interface Command {
|
|
1061
|
+
execute(world: World): void;
|
|
1062
|
+
undo(world: World): void;
|
|
1063
|
+
}
|
|
1064
|
+
declare class CommandBuffer {
|
|
1065
|
+
private undoStack;
|
|
1066
|
+
private redoStack;
|
|
1067
|
+
private currentGroup;
|
|
1068
|
+
/** Start grouping commands (e.g., on pointerdown). All commands until endGroup() are one undo step. */
|
|
1069
|
+
beginGroup(): void;
|
|
1070
|
+
/** Execute a command and record it for undo. */
|
|
1071
|
+
execute(command: Command, world: World): void;
|
|
1072
|
+
/** Close the current group — all commands since beginGroup() become one undo step. */
|
|
1073
|
+
endGroup(): void;
|
|
1074
|
+
/** Undo the last command group. */
|
|
1075
|
+
undo(world: World): boolean;
|
|
1076
|
+
/** Redo the last undone command group. */
|
|
1077
|
+
redo(world: World): boolean;
|
|
1078
|
+
canUndo(): boolean;
|
|
1079
|
+
canRedo(): boolean;
|
|
1080
|
+
clear(): void;
|
|
1081
|
+
get undoSize(): number;
|
|
1082
|
+
get redoSize(): number;
|
|
1083
|
+
}
|
|
1084
|
+
declare class MoveCommand implements Command {
|
|
1085
|
+
private entityIds;
|
|
1086
|
+
private dx;
|
|
1087
|
+
private dy;
|
|
1088
|
+
private transformType;
|
|
1089
|
+
private beforePositions;
|
|
1090
|
+
private afterPositions;
|
|
1091
|
+
private captured;
|
|
1092
|
+
constructor(entityIds: EntityId[], dx: number, dy: number, transformType: ComponentType<{
|
|
1093
|
+
x: number;
|
|
1094
|
+
y: number;
|
|
1095
|
+
}>);
|
|
1096
|
+
execute(world: World): void;
|
|
1097
|
+
undo(world: World): void;
|
|
1098
|
+
}
|
|
1099
|
+
declare class ResizeCommand implements Command {
|
|
1100
|
+
private entityId;
|
|
1101
|
+
private before;
|
|
1102
|
+
private after;
|
|
1103
|
+
private transformType;
|
|
1104
|
+
constructor(entityId: EntityId, before: {
|
|
1105
|
+
x: number;
|
|
1106
|
+
y: number;
|
|
1107
|
+
width: number;
|
|
1108
|
+
height: number;
|
|
1109
|
+
}, after: {
|
|
1110
|
+
x: number;
|
|
1111
|
+
y: number;
|
|
1112
|
+
width: number;
|
|
1113
|
+
height: number;
|
|
1114
|
+
}, transformType: ComponentType<{
|
|
1115
|
+
x: number;
|
|
1116
|
+
y: number;
|
|
1117
|
+
width: number;
|
|
1118
|
+
height: number;
|
|
1119
|
+
}>);
|
|
1120
|
+
execute(world: World): void;
|
|
1121
|
+
undo(world: World): void;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Snapshot of an entity's full component + tag state, suitable for
|
|
1125
|
+
* reconstruction via {@link rehydrateEntity} on undo.
|
|
1126
|
+
*/
|
|
1127
|
+
interface EntitySnapshot {
|
|
1128
|
+
entityId: EntityId;
|
|
1129
|
+
components: Array<{
|
|
1130
|
+
type: ComponentType<unknown>;
|
|
1131
|
+
data: unknown;
|
|
1132
|
+
}>;
|
|
1133
|
+
tags: TagType[];
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Capture an entity's complete current state. Used by `ConsumeCommand`
|
|
1137
|
+
* so `undo` can fully reconstitute a destroyed child. Relies on
|
|
1138
|
+
* `world.getComponentsOf(entity)` / `world.getTagsOf(entity)` so no
|
|
1139
|
+
* external type registries need to be threaded through.
|
|
1140
|
+
*/
|
|
1141
|
+
declare function snapshotEntity(world: World, entity: EntityId): EntitySnapshot;
|
|
1142
|
+
/**
|
|
1143
|
+
* Recreate an entity's state from a snapshot. Uses the same
|
|
1144
|
+
* `EntityId` captured in the snapshot if the world allows
|
|
1145
|
+
* reassignment; otherwise creates a fresh id and returns it.
|
|
1146
|
+
*/
|
|
1147
|
+
declare function rehydrateEntity(world: World, snapshot: EntitySnapshot): EntityId;
|
|
1148
|
+
/**
|
|
1149
|
+
* Drop-to-consume command (RFC-004 § Phase 4). Undo / redo are
|
|
1150
|
+
* fully supported by:
|
|
1151
|
+
* - capturing a full entity snapshot of the child at construction time,
|
|
1152
|
+
* - delegating the forward mutation to the parent widget's
|
|
1153
|
+
* `applyMutation` handler,
|
|
1154
|
+
* - delegating the reverse mutation to `revertMutation`.
|
|
1155
|
+
*
|
|
1156
|
+
* Handlers are supplied at construction time (captured from the widget
|
|
1157
|
+
* registry in the interaction runtime's pointerup path), so the
|
|
1158
|
+
* command doesn't need to know about the registry shape itself.
|
|
1159
|
+
*/
|
|
1160
|
+
declare class ConsumeCommand implements Command {
|
|
1161
|
+
readonly parentId: EntityId;
|
|
1162
|
+
readonly childId: EntityId;
|
|
1163
|
+
readonly childSnapshot: EntitySnapshot;
|
|
1164
|
+
readonly mutation: unknown;
|
|
1165
|
+
private readonly applyMutation;
|
|
1166
|
+
private readonly revertMutation;
|
|
1167
|
+
/**
|
|
1168
|
+
* The entity id we currently target for destroy. Starts as the
|
|
1169
|
+
* initial `childId` captured at construction; updated to the fresh
|
|
1170
|
+
* id returned by `rehydrateEntity` on undo so that a subsequent
|
|
1171
|
+
* `redo()` destroys the rehydrated entity (not a stale id from a
|
|
1172
|
+
* previous execute cycle).
|
|
1173
|
+
*/
|
|
1174
|
+
private currentChildId;
|
|
1175
|
+
constructor(parentId: EntityId, childId: EntityId, childSnapshot: EntitySnapshot, mutation: unknown, applyMutation: ((world: World, mutation: unknown) => void) | undefined, revertMutation: ((world: World, mutation: unknown) => void) | undefined);
|
|
1176
|
+
execute(world: World): void;
|
|
1177
|
+
undo(world: World): void;
|
|
1178
|
+
}
|
|
1179
|
+
declare class SetComponentCommand<T> implements Command {
|
|
1180
|
+
private entityId;
|
|
1181
|
+
private type;
|
|
1182
|
+
private before;
|
|
1183
|
+
private after;
|
|
1184
|
+
constructor(entityId: EntityId, type: ComponentType<T>, before: Partial<T>, after: Partial<T>);
|
|
1185
|
+
execute(world: World): void;
|
|
1186
|
+
undo(world: World): void;
|
|
1187
|
+
}
|
|
1188
|
+
//#endregion
|
|
1189
|
+
//#region src/ecs/engine/types.d.ts
|
|
1190
|
+
/** Directive returned by pointer handlers indicating how the canvas should handle capture. */
|
|
1191
|
+
type PointerDirective = {
|
|
1192
|
+
action: 'passthrough';
|
|
1193
|
+
} | {
|
|
1194
|
+
action: 'passthrough-track-drag';
|
|
1195
|
+
} | {
|
|
1196
|
+
action: 'capture-drag';
|
|
1197
|
+
} | {
|
|
1198
|
+
action: 'capture-resize';
|
|
1199
|
+
handle: ResizeHandlePos;
|
|
1200
|
+
} | {
|
|
1201
|
+
action: 'capture-marquee';
|
|
1202
|
+
};
|
|
1203
|
+
/** Keyboard modifier state captured alongside pointer events. */
|
|
1204
|
+
interface Modifiers {
|
|
1205
|
+
shift: boolean;
|
|
1206
|
+
ctrl: boolean;
|
|
1207
|
+
alt: boolean;
|
|
1208
|
+
meta: boolean;
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* A visible entity with its computed frame-local bounds and display metadata.
|
|
1212
|
+
*
|
|
1213
|
+
* Bounds are copied from the entity's `Transform2D` — post-RFC-004 Phase 0b,
|
|
1214
|
+
* every entity's Transform2D is already in its frame's local coord system,
|
|
1215
|
+
* so there is no distinct "world" space to translate into.
|
|
1216
|
+
*/
|
|
1217
|
+
interface VisibleEntity {
|
|
1218
|
+
entityId: EntityId;
|
|
1219
|
+
x: number;
|
|
1220
|
+
y: number;
|
|
1221
|
+
width: number;
|
|
1222
|
+
height: number;
|
|
1223
|
+
breakpoint: Breakpoint;
|
|
1224
|
+
zIndex: number;
|
|
1225
|
+
surface: string;
|
|
1226
|
+
widgetType: string;
|
|
1227
|
+
}
|
|
1228
|
+
/** Per-frame change flags indicating what changed during the last tick. */
|
|
1229
|
+
interface FrameChanges {
|
|
1230
|
+
positionsChanged: EntityId[];
|
|
1231
|
+
breakpointsChanged: EntityId[];
|
|
1232
|
+
/**
|
|
1233
|
+
* Entities whose `ZIndex` component value changed during the last
|
|
1234
|
+
* tick. Consumed by `<InfiniteCanvas>` to write `el.style.zIndex` on
|
|
1235
|
+
* each slot so R3F cards (which intentionally skip the `Layer`
|
|
1236
|
+
* re-bucket path during drag) still stack correctly when their
|
|
1237
|
+
* `ZIndex` is bumped — without this, a dragged R3F card's CSS chrome
|
|
1238
|
+
* retains its drag-start DOM order and hides beneath sibling
|
|
1239
|
+
* chromes whose intrinsic ZIndex was lower.
|
|
1240
|
+
*/
|
|
1241
|
+
zIndicesChanged: EntityId[];
|
|
1242
|
+
entered: EntityId[];
|
|
1243
|
+
exited: EntityId[];
|
|
1244
|
+
cameraChanged: boolean;
|
|
1245
|
+
navigationChanged: boolean;
|
|
1246
|
+
selectionChanged: boolean;
|
|
1247
|
+
/**
|
|
1248
|
+
* True when at least one entity's `Layer` component value changed
|
|
1249
|
+
* during the last tick. Render layers consume this to re-bucket
|
|
1250
|
+
* widget slots into their target layer container — see RFC-003 §
|
|
1251
|
+
* dragPromoteSystem and `<InfiniteCanvas>`'s bucket memo.
|
|
1252
|
+
*/
|
|
1253
|
+
layersChanged: boolean;
|
|
1254
|
+
}
|
|
1255
|
+
/** Configuration options for `createLayoutEngine()`. */
|
|
1256
|
+
interface LayoutEngineConfig<W extends WidgetBinding = Widget> {
|
|
1257
|
+
/** Maximum entity count (default: 10000). */
|
|
1258
|
+
maxEntities?: number;
|
|
1259
|
+
/** Minimum and maximum zoom levels. */
|
|
1260
|
+
zoom?: {
|
|
1261
|
+
min: number;
|
|
1262
|
+
max: number;
|
|
1263
|
+
};
|
|
1264
|
+
/** Screen-space pixel thresholds for responsive breakpoints. */
|
|
1265
|
+
breakpoints?: {
|
|
1266
|
+
micro: number;
|
|
1267
|
+
compact: number;
|
|
1268
|
+
normal: number;
|
|
1269
|
+
expanded: number;
|
|
1270
|
+
};
|
|
1271
|
+
/** Snap alignment configuration. */
|
|
1272
|
+
snap?: {
|
|
1273
|
+
/** Run alignment-snap math during drag. Default true. */enabled?: boolean; /** Threshold in world pixels. Default 5. Zoom-corrected at use site. */
|
|
1274
|
+
threshold?: number;
|
|
1275
|
+
/**
|
|
1276
|
+
* Render the magenta guide lines and equal-spacing indicators while
|
|
1277
|
+
* snap math is active. Default true. Independent of `enabled` —
|
|
1278
|
+
* you can run snap math without showing chrome (e.g. screenshot
|
|
1279
|
+
* mode) or vice versa for demos.
|
|
1280
|
+
*/
|
|
1281
|
+
guidesVisible?: boolean;
|
|
1282
|
+
};
|
|
1283
|
+
/** Widget definitions available to `spawn()`. */
|
|
1284
|
+
widgets?: W[];
|
|
1285
|
+
/** Archetype definitions available to `spawn()`. */
|
|
1286
|
+
archetypes?: Archetype[];
|
|
1287
|
+
/**
|
|
1288
|
+
* Override the default iOS-style card preset sizes (small/medium/large/xl).
|
|
1289
|
+
* Partial — unspecified presets keep their built-in defaults.
|
|
1290
|
+
*/
|
|
1291
|
+
cardPresets?: {
|
|
1292
|
+
presets?: Partial<Record<CardPreset, {
|
|
1293
|
+
width: number;
|
|
1294
|
+
height: number;
|
|
1295
|
+
}>>;
|
|
1296
|
+
gap?: number;
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* The core layout engine. Manages the ECS world, camera, input, undo/redo,
|
|
1301
|
+
* spatial indexing, and frame lifecycle for an infinite canvas.
|
|
1302
|
+
*/
|
|
1303
|
+
interface LayoutEngine<W extends WidgetBinding = Widget> {
|
|
1304
|
+
/** The underlying ECS world. Use for direct component/tag/resource access. */
|
|
1305
|
+
readonly world: World;
|
|
1306
|
+
createEntity(inits?: ComponentInit[]): EntityId;
|
|
1307
|
+
spawn(id: string, opts?: SpawnOptions): EntityId;
|
|
1308
|
+
spawnAtCameraCenter(id: string, opts?: Omit<SpawnOptions, 'at'>): EntityId;
|
|
1309
|
+
destroyEntity(id: EntityId): void;
|
|
1310
|
+
registerWidget(widget: W): void;
|
|
1311
|
+
getWidget(type: string): W | null;
|
|
1312
|
+
getWidgets(): W[];
|
|
1313
|
+
registerArchetype(archetype: Archetype): void;
|
|
1314
|
+
getArchetype(id: string): Archetype | null;
|
|
1315
|
+
get<T>(entity: EntityId, type: ComponentType<T>): T | undefined;
|
|
1316
|
+
set<T>(entity: EntityId, type: ComponentType<T>, data: Partial<T>): void;
|
|
1317
|
+
has(entity: EntityId, type: ComponentType | TagType): boolean;
|
|
1318
|
+
addComponent<T>(entity: EntityId, type: ComponentType<T>, data?: T): void;
|
|
1319
|
+
removeComponent(entity: EntityId, type: ComponentType): void;
|
|
1320
|
+
addTag(entity: EntityId, type: TagType): void;
|
|
1321
|
+
removeTag(entity: EntityId, type: TagType): void;
|
|
1322
|
+
getSchemaFor(entity: EntityId): StandardSchemaV1 | undefined;
|
|
1323
|
+
registerSystem(system: SystemDef): void;
|
|
1324
|
+
removeSystem(name: string): void;
|
|
1325
|
+
getCamera(): {
|
|
1326
|
+
x: number;
|
|
1327
|
+
y: number;
|
|
1328
|
+
zoom: number;
|
|
1329
|
+
gesturing: boolean;
|
|
1330
|
+
};
|
|
1331
|
+
panBy(dx: number, dy: number): void;
|
|
1332
|
+
panTo(worldX: number, worldY: number): void;
|
|
1333
|
+
zoomAtPoint(screenX: number, screenY: number, delta: number): void;
|
|
1334
|
+
zoomTo(zoom: number): void;
|
|
1335
|
+
zoomToFit(entityIds?: EntityId[], padding?: number): void;
|
|
1336
|
+
/** Mark the camera as actively manipulated (continuous wheel / pinch / pan). */
|
|
1337
|
+
setGesturing(active: boolean): void;
|
|
1338
|
+
setViewport(width: number, height: number, dpr?: number): void;
|
|
1339
|
+
/**
|
|
1340
|
+
* @deprecated RFC-008 Phase 3d retired the canvas's runtime use of these
|
|
1341
|
+
* methods — `InputManager` now drives the engine via `beginDrag` /
|
|
1342
|
+
* `beginResize` / `beginMarquee` etc. Kept as a backwards-compat shim
|
|
1343
|
+
* for tests and external integrations that still drive the state
|
|
1344
|
+
* machine via screen coordinates. Will be removed in a future release.
|
|
1345
|
+
*/
|
|
1346
|
+
handlePointerDown(screenX: number, screenY: number, button: number, modifiers: Modifiers): PointerDirective;
|
|
1347
|
+
/** @deprecated See {@link LayoutEngine.handlePointerDown}. */
|
|
1348
|
+
handlePointerMove(screenX: number, screenY: number, modifiers: Modifiers): PointerDirective;
|
|
1349
|
+
/** @deprecated See {@link LayoutEngine.handlePointerDown}. */
|
|
1350
|
+
handlePointerUp(): PointerDirective;
|
|
1351
|
+
/** @deprecated See {@link LayoutEngine.handlePointerDown}. */
|
|
1352
|
+
handlePointerCancel(): void;
|
|
1353
|
+
/**
|
|
1354
|
+
* Topmost interactable entity at a screen-space point. Same hit-test
|
|
1355
|
+
* the pointer state machine uses, exposed for callers that need to
|
|
1356
|
+
* resolve coords without firing pointer side-effects — RFC-006's
|
|
1357
|
+
* `PointerEventBus` (double-click) and the R3F `EventRouter`.
|
|
1358
|
+
*/
|
|
1359
|
+
pickAt(screenX: number, screenY: number): EntityId | null;
|
|
1360
|
+
/**
|
|
1361
|
+
* Rich variant of `pickAt` — returns the topmost interactable entity
|
|
1362
|
+
* AND its `InteractionRoleData` (role + layer). The role discriminates
|
|
1363
|
+
* between `'drag'`, `'select'`, `'resize'` (with `handle`), `'rotate'`,
|
|
1364
|
+
* `'connect'`, `'canvas'` so the InputManager's `drag-start` handler
|
|
1365
|
+
* can branch into the right engine entry point (`beginResize` for
|
|
1366
|
+
* resize hotspots, `beginDrag` otherwise). Same hit-test as `pickAt`.
|
|
1367
|
+
*/
|
|
1368
|
+
hitTest(screenX: number, screenY: number): {
|
|
1369
|
+
entityId: EntityId;
|
|
1370
|
+
role: InteractionRoleData;
|
|
1371
|
+
} | null;
|
|
1372
|
+
beginDrag(entity: EntityId, worldX: number, worldY: number): void;
|
|
1373
|
+
updateDrag(entity: EntityId, worldX: number, worldY: number): void;
|
|
1374
|
+
endDrag(entity: EntityId, opts: {
|
|
1375
|
+
cancelled: boolean;
|
|
1376
|
+
}): void;
|
|
1377
|
+
getDraggingEntity(): EntityId | null;
|
|
1378
|
+
/**
|
|
1379
|
+
* Cancel whatever interaction is in progress — drag, resize, marquee,
|
|
1380
|
+
* tracking, or fly-back. Used by the InputManager pipeline's `cancel`
|
|
1381
|
+
* engine handler so a native `pointercancel` (system dialog, palm
|
|
1382
|
+
* rejection, etc.) leaves no orphaned state behind.
|
|
1383
|
+
*/
|
|
1384
|
+
cancelInteraction(): void;
|
|
1385
|
+
beginResize(entity: EntityId, handle: ResizeHandlePos, worldX: number, worldY: number): boolean;
|
|
1386
|
+
updateResize(entity: EntityId, worldX: number, worldY: number): void;
|
|
1387
|
+
endResize(entity: EntityId, opts: {
|
|
1388
|
+
cancelled: boolean;
|
|
1389
|
+
}): void;
|
|
1390
|
+
isResizing(): boolean;
|
|
1391
|
+
getResizingEntity(): EntityId | null;
|
|
1392
|
+
beginMarquee(worldX: number, worldY: number): void;
|
|
1393
|
+
updateMarquee(worldX: number, worldY: number): void;
|
|
1394
|
+
endMarquee(): void;
|
|
1395
|
+
isMarqueeActive(): boolean;
|
|
1396
|
+
getSelectedEntities(): EntityId[];
|
|
1397
|
+
/**
|
|
1398
|
+
* Add `entity` to the selection. `additive=false` clears every other
|
|
1399
|
+
* `Selected` first; `additive=true` toggles `entity` and leaves the
|
|
1400
|
+
* rest untouched. Mirrors the legacy click-to-select semantics that
|
|
1401
|
+
* Phase 3d's `tap` and `drag-start` engine handlers will drive.
|
|
1402
|
+
*/
|
|
1403
|
+
selectEntity(entity: EntityId, additive: boolean): void;
|
|
1404
|
+
clearSelection(): void;
|
|
1405
|
+
getHoveredEntity(): EntityId | null;
|
|
1406
|
+
setHoveredEntity(entity: EntityId | null): void;
|
|
1407
|
+
/**
|
|
1408
|
+
* Run the engine's hit-test for a screen-space point and update both
|
|
1409
|
+
* `hoveredEntity` and the internal resize-handle cursor cache so the
|
|
1410
|
+
* cursor can switch between the 8 RFC-005 resize hotspots within a
|
|
1411
|
+
* single selected widget. No-op while a drag / resize / marquee /
|
|
1412
|
+
* fly-back is in progress.
|
|
1413
|
+
*/
|
|
1414
|
+
updateHover(screenX: number, screenY: number): void;
|
|
1415
|
+
enterContainer(entity: EntityId): void;
|
|
1416
|
+
exitContainer(): void;
|
|
1417
|
+
getActiveContainer(): EntityId | null;
|
|
1418
|
+
getNavigationDepth(): number;
|
|
1419
|
+
execute(command: Command): void;
|
|
1420
|
+
beginCommandGroup(): void;
|
|
1421
|
+
endCommandGroup(): void;
|
|
1422
|
+
undo(): boolean;
|
|
1423
|
+
redo(): boolean;
|
|
1424
|
+
canUndo(): boolean;
|
|
1425
|
+
canRedo(): boolean;
|
|
1426
|
+
markDirty(): void;
|
|
1427
|
+
tick(): void;
|
|
1428
|
+
flushIfDirty(): boolean;
|
|
1429
|
+
getVisibleEntities(): VisibleEntity[];
|
|
1430
|
+
getFrameChanges(): FrameChanges;
|
|
1431
|
+
getSpatialIndex(): SpatialIndex;
|
|
1432
|
+
getSnapGuides(): SnapGuide[];
|
|
1433
|
+
getEqualSpacing(): EqualSpacingIndicator[];
|
|
1434
|
+
setSnapEnabled(on: boolean): void;
|
|
1435
|
+
setSnapThreshold(worldPx: number): void;
|
|
1436
|
+
getSnapGuidesVisible(): boolean;
|
|
1437
|
+
setSnapGuidesVisible(on: boolean): void;
|
|
1438
|
+
readonly profiler: Profiler;
|
|
1439
|
+
onFrame(handler: () => void): Unsubscribe;
|
|
1440
|
+
destroy(): void;
|
|
1441
|
+
}
|
|
1442
|
+
//#endregion
|
|
1443
|
+
//#region src/ecs/engine/LayoutEngine.d.ts
|
|
1444
|
+
/**
|
|
1445
|
+
* Creates a new LayoutEngine instance with the given configuration.
|
|
1446
|
+
* This is the main entry point for the infinite canvas library.
|
|
1447
|
+
*/
|
|
1448
|
+
declare function createLayoutEngine<W extends WidgetBinding = WidgetBinding>(config?: LayoutEngineConfig<W>): LayoutEngine<W>;
|
|
1449
|
+
//#endregion
|
|
1450
|
+
export { FrameCameraState as $, Visible as $t, createWidgetRegistry as A, CursorHintData as At, WebGLPass as B, OverlapCandidate as Bt, R3FWidget as C, CardPreset as Ct, WidgetInteractionHandlers as D, ContainerChildren as Dt, isR3FWidget as E, ContainerCamera as Et, ProfilerStats as F, InteractionRoleType as Ft, SnapResult as G, Selectable as Gt, EntityBounds as H, ParentFrame as Ht, R3FPhaseHistogram as I, Layer as It, BreakpointConfigResource as J, SnapSource as Jt, computeSnapGuides as K, Selected as Kt, R3FSample as L, LayerData as Lt, EcsStats as M, Dragging as Mt, FrameTimeStats as N, InteractionRole as Nt, WidgetRegistry as O, Culled as Ot, Profiler as P, InteractionRoleData as Pt, CursorResourceData as Q, TweenEasing as Qt, R3FStats as R, LayerName as Rt, DomWidgetProps as S, CardOverlapHotPoint as St, Widget as T, Container as Tt, EqualSpacingIndicator as U, Resizable as Ut, WebGLStats as V, OverlapTarget as Vt, SnapGuide as W, ResizeHandlePos as Wt, CardPresetsResource as X, Transform2D as Xt, CameraResource as Y, SnapTarget as Yt, CursorResource as Z, TransformTween as Zt, Archetype as _, screenToWorld as _t, Modifiers as a, SpatialIndexResource as at, createArchetypeRegistry as b, CSSCursor as bt, Command as c, SpatialIndex as ct, EntitySnapshot as d, Vec2 as dt, Widget$1 as en, LayerOrderData as et, MoveCommand as f, aabbToRect as ft, snapshotEntity as g, rectToAABB as gt, rehydrateEntity as h, pointInAABB as ht, LayoutEngineConfig as i, RootCameraResource as it, StandardSchemaV1 as j, Draggable as jt, WidgetSurface as k, CursorHint as kt, CommandBuffer as l, AABB as lt, SetComponentCommand as m, intersectsAABB as mt, FrameChanges as n, WidgetData as nn, NavigationFrame as nt, PointerDirective as o, ViewportResource as ot, ResizeCommand as p, clamp as pt, Breakpoint as q, SelectionFrame as qt, LayoutEngine as r, ZIndex as rn, NavigationStackResource as rt, VisibleEntity as s, ZoomConfigResource as st, createLayoutEngine as t, WidgetBreakpoint as tn, LayerOrderResource as tt, ConsumeCommand as u, Rect as ut, ArchetypeRegistry as v, worldToScreen as vt, R3FWidgetProps as w, Children as wt, DomWidget as x, Card as xt, SpawnOptions as y, Active as yt, TickSample as z, Locked as zt };
|
|
1451
|
+
//# sourceMappingURL=index-DSdbSQ_t.d.cts.map
|