@ohhwells/bridge 0.1.138 → 0.1.139-next.373

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
@@ -7279,7 +7279,7 @@ var AI_MOBILE_CSS = [
7279
7279
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7280
7280
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7281
7281
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7282
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7282
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7283
7283
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7284
7284
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7285
7285
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -7295,8 +7295,10 @@ var AI_MOBILE_CSS = [
7295
7295
  "[data-ai-section]{overflow-x:hidden}",
7296
7296
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
7297
7297
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
7298
- // still got a 20px band of section background down both sides on a phone.
7299
- "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
7298
+ // still got a 20px band of section background down both sides on a phone. The footer's own
7299
+ // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
7300
+ "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
7301
+ "[data-ai-container]{padding:0 20px !important}",
7300
7302
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
7301
7303
  "[data-ai-cell]{width:100%;min-width:0}",
7302
7304
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -7497,6 +7499,31 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
7497
7499
  function textAttrs(ctx, path) {
7498
7500
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
7499
7501
  }
7502
+ var AI_RESPONSIVE_CSS = [
7503
+ "@media (max-width: 960px) {",
7504
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
7505
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
7506
+ "}",
7507
+ "@media (max-width: 640px) {",
7508
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
7509
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
7510
+ // Group containers flatten to a column on phones; span placements come along for free.
7511
+ // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
7512
+ // the comment there. This sheet's copy is the narrower selector, so without the exemption it
7513
+ // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
7514
+ // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
7515
+ // layered text used to be.
7516
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
7517
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
7518
+ // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
7519
+ // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
7520
+ // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
7521
+ // other rule had already been taught to stand down.
7522
+ " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
7523
+ " [data-ai-responsive] { overflow-x: hidden; }",
7524
+ " [data-ai-responsive] img { max-width: 100%; }",
7525
+ "}"
7526
+ ].join("\n");
7500
7527
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
7501
7528
  function MediaBox({
7502
7529
  refValue,
@@ -7517,9 +7544,9 @@ function MediaBox({
7517
7544
  const url = refValue ? ctx.resolveMedia(refValue) : null;
7518
7545
  const isIcon = /^(lucide|simple):/.test(refValue);
7519
7546
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
7520
- const editAttrs = ctx.keyFor && editPath && !isIcon ? {
7547
+ const editAttrs = ctx.keyFor && editPath ? {
7521
7548
  "data-ohw-key": ctx.keyFor(editPath),
7522
- ...genericImageEdit ? { "data-ohw-editable": "image" } : {}
7549
+ ...isIcon ? { "data-ohw-editable": "icon" } : genericImageEdit ? { "data-ohw-editable": "image" } : {}
7523
7550
  } : {};
7524
7551
  if (isIcon) {
7525
7552
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
@@ -7527,6 +7554,7 @@ function MediaBox({
7527
7554
  "span",
7528
7555
  {
7529
7556
  "data-ai-icon": refValue,
7557
+ ...editAttrs,
7530
7558
  style: {
7531
7559
  display: "inline-flex",
7532
7560
  width: 48,
@@ -9034,7 +9062,7 @@ function CollectionBlock({ node, ctx, path }) {
9034
9062
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9035
9063
  "div",
9036
9064
  {
9037
- "data-ai-grid": "",
9065
+ "data-ai-grid": String(itemsPerRow),
9038
9066
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9039
9067
  style: {
9040
9068
  display: "grid",
@@ -9294,7 +9322,7 @@ function renderNode(node, ctx, path, ambientAlign) {
9294
9322
  },
9295
9323
  children: [
9296
9324
  i > 0 && ctx.heroOverlapFullBleed ? (
9297
- // The section's `data-ai-container` gave up its max-width/gutter so the base
9325
+ // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9298
9326
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
9299
9327
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
9300
9328
  // the full section width instead of reading at the usual content width.
@@ -9687,6 +9715,7 @@ function AiTreeRenderer({
9687
9715
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
9688
9716
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
9689
9717
  ...bgAttrs,
9718
+ "data-ai-responsive": "",
9690
9719
  style: {
9691
9720
  position: "relative",
9692
9721
  padding: `${padCss} 0`,
@@ -9700,12 +9729,13 @@ function AiTreeRenderer({
9700
9729
  ...fillHeight ? { display: "grid" } : {}
9701
9730
  },
9702
9731
  children: [
9703
- isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
9732
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
9704
9733
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
9734
+ isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
9705
9735
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9706
9736
  "div",
9707
9737
  {
9708
- "data-ai-container": "",
9738
+ "data-ai-section-inner": "",
9709
9739
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
9710
9740
  style: {
9711
9741
  position: "relative",
@@ -9722,7 +9752,7 @@ function AiTreeRenderer({
9722
9752
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9723
9753
  "div",
9724
9754
  {
9725
- "data-ai-row": "",
9755
+ "data-ai-columns": "",
9726
9756
  style: {
9727
9757
  display: "grid",
9728
9758
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -10378,9 +10408,10 @@ function applyAiSectionsToDom(state, options) {
10378
10408
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
10379
10409
  continue;
10380
10410
  }
10381
- const isNewMount = !existing || !existing.container.isConnected;
10382
10411
  let container = existing?.container;
10412
+ let isNewMount = false;
10383
10413
  if (!container || !container.isConnected) {
10414
+ isNewMount = true;
10384
10415
  existing?.root.unmount();
10385
10416
  container?.remove();
10386
10417
  container = document.createElement("div");
@@ -16822,6 +16853,7 @@ function MediaOverlay({
16822
16853
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
16823
16854
  );
16824
16855
  }, [isVideo]);
16856
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
16825
16857
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
16826
16858
  const box = {
16827
16859
  position: "fixed",
@@ -16920,11 +16952,10 @@ function MediaOverlay({
16920
16952
  className: "flex items-center justify-center cursor-pointer",
16921
16953
  style: {
16922
16954
  ...box,
16923
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
16924
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
16925
- // in-document, pointer-events does it natively. The button below opts back in, so
16926
- // Replace still works.
16927
- pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
16955
+ // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
16956
+ // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
16957
+ // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
16958
+ pointerEvents: "none",
16928
16959
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
16929
16960
  // than hovered. Hover keeps the existing tinted preview.
16930
16961
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -16951,17 +16982,17 @@ function MediaOverlay({
16951
16982
  },
16952
16983
  children: [
16953
16984
  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 }),
16954
- isVideo ? "Replace video" : "Replace image"
16985
+ replaceLabel
16955
16986
  ]
16956
16987
  }
16957
16988
  ),
16958
- replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
16989
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
16959
16990
  Button,
16960
16991
  {
16961
16992
  "data-ohw-media-overlay": "",
16962
16993
  variant: "outline",
16963
16994
  size: "sm",
16964
- "aria-label": isVideo ? "Replace video" : "Replace image",
16995
+ "aria-label": replaceLabel,
16965
16996
  className: "gap-1.5 cursor-pointer hover:bg-background",
16966
16997
  style: {
16967
16998
  ...OVERLAY_BUTTON_STYLE,
@@ -16984,7 +17015,7 @@ function MediaOverlay({
16984
17015
  },
16985
17016
  children: [
16986
17017
  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 }),
16987
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
17018
+ replaceMode === "full" ? replaceLabel : null
16988
17019
  ]
16989
17020
  }
16990
17021
  )
@@ -18290,8 +18321,20 @@ function withPricingCollection(tree, mutate) {
18290
18321
  const next = JSON.parse(JSON.stringify(tree));
18291
18322
  const collection = findPricingCollection(next);
18292
18323
  if (!collection || !collection.children) return null;
18324
+ for (const card of collection.children) {
18325
+ if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
18326
+ }
18293
18327
  return mutate(collection) ? next : null;
18294
18328
  }
18329
+ function upgradeLegacyPricingSlots(card) {
18330
+ const slots = card.slots = card.slots ?? {};
18331
+ if (typeof slots.features === "string") {
18332
+ const rows = pricingFeatureList(card);
18333
+ if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
18334
+ }
18335
+ if (slots.featured === true) slots.featured = { enabled: true };
18336
+ else if (slots.featured === false) delete slots.featured;
18337
+ }
18295
18338
  function addPricingTier(tree) {
18296
18339
  return withPricingCollection(tree, (collection) => {
18297
18340
  const children = collection.children;
@@ -18413,7 +18456,7 @@ function pricingFeatureList(target) {
18413
18456
  }));
18414
18457
  }
18415
18458
  if (typeof raw === "string") {
18416
- return raw.split("\n").map((line) => line.replace(/^[-*•]\s*/, "").trim()).filter(Boolean).map((text) => ({ text }));
18459
+ return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
18417
18460
  }
18418
18461
  return [];
18419
18462
  }
@@ -21407,6 +21450,7 @@ function shouldUseDevFixtures() {
21407
21450
  function getLogoElement(el) {
21408
21451
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
21409
21452
  if (marked) return marked;
21453
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
21410
21454
  const root = el.closest("nav, [data-ohw-nav-root], footer");
21411
21455
  if (!root) return null;
21412
21456
  const anchor = el.closest("a");
@@ -23970,7 +24014,7 @@ function getNavigationSelectionParent(el) {
23970
24014
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
23971
24015
  return resolveFooterLinksContainerFrom(el);
23972
24016
  }
23973
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
24017
+ 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)) {
23974
24018
  return getNavigationRoot(el);
23975
24019
  }
23976
24020
  return null;
@@ -24192,7 +24236,6 @@ var ICONS = {
24192
24236
  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"/>',
24193
24237
  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"/>'
24194
24238
  };
24195
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
24196
24239
  var SELECTION_CHROME_GAP2 = 4;
24197
24240
  var TOOLBAR_STROKE_GAP2 = 4;
24198
24241
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25172,21 +25215,6 @@ function OhhwellsBridge() {
25172
25215
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
25173
25216
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
25174
25217
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
25175
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25176
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25177
- floatingPanelOpenRef.current = floatingPanel !== null;
25178
- const floatingPanelRef = (0, import_react21.useRef)(null);
25179
- floatingPanelRef.current = floatingPanel;
25180
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25181
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25182
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25183
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25184
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25185
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
25186
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25187
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25188
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25189
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25190
25218
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
25191
25219
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
25192
25220
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -25212,6 +25240,21 @@ function OhhwellsBridge() {
25212
25240
  const stylesRef = (0, import_react21.useRef)("");
25213
25241
  const appliedDrawerRef = (0, import_react21.useRef)(null);
25214
25242
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
25243
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25244
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25245
+ const floatingPanelRef = (0, import_react21.useRef)(null);
25246
+ floatingPanelRef.current = floatingPanel;
25247
+ const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
25248
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25249
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25250
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25251
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25252
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25253
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
25254
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25255
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25256
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25257
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25215
25258
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
25216
25259
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
25217
25260
  const sectionsPrefetchGenRef = (0, import_react21.useRef)(0);
@@ -25220,7 +25263,18 @@ function OhhwellsBridge() {
25220
25263
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
25221
25264
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
25222
25265
  setLinkPopoverRef.current = setLinkPopover;
25266
+ setFloatingPanelRef.current = setFloatingPanel;
25223
25267
  linkPopoverSessionRef.current = linkPopover;
25268
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
25269
+ (0, import_react21.useEffect)(() => {
25270
+ const syncViewport = () => {
25271
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
25272
+ setEditorViewport((prev) => prev === next ? prev : next);
25273
+ };
25274
+ syncViewport();
25275
+ window.addEventListener("resize", syncViewport);
25276
+ return () => window.removeEventListener("resize", syncViewport);
25277
+ }, []);
25224
25278
  (0, import_react21.useEffect)(() => {
25225
25279
  setSectionOrderPersistHandler((entries) => {
25226
25280
  const currentPath = window.location.pathname;
@@ -26899,6 +26953,7 @@ function OhhwellsBridge() {
26899
26953
  applyAiFooterFromContent(content);
26900
26954
  if (typeof content[STYLE_STORE_KEY] === "string") {
26901
26955
  stylesRef.current = content[STYLE_STORE_KEY];
26956
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
26902
26957
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
26903
26958
  }
26904
26959
  applyBrandChrome(content);
@@ -26909,12 +26964,12 @@ function OhhwellsBridge() {
26909
26964
  if (key === "__ohw_sections") continue;
26910
26965
  if (key === AI_SECTIONS_KEY) continue;
26911
26966
  if (key === AI_FOOTER_KEY) continue;
26967
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
26968
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
26912
26969
  if (key === BRAND_KIT_KEY) continue;
26913
26970
  if (key === STYLE_STORE_KEY) continue;
26914
26971
  if (BRAND_CHROME_KEYS.has(key)) continue;
26915
- if (key === LOGO_PLACEHOLDER_KEY) continue;
26916
26972
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
26917
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
26918
26973
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
26919
26974
  continue;
26920
26975
  }
@@ -27129,17 +27184,18 @@ function OhhwellsBridge() {
27129
27184
  }
27130
27185
  applyAiFooterFromContent(content);
27131
27186
  if (typeof content[STYLE_STORE_KEY] === "string") {
27187
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27132
27188
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27133
27189
  }
27134
27190
  for (const [key, val] of Object.entries(content)) {
27135
27191
  if (key === "__ohw_sections") continue;
27136
27192
  if (key === AI_SECTIONS_KEY) continue;
27137
27193
  if (key === AI_FOOTER_KEY) continue;
27194
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
27195
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27138
27196
  if (key === BRAND_KIT_KEY) continue;
27139
27197
  if (key === STYLE_STORE_KEY) continue;
27140
27198
  if (BRAND_CHROME_KEYS.has(key)) continue;
27141
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27142
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27143
27199
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27144
27200
  continue;
27145
27201
  }
@@ -27396,26 +27452,11 @@ function OhhwellsBridge() {
27396
27452
  const t2 = setTimeout(measure, 500);
27397
27453
  const ro = new ResizeObserver(schedule);
27398
27454
  ro.observe(document.body);
27399
- let lastWidth = window.innerWidth;
27400
- let resizeTimers = [];
27401
- const clearResizeTimers = () => {
27402
- resizeTimers.forEach(clearTimeout);
27403
- resizeTimers = [];
27404
- };
27405
- const handleResize = () => {
27406
- if (window.innerWidth === lastWidth) return;
27407
- lastWidth = window.innerWidth;
27408
- clearResizeTimers();
27409
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
27410
- };
27411
- window.addEventListener("resize", handleResize);
27412
27455
  return () => {
27413
27456
  clearTimeout(t1);
27414
27457
  clearTimeout(t2);
27415
27458
  if (raf != null) cancelAnimationFrame(raf);
27416
27459
  ro.disconnect();
27417
- clearResizeTimers();
27418
- window.removeEventListener("resize", handleResize);
27419
27460
  };
27420
27461
  }, [pathname, isEditMode, postToParent2]);
27421
27462
  (0, import_react21.useEffect)(() => {
@@ -27710,6 +27751,13 @@ function OhhwellsBridge() {
27710
27751
  if (target.closest("[data-ohw-state-toggle]")) return;
27711
27752
  if (target.closest("[data-ohw-max-badge]")) return;
27712
27753
  if (isInsideLinkEditor(target)) return;
27754
+ if (isInsideFloatingPanel(target)) return;
27755
+ if (target.closest("[data-ohw-form-toolbar]")) return;
27756
+ if (target.closest(
27757
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
27758
+ )) {
27759
+ return;
27760
+ }
27713
27761
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
27714
27762
  clearMediaSelectionRef.current();
27715
27763
  }
@@ -27730,13 +27778,6 @@ function OhhwellsBridge() {
27730
27778
  return;
27731
27779
  }
27732
27780
  }
27733
- if (isInsideFloatingPanel(target)) return;
27734
- if (target.closest("[data-ohw-form-toolbar]")) return;
27735
- if (target.closest(
27736
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
27737
- )) {
27738
- return;
27739
- }
27740
27781
  {
27741
27782
  const formEl = getFormElement(target);
27742
27783
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -28116,6 +28157,9 @@ function OhhwellsBridge() {
28116
28157
  setHoveredItemRect(null);
28117
28158
  hoveredNavContainerRef.current = null;
28118
28159
  setHoveredNavContainerRect(null);
28160
+ siblingHintElRef.current = null;
28161
+ setSiblingHintRect(null);
28162
+ setSiblingHintRects([]);
28119
28163
  return;
28120
28164
  }
28121
28165
  {
@@ -28236,7 +28280,6 @@ function OhhwellsBridge() {
28236
28280
  hoveredNavContainerRef.current = null;
28237
28281
  setHoveredNavContainerRect(null);
28238
28282
  hoveredItemElRef.current = editable;
28239
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
28240
28283
  }
28241
28284
  }
28242
28285
  }
@@ -28946,7 +28989,11 @@ function OhhwellsBridge() {
28946
28989
  };
28947
28990
  const handleMouseMove = (e) => {
28948
28991
  const { clientX, clientY } = e;
28949
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
28992
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
28993
+ dismissImageHover();
28994
+ clearImageHover();
28995
+ return;
28996
+ }
28950
28997
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
28951
28998
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
28952
28999
  formHoverElRef.current = null;
@@ -28975,7 +29022,12 @@ function OhhwellsBridge() {
28975
29022
  setHoveredItemRect(null);
28976
29023
  hoveredNavContainerRef.current = null;
28977
29024
  setHoveredNavContainerRect(null);
29025
+ siblingHintElRef.current = null;
29026
+ setSiblingHintRect(null);
29027
+ setSiblingHintRects([]);
28978
29028
  dismissImageHover();
29029
+ clearImageHover();
29030
+ setSectionGap(null);
28979
29031
  return;
28980
29032
  }
28981
29033
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -28987,7 +29039,11 @@ function OhhwellsBridge() {
28987
29039
  if (e.data?.type !== "ow:pointer-sync") return;
28988
29040
  const { clientX, clientY } = e.data;
28989
29041
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
28990
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
29042
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
29043
+ dismissImageHover();
29044
+ clearImageHover();
29045
+ return;
29046
+ }
28991
29047
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
28992
29048
  if (probeSocialsRowAt(clientX, clientY)) return;
28993
29049
  probeSectionGapAt(clientX, clientY);
@@ -29339,6 +29395,7 @@ function OhhwellsBridge() {
29339
29395
  applyAiFooterFromContent(content);
29340
29396
  if (typeof content[STYLE_STORE_KEY] === "string") {
29341
29397
  stylesRef.current = content[STYLE_STORE_KEY];
29398
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
29342
29399
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
29343
29400
  }
29344
29401
  applyBrandChrome(content);
@@ -29353,12 +29410,12 @@ function OhhwellsBridge() {
29353
29410
  }
29354
29411
  if (key === AI_SECTIONS_KEY) continue;
29355
29412
  if (key === AI_FOOTER_KEY) continue;
29413
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
29414
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
29356
29415
  if (key === BRAND_KIT_KEY) continue;
29357
29416
  if (key === STYLE_STORE_KEY) continue;
29358
29417
  if (BRAND_CHROME_KEYS.has(key)) continue;
29359
- if (key === LOGO_PLACEHOLDER_KEY) continue;
29360
29418
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
29361
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
29362
29419
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
29363
29420
  continue;
29364
29421
  }
@@ -29895,6 +29952,12 @@ function OhhwellsBridge() {
29895
29952
  closeLinkPopoverRef.current();
29896
29953
  return;
29897
29954
  }
29955
+ if (floatingPanelOpenRef.current) {
29956
+ setFloatingPanelRef.current(null);
29957
+ deselectRef.current();
29958
+ deactivateRef.current();
29959
+ return;
29960
+ }
29898
29961
  deselectRef.current();
29899
29962
  deactivateRef.current();
29900
29963
  clearMediaSelectionRef.current();
@@ -30708,10 +30771,10 @@ function OhhwellsBridge() {
30708
30771
  window.removeEventListener("message", handleDeleteSection);
30709
30772
  window.removeEventListener("message", handleDuplicateSection);
30710
30773
  window.removeEventListener("message", handleDeactivate);
30711
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
30712
30774
  window.removeEventListener("message", handleToastAction);
30713
30775
  window.removeEventListener("message", handleFormCount);
30714
30776
  window.removeEventListener("message", handleUiEscape);
30777
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
30715
30778
  autoSaveTimers.current.forEach(clearTimeout);
30716
30779
  autoSaveTimers.current.clear();
30717
30780
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -31005,7 +31068,7 @@ function OhhwellsBridge() {
31005
31068
  postToParent2({
31006
31069
  type: "ow:ready",
31007
31070
  version: "1",
31008
- bridgeVersion: "0.1.137",
31071
+ bridgeVersion: "0.1.139",
31009
31072
  path: pathname,
31010
31073
  nodes: collectEditableNodes(editContentRef.current),
31011
31074
  sections