@woven-canvas/core 1.0.6 → 1.0.10

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/build/index.cjs CHANGED
@@ -32,12 +32,12 @@ __export(index_exports, {
32
32
  BringForwardSelected: () => BringForwardSelected,
33
33
  CURSORS: () => CURSORS,
34
34
  Camera: () => Camera,
35
- CanvasComponentDef: () => import_canvas_store48.CanvasComponentDef,
36
- CanvasSingletonDef: () => import_canvas_store48.CanvasSingletonDef,
35
+ CanvasComponentDef: () => import_canvas_store50.CanvasComponentDef,
36
+ CanvasSingletonDef: () => import_canvas_store50.CanvasSingletonDef,
37
37
  CloneEntities: () => CloneEntities,
38
38
  Color: () => Color,
39
39
  CommandMarker: () => CommandMarker,
40
- ComponentDef: () => import_core71.ComponentDef,
40
+ ComponentDef: () => import_core74.ComponentDef,
41
41
  Connector: () => Connector,
42
42
  Controls: () => Controls,
43
43
  ControlsOptions: () => ControlsOptions,
@@ -57,7 +57,7 @@ __export(index_exports, {
57
57
  Embed: () => Embed,
58
58
  EmbedProvider: () => EmbedProvider,
59
59
  EndTransformBoxEdit: () => EndTransformBoxEdit,
60
- EventType: () => import_core71.EventType,
60
+ EventType: () => import_core74.EventType,
61
61
  FontFamily: () => FontFamily,
62
62
  FontLoader: () => FontLoader,
63
63
  Frame: () => Frame,
@@ -75,11 +75,14 @@ __export(index_exports, {
75
75
  Key: () => Key,
76
76
  Keybind: () => Keybind,
77
77
  Keyboard: () => Keyboard,
78
+ Layer: () => Layer,
78
79
  MAX_HIT_ARCS: () => MAX_HIT_ARCS,
79
80
  MAX_HIT_CAPSULES: () => MAX_HIT_CAPSULES,
80
- MainThreadSystem: () => import_core71.MainThreadSystem,
81
+ MAX_HIT_POLYGON_POINTS: () => MAX_HIT_POLYGON_POINTS,
82
+ MainThreadSystem: () => import_core74.MainThreadSystem,
81
83
  Mouse: () => Mouse,
82
84
  Opacity: () => Opacity,
85
+ PlaceBlockEvent: () => PlaceBlockEvent,
83
86
  Pointer: () => Pointer,
84
87
  PointerButton: () => PointerButton,
85
88
  PointerType: () => PointerType,
@@ -93,7 +96,7 @@ __export(index_exports, {
93
96
  RemoveTransformBox: () => RemoveTransformBox,
94
97
  ResetKeyboard: () => ResetKeyboard,
95
98
  ResizeMode: () => ResizeMode,
96
- SINGLETON_ENTITY_ID: () => import_core71.SINGLETON_ENTITY_ID,
99
+ SINGLETON_ENTITY_ID: () => import_core74.SINGLETON_ENTITY_ID,
97
100
  STRATUM_ORDER: () => STRATUM_ORDER,
98
101
  ScaleWithZoom: () => ScaleWithZoom,
99
102
  ScaleWithZoomState: () => ScaleWithZoomState,
@@ -113,7 +116,7 @@ __export(index_exports, {
113
116
  StartTransformBoxEdit: () => StartTransformBoxEdit,
114
117
  Stratum: () => Stratum,
115
118
  StrokeKind: () => StrokeKind,
116
- Synced: () => import_canvas_store48.Synced,
119
+ Synced: () => import_canvas_store50.Synced,
117
120
  Text: () => Text,
118
121
  TextAlignment: () => TextAlignment,
119
122
  Tick: () => Tick,
@@ -132,29 +135,30 @@ __export(index_exports, {
132
135
  UserData: () => UserData,
133
136
  VerticalAlign: () => VerticalAlign,
134
137
  VerticalAlignment: () => VerticalAlignment,
135
- addComponent: () => import_core71.addComponent,
138
+ addComponent: () => import_core74.addComponent,
136
139
  canBlockEdit: () => canBlockEdit,
137
140
  canBlockInteract: () => canBlockInteract,
138
141
  cascadeDelete: () => cascadeDelete,
139
142
  clearPointerTrackingState: () => clearPointerTrackingState,
140
143
  compareBlockOrder: () => compareBlockOrder,
141
144
  convertRefsToUuids: () => convertRefsToUuids,
145
+ createBlock: () => createBlock,
142
146
  createCorePlugin: () => createCorePlugin,
143
- createEntity: () => import_core71.createEntity,
144
- defineCanvasComponent: () => import_canvas_store48.defineCanvasComponent,
145
- defineCanvasSingleton: () => import_canvas_store48.defineCanvasSingleton,
147
+ createEntity: () => import_core74.createEntity,
148
+ defineCanvasComponent: () => import_canvas_store50.defineCanvasComponent,
149
+ defineCanvasSingleton: () => import_canvas_store50.defineCanvasSingleton,
146
150
  defineCommand: () => defineCommand,
147
151
  defineEditorState: () => defineEditorState,
148
152
  defineEditorSystem: () => defineEditorSystem,
149
- defineQuery: () => import_core71.defineQuery,
150
- defineSystem: () => import_core71.defineSystem,
153
+ defineQuery: () => import_core74.defineQuery,
154
+ defineSystem: () => import_core74.defineSystem,
151
155
  deselectAll: () => deselectAll,
152
156
  deselectBlock: () => deselectBlock,
153
157
  detectEmbedProvider: () => detectEmbedProvider,
154
- field: () => import_core71.field,
158
+ field: () => import_core74.field,
155
159
  findFrameAtPoint: () => findFrameAtPoint,
156
160
  generateUuidBySeed: () => generateUuidBySeed,
157
- getBackrefs: () => import_core71.getBackrefs,
161
+ getBackrefs: () => import_core74.getBackrefs,
158
162
  getBlockDef: () => getBlockDef,
159
163
  getBlockResizeMode: () => getBlockResizeMode,
160
164
  getChildren: () => getChildren,
@@ -166,22 +170,24 @@ __export(index_exports, {
166
170
  getPluginResources: () => getPluginResources,
167
171
  getPointerInput: () => getPointerInput,
168
172
  getRefFieldNames: () => getRefFieldNames,
169
- getResources: () => import_core71.getResources,
173
+ getResources: () => import_core74.getResources,
170
174
  getTopmostBlockAtPoint: () => getTopmostBlockAtPoint,
171
175
  getWorldPosition: () => getWorldPosition,
172
- hasComponent: () => import_core71.hasComponent,
176
+ hasComponent: () => import_core74.hasComponent,
173
177
  intersectAabb: () => intersectAabb,
174
178
  intersectCapsule: () => intersectCapsule,
175
179
  intersectPoint: () => intersectPoint,
176
- isAlive: () => import_core71.isAlive,
180
+ isAlive: () => import_core74.isAlive,
177
181
  isAncestor: () => isAncestor,
178
182
  isHeldByRemote: () => isHeldByRemote,
183
+ isLayerInteractive: () => isLayerInteractive,
179
184
  isReadonly: () => isReadonly,
180
185
  on: () => on,
181
186
  parsePlugin: () => parsePlugin,
182
- removeComponent: () => import_core71.removeComponent,
183
- removeEntity: () => import_core71.removeEntity,
187
+ removeComponent: () => import_core74.removeComponent,
188
+ removeEntity: () => import_core74.removeEntity,
184
189
  resolveEmbedUrl: () => resolveEmbedUrl,
190
+ resolveLayerRank: () => resolveLayerRank,
185
191
  resolveRefFields: () => resolveRefFields,
186
192
  runMachine: () => runMachine,
187
193
  selectAll: () => selectAll,
@@ -190,12 +196,12 @@ __export(index_exports, {
190
196
  worldToLocal: () => worldToLocal
191
197
  });
192
198
  module.exports = __toCommonJS(index_exports);
193
- var import_canvas_store48 = require("@woven-ecs/canvas-store");
194
- var import_core71 = require("@woven-ecs/core");
199
+ var import_canvas_store50 = require("@woven-ecs/canvas-store");
200
+ var import_core74 = require("@woven-ecs/core");
195
201
 
196
202
  // src/CorePlugin.ts
197
- var import_canvas_store46 = require("@woven-ecs/canvas-store");
198
- var import_core69 = require("@woven-ecs/core");
203
+ var import_canvas_store48 = require("@woven-ecs/canvas-store");
204
+ var import_core72 = require("@woven-ecs/core");
199
205
 
200
206
  // src/command.ts
201
207
  var import_core = require("@woven-ecs/core");
@@ -274,6 +280,7 @@ var SetCursor = defineCommand("set-cursor");
274
280
  var AddFrameHighlight = defineCommand("frame-add-highlight");
275
281
  var RemoveFrameHighlight = defineCommand("frame-remove-highlight");
276
282
  var AssignFrameChildren = defineCommand("frame-assign-children");
283
+ var PlaceBlockEvent = defineCommand("place-block");
277
284
 
278
285
  // src/commands/selection.ts
279
286
  var SelectBlock = defineCommand("select-block");
@@ -315,8 +322,10 @@ __export(components_exports, {
315
322
  HitGeometry: () => HitGeometry,
316
323
  Hovered: () => Hovered,
317
324
  Image: () => Image,
325
+ Layer: () => Layer,
318
326
  MAX_HIT_ARCS: () => MAX_HIT_ARCS,
319
327
  MAX_HIT_CAPSULES: () => MAX_HIT_CAPSULES,
328
+ MAX_HIT_POLYGON_POINTS: () => MAX_HIT_POLYGON_POINTS,
320
329
  Opacity: () => Opacity,
321
330
  Pointer: () => Pointer,
322
331
  PointerButton: () => PointerButton,
@@ -624,6 +633,7 @@ var BlockDef = import_zod.z.object({
624
633
  resizeMode: import_zod.z.enum([ResizeMode.Scale, ResizeMode.Text, ResizeMode.Free, ResizeMode.GroupOnly]).default(ResizeMode.Scale),
625
634
  canRotate: import_zod.z.boolean().default(true),
626
635
  canScale: import_zod.z.boolean().default(true),
636
+ snappable: import_zod.z.boolean().default(true),
627
637
  interactable: import_zod.z.boolean().default(true),
628
638
  excludeFromRankBounds: import_zod.z.boolean().default(false),
629
639
  connectors: BlockDefConnectors.default(BlockDefConnectors.parse({}))
@@ -691,7 +701,10 @@ var BlockSchema = {
691
701
  /** How the block can be resized */
692
702
  resizeMode: import_core2.field.enum(ResizeMode).default(ResizeMode.Default),
693
703
  /** Parent block ID — position is relative to parent if set */
694
- parentId: import_core2.field.ref()
704
+ parentId: import_core2.field.ref(),
705
+ /** Layer membership — carried by every block directly (a frame and its
706
+ * children each get their own `layerId`; no inheritance). Null = unlayered. */
707
+ layerId: import_core2.field.ref()
695
708
  };
696
709
  var _worldPos = [0, 0];
697
710
  var BlockDef2 = class extends import_canvas_store.CanvasComponentDef {
@@ -1184,15 +1197,21 @@ var import_math3 = require("@woven-canvas/math");
1184
1197
  var import_canvas_store13 = require("@woven-ecs/canvas-store");
1185
1198
  var import_core11 = require("@woven-ecs/core");
1186
1199
  var _uvToWorldMatrix = [1, 0, 0, 1, 0, 0];
1200
+ var _polyPt = [0, 0];
1187
1201
  var MAX_HIT_CAPSULES = 64;
1188
1202
  var FLOATS_PER_CAPSULE = 5;
1189
1203
  var MAX_HIT_ARCS = 2;
1190
1204
  var FLOATS_PER_ARC = 7;
1205
+ var MAX_HIT_POLYGON_POINTS = 128;
1206
+ var FLOATS_PER_POLYGON_POINT = 2;
1207
+ var _worldPolygon = new Float32Array(MAX_HIT_POLYGON_POINTS * FLOATS_PER_POLYGON_POINT);
1191
1208
  var HitGeometrySchema = {
1192
1209
  hitCapsules: import_core11.field.buffer(import_core11.field.float32()).size(MAX_HIT_CAPSULES * FLOATS_PER_CAPSULE),
1193
1210
  capsuleCount: import_core11.field.uint16().default(0),
1194
1211
  hitArcs: import_core11.field.buffer(import_core11.field.float32()).size(MAX_HIT_ARCS * FLOATS_PER_ARC),
1195
- arcCount: import_core11.field.uint16().default(0)
1212
+ arcCount: import_core11.field.uint16().default(0),
1213
+ hitPolygon: import_core11.field.buffer(import_core11.field.float32()).size(MAX_HIT_POLYGON_POINTS * FLOATS_PER_POLYGON_POINT),
1214
+ polygonPointCount: import_core11.field.uint16().default(0)
1196
1215
  };
1197
1216
  var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1198
1217
  constructor() {
@@ -1303,8 +1322,27 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1303
1322
  return true;
1304
1323
  }
1305
1324
  }
1325
+ const polygonPointCount = this.fillWorldPolygon(hitGeometry.hitPolygon, hitGeometry.polygonPointCount);
1326
+ if (polygonPointCount > 0 && import_math3.Polygon.containsPointEvenOdd(_worldPolygon, polygonPointCount, point)) {
1327
+ return true;
1328
+ }
1306
1329
  return false;
1307
1330
  }
1331
+ /**
1332
+ * Transform the UV polygon into the shared `_worldPolygon` scratch buffer
1333
+ * using the current `_uvToWorldMatrix` (caller must have built it first).
1334
+ * Returns the vertex count. Allocation-free.
1335
+ */
1336
+ fillWorldPolygon(hitPolygon, count) {
1337
+ for (let i = 0; i < count; i++) {
1338
+ _polyPt[0] = hitPolygon[i * 2];
1339
+ _polyPt[1] = hitPolygon[i * 2 + 1];
1340
+ import_math3.Mat2.transformPoint(_uvToWorldMatrix, _polyPt);
1341
+ _worldPolygon[i * 2] = _polyPt[0];
1342
+ _worldPolygon[i * 2 + 1] = _polyPt[1];
1343
+ }
1344
+ return count;
1345
+ }
1308
1346
  /**
1309
1347
  * Check if an AABB intersects with the entity's hit geometry.
1310
1348
  * Hit geometry is stored in UV coordinates (0-1) relative to the block.
@@ -1344,6 +1382,10 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1344
1382
  return true;
1345
1383
  }
1346
1384
  }
1385
+ const polygonPointCount = this.fillWorldPolygon(hitGeometry.hitPolygon, hitGeometry.polygonPointCount);
1386
+ if (polygonPointCount > 0 && import_math3.Polygon.intersectsAabb(_worldPolygon, polygonPointCount, aabb)) {
1387
+ return true;
1388
+ }
1347
1389
  return false;
1348
1390
  }
1349
1391
  /**
@@ -1381,6 +1423,11 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1381
1423
  const worldArc = import_math3.Arc.create(worldA[0], worldA[1], worldB[0], worldB[1], worldC[0], worldC[1], thickness);
1382
1424
  pts.push(...import_math3.Arc.getExtrema(worldArc));
1383
1425
  }
1426
+ for (let i = 0; i < hitGeometry.polygonPointCount; i++) {
1427
+ const world = [hitGeometry.hitPolygon[i * 2], hitGeometry.hitPolygon[i * 2 + 1]];
1428
+ import_math3.Mat2.transformPoint(_uvToWorldMatrix, world);
1429
+ pts.push(world);
1430
+ }
1384
1431
  return pts;
1385
1432
  }
1386
1433
  // ============================================
@@ -1483,6 +1530,27 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1483
1530
  const uvC = Block.worldToUv(ctx, entityId, worldC);
1484
1531
  this.addArcUv(ctx, entityId, uvA, uvB, uvC, worldThickness);
1485
1532
  }
1533
+ /**
1534
+ * Set the filled hit polygon from UV vertices, replacing any existing one.
1535
+ * Positions are in UV coordinates (0-1) relative to the block. The polygon is
1536
+ * implicitly closed and tested with the even-odd fill rule.
1537
+ *
1538
+ * @param ctx - ECS context
1539
+ * @param entityId - Entity ID
1540
+ * @param uvPoints - Polygon vertices in UV coordinates [[x, y], ...]
1541
+ */
1542
+ setPolygonUv(ctx, entityId, uvPoints) {
1543
+ const hitGeometry = this.write(ctx, entityId);
1544
+ const count = Math.min(uvPoints.length, MAX_HIT_POLYGON_POINTS);
1545
+ for (let i = 0; i < count; i++) {
1546
+ hitGeometry.hitPolygon[i * 2] = uvPoints[i][0];
1547
+ hitGeometry.hitPolygon[i * 2 + 1] = uvPoints[i][1];
1548
+ }
1549
+ hitGeometry.polygonPointCount = count;
1550
+ if (uvPoints.length > MAX_HIT_POLYGON_POINTS) {
1551
+ console.warn(`HitGeometry: polygon truncated to ${MAX_HIT_POLYGON_POINTS} points`);
1552
+ }
1553
+ }
1486
1554
  /**
1487
1555
  * Clear all hit geometry from an entity.
1488
1556
  *
@@ -1493,6 +1561,7 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1493
1561
  const hitGeometry = this.write(ctx, entityId);
1494
1562
  hitGeometry.capsuleCount = 0;
1495
1563
  hitGeometry.arcCount = 0;
1564
+ hitGeometry.polygonPointCount = 0;
1496
1565
  }
1497
1566
  };
1498
1567
  var HitGeometry = new HitGeometryDef();
@@ -1516,19 +1585,39 @@ var Image = (0, import_canvas_store15.defineCanvasComponent)(
1516
1585
  }
1517
1586
  );
1518
1587
 
1519
- // src/components/Opacity.ts
1588
+ // src/components/Layer.ts
1520
1589
  var import_canvas_store16 = require("@woven-ecs/canvas-store");
1521
1590
  var import_core13 = require("@woven-ecs/core");
1522
- var Opacity = (0, import_canvas_store16.defineCanvasComponent)(
1523
- { name: "opacity" },
1591
+ var Layer = (0, import_canvas_store16.defineCanvasComponent)(
1592
+ { name: "layer", sync: "document" },
1524
1593
  {
1525
- value: import_core13.field.uint8().default(255)
1594
+ /** Parent frame/page entity this layer belongs to — back-reference. Layers
1595
+ * exist only for sorting; they have no geometry of their own. */
1596
+ parentId: import_core13.field.ref(),
1597
+ /** Z-order rank among the page's layers (LexoRank string). Higher sorts in front. */
1598
+ rank: import_core13.field.string().max(36).default(""),
1599
+ /** Display name shown in the layers panel. */
1600
+ label: import_core13.field.string().max(128).default("Layer"),
1601
+ /** When true, blocks in this layer are not selectable (skipped by hit-testing). */
1602
+ locked: import_core13.field.boolean().default(false),
1603
+ /** When true, the layer is not rendered and is skipped by hit-testing. */
1604
+ hidden: import_core13.field.boolean().default(false)
1526
1605
  }
1527
1606
  );
1528
1607
 
1529
- // src/components/Pointer.ts
1608
+ // src/components/Opacity.ts
1530
1609
  var import_canvas_store17 = require("@woven-ecs/canvas-store");
1531
1610
  var import_core14 = require("@woven-ecs/core");
1611
+ var Opacity = (0, import_canvas_store17.defineCanvasComponent)(
1612
+ { name: "opacity" },
1613
+ {
1614
+ value: import_core14.field.uint8().default(255)
1615
+ }
1616
+ );
1617
+
1618
+ // src/components/Pointer.ts
1619
+ var import_canvas_store18 = require("@woven-ecs/canvas-store");
1620
+ var import_core15 = require("@woven-ecs/core");
1532
1621
  var PointerButton = {
1533
1622
  None: "none",
1534
1623
  Left: "left",
@@ -1545,32 +1634,32 @@ var PointerType = {
1545
1634
  var SAMPLE_COUNT = 6;
1546
1635
  var PointerSchema = {
1547
1636
  /** Unique pointer ID (from PointerEvent.pointerId) */
1548
- pointerId: import_core14.field.uint16().default(0),
1637
+ pointerId: import_core15.field.uint16().default(0),
1549
1638
  /** Current position relative to the editor element [x, y] */
1550
- position: import_core14.field.tuple(import_core14.field.float32(), 2).default([0, 0]),
1639
+ position: import_core15.field.tuple(import_core15.field.float32(), 2).default([0, 0]),
1551
1640
  /** Position where the pointer went down [x, y] */
1552
- downPosition: import_core14.field.tuple(import_core14.field.float32(), 2).default([0, 0]),
1641
+ downPosition: import_core15.field.tuple(import_core15.field.float32(), 2).default([0, 0]),
1553
1642
  /** Frame number when the pointer went down (for click detection) */
1554
- downFrame: import_core14.field.uint32().default(0),
1643
+ downFrame: import_core15.field.uint32().default(0),
1555
1644
  /** Which button is pressed */
1556
- button: import_core14.field.enum(PointerButton).default(PointerButton.None),
1645
+ button: import_core15.field.enum(PointerButton).default(PointerButton.None),
1557
1646
  /** Type of pointer device */
1558
- pointerType: import_core14.field.enum(PointerType).default(PointerType.Mouse),
1647
+ pointerType: import_core15.field.enum(PointerType).default(PointerType.Mouse),
1559
1648
  /** Pressure from 0 to 1 (for pen/touch) */
1560
- pressure: import_core14.field.float32().default(0),
1649
+ pressure: import_core15.field.float32().default(0),
1561
1650
  /** Whether the pointer event target was not the editor element */
1562
- obscured: import_core14.field.boolean().default(false),
1651
+ obscured: import_core15.field.boolean().default(false),
1563
1652
  // Velocity tracking (ring buffer for position samples)
1564
1653
  /** Ring buffer of previous positions [x0, y0, x1, y1, ...] @internal */
1565
- _prevPositions: import_core14.field.array(import_core14.field.float32(), SAMPLE_COUNT * 2),
1654
+ _prevPositions: import_core15.field.array(import_core15.field.float32(), SAMPLE_COUNT * 2),
1566
1655
  /** Ring buffer of timestamps for each position sample @internal */
1567
- _prevTimes: import_core14.field.array(import_core14.field.float32(), SAMPLE_COUNT),
1656
+ _prevTimes: import_core15.field.array(import_core15.field.float32(), SAMPLE_COUNT),
1568
1657
  /** Total number of samples added (used for ring buffer indexing) @internal */
1569
- _sampleCount: import_core14.field.int32().default(0),
1658
+ _sampleCount: import_core15.field.int32().default(0),
1570
1659
  /** Computed velocity [vx, vy] in pixels per second @internal */
1571
- _velocity: import_core14.field.tuple(import_core14.field.float32(), 2).default([0, 0])
1660
+ _velocity: import_core15.field.tuple(import_core15.field.float32(), 2).default([0, 0])
1572
1661
  };
1573
- var PointerDef = class extends import_canvas_store17.CanvasComponentDef {
1662
+ var PointerDef = class extends import_canvas_store18.CanvasComponentDef {
1574
1663
  constructor() {
1575
1664
  super({ name: "pointer" }, PointerSchema);
1576
1665
  }
@@ -1679,96 +1768,96 @@ function addPointerSample(pointer, position, time) {
1679
1768
  }
1680
1769
 
1681
1770
  // src/components/ScaleWithZoom.ts
1682
- var import_canvas_store18 = require("@woven-ecs/canvas-store");
1683
- var import_core15 = require("@woven-ecs/core");
1684
- var ScaleWithZoom = (0, import_canvas_store18.defineCanvasComponent)(
1771
+ var import_canvas_store19 = require("@woven-ecs/canvas-store");
1772
+ var import_core16 = require("@woven-ecs/core");
1773
+ var ScaleWithZoom = (0, import_canvas_store19.defineCanvasComponent)(
1685
1774
  { name: "scaleWithZoom" },
1686
1775
  {
1687
1776
  /** Pivot point for scaling as [x, y] (0-1, default 0.5,0.5 = center) */
1688
- anchor: import_core15.field.tuple(import_core15.field.float64(), 2).default([0.5, 0.5]),
1777
+ anchor: import_core16.field.tuple(import_core16.field.float64(), 2).default([0.5, 0.5]),
1689
1778
  /** Initial position as [left, top] at zoom=1 */
1690
- startPosition: import_core15.field.tuple(import_core15.field.float64(), 2).default([0, 0]),
1779
+ startPosition: import_core16.field.tuple(import_core16.field.float64(), 2).default([0, 0]),
1691
1780
  /** Initial size as [width, height] at zoom=1 */
1692
- startSize: import_core15.field.tuple(import_core15.field.float64(), 2).default([0, 0]),
1781
+ startSize: import_core16.field.tuple(import_core16.field.float64(), 2).default([0, 0]),
1693
1782
  /** Scale multiplier per dimension: [x, y] (0 = no zoom effect, 1 = full zoom effect, 0.5 = half effect) */
1694
- scaleMultiplier: import_core15.field.tuple(import_core15.field.float64(), 2).default([1, 1])
1783
+ scaleMultiplier: import_core16.field.tuple(import_core16.field.float64(), 2).default([1, 1])
1695
1784
  }
1696
1785
  );
1697
1786
 
1698
1787
  // src/components/Selected.ts
1699
- var import_canvas_store19 = require("@woven-ecs/canvas-store");
1700
- var Selected = (0, import_canvas_store19.defineCanvasComponent)({ name: "selected" }, {});
1788
+ var import_canvas_store20 = require("@woven-ecs/canvas-store");
1789
+ var Selected = (0, import_canvas_store20.defineCanvasComponent)({ name: "selected" }, {});
1701
1790
 
1702
1791
  // src/components/SelectionBox.ts
1703
- var import_canvas_store20 = require("@woven-ecs/canvas-store");
1704
- var SelectionBox = (0, import_canvas_store20.defineCanvasComponent)({ name: "selectionBox" }, {});
1792
+ var import_canvas_store21 = require("@woven-ecs/canvas-store");
1793
+ var SelectionBox = (0, import_canvas_store21.defineCanvasComponent)({ name: "selectionBox" }, {});
1705
1794
 
1706
1795
  // src/components/Shape.ts
1707
- var import_canvas_store21 = require("@woven-ecs/canvas-store");
1708
- var import_core16 = require("@woven-ecs/core");
1796
+ var import_canvas_store22 = require("@woven-ecs/canvas-store");
1797
+ var import_core17 = require("@woven-ecs/core");
1709
1798
  var StrokeKind = {
1710
1799
  Solid: "solid",
1711
1800
  Dashed: "dashed",
1712
1801
  None: "none"
1713
1802
  };
1714
- var Shape = (0, import_canvas_store21.defineCanvasComponent)(
1803
+ var Shape = (0, import_canvas_store22.defineCanvasComponent)(
1715
1804
  { name: "shape", sync: "document" },
1716
1805
  {
1717
1806
  /** The kind of shape to render (e.g. 'rectangle', 'ellipse', or custom shape key) */
1718
- kind: import_core16.field.string().default("rectangle"),
1807
+ kind: import_core17.field.string().default("rectangle"),
1719
1808
  /** Stroke style */
1720
- strokeKind: import_core16.field.enum(StrokeKind).default(StrokeKind.Solid),
1809
+ strokeKind: import_core17.field.enum(StrokeKind).default(StrokeKind.Solid),
1721
1810
  /** Stroke width in pixels */
1722
- strokeWidth: import_core16.field.uint16().default(2),
1811
+ strokeWidth: import_core17.field.uint16().default(2),
1723
1812
  /** Stroke color - red component (0-255) */
1724
- strokeRed: import_core16.field.uint8().default(0),
1813
+ strokeRed: import_core17.field.uint8().default(0),
1725
1814
  /** Stroke color - green component (0-255) */
1726
- strokeGreen: import_core16.field.uint8().default(0),
1815
+ strokeGreen: import_core17.field.uint8().default(0),
1727
1816
  /** Stroke color - blue component (0-255) */
1728
- strokeBlue: import_core16.field.uint8().default(0),
1817
+ strokeBlue: import_core17.field.uint8().default(0),
1729
1818
  /** Stroke color - alpha component (0-255) */
1730
- strokeAlpha: import_core16.field.uint8().default(255),
1819
+ strokeAlpha: import_core17.field.uint8().default(255),
1731
1820
  /** Fill color - red component (0-255) */
1732
- fillRed: import_core16.field.uint8().default(255),
1821
+ fillRed: import_core17.field.uint8().default(255),
1733
1822
  /** Fill color - green component (0-255) */
1734
- fillGreen: import_core16.field.uint8().default(255),
1823
+ fillGreen: import_core17.field.uint8().default(255),
1735
1824
  /** Fill color - blue component (0-255) */
1736
- fillBlue: import_core16.field.uint8().default(255),
1825
+ fillBlue: import_core17.field.uint8().default(255),
1737
1826
  /** Fill color - alpha component (0-255) */
1738
- fillAlpha: import_core16.field.uint8().default(0)
1827
+ fillAlpha: import_core17.field.uint8().default(0)
1739
1828
  }
1740
1829
  );
1741
1830
 
1742
1831
  // src/components/Text.ts
1743
- var import_canvas_store22 = require("@woven-ecs/canvas-store");
1744
- var import_core17 = require("@woven-ecs/core");
1745
- var Text = (0, import_canvas_store22.defineCanvasComponent)(
1832
+ var import_canvas_store23 = require("@woven-ecs/canvas-store");
1833
+ var import_core18 = require("@woven-ecs/core");
1834
+ var Text = (0, import_canvas_store23.defineCanvasComponent)(
1746
1835
  { name: "text", sync: "document" },
1747
1836
  {
1748
1837
  /** HTML content (supports rich text formatting) */
1749
- content: import_core17.field.string().max(1e4).default(""),
1838
+ content: import_core18.field.string().max(1e4).default(""),
1750
1839
  /** Font size in pixels */
1751
- fontSizePx: import_core17.field.float64().default(24),
1840
+ fontSizePx: import_core18.field.float64().default(24),
1752
1841
  /** Font family name */
1753
- fontFamily: import_core17.field.string().max(64).default(""),
1842
+ fontFamily: import_core18.field.string().max(64).default(""),
1754
1843
  /** Line height multiplier */
1755
- lineHeight: import_core17.field.float64().default(1.2),
1844
+ lineHeight: import_core18.field.float64().default(1.2),
1756
1845
  /** Letter spacing in em units */
1757
- letterSpacingEm: import_core17.field.float64().default(0),
1846
+ letterSpacingEm: import_core18.field.float64().default(0),
1758
1847
  /** Whether width is constrained (text wraps) */
1759
- constrainWidth: import_core17.field.boolean().default(true),
1848
+ constrainWidth: import_core18.field.boolean().default(true),
1760
1849
  /** Default text alignment for new paragraphs */
1761
- defaultAlignment: import_core17.field.enum(TextAlignment).default(TextAlignment.Left)
1850
+ defaultAlignment: import_core18.field.enum(TextAlignment).default(TextAlignment.Left)
1762
1851
  }
1763
1852
  );
1764
1853
 
1765
1854
  // src/components/TransformBox.ts
1766
- var import_canvas_store23 = require("@woven-ecs/canvas-store");
1767
- var TransformBox = (0, import_canvas_store23.defineCanvasComponent)({ name: "transformBox" }, {});
1855
+ var import_canvas_store24 = require("@woven-ecs/canvas-store");
1856
+ var TransformBox = (0, import_canvas_store24.defineCanvasComponent)({ name: "transformBox" }, {});
1768
1857
 
1769
1858
  // src/components/TransformHandle.ts
1770
- var import_canvas_store24 = require("@woven-ecs/canvas-store");
1771
- var import_core18 = require("@woven-ecs/core");
1859
+ var import_canvas_store25 = require("@woven-ecs/canvas-store");
1860
+ var import_core19 = require("@woven-ecs/core");
1772
1861
 
1773
1862
  // src/cursors.ts
1774
1863
  var CursorKind = /* @__PURE__ */ ((CursorKind2) => {
@@ -1869,39 +1958,39 @@ var CURSORS = {
1869
1958
  };
1870
1959
 
1871
1960
  // src/components/TransformHandle.ts
1872
- var TransformHandle = (0, import_canvas_store24.defineCanvasComponent)(
1961
+ var TransformHandle = (0, import_canvas_store25.defineCanvasComponent)(
1873
1962
  { name: "transformHandle" },
1874
1963
  {
1875
- kind: import_core18.field.string().max(16).default(TransformHandleKind.Scale),
1876
- vectorX: import_core18.field.int8().default(0),
1877
- vectorY: import_core18.field.int8().default(0),
1878
- transformBox: import_core18.field.ref(),
1879
- cursorKind: import_core18.field.string().max(16).default("drag" /* Drag */)
1964
+ kind: import_core19.field.string().max(16).default(TransformHandleKind.Scale),
1965
+ vectorX: import_core19.field.int8().default(0),
1966
+ vectorY: import_core19.field.int8().default(0),
1967
+ transformBox: import_core19.field.ref(),
1968
+ cursorKind: import_core19.field.string().max(16).default("drag" /* Drag */)
1880
1969
  }
1881
1970
  );
1882
1971
 
1883
1972
  // src/components/User.ts
1884
- var import_canvas_store25 = require("@woven-ecs/canvas-store");
1885
- var import_core19 = require("@woven-ecs/core");
1886
- var User = (0, import_canvas_store25.defineCanvasComponent)(
1973
+ var import_canvas_store26 = require("@woven-ecs/canvas-store");
1974
+ var import_core20 = require("@woven-ecs/core");
1975
+ var User = (0, import_canvas_store26.defineCanvasComponent)(
1887
1976
  { name: "user", sync: "ephemeral" },
1888
1977
  {
1889
- userId: import_core19.field.string().max(36),
1890
- sessionId: import_core19.field.string().max(36),
1891
- color: import_core19.field.string().max(7),
1892
- name: import_core19.field.string().max(100),
1893
- avatar: import_core19.field.string().max(500),
1894
- position: import_core19.field.tuple(import_core19.field.float32(), 2).default([0, 0])
1978
+ userId: import_core20.field.string().max(36),
1979
+ sessionId: import_core20.field.string().max(36),
1980
+ color: import_core20.field.string().max(7),
1981
+ name: import_core20.field.string().max(100),
1982
+ avatar: import_core20.field.string().max(500),
1983
+ position: import_core20.field.tuple(import_core20.field.float32(), 2).default([0, 0])
1895
1984
  }
1896
1985
  );
1897
1986
 
1898
1987
  // src/components/VerticalAlign.ts
1899
- var import_canvas_store26 = require("@woven-ecs/canvas-store");
1900
- var import_core20 = require("@woven-ecs/core");
1901
- var VerticalAlign = (0, import_canvas_store26.defineCanvasComponent)(
1988
+ var import_canvas_store27 = require("@woven-ecs/canvas-store");
1989
+ var import_core21 = require("@woven-ecs/core");
1990
+ var VerticalAlign = (0, import_canvas_store27.defineCanvasComponent)(
1902
1991
  { name: "verticalAlign", sync: "document" },
1903
1992
  {
1904
- value: import_core20.field.enum(VerticalAlignment).default(VerticalAlignment.Top)
1993
+ value: import_core21.field.enum(VerticalAlignment).default(VerticalAlignment.Top)
1905
1994
  }
1906
1995
  );
1907
1996
 
@@ -1939,23 +2028,23 @@ __export(singletons_exports, {
1939
2028
  });
1940
2029
 
1941
2030
  // src/singletons/Camera.ts
1942
- var import_canvas_store28 = require("@woven-ecs/canvas-store");
1943
- var import_core22 = require("@woven-ecs/core");
2031
+ var import_canvas_store29 = require("@woven-ecs/canvas-store");
2032
+ var import_core23 = require("@woven-ecs/core");
1944
2033
 
1945
2034
  // src/singletons/Screen.ts
1946
- var import_canvas_store27 = require("@woven-ecs/canvas-store");
1947
- var import_core21 = require("@woven-ecs/core");
2035
+ var import_canvas_store28 = require("@woven-ecs/canvas-store");
2036
+ var import_core22 = require("@woven-ecs/core");
1948
2037
  var ScreenSchema = {
1949
2038
  /** Width of the editor element in pixels */
1950
- width: import_core21.field.float64().default(0),
2039
+ width: import_core22.field.float64().default(0),
1951
2040
  /** Height of the editor element in pixels */
1952
- height: import_core21.field.float64().default(0),
2041
+ height: import_core22.field.float64().default(0),
1953
2042
  /** Left offset of the editor element relative to the viewport */
1954
- left: import_core21.field.float64().default(0),
2043
+ left: import_core22.field.float64().default(0),
1955
2044
  /** Top offset of the editor element relative to the viewport */
1956
- top: import_core21.field.float64().default(0)
2045
+ top: import_core22.field.float64().default(0)
1957
2046
  };
1958
- var ScreenDef = class extends import_canvas_store27.CanvasSingletonDef {
2047
+ var ScreenDef = class extends import_canvas_store28.CanvasSingletonDef {
1959
2048
  constructor() {
1960
2049
  super({ name: "screen" }, ScreenSchema);
1961
2050
  }
@@ -1980,17 +2069,17 @@ var Screen = new ScreenDef();
1980
2069
  // src/singletons/Camera.ts
1981
2070
  var CameraSchema = {
1982
2071
  /** Top position of the camera in world coordinates */
1983
- top: import_core22.field.float64().default(0),
2072
+ top: import_core23.field.float64().default(0),
1984
2073
  /** Left position of the camera in world coordinates */
1985
- left: import_core22.field.float64().default(0),
2074
+ left: import_core23.field.float64().default(0),
1986
2075
  /** Zoom level (1 = 100%, 2 = 200%, 0.5 = 50%) */
1987
- zoom: import_core22.field.float64().default(1),
2076
+ zoom: import_core23.field.float64().default(1),
1988
2077
  /** Whether the camera viewport intersects any blocks */
1989
- canSeeBlocks: import_core22.field.boolean().default(true),
2078
+ canSeeBlocks: import_core23.field.boolean().default(true),
1990
2079
  /** Reference to a block that the camera can currently see (for optimization) */
1991
- lastSeenBlock: import_core22.field.ref()
2080
+ lastSeenBlock: import_core23.field.ref()
1992
2081
  };
1993
- var CameraDef = class extends import_canvas_store28.CanvasSingletonDef {
2082
+ var CameraDef = class extends import_canvas_store29.CanvasSingletonDef {
1994
2083
  constructor() {
1995
2084
  super({ name: "camera", sync: "local" }, CameraSchema);
1996
2085
  }
@@ -2063,25 +2152,25 @@ var CameraDef = class extends import_canvas_store28.CanvasSingletonDef {
2063
2152
  var Camera = new CameraDef();
2064
2153
 
2065
2154
  // src/singletons/Controls.ts
2066
- var import_canvas_store29 = require("@woven-ecs/canvas-store");
2067
- var import_core23 = require("@woven-ecs/core");
2155
+ var import_canvas_store30 = require("@woven-ecs/canvas-store");
2156
+ var import_core24 = require("@woven-ecs/core");
2068
2157
  var ControlsSchema = {
2069
2158
  /** Tool activated by left mouse button */
2070
- leftMouseTool: import_core23.field.string().max(32).default("select"),
2159
+ leftMouseTool: import_core24.field.string().max(32).default("select"),
2071
2160
  /** Tool activated by middle mouse button */
2072
- middleMouseTool: import_core23.field.string().max(32).default("hand"),
2161
+ middleMouseTool: import_core24.field.string().max(32).default("hand"),
2073
2162
  /** Tool activated by right mouse button */
2074
- rightMouseTool: import_core23.field.string().max(32).default("hand"),
2163
+ rightMouseTool: import_core24.field.string().max(32).default("hand"),
2075
2164
  /** Tool activated by mouse wheel */
2076
- wheelTool: import_core23.field.string().max(32).default("scroll"),
2165
+ wheelTool: import_core24.field.string().max(32).default("scroll"),
2077
2166
  /** Tool activated by mouse wheel with modifier key held */
2078
- modWheelTool: import_core23.field.string().max(32).default("zoom"),
2167
+ modWheelTool: import_core24.field.string().max(32).default("zoom"),
2079
2168
  /** JSON snapshot of block to place on next click (empty string = no placement active) */
2080
- heldSnapshot: import_core23.field.string().max(65536).default(""),
2169
+ heldSnapshot: import_core24.field.string().max(65536).default(""),
2081
2170
  /** User-facing tool name for UI highlighting (may differ from leftMouseTool during draw/drag-out) */
2082
- activeToolName: import_core23.field.string().max(32).default("select")
2171
+ activeToolName: import_core24.field.string().max(32).default("select")
2083
2172
  };
2084
- var ControlsDef = class extends import_canvas_store29.CanvasSingletonDef {
2173
+ var ControlsDef = class extends import_canvas_store30.CanvasSingletonDef {
2085
2174
  constructor() {
2086
2175
  super({ name: "controls" }, ControlsSchema);
2087
2176
  }
@@ -2120,19 +2209,19 @@ var ControlsDef = class extends import_canvas_store29.CanvasSingletonDef {
2120
2209
  var Controls = new ControlsDef();
2121
2210
 
2122
2211
  // src/singletons/Cursor.ts
2123
- var import_canvas_store30 = require("@woven-ecs/canvas-store");
2124
- var import_core24 = require("@woven-ecs/core");
2212
+ var import_canvas_store31 = require("@woven-ecs/canvas-store");
2213
+ var import_core25 = require("@woven-ecs/core");
2125
2214
  var CursorSchema = {
2126
2215
  /** Base cursor kind (from current tool) */
2127
- cursorKind: import_core24.field.string().max(64).default("select"),
2216
+ cursorKind: import_core25.field.string().max(64).default("select"),
2128
2217
  /** Base cursor rotation in radians */
2129
- rotation: import_core24.field.float64().default(0),
2218
+ rotation: import_core25.field.float64().default(0),
2130
2219
  /** Context-specific cursor kind (overrides cursorKind when set, e.g., during drag/hover) */
2131
- contextCursorKind: import_core24.field.string().max(64).default(""),
2220
+ contextCursorKind: import_core25.field.string().max(64).default(""),
2132
2221
  /** Context cursor rotation in radians */
2133
- contextRotation: import_core24.field.float64().default(0)
2222
+ contextRotation: import_core25.field.float64().default(0)
2134
2223
  };
2135
- var CursorDef2 = class extends import_canvas_store30.CanvasSingletonDef {
2224
+ var CursorDef2 = class extends import_canvas_store31.CanvasSingletonDef {
2136
2225
  constructor() {
2137
2226
  super({ name: "cursor" }, CursorSchema);
2138
2227
  }
@@ -2177,10 +2266,10 @@ var CursorDef2 = class extends import_canvas_store30.CanvasSingletonDef {
2177
2266
  var Cursor = new CursorDef2();
2178
2267
 
2179
2268
  // src/singletons/FrameContainmentState.ts
2180
- var import_core25 = require("@woven-ecs/core");
2269
+ var import_core26 = require("@woven-ecs/core");
2181
2270
 
2182
2271
  // src/EditorStateDef.ts
2183
- var import_canvas_store31 = require("@woven-ecs/canvas-store");
2272
+ var import_canvas_store32 = require("@woven-ecs/canvas-store");
2184
2273
 
2185
2274
  // src/machine.ts
2186
2275
  var import_xstate = require("xstate");
@@ -2208,7 +2297,7 @@ function runMachine(machine, currentState, context, events) {
2208
2297
  }
2209
2298
 
2210
2299
  // src/EditorStateDef.ts
2211
- var EditorStateDef = class extends import_canvas_store31.CanvasSingletonDef {
2300
+ var EditorStateDef = class extends import_canvas_store32.CanvasSingletonDef {
2212
2301
  constructor(name, schema) {
2213
2302
  super({ name, sync: "none" }, schema);
2214
2303
  }
@@ -2285,29 +2374,29 @@ function defineEditorState(name, schema) {
2285
2374
  // src/singletons/FrameContainmentState.ts
2286
2375
  var FrameContainmentState = defineEditorState("frameContainmentState", {
2287
2376
  /** Current state machine state */
2288
- state: import_core25.field.string().max(16).default(FrameContainmentStateEnum.Idle),
2377
+ state: import_core26.field.string().max(16).default(FrameContainmentStateEnum.Idle),
2289
2378
  /** EntityId of the frame currently highlighted as a drop target, or null */
2290
- highlightedFrame: import_core25.field.ref()
2379
+ highlightedFrame: import_core26.field.ref()
2291
2380
  });
2292
2381
 
2293
2382
  // src/singletons/Grid.ts
2294
- var import_canvas_store32 = require("@woven-ecs/canvas-store");
2295
- var import_core26 = require("@woven-ecs/core");
2383
+ var import_canvas_store33 = require("@woven-ecs/canvas-store");
2384
+ var import_core27 = require("@woven-ecs/core");
2296
2385
  var GridSchema = {
2297
2386
  /** Whether grid snapping is enabled */
2298
- enabled: import_core26.field.boolean().default(false),
2387
+ enabled: import_core27.field.boolean().default(false),
2299
2388
  /** Whether resized/rotated objects must stay aligned to the grid */
2300
- strict: import_core26.field.boolean().default(false),
2389
+ strict: import_core27.field.boolean().default(false),
2301
2390
  /** Width of each grid column in world units */
2302
- colWidth: import_core26.field.float64().default(20),
2391
+ colWidth: import_core27.field.float64().default(20),
2303
2392
  /** Height of each grid row in world units */
2304
- rowHeight: import_core26.field.float64().default(20),
2393
+ rowHeight: import_core27.field.float64().default(20),
2305
2394
  /** Angular snap increment in radians when grid is enabled */
2306
- snapAngleRad: import_core26.field.float64().default(Math.PI / 36),
2395
+ snapAngleRad: import_core27.field.float64().default(Math.PI / 36),
2307
2396
  /** Angular snap increment in radians when shift key is held */
2308
- shiftSnapAngleRad: import_core26.field.float64().default(Math.PI / 12)
2397
+ shiftSnapAngleRad: import_core27.field.float64().default(Math.PI / 12)
2309
2398
  };
2310
- var GridDef = class extends import_canvas_store32.CanvasSingletonDef {
2399
+ var GridDef = class extends import_canvas_store33.CanvasSingletonDef {
2311
2400
  constructor() {
2312
2401
  super({ name: "grid" }, GridSchema);
2313
2402
  }
@@ -2367,17 +2456,17 @@ var GridDef = class extends import_canvas_store32.CanvasSingletonDef {
2367
2456
  var Grid = new GridDef();
2368
2457
 
2369
2458
  // src/singletons/Intersect.ts
2370
- var import_canvas_store33 = require("@woven-ecs/canvas-store");
2371
- var import_core27 = require("@woven-ecs/core");
2459
+ var import_canvas_store34 = require("@woven-ecs/canvas-store");
2460
+ var import_core28 = require("@woven-ecs/core");
2372
2461
  var IntersectSchema = {
2373
2462
  // Store up to 5 intersected entity IDs
2374
- entity1: import_core27.field.ref(),
2375
- entity2: import_core27.field.ref(),
2376
- entity3: import_core27.field.ref(),
2377
- entity4: import_core27.field.ref(),
2378
- entity5: import_core27.field.ref()
2463
+ entity1: import_core28.field.ref(),
2464
+ entity2: import_core28.field.ref(),
2465
+ entity3: import_core28.field.ref(),
2466
+ entity4: import_core28.field.ref(),
2467
+ entity5: import_core28.field.ref()
2379
2468
  };
2380
- var IntersectDef = class extends import_canvas_store33.CanvasSingletonDef {
2469
+ var IntersectDef = class extends import_canvas_store34.CanvasSingletonDef {
2381
2470
  constructor() {
2382
2471
  super({ name: "intersect" }, IntersectSchema);
2383
2472
  }
@@ -2426,31 +2515,31 @@ var IntersectDef = class extends import_canvas_store33.CanvasSingletonDef {
2426
2515
  var Intersect = new IntersectDef();
2427
2516
 
2428
2517
  // src/singletons/Keyboard.ts
2429
- var import_canvas_store34 = require("@woven-ecs/canvas-store");
2430
- var import_core28 = require("@woven-ecs/core");
2518
+ var import_canvas_store35 = require("@woven-ecs/canvas-store");
2519
+ var import_core29 = require("@woven-ecs/core");
2431
2520
  var KEY_BUFFER_SIZE = 32;
2432
2521
  var KeyboardSchema = {
2433
2522
  /**
2434
2523
  * Buffer where each bit represents whether a key is currently pressed.
2435
2524
  * Uses field.buffer for zero-allocation subarray views.
2436
2525
  */
2437
- keysDown: import_core28.field.buffer(import_core28.field.uint8()).size(KEY_BUFFER_SIZE),
2526
+ keysDown: import_core29.field.buffer(import_core29.field.uint8()).size(KEY_BUFFER_SIZE),
2438
2527
  /**
2439
2528
  * Buffer for key-down triggers (true for exactly 1 frame when key is pressed).
2440
2529
  * Uses field.buffer for zero-allocation subarray views.
2441
2530
  */
2442
- keysDownTrigger: import_core28.field.buffer(import_core28.field.uint8()).size(KEY_BUFFER_SIZE),
2531
+ keysDownTrigger: import_core29.field.buffer(import_core29.field.uint8()).size(KEY_BUFFER_SIZE),
2443
2532
  /**
2444
2533
  * Buffer for key-up triggers (true for exactly 1 frame when key is released).
2445
2534
  * Uses field.buffer for zero-allocation subarray views.
2446
2535
  */
2447
- keysUpTrigger: import_core28.field.buffer(import_core28.field.uint8()).size(KEY_BUFFER_SIZE),
2536
+ keysUpTrigger: import_core29.field.buffer(import_core29.field.uint8()).size(KEY_BUFFER_SIZE),
2448
2537
  /** Common modifier - Shift key is down */
2449
- shiftDown: import_core28.field.boolean().default(false),
2538
+ shiftDown: import_core29.field.boolean().default(false),
2450
2539
  /** Common modifier - Alt/Option key is down */
2451
- altDown: import_core28.field.boolean().default(false),
2540
+ altDown: import_core29.field.boolean().default(false),
2452
2541
  /** Common modifier - Ctrl (Windows/Linux) or Cmd (Mac) is down */
2453
- modDown: import_core28.field.boolean().default(false)
2542
+ modDown: import_core29.field.boolean().default(false)
2454
2543
  };
2455
2544
  function getBit(buffer, bitIndex) {
2456
2545
  if (bitIndex < 0 || bitIndex >= buffer.length * 8) return false;
@@ -2458,7 +2547,7 @@ function getBit(buffer, bitIndex) {
2458
2547
  const bitOffset = bitIndex % 8;
2459
2548
  return (buffer[byteIndex] & 1 << bitOffset) !== 0;
2460
2549
  }
2461
- var KeyboardDef = class extends import_canvas_store34.CanvasSingletonDef {
2550
+ var KeyboardDef = class extends import_canvas_store35.CanvasSingletonDef {
2462
2551
  constructor() {
2463
2552
  super({ name: "keyboard" }, KeyboardSchema);
2464
2553
  }
@@ -2732,27 +2821,27 @@ var Key = {
2732
2821
  };
2733
2822
 
2734
2823
  // src/singletons/Mouse.ts
2735
- var import_canvas_store35 = require("@woven-ecs/canvas-store");
2736
- var import_core29 = require("@woven-ecs/core");
2824
+ var import_canvas_store36 = require("@woven-ecs/canvas-store");
2825
+ var import_core30 = require("@woven-ecs/core");
2737
2826
  var MouseSchema = {
2738
2827
  /** Current mouse position relative to the editor element [x, y] */
2739
- position: import_core29.field.tuple(import_core29.field.float32(), 2).default([0, 0]),
2828
+ position: import_core30.field.tuple(import_core30.field.float32(), 2).default([0, 0]),
2740
2829
  /** Horizontal wheel delta (positive = scroll right) */
2741
- wheelDeltaX: import_core29.field.float32().default(0),
2830
+ wheelDeltaX: import_core30.field.float32().default(0),
2742
2831
  /** Vertical wheel delta (positive = scroll down), normalized across browsers */
2743
- wheelDeltaY: import_core29.field.float32().default(0),
2832
+ wheelDeltaY: import_core30.field.float32().default(0),
2744
2833
  /** True for 1 frame when mouse moves */
2745
- moveTrigger: import_core29.field.boolean().default(false),
2834
+ moveTrigger: import_core30.field.boolean().default(false),
2746
2835
  /** True for 1 frame when wheel is scrolled */
2747
- wheelTrigger: import_core29.field.boolean().default(false),
2836
+ wheelTrigger: import_core30.field.boolean().default(false),
2748
2837
  /** True for 1 frame when mouse enters the editor element */
2749
- enterTrigger: import_core29.field.boolean().default(false),
2838
+ enterTrigger: import_core30.field.boolean().default(false),
2750
2839
  /** True for 1 frame when mouse leaves the editor element */
2751
- leaveTrigger: import_core29.field.boolean().default(false),
2840
+ leaveTrigger: import_core30.field.boolean().default(false),
2752
2841
  /** Whether the mouse is over a UI element (not the canvas) */
2753
- obscured: import_core29.field.boolean().default(false)
2842
+ obscured: import_core30.field.boolean().default(false)
2754
2843
  };
2755
- var MouseDef = class extends import_canvas_store35.CanvasSingletonDef {
2844
+ var MouseDef = class extends import_canvas_store36.CanvasSingletonDef {
2756
2845
  constructor() {
2757
2846
  super({ name: "mouse" }, MouseSchema);
2758
2847
  }
@@ -2790,14 +2879,14 @@ var MouseDef = class extends import_canvas_store35.CanvasSingletonDef {
2790
2879
  var Mouse = new MouseDef();
2791
2880
 
2792
2881
  // src/singletons/RankBounds.ts
2793
- var import_canvas_store36 = require("@woven-ecs/canvas-store");
2794
- var import_core30 = require("@woven-ecs/core");
2882
+ var import_canvas_store37 = require("@woven-ecs/canvas-store");
2883
+ var import_core31 = require("@woven-ecs/core");
2795
2884
  var import_fractional_indexing_jittered = require("fractional-indexing-jittered");
2796
2885
  var RankBoundsSchema = {
2797
- minRank: import_core30.field.string().max(36).default(""),
2798
- maxRank: import_core30.field.string().max(36).default("")
2886
+ minRank: import_core31.field.string().max(36).default(""),
2887
+ maxRank: import_core31.field.string().max(36).default("")
2799
2888
  };
2800
- var RankBoundsDef = class extends import_canvas_store36.CanvasSingletonDef {
2889
+ var RankBoundsDef = class extends import_canvas_store37.CanvasSingletonDef {
2801
2890
  constructor() {
2802
2891
  super({ name: "rankBounds" }, RankBoundsSchema);
2803
2892
  }
@@ -2853,73 +2942,73 @@ var RankBoundsDef = class extends import_canvas_store36.CanvasSingletonDef {
2853
2942
  var RankBounds = new RankBoundsDef();
2854
2943
 
2855
2944
  // src/singletons/ScaleWithZoomState.ts
2856
- var import_canvas_store37 = require("@woven-ecs/canvas-store");
2857
- var import_core31 = require("@woven-ecs/core");
2858
- var ScaleWithZoomState = (0, import_canvas_store37.defineCanvasSingleton)(
2945
+ var import_canvas_store38 = require("@woven-ecs/canvas-store");
2946
+ var import_core32 = require("@woven-ecs/core");
2947
+ var ScaleWithZoomState = (0, import_canvas_store38.defineCanvasSingleton)(
2859
2948
  { name: "scaleWithZoomState" },
2860
2949
  {
2861
2950
  /** Last processed zoom level */
2862
- lastZoom: import_core31.field.float64().default(1)
2951
+ lastZoom: import_core32.field.float64().default(1)
2863
2952
  }
2864
2953
  );
2865
2954
 
2866
2955
  // src/singletons/ScrollEdgesStateSingleton.ts
2867
- var import_core32 = require("@woven-ecs/core");
2956
+ var import_core33 = require("@woven-ecs/core");
2868
2957
  var ScrollEdgesStateSingleton = defineEditorState("scrollEdgesState", {
2869
2958
  /** Current state of the scroll edges state machine */
2870
- state: import_core32.field.enum(ScrollEdgesState).default(ScrollEdgesState.Idle),
2959
+ state: import_core33.field.enum(ScrollEdgesState).default(ScrollEdgesState.Idle),
2871
2960
  /** Timestamp when pointer first entered the edge zone */
2872
- edgeEnterStartTime: import_core32.field.float64().default(0)
2961
+ edgeEnterStartTime: import_core33.field.float64().default(0)
2873
2962
  });
2874
2963
 
2875
2964
  // src/singletons/SelectionStateSingleton.ts
2876
- var import_core33 = require("@woven-ecs/core");
2965
+ var import_core34 = require("@woven-ecs/core");
2877
2966
  var SelectionStateSingleton = defineEditorState("selectionState", {
2878
- state: import_core33.field.enum(SelectionState).default(SelectionState.Idle),
2879
- dragStart: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2880
- pointingStartClient: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2881
- pointingStartWorld: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2882
- draggedEntityStart: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2883
- draggedEntity: import_core33.field.ref(),
2884
- cloneGeneratorSeed: import_core33.field.string().max(36).default(""),
2885
- isCloning: import_core33.field.boolean()
2967
+ state: import_core34.field.enum(SelectionState).default(SelectionState.Idle),
2968
+ dragStart: import_core34.field.tuple(import_core34.field.float64(), 2).default([0, 0]),
2969
+ pointingStartClient: import_core34.field.tuple(import_core34.field.float64(), 2).default([0, 0]),
2970
+ pointingStartWorld: import_core34.field.tuple(import_core34.field.float64(), 2).default([0, 0]),
2971
+ draggedEntityStart: import_core34.field.tuple(import_core34.field.float64(), 2).default([0, 0]),
2972
+ draggedEntity: import_core34.field.ref(),
2973
+ cloneGeneratorSeed: import_core34.field.string().max(36).default(""),
2974
+ isCloning: import_core34.field.boolean()
2886
2975
  });
2887
2976
 
2888
2977
  // src/singletons/Tick.ts
2889
- var import_canvas_store38 = require("@woven-ecs/canvas-store");
2890
- var import_core34 = require("@woven-ecs/core");
2891
- var Tick = (0, import_canvas_store38.defineCanvasSingleton)(
2978
+ var import_canvas_store39 = require("@woven-ecs/canvas-store");
2979
+ var import_core35 = require("@woven-ecs/core");
2980
+ var Tick = (0, import_canvas_store39.defineCanvasSingleton)(
2892
2981
  { name: "tick" },
2893
2982
  {
2894
2983
  /** Current tick number (increments each tick) */
2895
- number: import_core34.field.uint32().default(0),
2984
+ number: import_core35.field.uint32().default(0),
2896
2985
  /** Time since last tick in seconds */
2897
- delta: import_core34.field.float64().default(0),
2986
+ delta: import_core35.field.float64().default(0),
2898
2987
  /** Timestamp of current tick in milliseconds (from performance.now()) */
2899
- time: import_core34.field.float64().default(0),
2988
+ time: import_core35.field.float64().default(0),
2900
2989
  /** Timestamp of previous tick in milliseconds (0 if first tick) */
2901
- lastTime: import_core34.field.float64().default(0)
2990
+ lastTime: import_core35.field.float64().default(0)
2902
2991
  }
2903
2992
  );
2904
2993
 
2905
2994
  // src/singletons/TransformBoxStateSingleton.ts
2906
- var import_core35 = require("@woven-ecs/core");
2995
+ var import_core36 = require("@woven-ecs/core");
2907
2996
  var TransformBoxStateSingleton = defineEditorState("transformBoxState", {
2908
- state: import_core35.field.string().max(32).default(TransformBoxState.None),
2909
- transformBoxId: import_core35.field.ref()
2997
+ state: import_core36.field.string().max(32).default(TransformBoxState.None),
2998
+ transformBoxId: import_core36.field.ref()
2910
2999
  });
2911
3000
 
2912
3001
  // src/systems/capture/captureFrameContainmentSystem.ts
2913
- var import_canvas_store40 = require("@woven-ecs/canvas-store");
2914
- var import_core48 = require("@woven-ecs/core");
3002
+ var import_canvas_store42 = require("@woven-ecs/canvas-store");
3003
+ var import_core51 = require("@woven-ecs/core");
2915
3004
  var import_xstate2 = require("xstate");
2916
3005
 
2917
3006
  // src/EditorSystem.ts
2918
- var import_core36 = require("@woven-ecs/core");
3007
+ var import_core37 = require("@woven-ecs/core");
2919
3008
  var DEFAULT_PRIORITY = 0;
2920
3009
  function defineEditorSystem(options, execute) {
2921
3010
  return {
2922
- _system: new import_core36.MainThreadSystem(execute),
3011
+ _system: new import_core37.MainThreadSystem(execute),
2923
3012
  phase: options.phase,
2924
3013
  priority: options.priority ?? DEFAULT_PRIORITY
2925
3014
  };
@@ -2982,12 +3071,12 @@ function getMouseInput(ctx) {
2982
3071
 
2983
3072
  // src/events/pointerInputEvents.ts
2984
3073
  var import_math7 = require("@woven-canvas/math");
2985
- var import_core47 = require("@woven-ecs/core");
3074
+ var import_core50 = require("@woven-ecs/core");
2986
3075
 
2987
3076
  // src/helpers/blockDefs.ts
2988
- var import_core37 = require("@woven-ecs/core");
3077
+ var import_core38 = require("@woven-ecs/core");
2989
3078
  function getBlockDefs(ctx) {
2990
- const { editor } = (0, import_core37.getResources)(ctx);
3079
+ const { editor } = (0, import_core38.getResources)(ctx);
2991
3080
  return editor.blockDefs;
2992
3081
  }
2993
3082
  function getBlockDef(ctx, tag) {
@@ -3000,6 +3089,9 @@ function canBlockEdit(ctx, tag) {
3000
3089
  function canBlockInteract(ctx, tag) {
3001
3090
  return getBlockDef(ctx, tag).interactable;
3002
3091
  }
3092
+ function canBlockSnap(ctx, tag) {
3093
+ return getBlockDef(ctx, tag).snappable;
3094
+ }
3003
3095
  function getBlockResizeMode(ctx, entityId) {
3004
3096
  const block = Block.read(ctx, entityId);
3005
3097
  if (block.resizeMode !== ResizeMode.Default) {
@@ -3011,9 +3103,9 @@ function getBlockResizeMode(ctx, entityId) {
3011
3103
 
3012
3104
  // src/helpers/computeAabb.ts
3013
3105
  var import_math4 = require("@woven-canvas/math");
3014
- var import_core38 = require("@woven-ecs/core");
3106
+ var import_core39 = require("@woven-ecs/core");
3015
3107
  function computeAabb(ctx, entityId, out) {
3016
- if ((0, import_core38.hasComponent)(ctx, entityId, HitGeometry)) {
3108
+ if ((0, import_core39.hasComponent)(ctx, entityId, HitGeometry)) {
3017
3109
  const pts = HitGeometry.getExtremaWorld(ctx, entityId);
3018
3110
  import_math4.Aabb.setFromPoints(out, pts);
3019
3111
  } else {
@@ -3022,6 +3114,17 @@ function computeAabb(ctx, entityId, out) {
3022
3114
  }
3023
3115
  }
3024
3116
 
3117
+ // src/helpers/createBlock.ts
3118
+ var import_canvas_store40 = require("@woven-ecs/canvas-store");
3119
+ var import_core40 = require("@woven-ecs/core");
3120
+ function createBlock(ctx, block) {
3121
+ const entityId = (0, import_core40.createEntity)(ctx);
3122
+ (0, import_core40.addComponent)(ctx, entityId, import_canvas_store40.Synced, { id: crypto.randomUUID() });
3123
+ (0, import_core40.addComponent)(ctx, entityId, Block, { ...block, rank: block.rank ?? RankBounds.genNext(ctx) });
3124
+ PlaceBlockEvent.spawn(ctx, { entityId });
3125
+ return entityId;
3126
+ }
3127
+
3025
3128
  // src/helpers/embed.ts
3026
3129
  function detectEmbedProvider(url) {
3027
3130
  try {
@@ -3153,10 +3256,10 @@ function resolveEmbedUrl(url, provider) {
3153
3256
  }
3154
3257
 
3155
3258
  // src/helpers/findFrameAtPoint.ts
3156
- var import_core40 = require("@woven-ecs/core");
3259
+ var import_core42 = require("@woven-ecs/core");
3157
3260
 
3158
3261
  // src/helpers/hierarchy.ts
3159
- var import_core39 = require("@woven-ecs/core");
3262
+ var import_core41 = require("@woven-ecs/core");
3160
3263
  function getWorldPosition(ctx, entityId, out) {
3161
3264
  return Block.getWorldPosition(ctx, entityId, out);
3162
3265
  }
@@ -3164,7 +3267,7 @@ function worldToLocal(ctx, entityId, worldPos, out) {
3164
3267
  const result = out ?? [0, 0];
3165
3268
  const block = Block.read(ctx, entityId);
3166
3269
  const parentId = block.parentId;
3167
- if (parentId === null || !(0, import_core39.hasComponent)(ctx, parentId, Block)) {
3270
+ if (parentId === null || !(0, import_core41.hasComponent)(ctx, parentId, Block)) {
3168
3271
  result[0] = worldPos[0];
3169
3272
  result[1] = worldPos[1];
3170
3273
  return result;
@@ -3177,7 +3280,7 @@ function worldToLocal(ctx, entityId, worldPos, out) {
3177
3280
  function isAncestor(ctx, entityId, candidateAncestor) {
3178
3281
  let current = entityId;
3179
3282
  const visited = /* @__PURE__ */ new Set();
3180
- while ((0, import_core39.hasComponent)(ctx, current, Block)) {
3283
+ while ((0, import_core41.hasComponent)(ctx, current, Block)) {
3181
3284
  const block = Block.read(ctx, current);
3182
3285
  const parentId = block.parentId;
3183
3286
  if (parentId === null) break;
@@ -3189,11 +3292,11 @@ function isAncestor(ctx, entityId, candidateAncestor) {
3189
3292
  return false;
3190
3293
  }
3191
3294
  function getChildren(ctx, parentId) {
3192
- return [...(0, import_core39.getBackrefs)(ctx, parentId, Block, "parentId")];
3295
+ return [...(0, import_core41.getBackrefs)(ctx, parentId, Block, "parentId")];
3193
3296
  }
3194
3297
  function getDescendants(ctx, parentId) {
3195
3298
  const result = [];
3196
- const children = (0, import_core39.getBackrefs)(ctx, parentId, Block, "parentId");
3299
+ const children = (0, import_core41.getBackrefs)(ctx, parentId, Block, "parentId");
3197
3300
  for (const childId of children) {
3198
3301
  result.push(childId);
3199
3302
  result.push(...getDescendants(ctx, childId));
@@ -3201,11 +3304,11 @@ function getDescendants(ctx, parentId) {
3201
3304
  return result;
3202
3305
  }
3203
3306
  function cascadeDelete(ctx, entityId) {
3204
- const children = (0, import_core39.getBackrefs)(ctx, entityId, Block, "parentId");
3307
+ const children = (0, import_core41.getBackrefs)(ctx, entityId, Block, "parentId");
3205
3308
  for (const childId of children) {
3206
3309
  cascadeDelete(ctx, childId);
3207
3310
  }
3208
- (0, import_core39.removeEntity)(ctx, entityId);
3311
+ (0, import_core41.removeEntity)(ctx, entityId);
3209
3312
  }
3210
3313
 
3211
3314
  // src/helpers/sortBlocks.ts
@@ -3235,16 +3338,19 @@ function compareRankChains(a, b) {
3235
3338
  function compareBlockOrder(getBlock, a, b) {
3236
3339
  const stratumDiff = STRATUM_ORDER[a.stratum] - STRATUM_ORDER[b.stratum];
3237
3340
  if (stratumDiff !== 0) return stratumDiff;
3341
+ const aLayer = a.layerRank ?? "";
3342
+ const bLayer = b.layerRank ?? "";
3343
+ if (aLayer !== bLayer) return aLayer < bLayer ? -1 : 1;
3238
3344
  const chainA = getRankChain(getBlock, a);
3239
3345
  const chainB = getRankChain(getBlock, b);
3240
3346
  return compareRankChains(chainA, chainB);
3241
3347
  }
3242
3348
 
3243
3349
  // src/helpers/findFrameAtPoint.ts
3244
- var frameQuery = (0, import_core40.defineQuery)((q) => q.with(Block, Frame));
3350
+ var frameQuery = (0, import_core42.defineQuery)((q) => q.with(Block, Frame));
3245
3351
  function getBlockDef2(ctx) {
3246
3352
  return (id) => {
3247
- if (!(0, import_core40.hasComponent)(ctx, id, Block)) return null;
3353
+ if (!(0, import_core42.hasComponent)(ctx, id, Block)) return null;
3248
3354
  const block = Block.read(ctx, id);
3249
3355
  return { rank: block.rank, stratum: "content", parentId: block.parentId };
3250
3356
  };
@@ -3255,7 +3361,7 @@ function findFrameAtPoint(ctx, point, exclude = null) {
3255
3361
  const getBlock = getBlockDef2(ctx);
3256
3362
  for (const frameId of frameQuery.current(ctx)) {
3257
3363
  if (frameId === exclude) continue;
3258
- if ((0, import_core40.hasComponent)(ctx, frameId, Selected)) continue;
3364
+ if ((0, import_core42.hasComponent)(ctx, frameId, Selected)) continue;
3259
3365
  if (!Aabb.containsPoint(ctx, frameId, point)) continue;
3260
3366
  if (exclude !== null && isAncestor(ctx, frameId, exclude)) continue;
3261
3367
  const block = Block.read(ctx, frameId);
@@ -3269,22 +3375,40 @@ function findFrameAtPoint(ctx, point, exclude = null) {
3269
3375
  }
3270
3376
 
3271
3377
  // src/helpers/held.ts
3272
- var import_core41 = require("@woven-ecs/core");
3378
+ var import_core43 = require("@woven-ecs/core");
3273
3379
  function isHeldByRemote(ctx, entityId) {
3274
- if (!(0, import_core41.hasComponent)(ctx, entityId, Held)) return false;
3275
- const { sessionId } = (0, import_core41.getResources)(ctx);
3380
+ if (!(0, import_core43.hasComponent)(ctx, entityId, Held)) return false;
3381
+ const { sessionId } = (0, import_core43.getResources)(ctx);
3276
3382
  const held = Held.read(ctx, entityId);
3277
3383
  return held.sessionId !== "" && held.sessionId !== sessionId;
3278
3384
  }
3279
3385
 
3280
3386
  // src/helpers/intersect.ts
3281
3387
  var import_math5 = require("@woven-canvas/math");
3282
- var import_core42 = require("@woven-ecs/core");
3283
- var blocksWithAabb = (0, import_core42.defineQuery)((q) => q.with(Block, Aabb));
3388
+ var import_core45 = require("@woven-ecs/core");
3389
+
3390
+ // src/helpers/layerRank.ts
3391
+ var import_core44 = require("@woven-ecs/core");
3392
+ function resolveLayerRank(ctx, entityId) {
3393
+ if (!(0, import_core44.hasComponent)(ctx, entityId, Block)) return "";
3394
+ const layerId = Block.read(ctx, entityId).layerId;
3395
+ if (layerId !== null && (0, import_core44.hasComponent)(ctx, layerId, Layer)) {
3396
+ return Layer.read(ctx, layerId).rank;
3397
+ }
3398
+ return "";
3399
+ }
3400
+ function isLayerInteractive(ctx, layerId) {
3401
+ if (layerId === null || !(0, import_core44.hasComponent)(ctx, layerId, Layer)) return true;
3402
+ const layer = Layer.read(ctx, layerId);
3403
+ return !layer.hidden && !layer.locked;
3404
+ }
3405
+
3406
+ // src/helpers/intersect.ts
3407
+ var blocksWithAabb = (0, import_core45.defineQuery)((q) => q.with(Block, Aabb));
3284
3408
  function isPointInsideAncestorFrames(ctx, entityId, point) {
3285
3409
  let current = Block.read(ctx, entityId).parentId;
3286
- while (current !== null && (0, import_core42.hasComponent)(ctx, current, Block)) {
3287
- if ((0, import_core42.hasComponent)(ctx, current, Frame)) {
3410
+ while (current !== null && (0, import_core45.hasComponent)(ctx, current, Block)) {
3411
+ if ((0, import_core45.hasComponent)(ctx, current, Frame)) {
3288
3412
  if (!Aabb.containsPoint(ctx, current, point)) {
3289
3413
  return false;
3290
3414
  }
@@ -3295,8 +3419,8 @@ function isPointInsideAncestorFrames(ctx, entityId, point) {
3295
3419
  }
3296
3420
  function isAabbInsideAncestorFrames(ctx, entityId, bounds) {
3297
3421
  let current = Block.read(ctx, entityId).parentId;
3298
- while (current !== null && (0, import_core42.hasComponent)(ctx, current, Block)) {
3299
- if ((0, import_core42.hasComponent)(ctx, current, Frame)) {
3422
+ while (current !== null && (0, import_core45.hasComponent)(ctx, current, Block)) {
3423
+ if ((0, import_core45.hasComponent)(ctx, current, Frame)) {
3300
3424
  const { value: frameAabb } = Aabb.read(ctx, current);
3301
3425
  if (!import_math5.Aabb.intersects(bounds, frameAabb)) {
3302
3426
  return false;
@@ -3312,10 +3436,11 @@ function intersectPoint(ctx, point, entityIds) {
3312
3436
  for (const entityId of entities) {
3313
3437
  const block = Block.read(ctx, entityId);
3314
3438
  if (!canBlockInteract(ctx, block.tag)) continue;
3439
+ if (!isLayerInteractive(ctx, block.layerId)) continue;
3315
3440
  if (!Aabb.containsPoint(ctx, entityId, point)) {
3316
3441
  continue;
3317
3442
  }
3318
- if ((0, import_core42.hasComponent)(ctx, entityId, HitGeometry)) {
3443
+ if ((0, import_core45.hasComponent)(ctx, entityId, HitGeometry)) {
3319
3444
  if (!HitGeometry.containsPointWorld(ctx, entityId, point)) {
3320
3445
  continue;
3321
3446
  }
@@ -3337,12 +3462,13 @@ function intersectAabb(ctx, bounds, entityIds) {
3337
3462
  for (const entityId of entities) {
3338
3463
  const block = Block.read(ctx, entityId);
3339
3464
  if (!canBlockInteract(ctx, block.tag)) continue;
3465
+ if (!isLayerInteractive(ctx, block.layerId)) continue;
3340
3466
  const { value: entityAabb } = Aabb.read(ctx, entityId);
3341
3467
  if (!import_math5.Aabb.intersects(bounds, entityAabb)) {
3342
3468
  continue;
3343
3469
  }
3344
3470
  if (!import_math5.Aabb.contains(bounds, entityAabb)) {
3345
- if ((0, import_core42.hasComponent)(ctx, entityId, HitGeometry)) {
3471
+ if ((0, import_core45.hasComponent)(ctx, entityId, HitGeometry)) {
3346
3472
  if (!HitGeometry.intersectsAabbWorld(ctx, entityId, bounds)) {
3347
3473
  continue;
3348
3474
  }
@@ -3361,12 +3487,13 @@ function intersectAabb(ctx, bounds, entityIds) {
3361
3487
  }
3362
3488
  function sortByRankDescending(ctx, entityIds) {
3363
3489
  const getBlock = (id) => {
3364
- if (!(0, import_core42.hasComponent)(ctx, id, Block)) return null;
3490
+ if (!(0, import_core45.hasComponent)(ctx, id, Block)) return null;
3365
3491
  const block = Block.read(ctx, id);
3366
3492
  return {
3367
3493
  rank: block.rank,
3368
3494
  stratum: getBlockDef(ctx, block.tag).stratum,
3369
- parentId: block.parentId
3495
+ parentId: block.parentId,
3496
+ layerRank: resolveLayerRank(ctx, id)
3370
3497
  };
3371
3498
  };
3372
3499
  const entries = entityIds.map((id) => ({
@@ -3383,12 +3510,14 @@ function getTopmostBlockAtPoint(ctx, point) {
3383
3510
 
3384
3511
  // src/helpers/intersectCapsule.ts
3385
3512
  var import_math6 = require("@woven-canvas/math");
3386
- var import_core43 = require("@woven-ecs/core");
3513
+ var import_core46 = require("@woven-ecs/core");
3387
3514
  var _uvToWorldMatrix2 = [1, 0, 0, 1, 0, 0];
3515
+ var _worldPolygon2 = new Float32Array(MAX_HIT_POLYGON_POINTS * 2);
3516
+ var _polyPt2 = [0, 0];
3388
3517
  function isCapsuleInsideAncestorFrames(ctx, entityId, capsule) {
3389
3518
  let current = Block.read(ctx, entityId).parentId;
3390
- while (current !== null && (0, import_core43.hasComponent)(ctx, current, Block)) {
3391
- if ((0, import_core43.hasComponent)(ctx, current, Frame)) {
3519
+ while (current !== null && (0, import_core46.hasComponent)(ctx, current, Block)) {
3520
+ if ((0, import_core46.hasComponent)(ctx, current, Frame)) {
3392
3521
  const { value: frameAabb } = Aabb.read(ctx, current);
3393
3522
  if (!import_math6.Capsule.intersectsAabb(capsule, frameAabb)) {
3394
3523
  return false;
@@ -3406,7 +3535,7 @@ function intersectCapsule(ctx, capsule, entityIds) {
3406
3535
  if (!import_math6.Aabb.intersects(capsuleBounds, aabb.value)) {
3407
3536
  continue;
3408
3537
  }
3409
- if ((0, import_core43.hasComponent)(ctx, entityId, HitGeometry)) {
3538
+ if ((0, import_core46.hasComponent)(ctx, entityId, HitGeometry)) {
3410
3539
  if (!intersectsCapsuleHitGeometry(ctx, entityId, capsule)) {
3411
3540
  continue;
3412
3541
  }
@@ -3453,12 +3582,25 @@ function intersectsCapsuleHitGeometry(ctx, entityId, capsule) {
3453
3582
  return true;
3454
3583
  }
3455
3584
  }
3585
+ const polygonPointCount = hitGeometry.polygonPointCount;
3586
+ if (polygonPointCount > 0) {
3587
+ for (let i = 0; i < polygonPointCount; i++) {
3588
+ _polyPt2[0] = hitGeometry.hitPolygon[i * 2];
3589
+ _polyPt2[1] = hitGeometry.hitPolygon[i * 2 + 1];
3590
+ import_math6.Mat2.transformPoint(_uvToWorldMatrix2, _polyPt2);
3591
+ _worldPolygon2[i * 2] = _polyPt2[0];
3592
+ _worldPolygon2[i * 2 + 1] = _polyPt2[1];
3593
+ }
3594
+ if (import_math6.Polygon.intersectsCapsule(_worldPolygon2, polygonPointCount, capsule)) {
3595
+ return true;
3596
+ }
3597
+ }
3456
3598
  return false;
3457
3599
  }
3458
3600
 
3459
3601
  // src/helpers/ref.ts
3460
- var import_canvas_store39 = require("@woven-ecs/canvas-store");
3461
- var import_core44 = require("@woven-ecs/core");
3602
+ var import_canvas_store41 = require("@woven-ecs/canvas-store");
3603
+ var import_core47 = require("@woven-ecs/core");
3462
3604
  function convertRefsToUuids(ctx, schema, data) {
3463
3605
  const result = { ...data };
3464
3606
  for (const [fieldName, fieldBuilder] of Object.entries(schema)) {
@@ -3471,8 +3613,8 @@ function convertRefsToUuids(ctx, schema, data) {
3471
3613
  }
3472
3614
  function getEntityUuid(ctx, entityId) {
3473
3615
  if (entityId == null) return null;
3474
- if (!(0, import_core44.hasComponent)(ctx, entityId, import_canvas_store39.Synced)) return null;
3475
- return import_canvas_store39.Synced.read(ctx, entityId).id;
3616
+ if (!(0, import_core47.hasComponent)(ctx, entityId, import_canvas_store41.Synced)) return null;
3617
+ return import_canvas_store41.Synced.read(ctx, entityId).id;
3476
3618
  }
3477
3619
  function getRefFieldNames(schema) {
3478
3620
  const refFields = [];
@@ -3496,26 +3638,26 @@ function resolveRefFields(ctx, entityId, componentDef, componentData, uuidToEnti
3496
3638
  }
3497
3639
 
3498
3640
  // src/helpers/select.ts
3499
- var import_core45 = require("@woven-ecs/core");
3500
- var selectedQuery = (0, import_core45.defineQuery)((q) => q.with(Selected));
3501
- var blockQuery = (0, import_core45.defineQuery)((q) => q.with(Block));
3641
+ var import_core48 = require("@woven-ecs/core");
3642
+ var selectedQuery = (0, import_core48.defineQuery)((q) => q.with(Selected));
3643
+ var blockQuery = (0, import_core48.defineQuery)((q) => q.with(Block));
3502
3644
  function selectBlock(ctx, entityId) {
3503
- if ((0, import_core45.hasComponent)(ctx, entityId, Selected)) return;
3645
+ if ((0, import_core48.hasComponent)(ctx, entityId, Selected)) return;
3504
3646
  if (isHeldByRemote(ctx, entityId)) return;
3505
- if ((0, import_core45.hasComponent)(ctx, entityId, Block)) {
3647
+ if ((0, import_core48.hasComponent)(ctx, entityId, Block)) {
3506
3648
  const block = Block.read(ctx, entityId);
3507
3649
  if (!canBlockInteract(ctx, block.tag)) return;
3508
3650
  }
3509
- const { sessionId } = (0, import_core45.getResources)(ctx);
3510
- (0, import_core45.addComponent)(ctx, entityId, Selected, {});
3511
- if (!(0, import_core45.hasComponent)(ctx, entityId, Held)) {
3512
- (0, import_core45.addComponent)(ctx, entityId, Held, { sessionId });
3651
+ const { sessionId } = (0, import_core48.getResources)(ctx);
3652
+ (0, import_core48.addComponent)(ctx, entityId, Selected, {});
3653
+ if (!(0, import_core48.hasComponent)(ctx, entityId, Held)) {
3654
+ (0, import_core48.addComponent)(ctx, entityId, Held, { sessionId });
3513
3655
  }
3514
3656
  }
3515
3657
  function deselectBlock(ctx, entityId) {
3516
- if (!(0, import_core45.hasComponent)(ctx, entityId, Selected)) return;
3517
- (0, import_core45.removeComponent)(ctx, entityId, Selected);
3518
- (0, import_core45.removeComponent)(ctx, entityId, Held);
3658
+ if (!(0, import_core48.hasComponent)(ctx, entityId, Selected)) return;
3659
+ (0, import_core48.removeComponent)(ctx, entityId, Selected);
3660
+ (0, import_core48.removeComponent)(ctx, entityId, Held);
3519
3661
  }
3520
3662
  function deselectAll(ctx) {
3521
3663
  for (const entityId of selectedQuery.current(ctx)) {
@@ -3529,10 +3671,10 @@ function selectAll(ctx) {
3529
3671
  }
3530
3672
 
3531
3673
  // src/helpers/user.ts
3532
- var import_core46 = require("@woven-ecs/core");
3533
- var userQuery = (0, import_core46.defineQuery)((q) => q.with(User));
3674
+ var import_core49 = require("@woven-ecs/core");
3675
+ var userQuery = (0, import_core49.defineQuery)((q) => q.with(User));
3534
3676
  function getMyUserEntityId(ctx) {
3535
- const mySessionId = (0, import_core46.getResources)(ctx).sessionId;
3677
+ const mySessionId = (0, import_core49.getResources)(ctx).sessionId;
3536
3678
  for (const eid of userQuery.current(ctx)) {
3537
3679
  const user = User.read(ctx, eid);
3538
3680
  if (user.sessionId === mySessionId) {
@@ -3599,7 +3741,7 @@ function generateUuidBySeed(originalUuid, generatorSeed) {
3599
3741
  // src/events/pointerInputEvents.ts
3600
3742
  var DEFAULT_CLICK_MOVE_THRESHOLD = 3;
3601
3743
  var DEFAULT_CLICK_FRAME_THRESHOLD = 30;
3602
- var pointerQuery = (0, import_core47.defineQuery)((q) => q.tracking(Pointer));
3744
+ var pointerQuery = (0, import_core50.defineQuery)((q) => q.tracking(Pointer));
3603
3745
  var trackingState = /* @__PURE__ */ new Map();
3604
3746
  function getTrackingState(ctx) {
3605
3747
  const key = ctx.readerId;
@@ -3722,18 +3864,18 @@ function clearPointerTrackingState(ctx) {
3722
3864
  }
3723
3865
 
3724
3866
  // src/systems/capture/captureFrameContainmentSystem.ts
3725
- var selectedQuery2 = (0, import_core48.defineQuery)((q) => q.with(Block, Selected));
3867
+ var selectedQuery2 = (0, import_core51.defineQuery)((q) => q.with(Block, Selected));
3726
3868
  function spawnAssignments(ctx, targetFrame) {
3727
3869
  const draggedEntity = getDraggedEntity(ctx);
3728
3870
  if (draggedEntity === null) return;
3729
3871
  const assignments = [];
3730
- if ((0, import_core48.hasComponent)(ctx, draggedEntity, TransformBox)) {
3872
+ if ((0, import_core51.hasComponent)(ctx, draggedEntity, TransformBox)) {
3731
3873
  for (const blockId of selectedQuery2.current(ctx)) {
3732
3874
  if (targetFrame !== null && blockId === targetFrame) continue;
3733
3875
  if (targetFrame !== null && isAncestor(ctx, targetFrame, blockId)) continue;
3734
3876
  assignments.push({ entityId: blockId, frameId: targetFrame });
3735
3877
  }
3736
- } else if ((0, import_core48.hasComponent)(ctx, draggedEntity, import_canvas_store40.Synced)) {
3878
+ } else if ((0, import_core51.hasComponent)(ctx, draggedEntity, import_canvas_store42.Synced)) {
3737
3879
  if (targetFrame !== null && draggedEntity === targetFrame) return;
3738
3880
  if (targetFrame !== null && isAncestor(ctx, targetFrame, draggedEntity)) return;
3739
3881
  assignments.push({ entityId: draggedEntity, frameId: targetFrame });
@@ -3832,9 +3974,9 @@ var captureFrameContainmentSystem = defineEditorSystem({ phase: "capture", prior
3832
3974
  });
3833
3975
 
3834
3976
  // src/systems/capture/hoverCursorSystem.ts
3835
- var import_core49 = require("@woven-ecs/core");
3836
- var hoveredHandleQuery = (0, import_core49.defineQuery)((q) => q.with(Hovered, TransformHandle).tracking(Hovered));
3837
- var transformBoxQuery = (0, import_core49.defineQuery)((q) => q.with(Block, TransformBox).tracking(Block));
3977
+ var import_core52 = require("@woven-ecs/core");
3978
+ var hoveredHandleQuery = (0, import_core52.defineQuery)((q) => q.with(Hovered, TransformHandle).tracking(Hovered));
3979
+ var transformBoxQuery = (0, import_core52.defineQuery)((q) => q.with(Block, TransformBox).tracking(Block));
3838
3980
  var hoverCursorSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
3839
3981
  const addedHandles = hoveredHandleQuery.added(ctx);
3840
3982
  const removedHandles = hoveredHandleQuery.removed(ctx);
@@ -3858,18 +4000,18 @@ var hoverCursorSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
3858
4000
  });
3859
4001
 
3860
4002
  // src/systems/capture/keybindSystem.ts
3861
- var import_core50 = require("@woven-ecs/core");
4003
+ var import_core53 = require("@woven-ecs/core");
3862
4004
  var keybindSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
3863
4005
  if (isReadonly(ctx)) return;
3864
4006
  const keyboard = Keyboard.read(ctx);
3865
- const { editor } = (0, import_core50.getResources)(ctx);
4007
+ const { editor } = (0, import_core53.getResources)(ctx);
3866
4008
  for (const keybind of editor.keybinds) {
3867
4009
  let triggered = Keyboard.isKeyDownTrigger(ctx, keybind.key);
3868
4010
  triggered &&= !!keybind.mod === keyboard.modDown;
3869
4011
  triggered &&= !!keybind.shift === keyboard.shiftDown;
3870
4012
  if (triggered) {
3871
- const eid = (0, import_core50.createEntity)(ctx);
3872
- (0, import_core50.addComponent)(ctx, eid, CommandMarker, { name: keybind.command });
4013
+ const eid = (0, import_core53.createEntity)(ctx);
4014
+ (0, import_core53.addComponent)(ctx, eid, CommandMarker, { name: keybind.command });
3873
4015
  break;
3874
4016
  }
3875
4017
  }
@@ -3993,9 +4135,9 @@ var scrollEdgesSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
3993
4135
  });
3994
4136
 
3995
4137
  // src/systems/capture/transformBoxSystem.ts
3996
- var import_core51 = require("@woven-ecs/core");
4138
+ var import_core54 = require("@woven-ecs/core");
3997
4139
  var import_xstate4 = require("xstate");
3998
- var selectedBlocksQuery = (0, import_core51.defineQuery)((q) => q.with(Block).tracking(Selected));
4140
+ var selectedBlocksQuery = (0, import_core54.defineQuery)((q) => q.with(Block).tracking(Selected));
3999
4141
  var transformBoxMachine = (0, import_xstate4.setup)({
4000
4142
  types: {
4001
4143
  context: {},
@@ -4008,12 +4150,12 @@ var transformBoxMachine = (0, import_xstate4.setup)({
4008
4150
  isOverTransformBox: ({ event }) => {
4009
4151
  if (!("intersects" in event)) return false;
4010
4152
  if (event.intersects.length === 0) return false;
4011
- return (0, import_core51.hasComponent)(event.ctx, event.intersects[0], TransformBox);
4153
+ return (0, import_core54.hasComponent)(event.ctx, event.intersects[0], TransformBox);
4012
4154
  },
4013
4155
  isOverTransformHandle: ({ event }) => {
4014
4156
  if (!("intersects" in event)) return false;
4015
4157
  if (event.intersects.length === 0) return false;
4016
- return (0, import_core51.hasComponent)(event.ctx, event.intersects[0], TransformHandle);
4158
+ return (0, import_core54.hasComponent)(event.ctx, event.intersects[0], TransformHandle);
4017
4159
  },
4018
4160
  selectionIsTransformable: ({ event }) => {
4019
4161
  if (event.type !== "selectionChanged") return false;
@@ -4025,7 +4167,7 @@ var transformBoxMachine = (0, import_xstate4.setup)({
4025
4167
  hasEditAfterPlacing: ({ event }) => {
4026
4168
  if (event.type !== "selectionChanged") return false;
4027
4169
  if (event.selectedEntityIds.length !== 1) return false;
4028
- return (0, import_core51.hasComponent)(event.ctx, event.selectedEntityIds[0], EditAfterPlacing);
4170
+ return (0, import_core54.hasComponent)(event.ctx, event.selectedEntityIds[0], EditAfterPlacing);
4029
4171
  }
4030
4172
  },
4031
4173
  actions: {
@@ -4035,7 +4177,7 @@ var transformBoxMachine = (0, import_xstate4.setup)({
4035
4177
  transformBoxId: context.transformBoxId
4036
4178
  });
4037
4179
  }
4038
- const transformBoxId = (0, import_core51.createEntity)(event.ctx);
4180
+ const transformBoxId = (0, import_core54.createEntity)(event.ctx);
4039
4181
  AddTransformBox.spawn(event.ctx, {
4040
4182
  transformBoxId,
4041
4183
  skipHandles: params?.skipHandles
@@ -4078,8 +4220,8 @@ var transformBoxMachine = (0, import_xstate4.setup)({
4078
4220
  removeEditAfterPlacing: ({ event }) => {
4079
4221
  if (event.type !== "selectionChanged") return;
4080
4222
  for (const entityId of event.selectedEntityIds) {
4081
- if ((0, import_core51.hasComponent)(event.ctx, entityId, EditAfterPlacing)) {
4082
- (0, import_core51.removeComponent)(event.ctx, entityId, EditAfterPlacing);
4223
+ if ((0, import_core54.hasComponent)(event.ctx, entityId, EditAfterPlacing)) {
4224
+ (0, import_core54.removeComponent)(event.ctx, entityId, EditAfterPlacing);
4083
4225
  }
4084
4226
  }
4085
4227
  }
@@ -4178,7 +4320,7 @@ var transformBoxSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
4178
4320
  });
4179
4321
 
4180
4322
  // src/systems/input/keyboardSystem.ts
4181
- var import_core52 = require("@woven-ecs/core");
4323
+ var import_core55 = require("@woven-ecs/core");
4182
4324
  var instanceState = /* @__PURE__ */ new WeakMap();
4183
4325
  function attachKeyboardListeners(domElement) {
4184
4326
  if (instanceState.has(domElement)) return;
@@ -4214,7 +4356,7 @@ function detachKeyboardListeners(domElement) {
4214
4356
  instanceState.delete(domElement);
4215
4357
  }
4216
4358
  var keyboardSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4217
- const resources = (0, import_core52.getResources)(ctx);
4359
+ const resources = (0, import_core55.getResources)(ctx);
4218
4360
  if (!resources.domElement) return;
4219
4361
  const state = instanceState.get(resources.domElement);
4220
4362
  if (!state) return;
@@ -4268,7 +4410,7 @@ function isZeroed(buffer) {
4268
4410
  }
4269
4411
 
4270
4412
  // src/systems/input/mouseSystem.ts
4271
- var import_core53 = require("@woven-ecs/core");
4413
+ var import_core56 = require("@woven-ecs/core");
4272
4414
  var instanceState2 = /* @__PURE__ */ new WeakMap();
4273
4415
  function attachMouseListeners(domElement) {
4274
4416
  if (instanceState2.has(domElement)) return;
@@ -4316,7 +4458,7 @@ function detachMouseListeners(domElement) {
4316
4458
  instanceState2.delete(domElement);
4317
4459
  }
4318
4460
  var mouseSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4319
- const resources = (0, import_core53.getResources)(ctx);
4461
+ const resources = (0, import_core56.getResources)(ctx);
4320
4462
  if (!resources.domElement) return;
4321
4463
  const state = instanceState2.get(resources.domElement);
4322
4464
  if (!state) return;
@@ -4373,7 +4515,7 @@ function normalizeWheelDelta(deltaY, deltaMode) {
4373
4515
  }
4374
4516
 
4375
4517
  // src/systems/input/pointerSystem.ts
4376
- var import_core54 = require("@woven-ecs/core");
4518
+ var import_core57 = require("@woven-ecs/core");
4377
4519
  var instanceState3 = /* @__PURE__ */ new WeakMap();
4378
4520
  function attachPointerListeners(domElement) {
4379
4521
  if (instanceState3.has(domElement)) return;
@@ -4468,7 +4610,7 @@ function detachPointerListeners(domElement) {
4468
4610
  instanceState3.delete(domElement);
4469
4611
  }
4470
4612
  var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4471
- const resources = (0, import_core54.getResources)(ctx);
4613
+ const resources = (0, import_core57.getResources)(ctx);
4472
4614
  const { domElement } = resources;
4473
4615
  if (!domElement) return;
4474
4616
  const state = instanceState3.get(domElement);
@@ -4480,8 +4622,8 @@ var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4480
4622
  switch (event.type) {
4481
4623
  case "pointerdown": {
4482
4624
  const position = [event.clientX - screen.left, event.clientY - screen.top];
4483
- const entityId = (0, import_core54.createEntity)(ctx);
4484
- (0, import_core54.addComponent)(ctx, entityId, Pointer, {
4625
+ const entityId = (0, import_core57.createEntity)(ctx);
4626
+ (0, import_core57.addComponent)(ctx, entityId, Pointer, {
4485
4627
  pointerId: event.pointerId,
4486
4628
  position,
4487
4629
  downPosition: position,
@@ -4513,7 +4655,7 @@ var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4513
4655
  const position = [event.clientX - screen.left, event.clientY - screen.top];
4514
4656
  const pointer = Pointer.write(ctx, entityId);
4515
4657
  addPointerSample(pointer, position, time);
4516
- (0, import_core54.removeEntity)(ctx, entityId);
4658
+ (0, import_core57.removeEntity)(ctx, entityId);
4517
4659
  state.pointerEntityMap.delete(event.pointerId);
4518
4660
  break;
4519
4661
  }
@@ -4523,7 +4665,7 @@ var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4523
4665
  });
4524
4666
 
4525
4667
  // src/systems/input/screenSystem.ts
4526
- var import_core55 = require("@woven-ecs/core");
4668
+ var import_core58 = require("@woven-ecs/core");
4527
4669
  var instanceState4 = /* @__PURE__ */ new WeakMap();
4528
4670
  function attachScreenObserver(domElement) {
4529
4671
  if (instanceState4.has(domElement)) return;
@@ -4548,7 +4690,7 @@ function detachScreenObserver(domElement) {
4548
4690
  instanceState4.delete(domElement);
4549
4691
  }
4550
4692
  var screenSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4551
- const resources = (0, import_core55.getResources)(ctx);
4693
+ const resources = (0, import_core58.getResources)(ctx);
4552
4694
  const { domElement } = resources;
4553
4695
  if (!domElement) return;
4554
4696
  const state = instanceState4.get(domElement);
@@ -4583,8 +4725,8 @@ var tickSystem = defineEditorSystem({ phase: "input", priority: 100 }, (ctx) =>
4583
4725
  });
4584
4726
 
4585
4727
  // src/systems/postRender/cursorSystem.ts
4586
- var import_core56 = require("@woven-ecs/core");
4587
- var cursorQuery = (0, import_core56.defineQuery)((q) => q.tracking(Cursor));
4728
+ var import_core59 = require("@woven-ecs/core");
4729
+ var cursorQuery = (0, import_core59.defineQuery)((q) => q.tracking(Cursor));
4588
4730
  function getCursorSvg(cursors, kind, rotateZ) {
4589
4731
  const def = cursors[kind];
4590
4732
  if (!def) return "auto";
@@ -4603,15 +4745,15 @@ var cursorSystem = defineEditorSystem({ phase: "render", priority: -100 }, (ctx)
4603
4745
  document.body.style.cursor = "default";
4604
4746
  return;
4605
4747
  }
4606
- const { editor } = (0, import_core56.getResources)(ctx);
4748
+ const { editor } = (0, import_core59.getResources)(ctx);
4607
4749
  const cursorValue = getCursorSvg(editor.cursors, cursorKind, rotation);
4608
4750
  document.body.style.cursor = cursorValue;
4609
4751
  });
4610
4752
 
4611
4753
  // src/systems/postRender/presenceSystem.ts
4612
- var import_core57 = require("@woven-ecs/core");
4613
- var pointerQuery2 = (0, import_core57.defineQuery)((q) => q.tracking(Pointer));
4614
- var cameraChangedQuery = (0, import_core57.defineQuery)((q) => q.tracking(Camera));
4754
+ var import_core60 = require("@woven-ecs/core");
4755
+ var pointerQuery2 = (0, import_core60.defineQuery)((q) => q.tracking(Pointer));
4756
+ var cameraChangedQuery = (0, import_core60.defineQuery)((q) => q.tracking(Camera));
4615
4757
  var presenceSystem = defineEditorSystem({ phase: "render", priority: -100 }, (ctx) => {
4616
4758
  if (Mouse.didMove(ctx) || cameraChangedQuery.changed(ctx).length > 0) {
4617
4759
  const mouse = Mouse.read(ctx);
@@ -4642,9 +4784,9 @@ function updateUserPosition(ctx, position) {
4642
4784
 
4643
4785
  // src/systems/postUpdate/transformBoxSystem.ts
4644
4786
  var import_math8 = require("@woven-canvas/math");
4645
- var import_core58 = require("@woven-ecs/core");
4646
- var selectedBlocksQuery2 = (0, import_core58.defineQuery)((q) => q.with(Block, Selected));
4647
- var editedBlocksQuery = (0, import_core58.defineQuery)((q) => q.with(Block, Edited));
4787
+ var import_core61 = require("@woven-ecs/core");
4788
+ var selectedBlocksQuery2 = (0, import_core61.defineQuery)((q) => q.with(Block, Selected));
4789
+ var editedBlocksQuery = (0, import_core61.defineQuery)((q) => q.with(Block, Edited));
4648
4790
  var TRANSFORM_BOX_RANK = "a";
4649
4791
  var TRANSFORM_HANDLE_ROTATE_RANK = "b";
4650
4792
  var TRANSFORM_HANDLE_EDGE_RANK = "c";
@@ -4671,27 +4813,27 @@ var transformBoxSystem2 = defineEditorSystem({ phase: "update", priority: -100 }
4671
4813
  on(ctx, EndTransformBoxEdit, endTransformBoxEdit);
4672
4814
  });
4673
4815
  function removeTransformBox(ctx, transformBoxId) {
4674
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) {
4816
+ if (!(0, import_core61.hasComponent)(ctx, transformBoxId, TransformBox)) {
4675
4817
  endTransformBoxEdit(ctx);
4676
4818
  return;
4677
4819
  }
4678
- const handles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4820
+ const handles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4679
4821
  for (const handleId of handles) {
4680
- (0, import_core58.removeEntity)(ctx, handleId);
4822
+ (0, import_core61.removeEntity)(ctx, handleId);
4681
4823
  }
4682
- (0, import_core58.removeEntity)(ctx, transformBoxId);
4824
+ (0, import_core61.removeEntity)(ctx, transformBoxId);
4683
4825
  endTransformBoxEdit(ctx);
4684
4826
  }
4685
4827
  function addTransformBox(ctx, transformBoxId, skipHandles) {
4686
- (0, import_core58.addComponent)(ctx, transformBoxId, Block, {
4828
+ (0, import_core61.addComponent)(ctx, transformBoxId, Block, {
4687
4829
  tag: "transform-box",
4688
4830
  position: [0, 0],
4689
4831
  size: [0, 0],
4690
4832
  rotateZ: 0,
4691
4833
  rank: TRANSFORM_BOX_RANK
4692
4834
  });
4693
- (0, import_core58.addComponent)(ctx, transformBoxId, TransformBox, {});
4694
- (0, import_core58.addComponent)(ctx, transformBoxId, DragStart, {
4835
+ (0, import_core61.addComponent)(ctx, transformBoxId, TransformBox, {});
4836
+ (0, import_core61.addComponent)(ctx, transformBoxId, DragStart, {
4695
4837
  position: [0, 0],
4696
4838
  size: [0, 0],
4697
4839
  rotateZ: 0,
@@ -4702,7 +4844,7 @@ function addTransformBox(ctx, transformBoxId, skipHandles) {
4702
4844
  }
4703
4845
  }
4704
4846
  function updateTransformBox(ctx, transformBoxId) {
4705
- if (!(0, import_core58.isAlive)(ctx, transformBoxId)) return;
4847
+ if (!(0, import_core61.isAlive)(ctx, transformBoxId)) return;
4706
4848
  const selectedBlocks = selectedBlocksQuery2.current(ctx);
4707
4849
  if (selectedBlocks.length === 0) return;
4708
4850
  let rotateZ = 0;
@@ -4728,7 +4870,7 @@ function updateTransformBox(ctx, transformBoxId) {
4728
4870
  const selectionState = SelectionStateSingleton.read(ctx);
4729
4871
  const isDragging = selectionState.state === SelectionState.Dragging;
4730
4872
  if (!isDragging) {
4731
- if ((0, import_core58.hasComponent)(ctx, transformBoxId, DragStart)) {
4873
+ if ((0, import_core61.hasComponent)(ctx, transformBoxId, DragStart)) {
4732
4874
  const dragStart = DragStart.write(ctx, transformBoxId);
4733
4875
  dragStart.position = boxBlock.position;
4734
4876
  dragStart.size = boxBlock.size;
@@ -4737,9 +4879,9 @@ function updateTransformBox(ctx, transformBoxId) {
4737
4879
  for (const entityId of selectedBlocks) {
4738
4880
  const block = Block.read(ctx, entityId);
4739
4881
  const worldPos = Block.getWorldPosition(ctx, entityId);
4740
- const fontSize = (0, import_core58.hasComponent)(ctx, entityId, Text) ? Text.read(ctx, entityId).fontSizePx : 16;
4741
- if (!(0, import_core58.hasComponent)(ctx, entityId, DragStart)) {
4742
- (0, import_core58.addComponent)(ctx, entityId, DragStart, {
4882
+ const fontSize = (0, import_core61.hasComponent)(ctx, entityId, Text) ? Text.read(ctx, entityId).fontSizePx : 16;
4883
+ if (!(0, import_core61.hasComponent)(ctx, entityId, DragStart)) {
4884
+ (0, import_core61.addComponent)(ctx, entityId, DragStart, {
4743
4885
  position: worldPos,
4744
4886
  size: block.size,
4745
4887
  rotateZ: block.rotateZ,
@@ -4903,10 +5045,10 @@ function addOrUpdateTransformHandles(ctx, transformBoxId) {
4903
5045
  });
4904
5046
  }
4905
5047
  }
4906
- const existingHandles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5048
+ const existingHandles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4907
5049
  const handleMap = /* @__PURE__ */ new Map();
4908
5050
  for (const handleId of existingHandles) {
4909
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5051
+ if (!(0, import_core61.isAlive)(ctx, handleId)) continue;
4910
5052
  const handle = TransformHandle.read(ctx, handleId);
4911
5053
  const key = `${handle.kind}-${handle.vectorX}-${handle.vectorY}`;
4912
5054
  handleMap.set(key, handleId);
@@ -4921,11 +5063,11 @@ function addOrUpdateTransformHandles(ctx, transformBoxId) {
4921
5063
  const finalLeft = rotatedCenter[0] - def.width / 2;
4922
5064
  const finalTop = rotatedCenter[1] - def.height / 2;
4923
5065
  if (!handleId) {
4924
- handleId = (0, import_core58.createEntity)(ctx);
4925
- (0, import_core58.addComponent)(ctx, handleId, Block);
4926
- (0, import_core58.addComponent)(ctx, handleId, TransformHandle);
4927
- (0, import_core58.addComponent)(ctx, handleId, DragStart);
4928
- (0, import_core58.addComponent)(ctx, handleId, ScaleWithZoom);
5066
+ handleId = (0, import_core61.createEntity)(ctx);
5067
+ (0, import_core61.addComponent)(ctx, handleId, Block);
5068
+ (0, import_core61.addComponent)(ctx, handleId, TransformHandle);
5069
+ (0, import_core61.addComponent)(ctx, handleId, DragStart);
5070
+ (0, import_core61.addComponent)(ctx, handleId, ScaleWithZoom);
4929
5071
  }
4930
5072
  const block = Block.write(ctx, handleId);
4931
5073
  block.tag = def.tag;
@@ -4952,7 +5094,7 @@ function addOrUpdateTransformHandles(ctx, transformBoxId) {
4952
5094
  }
4953
5095
  for (const handleId of existingHandles) {
4954
5096
  if (!usedHandleIds.has(handleId)) {
4955
- (0, import_core58.removeEntity)(ctx, handleId);
5097
+ (0, import_core61.removeEntity)(ctx, handleId);
4956
5098
  }
4957
5099
  }
4958
5100
  }
@@ -4988,10 +5130,10 @@ function addTapeHandles(ctx, transformBoxId, left, top, width, height, rotateZ,
4988
5130
  scaleMultiplier: [1, 1],
4989
5131
  anchor: [0.5, 0.5]
4990
5132
  });
4991
- const existingHandles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5133
+ const existingHandles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4992
5134
  const handleMap = /* @__PURE__ */ new Map();
4993
5135
  for (const handleId of existingHandles) {
4994
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5136
+ if (!(0, import_core61.isAlive)(ctx, handleId)) continue;
4995
5137
  const handle = TransformHandle.read(ctx, handleId);
4996
5138
  const key = `${handle.kind}-${handle.vectorX}-${handle.vectorY}`;
4997
5139
  handleMap.set(key, handleId);
@@ -5006,11 +5148,11 @@ function addTapeHandles(ctx, transformBoxId, left, top, width, height, rotateZ,
5006
5148
  const finalLeft = rotatedCenter[0] - def.width / 2;
5007
5149
  const finalTop = rotatedCenter[1] - def.height / 2;
5008
5150
  if (!handleId) {
5009
- handleId = (0, import_core58.createEntity)(ctx);
5010
- (0, import_core58.addComponent)(ctx, handleId, Block);
5011
- (0, import_core58.addComponent)(ctx, handleId, TransformHandle);
5012
- (0, import_core58.addComponent)(ctx, handleId, DragStart);
5013
- (0, import_core58.addComponent)(ctx, handleId, ScaleWithZoom);
5151
+ handleId = (0, import_core61.createEntity)(ctx);
5152
+ (0, import_core61.addComponent)(ctx, handleId, Block);
5153
+ (0, import_core61.addComponent)(ctx, handleId, TransformHandle);
5154
+ (0, import_core61.addComponent)(ctx, handleId, DragStart);
5155
+ (0, import_core61.addComponent)(ctx, handleId, ScaleWithZoom);
5014
5156
  }
5015
5157
  const block = Block.write(ctx, handleId);
5016
5158
  block.tag = def.tag;
@@ -5037,23 +5179,23 @@ function addTapeHandles(ctx, transformBoxId, left, top, width, height, rotateZ,
5037
5179
  }
5038
5180
  for (const handleId of existingHandles) {
5039
5181
  if (!usedHandleIds.has(handleId)) {
5040
- (0, import_core58.removeEntity)(ctx, handleId);
5182
+ (0, import_core61.removeEntity)(ctx, handleId);
5041
5183
  }
5042
5184
  }
5043
5185
  }
5044
5186
  function hideTransformBox(ctx, transformBoxId) {
5045
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) return;
5046
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, Opacity)) {
5047
- (0, import_core58.addComponent)(ctx, transformBoxId, Opacity, { value: 0 });
5187
+ if (!(0, import_core61.hasComponent)(ctx, transformBoxId, TransformBox)) return;
5188
+ if (!(0, import_core61.hasComponent)(ctx, transformBoxId, Opacity)) {
5189
+ (0, import_core61.addComponent)(ctx, transformBoxId, Opacity, { value: 0 });
5048
5190
  } else {
5049
5191
  const opacity = Opacity.write(ctx, transformBoxId);
5050
5192
  opacity.value = 0;
5051
5193
  }
5052
- const handles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5194
+ const handles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5053
5195
  for (const handleId of handles) {
5054
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5055
- if (!(0, import_core58.hasComponent)(ctx, handleId, Opacity)) {
5056
- (0, import_core58.addComponent)(ctx, handleId, Opacity, { value: 0 });
5196
+ if (!(0, import_core61.isAlive)(ctx, handleId)) continue;
5197
+ if (!(0, import_core61.hasComponent)(ctx, handleId, Opacity)) {
5198
+ (0, import_core61.addComponent)(ctx, handleId, Opacity, { value: 0 });
5057
5199
  } else {
5058
5200
  const opacity = Opacity.write(ctx, handleId);
5059
5201
  opacity.value = 0;
@@ -5061,63 +5203,63 @@ function hideTransformBox(ctx, transformBoxId) {
5061
5203
  }
5062
5204
  }
5063
5205
  function showTransformBox(ctx, transformBoxId) {
5064
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) return;
5206
+ if (!(0, import_core61.hasComponent)(ctx, transformBoxId, TransformBox)) return;
5065
5207
  const selectedBlocks = selectedBlocksQuery2.current(ctx);
5066
5208
  if (selectedBlocks.length === 0) return;
5067
5209
  if (selectedBlocks.length === 1) {
5068
5210
  if (getBlockResizeMode(ctx, selectedBlocks[0]) === ResizeMode.GroupOnly) return;
5069
5211
  }
5070
- if ((0, import_core58.hasComponent)(ctx, transformBoxId, Opacity)) {
5071
- (0, import_core58.removeComponent)(ctx, transformBoxId, Opacity);
5212
+ if ((0, import_core61.hasComponent)(ctx, transformBoxId, Opacity)) {
5213
+ (0, import_core61.removeComponent)(ctx, transformBoxId, Opacity);
5072
5214
  }
5073
- const handles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5215
+ const handles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5074
5216
  for (const handleId of handles) {
5075
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5076
- if ((0, import_core58.hasComponent)(ctx, handleId, Opacity)) {
5077
- (0, import_core58.removeComponent)(ctx, handleId, Opacity);
5217
+ if (!(0, import_core61.isAlive)(ctx, handleId)) continue;
5218
+ if ((0, import_core61.hasComponent)(ctx, handleId, Opacity)) {
5219
+ (0, import_core61.removeComponent)(ctx, handleId, Opacity);
5078
5220
  }
5079
5221
  }
5080
5222
  updateTransformBox(ctx, transformBoxId);
5081
5223
  }
5082
5224
  function startTransformBoxEdit(ctx, transformBoxId) {
5083
- if ((0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) {
5084
- const handles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5225
+ if ((0, import_core61.hasComponent)(ctx, transformBoxId, TransformBox)) {
5226
+ const handles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5085
5227
  for (const handleId of handles) {
5086
- (0, import_core58.removeEntity)(ctx, handleId);
5228
+ (0, import_core61.removeEntity)(ctx, handleId);
5087
5229
  }
5088
5230
  }
5089
5231
  for (const blockId of selectedBlocksQuery2.current(ctx)) {
5090
- if (!(0, import_core58.hasComponent)(ctx, blockId, Edited)) {
5091
- (0, import_core58.addComponent)(ctx, blockId, Edited, {});
5232
+ if (!(0, import_core61.hasComponent)(ctx, blockId, Edited)) {
5233
+ (0, import_core61.addComponent)(ctx, blockId, Edited, {});
5092
5234
  }
5093
5235
  }
5094
5236
  }
5095
5237
  function endTransformBoxEdit(ctx) {
5096
5238
  for (const blockId of editedBlocksQuery.current(ctx)) {
5097
- if ((0, import_core58.hasComponent)(ctx, blockId, Edited)) {
5098
- (0, import_core58.removeComponent)(ctx, blockId, Edited);
5239
+ if ((0, import_core61.hasComponent)(ctx, blockId, Edited)) {
5240
+ (0, import_core61.removeComponent)(ctx, blockId, Edited);
5099
5241
  }
5100
5242
  }
5101
5243
  }
5102
5244
 
5103
5245
  // src/systems/preCapture/intersectSystem.ts
5104
- var import_core59 = require("@woven-ecs/core");
5105
- var blocksChanged = (0, import_core59.defineQuery)((q) => q.tracking(Block));
5106
- var hitGeometryChanged = (0, import_core59.defineQuery)((q) => q.tracking(HitGeometry));
5107
- var heldQuery = (0, import_core59.defineQuery)((q) => q.with(Held).tracking(Held));
5108
- var hoveredQuery = (0, import_core59.defineQuery)((q) => q.with(Hovered));
5109
- var pointerQuery3 = (0, import_core59.defineQuery)((q) => q.with(Pointer));
5246
+ var import_core62 = require("@woven-ecs/core");
5247
+ var blocksChanged = (0, import_core62.defineQuery)((q) => q.tracking(Block));
5248
+ var hitGeometryChanged = (0, import_core62.defineQuery)((q) => q.tracking(HitGeometry));
5249
+ var heldQuery = (0, import_core62.defineQuery)((q) => q.with(Held).tracking(Held));
5250
+ var hoveredQuery = (0, import_core62.defineQuery)((q) => q.with(Hovered));
5251
+ var pointerQuery3 = (0, import_core62.defineQuery)((q) => q.with(Pointer));
5110
5252
  function clearHovered(ctx) {
5111
5253
  for (const entityId of hoveredQuery.current(ctx)) {
5112
- if ((0, import_core59.hasComponent)(ctx, entityId, Hovered)) {
5113
- (0, import_core59.removeComponent)(ctx, entityId, Hovered);
5254
+ if ((0, import_core62.hasComponent)(ctx, entityId, Hovered)) {
5255
+ (0, import_core62.removeComponent)(ctx, entityId, Hovered);
5114
5256
  }
5115
5257
  }
5116
5258
  }
5117
5259
  function findValidHoverTarget(ctx, intersected) {
5118
5260
  for (const entityId of intersected) {
5119
5261
  if (isHeldByRemote(ctx, entityId)) return void 0;
5120
- if ((0, import_core59.hasComponent)(ctx, entityId, Block)) {
5262
+ if ((0, import_core62.hasComponent)(ctx, entityId, Block)) {
5121
5263
  const block = Block.read(ctx, entityId);
5122
5264
  if (!canBlockInteract(ctx, block.tag)) continue;
5123
5265
  }
@@ -5132,7 +5274,7 @@ function updateHovered(ctx, intersected) {
5132
5274
  if (!selectToolActive) return;
5133
5275
  const newHoveredId = findValidHoverTarget(ctx, intersected);
5134
5276
  if (newHoveredId !== void 0) {
5135
- (0, import_core59.addComponent)(ctx, newHoveredId, Hovered, {});
5277
+ (0, import_core62.addComponent)(ctx, newHoveredId, Hovered, {});
5136
5278
  }
5137
5279
  }
5138
5280
  function arraysEqual(a, b) {
@@ -5145,7 +5287,7 @@ function arraysEqual(a, b) {
5145
5287
  var added = /* @__PURE__ */ new Set();
5146
5288
  var changed = /* @__PURE__ */ new Set();
5147
5289
  function addDescendants(ctx, entityId, set) {
5148
- for (const childId of (0, import_core59.getBackrefs)(ctx, entityId, Block, "parentId")) {
5290
+ for (const childId of (0, import_core62.getBackrefs)(ctx, entityId, Block, "parentId")) {
5149
5291
  if (set.has(childId)) continue;
5150
5292
  set.add(childId);
5151
5293
  addDescendants(ctx, childId, set);
@@ -5171,14 +5313,14 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
5171
5313
  addDescendants(ctx, entityId, changed);
5172
5314
  }
5173
5315
  for (const entityId of added) {
5174
- if (!(0, import_core59.hasComponent)(ctx, entityId, Aabb)) {
5175
- (0, import_core59.addComponent)(ctx, entityId, Aabb, { value: [0, 0, 0, 0] });
5316
+ if (!(0, import_core62.hasComponent)(ctx, entityId, Aabb)) {
5317
+ (0, import_core62.addComponent)(ctx, entityId, Aabb, { value: [0, 0, 0, 0] });
5176
5318
  }
5177
5319
  const aabb = Aabb.write(ctx, entityId);
5178
5320
  computeAabb(ctx, entityId, aabb.value);
5179
5321
  }
5180
5322
  for (const entityId of changed) {
5181
- if (!(0, import_core59.hasComponent)(ctx, entityId, Aabb)) continue;
5323
+ if (!(0, import_core62.hasComponent)(ctx, entityId, Aabb)) continue;
5182
5324
  const aabb = Aabb.write(ctx, entityId);
5183
5325
  computeAabb(ctx, entityId, aabb.value);
5184
5326
  }
@@ -5197,7 +5339,7 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
5197
5339
  clearHovered(ctx);
5198
5340
  return;
5199
5341
  }
5200
- const resources = (0, import_core59.getResources)(ctx);
5342
+ const resources = (0, import_core62.getResources)(ctx);
5201
5343
  const isObscured = Mouse.isObscured(ctx);
5202
5344
  const wasObscured = resources.domElement ? prevObscuredByElement.get(resources.domElement) ?? false : false;
5203
5345
  const obscuredChanged = isObscured !== wasObscured;
@@ -5227,12 +5369,21 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
5227
5369
 
5228
5370
  // src/systems/preCapture/selectSystem.ts
5229
5371
  var import_math9 = require("@woven-canvas/math");
5230
- var import_canvas_store41 = require("@woven-ecs/canvas-store");
5231
- var import_core60 = require("@woven-ecs/core");
5372
+ var import_canvas_store43 = require("@woven-ecs/canvas-store");
5373
+ var import_core63 = require("@woven-ecs/core");
5232
5374
  var import_xstate5 = require("xstate");
5233
5375
  var POINTING_THRESHOLD = 4;
5234
- var selectedBlocksQuery3 = (0, import_core60.defineQuery)((q) => q.with(Block, Selected, import_canvas_store41.Synced));
5235
- var editedBlocksQuery2 = (0, import_core60.defineQuery)((q) => q.with(Block, Edited));
5376
+ var selectedBlocksQuery3 = (0, import_core63.defineQuery)((q) => q.with(Block, Selected, import_canvas_store43.Synced));
5377
+ var editedBlocksQuery2 = (0, import_core63.defineQuery)((q) => q.with(Block, Edited));
5378
+ function isDragSnappable(ctx, draggedEntity) {
5379
+ if ((0, import_core63.hasComponent)(ctx, draggedEntity, TransformBox)) {
5380
+ for (const blockId of selectedBlocksQuery3.current(ctx)) {
5381
+ if (canBlockSnap(ctx, Block.read(ctx, blockId).tag)) return true;
5382
+ }
5383
+ return false;
5384
+ }
5385
+ return canBlockSnap(ctx, Block.read(ctx, draggedEntity).tag);
5386
+ }
5236
5387
  var selectionMachine = (0, import_xstate5.setup)({
5237
5388
  types: {
5238
5389
  context: {},
@@ -5248,13 +5399,13 @@ var selectionMachine = (0, import_xstate5.setup)({
5248
5399
  },
5249
5400
  hasDraggedEntity: ({ context, event }) => {
5250
5401
  if (context.draggedEntity === null) return false;
5251
- return (0, import_core60.hasComponent)(event.ctx, context.draggedEntity, import_canvas_store41.Synced);
5402
+ return (0, import_core63.hasComponent)(event.ctx, context.draggedEntity, import_canvas_store43.Synced);
5252
5403
  },
5253
5404
  isOverSyncedBlock: ({ event }) => {
5254
5405
  const ctx = event.ctx;
5255
5406
  for (const entityId of event.intersects) {
5256
- if ((0, import_core60.hasComponent)(ctx, entityId, import_canvas_store41.Synced)) {
5257
- if ((0, import_core60.hasComponent)(ctx, entityId, Block)) {
5407
+ if ((0, import_core63.hasComponent)(ctx, entityId, import_canvas_store43.Synced)) {
5408
+ if ((0, import_core63.hasComponent)(ctx, entityId, Block)) {
5258
5409
  const block = Block.read(ctx, entityId);
5259
5410
  if (!canBlockInteract(ctx, block.tag)) continue;
5260
5411
  }
@@ -5291,7 +5442,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5291
5442
  },
5292
5443
  setDragCursor: ({ event, context }) => {
5293
5444
  if (!context.draggedEntity) return;
5294
- if ((0, import_core60.hasComponent)(event.ctx, context.draggedEntity, TransformHandle)) {
5445
+ if ((0, import_core63.hasComponent)(event.ctx, context.draggedEntity, TransformHandle)) {
5295
5446
  const handle = TransformHandle.read(event.ctx, context.draggedEntity);
5296
5447
  const transformBoxBlock = handle.transformBox !== null ? Block.read(event.ctx, handle.transformBox) : void 0;
5297
5448
  SetCursor.spawn(event.ctx, {
@@ -5351,7 +5502,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5351
5502
  if (context.draggedEntity === null) return false;
5352
5503
  let left = context.draggedEntityStart[0] + event.worldPosition[0] - context.dragStart[0];
5353
5504
  let top = context.draggedEntityStart[1] + event.worldPosition[1] - context.dragStart[1];
5354
- const draggingSynced = (0, import_core60.hasComponent)(ctx, context.draggedEntity, import_canvas_store41.Synced) || (0, import_core60.hasComponent)(ctx, context.draggedEntity, TransformBox);
5505
+ const draggingSynced = (0, import_core63.hasComponent)(ctx, context.draggedEntity, import_canvas_store43.Synced) || (0, import_core63.hasComponent)(ctx, context.draggedEntity, TransformBox);
5355
5506
  if (event.shiftDown && draggingSynced) {
5356
5507
  const dx = Math.abs(event.worldPosition[0] - context.dragStart[0]);
5357
5508
  const dy = Math.abs(event.worldPosition[1] - context.dragStart[1]);
@@ -5361,7 +5512,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5361
5512
  left = context.draggedEntityStart[0];
5362
5513
  }
5363
5514
  }
5364
- if (draggingSynced) {
5515
+ if (draggingSynced && isDragSnappable(ctx, context.draggedEntity)) {
5365
5516
  left = Grid.snapX(ctx, left);
5366
5517
  top = Grid.snapY(ctx, top);
5367
5518
  }
@@ -5374,7 +5525,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5374
5525
  const dx = context.draggedEntityStart[0] - left;
5375
5526
  const dy = context.draggedEntityStart[1] - top;
5376
5527
  isCloning = true;
5377
- const isTransformBox = (0, import_core60.hasComponent)(ctx, context.draggedEntity, TransformBox);
5528
+ const isTransformBox = (0, import_core63.hasComponent)(ctx, context.draggedEntity, TransformBox);
5378
5529
  const entityIds = isTransformBox ? Array.from(selectedBlocksQuery3.current(ctx)) : [context.draggedEntity];
5379
5530
  CloneEntities.spawn(ctx, {
5380
5531
  entityIds,
@@ -5382,7 +5533,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5382
5533
  seed: context.cloneGeneratorSeed
5383
5534
  });
5384
5535
  } else if (!event.altDown && context.isCloning) {
5385
- const isTransformBox = (0, import_core60.hasComponent)(ctx, context.draggedEntity, TransformBox);
5536
+ const isTransformBox = (0, import_core63.hasComponent)(ctx, context.draggedEntity, TransformBox);
5386
5537
  const entityIds = isTransformBox ? Array.from(selectedBlocksQuery3.current(ctx)) : [context.draggedEntity];
5387
5538
  UncloneEntities.spawn(ctx, {
5388
5539
  entityIds,
@@ -5407,8 +5558,8 @@ var selectionMachine = (0, import_xstate5.setup)({
5407
5558
  const ctx = event.ctx;
5408
5559
  let intersectId;
5409
5560
  for (const entityId of event.intersects) {
5410
- if ((0, import_core60.hasComponent)(ctx, entityId, import_canvas_store41.Synced)) {
5411
- if ((0, import_core60.hasComponent)(ctx, entityId, Block)) {
5561
+ if ((0, import_core63.hasComponent)(ctx, entityId, import_canvas_store43.Synced)) {
5562
+ if ((0, import_core63.hasComponent)(ctx, entityId, Block)) {
5412
5563
  const block = Block.read(ctx, entityId);
5413
5564
  if (!canBlockInteract(ctx, block.tag)) continue;
5414
5565
  }
@@ -5428,7 +5579,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5428
5579
  },
5429
5580
  selectDragged: ({ context, event }) => {
5430
5581
  if (context.draggedEntity === null) return;
5431
- if (!(0, import_core60.hasComponent)(event.ctx, context.draggedEntity, import_canvas_store41.Synced)) return;
5582
+ if (!(0, import_core63.hasComponent)(event.ctx, context.draggedEntity, import_canvas_store43.Synced)) return;
5432
5583
  SelectBlock.spawn(event.ctx, {
5433
5584
  entityId: context.draggedEntity,
5434
5585
  deselectOthers: true
@@ -5440,9 +5591,9 @@ var selectionMachine = (0, import_xstate5.setup)({
5440
5591
  },
5441
5592
  deselectAllIfDraggedNotSelected: ({ context, event }) => {
5442
5593
  if (context.draggedEntity === null) return;
5443
- if ((0, import_core60.hasComponent)(event.ctx, context.draggedEntity, Selected)) return;
5444
- if ((0, import_core60.hasComponent)(event.ctx, context.draggedEntity, TransformBox)) return;
5445
- if ((0, import_core60.hasComponent)(event.ctx, context.draggedEntity, TransformHandle)) return;
5594
+ if ((0, import_core63.hasComponent)(event.ctx, context.draggedEntity, Selected)) return;
5595
+ if ((0, import_core63.hasComponent)(event.ctx, context.draggedEntity, TransformBox)) return;
5596
+ if ((0, import_core63.hasComponent)(event.ctx, context.draggedEntity, TransformHandle)) return;
5446
5597
  DeselectAll.spawn(event.ctx, void 0);
5447
5598
  }
5448
5599
  }
@@ -5566,9 +5717,9 @@ var selectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (ctx)
5566
5717
  });
5567
5718
 
5568
5719
  // src/systems/preInput/rankBoundsSystem.ts
5569
- var import_canvas_store42 = require("@woven-ecs/canvas-store");
5570
- var import_core61 = require("@woven-ecs/core");
5571
- var blocksQuery = (0, import_core61.defineQuery)((q) => q.with(import_canvas_store42.Synced, Block));
5720
+ var import_canvas_store44 = require("@woven-ecs/canvas-store");
5721
+ var import_core64 = require("@woven-ecs/core");
5722
+ var blocksQuery = (0, import_core64.defineQuery)((q) => q.with(import_canvas_store44.Synced, Block));
5572
5723
  var rankBoundsSystem = defineEditorSystem({ phase: "input", priority: 100 }, (ctx) => {
5573
5724
  const added2 = blocksQuery.added(ctx);
5574
5725
  for (const entityId of added2) {
@@ -5585,11 +5736,11 @@ var rankBoundsSystem = defineEditorSystem({ phase: "input", priority: 100 }, (ct
5585
5736
 
5586
5737
  // src/systems/preRender/canSeeBlocksSystem.ts
5587
5738
  var import_math10 = require("@woven-canvas/math");
5588
- var import_canvas_store43 = require("@woven-ecs/canvas-store");
5589
- var import_core62 = require("@woven-ecs/core");
5590
- var camerasQuery = (0, import_core62.defineQuery)((q) => q.tracking(Camera));
5591
- var blocksQuery2 = (0, import_core62.defineQuery)((q) => q.with(import_canvas_store43.Synced, Block, Aabb));
5592
- var syncedBlocksQuery = (0, import_core62.defineQuery)((q) => q.with(import_canvas_store43.Synced, Block, Aabb));
5739
+ var import_canvas_store45 = require("@woven-ecs/canvas-store");
5740
+ var import_core65 = require("@woven-ecs/core");
5741
+ var camerasQuery = (0, import_core65.defineQuery)((q) => q.tracking(Camera));
5742
+ var blocksQuery2 = (0, import_core65.defineQuery)((q) => q.with(import_canvas_store45.Synced, Block, Aabb));
5743
+ var syncedBlocksQuery = (0, import_core65.defineQuery)((q) => q.with(import_canvas_store45.Synced, Block, Aabb));
5593
5744
  var _cameraAabb = [0, 0, 0, 0];
5594
5745
  var canSeeBlocksSystem = defineEditorSystem({ phase: "render", priority: 90 }, (ctx) => {
5595
5746
  if (camerasQuery.changed(ctx).length === 0 && syncedBlocksQuery.addedOrRemoved(ctx).length === 0) {
@@ -5598,7 +5749,7 @@ var canSeeBlocksSystem = defineEditorSystem({ phase: "render", priority: 90 }, (
5598
5749
  const camera = Camera.read(ctx);
5599
5750
  const cameraAabb = Camera.getAabb(ctx, _cameraAabb);
5600
5751
  if (camera.canSeeBlocks && camera.lastSeenBlock !== null) {
5601
- if ((0, import_core62.hasComponent)(ctx, camera.lastSeenBlock, Aabb)) {
5752
+ if ((0, import_core65.hasComponent)(ctx, camera.lastSeenBlock, Aabb)) {
5602
5753
  const aabb = Aabb.read(ctx, camera.lastSeenBlock);
5603
5754
  if (import_math10.Aabb.intersects(cameraAabb, aabb.value)) {
5604
5755
  return;
@@ -5627,8 +5778,8 @@ var canSeeBlocksSystem = defineEditorSystem({ phase: "render", priority: 90 }, (
5627
5778
 
5628
5779
  // src/systems/preRender/scaleWithZoomSystem.ts
5629
5780
  var import_math11 = require("@woven-canvas/math");
5630
- var import_core63 = require("@woven-ecs/core");
5631
- var scaleWithZoomQuery = (0, import_core63.defineQuery)((q) => q.with(Block).tracking(ScaleWithZoom));
5781
+ var import_core66 = require("@woven-ecs/core");
5782
+ var scaleWithZoomQuery = (0, import_core66.defineQuery)((q) => q.with(Block).tracking(ScaleWithZoom));
5632
5783
  var _scaledSize = [0, 0];
5633
5784
  var _anchorOffset = [0, 0];
5634
5785
  var _anchorAdj = [0, 0];
@@ -5672,13 +5823,13 @@ function scaleBlock(ctx, entityId, zoom) {
5672
5823
 
5673
5824
  // src/systems/update/blockSystem.ts
5674
5825
  var import_math12 = require("@woven-canvas/math");
5675
- var import_canvas_store44 = require("@woven-ecs/canvas-store");
5676
- var import_core64 = require("@woven-ecs/core");
5677
- var selectedBlocksQuery4 = (0, import_core64.defineQuery)((q) => q.with(Block, Selected));
5678
- var syncedBlocksQuery2 = (0, import_core64.defineQuery)((q) => q.with(Block, import_canvas_store44.Synced));
5826
+ var import_canvas_store46 = require("@woven-ecs/canvas-store");
5827
+ var import_core67 = require("@woven-ecs/core");
5828
+ var selectedBlocksQuery4 = (0, import_core67.defineQuery)((q) => q.with(Block, Selected));
5829
+ var syncedBlocksQuery2 = (0, import_core67.defineQuery)((q) => q.with(Block, import_canvas_store46.Synced));
5679
5830
  var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5680
5831
  on(ctx, DragBlock, (ctx2, { entityId, position }) => {
5681
- if (!(0, import_core64.hasComponent)(ctx2, entityId, Block)) return;
5832
+ if (!(0, import_core67.hasComponent)(ctx2, entityId, Block)) return;
5682
5833
  Block.setWorldPosition(ctx2, entityId, position);
5683
5834
  });
5684
5835
  on(ctx, SelectBlock, (ctx2, { entityId, deselectOthers }) => {
@@ -5691,7 +5842,7 @@ var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5691
5842
  deselectBlock(ctx2, entityId);
5692
5843
  });
5693
5844
  on(ctx, ToggleSelect, (ctx2, { entityId }) => {
5694
- if ((0, import_core64.hasComponent)(ctx2, entityId, Selected)) {
5845
+ if ((0, import_core67.hasComponent)(ctx2, entityId, Selected)) {
5695
5846
  deselectBlock(ctx2, entityId);
5696
5847
  } else {
5697
5848
  selectBlock(ctx2, entityId);
@@ -5699,15 +5850,15 @@ var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5699
5850
  });
5700
5851
  on(ctx, DeselectAll, deselectAllBlocks);
5701
5852
  on(ctx, AddHeld, (ctx2, { entityId }) => {
5702
- if (!(0, import_core64.hasComponent)(ctx2, entityId, import_canvas_store44.Synced)) return;
5703
- if ((0, import_core64.hasComponent)(ctx2, entityId, Held)) return;
5704
- const { sessionId } = (0, import_core64.getResources)(ctx2);
5705
- (0, import_core64.addComponent)(ctx2, entityId, Held, { sessionId });
5853
+ if (!(0, import_core67.hasComponent)(ctx2, entityId, import_canvas_store46.Synced)) return;
5854
+ if ((0, import_core67.hasComponent)(ctx2, entityId, Held)) return;
5855
+ const { sessionId } = (0, import_core67.getResources)(ctx2);
5856
+ (0, import_core67.addComponent)(ctx2, entityId, Held, { sessionId });
5706
5857
  });
5707
5858
  on(ctx, RemoveHeld, (ctx2, { entityId }) => {
5708
- if ((0, import_core64.hasComponent)(ctx2, entityId, Selected)) return;
5709
- if (!(0, import_core64.hasComponent)(ctx2, entityId, Held)) return;
5710
- (0, import_core64.removeComponent)(ctx2, entityId, Held);
5859
+ if ((0, import_core67.hasComponent)(ctx2, entityId, Selected)) return;
5860
+ if (!(0, import_core67.hasComponent)(ctx2, entityId, Held)) return;
5861
+ (0, import_core67.removeComponent)(ctx2, entityId, Held);
5711
5862
  });
5712
5863
  on(ctx, SelectAll, (ctx2) => {
5713
5864
  for (const entityId of syncedBlocksQuery2.current(ctx2)) {
@@ -5719,7 +5870,7 @@ var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5719
5870
  });
5720
5871
  on(ctx, RemoveSelected, (ctx2) => {
5721
5872
  for (const entityId of selectedBlocksQuery4.current(ctx2)) {
5722
- if ((0, import_core64.isAlive)(ctx2, entityId)) {
5873
+ if ((0, import_core67.isAlive)(ctx2, entityId)) {
5723
5874
  cascadeDelete(ctx2, entityId);
5724
5875
  }
5725
5876
  }
@@ -5794,12 +5945,12 @@ function cloneEntities(ctx, entityIds, offset, seed) {
5794
5945
  }
5795
5946
  }
5796
5947
  entityIds = [...allEntityIdsSet];
5797
- const { componentsById } = (0, import_core64.getResources)(ctx);
5948
+ const { componentsById } = (0, import_core67.getResources)(ctx);
5798
5949
  const documentComponents = new Map([...componentsById].filter(([, def]) => def.sync === "document"));
5799
- const syncedComponentId = import_canvas_store44.Synced._getComponentId(ctx);
5950
+ const syncedComponentId = import_canvas_store46.Synced._getComponentId(ctx);
5800
5951
  const blockComponentId = Block._getComponentId(ctx);
5801
5952
  const entityIdSet = new Set(entityIds);
5802
- const sortedEntities = entityIds.filter((id) => (0, import_core64.hasComponent)(ctx, id, Block)).sort((a, b) => {
5953
+ const sortedEntities = entityIds.filter((id) => (0, import_core67.hasComponent)(ctx, id, Block)).sort((a, b) => {
5803
5954
  const rankA = Block.read(ctx, a).rank;
5804
5955
  const rankB = Block.read(ctx, b).rank;
5805
5956
  return rankA < rankB ? -1 : rankA > rankB ? 1 : 0;
@@ -5823,12 +5974,12 @@ function cloneEntities(ctx, entityIds, offset, seed) {
5823
5974
  }
5824
5975
  const originalToClone = /* @__PURE__ */ new Map();
5825
5976
  for (const entityId of entityIds) {
5826
- if (!(0, import_core64.hasComponent)(ctx, entityId, import_canvas_store44.Synced)) continue;
5827
- const synced = import_canvas_store44.Synced.read(ctx, entityId);
5977
+ if (!(0, import_core67.hasComponent)(ctx, entityId, import_canvas_store46.Synced)) continue;
5978
+ const synced = import_canvas_store46.Synced.read(ctx, entityId);
5828
5979
  const cloneId = generateUuidBySeed(synced.id, seed);
5829
- const cloneEntityId = (0, import_core64.createEntity)(ctx);
5980
+ const cloneEntityId = (0, import_core67.createEntity)(ctx);
5830
5981
  originalToClone.set(entityId, cloneEntityId);
5831
- (0, import_core64.addComponent)(ctx, cloneEntityId, import_canvas_store44.Synced, { id: cloneId });
5982
+ (0, import_core67.addComponent)(ctx, cloneEntityId, import_canvas_store46.Synced, { id: cloneId });
5832
5983
  for (const componentId of ctx.entityBuffer.getComponentIds(entityId)) {
5833
5984
  if (componentId === syncedComponentId) continue;
5834
5985
  const componentDef = documentComponents.get(componentId);
@@ -5844,7 +5995,7 @@ function cloneEntities(ctx, entityIds, offset, seed) {
5844
5995
  }
5845
5996
  data.rank = cloneRankMap.get(entityId) ?? RankBounds.genBetween(prevRank, lowestOriginalRank);
5846
5997
  }
5847
- (0, import_core64.addComponent)(ctx, cloneEntityId, componentDef, data);
5998
+ (0, import_core67.addComponent)(ctx, cloneEntityId, componentDef, data);
5848
5999
  }
5849
6000
  }
5850
6001
  swapConnectorRefs(ctx, originalToClone);
@@ -5860,23 +6011,23 @@ function uncloneEntities(ctx, entityIds, seed) {
5860
6011
  entityIds = [...allEntityIdsSet];
5861
6012
  const expectedCloneUuids = /* @__PURE__ */ new Set();
5862
6013
  for (const entityId of entityIds) {
5863
- if (!(0, import_core64.hasComponent)(ctx, entityId, import_canvas_store44.Synced)) continue;
5864
- const synced = import_canvas_store44.Synced.read(ctx, entityId);
6014
+ if (!(0, import_core67.hasComponent)(ctx, entityId, import_canvas_store46.Synced)) continue;
6015
+ const synced = import_canvas_store46.Synced.read(ctx, entityId);
5865
6016
  expectedCloneUuids.add(generateUuidBySeed(synced.id, seed));
5866
6017
  }
5867
6018
  const clonesToRemove = [];
5868
6019
  for (const entityId of syncedBlocksQuery2.current(ctx)) {
5869
- const synced = import_canvas_store44.Synced.read(ctx, entityId);
6020
+ const synced = import_canvas_store46.Synced.read(ctx, entityId);
5870
6021
  if (expectedCloneUuids.has(synced.id)) {
5871
6022
  clonesToRemove.push(entityId);
5872
6023
  }
5873
6024
  }
5874
6025
  const cloneToOriginal = /* @__PURE__ */ new Map();
5875
6026
  for (const cloneId of clonesToRemove) {
5876
- const cloneUuid = import_canvas_store44.Synced.read(ctx, cloneId).id;
6027
+ const cloneUuid = import_canvas_store46.Synced.read(ctx, cloneId).id;
5877
6028
  for (const originalId of entityIds) {
5878
- if (!(0, import_core64.hasComponent)(ctx, originalId, import_canvas_store44.Synced)) continue;
5879
- const originalUuid = import_canvas_store44.Synced.read(ctx, originalId).id;
6029
+ if (!(0, import_core67.hasComponent)(ctx, originalId, import_canvas_store46.Synced)) continue;
6030
+ const originalUuid = import_canvas_store46.Synced.read(ctx, originalId).id;
5880
6031
  if (generateUuidBySeed(originalUuid, seed) === cloneUuid) {
5881
6032
  cloneToOriginal.set(cloneId, originalId);
5882
6033
  break;
@@ -5885,25 +6036,25 @@ function uncloneEntities(ctx, entityIds, seed) {
5885
6036
  }
5886
6037
  swapConnectorRefs(ctx, cloneToOriginal);
5887
6038
  for (const cloneId of clonesToRemove) {
5888
- (0, import_core64.removeEntity)(ctx, cloneId);
6039
+ (0, import_core67.removeEntity)(ctx, cloneId);
5889
6040
  }
5890
6041
  }
5891
6042
  function swapConnectorRefs(ctx, fromTo) {
5892
6043
  for (const [fromId, toId] of fromTo) {
5893
- for (const connectorId of (0, import_core64.getBackrefs)(ctx, fromId, Connector, "startBlock")) {
5894
- if (!(0, import_core64.isAlive)(ctx, connectorId)) continue;
6044
+ for (const connectorId of (0, import_core67.getBackrefs)(ctx, fromId, Connector, "startBlock")) {
6045
+ if (!(0, import_core67.isAlive)(ctx, connectorId)) continue;
5895
6046
  Connector.write(ctx, connectorId).startBlock = toId;
5896
6047
  }
5897
- for (const connectorId of (0, import_core64.getBackrefs)(ctx, fromId, Connector, "endBlock")) {
5898
- if (!(0, import_core64.isAlive)(ctx, connectorId)) continue;
6048
+ for (const connectorId of (0, import_core67.getBackrefs)(ctx, fromId, Connector, "endBlock")) {
6049
+ if (!(0, import_core67.isAlive)(ctx, connectorId)) continue;
5899
6050
  Connector.write(ctx, connectorId).endBlock = toId;
5900
6051
  }
5901
6052
  }
5902
6053
  }
5903
6054
  function swapParentRefs(ctx, fromTo) {
5904
6055
  for (const [, cloneId] of fromTo) {
5905
- if (!(0, import_core64.isAlive)(ctx, cloneId)) continue;
5906
- if (!(0, import_core64.hasComponent)(ctx, cloneId, Block)) continue;
6056
+ if (!(0, import_core67.isAlive)(ctx, cloneId)) continue;
6057
+ if (!(0, import_core67.hasComponent)(ctx, cloneId, Block)) continue;
5907
6058
  const block = Block.read(ctx, cloneId);
5908
6059
  if (block.parentId !== null && fromTo.has(block.parentId)) {
5909
6060
  Block.write(ctx, cloneId).parentId = fromTo.get(block.parentId);
@@ -5913,20 +6064,27 @@ function swapParentRefs(ctx, fromTo) {
5913
6064
 
5914
6065
  // src/systems/update/dragHandlerSystem.ts
5915
6066
  var import_math13 = require("@woven-canvas/math");
5916
- var import_core65 = require("@woven-ecs/core");
5917
- var selectedBlocksQuery5 = (0, import_core65.defineQuery)((q) => q.with(Block, Selected));
5918
- var transformBoxQuery2 = (0, import_core65.defineQuery)((q) => q.with(Block, TransformBox, DragStart));
6067
+ var import_core68 = require("@woven-ecs/core");
6068
+ var selectedBlocksQuery5 = (0, import_core68.defineQuery)((q) => q.with(Block, Selected));
6069
+ var transformBoxQuery2 = (0, import_core68.defineQuery)((q) => q.with(Block, TransformBox, DragStart));
5919
6070
  function isParentSelected(ctx, blockId) {
5920
6071
  const block = Block.read(ctx, blockId);
5921
- return block.parentId !== null && (0, import_core65.hasComponent)(ctx, block.parentId, Selected);
6072
+ return block.parentId !== null && (0, import_core68.hasComponent)(ctx, block.parentId, Selected);
6073
+ }
6074
+ function isSelectionSnappable(ctx) {
6075
+ for (const blockId of selectedBlocksQuery5.current(ctx)) {
6076
+ if (isParentSelected(ctx, blockId)) continue;
6077
+ if (canBlockSnap(ctx, Block.read(ctx, blockId).tag)) return true;
6078
+ }
6079
+ return false;
5922
6080
  }
5923
6081
  var dragHandlerSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5924
6082
  on(ctx, DragBlock, (ctx2, { entityId, position }) => {
5925
- if ((0, import_core65.hasComponent)(ctx2, entityId, TransformBox)) {
6083
+ if ((0, import_core68.hasComponent)(ctx2, entityId, TransformBox)) {
5926
6084
  onTransformBoxDrag(ctx2, entityId, position);
5927
6085
  return;
5928
6086
  }
5929
- if ((0, import_core65.hasComponent)(ctx2, entityId, TransformHandle)) {
6087
+ if ((0, import_core68.hasComponent)(ctx2, entityId, TransformHandle)) {
5930
6088
  onTransformHandleDrag(ctx2, entityId, position);
5931
6089
  return;
5932
6090
  }
@@ -5937,11 +6095,13 @@ function onTransformBoxDrag(ctx, boxId, position) {
5937
6095
  const dx = position[0] - boxStart.position[0];
5938
6096
  const dy = position[1] - boxStart.position[1];
5939
6097
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
5940
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6098
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
5941
6099
  if (isParentSelected(ctx, blockId)) continue;
5942
6100
  const blockStart = DragStart.read(ctx, blockId);
5943
6101
  const worldPos = [blockStart.position[0] + dx, blockStart.position[1] + dy];
5944
- Grid.snapPosition(ctx, worldPos);
6102
+ if (canBlockSnap(ctx, Block.read(ctx, blockId).tag)) {
6103
+ Grid.snapPosition(ctx, worldPos);
6104
+ }
5945
6105
  Block.setWorldPosition(ctx, blockId, worldPos);
5946
6106
  }
5947
6107
  }
@@ -5975,10 +6135,11 @@ function onRotateHandleDrag(ctx, handleId, position) {
5975
6135
  let delta = angleHandle - handleStartAngle;
5976
6136
  const grid = Grid.read(ctx);
5977
6137
  const keyboard = Keyboard.read(ctx);
6138
+ const selectionSnappable = isSelectionSnappable(ctx);
5978
6139
  let snapAngle = 0;
5979
6140
  if (keyboard.shiftDown) {
5980
6141
  snapAngle = grid.shiftSnapAngleRad;
5981
- } else if (grid.enabled) {
6142
+ } else if (grid.enabled && selectionSnappable) {
5982
6143
  snapAngle = grid.snapAngleRad;
5983
6144
  }
5984
6145
  if (snapAngle > 0) {
@@ -5987,7 +6148,7 @@ function onRotateHandleDrag(ctx, handleId, position) {
5987
6148
  }
5988
6149
  boxBlock.rotateZ = import_math13.Scalar.normalizeAngle(dragStart.rotateZ + delta);
5989
6150
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
5990
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6151
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
5991
6152
  if (isParentSelected(ctx, blockId)) continue;
5992
6153
  const blockStart = DragStart.read(ctx, blockId);
5993
6154
  const block = Block.write(ctx, blockId);
@@ -6000,7 +6161,7 @@ function onRotateHandleDrag(ctx, handleId, position) {
6000
6161
  const angle = import_math13.Vec2.angleTo(boxCenter, startCenter) + delta;
6001
6162
  const newCenter = import_math13.Vec2.fromPolar(r, angle, boxCenter);
6002
6163
  const worldPos = [newCenter[0] - block.size[0] / 2, newCenter[1] - block.size[1] / 2];
6003
- if (grid.strict) {
6164
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
6004
6165
  Grid.snapPosition(ctx, worldPos);
6005
6166
  }
6006
6167
  Block.setWorldPosition(ctx, blockId, worldPos);
@@ -6017,7 +6178,10 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6017
6178
  const handle = TransformHandle.read(ctx, handleId);
6018
6179
  const handleBlock = Block.read(ctx, handleId);
6019
6180
  const handleCenter = [position[0] + handleBlock.size[0] / 2, position[1] + handleBlock.size[1] / 2];
6020
- Grid.snapPosition(ctx, handleCenter);
6181
+ const selectionSnappable = isSelectionSnappable(ctx);
6182
+ if (selectionSnappable) {
6183
+ Grid.snapPosition(ctx, handleCenter);
6184
+ }
6021
6185
  const boxRotateZ = boxStart.rotateZ;
6022
6186
  const boxStartCenter = [
6023
6187
  boxStart.position[0] + boxStart.size[0] / 2,
@@ -6068,7 +6232,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6068
6232
  import_math13.Vec2.add(newBoxCenter, vec);
6069
6233
  const scaleFactor = [scaleX, scaleY];
6070
6234
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
6071
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6235
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
6072
6236
  if (isParentSelected(ctx, blockId)) continue;
6073
6237
  const blockStart = DragStart.read(ctx, blockId);
6074
6238
  const block = Block.write(ctx, blockId);
@@ -6086,7 +6250,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6086
6250
  const newBlockSize = import_math13.Vec2.clone(blockStart.size);
6087
6251
  import_math13.Vec2.multiply(newBlockSize, scaleFactor);
6088
6252
  const isTextResize = getBlockResizeMode(ctx, blockId) === ResizeMode.Text;
6089
- if (grid.strict) {
6253
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
6090
6254
  if (isTextResize && maintainAspectRatio) {
6091
6255
  newBlockSize[1] = Math.max(grid.rowHeight, Grid.snapY(ctx, newBlockSize[1]));
6092
6256
  const aspectRatio = blockStart.size[0] / blockStart.size[1];
@@ -6097,7 +6261,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6097
6261
  }
6098
6262
  const worldPos = import_math13.Vec2.clone(newBlockCenter);
6099
6263
  import_math13.Vec2.sub(worldPos, [newBlockSize[0] / 2, newBlockSize[1] / 2]);
6100
- if (grid.strict) {
6264
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
6101
6265
  Grid.snapPosition(ctx, worldPos);
6102
6266
  }
6103
6267
  Block.setWorldPosition(ctx, blockId, worldPos);
@@ -6108,7 +6272,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6108
6272
  } else {
6109
6273
  import_math13.Vec2.copy(block.size, newBlockSize);
6110
6274
  }
6111
- if ((0, import_core65.hasComponent)(ctx, blockId, Text)) {
6275
+ if ((0, import_core68.hasComponent)(ctx, blockId, Text)) {
6112
6276
  const didStretch = handle.kind === TransformHandleKind.Stretch;
6113
6277
  const text = Text.write(ctx, blockId);
6114
6278
  if (didStretch) {
@@ -6146,10 +6310,11 @@ function onRotateScaleHandleDrag(ctx, handleId, position) {
6146
6310
  }
6147
6311
  const grid = Grid.read(ctx);
6148
6312
  const keyboard = Keyboard.read(ctx);
6313
+ const selectionSnappable = isSelectionSnappable(ctx);
6149
6314
  let snapAngle = 0;
6150
6315
  if (keyboard.shiftDown) {
6151
6316
  snapAngle = grid.shiftSnapAngleRad;
6152
- } else if (grid.enabled) {
6317
+ } else if (grid.enabled && selectionSnappable) {
6153
6318
  snapAngle = grid.snapAngleRad;
6154
6319
  }
6155
6320
  if (snapAngle > 0) {
@@ -6169,13 +6334,13 @@ function onRotateScaleHandleDrag(ctx, handleId, position) {
6169
6334
  boxBlock.size = [finalLength, startHeight];
6170
6335
  boxBlock.position = [newCenter[0] - finalLength / 2, newCenter[1] - startHeight / 2];
6171
6336
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
6172
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6337
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
6173
6338
  if (isParentSelected(ctx, blockId)) continue;
6174
6339
  const block = Block.write(ctx, blockId);
6175
6340
  block.rotateZ = newRotateZ;
6176
6341
  block.size = [finalLength, startHeight];
6177
6342
  const blockWorldPos = [newCenter[0] - finalLength / 2, newCenter[1] - startHeight / 2];
6178
- if (grid.strict) {
6343
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
6179
6344
  Grid.snapPosition(ctx, blockWorldPos);
6180
6345
  }
6181
6346
  Block.setWorldPosition(ctx, blockId, blockWorldPos);
@@ -6183,23 +6348,23 @@ function onRotateScaleHandleDrag(ctx, handleId, position) {
6183
6348
  }
6184
6349
 
6185
6350
  // src/systems/update/selectSystem.ts
6186
- var import_canvas_store45 = require("@woven-ecs/canvas-store");
6187
- var import_core66 = require("@woven-ecs/core");
6188
- var selectedBlocksQuery6 = (0, import_core66.defineQuery)((q) => q.with(Block, Selected));
6189
- var selectionBoxQuery = (0, import_core66.defineQuery)((q) => q.with(Block, SelectionBox));
6190
- var syncedBlocksQuery3 = (0, import_core66.defineQuery)((q) => q.with(Block, import_canvas_store45.Synced, Aabb));
6351
+ var import_canvas_store47 = require("@woven-ecs/canvas-store");
6352
+ var import_core69 = require("@woven-ecs/core");
6353
+ var selectedBlocksQuery6 = (0, import_core69.defineQuery)((q) => q.with(Block, Selected));
6354
+ var selectionBoxQuery = (0, import_core69.defineQuery)((q) => q.with(Block, SelectionBox));
6355
+ var syncedBlocksQuery3 = (0, import_core69.defineQuery)((q) => q.with(Block, import_canvas_store47.Synced, Aabb));
6191
6356
  var SELECTION_BOX_RANK = "z";
6192
6357
  var selectSystem2 = defineEditorSystem({ phase: "update" }, (ctx) => {
6193
6358
  on(ctx, AddSelectionBox, (ctx2) => {
6194
- const entityId = (0, import_core66.createEntity)(ctx2);
6195
- (0, import_core66.addComponent)(ctx2, entityId, Block, {
6359
+ const entityId = (0, import_core69.createEntity)(ctx2);
6360
+ (0, import_core69.addComponent)(ctx2, entityId, Block, {
6196
6361
  tag: "selection-box",
6197
6362
  position: [0, 0],
6198
6363
  size: [0, 0],
6199
6364
  rotateZ: 0,
6200
6365
  rank: SELECTION_BOX_RANK
6201
6366
  });
6202
- (0, import_core66.addComponent)(ctx2, entityId, SelectionBox, {});
6367
+ (0, import_core69.addComponent)(ctx2, entityId, SelectionBox, {});
6203
6368
  });
6204
6369
  on(ctx, UpdateSelectionBox, (ctx2, { bounds, deselectOthers }) => {
6205
6370
  const selectionBoxes = selectionBoxQuery.current(ctx2);
@@ -6235,26 +6400,36 @@ var selectSystem2 = defineEditorSystem({ phase: "update" }, (ctx) => {
6235
6400
  return;
6236
6401
  }
6237
6402
  const selectionBoxId = selectionBoxes[0];
6238
- (0, import_core66.removeEntity)(ctx2, selectionBoxId);
6403
+ (0, import_core69.removeEntity)(ctx2, selectionBoxId);
6239
6404
  });
6240
6405
  });
6241
6406
 
6242
6407
  // src/systems/update/updateFrameContainmentSystem.ts
6243
- var import_core67 = require("@woven-ecs/core");
6408
+ var import_core70 = require("@woven-ecs/core");
6244
6409
  var updateFrameContainmentSystem = defineEditorSystem({ phase: "update", priority: -10 }, (ctx) => {
6245
6410
  on(ctx, AddFrameHighlight, (ctx2, { frameId }) => {
6246
- if (!(0, import_core67.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6247
- (0, import_core67.addComponent)(ctx2, frameId, FrameDropTarget);
6411
+ if (!(0, import_core70.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6412
+ (0, import_core70.addComponent)(ctx2, frameId, FrameDropTarget);
6248
6413
  }
6249
6414
  });
6415
+ on(ctx, PlaceBlockEvent, (ctx2, { entityId }) => {
6416
+ if (!(0, import_core70.hasComponent)(ctx2, entityId, Block)) return;
6417
+ if (Block.read(ctx2, entityId).parentId !== null) return;
6418
+ const center = Block.getCenter(ctx2, entityId);
6419
+ const frameId = findFrameAtPoint(ctx2, center, entityId);
6420
+ if (frameId === null) return;
6421
+ const worldPos = Block.getWorldPosition(ctx2, entityId);
6422
+ Block.write(ctx2, entityId).parentId = frameId;
6423
+ Block.setWorldPosition(ctx2, entityId, worldPos);
6424
+ });
6250
6425
  on(ctx, RemoveFrameHighlight, (ctx2, { frameId }) => {
6251
- if ((0, import_core67.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6252
- (0, import_core67.removeComponent)(ctx2, frameId, FrameDropTarget);
6426
+ if ((0, import_core70.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6427
+ (0, import_core70.removeComponent)(ctx2, frameId, FrameDropTarget);
6253
6428
  }
6254
6429
  });
6255
6430
  on(ctx, AssignFrameChildren, (ctx2, { assignments }) => {
6256
6431
  for (const { entityId, frameId } of assignments) {
6257
- if (!(0, import_core67.hasComponent)(ctx2, entityId, Block)) continue;
6432
+ if (!(0, import_core70.hasComponent)(ctx2, entityId, Block)) continue;
6258
6433
  const currentParentId = Block.read(ctx2, entityId).parentId;
6259
6434
  if (frameId !== null) {
6260
6435
  if (currentParentId !== frameId) {
@@ -6276,12 +6451,12 @@ var updateFrameContainmentSystem = defineEditorSystem({ phase: "update", priorit
6276
6451
  });
6277
6452
 
6278
6453
  // src/systems/update/updateFrameSetupSystem.ts
6279
- var import_core68 = require("@woven-ecs/core");
6280
- var frameQuery2 = (0, import_core68.defineQuery)((q) => q.tracking(Frame));
6454
+ var import_core71 = require("@woven-ecs/core");
6455
+ var frameQuery2 = (0, import_core71.defineQuery)((q) => q.tracking(Frame));
6281
6456
  var updateFrameSetupSystem = defineEditorSystem({ phase: "update", priority: 100 }, (ctx) => {
6282
6457
  for (const entityId of frameQuery2.added(ctx)) {
6283
- if ((0, import_core68.hasComponent)(ctx, entityId, HitGeometry)) continue;
6284
- (0, import_core68.addComponent)(ctx, entityId, HitGeometry);
6458
+ if ((0, import_core71.hasComponent)(ctx, entityId, HitGeometry)) continue;
6459
+ (0, import_core71.addComponent)(ctx, entityId, HitGeometry);
6285
6460
  HitGeometry.addCapsuleUv(ctx, entityId, [0, 0], [1, 0], FRAME_EDGE_THICKNESS);
6286
6461
  HitGeometry.addCapsuleUv(ctx, entityId, [1, 0], [1, 1], FRAME_EDGE_THICKNESS);
6287
6462
  HitGeometry.addCapsuleUv(ctx, entityId, [1, 1], [0, 1], FRAME_EDGE_THICKNESS);
@@ -6295,8 +6470,8 @@ function createCorePlugin(options = {}) {
6295
6470
  return {
6296
6471
  name: CORE_PLUGIN_NAME,
6297
6472
  resources: parsedOptions,
6298
- singletons: Object.values(singletons_exports).filter((v) => v instanceof import_canvas_store46.CanvasSingletonDef),
6299
- components: Object.values(components_exports).filter((v) => v instanceof import_canvas_store46.CanvasComponentDef),
6473
+ singletons: Object.values(singletons_exports).filter((v) => v instanceof import_canvas_store48.CanvasSingletonDef),
6474
+ components: Object.values(components_exports).filter((v) => v instanceof import_canvas_store48.CanvasComponentDef),
6300
6475
  blockDefs: [
6301
6476
  {
6302
6477
  tag: "sticky-note",
@@ -6440,7 +6615,7 @@ function createCorePlugin(options = {}) {
6440
6615
  ],
6441
6616
  cursors: CURSORS,
6442
6617
  setup(ctx) {
6443
- const { domElement } = (0, import_core69.getResources)(ctx);
6618
+ const { domElement } = (0, import_core72.getResources)(ctx);
6444
6619
  if (!domElement) return;
6445
6620
  attachKeyboardListeners(domElement);
6446
6621
  attachMouseListeners(domElement);
@@ -6448,7 +6623,7 @@ function createCorePlugin(options = {}) {
6448
6623
  attachPointerListeners(domElement);
6449
6624
  },
6450
6625
  teardown(ctx) {
6451
- const { domElement } = (0, import_core69.getResources)(ctx);
6626
+ const { domElement } = (0, import_core72.getResources)(ctx);
6452
6627
  if (!domElement) return;
6453
6628
  detachKeyboardListeners(domElement);
6454
6629
  detachMouseListeners(domElement);
@@ -6459,8 +6634,8 @@ function createCorePlugin(options = {}) {
6459
6634
  }
6460
6635
 
6461
6636
  // src/Editor.ts
6462
- var import_canvas_store47 = require("@woven-ecs/canvas-store");
6463
- var import_core70 = require("@woven-ecs/core");
6637
+ var import_canvas_store49 = require("@woven-ecs/canvas-store");
6638
+ var import_core73 = require("@woven-ecs/core");
6464
6639
 
6465
6640
  // src/FontLoader.ts
6466
6641
  var import_zod2 = require("zod");
@@ -6490,21 +6665,44 @@ var FontFamily = import_zod2.z.object({
6490
6665
  });
6491
6666
  var FontLoader = class {
6492
6667
  constructor() {
6493
- this.loadedFonts = /* @__PURE__ */ new Set();
6494
- }
6495
- /**
6496
- * Load multiple font families.
6497
- * Fonts that have already been loaded will be skipped.
6668
+ // Families whose stylesheet has fully loaded (fetched, injected, and the
6669
+ // FontFaces reported ready by the browser).
6670
+ this.loaded = /* @__PURE__ */ new Set();
6671
+ // In-flight loads, keyed by family name, so concurrent callers share one load
6672
+ // and critically can `await` it to *completion*. A plain "already started"
6673
+ // flag (the old behaviour) would let a second caller proceed before the font is
6674
+ // actually ready, which breaks code that measures text right after requesting a
6675
+ // font (the block would be sized against fallback metrics).
6676
+ this.inFlight = /* @__PURE__ */ new Map();
6677
+ }
6678
+ /**
6679
+ * Load multiple font families. Already-loaded families are skipped; in-flight
6680
+ * ones are shared. Resolves once all requested families are ready.
6498
6681
  *
6499
6682
  * @param families - Array of font families to load
6500
6683
  */
6501
6684
  async loadFonts(families) {
6502
- const unloadedFamilies = families.filter((family) => !this.loadedFonts.has(family.name));
6503
- if (unloadedFamilies.length === 0) {
6504
- return;
6505
- }
6506
- const fontPromises = unloadedFamilies.map((family) => this.loadSingleFont(family));
6507
- await Promise.all(fontPromises);
6685
+ await Promise.all(families.map((family) => this.loadFont(family)));
6686
+ }
6687
+ /**
6688
+ * Load a single font family, returning a promise that resolves only once the
6689
+ * font is actually ready to render. Idempotent: an already-loaded family
6690
+ * resolves immediately, and concurrent calls for the same family share the one
6691
+ * in-flight load — so a caller that measures text can `await` real metrics.
6692
+ *
6693
+ * @param family - Font family to load
6694
+ */
6695
+ loadFont(family) {
6696
+ if (this.loaded.has(family.name)) return Promise.resolve();
6697
+ const existing = this.inFlight.get(family.name);
6698
+ if (existing) return existing;
6699
+ const promise = this.loadSingleFont(family).then(() => {
6700
+ this.loaded.add(family.name);
6701
+ }).finally(() => {
6702
+ this.inFlight.delete(family.name);
6703
+ });
6704
+ this.inFlight.set(family.name, promise);
6705
+ return promise;
6508
6706
  }
6509
6707
  /**
6510
6708
  * Build a Google Fonts URL with weight and italic variants.
@@ -6541,34 +6739,36 @@ var FontLoader = class {
6541
6739
  }
6542
6740
  /**
6543
6741
  * Load a single font family.
6544
- * Adds a link element to the document head and waits for the font to be ready.
6742
+ * Fetches the stylesheet as text and injects it via an inline <style>, so
6743
+ * the @font-face rules land in a same-origin sheet. Tools that introspect
6744
+ * CSSOM (e.g. modern-screenshot's font embedding for /preview raster
6745
+ * captures) can't read cross-origin sheets, which a <link> to
6746
+ * fonts.googleapis.com would produce.
6545
6747
  *
6546
6748
  * @param family - Font family to load
6547
6749
  */
6548
- loadSingleFont(family) {
6549
- this.loadedFonts.add(family.name);
6550
- if (typeof document === "undefined") {
6551
- return Promise.resolve();
6552
- }
6553
- return new Promise((resolve, reject) => {
6554
- const link = document.createElement("link");
6555
- link.rel = "stylesheet";
6556
- link.href = this.buildGoogleFontsUrl(family);
6557
- link.onload = async () => {
6558
- try {
6559
- const loadPromises = this.getFontLoadPromises(family);
6560
- await Promise.all(loadPromises);
6561
- await document.fonts.ready;
6562
- resolve();
6563
- } catch (error) {
6564
- reject(new Error(`Failed to load font face for: ${family.name}. ${error}`));
6565
- }
6566
- };
6567
- link.onerror = () => {
6568
- reject(new Error(`Failed to load font stylesheet: ${family.name}`));
6569
- };
6570
- document.head.appendChild(link);
6571
- });
6750
+ async loadSingleFont(family) {
6751
+ if (typeof document === "undefined") return;
6752
+ const url = this.buildGoogleFontsUrl(family);
6753
+ let cssText;
6754
+ try {
6755
+ const res = await fetch(url);
6756
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
6757
+ cssText = await res.text();
6758
+ } catch (err) {
6759
+ throw new Error(`Failed to load font stylesheet: ${family.name}. ${err}`);
6760
+ }
6761
+ const style = document.createElement("style");
6762
+ style.setAttribute("data-wov-font-family", family.name);
6763
+ style.textContent = cssText;
6764
+ document.head.appendChild(style);
6765
+ try {
6766
+ const loadPromises = this.getFontLoadPromises(family);
6767
+ await Promise.all(loadPromises);
6768
+ await document.fonts.ready;
6769
+ } catch (err) {
6770
+ throw new Error(`Failed to load font face for: ${family.name}. ${err}`);
6771
+ }
6572
6772
  }
6573
6773
  /**
6574
6774
  * Get promises for loading all font variants (weights and italics).
@@ -6594,13 +6794,13 @@ var FontLoader = class {
6594
6794
  * @returns True if the font has been loaded
6595
6795
  */
6596
6796
  isLoaded(fontName) {
6597
- return this.loadedFonts.has(fontName);
6797
+ return this.loaded.has(fontName);
6598
6798
  }
6599
6799
  /**
6600
6800
  * Get the set of loaded font names.
6601
6801
  */
6602
6802
  getLoadedFonts() {
6603
- return this.loadedFonts;
6803
+ return this.loaded;
6604
6804
  }
6605
6805
  };
6606
6806
 
@@ -6681,7 +6881,7 @@ var Editor = class {
6681
6881
  const plugins = pluginInputs.map(parsePlugin);
6682
6882
  const corePlugin = createCorePlugin(options.corePlugin);
6683
6883
  const sortedPlugins = sortPluginsByDependencies([corePlugin, ...plugins]);
6684
- const allDefs = [CommandMarker, import_canvas_store47.Synced];
6884
+ const allDefs = [CommandMarker, import_canvas_store49.Synced];
6685
6885
  for (const plugin of sortedPlugins) {
6686
6886
  if (plugin.components) {
6687
6887
  allDefs.push(...plugin.components);
@@ -6757,7 +6957,7 @@ var Editor = class {
6757
6957
  singletonsById,
6758
6958
  readonly: options.readonly
6759
6959
  };
6760
- this.world = new import_core70.World(allDefs, {
6960
+ this.world = new import_core73.World(allDefs, {
6761
6961
  maxEntities,
6762
6962
  resources: allResources
6763
6963
  });
@@ -6811,17 +7011,14 @@ var Editor = class {
6811
7011
  * Call this after construction to run async setup.
6812
7012
  */
6813
7013
  async initialize() {
6814
- if (this.fonts.length > 0) {
6815
- await this.fontLoader.loadFonts(this.fonts);
6816
- }
6817
7014
  Grid.copy(this.ctx, this.gridOptions);
6818
7015
  Controls.copy(this.ctx, this.controlsOptions);
6819
7016
  this.nextTick((ctx) => {
6820
- const userEntity = (0, import_core70.createEntity)(ctx);
6821
- (0, import_core70.addComponent)(ctx, userEntity, import_canvas_store47.Synced, {
7017
+ const userEntity = (0, import_core73.createEntity)(ctx);
7018
+ (0, import_core73.addComponent)(ctx, userEntity, import_canvas_store49.Synced, {
6822
7019
  id: crypto.randomUUID()
6823
7020
  });
6824
- (0, import_core70.addComponent)(ctx, userEntity, User, {
7021
+ (0, import_core73.addComponent)(ctx, userEntity, User, {
6825
7022
  userId: this.userData.userId,
6826
7023
  sessionId: this.userData.sessionId,
6827
7024
  color: this.userData.color,
@@ -6835,6 +7032,29 @@ var Editor = class {
6835
7032
  }
6836
7033
  }
6837
7034
  }
7035
+ /**
7036
+ * Load a single font family's stylesheet on demand, by its `name` (the CSS
7037
+ * font-family — the same key the picker and `Text.fontFamily` use). Looks the
7038
+ * family up in the configured `fonts` and injects its @font-face rules into
7039
+ * the document. Idempotent — the FontLoader dedups, so repeated calls for an
7040
+ * already-loaded (or in-flight) family are cheap no-ops. Unknown names are
7041
+ * ignored. This is how the editor scales to a large font library: nothing is
7042
+ * fetched until a text block actually displays/edits in that family.
7043
+ */
7044
+ async loadFont(name) {
7045
+ if (!name) return;
7046
+ const family = this.fonts.find((f) => f.name === name);
7047
+ if (!family) return;
7048
+ try {
7049
+ await this.fontLoader.loadFont(family);
7050
+ } catch (err) {
7051
+ console.warn(`Failed to load font: ${name}`, err);
7052
+ }
7053
+ }
7054
+ /** Whether a font family's stylesheet has finished loading. */
7055
+ isFontLoaded(name) {
7056
+ return this.fontLoader.isLoaded(name);
7057
+ }
6838
7058
  /**
6839
7059
  * Run one frame of the editor loop.
6840
7060
  *
@@ -7050,11 +7270,14 @@ var Editor = class {
7050
7270
  Key,
7051
7271
  Keybind,
7052
7272
  Keyboard,
7273
+ Layer,
7053
7274
  MAX_HIT_ARCS,
7054
7275
  MAX_HIT_CAPSULES,
7276
+ MAX_HIT_POLYGON_POINTS,
7055
7277
  MainThreadSystem,
7056
7278
  Mouse,
7057
7279
  Opacity,
7280
+ PlaceBlockEvent,
7058
7281
  Pointer,
7059
7282
  PointerButton,
7060
7283
  PointerType,
@@ -7114,6 +7337,7 @@ var Editor = class {
7114
7337
  clearPointerTrackingState,
7115
7338
  compareBlockOrder,
7116
7339
  convertRefsToUuids,
7340
+ createBlock,
7117
7341
  createCorePlugin,
7118
7342
  createEntity,
7119
7343
  defineCanvasComponent,
@@ -7151,12 +7375,14 @@ var Editor = class {
7151
7375
  isAlive,
7152
7376
  isAncestor,
7153
7377
  isHeldByRemote,
7378
+ isLayerInteractive,
7154
7379
  isReadonly,
7155
7380
  on,
7156
7381
  parsePlugin,
7157
7382
  removeComponent,
7158
7383
  removeEntity,
7159
7384
  resolveEmbedUrl,
7385
+ resolveLayerRank,
7160
7386
  resolveRefFields,
7161
7387
  runMachine,
7162
7388
  selectAll,