@ohhwells/bridge 0.1.38-next.56 → 0.1.38-next.57

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
@@ -5181,9 +5181,9 @@ var DialogContent = React7.forwardRef(
5181
5181
  "data-slot": "dialog-content",
5182
5182
  className: cn(
5183
5183
  positionMode,
5184
- "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[483px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
5184
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
5185
5185
  "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5186
- container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5186
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
5187
5187
  className
5188
5188
  ),
5189
5189
  onMouseDown: (e) => e.stopPropagation(),
@@ -5448,7 +5448,7 @@ function UrlOrPageInput({
5448
5448
  requestAnimationFrame(() => inputRef.current?.focus());
5449
5449
  };
5450
5450
  const fieldClassName = cn(
5451
- "data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5451
+ "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5452
5452
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5453
5453
  );
5454
5454
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
@@ -5634,6 +5634,26 @@ function rectsEqual(a, b) {
5634
5634
  }
5635
5635
  return true;
5636
5636
  }
5637
+ function readSectionRects(sectionIds) {
5638
+ const next = /* @__PURE__ */ new Map();
5639
+ for (const id of sectionIds) {
5640
+ const el = document.querySelector(
5641
+ `[data-ohw-section="${CSS.escape(id)}"]`
5642
+ );
5643
+ if (el) next.set(id, el.getBoundingClientRect());
5644
+ }
5645
+ return next;
5646
+ }
5647
+ function hitTestSectionId(x, y, sectionIds, rects) {
5648
+ for (const id of sectionIds) {
5649
+ const rect = rects.get(id);
5650
+ if (!rect || rect.width <= 0 || rect.height <= 0) continue;
5651
+ if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
5652
+ return id;
5653
+ }
5654
+ }
5655
+ return null;
5656
+ }
5637
5657
  function useSectionRects(sectionIdsKey, enabled) {
5638
5658
  const [rects, setRects] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
5639
5659
  const sectionIds = (0, import_react5.useMemo)(
@@ -5646,13 +5666,7 @@ function useSectionRects(sectionIdsKey, enabled) {
5646
5666
  return;
5647
5667
  }
5648
5668
  const update = () => {
5649
- const next = /* @__PURE__ */ new Map();
5650
- for (const id of sectionIds) {
5651
- const el = document.querySelector(
5652
- `[data-ohw-section="${CSS.escape(id)}"]`
5653
- );
5654
- if (el) next.set(id, el.getBoundingClientRect());
5655
- }
5669
+ const next = readSectionRects(sectionIds);
5656
5670
  setRects((prev) => rectsEqual(prev, next) ? prev : next);
5657
5671
  };
5658
5672
  update();
@@ -5689,6 +5703,10 @@ function SectionPickerOverlay({
5689
5703
  const pathname = (0, import_navigation.usePathname)();
5690
5704
  const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
5691
5705
  const [hoveredId, setHoveredId] = (0, import_react5.useState)(null);
5706
+ const pointerRef = (0, import_react5.useRef)(null);
5707
+ const pointerScreenRef = (0, import_react5.useRef)(null);
5708
+ const iframeOffsetTopRef = (0, import_react5.useRef)(null);
5709
+ const sectionIdsRef = (0, import_react5.useRef)([]);
5692
5710
  const [chromeClip, setChromeClip] = (0, import_react5.useState)(
5693
5711
  () => ({
5694
5712
  top: 0,
@@ -5711,6 +5729,29 @@ function SectionPickerOverlay({
5711
5729
  document.documentElement.removeAttribute("data-ohw-section-picking");
5712
5730
  };
5713
5731
  }, []);
5732
+ const liveSections = (0, import_react5.useMemo)(() => {
5733
+ if (!isOnTargetPage) return sections;
5734
+ const live = collectSectionsFromDom();
5735
+ if (live.length === 0) return sections;
5736
+ const labels = new Map(sections.map((s) => [s.id, s.label]));
5737
+ return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5738
+ }, [isOnTargetPage, sections, pathname]);
5739
+ const sectionIdsKey = (0, import_react5.useMemo)(
5740
+ () => liveSections.map((s) => s.id).join("\0"),
5741
+ [liveSections]
5742
+ );
5743
+ const sectionIds = (0, import_react5.useMemo)(
5744
+ () => liveSections.map((s) => s.id),
5745
+ [liveSections]
5746
+ );
5747
+ sectionIdsRef.current = sectionIds;
5748
+ const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5749
+ const applyHoverAt = (0, import_react5.useCallback)((x, y, liveRects) => {
5750
+ const ids = sectionIdsRef.current;
5751
+ const map = liveRects ?? readSectionRects(ids);
5752
+ const next = hitTestSectionId(x, y, ids, map);
5753
+ setHoveredId((prev) => prev === next ? prev : next);
5754
+ }, []);
5714
5755
  (0, import_react5.useEffect)(() => {
5715
5756
  const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
5716
5757
  const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
@@ -5726,6 +5767,19 @@ function SectionPickerOverlay({
5726
5767
  if (e.data?.type !== "ow:parent-scroll") return;
5727
5768
  const { iframeOffsetTop, headerH, canvasH } = e.data;
5728
5769
  applyClip(iframeOffsetTop, headerH, canvasH);
5770
+ iframeOffsetTopRef.current = iframeOffsetTop;
5771
+ if (!pointerScreenRef.current && pointerRef.current) {
5772
+ pointerScreenRef.current = {
5773
+ x: pointerRef.current.x,
5774
+ y: pointerRef.current.y + iframeOffsetTop
5775
+ };
5776
+ }
5777
+ const screen = pointerScreenRef.current;
5778
+ if (screen) {
5779
+ const next = { x: screen.x, y: screen.y - iframeOffsetTop };
5780
+ pointerRef.current = next;
5781
+ applyHoverAt(next.x, next.y);
5782
+ }
5729
5783
  };
5730
5784
  const onResize = () => {
5731
5785
  setChromeClip((prev) => {
@@ -5739,19 +5793,36 @@ function SectionPickerOverlay({
5739
5793
  window.removeEventListener("message", onParentScroll);
5740
5794
  window.removeEventListener("resize", onResize);
5741
5795
  };
5742
- }, []);
5743
- const liveSections = (0, import_react5.useMemo)(() => {
5744
- if (!isOnTargetPage) return sections;
5745
- const live = collectSectionsFromDom();
5746
- if (live.length === 0) return sections;
5747
- const labels = new Map(sections.map((s) => [s.id, s.label]));
5748
- return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5749
- }, [isOnTargetPage, sections, pathname]);
5750
- const sectionIdsKey = (0, import_react5.useMemo)(
5751
- () => liveSections.map((s) => s.id).join("\0"),
5752
- [liveSections]
5753
- );
5754
- const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5796
+ }, [applyHoverAt]);
5797
+ (0, import_react5.useEffect)(() => {
5798
+ const ptr = pointerRef.current;
5799
+ if (!ptr) return;
5800
+ applyHoverAt(ptr.x, ptr.y, rects);
5801
+ }, [rects, applyHoverAt]);
5802
+ (0, import_react5.useEffect)(() => {
5803
+ const rememberPointer = (clientX, clientY) => {
5804
+ pointerRef.current = { x: clientX, y: clientY };
5805
+ const top = iframeOffsetTopRef.current;
5806
+ if (top != null) {
5807
+ pointerScreenRef.current = { x: clientX, y: clientY + top };
5808
+ }
5809
+ applyHoverAt(clientX, clientY, rects);
5810
+ };
5811
+ const onPointerMove = (e) => {
5812
+ rememberPointer(e.clientX, e.clientY);
5813
+ };
5814
+ const onPointerSync = (e) => {
5815
+ if (e.data?.type !== "ow:pointer-sync") return;
5816
+ const { clientX, clientY } = e.data;
5817
+ rememberPointer(clientX, clientY);
5818
+ };
5819
+ window.addEventListener("pointermove", onPointerMove, { passive: true });
5820
+ window.addEventListener("message", onPointerSync);
5821
+ return () => {
5822
+ window.removeEventListener("pointermove", onPointerMove);
5823
+ window.removeEventListener("message", onPointerSync);
5824
+ };
5825
+ }, [rects, applyHoverAt]);
5755
5826
  const handleSelect = (0, import_react5.useCallback)(
5756
5827
  (section) => {
5757
5828
  if (selectedId) return;
@@ -5845,8 +5916,6 @@ function SectionPickerOverlay({
5845
5916
  backgroundColor: isLit ? "transparent" : DIM_OVERLAY
5846
5917
  },
5847
5918
  "aria-label": `Select section ${section.label}`,
5848
- onMouseEnter: () => setHoveredId(section.id),
5849
- onMouseLeave: () => setHoveredId((prev) => prev === section.id ? null : prev),
5850
5919
  onClick: () => handleSelect(section),
5851
5920
  children: isSelected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5852
5921
  "span",
@@ -6090,7 +6159,8 @@ function LinkPopover({
6090
6159
  if (overflowY !== "auto" && overflowY !== "scroll") return false;
6091
6160
  if (el.scrollHeight <= el.clientHeight + 1) return false;
6092
6161
  if (deltaY < 0) return el.scrollTop > 0;
6093
- if (deltaY > 0) return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6162
+ if (deltaY > 0)
6163
+ return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6094
6164
  return true;
6095
6165
  };
6096
6166
  const allowsInternalScroll = (e) => {
@@ -6116,8 +6186,16 @@ function LinkPopover({
6116
6186
  body.style.overflow = prevBodyOverflow;
6117
6187
  html.style.overscrollBehavior = prevHtmlOverscroll;
6118
6188
  body.style.overscrollBehavior = prevBodyOverscroll;
6119
- document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6120
- document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6189
+ document.removeEventListener(
6190
+ "wheel",
6191
+ preventBackgroundScroll,
6192
+ scrollOpts
6193
+ );
6194
+ document.removeEventListener(
6195
+ "touchmove",
6196
+ preventBackgroundScroll,
6197
+ scrollOpts
6198
+ );
6121
6199
  };
6122
6200
  }, [open, sectionPickerActive]);
6123
6201
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
@@ -6137,7 +6215,7 @@ function LinkPopover({
6137
6215
  "data-ohw-link-modal-root": "",
6138
6216
  "data-ohw-bridge": "",
6139
6217
  showCloseButton: false,
6140
- className: "gap-0 p-0 w-full md:w-md pointer-events-auto z-[2147483646] overflow-visible",
6218
+ className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6141
6219
  children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { state, onClose })
6142
6220
  }
6143
6221
  )
@@ -9413,14 +9491,20 @@ function OhhwellsBridge() {
9413
9491
  const session = linkPopoverSessionRef.current;
9414
9492
  if (!session) return;
9415
9493
  if (session.intent === "add-nav") {
9416
- const { pageRoute, sectionId } = parseTarget(target);
9417
- const page = resolvePage(pageRoute, sitePages);
9418
- const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9419
- const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9420
- const label = section?.label ?? page.title;
9494
+ const trimmed = target.trim();
9495
+ let href = trimmed;
9496
+ let label = "Untitled";
9497
+ if (trimmed) {
9498
+ const { pageRoute, sectionId } = parseTarget(trimmed);
9499
+ const page = resolvePage(pageRoute, sitePages);
9500
+ const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9501
+ const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9502
+ label = section?.label ?? page.title;
9503
+ href = trimmed;
9504
+ }
9421
9505
  const afterAnchor = addNavAfterAnchorRef.current;
9422
- const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(target, label, afterAnchor);
9423
- applyLinkByKey(hrefKey, target);
9506
+ const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(href, label, afterAnchor);
9507
+ applyLinkByKey(hrefKey, href);
9424
9508
  document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9425
9509
  el.textContent = label;
9426
9510
  });
@@ -9428,7 +9512,7 @@ function OhhwellsBridge() {
9428
9512
  const orderJson = JSON.stringify(order);
9429
9513
  editContentRef.current = {
9430
9514
  ...editContentRef.current,
9431
- [hrefKey]: target,
9515
+ [hrefKey]: href,
9432
9516
  [labelKey]: label,
9433
9517
  [NAV_COUNT_KEY]: navCount,
9434
9518
  [NAV_ORDER_KEY]: orderJson
@@ -9436,12 +9520,13 @@ function OhhwellsBridge() {
9436
9520
  postToParent2({
9437
9521
  type: "ow:change",
9438
9522
  nodes: [
9439
- { key: hrefKey, text: target },
9523
+ { key: hrefKey, text: href },
9440
9524
  { key: labelKey, text: label },
9441
9525
  { key: NAV_COUNT_KEY, text: navCount },
9442
9526
  { key: NAV_ORDER_KEY, text: orderJson }
9443
9527
  ]
9444
9528
  });
9529
+ postToParent2({ type: "ow:toast", title: "Link added", toastType: "success" });
9445
9530
  addNavAfterAnchorRef.current = null;
9446
9531
  setLinkPopover(null);
9447
9532
  enforceLinkHrefs();