@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.
Files changed (56) hide show
  1. package/README.md +65 -0
  2. package/dist/advanced.cjs +61 -24
  3. package/dist/advanced.cjs.map +1 -1
  4. package/dist/advanced.d.cts +180 -64
  5. package/dist/advanced.d.cts.map +1 -1
  6. package/dist/advanced.d.mts +180 -64
  7. package/dist/advanced.d.mts.map +1 -1
  8. package/dist/advanced.mjs +29 -12
  9. package/dist/advanced.mjs.map +1 -1
  10. package/dist/devtools.cjs +22 -22
  11. package/dist/devtools.cjs.map +1 -1
  12. package/dist/devtools.d.cts +2 -2
  13. package/dist/devtools.d.cts.map +1 -1
  14. package/dist/devtools.d.mts +2 -2
  15. package/dist/devtools.d.mts.map +1 -1
  16. package/dist/devtools.mjs +2 -2
  17. package/dist/devtools.mjs.map +1 -1
  18. package/dist/{hooks-BwY7rRHg.mjs → ecs-3kimUV5Z.mjs} +238 -74
  19. package/dist/ecs-3kimUV5Z.mjs.map +1 -0
  20. package/dist/{hooks-DHShH86C.cjs → ecs-B4QrqfvQ.cjs} +320 -108
  21. package/dist/ecs-B4QrqfvQ.cjs.map +1 -0
  22. package/dist/hooks-CtP02JNt.cjs +3762 -0
  23. package/dist/hooks-CtP02JNt.cjs.map +1 -0
  24. package/dist/hooks-gsQDDE56.mjs +3494 -0
  25. package/dist/hooks-gsQDDE56.mjs.map +1 -0
  26. package/dist/index-3GY7T8JM.d.mts +480 -0
  27. package/dist/index-3GY7T8JM.d.mts.map +1 -0
  28. package/dist/index-B7B1tRPl.d.cts +480 -0
  29. package/dist/index-B7B1tRPl.d.cts.map +1 -0
  30. package/dist/index-DSdbSQ_t.d.cts +1451 -0
  31. package/dist/index-DSdbSQ_t.d.cts.map +1 -0
  32. package/dist/index-Dj9odADH.d.mts +1451 -0
  33. package/dist/index-Dj9odADH.d.mts.map +1 -0
  34. package/dist/index.cjs +3865 -643
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +315 -138
  37. package/dist/index.d.cts.map +1 -1
  38. package/dist/index.d.mts +315 -138
  39. package/dist/index.d.mts.map +1 -1
  40. package/dist/index.mjs +3767 -571
  41. package/dist/index.mjs.map +1 -1
  42. package/package.json +1 -1
  43. package/dist/SelectionRenderer-CR2PBQwx.d.cts +0 -105
  44. package/dist/SelectionRenderer-CR2PBQwx.d.cts.map +0 -1
  45. package/dist/SelectionRenderer-DlsBstAq.d.mts +0 -105
  46. package/dist/SelectionRenderer-DlsBstAq.d.mts.map +0 -1
  47. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs +0 -3560
  48. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs.map +0 -1
  49. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs +0 -3375
  50. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs.map +0 -1
  51. package/dist/engine-BfbvWXSk.d.mts +0 -982
  52. package/dist/engine-BfbvWXSk.d.mts.map +0 -1
  53. package/dist/engine-CCjuFMC-.d.cts +0 -982
  54. package/dist/engine-CCjuFMC-.d.cts.map +0 -1
  55. package/dist/hooks-BwY7rRHg.mjs.map +0 -1
  56. package/dist/hooks-DHShH86C.cjs.map +0 -1
@@ -1,99 +1,215 @@
1
- import { A as NavigationFrame, C as computeSnapGuides, G as FrameTimeStats, J as R3FSample, K as Profiler, L as R3FWidgetProps, Q as WebGLStats, W as EcsStats, X as TickSample, Y as R3FStats, Z as WebGLPass, l as SpatialIndex, n as LayoutEngine, o as SpatialIndexResource, q as ProfilerStats } from "./engine-BfbvWXSk.mjs";
2
- import { c as ContainerRefProvider, i as SelectionRenderer, l as EngineProvider, s as GridRenderer, u as ResolvedWidget } from "./SelectionRenderer-DlsBstAq.mjs";
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/react/SelectionOverlaySlot.d.ts
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 WebGL widgets — provides selection frame and pointer
14
- * interaction (select, drag, resize) without rendering widget content.
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/react/webgl/WebGLWidgetLayer.d.ts
32
- interface WebGLWidgetLayerProps {
33
- engine: LayoutEngine;
34
- entities: EntityId[];
35
- resolve: (entityId: EntityId) => ResolvedWidget | null;
36
- }
37
- declare function WebGLWidgetLayer({
38
- engine,
39
- entities,
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
- * Positions a Three.js Group at the entity's world-space center.
50
- * The widget component renders in local space: origin at center,
51
- * X right, Y up, dimensions = (width, height) in world units.
52
- */
53
- declare function WebGLWidgetSlot({
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
- /** A single serialized entity with its components and tags. */
73
- interface SerializedEntity {
74
- id: EntityId;
75
- components: Record<string, unknown>;
76
- tags: string[];
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
- * Serializes all entities, components, and tags to a JSON-compatible document.
80
- * Requires registries of known component and tag types for enumeration.
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 function serializeWorld(world: World, componentTypes: ComponentType[], tagTypes: TagType[], camera: {
83
- x: number;
84
- y: number;
85
- zoom: number;
86
- }, navigationFrames: NavigationFrame[]): CanvasDocument;
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
- * Restores entities from a serialized document into the world.
89
- * Clears existing state first and remaps entity IDs automatically.
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 deserializeWorld(world: World, doc: CanvasDocument, componentTypes: ComponentType[], tagTypes: TagType[]): void;
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
- * Serializes a subset of entities (e.g., for copy/paste).
94
- * Recursively includes children of the specified entities.
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 serializeEntities(world: World, entityIds: EntityId[], componentTypes: ComponentType[], tagTypes: TagType[]): SerializedEntity[];
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, WebGLWidgetLayer, WebGLWidgetSlot, WidgetSlot, computeSnapGuides, deserializeWorld, serializeEntities, serializeWorld };
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
@@ -1 +1 @@
1
- {"version":3,"file":"advanced.d.mts","names":[],"sources":["../src/react/SelectionOverlaySlot.tsx","../src/react/WidgetSlot.tsx","../src/react/webgl/WebGLWidgetLayer.tsx","../src/react/webgl/WebGLWidgetSlot.tsx","../src/serialization.ts"],"mappings":";;;;;;;UAMU,yBAAA;EACT,QAAA,EAAU,QAAA;EACV,OAAA,GAAU,QAAA,EAAU,QAAA,EAAU,EAAA,EAAI,cAAA;AAAA;;;AARuB;;cAmB7C,oBAAA,EAGe,OAAA,CAHK,mBAAA;EAAA,QAAA;EAAA;AAAA,GAG9B,yBAAA,KAAyB,oBAAA,CAAA,GAAA,CAAA,OAAA;;;UCflB,eAAA;EACT,QAAA,EAAU,QAAA;EACV,OAAA,GAAU,QAAA,EAAU,QAAA,EAAU,EAAA,EAAI,cAAA;AAAA;AAAA,cAOtB,UAAA,EAA4E,OAAA,CAAlE,mBAAA;EAAA,QAAA;EAAA;AAAA,GAAmD,eAAA,KAAe,oBAAA,CAAA,GAAA,CAAA,OAAA;;;UCgG/E,qBAAA;EACT,MAAA,EAAQ,YAAA;EACR,QAAA,EAAU,QAAA;EACV,OAAA,GAAU,QAAA,EAAU,QAAA,KAAa,cAAA;AAAA;AAAA,iBAGlB,gBAAA,CAAA;EAAmB,MAAA;EAAQ,QAAA;EAAU;AAAA,GAAW,qBAAA,GAAqB,oBAAA,CAAA,GAAA,CAAA,OAAA;;;UC7G3E,oBAAA;EACT,QAAA,EAAU,QAAA;EACV,SAAA,EAAW,KAAA,CAAM,aAAA,CAAc,cAAA;AAAA;;AHX0B;;;;iBGmB1C,eAAA,CAAA;EAAkB,QAAA;EAAU,SAAA,EAAW;AAAA,GAAmB,oBAAA,GAAoB,oBAAA,CAAA,GAAA,CAAA,OAAA;;;;UCZ7E,cAAA;EAChB,OAAA;EACA,QAAA,EAAU,gBAAA;EACV,SAAA;IACC,MAAA;MAAU,CAAA;MAAW,CAAA;MAAW,IAAA;IAAA;IAChC,eAAA,EAAiB,eAAA;EAAA;AAAA;;UAKF,gBAAA;EAChB,EAAA,EAAI,QAAA;EACJ,UAAA,EAAY,MAAA;EACZ,IAAA;AAAA;;;;AJDD;iBIUgB,cAAA,CACf,KAAA,EAAO,KAAA,EACP,cAAA,EAAgB,aAAA,IAChB,QAAA,EAAU,OAAA,IACV,MAAA;EAAU,CAAA;EAAW,CAAA;EAAW,IAAA;AAAA,GAChC,gBAAA,EAAkB,eAAA,KAChB,cAAA;;;;;iBA6Ca,gBAAA,CACf,KAAA,EAAO,KAAA,EACP,GAAA,EAAK,cAAA,EACL,cAAA,EAAgB,aAAA,IAChB,QAAA,EAAU,OAAA;;;;;iBAsEK,iBAAA,CACf,KAAA,EAAO,KAAA,EACP,SAAA,EAAW,QAAA,IACX,cAAA,EAAgB,aAAA,IAChB,QAAA,EAAU,OAAA,KACR,gBAAA"}
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 { c as SelectionOverlaySlot, d as SpatialIndex, f as computeSnapGuides, h as Profiler, i as SelectionRenderer, l as SpatialIndexResource, n as WebGLWidgetSlot, o as GridRenderer, s as WidgetSlot, t as WebGLWidgetLayer } from "./WebGLWidgetLayer-C3p1tnpm.mjs";
2
- import { D as Children, I as Parent, M as HandleSet, f as ContainerRefProvider, p as EngineProvider } from "./hooks-BwY7rRHg.mjs";
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, navigationFrames) {
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
- navigationStack: structuredClone(navigationFrames)
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, Parent);
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, Parent, { id: mappedId });
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 handleSet = world.getComponent(newId, HandleSet);
69
- if (handleSet) world.setComponent(newId, HandleSet, { ids: handleSet.ids.map((id) => idMap.get(id) ?? id) });
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, WebGLWidgetLayer, WebGLWidgetSlot, WidgetSlot, computeSnapGuides, deserializeWorld, serializeEntities, serializeWorld };
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
@@ -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":";;;;;;;AAwCA,SAAgB,eACf,OACA,gBACA,UACA,QACA,YACiB;CACjB,MAAM,WAA+B,EAAE;CAGvC,MAAM,cAAc,MAAM,OAAO;AAEjC,MAAK,MAAM,YAAY,aAAa;EACnC,MAAM,aAAsC,EAAE;EAC9C,MAAM,OAAiB,EAAE;AAEzB,OAAK,MAAM,QAAQ,gBAAgB;AAOlC,OACC,KAAK,SAAS,kBACd,KAAK,SAAS,oBACd,KAAK,SAAS,sBAEd;GAED,MAAM,OAAO,MAAM,aAAa,UAAU,KAAK;AAC/C,OAAI,SAAS,KAAA,EACZ,YAAW,KAAK,QAAQ,gBAAgB,KAAK;;AAI/C,OAAK,MAAM,QAAQ,SAClB,KAAI,MAAM,OAAO,UAAU,KAAK;OAK9B,KAAK,SAAS,YACd,KAAK,SAAS,aACd,KAAK,SAAS,YACd,KAAK,SAAS,sBACd,KAAK,SAAS,gBAEd,MAAK,KAAK,KAAK,KAAK;;AAKvB,MAAI,OAAO,KAAK,WAAW,CAAC,SAAS,KAAK,KAAK,SAAS,EACvD,UAAS,KAAK;GAAE,IAAI;GAAU;GAAY;GAAM,CAAC;;AAInD,QAAO;EACN,SAAS;EACT;EACA,WAAW;GACV,QAAQ,EAAE,GAAG,QAAQ;GACrB,YAAY,EAAE,GAAG,YAAY;GAC7B;EACD;;;;;;AAOF,SAAgB,iBACf,OACA,KACA,gBACA,UACO;AACP,KAAI,IAAI,YAAY,EACnB,OAAM,IAAI,MAAM,wCAAwC,IAAI,QAAQ,uBAAuB;CAI5F,MAAM,6BAAa,IAAI,KAA4B;AACnD,MAAK,MAAM,KAAK,eAAgB,YAAW,IAAI,EAAE,MAAM,EAAE;CAEzD,MAAM,4BAAY,IAAI,KAAsB;AAC5C,MAAK,MAAM,KAAK,SAAU,WAAU,IAAI,EAAE,MAAM,EAAE;AAGlD,MAAK,MAAM,YAAY,MAAM,OAAO,CACnC,OAAM,cAAc,SAAS;CAI9B,MAAM,wBAAQ,IAAI,KAAyB;AAE3C,MAAK,MAAM,SAAS,IAAI,UAAU;EACjC,MAAM,QAAQ,MAAM,cAAc;AAClC,QAAM,IAAI,MAAM,IAAgB,MAAM;AAEtC,OAAK,MAAM,CAAC,UAAU,SAAS,OAAO,QAAQ,MAAM,WAAW,EAAE;GAChE,MAAM,OAAO,WAAW,IAAI,SAAS;AACrC,OAAI,KACH,OAAM,aAAa,OAAO,MAAM,KAAK;;AAIvC,OAAK,MAAM,WAAW,MAAM,MAAM;GACjC,MAAM,OAAO,UAAU,IAAI,QAAQ;AACnC,OAAI,KACH,OAAM,OAAO,OAAO,KAAK;;;AAO5B,MAAK,MAAM,CAAC,QAAQ,UAAU,OAAO;EACpC,MAAM,SAAS,MAAM,aAAa,OAAO,YAAY;AACrD,MAAI,UAAU,MAAM,IAAI,OAAO,GAAG,EAAE;GACnC,MAAM,WAAW,MAAM,IAAI,OAAO,GAAG;AACrC,OAAI,aAAa,KAAA,EAChB,OAAM,aAAa,OAAO,aAAa,EAAE,IAAI,UAAU,CAAC;;EAI1D,MAAM,WAAW,MAAM,aAAa,OAAO,SAAS;AACpD,MAAI,SACH,OAAM,aAAa,OAAO,UAAU,EACnC,KAAK,SAAS,IAAI,KAAK,OAAiB,MAAM,IAAI,GAAG,IAAI,GAAG,EAC5D,CAAC;EAGH,MAAM,oBAAoB,MAAM,aAAa,OAAO,kBAAkB;AACtE,MAAI,mBAAmB;GAMtB,MAAM,SAAqB,EAAE;AAC7B,QAAK,MAAM,MAAM,kBAAkB,KAAK;IACvC,MAAM,WAAW,MAAM,IAAI,GAAG;AAC9B,QAAI,aAAa,KAAA,EAAW,QAAO,KAAK,SAAS;;AAElD,SAAM,aAAa,OAAO,mBAAmB,EAAE,KAAK,QAAQ,CAAC;;;CAM/D,MAAM,OAAO,IAAI,UAAU;AAC3B,OAAM,YAAY,gBAAgB;EAAE,GAAG,KAAK;EAAG,GAAG,KAAK;EAAG,MAAM,KAAK;EAAM,WAAW;EAAO,CAAC;AAC9F,OAAM,YAAY,oBAAoB,EAAE,GAAG,IAAI,UAAU,YAAY,CAAC;;;;;;AAUvE,SAAgB,kBACf,OACA,WACA,gBACA,UACqB;CACrB,MAAM,SAA6B,EAAE;CACrC,MAAM,0BAAU,IAAI,KAAe;CAEnC,SAAS,MAAM,UAAoB;AAClC,MAAI,QAAQ,IAAI,SAAS,CAAE;AAC3B,UAAQ,IAAI,SAAS;EAErB,MAAM,aAAsC,EAAE;EAC9C,MAAM,OAAiB,EAAE;AAEzB,OAAK,MAAM,QAAQ,gBAAgB;AAOlC,OACC,KAAK,SAAS,kBACd,KAAK,SAAS,oBACd,KAAK,SAAS,sBAEd;GAED,MAAM,OAAO,MAAM,aAAa,UAAU,KAAK;AAC/C,OAAI,SAAS,KAAA,EACZ,YAAW,KAAK,QAAQ,gBAAgB,KAAK;;AAI/C,OAAK,MAAM,QAAQ,SAClB,KAAI,MAAM,OAAO,UAAU,KAAK;OAC3B,KAAK,SAAS,YAAY,KAAK,SAAS,aAAa,KAAK,SAAS,SACtE,MAAK,KAAK,KAAK,KAAK;;AAKvB,SAAO,KAAK;GAAE,IAAI;GAAU;GAAY;GAAM,CAAC;EAI/C,MAAM,WAAW,WAAW;AAC5B,MAAI,UAAU,IACb,MAAK,MAAM,WAAW,SAAS,IAC9B,OAAM,QAAQ;;AAKjB,MAAK,MAAM,MAAM,UAChB,OAAM,GAAG;AAGV,QAAO"}
package/dist/devtools.cjs CHANGED
@@ -1,24 +1,24 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("./WebGLWidgetLayer-BBMuwzHq.cjs");
3
- const require_hooks = require("./hooks-DHShH86C.cjs");
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/react/devtools/EcsDevtools.tsx
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)(require_hooks.EngineProvider, {
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 = require_hooks.useLayoutEngine();
20
- const allEntities = require_hooks.useAllEntities();
21
- const selectedIds = require_hooks.useTaggedEntities(require_hooks.Selected);
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, require_hooks.Widget));
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 = require_hooks.useLayoutEngine();
120
- const label = require_hooks.useComponent(entity, require_hooks.Widget)?.type ?? "entity";
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 = require_hooks.useLayoutEngine();
148
- const components = require_hooks.useEntityComponents(entity);
149
- const tags = require_hooks.useEntityTags(entity);
150
- const registeredComponents = require_hooks.useRegisteredComponents();
151
- const registeredTags = require_hooks.useRegisteredTags();
152
- const widget = require_hooks.useComponent(entity, require_hooks.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 = require_hooks.useLayoutEngine();
232
- const value = require_hooks.useComponent(entity, type);
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 = require_hooks.useLayoutEngine();
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 = require_hooks.useLayoutEngine();
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, require_hooks.WidgetData, { data: {
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 = require_hooks.useLayoutEngine();
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" : ""}`,