@amplifyup/sdk 0.1.11 → 0.1.13

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/dist/react.mjs CHANGED
@@ -350,6 +350,15 @@ var AmplifyUpSDK = class {
350
350
  this.messageListener = null;
351
351
  this.mutationObserver = null;
352
352
  this.composerDragHandlers = null;
353
+ /** Last slot highlighted during Composer drag — avoid clear/re-add flicker. */
354
+ this.lastComposerDragSlot = null;
355
+ /** Consecutive miss frames before clearing highlight (anti-jitter). */
356
+ this.composerDragMissFrames = 0;
357
+ /**
358
+ * Parent overlay is driving drag via postMessage. Native iframe dragleave must
359
+ * not clear highlight — overlay takeover fires a spurious document dragleave.
360
+ */
361
+ this.composerOverlayDragActive = false;
353
362
  this.isVisualEditorMode = false;
354
363
  this.eventSelectionMode = false;
355
364
  this.eventSelectionClickHandler = null;
@@ -880,6 +889,12 @@ Available Console Commands:
880
889
  }
881
890
  throw new Error(`Failed to fetch page: ${response.statusText}`);
882
891
  }
892
+ const contentType = response.headers.get("content-type") || "";
893
+ if (!contentType.includes("application/json")) {
894
+ throw new Error(
895
+ `Layout provider returned non-JSON (${contentType || "unknown"}). Check Composer preview routing.`
896
+ );
897
+ }
883
898
  const data = await response.json();
884
899
  const pageConfig = data.page;
885
900
  if (pageConfig?.eventTracking) {
@@ -1164,33 +1179,94 @@ Available Console Commands:
1164
1179
  }
1165
1180
  return;
1166
1181
  }
1182
+ if (event.data?.type === "COMPOSER_DRAG_START") {
1183
+ this.composerOverlayDragActive = true;
1184
+ if (typeof document !== "undefined") {
1185
+ document.body.classList.add("amplifyup-composer-dragging");
1186
+ }
1187
+ return;
1188
+ }
1189
+ if (event.data?.type === "COMPOSER_DRAG_AT_POINT") {
1190
+ const { x, y } = event.data;
1191
+ if (typeof x === "number" && typeof y === "number") {
1192
+ this.handleComposerDragAtPoint(x, y);
1193
+ }
1194
+ return;
1195
+ }
1196
+ if (event.data?.type === "COMPOSER_DROP_AT_POINT") {
1197
+ const { x, y } = event.data;
1198
+ if (typeof x === "number" && typeof y === "number") {
1199
+ this.handleComposerDropAtPoint(x, y);
1200
+ }
1201
+ return;
1202
+ }
1203
+ if (event.data?.type === "COMPOSER_DRAG_END") {
1204
+ this.composerOverlayDragActive = false;
1205
+ this.clearComposerDragHighlight();
1206
+ return;
1207
+ }
1167
1208
  if (event.data?.type === "GET_ELEMENT_POSITION") {
1168
- const { componentId, slotName } = event.data;
1169
- const selector = slotName ? `[data-amplifyup-slot="${slotName}"], [data-slot-id="${slotName}"]` : `[data-amplifyup-component="${componentId}"], [data-component-id="${componentId}"]`;
1170
- console.log("[AmplifyUp SDK] Finding element position:", { selector, componentId, slotName });
1171
- const element = document.querySelector(selector);
1209
+ const { componentId, slotName, nodeId, isPageSlot } = event.data;
1210
+ let element = null;
1211
+ if (isPageSlot || slotName === "main" && !nodeId && !componentId) {
1212
+ element = document.querySelector(
1213
+ '[data-slot-source="page"]'
1214
+ );
1215
+ }
1216
+ if (!element && nodeId && slotName) {
1217
+ element = document.querySelector(
1218
+ `[data-parent-node-id="${nodeId}"][data-slot-id="${slotName}"], [data-parent-node-id="${nodeId}"][data-amplifyup-slot="${slotName}"]`
1219
+ );
1220
+ }
1221
+ if (!element && nodeId && !slotName) {
1222
+ element = document.querySelector(
1223
+ `[data-node-id="${nodeId}"]`
1224
+ );
1225
+ }
1226
+ if (!element && slotName) {
1227
+ element = document.querySelector(
1228
+ `[data-amplifyup-slot="${slotName}"], [data-slot-id="${slotName}"]`
1229
+ );
1230
+ }
1231
+ if (!element && componentId) {
1232
+ element = document.querySelector(
1233
+ `[data-amplifyup-component="${componentId}"], [data-component-id="${componentId}"]`
1234
+ );
1235
+ }
1236
+ console.log("[AmplifyUp SDK] Finding element position:", {
1237
+ componentId,
1238
+ slotName,
1239
+ nodeId,
1240
+ isPageSlot,
1241
+ found: !!element
1242
+ });
1172
1243
  if (element) {
1173
1244
  const rect = element.getBoundingClientRect();
1174
1245
  const position = {
1175
1246
  x: rect.left + rect.width / 2,
1176
- // Center of element
1177
1247
  y: rect.top - 50,
1178
- // Position above element
1179
1248
  width: rect.width,
1180
1249
  height: rect.height
1181
1250
  };
1182
- console.log("[AmplifyUp SDK] Element position calculated:", { rect, position });
1251
+ const bounds = {
1252
+ top: rect.top,
1253
+ left: rect.left,
1254
+ width: rect.width,
1255
+ height: rect.height
1256
+ };
1183
1257
  if (window.parent && window.parent !== window) {
1184
1258
  window.parent.postMessage({
1185
1259
  type: "ELEMENT_POSITION",
1186
- componentId,
1187
- slotName,
1188
- position
1260
+ componentId: componentId || null,
1261
+ slotName: slotName || null,
1262
+ nodeId: nodeId || null,
1263
+ isPageSlot: !!isPageSlot,
1264
+ position,
1265
+ bounds
1189
1266
  }, "*");
1190
- console.log("[AmplifyUp SDK] Sent ELEMENT_POSITION message");
1191
1267
  }
1192
1268
  } else {
1193
- console.warn("[AmplifyUp SDK] Element not found for selector:", selector);
1269
+ console.warn("[AmplifyUp SDK] Element not found for position request");
1194
1270
  }
1195
1271
  return;
1196
1272
  }
@@ -1297,6 +1373,157 @@ Available Console Commands:
1297
1373
  }
1298
1374
  return el.tagName.toLowerCase();
1299
1375
  }
1376
+ /** Clear drag-over highlight state (Composer overlay path or native drag end). */
1377
+ clearComposerDragHighlight() {
1378
+ if (typeof document === "undefined") return;
1379
+ const slotSelector = "[data-slot-id], [data-amplifyup-slot]";
1380
+ document.querySelectorAll(`${slotSelector}.amplifyup-drag-over-slot`).forEach((el) => {
1381
+ el.classList.remove("amplifyup-drag-over-slot");
1382
+ });
1383
+ document.body.classList.remove("amplifyup-drag-no-drop");
1384
+ document.body.classList.remove("amplifyup-composer-dragging");
1385
+ this.lastComposerDragSlot = null;
1386
+ this.composerDragMissFrames = 0;
1387
+ }
1388
+ /** Re-apply drag-over class only if missing (avoids classList churn / flicker). */
1389
+ ensureDragOverClass(slot) {
1390
+ if (!slot.classList.contains("amplifyup-drag-over-slot")) {
1391
+ slot.classList.add("amplifyup-drag-over-slot");
1392
+ }
1393
+ if (document.body.classList.contains("amplifyup-drag-no-drop")) {
1394
+ document.body.classList.remove("amplifyup-drag-no-drop");
1395
+ }
1396
+ }
1397
+ /**
1398
+ * Composer drag targets the innermost (most specific) slot under the cursor.
1399
+ * e.g. FullWidthGrid's main wins over a page-level main that wraps it.
1400
+ */
1401
+ resolveSlotAtPoint(x, y) {
1402
+ if (typeof document === "undefined") return null;
1403
+ const slotSelector = "[data-slot-id], [data-amplifyup-slot]";
1404
+ const containing = [];
1405
+ document.querySelectorAll(slotSelector).forEach((node) => {
1406
+ const el = node;
1407
+ const r = el.getBoundingClientRect();
1408
+ if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) {
1409
+ containing.push(el);
1410
+ }
1411
+ });
1412
+ if (containing.length === 0) return null;
1413
+ const innermost = containing.filter(
1414
+ (el) => !containing.some((other) => other !== el && el.contains(other))
1415
+ );
1416
+ if (innermost.length === 1) return innermost[0];
1417
+ let best = innermost[0];
1418
+ let bestArea = Infinity;
1419
+ for (const el of innermost) {
1420
+ const r = el.getBoundingClientRect();
1421
+ const area = Math.max(1, r.width) * Math.max(1, r.height);
1422
+ if (area < bestArea) {
1423
+ bestArea = area;
1424
+ best = el;
1425
+ }
1426
+ }
1427
+ return best;
1428
+ }
1429
+ /** Parent layout node that owns this slot (null for page-level code main). */
1430
+ parentComponentForSlot(slotEl) {
1431
+ let componentId = slotEl.getAttribute("data-parent-component-id");
1432
+ let nodeId = slotEl.getAttribute("data-parent-node-id");
1433
+ let el = slotEl.parentElement;
1434
+ while (el && (!nodeId || !componentId)) {
1435
+ if (!nodeId && el.hasAttribute("data-node-id")) {
1436
+ nodeId = el.getAttribute("data-node-id");
1437
+ }
1438
+ if (!componentId) {
1439
+ componentId = el.getAttribute("data-amplifyup-component") || el.getAttribute("data-component-id");
1440
+ }
1441
+ if (nodeId) break;
1442
+ el = el.parentElement;
1443
+ }
1444
+ return { componentId, nodeId };
1445
+ }
1446
+ pointInRect(x, y, el, pad = 8) {
1447
+ const r = el.getBoundingClientRect();
1448
+ return x >= r.left - pad && x <= r.right + pad && y >= r.top - pad && y <= r.bottom + pad;
1449
+ }
1450
+ setComposerDragHighlight(slot, x, y) {
1451
+ if (slot && slot === this.lastComposerDragSlot) {
1452
+ this.composerDragMissFrames = 0;
1453
+ this.ensureDragOverClass(slot);
1454
+ return;
1455
+ }
1456
+ if (slot && this.lastComposerDragSlot && this.lastComposerDragSlot.contains(slot) && slot !== this.lastComposerDragSlot) {
1457
+ this.composerDragMissFrames = 0;
1458
+ this.lastComposerDragSlot.classList.remove("amplifyup-drag-over-slot");
1459
+ this.lastComposerDragSlot = slot;
1460
+ this.ensureDragOverClass(slot);
1461
+ return;
1462
+ }
1463
+ if (this.lastComposerDragSlot && typeof x === "number" && typeof y === "number" && this.pointInRect(x, y, this.lastComposerDragSlot)) {
1464
+ if (!slot || slot.contains(this.lastComposerDragSlot) || slot === this.lastComposerDragSlot) {
1465
+ this.composerDragMissFrames = 0;
1466
+ this.ensureDragOverClass(this.lastComposerDragSlot);
1467
+ return;
1468
+ }
1469
+ }
1470
+ if (slot) {
1471
+ this.composerDragMissFrames = 0;
1472
+ if (this.lastComposerDragSlot && this.lastComposerDragSlot !== slot) {
1473
+ this.lastComposerDragSlot.classList.remove("amplifyup-drag-over-slot");
1474
+ }
1475
+ this.lastComposerDragSlot = slot;
1476
+ this.ensureDragOverClass(slot);
1477
+ return;
1478
+ }
1479
+ this.composerDragMissFrames += 1;
1480
+ if (this.lastComposerDragSlot && this.composerDragMissFrames < 8) {
1481
+ this.ensureDragOverClass(this.lastComposerDragSlot);
1482
+ return;
1483
+ }
1484
+ if (this.lastComposerDragSlot) {
1485
+ this.lastComposerDragSlot.classList.remove("amplifyup-drag-over-slot");
1486
+ this.lastComposerDragSlot = null;
1487
+ }
1488
+ if (!document.body.classList.contains("amplifyup-drag-no-drop")) {
1489
+ document.body.classList.add("amplifyup-drag-no-drop");
1490
+ }
1491
+ }
1492
+ handleComposerDragAtPoint(x, y) {
1493
+ this.composerOverlayDragActive = true;
1494
+ if (!document.body.classList.contains("amplifyup-composer-dragging")) {
1495
+ document.body.classList.add("amplifyup-composer-dragging");
1496
+ }
1497
+ this.setComposerDragHighlight(this.resolveSlotAtPoint(x, y), x, y);
1498
+ }
1499
+ postDropOnSlot(slotEl) {
1500
+ const win = window;
1501
+ if (!win.parent || win.parent === win) return;
1502
+ const slotName = slotEl.getAttribute("data-amplifyup-slot") || slotEl.getAttribute("data-slot-id");
1503
+ if (!slotName) return;
1504
+ const { componentId, nodeId } = this.parentComponentForSlot(slotEl);
1505
+ const isEmpty = slotEl.getAttribute("data-slot-empty") === "true";
1506
+ win.parent.postMessage(
1507
+ {
1508
+ type: "DROP_ON_SLOT",
1509
+ slotName,
1510
+ slotElement: {
1511
+ path: slotEl.getAttribute("data-slot-path"),
1512
+ isEmpty,
1513
+ parentComponentId: componentId,
1514
+ parentNodeId: nodeId
1515
+ }
1516
+ },
1517
+ "*"
1518
+ );
1519
+ }
1520
+ handleComposerDropAtPoint(x, y) {
1521
+ if (typeof document === "undefined") return;
1522
+ const htmlEl = this.resolveSlotAtPoint(x, y);
1523
+ this.clearComposerDragHighlight();
1524
+ if (!htmlEl) return;
1525
+ this.postDropOnSlot(htmlEl);
1526
+ }
1300
1527
  /**
1301
1528
  * Enable component highlighting
1302
1529
  */
@@ -1323,35 +1550,25 @@ Available Console Commands:
1323
1550
  this.teardownComposerDragDrop();
1324
1551
  const slotSelector = "[data-slot-id], [data-amplifyup-slot]";
1325
1552
  const clearDragOverState = () => {
1326
- document.querySelectorAll(`${slotSelector}.amplifyup-drag-over-slot`).forEach((el) => {
1327
- el.classList.remove("amplifyup-drag-over-slot");
1328
- });
1329
- document.body.classList.remove("amplifyup-drag-no-drop");
1553
+ this.clearComposerDragHighlight();
1330
1554
  };
1331
1555
  const dragenter = (e) => {
1332
1556
  e.preventDefault();
1333
- if (e.dataTransfer?.types?.length) {
1334
- document.body.classList.add("amplifyup-drag-no-drop");
1335
- }
1336
1557
  };
1337
1558
  const dragover = (e) => {
1338
1559
  e.preventDefault();
1339
- const target = e.target;
1340
- const slot = target?.closest?.(slotSelector);
1560
+ if (this.composerOverlayDragActive) {
1561
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
1562
+ return;
1563
+ }
1564
+ const slot = this.resolveSlotAtPoint(e.clientX, e.clientY);
1341
1565
  if (e.dataTransfer) {
1342
1566
  e.dataTransfer.dropEffect = slot ? "copy" : "none";
1343
1567
  }
1344
- document.querySelectorAll(`${slotSelector}.amplifyup-drag-over-slot`).forEach((el) => {
1345
- el.classList.remove("amplifyup-drag-over-slot");
1346
- });
1347
- if (slot) {
1348
- slot.classList.add("amplifyup-drag-over-slot");
1349
- document.body.classList.remove("amplifyup-drag-no-drop");
1350
- } else {
1351
- document.body.classList.add("amplifyup-drag-no-drop");
1352
- }
1568
+ this.setComposerDragHighlight(slot, e.clientX, e.clientY);
1353
1569
  };
1354
1570
  const dragleave = (e) => {
1571
+ if (this.composerOverlayDragActive) return;
1355
1572
  const related = e.relatedTarget;
1356
1573
  if (!related || !document.body.contains(related)) {
1357
1574
  clearDragOverState();
@@ -1359,20 +1576,10 @@ Available Console Commands:
1359
1576
  };
1360
1577
  const drop = (e) => {
1361
1578
  e.preventDefault();
1362
- const target = e.target;
1363
- const slot = target?.closest?.(slotSelector);
1579
+ const slot = this.resolveSlotAtPoint(e.clientX, e.clientY);
1364
1580
  clearDragOverState();
1365
1581
  if (!slot || !this.isVisualEditorMode) return;
1366
- const slotName = slot.getAttribute("data-amplifyup-slot") || slot.getAttribute("data-slot-id");
1367
- const isEmpty = slot.getAttribute("data-slot-empty") === "true";
1368
- const win = window;
1369
- if (slotName && win.parent && win.parent !== win) {
1370
- win.parent.postMessage({
1371
- type: "DROP_ON_SLOT",
1372
- slotName,
1373
- slotElement: { path: slot.getAttribute("data-slot-path"), isEmpty }
1374
- }, "*");
1375
- }
1582
+ this.postDropOnSlot(slot);
1376
1583
  };
1377
1584
  const mouseover = (e) => {
1378
1585
  if (!this.showZonesEnabled) return;
@@ -1411,7 +1618,11 @@ Available Console Commands:
1411
1618
  document.removeEventListener("mouseover", h.mouseover, true);
1412
1619
  document.removeEventListener("mouseout", h.mouseout, true);
1413
1620
  this.composerDragHandlers = null;
1621
+ this.composerOverlayDragActive = false;
1622
+ this.lastComposerDragSlot = null;
1623
+ this.composerDragMissFrames = 0;
1414
1624
  document.body.classList.remove("amplifyup-drag-no-drop");
1625
+ document.body.classList.remove("amplifyup-composer-dragging");
1415
1626
  document.querySelectorAll("[data-slot-id].amplifyup-drag-over-slot, [data-amplifyup-slot].amplifyup-drag-over-slot").forEach((el) => {
1416
1627
  el.classList.remove("amplifyup-drag-over-slot");
1417
1628
  });
@@ -1452,21 +1663,32 @@ Available Console Commands:
1452
1663
  '[data-slot-id="main"], [data-amplifyup-slot="main"] {',
1453
1664
  " min-height: 50vh !important;",
1454
1665
  "}",
1455
- '[data-slot-id="main"][data-slot-empty="true"]::before,',
1456
- '[data-amplifyup-slot="main"][data-slot-empty="true"]::before {',
1457
- ' content: "Drop components here" !important;',
1458
- " position: absolute !important;",
1459
- " top: 50% !important; left: 50% !important;",
1460
- " transform: translate(-50%, -50%) !important;",
1461
- " font-size: 14px !important; font-weight: 500 !important;",
1462
- " color: rgba(34, 197, 94, 0.65) !important;",
1666
+ /* Single empty-state hint — Slot React label uses .amplifyup-slot-placeholder */
1667
+ ".amplifyup-slot-placeholder {",
1668
+ " display: flex !important;",
1669
+ " align-items: center !important;",
1670
+ " justify-content: center !important;",
1671
+ " min-height: inherit !important;",
1672
+ " color: rgba(34, 197, 94, 0.75) !important;",
1673
+ " font-size: 14px !important;",
1674
+ " font-weight: 500 !important;",
1463
1675
  " pointer-events: none !important;",
1464
- " white-space: nowrap !important;",
1676
+ " text-align: center !important;",
1465
1677
  "}",
1466
1678
  "[data-slot-id].amplifyup-slot-hover, [data-amplifyup-slot].amplifyup-slot-hover {",
1467
1679
  " outline-color: rgba(34, 197, 94, 1) !important;",
1468
1680
  " background: rgba(34, 197, 94, 0.06) !important;",
1469
1681
  "}",
1682
+ /* During any Composer drag, hide idle slot labels so they don't flash
1683
+ when the active drag-over class briefly changes */
1684
+ "body.amplifyup-composer-dragging .amplifyup-slot-placeholder {",
1685
+ " visibility: hidden !important;",
1686
+ " opacity: 0 !important;",
1687
+ "}",
1688
+ "body.amplifyup-composer-dragging [data-slot-id]::before,",
1689
+ "body.amplifyup-composer-dragging [data-amplifyup-slot]::before {",
1690
+ " content: none !important;",
1691
+ "}",
1470
1692
  "[data-slot-id].amplifyup-drag-over-slot, [data-amplifyup-slot].amplifyup-drag-over-slot {",
1471
1693
  " outline: 2px solid rgba(34, 197, 94, 1) !important;",
1472
1694
  " background: rgba(34, 197, 94, 0.18) !important;",
@@ -1482,7 +1704,7 @@ Available Console Commands:
1482
1704
  " color: rgb(22, 163, 74) !important;",
1483
1705
  " pointer-events: none !important;",
1484
1706
  " white-space: nowrap !important;",
1485
- " z-index: 1 !important;",
1707
+ " z-index: 2 !important;",
1486
1708
  "}",
1487
1709
  "body.amplifyup-drag-no-drop { cursor: no-drop !important; }",
1488
1710
  "body.amplifyup-drag-no-drop::before {",
@@ -1609,18 +1831,23 @@ Available Console Commands:
1609
1831
  sendSelectionMessage(element, hierarchy, isEmpty) {
1610
1832
  if (!this.isVisualEditorMode) return;
1611
1833
  const componentId = element.getAttribute("data-amplifyup-component") || element.getAttribute("data-component-id");
1612
- const slotName = element.getAttribute("data-amplifyup-slot") || element.getAttribute("data-slot-id");
1834
+ const slotName = componentId ? null : element.getAttribute("data-amplifyup-slot") || element.getAttribute("data-slot-id");
1613
1835
  const componentName = element.getAttribute("data-component-name");
1614
1836
  const hierarchyInfo = hierarchy.map((el, index) => {
1615
1837
  const elComponentId = el.getAttribute("data-amplifyup-component") || el.getAttribute("data-component-id");
1616
1838
  const elSlotName = el.getAttribute("data-amplifyup-slot") || el.getAttribute("data-slot-id");
1617
1839
  const elComponentName = el.getAttribute("data-component-name");
1840
+ const isSlot = !elComponentId && !!elSlotName;
1841
+ const isPageSlot = isSlot && el.getAttribute("data-slot-source") === "page";
1842
+ const nodeId = el.getAttribute("data-node-id") || (isSlot ? el.getAttribute("data-parent-node-id") : null);
1618
1843
  return {
1619
1844
  index,
1620
1845
  componentId: elComponentId,
1621
- componentName: elComponentName,
1622
- slotName: elSlotName,
1623
- isSlot: !!elSlotName
1846
+ componentName: isPageSlot ? "Page" : elComponentName,
1847
+ slotName: isSlot ? elSlotName : null,
1848
+ isSlot,
1849
+ isPageSlot,
1850
+ nodeId
1624
1851
  };
1625
1852
  });
1626
1853
  const rect = element.getBoundingClientRect ? element.getBoundingClientRect() : null;
@@ -1657,7 +1884,6 @@ Available Console Commands:
1657
1884
  return;
1658
1885
  }
1659
1886
  const doc = document;
1660
- const win = window;
1661
1887
  if (highlightComponents) {
1662
1888
  const componentElements = doc.querySelectorAll("[data-amplifyup-component], [data-component-id]");
1663
1889
  console.log("[AmplifyUp SDK] Found", componentElements.length, "components to highlight");
@@ -1718,52 +1944,8 @@ Available Console Commands:
1718
1944
  e.stopPropagation();
1719
1945
  this.sendSelectionMessage(htmlEl, hierarchy, isEmpty);
1720
1946
  };
1721
- const handleDrop = (e) => {
1722
- e.preventDefault();
1723
- e.stopPropagation();
1724
- htmlEl.classList.remove("amplifyup-drag-over-slot");
1725
- if (!win.parent || win.parent === win) return;
1726
- win.parent.postMessage({
1727
- type: "DROP_ON_SLOT",
1728
- slotName,
1729
- slotElement: { path: htmlEl.getAttribute("data-slot-path"), isEmpty }
1730
- }, "*");
1731
- try {
1732
- const data = JSON.parse(e.dataTransfer.getData("application/json"));
1733
- if (data.type === "component") {
1734
- win.parent.postMessage({
1735
- type: "COMPONENT_DROPPED",
1736
- componentId: data.componentId,
1737
- componentName: data.componentName,
1738
- slotName,
1739
- slotElement: { path: htmlEl.getAttribute("data-slot-path"), isEmpty }
1740
- }, "*");
1741
- }
1742
- } catch (_err) {
1743
- }
1744
- };
1745
- const handleDragOver = (e) => {
1746
- e.preventDefault();
1747
- e.stopPropagation();
1748
- e.dataTransfer.dropEffect = "copy";
1749
- htmlEl.classList.add("amplifyup-drag-over-slot");
1750
- };
1751
- const handleDragLeave = (e) => {
1752
- const related = e.relatedTarget;
1753
- if (!related || !htmlEl.contains(related)) {
1754
- htmlEl.classList.remove("amplifyup-drag-over-slot");
1755
- }
1756
- };
1757
1947
  htmlEl.addEventListener("click", handleClick);
1758
- htmlEl.addEventListener("dragover", handleDragOver);
1759
- htmlEl.addEventListener("dragleave", handleDragLeave);
1760
- htmlEl.addEventListener("drop", handleDrop);
1761
- htmlEl._amplifyupSlotHandlers = {
1762
- handleClick,
1763
- handleDragOver,
1764
- handleDragLeave,
1765
- handleDrop
1766
- };
1948
+ htmlEl._amplifyupSlotHandlers = { handleClick };
1767
1949
  });
1768
1950
  }
1769
1951
  }
@@ -2074,6 +2256,8 @@ function AmplifyRenderer({
2074
2256
  "data-slot-id": slot.slotId,
2075
2257
  "data-amplifyup-slot": slot.slotId,
2076
2258
  "data-zone-id": slot.zoneId || void 0,
2259
+ "data-parent-node-id": node.id,
2260
+ "data-parent-component-id": node.componentId,
2077
2261
  children: childrenToRender.map((child) => renderNode(child))
2078
2262
  },
2079
2263
  slot.slotId
@@ -2089,17 +2273,25 @@ function AmplifyRenderer({
2089
2273
  hasSlots: !!slotsToPass,
2090
2274
  slotKeys: slotsToPass ? Object.keys(slotsToPass) : []
2091
2275
  });
2092
- return renderComponent(
2093
- node.componentId,
2094
- node.props || {},
2095
- slotsToPass
2096
- );
2276
+ return /* @__PURE__ */ jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: renderComponent(node.componentId, node.props || {}, slotsToPass, {
2277
+ layoutNodeId: node.id
2278
+ }) });
2097
2279
  };
2098
- const layoutTree = pageConfig.layoutTree;
2099
- if (!layoutTree || layoutTree.length === 0) {
2280
+ const rawTree = pageConfig.layoutTree;
2281
+ const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];
2282
+ if (layoutTree.length === 0) {
2100
2283
  return /* @__PURE__ */ jsx("main", { children: emptyComponent || /* @__PURE__ */ jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No content configured for this page." }) }) });
2101
2284
  }
2102
- return /* @__PURE__ */ jsx("main", { children: layoutTree.map((node) => /* @__PURE__ */ jsx("div", { children: renderNode(node) }, node.id)) });
2285
+ return /* @__PURE__ */ jsx("main", { children: isComposerPreview() ? /* @__PURE__ */ jsx(
2286
+ "div",
2287
+ {
2288
+ "data-slot-id": "main",
2289
+ "data-amplifyup-slot": "main",
2290
+ "data-slot-source": "page",
2291
+ "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
2292
+ children: layoutTree.map((node) => /* @__PURE__ */ jsx("div", { children: renderNode(node) }, node.id))
2293
+ }
2294
+ ) : layoutTree.map((node) => /* @__PURE__ */ jsx("div", { children: renderNode(node) }, node.id)) });
2103
2295
  }
2104
2296
  function defaultPreviewCanvas() {
2105
2297
  return /* @__PURE__ */ jsx(
@@ -2107,8 +2299,10 @@ function defaultPreviewCanvas() {
2107
2299
  {
2108
2300
  "data-slot-id": "main",
2109
2301
  "data-amplifyup-slot": "main",
2302
+ "data-slot-source": "page",
2110
2303
  "data-slot-empty": "true",
2111
- style: { minHeight: "50vh", width: "100%", display: "block" }
2304
+ style: { minHeight: "50vh", width: "100%", display: "block", position: "relative" },
2305
+ children: /* @__PURE__ */ jsx("div", { className: "amplifyup-slot-placeholder", children: "Drop components here" })
2112
2306
  }
2113
2307
  );
2114
2308
  }
@@ -2198,7 +2392,9 @@ var ComponentContext = createContext(null);
2198
2392
  function ComponentContextProvider({
2199
2393
  children,
2200
2394
  slots,
2201
- props
2395
+ props,
2396
+ layoutNodeId,
2397
+ componentId
2202
2398
  }) {
2203
2399
  console.log("[ComponentContextProvider] Providing context", {
2204
2400
  hasSlots: !!slots,
@@ -2209,9 +2405,11 @@ function ComponentContextProvider({
2209
2405
  contentType: slots[key] ? typeof slots[key] : "null"
2210
2406
  })) : [],
2211
2407
  hasProps: !!props,
2212
- propKeys: props ? Object.keys(props) : []
2408
+ propKeys: props ? Object.keys(props) : [],
2409
+ layoutNodeId,
2410
+ componentId
2213
2411
  });
2214
- return /* @__PURE__ */ jsx(ComponentContext.Provider, { value: { slots, props }, children });
2412
+ return /* @__PURE__ */ jsx(ComponentContext.Provider, { value: { slots, props, layoutNodeId, componentId }, children });
2215
2413
  }
2216
2414
  function Slot({
2217
2415
  name,
@@ -2262,33 +2460,50 @@ function Slot({
2262
2460
  {
2263
2461
  className,
2264
2462
  "data-slot-name": name,
2463
+ "data-slot-id": name,
2265
2464
  "data-amplifyup-slot": name,
2266
2465
  "data-slot-empty": isEmpty ? "true" : void 0,
2466
+ "data-parent-node-id": context.layoutNodeId || void 0,
2467
+ "data-parent-component-id": context.componentId || void 0,
2267
2468
  style: {
2268
2469
  // Only add minimum height when empty (for drop target visibility)
2269
2470
  minHeight: isEmpty ? "60px" : void 0,
2270
2471
  // Only add position relative for overlay positioning in editor mode
2271
2472
  position: isEditorMode ? "relative" : void 0
2272
2473
  },
2273
- children: isEmpty ? /* @__PURE__ */ jsx("div", { style: {
2274
- // Only style the empty state indicator, not the container
2275
- minHeight: "60px",
2276
- color: "#9ca3af",
2277
- fontSize: "14px",
2278
- pointerEvents: "none"
2279
- }, children: isEditorMode ? `[Slot: ${name}]` : `Empty slot: ${name}` }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2280
- isEditorMode && /* @__PURE__ */ jsxs("div", { style: {
2281
- position: "absolute",
2282
- top: "4px",
2283
- left: "4px",
2284
- fontSize: "10px",
2285
- color: "#9ca3af",
2286
- pointerEvents: "none",
2287
- zIndex: 1
2288
- }, children: [
2289
- "Slot: ",
2290
- name
2291
- ] }),
2474
+ children: isEmpty ? /* @__PURE__ */ jsx(
2475
+ "div",
2476
+ {
2477
+ className: "amplifyup-slot-placeholder",
2478
+ style: {
2479
+ minHeight: "60px",
2480
+ color: "#9ca3af",
2481
+ fontSize: "14px",
2482
+ pointerEvents: "none"
2483
+ },
2484
+ children: isEditorMode ? `Drop components here` : `Empty slot: ${name}`
2485
+ }
2486
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2487
+ isEditorMode && /* @__PURE__ */ jsxs(
2488
+ "div",
2489
+ {
2490
+ className: "amplifyup-slot-placeholder",
2491
+ style: {
2492
+ position: "absolute",
2493
+ top: "4px",
2494
+ left: "4px",
2495
+ fontSize: "10px",
2496
+ color: "#9ca3af",
2497
+ pointerEvents: "none",
2498
+ zIndex: 1,
2499
+ minHeight: "auto"
2500
+ },
2501
+ children: [
2502
+ "Slot: ",
2503
+ name
2504
+ ]
2505
+ }
2506
+ ),
2292
2507
  slotContent
2293
2508
  ] })
2294
2509
  }
@@ -2340,7 +2555,8 @@ function AmplifyUpProvider({ children, config }) {
2340
2555
  return routeFromQuery;
2341
2556
  }
2342
2557
  const actualRoute = window.__AMPLIFYUP_ACTUAL_ROUTE__;
2343
- if (actualRoute && typeof actualRoute === "string" && (currentPath === "/api/pages/proxy" || currentPath.startsWith("/api/pages/proxy"))) {
2558
+ const inComposerShell = currentPath === "/api/pages/proxy" || currentPath.startsWith("/api/pages/proxy") || currentPath.includes("/preview-frame");
2559
+ if (actualRoute && typeof actualRoute === "string" && inComposerShell) {
2344
2560
  return actualRoute;
2345
2561
  }
2346
2562
  return currentPath;