@jamesyong42/infinite-canvas 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +170 -9
  2. package/dist/SelectionRenderer-CR2PBQwx.d.cts +105 -0
  3. package/dist/SelectionRenderer-CR2PBQwx.d.cts.map +1 -0
  4. package/dist/SelectionRenderer-DlsBstAq.d.mts +105 -0
  5. package/dist/SelectionRenderer-DlsBstAq.d.mts.map +1 -0
  6. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs +3560 -0
  7. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs.map +1 -0
  8. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs +3375 -0
  9. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs.map +1 -0
  10. package/dist/advanced.cjs +110 -165
  11. package/dist/advanced.cjs.map +1 -1
  12. package/dist/advanced.d.cts +58 -40
  13. package/dist/advanced.d.cts.map +1 -0
  14. package/dist/advanced.d.mts +99 -0
  15. package/dist/advanced.d.mts.map +1 -0
  16. package/dist/advanced.mjs +105 -0
  17. package/dist/advanced.mjs.map +1 -0
  18. package/dist/devtools.cjs +654 -0
  19. package/dist/devtools.cjs.map +1 -0
  20. package/dist/devtools.d.cts +23 -0
  21. package/dist/devtools.d.cts.map +1 -0
  22. package/dist/devtools.d.mts +23 -0
  23. package/dist/devtools.d.mts.map +1 -0
  24. package/dist/devtools.mjs +652 -0
  25. package/dist/devtools.mjs.map +1 -0
  26. package/dist/engine-BfbvWXSk.d.mts +982 -0
  27. package/dist/engine-BfbvWXSk.d.mts.map +1 -0
  28. package/dist/engine-CCjuFMC-.d.cts +982 -0
  29. package/dist/engine-CCjuFMC-.d.cts.map +1 -0
  30. package/dist/hooks-BwY7rRHg.mjs +425 -0
  31. package/dist/hooks-BwY7rRHg.mjs.map +1 -0
  32. package/dist/hooks-DHShH86C.cjs +707 -0
  33. package/dist/hooks-DHShH86C.cjs.map +1 -0
  34. package/dist/index.cjs +909 -803
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +199 -67
  37. package/dist/index.d.cts.map +1 -0
  38. package/dist/index.d.mts +258 -0
  39. package/dist/index.d.mts.map +1 -0
  40. package/dist/index.mjs +855 -0
  41. package/dist/index.mjs.map +1 -0
  42. package/package.json +47 -15
  43. package/dist/SelectionRenderer-CeWSNZT8.d.cts +0 -891
  44. package/dist/SelectionRenderer-CeWSNZT8.d.ts +0 -891
  45. package/dist/advanced.d.ts +0 -81
  46. package/dist/advanced.js +0 -124
  47. package/dist/advanced.js.map +0 -1
  48. package/dist/chunk-VSHXWTJH.cjs +0 -3228
  49. package/dist/chunk-VSHXWTJH.cjs.map +0 -1
  50. package/dist/chunk-Z6JQQOWL.js +0 -3142
  51. package/dist/chunk-Z6JQQOWL.js.map +0 -1
  52. package/dist/index.d.ts +0 -126
  53. package/dist/index.js +0 -602
  54. package/dist/index.js.map +0 -1
package/README.md CHANGED
@@ -17,7 +17,9 @@ Build Figma-style infinite canvases in React -- drag, resize, snap, zoom, nested
17
17
  - **Undo / redo** -- Command buffer with grouped operations (an entire drag is one undo step)
18
18
  - **Hierarchical navigation** -- Enter and exit nested containers with camera state preservation
19
19
  - **ECS architecture** -- Extensible via custom components, tags, and systems with topologically-sorted scheduling
20
+ - **Card widgets** -- iOS-style preset-sized tiles (small / medium / large / xl) with rounded corners, soft shadows, and lift-on-drag animation; DOM or R3F/PBR flavors via `createCardWidget` / `createGeometryCardWidget`
20
21
  - **Performance** -- SDF shaders for grid and selection chrome, RBush spatial indexing, viewport culling, per-system profiling
22
+ - **Live ECS editor** -- Drop-in `<EcsDevtools>` panel for spawning, inspecting, and editing components and tags at runtime
21
23
  - **Dark mode** -- Full dark mode support across canvas, widgets, and UI chrome
22
24
 
23
25
  ## Quick Start
@@ -68,12 +70,13 @@ Widgets declare a **schema** (any [Standard Schema v1](https://standardschema.de
68
70
 
69
71
  ## Package
70
72
 
71
- Everything ships in a single package: **`@jamesyong42/infinite-canvas`**. It exposes two entry points:
73
+ Everything ships in a single package: **`@jamesyong42/infinite-canvas`**. It exposes three entry points:
72
74
 
73
75
  | Import | Purpose |
74
76
  |--------|---------|
75
77
  | `@jamesyong42/infinite-canvas` | Main API -- `<InfiniteCanvas>`, `createLayoutEngine`, hooks, built-in components |
76
78
  | `@jamesyong42/infinite-canvas/advanced` | WebGL renderers, serialization, profiler, spatial index |
79
+ | `@jamesyong42/infinite-canvas/devtools` | `<EcsDevtools>` live ECS editor (see [Devtools](#devtools)) |
77
80
 
78
81
  The underlying ECS primitives (`defineComponent`, `defineSystem`, `World`, `SystemScheduler`) live in a separate package: [**`@jamesyong42/reactive-ecs`**](https://github.com/jamesyong-42/reactive-ecs).
79
82
 
@@ -103,6 +106,11 @@ The underlying ECS primitives (`defineComponent`, `defineSystem`, `World`, `Syst
103
106
  | `useQuery(...types)` | Entity IDs matching component/tag types |
104
107
  | `useTaggedEntities(type)` | All entity IDs with a specific tag |
105
108
  | `useResource<T>(type)` | Read an ECS resource reactively |
109
+ | `useAllEntities()` | Every live entity ID (reactive on create/destroy) |
110
+ | `useEntityComponents(entityId)` | `ComponentType[]` currently on an entity |
111
+ | `useEntityTags(entityId)` | `TagType[]` currently on an entity |
112
+ | `useRegisteredComponents()` | Every `ComponentType` the world has observed |
113
+ | `useRegisteredTags()` | Every `TagType` the world has observed |
106
114
  | `useLayoutEngine()` | Access the `LayoutEngine` instance from context |
107
115
 
108
116
  ### InfiniteCanvas Props
@@ -208,6 +216,97 @@ Spawning is uniform: `engine.spawn(id, options)`. If `id` matches an archetype,
208
216
  | `parent` | Parent entity id for nesting. |
209
217
  | `rotation` | Initial rotation in radians. |
210
218
 
219
+ The `interactive` field on `Archetype` accepts a boolean or an object. Pass `{ selectable, draggable, resizable, selectionFrame }` (any subset) when you want finer control. Cards that move, select, but never resize and render their own chrome: `{ selectable: true, draggable: true, resizable: false, selectionFrame: false }`. Omitted keys default to `false`, except `selectionFrame` which follows `selectable` (an entity you can select gets a frame unless you opt out).
220
+
221
+ ## Card Widgets
222
+
223
+ **Card widgets** are fixed-size, non-resizable widgets that sit on an iOS-style preset grid. They ship with rounded corners, a soft drop shadow, a hairline ring, and a subtle lift animation while dragging. Use them when you want dashboard-style tiles rather than free-form resizable surfaces.
224
+
225
+ ```tsx
226
+ import { createCardWidget } from '@jamesyong42/infinite-canvas';
227
+ import { z } from 'zod';
228
+
229
+ const schema = z.object({ label: z.string().default('Hi') });
230
+
231
+ export const Greeting = createCardWidget<{ label: string }>({
232
+ type: 'greeting-card',
233
+ size: 'small', // 'small' | 'medium' | 'large' | 'xl'
234
+ schema,
235
+ defaultData: { label: 'Hi' },
236
+ render: ({ data }) => (
237
+ <div className="flex h-full w-full items-center justify-center bg-white">
238
+ {data.label}
239
+ </div>
240
+ ),
241
+ });
242
+
243
+ // Register both the widget and the matching archetype.
244
+ const engine = createLayoutEngine({
245
+ widgets: [Greeting.widget],
246
+ archetypes: [Greeting.archetype],
247
+ });
248
+ engine.spawn('greeting-card', { at: { x: 50, y: 50 } });
249
+ ```
250
+
251
+ Preset sizes (default, matching iOS widget conventions on a 19 px grid):
252
+
253
+ | Preset | Width × Height |
254
+ |--------|----------------|
255
+ | `small` | 155 × 155 |
256
+ | `medium` | 329 × 155 |
257
+ | `large` | 329 × 345 |
258
+ | `xl` | 329 × 535 |
259
+
260
+ Override per-engine via `createLayoutEngine({ cardPresets: { presets: { small: { width: 200, height: 200 } }, gap: 24 } })`. Omitted presets keep their defaults.
261
+
262
+ Under the hood: the returned widget wraps your `render` in `<CardFrame>` (exported for manual composition), the archetype is non-resizable, and it bundles a `Card` component. A built-in `cardSystem` stamps `Transform2D.width/height` from `Card.preset` each tick — to change a card's size at runtime, update the preset: `engine.set(id, Card, { preset: 'large' })`. Reading the `Dragging` tag from the frame drives the lift affordance; you can read it elsewhere too via `useTag(entityId, Dragging)`.
263
+
264
+ Cards also opt out of the engine-drawn selection + hover outline (`selectionFrame: false` in their archetype) — the iOS rounded chrome in `<CardFrame>` is the card's visual contract, so the standard blue frame would fight it. If you need a selected/hover affordance inside a card, read `useIsSelected(entityId)` / `useTag(entityId, /* Hovered tag */)` from within `render` and style accordingly.
265
+
266
+ ### 3D Card Widgets
267
+
268
+ `createGeometryCardWidget` is the R3F counterpart — same preset sizes, non-resizable archetype, and drag-lift behavior, but the widget body is a three.js scene instead of DOM content. The helper pairs cleanly with PBR materials.
269
+
270
+ ```tsx
271
+ import { createGeometryCardWidget } from '@jamesyong42/infinite-canvas';
272
+ import { useFrame } from '@react-three/fiber';
273
+ import { useRef } from 'react';
274
+ import type { Mesh } from 'three';
275
+ import { z } from 'zod';
276
+
277
+ const schema = z.object({ color: z.string().default('#F5B8D0') });
278
+
279
+ export const Sphere = createGeometryCardWidget<{ color: string }>({
280
+ type: 'sphere',
281
+ size: 'small',
282
+ schema,
283
+ defaultData: { color: '#F5B8D0' },
284
+ background: 'card', // or 'transparent' — the geometry floats over the canvas
285
+ geometry: ({ data, width }) => {
286
+ const ref = useRef<Mesh>(null);
287
+ useFrame((_, dt) => { if (ref.current) ref.current.rotation.y += dt * 0.3; });
288
+ return (
289
+ <group>
290
+ <pointLight position={[80, 80, 120]} intensity={160} distance={300} decay={1.4} />
291
+ <ambientLight intensity={0.3} />
292
+ <mesh ref={ref}>
293
+ <sphereGeometry args={[width * 0.32, 48, 48]} />
294
+ <meshStandardMaterial color={data.color} roughness={0.35} />
295
+ </mesh>
296
+ </group>
297
+ );
298
+ },
299
+ });
300
+ ```
301
+
302
+ `background` options:
303
+
304
+ - `'card'` (default) — a rounded iOS-style card mesh sits behind the geometry in the same widget group.
305
+ - `'transparent'` — no back plane; the geometry floats over whatever's behind the widget.
306
+ - `{ color, roughness?, metalness? }` — card back with custom PBR parameters for tinted or glossy variants.
307
+
308
+ **Lighting caveat.** All R3F widgets share a single `<Canvas>`, so lights and `envMap`s you declare inside one widget's render function affect every other 3D widget. Keep per-widget lights `pointLight`s with `distance` scoped to the widget's size, or add one shared `<Environment>` at the app level (if you control the R3F canvas). The helper itself adds no lights — declare what you need inside your `geometry` component.
309
+
211
310
  ## WebGL Widgets (R3F)
212
311
 
213
312
  Define an `R3FWidget<T>` with `surface: 'webgl'` to render 3D content via React Three Fiber. R3F widget views receive `{ entityId, width, height }` and render in local coordinates (origin at widget centre):
@@ -333,6 +432,42 @@ deserializeWorld(engine.world, saved, componentTypes, tagTypes);
333
432
  engine.markDirty();
334
433
  ```
335
434
 
435
+ ## Devtools
436
+
437
+ A live ECS editor ships in `@jamesyong42/infinite-canvas/devtools`. Drop it in during development to spawn widgets, inspect entities, edit components, and toggle tags at runtime — FLECS Explorer-style, but driven by the live React tree.
438
+
439
+ ```tsx
440
+ import { InfiniteCanvas } from '@jamesyong42/infinite-canvas';
441
+ import { EcsDevtools } from '@jamesyong42/infinite-canvas/devtools';
442
+
443
+ function App() {
444
+ const engine = useMemo(() => createLayoutEngine({ widgets: [MyWidget] }), []);
445
+ const [showDevtools, setShowDevtools] = useState(false);
446
+
447
+ return (
448
+ <>
449
+ <InfiniteCanvas engine={engine} />
450
+ {showDevtools && <EcsDevtools engine={engine} onClose={() => setShowDevtools(false)} />}
451
+ </>
452
+ );
453
+ }
454
+ ```
455
+
456
+ What the panel does:
457
+
458
+ - **Spawn** any registered widget at the current viewport centre.
459
+ - **List** all widget entities (or all entities with `show all`).
460
+ - **Inspect** the canvas-selected entity: its components and tags.
461
+ - **Edit** component fields inline — primitive types get typed inputs, everything else falls back to a JSON input. `WidgetData.data` is edited field-by-field.
462
+ - **Add / remove** components and toggle tags without leaving the canvas.
463
+ - **Destroy** entities.
464
+
465
+ Pass `engine` as a prop when the devtools render outside the `<InfiniteCanvas>` subtree (the usual case, since the panel is typically absolute-positioned above the canvas). If rendered inside, the prop is optional — it reads from context.
466
+
467
+ Styling is self-contained (a single scoped `<style>` injected once, classnames prefixed `ic-ecs-`). Dark mode is auto via `prefers-color-scheme` or an ancestor `.dark` class. No stylesheet import required.
468
+
469
+ The devtools consume the same introspection primitives (`useAllEntities`, `useEntityComponents`, `useRegisteredComponents`, etc.) that are exported from the main entry point, so you can build your own inspector UI on top of them if you need something bespoke.
470
+
336
471
  ## Programmatic Control
337
472
 
338
473
  ### Camera
@@ -353,6 +488,27 @@ engine.redo();
353
488
  engine.markDirty();
354
489
  ```
355
490
 
491
+ ### Spawning & ECS mutation
492
+
493
+ Runtime spawning and component edits go through the engine so it can cascade handles and mark dirty in one step:
494
+
495
+ ```tsx
496
+ // Spawn at the viewport centre (sized from the widget/archetype default)
497
+ const id = engine.spawnAtCameraCenter('my-widget');
498
+
499
+ // Component mutation
500
+ engine.addComponent(id, Container, { enterable: true });
501
+ engine.removeComponent(id, Container);
502
+ engine.set(id, Transform2D, { x: 200 }); // partial merge
503
+ engine.addTag(id, Selected);
504
+ engine.removeTag(id, Draggable);
505
+
506
+ // Widget-aware introspection
507
+ engine.getSchemaFor(id); // Standard Schema for the widget's data, if declared
508
+ ```
509
+
510
+ All of these mark the engine dirty internally — no separate `markDirty` call needed.
511
+
356
512
  ### Imperative Handle
357
513
 
358
514
  Use a ref on `<InfiniteCanvas>` for imperative control from outside:
@@ -461,20 +617,25 @@ z:3 UI chrome
461
617
  | `InteractionRole` | Interaction behavior (drag, select, resize, etc.) |
462
618
  | `HandleSet` | Child handle entity references |
463
619
  | `CursorHint` | Cursor style on hover/active |
620
+ | `Card` | Marks an iOS-style card; carries the preset (`small`/`medium`/`large`/`xl`) |
464
621
 
465
622
  ### ECS Tags
466
623
 
467
- `Selectable` `Draggable` `Resizable` `Locked` `Selected` `Active` `Visible`
624
+ `Selectable` `Draggable` `Resizable` `SelectionFrame` `Locked` `Selected` `Dragging` `Active` `Visible`
625
+
626
+ - `Dragging` — transient state tag (parallels `Selected`): added when the drag dead zone is crossed, removed on pointer up or cancel. Read via `useTag(entityId, Dragging)` to drive drag-time affordances.
627
+ - `SelectionFrame` — opts an entity into the engine-drawn selection + hover outline. Granted automatically to Selectable entities via the archetype's `interactive` caps; widgets that render their own chrome (e.g. cards) opt out.
468
628
 
469
629
  ### Systems (execution order)
470
630
 
471
- 1. `transformPropagate` -- Propagate transforms down hierarchy, compute WorldBounds
472
- 2. `handleSync` -- Synchronize resize handle entities with parent widgets
473
- 3. `hitboxWorldBounds` -- Compute world-space hitbox bounds
474
- 4. `navigationFilter` -- Filter entities to active navigation layer
475
- 5. `cull` -- Mark viewport-visible entities
476
- 6. `breakpoint` -- Compute responsive breakpoints
477
- 7. `sort` -- Z-index ordering
631
+ 1. `card` -- Stamp Transform2D size from Card preset
632
+ 2. `transformPropagate` -- Propagate transforms down hierarchy, compute WorldBounds
633
+ 3. `handleSync` -- Synchronize resize handle entities with parent widgets
634
+ 4. `hitboxWorldBounds` -- Compute world-space hitbox bounds
635
+ 5. `navigationFilter` -- Filter entities to active navigation layer
636
+ 6. `cull` -- Mark viewport-visible entities
637
+ 7. `breakpoint` -- Compute responsive breakpoints
638
+ 8. `sort` -- Z-index ordering
478
639
 
479
640
  ## Performance Profiling
480
641
 
@@ -0,0 +1,105 @@
1
+ import { F as DomWidgetProps, L as R3FWidgetProps, b as EqualSpacingIndicator, n as LayoutEngine, x as SnapGuide } from "./engine-CCjuFMC-.cjs";
2
+ import { EntityId } from "@jamesyong42/reactive-ecs";
3
+ import * as _$react from "react";
4
+ import * as THREE from "three";
5
+
6
+ //#region src/react/context.d.ts
7
+ declare const EngineProvider: _$react.Provider<LayoutEngine | null>;
8
+ declare const ContainerRefProvider: _$react.Provider<_$react.RefObject<HTMLDivElement | null> | null>;
9
+ declare function useContainerRef(): React.RefObject<HTMLDivElement | null> | null;
10
+ /**
11
+ * Returns the LayoutEngine instance from the nearest InfiniteCanvas context.
12
+ * Throws if used outside an InfiniteCanvas provider.
13
+ */
14
+ declare function useLayoutEngine(): LayoutEngine;
15
+ /**
16
+ * Discriminated resolution of a widget by type. The surface determines which
17
+ * layer renders the component and with what prop shape.
18
+ */
19
+ type ResolvedWidget = {
20
+ surface: 'dom';
21
+ component: React.ComponentType<DomWidgetProps>;
22
+ } | {
23
+ surface: 'webgl';
24
+ component: React.ComponentType<R3FWidgetProps>;
25
+ };
26
+ type WidgetResolver = (entityId: EntityId, widgetType: string) => ResolvedWidget | null;
27
+ declare const WidgetResolverProvider: _$react.Provider<WidgetResolver | null>;
28
+ declare function useWidgetResolver(): WidgetResolver | null;
29
+ //#endregion
30
+ //#region src/react/webgl/GridRenderer.d.ts
31
+ interface GridConfig {
32
+ /** World-unit spacings for up to 3 grid levels [fine, medium, coarse]. */
33
+ spacings: [number, number, number];
34
+ /** Dot RGB color as [r, g, b] in 0–1 range. */
35
+ dotColor: [number, number, number];
36
+ /** Base dot opacity multiplier (0–1). */
37
+ dotAlpha: number;
38
+ /** CSS-pixel range where a grid level fades in: [start, end]. */
39
+ fadeIn: [number, number];
40
+ /** CSS-pixel range where a grid level fades out: [start, end]. */
41
+ fadeOut: [number, number];
42
+ /** Dot radius range in CSS pixels [min, max]. Scaled by DPR internally. */
43
+ dotRadius: [number, number];
44
+ /** Per-level opacity weight: level i gets (base + i * step). */
45
+ levelWeight: [number, number];
46
+ }
47
+ declare const DEFAULT_GRID_CONFIG: GridConfig;
48
+ declare class GridRenderer {
49
+ private renderer;
50
+ private scene;
51
+ private camera;
52
+ private material;
53
+ private mesh;
54
+ constructor(canvas: HTMLCanvasElement);
55
+ /** Apply a (partial) grid config. Only provided fields are updated. */
56
+ setConfig(config: Partial<GridConfig>): void;
57
+ setSize(width: number, height: number, dpr?: number): void;
58
+ render(cameraX: number, cameraY: number, zoom: number): void;
59
+ dispose(): void;
60
+ /** Expose for future WebGL widget rendering */
61
+ getWebGLRenderer(): THREE.WebGLRenderer;
62
+ }
63
+ //#endregion
64
+ //#region src/react/webgl/SelectionRenderer.d.ts
65
+ interface SelectionConfig {
66
+ /** Selection outline color [r,g,b] 0-1. Default: Figma blue. */
67
+ outlineColor: [number, number, number];
68
+ /** Selection outline width in screen px. */
69
+ outlineWidth: number;
70
+ /** Hover outline color [r,g,b] 0-1. */
71
+ hoverColor: [number, number, number];
72
+ /** Hover outline width in screen px. */
73
+ hoverWidth: number;
74
+ /** Handle size in screen px. */
75
+ handleSize: number;
76
+ /** Handle fill color [r,g,b] 0-1 (white). */
77
+ handleFill: [number, number, number];
78
+ /** Handle border color [r,g,b] 0-1 (same as outline). */
79
+ handleBorder: [number, number, number];
80
+ /** Handle border width in screen px. */
81
+ handleBorderWidth: number;
82
+ /** Group bbox dash length in screen px (0 = solid). */
83
+ groupDash: number;
84
+ }
85
+ declare const DEFAULT_SELECTION_CONFIG: SelectionConfig;
86
+ interface SelectionBounds {
87
+ x: number;
88
+ y: number;
89
+ width: number;
90
+ height: number;
91
+ }
92
+ declare class SelectionRenderer {
93
+ private material;
94
+ private mesh;
95
+ private scene;
96
+ private camera;
97
+ constructor();
98
+ setConfig(config: Partial<SelectionConfig>): void;
99
+ setSize(resolution: THREE.Vector2, dpr: number): void;
100
+ render(renderer: THREE.WebGLRenderer, cameraX: number, cameraY: number, zoom: number, selected: SelectionBounds[], hovered: SelectionBounds | null, guides?: SnapGuide[], spacings?: EqualSpacingIndicator[]): void;
101
+ dispose(): void;
102
+ }
103
+ //#endregion
104
+ export { DEFAULT_GRID_CONFIG as a, ContainerRefProvider as c, WidgetResolverProvider as d, useContainerRef as f, SelectionRenderer as i, EngineProvider as l, useWidgetResolver as m, SelectionBounds as n, GridConfig as o, useLayoutEngine as p, SelectionConfig as r, GridRenderer as s, DEFAULT_SELECTION_CONFIG as t, ResolvedWidget as u };
105
+ //# sourceMappingURL=SelectionRenderer-CR2PBQwx.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectionRenderer-CR2PBQwx.d.cts","names":[],"sources":["../src/react/context.ts","../src/react/webgl/GridRenderer.ts","../src/react/webgl/SelectionRenderer.ts"],"mappings":";;;;;;cASa,cAAA,EAAc,OAAA,CAAA,QAAA,CAAA,YAAA;AAAA,cAOd,oBAAA,EAAoB,OAAA,CAAA,QAAA,CAAA,OAAA,CAAA,SAAA,CAAA,cAAA;AAAA,iBAEjB,eAAA,CAAA,GAAmB,KAAA,CAAM,SAAA,CAAU,cAAA;AATnD;;;;AAAA,iBAiBgB,eAAA,CAAA,GAAmB,YAAA;;;;;KAgBvB,cAAA;EACP,OAAA;EAAgB,SAAA,EAAW,KAAA,CAAM,aAAA,CAAc,cAAA;AAAA;EAC/C,OAAA;EAAkB,SAAA,EAAW,KAAA,CAAM,aAAA,CAAc,cAAA;AAAA;AAAA,KAE1C,cAAA,IAAkB,QAAA,EAAU,QAAA,EAAU,UAAA,aAAuB,cAAA;AAAA,cAI5D,sBAAA,EAAsB,OAAA,CAAA,QAAA,CAAA,cAAA;AAAA,iBAEnB,iBAAA,CAAA,GAAqB,cAAA;;;UChDpB,UAAA;;EAEhB,QAAA;;EAEA,QAAA;EDCY;ECCZ,QAAA;;EAEA,MAAA;EDH0B;ECK1B,OAAA;EDE+D;ECA/D,SAAA;EDAgC;ECEhC,WAAA;AAAA;AAAA,cAGY,mBAAA,EAAqB,UAAA;AAAA,cAoFrB,YAAA;EAAA,QACJ,QAAA;EAAA,QACA,KAAA;EAAA,QACA,MAAA;EAAA,QACA,QAAA;EAAA,QACA,IAAA;cAEI,MAAA,EAAQ,iBAAA;ED9FU;EC8I9B,SAAA,CAAU,MAAA,EAAQ,OAAA,CAAQ,UAAA;EAW1B,OAAA,CAAQ,KAAA,UAAe,MAAA,UAAgB,GAAA;EAQvC,MAAA,CAAO,OAAA,UAAiB,OAAA,UAAiB,IAAA;EAOzC,OAAA,CAAA;EDxKkD;EC+KlD,gBAAA,CAAA,GAAoB,KAAA,CAAM,aAAA;AAAA;;;UC3LV,eAAA;;EAEhB,YAAA;;EAEA,YAAA;EFDmD;EEGnD,UAAA;EFH0B;EEK1B,UAAA;EFEY;EEAZ,UAAA;;EAEA,UAAA;EFFgC;EEIhC,YAAA;EFJgC;EEMhC,iBAAA;EFNgC;EEQhC,SAAA;AAAA;AAAA,cAGY,wBAAA,EAA0B,eAAA;AAAA,UActB,eAAA;EAChB,CAAA;EACA,CAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,cAoOY,iBAAA;EAAA,QACJ,QAAA;EAAA,QACA,IAAA;EAAA,QACA,KAAA;EAAA,QACA,MAAA;;EAsDR,SAAA,CAAU,MAAA,EAAQ,OAAA,CAAQ,eAAA;EAc1B,OAAA,CAAQ,UAAA,EAAY,KAAA,CAAM,OAAA,EAAS,GAAA;EAKnC,MAAA,CACC,QAAA,EAAU,KAAA,CAAM,aAAA,EAChB,OAAA,UACA,OAAA,UACA,IAAA,UACA,QAAA,EAAU,eAAA,IACV,OAAA,EAAS,eAAA,SACT,MAAA,GAAQ,SAAA,IACR,QAAA,GAAU,qBAAA;EAiFX,OAAA,CAAA;AAAA"}
@@ -0,0 +1,105 @@
1
+ import { F as DomWidgetProps, L as R3FWidgetProps, b as EqualSpacingIndicator, n as LayoutEngine, x as SnapGuide } from "./engine-BfbvWXSk.mjs";
2
+ import { EntityId } from "@jamesyong42/reactive-ecs";
3
+ import * as _$react from "react";
4
+ import * as THREE from "three";
5
+
6
+ //#region src/react/context.d.ts
7
+ declare const EngineProvider: _$react.Provider<LayoutEngine | null>;
8
+ declare const ContainerRefProvider: _$react.Provider<_$react.RefObject<HTMLDivElement | null> | null>;
9
+ declare function useContainerRef(): React.RefObject<HTMLDivElement | null> | null;
10
+ /**
11
+ * Returns the LayoutEngine instance from the nearest InfiniteCanvas context.
12
+ * Throws if used outside an InfiniteCanvas provider.
13
+ */
14
+ declare function useLayoutEngine(): LayoutEngine;
15
+ /**
16
+ * Discriminated resolution of a widget by type. The surface determines which
17
+ * layer renders the component and with what prop shape.
18
+ */
19
+ type ResolvedWidget = {
20
+ surface: 'dom';
21
+ component: React.ComponentType<DomWidgetProps>;
22
+ } | {
23
+ surface: 'webgl';
24
+ component: React.ComponentType<R3FWidgetProps>;
25
+ };
26
+ type WidgetResolver = (entityId: EntityId, widgetType: string) => ResolvedWidget | null;
27
+ declare const WidgetResolverProvider: _$react.Provider<WidgetResolver | null>;
28
+ declare function useWidgetResolver(): WidgetResolver | null;
29
+ //#endregion
30
+ //#region src/react/webgl/GridRenderer.d.ts
31
+ interface GridConfig {
32
+ /** World-unit spacings for up to 3 grid levels [fine, medium, coarse]. */
33
+ spacings: [number, number, number];
34
+ /** Dot RGB color as [r, g, b] in 0–1 range. */
35
+ dotColor: [number, number, number];
36
+ /** Base dot opacity multiplier (0–1). */
37
+ dotAlpha: number;
38
+ /** CSS-pixel range where a grid level fades in: [start, end]. */
39
+ fadeIn: [number, number];
40
+ /** CSS-pixel range where a grid level fades out: [start, end]. */
41
+ fadeOut: [number, number];
42
+ /** Dot radius range in CSS pixels [min, max]. Scaled by DPR internally. */
43
+ dotRadius: [number, number];
44
+ /** Per-level opacity weight: level i gets (base + i * step). */
45
+ levelWeight: [number, number];
46
+ }
47
+ declare const DEFAULT_GRID_CONFIG: GridConfig;
48
+ declare class GridRenderer {
49
+ private renderer;
50
+ private scene;
51
+ private camera;
52
+ private material;
53
+ private mesh;
54
+ constructor(canvas: HTMLCanvasElement);
55
+ /** Apply a (partial) grid config. Only provided fields are updated. */
56
+ setConfig(config: Partial<GridConfig>): void;
57
+ setSize(width: number, height: number, dpr?: number): void;
58
+ render(cameraX: number, cameraY: number, zoom: number): void;
59
+ dispose(): void;
60
+ /** Expose for future WebGL widget rendering */
61
+ getWebGLRenderer(): THREE.WebGLRenderer;
62
+ }
63
+ //#endregion
64
+ //#region src/react/webgl/SelectionRenderer.d.ts
65
+ interface SelectionConfig {
66
+ /** Selection outline color [r,g,b] 0-1. Default: Figma blue. */
67
+ outlineColor: [number, number, number];
68
+ /** Selection outline width in screen px. */
69
+ outlineWidth: number;
70
+ /** Hover outline color [r,g,b] 0-1. */
71
+ hoverColor: [number, number, number];
72
+ /** Hover outline width in screen px. */
73
+ hoverWidth: number;
74
+ /** Handle size in screen px. */
75
+ handleSize: number;
76
+ /** Handle fill color [r,g,b] 0-1 (white). */
77
+ handleFill: [number, number, number];
78
+ /** Handle border color [r,g,b] 0-1 (same as outline). */
79
+ handleBorder: [number, number, number];
80
+ /** Handle border width in screen px. */
81
+ handleBorderWidth: number;
82
+ /** Group bbox dash length in screen px (0 = solid). */
83
+ groupDash: number;
84
+ }
85
+ declare const DEFAULT_SELECTION_CONFIG: SelectionConfig;
86
+ interface SelectionBounds {
87
+ x: number;
88
+ y: number;
89
+ width: number;
90
+ height: number;
91
+ }
92
+ declare class SelectionRenderer {
93
+ private material;
94
+ private mesh;
95
+ private scene;
96
+ private camera;
97
+ constructor();
98
+ setConfig(config: Partial<SelectionConfig>): void;
99
+ setSize(resolution: THREE.Vector2, dpr: number): void;
100
+ render(renderer: THREE.WebGLRenderer, cameraX: number, cameraY: number, zoom: number, selected: SelectionBounds[], hovered: SelectionBounds | null, guides?: SnapGuide[], spacings?: EqualSpacingIndicator[]): void;
101
+ dispose(): void;
102
+ }
103
+ //#endregion
104
+ export { DEFAULT_GRID_CONFIG as a, ContainerRefProvider as c, WidgetResolverProvider as d, useContainerRef as f, SelectionRenderer as i, EngineProvider as l, useWidgetResolver as m, SelectionBounds as n, GridConfig as o, useLayoutEngine as p, SelectionConfig as r, GridRenderer as s, DEFAULT_SELECTION_CONFIG as t, ResolvedWidget as u };
105
+ //# sourceMappingURL=SelectionRenderer-DlsBstAq.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectionRenderer-DlsBstAq.d.mts","names":[],"sources":["../src/react/context.ts","../src/react/webgl/GridRenderer.ts","../src/react/webgl/SelectionRenderer.ts"],"mappings":";;;;;;cASa,cAAA,EAAc,OAAA,CAAA,QAAA,CAAA,YAAA;AAAA,cAOd,oBAAA,EAAoB,OAAA,CAAA,QAAA,CAAA,OAAA,CAAA,SAAA,CAAA,cAAA;AAAA,iBAEjB,eAAA,CAAA,GAAmB,KAAA,CAAM,SAAA,CAAU,cAAA;AATnD;;;;AAAA,iBAiBgB,eAAA,CAAA,GAAmB,YAAA;;;;;KAgBvB,cAAA;EACP,OAAA;EAAgB,SAAA,EAAW,KAAA,CAAM,aAAA,CAAc,cAAA;AAAA;EAC/C,OAAA;EAAkB,SAAA,EAAW,KAAA,CAAM,aAAA,CAAc,cAAA;AAAA;AAAA,KAE1C,cAAA,IAAkB,QAAA,EAAU,QAAA,EAAU,UAAA,aAAuB,cAAA;AAAA,cAI5D,sBAAA,EAAsB,OAAA,CAAA,QAAA,CAAA,cAAA;AAAA,iBAEnB,iBAAA,CAAA,GAAqB,cAAA;;;UChDpB,UAAA;;EAEhB,QAAA;;EAEA,QAAA;EDCY;ECCZ,QAAA;;EAEA,MAAA;EDH0B;ECK1B,OAAA;EDE+D;ECA/D,SAAA;EDAgC;ECEhC,WAAA;AAAA;AAAA,cAGY,mBAAA,EAAqB,UAAA;AAAA,cAoFrB,YAAA;EAAA,QACJ,QAAA;EAAA,QACA,KAAA;EAAA,QACA,MAAA;EAAA,QACA,QAAA;EAAA,QACA,IAAA;cAEI,MAAA,EAAQ,iBAAA;ED9FU;EC8I9B,SAAA,CAAU,MAAA,EAAQ,OAAA,CAAQ,UAAA;EAW1B,OAAA,CAAQ,KAAA,UAAe,MAAA,UAAgB,GAAA;EAQvC,MAAA,CAAO,OAAA,UAAiB,OAAA,UAAiB,IAAA;EAOzC,OAAA,CAAA;EDxKkD;EC+KlD,gBAAA,CAAA,GAAoB,KAAA,CAAM,aAAA;AAAA;;;UC3LV,eAAA;;EAEhB,YAAA;;EAEA,YAAA;EFDmD;EEGnD,UAAA;EFH0B;EEK1B,UAAA;EFEY;EEAZ,UAAA;;EAEA,UAAA;EFFgC;EEIhC,YAAA;EFJgC;EEMhC,iBAAA;EFNgC;EEQhC,SAAA;AAAA;AAAA,cAGY,wBAAA,EAA0B,eAAA;AAAA,UActB,eAAA;EAChB,CAAA;EACA,CAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,cAoOY,iBAAA;EAAA,QACJ,QAAA;EAAA,QACA,IAAA;EAAA,QACA,KAAA;EAAA,QACA,MAAA;;EAsDR,SAAA,CAAU,MAAA,EAAQ,OAAA,CAAQ,eAAA;EAc1B,OAAA,CAAQ,UAAA,EAAY,KAAA,CAAM,OAAA,EAAS,GAAA;EAKnC,MAAA,CACC,QAAA,EAAU,KAAA,CAAM,aAAA,EAChB,OAAA,UACA,OAAA,UACA,IAAA,UACA,QAAA,EAAU,eAAA,IACV,OAAA,EAAS,eAAA,SACT,MAAA,GAAQ,SAAA,IACR,QAAA,GAAU,qBAAA;EAiFX,OAAA,CAAA;AAAA"}