@woven-canvas/core 0.1.4 → 1.0.1

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.js CHANGED
@@ -8,7 +8,7 @@ var __export = (target, all) => {
8
8
  import {
9
9
  CanvasComponentDef as CanvasComponentDef7,
10
10
  CanvasSingletonDef as CanvasSingletonDef12,
11
- defineCanvasComponent as defineCanvasComponent13,
11
+ defineCanvasComponent as defineCanvasComponent14,
12
12
  defineCanvasSingleton as defineCanvasSingleton3,
13
13
  Synced as Synced4
14
14
  } from "@woven-ecs/canvas-store";
@@ -19,9 +19,9 @@ import {
19
19
  defineQuery as defineQuery11,
20
20
  defineSystem,
21
21
  EventType,
22
- field as field28,
22
+ field as field29,
23
23
  getBackrefs,
24
- getResources as getResources12,
24
+ getResources as getResources13,
25
25
  hasComponent as hasComponent7,
26
26
  isAlive,
27
27
  MainThreadSystem as MainThreadSystem2,
@@ -35,7 +35,7 @@ import {
35
35
  CanvasComponentDef as CanvasComponentDef6,
36
36
  CanvasSingletonDef as CanvasSingletonDef10
37
37
  } from "@woven-ecs/canvas-store";
38
- import { getResources as getResources11 } from "@woven-ecs/core";
38
+ import { getResources as getResources12 } from "@woven-ecs/core";
39
39
 
40
40
  // src/components/index.ts
41
41
  var components_exports = {};
@@ -46,6 +46,8 @@ __export(components_exports, {
46
46
  Color: () => Color,
47
47
  Connector: () => Connector,
48
48
  Edited: () => Edited,
49
+ Embed: () => Embed,
50
+ EmbedProvider: () => EmbedProvider,
49
51
  Held: () => Held,
50
52
  HitGeometry: () => HitGeometry,
51
53
  Hovered: () => Hovered,
@@ -325,6 +327,7 @@ var BlockDef = z.object({
325
327
  resizeMode: z.enum([ResizeMode.Scale, ResizeMode.Text, ResizeMode.Free, ResizeMode.GroupOnly]).default(ResizeMode.Scale),
326
328
  canRotate: z.boolean().default(true),
327
329
  canScale: z.boolean().default(true),
330
+ selectable: z.boolean().default(true),
328
331
  connectors: BlockDefConnectors.default(BlockDefConnectors.parse({}))
329
332
  });
330
333
 
@@ -721,30 +724,57 @@ var Connector = defineCanvasComponent2(
721
724
  import { defineCanvasComponent as defineCanvasComponent3 } from "@woven-ecs/canvas-store";
722
725
  var Edited = defineCanvasComponent3({ name: "edited" }, {});
723
726
 
724
- // src/components/Held.ts
727
+ // src/components/Embed.ts
725
728
  import { defineCanvasComponent as defineCanvasComponent4 } from "@woven-ecs/canvas-store";
726
729
  import { field as field6 } from "@woven-ecs/core";
727
- var Held = defineCanvasComponent4(
730
+ var EmbedProvider = /* @__PURE__ */ ((EmbedProvider2) => {
731
+ EmbedProvider2["GoogleMaps"] = "google-maps";
732
+ EmbedProvider2["GoogleCalendar"] = "google-calendar";
733
+ EmbedProvider2["GoogleSlides"] = "google-slides";
734
+ EmbedProvider2["Tldraw"] = "tldraw";
735
+ EmbedProvider2["Figma"] = "figma";
736
+ EmbedProvider2["GithubGist"] = "github-gist";
737
+ EmbedProvider2["Youtube"] = "youtube";
738
+ EmbedProvider2["Spotify"] = "spotify";
739
+ EmbedProvider2["Unknown"] = "unknown";
740
+ return EmbedProvider2;
741
+ })(EmbedProvider || {});
742
+ var Embed = defineCanvasComponent4(
743
+ { name: "embed", sync: "document" },
744
+ {
745
+ /** The original URL provided by the user */
746
+ url: field6.string().max(2048).default(""),
747
+ /** The resolved embed/iframe URL */
748
+ embedUrl: field6.string().max(2048).default(""),
749
+ /** The embed provider (used for provider-specific styling/behavior) */
750
+ provider: field6.enum(EmbedProvider).default("unknown" /* Unknown */)
751
+ }
752
+ );
753
+
754
+ // src/components/Held.ts
755
+ import { defineCanvasComponent as defineCanvasComponent5 } from "@woven-ecs/canvas-store";
756
+ import { field as field7 } from "@woven-ecs/core";
757
+ var Held = defineCanvasComponent5(
728
758
  { name: "held", sync: "ephemeral" },
729
759
  {
730
- sessionId: field6.string().max(36).default("")
760
+ sessionId: field7.string().max(36).default("")
731
761
  }
732
762
  );
733
763
 
734
764
  // src/components/HitGeometry.ts
735
765
  import { Arc, Capsule, Mat2 } from "@woven-canvas/math";
736
766
  import { CanvasComponentDef as CanvasComponentDef4 } from "@woven-ecs/canvas-store";
737
- import { field as field7 } from "@woven-ecs/core";
767
+ import { field as field8 } from "@woven-ecs/core";
738
768
  var _uvToWorldMatrix = [1, 0, 0, 1, 0, 0];
739
769
  var MAX_HIT_CAPSULES = 64;
740
770
  var FLOATS_PER_CAPSULE = 5;
741
771
  var MAX_HIT_ARCS = 2;
742
772
  var FLOATS_PER_ARC = 7;
743
773
  var HitGeometrySchema = {
744
- hitCapsules: field7.buffer(field7.float32()).size(MAX_HIT_CAPSULES * FLOATS_PER_CAPSULE),
745
- capsuleCount: field7.uint16().default(0),
746
- hitArcs: field7.buffer(field7.float32()).size(MAX_HIT_ARCS * FLOATS_PER_ARC),
747
- arcCount: field7.uint16().default(0)
774
+ hitCapsules: field8.buffer(field8.float32()).size(MAX_HIT_CAPSULES * FLOATS_PER_CAPSULE),
775
+ capsuleCount: field8.uint16().default(0),
776
+ hitArcs: field8.buffer(field8.float32()).size(MAX_HIT_ARCS * FLOATS_PER_ARC),
777
+ arcCount: field8.uint16().default(0)
748
778
  };
749
779
  var HitGeometryDef = class extends CanvasComponentDef4 {
750
780
  constructor() {
@@ -1050,37 +1080,37 @@ var HitGeometryDef = class extends CanvasComponentDef4 {
1050
1080
  var HitGeometry = new HitGeometryDef();
1051
1081
 
1052
1082
  // src/components/Hovered.ts
1053
- import { defineCanvasComponent as defineCanvasComponent5 } from "@woven-ecs/canvas-store";
1054
- var Hovered = defineCanvasComponent5({ name: "hovered" }, {});
1083
+ import { defineCanvasComponent as defineCanvasComponent6 } from "@woven-ecs/canvas-store";
1084
+ var Hovered = defineCanvasComponent6({ name: "hovered" }, {});
1055
1085
 
1056
1086
  // src/components/Image.ts
1057
- import { defineCanvasComponent as defineCanvasComponent6 } from "@woven-ecs/canvas-store";
1058
- import { field as field8 } from "@woven-ecs/core";
1059
- var Image = defineCanvasComponent6(
1087
+ import { defineCanvasComponent as defineCanvasComponent7 } from "@woven-ecs/canvas-store";
1088
+ import { field as field9 } from "@woven-ecs/core";
1089
+ var Image = defineCanvasComponent7(
1060
1090
  { name: "image", sync: "document" },
1061
1091
  {
1062
1092
  /** Image width in pixels */
1063
- width: field8.uint16().default(0),
1093
+ width: field9.uint16().default(0),
1064
1094
  /** Image height in pixels */
1065
- height: field8.uint16().default(0),
1095
+ height: field9.uint16().default(0),
1066
1096
  /** Alt text for accessibility */
1067
- alt: field8.string().max(256).default("")
1097
+ alt: field9.string().max(256).default("")
1068
1098
  }
1069
1099
  );
1070
1100
 
1071
1101
  // src/components/Opacity.ts
1072
- import { defineCanvasComponent as defineCanvasComponent7 } from "@woven-ecs/canvas-store";
1073
- import { field as field9 } from "@woven-ecs/core";
1074
- var Opacity = defineCanvasComponent7(
1102
+ import { defineCanvasComponent as defineCanvasComponent8 } from "@woven-ecs/canvas-store";
1103
+ import { field as field10 } from "@woven-ecs/core";
1104
+ var Opacity = defineCanvasComponent8(
1075
1105
  { name: "opacity" },
1076
1106
  {
1077
- value: field9.uint8().default(255)
1107
+ value: field10.uint8().default(255)
1078
1108
  }
1079
1109
  );
1080
1110
 
1081
1111
  // src/components/Pointer.ts
1082
1112
  import { CanvasComponentDef as CanvasComponentDef5 } from "@woven-ecs/canvas-store";
1083
- import { field as field10 } from "@woven-ecs/core";
1113
+ import { field as field11 } from "@woven-ecs/core";
1084
1114
  var PointerButton = {
1085
1115
  None: "none",
1086
1116
  Left: "left",
@@ -1097,30 +1127,30 @@ var PointerType = {
1097
1127
  var SAMPLE_COUNT = 6;
1098
1128
  var PointerSchema = {
1099
1129
  /** Unique pointer ID (from PointerEvent.pointerId) */
1100
- pointerId: field10.uint16().default(0),
1130
+ pointerId: field11.uint16().default(0),
1101
1131
  /** Current position relative to the editor element [x, y] */
1102
- position: field10.tuple(field10.float32(), 2).default([0, 0]),
1132
+ position: field11.tuple(field11.float32(), 2).default([0, 0]),
1103
1133
  /** Position where the pointer went down [x, y] */
1104
- downPosition: field10.tuple(field10.float32(), 2).default([0, 0]),
1134
+ downPosition: field11.tuple(field11.float32(), 2).default([0, 0]),
1105
1135
  /** Frame number when the pointer went down (for click detection) */
1106
- downFrame: field10.uint32().default(0),
1136
+ downFrame: field11.uint32().default(0),
1107
1137
  /** Which button is pressed */
1108
- button: field10.enum(PointerButton).default(PointerButton.None),
1138
+ button: field11.enum(PointerButton).default(PointerButton.None),
1109
1139
  /** Type of pointer device */
1110
- pointerType: field10.enum(PointerType).default(PointerType.Mouse),
1140
+ pointerType: field11.enum(PointerType).default(PointerType.Mouse),
1111
1141
  /** Pressure from 0 to 1 (for pen/touch) */
1112
- pressure: field10.float32().default(0),
1142
+ pressure: field11.float32().default(0),
1113
1143
  /** Whether the pointer event target was not the editor element */
1114
- obscured: field10.boolean().default(false),
1144
+ obscured: field11.boolean().default(false),
1115
1145
  // Velocity tracking (ring buffer for position samples)
1116
1146
  /** Ring buffer of previous positions [x0, y0, x1, y1, ...] @internal */
1117
- _prevPositions: field10.array(field10.float32(), SAMPLE_COUNT * 2),
1147
+ _prevPositions: field11.array(field11.float32(), SAMPLE_COUNT * 2),
1118
1148
  /** Ring buffer of timestamps for each position sample @internal */
1119
- _prevTimes: field10.array(field10.float32(), SAMPLE_COUNT),
1149
+ _prevTimes: field11.array(field11.float32(), SAMPLE_COUNT),
1120
1150
  /** Total number of samples added (used for ring buffer indexing) @internal */
1121
- _sampleCount: field10.int32().default(0),
1151
+ _sampleCount: field11.int32().default(0),
1122
1152
  /** Computed velocity [vx, vy] in pixels per second @internal */
1123
- _velocity: field10.tuple(field10.float32(), 2).default([0, 0])
1153
+ _velocity: field11.tuple(field11.float32(), 2).default([0, 0])
1124
1154
  };
1125
1155
  var PointerDef = class extends CanvasComponentDef5 {
1126
1156
  constructor() {
@@ -1231,108 +1261,108 @@ function addPointerSample(pointer, position, time) {
1231
1261
  }
1232
1262
 
1233
1263
  // src/components/ScaleWithZoom.ts
1234
- import { defineCanvasComponent as defineCanvasComponent8 } from "@woven-ecs/canvas-store";
1235
- import { field as field11 } from "@woven-ecs/core";
1236
- var ScaleWithZoom = defineCanvasComponent8(
1264
+ import { defineCanvasComponent as defineCanvasComponent9 } from "@woven-ecs/canvas-store";
1265
+ import { field as field12 } from "@woven-ecs/core";
1266
+ var ScaleWithZoom = defineCanvasComponent9(
1237
1267
  { name: "scaleWithZoom" },
1238
1268
  {
1239
1269
  /** Pivot point for scaling as [x, y] (0-1, default 0.5,0.5 = center) */
1240
- anchor: field11.tuple(field11.float64(), 2).default([0.5, 0.5]),
1270
+ anchor: field12.tuple(field12.float64(), 2).default([0.5, 0.5]),
1241
1271
  /** Initial position as [left, top] at zoom=1 */
1242
- startPosition: field11.tuple(field11.float64(), 2).default([0, 0]),
1272
+ startPosition: field12.tuple(field12.float64(), 2).default([0, 0]),
1243
1273
  /** Initial size as [width, height] at zoom=1 */
1244
- startSize: field11.tuple(field11.float64(), 2).default([0, 0]),
1274
+ startSize: field12.tuple(field12.float64(), 2).default([0, 0]),
1245
1275
  /** Scale multiplier per dimension: [x, y] (0 = no zoom effect, 1 = full zoom effect, 0.5 = half effect) */
1246
- scaleMultiplier: field11.tuple(field11.float64(), 2).default([1, 1])
1276
+ scaleMultiplier: field12.tuple(field12.float64(), 2).default([1, 1])
1247
1277
  }
1248
1278
  );
1249
1279
 
1250
1280
  // src/components/Shape.ts
1251
- import { defineCanvasComponent as defineCanvasComponent9 } from "@woven-ecs/canvas-store";
1252
- import { field as field12 } from "@woven-ecs/core";
1281
+ import { defineCanvasComponent as defineCanvasComponent10 } from "@woven-ecs/canvas-store";
1282
+ import { field as field13 } from "@woven-ecs/core";
1253
1283
  var StrokeKind = {
1254
1284
  Solid: "solid",
1255
1285
  Dashed: "dashed",
1256
1286
  None: "none"
1257
1287
  };
1258
- var Shape = defineCanvasComponent9(
1288
+ var Shape = defineCanvasComponent10(
1259
1289
  { name: "shape", sync: "document" },
1260
1290
  {
1261
1291
  /** The kind of shape to render (e.g. 'rectangle', 'ellipse', or custom shape key) */
1262
- kind: field12.string().default("rectangle"),
1292
+ kind: field13.string().default("rectangle"),
1263
1293
  /** Stroke style */
1264
- strokeKind: field12.enum(StrokeKind).default(StrokeKind.Solid),
1294
+ strokeKind: field13.enum(StrokeKind).default(StrokeKind.Solid),
1265
1295
  /** Stroke width in pixels */
1266
- strokeWidth: field12.uint16().default(2),
1296
+ strokeWidth: field13.uint16().default(2),
1267
1297
  /** Stroke color - red component (0-255) */
1268
- strokeRed: field12.uint8().default(0),
1298
+ strokeRed: field13.uint8().default(0),
1269
1299
  /** Stroke color - green component (0-255) */
1270
- strokeGreen: field12.uint8().default(0),
1300
+ strokeGreen: field13.uint8().default(0),
1271
1301
  /** Stroke color - blue component (0-255) */
1272
- strokeBlue: field12.uint8().default(0),
1302
+ strokeBlue: field13.uint8().default(0),
1273
1303
  /** Stroke color - alpha component (0-255) */
1274
- strokeAlpha: field12.uint8().default(255),
1304
+ strokeAlpha: field13.uint8().default(255),
1275
1305
  /** Fill color - red component (0-255) */
1276
- fillRed: field12.uint8().default(255),
1306
+ fillRed: field13.uint8().default(255),
1277
1307
  /** Fill color - green component (0-255) */
1278
- fillGreen: field12.uint8().default(255),
1308
+ fillGreen: field13.uint8().default(255),
1279
1309
  /** Fill color - blue component (0-255) */
1280
- fillBlue: field12.uint8().default(255),
1310
+ fillBlue: field13.uint8().default(255),
1281
1311
  /** Fill color - alpha component (0-255) */
1282
- fillAlpha: field12.uint8().default(0)
1312
+ fillAlpha: field13.uint8().default(0)
1283
1313
  }
1284
1314
  );
1285
1315
 
1286
1316
  // src/components/Text.ts
1287
- import { defineCanvasComponent as defineCanvasComponent10 } from "@woven-ecs/canvas-store";
1288
- import { field as field13 } from "@woven-ecs/core";
1289
- var Text = defineCanvasComponent10(
1317
+ import { defineCanvasComponent as defineCanvasComponent11 } from "@woven-ecs/canvas-store";
1318
+ import { field as field14 } from "@woven-ecs/core";
1319
+ var Text = defineCanvasComponent11(
1290
1320
  { name: "text", sync: "document" },
1291
1321
  {
1292
1322
  /** HTML content (supports rich text formatting) */
1293
- content: field13.string().max(1e4).default(""),
1323
+ content: field14.string().max(1e4).default(""),
1294
1324
  /** Font size in pixels */
1295
- fontSizePx: field13.float64().default(24),
1325
+ fontSizePx: field14.float64().default(24),
1296
1326
  /** Font family name */
1297
- fontFamily: field13.string().max(64).default("Figtree"),
1327
+ fontFamily: field14.string().max(64).default("Figtree"),
1298
1328
  /** Line height multiplier */
1299
- lineHeight: field13.float64().default(1.2),
1329
+ lineHeight: field14.float64().default(1.2),
1300
1330
  /** Letter spacing in em units */
1301
- letterSpacingEm: field13.float64().default(0),
1331
+ letterSpacingEm: field14.float64().default(0),
1302
1332
  /** Whether width is constrained (text wraps) */
1303
- constrainWidth: field13.boolean().default(true),
1333
+ constrainWidth: field14.boolean().default(true),
1304
1334
  /** Default text alignment for new paragraphs */
1305
- defaultAlignment: field13.enum(TextAlignment).default(TextAlignment.Left)
1335
+ defaultAlignment: field14.enum(TextAlignment).default(TextAlignment.Left)
1306
1336
  }
1307
1337
  );
1308
1338
 
1309
1339
  // src/components/User.ts
1310
- import { defineCanvasComponent as defineCanvasComponent11 } from "@woven-ecs/canvas-store";
1311
- import { field as field14 } from "@woven-ecs/core";
1312
- var User = defineCanvasComponent11(
1340
+ import { defineCanvasComponent as defineCanvasComponent12 } from "@woven-ecs/canvas-store";
1341
+ import { field as field15 } from "@woven-ecs/core";
1342
+ var User = defineCanvasComponent12(
1313
1343
  { name: "user", sync: "ephemeral" },
1314
1344
  {
1315
- userId: field14.string().max(36),
1316
- sessionId: field14.string().max(36),
1317
- color: field14.string().max(7),
1318
- name: field14.string().max(100),
1319
- avatar: field14.string().max(500),
1320
- position: field14.tuple(field14.float32(), 2).default([0, 0])
1345
+ userId: field15.string().max(36),
1346
+ sessionId: field15.string().max(36),
1347
+ color: field15.string().max(7),
1348
+ name: field15.string().max(100),
1349
+ avatar: field15.string().max(500),
1350
+ position: field15.tuple(field15.float32(), 2).default([0, 0])
1321
1351
  }
1322
1352
  );
1323
1353
 
1324
1354
  // src/components/VerticalAlign.ts
1325
- import { defineCanvasComponent as defineCanvasComponent12 } from "@woven-ecs/canvas-store";
1326
- import { field as field15 } from "@woven-ecs/core";
1327
- var VerticalAlign = defineCanvasComponent12(
1355
+ import { defineCanvasComponent as defineCanvasComponent13 } from "@woven-ecs/canvas-store";
1356
+ import { field as field16 } from "@woven-ecs/core";
1357
+ var VerticalAlign = defineCanvasComponent13(
1328
1358
  { name: "verticalAlign", sync: "document" },
1329
1359
  {
1330
- value: field15.enum(VerticalAlignment).default(VerticalAlignment.Top)
1360
+ value: field16.enum(VerticalAlignment).default(VerticalAlignment.Top)
1331
1361
  }
1332
1362
  );
1333
1363
 
1334
1364
  // src/constants.ts
1335
- var PLUGIN_NAME = "core";
1365
+ var CORE_PLUGIN_NAME = "core";
1336
1366
  var STRATUM_ORDER = {
1337
1367
  background: 0,
1338
1368
  content: 1,
@@ -1361,20 +1391,20 @@ __export(singletons_exports, {
1361
1391
 
1362
1392
  // src/singletons/Camera.ts
1363
1393
  import { CanvasSingletonDef as CanvasSingletonDef2 } from "@woven-ecs/canvas-store";
1364
- import { field as field17 } from "@woven-ecs/core";
1394
+ import { field as field18 } from "@woven-ecs/core";
1365
1395
 
1366
1396
  // src/singletons/Screen.ts
1367
1397
  import { CanvasSingletonDef } from "@woven-ecs/canvas-store";
1368
- import { field as field16 } from "@woven-ecs/core";
1398
+ import { field as field17 } from "@woven-ecs/core";
1369
1399
  var ScreenSchema = {
1370
1400
  /** Width of the editor element in pixels */
1371
- width: field16.float64().default(0),
1401
+ width: field17.float64().default(0),
1372
1402
  /** Height of the editor element in pixels */
1373
- height: field16.float64().default(0),
1403
+ height: field17.float64().default(0),
1374
1404
  /** Left offset of the editor element relative to the viewport */
1375
- left: field16.float64().default(0),
1405
+ left: field17.float64().default(0),
1376
1406
  /** Top offset of the editor element relative to the viewport */
1377
- top: field16.float64().default(0)
1407
+ top: field17.float64().default(0)
1378
1408
  };
1379
1409
  var ScreenDef = class extends CanvasSingletonDef {
1380
1410
  constructor() {
@@ -1401,15 +1431,15 @@ var Screen = new ScreenDef();
1401
1431
  // src/singletons/Camera.ts
1402
1432
  var CameraSchema = {
1403
1433
  /** Top position of the camera in world coordinates */
1404
- top: field17.float64().default(0),
1434
+ top: field18.float64().default(0),
1405
1435
  /** Left position of the camera in world coordinates */
1406
- left: field17.float64().default(0),
1436
+ left: field18.float64().default(0),
1407
1437
  /** Zoom level (1 = 100%, 2 = 200%, 0.5 = 50%) */
1408
- zoom: field17.float64().default(1),
1438
+ zoom: field18.float64().default(1),
1409
1439
  /** Whether the camera viewport intersects any blocks */
1410
- canSeeBlocks: field17.boolean().default(true),
1440
+ canSeeBlocks: field18.boolean().default(true),
1411
1441
  /** Reference to a block that the camera can currently see (for optimization) */
1412
- lastSeenBlock: field17.ref()
1442
+ lastSeenBlock: field18.ref()
1413
1443
  };
1414
1444
  var CameraDef = class extends CanvasSingletonDef2 {
1415
1445
  constructor() {
@@ -1485,20 +1515,20 @@ var Camera = new CameraDef();
1485
1515
 
1486
1516
  // src/singletons/Controls.ts
1487
1517
  import { CanvasSingletonDef as CanvasSingletonDef3 } from "@woven-ecs/canvas-store";
1488
- import { field as field18 } from "@woven-ecs/core";
1518
+ import { field as field19 } from "@woven-ecs/core";
1489
1519
  var ControlsSchema = {
1490
1520
  /** Tool activated by left mouse button */
1491
- leftMouseTool: field18.string().max(32).default("select"),
1521
+ leftMouseTool: field19.string().max(32).default("select"),
1492
1522
  /** Tool activated by middle mouse button */
1493
- middleMouseTool: field18.string().max(32).default("hand"),
1523
+ middleMouseTool: field19.string().max(32).default("hand"),
1494
1524
  /** Tool activated by right mouse button */
1495
- rightMouseTool: field18.string().max(32).default("menu"),
1525
+ rightMouseTool: field19.string().max(32).default("menu"),
1496
1526
  /** Tool activated by mouse wheel */
1497
- wheelTool: field18.string().max(32).default("scroll"),
1527
+ wheelTool: field19.string().max(32).default("scroll"),
1498
1528
  /** Tool activated by mouse wheel with modifier key held */
1499
- modWheelTool: field18.string().max(32).default("zoom"),
1529
+ modWheelTool: field19.string().max(32).default("zoom"),
1500
1530
  /** JSON snapshot of block to place on next click (empty string = no placement active) */
1501
- heldSnapshot: field18.string().max(4096).default("")
1531
+ heldSnapshot: field19.string().max(65536).default("")
1502
1532
  };
1503
1533
  var ControlsDef = class extends CanvasSingletonDef3 {
1504
1534
  constructor() {
@@ -1540,16 +1570,16 @@ var Controls = new ControlsDef();
1540
1570
 
1541
1571
  // src/singletons/Cursor.ts
1542
1572
  import { CanvasSingletonDef as CanvasSingletonDef4 } from "@woven-ecs/canvas-store";
1543
- import { field as field19 } from "@woven-ecs/core";
1573
+ import { field as field20 } from "@woven-ecs/core";
1544
1574
  var CursorSchema = {
1545
1575
  /** Base cursor kind (from current tool) */
1546
- cursorKind: field19.string().max(64).default("select"),
1576
+ cursorKind: field20.string().max(64).default("select"),
1547
1577
  /** Base cursor rotation in radians */
1548
- rotation: field19.float64().default(0),
1578
+ rotation: field20.float64().default(0),
1549
1579
  /** Context-specific cursor kind (overrides cursorKind when set, e.g., during drag/hover) */
1550
- contextCursorKind: field19.string().max(64).default(""),
1580
+ contextCursorKind: field20.string().max(64).default(""),
1551
1581
  /** Context cursor rotation in radians */
1552
- contextRotation: field19.float64().default(0)
1582
+ contextRotation: field20.float64().default(0)
1553
1583
  };
1554
1584
  var CursorDef2 = class extends CanvasSingletonDef4 {
1555
1585
  constructor() {
@@ -1597,37 +1627,37 @@ var Cursor = new CursorDef2();
1597
1627
 
1598
1628
  // src/singletons/Frame.ts
1599
1629
  import { defineCanvasSingleton } from "@woven-ecs/canvas-store";
1600
- import { field as field20 } from "@woven-ecs/core";
1630
+ import { field as field21 } from "@woven-ecs/core";
1601
1631
  var Frame = defineCanvasSingleton(
1602
1632
  { name: "frame" },
1603
1633
  {
1604
1634
  /** Current frame number (increments each tick) */
1605
- number: field20.uint32().default(0),
1635
+ number: field21.uint32().default(0),
1606
1636
  /** Time since last frame in seconds */
1607
- delta: field20.float64().default(0),
1637
+ delta: field21.float64().default(0),
1608
1638
  /** Timestamp of current frame in milliseconds (from performance.now()) */
1609
- time: field20.float64().default(0),
1639
+ time: field21.float64().default(0),
1610
1640
  /** Timestamp of previous frame in milliseconds (0 if first frame) */
1611
- lastTime: field20.float64().default(0)
1641
+ lastTime: field21.float64().default(0)
1612
1642
  }
1613
1643
  );
1614
1644
 
1615
1645
  // src/singletons/Grid.ts
1616
1646
  import { CanvasSingletonDef as CanvasSingletonDef5 } from "@woven-ecs/canvas-store";
1617
- import { field as field21 } from "@woven-ecs/core";
1647
+ import { field as field22 } from "@woven-ecs/core";
1618
1648
  var GridSchema = {
1619
1649
  /** Whether grid snapping is enabled */
1620
- enabled: field21.boolean().default(false),
1650
+ enabled: field22.boolean().default(false),
1621
1651
  /** Whether resized/rotated objects must stay aligned to the grid */
1622
- strict: field21.boolean().default(false),
1652
+ strict: field22.boolean().default(false),
1623
1653
  /** Width of each grid column in world units */
1624
- colWidth: field21.float64().default(20),
1654
+ colWidth: field22.float64().default(20),
1625
1655
  /** Height of each grid row in world units */
1626
- rowHeight: field21.float64().default(20),
1656
+ rowHeight: field22.float64().default(20),
1627
1657
  /** Angular snap increment in radians when grid is enabled */
1628
- snapAngleRad: field21.float64().default(Math.PI / 36),
1658
+ snapAngleRad: field22.float64().default(Math.PI / 36),
1629
1659
  /** Angular snap increment in radians when shift key is held */
1630
- shiftSnapAngleRad: field21.float64().default(Math.PI / 12)
1660
+ shiftSnapAngleRad: field22.float64().default(Math.PI / 12)
1631
1661
  };
1632
1662
  var GridDef = class extends CanvasSingletonDef5 {
1633
1663
  constructor() {
@@ -1690,14 +1720,14 @@ var Grid = new GridDef();
1690
1720
 
1691
1721
  // src/singletons/Intersect.ts
1692
1722
  import { CanvasSingletonDef as CanvasSingletonDef6 } from "@woven-ecs/canvas-store";
1693
- import { field as field22 } from "@woven-ecs/core";
1723
+ import { field as field23 } from "@woven-ecs/core";
1694
1724
  var IntersectSchema = {
1695
1725
  // Store up to 5 intersected entity IDs
1696
- entity1: field22.ref(),
1697
- entity2: field22.ref(),
1698
- entity3: field22.ref(),
1699
- entity4: field22.ref(),
1700
- entity5: field22.ref()
1726
+ entity1: field23.ref(),
1727
+ entity2: field23.ref(),
1728
+ entity3: field23.ref(),
1729
+ entity4: field23.ref(),
1730
+ entity5: field23.ref()
1701
1731
  };
1702
1732
  var IntersectDef = class extends CanvasSingletonDef6 {
1703
1733
  constructor() {
@@ -1749,30 +1779,30 @@ var Intersect = new IntersectDef();
1749
1779
 
1750
1780
  // src/singletons/Keyboard.ts
1751
1781
  import { CanvasSingletonDef as CanvasSingletonDef7 } from "@woven-ecs/canvas-store";
1752
- import { field as field23 } from "@woven-ecs/core";
1782
+ import { field as field24 } from "@woven-ecs/core";
1753
1783
  var KEY_BUFFER_SIZE = 32;
1754
1784
  var KeyboardSchema = {
1755
1785
  /**
1756
1786
  * Buffer where each bit represents whether a key is currently pressed.
1757
1787
  * Uses field.buffer for zero-allocation subarray views.
1758
1788
  */
1759
- keysDown: field23.buffer(field23.uint8()).size(KEY_BUFFER_SIZE),
1789
+ keysDown: field24.buffer(field24.uint8()).size(KEY_BUFFER_SIZE),
1760
1790
  /**
1761
1791
  * Buffer for key-down triggers (true for exactly 1 frame when key is pressed).
1762
1792
  * Uses field.buffer for zero-allocation subarray views.
1763
1793
  */
1764
- keysDownTrigger: field23.buffer(field23.uint8()).size(KEY_BUFFER_SIZE),
1794
+ keysDownTrigger: field24.buffer(field24.uint8()).size(KEY_BUFFER_SIZE),
1765
1795
  /**
1766
1796
  * Buffer for key-up triggers (true for exactly 1 frame when key is released).
1767
1797
  * Uses field.buffer for zero-allocation subarray views.
1768
1798
  */
1769
- keysUpTrigger: field23.buffer(field23.uint8()).size(KEY_BUFFER_SIZE),
1799
+ keysUpTrigger: field24.buffer(field24.uint8()).size(KEY_BUFFER_SIZE),
1770
1800
  /** Common modifier - Shift key is down */
1771
- shiftDown: field23.boolean().default(false),
1801
+ shiftDown: field24.boolean().default(false),
1772
1802
  /** Common modifier - Alt/Option key is down */
1773
- altDown: field23.boolean().default(false),
1803
+ altDown: field24.boolean().default(false),
1774
1804
  /** Common modifier - Ctrl (Windows/Linux) or Cmd (Mac) is down */
1775
- modDown: field23.boolean().default(false)
1805
+ modDown: field24.boolean().default(false)
1776
1806
  };
1777
1807
  function getBit(buffer, bitIndex) {
1778
1808
  if (bitIndex < 0 || bitIndex >= buffer.length * 8) return false;
@@ -2055,22 +2085,24 @@ var Key = {
2055
2085
 
2056
2086
  // src/singletons/Mouse.ts
2057
2087
  import { CanvasSingletonDef as CanvasSingletonDef8 } from "@woven-ecs/canvas-store";
2058
- import { field as field24 } from "@woven-ecs/core";
2088
+ import { field as field25 } from "@woven-ecs/core";
2059
2089
  var MouseSchema = {
2060
2090
  /** Current mouse position relative to the editor element [x, y] */
2061
- position: field24.tuple(field24.float32(), 2).default([0, 0]),
2091
+ position: field25.tuple(field25.float32(), 2).default([0, 0]),
2062
2092
  /** Horizontal wheel delta (positive = scroll right) */
2063
- wheelDeltaX: field24.float32().default(0),
2093
+ wheelDeltaX: field25.float32().default(0),
2064
2094
  /** Vertical wheel delta (positive = scroll down), normalized across browsers */
2065
- wheelDeltaY: field24.float32().default(0),
2095
+ wheelDeltaY: field25.float32().default(0),
2066
2096
  /** True for 1 frame when mouse moves */
2067
- moveTrigger: field24.boolean().default(false),
2097
+ moveTrigger: field25.boolean().default(false),
2068
2098
  /** True for 1 frame when wheel is scrolled */
2069
- wheelTrigger: field24.boolean().default(false),
2099
+ wheelTrigger: field25.boolean().default(false),
2070
2100
  /** True for 1 frame when mouse enters the editor element */
2071
- enterTrigger: field24.boolean().default(false),
2101
+ enterTrigger: field25.boolean().default(false),
2072
2102
  /** True for 1 frame when mouse leaves the editor element */
2073
- leaveTrigger: field24.boolean().default(false)
2103
+ leaveTrigger: field25.boolean().default(false),
2104
+ /** Whether the mouse is over a UI element (not the canvas) */
2105
+ obscured: field25.boolean().default(false)
2074
2106
  };
2075
2107
  var MouseDef = class extends CanvasSingletonDef8 {
2076
2108
  constructor() {
@@ -2102,16 +2134,20 @@ var MouseDef = class extends CanvasSingletonDef8 {
2102
2134
  const m = this.read(ctx);
2103
2135
  return [m.wheelDeltaX, m.wheelDeltaY];
2104
2136
  }
2137
+ /** Check if mouse is over a UI element (not the canvas) */
2138
+ isObscured(ctx) {
2139
+ return this.read(ctx).obscured;
2140
+ }
2105
2141
  };
2106
2142
  var Mouse = new MouseDef();
2107
2143
 
2108
2144
  // src/singletons/RankBounds.ts
2109
2145
  import { CanvasSingletonDef as CanvasSingletonDef9 } from "@woven-ecs/canvas-store";
2110
- import { field as field25 } from "@woven-ecs/core";
2146
+ import { field as field26 } from "@woven-ecs/core";
2111
2147
  import { generateJitteredKeyBetween } from "fractional-indexing-jittered";
2112
2148
  var RankBoundsSchema = {
2113
- minRank: field25.string().max(36).default(""),
2114
- maxRank: field25.string().max(36).default("")
2149
+ minRank: field26.string().max(36).default(""),
2150
+ maxRank: field26.string().max(36).default("")
2115
2151
  };
2116
2152
  var RankBoundsDef = class extends CanvasSingletonDef9 {
2117
2153
  constructor() {
@@ -2170,12 +2206,12 @@ var RankBounds = new RankBoundsDef();
2170
2206
 
2171
2207
  // src/singletons/ScaleWithZoomState.ts
2172
2208
  import { defineCanvasSingleton as defineCanvasSingleton2 } from "@woven-ecs/canvas-store";
2173
- import { field as field26 } from "@woven-ecs/core";
2209
+ import { field as field27 } from "@woven-ecs/core";
2174
2210
  var ScaleWithZoomState = defineCanvasSingleton2(
2175
2211
  { name: "scaleWithZoomState" },
2176
2212
  {
2177
2213
  /** Last processed zoom level */
2178
- lastZoom: field26.float64().default(1)
2214
+ lastZoom: field27.float64().default(1)
2179
2215
  }
2180
2216
  );
2181
2217
 
@@ -2188,12 +2224,12 @@ import {
2188
2224
  createEntity,
2189
2225
  defineComponent,
2190
2226
  defineQuery,
2191
- field as field27,
2227
+ field as field28,
2192
2228
  getResources,
2193
2229
  removeEntity
2194
2230
  } from "@woven-ecs/core";
2195
2231
  var CommandMarker = defineComponent({
2196
- name: field27.string().max(128)
2232
+ name: field28.string().max(128)
2197
2233
  });
2198
2234
  var editorPayloads = /* @__PURE__ */ new WeakMap();
2199
2235
  function getPayloadMap(ctx) {
@@ -2332,6 +2368,7 @@ function detachKeyboardListeners(domElement) {
2332
2368
  }
2333
2369
  var keyboardSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2334
2370
  const resources = getResources3(ctx);
2371
+ if (!resources.domElement) return;
2335
2372
  const state = instanceState.get(resources.domElement);
2336
2373
  if (!state) return;
2337
2374
  on(ctx, ResetKeyboard, () => {
@@ -2394,7 +2431,8 @@ function attachMouseListeners(domElement) {
2394
2431
  state.eventsBuffer.push({
2395
2432
  type: "mousemove",
2396
2433
  clientX: e.clientX,
2397
- clientY: e.clientY
2434
+ clientY: e.clientY,
2435
+ target: e.target
2398
2436
  });
2399
2437
  },
2400
2438
  onWheel: (e) => {
@@ -2432,6 +2470,7 @@ function detachMouseListeners(domElement) {
2432
2470
  }
2433
2471
  var mouseSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2434
2472
  const resources = getResources4(ctx);
2473
+ if (!resources.domElement) return;
2435
2474
  const state = instanceState2.get(resources.domElement);
2436
2475
  if (!state) return;
2437
2476
  const currentMouse = Mouse.read(ctx);
@@ -2451,6 +2490,7 @@ var mouseSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2451
2490
  case "mousemove":
2452
2491
  mouse.position = [event.clientX - screen.left, event.clientY - screen.top];
2453
2492
  mouse.moveTrigger = true;
2493
+ mouse.obscured = event.target !== resources.domElement;
2454
2494
  break;
2455
2495
  case "wheel":
2456
2496
  mouse.wheelDeltaX = event.deltaX;
@@ -2579,6 +2619,7 @@ function detachPointerListeners(domElement) {
2579
2619
  var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2580
2620
  const resources = getResources5(ctx);
2581
2621
  const { domElement } = resources;
2622
+ if (!domElement) return;
2582
2623
  const state = instanceState3.get(domElement);
2583
2624
  if (!state) return;
2584
2625
  state.frameCount++;
@@ -2658,6 +2699,7 @@ function detachScreenObserver(domElement) {
2658
2699
  var screenSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2659
2700
  const resources = getResources6(ctx);
2660
2701
  const { domElement } = resources;
2702
+ if (!domElement) return;
2661
2703
  const state = instanceState4.get(domElement);
2662
2704
  if (!state) return;
2663
2705
  const frame = Frame.read(ctx);
@@ -2684,6 +2726,7 @@ function getCursorSvg(cursors, kind, rotateZ) {
2684
2726
  return `url("data:image/svg+xml,${encodeURIComponent(svg.trim())}") ${def.hotspot[0]} ${def.hotspot[1]}, auto`;
2685
2727
  }
2686
2728
  var cursorSystem = defineEditorSystem({ phase: "render", priority: -100 }, (ctx) => {
2729
+ if (typeof document === "undefined") return;
2687
2730
  const changedCursors = cursorQuery.changed(ctx);
2688
2731
  const frame = Frame.read(ctx);
2689
2732
  if (changedCursors.length === 0 && frame.number !== 1) {
@@ -2715,6 +2758,9 @@ function getBlockDef(ctx, tag) {
2715
2758
  function canBlockEdit(ctx, tag) {
2716
2759
  return getBlockDef(ctx, tag).editOptions.canEdit;
2717
2760
  }
2761
+ function canBlockSelect(ctx, tag) {
2762
+ return getBlockDef(ctx, tag).selectable;
2763
+ }
2718
2764
  function getBlockResizeMode(ctx, entityId) {
2719
2765
  const block = Block.read(ctx, entityId);
2720
2766
  if (block.resizeMode !== ResizeMode.Default) {
@@ -2737,6 +2783,136 @@ function computeAabb(ctx, entityId, out) {
2737
2783
  }
2738
2784
  }
2739
2785
 
2786
+ // src/helpers/embed.ts
2787
+ function detectEmbedProvider(url) {
2788
+ try {
2789
+ const parsed = new URL(url);
2790
+ const host = parsed.hostname.replace(/^www\./, "");
2791
+ if (host === "youtube.com" || host === "youtu.be") return "youtube" /* Youtube */;
2792
+ if (host === "open.spotify.com" || host === "spotify.com") return "spotify" /* Spotify */;
2793
+ if (host === "google.com" && parsed.pathname.startsWith("/maps")) return "google-maps" /* GoogleMaps */;
2794
+ if (host === "maps.google.com") return "google-maps" /* GoogleMaps */;
2795
+ if (host === "calendar.google.com") return "google-calendar" /* GoogleCalendar */;
2796
+ if (host === "docs.google.com" && parsed.pathname.includes("/presentation")) return "google-slides" /* GoogleSlides */;
2797
+ if (host === "figma.com" || host.endsWith(".figma.com")) return "figma" /* Figma */;
2798
+ if (host === "gist.github.com") return "github-gist" /* GithubGist */;
2799
+ if (host === "tldraw.com" || host.endsWith(".tldraw.com")) return "tldraw" /* Tldraw */;
2800
+ return "unknown" /* Unknown */;
2801
+ } catch {
2802
+ return "unknown" /* Unknown */;
2803
+ }
2804
+ }
2805
+ function validateEmbedUrl(url, provider) {
2806
+ try {
2807
+ const parsed = new URL(url);
2808
+ const host = parsed.hostname.replace(/^www\./, "");
2809
+ switch (provider) {
2810
+ case "youtube" /* Youtube */: {
2811
+ if (host === "youtu.be") return null;
2812
+ if (host !== "youtube.com") return "URL must be from youtube.com";
2813
+ if (parsed.pathname === "/watch" && parsed.searchParams.has("v")) return null;
2814
+ if (parsed.pathname.startsWith("/embed/")) return null;
2815
+ return "Paste a YouTube video link (e.g. youtube.com/watch?v=...)";
2816
+ }
2817
+ case "spotify" /* Spotify */: {
2818
+ if (host !== "open.spotify.com" && host !== "spotify.com") return "URL must be from open.spotify.com";
2819
+ if (parsed.pathname.length <= 1) return "Paste a Spotify track, album, or playlist link";
2820
+ return null;
2821
+ }
2822
+ case "google-maps" /* GoogleMaps */: {
2823
+ if (host !== "google.com" && host !== "maps.google.com") return "URL must be from google.com/maps";
2824
+ if (!parsed.pathname.startsWith("/maps") && host !== "maps.google.com") return "Paste a Google Maps link";
2825
+ return null;
2826
+ }
2827
+ case "google-calendar" /* GoogleCalendar */: {
2828
+ if (host !== "calendar.google.com") return "URL must be from calendar.google.com";
2829
+ return null;
2830
+ }
2831
+ case "google-slides" /* GoogleSlides */: {
2832
+ if (host !== "docs.google.com") return "URL must be from docs.google.com";
2833
+ if (!parsed.pathname.includes("/presentation/")) return "Paste a Google Slides presentation link";
2834
+ return null;
2835
+ }
2836
+ case "figma" /* Figma */: {
2837
+ if (host !== "figma.com" && !host.endsWith(".figma.com")) return "URL must be from figma.com";
2838
+ return null;
2839
+ }
2840
+ case "github-gist" /* GithubGist */: {
2841
+ if (host !== "gist.github.com") return "URL must be from gist.github.com";
2842
+ return null;
2843
+ }
2844
+ case "tldraw" /* Tldraw */: {
2845
+ if (host !== "tldraw.com" && !host.endsWith(".tldraw.com")) return "URL must be from tldraw.com";
2846
+ return null;
2847
+ }
2848
+ default:
2849
+ return null;
2850
+ }
2851
+ } catch {
2852
+ return "Please enter a valid URL";
2853
+ }
2854
+ }
2855
+ function resolveEmbedUrl(url, provider) {
2856
+ const p = provider ?? detectEmbedProvider(url);
2857
+ try {
2858
+ const parsed = new URL(url);
2859
+ switch (p) {
2860
+ case "youtube" /* Youtube */: {
2861
+ const host = parsed.hostname.replace(/^www\./, "");
2862
+ if (host === "youtu.be") {
2863
+ const videoId2 = parsed.pathname.slice(1);
2864
+ return `https://www.youtube.com/embed/${videoId2}`;
2865
+ }
2866
+ const videoId = parsed.searchParams.get("v");
2867
+ if (videoId && parsed.pathname === "/watch") {
2868
+ return `https://www.youtube.com/embed/${videoId}`;
2869
+ }
2870
+ return url;
2871
+ }
2872
+ case "spotify" /* Spotify */: {
2873
+ if (!parsed.pathname.startsWith("/embed/")) {
2874
+ return `https://open.spotify.com/embed${parsed.pathname}`;
2875
+ }
2876
+ return url;
2877
+ }
2878
+ case "google-maps" /* GoogleMaps */: {
2879
+ if (parsed.pathname.startsWith("/maps/embed")) return url;
2880
+ if (parsed.searchParams.get("output") === "embed") return url;
2881
+ const placeMatch = parsed.pathname.match(/\/maps\/place\/([^/]+)/);
2882
+ if (placeMatch) {
2883
+ return `https://www.google.com/maps?q=${encodeURIComponent(decodeURIComponent(placeMatch[1]))}&output=embed`;
2884
+ }
2885
+ const q = parsed.searchParams.get("q");
2886
+ if (q) {
2887
+ return `https://www.google.com/maps?q=${encodeURIComponent(q)}&output=embed`;
2888
+ }
2889
+ const atMatch = parsed.pathname.match(/\/@(-?[\d.]+),(-?[\d.]+),(\d+\.?\d*)z/);
2890
+ if (atMatch) {
2891
+ const [, lat, lng, zoom] = atMatch;
2892
+ return `https://www.google.com/maps?q=${lat},${lng}&z=${Math.round(parseFloat(zoom))}&output=embed`;
2893
+ }
2894
+ return `https://www.google.com/maps?q=${encodeURIComponent(url)}&output=embed`;
2895
+ }
2896
+ case "google-slides" /* GoogleSlides */: {
2897
+ return url.replace(/\/(edit|pub)(#.*)?(\?.*)?$/, "/embed");
2898
+ }
2899
+ case "figma" /* Figma */: {
2900
+ return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
2901
+ }
2902
+ case "github-gist" /* GithubGist */: {
2903
+ if (!url.endsWith(".pibb")) {
2904
+ return `${url}.pibb`;
2905
+ }
2906
+ return url;
2907
+ }
2908
+ default:
2909
+ return url;
2910
+ }
2911
+ } catch {
2912
+ return url;
2913
+ }
2914
+ }
2915
+
2740
2916
  // src/helpers/held.ts
2741
2917
  import { getResources as getResources9, hasComponent as hasComponent2 } from "@woven-ecs/core";
2742
2918
  function isHeldByRemote(ctx, entityId) {
@@ -2923,7 +3099,7 @@ function updateUserPosition(ctx, position) {
2923
3099
  }
2924
3100
 
2925
3101
  // src/systems/preCapture/intersectSystem.ts
2926
- import { addComponent as addComponent4, defineQuery as defineQuery6, hasComponent as hasComponent5, removeComponent } from "@woven-ecs/core";
3102
+ import { addComponent as addComponent4, defineQuery as defineQuery6, getResources as getResources11, hasComponent as hasComponent5, removeComponent } from "@woven-ecs/core";
2927
3103
  var blocksChanged = defineQuery6((q) => q.tracking(Block));
2928
3104
  var hitGeometryChanged = defineQuery6((q) => q.tracking(HitGeometry));
2929
3105
  var heldQuery = defineQuery6((q) => q.with(Held).tracking(Held));
@@ -2962,6 +3138,7 @@ function arraysEqual(a, b) {
2962
3138
  }
2963
3139
  var added = /* @__PURE__ */ new Set();
2964
3140
  var changed = /* @__PURE__ */ new Set();
3141
+ var prevObscuredByElement = /* @__PURE__ */ new WeakMap();
2965
3142
  var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (ctx) => {
2966
3143
  added.clear();
2967
3144
  for (const entityId of blocksChanged.added(ctx)) {
@@ -3003,6 +3180,17 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
3003
3180
  clearHovered(ctx);
3004
3181
  return;
3005
3182
  }
3183
+ const resources = getResources11(ctx);
3184
+ const isObscured = Mouse.isObscured(ctx);
3185
+ const wasObscured = resources.domElement ? prevObscuredByElement.get(resources.domElement) ?? false : false;
3186
+ const obscuredChanged = isObscured !== wasObscured;
3187
+ if (resources.domElement) {
3188
+ prevObscuredByElement.set(resources.domElement, isObscured);
3189
+ }
3190
+ if (isObscured) {
3191
+ clearHovered(ctx);
3192
+ return;
3193
+ }
3006
3194
  const mousePos = Mouse.getPosition(ctx);
3007
3195
  const worldPos = Camera.toWorld(ctx, mousePos);
3008
3196
  const intersected = intersectPoint(ctx, worldPos);
@@ -3015,7 +3203,7 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
3015
3203
  if (pointers.length > 0) {
3016
3204
  return;
3017
3205
  }
3018
- if (intersectsChanged || heldChanged) {
3206
+ if (intersectsChanged || heldChanged || obscuredChanged) {
3019
3207
  updateHovered(ctx, intersected);
3020
3208
  }
3021
3209
  });
@@ -3117,7 +3305,7 @@ function scaleBlock(ctx, entityId, zoom) {
3117
3305
 
3118
3306
  // src/CorePlugin.ts
3119
3307
  var CorePlugin = {
3120
- name: PLUGIN_NAME,
3308
+ name: CORE_PLUGIN_NAME,
3121
3309
  singletons: Object.values(singletons_exports).filter((v) => v instanceof CanvasSingletonDef10),
3122
3310
  components: Object.values(components_exports).filter((v) => v instanceof CanvasComponentDef6),
3123
3311
  blockDefs: [
@@ -3149,6 +3337,14 @@ var CorePlugin = {
3149
3337
  editOptions: {
3150
3338
  canEdit: true
3151
3339
  }
3340
+ },
3341
+ {
3342
+ tag: "embed",
3343
+ components: [Embed],
3344
+ resizeMode: ResizeMode.Free,
3345
+ editOptions: {
3346
+ canEdit: true
3347
+ }
3152
3348
  }
3153
3349
  ],
3154
3350
  systems: [
@@ -3176,14 +3372,16 @@ var CorePlugin = {
3176
3372
  // priority: -100
3177
3373
  ],
3178
3374
  setup(ctx) {
3179
- const { domElement } = getResources11(ctx);
3375
+ const { domElement } = getResources12(ctx);
3376
+ if (!domElement) return;
3180
3377
  attachKeyboardListeners(domElement);
3181
3378
  attachMouseListeners(domElement);
3182
3379
  attachScreenObserver(domElement);
3183
3380
  attachPointerListeners(domElement);
3184
3381
  },
3185
3382
  teardown(ctx) {
3186
- const { domElement } = getResources11(ctx);
3383
+ const { domElement } = getResources12(ctx);
3384
+ if (!domElement) return;
3187
3385
  detachKeyboardListeners(domElement);
3188
3386
  detachMouseListeners(domElement);
3189
3387
  detachScreenObserver(domElement);
@@ -3284,6 +3482,9 @@ var FontLoader = class {
3284
3482
  */
3285
3483
  loadSingleFont(family) {
3286
3484
  this.loadedFonts.add(family.name);
3485
+ if (typeof document === "undefined") {
3486
+ return Promise.resolve();
3487
+ }
3287
3488
  return new Promise((resolve, reject) => {
3288
3489
  const link = document.createElement("link");
3289
3490
  link.rel = "stylesheet";
@@ -3662,11 +3863,54 @@ var Editor = class {
3662
3863
  _getContext() {
3663
3864
  return this.ctx;
3664
3865
  }
3866
+ /**
3867
+ * Attach a DOM element to a headless editor.
3868
+ * Use this after SSR to connect input listeners and enable rendering.
3869
+ *
3870
+ * @param domElement - The DOM element to attach to
3871
+ *
3872
+ * @example
3873
+ * ```typescript
3874
+ * // SSR: create headless editor
3875
+ * const editor = new Editor(null, { plugins: [...] });
3876
+ * await editor.initialize();
3877
+ * editor.tick(); // process initial state
3878
+ *
3879
+ * // Client hydration: attach the DOM
3880
+ * editor.attachDom(document.getElementById('canvas')!);
3881
+ * ```
3882
+ */
3883
+ attachDom(domElement) {
3884
+ const resources = this.ctx.resources;
3885
+ if (resources.domElement) {
3886
+ throw new Error("Editor already has a DOM element attached. Call detachDom() first.");
3887
+ }
3888
+ resources.domElement = domElement;
3889
+ attachKeyboardListeners(domElement);
3890
+ attachMouseListeners(domElement);
3891
+ attachScreenObserver(domElement);
3892
+ attachPointerListeners(domElement);
3893
+ }
3894
+ /**
3895
+ * Detach the current DOM element.
3896
+ * Removes all input listeners. The editor continues to run headless.
3897
+ */
3898
+ detachDom() {
3899
+ const resources = this.ctx.resources;
3900
+ const { domElement } = resources;
3901
+ if (!domElement) return;
3902
+ detachKeyboardListeners(domElement);
3903
+ detachMouseListeners(domElement);
3904
+ detachScreenObserver(domElement);
3905
+ detachPointerListeners(domElement);
3906
+ resources.domElement = null;
3907
+ }
3665
3908
  /**
3666
3909
  * Clean up the editor.
3667
3910
  * Call this when done to release resources.
3668
3911
  */
3669
3912
  dispose() {
3913
+ this.detachDom();
3670
3914
  const plugins = Array.from(this.plugins.values()).reverse();
3671
3915
  for (const plugin of plugins) {
3672
3916
  if (plugin.teardown) {
@@ -3983,6 +4227,8 @@ export {
3983
4227
  Edited,
3984
4228
  Editor,
3985
4229
  EditorStateDef,
4230
+ Embed,
4231
+ EmbedProvider,
3986
4232
  EventType,
3987
4233
  FontFamily,
3988
4234
  FontLoader,
@@ -4028,16 +4274,18 @@ export {
4028
4274
  VerticalAlignment,
4029
4275
  addComponent6 as addComponent,
4030
4276
  canBlockEdit,
4277
+ canBlockSelect,
4031
4278
  clearPointerTrackingState,
4032
4279
  createEntity5 as createEntity,
4033
- defineCanvasComponent13 as defineCanvasComponent,
4280
+ defineCanvasComponent14 as defineCanvasComponent,
4034
4281
  defineCanvasSingleton3 as defineCanvasSingleton,
4035
4282
  defineCommand,
4036
4283
  defineEditorState,
4037
4284
  defineEditorSystem,
4038
4285
  defineQuery11 as defineQuery,
4039
4286
  defineSystem,
4040
- field28 as field,
4287
+ detectEmbedProvider,
4288
+ field29 as field,
4041
4289
  getBackrefs,
4042
4290
  getBlockDef,
4043
4291
  getBlockResizeMode,
@@ -4046,7 +4294,7 @@ export {
4046
4294
  getMouseInput,
4047
4295
  getPluginResources,
4048
4296
  getPointerInput,
4049
- getResources12 as getResources,
4297
+ getResources13 as getResources,
4050
4298
  getTopmostBlockAtPoint,
4051
4299
  hasComponent7 as hasComponent,
4052
4300
  intersectAabb,
@@ -4058,6 +4306,8 @@ export {
4058
4306
  parsePlugin,
4059
4307
  removeComponent2 as removeComponent,
4060
4308
  removeEntity3 as removeEntity,
4061
- runMachine
4309
+ resolveEmbedUrl,
4310
+ runMachine,
4311
+ validateEmbedUrl
4062
4312
  };
4063
4313
  //# sourceMappingURL=index.js.map