@ohhwells/bridge 0.1.131 → 0.1.132-next.357

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
@@ -2727,7 +2727,7 @@ var AI_MOBILE_CSS = [
2727
2727
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
2728
2728
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
2729
2729
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
2730
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
2730
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
2731
2731
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
2732
2732
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
2733
2733
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -2743,8 +2743,10 @@ var AI_MOBILE_CSS = [
2743
2743
  "[data-ai-section]{overflow-x:hidden}",
2744
2744
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
2745
2745
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
2746
- // still got a 20px band of section background down both sides on a phone.
2747
- "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
2746
+ // still got a 20px band of section background down both sides on a phone. The footer's own
2747
+ // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
2748
+ "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
2749
+ "[data-ai-container]{padding:0 20px !important}",
2748
2750
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
2749
2751
  "[data-ai-cell]{width:100%;min-width:0}",
2750
2752
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -2945,6 +2947,31 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
2945
2947
  function textAttrs(ctx, path) {
2946
2948
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
2947
2949
  }
2950
+ var AI_RESPONSIVE_CSS = [
2951
+ "@media (max-width: 960px) {",
2952
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
2953
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
2954
+ "}",
2955
+ "@media (max-width: 640px) {",
2956
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
2957
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
2958
+ // Group containers flatten to a column on phones; span placements come along for free.
2959
+ // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
2960
+ // the comment there. This sheet's copy is the narrower selector, so without the exemption it
2961
+ // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
2962
+ // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
2963
+ // layered text used to be.
2964
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
2965
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
2966
+ // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
2967
+ // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
2968
+ // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
2969
+ // other rule had already been taught to stand down.
2970
+ " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
2971
+ " [data-ai-responsive] { overflow-x: hidden; }",
2972
+ " [data-ai-responsive] img { max-width: 100%; }",
2973
+ "}"
2974
+ ].join("\n");
2948
2975
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
2949
2976
  function MediaBox({
2950
2977
  refValue,
@@ -2965,9 +2992,9 @@ function MediaBox({
2965
2992
  const url = refValue ? ctx.resolveMedia(refValue) : null;
2966
2993
  const isIcon = /^(lucide|simple):/.test(refValue);
2967
2994
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
2968
- const editAttrs = ctx.keyFor && editPath && !isIcon ? {
2995
+ const editAttrs = ctx.keyFor && editPath ? {
2969
2996
  "data-ohw-key": ctx.keyFor(editPath),
2970
- ...genericImageEdit ? { "data-ohw-editable": "image" } : {}
2997
+ ...isIcon ? { "data-ohw-editable": "icon" } : genericImageEdit ? { "data-ohw-editable": "image" } : {}
2971
2998
  } : {};
2972
2999
  if (isIcon) {
2973
3000
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
@@ -2975,6 +3002,7 @@ function MediaBox({
2975
3002
  "span",
2976
3003
  {
2977
3004
  "data-ai-icon": refValue,
3005
+ ...editAttrs,
2978
3006
  style: {
2979
3007
  display: "inline-flex",
2980
3008
  width: 48,
@@ -4461,7 +4489,7 @@ function CollectionBlock({ node, ctx, path }) {
4461
4489
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4462
4490
  "div",
4463
4491
  {
4464
- "data-ai-grid": "",
4492
+ "data-ai-grid": String(itemsPerRow),
4465
4493
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
4466
4494
  style: {
4467
4495
  display: "grid",
@@ -4721,7 +4749,7 @@ function renderNode(node, ctx, path, ambientAlign) {
4721
4749
  },
4722
4750
  children: [
4723
4751
  i > 0 && ctx.heroOverlapFullBleed ? (
4724
- // The section's `data-ai-container` gave up its max-width/gutter so the base
4752
+ // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
4725
4753
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
4726
4754
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
4727
4755
  // the full section width instead of reading at the usual content width.
@@ -5114,6 +5142,7 @@ function AiTreeRenderer({
5114
5142
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
5115
5143
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
5116
5144
  ...bgAttrs,
5145
+ "data-ai-responsive": "",
5117
5146
  style: {
5118
5147
  position: "relative",
5119
5148
  padding: `${padCss} 0`,
@@ -5127,12 +5156,13 @@ function AiTreeRenderer({
5127
5156
  ...fillHeight ? { display: "grid" } : {}
5128
5157
  },
5129
5158
  children: [
5130
- isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
5159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
5131
5160
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
5161
+ isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
5132
5162
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5133
5163
  "div",
5134
5164
  {
5135
- "data-ai-container": "",
5165
+ "data-ai-section-inner": "",
5136
5166
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
5137
5167
  style: {
5138
5168
  position: "relative",
@@ -5149,7 +5179,7 @@ function AiTreeRenderer({
5149
5179
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5150
5180
  "div",
5151
5181
  {
5152
- "data-ai-row": "",
5182
+ "data-ai-columns": "",
5153
5183
  style: {
5154
5184
  display: "grid",
5155
5185
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -5805,9 +5835,10 @@ function applyAiSectionsToDom(state, options) {
5805
5835
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
5806
5836
  continue;
5807
5837
  }
5808
- const isNewMount = !existing || !existing.container.isConnected;
5809
5838
  let container = existing?.container;
5839
+ let isNewMount = false;
5810
5840
  if (!container || !container.isConnected) {
5841
+ isNewMount = true;
5811
5842
  existing?.root.unmount();
5812
5843
  container?.remove();
5813
5844
  container = document.createElement("div");
@@ -13440,6 +13471,7 @@ function MediaOverlay({
13440
13471
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
13441
13472
  );
13442
13473
  }, [isVideo]);
13474
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
13443
13475
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
13444
13476
  const box = {
13445
13477
  position: "fixed",
@@ -13538,11 +13570,10 @@ function MediaOverlay({
13538
13570
  className: "flex items-center justify-center cursor-pointer",
13539
13571
  style: {
13540
13572
  ...box,
13541
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
13542
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
13543
- // in-document, pointer-events does it natively. The button below opts back in, so
13544
- // Replace still works.
13545
- pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
13573
+ // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
13574
+ // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
13575
+ // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
13576
+ pointerEvents: "none",
13546
13577
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
13547
13578
  // than hovered. Hover keeps the existing tinted preview.
13548
13579
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -13569,17 +13600,17 @@ function MediaOverlay({
13569
13600
  },
13570
13601
  children: [
13571
13602
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
13572
- isVideo ? "Replace video" : "Replace image"
13603
+ replaceLabel
13573
13604
  ]
13574
13605
  }
13575
13606
  ),
13576
- replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
13607
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
13577
13608
  Button,
13578
13609
  {
13579
13610
  "data-ohw-media-overlay": "",
13580
13611
  variant: "outline",
13581
13612
  size: "sm",
13582
- "aria-label": isVideo ? "Replace video" : "Replace image",
13613
+ "aria-label": replaceLabel,
13583
13614
  className: "gap-1.5 cursor-pointer hover:bg-background",
13584
13615
  style: {
13585
13616
  ...OVERLAY_BUTTON_STYLE,
@@ -13602,7 +13633,7 @@ function MediaOverlay({
13602
13633
  },
13603
13634
  children: [
13604
13635
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
13605
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
13636
+ replaceMode === "full" ? replaceLabel : null
13606
13637
  ]
13607
13638
  }
13608
13639
  )
@@ -19682,6 +19713,7 @@ function readLogoSizeState(content, placement) {
19682
19713
  function getLogoElement(el) {
19683
19714
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
19684
19715
  if (marked) return marked;
19716
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
19685
19717
  const root = el.closest("nav, [data-ohw-nav-root], footer");
19686
19718
  if (!root) return null;
19687
19719
  const anchor = el.closest("a");
@@ -23173,7 +23205,7 @@ function getNavigationSelectionParent(el) {
23173
23205
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
23174
23206
  return resolveFooterLinksContainerFrom(el);
23175
23207
  }
23176
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
23208
+ 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)) {
23177
23209
  return getNavigationRoot(el);
23178
23210
  }
23179
23211
  return null;
@@ -23395,7 +23427,6 @@ var ICONS = {
23395
23427
  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"/>',
23396
23428
  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"/>'
23397
23429
  };
23398
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
23399
23430
  var SELECTION_CHROME_GAP2 = 4;
23400
23431
  var TOOLBAR_STROKE_GAP2 = 4;
23401
23432
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -24375,21 +24406,6 @@ function OhhwellsBridge() {
24375
24406
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
24376
24407
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
24377
24408
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
24378
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
24379
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
24380
- floatingPanelOpenRef.current = floatingPanel !== null;
24381
- const floatingPanelRef = (0, import_react21.useRef)(null);
24382
- floatingPanelRef.current = floatingPanel;
24383
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
24384
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
24385
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
24386
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
24387
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
24388
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
24389
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
24390
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
24391
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
24392
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
24393
24409
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
24394
24410
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
24395
24411
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -24415,6 +24431,21 @@ function OhhwellsBridge() {
24415
24431
  const stylesRef = (0, import_react21.useRef)("");
24416
24432
  const appliedDrawerRef = (0, import_react21.useRef)(null);
24417
24433
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
24434
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
24435
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
24436
+ const floatingPanelRef = (0, import_react21.useRef)(null);
24437
+ floatingPanelRef.current = floatingPanel;
24438
+ const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
24439
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
24440
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
24441
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
24442
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
24443
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
24444
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
24445
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
24446
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
24447
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
24448
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
24418
24449
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
24419
24450
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
24420
24451
  const sectionsPrefetchGenRef = (0, import_react21.useRef)(0);
@@ -24423,7 +24454,18 @@ function OhhwellsBridge() {
24423
24454
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
24424
24455
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
24425
24456
  setLinkPopoverRef.current = setLinkPopover;
24457
+ setFloatingPanelRef.current = setFloatingPanel;
24426
24458
  linkPopoverSessionRef.current = linkPopover;
24459
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
24460
+ (0, import_react21.useEffect)(() => {
24461
+ const syncViewport = () => {
24462
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
24463
+ setEditorViewport((prev) => prev === next ? prev : next);
24464
+ };
24465
+ syncViewport();
24466
+ window.addEventListener("resize", syncViewport);
24467
+ return () => window.removeEventListener("resize", syncViewport);
24468
+ }, []);
24427
24469
  (0, import_react21.useEffect)(() => {
24428
24470
  setSectionOrderPersistHandler((entries) => {
24429
24471
  const currentPath = window.location.pathname;
@@ -26102,6 +26144,7 @@ function OhhwellsBridge() {
26102
26144
  applyAiFooterFromContent(content);
26103
26145
  if (typeof content[STYLE_STORE_KEY] === "string") {
26104
26146
  stylesRef.current = content[STYLE_STORE_KEY];
26147
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
26105
26148
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
26106
26149
  }
26107
26150
  applyBrandChrome(content);
@@ -26111,12 +26154,12 @@ function OhhwellsBridge() {
26111
26154
  if (key === "__ohw_sections") continue;
26112
26155
  if (key === AI_SECTIONS_KEY) continue;
26113
26156
  if (key === AI_FOOTER_KEY) continue;
26157
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
26158
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
26114
26159
  if (key === BRAND_KIT_KEY) continue;
26115
26160
  if (key === STYLE_STORE_KEY) continue;
26116
26161
  if (BRAND_CHROME_KEYS.has(key)) continue;
26117
- if (key === LOGO_PLACEHOLDER_KEY) continue;
26118
26162
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
26119
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
26120
26163
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
26121
26164
  continue;
26122
26165
  }
@@ -26330,17 +26373,18 @@ function OhhwellsBridge() {
26330
26373
  }
26331
26374
  applyAiFooterFromContent(content);
26332
26375
  if (typeof content[STYLE_STORE_KEY] === "string") {
26376
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
26333
26377
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
26334
26378
  }
26335
26379
  for (const [key, val] of Object.entries(content)) {
26336
26380
  if (key === "__ohw_sections") continue;
26337
26381
  if (key === AI_SECTIONS_KEY) continue;
26338
26382
  if (key === AI_FOOTER_KEY) continue;
26383
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
26384
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
26339
26385
  if (key === BRAND_KIT_KEY) continue;
26340
26386
  if (key === STYLE_STORE_KEY) continue;
26341
26387
  if (BRAND_CHROME_KEYS.has(key)) continue;
26342
- if (key === LOGO_PLACEHOLDER_KEY) continue;
26343
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
26344
26388
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
26345
26389
  continue;
26346
26390
  }
@@ -26596,26 +26640,11 @@ function OhhwellsBridge() {
26596
26640
  const t2 = setTimeout(measure, 500);
26597
26641
  const ro = new ResizeObserver(schedule);
26598
26642
  ro.observe(document.body);
26599
- let lastWidth = window.innerWidth;
26600
- let resizeTimers = [];
26601
- const clearResizeTimers = () => {
26602
- resizeTimers.forEach(clearTimeout);
26603
- resizeTimers = [];
26604
- };
26605
- const handleResize = () => {
26606
- if (window.innerWidth === lastWidth) return;
26607
- lastWidth = window.innerWidth;
26608
- clearResizeTimers();
26609
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
26610
- };
26611
- window.addEventListener("resize", handleResize);
26612
26643
  return () => {
26613
26644
  clearTimeout(t1);
26614
26645
  clearTimeout(t2);
26615
26646
  if (raf != null) cancelAnimationFrame(raf);
26616
26647
  ro.disconnect();
26617
- clearResizeTimers();
26618
- window.removeEventListener("resize", handleResize);
26619
26648
  };
26620
26649
  }, [pathname, isEditMode, postToParent2]);
26621
26650
  (0, import_react21.useEffect)(() => {
@@ -26910,6 +26939,13 @@ function OhhwellsBridge() {
26910
26939
  if (target.closest("[data-ohw-state-toggle]")) return;
26911
26940
  if (target.closest("[data-ohw-max-badge]")) return;
26912
26941
  if (isInsideLinkEditor(target)) return;
26942
+ if (isInsideFloatingPanel(target)) return;
26943
+ if (target.closest("[data-ohw-form-toolbar]")) return;
26944
+ if (target.closest(
26945
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
26946
+ )) {
26947
+ return;
26948
+ }
26913
26949
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
26914
26950
  clearMediaSelectionRef.current();
26915
26951
  }
@@ -26930,13 +26966,6 @@ function OhhwellsBridge() {
26930
26966
  return;
26931
26967
  }
26932
26968
  }
26933
- if (isInsideFloatingPanel(target)) return;
26934
- if (target.closest("[data-ohw-form-toolbar]")) return;
26935
- if (target.closest(
26936
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
26937
- )) {
26938
- return;
26939
- }
26940
26969
  {
26941
26970
  const formEl = getFormElement(target);
26942
26971
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -27316,6 +27345,9 @@ function OhhwellsBridge() {
27316
27345
  setHoveredItemRect(null);
27317
27346
  hoveredNavContainerRef.current = null;
27318
27347
  setHoveredNavContainerRect(null);
27348
+ siblingHintElRef.current = null;
27349
+ setSiblingHintRect(null);
27350
+ setSiblingHintRects([]);
27319
27351
  return;
27320
27352
  }
27321
27353
  {
@@ -27436,7 +27468,6 @@ function OhhwellsBridge() {
27436
27468
  hoveredNavContainerRef.current = null;
27437
27469
  setHoveredNavContainerRect(null);
27438
27470
  hoveredItemElRef.current = editable;
27439
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
27440
27471
  }
27441
27472
  }
27442
27473
  }
@@ -28146,7 +28177,11 @@ function OhhwellsBridge() {
28146
28177
  };
28147
28178
  const handleMouseMove = (e) => {
28148
28179
  const { clientX, clientY } = e;
28149
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
28180
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
28181
+ dismissImageHover();
28182
+ clearImageHover();
28183
+ return;
28184
+ }
28150
28185
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
28151
28186
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
28152
28187
  formHoverElRef.current = null;
@@ -28175,7 +28210,12 @@ function OhhwellsBridge() {
28175
28210
  setHoveredItemRect(null);
28176
28211
  hoveredNavContainerRef.current = null;
28177
28212
  setHoveredNavContainerRect(null);
28213
+ siblingHintElRef.current = null;
28214
+ setSiblingHintRect(null);
28215
+ setSiblingHintRects([]);
28178
28216
  dismissImageHover();
28217
+ clearImageHover();
28218
+ setSectionGap(null);
28179
28219
  return;
28180
28220
  }
28181
28221
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -28187,7 +28227,11 @@ function OhhwellsBridge() {
28187
28227
  if (e.data?.type !== "ow:pointer-sync") return;
28188
28228
  const { clientX, clientY } = e.data;
28189
28229
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
28190
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
28230
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
28231
+ dismissImageHover();
28232
+ clearImageHover();
28233
+ return;
28234
+ }
28191
28235
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
28192
28236
  if (probeSocialsRowAt(clientX, clientY)) return;
28193
28237
  probeSectionGapAt(clientX, clientY);
@@ -28539,6 +28583,7 @@ function OhhwellsBridge() {
28539
28583
  applyAiFooterFromContent(content);
28540
28584
  if (typeof content[STYLE_STORE_KEY] === "string") {
28541
28585
  stylesRef.current = content[STYLE_STORE_KEY];
28586
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28542
28587
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28543
28588
  }
28544
28589
  applyBrandChrome(content);
@@ -28552,12 +28597,12 @@ function OhhwellsBridge() {
28552
28597
  }
28553
28598
  if (key === AI_SECTIONS_KEY) continue;
28554
28599
  if (key === AI_FOOTER_KEY) continue;
28600
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28601
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
28555
28602
  if (key === BRAND_KIT_KEY) continue;
28556
28603
  if (key === STYLE_STORE_KEY) continue;
28557
28604
  if (BRAND_CHROME_KEYS.has(key)) continue;
28558
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28559
28605
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
28560
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
28561
28606
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28562
28607
  continue;
28563
28608
  }
@@ -29094,6 +29139,12 @@ function OhhwellsBridge() {
29094
29139
  closeLinkPopoverRef.current();
29095
29140
  return;
29096
29141
  }
29142
+ if (floatingPanelOpenRef.current) {
29143
+ setFloatingPanelRef.current(null);
29144
+ deselectRef.current();
29145
+ deactivateRef.current();
29146
+ return;
29147
+ }
29097
29148
  deselectRef.current();
29098
29149
  deactivateRef.current();
29099
29150
  clearMediaSelectionRef.current();
@@ -29856,10 +29907,10 @@ function OhhwellsBridge() {
29856
29907
  window.removeEventListener("message", handleDeleteSection);
29857
29908
  window.removeEventListener("message", handleDuplicateSection);
29858
29909
  window.removeEventListener("message", handleDeactivate);
29859
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
29860
29910
  window.removeEventListener("message", handleToastAction);
29861
29911
  window.removeEventListener("message", handleFormCount);
29862
29912
  window.removeEventListener("message", handleUiEscape);
29913
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
29863
29914
  autoSaveTimers.current.forEach(clearTimeout);
29864
29915
  autoSaveTimers.current.clear();
29865
29916
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -30153,7 +30204,7 @@ function OhhwellsBridge() {
30153
30204
  postToParent2({
30154
30205
  type: "ow:ready",
30155
30206
  version: "1",
30156
- bridgeVersion: "0.1.130",
30207
+ bridgeVersion: "0.1.132",
30157
30208
  path: pathname,
30158
30209
  nodes: collectEditableNodes(editContentRef.current),
30159
30210
  sections