@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
package/dist/advanced.d.mts
CHANGED
|
@@ -1,99 +1,215 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { $ as FrameCameraState, B as WebGLPass, F as ProfilerStats, I as R3FPhaseHistogram, K as computeSnapGuides, L as R3FSample, M as EcsStats, N as FrameTimeStats, P as Profiler, R as R3FStats, U as EqualSpacingIndicator, V as WebGLStats, W as SnapGuide, at as SpatialIndexResource, ct as SpatialIndex, r as LayoutEngine, z as TickSample } from "./index-Dj9odADH.mjs";
|
|
2
|
+
import { A as SelectionConfig, B as ContainerRefProvider, C as useCompositor, D as SnapGuideConfig, F as ResolvedWidget, N as GridConfig, P as GridRenderer, R as EngineProvider, S as CompositorWidgetEntry, T as ResourceRegistry, _ as R3FRenderState, a as VirtualWidget, b as CompositorContext, c as useSharedTexture, d as useWidgetPhase, f as R3FAnimationSignal, g as R3FRenderBudgetData, h as R3FRenderBudget, i as WidgetStateMachine, j as SelectionRenderer, k as SelectionBounds, l as useWidgetAnimation, m as R3FPhase, n as isOutOfBand, o as useSharedGeometry, p as R3FPaintedAt, r as selectBand, s as useSharedMaterial, t as ZOOM_BANDS, u as useWidgetInvalidate, v as R3FRenderStateData, w as WidgetRenderTargetPool, x as CompositorContextValue, y as Compositor } from "./index-3GY7T8JM.mjs";
|
|
3
3
|
import { ComponentType, EntityId, TagType, World } from "@jamesyong42/reactive-ecs";
|
|
4
4
|
import * as _$react from "react";
|
|
5
|
+
import * as THREE from "three";
|
|
5
6
|
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
6
7
|
|
|
7
|
-
//#region src/
|
|
8
|
+
//#region src/ecs/serialization.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* JSON-serializable snapshot of the canvas state, including all entities,
|
|
11
|
+
* the current camera, and the persisted root-frame camera.
|
|
12
|
+
*
|
|
13
|
+
* The navigation stack is **not** serialized — reloading a canvas always
|
|
14
|
+
* drops the user at the root frame (RFC-004 § Phase 0c). Per-container
|
|
15
|
+
* camera state is persisted via the `ContainerCamera` component on each
|
|
16
|
+
* container entity (serialized as part of its component set).
|
|
17
|
+
*/
|
|
18
|
+
interface CanvasDocument {
|
|
19
|
+
version: number;
|
|
20
|
+
entities: SerializedEntity[];
|
|
21
|
+
resources: {
|
|
22
|
+
/** Current live camera (whatever frame the user was in at serialize time). */camera: FrameCameraState; /** Persisted root-frame camera — restored on load. */
|
|
23
|
+
rootCamera: FrameCameraState;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** A single serialized entity with its components and tags. */
|
|
27
|
+
interface SerializedEntity {
|
|
28
|
+
id: EntityId;
|
|
29
|
+
components: Record<string, unknown>;
|
|
30
|
+
tags: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Serializes all entities, components, and tags to a JSON-compatible document.
|
|
34
|
+
* Requires registries of known component and tag types for enumeration.
|
|
35
|
+
*/
|
|
36
|
+
declare function serializeWorld(world: World, componentTypes: ComponentType[], tagTypes: TagType[], camera: FrameCameraState, rootCamera: FrameCameraState): CanvasDocument;
|
|
37
|
+
/**
|
|
38
|
+
* Restores entities from a serialized document into the world.
|
|
39
|
+
* Clears existing state first and remaps entity IDs automatically.
|
|
40
|
+
*/
|
|
41
|
+
declare function deserializeWorld(world: World, doc: CanvasDocument, componentTypes: ComponentType[], tagTypes: TagType[]): void;
|
|
42
|
+
/**
|
|
43
|
+
* Serializes a subset of entities (e.g., for copy/paste).
|
|
44
|
+
* Recursively includes children of the specified entities.
|
|
45
|
+
*/
|
|
46
|
+
declare function serializeEntities(world: World, entityIds: EntityId[], componentTypes: ComponentType[], tagTypes: TagType[]): SerializedEntity[];
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/react/overlays/SelectionOverlaySlot.d.ts
|
|
8
49
|
interface SelectionOverlaySlotProps {
|
|
9
50
|
entityId: EntityId;
|
|
10
51
|
slotRef: (entityId: EntityId, el: HTMLDivElement | null) => void;
|
|
11
52
|
}
|
|
12
53
|
/**
|
|
13
|
-
* DOM overlay for
|
|
14
|
-
*
|
|
54
|
+
* DOM chrome overlay for R3F widgets — renders the selection frame /
|
|
55
|
+
* card decoration and positions itself at the widget's world AABB.
|
|
56
|
+
*
|
|
57
|
+
* Decoration only. Pointer events bypass this wrapper
|
|
58
|
+
* (`pointer-events: none`) so they reach the R3F canvas underneath,
|
|
59
|
+
* where the `EventRouter` raycasts the widget's local scene (RFC-006).
|
|
60
|
+
* Engine semantics — drag, select, resize, double-click — are dispatched
|
|
61
|
+
* by the canvas-level `PointerEventBus` after the widget's R3F handlers
|
|
62
|
+
* have had a chance to call `event.stopPropagation()`.
|
|
15
63
|
*/
|
|
16
64
|
declare const SelectionOverlaySlot: _$react.MemoExoticComponent<({
|
|
17
65
|
entityId,
|
|
18
66
|
slotRef
|
|
19
67
|
}: SelectionOverlaySlotProps) => _$react_jsx_runtime0.JSX.Element>;
|
|
20
68
|
//#endregion
|
|
21
|
-
//#region src/react/WidgetSlot.d.ts
|
|
69
|
+
//#region src/react/widgets/WidgetSlot.d.ts
|
|
22
70
|
interface WidgetSlotProps {
|
|
23
71
|
entityId: EntityId;
|
|
24
72
|
slotRef: (entityId: EntityId, el: HTMLDivElement | null) => void;
|
|
25
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Wrapper for a DOM widget — owns the slot's positioning, registers its
|
|
76
|
+
* ref with the rAF batcher, and renders the user's widget component.
|
|
77
|
+
*
|
|
78
|
+
* Pointer routing lives in the canvas-level `PointerEventBus` (RFC-006).
|
|
79
|
+
* The slot does not call the engine; user widget React handlers run on
|
|
80
|
+
* the natural DOM event path and bubble to the bus, which decides
|
|
81
|
+
* whether to invoke engine semantics. Authors call `e.stopPropagation()`
|
|
82
|
+
* from inside their widget to opt out of engine drag/select.
|
|
83
|
+
*/
|
|
26
84
|
declare const WidgetSlot: _$react.MemoExoticComponent<({
|
|
27
85
|
entityId,
|
|
28
86
|
slotRef
|
|
29
87
|
}: WidgetSlotProps) => _$react_jsx_runtime0.JSX.Element>;
|
|
30
88
|
//#endregion
|
|
31
|
-
//#region src/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
resolve
|
|
41
|
-
}: WebGLWidgetLayerProps): _$react_jsx_runtime0.JSX.Element;
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/react/webgl/WebGLWidgetSlot.d.ts
|
|
44
|
-
interface WebGLWidgetSlotProps {
|
|
45
|
-
entityId: EntityId;
|
|
46
|
-
component: React.ComponentType<R3FWidgetProps>;
|
|
89
|
+
//#region src/webgl/WebGLManager.d.ts
|
|
90
|
+
/** Construction options for {@link WebGLManager}. */
|
|
91
|
+
interface WebGLManagerOptions {
|
|
92
|
+
/** Grid rendering config. Pass `false` to disable the grid entirely. */
|
|
93
|
+
grid?: Partial<GridConfig> | false;
|
|
94
|
+
/** Selection overlay config (outline color, handle size, etc.). */
|
|
95
|
+
selection?: Partial<SelectionConfig>;
|
|
96
|
+
/** Snap guide overlay config (color, line width, alpha). */
|
|
97
|
+
snapGuides?: Partial<SnapGuideConfig>;
|
|
47
98
|
}
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
entityId,
|
|
55
|
-
component: WidgetComponent
|
|
56
|
-
}: WebGLWidgetSlotProps): _$react_jsx_runtime0.JSX.Element | null;
|
|
57
|
-
//#endregion
|
|
58
|
-
//#region src/serialization.d.ts
|
|
59
|
-
/** JSON-serializable snapshot of the canvas state, including all entities and camera. */
|
|
60
|
-
interface CanvasDocument {
|
|
61
|
-
version: number;
|
|
62
|
-
entities: SerializedEntity[];
|
|
63
|
-
resources: {
|
|
64
|
-
camera: {
|
|
65
|
-
x: number;
|
|
66
|
-
y: number;
|
|
67
|
-
zoom: number;
|
|
68
|
-
};
|
|
69
|
-
navigationStack: NavigationFrame[];
|
|
99
|
+
/** Per-frame input passed to {@link WebGLManager.render}. */
|
|
100
|
+
interface WebGLFrameInput {
|
|
101
|
+
camera: {
|
|
102
|
+
x: number;
|
|
103
|
+
y: number;
|
|
104
|
+
zoom: number;
|
|
70
105
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
106
|
+
/** Selection outlines + 8 resize handles + hover. */
|
|
107
|
+
selection: {
|
|
108
|
+
bounds: SelectionBounds[];
|
|
109
|
+
hovered: SelectionBounds | null;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Alignment guides + equal-spacing indicators. Independent of
|
|
113
|
+
* `selection` — guides display whenever the engine is computing
|
|
114
|
+
* snap math, regardless of whether anything is selected. Set
|
|
115
|
+
* `visible: false` to skip the snap-guide pass entirely (the
|
|
116
|
+
* guides are still computed by the engine; only the render is
|
|
117
|
+
* suppressed).
|
|
118
|
+
*/
|
|
119
|
+
snap: {
|
|
120
|
+
guides: SnapGuide[];
|
|
121
|
+
spacings: EqualSpacingIndicator[];
|
|
122
|
+
visible: boolean;
|
|
123
|
+
};
|
|
124
|
+
/** Optional — if supplied, the manager records per-pass timings + GL info. */
|
|
125
|
+
profiler?: Profiler;
|
|
126
|
+
/** Forwarded into the profiler sample (count of DOM slot transform writes this tick). */
|
|
127
|
+
domPositionsUpdated?: number;
|
|
77
128
|
}
|
|
78
129
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
130
|
+
* Top-level coordinator for the library's vanilla-WebGL layer.
|
|
131
|
+
*
|
|
132
|
+
* Owns a single `THREE.WebGLRenderer` and drives the built-in renderers
|
|
133
|
+
* (dot grid, selection overlay, snap guides) through it. This replaces
|
|
134
|
+
* the previous pattern where `GridRenderer` owned the renderer and
|
|
135
|
+
* `SelectionRenderer` piggy-backed on it via an implicit side-effect —
|
|
136
|
+
* a manager makes the sharing explicit and gives {@link InfiniteCanvas}
|
|
137
|
+
* a single surface to talk to instead of three.
|
|
81
138
|
*/
|
|
82
|
-
declare
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
139
|
+
declare class WebGLManager {
|
|
140
|
+
private renderer;
|
|
141
|
+
private grid;
|
|
142
|
+
private selection;
|
|
143
|
+
private snapGuides;
|
|
144
|
+
constructor(canvas: HTMLCanvasElement, opts?: WebGLManagerOptions);
|
|
145
|
+
/** Resize the drawing buffer. Call on mount and ResizeObserver events. */
|
|
146
|
+
setSize(width: number, height: number, dpr?: number): void;
|
|
147
|
+
/** Update grid visuals (colors, spacings, fade ranges, etc.). */
|
|
148
|
+
setGridConfig(config: Partial<GridConfig>): void;
|
|
149
|
+
/** Update selection overlay visuals (outline color, handle size, etc.). */
|
|
150
|
+
setSelectionConfig(config: Partial<SelectionConfig>): void;
|
|
151
|
+
/** Update snap-guide visuals (color, line width, alpha). */
|
|
152
|
+
setSnapGuideConfig(config: Partial<SnapGuideConfig>): void;
|
|
153
|
+
/** Render one frame: grid → selection → snap guides, in stacking order. */
|
|
154
|
+
render(input: WebGLFrameInput): void;
|
|
155
|
+
/** Release GL resources — call on unmount. */
|
|
156
|
+
dispose(): void;
|
|
157
|
+
/** Escape hatch if an advanced consumer needs the underlying three renderer. */
|
|
158
|
+
getWebGLRenderer(): THREE.WebGLRenderer;
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/r3f/ProfilerProbe.d.ts
|
|
87
162
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
163
|
+
* Reports one R3F frame sample per animation frame to the engine profiler.
|
|
164
|
+
* Reads `renderer.info` from three.js — draw calls / triangles / memory /
|
|
165
|
+
* programs — which is maintained by R3F's default render loop regardless
|
|
166
|
+
* of whether we opt in. Only samples when the profiler is enabled.
|
|
90
167
|
*/
|
|
91
|
-
declare function
|
|
168
|
+
declare function ProfilerProbe({
|
|
169
|
+
engine,
|
|
170
|
+
widgetCount
|
|
171
|
+
}: {
|
|
172
|
+
engine: LayoutEngine;
|
|
173
|
+
widgetCount: number;
|
|
174
|
+
}): null;
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/r3f/R3FManager.d.ts
|
|
177
|
+
interface R3FManagerProps {
|
|
178
|
+
engine: LayoutEngine;
|
|
179
|
+
entities: EntityId[];
|
|
180
|
+
resolve: (entityId: EntityId) => ResolvedWidget | null;
|
|
181
|
+
/**
|
|
182
|
+
* Optional R3F nodes mounted at the Canvas root as siblings of the
|
|
183
|
+
* Compositor (so they live in the Canvas's default scene, not inside
|
|
184
|
+
* any widget portal). Canonical use: drei's `<Environment>` — the
|
|
185
|
+
* Compositor's `sharedEnv` propagation logic checks the root scene
|
|
186
|
+
* before iterating widget scenes, so a root-level env stays alive
|
|
187
|
+
* across widget navigation (RFC-004 Phase 5 follow-up).
|
|
188
|
+
*/
|
|
189
|
+
r3fRoot?: _$react.ReactNode;
|
|
190
|
+
/**
|
|
191
|
+
* Late-bound handle for the R3F event manager. RFC-008's `R3FRouter`
|
|
192
|
+
* (constructed in `InfiniteCanvas`) reads this ref to dispatch into
|
|
193
|
+
* R3F's mesh handlers from the InputManager pipeline. The factory
|
|
194
|
+
* writes the produced manager into `.current` once R3F invokes it.
|
|
195
|
+
*/
|
|
196
|
+
eventManagerRef?: _$react.MutableRefObject<any>;
|
|
197
|
+
}
|
|
92
198
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
199
|
+
* Top-level coordinator for the R3F (React Three Fiber) rendering layer.
|
|
200
|
+
*
|
|
201
|
+
* Mounts a single `<Canvas>` and lets the {@link Compositor} drive the
|
|
202
|
+
* render loop — each R3F widget paints into its own `WebGLRenderTarget`
|
|
203
|
+
* via {@link VirtualWidget} and a final composition pass samples those
|
|
204
|
+
* textures into the visible canvas (RFC-002 Phase 4).
|
|
95
205
|
*/
|
|
96
|
-
declare function
|
|
206
|
+
declare function R3FManager({
|
|
207
|
+
engine,
|
|
208
|
+
entities,
|
|
209
|
+
resolve,
|
|
210
|
+
r3fRoot,
|
|
211
|
+
eventManagerRef
|
|
212
|
+
}: R3FManagerProps): _$react_jsx_runtime0.JSX.Element;
|
|
97
213
|
//#endregion
|
|
98
|
-
export { type CanvasDocument, ContainerRefProvider, type EcsStats, EngineProvider, type FrameTimeStats, GridRenderer, Profiler, type ProfilerStats, type R3FSample, type R3FStats, SelectionOverlaySlot, SelectionRenderer, type SerializedEntity, SpatialIndex, SpatialIndexResource, type TickSample, type WebGLPass, type WebGLStats,
|
|
214
|
+
export { type CanvasDocument, Compositor, CompositorContext, type CompositorContextValue, type CompositorWidgetEntry, ContainerRefProvider, type EcsStats, EngineProvider, type FrameTimeStats, GridRenderer, Profiler, ProfilerProbe, type ProfilerStats, R3FAnimationSignal, R3FManager, type R3FPaintedAt, type R3FPhase, type R3FPhaseHistogram, R3FRenderBudget, type R3FRenderBudgetData, R3FRenderState, type R3FRenderStateData, type R3FSample, type R3FStats, ResourceRegistry, SelectionOverlaySlot, SelectionRenderer, type SerializedEntity, SpatialIndex, SpatialIndexResource, type TickSample, VirtualWidget, type WebGLFrameInput, WebGLManager, type WebGLManagerOptions, type WebGLPass, type WebGLStats, WidgetRenderTargetPool, WidgetSlot, WidgetStateMachine, ZOOM_BANDS, computeSnapGuides, deserializeWorld, isOutOfBand, selectBand, serializeEntities, serializeWorld, useCompositor, useSharedGeometry, useSharedMaterial, useSharedTexture, useWidgetAnimation, useWidgetInvalidate, useWidgetPhase };
|
|
99
215
|
//# sourceMappingURL=advanced.d.mts.map
|
package/dist/advanced.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"advanced.d.mts","names":[],"sources":["../src/react/SelectionOverlaySlot.tsx","../src/react/WidgetSlot.tsx","../src/
|
|
1
|
+
{"version":3,"file":"advanced.d.mts","names":[],"sources":["../src/ecs/serialization.ts","../src/react/overlays/SelectionOverlaySlot.tsx","../src/react/widgets/WidgetSlot.tsx","../src/webgl/WebGLManager.ts","../src/r3f/ProfilerProbe.tsx","../src/r3f/R3FManager.tsx"],"mappings":"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"}
|
package/dist/advanced.mjs
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { D as
|
|
3
|
-
//#region src/serialization.ts
|
|
1
|
+
import { O as Children, S as SpatialIndexResource, V as ParentFrame, f as EngineProvider, h as CameraResource, j as ContainerChildren, x as RootCameraResource } from "./ecs-3kimUV5Z.mjs";
|
|
2
|
+
import { A as useCompositor, B as Profiler, C as selectBand, D as R3FRenderState, E as R3FRenderBudget, I as ContainerRefProvider, O as ResourceRegistry, R as computeSnapGuides, S as isOutOfBand, T as R3FAnimationSignal, _ as ProfilerProbe, a as useWidgetInvalidate, b as Compositor, c as SelectionOverlaySlot, g as R3FManager, h as GridRenderer, i as useWidgetAnimation, k as CompositorContext, n as useSharedMaterial, o as useWidgetPhase, p as SelectionRenderer, r as useSharedTexture, s as WidgetSlot, t as useSharedGeometry, u as WebGLManager, v as WidgetStateMachine, w as WidgetRenderTargetPool, x as ZOOM_BANDS, y as VirtualWidget, z as SpatialIndex } from "./hooks-gsQDDE56.mjs";
|
|
3
|
+
//#region src/ecs/serialization.ts
|
|
4
4
|
/**
|
|
5
5
|
* Serializes all entities, components, and tags to a JSON-compatible document.
|
|
6
6
|
* Requires registries of known component and tag types for enumeration.
|
|
7
7
|
*/
|
|
8
|
-
function serializeWorld(world, componentTypes, tagTypes, camera,
|
|
8
|
+
function serializeWorld(world, componentTypes, tagTypes, camera, rootCamera) {
|
|
9
9
|
const entities = [];
|
|
10
10
|
const allEntities = world.query();
|
|
11
11
|
for (const entityId of allEntities) {
|
|
12
12
|
const components = {};
|
|
13
13
|
const tags = [];
|
|
14
14
|
for (const type of componentTypes) {
|
|
15
|
+
if (type.name === "PreDragLayer" || type.name === "TransformTween" || type.name === "CardOverlapHotPoint") continue;
|
|
15
16
|
const data = world.getComponent(entityId, type);
|
|
16
17
|
if (data !== void 0) components[type.name] = structuredClone(data);
|
|
17
18
|
}
|
|
18
19
|
for (const type of tagTypes) if (world.hasTag(entityId, type)) {
|
|
19
|
-
if (type.name !== "Active" && type.name !== "Visible") tags.push(type.name);
|
|
20
|
+
if (type.name !== "Active" && type.name !== "Visible" && type.name !== "Culled" && type.name !== "OverlapCandidate" && type.name !== "OverlapTarget") tags.push(type.name);
|
|
20
21
|
}
|
|
21
22
|
if (Object.keys(components).length > 0 || tags.length > 0) entities.push({
|
|
22
23
|
id: entityId,
|
|
@@ -29,7 +30,7 @@ function serializeWorld(world, componentTypes, tagTypes, camera, navigationFrame
|
|
|
29
30
|
entities,
|
|
30
31
|
resources: {
|
|
31
32
|
camera: { ...camera },
|
|
32
|
-
|
|
33
|
+
rootCamera: { ...rootCamera }
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
36
|
}
|
|
@@ -58,16 +59,31 @@ function deserializeWorld(world, doc, componentTypes, tagTypes) {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
for (const [_oldId, newId] of idMap) {
|
|
61
|
-
const parent = world.getComponent(newId,
|
|
62
|
+
const parent = world.getComponent(newId, ParentFrame);
|
|
62
63
|
if (parent && idMap.has(parent.id)) {
|
|
63
64
|
const mappedId = idMap.get(parent.id);
|
|
64
|
-
if (mappedId !== void 0) world.setComponent(newId,
|
|
65
|
+
if (mappedId !== void 0) world.setComponent(newId, ParentFrame, { id: mappedId });
|
|
65
66
|
}
|
|
66
67
|
const children = world.getComponent(newId, Children);
|
|
67
68
|
if (children) world.setComponent(newId, Children, { ids: children.ids.map((id) => idMap.get(id) ?? id) });
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
69
|
+
const containerChildren = world.getComponent(newId, ContainerChildren);
|
|
70
|
+
if (containerChildren) {
|
|
71
|
+
const mapped = [];
|
|
72
|
+
for (const id of containerChildren.ids) {
|
|
73
|
+
const remapped = idMap.get(id);
|
|
74
|
+
if (remapped !== void 0) mapped.push(remapped);
|
|
75
|
+
}
|
|
76
|
+
world.setComponent(newId, ContainerChildren, { ids: mapped });
|
|
77
|
+
}
|
|
70
78
|
}
|
|
79
|
+
const live = doc.resources.camera;
|
|
80
|
+
world.setResource(CameraResource, {
|
|
81
|
+
x: live.x,
|
|
82
|
+
y: live.y,
|
|
83
|
+
zoom: live.zoom,
|
|
84
|
+
gesturing: false
|
|
85
|
+
});
|
|
86
|
+
world.setResource(RootCameraResource, { ...doc.resources.rootCamera });
|
|
71
87
|
}
|
|
72
88
|
/**
|
|
73
89
|
* Serializes a subset of entities (e.g., for copy/paste).
|
|
@@ -82,11 +98,12 @@ function serializeEntities(world, entityIds, componentTypes, tagTypes) {
|
|
|
82
98
|
const components = {};
|
|
83
99
|
const tags = [];
|
|
84
100
|
for (const type of componentTypes) {
|
|
101
|
+
if (type.name === "PreDragLayer" || type.name === "TransformTween" || type.name === "CardOverlapHotPoint") continue;
|
|
85
102
|
const data = world.getComponent(entityId, type);
|
|
86
103
|
if (data !== void 0) components[type.name] = structuredClone(data);
|
|
87
104
|
}
|
|
88
105
|
for (const type of tagTypes) if (world.hasTag(entityId, type)) {
|
|
89
|
-
if (type.name !== "Active" && type.name !== "Visible") tags.push(type.name);
|
|
106
|
+
if (type.name !== "Active" && type.name !== "Visible" && type.name !== "Culled") tags.push(type.name);
|
|
90
107
|
}
|
|
91
108
|
result.push({
|
|
92
109
|
id: entityId,
|
|
@@ -100,6 +117,6 @@ function serializeEntities(world, entityIds, componentTypes, tagTypes) {
|
|
|
100
117
|
return result;
|
|
101
118
|
}
|
|
102
119
|
//#endregion
|
|
103
|
-
export { ContainerRefProvider, EngineProvider, GridRenderer, Profiler, SelectionOverlaySlot, SelectionRenderer, SpatialIndex, SpatialIndexResource,
|
|
120
|
+
export { Compositor, CompositorContext, ContainerRefProvider, EngineProvider, GridRenderer, Profiler, ProfilerProbe, R3FAnimationSignal, R3FManager, R3FRenderBudget, R3FRenderState, ResourceRegistry, SelectionOverlaySlot, SelectionRenderer, SpatialIndex, SpatialIndexResource, VirtualWidget, WebGLManager, WidgetRenderTargetPool, WidgetSlot, WidgetStateMachine, ZOOM_BANDS, computeSnapGuides, deserializeWorld, isOutOfBand, selectBand, serializeEntities, serializeWorld, useCompositor, useSharedGeometry, useSharedMaterial, useSharedTexture, useWidgetAnimation, useWidgetInvalidate, useWidgetPhase };
|
|
104
121
|
|
|
105
122
|
//# sourceMappingURL=advanced.mjs.map
|
package/dist/advanced.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"advanced.mjs","names":[],"sources":["../src/serialization.ts"],"sourcesContent":["import type { ComponentType, EntityId, TagType, World } from '@jamesyong42/reactive-ecs';\nimport { Children, HandleSet, Parent } from './components.js';\nimport type { NavigationFrame } from './resources.js';\n\n// === Serialization Types ===\n\n/** JSON-serializable snapshot of the canvas state, including all entities and camera. */\nexport interface CanvasDocument {\n\tversion: number;\n\tentities: SerializedEntity[];\n\tresources: {\n\t\tcamera: { x: number; y: number; zoom: number };\n\t\tnavigationStack: NavigationFrame[];\n\t};\n}\n\n/** A single serialized entity with its components and tags. */\nexport interface SerializedEntity {\n\tid: EntityId;\n\tcomponents: Record<string, unknown>;\n\ttags: string[];\n}\n\n// === Serialize/Deserialize ===\n\n/**\n * Serializes all entities, components, and tags to a JSON-compatible document.\n * Requires registries of known component and tag types for enumeration.\n */\nexport function serializeWorld(\n\tworld: World,\n\tcomponentTypes: ComponentType[],\n\ttagTypes: TagType[],\n\tcamera: { x: number; y: number; zoom: number },\n\tnavigationFrames: NavigationFrame[],\n): CanvasDocument {\n\tconst entities: SerializedEntity[] = [];\n\n\t// Get all entity IDs (use a broad query)\n\tconst allEntities = world.query();\n\n\tfor (const entityId of allEntities) {\n\t\tconst components: Record<string, unknown> = {};\n\t\tconst tags: string[] = [];\n\n\t\tfor (const type of componentTypes) {\n\t\t\tconst data = world.getComponent(entityId, type);\n\t\t\tif (data !== undefined) {\n\t\t\t\tcomponents[type.name] = structuredClone(data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const type of tagTypes) {\n\t\t\tif (world.hasTag(entityId, type)) {\n\t\t\t\t// Skip runtime-only tags (Active, Visible — they're recomputed)\n\t\t\t\tif (type.name !== 'Active' && type.name !== 'Visible') {\n\t\t\t\t\ttags.push(type.name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Object.keys(components).length > 0 || tags.length > 0) {\n\t\t\tentities.push({ id: entityId, components, tags });\n\t\t}\n\t}\n\n\treturn {\n\t\tversion: 1,\n\t\tentities,\n\t\tresources: {\n\t\t\tcamera: { ...camera },\n\t\t\tnavigationStack: structuredClone(navigationFrames),\n\t\t},\n\t};\n}\n\n/**\n * Restores entities from a serialized document into the world.\n * Clears existing state first and remaps entity IDs automatically.\n */\nexport function deserializeWorld(\n\tworld: World,\n\tdoc: CanvasDocument,\n\tcomponentTypes: ComponentType[],\n\ttagTypes: TagType[],\n): void {\n\tif (doc.version !== 1) {\n\t\tthrow new Error(`Unsupported canvas document version: ${doc.version}. Expected version 1.`);\n\t}\n\n\t// Build lookup maps\n\tconst compByName = new Map<string, ComponentType>();\n\tfor (const t of componentTypes) compByName.set(t.name, t);\n\n\tconst tagByName = new Map<string, TagType>();\n\tfor (const t of tagTypes) tagByName.set(t.name, t);\n\n\t// Destroy all existing entities\n\tfor (const entityId of world.query()) {\n\t\tworld.destroyEntity(entityId);\n\t}\n\n\t// First pass: create entities and build old-to-new ID mapping\n\tconst idMap = new Map<EntityId, EntityId>();\n\n\tfor (const entry of doc.entities) {\n\t\tconst newId = world.createEntity();\n\t\tidMap.set(entry.id as EntityId, newId);\n\n\t\tfor (const [compName, data] of Object.entries(entry.components)) {\n\t\t\tconst type = compByName.get(compName);\n\t\t\tif (type) {\n\t\t\t\tworld.addComponent(newId, type, data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const tagName of entry.tags) {\n\t\t\tconst type = tagByName.get(tagName);\n\t\t\tif (type) {\n\t\t\t\tworld.addTag(newId, type);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second pass: remap cross-reference components (Parent, Children, HandleSet)\n\tfor (const [_oldId, newId] of idMap) {\n\t\tconst parent = world.getComponent(newId, Parent);\n\t\tif (parent && idMap.has(parent.id)) {\n\t\t\tconst mappedId = idMap.get(parent.id);\n\t\t\tif (mappedId !== undefined) {\n\t\t\t\tworld.setComponent(newId, Parent, { id: mappedId });\n\t\t\t}\n\t\t}\n\n\t\tconst children = world.getComponent(newId, Children);\n\t\tif (children) {\n\t\t\tworld.setComponent(newId, Children, {\n\t\t\t\tids: children.ids.map((id: EntityId) => idMap.get(id) ?? id),\n\t\t\t});\n\t\t}\n\n\t\tconst handleSet = world.getComponent(newId, HandleSet);\n\t\tif (handleSet) {\n\t\t\tworld.setComponent(newId, HandleSet, {\n\t\t\t\tids: handleSet.ids.map((id: EntityId) => idMap.get(id) ?? id),\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Serializes a subset of entities (e.g., for copy/paste).\n * Recursively includes children of the specified entities.\n */\nexport function serializeEntities(\n\tworld: World,\n\tentityIds: EntityId[],\n\tcomponentTypes: ComponentType[],\n\ttagTypes: TagType[],\n): SerializedEntity[] {\n\tconst result: SerializedEntity[] = [];\n\tconst visited = new Set<EntityId>();\n\n\tfunction visit(entityId: EntityId) {\n\t\tif (visited.has(entityId)) return;\n\t\tvisited.add(entityId);\n\n\t\tconst components: Record<string, unknown> = {};\n\t\tconst tags: string[] = [];\n\n\t\tfor (const type of componentTypes) {\n\t\t\tconst data = world.getComponent(entityId, type);\n\t\t\tif (data !== undefined) {\n\t\t\t\tcomponents[type.name] = structuredClone(data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const type of tagTypes) {\n\t\t\tif (world.hasTag(entityId, type)) {\n\t\t\t\tif (type.name !== 'Active' && type.name !== 'Visible') {\n\t\t\t\t\ttags.push(type.name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tresult.push({ id: entityId, components, tags });\n\n\t\t// Recurse into children. components.Children is typed as unknown via\n\t\t// the Record<string, unknown> shape, so narrow through a cast.\n\t\tconst children = components.Children as { ids?: EntityId[] } | undefined;\n\t\tif (children?.ids) {\n\t\t\tfor (const childId of children.ids) {\n\t\t\t\tvisit(childId);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const id of entityIds) {\n\t\tvisit(id);\n\t}\n\n\treturn result;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"advanced.mjs","names":[],"sources":["../src/ecs/serialization.ts"],"sourcesContent":["import type { ComponentType, EntityId, TagType, World } from '@jamesyong42/reactive-ecs';\nimport { Children, ContainerChildren, ParentFrame } from './components.js';\nimport type { FrameCameraState } from './resources.js';\nimport { CameraResource, RootCameraResource } from './resources.js';\n\n// === Serialization Types ===\n\n/**\n * JSON-serializable snapshot of the canvas state, including all entities,\n * the current camera, and the persisted root-frame camera.\n *\n * The navigation stack is **not** serialized — reloading a canvas always\n * drops the user at the root frame (RFC-004 § Phase 0c). Per-container\n * camera state is persisted via the `ContainerCamera` component on each\n * container entity (serialized as part of its component set).\n */\nexport interface CanvasDocument {\n\tversion: number;\n\tentities: SerializedEntity[];\n\tresources: {\n\t\t/** Current live camera (whatever frame the user was in at serialize time). */\n\t\tcamera: FrameCameraState;\n\t\t/** Persisted root-frame camera — restored on load. */\n\t\trootCamera: FrameCameraState;\n\t};\n}\n\n/** A single serialized entity with its components and tags. */\nexport interface SerializedEntity {\n\tid: EntityId;\n\tcomponents: Record<string, unknown>;\n\ttags: string[];\n}\n\n// === Serialize/Deserialize ===\n\n/**\n * Serializes all entities, components, and tags to a JSON-compatible document.\n * Requires registries of known component and tag types for enumeration.\n */\nexport function serializeWorld(\n\tworld: World,\n\tcomponentTypes: ComponentType[],\n\ttagTypes: TagType[],\n\tcamera: FrameCameraState,\n\trootCamera: FrameCameraState,\n): CanvasDocument {\n\tconst entities: SerializedEntity[] = [];\n\n\t// Get all entity IDs (use a broad query)\n\tconst allEntities = world.query();\n\n\tfor (const entityId of allEntities) {\n\t\tconst components: Record<string, unknown> = {};\n\t\tconst tags: string[] = [];\n\n\t\tfor (const type of componentTypes) {\n\t\t\t// Skip runtime-only components: `PreDragLayer` is recomputed by\n\t\t\t// dragPromoteSystem on the next Dragging flip; `TransformTween`\n\t\t\t// is an in-flight animation that should not be paused / resumed\n\t\t\t// across reloads (the destination `Transform2D` persists instead);\n\t\t\t// `CardOverlapHotPoint` is drag-scoped visual state with no\n\t\t\t// meaning outside an active drag.\n\t\t\tif (\n\t\t\t\ttype.name === 'PreDragLayer' ||\n\t\t\t\ttype.name === 'TransformTween' ||\n\t\t\t\ttype.name === 'CardOverlapHotPoint'\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst data = world.getComponent(entityId, type);\n\t\t\tif (data !== undefined) {\n\t\t\t\tcomponents[type.name] = structuredClone(data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const type of tagTypes) {\n\t\t\tif (world.hasTag(entityId, type)) {\n\t\t\t\t// Skip runtime-only tags: Active/Visible/Culled are recomputed\n\t\t\t\t// by the cull pipeline; OverlapCandidate/OverlapTarget are\n\t\t\t\t// drag-scoped and meaningless outside an active drag.\n\t\t\t\tif (\n\t\t\t\t\ttype.name !== 'Active' &&\n\t\t\t\t\ttype.name !== 'Visible' &&\n\t\t\t\t\ttype.name !== 'Culled' &&\n\t\t\t\t\ttype.name !== 'OverlapCandidate' &&\n\t\t\t\t\ttype.name !== 'OverlapTarget'\n\t\t\t\t) {\n\t\t\t\t\ttags.push(type.name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Object.keys(components).length > 0 || tags.length > 0) {\n\t\t\tentities.push({ id: entityId, components, tags });\n\t\t}\n\t}\n\n\treturn {\n\t\tversion: 1,\n\t\tentities,\n\t\tresources: {\n\t\t\tcamera: { ...camera },\n\t\t\trootCamera: { ...rootCamera },\n\t\t},\n\t};\n}\n\n/**\n * Restores entities from a serialized document into the world.\n * Clears existing state first and remaps entity IDs automatically.\n */\nexport function deserializeWorld(\n\tworld: World,\n\tdoc: CanvasDocument,\n\tcomponentTypes: ComponentType[],\n\ttagTypes: TagType[],\n): void {\n\tif (doc.version !== 1) {\n\t\tthrow new Error(`Unsupported canvas document version: ${doc.version}. Expected version 1.`);\n\t}\n\n\t// Build lookup maps\n\tconst compByName = new Map<string, ComponentType>();\n\tfor (const t of componentTypes) compByName.set(t.name, t);\n\n\tconst tagByName = new Map<string, TagType>();\n\tfor (const t of tagTypes) tagByName.set(t.name, t);\n\n\t// Destroy all existing entities\n\tfor (const entityId of world.query()) {\n\t\tworld.destroyEntity(entityId);\n\t}\n\n\t// First pass: create entities and build old-to-new ID mapping\n\tconst idMap = new Map<EntityId, EntityId>();\n\n\tfor (const entry of doc.entities) {\n\t\tconst newId = world.createEntity();\n\t\tidMap.set(entry.id as EntityId, newId);\n\n\t\tfor (const [compName, data] of Object.entries(entry.components)) {\n\t\t\tconst type = compByName.get(compName);\n\t\t\tif (type) {\n\t\t\t\tworld.addComponent(newId, type, data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const tagName of entry.tags) {\n\t\t\tconst type = tagByName.get(tagName);\n\t\t\tif (type) {\n\t\t\t\tworld.addTag(newId, type);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second pass: remap cross-reference components (ParentFrame, Children,\n\t// ContainerChildren).\n\tfor (const [_oldId, newId] of idMap) {\n\t\tconst parent = world.getComponent(newId, ParentFrame);\n\t\tif (parent && idMap.has(parent.id)) {\n\t\t\tconst mappedId = idMap.get(parent.id);\n\t\t\tif (mappedId !== undefined) {\n\t\t\t\tworld.setComponent(newId, ParentFrame, { id: mappedId });\n\t\t\t}\n\t\t}\n\n\t\tconst children = world.getComponent(newId, Children);\n\t\tif (children) {\n\t\t\tworld.setComponent(newId, Children, {\n\t\t\t\tids: children.ids.map((id: EntityId) => idMap.get(id) ?? id),\n\t\t\t});\n\t\t}\n\n\t\tconst containerChildren = world.getComponent(newId, ContainerChildren);\n\t\tif (containerChildren) {\n\t\t\t// Drop ids that didn't round-trip (child was destroyed before save\n\t\t\t// and still lingered in the list) rather than falling back to the\n\t\t\t// raw pre-save id — that id may have been recycled to an unrelated\n\t\t\t// entity by the post-load world and would leak into the container's\n\t\t\t// child count / navigation target list.\n\t\t\tconst mapped: EntityId[] = [];\n\t\t\tfor (const id of containerChildren.ids) {\n\t\t\t\tconst remapped = idMap.get(id);\n\t\t\t\tif (remapped !== undefined) mapped.push(remapped);\n\t\t\t}\n\t\t\tworld.setComponent(newId, ContainerChildren, { ids: mapped });\n\t\t}\n\t}\n\n\t// Restore camera resources. `gesturing` resets to false on load — it's\n\t// transient interaction state, not persisted view state.\n\tconst live = doc.resources.camera;\n\tworld.setResource(CameraResource, { x: live.x, y: live.y, zoom: live.zoom, gesturing: false });\n\tworld.setResource(RootCameraResource, { ...doc.resources.rootCamera });\n\n\t// NavigationStack is deliberately not restored — users always return\n\t// to the root frame on load (RFC-004 § Phase 0c).\n}\n\n/**\n * Serializes a subset of entities (e.g., for copy/paste).\n * Recursively includes children of the specified entities.\n */\nexport function serializeEntities(\n\tworld: World,\n\tentityIds: EntityId[],\n\tcomponentTypes: ComponentType[],\n\ttagTypes: TagType[],\n): SerializedEntity[] {\n\tconst result: SerializedEntity[] = [];\n\tconst visited = new Set<EntityId>();\n\n\tfunction visit(entityId: EntityId) {\n\t\tif (visited.has(entityId)) return;\n\t\tvisited.add(entityId);\n\n\t\tconst components: Record<string, unknown> = {};\n\t\tconst tags: string[] = [];\n\n\t\tfor (const type of componentTypes) {\n\t\t\t// Skip runtime-only components: `PreDragLayer` is recomputed by\n\t\t\t// dragPromoteSystem on the next Dragging flip; `TransformTween`\n\t\t\t// is an in-flight animation that should not be paused / resumed\n\t\t\t// across reloads (the destination `Transform2D` persists instead);\n\t\t\t// `CardOverlapHotPoint` is drag-scoped visual state with no\n\t\t\t// meaning outside an active drag.\n\t\t\tif (\n\t\t\t\ttype.name === 'PreDragLayer' ||\n\t\t\t\ttype.name === 'TransformTween' ||\n\t\t\t\ttype.name === 'CardOverlapHotPoint'\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst data = world.getComponent(entityId, type);\n\t\t\tif (data !== undefined) {\n\t\t\t\tcomponents[type.name] = structuredClone(data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const type of tagTypes) {\n\t\t\tif (world.hasTag(entityId, type)) {\n\t\t\t\tif (type.name !== 'Active' && type.name !== 'Visible' && type.name !== 'Culled') {\n\t\t\t\t\ttags.push(type.name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tresult.push({ id: entityId, components, tags });\n\n\t\t// Recurse into children. components.Children is typed as unknown via\n\t\t// the Record<string, unknown> shape, so narrow through a cast.\n\t\tconst children = components.Children as { ids?: EntityId[] } | undefined;\n\t\tif (children?.ids) {\n\t\t\tfor (const childId of children.ids) {\n\t\t\t\tvisit(childId);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const id of entityIds) {\n\t\tvisit(id);\n\t}\n\n\treturn result;\n}\n"],"mappings":"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"}
|
package/dist/devtools.cjs
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./
|
|
3
|
-
const
|
|
2
|
+
require("./hooks-CtP02JNt.cjs");
|
|
3
|
+
const require_ecs = require("./ecs-B4QrqfvQ.cjs");
|
|
4
4
|
let react = require("react");
|
|
5
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
|
-
//#region src/
|
|
6
|
+
//#region src/devtools/EcsDevtools.tsx
|
|
7
7
|
/**
|
|
8
8
|
* Live ECS editor: spawn widgets, browse entities, edit components, toggle tags.
|
|
9
9
|
* Ship in a dev mode or behind a feature flag — not intended for production users.
|
|
10
10
|
*/
|
|
11
11
|
function EcsDevtools({ engine, onClose }) {
|
|
12
|
-
if (engine) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
12
|
+
if (engine) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ecs.EngineProvider, {
|
|
13
13
|
value: engine,
|
|
14
14
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EcsDevtoolsInner, { onClose })
|
|
15
15
|
});
|
|
16
16
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EcsDevtoolsInner, { onClose });
|
|
17
17
|
}
|
|
18
18
|
function EcsDevtoolsInner({ onClose }) {
|
|
19
|
-
const engine =
|
|
20
|
-
const allEntities =
|
|
21
|
-
const selectedIds =
|
|
19
|
+
const engine = require_ecs.useLayoutEngine();
|
|
20
|
+
const allEntities = require_ecs.useAllEntities();
|
|
21
|
+
const selectedIds = require_ecs.useTaggedEntities(require_ecs.Selected);
|
|
22
22
|
const widgets = (0, react.useMemo)(() => engine.getWidgets(), [engine]);
|
|
23
23
|
const [showAll, setShowAll] = (0, react.useState)(false);
|
|
24
24
|
const [spawnType, setSpawnType] = (0, react.useState)(widgets[0]?.type ?? "");
|
|
@@ -26,7 +26,7 @@ function EcsDevtoolsInner({ onClose }) {
|
|
|
26
26
|
const focusId = selectedIds[0] ?? manualFocusId;
|
|
27
27
|
const entityList = (0, react.useMemo)(() => {
|
|
28
28
|
if (showAll) return allEntities;
|
|
29
|
-
return allEntities.filter((id) => engine.has(id,
|
|
29
|
+
return allEntities.filter((id) => engine.has(id, require_ecs.Widget));
|
|
30
30
|
}, [
|
|
31
31
|
engine,
|
|
32
32
|
allEntities,
|
|
@@ -116,8 +116,8 @@ function EcsDevtoolsInner({ onClose }) {
|
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
118
|
function EntityRow({ entity, focused, onClick }) {
|
|
119
|
-
const engine =
|
|
120
|
-
const label =
|
|
119
|
+
const engine = require_ecs.useLayoutEngine();
|
|
120
|
+
const label = require_ecs.useComponent(entity, require_ecs.Widget)?.type ?? "entity";
|
|
121
121
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
122
122
|
className: `ic-ecs-entity-row ${focused ? "is-focused" : ""}`,
|
|
123
123
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -144,12 +144,12 @@ function EntityRow({ entity, focused, onClick }) {
|
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
146
|
function EntityInspector({ entity }) {
|
|
147
|
-
const engine =
|
|
148
|
-
const components =
|
|
149
|
-
const tags =
|
|
150
|
-
const registeredComponents =
|
|
151
|
-
const registeredTags =
|
|
152
|
-
const widget =
|
|
147
|
+
const engine = require_ecs.useLayoutEngine();
|
|
148
|
+
const components = require_ecs.useEntityComponents(entity);
|
|
149
|
+
const tags = require_ecs.useEntityTags(entity);
|
|
150
|
+
const registeredComponents = require_ecs.useRegisteredComponents();
|
|
151
|
+
const registeredTags = require_ecs.useRegisteredTags();
|
|
152
|
+
const widget = require_ecs.useComponent(entity, require_ecs.Widget);
|
|
153
153
|
const absentComponents = (0, react.useMemo)(() => {
|
|
154
154
|
const present = new Set(components.map((c) => c.name));
|
|
155
155
|
return registeredComponents.filter((c) => !present.has(c.name));
|
|
@@ -228,8 +228,8 @@ function EntityInspector({ entity }) {
|
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
230
|
function ComponentEditor({ entity, type }) {
|
|
231
|
-
const engine =
|
|
232
|
-
const value =
|
|
231
|
+
const engine = require_ecs.useLayoutEngine();
|
|
232
|
+
const value = require_ecs.useComponent(entity, type);
|
|
233
233
|
const [collapsed, setCollapsed] = (0, react.useState)(false);
|
|
234
234
|
if (!value) return null;
|
|
235
235
|
const isWidgetData = type.name === "WidgetData";
|
|
@@ -267,7 +267,7 @@ function ComponentEditor({ entity, type }) {
|
|
|
267
267
|
});
|
|
268
268
|
}
|
|
269
269
|
function GenericFieldsEditor({ entity, type, value }) {
|
|
270
|
-
const engine =
|
|
270
|
+
const engine = require_ecs.useLayoutEngine();
|
|
271
271
|
const defaults = type.defaults;
|
|
272
272
|
const val = value;
|
|
273
273
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: Object.keys(defaults).map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldRow, {
|
|
@@ -279,7 +279,7 @@ function GenericFieldsEditor({ entity, type, value }) {
|
|
|
279
279
|
}, key)) });
|
|
280
280
|
}
|
|
281
281
|
function WidgetDataEditor({ entity, value }) {
|
|
282
|
-
const engine =
|
|
282
|
+
const engine = require_ecs.useLayoutEngine();
|
|
283
283
|
const data = value.data ?? {};
|
|
284
284
|
const keys = Object.keys(data);
|
|
285
285
|
if (keys.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -290,7 +290,7 @@ function WidgetDataEditor({ entity, value }) {
|
|
|
290
290
|
label: key,
|
|
291
291
|
value: data[key],
|
|
292
292
|
onChange: (next) => {
|
|
293
|
-
engine.set(entity,
|
|
293
|
+
engine.set(entity, require_ecs.WidgetData, { data: {
|
|
294
294
|
...data,
|
|
295
295
|
[key]: next
|
|
296
296
|
} });
|
|
@@ -364,7 +364,7 @@ function JsonInput({ value, onChange }) {
|
|
|
364
364
|
});
|
|
365
365
|
}
|
|
366
366
|
function TagPill({ entity, type, active }) {
|
|
367
|
-
const engine =
|
|
367
|
+
const engine = require_ecs.useLayoutEngine();
|
|
368
368
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
369
369
|
type: "button",
|
|
370
370
|
className: `ic-ecs-tag ${active ? "is-active" : ""}`,
|