@ohhwells/bridge 0.1.44 → 0.1.45

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
 
@@ -4360,33 +4360,39 @@ function Tooltip({ ...props }) {
4360
4360
  function TooltipTrigger({ ...props }) {
4361
4361
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
4362
4362
  }
4363
+ var arrowClassBySide = {
4364
+ top: "before:bottom-0 before:left-1/2 before:-translate-x-1/2 before:translate-y-1/2",
4365
+ bottom: "before:top-0 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2",
4366
+ left: "before:right-0 before:top-1/2 before:-translate-y-1/2 before:translate-x-1/2",
4367
+ right: "before:left-0 before:top-1/2 before:-translate-y-1/2 before:-translate-x-1/2"
4368
+ };
4363
4369
  function TooltipContent({
4364
4370
  className,
4365
- sideOffset = 3,
4371
+ sideOffset = 9,
4366
4372
  side = "bottom",
4367
4373
  children,
4368
4374
  hideArrow,
4369
4375
  ...props
4370
4376
  }) {
4371
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4377
+ const resolvedSide = side ?? "bottom";
4378
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4372
4379
  import_radix_ui3.Tooltip.Content,
4373
4380
  {
4374
4381
  "data-slot": "tooltip-content",
4375
4382
  side,
4376
4383
  sideOffset,
4377
4384
  className: cn(
4378
- "z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4385
+ "relative z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4379
4386
  "origin-[var(--radix-tooltip-content-transform-origin)] animate-in fade-in-0 zoom-in-95",
4380
4387
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4381
4388
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4382
4389
  "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
4390
+ !hideArrow && 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4391
+ !hideArrow && arrowClassBySide[resolvedSide],
4383
4392
  className
4384
4393
  ),
4385
4394
  ...props,
4386
- children: [
4387
- children,
4388
- !hideArrow && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Arrow, { className: "z-[2147483647] size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
4389
- ]
4395
+ children
4390
4396
  }
4391
4397
  ) });
4392
4398
  }
@@ -4403,7 +4409,7 @@ function ToolbarActionTooltip({
4403
4409
  const button = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, ...buttonProps, children });
4404
4410
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Tooltip, { children: [
4405
4411
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-flex", children: button }) : button }),
4406
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, sideOffset: 3, children: label })
4412
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, sideOffset: 9, children: label })
4407
4413
  ] });
4408
4414
  }
4409
4415
  function ItemActionToolbar({
@@ -4485,6 +4491,19 @@ var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0,
4485
4491
  var TOOLBAR_EDGE_MARGIN = 4;
4486
4492
  var SELECTION_CHROME_GAP = 4;
4487
4493
  var TOOLBAR_STROKE_GAP = 4;
4494
+ function getChromeZIndex(state) {
4495
+ switch (state) {
4496
+ case "active-top":
4497
+ case "active-bottom":
4498
+ case "dragging":
4499
+ return 2147483644;
4500
+ case "hover":
4501
+ case "sibling-hint":
4502
+ return 2147483643;
4503
+ default:
4504
+ return 2147483642;
4505
+ }
4506
+ }
4488
4507
  function getChromeStyle(state) {
4489
4508
  switch (state) {
4490
4509
  case "hover":
@@ -4506,7 +4525,7 @@ function getChromeStyle(state) {
4506
4525
  case "dragging":
4507
4526
  return {
4508
4527
  border: `2px solid ${PRIMARY}`,
4509
- boxShadow: DRAG_SHADOW
4528
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4510
4529
  };
4511
4530
  default:
4512
4531
  return {};
@@ -4575,6 +4594,9 @@ function ItemInteractionLayer({
4575
4594
  dragHandleLabel = "Reorder item",
4576
4595
  onDragHandleDragStart,
4577
4596
  onDragHandleDragEnd,
4597
+ onItemPointerDown,
4598
+ onItemClick,
4599
+ itemDragSurface = true,
4578
4600
  chromeGap = SELECTION_CHROME_GAP,
4579
4601
  className
4580
4602
  }) {
@@ -4582,7 +4604,8 @@ function ItemInteractionLayer({
4582
4604
  const isActive = state === "active-top" || state === "active-bottom";
4583
4605
  const isDragging = state === "dragging";
4584
4606
  const showToolbar = isActive && toolbar;
4585
- const showDragHandle = isActive && showHandle && !isDragging;
4607
+ const showDragHandle = (isActive || isDragging) && showHandle;
4608
+ const itemDragEnabled = itemDragSurface && isActive && showHandle && !isDragging && !dragDisabled;
4586
4609
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4587
4610
  "div",
4588
4611
  {
@@ -4596,7 +4619,7 @@ function ItemInteractionLayer({
4596
4619
  left: rect.left - chromeGap,
4597
4620
  width: rect.width + chromeGap * 2,
4598
4621
  height: rect.height + chromeGap * 2,
4599
- zIndex: 2147483646
4622
+ zIndex: getChromeZIndex(state)
4600
4623
  },
4601
4624
  children: [
4602
4625
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -4607,11 +4630,29 @@ function ItemInteractionLayer({
4607
4630
  style: getChromeStyle(state)
4608
4631
  }
4609
4632
  ),
4633
+ itemDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4634
+ "div",
4635
+ {
4636
+ "data-ohw-item-drag-surface": "",
4637
+ className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
4638
+ onPointerDown: (e) => {
4639
+ if (e.button !== 0) return;
4640
+ onItemPointerDown?.(e);
4641
+ },
4642
+ onClick: (e) => {
4643
+ e.preventDefault();
4644
+ e.stopPropagation();
4645
+ onItemClick?.(e.clientX, e.clientY);
4646
+ }
4647
+ }
4648
+ ),
4610
4649
  showDragHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4611
4650
  "div",
4612
4651
  {
4613
4652
  "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",
4653
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4654
+ style: isDragging ? { opacity: 0 } : void 0,
4655
+ "aria-hidden": isDragging || void 0,
4615
4656
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4616
4657
  DragHandle,
4617
4658
  {
@@ -4623,7 +4664,17 @@ function ItemInteractionLayer({
4623
4664
  e.preventDefault();
4624
4665
  return;
4625
4666
  }
4626
- e.dataTransfer?.setData("text/plain", dragHandleLabel);
4667
+ const blank = document.createElement("canvas");
4668
+ blank.width = 1;
4669
+ blank.height = 1;
4670
+ blank.style.cssText = "position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none";
4671
+ document.body.appendChild(blank);
4672
+ try {
4673
+ e.dataTransfer.setDragImage(blank, 0, 0);
4674
+ } finally {
4675
+ requestAnimationFrame(() => blank.remove());
4676
+ }
4677
+ e.dataTransfer.setData("text/plain", dragHandleLabel);
4627
4678
  e.dataTransfer.effectAllowed = "move";
4628
4679
  onDragHandleDragStart?.(e);
4629
4680
  },
@@ -6319,9 +6370,155 @@ function shouldUseDevFixtures() {
6319
6370
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6320
6371
  }
6321
6372
 
6373
+ // src/lib/nav-tree.ts
6374
+ var MAX_LEVEL = { nav: 1 };
6375
+ var ROOT = null;
6376
+ function cloneForest(nodes) {
6377
+ return nodes.map((n) => ({ id: n.id, children: cloneForest(n.children) }));
6378
+ }
6379
+ function findNode(nodes, id, parentId = ROOT) {
6380
+ for (let i = 0; i < nodes.length; i++) {
6381
+ const node = nodes[i];
6382
+ if (node.id === id) return { node, parentId, index: i, siblings: nodes };
6383
+ const nested = findNode(node.children, id, node.id);
6384
+ if (nested) return nested;
6385
+ }
6386
+ return null;
6387
+ }
6388
+ function getChildrenOf(nodes, parentId) {
6389
+ if (parentId === ROOT) return nodes;
6390
+ const found = findNode(nodes, parentId);
6391
+ return found ? found.node.children : null;
6392
+ }
6393
+ function isDescendant(nodes, ancestorId, maybeDescendantId) {
6394
+ const found = findNode(nodes, ancestorId);
6395
+ if (!found) return false;
6396
+ return findNode(found.node.children, maybeDescendantId) !== null;
6397
+ }
6398
+ function levelOf(nodes, id) {
6399
+ const loc = findNode(nodes, id);
6400
+ if (!loc) return -1;
6401
+ if (loc.parentId === ROOT) return 0;
6402
+ return 1 + Math.max(0, levelOf(nodes, loc.parentId));
6403
+ }
6404
+ function insertedLevel(nodes, parentId) {
6405
+ if (parentId === ROOT) return 0;
6406
+ const parentLevel = levelOf(nodes, parentId);
6407
+ if (parentLevel < 0) return -1;
6408
+ return parentLevel + 1;
6409
+ }
6410
+ function takeNode(nodes, id) {
6411
+ const forest = cloneForest(nodes);
6412
+ const loc = findNode(forest, id);
6413
+ if (!loc) return null;
6414
+ const [taken] = loc.siblings.splice(loc.index, 1);
6415
+ if (!taken) return null;
6416
+ return { forest, taken };
6417
+ }
6418
+ function insertInto(forest, node, parentId, index) {
6419
+ const siblings = getChildrenOf(forest, parentId);
6420
+ if (!siblings) return false;
6421
+ const clamped = Math.max(0, Math.min(index, siblings.length));
6422
+ siblings.splice(clamped, 0, node);
6423
+ return true;
6424
+ }
6425
+ function normalize(nodes) {
6426
+ const walk = (list) => list.map((n) => {
6427
+ const children = walk(n.children);
6428
+ return { id: n.id, children };
6429
+ });
6430
+ return walk(nodes);
6431
+ }
6432
+ function moveNode(model, draggedId, target) {
6433
+ const { parentId, index: targetIndex } = target;
6434
+ if (parentId === draggedId) return null;
6435
+ if (parentId !== ROOT && isDescendant(model, draggedId, parentId)) return null;
6436
+ const nextLevel = insertedLevel(model, parentId);
6437
+ if (nextLevel < 0 || nextLevel > MAX_LEVEL.nav) return null;
6438
+ const draggedLoc = findNode(model, draggedId);
6439
+ if (!draggedLoc) return null;
6440
+ if (parentId !== ROOT && draggedLoc.node.children.length > 0) return null;
6441
+ const taken = takeNode(model, draggedId);
6442
+ if (!taken) return null;
6443
+ let insertIndex = targetIndex;
6444
+ if (draggedLoc.parentId === parentId && draggedLoc.index < targetIndex) {
6445
+ insertIndex -= 1;
6446
+ }
6447
+ if (!insertInto(taken.forest, taken.taken, parentId, insertIndex)) return null;
6448
+ const next = normalize(taken.forest);
6449
+ if (forestsEqual(model, next)) return null;
6450
+ return next;
6451
+ }
6452
+ function forestsEqual(a, b) {
6453
+ if (a.length !== b.length) return false;
6454
+ for (let i = 0; i < a.length; i++) {
6455
+ const left = a[i];
6456
+ const right = b[i];
6457
+ if (left.id !== right.id) return false;
6458
+ if (!forestsEqual(left.children, right.children)) return false;
6459
+ }
6460
+ return true;
6461
+ }
6462
+ function forestFromFlatOrder(order) {
6463
+ return order.map((id) => ({ id, children: [] }));
6464
+ }
6465
+ function serializeNavOrder(nodes) {
6466
+ const hasNesting = nodes.some((n) => n.children.length > 0);
6467
+ if (!hasNesting) {
6468
+ return JSON.stringify(nodes.map((n) => n.id));
6469
+ }
6470
+ return JSON.stringify(nodes);
6471
+ }
6472
+ function parseNavOrderJson(raw) {
6473
+ if (!raw) return null;
6474
+ try {
6475
+ const parsed = JSON.parse(raw);
6476
+ if (!Array.isArray(parsed)) return null;
6477
+ if (parsed.every((x) => typeof x === "string")) {
6478
+ return forestFromFlatOrder(parsed.filter((x) => typeof x === "string" && x.length > 0));
6479
+ }
6480
+ return parseNavNodeList(parsed);
6481
+ } catch {
6482
+ return null;
6483
+ }
6484
+ }
6485
+ function parseNavNodeList(raw) {
6486
+ const out = [];
6487
+ for (const item of raw) {
6488
+ if (typeof item === "string" && item.length > 0) {
6489
+ out.push({ id: item, children: [] });
6490
+ continue;
6491
+ }
6492
+ if (!item || typeof item !== "object") return null;
6493
+ const rec = item;
6494
+ if (typeof rec.id !== "string" || !rec.id) return null;
6495
+ const children = Array.isArray(rec.children) ? parseNavNodeList(rec.children) : [];
6496
+ if (!children) return null;
6497
+ out.push({ id: rec.id, children });
6498
+ }
6499
+ return out;
6500
+ }
6501
+ function flattenNavOrder(nodes) {
6502
+ const out = [];
6503
+ const walk = (list) => {
6504
+ for (const n of list) {
6505
+ out.push(n.id);
6506
+ walk(n.children);
6507
+ }
6508
+ };
6509
+ walk(nodes);
6510
+ return out;
6511
+ }
6512
+
6322
6513
  // src/lib/nav-items.ts
6323
6514
  var NAV_COUNT_KEY = "__ohw_nav_count";
6324
6515
  var NAV_ORDER_KEY = "__ohw_nav_order";
6516
+ var NAV_HREF_RE = /^nav-(\d+)-href$/;
6517
+ function isNavbarHrefKey(key) {
6518
+ if (!key) return false;
6519
+ if (key === "nav-book-href") return false;
6520
+ return NAV_HREF_RE.test(key);
6521
+ }
6325
6522
  function getLinkHref(el) {
6326
6523
  const key = el.getAttribute("data-ohw-href-key");
6327
6524
  if (key) {
@@ -6358,7 +6555,7 @@ function listNavbarItems() {
6358
6555
  }
6359
6556
  function parseNavIndexFromKey(key) {
6360
6557
  if (!key) return null;
6361
- const match = key.match(/^nav-(\d+)-href$/);
6558
+ const match = key.match(NAV_HREF_RE);
6362
6559
  if (!match) return null;
6363
6560
  return parseInt(match[1], 10);
6364
6561
  }
@@ -6381,17 +6578,6 @@ function getNavbarExistingTargets() {
6381
6578
  function getNavOrderFromDom() {
6382
6579
  return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6383
6580
  }
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
6581
  function findCounterpartByHrefKey(hrefKey, root) {
6396
6582
  return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6397
6583
  }
@@ -6455,6 +6641,13 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6455
6641
  }
6456
6642
  return primary;
6457
6643
  }
6644
+ function navOrderKeysEqual(a, b) {
6645
+ if (a.length !== b.length) return false;
6646
+ for (let i = 0; i < a.length; i++) {
6647
+ if (a[i] !== b[i]) return false;
6648
+ }
6649
+ return true;
6650
+ }
6458
6651
  function getNavbarLinkKeysInContainer(container) {
6459
6652
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6460
6653
  }
@@ -6463,6 +6656,8 @@ function applyNavOrderToContainer(container, order) {
6463
6656
  const current = getNavbarLinkKeysInContainer(container);
6464
6657
  const extras = current.filter((key) => !desired.includes(key));
6465
6658
  const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6659
+ const orderedEls = [];
6660
+ if (navOrderKeysEqual(current, expected)) return;
6466
6661
  if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6467
6662
  return;
6468
6663
  }
@@ -6471,13 +6666,18 @@ function applyNavOrderToContainer(container, order) {
6471
6666
  if (seen.has(hrefKey)) continue;
6472
6667
  seen.add(hrefKey);
6473
6668
  const el = findCounterpartByHrefKey(hrefKey, container);
6474
- if (el) container.appendChild(el);
6669
+ if (el && isNavbarLinkItem(el)) orderedEls.push(el);
6475
6670
  }
6476
6671
  for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6477
6672
  if (!isNavbarLinkItem(el)) continue;
6478
6673
  const key = el.getAttribute("data-ohw-href-key");
6479
6674
  if (!key || seen.has(key)) continue;
6480
- container.appendChild(el);
6675
+ orderedEls.push(el);
6676
+ }
6677
+ for (const el of orderedEls) {
6678
+ if (container.lastElementChild !== el) {
6679
+ container.appendChild(el);
6680
+ }
6481
6681
  }
6482
6682
  }
6483
6683
  function applyNavOrder(order) {
@@ -6486,6 +6686,75 @@ function applyNavOrder(order) {
6486
6686
  const drawer = getNavbarDrawerContainer();
6487
6687
  if (drawer) applyNavOrderToContainer(drawer, order);
6488
6688
  }
6689
+ function applyChildrenOrder(parentHrefKey, childKeys, root) {
6690
+ const parent = findCounterpartByHrefKey(parentHrefKey, root);
6691
+ if (!parent) return;
6692
+ const group = parent.closest("[data-ohw-nav-group]");
6693
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6694
+ if (!childrenRoot) return;
6695
+ const current = Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6696
+ if (navOrderKeysEqual(current, childKeys)) return;
6697
+ for (const key of childKeys) {
6698
+ const el = findCounterpartByHrefKey(key, root);
6699
+ if (!el) continue;
6700
+ if (childrenRoot.lastElementChild !== el) {
6701
+ childrenRoot.appendChild(el);
6702
+ }
6703
+ }
6704
+ }
6705
+ function applyNavForest(forest) {
6706
+ if (forestsEqual(forest, getNavForestFromDom())) return;
6707
+ const rootKeys = forest.map((n) => n.id);
6708
+ applyNavOrder(rootKeys);
6709
+ const desktop = getNavbarDesktopContainer();
6710
+ const drawer = getNavbarDrawerContainer();
6711
+ for (const node of forest) {
6712
+ if (node.children.length === 0) continue;
6713
+ const childKeys = node.children.map((c) => c.id);
6714
+ if (desktop) applyChildrenOrder(node.id, childKeys, desktop);
6715
+ if (drawer) applyChildrenOrder(node.id, childKeys, drawer);
6716
+ }
6717
+ }
6718
+ function getNavForestFromDom() {
6719
+ const items = listNavbarItems();
6720
+ const nestedChildKeys = /* @__PURE__ */ new Set();
6721
+ const roots = [];
6722
+ for (const item of items) {
6723
+ const key = item.getAttribute("data-ohw-href-key");
6724
+ if (!key || !isNavbarHrefKey(key)) continue;
6725
+ if (item.closest("[data-ohw-nav-children]")) continue;
6726
+ const group = item.closest("[data-ohw-nav-group]");
6727
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6728
+ const children = [];
6729
+ if (childrenRoot) {
6730
+ for (const child of childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")) {
6731
+ if (!isNavbarLinkItem(child)) continue;
6732
+ const childKey = child.getAttribute("data-ohw-href-key");
6733
+ if (!childKey || !isNavbarHrefKey(childKey)) continue;
6734
+ nestedChildKeys.add(childKey);
6735
+ children.push({ id: childKey, children: [] });
6736
+ }
6737
+ }
6738
+ roots.push({ id: key, children });
6739
+ }
6740
+ if (roots.length === 0) {
6741
+ return forestFromFlatOrder(
6742
+ getNavOrderFromDom().filter((k) => isNavbarHrefKey(k) && !nestedChildKeys.has(k))
6743
+ );
6744
+ }
6745
+ return roots;
6746
+ }
6747
+ function planNavItemMove(hrefKey, parentId, insertIndex) {
6748
+ const model = getNavForestFromDom();
6749
+ const next = moveNode(model, hrefKey, { parentId, index: insertIndex });
6750
+ if (!next) return null;
6751
+ if (forestsEqual(model, next)) return null;
6752
+ return {
6753
+ forest: next,
6754
+ orderJson: serializeNavOrder(next),
6755
+ flatOrder: flattenNavOrder(next)
6756
+ };
6757
+ }
6489
6758
  function collectNavbarIndicesFromContent(content) {
6490
6759
  const indices = /* @__PURE__ */ new Set();
6491
6760
  for (const key of Object.keys(content)) {
@@ -6513,8 +6782,10 @@ function reconcileNavbarItemsFromContent(content) {
6513
6782
  if (!href && !label) continue;
6514
6783
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6515
6784
  }
6516
- const order = parseNavOrder(content);
6517
- if (order?.length) applyNavOrder(order);
6785
+ const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
6786
+ if (orderForest?.length) {
6787
+ applyNavForest(orderForest);
6788
+ }
6518
6789
  }
6519
6790
  function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6520
6791
  const order = getNavOrderFromDom();
@@ -6554,160 +6825,1050 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6554
6825
  };
6555
6826
  }
6556
6827
 
6557
- // src/ui/navbar-container-chrome.tsx
6558
- var import_lucide_react10 = require("lucide-react");
6559
- var import_jsx_runtime21 = require("react/jsx-runtime");
6560
- function NavbarContainerChrome({
6561
- rect,
6562
- onAdd
6563
- }) {
6564
- const chromeGap = 6;
6565
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6566
- "div",
6567
- {
6568
- "data-ohw-navbar-container-chrome": "",
6569
- "data-ohw-bridge": "",
6570
- className: "pointer-events-none fixed z-[2147483647]",
6571
- style: {
6572
- top: rect.top - chromeGap,
6573
- left: rect.left - chromeGap,
6574
- width: rect.width + chromeGap * 2,
6575
- height: rect.height + chromeGap * 2
6576
- },
6577
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6578
- "button",
6579
- {
6580
- type: "button",
6581
- "data-ohw-navbar-add-button": "",
6582
- className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
6583
- style: { top: "70%" },
6584
- "aria-label": "Add item",
6585
- onMouseDown: (e) => {
6586
- e.preventDefault();
6587
- e.stopPropagation();
6588
- },
6589
- onClick: (e) => {
6590
- e.preventDefault();
6591
- e.stopPropagation();
6592
- onAdd();
6593
- },
6594
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6595
- }
6596
- )
6597
- }
6598
- );
6828
+ // src/lib/footer-items.ts
6829
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6830
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6831
+ function parseFooterHrefKey(key) {
6832
+ if (!key) return null;
6833
+ const match = key.match(FOOTER_HREF_RE);
6834
+ if (!match) return null;
6835
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6599
6836
  }
6600
-
6601
- // src/ui/badge.tsx
6602
- var import_jsx_runtime22 = require("react/jsx-runtime");
6603
- var badgeVariants = cva(
6604
- "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
- {
6606
- variants: {
6607
- variant: {
6608
- default: "border-transparent bg-primary text-primary-foreground",
6609
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
6610
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
6611
- outline: "text-foreground"
6612
- }
6613
- },
6614
- defaultVariants: {
6615
- variant: "default"
6616
- }
6617
- }
6618
- );
6619
- function Badge({ className, variant, ...props }) {
6620
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6837
+ function isFooterHrefKey(key) {
6838
+ return parseFooterHrefKey(key) !== null;
6621
6839
  }
6622
-
6623
- // src/OhhwellsBridge.tsx
6624
- var import_lucide_react11 = require("lucide-react");
6625
- var import_jsx_runtime23 = require("react/jsx-runtime");
6626
- var PRIMARY2 = "#0885FE";
6627
- var IMAGE_FADE_MS = 300;
6628
- function runOpacityFade(el, onDone) {
6629
- const anim = el.animate([{ opacity: 0 }, { opacity: 1 }], {
6630
- duration: IMAGE_FADE_MS,
6631
- easing: "ease",
6632
- fill: "forwards"
6633
- });
6634
- let finished = false;
6635
- const finish = () => {
6636
- if (finished) return;
6637
- finished = true;
6638
- anim.cancel();
6639
- el.style.opacity = "";
6640
- onDone();
6641
- };
6642
- anim.finished.then(finish).catch(finish);
6643
- window.setTimeout(finish, IMAGE_FADE_MS + 100);
6840
+ function getFooterRoot() {
6841
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6644
6842
  }
6645
- function fadeInImageElement(img, onReady) {
6646
- onReady();
6647
- img.style.opacity = "0";
6648
- runOpacityFade(img, () => {
6649
- img.style.opacity = "";
6843
+ function getFooterLinksContainer() {
6844
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6845
+ }
6846
+ function isFooterLinkAnchor(el) {
6847
+ if (!el.matches("[data-ohw-href-key]")) return false;
6848
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6849
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6850
+ }
6851
+ function listFooterColumns() {
6852
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6853
+ if (!root) return [];
6854
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6855
+ if (explicit.length > 0) return explicit;
6856
+ return Array.from(root.children).filter((el) => {
6857
+ if (!(el instanceof HTMLElement)) return false;
6858
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6859
+ isFooterLinkAnchor
6860
+ );
6650
6861
  });
6651
6862
  }
6652
- function applyEditableImageSrc(img, url) {
6653
- img.removeAttribute("srcset");
6654
- img.removeAttribute("sizes");
6655
- img.src = url;
6863
+ function listFooterLinksInColumn(column) {
6864
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6865
+ isFooterLinkAnchor
6866
+ );
6656
6867
  }
6657
- function fadeInBgImage(el, url, onReady) {
6658
- const prevPos = el.style.position;
6659
- if (!prevPos || prevPos === "static") el.style.position = "relative";
6660
- const layer = document.createElement("div");
6661
- layer.setAttribute("data-ohw-bg-fade-layer", "");
6662
- Object.assign(layer.style, {
6663
- position: "absolute",
6664
- inset: "0",
6665
- zIndex: "0",
6666
- backgroundImage: `url('${url}')`,
6667
- backgroundSize: "cover",
6668
- backgroundPosition: "center",
6669
- backgroundRepeat: "no-repeat",
6670
- opacity: "0",
6671
- pointerEvents: "none"
6672
- });
6673
- el.prepend(layer);
6674
- onReady();
6675
- runOpacityFade(layer, () => {
6676
- el.style.backgroundImage = `url('${url}')`;
6677
- layer.remove();
6678
- if (!prevPos || prevPos === "static") el.style.position = prevPos;
6868
+ function findFooterColumnForLink(anchor) {
6869
+ const columns = listFooterColumns();
6870
+ return columns.find((col) => col.contains(anchor)) ?? null;
6871
+ }
6872
+ function getFooterOrderFromDom() {
6873
+ return listFooterColumns().map(
6874
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6875
+ );
6876
+ }
6877
+ function findFooterLinkByKey(hrefKey) {
6878
+ return document.querySelector(
6879
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6880
+ );
6881
+ }
6882
+ function syncFooterColumnIndices(columns) {
6883
+ columns.forEach((col, index) => {
6884
+ const next = String(index);
6885
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6886
+ col.setAttribute("data-ohw-footer-col", next);
6887
+ }
6679
6888
  });
6680
6889
  }
6681
- function getSectionsTracker() {
6682
- let el = document.querySelector("[data-ohw-sections-tracker]");
6683
- if (!el) {
6684
- el = document.createElement("div");
6685
- el.setAttribute("data-ohw-sections-tracker", "");
6686
- el.style.display = "none";
6687
- document.body.appendChild(el);
6890
+ function appendChildIfNeeded(parent, child) {
6891
+ if (parent.lastElementChild !== child) {
6892
+ parent.appendChild(child);
6688
6893
  }
6689
- return el;
6690
6894
  }
6691
- function updateSectionScheduleId(insertAfter, scheduleId) {
6692
- const tracker = getSectionsTracker();
6693
- let sections = [];
6694
- try {
6695
- sections = JSON.parse(tracker.textContent || "[]");
6696
- } catch {
6895
+ function applyFooterOrder(order) {
6896
+ const container = getFooterLinksContainer();
6897
+ if (!container) return;
6898
+ const columns = listFooterColumns();
6899
+ if (columns.length === 0) return;
6900
+ const currentOrder = getFooterOrderFromDom();
6901
+ if (ordersEqual(order, currentOrder)) {
6902
+ syncFooterColumnIndices(columns);
6903
+ return;
6904
+ }
6905
+ const used = /* @__PURE__ */ new Set();
6906
+ const orderedCols = [];
6907
+ for (const colOrder of order) {
6908
+ let matched = null;
6909
+ for (const hrefKey of colOrder) {
6910
+ const link = findFooterLinkByKey(hrefKey);
6911
+ if (!link) continue;
6912
+ const owner = findFooterColumnForLink(link);
6913
+ if (owner && !used.has(owner)) {
6914
+ matched = owner;
6915
+ break;
6916
+ }
6917
+ }
6918
+ if (!matched) {
6919
+ matched = columns.find((col) => !used.has(col)) ?? null;
6920
+ }
6921
+ if (matched) {
6922
+ used.add(matched);
6923
+ orderedCols.push(matched);
6924
+ }
6925
+ }
6926
+ for (const col of columns) {
6927
+ if (!used.has(col)) orderedCols.push(col);
6928
+ }
6929
+ for (const col of orderedCols) {
6930
+ appendChildIfNeeded(container, col);
6931
+ }
6932
+ const freshColumns = listFooterColumns();
6933
+ syncFooterColumnIndices(freshColumns);
6934
+ for (let c = 0; c < order.length; c++) {
6935
+ const col = freshColumns[c];
6936
+ if (!col) continue;
6937
+ const colOrder = order[c];
6938
+ for (const hrefKey of colOrder) {
6939
+ const el = findFooterLinkByKey(hrefKey);
6940
+ if (!el) continue;
6941
+ if (el.parentElement !== col || col.lastElementChild !== el) {
6942
+ col.appendChild(el);
6943
+ }
6944
+ }
6697
6945
  }
6698
- const currentPath = window.location.pathname;
6699
- const updated = sections.map((s) => {
6700
- if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
6701
- if (s.pagePath && s.pagePath !== currentPath) return s;
6702
- return { ...s, scheduleId };
6703
- });
6704
- tracker.textContent = JSON.stringify(updated);
6705
- return tracker.textContent ?? "[]";
6706
6946
  }
6707
- function schedulingSectionId(insertAfter) {
6708
- return `scheduling-${insertAfter}`;
6947
+ function ordersEqual(a, b) {
6948
+ if (a.length !== b.length) return false;
6949
+ for (let i = 0; i < a.length; i++) {
6950
+ const left = a[i];
6951
+ const right = b[i];
6952
+ if (left.length !== right.length) return false;
6953
+ for (let j = 0; j < left.length; j++) {
6954
+ if (left[j] !== right[j]) return false;
6955
+ }
6956
+ }
6957
+ return true;
6709
6958
  }
6710
- var INSERT_BEFORE_MARKER = ":before:";
6959
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6960
+ const order = getFooterOrderFromDom();
6961
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6962
+ let fromCol = -1;
6963
+ let fromIdx = -1;
6964
+ for (let c = 0; c < order.length; c++) {
6965
+ const idx = order[c].indexOf(hrefKey);
6966
+ if (idx >= 0) {
6967
+ fromCol = c;
6968
+ fromIdx = idx;
6969
+ break;
6970
+ }
6971
+ }
6972
+ if (fromCol < 0) return null;
6973
+ const next = order.map((col) => [...col]);
6974
+ next[fromCol].splice(fromIdx, 1);
6975
+ let adjusted = insertIndex;
6976
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6977
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6978
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
6979
+ if (ordersEqual(next, order)) return null;
6980
+ return next;
6981
+ }
6982
+ function planFooterColumnMove(fromIndex, toIndex) {
6983
+ const order = getFooterOrderFromDom();
6984
+ const columns = listFooterColumns();
6985
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6986
+ return null;
6987
+ }
6988
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6989
+ const nextOrder = order.map((col) => [...col]);
6990
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
6991
+ if (!movedOrder) return null;
6992
+ let adjusted = toIndex;
6993
+ if (fromIndex < toIndex) adjusted -= 1;
6994
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6995
+ nextOrder.splice(adjusted, 0, movedOrder);
6996
+ if (ordersEqual(nextOrder, order)) return null;
6997
+ return nextOrder;
6998
+ }
6999
+ function parseFooterOrder(content) {
7000
+ const raw = content[FOOTER_ORDER_KEY];
7001
+ if (!raw) return null;
7002
+ try {
7003
+ const parsed = JSON.parse(raw);
7004
+ if (!Array.isArray(parsed)) return null;
7005
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
7006
+ } catch {
7007
+ return null;
7008
+ }
7009
+ }
7010
+ function reconcileFooterOrderFromContent(content) {
7011
+ const order = parseFooterOrder(content);
7012
+ if (!order?.length) return;
7013
+ const current = getFooterOrderFromDom();
7014
+ if (ordersEqual(order, current)) {
7015
+ syncFooterColumnIndices(listFooterColumns());
7016
+ return;
7017
+ }
7018
+ applyFooterOrder(order);
7019
+ }
7020
+ function buildLinkDropSlots(column, columnIndex) {
7021
+ const links = listFooterLinksInColumn(column);
7022
+ const colRect = column.getBoundingClientRect();
7023
+ const slots = [];
7024
+ const barThickness = 3;
7025
+ if (links.length === 0) {
7026
+ slots.push({
7027
+ insertIndex: 0,
7028
+ columnIndex,
7029
+ left: colRect.left,
7030
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
7031
+ width: colRect.width,
7032
+ height: barThickness,
7033
+ direction: "horizontal"
7034
+ });
7035
+ return slots;
7036
+ }
7037
+ for (let i = 0; i <= links.length; i++) {
7038
+ let top;
7039
+ if (i === 0) {
7040
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
7041
+ } else if (i === links.length) {
7042
+ const last = links[links.length - 1].getBoundingClientRect();
7043
+ top = last.bottom - barThickness / 2;
7044
+ } else {
7045
+ const prev = links[i - 1].getBoundingClientRect();
7046
+ const next = links[i].getBoundingClientRect();
7047
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
7048
+ }
7049
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7050
+ slots.push({
7051
+ insertIndex: i,
7052
+ columnIndex,
7053
+ left: widthRef.left,
7054
+ top,
7055
+ width: Math.max(widthRef.width, colRect.width * 0.8),
7056
+ height: barThickness,
7057
+ direction: "horizontal"
7058
+ });
7059
+ }
7060
+ return slots;
7061
+ }
7062
+ function buildColumnDropSlots() {
7063
+ const columns = listFooterColumns();
7064
+ const slots = [];
7065
+ const barThickness = 3;
7066
+ if (columns.length === 0) return slots;
7067
+ for (let i = 0; i <= columns.length; i++) {
7068
+ let left;
7069
+ let height;
7070
+ let top;
7071
+ if (i === 0) {
7072
+ const first = columns[0].getBoundingClientRect();
7073
+ left = first.left - barThickness / 2;
7074
+ top = first.top;
7075
+ height = first.height;
7076
+ } else if (i === columns.length) {
7077
+ const last = columns[columns.length - 1].getBoundingClientRect();
7078
+ left = last.right - barThickness / 2;
7079
+ top = last.top;
7080
+ height = last.height;
7081
+ } else {
7082
+ const prev = columns[i - 1].getBoundingClientRect();
7083
+ const next = columns[i].getBoundingClientRect();
7084
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7085
+ top = Math.min(prev.top, next.top);
7086
+ height = Math.max(prev.bottom, next.bottom) - top;
7087
+ }
7088
+ slots.push({
7089
+ insertIndex: i,
7090
+ columnIndex: i,
7091
+ left,
7092
+ top,
7093
+ width: barThickness,
7094
+ height: Math.max(height, 24),
7095
+ direction: "vertical"
7096
+ });
7097
+ }
7098
+ return slots;
7099
+ }
7100
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
7101
+ const columns = listFooterColumns();
7102
+ let best = null;
7103
+ for (let c = 0; c < columns.length; c++) {
7104
+ const col = columns[c];
7105
+ const colRect = col.getBoundingClientRect();
7106
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
7107
+ if (!inColX) continue;
7108
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
7109
+ const links = listFooterLinksInColumn(col);
7110
+ const fromIdx = links.findIndex(
7111
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
7112
+ );
7113
+ if (fromIdx < 0) return true;
7114
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
7115
+ return true;
7116
+ }
7117
+ return true;
7118
+ });
7119
+ for (const slot of slots) {
7120
+ const cy = slot.top + slot.height / 2;
7121
+ const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
7122
+ if (!best || dist < best.dist) best = { slot, dist };
7123
+ }
7124
+ }
7125
+ return best?.slot ?? null;
7126
+ }
7127
+ function findColumnIndexForKey(hrefKey) {
7128
+ const order = getFooterOrderFromDom();
7129
+ for (let c = 0; c < order.length; c++) {
7130
+ if (order[c].includes(hrefKey)) return c;
7131
+ }
7132
+ return -1;
7133
+ }
7134
+ function hitTestColumnDropSlot(clientX, _clientY) {
7135
+ const slots = buildColumnDropSlots();
7136
+ let best = null;
7137
+ for (const slot of slots) {
7138
+ const cx2 = slot.left + slot.width / 2;
7139
+ const dist = Math.abs(clientX - cx2);
7140
+ if (!best || dist < best.dist) best = { slot, dist };
7141
+ }
7142
+ return best?.slot ?? null;
7143
+ }
7144
+
7145
+ // src/lib/item-drag-interaction.ts
7146
+ function disableNativeHrefDrag(el) {
7147
+ if (el.draggable) el.draggable = false;
7148
+ if (el.getAttribute("draggable") !== "false") {
7149
+ el.setAttribute("draggable", "false");
7150
+ }
7151
+ }
7152
+ function clearTextSelection() {
7153
+ const sel = window.getSelection();
7154
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7155
+ }
7156
+ function armItemPressDrag() {
7157
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7158
+ }
7159
+ function lockItemDuringDrag() {
7160
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7161
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7162
+ clearTextSelection();
7163
+ }
7164
+ function unlockItemDragInteraction() {
7165
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7166
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7167
+ clearTextSelection();
7168
+ }
7169
+ var armFooterPressDrag = armItemPressDrag;
7170
+ var lockFooterDuringDrag = lockItemDuringDrag;
7171
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
7172
+
7173
+ // src/lib/nav-dnd.ts
7174
+ function listReorderableNavItems() {
7175
+ return listNavbarItems().filter((el) => {
7176
+ const key = el.getAttribute("data-ohw-href-key");
7177
+ return isNavbarHrefKey(key) && !el.closest("[data-ohw-nav-children]");
7178
+ });
7179
+ }
7180
+ function listNavChildren(parentHrefKey) {
7181
+ const items = listNavbarItems();
7182
+ const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
7183
+ if (!parent) return [];
7184
+ const group = parent.closest("[data-ohw-nav-group]");
7185
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
7186
+ if (!childrenRoot) return [];
7187
+ return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
7188
+ (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
7189
+ );
7190
+ }
7191
+ function activeNavListContainer() {
7192
+ const desktop = getNavbarDesktopContainer();
7193
+ if (desktop && desktop.getClientRects().length > 0) {
7194
+ const style = window.getComputedStyle(desktop);
7195
+ if (style.display !== "none" && style.visibility !== "hidden") return desktop;
7196
+ }
7197
+ return getNavbarDrawerContainer();
7198
+ }
7199
+ function buildRootNavDropSlots() {
7200
+ const items = listReorderableNavItems();
7201
+ const slots = [];
7202
+ const barThickness = 3;
7203
+ if (items.length === 0) {
7204
+ const container = activeNavListContainer();
7205
+ if (!container) return slots;
7206
+ const rect = container.getBoundingClientRect();
7207
+ slots.push({
7208
+ insertIndex: 0,
7209
+ parentId: null,
7210
+ left: rect.left + rect.width / 2 - barThickness / 2,
7211
+ top: rect.top,
7212
+ width: barThickness,
7213
+ height: Math.max(rect.height, 24),
7214
+ direction: "vertical"
7215
+ });
7216
+ return slots;
7217
+ }
7218
+ const edgeGap = (() => {
7219
+ if (items.length < 2) return 32;
7220
+ const a = items[0].getBoundingClientRect();
7221
+ const b = items[1].getBoundingClientRect();
7222
+ return Math.max(0, b.left - a.right);
7223
+ })();
7224
+ for (let i = 0; i <= items.length; i++) {
7225
+ let left;
7226
+ let top;
7227
+ let height;
7228
+ if (i === 0) {
7229
+ const first = items[0].getBoundingClientRect();
7230
+ left = first.left - edgeGap / 2 - barThickness / 2;
7231
+ top = first.top;
7232
+ height = first.height;
7233
+ } else if (i === items.length) {
7234
+ const last = items[items.length - 1].getBoundingClientRect();
7235
+ const lastGap = items.length >= 2 ? Math.max(
7236
+ 0,
7237
+ last.left - items[items.length - 2].getBoundingClientRect().right
7238
+ ) : edgeGap;
7239
+ left = last.right + lastGap / 2 - barThickness / 2;
7240
+ top = last.top;
7241
+ height = last.height;
7242
+ } else {
7243
+ const prev = items[i - 1].getBoundingClientRect();
7244
+ const next = items[i].getBoundingClientRect();
7245
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7246
+ top = Math.min(prev.top, next.top);
7247
+ height = Math.max(prev.bottom, next.bottom) - top;
7248
+ }
7249
+ slots.push({
7250
+ insertIndex: i,
7251
+ parentId: null,
7252
+ left,
7253
+ top,
7254
+ width: barThickness,
7255
+ height: Math.max(height, 24),
7256
+ direction: "vertical"
7257
+ });
7258
+ }
7259
+ return slots;
7260
+ }
7261
+ function buildChildNavDropSlots(parentHrefKey) {
7262
+ const children = listNavChildren(parentHrefKey);
7263
+ const slots = [];
7264
+ const barThickness = 3;
7265
+ if (children.length === 0) return slots;
7266
+ const edgeGap = (() => {
7267
+ if (children.length < 2) return 16;
7268
+ const a = children[0].getBoundingClientRect();
7269
+ const b = children[1].getBoundingClientRect();
7270
+ return Math.max(0, b.top - a.bottom);
7271
+ })();
7272
+ for (let i = 0; i <= children.length; i++) {
7273
+ let top;
7274
+ let width;
7275
+ let left;
7276
+ if (i === 0) {
7277
+ const first = children[0].getBoundingClientRect();
7278
+ top = first.top - edgeGap / 2 - barThickness / 2;
7279
+ left = first.left;
7280
+ width = first.width;
7281
+ } else if (i === children.length) {
7282
+ const last = children[children.length - 1].getBoundingClientRect();
7283
+ const lastGap = children.length >= 2 ? Math.max(
7284
+ 0,
7285
+ last.top - children[children.length - 2].getBoundingClientRect().bottom
7286
+ ) : edgeGap;
7287
+ top = last.bottom + lastGap / 2 - barThickness / 2;
7288
+ left = last.left;
7289
+ width = last.width;
7290
+ } else {
7291
+ const prev = children[i - 1].getBoundingClientRect();
7292
+ const next = children[i].getBoundingClientRect();
7293
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
7294
+ left = Math.min(prev.left, next.left);
7295
+ width = Math.max(prev.right, next.right) - left;
7296
+ }
7297
+ slots.push({
7298
+ insertIndex: i,
7299
+ parentId: parentHrefKey,
7300
+ left,
7301
+ top,
7302
+ width: Math.max(width, 40),
7303
+ height: barThickness,
7304
+ direction: "horizontal"
7305
+ });
7306
+ }
7307
+ return slots;
7308
+ }
7309
+ function buildAllNavDropSlots(draggedHrefKey) {
7310
+ const slots = [...buildRootNavDropSlots()];
7311
+ for (const item of listReorderableNavItems()) {
7312
+ const key = item.getAttribute("data-ohw-href-key");
7313
+ if (!key || key === draggedHrefKey) continue;
7314
+ const group = item.closest("[data-ohw-nav-group]");
7315
+ if (!group?.querySelector(":scope > [data-ohw-nav-children]")) continue;
7316
+ slots.push(...buildChildNavDropSlots(key));
7317
+ }
7318
+ const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
7319
+ const parentGroup = dragged?.closest("[data-ohw-nav-children]")?.closest("[data-ohw-nav-group]");
7320
+ const parentTrigger = parentGroup?.querySelector(":scope > [data-ohw-href-key]");
7321
+ const parentKey = parentTrigger?.getAttribute("data-ohw-href-key");
7322
+ if (parentKey && isNavbarHrefKey(parentKey)) {
7323
+ const existing = slots.some((s) => s.parentId === parentKey);
7324
+ if (!existing) slots.push(...buildChildNavDropSlots(parentKey));
7325
+ }
7326
+ return slots;
7327
+ }
7328
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
7329
+ const slots = buildAllNavDropSlots(draggedHrefKey);
7330
+ let best = null;
7331
+ for (const slot of slots) {
7332
+ const cx2 = slot.left + slot.width / 2;
7333
+ const cy = slot.top + slot.height / 2;
7334
+ 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;
7335
+ if (!best || dist < best.dist) best = { slot, dist };
7336
+ }
7337
+ return best?.slot ?? null;
7338
+ }
7339
+
7340
+ // src/useNavItemDrag.ts
7341
+ var import_react8 = require("react");
7342
+ function useNavItemDrag({
7343
+ isEditMode,
7344
+ editContentRef,
7345
+ selectedElRef,
7346
+ activeElRef,
7347
+ footerDragRef,
7348
+ suppressNextClickRef,
7349
+ suppressClickUntilRef,
7350
+ siblingHintElRef,
7351
+ postToParentRef,
7352
+ deselectRef,
7353
+ selectRef,
7354
+ deactivateRef,
7355
+ setLinkPopover,
7356
+ linkPopoverOpenRef,
7357
+ setIsItemDragging,
7358
+ setDraggedItemRect,
7359
+ setSiblingHintRect,
7360
+ setSiblingHintRects,
7361
+ setToolbarRect,
7362
+ getNavigationItemAnchor: getNavigationItemAnchor2,
7363
+ isDragHandleDisabled: isDragHandleDisabled2,
7364
+ isNavbarButton: isNavbarButton3
7365
+ }) {
7366
+ const navDragRef = (0, import_react8.useRef)(null);
7367
+ const [navDropSlots, setNavDropSlots] = (0, import_react8.useState)([]);
7368
+ const [activeNavDropIndex, setActiveNavDropIndex] = (0, import_react8.useState)(null);
7369
+ const navPointerDragRef = (0, import_react8.useRef)(null);
7370
+ const clearNavDragVisuals = (0, import_react8.useCallback)(() => {
7371
+ navDragRef.current = null;
7372
+ setNavDropSlots([]);
7373
+ setActiveNavDropIndex(null);
7374
+ setDraggedItemRect(null);
7375
+ setSiblingHintRects([]);
7376
+ setIsItemDragging(false);
7377
+ unlockItemDragInteraction();
7378
+ }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
7379
+ const refreshNavDragVisuals = (0, import_react8.useCallback)(
7380
+ (session, activeSlot, clientX, clientY) => {
7381
+ setDraggedItemRect(session.draggedEl.getBoundingClientRect());
7382
+ if (typeof clientX === "number" && typeof clientY === "number") {
7383
+ session.lastClientX = clientX;
7384
+ session.lastClientY = clientY;
7385
+ }
7386
+ session.activeSlot = activeSlot;
7387
+ setSiblingHintRects([]);
7388
+ const slots = buildAllNavDropSlots(session.hrefKey);
7389
+ setNavDropSlots(slots);
7390
+ const activeIdx = activeSlot ? slots.findIndex(
7391
+ (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
7392
+ ) : -1;
7393
+ setActiveNavDropIndex(activeIdx >= 0 ? activeIdx : null);
7394
+ },
7395
+ [setDraggedItemRect, setSiblingHintRects]
7396
+ );
7397
+ const refreshNavDragVisualsRef = (0, import_react8.useRef)(refreshNavDragVisuals);
7398
+ refreshNavDragVisualsRef.current = refreshNavDragVisuals;
7399
+ const commitNavDragRef = (0, import_react8.useRef)(() => {
7400
+ });
7401
+ const beginNavDragRef = (0, import_react8.useRef)(() => {
7402
+ });
7403
+ const beginNavDrag = (0, import_react8.useCallback)(
7404
+ (session) => {
7405
+ const rect = session.draggedEl.getBoundingClientRect();
7406
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
7407
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
7408
+ session.activeSlot = session.activeSlot ?? null;
7409
+ navDragRef.current = session;
7410
+ setIsItemDragging(true);
7411
+ lockItemDuringDrag();
7412
+ siblingHintElRef.current = null;
7413
+ setSiblingHintRect(null);
7414
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
7415
+ setToolbarRect(rect);
7416
+ }
7417
+ const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
7418
+ refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
7419
+ },
7420
+ [
7421
+ refreshNavDragVisuals,
7422
+ selectedElRef,
7423
+ setIsItemDragging,
7424
+ setSiblingHintRect,
7425
+ setToolbarRect,
7426
+ siblingHintElRef
7427
+ ]
7428
+ );
7429
+ beginNavDragRef.current = beginNavDrag;
7430
+ const commitNavDrag = (0, import_react8.useCallback)(
7431
+ (clientX, clientY) => {
7432
+ const session = navDragRef.current;
7433
+ if (!session) {
7434
+ clearNavDragVisuals();
7435
+ return;
7436
+ }
7437
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
7438
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
7439
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
7440
+ const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
7441
+ const wasSelected = session.wasSelected;
7442
+ const hrefKey = session.hrefKey;
7443
+ const applySelectionAfterDrop = () => {
7444
+ if (!wasSelected) {
7445
+ deselectRef.current();
7446
+ return;
7447
+ }
7448
+ const desktop = document.querySelector("[data-ohw-nav-container]");
7449
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
7450
+ 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)}"]`);
7451
+ if (link) {
7452
+ selectRef.current(link);
7453
+ return;
7454
+ }
7455
+ deselectRef.current();
7456
+ };
7457
+ if (planned) {
7458
+ editContentRef.current = {
7459
+ ...editContentRef.current,
7460
+ [NAV_ORDER_KEY]: planned.orderJson
7461
+ };
7462
+ applyNavForest(planned.forest);
7463
+ document.querySelectorAll(
7464
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
7465
+ ).forEach((el) => {
7466
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
7467
+ disableNativeHrefDrag(el);
7468
+ }
7469
+ });
7470
+ postToParentRef.current({
7471
+ type: "ow:change",
7472
+ nodes: [{ key: NAV_ORDER_KEY, text: planned.orderJson }]
7473
+ });
7474
+ applySelectionAfterDrop();
7475
+ clearNavDragVisuals();
7476
+ requestAnimationFrame(() => {
7477
+ if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
7478
+ applyNavForest(planned.forest);
7479
+ }
7480
+ applySelectionAfterDrop();
7481
+ requestAnimationFrame(applySelectionAfterDrop);
7482
+ });
7483
+ return;
7484
+ }
7485
+ applySelectionAfterDrop();
7486
+ clearNavDragVisuals();
7487
+ },
7488
+ [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
7489
+ );
7490
+ commitNavDragRef.current = commitNavDrag;
7491
+ const startNavLinkDrag = (0, import_react8.useCallback)(
7492
+ (anchor, clientX, clientY, wasSelected) => {
7493
+ if (footerDragRef.current) return false;
7494
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
7495
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7496
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return false;
7497
+ beginNavDrag({
7498
+ hrefKey,
7499
+ wasSelected,
7500
+ draggedEl: anchor,
7501
+ lastClientX: clientX,
7502
+ lastClientY: clientY,
7503
+ activeSlot: null
7504
+ });
7505
+ return true;
7506
+ },
7507
+ [beginNavDrag, footerDragRef, isDragHandleDisabled2, isNavbarButton3]
7508
+ );
7509
+ const onNavDragOver = (0, import_react8.useCallback)(
7510
+ (e) => {
7511
+ const session = navDragRef.current;
7512
+ if (!session) return false;
7513
+ e.preventDefault();
7514
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
7515
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7516
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7517
+ return true;
7518
+ },
7519
+ []
7520
+ );
7521
+ (0, import_react8.useEffect)(() => {
7522
+ if (!isEditMode) return;
7523
+ const THRESHOLD = 10;
7524
+ const resolveWasSelected = (el) => {
7525
+ if (selectedElRef.current === el) return true;
7526
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor2(activeElRef.current) : null;
7527
+ return activeAnchor === el;
7528
+ };
7529
+ const onPointerDown = (e) => {
7530
+ if (e.button !== 0) return;
7531
+ if (navDragRef.current || footerDragRef.current) return;
7532
+ const target = e.target;
7533
+ if (!target) return;
7534
+ if (target.closest(
7535
+ '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]'
7536
+ )) {
7537
+ return;
7538
+ }
7539
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
7540
+ const anchor = getNavigationItemAnchor2(target);
7541
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
7542
+ if (!anchor || !isNavbarHrefKey(hrefKey)) return;
7543
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return;
7544
+ armItemPressDrag();
7545
+ navPointerDragRef.current = {
7546
+ el: anchor,
7547
+ startX: e.clientX,
7548
+ startY: e.clientY,
7549
+ pointerId: e.pointerId,
7550
+ wasSelected: resolveWasSelected(anchor),
7551
+ started: false
7552
+ };
7553
+ };
7554
+ const onPointerMove = (e) => {
7555
+ const pending = navPointerDragRef.current;
7556
+ if (!pending) return;
7557
+ if (pending.started) {
7558
+ e.preventDefault();
7559
+ clearTextSelection();
7560
+ const session = navDragRef.current;
7561
+ if (!session) return;
7562
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7563
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7564
+ return;
7565
+ }
7566
+ const dx = e.clientX - pending.startX;
7567
+ const dy = e.clientY - pending.startY;
7568
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
7569
+ e.preventDefault();
7570
+ pending.started = true;
7571
+ clearTextSelection();
7572
+ try {
7573
+ document.body.setPointerCapture(pending.pointerId);
7574
+ } catch {
7575
+ }
7576
+ if (linkPopoverOpenRef.current) setLinkPopover(null);
7577
+ if (activeElRef.current) deactivateRef.current();
7578
+ const key = pending.el.getAttribute("data-ohw-href-key");
7579
+ if (!key) return;
7580
+ beginNavDragRef.current({
7581
+ hrefKey: key,
7582
+ wasSelected: pending.wasSelected,
7583
+ draggedEl: pending.el,
7584
+ lastClientX: e.clientX,
7585
+ lastClientY: e.clientY,
7586
+ activeSlot: null
7587
+ });
7588
+ };
7589
+ const endPointerDrag = (e) => {
7590
+ const pending = navPointerDragRef.current;
7591
+ navPointerDragRef.current = null;
7592
+ try {
7593
+ if (document.body.hasPointerCapture(e.pointerId)) {
7594
+ document.body.releasePointerCapture(e.pointerId);
7595
+ }
7596
+ } catch {
7597
+ }
7598
+ if (!pending?.started) {
7599
+ unlockItemDragInteraction();
7600
+ return;
7601
+ }
7602
+ suppressNextClickRef.current = true;
7603
+ suppressClickUntilRef.current = Date.now() + 500;
7604
+ commitNavDragRef.current(e.clientX, e.clientY);
7605
+ };
7606
+ const blockSelectStart = (e) => {
7607
+ if (navDragRef.current || navPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
7608
+ e.preventDefault();
7609
+ }
7610
+ };
7611
+ const onDragEnd = () => {
7612
+ if (!navDragRef.current) return;
7613
+ suppressNextClickRef.current = true;
7614
+ suppressClickUntilRef.current = Date.now() + 500;
7615
+ commitNavDragRef.current();
7616
+ };
7617
+ document.addEventListener("pointerdown", onPointerDown, true);
7618
+ document.addEventListener("pointermove", onPointerMove, true);
7619
+ document.addEventListener("pointerup", endPointerDrag, true);
7620
+ document.addEventListener("pointercancel", endPointerDrag, true);
7621
+ document.addEventListener("selectstart", blockSelectStart, true);
7622
+ document.addEventListener("dragend", onDragEnd, true);
7623
+ return () => {
7624
+ document.removeEventListener("pointerdown", onPointerDown, true);
7625
+ document.removeEventListener("pointermove", onPointerMove, true);
7626
+ document.removeEventListener("pointerup", endPointerDrag, true);
7627
+ document.removeEventListener("pointercancel", endPointerDrag, true);
7628
+ document.removeEventListener("selectstart", blockSelectStart, true);
7629
+ document.removeEventListener("dragend", onDragEnd, true);
7630
+ unlockItemDragInteraction();
7631
+ };
7632
+ }, [
7633
+ activeElRef,
7634
+ deactivateRef,
7635
+ footerDragRef,
7636
+ getNavigationItemAnchor2,
7637
+ isDragHandleDisabled2,
7638
+ isEditMode,
7639
+ isNavbarButton3,
7640
+ linkPopoverOpenRef,
7641
+ selectedElRef,
7642
+ setLinkPopover,
7643
+ suppressNextClickRef
7644
+ ]);
7645
+ const armNavPressFromChrome = (0, import_react8.useCallback)(
7646
+ (selected, clientX, clientY, pointerId) => {
7647
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
7648
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7649
+ if (isNavbarButton3(selected) || isDragHandleDisabled2(selected)) return false;
7650
+ armItemPressDrag();
7651
+ navPointerDragRef.current = {
7652
+ el: selected,
7653
+ startX: clientX,
7654
+ startY: clientY,
7655
+ pointerId,
7656
+ wasSelected: true,
7657
+ started: false
7658
+ };
7659
+ return true;
7660
+ },
7661
+ [isDragHandleDisabled2, isNavbarButton3]
7662
+ );
7663
+ return {
7664
+ navDragRef,
7665
+ navDropSlots,
7666
+ activeNavDropIndex,
7667
+ startNavLinkDrag,
7668
+ commitNavDrag,
7669
+ onNavDragOver,
7670
+ armNavPressFromChrome,
7671
+ clearNavDragVisuals,
7672
+ refreshNavDragVisualsRef
7673
+ };
7674
+ }
7675
+
7676
+ // src/ui/navbar-container-chrome.tsx
7677
+ var import_lucide_react10 = require("lucide-react");
7678
+ var import_jsx_runtime21 = require("react/jsx-runtime");
7679
+ function NavbarContainerChrome({
7680
+ rect,
7681
+ onAdd
7682
+ }) {
7683
+ const chromeGap = 6;
7684
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7685
+ "div",
7686
+ {
7687
+ "data-ohw-navbar-container-chrome": "",
7688
+ "data-ohw-bridge": "",
7689
+ className: "pointer-events-none fixed z-[2147483647]",
7690
+ style: {
7691
+ top: rect.top - chromeGap,
7692
+ left: rect.left - chromeGap,
7693
+ width: rect.width + chromeGap * 2,
7694
+ height: rect.height + chromeGap * 2
7695
+ },
7696
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7697
+ "button",
7698
+ {
7699
+ type: "button",
7700
+ "data-ohw-navbar-add-button": "",
7701
+ className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
7702
+ style: { top: "70%" },
7703
+ "aria-label": "Add item",
7704
+ onMouseDown: (e) => {
7705
+ e.preventDefault();
7706
+ e.stopPropagation();
7707
+ },
7708
+ onClick: (e) => {
7709
+ e.preventDefault();
7710
+ e.stopPropagation();
7711
+ onAdd();
7712
+ },
7713
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7714
+ }
7715
+ )
7716
+ }
7717
+ );
7718
+ }
7719
+
7720
+ // src/ui/drop-indicator.tsx
7721
+ var React10 = __toESM(require("react"), 1);
7722
+ var import_jsx_runtime22 = require("react/jsx-runtime");
7723
+ var dropIndicatorVariants = cva(
7724
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7725
+ {
7726
+ variants: {
7727
+ direction: {
7728
+ vertical: "h-6 w-[3px]",
7729
+ horizontal: "h-[3px] w-[200px]"
7730
+ },
7731
+ state: {
7732
+ default: "opacity-0",
7733
+ hover: "opacity-100",
7734
+ dragIdle: "opacity-40",
7735
+ dragActive: "opacity-100"
7736
+ }
7737
+ },
7738
+ defaultVariants: {
7739
+ direction: "vertical",
7740
+ state: "default"
7741
+ }
7742
+ }
7743
+ );
7744
+ var DropIndicator = React10.forwardRef(
7745
+ ({ className, direction, state, ...props }, ref) => {
7746
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7747
+ "div",
7748
+ {
7749
+ ref,
7750
+ "data-slot": "drop-indicator",
7751
+ "data-direction": direction ?? "vertical",
7752
+ "data-state": state ?? "default",
7753
+ "aria-hidden": "true",
7754
+ className: cn(dropIndicatorVariants({ direction, state }), className),
7755
+ ...props
7756
+ }
7757
+ );
7758
+ }
7759
+ );
7760
+ DropIndicator.displayName = "DropIndicator";
7761
+
7762
+ // src/ui/badge.tsx
7763
+ var import_jsx_runtime23 = require("react/jsx-runtime");
7764
+ var badgeVariants = cva(
7765
+ "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",
7766
+ {
7767
+ variants: {
7768
+ variant: {
7769
+ default: "border-transparent bg-primary text-primary-foreground",
7770
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
7771
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
7772
+ outline: "text-foreground"
7773
+ }
7774
+ },
7775
+ defaultVariants: {
7776
+ variant: "default"
7777
+ }
7778
+ }
7779
+ );
7780
+ function Badge({ className, variant, ...props }) {
7781
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
7782
+ }
7783
+
7784
+ // src/OhhwellsBridge.tsx
7785
+ var import_lucide_react11 = require("lucide-react");
7786
+ var import_jsx_runtime24 = require("react/jsx-runtime");
7787
+ var PRIMARY2 = "#0885FE";
7788
+ var IMAGE_FADE_MS = 300;
7789
+ function runOpacityFade(el, onDone) {
7790
+ const anim = el.animate([{ opacity: 0 }, { opacity: 1 }], {
7791
+ duration: IMAGE_FADE_MS,
7792
+ easing: "ease",
7793
+ fill: "forwards"
7794
+ });
7795
+ let finished = false;
7796
+ const finish = () => {
7797
+ if (finished) return;
7798
+ finished = true;
7799
+ anim.cancel();
7800
+ el.style.opacity = "";
7801
+ onDone();
7802
+ };
7803
+ anim.finished.then(finish).catch(finish);
7804
+ window.setTimeout(finish, IMAGE_FADE_MS + 100);
7805
+ }
7806
+ function fadeInImageElement(img, onReady) {
7807
+ onReady();
7808
+ img.style.opacity = "0";
7809
+ runOpacityFade(img, () => {
7810
+ img.style.opacity = "";
7811
+ });
7812
+ }
7813
+ function applyEditableImageSrc(img, url) {
7814
+ img.removeAttribute("srcset");
7815
+ img.removeAttribute("sizes");
7816
+ img.src = url;
7817
+ }
7818
+ function fadeInBgImage(el, url, onReady) {
7819
+ const prevPos = el.style.position;
7820
+ if (!prevPos || prevPos === "static") el.style.position = "relative";
7821
+ const layer = document.createElement("div");
7822
+ layer.setAttribute("data-ohw-bg-fade-layer", "");
7823
+ Object.assign(layer.style, {
7824
+ position: "absolute",
7825
+ inset: "0",
7826
+ zIndex: "0",
7827
+ backgroundImage: `url('${url}')`,
7828
+ backgroundSize: "cover",
7829
+ backgroundPosition: "center",
7830
+ backgroundRepeat: "no-repeat",
7831
+ opacity: "0",
7832
+ pointerEvents: "none"
7833
+ });
7834
+ el.prepend(layer);
7835
+ onReady();
7836
+ runOpacityFade(layer, () => {
7837
+ el.style.backgroundImage = `url('${url}')`;
7838
+ layer.remove();
7839
+ if (!prevPos || prevPos === "static") el.style.position = prevPos;
7840
+ });
7841
+ }
7842
+ function getSectionsTracker() {
7843
+ let el = document.querySelector("[data-ohw-sections-tracker]");
7844
+ if (!el) {
7845
+ el = document.createElement("div");
7846
+ el.setAttribute("data-ohw-sections-tracker", "");
7847
+ el.style.display = "none";
7848
+ document.body.appendChild(el);
7849
+ }
7850
+ return el;
7851
+ }
7852
+ function updateSectionScheduleId(insertAfter, scheduleId) {
7853
+ const tracker = getSectionsTracker();
7854
+ let sections = [];
7855
+ try {
7856
+ sections = JSON.parse(tracker.textContent || "[]");
7857
+ } catch {
7858
+ }
7859
+ const currentPath = window.location.pathname;
7860
+ const updated = sections.map((s) => {
7861
+ if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
7862
+ if (s.pagePath && s.pagePath !== currentPath) return s;
7863
+ return { ...s, scheduleId };
7864
+ });
7865
+ tracker.textContent = JSON.stringify(updated);
7866
+ return tracker.textContent ?? "[]";
7867
+ }
7868
+ function schedulingSectionId(insertAfter) {
7869
+ return `scheduling-${insertAfter}`;
7870
+ }
7871
+ var INSERT_BEFORE_MARKER = ":before:";
6711
7872
  function parseSchedulingInsertAfter(insertAfter) {
6712
7873
  const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
6713
7874
  if (idx < 0) return { anchor: insertAfter, insertBefore: null };
@@ -6801,7 +7962,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6801
7962
  const root = (0, import_client.createRoot)(container);
6802
7963
  (0, import_react_dom2.flushSync)(() => {
6803
7964
  root.render(
6804
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7965
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6805
7966
  SchedulingWidget,
6806
7967
  {
6807
7968
  notifyOnConnect,
@@ -6876,7 +8037,7 @@ function syncNavigationDragCursorAttrs() {
6876
8037
  el.setAttribute("data-ohw-can-drag", "");
6877
8038
  });
6878
8039
  }
6879
- function collectEditableNodes() {
8040
+ function collectEditableNodes(extraContent) {
6880
8041
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6881
8042
  if (el.dataset.ohwEditable === "image") {
6882
8043
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6904,6 +8065,14 @@ function collectEditableNodes() {
6904
8065
  if (!key) return;
6905
8066
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6906
8067
  });
8068
+ if (extraContent) {
8069
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
8070
+ const text = extraContent[key];
8071
+ if (typeof text === "string" && text.length > 0) {
8072
+ nodes.push({ key, type: "meta", text });
8073
+ }
8074
+ }
8075
+ }
6907
8076
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6908
8077
  const key = el.dataset.ohwKey ?? "";
6909
8078
  const video = getVideoEl(el);
@@ -7017,6 +8186,11 @@ function getNavigationItemAnchor(el) {
7017
8186
  function isNavigationItem(el) {
7018
8187
  return getNavigationItemAnchor(el) !== null;
7019
8188
  }
8189
+ function listNavigationItems() {
8190
+ return Array.from(
8191
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
8192
+ ).filter((el) => isNavigationItem(el));
8193
+ }
7020
8194
  function getNavigationItemReorderState(anchor) {
7021
8195
  if (isNavbarButton2(anchor)) return { key: null, disabled: false };
7022
8196
  return getReorderHandleStateFromAnchor(anchor);
@@ -7049,7 +8223,7 @@ function countFooterNavItems(el) {
7049
8223
  return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
7050
8224
  }
7051
8225
  function findFooterItemGroup(item) {
7052
- const explicit = item.closest("[data-ohw-footer-column]");
8226
+ const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
7053
8227
  if (explicit) return explicit;
7054
8228
  const footer = item.closest("footer");
7055
8229
  if (!footer) return null;
@@ -7066,17 +8240,17 @@ function isNavigationRoot(el) {
7066
8240
  function isInferredFooterGroup(el) {
7067
8241
  const footer = el.closest("footer");
7068
8242
  if (!footer || el === footer) return false;
7069
- if (el.hasAttribute("data-ohw-footer-column")) return true;
8243
+ if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
7070
8244
  return countFooterNavItems(el) >= 2;
7071
8245
  }
7072
8246
  function isNavigationContainer(el) {
7073
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
8247
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
7074
8248
  }
7075
8249
  function isNavbarLinksContainer(el) {
7076
8250
  return el.hasAttribute("data-ohw-nav-container");
7077
8251
  }
7078
8252
  function getFooterColumn(el) {
7079
- return el.closest("[data-ohw-footer-column]");
8253
+ return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
7080
8254
  }
7081
8255
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
7082
8256
  if (getNavigationItemAnchor(target)) return null;
@@ -7100,7 +8274,7 @@ function isPointOverNavigation(x, y) {
7100
8274
  function findHoveredNavOrFooterContainer(x, y) {
7101
8275
  const candidates = [
7102
8276
  ...document.querySelectorAll("[data-ohw-nav-container]"),
7103
- ...document.querySelectorAll("[data-ohw-footer-column]")
8277
+ ...listFooterColumns()
7104
8278
  ];
7105
8279
  for (const container of candidates) {
7106
8280
  const r2 = container.getBoundingClientRect();
@@ -7147,32 +8321,57 @@ function getNavigationSelectionParent(el) {
7147
8321
  if (footerGroup) return footerGroup;
7148
8322
  return getNavigationRoot(el);
7149
8323
  }
7150
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
8324
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
7151
8325
  return getNavigationRoot(el);
7152
8326
  }
7153
8327
  return null;
7154
8328
  }
8329
+ function collectNavigationItemSiblingHintRects(selected) {
8330
+ if (!isNavigationItem(selected)) return [];
8331
+ const footerColumn = getFooterColumn(selected);
8332
+ if (footerColumn) {
8333
+ return listFooterLinksInColumn(footerColumn).filter((link) => link !== selected).map((link) => link.getBoundingClientRect());
8334
+ }
8335
+ const navContainer = selected.closest("[data-ohw-nav-container], [data-ohw-nav-drawer]");
8336
+ if (navContainer) {
8337
+ return Array.from(navContainer.querySelectorAll("[data-ohw-href-key]")).filter((el) => isNavigationItem(el) && el !== selected).map((el) => el.getBoundingClientRect());
8338
+ }
8339
+ return listNavigationItems().filter((el) => el !== selected).map((el) => el.getBoundingClientRect());
8340
+ }
7155
8341
  function placeCaretAtPoint(el, x, y) {
7156
8342
  const selection = window.getSelection();
7157
8343
  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;
8344
+ const overlays = Array.from(
8345
+ document.querySelectorAll(
8346
+ "[data-ohw-item-drag-surface], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-ohw-item-toolbar-anchor]"
8347
+ )
8348
+ );
8349
+ const prevPointerEvents = overlays.map((node) => node.style.pointerEvents);
8350
+ for (const node of overlays) node.style.pointerEvents = "none";
8351
+ try {
8352
+ if (typeof document.caretRangeFromPoint === "function") {
8353
+ const range = document.caretRangeFromPoint(x, y);
8354
+ if (range && el.contains(range.startContainer)) {
8355
+ selection.removeAllRanges();
8356
+ selection.addRange(range);
8357
+ return;
8358
+ }
7164
8359
  }
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);
8360
+ const caretPositionFromPoint = document.caretPositionFromPoint;
8361
+ if (typeof caretPositionFromPoint === "function") {
8362
+ const position = caretPositionFromPoint(x, y);
8363
+ if (position && el.contains(position.offsetNode)) {
8364
+ const range = document.createRange();
8365
+ range.setStart(position.offsetNode, position.offset);
8366
+ range.collapse(true);
8367
+ selection.removeAllRanges();
8368
+ selection.addRange(range);
8369
+ }
7175
8370
  }
8371
+ } finally {
8372
+ overlays.forEach((node, i) => {
8373
+ node.style.pointerEvents = prevPointerEvents[i] ?? "";
8374
+ });
7176
8375
  }
7177
8376
  }
7178
8377
  function selectAllTextInEditable(el) {
@@ -7338,10 +8537,11 @@ function EditGlowChrome({
7338
8537
  rect,
7339
8538
  elRef,
7340
8539
  reorderHrefKey,
7341
- dragDisabled = false
8540
+ dragDisabled = false,
8541
+ hideHandle = false
7342
8542
  }) {
7343
8543
  const GAP = SELECTION_CHROME_GAP2;
7344
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8544
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
7345
8545
  "div",
7346
8546
  {
7347
8547
  ref: elRef,
@@ -7356,7 +8556,7 @@ function EditGlowChrome({
7356
8556
  zIndex: 2147483646
7357
8557
  },
7358
8558
  children: [
7359
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8559
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7360
8560
  "div",
7361
8561
  {
7362
8562
  style: {
@@ -7369,7 +8569,7 @@ function EditGlowChrome({
7369
8569
  }
7370
8570
  }
7371
8571
  ),
7372
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8572
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7373
8573
  "div",
7374
8574
  {
7375
8575
  "data-ohw-drag-handle-container": "",
@@ -7381,7 +8581,7 @@ function EditGlowChrome({
7381
8581
  transform: "translate(calc(-100% - 7px), -50%)",
7382
8582
  pointerEvents: dragDisabled ? "none" : "auto"
7383
8583
  },
7384
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8584
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7385
8585
  DragHandle,
7386
8586
  {
7387
8587
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7491,9 +8691,9 @@ function FloatingToolbar({
7491
8691
  showEditLink,
7492
8692
  onEditLink
7493
8693
  }) {
7494
- const localRef = import_react8.default.useRef(null);
7495
- const [measuredW, setMeasuredW] = import_react8.default.useState(330);
7496
- const setRefs = import_react8.default.useCallback(
8694
+ const localRef = import_react9.default.useRef(null);
8695
+ const [measuredW, setMeasuredW] = import_react9.default.useState(330);
8696
+ const setRefs = import_react9.default.useCallback(
7497
8697
  (node) => {
7498
8698
  localRef.current = node;
7499
8699
  if (typeof elRef === "function") elRef(node);
@@ -7505,7 +8705,7 @@ function FloatingToolbar({
7505
8705
  },
7506
8706
  [elRef]
7507
8707
  );
7508
- import_react8.default.useLayoutEffect(() => {
8708
+ import_react9.default.useLayoutEffect(() => {
7509
8709
  const node = localRef.current;
7510
8710
  if (!node) return;
7511
8711
  const update = () => {
@@ -7518,7 +8718,7 @@ function FloatingToolbar({
7518
8718
  return () => ro.disconnect();
7519
8719
  }, [showEditLink, activeCommands]);
7520
8720
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
7521
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8721
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7522
8722
  "div",
7523
8723
  {
7524
8724
  ref: setRefs,
@@ -7530,12 +8730,12 @@ function FloatingToolbar({
7530
8730
  zIndex: 2147483647,
7531
8731
  pointerEvents: "auto"
7532
8732
  },
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, {}),
8733
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CustomToolbar, { children: [
8734
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react9.default.Fragment, { children: [
8735
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CustomToolbarDivider, {}),
7536
8736
  btns.map((btn) => {
7537
8737
  const isActive = activeCommands.has(btn.cmd);
7538
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8738
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7539
8739
  CustomToolbarButton,
7540
8740
  {
7541
8741
  title: btn.title,
@@ -7544,7 +8744,7 @@ function FloatingToolbar({
7544
8744
  e.preventDefault();
7545
8745
  onCommand(btn.cmd);
7546
8746
  },
7547
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8747
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7548
8748
  "svg",
7549
8749
  {
7550
8750
  width: "16",
@@ -7565,7 +8765,7 @@ function FloatingToolbar({
7565
8765
  );
7566
8766
  })
7567
8767
  ] }, gi)),
7568
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8768
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7569
8769
  CustomToolbarButton,
7570
8770
  {
7571
8771
  type: "button",
@@ -7579,7 +8779,7 @@ function FloatingToolbar({
7579
8779
  e.preventDefault();
7580
8780
  e.stopPropagation();
7581
8781
  },
7582
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
8782
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7583
8783
  }
7584
8784
  ) : null
7585
8785
  ] })
@@ -7596,7 +8796,7 @@ function StateToggle({
7596
8796
  states,
7597
8797
  onStateChange
7598
8798
  }) {
7599
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8799
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7600
8800
  ToggleGroup,
7601
8801
  {
7602
8802
  "data-ohw-state-toggle": "",
@@ -7610,7 +8810,7 @@ function StateToggle({
7610
8810
  left: rect.right - 8,
7611
8811
  transform: "translateX(-100%)"
7612
8812
  },
7613
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8813
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7614
8814
  }
7615
8815
  );
7616
8816
  }
@@ -7637,8 +8837,8 @@ function OhhwellsBridge() {
7637
8837
  const router = (0, import_navigation2.useRouter)();
7638
8838
  const searchParams = (0, import_navigation2.useSearchParams)();
7639
8839
  const isEditMode = isEditSessionActive();
7640
- const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7641
- (0, import_react8.useEffect)(() => {
8840
+ const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
8841
+ (0, import_react9.useEffect)(() => {
7642
8842
  const figtreeFontId = "ohw-figtree-font";
7643
8843
  if (!document.getElementById(figtreeFontId)) {
7644
8844
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7666,92 +8866,136 @@ function OhhwellsBridge() {
7666
8866
  const subdomainFromQuery = searchParams.get("subdomain");
7667
8867
  const subdomain = resolveSubdomain(subdomainFromQuery);
7668
8868
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7669
- const postToParent2 = (0, import_react8.useCallback)((data) => {
8869
+ const postToParent2 = (0, import_react9.useCallback)((data) => {
7670
8870
  if (typeof window !== "undefined" && window.parent !== window) {
7671
8871
  window.parent.postMessage(data, "*");
7672
8872
  }
7673
8873
  }, []);
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) => {
8874
+ const [fetchState, setFetchState] = (0, import_react9.useState)("idle");
8875
+ const autoSaveTimers = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
8876
+ const activeElRef = (0, import_react9.useRef)(null);
8877
+ const selectedElRef = (0, import_react9.useRef)(null);
8878
+ const selectedHrefKeyRef = (0, import_react9.useRef)(null);
8879
+ const selectedFooterColAttrRef = (0, import_react9.useRef)(null);
8880
+ const originalContentRef = (0, import_react9.useRef)(null);
8881
+ const activeStateElRef = (0, import_react9.useRef)(null);
8882
+ const parentScrollRef = (0, import_react9.useRef)(null);
8883
+ const visibleViewportRef = (0, import_react9.useRef)(null);
8884
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react9.useState)(null);
8885
+ const attachVisibleViewport = (0, import_react9.useCallback)((node) => {
7684
8886
  visibleViewportRef.current = node;
7685
8887
  setDialogPortalContainer(node);
7686
8888
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7687
8889
  }, []);
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)(() => {
8890
+ const toolbarElRef = (0, import_react9.useRef)(null);
8891
+ const glowElRef = (0, import_react9.useRef)(null);
8892
+ const hoveredImageRef = (0, import_react9.useRef)(null);
8893
+ const hoveredImageHasTextOverlapRef = (0, import_react9.useRef)(false);
8894
+ const dragOverElRef = (0, import_react9.useRef)(null);
8895
+ const [mediaHover, setMediaHover] = (0, import_react9.useState)(null);
8896
+ const [uploadingRects, setUploadingRects] = (0, import_react9.useState)({});
8897
+ const hoveredGapRef = (0, import_react9.useRef)(null);
8898
+ const imageUnhoverTimerRef = (0, import_react9.useRef)(null);
8899
+ const imageShowTimerRef = (0, import_react9.useRef)(null);
8900
+ const editStylesRef = (0, import_react9.useRef)(null);
8901
+ const activateRef = (0, import_react9.useRef)(() => {
7700
8902
  });
7701
- const deactivateRef = (0, import_react8.useRef)(() => {
8903
+ const deactivateRef = (0, import_react9.useRef)(() => {
7702
8904
  });
7703
- const selectRef = (0, import_react8.useRef)(() => {
8905
+ const selectRef = (0, import_react9.useRef)(() => {
7704
8906
  });
7705
- const selectFrameRef = (0, import_react8.useRef)(() => {
8907
+ const selectFrameRef = (0, import_react9.useRef)(() => {
7706
8908
  });
7707
- const deselectRef = (0, import_react8.useRef)(() => {
8909
+ const deselectRef = (0, import_react9.useRef)(() => {
7708
8910
  });
7709
- const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
8911
+ const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
7710
8912
  });
7711
- const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
8913
+ const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
7712
8914
  });
7713
- const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
8915
+ const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
7714
8916
  });
7715
- const postToParentRef = (0, import_react8.useRef)(postToParent2);
8917
+ const postToParentRef = (0, import_react9.useRef)(postToParent2);
7716
8918
  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");
8919
+ const sectionsLoadedRef = (0, import_react9.useRef)(false);
8920
+ const pendingScheduleConfigRequests = (0, import_react9.useRef)([]);
8921
+ const [toolbarRect, setToolbarRect] = (0, import_react9.useState)(null);
8922
+ const [toolbarVariant, setToolbarVariant] = (0, import_react9.useState)("none");
8923
+ const toolbarVariantRef = (0, import_react9.useRef)("none");
7722
8924
  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);
8925
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react9.useState)(false);
8926
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react9.useState)(null);
8927
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react9.useState)(false);
8928
+ const [toggleState, setToggleState] = (0, import_react9.useState)(null);
8929
+ const [maxBadge, setMaxBadge] = (0, import_react9.useState)(null);
8930
+ const [activeCommands, setActiveCommands] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
8931
+ const [sectionGap, setSectionGap] = (0, import_react9.useState)(null);
8932
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react9.useState)(false);
8933
+ const hoveredNavContainerRef = (0, import_react9.useRef)(null);
8934
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react9.useState)(null);
8935
+ const hoveredItemElRef = (0, import_react9.useRef)(null);
8936
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react9.useState)(null);
8937
+ const siblingHintElRef = (0, import_react9.useRef)(null);
8938
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react9.useState)(null);
8939
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react9.useState)([]);
8940
+ const [isItemDragging, setIsItemDragging] = (0, import_react9.useState)(false);
8941
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react9.useState)(false);
8942
+ const footerDragRef = (0, import_react9.useRef)(null);
8943
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react9.useState)([]);
8944
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react9.useState)(null);
8945
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react9.useState)(null);
8946
+ const footerPointerDragRef = (0, import_react9.useRef)(null);
8947
+ const suppressNextClickRef = (0, import_react9.useRef)(false);
8948
+ const suppressClickUntilRef = (0, import_react9.useRef)(0);
8949
+ const [linkPopover, setLinkPopover] = (0, import_react9.useState)(null);
8950
+ const linkPopoverSessionRef = (0, import_react9.useRef)(null);
8951
+ const addNavAfterAnchorRef = (0, import_react9.useRef)(null);
8952
+ const editContentRef = (0, import_react9.useRef)({});
8953
+ const [sitePages, setSitePages] = (0, import_react9.useState)([]);
8954
+ const [sectionsByPath, setSectionsByPath] = (0, import_react9.useState)({});
8955
+ const sectionsPrefetchGenRef = (0, import_react9.useRef)(0);
8956
+ const setLinkPopoverRef = (0, import_react9.useRef)(setLinkPopover);
8957
+ const linkPopoverPanelRef = (0, import_react9.useRef)(null);
8958
+ const linkPopoverOpenRef = (0, import_react9.useRef)(false);
8959
+ const linkPopoverGraceUntilRef = (0, import_react9.useRef)(0);
7749
8960
  setLinkPopoverRef.current = setLinkPopover;
7750
8961
  linkPopoverSessionRef.current = linkPopover;
8962
+ const {
8963
+ navDragRef,
8964
+ navDropSlots,
8965
+ activeNavDropIndex,
8966
+ startNavLinkDrag,
8967
+ commitNavDrag,
8968
+ onNavDragOver,
8969
+ armNavPressFromChrome,
8970
+ refreshNavDragVisualsRef
8971
+ } = useNavItemDrag({
8972
+ isEditMode,
8973
+ editContentRef,
8974
+ selectedElRef,
8975
+ activeElRef,
8976
+ footerDragRef,
8977
+ suppressNextClickRef,
8978
+ suppressClickUntilRef,
8979
+ siblingHintElRef,
8980
+ postToParentRef,
8981
+ deselectRef,
8982
+ selectRef,
8983
+ deactivateRef,
8984
+ setLinkPopover: () => setLinkPopover(null),
8985
+ linkPopoverOpenRef,
8986
+ setIsItemDragging,
8987
+ setDraggedItemRect,
8988
+ setSiblingHintRect,
8989
+ setSiblingHintRects,
8990
+ setToolbarRect,
8991
+ getNavigationItemAnchor,
8992
+ isDragHandleDisabled,
8993
+ isNavbarButton: isNavbarButton2
8994
+ });
7751
8995
  const bumpLinkPopoverGrace = () => {
7752
8996
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7753
8997
  };
7754
- const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
8998
+ const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
7755
8999
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7756
9000
  const gen = ++sectionsPrefetchGenRef.current;
7757
9001
  const paths = pages.map((p) => p.path);
@@ -7770,10 +9014,24 @@ function OhhwellsBridge() {
7770
9014
  );
7771
9015
  });
7772
9016
  }, [isEditMode, pathname]);
7773
- const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
9017
+ const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
7774
9018
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7775
- (0, import_react8.useEffect)(() => {
7776
- if (!linkPopover) return;
9019
+ (0, import_react9.useEffect)(() => {
9020
+ if (!linkPopover) {
9021
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
9022
+ return;
9023
+ }
9024
+ document.documentElement.setAttribute("data-ohw-link-popover-open", "");
9025
+ hoveredItemElRef.current = null;
9026
+ setHoveredItemRect(null);
9027
+ hoveredNavContainerRef.current = null;
9028
+ setHoveredNavContainerRect(null);
9029
+ siblingHintElRef.current = null;
9030
+ setSiblingHintRect(null);
9031
+ setSiblingHintRects([]);
9032
+ if (selectedElRef.current || toolbarVariantRef.current !== "none") {
9033
+ deselectRef.current();
9034
+ }
7777
9035
  if (hoveredImageRef.current) {
7778
9036
  hoveredImageRef.current = null;
7779
9037
  hoveredImageHasTextOverlapRef.current = false;
@@ -7807,7 +9065,7 @@ function OhhwellsBridge() {
7807
9065
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7808
9066
  };
7809
9067
  }, [linkPopover, postToParent2]);
7810
- (0, import_react8.useEffect)(() => {
9068
+ (0, import_react9.useEffect)(() => {
7811
9069
  if (!isEditMode) return;
7812
9070
  const useFixtures = shouldUseDevFixtures();
7813
9071
  if (useFixtures) {
@@ -7831,14 +9089,14 @@ function OhhwellsBridge() {
7831
9089
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
7832
9090
  return () => window.removeEventListener("message", onSitePages);
7833
9091
  }, [isEditMode, postToParent2]);
7834
- (0, import_react8.useEffect)(() => {
9092
+ (0, import_react9.useEffect)(() => {
7835
9093
  if (!isEditMode || shouldUseDevFixtures()) return;
7836
9094
  void loadAllSectionsManifest().then((manifest) => {
7837
9095
  if (Object.keys(manifest).length === 0) return;
7838
9096
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7839
9097
  });
7840
9098
  }, [isEditMode]);
7841
- (0, import_react8.useEffect)(() => {
9099
+ (0, import_react9.useEffect)(() => {
7842
9100
  const update = () => {
7843
9101
  const el = activeElRef.current ?? selectedElRef.current;
7844
9102
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7862,10 +9120,10 @@ function OhhwellsBridge() {
7862
9120
  vvp.removeEventListener("resize", update);
7863
9121
  };
7864
9122
  }, []);
7865
- const refreshStateRules = (0, import_react8.useCallback)(() => {
9123
+ const refreshStateRules = (0, import_react9.useCallback)(() => {
7866
9124
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7867
9125
  }, []);
7868
- const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
9126
+ const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
7869
9127
  const tracker = getSectionsTracker();
7870
9128
  let entries = [];
7871
9129
  try {
@@ -7888,7 +9146,7 @@ function OhhwellsBridge() {
7888
9146
  }
7889
9147
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7890
9148
  }, [isEditMode]);
7891
- const deactivate = (0, import_react8.useCallback)(() => {
9149
+ const deactivate = (0, import_react9.useCallback)(() => {
7892
9150
  const el = activeElRef.current;
7893
9151
  if (!el) return;
7894
9152
  const key = el.dataset.ohwKey;
@@ -7920,19 +9178,23 @@ function OhhwellsBridge() {
7920
9178
  setToolbarShowEditLink(false);
7921
9179
  postToParent2({ type: "ow:exit-edit" });
7922
9180
  }, [postToParent2]);
7923
- const clearSelectedAttr = (0, import_react8.useCallback)(() => {
9181
+ const clearSelectedAttr = (0, import_react9.useCallback)(() => {
7924
9182
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
7925
9183
  el.removeAttribute("data-ohw-selected");
7926
9184
  });
7927
9185
  }, []);
7928
- const deselect = (0, import_react8.useCallback)(() => {
9186
+ const deselect = (0, import_react9.useCallback)(() => {
7929
9187
  clearSelectedAttr();
7930
9188
  selectedElRef.current = null;
9189
+ selectedHrefKeyRef.current = null;
9190
+ selectedFooterColAttrRef.current = null;
7931
9191
  setSelectedIsCta(false);
7932
9192
  setReorderHrefKey(null);
7933
9193
  setReorderDragDisabled(false);
9194
+ setIsFooterFrameSelection(false);
7934
9195
  siblingHintElRef.current = null;
7935
9196
  setSiblingHintRect(null);
9197
+ setSiblingHintRects([]);
7936
9198
  setIsItemDragging(false);
7937
9199
  hoveredNavContainerRef.current = null;
7938
9200
  setHoveredNavContainerRect(null);
@@ -7941,12 +9203,57 @@ function OhhwellsBridge() {
7941
9203
  setToolbarVariant("none");
7942
9204
  }
7943
9205
  }, [clearSelectedAttr]);
7944
- const markSelected = (0, import_react8.useCallback)((el) => {
9206
+ const markSelected = (0, import_react9.useCallback)((el) => {
7945
9207
  clearSelectedAttr();
7946
9208
  el.setAttribute("data-ohw-selected", "");
7947
9209
  }, [clearSelectedAttr]);
7948
- const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
9210
+ const resolveHrefKeyElement = (0, import_react9.useCallback)((hrefKey) => {
9211
+ if (isFooterHrefKey(hrefKey)) {
9212
+ return document.querySelector(
9213
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9214
+ );
9215
+ }
9216
+ if (isNavbarHrefKey(hrefKey)) {
9217
+ const desktop = document.querySelector("[data-ohw-nav-container]");
9218
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
9219
+ 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)}"]`);
9220
+ }
9221
+ return document.querySelector(
9222
+ `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9223
+ );
9224
+ }, []);
9225
+ const resyncSelectedNavigationItem = (0, import_react9.useCallback)(() => {
9226
+ const hrefKey = selectedHrefKeyRef.current;
9227
+ if (hrefKey) {
9228
+ const link = resolveHrefKeyElement(hrefKey);
9229
+ if (!link || !isNavigationItem(link)) return;
9230
+ selectedElRef.current = link;
9231
+ const { key, disabled } = getNavigationItemReorderState(link);
9232
+ setReorderHrefKey(key);
9233
+ setReorderDragDisabled(disabled);
9234
+ setIsFooterFrameSelection(false);
9235
+ setToolbarVariant("link-action");
9236
+ setToolbarRect(link.getBoundingClientRect());
9237
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(link));
9238
+ return;
9239
+ }
9240
+ const colAttr = selectedFooterColAttrRef.current;
9241
+ if (colAttr != null) {
9242
+ const column = document.querySelector(`[data-ohw-footer-col="${CSS.escape(colAttr)}"]`) ?? listFooterColumns()[Number(colAttr)] ?? null;
9243
+ if (!column || !isNavigationContainer(column)) return;
9244
+ selectedElRef.current = column;
9245
+ setIsFooterFrameSelection(true);
9246
+ setToolbarVariant("select-frame");
9247
+ setToolbarRect(column.getBoundingClientRect());
9248
+ setSiblingHintRects(
9249
+ listFooterColumns().filter((c) => c !== column).map((c) => c.getBoundingClientRect())
9250
+ );
9251
+ }
9252
+ }, [resolveHrefKeyElement]);
9253
+ const reselectNavigationItem = (0, import_react9.useCallback)((navAnchor) => {
7949
9254
  selectedElRef.current = navAnchor;
9255
+ selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9256
+ selectedFooterColAttrRef.current = null;
7950
9257
  markSelected(navAnchor);
7951
9258
  setSelectedIsCta(isNavbarButton2(navAnchor));
7952
9259
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
@@ -7954,10 +9261,11 @@ function OhhwellsBridge() {
7954
9261
  setReorderDragDisabled(disabled);
7955
9262
  setToolbarVariant("link-action");
7956
9263
  setToolbarRect(navAnchor.getBoundingClientRect());
9264
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(navAnchor));
7957
9265
  setToolbarShowEditLink(false);
7958
9266
  setActiveCommands(/* @__PURE__ */ new Set());
7959
9267
  }, [markSelected]);
7960
- const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
9268
+ const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
7961
9269
  const el = activeElRef.current;
7962
9270
  if (!el) return;
7963
9271
  const key = el.dataset.ohwKey;
@@ -7967,49 +9275,337 @@ function OhhwellsBridge() {
7967
9275
  clearTimeout(timer);
7968
9276
  autoSaveTimers.current.delete(key);
7969
9277
  }
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 });
9278
+ const html = sanitizeHtml(el.innerHTML);
9279
+ const original = originalContentRef.current ?? "";
9280
+ if (html !== sanitizeHtml(original)) {
9281
+ postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
9282
+ const h = document.documentElement.scrollHeight;
9283
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
9284
+ }
9285
+ }
9286
+ el.removeAttribute("contenteditable");
9287
+ el.removeAttribute("data-ohw-editing");
9288
+ activeElRef.current = null;
9289
+ setMaxBadge(null);
9290
+ setActiveCommands(/* @__PURE__ */ new Set());
9291
+ setToolbarShowEditLink(false);
9292
+ postToParent2({ type: "ow:exit-edit" });
9293
+ reselectNavigationItem(navAnchor);
9294
+ }, [postToParent2, reselectNavigationItem]);
9295
+ const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
9296
+ const items = listNavbarItems();
9297
+ addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
9298
+ deselectRef.current();
9299
+ deactivateRef.current();
9300
+ bumpLinkPopoverGrace();
9301
+ setLinkPopover({
9302
+ key: "__add-nav__",
9303
+ mode: "create",
9304
+ intent: "add-nav"
9305
+ });
9306
+ }, []);
9307
+ const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
9308
+ footerDragRef.current = null;
9309
+ setSiblingHintRects([]);
9310
+ setFooterDropSlots([]);
9311
+ setActiveFooterDropIndex(null);
9312
+ setDraggedItemRect(null);
9313
+ setIsItemDragging(false);
9314
+ unlockFooterDragInteraction();
9315
+ }, []);
9316
+ const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
9317
+ const dragged = session.draggedEl;
9318
+ setDraggedItemRect(dragged.getBoundingClientRect());
9319
+ if (typeof clientX === "number" && typeof clientY === "number") {
9320
+ session.lastClientX = clientX;
9321
+ session.lastClientY = clientY;
9322
+ }
9323
+ session.activeSlot = activeSlot;
9324
+ setSiblingHintRects([]);
9325
+ if (session.kind === "link") {
9326
+ const columns = listFooterColumns();
9327
+ const slots2 = [];
9328
+ columns.forEach((col, i) => {
9329
+ slots2.push(...buildLinkDropSlots(col, i));
9330
+ });
9331
+ setFooterDropSlots(slots2);
9332
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
9333
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
9334
+ return;
9335
+ }
9336
+ const slots = buildColumnDropSlots();
9337
+ setFooterDropSlots(slots);
9338
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
9339
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
9340
+ }, []);
9341
+ const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
9342
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
9343
+ const commitFooterDragRef = (0, import_react9.useRef)(() => {
9344
+ });
9345
+ const beginFooterDragRef = (0, import_react9.useRef)(() => {
9346
+ });
9347
+ const beginFooterDrag = (0, import_react9.useCallback)(
9348
+ (session) => {
9349
+ const rect = session.draggedEl.getBoundingClientRect();
9350
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
9351
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
9352
+ session.activeSlot = session.activeSlot ?? null;
9353
+ footerDragRef.current = session;
9354
+ setIsItemDragging(true);
9355
+ lockFooterDuringDrag();
9356
+ siblingHintElRef.current = null;
9357
+ setSiblingHintRect(null);
9358
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
9359
+ setToolbarRect(rect);
9360
+ }
9361
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
9362
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
9363
+ },
9364
+ [refreshFooterDragVisuals]
9365
+ );
9366
+ beginFooterDragRef.current = beginFooterDrag;
9367
+ const commitFooterDrag = (0, import_react9.useCallback)(
9368
+ (clientX, clientY) => {
9369
+ const session = footerDragRef.current;
9370
+ if (!session) {
9371
+ clearFooterDragVisuals();
9372
+ return;
9373
+ }
9374
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
9375
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
9376
+ let nextOrder = null;
9377
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
9378
+ if (session.kind === "link" && session.hrefKey && slot) {
9379
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
9380
+ } else if (session.kind === "column" && slot) {
9381
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
9382
+ }
9383
+ const wasSelected = session.wasSelected;
9384
+ const draggedEl = session.draggedEl;
9385
+ const hrefKey = session.hrefKey;
9386
+ let movedColumnIndex = null;
9387
+ if (session.kind === "column" && slot && nextOrder) {
9388
+ let adjusted = slot.insertIndex;
9389
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
9390
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
9391
+ }
9392
+ const applySelectionAfterDrop = () => {
9393
+ if (!wasSelected) {
9394
+ deselectRef.current();
9395
+ return;
9396
+ }
9397
+ if (hrefKey) {
9398
+ selectedHrefKeyRef.current = hrefKey;
9399
+ selectedFooterColAttrRef.current = null;
9400
+ const link = resolveHrefKeyElement(hrefKey);
9401
+ if (link && isNavigationItem(link)) {
9402
+ selectRef.current(link);
9403
+ return;
9404
+ }
9405
+ }
9406
+ if (movedColumnIndex !== null && nextOrder) {
9407
+ const colKeys = nextOrder[movedColumnIndex] ?? [];
9408
+ let column = null;
9409
+ for (const key of colKeys) {
9410
+ const link = document.querySelector(
9411
+ `footer [data-ohw-href-key="${CSS.escape(key)}"]`
9412
+ );
9413
+ if (link) {
9414
+ column = findFooterColumnForLink(link);
9415
+ if (column) break;
9416
+ }
9417
+ }
9418
+ if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
9419
+ if (column && isNavigationContainer(column)) {
9420
+ selectFrameRef.current(column);
9421
+ return;
9422
+ }
9423
+ }
9424
+ if (document.body.contains(draggedEl)) {
9425
+ if (isNavigationItem(draggedEl)) {
9426
+ selectRef.current(draggedEl);
9427
+ return;
9428
+ }
9429
+ if (isNavigationContainer(draggedEl)) {
9430
+ selectFrameRef.current(draggedEl);
9431
+ return;
9432
+ }
9433
+ }
9434
+ deselectRef.current();
9435
+ };
9436
+ if (nextOrder) {
9437
+ const orderJson = JSON.stringify(nextOrder);
9438
+ editContentRef.current = {
9439
+ ...editContentRef.current,
9440
+ [FOOTER_ORDER_KEY]: orderJson
9441
+ };
9442
+ applyFooterOrder(nextOrder);
9443
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9444
+ postToParentRef.current({
9445
+ type: "ow:change",
9446
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
9447
+ });
9448
+ applySelectionAfterDrop();
9449
+ clearFooterDragVisuals();
9450
+ requestAnimationFrame(() => {
9451
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9452
+ applyFooterOrder(nextOrder);
9453
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9454
+ }
9455
+ applySelectionAfterDrop();
9456
+ requestAnimationFrame(() => {
9457
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9458
+ applyFooterOrder(nextOrder);
9459
+ }
9460
+ resyncSelectedNavigationItem();
9461
+ });
9462
+ });
9463
+ return;
9464
+ }
9465
+ applySelectionAfterDrop();
9466
+ clearFooterDragVisuals();
9467
+ },
9468
+ [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
9469
+ );
9470
+ commitFooterDragRef.current = commitFooterDrag;
9471
+ const startFooterLinkDrag = (0, import_react9.useCallback)(
9472
+ (anchor, clientX, clientY, wasSelected) => {
9473
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
9474
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
9475
+ const column = findFooterColumnForLink(anchor);
9476
+ const columns = listFooterColumns();
9477
+ beginFooterDrag({
9478
+ kind: "link",
9479
+ hrefKey,
9480
+ columnEl: column,
9481
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
9482
+ wasSelected,
9483
+ draggedEl: anchor,
9484
+ lastClientX: clientX,
9485
+ lastClientY: clientY,
9486
+ activeSlot: null
9487
+ });
9488
+ return true;
9489
+ },
9490
+ [beginFooterDrag]
9491
+ );
9492
+ const startFooterColumnDrag = (0, import_react9.useCallback)(
9493
+ (columnEl, clientX, clientY, wasSelected) => {
9494
+ const columns = listFooterColumns();
9495
+ const idx = columns.indexOf(columnEl);
9496
+ if (idx < 0) return false;
9497
+ beginFooterDrag({
9498
+ kind: "column",
9499
+ hrefKey: null,
9500
+ columnEl,
9501
+ sourceColumnIndex: idx,
9502
+ wasSelected,
9503
+ draggedEl: columnEl,
9504
+ lastClientX: clientX,
9505
+ lastClientY: clientY,
9506
+ activeSlot: null
9507
+ });
9508
+ return true;
9509
+ },
9510
+ [beginFooterDrag]
9511
+ );
9512
+ const handleItemDragStart = (0, import_react9.useCallback)(
9513
+ (e) => {
9514
+ const selected = selectedElRef.current;
9515
+ if (!selected) {
9516
+ setIsItemDragging(true);
9517
+ return;
9518
+ }
9519
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
9520
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
9521
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
9522
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9523
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
9524
+ }
9525
+ if (startNavLinkDrag(selected, clientX, clientY, true)) return;
9526
+ siblingHintElRef.current = null;
9527
+ setSiblingHintRect(null);
9528
+ setIsItemDragging(true);
9529
+ },
9530
+ [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
9531
+ );
9532
+ const handleItemDragEnd = (0, import_react9.useCallback)(
9533
+ (e) => {
9534
+ if (footerDragRef.current) {
9535
+ const x = e?.clientX;
9536
+ const y = e?.clientY;
9537
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9538
+ commitFooterDrag(x, y);
9539
+ } else {
9540
+ commitFooterDrag();
9541
+ }
9542
+ return;
9543
+ }
9544
+ if (navDragRef.current) {
9545
+ const x = e?.clientX;
9546
+ const y = e?.clientY;
9547
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9548
+ commitNavDrag(x, y);
9549
+ } else {
9550
+ commitNavDrag();
9551
+ }
9552
+ return;
7976
9553
  }
9554
+ setIsItemDragging(false);
9555
+ },
9556
+ [commitFooterDrag, commitNavDrag, navDragRef]
9557
+ );
9558
+ const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
9559
+ if (e.button !== 0) return;
9560
+ const selected = selectedElRef.current;
9561
+ if (!selected) return;
9562
+ armFooterPressDrag();
9563
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
9564
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
9565
+ footerPointerDragRef.current = {
9566
+ el: selected,
9567
+ kind: "link",
9568
+ startX: e.clientX,
9569
+ startY: e.clientY,
9570
+ pointerId: e.pointerId,
9571
+ wasSelected: true,
9572
+ started: false
9573
+ };
9574
+ return;
7977
9575
  }
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);
9576
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9577
+ footerPointerDragRef.current = {
9578
+ el: selected,
9579
+ kind: "column",
9580
+ startX: e.clientX,
9581
+ startY: e.clientY,
9582
+ pointerId: e.pointerId,
9583
+ wasSelected: true,
9584
+ started: false
9585
+ };
9586
+ return;
9587
+ }
9588
+ if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
9589
+ }, [armNavPressFromChrome]);
9590
+ const handleItemChromeClick = (0, import_react9.useCallback)((clientX, clientY) => {
9591
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
9592
+ suppressNextClickRef.current = false;
9593
+ return;
9594
+ }
9595
+ const selected = selectedElRef.current;
9596
+ if (!selected || !isNavigationItem(selected)) return;
9597
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9598
+ if (!editable) return;
9599
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
8006
9600
  }, []);
8007
9601
  reselectNavigationItemRef.current = reselectNavigationItem;
8008
9602
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
8009
- const select = (0, import_react8.useCallback)((anchor) => {
9603
+ const select = (0, import_react9.useCallback)((anchor) => {
8010
9604
  if (!isNavigationItem(anchor)) return;
8011
9605
  if (activeElRef.current) deactivate();
8012
9606
  selectedElRef.current = anchor;
9607
+ selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
9608
+ selectedFooterColAttrRef.current = null;
8013
9609
  markSelected(anchor);
8014
9610
  setSelectedIsCta(isNavbarButton2(anchor));
8015
9611
  clearHrefKeyHover(anchor);
@@ -8019,19 +9615,24 @@ function OhhwellsBridge() {
8019
9615
  hoveredItemElRef.current = null;
8020
9616
  siblingHintElRef.current = null;
8021
9617
  setSiblingHintRect(null);
9618
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(anchor));
8022
9619
  setIsItemDragging(false);
8023
9620
  const { key, disabled } = getNavigationItemReorderState(anchor);
8024
9621
  setReorderHrefKey(key);
8025
9622
  setReorderDragDisabled(disabled);
9623
+ setIsFooterFrameSelection(false);
8026
9624
  setToolbarVariant("link-action");
8027
9625
  setToolbarRect(anchor.getBoundingClientRect());
8028
9626
  setToolbarShowEditLink(false);
8029
9627
  setActiveCommands(/* @__PURE__ */ new Set());
8030
9628
  }, [deactivate, markSelected]);
8031
- const selectFrame = (0, import_react8.useCallback)((el) => {
9629
+ const selectFrame = (0, import_react9.useCallback)((el) => {
8032
9630
  if (!isNavigationContainer(el)) return;
8033
9631
  if (activeElRef.current) deactivate();
9632
+ const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8034
9633
  selectedElRef.current = el;
9634
+ selectedHrefKeyRef.current = null;
9635
+ selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
8035
9636
  markSelected(el);
8036
9637
  setSelectedIsCta(false);
8037
9638
  clearHrefKeyHover(el);
@@ -8041,18 +9642,24 @@ function OhhwellsBridge() {
8041
9642
  hoveredItemElRef.current = null;
8042
9643
  siblingHintElRef.current = null;
8043
9644
  setSiblingHintRect(null);
9645
+ setSiblingHintRects(
9646
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
9647
+ );
8044
9648
  setIsItemDragging(false);
8045
9649
  setReorderHrefKey(null);
8046
9650
  setReorderDragDisabled(false);
9651
+ setIsFooterFrameSelection(isFooterColumn);
8047
9652
  setToolbarVariant("select-frame");
8048
9653
  setToolbarRect(el.getBoundingClientRect());
8049
9654
  setToolbarShowEditLink(false);
8050
9655
  setActiveCommands(/* @__PURE__ */ new Set());
8051
9656
  }, [deactivate, markSelected]);
8052
- const activate = (0, import_react8.useCallback)((el, options) => {
9657
+ const activate = (0, import_react9.useCallback)((el, options) => {
8053
9658
  if (activeElRef.current === el) return;
8054
9659
  clearSelectedAttr();
8055
9660
  selectedElRef.current = null;
9661
+ selectedHrefKeyRef.current = null;
9662
+ selectedFooterColAttrRef.current = null;
8056
9663
  setSelectedIsCta(false);
8057
9664
  deactivate();
8058
9665
  if (hoveredImageRef.current) {
@@ -8071,7 +9678,11 @@ function OhhwellsBridge() {
8071
9678
  originalContentRef.current = el.innerHTML;
8072
9679
  el.focus({ preventScroll: true });
8073
9680
  if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8074
- placeCaretAtPoint(el, options.caretX, options.caretY);
9681
+ const { caretX, caretY } = options;
9682
+ placeCaretAtPoint(el, caretX, caretY);
9683
+ requestAnimationFrame(() => {
9684
+ if (activeElRef.current === el) placeCaretAtPoint(el, caretX, caretY);
9685
+ });
8075
9686
  } else {
8076
9687
  const selection = window.getSelection();
8077
9688
  if (selection && selection.rangeCount === 0) {
@@ -8101,7 +9712,7 @@ function OhhwellsBridge() {
8101
9712
  selectRef.current = select;
8102
9713
  selectFrameRef.current = selectFrame;
8103
9714
  deselectRef.current = deselect;
8104
- (0, import_react8.useLayoutEffect)(() => {
9715
+ (0, import_react9.useLayoutEffect)(() => {
8105
9716
  if (!subdomain || isEditMode) {
8106
9717
  setFetchState("done");
8107
9718
  return;
@@ -8142,6 +9753,7 @@ function OhhwellsBridge() {
8142
9753
  applyLinkByKey(key, val);
8143
9754
  }
8144
9755
  reconcileNavbarItemsFromContent(content);
9756
+ reconcileFooterOrderFromContent(content);
8145
9757
  enforceLinkHrefs();
8146
9758
  initSectionsFromContent(content, true);
8147
9759
  sectionsLoadedRef.current = true;
@@ -8170,7 +9782,7 @@ function OhhwellsBridge() {
8170
9782
  cancelled = true;
8171
9783
  };
8172
9784
  }, [subdomain, isEditMode]);
8173
- (0, import_react8.useEffect)(() => {
9785
+ (0, import_react9.useEffect)(() => {
8174
9786
  if (!subdomain || isEditMode) return;
8175
9787
  let debounceTimer = null;
8176
9788
  let observer = null;
@@ -8202,6 +9814,7 @@ function OhhwellsBridge() {
8202
9814
  applyLinkByKey(key, val);
8203
9815
  }
8204
9816
  reconcileNavbarItemsFromContent(content);
9817
+ reconcileFooterOrderFromContent(content);
8205
9818
  } finally {
8206
9819
  observer?.observe(document.body, { childList: true, subtree: true });
8207
9820
  }
@@ -8219,16 +9832,16 @@ function OhhwellsBridge() {
8219
9832
  if (debounceTimer) clearTimeout(debounceTimer);
8220
9833
  };
8221
9834
  }, [subdomain, isEditMode, pathname]);
8222
- (0, import_react8.useLayoutEffect)(() => {
9835
+ (0, import_react9.useLayoutEffect)(() => {
8223
9836
  const el = document.getElementById("ohw-loader");
8224
9837
  if (!el) return;
8225
9838
  const visible = Boolean(subdomain) && fetchState !== "done";
8226
9839
  el.style.display = visible ? "flex" : "none";
8227
9840
  }, [subdomain, fetchState]);
8228
- (0, import_react8.useEffect)(() => {
9841
+ (0, import_react9.useEffect)(() => {
8229
9842
  postToParent2({ type: "ow:navigation", path: pathname });
8230
9843
  }, [pathname, postToParent2]);
8231
- (0, import_react8.useEffect)(() => {
9844
+ (0, import_react9.useEffect)(() => {
8232
9845
  if (!isEditMode) return;
8233
9846
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8234
9847
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -8236,7 +9849,7 @@ function OhhwellsBridge() {
8236
9849
  deselectRef.current();
8237
9850
  deactivateRef.current();
8238
9851
  }, [pathname, isEditMode]);
8239
- (0, import_react8.useEffect)(() => {
9852
+ (0, import_react9.useEffect)(() => {
8240
9853
  const contentForNav = () => {
8241
9854
  if (isEditMode) return editContentRef.current;
8242
9855
  if (!subdomain) return {};
@@ -8248,6 +9861,7 @@ function OhhwellsBridge() {
8248
9861
  const roots = observeRoots();
8249
9862
  if (roots.length === 0) {
8250
9863
  reconcileNavbarItemsFromContent(contentForNav());
9864
+ reconcileFooterOrderFromContent(contentForNav());
8251
9865
  if (isEditMode) syncNavigationDragCursorAttrs();
8252
9866
  return;
8253
9867
  }
@@ -8260,11 +9874,26 @@ function OhhwellsBridge() {
8260
9874
  }
8261
9875
  };
8262
9876
  const run = () => {
8263
- if (applying) return;
9877
+ if (footerDragRef.current || navDragRef.current || applying) return;
8264
9878
  applying = true;
8265
9879
  observer?.disconnect();
8266
9880
  try {
8267
- reconcileNavbarItemsFromContent(contentForNav());
9881
+ const content = contentForNav();
9882
+ reconcileNavbarItemsFromContent(content);
9883
+ reconcileFooterOrderFromContent(content);
9884
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
9885
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
9886
+ disableNativeHrefDrag(el);
9887
+ }
9888
+ });
9889
+ document.querySelectorAll(
9890
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
9891
+ ).forEach((el) => {
9892
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
9893
+ disableNativeHrefDrag(el);
9894
+ }
9895
+ });
9896
+ resyncSelectedNavigationItem();
8268
9897
  if (isEditMode) syncNavigationDragCursorAttrs();
8269
9898
  } finally {
8270
9899
  applying = false;
@@ -8284,8 +9913,8 @@ function OhhwellsBridge() {
8284
9913
  if (rafId != null) cancelAnimationFrame(rafId);
8285
9914
  observer?.disconnect();
8286
9915
  };
8287
- }, [isEditMode, pathname, subdomain, fetchState]);
8288
- (0, import_react8.useEffect)(() => {
9916
+ }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
9917
+ (0, import_react9.useEffect)(() => {
8289
9918
  if (!isEditMode) return;
8290
9919
  const measure = () => {
8291
9920
  const h = document.body.scrollHeight;
@@ -8309,7 +9938,7 @@ function OhhwellsBridge() {
8309
9938
  window.removeEventListener("resize", handleResize);
8310
9939
  };
8311
9940
  }, [pathname, isEditMode, postToParent2]);
8312
- (0, import_react8.useEffect)(() => {
9941
+ (0, import_react9.useEffect)(() => {
8313
9942
  if (!subdomainFromQuery || isEditMode) return;
8314
9943
  const handleClick = (e) => {
8315
9944
  const anchor = e.target.closest("a");
@@ -8325,7 +9954,7 @@ function OhhwellsBridge() {
8325
9954
  document.addEventListener("click", handleClick, true);
8326
9955
  return () => document.removeEventListener("click", handleClick, true);
8327
9956
  }, [subdomainFromQuery, isEditMode, router]);
8328
- (0, import_react8.useEffect)(() => {
9957
+ (0, import_react9.useEffect)(() => {
8329
9958
  if (!isEditMode) {
8330
9959
  editStylesRef.current?.base.remove();
8331
9960
  editStylesRef.current?.forceHover.remove();
@@ -8387,6 +10016,21 @@ function OhhwellsBridge() {
8387
10016
  outline: none !important;
8388
10017
  outline-offset: 0 !important;
8389
10018
  }
10019
+ /* Editor chrome owns selection \u2014 suppress native focus rings on nav/footer items. */
10020
+ nav [data-ohw-href-key]:focus,
10021
+ nav [data-ohw-href-key]:focus-visible,
10022
+ nav [data-ohw-href-key]:focus-within,
10023
+ footer [data-ohw-href-key]:focus,
10024
+ footer [data-ohw-href-key]:focus-visible,
10025
+ footer [data-ohw-href-key]:focus-within,
10026
+ nav [data-ohw-href-key] [data-ohw-editable]:focus,
10027
+ nav [data-ohw-href-key] [data-ohw-editable]:focus-visible,
10028
+ footer [data-ohw-href-key] [data-ohw-editable]:focus,
10029
+ footer [data-ohw-href-key] [data-ohw-editable]:focus-visible {
10030
+ outline: none !important;
10031
+ outline-offset: 0 !important;
10032
+ box-shadow: none !important;
10033
+ }
8390
10034
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
8391
10035
  [data-ohw-editing],
8392
10036
  [data-ohw-editing] *,
@@ -8428,12 +10072,39 @@ function OhhwellsBridge() {
8428
10072
  syncNavigationDragCursorAttrs();
8429
10073
  refreshStateRules();
8430
10074
  const handleClick = (e) => {
10075
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
10076
+ suppressNextClickRef.current = false;
10077
+ e.preventDefault();
10078
+ e.stopPropagation();
10079
+ return;
10080
+ }
8431
10081
  const target = e.target;
8432
10082
  if (target.closest("[data-ohw-toolbar]")) return;
8433
10083
  if (target.closest("[data-ohw-state-toggle]")) return;
8434
10084
  if (target.closest("[data-ohw-max-badge]")) return;
8435
10085
  if (isInsideLinkEditor(target)) return;
8436
10086
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
10087
+ if (target.closest("[data-ohw-item-drag-surface]")) {
10088
+ e.preventDefault();
10089
+ e.stopPropagation();
10090
+ const selected = selectedElRef.current;
10091
+ if (selected && isNavigationItem(selected)) {
10092
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
10093
+ if (editable2) {
10094
+ activateRef.current(editable2, {
10095
+ caretX: e.clientX,
10096
+ caretY: e.clientY
10097
+ });
10098
+ }
10099
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
10100
+ const link = listFooterLinksInColumn(selected).find((el) => {
10101
+ const r2 = el.getBoundingClientRect();
10102
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
10103
+ });
10104
+ if (link) selectRef.current(link);
10105
+ }
10106
+ return;
10107
+ }
8437
10108
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8438
10109
  if (navFromChrome) {
8439
10110
  e.preventDefault();
@@ -8512,10 +10183,26 @@ function OhhwellsBridge() {
8512
10183
  if (hrefAnchor) {
8513
10184
  e.preventDefault();
8514
10185
  e.stopPropagation();
8515
- if (selectedElRef.current === hrefAnchor) return;
10186
+ if (selectedElRef.current === hrefAnchor) {
10187
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
10188
+ if (textEditable) {
10189
+ activateRef.current(textEditable, {
10190
+ caretX: e.clientX,
10191
+ caretY: e.clientY
10192
+ });
10193
+ }
10194
+ return;
10195
+ }
8516
10196
  selectRef.current(hrefAnchor);
8517
10197
  return;
8518
10198
  }
10199
+ const footerColClick = target.closest("[data-ohw-footer-col]");
10200
+ if (footerColClick) {
10201
+ e.preventDefault();
10202
+ e.stopPropagation();
10203
+ selectFrameRef.current(footerColClick);
10204
+ return;
10205
+ }
8519
10206
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8520
10207
  if (navContainerToSelect) {
8521
10208
  e.preventDefault();
@@ -8589,16 +10276,23 @@ function OhhwellsBridge() {
8589
10276
  const handleMouseOver = (e) => {
8590
10277
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8591
10278
  const target = e.target;
10279
+ if (linkPopoverOpenRef.current) {
10280
+ hoveredItemElRef.current = null;
10281
+ setHoveredItemRect(null);
10282
+ hoveredNavContainerRef.current = null;
10283
+ setHoveredNavContainerRect(null);
10284
+ return;
10285
+ }
8592
10286
  if (toolbarVariantRef.current !== "select-frame") {
8593
- const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-column]");
8594
- if (frameContainer && !getNavigationItemAnchor(target)) {
8595
- hoveredNavContainerRef.current = frameContainer;
8596
- setHoveredNavContainerRect(frameContainer.getBoundingClientRect());
10287
+ const navContainer = target.closest("[data-ohw-nav-container]");
10288
+ if (navContainer && !getNavigationItemAnchor(target)) {
10289
+ hoveredNavContainerRef.current = navContainer;
10290
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8597
10291
  hoveredItemElRef.current = null;
8598
10292
  setHoveredItemRect(null);
8599
10293
  return;
8600
10294
  }
8601
- if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10295
+ if (!target.closest("[data-ohw-nav-container]")) {
8602
10296
  hoveredNavContainerRef.current = null;
8603
10297
  setHoveredNavContainerRect(null);
8604
10298
  }
@@ -8608,12 +10302,21 @@ function OhhwellsBridge() {
8608
10302
  hoveredNavContainerRef.current = null;
8609
10303
  setHoveredNavContainerRect(null);
8610
10304
  const selected2 = selectedElRef.current;
8611
- if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
10305
+ if (selected2 === navAnchor) return;
8612
10306
  clearHrefKeyHover(navAnchor);
8613
10307
  hoveredItemElRef.current = navAnchor;
8614
10308
  setHoveredItemRect(navAnchor.getBoundingClientRect());
8615
10309
  return;
8616
10310
  }
10311
+ const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10312
+ if (footerCol) {
10313
+ hoveredNavContainerRef.current = null;
10314
+ setHoveredNavContainerRect(null);
10315
+ if (selectedElRef.current === footerCol) return;
10316
+ hoveredItemElRef.current = footerCol;
10317
+ setHoveredItemRect(footerCol.getBoundingClientRect());
10318
+ return;
10319
+ }
8617
10320
  const editable = target.closest("[data-ohw-editable]");
8618
10321
  if (!editable) return;
8619
10322
  const selected = selectedElRef.current;
@@ -8631,14 +10334,25 @@ function OhhwellsBridge() {
8631
10334
  };
8632
10335
  const handleMouseOut = (e) => {
8633
10336
  const target = e.target;
8634
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10337
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column]")) {
8635
10338
  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]")) {
10339
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8637
10340
  return;
8638
10341
  }
8639
10342
  hoveredNavContainerRef.current = null;
8640
10343
  setHoveredNavContainerRect(null);
8641
10344
  }
10345
+ const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10346
+ if (footerCol && hoveredItemElRef.current === footerCol) {
10347
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10348
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
10349
+ if (related2?.closest("[data-ohw-footer-col], [data-ohw-footer-column]") === footerCol) {
10350
+ return;
10351
+ }
10352
+ hoveredItemElRef.current = null;
10353
+ setHoveredItemRect(null);
10354
+ return;
10355
+ }
8642
10356
  const navAnchor = getNavigationItemAnchor(target);
8643
10357
  if (navAnchor) {
8644
10358
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8772,14 +10486,12 @@ function OhhwellsBridge() {
8772
10486
  if (toolbarVariantRef.current === "select-frame") {
8773
10487
  hoveredNavContainerRef.current = null;
8774
10488
  setHoveredNavContainerRect(null);
8775
- return;
8776
10489
  }
8777
- const frameContainers = [
8778
- ...document.querySelectorAll("[data-ohw-nav-container]"),
8779
- ...document.querySelectorAll("[data-ohw-footer-column]")
8780
- ];
8781
- let overFrame = null;
8782
- for (const container of frameContainers) {
10490
+ const navContainers = Array.from(
10491
+ document.querySelectorAll("[data-ohw-nav-container]")
10492
+ );
10493
+ const footerColumns = listFooterColumns();
10494
+ for (const container of [...navContainers, ...footerColumns]) {
8783
10495
  const containerRect = container.getBoundingClientRect();
8784
10496
  const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8785
10497
  if (!overContainer) continue;
@@ -8794,25 +10506,55 @@ function OhhwellsBridge() {
8794
10506
  hoveredNavContainerRef.current = null;
8795
10507
  setHoveredNavContainerRect(null);
8796
10508
  const selected = selectedElRef.current;
8797
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
10509
+ if (selected !== navItemHit) {
8798
10510
  clearHrefKeyHover(navItemHit);
8799
10511
  hoveredItemElRef.current = navItemHit;
8800
10512
  setHoveredItemRect(navItemHit.getBoundingClientRect());
10513
+ } else if (hoveredItemElRef.current === navItemHit) {
10514
+ hoveredItemElRef.current = null;
10515
+ setHoveredItemRect(null);
8801
10516
  }
8802
10517
  return;
8803
10518
  }
8804
- overFrame = container;
8805
- break;
8806
10519
  }
8807
- if (!overFrame) {
10520
+ if (toolbarVariantRef.current !== "select-frame") {
10521
+ for (const container of navContainers) {
10522
+ const containerRect = container.getBoundingClientRect();
10523
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10524
+ if (!overContainer) continue;
10525
+ if (isPointOverNavItem(container, x, y)) continue;
10526
+ hoveredNavContainerRef.current = container;
10527
+ setHoveredNavContainerRect(container.getBoundingClientRect());
10528
+ hoveredItemElRef.current = null;
10529
+ setHoveredItemRect(null);
10530
+ return;
10531
+ }
10532
+ hoveredNavContainerRef.current = null;
10533
+ setHoveredNavContainerRect(null);
10534
+ } else {
8808
10535
  hoveredNavContainerRef.current = null;
8809
10536
  setHoveredNavContainerRect(null);
10537
+ }
10538
+ for (const column of footerColumns) {
10539
+ const containerRect = column.getBoundingClientRect();
10540
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10541
+ if (!overContainer) continue;
10542
+ if (isPointOverNavItem(column, x, y)) continue;
10543
+ if (selectedElRef.current === column) {
10544
+ if (hoveredItemElRef.current === column) {
10545
+ hoveredItemElRef.current = null;
10546
+ setHoveredItemRect(null);
10547
+ }
10548
+ return;
10549
+ }
10550
+ hoveredItemElRef.current = column;
10551
+ setHoveredItemRect(column.getBoundingClientRect());
8810
10552
  return;
8811
10553
  }
8812
- hoveredNavContainerRef.current = overFrame;
8813
- setHoveredNavContainerRect(overFrame.getBoundingClientRect());
8814
- hoveredItemElRef.current = null;
8815
- setHoveredItemRect(null);
10554
+ if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10555
+ hoveredItemElRef.current = null;
10556
+ setHoveredItemRect(null);
10557
+ }
8816
10558
  };
8817
10559
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
8818
10560
  if (linkPopoverOpenRef.current) {
@@ -8833,6 +10575,10 @@ function OhhwellsBridge() {
8833
10575
  }
8834
10576
  hoveredNavContainerRef.current = null;
8835
10577
  setHoveredNavContainerRect(null);
10578
+ if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10579
+ hoveredItemElRef.current = null;
10580
+ setHoveredItemRect(null);
10581
+ }
8836
10582
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
8837
10583
  if (toggleEl) {
8838
10584
  const tr = toggleEl.getBoundingClientRect();
@@ -9080,6 +10826,15 @@ function OhhwellsBridge() {
9080
10826
  probeHoverCardsAt(clientX, clientY);
9081
10827
  };
9082
10828
  const handleDragOver = (e) => {
10829
+ const footerSession = footerDragRef.current;
10830
+ if (footerSession) {
10831
+ e.preventDefault();
10832
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
10833
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10834
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
10835
+ return;
10836
+ }
10837
+ if (onNavDragOver(e)) return;
9083
10838
  e.preventDefault();
9084
10839
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9085
10840
  if (!el) {
@@ -9443,6 +11198,26 @@ function OhhwellsBridge() {
9443
11198
  if (siblingHintElRef.current) {
9444
11199
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9445
11200
  }
11201
+ const selected = selectedElRef.current;
11202
+ if (selected && !footerDragRef.current && !navDragRef.current) {
11203
+ if (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected))) {
11204
+ setSiblingHintRects(
11205
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
11206
+ );
11207
+ } else if (isNavigationItem(selected)) {
11208
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(selected));
11209
+ }
11210
+ }
11211
+ if (footerDragRef.current) {
11212
+ const session = footerDragRef.current;
11213
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
11214
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11215
+ }
11216
+ if (navDragRef.current) {
11217
+ const session = navDragRef.current;
11218
+ const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
11219
+ refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11220
+ }
9446
11221
  if (hoveredImageRef.current) {
9447
11222
  const el = hoveredImageRef.current;
9448
11223
  const r2 = el.getBoundingClientRect();
@@ -9453,7 +11228,7 @@ function OhhwellsBridge() {
9453
11228
  };
9454
11229
  const handleSave = (e) => {
9455
11230
  if (e.data?.type !== "ow:save") return;
9456
- const nodes = collectEditableNodes();
11231
+ const nodes = collectEditableNodes(editContentRef.current);
9457
11232
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9458
11233
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9459
11234
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9676,7 +11451,7 @@ function OhhwellsBridge() {
9676
11451
  }
9677
11452
  }
9678
11453
  const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
9679
- if (footerColumn?.hasAttribute("data-ohw-footer-column")) {
11454
+ if (footerColumn?.hasAttribute("data-ohw-footer-col") || footerColumn?.hasAttribute("data-ohw-footer-column")) {
9680
11455
  selectFrameRef.current(footerColumn);
9681
11456
  return;
9682
11457
  }
@@ -9751,7 +11526,161 @@ function OhhwellsBridge() {
9751
11526
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9752
11527
  };
9753
11528
  }, [isEditMode, refreshStateRules]);
9754
- (0, import_react8.useEffect)(() => {
11529
+ (0, import_react9.useEffect)(() => {
11530
+ if (!isEditMode) return;
11531
+ const THRESHOLD = 10;
11532
+ const resolveWasSelected = (el) => {
11533
+ if (selectedElRef.current === el) return true;
11534
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
11535
+ return activeAnchor === el;
11536
+ };
11537
+ const onPointerDown = (e) => {
11538
+ if (e.button !== 0) return;
11539
+ if (suppressNextClickRef.current && Date.now() >= suppressClickUntilRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
11540
+ suppressNextClickRef.current = false;
11541
+ }
11542
+ if (footerDragRef.current) return;
11543
+ const target = e.target;
11544
+ if (!target) return;
11545
+ 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]')) {
11546
+ return;
11547
+ }
11548
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
11549
+ const anchor = getNavigationItemAnchor(target);
11550
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
11551
+ if (anchor && isFooterHrefKey(hrefKey)) {
11552
+ armFooterPressDrag();
11553
+ footerPointerDragRef.current = {
11554
+ el: anchor,
11555
+ kind: "link",
11556
+ startX: e.clientX,
11557
+ startY: e.clientY,
11558
+ pointerId: e.pointerId,
11559
+ wasSelected: resolveWasSelected(anchor),
11560
+ started: false
11561
+ };
11562
+ return;
11563
+ }
11564
+ const col = target.closest("[data-ohw-footer-col]");
11565
+ if (col && !getNavigationItemAnchor(target)) {
11566
+ armFooterPressDrag();
11567
+ footerPointerDragRef.current = {
11568
+ el: col,
11569
+ kind: "column",
11570
+ startX: e.clientX,
11571
+ startY: e.clientY,
11572
+ pointerId: e.pointerId,
11573
+ wasSelected: resolveWasSelected(col),
11574
+ started: false
11575
+ };
11576
+ }
11577
+ };
11578
+ const onPointerMove = (e) => {
11579
+ const pending = footerPointerDragRef.current;
11580
+ if (!pending) return;
11581
+ if (pending.started) {
11582
+ e.preventDefault();
11583
+ clearTextSelection();
11584
+ const session = footerDragRef.current;
11585
+ if (!session) return;
11586
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
11587
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
11588
+ return;
11589
+ }
11590
+ const dx = e.clientX - pending.startX;
11591
+ const dy = e.clientY - pending.startY;
11592
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
11593
+ e.preventDefault();
11594
+ pending.started = true;
11595
+ clearTextSelection();
11596
+ try {
11597
+ document.body.setPointerCapture(pending.pointerId);
11598
+ } catch {
11599
+ }
11600
+ if (linkPopoverOpenRef.current) {
11601
+ setLinkPopoverRef.current(null);
11602
+ }
11603
+ if (activeElRef.current) {
11604
+ deactivateRef.current();
11605
+ }
11606
+ if (pending.kind === "link") {
11607
+ const key = pending.el.getAttribute("data-ohw-href-key");
11608
+ if (!key) return;
11609
+ const column = findFooterColumnForLink(pending.el);
11610
+ const columns2 = listFooterColumns();
11611
+ beginFooterDragRef.current({
11612
+ kind: "link",
11613
+ hrefKey: key,
11614
+ columnEl: column,
11615
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
11616
+ wasSelected: pending.wasSelected,
11617
+ draggedEl: pending.el,
11618
+ lastClientX: e.clientX,
11619
+ lastClientY: e.clientY,
11620
+ activeSlot: null
11621
+ });
11622
+ return;
11623
+ }
11624
+ const columns = listFooterColumns();
11625
+ const idx = columns.indexOf(pending.el);
11626
+ if (idx < 0) return;
11627
+ beginFooterDragRef.current({
11628
+ kind: "column",
11629
+ hrefKey: null,
11630
+ columnEl: pending.el,
11631
+ sourceColumnIndex: idx,
11632
+ wasSelected: pending.wasSelected,
11633
+ draggedEl: pending.el,
11634
+ lastClientX: e.clientX,
11635
+ lastClientY: e.clientY,
11636
+ activeSlot: null
11637
+ });
11638
+ };
11639
+ const endPointerDrag = (e) => {
11640
+ const pending = footerPointerDragRef.current;
11641
+ footerPointerDragRef.current = null;
11642
+ try {
11643
+ if (document.body.hasPointerCapture(e.pointerId)) {
11644
+ document.body.releasePointerCapture(e.pointerId);
11645
+ }
11646
+ } catch {
11647
+ }
11648
+ if (!pending?.started) {
11649
+ unlockFooterDragInteraction();
11650
+ return;
11651
+ }
11652
+ suppressNextClickRef.current = true;
11653
+ suppressClickUntilRef.current = Date.now() + 500;
11654
+ commitFooterDragRef.current(e.clientX, e.clientY);
11655
+ };
11656
+ const blockSelectStart = (e) => {
11657
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
11658
+ e.preventDefault();
11659
+ }
11660
+ };
11661
+ const onDragEnd = (_e) => {
11662
+ if (!footerDragRef.current) return;
11663
+ suppressNextClickRef.current = true;
11664
+ suppressClickUntilRef.current = Date.now() + 500;
11665
+ commitFooterDragRef.current();
11666
+ };
11667
+ document.addEventListener("pointerdown", onPointerDown, true);
11668
+ document.addEventListener("pointermove", onPointerMove, true);
11669
+ document.addEventListener("pointerup", endPointerDrag, true);
11670
+ document.addEventListener("pointercancel", endPointerDrag, true);
11671
+ document.addEventListener("selectstart", blockSelectStart, true);
11672
+ document.addEventListener("dragend", onDragEnd, true);
11673
+ return () => {
11674
+ document.removeEventListener("pointerdown", onPointerDown, true);
11675
+ document.removeEventListener("pointermove", onPointerMove, true);
11676
+ document.removeEventListener("pointerup", endPointerDrag, true);
11677
+ document.removeEventListener("pointercancel", endPointerDrag, true);
11678
+ document.removeEventListener("selectstart", blockSelectStart, true);
11679
+ document.removeEventListener("dragend", onDragEnd, true);
11680
+ unlockFooterDragInteraction();
11681
+ };
11682
+ }, [isEditMode]);
11683
+ (0, import_react9.useEffect)(() => {
9755
11684
  const handler = (e) => {
9756
11685
  if (e.data?.type !== "ow:request-schedule-config") return;
9757
11686
  const insertAfterVal = e.data.insertAfter;
@@ -9767,7 +11696,7 @@ function OhhwellsBridge() {
9767
11696
  window.addEventListener("message", handler);
9768
11697
  return () => window.removeEventListener("message", handler);
9769
11698
  }, [processConfigRequest]);
9770
- (0, import_react8.useEffect)(() => {
11699
+ (0, import_react9.useEffect)(() => {
9771
11700
  if (!isEditMode) return;
9772
11701
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9773
11702
  el.removeAttribute("data-ohw-active-state");
@@ -9788,7 +11717,13 @@ function OhhwellsBridge() {
9788
11717
  const next = { ...prev, [pathKey]: sections };
9789
11718
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9790
11719
  });
9791
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
11720
+ postToParent2({
11721
+ type: "ow:ready",
11722
+ version: "1",
11723
+ path: pathname,
11724
+ nodes: collectEditableNodes(editContentRef.current),
11725
+ sections
11726
+ });
9792
11727
  postToParent2({ type: "ow:request-site-pages" });
9793
11728
  }, 150);
9794
11729
  return () => {
@@ -9796,19 +11731,19 @@ function OhhwellsBridge() {
9796
11731
  clearTimeout(timer);
9797
11732
  };
9798
11733
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9799
- (0, import_react8.useEffect)(() => {
11734
+ (0, import_react9.useEffect)(() => {
9800
11735
  scrollToHashSectionWhenReady();
9801
11736
  const onHashChange = () => scrollToHashSectionWhenReady();
9802
11737
  window.addEventListener("hashchange", onHashChange);
9803
11738
  return () => window.removeEventListener("hashchange", onHashChange);
9804
11739
  }, [pathname]);
9805
- const handleCommand = (0, import_react8.useCallback)((cmd) => {
11740
+ const handleCommand = (0, import_react9.useCallback)((cmd) => {
9806
11741
  document.execCommand(cmd, false);
9807
11742
  activeElRef.current?.focus();
9808
11743
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
9809
11744
  refreshActiveCommandsRef.current();
9810
11745
  }, []);
9811
- const handleStateChange = (0, import_react8.useCallback)((state) => {
11746
+ const handleStateChange = (0, import_react9.useCallback)((state) => {
9812
11747
  if (!activeStateElRef.current) return;
9813
11748
  const el = activeStateElRef.current;
9814
11749
  if (state === "Default") {
@@ -9821,11 +11756,11 @@ function OhhwellsBridge() {
9821
11756
  }
9822
11757
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9823
11758
  }, [deactivate]);
9824
- const closeLinkPopover = (0, import_react8.useCallback)(() => {
11759
+ const closeLinkPopover = (0, import_react9.useCallback)(() => {
9825
11760
  addNavAfterAnchorRef.current = null;
9826
11761
  setLinkPopover(null);
9827
11762
  }, []);
9828
- const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
11763
+ const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
9829
11764
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9830
11765
  if (!hrefCtx) return;
9831
11766
  bumpLinkPopoverGrace();
@@ -9836,7 +11771,7 @@ function OhhwellsBridge() {
9836
11771
  });
9837
11772
  deactivate();
9838
11773
  }, [deactivate]);
9839
- const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
11774
+ const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
9840
11775
  const anchor = selectedElRef.current;
9841
11776
  if (!anchor) return;
9842
11777
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9849,7 +11784,7 @@ function OhhwellsBridge() {
9849
11784
  });
9850
11785
  deselect();
9851
11786
  }, [deselect]);
9852
- const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
11787
+ const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
9853
11788
  (target) => {
9854
11789
  const session = linkPopoverSessionRef.current;
9855
11790
  if (!session) return;
@@ -9908,13 +11843,13 @@ function OhhwellsBridge() {
9908
11843
  const showEditLink = toolbarShowEditLink;
9909
11844
  const currentSections = sectionsByPath[pathname] ?? [];
9910
11845
  linkPopoverOpenRef.current = linkPopover !== null;
9911
- const handleMediaReplace = (0, import_react8.useCallback)(
11846
+ const handleMediaReplace = (0, import_react9.useCallback)(
9912
11847
  (key) => {
9913
11848
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9914
11849
  },
9915
11850
  [postToParent2, mediaHover?.elementType]
9916
11851
  );
9917
- const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
11852
+ const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
9918
11853
  setUploadingRects((prev) => {
9919
11854
  if (!(key in prev)) return prev;
9920
11855
  const next = { ...prev };
@@ -9922,7 +11857,7 @@ function OhhwellsBridge() {
9922
11857
  return next;
9923
11858
  });
9924
11859
  }, []);
9925
- const handleVideoSettingsChange = (0, import_react8.useCallback)(
11860
+ const handleVideoSettingsChange = (0, import_react9.useCallback)(
9926
11861
  (key, settings) => {
9927
11862
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9928
11863
  const video = getVideoEl(el);
@@ -9945,9 +11880,9 @@ function OhhwellsBridge() {
9945
11880
  [postToParent2]
9946
11881
  );
9947
11882
  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)(
11883
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11884
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
11885
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9951
11886
  MediaOverlay,
9952
11887
  {
9953
11888
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -9958,7 +11893,7 @@ function OhhwellsBridge() {
9958
11893
  },
9959
11894
  `uploading-${key}`
9960
11895
  )),
9961
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11896
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9962
11897
  MediaOverlay,
9963
11898
  {
9964
11899
  hover: mediaHover,
@@ -9967,28 +11902,69 @@ function OhhwellsBridge() {
9967
11902
  onVideoSettingsChange: handleVideoSettingsChange
9968
11903
  }
9969
11904
  ),
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,
11905
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
11906
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
11907
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
11908
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11909
+ "div",
9974
11910
  {
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)(
11911
+ className: "pointer-events-none fixed z-2147483646",
11912
+ style: {
11913
+ left: slot.left,
11914
+ top: slot.top,
11915
+ width: slot.width,
11916
+ height: slot.height
11917
+ },
11918
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11919
+ DropIndicator,
11920
+ {
11921
+ direction: slot.direction,
11922
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
11923
+ className: "!h-full !w-full"
11924
+ }
11925
+ )
11926
+ },
11927
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
11928
+ )),
11929
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11930
+ "div",
11931
+ {
11932
+ className: "pointer-events-none fixed z-2147483646",
11933
+ style: {
11934
+ left: slot.left,
11935
+ top: slot.top,
11936
+ width: slot.width,
11937
+ height: slot.height
11938
+ },
11939
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11940
+ DropIndicator,
11941
+ {
11942
+ direction: slot.direction,
11943
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
11944
+ className: "!h-full !w-full"
11945
+ }
11946
+ )
11947
+ },
11948
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
11949
+ )),
11950
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
11951
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
11952
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
11953
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9981
11954
  ItemInteractionLayer,
9982
11955
  {
9983
- rect: toolbarRect,
11956
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
9984
11957
  elRef: glowElRef,
9985
11958
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9986
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
11959
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
9987
11960
  dragDisabled: reorderDragDisabled,
9988
11961
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9989
11962
  onDragHandleDragStart: handleItemDragStart,
9990
11963
  onDragHandleDragEnd: handleItemDragEnd,
9991
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11964
+ onItemPointerDown: handleItemChromePointerDown,
11965
+ onItemClick: handleItemChromeClick,
11966
+ itemDragSurface: !isFooterFrameSelection,
11967
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9992
11968
  ItemActionToolbar,
9993
11969
  {
9994
11970
  onEditLink: openLinkPopoverForSelected,
@@ -10001,17 +11977,18 @@ function OhhwellsBridge() {
10001
11977
  ) : void 0
10002
11978
  }
10003
11979
  ),
10004
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
10005
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11980
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11981
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10006
11982
  EditGlowChrome,
10007
11983
  {
10008
11984
  rect: toolbarRect,
10009
11985
  elRef: glowElRef,
10010
11986
  reorderHrefKey,
10011
- dragDisabled: reorderDragDisabled
11987
+ dragDisabled: reorderDragDisabled,
11988
+ hideHandle: isItemDragging
10012
11989
  }
10013
11990
  ),
10014
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11991
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10015
11992
  FloatingToolbar,
10016
11993
  {
10017
11994
  rect: toolbarRect,
@@ -10024,7 +12001,7 @@ function OhhwellsBridge() {
10024
12001
  }
10025
12002
  )
10026
12003
  ] }),
10027
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12004
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
10028
12005
  "div",
10029
12006
  {
10030
12007
  "data-ohw-max-badge": "",
@@ -10050,7 +12027,7 @@ function OhhwellsBridge() {
10050
12027
  ]
10051
12028
  }
10052
12029
  ),
10053
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12030
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10054
12031
  StateToggle,
10055
12032
  {
10056
12033
  rect: toggleState.rect,
@@ -10059,32 +12036,36 @@ function OhhwellsBridge() {
10059
12036
  onStateChange: handleStateChange
10060
12037
  }
10061
12038
  ),
10062
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12039
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
10063
12040
  "div",
10064
12041
  {
10065
12042
  "data-ohw-section-insert-line": "",
10066
12043
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
10067
12044
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
10068
12045
  children: [
10069
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10070
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12046
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
12047
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10071
12048
  Badge,
10072
12049
  {
10073
12050
  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
12051
  onClick: () => {
10075
12052
  window.parent.postMessage(
10076
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
12053
+ {
12054
+ type: "ow:add-section",
12055
+ insertAfter: sectionGap.insertAfter,
12056
+ insertBefore: sectionGap.insertBefore
12057
+ },
10077
12058
  "*"
10078
12059
  );
10079
12060
  },
10080
12061
  children: "Add Section"
10081
12062
  }
10082
12063
  ),
10083
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12064
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10084
12065
  ]
10085
12066
  }
10086
12067
  ),
10087
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12068
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10088
12069
  LinkPopover,
10089
12070
  {
10090
12071
  panelRef: linkPopoverPanelRef,
@@ -10105,48 +12086,6 @@ function OhhwellsBridge() {
10105
12086
  bridgeRoot
10106
12087
  ) : null;
10107
12088
  }
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
12089
  // Annotate the CommonJS export names for ESM import in node:
10151
12090
  0 && (module.exports = {
10152
12091
  CustomToolbar,