@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.js CHANGED
@@ -13157,6 +13157,18 @@ function collectEditableNodes(extraContent, root = document) {
13157
13157
  }
13158
13158
  if (extraContent && !isScoped) {
13159
13159
  applyNavFooterDeleteOverrides(byKey, extraContent);
13160
+ for (const key of LOGO_IMAGE_KEYS) {
13161
+ if (!(key in extraContent)) continue;
13162
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
13163
+ }
13164
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
13165
+ if (!(key in extraContent)) continue;
13166
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13167
+ }
13168
+ for (const key of LOGO_SIZE_KEYS) {
13169
+ if (!(key in extraContent)) continue;
13170
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
13171
+ }
13160
13172
  }
13161
13173
  return Array.from(byKey.values());
13162
13174
  }
@@ -13754,9 +13766,21 @@ function getLinkHref4(el) {
13754
13766
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
13755
13767
  return anchor?.getAttribute("href") ?? "";
13756
13768
  }
13769
+ function normalizeLinkHref(value) {
13770
+ const href = value.trim();
13771
+ if (!href) return href;
13772
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return href;
13773
+ if (href.startsWith("/") || href.startsWith("#") || href.startsWith("?")) return href;
13774
+ if (!/^[^\s/]+\.[a-z]{2,}(?:[/:?#]|$)/i.test(href)) return href;
13775
+ return `https://${href}`;
13776
+ }
13777
+ function isPageFrameSection(el) {
13778
+ const id = el.dataset.ohwSection ?? "";
13779
+ return el.tagName === "FOOTER" || el.tagName === "HEADER" || Boolean(el.closest("footer")) || /(^|[-_])(footer|navbar)$/i.test(id);
13780
+ }
13757
13781
  function applyLinkHref(el, val) {
13758
13782
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
13759
- if (anchor) anchor.setAttribute("href", val);
13783
+ if (anchor) anchor.setAttribute("href", normalizeLinkHref(val));
13760
13784
  }
13761
13785
  function currentIconRef(el) {
13762
13786
  const uploaded = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -13886,7 +13910,7 @@ function applyLinkByKey(key, val) {
13886
13910
  });
13887
13911
  const hrefAnchors = document.querySelectorAll(`[data-ohw-href-key="${key}"]`);
13888
13912
  if (hrefAnchors.length > 0) {
13889
- setStoredLinkHref(key, val);
13913
+ setStoredLinkHref(key, normalizeLinkHref(val));
13890
13914
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
13891
13915
  }
13892
13916
  }
@@ -15071,7 +15095,8 @@ function OhhwellsBridge() {
15071
15095
  const el = document.querySelector(
15072
15096
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
15073
15097
  );
15074
- const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
15098
+ const busy = el?.querySelector('[contenteditable="true"]') ?? null;
15099
+ const target = el && !el.closest("[data-ohw-href-key]") && !busy ? el : null;
15075
15100
  if (!target) {
15076
15101
  setHoveredTextRect(null);
15077
15102
  return;
@@ -15082,7 +15107,10 @@ function OhhwellsBridge() {
15082
15107
  const observer = new MutationObserver(sync);
15083
15108
  observer.observe(document.documentElement, {
15084
15109
  attributes: true,
15085
- attributeFilter: ["data-ohw-hovered"],
15110
+ // `contenteditable` too: the box is recomputed only when one of these changes, and a click
15111
+ // that opens an element for typing changes nothing else — so the frame measured a moment
15112
+ // earlier, while merely hovering, stayed on screen inside the solid one (OHH-762).
15113
+ attributeFilter: ["data-ohw-hovered", "contenteditable"],
15086
15114
  subtree: true
15087
15115
  });
15088
15116
  return () => observer.disconnect();
@@ -15369,6 +15397,14 @@ function OhhwellsBridge() {
15369
15397
  el.removeAttribute("data-ohw-hovered");
15370
15398
  el.setAttribute("data-ohw-selected", "");
15371
15399
  }, [clearSelectedAttr]);
15400
+ const isSelectedForHover = useCallback7((el) => {
15401
+ if (!el) return false;
15402
+ return [selectedElRef.current, activeElRef.current].some(
15403
+ (busy) => busy && (el === busy || busy.contains(el) || el.contains(busy))
15404
+ );
15405
+ }, []);
15406
+ const isSelectedForHoverRef = useRef9(isSelectedForHover);
15407
+ isSelectedForHoverRef.current = isSelectedForHover;
15372
15408
  const resolveHrefKeyElement = useCallback7((hrefKey) => {
15373
15409
  if (isFooterHrefKey(hrefKey)) {
15374
15410
  return document.querySelector(
@@ -16286,6 +16322,12 @@ function OhhwellsBridge() {
16286
16322
  );
16287
16323
  const activate = useCallback7((el, options) => {
16288
16324
  if (activeElRef.current === el) return;
16325
+ document.querySelectorAll("[data-ohw-hovered]").forEach((hovered) => {
16326
+ hovered.removeAttribute("data-ohw-hovered");
16327
+ });
16328
+ setHoveredTextRect(null);
16329
+ hoveredItemElRef.current = null;
16330
+ setHoveredItemRect(null);
16289
16331
  if (isIconEditable(el)) {
16290
16332
  const social = getSocialItem(el);
16291
16333
  const words = social ? social.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') : null;
@@ -17472,13 +17514,13 @@ function OhhwellsBridge() {
17472
17514
  clearHrefKeyHover(hoverTarget);
17473
17515
  hoveredItemElRef.current = hoverTarget;
17474
17516
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
17475
- } else if (!isInsideNavigationItem(editable) && hoverTarget !== selectedElRef.current) {
17517
+ } else if (!isInsideNavigationItem(editable) && !isSelectedForHoverRef.current(hoverTarget)) {
17476
17518
  hoverTarget.setAttribute("data-ohw-hovered", "");
17477
17519
  if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
17478
17520
  hoveredNavContainerRef.current = null;
17479
17521
  setHoveredNavContainerRect(null);
17480
17522
  hoveredItemElRef.current = editable;
17481
- setHoveredItemRect(editable.getBoundingClientRect());
17523
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
17482
17524
  }
17483
17525
  }
17484
17526
  }
@@ -17933,7 +17975,7 @@ function OhhwellsBridge() {
17933
17975
  showImageHover(imgEl, isDragOver, true);
17934
17976
  }
17935
17977
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
17936
- if (textEditable && !textEditable.closest("[data-ohw-href-key]")) {
17978
+ if (textEditable && !textEditable.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEditable)) {
17937
17979
  textEditable.setAttribute("data-ohw-hovered", "");
17938
17980
  }
17939
17981
  return;
@@ -17945,7 +17987,7 @@ function OhhwellsBridge() {
17945
17987
  clearImageHover();
17946
17988
  }
17947
17989
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
17948
- if (textEditable && !textEditable.closest("[data-ohw-href-key]")) {
17990
+ if (textEditable && !textEditable.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEditable)) {
17949
17991
  textEditable.setAttribute("data-ohw-hovered", "");
17950
17992
  }
17951
17993
  return;
@@ -18019,7 +18061,7 @@ function OhhwellsBridge() {
18019
18061
  hoveredItemElRef.current = navAnchor;
18020
18062
  setHoveredItemRect(navAnchor.getBoundingClientRect());
18021
18063
  }
18022
- } else if (!textEl.closest("[data-ohw-href-key]")) {
18064
+ } else if (!textEl.closest("[data-ohw-href-key]") && !isSelectedForHoverRef.current(textEl)) {
18023
18065
  hoveredItemElRef.current = null;
18024
18066
  setHoveredItemRect(null);
18025
18067
  textEl.setAttribute("data-ohw-hovered", "");
@@ -18113,7 +18155,7 @@ function OhhwellsBridge() {
18113
18155
  const ZONE = 20;
18114
18156
  for (let i = 0; i < sections.length; i++) {
18115
18157
  const a = sections[i];
18116
- if (a.dataset.ohwSection === "footer") continue;
18158
+ if (isPageFrameSection(a)) continue;
18117
18159
  const b = sections[i + 1] ?? null;
18118
18160
  const boundaryY = a.getBoundingClientRect().bottom;
18119
18161
  if (Math.abs(y - boundaryY) <= ZONE) {
@@ -18573,9 +18615,10 @@ function OhhwellsBridge() {
18573
18615
  const handleAiDeleteSection = (e) => {
18574
18616
  if (e.data?.type !== "ow:ai-delete-section") return;
18575
18617
  const sectionId = typeof e.data.sectionId === "string" ? e.data.sectionId : "";
18576
- if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
18618
+ if (!sectionId) return;
18577
18619
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
18578
18620
  if (!exists) return;
18621
+ if (isPageFrameSection(exists)) return;
18579
18622
  const previous = aiSectionsRef.current;
18580
18623
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
18581
18624
  const nextValue = serializeAiSectionsState(nextState);
@@ -19208,6 +19251,15 @@ function OhhwellsBridge() {
19208
19251
  if (visibleViewportRef.current) {
19209
19252
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
19210
19253
  }
19254
+ handleScroll();
19255
+ document.querySelectorAll("[data-ohw-hovered]").forEach((hovered) => {
19256
+ hovered.removeAttribute("data-ohw-hovered");
19257
+ });
19258
+ setHoveredTextRect(null);
19259
+ hoveredItemElRef.current = null;
19260
+ setHoveredItemRect(null);
19261
+ hoveredNavContainerRef.current = null;
19262
+ setHoveredNavContainerRect(null);
19211
19263
  };
19212
19264
  window.addEventListener("resize", handleViewportResize, { passive: true });
19213
19265
  document.addEventListener("click", handleClick, true);
@@ -19473,7 +19525,7 @@ function OhhwellsBridge() {
19473
19525
  postToParent2({
19474
19526
  type: "ow:ready",
19475
19527
  version: "1",
19476
- bridgeVersion: "0.1.70",
19528
+ bridgeVersion: "0.1.73",
19477
19529
  path: pathname,
19478
19530
  nodes: collectEditableNodes(editContentRef.current),
19479
19531
  sections