@ohhwells/bridge 0.1.71-next.217 → 0.1.71-next.218

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
@@ -14941,9 +14941,21 @@ function getLinkHref4(el) {
14941
14941
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
14942
14942
  return anchor?.getAttribute("href") ?? "";
14943
14943
  }
14944
+ function normalizeLinkHref(value) {
14945
+ const href = value.trim();
14946
+ if (!href) return href;
14947
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return href;
14948
+ if (href.startsWith("/") || href.startsWith("#") || href.startsWith("?")) return href;
14949
+ if (!/^[^\s/]+\.[a-z]{2,}(?:[/:?#]|$)/i.test(href)) return href;
14950
+ return `https://${href}`;
14951
+ }
14952
+ function isPageFrameSection(el) {
14953
+ const id = el.dataset.ohwSection ?? "";
14954
+ return el.tagName === "FOOTER" || el.tagName === "HEADER" || Boolean(el.closest("footer")) || /(^|[-_])(footer|navbar)$/i.test(id);
14955
+ }
14944
14956
  function applyLinkHref(el, val) {
14945
14957
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
14946
- if (anchor) anchor.setAttribute("href", val);
14958
+ if (anchor) anchor.setAttribute("href", normalizeLinkHref(val));
14947
14959
  }
14948
14960
  function currentIconRef(el) {
14949
14961
  const uploaded = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -15073,7 +15085,7 @@ function applyLinkByKey(key, val) {
15073
15085
  });
15074
15086
  const hrefAnchors = document.querySelectorAll(`[data-ohw-href-key="${key}"]`);
15075
15087
  if (hrefAnchors.length > 0) {
15076
- setStoredLinkHref(key, val);
15088
+ setStoredLinkHref(key, normalizeLinkHref(val));
15077
15089
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15078
15090
  }
15079
15091
  }
@@ -16360,7 +16372,8 @@ function OhhwellsBridge() {
16360
16372
  const el = document.querySelector(
16361
16373
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
16362
16374
  );
16363
- const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
16375
+ const busy = el?.querySelector('[contenteditable="true"]') ?? null;
16376
+ const target = el && !el.closest("[data-ohw-href-key]") && !busy ? el : null;
16364
16377
  if (!target) {
16365
16378
  setHoveredTextRect(null);
16366
16379
  return;
@@ -16371,7 +16384,10 @@ function OhhwellsBridge() {
16371
16384
  const observer = new MutationObserver(sync);
16372
16385
  observer.observe(document.documentElement, {
16373
16386
  attributes: true,
16374
- attributeFilter: ["data-ohw-hovered"],
16387
+ // `contenteditable` too: the box is recomputed only when one of these changes, and a click
16388
+ // that opens an element for typing changes nothing else — so the frame measured a moment
16389
+ // earlier, while merely hovering, stayed on screen inside the solid one (OHH-762).
16390
+ attributeFilter: ["data-ohw-hovered", "contenteditable"],
16375
16391
  subtree: true
16376
16392
  });
16377
16393
  return () => observer.disconnect();
@@ -16681,6 +16697,14 @@ function OhhwellsBridge() {
16681
16697
  el.removeAttribute("data-ohw-hovered");
16682
16698
  el.setAttribute("data-ohw-selected", "");
16683
16699
  }, [clearSelectedAttr]);
16700
+ const isSelectedForHover = (0, import_react17.useCallback)((el) => {
16701
+ if (!el) return false;
16702
+ return [selectedElRef.current, activeElRef.current].some(
16703
+ (busy) => busy && (el === busy || busy.contains(el) || el.contains(busy))
16704
+ );
16705
+ }, []);
16706
+ const isSelectedForHoverRef = (0, import_react17.useRef)(isSelectedForHover);
16707
+ isSelectedForHoverRef.current = isSelectedForHover;
16684
16708
  const resolveHrefKeyElement = (0, import_react17.useCallback)((hrefKey) => {
16685
16709
  if (isFooterHrefKey(hrefKey)) {
16686
16710
  return document.querySelector(
@@ -17598,6 +17622,12 @@ function OhhwellsBridge() {
17598
17622
  );
17599
17623
  const activate = (0, import_react17.useCallback)((el, options) => {
17600
17624
  if (activeElRef.current === el) return;
17625
+ document.querySelectorAll("[data-ohw-hovered]").forEach((hovered) => {
17626
+ hovered.removeAttribute("data-ohw-hovered");
17627
+ });
17628
+ setHoveredTextRect(null);
17629
+ hoveredItemElRef.current = null;
17630
+ setHoveredItemRect(null);
17601
17631
  if (isIconEditable(el)) {
17602
17632
  const social = getSocialItem(el);
17603
17633
  const words = social ? social.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') : null;
@@ -18836,7 +18866,7 @@ function OhhwellsBridge() {
18836
18866
  clearHrefKeyHover(hoverTarget);
18837
18867
  hoveredItemElRef.current = hoverTarget;
18838
18868
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
18839
- } else if (!isInsideNavigationItem(editable) && hoverTarget !== selectedElRef.current) {
18869
+ } else if (!isInsideNavigationItem(editable) && !isSelectedForHoverRef.current(hoverTarget)) {
18840
18870
  hoverTarget.setAttribute("data-ohw-hovered", "");
18841
18871
  if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
18842
18872
  hoveredNavContainerRef.current = null;
@@ -19296,7 +19326,7 @@ function OhhwellsBridge() {
19296
19326
  showImageHover(imgEl, isDragOver, true);
19297
19327
  }
19298
19328
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19299
- if (textEditable && !textEditable.closest("[data-ohw-href-key]")) {
19329
+ if (textEditable && !textEditable.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEditable)) {
19300
19330
  textEditable.setAttribute("data-ohw-hovered", "");
19301
19331
  }
19302
19332
  return;
@@ -19308,7 +19338,7 @@ function OhhwellsBridge() {
19308
19338
  clearImageHover();
19309
19339
  }
19310
19340
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19311
- if (textEditable && !textEditable.closest("[data-ohw-href-key]")) {
19341
+ if (textEditable && !textEditable.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEditable)) {
19312
19342
  textEditable.setAttribute("data-ohw-hovered", "");
19313
19343
  }
19314
19344
  return;
@@ -19382,7 +19412,7 @@ function OhhwellsBridge() {
19382
19412
  hoveredItemElRef.current = navAnchor;
19383
19413
  setHoveredItemRect(navAnchor.getBoundingClientRect());
19384
19414
  }
19385
- } else if (!textEl.closest("[data-ohw-href-key]")) {
19415
+ } else if (!textEl.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEl)) {
19386
19416
  hoveredItemElRef.current = null;
19387
19417
  setHoveredItemRect(null);
19388
19418
  textEl.setAttribute("data-ohw-hovered", "");
@@ -19478,7 +19508,7 @@ function OhhwellsBridge() {
19478
19508
  const ZONE = 20;
19479
19509
  for (let i = 0; i < sections.length; i++) {
19480
19510
  const a = sections[i];
19481
- if (a.dataset.ohwSection === "footer") continue;
19511
+ if (isPageFrameSection(a)) continue;
19482
19512
  const b = sections[i + 1] ?? null;
19483
19513
  const boundaryY = a.getBoundingClientRect().bottom;
19484
19514
  if (Math.abs(y - boundaryY) <= ZONE) {
@@ -19970,9 +20000,10 @@ function OhhwellsBridge() {
19970
20000
  const handleAiDeleteSection = (e) => {
19971
20001
  if (e.data?.type !== "ow:ai-delete-section") return;
19972
20002
  const sectionId = typeof e.data.sectionId === "string" ? e.data.sectionId : "";
19973
- if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
20003
+ if (!sectionId) return;
19974
20004
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
19975
20005
  if (!exists) return;
20006
+ if (isPageFrameSection(exists)) return;
19976
20007
  clearInteractionChrome();
19977
20008
  const previous = aiSectionsRef.current;
19978
20009
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
@@ -20713,6 +20744,15 @@ function OhhwellsBridge() {
20713
20744
  if (visibleViewportRef.current) {
20714
20745
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
20715
20746
  }
20747
+ handleScroll();
20748
+ document.querySelectorAll("[data-ohw-hovered]").forEach((hovered) => {
20749
+ hovered.removeAttribute("data-ohw-hovered");
20750
+ });
20751
+ setHoveredTextRect(null);
20752
+ hoveredItemElRef.current = null;
20753
+ setHoveredItemRect(null);
20754
+ hoveredNavContainerRef.current = null;
20755
+ setHoveredNavContainerRect(null);
20716
20756
  };
20717
20757
  window.addEventListener("resize", handleViewportResize, { passive: true });
20718
20758
  document.addEventListener("click", handleClick, true);
@@ -20983,7 +21023,7 @@ function OhhwellsBridge() {
20983
21023
  postToParent2({
20984
21024
  type: "ow:ready",
20985
21025
  version: "1",
20986
- bridgeVersion: "0.1.71",
21026
+ bridgeVersion: "0.1.71-next.218",
20987
21027
  path: pathname,
20988
21028
  nodes: collectEditableNodes(editContentRef.current),
20989
21029
  sections