@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.cjs CHANGED
@@ -2620,6 +2620,10 @@ var REPLACED_ATTR = "data-ohw-ai-replaced-by";
2620
2620
  var REMOVED_ATTR2 = "data-ohw-ai-removed";
2621
2621
  var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
2622
2622
  var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
2623
+ function isChromeSection2(el) {
2624
+ if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) return true;
2625
+ return el.tagName === "HEADER" || el.tagName === "FOOTER";
2626
+ }
2623
2627
  function readRootVar(name) {
2624
2628
  if (typeof document === "undefined") return "";
2625
2629
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
@@ -2671,7 +2675,10 @@ function deriveTemplateButtonStyle() {
2671
2675
  ].map((v) => v || "0px");
2672
2676
  const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
2673
2677
  const px = (v) => parseFloat(v) || 0;
2674
- const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom));
2678
+ const lineHeight = px(cs.lineHeight) || px(cs.fontSize) * 1.2;
2679
+ const contentH = btn.getBoundingClientRect().height - px(cs.borderTopWidth) - px(cs.borderBottomWidth);
2680
+ const impliedY = Math.round(Math.max(0, (contentH - lineHeight) / 2));
2681
+ const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom), impliedY);
2675
2682
  const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
2676
2683
  return {
2677
2684
  radius: radius || "10px",
@@ -2697,6 +2704,19 @@ function findPlacementAnchor(id, exclude) {
2697
2704
  for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
2698
2705
  if (el === exclude) continue;
2699
2706
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
2707
+ if (isChromeSection2(el)) return null;
2708
+ return el;
2709
+ }
2710
+ return null;
2711
+ }
2712
+ function findFooterSection() {
2713
+ const byId = findTemplateSection("footer");
2714
+ if (byId) return byId;
2715
+ for (const el of Array.from(
2716
+ document.querySelectorAll("footer[data-ohw-section]")
2717
+ ).reverse()) {
2718
+ if (el.hasAttribute(CONTAINER_ATTR)) continue;
2719
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
2700
2720
  return el;
2701
2721
  }
2702
2722
  return null;
@@ -2723,7 +2743,7 @@ function placeContainer(container, entry) {
2723
2743
  return;
2724
2744
  }
2725
2745
  }
2726
- const footer = findTemplateSection("footer");
2746
+ const footer = findFooterSection();
2727
2747
  if (footer) {
2728
2748
  footer.insertAdjacentElement("beforebegin", container);
2729
2749
  } else {
@@ -2758,7 +2778,7 @@ function syncTemplateHidden(state, pageHasSections) {
2758
2778
  if (!hide) return;
2759
2779
  for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
2760
2780
  if (el.hasAttribute(CONTAINER_ATTR)) continue;
2761
- if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
2781
+ if (isChromeSection2(el)) continue;
2762
2782
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
2763
2783
  if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR2)) continue;
2764
2784
  el.style.display = "none";
@@ -7480,9 +7500,7 @@ function ItemActionToolbar({
7480
7500
  ToggleGroup,
7481
7501
  {
7482
7502
  value: dropdownOpen ? "open" : "closed",
7483
- onValueChange: (value) => {
7484
- if (value !== "open" && value !== "closed") return;
7485
- onDropdownOpenChange?.(value === "open");
7503
+ onValueChange: () => {
7486
7504
  },
7487
7505
  className: "h-7 gap-0.5 rounded-[calc(var(--radius,0.5rem)-2px)] bg-muted p-0.5",
7488
7506
  onMouseDown: (e) => {
@@ -7497,6 +7515,11 @@ function ItemActionToolbar({
7497
7515
  size: "sm",
7498
7516
  "aria-label": "Closed",
7499
7517
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7518
+ onMouseDown: (e) => {
7519
+ e.preventDefault();
7520
+ e.stopPropagation();
7521
+ if (dropdownOpen !== false) onDropdownOpenChange?.(false);
7522
+ },
7500
7523
  children: "Closed"
7501
7524
  }
7502
7525
  ),
@@ -7507,6 +7530,11 @@ function ItemActionToolbar({
7507
7530
  size: "sm",
7508
7531
  "aria-label": "Open",
7509
7532
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7533
+ onMouseDown: (e) => {
7534
+ e.preventDefault();
7535
+ e.stopPropagation();
7536
+ if (dropdownOpen !== true) onDropdownOpenChange?.(true);
7537
+ },
7510
7538
  children: "Open"
7511
7539
  }
7512
7540
  )
@@ -8532,7 +8560,7 @@ function FieldTypePicker({ onPick }) {
8532
8560
  "div",
8533
8561
  {
8534
8562
  "data-ohw-field-type-picker": "",
8535
- className: "pointer-events-auto grid w-[420px] grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
8563
+ className: "pointer-events-auto grid w-[280px] grid-cols-2 gap-2 rounded-xl border border-border bg-background p-3 shadow-lg",
8536
8564
  children: FIELD_TYPES.map((entry) => {
8537
8565
  const Icon = TYPE_ICONS[entry.type];
8538
8566
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
@@ -8540,7 +8568,7 @@ function FieldTypePicker({ onPick }) {
8540
8568
  {
8541
8569
  type: "button",
8542
8570
  onClick: () => onPick(entry.type),
8543
- 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",
8571
+ 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",
8544
8572
  children: [
8545
8573
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8546
8574
  entry.label
@@ -10893,6 +10921,32 @@ function getNavbarDesktopContainer() {
10893
10921
  function getNavbarDrawerContainer() {
10894
10922
  return document.querySelector("[data-ohw-nav-drawer]");
10895
10923
  }
10924
+ function isNavbarListContainerVisible(el) {
10925
+ if (el.hasAttribute("hidden")) return false;
10926
+ if (el.getClientRects().length === 0) return false;
10927
+ const style = window.getComputedStyle(el);
10928
+ return style.display !== "none" && style.visibility !== "hidden";
10929
+ }
10930
+ function getActiveNavbarListContainer(draggedEl) {
10931
+ const desktop = getNavbarDesktopContainer();
10932
+ const drawer = getNavbarDrawerContainer();
10933
+ if (draggedEl) {
10934
+ if (drawer?.contains(draggedEl)) return drawer;
10935
+ if (desktop?.contains(draggedEl)) return desktop;
10936
+ }
10937
+ if (desktop && isNavbarListContainerVisible(desktop)) return desktop;
10938
+ if (drawer && isNavbarListContainerVisible(drawer)) return drawer;
10939
+ return desktop ?? drawer;
10940
+ }
10941
+ function listActiveNavbarItems(draggedEl) {
10942
+ const container = getActiveNavbarListContainer(draggedEl);
10943
+ if (container) {
10944
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(
10945
+ isNavbarLinkItem
10946
+ );
10947
+ }
10948
+ return listNavbarItems();
10949
+ }
10896
10950
  function listNavbarItems() {
10897
10951
  const desktop = getNavbarDesktopContainer();
10898
10952
  if (desktop) {
@@ -12005,20 +12059,23 @@ function isEmptyLabelValue(value) {
12005
12059
  function applyStoredValues(item, content) {
12006
12060
  const hrefKey = socialHrefKey(item);
12007
12061
  const iconKey = socialIconKey(item);
12062
+ const baseKey = hrefKey?.replace(/-href$/, "");
12008
12063
  if (hrefKey && content[hrefKey] !== void 0) item.setAttribute("href", content[hrefKey]);
12009
- if (iconKey) {
12064
+ const glyphKey = iconKey ?? baseKey ?? null;
12065
+ const glyphStored = glyphKey ? content[glyphKey]?.trim() : void 0;
12066
+ if (glyphStored) {
12067
+ ensureIconSlot(item);
12010
12068
  const glyph = item.querySelector(ICON_SELECTOR);
12011
- if (glyph && content[iconKey]) {
12012
- applyIconMarkup(glyph, content[iconKey]);
12069
+ if (glyph) {
12070
+ applyIconMarkup(glyph, glyphStored);
12013
12071
  glyph.removeAttribute(SOCIALS_ICON_PLACEHOLDER_ATTR);
12014
12072
  }
12015
- const label = item.querySelector(`[${SOCIALS_LABEL_ATTR}]`);
12016
- label?.setAttribute("data-ohw-key", socialLabelKey(iconKey));
12017
- const stored = content[socialLabelKey(iconKey)];
12018
- if (label && stored !== void 0) {
12019
- const words = isEmptyLabelValue(stored) ? "" : stored;
12020
- if (label.innerHTML !== words) label.innerHTML = words;
12021
- }
12073
+ }
12074
+ const label = socialLabelElement(item);
12075
+ const labelKey = label?.getAttribute("data-ohw-key");
12076
+ if (label && labelKey && content[labelKey] !== void 0) {
12077
+ const words = isEmptyLabelValue(content[labelKey]) ? "" : content[labelKey];
12078
+ if (label.innerHTML !== words) label.innerHTML = words;
12022
12079
  }
12023
12080
  }
12024
12081
  function socialPlatformKey(iconKey) {
@@ -12107,6 +12164,9 @@ function insertSocialItem(row, after, content = {}, { placeholder = true, keys }
12107
12164
  if (placeholder) {
12108
12165
  const label = socialLabelElement(item);
12109
12166
  if (label) label.textContent = PLACEHOLDER_SOCIAL_LABEL;
12167
+ } else if (keys) {
12168
+ const label = socialLabelElement(item);
12169
+ if (label) label.textContent = "";
12110
12170
  }
12111
12171
  applySocialsDisplayToRow(row, display);
12112
12172
  return { item, hrefKey, iconKey, order: getSocialsOrderFromDom(row.ownerDocument) };
@@ -13963,8 +14023,8 @@ var lockFooterDuringDrag = lockItemDuringDrag;
13963
14023
  var unlockFooterDragInteraction = unlockItemDragInteraction;
13964
14024
 
13965
14025
  // src/lib/nav-dnd.ts
13966
- function listReorderableNavItems() {
13967
- return listNavbarItems().filter((el) => {
14026
+ function listReorderableNavItems(draggedEl) {
14027
+ return listActiveNavbarItems(draggedEl).filter((el) => {
13968
14028
  const key = el.getAttribute("data-ohw-href-key");
13969
14029
  return isNavbarHrefKey(key) && !isNestedNavChild(el);
13970
14030
  });
@@ -13979,8 +14039,8 @@ function resolveParentNavHrefKey(child) {
13979
14039
  const key = trigger?.getAttribute("data-ohw-href-key");
13980
14040
  return key && isNavbarHrefKey(key) ? key : null;
13981
14041
  }
13982
- function listNavChildren(parentHrefKey) {
13983
- const items = listNavbarItems();
14042
+ function listNavChildren(parentHrefKey, draggedEl) {
14043
+ const items = listActiveNavbarItems(draggedEl);
13984
14044
  const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
13985
14045
  if (!parent) return [];
13986
14046
  const group = parent.closest("[data-ohw-nav-group]");
@@ -13990,20 +14050,83 @@ function listNavChildren(parentHrefKey) {
13990
14050
  (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
13991
14051
  );
13992
14052
  }
13993
- function activeNavListContainer() {
13994
- const desktop = getNavbarDesktopContainer();
13995
- if (desktop && desktop.getClientRects().length > 0) {
13996
- const style = window.getComputedStyle(desktop);
13997
- if (style.display !== "none" && style.visibility !== "hidden") return desktop;
14053
+ function isVerticalNavLayout(items, draggedEl) {
14054
+ const visible = items.filter((el) => el.getClientRects().length > 0);
14055
+ if (visible.length >= 2) {
14056
+ const a = visible[0].getBoundingClientRect();
14057
+ const b = visible[1].getBoundingClientRect();
14058
+ const verticalGap = b.top - a.bottom;
14059
+ const horizontalGap = b.left - a.right;
14060
+ return verticalGap > horizontalGap;
14061
+ }
14062
+ const container = getActiveNavbarListContainer(draggedEl);
14063
+ if (!container) return false;
14064
+ const style = window.getComputedStyle(container);
14065
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
14066
+ }
14067
+ function buildVerticalRootNavDropSlots(items, draggedEl) {
14068
+ const slots = [];
14069
+ const barThickness = 3;
14070
+ if (items.length === 0) {
14071
+ const container = getActiveNavbarListContainer(draggedEl);
14072
+ if (!container) return slots;
14073
+ const rect = container.getBoundingClientRect();
14074
+ slots.push({
14075
+ insertIndex: 0,
14076
+ parentId: null,
14077
+ left: rect.left,
14078
+ top: rect.top + rect.height / 2 - barThickness / 2,
14079
+ width: Math.max(rect.width, 40),
14080
+ height: barThickness,
14081
+ direction: "horizontal"
14082
+ });
14083
+ return slots;
13998
14084
  }
13999
- return getNavbarDrawerContainer();
14085
+ const edgeGap = (() => {
14086
+ if (items.length < 2) return 16;
14087
+ const a = items[0].getBoundingClientRect();
14088
+ const b = items[1].getBoundingClientRect();
14089
+ return Math.max(0, b.top - a.bottom);
14090
+ })();
14091
+ for (let i = 0; i <= items.length; i++) {
14092
+ let top;
14093
+ let width;
14094
+ let left;
14095
+ if (i === 0) {
14096
+ const first = items[0].getBoundingClientRect();
14097
+ top = first.top - edgeGap / 2 - barThickness / 2;
14098
+ left = first.left;
14099
+ width = first.width;
14100
+ } else if (i === items.length) {
14101
+ const last = items[items.length - 1].getBoundingClientRect();
14102
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
14103
+ top = last.bottom + lastGap / 2 - barThickness / 2;
14104
+ left = last.left;
14105
+ width = last.width;
14106
+ } else {
14107
+ const prev = items[i - 1].getBoundingClientRect();
14108
+ const next = items[i].getBoundingClientRect();
14109
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
14110
+ left = Math.min(prev.left, next.left);
14111
+ width = Math.max(prev.right, next.right) - left;
14112
+ }
14113
+ slots.push({
14114
+ insertIndex: i,
14115
+ parentId: null,
14116
+ left,
14117
+ top,
14118
+ width: Math.max(width, 40),
14119
+ height: barThickness,
14120
+ direction: "horizontal"
14121
+ });
14122
+ }
14123
+ return slots;
14000
14124
  }
14001
- function buildRootNavDropSlots() {
14002
- const items = listReorderableNavItems();
14125
+ function buildHorizontalRootNavDropSlots(items, draggedEl) {
14003
14126
  const slots = [];
14004
14127
  const barThickness = 3;
14005
14128
  if (items.length === 0) {
14006
- const container = activeNavListContainer();
14129
+ const container = getActiveNavbarListContainer(draggedEl);
14007
14130
  if (!container) return slots;
14008
14131
  const rect = container.getBoundingClientRect();
14009
14132
  slots.push({
@@ -14034,10 +14157,7 @@ function buildRootNavDropSlots() {
14034
14157
  height = first.height;
14035
14158
  } else if (i === items.length) {
14036
14159
  const last = items[items.length - 1].getBoundingClientRect();
14037
- const lastGap = items.length >= 2 ? Math.max(
14038
- 0,
14039
- last.left - items[items.length - 2].getBoundingClientRect().right
14040
- ) : edgeGap;
14160
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
14041
14161
  left = last.right + lastGap / 2 - barThickness / 2;
14042
14162
  top = last.top;
14043
14163
  height = last.height;
@@ -14060,8 +14180,15 @@ function buildRootNavDropSlots() {
14060
14180
  }
14061
14181
  return slots;
14062
14182
  }
14063
- function buildChildNavDropSlots(parentHrefKey) {
14064
- const children = listNavChildren(parentHrefKey);
14183
+ function buildRootNavDropSlots(draggedEl) {
14184
+ const items = listReorderableNavItems(draggedEl);
14185
+ if (isVerticalNavLayout(items, draggedEl)) {
14186
+ return buildVerticalRootNavDropSlots(items, draggedEl);
14187
+ }
14188
+ return buildHorizontalRootNavDropSlots(items, draggedEl);
14189
+ }
14190
+ function buildChildNavDropSlots(parentHrefKey, draggedEl) {
14191
+ const children = listNavChildren(parentHrefKey, draggedEl);
14065
14192
  const slots = [];
14066
14193
  const barThickness = 3;
14067
14194
  if (children.length === 0) return slots;
@@ -14082,10 +14209,7 @@ function buildChildNavDropSlots(parentHrefKey) {
14082
14209
  width = first.width;
14083
14210
  } else if (i === children.length) {
14084
14211
  const last = children[children.length - 1].getBoundingClientRect();
14085
- const lastGap = children.length >= 2 ? Math.max(
14086
- 0,
14087
- last.top - children[children.length - 2].getBoundingClientRect().bottom
14088
- ) : edgeGap;
14212
+ const lastGap = children.length >= 2 ? Math.max(0, last.top - children[children.length - 2].getBoundingClientRect().bottom) : edgeGap;
14089
14213
  top = last.bottom + lastGap / 2 - barThickness / 2;
14090
14214
  left = last.left;
14091
14215
  width = last.width;
@@ -14108,18 +14232,19 @@ function buildChildNavDropSlots(parentHrefKey) {
14108
14232
  }
14109
14233
  return slots;
14110
14234
  }
14111
- function buildAllNavDropSlots(draggedHrefKey) {
14112
- const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
14113
- if (!dragged) return buildRootNavDropSlots();
14235
+ function buildAllNavDropSlots(draggedHrefKey, draggedEl) {
14236
+ const activeItems = listActiveNavbarItems(draggedEl);
14237
+ 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);
14238
+ if (!dragged) return buildRootNavDropSlots(draggedEl);
14114
14239
  if (isNestedNavChild(dragged)) {
14115
14240
  const parentKey = resolveParentNavHrefKey(dragged);
14116
14241
  if (!parentKey) return [];
14117
- return buildChildNavDropSlots(parentKey);
14242
+ return buildChildNavDropSlots(parentKey, draggedEl);
14118
14243
  }
14119
- return buildRootNavDropSlots();
14244
+ return buildRootNavDropSlots(draggedEl);
14120
14245
  }
14121
- function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
14122
- const slots = buildAllNavDropSlots(draggedHrefKey);
14246
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey, draggedEl) {
14247
+ const slots = buildAllNavDropSlots(draggedHrefKey, draggedEl);
14123
14248
  let best = null;
14124
14249
  for (const slot of slots) {
14125
14250
  const cx2 = slot.left + slot.width / 2;
@@ -14170,6 +14295,12 @@ function useNavItemDrag({
14170
14295
  setIsItemDragging(false);
14171
14296
  if (keepOpenEl?.isConnected) {
14172
14297
  setNavGroupForceOpen(keepOpenEl, true);
14298
+ requestAnimationFrame(() => {
14299
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14300
+ requestAnimationFrame(() => {
14301
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14302
+ });
14303
+ });
14173
14304
  } else {
14174
14305
  setNavGroupForceOpen(null, false);
14175
14306
  }
@@ -14185,7 +14316,7 @@ function useNavItemDrag({
14185
14316
  }
14186
14317
  session.activeSlot = activeSlot;
14187
14318
  setSiblingHintRects([]);
14188
- const slots = buildAllNavDropSlots(session.hrefKey);
14319
+ const slots = buildAllNavDropSlots(session.hrefKey, session.draggedEl);
14189
14320
  setNavDropSlots(slots);
14190
14321
  const activeIdx = activeSlot ? slots.findIndex(
14191
14322
  (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
@@ -14221,7 +14352,12 @@ function useNavItemDrag({
14221
14352
  if (session.wasSelected && selectedElRef.current === session.draggedEl) {
14222
14353
  setToolbarRect(rect);
14223
14354
  }
14224
- const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
14355
+ const initialSlot = hitTestNavDropSlot(
14356
+ session.lastClientX,
14357
+ session.lastClientY,
14358
+ session.hrefKey,
14359
+ session.draggedEl
14360
+ );
14225
14361
  refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
14226
14362
  },
14227
14363
  [
@@ -14243,10 +14379,11 @@ function useNavItemDrag({
14243
14379
  }
14244
14380
  const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
14245
14381
  const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
14246
- const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
14382
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey, session.draggedEl);
14247
14383
  const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
14248
14384
  const wasSelected = session.wasSelected;
14249
14385
  const hrefKey = session.hrefKey;
14386
+ const keepDropdownOpen = Boolean(session.draggedEl.closest("[data-ohw-nav-children]"));
14250
14387
  const applySelectionAfterDrop = () => {
14251
14388
  if (!wasSelected) {
14252
14389
  deselectRef.current();
@@ -14280,6 +14417,14 @@ function useNavItemDrag({
14280
14417
  });
14281
14418
  applySelectionAfterDrop();
14282
14419
  clearNavDragVisuals();
14420
+ if (keepDropdownOpen) {
14421
+ requestAnimationFrame(() => {
14422
+ const desktop = document.querySelector("[data-ohw-nav-container]");
14423
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
14424
+ 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)}"]`);
14425
+ if (link) setNavGroupForceOpen(link, true);
14426
+ });
14427
+ }
14283
14428
  requestAnimationFrame(() => {
14284
14429
  if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
14285
14430
  applyNavForest(planned.forest);
@@ -14319,7 +14464,7 @@ function useNavItemDrag({
14319
14464
  if (!session) return false;
14320
14465
  e.preventDefault();
14321
14466
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
14322
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14467
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14323
14468
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14324
14469
  return true;
14325
14470
  },
@@ -14365,7 +14510,7 @@ function useNavItemDrag({
14365
14510
  clearTextSelection();
14366
14511
  const session = navDragRef.current;
14367
14512
  if (!session) return;
14368
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14513
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14369
14514
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14370
14515
  return;
14371
14516
  }
@@ -14381,7 +14526,8 @@ function useNavItemDrag({
14381
14526
  } catch {
14382
14527
  }
14383
14528
  if (linkPopoverOpenRef.current) setLinkPopover(null);
14384
- if (activeElRef.current) deactivateRef.current();
14529
+ const isNestedDrag = Boolean(pending.el.closest("[data-ohw-nav-children]"));
14530
+ if (activeElRef.current && !isNestedDrag) deactivateRef.current();
14385
14531
  const key = pending.el.getAttribute("data-ohw-href-key");
14386
14532
  if (!key) return;
14387
14533
  beginNavDragRef.current({
@@ -14392,6 +14538,9 @@ function useNavItemDrag({
14392
14538
  lastClientY: e.clientY,
14393
14539
  activeSlot: null
14394
14540
  });
14541
+ if (activeElRef.current && isNestedDrag) {
14542
+ deactivateRef.current();
14543
+ }
14395
14544
  };
14396
14545
  const endPointerDrag = (e) => {
14397
14546
  const pending = navPointerDragRef.current;
@@ -14469,6 +14618,7 @@ function useNavItemDrag({
14469
14618
  );
14470
14619
  return {
14471
14620
  navDragRef,
14621
+ navPointerDragRef,
14472
14622
  navDropSlots,
14473
14623
  activeNavDropIndex,
14474
14624
  startNavLinkDrag,
@@ -15867,6 +16017,7 @@ function isNavDropdownPanelOpen(childrenRoot) {
15867
16017
  if (childrenRoot.closest("[data-ohw-nav-drawer]")) return true;
15868
16018
  const group = childrenRoot.closest("[data-ohw-nav-group]");
15869
16019
  if (group?.hasAttribute("data-ohw-nav-force-open")) return true;
16020
+ if (navDropdownsOpenOnClick()) return false;
15870
16021
  if (childrenRoot.clientHeight < 2 || childrenRoot.clientWidth < 2) return false;
15871
16022
  const style = window.getComputedStyle(childrenRoot);
15872
16023
  if (style.display === "none" || style.visibility === "hidden") return false;
@@ -15910,6 +16061,11 @@ function getNavigationItemAnchor(el) {
15910
16061
  function isNavigationItem2(el) {
15911
16062
  return getNavigationItemAnchor(el) !== null;
15912
16063
  }
16064
+ function isNavFooterScopedNavigationItem(el) {
16065
+ return Boolean(
16066
+ el.closest("nav, footer, [data-ohw-nav-container], [data-ohw-nav-drawer]")
16067
+ );
16068
+ }
15913
16069
  function socialWordsClicked(target, item) {
15914
16070
  const text = target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
15915
16071
  return Boolean(text && item.contains(text));
@@ -16120,6 +16276,7 @@ function getNavigationSelectionParent(el) {
16120
16276
  }
16121
16277
  function collectNavigationItemSiblingHintRects(selected) {
16122
16278
  if (!isNavigationItem2(selected)) return [];
16279
+ if (!isNavFooterScopedNavigationItem(selected)) return [];
16123
16280
  const socialsRow = findSocialsRow(selected);
16124
16281
  if (socialsRow) {
16125
16282
  return listSocialItems(socialsRow).filter((item) => item !== selected).map((item) => item.getBoundingClientRect());
@@ -17291,6 +17448,7 @@ function OhhwellsBridge() {
17291
17448
  linkPopoverSessionRef.current = linkPopover;
17292
17449
  const {
17293
17450
  navDragRef,
17451
+ navPointerDragRef,
17294
17452
  navDropSlots,
17295
17453
  activeNavDropIndex,
17296
17454
  startNavLinkDrag,
@@ -17493,7 +17651,12 @@ function OhhwellsBridge() {
17493
17651
  if (owner) persistFieldsRef.current(owner);
17494
17652
  }
17495
17653
  activeElRef.current = null;
17496
- setNavGroupForceOpen(null, false);
17654
+ const preserveNavDropdown = Boolean(navPointerDragRef.current?.el?.closest("[data-ohw-nav-children]")) || Boolean(navDragRef.current?.draggedEl?.closest("[data-ohw-nav-children]")) || Boolean(
17655
+ selectedElRef.current?.closest("[data-ohw-nav-children]") && selectedElRef.current?.closest("[data-ohw-nav-group]")?.hasAttribute("data-ohw-nav-force-open")
17656
+ );
17657
+ if (!preserveNavDropdown) {
17658
+ setNavGroupForceOpen(null, false);
17659
+ }
17497
17660
  setReorderHrefKey(null);
17498
17661
  setReorderDragDisabled(false);
17499
17662
  if (!selectedElRef.current) {
@@ -17615,7 +17778,10 @@ function OhhwellsBridge() {
17615
17778
  setSelectedIsSocialsRow(false);
17616
17779
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
17617
17780
  if (isNestedNavChild(navAnchor)) {
17618
- setNavGroupForceOpen(navAnchor, true);
17781
+ const group = navAnchor.closest("[data-ohw-nav-group]");
17782
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
17783
+ setNavGroupForceOpen(navAnchor, true);
17784
+ }
17619
17785
  setNavDropdownPreviewOpen(null);
17620
17786
  } else if (isDropdownTrigger) {
17621
17787
  const group = navAnchor.closest("[data-ohw-nav-group]");
@@ -17697,6 +17863,10 @@ function OhhwellsBridge() {
17697
17863
  const handleNavDropdownOpenChange = (0, import_react17.useCallback)((open) => {
17698
17864
  const selected = selectedElRef.current;
17699
17865
  if (!selected || !isNavigationItem2(selected)) return;
17866
+ if (isNestedNavChild(selected)) return;
17867
+ const group = selected.closest("[data-ohw-nav-group]");
17868
+ const domOpen = Boolean(group?.hasAttribute("data-ohw-nav-force-open"));
17869
+ if (open === domOpen) return;
17700
17870
  setNavGroupForceOpen(selected, open);
17701
17871
  setNavDropdownPreviewOpen(open);
17702
17872
  requestAnimationFrame(() => {
@@ -18226,7 +18396,10 @@ function OhhwellsBridge() {
18226
18396
  clearHrefKeyHover(anchor);
18227
18397
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
18228
18398
  if (isNestedNavChild(anchor)) {
18229
- setNavGroupForceOpen(anchor, true);
18399
+ const group = anchor.closest("[data-ohw-nav-group]");
18400
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
18401
+ setNavGroupForceOpen(anchor, true);
18402
+ }
18230
18403
  setNavDropdownPreviewOpen(null);
18231
18404
  } else if (isDropdownTrigger) {
18232
18405
  setNavGroupForceOpen(null, false);
@@ -19068,28 +19241,19 @@ function OhhwellsBridge() {
19068
19241
  return;
19069
19242
  }
19070
19243
  const existing = editStylesRef.current;
19071
- let initialVh = window.innerHeight;
19072
- if (existing?.base.textContent) {
19073
- const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
19074
- if (match) initialVh = parseInt(match[1], 10);
19075
- }
19244
+ const canvasHeight = "var(--ohw-canvas-h, 852px)";
19076
19245
  const baseCss = `
19077
19246
  html { height: auto !important; }
19078
19247
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
19079
- .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
19080
- .h-screen, .h-svh, .h-dvh { height: ${initialVh}px !important; }
19081
- [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19082
- [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19083
- [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19084
- /* A section written as min-height: var(--ohw-canvas-h, 100svh) \u2014 the documented way to
19085
- build a full-viewport section that still grows for its own content \u2014 matches the three
19086
- rules above on substring alone, since the fallback text contains "100svh" too. Forcing
19087
- a literal height on top of that turns "at least one screen" into "exactly one screen",
19088
- so content taller than one screen (a long mobile hero, say) overflows a centered flex
19089
- column upward, under whatever sits above it. Only the min-height half belongs to it. */
19090
- [style*="min-height"][style*="100vh"],
19091
- [style*="min-height"][style*="100svh"],
19092
- [style*="min-height"][style*="100dvh"] { height: auto !important; }
19248
+ .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${canvasHeight} !important; }
19249
+ .h-screen, .h-svh, .h-dvh { height: ${canvasHeight} !important; }
19250
+ /* Literal inline viewport units only \u2014 skip R11 sections that already reference --ohw-canvas-h
19251
+ (their style attribute contains "100svh" only as a var() fallback string). */
19252
+ [style*="100vh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19253
+ [style*="100svh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19254
+ [style*="100dvh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19255
+ /* R11: min-height via --ohw-canvas-h; height must grow with content taller than one screen. */
19256
+ [style*="min-height"][style*="--ohw-canvas-h"] { height: auto !important; }
19093
19257
  /* Emptied text keeps somewhere to click. A label typed down to nothing collapses to a
19094
19258
  couple of pixels, and getting back into it meant hunting for the caret with the mouse.
19095
19259
  Edit mode only \u2014 the published page shows nothing where there is nothing (OHH-736). */
@@ -19236,6 +19400,10 @@ function OhhwellsBridge() {
19236
19400
  outline-offset: 0 !important;
19237
19401
  box-shadow: none !important;
19238
19402
  }
19403
+ /* Open nav dropdown panels paint above AI section selection chrome (2147483000). */
19404
+ [data-ohw-nav-group][data-ohw-nav-force-open] > [data-ohw-nav-children] {
19405
+ z-index: 2147483100 !important;
19406
+ }
19239
19407
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
19240
19408
  [data-ohw-editing],
19241
19409
  [data-ohw-editing] *,
@@ -20574,7 +20742,11 @@ function OhhwellsBridge() {
20574
20742
  const slotFromItem = hrefKey ? findSocialByHrefKey(hrefKey)?.querySelector('[data-ohw-editable="icon"]') : null;
20575
20743
  const requestedIsIconSlot = Boolean(requestedIconKey) && Boolean(document.querySelector(`[data-ohw-key="${requestedIconKey}"][data-ohw-editable="icon"]`));
20576
20744
  const requestedIsFree = Boolean(requestedIconKey) && !document.querySelector(`[data-ohw-key="${requestedIconKey}"]`);
20577
- const iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20745
+ let iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20746
+ const item = hrefKey ? findSocialByHrefKey(hrefKey) : null;
20747
+ if (item && typeof iconMarkup === "string" && iconMarkup) {
20748
+ iconKey = ensureIconSlot(item) ?? iconKey;
20749
+ }
20578
20750
  if (iconKey && typeof iconMarkup === "string" && iconMarkup) {
20579
20751
  document.querySelectorAll(`[data-ohw-key="${iconKey}"][data-ohw-editable="icon"]`).forEach((el) => {
20580
20752
  applyIconMarkup(el, iconMarkup);
@@ -20584,7 +20756,7 @@ function OhhwellsBridge() {
20584
20756
  }
20585
20757
  if (iconKey && platformId) nodes.push({ key: socialPlatformKey(iconKey), text: platformId });
20586
20758
  if (iconKey && label) {
20587
- const labelKey = socialLabelKey(iconKey);
20759
+ const labelKey = (item ? socialLabelElement(item)?.getAttribute("data-ohw-key") : null) ?? socialLabelKey(iconKey);
20588
20760
  document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
20589
20761
  if (keepLabel && el.textContent?.trim()) return;
20590
20762
  el.textContent = label;
@@ -20854,6 +21026,9 @@ function OhhwellsBridge() {
20854
21026
  if (reapNodes.length > 0) postToParentRef.current({ type: "ow:change", nodes: reapNodes });
20855
21027
  const hydratedHeight = document.body.scrollHeight;
20856
21028
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
21029
+ if (parseAiSectionsState(aiSectionsRef.current).sections.length > 0) {
21030
+ postAiSectionsChanged();
21031
+ }
20857
21032
  postToParentRef.current({ type: "ow:hydrate-done" });
20858
21033
  };
20859
21034
  const handleUpdateLogoIdentity = (e) => {
@@ -21400,7 +21575,12 @@ function OhhwellsBridge() {
21400
21575
  }
21401
21576
  if (navDragRef.current) {
21402
21577
  const session = navDragRef.current;
21403
- const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
21578
+ const slot = hitTestNavDropSlot(
21579
+ session.lastClientX,
21580
+ session.lastClientY,
21581
+ session.hrefKey,
21582
+ session.draggedEl
21583
+ );
21404
21584
  refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
21405
21585
  }
21406
21586
  if (hoveredImageRef.current) {
@@ -21602,18 +21782,21 @@ function OhhwellsBridge() {
21602
21782
  }
21603
21783
  return null;
21604
21784
  };
21785
+ const isPointInRect = (el, clientX, clientY) => {
21786
+ const r2 = el.getBoundingClientRect();
21787
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
21788
+ };
21605
21789
  const isPointOverEditable = (scope, clientX, clientY) => {
21606
21790
  const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
21607
21791
  for (const el of editables) {
21608
- const r2 = el.getBoundingClientRect();
21609
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
21792
+ if (isPointInRect(el, clientX, clientY)) return true;
21610
21793
  }
21611
21794
  return false;
21612
21795
  };
21613
21796
  const handleCarouselHover = (e) => {
21614
21797
  const container = findCarouselAtPoint(e.clientX, e.clientY);
21615
21798
  const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
21616
- if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
21799
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY) || isPointOverNavigation(e.clientX, e.clientY) || isPointOverBridgeChrome(e.clientX, e.clientY)) {
21617
21800
  setCarouselHover((prev) => prev ? null : prev);
21618
21801
  return;
21619
21802
  }
@@ -22062,7 +22245,7 @@ function OhhwellsBridge() {
22062
22245
  postToParent2({
22063
22246
  type: "ow:ready",
22064
22247
  version: "1",
22065
- bridgeVersion: "0.1.98",
22248
+ bridgeVersion: "0.1.100",
22066
22249
  path: pathname,
22067
22250
  nodes: collectEditableNodes(editContentRef.current),
22068
22251
  sections