@amplifyup/sdk 0.1.27 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js CHANGED
@@ -2,8 +2,8 @@
2
2
  'use strict';
3
3
 
4
4
  var react = require('react');
5
- var jsxRuntime = require('react/jsx-runtime');
6
5
  var marked = require('marked');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
8
  /* @amplifyup/sdk - https://amplifyup.ai */
9
9
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -2743,123 +2743,586 @@ function getSDK(adapter) {
2743
2743
  var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
2744
2744
  var refreshEventTracking = () => getSDK().refreshEventTracking();
2745
2745
  getSDK();
2746
- var ComponentRenderBoundary = class extends react.Component {
2747
- constructor() {
2748
- super(...arguments);
2749
- this.state = { error: null };
2750
- }
2751
- static getDerivedStateFromError(error) {
2752
- return { error };
2753
- }
2754
- componentDidCatch(error) {
2755
- console.error(
2756
- `[AmplifyRenderer] "${this.props.componentId}" (${this.props.nodeId}) threw while rendering:`,
2757
- error
2746
+ var ComponentContext = react.createContext(null);
2747
+ function useLiveComposerProps(layoutNodeId, props) {
2748
+ const [live, setLive] = react.useState(props);
2749
+ react.useEffect(() => {
2750
+ setLive(props);
2751
+ }, [props]);
2752
+ react.useEffect(() => {
2753
+ const apply = (fieldName, value, nodeId) => {
2754
+ if (!fieldName || typeof fieldName !== "string") return;
2755
+ if (nodeId && layoutNodeId && String(nodeId) !== String(layoutNodeId)) {
2756
+ return;
2757
+ }
2758
+ setLive((prev) => ({ ...prev, [fieldName]: value }));
2759
+ };
2760
+ const onPatch = (event) => {
2761
+ const detail = event.detail || {};
2762
+ apply(detail.fieldName, detail.value, detail.layoutNodeId);
2763
+ };
2764
+ const onMessage = (event) => {
2765
+ if (event.data?.type !== "AMPLIFYUP_FIELD_PATCH") return;
2766
+ apply(event.data.fieldName, event.data.value, event.data.layoutNodeId);
2767
+ };
2768
+ window.addEventListener("amplifyup:field-patch", onPatch);
2769
+ window.addEventListener("message", onMessage);
2770
+ return () => {
2771
+ window.removeEventListener("amplifyup:field-patch", onPatch);
2772
+ window.removeEventListener("message", onMessage);
2773
+ };
2774
+ }, [layoutNodeId]);
2775
+ return live;
2776
+ }
2777
+ function useComponentProps() {
2778
+ const context = react.useContext(ComponentContext);
2779
+ if (!context) {
2780
+ console.warn(
2781
+ "[AmplifyUp SDK] useComponentProps used outside ComponentContextProvider"
2758
2782
  );
2783
+ return {};
2759
2784
  }
2760
- render() {
2761
- if (this.state.error) {
2762
- if (process.env.NODE_ENV !== "production") {
2763
- return /* @__PURE__ */ jsxRuntime.jsxs(
2785
+ return context.props || {};
2786
+ }
2787
+ function ComponentContextProvider({
2788
+ children,
2789
+ slots,
2790
+ props,
2791
+ layoutNodeId,
2792
+ componentId
2793
+ }) {
2794
+ const liveProps = useLiveComposerProps(layoutNodeId, props || {});
2795
+ return /* @__PURE__ */ jsxRuntime.jsx(
2796
+ ComponentContext.Provider,
2797
+ {
2798
+ value: { slots, props: liveProps, layoutNodeId, componentId },
2799
+ children
2800
+ }
2801
+ );
2802
+ }
2803
+ function Slot({
2804
+ name,
2805
+ className
2806
+ }) {
2807
+ const context = react.useContext(ComponentContext);
2808
+ console.log(`[Slot:${name}] Rendering slot`, {
2809
+ hasContext: !!context,
2810
+ availableSlotKeys: context?.slots ? Object.keys(context.slots) : [],
2811
+ slotContent: context?.slots?.[name] ? "EXISTS" : "MISSING",
2812
+ contextSlots: context?.slots
2813
+ });
2814
+ if (!context) {
2815
+ console.warn(`[AmplifyUp SDK] Slot "${name}" used outside ComponentContextProvider`);
2816
+ return null;
2817
+ }
2818
+ const slotContent = context.slots?.[name];
2819
+ const isEmpty = !slotContent;
2820
+ console.log(`[Slot:${name}] Slot state`, {
2821
+ isEmpty,
2822
+ hasSlotContent: !!slotContent,
2823
+ slotContentType: slotContent ? typeof slotContent : "null"
2824
+ });
2825
+ const isEditorMode = typeof window !== "undefined" && // Check for preview mode flag set by proxy
2826
+ (window.__AMPLIFYUP_PREVIEW_MODE__ === true || // Check URL query params
2827
+ 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)
2828
+ window.self !== window.top && document.referrer && (() => {
2829
+ try {
2830
+ const referrerUrl = new URL(document.referrer);
2831
+ return referrerUrl.hostname === "localhost" && referrerUrl.port === "5001" || referrerUrl.pathname.includes("/dashboard/composer") || referrerUrl.pathname.includes("/dashboard/routes/");
2832
+ } catch {
2833
+ return false;
2834
+ }
2835
+ })());
2836
+ console.log(`[Slot:${name}] Editor mode detection`, {
2837
+ isEditorMode,
2838
+ isWindowDefined: typeof window !== "undefined",
2839
+ previewModeFlag: typeof window !== "undefined" ? window.__AMPLIFYUP_PREVIEW_MODE__ : void 0,
2840
+ urlParams: typeof window !== "undefined" ? {
2841
+ preview: new URLSearchParams(window.location.search).get("preview"),
2842
+ visualEditor: new URLSearchParams(window.location.search).get("visualEditor")
2843
+ } : void 0,
2844
+ inIframe: typeof window !== "undefined" ? window.self !== window.top : false,
2845
+ referrer: typeof window !== "undefined" && document.referrer ? document.referrer : void 0
2846
+ });
2847
+ return /* @__PURE__ */ jsxRuntime.jsx(
2848
+ "div",
2849
+ {
2850
+ className,
2851
+ "data-slot-name": name,
2852
+ "data-slot-id": name,
2853
+ "data-amplifyup-slot": name,
2854
+ "data-slot-empty": isEmpty ? "true" : void 0,
2855
+ "data-parent-node-id": context.layoutNodeId || void 0,
2856
+ "data-parent-component-id": context.componentId || void 0,
2857
+ style: {
2858
+ // Only add minimum height when empty (for drop target visibility)
2859
+ minHeight: isEmpty ? "60px" : void 0,
2860
+ // Only add position relative for overlay positioning in editor mode
2861
+ position: isEditorMode ? "relative" : void 0
2862
+ },
2863
+ children: isEmpty ? /* @__PURE__ */ jsxRuntime.jsx(
2864
+ "div",
2865
+ {
2866
+ className: "amplifyup-slot-placeholder",
2867
+ style: {
2868
+ minHeight: "60px",
2869
+ color: "#9ca3af",
2870
+ fontSize: "14px",
2871
+ pointerEvents: "none"
2872
+ },
2873
+ children: isEditorMode ? `Drop components here` : `Empty slot: ${name}`
2874
+ }
2875
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2876
+ isEditorMode && /* @__PURE__ */ jsxRuntime.jsxs(
2764
2877
  "div",
2765
2878
  {
2766
- "data-amplifyup-render-error": "",
2767
- "data-component-id": this.props.componentId,
2768
- "data-node-id": this.props.nodeId,
2879
+ className: "amplifyup-slot-placeholder",
2769
2880
  style: {
2770
- margin: "1rem 0",
2771
- padding: "0.75rem 1rem",
2772
- border: "1px solid #fca5a5",
2773
- borderRadius: "0.5rem",
2774
- background: "#fef2f2",
2775
- color: "#991b1b",
2776
- fontSize: "0.875rem"
2881
+ position: "absolute",
2882
+ top: "4px",
2883
+ left: "4px",
2884
+ fontSize: "10px",
2885
+ color: "#9ca3af",
2886
+ pointerEvents: "none",
2887
+ zIndex: 1,
2888
+ minHeight: "auto"
2777
2889
  },
2778
2890
  children: [
2779
- 'Component "',
2780
- this.props.componentId,
2781
- '" failed to render:',
2782
- " ",
2783
- this.state.error.message
2891
+ "Slot: ",
2892
+ name
2784
2893
  ]
2785
2894
  }
2786
- );
2787
- }
2788
- return null;
2895
+ ),
2896
+ slotContent
2897
+ ] })
2789
2898
  }
2790
- return this.props.children;
2899
+ );
2900
+ }
2901
+ function isComposerEditMode() {
2902
+ if (typeof window === "undefined") return false;
2903
+ return isComposerPreview() && window.__AMPLIFYUP_EVENT_SELECTION__ !== true;
2904
+ }
2905
+ function isLikelyNonTextFieldName(name) {
2906
+ const n = name.toLowerCase();
2907
+ return n.includes("href") || n.includes("url") || n.includes("src") || n.includes("image") || n.includes("icon") || n.includes("overlay") || n.includes("color");
2908
+ }
2909
+ function canInlineEdit(name, value) {
2910
+ if (isLikelyNonTextFieldName(name)) return false;
2911
+ if (value !== void 0 && value !== null && typeof value === "object") {
2912
+ return false;
2791
2913
  }
2792
- };
2793
- function AmplifyRenderer({
2794
- renderComponent,
2795
- pageConfig: serverPageConfig,
2796
- loadingComponent,
2797
- errorComponent,
2798
- emptyComponent,
2799
- composerPreview
2800
- }) {
2801
- const clientHook = useAmplifyUp();
2802
- const pageConfig = serverPageConfig || clientHook.pageConfig || null;
2803
- const loading = serverPageConfig ? false : clientHook.loading;
2804
- const error = serverPageConfig ? null : clientHook.error;
2805
- const contentSource = clientHook.source;
2806
- const [variantCache, setVariantCache] = react.useState({});
2914
+ if (typeof value === "boolean") return false;
2915
+ return true;
2916
+ }
2917
+ function useComposerEditMode() {
2918
+ const [enabled, setEnabled] = react.useState(false);
2807
2919
  react.useEffect(() => {
2808
- if (typeof window !== "undefined") {
2809
- if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
2810
- return;
2811
- }
2812
- if (window.location.pathname.includes("/api/pages/proxy")) {
2813
- return;
2814
- }
2815
- if (window.self !== window.top) {
2816
- return;
2817
- }
2818
- const urlParams = new URLSearchParams(window.location.search);
2819
- if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
2820
- return;
2821
- }
2822
- if (window.location.href.includes("preview=true") || window.location.href.includes("visualEditor=true")) {
2920
+ const sync = () => setEnabled(isComposerEditMode());
2921
+ sync();
2922
+ window.addEventListener("amplifyup:composer-edit", sync);
2923
+ return () => window.removeEventListener("amplifyup:composer-edit", sync);
2924
+ }, []);
2925
+ return enabled;
2926
+ }
2927
+ function useFieldOverlay(layoutNodeId, name, source) {
2928
+ const [overlay, setOverlay] = react.useState(void 0);
2929
+ react.useEffect(() => {
2930
+ const onPatch = (event) => {
2931
+ const detail = event.detail || {};
2932
+ if (detail.fieldName !== name) return;
2933
+ if (detail.layoutNodeId && layoutNodeId && String(detail.layoutNodeId) !== String(layoutNodeId)) {
2823
2934
  return;
2824
2935
  }
2825
- }
2826
- if (pageConfig && pageConfig.id !== "default") {
2827
- trackContentView(pageConfig.id, "page", {
2828
- route: pageConfig.route,
2829
- title: pageConfig.title
2830
- });
2831
- }
2832
- }, [pageConfig]);
2833
- react.useEffect(() => {
2834
- if (!pageConfig?.eventTracking?.length) return;
2835
- const sdk = getSDK();
2836
- if (!sdk.isInitialized()) return;
2837
- sdk.setupEventTracking(pageConfig.eventTracking);
2838
- const t1 = window.setTimeout(() => {
2839
- sdk.setupComponentViewTracking();
2840
- refreshEventTracking();
2841
- }, 200);
2842
- const t2 = window.setTimeout(() => {
2843
- sdk.setupComponentViewTracking();
2844
- refreshEventTracking();
2845
- }, 1e3);
2846
- return () => {
2847
- window.clearTimeout(t1);
2848
- window.clearTimeout(t2);
2936
+ setOverlay(detail.value);
2849
2937
  };
2850
- }, [pageConfig?.id]);
2851
- react.useEffect(() => {
2852
- if (!pageConfig) return;
2853
- const t = window.setTimeout(() => {
2854
- getSDK().setupComponentViewTracking();
2855
- refreshEventTracking();
2856
- }, 100);
2857
- return () => window.clearTimeout(t);
2858
- }, [variantCache, pageConfig?.id]);
2859
- react.useEffect(() => {
2860
- const isVisualEditorMode = () => {
2861
- if (typeof window === "undefined") return false;
2862
- const urlParams = new URLSearchParams(window.location.search);
2938
+ window.addEventListener("amplifyup:field-patch", onPatch);
2939
+ return () => window.removeEventListener("amplifyup:field-patch", onPatch);
2940
+ }, [layoutNodeId, name]);
2941
+ return overlay !== void 0 ? overlay : source;
2942
+ }
2943
+ function commitComposerField(layoutNodeId, componentId, name, value) {
2944
+ if (typeof window === "undefined" || window.parent === window) return;
2945
+ window.parent.postMessage(
2946
+ {
2947
+ type: "AMPLIFYUP_FIELD_COMMIT",
2948
+ layoutNodeId,
2949
+ componentId,
2950
+ fieldName: name,
2951
+ value
2952
+ },
2953
+ "*"
2954
+ );
2955
+ }
2956
+ function ComposerInlineField({
2957
+ name,
2958
+ value,
2959
+ fallback,
2960
+ className,
2961
+ render,
2962
+ layoutNodeId,
2963
+ componentId
2964
+ }) {
2965
+ const [editing, setEditing] = react.useState(false);
2966
+ const elRef = react.useRef(null);
2967
+ const text = formatFieldValue(value);
2968
+ const fallbackText = typeof fallback === "string" || typeof fallback === "number" ? String(fallback) : "";
2969
+ const display = text || fallbackText;
2970
+ const showRendered = !!render && !editing;
2971
+ react.useEffect(() => {
2972
+ const el = elRef.current;
2973
+ if (!el || showRendered) return;
2974
+ if (document.activeElement === el) return;
2975
+ const next = display || "";
2976
+ if (el.textContent !== next) el.textContent = next;
2977
+ }, [display, showRendered]);
2978
+ react.useEffect(() => {
2979
+ if (!editing || !elRef.current) return;
2980
+ const el = elRef.current;
2981
+ el.textContent = display || "";
2982
+ el.focus();
2983
+ const range = document.createRange();
2984
+ range.selectNodeContents(el);
2985
+ const sel = window.getSelection();
2986
+ sel?.removeAllRanges();
2987
+ sel?.addRange(range);
2988
+ }, [editing]);
2989
+ const finishEdit = () => {
2990
+ const raw = (elRef.current?.innerText ?? "").replace(/\u00a0/g, " ").replace(/\n+$/g, "");
2991
+ const next = raw.trim() === "" ? "" : raw;
2992
+ setEditing(false);
2993
+ if (next === text) return;
2994
+ if (!text && next === fallbackText) return;
2995
+ commitComposerField(layoutNodeId, componentId, name, next);
2996
+ };
2997
+ return /* @__PURE__ */ jsxRuntime.jsx(
2998
+ "span",
2999
+ {
3000
+ ref: elRef,
3001
+ className,
3002
+ "data-amplifyup-field": name,
3003
+ "data-amplifyup-node-id": layoutNodeId || void 0,
3004
+ contentEditable: !showRendered,
3005
+ suppressContentEditableWarning: true,
3006
+ role: "textbox",
3007
+ tabIndex: 0,
3008
+ onMouseDown: (event) => {
3009
+ event.stopPropagation();
3010
+ },
3011
+ onClick: (event) => {
3012
+ const link = event.target?.closest?.("a");
3013
+ if (link) event.preventDefault();
3014
+ if (render && isComposerEditMode()) {
3015
+ setEditing(true);
3016
+ }
3017
+ },
3018
+ onFocus: () => {
3019
+ if (!isComposerEditMode()) return;
3020
+ if (render) setEditing(true);
3021
+ },
3022
+ onBlur: finishEdit,
3023
+ onKeyDown: (event) => {
3024
+ event.stopPropagation();
3025
+ if (event.key === "Enter" && !event.shiftKey) {
3026
+ event.preventDefault();
3027
+ event.currentTarget.blur();
3028
+ }
3029
+ if (event.key === "Escape") {
3030
+ event.preventDefault();
3031
+ if (elRef.current) elRef.current.textContent = display || "";
3032
+ setEditing(false);
3033
+ event.currentTarget.blur();
3034
+ }
3035
+ },
3036
+ style: {
3037
+ cursor: "text",
3038
+ outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
3039
+ outlineOffset: "2px",
3040
+ minWidth: "1ch",
3041
+ display: render ? void 0 : "inline"
3042
+ },
3043
+ children: showRendered ? value != null && value !== "" ? render(value) : fallback ?? display : null
3044
+ }
3045
+ );
3046
+ }
3047
+ function formatFieldValue(value) {
3048
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3049
+ return String(value);
3050
+ }
3051
+ if (Array.isArray(value)) {
3052
+ const parts = value.map((item) => {
3053
+ if (typeof item === "string") return item;
3054
+ if (item && typeof item === "object" && "children" in item && Array.isArray(item.children)) {
3055
+ return item.children.map((c) => typeof c?.text === "string" ? c.text : "").join("");
3056
+ }
3057
+ return "";
3058
+ }).filter(Boolean);
3059
+ if (parts.length > 0) return parts.join(" ");
3060
+ }
3061
+ if (value && typeof value === "object") {
3062
+ return "";
3063
+ }
3064
+ return "";
3065
+ }
3066
+ function isLinkListItem(value) {
3067
+ return !!value && typeof value === "object" && !Array.isArray(value);
3068
+ }
3069
+ function renderLinkList(items, className) {
3070
+ const links = items.filter(isLinkListItem).filter((item) => {
3071
+ const label = typeof item.label === "string" ? item.label.trim() : "";
3072
+ const href = typeof item.href === "string" ? item.href.trim() : "";
3073
+ return !!(label || href);
3074
+ });
3075
+ if (links.length === 0) return null;
3076
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, style: { display: "contents" }, children: links.map((item, index) => {
3077
+ const label = typeof item.label === "string" && item.label.trim() ? item.label : typeof item.href === "string" ? item.href : "Link";
3078
+ const href = typeof item.href === "string" && item.href.trim() ? item.href : "#";
3079
+ const style = typeof item.style === "string" ? item.style.trim() : "";
3080
+ return /* @__PURE__ */ jsxRuntime.jsx(
3081
+ "a",
3082
+ {
3083
+ href,
3084
+ target: item.openInNewTab ? "_blank" : void 0,
3085
+ rel: item.openInNewTab ? "noreferrer" : void 0,
3086
+ "data-amplifyup-action-style": style || void 0,
3087
+ children: label
3088
+ },
3089
+ typeof item._key === "string" && item._key ? item._key : `${href}-${index}`
3090
+ );
3091
+ }) });
3092
+ }
3093
+ function Field({
3094
+ name,
3095
+ fallback,
3096
+ className,
3097
+ render
3098
+ }) {
3099
+ const context = react.useContext(ComponentContext);
3100
+ const composerEdit = useComposerEditMode();
3101
+ const fieldValue = useFieldOverlay(
3102
+ context?.layoutNodeId,
3103
+ name,
3104
+ context?.props?.[name]
3105
+ );
3106
+ if (!context) {
3107
+ console.warn(`[AmplifyUp SDK] Field "${name}" used outside ComponentContextProvider`);
3108
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3109
+ }
3110
+ if (render) {
3111
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, style: { display: "contents" }, children: render(fieldValue) });
3112
+ }
3113
+ if (composerEdit && canInlineEdit(name, fieldValue)) {
3114
+ return /* @__PURE__ */ jsxRuntime.jsx(
3115
+ ComposerInlineField,
3116
+ {
3117
+ name,
3118
+ value: fieldValue,
3119
+ fallback,
3120
+ className,
3121
+ layoutNodeId: context.layoutNodeId,
3122
+ componentId: context.componentId
3123
+ }
3124
+ );
3125
+ }
3126
+ if (fieldValue === void 0 || fieldValue === null) {
3127
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
3128
+ }
3129
+ if (Array.isArray(fieldValue)) {
3130
+ const list = renderLinkList(fieldValue, className);
3131
+ if (list) return list;
3132
+ }
3133
+ const text = formatFieldValue(fieldValue);
3134
+ if (!text) {
3135
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
3136
+ }
3137
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: text });
3138
+ }
3139
+ function markdownToHtml(markdown) {
3140
+ try {
3141
+ const result = marked.marked.parse(markdown, { async: false });
3142
+ return typeof result === "string" ? result : "";
3143
+ } catch {
3144
+ return markdown;
3145
+ }
3146
+ }
3147
+ function RichText({
3148
+ name,
3149
+ fallback,
3150
+ className,
3151
+ as: Tag = "div"
3152
+ }) {
3153
+ const context = react.useContext(ComponentContext);
3154
+ const composerEdit = useComposerEditMode();
3155
+ const fieldValue = useFieldOverlay(
3156
+ context?.layoutNodeId,
3157
+ name,
3158
+ context?.props?.[name]
3159
+ );
3160
+ if (!context) {
3161
+ console.warn(`[AmplifyUp SDK] RichText "${name}" used outside ComponentContextProvider`);
3162
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3163
+ }
3164
+ if (composerEdit && canInlineEdit(name, fieldValue)) {
3165
+ return /* @__PURE__ */ jsxRuntime.jsx(
3166
+ ComposerInlineField,
3167
+ {
3168
+ name,
3169
+ value: fieldValue,
3170
+ fallback,
3171
+ className,
3172
+ layoutNodeId: context.layoutNodeId,
3173
+ componentId: context.componentId
3174
+ }
3175
+ );
3176
+ }
3177
+ if (fieldValue === void 0 || fieldValue === null || fieldValue === "") {
3178
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3179
+ }
3180
+ let html = "";
3181
+ if (typeof fieldValue === "string") {
3182
+ const trimmed = fieldValue.trim();
3183
+ if (trimmed.startsWith("<") && trimmed.includes(">")) {
3184
+ html = fieldValue;
3185
+ } else {
3186
+ html = markdownToHtml(fieldValue);
3187
+ }
3188
+ } else {
3189
+ const text = formatFieldValue(fieldValue);
3190
+ html = text ? markdownToHtml(text) : "";
3191
+ }
3192
+ if (!html) {
3193
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3194
+ }
3195
+ return /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, dangerouslySetInnerHTML: { __html: html } });
3196
+ }
3197
+ var ComponentRenderBoundary = class extends react.Component {
3198
+ constructor() {
3199
+ super(...arguments);
3200
+ this.state = { error: null };
3201
+ }
3202
+ static getDerivedStateFromError(error) {
3203
+ return { error };
3204
+ }
3205
+ componentDidCatch(error) {
3206
+ console.error(
3207
+ `[AmplifyRenderer] "${this.props.componentId}" (${this.props.nodeId}) threw while rendering:`,
3208
+ error
3209
+ );
3210
+ }
3211
+ render() {
3212
+ if (this.state.error) {
3213
+ if (process.env.NODE_ENV !== "production") {
3214
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3215
+ "div",
3216
+ {
3217
+ "data-amplifyup-render-error": "",
3218
+ "data-component-id": this.props.componentId,
3219
+ "data-node-id": this.props.nodeId,
3220
+ style: {
3221
+ margin: "1rem 0",
3222
+ padding: "0.75rem 1rem",
3223
+ border: "1px solid #fca5a5",
3224
+ borderRadius: "0.5rem",
3225
+ background: "#fef2f2",
3226
+ color: "#991b1b",
3227
+ fontSize: "0.875rem"
3228
+ },
3229
+ children: [
3230
+ 'Component "',
3231
+ this.props.componentId,
3232
+ '" failed to render:',
3233
+ " ",
3234
+ this.state.error.message
3235
+ ]
3236
+ }
3237
+ );
3238
+ }
3239
+ return null;
3240
+ }
3241
+ return this.props.children;
3242
+ }
3243
+ };
3244
+ function LiveNodeRender({
3245
+ nodeId,
3246
+ componentId,
3247
+ props,
3248
+ slots,
3249
+ renderComponent
3250
+ }) {
3251
+ const liveProps = useLiveComposerProps(nodeId, props);
3252
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderComponent(componentId, liveProps, slots, {
3253
+ layoutNodeId: nodeId
3254
+ }) });
3255
+ }
3256
+ function AmplifyRenderer({
3257
+ renderComponent,
3258
+ pageConfig: serverPageConfig,
3259
+ loadingComponent,
3260
+ errorComponent,
3261
+ emptyComponent,
3262
+ composerPreview
3263
+ }) {
3264
+ const clientHook = useAmplifyUp();
3265
+ const pageConfig = serverPageConfig || clientHook.pageConfig || null;
3266
+ const loading = serverPageConfig ? false : clientHook.loading;
3267
+ const error = serverPageConfig ? null : clientHook.error;
3268
+ const contentSource = clientHook.source;
3269
+ const [variantCache, setVariantCache] = react.useState({});
3270
+ react.useEffect(() => {
3271
+ if (typeof window !== "undefined") {
3272
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
3273
+ return;
3274
+ }
3275
+ if (window.location.pathname.includes("/api/pages/proxy")) {
3276
+ return;
3277
+ }
3278
+ if (window.self !== window.top) {
3279
+ return;
3280
+ }
3281
+ const urlParams = new URLSearchParams(window.location.search);
3282
+ if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
3283
+ return;
3284
+ }
3285
+ if (window.location.href.includes("preview=true") || window.location.href.includes("visualEditor=true")) {
3286
+ return;
3287
+ }
3288
+ }
3289
+ if (pageConfig && pageConfig.id !== "default") {
3290
+ trackContentView(pageConfig.id, "page", {
3291
+ route: pageConfig.route,
3292
+ title: pageConfig.title
3293
+ });
3294
+ }
3295
+ }, [pageConfig]);
3296
+ react.useEffect(() => {
3297
+ if (!pageConfig?.eventTracking?.length) return;
3298
+ const sdk = getSDK();
3299
+ if (!sdk.isInitialized()) return;
3300
+ sdk.setupEventTracking(pageConfig.eventTracking);
3301
+ const t1 = window.setTimeout(() => {
3302
+ sdk.setupComponentViewTracking();
3303
+ refreshEventTracking();
3304
+ }, 200);
3305
+ const t2 = window.setTimeout(() => {
3306
+ sdk.setupComponentViewTracking();
3307
+ refreshEventTracking();
3308
+ }, 1e3);
3309
+ return () => {
3310
+ window.clearTimeout(t1);
3311
+ window.clearTimeout(t2);
3312
+ };
3313
+ }, [pageConfig?.id]);
3314
+ react.useEffect(() => {
3315
+ if (!pageConfig) return;
3316
+ const t = window.setTimeout(() => {
3317
+ getSDK().setupComponentViewTracking();
3318
+ refreshEventTracking();
3319
+ }, 100);
3320
+ return () => window.clearTimeout(t);
3321
+ }, [variantCache, pageConfig?.id]);
3322
+ react.useEffect(() => {
3323
+ const isVisualEditorMode = () => {
3324
+ if (typeof window === "undefined") return false;
3325
+ const urlParams = new URLSearchParams(window.location.search);
2863
3326
  if (urlParams.get("visualEditor") === "true" || urlParams.get("preview") === "true") {
2864
3327
  return true;
2865
3328
  }
@@ -3057,619 +3520,233 @@ function AmplifyRenderer({
3057
3520
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-red-900 mb-2", children: "Error Loading Page" }),
3058
3521
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-700", children: error })
3059
3522
  ] }) }) }) });
3060
- }
3061
- if (!pageConfig) {
3062
- return null;
3063
- }
3064
- pageConfig.layout?.header !== false;
3065
- pageConfig.layout?.footer !== false;
3066
- const renderNode = (node) => {
3067
- const zoneIdFromProps = node.componentId === "AmplifyZone" ? String(node.props?.zoneId || "") : "";
3068
- if (node.componentId === "AmplifyZone") {
3069
- const contentSlot = node.slots?.find((s) => s.slotId === "content") || node.slots?.[0];
3070
- const children = Array.isArray(contentSlot?.children) ? contentSlot.children : [];
3071
- return /* @__PURE__ */ jsxRuntime.jsx(
3072
- "div",
3073
- {
3074
- "data-node-id": node.id,
3075
- "data-amplifyup-node": "",
3076
- "data-amplifyup-component": "AmplifyZone",
3077
- "data-amplifyup-zone": zoneIdFromProps || void 0,
3078
- "data-zone-id": zoneIdFromProps || void 0,
3079
- children: /* @__PURE__ */ jsxRuntime.jsx(
3080
- "div",
3081
- {
3082
- "data-slot-id": "content",
3083
- "data-amplifyup-slot": "content",
3084
- "data-parent-node-id": node.id,
3085
- "data-parent-component-id": "AmplifyZone",
3086
- children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(child) }, child.id))
3087
- }
3088
- )
3089
- }
3090
- );
3091
- }
3092
- let slotChildren = {};
3093
- console.log(`[AmplifyRenderer] Rendering node ${node.id} (${node.componentId})`, {
3094
- hasSlots: !!(node.slots && node.slots.length > 0),
3095
- slotCount: node.slots?.length || 0,
3096
- slots: node.slots?.map((s) => ({ slotId: s.slotId, zoneId: s.zoneId, childrenCount: s.children?.length || 0 }))
3097
- });
3098
- if (node.slots && node.slots.length > 0) {
3099
- for (const slot of node.slots) {
3100
- const slotKey = `${node.id}-${slot.slotId}`;
3101
- let childrenToRender = [];
3102
- console.log(`[AmplifyRenderer] Processing slot ${slot.slotId} for node ${node.id}`, {
3103
- slotKey,
3104
- hasZoneId: !!slot.zoneId,
3105
- hasVariants: !!slot.variants,
3106
- hasVariantCache: !!variantCache[slotKey],
3107
- directChildrenCount: slot.children?.length || 0
3108
- });
3109
- if (slot.zoneId && slot.variants && variantCache[slotKey]) {
3110
- childrenToRender = variantCache[slotKey].children;
3111
- console.log(`[AmplifyRenderer] Using cached variant for slot ${slot.slotId}`, {
3112
- variantId: variantCache[slotKey].variantId,
3113
- childrenCount: childrenToRender.length
3114
- });
3115
- } else if (slot.zoneId && slot.variants) {
3116
- const defaultVariant = slot.variants.find((v) => v.isDefault);
3117
- childrenToRender = defaultVariant?.children || [];
3118
- console.log(`[AmplifyRenderer] Using default variant for slot ${slot.slotId}`, {
3119
- childrenCount: childrenToRender.length
3120
- });
3121
- } else {
3122
- childrenToRender = Array.isArray(slot.children) ? slot.children : [];
3123
- console.log(`[AmplifyRenderer] Using direct children for slot ${slot.slotId}`, {
3124
- childrenCount: childrenToRender.length
3125
- });
3126
- }
3127
- const safeChildren = Array.isArray(childrenToRender) ? childrenToRender : [];
3128
- slotChildren[slot.slotId] = /* @__PURE__ */ jsxRuntime.jsx(
3129
- "div",
3130
- {
3131
- "data-slot-id": slot.slotId,
3132
- "data-amplifyup-slot": slot.slotId,
3133
- "data-zone-id": slot.zoneId || void 0,
3134
- "data-parent-node-id": node.id,
3135
- "data-parent-component-id": node.componentId,
3136
- children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(child) }, child.id))
3137
- },
3138
- slot.slotId
3139
- );
3140
- console.log(`[AmplifyRenderer] Created slot content for ${slot.slotId}`, {
3141
- slotId: slot.slotId,
3142
- renderedChildrenCount: childrenToRender.length
3143
- });
3144
- }
3145
- }
3146
- const slotsToPass = Object.keys(slotChildren).length > 0 ? slotChildren : void 0;
3147
- console.log(`[AmplifyRenderer] Calling renderComponent for ${node.componentId}`, {
3148
- hasSlots: !!slotsToPass,
3149
- slotKeys: slotsToPass ? Object.keys(slotsToPass) : [],
3150
- propKeys: Object.keys(node.props || {}),
3151
- props: node.props || {}
3152
- });
3153
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: /* @__PURE__ */ jsxRuntime.jsx(
3154
- ComponentRenderBoundary,
3155
- {
3156
- componentId: node.componentId,
3157
- nodeId: node.id,
3158
- children: renderComponent(node.componentId, node.props || {}, slotsToPass, {
3159
- layoutNodeId: node.id
3160
- })
3161
- }
3162
- ) });
3163
- };
3164
- const rawTree = pageConfig.layoutTree;
3165
- const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];
3166
- if (layoutTree.length === 0) {
3167
- return /* @__PURE__ */ jsxRuntime.jsx("main", { children: emptyComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No content configured for this page." }) }) });
3168
- }
3169
- return /* @__PURE__ */ jsxRuntime.jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsxRuntime.jsx(
3170
- "div",
3171
- {
3172
- "data-slot-id": "main",
3173
- "data-amplifyup-slot": "main",
3174
- "data-slot-source": "page",
3175
- "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
3176
- children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id))
3177
- }
3178
- ) : layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id)) });
3179
- }
3180
- function defaultPreviewCanvas() {
3181
- return /* @__PURE__ */ jsxRuntime.jsx(
3182
- "div",
3183
- {
3184
- "data-slot-id": "main",
3185
- "data-amplifyup-slot": "main",
3186
- "data-slot-source": "page",
3187
- "data-slot-empty": "true",
3188
- style: { minHeight: "50vh", width: "100%", display: "block", position: "relative" },
3189
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "amplifyup-slot-placeholder", children: "Drop components here" })
3190
- }
3191
- );
3192
- }
3193
- function hasAmplifyLayout(pageConfig) {
3194
- return !!(pageConfig && pageConfig.id !== "default" && Array.isArray(pageConfig.layoutTree) && pageConfig.layoutTree.length > 0);
3195
- }
3196
- function AmplifyPageContent({
3197
- fallback,
3198
- renderComponent,
3199
- pageConfig: pageConfigProp,
3200
- forceComposerPreview = false,
3201
- previewLoadingComponent,
3202
- previewEmptyComponent,
3203
- loadingComponent,
3204
- emptyComponent,
3205
- errorComponent
3206
- }) {
3207
- const client = useAmplifyUp();
3208
- const preview = forceComposerPreview || isComposerPreview();
3209
- const pageConfig = pageConfigProp ?? client.pageConfig;
3210
- const loading = pageConfigProp ? false : client.loading;
3211
- const error = pageConfigProp ? null : client.error;
3212
- const previewCanvas = previewEmptyComponent ?? defaultPreviewCanvas();
3213
- const previewLoading = previewLoadingComponent ?? previewCanvas;
3214
- if (preview) {
3215
- return /* @__PURE__ */ jsxRuntime.jsx(
3216
- AmplifyRenderer,
3217
- {
3218
- pageConfig: pageConfig ?? void 0,
3219
- renderComponent,
3220
- composerPreview: true,
3221
- loadingComponent: loadingComponent ?? previewLoading,
3222
- emptyComponent: emptyComponent ?? previewCanvas,
3223
- errorComponent: errorComponent ?? (() => previewCanvas)
3224
- }
3225
- );
3226
- }
3227
- if (loading) {
3228
- return loadingComponent ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent }) : null;
3229
- }
3230
- if (error) {
3231
- if (errorComponent) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: errorComponent(error) });
3232
- return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3233
- }
3234
- if (!hasAmplifyLayout(pageConfig)) {
3235
- return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3236
- }
3237
- return /* @__PURE__ */ jsxRuntime.jsx(
3238
- AmplifyRenderer,
3239
- {
3240
- pageConfig: pageConfig ?? void 0,
3241
- renderComponent,
3242
- loadingComponent,
3243
- emptyComponent,
3244
- errorComponent
3245
- }
3246
- );
3247
- }
3248
-
3249
- // src/server.tsx
3250
- async function fetchPageConfigServer(route, trackingId, preview = false, options) {
3251
- const target = resolveAmplifyUpTarget({
3252
- configTarget: options?.target
3253
- });
3254
- const { orchestratorUrl, edgeUrl } = resolveAmplifyUpUrls(target);
3255
- const previewFlag = preview || (options?.searchParams ? isPreviewRequest(options.searchParams) : false);
3256
- const fetchOrchestratorPage = async (pageRoute, isPreview) => {
3257
- const params = new URLSearchParams({
3258
- route: pageRoute,
3259
- trackingId
3260
- });
3261
- if (isPreview) params.set("preview", "true");
3262
- const url = `${orchestratorUrl}/api/layout/provider?${params.toString()}`;
3263
- try {
3264
- const res = await fetch(url, {
3265
- cache: "no-store",
3266
- headers: { Accept: "application/json" }
3267
- });
3268
- if (!res.ok) return null;
3269
- const data = await res.json();
3270
- return data.page || null;
3271
- } catch {
3272
- return null;
3273
- }
3274
- };
3275
- const result = await fetchContent({
3276
- route,
3277
- trackingId,
3278
- anonId: options?.anonId,
3279
- siteId: options?.siteId,
3280
- preview: previewFlag,
3281
- orchestratorUrl,
3282
- edgeUrl,
3283
- fetchOrchestratorPage
3284
- });
3285
- return result.pageConfig;
3286
- }
3287
- var ComponentContext = react.createContext(null);
3288
- function ComponentContextProvider({
3289
- children,
3290
- slots,
3291
- props,
3292
- layoutNodeId,
3293
- componentId
3294
- }) {
3295
- console.log("[ComponentContextProvider] Providing context", {
3296
- hasSlots: !!slots,
3297
- slotKeys: slots ? Object.keys(slots) : [],
3298
- slotDetails: slots ? Object.keys(slots).map((key) => ({
3299
- key,
3300
- hasContent: !!slots[key],
3301
- contentType: slots[key] ? typeof slots[key] : "null"
3302
- })) : [],
3303
- hasProps: !!props,
3304
- propKeys: props ? Object.keys(props) : [],
3305
- layoutNodeId,
3306
- componentId
3307
- });
3308
- return /* @__PURE__ */ jsxRuntime.jsx(ComponentContext.Provider, { value: { slots, props, layoutNodeId, componentId }, children });
3309
- }
3310
- function Slot({
3311
- name,
3312
- className
3313
- }) {
3314
- const context = react.useContext(ComponentContext);
3315
- console.log(`[Slot:${name}] Rendering slot`, {
3316
- hasContext: !!context,
3317
- availableSlotKeys: context?.slots ? Object.keys(context.slots) : [],
3318
- slotContent: context?.slots?.[name] ? "EXISTS" : "MISSING",
3319
- contextSlots: context?.slots
3320
- });
3321
- if (!context) {
3322
- console.warn(`[AmplifyUp SDK] Slot "${name}" used outside ComponentContextProvider`);
3523
+ }
3524
+ if (!pageConfig) {
3323
3525
  return null;
3324
3526
  }
3325
- const slotContent = context.slots?.[name];
3326
- const isEmpty = !slotContent;
3327
- console.log(`[Slot:${name}] Slot state`, {
3328
- isEmpty,
3329
- hasSlotContent: !!slotContent,
3330
- slotContentType: slotContent ? typeof slotContent : "null"
3331
- });
3332
- const isEditorMode = typeof window !== "undefined" && // Check for preview mode flag set by proxy
3333
- (window.__AMPLIFYUP_PREVIEW_MODE__ === true || // Check URL query params
3334
- 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)
3335
- window.self !== window.top && document.referrer && (() => {
3336
- try {
3337
- const referrerUrl = new URL(document.referrer);
3338
- return referrerUrl.hostname === "localhost" && referrerUrl.port === "5001" || referrerUrl.pathname.includes("/dashboard/composer") || referrerUrl.pathname.includes("/dashboard/routes/");
3339
- } catch {
3340
- return false;
3341
- }
3342
- })());
3343
- console.log(`[Slot:${name}] Editor mode detection`, {
3344
- isEditorMode,
3345
- isWindowDefined: typeof window !== "undefined",
3346
- previewModeFlag: typeof window !== "undefined" ? window.__AMPLIFYUP_PREVIEW_MODE__ : void 0,
3347
- urlParams: typeof window !== "undefined" ? {
3348
- preview: new URLSearchParams(window.location.search).get("preview"),
3349
- visualEditor: new URLSearchParams(window.location.search).get("visualEditor")
3350
- } : void 0,
3351
- inIframe: typeof window !== "undefined" ? window.self !== window.top : false,
3352
- referrer: typeof window !== "undefined" && document.referrer ? document.referrer : void 0
3353
- });
3354
- return /* @__PURE__ */ jsxRuntime.jsx(
3355
- "div",
3356
- {
3357
- className,
3358
- "data-slot-name": name,
3359
- "data-slot-id": name,
3360
- "data-amplifyup-slot": name,
3361
- "data-slot-empty": isEmpty ? "true" : void 0,
3362
- "data-parent-node-id": context.layoutNodeId || void 0,
3363
- "data-parent-component-id": context.componentId || void 0,
3364
- style: {
3365
- // Only add minimum height when empty (for drop target visibility)
3366
- minHeight: isEmpty ? "60px" : void 0,
3367
- // Only add position relative for overlay positioning in editor mode
3368
- position: isEditorMode ? "relative" : void 0
3369
- },
3370
- children: isEmpty ? /* @__PURE__ */ jsxRuntime.jsx(
3527
+ pageConfig.layout?.header !== false;
3528
+ pageConfig.layout?.footer !== false;
3529
+ const renderNode = (node) => {
3530
+ const zoneIdFromProps = node.componentId === "AmplifyZone" ? String(node.props?.zoneId || "") : "";
3531
+ if (node.componentId === "AmplifyZone") {
3532
+ const contentSlot = node.slots?.find((s) => s.slotId === "content") || node.slots?.[0];
3533
+ const children = Array.isArray(contentSlot?.children) ? contentSlot.children : [];
3534
+ return /* @__PURE__ */ jsxRuntime.jsx(
3371
3535
  "div",
3372
3536
  {
3373
- className: "amplifyup-slot-placeholder",
3374
- style: {
3375
- minHeight: "60px",
3376
- color: "#9ca3af",
3377
- fontSize: "14px",
3378
- pointerEvents: "none"
3379
- },
3380
- children: isEditorMode ? `Drop components here` : `Empty slot: ${name}`
3537
+ "data-node-id": node.id,
3538
+ "data-amplifyup-node": "",
3539
+ "data-amplifyup-component": "AmplifyZone",
3540
+ "data-amplifyup-zone": zoneIdFromProps || void 0,
3541
+ "data-zone-id": zoneIdFromProps || void 0,
3542
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3543
+ "div",
3544
+ {
3545
+ "data-slot-id": "content",
3546
+ "data-amplifyup-slot": "content",
3547
+ "data-parent-node-id": node.id,
3548
+ "data-parent-component-id": "AmplifyZone",
3549
+ children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(child) }, child.id))
3550
+ }
3551
+ )
3381
3552
  }
3382
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3383
- isEditorMode && /* @__PURE__ */ jsxRuntime.jsxs(
3553
+ );
3554
+ }
3555
+ let slotChildren = {};
3556
+ console.log(`[AmplifyRenderer] Rendering node ${node.id} (${node.componentId})`, {
3557
+ hasSlots: !!(node.slots && node.slots.length > 0),
3558
+ slotCount: node.slots?.length || 0,
3559
+ slots: node.slots?.map((s) => ({ slotId: s.slotId, zoneId: s.zoneId, childrenCount: s.children?.length || 0 }))
3560
+ });
3561
+ if (node.slots && node.slots.length > 0) {
3562
+ for (const slot of node.slots) {
3563
+ const slotKey = `${node.id}-${slot.slotId}`;
3564
+ let childrenToRender = [];
3565
+ console.log(`[AmplifyRenderer] Processing slot ${slot.slotId} for node ${node.id}`, {
3566
+ slotKey,
3567
+ hasZoneId: !!slot.zoneId,
3568
+ hasVariants: !!slot.variants,
3569
+ hasVariantCache: !!variantCache[slotKey],
3570
+ directChildrenCount: slot.children?.length || 0
3571
+ });
3572
+ if (slot.zoneId && slot.variants && variantCache[slotKey]) {
3573
+ childrenToRender = variantCache[slotKey].children;
3574
+ console.log(`[AmplifyRenderer] Using cached variant for slot ${slot.slotId}`, {
3575
+ variantId: variantCache[slotKey].variantId,
3576
+ childrenCount: childrenToRender.length
3577
+ });
3578
+ } else if (slot.zoneId && slot.variants) {
3579
+ const defaultVariant = slot.variants.find((v) => v.isDefault);
3580
+ childrenToRender = defaultVariant?.children || [];
3581
+ console.log(`[AmplifyRenderer] Using default variant for slot ${slot.slotId}`, {
3582
+ childrenCount: childrenToRender.length
3583
+ });
3584
+ } else {
3585
+ childrenToRender = Array.isArray(slot.children) ? slot.children : [];
3586
+ console.log(`[AmplifyRenderer] Using direct children for slot ${slot.slotId}`, {
3587
+ childrenCount: childrenToRender.length
3588
+ });
3589
+ }
3590
+ const safeChildren = Array.isArray(childrenToRender) ? childrenToRender : [];
3591
+ slotChildren[slot.slotId] = /* @__PURE__ */ jsxRuntime.jsx(
3384
3592
  "div",
3385
3593
  {
3386
- className: "amplifyup-slot-placeholder",
3387
- style: {
3388
- position: "absolute",
3389
- top: "4px",
3390
- left: "4px",
3391
- fontSize: "10px",
3392
- color: "#9ca3af",
3393
- pointerEvents: "none",
3394
- zIndex: 1,
3395
- minHeight: "auto"
3396
- },
3397
- children: [
3398
- "Slot: ",
3399
- name
3400
- ]
3401
- }
3402
- ),
3403
- slotContent
3404
- ] })
3405
- }
3406
- );
3407
- }
3408
- function isComposerEditMode() {
3409
- if (typeof window === "undefined") return false;
3410
- return isComposerPreview() && window.__AMPLIFYUP_COMPOSER_EDIT__ === true && window.__AMPLIFYUP_EVENT_SELECTION__ !== true;
3411
- }
3412
- function isLikelyNonTextFieldName(name) {
3413
- const n = name.toLowerCase();
3414
- return n.includes("href") || n.includes("url") || n.includes("src") || n.includes("image") || n.includes("icon") || n.includes("overlay") || n.includes("color");
3415
- }
3416
- function canInlineEdit(name, value) {
3417
- if (isLikelyNonTextFieldName(name)) return false;
3418
- if (value !== void 0 && value !== null && typeof value === "object") {
3419
- return false;
3420
- }
3421
- if (typeof value === "boolean") return false;
3422
- return true;
3423
- }
3424
- function useComposerEditMode() {
3425
- const [enabled, setEnabled] = react.useState(false);
3426
- react.useEffect(() => {
3427
- const sync = () => setEnabled(isComposerEditMode());
3428
- sync();
3429
- window.addEventListener("amplifyup:composer-edit", sync);
3430
- return () => window.removeEventListener("amplifyup:composer-edit", sync);
3431
- }, []);
3432
- return enabled;
3433
- }
3434
- function useFieldOverlay(layoutNodeId, name, source) {
3435
- const [overlay, setOverlay] = react.useState(void 0);
3436
- react.useEffect(() => {
3437
- const onPatch = (event) => {
3438
- const detail = event.detail || {};
3439
- if (detail.fieldName !== name) return;
3440
- if (detail.layoutNodeId && layoutNodeId && String(detail.layoutNodeId) !== String(layoutNodeId)) {
3441
- return;
3594
+ "data-slot-id": slot.slotId,
3595
+ "data-amplifyup-slot": slot.slotId,
3596
+ "data-zone-id": slot.zoneId || void 0,
3597
+ "data-parent-node-id": node.id,
3598
+ "data-parent-component-id": node.componentId,
3599
+ children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(child) }, child.id))
3600
+ },
3601
+ slot.slotId
3602
+ );
3603
+ console.log(`[AmplifyRenderer] Created slot content for ${slot.slotId}`, {
3604
+ slotId: slot.slotId,
3605
+ renderedChildrenCount: childrenToRender.length
3606
+ });
3442
3607
  }
3443
- setOverlay(detail.value);
3444
- };
3445
- window.addEventListener("amplifyup:field-patch", onPatch);
3446
- return () => window.removeEventListener("amplifyup:field-patch", onPatch);
3447
- }, [layoutNodeId, name]);
3448
- return overlay !== void 0 ? overlay : source;
3449
- }
3450
- function commitComposerField(layoutNodeId, componentId, name, value) {
3451
- if (typeof window === "undefined" || window.parent === window) return;
3452
- window.parent.postMessage(
3453
- {
3454
- type: "AMPLIFYUP_FIELD_COMMIT",
3455
- layoutNodeId,
3456
- componentId,
3457
- fieldName: name,
3458
- value
3459
- },
3460
- "*"
3461
- );
3462
- }
3463
- function ComposerInlineField({
3464
- name,
3465
- value,
3466
- fallback,
3467
- className,
3468
- render,
3469
- layoutNodeId,
3470
- componentId
3471
- }) {
3472
- const [editing, setEditing] = react.useState(false);
3473
- const elRef = react.useRef(null);
3474
- const text = formatFieldValue(value);
3475
- const fallbackText = typeof fallback === "string" || typeof fallback === "number" ? String(fallback) : "";
3476
- const display = text || fallbackText;
3477
- const showRendered = !!render && !editing;
3478
- react.useEffect(() => {
3479
- const el = elRef.current;
3480
- if (!el || showRendered) return;
3481
- if (document.activeElement === el) return;
3482
- const next = display || "";
3483
- if (el.textContent !== next) el.textContent = next;
3484
- }, [display, showRendered]);
3485
- react.useEffect(() => {
3486
- if (!editing || !elRef.current) return;
3487
- const el = elRef.current;
3488
- el.textContent = display || "";
3489
- el.focus();
3490
- const range = document.createRange();
3491
- range.selectNodeContents(el);
3492
- const sel = window.getSelection();
3493
- sel?.removeAllRanges();
3494
- sel?.addRange(range);
3495
- }, [editing]);
3496
- const finishEdit = () => {
3497
- const raw = (elRef.current?.innerText ?? "").replace(/\u00a0/g, " ").replace(/\n+$/g, "");
3498
- const next = raw.trim() === "" ? "" : raw;
3499
- setEditing(false);
3500
- if (next === text) return;
3501
- if (!text && next === fallbackText) return;
3502
- commitComposerField(layoutNodeId, componentId, name, next);
3608
+ }
3609
+ const slotsToPass = Object.keys(slotChildren).length > 0 ? slotChildren : void 0;
3610
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: /* @__PURE__ */ jsxRuntime.jsx(
3611
+ ComponentRenderBoundary,
3612
+ {
3613
+ componentId: node.componentId,
3614
+ nodeId: node.id,
3615
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3616
+ LiveNodeRender,
3617
+ {
3618
+ nodeId: node.id,
3619
+ componentId: node.componentId,
3620
+ props: node.props || {},
3621
+ slots: slotsToPass,
3622
+ renderComponent
3623
+ }
3624
+ )
3625
+ }
3626
+ ) });
3503
3627
  };
3628
+ const rawTree = pageConfig.layoutTree;
3629
+ const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];
3630
+ if (layoutTree.length === 0) {
3631
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { children: emptyComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No content configured for this page." }) }) });
3632
+ }
3633
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsxRuntime.jsx(
3634
+ "div",
3635
+ {
3636
+ "data-slot-id": "main",
3637
+ "data-amplifyup-slot": "main",
3638
+ "data-slot-source": "page",
3639
+ "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
3640
+ children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id))
3641
+ }
3642
+ ) : layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id)) });
3643
+ }
3644
+ function defaultPreviewCanvas() {
3504
3645
  return /* @__PURE__ */ jsxRuntime.jsx(
3505
- "span",
3646
+ "div",
3506
3647
  {
3507
- ref: elRef,
3508
- className,
3509
- "data-amplifyup-field": name,
3510
- "data-amplifyup-node-id": layoutNodeId || void 0,
3511
- contentEditable: !showRendered,
3512
- suppressContentEditableWarning: true,
3513
- role: "textbox",
3514
- tabIndex: 0,
3515
- onMouseDown: (event) => {
3516
- event.stopPropagation();
3517
- },
3518
- onClick: (event) => {
3519
- const link = event.target?.closest?.("a");
3520
- if (link) event.preventDefault();
3521
- if (render && isComposerEditMode()) {
3522
- setEditing(true);
3523
- }
3524
- },
3525
- onFocus: () => {
3526
- if (!isComposerEditMode()) return;
3527
- if (render) setEditing(true);
3528
- },
3529
- onBlur: finishEdit,
3530
- onKeyDown: (event) => {
3531
- event.stopPropagation();
3532
- if (event.key === "Enter" && !event.shiftKey) {
3533
- event.preventDefault();
3534
- event.currentTarget.blur();
3535
- }
3536
- if (event.key === "Escape") {
3537
- event.preventDefault();
3538
- if (elRef.current) elRef.current.textContent = display || "";
3539
- setEditing(false);
3540
- event.currentTarget.blur();
3541
- }
3542
- },
3543
- style: {
3544
- cursor: "text",
3545
- outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
3546
- outlineOffset: "2px",
3547
- minWidth: "1ch",
3548
- display: render ? void 0 : "inline"
3549
- },
3550
- children: showRendered ? value != null && value !== "" ? render(value) : fallback ?? display : null
3648
+ "data-slot-id": "main",
3649
+ "data-amplifyup-slot": "main",
3650
+ "data-slot-source": "page",
3651
+ "data-slot-empty": "true",
3652
+ style: { minHeight: "50vh", width: "100%", display: "block", position: "relative" },
3653
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "amplifyup-slot-placeholder", children: "Drop components here" })
3551
3654
  }
3552
3655
  );
3553
3656
  }
3554
- function formatFieldValue(value) {
3555
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3556
- return String(value);
3557
- }
3558
- if (Array.isArray(value)) {
3559
- const parts = value.map((item) => {
3560
- if (typeof item === "string") return item;
3561
- if (item && typeof item === "object" && "children" in item && Array.isArray(item.children)) {
3562
- return item.children.map((c) => typeof c?.text === "string" ? c.text : "").join("");
3563
- }
3564
- return "";
3565
- }).filter(Boolean);
3566
- if (parts.length > 0) return parts.join(" ");
3567
- }
3568
- if (value && typeof value === "object") {
3569
- return "";
3570
- }
3571
- return "";
3657
+ function hasAmplifyLayout(pageConfig) {
3658
+ return !!(pageConfig && pageConfig.id !== "default" && Array.isArray(pageConfig.layoutTree) && pageConfig.layoutTree.length > 0);
3572
3659
  }
3573
- function Field({
3574
- name,
3660
+ function AmplifyPageContent({
3575
3661
  fallback,
3576
- className,
3577
- render
3662
+ renderComponent,
3663
+ pageConfig: pageConfigProp,
3664
+ forceComposerPreview = false,
3665
+ previewLoadingComponent,
3666
+ previewEmptyComponent,
3667
+ loadingComponent,
3668
+ emptyComponent,
3669
+ errorComponent
3578
3670
  }) {
3579
- const context = react.useContext(ComponentContext);
3580
- const composerEdit = useComposerEditMode();
3581
- const fieldValue = useFieldOverlay(
3582
- context?.layoutNodeId,
3583
- name,
3584
- context?.props?.[name]
3585
- );
3586
- if (!context) {
3587
- console.warn(`[AmplifyUp SDK] Field "${name}" used outside ComponentContextProvider`);
3588
- return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3589
- }
3590
- if (composerEdit && canInlineEdit(name, fieldValue)) {
3671
+ const client = useAmplifyUp();
3672
+ const preview = forceComposerPreview || isComposerPreview();
3673
+ const pageConfig = pageConfigProp ?? client.pageConfig;
3674
+ const loading = pageConfigProp ? false : client.loading;
3675
+ const error = pageConfigProp ? null : client.error;
3676
+ const previewCanvas = previewEmptyComponent ?? defaultPreviewCanvas();
3677
+ const previewLoading = previewLoadingComponent ?? previewCanvas;
3678
+ if (preview) {
3591
3679
  return /* @__PURE__ */ jsxRuntime.jsx(
3592
- ComposerInlineField,
3680
+ AmplifyRenderer,
3593
3681
  {
3594
- name,
3595
- value: fieldValue,
3596
- fallback,
3597
- className,
3598
- render,
3599
- layoutNodeId: context.layoutNodeId,
3600
- componentId: context.componentId
3682
+ pageConfig: pageConfig ?? void 0,
3683
+ renderComponent,
3684
+ composerPreview: true,
3685
+ loadingComponent: loadingComponent ?? previewLoading,
3686
+ emptyComponent: emptyComponent ?? previewCanvas,
3687
+ errorComponent: errorComponent ?? (() => previewCanvas)
3601
3688
  }
3602
3689
  );
3603
3690
  }
3604
- if (fieldValue === void 0 || fieldValue === null) {
3605
- return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
3606
- }
3607
- if (render) {
3608
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: render(fieldValue) });
3609
- }
3610
- const text = formatFieldValue(fieldValue);
3611
- if (!text) {
3612
- return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
3613
- }
3614
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: text });
3615
- }
3616
- function markdownToHtml(markdown) {
3617
- try {
3618
- const result = marked.marked.parse(markdown, { async: false });
3619
- return typeof result === "string" ? result : "";
3620
- } catch {
3621
- return markdown;
3691
+ if (loading) {
3692
+ return loadingComponent ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent }) : null;
3622
3693
  }
3623
- }
3624
- function RichText({
3625
- name,
3626
- fallback,
3627
- className,
3628
- as: Tag = "div"
3629
- }) {
3630
- const context = react.useContext(ComponentContext);
3631
- const composerEdit = useComposerEditMode();
3632
- const fieldValue = useFieldOverlay(
3633
- context?.layoutNodeId,
3634
- name,
3635
- context?.props?.[name]
3636
- );
3637
- if (!context) {
3638
- console.warn(`[AmplifyUp SDK] RichText "${name}" used outside ComponentContextProvider`);
3694
+ if (error) {
3695
+ if (errorComponent) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: errorComponent(error) });
3639
3696
  return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3640
3697
  }
3641
- if (composerEdit && canInlineEdit(name, fieldValue)) {
3642
- return /* @__PURE__ */ jsxRuntime.jsx(
3643
- ComposerInlineField,
3644
- {
3645
- name,
3646
- value: fieldValue,
3647
- fallback,
3648
- className,
3649
- layoutNodeId: context.layoutNodeId,
3650
- componentId: context.componentId
3651
- }
3652
- );
3653
- }
3654
- if (fieldValue === void 0 || fieldValue === null || fieldValue === "") {
3655
- return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3698
+ if (!hasAmplifyLayout(pageConfig)) {
3699
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3656
3700
  }
3657
- let html = "";
3658
- if (typeof fieldValue === "string") {
3659
- const trimmed = fieldValue.trim();
3660
- if (trimmed.startsWith("<") && trimmed.includes(">")) {
3661
- html = fieldValue;
3662
- } else {
3663
- html = markdownToHtml(fieldValue);
3701
+ return /* @__PURE__ */ jsxRuntime.jsx(
3702
+ AmplifyRenderer,
3703
+ {
3704
+ pageConfig: pageConfig ?? void 0,
3705
+ renderComponent,
3706
+ loadingComponent,
3707
+ emptyComponent,
3708
+ errorComponent
3664
3709
  }
3665
- } else {
3666
- const text = formatFieldValue(fieldValue);
3667
- html = text ? markdownToHtml(text) : "";
3668
- }
3669
- if (!html) {
3670
- return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3671
- }
3672
- return /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, dangerouslySetInnerHTML: { __html: html } });
3710
+ );
3711
+ }
3712
+
3713
+ // src/server.tsx
3714
+ async function fetchPageConfigServer(route, trackingId, preview = false, options) {
3715
+ const target = resolveAmplifyUpTarget({
3716
+ configTarget: options?.target
3717
+ });
3718
+ const { orchestratorUrl, edgeUrl } = resolveAmplifyUpUrls(target);
3719
+ const previewFlag = preview || (options?.searchParams ? isPreviewRequest(options.searchParams) : false);
3720
+ const fetchOrchestratorPage = async (pageRoute, isPreview) => {
3721
+ const params = new URLSearchParams({
3722
+ route: pageRoute,
3723
+ trackingId
3724
+ });
3725
+ if (isPreview) params.set("preview", "true");
3726
+ const url = `${orchestratorUrl}/api/layout/provider?${params.toString()}`;
3727
+ try {
3728
+ const res = await fetch(url, {
3729
+ cache: "no-store",
3730
+ headers: { Accept: "application/json" }
3731
+ });
3732
+ if (!res.ok) return null;
3733
+ const data = await res.json();
3734
+ return data.page || null;
3735
+ } catch {
3736
+ return null;
3737
+ }
3738
+ };
3739
+ const result = await fetchContent({
3740
+ route,
3741
+ trackingId,
3742
+ anonId: options?.anonId,
3743
+ siteId: options?.siteId,
3744
+ preview: previewFlag,
3745
+ orchestratorUrl,
3746
+ edgeUrl,
3747
+ fetchOrchestratorPage
3748
+ });
3749
+ return result.pageConfig;
3673
3750
  }
3674
3751
  var usePathname = null;
3675
3752
  if (typeof window !== "undefined") {
@@ -3839,5 +3916,7 @@ exports.fetchPageConfigServer = fetchPageConfigServer;
3839
3916
  exports.isComposerPreview = isComposerPreview;
3840
3917
  exports.isPreviewRequest = isPreviewRequest;
3841
3918
  exports.useAmplifyUp = useAmplifyUp;
3919
+ exports.useComponentProps = useComponentProps;
3920
+ exports.useLiveComposerProps = useLiveComposerProps;
3842
3921
  //# sourceMappingURL=react.js.map
3843
3922
  //# sourceMappingURL=react.js.map