@ohhwells/bridge 0.1.38-next.63 → 0.1.38-next.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React9, { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState7 } from "react";
4
+ import React10, { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState7 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
@@ -4493,7 +4493,7 @@ function getChromeStyle(state) {
4493
4493
  case "dragging":
4494
4494
  return {
4495
4495
  border: `2px solid ${PRIMARY}`,
4496
- boxShadow: DRAG_SHADOW
4496
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4497
4497
  };
4498
4498
  default:
4499
4499
  return {};
@@ -4509,6 +4509,8 @@ function ItemInteractionLayer({
4509
4509
  dragHandleLabel = "Reorder item",
4510
4510
  onDragHandleDragStart,
4511
4511
  onDragHandleDragEnd,
4512
+ onItemPointerDown,
4513
+ onItemClick,
4512
4514
  chromeGap = 6,
4513
4515
  className
4514
4516
  }) {
@@ -4516,7 +4518,8 @@ function ItemInteractionLayer({
4516
4518
  const isActive = state === "active-top" || state === "active-bottom";
4517
4519
  const isDragging = state === "dragging";
4518
4520
  const showToolbar = isActive && toolbar;
4519
- const showDragHandle = isActive && showHandle && !isDragging;
4521
+ const showDragHandle = (isActive || isDragging) && showHandle;
4522
+ const itemDragEnabled = isActive && showHandle && !isDragging && !dragDisabled;
4520
4523
  return /* @__PURE__ */ jsxs4(
4521
4524
  "div",
4522
4525
  {
@@ -4541,11 +4544,28 @@ function ItemInteractionLayer({
4541
4544
  style: getChromeStyle(state)
4542
4545
  }
4543
4546
  ),
4547
+ itemDragEnabled && /* @__PURE__ */ jsx9(
4548
+ "div",
4549
+ {
4550
+ "data-ohw-item-drag-surface": "",
4551
+ className: "pointer-events-auto absolute inset-0 cursor-grab rounded-lg active:cursor-grabbing",
4552
+ onPointerDown: (e) => {
4553
+ if (e.button !== 0) return;
4554
+ onItemPointerDown?.(e);
4555
+ },
4556
+ onClick: (e) => {
4557
+ e.preventDefault();
4558
+ e.stopPropagation();
4559
+ onItemClick?.(e.clientX, e.clientY);
4560
+ }
4561
+ }
4562
+ ),
4544
4563
  showDragHandle && /* @__PURE__ */ jsx9(
4545
4564
  "div",
4546
4565
  {
4547
4566
  "data-ohw-drag-handle-container": "",
4548
- className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4567
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4568
+ style: isDragging ? { visibility: "hidden" } : void 0,
4549
4569
  children: /* @__PURE__ */ jsx9(
4550
4570
  DragHandle,
4551
4571
  {
@@ -6547,6 +6567,319 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6547
6567
  };
6548
6568
  }
6549
6569
 
6570
+ // src/lib/footer-items.ts
6571
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6572
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6573
+ function parseFooterHrefKey(key) {
6574
+ if (!key) return null;
6575
+ const match = key.match(FOOTER_HREF_RE);
6576
+ if (!match) return null;
6577
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6578
+ }
6579
+ function isFooterHrefKey(key) {
6580
+ return parseFooterHrefKey(key) !== null;
6581
+ }
6582
+ function getFooterRoot() {
6583
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6584
+ }
6585
+ function getFooterLinksContainer() {
6586
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6587
+ }
6588
+ function isFooterLinkAnchor(el) {
6589
+ if (!el.matches("[data-ohw-href-key]")) return false;
6590
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6591
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6592
+ }
6593
+ function listFooterColumns() {
6594
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6595
+ if (!root) return [];
6596
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6597
+ if (explicit.length > 0) return explicit;
6598
+ return Array.from(root.children).filter((el) => {
6599
+ if (!(el instanceof HTMLElement)) return false;
6600
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6601
+ isFooterLinkAnchor
6602
+ );
6603
+ });
6604
+ }
6605
+ function listFooterLinksInColumn(column) {
6606
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6607
+ isFooterLinkAnchor
6608
+ );
6609
+ }
6610
+ function findFooterColumnForLink(anchor) {
6611
+ const columns = listFooterColumns();
6612
+ return columns.find((col) => col.contains(anchor)) ?? null;
6613
+ }
6614
+ function getFooterOrderFromDom() {
6615
+ return listFooterColumns().map(
6616
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6617
+ );
6618
+ }
6619
+ function findFooterLinkByKey(hrefKey) {
6620
+ return document.querySelector(
6621
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6622
+ );
6623
+ }
6624
+ function syncFooterColumnIndices(columns) {
6625
+ columns.forEach((col, index) => {
6626
+ const next = String(index);
6627
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6628
+ col.setAttribute("data-ohw-footer-col", next);
6629
+ }
6630
+ });
6631
+ }
6632
+ function appendChildIfNeeded(parent, child) {
6633
+ if (parent.lastElementChild !== child) {
6634
+ parent.appendChild(child);
6635
+ }
6636
+ }
6637
+ function insertLinkBeforeNext(col, el, nextEl) {
6638
+ if (nextEl) {
6639
+ if (el.nextElementSibling !== nextEl) {
6640
+ col.insertBefore(el, nextEl);
6641
+ }
6642
+ return;
6643
+ }
6644
+ if (col.lastElementChild !== el) {
6645
+ col.appendChild(el);
6646
+ }
6647
+ }
6648
+ function applyFooterOrder(order) {
6649
+ const container = getFooterLinksContainer();
6650
+ if (!container) return;
6651
+ const columns = listFooterColumns();
6652
+ if (columns.length === 0) return;
6653
+ const currentOrder = getFooterOrderFromDom();
6654
+ if (ordersEqual(order, currentOrder)) {
6655
+ syncFooterColumnIndices(columns);
6656
+ return;
6657
+ }
6658
+ const colCount = Math.max(columns.length, order.length);
6659
+ const working = columns.slice(0, colCount);
6660
+ for (let i = 0; i < order.length && i < working.length; i++) {
6661
+ appendChildIfNeeded(container, working[i]);
6662
+ }
6663
+ for (let i = order.length; i < working.length; i++) {
6664
+ appendChildIfNeeded(container, working[i]);
6665
+ }
6666
+ const freshColumns = listFooterColumns();
6667
+ syncFooterColumnIndices(freshColumns);
6668
+ for (let c = 0; c < order.length; c++) {
6669
+ const col = freshColumns[c];
6670
+ if (!col) continue;
6671
+ const colOrder = order[c];
6672
+ for (let j = 0; j < colOrder.length; j++) {
6673
+ const hrefKey = colOrder[j];
6674
+ const el = findFooterLinkByKey(hrefKey);
6675
+ if (!el) continue;
6676
+ if (el.parentElement !== col) {
6677
+ col.appendChild(el);
6678
+ }
6679
+ const nextKey = colOrder[j + 1];
6680
+ const nextEl = nextKey ? findFooterLinkByKey(nextKey) : null;
6681
+ insertLinkBeforeNext(col, el, nextEl?.parentElement === col ? nextEl : null);
6682
+ }
6683
+ }
6684
+ }
6685
+ function ordersEqual(a, b) {
6686
+ if (a.length !== b.length) return false;
6687
+ for (let i = 0; i < a.length; i++) {
6688
+ const left = a[i];
6689
+ const right = b[i];
6690
+ if (left.length !== right.length) return false;
6691
+ for (let j = 0; j < left.length; j++) {
6692
+ if (left[j] !== right[j]) return false;
6693
+ }
6694
+ }
6695
+ return true;
6696
+ }
6697
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6698
+ const order = getFooterOrderFromDom();
6699
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6700
+ let fromCol = -1;
6701
+ let fromIdx = -1;
6702
+ for (let c = 0; c < order.length; c++) {
6703
+ const idx = order[c].indexOf(hrefKey);
6704
+ if (idx >= 0) {
6705
+ fromCol = c;
6706
+ fromIdx = idx;
6707
+ break;
6708
+ }
6709
+ }
6710
+ if (fromCol < 0) return null;
6711
+ const next = order.map((col) => [...col]);
6712
+ next[fromCol].splice(fromIdx, 1);
6713
+ let adjusted = insertIndex;
6714
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6715
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6716
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
6717
+ if (ordersEqual(next, order)) return null;
6718
+ return next;
6719
+ }
6720
+ function planFooterColumnMove(fromIndex, toIndex) {
6721
+ const order = getFooterOrderFromDom();
6722
+ const columns = listFooterColumns();
6723
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6724
+ return null;
6725
+ }
6726
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6727
+ const nextOrder = order.map((col) => [...col]);
6728
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
6729
+ if (!movedOrder) return null;
6730
+ let adjusted = toIndex;
6731
+ if (fromIndex < toIndex) adjusted -= 1;
6732
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6733
+ nextOrder.splice(adjusted, 0, movedOrder);
6734
+ if (ordersEqual(nextOrder, order)) return null;
6735
+ return nextOrder;
6736
+ }
6737
+ function parseFooterOrder(content) {
6738
+ const raw = content[FOOTER_ORDER_KEY];
6739
+ if (!raw) return null;
6740
+ try {
6741
+ const parsed = JSON.parse(raw);
6742
+ if (!Array.isArray(parsed)) return null;
6743
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
6744
+ } catch {
6745
+ return null;
6746
+ }
6747
+ }
6748
+ function reconcileFooterOrderFromContent(content) {
6749
+ const order = parseFooterOrder(content);
6750
+ if (!order?.length) return;
6751
+ const current = getFooterOrderFromDom();
6752
+ if (ordersEqual(order, current)) {
6753
+ syncFooterColumnIndices(listFooterColumns());
6754
+ return;
6755
+ }
6756
+ applyFooterOrder(order);
6757
+ }
6758
+ function buildLinkDropSlots(column, columnIndex) {
6759
+ const links = listFooterLinksInColumn(column);
6760
+ const colRect = column.getBoundingClientRect();
6761
+ const slots = [];
6762
+ const barThickness = 3;
6763
+ if (links.length === 0) {
6764
+ slots.push({
6765
+ insertIndex: 0,
6766
+ columnIndex,
6767
+ left: colRect.left,
6768
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
6769
+ width: colRect.width,
6770
+ height: barThickness,
6771
+ direction: "horizontal"
6772
+ });
6773
+ return slots;
6774
+ }
6775
+ for (let i = 0; i <= links.length; i++) {
6776
+ let top;
6777
+ if (i === 0) {
6778
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
6779
+ } else if (i === links.length) {
6780
+ const last = links[links.length - 1].getBoundingClientRect();
6781
+ top = last.bottom - barThickness / 2;
6782
+ } else {
6783
+ const prev = links[i - 1].getBoundingClientRect();
6784
+ const next = links[i].getBoundingClientRect();
6785
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
6786
+ }
6787
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
6788
+ slots.push({
6789
+ insertIndex: i,
6790
+ columnIndex,
6791
+ left: widthRef.left,
6792
+ top,
6793
+ width: Math.max(widthRef.width, colRect.width * 0.8),
6794
+ height: barThickness,
6795
+ direction: "horizontal"
6796
+ });
6797
+ }
6798
+ return slots;
6799
+ }
6800
+ function buildColumnDropSlots() {
6801
+ const columns = listFooterColumns();
6802
+ const slots = [];
6803
+ const barThickness = 3;
6804
+ if (columns.length === 0) return slots;
6805
+ for (let i = 0; i <= columns.length; i++) {
6806
+ let left;
6807
+ let height;
6808
+ let top;
6809
+ if (i === 0) {
6810
+ const first = columns[0].getBoundingClientRect();
6811
+ left = first.left - barThickness / 2;
6812
+ top = first.top;
6813
+ height = first.height;
6814
+ } else if (i === columns.length) {
6815
+ const last = columns[columns.length - 1].getBoundingClientRect();
6816
+ left = last.right - barThickness / 2;
6817
+ top = last.top;
6818
+ height = last.height;
6819
+ } else {
6820
+ const prev = columns[i - 1].getBoundingClientRect();
6821
+ const next = columns[i].getBoundingClientRect();
6822
+ left = (prev.right + next.left) / 2 - barThickness / 2;
6823
+ top = Math.min(prev.top, next.top);
6824
+ height = Math.max(prev.bottom, next.bottom) - top;
6825
+ }
6826
+ slots.push({
6827
+ insertIndex: i,
6828
+ columnIndex: i,
6829
+ left,
6830
+ top,
6831
+ width: barThickness,
6832
+ height: Math.max(height, 24),
6833
+ direction: "vertical"
6834
+ });
6835
+ }
6836
+ return slots;
6837
+ }
6838
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
6839
+ const columns = listFooterColumns();
6840
+ let best = null;
6841
+ for (let c = 0; c < columns.length; c++) {
6842
+ const col = columns[c];
6843
+ const colRect = col.getBoundingClientRect();
6844
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
6845
+ if (!inColX) continue;
6846
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
6847
+ const links = listFooterLinksInColumn(col);
6848
+ const fromIdx = links.findIndex(
6849
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
6850
+ );
6851
+ if (fromIdx < 0) return true;
6852
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
6853
+ return true;
6854
+ }
6855
+ return true;
6856
+ });
6857
+ for (const slot of slots) {
6858
+ const cy = slot.top + slot.height / 2;
6859
+ const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
6860
+ if (!best || dist < best.dist) best = { slot, dist };
6861
+ }
6862
+ }
6863
+ return best?.slot ?? null;
6864
+ }
6865
+ function findColumnIndexForKey(hrefKey) {
6866
+ const order = getFooterOrderFromDom();
6867
+ for (let c = 0; c < order.length; c++) {
6868
+ if (order[c].includes(hrefKey)) return c;
6869
+ }
6870
+ return -1;
6871
+ }
6872
+ function hitTestColumnDropSlot(clientX, _clientY) {
6873
+ const slots = buildColumnDropSlots();
6874
+ let best = null;
6875
+ for (const slot of slots) {
6876
+ const cx2 = slot.left + slot.width / 2;
6877
+ const dist = Math.abs(clientX - cx2);
6878
+ if (!best || dist < best.dist) best = { slot, dist };
6879
+ }
6880
+ return best?.slot ?? null;
6881
+ }
6882
+
6550
6883
  // src/lib/carousel.ts
6551
6884
  import { useEffect as useEffect7, useState as useState6 } from "react";
6552
6885
  var CAROUSEL_ATTR = "data-ohw-carousel";
@@ -6675,8 +7008,50 @@ function NavbarContainerChrome({
6675
7008
  );
6676
7009
  }
6677
7010
 
6678
- // src/ui/badge.tsx
7011
+ // src/ui/drop-indicator.tsx
7012
+ import * as React9 from "react";
6679
7013
  import { jsx as jsx23 } from "react/jsx-runtime";
7014
+ var dropIndicatorVariants = cva(
7015
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7016
+ {
7017
+ variants: {
7018
+ direction: {
7019
+ vertical: "h-6 w-[3px]",
7020
+ horizontal: "h-[3px] w-[200px]"
7021
+ },
7022
+ state: {
7023
+ default: "opacity-0",
7024
+ hover: "opacity-100",
7025
+ dragIdle: "opacity-40",
7026
+ dragActive: "opacity-100"
7027
+ }
7028
+ },
7029
+ defaultVariants: {
7030
+ direction: "vertical",
7031
+ state: "default"
7032
+ }
7033
+ }
7034
+ );
7035
+ var DropIndicator = React9.forwardRef(
7036
+ ({ className, direction, state, ...props }, ref) => {
7037
+ return /* @__PURE__ */ jsx23(
7038
+ "div",
7039
+ {
7040
+ ref,
7041
+ "data-slot": "drop-indicator",
7042
+ "data-direction": direction ?? "vertical",
7043
+ "data-state": state ?? "default",
7044
+ "aria-hidden": "true",
7045
+ className: cn(dropIndicatorVariants({ direction, state }), className),
7046
+ ...props
7047
+ }
7048
+ );
7049
+ }
7050
+ );
7051
+ DropIndicator.displayName = "DropIndicator";
7052
+
7053
+ // src/ui/badge.tsx
7054
+ import { jsx as jsx24 } from "react/jsx-runtime";
6680
7055
  var badgeVariants = cva(
6681
7056
  "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",
6682
7057
  {
@@ -6694,12 +7069,12 @@ var badgeVariants = cva(
6694
7069
  }
6695
7070
  );
6696
7071
  function Badge({ className, variant, ...props }) {
6697
- return /* @__PURE__ */ jsx23("div", { className: cn(badgeVariants({ variant }), className), ...props });
7072
+ return /* @__PURE__ */ jsx24("div", { className: cn(badgeVariants({ variant }), className), ...props });
6698
7073
  }
6699
7074
 
6700
7075
  // src/OhhwellsBridge.tsx
6701
7076
  import { Link as Link2 } from "lucide-react";
6702
- import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
7077
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs14 } from "react/jsx-runtime";
6703
7078
  var PRIMARY2 = "#0885FE";
6704
7079
  var IMAGE_FADE_MS = 300;
6705
7080
  function runOpacityFade(el, onDone) {
@@ -6878,7 +7253,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6878
7253
  const root = createRoot(container);
6879
7254
  flushSync(() => {
6880
7255
  root.render(
6881
- /* @__PURE__ */ jsx24(
7256
+ /* @__PURE__ */ jsx25(
6882
7257
  SchedulingWidget,
6883
7258
  {
6884
7259
  notifyOnConnect,
@@ -6940,7 +7315,7 @@ function isDragHandleDisabled(el) {
6940
7315
  return raw === "true" || raw === "";
6941
7316
  });
6942
7317
  }
6943
- function collectEditableNodes() {
7318
+ function collectEditableNodes(extraContent) {
6944
7319
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6945
7320
  if (el.dataset.ohwEditable === "image") {
6946
7321
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6968,6 +7343,14 @@ function collectEditableNodes() {
6968
7343
  if (!key) return;
6969
7344
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6970
7345
  });
7346
+ if (extraContent) {
7347
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
7348
+ const text = extraContent[key];
7349
+ if (typeof text === "string" && text.length > 0) {
7350
+ nodes.push({ key, type: "meta", text });
7351
+ }
7352
+ }
7353
+ }
6971
7354
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6972
7355
  const key = el.dataset.ohwKey ?? "";
6973
7356
  const video = getVideoEl(el);
@@ -7066,6 +7449,29 @@ function getHrefKeyFromElement(el) {
7066
7449
  if (!key) return null;
7067
7450
  return { anchor, key };
7068
7451
  }
7452
+ function disableNativeHrefDrag(el) {
7453
+ if (el.draggable) el.draggable = false;
7454
+ if (el.getAttribute("draggable") !== "false") {
7455
+ el.setAttribute("draggable", "false");
7456
+ }
7457
+ }
7458
+ function clearTextSelection() {
7459
+ const sel = window.getSelection();
7460
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7461
+ }
7462
+ function armFooterPressDrag() {
7463
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7464
+ }
7465
+ function lockFooterDuringDrag() {
7466
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7467
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7468
+ clearTextSelection();
7469
+ }
7470
+ function unlockFooterDragInteraction() {
7471
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7472
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7473
+ clearTextSelection();
7474
+ }
7069
7475
  function isNavbarButton2(el) {
7070
7476
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
7071
7477
  }
@@ -7379,7 +7785,7 @@ function EditGlowChrome({
7379
7785
  zIndex: 2147483646
7380
7786
  },
7381
7787
  children: [
7382
- /* @__PURE__ */ jsx24(
7788
+ /* @__PURE__ */ jsx25(
7383
7789
  "div",
7384
7790
  {
7385
7791
  style: {
@@ -7392,7 +7798,7 @@ function EditGlowChrome({
7392
7798
  }
7393
7799
  }
7394
7800
  ),
7395
- reorderHrefKey && /* @__PURE__ */ jsx24(
7801
+ reorderHrefKey && /* @__PURE__ */ jsx25(
7396
7802
  "div",
7397
7803
  {
7398
7804
  "data-ohw-drag-handle-container": "",
@@ -7404,7 +7810,7 @@ function EditGlowChrome({
7404
7810
  transform: "translate(calc(-100% - 7px), -50%)",
7405
7811
  pointerEvents: dragDisabled ? "none" : "auto"
7406
7812
  },
7407
- children: /* @__PURE__ */ jsx24(
7813
+ children: /* @__PURE__ */ jsx25(
7408
7814
  DragHandle,
7409
7815
  {
7410
7816
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7508,7 +7914,7 @@ function FloatingToolbar({
7508
7914
  onEditLink
7509
7915
  }) {
7510
7916
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7511
- return /* @__PURE__ */ jsx24(
7917
+ return /* @__PURE__ */ jsx25(
7512
7918
  "div",
7513
7919
  {
7514
7920
  ref: elRef,
@@ -7530,11 +7936,11 @@ function FloatingToolbar({
7530
7936
  pointerEvents: "auto"
7531
7937
  },
7532
7938
  children: /* @__PURE__ */ jsxs14(CustomToolbar, { children: [
7533
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs14(React9.Fragment, { children: [
7534
- gi > 0 && /* @__PURE__ */ jsx24(CustomToolbarDivider, {}),
7939
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs14(React10.Fragment, { children: [
7940
+ gi > 0 && /* @__PURE__ */ jsx25(CustomToolbarDivider, {}),
7535
7941
  btns.map((btn) => {
7536
7942
  const isActive = activeCommands.has(btn.cmd);
7537
- return /* @__PURE__ */ jsx24(
7943
+ return /* @__PURE__ */ jsx25(
7538
7944
  CustomToolbarButton,
7539
7945
  {
7540
7946
  title: btn.title,
@@ -7543,7 +7949,7 @@ function FloatingToolbar({
7543
7949
  e.preventDefault();
7544
7950
  onCommand(btn.cmd);
7545
7951
  },
7546
- children: /* @__PURE__ */ jsx24(
7952
+ children: /* @__PURE__ */ jsx25(
7547
7953
  "svg",
7548
7954
  {
7549
7955
  width: "16",
@@ -7564,7 +7970,7 @@ function FloatingToolbar({
7564
7970
  );
7565
7971
  })
7566
7972
  ] }, gi)),
7567
- showEditLink ? /* @__PURE__ */ jsx24(
7973
+ showEditLink ? /* @__PURE__ */ jsx25(
7568
7974
  CustomToolbarButton,
7569
7975
  {
7570
7976
  type: "button",
@@ -7578,7 +7984,7 @@ function FloatingToolbar({
7578
7984
  e.preventDefault();
7579
7985
  e.stopPropagation();
7580
7986
  },
7581
- children: /* @__PURE__ */ jsx24(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7987
+ children: /* @__PURE__ */ jsx25(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7582
7988
  }
7583
7989
  ) : null
7584
7990
  ] })
@@ -7595,7 +8001,7 @@ function StateToggle({
7595
8001
  states,
7596
8002
  onStateChange
7597
8003
  }) {
7598
- return /* @__PURE__ */ jsx24(
8004
+ return /* @__PURE__ */ jsx25(
7599
8005
  ToggleGroup,
7600
8006
  {
7601
8007
  "data-ohw-state-toggle": "",
@@ -7609,7 +8015,7 @@ function StateToggle({
7609
8015
  left: rect.right - 8,
7610
8016
  transform: "translateX(-100%)"
7611
8017
  },
7612
- children: states.map((state) => /* @__PURE__ */ jsx24(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8018
+ children: states.map((state) => /* @__PURE__ */ jsx25(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7613
8019
  }
7614
8020
  );
7615
8021
  }
@@ -7733,7 +8139,15 @@ function OhhwellsBridge() {
7733
8139
  const [hoveredItemRect, setHoveredItemRect] = useState7(null);
7734
8140
  const siblingHintElRef = useRef5(null);
7735
8141
  const [siblingHintRect, setSiblingHintRect] = useState7(null);
8142
+ const [siblingHintRects, setSiblingHintRects] = useState7([]);
7736
8143
  const [isItemDragging, setIsItemDragging] = useState7(false);
8144
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState7(false);
8145
+ const footerDragRef = useRef5(null);
8146
+ const [footerDropSlots, setFooterDropSlots] = useState7([]);
8147
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState7(null);
8148
+ const [draggedItemRect, setDraggedItemRect] = useState7(null);
8149
+ const footerPointerDragRef = useRef5(null);
8150
+ const suppressNextClickRef = useRef5(false);
7737
8151
  const [linkPopover, setLinkPopover] = useState7(null);
7738
8152
  const linkPopoverSessionRef = useRef5(null);
7739
8153
  const addNavAfterAnchorRef = useRef5(null);
@@ -7897,8 +8311,10 @@ function OhhwellsBridge() {
7897
8311
  selectedElRef.current = null;
7898
8312
  setReorderHrefKey(null);
7899
8313
  setReorderDragDisabled(false);
8314
+ setIsFooterFrameSelection(false);
7900
8315
  siblingHintElRef.current = null;
7901
8316
  setSiblingHintRect(null);
8317
+ setSiblingHintRects([]);
7902
8318
  setIsItemDragging(false);
7903
8319
  hoveredNavContainerRef.current = null;
7904
8320
  setHoveredNavContainerRect(null);
@@ -7955,90 +8371,345 @@ function OhhwellsBridge() {
7955
8371
  intent: "add-nav"
7956
8372
  });
7957
8373
  }, []);
7958
- const handleItemDragStart = useCallback4(() => {
7959
- siblingHintElRef.current = null;
7960
- setSiblingHintRect(null);
7961
- setIsItemDragging(true);
7962
- }, []);
7963
- const handleItemDragEnd = useCallback4(() => {
8374
+ const clearFooterDragVisuals = useCallback4(() => {
8375
+ footerDragRef.current = null;
8376
+ setSiblingHintRects([]);
8377
+ setFooterDropSlots([]);
8378
+ setActiveFooterDropIndex(null);
8379
+ setDraggedItemRect(null);
7964
8380
  setIsItemDragging(false);
8381
+ unlockFooterDragInteraction();
7965
8382
  }, []);
7966
- reselectNavigationItemRef.current = reselectNavigationItem;
7967
- commitNavigationTextEditRef.current = commitNavigationTextEdit;
7968
- const select = useCallback4((anchor) => {
7969
- if (!isNavigationItem(anchor)) return;
7970
- if (activeElRef.current) deactivate();
7971
- selectedElRef.current = anchor;
7972
- clearHrefKeyHover(anchor);
7973
- hoveredNavContainerRef.current = null;
7974
- setHoveredNavContainerRect(null);
7975
- setHoveredItemRect(null);
7976
- hoveredItemElRef.current = null;
7977
- siblingHintElRef.current = null;
7978
- setSiblingHintRect(null);
7979
- setIsItemDragging(false);
7980
- const { key, disabled } = getNavigationItemReorderState(anchor);
7981
- setReorderHrefKey(key);
7982
- setReorderDragDisabled(disabled);
7983
- setToolbarVariant("link-action");
7984
- setToolbarRect(anchor.getBoundingClientRect());
7985
- setToolbarShowEditLink(false);
7986
- setActiveCommands(/* @__PURE__ */ new Set());
7987
- }, [deactivate]);
7988
- const selectFrame = useCallback4((el) => {
7989
- if (!isNavigationContainer(el)) return;
7990
- if (activeElRef.current) deactivate();
7991
- selectedElRef.current = el;
7992
- clearHrefKeyHover(el);
7993
- hoveredNavContainerRef.current = null;
7994
- setHoveredNavContainerRect(null);
7995
- setHoveredItemRect(null);
7996
- hoveredItemElRef.current = null;
7997
- siblingHintElRef.current = null;
7998
- setSiblingHintRect(null);
7999
- setIsItemDragging(false);
8000
- setReorderHrefKey(null);
8001
- setReorderDragDisabled(false);
8002
- setToolbarVariant("select-frame");
8003
- setToolbarRect(el.getBoundingClientRect());
8004
- setToolbarShowEditLink(false);
8005
- setActiveCommands(/* @__PURE__ */ new Set());
8006
- }, [deactivate]);
8007
- const activate = useCallback4((el, options) => {
8008
- if (activeElRef.current === el) return;
8009
- selectedElRef.current = null;
8010
- deactivate();
8011
- if (hoveredImageRef.current) {
8012
- hoveredImageRef.current = null;
8013
- setMediaHover(null);
8383
+ const refreshFooterDragVisuals = useCallback4((session, activeSlot, clientX, clientY) => {
8384
+ const dragged = session.draggedEl;
8385
+ setDraggedItemRect(dragged.getBoundingClientRect());
8386
+ if (typeof clientX === "number" && typeof clientY === "number") {
8387
+ session.lastClientX = clientX;
8388
+ session.lastClientY = clientY;
8014
8389
  }
8015
- setToolbarVariant("rich-text");
8016
- siblingHintElRef.current = null;
8017
- setSiblingHintRect(null);
8018
- setIsItemDragging(false);
8019
- el.setAttribute("contenteditable", "true");
8020
- el.removeAttribute("data-ohw-hovered");
8021
- el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
8022
- activeElRef.current = el;
8023
- originalContentRef.current = el.innerHTML;
8024
- el.focus();
8025
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8026
- placeCaretAtPoint(el, options.caretX, options.caretY);
8390
+ session.activeSlot = activeSlot;
8391
+ if (session.kind === "link") {
8392
+ const columns2 = listFooterColumns();
8393
+ const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
8394
+ if (activeSlot) focusCols.add(activeSlot.columnIndex);
8395
+ const siblingRects = [];
8396
+ columns2.forEach((col, i) => {
8397
+ if (!focusCols.has(i)) return;
8398
+ for (const link of listFooterLinksInColumn(col)) {
8399
+ if (link === dragged) continue;
8400
+ siblingRects.push(link.getBoundingClientRect());
8401
+ }
8402
+ });
8403
+ setSiblingHintRects(siblingRects);
8404
+ const slots2 = [];
8405
+ columns2.forEach((col, i) => {
8406
+ slots2.push(...buildLinkDropSlots(col, i));
8407
+ });
8408
+ setFooterDropSlots(slots2);
8409
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
8410
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
8411
+ return;
8412
+ }
8413
+ const columns = listFooterColumns();
8414
+ setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
8415
+ const slots = buildColumnDropSlots();
8416
+ setFooterDropSlots(slots);
8417
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
8418
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
8419
+ }, []);
8420
+ const refreshFooterDragVisualsRef = useRef5(refreshFooterDragVisuals);
8421
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
8422
+ const commitFooterDragRef = useRef5(() => {
8423
+ });
8424
+ const beginFooterDragRef = useRef5(() => {
8425
+ });
8426
+ const beginFooterDrag = useCallback4(
8427
+ (session) => {
8428
+ const rect = session.draggedEl.getBoundingClientRect();
8429
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
8430
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
8431
+ session.activeSlot = session.activeSlot ?? null;
8432
+ footerDragRef.current = session;
8433
+ setIsItemDragging(true);
8434
+ lockFooterDuringDrag();
8435
+ siblingHintElRef.current = null;
8436
+ setSiblingHintRect(null);
8437
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
8438
+ setToolbarRect(rect);
8439
+ }
8440
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
8441
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
8442
+ },
8443
+ [refreshFooterDragVisuals]
8444
+ );
8445
+ beginFooterDragRef.current = beginFooterDrag;
8446
+ const commitFooterDrag = useCallback4(
8447
+ (clientX, clientY) => {
8448
+ const session = footerDragRef.current;
8449
+ if (!session) {
8450
+ clearFooterDragVisuals();
8451
+ return;
8452
+ }
8453
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
8454
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
8455
+ let nextOrder = null;
8456
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
8457
+ if (session.kind === "link" && session.hrefKey && slot) {
8458
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
8459
+ } else if (session.kind === "column" && slot) {
8460
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
8461
+ }
8462
+ const wasSelected = session.wasSelected;
8463
+ const draggedEl = session.draggedEl;
8464
+ const hrefKey = session.hrefKey;
8465
+ let movedColumnIndex = null;
8466
+ if (session.kind === "column" && slot && nextOrder) {
8467
+ let adjusted = slot.insertIndex;
8468
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
8469
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
8470
+ }
8471
+ clearFooterDragVisuals();
8472
+ if (nextOrder) {
8473
+ const orderJson = JSON.stringify(nextOrder);
8474
+ editContentRef.current = {
8475
+ ...editContentRef.current,
8476
+ [FOOTER_ORDER_KEY]: orderJson
8477
+ };
8478
+ applyFooterOrder(nextOrder);
8479
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8480
+ requestAnimationFrame(() => {
8481
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
8482
+ applyFooterOrder(nextOrder);
8483
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8484
+ }
8485
+ });
8486
+ postToParentRef.current({
8487
+ type: "ow:change",
8488
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
8489
+ });
8490
+ }
8491
+ const still = (hrefKey ? document.querySelector(`footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`) : null) ?? (movedColumnIndex !== null ? listFooterColumns()[movedColumnIndex] ?? null : null) ?? (document.body.contains(draggedEl) ? draggedEl : null);
8492
+ if (still && isNavigationItem(still)) {
8493
+ selectRef.current(still);
8494
+ } else if (still && isNavigationContainer(still)) {
8495
+ selectFrameRef.current(still);
8496
+ } else if (wasSelected) {
8497
+ deselectRef.current();
8498
+ }
8499
+ },
8500
+ [clearFooterDragVisuals]
8501
+ );
8502
+ commitFooterDragRef.current = commitFooterDrag;
8503
+ const startFooterLinkDrag = useCallback4(
8504
+ (anchor, clientX, clientY, wasSelected) => {
8505
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
8506
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
8507
+ const column = findFooterColumnForLink(anchor);
8508
+ const columns = listFooterColumns();
8509
+ beginFooterDrag({
8510
+ kind: "link",
8511
+ hrefKey,
8512
+ columnEl: column,
8513
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
8514
+ wasSelected,
8515
+ draggedEl: anchor,
8516
+ lastClientX: clientX,
8517
+ lastClientY: clientY,
8518
+ activeSlot: null
8519
+ });
8520
+ return true;
8521
+ },
8522
+ [beginFooterDrag]
8523
+ );
8524
+ const startFooterColumnDrag = useCallback4(
8525
+ (columnEl, clientX, clientY, wasSelected) => {
8526
+ const columns = listFooterColumns();
8527
+ const idx = columns.indexOf(columnEl);
8528
+ if (idx < 0) return false;
8529
+ beginFooterDrag({
8530
+ kind: "column",
8531
+ hrefKey: null,
8532
+ columnEl,
8533
+ sourceColumnIndex: idx,
8534
+ wasSelected,
8535
+ draggedEl: columnEl,
8536
+ lastClientX: clientX,
8537
+ lastClientY: clientY,
8538
+ activeSlot: null
8539
+ });
8540
+ return true;
8541
+ },
8542
+ [beginFooterDrag]
8543
+ );
8544
+ const handleItemDragStart = useCallback4(
8545
+ (e) => {
8546
+ const selected = selectedElRef.current;
8547
+ if (!selected) {
8548
+ setIsItemDragging(true);
8549
+ return;
8550
+ }
8551
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
8552
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
8553
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
8554
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8555
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
8556
+ }
8557
+ siblingHintElRef.current = null;
8558
+ setSiblingHintRect(null);
8559
+ setIsItemDragging(true);
8560
+ },
8561
+ [startFooterColumnDrag, startFooterLinkDrag]
8562
+ );
8563
+ const handleItemDragEnd = useCallback4(
8564
+ (e) => {
8565
+ if (footerDragRef.current) {
8566
+ const x = e?.clientX;
8567
+ const y = e?.clientY;
8568
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
8569
+ commitFooterDrag(x, y);
8570
+ } else {
8571
+ commitFooterDrag();
8572
+ }
8573
+ return;
8574
+ }
8575
+ setIsItemDragging(false);
8576
+ },
8577
+ [commitFooterDrag]
8578
+ );
8579
+ const handleItemChromePointerDown = useCallback4((e) => {
8580
+ if (e.button !== 0) return;
8581
+ const selected = selectedElRef.current;
8582
+ if (!selected) return;
8583
+ armFooterPressDrag();
8584
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
8585
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
8586
+ footerPointerDragRef.current = {
8587
+ el: selected,
8588
+ kind: "link",
8589
+ startX: e.clientX,
8590
+ startY: e.clientY,
8591
+ pointerId: e.pointerId,
8592
+ wasSelected: true,
8593
+ started: false
8594
+ };
8595
+ return;
8596
+ }
8597
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8598
+ footerPointerDragRef.current = {
8599
+ el: selected,
8600
+ kind: "column",
8601
+ startX: e.clientX,
8602
+ startY: e.clientY,
8603
+ pointerId: e.pointerId,
8604
+ wasSelected: true,
8605
+ started: false
8606
+ };
8607
+ }
8608
+ }, []);
8609
+ const handleItemChromeClick = useCallback4((clientX, clientY) => {
8610
+ if (suppressNextClickRef.current) {
8611
+ suppressNextClickRef.current = false;
8612
+ return;
8027
8613
  }
8028
- setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
8029
- const navAnchor = getNavigationItemAnchor(el);
8030
- if (navAnchor) {
8614
+ const selected = selectedElRef.current;
8615
+ if (!selected || !isNavigationItem(selected)) return;
8616
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
8617
+ if (!editable) return;
8618
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
8619
+ }, []);
8620
+ reselectNavigationItemRef.current = reselectNavigationItem;
8621
+ commitNavigationTextEditRef.current = commitNavigationTextEdit;
8622
+ const select = useCallback4(
8623
+ (anchor) => {
8624
+ if (!isNavigationItem(anchor)) return;
8625
+ if (activeElRef.current) deactivate();
8626
+ selectedElRef.current = anchor;
8627
+ clearHrefKeyHover(anchor);
8628
+ hoveredNavContainerRef.current = null;
8629
+ setHoveredNavContainerRect(null);
8630
+ setHoveredItemRect(null);
8631
+ hoveredItemElRef.current = null;
8632
+ siblingHintElRef.current = null;
8633
+ setSiblingHintRect(null);
8634
+ setSiblingHintRects([]);
8635
+ setIsItemDragging(false);
8636
+ const { key, disabled } = getNavigationItemReorderState(anchor);
8637
+ setReorderHrefKey(key);
8638
+ setReorderDragDisabled(disabled);
8639
+ setIsFooterFrameSelection(false);
8640
+ setToolbarVariant("link-action");
8641
+ setToolbarRect(anchor.getBoundingClientRect());
8642
+ setToolbarShowEditLink(false);
8643
+ setActiveCommands(/* @__PURE__ */ new Set());
8644
+ },
8645
+ [deactivate]
8646
+ );
8647
+ const selectFrame = useCallback4(
8648
+ (el) => {
8649
+ if (!isNavigationContainer(el)) return;
8650
+ if (activeElRef.current) deactivate();
8651
+ const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8652
+ selectedElRef.current = el;
8653
+ clearHrefKeyHover(el);
8654
+ hoveredNavContainerRef.current = null;
8655
+ setHoveredNavContainerRect(null);
8656
+ setHoveredItemRect(null);
8657
+ hoveredItemElRef.current = null;
8658
+ siblingHintElRef.current = null;
8659
+ setSiblingHintRect(null);
8660
+ setSiblingHintRects(
8661
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
8662
+ );
8663
+ setIsItemDragging(false);
8031
8664
  setReorderHrefKey(null);
8032
8665
  setReorderDragDisabled(false);
8033
- } else {
8034
- const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
8035
- setReorderHrefKey(reorderKey);
8036
- setReorderDragDisabled(reorderDisabled);
8037
- }
8038
- setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
8039
- postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
8040
- requestAnimationFrame(() => refreshActiveCommandsRef.current());
8041
- }, [deactivate, postToParent2]);
8666
+ setIsFooterFrameSelection(isFooterColumn);
8667
+ setToolbarVariant("select-frame");
8668
+ setToolbarRect(el.getBoundingClientRect());
8669
+ setToolbarShowEditLink(false);
8670
+ setActiveCommands(/* @__PURE__ */ new Set());
8671
+ },
8672
+ [deactivate]
8673
+ );
8674
+ const activate = useCallback4(
8675
+ (el, options) => {
8676
+ if (activeElRef.current === el) return;
8677
+ selectedElRef.current = null;
8678
+ deactivate();
8679
+ if (hoveredImageRef.current) {
8680
+ hoveredImageRef.current = null;
8681
+ postToParentRef.current({ type: "ow:image-unhover" });
8682
+ }
8683
+ setToolbarVariant("rich-text");
8684
+ siblingHintElRef.current = null;
8685
+ setSiblingHintRect(null);
8686
+ setSiblingHintRects([]);
8687
+ setIsItemDragging(false);
8688
+ el.setAttribute("contenteditable", "true");
8689
+ el.removeAttribute("data-ohw-hovered");
8690
+ el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
8691
+ activeElRef.current = el;
8692
+ originalContentRef.current = el.innerHTML;
8693
+ el.focus();
8694
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8695
+ placeCaretAtPoint(el, options.caretX, options.caretY);
8696
+ }
8697
+ setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
8698
+ const navAnchor = getNavigationItemAnchor(el);
8699
+ if (navAnchor) {
8700
+ setReorderHrefKey(null);
8701
+ setReorderDragDisabled(false);
8702
+ } else {
8703
+ const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
8704
+ setReorderHrefKey(reorderKey);
8705
+ setReorderDragDisabled(reorderDisabled);
8706
+ }
8707
+ setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
8708
+ postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
8709
+ requestAnimationFrame(() => refreshActiveCommandsRef.current());
8710
+ },
8711
+ [deactivate, postToParent2]
8712
+ );
8042
8713
  activateRef.current = activate;
8043
8714
  deactivateRef.current = deactivate;
8044
8715
  selectRef.current = select;
@@ -8086,6 +8757,7 @@ function OhhwellsBridge() {
8086
8757
  applyLinkByKey(key, val);
8087
8758
  }
8088
8759
  reconcileNavbarItemsFromContent(content);
8760
+ reconcileFooterOrderFromContent(content);
8089
8761
  enforceLinkHrefs();
8090
8762
  initSectionsFromContent(content, true);
8091
8763
  sectionsLoadedRef.current = true;
@@ -8147,6 +8819,7 @@ function OhhwellsBridge() {
8147
8819
  applyLinkByKey(key, val);
8148
8820
  }
8149
8821
  reconcileNavbarItemsFromContent(content);
8822
+ reconcileFooterOrderFromContent(content);
8150
8823
  } finally {
8151
8824
  observer?.observe(document.body, { childList: true, subtree: true });
8152
8825
  }
@@ -8187,15 +8860,42 @@ function OhhwellsBridge() {
8187
8860
  if (!subdomain) return {};
8188
8861
  return contentCache.get(subdomain) ?? {};
8189
8862
  };
8190
- const run = () => reconcileNavbarItemsFromContent(contentForNav());
8863
+ let applying = false;
8864
+ let rafId = null;
8865
+ const run = () => {
8866
+ if (footerDragRef.current || applying) return;
8867
+ applying = true;
8868
+ try {
8869
+ const content = contentForNav();
8870
+ reconcileNavbarItemsFromContent(content);
8871
+ reconcileFooterOrderFromContent(content);
8872
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
8873
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
8874
+ disableNativeHrefDrag(el);
8875
+ }
8876
+ });
8877
+ } finally {
8878
+ applying = false;
8879
+ }
8880
+ };
8881
+ const scheduleRun = () => {
8882
+ if (rafId !== null) cancelAnimationFrame(rafId);
8883
+ rafId = requestAnimationFrame(() => {
8884
+ rafId = null;
8885
+ run();
8886
+ });
8887
+ };
8191
8888
  run();
8192
8889
  const nav = document.querySelector("nav");
8193
- if (!nav) return;
8194
- const observer = new MutationObserver(() => {
8195
- requestAnimationFrame(run);
8196
- });
8197
- observer.observe(nav, { childList: true, subtree: true });
8198
- return () => observer.disconnect();
8890
+ const footer = document.querySelector("footer");
8891
+ const observer = new MutationObserver(scheduleRun);
8892
+ if (nav) observer.observe(nav, { childList: true, subtree: true });
8893
+ if (footer) observer.observe(footer, { childList: true, subtree: true });
8894
+ if (!nav && !footer) return;
8895
+ return () => {
8896
+ observer.disconnect();
8897
+ if (rafId !== null) cancelAnimationFrame(rafId);
8898
+ };
8199
8899
  }, [isEditMode, pathname, subdomain, fetchState]);
8200
8900
  useEffect8(() => {
8201
8901
  if (!isEditMode) return;
@@ -8260,6 +8960,29 @@ function OhhwellsBridge() {
8260
8960
  [data-ohw-editable] {
8261
8961
  display: block;
8262
8962
  }
8963
+ /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
8964
+ footer [data-ohw-href-key] {
8965
+ -webkit-user-drag: none !important;
8966
+ }
8967
+ footer [data-ohw-footer-col]:hover {
8968
+ outline: 1.5px dashed ${PRIMARY2} !important;
8969
+ outline-offset: 6px;
8970
+ border-radius: 8px;
8971
+ }
8972
+ html[data-ohw-footer-press-drag] footer,
8973
+ html[data-ohw-footer-press-drag] footer * {
8974
+ user-select: none !important;
8975
+ -webkit-user-select: none !important;
8976
+ }
8977
+ html[data-ohw-item-dragging] footer,
8978
+ html[data-ohw-item-dragging] footer * {
8979
+ user-select: none !important;
8980
+ -webkit-user-select: none !important;
8981
+ pointer-events: none !important;
8982
+ }
8983
+ html[data-ohw-item-dragging] {
8984
+ cursor: grabbing !important;
8985
+ }
8263
8986
  [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
8264
8987
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8265
8988
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
@@ -8305,12 +9028,39 @@ function OhhwellsBridge() {
8305
9028
  }
8306
9029
  refreshStateRules();
8307
9030
  const handleClick = (e) => {
9031
+ if (suppressNextClickRef.current) {
9032
+ suppressNextClickRef.current = false;
9033
+ e.preventDefault();
9034
+ e.stopPropagation();
9035
+ return;
9036
+ }
8308
9037
  const target = e.target;
8309
9038
  if (target.closest("[data-ohw-toolbar]")) return;
8310
9039
  if (target.closest("[data-ohw-state-toggle]")) return;
8311
9040
  if (target.closest("[data-ohw-max-badge]")) return;
8312
9041
  if (isInsideLinkEditor(target)) return;
8313
9042
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9043
+ if (target.closest("[data-ohw-item-drag-surface]")) {
9044
+ e.preventDefault();
9045
+ e.stopPropagation();
9046
+ const selected = selectedElRef.current;
9047
+ if (selected && isNavigationItem(selected)) {
9048
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9049
+ if (editable2) {
9050
+ activateRef.current(editable2, {
9051
+ caretX: e.clientX,
9052
+ caretY: e.clientY
9053
+ });
9054
+ }
9055
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
9056
+ const link = listFooterLinksInColumn(selected).find((el) => {
9057
+ const r2 = el.getBoundingClientRect();
9058
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
9059
+ });
9060
+ if (link) selectRef.current(link);
9061
+ }
9062
+ return;
9063
+ }
8314
9064
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8315
9065
  if (navFromChrome) {
8316
9066
  e.preventDefault();
@@ -8365,10 +9115,26 @@ function OhhwellsBridge() {
8365
9115
  if (hrefAnchor) {
8366
9116
  e.preventDefault();
8367
9117
  e.stopPropagation();
8368
- if (selectedElRef.current === hrefAnchor) return;
9118
+ if (selectedElRef.current === hrefAnchor) {
9119
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
9120
+ if (textEditable) {
9121
+ activateRef.current(textEditable, {
9122
+ caretX: e.clientX,
9123
+ caretY: e.clientY
9124
+ });
9125
+ }
9126
+ return;
9127
+ }
8369
9128
  selectRef.current(hrefAnchor);
8370
9129
  return;
8371
9130
  }
9131
+ const footerColClick = target.closest("[data-ohw-footer-col]");
9132
+ if (footerColClick) {
9133
+ e.preventDefault();
9134
+ e.stopPropagation();
9135
+ selectFrameRef.current(footerColClick);
9136
+ return;
9137
+ }
8372
9138
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8373
9139
  if (navContainerToSelect) {
8374
9140
  e.preventDefault();
@@ -8449,6 +9215,15 @@ function OhhwellsBridge() {
8449
9215
  setHoveredNavContainerRect(null);
8450
9216
  }
8451
9217
  }
9218
+ const footerCol = target.closest("[data-ohw-footer-col]");
9219
+ if (footerCol) {
9220
+ hoveredNavContainerRef.current = null;
9221
+ setHoveredNavContainerRect(null);
9222
+ if (selectedElRef.current === footerCol) return;
9223
+ hoveredItemElRef.current = footerCol;
9224
+ setHoveredItemRect(footerCol.getBoundingClientRect());
9225
+ return;
9226
+ }
8452
9227
  const navAnchor = getNavigationItemAnchor(target);
8453
9228
  if (navAnchor) {
8454
9229
  hoveredNavContainerRef.current = null;
@@ -8485,6 +9260,15 @@ function OhhwellsBridge() {
8485
9260
  hoveredNavContainerRef.current = null;
8486
9261
  setHoveredNavContainerRect(null);
8487
9262
  }
9263
+ const footerCol = target.closest("[data-ohw-footer-col]");
9264
+ if (footerCol && hoveredItemElRef.current === footerCol) {
9265
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9266
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
9267
+ if (related2?.closest("[data-ohw-footer-col]") === footerCol) return;
9268
+ hoveredItemElRef.current = null;
9269
+ setHoveredItemRect(null);
9270
+ return;
9271
+ }
8488
9272
  const navAnchor = getNavigationItemAnchor(target);
8489
9273
  if (navAnchor) {
8490
9274
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8893,6 +9677,14 @@ function OhhwellsBridge() {
8893
9677
  probeHoverCardsAt(clientX, clientY);
8894
9678
  };
8895
9679
  const handleDragOver = (e) => {
9680
+ const footerSession = footerDragRef.current;
9681
+ if (footerSession) {
9682
+ e.preventDefault();
9683
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
9684
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
9685
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
9686
+ return;
9687
+ }
8896
9688
  e.preventDefault();
8897
9689
  const el = findImageAtPoint(e.clientX, e.clientY, false);
8898
9690
  if (!el) {
@@ -9100,6 +9892,7 @@ function OhhwellsBridge() {
9100
9892
  }
9101
9893
  editContentRef.current = { ...editContentRef.current, ...content };
9102
9894
  reconcileNavbarItemsFromContent(editContentRef.current);
9895
+ reconcileFooterOrderFromContent(editContentRef.current);
9103
9896
  enforceLinkHrefs();
9104
9897
  postToParentRef.current({ type: "ow:hydrate-done" });
9105
9898
  };
@@ -9213,6 +10006,17 @@ function OhhwellsBridge() {
9213
10006
  if (siblingHintElRef.current) {
9214
10007
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9215
10008
  }
10009
+ const selected = selectedElRef.current;
10010
+ if (selected && !footerDragRef.current && (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)))) {
10011
+ setSiblingHintRects(
10012
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
10013
+ );
10014
+ }
10015
+ if (footerDragRef.current) {
10016
+ const session = footerDragRef.current;
10017
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
10018
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
10019
+ }
9216
10020
  if (hoveredImageRef.current) {
9217
10021
  const el = hoveredImageRef.current;
9218
10022
  const r2 = el.getBoundingClientRect();
@@ -9223,7 +10027,7 @@ function OhhwellsBridge() {
9223
10027
  };
9224
10028
  const handleSave = (e) => {
9225
10029
  if (e.data?.type !== "ow:save") return;
9226
- const nodes = collectEditableNodes();
10030
+ const nodes = collectEditableNodes(editContentRef.current);
9227
10031
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9228
10032
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9229
10033
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9563,6 +10367,154 @@ function OhhwellsBridge() {
9563
10367
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9564
10368
  };
9565
10369
  }, [isEditMode, refreshStateRules]);
10370
+ useEffect8(() => {
10371
+ if (!isEditMode) return;
10372
+ const THRESHOLD = 10;
10373
+ const resolveWasSelected = (el) => {
10374
+ if (selectedElRef.current === el) return true;
10375
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
10376
+ return activeAnchor === el;
10377
+ };
10378
+ const onPointerDown = (e) => {
10379
+ if (e.button !== 0) return;
10380
+ if (footerDragRef.current) return;
10381
+ const target = e.target;
10382
+ if (!target) return;
10383
+ 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]')) {
10384
+ return;
10385
+ }
10386
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
10387
+ const anchor = getNavigationItemAnchor(target);
10388
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
10389
+ if (anchor && isFooterHrefKey(hrefKey)) {
10390
+ armFooterPressDrag();
10391
+ footerPointerDragRef.current = {
10392
+ el: anchor,
10393
+ kind: "link",
10394
+ startX: e.clientX,
10395
+ startY: e.clientY,
10396
+ pointerId: e.pointerId,
10397
+ wasSelected: resolveWasSelected(anchor),
10398
+ started: false
10399
+ };
10400
+ return;
10401
+ }
10402
+ const col = target.closest("[data-ohw-footer-col]");
10403
+ if (col && !getNavigationItemAnchor(target)) {
10404
+ armFooterPressDrag();
10405
+ footerPointerDragRef.current = {
10406
+ el: col,
10407
+ kind: "column",
10408
+ startX: e.clientX,
10409
+ startY: e.clientY,
10410
+ pointerId: e.pointerId,
10411
+ wasSelected: resolveWasSelected(col),
10412
+ started: false
10413
+ };
10414
+ }
10415
+ };
10416
+ const onPointerMove = (e) => {
10417
+ const pending = footerPointerDragRef.current;
10418
+ if (!pending) return;
10419
+ if (pending.started) {
10420
+ e.preventDefault();
10421
+ clearTextSelection();
10422
+ const session = footerDragRef.current;
10423
+ if (!session) return;
10424
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10425
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
10426
+ return;
10427
+ }
10428
+ const dx = e.clientX - pending.startX;
10429
+ const dy = e.clientY - pending.startY;
10430
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
10431
+ e.preventDefault();
10432
+ pending.started = true;
10433
+ clearTextSelection();
10434
+ try {
10435
+ document.body.setPointerCapture(pending.pointerId);
10436
+ } catch {
10437
+ }
10438
+ if (linkPopoverOpenRef.current) {
10439
+ setLinkPopoverRef.current(null);
10440
+ }
10441
+ if (activeElRef.current) {
10442
+ deactivateRef.current();
10443
+ }
10444
+ if (pending.kind === "link") {
10445
+ const key = pending.el.getAttribute("data-ohw-href-key");
10446
+ if (!key) return;
10447
+ const column = findFooterColumnForLink(pending.el);
10448
+ const columns2 = listFooterColumns();
10449
+ beginFooterDragRef.current({
10450
+ kind: "link",
10451
+ hrefKey: key,
10452
+ columnEl: column,
10453
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
10454
+ wasSelected: pending.wasSelected,
10455
+ draggedEl: pending.el,
10456
+ lastClientX: e.clientX,
10457
+ lastClientY: e.clientY,
10458
+ activeSlot: null
10459
+ });
10460
+ return;
10461
+ }
10462
+ const columns = listFooterColumns();
10463
+ const idx = columns.indexOf(pending.el);
10464
+ if (idx < 0) return;
10465
+ beginFooterDragRef.current({
10466
+ kind: "column",
10467
+ hrefKey: null,
10468
+ columnEl: pending.el,
10469
+ sourceColumnIndex: idx,
10470
+ wasSelected: pending.wasSelected,
10471
+ draggedEl: pending.el,
10472
+ lastClientX: e.clientX,
10473
+ lastClientY: e.clientY,
10474
+ activeSlot: null
10475
+ });
10476
+ };
10477
+ const endPointerDrag = (e) => {
10478
+ const pending = footerPointerDragRef.current;
10479
+ footerPointerDragRef.current = null;
10480
+ try {
10481
+ if (document.body.hasPointerCapture(e.pointerId)) {
10482
+ document.body.releasePointerCapture(e.pointerId);
10483
+ }
10484
+ } catch {
10485
+ }
10486
+ if (!pending?.started) {
10487
+ unlockFooterDragInteraction();
10488
+ return;
10489
+ }
10490
+ suppressNextClickRef.current = true;
10491
+ commitFooterDragRef.current(e.clientX, e.clientY);
10492
+ };
10493
+ const blockSelectStart = (e) => {
10494
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
10495
+ e.preventDefault();
10496
+ }
10497
+ };
10498
+ const onDragEnd = (_e) => {
10499
+ if (!footerDragRef.current) return;
10500
+ commitFooterDragRef.current();
10501
+ };
10502
+ document.addEventListener("pointerdown", onPointerDown, true);
10503
+ document.addEventListener("pointermove", onPointerMove, true);
10504
+ document.addEventListener("pointerup", endPointerDrag, true);
10505
+ document.addEventListener("pointercancel", endPointerDrag, true);
10506
+ document.addEventListener("selectstart", blockSelectStart, true);
10507
+ document.addEventListener("dragend", onDragEnd, true);
10508
+ return () => {
10509
+ document.removeEventListener("pointerdown", onPointerDown, true);
10510
+ document.removeEventListener("pointermove", onPointerMove, true);
10511
+ document.removeEventListener("pointerup", endPointerDrag, true);
10512
+ document.removeEventListener("pointercancel", endPointerDrag, true);
10513
+ document.removeEventListener("selectstart", blockSelectStart, true);
10514
+ document.removeEventListener("dragend", onDragEnd, true);
10515
+ unlockFooterDragInteraction();
10516
+ };
10517
+ }, [isEditMode]);
9566
10518
  useEffect8(() => {
9567
10519
  const handler = (e) => {
9568
10520
  if (e.data?.type !== "ow:request-schedule-config") return;
@@ -9600,7 +10552,13 @@ function OhhwellsBridge() {
9600
10552
  const next = { ...prev, [pathKey]: sections };
9601
10553
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9602
10554
  });
9603
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
10555
+ postToParent2({
10556
+ type: "ow:ready",
10557
+ version: "1",
10558
+ path: pathname,
10559
+ nodes: collectEditableNodes(editContentRef.current),
10560
+ sections
10561
+ });
9604
10562
  postToParent2({ type: "ow:request-site-pages" });
9605
10563
  }, 150);
9606
10564
  return () => {
@@ -9717,9 +10675,6 @@ function OhhwellsBridge() {
9717
10675
  },
9718
10676
  [postToParent2, sitePages, sectionsByPath]
9719
10677
  );
9720
- const showEditLink = toolbarShowEditLink;
9721
- const currentSections = sectionsByPath[pathname] ?? [];
9722
- linkPopoverOpenRef.current = linkPopover !== null;
9723
10678
  const handleMediaReplace = useCallback4(
9724
10679
  (key) => {
9725
10680
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
@@ -9762,10 +10717,13 @@ function OhhwellsBridge() {
9762
10717
  },
9763
10718
  [postToParent2]
9764
10719
  );
10720
+ const showEditLink = toolbarShowEditLink;
10721
+ const currentSections = sectionsByPath[pathname] ?? [];
10722
+ linkPopoverOpenRef.current = linkPopover !== null;
9765
10723
  return bridgeRoot ? createPortal2(
9766
10724
  /* @__PURE__ */ jsxs14(Fragment5, { children: [
9767
- /* @__PURE__ */ jsx24("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9768
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx24(
10725
+ /* @__PURE__ */ jsx25("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
10726
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx25(
9769
10727
  MediaOverlay,
9770
10728
  {
9771
10729
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -9776,7 +10734,7 @@ function OhhwellsBridge() {
9776
10734
  },
9777
10735
  `uploading-${key}`
9778
10736
  )),
9779
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx24(
10737
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx25(
9780
10738
  MediaOverlay,
9781
10739
  {
9782
10740
  hover: mediaHover,
@@ -9785,61 +10743,31 @@ function OhhwellsBridge() {
9785
10743
  onVideoSettingsChange: handleVideoSettingsChange
9786
10744
  }
9787
10745
  ),
9788
- carouselHover && /* @__PURE__ */ jsx24(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
9789
- siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9790
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9791
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx24(
9792
- NavbarContainerChrome,
9793
- {
9794
- rect: toolbarRect,
9795
- onAdd: handleAddTopLevelNavItem
9796
- }
9797
- ),
9798
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9799
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx24(
9800
- ItemInteractionLayer,
10746
+ carouselHover && /* @__PURE__ */ jsx25(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
10747
+ siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
10748
+ siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
10749
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
10750
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx25(
10751
+ "div",
9801
10752
  {
9802
- rect: toolbarRect,
9803
- elRef: glowElRef,
9804
- state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9805
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
9806
- dragDisabled: reorderDragDisabled,
9807
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9808
- onDragHandleDragStart: handleItemDragStart,
9809
- onDragHandleDragEnd: handleItemDragEnd,
9810
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx24(
9811
- ItemActionToolbar,
9812
- {
9813
- onEditLink: openLinkPopoverForSelected,
9814
- addItemDisabled: true,
9815
- editLinkDisabled: false,
9816
- moreDisabled: true
9817
- }
9818
- ) : void 0
9819
- }
9820
- ),
10753
+ className: "pointer-events-none fixed z-2147483646",
10754
+ style: {
10755
+ left: slot.left,
10756
+ top: slot.top,
10757
+ width: slot.width,
10758
+ height: slot.height
10759
+ },
10760
+ children: /* @__PURE__ */ jsx25(DropIndicator, { direction: slot.direction, state: activeFooterDropIndex === i ? "dragActive" : "dragIdle", className: slot.direction === "horizontal" ? "!h-full !w-full" : "!h-full !w-full" })
10761
+ },
10762
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
10763
+ )),
10764
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
10765
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ jsx25(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
10766
+ hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
10767
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: isItemDragging && draggedItemRect && footerDragRef.current?.wasSelected ? draggedItemRect : toolbarRect, elRef: glowElRef, state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current), showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection, dragDisabled: reorderDragDisabled, dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item", onDragHandleDragStart: handleItemDragStart, onDragHandleDragEnd: handleItemDragEnd, onItemPointerDown: handleItemChromePointerDown, onItemClick: handleItemChromeClick, toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx25(ItemActionToolbar, { onEditLink: openLinkPopoverForSelected, addItemDisabled: true, editLinkDisabled: false, moreDisabled: true }) : void 0 }),
9821
10768
  toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs14(Fragment5, { children: [
9822
- /* @__PURE__ */ jsx24(
9823
- EditGlowChrome,
9824
- {
9825
- rect: toolbarRect,
9826
- elRef: glowElRef,
9827
- reorderHrefKey,
9828
- dragDisabled: reorderDragDisabled
9829
- }
9830
- ),
9831
- /* @__PURE__ */ jsx24(
9832
- FloatingToolbar,
9833
- {
9834
- rect: toolbarRect,
9835
- parentScroll: parentScrollRef.current,
9836
- elRef: toolbarElRef,
9837
- onCommand: handleCommand,
9838
- activeCommands,
9839
- showEditLink,
9840
- onEditLink: openLinkPopoverForActive
9841
- }
9842
- )
10769
+ /* @__PURE__ */ jsx25(EditGlowChrome, { rect: toolbarRect, elRef: glowElRef, reorderHrefKey, dragDisabled: reorderDragDisabled }),
10770
+ /* @__PURE__ */ jsx25(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands, showEditLink, onEditLink: openLinkPopoverForActive })
9843
10771
  ] }),
9844
10772
  maxBadge && /* @__PURE__ */ jsxs14(
9845
10773
  "div",
@@ -9867,128 +10795,33 @@ function OhhwellsBridge() {
9867
10795
  ]
9868
10796
  }
9869
10797
  ),
9870
- toggleState && !linkPopover && /* @__PURE__ */ jsx24(
9871
- StateToggle,
9872
- {
9873
- rect: toggleState.rect,
9874
- activeState: toggleState.activeState,
9875
- states: toggleState.states,
9876
- onStateChange: handleStateChange
9877
- }
9878
- ),
9879
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs14(
9880
- "div",
9881
- {
9882
- "data-ohw-section-insert-line": "",
9883
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9884
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
9885
- children: [
9886
- /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9887
- /* @__PURE__ */ jsx24(
9888
- Badge,
9889
- {
9890
- 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",
9891
- onClick: () => {
9892
- window.parent.postMessage(
9893
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
9894
- "*"
9895
- );
9896
- },
9897
- children: "Add Section"
9898
- }
9899
- ),
9900
- /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9901
- ]
9902
- }
9903
- ),
9904
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx24(
9905
- LinkPopover,
9906
- {
9907
- panelRef: linkPopoverPanelRef,
9908
- portalContainer: dialogPortalContainer,
9909
- open: true,
9910
- mode: linkPopover.mode ?? "edit",
9911
- pages: sitePages,
9912
- sections: currentSections,
9913
- sectionsByPath,
9914
- initialTarget: linkPopover.target,
9915
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
9916
- onClose: closeLinkPopover,
9917
- onSubmit: handleLinkPopoverSubmit
9918
- },
9919
- linkPopover.key
9920
- ) : null,
9921
- sectionGap && /* @__PURE__ */ jsxs14(
9922
- "div",
9923
- {
9924
- "data-ohw-section-insert-line": "",
9925
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9926
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
9927
- children: [
9928
- /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9929
- /* @__PURE__ */ jsx24(
9930
- Badge,
9931
- {
9932
- 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",
9933
- onClick: () => {
9934
- window.parent.postMessage(
9935
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
9936
- "*"
9937
- );
10798
+ toggleState && !linkPopover && /* @__PURE__ */ jsx25(StateToggle, { rect: toggleState.rect, activeState: toggleState.activeState, states: toggleState.states, onStateChange: handleStateChange }),
10799
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs14("div", { "data-ohw-section-insert-line": "", className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none", style: { top: sectionGap.y, transform: "translateY(-50%)" }, children: [
10800
+ /* @__PURE__ */ jsx25("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10801
+ /* @__PURE__ */ jsx25(
10802
+ Badge,
10803
+ {
10804
+ 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",
10805
+ onClick: () => {
10806
+ window.parent.postMessage(
10807
+ {
10808
+ type: "ow:add-section",
10809
+ insertAfter: sectionGap.insertAfter,
10810
+ insertBefore: sectionGap.insertBefore
9938
10811
  },
9939
- children: "Add Section"
9940
- }
9941
- ),
9942
- /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9943
- ]
9944
- }
9945
- )
10812
+ "*"
10813
+ );
10814
+ },
10815
+ children: "Add Section"
10816
+ }
10817
+ ),
10818
+ /* @__PURE__ */ jsx25("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10819
+ ] }),
10820
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx25(LinkPopover, { panelRef: linkPopoverPanelRef, portalContainer: dialogPortalContainer, open: true, mode: linkPopover.mode ?? "edit", pages: sitePages, sections: currentSections, sectionsByPath, initialTarget: linkPopover.target, existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [], onClose: closeLinkPopover, onSubmit: handleLinkPopoverSubmit }, linkPopover.key) : null
9946
10821
  ] }),
9947
10822
  bridgeRoot
9948
10823
  ) : null;
9949
10824
  }
9950
-
9951
- // src/ui/drop-indicator.tsx
9952
- import * as React10 from "react";
9953
- import { jsx as jsx25 } from "react/jsx-runtime";
9954
- var dropIndicatorVariants = cva(
9955
- "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
9956
- {
9957
- variants: {
9958
- direction: {
9959
- vertical: "h-6 w-[3px]",
9960
- horizontal: "h-[3px] w-[200px]"
9961
- },
9962
- state: {
9963
- default: "opacity-0",
9964
- hover: "opacity-100",
9965
- dragIdle: "opacity-40",
9966
- dragActive: "opacity-100"
9967
- }
9968
- },
9969
- defaultVariants: {
9970
- direction: "vertical",
9971
- state: "default"
9972
- }
9973
- }
9974
- );
9975
- var DropIndicator = React10.forwardRef(
9976
- ({ className, direction, state, ...props }, ref) => {
9977
- return /* @__PURE__ */ jsx25(
9978
- "div",
9979
- {
9980
- ref,
9981
- "data-slot": "drop-indicator",
9982
- "data-direction": direction ?? "vertical",
9983
- "data-state": state ?? "default",
9984
- "aria-hidden": "true",
9985
- className: cn(dropIndicatorVariants({ direction, state }), className),
9986
- ...props
9987
- }
9988
- );
9989
- }
9990
- );
9991
- DropIndicator.displayName = "DropIndicator";
9992
10825
  export {
9993
10826
  CustomToolbar,
9994
10827
  CustomToolbarButton,