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