@ohhwells/bridge 0.1.122-next.338 → 0.1.123

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.js CHANGED
@@ -1679,22 +1679,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
1679
1679
  function textAttrs(ctx, path) {
1680
1680
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
1681
1681
  }
1682
- var AI_RESPONSIVE_CSS = [
1683
- "@media (max-width: 960px) {",
1684
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
1685
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
1686
- "}",
1687
- "@media (max-width: 640px) {",
1688
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
1689
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
1690
- // Group containers flatten to a column on phones; span placements come along for free.
1691
- " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
1692
- " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
1693
- " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
1694
- " [data-ai-responsive] { overflow-x: hidden; }",
1695
- " [data-ai-responsive] img { max-width: 100%; }",
1696
- "}"
1697
- ].join("\n");
1698
1682
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
1699
1683
  function MediaBox({
1700
1684
  refValue,
@@ -1712,9 +1696,9 @@ function MediaBox({
1712
1696
  const url = refValue ? ctx.resolveMedia(refValue) : null;
1713
1697
  const isIcon = /^(lucide|simple):/.test(refValue);
1714
1698
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
1715
- const editAttrs = ctx.keyFor && editPath ? {
1699
+ const editAttrs = ctx.keyFor && editPath && !isIcon ? {
1716
1700
  "data-ohw-key": ctx.keyFor(editPath),
1717
- ...isIcon ? { "data-ohw-editable": "icon" } : genericImageEdit ? { "data-ohw-editable": "image" } : {}
1701
+ ...genericImageEdit ? { "data-ohw-editable": "image" } : {}
1718
1702
  } : {};
1719
1703
  if (isIcon) {
1720
1704
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
@@ -1722,7 +1706,6 @@ function MediaBox({
1722
1706
  "span",
1723
1707
  {
1724
1708
  "data-ai-icon": refValue,
1725
- ...editAttrs,
1726
1709
  style: {
1727
1710
  display: "inline-flex",
1728
1711
  width: 48,
@@ -2781,7 +2764,7 @@ function CollectionBlock({ node, ctx, path }) {
2781
2764
  return /* @__PURE__ */ jsx(
2782
2765
  "div",
2783
2766
  {
2784
- "data-ai-grid": String(itemsPerRow),
2767
+ "data-ai-grid": "",
2785
2768
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
2786
2769
  style: {
2787
2770
  display: "grid",
@@ -3140,7 +3123,6 @@ function AiTreeRenderer({
3140
3123
  {
3141
3124
  "data-ai-section": tree.tag ?? "",
3142
3125
  ...bgAttrs,
3143
- "data-ai-responsive": "",
3144
3126
  style: {
3145
3127
  position: "relative",
3146
3128
  padding: `${pad}px 0`,
@@ -3151,13 +3133,12 @@ function AiTreeRenderer({
3151
3133
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
3152
3134
  },
3153
3135
  children: [
3154
- /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
3155
- /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
3156
3136
  isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
3137
+ /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
3157
3138
  /* @__PURE__ */ jsx(
3158
3139
  "div",
3159
3140
  {
3160
- "data-ai-section-inner": "",
3141
+ "data-ai-container": "",
3161
3142
  style: {
3162
3143
  position: "relative",
3163
3144
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -3168,7 +3149,7 @@ function AiTreeRenderer({
3168
3149
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
3169
3150
  "div",
3170
3151
  {
3171
- "data-ai-columns": "",
3152
+ "data-ai-row": "",
3172
3153
  style: {
3173
3154
  display: "grid",
3174
3155
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -3572,10 +3553,9 @@ function applyAiSectionsToDom(state, options) {
3572
3553
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
3573
3554
  continue;
3574
3555
  }
3556
+ const isNewMount = !existing || !existing.container.isConnected;
3575
3557
  let container = existing?.container;
3576
- let isNewMount = false;
3577
3558
  if (!container || !container.isConnected) {
3578
- isNewMount = true;
3579
3559
  existing?.root.unmount();
3580
3560
  container?.remove();
3581
3561
  container = document.createElement("div");
@@ -3964,7 +3944,6 @@ function EmptyState({ inEditor, insertAfter }) {
3964
3944
  /* @__PURE__ */ jsx4(
3965
3945
  "button",
3966
3946
  {
3967
- "data-ohw-bridge": true,
3968
3947
  onClick: handleAddSchedule,
3969
3948
  className: "h-9 px-2 py-1.5 flex items-center justify-center bg-white border border-[#E7E5E4] rounded-md font-body text-sm font-medium text-(--color-dark,#200C02) cursor-pointer",
3970
3949
  children: "Add Schedule"
@@ -4382,7 +4361,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
4382
4361
  const timer = setTimeout(() => {
4383
4362
  if (!done) {
4384
4363
  done = true;
4385
- innerCleanup = loadWithId(null);
4364
+ setLoading(false);
4386
4365
  }
4387
4366
  }, 2e3);
4388
4367
  const configHandler = (e) => {
@@ -9470,7 +9449,6 @@ function MediaOverlay({
9470
9449
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
9471
9450
  );
9472
9451
  }, [isVideo]);
9473
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
9474
9452
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
9475
9453
  const box = {
9476
9454
  position: "fixed",
@@ -9569,10 +9547,11 @@ function MediaOverlay({
9569
9547
  className: "flex items-center justify-center cursor-pointer",
9570
9548
  style: {
9571
9549
  ...box,
9572
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
9573
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
9574
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
9575
- pointerEvents: "none",
9550
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
9551
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
9552
+ // in-document, pointer-events does it natively. The button below opts back in, so
9553
+ // Replace still works.
9554
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
9576
9555
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
9577
9556
  // than hovered. Hover keeps the existing tinted preview.
9578
9557
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -9599,17 +9578,17 @@ function MediaOverlay({
9599
9578
  },
9600
9579
  children: [
9601
9580
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
9602
- replaceLabel
9581
+ isVideo ? "Replace video" : "Replace image"
9603
9582
  ]
9604
9583
  }
9605
9584
  ),
9606
- showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
9585
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ jsxs7(
9607
9586
  Button,
9608
9587
  {
9609
9588
  "data-ohw-media-overlay": "",
9610
9589
  variant: "outline",
9611
9590
  size: "sm",
9612
- "aria-label": replaceLabel,
9591
+ "aria-label": isVideo ? "Replace video" : "Replace image",
9613
9592
  className: "gap-1.5 cursor-pointer hover:bg-background",
9614
9593
  style: {
9615
9594
  ...OVERLAY_BUTTON_STYLE,
@@ -9632,7 +9611,7 @@ function MediaOverlay({
9632
9611
  },
9633
9612
  children: [
9634
9613
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
9635
- replaceMode === "full" ? replaceLabel : null
9614
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
9636
9615
  ]
9637
9616
  }
9638
9617
  )
@@ -15966,7 +15945,6 @@ function readLogoSizeState(content, placement) {
15966
15945
  function getLogoElement(el) {
15967
15946
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
15968
15947
  if (marked) return marked;
15969
- if (el.closest('[data-ohw-editable="icon"]')) return null;
15970
15948
  const root = el.closest("nav, [data-ohw-nav-root], footer");
15971
15949
  if (!root) return null;
15972
15950
  const anchor = el.closest("a");
@@ -19572,10 +19550,21 @@ function parseSchedulingInsertAfter(insertAfter) {
19572
19550
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
19573
19551
  };
19574
19552
  }
19575
- function resolveEntryAnchor(entry) {
19576
- if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
19577
- const parsed = parseSchedulingInsertAfter(entry.insertAfter);
19578
- return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
19553
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
19554
+ const parsed = parseSchedulingInsertAfter(insertAfter);
19555
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
19556
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
19557
+ return { effectiveInsertAfter, insertBefore };
19558
+ }
19559
+ function getSchedulingMountPoint(insertAfter) {
19560
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
19561
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
19562
+ if (!anchorEl && anchor === "scheduling") {
19563
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
19564
+ anchorEl = widgets.at(-1) ?? null;
19565
+ }
19566
+ if (!anchorEl) return null;
19567
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
19579
19568
  }
19580
19569
  function schedulingMountDepth(insertAfter) {
19581
19570
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -19592,7 +19581,8 @@ function getPageSchedulingEntries(raw) {
19592
19581
  }
19593
19582
  }
19594
19583
  function isSchedulingWidgetMissing(entry) {
19595
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
19584
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
19585
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
19596
19586
  }
19597
19587
  function hasMissingSchedulingWidgets(entries) {
19598
19588
  return entries.some(isSchedulingWidgetMissing);
@@ -19630,18 +19620,17 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
19630
19620
  } catch {
19631
19621
  }
19632
19622
  }
19633
- function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
19634
- const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
19635
- const sectionId = schedulingSectionId(widgetId);
19623
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
19624
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
19625
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
19636
19626
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
19637
- const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
19638
- if (!anchorEl) return false;
19639
- const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
19627
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
19628
+ if (!mountPoint) return false;
19640
19629
  const container = document.createElement("div");
19641
19630
  container.dataset.ohwSectionContainer = "scheduling";
19642
19631
  container.dataset.ohwInstance = sectionId;
19643
- if (beforeId) {
19644
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
19632
+ if (insertBefore) {
19633
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
19645
19634
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
19646
19635
  if (!beforePoint) return false;
19647
19636
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -19652,26 +19641,20 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
19652
19641
  }
19653
19642
  tail.insertAdjacentElement("afterend", container);
19654
19643
  }
19655
- try {
19656
- const root = createRoot2(container);
19657
- schedulingRoots.set(container, root);
19658
- flushSync2(() => {
19659
- root.render(
19660
- /* @__PURE__ */ jsx36(
19661
- SchedulingWidget,
19662
- {
19663
- notifyOnConnect,
19664
- initialScheduleId: scheduleId,
19665
- insertAfter: widgetId
19666
- }
19667
- )
19668
- );
19669
- });
19670
- } catch (err) {
19671
- console.error("[ow:scheduling] render threw", err);
19672
- container.remove();
19673
- return false;
19674
- }
19644
+ const root = createRoot2(container);
19645
+ schedulingRoots.set(container, root);
19646
+ flushSync2(() => {
19647
+ root.render(
19648
+ /* @__PURE__ */ jsx36(
19649
+ SchedulingWidget,
19650
+ {
19651
+ notifyOnConnect,
19652
+ initialScheduleId: scheduleId,
19653
+ insertAfter: effectiveInsertAfter
19654
+ }
19655
+ )
19656
+ );
19657
+ });
19675
19658
  const tracker = getSectionsTracker();
19676
19659
  let sections = [];
19677
19660
  try {
@@ -19679,12 +19662,10 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
19679
19662
  } catch {
19680
19663
  }
19681
19664
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
19682
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
19665
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
19683
19666
  sections.push({
19684
19667
  type: "scheduling",
19685
- insertAfter: widgetId,
19686
- anchorId,
19687
- beforeId: beforeId ?? null,
19668
+ insertAfter: effectiveInsertAfter,
19688
19669
  pagePath: window.location.pathname,
19689
19670
  ...scheduleId ? { scheduleId } : {}
19690
19671
  });
@@ -19698,8 +19679,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
19698
19679
  for (let i = pending.length - 1; i >= 0; i--) {
19699
19680
  const entry = pending[i];
19700
19681
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
19701
- const { anchorId, beforeId } = resolveEntryAnchor(entry);
19702
- if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
19682
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
19703
19683
  pending.splice(i, 1);
19704
19684
  }
19705
19685
  }
@@ -19876,11 +19856,6 @@ function applyLinkByKey(key, val) {
19876
19856
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
19877
19857
  }
19878
19858
  }
19879
- function isInsideLinkEditor(target) {
19880
- return Boolean(
19881
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
19882
- );
19883
- }
19884
19859
  function isInsideFloatingPanel(target) {
19885
19860
  return Boolean(target.closest("[data-ohw-floating-panel]"));
19886
19861
  }
@@ -19888,6 +19863,11 @@ function isPointOverFloatingPanel(clientX, clientY) {
19888
19863
  const el = document.elementFromPoint(clientX, clientY);
19889
19864
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
19890
19865
  }
19866
+ function isInsideLinkEditor(target) {
19867
+ return Boolean(
19868
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
19869
+ );
19870
+ }
19891
19871
  function getHrefKeyFromElement(el) {
19892
19872
  if (!el) return null;
19893
19873
  const anchor = el.closest("[data-ohw-href-key]");
@@ -20166,7 +20146,7 @@ function getNavigationSelectionParent(el) {
20166
20146
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
20167
20147
  return getFooterLinksContainer();
20168
20148
  }
20169
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
20149
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
20170
20150
  return getNavigationRoot(el);
20171
20151
  }
20172
20152
  return null;
@@ -20388,6 +20368,7 @@ var ICONS = {
20388
20368
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
20389
20369
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
20390
20370
  };
20371
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
20391
20372
  var SELECTION_CHROME_GAP2 = 4;
20392
20373
  var TOOLBAR_STROKE_GAP2 = 4;
20393
20374
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -20761,7 +20742,6 @@ function StateToggle({
20761
20742
  );
20762
20743
  }
20763
20744
  var contentCache = /* @__PURE__ */ new Map();
20764
- var fetchedContentPaths = /* @__PURE__ */ new Set();
20765
20745
  var brandingCache = /* @__PURE__ */ new Map();
20766
20746
  var OHW_LOADER_STYLE = {
20767
20747
  position: "fixed",
@@ -21365,6 +21345,21 @@ function OhhwellsBridge() {
21365
21345
  const [isItemDragging, setIsItemDragging] = useState16(false);
21366
21346
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState16(false);
21367
21347
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
21348
+ const [floatingPanel, setFloatingPanel] = useState16(null);
21349
+ const floatingPanelOpenRef = useRef12(false);
21350
+ floatingPanelOpenRef.current = floatingPanel !== null;
21351
+ const floatingPanelRef = useRef12(null);
21352
+ floatingPanelRef.current = floatingPanel;
21353
+ const [floatingPanelPos, setFloatingPanelPos] = useState16(null);
21354
+ const buttonCollectionDragRef = useRef12(null);
21355
+ const buttonDropInsertIndexRef = useRef12(null);
21356
+ const [buttonDropSlots, setButtonDropSlots] = useState16([]);
21357
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = useState16(null);
21358
+ const buttonPointerDragRef = useRef12(null);
21359
+ const [buttonStyleTick, setButtonStyleTick] = useState16(0);
21360
+ const [logoSizeDraft, setLogoSizeDraft] = useState16(null);
21361
+ const [editorViewport, setEditorViewport] = useState16("desktop");
21362
+ const [parentScrollSnap, setParentScrollSnap] = useState16(null);
21368
21363
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState16(null);
21369
21364
  const [footerHeadingVisible, setFooterHeadingVisible] = useState16(null);
21370
21365
  const footerDragRef = useRef12(null);
@@ -21388,21 +21383,6 @@ function OhhwellsBridge() {
21388
21383
  const stylesRef = useRef12("");
21389
21384
  const appliedDrawerRef = useRef12(null);
21390
21385
  const pendingDeleteUndoRef = useRef12(null);
21391
- const [floatingPanel, setFloatingPanel] = useState16(null);
21392
- const floatingPanelOpenRef = useRef12(false);
21393
- const floatingPanelRef = useRef12(null);
21394
- floatingPanelRef.current = floatingPanel;
21395
- const setFloatingPanelRef = useRef12(setFloatingPanel);
21396
- const [floatingPanelPos, setFloatingPanelPos] = useState16(null);
21397
- const buttonCollectionDragRef = useRef12(null);
21398
- const buttonDropInsertIndexRef = useRef12(null);
21399
- const [buttonDropSlots, setButtonDropSlots] = useState16([]);
21400
- const [activeButtonDropIndex, setActiveButtonDropIndex] = useState16(null);
21401
- const buttonPointerDragRef = useRef12(null);
21402
- const [buttonStyleTick, setButtonStyleTick] = useState16(0);
21403
- const [logoSizeDraft, setLogoSizeDraft] = useState16(null);
21404
- const [editorViewport, setEditorViewport] = useState16("desktop");
21405
- const [parentScrollSnap, setParentScrollSnap] = useState16(null);
21406
21386
  const [sitePages, setSitePages] = useState16([]);
21407
21387
  const [sectionsByPath, setSectionsByPath] = useState16({});
21408
21388
  const sectionsPrefetchGenRef = useRef12(0);
@@ -21411,18 +21391,7 @@ function OhhwellsBridge() {
21411
21391
  const linkPopoverOpenRef = useRef12(false);
21412
21392
  const linkPopoverGraceUntilRef = useRef12(0);
21413
21393
  setLinkPopoverRef.current = setLinkPopover;
21414
- setFloatingPanelRef.current = setFloatingPanel;
21415
21394
  linkPopoverSessionRef.current = linkPopover;
21416
- floatingPanelOpenRef.current = Boolean(floatingPanel);
21417
- useEffect15(() => {
21418
- const syncViewport = () => {
21419
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
21420
- setEditorViewport((prev) => prev === next ? prev : next);
21421
- };
21422
- syncViewport();
21423
- window.addEventListener("resize", syncViewport);
21424
- return () => window.removeEventListener("resize", syncViewport);
21425
- }, []);
21426
21395
  useEffect15(() => {
21427
21396
  setSectionOrderPersistHandler((entries) => {
21428
21397
  const currentPath = window.location.pathname;
@@ -22940,7 +22909,6 @@ function OhhwellsBridge() {
22940
22909
  }
22941
22910
  if (typeof content[STYLE_STORE_KEY] === "string") {
22942
22911
  stylesRef.current = content[STYLE_STORE_KEY];
22943
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
22944
22912
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
22945
22913
  }
22946
22914
  applyBrandChrome(content);
@@ -22949,12 +22917,12 @@ function OhhwellsBridge() {
22949
22917
  for (const [key, val] of Object.entries(content)) {
22950
22918
  if (key === "__ohw_sections") continue;
22951
22919
  if (key === AI_SECTIONS_KEY) continue;
22952
- if (key === LOGO_PLACEHOLDER_KEY) continue;
22953
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
22954
22920
  if (key === BRAND_KIT_KEY) continue;
22955
22921
  if (key === STYLE_STORE_KEY) continue;
22956
22922
  if (BRAND_CHROME_KEYS.has(key)) continue;
22923
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
22957
22924
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
22925
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
22958
22926
  if (applyVideoSettingNode(key, val)) continue;
22959
22927
  if (applyCarouselNode(key, val)) continue;
22960
22928
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -23026,9 +22994,7 @@ function OhhwellsBridge() {
23026
22994
  let cancelled = false;
23027
22995
  setFetchState("loading");
23028
22996
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
23029
- const initialPath = pathname;
23030
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
23031
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
22997
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
23032
22998
  if (cancelled) return;
23033
22999
  const content = data?.content ?? {};
23034
23000
  const branding = Boolean(data?.showBranding);
@@ -23164,17 +23130,16 @@ function OhhwellsBridge() {
23164
23130
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
23165
23131
  }
23166
23132
  if (typeof content[STYLE_STORE_KEY] === "string") {
23167
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
23168
23133
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
23169
23134
  }
23170
23135
  for (const [key, val] of Object.entries(content)) {
23171
23136
  if (key === "__ohw_sections") continue;
23172
23137
  if (key === AI_SECTIONS_KEY) continue;
23173
- if (key === LOGO_PLACEHOLDER_KEY) continue;
23174
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
23175
23138
  if (key === BRAND_KIT_KEY) continue;
23176
23139
  if (key === STYLE_STORE_KEY) continue;
23177
23140
  if (BRAND_CHROME_KEYS.has(key)) continue;
23141
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
23142
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
23178
23143
  if (applyVideoSettingNode(key, val)) continue;
23179
23144
  if (applyCarouselNode(key, val)) continue;
23180
23145
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -23226,17 +23191,6 @@ function OhhwellsBridge() {
23226
23191
  debounceTimer = setTimeout(applyFromCache, 150);
23227
23192
  };
23228
23193
  applyFromCache();
23229
- const pathCacheKey = `${subdomain}::${pathname}`;
23230
- if (!fetchedContentPaths.has(pathCacheKey)) {
23231
- fetchedContentPaths.add(pathCacheKey);
23232
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
23233
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
23234
- if (!data?.content) return;
23235
- contentCache.set(subdomain, data.content);
23236
- applyFromCache();
23237
- }).catch(() => {
23238
- });
23239
- }
23240
23194
  observer = new MutationObserver(scheduleApply);
23241
23195
  observer.observe(document.body, { childList: true, subtree: true });
23242
23196
  return () => {
@@ -23425,11 +23379,26 @@ function OhhwellsBridge() {
23425
23379
  const t2 = setTimeout(measure, 500);
23426
23380
  const ro = new ResizeObserver(schedule);
23427
23381
  ro.observe(document.body);
23382
+ let lastWidth = window.innerWidth;
23383
+ let resizeTimers = [];
23384
+ const clearResizeTimers = () => {
23385
+ resizeTimers.forEach(clearTimeout);
23386
+ resizeTimers = [];
23387
+ };
23388
+ const handleResize = () => {
23389
+ if (window.innerWidth === lastWidth) return;
23390
+ lastWidth = window.innerWidth;
23391
+ clearResizeTimers();
23392
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
23393
+ };
23394
+ window.addEventListener("resize", handleResize);
23428
23395
  return () => {
23429
23396
  clearTimeout(t1);
23430
23397
  clearTimeout(t2);
23431
23398
  if (raf != null) cancelAnimationFrame(raf);
23432
23399
  ro.disconnect();
23400
+ clearResizeTimers();
23401
+ window.removeEventListener("resize", handleResize);
23433
23402
  };
23434
23403
  }, [pathname, isEditMode, postToParent2]);
23435
23404
  useEffect15(() => {
@@ -23724,13 +23693,6 @@ function OhhwellsBridge() {
23724
23693
  if (target.closest("[data-ohw-state-toggle]")) return;
23725
23694
  if (target.closest("[data-ohw-max-badge]")) return;
23726
23695
  if (isInsideLinkEditor(target)) return;
23727
- if (isInsideFloatingPanel(target)) return;
23728
- if (target.closest("[data-ohw-form-toolbar]")) return;
23729
- if (target.closest(
23730
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
23731
- )) {
23732
- return;
23733
- }
23734
23696
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
23735
23697
  clearMediaSelectionRef.current();
23736
23698
  }
@@ -23751,6 +23713,13 @@ function OhhwellsBridge() {
23751
23713
  return;
23752
23714
  }
23753
23715
  }
23716
+ if (isInsideFloatingPanel(target)) return;
23717
+ if (target.closest("[data-ohw-form-toolbar]")) return;
23718
+ if (target.closest(
23719
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
23720
+ )) {
23721
+ return;
23722
+ }
23754
23723
  {
23755
23724
  const formEl = getFormElement(target);
23756
23725
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -24130,9 +24099,6 @@ function OhhwellsBridge() {
24130
24099
  setHoveredItemRect(null);
24131
24100
  hoveredNavContainerRef.current = null;
24132
24101
  setHoveredNavContainerRect(null);
24133
- siblingHintElRef.current = null;
24134
- setSiblingHintRect(null);
24135
- setSiblingHintRects([]);
24136
24102
  return;
24137
24103
  }
24138
24104
  {
@@ -24253,6 +24219,7 @@ function OhhwellsBridge() {
24253
24219
  hoveredNavContainerRef.current = null;
24254
24220
  setHoveredNavContainerRect(null);
24255
24221
  hoveredItemElRef.current = editable;
24222
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
24256
24223
  }
24257
24224
  }
24258
24225
  }
@@ -24570,7 +24537,7 @@ function OhhwellsBridge() {
24570
24537
  };
24571
24538
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
24572
24539
  syncSelectedMediaPointerRelease(true);
24573
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
24540
+ if (linkPopoverOpenRef.current) {
24574
24541
  if (hoveredImageRef.current) {
24575
24542
  hoveredImageRef.current = null;
24576
24543
  hoveredImageHasTextOverlapRef.current = false;
@@ -24962,11 +24929,7 @@ function OhhwellsBridge() {
24962
24929
  };
24963
24930
  const handleMouseMove = (e) => {
24964
24931
  const { clientX, clientY } = e;
24965
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
24966
- dismissImageHover();
24967
- clearImageHover();
24968
- return;
24969
- }
24932
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
24970
24933
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
24971
24934
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
24972
24935
  formHoverElRef.current = null;
@@ -24995,12 +24958,7 @@ function OhhwellsBridge() {
24995
24958
  setHoveredItemRect(null);
24996
24959
  hoveredNavContainerRef.current = null;
24997
24960
  setHoveredNavContainerRect(null);
24998
- siblingHintElRef.current = null;
24999
- setSiblingHintRect(null);
25000
- setSiblingHintRects([]);
25001
24961
  dismissImageHover();
25002
- clearImageHover();
25003
- setSectionGap(null);
25004
24962
  return;
25005
24963
  }
25006
24964
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -25012,11 +24970,7 @@ function OhhwellsBridge() {
25012
24970
  if (e.data?.type !== "ow:pointer-sync") return;
25013
24971
  const { clientX, clientY } = e.data;
25014
24972
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
25015
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
25016
- dismissImageHover();
25017
- clearImageHover();
25018
- return;
25019
- }
24973
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
25020
24974
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
25021
24975
  if (probeSocialsRowAt(clientX, clientY)) return;
25022
24976
  probeSectionGapAt(clientX, clientY);
@@ -25362,7 +25316,6 @@ function OhhwellsBridge() {
25362
25316
  }
25363
25317
  if (typeof content[STYLE_STORE_KEY] === "string") {
25364
25318
  stylesRef.current = content[STYLE_STORE_KEY];
25365
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
25366
25319
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
25367
25320
  }
25368
25321
  applyBrandChrome(content);
@@ -25375,12 +25328,12 @@ function OhhwellsBridge() {
25375
25328
  continue;
25376
25329
  }
25377
25330
  if (key === AI_SECTIONS_KEY) continue;
25378
- if (key === LOGO_PLACEHOLDER_KEY) continue;
25379
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
25380
25331
  if (key === BRAND_KIT_KEY) continue;
25381
25332
  if (key === STYLE_STORE_KEY) continue;
25382
25333
  if (BRAND_CHROME_KEYS.has(key)) continue;
25334
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
25383
25335
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
25336
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
25384
25337
  if (applyVideoSettingNode(key, val)) continue;
25385
25338
  if (applyCarouselNode(key, val)) continue;
25386
25339
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -25826,12 +25779,6 @@ function OhhwellsBridge() {
25826
25779
  closeLinkPopoverRef.current();
25827
25780
  return;
25828
25781
  }
25829
- if (floatingPanelOpenRef.current) {
25830
- setFloatingPanelRef.current(null);
25831
- deselectRef.current();
25832
- deactivateRef.current();
25833
- return;
25834
- }
25835
25782
  deselectRef.current();
25836
25783
  deactivateRef.current();
25837
25784
  clearMediaSelectionRef.current();
@@ -26130,12 +26077,8 @@ function OhhwellsBridge() {
26130
26077
  if (inserted) {
26131
26078
  const tracker = getSectionsTracker();
26132
26079
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
26133
- const reportHeight = () => {
26134
- const h = document.body.scrollHeight;
26135
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
26136
- };
26137
- reportHeight();
26138
- setTimeout(reportHeight, 500);
26080
+ const h = document.body.scrollHeight;
26081
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
26139
26082
  }
26140
26083
  };
26141
26084
  const handleSwitchSchedule = (e) => {
@@ -26564,10 +26507,10 @@ function OhhwellsBridge() {
26564
26507
  window.removeEventListener("message", handleDeleteSection);
26565
26508
  window.removeEventListener("message", handleDuplicateSection);
26566
26509
  window.removeEventListener("message", handleDeactivate);
26510
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
26567
26511
  window.removeEventListener("message", handleToastAction);
26568
26512
  window.removeEventListener("message", handleFormCount);
26569
26513
  window.removeEventListener("message", handleUiEscape);
26570
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
26571
26514
  autoSaveTimers.current.forEach(clearTimeout);
26572
26515
  autoSaveTimers.current.clear();
26573
26516
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -27948,59 +27891,6 @@ function OhhwellsBridge() {
27948
27891
  ) : null
27949
27892
  ] });
27950
27893
  }
27951
-
27952
- // src/ui/EmptySection.tsx
27953
- import Link3 from "next/link";
27954
- import { Fragment as Fragment11, jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
27955
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
27956
- return /* @__PURE__ */ jsxs24(Fragment11, { children: [
27957
- /* @__PURE__ */ jsx37(
27958
- "p",
27959
- {
27960
- style: {
27961
- fontFamily: "var(--brand-font-body)",
27962
- fontSize: "0.75rem",
27963
- fontWeight: 500,
27964
- letterSpacing: "0.15em",
27965
- textTransform: "uppercase",
27966
- color: "var(--brand-accent)",
27967
- marginBottom: "1.5rem"
27968
- },
27969
- children: /* @__PURE__ */ jsx37(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx37("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
27970
- }
27971
- ),
27972
- /* @__PURE__ */ jsx37(
27973
- "h1",
27974
- {
27975
- style: {
27976
- fontFamily: "var(--brand-font-heading)",
27977
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
27978
- lineHeight: 1.1,
27979
- letterSpacing: "-0.025em",
27980
- color: "var(--brand-text)",
27981
- marginBottom: "1rem"
27982
- },
27983
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
27984
- children: title
27985
- }
27986
- ),
27987
- /* @__PURE__ */ jsx37(
27988
- "p",
27989
- {
27990
- style: {
27991
- fontFamily: "var(--brand-font-body)",
27992
- fontSize: "1rem",
27993
- lineHeight: 1.7,
27994
- fontWeight: 300,
27995
- color: "var(--brand-text-muted)",
27996
- maxWidth: "340px"
27997
- },
27998
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
27999
- children: "This page doesn't have any content yet."
28000
- }
28001
- )
28002
- ] });
28003
- }
28004
27894
  export {
28005
27895
  AI_DEFAULT_BRAND,
28006
27896
  AI_TREE_SCHEMA_VERSIONS,
@@ -28017,7 +27907,6 @@ export {
28017
27907
  DropdownMenuItem,
28018
27908
  DropdownMenuSeparator,
28019
27909
  DropdownMenuTrigger,
28020
- EmptySection,
28021
27910
  ItemActionToolbar,
28022
27911
  ItemInteractionLayer,
28023
27912
  LinkEditorPanel,