@ohhwells/bridge 0.1.114 → 0.1.115

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
@@ -459,6 +459,20 @@ var BG_VALUES = {
459
459
  accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
460
460
  "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
461
461
  };
462
+ var NAVBAR_COLOR_TOKENS = ["dark", "primary", "accent", "light"];
463
+ function navbarColorVar(token) {
464
+ const fallback = {
465
+ dark: "#0C0A09",
466
+ primary: "#0078E5",
467
+ accent: "#0078E5",
468
+ light: "#FFFFFF"
469
+ };
470
+ return `var(--ohw-brand-${token}, var(--color-${token}, ${fallback[token]}))`;
471
+ }
472
+ function resolveNavbarValue(value) {
473
+ if (value === void 0) return void 0;
474
+ return NAVBAR_COLOR_TOKENS.includes(value) ? navbarColorVar(value) : value;
475
+ }
462
476
  var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
463
477
  function styleSheetCss() {
464
478
  const rules = [];
@@ -628,8 +642,18 @@ function applyStylesToDom(store) {
628
642
  for (const [sectionId, override] of Object.entries(store.sections)) {
629
643
  const escaped = CSS.escape(sectionId);
630
644
  const byInstance = document.querySelectorAll(`[data-ohw-instance="${escaped}"]`);
631
- const sections = byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`);
645
+ const sections = Array.from(
646
+ byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`)
647
+ );
648
+ const isNavbar = sectionId === "navbar";
649
+ if (isNavbar) {
650
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
651
+ if (drawer && !sections.includes(drawer)) sections.push(drawer);
652
+ const header = document.querySelector("[data-ohw-nav-root]");
653
+ if (header && !sections.includes(header)) sections.push(header);
654
+ }
632
655
  for (const marker of Array.from(sections)) {
656
+ const isDrawerMarker = isNavbar && marker.hasAttribute("data-ohw-nav-drawer");
633
657
  const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
634
658
  for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
635
659
  const value = override[prop];
@@ -637,19 +661,23 @@ function applyStylesToDom(store) {
637
661
  if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
638
662
  section.setAttribute(attr, String(value).replace(":", "-"));
639
663
  }
640
- if (override.sectionBackgroundColor !== void 0) {
664
+ const bgOverride = isDrawerMarker ? override.navbarMobileBackground : override.sectionBackgroundColor;
665
+ const textOverride = isDrawerMarker ? override.navbarMobileTextColor : override.textColor;
666
+ const bgValue = isNavbar ? resolveNavbarValue(bgOverride) : bgOverride;
667
+ const textValue = isNavbar ? resolveNavbarValue(textOverride) : textOverride;
668
+ if (bgValue !== void 0) {
641
669
  saveInline(section, "background");
642
- section.style.setProperty("background", override.sectionBackgroundColor, "important");
670
+ section.style.setProperty("background", bgValue, "important");
643
671
  section.setAttribute("data-ohw-style-bgcolor", "");
644
672
  }
645
- if (override.textColor !== void 0 || override.fontFamily !== void 0) {
673
+ if (textValue !== void 0 || override.fontFamily !== void 0) {
646
674
  const textEls = marker.querySelectorAll(
647
675
  '[data-ohw-editable="text"], [data-ohw-editable="plain"], [data-ohw-field-label]'
648
676
  );
649
677
  for (const el of Array.from(textEls)) {
650
- if (override.textColor !== void 0) {
678
+ if (textValue !== void 0) {
651
679
  saveInline(el, "color");
652
- el.style.setProperty("color", override.textColor, "important");
680
+ el.style.setProperty("color", textValue, "important");
653
681
  el.setAttribute(NODE_WROTE_ATTR, "");
654
682
  }
655
683
  if (override.fontFamily !== void 0) {
@@ -9101,6 +9129,57 @@ function useLiveSectionRect(sectionId) {
9101
9129
  }, [sectionId]);
9102
9130
  return rect;
9103
9131
  }
9132
+ var NAV_TOGGLE_LABEL_RE = /^(toggle menu|open menu|close menu)$/i;
9133
+ function readNavToggleRect() {
9134
+ const el = Array.from(document.querySelectorAll("button[aria-label]")).find(
9135
+ (e) => NAV_TOGGLE_LABEL_RE.test(e.getAttribute("aria-label") ?? "")
9136
+ );
9137
+ if (!el) return null;
9138
+ const r2 = el.getBoundingClientRect();
9139
+ if (r2.width <= 0 || r2.height <= 0) return null;
9140
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
9141
+ }
9142
+ function useLiveNavToggleHole(active) {
9143
+ const [rect, setRect] = (0, import_react8.useState)(null);
9144
+ (0, import_react8.useEffect)(() => {
9145
+ if (!active) {
9146
+ setRect(null);
9147
+ return;
9148
+ }
9149
+ const update = () => {
9150
+ const next = readNavToggleRect();
9151
+ setRect(
9152
+ (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
9153
+ );
9154
+ };
9155
+ update();
9156
+ const opts = { capture: true, passive: true };
9157
+ window.addEventListener("scroll", update, opts);
9158
+ window.addEventListener("resize", update);
9159
+ const interval = setInterval(update, 500);
9160
+ return () => {
9161
+ window.removeEventListener("scroll", update, opts);
9162
+ window.removeEventListener("resize", update);
9163
+ clearInterval(interval);
9164
+ };
9165
+ }, [active]);
9166
+ return rect;
9167
+ }
9168
+ function subtractRect(outer, hole) {
9169
+ if (!hole) return [outer];
9170
+ const l = Math.max(outer.left, hole.left);
9171
+ const t = Math.max(outer.top, hole.top);
9172
+ const r2 = Math.min(outer.left + outer.width, hole.left + hole.width);
9173
+ const b = Math.min(outer.top + outer.height, hole.top + hole.height);
9174
+ if (l >= r2 || t >= b) return [outer];
9175
+ const rects = [];
9176
+ if (t > outer.top) rects.push({ left: outer.left, top: outer.top, width: outer.width, height: t - outer.top });
9177
+ const midHeight = b - t;
9178
+ if (l > outer.left) rects.push({ left: outer.left, top: t, width: l - outer.left, height: midHeight });
9179
+ if (r2 < outer.left + outer.width) rects.push({ left: r2, top: t, width: outer.left + outer.width - r2, height: midHeight });
9180
+ if (b < outer.top + outer.height) rects.push({ left: outer.left, top: b, width: outer.width, height: outer.top + outer.height - b });
9181
+ return rects;
9182
+ }
9104
9183
  function computeSectionBoundaryFlags(instanceId) {
9105
9184
  const topLevel = topLevelSections();
9106
9185
  const index = topLevel.findIndex((el) => instanceIdOf(el) === instanceId);
@@ -9316,6 +9395,7 @@ function AiSectionOverlay({
9316
9395
  const activeSelectionId = reviewId ? null : selectedId;
9317
9396
  const selectionRect = useLiveSectionRect(activeSelectionId);
9318
9397
  const reviewRect = useLiveSectionRect(reviewId);
9398
+ const navToggleHole = useLiveNavToggleHole(Boolean(reviewId));
9319
9399
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
9320
9400
  const showSelectionBorder = Boolean(selectionRect) && !navDropdownForceOpen;
9321
9401
  (0, import_react8.useEffect)(() => {
@@ -9375,48 +9455,65 @@ function AiSectionOverlay({
9375
9455
  }
9376
9456
  }
9377
9457
  ),
9378
- reviewRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
9379
- "div",
9380
- {
9381
- "data-ohw-ai-review": "",
9382
- style: {
9383
- position: "fixed",
9384
- top: reviewRect.top,
9385
- left: reviewRect.left,
9386
- width: reviewRect.width,
9387
- height: reviewRect.height,
9388
- border: `2px solid ${PRIMARY2}`,
9389
- borderRadius: edgeAwareRadius(reviewRect),
9390
- zIndex: 2147483200,
9391
- // The veil itself: swallows clicks so the section stays locked until decided. This
9392
- // stopPropagation only guards the bubble phase; the bridge's capture-phase click
9393
- // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
9394
- // Accept/Discard resolves to the media beneath and opens the file picker.
9395
- background: "rgba(8, 133, 254, 0.04)",
9396
- pointerEvents: "auto",
9397
- cursor: "default"
9458
+ reviewRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
9459
+ subtractRect(reviewRect, navToggleHole).map((r2, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
9460
+ "div",
9461
+ {
9462
+ "data-ohw-ai-review": "",
9463
+ "aria-hidden": true,
9464
+ style: {
9465
+ position: "fixed",
9466
+ top: r2.top,
9467
+ left: r2.left,
9468
+ width: r2.width,
9469
+ height: r2.height,
9470
+ background: "rgba(8, 133, 254, 0.04)",
9471
+ pointerEvents: "auto",
9472
+ cursor: "default",
9473
+ zIndex: 2147483200
9474
+ },
9475
+ onClick: (e) => e.stopPropagation()
9398
9476
  },
9399
- onClick: (e) => e.stopPropagation(),
9400
- children: !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
9401
- "div",
9402
- {
9403
- style: {
9404
- position: "sticky",
9405
- top: 12,
9406
- display: "flex",
9407
- gap: 8,
9408
- justifyContent: "flex-end",
9409
- paddingRight: 12,
9410
- paddingTop: 12
9411
- },
9412
- children: [
9413
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Accept", onClick: () => decide("accept"), background: PRIMARY2, color: "#ffffff", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.Check, { size: 16, strokeWidth: 2.5, "aria-hidden": true }) }),
9414
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Discard", onClick: () => decide("discard"), background: "#EFF6FF", color: "#0c0a09", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, strokeWidth: 2, "aria-hidden": true }) })
9415
- ]
9477
+ i
9478
+ )),
9479
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
9480
+ "div",
9481
+ {
9482
+ "aria-hidden": true,
9483
+ style: {
9484
+ position: "fixed",
9485
+ top: reviewRect.top,
9486
+ left: reviewRect.left,
9487
+ width: reviewRect.width,
9488
+ height: reviewRect.height,
9489
+ border: `2px solid ${PRIMARY2}`,
9490
+ borderRadius: edgeAwareRadius(reviewRect),
9491
+ pointerEvents: "none",
9492
+ zIndex: 2147483200
9416
9493
  }
9417
- )
9418
- }
9419
- )
9494
+ }
9495
+ ),
9496
+ !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
9497
+ "div",
9498
+ {
9499
+ style: {
9500
+ position: "fixed",
9501
+ top: reviewRect.top + 12,
9502
+ left: reviewRect.left,
9503
+ width: reviewRect.width,
9504
+ display: "flex",
9505
+ gap: 8,
9506
+ justifyContent: "flex-end",
9507
+ paddingRight: 12,
9508
+ zIndex: 2147483201
9509
+ },
9510
+ children: [
9511
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Accept", onClick: () => decide("accept"), background: PRIMARY2, color: "#ffffff", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.Check, { size: 16, strokeWidth: 2.5, "aria-hidden": true }) }),
9512
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Discard", onClick: () => decide("discard"), background: "#EFF6FF", color: "#0c0a09", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, strokeWidth: 2, "aria-hidden": true }) })
9513
+ ]
9514
+ }
9515
+ )
9516
+ ] })
9420
9517
  ] });
9421
9518
  }
9422
9519
 
@@ -11079,6 +11176,15 @@ function getNavbarDesktopContainer() {
11079
11176
  function getNavbarDrawerContainer() {
11080
11177
  return document.querySelector("[data-ohw-nav-drawer]");
11081
11178
  }
11179
+ function isNavDrawerOpen() {
11180
+ return document.querySelector('[data-ohw-nav-drawer][data-ohw-section="navbar"]') !== null;
11181
+ }
11182
+ function isPointOutsideOpenDrawer(clientX, clientY) {
11183
+ const drawer = document.querySelector('[data-ohw-nav-drawer][data-ohw-section="navbar"]');
11184
+ if (!drawer) return false;
11185
+ const r2 = drawer.getBoundingClientRect();
11186
+ return clientX < r2.left || clientX > r2.right || clientY < r2.top || clientY > r2.bottom;
11187
+ }
11082
11188
  function isNavbarListContainerVisible(el) {
11083
11189
  if (el.hasAttribute("hidden")) return false;
11084
11190
  if (el.closest("[hidden]")) return false;
@@ -17805,6 +17911,7 @@ function OhhwellsBridge() {
17805
17911
  const aiSectionsRef = (0, import_react17.useRef)("");
17806
17912
  const brandKitRef = (0, import_react17.useRef)("");
17807
17913
  const stylesRef = (0, import_react17.useRef)("");
17914
+ const appliedDrawerRef = (0, import_react17.useRef)(null);
17808
17915
  const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
17809
17916
  const [sitePages, setSitePages] = (0, import_react17.useState)([]);
17810
17917
  const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
@@ -19470,6 +19577,17 @@ function OhhwellsBridge() {
19470
19577
  if (!isEditMode) return;
19471
19578
  initSectionInstancesFromContent(editContentRef.current, pathname);
19472
19579
  }, [pathname, isEditMode]);
19580
+ (0, import_react17.useEffect)(() => {
19581
+ if (!isEditMode) return;
19582
+ const observer = new MutationObserver(() => {
19583
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
19584
+ if (!drawer || drawer === appliedDrawerRef.current) return;
19585
+ appliedDrawerRef.current = drawer;
19586
+ applyStylesToDom(parseStyleStore(stylesRef.current));
19587
+ });
19588
+ observer.observe(document.body, { childList: true, subtree: true });
19589
+ return () => observer.disconnect();
19590
+ }, [isEditMode, pathname]);
19473
19591
  (0, import_react17.useEffect)(() => {
19474
19592
  const contentForNav = () => {
19475
19593
  if (isEditMode) return editContentRef.current;
@@ -20231,6 +20349,19 @@ function OhhwellsBridge() {
20231
20349
  const handleMouseOver = (e) => {
20232
20350
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
20233
20351
  const target = e.target;
20352
+ if (isNavDrawerOpen() && isPointOutsideOpenDrawer(e.clientX, e.clientY)) {
20353
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
20354
+ hoveredItemElRef.current = null;
20355
+ setHoveredItemRect(null);
20356
+ hoveredNavContainerRef.current = null;
20357
+ setHoveredNavContainerRect(null);
20358
+ formHoverElRef.current = null;
20359
+ setFormHoverRect(null);
20360
+ siblingHintElRef.current = null;
20361
+ setSiblingHintRect(null);
20362
+ setSiblingHintRects([]);
20363
+ return;
20364
+ }
20234
20365
  if (linkPopoverOpenRef.current) {
20235
20366
  hoveredItemElRef.current = null;
20236
20367
  setHoveredItemRect(null);
@@ -21047,6 +21178,26 @@ function OhhwellsBridge() {
21047
21178
  const handleMouseMove = (e) => {
21048
21179
  const { clientX, clientY } = e;
21049
21180
  if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21181
+ if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
21182
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
21183
+ formHoverElRef.current = null;
21184
+ setFormHoverRect(null);
21185
+ hoveredItemElRef.current = null;
21186
+ setHoveredItemRect(null);
21187
+ hoveredNavContainerRef.current = null;
21188
+ setHoveredNavContainerRect(null);
21189
+ dismissImageHover();
21190
+ if (hoveredGapRef.current) {
21191
+ hoveredGapRef.current = null;
21192
+ setSectionGap(null);
21193
+ }
21194
+ if (activeStateElRef.current) {
21195
+ activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
21196
+ activeStateElRef.current = null;
21197
+ setToggleState(null);
21198
+ }
21199
+ return;
21200
+ }
21050
21201
  if (isOverEditorChrome(clientX, clientY)) {
21051
21202
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
21052
21203
  formHoverElRef.current = null;
@@ -21068,6 +21219,7 @@ function OhhwellsBridge() {
21068
21219
  const { clientX, clientY } = e.data;
21069
21220
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
21070
21221
  if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21222
+ if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
21071
21223
  if (probeSocialsRowAt(clientX, clientY)) return;
21072
21224
  probeSectionGapAt(clientX, clientY);
21073
21225
  probeImageAt(clientX, clientY);
@@ -22763,7 +22915,7 @@ function OhhwellsBridge() {
22763
22915
  postToParent2({
22764
22916
  type: "ow:ready",
22765
22917
  version: "1",
22766
- bridgeVersion: "0.1.113",
22918
+ bridgeVersion: "0.1.114",
22767
22919
  path: pathname,
22768
22920
  nodes: collectEditableNodes(editContentRef.current),
22769
22921
  sections