@amplifyup/sdk 0.1.39 → 0.1.41

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
@@ -1,10 +1,14 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var react = require('react');
4
+ var React = require('react');
5
5
  var marked = require('marked');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React__default = /*#__PURE__*/_interopDefault(React);
11
+
8
12
  /* @amplifyup/sdk - https://amplifyup.ai */
9
13
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
14
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -104,6 +108,7 @@ var AWSAdapter = class {
104
108
  const eventData = {
105
109
  type: "page",
106
110
  anonymousId: properties.instanceId,
111
+ sessionId: properties.sessionId,
107
112
  userId: properties.userId,
108
113
  properties: {
109
114
  path: properties.path || properties.url,
@@ -146,6 +151,7 @@ var AWSAdapter = class {
146
151
  type: "track",
147
152
  event: eventName,
148
153
  anonymousId: properties.instanceId,
154
+ sessionId: properties.sessionId,
149
155
  userId: properties.userId,
150
156
  properties: {
151
157
  ...properties
@@ -168,6 +174,7 @@ var AWSAdapter = class {
168
174
  const eventData = {
169
175
  type: "identify",
170
176
  anonymousId: traits.instanceId,
177
+ sessionId: traits.sessionId,
171
178
  traits: {
172
179
  ...traits,
173
180
  ...identifiers ? { identifiers } : {}
@@ -186,6 +193,7 @@ var AWSAdapter = class {
186
193
  const eventData = {
187
194
  type: "set_fields",
188
195
  anonymousId: context.instanceId,
196
+ sessionId: context.sessionId,
189
197
  fields,
190
198
  traits: {
191
199
  instanceId: context.instanceId,
@@ -208,7 +216,11 @@ var AWSAdapter = class {
208
216
  // src/storage.ts
209
217
  var INSTANCE_ID_KEY = "amplifyup_instance_id";
210
218
  var USER_ID_KEY = "amplifyup_user_id";
219
+ var SESSION_ID_KEY = "amplifyup_session_id";
220
+ var SESSION_ACTIVITY_KEY = "amplifyup_session_activity";
211
221
  var ANON_COOKIE_NAME = "amplifyup_anon_id";
222
+ var SESSION_COOKIE_NAME = "amplifyup_session_id";
223
+ var SESSION_IDLE_GAP_MS = 30 * 60 * 1e3;
212
224
  function generateGuid() {
213
225
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
214
226
  return crypto.randomUUID();
@@ -235,9 +247,9 @@ function readCookie(name) {
235
247
  }
236
248
  return null;
237
249
  }
238
- function writeCookie(name, value) {
250
+ function writeCookie(name, value, maxAgeSeconds = 31536e3) {
239
251
  if (typeof document === "undefined") return;
240
- document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; Max-Age=31536000; SameSite=Lax`;
252
+ document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; Max-Age=${maxAgeSeconds}; SameSite=Lax`;
241
253
  }
242
254
  function getInstanceId() {
243
255
  if (typeof window === "undefined") {
@@ -260,6 +272,23 @@ function getUserId() {
260
272
  }
261
273
  return localStorage.getItem(USER_ID_KEY);
262
274
  }
275
+ function getSessionId() {
276
+ if (typeof window === "undefined") {
277
+ return "";
278
+ }
279
+ const now = Date.now();
280
+ const lastActivity = Number(localStorage.getItem(SESSION_ACTIVITY_KEY) || 0);
281
+ const fromCookie = readCookie(SESSION_COOKIE_NAME);
282
+ const fromLs = localStorage.getItem(SESSION_ID_KEY);
283
+ let sessionId = fromLs || fromCookie || "";
284
+ if (!sessionId || lastActivity > 0 && now - lastActivity >= SESSION_IDLE_GAP_MS) {
285
+ sessionId = generateGuid();
286
+ }
287
+ localStorage.setItem(SESSION_ID_KEY, sessionId);
288
+ localStorage.setItem(SESSION_ACTIVITY_KEY, String(now));
289
+ writeCookie(SESSION_COOKIE_NAME, sessionId, Math.floor(SESSION_IDLE_GAP_MS / 1e3));
290
+ return sessionId;
291
+ }
263
292
  function setUserId(userId) {
264
293
  if (typeof window === "undefined") {
265
294
  return;
@@ -272,7 +301,10 @@ function clearIds() {
272
301
  }
273
302
  localStorage.removeItem(INSTANCE_ID_KEY);
274
303
  localStorage.removeItem(USER_ID_KEY);
304
+ localStorage.removeItem(SESSION_ID_KEY);
305
+ localStorage.removeItem(SESSION_ACTIVITY_KEY);
275
306
  document.cookie = `${ANON_COOKIE_NAME}=; Path=/; Max-Age=0; SameSite=Lax`;
307
+ document.cookie = `${SESSION_COOKIE_NAME}=; Path=/; Max-Age=0; SameSite=Lax`;
276
308
  }
277
309
 
278
310
  // src/targets.ts
@@ -435,6 +467,7 @@ async function fetchEdgeResolve(options) {
435
467
  params.set("variants", variantParam);
436
468
  } else if (options.anonId) {
437
469
  params.set("anonId", options.anonId);
470
+ if (options.sessionId) params.set("sessionId", options.sessionId);
438
471
  }
439
472
  const fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
440
473
  const init = {
@@ -536,6 +569,7 @@ async function fetchContent(options) {
536
569
  trackingId: options.trackingId,
537
570
  route: options.route,
538
571
  anonId: options.variantKeys ? void 0 : options.anonId,
572
+ sessionId: options.variantKeys ? void 0 : options.sessionId,
539
573
  siteId: options.siteId,
540
574
  fetch: options.fetchFn,
541
575
  revalidate: options.revalidate,
@@ -605,6 +639,7 @@ async function fetchEdgeSelect(options) {
605
639
  ...options.trackingId ? { trackingId: options.trackingId } : {},
606
640
  ...options.site ? { site: options.site } : {},
607
641
  anonId: options.anonId,
642
+ ...options.sessionId ? { sessionId: options.sessionId } : {},
608
643
  ...options.zones?.length ? { zones: options.zones } : {},
609
644
  ...options.context ? { context: options.context } : {}
610
645
  })
@@ -633,6 +668,7 @@ async function selectVariants(options) {
633
668
  site: options.site,
634
669
  view: options.view,
635
670
  anonId: options.anonId,
671
+ sessionId: options.sessionId,
636
672
  zones: options.zones,
637
673
  context: options.context,
638
674
  fetch: options.fetch,
@@ -717,7 +753,6 @@ function searchSpec(pagination, field, term) {
717
753
  }
718
754
 
719
755
  // src/index.ts
720
- var SESSION_IDLE_GAP_MS = 30 * 60 * 1e3;
721
756
  var DWELL_ACTIVITY_IDLE_MS = 2 * 60 * 1e3;
722
757
  var DWELL_POLL_MS = 5e3;
723
758
  function readPreviewQueryOverrides() {
@@ -893,6 +928,7 @@ var AmplifyUpSDK = class {
893
928
  if (typeof window === "undefined") return;
894
929
  window.AmplifyUp = {
895
930
  getInstanceId: () => this.getInstanceId(),
931
+ getSessionId: () => this.getSessionId(),
896
932
  getUserId: () => this.getUserId(),
897
933
  getTrackingId: () => this.getTrackingId(),
898
934
  getTarget: () => this.getTarget(),
@@ -1315,12 +1351,14 @@ var AmplifyUpSDK = class {
1315
1351
  return;
1316
1352
  }
1317
1353
  const instanceId = getInstanceId();
1354
+ const sessionId = getSessionId();
1318
1355
  const pageProperties = {
1319
1356
  name: url,
1320
1357
  path: url,
1321
1358
  url: typeof window !== "undefined" ? window.location.href : url,
1322
1359
  referrer: typeof document !== "undefined" ? document.referrer : void 0,
1323
1360
  instanceId,
1361
+ sessionId,
1324
1362
  trackingId: this.config?.trackingId,
1325
1363
  // Timestamp is included in event payload
1326
1364
  ...properties
@@ -1339,8 +1377,10 @@ var AmplifyUpSDK = class {
1339
1377
  return;
1340
1378
  }
1341
1379
  const instanceId = getInstanceId();
1380
+ const sessionId = getSessionId();
1342
1381
  const trackProperties = {
1343
1382
  instanceId,
1383
+ sessionId,
1344
1384
  trackingId: this.config?.trackingId,
1345
1385
  url: typeof window !== "undefined" ? window.location.href : void 0,
1346
1386
  path: properties?.path || (typeof window !== "undefined" ? window.location.pathname : void 0),
@@ -1413,8 +1453,10 @@ var AmplifyUpSDK = class {
1413
1453
  return;
1414
1454
  }
1415
1455
  const instanceId = getInstanceId();
1456
+ const sessionId = getSessionId();
1416
1457
  const identifyTraits = {
1417
1458
  instanceId,
1459
+ sessionId,
1418
1460
  trackingId: this.config?.trackingId,
1419
1461
  ...traits
1420
1462
  };
@@ -1452,6 +1494,7 @@ var AmplifyUpSDK = class {
1452
1494
  if (typeof this.adapter.setProfileFields === "function") {
1453
1495
  this.adapter.setProfileFields(fields, {
1454
1496
  instanceId: instanceId || void 0,
1497
+ sessionId: getSessionId() || void 0,
1455
1498
  trackingId: this.config?.trackingId
1456
1499
  });
1457
1500
  }
@@ -1502,6 +1545,12 @@ var AmplifyUpSDK = class {
1502
1545
  getInstanceId() {
1503
1546
  return getInstanceId();
1504
1547
  }
1548
+ /**
1549
+ * Get session id (30-minute inactivity reset).
1550
+ */
1551
+ getSessionId() {
1552
+ return getSessionId();
1553
+ }
1505
1554
  /**
1506
1555
  * Get user ID
1507
1556
  */
@@ -3010,13 +3059,13 @@ function getSDK(adapter) {
3010
3059
  var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
3011
3060
  var refreshEventTracking = () => getSDK().refreshEventTracking();
3012
3061
  getSDK();
3013
- var ComponentContext = react.createContext(null);
3062
+ var ComponentContext = React.createContext(null);
3014
3063
  function useLiveComposerProps(layoutNodeId, props) {
3015
- const [live, setLive] = react.useState(props);
3016
- react.useEffect(() => {
3064
+ const [live, setLive] = React.useState(props);
3065
+ React.useEffect(() => {
3017
3066
  setLive(props);
3018
3067
  }, [props]);
3019
- react.useEffect(() => {
3068
+ React.useEffect(() => {
3020
3069
  const matchesNode = (nodeId) => {
3021
3070
  if (nodeId && layoutNodeId && String(nodeId) !== String(layoutNodeId)) {
3022
3071
  return false;
@@ -3033,7 +3082,10 @@ function useLiveComposerProps(layoutNodeId, props) {
3033
3082
  return;
3034
3083
  }
3035
3084
  if (!matchesNode(nodeId)) return;
3036
- setLive(nextProps);
3085
+ setLive((prev) => ({
3086
+ ...prev,
3087
+ ...nextProps
3088
+ }));
3037
3089
  };
3038
3090
  const onPatch = (event) => {
3039
3091
  const detail = event.detail || {};
@@ -3063,7 +3115,7 @@ function useLiveComposerProps(layoutNodeId, props) {
3063
3115
  return live;
3064
3116
  }
3065
3117
  function useComponentProps() {
3066
- const context = react.useContext(ComponentContext);
3118
+ const context = React.useContext(ComponentContext);
3067
3119
  if (!context) {
3068
3120
  console.warn(
3069
3121
  "[AmplifyUp SDK] useComponentProps used outside ComponentContextProvider"
@@ -3092,7 +3144,7 @@ function Slot({
3092
3144
  name,
3093
3145
  className
3094
3146
  }) {
3095
- const context = react.useContext(ComponentContext);
3147
+ const context = React.useContext(ComponentContext);
3096
3148
  console.log(`[Slot:${name}] Rendering slot`, {
3097
3149
  hasContext: !!context,
3098
3150
  availableSlotKeys: context?.slots ? Object.keys(context.slots) : [],
@@ -3194,6 +3246,32 @@ function isLikelyNonTextFieldName(name) {
3194
3246
  const n = name.toLowerCase();
3195
3247
  return n.includes("href") || n.includes("url") || n.includes("src") || n.includes("image") || n.includes("icon") || n.includes("overlay") || n.includes("color");
3196
3248
  }
3249
+ function isPlainTextValue(value) {
3250
+ return typeof value === "string" || typeof value === "number";
3251
+ }
3252
+ var COMPOSER_EMPTY_FIELD_HINT = "Click here to edit this field";
3253
+ var EMPTY_FIELD_PLACEHOLDERS = /* @__PURE__ */ new Set([
3254
+ COMPOSER_EMPTY_FIELD_HINT,
3255
+ "Click here to enter text",
3256
+ "Sample Title",
3257
+ "Sample Heading",
3258
+ "Sample Subheading"
3259
+ ]);
3260
+ function isComposerEmptyText(value) {
3261
+ if (value === void 0 || value === null) return true;
3262
+ if (typeof value !== "string") return false;
3263
+ const trimmed = value.trim();
3264
+ return trimmed === "" || EMPTY_FIELD_PLACEHOLDERS.has(trimmed);
3265
+ }
3266
+ function isReactNodeFallback(fallback) {
3267
+ if (fallback == null || fallback === false) return false;
3268
+ return !isPlainTextValue(fallback);
3269
+ }
3270
+ function htmlHasVisualChrome(html) {
3271
+ return /<(input|button|select|textarea|form|svg|iframe|video|audio|img)\b/i.test(
3272
+ html
3273
+ );
3274
+ }
3197
3275
  function canInlineEdit(name, value) {
3198
3276
  if (isLikelyNonTextFieldName(name)) return false;
3199
3277
  if (value !== void 0 && value !== null && typeof value === "object") {
@@ -3202,19 +3280,22 @@ function canInlineEdit(name, value) {
3202
3280
  if (typeof value === "boolean") return false;
3203
3281
  return true;
3204
3282
  }
3283
+ function subscribeComposerEdit(onStoreChange) {
3284
+ if (typeof window === "undefined") return () => {
3285
+ };
3286
+ window.addEventListener("amplifyup:composer-edit", onStoreChange);
3287
+ return () => window.removeEventListener("amplifyup:composer-edit", onStoreChange);
3288
+ }
3205
3289
  function useComposerEditMode() {
3206
- const [enabled, setEnabled] = react.useState(false);
3207
- react.useEffect(() => {
3208
- const sync = () => setEnabled(isComposerEditMode());
3209
- sync();
3210
- window.addEventListener("amplifyup:composer-edit", sync);
3211
- return () => window.removeEventListener("amplifyup:composer-edit", sync);
3212
- }, []);
3213
- return enabled;
3290
+ return React.useSyncExternalStore(
3291
+ subscribeComposerEdit,
3292
+ isComposerEditMode,
3293
+ () => false
3294
+ );
3214
3295
  }
3215
3296
  function useFieldOverlay(layoutNodeId, name, source) {
3216
- const [overlay, setOverlay] = react.useState(void 0);
3217
- react.useEffect(() => {
3297
+ const [overlay, setOverlay] = React.useState(void 0);
3298
+ React.useEffect(() => {
3218
3299
  const onPatch = (event) => {
3219
3300
  const detail = event.detail || {};
3220
3301
  if (detail.fieldName !== name) return;
@@ -3241,6 +3322,14 @@ function commitComposerField(layoutNodeId, componentId, name, value) {
3241
3322
  "*"
3242
3323
  );
3243
3324
  }
3325
+ function placeCaret(el, atStart = false) {
3326
+ const range = document.createRange();
3327
+ range.selectNodeContents(el);
3328
+ range.collapse(atStart);
3329
+ const sel = window.getSelection();
3330
+ sel?.removeAllRanges();
3331
+ sel?.addRange(range);
3332
+ }
3244
3333
  function ComposerInlineField({
3245
3334
  name,
3246
3335
  value,
@@ -3250,36 +3339,55 @@ function ComposerInlineField({
3250
3339
  layoutNodeId,
3251
3340
  componentId
3252
3341
  }) {
3253
- const [editing, setEditing] = react.useState(false);
3254
- const elRef = react.useRef(null);
3255
- const text = formatFieldValue(value);
3342
+ const [editing, setEditing] = React.useState(false);
3343
+ const [focused, setFocused] = React.useState(false);
3344
+ const elRef = React.useRef(null);
3345
+ const storedEmpty = isComposerEmptyText(value);
3346
+ const text = storedEmpty ? "" : formatFieldValue(value);
3256
3347
  const fallbackText = typeof fallback === "string" || typeof fallback === "number" ? String(fallback) : "";
3257
- const display = text || fallbackText;
3348
+ const hint = COMPOSER_EMPTY_FIELD_HINT;
3349
+ const idleDisplay = storedEmpty ? hint : text || fallbackText;
3258
3350
  const showRendered = !!render && !editing;
3259
- react.useEffect(() => {
3351
+ const showHint = storedEmpty && !focused && !showRendered;
3352
+ React.useEffect(() => {
3260
3353
  const el = elRef.current;
3261
3354
  if (!el || showRendered) return;
3262
3355
  if (document.activeElement === el) return;
3263
- const next = display || "";
3356
+ if (el.querySelector(
3357
+ "input, button, select, textarea, form, svg, iframe, img"
3358
+ )) {
3359
+ return;
3360
+ }
3361
+ const next = idleDisplay || "";
3264
3362
  if (el.textContent !== next) el.textContent = next;
3265
- }, [display, showRendered]);
3266
- react.useEffect(() => {
3363
+ }, [idleDisplay, showRendered]);
3364
+ React.useEffect(() => {
3267
3365
  if (!editing || !elRef.current) return;
3268
3366
  const el = elRef.current;
3269
- el.textContent = display || "";
3367
+ if (storedEmpty) {
3368
+ el.textContent = "";
3369
+ el.focus();
3370
+ placeCaret(el, true);
3371
+ return;
3372
+ }
3373
+ el.textContent = idleDisplay || "";
3270
3374
  el.focus();
3271
- const range = document.createRange();
3272
- range.selectNodeContents(el);
3273
- const sel = window.getSelection();
3274
- sel?.removeAllRanges();
3275
- sel?.addRange(range);
3276
- }, [editing]);
3375
+ placeCaret(el, false);
3376
+ }, [editing, storedEmpty, idleDisplay]);
3277
3377
  const finishEdit = () => {
3278
3378
  const raw = (elRef.current?.innerText ?? "").replace(/\u00a0/g, " ").replace(/\n+$/g, "");
3279
3379
  const next = raw.trim() === "" ? "" : raw;
3280
3380
  setEditing(false);
3381
+ setFocused(false);
3382
+ if (storedEmpty && next === "") {
3383
+ if (elRef.current) elRef.current.textContent = hint;
3384
+ if (typeof value === "string" && EMPTY_FIELD_PLACEHOLDERS.has(value.trim())) {
3385
+ commitComposerField(layoutNodeId, componentId, name, "");
3386
+ }
3387
+ return;
3388
+ }
3281
3389
  if (next === text) return;
3282
- if (!text && next === fallbackText) return;
3390
+ if (!text && !storedEmpty && next === fallbackText) return;
3283
3391
  commitComposerField(layoutNodeId, componentId, name, next);
3284
3392
  };
3285
3393
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3289,6 +3397,7 @@ function ComposerInlineField({
3289
3397
  className,
3290
3398
  "data-amplifyup-field": name,
3291
3399
  "data-amplifyup-node-id": layoutNodeId || void 0,
3400
+ "data-amplifyup-empty-hint": showHint ? "true" : void 0,
3292
3401
  contentEditable: !showRendered,
3293
3402
  suppressContentEditableWarning: true,
3294
3403
  role: "textbox",
@@ -3305,6 +3414,12 @@ function ComposerInlineField({
3305
3414
  },
3306
3415
  onFocus: () => {
3307
3416
  if (!isComposerEditMode()) return;
3417
+ setFocused(true);
3418
+ const el = elRef.current;
3419
+ if (el && storedEmpty) {
3420
+ el.textContent = "";
3421
+ placeCaret(el, true);
3422
+ }
3308
3423
  if (render) setEditing(true);
3309
3424
  },
3310
3425
  onBlur: finishEdit,
@@ -3316,8 +3431,9 @@ function ComposerInlineField({
3316
3431
  }
3317
3432
  if (event.key === "Escape") {
3318
3433
  event.preventDefault();
3319
- if (elRef.current) elRef.current.textContent = display || "";
3434
+ if (elRef.current) elRef.current.textContent = idleDisplay || "";
3320
3435
  setEditing(false);
3436
+ setFocused(false);
3321
3437
  event.currentTarget.blur();
3322
3438
  }
3323
3439
  },
@@ -3326,12 +3442,59 @@ function ComposerInlineField({
3326
3442
  outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
3327
3443
  outlineOffset: "2px",
3328
3444
  minWidth: "1ch",
3329
- display: render ? void 0 : "inline"
3445
+ minHeight: "1em",
3446
+ display: render ? void 0 : "inline",
3447
+ opacity: showHint ? 0.5 : void 0
3330
3448
  },
3331
- children: showRendered ? value != null && value !== "" ? render(value) : fallback ?? display : null
3449
+ children: showRendered ? value != null && value !== "" && !storedEmpty ? render(value) : fallback ?? idleDisplay : isReactNodeFallback(fallback) && !idleDisplay ? fallback : null
3332
3450
  }
3333
3451
  );
3334
3452
  }
3453
+ function ComposerInlineHtml({
3454
+ name,
3455
+ html,
3456
+ className,
3457
+ Tag = "div",
3458
+ layoutNodeId,
3459
+ componentId
3460
+ }) {
3461
+ const elRef = React.useRef(null);
3462
+ const sourceTextRef = React.useRef("");
3463
+ React.useEffect(() => {
3464
+ const el = elRef.current;
3465
+ if (!el) return;
3466
+ if (document.activeElement === el || el.contains(document.activeElement)) {
3467
+ return;
3468
+ }
3469
+ if (el.innerHTML !== html) el.innerHTML = html;
3470
+ sourceTextRef.current = (el.innerText ?? "").replace(/\u00a0/g, " ").trim();
3471
+ }, [html]);
3472
+ const finishEdit = () => {
3473
+ const el = elRef.current;
3474
+ if (!el) return;
3475
+ const next = (el.innerText ?? "").replace(/\u00a0/g, " ").replace(/\n+$/g, "");
3476
+ if (next.trim() === sourceTextRef.current) return;
3477
+ commitComposerField(layoutNodeId, componentId, name, next.trim());
3478
+ };
3479
+ return React__default.default.createElement(Tag, {
3480
+ ref: elRef,
3481
+ className,
3482
+ "data-amplifyup-field": name,
3483
+ "data-amplifyup-node-id": layoutNodeId || void 0,
3484
+ contentEditable: true,
3485
+ suppressContentEditableWarning: true,
3486
+ dangerouslySetInnerHTML: { __html: html },
3487
+ onMouseDown: (event) => {
3488
+ event.stopPropagation();
3489
+ },
3490
+ onBlur: finishEdit,
3491
+ style: {
3492
+ cursor: "text",
3493
+ outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
3494
+ outlineOffset: "2px"
3495
+ }
3496
+ });
3497
+ }
3335
3498
  function formatFieldValue(value) {
3336
3499
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3337
3500
  return String(value);
@@ -3384,7 +3547,7 @@ function Field({
3384
3547
  className,
3385
3548
  render
3386
3549
  }) {
3387
- const context = react.useContext(ComponentContext);
3550
+ const context = React.useContext(ComponentContext);
3388
3551
  const composerEdit = useComposerEditMode();
3389
3552
  const fieldValue = useFieldOverlay(
3390
3553
  context?.layoutNodeId,
@@ -3398,7 +3561,13 @@ function Field({
3398
3561
  if (render) {
3399
3562
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className, style: { display: "contents" }, children: render(fieldValue) });
3400
3563
  }
3401
- if (composerEdit && canInlineEdit(name, fieldValue)) {
3564
+ if (Array.isArray(fieldValue)) {
3565
+ const list = renderLinkList(fieldValue, className);
3566
+ if (list) return list;
3567
+ }
3568
+ const text = formatFieldValue(fieldValue);
3569
+ const showVisualFallback = !text && isReactNodeFallback(fallback);
3570
+ if (composerEdit && canInlineEdit(name, fieldValue) && !showVisualFallback) {
3402
3571
  return /* @__PURE__ */ jsxRuntime.jsx(
3403
3572
  ComposerInlineField,
3404
3573
  {
@@ -3414,11 +3583,6 @@ function Field({
3414
3583
  if (fieldValue === void 0 || fieldValue === null) {
3415
3584
  return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
3416
3585
  }
3417
- if (Array.isArray(fieldValue)) {
3418
- const list = renderLinkList(fieldValue, className);
3419
- if (list) return list;
3420
- }
3421
- const text = formatFieldValue(fieldValue);
3422
3586
  if (!text) {
3423
3587
  return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
3424
3588
  }
@@ -3438,7 +3602,7 @@ function RichText({
3438
3602
  className,
3439
3603
  as: Tag = "div"
3440
3604
  }) {
3441
- const context = react.useContext(ComponentContext);
3605
+ const context = React.useContext(ComponentContext);
3442
3606
  const composerEdit = useComposerEditMode();
3443
3607
  const fieldValue = useFieldOverlay(
3444
3608
  context?.layoutNodeId,
@@ -3449,20 +3613,20 @@ function RichText({
3449
3613
  console.warn(`[AmplifyUp SDK] RichText "${name}" used outside ComponentContextProvider`);
3450
3614
  return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3451
3615
  }
3452
- if (composerEdit && canInlineEdit(name, fieldValue)) {
3453
- return /* @__PURE__ */ jsxRuntime.jsx(
3454
- ComposerInlineField,
3455
- {
3456
- name,
3457
- value: fieldValue,
3458
- fallback,
3459
- className,
3460
- layoutNodeId: context.layoutNodeId,
3461
- componentId: context.componentId
3462
- }
3463
- );
3464
- }
3465
- if (fieldValue === void 0 || fieldValue === null || fieldValue === "") {
3616
+ if (isComposerEmptyText(fieldValue)) {
3617
+ if (composerEdit && canInlineEdit(name, fieldValue) && !isReactNodeFallback(fallback)) {
3618
+ return /* @__PURE__ */ jsxRuntime.jsx(
3619
+ ComposerInlineField,
3620
+ {
3621
+ name,
3622
+ value: fieldValue,
3623
+ fallback: typeof fallback === "string" || typeof fallback === "number" ? fallback : void 0,
3624
+ className,
3625
+ layoutNodeId: context.layoutNodeId,
3626
+ componentId: context.componentId
3627
+ }
3628
+ );
3629
+ }
3466
3630
  return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3467
3631
  }
3468
3632
  let html = "";
@@ -3480,6 +3644,30 @@ function RichText({
3480
3644
  if (!html) {
3481
3645
  return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3482
3646
  }
3647
+ if (composerEdit && htmlHasVisualChrome(html)) {
3648
+ return /* @__PURE__ */ jsxRuntime.jsx(
3649
+ Tag,
3650
+ {
3651
+ className,
3652
+ "data-amplifyup-field": name,
3653
+ "data-amplifyup-node-id": context.layoutNodeId || void 0,
3654
+ dangerouslySetInnerHTML: { __html: html }
3655
+ }
3656
+ );
3657
+ }
3658
+ if (composerEdit && canInlineEdit(name, fieldValue)) {
3659
+ return /* @__PURE__ */ jsxRuntime.jsx(
3660
+ ComposerInlineHtml,
3661
+ {
3662
+ name,
3663
+ html,
3664
+ className,
3665
+ Tag,
3666
+ layoutNodeId: context.layoutNodeId,
3667
+ componentId: context.componentId
3668
+ }
3669
+ );
3670
+ }
3483
3671
  return /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, dangerouslySetInnerHTML: { __html: html } });
3484
3672
  }
3485
3673
  function isPortableImageValue(value) {
@@ -3500,7 +3688,7 @@ function Image({
3500
3688
  height: heightProp,
3501
3689
  ...imgProps
3502
3690
  }) {
3503
- const context = react.useContext(ComponentContext);
3691
+ const context = React.useContext(ComponentContext);
3504
3692
  const fieldValue = useFieldOverlay(
3505
3693
  context?.layoutNodeId,
3506
3694
  name,
@@ -3530,7 +3718,13 @@ function Image({
3530
3718
  }
3531
3719
  );
3532
3720
  }
3533
- var ComponentRenderBoundary = class extends react.Component {
3721
+ function nodeRenderProps(node) {
3722
+ return {
3723
+ ...node.settings || {},
3724
+ ...node.props || {}
3725
+ };
3726
+ }
3727
+ var ComponentRenderBoundary = class extends React.Component {
3534
3728
  constructor() {
3535
3729
  super(...arguments);
3536
3730
  this.state = { error: null };
@@ -3602,8 +3796,8 @@ function AmplifyRenderer({
3602
3796
  const loading = serverPageConfig ? false : clientHook.loading;
3603
3797
  const error = serverPageConfig ? null : clientHook.error;
3604
3798
  const contentSource = clientHook.source;
3605
- const [variantCache, setVariantCache] = react.useState({});
3606
- react.useEffect(() => {
3799
+ const [variantCache, setVariantCache] = React.useState({});
3800
+ React.useEffect(() => {
3607
3801
  if (typeof window !== "undefined") {
3608
3802
  if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
3609
3803
  return;
@@ -3629,7 +3823,7 @@ function AmplifyRenderer({
3629
3823
  });
3630
3824
  }
3631
3825
  }, [pageConfig]);
3632
- react.useEffect(() => {
3826
+ React.useEffect(() => {
3633
3827
  if (!pageConfig?.eventTracking?.length) return;
3634
3828
  const sdk = getSDK();
3635
3829
  if (!sdk.isInitialized()) return;
@@ -3647,7 +3841,7 @@ function AmplifyRenderer({
3647
3841
  window.clearTimeout(t2);
3648
3842
  };
3649
3843
  }, [pageConfig?.id, pageConfig?.eventTracking]);
3650
- react.useEffect(() => {
3844
+ React.useEffect(() => {
3651
3845
  if (!pageConfig) return;
3652
3846
  const t = window.setTimeout(() => {
3653
3847
  getSDK().setupComponentViewTracking();
@@ -3655,7 +3849,7 @@ function AmplifyRenderer({
3655
3849
  }, 100);
3656
3850
  return () => window.clearTimeout(t);
3657
3851
  }, [variantCache, pageConfig?.id]);
3658
- react.useEffect(() => {
3852
+ React.useEffect(() => {
3659
3853
  const isVisualEditorMode = () => {
3660
3854
  if (typeof window === "undefined") return false;
3661
3855
  const urlParams = new URLSearchParams(window.location.search);
@@ -3741,7 +3935,7 @@ function AmplifyRenderer({
3741
3935
  console.log("[AmplifyRenderer] Sent component tree from layoutTree:", componentTree.length, "root components");
3742
3936
  }
3743
3937
  }, [pageConfig]);
3744
- react.useEffect(() => {
3938
+ React.useEffect(() => {
3745
3939
  if (!pageConfig?.layoutTree || !pageConfig.layoutId) return;
3746
3940
  const roots = pageConfig.layoutTree;
3747
3941
  const fromEdge = contentSource === "edge" || roots.some((n) => n._contentSource === "edge");
@@ -3882,7 +4076,7 @@ function AmplifyRenderer({
3882
4076
  "data-amplifyup-slot": "content",
3883
4077
  "data-parent-node-id": node.id,
3884
4078
  "data-parent-component-id": "AmplifyZone",
3885
- children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(child) }, child.id))
4079
+ children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: renderNode(child) }, child.id))
3886
4080
  }
3887
4081
  )
3888
4082
  }
@@ -3932,7 +4126,7 @@ function AmplifyRenderer({
3932
4126
  "data-zone-id": slot.zoneId || void 0,
3933
4127
  "data-parent-node-id": node.id,
3934
4128
  "data-parent-component-id": node.componentId,
3935
- children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderNode(child) }, child.id))
4129
+ children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: renderNode(child) }, child.id))
3936
4130
  },
3937
4131
  slot.slotId
3938
4132
  );
@@ -3953,7 +4147,7 @@ function AmplifyRenderer({
3953
4147
  {
3954
4148
  nodeId: node.id,
3955
4149
  componentId: node.componentId,
3956
- props: node.props || {},
4150
+ props: nodeRenderProps(node),
3957
4151
  slots: slotsToPass,
3958
4152
  renderComponent
3959
4153
  }
@@ -4123,14 +4317,14 @@ if (typeof window !== "undefined") {
4123
4317
  } catch {
4124
4318
  }
4125
4319
  }
4126
- var AmplifyUpContext = react.createContext(null);
4320
+ var AmplifyUpContext = React.createContext(null);
4127
4321
  function AmplifyUpProvider({ children, config }) {
4128
- const [initialized, setInitialized] = react.useState(false);
4129
- const [pageConfig, setPageConfig] = react.useState(null);
4130
- const [loading, setLoading] = react.useState(true);
4131
- const [error, setError] = react.useState(null);
4132
- const [source, setSource] = react.useState(null);
4133
- const [meta, setMeta] = react.useState(null);
4322
+ const [initialized, setInitialized] = React.useState(false);
4323
+ const [pageConfig, setPageConfig] = React.useState(null);
4324
+ const [loading, setLoading] = React.useState(true);
4325
+ const [error, setError] = React.useState(null);
4326
+ const [source, setSource] = React.useState(null);
4327
+ const [meta, setMeta] = React.useState(null);
4134
4328
  const getActualRoute = () => {
4135
4329
  if (typeof window === "undefined") return "";
4136
4330
  const currentPath = window.location.pathname;
@@ -4148,13 +4342,13 @@ function AmplifyUpProvider({ children, config }) {
4148
4342
  return currentPath;
4149
4343
  };
4150
4344
  const nextPathname = usePathname ? usePathname() : null;
4151
- const [pathname, setPathname] = react.useState(() => {
4345
+ const [pathname, setPathname] = React.useState(() => {
4152
4346
  if (typeof window !== "undefined") {
4153
4347
  return getActualRoute();
4154
4348
  }
4155
4349
  return "";
4156
4350
  });
4157
- react.useEffect(() => {
4351
+ React.useEffect(() => {
4158
4352
  if (typeof window !== "undefined") {
4159
4353
  const actualRoute = getActualRoute();
4160
4354
  if (window.location.pathname === "/api/pages/proxy" || window.location.pathname.startsWith("/api/pages/proxy")) {
@@ -4180,7 +4374,7 @@ function AmplifyUpProvider({ children, config }) {
4180
4374
  };
4181
4375
  }
4182
4376
  }, [nextPathname]);
4183
- react.useEffect(() => {
4377
+ React.useEffect(() => {
4184
4378
  let cancelled = false;
4185
4379
  const sdk = getSDK();
4186
4380
  sdk.init(config).then(() => {
@@ -4197,7 +4391,7 @@ function AmplifyUpProvider({ children, config }) {
4197
4391
  cancelled = true;
4198
4392
  };
4199
4393
  }, [config.trackingId, config.target, config.debug, config.dev, config.siteId]);
4200
- react.useEffect(() => {
4394
+ React.useEffect(() => {
4201
4395
  if (!initialized) return;
4202
4396
  let cancelled = false;
4203
4397
  const load = async () => {
@@ -4229,12 +4423,14 @@ function AmplifyUpProvider({ children, config }) {
4229
4423
  }
4230
4424
  let variantKeys;
4231
4425
  const anonId = sdk.getInstanceId();
4426
+ const sessionId = sdk.getSessionId();
4232
4427
  if (!preview && anonId) {
4233
4428
  try {
4234
4429
  const selected = await selectVariants({
4235
4430
  trackingId,
4236
4431
  view: actualRoute,
4237
4432
  anonId,
4433
+ sessionId: sessionId || void 0,
4238
4434
  site: sdk.getSiteId() || void 0,
4239
4435
  edgeUrl
4240
4436
  });
@@ -4247,6 +4443,7 @@ function AmplifyUpProvider({ children, config }) {
4247
4443
  route: actualRoute,
4248
4444
  trackingId,
4249
4445
  anonId: variantKeys ? void 0 : anonId,
4446
+ sessionId: variantKeys ? void 0 : sessionId || void 0,
4250
4447
  siteId: sdk.getSiteId() || null,
4251
4448
  preview,
4252
4449
  orchestratorUrl,
@@ -4293,7 +4490,7 @@ function AmplifyUpProvider({ children, config }) {
4293
4490
  );
4294
4491
  }
4295
4492
  function useAmplifyUp() {
4296
- const context = react.useContext(AmplifyUpContext);
4493
+ const context = React.useContext(AmplifyUpContext);
4297
4494
  if (!context) {
4298
4495
  throw new Error("useAmplifyUp must be used within AmplifyUpProvider");
4299
4496
  }