@ohhwells/bridge 0.1.100 → 0.1.102

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
@@ -1307,7 +1307,12 @@ function ButtonEl({
1307
1307
  textDecoration: "none",
1308
1308
  cursor: "pointer",
1309
1309
  ...buttonShellStyle(ctx, fullWidth),
1310
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1310
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : (
1311
+ // Off-band, prefer the template button's measured fill/label pair — a template may
1312
+ // fill its CTAs with any token (hvac: accent bg, primary text). On an accent band
1313
+ // the flipped palette keeps contrast, so the brand-driven colours stay.
1314
+ !ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1315
+ )
1311
1316
  },
1312
1317
  children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
1313
1318
  }
@@ -2379,10 +2384,12 @@ function renderNode(node, ctx, path) {
2379
2384
  cursor: "pointer",
2380
2385
  // Shape/padding/typography follow the host template's own buttons.
2381
2386
  ...buttonShellStyle(ctx),
2382
- // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
2383
- // reads correctly on custom palettes.
2384
- background: ctx.brand.palette.primary,
2385
- color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2387
+ // Brand-styled: the template button's measured fill/label pair off-band, else
2388
+ // primary fill with a brand-derived label so it reads on custom palettes.
2389
+ ...!ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : {
2390
+ background: ctx.brand.palette.primary,
2391
+ color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2392
+ }
2386
2393
  },
2387
2394
  children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
2388
2395
  },
@@ -2589,11 +2596,19 @@ function deriveTemplateBrand() {
2589
2596
  }
2590
2597
  function deriveTemplateButtonStyle() {
2591
2598
  if (typeof document === "undefined") return null;
2592
- const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
2593
- (el) => !el.closest(`[${CONTAINER_ATTR}]`)
2594
- );
2599
+ const candidates = Array.from(
2600
+ document.querySelectorAll('[data-ohw-role="button"]')
2601
+ ).filter((el) => !el.closest(`[${CONTAINER_ATTR}]`));
2602
+ const isFilled = (el) => {
2603
+ const bg = getComputedStyle(el).backgroundColor;
2604
+ if (!bg || bg === "transparent") return false;
2605
+ const alpha = bg.match(/rgba?\([^)]*,\s*([\d.]+)\)$/);
2606
+ return !alpha || parseFloat(alpha[1]) > 0;
2607
+ };
2608
+ const btn = candidates.find(isFilled) ?? candidates[0];
2595
2609
  if (!btn) return null;
2596
2610
  const cs = getComputedStyle(btn);
2611
+ const filled = isFilled(btn);
2597
2612
  const corners = [
2598
2613
  cs.borderTopLeftRadius,
2599
2614
  cs.borderTopRightRadius,
@@ -2610,6 +2625,7 @@ function deriveTemplateButtonStyle() {
2610
2625
  return {
2611
2626
  radius: radius || "10px",
2612
2627
  padding: `${padY}px ${padX}px`,
2628
+ ...filled ? { background: cs.backgroundColor, color: cs.color } : {},
2613
2629
  fontFamily: cs.fontFamily || "",
2614
2630
  fontSize: cs.fontSize || "",
2615
2631
  fontWeight: cs.fontWeight || "",
@@ -7427,9 +7443,7 @@ function ItemActionToolbar({
7427
7443
  ToggleGroup,
7428
7444
  {
7429
7445
  value: dropdownOpen ? "open" : "closed",
7430
- onValueChange: (value) => {
7431
- if (value !== "open" && value !== "closed") return;
7432
- onDropdownOpenChange?.(value === "open");
7446
+ onValueChange: () => {
7433
7447
  },
7434
7448
  className: "h-7 gap-0.5 rounded-[calc(var(--radius,0.5rem)-2px)] bg-muted p-0.5",
7435
7449
  onMouseDown: (e) => {
@@ -7444,6 +7458,11 @@ function ItemActionToolbar({
7444
7458
  size: "sm",
7445
7459
  "aria-label": "Closed",
7446
7460
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7461
+ onMouseDown: (e) => {
7462
+ e.preventDefault();
7463
+ e.stopPropagation();
7464
+ if (dropdownOpen !== false) onDropdownOpenChange?.(false);
7465
+ },
7447
7466
  children: "Closed"
7448
7467
  }
7449
7468
  ),
@@ -7454,6 +7473,11 @@ function ItemActionToolbar({
7454
7473
  size: "sm",
7455
7474
  "aria-label": "Open",
7456
7475
  className: "h-6 min-w-0 px-2 text-xs font-medium",
7476
+ onMouseDown: (e) => {
7477
+ e.preventDefault();
7478
+ e.stopPropagation();
7479
+ if (dropdownOpen !== true) onDropdownOpenChange?.(true);
7480
+ },
7457
7481
  children: "Open"
7458
7482
  }
7459
7483
  )
@@ -8479,7 +8503,7 @@ function FieldTypePicker({ onPick }) {
8479
8503
  "div",
8480
8504
  {
8481
8505
  "data-ohw-field-type-picker": "",
8482
- className: "pointer-events-auto grid w-[420px] grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
8506
+ className: "pointer-events-auto grid w-[280px] grid-cols-2 gap-2 rounded-xl border border-border bg-background p-3 shadow-lg",
8483
8507
  children: FIELD_TYPES.map((entry) => {
8484
8508
  const Icon = TYPE_ICONS[entry.type];
8485
8509
  return /* @__PURE__ */ jsxs6(
@@ -8487,7 +8511,7 @@ function FieldTypePicker({ onPick }) {
8487
8511
  {
8488
8512
  type: "button",
8489
8513
  onClick: () => onPick(entry.type),
8490
- 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",
8514
+ 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",
8491
8515
  children: [
8492
8516
  /* @__PURE__ */ jsx13(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8493
8517
  entry.label
@@ -10840,6 +10864,32 @@ function getNavbarDesktopContainer() {
10840
10864
  function getNavbarDrawerContainer() {
10841
10865
  return document.querySelector("[data-ohw-nav-drawer]");
10842
10866
  }
10867
+ function isNavbarListContainerVisible(el) {
10868
+ if (el.hasAttribute("hidden")) return false;
10869
+ if (el.getClientRects().length === 0) return false;
10870
+ const style = window.getComputedStyle(el);
10871
+ return style.display !== "none" && style.visibility !== "hidden";
10872
+ }
10873
+ function getActiveNavbarListContainer(draggedEl) {
10874
+ const desktop = getNavbarDesktopContainer();
10875
+ const drawer = getNavbarDrawerContainer();
10876
+ if (draggedEl) {
10877
+ if (drawer?.contains(draggedEl)) return drawer;
10878
+ if (desktop?.contains(draggedEl)) return desktop;
10879
+ }
10880
+ if (desktop && isNavbarListContainerVisible(desktop)) return desktop;
10881
+ if (drawer && isNavbarListContainerVisible(drawer)) return drawer;
10882
+ return desktop ?? drawer;
10883
+ }
10884
+ function listActiveNavbarItems(draggedEl) {
10885
+ const container = getActiveNavbarListContainer(draggedEl);
10886
+ if (container) {
10887
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(
10888
+ isNavbarLinkItem
10889
+ );
10890
+ }
10891
+ return listNavbarItems();
10892
+ }
10843
10893
  function listNavbarItems() {
10844
10894
  const desktop = getNavbarDesktopContainer();
10845
10895
  if (desktop) {
@@ -11952,20 +12002,23 @@ function isEmptyLabelValue(value) {
11952
12002
  function applyStoredValues(item, content) {
11953
12003
  const hrefKey = socialHrefKey(item);
11954
12004
  const iconKey = socialIconKey(item);
12005
+ const baseKey = hrefKey?.replace(/-href$/, "");
11955
12006
  if (hrefKey && content[hrefKey] !== void 0) item.setAttribute("href", content[hrefKey]);
11956
- if (iconKey) {
12007
+ const glyphKey = iconKey ?? baseKey ?? null;
12008
+ const glyphStored = glyphKey ? content[glyphKey]?.trim() : void 0;
12009
+ if (glyphStored) {
12010
+ ensureIconSlot(item);
11957
12011
  const glyph = item.querySelector(ICON_SELECTOR);
11958
- if (glyph && content[iconKey]) {
11959
- applyIconMarkup(glyph, content[iconKey]);
12012
+ if (glyph) {
12013
+ applyIconMarkup(glyph, glyphStored);
11960
12014
  glyph.removeAttribute(SOCIALS_ICON_PLACEHOLDER_ATTR);
11961
12015
  }
11962
- const label = item.querySelector(`[${SOCIALS_LABEL_ATTR}]`);
11963
- label?.setAttribute("data-ohw-key", socialLabelKey(iconKey));
11964
- const stored = content[socialLabelKey(iconKey)];
11965
- if (label && stored !== void 0) {
11966
- const words = isEmptyLabelValue(stored) ? "" : stored;
11967
- if (label.innerHTML !== words) label.innerHTML = words;
11968
- }
12016
+ }
12017
+ const label = socialLabelElement(item);
12018
+ const labelKey = label?.getAttribute("data-ohw-key");
12019
+ if (label && labelKey && content[labelKey] !== void 0) {
12020
+ const words = isEmptyLabelValue(content[labelKey]) ? "" : content[labelKey];
12021
+ if (label.innerHTML !== words) label.innerHTML = words;
11969
12022
  }
11970
12023
  }
11971
12024
  function socialPlatformKey(iconKey) {
@@ -12054,6 +12107,9 @@ function insertSocialItem(row, after, content = {}, { placeholder = true, keys }
12054
12107
  if (placeholder) {
12055
12108
  const label = socialLabelElement(item);
12056
12109
  if (label) label.textContent = PLACEHOLDER_SOCIAL_LABEL;
12110
+ } else if (keys) {
12111
+ const label = socialLabelElement(item);
12112
+ if (label) label.textContent = "";
12057
12113
  }
12058
12114
  applySocialsDisplayToRow(row, display);
12059
12115
  return { item, hrefKey, iconKey, order: getSocialsOrderFromDom(row.ownerDocument) };
@@ -13916,8 +13972,8 @@ var lockFooterDuringDrag = lockItemDuringDrag;
13916
13972
  var unlockFooterDragInteraction = unlockItemDragInteraction;
13917
13973
 
13918
13974
  // src/lib/nav-dnd.ts
13919
- function listReorderableNavItems() {
13920
- return listNavbarItems().filter((el) => {
13975
+ function listReorderableNavItems(draggedEl) {
13976
+ return listActiveNavbarItems(draggedEl).filter((el) => {
13921
13977
  const key = el.getAttribute("data-ohw-href-key");
13922
13978
  return isNavbarHrefKey(key) && !isNestedNavChild(el);
13923
13979
  });
@@ -13932,8 +13988,8 @@ function resolveParentNavHrefKey(child) {
13932
13988
  const key = trigger?.getAttribute("data-ohw-href-key");
13933
13989
  return key && isNavbarHrefKey(key) ? key : null;
13934
13990
  }
13935
- function listNavChildren(parentHrefKey) {
13936
- const items = listNavbarItems();
13991
+ function listNavChildren(parentHrefKey, draggedEl) {
13992
+ const items = listActiveNavbarItems(draggedEl);
13937
13993
  const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
13938
13994
  if (!parent) return [];
13939
13995
  const group = parent.closest("[data-ohw-nav-group]");
@@ -13943,20 +13999,83 @@ function listNavChildren(parentHrefKey) {
13943
13999
  (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
13944
14000
  );
13945
14001
  }
13946
- function activeNavListContainer() {
13947
- const desktop = getNavbarDesktopContainer();
13948
- if (desktop && desktop.getClientRects().length > 0) {
13949
- const style = window.getComputedStyle(desktop);
13950
- if (style.display !== "none" && style.visibility !== "hidden") return desktop;
14002
+ function isVerticalNavLayout(items, draggedEl) {
14003
+ const visible = items.filter((el) => el.getClientRects().length > 0);
14004
+ if (visible.length >= 2) {
14005
+ const a = visible[0].getBoundingClientRect();
14006
+ const b = visible[1].getBoundingClientRect();
14007
+ const verticalGap = b.top - a.bottom;
14008
+ const horizontalGap = b.left - a.right;
14009
+ return verticalGap > horizontalGap;
14010
+ }
14011
+ const container = getActiveNavbarListContainer(draggedEl);
14012
+ if (!container) return false;
14013
+ const style = window.getComputedStyle(container);
14014
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
14015
+ }
14016
+ function buildVerticalRootNavDropSlots(items, draggedEl) {
14017
+ const slots = [];
14018
+ const barThickness = 3;
14019
+ if (items.length === 0) {
14020
+ const container = getActiveNavbarListContainer(draggedEl);
14021
+ if (!container) return slots;
14022
+ const rect = container.getBoundingClientRect();
14023
+ slots.push({
14024
+ insertIndex: 0,
14025
+ parentId: null,
14026
+ left: rect.left,
14027
+ top: rect.top + rect.height / 2 - barThickness / 2,
14028
+ width: Math.max(rect.width, 40),
14029
+ height: barThickness,
14030
+ direction: "horizontal"
14031
+ });
14032
+ return slots;
14033
+ }
14034
+ const edgeGap = (() => {
14035
+ if (items.length < 2) return 16;
14036
+ const a = items[0].getBoundingClientRect();
14037
+ const b = items[1].getBoundingClientRect();
14038
+ return Math.max(0, b.top - a.bottom);
14039
+ })();
14040
+ for (let i = 0; i <= items.length; i++) {
14041
+ let top;
14042
+ let width;
14043
+ let left;
14044
+ if (i === 0) {
14045
+ const first = items[0].getBoundingClientRect();
14046
+ top = first.top - edgeGap / 2 - barThickness / 2;
14047
+ left = first.left;
14048
+ width = first.width;
14049
+ } else if (i === items.length) {
14050
+ const last = items[items.length - 1].getBoundingClientRect();
14051
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
14052
+ top = last.bottom + lastGap / 2 - barThickness / 2;
14053
+ left = last.left;
14054
+ width = last.width;
14055
+ } else {
14056
+ const prev = items[i - 1].getBoundingClientRect();
14057
+ const next = items[i].getBoundingClientRect();
14058
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
14059
+ left = Math.min(prev.left, next.left);
14060
+ width = Math.max(prev.right, next.right) - left;
14061
+ }
14062
+ slots.push({
14063
+ insertIndex: i,
14064
+ parentId: null,
14065
+ left,
14066
+ top,
14067
+ width: Math.max(width, 40),
14068
+ height: barThickness,
14069
+ direction: "horizontal"
14070
+ });
13951
14071
  }
13952
- return getNavbarDrawerContainer();
14072
+ return slots;
13953
14073
  }
13954
- function buildRootNavDropSlots() {
13955
- const items = listReorderableNavItems();
14074
+ function buildHorizontalRootNavDropSlots(items, draggedEl) {
13956
14075
  const slots = [];
13957
14076
  const barThickness = 3;
13958
14077
  if (items.length === 0) {
13959
- const container = activeNavListContainer();
14078
+ const container = getActiveNavbarListContainer(draggedEl);
13960
14079
  if (!container) return slots;
13961
14080
  const rect = container.getBoundingClientRect();
13962
14081
  slots.push({
@@ -13987,10 +14106,7 @@ function buildRootNavDropSlots() {
13987
14106
  height = first.height;
13988
14107
  } else if (i === items.length) {
13989
14108
  const last = items[items.length - 1].getBoundingClientRect();
13990
- const lastGap = items.length >= 2 ? Math.max(
13991
- 0,
13992
- last.left - items[items.length - 2].getBoundingClientRect().right
13993
- ) : edgeGap;
14109
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
13994
14110
  left = last.right + lastGap / 2 - barThickness / 2;
13995
14111
  top = last.top;
13996
14112
  height = last.height;
@@ -14013,8 +14129,15 @@ function buildRootNavDropSlots() {
14013
14129
  }
14014
14130
  return slots;
14015
14131
  }
14016
- function buildChildNavDropSlots(parentHrefKey) {
14017
- const children = listNavChildren(parentHrefKey);
14132
+ function buildRootNavDropSlots(draggedEl) {
14133
+ const items = listReorderableNavItems(draggedEl);
14134
+ if (isVerticalNavLayout(items, draggedEl)) {
14135
+ return buildVerticalRootNavDropSlots(items, draggedEl);
14136
+ }
14137
+ return buildHorizontalRootNavDropSlots(items, draggedEl);
14138
+ }
14139
+ function buildChildNavDropSlots(parentHrefKey, draggedEl) {
14140
+ const children = listNavChildren(parentHrefKey, draggedEl);
14018
14141
  const slots = [];
14019
14142
  const barThickness = 3;
14020
14143
  if (children.length === 0) return slots;
@@ -14035,10 +14158,7 @@ function buildChildNavDropSlots(parentHrefKey) {
14035
14158
  width = first.width;
14036
14159
  } else if (i === children.length) {
14037
14160
  const last = children[children.length - 1].getBoundingClientRect();
14038
- const lastGap = children.length >= 2 ? Math.max(
14039
- 0,
14040
- last.top - children[children.length - 2].getBoundingClientRect().bottom
14041
- ) : edgeGap;
14161
+ const lastGap = children.length >= 2 ? Math.max(0, last.top - children[children.length - 2].getBoundingClientRect().bottom) : edgeGap;
14042
14162
  top = last.bottom + lastGap / 2 - barThickness / 2;
14043
14163
  left = last.left;
14044
14164
  width = last.width;
@@ -14061,18 +14181,19 @@ function buildChildNavDropSlots(parentHrefKey) {
14061
14181
  }
14062
14182
  return slots;
14063
14183
  }
14064
- function buildAllNavDropSlots(draggedHrefKey) {
14065
- const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
14066
- if (!dragged) return buildRootNavDropSlots();
14184
+ function buildAllNavDropSlots(draggedHrefKey, draggedEl) {
14185
+ const activeItems = listActiveNavbarItems(draggedEl);
14186
+ 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);
14187
+ if (!dragged) return buildRootNavDropSlots(draggedEl);
14067
14188
  if (isNestedNavChild(dragged)) {
14068
14189
  const parentKey = resolveParentNavHrefKey(dragged);
14069
14190
  if (!parentKey) return [];
14070
- return buildChildNavDropSlots(parentKey);
14191
+ return buildChildNavDropSlots(parentKey, draggedEl);
14071
14192
  }
14072
- return buildRootNavDropSlots();
14193
+ return buildRootNavDropSlots(draggedEl);
14073
14194
  }
14074
- function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
14075
- const slots = buildAllNavDropSlots(draggedHrefKey);
14195
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey, draggedEl) {
14196
+ const slots = buildAllNavDropSlots(draggedHrefKey, draggedEl);
14076
14197
  let best = null;
14077
14198
  for (const slot of slots) {
14078
14199
  const cx2 = slot.left + slot.width / 2;
@@ -14123,6 +14244,12 @@ function useNavItemDrag({
14123
14244
  setIsItemDragging(false);
14124
14245
  if (keepOpenEl?.isConnected) {
14125
14246
  setNavGroupForceOpen(keepOpenEl, true);
14247
+ requestAnimationFrame(() => {
14248
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14249
+ requestAnimationFrame(() => {
14250
+ if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14251
+ });
14252
+ });
14126
14253
  } else {
14127
14254
  setNavGroupForceOpen(null, false);
14128
14255
  }
@@ -14138,7 +14265,7 @@ function useNavItemDrag({
14138
14265
  }
14139
14266
  session.activeSlot = activeSlot;
14140
14267
  setSiblingHintRects([]);
14141
- const slots = buildAllNavDropSlots(session.hrefKey);
14268
+ const slots = buildAllNavDropSlots(session.hrefKey, session.draggedEl);
14142
14269
  setNavDropSlots(slots);
14143
14270
  const activeIdx = activeSlot ? slots.findIndex(
14144
14271
  (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
@@ -14174,7 +14301,12 @@ function useNavItemDrag({
14174
14301
  if (session.wasSelected && selectedElRef.current === session.draggedEl) {
14175
14302
  setToolbarRect(rect);
14176
14303
  }
14177
- const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
14304
+ const initialSlot = hitTestNavDropSlot(
14305
+ session.lastClientX,
14306
+ session.lastClientY,
14307
+ session.hrefKey,
14308
+ session.draggedEl
14309
+ );
14178
14310
  refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
14179
14311
  },
14180
14312
  [
@@ -14196,10 +14328,11 @@ function useNavItemDrag({
14196
14328
  }
14197
14329
  const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
14198
14330
  const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
14199
- const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
14331
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey, session.draggedEl);
14200
14332
  const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
14201
14333
  const wasSelected = session.wasSelected;
14202
14334
  const hrefKey = session.hrefKey;
14335
+ const keepDropdownOpen = Boolean(session.draggedEl.closest("[data-ohw-nav-children]"));
14203
14336
  const applySelectionAfterDrop = () => {
14204
14337
  if (!wasSelected) {
14205
14338
  deselectRef.current();
@@ -14233,6 +14366,14 @@ function useNavItemDrag({
14233
14366
  });
14234
14367
  applySelectionAfterDrop();
14235
14368
  clearNavDragVisuals();
14369
+ if (keepDropdownOpen) {
14370
+ requestAnimationFrame(() => {
14371
+ const desktop = document.querySelector("[data-ohw-nav-container]");
14372
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
14373
+ 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)}"]`);
14374
+ if (link) setNavGroupForceOpen(link, true);
14375
+ });
14376
+ }
14236
14377
  requestAnimationFrame(() => {
14237
14378
  if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
14238
14379
  applyNavForest(planned.forest);
@@ -14272,7 +14413,7 @@ function useNavItemDrag({
14272
14413
  if (!session) return false;
14273
14414
  e.preventDefault();
14274
14415
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
14275
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14416
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14276
14417
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14277
14418
  return true;
14278
14419
  },
@@ -14318,7 +14459,7 @@ function useNavItemDrag({
14318
14459
  clearTextSelection();
14319
14460
  const session = navDragRef.current;
14320
14461
  if (!session) return;
14321
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
14462
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey, session.draggedEl);
14322
14463
  refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
14323
14464
  return;
14324
14465
  }
@@ -14334,7 +14475,8 @@ function useNavItemDrag({
14334
14475
  } catch {
14335
14476
  }
14336
14477
  if (linkPopoverOpenRef.current) setLinkPopover(null);
14337
- if (activeElRef.current) deactivateRef.current();
14478
+ const isNestedDrag = Boolean(pending.el.closest("[data-ohw-nav-children]"));
14479
+ if (activeElRef.current && !isNestedDrag) deactivateRef.current();
14338
14480
  const key = pending.el.getAttribute("data-ohw-href-key");
14339
14481
  if (!key) return;
14340
14482
  beginNavDragRef.current({
@@ -14345,6 +14487,9 @@ function useNavItemDrag({
14345
14487
  lastClientY: e.clientY,
14346
14488
  activeSlot: null
14347
14489
  });
14490
+ if (activeElRef.current && isNestedDrag) {
14491
+ deactivateRef.current();
14492
+ }
14348
14493
  };
14349
14494
  const endPointerDrag = (e) => {
14350
14495
  const pending = navPointerDragRef.current;
@@ -14422,6 +14567,7 @@ function useNavItemDrag({
14422
14567
  );
14423
14568
  return {
14424
14569
  navDragRef,
14570
+ navPointerDragRef,
14425
14571
  navDropSlots,
14426
14572
  activeNavDropIndex,
14427
14573
  startNavLinkDrag,
@@ -15820,6 +15966,7 @@ function isNavDropdownPanelOpen(childrenRoot) {
15820
15966
  if (childrenRoot.closest("[data-ohw-nav-drawer]")) return true;
15821
15967
  const group = childrenRoot.closest("[data-ohw-nav-group]");
15822
15968
  if (group?.hasAttribute("data-ohw-nav-force-open")) return true;
15969
+ if (navDropdownsOpenOnClick()) return false;
15823
15970
  if (childrenRoot.clientHeight < 2 || childrenRoot.clientWidth < 2) return false;
15824
15971
  const style = window.getComputedStyle(childrenRoot);
15825
15972
  if (style.display === "none" || style.visibility === "hidden") return false;
@@ -15863,6 +16010,11 @@ function getNavigationItemAnchor(el) {
15863
16010
  function isNavigationItem2(el) {
15864
16011
  return getNavigationItemAnchor(el) !== null;
15865
16012
  }
16013
+ function isNavFooterScopedNavigationItem(el) {
16014
+ return Boolean(
16015
+ el.closest("nav, footer, [data-ohw-nav-container], [data-ohw-nav-drawer]")
16016
+ );
16017
+ }
15866
16018
  function socialWordsClicked(target, item) {
15867
16019
  const text = target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
15868
16020
  return Boolean(text && item.contains(text));
@@ -16073,6 +16225,7 @@ function getNavigationSelectionParent(el) {
16073
16225
  }
16074
16226
  function collectNavigationItemSiblingHintRects(selected) {
16075
16227
  if (!isNavigationItem2(selected)) return [];
16228
+ if (!isNavFooterScopedNavigationItem(selected)) return [];
16076
16229
  const socialsRow = findSocialsRow(selected);
16077
16230
  if (socialsRow) {
16078
16231
  return listSocialItems(socialsRow).filter((item) => item !== selected).map((item) => item.getBoundingClientRect());
@@ -17244,6 +17397,7 @@ function OhhwellsBridge() {
17244
17397
  linkPopoverSessionRef.current = linkPopover;
17245
17398
  const {
17246
17399
  navDragRef,
17400
+ navPointerDragRef,
17247
17401
  navDropSlots,
17248
17402
  activeNavDropIndex,
17249
17403
  startNavLinkDrag,
@@ -17446,7 +17600,12 @@ function OhhwellsBridge() {
17446
17600
  if (owner) persistFieldsRef.current(owner);
17447
17601
  }
17448
17602
  activeElRef.current = null;
17449
- setNavGroupForceOpen(null, false);
17603
+ const preserveNavDropdown = Boolean(navPointerDragRef.current?.el?.closest("[data-ohw-nav-children]")) || Boolean(navDragRef.current?.draggedEl?.closest("[data-ohw-nav-children]")) || Boolean(
17604
+ selectedElRef.current?.closest("[data-ohw-nav-children]") && selectedElRef.current?.closest("[data-ohw-nav-group]")?.hasAttribute("data-ohw-nav-force-open")
17605
+ );
17606
+ if (!preserveNavDropdown) {
17607
+ setNavGroupForceOpen(null, false);
17608
+ }
17450
17609
  setReorderHrefKey(null);
17451
17610
  setReorderDragDisabled(false);
17452
17611
  if (!selectedElRef.current) {
@@ -17568,7 +17727,10 @@ function OhhwellsBridge() {
17568
17727
  setSelectedIsSocialsRow(false);
17569
17728
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
17570
17729
  if (isNestedNavChild(navAnchor)) {
17571
- setNavGroupForceOpen(navAnchor, true);
17730
+ const group = navAnchor.closest("[data-ohw-nav-group]");
17731
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
17732
+ setNavGroupForceOpen(navAnchor, true);
17733
+ }
17572
17734
  setNavDropdownPreviewOpen(null);
17573
17735
  } else if (isDropdownTrigger) {
17574
17736
  const group = navAnchor.closest("[data-ohw-nav-group]");
@@ -17650,6 +17812,10 @@ function OhhwellsBridge() {
17650
17812
  const handleNavDropdownOpenChange = useCallback8((open) => {
17651
17813
  const selected = selectedElRef.current;
17652
17814
  if (!selected || !isNavigationItem2(selected)) return;
17815
+ if (isNestedNavChild(selected)) return;
17816
+ const group = selected.closest("[data-ohw-nav-group]");
17817
+ const domOpen = Boolean(group?.hasAttribute("data-ohw-nav-force-open"));
17818
+ if (open === domOpen) return;
17653
17819
  setNavGroupForceOpen(selected, open);
17654
17820
  setNavDropdownPreviewOpen(open);
17655
17821
  requestAnimationFrame(() => {
@@ -18179,7 +18345,10 @@ function OhhwellsBridge() {
18179
18345
  clearHrefKeyHover(anchor);
18180
18346
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
18181
18347
  if (isNestedNavChild(anchor)) {
18182
- setNavGroupForceOpen(anchor, true);
18348
+ const group = anchor.closest("[data-ohw-nav-group]");
18349
+ if (group?.hasAttribute("data-ohw-nav-force-open")) {
18350
+ setNavGroupForceOpen(anchor, true);
18351
+ }
18183
18352
  setNavDropdownPreviewOpen(null);
18184
18353
  } else if (isDropdownTrigger) {
18185
18354
  setNavGroupForceOpen(null, false);
@@ -19021,28 +19190,19 @@ function OhhwellsBridge() {
19021
19190
  return;
19022
19191
  }
19023
19192
  const existing = editStylesRef.current;
19024
- let initialVh = window.innerHeight;
19025
- if (existing?.base.textContent) {
19026
- const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
19027
- if (match) initialVh = parseInt(match[1], 10);
19028
- }
19193
+ const canvasHeight = "var(--ohw-canvas-h, 852px)";
19029
19194
  const baseCss = `
19030
19195
  html { height: auto !important; }
19031
19196
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
19032
- .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
19033
- .h-screen, .h-svh, .h-dvh { height: ${initialVh}px !important; }
19034
- [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19035
- [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19036
- [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
19037
- /* A section written as min-height: var(--ohw-canvas-h, 100svh) \u2014 the documented way to
19038
- build a full-viewport section that still grows for its own content \u2014 matches the three
19039
- rules above on substring alone, since the fallback text contains "100svh" too. Forcing
19040
- a literal height on top of that turns "at least one screen" into "exactly one screen",
19041
- so content taller than one screen (a long mobile hero, say) overflows a centered flex
19042
- column upward, under whatever sits above it. Only the min-height half belongs to it. */
19043
- [style*="min-height"][style*="100vh"],
19044
- [style*="min-height"][style*="100svh"],
19045
- [style*="min-height"][style*="100dvh"] { height: auto !important; }
19197
+ .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${canvasHeight} !important; }
19198
+ .h-screen, .h-svh, .h-dvh { height: ${canvasHeight} !important; }
19199
+ /* Literal inline viewport units only \u2014 skip R11 sections that already reference --ohw-canvas-h
19200
+ (their style attribute contains "100svh" only as a var() fallback string). */
19201
+ [style*="100vh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19202
+ [style*="100svh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19203
+ [style*="100dvh"]:not([style*="--ohw-canvas-h"]) { min-height: ${canvasHeight} !important; height: ${canvasHeight} !important; }
19204
+ /* R11: min-height via --ohw-canvas-h; height must grow with content taller than one screen. */
19205
+ [style*="min-height"][style*="--ohw-canvas-h"] { height: auto !important; }
19046
19206
  /* Emptied text keeps somewhere to click. A label typed down to nothing collapses to a
19047
19207
  couple of pixels, and getting back into it meant hunting for the caret with the mouse.
19048
19208
  Edit mode only \u2014 the published page shows nothing where there is nothing (OHH-736). */
@@ -19189,6 +19349,10 @@ function OhhwellsBridge() {
19189
19349
  outline-offset: 0 !important;
19190
19350
  box-shadow: none !important;
19191
19351
  }
19352
+ /* Open nav dropdown panels paint above AI section selection chrome (2147483000). */
19353
+ [data-ohw-nav-group][data-ohw-nav-force-open] > [data-ohw-nav-children] {
19354
+ z-index: 2147483100 !important;
19355
+ }
19192
19356
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
19193
19357
  [data-ohw-editing],
19194
19358
  [data-ohw-editing] *,
@@ -20527,7 +20691,11 @@ function OhhwellsBridge() {
20527
20691
  const slotFromItem = hrefKey ? findSocialByHrefKey(hrefKey)?.querySelector('[data-ohw-editable="icon"]') : null;
20528
20692
  const requestedIsIconSlot = Boolean(requestedIconKey) && Boolean(document.querySelector(`[data-ohw-key="${requestedIconKey}"][data-ohw-editable="icon"]`));
20529
20693
  const requestedIsFree = Boolean(requestedIconKey) && !document.querySelector(`[data-ohw-key="${requestedIconKey}"]`);
20530
- const iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20694
+ let iconKey = requestedIsIconSlot ? requestedIconKey : slotFromItem?.dataset.ohwKey ?? (requestedIsFree ? requestedIconKey : void 0);
20695
+ const item = hrefKey ? findSocialByHrefKey(hrefKey) : null;
20696
+ if (item && typeof iconMarkup === "string" && iconMarkup) {
20697
+ iconKey = ensureIconSlot(item) ?? iconKey;
20698
+ }
20531
20699
  if (iconKey && typeof iconMarkup === "string" && iconMarkup) {
20532
20700
  document.querySelectorAll(`[data-ohw-key="${iconKey}"][data-ohw-editable="icon"]`).forEach((el) => {
20533
20701
  applyIconMarkup(el, iconMarkup);
@@ -20537,7 +20705,7 @@ function OhhwellsBridge() {
20537
20705
  }
20538
20706
  if (iconKey && platformId) nodes.push({ key: socialPlatformKey(iconKey), text: platformId });
20539
20707
  if (iconKey && label) {
20540
- const labelKey = socialLabelKey(iconKey);
20708
+ const labelKey = (item ? socialLabelElement(item)?.getAttribute("data-ohw-key") : null) ?? socialLabelKey(iconKey);
20541
20709
  document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
20542
20710
  if (keepLabel && el.textContent?.trim()) return;
20543
20711
  el.textContent = label;
@@ -21356,7 +21524,12 @@ function OhhwellsBridge() {
21356
21524
  }
21357
21525
  if (navDragRef.current) {
21358
21526
  const session = navDragRef.current;
21359
- const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
21527
+ const slot = hitTestNavDropSlot(
21528
+ session.lastClientX,
21529
+ session.lastClientY,
21530
+ session.hrefKey,
21531
+ session.draggedEl
21532
+ );
21360
21533
  refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
21361
21534
  }
21362
21535
  if (hoveredImageRef.current) {
@@ -21558,18 +21731,21 @@ function OhhwellsBridge() {
21558
21731
  }
21559
21732
  return null;
21560
21733
  };
21734
+ const isPointInRect = (el, clientX, clientY) => {
21735
+ const r2 = el.getBoundingClientRect();
21736
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
21737
+ };
21561
21738
  const isPointOverEditable = (scope, clientX, clientY) => {
21562
21739
  const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
21563
21740
  for (const el of editables) {
21564
- const r2 = el.getBoundingClientRect();
21565
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
21741
+ if (isPointInRect(el, clientX, clientY)) return true;
21566
21742
  }
21567
21743
  return false;
21568
21744
  };
21569
21745
  const handleCarouselHover = (e) => {
21570
21746
  const container = findCarouselAtPoint(e.clientX, e.clientY);
21571
21747
  const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
21572
- if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
21748
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY) || isPointOverNavigation(e.clientX, e.clientY) || isPointOverBridgeChrome(e.clientX, e.clientY)) {
21573
21749
  setCarouselHover((prev) => prev ? null : prev);
21574
21750
  return;
21575
21751
  }
@@ -22018,7 +22194,7 @@ function OhhwellsBridge() {
22018
22194
  postToParent2({
22019
22195
  type: "ow:ready",
22020
22196
  version: "1",
22021
- bridgeVersion: "0.1.99",
22197
+ bridgeVersion: "0.1.101",
22022
22198
  path: pathname,
22023
22199
  nodes: collectEditableNodes(editContentRef.current),
22024
22200
  sections