@ohhwells/bridge 0.1.71 → 0.1.73

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
@@ -13224,6 +13224,18 @@ function collectEditableNodes(extraContent, root = document) {
13224
13224
  }
13225
13225
  if (extraContent && !isScoped) {
13226
13226
  applyNavFooterDeleteOverrides(byKey, extraContent);
13227
+ for (const key of LOGO_IMAGE_KEYS) {
13228
+ if (!(key in extraContent)) continue;
13229
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
13230
+ }
13231
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
13232
+ if (!(key in extraContent)) continue;
13233
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13234
+ }
13235
+ for (const key of LOGO_SIZE_KEYS) {
13236
+ if (!(key in extraContent)) continue;
13237
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13238
+ }
13227
13239
  }
13228
13240
  return Array.from(byKey.values());
13229
13241
  }
@@ -13821,9 +13833,21 @@ function getLinkHref4(el) {
13821
13833
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
13822
13834
  return anchor?.getAttribute("href") ?? "";
13823
13835
  }
13836
+ function normalizeLinkHref(value) {
13837
+ const href = value.trim();
13838
+ if (!href) return href;
13839
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return href;
13840
+ if (href.startsWith("/") || href.startsWith("#") || href.startsWith("?")) return href;
13841
+ if (!/^[^\s/]+\.[a-z]{2,}(?:[/:?#]|$)/i.test(href)) return href;
13842
+ return `https://${href}`;
13843
+ }
13844
+ function isPageFrameSection(el) {
13845
+ const id = el.dataset.ohwSection ?? "";
13846
+ return el.tagName === "FOOTER" || el.tagName === "HEADER" || Boolean(el.closest("footer")) || /(^|[-_])(footer|navbar)$/i.test(id);
13847
+ }
13824
13848
  function applyLinkHref(el, val) {
13825
13849
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
13826
- if (anchor) anchor.setAttribute("href", val);
13850
+ if (anchor) anchor.setAttribute("href", normalizeLinkHref(val));
13827
13851
  }
13828
13852
  function currentIconRef(el) {
13829
13853
  const uploaded = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -13953,7 +13977,7 @@ function applyLinkByKey(key, val) {
13953
13977
  });
13954
13978
  const hrefAnchors = document.querySelectorAll(`[data-ohw-href-key="${key}"]`);
13955
13979
  if (hrefAnchors.length > 0) {
13956
- setStoredLinkHref(key, val);
13980
+ setStoredLinkHref(key, normalizeLinkHref(val));
13957
13981
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
13958
13982
  }
13959
13983
  }
@@ -15138,7 +15162,8 @@ function OhhwellsBridge() {
15138
15162
  const el = document.querySelector(
15139
15163
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
15140
15164
  );
15141
- const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
15165
+ const busy = el?.querySelector('[contenteditable="true"]') ?? null;
15166
+ const target = el && !el.closest("[data-ohw-href-key]") && !busy ? el : null;
15142
15167
  if (!target) {
15143
15168
  setHoveredTextRect(null);
15144
15169
  return;
@@ -15149,7 +15174,10 @@ function OhhwellsBridge() {
15149
15174
  const observer = new MutationObserver(sync);
15150
15175
  observer.observe(document.documentElement, {
15151
15176
  attributes: true,
15152
- attributeFilter: ["data-ohw-hovered"],
15177
+ // `contenteditable` too: the box is recomputed only when one of these changes, and a click
15178
+ // that opens an element for typing changes nothing else — so the frame measured a moment
15179
+ // earlier, while merely hovering, stayed on screen inside the solid one (OHH-762).
15180
+ attributeFilter: ["data-ohw-hovered", "contenteditable"],
15153
15181
  subtree: true
15154
15182
  });
15155
15183
  return () => observer.disconnect();
@@ -15436,6 +15464,14 @@ function OhhwellsBridge() {
15436
15464
  el.removeAttribute("data-ohw-hovered");
15437
15465
  el.setAttribute("data-ohw-selected", "");
15438
15466
  }, [clearSelectedAttr]);
15467
+ const isSelectedForHover = (0, import_react16.useCallback)((el) => {
15468
+ if (!el) return false;
15469
+ return [selectedElRef.current, activeElRef.current].some(
15470
+ (busy) => busy && (el === busy || busy.contains(el) || el.contains(busy))
15471
+ );
15472
+ }, []);
15473
+ const isSelectedForHoverRef = (0, import_react16.useRef)(isSelectedForHover);
15474
+ isSelectedForHoverRef.current = isSelectedForHover;
15439
15475
  const resolveHrefKeyElement = (0, import_react16.useCallback)((hrefKey) => {
15440
15476
  if (isFooterHrefKey(hrefKey)) {
15441
15477
  return document.querySelector(
@@ -16353,6 +16389,12 @@ function OhhwellsBridge() {
16353
16389
  );
16354
16390
  const activate = (0, import_react16.useCallback)((el, options) => {
16355
16391
  if (activeElRef.current === el) return;
16392
+ document.querySelectorAll("[data-ohw-hovered]").forEach((hovered) => {
16393
+ hovered.removeAttribute("data-ohw-hovered");
16394
+ });
16395
+ setHoveredTextRect(null);
16396
+ hoveredItemElRef.current = null;
16397
+ setHoveredItemRect(null);
16356
16398
  if (isIconEditable(el)) {
16357
16399
  const social = getSocialItem(el);
16358
16400
  const words = social ? social.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') : null;
@@ -17539,13 +17581,13 @@ function OhhwellsBridge() {
17539
17581
  clearHrefKeyHover(hoverTarget);
17540
17582
  hoveredItemElRef.current = hoverTarget;
17541
17583
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
17542
- } else if (!isInsideNavigationItem(editable) && hoverTarget !== selectedElRef.current) {
17584
+ } else if (!isInsideNavigationItem(editable) && !isSelectedForHoverRef.current(hoverTarget)) {
17543
17585
  hoverTarget.setAttribute("data-ohw-hovered", "");
17544
17586
  if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
17545
17587
  hoveredNavContainerRef.current = null;
17546
17588
  setHoveredNavContainerRect(null);
17547
17589
  hoveredItemElRef.current = editable;
17548
- setHoveredItemRect(editable.getBoundingClientRect());
17590
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
17549
17591
  }
17550
17592
  }
17551
17593
  }
@@ -18000,7 +18042,7 @@ function OhhwellsBridge() {
18000
18042
  showImageHover(imgEl, isDragOver, true);
18001
18043
  }
18002
18044
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
18003
- if (textEditable && !textEditable.closest("[data-ohw-href-key]")) {
18045
+ if (textEditable && !textEditable.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEditable)) {
18004
18046
  textEditable.setAttribute("data-ohw-hovered", "");
18005
18047
  }
18006
18048
  return;
@@ -18012,7 +18054,7 @@ function OhhwellsBridge() {
18012
18054
  clearImageHover();
18013
18055
  }
18014
18056
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
18015
- if (textEditable && !textEditable.closest("[data-ohw-href-key]")) {
18057
+ if (textEditable && !textEditable.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEditable)) {
18016
18058
  textEditable.setAttribute("data-ohw-hovered", "");
18017
18059
  }
18018
18060
  return;
@@ -18086,7 +18128,7 @@ function OhhwellsBridge() {
18086
18128
  hoveredItemElRef.current = navAnchor;
18087
18129
  setHoveredItemRect(navAnchor.getBoundingClientRect());
18088
18130
  }
18089
- } else if (!textEl.closest("[data-ohw-href-key]")) {
18131
+ } else if (!textEl.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEl)) {
18090
18132
  hoveredItemElRef.current = null;
18091
18133
  setHoveredItemRect(null);
18092
18134
  textEl.setAttribute("data-ohw-hovered", "");
@@ -18180,7 +18222,7 @@ function OhhwellsBridge() {
18180
18222
  const ZONE = 20;
18181
18223
  for (let i = 0; i < sections.length; i++) {
18182
18224
  const a = sections[i];
18183
- if (a.dataset.ohwSection === "footer") continue;
18225
+ if (isPageFrameSection(a)) continue;
18184
18226
  const b = sections[i + 1] ?? null;
18185
18227
  const boundaryY = a.getBoundingClientRect().bottom;
18186
18228
  if (Math.abs(y - boundaryY) <= ZONE) {
@@ -18640,9 +18682,10 @@ function OhhwellsBridge() {
18640
18682
  const handleAiDeleteSection = (e) => {
18641
18683
  if (e.data?.type !== "ow:ai-delete-section") return;
18642
18684
  const sectionId = typeof e.data.sectionId === "string" ? e.data.sectionId : "";
18643
- if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
18685
+ if (!sectionId) return;
18644
18686
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
18645
18687
  if (!exists) return;
18688
+ if (isPageFrameSection(exists)) return;
18646
18689
  const previous = aiSectionsRef.current;
18647
18690
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
18648
18691
  const nextValue = serializeAiSectionsState(nextState);
@@ -19275,6 +19318,15 @@ function OhhwellsBridge() {
19275
19318
  if (visibleViewportRef.current) {
19276
19319
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
19277
19320
  }
19321
+ handleScroll();
19322
+ document.querySelectorAll("[data-ohw-hovered]").forEach((hovered) => {
19323
+ hovered.removeAttribute("data-ohw-hovered");
19324
+ });
19325
+ setHoveredTextRect(null);
19326
+ hoveredItemElRef.current = null;
19327
+ setHoveredItemRect(null);
19328
+ hoveredNavContainerRef.current = null;
19329
+ setHoveredNavContainerRect(null);
19278
19330
  };
19279
19331
  window.addEventListener("resize", handleViewportResize, { passive: true });
19280
19332
  document.addEventListener("click", handleClick, true);
@@ -19540,7 +19592,7 @@ function OhhwellsBridge() {
19540
19592
  postToParent2({
19541
19593
  type: "ow:ready",
19542
19594
  version: "1",
19543
- bridgeVersion: "0.1.70",
19595
+ bridgeVersion: "0.1.73",
19544
19596
  path: pathname,
19545
19597
  nodes: collectEditableNodes(editContentRef.current),
19546
19598
  sections