@ohhwells/bridge 0.1.99 → 0.1.101

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
@@ -2547,6 +2547,10 @@ var REPLACED_ATTR = "data-ohw-ai-replaced-by";
2547
2547
  var REMOVED_ATTR2 = "data-ohw-ai-removed";
2548
2548
  var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
2549
2549
  var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
2550
+ function isChromeSection2(el) {
2551
+ if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) return true;
2552
+ return el.tagName === "HEADER" || el.tagName === "FOOTER";
2553
+ }
2550
2554
  function readRootVar(name) {
2551
2555
  if (typeof document === "undefined") return "";
2552
2556
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
@@ -2598,7 +2602,10 @@ function deriveTemplateButtonStyle() {
2598
2602
  ].map((v) => v || "0px");
2599
2603
  const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
2600
2604
  const px = (v) => parseFloat(v) || 0;
2601
- const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom));
2605
+ const lineHeight = px(cs.lineHeight) || px(cs.fontSize) * 1.2;
2606
+ const contentH = btn.getBoundingClientRect().height - px(cs.borderTopWidth) - px(cs.borderBottomWidth);
2607
+ const impliedY = Math.round(Math.max(0, (contentH - lineHeight) / 2));
2608
+ const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom), impliedY);
2602
2609
  const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
2603
2610
  return {
2604
2611
  radius: radius || "10px",
@@ -2624,6 +2631,19 @@ function findPlacementAnchor(id, exclude) {
2624
2631
  for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
2625
2632
  if (el === exclude) continue;
2626
2633
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
2634
+ if (isChromeSection2(el)) return null;
2635
+ return el;
2636
+ }
2637
+ return null;
2638
+ }
2639
+ function findFooterSection() {
2640
+ const byId = findTemplateSection("footer");
2641
+ if (byId) return byId;
2642
+ for (const el of Array.from(
2643
+ document.querySelectorAll("footer[data-ohw-section]")
2644
+ ).reverse()) {
2645
+ if (el.hasAttribute(CONTAINER_ATTR)) continue;
2646
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
2627
2647
  return el;
2628
2648
  }
2629
2649
  return null;
@@ -2650,7 +2670,7 @@ function placeContainer(container, entry) {
2650
2670
  return;
2651
2671
  }
2652
2672
  }
2653
- const footer = findTemplateSection("footer");
2673
+ const footer = findFooterSection();
2654
2674
  if (footer) {
2655
2675
  footer.insertAdjacentElement("beforebegin", container);
2656
2676
  } else {
@@ -2685,7 +2705,7 @@ function syncTemplateHidden(state, pageHasSections) {
2685
2705
  if (!hide) return;
2686
2706
  for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
2687
2707
  if (el.hasAttribute(CONTAINER_ATTR)) continue;
2688
- if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
2708
+ if (isChromeSection2(el)) continue;
2689
2709
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
2690
2710
  if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR2)) continue;
2691
2711
  el.style.display = "none";
@@ -7407,9 +7427,7 @@ function ItemActionToolbar({
7407
7427
  ToggleGroup,
7408
7428
  {
7409
7429
  value: dropdownOpen ? "open" : "closed",
7410
- onValueChange: (value) => {
7411
- if (value !== "open" && value !== "closed") return;
7412
- onDropdownOpenChange?.(value === "open");
7430
+ onValueChange: () => {
7413
7431
  },
7414
7432
  className: "h-7 gap-0.5 rounded-[calc(var(--radius,0.5rem)-2px)] bg-muted p-0.5",
7415
7433
  onMouseDown: (e) => {
@@ -7424,6 +7442,11 @@ function ItemActionToolbar({
7424
7442
  size: "sm",
7425
7443
  "aria-label": "Closed",
7426
7444
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7445
+ onMouseDown: (e) => {
7446
+ e.preventDefault();
7447
+ e.stopPropagation();
7448
+ if (dropdownOpen !== false) onDropdownOpenChange?.(false);
7449
+ },
7427
7450
  children: "Closed"
7428
7451
  }
7429
7452
  ),
@@ -7434,6 +7457,11 @@ function ItemActionToolbar({
7434
7457
  size: "sm",
7435
7458
  "aria-label": "Open",
7436
7459
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7460
+ onMouseDown: (e) => {
7461
+ e.preventDefault();
7462
+ e.stopPropagation();
7463
+ if (dropdownOpen !== true) onDropdownOpenChange?.(true);
7464
+ },
7437
7465
  children: "Open"
7438
7466
  }
7439
7467
  )
@@ -8459,7 +8487,7 @@ function FieldTypePicker({ onPick }) {
8459
8487
  "div",
8460
8488
  {
8461
8489
  "data-ohw-field-type-picker": "",
8462
- className: "pointer-events-auto grid w-[420px] grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
8490
+ className: "pointer-events-auto grid w-[280px] grid-cols-2 gap-2 rounded-xl border border-border bg-background p-3 shadow-lg",
8463
8491
  children: FIELD_TYPES.map((entry) => {
8464
8492
  const Icon = TYPE_ICONS[entry.type];
8465
8493
  return /* @__PURE__ */ jsxs6(
@@ -8467,7 +8495,7 @@ function FieldTypePicker({ onPick }) {
8467
8495
  {
8468
8496
  type: "button",
8469
8497
  onClick: () => onPick(entry.type),
8470
- className: "flex h-[104px] flex-col items-center justify-center gap-3 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
8498
+ className: "flex h-[76px] flex-col items-center justify-center gap-2 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
8471
8499
  children: [
8472
8500
  /* @__PURE__ */ jsx13(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8473
8501
  entry.label
@@ -10820,6 +10848,32 @@ function getNavbarDesktopContainer() {
10820
10848
  function getNavbarDrawerContainer() {
10821
10849
  return document.querySelector("[data-ohw-nav-drawer]");
10822
10850
  }
10851
+ function isNavbarListContainerVisible(el) {
10852
+ if (el.hasAttribute("hidden")) return false;
10853
+ if (el.getClientRects().length === 0) return false;
10854
+ const style = window.getComputedStyle(el);
10855
+ return style.display !== "none" && style.visibility !== "hidden";
10856
+ }
10857
+ function getActiveNavbarListContainer(draggedEl) {
10858
+ const desktop = getNavbarDesktopContainer();
10859
+ const drawer = getNavbarDrawerContainer();
10860
+ if (draggedEl) {
10861
+ if (drawer?.contains(draggedEl)) return drawer;
10862
+ if (desktop?.contains(draggedEl)) return desktop;
10863
+ }
10864
+ if (desktop && isNavbarListContainerVisible(desktop)) return desktop;
10865
+ if (drawer && isNavbarListContainerVisible(drawer)) return drawer;
10866
+ return desktop ?? drawer;
10867
+ }
10868
+ function listActiveNavbarItems(draggedEl) {
10869
+ const container = getActiveNavbarListContainer(draggedEl);
10870
+ if (container) {
10871
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(
10872
+ isNavbarLinkItem
10873
+ );
10874
+ }
10875
+ return listNavbarItems();
10876
+ }
10823
10877
  function listNavbarItems() {
10824
10878
  const desktop = getNavbarDesktopContainer();
10825
10879
  if (desktop) {
@@ -11932,20 +11986,23 @@ function isEmptyLabelValue(value) {
11932
11986
  function applyStoredValues(item, content) {
11933
11987
  const hrefKey = socialHrefKey(item);
11934
11988
  const iconKey = socialIconKey(item);
11989
+ const baseKey = hrefKey?.replace(/-href$/, "");
11935
11990
  if (hrefKey && content[hrefKey] !== void 0) item.setAttribute("href", content[hrefKey]);
11936
- if (iconKey) {
11991
+ const glyphKey = iconKey ?? baseKey ?? null;
11992
+ const glyphStored = glyphKey ? content[glyphKey]?.trim() : void 0;
11993
+ if (glyphStored) {
11994
+ ensureIconSlot(item);
11937
11995
  const glyph = item.querySelector(ICON_SELECTOR);
11938
- if (glyph && content[iconKey]) {
11939
- applyIconMarkup(glyph, content[iconKey]);
11996
+ if (glyph) {
11997
+ applyIconMarkup(glyph, glyphStored);
11940
11998
  glyph.removeAttribute(SOCIALS_ICON_PLACEHOLDER_ATTR);
11941
11999
  }
11942
- const label = item.querySelector(`[${SOCIALS_LABEL_ATTR}]`);
11943
- label?.setAttribute("data-ohw-key", socialLabelKey(iconKey));
11944
- const stored = content[socialLabelKey(iconKey)];
11945
- if (label && stored !== void 0) {
11946
- const words = isEmptyLabelValue(stored) ? "" : stored;
11947
- if (label.innerHTML !== words) label.innerHTML = words;
11948
- }
12000
+ }
12001
+ const label = socialLabelElement(item);
12002
+ const labelKey = label?.getAttribute("data-ohw-key");
12003
+ if (label && labelKey && content[labelKey] !== void 0) {
12004
+ const words = isEmptyLabelValue(content[labelKey]) ? "" : content[labelKey];
12005
+ if (label.innerHTML !== words) label.innerHTML = words;
11949
12006
  }
11950
12007
  }
11951
12008
  function socialPlatformKey(iconKey) {
@@ -12034,6 +12091,9 @@ function insertSocialItem(row, after, content = {}, { placeholder = true, keys }
12034
12091
  if (placeholder) {
12035
12092
  const label = socialLabelElement(item);
12036
12093
  if (label) label.textContent = PLACEHOLDER_SOCIAL_LABEL;
12094
+ } else if (keys) {
12095
+ const label = socialLabelElement(item);
12096
+ if (label) label.textContent = "";
12037
12097
  }
12038
12098
  applySocialsDisplayToRow(row, display);
12039
12099
  return { item, hrefKey, iconKey, order: getSocialsOrderFromDom(row.ownerDocument) };
@@ -13896,8 +13956,8 @@ var lockFooterDuringDrag = lockItemDuringDrag;
13896
13956
  var unlockFooterDragInteraction = unlockItemDragInteraction;
13897
13957
 
13898
13958
  // src/lib/nav-dnd.ts
13899
- function listReorderableNavItems() {
13900
- return listNavbarItems().filter((el) => {
13959
+ function listReorderableNavItems(draggedEl) {
13960
+ return listActiveNavbarItems(draggedEl).filter((el) => {
13901
13961
  const key = el.getAttribute("data-ohw-href-key");
13902
13962
  return isNavbarHrefKey(key) && !isNestedNavChild(el);
13903
13963
  });
@@ -13912,8 +13972,8 @@ function resolveParentNavHrefKey(child) {
13912
13972
  const key = trigger?.getAttribute("data-ohw-href-key");
13913
13973
  return key && isNavbarHrefKey(key) ? key : null;
13914
13974
  }
13915
- function listNavChildren(parentHrefKey) {
13916
- const items = listNavbarItems();
13975
+ function listNavChildren(parentHrefKey, draggedEl) {
13976
+ const items = listActiveNavbarItems(draggedEl);
13917
13977
  const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
13918
13978
  if (!parent) return [];
13919
13979
  const group = parent.closest("[data-ohw-nav-group]");
@@ -13923,20 +13983,83 @@ function listNavChildren(parentHrefKey) {
13923
13983
  (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
13924
13984
  );
13925
13985
  }
13926
- function activeNavListContainer() {
13927
- const desktop = getNavbarDesktopContainer();
13928
- if (desktop && desktop.getClientRects().length > 0) {
13929
- const style = window.getComputedStyle(desktop);
13930
- if (style.display !== "none" && style.visibility !== "hidden") return desktop;
13986
+ function isVerticalNavLayout(items, draggedEl) {
13987
+ const visible = items.filter((el) => el.getClientRects().length > 0);
13988
+ if (visible.length >= 2) {
13989
+ const a = visible[0].getBoundingClientRect();
13990
+ const b = visible[1].getBoundingClientRect();
13991
+ const verticalGap = b.top - a.bottom;
13992
+ const horizontalGap = b.left - a.right;
13993
+ return verticalGap > horizontalGap;
13994
+ }
13995
+ const container = getActiveNavbarListContainer(draggedEl);
13996
+ if (!container) return false;
13997
+ const style = window.getComputedStyle(container);
13998
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
13999
+ }
14000
+ function buildVerticalRootNavDropSlots(items, draggedEl) {
14001
+ const slots = [];
14002
+ const barThickness = 3;
14003
+ if (items.length === 0) {
14004
+ const container = getActiveNavbarListContainer(draggedEl);
14005
+ if (!container) return slots;
14006
+ const rect = container.getBoundingClientRect();
14007
+ slots.push({
14008
+ insertIndex: 0,
14009
+ parentId: null,
14010
+ left: rect.left,
14011
+ top: rect.top + rect.height / 2 - barThickness / 2,
14012
+ width: Math.max(rect.width, 40),
14013
+ height: barThickness,
14014
+ direction: "horizontal"
14015
+ });
14016
+ return slots;
13931
14017
  }
13932
- return getNavbarDrawerContainer();
14018
+ const edgeGap = (() => {
14019
+ if (items.length < 2) return 16;
14020
+ const a = items[0].getBoundingClientRect();
14021
+ const b = items[1].getBoundingClientRect();
14022
+ return Math.max(0, b.top - a.bottom);
14023
+ })();
14024
+ for (let i = 0; i <= items.length; i++) {
14025
+ let top;
14026
+ let width;
14027
+ let left;
14028
+ if (i === 0) {
14029
+ const first = items[0].getBoundingClientRect();
14030
+ top = first.top - edgeGap / 2 - barThickness / 2;
14031
+ left = first.left;
14032
+ width = first.width;
14033
+ } else if (i === items.length) {
14034
+ const last = items[items.length - 1].getBoundingClientRect();
14035
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
14036
+ top = last.bottom + lastGap / 2 - barThickness / 2;
14037
+ left = last.left;
14038
+ width = last.width;
14039
+ } else {
14040
+ const prev = items[i - 1].getBoundingClientRect();
14041
+ const next = items[i].getBoundingClientRect();
14042
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
14043
+ left = Math.min(prev.left, next.left);
14044
+ width = Math.max(prev.right, next.right) - left;
14045
+ }
14046
+ slots.push({
14047
+ insertIndex: i,
14048
+ parentId: null,
14049
+ left,
14050
+ top,
14051
+ width: Math.max(width, 40),
14052
+ height: barThickness,
14053
+ direction: "horizontal"
14054
+ });
14055
+ }
14056
+ return slots;
13933
14057
  }
13934
- function buildRootNavDropSlots() {
13935
- const items = listReorderableNavItems();
14058
+ function buildHorizontalRootNavDropSlots(items, draggedEl) {
13936
14059
  const slots = [];
13937
14060
  const barThickness = 3;
13938
14061
  if (items.length === 0) {
13939
- const container = activeNavListContainer();
14062
+ const container = getActiveNavbarListContainer(draggedEl);
13940
14063
  if (!container) return slots;
13941
14064
  const rect = container.getBoundingClientRect();
13942
14065
  slots.push({
@@ -13967,10 +14090,7 @@ function buildRootNavDropSlots() {
13967
14090
  height = first.height;
13968
14091
  } else if (i === items.length) {
13969
14092
  const last = items[items.length - 1].getBoundingClientRect();
13970
- const lastGap = items.length >= 2 ? Math.max(
13971
- 0,
13972
- last.left - items[items.length - 2].getBoundingClientRect().right
13973
- ) : edgeGap;
14093
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
13974
14094
  left = last.right + lastGap / 2 - barThickness / 2;
13975
14095
  top = last.top;
13976
14096
  height = last.height;
@@ -13993,8 +14113,15 @@ function buildRootNavDropSlots() {
13993
14113
  }
13994
14114
  return slots;
13995
14115
  }
13996
- function buildChildNavDropSlots(parentHrefKey) {
13997
- const children = listNavChildren(parentHrefKey);
14116
+ function buildRootNavDropSlots(draggedEl) {
14117
+ const items = listReorderableNavItems(draggedEl);
14118
+ if (isVerticalNavLayout(items, draggedEl)) {
14119
+ return buildVerticalRootNavDropSlots(items, draggedEl);
14120
+ }
14121
+ return buildHorizontalRootNavDropSlots(items, draggedEl);
14122
+ }
14123
+ function buildChildNavDropSlots(parentHrefKey, draggedEl) {
14124
+ const children = listNavChildren(parentHrefKey, draggedEl);
13998
14125
  const slots = [];
13999
14126
  const barThickness = 3;
14000
14127
  if (children.length === 0) return slots;
@@ -14015,10 +14142,7 @@ function buildChildNavDropSlots(parentHrefKey) {
14015
14142
  width = first.width;
14016
14143
  } else if (i === children.length) {
14017
14144
  const last = children[children.length - 1].getBoundingClientRect();
14018
- const lastGap = children.length >= 2 ? Math.max(
14019
- 0,
14020
- last.top - children[children.length - 2].getBoundingClientRect().bottom
14021
- ) : edgeGap;
14145
+ const lastGap = children.length >= 2 ? Math.max(0, last.top - children[children.length - 2].getBoundingClientRect().bottom) : edgeGap;
14022
14146
  top = last.bottom + lastGap / 2 - barThickness / 2;
14023
14147
  left = last.left;
14024
14148
  width = last.width;
@@ -14041,18 +14165,19 @@ function buildChildNavDropSlots(parentHrefKey) {
14041
14165
  }
14042
14166
  return slots;
14043
14167
  }
14044
- function buildAllNavDropSlots(draggedHrefKey) {
14045
- const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
14046
- if (!dragged) return buildRootNavDropSlots();
14168
+ function buildAllNavDropSlots(draggedHrefKey, draggedEl) {
14169
+ const activeItems = listActiveNavbarItems(draggedEl);
14170
+ const dragged = (draggedEl && activeItems.includes(draggedEl) ? draggedEl : null) ?? activeItems.find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey) ?? listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
14171
+ if (!dragged) return buildRootNavDropSlots(draggedEl);
14047
14172
  if (isNestedNavChild(dragged)) {
14048
14173
  const parentKey = resolveParentNavHrefKey(dragged);
14049
14174
  if (!parentKey) return [];
14050
- return buildChildNavDropSlots(parentKey);
14175
+ return buildChildNavDropSlots(parentKey, draggedEl);
14051
14176
  }
14052
- return buildRootNavDropSlots();
14177
+ return buildRootNavDropSlots(draggedEl);
14053
14178
  }
14054
- function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
14055
- const slots = buildAllNavDropSlots(draggedHrefKey);
14179
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey, draggedEl) {
14180
+ const slots = buildAllNavDropSlots(draggedHrefKey, draggedEl);
14056
14181
  let best = null;
14057
14182
  for (const slot of slots) {
14058
14183
  const cx2 = slot.left + slot.width / 2;
@@ -14103,6 +14228,12 @@ function useNavItemDrag({
14103
14228
  setIsItemDragging(false);
14104
14229
  if (keepOpenEl?.isConnected) {
14105
14230
  setNavGroupForceOpen(keepOpenEl, true);
14231
+ requestAnimationFrame(() => {
14232
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14233
+ requestAnimationFrame(() => {
14234
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14235
+ });
14236
+ });
14106
14237
  } else {
14107
14238
  setNavGroupForceOpen(null, false);
14108
14239
  }
@@ -14118,7 +14249,7 @@ function useNavItemDrag({
14118
14249
  }
14119
14250
  session.activeSlot = activeSlot;
14120
14251
  setSiblingHintRects([]);
14121
- const slots = buildAllNavDropSlots(session.hrefKey);
14252
+ const slots = buildAllNavDropSlots(session.hrefKey, session.draggedEl);
14122
14253
  setNavDropSlots(slots);
14123
14254
  const activeIdx = activeSlot ? slots.findIndex(
14124
14255
  (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
@@ -14154,7 +14285,12 @@ function useNavItemDrag({
14154
14285
  if (session.wasSelected && selectedElRef.current === session.draggedEl) {
14155
14286
  setToolbarRect(rect);
14156
14287
  }
14157
- const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
14288
+ const initialSlot = hitTestNavDropSlot(
14289
+ session.lastClientX,
14290
+ session.lastClientY,
14291
+ session.hrefKey,
14292
+ session.draggedEl
14293
+ );
14158
14294
  refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
14159
14295
  },
14160
14296
  [
@@ -14176,10 +14312,11 @@ function useNavItemDrag({
14176
14312
  }
14177
14313
  const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
14178
14314
  const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
14179
- const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
14315
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey, session.draggedEl);
14180
14316
  const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
14181
14317
  const wasSelected = session.wasSelected;
14182
14318
  const hrefKey = session.hrefKey;
14319
+ const keepDropdownOpen = Boolean(session.draggedEl.closest("[data-ohw-nav-children]"));
14183
14320
  const applySelectionAfterDrop = () => {
14184
14321
  if (!wasSelected) {
14185
14322
  deselectRef.current();
@@ -14213,6 +14350,14 @@ function useNavItemDrag({
14213
14350
  });
14214
14351
  applySelectionAfterDrop();
14215
14352
  clearNavDragVisuals();
14353
+ if (keepDropdownOpen) {
14354
+ requestAnimationFrame(() => {
14355
+ const desktop = document.querySelector("[data-ohw-nav-container]");
14356
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
14357
+ const link = desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
14358
+ if (link) setNavGroupForceOpen(link, true);
14359
+ });
14360
+ }
14216
14361
  requestAnimationFrame(() => {
14217
14362
  if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
14218
14363
  applyNavForest(planned.forest);
@@ -14252,7 +14397,7 @@ function useNavItemDrag({
14252
14397
  if (!session) return false;
14253
14398
  e.preventDefault();
14254
14399
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
14255
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14400
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14256
14401
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14257
14402
  return true;
14258
14403
  },
@@ -14298,7 +14443,7 @@ function useNavItemDrag({
14298
14443
  clearTextSelection();
14299
14444
  const session = navDragRef.current;
14300
14445
  if (!session) return;
14301
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14446
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14302
14447
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14303
14448
  return;
14304
14449
  }
@@ -14314,7 +14459,8 @@ function useNavItemDrag({
14314
14459
  } catch {
14315
14460
  }
14316
14461
  if (linkPopoverOpenRef.current) setLinkPopover(null);
14317
- if (activeElRef.current) deactivateRef.current();
14462
+ const isNestedDrag = Boolean(pending.el.closest("[data-ohw-nav-children]"));
14463
+ if (activeElRef.current && !isNestedDrag) deactivateRef.current();
14318
14464
  const key = pending.el.getAttribute("data-ohw-href-key");
14319
14465
  if (!key) return;
14320
14466
  beginNavDragRef.current({
@@ -14325,6 +14471,9 @@ function useNavItemDrag({
14325
14471
  lastClientY: e.clientY,
14326
14472
  activeSlot: null
14327
14473
  });
14474
+ if (activeElRef.current && isNestedDrag) {
14475
+ deactivateRef.current();
14476
+ }
14328
14477
  };
14329
14478
  const endPointerDrag = (e) => {
14330
14479
  const pending = navPointerDragRef.current;
@@ -14402,6 +14551,7 @@ function useNavItemDrag({
14402
14551
  );
14403
14552
  return {
14404
14553
  navDragRef,
14554
+ navPointerDragRef,
14405
14555
  navDropSlots,
14406
14556
  activeNavDropIndex,
14407
14557
  startNavLinkDrag,
@@ -15800,6 +15950,7 @@ function isNavDropdownPanelOpen(childrenRoot) {
15800
15950
  if (childrenRoot.closest("[data-ohw-nav-drawer]")) return true;
15801
15951
  const group = childrenRoot.closest("[data-ohw-nav-group]");
15802
15952
  if (group?.hasAttribute("data-ohw-nav-force-open")) return true;
15953
+ if (navDropdownsOpenOnClick()) return false;
15803
15954
  if (childrenRoot.clientHeight < 2 || childrenRoot.clientWidth < 2) return false;
15804
15955
  const style = window.getComputedStyle(childrenRoot);
15805
15956
  if (style.display === "none" || style.visibility === "hidden") return false;
@@ -15843,6 +15994,11 @@ function getNavigationItemAnchor(el) {
15843
15994
  function isNavigationItem2(el) {
15844
15995
  return getNavigationItemAnchor(el) !== null;
15845
15996
  }
15997
+ function isNavFooterScopedNavigationItem(el) {
15998
+ return Boolean(
15999
+ el.closest("nav, footer, [data-ohw-nav-container], [data-ohw-nav-drawer]")
16000
+ );
16001
+ }
15846
16002
  function socialWordsClicked(target, item) {
15847
16003
  const text = target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
15848
16004
  return Boolean(text && item.contains(text));
@@ -16053,6 +16209,7 @@ function getNavigationSelectionParent(el) {
16053
16209
  }
16054
16210
  function collectNavigationItemSiblingHintRects(selected) {
16055
16211
  if (!isNavigationItem2(selected)) return [];
16212
+ if (!isNavFooterScopedNavigationItem(selected)) return [];
16056
16213
  const socialsRow = findSocialsRow(selected);
16057
16214
  if (socialsRow) {
16058
16215
  return listSocialItems(socialsRow).filter((item) => item !== selected).map((item) => item.getBoundingClientRect());
@@ -17224,6 +17381,7 @@ function OhhwellsBridge() {
17224
17381
  linkPopoverSessionRef.current = linkPopover;
17225
17382
  const {
17226
17383
  navDragRef,
17384
+ navPointerDragRef,
17227
17385
  navDropSlots,
17228
17386
  activeNavDropIndex,
17229
17387
  startNavLinkDrag,
@@ -17426,7 +17584,12 @@ function OhhwellsBridge() {
17426
17584
  if (owner) persistFieldsRef.current(owner);
17427
17585
  }
17428
17586
  activeElRef.current = null;
17429
- setNavGroupForceOpen(null, false);
17587
+ const preserveNavDropdown = Boolean(navPointerDragRef.current?.el?.closest("[data-ohw-nav-children]")) || Boolean(navDragRef.current?.draggedEl?.closest("[data-ohw-nav-children]")) || Boolean(
17588
+ selectedElRef.current?.closest("[data-ohw-nav-children]") && selectedElRef.current?.closest("[data-ohw-nav-group]")?.hasAttribute("data-ohw-nav-force-open")
17589
+ );
17590
+ if (!preserveNavDropdown) {
17591
+ setNavGroupForceOpen(null, false);
17592
+ }
17430
17593
  setReorderHrefKey(null);
17431
17594
  setReorderDragDisabled(false);
17432
17595
  if (!selectedElRef.current) {
@@ -17548,7 +17711,10 @@ function OhhwellsBridge() {
17548
17711
  setSelectedIsSocialsRow(false);
17549
17712
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
17550
17713
  if (isNestedNavChild(navAnchor)) {
17551
- setNavGroupForceOpen(navAnchor, true);
17714
+ const group = navAnchor.closest("[data-ohw-nav-group]");
17715
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
17716
+ setNavGroupForceOpen(navAnchor, true);
17717
+ }
17552
17718
  setNavDropdownPreviewOpen(null);
17553
17719
  } else if (isDropdownTrigger) {
17554
17720
  const group = navAnchor.closest("[data-ohw-nav-group]");
@@ -17630,6 +17796,10 @@ function OhhwellsBridge() {
17630
17796
  const handleNavDropdownOpenChange = useCallback8((open) => {
17631
17797
  const selected = selectedElRef.current;
17632
17798
  if (!selected || !isNavigationItem2(selected)) return;
17799
+ if (isNestedNavChild(selected)) return;
17800
+ const group = selected.closest("[data-ohw-nav-group]");
17801
+ const domOpen = Boolean(group?.hasAttribute("data-ohw-nav-force-open"));
17802
+ if (open === domOpen) return;
17633
17803
  setNavGroupForceOpen(selected, open);
17634
17804
  setNavDropdownPreviewOpen(open);
17635
17805
  requestAnimationFrame(() => {
@@ -18159,7 +18329,10 @@ function OhhwellsBridge() {
18159
18329
  clearHrefKeyHover(anchor);
18160
18330
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
18161
18331
  if (isNestedNavChild(anchor)) {
18162
- setNavGroupForceOpen(anchor, true);
18332
+ const group = anchor.closest("[data-ohw-nav-group]");
18333
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
18334
+ setNavGroupForceOpen(anchor, true);
18335
+ }
18163
18336
  setNavDropdownPreviewOpen(null);
18164
18337
  } else if (isDropdownTrigger) {
18165
18338
  setNavGroupForceOpen(null, false);
@@ -19001,28 +19174,19 @@ function OhhwellsBridge() {
19001
19174
  return;
19002
19175
  }
19003
19176
  const existing = editStylesRef.current;
19004
- let initialVh = window.innerHeight;
19005
- if (existing?.base.textContent) {
19006
- const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
19007
- if (match) initialVh = parseInt(match[1], 10);
19008
- }
19177
+ const canvasHeight = "var(--ohw-canvas-h, 852px)";
19009
19178
  const baseCss = `
19010
19179
  html { height: auto !important; }
19011
19180
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
19012
- .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
19013
- .h-screen, .h-svh, .h-dvh { height: ${initialVh}px !important; }
19014
- [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19015
- [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19016
- [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19017
- /* A section written as min-height: var(--ohw-canvas-h, 100svh) \u2014 the documented way to
19018
- build a full-viewport section that still grows for its own content \u2014 matches the three
19019
- rules above on substring alone, since the fallback text contains "100svh" too. Forcing
19020
- a literal height on top of that turns "at least one screen" into "exactly one screen",
19021
- so content taller than one screen (a long mobile hero, say) overflows a centered flex
19022
- column upward, under whatever sits above it. Only the min-height half belongs to it. */
19023
- [style*="min-height"][style*="100vh"],
19024
- [style*="min-height"][style*="100svh"],
19025
- [style*="min-height"][style*="100dvh"] { height: auto !important; }
19181
+ .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${canvasHeight} !important; }
19182
+ .h-screen, .h-svh, .h-dvh { height: ${canvasHeight} !important; }
19183
+ /* Literal inline viewport units only \u2014 skip R11 sections that already reference --ohw-canvas-h
19184
+ (their style attribute contains "100svh" only as a var() fallback string). */
19185
+ [style*="100vh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19186
+ [style*="100svh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19187
+ [style*="100dvh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19188
+ /* R11: min-height via --ohw-canvas-h; height must grow with content taller than one screen. */
19189
+ [style*="min-height"][style*="--ohw-canvas-h"] { height: auto !important; }
19026
19190
  /* Emptied text keeps somewhere to click. A label typed down to nothing collapses to a
19027
19191
  couple of pixels, and getting back into it meant hunting for the caret with the mouse.
19028
19192
  Edit mode only \u2014 the published page shows nothing where there is nothing (OHH-736). */
@@ -19169,6 +19333,10 @@ function OhhwellsBridge() {
19169
19333
  outline-offset: 0 !important;
19170
19334
  box-shadow: none !important;
19171
19335
  }
19336
+ /* Open nav dropdown panels paint above AI section selection chrome (2147483000). */
19337
+ [data-ohw-nav-group][data-ohw-nav-force-open] > [data-ohw-nav-children] {
19338
+ z-index: 2147483100 !important;
19339
+ }
19172
19340
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
19173
19341
  [data-ohw-editing],
19174
19342
  [data-ohw-editing] *,
@@ -20507,7 +20675,11 @@ function OhhwellsBridge() {
20507
20675
  const slotFromItem = hrefKey ? findSocialByHrefKey(hrefKey)?.querySelector('[data-ohw-editable="icon"]') : null;
20508
20676
  const requestedIsIconSlot = Boolean(requestedIconKey) && Boolean(document.querySelector(`[data-ohw-key="${requestedIconKey}"][data-ohw-editable="icon"]`));
20509
20677
  const requestedIsFree = Boolean(requestedIconKey) && !document.querySelector(`[data-ohw-key="${requestedIconKey}"]`);
20510
- const iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20678
+ let iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20679
+ const item = hrefKey ? findSocialByHrefKey(hrefKey) : null;
20680
+ if (item && typeof iconMarkup === "string" && iconMarkup) {
20681
+ iconKey = ensureIconSlot(item) ?? iconKey;
20682
+ }
20511
20683
  if (iconKey && typeof iconMarkup === "string" && iconMarkup) {
20512
20684
  document.querySelectorAll(`[data-ohw-key="${iconKey}"][data-ohw-editable="icon"]`).forEach((el) => {
20513
20685
  applyIconMarkup(el, iconMarkup);
@@ -20517,7 +20689,7 @@ function OhhwellsBridge() {
20517
20689
  }
20518
20690
  if (iconKey && platformId) nodes.push({ key: socialPlatformKey(iconKey), text: platformId });
20519
20691
  if (iconKey && label) {
20520
- const labelKey = socialLabelKey(iconKey);
20692
+ const labelKey = (item ? socialLabelElement(item)?.getAttribute("data-ohw-key") : null) ?? socialLabelKey(iconKey);
20521
20693
  document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
20522
20694
  if (keepLabel && el.textContent?.trim()) return;
20523
20695
  el.textContent = label;
@@ -20787,6 +20959,9 @@ function OhhwellsBridge() {
20787
20959
  if (reapNodes.length > 0) postToParentRef.current({ type: "ow:change", nodes: reapNodes });
20788
20960
  const hydratedHeight = document.body.scrollHeight;
20789
20961
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
20962
+ if (parseAiSectionsState(aiSectionsRef.current).sections.length > 0) {
20963
+ postAiSectionsChanged();
20964
+ }
20790
20965
  postToParentRef.current({ type: "ow:hydrate-done" });
20791
20966
  };
20792
20967
  const handleUpdateLogoIdentity = (e) => {
@@ -21333,7 +21508,12 @@ function OhhwellsBridge() {
21333
21508
  }
21334
21509
  if (navDragRef.current) {
21335
21510
  const session = navDragRef.current;
21336
- const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
21511
+ const slot = hitTestNavDropSlot(
21512
+ session.lastClientX,
21513
+ session.lastClientY,
21514
+ session.hrefKey,
21515
+ session.draggedEl
21516
+ );
21337
21517
  refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
21338
21518
  }
21339
21519
  if (hoveredImageRef.current) {
@@ -21535,18 +21715,21 @@ function OhhwellsBridge() {
21535
21715
  }
21536
21716
  return null;
21537
21717
  };
21718
+ const isPointInRect = (el, clientX, clientY) => {
21719
+ const r2 = el.getBoundingClientRect();
21720
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
21721
+ };
21538
21722
  const isPointOverEditable = (scope, clientX, clientY) => {
21539
21723
  const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
21540
21724
  for (const el of editables) {
21541
- const r2 = el.getBoundingClientRect();
21542
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
21725
+ if (isPointInRect(el, clientX, clientY)) return true;
21543
21726
  }
21544
21727
  return false;
21545
21728
  };
21546
21729
  const handleCarouselHover = (e) => {
21547
21730
  const container = findCarouselAtPoint(e.clientX, e.clientY);
21548
21731
  const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
21549
- if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
21732
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY) || isPointOverNavigation(e.clientX, e.clientY) || isPointOverBridgeChrome(e.clientX, e.clientY)) {
21550
21733
  setCarouselHover((prev) => prev ? null : prev);
21551
21734
  return;
21552
21735
  }
@@ -21995,7 +22178,7 @@ function OhhwellsBridge() {
21995
22178
  postToParent2({
21996
22179
  type: "ow:ready",
21997
22180
  version: "1",
21998
- bridgeVersion: "0.1.98",
22181
+ bridgeVersion: "0.1.100",
21999
22182
  path: pathname,
22000
22183
  nodes: collectEditableNodes(editContentRef.current),
22001
22184
  sections