@ohhwells/bridge 0.1.42-next.85 → 0.1.42-next.86

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
@@ -62,7 +62,7 @@ __export(index_exports, {
62
62
  module.exports = __toCommonJS(index_exports);
63
63
 
64
64
  // src/OhhwellsBridge.tsx
65
- var import_react8 = __toESM(require("react"), 1);
65
+ var import_react9 = __toESM(require("react"), 1);
66
66
  var import_client = require("react-dom/client");
67
67
  var import_react_dom2 = require("react-dom");
68
68
 
@@ -4614,7 +4614,7 @@ function ItemInteractionLayer({
4614
4614
  "div",
4615
4615
  {
4616
4616
  "data-ohw-item-drag-surface": "",
4617
- className: "pointer-events-auto absolute inset-0 cursor-grab rounded-lg active:cursor-grabbing",
4617
+ className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
4618
4618
  onPointerDown: (e) => {
4619
4619
  if (e.button !== 0) return;
4620
4620
  onItemPointerDown?.(e);
@@ -6339,9 +6339,155 @@ function shouldUseDevFixtures() {
6339
6339
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6340
6340
  }
6341
6341
 
6342
+ // src/lib/nav-tree.ts
6343
+ var MAX_LEVEL = { nav: 1 };
6344
+ var ROOT = null;
6345
+ function cloneForest(nodes) {
6346
+ return nodes.map((n) => ({ id: n.id, children: cloneForest(n.children) }));
6347
+ }
6348
+ function findNode(nodes, id, parentId = ROOT) {
6349
+ for (let i = 0; i < nodes.length; i++) {
6350
+ const node = nodes[i];
6351
+ if (node.id === id) return { node, parentId, index: i, siblings: nodes };
6352
+ const nested = findNode(node.children, id, node.id);
6353
+ if (nested) return nested;
6354
+ }
6355
+ return null;
6356
+ }
6357
+ function getChildrenOf(nodes, parentId) {
6358
+ if (parentId === ROOT) return nodes;
6359
+ const found = findNode(nodes, parentId);
6360
+ return found ? found.node.children : null;
6361
+ }
6362
+ function isDescendant(nodes, ancestorId, maybeDescendantId) {
6363
+ const found = findNode(nodes, ancestorId);
6364
+ if (!found) return false;
6365
+ return findNode(found.node.children, maybeDescendantId) !== null;
6366
+ }
6367
+ function levelOf(nodes, id) {
6368
+ const loc = findNode(nodes, id);
6369
+ if (!loc) return -1;
6370
+ if (loc.parentId === ROOT) return 0;
6371
+ return 1 + Math.max(0, levelOf(nodes, loc.parentId));
6372
+ }
6373
+ function insertedLevel(nodes, parentId) {
6374
+ if (parentId === ROOT) return 0;
6375
+ const parentLevel = levelOf(nodes, parentId);
6376
+ if (parentLevel < 0) return -1;
6377
+ return parentLevel + 1;
6378
+ }
6379
+ function takeNode(nodes, id) {
6380
+ const forest = cloneForest(nodes);
6381
+ const loc = findNode(forest, id);
6382
+ if (!loc) return null;
6383
+ const [taken] = loc.siblings.splice(loc.index, 1);
6384
+ if (!taken) return null;
6385
+ return { forest, taken };
6386
+ }
6387
+ function insertInto(forest, node, parentId, index) {
6388
+ const siblings = getChildrenOf(forest, parentId);
6389
+ if (!siblings) return false;
6390
+ const clamped = Math.max(0, Math.min(index, siblings.length));
6391
+ siblings.splice(clamped, 0, node);
6392
+ return true;
6393
+ }
6394
+ function normalize(nodes) {
6395
+ const walk = (list) => list.map((n) => {
6396
+ const children = walk(n.children);
6397
+ return { id: n.id, children };
6398
+ });
6399
+ return walk(nodes);
6400
+ }
6401
+ function moveNode(model, draggedId, target) {
6402
+ const { parentId, index: targetIndex } = target;
6403
+ if (parentId === draggedId) return null;
6404
+ if (parentId !== ROOT && isDescendant(model, draggedId, parentId)) return null;
6405
+ const nextLevel = insertedLevel(model, parentId);
6406
+ if (nextLevel < 0 || nextLevel > MAX_LEVEL.nav) return null;
6407
+ const draggedLoc = findNode(model, draggedId);
6408
+ if (!draggedLoc) return null;
6409
+ if (parentId !== ROOT && draggedLoc.node.children.length > 0) return null;
6410
+ const taken = takeNode(model, draggedId);
6411
+ if (!taken) return null;
6412
+ let insertIndex = targetIndex;
6413
+ if (draggedLoc.parentId === parentId && draggedLoc.index < targetIndex) {
6414
+ insertIndex -= 1;
6415
+ }
6416
+ if (!insertInto(taken.forest, taken.taken, parentId, insertIndex)) return null;
6417
+ const next = normalize(taken.forest);
6418
+ if (forestsEqual(model, next)) return null;
6419
+ return next;
6420
+ }
6421
+ function forestsEqual(a, b) {
6422
+ if (a.length !== b.length) return false;
6423
+ for (let i = 0; i < a.length; i++) {
6424
+ const left = a[i];
6425
+ const right = b[i];
6426
+ if (left.id !== right.id) return false;
6427
+ if (!forestsEqual(left.children, right.children)) return false;
6428
+ }
6429
+ return true;
6430
+ }
6431
+ function forestFromFlatOrder(order) {
6432
+ return order.map((id) => ({ id, children: [] }));
6433
+ }
6434
+ function serializeNavOrder(nodes) {
6435
+ const hasNesting = nodes.some((n) => n.children.length > 0);
6436
+ if (!hasNesting) {
6437
+ return JSON.stringify(nodes.map((n) => n.id));
6438
+ }
6439
+ return JSON.stringify(nodes);
6440
+ }
6441
+ function parseNavOrderJson(raw) {
6442
+ if (!raw) return null;
6443
+ try {
6444
+ const parsed = JSON.parse(raw);
6445
+ if (!Array.isArray(parsed)) return null;
6446
+ if (parsed.every((x) => typeof x === "string")) {
6447
+ return forestFromFlatOrder(parsed.filter((x) => typeof x === "string" && x.length > 0));
6448
+ }
6449
+ return parseNavNodeList(parsed);
6450
+ } catch {
6451
+ return null;
6452
+ }
6453
+ }
6454
+ function parseNavNodeList(raw) {
6455
+ const out = [];
6456
+ for (const item of raw) {
6457
+ if (typeof item === "string" && item.length > 0) {
6458
+ out.push({ id: item, children: [] });
6459
+ continue;
6460
+ }
6461
+ if (!item || typeof item !== "object") return null;
6462
+ const rec = item;
6463
+ if (typeof rec.id !== "string" || !rec.id) return null;
6464
+ const children = Array.isArray(rec.children) ? parseNavNodeList(rec.children) : [];
6465
+ if (!children) return null;
6466
+ out.push({ id: rec.id, children });
6467
+ }
6468
+ return out;
6469
+ }
6470
+ function flattenNavOrder(nodes) {
6471
+ const out = [];
6472
+ const walk = (list) => {
6473
+ for (const n of list) {
6474
+ out.push(n.id);
6475
+ walk(n.children);
6476
+ }
6477
+ };
6478
+ walk(nodes);
6479
+ return out;
6480
+ }
6481
+
6342
6482
  // src/lib/nav-items.ts
6343
6483
  var NAV_COUNT_KEY = "__ohw_nav_count";
6344
6484
  var NAV_ORDER_KEY = "__ohw_nav_order";
6485
+ var NAV_HREF_RE = /^nav-(\d+)-href$/;
6486
+ function isNavbarHrefKey(key) {
6487
+ if (!key) return false;
6488
+ if (key === "nav-book-href") return false;
6489
+ return NAV_HREF_RE.test(key);
6490
+ }
6345
6491
  function getLinkHref(el) {
6346
6492
  const key = el.getAttribute("data-ohw-href-key");
6347
6493
  if (key) {
@@ -6378,7 +6524,7 @@ function listNavbarItems() {
6378
6524
  }
6379
6525
  function parseNavIndexFromKey(key) {
6380
6526
  if (!key) return null;
6381
- const match = key.match(/^nav-(\d+)-href$/);
6527
+ const match = key.match(NAV_HREF_RE);
6382
6528
  if (!match) return null;
6383
6529
  return parseInt(match[1], 10);
6384
6530
  }
@@ -6401,17 +6547,6 @@ function getNavbarExistingTargets() {
6401
6547
  function getNavOrderFromDom() {
6402
6548
  return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6403
6549
  }
6404
- function parseNavOrder(content) {
6405
- const raw = content[NAV_ORDER_KEY];
6406
- if (!raw) return null;
6407
- try {
6408
- const parsed = JSON.parse(raw);
6409
- if (!Array.isArray(parsed)) return null;
6410
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6411
- } catch {
6412
- return null;
6413
- }
6414
- }
6415
6550
  function findCounterpartByHrefKey(hrefKey, root) {
6416
6551
  return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6417
6552
  }
@@ -6475,29 +6610,40 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6475
6610
  }
6476
6611
  return primary;
6477
6612
  }
6478
- function getNavbarLinkKeysInContainer(container) {
6613
+ function navOrderKeysEqual(a, b) {
6614
+ if (a.length !== b.length) return false;
6615
+ for (let i = 0; i < a.length; i++) {
6616
+ if (a[i] !== b[i]) return false;
6617
+ }
6618
+ return true;
6619
+ }
6620
+ function getNavOrderFromContainer(container) {
6479
6621
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6480
6622
  }
6481
6623
  function applyNavOrderToContainer(container, order) {
6482
6624
  const desired = order.filter((key, i) => order.indexOf(key) === i);
6483
- const current = getNavbarLinkKeysInContainer(container);
6625
+ const current = getNavOrderFromContainer(container);
6484
6626
  const extras = current.filter((key) => !desired.includes(key));
6485
6627
  const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6486
- if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6487
- return;
6488
- }
6628
+ if (navOrderKeysEqual(current, expected)) return;
6489
6629
  const seen = /* @__PURE__ */ new Set();
6630
+ const orderedEls = [];
6490
6631
  for (const hrefKey of desired) {
6491
6632
  if (seen.has(hrefKey)) continue;
6492
6633
  seen.add(hrefKey);
6493
6634
  const el = findCounterpartByHrefKey(hrefKey, container);
6494
- if (el) container.appendChild(el);
6635
+ if (el && isNavbarLinkItem(el)) orderedEls.push(el);
6495
6636
  }
6496
6637
  for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6497
6638
  if (!isNavbarLinkItem(el)) continue;
6498
6639
  const key = el.getAttribute("data-ohw-href-key");
6499
6640
  if (!key || seen.has(key)) continue;
6500
- container.appendChild(el);
6641
+ orderedEls.push(el);
6642
+ }
6643
+ for (const el of orderedEls) {
6644
+ if (container.lastElementChild !== el) {
6645
+ container.appendChild(el);
6646
+ }
6501
6647
  }
6502
6648
  }
6503
6649
  function applyNavOrder(order) {
@@ -6506,6 +6652,75 @@ function applyNavOrder(order) {
6506
6652
  const drawer = getNavbarDrawerContainer();
6507
6653
  if (drawer) applyNavOrderToContainer(drawer, order);
6508
6654
  }
6655
+ function applyChildrenOrder(parentHrefKey, childKeys, root) {
6656
+ const parent = findCounterpartByHrefKey(parentHrefKey, root);
6657
+ if (!parent) return;
6658
+ const group = parent.closest("[data-ohw-nav-group]");
6659
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6660
+ if (!childrenRoot) return;
6661
+ const current = Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6662
+ if (navOrderKeysEqual(current, childKeys)) return;
6663
+ for (const key of childKeys) {
6664
+ const el = findCounterpartByHrefKey(key, root);
6665
+ if (!el) continue;
6666
+ if (childrenRoot.lastElementChild !== el) {
6667
+ childrenRoot.appendChild(el);
6668
+ }
6669
+ }
6670
+ }
6671
+ function applyNavForest(forest) {
6672
+ if (forestsEqual(forest, getNavForestFromDom())) return;
6673
+ const rootKeys = forest.map((n) => n.id);
6674
+ applyNavOrder(rootKeys);
6675
+ const desktop = getNavbarDesktopContainer();
6676
+ const drawer = getNavbarDrawerContainer();
6677
+ for (const node of forest) {
6678
+ if (node.children.length === 0) continue;
6679
+ const childKeys = node.children.map((c) => c.id);
6680
+ if (desktop) applyChildrenOrder(node.id, childKeys, desktop);
6681
+ if (drawer) applyChildrenOrder(node.id, childKeys, drawer);
6682
+ }
6683
+ }
6684
+ function getNavForestFromDom() {
6685
+ const items = listNavbarItems();
6686
+ const nestedChildKeys = /* @__PURE__ */ new Set();
6687
+ const roots = [];
6688
+ for (const item of items) {
6689
+ const key = item.getAttribute("data-ohw-href-key");
6690
+ if (!key || !isNavbarHrefKey(key)) continue;
6691
+ if (item.closest("[data-ohw-nav-children]")) continue;
6692
+ const group = item.closest("[data-ohw-nav-group]");
6693
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6694
+ const children = [];
6695
+ if (childrenRoot) {
6696
+ for (const child of childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")) {
6697
+ if (!isNavbarLinkItem(child)) continue;
6698
+ const childKey = child.getAttribute("data-ohw-href-key");
6699
+ if (!childKey || !isNavbarHrefKey(childKey)) continue;
6700
+ nestedChildKeys.add(childKey);
6701
+ children.push({ id: childKey, children: [] });
6702
+ }
6703
+ }
6704
+ roots.push({ id: key, children });
6705
+ }
6706
+ if (roots.length === 0) {
6707
+ return forestFromFlatOrder(
6708
+ getNavOrderFromDom().filter((k) => isNavbarHrefKey(k) && !nestedChildKeys.has(k))
6709
+ );
6710
+ }
6711
+ return roots;
6712
+ }
6713
+ function planNavItemMove(hrefKey, parentId, insertIndex) {
6714
+ const model = getNavForestFromDom();
6715
+ const next = moveNode(model, hrefKey, { parentId, index: insertIndex });
6716
+ if (!next) return null;
6717
+ if (forestsEqual(model, next)) return null;
6718
+ return {
6719
+ forest: next,
6720
+ orderJson: serializeNavOrder(next),
6721
+ flatOrder: flattenNavOrder(next)
6722
+ };
6723
+ }
6509
6724
  function collectNavbarIndicesFromContent(content) {
6510
6725
  const indices = /* @__PURE__ */ new Set();
6511
6726
  for (const key of Object.keys(content)) {
@@ -6533,8 +6748,10 @@ function reconcileNavbarItemsFromContent(content) {
6533
6748
  if (!href && !label) continue;
6534
6749
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6535
6750
  }
6536
- const order = parseNavOrder(content);
6537
- if (order?.length) applyNavOrder(order);
6751
+ const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
6752
+ if (orderForest?.length) {
6753
+ applyNavForest(orderForest);
6754
+ }
6538
6755
  }
6539
6756
  function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6540
6757
  const order = getNavOrderFromDom();
@@ -6641,17 +6858,6 @@ function appendChildIfNeeded(parent, child) {
6641
6858
  parent.appendChild(child);
6642
6859
  }
6643
6860
  }
6644
- function insertLinkBeforeNext(col, el, nextEl) {
6645
- if (nextEl) {
6646
- if (el.nextElementSibling !== nextEl) {
6647
- col.insertBefore(el, nextEl);
6648
- }
6649
- return;
6650
- }
6651
- if (col.lastElementChild !== el) {
6652
- col.appendChild(el);
6653
- }
6654
- }
6655
6861
  function applyFooterOrder(order) {
6656
6862
  const container = getFooterLinksContainer();
6657
6863
  if (!container) return;
@@ -6662,13 +6868,32 @@ function applyFooterOrder(order) {
6662
6868
  syncFooterColumnIndices(columns);
6663
6869
  return;
6664
6870
  }
6665
- const colCount = Math.max(columns.length, order.length);
6666
- const working = columns.slice(0, colCount);
6667
- for (let i = 0; i < order.length && i < working.length; i++) {
6668
- appendChildIfNeeded(container, working[i]);
6871
+ const used = /* @__PURE__ */ new Set();
6872
+ const orderedCols = [];
6873
+ for (const colOrder of order) {
6874
+ let matched = null;
6875
+ for (const hrefKey of colOrder) {
6876
+ const link = findFooterLinkByKey(hrefKey);
6877
+ if (!link) continue;
6878
+ const owner = findFooterColumnForLink(link);
6879
+ if (owner && !used.has(owner)) {
6880
+ matched = owner;
6881
+ break;
6882
+ }
6883
+ }
6884
+ if (!matched) {
6885
+ matched = columns.find((col) => !used.has(col)) ?? null;
6886
+ }
6887
+ if (matched) {
6888
+ used.add(matched);
6889
+ orderedCols.push(matched);
6890
+ }
6891
+ }
6892
+ for (const col of columns) {
6893
+ if (!used.has(col)) orderedCols.push(col);
6669
6894
  }
6670
- for (let i = order.length; i < working.length; i++) {
6671
- appendChildIfNeeded(container, working[i]);
6895
+ for (const col of orderedCols) {
6896
+ appendChildIfNeeded(container, col);
6672
6897
  }
6673
6898
  const freshColumns = listFooterColumns();
6674
6899
  syncFooterColumnIndices(freshColumns);
@@ -6676,16 +6901,12 @@ function applyFooterOrder(order) {
6676
6901
  const col = freshColumns[c];
6677
6902
  if (!col) continue;
6678
6903
  const colOrder = order[c];
6679
- for (let j = 0; j < colOrder.length; j++) {
6680
- const hrefKey = colOrder[j];
6904
+ for (const hrefKey of colOrder) {
6681
6905
  const el = findFooterLinkByKey(hrefKey);
6682
6906
  if (!el) continue;
6683
- if (el.parentElement !== col) {
6907
+ if (el.parentElement !== col || col.lastElementChild !== el) {
6684
6908
  col.appendChild(el);
6685
6909
  }
6686
- const nextKey = colOrder[j + 1];
6687
- const nextEl = nextKey ? findFooterLinkByKey(nextKey) : null;
6688
- insertLinkBeforeNext(col, el, nextEl?.parentElement === col ? nextEl : null);
6689
6910
  }
6690
6911
  }
6691
6912
  }
@@ -6887,6 +7108,524 @@ function hitTestColumnDropSlot(clientX, _clientY) {
6887
7108
  return best?.slot ?? null;
6888
7109
  }
6889
7110
 
7111
+ // src/lib/item-drag-interaction.ts
7112
+ function disableNativeHrefDrag(el) {
7113
+ if (el.draggable) el.draggable = false;
7114
+ if (el.getAttribute("draggable") !== "false") {
7115
+ el.setAttribute("draggable", "false");
7116
+ }
7117
+ }
7118
+ function clearTextSelection() {
7119
+ const sel = window.getSelection();
7120
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7121
+ }
7122
+ function armItemPressDrag() {
7123
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7124
+ }
7125
+ function lockItemDuringDrag() {
7126
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7127
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7128
+ clearTextSelection();
7129
+ }
7130
+ function unlockItemDragInteraction() {
7131
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7132
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7133
+ clearTextSelection();
7134
+ }
7135
+ var armFooterPressDrag = armItemPressDrag;
7136
+ var lockFooterDuringDrag = lockItemDuringDrag;
7137
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
7138
+
7139
+ // src/lib/nav-dnd.ts
7140
+ function listReorderableNavItems() {
7141
+ return listNavbarItems().filter((el) => {
7142
+ const key = el.getAttribute("data-ohw-href-key");
7143
+ return isNavbarHrefKey(key) && !el.closest("[data-ohw-nav-children]");
7144
+ });
7145
+ }
7146
+ function listNavChildren(parentHrefKey) {
7147
+ const items = listNavbarItems();
7148
+ const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
7149
+ if (!parent) return [];
7150
+ const group = parent.closest("[data-ohw-nav-group]");
7151
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
7152
+ if (!childrenRoot) return [];
7153
+ return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
7154
+ (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
7155
+ );
7156
+ }
7157
+ function activeNavListContainer() {
7158
+ const desktop = getNavbarDesktopContainer();
7159
+ if (desktop && desktop.getClientRects().length > 0) {
7160
+ const style = window.getComputedStyle(desktop);
7161
+ if (style.display !== "none" && style.visibility !== "hidden") return desktop;
7162
+ }
7163
+ return getNavbarDrawerContainer();
7164
+ }
7165
+ function buildRootNavDropSlots() {
7166
+ const items = listReorderableNavItems();
7167
+ const slots = [];
7168
+ const barThickness = 3;
7169
+ if (items.length === 0) {
7170
+ const container = activeNavListContainer();
7171
+ if (!container) return slots;
7172
+ const rect = container.getBoundingClientRect();
7173
+ slots.push({
7174
+ insertIndex: 0,
7175
+ parentId: null,
7176
+ left: rect.left + rect.width / 2 - barThickness / 2,
7177
+ top: rect.top,
7178
+ width: barThickness,
7179
+ height: Math.max(rect.height, 24),
7180
+ direction: "vertical"
7181
+ });
7182
+ return slots;
7183
+ }
7184
+ for (let i = 0; i <= items.length; i++) {
7185
+ let left;
7186
+ let top;
7187
+ let height;
7188
+ if (i === 0) {
7189
+ const first = items[0].getBoundingClientRect();
7190
+ left = first.left - barThickness / 2;
7191
+ top = first.top;
7192
+ height = first.height;
7193
+ } else if (i === items.length) {
7194
+ const last = items[items.length - 1].getBoundingClientRect();
7195
+ left = last.right - barThickness / 2;
7196
+ top = last.top;
7197
+ height = last.height;
7198
+ } else {
7199
+ const prev = items[i - 1].getBoundingClientRect();
7200
+ const next = items[i].getBoundingClientRect();
7201
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7202
+ top = Math.min(prev.top, next.top);
7203
+ height = Math.max(prev.bottom, next.bottom) - top;
7204
+ }
7205
+ slots.push({
7206
+ insertIndex: i,
7207
+ parentId: null,
7208
+ left,
7209
+ top,
7210
+ width: barThickness,
7211
+ height: Math.max(height, 24),
7212
+ direction: "vertical"
7213
+ });
7214
+ }
7215
+ return slots;
7216
+ }
7217
+ function buildChildNavDropSlots(parentHrefKey) {
7218
+ const children = listNavChildren(parentHrefKey);
7219
+ const slots = [];
7220
+ const barThickness = 3;
7221
+ if (children.length === 0) return slots;
7222
+ for (let i = 0; i <= children.length; i++) {
7223
+ let top;
7224
+ let width;
7225
+ let left;
7226
+ if (i === 0) {
7227
+ const first = children[0].getBoundingClientRect();
7228
+ top = first.top - barThickness / 2;
7229
+ left = first.left;
7230
+ width = first.width;
7231
+ } else if (i === children.length) {
7232
+ const last = children[children.length - 1].getBoundingClientRect();
7233
+ top = last.bottom - barThickness / 2;
7234
+ left = last.left;
7235
+ width = last.width;
7236
+ } else {
7237
+ const prev = children[i - 1].getBoundingClientRect();
7238
+ const next = children[i].getBoundingClientRect();
7239
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
7240
+ left = Math.min(prev.left, next.left);
7241
+ width = Math.max(prev.right, next.right) - left;
7242
+ }
7243
+ slots.push({
7244
+ insertIndex: i,
7245
+ parentId: parentHrefKey,
7246
+ left,
7247
+ top,
7248
+ width: Math.max(width, 40),
7249
+ height: barThickness,
7250
+ direction: "horizontal"
7251
+ });
7252
+ }
7253
+ return slots;
7254
+ }
7255
+ function buildAllNavDropSlots(draggedHrefKey) {
7256
+ const slots = [...buildRootNavDropSlots()];
7257
+ for (const item of listReorderableNavItems()) {
7258
+ const key = item.getAttribute("data-ohw-href-key");
7259
+ if (!key || key === draggedHrefKey) continue;
7260
+ const group = item.closest("[data-ohw-nav-group]");
7261
+ if (!group?.querySelector(":scope > [data-ohw-nav-children]")) continue;
7262
+ slots.push(...buildChildNavDropSlots(key));
7263
+ }
7264
+ const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
7265
+ const parentGroup = dragged?.closest("[data-ohw-nav-children]")?.closest("[data-ohw-nav-group]");
7266
+ const parentTrigger = parentGroup?.querySelector(":scope > [data-ohw-href-key]");
7267
+ const parentKey = parentTrigger?.getAttribute("data-ohw-href-key");
7268
+ if (parentKey && isNavbarHrefKey(parentKey)) {
7269
+ const existing = slots.some((s) => s.parentId === parentKey);
7270
+ if (!existing) slots.push(...buildChildNavDropSlots(parentKey));
7271
+ }
7272
+ return slots;
7273
+ }
7274
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
7275
+ const slots = buildAllNavDropSlots(draggedHrefKey);
7276
+ let best = null;
7277
+ for (const slot of slots) {
7278
+ const cx2 = slot.left + slot.width / 2;
7279
+ const cy = slot.top + slot.height / 2;
7280
+ const dist = slot.direction === "vertical" ? Math.abs(clientX - cx2) + Math.abs(clientY - cy) * 0.25 : Math.abs(clientY - cy) + Math.abs(clientX - cx2) * 0.25;
7281
+ if (!best || dist < best.dist) best = { slot, dist };
7282
+ }
7283
+ return best?.slot ?? null;
7284
+ }
7285
+ function siblingRectsForNavDrag(draggedEl, activeParentId) {
7286
+ if (activeParentId) {
7287
+ return listNavChildren(activeParentId).filter((el) => el !== draggedEl).map((el) => el.getBoundingClientRect());
7288
+ }
7289
+ return listReorderableNavItems().filter((el) => el !== draggedEl).map((el) => el.getBoundingClientRect());
7290
+ }
7291
+
7292
+ // src/useNavItemDrag.ts
7293
+ var import_react8 = require("react");
7294
+ function useNavItemDrag({
7295
+ isEditMode,
7296
+ editContentRef,
7297
+ selectedElRef,
7298
+ activeElRef,
7299
+ footerDragRef,
7300
+ suppressNextClickRef,
7301
+ suppressClickUntilRef,
7302
+ siblingHintElRef,
7303
+ postToParentRef,
7304
+ deselectRef,
7305
+ selectRef,
7306
+ deactivateRef,
7307
+ setLinkPopover,
7308
+ linkPopoverOpenRef,
7309
+ setIsItemDragging,
7310
+ setDraggedItemRect,
7311
+ setSiblingHintRect,
7312
+ setSiblingHintRects,
7313
+ setToolbarRect,
7314
+ getNavigationItemAnchor: getNavigationItemAnchor2,
7315
+ isDragHandleDisabled: isDragHandleDisabled2,
7316
+ isNavbarButton: isNavbarButton3
7317
+ }) {
7318
+ const navDragRef = (0, import_react8.useRef)(null);
7319
+ const [navDropSlots, setNavDropSlots] = (0, import_react8.useState)([]);
7320
+ const [activeNavDropIndex, setActiveNavDropIndex] = (0, import_react8.useState)(null);
7321
+ const navPointerDragRef = (0, import_react8.useRef)(null);
7322
+ const clearNavDragVisuals = (0, import_react8.useCallback)(() => {
7323
+ navDragRef.current = null;
7324
+ setNavDropSlots([]);
7325
+ setActiveNavDropIndex(null);
7326
+ setDraggedItemRect(null);
7327
+ setSiblingHintRects([]);
7328
+ setIsItemDragging(false);
7329
+ unlockItemDragInteraction();
7330
+ }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
7331
+ const refreshNavDragVisuals = (0, import_react8.useCallback)(
7332
+ (session, activeSlot, clientX, clientY) => {
7333
+ setDraggedItemRect(session.draggedEl.getBoundingClientRect());
7334
+ if (typeof clientX === "number" && typeof clientY === "number") {
7335
+ session.lastClientX = clientX;
7336
+ session.lastClientY = clientY;
7337
+ }
7338
+ session.activeSlot = activeSlot;
7339
+ const parentId = activeSlot?.parentId ?? null;
7340
+ setSiblingHintRects(siblingRectsForNavDrag(session.draggedEl, parentId));
7341
+ const slots = buildAllNavDropSlots(session.hrefKey);
7342
+ setNavDropSlots(slots);
7343
+ const activeIdx = activeSlot ? slots.findIndex(
7344
+ (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
7345
+ ) : -1;
7346
+ setActiveNavDropIndex(activeIdx >= 0 ? activeIdx : null);
7347
+ },
7348
+ [setDraggedItemRect, setSiblingHintRects]
7349
+ );
7350
+ const refreshNavDragVisualsRef = (0, import_react8.useRef)(refreshNavDragVisuals);
7351
+ refreshNavDragVisualsRef.current = refreshNavDragVisuals;
7352
+ const commitNavDragRef = (0, import_react8.useRef)(() => {
7353
+ });
7354
+ const beginNavDragRef = (0, import_react8.useRef)(() => {
7355
+ });
7356
+ const beginNavDrag = (0, import_react8.useCallback)(
7357
+ (session) => {
7358
+ const rect = session.draggedEl.getBoundingClientRect();
7359
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
7360
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
7361
+ session.activeSlot = session.activeSlot ?? null;
7362
+ navDragRef.current = session;
7363
+ setIsItemDragging(true);
7364
+ lockItemDuringDrag();
7365
+ siblingHintElRef.current = null;
7366
+ setSiblingHintRect(null);
7367
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
7368
+ setToolbarRect(rect);
7369
+ }
7370
+ const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
7371
+ refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
7372
+ },
7373
+ [
7374
+ refreshNavDragVisuals,
7375
+ selectedElRef,
7376
+ setIsItemDragging,
7377
+ setSiblingHintRect,
7378
+ setToolbarRect,
7379
+ siblingHintElRef
7380
+ ]
7381
+ );
7382
+ beginNavDragRef.current = beginNavDrag;
7383
+ const commitNavDrag = (0, import_react8.useCallback)(
7384
+ (clientX, clientY) => {
7385
+ const session = navDragRef.current;
7386
+ if (!session) {
7387
+ clearNavDragVisuals();
7388
+ return;
7389
+ }
7390
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
7391
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
7392
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
7393
+ const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
7394
+ const wasSelected = session.wasSelected;
7395
+ const hrefKey = session.hrefKey;
7396
+ const applySelectionAfterDrop = () => {
7397
+ if (!wasSelected) {
7398
+ deselectRef.current();
7399
+ return;
7400
+ }
7401
+ const desktop = document.querySelector("[data-ohw-nav-container]");
7402
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
7403
+ 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)}"]`);
7404
+ if (link) {
7405
+ selectRef.current(link);
7406
+ return;
7407
+ }
7408
+ deselectRef.current();
7409
+ };
7410
+ if (planned) {
7411
+ editContentRef.current = {
7412
+ ...editContentRef.current,
7413
+ [NAV_ORDER_KEY]: planned.orderJson
7414
+ };
7415
+ applyNavForest(planned.forest);
7416
+ document.querySelectorAll(
7417
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
7418
+ ).forEach((el) => {
7419
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
7420
+ disableNativeHrefDrag(el);
7421
+ }
7422
+ });
7423
+ postToParentRef.current({
7424
+ type: "ow:change",
7425
+ nodes: [{ key: NAV_ORDER_KEY, text: planned.orderJson }]
7426
+ });
7427
+ applySelectionAfterDrop();
7428
+ clearNavDragVisuals();
7429
+ requestAnimationFrame(() => {
7430
+ if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
7431
+ applyNavForest(planned.forest);
7432
+ }
7433
+ applySelectionAfterDrop();
7434
+ requestAnimationFrame(applySelectionAfterDrop);
7435
+ });
7436
+ return;
7437
+ }
7438
+ applySelectionAfterDrop();
7439
+ clearNavDragVisuals();
7440
+ },
7441
+ [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
7442
+ );
7443
+ commitNavDragRef.current = commitNavDrag;
7444
+ const startNavLinkDrag = (0, import_react8.useCallback)(
7445
+ (anchor, clientX, clientY, wasSelected) => {
7446
+ if (footerDragRef.current) return false;
7447
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
7448
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7449
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return false;
7450
+ beginNavDrag({
7451
+ hrefKey,
7452
+ wasSelected,
7453
+ draggedEl: anchor,
7454
+ lastClientX: clientX,
7455
+ lastClientY: clientY,
7456
+ activeSlot: null
7457
+ });
7458
+ return true;
7459
+ },
7460
+ [beginNavDrag, footerDragRef, isDragHandleDisabled2, isNavbarButton3]
7461
+ );
7462
+ const onNavDragOver = (0, import_react8.useCallback)(
7463
+ (e) => {
7464
+ const session = navDragRef.current;
7465
+ if (!session) return false;
7466
+ e.preventDefault();
7467
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
7468
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7469
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7470
+ return true;
7471
+ },
7472
+ []
7473
+ );
7474
+ (0, import_react8.useEffect)(() => {
7475
+ if (!isEditMode) return;
7476
+ const THRESHOLD = 10;
7477
+ const resolveWasSelected = (el) => {
7478
+ if (selectedElRef.current === el) return true;
7479
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor2(activeElRef.current) : null;
7480
+ return activeAnchor === el;
7481
+ };
7482
+ const onPointerDown = (e) => {
7483
+ if (e.button !== 0) return;
7484
+ if (navDragRef.current || footerDragRef.current) return;
7485
+ const target = e.target;
7486
+ if (!target) return;
7487
+ if (target.closest(
7488
+ '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]'
7489
+ )) {
7490
+ return;
7491
+ }
7492
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
7493
+ const anchor = getNavigationItemAnchor2(target);
7494
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
7495
+ if (!anchor || !isNavbarHrefKey(hrefKey)) return;
7496
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return;
7497
+ armItemPressDrag();
7498
+ navPointerDragRef.current = {
7499
+ el: anchor,
7500
+ startX: e.clientX,
7501
+ startY: e.clientY,
7502
+ pointerId: e.pointerId,
7503
+ wasSelected: resolveWasSelected(anchor),
7504
+ started: false
7505
+ };
7506
+ };
7507
+ const onPointerMove = (e) => {
7508
+ const pending = navPointerDragRef.current;
7509
+ if (!pending) return;
7510
+ if (pending.started) {
7511
+ e.preventDefault();
7512
+ clearTextSelection();
7513
+ const session = navDragRef.current;
7514
+ if (!session) return;
7515
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7516
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7517
+ return;
7518
+ }
7519
+ const dx = e.clientX - pending.startX;
7520
+ const dy = e.clientY - pending.startY;
7521
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
7522
+ e.preventDefault();
7523
+ pending.started = true;
7524
+ clearTextSelection();
7525
+ try {
7526
+ document.body.setPointerCapture(pending.pointerId);
7527
+ } catch {
7528
+ }
7529
+ if (linkPopoverOpenRef.current) setLinkPopover(null);
7530
+ if (activeElRef.current) deactivateRef.current();
7531
+ const key = pending.el.getAttribute("data-ohw-href-key");
7532
+ if (!key) return;
7533
+ beginNavDragRef.current({
7534
+ hrefKey: key,
7535
+ wasSelected: pending.wasSelected,
7536
+ draggedEl: pending.el,
7537
+ lastClientX: e.clientX,
7538
+ lastClientY: e.clientY,
7539
+ activeSlot: null
7540
+ });
7541
+ };
7542
+ const endPointerDrag = (e) => {
7543
+ const pending = navPointerDragRef.current;
7544
+ navPointerDragRef.current = null;
7545
+ try {
7546
+ if (document.body.hasPointerCapture(e.pointerId)) {
7547
+ document.body.releasePointerCapture(e.pointerId);
7548
+ }
7549
+ } catch {
7550
+ }
7551
+ if (!pending?.started) {
7552
+ unlockItemDragInteraction();
7553
+ return;
7554
+ }
7555
+ suppressNextClickRef.current = true;
7556
+ suppressClickUntilRef.current = Date.now() + 500;
7557
+ commitNavDragRef.current(e.clientX, e.clientY);
7558
+ };
7559
+ const blockSelectStart = (e) => {
7560
+ if (navDragRef.current || navPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
7561
+ e.preventDefault();
7562
+ }
7563
+ };
7564
+ const onDragEnd = () => {
7565
+ if (!navDragRef.current) return;
7566
+ suppressNextClickRef.current = true;
7567
+ suppressClickUntilRef.current = Date.now() + 500;
7568
+ commitNavDragRef.current();
7569
+ };
7570
+ document.addEventListener("pointerdown", onPointerDown, true);
7571
+ document.addEventListener("pointermove", onPointerMove, true);
7572
+ document.addEventListener("pointerup", endPointerDrag, true);
7573
+ document.addEventListener("pointercancel", endPointerDrag, true);
7574
+ document.addEventListener("selectstart", blockSelectStart, true);
7575
+ document.addEventListener("dragend", onDragEnd, true);
7576
+ return () => {
7577
+ document.removeEventListener("pointerdown", onPointerDown, true);
7578
+ document.removeEventListener("pointermove", onPointerMove, true);
7579
+ document.removeEventListener("pointerup", endPointerDrag, true);
7580
+ document.removeEventListener("pointercancel", endPointerDrag, true);
7581
+ document.removeEventListener("selectstart", blockSelectStart, true);
7582
+ document.removeEventListener("dragend", onDragEnd, true);
7583
+ unlockItemDragInteraction();
7584
+ };
7585
+ }, [
7586
+ activeElRef,
7587
+ deactivateRef,
7588
+ footerDragRef,
7589
+ getNavigationItemAnchor2,
7590
+ isDragHandleDisabled2,
7591
+ isEditMode,
7592
+ isNavbarButton3,
7593
+ linkPopoverOpenRef,
7594
+ selectedElRef,
7595
+ setLinkPopover,
7596
+ suppressNextClickRef
7597
+ ]);
7598
+ const armNavPressFromChrome = (0, import_react8.useCallback)(
7599
+ (selected, clientX, clientY, pointerId) => {
7600
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
7601
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7602
+ if (isNavbarButton3(selected) || isDragHandleDisabled2(selected)) return false;
7603
+ armItemPressDrag();
7604
+ navPointerDragRef.current = {
7605
+ el: selected,
7606
+ startX: clientX,
7607
+ startY: clientY,
7608
+ pointerId,
7609
+ wasSelected: true,
7610
+ started: false
7611
+ };
7612
+ return true;
7613
+ },
7614
+ [isDragHandleDisabled2, isNavbarButton3]
7615
+ );
7616
+ return {
7617
+ navDragRef,
7618
+ navDropSlots,
7619
+ activeNavDropIndex,
7620
+ startNavLinkDrag,
7621
+ commitNavDrag,
7622
+ onNavDragOver,
7623
+ armNavPressFromChrome,
7624
+ clearNavDragVisuals,
7625
+ refreshNavDragVisualsRef
7626
+ };
7627
+ }
7628
+
6890
7629
  // src/ui/navbar-container-chrome.tsx
6891
7630
  var import_lucide_react10 = require("lucide-react");
6892
7631
  var import_jsx_runtime21 = require("react/jsx-runtime");
@@ -7382,29 +8121,6 @@ function getHrefKeyFromElement(el) {
7382
8121
  if (!key) return null;
7383
8122
  return { anchor, key };
7384
8123
  }
7385
- function disableNativeHrefDrag(el) {
7386
- if (el.draggable) el.draggable = false;
7387
- if (el.getAttribute("draggable") !== "false") {
7388
- el.setAttribute("draggable", "false");
7389
- }
7390
- }
7391
- function clearTextSelection() {
7392
- const sel = window.getSelection();
7393
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
7394
- }
7395
- function armFooterPressDrag() {
7396
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7397
- }
7398
- function lockFooterDuringDrag() {
7399
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7400
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
7401
- clearTextSelection();
7402
- }
7403
- function unlockFooterDragInteraction() {
7404
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7405
- document.documentElement.removeAttribute("data-ohw-item-dragging");
7406
- clearTextSelection();
7407
- }
7408
8124
  function isNavbarButton2(el) {
7409
8125
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
7410
8126
  }
@@ -7552,24 +8268,37 @@ function getNavigationSelectionParent(el) {
7552
8268
  function placeCaretAtPoint(el, x, y) {
7553
8269
  const selection = window.getSelection();
7554
8270
  if (!selection) return;
7555
- if (typeof document.caretRangeFromPoint === "function") {
7556
- const range = document.caretRangeFromPoint(x, y);
7557
- if (range && el.contains(range.startContainer)) {
7558
- selection.removeAllRanges();
7559
- selection.addRange(range);
7560
- return;
8271
+ const overlays = Array.from(
8272
+ document.querySelectorAll(
8273
+ "[data-ohw-item-drag-surface], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-ohw-item-toolbar-anchor]"
8274
+ )
8275
+ );
8276
+ const prevPointerEvents = overlays.map((node) => node.style.pointerEvents);
8277
+ for (const node of overlays) node.style.pointerEvents = "none";
8278
+ try {
8279
+ if (typeof document.caretRangeFromPoint === "function") {
8280
+ const range = document.caretRangeFromPoint(x, y);
8281
+ if (range && el.contains(range.startContainer)) {
8282
+ selection.removeAllRanges();
8283
+ selection.addRange(range);
8284
+ return;
8285
+ }
7561
8286
  }
7562
- }
7563
- const caretPositionFromPoint = document.caretPositionFromPoint;
7564
- if (typeof caretPositionFromPoint === "function") {
7565
- const position = caretPositionFromPoint(x, y);
7566
- if (position && el.contains(position.offsetNode)) {
7567
- const range = document.createRange();
7568
- range.setStart(position.offsetNode, position.offset);
7569
- range.collapse(true);
7570
- selection.removeAllRanges();
7571
- selection.addRange(range);
8287
+ const caretPositionFromPoint = document.caretPositionFromPoint;
8288
+ if (typeof caretPositionFromPoint === "function") {
8289
+ const position = caretPositionFromPoint(x, y);
8290
+ if (position && el.contains(position.offsetNode)) {
8291
+ const range = document.createRange();
8292
+ range.setStart(position.offsetNode, position.offset);
8293
+ range.collapse(true);
8294
+ selection.removeAllRanges();
8295
+ selection.addRange(range);
8296
+ }
7572
8297
  }
8298
+ } finally {
8299
+ overlays.forEach((node, i) => {
8300
+ node.style.pointerEvents = prevPointerEvents[i] ?? "";
8301
+ });
7573
8302
  }
7574
8303
  }
7575
8304
  function selectAllTextInEditable(el) {
@@ -7890,9 +8619,9 @@ function FloatingToolbar({
7890
8619
  showEditLink,
7891
8620
  onEditLink
7892
8621
  }) {
7893
- const localRef = import_react8.default.useRef(null);
7894
- const [measuredW, setMeasuredW] = import_react8.default.useState(330);
7895
- const setRefs = import_react8.default.useCallback(
8622
+ const localRef = import_react9.default.useRef(null);
8623
+ const [measuredW, setMeasuredW] = import_react9.default.useState(330);
8624
+ const setRefs = import_react9.default.useCallback(
7896
8625
  (node) => {
7897
8626
  localRef.current = node;
7898
8627
  if (typeof elRef === "function") elRef(node);
@@ -7904,7 +8633,7 @@ function FloatingToolbar({
7904
8633
  },
7905
8634
  [elRef]
7906
8635
  );
7907
- import_react8.default.useLayoutEffect(() => {
8636
+ import_react9.default.useLayoutEffect(() => {
7908
8637
  const node = localRef.current;
7909
8638
  if (!node) return;
7910
8639
  const update = () => {
@@ -7939,7 +8668,7 @@ function FloatingToolbar({
7939
8668
  pointerEvents: "auto"
7940
8669
  },
7941
8670
  children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CustomToolbar, { children: [
7942
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react8.default.Fragment, { children: [
8671
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react9.default.Fragment, { children: [
7943
8672
  gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CustomToolbarDivider, {}),
7944
8673
  btns.map((btn) => {
7945
8674
  const isActive = activeCommands.has(btn.cmd);
@@ -8045,8 +8774,8 @@ function OhhwellsBridge() {
8045
8774
  const router = (0, import_navigation2.useRouter)();
8046
8775
  const searchParams = (0, import_navigation2.useSearchParams)();
8047
8776
  const isEditMode = isEditSessionActive();
8048
- const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
8049
- (0, import_react8.useEffect)(() => {
8777
+ const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
8778
+ (0, import_react9.useEffect)(() => {
8050
8779
  const figtreeFontId = "ohw-figtree-font";
8051
8780
  if (!document.getElementById(figtreeFontId)) {
8052
8781
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -8074,101 +8803,137 @@ function OhhwellsBridge() {
8074
8803
  const subdomainFromQuery = searchParams.get("subdomain");
8075
8804
  const subdomain = resolveSubdomain(subdomainFromQuery);
8076
8805
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
8077
- const postToParent2 = (0, import_react8.useCallback)((data) => {
8806
+ const postToParent2 = (0, import_react9.useCallback)((data) => {
8078
8807
  if (typeof window !== "undefined" && window.parent !== window) {
8079
8808
  window.parent.postMessage(data, "*");
8080
8809
  }
8081
8810
  }, []);
8082
- const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
8083
- const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
8084
- const activeElRef = (0, import_react8.useRef)(null);
8085
- const selectedElRef = (0, import_react8.useRef)(null);
8086
- const originalContentRef = (0, import_react8.useRef)(null);
8087
- const activeStateElRef = (0, import_react8.useRef)(null);
8088
- const parentScrollRef = (0, import_react8.useRef)(null);
8089
- const visibleViewportRef = (0, import_react8.useRef)(null);
8090
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
8091
- const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
8811
+ const [fetchState, setFetchState] = (0, import_react9.useState)("idle");
8812
+ const autoSaveTimers = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
8813
+ const activeElRef = (0, import_react9.useRef)(null);
8814
+ const selectedElRef = (0, import_react9.useRef)(null);
8815
+ const selectedHrefKeyRef = (0, import_react9.useRef)(null);
8816
+ const selectedFooterColAttrRef = (0, import_react9.useRef)(null);
8817
+ const originalContentRef = (0, import_react9.useRef)(null);
8818
+ const activeStateElRef = (0, import_react9.useRef)(null);
8819
+ const parentScrollRef = (0, import_react9.useRef)(null);
8820
+ const visibleViewportRef = (0, import_react9.useRef)(null);
8821
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react9.useState)(null);
8822
+ const attachVisibleViewport = (0, import_react9.useCallback)((node) => {
8092
8823
  visibleViewportRef.current = node;
8093
8824
  setDialogPortalContainer(node);
8094
8825
  if (node) applyVisibleViewport(node, parentScrollRef.current);
8095
8826
  }, []);
8096
- const toolbarElRef = (0, import_react8.useRef)(null);
8097
- const glowElRef = (0, import_react8.useRef)(null);
8098
- const hoveredImageRef = (0, import_react8.useRef)(null);
8099
- const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
8100
- const dragOverElRef = (0, import_react8.useRef)(null);
8101
- const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
8102
- const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
8103
- const pendingUploadRectRef = (0, import_react8.useRef)(null);
8104
- const hoveredGapRef = (0, import_react8.useRef)(null);
8105
- const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
8106
- const imageShowTimerRef = (0, import_react8.useRef)(null);
8107
- const editStylesRef = (0, import_react8.useRef)(null);
8108
- const activateRef = (0, import_react8.useRef)(() => {
8827
+ const toolbarElRef = (0, import_react9.useRef)(null);
8828
+ const glowElRef = (0, import_react9.useRef)(null);
8829
+ const hoveredImageRef = (0, import_react9.useRef)(null);
8830
+ const hoveredImageHasTextOverlapRef = (0, import_react9.useRef)(false);
8831
+ const dragOverElRef = (0, import_react9.useRef)(null);
8832
+ const [mediaHover, setMediaHover] = (0, import_react9.useState)(null);
8833
+ const [uploadingRects, setUploadingRects] = (0, import_react9.useState)({});
8834
+ const pendingUploadRectRef = (0, import_react9.useRef)(null);
8835
+ const hoveredGapRef = (0, import_react9.useRef)(null);
8836
+ const imageUnhoverTimerRef = (0, import_react9.useRef)(null);
8837
+ const imageShowTimerRef = (0, import_react9.useRef)(null);
8838
+ const editStylesRef = (0, import_react9.useRef)(null);
8839
+ const activateRef = (0, import_react9.useRef)(() => {
8109
8840
  });
8110
- const deactivateRef = (0, import_react8.useRef)(() => {
8841
+ const deactivateRef = (0, import_react9.useRef)(() => {
8111
8842
  });
8112
- const selectRef = (0, import_react8.useRef)(() => {
8843
+ const selectRef = (0, import_react9.useRef)(() => {
8113
8844
  });
8114
- const selectFrameRef = (0, import_react8.useRef)(() => {
8845
+ const selectFrameRef = (0, import_react9.useRef)(() => {
8115
8846
  });
8116
- const deselectRef = (0, import_react8.useRef)(() => {
8847
+ const deselectRef = (0, import_react9.useRef)(() => {
8117
8848
  });
8118
- const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
8849
+ const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
8119
8850
  });
8120
- const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
8851
+ const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
8121
8852
  });
8122
- const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
8853
+ const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
8123
8854
  });
8124
- const postToParentRef = (0, import_react8.useRef)(postToParent2);
8855
+ const postToParentRef = (0, import_react9.useRef)(postToParent2);
8125
8856
  postToParentRef.current = postToParent2;
8126
- const sectionsLoadedRef = (0, import_react8.useRef)(false);
8127
- const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
8128
- const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
8129
- const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
8130
- const toolbarVariantRef = (0, import_react8.useRef)("none");
8857
+ const sectionsLoadedRef = (0, import_react9.useRef)(false);
8858
+ const pendingScheduleConfigRequests = (0, import_react9.useRef)([]);
8859
+ const [toolbarRect, setToolbarRect] = (0, import_react9.useState)(null);
8860
+ const [toolbarVariant, setToolbarVariant] = (0, import_react9.useState)("none");
8861
+ const toolbarVariantRef = (0, import_react9.useRef)("none");
8131
8862
  toolbarVariantRef.current = toolbarVariant;
8132
- const [selectedIsCta, setSelectedIsCta] = (0, import_react8.useState)(false);
8133
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
8134
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
8135
- const [toggleState, setToggleState] = (0, import_react8.useState)(null);
8136
- const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
8137
- const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
8138
- const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
8139
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
8140
- const hoveredNavContainerRef = (0, import_react8.useRef)(null);
8141
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
8142
- const hoveredItemElRef = (0, import_react8.useRef)(null);
8143
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
8144
- const siblingHintElRef = (0, import_react8.useRef)(null);
8145
- const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
8146
- const [siblingHintRects, setSiblingHintRects] = (0, import_react8.useState)([]);
8147
- const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
8148
- const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react8.useState)(false);
8149
- const footerDragRef = (0, import_react8.useRef)(null);
8150
- const [footerDropSlots, setFooterDropSlots] = (0, import_react8.useState)([]);
8151
- const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react8.useState)(null);
8152
- const [draggedItemRect, setDraggedItemRect] = (0, import_react8.useState)(null);
8153
- const footerPointerDragRef = (0, import_react8.useRef)(null);
8154
- const suppressNextClickRef = (0, import_react8.useRef)(false);
8155
- const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
8156
- const linkPopoverSessionRef = (0, import_react8.useRef)(null);
8157
- const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
8158
- const editContentRef = (0, import_react8.useRef)({});
8159
- const [sitePages, setSitePages] = (0, import_react8.useState)([]);
8160
- const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
8161
- const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
8162
- const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
8163
- const linkPopoverPanelRef = (0, import_react8.useRef)(null);
8164
- const linkPopoverOpenRef = (0, import_react8.useRef)(false);
8165
- const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
8863
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react9.useState)(false);
8864
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react9.useState)(null);
8865
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react9.useState)(false);
8866
+ const [toggleState, setToggleState] = (0, import_react9.useState)(null);
8867
+ const [maxBadge, setMaxBadge] = (0, import_react9.useState)(null);
8868
+ const [activeCommands, setActiveCommands] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
8869
+ const [sectionGap, setSectionGap] = (0, import_react9.useState)(null);
8870
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react9.useState)(false);
8871
+ const hoveredNavContainerRef = (0, import_react9.useRef)(null);
8872
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react9.useState)(null);
8873
+ const hoveredItemElRef = (0, import_react9.useRef)(null);
8874
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react9.useState)(null);
8875
+ const siblingHintElRef = (0, import_react9.useRef)(null);
8876
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react9.useState)(null);
8877
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react9.useState)([]);
8878
+ const [isItemDragging, setIsItemDragging] = (0, import_react9.useState)(false);
8879
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react9.useState)(false);
8880
+ const footerDragRef = (0, import_react9.useRef)(null);
8881
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react9.useState)([]);
8882
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react9.useState)(null);
8883
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react9.useState)(null);
8884
+ const footerPointerDragRef = (0, import_react9.useRef)(null);
8885
+ const suppressNextClickRef = (0, import_react9.useRef)(false);
8886
+ const suppressClickUntilRef = (0, import_react9.useRef)(0);
8887
+ const [linkPopover, setLinkPopover] = (0, import_react9.useState)(null);
8888
+ const linkPopoverSessionRef = (0, import_react9.useRef)(null);
8889
+ const addNavAfterAnchorRef = (0, import_react9.useRef)(null);
8890
+ const editContentRef = (0, import_react9.useRef)({});
8891
+ const [sitePages, setSitePages] = (0, import_react9.useState)([]);
8892
+ const [sectionsByPath, setSectionsByPath] = (0, import_react9.useState)({});
8893
+ const sectionsPrefetchGenRef = (0, import_react9.useRef)(0);
8894
+ const setLinkPopoverRef = (0, import_react9.useRef)(setLinkPopover);
8895
+ const linkPopoverPanelRef = (0, import_react9.useRef)(null);
8896
+ const linkPopoverOpenRef = (0, import_react9.useRef)(false);
8897
+ const linkPopoverGraceUntilRef = (0, import_react9.useRef)(0);
8166
8898
  setLinkPopoverRef.current = setLinkPopover;
8167
8899
  linkPopoverSessionRef.current = linkPopover;
8900
+ const {
8901
+ navDragRef,
8902
+ navDropSlots,
8903
+ activeNavDropIndex,
8904
+ startNavLinkDrag,
8905
+ commitNavDrag,
8906
+ onNavDragOver,
8907
+ armNavPressFromChrome,
8908
+ refreshNavDragVisualsRef
8909
+ } = useNavItemDrag({
8910
+ isEditMode,
8911
+ editContentRef,
8912
+ selectedElRef,
8913
+ activeElRef,
8914
+ footerDragRef,
8915
+ suppressNextClickRef,
8916
+ suppressClickUntilRef,
8917
+ siblingHintElRef,
8918
+ postToParentRef,
8919
+ deselectRef,
8920
+ selectRef,
8921
+ deactivateRef,
8922
+ setLinkPopover: () => setLinkPopover(null),
8923
+ linkPopoverOpenRef,
8924
+ setIsItemDragging,
8925
+ setDraggedItemRect,
8926
+ setSiblingHintRect,
8927
+ setSiblingHintRects,
8928
+ setToolbarRect,
8929
+ getNavigationItemAnchor,
8930
+ isDragHandleDisabled,
8931
+ isNavbarButton: isNavbarButton2
8932
+ });
8168
8933
  const bumpLinkPopoverGrace = () => {
8169
8934
  linkPopoverGraceUntilRef.current = Date.now() + 350;
8170
8935
  };
8171
- const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
8936
+ const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
8172
8937
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
8173
8938
  const gen = ++sectionsPrefetchGenRef.current;
8174
8939
  const paths = pages.map((p) => p.path);
@@ -8187,9 +8952,9 @@ function OhhwellsBridge() {
8187
8952
  );
8188
8953
  });
8189
8954
  }, [isEditMode, pathname]);
8190
- const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
8955
+ const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
8191
8956
  runSectionsPrefetchRef.current = runSectionsPrefetch;
8192
- (0, import_react8.useEffect)(() => {
8957
+ (0, import_react9.useEffect)(() => {
8193
8958
  if (!linkPopover) {
8194
8959
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
8195
8960
  return;
@@ -8221,10 +8986,15 @@ function OhhwellsBridge() {
8221
8986
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
8222
8987
  postToParent2({ type: "ow:image-unhover" });
8223
8988
  return () => {
8989
+ postToParent2({ type: "ow:link-modal-lock", locked: false });
8990
+ html.style.overflow = prevHtmlOverflow;
8991
+ body.style.overflow = prevBodyOverflow;
8992
+ document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
8993
+ document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
8224
8994
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
8225
8995
  };
8226
8996
  }, [linkPopover, postToParent2]);
8227
- (0, import_react8.useEffect)(() => {
8997
+ (0, import_react9.useEffect)(() => {
8228
8998
  if (!isEditMode) return;
8229
8999
  const useFixtures = shouldUseDevFixtures();
8230
9000
  if (useFixtures) {
@@ -8248,14 +9018,14 @@ function OhhwellsBridge() {
8248
9018
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
8249
9019
  return () => window.removeEventListener("message", onSitePages);
8250
9020
  }, [isEditMode, postToParent2]);
8251
- (0, import_react8.useEffect)(() => {
9021
+ (0, import_react9.useEffect)(() => {
8252
9022
  if (!isEditMode || shouldUseDevFixtures()) return;
8253
9023
  void loadAllSectionsManifest().then((manifest) => {
8254
9024
  if (Object.keys(manifest).length === 0) return;
8255
9025
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
8256
9026
  });
8257
9027
  }, [isEditMode]);
8258
- (0, import_react8.useEffect)(() => {
9028
+ (0, import_react9.useEffect)(() => {
8259
9029
  const update = () => {
8260
9030
  const el = activeElRef.current ?? selectedElRef.current;
8261
9031
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -8279,10 +9049,10 @@ function OhhwellsBridge() {
8279
9049
  vvp.removeEventListener("resize", update);
8280
9050
  };
8281
9051
  }, []);
8282
- const refreshStateRules = (0, import_react8.useCallback)(() => {
9052
+ const refreshStateRules = (0, import_react9.useCallback)(() => {
8283
9053
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
8284
9054
  }, []);
8285
- const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
9055
+ const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
8286
9056
  const tracker = getSectionsTracker();
8287
9057
  let entries = [];
8288
9058
  try {
@@ -8305,7 +9075,7 @@ function OhhwellsBridge() {
8305
9075
  }
8306
9076
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
8307
9077
  }, [isEditMode]);
8308
- const deactivate = (0, import_react8.useCallback)(() => {
9078
+ const deactivate = (0, import_react9.useCallback)(() => {
8309
9079
  const el = activeElRef.current;
8310
9080
  if (!el) return;
8311
9081
  const key = el.dataset.ohwKey;
@@ -8337,14 +9107,16 @@ function OhhwellsBridge() {
8337
9107
  setToolbarShowEditLink(false);
8338
9108
  postToParent2({ type: "ow:exit-edit" });
8339
9109
  }, [postToParent2]);
8340
- const clearSelectedAttr = (0, import_react8.useCallback)(() => {
9110
+ const clearSelectedAttr = (0, import_react9.useCallback)(() => {
8341
9111
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
8342
9112
  el.removeAttribute("data-ohw-selected");
8343
9113
  });
8344
9114
  }, []);
8345
- const deselect = (0, import_react8.useCallback)(() => {
9115
+ const deselect = (0, import_react9.useCallback)(() => {
8346
9116
  clearSelectedAttr();
8347
9117
  selectedElRef.current = null;
9118
+ selectedHrefKeyRef.current = null;
9119
+ selectedFooterColAttrRef.current = null;
8348
9120
  setSelectedIsCta(false);
8349
9121
  setReorderHrefKey(null);
8350
9122
  setReorderDragDisabled(false);
@@ -8360,12 +9132,56 @@ function OhhwellsBridge() {
8360
9132
  setToolbarVariant("none");
8361
9133
  }
8362
9134
  }, [clearSelectedAttr]);
8363
- const markSelected = (0, import_react8.useCallback)((el) => {
9135
+ const markSelected = (0, import_react9.useCallback)((el) => {
8364
9136
  clearSelectedAttr();
8365
9137
  el.setAttribute("data-ohw-selected", "");
8366
9138
  }, [clearSelectedAttr]);
8367
- const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
9139
+ const resolveHrefKeyElement = (0, import_react9.useCallback)((hrefKey) => {
9140
+ if (isFooterHrefKey(hrefKey)) {
9141
+ return document.querySelector(
9142
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9143
+ );
9144
+ }
9145
+ if (isNavbarHrefKey(hrefKey)) {
9146
+ const desktop = document.querySelector("[data-ohw-nav-container]");
9147
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
9148
+ return 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)}"]`);
9149
+ }
9150
+ return document.querySelector(
9151
+ `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9152
+ );
9153
+ }, []);
9154
+ const resyncSelectedNavigationItem = (0, import_react9.useCallback)(() => {
9155
+ const hrefKey = selectedHrefKeyRef.current;
9156
+ if (hrefKey) {
9157
+ const link = resolveHrefKeyElement(hrefKey);
9158
+ if (!link || !isNavigationItem(link)) return;
9159
+ selectedElRef.current = link;
9160
+ const { key, disabled } = getNavigationItemReorderState(link);
9161
+ setReorderHrefKey(key);
9162
+ setReorderDragDisabled(disabled);
9163
+ setIsFooterFrameSelection(false);
9164
+ setToolbarVariant("link-action");
9165
+ setToolbarRect(link.getBoundingClientRect());
9166
+ return;
9167
+ }
9168
+ const colAttr = selectedFooterColAttrRef.current;
9169
+ if (colAttr != null) {
9170
+ const column = document.querySelector(`[data-ohw-footer-col="${CSS.escape(colAttr)}"]`) ?? listFooterColumns()[Number(colAttr)] ?? null;
9171
+ if (!column || !isNavigationContainer(column)) return;
9172
+ selectedElRef.current = column;
9173
+ setIsFooterFrameSelection(true);
9174
+ setToolbarVariant("select-frame");
9175
+ setToolbarRect(column.getBoundingClientRect());
9176
+ setSiblingHintRects(
9177
+ listFooterColumns().filter((c) => c !== column).map((c) => c.getBoundingClientRect())
9178
+ );
9179
+ }
9180
+ }, [resolveHrefKeyElement]);
9181
+ const reselectNavigationItem = (0, import_react9.useCallback)((navAnchor) => {
8368
9182
  selectedElRef.current = navAnchor;
9183
+ selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9184
+ selectedFooterColAttrRef.current = null;
8369
9185
  markSelected(navAnchor);
8370
9186
  setSelectedIsCta(isNavbarButton2(navAnchor));
8371
9187
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
@@ -8376,7 +9192,7 @@ function OhhwellsBridge() {
8376
9192
  setToolbarShowEditLink(false);
8377
9193
  setActiveCommands(/* @__PURE__ */ new Set());
8378
9194
  }, [markSelected]);
8379
- const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
9195
+ const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
8380
9196
  const el = activeElRef.current;
8381
9197
  if (!el) return;
8382
9198
  const key = el.dataset.ohwKey;
@@ -8403,7 +9219,7 @@ function OhhwellsBridge() {
8403
9219
  postToParent2({ type: "ow:exit-edit" });
8404
9220
  reselectNavigationItem(navAnchor);
8405
9221
  }, [postToParent2, reselectNavigationItem]);
8406
- const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
9222
+ const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
8407
9223
  const items = listNavbarItems();
8408
9224
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
8409
9225
  deselectRef.current();
@@ -8415,7 +9231,7 @@ function OhhwellsBridge() {
8415
9231
  intent: "add-nav"
8416
9232
  });
8417
9233
  }, []);
8418
- const clearFooterDragVisuals = (0, import_react8.useCallback)(() => {
9234
+ const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
8419
9235
  footerDragRef.current = null;
8420
9236
  setSiblingHintRects([]);
8421
9237
  setFooterDropSlots([]);
@@ -8424,7 +9240,7 @@ function OhhwellsBridge() {
8424
9240
  setIsItemDragging(false);
8425
9241
  unlockFooterDragInteraction();
8426
9242
  }, []);
8427
- const refreshFooterDragVisuals = (0, import_react8.useCallback)((session, activeSlot, clientX, clientY) => {
9243
+ const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
8428
9244
  const dragged = session.draggedEl;
8429
9245
  setDraggedItemRect(dragged.getBoundingClientRect());
8430
9246
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -8461,13 +9277,13 @@ function OhhwellsBridge() {
8461
9277
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
8462
9278
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
8463
9279
  }, []);
8464
- const refreshFooterDragVisualsRef = (0, import_react8.useRef)(refreshFooterDragVisuals);
9280
+ const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
8465
9281
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
8466
- const commitFooterDragRef = (0, import_react8.useRef)(() => {
9282
+ const commitFooterDragRef = (0, import_react9.useRef)(() => {
8467
9283
  });
8468
- const beginFooterDragRef = (0, import_react8.useRef)(() => {
9284
+ const beginFooterDragRef = (0, import_react9.useRef)(() => {
8469
9285
  });
8470
- const beginFooterDrag = (0, import_react8.useCallback)(
9286
+ const beginFooterDrag = (0, import_react9.useCallback)(
8471
9287
  (session) => {
8472
9288
  const rect = session.draggedEl.getBoundingClientRect();
8473
9289
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -8487,7 +9303,7 @@ function OhhwellsBridge() {
8487
9303
  [refreshFooterDragVisuals]
8488
9304
  );
8489
9305
  beginFooterDragRef.current = beginFooterDrag;
8490
- const commitFooterDrag = (0, import_react8.useCallback)(
9306
+ const commitFooterDrag = (0, import_react9.useCallback)(
8491
9307
  (clientX, clientY) => {
8492
9308
  const session = footerDragRef.current;
8493
9309
  if (!session) {
@@ -8512,16 +9328,15 @@ function OhhwellsBridge() {
8512
9328
  if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
8513
9329
  movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
8514
9330
  }
8515
- clearFooterDragVisuals();
8516
9331
  const applySelectionAfterDrop = () => {
8517
9332
  if (!wasSelected) {
8518
9333
  deselectRef.current();
8519
9334
  return;
8520
9335
  }
8521
9336
  if (hrefKey) {
8522
- const link = document.querySelector(
8523
- `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
8524
- );
9337
+ selectedHrefKeyRef.current = hrefKey;
9338
+ selectedFooterColAttrRef.current = null;
9339
+ const link = resolveHrefKeyElement(hrefKey);
8525
9340
  if (link && isNavigationItem(link)) {
8526
9341
  selectRef.current(link);
8527
9342
  return;
@@ -8557,9 +9372,6 @@ function OhhwellsBridge() {
8557
9372
  }
8558
9373
  deselectRef.current();
8559
9374
  };
8560
- if (!wasSelected) {
8561
- deselectRef.current();
8562
- }
8563
9375
  if (nextOrder) {
8564
9376
  const orderJson = JSON.stringify(nextOrder);
8565
9377
  editContentRef.current = {
@@ -8572,21 +9384,30 @@ function OhhwellsBridge() {
8572
9384
  type: "ow:change",
8573
9385
  nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
8574
9386
  });
9387
+ applySelectionAfterDrop();
9388
+ clearFooterDragVisuals();
8575
9389
  requestAnimationFrame(() => {
8576
9390
  if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
8577
9391
  applyFooterOrder(nextOrder);
8578
9392
  document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8579
9393
  }
8580
- requestAnimationFrame(applySelectionAfterDrop);
9394
+ applySelectionAfterDrop();
9395
+ requestAnimationFrame(() => {
9396
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9397
+ applyFooterOrder(nextOrder);
9398
+ }
9399
+ resyncSelectedNavigationItem();
9400
+ });
8581
9401
  });
8582
9402
  return;
8583
9403
  }
8584
9404
  applySelectionAfterDrop();
9405
+ clearFooterDragVisuals();
8585
9406
  },
8586
- [clearFooterDragVisuals]
9407
+ [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
8587
9408
  );
8588
9409
  commitFooterDragRef.current = commitFooterDrag;
8589
- const startFooterLinkDrag = (0, import_react8.useCallback)(
9410
+ const startFooterLinkDrag = (0, import_react9.useCallback)(
8590
9411
  (anchor, clientX, clientY, wasSelected) => {
8591
9412
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
8592
9413
  if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
@@ -8607,7 +9428,7 @@ function OhhwellsBridge() {
8607
9428
  },
8608
9429
  [beginFooterDrag]
8609
9430
  );
8610
- const startFooterColumnDrag = (0, import_react8.useCallback)(
9431
+ const startFooterColumnDrag = (0, import_react9.useCallback)(
8611
9432
  (columnEl, clientX, clientY, wasSelected) => {
8612
9433
  const columns = listFooterColumns();
8613
9434
  const idx = columns.indexOf(columnEl);
@@ -8627,7 +9448,7 @@ function OhhwellsBridge() {
8627
9448
  },
8628
9449
  [beginFooterDrag]
8629
9450
  );
8630
- const handleItemDragStart = (0, import_react8.useCallback)(
9451
+ const handleItemDragStart = (0, import_react9.useCallback)(
8631
9452
  (e) => {
8632
9453
  const selected = selectedElRef.current;
8633
9454
  if (!selected) {
@@ -8640,13 +9461,14 @@ function OhhwellsBridge() {
8640
9461
  if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8641
9462
  if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
8642
9463
  }
9464
+ if (startNavLinkDrag(selected, clientX, clientY, true)) return;
8643
9465
  siblingHintElRef.current = null;
8644
9466
  setSiblingHintRect(null);
8645
9467
  setIsItemDragging(true);
8646
9468
  },
8647
- [startFooterColumnDrag, startFooterLinkDrag]
9469
+ [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
8648
9470
  );
8649
- const handleItemDragEnd = (0, import_react8.useCallback)(
9471
+ const handleItemDragEnd = (0, import_react9.useCallback)(
8650
9472
  (e) => {
8651
9473
  if (footerDragRef.current) {
8652
9474
  const x = e?.clientX;
@@ -8658,11 +9480,21 @@ function OhhwellsBridge() {
8658
9480
  }
8659
9481
  return;
8660
9482
  }
9483
+ if (navDragRef.current) {
9484
+ const x = e?.clientX;
9485
+ const y = e?.clientY;
9486
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9487
+ commitNavDrag(x, y);
9488
+ } else {
9489
+ commitNavDrag();
9490
+ }
9491
+ return;
9492
+ }
8661
9493
  setIsItemDragging(false);
8662
9494
  },
8663
- [commitFooterDrag]
9495
+ [commitFooterDrag, commitNavDrag, navDragRef]
8664
9496
  );
8665
- const handleItemChromePointerDown = (0, import_react8.useCallback)((e) => {
9497
+ const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
8666
9498
  if (e.button !== 0) return;
8667
9499
  const selected = selectedElRef.current;
8668
9500
  if (!selected) return;
@@ -8690,10 +9522,12 @@ function OhhwellsBridge() {
8690
9522
  wasSelected: true,
8691
9523
  started: false
8692
9524
  };
9525
+ return;
8693
9526
  }
8694
- }, []);
8695
- const handleItemChromeClick = (0, import_react8.useCallback)((clientX, clientY) => {
8696
- if (suppressNextClickRef.current) {
9527
+ if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
9528
+ }, [armNavPressFromChrome]);
9529
+ const handleItemChromeClick = (0, import_react9.useCallback)((clientX, clientY) => {
9530
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
8697
9531
  suppressNextClickRef.current = false;
8698
9532
  return;
8699
9533
  }
@@ -8705,10 +9539,12 @@ function OhhwellsBridge() {
8705
9539
  }, []);
8706
9540
  reselectNavigationItemRef.current = reselectNavigationItem;
8707
9541
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
8708
- const select = (0, import_react8.useCallback)((anchor) => {
9542
+ const select = (0, import_react9.useCallback)((anchor) => {
8709
9543
  if (!isNavigationItem(anchor)) return;
8710
9544
  if (activeElRef.current) deactivate();
8711
9545
  selectedElRef.current = anchor;
9546
+ selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
9547
+ selectedFooterColAttrRef.current = null;
8712
9548
  markSelected(anchor);
8713
9549
  setSelectedIsCta(isNavbarButton2(anchor));
8714
9550
  clearHrefKeyHover(anchor);
@@ -8729,11 +9565,13 @@ function OhhwellsBridge() {
8729
9565
  setToolbarShowEditLink(false);
8730
9566
  setActiveCommands(/* @__PURE__ */ new Set());
8731
9567
  }, [deactivate, markSelected]);
8732
- const selectFrame = (0, import_react8.useCallback)((el) => {
9568
+ const selectFrame = (0, import_react9.useCallback)((el) => {
8733
9569
  if (!isNavigationContainer(el)) return;
8734
9570
  if (activeElRef.current) deactivate();
8735
9571
  const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8736
9572
  selectedElRef.current = el;
9573
+ selectedHrefKeyRef.current = null;
9574
+ selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
8737
9575
  markSelected(el);
8738
9576
  setSelectedIsCta(false);
8739
9577
  clearHrefKeyHover(el);
@@ -8755,10 +9593,12 @@ function OhhwellsBridge() {
8755
9593
  setToolbarShowEditLink(false);
8756
9594
  setActiveCommands(/* @__PURE__ */ new Set());
8757
9595
  }, [deactivate, markSelected]);
8758
- const activate = (0, import_react8.useCallback)((el, options) => {
9596
+ const activate = (0, import_react9.useCallback)((el, options) => {
8759
9597
  if (activeElRef.current === el) return;
8760
9598
  clearSelectedAttr();
8761
9599
  selectedElRef.current = null;
9600
+ selectedHrefKeyRef.current = null;
9601
+ selectedFooterColAttrRef.current = null;
8762
9602
  setSelectedIsCta(false);
8763
9603
  deactivate();
8764
9604
  if (hoveredImageRef.current) {
@@ -8780,7 +9620,11 @@ function OhhwellsBridge() {
8780
9620
  originalContentRef.current = el.innerHTML;
8781
9621
  el.focus({ preventScroll: true });
8782
9622
  if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8783
- placeCaretAtPoint(el, options.caretX, options.caretY);
9623
+ const { caretX, caretY } = options;
9624
+ placeCaretAtPoint(el, caretX, caretY);
9625
+ requestAnimationFrame(() => {
9626
+ if (activeElRef.current === el) placeCaretAtPoint(el, caretX, caretY);
9627
+ });
8784
9628
  } else {
8785
9629
  const selection = window.getSelection();
8786
9630
  if (selection && selection.rangeCount === 0) {
@@ -8810,7 +9654,7 @@ function OhhwellsBridge() {
8810
9654
  selectRef.current = select;
8811
9655
  selectFrameRef.current = selectFrame;
8812
9656
  deselectRef.current = deselect;
8813
- (0, import_react8.useLayoutEffect)(() => {
9657
+ (0, import_react9.useLayoutEffect)(() => {
8814
9658
  if (!subdomain || isEditMode) {
8815
9659
  setFetchState("done");
8816
9660
  return;
@@ -8880,7 +9724,7 @@ function OhhwellsBridge() {
8880
9724
  cancelled = true;
8881
9725
  };
8882
9726
  }, [subdomain, isEditMode]);
8883
- (0, import_react8.useEffect)(() => {
9727
+ (0, import_react9.useEffect)(() => {
8884
9728
  if (!subdomain || isEditMode) return;
8885
9729
  let debounceTimer = null;
8886
9730
  let observer = null;
@@ -8930,16 +9774,16 @@ function OhhwellsBridge() {
8930
9774
  if (debounceTimer) clearTimeout(debounceTimer);
8931
9775
  };
8932
9776
  }, [subdomain, isEditMode, pathname]);
8933
- (0, import_react8.useLayoutEffect)(() => {
9777
+ (0, import_react9.useLayoutEffect)(() => {
8934
9778
  const el = document.getElementById("ohw-loader");
8935
9779
  if (!el) return;
8936
9780
  const visible = Boolean(subdomain) && fetchState !== "done";
8937
9781
  el.style.display = visible ? "flex" : "none";
8938
9782
  }, [subdomain, fetchState]);
8939
- (0, import_react8.useEffect)(() => {
9783
+ (0, import_react9.useEffect)(() => {
8940
9784
  postToParent2({ type: "ow:navigation", path: pathname });
8941
9785
  }, [pathname, postToParent2]);
8942
- (0, import_react8.useEffect)(() => {
9786
+ (0, import_react9.useEffect)(() => {
8943
9787
  if (!isEditMode) return;
8944
9788
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8945
9789
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -8947,7 +9791,7 @@ function OhhwellsBridge() {
8947
9791
  deselectRef.current();
8948
9792
  deactivateRef.current();
8949
9793
  }, [pathname, isEditMode]);
8950
- (0, import_react8.useEffect)(() => {
9794
+ (0, import_react9.useEffect)(() => {
8951
9795
  const contentForNav = () => {
8952
9796
  if (isEditMode) return editContentRef.current;
8953
9797
  if (!subdomain) return {};
@@ -8965,7 +9809,7 @@ function OhhwellsBridge() {
8965
9809
  }
8966
9810
  };
8967
9811
  const run = () => {
8968
- if (footerDragRef.current || applying) return;
9812
+ if (footerDragRef.current || navDragRef.current || applying) return;
8969
9813
  applying = true;
8970
9814
  observer?.disconnect();
8971
9815
  try {
@@ -8977,6 +9821,14 @@ function OhhwellsBridge() {
8977
9821
  disableNativeHrefDrag(el);
8978
9822
  }
8979
9823
  });
9824
+ document.querySelectorAll(
9825
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
9826
+ ).forEach((el) => {
9827
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
9828
+ disableNativeHrefDrag(el);
9829
+ }
9830
+ });
9831
+ resyncSelectedNavigationItem();
8980
9832
  if (isEditMode) syncNavigationDragCursorAttrs();
8981
9833
  } finally {
8982
9834
  applying = false;
@@ -8996,8 +9848,8 @@ function OhhwellsBridge() {
8996
9848
  if (rafId != null) cancelAnimationFrame(rafId);
8997
9849
  observer?.disconnect();
8998
9850
  };
8999
- }, [isEditMode, pathname, subdomain, fetchState]);
9000
- (0, import_react8.useEffect)(() => {
9851
+ }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
9852
+ (0, import_react9.useEffect)(() => {
9001
9853
  if (!isEditMode) return;
9002
9854
  const measure = () => {
9003
9855
  const h = document.body.scrollHeight;
@@ -9021,7 +9873,7 @@ function OhhwellsBridge() {
9021
9873
  window.removeEventListener("resize", handleResize);
9022
9874
  };
9023
9875
  }, [pathname, isEditMode, postToParent2]);
9024
- (0, import_react8.useEffect)(() => {
9876
+ (0, import_react9.useEffect)(() => {
9025
9877
  if (!subdomainFromQuery || isEditMode) return;
9026
9878
  const handleClick = (e) => {
9027
9879
  const anchor = e.target.closest("a");
@@ -9037,7 +9889,7 @@ function OhhwellsBridge() {
9037
9889
  document.addEventListener("click", handleClick, true);
9038
9890
  return () => document.removeEventListener("click", handleClick, true);
9039
9891
  }, [subdomainFromQuery, isEditMode, router]);
9040
- (0, import_react8.useEffect)(() => {
9892
+ (0, import_react9.useEffect)(() => {
9041
9893
  if (!isEditMode) {
9042
9894
  editStylesRef.current?.base.remove();
9043
9895
  editStylesRef.current?.forceHover.remove();
@@ -9085,7 +9937,10 @@ function OhhwellsBridge() {
9085
9937
  cursor: text !important;
9086
9938
  }
9087
9939
  /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
9088
- footer [data-ohw-href-key] {
9940
+ footer [data-ohw-href-key],
9941
+ nav [data-ohw-href-key],
9942
+ [data-ohw-nav-container] [data-ohw-href-key],
9943
+ [data-ohw-nav-drawer] [data-ohw-href-key] {
9089
9944
  -webkit-user-drag: none !important;
9090
9945
  }
9091
9946
  footer [data-ohw-footer-col]:hover {
@@ -9102,7 +9957,9 @@ function OhhwellsBridge() {
9102
9957
  -webkit-user-select: none !important;
9103
9958
  }
9104
9959
  html[data-ohw-item-dragging] footer,
9105
- html[data-ohw-item-dragging] footer * {
9960
+ html[data-ohw-item-dragging] footer *,
9961
+ html[data-ohw-item-dragging] nav,
9962
+ html[data-ohw-item-dragging] nav * {
9106
9963
  user-select: none !important;
9107
9964
  -webkit-user-select: none !important;
9108
9965
  pointer-events: none !important;
@@ -9110,13 +9967,6 @@ function OhhwellsBridge() {
9110
9967
  html[data-ohw-item-dragging] {
9111
9968
  cursor: grabbing !important;
9112
9969
  }
9113
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not(nav [data-ohw-href-key] *):not(footer [data-ohw-href-key] *) { cursor: text !important; }
9114
- nav [data-ohw-href-key],
9115
- nav [data-ohw-href-key] [data-ohw-editable]:not([contenteditable]),
9116
- footer [data-ohw-href-key],
9117
- footer [data-ohw-href-key] [data-ohw-editable]:not([contenteditable]) {
9118
- cursor: grab !important;
9119
- }
9120
9970
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
9121
9971
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
9122
9972
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -9173,7 +10023,7 @@ function OhhwellsBridge() {
9173
10023
  syncNavigationDragCursorAttrs();
9174
10024
  refreshStateRules();
9175
10025
  const handleClick = (e) => {
9176
- if (suppressNextClickRef.current) {
10026
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
9177
10027
  suppressNextClickRef.current = false;
9178
10028
  e.preventDefault();
9179
10029
  e.stopPropagation();
@@ -9910,6 +10760,7 @@ function OhhwellsBridge() {
9910
10760
  refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
9911
10761
  return;
9912
10762
  }
10763
+ if (onNavDragOver(e)) return;
9913
10764
  e.preventDefault();
9914
10765
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9915
10766
  if (!el) {
@@ -10286,6 +11137,11 @@ function OhhwellsBridge() {
10286
11137
  const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
10287
11138
  refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
10288
11139
  }
11140
+ if (navDragRef.current) {
11141
+ const session = navDragRef.current;
11142
+ const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
11143
+ refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11144
+ }
10289
11145
  if (hoveredImageRef.current) {
10290
11146
  const el = hoveredImageRef.current;
10291
11147
  const r2 = el.getBoundingClientRect();
@@ -10614,7 +11470,7 @@ function OhhwellsBridge() {
10614
11470
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
10615
11471
  };
10616
11472
  }, [isEditMode, refreshStateRules]);
10617
- (0, import_react8.useEffect)(() => {
11473
+ (0, import_react9.useEffect)(() => {
10618
11474
  if (!isEditMode) return;
10619
11475
  const THRESHOLD = 10;
10620
11476
  const resolveWasSelected = (el) => {
@@ -10624,7 +11480,7 @@ function OhhwellsBridge() {
10624
11480
  };
10625
11481
  const onPointerDown = (e) => {
10626
11482
  if (e.button !== 0) return;
10627
- if (suppressNextClickRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
11483
+ if (suppressNextClickRef.current && Date.now() >= suppressClickUntilRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
10628
11484
  suppressNextClickRef.current = false;
10629
11485
  }
10630
11486
  if (footerDragRef.current) return;
@@ -10738,6 +11594,7 @@ function OhhwellsBridge() {
10738
11594
  return;
10739
11595
  }
10740
11596
  suppressNextClickRef.current = true;
11597
+ suppressClickUntilRef.current = Date.now() + 500;
10741
11598
  commitFooterDragRef.current(e.clientX, e.clientY);
10742
11599
  };
10743
11600
  const blockSelectStart = (e) => {
@@ -10747,6 +11604,8 @@ function OhhwellsBridge() {
10747
11604
  };
10748
11605
  const onDragEnd = (_e) => {
10749
11606
  if (!footerDragRef.current) return;
11607
+ suppressNextClickRef.current = true;
11608
+ suppressClickUntilRef.current = Date.now() + 500;
10750
11609
  commitFooterDragRef.current();
10751
11610
  };
10752
11611
  document.addEventListener("pointerdown", onPointerDown, true);
@@ -10765,7 +11624,7 @@ function OhhwellsBridge() {
10765
11624
  unlockFooterDragInteraction();
10766
11625
  };
10767
11626
  }, [isEditMode]);
10768
- (0, import_react8.useEffect)(() => {
11627
+ (0, import_react9.useEffect)(() => {
10769
11628
  const handler = (e) => {
10770
11629
  if (e.data?.type !== "ow:request-schedule-config") return;
10771
11630
  const insertAfterVal = e.data.insertAfter;
@@ -10781,7 +11640,7 @@ function OhhwellsBridge() {
10781
11640
  window.addEventListener("message", handler);
10782
11641
  return () => window.removeEventListener("message", handler);
10783
11642
  }, [processConfigRequest]);
10784
- (0, import_react8.useEffect)(() => {
11643
+ (0, import_react9.useEffect)(() => {
10785
11644
  if (!isEditMode) return;
10786
11645
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
10787
11646
  el.removeAttribute("data-ohw-active-state");
@@ -10816,19 +11675,19 @@ function OhhwellsBridge() {
10816
11675
  clearTimeout(timer);
10817
11676
  };
10818
11677
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
10819
- (0, import_react8.useEffect)(() => {
11678
+ (0, import_react9.useEffect)(() => {
10820
11679
  scrollToHashSectionWhenReady();
10821
11680
  const onHashChange = () => scrollToHashSectionWhenReady();
10822
11681
  window.addEventListener("hashchange", onHashChange);
10823
11682
  return () => window.removeEventListener("hashchange", onHashChange);
10824
11683
  }, [pathname]);
10825
- const handleCommand = (0, import_react8.useCallback)((cmd) => {
11684
+ const handleCommand = (0, import_react9.useCallback)((cmd) => {
10826
11685
  document.execCommand(cmd, false);
10827
11686
  activeElRef.current?.focus();
10828
11687
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
10829
11688
  refreshActiveCommandsRef.current();
10830
11689
  }, []);
10831
- const handleStateChange = (0, import_react8.useCallback)((state) => {
11690
+ const handleStateChange = (0, import_react9.useCallback)((state) => {
10832
11691
  if (!activeStateElRef.current) return;
10833
11692
  const el = activeStateElRef.current;
10834
11693
  if (state === "Default") {
@@ -10841,11 +11700,11 @@ function OhhwellsBridge() {
10841
11700
  }
10842
11701
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
10843
11702
  }, [deactivate]);
10844
- const closeLinkPopover = (0, import_react8.useCallback)(() => {
11703
+ const closeLinkPopover = (0, import_react9.useCallback)(() => {
10845
11704
  addNavAfterAnchorRef.current = null;
10846
11705
  setLinkPopover(null);
10847
11706
  }, []);
10848
- const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
11707
+ const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
10849
11708
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
10850
11709
  if (!hrefCtx) return;
10851
11710
  bumpLinkPopoverGrace();
@@ -10856,7 +11715,7 @@ function OhhwellsBridge() {
10856
11715
  });
10857
11716
  deactivate();
10858
11717
  }, [deactivate]);
10859
- const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
11718
+ const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
10860
11719
  const anchor = selectedElRef.current;
10861
11720
  if (!anchor) return;
10862
11721
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -10869,7 +11728,7 @@ function OhhwellsBridge() {
10869
11728
  });
10870
11729
  deselect();
10871
11730
  }, [deselect]);
10872
- const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
11731
+ const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
10873
11732
  (target) => {
10874
11733
  const session = linkPopoverSessionRef.current;
10875
11734
  if (!session) return;
@@ -10925,7 +11784,7 @@ function OhhwellsBridge() {
10925
11784
  },
10926
11785
  [postToParent2, sitePages, sectionsByPath]
10927
11786
  );
10928
- const handleMediaReplace = (0, import_react8.useCallback)(
11787
+ const handleMediaReplace = (0, import_react9.useCallback)(
10929
11788
  (key) => {
10930
11789
  if (mediaHover?.key === key) {
10931
11790
  pendingUploadRectRef.current = { key, rect: mediaHover.rect };
@@ -10934,7 +11793,7 @@ function OhhwellsBridge() {
10934
11793
  },
10935
11794
  [postToParent2, mediaHover]
10936
11795
  );
10937
- const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
11796
+ const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
10938
11797
  setUploadingRects((prev) => {
10939
11798
  if (!(key in prev)) return prev;
10940
11799
  const next = { ...prev };
@@ -10942,7 +11801,7 @@ function OhhwellsBridge() {
10942
11801
  return next;
10943
11802
  });
10944
11803
  }, []);
10945
- const handleVideoSettingsChange = (0, import_react8.useCallback)(
11804
+ const handleVideoSettingsChange = (0, import_react9.useCallback)(
10946
11805
  (key, settings) => {
10947
11806
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
10948
11807
  const video = getVideoEl(el);
@@ -10994,7 +11853,7 @@ function OhhwellsBridge() {
10994
11853
  ),
10995
11854
  siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
10996
11855
  siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
10997
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
11856
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
10998
11857
  isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10999
11858
  "div",
11000
11859
  {
@@ -11005,17 +11864,45 @@ function OhhwellsBridge() {
11005
11864
  width: slot.width,
11006
11865
  height: slot.height
11007
11866
  },
11008
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DropIndicator, { direction: slot.direction, state: activeFooterDropIndex === i ? "dragActive" : "dragIdle", className: slot.direction === "horizontal" ? "!h-full !w-full" : "!h-full !w-full" })
11867
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11868
+ DropIndicator,
11869
+ {
11870
+ direction: slot.direction,
11871
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
11872
+ className: "!h-full !w-full"
11873
+ }
11874
+ )
11009
11875
  },
11010
11876
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
11011
11877
  )),
11878
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11879
+ "div",
11880
+ {
11881
+ className: "pointer-events-none fixed z-2147483646",
11882
+ style: {
11883
+ left: slot.left,
11884
+ top: slot.top,
11885
+ width: slot.width,
11886
+ height: slot.height
11887
+ },
11888
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11889
+ DropIndicator,
11890
+ {
11891
+ direction: slot.direction,
11892
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
11893
+ className: "!h-full !w-full"
11894
+ }
11895
+ )
11896
+ },
11897
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
11898
+ )),
11012
11899
  hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
11013
11900
  toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
11014
11901
  hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
11015
11902
  toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11016
11903
  ItemInteractionLayer,
11017
11904
  {
11018
- rect: isItemDragging && draggedItemRect && footerDragRef.current?.wasSelected ? draggedItemRect : toolbarRect,
11905
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
11019
11906
  elRef: glowElRef,
11020
11907
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
11021
11908
  showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
@@ -11039,8 +11926,27 @@ function OhhwellsBridge() {
11039
11926
  }
11040
11927
  ),
11041
11928
  toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11042
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(EditGlowChrome, { rect: toolbarRect, elRef: glowElRef, reorderHrefKey, dragDisabled: reorderDragDisabled }),
11043
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands, showEditLink, onEditLink: openLinkPopoverForActive })
11929
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11930
+ EditGlowChrome,
11931
+ {
11932
+ rect: toolbarRect,
11933
+ elRef: glowElRef,
11934
+ reorderHrefKey,
11935
+ dragDisabled: reorderDragDisabled
11936
+ }
11937
+ ),
11938
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11939
+ FloatingToolbar,
11940
+ {
11941
+ rect: toolbarRect,
11942
+ parentScroll: parentScrollRef.current,
11943
+ elRef: toolbarElRef,
11944
+ onCommand: handleCommand,
11945
+ activeCommands,
11946
+ showEditLink,
11947
+ onEditLink: openLinkPopoverForActive
11948
+ }
11949
+ )
11044
11950
  ] }),
11045
11951
  maxBadge && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
11046
11952
  "div",
@@ -11068,29 +11974,61 @@ function OhhwellsBridge() {
11068
11974
  ]
11069
11975
  }
11070
11976
  ),
11071
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(StateToggle, { rect: toggleState.rect, activeState: toggleState.activeState, states: toggleState.states, onStateChange: handleStateChange }),
11072
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { "data-ohw-section-insert-line": "", className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none", style: { top: sectionGap.y, transform: "translateY(-50%)" }, children: [
11073
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
11074
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11075
- Badge,
11076
- {
11077
- className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
11078
- onClick: () => {
11079
- window.parent.postMessage(
11080
- {
11081
- type: "ow:add-section",
11082
- insertAfter: sectionGap.insertAfter,
11083
- insertBefore: sectionGap.insertBefore
11977
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11978
+ StateToggle,
11979
+ {
11980
+ rect: toggleState.rect,
11981
+ activeState: toggleState.activeState,
11982
+ states: toggleState.states,
11983
+ onStateChange: handleStateChange
11984
+ }
11985
+ ),
11986
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
11987
+ "div",
11988
+ {
11989
+ "data-ohw-section-insert-line": "",
11990
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
11991
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
11992
+ children: [
11993
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
11994
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11995
+ Badge,
11996
+ {
11997
+ className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
11998
+ onClick: () => {
11999
+ window.parent.postMessage(
12000
+ {
12001
+ type: "ow:add-section",
12002
+ insertAfter: sectionGap.insertAfter,
12003
+ insertBefore: sectionGap.insertBefore
12004
+ },
12005
+ "*"
12006
+ );
11084
12007
  },
11085
- "*"
11086
- );
11087
- },
11088
- children: "Add Section"
11089
- }
11090
- ),
11091
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
11092
- ] }),
11093
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(LinkPopover, { panelRef: linkPopoverPanelRef, portalContainer: dialogPortalContainer, open: true, mode: linkPopover.mode ?? "edit", pages: sitePages, sections: currentSections, sectionsByPath, initialTarget: linkPopover.target, existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [], onClose: closeLinkPopover, onSubmit: handleLinkPopoverSubmit }, linkPopover.key) : null
12008
+ children: "Add Section"
12009
+ }
12010
+ ),
12011
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12012
+ ]
12013
+ }
12014
+ ),
12015
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12016
+ LinkPopover,
12017
+ {
12018
+ panelRef: linkPopoverPanelRef,
12019
+ portalContainer: dialogPortalContainer,
12020
+ open: true,
12021
+ mode: linkPopover.mode ?? "edit",
12022
+ pages: sitePages,
12023
+ sections: currentSections,
12024
+ sectionsByPath,
12025
+ initialTarget: linkPopover.target,
12026
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
12027
+ onClose: closeLinkPopover,
12028
+ onSubmit: handleLinkPopoverSubmit
12029
+ },
12030
+ linkPopover.key
12031
+ ) : null
11094
12032
  ] }),
11095
12033
  bridgeRoot
11096
12034
  ) : null;