@ohhwells/bridge 0.1.133 → 0.1.134-next.362

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
@@ -2564,6 +2564,19 @@ function quotedFont(font) {
2564
2564
  const cleaned = font.replace(/["']/g, "").trim();
2565
2565
  return cleaned ? `"${cleaned}"` : "sans-serif";
2566
2566
  }
2567
+ function themeFont(role, fallback) {
2568
+ const quoted = quotedFont(fallback);
2569
+ return role === "heading" ? `var(--brand-font-heading, var(--font-heading, var(--font-display, ${quoted})))` : `var(--brand-font-body, var(--font-body, ${quoted}))`;
2570
+ }
2571
+ function withThemeFonts(brand) {
2572
+ return {
2573
+ ...brand,
2574
+ fonts: {
2575
+ heading: themeFont("heading", brand.fonts.heading),
2576
+ body: themeFont("body", brand.fonts.body)
2577
+ }
2578
+ };
2579
+ }
2567
2580
  function footerLogoStyle(fallbackFont, fallbackColor) {
2568
2581
  return {
2569
2582
  fontFamily: `var(--brand-font-heading, var(--font-heading, var(--font-display, ${quotedFont(fallbackFont)})))`,
@@ -2635,7 +2648,7 @@ var AI_MOBILE_CSS = [
2635
2648
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
2636
2649
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
2637
2650
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
2638
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
2651
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
2639
2652
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
2640
2653
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
2641
2654
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -2651,8 +2664,10 @@ var AI_MOBILE_CSS = [
2651
2664
  "[data-ai-section]{overflow-x:hidden}",
2652
2665
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
2653
2666
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
2654
- // still got a 20px band of section background down both sides on a phone.
2655
- "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
2667
+ // still got a 20px band of section background down both sides on a phone. The footer's own
2668
+ // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
2669
+ "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
2670
+ "[data-ai-container]{padding:0 20px !important}",
2656
2671
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
2657
2672
  "[data-ai-cell]{width:100%;min-width:0}",
2658
2673
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -2853,6 +2868,31 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
2853
2868
  function textAttrs(ctx, path) {
2854
2869
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
2855
2870
  }
2871
+ var AI_RESPONSIVE_CSS = [
2872
+ "@media (max-width: 960px) {",
2873
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
2874
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
2875
+ "}",
2876
+ "@media (max-width: 640px) {",
2877
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
2878
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
2879
+ // Group containers flatten to a column on phones; span placements come along for free.
2880
+ // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
2881
+ // the comment there. This sheet's copy is the narrower selector, so without the exemption it
2882
+ // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
2883
+ // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
2884
+ // layered text used to be.
2885
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
2886
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
2887
+ // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
2888
+ // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
2889
+ // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
2890
+ // other rule had already been taught to stand down.
2891
+ " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
2892
+ " [data-ai-responsive] { overflow-x: hidden; }",
2893
+ " [data-ai-responsive] img { max-width: 100%; }",
2894
+ "}"
2895
+ ].join("\n");
2856
2896
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
2857
2897
  function MediaBox({
2858
2898
  refValue,
@@ -2873,9 +2913,9 @@ function MediaBox({
2873
2913
  const url = refValue ? ctx.resolveMedia(refValue) : null;
2874
2914
  const isIcon = /^(lucide|simple):/.test(refValue);
2875
2915
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
2876
- const editAttrs = ctx.keyFor && editPath && !isIcon ? {
2916
+ const editAttrs = ctx.keyFor && editPath ? {
2877
2917
  "data-ohw-key": ctx.keyFor(editPath),
2878
- ...genericImageEdit ? { "data-ohw-editable": "image" } : {}
2918
+ ...isIcon ? { "data-ohw-editable": "icon" } : genericImageEdit ? { "data-ohw-editable": "image" } : {}
2879
2919
  } : {};
2880
2920
  if (isIcon) {
2881
2921
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
@@ -2883,6 +2923,7 @@ function MediaBox({
2883
2923
  "span",
2884
2924
  {
2885
2925
  "data-ai-icon": refValue,
2926
+ ...editAttrs,
2886
2927
  style: {
2887
2928
  display: "inline-flex",
2888
2929
  width: 48,
@@ -4376,7 +4417,7 @@ function CollectionBlock({ node, ctx, path }) {
4376
4417
  return /* @__PURE__ */ jsx(
4377
4418
  "div",
4378
4419
  {
4379
- "data-ai-grid": "",
4420
+ "data-ai-grid": String(itemsPerRow),
4380
4421
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
4381
4422
  style: {
4382
4423
  display: "grid",
@@ -4636,7 +4677,7 @@ function renderNode(node, ctx, path, ambientAlign) {
4636
4677
  },
4637
4678
  children: [
4638
4679
  i > 0 && ctx.heroOverlapFullBleed ? (
4639
- // The section's `data-ai-container` gave up its max-width/gutter so the base
4680
+ // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
4640
4681
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
4641
4682
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
4642
4683
  // the full section width instead of reading at the usual content width.
@@ -4959,7 +5000,7 @@ function AiTreeRenderer({
4959
5000
  if (!isRenderableTree(tree)) {
4960
5001
  return null;
4961
5002
  }
4962
- const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
5003
+ const resolvedBrand = withThemeFonts(brand ?? AI_DEFAULT_BRAND);
4963
5004
  const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
4964
5005
  const blockBrand = band?.brand ?? resolvedBrand;
4965
5006
  const placeholderMap = buildPlaceholderMap(tree);
@@ -5029,6 +5070,7 @@ function AiTreeRenderer({
5029
5070
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
5030
5071
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
5031
5072
  ...bgAttrs,
5073
+ "data-ai-responsive": "",
5032
5074
  style: {
5033
5075
  position: "relative",
5034
5076
  padding: `${padCss} 0`,
@@ -5042,12 +5084,13 @@ function AiTreeRenderer({
5042
5084
  ...fillHeight ? { display: "grid" } : {}
5043
5085
  },
5044
5086
  children: [
5045
- isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
5087
+ /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
5046
5088
  /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
5089
+ isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
5047
5090
  /* @__PURE__ */ jsx(
5048
5091
  "div",
5049
5092
  {
5050
- "data-ai-container": "",
5093
+ "data-ai-section-inner": "",
5051
5094
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
5052
5095
  style: {
5053
5096
  position: "relative",
@@ -5064,7 +5107,7 @@ function AiTreeRenderer({
5064
5107
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
5065
5108
  "div",
5066
5109
  {
5067
- "data-ai-row": "",
5110
+ "data-ai-columns": "",
5068
5111
  style: {
5069
5112
  display: "grid",
5070
5113
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -5115,7 +5158,7 @@ function AiFooterRenderer({
5115
5158
  if (!isRenderableFooterTree(tree)) {
5116
5159
  return null;
5117
5160
  }
5118
- const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
5161
+ const resolvedBrand = withThemeFonts(brand ?? AI_DEFAULT_BRAND);
5119
5162
  const bandBg = surface?.background ?? resolvedBrand.palette.dark;
5120
5163
  const bandText = surface?.text ?? resolvedBrand.palette.light;
5121
5164
  const onDark = surface ? isDarkColor(surface.background) : true;
@@ -5720,9 +5763,10 @@ function applyAiSectionsToDom(state, options) {
5720
5763
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
5721
5764
  continue;
5722
5765
  }
5723
- const isNewMount = !existing || !existing.container.isConnected;
5724
5766
  let container = existing?.container;
5767
+ let isNewMount = false;
5725
5768
  if (!container || !container.isConnected) {
5769
+ isNewMount = true;
5726
5770
  existing?.root.unmount();
5727
5771
  container?.remove();
5728
5772
  container = document.createElement("div");
@@ -8938,7 +8982,7 @@ var isLabelWeight = (label) => label === "number" || label === "weight";
8938
8982
  var isLabelShadow = (label) => label === "shadow";
8939
8983
  var getDefaultConfig = () => {
8940
8984
  const themeColor = fromTheme("color");
8941
- const themeFont = fromTheme("font");
8985
+ const themeFont2 = fromTheme("font");
8942
8986
  const themeText = fromTheme("text");
8943
8987
  const themeFontWeight = fromTheme("font-weight");
8944
8988
  const themeTracking = fromTheme("tracking");
@@ -9860,7 +9904,7 @@ var getDefaultConfig = () => {
9860
9904
  * @see https://tailwindcss.com/docs/font-family
9861
9905
  */
9862
9906
  "font-family": [{
9863
- font: [isArbitraryVariableFamilyName, isArbitraryFamilyName, themeFont]
9907
+ font: [isArbitraryVariableFamilyName, isArbitraryFamilyName, themeFont2]
9864
9908
  }],
9865
9909
  /**
9866
9910
  * Font Feature Settings
@@ -13357,6 +13401,7 @@ function MediaOverlay({
13357
13401
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
13358
13402
  );
13359
13403
  }, [isVideo]);
13404
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
13360
13405
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
13361
13406
  const box = {
13362
13407
  position: "fixed",
@@ -13455,11 +13500,10 @@ function MediaOverlay({
13455
13500
  className: "flex items-center justify-center cursor-pointer",
13456
13501
  style: {
13457
13502
  ...box,
13458
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
13459
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
13460
- // in-document, pointer-events does it natively. The button below opts back in, so
13461
- // Replace still works.
13462
- pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
13503
+ // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
13504
+ // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
13505
+ // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
13506
+ pointerEvents: "none",
13463
13507
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
13464
13508
  // than hovered. Hover keeps the existing tinted preview.
13465
13509
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -13486,17 +13530,17 @@ function MediaOverlay({
13486
13530
  },
13487
13531
  children: [
13488
13532
  isVideo ? /* @__PURE__ */ jsx16(Film, { size: 14 }) : /* @__PURE__ */ jsx16(ImageIcon, { size: 14 }),
13489
- isVideo ? "Replace video" : "Replace image"
13533
+ replaceLabel
13490
13534
  ]
13491
13535
  }
13492
13536
  ),
13493
- replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ jsxs7(
13537
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
13494
13538
  Button,
13495
13539
  {
13496
13540
  "data-ohw-media-overlay": "",
13497
13541
  variant: "outline",
13498
13542
  size: "sm",
13499
- "aria-label": isVideo ? "Replace video" : "Replace image",
13543
+ "aria-label": replaceLabel,
13500
13544
  className: "gap-1.5 cursor-pointer hover:bg-background",
13501
13545
  style: {
13502
13546
  ...OVERLAY_BUTTON_STYLE,
@@ -13519,7 +13563,7 @@ function MediaOverlay({
13519
13563
  },
13520
13564
  children: [
13521
13565
  isVideo ? /* @__PURE__ */ jsx16(Film, { size: 14 }) : /* @__PURE__ */ jsx16(ImageIcon, { size: 14 }),
13522
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
13566
+ replaceMode === "full" ? replaceLabel : null
13523
13567
  ]
13524
13568
  }
13525
13569
  )
@@ -19751,6 +19795,7 @@ function readLogoSizeState(content, placement) {
19751
19795
  function getLogoElement(el) {
19752
19796
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
19753
19797
  if (marked) return marked;
19798
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
19754
19799
  const root = el.closest("nav, [data-ohw-nav-root], footer");
19755
19800
  if (!root) return null;
19756
19801
  const anchor = el.closest("a");
@@ -23248,7 +23293,7 @@ function getNavigationSelectionParent(el) {
23248
23293
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
23249
23294
  return resolveFooterLinksContainerFrom(el);
23250
23295
  }
23251
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
23296
+ 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)) {
23252
23297
  return getNavigationRoot(el);
23253
23298
  }
23254
23299
  return null;
@@ -23470,7 +23515,6 @@ var ICONS = {
23470
23515
  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"/>',
23471
23516
  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"/>'
23472
23517
  };
23473
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
23474
23518
  var SELECTION_CHROME_GAP2 = 4;
23475
23519
  var TOOLBAR_STROKE_GAP2 = 4;
23476
23520
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -24450,21 +24494,6 @@ function OhhwellsBridge() {
24450
24494
  const [isItemDragging, setIsItemDragging] = useState18(false);
24451
24495
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState18(false);
24452
24496
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
24453
- const [floatingPanel, setFloatingPanel] = useState18(null);
24454
- const floatingPanelOpenRef = useRef14(false);
24455
- floatingPanelOpenRef.current = floatingPanel !== null;
24456
- const floatingPanelRef = useRef14(null);
24457
- floatingPanelRef.current = floatingPanel;
24458
- const [floatingPanelPos, setFloatingPanelPos] = useState18(null);
24459
- const buttonCollectionDragRef = useRef14(null);
24460
- const buttonDropInsertIndexRef = useRef14(null);
24461
- const [buttonDropSlots, setButtonDropSlots] = useState18([]);
24462
- const [activeButtonDropIndex, setActiveButtonDropIndex] = useState18(null);
24463
- const buttonPointerDragRef = useRef14(null);
24464
- const [buttonStyleTick, setButtonStyleTick] = useState18(0);
24465
- const [logoSizeDraft, setLogoSizeDraft] = useState18(null);
24466
- const [editorViewport, setEditorViewport] = useState18("desktop");
24467
- const [parentScrollSnap, setParentScrollSnap] = useState18(null);
24468
24497
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState18(null);
24469
24498
  const [footerHeadingVisible, setFooterHeadingVisible] = useState18(null);
24470
24499
  const footerDragRef = useRef14(null);
@@ -24490,6 +24519,21 @@ function OhhwellsBridge() {
24490
24519
  const stylesRef = useRef14("");
24491
24520
  const appliedDrawerRef = useRef14(null);
24492
24521
  const pendingDeleteUndoRef = useRef14(null);
24522
+ const [floatingPanel, setFloatingPanel] = useState18(null);
24523
+ const floatingPanelOpenRef = useRef14(false);
24524
+ const floatingPanelRef = useRef14(null);
24525
+ floatingPanelRef.current = floatingPanel;
24526
+ const setFloatingPanelRef = useRef14(setFloatingPanel);
24527
+ const [floatingPanelPos, setFloatingPanelPos] = useState18(null);
24528
+ const buttonCollectionDragRef = useRef14(null);
24529
+ const buttonDropInsertIndexRef = useRef14(null);
24530
+ const [buttonDropSlots, setButtonDropSlots] = useState18([]);
24531
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = useState18(null);
24532
+ const buttonPointerDragRef = useRef14(null);
24533
+ const [buttonStyleTick, setButtonStyleTick] = useState18(0);
24534
+ const [logoSizeDraft, setLogoSizeDraft] = useState18(null);
24535
+ const [editorViewport, setEditorViewport] = useState18("desktop");
24536
+ const [parentScrollSnap, setParentScrollSnap] = useState18(null);
24493
24537
  const [sitePages, setSitePages] = useState18([]);
24494
24538
  const [sectionsByPath, setSectionsByPath] = useState18({});
24495
24539
  const sectionsPrefetchGenRef = useRef14(0);
@@ -24498,7 +24542,18 @@ function OhhwellsBridge() {
24498
24542
  const linkPopoverOpenRef = useRef14(false);
24499
24543
  const linkPopoverGraceUntilRef = useRef14(0);
24500
24544
  setLinkPopoverRef.current = setLinkPopover;
24545
+ setFloatingPanelRef.current = setFloatingPanel;
24501
24546
  linkPopoverSessionRef.current = linkPopover;
24547
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
24548
+ useEffect17(() => {
24549
+ const syncViewport = () => {
24550
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
24551
+ setEditorViewport((prev) => prev === next ? prev : next);
24552
+ };
24553
+ syncViewport();
24554
+ window.addEventListener("resize", syncViewport);
24555
+ return () => window.removeEventListener("resize", syncViewport);
24556
+ }, []);
24502
24557
  useEffect17(() => {
24503
24558
  setSectionOrderPersistHandler((entries) => {
24504
24559
  const currentPath = window.location.pathname;
@@ -26177,6 +26232,7 @@ function OhhwellsBridge() {
26177
26232
  applyAiFooterFromContent(content);
26178
26233
  if (typeof content[STYLE_STORE_KEY] === "string") {
26179
26234
  stylesRef.current = content[STYLE_STORE_KEY];
26235
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
26180
26236
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
26181
26237
  }
26182
26238
  applyBrandChrome(content);
@@ -26186,12 +26242,12 @@ function OhhwellsBridge() {
26186
26242
  if (key === "__ohw_sections") continue;
26187
26243
  if (key === AI_SECTIONS_KEY) continue;
26188
26244
  if (key === AI_FOOTER_KEY) continue;
26245
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
26246
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
26189
26247
  if (key === BRAND_KIT_KEY) continue;
26190
26248
  if (key === STYLE_STORE_KEY) continue;
26191
26249
  if (BRAND_CHROME_KEYS.has(key)) continue;
26192
- if (key === LOGO_PLACEHOLDER_KEY) continue;
26193
26250
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
26194
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
26195
26251
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
26196
26252
  continue;
26197
26253
  }
@@ -26405,17 +26461,18 @@ function OhhwellsBridge() {
26405
26461
  }
26406
26462
  applyAiFooterFromContent(content);
26407
26463
  if (typeof content[STYLE_STORE_KEY] === "string") {
26464
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
26408
26465
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
26409
26466
  }
26410
26467
  for (const [key, val] of Object.entries(content)) {
26411
26468
  if (key === "__ohw_sections") continue;
26412
26469
  if (key === AI_SECTIONS_KEY) continue;
26413
26470
  if (key === AI_FOOTER_KEY) continue;
26471
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
26472
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
26414
26473
  if (key === BRAND_KIT_KEY) continue;
26415
26474
  if (key === STYLE_STORE_KEY) continue;
26416
26475
  if (BRAND_CHROME_KEYS.has(key)) continue;
26417
- if (key === LOGO_PLACEHOLDER_KEY) continue;
26418
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
26419
26476
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
26420
26477
  continue;
26421
26478
  }
@@ -26671,26 +26728,11 @@ function OhhwellsBridge() {
26671
26728
  const t2 = setTimeout(measure, 500);
26672
26729
  const ro = new ResizeObserver(schedule);
26673
26730
  ro.observe(document.body);
26674
- let lastWidth = window.innerWidth;
26675
- let resizeTimers = [];
26676
- const clearResizeTimers = () => {
26677
- resizeTimers.forEach(clearTimeout);
26678
- resizeTimers = [];
26679
- };
26680
- const handleResize = () => {
26681
- if (window.innerWidth === lastWidth) return;
26682
- lastWidth = window.innerWidth;
26683
- clearResizeTimers();
26684
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
26685
- };
26686
- window.addEventListener("resize", handleResize);
26687
26731
  return () => {
26688
26732
  clearTimeout(t1);
26689
26733
  clearTimeout(t2);
26690
26734
  if (raf != null) cancelAnimationFrame(raf);
26691
26735
  ro.disconnect();
26692
- clearResizeTimers();
26693
- window.removeEventListener("resize", handleResize);
26694
26736
  };
26695
26737
  }, [pathname, isEditMode, postToParent2]);
26696
26738
  useEffect17(() => {
@@ -26985,6 +27027,13 @@ function OhhwellsBridge() {
26985
27027
  if (target.closest("[data-ohw-state-toggle]")) return;
26986
27028
  if (target.closest("[data-ohw-max-badge]")) return;
26987
27029
  if (isInsideLinkEditor(target)) return;
27030
+ if (isInsideFloatingPanel(target)) return;
27031
+ if (target.closest("[data-ohw-form-toolbar]")) return;
27032
+ if (target.closest(
27033
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
27034
+ )) {
27035
+ return;
27036
+ }
26988
27037
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
26989
27038
  clearMediaSelectionRef.current();
26990
27039
  }
@@ -27005,13 +27054,6 @@ function OhhwellsBridge() {
27005
27054
  return;
27006
27055
  }
27007
27056
  }
27008
- if (isInsideFloatingPanel(target)) return;
27009
- if (target.closest("[data-ohw-form-toolbar]")) return;
27010
- if (target.closest(
27011
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
27012
- )) {
27013
- return;
27014
- }
27015
27057
  {
27016
27058
  const formEl = getFormElement(target);
27017
27059
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -27391,6 +27433,9 @@ function OhhwellsBridge() {
27391
27433
  setHoveredItemRect(null);
27392
27434
  hoveredNavContainerRef.current = null;
27393
27435
  setHoveredNavContainerRect(null);
27436
+ siblingHintElRef.current = null;
27437
+ setSiblingHintRect(null);
27438
+ setSiblingHintRects([]);
27394
27439
  return;
27395
27440
  }
27396
27441
  {
@@ -27511,7 +27556,6 @@ function OhhwellsBridge() {
27511
27556
  hoveredNavContainerRef.current = null;
27512
27557
  setHoveredNavContainerRect(null);
27513
27558
  hoveredItemElRef.current = editable;
27514
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
27515
27559
  }
27516
27560
  }
27517
27561
  }
@@ -28221,7 +28265,11 @@ function OhhwellsBridge() {
28221
28265
  };
28222
28266
  const handleMouseMove = (e) => {
28223
28267
  const { clientX, clientY } = e;
28224
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
28268
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
28269
+ dismissImageHover();
28270
+ clearImageHover();
28271
+ return;
28272
+ }
28225
28273
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
28226
28274
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
28227
28275
  formHoverElRef.current = null;
@@ -28250,7 +28298,12 @@ function OhhwellsBridge() {
28250
28298
  setHoveredItemRect(null);
28251
28299
  hoveredNavContainerRef.current = null;
28252
28300
  setHoveredNavContainerRect(null);
28301
+ siblingHintElRef.current = null;
28302
+ setSiblingHintRect(null);
28303
+ setSiblingHintRects([]);
28253
28304
  dismissImageHover();
28305
+ clearImageHover();
28306
+ setSectionGap(null);
28254
28307
  return;
28255
28308
  }
28256
28309
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -28262,7 +28315,11 @@ function OhhwellsBridge() {
28262
28315
  if (e.data?.type !== "ow:pointer-sync") return;
28263
28316
  const { clientX, clientY } = e.data;
28264
28317
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
28265
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
28318
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
28319
+ dismissImageHover();
28320
+ clearImageHover();
28321
+ return;
28322
+ }
28266
28323
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
28267
28324
  if (probeSocialsRowAt(clientX, clientY)) return;
28268
28325
  probeSectionGapAt(clientX, clientY);
@@ -28614,6 +28671,7 @@ function OhhwellsBridge() {
28614
28671
  applyAiFooterFromContent(content);
28615
28672
  if (typeof content[STYLE_STORE_KEY] === "string") {
28616
28673
  stylesRef.current = content[STYLE_STORE_KEY];
28674
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28617
28675
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28618
28676
  }
28619
28677
  applyBrandChrome(content);
@@ -28627,12 +28685,12 @@ function OhhwellsBridge() {
28627
28685
  }
28628
28686
  if (key === AI_SECTIONS_KEY) continue;
28629
28687
  if (key === AI_FOOTER_KEY) continue;
28688
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28689
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
28630
28690
  if (key === BRAND_KIT_KEY) continue;
28631
28691
  if (key === STYLE_STORE_KEY) continue;
28632
28692
  if (BRAND_CHROME_KEYS.has(key)) continue;
28633
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28634
28693
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
28635
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
28636
28694
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28637
28695
  continue;
28638
28696
  }
@@ -29169,6 +29227,12 @@ function OhhwellsBridge() {
29169
29227
  closeLinkPopoverRef.current();
29170
29228
  return;
29171
29229
  }
29230
+ if (floatingPanelOpenRef.current) {
29231
+ setFloatingPanelRef.current(null);
29232
+ deselectRef.current();
29233
+ deactivateRef.current();
29234
+ return;
29235
+ }
29172
29236
  deselectRef.current();
29173
29237
  deactivateRef.current();
29174
29238
  clearMediaSelectionRef.current();
@@ -29931,10 +29995,10 @@ function OhhwellsBridge() {
29931
29995
  window.removeEventListener("message", handleDeleteSection);
29932
29996
  window.removeEventListener("message", handleDuplicateSection);
29933
29997
  window.removeEventListener("message", handleDeactivate);
29934
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
29935
29998
  window.removeEventListener("message", handleToastAction);
29936
29999
  window.removeEventListener("message", handleFormCount);
29937
30000
  window.removeEventListener("message", handleUiEscape);
30001
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
29938
30002
  autoSaveTimers.current.forEach(clearTimeout);
29939
30003
  autoSaveTimers.current.clear();
29940
30004
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -30228,7 +30292,7 @@ function OhhwellsBridge() {
30228
30292
  postToParent2({
30229
30293
  type: "ow:ready",
30230
30294
  version: "1",
30231
- bridgeVersion: "0.1.132",
30295
+ bridgeVersion: "0.1.134",
30232
30296
  path: pathname,
30233
30297
  nodes: collectEditableNodes(editContentRef.current),
30234
30298
  sections