@ohhwells/bridge 0.1.113-next.294 → 0.1.113-next.296

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/index.cjs CHANGED
@@ -105,6 +105,10 @@ function enforceLinkHrefs() {
105
105
 
106
106
  // src/lib/ai-tree.ts
107
107
  var AI_TREE_SCHEMA_VERSIONS = [1];
108
+ var BUTTON_FILL_TOKENS = ["dark", "primary", "accent", "light"];
109
+ var BUTTON_STYLES = ["solid", "outline", "ghost"];
110
+ var BUTTON_RADII = ["square", "rounded", "pill"];
111
+ var BUTTON_SIZES = ["s", "m", "l"];
108
112
  var AI_DEFAULT_BRAND = {
109
113
  palette: { dark: "#0C0A09", primary: "#0078E5", accent: "#747271", light: "#ECECEB" },
110
114
  fonts: {
@@ -362,6 +366,59 @@ var FONT_VARS = {
362
366
  var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
363
367
  var CUSTOM_FONT_STYLE_ID = "ohw-brand-custom-fonts";
364
368
  var CUSTOM_FONT_FORMATS = /* @__PURE__ */ new Set(["woff2", "woff", "truetype", "opentype"]);
369
+ var BUTTON_FILL_VAR = {
370
+ dark: "var(--brand-text)",
371
+ primary: "var(--brand-primary)",
372
+ accent: "var(--brand-accent)",
373
+ light: "var(--brand-background)"
374
+ };
375
+ var BUTTON_RADIUS_PX = {
376
+ square: 0,
377
+ rounded: AI_TREE_TOKENS.radiusButton,
378
+ pill: 999
379
+ };
380
+ var BUTTON_SIZE_SPEC = {
381
+ s: {
382
+ paddingX: `${AI_TREE_TOKENS.spacing4}px`,
383
+ paddingY: `${AI_TREE_TOKENS.spacing2}px`,
384
+ fontSize: "14px"
385
+ },
386
+ m: {
387
+ paddingX: `${AI_TREE_TOKENS.spacing6}px`,
388
+ paddingY: `${AI_TREE_TOKENS.spacing3}px`,
389
+ fontSize: `${AI_TREE_TOKENS.type.button.size}px`
390
+ },
391
+ l: {
392
+ paddingX: `${AI_TREE_TOKENS.spacing8}px`,
393
+ paddingY: `${AI_TREE_TOKENS.spacing4}px`,
394
+ fontSize: "18px"
395
+ }
396
+ };
397
+ var BUTTON_VAR_ROLES = ["bg", "text", "border", "radius", "padding-x", "padding-y", "font-size"];
398
+ var BUTTON_VAR_NAMES = ["primary", "secondary"].flatMap(
399
+ (variant) => BUTTON_VAR_ROLES.map((role) => `--brand-button-${variant}-${role}`)
400
+ );
401
+ function buttonVariantVars(variant, style) {
402
+ const fill = BUTTON_FILL_VAR[style.fill];
403
+ const text = BUTTON_FILL_VAR[style.textColor];
404
+ const size = BUTTON_SIZE_SPEC[style.size];
405
+ const { bg, border } = style.style === "outline" ? { bg: "transparent", border: fill } : style.style === "ghost" ? { bg: "transparent", border: "transparent" } : { bg: fill, border: "transparent" };
406
+ return {
407
+ [`--brand-button-${variant}-bg`]: bg,
408
+ [`--brand-button-${variant}-text`]: text,
409
+ [`--brand-button-${variant}-border`]: border,
410
+ [`--brand-button-${variant}-radius`]: `${BUTTON_RADIUS_PX[style.radius]}px`,
411
+ [`--brand-button-${variant}-padding-x`]: size.paddingX,
412
+ [`--brand-button-${variant}-padding-y`]: size.paddingY,
413
+ [`--brand-button-${variant}-font-size`]: size.fontSize
414
+ };
415
+ }
416
+ function buttonStyleVars(buttons) {
417
+ return {
418
+ ...buttonVariantVars("primary", buttons.primary),
419
+ ...buttonVariantVars("secondary", buttons.secondary)
420
+ };
421
+ }
365
422
  function brandColorVars(palette) {
366
423
  const { dark, primary, accent, light } = palette;
367
424
  const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
@@ -389,6 +446,11 @@ function brandColorVars(palette) {
389
446
  "--brand-on-primary": light
390
447
  };
391
448
  }
449
+ function isButtonStyle(value) {
450
+ if (!value || typeof value !== "object") return false;
451
+ const v = value;
452
+ return BUTTON_FILL_TOKENS.includes(v.fill) && BUTTON_FILL_TOKENS.includes(v.textColor) && BUTTON_STYLES.includes(v.style) && BUTTON_RADII.includes(v.radius) && BUTTON_SIZES.includes(v.size);
453
+ }
392
454
  function parseCustomFontWeight(raw) {
393
455
  if (typeof raw !== "object" || raw === null) return null;
394
456
  const w = raw;
@@ -417,11 +479,14 @@ function parseBrandKit(raw) {
417
479
  const palette = p && typeof p.dark === "string" && typeof p.primary === "string" && typeof p.accent === "string" && typeof p.light === "string" ? { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light } : void 0;
418
480
  const fonts = f && typeof f.heading === "string" && typeof f.body === "string" ? { heading: f.heading, body: f.body } : void 0;
419
481
  const customFonts = Array.isArray(parsed?.customFonts) ? parsed.customFonts.map(parseCustomFont).filter((c) => c !== null) : void 0;
420
- if (!palette && !fonts && !customFonts) return null;
482
+ const b = parsed?.buttons;
483
+ const buttons = b && isButtonStyle(b.primary) && isButtonStyle(b.secondary) ? { primary: b.primary, secondary: b.secondary } : void 0;
484
+ if (!palette && !fonts && !customFonts && !buttons) return null;
421
485
  return {
422
486
  ...palette ? { palette } : {},
423
487
  ...fonts ? { fonts } : {},
424
- ...customFonts ? { customFonts } : {}
488
+ ...customFonts ? { customFonts } : {},
489
+ ...buttons ? { buttons } : {}
425
490
  };
426
491
  } catch {
427
492
  return null;
@@ -475,7 +540,9 @@ function loadCustomFonts(fonts) {
475
540
  function applyBrandToDom(kit) {
476
541
  const root = document.documentElement;
477
542
  if (!kit) {
478
- for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
543
+ for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...BUTTON_VAR_NAMES]) {
544
+ root.style.removeProperty(name);
545
+ }
479
546
  for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
480
547
  document.getElementById(BRAND_FONT_LINK_ID)?.remove();
481
548
  document.getElementById(CUSTOM_FONT_STYLE_ID)?.remove();
@@ -484,6 +551,9 @@ function applyBrandToDom(kit) {
484
551
  if (kit.palette) {
485
552
  for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
486
553
  }
554
+ if (kit.buttons) {
555
+ for (const [name, value] of Object.entries(buttonStyleVars(kit.buttons))) root.style.setProperty(name, value);
556
+ }
487
557
  if (kit.customFonts) {
488
558
  loadCustomFonts(kit.customFonts);
489
559
  }
@@ -612,7 +682,6 @@ var NODE_PROPS = [
612
682
  "color",
613
683
  "font-family",
614
684
  "font-size",
615
- "background",
616
685
  "text-align",
617
686
  "justify-content",
618
687
  "align-items"
@@ -662,9 +731,6 @@ function clearNodeProps(root) {
662
731
  h.removeAttribute(NODE_WROTE_ATTR);
663
732
  }
664
733
  }
665
- function buttonSurfaceOf(el) {
666
- return el.closest("a, button") ?? el;
667
- }
668
734
  function alignSubjectOf(el) {
669
735
  const button = el.closest('[data-ohw-role="button"]');
670
736
  if (button?.parentElement) return button.parentElement;
@@ -780,18 +846,6 @@ function applyStylesToDom(store) {
780
846
  );
781
847
  subject.setAttribute(NODE_WROTE_ATTR, "");
782
848
  }
783
- if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
784
- const surface = buttonSurfaceOf(el);
785
- if (override.buttonBackground !== void 0) {
786
- saveInline(surface, "background");
787
- surface.style.setProperty("background", override.buttonBackground, "important");
788
- }
789
- if (override.buttonText !== void 0) {
790
- saveInline(surface, "color");
791
- surface.style.setProperty("color", override.buttonText, "important");
792
- }
793
- surface.setAttribute(NODE_WROTE_ATTR, "");
794
- }
795
849
  }
796
850
  }
797
851
  for (const [sectionId, override] of Object.entries(store.sections)) {
@@ -1386,7 +1440,59 @@ var FEATURE_LINE_CSS = [
1386
1440
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
1387
1441
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
1388
1442
  ].join("");
1389
- function buttonShellStyle(ctx, fullWidth) {
1443
+ function buttonSizeSpec(size) {
1444
+ switch (size) {
1445
+ case "s":
1446
+ return { padding: `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px`, fontSize: 14 };
1447
+ case "l":
1448
+ return { padding: `${AI_TREE_TOKENS.spacing4}px ${AI_TREE_TOKENS.spacing8}px`, fontSize: 18 };
1449
+ case "m":
1450
+ default:
1451
+ return { padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`, fontSize: AI_TREE_TOKENS.type.button.size };
1452
+ }
1453
+ }
1454
+ function buttonRadiusPx(radius) {
1455
+ switch (radius) {
1456
+ case "square":
1457
+ return 0;
1458
+ case "pill":
1459
+ return 999;
1460
+ case "rounded":
1461
+ default:
1462
+ return AI_TREE_TOKENS.radiusButton;
1463
+ }
1464
+ }
1465
+ function resolveButtonToken(brand, token) {
1466
+ return brand.palette[token];
1467
+ }
1468
+ function buttonColorStyle(ctx, variant) {
1469
+ const token = ctx.brand.buttons?.[variant];
1470
+ if (!token) return null;
1471
+ const fill = resolveButtonToken(ctx.brand, token.fill);
1472
+ const text = resolveButtonToken(ctx.brand, token.textColor);
1473
+ switch (token.style) {
1474
+ case "outline":
1475
+ return { background: "transparent", border: `1px solid ${fill}`, color: text };
1476
+ case "ghost":
1477
+ return { background: "transparent", border: "1px solid transparent", color: text };
1478
+ case "solid":
1479
+ default:
1480
+ return { background: fill, color: text };
1481
+ }
1482
+ }
1483
+ function buttonShellStyle(ctx, variant, fullWidth) {
1484
+ const buttonToken = ctx.brand.buttons?.[variant];
1485
+ if (buttonToken) {
1486
+ const { padding, fontSize } = buttonSizeSpec(buttonToken.size);
1487
+ return {
1488
+ borderRadius: buttonRadiusPx(buttonToken.radius),
1489
+ padding,
1490
+ fontFamily: ctx.brand.fonts.body,
1491
+ fontSize,
1492
+ lineHeight: AI_TREE_TOKENS.type.button.line,
1493
+ fontWeight: AI_TREE_TOKENS.type.button.weight
1494
+ };
1495
+ }
1390
1496
  const bs = ctx.buttonStyle;
1391
1497
  if (bs) {
1392
1498
  return {
@@ -1534,6 +1640,8 @@ function ButtonEl({
1534
1640
  fullWidth
1535
1641
  }) {
1536
1642
  const secondary = slots.variant === "secondary";
1643
+ const variant = secondary ? "secondary" : "primary";
1644
+ const tokenColorStyle = buttonColorStyle(ctx, variant);
1537
1645
  const href = str(slots.href);
1538
1646
  const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
1539
1647
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1548,13 +1656,13 @@ function ButtonEl({
1548
1656
  justifyContent: "center",
1549
1657
  textDecoration: "none",
1550
1658
  cursor: "pointer",
1551
- ...buttonShellStyle(ctx, fullWidth),
1552
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : (
1659
+ ...buttonShellStyle(ctx, variant, fullWidth),
1660
+ ...tokenColorStyle ?? (secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : (
1553
1661
  // Off-band, prefer the template button's measured fill/label pair — a template may
1554
1662
  // fill its CTAs with any token (hvac: accent bg, primary text). On an accent band
1555
1663
  // the flipped palette keeps contrast, so the brand-driven colours stay.
1556
1664
  !ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1557
- )
1665
+ ))
1558
1666
  },
1559
1667
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
1560
1668
  }
@@ -2625,13 +2733,13 @@ function renderNode(node, ctx, path) {
2625
2733
  border: "none",
2626
2734
  cursor: "pointer",
2627
2735
  // Shape/padding/typography follow the host template's own buttons.
2628
- ...buttonShellStyle(ctx),
2736
+ ...buttonShellStyle(ctx, "primary"),
2629
2737
  // Brand-styled: the template button's measured fill/label pair off-band, else
2630
2738
  // primary fill with a brand-derived label so it reads on custom palettes.
2631
- ...!ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : {
2739
+ ...buttonColorStyle(ctx, "primary") ?? (!ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : {
2632
2740
  background: ctx.brand.palette.primary,
2633
2741
  color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2634
- }
2742
+ })
2635
2743
  },
2636
2744
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
2637
2745
  },
@@ -8992,6 +9100,7 @@ function MediaOverlay({
8992
9100
  fadingOut = false,
8993
9101
  selected = false,
8994
9102
  hovered = false,
9103
+ cueOnly = false,
8995
9104
  onFadeOutComplete,
8996
9105
  onReplace,
8997
9106
  onSelect,
@@ -9000,7 +9109,7 @@ function MediaOverlay({
9000
9109
  const { rect } = hover;
9001
9110
  const skeletonRef = React8.useRef(null);
9002
9111
  const isVideo = hover.elementType === "video";
9003
- const showChrome = !selected || hovered;
9112
+ const showChrome = (!selected || hovered) && !cueOnly;
9004
9113
  const autoplay = hover.videoAutoplay ?? true;
9005
9114
  const muted = hover.videoMuted ?? true;
9006
9115
  const probeRef = React8.useRef(null);
@@ -9117,7 +9226,7 @@ function MediaOverlay({
9117
9226
  // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
9118
9227
  // in-document, pointer-events does it natively. The button below opts back in, so
9119
9228
  // Replace still works.
9120
- pointerEvents: hover.hasTextOverlap ? "none" : "auto",
9229
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
9121
9230
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
9122
9231
  // than hovered. Hover keeps the existing tinted preview.
9123
9232
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -9168,7 +9277,7 @@ function MediaOverlay({
9168
9277
  justifyContent: "center"
9169
9278
  } : null,
9170
9279
  // Don't let Replace steal clicks when text (e.g. nav) sits on top of this media.
9171
- pointerEvents: hover.hasTextOverlap ? "none" : "auto"
9280
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto"
9172
9281
  },
9173
9282
  onMouseDown: (e) => e.preventDefault(),
9174
9283
  onClick: (e) => {
@@ -9198,7 +9307,8 @@ var OVERLAY_BUTTON_STYLE2 = {
9198
9307
  };
9199
9308
  function CarouselOverlay({
9200
9309
  hover,
9201
- onEdit
9310
+ onEdit,
9311
+ cueOnly = false
9202
9312
  }) {
9203
9313
  const { rect } = hover;
9204
9314
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
@@ -9214,12 +9324,12 @@ function CarouselOverlay({
9214
9324
  width: rect.width,
9215
9325
  height: rect.height,
9216
9326
  zIndex: 2147483646,
9217
- pointerEvents: "auto",
9327
+ pointerEvents: cueOnly ? "none" : "auto",
9218
9328
  boxShadow: "inset 0 0 0 1.5px var(--color-bridge-primary)",
9219
9329
  background: "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
9220
9330
  },
9221
9331
  onClick: () => onEdit(hover.key),
9222
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
9332
+ children: cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
9223
9333
  Button,
9224
9334
  {
9225
9335
  "data-ohw-carousel-overlay": "",
@@ -9410,7 +9520,8 @@ function ReviewButton({
9410
9520
  }
9411
9521
  function AiSectionOverlay({
9412
9522
  postToParent: postToParent2,
9413
- apiRef
9523
+ apiRef,
9524
+ onSelectionChange
9414
9525
  }) {
9415
9526
  const [selectedId, setSelectedId] = (0, import_react8.useState)(null);
9416
9527
  const [reviewId, setReviewId] = (0, import_react8.useState)(null);
@@ -9419,6 +9530,9 @@ function AiSectionOverlay({
9419
9530
  reviewIdRef.current = reviewId;
9420
9531
  const selectedIdRef = (0, import_react8.useRef)(null);
9421
9532
  selectedIdRef.current = selectedId;
9533
+ (0, import_react8.useEffect)(() => {
9534
+ onSelectionChange?.(selectedId);
9535
+ }, [selectedId, onSelectionChange]);
9422
9536
  const report = (0, import_react8.useCallback)(
9423
9537
  (el) => {
9424
9538
  const labelSrc = el ? sectionElementOf(el) : null;
@@ -16524,6 +16638,19 @@ function isMediaEditable(el) {
16524
16638
  const t = el.dataset.ohwEditable;
16525
16639
  return t === "image" || t === "bg-image" || t === "video";
16526
16640
  }
16641
+ var FULL_BLEED_MIN_COVERAGE = 0.98;
16642
+ function fullBleedSectionIdOf(media) {
16643
+ const section = media.closest("[data-ohw-section]");
16644
+ if (!section) return null;
16645
+ const s = section.getBoundingClientRect();
16646
+ if (s.width <= 0 || s.height <= 0) return null;
16647
+ const m = media.getBoundingClientRect();
16648
+ const overlapX = Math.min(m.right, s.right) - Math.max(m.left, s.left);
16649
+ const overlapY = Math.min(m.bottom, s.bottom) - Math.max(m.top, s.top);
16650
+ if (overlapX <= 0 || overlapY <= 0) return null;
16651
+ if (overlapX * overlapY / (s.width * s.height) < FULL_BLEED_MIN_COVERAGE) return null;
16652
+ return instanceIdOf(movableUnit(section)) || null;
16653
+ }
16527
16654
  function isIconMarkupValue(value) {
16528
16655
  return /^\s*<(svg|img)\b/i.test(value);
16529
16656
  }
@@ -17734,6 +17861,12 @@ function OhhwellsBridge() {
17734
17861
  const hoveredImageHasTextOverlapRef = (0, import_react17.useRef)(false);
17735
17862
  const dragOverElRef = (0, import_react17.useRef)(null);
17736
17863
  const [mediaHover, setMediaHover] = (0, import_react17.useState)(null);
17864
+ const [selectedAiSectionId, setSelectedAiSectionId] = (0, import_react17.useState)(null);
17865
+ const selectedAiSectionIdRef = (0, import_react17.useRef)(null);
17866
+ const handleAiSectionSelectionChange = (0, import_react17.useCallback)((id) => {
17867
+ selectedAiSectionIdRef.current = id;
17868
+ setSelectedAiSectionId(id);
17869
+ }, []);
17737
17870
  const [selectedMedia, setSelectedMedia] = (0, import_react17.useState)(null);
17738
17871
  const selectedMediaElRef = (0, import_react17.useRef)(null);
17739
17872
  const clearMediaSelection = (0, import_react17.useCallback)(() => {
@@ -20373,6 +20506,14 @@ function OhhwellsBridge() {
20373
20506
  if (isMediaEditable(editable) && !buttonOnMedia) {
20374
20507
  e.preventDefault();
20375
20508
  e.stopPropagation();
20509
+ const fullBleedSectionId = fullBleedSectionIdOf(editable);
20510
+ if (fullBleedSectionId && fullBleedSectionId !== selectedAiSectionIdRef.current) {
20511
+ clearMediaSelectionRef.current();
20512
+ deselectRef.current();
20513
+ deactivateRef.current();
20514
+ aiSectionApiRef.current?.selectFromElement(editable);
20515
+ return;
20516
+ }
20376
20517
  if (selectedMediaElRef.current === editable) {
20377
20518
  postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
20378
20519
  } else {
@@ -20824,6 +20965,7 @@ function OhhwellsBridge() {
20824
20965
  elementType: imgEl.dataset.ohwEditable ?? "image",
20825
20966
  hasTextOverlap,
20826
20967
  isDragOver,
20968
+ fullBleedSectionId: fullBleedSectionIdOf(imgEl),
20827
20969
  ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
20828
20970
  });
20829
20971
  const track = imgEl.closest("[data-ohw-hover-pause]");
@@ -22688,7 +22830,11 @@ function OhhwellsBridge() {
22688
22830
  if (!key) return;
22689
22831
  const r2 = getVisibleRect(container);
22690
22832
  setCarouselHover(
22691
- (prev) => prev && prev.key === key && prev.rect.top === r2.top && prev.rect.left === r2.left && prev.rect.width === r2.width && prev.rect.height === r2.height ? prev : { key, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
22833
+ (prev) => prev && prev.key === key && prev.rect.top === r2.top && prev.rect.left === r2.left && prev.rect.width === r2.width && prev.rect.height === r2.height ? prev : {
22834
+ key,
22835
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
22836
+ fullBleedSectionId: fullBleedSectionIdOf(container)
22837
+ }
22692
22838
  );
22693
22839
  };
22694
22840
  const applyToolbarPos = (rect) => {
@@ -23617,7 +23763,14 @@ function OhhwellsBridge() {
23617
23763
  bridgeRoot ? (0, import_react_dom5.createPortal)(
23618
23764
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
23619
23765
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
23620
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
23766
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
23767
+ AiSectionOverlay,
23768
+ {
23769
+ postToParent: postToParent2,
23770
+ apiRef: aiSectionApiRef,
23771
+ onSelectionChange: handleAiSectionSelectionChange
23772
+ }
23773
+ ),
23621
23774
  isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
23622
23775
  "div",
23623
23776
  {
@@ -23650,6 +23803,7 @@ function OhhwellsBridge() {
23650
23803
  {
23651
23804
  hover: mediaHover,
23652
23805
  isUploading: false,
23806
+ cueOnly: !mediaHover.isDragOver && !!mediaHover.fullBleedSectionId && mediaHover.fullBleedSectionId !== selectedAiSectionId,
23653
23807
  onReplace: handleMediaReplace,
23654
23808
  onSelect: handleMediaSelect,
23655
23809
  onVideoSettingsChange: handleVideoSettingsChange
@@ -23667,7 +23821,14 @@ function OhhwellsBridge() {
23667
23821
  onVideoSettingsChange: handleVideoSettingsChange
23668
23822
  }
23669
23823
  ),
23670
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
23824
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
23825
+ CarouselOverlay,
23826
+ {
23827
+ hover: carouselHover,
23828
+ onEdit: handleEditCarousel,
23829
+ cueOnly: !!carouselHover.fullBleedSectionId && carouselHover.fullBleedSectionId !== selectedAiSectionId
23830
+ }
23831
+ ),
23671
23832
  siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
23672
23833
  siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
23673
23834
  isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),