@amplifyup/sdk 0.1.60 → 0.1.62

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
@@ -1,7 +1,7 @@
1
1
  'use client';
2
- import React, { createContext, memo, useState, useEffect, Fragment as Fragment$1, useContext, Component, useRef, useSyncExternalStore } from 'react';
3
- import { marked } from 'marked';
2
+ import React2, { createContext, memo, useState, useEffect, Fragment as Fragment$1, useContext, Component, useMemo, useRef, useSyncExternalStore, useCallback } from 'react';
4
3
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
4
+ import { marked } from 'marked';
5
5
 
6
6
  /* @amplifyup/sdk - https://amplifyup.ai */
7
7
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -405,6 +405,116 @@ function isComposerPreview() {
405
405
  return false;
406
406
  }
407
407
 
408
+ // src/field-envelope.ts
409
+ var FIELDS_BRAND = Symbol("amplifyup.fields");
410
+ function isBrandedFields(value) {
411
+ return !!value && typeof value === "object" && FIELDS_BRAND in value;
412
+ }
413
+ function brandFields(bag) {
414
+ if (isBrandedFields(bag)) return bag;
415
+ Object.defineProperty(bag, FIELDS_BRAND, {
416
+ value: true,
417
+ enumerable: false,
418
+ configurable: false
419
+ });
420
+ return bag;
421
+ }
422
+ function isFieldEnvelope(value) {
423
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
424
+ const rec = value;
425
+ return typeof rec.name === "string" && "value" in rec && !("url" in rec);
426
+ }
427
+ function isPortableImageShape(value) {
428
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
429
+ return "url" in value;
430
+ }
431
+ function joinPath(prefix, key) {
432
+ return prefix ? `${prefix}.${key}` : key;
433
+ }
434
+ function emptyValue(raw) {
435
+ if (raw === void 0) return null;
436
+ return raw;
437
+ }
438
+ function toFieldEnvelopes(source, prefix = "") {
439
+ const input = source && typeof source === "object" && !Array.isArray(source) ? source : {};
440
+ if (isBrandedFields(input)) {
441
+ return input;
442
+ }
443
+ const out = {};
444
+ for (const [key, raw] of Object.entries(input)) {
445
+ const path = joinPath(prefix, key);
446
+ if (isFieldEnvelope(raw)) {
447
+ out[key] = { value: raw.value ?? null, name: raw.name || path };
448
+ continue;
449
+ }
450
+ if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
451
+ out[key] = toFieldEnvelopes(raw, path);
452
+ continue;
453
+ }
454
+ out[key] = { value: emptyValue(raw), name: path };
455
+ }
456
+ return brandFields(out);
457
+ }
458
+ function attachFieldEnvelopes(props) {
459
+ const raw = props && typeof props === "object" ? { ...props } : {};
460
+ if (isBrandedFields(raw.fields)) return raw;
461
+ const { fields: existing, ...content } = raw;
462
+ const source = existing && typeof existing === "object" && !isFieldEnvelope(existing) ? existing : content;
463
+ return { ...raw, fields: toFieldEnvelopes(source) };
464
+ }
465
+ function walkLayoutNodes(nodes) {
466
+ if (!Array.isArray(nodes)) return;
467
+ for (const node of nodes) {
468
+ if (!node || typeof node !== "object") continue;
469
+ const rec = node;
470
+ rec.props = attachFieldEnvelopes(rec.props);
471
+ for (const slot of rec.slots || []) {
472
+ walkLayoutNodes(slot.children);
473
+ }
474
+ }
475
+ }
476
+ function envelopePageConfig(page) {
477
+ if (!page || typeof page !== "object") return page ?? null;
478
+ const tree = page.layoutTree;
479
+ if (Array.isArray(tree)) walkLayoutNodes(tree);
480
+ return page;
481
+ }
482
+ function collectFieldPatches(props) {
483
+ const out = [];
484
+ const walk = (obj, prefix) => {
485
+ for (const [key, raw] of Object.entries(obj)) {
486
+ if (key === "fields" && raw && typeof raw === "object" && !Array.isArray(raw) && !isFieldEnvelope(raw)) {
487
+ walk(raw, prefix);
488
+ continue;
489
+ }
490
+ const path = joinPath(prefix, key);
491
+ if (isFieldEnvelope(raw)) {
492
+ out.push({ name: raw.name || path, value: raw.value });
493
+ continue;
494
+ }
495
+ if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
496
+ walk(raw, path);
497
+ continue;
498
+ }
499
+ out.push({ name: path, value: emptyValue(raw) });
500
+ }
501
+ };
502
+ if (props && typeof props === "object" && !Array.isArray(props)) {
503
+ walk(props, "");
504
+ }
505
+ return out;
506
+ }
507
+ function resolveEditableBinding(field, value, name) {
508
+ if (isFieldEnvelope(field)) {
509
+ return { name: field.name, value: field.value, requiresName: false };
510
+ }
511
+ return {
512
+ name: name || "",
513
+ value,
514
+ requiresName: value !== void 0 && !name
515
+ };
516
+ }
517
+
408
518
  // src/page-context.ts
409
519
  function extractRouteParams(pattern, url) {
410
520
  const patternParts = String(pattern || "").split("/").filter(Boolean);
@@ -588,10 +698,34 @@ function hydratePageResourceValue(value, fields) {
588
698
  if (value.path) return getByDotPath(fields, value.path) ?? null;
589
699
  return fields;
590
700
  }
701
+ function hydrateFieldsBag(bag, fields) {
702
+ const out = {};
703
+ for (const [key, val] of Object.entries(bag)) {
704
+ if (val && typeof val === "object" && !Array.isArray(val) && typeof val.name === "string" && "value" in val && !("url" in val)) {
705
+ const env = val;
706
+ out[key] = {
707
+ name: env.name,
708
+ value: isPageResourceSentinel(env.value) ? hydratePageResourceValue(env.value, fields) : env.value
709
+ };
710
+ continue;
711
+ }
712
+ if (val && typeof val === "object" && !Array.isArray(val) && !("url" in val)) {
713
+ out[key] = hydrateFieldsBag(val, fields);
714
+ continue;
715
+ }
716
+ out[key] = isPageResourceSentinel(val) ? hydratePageResourceValue(val, fields) : val;
717
+ }
718
+ return out;
719
+ }
591
720
  function hydratePageResourceProps(props, fields) {
592
721
  const source = props && typeof props === "object" ? props : {};
593
722
  const out = { ...source };
723
+ if (out.fields && typeof out.fields === "object" && !Array.isArray(out.fields)) {
724
+ const next = hydrateFieldsBag(out.fields, fields);
725
+ out.fields = isBrandedFields(out.fields) ? brandFields(next) : next;
726
+ }
594
727
  for (const [key, val] of Object.entries(out)) {
728
+ if (key === "fields") continue;
595
729
  if (isPageResourceSentinel(val)) {
596
730
  out[key] = hydratePageResourceValue(val, fields);
597
731
  }
@@ -728,7 +862,25 @@ async function fetchEdgeResolve(options) {
728
862
  body
729
863
  );
730
864
  }
731
- return body;
865
+ return envelopeResolvedPayload(body);
866
+ }
867
+ function envelopeResolvedComponent(node) {
868
+ node.props = attachFieldEnvelopes(node.props);
869
+ if (!node.children) return;
870
+ for (const kids of Object.values(node.children)) {
871
+ for (const child of kids || []) envelopeResolvedComponent(child);
872
+ }
873
+ }
874
+ function envelopeResolvedPayload(payload) {
875
+ if (payload.tree) {
876
+ for (const node of payload.tree) envelopeResolvedComponent(node);
877
+ }
878
+ if (payload.zones) {
879
+ for (const roots of Object.values(payload.zones)) {
880
+ for (const node of roots || []) envelopeResolvedComponent(node);
881
+ }
882
+ }
883
+ return payload;
732
884
  }
733
885
 
734
886
  // src/edge/mapResolvedToPageConfig.ts
@@ -788,7 +940,9 @@ async function fetchContent(options) {
788
940
  const preview = options.preview ?? (typeof window !== "undefined" ? isComposerPreview() : false);
789
941
  if (preview) {
790
942
  try {
791
- const page = await options.fetchOrchestratorPage(options.route, true);
943
+ const page = envelopePageConfig(
944
+ await options.fetchOrchestratorPage(options.route, true)
945
+ );
792
946
  return {
793
947
  pageConfig: page,
794
948
  source: "orchestrator",
@@ -2059,7 +2213,7 @@ Available Console Commands:
2059
2213
  this.refreshEventTracking();
2060
2214
  }, 800);
2061
2215
  }
2062
- return pageConfig;
2216
+ return envelopePageConfig(pageConfig);
2063
2217
  }
2064
2218
  /**
2065
2219
  * Set up view-level event tracking based on configuration
@@ -3432,125 +3586,66 @@ function getSDK(adapter) {
3432
3586
  var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
3433
3587
  var refreshEventTracking = () => getSDK().refreshEventTracking();
3434
3588
  getSDK();
3435
- function AddComponentIcon({ size = 28 }) {
3436
- return /* @__PURE__ */ jsxs(
3437
- "svg",
3438
- {
3439
- width: size,
3440
- height: size,
3441
- viewBox: "0 0 24 24",
3442
- fill: "none",
3443
- xmlns: "http://www.w3.org/2000/svg",
3444
- "aria-hidden": "true",
3445
- focusable: "false",
3446
- children: [
3447
- /* @__PURE__ */ jsx(
3448
- "rect",
3449
- {
3450
- x: "3",
3451
- y: "3",
3452
- width: "18",
3453
- height: "18",
3454
- rx: "4",
3455
- stroke: "currentColor",
3456
- strokeWidth: "1.75"
3457
- }
3458
- ),
3459
- /* @__PURE__ */ jsx(
3460
- "path",
3461
- {
3462
- d: "M12 8v8M8 12h8",
3463
- stroke: "currentColor",
3464
- strokeWidth: "1.75",
3465
- strokeLinecap: "round"
3466
- }
3467
- )
3468
- ]
3589
+
3590
+ // src/composer-store.ts
3591
+ var ComposerFieldStore = class {
3592
+ constructor() {
3593
+ this.values = /* @__PURE__ */ new Map();
3594
+ this.listeners = /* @__PURE__ */ new Map();
3595
+ }
3596
+ get(name) {
3597
+ return this.values.has(name) ? this.values.get(name) : void 0;
3598
+ }
3599
+ set(name, value) {
3600
+ if (!name) return;
3601
+ this.values.set(name, value);
3602
+ const set = this.listeners.get(name);
3603
+ if (!set) return;
3604
+ for (const fn of set) fn();
3605
+ }
3606
+ subscribe(name, fn) {
3607
+ if (!name) return () => {
3608
+ };
3609
+ let set = this.listeners.get(name);
3610
+ if (!set) {
3611
+ set = /* @__PURE__ */ new Set();
3612
+ this.listeners.set(name, set);
3469
3613
  }
3470
- );
3614
+ set.add(fn);
3615
+ return () => {
3616
+ set.delete(fn);
3617
+ if (set.size === 0) this.listeners.delete(name);
3618
+ };
3619
+ }
3620
+ };
3621
+ var ComposerStoreContext = createContext(null);
3622
+ function matchesNode(eventNodeId, layoutNodeId) {
3623
+ if (eventNodeId && layoutNodeId && String(eventNodeId) !== String(layoutNodeId)) {
3624
+ return false;
3625
+ }
3626
+ return true;
3471
3627
  }
3472
- function EmptySlotHint({
3473
- label = "Click here to Add Component",
3474
- compact = false
3628
+ function ComposerProvider({
3629
+ layoutNodeId,
3630
+ children
3475
3631
  }) {
3476
- const iconSize = compact ? 20 : 28;
3477
- return /* @__PURE__ */ jsxs(
3478
- "div",
3479
- {
3480
- className: "amplifyup-slot-placeholder",
3481
- style: {
3482
- display: "flex",
3483
- flexDirection: "column",
3484
- alignItems: "center",
3485
- justifyContent: "center",
3486
- gap: compact ? 8 : 12,
3487
- minHeight: compact ? 60 : void 0,
3488
- width: "100%",
3489
- pointerEvents: "none",
3490
- textAlign: "center",
3491
- boxSizing: "border-box",
3492
- padding: compact ? "12px 8px" : "24px 16px"
3493
- },
3494
- children: [
3495
- /* @__PURE__ */ jsx(
3496
- "span",
3497
- {
3498
- className: "amplifyup-slot-placeholder-icon",
3499
- style: {
3500
- display: "inline-flex",
3501
- alignItems: "center",
3502
- justifyContent: "center",
3503
- width: compact ? 36 : 48,
3504
- height: compact ? 36 : 48,
3505
- borderRadius: 12,
3506
- border: "1.5px dashed currentColor",
3507
- opacity: 0.9
3508
- },
3509
- children: /* @__PURE__ */ jsx(AddComponentIcon, { size: iconSize })
3510
- }
3511
- ),
3512
- /* @__PURE__ */ jsx(
3513
- "span",
3514
- {
3515
- className: "amplifyup-slot-placeholder-label",
3516
- style: {
3517
- fontSize: compact ? 13 : 15,
3518
- fontWeight: 600,
3519
- lineHeight: 1.3,
3520
- letterSpacing: "0.01em"
3521
- },
3522
- children: label
3523
- }
3524
- )
3525
- ]
3526
- }
3527
- );
3528
- }
3529
- var ComponentContext = createContext(null);
3530
- function useLiveComposerProps(layoutNodeId, props) {
3531
- const [patch, setPatch] = useState(null);
3632
+ const storeRef = useRef(null);
3633
+ if (!storeRef.current) storeRef.current = new ComposerFieldStore();
3634
+ const store = storeRef.current;
3532
3635
  useEffect(() => {
3533
- setPatch(null);
3534
- const matchesNode = (nodeId) => {
3535
- if (nodeId && layoutNodeId && String(nodeId) !== String(layoutNodeId)) {
3536
- return false;
3537
- }
3538
- return true;
3539
- };
3540
3636
  const applyField = (fieldName, value, nodeId) => {
3541
3637
  if (!fieldName || typeof fieldName !== "string") return;
3542
- if (!matchesNode(nodeId)) return;
3543
- setPatch((prev) => ({ ...prev || {}, [fieldName]: value }));
3638
+ if (!matchesNode(nodeId, layoutNodeId)) return;
3639
+ store.set(fieldName, value);
3544
3640
  };
3545
3641
  const applyProps = (nextProps, nodeId) => {
3546
3642
  if (!nextProps || typeof nextProps !== "object" || Array.isArray(nextProps)) {
3547
3643
  return;
3548
3644
  }
3549
- if (!matchesNode(nodeId)) return;
3550
- setPatch((prev) => ({
3551
- ...prev || {},
3552
- ...nextProps
3553
- }));
3645
+ if (!matchesNode(nodeId, layoutNodeId)) return;
3646
+ for (const patch of collectFieldPatches(nextProps)) {
3647
+ store.set(patch.name, patch.value);
3648
+ }
3554
3649
  };
3555
3650
  const onPatch = (event) => {
3556
3651
  const detail = event.detail || {};
@@ -3576,1280 +3671,1348 @@ function useLiveComposerProps(layoutNodeId, props) {
3576
3671
  window.removeEventListener("amplifyup:props-patch", onPropsPatch);
3577
3672
  window.removeEventListener("message", onMessage);
3578
3673
  };
3579
- }, [layoutNodeId]);
3580
- return patch ? { ...props, ...patch } : props;
3674
+ }, [layoutNodeId, store]);
3675
+ return /* @__PURE__ */ jsx(ComposerStoreContext.Provider, { value: store, children });
3581
3676
  }
3582
- function useComponentProps() {
3583
- const context = useContext(ComponentContext);
3584
- if (!context) {
3585
- console.warn(
3586
- "[AmplifyUp SDK] useComponentProps used outside ComponentContextProvider"
3587
- );
3588
- return {};
3589
- }
3590
- return context.props || {};
3677
+ function useComposerFieldValue(name, fallback) {
3678
+ const store = useContext(ComposerStoreContext);
3679
+ const subscribe = useCallback(
3680
+ (onStoreChange) => {
3681
+ if (!store || !name) return () => {
3682
+ };
3683
+ return store.subscribe(name, onStoreChange);
3684
+ },
3685
+ [store, name]
3686
+ );
3687
+ const getSnapshot = useCallback(() => {
3688
+ if (!store || !name) return void 0;
3689
+ return store.get(name);
3690
+ }, [store, name]);
3691
+ const overlay = useSyncExternalStore(subscribe, getSnapshot, () => void 0);
3692
+ return overlay !== void 0 ? overlay : fallback;
3591
3693
  }
3592
- function ComponentContextProvider({
3593
- children,
3594
- slots,
3595
- props,
3694
+ function MaybeComposerProvider({
3695
+ enabled,
3596
3696
  layoutNodeId,
3597
- componentId
3697
+ children
3598
3698
  }) {
3599
- const liveProps = useLiveComposerProps(
3600
- layoutNodeId,
3601
- hydratePageResourceProps(props || {}, getPageResourceFields())
3699
+ const content = useMemo(() => children, [children]);
3700
+ if (!enabled) return /* @__PURE__ */ jsx(Fragment, { children: content });
3701
+ return /* @__PURE__ */ jsx(ComposerProvider, { layoutNodeId, children });
3702
+ }
3703
+ function nodeRenderProps(node) {
3704
+ const settings = node.settings || {};
3705
+ const hydrated = hydratePageResourceProps(
3706
+ node.props || {},
3707
+ getPageResourceFields()
3708
+ );
3709
+ const fields = hydrated.fields;
3710
+ const branded = isBrandedFields(fields) ? fields : brandFields(
3711
+ fields && typeof fields === "object" ? fields : {}
3602
3712
  );
3713
+ return {
3714
+ ...settings,
3715
+ ...hydrated,
3716
+ fields: branded
3717
+ };
3718
+ }
3719
+ var ComponentRenderBoundary = class extends Component {
3720
+ constructor() {
3721
+ super(...arguments);
3722
+ this.state = { error: null };
3723
+ }
3724
+ static getDerivedStateFromError(error) {
3725
+ return { error };
3726
+ }
3727
+ componentDidCatch(error) {
3728
+ console.error(
3729
+ `[AmplifyRenderer] "${this.props.componentId}" (${this.props.nodeId}) threw while rendering:`,
3730
+ error
3731
+ );
3732
+ }
3733
+ render() {
3734
+ if (this.state.error) {
3735
+ if (process.env.NODE_ENV !== "production") {
3736
+ return /* @__PURE__ */ jsxs(
3737
+ "div",
3738
+ {
3739
+ "data-amplifyup-render-error": "",
3740
+ "data-component-id": this.props.componentId,
3741
+ "data-node-id": this.props.nodeId,
3742
+ style: {
3743
+ margin: "1rem 0",
3744
+ padding: "0.75rem 1rem",
3745
+ border: "1px solid #fca5a5",
3746
+ borderRadius: "0.5rem",
3747
+ background: "#fef2f2",
3748
+ color: "#991b1b",
3749
+ fontSize: "0.875rem"
3750
+ },
3751
+ children: [
3752
+ 'Component "',
3753
+ this.props.componentId,
3754
+ '" failed to render:',
3755
+ " ",
3756
+ this.state.error.message
3757
+ ]
3758
+ }
3759
+ );
3760
+ }
3761
+ return null;
3762
+ }
3763
+ return this.props.children;
3764
+ }
3765
+ };
3766
+ function NodeWithFields({
3767
+ nodeId,
3768
+ componentId,
3769
+ props,
3770
+ slots,
3771
+ renderComponent,
3772
+ composerPreview
3773
+ }) {
3603
3774
  return /* @__PURE__ */ jsx(
3604
- ComponentContext.Provider,
3775
+ MaybeComposerProvider,
3605
3776
  {
3606
- value: { slots, props: liveProps, layoutNodeId, componentId },
3607
- children
3777
+ enabled: !!(composerPreview || isComposerPreview()),
3778
+ layoutNodeId: nodeId,
3779
+ children: renderComponent(componentId, props, slots, {
3780
+ layoutNodeId: nodeId
3781
+ })
3608
3782
  }
3609
3783
  );
3610
3784
  }
3611
- function Slot({
3612
- name,
3613
- className
3785
+ function AmplifyRendererInner({
3786
+ renderComponent,
3787
+ pageConfig: serverPageConfig,
3788
+ loadingComponent,
3789
+ errorComponent,
3790
+ emptyComponent,
3791
+ composerPreview
3614
3792
  }) {
3615
- const context = useContext(ComponentContext);
3616
- if (!context) {
3617
- console.warn(`[AmplifyUp SDK] Slot "${name}" used outside ComponentContextProvider`);
3618
- return null;
3619
- }
3620
- const slotContent = context.slots?.[name];
3621
- const isEmpty = !slotContent;
3622
- const isEditorMode = typeof window !== "undefined" && // Check for preview mode flag set by proxy
3623
- (window.__AMPLIFYUP_PREVIEW_MODE__ === true || // Check URL query params
3624
- new URLSearchParams(window.location.search).get("preview") === "true" || new URLSearchParams(window.location.search).get("visualEditor") === "true" || // Check if in iframe from orchestrator (check referrer hostname/port)
3625
- window.self !== window.top && document.referrer && (() => {
3626
- try {
3627
- const referrerUrl = new URL(document.referrer);
3628
- return referrerUrl.hostname === "localhost" && referrerUrl.port === "5001" || referrerUrl.pathname.includes("/dashboard/composer") || referrerUrl.pathname.includes("/dashboard/routes/");
3629
- } catch {
3630
- return false;
3631
- }
3632
- })());
3633
- sdkDebugOnce(
3634
- `slot:${context.layoutNodeId || ""}:${name}`,
3635
- `[Slot:${name}]`,
3636
- {
3637
- isEmpty,
3638
- isEditorMode,
3639
- availableSlotKeys: context.slots ? Object.keys(context.slots) : []
3640
- }
3641
- );
3642
- return /* @__PURE__ */ jsx(
3643
- "div",
3644
- {
3645
- className,
3646
- "data-slot-name": name,
3647
- "data-slot-id": name,
3648
- "data-amplifyup-slot": name,
3649
- "data-slot-empty": isEmpty ? "true" : void 0,
3650
- "data-parent-node-id": context.layoutNodeId || void 0,
3651
- "data-parent-component-id": context.componentId || void 0,
3652
- style: {
3653
- // Only add minimum height when empty (for drop target visibility)
3654
- minHeight: isEmpty ? "60px" : void 0,
3655
- // Only add position relative for overlay positioning in editor mode
3656
- position: isEditorMode ? "relative" : void 0
3657
- },
3658
- children: isEmpty ? isEditorMode ? /* @__PURE__ */ jsx(EmptySlotHint, { compact: true }) : /* @__PURE__ */ jsx(
3659
- "div",
3660
- {
3661
- className: "amplifyup-slot-placeholder",
3662
- style: {
3663
- minHeight: "60px",
3664
- color: "#9ca3af",
3665
- fontSize: "14px",
3666
- pointerEvents: "none"
3667
- },
3668
- children: `Empty slot: ${name}`
3669
- }
3670
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
3671
- isEditorMode && /* @__PURE__ */ jsxs(
3672
- "div",
3673
- {
3674
- className: "amplifyup-slot-label",
3675
- style: {
3676
- position: "absolute",
3677
- top: "4px",
3678
- left: "4px",
3679
- fontSize: "10px",
3680
- color: "#9ca3af",
3681
- pointerEvents: "none",
3682
- zIndex: 1
3683
- },
3684
- children: [
3685
- "Slot: ",
3686
- name
3687
- ]
3688
- }
3689
- ),
3690
- slotContent
3691
- ] })
3692
- }
3693
- );
3694
- }
3695
- function isComposerEditMode() {
3696
- if (typeof window === "undefined") return false;
3697
- return isComposerPreview() && window.__AMPLIFYUP_EVENT_SELECTION__ !== true;
3698
- }
3699
- function isLikelyNonTextFieldName(name) {
3700
- const n = name.toLowerCase();
3701
- return n.includes("href") || n.includes("url") || n.includes("src") || n.includes("image") || n.includes("icon") || n.includes("overlay") || n.includes("color");
3702
- }
3703
- function isPlainTextValue(value) {
3704
- return typeof value === "string" || typeof value === "number";
3705
- }
3706
- var COMPOSER_EMPTY_FIELD_HINT = "Click here to edit this field";
3707
- var EMPTY_FIELD_PLACEHOLDERS = /* @__PURE__ */ new Set([
3708
- COMPOSER_EMPTY_FIELD_HINT,
3709
- "Click here to enter text",
3710
- "Sample Title",
3711
- "Sample Heading",
3712
- "Sample Subheading"
3713
- ]);
3714
- function isComposerEmptyText(value) {
3715
- if (value === void 0 || value === null) return true;
3716
- if (typeof value !== "string") return false;
3717
- const trimmed = value.trim();
3718
- return trimmed === "" || EMPTY_FIELD_PLACEHOLDERS.has(trimmed);
3719
- }
3720
- function isReactNodeFallback(fallback) {
3721
- if (fallback == null || fallback === false) return false;
3722
- return !isPlainTextValue(fallback);
3723
- }
3724
- function classNameIsBlock(className) {
3725
- return /(?:^|\s)block(?:\s|$)/.test(className || "");
3726
- }
3727
- function canInlineEdit(name, value) {
3728
- if (!name || isLikelyNonTextFieldName(name)) return false;
3729
- if (value !== void 0 && value !== null && typeof value === "object") {
3730
- return false;
3731
- }
3732
- if (typeof value === "boolean") return false;
3733
- return true;
3734
- }
3735
- function canInlineEditRichText(name, value) {
3736
- if (!name || isLikelyNonTextFieldName(name)) return false;
3737
- if (typeof value === "boolean") return false;
3738
- if (value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value)) {
3739
- return false;
3740
- }
3741
- return true;
3742
- }
3743
- function inferEditName(explicitName, contextProps, sourceValue) {
3744
- if (explicitName) return explicitName;
3745
- if (!contextProps) return "";
3746
- const keys = Object.keys(contextProps).filter(
3747
- (key) => Object.is(contextProps[key], sourceValue)
3748
- );
3749
- if (keys.length === 1) return keys[0];
3750
- if (keys.length === 0 || isComposerEmptyText(sourceValue)) return "";
3751
- return keys[0];
3752
- }
3753
- function subscribeComposerEdit(onStoreChange) {
3754
- if (typeof window === "undefined") return () => {
3755
- };
3756
- window.addEventListener("amplifyup:composer-edit", onStoreChange);
3757
- return () => window.removeEventListener("amplifyup:composer-edit", onStoreChange);
3758
- }
3759
- function useComposerEditMode() {
3760
- return useSyncExternalStore(
3761
- subscribeComposerEdit,
3762
- isComposerEditMode,
3763
- () => false
3764
- );
3765
- }
3766
- function useFieldOverlay(layoutNodeId, name, source) {
3767
- const [overlay, setOverlay] = useState(void 0);
3793
+ const clientHook = useAmplifyUp();
3794
+ const pageConfig = serverPageConfig || clientHook.pageConfig || null;
3795
+ const loading = serverPageConfig ? false : clientHook.loading;
3796
+ const error = serverPageConfig ? null : clientHook.error;
3797
+ const contentSource = clientHook.source;
3798
+ const [variantCache, setVariantCache] = useState({});
3768
3799
  useEffect(() => {
3769
- const onPatch = (event) => {
3770
- const detail = event.detail || {};
3771
- if (detail.fieldName !== name) return;
3772
- if (detail.layoutNodeId && layoutNodeId && String(detail.layoutNodeId) !== String(layoutNodeId)) {
3800
+ if (typeof window !== "undefined") {
3801
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
3773
3802
  return;
3774
3803
  }
3775
- setOverlay(detail.value);
3776
- };
3777
- window.addEventListener("amplifyup:field-patch", onPatch);
3778
- return () => window.removeEventListener("amplifyup:field-patch", onPatch);
3779
- }, [layoutNodeId, name]);
3780
- return overlay !== void 0 ? overlay : source;
3781
- }
3782
- function commitComposerField(layoutNodeId, componentId, name, value) {
3783
- if (typeof window === "undefined" || window.parent === window) return;
3784
- window.parent.postMessage(
3785
- {
3786
- type: "AMPLIFYUP_FIELD_COMMIT",
3787
- layoutNodeId,
3788
- componentId,
3789
- fieldName: name,
3790
- value
3791
- },
3792
- "*"
3793
- );
3794
- }
3795
- function looksLikeRichCanvasField(name) {
3796
- const tokens = String(name || "").replace(/([a-z])([A-Z])/g, "$1 $2").split(/[._\-\s/]+/).map((s) => s.toLowerCase()).filter(Boolean);
3797
- const last = tokens[tokens.length - 1] || "";
3798
- return last === "body" || last === "content" || last === "copy" || last === "excerpt" || last === "markdown" || last === "richtext";
3799
- }
3800
- var lastRichTextRequestAt = 0;
3801
- function requestCanvasRichTextEdit(opts) {
3802
- if (typeof window === "undefined" || window.parent === window) return;
3803
- if (!opts.name) return;
3804
- const now = Date.now();
3805
- if (now - lastRichTextRequestAt < 400) return;
3806
- lastRichTextRequestAt = now;
3807
- const rect = opts.el.getBoundingClientRect();
3808
- window.parent.postMessage(
3809
- {
3810
- type: "AMPLIFYUP_RICHTEXT_EDIT",
3811
- layoutNodeId: opts.layoutNodeId,
3812
- componentId: opts.componentId,
3813
- fieldName: opts.name,
3814
- value: opts.value,
3815
- bounds: {
3816
- top: rect.top,
3817
- left: rect.left,
3818
- width: Math.max(rect.width, 240),
3819
- height: Math.max(rect.height, 64)
3804
+ if (window.location.pathname.includes("/api/pages/proxy")) {
3805
+ return;
3806
+ }
3807
+ if (window.self !== window.top) {
3808
+ return;
3809
+ }
3810
+ const urlParams = new URLSearchParams(window.location.search);
3811
+ if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
3812
+ return;
3813
+ }
3814
+ if (window.location.href.includes("preview=true") || window.location.href.includes("visualEditor=true")) {
3815
+ return;
3820
3816
  }
3821
- },
3822
- "*"
3823
- );
3824
- }
3825
- function placeCaret(el, atStart = false) {
3826
- const range = document.createRange();
3827
- range.selectNodeContents(el);
3828
- range.collapse(atStart);
3829
- const sel = window.getSelection();
3830
- sel?.removeAllRanges();
3831
- sel?.addRange(range);
3832
- }
3833
- function ComposerInlineField({
3834
- name,
3835
- value,
3836
- fallback,
3837
- className,
3838
- render,
3839
- layoutNodeId,
3840
- componentId,
3841
- rich = false
3842
- }) {
3843
- const [editing, setEditing] = useState(false);
3844
- const [focused, setFocused] = useState(false);
3845
- const elRef = useRef(null);
3846
- const storedEmpty = isComposerEmptyText(value);
3847
- const text = storedEmpty ? "" : formatFieldValue(value);
3848
- const fallbackText = typeof fallback === "string" || typeof fallback === "number" ? String(fallback) : "";
3849
- const hint = COMPOSER_EMPTY_FIELD_HINT;
3850
- const idleDisplay = storedEmpty ? hint : text || fallbackText;
3851
- const showRendered = !!render && !editing;
3852
- const showHint = storedEmpty && !focused && !showRendered;
3853
- useEffect(() => {
3854
- const el = elRef.current;
3855
- if (!el || showRendered) return;
3856
- if (document.activeElement === el) return;
3857
- if (el.querySelector(
3858
- "input, button, select, textarea, form, svg, iframe, img"
3859
- )) {
3860
- return;
3861
3817
  }
3862
- const next = idleDisplay || "";
3863
- if (el.textContent !== next) el.textContent = next;
3864
- }, [idleDisplay, showRendered]);
3865
- useEffect(() => {
3866
- if (!editing || !elRef.current) return;
3867
- const el = elRef.current;
3868
- if (storedEmpty) {
3869
- el.textContent = "";
3870
- el.focus();
3871
- placeCaret(el, true);
3872
- return;
3818
+ if (pageConfig && pageConfig.id !== "default") {
3819
+ trackContentView(pageConfig.id, "page", {
3820
+ route: pageConfig.route,
3821
+ title: pageConfig.title
3822
+ });
3873
3823
  }
3874
- el.textContent = idleDisplay || "";
3875
- el.focus();
3876
- placeCaret(el, false);
3877
- }, [editing, storedEmpty, idleDisplay]);
3878
- const finishEdit = () => {
3879
- const raw = (elRef.current?.innerText ?? "").replace(/\u00a0/g, " ").replace(/\n+$/g, "");
3880
- const next = raw.trim() === "" ? "" : raw;
3881
- setEditing(false);
3882
- setFocused(false);
3883
- if (storedEmpty && next === "") {
3884
- if (elRef.current) elRef.current.textContent = hint;
3885
- if (typeof value === "string" && EMPTY_FIELD_PLACEHOLDERS.has(value.trim())) {
3886
- commitComposerField(layoutNodeId, componentId, name, "");
3824
+ }, [pageConfig]);
3825
+ useEffect(() => {
3826
+ if (!pageConfig?.eventTracking?.length) return;
3827
+ const sdk = getSDK();
3828
+ if (!sdk.isInitialized()) return;
3829
+ sdk.setupEventTracking(pageConfig.eventTracking);
3830
+ const t1 = window.setTimeout(() => {
3831
+ sdk.setupComponentViewTracking();
3832
+ refreshEventTracking();
3833
+ }, 200);
3834
+ const t2 = window.setTimeout(() => {
3835
+ sdk.setupComponentViewTracking();
3836
+ refreshEventTracking();
3837
+ }, 1e3);
3838
+ return () => {
3839
+ window.clearTimeout(t1);
3840
+ window.clearTimeout(t2);
3841
+ };
3842
+ }, [pageConfig?.id, pageConfig?.eventTracking]);
3843
+ useEffect(() => {
3844
+ if (!pageConfig) return;
3845
+ const t = window.setTimeout(() => {
3846
+ getSDK().setupComponentViewTracking();
3847
+ refreshEventTracking();
3848
+ }, 100);
3849
+ return () => window.clearTimeout(t);
3850
+ }, [variantCache, pageConfig?.id]);
3851
+ useEffect(() => {
3852
+ const isVisualEditorMode = () => {
3853
+ if (typeof window === "undefined") return false;
3854
+ const urlParams = new URLSearchParams(window.location.search);
3855
+ if (urlParams.get("visualEditor") === "true" || urlParams.get("preview") === "true") {
3856
+ return true;
3887
3857
  }
3888
- return;
3889
- }
3890
- if (next === text) return;
3891
- if (!text && !storedEmpty && next === fallbackText) return;
3892
- commitComposerField(layoutNodeId, componentId, name, next);
3893
- };
3894
- const openRichEditor = (event) => {
3895
- if (!isComposerEditMode() || !elRef.current) return;
3896
- event?.preventDefault();
3897
- event?.stopPropagation();
3898
- const source = typeof value === "string" && !storedEmpty ? value : text || "";
3899
- requestCanvasRichTextEdit({
3900
- el: elRef.current,
3901
- layoutNodeId,
3902
- componentId,
3903
- name,
3904
- value: source
3905
- });
3906
- };
3907
- return /* @__PURE__ */ jsx(
3908
- "span",
3909
- {
3910
- ref: elRef,
3911
- className,
3912
- "data-amplifyup-field": name,
3913
- "data-amplifyup-field-type": rich ? "richtext" : "text",
3914
- "data-amplifyup-node-id": layoutNodeId || void 0,
3915
- "data-amplifyup-empty-hint": showHint ? "true" : void 0,
3916
- contentEditable: !rich && !showRendered,
3917
- suppressContentEditableWarning: true,
3918
- role: "textbox",
3919
- tabIndex: 0,
3920
- onMouseDown: (event) => {
3921
- event.stopPropagation();
3922
- },
3923
- onClick: (event) => {
3924
- if (!isComposerEditMode()) return;
3925
- const link = event.target?.closest?.("a");
3926
- if (link) event.preventDefault();
3927
- if (rich) {
3928
- openRichEditor(event);
3929
- return;
3930
- }
3931
- if (render) {
3932
- setEditing(true);
3933
- }
3934
- },
3935
- onFocus: () => {
3936
- if (!isComposerEditMode()) return;
3937
- if (rich) {
3938
- openRichEditor();
3939
- return;
3940
- }
3941
- setFocused(true);
3942
- const el = elRef.current;
3943
- if (el && storedEmpty) {
3944
- el.textContent = "";
3945
- placeCaret(el, true);
3946
- }
3947
- if (render) setEditing(true);
3948
- },
3949
- onBlur: rich ? void 0 : finishEdit,
3950
- onKeyDown: (event) => {
3951
- event.stopPropagation();
3952
- if (event.key === "Enter" && !event.shiftKey) {
3953
- event.preventDefault();
3954
- event.currentTarget.blur();
3955
- }
3956
- if (event.key === "Escape") {
3957
- event.preventDefault();
3958
- if (elRef.current) elRef.current.textContent = idleDisplay || "";
3959
- setEditing(false);
3960
- setFocused(false);
3961
- event.currentTarget.blur();
3858
+ const inIframe = window.self !== window.top;
3859
+ if (!inIframe) return false;
3860
+ try {
3861
+ if (document.referrer) {
3862
+ const referrerUrl = new URL(document.referrer);
3863
+ if (referrerUrl.hostname === "localhost" && referrerUrl.port === "5001") {
3864
+ return true;
3865
+ }
3962
3866
  }
3963
- },
3964
- style: {
3965
- cursor: "text",
3966
- outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
3967
- outlineOffset: "2px",
3968
- minWidth: "1ch",
3969
- minHeight: "1em",
3970
- // Do not force `inline` — that overrides Tailwind `block` and parks
3971
- // an empty-field hint on the same line as a sibling that has a value.
3972
- opacity: showHint ? 0.5 : void 0
3973
- },
3974
- children: showRendered ? value != null && value !== "" && !storedEmpty ? render(value) : fallback ?? idleDisplay : isReactNodeFallback(fallback) && !idleDisplay ? fallback : null
3867
+ } catch (e) {
3868
+ }
3869
+ return false;
3870
+ };
3871
+ if (!isVisualEditorMode()) {
3872
+ return;
3975
3873
  }
3976
- );
3977
- }
3978
- function ComposerInlineHtml({
3979
- name,
3980
- html,
3981
- sourceValue,
3982
- className,
3983
- Tag = "div",
3984
- layoutNodeId,
3985
- componentId
3986
- }) {
3987
- const elRef = useRef(null);
3988
- useEffect(() => {
3989
- const el = elRef.current;
3990
- if (!el) return;
3991
- if (el.innerHTML !== html) el.innerHTML = html;
3992
- }, [html]);
3993
- const openRichEditor = (event) => {
3994
- if (!elRef.current) return;
3995
- event.preventDefault();
3996
- event.stopPropagation();
3997
- requestCanvasRichTextEdit({
3998
- el: elRef.current,
3999
- layoutNodeId,
4000
- componentId,
4001
- name,
4002
- value: sourceValue
4003
- });
4004
- };
4005
- return React.createElement(Tag, {
4006
- ref: elRef,
4007
- className,
4008
- "data-amplifyup-field": name,
4009
- "data-amplifyup-field-type": "richtext",
4010
- "data-amplifyup-node-id": layoutNodeId || void 0,
4011
- dangerouslySetInnerHTML: { __html: html },
4012
- onMouseDown: (event) => {
4013
- event.stopPropagation();
4014
- },
4015
- onClick: openRichEditor,
4016
- style: {
4017
- cursor: "text",
4018
- outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
4019
- outlineOffset: "2px"
3874
+ if (!pageConfig) {
3875
+ return;
4020
3876
  }
4021
- });
4022
- }
4023
- function formatFieldValue(value) {
4024
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4025
- return String(value);
4026
- }
4027
- if (Array.isArray(value)) {
4028
- const parts = value.map((item) => {
4029
- if (typeof item === "string") return item;
4030
- if (item && typeof item === "object" && "children" in item && Array.isArray(item.children)) {
4031
- return item.children.map((c) => typeof c?.text === "string" ? c.text : "").join("");
3877
+ const rawTree2 = pageConfig.layoutTree;
3878
+ const normalizedTree = Array.isArray(rawTree2) ? rawTree2 : rawTree2 && typeof rawTree2 === "object" && rawTree2.componentId ? [rawTree2] : [];
3879
+ if (normalizedTree.length === 0) {
3880
+ if (typeof window !== "undefined" && window.parent && window.parent !== window) {
3881
+ window.parent.postMessage({
3882
+ type: "COMPONENT_TREE",
3883
+ tree: []
3884
+ }, "*");
4032
3885
  }
4033
- return "";
4034
- }).filter(Boolean);
4035
- if (parts.length > 0) return parts.join(" ");
4036
- }
4037
- if (value && typeof value === "object") {
4038
- return "";
4039
- }
4040
- return "";
4041
- }
4042
- function warnProjectionProps(component, name, hasExplicitValue) {
4043
- if (process.env.NODE_ENV === "production") return;
4044
- if (hasExplicitValue) return;
4045
- if (name) {
4046
- console.warn(
4047
- `[AmplifyUp SDK] ${component} \`name\` is deprecated and will be removed. Prefer value={${name}} from your component props.`
3886
+ return;
3887
+ }
3888
+ const convertLayoutTreeToComponentTree = (nodes, parentId = null) => {
3889
+ const result = [];
3890
+ nodes.forEach((node) => {
3891
+ if (!node) return;
3892
+ const treeNode = {
3893
+ id: node.id,
3894
+ componentId: node.componentId,
3895
+ componentName: node.componentId,
3896
+ // Will be enhanced with actual name if available
3897
+ parentId,
3898
+ children: []
3899
+ };
3900
+ if (node.slots && node.slots.length > 0) {
3901
+ node.slots.forEach((slot) => {
3902
+ const kids = Array.isArray(slot?.children) ? slot.children : [];
3903
+ if (kids.length > 0) {
3904
+ const slotChildren = convertLayoutTreeToComponentTree(kids, node.id);
3905
+ treeNode.children.push(...slotChildren);
3906
+ }
3907
+ });
3908
+ }
3909
+ result.push(treeNode);
3910
+ });
3911
+ return result;
3912
+ };
3913
+ const layoutTree2 = normalizedTree;
3914
+ const componentTree = convertLayoutTreeToComponentTree(layoutTree2);
3915
+ const describeLayoutTree = (nodes, depth = 0) => {
3916
+ if (!nodes?.length) return "";
3917
+ const indent = " ".repeat(depth);
3918
+ return nodes.map((n) => {
3919
+ const slotsDesc = (n.slots || []).map((s) => {
3920
+ const childCount = Array.isArray(s.children) ? s.children.length : 0;
3921
+ const childrenDesc = childCount > 0 ? describeLayoutTree(s.children || [], depth + 1) : "";
3922
+ return `${indent} slot "${s.slotId || s.slotName || "?"}" (${childCount} children)${childrenDesc ? "\n" + childrenDesc : ""}`;
3923
+ }).join("\n");
3924
+ return `${indent}- ${n.id} (${n.componentId || "?"})${slotsDesc ? "\n" + slotsDesc : ""}`;
3925
+ }).join("\n");
3926
+ };
3927
+ sdkDebugOnce(
3928
+ `layoutTree:${pageConfig?.id || ""}:${pageConfig?.route || ""}`,
3929
+ "[AmplifyRenderer] layoutTree received:\n%s",
3930
+ describeLayoutTree(layoutTree2)
4048
3931
  );
4049
- }
4050
- }
4051
- function hasOwnProp(props, key) {
4052
- return Object.prototype.hasOwnProperty.call(props, key);
4053
- }
4054
- function isLinkListItem(value) {
4055
- return !!value && typeof value === "object" && !Array.isArray(value);
4056
- }
4057
- function renderLinkList(items, className) {
4058
- const links = items.filter(isLinkListItem).filter((item) => {
4059
- const label = typeof item.label === "string" ? item.label.trim() : "";
4060
- const href = typeof item.href === "string" ? item.href.trim() : "";
4061
- return !!(label || href);
4062
- });
4063
- if (links.length === 0) return null;
4064
- return /* @__PURE__ */ jsx("span", { className, style: { display: "contents" }, children: links.map((item, index) => {
4065
- const label = typeof item.label === "string" && item.label.trim() ? item.label : typeof item.href === "string" ? item.href : "Link";
4066
- const href = typeof item.href === "string" && item.href.trim() ? item.href : "#";
4067
- const style = typeof item.style === "string" ? item.style.trim() : "";
4068
- return /* @__PURE__ */ jsx(
4069
- "a",
4070
- {
4071
- href,
4072
- target: item.openInNewTab ? "_blank" : void 0,
4073
- rel: item.openInNewTab ? "noreferrer" : void 0,
4074
- "data-amplifyup-action-style": style || void 0,
4075
- children: label
4076
- },
4077
- typeof item._key === "string" && item._key ? item._key : `${href}-${index}`
4078
- );
4079
- }) });
4080
- }
4081
- function Field(props) {
4082
- const { value, name, fallback, className, render } = props;
4083
- const context = useContext(ComponentContext);
4084
- const composerEdit = useComposerEditMode();
4085
- const hasExplicitValue = hasOwnProp(props, "value");
4086
- const sourceValue = hasExplicitValue ? value : name ? context?.props?.[name] : void 0;
4087
- const editName = inferEditName(name, context?.props, sourceValue);
4088
- const fieldValue = useFieldOverlay(
4089
- context?.layoutNodeId,
4090
- editName,
4091
- sourceValue
4092
- );
4093
- warnProjectionProps("Field", name, hasExplicitValue);
4094
- if (!context && !hasExplicitValue) {
4095
- console.warn(
4096
- "[AmplifyUp SDK] Field needs a `value` prop (or deprecated `name` alone)"
4097
- );
4098
- return fallback ? /* @__PURE__ */ jsx(Fragment, { children: fallback }) : null;
4099
- }
4100
- if (render) {
4101
- if (composerEdit && editName && context && canInlineEdit(editName, fieldValue)) {
4102
- return /* @__PURE__ */ jsx(
4103
- ComposerInlineField,
4104
- {
4105
- name: editName,
4106
- value: fieldValue,
4107
- fallback,
4108
- className,
4109
- render,
4110
- layoutNodeId: context.layoutNodeId,
4111
- componentId: context.componentId,
4112
- rich: looksLikeRichCanvasField(editName)
4113
- }
3932
+ if (typeof window !== "undefined" && window.parent && window.parent !== window) {
3933
+ const message = {
3934
+ type: "COMPONENT_TREE",
3935
+ tree: componentTree
3936
+ };
3937
+ window.parent.postMessage(message, "*");
3938
+ sdkDebug(
3939
+ "[AmplifyRenderer] Sent component tree from layoutTree:",
3940
+ componentTree.length,
3941
+ "root components"
4114
3942
  );
4115
3943
  }
4116
- return /* @__PURE__ */ jsx("span", { className, style: { display: "contents" }, children: render(fieldValue) });
4117
- }
4118
- if (Array.isArray(fieldValue)) {
4119
- const list = renderLinkList(fieldValue, className);
4120
- if (list) return list;
4121
- }
4122
- const text = formatFieldValue(fieldValue);
4123
- const showVisualFallback = !text && isReactNodeFallback(fallback);
4124
- const emptyBlockField = isComposerEmptyText(fieldValue) && classNameIsBlock(className);
4125
- if (composerEdit && editName && context && canInlineEdit(editName, fieldValue) && !showVisualFallback && !emptyBlockField) {
4126
- return /* @__PURE__ */ jsx(
4127
- ComposerInlineField,
4128
- {
4129
- name: editName,
4130
- value: fieldValue,
4131
- fallback,
4132
- className,
4133
- layoutNodeId: context.layoutNodeId,
4134
- componentId: context.componentId,
4135
- rich: looksLikeRichCanvasField(editName)
4136
- }
4137
- );
4138
- }
4139
- if (fieldValue === void 0 || fieldValue === null || isComposerEmptyText(fieldValue)) {
4140
- return fallback ? /* @__PURE__ */ jsx("span", { className, children: fallback }) : null;
4141
- }
4142
- if (!text) {
4143
- return fallback ? /* @__PURE__ */ jsx("span", { className, children: fallback }) : null;
3944
+ }, [pageConfig]);
3945
+ useEffect(() => {
3946
+ if (!pageConfig?.layoutTree || !pageConfig.layoutId) return;
3947
+ const roots = pageConfig.layoutTree;
3948
+ const fromEdge = contentSource === "edge" || roots.some((n) => n._contentSource === "edge");
3949
+ if (fromEdge) return;
3950
+ const sdk = getSDK();
3951
+ const instanceId = sdk.getInstanceId();
3952
+ const decisionApiUrl = sdk.getDecisionApiUrl();
3953
+ const orchestratorUrl = sdk.getOrchestratorUrl();
3954
+ if (!decisionApiUrl && !orchestratorUrl || !instanceId) return;
3955
+ const fetchVariants = async () => {
3956
+ const nodes = pageConfig.layoutTree;
3957
+ const newCache = {};
3958
+ const findZonedSlots = (nodeList) => {
3959
+ const results = [];
3960
+ for (const node of nodeList) {
3961
+ if (node.slots) {
3962
+ for (const slot of node.slots) {
3963
+ if (slot.zoneId && slot.variants) {
3964
+ const key = `${node.id}-${slot.slotId}`;
3965
+ results.push({ node, slot, key });
3966
+ }
3967
+ }
3968
+ }
3969
+ }
3970
+ return results;
3971
+ };
3972
+ const zonedSlots = findZonedSlots(nodes);
3973
+ await Promise.all(
3974
+ zonedSlots.map(async ({ node, slot, key }) => {
3975
+ try {
3976
+ let personalizationUrl;
3977
+ if (decisionApiUrl) {
3978
+ const url = new URL(decisionApiUrl);
3979
+ url.searchParams.set("instanceId", instanceId);
3980
+ const zoneId = slot.zoneId;
3981
+ if (zoneId) {
3982
+ url.searchParams.set("zoneId", zoneId);
3983
+ }
3984
+ const campaignId = slot.campaignId;
3985
+ if (campaignId) {
3986
+ url.searchParams.set("campaignId", campaignId);
3987
+ }
3988
+ personalizationUrl = url.toString();
3989
+ } else if (orchestratorUrl) {
3990
+ personalizationUrl = `${orchestratorUrl}/api/personalization/variant?instanceId=${encodeURIComponent(instanceId)}&layoutId=${encodeURIComponent(pageConfig.layoutId)}&slotId=${encodeURIComponent(slot.slotId)}&nodeId=${encodeURIComponent(node.id)}`;
3991
+ } else {
3992
+ return;
3993
+ }
3994
+ const response = await fetch(personalizationUrl);
3995
+ if (response.ok) {
3996
+ const data = await response.json();
3997
+ if (data.variantId && data.componentTree) {
3998
+ newCache[key] = {
3999
+ variantId: data.variantId,
4000
+ children: data.componentTree
4001
+ };
4002
+ if (typeof window !== "undefined") {
4003
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
4004
+ return;
4005
+ }
4006
+ if (window.location.pathname.includes("/api/pages/proxy")) {
4007
+ return;
4008
+ }
4009
+ if (window.self !== window.top) {
4010
+ return;
4011
+ }
4012
+ }
4013
+ sdk.track("Zone Impression", {
4014
+ zoneId: slot.zoneId,
4015
+ variantId: data.variantId,
4016
+ layoutId: pageConfig.layoutId,
4017
+ route: pageConfig.route
4018
+ });
4019
+ } else {
4020
+ const defaultVariant = slot.variants?.find((v) => v.isDefault);
4021
+ if (defaultVariant) {
4022
+ newCache[key] = {
4023
+ variantId: defaultVariant.variantId,
4024
+ children: defaultVariant.children
4025
+ };
4026
+ }
4027
+ }
4028
+ }
4029
+ } catch (err) {
4030
+ console.error(`Error fetching variant for slot ${key}:`, err);
4031
+ const defaultVariant = slot.variants?.find((v) => v.isDefault);
4032
+ if (defaultVariant) {
4033
+ newCache[key] = {
4034
+ variantId: defaultVariant.variantId,
4035
+ children: defaultVariant.children
4036
+ };
4037
+ }
4038
+ }
4039
+ })
4040
+ );
4041
+ setVariantCache(newCache);
4042
+ };
4043
+ fetchVariants();
4044
+ }, [pageConfig?.layoutTree, pageConfig?.layoutId, pageConfig?.route, contentSource]);
4045
+ if (loading) {
4046
+ return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse", children: [
4047
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-gray-200 rounded w-1/4 mb-4" }),
4048
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-gray-200 rounded w-1/2 mb-8" }),
4049
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4050
+ /* @__PURE__ */ jsx("div", { className: "h-64 bg-gray-200 rounded" }),
4051
+ /* @__PURE__ */ jsx("div", { className: "h-32 bg-gray-200 rounded" })
4052
+ ] })
4053
+ ] }) }) }) });
4144
4054
  }
4145
- return /* @__PURE__ */ jsx("span", { className, children: text });
4146
- }
4147
- function markdownToHtml(markdown) {
4148
- try {
4149
- const result = marked.parse(markdown, { async: false });
4150
- return typeof result === "string" ? result : "";
4151
- } catch {
4152
- return markdown;
4055
+ if (error) {
4056
+ return /* @__PURE__ */ jsx(Fragment, { children: errorComponent ? errorComponent(error) : /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxs("div", { className: "bg-red-50 border border-red-200 rounded-lg p-6", children: [
4057
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-red-900 mb-2", children: "Error Loading Page" }),
4058
+ /* @__PURE__ */ jsx("p", { className: "text-red-700", children: error })
4059
+ ] }) }) }) });
4153
4060
  }
4154
- }
4155
- function RichText(props) {
4156
- const {
4157
- value,
4158
- name,
4159
- fallback,
4160
- className,
4161
- as: Tag = "div",
4162
- children
4163
- } = props;
4164
- const context = useContext(ComponentContext);
4165
- const composerEdit = useComposerEditMode();
4166
- const childrenValue = typeof children === "string" || typeof children === "number" ? String(children) : void 0;
4167
- const hasValueProp = hasOwnProp(props, "value");
4168
- const hasExplicitValue = hasValueProp || childrenValue !== void 0;
4169
- const sourceValue = hasValueProp ? value : childrenValue !== void 0 ? childrenValue : name ? context?.props?.[name] : void 0;
4170
- const editName = inferEditName(name, context?.props, sourceValue);
4171
- const fieldValue = useFieldOverlay(
4172
- context?.layoutNodeId,
4173
- editName,
4174
- sourceValue
4175
- );
4176
- if (!context && !hasExplicitValue) {
4177
- console.warn(
4178
- "[AmplifyUp SDK] RichText needs a `value` prop (or string children), or deprecated `name` alone"
4179
- );
4180
- return fallback ? /* @__PURE__ */ jsx(Fragment, { children: fallback }) : null;
4061
+ if (!pageConfig) {
4062
+ return null;
4181
4063
  }
4182
- warnProjectionProps("RichText", name, hasExplicitValue);
4183
- const canEdit = !!context && canInlineEditRichText(editName, fieldValue);
4184
- if (isComposerEmptyText(fieldValue)) {
4185
- if (composerEdit && canEdit && !isReactNodeFallback(fallback) && !classNameIsBlock(className)) {
4064
+ pageConfig.layout?.header !== false;
4065
+ pageConfig.layout?.footer !== false;
4066
+ const renderNode = (node) => {
4067
+ const zoneIdFromProps = node.componentId === "AmplifyZone" ? String(node.props?.zoneId || "") : "";
4068
+ if (node.componentId === "AmplifyZone") {
4069
+ const contentSlot = node.slots?.find((s) => s.slotId === "content") || node.slots?.[0];
4070
+ const children = Array.isArray(contentSlot?.children) ? contentSlot.children : [];
4186
4071
  return /* @__PURE__ */ jsx(
4187
- ComposerInlineField,
4072
+ "div",
4188
4073
  {
4189
- name: editName,
4190
- value: fieldValue,
4191
- fallback: typeof fallback === "string" || typeof fallback === "number" ? fallback : void 0,
4192
- className,
4193
- layoutNodeId: context?.layoutNodeId,
4194
- componentId: context?.componentId,
4195
- rich: true
4074
+ "data-node-id": node.id,
4075
+ "data-amplifyup-node": "",
4076
+ "data-amplifyup-component": "AmplifyZone",
4077
+ "data-amplifyup-zone": zoneIdFromProps || void 0,
4078
+ "data-zone-id": zoneIdFromProps || void 0,
4079
+ children: /* @__PURE__ */ jsx(
4080
+ "div",
4081
+ {
4082
+ "data-slot-id": "content",
4083
+ "data-amplifyup-slot": "content",
4084
+ "data-parent-node-id": node.id,
4085
+ "data-parent-component-id": "AmplifyZone",
4086
+ children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child) }, child.id))
4087
+ }
4088
+ )
4196
4089
  }
4197
4090
  );
4198
4091
  }
4199
- return fallback ? /* @__PURE__ */ jsx(Tag, { className, children: fallback }) : null;
4200
- }
4201
- let html = "";
4202
- if (typeof fieldValue === "string") {
4203
- const trimmed = fieldValue.trim();
4204
- if (trimmed.startsWith("<") && trimmed.includes(">")) {
4205
- html = fieldValue;
4206
- } else {
4207
- html = markdownToHtml(fieldValue);
4092
+ let slotChildren = {};
4093
+ sdkDebugOnce(`node:${node.id}`, `[AmplifyRenderer] Rendering node ${node.id} (${node.componentId})`, {
4094
+ hasSlots: !!(node.slots && node.slots.length > 0),
4095
+ slotCount: node.slots?.length || 0,
4096
+ slots: node.slots?.map((s) => ({ slotId: s.slotId, zoneId: s.zoneId, childrenCount: s.children?.length || 0 }))
4097
+ });
4098
+ if (node.slots && node.slots.length > 0) {
4099
+ for (const slot of node.slots) {
4100
+ const slotKey = `${node.id}-${slot.slotId}`;
4101
+ let childrenToRender = [];
4102
+ if (slot.zoneId && slot.variants && variantCache[slotKey]) {
4103
+ childrenToRender = variantCache[slotKey].children;
4104
+ } else if (slot.zoneId && slot.variants) {
4105
+ const defaultVariant = slot.variants.find((v) => v.isDefault);
4106
+ childrenToRender = defaultVariant?.children || [];
4107
+ } else {
4108
+ childrenToRender = Array.isArray(slot.children) ? slot.children : [];
4109
+ }
4110
+ const safeChildren = Array.isArray(childrenToRender) ? childrenToRender : [];
4111
+ slotChildren[slot.slotId] = /* @__PURE__ */ jsx(
4112
+ "div",
4113
+ {
4114
+ "data-slot-id": slot.slotId,
4115
+ "data-amplifyup-slot": slot.slotId,
4116
+ "data-zone-id": slot.zoneId || void 0,
4117
+ "data-parent-node-id": node.id,
4118
+ "data-parent-component-id": node.componentId,
4119
+ children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child) }, child.id))
4120
+ },
4121
+ slot.slotId
4122
+ );
4123
+ }
4208
4124
  }
4209
- } else {
4210
- const text = formatFieldValue(fieldValue);
4211
- html = text ? markdownToHtml(text) : "";
4212
- }
4213
- if (!html) {
4214
- return fallback ? /* @__PURE__ */ jsx(Tag, { className, children: fallback }) : null;
4215
- }
4216
- if (composerEdit && canEdit) {
4125
+ const slotsToPass = Object.keys(slotChildren).length > 0 ? slotChildren : void 0;
4217
4126
  return /* @__PURE__ */ jsx(
4218
- ComposerInlineHtml,
4127
+ "div",
4219
4128
  {
4220
- name: editName,
4221
- html,
4222
- sourceValue: typeof fieldValue === "string" ? fieldValue : html,
4223
- className,
4224
- Tag,
4225
- layoutNodeId: context?.layoutNodeId,
4226
- componentId: context?.componentId
4129
+ "data-node-id": node.id,
4130
+ "data-amplifyup-node": "",
4131
+ "data-component-id": node.componentId,
4132
+ "data-amplifyup-component": node.componentId,
4133
+ children: /* @__PURE__ */ jsx(
4134
+ ComponentRenderBoundary,
4135
+ {
4136
+ componentId: node.componentId,
4137
+ nodeId: node.id,
4138
+ children: /* @__PURE__ */ jsx(
4139
+ NodeWithFields,
4140
+ {
4141
+ nodeId: node.id,
4142
+ componentId: node.componentId,
4143
+ props: nodeRenderProps(node),
4144
+ slots: slotsToPass,
4145
+ renderComponent,
4146
+ composerPreview
4147
+ }
4148
+ )
4149
+ }
4150
+ )
4227
4151
  }
4228
4152
  );
4153
+ };
4154
+ const rawTree = pageConfig.layoutTree;
4155
+ const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];
4156
+ if (layoutTree.length === 0) {
4157
+ 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." }) }) });
4229
4158
  }
4230
- return /* @__PURE__ */ jsx(Tag, { className, dangerouslySetInnerHTML: { __html: html } });
4231
- }
4232
- function isPortableImageValue(value) {
4233
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
4234
- const url = value.url;
4235
- return typeof url === "string" && url.trim().length > 0;
4236
- }
4237
- function imageSrc(value) {
4238
- if (typeof value === "string") return value.trim();
4239
- if (isPortableImageValue(value)) return value.url.trim();
4240
- return "";
4159
+ return /* @__PURE__ */ jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsx(
4160
+ "div",
4161
+ {
4162
+ "data-slot-id": "main",
4163
+ "data-amplifyup-slot": "main",
4164
+ "data-slot-source": "page",
4165
+ "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
4166
+ children: layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id))
4167
+ }
4168
+ ) : layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id)) });
4241
4169
  }
4242
- function Image(props) {
4243
- const {
4244
- value,
4245
- name,
4246
- alt: altProp,
4247
- className,
4248
- width: widthProp,
4249
- height: heightProp,
4250
- ...imgProps
4251
- } = props;
4252
- const context = useContext(ComponentContext);
4253
- const hasExplicitValue = hasOwnProp(props, "value");
4254
- const sourceValue = hasExplicitValue ? value : name ? context?.props?.[name] : void 0;
4255
- const fieldValue = useFieldOverlay(
4256
- context?.layoutNodeId,
4257
- inferEditName(name, context?.props, sourceValue),
4258
- sourceValue
4259
- );
4260
- const composerEdit = useComposerEditMode();
4261
- const editName = inferEditName(name, context?.props, sourceValue);
4262
- warnProjectionProps("Image", name, hasExplicitValue);
4263
- if (!context && !hasExplicitValue) {
4264
- console.warn(
4265
- "[AmplifyUp SDK] Image needs a `value` prop (or deprecated `name` alone)"
4266
- );
4267
- return null;
4268
- }
4269
- const src = imageSrc(fieldValue);
4270
- const portable = isPortableImageValue(fieldValue) ? fieldValue : null;
4271
- const alt = altProp ?? portable?.alt ?? "";
4272
- const width = widthProp ?? portable?.width;
4273
- const height = heightProp ?? portable?.height;
4274
- const fieldAttrs = composerEdit && editName ? {
4275
- "data-amplifyup-field": editName,
4276
- "data-amplifyup-field-type": "image",
4277
- "data-amplifyup-node-id": context?.layoutNodeId || void 0
4278
- } : null;
4279
- if (!src) {
4280
- if (!composerEdit || !fieldAttrs) return null;
4281
- return /* @__PURE__ */ jsx(
4282
- "span",
4283
- {
4284
- ...fieldAttrs,
4285
- className,
4286
- role: "img",
4287
- "aria-label": alt || editName || "Image",
4288
- style: {
4289
- display: "inline-block",
4290
- minWidth: width ? void 0 : 48,
4291
- minHeight: height ? void 0 : 48,
4292
- width: width || void 0,
4293
- height: height || void 0,
4294
- background: "color-mix(in srgb, currentColor 8%, transparent)",
4295
- outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
4296
- outlineOffset: 2
4297
- }
4298
- }
4299
- );
4300
- }
4301
- return /* @__PURE__ */ jsx(
4302
- "img",
4170
+ var AmplifyRenderer = memo(AmplifyRendererInner);
4171
+ function AddComponentIcon({ size = 28 }) {
4172
+ return /* @__PURE__ */ jsxs(
4173
+ "svg",
4303
4174
  {
4304
- ...imgProps,
4305
- ...fieldAttrs,
4306
- src,
4307
- alt,
4308
- className,
4309
- width,
4310
- height
4175
+ width: size,
4176
+ height: size,
4177
+ viewBox: "0 0 24 24",
4178
+ fill: "none",
4179
+ xmlns: "http://www.w3.org/2000/svg",
4180
+ "aria-hidden": "true",
4181
+ focusable: "false",
4182
+ children: [
4183
+ /* @__PURE__ */ jsx(
4184
+ "rect",
4185
+ {
4186
+ x: "3",
4187
+ y: "3",
4188
+ width: "18",
4189
+ height: "18",
4190
+ rx: "4",
4191
+ stroke: "currentColor",
4192
+ strokeWidth: "1.75"
4193
+ }
4194
+ ),
4195
+ /* @__PURE__ */ jsx(
4196
+ "path",
4197
+ {
4198
+ d: "M12 8v8M8 12h8",
4199
+ stroke: "currentColor",
4200
+ strokeWidth: "1.75",
4201
+ strokeLinecap: "round"
4202
+ }
4203
+ )
4204
+ ]
4311
4205
  }
4312
4206
  );
4313
4207
  }
4314
- function nodeRenderProps(node) {
4315
- return {
4316
- ...node.settings || {},
4317
- ...node.props || {}
4318
- };
4319
- }
4320
- var ComponentRenderBoundary = class extends Component {
4321
- constructor() {
4322
- super(...arguments);
4323
- this.state = { error: null };
4324
- }
4325
- static getDerivedStateFromError(error) {
4326
- return { error };
4327
- }
4328
- componentDidCatch(error) {
4329
- console.error(
4330
- `[AmplifyRenderer] "${this.props.componentId}" (${this.props.nodeId}) threw while rendering:`,
4331
- error
4332
- );
4333
- }
4334
- render() {
4335
- if (this.state.error) {
4336
- if (process.env.NODE_ENV !== "production") {
4337
- return /* @__PURE__ */ jsxs(
4338
- "div",
4208
+ function EmptySlotHint({
4209
+ label = "Click here to Add Component",
4210
+ compact = false
4211
+ }) {
4212
+ const iconSize = compact ? 20 : 28;
4213
+ return /* @__PURE__ */ jsxs(
4214
+ "div",
4215
+ {
4216
+ className: "amplifyup-slot-placeholder",
4217
+ style: {
4218
+ display: "flex",
4219
+ flexDirection: "column",
4220
+ alignItems: "center",
4221
+ justifyContent: "center",
4222
+ gap: compact ? 8 : 12,
4223
+ minHeight: compact ? 60 : void 0,
4224
+ width: "100%",
4225
+ pointerEvents: "none",
4226
+ textAlign: "center",
4227
+ boxSizing: "border-box",
4228
+ padding: compact ? "12px 8px" : "24px 16px"
4229
+ },
4230
+ children: [
4231
+ /* @__PURE__ */ jsx(
4232
+ "span",
4339
4233
  {
4340
- "data-amplifyup-render-error": "",
4341
- "data-component-id": this.props.componentId,
4342
- "data-node-id": this.props.nodeId,
4234
+ className: "amplifyup-slot-placeholder-icon",
4343
4235
  style: {
4344
- margin: "1rem 0",
4345
- padding: "0.75rem 1rem",
4346
- border: "1px solid #fca5a5",
4347
- borderRadius: "0.5rem",
4348
- background: "#fef2f2",
4349
- color: "#991b1b",
4350
- fontSize: "0.875rem"
4236
+ display: "inline-flex",
4237
+ alignItems: "center",
4238
+ justifyContent: "center",
4239
+ width: compact ? 36 : 48,
4240
+ height: compact ? 36 : 48,
4241
+ borderRadius: 12,
4242
+ border: "1.5px dashed currentColor",
4243
+ opacity: 0.9
4351
4244
  },
4352
- children: [
4353
- 'Component "',
4354
- this.props.componentId,
4355
- '" failed to render:',
4356
- " ",
4357
- this.state.error.message
4358
- ]
4245
+ children: /* @__PURE__ */ jsx(AddComponentIcon, { size: iconSize })
4359
4246
  }
4360
- );
4361
- }
4362
- return null;
4247
+ ),
4248
+ /* @__PURE__ */ jsx(
4249
+ "span",
4250
+ {
4251
+ className: "amplifyup-slot-placeholder-label",
4252
+ style: {
4253
+ fontSize: compact ? 13 : 15,
4254
+ fontWeight: 600,
4255
+ lineHeight: 1.3,
4256
+ letterSpacing: "0.01em"
4257
+ },
4258
+ children: label
4259
+ }
4260
+ )
4261
+ ]
4363
4262
  }
4364
- return this.props.children;
4365
- }
4366
- };
4367
- function LiveNodeRender({
4368
- nodeId,
4369
- componentId,
4370
- props,
4371
- slots,
4372
- renderComponent
4373
- }) {
4374
- const liveProps = useLiveComposerProps(nodeId, props);
4375
- return /* @__PURE__ */ jsx(Fragment, { children: renderComponent(componentId, liveProps, slots, {
4376
- layoutNodeId: nodeId
4377
- }) });
4263
+ );
4378
4264
  }
4379
- function AmplifyRendererInner({
4265
+ function defaultPreviewCanvas() {
4266
+ return /* @__PURE__ */ jsx(
4267
+ "div",
4268
+ {
4269
+ "data-slot-id": "main",
4270
+ "data-amplifyup-slot": "main",
4271
+ "data-slot-source": "page",
4272
+ "data-slot-empty": "true",
4273
+ style: { minHeight: "50vh", width: "100%", display: "block", position: "relative" },
4274
+ children: /* @__PURE__ */ jsx(EmptySlotHint, {})
4275
+ }
4276
+ );
4277
+ }
4278
+ function hasAmplifyLayout(pageConfig) {
4279
+ return !!(pageConfig && pageConfig.id !== "default" && Array.isArray(pageConfig.layoutTree) && pageConfig.layoutTree.length > 0);
4280
+ }
4281
+ function normalizeRoutePath(route) {
4282
+ if (!route) return "";
4283
+ const path = route.split("?")[0] || "/";
4284
+ if (path.length > 1 && path.endsWith("/")) return path.replace(/\/+$/, "");
4285
+ return path || "/";
4286
+ }
4287
+ function samePageRoute(a, b) {
4288
+ const ar = normalizeRoutePath(a?.route);
4289
+ const br = normalizeRoutePath(b?.route);
4290
+ return !!ar && !!br && ar === br;
4291
+ }
4292
+ function layoutTreesEqual(a, b) {
4293
+ if (!a || !b) return a === b;
4294
+ return JSON.stringify(a.layoutTree) === JSON.stringify(b.layoutTree);
4295
+ }
4296
+ function AmplifyPageContent({
4297
+ fallback,
4380
4298
  renderComponent,
4381
- pageConfig: serverPageConfig,
4299
+ pageConfig: pageConfigProp,
4300
+ forceComposerPreview = false,
4301
+ previewLoadingComponent,
4302
+ previewEmptyComponent,
4382
4303
  loadingComponent,
4383
- errorComponent,
4384
4304
  emptyComponent,
4385
- composerPreview
4305
+ errorComponent
4386
4306
  }) {
4387
- const clientHook = useAmplifyUp();
4388
- const pageConfig = serverPageConfig || clientHook.pageConfig || null;
4389
- const loading = serverPageConfig ? false : clientHook.loading;
4390
- const error = serverPageConfig ? null : clientHook.error;
4391
- const contentSource = clientHook.source;
4392
- const [variantCache, setVariantCache] = useState({});
4393
- useEffect(() => {
4394
- if (typeof window !== "undefined") {
4395
- if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
4396
- return;
4397
- }
4398
- if (window.location.pathname.includes("/api/pages/proxy")) {
4399
- return;
4400
- }
4401
- if (window.self !== window.top) {
4402
- return;
4403
- }
4404
- const urlParams = new URLSearchParams(window.location.search);
4405
- if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
4406
- return;
4407
- }
4408
- if (window.location.href.includes("preview=true") || window.location.href.includes("visualEditor=true")) {
4409
- return;
4307
+ const client = useAmplifyUp();
4308
+ const preview = forceComposerPreview || isComposerPreview();
4309
+ const clientLayout = hasAmplifyLayout(client.pageConfig) ? client.pageConfig : null;
4310
+ const serverLayout = hasAmplifyLayout(pageConfigProp) ? pageConfigProp : null;
4311
+ let pageConfig = serverLayout ?? clientLayout;
4312
+ if (preview && clientLayout) {
4313
+ pageConfig = clientLayout;
4314
+ } else if (clientLayout && serverLayout && samePageRoute(clientLayout, serverLayout) && !layoutTreesEqual(clientLayout, serverLayout)) {
4315
+ pageConfig = clientLayout;
4316
+ } else if (clientLayout && !serverLayout) {
4317
+ pageConfig = clientLayout;
4318
+ }
4319
+ const loading = !hasAmplifyLayout(pageConfig) && client.loading;
4320
+ const error = hasAmplifyLayout(pageConfig) ? null : pageConfigProp ? null : client.error;
4321
+ const previewCanvas = previewEmptyComponent ?? defaultPreviewCanvas();
4322
+ const previewLoading = previewLoadingComponent ?? previewCanvas;
4323
+ if (preview) {
4324
+ return /* @__PURE__ */ jsx(
4325
+ AmplifyRenderer,
4326
+ {
4327
+ pageConfig: pageConfig ?? void 0,
4328
+ renderComponent,
4329
+ composerPreview: true,
4330
+ loadingComponent: loadingComponent ?? previewLoading,
4331
+ emptyComponent: emptyComponent ?? previewCanvas,
4332
+ errorComponent: errorComponent ?? (() => previewCanvas)
4410
4333
  }
4334
+ );
4335
+ }
4336
+ if (loading) {
4337
+ return loadingComponent ? /* @__PURE__ */ jsx(Fragment, { children: loadingComponent }) : null;
4338
+ }
4339
+ if (error) {
4340
+ if (errorComponent) return /* @__PURE__ */ jsx(Fragment, { children: errorComponent(error) });
4341
+ return fallback ? /* @__PURE__ */ jsx(Fragment, { children: fallback }) : null;
4342
+ }
4343
+ if (!hasAmplifyLayout(pageConfig)) {
4344
+ return fallback ? /* @__PURE__ */ jsx(Fragment, { children: fallback }) : null;
4345
+ }
4346
+ return /* @__PURE__ */ jsx(
4347
+ AmplifyRenderer,
4348
+ {
4349
+ pageConfig: pageConfig ?? void 0,
4350
+ renderComponent,
4351
+ loadingComponent,
4352
+ emptyComponent,
4353
+ errorComponent
4411
4354
  }
4412
- if (pageConfig && pageConfig.id !== "default") {
4413
- trackContentView(pageConfig.id, "page", {
4414
- route: pageConfig.route,
4415
- title: pageConfig.title
4416
- });
4355
+ );
4356
+ }
4357
+ var ComponentContext = createContext(null);
4358
+ function ComponentContextProvider({
4359
+ children,
4360
+ slots,
4361
+ props,
4362
+ layoutNodeId,
4363
+ componentId
4364
+ }) {
4365
+ const next = hydratePageResourceProps(props || {}, getPageResourceFields());
4366
+ return /* @__PURE__ */ jsx(
4367
+ ComponentContext.Provider,
4368
+ {
4369
+ value: { slots, props: next, layoutNodeId, componentId },
4370
+ children
4417
4371
  }
4418
- }, [pageConfig]);
4419
- useEffect(() => {
4420
- if (!pageConfig?.eventTracking?.length) return;
4421
- const sdk = getSDK();
4422
- if (!sdk.isInitialized()) return;
4423
- sdk.setupEventTracking(pageConfig.eventTracking);
4424
- const t1 = window.setTimeout(() => {
4425
- sdk.setupComponentViewTracking();
4426
- refreshEventTracking();
4427
- }, 200);
4428
- const t2 = window.setTimeout(() => {
4429
- sdk.setupComponentViewTracking();
4430
- refreshEventTracking();
4431
- }, 1e3);
4432
- return () => {
4433
- window.clearTimeout(t1);
4434
- window.clearTimeout(t2);
4435
- };
4436
- }, [pageConfig?.id, pageConfig?.eventTracking]);
4437
- useEffect(() => {
4438
- if (!pageConfig) return;
4439
- const t = window.setTimeout(() => {
4440
- getSDK().setupComponentViewTracking();
4441
- refreshEventTracking();
4442
- }, 100);
4443
- return () => window.clearTimeout(t);
4444
- }, [variantCache, pageConfig?.id]);
4445
- useEffect(() => {
4446
- const isVisualEditorMode = () => {
4447
- if (typeof window === "undefined") return false;
4448
- const urlParams = new URLSearchParams(window.location.search);
4449
- if (urlParams.get("visualEditor") === "true" || urlParams.get("preview") === "true") {
4450
- return true;
4451
- }
4452
- const inIframe = window.self !== window.top;
4453
- if (!inIframe) return false;
4454
- try {
4455
- if (document.referrer) {
4456
- const referrerUrl = new URL(document.referrer);
4457
- if (referrerUrl.hostname === "localhost" && referrerUrl.port === "5001") {
4458
- return true;
4459
- }
4460
- }
4461
- } catch (e) {
4462
- }
4372
+ );
4373
+ }
4374
+ function Slot({
4375
+ name,
4376
+ className
4377
+ }) {
4378
+ const context = useContext(ComponentContext);
4379
+ if (!context) {
4380
+ console.warn(`[AmplifyUp SDK] Slot "${name}" used outside ComponentContextProvider`);
4381
+ return null;
4382
+ }
4383
+ const slotContent = context.slots?.[name];
4384
+ const isEmpty = !slotContent;
4385
+ const isEditorMode = typeof window !== "undefined" && // Check for preview mode flag set by proxy
4386
+ (window.__AMPLIFYUP_PREVIEW_MODE__ === true || // Check URL query params
4387
+ new URLSearchParams(window.location.search).get("preview") === "true" || new URLSearchParams(window.location.search).get("visualEditor") === "true" || // Check if in iframe from orchestrator (check referrer hostname/port)
4388
+ window.self !== window.top && document.referrer && (() => {
4389
+ try {
4390
+ const referrerUrl = new URL(document.referrer);
4391
+ return referrerUrl.hostname === "localhost" && referrerUrl.port === "5001" || referrerUrl.pathname.includes("/dashboard/composer") || referrerUrl.pathname.includes("/dashboard/routes/");
4392
+ } catch {
4463
4393
  return false;
4464
- };
4465
- if (!isVisualEditorMode()) {
4466
- return;
4467
- }
4468
- if (!pageConfig) {
4469
- return;
4470
4394
  }
4471
- const rawTree2 = pageConfig.layoutTree;
4472
- const normalizedTree = Array.isArray(rawTree2) ? rawTree2 : rawTree2 && typeof rawTree2 === "object" && rawTree2.componentId ? [rawTree2] : [];
4473
- if (normalizedTree.length === 0) {
4474
- if (typeof window !== "undefined" && window.parent && window.parent !== window) {
4475
- window.parent.postMessage({
4476
- type: "COMPONENT_TREE",
4477
- tree: []
4478
- }, "*");
4479
- }
4480
- return;
4395
+ })());
4396
+ sdkDebugOnce(
4397
+ `slot:${context.layoutNodeId || ""}:${name}`,
4398
+ `[Slot:${name}]`,
4399
+ {
4400
+ isEmpty,
4401
+ isEditorMode,
4402
+ availableSlotKeys: context.slots ? Object.keys(context.slots) : []
4481
4403
  }
4482
- const convertLayoutTreeToComponentTree = (nodes, parentId = null) => {
4483
- const result = [];
4484
- nodes.forEach((node) => {
4485
- if (!node) return;
4486
- const treeNode = {
4487
- id: node.id,
4488
- componentId: node.componentId,
4489
- componentName: node.componentId,
4490
- // Will be enhanced with actual name if available
4491
- parentId,
4492
- children: []
4493
- };
4494
- if (node.slots && node.slots.length > 0) {
4495
- node.slots.forEach((slot) => {
4496
- const kids = Array.isArray(slot?.children) ? slot.children : [];
4497
- if (kids.length > 0) {
4498
- const slotChildren = convertLayoutTreeToComponentTree(kids, node.id);
4499
- treeNode.children.push(...slotChildren);
4500
- }
4501
- });
4404
+ );
4405
+ return /* @__PURE__ */ jsx(
4406
+ "div",
4407
+ {
4408
+ className,
4409
+ "data-slot-name": name,
4410
+ "data-slot-id": name,
4411
+ "data-amplifyup-slot": name,
4412
+ "data-slot-empty": isEmpty ? "true" : void 0,
4413
+ "data-parent-node-id": context.layoutNodeId || void 0,
4414
+ "data-parent-component-id": context.componentId || void 0,
4415
+ style: {
4416
+ // Only add minimum height when empty (for drop target visibility)
4417
+ minHeight: isEmpty ? "60px" : void 0,
4418
+ // Only add position relative for overlay positioning in editor mode
4419
+ position: isEditorMode ? "relative" : void 0
4420
+ },
4421
+ children: isEmpty ? isEditorMode ? /* @__PURE__ */ jsx(EmptySlotHint, { compact: true }) : /* @__PURE__ */ jsx(
4422
+ "div",
4423
+ {
4424
+ className: "amplifyup-slot-placeholder",
4425
+ style: {
4426
+ minHeight: "60px",
4427
+ color: "#9ca3af",
4428
+ fontSize: "14px",
4429
+ pointerEvents: "none"
4430
+ },
4431
+ children: `Empty slot: ${name}`
4502
4432
  }
4503
- result.push(treeNode);
4504
- });
4505
- return result;
4506
- };
4507
- const layoutTree2 = normalizedTree;
4508
- const componentTree = convertLayoutTreeToComponentTree(layoutTree2);
4509
- const describeLayoutTree = (nodes, depth = 0) => {
4510
- if (!nodes?.length) return "";
4511
- const indent = " ".repeat(depth);
4512
- return nodes.map((n) => {
4513
- const slotsDesc = (n.slots || []).map((s) => {
4514
- const childCount = Array.isArray(s.children) ? s.children.length : 0;
4515
- const childrenDesc = childCount > 0 ? describeLayoutTree(s.children || [], depth + 1) : "";
4516
- return `${indent} slot "${s.slotId || s.slotName || "?"}" (${childCount} children)${childrenDesc ? "\n" + childrenDesc : ""}`;
4517
- }).join("\n");
4518
- return `${indent}- ${n.id} (${n.componentId || "?"})${slotsDesc ? "\n" + slotsDesc : ""}`;
4519
- }).join("\n");
4520
- };
4521
- sdkDebugOnce(
4522
- `layoutTree:${pageConfig?.id || ""}:${pageConfig?.route || ""}`,
4523
- "[AmplifyRenderer] layoutTree received:\n%s",
4524
- describeLayoutTree(layoutTree2)
4433
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
4434
+ isEditorMode && /* @__PURE__ */ jsxs(
4435
+ "div",
4436
+ {
4437
+ className: "amplifyup-slot-label",
4438
+ style: {
4439
+ position: "absolute",
4440
+ top: "4px",
4441
+ left: "4px",
4442
+ fontSize: "10px",
4443
+ color: "#9ca3af",
4444
+ pointerEvents: "none",
4445
+ zIndex: 1
4446
+ },
4447
+ children: [
4448
+ "Slot: ",
4449
+ name
4450
+ ]
4451
+ }
4452
+ ),
4453
+ slotContent
4454
+ ] })
4455
+ }
4456
+ );
4457
+ }
4458
+ function isComposerEditMode() {
4459
+ if (typeof window === "undefined") return false;
4460
+ return isComposerPreview() && window.__AMPLIFYUP_EVENT_SELECTION__ !== true;
4461
+ }
4462
+ function isLikelyNonTextFieldName(name) {
4463
+ const n = name.toLowerCase();
4464
+ return n.includes("href") || n.includes("url") || n.includes("src") || n.includes("image") || n.includes("icon") || n.includes("overlay") || n.includes("color");
4465
+ }
4466
+ var COMPOSER_EMPTY_FIELD_HINT = "Click here to edit this field";
4467
+ var EMPTY_FIELD_PLACEHOLDERS = /* @__PURE__ */ new Set([
4468
+ COMPOSER_EMPTY_FIELD_HINT,
4469
+ "Click here to enter text",
4470
+ "Sample Title",
4471
+ "Sample Heading",
4472
+ "Sample Subheading"
4473
+ ]);
4474
+ function isComposerEmptyText(value) {
4475
+ if (value === void 0 || value === null) return true;
4476
+ if (typeof value !== "string") return false;
4477
+ const trimmed = value.trim();
4478
+ return trimmed === "" || EMPTY_FIELD_PLACEHOLDERS.has(trimmed);
4479
+ }
4480
+ function canInlineEdit(name, value) {
4481
+ if (!name || isLikelyNonTextFieldName(name)) return false;
4482
+ if (value !== void 0 && value !== null && typeof value === "object") {
4483
+ return false;
4484
+ }
4485
+ if (typeof value === "boolean") return false;
4486
+ return true;
4487
+ }
4488
+ function canInlineEditRichText(name, value) {
4489
+ if (!name || isLikelyNonTextFieldName(name)) return false;
4490
+ if (typeof value === "boolean") return false;
4491
+ if (value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value)) {
4492
+ return false;
4493
+ }
4494
+ return true;
4495
+ }
4496
+ function subscribeComposerEdit(onStoreChange) {
4497
+ if (typeof window === "undefined") return () => {
4498
+ };
4499
+ window.addEventListener("amplifyup:composer-edit", onStoreChange);
4500
+ return () => window.removeEventListener("amplifyup:composer-edit", onStoreChange);
4501
+ }
4502
+ function useComposerEditMode() {
4503
+ return useSyncExternalStore(
4504
+ subscribeComposerEdit,
4505
+ isComposerEditMode,
4506
+ () => false
4507
+ );
4508
+ }
4509
+ function useBoundField(field, value, name, component) {
4510
+ const bound = resolveEditableBinding(field, value, name);
4511
+ if (bound.requiresName) {
4512
+ console.warn(
4513
+ `[AmplifyUp SDK] ${component} value={\u2026} requires name="\u2026" for computed values`
4525
4514
  );
4526
- if (typeof window !== "undefined" && window.parent && window.parent !== window) {
4527
- const message = {
4528
- type: "COMPONENT_TREE",
4529
- tree: componentTree
4530
- };
4531
- window.parent.postMessage(message, "*");
4532
- sdkDebug(
4533
- "[AmplifyRenderer] Sent component tree from layoutTree:",
4534
- componentTree.length,
4535
- "root components"
4536
- );
4515
+ }
4516
+ const fieldValue = useComposerFieldValue(bound.name, bound.value);
4517
+ return { editName: bound.name, fieldValue };
4518
+ }
4519
+ function commitComposerField(layoutNodeId, componentId, name, value) {
4520
+ if (typeof window === "undefined" || window.parent === window) return;
4521
+ window.parent.postMessage(
4522
+ {
4523
+ type: "AMPLIFYUP_FIELD_COMMIT",
4524
+ layoutNodeId,
4525
+ componentId,
4526
+ fieldName: name,
4527
+ value
4528
+ },
4529
+ "*"
4530
+ );
4531
+ }
4532
+ function looksLikeRichCanvasField(name) {
4533
+ const tokens = String(name || "").replace(/([a-z])([A-Z])/g, "$1 $2").split(/[._\-\s/]+/).map((s) => s.toLowerCase()).filter(Boolean);
4534
+ const last = tokens[tokens.length - 1] || "";
4535
+ return last === "body" || last === "content" || last === "copy" || last === "excerpt" || last === "markdown" || last === "richtext";
4536
+ }
4537
+ var lastRichTextRequestAt = 0;
4538
+ function requestCanvasRichTextEdit(opts) {
4539
+ if (typeof window === "undefined" || window.parent === window) return;
4540
+ if (!opts.name) return;
4541
+ const now = Date.now();
4542
+ if (now - lastRichTextRequestAt < 400) return;
4543
+ lastRichTextRequestAt = now;
4544
+ const rect = opts.el.getBoundingClientRect();
4545
+ window.parent.postMessage(
4546
+ {
4547
+ type: "AMPLIFYUP_RICHTEXT_EDIT",
4548
+ layoutNodeId: opts.layoutNodeId,
4549
+ componentId: opts.componentId,
4550
+ fieldName: opts.name,
4551
+ value: opts.value,
4552
+ bounds: {
4553
+ top: rect.top,
4554
+ left: rect.left,
4555
+ width: Math.max(rect.width, 240),
4556
+ height: Math.max(rect.height, 64)
4557
+ }
4558
+ },
4559
+ "*"
4560
+ );
4561
+ }
4562
+ function placeCaret(el, atStart = false) {
4563
+ const range = document.createRange();
4564
+ range.selectNodeContents(el);
4565
+ range.collapse(atStart);
4566
+ const sel = window.getSelection();
4567
+ sel?.removeAllRanges();
4568
+ sel?.addRange(range);
4569
+ }
4570
+ function ComposerInlineField({
4571
+ name,
4572
+ value,
4573
+ className,
4574
+ render,
4575
+ layoutNodeId,
4576
+ componentId,
4577
+ rich = false
4578
+ }) {
4579
+ const [editing, setEditing] = useState(false);
4580
+ const [focused, setFocused] = useState(false);
4581
+ const elRef = useRef(null);
4582
+ const storedEmpty = isComposerEmptyText(value);
4583
+ const text = storedEmpty ? "" : formatFieldValue(value);
4584
+ const hint = COMPOSER_EMPTY_FIELD_HINT;
4585
+ const idleDisplay = storedEmpty ? hint : text;
4586
+ const showRendered = !!render && !editing && !storedEmpty;
4587
+ const showHint = storedEmpty && !focused && !showRendered;
4588
+ useEffect(() => {
4589
+ const el = elRef.current;
4590
+ if (!el || showRendered) return;
4591
+ if (document.activeElement === el) return;
4592
+ if (el.querySelector(
4593
+ "input, button, select, textarea, form, svg, iframe, img"
4594
+ )) {
4595
+ return;
4537
4596
  }
4538
- }, [pageConfig]);
4597
+ const next = idleDisplay || "";
4598
+ if (el.textContent !== next) el.textContent = next;
4599
+ }, [idleDisplay, showRendered]);
4539
4600
  useEffect(() => {
4540
- if (!pageConfig?.layoutTree || !pageConfig.layoutId) return;
4541
- const roots = pageConfig.layoutTree;
4542
- const fromEdge = contentSource === "edge" || roots.some((n) => n._contentSource === "edge");
4543
- if (fromEdge) return;
4544
- const sdk = getSDK();
4545
- const instanceId = sdk.getInstanceId();
4546
- const decisionApiUrl = sdk.getDecisionApiUrl();
4547
- const orchestratorUrl = sdk.getOrchestratorUrl();
4548
- if (!decisionApiUrl && !orchestratorUrl || !instanceId) return;
4549
- const fetchVariants = async () => {
4550
- const nodes = pageConfig.layoutTree;
4551
- const newCache = {};
4552
- const findZonedSlots = (nodeList) => {
4553
- const results = [];
4554
- for (const node of nodeList) {
4555
- if (node.slots) {
4556
- for (const slot of node.slots) {
4557
- if (slot.zoneId && slot.variants) {
4558
- const key = `${node.id}-${slot.slotId}`;
4559
- results.push({ node, slot, key });
4560
- }
4561
- }
4562
- }
4601
+ if (!editing || !elRef.current) return;
4602
+ const el = elRef.current;
4603
+ if (storedEmpty) {
4604
+ el.textContent = "";
4605
+ el.focus();
4606
+ placeCaret(el, true);
4607
+ return;
4608
+ }
4609
+ el.textContent = idleDisplay || "";
4610
+ el.focus();
4611
+ placeCaret(el, false);
4612
+ }, [editing, storedEmpty, idleDisplay]);
4613
+ const finishEdit = () => {
4614
+ const raw = (elRef.current?.innerText ?? "").replace(/\u00a0/g, " ").replace(/\n+$/g, "");
4615
+ const next = raw.trim() === "" ? "" : raw;
4616
+ setEditing(false);
4617
+ setFocused(false);
4618
+ if (storedEmpty && next === "") {
4619
+ if (elRef.current) elRef.current.textContent = hint;
4620
+ if (typeof value === "string" && EMPTY_FIELD_PLACEHOLDERS.has(value.trim())) {
4621
+ commitComposerField(layoutNodeId, componentId, name, "");
4622
+ }
4623
+ return;
4624
+ }
4625
+ if (next === text) return;
4626
+ commitComposerField(layoutNodeId, componentId, name, next);
4627
+ };
4628
+ const openRichEditor = (event) => {
4629
+ if (!isComposerEditMode() || !elRef.current) return;
4630
+ event?.preventDefault();
4631
+ event?.stopPropagation();
4632
+ const source = typeof value === "string" && !storedEmpty ? value : text || "";
4633
+ requestCanvasRichTextEdit({
4634
+ el: elRef.current,
4635
+ layoutNodeId,
4636
+ componentId,
4637
+ name,
4638
+ value: source
4639
+ });
4640
+ };
4641
+ return /* @__PURE__ */ jsx(
4642
+ "span",
4643
+ {
4644
+ ref: elRef,
4645
+ className,
4646
+ "data-amplifyup-field": name,
4647
+ "data-amplifyup-field-type": rich ? "richtext" : "text",
4648
+ "data-amplifyup-node-id": layoutNodeId || void 0,
4649
+ "data-amplifyup-empty-hint": showHint ? "true" : void 0,
4650
+ contentEditable: !rich && !showRendered,
4651
+ suppressContentEditableWarning: true,
4652
+ role: "textbox",
4653
+ tabIndex: 0,
4654
+ onMouseDown: (event) => {
4655
+ event.stopPropagation();
4656
+ },
4657
+ onClick: (event) => {
4658
+ if (!isComposerEditMode()) return;
4659
+ const link = event.target?.closest?.("a");
4660
+ if (link) event.preventDefault();
4661
+ if (rich) {
4662
+ openRichEditor(event);
4663
+ return;
4664
+ }
4665
+ if (render) {
4666
+ setEditing(true);
4667
+ }
4668
+ },
4669
+ onFocus: () => {
4670
+ if (!isComposerEditMode()) return;
4671
+ if (rich) {
4672
+ openRichEditor();
4673
+ return;
4674
+ }
4675
+ setFocused(true);
4676
+ const el = elRef.current;
4677
+ if (el && storedEmpty) {
4678
+ el.textContent = "";
4679
+ placeCaret(el, true);
4680
+ }
4681
+ if (render) setEditing(true);
4682
+ },
4683
+ onBlur: rich ? void 0 : finishEdit,
4684
+ onKeyDown: (event) => {
4685
+ event.stopPropagation();
4686
+ if (event.key === "Enter" && !event.shiftKey) {
4687
+ event.preventDefault();
4688
+ event.currentTarget.blur();
4689
+ }
4690
+ if (event.key === "Escape") {
4691
+ event.preventDefault();
4692
+ if (elRef.current) elRef.current.textContent = idleDisplay || "";
4693
+ setEditing(false);
4694
+ setFocused(false);
4695
+ event.currentTarget.blur();
4563
4696
  }
4564
- return results;
4565
- };
4566
- const zonedSlots = findZonedSlots(nodes);
4567
- await Promise.all(
4568
- zonedSlots.map(async ({ node, slot, key }) => {
4569
- try {
4570
- let personalizationUrl;
4571
- if (decisionApiUrl) {
4572
- const url = new URL(decisionApiUrl);
4573
- url.searchParams.set("instanceId", instanceId);
4574
- const zoneId = slot.zoneId;
4575
- if (zoneId) {
4576
- url.searchParams.set("zoneId", zoneId);
4577
- }
4578
- const campaignId = slot.campaignId;
4579
- if (campaignId) {
4580
- url.searchParams.set("campaignId", campaignId);
4581
- }
4582
- personalizationUrl = url.toString();
4583
- } else if (orchestratorUrl) {
4584
- personalizationUrl = `${orchestratorUrl}/api/personalization/variant?instanceId=${encodeURIComponent(instanceId)}&layoutId=${encodeURIComponent(pageConfig.layoutId)}&slotId=${encodeURIComponent(slot.slotId)}&nodeId=${encodeURIComponent(node.id)}`;
4585
- } else {
4586
- return;
4587
- }
4588
- const response = await fetch(personalizationUrl);
4589
- if (response.ok) {
4590
- const data = await response.json();
4591
- if (data.variantId && data.componentTree) {
4592
- newCache[key] = {
4593
- variantId: data.variantId,
4594
- children: data.componentTree
4595
- };
4596
- if (typeof window !== "undefined") {
4597
- if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
4598
- return;
4599
- }
4600
- if (window.location.pathname.includes("/api/pages/proxy")) {
4601
- return;
4602
- }
4603
- if (window.self !== window.top) {
4604
- return;
4605
- }
4606
- }
4607
- sdk.track("Zone Impression", {
4608
- zoneId: slot.zoneId,
4609
- variantId: data.variantId,
4610
- layoutId: pageConfig.layoutId,
4611
- route: pageConfig.route
4612
- });
4613
- } else {
4614
- const defaultVariant = slot.variants?.find((v) => v.isDefault);
4615
- if (defaultVariant) {
4616
- newCache[key] = {
4617
- variantId: defaultVariant.variantId,
4618
- children: defaultVariant.children
4619
- };
4620
- }
4621
- }
4622
- }
4623
- } catch (err) {
4624
- console.error(`Error fetching variant for slot ${key}:`, err);
4625
- const defaultVariant = slot.variants?.find((v) => v.isDefault);
4626
- if (defaultVariant) {
4627
- newCache[key] = {
4628
- variantId: defaultVariant.variantId,
4629
- children: defaultVariant.children
4630
- };
4631
- }
4632
- }
4633
- })
4634
- );
4635
- setVariantCache(newCache);
4636
- };
4637
- fetchVariants();
4638
- }, [pageConfig?.layoutTree, pageConfig?.layoutId, pageConfig?.route, contentSource]);
4639
- if (loading) {
4640
- return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse", children: [
4641
- /* @__PURE__ */ jsx("div", { className: "h-8 bg-gray-200 rounded w-1/4 mb-4" }),
4642
- /* @__PURE__ */ jsx("div", { className: "h-4 bg-gray-200 rounded w-1/2 mb-8" }),
4643
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4644
- /* @__PURE__ */ jsx("div", { className: "h-64 bg-gray-200 rounded" }),
4645
- /* @__PURE__ */ jsx("div", { className: "h-32 bg-gray-200 rounded" })
4646
- ] })
4647
- ] }) }) }) });
4697
+ },
4698
+ style: {
4699
+ cursor: "text",
4700
+ outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
4701
+ outlineOffset: "2px",
4702
+ minWidth: "1ch",
4703
+ minHeight: "1em",
4704
+ // Do not force `inline` — that overrides Tailwind `block` and parks
4705
+ // an empty-field hint on the same line as a sibling that has a value.
4706
+ opacity: showHint ? 0.5 : void 0
4707
+ },
4708
+ children: showRendered ? render(value) : null
4709
+ }
4710
+ );
4711
+ }
4712
+ function ComposerInlineHtml({
4713
+ name,
4714
+ html,
4715
+ sourceValue,
4716
+ className,
4717
+ Tag = "div",
4718
+ layoutNodeId,
4719
+ componentId
4720
+ }) {
4721
+ const elRef = useRef(null);
4722
+ useEffect(() => {
4723
+ const el = elRef.current;
4724
+ if (!el) return;
4725
+ if (el.innerHTML !== html) el.innerHTML = html;
4726
+ }, [html]);
4727
+ const openRichEditor = (event) => {
4728
+ if (!elRef.current) return;
4729
+ event.preventDefault();
4730
+ event.stopPropagation();
4731
+ requestCanvasRichTextEdit({
4732
+ el: elRef.current,
4733
+ layoutNodeId,
4734
+ componentId,
4735
+ name,
4736
+ value: sourceValue
4737
+ });
4738
+ };
4739
+ return React2.createElement(Tag, {
4740
+ ref: elRef,
4741
+ className,
4742
+ "data-amplifyup-field": name,
4743
+ "data-amplifyup-field-type": "richtext",
4744
+ "data-amplifyup-node-id": layoutNodeId || void 0,
4745
+ dangerouslySetInnerHTML: { __html: html },
4746
+ onMouseDown: (event) => {
4747
+ event.stopPropagation();
4748
+ },
4749
+ onClick: openRichEditor,
4750
+ style: {
4751
+ cursor: "text",
4752
+ outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
4753
+ outlineOffset: "2px"
4754
+ }
4755
+ });
4756
+ }
4757
+ function formatFieldValue(value) {
4758
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4759
+ return String(value);
4648
4760
  }
4649
- if (error) {
4650
- return /* @__PURE__ */ jsx(Fragment, { children: errorComponent ? errorComponent(error) : /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxs("div", { className: "bg-red-50 border border-red-200 rounded-lg p-6", children: [
4651
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-red-900 mb-2", children: "Error Loading Page" }),
4652
- /* @__PURE__ */ jsx("p", { className: "text-red-700", children: error })
4653
- ] }) }) }) });
4761
+ if (Array.isArray(value)) {
4762
+ const parts = value.map((item) => {
4763
+ if (typeof item === "string") return item;
4764
+ if (item && typeof item === "object" && "children" in item && Array.isArray(item.children)) {
4765
+ return item.children.map((c) => typeof c?.text === "string" ? c.text : "").join("");
4766
+ }
4767
+ return "";
4768
+ }).filter(Boolean);
4769
+ if (parts.length > 0) return parts.join(" ");
4654
4770
  }
4655
- if (!pageConfig) {
4771
+ if (value && typeof value === "object") {
4772
+ return "";
4773
+ }
4774
+ return "";
4775
+ }
4776
+ function warnProjectionProps(component, hasField, hasValue, name) {
4777
+ if (process.env.NODE_ENV === "production") return;
4778
+ if (!hasField && !hasValue && !name) {
4779
+ console.warn(
4780
+ `[AmplifyUp SDK] ${component} needs field={fields.yourProp} (or value + name for computed values)`
4781
+ );
4782
+ }
4783
+ }
4784
+ function hasOwnProp(props, key) {
4785
+ return Object.prototype.hasOwnProperty.call(props, key);
4786
+ }
4787
+ function isLinkListItem(value) {
4788
+ return !!value && typeof value === "object" && !Array.isArray(value);
4789
+ }
4790
+ function renderLinkList(items, className) {
4791
+ const links = items.filter(isLinkListItem).filter((item) => {
4792
+ const label = typeof item.label === "string" ? item.label.trim() : "";
4793
+ const href = typeof item.href === "string" ? item.href.trim() : "";
4794
+ return !!(label || href);
4795
+ });
4796
+ if (links.length === 0) return null;
4797
+ return /* @__PURE__ */ jsx("span", { className, style: { display: "contents" }, children: links.map((item, index) => {
4798
+ const label = typeof item.label === "string" && item.label.trim() ? item.label : typeof item.href === "string" ? item.href : "Link";
4799
+ const href = typeof item.href === "string" && item.href.trim() ? item.href : "#";
4800
+ const style = typeof item.style === "string" ? item.style.trim() : "";
4801
+ return /* @__PURE__ */ jsx(
4802
+ "a",
4803
+ {
4804
+ href,
4805
+ target: item.openInNewTab ? "_blank" : void 0,
4806
+ rel: item.openInNewTab ? "noreferrer" : void 0,
4807
+ "data-amplifyup-action-style": style || void 0,
4808
+ children: label
4809
+ },
4810
+ typeof item._key === "string" && item._key ? item._key : `${href}-${index}`
4811
+ );
4812
+ }) });
4813
+ }
4814
+ function Field(props) {
4815
+ const { field, value, name, className, render } = props;
4816
+ const context = useContext(ComponentContext);
4817
+ const composerEdit = useComposerEditMode();
4818
+ const hasField = isFieldEnvelope(field);
4819
+ const hasValue = hasOwnProp(props, "value");
4820
+ const { editName, fieldValue } = useBoundField(field, value, name, "Field");
4821
+ warnProjectionProps("Field", hasField, hasValue, name);
4822
+ if (!hasField && !hasValue && !name) {
4656
4823
  return null;
4657
4824
  }
4658
- pageConfig.layout?.header !== false;
4659
- pageConfig.layout?.footer !== false;
4660
- const renderNode = (node) => {
4661
- const zoneIdFromProps = node.componentId === "AmplifyZone" ? String(node.props?.zoneId || "") : "";
4662
- if (node.componentId === "AmplifyZone") {
4663
- const contentSlot = node.slots?.find((s) => s.slotId === "content") || node.slots?.[0];
4664
- const children = Array.isArray(contentSlot?.children) ? contentSlot.children : [];
4825
+ const empty = isComposerEmptyText(fieldValue);
4826
+ if (composerEdit && editName && context && canInlineEdit(editName, fieldValue)) {
4827
+ return /* @__PURE__ */ jsx(
4828
+ ComposerInlineField,
4829
+ {
4830
+ name: editName,
4831
+ value: fieldValue,
4832
+ className,
4833
+ render: empty ? void 0 : render,
4834
+ layoutNodeId: context.layoutNodeId,
4835
+ componentId: context.componentId,
4836
+ rich: looksLikeRichCanvasField(editName)
4837
+ }
4838
+ );
4839
+ }
4840
+ if (empty) return null;
4841
+ if (render) {
4842
+ return /* @__PURE__ */ jsx("span", { className, style: { display: "contents" }, children: render(fieldValue) });
4843
+ }
4844
+ if (Array.isArray(fieldValue)) {
4845
+ const list = renderLinkList(fieldValue, className);
4846
+ if (list) return list;
4847
+ }
4848
+ const text = formatFieldValue(fieldValue);
4849
+ if (!text) return null;
4850
+ return /* @__PURE__ */ jsx("span", { className, children: text });
4851
+ }
4852
+ function markdownToHtml(markdown) {
4853
+ try {
4854
+ const result = marked.parse(markdown, { async: false });
4855
+ return typeof result === "string" ? result : "";
4856
+ } catch {
4857
+ return markdown;
4858
+ }
4859
+ }
4860
+ function RichText(props) {
4861
+ const {
4862
+ field,
4863
+ value,
4864
+ name,
4865
+ className,
4866
+ as: Tag = "div",
4867
+ children
4868
+ } = props;
4869
+ const context = useContext(ComponentContext);
4870
+ const composerEdit = useComposerEditMode();
4871
+ const childrenValue = typeof children === "string" || typeof children === "number" ? String(children) : void 0;
4872
+ const hasField = isFieldEnvelope(field);
4873
+ const hasValueProp = hasOwnProp(props, "value") || childrenValue !== void 0;
4874
+ const boundValue = hasOwnProp(props, "value") ? value : childrenValue !== void 0 ? childrenValue : value;
4875
+ const { editName, fieldValue } = useBoundField(
4876
+ field,
4877
+ boundValue,
4878
+ name,
4879
+ "RichText"
4880
+ );
4881
+ if (!hasField && !hasValueProp && !name) {
4882
+ return null;
4883
+ }
4884
+ warnProjectionProps("RichText", hasField, hasValueProp, name);
4885
+ const canEdit = !!context && canInlineEditRichText(editName, fieldValue);
4886
+ if (isComposerEmptyText(fieldValue)) {
4887
+ if (composerEdit && canEdit) {
4665
4888
  return /* @__PURE__ */ jsx(
4666
- "div",
4889
+ ComposerInlineField,
4667
4890
  {
4668
- "data-node-id": node.id,
4669
- "data-amplifyup-node": "",
4670
- "data-amplifyup-component": "AmplifyZone",
4671
- "data-amplifyup-zone": zoneIdFromProps || void 0,
4672
- "data-zone-id": zoneIdFromProps || void 0,
4673
- children: /* @__PURE__ */ jsx(
4674
- "div",
4675
- {
4676
- "data-slot-id": "content",
4677
- "data-amplifyup-slot": "content",
4678
- "data-parent-node-id": node.id,
4679
- "data-parent-component-id": "AmplifyZone",
4680
- children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child) }, child.id))
4681
- }
4682
- )
4891
+ name: editName,
4892
+ value: fieldValue,
4893
+ className,
4894
+ layoutNodeId: context?.layoutNodeId,
4895
+ componentId: context?.componentId,
4896
+ rich: true
4683
4897
  }
4684
4898
  );
4685
4899
  }
4686
- let slotChildren = {};
4687
- sdkDebugOnce(`node:${node.id}`, `[AmplifyRenderer] Rendering node ${node.id} (${node.componentId})`, {
4688
- hasSlots: !!(node.slots && node.slots.length > 0),
4689
- slotCount: node.slots?.length || 0,
4690
- slots: node.slots?.map((s) => ({ slotId: s.slotId, zoneId: s.zoneId, childrenCount: s.children?.length || 0 }))
4691
- });
4692
- if (node.slots && node.slots.length > 0) {
4693
- for (const slot of node.slots) {
4694
- const slotKey = `${node.id}-${slot.slotId}`;
4695
- let childrenToRender = [];
4696
- if (slot.zoneId && slot.variants && variantCache[slotKey]) {
4697
- childrenToRender = variantCache[slotKey].children;
4698
- } else if (slot.zoneId && slot.variants) {
4699
- const defaultVariant = slot.variants.find((v) => v.isDefault);
4700
- childrenToRender = defaultVariant?.children || [];
4701
- } else {
4702
- childrenToRender = Array.isArray(slot.children) ? slot.children : [];
4703
- }
4704
- const safeChildren = Array.isArray(childrenToRender) ? childrenToRender : [];
4705
- slotChildren[slot.slotId] = /* @__PURE__ */ jsx(
4706
- "div",
4707
- {
4708
- "data-slot-id": slot.slotId,
4709
- "data-amplifyup-slot": slot.slotId,
4710
- "data-zone-id": slot.zoneId || void 0,
4711
- "data-parent-node-id": node.id,
4712
- "data-parent-component-id": node.componentId,
4713
- children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(child) }, child.id))
4714
- },
4715
- slot.slotId
4716
- );
4717
- }
4900
+ return null;
4901
+ }
4902
+ let html = "";
4903
+ if (typeof fieldValue === "string") {
4904
+ const trimmed = fieldValue.trim();
4905
+ if (trimmed.startsWith("<") && trimmed.includes(">")) {
4906
+ html = fieldValue;
4907
+ } else {
4908
+ html = markdownToHtml(fieldValue);
4718
4909
  }
4719
- const slotsToPass = Object.keys(slotChildren).length > 0 ? slotChildren : void 0;
4910
+ } else {
4911
+ const text = formatFieldValue(fieldValue);
4912
+ html = text ? markdownToHtml(text) : "";
4913
+ }
4914
+ if (!html) {
4915
+ return null;
4916
+ }
4917
+ if (composerEdit && canEdit) {
4720
4918
  return /* @__PURE__ */ jsx(
4721
- "div",
4919
+ ComposerInlineHtml,
4722
4920
  {
4723
- "data-node-id": node.id,
4724
- "data-amplifyup-node": "",
4725
- "data-component-id": node.componentId,
4726
- "data-amplifyup-component": node.componentId,
4727
- children: /* @__PURE__ */ jsx(
4728
- ComponentRenderBoundary,
4729
- {
4730
- componentId: node.componentId,
4731
- nodeId: node.id,
4732
- children: /* @__PURE__ */ jsx(
4733
- LiveNodeRender,
4734
- {
4735
- nodeId: node.id,
4736
- componentId: node.componentId,
4737
- props: nodeRenderProps(node),
4738
- slots: slotsToPass,
4739
- renderComponent
4740
- }
4741
- )
4742
- }
4743
- )
4921
+ name: editName,
4922
+ html,
4923
+ sourceValue: typeof fieldValue === "string" ? fieldValue : html,
4924
+ className,
4925
+ Tag,
4926
+ layoutNodeId: context?.layoutNodeId,
4927
+ componentId: context?.componentId
4744
4928
  }
4745
4929
  );
4746
- };
4747
- const rawTree = pageConfig.layoutTree;
4748
- const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];
4749
- if (layoutTree.length === 0) {
4750
- 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." }) }) });
4751
4930
  }
4752
- return /* @__PURE__ */ jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsx(
4753
- "div",
4754
- {
4755
- "data-slot-id": "main",
4756
- "data-amplifyup-slot": "main",
4757
- "data-slot-source": "page",
4758
- "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
4759
- children: layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id))
4760
- }
4761
- ) : layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id)) });
4762
- }
4763
- var AmplifyRenderer = memo(AmplifyRendererInner);
4764
- function defaultPreviewCanvas() {
4765
- return /* @__PURE__ */ jsx(
4766
- "div",
4767
- {
4768
- "data-slot-id": "main",
4769
- "data-amplifyup-slot": "main",
4770
- "data-slot-source": "page",
4771
- "data-slot-empty": "true",
4772
- style: { minHeight: "50vh", width: "100%", display: "block", position: "relative" },
4773
- children: /* @__PURE__ */ jsx(EmptySlotHint, {})
4774
- }
4775
- );
4776
- }
4777
- function hasAmplifyLayout(pageConfig) {
4778
- return !!(pageConfig && pageConfig.id !== "default" && Array.isArray(pageConfig.layoutTree) && pageConfig.layoutTree.length > 0);
4779
- }
4780
- function normalizeRoutePath(route) {
4781
- if (!route) return "";
4782
- const path = route.split("?")[0] || "/";
4783
- if (path.length > 1 && path.endsWith("/")) return path.replace(/\/+$/, "");
4784
- return path || "/";
4931
+ return /* @__PURE__ */ jsx(Tag, { className, dangerouslySetInnerHTML: { __html: html } });
4785
4932
  }
4786
- function samePageRoute(a, b) {
4787
- const ar = normalizeRoutePath(a?.route);
4788
- const br = normalizeRoutePath(b?.route);
4789
- return !!ar && !!br && ar === br;
4933
+ function isPortableImageValue(value) {
4934
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
4935
+ const url = value.url;
4936
+ return typeof url === "string" && url.trim().length > 0;
4790
4937
  }
4791
- function layoutTreesEqual(a, b) {
4792
- if (!a || !b) return a === b;
4793
- return JSON.stringify(a.layoutTree) === JSON.stringify(b.layoutTree);
4938
+ function imageSrc(value) {
4939
+ if (typeof value === "string") return value.trim();
4940
+ if (isPortableImageValue(value)) return value.url.trim();
4941
+ return "";
4794
4942
  }
4795
- function AmplifyPageContent({
4796
- fallback,
4797
- renderComponent,
4798
- pageConfig: pageConfigProp,
4799
- forceComposerPreview = false,
4800
- previewLoadingComponent,
4801
- previewEmptyComponent,
4802
- loadingComponent,
4803
- emptyComponent,
4804
- errorComponent
4805
- }) {
4806
- const client = useAmplifyUp();
4807
- const preview = forceComposerPreview || isComposerPreview();
4808
- const clientLayout = hasAmplifyLayout(client.pageConfig) ? client.pageConfig : null;
4809
- const serverLayout = hasAmplifyLayout(pageConfigProp) ? pageConfigProp : null;
4810
- let pageConfig = serverLayout ?? clientLayout;
4811
- if (preview && clientLayout) {
4812
- pageConfig = clientLayout;
4813
- } else if (clientLayout && serverLayout && samePageRoute(clientLayout, serverLayout) && !layoutTreesEqual(clientLayout, serverLayout)) {
4814
- pageConfig = clientLayout;
4815
- } else if (clientLayout && !serverLayout) {
4816
- pageConfig = clientLayout;
4943
+ function Image(props) {
4944
+ const {
4945
+ field,
4946
+ value,
4947
+ name,
4948
+ alt: altProp,
4949
+ className,
4950
+ width: widthProp,
4951
+ height: heightProp,
4952
+ ...imgProps
4953
+ } = props;
4954
+ const context = useContext(ComponentContext);
4955
+ const hasField = isFieldEnvelope(field);
4956
+ const hasValue = hasOwnProp(props, "value");
4957
+ const { editName, fieldValue } = useBoundField(field, value, name, "Image");
4958
+ const composerEdit = useComposerEditMode();
4959
+ warnProjectionProps("Image", hasField, hasValue, name);
4960
+ if (!hasField && !hasValue && !name) {
4961
+ return null;
4817
4962
  }
4818
- const loading = !hasAmplifyLayout(pageConfig) && client.loading;
4819
- const error = hasAmplifyLayout(pageConfig) ? null : pageConfigProp ? null : client.error;
4820
- const previewCanvas = previewEmptyComponent ?? defaultPreviewCanvas();
4821
- const previewLoading = previewLoadingComponent ?? previewCanvas;
4822
- if (preview) {
4963
+ const src = imageSrc(fieldValue);
4964
+ const portable = isPortableImageValue(fieldValue) ? fieldValue : null;
4965
+ const alt = altProp ?? portable?.alt ?? "";
4966
+ const width = widthProp ?? portable?.width;
4967
+ const height = heightProp ?? portable?.height;
4968
+ const fieldAttrs = composerEdit && editName ? {
4969
+ "data-amplifyup-field": editName,
4970
+ "data-amplifyup-field-type": "image",
4971
+ "data-amplifyup-node-id": context?.layoutNodeId || void 0
4972
+ } : null;
4973
+ if (composerEdit && !editName && (hasField || hasValue)) {
4974
+ console.warn(
4975
+ '[AmplifyUp SDK] Image value={\u2026} requires name="\u2026" for computed values'
4976
+ );
4977
+ }
4978
+ if (!src) {
4979
+ if (!composerEdit || !fieldAttrs) return null;
4823
4980
  return /* @__PURE__ */ jsx(
4824
- AmplifyRenderer,
4981
+ "span",
4825
4982
  {
4826
- pageConfig: pageConfig ?? void 0,
4827
- renderComponent,
4828
- composerPreview: true,
4829
- loadingComponent: loadingComponent ?? previewLoading,
4830
- emptyComponent: emptyComponent ?? previewCanvas,
4831
- errorComponent: errorComponent ?? (() => previewCanvas)
4983
+ ...fieldAttrs,
4984
+ className,
4985
+ role: "img",
4986
+ "aria-label": alt || editName || "Image",
4987
+ style: {
4988
+ display: "block",
4989
+ boxSizing: "border-box",
4990
+ minWidth: width ? void 0 : 120,
4991
+ minHeight: height ? void 0 : 80,
4992
+ width: width || "100%",
4993
+ height: height || void 0,
4994
+ background: "color-mix(in srgb, currentColor 8%, transparent)",
4995
+ outline: "2px dashed hsl(82 100% 45%)",
4996
+ outlineOffset: 2,
4997
+ cursor: "pointer"
4998
+ }
4832
4999
  }
4833
5000
  );
4834
5001
  }
4835
- if (loading) {
4836
- return loadingComponent ? /* @__PURE__ */ jsx(Fragment, { children: loadingComponent }) : null;
4837
- }
4838
- if (error) {
4839
- if (errorComponent) return /* @__PURE__ */ jsx(Fragment, { children: errorComponent(error) });
4840
- return fallback ? /* @__PURE__ */ jsx(Fragment, { children: fallback }) : null;
4841
- }
4842
- if (!hasAmplifyLayout(pageConfig)) {
4843
- return fallback ? /* @__PURE__ */ jsx(Fragment, { children: fallback }) : null;
4844
- }
4845
5002
  return /* @__PURE__ */ jsx(
4846
- AmplifyRenderer,
5003
+ "img",
4847
5004
  {
4848
- pageConfig: pageConfig ?? void 0,
4849
- renderComponent,
4850
- loadingComponent,
4851
- emptyComponent,
4852
- errorComponent
5005
+ ...imgProps,
5006
+ ...fieldAttrs,
5007
+ src,
5008
+ alt,
5009
+ className,
5010
+ width,
5011
+ height,
5012
+ style: {
5013
+ ...typeof imgProps.style === "object" && imgProps.style ? imgProps.style : {},
5014
+ ...composerEdit && fieldAttrs ? { cursor: "pointer" } : null
5015
+ }
4853
5016
  }
4854
5017
  );
4855
5018
  }
@@ -5074,6 +5237,6 @@ function useAmplifyUp() {
5074
5237
  return context;
5075
5238
  }
5076
5239
 
5077
- export { AmplifyPageContent, AmplifyRenderer, AmplifyUpProvider, ComponentContextProvider, Field, Image, QueryContentError, RichText, SelectVariantsError, Slot, isComposerPreview, isPreviewRequest, isWorkingContentRequest, nextPageSpec, queryContent, searchSpec, selectVariants, useAmplifyUp, useComponentProps, useLiveComposerProps };
5240
+ export { AmplifyPageContent, AmplifyRenderer, AmplifyUpProvider, ComponentContextProvider, ComposerProvider, Field, Image, MaybeComposerProvider, QueryContentError, RichText, SelectVariantsError, Slot, isBrandedFields, isComposerPreview, isFieldEnvelope, isPreviewRequest, isWorkingContentRequest, nextPageSpec, queryContent, searchSpec, selectVariants, toFieldEnvelopes, useAmplifyUp };
5078
5241
  //# sourceMappingURL=react.mjs.map
5079
5242
  //# sourceMappingURL=react.mjs.map