@woven-canvas/core 1.0.0 → 1.0.2

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,7 +19,7 @@ import {
19
19
  defineQuery as defineQuery11,
20
20
  defineSystem,
21
21
  EventType,
22
- field as field28,
22
+ field as field29,
23
23
  getBackrefs,
24
24
  getResources as getResources13,
25
25
  hasComponent as hasComponent7,
@@ -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,
@@ -164,7 +166,7 @@ var ControlsOptions = z.object({
164
166
  * Tool activated by right mouse button.
165
167
  * @default 'menu'
166
168
  */
167
- rightMouseTool: z.string().max(32).default("menu"),
169
+ rightMouseTool: z.string().max(32).default("hand"),
168
170
  /**
169
171
  * Tool activated by mouse wheel.
170
172
  * @default 'scroll'
@@ -325,7 +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),
328
- selectable: z.boolean().default(true),
330
+ interactable: z.boolean().default(true),
329
331
  connectors: BlockDefConnectors.default(BlockDefConnectors.parse({}))
330
332
  });
331
333
 
@@ -722,30 +724,57 @@ var Connector = defineCanvasComponent2(
722
724
  import { defineCanvasComponent as defineCanvasComponent3 } from "@woven-ecs/canvas-store";
723
725
  var Edited = defineCanvasComponent3({ name: "edited" }, {});
724
726
 
725
- // src/components/Held.ts
727
+ // src/components/Embed.ts
726
728
  import { defineCanvasComponent as defineCanvasComponent4 } from "@woven-ecs/canvas-store";
727
729
  import { field as field6 } from "@woven-ecs/core";
728
- 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(
729
758
  { name: "held", sync: "ephemeral" },
730
759
  {
731
- sessionId: field6.string().max(36).default("")
760
+ sessionId: field7.string().max(36).default("")
732
761
  }
733
762
  );
734
763
 
735
764
  // src/components/HitGeometry.ts
736
765
  import { Arc, Capsule, Mat2 } from "@woven-canvas/math";
737
766
  import { CanvasComponentDef as CanvasComponentDef4 } from "@woven-ecs/canvas-store";
738
- import { field as field7 } from "@woven-ecs/core";
767
+ import { field as field8 } from "@woven-ecs/core";
739
768
  var _uvToWorldMatrix = [1, 0, 0, 1, 0, 0];
740
769
  var MAX_HIT_CAPSULES = 64;
741
770
  var FLOATS_PER_CAPSULE = 5;
742
771
  var MAX_HIT_ARCS = 2;
743
772
  var FLOATS_PER_ARC = 7;
744
773
  var HitGeometrySchema = {
745
- hitCapsules: field7.buffer(field7.float32()).size(MAX_HIT_CAPSULES * FLOATS_PER_CAPSULE),
746
- capsuleCount: field7.uint16().default(0),
747
- hitArcs: field7.buffer(field7.float32()).size(MAX_HIT_ARCS * FLOATS_PER_ARC),
748
- 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)
749
778
  };
750
779
  var HitGeometryDef = class extends CanvasComponentDef4 {
751
780
  constructor() {
@@ -1051,37 +1080,37 @@ var HitGeometryDef = class extends CanvasComponentDef4 {
1051
1080
  var HitGeometry = new HitGeometryDef();
1052
1081
 
1053
1082
  // src/components/Hovered.ts
1054
- import { defineCanvasComponent as defineCanvasComponent5 } from "@woven-ecs/canvas-store";
1055
- var Hovered = defineCanvasComponent5({ name: "hovered" }, {});
1083
+ import { defineCanvasComponent as defineCanvasComponent6 } from "@woven-ecs/canvas-store";
1084
+ var Hovered = defineCanvasComponent6({ name: "hovered" }, {});
1056
1085
 
1057
1086
  // src/components/Image.ts
1058
- import { defineCanvasComponent as defineCanvasComponent6 } from "@woven-ecs/canvas-store";
1059
- import { field as field8 } from "@woven-ecs/core";
1060
- 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(
1061
1090
  { name: "image", sync: "document" },
1062
1091
  {
1063
1092
  /** Image width in pixels */
1064
- width: field8.uint16().default(0),
1093
+ width: field9.uint16().default(0),
1065
1094
  /** Image height in pixels */
1066
- height: field8.uint16().default(0),
1095
+ height: field9.uint16().default(0),
1067
1096
  /** Alt text for accessibility */
1068
- alt: field8.string().max(256).default("")
1097
+ alt: field9.string().max(256).default("")
1069
1098
  }
1070
1099
  );
1071
1100
 
1072
1101
  // src/components/Opacity.ts
1073
- import { defineCanvasComponent as defineCanvasComponent7 } from "@woven-ecs/canvas-store";
1074
- import { field as field9 } from "@woven-ecs/core";
1075
- 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(
1076
1105
  { name: "opacity" },
1077
1106
  {
1078
- value: field9.uint8().default(255)
1107
+ value: field10.uint8().default(255)
1079
1108
  }
1080
1109
  );
1081
1110
 
1082
1111
  // src/components/Pointer.ts
1083
1112
  import { CanvasComponentDef as CanvasComponentDef5 } from "@woven-ecs/canvas-store";
1084
- import { field as field10 } from "@woven-ecs/core";
1113
+ import { field as field11 } from "@woven-ecs/core";
1085
1114
  var PointerButton = {
1086
1115
  None: "none",
1087
1116
  Left: "left",
@@ -1098,30 +1127,30 @@ var PointerType = {
1098
1127
  var SAMPLE_COUNT = 6;
1099
1128
  var PointerSchema = {
1100
1129
  /** Unique pointer ID (from PointerEvent.pointerId) */
1101
- pointerId: field10.uint16().default(0),
1130
+ pointerId: field11.uint16().default(0),
1102
1131
  /** Current position relative to the editor element [x, y] */
1103
- position: field10.tuple(field10.float32(), 2).default([0, 0]),
1132
+ position: field11.tuple(field11.float32(), 2).default([0, 0]),
1104
1133
  /** Position where the pointer went down [x, y] */
1105
- downPosition: field10.tuple(field10.float32(), 2).default([0, 0]),
1134
+ downPosition: field11.tuple(field11.float32(), 2).default([0, 0]),
1106
1135
  /** Frame number when the pointer went down (for click detection) */
1107
- downFrame: field10.uint32().default(0),
1136
+ downFrame: field11.uint32().default(0),
1108
1137
  /** Which button is pressed */
1109
- button: field10.enum(PointerButton).default(PointerButton.None),
1138
+ button: field11.enum(PointerButton).default(PointerButton.None),
1110
1139
  /** Type of pointer device */
1111
- pointerType: field10.enum(PointerType).default(PointerType.Mouse),
1140
+ pointerType: field11.enum(PointerType).default(PointerType.Mouse),
1112
1141
  /** Pressure from 0 to 1 (for pen/touch) */
1113
- pressure: field10.float32().default(0),
1142
+ pressure: field11.float32().default(0),
1114
1143
  /** Whether the pointer event target was not the editor element */
1115
- obscured: field10.boolean().default(false),
1144
+ obscured: field11.boolean().default(false),
1116
1145
  // Velocity tracking (ring buffer for position samples)
1117
1146
  /** Ring buffer of previous positions [x0, y0, x1, y1, ...] @internal */
1118
- _prevPositions: field10.array(field10.float32(), SAMPLE_COUNT * 2),
1147
+ _prevPositions: field11.array(field11.float32(), SAMPLE_COUNT * 2),
1119
1148
  /** Ring buffer of timestamps for each position sample @internal */
1120
- _prevTimes: field10.array(field10.float32(), SAMPLE_COUNT),
1149
+ _prevTimes: field11.array(field11.float32(), SAMPLE_COUNT),
1121
1150
  /** Total number of samples added (used for ring buffer indexing) @internal */
1122
- _sampleCount: field10.int32().default(0),
1151
+ _sampleCount: field11.int32().default(0),
1123
1152
  /** Computed velocity [vx, vy] in pixels per second @internal */
1124
- _velocity: field10.tuple(field10.float32(), 2).default([0, 0])
1153
+ _velocity: field11.tuple(field11.float32(), 2).default([0, 0])
1125
1154
  };
1126
1155
  var PointerDef = class extends CanvasComponentDef5 {
1127
1156
  constructor() {
@@ -1232,103 +1261,103 @@ function addPointerSample(pointer, position, time) {
1232
1261
  }
1233
1262
 
1234
1263
  // src/components/ScaleWithZoom.ts
1235
- import { defineCanvasComponent as defineCanvasComponent8 } from "@woven-ecs/canvas-store";
1236
- import { field as field11 } from "@woven-ecs/core";
1237
- 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(
1238
1267
  { name: "scaleWithZoom" },
1239
1268
  {
1240
1269
  /** Pivot point for scaling as [x, y] (0-1, default 0.5,0.5 = center) */
1241
- anchor: field11.tuple(field11.float64(), 2).default([0.5, 0.5]),
1270
+ anchor: field12.tuple(field12.float64(), 2).default([0.5, 0.5]),
1242
1271
  /** Initial position as [left, top] at zoom=1 */
1243
- startPosition: field11.tuple(field11.float64(), 2).default([0, 0]),
1272
+ startPosition: field12.tuple(field12.float64(), 2).default([0, 0]),
1244
1273
  /** Initial size as [width, height] at zoom=1 */
1245
- startSize: field11.tuple(field11.float64(), 2).default([0, 0]),
1274
+ startSize: field12.tuple(field12.float64(), 2).default([0, 0]),
1246
1275
  /** Scale multiplier per dimension: [x, y] (0 = no zoom effect, 1 = full zoom effect, 0.5 = half effect) */
1247
- scaleMultiplier: field11.tuple(field11.float64(), 2).default([1, 1])
1276
+ scaleMultiplier: field12.tuple(field12.float64(), 2).default([1, 1])
1248
1277
  }
1249
1278
  );
1250
1279
 
1251
1280
  // src/components/Shape.ts
1252
- import { defineCanvasComponent as defineCanvasComponent9 } from "@woven-ecs/canvas-store";
1253
- 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";
1254
1283
  var StrokeKind = {
1255
1284
  Solid: "solid",
1256
1285
  Dashed: "dashed",
1257
1286
  None: "none"
1258
1287
  };
1259
- var Shape = defineCanvasComponent9(
1288
+ var Shape = defineCanvasComponent10(
1260
1289
  { name: "shape", sync: "document" },
1261
1290
  {
1262
1291
  /** The kind of shape to render (e.g. 'rectangle', 'ellipse', or custom shape key) */
1263
- kind: field12.string().default("rectangle"),
1292
+ kind: field13.string().default("rectangle"),
1264
1293
  /** Stroke style */
1265
- strokeKind: field12.enum(StrokeKind).default(StrokeKind.Solid),
1294
+ strokeKind: field13.enum(StrokeKind).default(StrokeKind.Solid),
1266
1295
  /** Stroke width in pixels */
1267
- strokeWidth: field12.uint16().default(2),
1296
+ strokeWidth: field13.uint16().default(2),
1268
1297
  /** Stroke color - red component (0-255) */
1269
- strokeRed: field12.uint8().default(0),
1298
+ strokeRed: field13.uint8().default(0),
1270
1299
  /** Stroke color - green component (0-255) */
1271
- strokeGreen: field12.uint8().default(0),
1300
+ strokeGreen: field13.uint8().default(0),
1272
1301
  /** Stroke color - blue component (0-255) */
1273
- strokeBlue: field12.uint8().default(0),
1302
+ strokeBlue: field13.uint8().default(0),
1274
1303
  /** Stroke color - alpha component (0-255) */
1275
- strokeAlpha: field12.uint8().default(255),
1304
+ strokeAlpha: field13.uint8().default(255),
1276
1305
  /** Fill color - red component (0-255) */
1277
- fillRed: field12.uint8().default(255),
1306
+ fillRed: field13.uint8().default(255),
1278
1307
  /** Fill color - green component (0-255) */
1279
- fillGreen: field12.uint8().default(255),
1308
+ fillGreen: field13.uint8().default(255),
1280
1309
  /** Fill color - blue component (0-255) */
1281
- fillBlue: field12.uint8().default(255),
1310
+ fillBlue: field13.uint8().default(255),
1282
1311
  /** Fill color - alpha component (0-255) */
1283
- fillAlpha: field12.uint8().default(0)
1312
+ fillAlpha: field13.uint8().default(0)
1284
1313
  }
1285
1314
  );
1286
1315
 
1287
1316
  // src/components/Text.ts
1288
- import { defineCanvasComponent as defineCanvasComponent10 } from "@woven-ecs/canvas-store";
1289
- import { field as field13 } from "@woven-ecs/core";
1290
- 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(
1291
1320
  { name: "text", sync: "document" },
1292
1321
  {
1293
1322
  /** HTML content (supports rich text formatting) */
1294
- content: field13.string().max(1e4).default(""),
1323
+ content: field14.string().max(1e4).default(""),
1295
1324
  /** Font size in pixels */
1296
- fontSizePx: field13.float64().default(24),
1325
+ fontSizePx: field14.float64().default(24),
1297
1326
  /** Font family name */
1298
- fontFamily: field13.string().max(64).default("Figtree"),
1327
+ fontFamily: field14.string().max(64).default("Figtree"),
1299
1328
  /** Line height multiplier */
1300
- lineHeight: field13.float64().default(1.2),
1329
+ lineHeight: field14.float64().default(1.2),
1301
1330
  /** Letter spacing in em units */
1302
- letterSpacingEm: field13.float64().default(0),
1331
+ letterSpacingEm: field14.float64().default(0),
1303
1332
  /** Whether width is constrained (text wraps) */
1304
- constrainWidth: field13.boolean().default(true),
1333
+ constrainWidth: field14.boolean().default(true),
1305
1334
  /** Default text alignment for new paragraphs */
1306
- defaultAlignment: field13.enum(TextAlignment).default(TextAlignment.Left)
1335
+ defaultAlignment: field14.enum(TextAlignment).default(TextAlignment.Left)
1307
1336
  }
1308
1337
  );
1309
1338
 
1310
1339
  // src/components/User.ts
1311
- import { defineCanvasComponent as defineCanvasComponent11 } from "@woven-ecs/canvas-store";
1312
- import { field as field14 } from "@woven-ecs/core";
1313
- 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(
1314
1343
  { name: "user", sync: "ephemeral" },
1315
1344
  {
1316
- userId: field14.string().max(36),
1317
- sessionId: field14.string().max(36),
1318
- color: field14.string().max(7),
1319
- name: field14.string().max(100),
1320
- avatar: field14.string().max(500),
1321
- 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])
1322
1351
  }
1323
1352
  );
1324
1353
 
1325
1354
  // src/components/VerticalAlign.ts
1326
- import { defineCanvasComponent as defineCanvasComponent12 } from "@woven-ecs/canvas-store";
1327
- import { field as field15 } from "@woven-ecs/core";
1328
- 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(
1329
1358
  { name: "verticalAlign", sync: "document" },
1330
1359
  {
1331
- value: field15.enum(VerticalAlignment).default(VerticalAlignment.Top)
1360
+ value: field16.enum(VerticalAlignment).default(VerticalAlignment.Top)
1332
1361
  }
1333
1362
  );
1334
1363
 
@@ -1362,20 +1391,20 @@ __export(singletons_exports, {
1362
1391
 
1363
1392
  // src/singletons/Camera.ts
1364
1393
  import { CanvasSingletonDef as CanvasSingletonDef2 } from "@woven-ecs/canvas-store";
1365
- import { field as field17 } from "@woven-ecs/core";
1394
+ import { field as field18 } from "@woven-ecs/core";
1366
1395
 
1367
1396
  // src/singletons/Screen.ts
1368
1397
  import { CanvasSingletonDef } from "@woven-ecs/canvas-store";
1369
- import { field as field16 } from "@woven-ecs/core";
1398
+ import { field as field17 } from "@woven-ecs/core";
1370
1399
  var ScreenSchema = {
1371
1400
  /** Width of the editor element in pixels */
1372
- width: field16.float64().default(0),
1401
+ width: field17.float64().default(0),
1373
1402
  /** Height of the editor element in pixels */
1374
- height: field16.float64().default(0),
1403
+ height: field17.float64().default(0),
1375
1404
  /** Left offset of the editor element relative to the viewport */
1376
- left: field16.float64().default(0),
1405
+ left: field17.float64().default(0),
1377
1406
  /** Top offset of the editor element relative to the viewport */
1378
- top: field16.float64().default(0)
1407
+ top: field17.float64().default(0)
1379
1408
  };
1380
1409
  var ScreenDef = class extends CanvasSingletonDef {
1381
1410
  constructor() {
@@ -1402,15 +1431,15 @@ var Screen = new ScreenDef();
1402
1431
  // src/singletons/Camera.ts
1403
1432
  var CameraSchema = {
1404
1433
  /** Top position of the camera in world coordinates */
1405
- top: field17.float64().default(0),
1434
+ top: field18.float64().default(0),
1406
1435
  /** Left position of the camera in world coordinates */
1407
- left: field17.float64().default(0),
1436
+ left: field18.float64().default(0),
1408
1437
  /** Zoom level (1 = 100%, 2 = 200%, 0.5 = 50%) */
1409
- zoom: field17.float64().default(1),
1438
+ zoom: field18.float64().default(1),
1410
1439
  /** Whether the camera viewport intersects any blocks */
1411
- canSeeBlocks: field17.boolean().default(true),
1440
+ canSeeBlocks: field18.boolean().default(true),
1412
1441
  /** Reference to a block that the camera can currently see (for optimization) */
1413
- lastSeenBlock: field17.ref()
1442
+ lastSeenBlock: field18.ref()
1414
1443
  };
1415
1444
  var CameraDef = class extends CanvasSingletonDef2 {
1416
1445
  constructor() {
@@ -1486,20 +1515,20 @@ var Camera = new CameraDef();
1486
1515
 
1487
1516
  // src/singletons/Controls.ts
1488
1517
  import { CanvasSingletonDef as CanvasSingletonDef3 } from "@woven-ecs/canvas-store";
1489
- import { field as field18 } from "@woven-ecs/core";
1518
+ import { field as field19 } from "@woven-ecs/core";
1490
1519
  var ControlsSchema = {
1491
1520
  /** Tool activated by left mouse button */
1492
- leftMouseTool: field18.string().max(32).default("select"),
1521
+ leftMouseTool: field19.string().max(32).default("select"),
1493
1522
  /** Tool activated by middle mouse button */
1494
- middleMouseTool: field18.string().max(32).default("hand"),
1523
+ middleMouseTool: field19.string().max(32).default("hand"),
1495
1524
  /** Tool activated by right mouse button */
1496
- rightMouseTool: field18.string().max(32).default("menu"),
1525
+ rightMouseTool: field19.string().max(32).default("hand"),
1497
1526
  /** Tool activated by mouse wheel */
1498
- wheelTool: field18.string().max(32).default("scroll"),
1527
+ wheelTool: field19.string().max(32).default("scroll"),
1499
1528
  /** Tool activated by mouse wheel with modifier key held */
1500
- modWheelTool: field18.string().max(32).default("zoom"),
1529
+ modWheelTool: field19.string().max(32).default("zoom"),
1501
1530
  /** JSON snapshot of block to place on next click (empty string = no placement active) */
1502
- heldSnapshot: field18.string().max(65536).default("")
1531
+ heldSnapshot: field19.string().max(65536).default("")
1503
1532
  };
1504
1533
  var ControlsDef = class extends CanvasSingletonDef3 {
1505
1534
  constructor() {
@@ -1541,16 +1570,16 @@ var Controls = new ControlsDef();
1541
1570
 
1542
1571
  // src/singletons/Cursor.ts
1543
1572
  import { CanvasSingletonDef as CanvasSingletonDef4 } from "@woven-ecs/canvas-store";
1544
- import { field as field19 } from "@woven-ecs/core";
1573
+ import { field as field20 } from "@woven-ecs/core";
1545
1574
  var CursorSchema = {
1546
1575
  /** Base cursor kind (from current tool) */
1547
- cursorKind: field19.string().max(64).default("select"),
1576
+ cursorKind: field20.string().max(64).default("select"),
1548
1577
  /** Base cursor rotation in radians */
1549
- rotation: field19.float64().default(0),
1578
+ rotation: field20.float64().default(0),
1550
1579
  /** Context-specific cursor kind (overrides cursorKind when set, e.g., during drag/hover) */
1551
- contextCursorKind: field19.string().max(64).default(""),
1580
+ contextCursorKind: field20.string().max(64).default(""),
1552
1581
  /** Context cursor rotation in radians */
1553
- contextRotation: field19.float64().default(0)
1582
+ contextRotation: field20.float64().default(0)
1554
1583
  };
1555
1584
  var CursorDef2 = class extends CanvasSingletonDef4 {
1556
1585
  constructor() {
@@ -1598,37 +1627,37 @@ var Cursor = new CursorDef2();
1598
1627
 
1599
1628
  // src/singletons/Frame.ts
1600
1629
  import { defineCanvasSingleton } from "@woven-ecs/canvas-store";
1601
- import { field as field20 } from "@woven-ecs/core";
1630
+ import { field as field21 } from "@woven-ecs/core";
1602
1631
  var Frame = defineCanvasSingleton(
1603
1632
  { name: "frame" },
1604
1633
  {
1605
1634
  /** Current frame number (increments each tick) */
1606
- number: field20.uint32().default(0),
1635
+ number: field21.uint32().default(0),
1607
1636
  /** Time since last frame in seconds */
1608
- delta: field20.float64().default(0),
1637
+ delta: field21.float64().default(0),
1609
1638
  /** Timestamp of current frame in milliseconds (from performance.now()) */
1610
- time: field20.float64().default(0),
1639
+ time: field21.float64().default(0),
1611
1640
  /** Timestamp of previous frame in milliseconds (0 if first frame) */
1612
- lastTime: field20.float64().default(0)
1641
+ lastTime: field21.float64().default(0)
1613
1642
  }
1614
1643
  );
1615
1644
 
1616
1645
  // src/singletons/Grid.ts
1617
1646
  import { CanvasSingletonDef as CanvasSingletonDef5 } from "@woven-ecs/canvas-store";
1618
- import { field as field21 } from "@woven-ecs/core";
1647
+ import { field as field22 } from "@woven-ecs/core";
1619
1648
  var GridSchema = {
1620
1649
  /** Whether grid snapping is enabled */
1621
- enabled: field21.boolean().default(false),
1650
+ enabled: field22.boolean().default(false),
1622
1651
  /** Whether resized/rotated objects must stay aligned to the grid */
1623
- strict: field21.boolean().default(false),
1652
+ strict: field22.boolean().default(false),
1624
1653
  /** Width of each grid column in world units */
1625
- colWidth: field21.float64().default(20),
1654
+ colWidth: field22.float64().default(20),
1626
1655
  /** Height of each grid row in world units */
1627
- rowHeight: field21.float64().default(20),
1656
+ rowHeight: field22.float64().default(20),
1628
1657
  /** Angular snap increment in radians when grid is enabled */
1629
- snapAngleRad: field21.float64().default(Math.PI / 36),
1658
+ snapAngleRad: field22.float64().default(Math.PI / 36),
1630
1659
  /** Angular snap increment in radians when shift key is held */
1631
- shiftSnapAngleRad: field21.float64().default(Math.PI / 12)
1660
+ shiftSnapAngleRad: field22.float64().default(Math.PI / 12)
1632
1661
  };
1633
1662
  var GridDef = class extends CanvasSingletonDef5 {
1634
1663
  constructor() {
@@ -1691,14 +1720,14 @@ var Grid = new GridDef();
1691
1720
 
1692
1721
  // src/singletons/Intersect.ts
1693
1722
  import { CanvasSingletonDef as CanvasSingletonDef6 } from "@woven-ecs/canvas-store";
1694
- import { field as field22 } from "@woven-ecs/core";
1723
+ import { field as field23 } from "@woven-ecs/core";
1695
1724
  var IntersectSchema = {
1696
1725
  // Store up to 5 intersected entity IDs
1697
- entity1: field22.ref(),
1698
- entity2: field22.ref(),
1699
- entity3: field22.ref(),
1700
- entity4: field22.ref(),
1701
- entity5: field22.ref()
1726
+ entity1: field23.ref(),
1727
+ entity2: field23.ref(),
1728
+ entity3: field23.ref(),
1729
+ entity4: field23.ref(),
1730
+ entity5: field23.ref()
1702
1731
  };
1703
1732
  var IntersectDef = class extends CanvasSingletonDef6 {
1704
1733
  constructor() {
@@ -1750,30 +1779,30 @@ var Intersect = new IntersectDef();
1750
1779
 
1751
1780
  // src/singletons/Keyboard.ts
1752
1781
  import { CanvasSingletonDef as CanvasSingletonDef7 } from "@woven-ecs/canvas-store";
1753
- import { field as field23 } from "@woven-ecs/core";
1782
+ import { field as field24 } from "@woven-ecs/core";
1754
1783
  var KEY_BUFFER_SIZE = 32;
1755
1784
  var KeyboardSchema = {
1756
1785
  /**
1757
1786
  * Buffer where each bit represents whether a key is currently pressed.
1758
1787
  * Uses field.buffer for zero-allocation subarray views.
1759
1788
  */
1760
- keysDown: field23.buffer(field23.uint8()).size(KEY_BUFFER_SIZE),
1789
+ keysDown: field24.buffer(field24.uint8()).size(KEY_BUFFER_SIZE),
1761
1790
  /**
1762
1791
  * Buffer for key-down triggers (true for exactly 1 frame when key is pressed).
1763
1792
  * Uses field.buffer for zero-allocation subarray views.
1764
1793
  */
1765
- keysDownTrigger: field23.buffer(field23.uint8()).size(KEY_BUFFER_SIZE),
1794
+ keysDownTrigger: field24.buffer(field24.uint8()).size(KEY_BUFFER_SIZE),
1766
1795
  /**
1767
1796
  * Buffer for key-up triggers (true for exactly 1 frame when key is released).
1768
1797
  * Uses field.buffer for zero-allocation subarray views.
1769
1798
  */
1770
- keysUpTrigger: field23.buffer(field23.uint8()).size(KEY_BUFFER_SIZE),
1799
+ keysUpTrigger: field24.buffer(field24.uint8()).size(KEY_BUFFER_SIZE),
1771
1800
  /** Common modifier - Shift key is down */
1772
- shiftDown: field23.boolean().default(false),
1801
+ shiftDown: field24.boolean().default(false),
1773
1802
  /** Common modifier - Alt/Option key is down */
1774
- altDown: field23.boolean().default(false),
1803
+ altDown: field24.boolean().default(false),
1775
1804
  /** Common modifier - Ctrl (Windows/Linux) or Cmd (Mac) is down */
1776
- modDown: field23.boolean().default(false)
1805
+ modDown: field24.boolean().default(false)
1777
1806
  };
1778
1807
  function getBit(buffer, bitIndex) {
1779
1808
  if (bitIndex < 0 || bitIndex >= buffer.length * 8) return false;
@@ -2056,24 +2085,24 @@ var Key = {
2056
2085
 
2057
2086
  // src/singletons/Mouse.ts
2058
2087
  import { CanvasSingletonDef as CanvasSingletonDef8 } from "@woven-ecs/canvas-store";
2059
- import { field as field24 } from "@woven-ecs/core";
2088
+ import { field as field25 } from "@woven-ecs/core";
2060
2089
  var MouseSchema = {
2061
2090
  /** Current mouse position relative to the editor element [x, y] */
2062
- position: field24.tuple(field24.float32(), 2).default([0, 0]),
2091
+ position: field25.tuple(field25.float32(), 2).default([0, 0]),
2063
2092
  /** Horizontal wheel delta (positive = scroll right) */
2064
- wheelDeltaX: field24.float32().default(0),
2093
+ wheelDeltaX: field25.float32().default(0),
2065
2094
  /** Vertical wheel delta (positive = scroll down), normalized across browsers */
2066
- wheelDeltaY: field24.float32().default(0),
2095
+ wheelDeltaY: field25.float32().default(0),
2067
2096
  /** True for 1 frame when mouse moves */
2068
- moveTrigger: field24.boolean().default(false),
2097
+ moveTrigger: field25.boolean().default(false),
2069
2098
  /** True for 1 frame when wheel is scrolled */
2070
- wheelTrigger: field24.boolean().default(false),
2099
+ wheelTrigger: field25.boolean().default(false),
2071
2100
  /** True for 1 frame when mouse enters the editor element */
2072
- enterTrigger: field24.boolean().default(false),
2101
+ enterTrigger: field25.boolean().default(false),
2073
2102
  /** True for 1 frame when mouse leaves the editor element */
2074
- leaveTrigger: field24.boolean().default(false),
2103
+ leaveTrigger: field25.boolean().default(false),
2075
2104
  /** Whether the mouse is over a UI element (not the canvas) */
2076
- obscured: field24.boolean().default(false)
2105
+ obscured: field25.boolean().default(false)
2077
2106
  };
2078
2107
  var MouseDef = class extends CanvasSingletonDef8 {
2079
2108
  constructor() {
@@ -2114,11 +2143,11 @@ var Mouse = new MouseDef();
2114
2143
 
2115
2144
  // src/singletons/RankBounds.ts
2116
2145
  import { CanvasSingletonDef as CanvasSingletonDef9 } from "@woven-ecs/canvas-store";
2117
- import { field as field25 } from "@woven-ecs/core";
2146
+ import { field as field26 } from "@woven-ecs/core";
2118
2147
  import { generateJitteredKeyBetween } from "fractional-indexing-jittered";
2119
2148
  var RankBoundsSchema = {
2120
- minRank: field25.string().max(36).default(""),
2121
- maxRank: field25.string().max(36).default("")
2149
+ minRank: field26.string().max(36).default(""),
2150
+ maxRank: field26.string().max(36).default("")
2122
2151
  };
2123
2152
  var RankBoundsDef = class extends CanvasSingletonDef9 {
2124
2153
  constructor() {
@@ -2177,12 +2206,12 @@ var RankBounds = new RankBoundsDef();
2177
2206
 
2178
2207
  // src/singletons/ScaleWithZoomState.ts
2179
2208
  import { defineCanvasSingleton as defineCanvasSingleton2 } from "@woven-ecs/canvas-store";
2180
- import { field as field26 } from "@woven-ecs/core";
2209
+ import { field as field27 } from "@woven-ecs/core";
2181
2210
  var ScaleWithZoomState = defineCanvasSingleton2(
2182
2211
  { name: "scaleWithZoomState" },
2183
2212
  {
2184
2213
  /** Last processed zoom level */
2185
- lastZoom: field26.float64().default(1)
2214
+ lastZoom: field27.float64().default(1)
2186
2215
  }
2187
2216
  );
2188
2217
 
@@ -2195,12 +2224,12 @@ import {
2195
2224
  createEntity,
2196
2225
  defineComponent,
2197
2226
  defineQuery,
2198
- field as field27,
2227
+ field as field28,
2199
2228
  getResources,
2200
2229
  removeEntity
2201
2230
  } from "@woven-ecs/core";
2202
2231
  var CommandMarker = defineComponent({
2203
- name: field27.string().max(128)
2232
+ name: field28.string().max(128)
2204
2233
  });
2205
2234
  var editorPayloads = /* @__PURE__ */ new WeakMap();
2206
2235
  function getPayloadMap(ctx) {
@@ -2339,6 +2368,7 @@ function detachKeyboardListeners(domElement) {
2339
2368
  }
2340
2369
  var keyboardSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2341
2370
  const resources = getResources3(ctx);
2371
+ if (!resources.domElement) return;
2342
2372
  const state = instanceState.get(resources.domElement);
2343
2373
  if (!state) return;
2344
2374
  on(ctx, ResetKeyboard, () => {
@@ -2440,6 +2470,7 @@ function detachMouseListeners(domElement) {
2440
2470
  }
2441
2471
  var mouseSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2442
2472
  const resources = getResources4(ctx);
2473
+ if (!resources.domElement) return;
2443
2474
  const state = instanceState2.get(resources.domElement);
2444
2475
  if (!state) return;
2445
2476
  const currentMouse = Mouse.read(ctx);
@@ -2516,9 +2547,12 @@ function attachPointerListeners(domElement) {
2516
2547
  });
2517
2548
  },
2518
2549
  onContextMenu: (e) => {
2550
+ if (e.target instanceof HTMLElement && e.target.closest('[contenteditable="true"]')) return;
2519
2551
  e.preventDefault();
2520
- const hasPointerDown = state.eventsBuffer.some((evt) => evt.type === "pointerdown");
2521
- if (hasPointerDown) return;
2552
+ const hasRightButtonEvent = state.eventsBuffer.some(
2553
+ (evt) => evt.button === 2 && (evt.type === "pointerdown" || evt.type === "pointerup")
2554
+ );
2555
+ if (hasRightButtonEvent) return;
2522
2556
  state.eventsBuffer.push({
2523
2557
  type: "pointerdown",
2524
2558
  pointerId: 0,
@@ -2588,6 +2622,7 @@ function detachPointerListeners(domElement) {
2588
2622
  var pointerSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2589
2623
  const resources = getResources5(ctx);
2590
2624
  const { domElement } = resources;
2625
+ if (!domElement) return;
2591
2626
  const state = instanceState3.get(domElement);
2592
2627
  if (!state) return;
2593
2628
  state.frameCount++;
@@ -2667,6 +2702,7 @@ function detachScreenObserver(domElement) {
2667
2702
  var screenSystem = defineEditorSystem({ phase: "input" }, (ctx) => {
2668
2703
  const resources = getResources6(ctx);
2669
2704
  const { domElement } = resources;
2705
+ if (!domElement) return;
2670
2706
  const state = instanceState4.get(domElement);
2671
2707
  if (!state) return;
2672
2708
  const frame = Frame.read(ctx);
@@ -2693,6 +2729,7 @@ function getCursorSvg(cursors, kind, rotateZ) {
2693
2729
  return `url("data:image/svg+xml,${encodeURIComponent(svg.trim())}") ${def.hotspot[0]} ${def.hotspot[1]}, auto`;
2694
2730
  }
2695
2731
  var cursorSystem = defineEditorSystem({ phase: "render", priority: -100 }, (ctx) => {
2732
+ if (typeof document === "undefined") return;
2696
2733
  const changedCursors = cursorQuery.changed(ctx);
2697
2734
  const frame = Frame.read(ctx);
2698
2735
  if (changedCursors.length === 0 && frame.number !== 1) {
@@ -2724,8 +2761,8 @@ function getBlockDef(ctx, tag) {
2724
2761
  function canBlockEdit(ctx, tag) {
2725
2762
  return getBlockDef(ctx, tag).editOptions.canEdit;
2726
2763
  }
2727
- function canBlockSelect(ctx, tag) {
2728
- return getBlockDef(ctx, tag).selectable;
2764
+ function canBlockInteract(ctx, tag) {
2765
+ return getBlockDef(ctx, tag).interactable;
2729
2766
  }
2730
2767
  function getBlockResizeMode(ctx, entityId) {
2731
2768
  const block = Block.read(ctx, entityId);
@@ -2749,6 +2786,136 @@ function computeAabb(ctx, entityId, out) {
2749
2786
  }
2750
2787
  }
2751
2788
 
2789
+ // src/helpers/embed.ts
2790
+ function detectEmbedProvider(url) {
2791
+ try {
2792
+ const parsed = new URL(url);
2793
+ const host = parsed.hostname.replace(/^www\./, "");
2794
+ if (host === "youtube.com" || host === "youtu.be") return "youtube" /* Youtube */;
2795
+ if (host === "open.spotify.com" || host === "spotify.com") return "spotify" /* Spotify */;
2796
+ if (host === "google.com" && parsed.pathname.startsWith("/maps")) return "google-maps" /* GoogleMaps */;
2797
+ if (host === "maps.google.com") return "google-maps" /* GoogleMaps */;
2798
+ if (host === "calendar.google.com") return "google-calendar" /* GoogleCalendar */;
2799
+ if (host === "docs.google.com" && parsed.pathname.includes("/presentation")) return "google-slides" /* GoogleSlides */;
2800
+ if (host === "figma.com" || host.endsWith(".figma.com")) return "figma" /* Figma */;
2801
+ if (host === "gist.github.com") return "github-gist" /* GithubGist */;
2802
+ if (host === "tldraw.com" || host.endsWith(".tldraw.com")) return "tldraw" /* Tldraw */;
2803
+ return "unknown" /* Unknown */;
2804
+ } catch {
2805
+ return "unknown" /* Unknown */;
2806
+ }
2807
+ }
2808
+ function validateEmbedUrl(url, provider) {
2809
+ try {
2810
+ const parsed = new URL(url);
2811
+ const host = parsed.hostname.replace(/^www\./, "");
2812
+ switch (provider) {
2813
+ case "youtube" /* Youtube */: {
2814
+ if (host === "youtu.be") return null;
2815
+ if (host !== "youtube.com") return "URL must be from youtube.com";
2816
+ if (parsed.pathname === "/watch" && parsed.searchParams.has("v")) return null;
2817
+ if (parsed.pathname.startsWith("/embed/")) return null;
2818
+ return "Paste a YouTube video link (e.g. youtube.com/watch?v=...)";
2819
+ }
2820
+ case "spotify" /* Spotify */: {
2821
+ if (host !== "open.spotify.com" && host !== "spotify.com") return "URL must be from open.spotify.com";
2822
+ if (parsed.pathname.length <= 1) return "Paste a Spotify track, album, or playlist link";
2823
+ return null;
2824
+ }
2825
+ case "google-maps" /* GoogleMaps */: {
2826
+ if (host !== "google.com" && host !== "maps.google.com") return "URL must be from google.com/maps";
2827
+ if (!parsed.pathname.startsWith("/maps") && host !== "maps.google.com") return "Paste a Google Maps link";
2828
+ return null;
2829
+ }
2830
+ case "google-calendar" /* GoogleCalendar */: {
2831
+ if (host !== "calendar.google.com") return "URL must be from calendar.google.com";
2832
+ return null;
2833
+ }
2834
+ case "google-slides" /* GoogleSlides */: {
2835
+ if (host !== "docs.google.com") return "URL must be from docs.google.com";
2836
+ if (!parsed.pathname.includes("/presentation/")) return "Paste a Google Slides presentation link";
2837
+ return null;
2838
+ }
2839
+ case "figma" /* Figma */: {
2840
+ if (host !== "figma.com" && !host.endsWith(".figma.com")) return "URL must be from figma.com";
2841
+ return null;
2842
+ }
2843
+ case "github-gist" /* GithubGist */: {
2844
+ if (host !== "gist.github.com") return "URL must be from gist.github.com";
2845
+ return null;
2846
+ }
2847
+ case "tldraw" /* Tldraw */: {
2848
+ if (host !== "tldraw.com" && !host.endsWith(".tldraw.com")) return "URL must be from tldraw.com";
2849
+ return null;
2850
+ }
2851
+ default:
2852
+ return null;
2853
+ }
2854
+ } catch {
2855
+ return "Please enter a valid URL";
2856
+ }
2857
+ }
2858
+ function resolveEmbedUrl(url, provider) {
2859
+ const p = provider ?? detectEmbedProvider(url);
2860
+ try {
2861
+ const parsed = new URL(url);
2862
+ switch (p) {
2863
+ case "youtube" /* Youtube */: {
2864
+ const host = parsed.hostname.replace(/^www\./, "");
2865
+ if (host === "youtu.be") {
2866
+ const videoId2 = parsed.pathname.slice(1);
2867
+ return `https://www.youtube.com/embed/${videoId2}`;
2868
+ }
2869
+ const videoId = parsed.searchParams.get("v");
2870
+ if (videoId && parsed.pathname === "/watch") {
2871
+ return `https://www.youtube.com/embed/${videoId}`;
2872
+ }
2873
+ return url;
2874
+ }
2875
+ case "spotify" /* Spotify */: {
2876
+ if (!parsed.pathname.startsWith("/embed/")) {
2877
+ return `https://open.spotify.com/embed${parsed.pathname}`;
2878
+ }
2879
+ return url;
2880
+ }
2881
+ case "google-maps" /* GoogleMaps */: {
2882
+ if (parsed.pathname.startsWith("/maps/embed")) return url;
2883
+ if (parsed.searchParams.get("output") === "embed") return url;
2884
+ const placeMatch = parsed.pathname.match(/\/maps\/place\/([^/]+)/);
2885
+ if (placeMatch) {
2886
+ return `https://www.google.com/maps?q=${encodeURIComponent(decodeURIComponent(placeMatch[1]))}&output=embed`;
2887
+ }
2888
+ const q = parsed.searchParams.get("q");
2889
+ if (q) {
2890
+ return `https://www.google.com/maps?q=${encodeURIComponent(q)}&output=embed`;
2891
+ }
2892
+ const atMatch = parsed.pathname.match(/\/@(-?[\d.]+),(-?[\d.]+),(\d+\.?\d*)z/);
2893
+ if (atMatch) {
2894
+ const [, lat, lng, zoom] = atMatch;
2895
+ return `https://www.google.com/maps?q=${lat},${lng}&z=${Math.round(parseFloat(zoom))}&output=embed`;
2896
+ }
2897
+ return `https://www.google.com/maps?q=${encodeURIComponent(url)}&output=embed`;
2898
+ }
2899
+ case "google-slides" /* GoogleSlides */: {
2900
+ return url.replace(/\/(edit|pub)(#.*)?(\?.*)?$/, "/embed");
2901
+ }
2902
+ case "figma" /* Figma */: {
2903
+ return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
2904
+ }
2905
+ case "github-gist" /* GithubGist */: {
2906
+ if (!url.endsWith(".pibb")) {
2907
+ return `${url}.pibb`;
2908
+ }
2909
+ return url;
2910
+ }
2911
+ default:
2912
+ return url;
2913
+ }
2914
+ } catch {
2915
+ return url;
2916
+ }
2917
+ }
2918
+
2752
2919
  // src/helpers/held.ts
2753
2920
  import { getResources as getResources9, hasComponent as hasComponent2 } from "@woven-ecs/core";
2754
2921
  function isHeldByRemote(ctx, entityId) {
@@ -2991,6 +3158,10 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
2991
3158
  changed.add(entityId);
2992
3159
  }
2993
3160
  for (const entityId of added) {
3161
+ if (hasComponent5(ctx, entityId, Block)) {
3162
+ const block = Block.read(ctx, entityId);
3163
+ if (!canBlockInteract(ctx, block.tag)) continue;
3164
+ }
2994
3165
  if (!hasComponent5(ctx, entityId, Aabb)) {
2995
3166
  addComponent4(ctx, entityId, Aabb, { value: [0, 0, 0, 0] });
2996
3167
  }
@@ -2998,6 +3169,7 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
2998
3169
  computeAabb(ctx, entityId, aabb.value);
2999
3170
  }
3000
3171
  for (const entityId of changed) {
3172
+ if (!hasComponent5(ctx, entityId, Aabb)) continue;
3001
3173
  const aabb = Aabb.write(ctx, entityId);
3002
3174
  computeAabb(ctx, entityId, aabb.value);
3003
3175
  }
@@ -3018,9 +3190,11 @@ var intersectSystem = defineEditorSystem({ phase: "capture", priority: 100 }, (c
3018
3190
  }
3019
3191
  const resources = getResources11(ctx);
3020
3192
  const isObscured = Mouse.isObscured(ctx);
3021
- const wasObscured = prevObscuredByElement.get(resources.domElement) ?? false;
3193
+ const wasObscured = resources.domElement ? prevObscuredByElement.get(resources.domElement) ?? false : false;
3022
3194
  const obscuredChanged = isObscured !== wasObscured;
3023
- prevObscuredByElement.set(resources.domElement, isObscured);
3195
+ if (resources.domElement) {
3196
+ prevObscuredByElement.set(resources.domElement, isObscured);
3197
+ }
3024
3198
  if (isObscured) {
3025
3199
  clearHovered(ctx);
3026
3200
  return;
@@ -3107,6 +3281,7 @@ import { defineQuery as defineQuery9 } from "@woven-ecs/core";
3107
3281
  var scaleWithZoomQuery = defineQuery9((q) => q.with(Block).tracking(ScaleWithZoom));
3108
3282
  var _scaledSize = [0, 0];
3109
3283
  var _anchorOffset = [0, 0];
3284
+ var _anchorAdj = [0, 0];
3110
3285
  var scaleWithZoomSystem = defineEditorSystem({ phase: "render", priority: 100 }, (ctx) => {
3111
3286
  const camera = Camera.read(ctx);
3112
3287
  const state = ScaleWithZoomState.read(ctx);
@@ -3131,7 +3306,15 @@ function scaleBlock(ctx, entityId, zoom) {
3131
3306
  _scaledSize[1] = swz.startSize[1] * scaleY;
3132
3307
  Vec22.copy(_anchorOffset, swz.startSize);
3133
3308
  Vec22.sub(_anchorOffset, _scaledSize);
3134
- Vec22.multiply(_anchorOffset, swz.anchor);
3309
+ if (block.rotateZ !== 0) {
3310
+ _anchorAdj[0] = _anchorOffset[0] * (swz.anchor[0] - 0.5);
3311
+ _anchorAdj[1] = _anchorOffset[1] * (swz.anchor[1] - 0.5);
3312
+ Vec22.rotate(_anchorAdj, block.rotateZ);
3313
+ _anchorOffset[0] = _anchorOffset[0] * 0.5 + _anchorAdj[0];
3314
+ _anchorOffset[1] = _anchorOffset[1] * 0.5 + _anchorAdj[1];
3315
+ } else {
3316
+ Vec22.multiply(_anchorOffset, swz.anchor);
3317
+ }
3135
3318
  Vec22.copy(block.position, swz.startPosition);
3136
3319
  Vec22.add(block.position, _anchorOffset);
3137
3320
  Vec22.copy(block.size, _scaledSize);
@@ -3171,6 +3354,14 @@ var CorePlugin = {
3171
3354
  editOptions: {
3172
3355
  canEdit: true
3173
3356
  }
3357
+ },
3358
+ {
3359
+ tag: "embed",
3360
+ components: [Embed],
3361
+ resizeMode: ResizeMode.Free,
3362
+ editOptions: {
3363
+ canEdit: true
3364
+ }
3174
3365
  }
3175
3366
  ],
3176
3367
  systems: [
@@ -3199,6 +3390,7 @@ var CorePlugin = {
3199
3390
  ],
3200
3391
  setup(ctx) {
3201
3392
  const { domElement } = getResources12(ctx);
3393
+ if (!domElement) return;
3202
3394
  attachKeyboardListeners(domElement);
3203
3395
  attachMouseListeners(domElement);
3204
3396
  attachScreenObserver(domElement);
@@ -3206,6 +3398,7 @@ var CorePlugin = {
3206
3398
  },
3207
3399
  teardown(ctx) {
3208
3400
  const { domElement } = getResources12(ctx);
3401
+ if (!domElement) return;
3209
3402
  detachKeyboardListeners(domElement);
3210
3403
  detachMouseListeners(domElement);
3211
3404
  detachScreenObserver(domElement);
@@ -3306,6 +3499,9 @@ var FontLoader = class {
3306
3499
  */
3307
3500
  loadSingleFont(family) {
3308
3501
  this.loadedFonts.add(family.name);
3502
+ if (typeof document === "undefined") {
3503
+ return Promise.resolve();
3504
+ }
3309
3505
  return new Promise((resolve, reject) => {
3310
3506
  const link = document.createElement("link");
3311
3507
  link.rel = "stylesheet";
@@ -3684,11 +3880,54 @@ var Editor = class {
3684
3880
  _getContext() {
3685
3881
  return this.ctx;
3686
3882
  }
3883
+ /**
3884
+ * Attach a DOM element to a headless editor.
3885
+ * Use this after SSR to connect input listeners and enable rendering.
3886
+ *
3887
+ * @param domElement - The DOM element to attach to
3888
+ *
3889
+ * @example
3890
+ * ```typescript
3891
+ * // SSR: create headless editor
3892
+ * const editor = new Editor(null, { plugins: [...] });
3893
+ * await editor.initialize();
3894
+ * editor.tick(); // process initial state
3895
+ *
3896
+ * // Client hydration: attach the DOM
3897
+ * editor.attachDom(document.getElementById('canvas')!);
3898
+ * ```
3899
+ */
3900
+ attachDom(domElement) {
3901
+ const resources = this.ctx.resources;
3902
+ if (resources.domElement) {
3903
+ throw new Error("Editor already has a DOM element attached. Call detachDom() first.");
3904
+ }
3905
+ resources.domElement = domElement;
3906
+ attachKeyboardListeners(domElement);
3907
+ attachMouseListeners(domElement);
3908
+ attachScreenObserver(domElement);
3909
+ attachPointerListeners(domElement);
3910
+ }
3911
+ /**
3912
+ * Detach the current DOM element.
3913
+ * Removes all input listeners. The editor continues to run headless.
3914
+ */
3915
+ detachDom() {
3916
+ const resources = this.ctx.resources;
3917
+ const { domElement } = resources;
3918
+ if (!domElement) return;
3919
+ detachKeyboardListeners(domElement);
3920
+ detachMouseListeners(domElement);
3921
+ detachScreenObserver(domElement);
3922
+ detachPointerListeners(domElement);
3923
+ resources.domElement = null;
3924
+ }
3687
3925
  /**
3688
3926
  * Clean up the editor.
3689
3927
  * Call this when done to release resources.
3690
3928
  */
3691
3929
  dispose() {
3930
+ this.detachDom();
3692
3931
  const plugins = Array.from(this.plugins.values()).reverse();
3693
3932
  for (const plugin of plugins) {
3694
3933
  if (plugin.teardown) {
@@ -4005,6 +4244,8 @@ export {
4005
4244
  Edited,
4006
4245
  Editor,
4007
4246
  EditorStateDef,
4247
+ Embed,
4248
+ EmbedProvider,
4008
4249
  EventType,
4009
4250
  FontFamily,
4010
4251
  FontLoader,
@@ -4050,17 +4291,18 @@ export {
4050
4291
  VerticalAlignment,
4051
4292
  addComponent6 as addComponent,
4052
4293
  canBlockEdit,
4053
- canBlockSelect,
4294
+ canBlockInteract,
4054
4295
  clearPointerTrackingState,
4055
4296
  createEntity5 as createEntity,
4056
- defineCanvasComponent13 as defineCanvasComponent,
4297
+ defineCanvasComponent14 as defineCanvasComponent,
4057
4298
  defineCanvasSingleton3 as defineCanvasSingleton,
4058
4299
  defineCommand,
4059
4300
  defineEditorState,
4060
4301
  defineEditorSystem,
4061
4302
  defineQuery11 as defineQuery,
4062
4303
  defineSystem,
4063
- field28 as field,
4304
+ detectEmbedProvider,
4305
+ field29 as field,
4064
4306
  getBackrefs,
4065
4307
  getBlockDef,
4066
4308
  getBlockResizeMode,
@@ -4081,6 +4323,8 @@ export {
4081
4323
  parsePlugin,
4082
4324
  removeComponent2 as removeComponent,
4083
4325
  removeEntity3 as removeEntity,
4084
- runMachine
4326
+ resolveEmbedUrl,
4327
+ runMachine,
4328
+ validateEmbedUrl
4085
4329
  };
4086
4330
  //# sourceMappingURL=index.js.map