@ohhwells/bridge 0.1.42-next.84 → 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
 
@@ -4506,7 +4506,7 @@ function getChromeStyle(state) {
4506
4506
  case "dragging":
4507
4507
  return {
4508
4508
  border: `2px solid ${PRIMARY}`,
4509
- boxShadow: DRAG_SHADOW
4509
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4510
4510
  };
4511
4511
  default:
4512
4512
  return {};
@@ -4575,6 +4575,8 @@ function ItemInteractionLayer({
4575
4575
  dragHandleLabel = "Reorder item",
4576
4576
  onDragHandleDragStart,
4577
4577
  onDragHandleDragEnd,
4578
+ onItemPointerDown,
4579
+ onItemClick,
4578
4580
  chromeGap = SELECTION_CHROME_GAP,
4579
4581
  className
4580
4582
  }) {
@@ -4582,7 +4584,8 @@ function ItemInteractionLayer({
4582
4584
  const isActive = state === "active-top" || state === "active-bottom";
4583
4585
  const isDragging = state === "dragging";
4584
4586
  const showToolbar = isActive && toolbar;
4585
- const showDragHandle = isActive && showHandle && !isDragging;
4587
+ const showDragHandle = (isActive || isDragging) && showHandle;
4588
+ const itemDragEnabled = isActive && showHandle && !isDragging && !dragDisabled;
4586
4589
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4587
4590
  "div",
4588
4591
  {
@@ -4607,11 +4610,28 @@ function ItemInteractionLayer({
4607
4610
  style: getChromeStyle(state)
4608
4611
  }
4609
4612
  ),
4613
+ itemDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4614
+ "div",
4615
+ {
4616
+ "data-ohw-item-drag-surface": "",
4617
+ className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
4618
+ onPointerDown: (e) => {
4619
+ if (e.button !== 0) return;
4620
+ onItemPointerDown?.(e);
4621
+ },
4622
+ onClick: (e) => {
4623
+ e.preventDefault();
4624
+ e.stopPropagation();
4625
+ onItemClick?.(e.clientX, e.clientY);
4626
+ }
4627
+ }
4628
+ ),
4610
4629
  showDragHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4611
4630
  "div",
4612
4631
  {
4613
4632
  "data-ohw-drag-handle-container": "",
4614
- className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4633
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4634
+ style: isDragging ? { visibility: "hidden" } : void 0,
4615
4635
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4616
4636
  DragHandle,
4617
4637
  {
@@ -6319,9 +6339,155 @@ function shouldUseDevFixtures() {
6319
6339
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6320
6340
  }
6321
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
+
6322
6482
  // src/lib/nav-items.ts
6323
6483
  var NAV_COUNT_KEY = "__ohw_nav_count";
6324
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
+ }
6325
6491
  function getLinkHref(el) {
6326
6492
  const key = el.getAttribute("data-ohw-href-key");
6327
6493
  if (key) {
@@ -6358,7 +6524,7 @@ function listNavbarItems() {
6358
6524
  }
6359
6525
  function parseNavIndexFromKey(key) {
6360
6526
  if (!key) return null;
6361
- const match = key.match(/^nav-(\d+)-href$/);
6527
+ const match = key.match(NAV_HREF_RE);
6362
6528
  if (!match) return null;
6363
6529
  return parseInt(match[1], 10);
6364
6530
  }
@@ -6381,17 +6547,6 @@ function getNavbarExistingTargets() {
6381
6547
  function getNavOrderFromDom() {
6382
6548
  return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6383
6549
  }
6384
- function parseNavOrder(content) {
6385
- const raw = content[NAV_ORDER_KEY];
6386
- if (!raw) return null;
6387
- try {
6388
- const parsed = JSON.parse(raw);
6389
- if (!Array.isArray(parsed)) return null;
6390
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6391
- } catch {
6392
- return null;
6393
- }
6394
- }
6395
6550
  function findCounterpartByHrefKey(hrefKey, root) {
6396
6551
  return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6397
6552
  }
@@ -6455,29 +6610,40 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6455
6610
  }
6456
6611
  return primary;
6457
6612
  }
6458
- 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) {
6459
6621
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6460
6622
  }
6461
6623
  function applyNavOrderToContainer(container, order) {
6462
6624
  const desired = order.filter((key, i) => order.indexOf(key) === i);
6463
- const current = getNavbarLinkKeysInContainer(container);
6625
+ const current = getNavOrderFromContainer(container);
6464
6626
  const extras = current.filter((key) => !desired.includes(key));
6465
6627
  const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6466
- if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6467
- return;
6468
- }
6628
+ if (navOrderKeysEqual(current, expected)) return;
6469
6629
  const seen = /* @__PURE__ */ new Set();
6630
+ const orderedEls = [];
6470
6631
  for (const hrefKey of desired) {
6471
6632
  if (seen.has(hrefKey)) continue;
6472
6633
  seen.add(hrefKey);
6473
6634
  const el = findCounterpartByHrefKey(hrefKey, container);
6474
- if (el) container.appendChild(el);
6635
+ if (el && isNavbarLinkItem(el)) orderedEls.push(el);
6475
6636
  }
6476
6637
  for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6477
6638
  if (!isNavbarLinkItem(el)) continue;
6478
6639
  const key = el.getAttribute("data-ohw-href-key");
6479
6640
  if (!key || seen.has(key)) continue;
6480
- container.appendChild(el);
6641
+ orderedEls.push(el);
6642
+ }
6643
+ for (const el of orderedEls) {
6644
+ if (container.lastElementChild !== el) {
6645
+ container.appendChild(el);
6646
+ }
6481
6647
  }
6482
6648
  }
6483
6649
  function applyNavOrder(order) {
@@ -6486,6 +6652,75 @@ function applyNavOrder(order) {
6486
6652
  const drawer = getNavbarDrawerContainer();
6487
6653
  if (drawer) applyNavOrderToContainer(drawer, order);
6488
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
+ }
6489
6724
  function collectNavbarIndicesFromContent(content) {
6490
6725
  const indices = /* @__PURE__ */ new Set();
6491
6726
  for (const key of Object.keys(content)) {
@@ -6513,8 +6748,10 @@ function reconcileNavbarItemsFromContent(content) {
6513
6748
  if (!href && !label) continue;
6514
6749
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6515
6750
  }
6516
- const order = parseNavOrder(content);
6517
- if (order?.length) applyNavOrder(order);
6751
+ const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
6752
+ if (orderForest?.length) {
6753
+ applyNavForest(orderForest);
6754
+ }
6518
6755
  }
6519
6756
  function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6520
6757
  const order = getNavOrderFromDom();
@@ -6554,6 +6791,841 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6554
6791
  };
6555
6792
  }
6556
6793
 
6794
+ // src/lib/footer-items.ts
6795
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6796
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6797
+ function parseFooterHrefKey(key) {
6798
+ if (!key) return null;
6799
+ const match = key.match(FOOTER_HREF_RE);
6800
+ if (!match) return null;
6801
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6802
+ }
6803
+ function isFooterHrefKey(key) {
6804
+ return parseFooterHrefKey(key) !== null;
6805
+ }
6806
+ function getFooterRoot() {
6807
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6808
+ }
6809
+ function getFooterLinksContainer() {
6810
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6811
+ }
6812
+ function isFooterLinkAnchor(el) {
6813
+ if (!el.matches("[data-ohw-href-key]")) return false;
6814
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6815
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6816
+ }
6817
+ function listFooterColumns() {
6818
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6819
+ if (!root) return [];
6820
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6821
+ if (explicit.length > 0) return explicit;
6822
+ return Array.from(root.children).filter((el) => {
6823
+ if (!(el instanceof HTMLElement)) return false;
6824
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6825
+ isFooterLinkAnchor
6826
+ );
6827
+ });
6828
+ }
6829
+ function listFooterLinksInColumn(column) {
6830
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6831
+ isFooterLinkAnchor
6832
+ );
6833
+ }
6834
+ function findFooterColumnForLink(anchor) {
6835
+ const columns = listFooterColumns();
6836
+ return columns.find((col) => col.contains(anchor)) ?? null;
6837
+ }
6838
+ function getFooterOrderFromDom() {
6839
+ return listFooterColumns().map(
6840
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6841
+ );
6842
+ }
6843
+ function findFooterLinkByKey(hrefKey) {
6844
+ return document.querySelector(
6845
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6846
+ );
6847
+ }
6848
+ function syncFooterColumnIndices(columns) {
6849
+ columns.forEach((col, index) => {
6850
+ const next = String(index);
6851
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6852
+ col.setAttribute("data-ohw-footer-col", next);
6853
+ }
6854
+ });
6855
+ }
6856
+ function appendChildIfNeeded(parent, child) {
6857
+ if (parent.lastElementChild !== child) {
6858
+ parent.appendChild(child);
6859
+ }
6860
+ }
6861
+ function applyFooterOrder(order) {
6862
+ const container = getFooterLinksContainer();
6863
+ if (!container) return;
6864
+ const columns = listFooterColumns();
6865
+ if (columns.length === 0) return;
6866
+ const currentOrder = getFooterOrderFromDom();
6867
+ if (ordersEqual(order, currentOrder)) {
6868
+ syncFooterColumnIndices(columns);
6869
+ return;
6870
+ }
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);
6894
+ }
6895
+ for (const col of orderedCols) {
6896
+ appendChildIfNeeded(container, col);
6897
+ }
6898
+ const freshColumns = listFooterColumns();
6899
+ syncFooterColumnIndices(freshColumns);
6900
+ for (let c = 0; c < order.length; c++) {
6901
+ const col = freshColumns[c];
6902
+ if (!col) continue;
6903
+ const colOrder = order[c];
6904
+ for (const hrefKey of colOrder) {
6905
+ const el = findFooterLinkByKey(hrefKey);
6906
+ if (!el) continue;
6907
+ if (el.parentElement !== col || col.lastElementChild !== el) {
6908
+ col.appendChild(el);
6909
+ }
6910
+ }
6911
+ }
6912
+ }
6913
+ function ordersEqual(a, b) {
6914
+ if (a.length !== b.length) return false;
6915
+ for (let i = 0; i < a.length; i++) {
6916
+ const left = a[i];
6917
+ const right = b[i];
6918
+ if (left.length !== right.length) return false;
6919
+ for (let j = 0; j < left.length; j++) {
6920
+ if (left[j] !== right[j]) return false;
6921
+ }
6922
+ }
6923
+ return true;
6924
+ }
6925
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6926
+ const order = getFooterOrderFromDom();
6927
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6928
+ let fromCol = -1;
6929
+ let fromIdx = -1;
6930
+ for (let c = 0; c < order.length; c++) {
6931
+ const idx = order[c].indexOf(hrefKey);
6932
+ if (idx >= 0) {
6933
+ fromCol = c;
6934
+ fromIdx = idx;
6935
+ break;
6936
+ }
6937
+ }
6938
+ if (fromCol < 0) return null;
6939
+ const next = order.map((col) => [...col]);
6940
+ next[fromCol].splice(fromIdx, 1);
6941
+ let adjusted = insertIndex;
6942
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6943
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6944
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
6945
+ if (ordersEqual(next, order)) return null;
6946
+ return next;
6947
+ }
6948
+ function planFooterColumnMove(fromIndex, toIndex) {
6949
+ const order = getFooterOrderFromDom();
6950
+ const columns = listFooterColumns();
6951
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6952
+ return null;
6953
+ }
6954
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6955
+ const nextOrder = order.map((col) => [...col]);
6956
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
6957
+ if (!movedOrder) return null;
6958
+ let adjusted = toIndex;
6959
+ if (fromIndex < toIndex) adjusted -= 1;
6960
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6961
+ nextOrder.splice(adjusted, 0, movedOrder);
6962
+ if (ordersEqual(nextOrder, order)) return null;
6963
+ return nextOrder;
6964
+ }
6965
+ function parseFooterOrder(content) {
6966
+ const raw = content[FOOTER_ORDER_KEY];
6967
+ if (!raw) return null;
6968
+ try {
6969
+ const parsed = JSON.parse(raw);
6970
+ if (!Array.isArray(parsed)) return null;
6971
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
6972
+ } catch {
6973
+ return null;
6974
+ }
6975
+ }
6976
+ function reconcileFooterOrderFromContent(content) {
6977
+ const order = parseFooterOrder(content);
6978
+ if (!order?.length) return;
6979
+ const current = getFooterOrderFromDom();
6980
+ if (ordersEqual(order, current)) {
6981
+ syncFooterColumnIndices(listFooterColumns());
6982
+ return;
6983
+ }
6984
+ applyFooterOrder(order);
6985
+ }
6986
+ function buildLinkDropSlots(column, columnIndex) {
6987
+ const links = listFooterLinksInColumn(column);
6988
+ const colRect = column.getBoundingClientRect();
6989
+ const slots = [];
6990
+ const barThickness = 3;
6991
+ if (links.length === 0) {
6992
+ slots.push({
6993
+ insertIndex: 0,
6994
+ columnIndex,
6995
+ left: colRect.left,
6996
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
6997
+ width: colRect.width,
6998
+ height: barThickness,
6999
+ direction: "horizontal"
7000
+ });
7001
+ return slots;
7002
+ }
7003
+ for (let i = 0; i <= links.length; i++) {
7004
+ let top;
7005
+ if (i === 0) {
7006
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
7007
+ } else if (i === links.length) {
7008
+ const last = links[links.length - 1].getBoundingClientRect();
7009
+ top = last.bottom - barThickness / 2;
7010
+ } else {
7011
+ const prev = links[i - 1].getBoundingClientRect();
7012
+ const next = links[i].getBoundingClientRect();
7013
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
7014
+ }
7015
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7016
+ slots.push({
7017
+ insertIndex: i,
7018
+ columnIndex,
7019
+ left: widthRef.left,
7020
+ top,
7021
+ width: Math.max(widthRef.width, colRect.width * 0.8),
7022
+ height: barThickness,
7023
+ direction: "horizontal"
7024
+ });
7025
+ }
7026
+ return slots;
7027
+ }
7028
+ function buildColumnDropSlots() {
7029
+ const columns = listFooterColumns();
7030
+ const slots = [];
7031
+ const barThickness = 3;
7032
+ if (columns.length === 0) return slots;
7033
+ for (let i = 0; i <= columns.length; i++) {
7034
+ let left;
7035
+ let height;
7036
+ let top;
7037
+ if (i === 0) {
7038
+ const first = columns[0].getBoundingClientRect();
7039
+ left = first.left - barThickness / 2;
7040
+ top = first.top;
7041
+ height = first.height;
7042
+ } else if (i === columns.length) {
7043
+ const last = columns[columns.length - 1].getBoundingClientRect();
7044
+ left = last.right - barThickness / 2;
7045
+ top = last.top;
7046
+ height = last.height;
7047
+ } else {
7048
+ const prev = columns[i - 1].getBoundingClientRect();
7049
+ const next = columns[i].getBoundingClientRect();
7050
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7051
+ top = Math.min(prev.top, next.top);
7052
+ height = Math.max(prev.bottom, next.bottom) - top;
7053
+ }
7054
+ slots.push({
7055
+ insertIndex: i,
7056
+ columnIndex: i,
7057
+ left,
7058
+ top,
7059
+ width: barThickness,
7060
+ height: Math.max(height, 24),
7061
+ direction: "vertical"
7062
+ });
7063
+ }
7064
+ return slots;
7065
+ }
7066
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
7067
+ const columns = listFooterColumns();
7068
+ let best = null;
7069
+ for (let c = 0; c < columns.length; c++) {
7070
+ const col = columns[c];
7071
+ const colRect = col.getBoundingClientRect();
7072
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
7073
+ if (!inColX) continue;
7074
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
7075
+ const links = listFooterLinksInColumn(col);
7076
+ const fromIdx = links.findIndex(
7077
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
7078
+ );
7079
+ if (fromIdx < 0) return true;
7080
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
7081
+ return true;
7082
+ }
7083
+ return true;
7084
+ });
7085
+ for (const slot of slots) {
7086
+ const cy = slot.top + slot.height / 2;
7087
+ const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
7088
+ if (!best || dist < best.dist) best = { slot, dist };
7089
+ }
7090
+ }
7091
+ return best?.slot ?? null;
7092
+ }
7093
+ function findColumnIndexForKey(hrefKey) {
7094
+ const order = getFooterOrderFromDom();
7095
+ for (let c = 0; c < order.length; c++) {
7096
+ if (order[c].includes(hrefKey)) return c;
7097
+ }
7098
+ return -1;
7099
+ }
7100
+ function hitTestColumnDropSlot(clientX, _clientY) {
7101
+ const slots = buildColumnDropSlots();
7102
+ let best = null;
7103
+ for (const slot of slots) {
7104
+ const cx2 = slot.left + slot.width / 2;
7105
+ const dist = Math.abs(clientX - cx2);
7106
+ if (!best || dist < best.dist) best = { slot, dist };
7107
+ }
7108
+ return best?.slot ?? null;
7109
+ }
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
+
6557
7629
  // src/ui/navbar-container-chrome.tsx
6558
7630
  var import_lucide_react10 = require("lucide-react");
6559
7631
  var import_jsx_runtime21 = require("react/jsx-runtime");
@@ -6598,8 +7670,50 @@ function NavbarContainerChrome({
6598
7670
  );
6599
7671
  }
6600
7672
 
6601
- // src/ui/badge.tsx
7673
+ // src/ui/drop-indicator.tsx
7674
+ var React10 = __toESM(require("react"), 1);
6602
7675
  var import_jsx_runtime22 = require("react/jsx-runtime");
7676
+ var dropIndicatorVariants = cva(
7677
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7678
+ {
7679
+ variants: {
7680
+ direction: {
7681
+ vertical: "h-6 w-[3px]",
7682
+ horizontal: "h-[3px] w-[200px]"
7683
+ },
7684
+ state: {
7685
+ default: "opacity-0",
7686
+ hover: "opacity-100",
7687
+ dragIdle: "opacity-40",
7688
+ dragActive: "opacity-100"
7689
+ }
7690
+ },
7691
+ defaultVariants: {
7692
+ direction: "vertical",
7693
+ state: "default"
7694
+ }
7695
+ }
7696
+ );
7697
+ var DropIndicator = React10.forwardRef(
7698
+ ({ className, direction, state, ...props }, ref) => {
7699
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7700
+ "div",
7701
+ {
7702
+ ref,
7703
+ "data-slot": "drop-indicator",
7704
+ "data-direction": direction ?? "vertical",
7705
+ "data-state": state ?? "default",
7706
+ "aria-hidden": "true",
7707
+ className: cn(dropIndicatorVariants({ direction, state }), className),
7708
+ ...props
7709
+ }
7710
+ );
7711
+ }
7712
+ );
7713
+ DropIndicator.displayName = "DropIndicator";
7714
+
7715
+ // src/ui/badge.tsx
7716
+ var import_jsx_runtime23 = require("react/jsx-runtime");
6603
7717
  var badgeVariants = cva(
6604
7718
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
6605
7719
  {
@@ -6617,12 +7731,12 @@ var badgeVariants = cva(
6617
7731
  }
6618
7732
  );
6619
7733
  function Badge({ className, variant, ...props }) {
6620
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
7734
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6621
7735
  }
6622
7736
 
6623
7737
  // src/OhhwellsBridge.tsx
6624
7738
  var import_lucide_react11 = require("lucide-react");
6625
- var import_jsx_runtime23 = require("react/jsx-runtime");
7739
+ var import_jsx_runtime24 = require("react/jsx-runtime");
6626
7740
  var PRIMARY2 = "#0885FE";
6627
7741
  var IMAGE_FADE_MS = 300;
6628
7742
  function runOpacityFade(el, onDone) {
@@ -6801,7 +7915,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6801
7915
  const root = (0, import_client.createRoot)(container);
6802
7916
  (0, import_react_dom2.flushSync)(() => {
6803
7917
  root.render(
6804
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7918
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6805
7919
  SchedulingWidget,
6806
7920
  {
6807
7921
  notifyOnConnect,
@@ -6876,7 +7990,7 @@ function syncNavigationDragCursorAttrs() {
6876
7990
  el.setAttribute("data-ohw-can-drag", "");
6877
7991
  });
6878
7992
  }
6879
- function collectEditableNodes() {
7993
+ function collectEditableNodes(extraContent) {
6880
7994
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6881
7995
  if (el.dataset.ohwEditable === "image") {
6882
7996
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6904,6 +8018,14 @@ function collectEditableNodes() {
6904
8018
  if (!key) return;
6905
8019
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6906
8020
  });
8021
+ if (extraContent) {
8022
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
8023
+ const text = extraContent[key];
8024
+ if (typeof text === "string" && text.length > 0) {
8025
+ nodes.push({ key, type: "meta", text });
8026
+ }
8027
+ }
8028
+ }
6907
8029
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6908
8030
  const key = el.dataset.ohwKey ?? "";
6909
8031
  const video = getVideoEl(el);
@@ -6911,13 +8033,7 @@ function collectEditableNodes() {
6911
8033
  nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
6912
8034
  nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
6913
8035
  });
6914
- const seen = /* @__PURE__ */ new Set();
6915
- return nodes.filter((node) => {
6916
- if (!node.key) return true;
6917
- if (seen.has(node.key)) return false;
6918
- seen.add(node.key);
6919
- return true;
6920
- });
8036
+ return nodes;
6921
8037
  }
6922
8038
  function isMediaEditable(el) {
6923
8039
  const t = el.dataset.ohwEditable;
@@ -7049,7 +8165,7 @@ function countFooterNavItems(el) {
7049
8165
  return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
7050
8166
  }
7051
8167
  function findFooterItemGroup(item) {
7052
- const explicit = item.closest("[data-ohw-footer-column]");
8168
+ const explicit = item.closest("[data-ohw-footer-col]");
7053
8169
  if (explicit) return explicit;
7054
8170
  const footer = item.closest("footer");
7055
8171
  if (!footer) return null;
@@ -7066,17 +8182,14 @@ function isNavigationRoot(el) {
7066
8182
  function isInferredFooterGroup(el) {
7067
8183
  const footer = el.closest("footer");
7068
8184
  if (!footer || el === footer) return false;
7069
- if (el.hasAttribute("data-ohw-footer-column")) return true;
8185
+ if (el.hasAttribute("data-ohw-footer-col")) return true;
7070
8186
  return countFooterNavItems(el) >= 2;
7071
8187
  }
7072
8188
  function isNavigationContainer(el) {
7073
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
7074
- }
7075
- function isNavbarLinksContainer(el) {
7076
- return el.hasAttribute("data-ohw-nav-container");
8189
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || isNavigationRoot(el) || isInferredFooterGroup(el);
7077
8190
  }
7078
8191
  function getFooterColumn(el) {
7079
- return el.closest("[data-ohw-footer-column]");
8192
+ return el.closest("[data-ohw-footer-col]");
7080
8193
  }
7081
8194
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
7082
8195
  if (getNavigationItemAnchor(target)) return null;
@@ -7100,7 +8213,7 @@ function isPointOverNavigation(x, y) {
7100
8213
  function findHoveredNavOrFooterContainer(x, y) {
7101
8214
  const candidates = [
7102
8215
  ...document.querySelectorAll("[data-ohw-nav-container]"),
7103
- ...document.querySelectorAll("[data-ohw-footer-column]")
8216
+ ...document.querySelectorAll("[data-ohw-footer-col]")
7104
8217
  ];
7105
8218
  for (const container of candidates) {
7106
8219
  const r2 = container.getBoundingClientRect();
@@ -7147,7 +8260,7 @@ function getNavigationSelectionParent(el) {
7147
8260
  if (footerGroup) return footerGroup;
7148
8261
  return getNavigationRoot(el);
7149
8262
  }
7150
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
8263
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(el)) {
7151
8264
  return getNavigationRoot(el);
7152
8265
  }
7153
8266
  return null;
@@ -7155,24 +8268,37 @@ function getNavigationSelectionParent(el) {
7155
8268
  function placeCaretAtPoint(el, x, y) {
7156
8269
  const selection = window.getSelection();
7157
8270
  if (!selection) return;
7158
- if (typeof document.caretRangeFromPoint === "function") {
7159
- const range = document.caretRangeFromPoint(x, y);
7160
- if (range && el.contains(range.startContainer)) {
7161
- selection.removeAllRanges();
7162
- selection.addRange(range);
7163
- 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
+ }
7164
8286
  }
7165
- }
7166
- const caretPositionFromPoint = document.caretPositionFromPoint;
7167
- if (typeof caretPositionFromPoint === "function") {
7168
- const position = caretPositionFromPoint(x, y);
7169
- if (position && el.contains(position.offsetNode)) {
7170
- const range = document.createRange();
7171
- range.setStart(position.offsetNode, position.offset);
7172
- range.collapse(true);
7173
- selection.removeAllRanges();
7174
- 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
+ }
7175
8297
  }
8298
+ } finally {
8299
+ overlays.forEach((node, i) => {
8300
+ node.style.pointerEvents = prevPointerEvents[i] ?? "";
8301
+ });
7176
8302
  }
7177
8303
  }
7178
8304
  function selectAllTextInEditable(el) {
@@ -7314,6 +8440,8 @@ var ICONS = {
7314
8440
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
7315
8441
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
7316
8442
  };
8443
+ var TOOLBAR_PILL_PADDING = 2;
8444
+ var TOOLBAR_TOGGLE_GAP = 4;
7317
8445
  var SELECTION_CHROME_GAP2 = 4;
7318
8446
  var TOOLBAR_STROKE_GAP2 = 4;
7319
8447
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -7341,7 +8469,7 @@ function EditGlowChrome({
7341
8469
  dragDisabled = false
7342
8470
  }) {
7343
8471
  const GAP = SELECTION_CHROME_GAP2;
7344
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8472
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
7345
8473
  "div",
7346
8474
  {
7347
8475
  ref: elRef,
@@ -7356,7 +8484,7 @@ function EditGlowChrome({
7356
8484
  zIndex: 2147483646
7357
8485
  },
7358
8486
  children: [
7359
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8487
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7360
8488
  "div",
7361
8489
  {
7362
8490
  style: {
@@ -7369,7 +8497,7 @@ function EditGlowChrome({
7369
8497
  }
7370
8498
  }
7371
8499
  ),
7372
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8500
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7373
8501
  "div",
7374
8502
  {
7375
8503
  "data-ohw-drag-handle-container": "",
@@ -7381,7 +8509,7 @@ function EditGlowChrome({
7381
8509
  transform: "translate(calc(-100% - 7px), -50%)",
7382
8510
  pointerEvents: dragDisabled ? "none" : "auto"
7383
8511
  },
7384
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8512
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7385
8513
  DragHandle,
7386
8514
  {
7387
8515
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7491,9 +8619,9 @@ function FloatingToolbar({
7491
8619
  showEditLink,
7492
8620
  onEditLink
7493
8621
  }) {
7494
- const localRef = import_react8.default.useRef(null);
7495
- const [measuredW, setMeasuredW] = import_react8.default.useState(330);
7496
- 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(
7497
8625
  (node) => {
7498
8626
  localRef.current = node;
7499
8627
  if (typeof elRef === "function") elRef(node);
@@ -7505,7 +8633,7 @@ function FloatingToolbar({
7505
8633
  },
7506
8634
  [elRef]
7507
8635
  );
7508
- import_react8.default.useLayoutEffect(() => {
8636
+ import_react9.default.useLayoutEffect(() => {
7509
8637
  const node = localRef.current;
7510
8638
  if (!node) return;
7511
8639
  const update = () => {
@@ -7518,7 +8646,7 @@ function FloatingToolbar({
7518
8646
  return () => ro.disconnect();
7519
8647
  }, [showEditLink, activeCommands]);
7520
8648
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
7521
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8649
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7522
8650
  "div",
7523
8651
  {
7524
8652
  ref: setRefs,
@@ -7528,14 +8656,23 @@ function FloatingToolbar({
7528
8656
  left,
7529
8657
  transform,
7530
8658
  zIndex: 2147483647,
8659
+ background: "#fff",
8660
+ border: "1px solid #E7E5E4",
8661
+ borderRadius: 6,
8662
+ boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
8663
+ display: "flex",
8664
+ alignItems: "center",
8665
+ padding: TOOLBAR_PILL_PADDING,
8666
+ gap: TOOLBAR_TOGGLE_GAP,
8667
+ fontFamily: "sans-serif",
7531
8668
  pointerEvents: "auto"
7532
8669
  },
7533
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CustomToolbar, { children: [
7534
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react8.default.Fragment, { children: [
7535
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CustomToolbarDivider, {}),
8670
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CustomToolbar, { children: [
8671
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react9.default.Fragment, { children: [
8672
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CustomToolbarDivider, {}),
7536
8673
  btns.map((btn) => {
7537
8674
  const isActive = activeCommands.has(btn.cmd);
7538
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8675
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7539
8676
  CustomToolbarButton,
7540
8677
  {
7541
8678
  title: btn.title,
@@ -7544,7 +8681,7 @@ function FloatingToolbar({
7544
8681
  e.preventDefault();
7545
8682
  onCommand(btn.cmd);
7546
8683
  },
7547
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8684
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7548
8685
  "svg",
7549
8686
  {
7550
8687
  width: "16",
@@ -7565,7 +8702,7 @@ function FloatingToolbar({
7565
8702
  );
7566
8703
  })
7567
8704
  ] }, gi)),
7568
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8705
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7569
8706
  CustomToolbarButton,
7570
8707
  {
7571
8708
  type: "button",
@@ -7579,7 +8716,7 @@ function FloatingToolbar({
7579
8716
  e.preventDefault();
7580
8717
  e.stopPropagation();
7581
8718
  },
7582
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
8719
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7583
8720
  }
7584
8721
  ) : null
7585
8722
  ] })
@@ -7596,7 +8733,7 @@ function StateToggle({
7596
8733
  states,
7597
8734
  onStateChange
7598
8735
  }) {
7599
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8736
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7600
8737
  ToggleGroup,
7601
8738
  {
7602
8739
  "data-ohw-state-toggle": "",
@@ -7610,7 +8747,7 @@ function StateToggle({
7610
8747
  left: rect.right - 8,
7611
8748
  transform: "translateX(-100%)"
7612
8749
  },
7613
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8750
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7614
8751
  }
7615
8752
  );
7616
8753
  }
@@ -7637,8 +8774,8 @@ function OhhwellsBridge() {
7637
8774
  const router = (0, import_navigation2.useRouter)();
7638
8775
  const searchParams = (0, import_navigation2.useSearchParams)();
7639
8776
  const isEditMode = isEditSessionActive();
7640
- const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7641
- (0, import_react8.useEffect)(() => {
8777
+ const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
8778
+ (0, import_react9.useEffect)(() => {
7642
8779
  const figtreeFontId = "ohw-figtree-font";
7643
8780
  if (!document.getElementById(figtreeFontId)) {
7644
8781
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7666,92 +8803,137 @@ function OhhwellsBridge() {
7666
8803
  const subdomainFromQuery = searchParams.get("subdomain");
7667
8804
  const subdomain = resolveSubdomain(subdomainFromQuery);
7668
8805
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7669
- const postToParent2 = (0, import_react8.useCallback)((data) => {
8806
+ const postToParent2 = (0, import_react9.useCallback)((data) => {
7670
8807
  if (typeof window !== "undefined" && window.parent !== window) {
7671
8808
  window.parent.postMessage(data, "*");
7672
8809
  }
7673
8810
  }, []);
7674
- const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
7675
- const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
7676
- const activeElRef = (0, import_react8.useRef)(null);
7677
- const selectedElRef = (0, import_react8.useRef)(null);
7678
- const originalContentRef = (0, import_react8.useRef)(null);
7679
- const activeStateElRef = (0, import_react8.useRef)(null);
7680
- const parentScrollRef = (0, import_react8.useRef)(null);
7681
- const visibleViewportRef = (0, import_react8.useRef)(null);
7682
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
7683
- 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) => {
7684
8823
  visibleViewportRef.current = node;
7685
8824
  setDialogPortalContainer(node);
7686
8825
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7687
8826
  }, []);
7688
- const toolbarElRef = (0, import_react8.useRef)(null);
7689
- const glowElRef = (0, import_react8.useRef)(null);
7690
- const hoveredImageRef = (0, import_react8.useRef)(null);
7691
- const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
7692
- const dragOverElRef = (0, import_react8.useRef)(null);
7693
- const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7694
- const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
7695
- const hoveredGapRef = (0, import_react8.useRef)(null);
7696
- const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7697
- const imageShowTimerRef = (0, import_react8.useRef)(null);
7698
- const editStylesRef = (0, import_react8.useRef)(null);
7699
- 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)(() => {
7700
8840
  });
7701
- const deactivateRef = (0, import_react8.useRef)(() => {
8841
+ const deactivateRef = (0, import_react9.useRef)(() => {
7702
8842
  });
7703
- const selectRef = (0, import_react8.useRef)(() => {
8843
+ const selectRef = (0, import_react9.useRef)(() => {
7704
8844
  });
7705
- const selectFrameRef = (0, import_react8.useRef)(() => {
8845
+ const selectFrameRef = (0, import_react9.useRef)(() => {
7706
8846
  });
7707
- const deselectRef = (0, import_react8.useRef)(() => {
8847
+ const deselectRef = (0, import_react9.useRef)(() => {
7708
8848
  });
7709
- const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
8849
+ const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
7710
8850
  });
7711
- const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
8851
+ const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
7712
8852
  });
7713
- const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
8853
+ const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
7714
8854
  });
7715
- const postToParentRef = (0, import_react8.useRef)(postToParent2);
8855
+ const postToParentRef = (0, import_react9.useRef)(postToParent2);
7716
8856
  postToParentRef.current = postToParent2;
7717
- const sectionsLoadedRef = (0, import_react8.useRef)(false);
7718
- const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
7719
- const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
7720
- const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7721
- 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");
7722
8862
  toolbarVariantRef.current = toolbarVariant;
7723
- const [selectedIsCta, setSelectedIsCta] = (0, import_react8.useState)(false);
7724
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7725
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7726
- const [toggleState, setToggleState] = (0, import_react8.useState)(null);
7727
- const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
7728
- const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
7729
- const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
7730
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
7731
- const hoveredNavContainerRef = (0, import_react8.useRef)(null);
7732
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
7733
- const hoveredItemElRef = (0, import_react8.useRef)(null);
7734
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7735
- const siblingHintElRef = (0, import_react8.useRef)(null);
7736
- const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
7737
- const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
7738
- const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7739
- const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7740
- const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
7741
- const editContentRef = (0, import_react8.useRef)({});
7742
- const [sitePages, setSitePages] = (0, import_react8.useState)([]);
7743
- const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
7744
- const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
7745
- const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
7746
- const linkPopoverPanelRef = (0, import_react8.useRef)(null);
7747
- const linkPopoverOpenRef = (0, import_react8.useRef)(false);
7748
- 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);
7749
8898
  setLinkPopoverRef.current = setLinkPopover;
7750
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
+ });
7751
8933
  const bumpLinkPopoverGrace = () => {
7752
8934
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7753
8935
  };
7754
- const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
8936
+ const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
7755
8937
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7756
8938
  const gen = ++sectionsPrefetchGenRef.current;
7757
8939
  const paths = pages.map((p) => p.path);
@@ -7770,10 +8952,14 @@ function OhhwellsBridge() {
7770
8952
  );
7771
8953
  });
7772
8954
  }, [isEditMode, pathname]);
7773
- const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
8955
+ const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
7774
8956
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7775
- (0, import_react8.useEffect)(() => {
7776
- if (!linkPopover) return;
8957
+ (0, import_react9.useEffect)(() => {
8958
+ if (!linkPopover) {
8959
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
8960
+ return;
8961
+ }
8962
+ document.documentElement.setAttribute("data-ohw-link-popover-open", "");
7777
8963
  if (hoveredImageRef.current) {
7778
8964
  hoveredImageRef.current = null;
7779
8965
  hoveredImageHasTextOverlapRef.current = false;
@@ -7805,9 +8991,10 @@ function OhhwellsBridge() {
7805
8991
  body.style.overflow = prevBodyOverflow;
7806
8992
  document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
7807
8993
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
8994
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
7808
8995
  };
7809
8996
  }, [linkPopover, postToParent2]);
7810
- (0, import_react8.useEffect)(() => {
8997
+ (0, import_react9.useEffect)(() => {
7811
8998
  if (!isEditMode) return;
7812
8999
  const useFixtures = shouldUseDevFixtures();
7813
9000
  if (useFixtures) {
@@ -7831,14 +9018,14 @@ function OhhwellsBridge() {
7831
9018
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
7832
9019
  return () => window.removeEventListener("message", onSitePages);
7833
9020
  }, [isEditMode, postToParent2]);
7834
- (0, import_react8.useEffect)(() => {
9021
+ (0, import_react9.useEffect)(() => {
7835
9022
  if (!isEditMode || shouldUseDevFixtures()) return;
7836
9023
  void loadAllSectionsManifest().then((manifest) => {
7837
9024
  if (Object.keys(manifest).length === 0) return;
7838
9025
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7839
9026
  });
7840
9027
  }, [isEditMode]);
7841
- (0, import_react8.useEffect)(() => {
9028
+ (0, import_react9.useEffect)(() => {
7842
9029
  const update = () => {
7843
9030
  const el = activeElRef.current ?? selectedElRef.current;
7844
9031
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7862,10 +9049,10 @@ function OhhwellsBridge() {
7862
9049
  vvp.removeEventListener("resize", update);
7863
9050
  };
7864
9051
  }, []);
7865
- const refreshStateRules = (0, import_react8.useCallback)(() => {
9052
+ const refreshStateRules = (0, import_react9.useCallback)(() => {
7866
9053
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7867
9054
  }, []);
7868
- const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
9055
+ const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
7869
9056
  const tracker = getSectionsTracker();
7870
9057
  let entries = [];
7871
9058
  try {
@@ -7888,7 +9075,7 @@ function OhhwellsBridge() {
7888
9075
  }
7889
9076
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7890
9077
  }, [isEditMode]);
7891
- const deactivate = (0, import_react8.useCallback)(() => {
9078
+ const deactivate = (0, import_react9.useCallback)(() => {
7892
9079
  const el = activeElRef.current;
7893
9080
  if (!el) return;
7894
9081
  const key = el.dataset.ohwKey;
@@ -7920,19 +9107,23 @@ function OhhwellsBridge() {
7920
9107
  setToolbarShowEditLink(false);
7921
9108
  postToParent2({ type: "ow:exit-edit" });
7922
9109
  }, [postToParent2]);
7923
- const clearSelectedAttr = (0, import_react8.useCallback)(() => {
9110
+ const clearSelectedAttr = (0, import_react9.useCallback)(() => {
7924
9111
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
7925
9112
  el.removeAttribute("data-ohw-selected");
7926
9113
  });
7927
9114
  }, []);
7928
- const deselect = (0, import_react8.useCallback)(() => {
9115
+ const deselect = (0, import_react9.useCallback)(() => {
7929
9116
  clearSelectedAttr();
7930
9117
  selectedElRef.current = null;
9118
+ selectedHrefKeyRef.current = null;
9119
+ selectedFooterColAttrRef.current = null;
7931
9120
  setSelectedIsCta(false);
7932
9121
  setReorderHrefKey(null);
7933
9122
  setReorderDragDisabled(false);
9123
+ setIsFooterFrameSelection(false);
7934
9124
  siblingHintElRef.current = null;
7935
9125
  setSiblingHintRect(null);
9126
+ setSiblingHintRects([]);
7936
9127
  setIsItemDragging(false);
7937
9128
  hoveredNavContainerRef.current = null;
7938
9129
  setHoveredNavContainerRect(null);
@@ -7941,12 +9132,56 @@ function OhhwellsBridge() {
7941
9132
  setToolbarVariant("none");
7942
9133
  }
7943
9134
  }, [clearSelectedAttr]);
7944
- const markSelected = (0, import_react8.useCallback)((el) => {
9135
+ const markSelected = (0, import_react9.useCallback)((el) => {
7945
9136
  clearSelectedAttr();
7946
9137
  el.setAttribute("data-ohw-selected", "");
7947
9138
  }, [clearSelectedAttr]);
7948
- 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) => {
7949
9182
  selectedElRef.current = navAnchor;
9183
+ selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9184
+ selectedFooterColAttrRef.current = null;
7950
9185
  markSelected(navAnchor);
7951
9186
  setSelectedIsCta(isNavbarButton2(navAnchor));
7952
9187
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
@@ -7957,7 +9192,7 @@ function OhhwellsBridge() {
7957
9192
  setToolbarShowEditLink(false);
7958
9193
  setActiveCommands(/* @__PURE__ */ new Set());
7959
9194
  }, [markSelected]);
7960
- const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
9195
+ const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
7961
9196
  const el = activeElRef.current;
7962
9197
  if (!el) return;
7963
9198
  const key = el.dataset.ohwKey;
@@ -7967,49 +9202,349 @@ function OhhwellsBridge() {
7967
9202
  clearTimeout(timer);
7968
9203
  autoSaveTimers.current.delete(key);
7969
9204
  }
7970
- const html = sanitizeHtml(el.innerHTML);
7971
- const original = originalContentRef.current ?? "";
7972
- if (html !== sanitizeHtml(original)) {
7973
- postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
7974
- const h = document.documentElement.scrollHeight;
7975
- if (h > 50) postToParent2({ type: "ow:height", height: h });
9205
+ const html = sanitizeHtml(el.innerHTML);
9206
+ const original = originalContentRef.current ?? "";
9207
+ if (html !== sanitizeHtml(original)) {
9208
+ postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
9209
+ const h = document.documentElement.scrollHeight;
9210
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
9211
+ }
9212
+ }
9213
+ el.removeAttribute("contenteditable");
9214
+ el.removeAttribute("data-ohw-editing");
9215
+ activeElRef.current = null;
9216
+ setMaxBadge(null);
9217
+ setActiveCommands(/* @__PURE__ */ new Set());
9218
+ setToolbarShowEditLink(false);
9219
+ postToParent2({ type: "ow:exit-edit" });
9220
+ reselectNavigationItem(navAnchor);
9221
+ }, [postToParent2, reselectNavigationItem]);
9222
+ const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
9223
+ const items = listNavbarItems();
9224
+ addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
9225
+ deselectRef.current();
9226
+ deactivateRef.current();
9227
+ bumpLinkPopoverGrace();
9228
+ setLinkPopover({
9229
+ key: "__add-nav__",
9230
+ mode: "create",
9231
+ intent: "add-nav"
9232
+ });
9233
+ }, []);
9234
+ const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
9235
+ footerDragRef.current = null;
9236
+ setSiblingHintRects([]);
9237
+ setFooterDropSlots([]);
9238
+ setActiveFooterDropIndex(null);
9239
+ setDraggedItemRect(null);
9240
+ setIsItemDragging(false);
9241
+ unlockFooterDragInteraction();
9242
+ }, []);
9243
+ const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
9244
+ const dragged = session.draggedEl;
9245
+ setDraggedItemRect(dragged.getBoundingClientRect());
9246
+ if (typeof clientX === "number" && typeof clientY === "number") {
9247
+ session.lastClientX = clientX;
9248
+ session.lastClientY = clientY;
9249
+ }
9250
+ session.activeSlot = activeSlot;
9251
+ if (session.kind === "link") {
9252
+ const columns2 = listFooterColumns();
9253
+ const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
9254
+ if (activeSlot) focusCols.add(activeSlot.columnIndex);
9255
+ const siblingRects = [];
9256
+ columns2.forEach((col, i) => {
9257
+ if (!focusCols.has(i)) return;
9258
+ for (const link of listFooterLinksInColumn(col)) {
9259
+ if (link === dragged) continue;
9260
+ siblingRects.push(link.getBoundingClientRect());
9261
+ }
9262
+ });
9263
+ setSiblingHintRects(siblingRects);
9264
+ const slots2 = [];
9265
+ columns2.forEach((col, i) => {
9266
+ slots2.push(...buildLinkDropSlots(col, i));
9267
+ });
9268
+ setFooterDropSlots(slots2);
9269
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
9270
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
9271
+ return;
9272
+ }
9273
+ const columns = listFooterColumns();
9274
+ setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
9275
+ const slots = buildColumnDropSlots();
9276
+ setFooterDropSlots(slots);
9277
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
9278
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
9279
+ }, []);
9280
+ const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
9281
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
9282
+ const commitFooterDragRef = (0, import_react9.useRef)(() => {
9283
+ });
9284
+ const beginFooterDragRef = (0, import_react9.useRef)(() => {
9285
+ });
9286
+ const beginFooterDrag = (0, import_react9.useCallback)(
9287
+ (session) => {
9288
+ const rect = session.draggedEl.getBoundingClientRect();
9289
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
9290
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
9291
+ session.activeSlot = session.activeSlot ?? null;
9292
+ footerDragRef.current = session;
9293
+ setIsItemDragging(true);
9294
+ lockFooterDuringDrag();
9295
+ siblingHintElRef.current = null;
9296
+ setSiblingHintRect(null);
9297
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
9298
+ setToolbarRect(rect);
9299
+ }
9300
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
9301
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
9302
+ },
9303
+ [refreshFooterDragVisuals]
9304
+ );
9305
+ beginFooterDragRef.current = beginFooterDrag;
9306
+ const commitFooterDrag = (0, import_react9.useCallback)(
9307
+ (clientX, clientY) => {
9308
+ const session = footerDragRef.current;
9309
+ if (!session) {
9310
+ clearFooterDragVisuals();
9311
+ return;
9312
+ }
9313
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
9314
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
9315
+ let nextOrder = null;
9316
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
9317
+ if (session.kind === "link" && session.hrefKey && slot) {
9318
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
9319
+ } else if (session.kind === "column" && slot) {
9320
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
9321
+ }
9322
+ const wasSelected = session.wasSelected;
9323
+ const draggedEl = session.draggedEl;
9324
+ const hrefKey = session.hrefKey;
9325
+ let movedColumnIndex = null;
9326
+ if (session.kind === "column" && slot && nextOrder) {
9327
+ let adjusted = slot.insertIndex;
9328
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
9329
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
9330
+ }
9331
+ const applySelectionAfterDrop = () => {
9332
+ if (!wasSelected) {
9333
+ deselectRef.current();
9334
+ return;
9335
+ }
9336
+ if (hrefKey) {
9337
+ selectedHrefKeyRef.current = hrefKey;
9338
+ selectedFooterColAttrRef.current = null;
9339
+ const link = resolveHrefKeyElement(hrefKey);
9340
+ if (link && isNavigationItem(link)) {
9341
+ selectRef.current(link);
9342
+ return;
9343
+ }
9344
+ }
9345
+ if (movedColumnIndex !== null && nextOrder) {
9346
+ const colKeys = nextOrder[movedColumnIndex] ?? [];
9347
+ let column = null;
9348
+ for (const key of colKeys) {
9349
+ const link = document.querySelector(
9350
+ `footer [data-ohw-href-key="${CSS.escape(key)}"]`
9351
+ );
9352
+ if (link) {
9353
+ column = findFooterColumnForLink(link);
9354
+ if (column) break;
9355
+ }
9356
+ }
9357
+ if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
9358
+ if (column && isNavigationContainer(column)) {
9359
+ selectFrameRef.current(column);
9360
+ return;
9361
+ }
9362
+ }
9363
+ if (document.body.contains(draggedEl)) {
9364
+ if (isNavigationItem(draggedEl)) {
9365
+ selectRef.current(draggedEl);
9366
+ return;
9367
+ }
9368
+ if (isNavigationContainer(draggedEl)) {
9369
+ selectFrameRef.current(draggedEl);
9370
+ return;
9371
+ }
9372
+ }
9373
+ deselectRef.current();
9374
+ };
9375
+ if (nextOrder) {
9376
+ const orderJson = JSON.stringify(nextOrder);
9377
+ editContentRef.current = {
9378
+ ...editContentRef.current,
9379
+ [FOOTER_ORDER_KEY]: orderJson
9380
+ };
9381
+ applyFooterOrder(nextOrder);
9382
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9383
+ postToParentRef.current({
9384
+ type: "ow:change",
9385
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
9386
+ });
9387
+ applySelectionAfterDrop();
9388
+ clearFooterDragVisuals();
9389
+ requestAnimationFrame(() => {
9390
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9391
+ applyFooterOrder(nextOrder);
9392
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9393
+ }
9394
+ applySelectionAfterDrop();
9395
+ requestAnimationFrame(() => {
9396
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9397
+ applyFooterOrder(nextOrder);
9398
+ }
9399
+ resyncSelectedNavigationItem();
9400
+ });
9401
+ });
9402
+ return;
9403
+ }
9404
+ applySelectionAfterDrop();
9405
+ clearFooterDragVisuals();
9406
+ },
9407
+ [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
9408
+ );
9409
+ commitFooterDragRef.current = commitFooterDrag;
9410
+ const startFooterLinkDrag = (0, import_react9.useCallback)(
9411
+ (anchor, clientX, clientY, wasSelected) => {
9412
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
9413
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
9414
+ const column = findFooterColumnForLink(anchor);
9415
+ const columns = listFooterColumns();
9416
+ beginFooterDrag({
9417
+ kind: "link",
9418
+ hrefKey,
9419
+ columnEl: column,
9420
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
9421
+ wasSelected,
9422
+ draggedEl: anchor,
9423
+ lastClientX: clientX,
9424
+ lastClientY: clientY,
9425
+ activeSlot: null
9426
+ });
9427
+ return true;
9428
+ },
9429
+ [beginFooterDrag]
9430
+ );
9431
+ const startFooterColumnDrag = (0, import_react9.useCallback)(
9432
+ (columnEl, clientX, clientY, wasSelected) => {
9433
+ const columns = listFooterColumns();
9434
+ const idx = columns.indexOf(columnEl);
9435
+ if (idx < 0) return false;
9436
+ beginFooterDrag({
9437
+ kind: "column",
9438
+ hrefKey: null,
9439
+ columnEl,
9440
+ sourceColumnIndex: idx,
9441
+ wasSelected,
9442
+ draggedEl: columnEl,
9443
+ lastClientX: clientX,
9444
+ lastClientY: clientY,
9445
+ activeSlot: null
9446
+ });
9447
+ return true;
9448
+ },
9449
+ [beginFooterDrag]
9450
+ );
9451
+ const handleItemDragStart = (0, import_react9.useCallback)(
9452
+ (e) => {
9453
+ const selected = selectedElRef.current;
9454
+ if (!selected) {
9455
+ setIsItemDragging(true);
9456
+ return;
9457
+ }
9458
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
9459
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
9460
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
9461
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9462
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
9463
+ }
9464
+ if (startNavLinkDrag(selected, clientX, clientY, true)) return;
9465
+ siblingHintElRef.current = null;
9466
+ setSiblingHintRect(null);
9467
+ setIsItemDragging(true);
9468
+ },
9469
+ [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
9470
+ );
9471
+ const handleItemDragEnd = (0, import_react9.useCallback)(
9472
+ (e) => {
9473
+ if (footerDragRef.current) {
9474
+ const x = e?.clientX;
9475
+ const y = e?.clientY;
9476
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9477
+ commitFooterDrag(x, y);
9478
+ } else {
9479
+ commitFooterDrag();
9480
+ }
9481
+ return;
7976
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
+ }
9493
+ setIsItemDragging(false);
9494
+ },
9495
+ [commitFooterDrag, commitNavDrag, navDragRef]
9496
+ );
9497
+ const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
9498
+ if (e.button !== 0) return;
9499
+ const selected = selectedElRef.current;
9500
+ if (!selected) return;
9501
+ armFooterPressDrag();
9502
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
9503
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
9504
+ footerPointerDragRef.current = {
9505
+ el: selected,
9506
+ kind: "link",
9507
+ startX: e.clientX,
9508
+ startY: e.clientY,
9509
+ pointerId: e.pointerId,
9510
+ wasSelected: true,
9511
+ started: false
9512
+ };
9513
+ return;
7977
9514
  }
7978
- el.removeAttribute("contenteditable");
7979
- el.removeAttribute("data-ohw-editing");
7980
- activeElRef.current = null;
7981
- setMaxBadge(null);
7982
- setActiveCommands(/* @__PURE__ */ new Set());
7983
- setToolbarShowEditLink(false);
7984
- postToParent2({ type: "ow:exit-edit" });
7985
- reselectNavigationItem(navAnchor);
7986
- }, [postToParent2, reselectNavigationItem]);
7987
- const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
7988
- const items = listNavbarItems();
7989
- addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7990
- deselectRef.current();
7991
- deactivateRef.current();
7992
- bumpLinkPopoverGrace();
7993
- setLinkPopover({
7994
- key: "__add-nav__",
7995
- mode: "create",
7996
- intent: "add-nav"
7997
- });
7998
- }, []);
7999
- const handleItemDragStart = (0, import_react8.useCallback)(() => {
8000
- siblingHintElRef.current = null;
8001
- setSiblingHintRect(null);
8002
- setIsItemDragging(true);
8003
- }, []);
8004
- const handleItemDragEnd = (0, import_react8.useCallback)(() => {
8005
- setIsItemDragging(false);
9515
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9516
+ footerPointerDragRef.current = {
9517
+ el: selected,
9518
+ kind: "column",
9519
+ startX: e.clientX,
9520
+ startY: e.clientY,
9521
+ pointerId: e.pointerId,
9522
+ wasSelected: true,
9523
+ started: false
9524
+ };
9525
+ return;
9526
+ }
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) {
9531
+ suppressNextClickRef.current = false;
9532
+ return;
9533
+ }
9534
+ const selected = selectedElRef.current;
9535
+ if (!selected || !isNavigationItem(selected)) return;
9536
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9537
+ if (!editable) return;
9538
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
8006
9539
  }, []);
8007
9540
  reselectNavigationItemRef.current = reselectNavigationItem;
8008
9541
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
8009
- const select = (0, import_react8.useCallback)((anchor) => {
9542
+ const select = (0, import_react9.useCallback)((anchor) => {
8010
9543
  if (!isNavigationItem(anchor)) return;
8011
9544
  if (activeElRef.current) deactivate();
8012
9545
  selectedElRef.current = anchor;
9546
+ selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
9547
+ selectedFooterColAttrRef.current = null;
8013
9548
  markSelected(anchor);
8014
9549
  setSelectedIsCta(isNavbarButton2(anchor));
8015
9550
  clearHrefKeyHover(anchor);
@@ -8019,19 +9554,24 @@ function OhhwellsBridge() {
8019
9554
  hoveredItemElRef.current = null;
8020
9555
  siblingHintElRef.current = null;
8021
9556
  setSiblingHintRect(null);
9557
+ setSiblingHintRects([]);
8022
9558
  setIsItemDragging(false);
8023
9559
  const { key, disabled } = getNavigationItemReorderState(anchor);
8024
9560
  setReorderHrefKey(key);
8025
9561
  setReorderDragDisabled(disabled);
9562
+ setIsFooterFrameSelection(false);
8026
9563
  setToolbarVariant("link-action");
8027
9564
  setToolbarRect(anchor.getBoundingClientRect());
8028
9565
  setToolbarShowEditLink(false);
8029
9566
  setActiveCommands(/* @__PURE__ */ new Set());
8030
9567
  }, [deactivate, markSelected]);
8031
- const selectFrame = (0, import_react8.useCallback)((el) => {
9568
+ const selectFrame = (0, import_react9.useCallback)((el) => {
8032
9569
  if (!isNavigationContainer(el)) return;
8033
9570
  if (activeElRef.current) deactivate();
9571
+ const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8034
9572
  selectedElRef.current = el;
9573
+ selectedHrefKeyRef.current = null;
9574
+ selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
8035
9575
  markSelected(el);
8036
9576
  setSelectedIsCta(false);
8037
9577
  clearHrefKeyHover(el);
@@ -8041,28 +9581,37 @@ function OhhwellsBridge() {
8041
9581
  hoveredItemElRef.current = null;
8042
9582
  siblingHintElRef.current = null;
8043
9583
  setSiblingHintRect(null);
9584
+ setSiblingHintRects(
9585
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
9586
+ );
8044
9587
  setIsItemDragging(false);
8045
9588
  setReorderHrefKey(null);
8046
9589
  setReorderDragDisabled(false);
9590
+ setIsFooterFrameSelection(isFooterColumn);
8047
9591
  setToolbarVariant("select-frame");
8048
9592
  setToolbarRect(el.getBoundingClientRect());
8049
9593
  setToolbarShowEditLink(false);
8050
9594
  setActiveCommands(/* @__PURE__ */ new Set());
8051
9595
  }, [deactivate, markSelected]);
8052
- const activate = (0, import_react8.useCallback)((el, options) => {
9596
+ const activate = (0, import_react9.useCallback)((el, options) => {
8053
9597
  if (activeElRef.current === el) return;
8054
9598
  clearSelectedAttr();
8055
9599
  selectedElRef.current = null;
9600
+ selectedHrefKeyRef.current = null;
9601
+ selectedFooterColAttrRef.current = null;
8056
9602
  setSelectedIsCta(false);
8057
9603
  deactivate();
8058
9604
  if (hoveredImageRef.current) {
8059
9605
  hoveredImageRef.current = null;
8060
9606
  setMediaHover(null);
9607
+ postToParentRef.current({ type: "ow:image-unhover" });
8061
9608
  }
8062
9609
  setToolbarVariant("rich-text");
8063
9610
  siblingHintElRef.current = null;
8064
9611
  setSiblingHintRect(null);
9612
+ setSiblingHintRects([]);
8065
9613
  setIsItemDragging(false);
9614
+ setIsFooterFrameSelection(false);
8066
9615
  el.setAttribute("contenteditable", "true");
8067
9616
  el.setAttribute("data-ohw-editing", "");
8068
9617
  el.removeAttribute("data-ohw-hovered");
@@ -8071,7 +9620,11 @@ function OhhwellsBridge() {
8071
9620
  originalContentRef.current = el.innerHTML;
8072
9621
  el.focus({ preventScroll: true });
8073
9622
  if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8074
- 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
+ });
8075
9628
  } else {
8076
9629
  const selection = window.getSelection();
8077
9630
  if (selection && selection.rangeCount === 0) {
@@ -8101,7 +9654,7 @@ function OhhwellsBridge() {
8101
9654
  selectRef.current = select;
8102
9655
  selectFrameRef.current = selectFrame;
8103
9656
  deselectRef.current = deselect;
8104
- (0, import_react8.useLayoutEffect)(() => {
9657
+ (0, import_react9.useLayoutEffect)(() => {
8105
9658
  if (!subdomain || isEditMode) {
8106
9659
  setFetchState("done");
8107
9660
  return;
@@ -8142,6 +9695,7 @@ function OhhwellsBridge() {
8142
9695
  applyLinkByKey(key, val);
8143
9696
  }
8144
9697
  reconcileNavbarItemsFromContent(content);
9698
+ reconcileFooterOrderFromContent(content);
8145
9699
  enforceLinkHrefs();
8146
9700
  initSectionsFromContent(content, true);
8147
9701
  sectionsLoadedRef.current = true;
@@ -8170,7 +9724,7 @@ function OhhwellsBridge() {
8170
9724
  cancelled = true;
8171
9725
  };
8172
9726
  }, [subdomain, isEditMode]);
8173
- (0, import_react8.useEffect)(() => {
9727
+ (0, import_react9.useEffect)(() => {
8174
9728
  if (!subdomain || isEditMode) return;
8175
9729
  let debounceTimer = null;
8176
9730
  let observer = null;
@@ -8202,6 +9756,7 @@ function OhhwellsBridge() {
8202
9756
  applyLinkByKey(key, val);
8203
9757
  }
8204
9758
  reconcileNavbarItemsFromContent(content);
9759
+ reconcileFooterOrderFromContent(content);
8205
9760
  } finally {
8206
9761
  observer?.observe(document.body, { childList: true, subtree: true });
8207
9762
  }
@@ -8219,16 +9774,16 @@ function OhhwellsBridge() {
8219
9774
  if (debounceTimer) clearTimeout(debounceTimer);
8220
9775
  };
8221
9776
  }, [subdomain, isEditMode, pathname]);
8222
- (0, import_react8.useLayoutEffect)(() => {
9777
+ (0, import_react9.useLayoutEffect)(() => {
8223
9778
  const el = document.getElementById("ohw-loader");
8224
9779
  if (!el) return;
8225
9780
  const visible = Boolean(subdomain) && fetchState !== "done";
8226
9781
  el.style.display = visible ? "flex" : "none";
8227
9782
  }, [subdomain, fetchState]);
8228
- (0, import_react8.useEffect)(() => {
9783
+ (0, import_react9.useEffect)(() => {
8229
9784
  postToParent2({ type: "ow:navigation", path: pathname });
8230
9785
  }, [pathname, postToParent2]);
8231
- (0, import_react8.useEffect)(() => {
9786
+ (0, import_react9.useEffect)(() => {
8232
9787
  if (!isEditMode) return;
8233
9788
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8234
9789
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -8236,7 +9791,7 @@ function OhhwellsBridge() {
8236
9791
  deselectRef.current();
8237
9792
  deactivateRef.current();
8238
9793
  }, [pathname, isEditMode]);
8239
- (0, import_react8.useEffect)(() => {
9794
+ (0, import_react9.useEffect)(() => {
8240
9795
  const contentForNav = () => {
8241
9796
  if (isEditMode) return editContentRef.current;
8242
9797
  if (!subdomain) return {};
@@ -8245,12 +9800,6 @@ function OhhwellsBridge() {
8245
9800
  const observeRoots = () => [document.querySelector("nav"), document.querySelector("footer")].filter(
8246
9801
  (el) => Boolean(el)
8247
9802
  );
8248
- const roots = observeRoots();
8249
- if (roots.length === 0) {
8250
- reconcileNavbarItemsFromContent(contentForNav());
8251
- if (isEditMode) syncNavigationDragCursorAttrs();
8252
- return;
8253
- }
8254
9803
  let rafId = null;
8255
9804
  let applying = false;
8256
9805
  let observer = null;
@@ -8260,11 +9809,26 @@ function OhhwellsBridge() {
8260
9809
  }
8261
9810
  };
8262
9811
  const run = () => {
8263
- if (applying) return;
9812
+ if (footerDragRef.current || navDragRef.current || applying) return;
8264
9813
  applying = true;
8265
9814
  observer?.disconnect();
8266
9815
  try {
8267
- reconcileNavbarItemsFromContent(contentForNav());
9816
+ const content = contentForNav();
9817
+ reconcileNavbarItemsFromContent(content);
9818
+ reconcileFooterOrderFromContent(content);
9819
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
9820
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
9821
+ disableNativeHrefDrag(el);
9822
+ }
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();
8268
9832
  if (isEditMode) syncNavigationDragCursorAttrs();
8269
9833
  } finally {
8270
9834
  applying = false;
@@ -8284,8 +9848,8 @@ function OhhwellsBridge() {
8284
9848
  if (rafId != null) cancelAnimationFrame(rafId);
8285
9849
  observer?.disconnect();
8286
9850
  };
8287
- }, [isEditMode, pathname, subdomain, fetchState]);
8288
- (0, import_react8.useEffect)(() => {
9851
+ }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
9852
+ (0, import_react9.useEffect)(() => {
8289
9853
  if (!isEditMode) return;
8290
9854
  const measure = () => {
8291
9855
  const h = document.body.scrollHeight;
@@ -8309,7 +9873,7 @@ function OhhwellsBridge() {
8309
9873
  window.removeEventListener("resize", handleResize);
8310
9874
  };
8311
9875
  }, [pathname, isEditMode, postToParent2]);
8312
- (0, import_react8.useEffect)(() => {
9876
+ (0, import_react9.useEffect)(() => {
8313
9877
  if (!subdomainFromQuery || isEditMode) return;
8314
9878
  const handleClick = (e) => {
8315
9879
  const anchor = e.target.closest("a");
@@ -8325,7 +9889,7 @@ function OhhwellsBridge() {
8325
9889
  document.addEventListener("click", handleClick, true);
8326
9890
  return () => document.removeEventListener("click", handleClick, true);
8327
9891
  }, [subdomainFromQuery, isEditMode, router]);
8328
- (0, import_react8.useEffect)(() => {
9892
+ (0, import_react9.useEffect)(() => {
8329
9893
  if (!isEditMode) {
8330
9894
  editStylesRef.current?.base.remove();
8331
9895
  editStylesRef.current?.forceHover.remove();
@@ -8372,6 +9936,37 @@ function OhhwellsBridge() {
8372
9936
  [data-ohw-href-key][data-ohw-selected] * {
8373
9937
  cursor: text !important;
8374
9938
  }
9939
+ /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
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] {
9944
+ -webkit-user-drag: none !important;
9945
+ }
9946
+ footer [data-ohw-footer-col]:hover {
9947
+ outline: 1.5px dashed ${PRIMARY2} !important;
9948
+ outline-offset: 6px;
9949
+ border-radius: 8px;
9950
+ }
9951
+ html[data-ohw-link-popover-open] footer [data-ohw-footer-col]:hover {
9952
+ outline: none !important;
9953
+ }
9954
+ html[data-ohw-footer-press-drag] footer,
9955
+ html[data-ohw-footer-press-drag] footer * {
9956
+ user-select: none !important;
9957
+ -webkit-user-select: none !important;
9958
+ }
9959
+ 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 * {
9963
+ user-select: none !important;
9964
+ -webkit-user-select: none !important;
9965
+ pointer-events: none !important;
9966
+ }
9967
+ html[data-ohw-item-dragging] {
9968
+ cursor: grabbing !important;
9969
+ }
8375
9970
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8376
9971
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
8377
9972
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -8428,12 +10023,39 @@ function OhhwellsBridge() {
8428
10023
  syncNavigationDragCursorAttrs();
8429
10024
  refreshStateRules();
8430
10025
  const handleClick = (e) => {
10026
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
10027
+ suppressNextClickRef.current = false;
10028
+ e.preventDefault();
10029
+ e.stopPropagation();
10030
+ return;
10031
+ }
8431
10032
  const target = e.target;
8432
10033
  if (target.closest("[data-ohw-toolbar]")) return;
8433
10034
  if (target.closest("[data-ohw-state-toggle]")) return;
8434
10035
  if (target.closest("[data-ohw-max-badge]")) return;
8435
10036
  if (isInsideLinkEditor(target)) return;
8436
10037
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
10038
+ if (target.closest("[data-ohw-item-drag-surface]")) {
10039
+ e.preventDefault();
10040
+ e.stopPropagation();
10041
+ const selected = selectedElRef.current;
10042
+ if (selected && isNavigationItem(selected)) {
10043
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
10044
+ if (editable2) {
10045
+ activateRef.current(editable2, {
10046
+ caretX: e.clientX,
10047
+ caretY: e.clientY
10048
+ });
10049
+ }
10050
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
10051
+ const link = listFooterLinksInColumn(selected).find((el) => {
10052
+ const r2 = el.getBoundingClientRect();
10053
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
10054
+ });
10055
+ if (link) selectRef.current(link);
10056
+ }
10057
+ return;
10058
+ }
8437
10059
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8438
10060
  if (navFromChrome) {
8439
10061
  e.preventDefault();
@@ -8487,7 +10109,15 @@ function OhhwellsBridge() {
8487
10109
  if (isMediaEditable(editable)) {
8488
10110
  e.preventDefault();
8489
10111
  e.stopPropagation();
8490
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
10112
+ const key = editable.dataset.ohwKey ?? "";
10113
+ const r2 = editable.getBoundingClientRect();
10114
+ if (key && r2.width > 1 && r2.height > 1) {
10115
+ pendingUploadRectRef.current = {
10116
+ key,
10117
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height }
10118
+ };
10119
+ }
10120
+ postToParentRef.current({ type: "ow:image-pick", key, elementType: editable.dataset.ohwEditable ?? "image" });
8491
10121
  return;
8492
10122
  }
8493
10123
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -8512,10 +10142,26 @@ function OhhwellsBridge() {
8512
10142
  if (hrefAnchor) {
8513
10143
  e.preventDefault();
8514
10144
  e.stopPropagation();
8515
- if (selectedElRef.current === hrefAnchor) return;
10145
+ if (selectedElRef.current === hrefAnchor) {
10146
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
10147
+ if (textEditable) {
10148
+ activateRef.current(textEditable, {
10149
+ caretX: e.clientX,
10150
+ caretY: e.clientY
10151
+ });
10152
+ }
10153
+ return;
10154
+ }
8516
10155
  selectRef.current(hrefAnchor);
8517
10156
  return;
8518
10157
  }
10158
+ const footerColClick = target.closest("[data-ohw-footer-col]");
10159
+ if (footerColClick) {
10160
+ e.preventDefault();
10161
+ e.stopPropagation();
10162
+ selectFrameRef.current(footerColClick);
10163
+ return;
10164
+ }
8519
10165
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8520
10166
  if (navContainerToSelect) {
8521
10167
  e.preventDefault();
@@ -8589,8 +10235,15 @@ function OhhwellsBridge() {
8589
10235
  const handleMouseOver = (e) => {
8590
10236
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8591
10237
  const target = e.target;
10238
+ if (linkPopoverOpenRef.current) {
10239
+ hoveredItemElRef.current = null;
10240
+ setHoveredItemRect(null);
10241
+ hoveredNavContainerRef.current = null;
10242
+ setHoveredNavContainerRect(null);
10243
+ return;
10244
+ }
8592
10245
  if (toolbarVariantRef.current !== "select-frame") {
8593
- const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-column]");
10246
+ const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-col]");
8594
10247
  if (frameContainer && !getNavigationItemAnchor(target)) {
8595
10248
  hoveredNavContainerRef.current = frameContainer;
8596
10249
  setHoveredNavContainerRect(frameContainer.getBoundingClientRect());
@@ -8598,11 +10251,20 @@ function OhhwellsBridge() {
8598
10251
  setHoveredItemRect(null);
8599
10252
  return;
8600
10253
  }
8601
- if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10254
+ if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-col]")) {
8602
10255
  hoveredNavContainerRef.current = null;
8603
10256
  setHoveredNavContainerRect(null);
8604
10257
  }
8605
10258
  }
10259
+ const footerCol = target.closest("[data-ohw-footer-col]");
10260
+ if (footerCol) {
10261
+ hoveredNavContainerRef.current = null;
10262
+ setHoveredNavContainerRect(null);
10263
+ if (selectedElRef.current === footerCol) return;
10264
+ hoveredItemElRef.current = footerCol;
10265
+ setHoveredItemRect(footerCol.getBoundingClientRect());
10266
+ return;
10267
+ }
8606
10268
  const navAnchor = getNavigationItemAnchor(target);
8607
10269
  if (navAnchor) {
8608
10270
  hoveredNavContainerRef.current = null;
@@ -8631,14 +10293,23 @@ function OhhwellsBridge() {
8631
10293
  };
8632
10294
  const handleMouseOut = (e) => {
8633
10295
  const target = e.target;
8634
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10296
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col]")) {
8635
10297
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8636
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-column], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
10298
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8637
10299
  return;
8638
10300
  }
8639
10301
  hoveredNavContainerRef.current = null;
8640
10302
  setHoveredNavContainerRect(null);
8641
10303
  }
10304
+ const footerCol = target.closest("[data-ohw-footer-col]");
10305
+ if (footerCol && hoveredItemElRef.current === footerCol) {
10306
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10307
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
10308
+ if (related2?.closest("[data-ohw-footer-col]") === footerCol) return;
10309
+ hoveredItemElRef.current = null;
10310
+ setHoveredItemRect(null);
10311
+ return;
10312
+ }
8642
10313
  const navAnchor = getNavigationItemAnchor(target);
8643
10314
  if (navAnchor) {
8644
10315
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8750,12 +10421,12 @@ function OhhwellsBridge() {
8750
10421
  const stickyPad = 48;
8751
10422
  const withinPad = x >= pr.left - stickyPad && x <= pr.left + pr.width + stickyPad && y >= pr.top - stickyPad && y <= pr.top + pr.height + stickyPad;
8752
10423
  if (withinPad) {
8753
- if (smallest !== pinned && candidatePool.includes(smallest)) {
10424
+ const yieldToSmaller = smallest !== pinned && candidatePool.includes(smallest) && (() => {
8754
10425
  const sr = getVisibleRect(smallest);
8755
- const overOther = x >= sr.left && x <= sr.left + sr.width && y >= sr.top && y <= sr.top + sr.height;
8756
- if (overOther) return smallest;
8757
- }
8758
- return pinned;
10426
+ const pr2 = getVisibleRect(pinned);
10427
+ return sr.width * sr.height < pr2.width * pr2.height;
10428
+ })();
10429
+ if (!yieldToSmaller) return pinned;
8759
10430
  }
8760
10431
  }
8761
10432
  return smallest;
@@ -8767,6 +10438,7 @@ function OhhwellsBridge() {
8767
10438
  resumeAnimTracks();
8768
10439
  postToParentRef.current({ type: "ow:image-unhover" });
8769
10440
  }
10441
+ clearImageHover();
8770
10442
  };
8771
10443
  const probeNavigationHoverAt = (x, y) => {
8772
10444
  if (toolbarVariantRef.current === "select-frame") {
@@ -8776,7 +10448,7 @@ function OhhwellsBridge() {
8776
10448
  }
8777
10449
  const frameContainers = [
8778
10450
  ...document.querySelectorAll("[data-ohw-nav-container]"),
8779
- ...document.querySelectorAll("[data-ohw-footer-column]")
10451
+ ...document.querySelectorAll("[data-ohw-footer-col]")
8780
10452
  ];
8781
10453
  let overFrame = null;
8782
10454
  for (const container of frameContainers) {
@@ -8873,7 +10545,7 @@ function OhhwellsBridge() {
8873
10545
  return;
8874
10546
  }
8875
10547
  const topEl = document.elementFromPoint(x2, y2);
8876
- if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
10548
+ if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-media-chrome]')) {
8877
10549
  if (hoveredImageRef.current) {
8878
10550
  hoveredImageRef.current = null;
8879
10551
  resumeAnimTracks();
@@ -9080,6 +10752,15 @@ function OhhwellsBridge() {
9080
10752
  probeHoverCardsAt(clientX, clientY);
9081
10753
  };
9082
10754
  const handleDragOver = (e) => {
10755
+ const footerSession = footerDragRef.current;
10756
+ if (footerSession) {
10757
+ e.preventDefault();
10758
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
10759
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10760
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
10761
+ return;
10762
+ }
10763
+ if (onNavDragOver(e)) return;
9083
10764
  e.preventDefault();
9084
10765
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9085
10766
  if (!el) {
@@ -9148,6 +10829,7 @@ function OhhwellsBridge() {
9148
10829
  if (!entry || entry.fadingOut) return prev;
9149
10830
  return { ...prev, [key]: { ...entry, fadingOut: true } };
9150
10831
  });
10832
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
9151
10833
  };
9152
10834
  let found = false;
9153
10835
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -9287,6 +10969,7 @@ function OhhwellsBridge() {
9287
10969
  editContentRef.current = { ...editContentRef.current, ...content };
9288
10970
  reconcileNavbarItemsFromContent(editContentRef.current);
9289
10971
  syncNavigationDragCursorAttrs();
10972
+ reconcileFooterOrderFromContent(editContentRef.current);
9290
10973
  enforceLinkHrefs();
9291
10974
  postToParentRef.current({ type: "ow:hydrate-done" });
9292
10975
  };
@@ -9443,6 +11126,22 @@ function OhhwellsBridge() {
9443
11126
  if (siblingHintElRef.current) {
9444
11127
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9445
11128
  }
11129
+ const selected = selectedElRef.current;
11130
+ if (selected && !footerDragRef.current && (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)))) {
11131
+ setSiblingHintRects(
11132
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
11133
+ );
11134
+ }
11135
+ if (footerDragRef.current) {
11136
+ const session = footerDragRef.current;
11137
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
11138
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
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
+ }
9446
11145
  if (hoveredImageRef.current) {
9447
11146
  const el = hoveredImageRef.current;
9448
11147
  const r2 = el.getBoundingClientRect();
@@ -9453,7 +11152,7 @@ function OhhwellsBridge() {
9453
11152
  };
9454
11153
  const handleSave = (e) => {
9455
11154
  if (e.data?.type !== "ow:save") return;
9456
- const nodes = collectEditableNodes();
11155
+ const nodes = collectEditableNodes(editContentRef.current);
9457
11156
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9458
11157
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9459
11158
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9543,23 +11242,43 @@ function OhhwellsBridge() {
9543
11242
  activeStateElRef.current = null;
9544
11243
  setToggleState(null);
9545
11244
  };
11245
+ const resolveUploadRects = (key) => {
11246
+ const matches = Array.from(document.querySelectorAll(`[data-ohw-key="${key}"]`));
11247
+ const rects = matches.map((el) => {
11248
+ const r2 = el.getBoundingClientRect();
11249
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
11250
+ }).filter((r2) => r2.width > 1 && r2.height > 1);
11251
+ if (rects.length > 0) return rects;
11252
+ const pending = pendingUploadRectRef.current;
11253
+ if (pending?.key === key && pending.rect.width > 1 && pending.rect.height > 1) {
11254
+ return [pending.rect];
11255
+ }
11256
+ const hovered = hoveredImageRef.current;
11257
+ if (hovered?.dataset.ohwKey === key) {
11258
+ const r2 = hovered.getBoundingClientRect();
11259
+ if (r2.width > 1 && r2.height > 1) {
11260
+ return [{ top: r2.top, left: r2.left, width: r2.width, height: r2.height }];
11261
+ }
11262
+ }
11263
+ return [];
11264
+ };
9546
11265
  const handleImageUploading = (e) => {
9547
- if (e.data?.type !== "ow:image-uploading") return;
9548
- const { key, uploading } = e.data;
11266
+ const type = e.data?.type;
11267
+ if (type !== "ow:image-uploading" && type !== "ow:anim-lock") return;
11268
+ const key = e.data.key;
11269
+ if (!key) return;
11270
+ const uploading = type === "ow:anim-lock" ? true : !!e.data.uploading;
9549
11271
  setUploadingRects((prev) => {
9550
11272
  if (!uploading) {
11273
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
9551
11274
  if (!(key in prev)) return prev;
9552
11275
  const next = { ...prev };
9553
11276
  delete next[key];
9554
11277
  return next;
9555
11278
  }
9556
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
9557
- if (!el) return prev;
9558
- const r2 = getVisibleRect(el);
9559
- return {
9560
- ...prev,
9561
- [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
9562
- };
11279
+ const rects = resolveUploadRects(key);
11280
+ if (rects.length === 0) return prev;
11281
+ return { ...prev, [key]: { rects } };
9563
11282
  });
9564
11283
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9565
11284
  const track = el.closest("[data-ohw-hover-pause]");
@@ -9676,7 +11395,7 @@ function OhhwellsBridge() {
9676
11395
  }
9677
11396
  }
9678
11397
  const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
9679
- if (footerColumn?.hasAttribute("data-ohw-footer-column")) {
11398
+ if (footerColumn?.hasAttribute("data-ohw-footer-col")) {
9680
11399
  selectFrameRef.current(footerColumn);
9681
11400
  return;
9682
11401
  }
@@ -9751,7 +11470,161 @@ function OhhwellsBridge() {
9751
11470
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9752
11471
  };
9753
11472
  }, [isEditMode, refreshStateRules]);
9754
- (0, import_react8.useEffect)(() => {
11473
+ (0, import_react9.useEffect)(() => {
11474
+ if (!isEditMode) return;
11475
+ const THRESHOLD = 10;
11476
+ const resolveWasSelected = (el) => {
11477
+ if (selectedElRef.current === el) return true;
11478
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
11479
+ return activeAnchor === el;
11480
+ };
11481
+ const onPointerDown = (e) => {
11482
+ if (e.button !== 0) return;
11483
+ if (suppressNextClickRef.current && Date.now() >= suppressClickUntilRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
11484
+ suppressNextClickRef.current = false;
11485
+ }
11486
+ if (footerDragRef.current) return;
11487
+ const target = e.target;
11488
+ if (!target) return;
11489
+ if (target.closest('[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]')) {
11490
+ return;
11491
+ }
11492
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
11493
+ const anchor = getNavigationItemAnchor(target);
11494
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
11495
+ if (anchor && isFooterHrefKey(hrefKey)) {
11496
+ armFooterPressDrag();
11497
+ footerPointerDragRef.current = {
11498
+ el: anchor,
11499
+ kind: "link",
11500
+ startX: e.clientX,
11501
+ startY: e.clientY,
11502
+ pointerId: e.pointerId,
11503
+ wasSelected: resolveWasSelected(anchor),
11504
+ started: false
11505
+ };
11506
+ return;
11507
+ }
11508
+ const col = target.closest("[data-ohw-footer-col]");
11509
+ if (col && !getNavigationItemAnchor(target)) {
11510
+ armFooterPressDrag();
11511
+ footerPointerDragRef.current = {
11512
+ el: col,
11513
+ kind: "column",
11514
+ startX: e.clientX,
11515
+ startY: e.clientY,
11516
+ pointerId: e.pointerId,
11517
+ wasSelected: resolveWasSelected(col),
11518
+ started: false
11519
+ };
11520
+ }
11521
+ };
11522
+ const onPointerMove = (e) => {
11523
+ const pending = footerPointerDragRef.current;
11524
+ if (!pending) return;
11525
+ if (pending.started) {
11526
+ e.preventDefault();
11527
+ clearTextSelection();
11528
+ const session = footerDragRef.current;
11529
+ if (!session) return;
11530
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
11531
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
11532
+ return;
11533
+ }
11534
+ const dx = e.clientX - pending.startX;
11535
+ const dy = e.clientY - pending.startY;
11536
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
11537
+ e.preventDefault();
11538
+ pending.started = true;
11539
+ clearTextSelection();
11540
+ try {
11541
+ document.body.setPointerCapture(pending.pointerId);
11542
+ } catch {
11543
+ }
11544
+ if (linkPopoverOpenRef.current) {
11545
+ setLinkPopoverRef.current(null);
11546
+ }
11547
+ if (activeElRef.current) {
11548
+ deactivateRef.current();
11549
+ }
11550
+ if (pending.kind === "link") {
11551
+ const key = pending.el.getAttribute("data-ohw-href-key");
11552
+ if (!key) return;
11553
+ const column = findFooterColumnForLink(pending.el);
11554
+ const columns2 = listFooterColumns();
11555
+ beginFooterDragRef.current({
11556
+ kind: "link",
11557
+ hrefKey: key,
11558
+ columnEl: column,
11559
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
11560
+ wasSelected: pending.wasSelected,
11561
+ draggedEl: pending.el,
11562
+ lastClientX: e.clientX,
11563
+ lastClientY: e.clientY,
11564
+ activeSlot: null
11565
+ });
11566
+ return;
11567
+ }
11568
+ const columns = listFooterColumns();
11569
+ const idx = columns.indexOf(pending.el);
11570
+ if (idx < 0) return;
11571
+ beginFooterDragRef.current({
11572
+ kind: "column",
11573
+ hrefKey: null,
11574
+ columnEl: pending.el,
11575
+ sourceColumnIndex: idx,
11576
+ wasSelected: pending.wasSelected,
11577
+ draggedEl: pending.el,
11578
+ lastClientX: e.clientX,
11579
+ lastClientY: e.clientY,
11580
+ activeSlot: null
11581
+ });
11582
+ };
11583
+ const endPointerDrag = (e) => {
11584
+ const pending = footerPointerDragRef.current;
11585
+ footerPointerDragRef.current = null;
11586
+ try {
11587
+ if (document.body.hasPointerCapture(e.pointerId)) {
11588
+ document.body.releasePointerCapture(e.pointerId);
11589
+ }
11590
+ } catch {
11591
+ }
11592
+ if (!pending?.started) {
11593
+ unlockFooterDragInteraction();
11594
+ return;
11595
+ }
11596
+ suppressNextClickRef.current = true;
11597
+ suppressClickUntilRef.current = Date.now() + 500;
11598
+ commitFooterDragRef.current(e.clientX, e.clientY);
11599
+ };
11600
+ const blockSelectStart = (e) => {
11601
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
11602
+ e.preventDefault();
11603
+ }
11604
+ };
11605
+ const onDragEnd = (_e) => {
11606
+ if (!footerDragRef.current) return;
11607
+ suppressNextClickRef.current = true;
11608
+ suppressClickUntilRef.current = Date.now() + 500;
11609
+ commitFooterDragRef.current();
11610
+ };
11611
+ document.addEventListener("pointerdown", onPointerDown, true);
11612
+ document.addEventListener("pointermove", onPointerMove, true);
11613
+ document.addEventListener("pointerup", endPointerDrag, true);
11614
+ document.addEventListener("pointercancel", endPointerDrag, true);
11615
+ document.addEventListener("selectstart", blockSelectStart, true);
11616
+ document.addEventListener("dragend", onDragEnd, true);
11617
+ return () => {
11618
+ document.removeEventListener("pointerdown", onPointerDown, true);
11619
+ document.removeEventListener("pointermove", onPointerMove, true);
11620
+ document.removeEventListener("pointerup", endPointerDrag, true);
11621
+ document.removeEventListener("pointercancel", endPointerDrag, true);
11622
+ document.removeEventListener("selectstart", blockSelectStart, true);
11623
+ document.removeEventListener("dragend", onDragEnd, true);
11624
+ unlockFooterDragInteraction();
11625
+ };
11626
+ }, [isEditMode]);
11627
+ (0, import_react9.useEffect)(() => {
9755
11628
  const handler = (e) => {
9756
11629
  if (e.data?.type !== "ow:request-schedule-config") return;
9757
11630
  const insertAfterVal = e.data.insertAfter;
@@ -9767,7 +11640,7 @@ function OhhwellsBridge() {
9767
11640
  window.addEventListener("message", handler);
9768
11641
  return () => window.removeEventListener("message", handler);
9769
11642
  }, [processConfigRequest]);
9770
- (0, import_react8.useEffect)(() => {
11643
+ (0, import_react9.useEffect)(() => {
9771
11644
  if (!isEditMode) return;
9772
11645
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9773
11646
  el.removeAttribute("data-ohw-active-state");
@@ -9788,7 +11661,13 @@ function OhhwellsBridge() {
9788
11661
  const next = { ...prev, [pathKey]: sections };
9789
11662
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9790
11663
  });
9791
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
11664
+ postToParent2({
11665
+ type: "ow:ready",
11666
+ version: "1",
11667
+ path: pathname,
11668
+ nodes: collectEditableNodes(editContentRef.current),
11669
+ sections
11670
+ });
9792
11671
  postToParent2({ type: "ow:request-site-pages" });
9793
11672
  }, 150);
9794
11673
  return () => {
@@ -9796,19 +11675,19 @@ function OhhwellsBridge() {
9796
11675
  clearTimeout(timer);
9797
11676
  };
9798
11677
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9799
- (0, import_react8.useEffect)(() => {
11678
+ (0, import_react9.useEffect)(() => {
9800
11679
  scrollToHashSectionWhenReady();
9801
11680
  const onHashChange = () => scrollToHashSectionWhenReady();
9802
11681
  window.addEventListener("hashchange", onHashChange);
9803
11682
  return () => window.removeEventListener("hashchange", onHashChange);
9804
11683
  }, [pathname]);
9805
- const handleCommand = (0, import_react8.useCallback)((cmd) => {
11684
+ const handleCommand = (0, import_react9.useCallback)((cmd) => {
9806
11685
  document.execCommand(cmd, false);
9807
11686
  activeElRef.current?.focus();
9808
11687
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
9809
11688
  refreshActiveCommandsRef.current();
9810
11689
  }, []);
9811
- const handleStateChange = (0, import_react8.useCallback)((state) => {
11690
+ const handleStateChange = (0, import_react9.useCallback)((state) => {
9812
11691
  if (!activeStateElRef.current) return;
9813
11692
  const el = activeStateElRef.current;
9814
11693
  if (state === "Default") {
@@ -9821,11 +11700,11 @@ function OhhwellsBridge() {
9821
11700
  }
9822
11701
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9823
11702
  }, [deactivate]);
9824
- const closeLinkPopover = (0, import_react8.useCallback)(() => {
11703
+ const closeLinkPopover = (0, import_react9.useCallback)(() => {
9825
11704
  addNavAfterAnchorRef.current = null;
9826
11705
  setLinkPopover(null);
9827
11706
  }, []);
9828
- const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
11707
+ const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
9829
11708
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9830
11709
  if (!hrefCtx) return;
9831
11710
  bumpLinkPopoverGrace();
@@ -9836,7 +11715,7 @@ function OhhwellsBridge() {
9836
11715
  });
9837
11716
  deactivate();
9838
11717
  }, [deactivate]);
9839
- const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
11718
+ const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
9840
11719
  const anchor = selectedElRef.current;
9841
11720
  if (!anchor) return;
9842
11721
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9849,7 +11728,7 @@ function OhhwellsBridge() {
9849
11728
  });
9850
11729
  deselect();
9851
11730
  }, [deselect]);
9852
- const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
11731
+ const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
9853
11732
  (target) => {
9854
11733
  const session = linkPopoverSessionRef.current;
9855
11734
  if (!session) return;
@@ -9905,16 +11784,16 @@ function OhhwellsBridge() {
9905
11784
  },
9906
11785
  [postToParent2, sitePages, sectionsByPath]
9907
11786
  );
9908
- const showEditLink = toolbarShowEditLink;
9909
- const currentSections = sectionsByPath[pathname] ?? [];
9910
- linkPopoverOpenRef.current = linkPopover !== null;
9911
- const handleMediaReplace = (0, import_react8.useCallback)(
11787
+ const handleMediaReplace = (0, import_react9.useCallback)(
9912
11788
  (key) => {
11789
+ if (mediaHover?.key === key) {
11790
+ pendingUploadRectRef.current = { key, rect: mediaHover.rect };
11791
+ }
9913
11792
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9914
11793
  },
9915
- [postToParent2, mediaHover?.elementType]
11794
+ [postToParent2, mediaHover]
9916
11795
  );
9917
- const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
11796
+ const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
9918
11797
  setUploadingRects((prev) => {
9919
11798
  if (!(key in prev)) return prev;
9920
11799
  const next = { ...prev };
@@ -9922,7 +11801,7 @@ function OhhwellsBridge() {
9922
11801
  return next;
9923
11802
  });
9924
11803
  }, []);
9925
- const handleVideoSettingsChange = (0, import_react8.useCallback)(
11804
+ const handleVideoSettingsChange = (0, import_react9.useCallback)(
9926
11805
  (key, settings) => {
9927
11806
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9928
11807
  const video = getVideoEl(el);
@@ -9944,21 +11823,26 @@ function OhhwellsBridge() {
9944
11823
  },
9945
11824
  [postToParent2]
9946
11825
  );
11826
+ const showEditLink = toolbarShowEditLink;
11827
+ const currentSections = sectionsByPath[pathname] ?? [];
11828
+ linkPopoverOpenRef.current = linkPopover !== null;
9947
11829
  return bridgeRoot ? (0, import_react_dom3.createPortal)(
9948
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9949
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9950
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9951
- MediaOverlay,
9952
- {
9953
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
9954
- isUploading: true,
9955
- fadingOut,
9956
- onFadeOutComplete: handleMediaFadeOutComplete,
9957
- onReplace: handleMediaReplace
9958
- },
9959
- `uploading-${key}`
9960
- )),
9961
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11830
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11831
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
11832
+ Object.entries(uploadingRects).flatMap(
11833
+ ([key, { rects, fadingOut }]) => rects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11834
+ MediaOverlay,
11835
+ {
11836
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
11837
+ isUploading: true,
11838
+ fadingOut,
11839
+ onFadeOutComplete: handleMediaFadeOutComplete,
11840
+ onReplace: handleMediaReplace
11841
+ },
11842
+ `uploading-${key}-${i}`
11843
+ ))
11844
+ ),
11845
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9962
11846
  MediaOverlay,
9963
11847
  {
9964
11848
  hover: mediaHover,
@@ -9967,28 +11851,68 @@ function OhhwellsBridge() {
9967
11851
  onVideoSettingsChange: handleVideoSettingsChange
9968
11852
  }
9969
11853
  ),
9970
- siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9971
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9972
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9973
- NavbarContainerChrome,
11854
+ siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
11855
+ siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
11856
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
11857
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11858
+ "div",
9974
11859
  {
9975
- rect: toolbarRect,
9976
- onAdd: handleAddTopLevelNavItem
9977
- }
9978
- ),
9979
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9980
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11860
+ className: "pointer-events-none fixed z-2147483646",
11861
+ style: {
11862
+ left: slot.left,
11863
+ top: slot.top,
11864
+ width: slot.width,
11865
+ height: slot.height
11866
+ },
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
+ )
11875
+ },
11876
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
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
+ )),
11899
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
11900
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
11901
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
11902
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9981
11903
  ItemInteractionLayer,
9982
11904
  {
9983
- rect: toolbarRect,
11905
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
9984
11906
  elRef: glowElRef,
9985
11907
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9986
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
11908
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
9987
11909
  dragDisabled: reorderDragDisabled,
9988
11910
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9989
11911
  onDragHandleDragStart: handleItemDragStart,
9990
11912
  onDragHandleDragEnd: handleItemDragEnd,
9991
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11913
+ onItemPointerDown: handleItemChromePointerDown,
11914
+ onItemClick: handleItemChromeClick,
11915
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9992
11916
  ItemActionToolbar,
9993
11917
  {
9994
11918
  onEditLink: openLinkPopoverForSelected,
@@ -10001,8 +11925,8 @@ function OhhwellsBridge() {
10001
11925
  ) : void 0
10002
11926
  }
10003
11927
  ),
10004
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
10005
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11928
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11929
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10006
11930
  EditGlowChrome,
10007
11931
  {
10008
11932
  rect: toolbarRect,
@@ -10011,7 +11935,7 @@ function OhhwellsBridge() {
10011
11935
  dragDisabled: reorderDragDisabled
10012
11936
  }
10013
11937
  ),
10014
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11938
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10015
11939
  FloatingToolbar,
10016
11940
  {
10017
11941
  rect: toolbarRect,
@@ -10024,7 +11948,7 @@ function OhhwellsBridge() {
10024
11948
  }
10025
11949
  )
10026
11950
  ] }),
10027
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
11951
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
10028
11952
  "div",
10029
11953
  {
10030
11954
  "data-ohw-max-badge": "",
@@ -10050,7 +11974,7 @@ function OhhwellsBridge() {
10050
11974
  ]
10051
11975
  }
10052
11976
  ),
10053
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11977
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10054
11978
  StateToggle,
10055
11979
  {
10056
11980
  rect: toggleState.rect,
@@ -10059,32 +11983,36 @@ function OhhwellsBridge() {
10059
11983
  onStateChange: handleStateChange
10060
11984
  }
10061
11985
  ),
10062
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
11986
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
10063
11987
  "div",
10064
11988
  {
10065
11989
  "data-ohw-section-insert-line": "",
10066
11990
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
10067
11991
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
10068
11992
  children: [
10069
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10070
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11993
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
11994
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10071
11995
  Badge,
10072
11996
  {
10073
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",
10074
11998
  onClick: () => {
10075
11999
  window.parent.postMessage(
10076
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
12000
+ {
12001
+ type: "ow:add-section",
12002
+ insertAfter: sectionGap.insertAfter,
12003
+ insertBefore: sectionGap.insertBefore
12004
+ },
10077
12005
  "*"
10078
12006
  );
10079
12007
  },
10080
12008
  children: "Add Section"
10081
12009
  }
10082
12010
  ),
10083
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12011
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10084
12012
  ]
10085
12013
  }
10086
12014
  ),
10087
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12015
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10088
12016
  LinkPopover,
10089
12017
  {
10090
12018
  panelRef: linkPopoverPanelRef,
@@ -10105,48 +12033,6 @@ function OhhwellsBridge() {
10105
12033
  bridgeRoot
10106
12034
  ) : null;
10107
12035
  }
10108
-
10109
- // src/ui/drop-indicator.tsx
10110
- var React11 = __toESM(require("react"), 1);
10111
- var import_jsx_runtime24 = require("react/jsx-runtime");
10112
- var dropIndicatorVariants = cva(
10113
- "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
10114
- {
10115
- variants: {
10116
- direction: {
10117
- vertical: "h-6 w-[3px]",
10118
- horizontal: "h-[3px] w-[200px]"
10119
- },
10120
- state: {
10121
- default: "opacity-0",
10122
- hover: "opacity-100",
10123
- dragIdle: "opacity-40",
10124
- dragActive: "opacity-100"
10125
- }
10126
- },
10127
- defaultVariants: {
10128
- direction: "vertical",
10129
- state: "default"
10130
- }
10131
- }
10132
- );
10133
- var DropIndicator = React11.forwardRef(
10134
- ({ className, direction, state, ...props }, ref) => {
10135
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10136
- "div",
10137
- {
10138
- ref,
10139
- "data-slot": "drop-indicator",
10140
- "data-direction": direction ?? "vertical",
10141
- "data-state": state ?? "default",
10142
- "aria-hidden": "true",
10143
- className: cn(dropIndicatorVariants({ direction, state }), className),
10144
- ...props
10145
- }
10146
- );
10147
- }
10148
- );
10149
- DropIndicator.displayName = "DropIndicator";
10150
12036
  // Annotate the CommonJS export names for ESM import in node:
10151
12037
  0 && (module.exports = {
10152
12038
  CustomToolbar,