@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.
@@ -7,7 +7,9 @@ interface AmplifyRendererProps {
7
7
  * Component renderer function that maps componentId to React component
8
8
  * This is site-specific and must be provided by the developer
9
9
  */
10
- renderComponent: (componentId: string, props: Record<string, any>, slots?: Record<string, ReactNode>) => ReactNode;
10
+ renderComponent: (componentId: string, props: Record<string, any>, slots?: Record<string, ReactNode>, context?: {
11
+ layoutNodeId: string;
12
+ }) => ReactNode;
11
13
  /**
12
14
  * Optional page config for server-side rendering (SSR/SSG)
13
15
  * If provided, the component will use this instead of fetching client-side
@@ -7,7 +7,9 @@ interface AmplifyRendererProps {
7
7
  * Component renderer function that maps componentId to React component
8
8
  * This is site-specific and must be provided by the developer
9
9
  */
10
- renderComponent: (componentId: string, props: Record<string, any>, slots?: Record<string, ReactNode>) => ReactNode;
10
+ renderComponent: (componentId: string, props: Record<string, any>, slots?: Record<string, ReactNode>, context?: {
11
+ layoutNodeId: string;
12
+ }) => ReactNode;
11
13
  /**
12
14
  * Optional page config for server-side rendering (SSR/SSG)
13
15
  * If provided, the component will use this instead of fetching client-side
@@ -285,6 +285,32 @@ function resolveAmplifyUpUrls(target) {
285
285
  };
286
286
  }
287
287
 
288
+ // src/preview.ts
289
+ function isPreviewRequest(searchParams) {
290
+ if (searchParams instanceof URLSearchParams) {
291
+ return searchParams.get("preview") === "true" || searchParams.get("visualEditor") === "true";
292
+ }
293
+ const preview = searchParams.preview;
294
+ const visualEditor = searchParams.visualEditor;
295
+ return preview === "true" || visualEditor === "true";
296
+ }
297
+ function isComposerPreview() {
298
+ if (typeof window === "undefined") return false;
299
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) return true;
300
+ const params = new URLSearchParams(window.location.search);
301
+ if (isPreviewRequest(params)) return true;
302
+ if (window.self !== window.top && document.referrer) {
303
+ try {
304
+ const refUrl = new URL(document.referrer);
305
+ if (refUrl.hostname === "amplifyup.ai" || refUrl.hostname.endsWith(".amplifyup.ai") || refUrl.hostname === "localhost" && refUrl.port === "5001" || refUrl.pathname.includes("/dashboard/composer") || refUrl.pathname.includes("/dashboard/routes/")) {
306
+ return true;
307
+ }
308
+ } catch {
309
+ }
310
+ }
311
+ return false;
312
+ }
313
+
288
314
  // src/index.ts
289
315
  function readPreviewQueryOverrides() {
290
316
  if (typeof window === "undefined") return {};
@@ -326,6 +352,15 @@ var AmplifyUpSDK = class {
326
352
  this.messageListener = null;
327
353
  this.mutationObserver = null;
328
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;
329
364
  this.isVisualEditorMode = false;
330
365
  this.eventSelectionMode = false;
331
366
  this.eventSelectionClickHandler = null;
@@ -856,6 +891,12 @@ Available Console Commands:
856
891
  }
857
892
  throw new Error(`Failed to fetch page: ${response.statusText}`);
858
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
+ }
859
900
  const data = await response.json();
860
901
  const pageConfig = data.page;
861
902
  if (pageConfig?.eventTracking) {
@@ -1140,33 +1181,94 @@ Available Console Commands:
1140
1181
  }
1141
1182
  return;
1142
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
+ }
1143
1210
  if (event.data?.type === "GET_ELEMENT_POSITION") {
1144
- const { componentId, slotName } = event.data;
1145
- const selector = slotName ? `[data-amplifyup-slot="${slotName}"], [data-slot-id="${slotName}"]` : `[data-amplifyup-component="${componentId}"], [data-component-id="${componentId}"]`;
1146
- console.log("[AmplifyUp SDK] Finding element position:", { selector, componentId, slotName });
1147
- 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
+ });
1148
1245
  if (element) {
1149
1246
  const rect = element.getBoundingClientRect();
1150
1247
  const position = {
1151
1248
  x: rect.left + rect.width / 2,
1152
- // Center of element
1153
1249
  y: rect.top - 50,
1154
- // Position above element
1155
1250
  width: rect.width,
1156
1251
  height: rect.height
1157
1252
  };
1158
- 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
+ };
1159
1259
  if (window.parent && window.parent !== window) {
1160
1260
  window.parent.postMessage({
1161
1261
  type: "ELEMENT_POSITION",
1162
- componentId,
1163
- slotName,
1164
- position
1262
+ componentId: componentId || null,
1263
+ slotName: slotName || null,
1264
+ nodeId: nodeId || null,
1265
+ isPageSlot: !!isPageSlot,
1266
+ position,
1267
+ bounds
1165
1268
  }, "*");
1166
- console.log("[AmplifyUp SDK] Sent ELEMENT_POSITION message");
1167
1269
  }
1168
1270
  } else {
1169
- console.warn("[AmplifyUp SDK] Element not found for selector:", selector);
1271
+ console.warn("[AmplifyUp SDK] Element not found for position request");
1170
1272
  }
1171
1273
  return;
1172
1274
  }
@@ -1273,6 +1375,157 @@ Available Console Commands:
1273
1375
  }
1274
1376
  return el.tagName.toLowerCase();
1275
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
+ }
1276
1529
  /**
1277
1530
  * Enable component highlighting
1278
1531
  */
@@ -1299,35 +1552,25 @@ Available Console Commands:
1299
1552
  this.teardownComposerDragDrop();
1300
1553
  const slotSelector = "[data-slot-id], [data-amplifyup-slot]";
1301
1554
  const clearDragOverState = () => {
1302
- document.querySelectorAll(`${slotSelector}.amplifyup-drag-over-slot`).forEach((el) => {
1303
- el.classList.remove("amplifyup-drag-over-slot");
1304
- });
1305
- document.body.classList.remove("amplifyup-drag-no-drop");
1555
+ this.clearComposerDragHighlight();
1306
1556
  };
1307
1557
  const dragenter = (e) => {
1308
1558
  e.preventDefault();
1309
- if (e.dataTransfer?.types?.length) {
1310
- document.body.classList.add("amplifyup-drag-no-drop");
1311
- }
1312
1559
  };
1313
1560
  const dragover = (e) => {
1314
1561
  e.preventDefault();
1315
- const target = e.target;
1316
- 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);
1317
1567
  if (e.dataTransfer) {
1318
1568
  e.dataTransfer.dropEffect = slot ? "copy" : "none";
1319
1569
  }
1320
- document.querySelectorAll(`${slotSelector}.amplifyup-drag-over-slot`).forEach((el) => {
1321
- el.classList.remove("amplifyup-drag-over-slot");
1322
- });
1323
- if (slot) {
1324
- slot.classList.add("amplifyup-drag-over-slot");
1325
- document.body.classList.remove("amplifyup-drag-no-drop");
1326
- } else {
1327
- document.body.classList.add("amplifyup-drag-no-drop");
1328
- }
1570
+ this.setComposerDragHighlight(slot, e.clientX, e.clientY);
1329
1571
  };
1330
1572
  const dragleave = (e) => {
1573
+ if (this.composerOverlayDragActive) return;
1331
1574
  const related = e.relatedTarget;
1332
1575
  if (!related || !document.body.contains(related)) {
1333
1576
  clearDragOverState();
@@ -1335,20 +1578,10 @@ Available Console Commands:
1335
1578
  };
1336
1579
  const drop = (e) => {
1337
1580
  e.preventDefault();
1338
- const target = e.target;
1339
- const slot = target?.closest?.(slotSelector);
1581
+ const slot = this.resolveSlotAtPoint(e.clientX, e.clientY);
1340
1582
  clearDragOverState();
1341
1583
  if (!slot || !this.isVisualEditorMode) return;
1342
- const slotName = slot.getAttribute("data-amplifyup-slot") || slot.getAttribute("data-slot-id");
1343
- const isEmpty = slot.getAttribute("data-slot-empty") === "true";
1344
- const win = window;
1345
- if (slotName && win.parent && win.parent !== win) {
1346
- win.parent.postMessage({
1347
- type: "DROP_ON_SLOT",
1348
- slotName,
1349
- slotElement: { path: slot.getAttribute("data-slot-path"), isEmpty }
1350
- }, "*");
1351
- }
1584
+ this.postDropOnSlot(slot);
1352
1585
  };
1353
1586
  const mouseover = (e) => {
1354
1587
  if (!this.showZonesEnabled) return;
@@ -1387,7 +1620,11 @@ Available Console Commands:
1387
1620
  document.removeEventListener("mouseover", h.mouseover, true);
1388
1621
  document.removeEventListener("mouseout", h.mouseout, true);
1389
1622
  this.composerDragHandlers = null;
1623
+ this.composerOverlayDragActive = false;
1624
+ this.lastComposerDragSlot = null;
1625
+ this.composerDragMissFrames = 0;
1390
1626
  document.body.classList.remove("amplifyup-drag-no-drop");
1627
+ document.body.classList.remove("amplifyup-composer-dragging");
1391
1628
  document.querySelectorAll("[data-slot-id].amplifyup-drag-over-slot, [data-amplifyup-slot].amplifyup-drag-over-slot").forEach((el) => {
1392
1629
  el.classList.remove("amplifyup-drag-over-slot");
1393
1630
  });
@@ -1428,21 +1665,32 @@ Available Console Commands:
1428
1665
  '[data-slot-id="main"], [data-amplifyup-slot="main"] {',
1429
1666
  " min-height: 50vh !important;",
1430
1667
  "}",
1431
- '[data-slot-id="main"][data-slot-empty="true"]::before,',
1432
- '[data-amplifyup-slot="main"][data-slot-empty="true"]::before {',
1433
- ' content: "Drop components here" !important;',
1434
- " position: absolute !important;",
1435
- " top: 50% !important; left: 50% !important;",
1436
- " transform: translate(-50%, -50%) !important;",
1437
- " font-size: 14px !important; font-weight: 500 !important;",
1438
- " 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;",
1439
1677
  " pointer-events: none !important;",
1440
- " white-space: nowrap !important;",
1678
+ " text-align: center !important;",
1441
1679
  "}",
1442
1680
  "[data-slot-id].amplifyup-slot-hover, [data-amplifyup-slot].amplifyup-slot-hover {",
1443
1681
  " outline-color: rgba(34, 197, 94, 1) !important;",
1444
1682
  " background: rgba(34, 197, 94, 0.06) !important;",
1445
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
+ "}",
1446
1694
  "[data-slot-id].amplifyup-drag-over-slot, [data-amplifyup-slot].amplifyup-drag-over-slot {",
1447
1695
  " outline: 2px solid rgba(34, 197, 94, 1) !important;",
1448
1696
  " background: rgba(34, 197, 94, 0.18) !important;",
@@ -1458,7 +1706,7 @@ Available Console Commands:
1458
1706
  " color: rgb(22, 163, 74) !important;",
1459
1707
  " pointer-events: none !important;",
1460
1708
  " white-space: nowrap !important;",
1461
- " z-index: 1 !important;",
1709
+ " z-index: 2 !important;",
1462
1710
  "}",
1463
1711
  "body.amplifyup-drag-no-drop { cursor: no-drop !important; }",
1464
1712
  "body.amplifyup-drag-no-drop::before {",
@@ -1585,18 +1833,23 @@ Available Console Commands:
1585
1833
  sendSelectionMessage(element, hierarchy, isEmpty) {
1586
1834
  if (!this.isVisualEditorMode) return;
1587
1835
  const componentId = element.getAttribute("data-amplifyup-component") || element.getAttribute("data-component-id");
1588
- 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");
1589
1837
  const componentName = element.getAttribute("data-component-name");
1590
1838
  const hierarchyInfo = hierarchy.map((el, index) => {
1591
1839
  const elComponentId = el.getAttribute("data-amplifyup-component") || el.getAttribute("data-component-id");
1592
1840
  const elSlotName = el.getAttribute("data-amplifyup-slot") || el.getAttribute("data-slot-id");
1593
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);
1594
1845
  return {
1595
1846
  index,
1596
1847
  componentId: elComponentId,
1597
- componentName: elComponentName,
1598
- slotName: elSlotName,
1599
- isSlot: !!elSlotName
1848
+ componentName: isPageSlot ? "Page" : elComponentName,
1849
+ slotName: isSlot ? elSlotName : null,
1850
+ isSlot,
1851
+ isPageSlot,
1852
+ nodeId
1600
1853
  };
1601
1854
  });
1602
1855
  const rect = element.getBoundingClientRect ? element.getBoundingClientRect() : null;
@@ -1633,7 +1886,6 @@ Available Console Commands:
1633
1886
  return;
1634
1887
  }
1635
1888
  const doc = document;
1636
- const win = window;
1637
1889
  if (highlightComponents) {
1638
1890
  const componentElements = doc.querySelectorAll("[data-amplifyup-component], [data-component-id]");
1639
1891
  console.log("[AmplifyUp SDK] Found", componentElements.length, "components to highlight");
@@ -1694,52 +1946,8 @@ Available Console Commands:
1694
1946
  e.stopPropagation();
1695
1947
  this.sendSelectionMessage(htmlEl, hierarchy, isEmpty);
1696
1948
  };
1697
- const handleDrop = (e) => {
1698
- e.preventDefault();
1699
- e.stopPropagation();
1700
- htmlEl.classList.remove("amplifyup-drag-over-slot");
1701
- if (!win.parent || win.parent === win) return;
1702
- win.parent.postMessage({
1703
- type: "DROP_ON_SLOT",
1704
- slotName,
1705
- slotElement: { path: htmlEl.getAttribute("data-slot-path"), isEmpty }
1706
- }, "*");
1707
- try {
1708
- const data = JSON.parse(e.dataTransfer.getData("application/json"));
1709
- if (data.type === "component") {
1710
- win.parent.postMessage({
1711
- type: "COMPONENT_DROPPED",
1712
- componentId: data.componentId,
1713
- componentName: data.componentName,
1714
- slotName,
1715
- slotElement: { path: htmlEl.getAttribute("data-slot-path"), isEmpty }
1716
- }, "*");
1717
- }
1718
- } catch (_err) {
1719
- }
1720
- };
1721
- const handleDragOver = (e) => {
1722
- e.preventDefault();
1723
- e.stopPropagation();
1724
- e.dataTransfer.dropEffect = "copy";
1725
- htmlEl.classList.add("amplifyup-drag-over-slot");
1726
- };
1727
- const handleDragLeave = (e) => {
1728
- const related = e.relatedTarget;
1729
- if (!related || !htmlEl.contains(related)) {
1730
- htmlEl.classList.remove("amplifyup-drag-over-slot");
1731
- }
1732
- };
1733
1949
  htmlEl.addEventListener("click", handleClick);
1734
- htmlEl.addEventListener("dragover", handleDragOver);
1735
- htmlEl.addEventListener("dragleave", handleDragLeave);
1736
- htmlEl.addEventListener("drop", handleDrop);
1737
- htmlEl._amplifyupSlotHandlers = {
1738
- handleClick,
1739
- handleDragOver,
1740
- handleDragLeave,
1741
- handleDrop
1742
- };
1950
+ htmlEl._amplifyupSlotHandlers = { handleClick };
1743
1951
  });
1744
1952
  }
1745
1953
  }
@@ -2069,6 +2277,8 @@ function AmplifyRenderer({
2069
2277
  "data-slot-id": slot.slotId,
2070
2278
  "data-amplifyup-slot": slot.slotId,
2071
2279
  "data-zone-id": slot.zoneId || void 0,
2280
+ "data-parent-node-id": node.id,
2281
+ "data-parent-component-id": node.componentId,
2072
2282
  children: childrenToRender.map((child) => renderNode(child))
2073
2283
  },
2074
2284
  slot.slotId
@@ -2084,17 +2294,25 @@ function AmplifyRenderer({
2084
2294
  hasSlots: !!slotsToPass,
2085
2295
  slotKeys: slotsToPass ? Object.keys(slotsToPass) : []
2086
2296
  });
2087
- return renderComponent(
2088
- node.componentId,
2089
- node.props || {},
2090
- slotsToPass
2091
- );
2297
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: renderComponent(node.componentId, node.props || {}, slotsToPass, {
2298
+ layoutNodeId: node.id
2299
+ }) });
2092
2300
  };
2093
- const layoutTree = pageConfig.layoutTree;
2094
- if (!layoutTree || layoutTree.length === 0) {
2301
+ const rawTree = pageConfig.layoutTree;
2302
+ const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];
2303
+ if (layoutTree.length === 0) {
2095
2304
  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." }) }) });
2096
2305
  }
2097
- return /* @__PURE__ */ jsxRuntime.jsx("main", { children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id)) });
2306
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { children: isComposerPreview() ? /* @__PURE__ */ jsxRuntime.jsx(
2307
+ "div",
2308
+ {
2309
+ "data-slot-id": "main",
2310
+ "data-amplifyup-slot": "main",
2311
+ "data-slot-source": "page",
2312
+ "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
2313
+ children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id))
2314
+ }
2315
+ ) : layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id)) });
2098
2316
  }
2099
2317
 
2100
2318
  exports.AmplifyRenderer = AmplifyRenderer;