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