@woven-canvas/core 1.0.5 → 1.0.9

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,21 +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,
184
+ isReadonly: () => isReadonly,
179
185
  on: () => on,
180
186
  parsePlugin: () => parsePlugin,
181
- removeComponent: () => import_core71.removeComponent,
182
- removeEntity: () => import_core71.removeEntity,
187
+ removeComponent: () => import_core74.removeComponent,
188
+ removeEntity: () => import_core74.removeEntity,
183
189
  resolveEmbedUrl: () => resolveEmbedUrl,
190
+ resolveLayerRank: () => resolveLayerRank,
184
191
  resolveRefFields: () => resolveRefFields,
185
192
  runMachine: () => runMachine,
186
193
  selectAll: () => selectAll,
@@ -189,12 +196,12 @@ __export(index_exports, {
189
196
  worldToLocal: () => worldToLocal
190
197
  });
191
198
  module.exports = __toCommonJS(index_exports);
192
- var import_canvas_store48 = require("@woven-ecs/canvas-store");
193
- 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");
194
201
 
195
202
  // src/CorePlugin.ts
196
- var import_canvas_store46 = require("@woven-ecs/canvas-store");
197
- 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");
198
205
 
199
206
  // src/command.ts
200
207
  var import_core = require("@woven-ecs/core");
@@ -273,6 +280,7 @@ var SetCursor = defineCommand("set-cursor");
273
280
  var AddFrameHighlight = defineCommand("frame-add-highlight");
274
281
  var RemoveFrameHighlight = defineCommand("frame-remove-highlight");
275
282
  var AssignFrameChildren = defineCommand("frame-assign-children");
283
+ var PlaceBlockEvent = defineCommand("place-block");
276
284
 
277
285
  // src/commands/selection.ts
278
286
  var SelectBlock = defineCommand("select-block");
@@ -314,8 +322,10 @@ __export(components_exports, {
314
322
  HitGeometry: () => HitGeometry,
315
323
  Hovered: () => Hovered,
316
324
  Image: () => Image,
325
+ Layer: () => Layer,
317
326
  MAX_HIT_ARCS: () => MAX_HIT_ARCS,
318
327
  MAX_HIT_CAPSULES: () => MAX_HIT_CAPSULES,
328
+ MAX_HIT_POLYGON_POINTS: () => MAX_HIT_POLYGON_POINTS,
319
329
  Opacity: () => Opacity,
320
330
  Pointer: () => Pointer,
321
331
  PointerButton: () => PointerButton,
@@ -382,6 +392,9 @@ function getPluginResources(ctx, pluginName) {
382
392
  const resources = ctx.resources;
383
393
  return resources.pluginResources[pluginName];
384
394
  }
395
+ function isReadonly(ctx) {
396
+ return ctx.resources.readonly;
397
+ }
385
398
  var GridOptions = import_zod.z.object({
386
399
  /**
387
400
  * Whether grid snapping is enabled.
@@ -543,7 +556,16 @@ var EditorOptionsSchema = import_zod.z.object({
543
556
  * }
544
557
  * ```
545
558
  */
546
- controls: import_zod.z.custom().optional()
559
+ controls: import_zod.z.custom().optional(),
560
+ /**
561
+ * When true, user-initiated mutations are blocked — input systems skip
562
+ * their bodies and `editor.command()` is a no-op. Adapter-originated
563
+ * mutations (websocket, persistence) still apply, so remote edits remain
564
+ * visible. Useful for viewer-mode share links.
565
+ *
566
+ * @default false
567
+ */
568
+ readonly: import_zod.z.boolean().default(false)
547
569
  });
548
570
  var Keybind = import_zod.z.object({
549
571
  /** The command to execute when this keybind is triggered */
@@ -611,7 +633,9 @@ var BlockDef = import_zod.z.object({
611
633
  resizeMode: import_zod.z.enum([ResizeMode.Scale, ResizeMode.Text, ResizeMode.Free, ResizeMode.GroupOnly]).default(ResizeMode.Scale),
612
634
  canRotate: import_zod.z.boolean().default(true),
613
635
  canScale: import_zod.z.boolean().default(true),
636
+ snappable: import_zod.z.boolean().default(true),
614
637
  interactable: import_zod.z.boolean().default(true),
638
+ excludeFromRankBounds: import_zod.z.boolean().default(false),
615
639
  connectors: BlockDefConnectors.default(BlockDefConnectors.parse({}))
616
640
  });
617
641
  var SelectionState = {
@@ -677,7 +701,10 @@ var BlockSchema = {
677
701
  /** How the block can be resized */
678
702
  resizeMode: import_core2.field.enum(ResizeMode).default(ResizeMode.Default),
679
703
  /** Parent block ID — position is relative to parent if set */
680
- 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()
681
708
  };
682
709
  var _worldPos = [0, 0];
683
710
  var BlockDef2 = class extends import_canvas_store.CanvasComponentDef {
@@ -1170,15 +1197,21 @@ var import_math3 = require("@woven-canvas/math");
1170
1197
  var import_canvas_store13 = require("@woven-ecs/canvas-store");
1171
1198
  var import_core11 = require("@woven-ecs/core");
1172
1199
  var _uvToWorldMatrix = [1, 0, 0, 1, 0, 0];
1200
+ var _polyPt = [0, 0];
1173
1201
  var MAX_HIT_CAPSULES = 64;
1174
1202
  var FLOATS_PER_CAPSULE = 5;
1175
1203
  var MAX_HIT_ARCS = 2;
1176
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);
1177
1208
  var HitGeometrySchema = {
1178
1209
  hitCapsules: import_core11.field.buffer(import_core11.field.float32()).size(MAX_HIT_CAPSULES * FLOATS_PER_CAPSULE),
1179
1210
  capsuleCount: import_core11.field.uint16().default(0),
1180
1211
  hitArcs: import_core11.field.buffer(import_core11.field.float32()).size(MAX_HIT_ARCS * FLOATS_PER_ARC),
1181
- 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)
1182
1215
  };
1183
1216
  var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1184
1217
  constructor() {
@@ -1289,8 +1322,27 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1289
1322
  return true;
1290
1323
  }
1291
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
+ }
1292
1329
  return false;
1293
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
+ }
1294
1346
  /**
1295
1347
  * Check if an AABB intersects with the entity's hit geometry.
1296
1348
  * Hit geometry is stored in UV coordinates (0-1) relative to the block.
@@ -1330,6 +1382,10 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1330
1382
  return true;
1331
1383
  }
1332
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
+ }
1333
1389
  return false;
1334
1390
  }
1335
1391
  /**
@@ -1367,6 +1423,11 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1367
1423
  const worldArc = import_math3.Arc.create(worldA[0], worldA[1], worldB[0], worldB[1], worldC[0], worldC[1], thickness);
1368
1424
  pts.push(...import_math3.Arc.getExtrema(worldArc));
1369
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
+ }
1370
1431
  return pts;
1371
1432
  }
1372
1433
  // ============================================
@@ -1469,6 +1530,27 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1469
1530
  const uvC = Block.worldToUv(ctx, entityId, worldC);
1470
1531
  this.addArcUv(ctx, entityId, uvA, uvB, uvC, worldThickness);
1471
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
+ }
1472
1554
  /**
1473
1555
  * Clear all hit geometry from an entity.
1474
1556
  *
@@ -1479,6 +1561,7 @@ var HitGeometryDef = class extends import_canvas_store13.CanvasComponentDef {
1479
1561
  const hitGeometry = this.write(ctx, entityId);
1480
1562
  hitGeometry.capsuleCount = 0;
1481
1563
  hitGeometry.arcCount = 0;
1564
+ hitGeometry.polygonPointCount = 0;
1482
1565
  }
1483
1566
  };
1484
1567
  var HitGeometry = new HitGeometryDef();
@@ -1502,19 +1585,39 @@ var Image = (0, import_canvas_store15.defineCanvasComponent)(
1502
1585
  }
1503
1586
  );
1504
1587
 
1505
- // src/components/Opacity.ts
1588
+ // src/components/Layer.ts
1506
1589
  var import_canvas_store16 = require("@woven-ecs/canvas-store");
1507
1590
  var import_core13 = require("@woven-ecs/core");
1508
- var Opacity = (0, import_canvas_store16.defineCanvasComponent)(
1509
- { name: "opacity" },
1591
+ var Layer = (0, import_canvas_store16.defineCanvasComponent)(
1592
+ { name: "layer", sync: "document" },
1510
1593
  {
1511
- 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)
1512
1605
  }
1513
1606
  );
1514
1607
 
1515
- // src/components/Pointer.ts
1608
+ // src/components/Opacity.ts
1516
1609
  var import_canvas_store17 = require("@woven-ecs/canvas-store");
1517
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");
1518
1621
  var PointerButton = {
1519
1622
  None: "none",
1520
1623
  Left: "left",
@@ -1531,32 +1634,32 @@ var PointerType = {
1531
1634
  var SAMPLE_COUNT = 6;
1532
1635
  var PointerSchema = {
1533
1636
  /** Unique pointer ID (from PointerEvent.pointerId) */
1534
- pointerId: import_core14.field.uint16().default(0),
1637
+ pointerId: import_core15.field.uint16().default(0),
1535
1638
  /** Current position relative to the editor element [x, y] */
1536
- 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]),
1537
1640
  /** Position where the pointer went down [x, y] */
1538
- 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]),
1539
1642
  /** Frame number when the pointer went down (for click detection) */
1540
- downFrame: import_core14.field.uint32().default(0),
1643
+ downFrame: import_core15.field.uint32().default(0),
1541
1644
  /** Which button is pressed */
1542
- button: import_core14.field.enum(PointerButton).default(PointerButton.None),
1645
+ button: import_core15.field.enum(PointerButton).default(PointerButton.None),
1543
1646
  /** Type of pointer device */
1544
- pointerType: import_core14.field.enum(PointerType).default(PointerType.Mouse),
1647
+ pointerType: import_core15.field.enum(PointerType).default(PointerType.Mouse),
1545
1648
  /** Pressure from 0 to 1 (for pen/touch) */
1546
- pressure: import_core14.field.float32().default(0),
1649
+ pressure: import_core15.field.float32().default(0),
1547
1650
  /** Whether the pointer event target was not the editor element */
1548
- obscured: import_core14.field.boolean().default(false),
1651
+ obscured: import_core15.field.boolean().default(false),
1549
1652
  // Velocity tracking (ring buffer for position samples)
1550
1653
  /** Ring buffer of previous positions [x0, y0, x1, y1, ...] @internal */
1551
- _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),
1552
1655
  /** Ring buffer of timestamps for each position sample @internal */
1553
- _prevTimes: import_core14.field.array(import_core14.field.float32(), SAMPLE_COUNT),
1656
+ _prevTimes: import_core15.field.array(import_core15.field.float32(), SAMPLE_COUNT),
1554
1657
  /** Total number of samples added (used for ring buffer indexing) @internal */
1555
- _sampleCount: import_core14.field.int32().default(0),
1658
+ _sampleCount: import_core15.field.int32().default(0),
1556
1659
  /** Computed velocity [vx, vy] in pixels per second @internal */
1557
- _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])
1558
1661
  };
1559
- var PointerDef = class extends import_canvas_store17.CanvasComponentDef {
1662
+ var PointerDef = class extends import_canvas_store18.CanvasComponentDef {
1560
1663
  constructor() {
1561
1664
  super({ name: "pointer" }, PointerSchema);
1562
1665
  }
@@ -1665,96 +1768,96 @@ function addPointerSample(pointer, position, time) {
1665
1768
  }
1666
1769
 
1667
1770
  // src/components/ScaleWithZoom.ts
1668
- var import_canvas_store18 = require("@woven-ecs/canvas-store");
1669
- var import_core15 = require("@woven-ecs/core");
1670
- 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)(
1671
1774
  { name: "scaleWithZoom" },
1672
1775
  {
1673
1776
  /** Pivot point for scaling as [x, y] (0-1, default 0.5,0.5 = center) */
1674
- 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]),
1675
1778
  /** Initial position as [left, top] at zoom=1 */
1676
- 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]),
1677
1780
  /** Initial size as [width, height] at zoom=1 */
1678
- 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]),
1679
1782
  /** Scale multiplier per dimension: [x, y] (0 = no zoom effect, 1 = full zoom effect, 0.5 = half effect) */
1680
- 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])
1681
1784
  }
1682
1785
  );
1683
1786
 
1684
1787
  // src/components/Selected.ts
1685
- var import_canvas_store19 = require("@woven-ecs/canvas-store");
1686
- 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" }, {});
1687
1790
 
1688
1791
  // src/components/SelectionBox.ts
1689
- var import_canvas_store20 = require("@woven-ecs/canvas-store");
1690
- 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" }, {});
1691
1794
 
1692
1795
  // src/components/Shape.ts
1693
- var import_canvas_store21 = require("@woven-ecs/canvas-store");
1694
- 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");
1695
1798
  var StrokeKind = {
1696
1799
  Solid: "solid",
1697
1800
  Dashed: "dashed",
1698
1801
  None: "none"
1699
1802
  };
1700
- var Shape = (0, import_canvas_store21.defineCanvasComponent)(
1803
+ var Shape = (0, import_canvas_store22.defineCanvasComponent)(
1701
1804
  { name: "shape", sync: "document" },
1702
1805
  {
1703
1806
  /** The kind of shape to render (e.g. 'rectangle', 'ellipse', or custom shape key) */
1704
- kind: import_core16.field.string().default("rectangle"),
1807
+ kind: import_core17.field.string().default("rectangle"),
1705
1808
  /** Stroke style */
1706
- strokeKind: import_core16.field.enum(StrokeKind).default(StrokeKind.Solid),
1809
+ strokeKind: import_core17.field.enum(StrokeKind).default(StrokeKind.Solid),
1707
1810
  /** Stroke width in pixels */
1708
- strokeWidth: import_core16.field.uint16().default(2),
1811
+ strokeWidth: import_core17.field.uint16().default(2),
1709
1812
  /** Stroke color - red component (0-255) */
1710
- strokeRed: import_core16.field.uint8().default(0),
1813
+ strokeRed: import_core17.field.uint8().default(0),
1711
1814
  /** Stroke color - green component (0-255) */
1712
- strokeGreen: import_core16.field.uint8().default(0),
1815
+ strokeGreen: import_core17.field.uint8().default(0),
1713
1816
  /** Stroke color - blue component (0-255) */
1714
- strokeBlue: import_core16.field.uint8().default(0),
1817
+ strokeBlue: import_core17.field.uint8().default(0),
1715
1818
  /** Stroke color - alpha component (0-255) */
1716
- strokeAlpha: import_core16.field.uint8().default(255),
1819
+ strokeAlpha: import_core17.field.uint8().default(255),
1717
1820
  /** Fill color - red component (0-255) */
1718
- fillRed: import_core16.field.uint8().default(255),
1821
+ fillRed: import_core17.field.uint8().default(255),
1719
1822
  /** Fill color - green component (0-255) */
1720
- fillGreen: import_core16.field.uint8().default(255),
1823
+ fillGreen: import_core17.field.uint8().default(255),
1721
1824
  /** Fill color - blue component (0-255) */
1722
- fillBlue: import_core16.field.uint8().default(255),
1825
+ fillBlue: import_core17.field.uint8().default(255),
1723
1826
  /** Fill color - alpha component (0-255) */
1724
- fillAlpha: import_core16.field.uint8().default(0)
1827
+ fillAlpha: import_core17.field.uint8().default(0)
1725
1828
  }
1726
1829
  );
1727
1830
 
1728
1831
  // src/components/Text.ts
1729
- var import_canvas_store22 = require("@woven-ecs/canvas-store");
1730
- var import_core17 = require("@woven-ecs/core");
1731
- 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)(
1732
1835
  { name: "text", sync: "document" },
1733
1836
  {
1734
1837
  /** HTML content (supports rich text formatting) */
1735
- content: import_core17.field.string().max(1e4).default(""),
1838
+ content: import_core18.field.string().max(1e4).default(""),
1736
1839
  /** Font size in pixels */
1737
- fontSizePx: import_core17.field.float64().default(24),
1840
+ fontSizePx: import_core18.field.float64().default(24),
1738
1841
  /** Font family name */
1739
- fontFamily: import_core17.field.string().max(64).default(""),
1842
+ fontFamily: import_core18.field.string().max(64).default(""),
1740
1843
  /** Line height multiplier */
1741
- lineHeight: import_core17.field.float64().default(1.2),
1844
+ lineHeight: import_core18.field.float64().default(1.2),
1742
1845
  /** Letter spacing in em units */
1743
- letterSpacingEm: import_core17.field.float64().default(0),
1846
+ letterSpacingEm: import_core18.field.float64().default(0),
1744
1847
  /** Whether width is constrained (text wraps) */
1745
- constrainWidth: import_core17.field.boolean().default(true),
1848
+ constrainWidth: import_core18.field.boolean().default(true),
1746
1849
  /** Default text alignment for new paragraphs */
1747
- defaultAlignment: import_core17.field.enum(TextAlignment).default(TextAlignment.Left)
1850
+ defaultAlignment: import_core18.field.enum(TextAlignment).default(TextAlignment.Left)
1748
1851
  }
1749
1852
  );
1750
1853
 
1751
1854
  // src/components/TransformBox.ts
1752
- var import_canvas_store23 = require("@woven-ecs/canvas-store");
1753
- 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" }, {});
1754
1857
 
1755
1858
  // src/components/TransformHandle.ts
1756
- var import_canvas_store24 = require("@woven-ecs/canvas-store");
1757
- 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");
1758
1861
 
1759
1862
  // src/cursors.ts
1760
1863
  var CursorKind = /* @__PURE__ */ ((CursorKind2) => {
@@ -1855,39 +1958,39 @@ var CURSORS = {
1855
1958
  };
1856
1959
 
1857
1960
  // src/components/TransformHandle.ts
1858
- var TransformHandle = (0, import_canvas_store24.defineCanvasComponent)(
1961
+ var TransformHandle = (0, import_canvas_store25.defineCanvasComponent)(
1859
1962
  { name: "transformHandle" },
1860
1963
  {
1861
- kind: import_core18.field.string().max(16).default(TransformHandleKind.Scale),
1862
- vectorX: import_core18.field.int8().default(0),
1863
- vectorY: import_core18.field.int8().default(0),
1864
- transformBox: import_core18.field.ref(),
1865
- 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 */)
1866
1969
  }
1867
1970
  );
1868
1971
 
1869
1972
  // src/components/User.ts
1870
- var import_canvas_store25 = require("@woven-ecs/canvas-store");
1871
- var import_core19 = require("@woven-ecs/core");
1872
- 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)(
1873
1976
  { name: "user", sync: "ephemeral" },
1874
1977
  {
1875
- userId: import_core19.field.string().max(36),
1876
- sessionId: import_core19.field.string().max(36),
1877
- color: import_core19.field.string().max(7),
1878
- name: import_core19.field.string().max(100),
1879
- avatar: import_core19.field.string().max(500),
1880
- 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])
1881
1984
  }
1882
1985
  );
1883
1986
 
1884
1987
  // src/components/VerticalAlign.ts
1885
- var import_canvas_store26 = require("@woven-ecs/canvas-store");
1886
- var import_core20 = require("@woven-ecs/core");
1887
- 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)(
1888
1991
  { name: "verticalAlign", sync: "document" },
1889
1992
  {
1890
- value: import_core20.field.enum(VerticalAlignment).default(VerticalAlignment.Top)
1993
+ value: import_core21.field.enum(VerticalAlignment).default(VerticalAlignment.Top)
1891
1994
  }
1892
1995
  );
1893
1996
 
@@ -1925,23 +2028,23 @@ __export(singletons_exports, {
1925
2028
  });
1926
2029
 
1927
2030
  // src/singletons/Camera.ts
1928
- var import_canvas_store28 = require("@woven-ecs/canvas-store");
1929
- 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");
1930
2033
 
1931
2034
  // src/singletons/Screen.ts
1932
- var import_canvas_store27 = require("@woven-ecs/canvas-store");
1933
- 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");
1934
2037
  var ScreenSchema = {
1935
2038
  /** Width of the editor element in pixels */
1936
- width: import_core21.field.float64().default(0),
2039
+ width: import_core22.field.float64().default(0),
1937
2040
  /** Height of the editor element in pixels */
1938
- height: import_core21.field.float64().default(0),
2041
+ height: import_core22.field.float64().default(0),
1939
2042
  /** Left offset of the editor element relative to the viewport */
1940
- left: import_core21.field.float64().default(0),
2043
+ left: import_core22.field.float64().default(0),
1941
2044
  /** Top offset of the editor element relative to the viewport */
1942
- top: import_core21.field.float64().default(0)
2045
+ top: import_core22.field.float64().default(0)
1943
2046
  };
1944
- var ScreenDef = class extends import_canvas_store27.CanvasSingletonDef {
2047
+ var ScreenDef = class extends import_canvas_store28.CanvasSingletonDef {
1945
2048
  constructor() {
1946
2049
  super({ name: "screen" }, ScreenSchema);
1947
2050
  }
@@ -1966,17 +2069,17 @@ var Screen = new ScreenDef();
1966
2069
  // src/singletons/Camera.ts
1967
2070
  var CameraSchema = {
1968
2071
  /** Top position of the camera in world coordinates */
1969
- top: import_core22.field.float64().default(0),
2072
+ top: import_core23.field.float64().default(0),
1970
2073
  /** Left position of the camera in world coordinates */
1971
- left: import_core22.field.float64().default(0),
2074
+ left: import_core23.field.float64().default(0),
1972
2075
  /** Zoom level (1 = 100%, 2 = 200%, 0.5 = 50%) */
1973
- zoom: import_core22.field.float64().default(1),
2076
+ zoom: import_core23.field.float64().default(1),
1974
2077
  /** Whether the camera viewport intersects any blocks */
1975
- canSeeBlocks: import_core22.field.boolean().default(true),
2078
+ canSeeBlocks: import_core23.field.boolean().default(true),
1976
2079
  /** Reference to a block that the camera can currently see (for optimization) */
1977
- lastSeenBlock: import_core22.field.ref()
2080
+ lastSeenBlock: import_core23.field.ref()
1978
2081
  };
1979
- var CameraDef = class extends import_canvas_store28.CanvasSingletonDef {
2082
+ var CameraDef = class extends import_canvas_store29.CanvasSingletonDef {
1980
2083
  constructor() {
1981
2084
  super({ name: "camera", sync: "local" }, CameraSchema);
1982
2085
  }
@@ -2049,25 +2152,25 @@ var CameraDef = class extends import_canvas_store28.CanvasSingletonDef {
2049
2152
  var Camera = new CameraDef();
2050
2153
 
2051
2154
  // src/singletons/Controls.ts
2052
- var import_canvas_store29 = require("@woven-ecs/canvas-store");
2053
- 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");
2054
2157
  var ControlsSchema = {
2055
2158
  /** Tool activated by left mouse button */
2056
- leftMouseTool: import_core23.field.string().max(32).default("select"),
2159
+ leftMouseTool: import_core24.field.string().max(32).default("select"),
2057
2160
  /** Tool activated by middle mouse button */
2058
- middleMouseTool: import_core23.field.string().max(32).default("hand"),
2161
+ middleMouseTool: import_core24.field.string().max(32).default("hand"),
2059
2162
  /** Tool activated by right mouse button */
2060
- rightMouseTool: import_core23.field.string().max(32).default("hand"),
2163
+ rightMouseTool: import_core24.field.string().max(32).default("hand"),
2061
2164
  /** Tool activated by mouse wheel */
2062
- wheelTool: import_core23.field.string().max(32).default("scroll"),
2165
+ wheelTool: import_core24.field.string().max(32).default("scroll"),
2063
2166
  /** Tool activated by mouse wheel with modifier key held */
2064
- modWheelTool: import_core23.field.string().max(32).default("zoom"),
2167
+ modWheelTool: import_core24.field.string().max(32).default("zoom"),
2065
2168
  /** JSON snapshot of block to place on next click (empty string = no placement active) */
2066
- heldSnapshot: import_core23.field.string().max(65536).default(""),
2169
+ heldSnapshot: import_core24.field.string().max(65536).default(""),
2067
2170
  /** User-facing tool name for UI highlighting (may differ from leftMouseTool during draw/drag-out) */
2068
- activeToolName: import_core23.field.string().max(32).default("select")
2171
+ activeToolName: import_core24.field.string().max(32).default("select")
2069
2172
  };
2070
- var ControlsDef = class extends import_canvas_store29.CanvasSingletonDef {
2173
+ var ControlsDef = class extends import_canvas_store30.CanvasSingletonDef {
2071
2174
  constructor() {
2072
2175
  super({ name: "controls" }, ControlsSchema);
2073
2176
  }
@@ -2106,19 +2209,19 @@ var ControlsDef = class extends import_canvas_store29.CanvasSingletonDef {
2106
2209
  var Controls = new ControlsDef();
2107
2210
 
2108
2211
  // src/singletons/Cursor.ts
2109
- var import_canvas_store30 = require("@woven-ecs/canvas-store");
2110
- 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");
2111
2214
  var CursorSchema = {
2112
2215
  /** Base cursor kind (from current tool) */
2113
- cursorKind: import_core24.field.string().max(64).default("select"),
2216
+ cursorKind: import_core25.field.string().max(64).default("select"),
2114
2217
  /** Base cursor rotation in radians */
2115
- rotation: import_core24.field.float64().default(0),
2218
+ rotation: import_core25.field.float64().default(0),
2116
2219
  /** Context-specific cursor kind (overrides cursorKind when set, e.g., during drag/hover) */
2117
- contextCursorKind: import_core24.field.string().max(64).default(""),
2220
+ contextCursorKind: import_core25.field.string().max(64).default(""),
2118
2221
  /** Context cursor rotation in radians */
2119
- contextRotation: import_core24.field.float64().default(0)
2222
+ contextRotation: import_core25.field.float64().default(0)
2120
2223
  };
2121
- var CursorDef2 = class extends import_canvas_store30.CanvasSingletonDef {
2224
+ var CursorDef2 = class extends import_canvas_store31.CanvasSingletonDef {
2122
2225
  constructor() {
2123
2226
  super({ name: "cursor" }, CursorSchema);
2124
2227
  }
@@ -2163,10 +2266,10 @@ var CursorDef2 = class extends import_canvas_store30.CanvasSingletonDef {
2163
2266
  var Cursor = new CursorDef2();
2164
2267
 
2165
2268
  // src/singletons/FrameContainmentState.ts
2166
- var import_core25 = require("@woven-ecs/core");
2269
+ var import_core26 = require("@woven-ecs/core");
2167
2270
 
2168
2271
  // src/EditorStateDef.ts
2169
- var import_canvas_store31 = require("@woven-ecs/canvas-store");
2272
+ var import_canvas_store32 = require("@woven-ecs/canvas-store");
2170
2273
 
2171
2274
  // src/machine.ts
2172
2275
  var import_xstate = require("xstate");
@@ -2194,7 +2297,7 @@ function runMachine(machine, currentState, context, events) {
2194
2297
  }
2195
2298
 
2196
2299
  // src/EditorStateDef.ts
2197
- var EditorStateDef = class extends import_canvas_store31.CanvasSingletonDef {
2300
+ var EditorStateDef = class extends import_canvas_store32.CanvasSingletonDef {
2198
2301
  constructor(name, schema) {
2199
2302
  super({ name, sync: "none" }, schema);
2200
2303
  }
@@ -2271,29 +2374,29 @@ function defineEditorState(name, schema) {
2271
2374
  // src/singletons/FrameContainmentState.ts
2272
2375
  var FrameContainmentState = defineEditorState("frameContainmentState", {
2273
2376
  /** Current state machine state */
2274
- state: import_core25.field.string().max(16).default(FrameContainmentStateEnum.Idle),
2377
+ state: import_core26.field.string().max(16).default(FrameContainmentStateEnum.Idle),
2275
2378
  /** EntityId of the frame currently highlighted as a drop target, or null */
2276
- highlightedFrame: import_core25.field.ref()
2379
+ highlightedFrame: import_core26.field.ref()
2277
2380
  });
2278
2381
 
2279
2382
  // src/singletons/Grid.ts
2280
- var import_canvas_store32 = require("@woven-ecs/canvas-store");
2281
- 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");
2282
2385
  var GridSchema = {
2283
2386
  /** Whether grid snapping is enabled */
2284
- enabled: import_core26.field.boolean().default(false),
2387
+ enabled: import_core27.field.boolean().default(false),
2285
2388
  /** Whether resized/rotated objects must stay aligned to the grid */
2286
- strict: import_core26.field.boolean().default(false),
2389
+ strict: import_core27.field.boolean().default(false),
2287
2390
  /** Width of each grid column in world units */
2288
- colWidth: import_core26.field.float64().default(20),
2391
+ colWidth: import_core27.field.float64().default(20),
2289
2392
  /** Height of each grid row in world units */
2290
- rowHeight: import_core26.field.float64().default(20),
2393
+ rowHeight: import_core27.field.float64().default(20),
2291
2394
  /** Angular snap increment in radians when grid is enabled */
2292
- snapAngleRad: import_core26.field.float64().default(Math.PI / 36),
2395
+ snapAngleRad: import_core27.field.float64().default(Math.PI / 36),
2293
2396
  /** Angular snap increment in radians when shift key is held */
2294
- shiftSnapAngleRad: import_core26.field.float64().default(Math.PI / 12)
2397
+ shiftSnapAngleRad: import_core27.field.float64().default(Math.PI / 12)
2295
2398
  };
2296
- var GridDef = class extends import_canvas_store32.CanvasSingletonDef {
2399
+ var GridDef = class extends import_canvas_store33.CanvasSingletonDef {
2297
2400
  constructor() {
2298
2401
  super({ name: "grid" }, GridSchema);
2299
2402
  }
@@ -2353,17 +2456,17 @@ var GridDef = class extends import_canvas_store32.CanvasSingletonDef {
2353
2456
  var Grid = new GridDef();
2354
2457
 
2355
2458
  // src/singletons/Intersect.ts
2356
- var import_canvas_store33 = require("@woven-ecs/canvas-store");
2357
- 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");
2358
2461
  var IntersectSchema = {
2359
2462
  // Store up to 5 intersected entity IDs
2360
- entity1: import_core27.field.ref(),
2361
- entity2: import_core27.field.ref(),
2362
- entity3: import_core27.field.ref(),
2363
- entity4: import_core27.field.ref(),
2364
- 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()
2365
2468
  };
2366
- var IntersectDef = class extends import_canvas_store33.CanvasSingletonDef {
2469
+ var IntersectDef = class extends import_canvas_store34.CanvasSingletonDef {
2367
2470
  constructor() {
2368
2471
  super({ name: "intersect" }, IntersectSchema);
2369
2472
  }
@@ -2412,31 +2515,31 @@ var IntersectDef = class extends import_canvas_store33.CanvasSingletonDef {
2412
2515
  var Intersect = new IntersectDef();
2413
2516
 
2414
2517
  // src/singletons/Keyboard.ts
2415
- var import_canvas_store34 = require("@woven-ecs/canvas-store");
2416
- 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");
2417
2520
  var KEY_BUFFER_SIZE = 32;
2418
2521
  var KeyboardSchema = {
2419
2522
  /**
2420
2523
  * Buffer where each bit represents whether a key is currently pressed.
2421
2524
  * Uses field.buffer for zero-allocation subarray views.
2422
2525
  */
2423
- 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),
2424
2527
  /**
2425
2528
  * Buffer for key-down triggers (true for exactly 1 frame when key is pressed).
2426
2529
  * Uses field.buffer for zero-allocation subarray views.
2427
2530
  */
2428
- 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),
2429
2532
  /**
2430
2533
  * Buffer for key-up triggers (true for exactly 1 frame when key is released).
2431
2534
  * Uses field.buffer for zero-allocation subarray views.
2432
2535
  */
2433
- 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),
2434
2537
  /** Common modifier - Shift key is down */
2435
- shiftDown: import_core28.field.boolean().default(false),
2538
+ shiftDown: import_core29.field.boolean().default(false),
2436
2539
  /** Common modifier - Alt/Option key is down */
2437
- altDown: import_core28.field.boolean().default(false),
2540
+ altDown: import_core29.field.boolean().default(false),
2438
2541
  /** Common modifier - Ctrl (Windows/Linux) or Cmd (Mac) is down */
2439
- modDown: import_core28.field.boolean().default(false)
2542
+ modDown: import_core29.field.boolean().default(false)
2440
2543
  };
2441
2544
  function getBit(buffer, bitIndex) {
2442
2545
  if (bitIndex < 0 || bitIndex >= buffer.length * 8) return false;
@@ -2444,7 +2547,7 @@ function getBit(buffer, bitIndex) {
2444
2547
  const bitOffset = bitIndex % 8;
2445
2548
  return (buffer[byteIndex] & 1 << bitOffset) !== 0;
2446
2549
  }
2447
- var KeyboardDef = class extends import_canvas_store34.CanvasSingletonDef {
2550
+ var KeyboardDef = class extends import_canvas_store35.CanvasSingletonDef {
2448
2551
  constructor() {
2449
2552
  super({ name: "keyboard" }, KeyboardSchema);
2450
2553
  }
@@ -2718,27 +2821,27 @@ var Key = {
2718
2821
  };
2719
2822
 
2720
2823
  // src/singletons/Mouse.ts
2721
- var import_canvas_store35 = require("@woven-ecs/canvas-store");
2722
- 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");
2723
2826
  var MouseSchema = {
2724
2827
  /** Current mouse position relative to the editor element [x, y] */
2725
- 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]),
2726
2829
  /** Horizontal wheel delta (positive = scroll right) */
2727
- wheelDeltaX: import_core29.field.float32().default(0),
2830
+ wheelDeltaX: import_core30.field.float32().default(0),
2728
2831
  /** Vertical wheel delta (positive = scroll down), normalized across browsers */
2729
- wheelDeltaY: import_core29.field.float32().default(0),
2832
+ wheelDeltaY: import_core30.field.float32().default(0),
2730
2833
  /** True for 1 frame when mouse moves */
2731
- moveTrigger: import_core29.field.boolean().default(false),
2834
+ moveTrigger: import_core30.field.boolean().default(false),
2732
2835
  /** True for 1 frame when wheel is scrolled */
2733
- wheelTrigger: import_core29.field.boolean().default(false),
2836
+ wheelTrigger: import_core30.field.boolean().default(false),
2734
2837
  /** True for 1 frame when mouse enters the editor element */
2735
- enterTrigger: import_core29.field.boolean().default(false),
2838
+ enterTrigger: import_core30.field.boolean().default(false),
2736
2839
  /** True for 1 frame when mouse leaves the editor element */
2737
- leaveTrigger: import_core29.field.boolean().default(false),
2840
+ leaveTrigger: import_core30.field.boolean().default(false),
2738
2841
  /** Whether the mouse is over a UI element (not the canvas) */
2739
- obscured: import_core29.field.boolean().default(false)
2842
+ obscured: import_core30.field.boolean().default(false)
2740
2843
  };
2741
- var MouseDef = class extends import_canvas_store35.CanvasSingletonDef {
2844
+ var MouseDef = class extends import_canvas_store36.CanvasSingletonDef {
2742
2845
  constructor() {
2743
2846
  super({ name: "mouse" }, MouseSchema);
2744
2847
  }
@@ -2776,14 +2879,14 @@ var MouseDef = class extends import_canvas_store35.CanvasSingletonDef {
2776
2879
  var Mouse = new MouseDef();
2777
2880
 
2778
2881
  // src/singletons/RankBounds.ts
2779
- var import_canvas_store36 = require("@woven-ecs/canvas-store");
2780
- 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");
2781
2884
  var import_fractional_indexing_jittered = require("fractional-indexing-jittered");
2782
2885
  var RankBoundsSchema = {
2783
- minRank: import_core30.field.string().max(36).default(""),
2784
- 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("")
2785
2888
  };
2786
- var RankBoundsDef = class extends import_canvas_store36.CanvasSingletonDef {
2889
+ var RankBoundsDef = class extends import_canvas_store37.CanvasSingletonDef {
2787
2890
  constructor() {
2788
2891
  super({ name: "rankBounds" }, RankBoundsSchema);
2789
2892
  }
@@ -2839,73 +2942,73 @@ var RankBoundsDef = class extends import_canvas_store36.CanvasSingletonDef {
2839
2942
  var RankBounds = new RankBoundsDef();
2840
2943
 
2841
2944
  // src/singletons/ScaleWithZoomState.ts
2842
- var import_canvas_store37 = require("@woven-ecs/canvas-store");
2843
- var import_core31 = require("@woven-ecs/core");
2844
- 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)(
2845
2948
  { name: "scaleWithZoomState" },
2846
2949
  {
2847
2950
  /** Last processed zoom level */
2848
- lastZoom: import_core31.field.float64().default(1)
2951
+ lastZoom: import_core32.field.float64().default(1)
2849
2952
  }
2850
2953
  );
2851
2954
 
2852
2955
  // src/singletons/ScrollEdgesStateSingleton.ts
2853
- var import_core32 = require("@woven-ecs/core");
2956
+ var import_core33 = require("@woven-ecs/core");
2854
2957
  var ScrollEdgesStateSingleton = defineEditorState("scrollEdgesState", {
2855
2958
  /** Current state of the scroll edges state machine */
2856
- state: import_core32.field.enum(ScrollEdgesState).default(ScrollEdgesState.Idle),
2959
+ state: import_core33.field.enum(ScrollEdgesState).default(ScrollEdgesState.Idle),
2857
2960
  /** Timestamp when pointer first entered the edge zone */
2858
- edgeEnterStartTime: import_core32.field.float64().default(0)
2961
+ edgeEnterStartTime: import_core33.field.float64().default(0)
2859
2962
  });
2860
2963
 
2861
2964
  // src/singletons/SelectionStateSingleton.ts
2862
- var import_core33 = require("@woven-ecs/core");
2965
+ var import_core34 = require("@woven-ecs/core");
2863
2966
  var SelectionStateSingleton = defineEditorState("selectionState", {
2864
- state: import_core33.field.enum(SelectionState).default(SelectionState.Idle),
2865
- dragStart: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2866
- pointingStartClient: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2867
- pointingStartWorld: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2868
- draggedEntityStart: import_core33.field.tuple(import_core33.field.float64(), 2).default([0, 0]),
2869
- draggedEntity: import_core33.field.ref(),
2870
- cloneGeneratorSeed: import_core33.field.string().max(36).default(""),
2871
- 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()
2872
2975
  });
2873
2976
 
2874
2977
  // src/singletons/Tick.ts
2875
- var import_canvas_store38 = require("@woven-ecs/canvas-store");
2876
- var import_core34 = require("@woven-ecs/core");
2877
- 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)(
2878
2981
  { name: "tick" },
2879
2982
  {
2880
2983
  /** Current tick number (increments each tick) */
2881
- number: import_core34.field.uint32().default(0),
2984
+ number: import_core35.field.uint32().default(0),
2882
2985
  /** Time since last tick in seconds */
2883
- delta: import_core34.field.float64().default(0),
2986
+ delta: import_core35.field.float64().default(0),
2884
2987
  /** Timestamp of current tick in milliseconds (from performance.now()) */
2885
- time: import_core34.field.float64().default(0),
2988
+ time: import_core35.field.float64().default(0),
2886
2989
  /** Timestamp of previous tick in milliseconds (0 if first tick) */
2887
- lastTime: import_core34.field.float64().default(0)
2990
+ lastTime: import_core35.field.float64().default(0)
2888
2991
  }
2889
2992
  );
2890
2993
 
2891
2994
  // src/singletons/TransformBoxStateSingleton.ts
2892
- var import_core35 = require("@woven-ecs/core");
2995
+ var import_core36 = require("@woven-ecs/core");
2893
2996
  var TransformBoxStateSingleton = defineEditorState("transformBoxState", {
2894
- state: import_core35.field.string().max(32).default(TransformBoxState.None),
2895
- transformBoxId: import_core35.field.ref()
2997
+ state: import_core36.field.string().max(32).default(TransformBoxState.None),
2998
+ transformBoxId: import_core36.field.ref()
2896
2999
  });
2897
3000
 
2898
3001
  // src/systems/capture/captureFrameContainmentSystem.ts
2899
- var import_canvas_store40 = require("@woven-ecs/canvas-store");
2900
- 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");
2901
3004
  var import_xstate2 = require("xstate");
2902
3005
 
2903
3006
  // src/EditorSystem.ts
2904
- var import_core36 = require("@woven-ecs/core");
3007
+ var import_core37 = require("@woven-ecs/core");
2905
3008
  var DEFAULT_PRIORITY = 0;
2906
3009
  function defineEditorSystem(options, execute) {
2907
3010
  return {
2908
- _system: new import_core36.MainThreadSystem(execute),
3011
+ _system: new import_core37.MainThreadSystem(execute),
2909
3012
  phase: options.phase,
2910
3013
  priority: options.priority ?? DEFAULT_PRIORITY
2911
3014
  };
@@ -2968,12 +3071,12 @@ function getMouseInput(ctx) {
2968
3071
 
2969
3072
  // src/events/pointerInputEvents.ts
2970
3073
  var import_math7 = require("@woven-canvas/math");
2971
- var import_core47 = require("@woven-ecs/core");
3074
+ var import_core50 = require("@woven-ecs/core");
2972
3075
 
2973
3076
  // src/helpers/blockDefs.ts
2974
- var import_core37 = require("@woven-ecs/core");
3077
+ var import_core38 = require("@woven-ecs/core");
2975
3078
  function getBlockDefs(ctx) {
2976
- const { editor } = (0, import_core37.getResources)(ctx);
3079
+ const { editor } = (0, import_core38.getResources)(ctx);
2977
3080
  return editor.blockDefs;
2978
3081
  }
2979
3082
  function getBlockDef(ctx, tag) {
@@ -2986,6 +3089,9 @@ function canBlockEdit(ctx, tag) {
2986
3089
  function canBlockInteract(ctx, tag) {
2987
3090
  return getBlockDef(ctx, tag).interactable;
2988
3091
  }
3092
+ function canBlockSnap(ctx, tag) {
3093
+ return getBlockDef(ctx, tag).snappable;
3094
+ }
2989
3095
  function getBlockResizeMode(ctx, entityId) {
2990
3096
  const block = Block.read(ctx, entityId);
2991
3097
  if (block.resizeMode !== ResizeMode.Default) {
@@ -2997,9 +3103,9 @@ function getBlockResizeMode(ctx, entityId) {
2997
3103
 
2998
3104
  // src/helpers/computeAabb.ts
2999
3105
  var import_math4 = require("@woven-canvas/math");
3000
- var import_core38 = require("@woven-ecs/core");
3106
+ var import_core39 = require("@woven-ecs/core");
3001
3107
  function computeAabb(ctx, entityId, out) {
3002
- if ((0, import_core38.hasComponent)(ctx, entityId, HitGeometry)) {
3108
+ if ((0, import_core39.hasComponent)(ctx, entityId, HitGeometry)) {
3003
3109
  const pts = HitGeometry.getExtremaWorld(ctx, entityId);
3004
3110
  import_math4.Aabb.setFromPoints(out, pts);
3005
3111
  } else {
@@ -3008,6 +3114,17 @@ function computeAabb(ctx, entityId, out) {
3008
3114
  }
3009
3115
  }
3010
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
+
3011
3128
  // src/helpers/embed.ts
3012
3129
  function detectEmbedProvider(url) {
3013
3130
  try {
@@ -3139,10 +3256,10 @@ function resolveEmbedUrl(url, provider) {
3139
3256
  }
3140
3257
 
3141
3258
  // src/helpers/findFrameAtPoint.ts
3142
- var import_core40 = require("@woven-ecs/core");
3259
+ var import_core42 = require("@woven-ecs/core");
3143
3260
 
3144
3261
  // src/helpers/hierarchy.ts
3145
- var import_core39 = require("@woven-ecs/core");
3262
+ var import_core41 = require("@woven-ecs/core");
3146
3263
  function getWorldPosition(ctx, entityId, out) {
3147
3264
  return Block.getWorldPosition(ctx, entityId, out);
3148
3265
  }
@@ -3150,7 +3267,7 @@ function worldToLocal(ctx, entityId, worldPos, out) {
3150
3267
  const result = out ?? [0, 0];
3151
3268
  const block = Block.read(ctx, entityId);
3152
3269
  const parentId = block.parentId;
3153
- if (parentId === null || !(0, import_core39.hasComponent)(ctx, parentId, Block)) {
3270
+ if (parentId === null || !(0, import_core41.hasComponent)(ctx, parentId, Block)) {
3154
3271
  result[0] = worldPos[0];
3155
3272
  result[1] = worldPos[1];
3156
3273
  return result;
@@ -3163,7 +3280,7 @@ function worldToLocal(ctx, entityId, worldPos, out) {
3163
3280
  function isAncestor(ctx, entityId, candidateAncestor) {
3164
3281
  let current = entityId;
3165
3282
  const visited = /* @__PURE__ */ new Set();
3166
- while ((0, import_core39.hasComponent)(ctx, current, Block)) {
3283
+ while ((0, import_core41.hasComponent)(ctx, current, Block)) {
3167
3284
  const block = Block.read(ctx, current);
3168
3285
  const parentId = block.parentId;
3169
3286
  if (parentId === null) break;
@@ -3175,11 +3292,11 @@ function isAncestor(ctx, entityId, candidateAncestor) {
3175
3292
  return false;
3176
3293
  }
3177
3294
  function getChildren(ctx, parentId) {
3178
- return [...(0, import_core39.getBackrefs)(ctx, parentId, Block, "parentId")];
3295
+ return [...(0, import_core41.getBackrefs)(ctx, parentId, Block, "parentId")];
3179
3296
  }
3180
3297
  function getDescendants(ctx, parentId) {
3181
3298
  const result = [];
3182
- const children = (0, import_core39.getBackrefs)(ctx, parentId, Block, "parentId");
3299
+ const children = (0, import_core41.getBackrefs)(ctx, parentId, Block, "parentId");
3183
3300
  for (const childId of children) {
3184
3301
  result.push(childId);
3185
3302
  result.push(...getDescendants(ctx, childId));
@@ -3187,11 +3304,11 @@ function getDescendants(ctx, parentId) {
3187
3304
  return result;
3188
3305
  }
3189
3306
  function cascadeDelete(ctx, entityId) {
3190
- const children = (0, import_core39.getBackrefs)(ctx, entityId, Block, "parentId");
3307
+ const children = (0, import_core41.getBackrefs)(ctx, entityId, Block, "parentId");
3191
3308
  for (const childId of children) {
3192
3309
  cascadeDelete(ctx, childId);
3193
3310
  }
3194
- (0, import_core39.removeEntity)(ctx, entityId);
3311
+ (0, import_core41.removeEntity)(ctx, entityId);
3195
3312
  }
3196
3313
 
3197
3314
  // src/helpers/sortBlocks.ts
@@ -3221,16 +3338,19 @@ function compareRankChains(a, b) {
3221
3338
  function compareBlockOrder(getBlock, a, b) {
3222
3339
  const stratumDiff = STRATUM_ORDER[a.stratum] - STRATUM_ORDER[b.stratum];
3223
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;
3224
3344
  const chainA = getRankChain(getBlock, a);
3225
3345
  const chainB = getRankChain(getBlock, b);
3226
3346
  return compareRankChains(chainA, chainB);
3227
3347
  }
3228
3348
 
3229
3349
  // src/helpers/findFrameAtPoint.ts
3230
- var frameQuery = (0, import_core40.defineQuery)((q) => q.with(Block, Frame));
3350
+ var frameQuery = (0, import_core42.defineQuery)((q) => q.with(Block, Frame));
3231
3351
  function getBlockDef2(ctx) {
3232
3352
  return (id) => {
3233
- if (!(0, import_core40.hasComponent)(ctx, id, Block)) return null;
3353
+ if (!(0, import_core42.hasComponent)(ctx, id, Block)) return null;
3234
3354
  const block = Block.read(ctx, id);
3235
3355
  return { rank: block.rank, stratum: "content", parentId: block.parentId };
3236
3356
  };
@@ -3241,7 +3361,7 @@ function findFrameAtPoint(ctx, point, exclude = null) {
3241
3361
  const getBlock = getBlockDef2(ctx);
3242
3362
  for (const frameId of frameQuery.current(ctx)) {
3243
3363
  if (frameId === exclude) continue;
3244
- if ((0, import_core40.hasComponent)(ctx, frameId, Selected)) continue;
3364
+ if ((0, import_core42.hasComponent)(ctx, frameId, Selected)) continue;
3245
3365
  if (!Aabb.containsPoint(ctx, frameId, point)) continue;
3246
3366
  if (exclude !== null && isAncestor(ctx, frameId, exclude)) continue;
3247
3367
  const block = Block.read(ctx, frameId);
@@ -3255,22 +3375,40 @@ function findFrameAtPoint(ctx, point, exclude = null) {
3255
3375
  }
3256
3376
 
3257
3377
  // src/helpers/held.ts
3258
- var import_core41 = require("@woven-ecs/core");
3378
+ var import_core43 = require("@woven-ecs/core");
3259
3379
  function isHeldByRemote(ctx, entityId) {
3260
- if (!(0, import_core41.hasComponent)(ctx, entityId, Held)) return false;
3261
- 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);
3262
3382
  const held = Held.read(ctx, entityId);
3263
3383
  return held.sessionId !== "" && held.sessionId !== sessionId;
3264
3384
  }
3265
3385
 
3266
3386
  // src/helpers/intersect.ts
3267
3387
  var import_math5 = require("@woven-canvas/math");
3268
- var import_core42 = require("@woven-ecs/core");
3269
- 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));
3270
3408
  function isPointInsideAncestorFrames(ctx, entityId, point) {
3271
3409
  let current = Block.read(ctx, entityId).parentId;
3272
- while (current !== null && (0, import_core42.hasComponent)(ctx, current, Block)) {
3273
- 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)) {
3274
3412
  if (!Aabb.containsPoint(ctx, current, point)) {
3275
3413
  return false;
3276
3414
  }
@@ -3281,8 +3419,8 @@ function isPointInsideAncestorFrames(ctx, entityId, point) {
3281
3419
  }
3282
3420
  function isAabbInsideAncestorFrames(ctx, entityId, bounds) {
3283
3421
  let current = Block.read(ctx, entityId).parentId;
3284
- while (current !== null && (0, import_core42.hasComponent)(ctx, current, Block)) {
3285
- 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)) {
3286
3424
  const { value: frameAabb } = Aabb.read(ctx, current);
3287
3425
  if (!import_math5.Aabb.intersects(bounds, frameAabb)) {
3288
3426
  return false;
@@ -3296,10 +3434,13 @@ function intersectPoint(ctx, point, entityIds) {
3296
3434
  const intersects = [];
3297
3435
  const entities = entityIds ?? blocksWithAabb.current(ctx);
3298
3436
  for (const entityId of entities) {
3437
+ const block = Block.read(ctx, entityId);
3438
+ if (!canBlockInteract(ctx, block.tag)) continue;
3439
+ if (!isLayerInteractive(ctx, block.layerId)) continue;
3299
3440
  if (!Aabb.containsPoint(ctx, entityId, point)) {
3300
3441
  continue;
3301
3442
  }
3302
- if ((0, import_core42.hasComponent)(ctx, entityId, HitGeometry)) {
3443
+ if ((0, import_core45.hasComponent)(ctx, entityId, HitGeometry)) {
3303
3444
  if (!HitGeometry.containsPointWorld(ctx, entityId, point)) {
3304
3445
  continue;
3305
3446
  }
@@ -3319,12 +3460,15 @@ function intersectAabb(ctx, bounds, entityIds) {
3319
3460
  const intersecting = [];
3320
3461
  const entities = entityIds ?? blocksWithAabb.current(ctx);
3321
3462
  for (const entityId of entities) {
3463
+ const block = Block.read(ctx, entityId);
3464
+ if (!canBlockInteract(ctx, block.tag)) continue;
3465
+ if (!isLayerInteractive(ctx, block.layerId)) continue;
3322
3466
  const { value: entityAabb } = Aabb.read(ctx, entityId);
3323
3467
  if (!import_math5.Aabb.intersects(bounds, entityAabb)) {
3324
3468
  continue;
3325
3469
  }
3326
3470
  if (!import_math5.Aabb.contains(bounds, entityAabb)) {
3327
- if ((0, import_core42.hasComponent)(ctx, entityId, HitGeometry)) {
3471
+ if ((0, import_core45.hasComponent)(ctx, entityId, HitGeometry)) {
3328
3472
  if (!HitGeometry.intersectsAabbWorld(ctx, entityId, bounds)) {
3329
3473
  continue;
3330
3474
  }
@@ -3343,12 +3487,13 @@ function intersectAabb(ctx, bounds, entityIds) {
3343
3487
  }
3344
3488
  function sortByRankDescending(ctx, entityIds) {
3345
3489
  const getBlock = (id) => {
3346
- if (!(0, import_core42.hasComponent)(ctx, id, Block)) return null;
3490
+ if (!(0, import_core45.hasComponent)(ctx, id, Block)) return null;
3347
3491
  const block = Block.read(ctx, id);
3348
3492
  return {
3349
3493
  rank: block.rank,
3350
3494
  stratum: getBlockDef(ctx, block.tag).stratum,
3351
- parentId: block.parentId
3495
+ parentId: block.parentId,
3496
+ layerRank: resolveLayerRank(ctx, id)
3352
3497
  };
3353
3498
  };
3354
3499
  const entries = entityIds.map((id) => ({
@@ -3365,12 +3510,14 @@ function getTopmostBlockAtPoint(ctx, point) {
3365
3510
 
3366
3511
  // src/helpers/intersectCapsule.ts
3367
3512
  var import_math6 = require("@woven-canvas/math");
3368
- var import_core43 = require("@woven-ecs/core");
3513
+ var import_core46 = require("@woven-ecs/core");
3369
3514
  var _uvToWorldMatrix2 = [1, 0, 0, 1, 0, 0];
3515
+ var _worldPolygon2 = new Float32Array(MAX_HIT_POLYGON_POINTS * 2);
3516
+ var _polyPt2 = [0, 0];
3370
3517
  function isCapsuleInsideAncestorFrames(ctx, entityId, capsule) {
3371
3518
  let current = Block.read(ctx, entityId).parentId;
3372
- while (current !== null && (0, import_core43.hasComponent)(ctx, current, Block)) {
3373
- 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)) {
3374
3521
  const { value: frameAabb } = Aabb.read(ctx, current);
3375
3522
  if (!import_math6.Capsule.intersectsAabb(capsule, frameAabb)) {
3376
3523
  return false;
@@ -3388,7 +3535,7 @@ function intersectCapsule(ctx, capsule, entityIds) {
3388
3535
  if (!import_math6.Aabb.intersects(capsuleBounds, aabb.value)) {
3389
3536
  continue;
3390
3537
  }
3391
- if ((0, import_core43.hasComponent)(ctx, entityId, HitGeometry)) {
3538
+ if ((0, import_core46.hasComponent)(ctx, entityId, HitGeometry)) {
3392
3539
  if (!intersectsCapsuleHitGeometry(ctx, entityId, capsule)) {
3393
3540
  continue;
3394
3541
  }
@@ -3435,12 +3582,25 @@ function intersectsCapsuleHitGeometry(ctx, entityId, capsule) {
3435
3582
  return true;
3436
3583
  }
3437
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
+ }
3438
3598
  return false;
3439
3599
  }
3440
3600
 
3441
3601
  // src/helpers/ref.ts
3442
- var import_canvas_store39 = require("@woven-ecs/canvas-store");
3443
- 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");
3444
3604
  function convertRefsToUuids(ctx, schema, data) {
3445
3605
  const result = { ...data };
3446
3606
  for (const [fieldName, fieldBuilder] of Object.entries(schema)) {
@@ -3453,8 +3613,8 @@ function convertRefsToUuids(ctx, schema, data) {
3453
3613
  }
3454
3614
  function getEntityUuid(ctx, entityId) {
3455
3615
  if (entityId == null) return null;
3456
- if (!(0, import_core44.hasComponent)(ctx, entityId, import_canvas_store39.Synced)) return null;
3457
- 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;
3458
3618
  }
3459
3619
  function getRefFieldNames(schema) {
3460
3620
  const refFields = [];
@@ -3478,26 +3638,26 @@ function resolveRefFields(ctx, entityId, componentDef, componentData, uuidToEnti
3478
3638
  }
3479
3639
 
3480
3640
  // src/helpers/select.ts
3481
- var import_core45 = require("@woven-ecs/core");
3482
- var selectedQuery = (0, import_core45.defineQuery)((q) => q.with(Selected));
3483
- 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));
3484
3644
  function selectBlock(ctx, entityId) {
3485
- if ((0, import_core45.hasComponent)(ctx, entityId, Selected)) return;
3645
+ if ((0, import_core48.hasComponent)(ctx, entityId, Selected)) return;
3486
3646
  if (isHeldByRemote(ctx, entityId)) return;
3487
- if ((0, import_core45.hasComponent)(ctx, entityId, Block)) {
3647
+ if ((0, import_core48.hasComponent)(ctx, entityId, Block)) {
3488
3648
  const block = Block.read(ctx, entityId);
3489
3649
  if (!canBlockInteract(ctx, block.tag)) return;
3490
3650
  }
3491
- const { sessionId } = (0, import_core45.getResources)(ctx);
3492
- (0, import_core45.addComponent)(ctx, entityId, Selected, {});
3493
- if (!(0, import_core45.hasComponent)(ctx, entityId, Held)) {
3494
- (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 });
3495
3655
  }
3496
3656
  }
3497
3657
  function deselectBlock(ctx, entityId) {
3498
- if (!(0, import_core45.hasComponent)(ctx, entityId, Selected)) return;
3499
- (0, import_core45.removeComponent)(ctx, entityId, Selected);
3500
- (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);
3501
3661
  }
3502
3662
  function deselectAll(ctx) {
3503
3663
  for (const entityId of selectedQuery.current(ctx)) {
@@ -3511,10 +3671,10 @@ function selectAll(ctx) {
3511
3671
  }
3512
3672
 
3513
3673
  // src/helpers/user.ts
3514
- var import_core46 = require("@woven-ecs/core");
3515
- 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));
3516
3676
  function getMyUserEntityId(ctx) {
3517
- const mySessionId = (0, import_core46.getResources)(ctx).sessionId;
3677
+ const mySessionId = (0, import_core49.getResources)(ctx).sessionId;
3518
3678
  for (const eid of userQuery.current(ctx)) {
3519
3679
  const user = User.read(ctx, eid);
3520
3680
  if (user.sessionId === mySessionId) {
@@ -3581,7 +3741,7 @@ function generateUuidBySeed(originalUuid, generatorSeed) {
3581
3741
  // src/events/pointerInputEvents.ts
3582
3742
  var DEFAULT_CLICK_MOVE_THRESHOLD = 3;
3583
3743
  var DEFAULT_CLICK_FRAME_THRESHOLD = 30;
3584
- var pointerQuery = (0, import_core47.defineQuery)((q) => q.tracking(Pointer));
3744
+ var pointerQuery = (0, import_core50.defineQuery)((q) => q.tracking(Pointer));
3585
3745
  var trackingState = /* @__PURE__ */ new Map();
3586
3746
  function getTrackingState(ctx) {
3587
3747
  const key = ctx.readerId;
@@ -3704,18 +3864,18 @@ function clearPointerTrackingState(ctx) {
3704
3864
  }
3705
3865
 
3706
3866
  // src/systems/capture/captureFrameContainmentSystem.ts
3707
- var selectedQuery2 = (0, import_core48.defineQuery)((q) => q.with(Block, Selected));
3867
+ var selectedQuery2 = (0, import_core51.defineQuery)((q) => q.with(Block, Selected));
3708
3868
  function spawnAssignments(ctx, targetFrame) {
3709
3869
  const draggedEntity = getDraggedEntity(ctx);
3710
3870
  if (draggedEntity === null) return;
3711
3871
  const assignments = [];
3712
- if ((0, import_core48.hasComponent)(ctx, draggedEntity, TransformBox)) {
3872
+ if ((0, import_core51.hasComponent)(ctx, draggedEntity, TransformBox)) {
3713
3873
  for (const blockId of selectedQuery2.current(ctx)) {
3714
3874
  if (targetFrame !== null && blockId === targetFrame) continue;
3715
3875
  if (targetFrame !== null && isAncestor(ctx, targetFrame, blockId)) continue;
3716
3876
  assignments.push({ entityId: blockId, frameId: targetFrame });
3717
3877
  }
3718
- } 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)) {
3719
3879
  if (targetFrame !== null && draggedEntity === targetFrame) return;
3720
3880
  if (targetFrame !== null && isAncestor(ctx, targetFrame, draggedEntity)) return;
3721
3881
  assignments.push({ entityId: draggedEntity, frameId: targetFrame });
@@ -3814,9 +3974,9 @@ var captureFrameContainmentSystem = defineEditorSystem({ phase: "capture", prior
3814
3974
  });
3815
3975
 
3816
3976
  // src/systems/capture/hoverCursorSystem.ts
3817
- var import_core49 = require("@woven-ecs/core");
3818
- var hoveredHandleQuery = (0, import_core49.defineQuery)((q) => q.with(Hovered, TransformHandle).tracking(Hovered));
3819
- 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));
3820
3980
  var hoverCursorSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
3821
3981
  const addedHandles = hoveredHandleQuery.added(ctx);
3822
3982
  const removedHandles = hoveredHandleQuery.removed(ctx);
@@ -3840,17 +4000,18 @@ var hoverCursorSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
3840
4000
  });
3841
4001
 
3842
4002
  // src/systems/capture/keybindSystem.ts
3843
- var import_core50 = require("@woven-ecs/core");
4003
+ var import_core53 = require("@woven-ecs/core");
3844
4004
  var keybindSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
4005
+ if (isReadonly(ctx)) return;
3845
4006
  const keyboard = Keyboard.read(ctx);
3846
- const { editor } = (0, import_core50.getResources)(ctx);
4007
+ const { editor } = (0, import_core53.getResources)(ctx);
3847
4008
  for (const keybind of editor.keybinds) {
3848
4009
  let triggered = Keyboard.isKeyDownTrigger(ctx, keybind.key);
3849
4010
  triggered &&= !!keybind.mod === keyboard.modDown;
3850
4011
  triggered &&= !!keybind.shift === keyboard.shiftDown;
3851
4012
  if (triggered) {
3852
- const eid = (0, import_core50.createEntity)(ctx);
3853
- (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 });
3854
4015
  break;
3855
4016
  }
3856
4017
  }
@@ -3974,9 +4135,9 @@ var scrollEdgesSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
3974
4135
  });
3975
4136
 
3976
4137
  // src/systems/capture/transformBoxSystem.ts
3977
- var import_core51 = require("@woven-ecs/core");
4138
+ var import_core54 = require("@woven-ecs/core");
3978
4139
  var import_xstate4 = require("xstate");
3979
- 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));
3980
4141
  var transformBoxMachine = (0, import_xstate4.setup)({
3981
4142
  types: {
3982
4143
  context: {},
@@ -3989,12 +4150,12 @@ var transformBoxMachine = (0, import_xstate4.setup)({
3989
4150
  isOverTransformBox: ({ event }) => {
3990
4151
  if (!("intersects" in event)) return false;
3991
4152
  if (event.intersects.length === 0) return false;
3992
- return (0, import_core51.hasComponent)(event.ctx, event.intersects[0], TransformBox);
4153
+ return (0, import_core54.hasComponent)(event.ctx, event.intersects[0], TransformBox);
3993
4154
  },
3994
4155
  isOverTransformHandle: ({ event }) => {
3995
4156
  if (!("intersects" in event)) return false;
3996
4157
  if (event.intersects.length === 0) return false;
3997
- return (0, import_core51.hasComponent)(event.ctx, event.intersects[0], TransformHandle);
4158
+ return (0, import_core54.hasComponent)(event.ctx, event.intersects[0], TransformHandle);
3998
4159
  },
3999
4160
  selectionIsTransformable: ({ event }) => {
4000
4161
  if (event.type !== "selectionChanged") return false;
@@ -4006,7 +4167,7 @@ var transformBoxMachine = (0, import_xstate4.setup)({
4006
4167
  hasEditAfterPlacing: ({ event }) => {
4007
4168
  if (event.type !== "selectionChanged") return false;
4008
4169
  if (event.selectedEntityIds.length !== 1) return false;
4009
- return (0, import_core51.hasComponent)(event.ctx, event.selectedEntityIds[0], EditAfterPlacing);
4170
+ return (0, import_core54.hasComponent)(event.ctx, event.selectedEntityIds[0], EditAfterPlacing);
4010
4171
  }
4011
4172
  },
4012
4173
  actions: {
@@ -4016,7 +4177,7 @@ var transformBoxMachine = (0, import_xstate4.setup)({
4016
4177
  transformBoxId: context.transformBoxId
4017
4178
  });
4018
4179
  }
4019
- const transformBoxId = (0, import_core51.createEntity)(event.ctx);
4180
+ const transformBoxId = (0, import_core54.createEntity)(event.ctx);
4020
4181
  AddTransformBox.spawn(event.ctx, {
4021
4182
  transformBoxId,
4022
4183
  skipHandles: params?.skipHandles
@@ -4059,8 +4220,8 @@ var transformBoxMachine = (0, import_xstate4.setup)({
4059
4220
  removeEditAfterPlacing: ({ event }) => {
4060
4221
  if (event.type !== "selectionChanged") return;
4061
4222
  for (const entityId of event.selectedEntityIds) {
4062
- if ((0, import_core51.hasComponent)(event.ctx, entityId, EditAfterPlacing)) {
4063
- (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);
4064
4225
  }
4065
4226
  }
4066
4227
  }
@@ -4159,7 +4320,7 @@ var transformBoxSystem = defineEditorSystem({ phase: "capture" }, (ctx) => {
4159
4320
  });
4160
4321
 
4161
4322
  // src/systems/input/keyboardSystem.ts
4162
- var import_core52 = require("@woven-ecs/core");
4323
+ var import_core55 = require("@woven-ecs/core");
4163
4324
  var instanceState = /* @__PURE__ */ new WeakMap();
4164
4325
  function attachKeyboardListeners(domElement) {
4165
4326
  if (instanceState.has(domElement)) return;
@@ -4195,7 +4356,7 @@ function detachKeyboardListeners(domElement) {
4195
4356
  instanceState.delete(domElement);
4196
4357
  }
4197
4358
  var keyboardSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4198
- const resources = (0, import_core52.getResources)(ctx);
4359
+ const resources = (0, import_core55.getResources)(ctx);
4199
4360
  if (!resources.domElement) return;
4200
4361
  const state = instanceState.get(resources.domElement);
4201
4362
  if (!state) return;
@@ -4249,7 +4410,7 @@ function isZeroed(buffer) {
4249
4410
  }
4250
4411
 
4251
4412
  // src/systems/input/mouseSystem.ts
4252
- var import_core53 = require("@woven-ecs/core");
4413
+ var import_core56 = require("@woven-ecs/core");
4253
4414
  var instanceState2 = /* @__PURE__ */ new WeakMap();
4254
4415
  function attachMouseListeners(domElement) {
4255
4416
  if (instanceState2.has(domElement)) return;
@@ -4297,7 +4458,7 @@ function detachMouseListeners(domElement) {
4297
4458
  instanceState2.delete(domElement);
4298
4459
  }
4299
4460
  var mouseSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4300
- const resources = (0, import_core53.getResources)(ctx);
4461
+ const resources = (0, import_core56.getResources)(ctx);
4301
4462
  if (!resources.domElement) return;
4302
4463
  const state = instanceState2.get(resources.domElement);
4303
4464
  if (!state) return;
@@ -4354,7 +4515,7 @@ function normalizeWheelDelta(deltaY, deltaMode) {
4354
4515
  }
4355
4516
 
4356
4517
  // src/systems/input/pointerSystem.ts
4357
- var import_core54 = require("@woven-ecs/core");
4518
+ var import_core57 = require("@woven-ecs/core");
4358
4519
  var instanceState3 = /* @__PURE__ */ new WeakMap();
4359
4520
  function attachPointerListeners(domElement) {
4360
4521
  if (instanceState3.has(domElement)) return;
@@ -4449,7 +4610,7 @@ function detachPointerListeners(domElement) {
4449
4610
  instanceState3.delete(domElement);
4450
4611
  }
4451
4612
  var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4452
- const resources = (0, import_core54.getResources)(ctx);
4613
+ const resources = (0, import_core57.getResources)(ctx);
4453
4614
  const { domElement } = resources;
4454
4615
  if (!domElement) return;
4455
4616
  const state = instanceState3.get(domElement);
@@ -4461,8 +4622,8 @@ var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4461
4622
  switch (event.type) {
4462
4623
  case "pointerdown": {
4463
4624
  const position = [event.clientX - screen.left, event.clientY - screen.top];
4464
- const entityId = (0, import_core54.createEntity)(ctx);
4465
- (0, import_core54.addComponent)(ctx, entityId, Pointer, {
4625
+ const entityId = (0, import_core57.createEntity)(ctx);
4626
+ (0, import_core57.addComponent)(ctx, entityId, Pointer, {
4466
4627
  pointerId: event.pointerId,
4467
4628
  position,
4468
4629
  downPosition: position,
@@ -4494,7 +4655,7 @@ var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4494
4655
  const position = [event.clientX - screen.left, event.clientY - screen.top];
4495
4656
  const pointer = Pointer.write(ctx, entityId);
4496
4657
  addPointerSample(pointer, position, time);
4497
- (0, import_core54.removeEntity)(ctx, entityId);
4658
+ (0, import_core57.removeEntity)(ctx, entityId);
4498
4659
  state.pointerEntityMap.delete(event.pointerId);
4499
4660
  break;
4500
4661
  }
@@ -4504,7 +4665,7 @@ var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4504
4665
  });
4505
4666
 
4506
4667
  // src/systems/input/screenSystem.ts
4507
- var import_core55 = require("@woven-ecs/core");
4668
+ var import_core58 = require("@woven-ecs/core");
4508
4669
  var instanceState4 = /* @__PURE__ */ new WeakMap();
4509
4670
  function attachScreenObserver(domElement) {
4510
4671
  if (instanceState4.has(domElement)) return;
@@ -4529,7 +4690,7 @@ function detachScreenObserver(domElement) {
4529
4690
  instanceState4.delete(domElement);
4530
4691
  }
4531
4692
  var screenSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
4532
- const resources = (0, import_core55.getResources)(ctx);
4693
+ const resources = (0, import_core58.getResources)(ctx);
4533
4694
  const { domElement } = resources;
4534
4695
  if (!domElement) return;
4535
4696
  const state = instanceState4.get(domElement);
@@ -4564,8 +4725,8 @@ var tickSystem = defineEditorSystem({ phase: "input", priority: 100 }, (ctx) =>
4564
4725
  });
4565
4726
 
4566
4727
  // src/systems/postRender/cursorSystem.ts
4567
- var import_core56 = require("@woven-ecs/core");
4568
- 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));
4569
4730
  function getCursorSvg(cursors, kind, rotateZ) {
4570
4731
  const def = cursors[kind];
4571
4732
  if (!def) return "auto";
@@ -4584,16 +4745,17 @@ var cursorSystem = defineEditorSystem({ phase: "render", priority: -100 }, (ctx)
4584
4745
  document.body.style.cursor = "default";
4585
4746
  return;
4586
4747
  }
4587
- const { editor } = (0, import_core56.getResources)(ctx);
4748
+ const { editor } = (0, import_core59.getResources)(ctx);
4588
4749
  const cursorValue = getCursorSvg(editor.cursors, cursorKind, rotation);
4589
4750
  document.body.style.cursor = cursorValue;
4590
4751
  });
4591
4752
 
4592
4753
  // src/systems/postRender/presenceSystem.ts
4593
- var import_core57 = require("@woven-ecs/core");
4594
- var pointerQuery2 = (0, import_core57.defineQuery)((q) => q.tracking(Pointer));
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));
4595
4757
  var presenceSystem = defineEditorSystem({ phase: "render", priority: -100 }, (ctx) => {
4596
- if (Mouse.didMove(ctx)) {
4758
+ if (Mouse.didMove(ctx) || cameraChangedQuery.changed(ctx).length > 0) {
4597
4759
  const mouse = Mouse.read(ctx);
4598
4760
  updateUserPosition(ctx, mouse.position);
4599
4761
  return;
@@ -4622,9 +4784,9 @@ function updateUserPosition(ctx, position) {
4622
4784
 
4623
4785
  // src/systems/postUpdate/transformBoxSystem.ts
4624
4786
  var import_math8 = require("@woven-canvas/math");
4625
- var import_core58 = require("@woven-ecs/core");
4626
- var selectedBlocksQuery2 = (0, import_core58.defineQuery)((q) => q.with(Block, Selected));
4627
- 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));
4628
4790
  var TRANSFORM_BOX_RANK = "a";
4629
4791
  var TRANSFORM_HANDLE_ROTATE_RANK = "b";
4630
4792
  var TRANSFORM_HANDLE_EDGE_RANK = "c";
@@ -4651,27 +4813,27 @@ var transformBoxSystem2 = defineEditorSystem({ phase: "update", priority: -100 }
4651
4813
  on(ctx, EndTransformBoxEdit, endTransformBoxEdit);
4652
4814
  });
4653
4815
  function removeTransformBox(ctx, transformBoxId) {
4654
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) {
4816
+ if (!(0, import_core61.hasComponent)(ctx, transformBoxId, TransformBox)) {
4655
4817
  endTransformBoxEdit(ctx);
4656
4818
  return;
4657
4819
  }
4658
- const handles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4820
+ const handles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4659
4821
  for (const handleId of handles) {
4660
- (0, import_core58.removeEntity)(ctx, handleId);
4822
+ (0, import_core61.removeEntity)(ctx, handleId);
4661
4823
  }
4662
- (0, import_core58.removeEntity)(ctx, transformBoxId);
4824
+ (0, import_core61.removeEntity)(ctx, transformBoxId);
4663
4825
  endTransformBoxEdit(ctx);
4664
4826
  }
4665
4827
  function addTransformBox(ctx, transformBoxId, skipHandles) {
4666
- (0, import_core58.addComponent)(ctx, transformBoxId, Block, {
4828
+ (0, import_core61.addComponent)(ctx, transformBoxId, Block, {
4667
4829
  tag: "transform-box",
4668
4830
  position: [0, 0],
4669
4831
  size: [0, 0],
4670
4832
  rotateZ: 0,
4671
4833
  rank: TRANSFORM_BOX_RANK
4672
4834
  });
4673
- (0, import_core58.addComponent)(ctx, transformBoxId, TransformBox, {});
4674
- (0, import_core58.addComponent)(ctx, transformBoxId, DragStart, {
4835
+ (0, import_core61.addComponent)(ctx, transformBoxId, TransformBox, {});
4836
+ (0, import_core61.addComponent)(ctx, transformBoxId, DragStart, {
4675
4837
  position: [0, 0],
4676
4838
  size: [0, 0],
4677
4839
  rotateZ: 0,
@@ -4682,7 +4844,7 @@ function addTransformBox(ctx, transformBoxId, skipHandles) {
4682
4844
  }
4683
4845
  }
4684
4846
  function updateTransformBox(ctx, transformBoxId) {
4685
- if (!(0, import_core58.isAlive)(ctx, transformBoxId)) return;
4847
+ if (!(0, import_core61.isAlive)(ctx, transformBoxId)) return;
4686
4848
  const selectedBlocks = selectedBlocksQuery2.current(ctx);
4687
4849
  if (selectedBlocks.length === 0) return;
4688
4850
  let rotateZ = 0;
@@ -4708,7 +4870,7 @@ function updateTransformBox(ctx, transformBoxId) {
4708
4870
  const selectionState = SelectionStateSingleton.read(ctx);
4709
4871
  const isDragging = selectionState.state === SelectionState.Dragging;
4710
4872
  if (!isDragging) {
4711
- if ((0, import_core58.hasComponent)(ctx, transformBoxId, DragStart)) {
4873
+ if ((0, import_core61.hasComponent)(ctx, transformBoxId, DragStart)) {
4712
4874
  const dragStart = DragStart.write(ctx, transformBoxId);
4713
4875
  dragStart.position = boxBlock.position;
4714
4876
  dragStart.size = boxBlock.size;
@@ -4717,9 +4879,9 @@ function updateTransformBox(ctx, transformBoxId) {
4717
4879
  for (const entityId of selectedBlocks) {
4718
4880
  const block = Block.read(ctx, entityId);
4719
4881
  const worldPos = Block.getWorldPosition(ctx, entityId);
4720
- const fontSize = (0, import_core58.hasComponent)(ctx, entityId, Text) ? Text.read(ctx, entityId).fontSizePx : 16;
4721
- if (!(0, import_core58.hasComponent)(ctx, entityId, DragStart)) {
4722
- (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, {
4723
4885
  position: worldPos,
4724
4886
  size: block.size,
4725
4887
  rotateZ: block.rotateZ,
@@ -4883,10 +5045,10 @@ function addOrUpdateTransformHandles(ctx, transformBoxId) {
4883
5045
  });
4884
5046
  }
4885
5047
  }
4886
- const existingHandles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5048
+ const existingHandles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4887
5049
  const handleMap = /* @__PURE__ */ new Map();
4888
5050
  for (const handleId of existingHandles) {
4889
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5051
+ if (!(0, import_core61.isAlive)(ctx, handleId)) continue;
4890
5052
  const handle = TransformHandle.read(ctx, handleId);
4891
5053
  const key = `${handle.kind}-${handle.vectorX}-${handle.vectorY}`;
4892
5054
  handleMap.set(key, handleId);
@@ -4901,11 +5063,11 @@ function addOrUpdateTransformHandles(ctx, transformBoxId) {
4901
5063
  const finalLeft = rotatedCenter[0] - def.width / 2;
4902
5064
  const finalTop = rotatedCenter[1] - def.height / 2;
4903
5065
  if (!handleId) {
4904
- handleId = (0, import_core58.createEntity)(ctx);
4905
- (0, import_core58.addComponent)(ctx, handleId, Block);
4906
- (0, import_core58.addComponent)(ctx, handleId, TransformHandle);
4907
- (0, import_core58.addComponent)(ctx, handleId, DragStart);
4908
- (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);
4909
5071
  }
4910
5072
  const block = Block.write(ctx, handleId);
4911
5073
  block.tag = def.tag;
@@ -4932,7 +5094,7 @@ function addOrUpdateTransformHandles(ctx, transformBoxId) {
4932
5094
  }
4933
5095
  for (const handleId of existingHandles) {
4934
5096
  if (!usedHandleIds.has(handleId)) {
4935
- (0, import_core58.removeEntity)(ctx, handleId);
5097
+ (0, import_core61.removeEntity)(ctx, handleId);
4936
5098
  }
4937
5099
  }
4938
5100
  }
@@ -4968,10 +5130,10 @@ function addTapeHandles(ctx, transformBoxId, left, top, width, height, rotateZ,
4968
5130
  scaleMultiplier: [1, 1],
4969
5131
  anchor: [0.5, 0.5]
4970
5132
  });
4971
- const existingHandles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5133
+ const existingHandles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
4972
5134
  const handleMap = /* @__PURE__ */ new Map();
4973
5135
  for (const handleId of existingHandles) {
4974
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5136
+ if (!(0, import_core61.isAlive)(ctx, handleId)) continue;
4975
5137
  const handle = TransformHandle.read(ctx, handleId);
4976
5138
  const key = `${handle.kind}-${handle.vectorX}-${handle.vectorY}`;
4977
5139
  handleMap.set(key, handleId);
@@ -4986,11 +5148,11 @@ function addTapeHandles(ctx, transformBoxId, left, top, width, height, rotateZ,
4986
5148
  const finalLeft = rotatedCenter[0] - def.width / 2;
4987
5149
  const finalTop = rotatedCenter[1] - def.height / 2;
4988
5150
  if (!handleId) {
4989
- handleId = (0, import_core58.createEntity)(ctx);
4990
- (0, import_core58.addComponent)(ctx, handleId, Block);
4991
- (0, import_core58.addComponent)(ctx, handleId, TransformHandle);
4992
- (0, import_core58.addComponent)(ctx, handleId, DragStart);
4993
- (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);
4994
5156
  }
4995
5157
  const block = Block.write(ctx, handleId);
4996
5158
  block.tag = def.tag;
@@ -5017,23 +5179,23 @@ function addTapeHandles(ctx, transformBoxId, left, top, width, height, rotateZ,
5017
5179
  }
5018
5180
  for (const handleId of existingHandles) {
5019
5181
  if (!usedHandleIds.has(handleId)) {
5020
- (0, import_core58.removeEntity)(ctx, handleId);
5182
+ (0, import_core61.removeEntity)(ctx, handleId);
5021
5183
  }
5022
5184
  }
5023
5185
  }
5024
5186
  function hideTransformBox(ctx, transformBoxId) {
5025
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) return;
5026
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, Opacity)) {
5027
- (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 });
5028
5190
  } else {
5029
5191
  const opacity = Opacity.write(ctx, transformBoxId);
5030
5192
  opacity.value = 0;
5031
5193
  }
5032
- const handles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5194
+ const handles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5033
5195
  for (const handleId of handles) {
5034
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5035
- if (!(0, import_core58.hasComponent)(ctx, handleId, Opacity)) {
5036
- (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 });
5037
5199
  } else {
5038
5200
  const opacity = Opacity.write(ctx, handleId);
5039
5201
  opacity.value = 0;
@@ -5041,62 +5203,66 @@ function hideTransformBox(ctx, transformBoxId) {
5041
5203
  }
5042
5204
  }
5043
5205
  function showTransformBox(ctx, transformBoxId) {
5044
- if (!(0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) return;
5206
+ if (!(0, import_core61.hasComponent)(ctx, transformBoxId, TransformBox)) return;
5045
5207
  const selectedBlocks = selectedBlocksQuery2.current(ctx);
5046
5208
  if (selectedBlocks.length === 0) return;
5047
5209
  if (selectedBlocks.length === 1) {
5048
5210
  if (getBlockResizeMode(ctx, selectedBlocks[0]) === ResizeMode.GroupOnly) return;
5049
5211
  }
5050
- if ((0, import_core58.hasComponent)(ctx, transformBoxId, Opacity)) {
5051
- (0, import_core58.removeComponent)(ctx, transformBoxId, Opacity);
5212
+ if ((0, import_core61.hasComponent)(ctx, transformBoxId, Opacity)) {
5213
+ (0, import_core61.removeComponent)(ctx, transformBoxId, Opacity);
5052
5214
  }
5053
- const handles = (0, import_core58.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5215
+ const handles = (0, import_core61.getBackrefs)(ctx, transformBoxId, TransformHandle, "transformBox");
5054
5216
  for (const handleId of handles) {
5055
- if (!(0, import_core58.isAlive)(ctx, handleId)) continue;
5056
- if ((0, import_core58.hasComponent)(ctx, handleId, Opacity)) {
5057
- (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);
5058
5220
  }
5059
5221
  }
5060
5222
  updateTransformBox(ctx, transformBoxId);
5061
5223
  }
5062
5224
  function startTransformBoxEdit(ctx, transformBoxId) {
5063
- if ((0, import_core58.hasComponent)(ctx, transformBoxId, TransformBox)) {
5064
- 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");
5065
5227
  for (const handleId of handles) {
5066
- (0, import_core58.removeEntity)(ctx, handleId);
5228
+ (0, import_core61.removeEntity)(ctx, handleId);
5067
5229
  }
5068
5230
  }
5069
5231
  for (const blockId of selectedBlocksQuery2.current(ctx)) {
5070
- if (!(0, import_core58.hasComponent)(ctx, blockId, Edited)) {
5071
- (0, import_core58.addComponent)(ctx, blockId, Edited, {});
5232
+ if (!(0, import_core61.hasComponent)(ctx, blockId, Edited)) {
5233
+ (0, import_core61.addComponent)(ctx, blockId, Edited, {});
5072
5234
  }
5073
5235
  }
5074
5236
  }
5075
5237
  function endTransformBoxEdit(ctx) {
5076
5238
  for (const blockId of editedBlocksQuery.current(ctx)) {
5077
- if ((0, import_core58.hasComponent)(ctx, blockId, Edited)) {
5078
- (0, import_core58.removeComponent)(ctx, blockId, Edited);
5239
+ if ((0, import_core61.hasComponent)(ctx, blockId, Edited)) {
5240
+ (0, import_core61.removeComponent)(ctx, blockId, Edited);
5079
5241
  }
5080
5242
  }
5081
5243
  }
5082
5244
 
5083
5245
  // src/systems/preCapture/intersectSystem.ts
5084
- var import_core59 = require("@woven-ecs/core");
5085
- var blocksChanged = (0, import_core59.defineQuery)((q) => q.tracking(Block));
5086
- var hitGeometryChanged = (0, import_core59.defineQuery)((q) => q.tracking(HitGeometry));
5087
- var heldQuery = (0, import_core59.defineQuery)((q) => q.with(Held).tracking(Held));
5088
- var hoveredQuery = (0, import_core59.defineQuery)((q) => q.with(Hovered));
5089
- 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));
5090
5252
  function clearHovered(ctx) {
5091
5253
  for (const entityId of hoveredQuery.current(ctx)) {
5092
- if ((0, import_core59.hasComponent)(ctx, entityId, Hovered)) {
5093
- (0, import_core59.removeComponent)(ctx, entityId, Hovered);
5254
+ if ((0, import_core62.hasComponent)(ctx, entityId, Hovered)) {
5255
+ (0, import_core62.removeComponent)(ctx, entityId, Hovered);
5094
5256
  }
5095
5257
  }
5096
5258
  }
5097
5259
  function findValidHoverTarget(ctx, intersected) {
5098
5260
  for (const entityId of intersected) {
5099
5261
  if (isHeldByRemote(ctx, entityId)) return void 0;
5262
+ if ((0, import_core62.hasComponent)(ctx, entityId, Block)) {
5263
+ const block = Block.read(ctx, entityId);
5264
+ if (!canBlockInteract(ctx, block.tag)) continue;
5265
+ }
5100
5266
  return entityId;
5101
5267
  }
5102
5268
  return void 0;
@@ -5108,7 +5274,7 @@ function updateHovered(ctx, intersected) {
5108
5274
  if (!selectToolActive) return;
5109
5275
  const newHoveredId = findValidHoverTarget(ctx, intersected);
5110
5276
  if (newHoveredId !== void 0) {
5111
- (0, import_core59.addComponent)(ctx, newHoveredId, Hovered, {});
5277
+ (0, import_core62.addComponent)(ctx, newHoveredId, Hovered, {});
5112
5278
  }
5113
5279
  }
5114
5280
  function arraysEqual(a, b) {
@@ -5121,7 +5287,7 @@ function arraysEqual(a, b) {
5121
5287
  var added = /* @__PURE__ */ new Set();
5122
5288
  var changed = /* @__PURE__ */ new Set();
5123
5289
  function addDescendants(ctx, entityId, set) {
5124
- 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")) {
5125
5291
  if (set.has(childId)) continue;
5126
5292
  set.add(childId);
5127
5293
  addDescendants(ctx, childId, set);
@@ -5147,18 +5313,14 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
5147
5313
  addDescendants(ctx, entityId, changed);
5148
5314
  }
5149
5315
  for (const entityId of added) {
5150
- if ((0, import_core59.hasComponent)(ctx, entityId, Block)) {
5151
- const block = Block.read(ctx, entityId);
5152
- if (!canBlockInteract(ctx, block.tag)) continue;
5153
- }
5154
- if (!(0, import_core59.hasComponent)(ctx, entityId, Aabb)) {
5155
- (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] });
5156
5318
  }
5157
5319
  const aabb = Aabb.write(ctx, entityId);
5158
5320
  computeAabb(ctx, entityId, aabb.value);
5159
5321
  }
5160
5322
  for (const entityId of changed) {
5161
- if (!(0, import_core59.hasComponent)(ctx, entityId, Aabb)) continue;
5323
+ if (!(0, import_core62.hasComponent)(ctx, entityId, Aabb)) continue;
5162
5324
  const aabb = Aabb.write(ctx, entityId);
5163
5325
  computeAabb(ctx, entityId, aabb.value);
5164
5326
  }
@@ -5177,7 +5339,7 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
5177
5339
  clearHovered(ctx);
5178
5340
  return;
5179
5341
  }
5180
- const resources = (0, import_core59.getResources)(ctx);
5342
+ const resources = (0, import_core62.getResources)(ctx);
5181
5343
  const isObscured = Mouse.isObscured(ctx);
5182
5344
  const wasObscured = resources.domElement ? prevObscuredByElement.get(resources.domElement) ?? false : false;
5183
5345
  const obscuredChanged = isObscured !== wasObscured;
@@ -5207,12 +5369,21 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
5207
5369
 
5208
5370
  // src/systems/preCapture/selectSystem.ts
5209
5371
  var import_math9 = require("@woven-canvas/math");
5210
- var import_canvas_store41 = require("@woven-ecs/canvas-store");
5211
- 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");
5212
5374
  var import_xstate5 = require("xstate");
5213
5375
  var POINTING_THRESHOLD = 4;
5214
- var selectedBlocksQuery3 = (0, import_core60.defineQuery)((q) => q.with(Block, Selected, import_canvas_store41.Synced));
5215
- 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
+ }
5216
5387
  var selectionMachine = (0, import_xstate5.setup)({
5217
5388
  types: {
5218
5389
  context: {},
@@ -5228,13 +5399,13 @@ var selectionMachine = (0, import_xstate5.setup)({
5228
5399
  },
5229
5400
  hasDraggedEntity: ({ context, event }) => {
5230
5401
  if (context.draggedEntity === null) return false;
5231
- 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);
5232
5403
  },
5233
5404
  isOverSyncedBlock: ({ event }) => {
5234
5405
  const ctx = event.ctx;
5235
5406
  for (const entityId of event.intersects) {
5236
- if ((0, import_core60.hasComponent)(ctx, entityId, import_canvas_store41.Synced)) {
5237
- 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)) {
5238
5409
  const block = Block.read(ctx, entityId);
5239
5410
  if (!canBlockInteract(ctx, block.tag)) continue;
5240
5411
  }
@@ -5271,7 +5442,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5271
5442
  },
5272
5443
  setDragCursor: ({ event, context }) => {
5273
5444
  if (!context.draggedEntity) return;
5274
- if ((0, import_core60.hasComponent)(event.ctx, context.draggedEntity, TransformHandle)) {
5445
+ if ((0, import_core63.hasComponent)(event.ctx, context.draggedEntity, TransformHandle)) {
5275
5446
  const handle = TransformHandle.read(event.ctx, context.draggedEntity);
5276
5447
  const transformBoxBlock = handle.transformBox !== null ? Block.read(event.ctx, handle.transformBox) : void 0;
5277
5448
  SetCursor.spawn(event.ctx, {
@@ -5331,7 +5502,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5331
5502
  if (context.draggedEntity === null) return false;
5332
5503
  let left = context.draggedEntityStart[0] + event.worldPosition[0] - context.dragStart[0];
5333
5504
  let top = context.draggedEntityStart[1] + event.worldPosition[1] - context.dragStart[1];
5334
- 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);
5335
5506
  if (event.shiftDown && draggingSynced) {
5336
5507
  const dx = Math.abs(event.worldPosition[0] - context.dragStart[0]);
5337
5508
  const dy = Math.abs(event.worldPosition[1] - context.dragStart[1]);
@@ -5341,7 +5512,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5341
5512
  left = context.draggedEntityStart[0];
5342
5513
  }
5343
5514
  }
5344
- if (draggingSynced) {
5515
+ if (draggingSynced && isDragSnappable(ctx, context.draggedEntity)) {
5345
5516
  left = Grid.snapX(ctx, left);
5346
5517
  top = Grid.snapY(ctx, top);
5347
5518
  }
@@ -5354,7 +5525,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5354
5525
  const dx = context.draggedEntityStart[0] - left;
5355
5526
  const dy = context.draggedEntityStart[1] - top;
5356
5527
  isCloning = true;
5357
- const isTransformBox = (0, import_core60.hasComponent)(ctx, context.draggedEntity, TransformBox);
5528
+ const isTransformBox = (0, import_core63.hasComponent)(ctx, context.draggedEntity, TransformBox);
5358
5529
  const entityIds = isTransformBox ? Array.from(selectedBlocksQuery3.current(ctx)) : [context.draggedEntity];
5359
5530
  CloneEntities.spawn(ctx, {
5360
5531
  entityIds,
@@ -5362,7 +5533,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5362
5533
  seed: context.cloneGeneratorSeed
5363
5534
  });
5364
5535
  } else if (!event.altDown && context.isCloning) {
5365
- const isTransformBox = (0, import_core60.hasComponent)(ctx, context.draggedEntity, TransformBox);
5536
+ const isTransformBox = (0, import_core63.hasComponent)(ctx, context.draggedEntity, TransformBox);
5366
5537
  const entityIds = isTransformBox ? Array.from(selectedBlocksQuery3.current(ctx)) : [context.draggedEntity];
5367
5538
  UncloneEntities.spawn(ctx, {
5368
5539
  entityIds,
@@ -5387,8 +5558,8 @@ var selectionMachine = (0, import_xstate5.setup)({
5387
5558
  const ctx = event.ctx;
5388
5559
  let intersectId;
5389
5560
  for (const entityId of event.intersects) {
5390
- if ((0, import_core60.hasComponent)(ctx, entityId, import_canvas_store41.Synced)) {
5391
- 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)) {
5392
5563
  const block = Block.read(ctx, entityId);
5393
5564
  if (!canBlockInteract(ctx, block.tag)) continue;
5394
5565
  }
@@ -5408,7 +5579,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5408
5579
  },
5409
5580
  selectDragged: ({ context, event }) => {
5410
5581
  if (context.draggedEntity === null) return;
5411
- 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;
5412
5583
  SelectBlock.spawn(event.ctx, {
5413
5584
  entityId: context.draggedEntity,
5414
5585
  deselectOthers: true
@@ -5420,9 +5591,9 @@ var selectionMachine = (0, import_xstate5.setup)({
5420
5591
  },
5421
5592
  deselectAllIfDraggedNotSelected: ({ context, event }) => {
5422
5593
  if (context.draggedEntity === null) return;
5423
- if ((0, import_core60.hasComponent)(event.ctx, context.draggedEntity, Selected)) return;
5424
- if ((0, import_core60.hasComponent)(event.ctx, context.draggedEntity, TransformBox)) return;
5425
- 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;
5426
5597
  DeselectAll.spawn(event.ctx, void 0);
5427
5598
  }
5428
5599
  }
@@ -5534,6 +5705,7 @@ var selectionMachine = (0, import_xstate5.setup)({
5534
5705
  }
5535
5706
  });
5536
5707
  var selectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (ctx) => {
5708
+ if (isReadonly(ctx)) return;
5537
5709
  let buttons = Controls.getButtons(ctx, "select");
5538
5710
  const state = SelectionStateSingleton.read(ctx);
5539
5711
  if (state.state === SelectionState.Dragging && buttons.length === 0) {
@@ -5545,13 +5717,14 @@ var selectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (ctx)
5545
5717
  });
5546
5718
 
5547
5719
  // src/systems/preInput/rankBoundsSystem.ts
5548
- var import_canvas_store42 = require("@woven-ecs/canvas-store");
5549
- var import_core61 = require("@woven-ecs/core");
5550
- 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));
5551
5723
  var rankBoundsSystem = defineEditorSystem({ phase: "input", priority: 100 }, (ctx) => {
5552
5724
  const added2 = blocksQuery.added(ctx);
5553
5725
  for (const entityId of added2) {
5554
5726
  const block = Block.read(ctx, entityId);
5727
+ if (getBlockDef(ctx, block.tag).excludeFromRankBounds) continue;
5555
5728
  if (block.rank === "") {
5556
5729
  const writableBlock = Block.write(ctx, entityId);
5557
5730
  writableBlock.rank = RankBounds.genNext(ctx);
@@ -5563,11 +5736,11 @@ var rankBoundsSystem = defineEditorSystem({ phase: "input", priority: 100 }, (ct
5563
5736
 
5564
5737
  // src/systems/preRender/canSeeBlocksSystem.ts
5565
5738
  var import_math10 = require("@woven-canvas/math");
5566
- var import_canvas_store43 = require("@woven-ecs/canvas-store");
5567
- var import_core62 = require("@woven-ecs/core");
5568
- var camerasQuery = (0, import_core62.defineQuery)((q) => q.tracking(Camera));
5569
- var blocksQuery2 = (0, import_core62.defineQuery)((q) => q.with(import_canvas_store43.Synced, Block, Aabb));
5570
- 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));
5571
5744
  var _cameraAabb = [0, 0, 0, 0];
5572
5745
  var canSeeBlocksSystem = defineEditorSystem({ phase: "render", priority: 90 }, (ctx) => {
5573
5746
  if (camerasQuery.changed(ctx).length === 0 && syncedBlocksQuery.addedOrRemoved(ctx).length === 0) {
@@ -5576,7 +5749,7 @@ var canSeeBlocksSystem = defineEditorSystem({ phase: "render", priority: 90 }, (
5576
5749
  const camera = Camera.read(ctx);
5577
5750
  const cameraAabb = Camera.getAabb(ctx, _cameraAabb);
5578
5751
  if (camera.canSeeBlocks && camera.lastSeenBlock !== null) {
5579
- if ((0, import_core62.hasComponent)(ctx, camera.lastSeenBlock, Aabb)) {
5752
+ if ((0, import_core65.hasComponent)(ctx, camera.lastSeenBlock, Aabb)) {
5580
5753
  const aabb = Aabb.read(ctx, camera.lastSeenBlock);
5581
5754
  if (import_math10.Aabb.intersects(cameraAabb, aabb.value)) {
5582
5755
  return;
@@ -5605,8 +5778,8 @@ var canSeeBlocksSystem = defineEditorSystem({ phase: "render", priority: 90 }, (
5605
5778
 
5606
5779
  // src/systems/preRender/scaleWithZoomSystem.ts
5607
5780
  var import_math11 = require("@woven-canvas/math");
5608
- var import_core63 = require("@woven-ecs/core");
5609
- 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));
5610
5783
  var _scaledSize = [0, 0];
5611
5784
  var _anchorOffset = [0, 0];
5612
5785
  var _anchorAdj = [0, 0];
@@ -5650,13 +5823,13 @@ function scaleBlock(ctx, entityId, zoom) {
5650
5823
 
5651
5824
  // src/systems/update/blockSystem.ts
5652
5825
  var import_math12 = require("@woven-canvas/math");
5653
- var import_canvas_store44 = require("@woven-ecs/canvas-store");
5654
- var import_core64 = require("@woven-ecs/core");
5655
- var selectedBlocksQuery4 = (0, import_core64.defineQuery)((q) => q.with(Block, Selected));
5656
- 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));
5657
5830
  var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5658
5831
  on(ctx, DragBlock, (ctx2, { entityId, position }) => {
5659
- if (!(0, import_core64.hasComponent)(ctx2, entityId, Block)) return;
5832
+ if (!(0, import_core67.hasComponent)(ctx2, entityId, Block)) return;
5660
5833
  Block.setWorldPosition(ctx2, entityId, position);
5661
5834
  });
5662
5835
  on(ctx, SelectBlock, (ctx2, { entityId, deselectOthers }) => {
@@ -5669,7 +5842,7 @@ var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5669
5842
  deselectBlock(ctx2, entityId);
5670
5843
  });
5671
5844
  on(ctx, ToggleSelect, (ctx2, { entityId }) => {
5672
- if ((0, import_core64.hasComponent)(ctx2, entityId, Selected)) {
5845
+ if ((0, import_core67.hasComponent)(ctx2, entityId, Selected)) {
5673
5846
  deselectBlock(ctx2, entityId);
5674
5847
  } else {
5675
5848
  selectBlock(ctx2, entityId);
@@ -5677,15 +5850,15 @@ var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5677
5850
  });
5678
5851
  on(ctx, DeselectAll, deselectAllBlocks);
5679
5852
  on(ctx, AddHeld, (ctx2, { entityId }) => {
5680
- if (!(0, import_core64.hasComponent)(ctx2, entityId, import_canvas_store44.Synced)) return;
5681
- if ((0, import_core64.hasComponent)(ctx2, entityId, Held)) return;
5682
- const { sessionId } = (0, import_core64.getResources)(ctx2);
5683
- (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 });
5684
5857
  });
5685
5858
  on(ctx, RemoveHeld, (ctx2, { entityId }) => {
5686
- if ((0, import_core64.hasComponent)(ctx2, entityId, Selected)) return;
5687
- if (!(0, import_core64.hasComponent)(ctx2, entityId, Held)) return;
5688
- (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);
5689
5862
  });
5690
5863
  on(ctx, SelectAll, (ctx2) => {
5691
5864
  for (const entityId of syncedBlocksQuery2.current(ctx2)) {
@@ -5697,7 +5870,7 @@ var blockSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5697
5870
  });
5698
5871
  on(ctx, RemoveSelected, (ctx2) => {
5699
5872
  for (const entityId of selectedBlocksQuery4.current(ctx2)) {
5700
- if ((0, import_core64.isAlive)(ctx2, entityId)) {
5873
+ if ((0, import_core67.isAlive)(ctx2, entityId)) {
5701
5874
  cascadeDelete(ctx2, entityId);
5702
5875
  }
5703
5876
  }
@@ -5772,12 +5945,12 @@ function cloneEntities(ctx, entityIds, offset, seed) {
5772
5945
  }
5773
5946
  }
5774
5947
  entityIds = [...allEntityIdsSet];
5775
- const { componentsById } = (0, import_core64.getResources)(ctx);
5948
+ const { componentsById } = (0, import_core67.getResources)(ctx);
5776
5949
  const documentComponents = new Map([...componentsById].filter(([, def]) => def.sync === "document"));
5777
- const syncedComponentId = import_canvas_store44.Synced._getComponentId(ctx);
5950
+ const syncedComponentId = import_canvas_store46.Synced._getComponentId(ctx);
5778
5951
  const blockComponentId = Block._getComponentId(ctx);
5779
5952
  const entityIdSet = new Set(entityIds);
5780
- 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) => {
5781
5954
  const rankA = Block.read(ctx, a).rank;
5782
5955
  const rankB = Block.read(ctx, b).rank;
5783
5956
  return rankA < rankB ? -1 : rankA > rankB ? 1 : 0;
@@ -5801,12 +5974,12 @@ function cloneEntities(ctx, entityIds, offset, seed) {
5801
5974
  }
5802
5975
  const originalToClone = /* @__PURE__ */ new Map();
5803
5976
  for (const entityId of entityIds) {
5804
- if (!(0, import_core64.hasComponent)(ctx, entityId, import_canvas_store44.Synced)) continue;
5805
- 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);
5806
5979
  const cloneId = generateUuidBySeed(synced.id, seed);
5807
- const cloneEntityId = (0, import_core64.createEntity)(ctx);
5980
+ const cloneEntityId = (0, import_core67.createEntity)(ctx);
5808
5981
  originalToClone.set(entityId, cloneEntityId);
5809
- (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 });
5810
5983
  for (const componentId of ctx.entityBuffer.getComponentIds(entityId)) {
5811
5984
  if (componentId === syncedComponentId) continue;
5812
5985
  const componentDef = documentComponents.get(componentId);
@@ -5822,7 +5995,7 @@ function cloneEntities(ctx, entityIds, offset, seed) {
5822
5995
  }
5823
5996
  data.rank = cloneRankMap.get(entityId) ?? RankBounds.genBetween(prevRank, lowestOriginalRank);
5824
5997
  }
5825
- (0, import_core64.addComponent)(ctx, cloneEntityId, componentDef, data);
5998
+ (0, import_core67.addComponent)(ctx, cloneEntityId, componentDef, data);
5826
5999
  }
5827
6000
  }
5828
6001
  swapConnectorRefs(ctx, originalToClone);
@@ -5838,23 +6011,23 @@ function uncloneEntities(ctx, entityIds, seed) {
5838
6011
  entityIds = [...allEntityIdsSet];
5839
6012
  const expectedCloneUuids = /* @__PURE__ */ new Set();
5840
6013
  for (const entityId of entityIds) {
5841
- if (!(0, import_core64.hasComponent)(ctx, entityId, import_canvas_store44.Synced)) continue;
5842
- 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);
5843
6016
  expectedCloneUuids.add(generateUuidBySeed(synced.id, seed));
5844
6017
  }
5845
6018
  const clonesToRemove = [];
5846
6019
  for (const entityId of syncedBlocksQuery2.current(ctx)) {
5847
- const synced = import_canvas_store44.Synced.read(ctx, entityId);
6020
+ const synced = import_canvas_store46.Synced.read(ctx, entityId);
5848
6021
  if (expectedCloneUuids.has(synced.id)) {
5849
6022
  clonesToRemove.push(entityId);
5850
6023
  }
5851
6024
  }
5852
6025
  const cloneToOriginal = /* @__PURE__ */ new Map();
5853
6026
  for (const cloneId of clonesToRemove) {
5854
- const cloneUuid = import_canvas_store44.Synced.read(ctx, cloneId).id;
6027
+ const cloneUuid = import_canvas_store46.Synced.read(ctx, cloneId).id;
5855
6028
  for (const originalId of entityIds) {
5856
- if (!(0, import_core64.hasComponent)(ctx, originalId, import_canvas_store44.Synced)) continue;
5857
- 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;
5858
6031
  if (generateUuidBySeed(originalUuid, seed) === cloneUuid) {
5859
6032
  cloneToOriginal.set(cloneId, originalId);
5860
6033
  break;
@@ -5863,25 +6036,25 @@ function uncloneEntities(ctx, entityIds, seed) {
5863
6036
  }
5864
6037
  swapConnectorRefs(ctx, cloneToOriginal);
5865
6038
  for (const cloneId of clonesToRemove) {
5866
- (0, import_core64.removeEntity)(ctx, cloneId);
6039
+ (0, import_core67.removeEntity)(ctx, cloneId);
5867
6040
  }
5868
6041
  }
5869
6042
  function swapConnectorRefs(ctx, fromTo) {
5870
6043
  for (const [fromId, toId] of fromTo) {
5871
- for (const connectorId of (0, import_core64.getBackrefs)(ctx, fromId, Connector, "startBlock")) {
5872
- 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;
5873
6046
  Connector.write(ctx, connectorId).startBlock = toId;
5874
6047
  }
5875
- for (const connectorId of (0, import_core64.getBackrefs)(ctx, fromId, Connector, "endBlock")) {
5876
- 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;
5877
6050
  Connector.write(ctx, connectorId).endBlock = toId;
5878
6051
  }
5879
6052
  }
5880
6053
  }
5881
6054
  function swapParentRefs(ctx, fromTo) {
5882
6055
  for (const [, cloneId] of fromTo) {
5883
- if (!(0, import_core64.isAlive)(ctx, cloneId)) continue;
5884
- 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;
5885
6058
  const block = Block.read(ctx, cloneId);
5886
6059
  if (block.parentId !== null && fromTo.has(block.parentId)) {
5887
6060
  Block.write(ctx, cloneId).parentId = fromTo.get(block.parentId);
@@ -5891,20 +6064,27 @@ function swapParentRefs(ctx, fromTo) {
5891
6064
 
5892
6065
  // src/systems/update/dragHandlerSystem.ts
5893
6066
  var import_math13 = require("@woven-canvas/math");
5894
- var import_core65 = require("@woven-ecs/core");
5895
- var selectedBlocksQuery5 = (0, import_core65.defineQuery)((q) => q.with(Block, Selected));
5896
- 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));
5897
6070
  function isParentSelected(ctx, blockId) {
5898
6071
  const block = Block.read(ctx, blockId);
5899
- 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;
5900
6080
  }
5901
6081
  var dragHandlerSystem = defineEditorSystem({ phase: "update" }, (ctx) => {
5902
6082
  on(ctx, DragBlock, (ctx2, { entityId, position }) => {
5903
- if ((0, import_core65.hasComponent)(ctx2, entityId, TransformBox)) {
6083
+ if ((0, import_core68.hasComponent)(ctx2, entityId, TransformBox)) {
5904
6084
  onTransformBoxDrag(ctx2, entityId, position);
5905
6085
  return;
5906
6086
  }
5907
- if ((0, import_core65.hasComponent)(ctx2, entityId, TransformHandle)) {
6087
+ if ((0, import_core68.hasComponent)(ctx2, entityId, TransformHandle)) {
5908
6088
  onTransformHandleDrag(ctx2, entityId, position);
5909
6089
  return;
5910
6090
  }
@@ -5915,11 +6095,13 @@ function onTransformBoxDrag(ctx, boxId, position) {
5915
6095
  const dx = position[0] - boxStart.position[0];
5916
6096
  const dy = position[1] - boxStart.position[1];
5917
6097
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
5918
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6098
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
5919
6099
  if (isParentSelected(ctx, blockId)) continue;
5920
6100
  const blockStart = DragStart.read(ctx, blockId);
5921
6101
  const worldPos = [blockStart.position[0] + dx, blockStart.position[1] + dy];
5922
- Grid.snapPosition(ctx, worldPos);
6102
+ if (canBlockSnap(ctx, Block.read(ctx, blockId).tag)) {
6103
+ Grid.snapPosition(ctx, worldPos);
6104
+ }
5923
6105
  Block.setWorldPosition(ctx, blockId, worldPos);
5924
6106
  }
5925
6107
  }
@@ -5953,10 +6135,11 @@ function onRotateHandleDrag(ctx, handleId, position) {
5953
6135
  let delta = angleHandle - handleStartAngle;
5954
6136
  const grid = Grid.read(ctx);
5955
6137
  const keyboard = Keyboard.read(ctx);
6138
+ const selectionSnappable = isSelectionSnappable(ctx);
5956
6139
  let snapAngle = 0;
5957
6140
  if (keyboard.shiftDown) {
5958
6141
  snapAngle = grid.shiftSnapAngleRad;
5959
- } else if (grid.enabled) {
6142
+ } else if (grid.enabled && selectionSnappable) {
5960
6143
  snapAngle = grid.snapAngleRad;
5961
6144
  }
5962
6145
  if (snapAngle > 0) {
@@ -5965,7 +6148,7 @@ function onRotateHandleDrag(ctx, handleId, position) {
5965
6148
  }
5966
6149
  boxBlock.rotateZ = import_math13.Scalar.normalizeAngle(dragStart.rotateZ + delta);
5967
6150
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
5968
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6151
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
5969
6152
  if (isParentSelected(ctx, blockId)) continue;
5970
6153
  const blockStart = DragStart.read(ctx, blockId);
5971
6154
  const block = Block.write(ctx, blockId);
@@ -5978,7 +6161,7 @@ function onRotateHandleDrag(ctx, handleId, position) {
5978
6161
  const angle = import_math13.Vec2.angleTo(boxCenter, startCenter) + delta;
5979
6162
  const newCenter = import_math13.Vec2.fromPolar(r, angle, boxCenter);
5980
6163
  const worldPos = [newCenter[0] - block.size[0] / 2, newCenter[1] - block.size[1] / 2];
5981
- if (grid.strict) {
6164
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
5982
6165
  Grid.snapPosition(ctx, worldPos);
5983
6166
  }
5984
6167
  Block.setWorldPosition(ctx, blockId, worldPos);
@@ -5995,7 +6178,10 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
5995
6178
  const handle = TransformHandle.read(ctx, handleId);
5996
6179
  const handleBlock = Block.read(ctx, handleId);
5997
6180
  const handleCenter = [position[0] + handleBlock.size[0] / 2, position[1] + handleBlock.size[1] / 2];
5998
- Grid.snapPosition(ctx, handleCenter);
6181
+ const selectionSnappable = isSelectionSnappable(ctx);
6182
+ if (selectionSnappable) {
6183
+ Grid.snapPosition(ctx, handleCenter);
6184
+ }
5999
6185
  const boxRotateZ = boxStart.rotateZ;
6000
6186
  const boxStartCenter = [
6001
6187
  boxStart.position[0] + boxStart.size[0] / 2,
@@ -6046,7 +6232,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6046
6232
  import_math13.Vec2.add(newBoxCenter, vec);
6047
6233
  const scaleFactor = [scaleX, scaleY];
6048
6234
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
6049
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6235
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
6050
6236
  if (isParentSelected(ctx, blockId)) continue;
6051
6237
  const blockStart = DragStart.read(ctx, blockId);
6052
6238
  const block = Block.write(ctx, blockId);
@@ -6064,7 +6250,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6064
6250
  const newBlockSize = import_math13.Vec2.clone(blockStart.size);
6065
6251
  import_math13.Vec2.multiply(newBlockSize, scaleFactor);
6066
6252
  const isTextResize = getBlockResizeMode(ctx, blockId) === ResizeMode.Text;
6067
- if (grid.strict) {
6253
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
6068
6254
  if (isTextResize && maintainAspectRatio) {
6069
6255
  newBlockSize[1] = Math.max(grid.rowHeight, Grid.snapY(ctx, newBlockSize[1]));
6070
6256
  const aspectRatio = blockStart.size[0] / blockStart.size[1];
@@ -6075,7 +6261,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6075
6261
  }
6076
6262
  const worldPos = import_math13.Vec2.clone(newBlockCenter);
6077
6263
  import_math13.Vec2.sub(worldPos, [newBlockSize[0] / 2, newBlockSize[1] / 2]);
6078
- if (grid.strict) {
6264
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
6079
6265
  Grid.snapPosition(ctx, worldPos);
6080
6266
  }
6081
6267
  Block.setWorldPosition(ctx, blockId, worldPos);
@@ -6086,7 +6272,7 @@ function onScaleHandleDrag(ctx, handleId, position, maintainAspectRatio) {
6086
6272
  } else {
6087
6273
  import_math13.Vec2.copy(block.size, newBlockSize);
6088
6274
  }
6089
- if ((0, import_core65.hasComponent)(ctx, blockId, Text)) {
6275
+ if ((0, import_core68.hasComponent)(ctx, blockId, Text)) {
6090
6276
  const didStretch = handle.kind === TransformHandleKind.Stretch;
6091
6277
  const text = Text.write(ctx, blockId);
6092
6278
  if (didStretch) {
@@ -6124,10 +6310,11 @@ function onRotateScaleHandleDrag(ctx, handleId, position) {
6124
6310
  }
6125
6311
  const grid = Grid.read(ctx);
6126
6312
  const keyboard = Keyboard.read(ctx);
6313
+ const selectionSnappable = isSelectionSnappable(ctx);
6127
6314
  let snapAngle = 0;
6128
6315
  if (keyboard.shiftDown) {
6129
6316
  snapAngle = grid.shiftSnapAngleRad;
6130
- } else if (grid.enabled) {
6317
+ } else if (grid.enabled && selectionSnappable) {
6131
6318
  snapAngle = grid.snapAngleRad;
6132
6319
  }
6133
6320
  if (snapAngle > 0) {
@@ -6147,13 +6334,13 @@ function onRotateScaleHandleDrag(ctx, handleId, position) {
6147
6334
  boxBlock.size = [finalLength, startHeight];
6148
6335
  boxBlock.position = [newCenter[0] - finalLength / 2, newCenter[1] - startHeight / 2];
6149
6336
  for (const blockId of selectedBlocksQuery5.current(ctx)) {
6150
- if (!(0, import_core65.hasComponent)(ctx, blockId, DragStart)) continue;
6337
+ if (!(0, import_core68.hasComponent)(ctx, blockId, DragStart)) continue;
6151
6338
  if (isParentSelected(ctx, blockId)) continue;
6152
6339
  const block = Block.write(ctx, blockId);
6153
6340
  block.rotateZ = newRotateZ;
6154
6341
  block.size = [finalLength, startHeight];
6155
6342
  const blockWorldPos = [newCenter[0] - finalLength / 2, newCenter[1] - startHeight / 2];
6156
- if (grid.strict) {
6343
+ if (grid.strict && canBlockSnap(ctx, block.tag)) {
6157
6344
  Grid.snapPosition(ctx, blockWorldPos);
6158
6345
  }
6159
6346
  Block.setWorldPosition(ctx, blockId, blockWorldPos);
@@ -6161,23 +6348,23 @@ function onRotateScaleHandleDrag(ctx, handleId, position) {
6161
6348
  }
6162
6349
 
6163
6350
  // src/systems/update/selectSystem.ts
6164
- var import_canvas_store45 = require("@woven-ecs/canvas-store");
6165
- var import_core66 = require("@woven-ecs/core");
6166
- var selectedBlocksQuery6 = (0, import_core66.defineQuery)((q) => q.with(Block, Selected));
6167
- var selectionBoxQuery = (0, import_core66.defineQuery)((q) => q.with(Block, SelectionBox));
6168
- 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));
6169
6356
  var SELECTION_BOX_RANK = "z";
6170
6357
  var selectSystem2 = defineEditorSystem({ phase: "update" }, (ctx) => {
6171
6358
  on(ctx, AddSelectionBox, (ctx2) => {
6172
- const entityId = (0, import_core66.createEntity)(ctx2);
6173
- (0, import_core66.addComponent)(ctx2, entityId, Block, {
6359
+ const entityId = (0, import_core69.createEntity)(ctx2);
6360
+ (0, import_core69.addComponent)(ctx2, entityId, Block, {
6174
6361
  tag: "selection-box",
6175
6362
  position: [0, 0],
6176
6363
  size: [0, 0],
6177
6364
  rotateZ: 0,
6178
6365
  rank: SELECTION_BOX_RANK
6179
6366
  });
6180
- (0, import_core66.addComponent)(ctx2, entityId, SelectionBox, {});
6367
+ (0, import_core69.addComponent)(ctx2, entityId, SelectionBox, {});
6181
6368
  });
6182
6369
  on(ctx, UpdateSelectionBox, (ctx2, { bounds, deselectOthers }) => {
6183
6370
  const selectionBoxes = selectionBoxQuery.current(ctx2);
@@ -6213,26 +6400,36 @@ var selectSystem2 = defineEditorSystem({ phase: "update" }, (ctx) => {
6213
6400
  return;
6214
6401
  }
6215
6402
  const selectionBoxId = selectionBoxes[0];
6216
- (0, import_core66.removeEntity)(ctx2, selectionBoxId);
6403
+ (0, import_core69.removeEntity)(ctx2, selectionBoxId);
6217
6404
  });
6218
6405
  });
6219
6406
 
6220
6407
  // src/systems/update/updateFrameContainmentSystem.ts
6221
- var import_core67 = require("@woven-ecs/core");
6408
+ var import_core70 = require("@woven-ecs/core");
6222
6409
  var updateFrameContainmentSystem = defineEditorSystem({ phase: "update", priority: -10 }, (ctx) => {
6223
6410
  on(ctx, AddFrameHighlight, (ctx2, { frameId }) => {
6224
- if (!(0, import_core67.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6225
- (0, import_core67.addComponent)(ctx2, frameId, FrameDropTarget);
6411
+ if (!(0, import_core70.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6412
+ (0, import_core70.addComponent)(ctx2, frameId, FrameDropTarget);
6226
6413
  }
6227
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
+ });
6228
6425
  on(ctx, RemoveFrameHighlight, (ctx2, { frameId }) => {
6229
- if ((0, import_core67.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6230
- (0, import_core67.removeComponent)(ctx2, frameId, FrameDropTarget);
6426
+ if ((0, import_core70.hasComponent)(ctx2, frameId, FrameDropTarget)) {
6427
+ (0, import_core70.removeComponent)(ctx2, frameId, FrameDropTarget);
6231
6428
  }
6232
6429
  });
6233
6430
  on(ctx, AssignFrameChildren, (ctx2, { assignments }) => {
6234
6431
  for (const { entityId, frameId } of assignments) {
6235
- if (!(0, import_core67.hasComponent)(ctx2, entityId, Block)) continue;
6432
+ if (!(0, import_core70.hasComponent)(ctx2, entityId, Block)) continue;
6236
6433
  const currentParentId = Block.read(ctx2, entityId).parentId;
6237
6434
  if (frameId !== null) {
6238
6435
  if (currentParentId !== frameId) {
@@ -6254,12 +6451,12 @@ var updateFrameContainmentSystem = defineEditorSystem({ phase: "update", priorit
6254
6451
  });
6255
6452
 
6256
6453
  // src/systems/update/updateFrameSetupSystem.ts
6257
- var import_core68 = require("@woven-ecs/core");
6258
- 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));
6259
6456
  var updateFrameSetupSystem = defineEditorSystem({ phase: "update", priority: 100 }, (ctx) => {
6260
6457
  for (const entityId of frameQuery2.added(ctx)) {
6261
- if ((0, import_core68.hasComponent)(ctx, entityId, HitGeometry)) continue;
6262
- (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);
6263
6460
  HitGeometry.addCapsuleUv(ctx, entityId, [0, 0], [1, 0], FRAME_EDGE_THICKNESS);
6264
6461
  HitGeometry.addCapsuleUv(ctx, entityId, [1, 0], [1, 1], FRAME_EDGE_THICKNESS);
6265
6462
  HitGeometry.addCapsuleUv(ctx, entityId, [1, 1], [0, 1], FRAME_EDGE_THICKNESS);
@@ -6273,8 +6470,8 @@ function createCorePlugin(options = {}) {
6273
6470
  return {
6274
6471
  name: CORE_PLUGIN_NAME,
6275
6472
  resources: parsedOptions,
6276
- singletons: Object.values(singletons_exports).filter((v) => v instanceof import_canvas_store46.CanvasSingletonDef),
6277
- 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),
6278
6475
  blockDefs: [
6279
6476
  {
6280
6477
  tag: "sticky-note",
@@ -6418,7 +6615,7 @@ function createCorePlugin(options = {}) {
6418
6615
  ],
6419
6616
  cursors: CURSORS,
6420
6617
  setup(ctx) {
6421
- const { domElement } = (0, import_core69.getResources)(ctx);
6618
+ const { domElement } = (0, import_core72.getResources)(ctx);
6422
6619
  if (!domElement) return;
6423
6620
  attachKeyboardListeners(domElement);
6424
6621
  attachMouseListeners(domElement);
@@ -6426,7 +6623,7 @@ function createCorePlugin(options = {}) {
6426
6623
  attachPointerListeners(domElement);
6427
6624
  },
6428
6625
  teardown(ctx) {
6429
- const { domElement } = (0, import_core69.getResources)(ctx);
6626
+ const { domElement } = (0, import_core72.getResources)(ctx);
6430
6627
  if (!domElement) return;
6431
6628
  detachKeyboardListeners(domElement);
6432
6629
  detachMouseListeners(domElement);
@@ -6437,8 +6634,8 @@ function createCorePlugin(options = {}) {
6437
6634
  }
6438
6635
 
6439
6636
  // src/Editor.ts
6440
- var import_canvas_store47 = require("@woven-ecs/canvas-store");
6441
- 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");
6442
6639
 
6443
6640
  // src/FontLoader.ts
6444
6641
  var import_zod2 = require("zod");
@@ -6468,21 +6665,44 @@ var FontFamily = import_zod2.z.object({
6468
6665
  });
6469
6666
  var FontLoader = class {
6470
6667
  constructor() {
6471
- this.loadedFonts = /* @__PURE__ */ new Set();
6472
- }
6473
- /**
6474
- * Load multiple font families.
6475
- * 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.
6476
6681
  *
6477
6682
  * @param families - Array of font families to load
6478
6683
  */
6479
6684
  async loadFonts(families) {
6480
- const unloadedFamilies = families.filter((family) => !this.loadedFonts.has(family.name));
6481
- if (unloadedFamilies.length === 0) {
6482
- return;
6483
- }
6484
- const fontPromises = unloadedFamilies.map((family) => this.loadSingleFont(family));
6485
- 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;
6486
6706
  }
6487
6707
  /**
6488
6708
  * Build a Google Fonts URL with weight and italic variants.
@@ -6519,34 +6739,36 @@ var FontLoader = class {
6519
6739
  }
6520
6740
  /**
6521
6741
  * Load a single font family.
6522
- * 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.
6523
6747
  *
6524
6748
  * @param family - Font family to load
6525
6749
  */
6526
- loadSingleFont(family) {
6527
- this.loadedFonts.add(family.name);
6528
- if (typeof document === "undefined") {
6529
- return Promise.resolve();
6530
- }
6531
- return new Promise((resolve, reject) => {
6532
- const link = document.createElement("link");
6533
- link.rel = "stylesheet";
6534
- link.href = this.buildGoogleFontsUrl(family);
6535
- link.onload = async () => {
6536
- try {
6537
- const loadPromises = this.getFontLoadPromises(family);
6538
- await Promise.all(loadPromises);
6539
- await document.fonts.ready;
6540
- resolve();
6541
- } catch (error) {
6542
- reject(new Error(`Failed to load font face for: ${family.name}. ${error}`));
6543
- }
6544
- };
6545
- link.onerror = () => {
6546
- reject(new Error(`Failed to load font stylesheet: ${family.name}`));
6547
- };
6548
- document.head.appendChild(link);
6549
- });
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
+ }
6550
6772
  }
6551
6773
  /**
6552
6774
  * Get promises for loading all font variants (weights and italics).
@@ -6572,13 +6794,13 @@ var FontLoader = class {
6572
6794
  * @returns True if the font has been loaded
6573
6795
  */
6574
6796
  isLoaded(fontName) {
6575
- return this.loadedFonts.has(fontName);
6797
+ return this.loaded.has(fontName);
6576
6798
  }
6577
6799
  /**
6578
6800
  * Get the set of loaded font names.
6579
6801
  */
6580
6802
  getLoadedFonts() {
6581
- return this.loadedFonts;
6803
+ return this.loaded;
6582
6804
  }
6583
6805
  };
6584
6806
 
@@ -6659,7 +6881,7 @@ var Editor = class {
6659
6881
  const plugins = pluginInputs.map(parsePlugin);
6660
6882
  const corePlugin = createCorePlugin(options.corePlugin);
6661
6883
  const sortedPlugins = sortPluginsByDependencies([corePlugin, ...plugins]);
6662
- const allDefs = [CommandMarker, import_canvas_store47.Synced];
6884
+ const allDefs = [CommandMarker, import_canvas_store49.Synced];
6663
6885
  for (const plugin of sortedPlugins) {
6664
6886
  if (plugin.components) {
6665
6887
  allDefs.push(...plugin.components);
@@ -6732,9 +6954,10 @@ var Editor = class {
6732
6954
  componentsByName,
6733
6955
  singletonsByName,
6734
6956
  componentsById,
6735
- singletonsById
6957
+ singletonsById,
6958
+ readonly: options.readonly
6736
6959
  };
6737
- this.world = new import_core70.World(allDefs, {
6960
+ this.world = new import_core73.World(allDefs, {
6738
6961
  maxEntities,
6739
6962
  resources: allResources
6740
6963
  });
@@ -6788,17 +7011,14 @@ var Editor = class {
6788
7011
  * Call this after construction to run async setup.
6789
7012
  */
6790
7013
  async initialize() {
6791
- if (this.fonts.length > 0) {
6792
- await this.fontLoader.loadFonts(this.fonts);
6793
- }
6794
7014
  Grid.copy(this.ctx, this.gridOptions);
6795
7015
  Controls.copy(this.ctx, this.controlsOptions);
6796
7016
  this.nextTick((ctx) => {
6797
- const userEntity = (0, import_core70.createEntity)(ctx);
6798
- (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, {
6799
7019
  id: crypto.randomUUID()
6800
7020
  });
6801
- (0, import_core70.addComponent)(ctx, userEntity, User, {
7021
+ (0, import_core73.addComponent)(ctx, userEntity, User, {
6802
7022
  userId: this.userData.userId,
6803
7023
  sessionId: this.userData.sessionId,
6804
7024
  color: this.userData.color,
@@ -6812,6 +7032,29 @@ var Editor = class {
6812
7032
  }
6813
7033
  }
6814
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
+ }
6815
7058
  /**
6816
7059
  * Run one frame of the editor loop.
6817
7060
  *
@@ -6877,6 +7120,10 @@ var Editor = class {
6877
7120
  def.spawn(ctx, payload);
6878
7121
  });
6879
7122
  }
7123
+ /** Runtime readonly flag. Input systems and command() check this to gate mutations. */
7124
+ get readonly() {
7125
+ return this.ctx.resources.readonly;
7126
+ }
6880
7127
  /**
6881
7128
  * Subscribe to query changes.
6882
7129
  *
@@ -7023,11 +7270,14 @@ var Editor = class {
7023
7270
  Key,
7024
7271
  Keybind,
7025
7272
  Keyboard,
7273
+ Layer,
7026
7274
  MAX_HIT_ARCS,
7027
7275
  MAX_HIT_CAPSULES,
7276
+ MAX_HIT_POLYGON_POINTS,
7028
7277
  MainThreadSystem,
7029
7278
  Mouse,
7030
7279
  Opacity,
7280
+ PlaceBlockEvent,
7031
7281
  Pointer,
7032
7282
  PointerButton,
7033
7283
  PointerType,
@@ -7087,6 +7337,7 @@ var Editor = class {
7087
7337
  clearPointerTrackingState,
7088
7338
  compareBlockOrder,
7089
7339
  convertRefsToUuids,
7340
+ createBlock,
7090
7341
  createCorePlugin,
7091
7342
  createEntity,
7092
7343
  defineCanvasComponent,
@@ -7124,11 +7375,14 @@ var Editor = class {
7124
7375
  isAlive,
7125
7376
  isAncestor,
7126
7377
  isHeldByRemote,
7378
+ isLayerInteractive,
7379
+ isReadonly,
7127
7380
  on,
7128
7381
  parsePlugin,
7129
7382
  removeComponent,
7130
7383
  removeEntity,
7131
7384
  resolveEmbedUrl,
7385
+ resolveLayerRank,
7132
7386
  resolveRefFields,
7133
7387
  runMachine,
7134
7388
  selectAll,