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

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
@@ -4506,7 +4506,7 @@ function getChromeStyle(state) {
4506
4506
  case "dragging":
4507
4507
  return {
4508
4508
  border: `2px solid ${PRIMARY}`,
4509
- boxShadow: DRAG_SHADOW
4509
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4510
4510
  };
4511
4511
  default:
4512
4512
  return {};
@@ -4575,6 +4575,8 @@ function ItemInteractionLayer({
4575
4575
  dragHandleLabel = "Reorder item",
4576
4576
  onDragHandleDragStart,
4577
4577
  onDragHandleDragEnd,
4578
+ onItemPointerDown,
4579
+ onItemClick,
4578
4580
  chromeGap = SELECTION_CHROME_GAP,
4579
4581
  className
4580
4582
  }) {
@@ -4582,7 +4584,8 @@ function ItemInteractionLayer({
4582
4584
  const isActive = state === "active-top" || state === "active-bottom";
4583
4585
  const isDragging = state === "dragging";
4584
4586
  const showToolbar = isActive && toolbar;
4585
- const showDragHandle = isActive && showHandle && !isDragging;
4587
+ const showDragHandle = (isActive || isDragging) && showHandle;
4588
+ const itemDragEnabled = isActive && showHandle && !isDragging && !dragDisabled;
4586
4589
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4587
4590
  "div",
4588
4591
  {
@@ -4607,11 +4610,28 @@ function ItemInteractionLayer({
4607
4610
  style: getChromeStyle(state)
4608
4611
  }
4609
4612
  ),
4613
+ itemDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4614
+ "div",
4615
+ {
4616
+ "data-ohw-item-drag-surface": "",
4617
+ className: "pointer-events-auto absolute inset-0 cursor-grab rounded-lg active:cursor-grabbing",
4618
+ onPointerDown: (e) => {
4619
+ if (e.button !== 0) return;
4620
+ onItemPointerDown?.(e);
4621
+ },
4622
+ onClick: (e) => {
4623
+ e.preventDefault();
4624
+ e.stopPropagation();
4625
+ onItemClick?.(e.clientX, e.clientY);
4626
+ }
4627
+ }
4628
+ ),
4610
4629
  showDragHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4611
4630
  "div",
4612
4631
  {
4613
4632
  "data-ohw-drag-handle-container": "",
4614
- className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4633
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4634
+ style: isDragging ? { visibility: "hidden" } : void 0,
4615
4635
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4616
4636
  DragHandle,
4617
4637
  {
@@ -6554,6 +6574,319 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6554
6574
  };
6555
6575
  }
6556
6576
 
6577
+ // src/lib/footer-items.ts
6578
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6579
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6580
+ function parseFooterHrefKey(key) {
6581
+ if (!key) return null;
6582
+ const match = key.match(FOOTER_HREF_RE);
6583
+ if (!match) return null;
6584
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6585
+ }
6586
+ function isFooterHrefKey(key) {
6587
+ return parseFooterHrefKey(key) !== null;
6588
+ }
6589
+ function getFooterRoot() {
6590
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6591
+ }
6592
+ function getFooterLinksContainer() {
6593
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6594
+ }
6595
+ function isFooterLinkAnchor(el) {
6596
+ if (!el.matches("[data-ohw-href-key]")) return false;
6597
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6598
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6599
+ }
6600
+ function listFooterColumns() {
6601
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6602
+ if (!root) return [];
6603
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6604
+ if (explicit.length > 0) return explicit;
6605
+ return Array.from(root.children).filter((el) => {
6606
+ if (!(el instanceof HTMLElement)) return false;
6607
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6608
+ isFooterLinkAnchor
6609
+ );
6610
+ });
6611
+ }
6612
+ function listFooterLinksInColumn(column) {
6613
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6614
+ isFooterLinkAnchor
6615
+ );
6616
+ }
6617
+ function findFooterColumnForLink(anchor) {
6618
+ const columns = listFooterColumns();
6619
+ return columns.find((col) => col.contains(anchor)) ?? null;
6620
+ }
6621
+ function getFooterOrderFromDom() {
6622
+ return listFooterColumns().map(
6623
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6624
+ );
6625
+ }
6626
+ function findFooterLinkByKey(hrefKey) {
6627
+ return document.querySelector(
6628
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6629
+ );
6630
+ }
6631
+ function syncFooterColumnIndices(columns) {
6632
+ columns.forEach((col, index) => {
6633
+ const next = String(index);
6634
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6635
+ col.setAttribute("data-ohw-footer-col", next);
6636
+ }
6637
+ });
6638
+ }
6639
+ function appendChildIfNeeded(parent, child) {
6640
+ if (parent.lastElementChild !== child) {
6641
+ parent.appendChild(child);
6642
+ }
6643
+ }
6644
+ function insertLinkBeforeNext(col, el, nextEl) {
6645
+ if (nextEl) {
6646
+ if (el.nextElementSibling !== nextEl) {
6647
+ col.insertBefore(el, nextEl);
6648
+ }
6649
+ return;
6650
+ }
6651
+ if (col.lastElementChild !== el) {
6652
+ col.appendChild(el);
6653
+ }
6654
+ }
6655
+ function applyFooterOrder(order) {
6656
+ const container = getFooterLinksContainer();
6657
+ if (!container) return;
6658
+ const columns = listFooterColumns();
6659
+ if (columns.length === 0) return;
6660
+ const currentOrder = getFooterOrderFromDom();
6661
+ if (ordersEqual(order, currentOrder)) {
6662
+ syncFooterColumnIndices(columns);
6663
+ return;
6664
+ }
6665
+ const colCount = Math.max(columns.length, order.length);
6666
+ const working = columns.slice(0, colCount);
6667
+ for (let i = 0; i < order.length && i < working.length; i++) {
6668
+ appendChildIfNeeded(container, working[i]);
6669
+ }
6670
+ for (let i = order.length; i < working.length; i++) {
6671
+ appendChildIfNeeded(container, working[i]);
6672
+ }
6673
+ const freshColumns = listFooterColumns();
6674
+ syncFooterColumnIndices(freshColumns);
6675
+ for (let c = 0; c < order.length; c++) {
6676
+ const col = freshColumns[c];
6677
+ if (!col) continue;
6678
+ const colOrder = order[c];
6679
+ for (let j = 0; j < colOrder.length; j++) {
6680
+ const hrefKey = colOrder[j];
6681
+ const el = findFooterLinkByKey(hrefKey);
6682
+ if (!el) continue;
6683
+ if (el.parentElement !== col) {
6684
+ col.appendChild(el);
6685
+ }
6686
+ const nextKey = colOrder[j + 1];
6687
+ const nextEl = nextKey ? findFooterLinkByKey(nextKey) : null;
6688
+ insertLinkBeforeNext(col, el, nextEl?.parentElement === col ? nextEl : null);
6689
+ }
6690
+ }
6691
+ }
6692
+ function ordersEqual(a, b) {
6693
+ if (a.length !== b.length) return false;
6694
+ for (let i = 0; i < a.length; i++) {
6695
+ const left = a[i];
6696
+ const right = b[i];
6697
+ if (left.length !== right.length) return false;
6698
+ for (let j = 0; j < left.length; j++) {
6699
+ if (left[j] !== right[j]) return false;
6700
+ }
6701
+ }
6702
+ return true;
6703
+ }
6704
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6705
+ const order = getFooterOrderFromDom();
6706
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6707
+ let fromCol = -1;
6708
+ let fromIdx = -1;
6709
+ for (let c = 0; c < order.length; c++) {
6710
+ const idx = order[c].indexOf(hrefKey);
6711
+ if (idx >= 0) {
6712
+ fromCol = c;
6713
+ fromIdx = idx;
6714
+ break;
6715
+ }
6716
+ }
6717
+ if (fromCol < 0) return null;
6718
+ const next = order.map((col) => [...col]);
6719
+ next[fromCol].splice(fromIdx, 1);
6720
+ let adjusted = insertIndex;
6721
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6722
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6723
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
6724
+ if (ordersEqual(next, order)) return null;
6725
+ return next;
6726
+ }
6727
+ function planFooterColumnMove(fromIndex, toIndex) {
6728
+ const order = getFooterOrderFromDom();
6729
+ const columns = listFooterColumns();
6730
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6731
+ return null;
6732
+ }
6733
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6734
+ const nextOrder = order.map((col) => [...col]);
6735
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
6736
+ if (!movedOrder) return null;
6737
+ let adjusted = toIndex;
6738
+ if (fromIndex < toIndex) adjusted -= 1;
6739
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6740
+ nextOrder.splice(adjusted, 0, movedOrder);
6741
+ if (ordersEqual(nextOrder, order)) return null;
6742
+ return nextOrder;
6743
+ }
6744
+ function parseFooterOrder(content) {
6745
+ const raw = content[FOOTER_ORDER_KEY];
6746
+ if (!raw) return null;
6747
+ try {
6748
+ const parsed = JSON.parse(raw);
6749
+ if (!Array.isArray(parsed)) return null;
6750
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
6751
+ } catch {
6752
+ return null;
6753
+ }
6754
+ }
6755
+ function reconcileFooterOrderFromContent(content) {
6756
+ const order = parseFooterOrder(content);
6757
+ if (!order?.length) return;
6758
+ const current = getFooterOrderFromDom();
6759
+ if (ordersEqual(order, current)) {
6760
+ syncFooterColumnIndices(listFooterColumns());
6761
+ return;
6762
+ }
6763
+ applyFooterOrder(order);
6764
+ }
6765
+ function buildLinkDropSlots(column, columnIndex) {
6766
+ const links = listFooterLinksInColumn(column);
6767
+ const colRect = column.getBoundingClientRect();
6768
+ const slots = [];
6769
+ const barThickness = 3;
6770
+ if (links.length === 0) {
6771
+ slots.push({
6772
+ insertIndex: 0,
6773
+ columnIndex,
6774
+ left: colRect.left,
6775
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
6776
+ width: colRect.width,
6777
+ height: barThickness,
6778
+ direction: "horizontal"
6779
+ });
6780
+ return slots;
6781
+ }
6782
+ for (let i = 0; i <= links.length; i++) {
6783
+ let top;
6784
+ if (i === 0) {
6785
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
6786
+ } else if (i === links.length) {
6787
+ const last = links[links.length - 1].getBoundingClientRect();
6788
+ top = last.bottom - barThickness / 2;
6789
+ } else {
6790
+ const prev = links[i - 1].getBoundingClientRect();
6791
+ const next = links[i].getBoundingClientRect();
6792
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
6793
+ }
6794
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
6795
+ slots.push({
6796
+ insertIndex: i,
6797
+ columnIndex,
6798
+ left: widthRef.left,
6799
+ top,
6800
+ width: Math.max(widthRef.width, colRect.width * 0.8),
6801
+ height: barThickness,
6802
+ direction: "horizontal"
6803
+ });
6804
+ }
6805
+ return slots;
6806
+ }
6807
+ function buildColumnDropSlots() {
6808
+ const columns = listFooterColumns();
6809
+ const slots = [];
6810
+ const barThickness = 3;
6811
+ if (columns.length === 0) return slots;
6812
+ for (let i = 0; i <= columns.length; i++) {
6813
+ let left;
6814
+ let height;
6815
+ let top;
6816
+ if (i === 0) {
6817
+ const first = columns[0].getBoundingClientRect();
6818
+ left = first.left - barThickness / 2;
6819
+ top = first.top;
6820
+ height = first.height;
6821
+ } else if (i === columns.length) {
6822
+ const last = columns[columns.length - 1].getBoundingClientRect();
6823
+ left = last.right - barThickness / 2;
6824
+ top = last.top;
6825
+ height = last.height;
6826
+ } else {
6827
+ const prev = columns[i - 1].getBoundingClientRect();
6828
+ const next = columns[i].getBoundingClientRect();
6829
+ left = (prev.right + next.left) / 2 - barThickness / 2;
6830
+ top = Math.min(prev.top, next.top);
6831
+ height = Math.max(prev.bottom, next.bottom) - top;
6832
+ }
6833
+ slots.push({
6834
+ insertIndex: i,
6835
+ columnIndex: i,
6836
+ left,
6837
+ top,
6838
+ width: barThickness,
6839
+ height: Math.max(height, 24),
6840
+ direction: "vertical"
6841
+ });
6842
+ }
6843
+ return slots;
6844
+ }
6845
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
6846
+ const columns = listFooterColumns();
6847
+ let best = null;
6848
+ for (let c = 0; c < columns.length; c++) {
6849
+ const col = columns[c];
6850
+ const colRect = col.getBoundingClientRect();
6851
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
6852
+ if (!inColX) continue;
6853
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
6854
+ const links = listFooterLinksInColumn(col);
6855
+ const fromIdx = links.findIndex(
6856
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
6857
+ );
6858
+ if (fromIdx < 0) return true;
6859
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
6860
+ return true;
6861
+ }
6862
+ return true;
6863
+ });
6864
+ for (const slot of slots) {
6865
+ const cy = slot.top + slot.height / 2;
6866
+ const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
6867
+ if (!best || dist < best.dist) best = { slot, dist };
6868
+ }
6869
+ }
6870
+ return best?.slot ?? null;
6871
+ }
6872
+ function findColumnIndexForKey(hrefKey) {
6873
+ const order = getFooterOrderFromDom();
6874
+ for (let c = 0; c < order.length; c++) {
6875
+ if (order[c].includes(hrefKey)) return c;
6876
+ }
6877
+ return -1;
6878
+ }
6879
+ function hitTestColumnDropSlot(clientX, _clientY) {
6880
+ const slots = buildColumnDropSlots();
6881
+ let best = null;
6882
+ for (const slot of slots) {
6883
+ const cx2 = slot.left + slot.width / 2;
6884
+ const dist = Math.abs(clientX - cx2);
6885
+ if (!best || dist < best.dist) best = { slot, dist };
6886
+ }
6887
+ return best?.slot ?? null;
6888
+ }
6889
+
6557
6890
  // src/ui/navbar-container-chrome.tsx
6558
6891
  var import_lucide_react10 = require("lucide-react");
6559
6892
  var import_jsx_runtime21 = require("react/jsx-runtime");
@@ -6598,8 +6931,50 @@ function NavbarContainerChrome({
6598
6931
  );
6599
6932
  }
6600
6933
 
6601
- // src/ui/badge.tsx
6934
+ // src/ui/drop-indicator.tsx
6935
+ var React10 = __toESM(require("react"), 1);
6602
6936
  var import_jsx_runtime22 = require("react/jsx-runtime");
6937
+ var dropIndicatorVariants = cva(
6938
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
6939
+ {
6940
+ variants: {
6941
+ direction: {
6942
+ vertical: "h-6 w-[3px]",
6943
+ horizontal: "h-[3px] w-[200px]"
6944
+ },
6945
+ state: {
6946
+ default: "opacity-0",
6947
+ hover: "opacity-100",
6948
+ dragIdle: "opacity-40",
6949
+ dragActive: "opacity-100"
6950
+ }
6951
+ },
6952
+ defaultVariants: {
6953
+ direction: "vertical",
6954
+ state: "default"
6955
+ }
6956
+ }
6957
+ );
6958
+ var DropIndicator = React10.forwardRef(
6959
+ ({ className, direction, state, ...props }, ref) => {
6960
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6961
+ "div",
6962
+ {
6963
+ ref,
6964
+ "data-slot": "drop-indicator",
6965
+ "data-direction": direction ?? "vertical",
6966
+ "data-state": state ?? "default",
6967
+ "aria-hidden": "true",
6968
+ className: cn(dropIndicatorVariants({ direction, state }), className),
6969
+ ...props
6970
+ }
6971
+ );
6972
+ }
6973
+ );
6974
+ DropIndicator.displayName = "DropIndicator";
6975
+
6976
+ // src/ui/badge.tsx
6977
+ var import_jsx_runtime23 = require("react/jsx-runtime");
6603
6978
  var badgeVariants = cva(
6604
6979
  "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
6980
  {
@@ -6617,12 +6992,12 @@ var badgeVariants = cva(
6617
6992
  }
6618
6993
  );
6619
6994
  function Badge({ className, variant, ...props }) {
6620
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6995
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6621
6996
  }
6622
6997
 
6623
6998
  // src/OhhwellsBridge.tsx
6624
6999
  var import_lucide_react11 = require("lucide-react");
6625
- var import_jsx_runtime23 = require("react/jsx-runtime");
7000
+ var import_jsx_runtime24 = require("react/jsx-runtime");
6626
7001
  var PRIMARY2 = "#0885FE";
6627
7002
  var IMAGE_FADE_MS = 300;
6628
7003
  function runOpacityFade(el, onDone) {
@@ -6801,7 +7176,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6801
7176
  const root = (0, import_client.createRoot)(container);
6802
7177
  (0, import_react_dom2.flushSync)(() => {
6803
7178
  root.render(
6804
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7179
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6805
7180
  SchedulingWidget,
6806
7181
  {
6807
7182
  notifyOnConnect,
@@ -6876,7 +7251,7 @@ function syncNavigationDragCursorAttrs() {
6876
7251
  el.setAttribute("data-ohw-can-drag", "");
6877
7252
  });
6878
7253
  }
6879
- function collectEditableNodes() {
7254
+ function collectEditableNodes(extraContent) {
6880
7255
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6881
7256
  if (el.dataset.ohwEditable === "image") {
6882
7257
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6904,6 +7279,14 @@ function collectEditableNodes() {
6904
7279
  if (!key) return;
6905
7280
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6906
7281
  });
7282
+ if (extraContent) {
7283
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
7284
+ const text = extraContent[key];
7285
+ if (typeof text === "string" && text.length > 0) {
7286
+ nodes.push({ key, type: "meta", text });
7287
+ }
7288
+ }
7289
+ }
6907
7290
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6908
7291
  const key = el.dataset.ohwKey ?? "";
6909
7292
  const video = getVideoEl(el);
@@ -6911,13 +7294,7 @@ function collectEditableNodes() {
6911
7294
  nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
6912
7295
  nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
6913
7296
  });
6914
- const seen = /* @__PURE__ */ new Set();
6915
- return nodes.filter((node) => {
6916
- if (!node.key) return true;
6917
- if (seen.has(node.key)) return false;
6918
- seen.add(node.key);
6919
- return true;
6920
- });
7297
+ return nodes;
6921
7298
  }
6922
7299
  function isMediaEditable(el) {
6923
7300
  const t = el.dataset.ohwEditable;
@@ -7005,6 +7382,29 @@ function getHrefKeyFromElement(el) {
7005
7382
  if (!key) return null;
7006
7383
  return { anchor, key };
7007
7384
  }
7385
+ function disableNativeHrefDrag(el) {
7386
+ if (el.draggable) el.draggable = false;
7387
+ if (el.getAttribute("draggable") !== "false") {
7388
+ el.setAttribute("draggable", "false");
7389
+ }
7390
+ }
7391
+ function clearTextSelection() {
7392
+ const sel = window.getSelection();
7393
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7394
+ }
7395
+ function armFooterPressDrag() {
7396
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7397
+ }
7398
+ function lockFooterDuringDrag() {
7399
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7400
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7401
+ clearTextSelection();
7402
+ }
7403
+ function unlockFooterDragInteraction() {
7404
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7405
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7406
+ clearTextSelection();
7407
+ }
7008
7408
  function isNavbarButton2(el) {
7009
7409
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
7010
7410
  }
@@ -7049,7 +7449,7 @@ function countFooterNavItems(el) {
7049
7449
  return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
7050
7450
  }
7051
7451
  function findFooterItemGroup(item) {
7052
- const explicit = item.closest("[data-ohw-footer-column]");
7452
+ const explicit = item.closest("[data-ohw-footer-col]");
7053
7453
  if (explicit) return explicit;
7054
7454
  const footer = item.closest("footer");
7055
7455
  if (!footer) return null;
@@ -7066,17 +7466,14 @@ function isNavigationRoot(el) {
7066
7466
  function isInferredFooterGroup(el) {
7067
7467
  const footer = el.closest("footer");
7068
7468
  if (!footer || el === footer) return false;
7069
- if (el.hasAttribute("data-ohw-footer-column")) return true;
7469
+ if (el.hasAttribute("data-ohw-footer-col")) return true;
7070
7470
  return countFooterNavItems(el) >= 2;
7071
7471
  }
7072
7472
  function isNavigationContainer(el) {
7073
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
7074
- }
7075
- function isNavbarLinksContainer(el) {
7076
- return el.hasAttribute("data-ohw-nav-container");
7473
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || isNavigationRoot(el) || isInferredFooterGroup(el);
7077
7474
  }
7078
7475
  function getFooterColumn(el) {
7079
- return el.closest("[data-ohw-footer-column]");
7476
+ return el.closest("[data-ohw-footer-col]");
7080
7477
  }
7081
7478
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
7082
7479
  if (getNavigationItemAnchor(target)) return null;
@@ -7100,7 +7497,7 @@ function isPointOverNavigation(x, y) {
7100
7497
  function findHoveredNavOrFooterContainer(x, y) {
7101
7498
  const candidates = [
7102
7499
  ...document.querySelectorAll("[data-ohw-nav-container]"),
7103
- ...document.querySelectorAll("[data-ohw-footer-column]")
7500
+ ...document.querySelectorAll("[data-ohw-footer-col]")
7104
7501
  ];
7105
7502
  for (const container of candidates) {
7106
7503
  const r2 = container.getBoundingClientRect();
@@ -7147,7 +7544,7 @@ function getNavigationSelectionParent(el) {
7147
7544
  if (footerGroup) return footerGroup;
7148
7545
  return getNavigationRoot(el);
7149
7546
  }
7150
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
7547
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(el)) {
7151
7548
  return getNavigationRoot(el);
7152
7549
  }
7153
7550
  return null;
@@ -7314,6 +7711,8 @@ var ICONS = {
7314
7711
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
7315
7712
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
7316
7713
  };
7714
+ var TOOLBAR_PILL_PADDING = 2;
7715
+ var TOOLBAR_TOGGLE_GAP = 4;
7317
7716
  var SELECTION_CHROME_GAP2 = 4;
7318
7717
  var TOOLBAR_STROKE_GAP2 = 4;
7319
7718
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -7341,7 +7740,7 @@ function EditGlowChrome({
7341
7740
  dragDisabled = false
7342
7741
  }) {
7343
7742
  const GAP = SELECTION_CHROME_GAP2;
7344
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7743
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
7345
7744
  "div",
7346
7745
  {
7347
7746
  ref: elRef,
@@ -7356,7 +7755,7 @@ function EditGlowChrome({
7356
7755
  zIndex: 2147483646
7357
7756
  },
7358
7757
  children: [
7359
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7758
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7360
7759
  "div",
7361
7760
  {
7362
7761
  style: {
@@ -7369,7 +7768,7 @@ function EditGlowChrome({
7369
7768
  }
7370
7769
  }
7371
7770
  ),
7372
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7771
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7373
7772
  "div",
7374
7773
  {
7375
7774
  "data-ohw-drag-handle-container": "",
@@ -7381,7 +7780,7 @@ function EditGlowChrome({
7381
7780
  transform: "translate(calc(-100% - 7px), -50%)",
7382
7781
  pointerEvents: dragDisabled ? "none" : "auto"
7383
7782
  },
7384
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7783
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7385
7784
  DragHandle,
7386
7785
  {
7387
7786
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7518,7 +7917,7 @@ function FloatingToolbar({
7518
7917
  return () => ro.disconnect();
7519
7918
  }, [showEditLink, activeCommands]);
7520
7919
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
7521
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7920
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7522
7921
  "div",
7523
7922
  {
7524
7923
  ref: setRefs,
@@ -7528,14 +7927,23 @@ function FloatingToolbar({
7528
7927
  left,
7529
7928
  transform,
7530
7929
  zIndex: 2147483647,
7930
+ background: "#fff",
7931
+ border: "1px solid #E7E5E4",
7932
+ borderRadius: 6,
7933
+ boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
7934
+ display: "flex",
7935
+ alignItems: "center",
7936
+ padding: TOOLBAR_PILL_PADDING,
7937
+ gap: TOOLBAR_TOGGLE_GAP,
7938
+ fontFamily: "sans-serif",
7531
7939
  pointerEvents: "auto"
7532
7940
  },
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, {}),
7941
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CustomToolbar, { children: [
7942
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react8.default.Fragment, { children: [
7943
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CustomToolbarDivider, {}),
7536
7944
  btns.map((btn) => {
7537
7945
  const isActive = activeCommands.has(btn.cmd);
7538
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7946
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7539
7947
  CustomToolbarButton,
7540
7948
  {
7541
7949
  title: btn.title,
@@ -7544,7 +7952,7 @@ function FloatingToolbar({
7544
7952
  e.preventDefault();
7545
7953
  onCommand(btn.cmd);
7546
7954
  },
7547
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7955
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7548
7956
  "svg",
7549
7957
  {
7550
7958
  width: "16",
@@ -7565,7 +7973,7 @@ function FloatingToolbar({
7565
7973
  );
7566
7974
  })
7567
7975
  ] }, gi)),
7568
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7976
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7569
7977
  CustomToolbarButton,
7570
7978
  {
7571
7979
  type: "button",
@@ -7579,7 +7987,7 @@ function FloatingToolbar({
7579
7987
  e.preventDefault();
7580
7988
  e.stopPropagation();
7581
7989
  },
7582
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7990
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7583
7991
  }
7584
7992
  ) : null
7585
7993
  ] })
@@ -7596,7 +8004,7 @@ function StateToggle({
7596
8004
  states,
7597
8005
  onStateChange
7598
8006
  }) {
7599
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8007
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7600
8008
  ToggleGroup,
7601
8009
  {
7602
8010
  "data-ohw-state-toggle": "",
@@ -7610,7 +8018,7 @@ function StateToggle({
7610
8018
  left: rect.right - 8,
7611
8019
  transform: "translateX(-100%)"
7612
8020
  },
7613
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8021
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7614
8022
  }
7615
8023
  );
7616
8024
  }
@@ -7692,6 +8100,7 @@ function OhhwellsBridge() {
7692
8100
  const dragOverElRef = (0, import_react8.useRef)(null);
7693
8101
  const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7694
8102
  const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
8103
+ const pendingUploadRectRef = (0, import_react8.useRef)(null);
7695
8104
  const hoveredGapRef = (0, import_react8.useRef)(null);
7696
8105
  const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7697
8106
  const imageShowTimerRef = (0, import_react8.useRef)(null);
@@ -7734,7 +8143,15 @@ function OhhwellsBridge() {
7734
8143
  const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7735
8144
  const siblingHintElRef = (0, import_react8.useRef)(null);
7736
8145
  const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
8146
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react8.useState)([]);
7737
8147
  const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
8148
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react8.useState)(false);
8149
+ const footerDragRef = (0, import_react8.useRef)(null);
8150
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react8.useState)([]);
8151
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react8.useState)(null);
8152
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react8.useState)(null);
8153
+ const footerPointerDragRef = (0, import_react8.useRef)(null);
8154
+ const suppressNextClickRef = (0, import_react8.useRef)(false);
7738
8155
  const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7739
8156
  const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7740
8157
  const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
@@ -7773,7 +8190,11 @@ function OhhwellsBridge() {
7773
8190
  const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
7774
8191
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7775
8192
  (0, import_react8.useEffect)(() => {
7776
- if (!linkPopover) return;
8193
+ if (!linkPopover) {
8194
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
8195
+ return;
8196
+ }
8197
+ document.documentElement.setAttribute("data-ohw-link-popover-open", "");
7777
8198
  if (hoveredImageRef.current) {
7778
8199
  hoveredImageRef.current = null;
7779
8200
  hoveredImageHasTextOverlapRef.current = false;
@@ -7800,11 +8221,7 @@ function OhhwellsBridge() {
7800
8221
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7801
8222
  postToParent2({ type: "ow:image-unhover" });
7802
8223
  return () => {
7803
- postToParent2({ type: "ow:link-modal-lock", locked: false });
7804
- html.style.overflow = prevHtmlOverflow;
7805
- body.style.overflow = prevBodyOverflow;
7806
- document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
7807
- document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
8224
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
7808
8225
  };
7809
8226
  }, [linkPopover, postToParent2]);
7810
8227
  (0, import_react8.useEffect)(() => {
@@ -7931,8 +8348,10 @@ function OhhwellsBridge() {
7931
8348
  setSelectedIsCta(false);
7932
8349
  setReorderHrefKey(null);
7933
8350
  setReorderDragDisabled(false);
8351
+ setIsFooterFrameSelection(false);
7934
8352
  siblingHintElRef.current = null;
7935
8353
  setSiblingHintRect(null);
8354
+ setSiblingHintRects([]);
7936
8355
  setIsItemDragging(false);
7937
8356
  hoveredNavContainerRef.current = null;
7938
8357
  setHoveredNavContainerRect(null);
@@ -7996,13 +8415,293 @@ function OhhwellsBridge() {
7996
8415
  intent: "add-nav"
7997
8416
  });
7998
8417
  }, []);
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)(() => {
8418
+ const clearFooterDragVisuals = (0, import_react8.useCallback)(() => {
8419
+ footerDragRef.current = null;
8420
+ setSiblingHintRects([]);
8421
+ setFooterDropSlots([]);
8422
+ setActiveFooterDropIndex(null);
8423
+ setDraggedItemRect(null);
8005
8424
  setIsItemDragging(false);
8425
+ unlockFooterDragInteraction();
8426
+ }, []);
8427
+ const refreshFooterDragVisuals = (0, import_react8.useCallback)((session, activeSlot, clientX, clientY) => {
8428
+ const dragged = session.draggedEl;
8429
+ setDraggedItemRect(dragged.getBoundingClientRect());
8430
+ if (typeof clientX === "number" && typeof clientY === "number") {
8431
+ session.lastClientX = clientX;
8432
+ session.lastClientY = clientY;
8433
+ }
8434
+ session.activeSlot = activeSlot;
8435
+ if (session.kind === "link") {
8436
+ const columns2 = listFooterColumns();
8437
+ const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
8438
+ if (activeSlot) focusCols.add(activeSlot.columnIndex);
8439
+ const siblingRects = [];
8440
+ columns2.forEach((col, i) => {
8441
+ if (!focusCols.has(i)) return;
8442
+ for (const link of listFooterLinksInColumn(col)) {
8443
+ if (link === dragged) continue;
8444
+ siblingRects.push(link.getBoundingClientRect());
8445
+ }
8446
+ });
8447
+ setSiblingHintRects(siblingRects);
8448
+ const slots2 = [];
8449
+ columns2.forEach((col, i) => {
8450
+ slots2.push(...buildLinkDropSlots(col, i));
8451
+ });
8452
+ setFooterDropSlots(slots2);
8453
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
8454
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
8455
+ return;
8456
+ }
8457
+ const columns = listFooterColumns();
8458
+ setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
8459
+ const slots = buildColumnDropSlots();
8460
+ setFooterDropSlots(slots);
8461
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
8462
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
8463
+ }, []);
8464
+ const refreshFooterDragVisualsRef = (0, import_react8.useRef)(refreshFooterDragVisuals);
8465
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
8466
+ const commitFooterDragRef = (0, import_react8.useRef)(() => {
8467
+ });
8468
+ const beginFooterDragRef = (0, import_react8.useRef)(() => {
8469
+ });
8470
+ const beginFooterDrag = (0, import_react8.useCallback)(
8471
+ (session) => {
8472
+ const rect = session.draggedEl.getBoundingClientRect();
8473
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
8474
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
8475
+ session.activeSlot = session.activeSlot ?? null;
8476
+ footerDragRef.current = session;
8477
+ setIsItemDragging(true);
8478
+ lockFooterDuringDrag();
8479
+ siblingHintElRef.current = null;
8480
+ setSiblingHintRect(null);
8481
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
8482
+ setToolbarRect(rect);
8483
+ }
8484
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
8485
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
8486
+ },
8487
+ [refreshFooterDragVisuals]
8488
+ );
8489
+ beginFooterDragRef.current = beginFooterDrag;
8490
+ const commitFooterDrag = (0, import_react8.useCallback)(
8491
+ (clientX, clientY) => {
8492
+ const session = footerDragRef.current;
8493
+ if (!session) {
8494
+ clearFooterDragVisuals();
8495
+ return;
8496
+ }
8497
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
8498
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
8499
+ let nextOrder = null;
8500
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
8501
+ if (session.kind === "link" && session.hrefKey && slot) {
8502
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
8503
+ } else if (session.kind === "column" && slot) {
8504
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
8505
+ }
8506
+ const wasSelected = session.wasSelected;
8507
+ const draggedEl = session.draggedEl;
8508
+ const hrefKey = session.hrefKey;
8509
+ let movedColumnIndex = null;
8510
+ if (session.kind === "column" && slot && nextOrder) {
8511
+ let adjusted = slot.insertIndex;
8512
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
8513
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
8514
+ }
8515
+ clearFooterDragVisuals();
8516
+ const applySelectionAfterDrop = () => {
8517
+ if (!wasSelected) {
8518
+ deselectRef.current();
8519
+ return;
8520
+ }
8521
+ if (hrefKey) {
8522
+ const link = document.querySelector(
8523
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
8524
+ );
8525
+ if (link && isNavigationItem(link)) {
8526
+ selectRef.current(link);
8527
+ return;
8528
+ }
8529
+ }
8530
+ if (movedColumnIndex !== null && nextOrder) {
8531
+ const colKeys = nextOrder[movedColumnIndex] ?? [];
8532
+ let column = null;
8533
+ for (const key of colKeys) {
8534
+ const link = document.querySelector(
8535
+ `footer [data-ohw-href-key="${CSS.escape(key)}"]`
8536
+ );
8537
+ if (link) {
8538
+ column = findFooterColumnForLink(link);
8539
+ if (column) break;
8540
+ }
8541
+ }
8542
+ if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
8543
+ if (column && isNavigationContainer(column)) {
8544
+ selectFrameRef.current(column);
8545
+ return;
8546
+ }
8547
+ }
8548
+ if (document.body.contains(draggedEl)) {
8549
+ if (isNavigationItem(draggedEl)) {
8550
+ selectRef.current(draggedEl);
8551
+ return;
8552
+ }
8553
+ if (isNavigationContainer(draggedEl)) {
8554
+ selectFrameRef.current(draggedEl);
8555
+ return;
8556
+ }
8557
+ }
8558
+ deselectRef.current();
8559
+ };
8560
+ if (!wasSelected) {
8561
+ deselectRef.current();
8562
+ }
8563
+ if (nextOrder) {
8564
+ const orderJson = JSON.stringify(nextOrder);
8565
+ editContentRef.current = {
8566
+ ...editContentRef.current,
8567
+ [FOOTER_ORDER_KEY]: orderJson
8568
+ };
8569
+ applyFooterOrder(nextOrder);
8570
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8571
+ postToParentRef.current({
8572
+ type: "ow:change",
8573
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
8574
+ });
8575
+ requestAnimationFrame(() => {
8576
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
8577
+ applyFooterOrder(nextOrder);
8578
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8579
+ }
8580
+ requestAnimationFrame(applySelectionAfterDrop);
8581
+ });
8582
+ return;
8583
+ }
8584
+ applySelectionAfterDrop();
8585
+ },
8586
+ [clearFooterDragVisuals]
8587
+ );
8588
+ commitFooterDragRef.current = commitFooterDrag;
8589
+ const startFooterLinkDrag = (0, import_react8.useCallback)(
8590
+ (anchor, clientX, clientY, wasSelected) => {
8591
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
8592
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
8593
+ const column = findFooterColumnForLink(anchor);
8594
+ const columns = listFooterColumns();
8595
+ beginFooterDrag({
8596
+ kind: "link",
8597
+ hrefKey,
8598
+ columnEl: column,
8599
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
8600
+ wasSelected,
8601
+ draggedEl: anchor,
8602
+ lastClientX: clientX,
8603
+ lastClientY: clientY,
8604
+ activeSlot: null
8605
+ });
8606
+ return true;
8607
+ },
8608
+ [beginFooterDrag]
8609
+ );
8610
+ const startFooterColumnDrag = (0, import_react8.useCallback)(
8611
+ (columnEl, clientX, clientY, wasSelected) => {
8612
+ const columns = listFooterColumns();
8613
+ const idx = columns.indexOf(columnEl);
8614
+ if (idx < 0) return false;
8615
+ beginFooterDrag({
8616
+ kind: "column",
8617
+ hrefKey: null,
8618
+ columnEl,
8619
+ sourceColumnIndex: idx,
8620
+ wasSelected,
8621
+ draggedEl: columnEl,
8622
+ lastClientX: clientX,
8623
+ lastClientY: clientY,
8624
+ activeSlot: null
8625
+ });
8626
+ return true;
8627
+ },
8628
+ [beginFooterDrag]
8629
+ );
8630
+ const handleItemDragStart = (0, import_react8.useCallback)(
8631
+ (e) => {
8632
+ const selected = selectedElRef.current;
8633
+ if (!selected) {
8634
+ setIsItemDragging(true);
8635
+ return;
8636
+ }
8637
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
8638
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
8639
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
8640
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8641
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
8642
+ }
8643
+ siblingHintElRef.current = null;
8644
+ setSiblingHintRect(null);
8645
+ setIsItemDragging(true);
8646
+ },
8647
+ [startFooterColumnDrag, startFooterLinkDrag]
8648
+ );
8649
+ const handleItemDragEnd = (0, import_react8.useCallback)(
8650
+ (e) => {
8651
+ if (footerDragRef.current) {
8652
+ const x = e?.clientX;
8653
+ const y = e?.clientY;
8654
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
8655
+ commitFooterDrag(x, y);
8656
+ } else {
8657
+ commitFooterDrag();
8658
+ }
8659
+ return;
8660
+ }
8661
+ setIsItemDragging(false);
8662
+ },
8663
+ [commitFooterDrag]
8664
+ );
8665
+ const handleItemChromePointerDown = (0, import_react8.useCallback)((e) => {
8666
+ if (e.button !== 0) return;
8667
+ const selected = selectedElRef.current;
8668
+ if (!selected) return;
8669
+ armFooterPressDrag();
8670
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
8671
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
8672
+ footerPointerDragRef.current = {
8673
+ el: selected,
8674
+ kind: "link",
8675
+ startX: e.clientX,
8676
+ startY: e.clientY,
8677
+ pointerId: e.pointerId,
8678
+ wasSelected: true,
8679
+ started: false
8680
+ };
8681
+ return;
8682
+ }
8683
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8684
+ footerPointerDragRef.current = {
8685
+ el: selected,
8686
+ kind: "column",
8687
+ startX: e.clientX,
8688
+ startY: e.clientY,
8689
+ pointerId: e.pointerId,
8690
+ wasSelected: true,
8691
+ started: false
8692
+ };
8693
+ }
8694
+ }, []);
8695
+ const handleItemChromeClick = (0, import_react8.useCallback)((clientX, clientY) => {
8696
+ if (suppressNextClickRef.current) {
8697
+ suppressNextClickRef.current = false;
8698
+ return;
8699
+ }
8700
+ const selected = selectedElRef.current;
8701
+ if (!selected || !isNavigationItem(selected)) return;
8702
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
8703
+ if (!editable) return;
8704
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
8006
8705
  }, []);
8007
8706
  reselectNavigationItemRef.current = reselectNavigationItem;
8008
8707
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
@@ -8019,10 +8718,12 @@ function OhhwellsBridge() {
8019
8718
  hoveredItemElRef.current = null;
8020
8719
  siblingHintElRef.current = null;
8021
8720
  setSiblingHintRect(null);
8721
+ setSiblingHintRects([]);
8022
8722
  setIsItemDragging(false);
8023
8723
  const { key, disabled } = getNavigationItemReorderState(anchor);
8024
8724
  setReorderHrefKey(key);
8025
8725
  setReorderDragDisabled(disabled);
8726
+ setIsFooterFrameSelection(false);
8026
8727
  setToolbarVariant("link-action");
8027
8728
  setToolbarRect(anchor.getBoundingClientRect());
8028
8729
  setToolbarShowEditLink(false);
@@ -8031,6 +8732,7 @@ function OhhwellsBridge() {
8031
8732
  const selectFrame = (0, import_react8.useCallback)((el) => {
8032
8733
  if (!isNavigationContainer(el)) return;
8033
8734
  if (activeElRef.current) deactivate();
8735
+ const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8034
8736
  selectedElRef.current = el;
8035
8737
  markSelected(el);
8036
8738
  setSelectedIsCta(false);
@@ -8041,9 +8743,13 @@ function OhhwellsBridge() {
8041
8743
  hoveredItemElRef.current = null;
8042
8744
  siblingHintElRef.current = null;
8043
8745
  setSiblingHintRect(null);
8746
+ setSiblingHintRects(
8747
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
8748
+ );
8044
8749
  setIsItemDragging(false);
8045
8750
  setReorderHrefKey(null);
8046
8751
  setReorderDragDisabled(false);
8752
+ setIsFooterFrameSelection(isFooterColumn);
8047
8753
  setToolbarVariant("select-frame");
8048
8754
  setToolbarRect(el.getBoundingClientRect());
8049
8755
  setToolbarShowEditLink(false);
@@ -8058,11 +8764,14 @@ function OhhwellsBridge() {
8058
8764
  if (hoveredImageRef.current) {
8059
8765
  hoveredImageRef.current = null;
8060
8766
  setMediaHover(null);
8767
+ postToParentRef.current({ type: "ow:image-unhover" });
8061
8768
  }
8062
8769
  setToolbarVariant("rich-text");
8063
8770
  siblingHintElRef.current = null;
8064
8771
  setSiblingHintRect(null);
8772
+ setSiblingHintRects([]);
8065
8773
  setIsItemDragging(false);
8774
+ setIsFooterFrameSelection(false);
8066
8775
  el.setAttribute("contenteditable", "true");
8067
8776
  el.setAttribute("data-ohw-editing", "");
8068
8777
  el.removeAttribute("data-ohw-hovered");
@@ -8142,6 +8851,7 @@ function OhhwellsBridge() {
8142
8851
  applyLinkByKey(key, val);
8143
8852
  }
8144
8853
  reconcileNavbarItemsFromContent(content);
8854
+ reconcileFooterOrderFromContent(content);
8145
8855
  enforceLinkHrefs();
8146
8856
  initSectionsFromContent(content, true);
8147
8857
  sectionsLoadedRef.current = true;
@@ -8202,6 +8912,7 @@ function OhhwellsBridge() {
8202
8912
  applyLinkByKey(key, val);
8203
8913
  }
8204
8914
  reconcileNavbarItemsFromContent(content);
8915
+ reconcileFooterOrderFromContent(content);
8205
8916
  } finally {
8206
8917
  observer?.observe(document.body, { childList: true, subtree: true });
8207
8918
  }
@@ -8245,12 +8956,6 @@ function OhhwellsBridge() {
8245
8956
  const observeRoots = () => [document.querySelector("nav"), document.querySelector("footer")].filter(
8246
8957
  (el) => Boolean(el)
8247
8958
  );
8248
- const roots = observeRoots();
8249
- if (roots.length === 0) {
8250
- reconcileNavbarItemsFromContent(contentForNav());
8251
- if (isEditMode) syncNavigationDragCursorAttrs();
8252
- return;
8253
- }
8254
8959
  let rafId = null;
8255
8960
  let applying = false;
8256
8961
  let observer = null;
@@ -8260,11 +8965,18 @@ function OhhwellsBridge() {
8260
8965
  }
8261
8966
  };
8262
8967
  const run = () => {
8263
- if (applying) return;
8968
+ if (footerDragRef.current || applying) return;
8264
8969
  applying = true;
8265
8970
  observer?.disconnect();
8266
8971
  try {
8267
- reconcileNavbarItemsFromContent(contentForNav());
8972
+ const content = contentForNav();
8973
+ reconcileNavbarItemsFromContent(content);
8974
+ reconcileFooterOrderFromContent(content);
8975
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
8976
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
8977
+ disableNativeHrefDrag(el);
8978
+ }
8979
+ });
8268
8980
  if (isEditMode) syncNavigationDragCursorAttrs();
8269
8981
  } finally {
8270
8982
  applying = false;
@@ -8372,6 +9084,39 @@ function OhhwellsBridge() {
8372
9084
  [data-ohw-href-key][data-ohw-selected] * {
8373
9085
  cursor: text !important;
8374
9086
  }
9087
+ /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
9088
+ footer [data-ohw-href-key] {
9089
+ -webkit-user-drag: none !important;
9090
+ }
9091
+ footer [data-ohw-footer-col]:hover {
9092
+ outline: 1.5px dashed ${PRIMARY2} !important;
9093
+ outline-offset: 6px;
9094
+ border-radius: 8px;
9095
+ }
9096
+ html[data-ohw-link-popover-open] footer [data-ohw-footer-col]:hover {
9097
+ outline: none !important;
9098
+ }
9099
+ html[data-ohw-footer-press-drag] footer,
9100
+ html[data-ohw-footer-press-drag] footer * {
9101
+ user-select: none !important;
9102
+ -webkit-user-select: none !important;
9103
+ }
9104
+ html[data-ohw-item-dragging] footer,
9105
+ html[data-ohw-item-dragging] footer * {
9106
+ user-select: none !important;
9107
+ -webkit-user-select: none !important;
9108
+ pointer-events: none !important;
9109
+ }
9110
+ html[data-ohw-item-dragging] {
9111
+ cursor: grabbing !important;
9112
+ }
9113
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not(nav [data-ohw-href-key] *):not(footer [data-ohw-href-key] *) { cursor: text !important; }
9114
+ nav [data-ohw-href-key],
9115
+ nav [data-ohw-href-key] [data-ohw-editable]:not([contenteditable]),
9116
+ footer [data-ohw-href-key],
9117
+ footer [data-ohw-href-key] [data-ohw-editable]:not([contenteditable]) {
9118
+ cursor: grab !important;
9119
+ }
8375
9120
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8376
9121
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
8377
9122
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -8428,12 +9173,39 @@ function OhhwellsBridge() {
8428
9173
  syncNavigationDragCursorAttrs();
8429
9174
  refreshStateRules();
8430
9175
  const handleClick = (e) => {
9176
+ if (suppressNextClickRef.current) {
9177
+ suppressNextClickRef.current = false;
9178
+ e.preventDefault();
9179
+ e.stopPropagation();
9180
+ return;
9181
+ }
8431
9182
  const target = e.target;
8432
9183
  if (target.closest("[data-ohw-toolbar]")) return;
8433
9184
  if (target.closest("[data-ohw-state-toggle]")) return;
8434
9185
  if (target.closest("[data-ohw-max-badge]")) return;
8435
9186
  if (isInsideLinkEditor(target)) return;
8436
9187
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9188
+ if (target.closest("[data-ohw-item-drag-surface]")) {
9189
+ e.preventDefault();
9190
+ e.stopPropagation();
9191
+ const selected = selectedElRef.current;
9192
+ if (selected && isNavigationItem(selected)) {
9193
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9194
+ if (editable2) {
9195
+ activateRef.current(editable2, {
9196
+ caretX: e.clientX,
9197
+ caretY: e.clientY
9198
+ });
9199
+ }
9200
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
9201
+ const link = listFooterLinksInColumn(selected).find((el) => {
9202
+ const r2 = el.getBoundingClientRect();
9203
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
9204
+ });
9205
+ if (link) selectRef.current(link);
9206
+ }
9207
+ return;
9208
+ }
8437
9209
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8438
9210
  if (navFromChrome) {
8439
9211
  e.preventDefault();
@@ -8487,7 +9259,15 @@ function OhhwellsBridge() {
8487
9259
  if (isMediaEditable(editable)) {
8488
9260
  e.preventDefault();
8489
9261
  e.stopPropagation();
8490
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
9262
+ const key = editable.dataset.ohwKey ?? "";
9263
+ const r2 = editable.getBoundingClientRect();
9264
+ if (key && r2.width > 1 && r2.height > 1) {
9265
+ pendingUploadRectRef.current = {
9266
+ key,
9267
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height }
9268
+ };
9269
+ }
9270
+ postToParentRef.current({ type: "ow:image-pick", key, elementType: editable.dataset.ohwEditable ?? "image" });
8491
9271
  return;
8492
9272
  }
8493
9273
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -8512,10 +9292,26 @@ function OhhwellsBridge() {
8512
9292
  if (hrefAnchor) {
8513
9293
  e.preventDefault();
8514
9294
  e.stopPropagation();
8515
- if (selectedElRef.current === hrefAnchor) return;
9295
+ if (selectedElRef.current === hrefAnchor) {
9296
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
9297
+ if (textEditable) {
9298
+ activateRef.current(textEditable, {
9299
+ caretX: e.clientX,
9300
+ caretY: e.clientY
9301
+ });
9302
+ }
9303
+ return;
9304
+ }
8516
9305
  selectRef.current(hrefAnchor);
8517
9306
  return;
8518
9307
  }
9308
+ const footerColClick = target.closest("[data-ohw-footer-col]");
9309
+ if (footerColClick) {
9310
+ e.preventDefault();
9311
+ e.stopPropagation();
9312
+ selectFrameRef.current(footerColClick);
9313
+ return;
9314
+ }
8519
9315
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8520
9316
  if (navContainerToSelect) {
8521
9317
  e.preventDefault();
@@ -8589,8 +9385,15 @@ function OhhwellsBridge() {
8589
9385
  const handleMouseOver = (e) => {
8590
9386
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8591
9387
  const target = e.target;
9388
+ if (linkPopoverOpenRef.current) {
9389
+ hoveredItemElRef.current = null;
9390
+ setHoveredItemRect(null);
9391
+ hoveredNavContainerRef.current = null;
9392
+ setHoveredNavContainerRect(null);
9393
+ return;
9394
+ }
8592
9395
  if (toolbarVariantRef.current !== "select-frame") {
8593
- const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-column]");
9396
+ const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-col]");
8594
9397
  if (frameContainer && !getNavigationItemAnchor(target)) {
8595
9398
  hoveredNavContainerRef.current = frameContainer;
8596
9399
  setHoveredNavContainerRect(frameContainer.getBoundingClientRect());
@@ -8598,11 +9401,20 @@ function OhhwellsBridge() {
8598
9401
  setHoveredItemRect(null);
8599
9402
  return;
8600
9403
  }
8601
- if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
9404
+ if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-col]")) {
8602
9405
  hoveredNavContainerRef.current = null;
8603
9406
  setHoveredNavContainerRect(null);
8604
9407
  }
8605
9408
  }
9409
+ const footerCol = target.closest("[data-ohw-footer-col]");
9410
+ if (footerCol) {
9411
+ hoveredNavContainerRef.current = null;
9412
+ setHoveredNavContainerRect(null);
9413
+ if (selectedElRef.current === footerCol) return;
9414
+ hoveredItemElRef.current = footerCol;
9415
+ setHoveredItemRect(footerCol.getBoundingClientRect());
9416
+ return;
9417
+ }
8606
9418
  const navAnchor = getNavigationItemAnchor(target);
8607
9419
  if (navAnchor) {
8608
9420
  hoveredNavContainerRef.current = null;
@@ -8631,14 +9443,23 @@ function OhhwellsBridge() {
8631
9443
  };
8632
9444
  const handleMouseOut = (e) => {
8633
9445
  const target = e.target;
8634
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
9446
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col]")) {
8635
9447
  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]")) {
9448
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8637
9449
  return;
8638
9450
  }
8639
9451
  hoveredNavContainerRef.current = null;
8640
9452
  setHoveredNavContainerRect(null);
8641
9453
  }
9454
+ const footerCol = target.closest("[data-ohw-footer-col]");
9455
+ if (footerCol && hoveredItemElRef.current === footerCol) {
9456
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9457
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
9458
+ if (related2?.closest("[data-ohw-footer-col]") === footerCol) return;
9459
+ hoveredItemElRef.current = null;
9460
+ setHoveredItemRect(null);
9461
+ return;
9462
+ }
8642
9463
  const navAnchor = getNavigationItemAnchor(target);
8643
9464
  if (navAnchor) {
8644
9465
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8750,12 +9571,12 @@ function OhhwellsBridge() {
8750
9571
  const stickyPad = 48;
8751
9572
  const withinPad = x >= pr.left - stickyPad && x <= pr.left + pr.width + stickyPad && y >= pr.top - stickyPad && y <= pr.top + pr.height + stickyPad;
8752
9573
  if (withinPad) {
8753
- if (smallest !== pinned && candidatePool.includes(smallest)) {
9574
+ const yieldToSmaller = smallest !== pinned && candidatePool.includes(smallest) && (() => {
8754
9575
  const sr = getVisibleRect(smallest);
8755
- const overOther = x >= sr.left && x <= sr.left + sr.width && y >= sr.top && y <= sr.top + sr.height;
8756
- if (overOther) return smallest;
8757
- }
8758
- return pinned;
9576
+ const pr2 = getVisibleRect(pinned);
9577
+ return sr.width * sr.height < pr2.width * pr2.height;
9578
+ })();
9579
+ if (!yieldToSmaller) return pinned;
8759
9580
  }
8760
9581
  }
8761
9582
  return smallest;
@@ -8767,6 +9588,7 @@ function OhhwellsBridge() {
8767
9588
  resumeAnimTracks();
8768
9589
  postToParentRef.current({ type: "ow:image-unhover" });
8769
9590
  }
9591
+ clearImageHover();
8770
9592
  };
8771
9593
  const probeNavigationHoverAt = (x, y) => {
8772
9594
  if (toolbarVariantRef.current === "select-frame") {
@@ -8776,7 +9598,7 @@ function OhhwellsBridge() {
8776
9598
  }
8777
9599
  const frameContainers = [
8778
9600
  ...document.querySelectorAll("[data-ohw-nav-container]"),
8779
- ...document.querySelectorAll("[data-ohw-footer-column]")
9601
+ ...document.querySelectorAll("[data-ohw-footer-col]")
8780
9602
  ];
8781
9603
  let overFrame = null;
8782
9604
  for (const container of frameContainers) {
@@ -8873,7 +9695,7 @@ function OhhwellsBridge() {
8873
9695
  return;
8874
9696
  }
8875
9697
  const topEl = document.elementFromPoint(x2, y2);
8876
- if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9698
+ if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-media-chrome]')) {
8877
9699
  if (hoveredImageRef.current) {
8878
9700
  hoveredImageRef.current = null;
8879
9701
  resumeAnimTracks();
@@ -9080,6 +9902,14 @@ function OhhwellsBridge() {
9080
9902
  probeHoverCardsAt(clientX, clientY);
9081
9903
  };
9082
9904
  const handleDragOver = (e) => {
9905
+ const footerSession = footerDragRef.current;
9906
+ if (footerSession) {
9907
+ e.preventDefault();
9908
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
9909
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
9910
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
9911
+ return;
9912
+ }
9083
9913
  e.preventDefault();
9084
9914
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9085
9915
  if (!el) {
@@ -9148,6 +9978,7 @@ function OhhwellsBridge() {
9148
9978
  if (!entry || entry.fadingOut) return prev;
9149
9979
  return { ...prev, [key]: { ...entry, fadingOut: true } };
9150
9980
  });
9981
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
9151
9982
  };
9152
9983
  let found = false;
9153
9984
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -9287,6 +10118,7 @@ function OhhwellsBridge() {
9287
10118
  editContentRef.current = { ...editContentRef.current, ...content };
9288
10119
  reconcileNavbarItemsFromContent(editContentRef.current);
9289
10120
  syncNavigationDragCursorAttrs();
10121
+ reconcileFooterOrderFromContent(editContentRef.current);
9290
10122
  enforceLinkHrefs();
9291
10123
  postToParentRef.current({ type: "ow:hydrate-done" });
9292
10124
  };
@@ -9443,6 +10275,17 @@ function OhhwellsBridge() {
9443
10275
  if (siblingHintElRef.current) {
9444
10276
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9445
10277
  }
10278
+ const selected = selectedElRef.current;
10279
+ if (selected && !footerDragRef.current && (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)))) {
10280
+ setSiblingHintRects(
10281
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
10282
+ );
10283
+ }
10284
+ if (footerDragRef.current) {
10285
+ const session = footerDragRef.current;
10286
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
10287
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
10288
+ }
9446
10289
  if (hoveredImageRef.current) {
9447
10290
  const el = hoveredImageRef.current;
9448
10291
  const r2 = el.getBoundingClientRect();
@@ -9453,7 +10296,7 @@ function OhhwellsBridge() {
9453
10296
  };
9454
10297
  const handleSave = (e) => {
9455
10298
  if (e.data?.type !== "ow:save") return;
9456
- const nodes = collectEditableNodes();
10299
+ const nodes = collectEditableNodes(editContentRef.current);
9457
10300
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9458
10301
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9459
10302
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9543,23 +10386,43 @@ function OhhwellsBridge() {
9543
10386
  activeStateElRef.current = null;
9544
10387
  setToggleState(null);
9545
10388
  };
10389
+ const resolveUploadRects = (key) => {
10390
+ const matches = Array.from(document.querySelectorAll(`[data-ohw-key="${key}"]`));
10391
+ const rects = matches.map((el) => {
10392
+ const r2 = el.getBoundingClientRect();
10393
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
10394
+ }).filter((r2) => r2.width > 1 && r2.height > 1);
10395
+ if (rects.length > 0) return rects;
10396
+ const pending = pendingUploadRectRef.current;
10397
+ if (pending?.key === key && pending.rect.width > 1 && pending.rect.height > 1) {
10398
+ return [pending.rect];
10399
+ }
10400
+ const hovered = hoveredImageRef.current;
10401
+ if (hovered?.dataset.ohwKey === key) {
10402
+ const r2 = hovered.getBoundingClientRect();
10403
+ if (r2.width > 1 && r2.height > 1) {
10404
+ return [{ top: r2.top, left: r2.left, width: r2.width, height: r2.height }];
10405
+ }
10406
+ }
10407
+ return [];
10408
+ };
9546
10409
  const handleImageUploading = (e) => {
9547
- if (e.data?.type !== "ow:image-uploading") return;
9548
- const { key, uploading } = e.data;
10410
+ const type = e.data?.type;
10411
+ if (type !== "ow:image-uploading" && type !== "ow:anim-lock") return;
10412
+ const key = e.data.key;
10413
+ if (!key) return;
10414
+ const uploading = type === "ow:anim-lock" ? true : !!e.data.uploading;
9549
10415
  setUploadingRects((prev) => {
9550
10416
  if (!uploading) {
10417
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
9551
10418
  if (!(key in prev)) return prev;
9552
10419
  const next = { ...prev };
9553
10420
  delete next[key];
9554
10421
  return next;
9555
10422
  }
9556
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
9557
- if (!el) return prev;
9558
- const r2 = getVisibleRect(el);
9559
- return {
9560
- ...prev,
9561
- [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
9562
- };
10423
+ const rects = resolveUploadRects(key);
10424
+ if (rects.length === 0) return prev;
10425
+ return { ...prev, [key]: { rects } };
9563
10426
  });
9564
10427
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9565
10428
  const track = el.closest("[data-ohw-hover-pause]");
@@ -9676,7 +10539,7 @@ function OhhwellsBridge() {
9676
10539
  }
9677
10540
  }
9678
10541
  const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
9679
- if (footerColumn?.hasAttribute("data-ohw-footer-column")) {
10542
+ if (footerColumn?.hasAttribute("data-ohw-footer-col")) {
9680
10543
  selectFrameRef.current(footerColumn);
9681
10544
  return;
9682
10545
  }
@@ -9751,6 +10614,157 @@ function OhhwellsBridge() {
9751
10614
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9752
10615
  };
9753
10616
  }, [isEditMode, refreshStateRules]);
10617
+ (0, import_react8.useEffect)(() => {
10618
+ if (!isEditMode) return;
10619
+ const THRESHOLD = 10;
10620
+ const resolveWasSelected = (el) => {
10621
+ if (selectedElRef.current === el) return true;
10622
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
10623
+ return activeAnchor === el;
10624
+ };
10625
+ const onPointerDown = (e) => {
10626
+ if (e.button !== 0) return;
10627
+ if (suppressNextClickRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
10628
+ suppressNextClickRef.current = false;
10629
+ }
10630
+ if (footerDragRef.current) return;
10631
+ const target = e.target;
10632
+ if (!target) return;
10633
+ 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]')) {
10634
+ return;
10635
+ }
10636
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
10637
+ const anchor = getNavigationItemAnchor(target);
10638
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
10639
+ if (anchor && isFooterHrefKey(hrefKey)) {
10640
+ armFooterPressDrag();
10641
+ footerPointerDragRef.current = {
10642
+ el: anchor,
10643
+ kind: "link",
10644
+ startX: e.clientX,
10645
+ startY: e.clientY,
10646
+ pointerId: e.pointerId,
10647
+ wasSelected: resolveWasSelected(anchor),
10648
+ started: false
10649
+ };
10650
+ return;
10651
+ }
10652
+ const col = target.closest("[data-ohw-footer-col]");
10653
+ if (col && !getNavigationItemAnchor(target)) {
10654
+ armFooterPressDrag();
10655
+ footerPointerDragRef.current = {
10656
+ el: col,
10657
+ kind: "column",
10658
+ startX: e.clientX,
10659
+ startY: e.clientY,
10660
+ pointerId: e.pointerId,
10661
+ wasSelected: resolveWasSelected(col),
10662
+ started: false
10663
+ };
10664
+ }
10665
+ };
10666
+ const onPointerMove = (e) => {
10667
+ const pending = footerPointerDragRef.current;
10668
+ if (!pending) return;
10669
+ if (pending.started) {
10670
+ e.preventDefault();
10671
+ clearTextSelection();
10672
+ const session = footerDragRef.current;
10673
+ if (!session) return;
10674
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10675
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
10676
+ return;
10677
+ }
10678
+ const dx = e.clientX - pending.startX;
10679
+ const dy = e.clientY - pending.startY;
10680
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
10681
+ e.preventDefault();
10682
+ pending.started = true;
10683
+ clearTextSelection();
10684
+ try {
10685
+ document.body.setPointerCapture(pending.pointerId);
10686
+ } catch {
10687
+ }
10688
+ if (linkPopoverOpenRef.current) {
10689
+ setLinkPopoverRef.current(null);
10690
+ }
10691
+ if (activeElRef.current) {
10692
+ deactivateRef.current();
10693
+ }
10694
+ if (pending.kind === "link") {
10695
+ const key = pending.el.getAttribute("data-ohw-href-key");
10696
+ if (!key) return;
10697
+ const column = findFooterColumnForLink(pending.el);
10698
+ const columns2 = listFooterColumns();
10699
+ beginFooterDragRef.current({
10700
+ kind: "link",
10701
+ hrefKey: key,
10702
+ columnEl: column,
10703
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
10704
+ wasSelected: pending.wasSelected,
10705
+ draggedEl: pending.el,
10706
+ lastClientX: e.clientX,
10707
+ lastClientY: e.clientY,
10708
+ activeSlot: null
10709
+ });
10710
+ return;
10711
+ }
10712
+ const columns = listFooterColumns();
10713
+ const idx = columns.indexOf(pending.el);
10714
+ if (idx < 0) return;
10715
+ beginFooterDragRef.current({
10716
+ kind: "column",
10717
+ hrefKey: null,
10718
+ columnEl: pending.el,
10719
+ sourceColumnIndex: idx,
10720
+ wasSelected: pending.wasSelected,
10721
+ draggedEl: pending.el,
10722
+ lastClientX: e.clientX,
10723
+ lastClientY: e.clientY,
10724
+ activeSlot: null
10725
+ });
10726
+ };
10727
+ const endPointerDrag = (e) => {
10728
+ const pending = footerPointerDragRef.current;
10729
+ footerPointerDragRef.current = null;
10730
+ try {
10731
+ if (document.body.hasPointerCapture(e.pointerId)) {
10732
+ document.body.releasePointerCapture(e.pointerId);
10733
+ }
10734
+ } catch {
10735
+ }
10736
+ if (!pending?.started) {
10737
+ unlockFooterDragInteraction();
10738
+ return;
10739
+ }
10740
+ suppressNextClickRef.current = true;
10741
+ commitFooterDragRef.current(e.clientX, e.clientY);
10742
+ };
10743
+ const blockSelectStart = (e) => {
10744
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
10745
+ e.preventDefault();
10746
+ }
10747
+ };
10748
+ const onDragEnd = (_e) => {
10749
+ if (!footerDragRef.current) return;
10750
+ commitFooterDragRef.current();
10751
+ };
10752
+ document.addEventListener("pointerdown", onPointerDown, true);
10753
+ document.addEventListener("pointermove", onPointerMove, true);
10754
+ document.addEventListener("pointerup", endPointerDrag, true);
10755
+ document.addEventListener("pointercancel", endPointerDrag, true);
10756
+ document.addEventListener("selectstart", blockSelectStart, true);
10757
+ document.addEventListener("dragend", onDragEnd, true);
10758
+ return () => {
10759
+ document.removeEventListener("pointerdown", onPointerDown, true);
10760
+ document.removeEventListener("pointermove", onPointerMove, true);
10761
+ document.removeEventListener("pointerup", endPointerDrag, true);
10762
+ document.removeEventListener("pointercancel", endPointerDrag, true);
10763
+ document.removeEventListener("selectstart", blockSelectStart, true);
10764
+ document.removeEventListener("dragend", onDragEnd, true);
10765
+ unlockFooterDragInteraction();
10766
+ };
10767
+ }, [isEditMode]);
9754
10768
  (0, import_react8.useEffect)(() => {
9755
10769
  const handler = (e) => {
9756
10770
  if (e.data?.type !== "ow:request-schedule-config") return;
@@ -9788,7 +10802,13 @@ function OhhwellsBridge() {
9788
10802
  const next = { ...prev, [pathKey]: sections };
9789
10803
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9790
10804
  });
9791
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
10805
+ postToParent2({
10806
+ type: "ow:ready",
10807
+ version: "1",
10808
+ path: pathname,
10809
+ nodes: collectEditableNodes(editContentRef.current),
10810
+ sections
10811
+ });
9792
10812
  postToParent2({ type: "ow:request-site-pages" });
9793
10813
  }, 150);
9794
10814
  return () => {
@@ -9905,14 +10925,14 @@ function OhhwellsBridge() {
9905
10925
  },
9906
10926
  [postToParent2, sitePages, sectionsByPath]
9907
10927
  );
9908
- const showEditLink = toolbarShowEditLink;
9909
- const currentSections = sectionsByPath[pathname] ?? [];
9910
- linkPopoverOpenRef.current = linkPopover !== null;
9911
10928
  const handleMediaReplace = (0, import_react8.useCallback)(
9912
10929
  (key) => {
10930
+ if (mediaHover?.key === key) {
10931
+ pendingUploadRectRef.current = { key, rect: mediaHover.rect };
10932
+ }
9913
10933
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9914
10934
  },
9915
- [postToParent2, mediaHover?.elementType]
10935
+ [postToParent2, mediaHover]
9916
10936
  );
9917
10937
  const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
9918
10938
  setUploadingRects((prev) => {
@@ -9944,21 +10964,26 @@ function OhhwellsBridge() {
9944
10964
  },
9945
10965
  [postToParent2]
9946
10966
  );
10967
+ const showEditLink = toolbarShowEditLink;
10968
+ const currentSections = sectionsByPath[pathname] ?? [];
10969
+ linkPopoverOpenRef.current = linkPopover !== null;
9947
10970
  return bridgeRoot ? (0, import_react_dom3.createPortal)(
9948
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9949
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9950
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9951
- MediaOverlay,
9952
- {
9953
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
9954
- isUploading: true,
9955
- fadingOut,
9956
- onFadeOutComplete: handleMediaFadeOutComplete,
9957
- onReplace: handleMediaReplace
9958
- },
9959
- `uploading-${key}`
9960
- )),
9961
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10971
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
10972
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
10973
+ Object.entries(uploadingRects).flatMap(
10974
+ ([key, { rects, fadingOut }]) => rects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10975
+ MediaOverlay,
10976
+ {
10977
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
10978
+ isUploading: true,
10979
+ fadingOut,
10980
+ onFadeOutComplete: handleMediaFadeOutComplete,
10981
+ onReplace: handleMediaReplace
10982
+ },
10983
+ `uploading-${key}-${i}`
10984
+ ))
10985
+ ),
10986
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9962
10987
  MediaOverlay,
9963
10988
  {
9964
10989
  hover: mediaHover,
@@ -9967,28 +10992,40 @@ function OhhwellsBridge() {
9967
10992
  onVideoSettingsChange: handleVideoSettingsChange
9968
10993
  }
9969
10994
  ),
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,
10995
+ siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
10996
+ siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
10997
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
10998
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
10999
+ "div",
9974
11000
  {
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)(
11001
+ className: "pointer-events-none fixed z-2147483646",
11002
+ style: {
11003
+ left: slot.left,
11004
+ top: slot.top,
11005
+ width: slot.width,
11006
+ height: slot.height
11007
+ },
11008
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DropIndicator, { direction: slot.direction, state: activeFooterDropIndex === i ? "dragActive" : "dragIdle", className: slot.direction === "horizontal" ? "!h-full !w-full" : "!h-full !w-full" })
11009
+ },
11010
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
11011
+ )),
11012
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
11013
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
11014
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
11015
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9981
11016
  ItemInteractionLayer,
9982
11017
  {
9983
- rect: toolbarRect,
11018
+ rect: isItemDragging && draggedItemRect && footerDragRef.current?.wasSelected ? draggedItemRect : toolbarRect,
9984
11019
  elRef: glowElRef,
9985
11020
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9986
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
11021
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
9987
11022
  dragDisabled: reorderDragDisabled,
9988
11023
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9989
11024
  onDragHandleDragStart: handleItemDragStart,
9990
11025
  onDragHandleDragEnd: handleItemDragEnd,
9991
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11026
+ onItemPointerDown: handleItemChromePointerDown,
11027
+ onItemClick: handleItemChromeClick,
11028
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9992
11029
  ItemActionToolbar,
9993
11030
  {
9994
11031
  onEditLink: openLinkPopoverForSelected,
@@ -10001,30 +11038,11 @@ function OhhwellsBridge() {
10001
11038
  ) : void 0
10002
11039
  }
10003
11040
  ),
10004
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
10005
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10006
- EditGlowChrome,
10007
- {
10008
- rect: toolbarRect,
10009
- elRef: glowElRef,
10010
- reorderHrefKey,
10011
- dragDisabled: reorderDragDisabled
10012
- }
10013
- ),
10014
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10015
- FloatingToolbar,
10016
- {
10017
- rect: toolbarRect,
10018
- parentScroll: parentScrollRef.current,
10019
- elRef: toolbarElRef,
10020
- onCommand: handleCommand,
10021
- activeCommands,
10022
- showEditLink,
10023
- onEditLink: openLinkPopoverForActive
10024
- }
10025
- )
11041
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11042
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(EditGlowChrome, { rect: toolbarRect, elRef: glowElRef, reorderHrefKey, dragDisabled: reorderDragDisabled }),
11043
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands, showEditLink, onEditLink: openLinkPopoverForActive })
10026
11044
  ] }),
10027
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
11045
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
10028
11046
  "div",
10029
11047
  {
10030
11048
  "data-ohw-max-badge": "",
@@ -10050,103 +11068,33 @@ function OhhwellsBridge() {
10050
11068
  ]
10051
11069
  }
10052
11070
  ),
10053
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10054
- StateToggle,
10055
- {
10056
- rect: toggleState.rect,
10057
- activeState: toggleState.activeState,
10058
- states: toggleState.states,
10059
- onStateChange: handleStateChange
10060
- }
10061
- ),
10062
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
10063
- "div",
10064
- {
10065
- "data-ohw-section-insert-line": "",
10066
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
10067
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
10068
- children: [
10069
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10070
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10071
- Badge,
10072
- {
10073
- 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
- onClick: () => {
10075
- window.parent.postMessage(
10076
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
10077
- "*"
10078
- );
11071
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(StateToggle, { rect: toggleState.rect, activeState: toggleState.activeState, states: toggleState.states, onStateChange: handleStateChange }),
11072
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { "data-ohw-section-insert-line": "", className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none", style: { top: sectionGap.y, transform: "translateY(-50%)" }, children: [
11073
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
11074
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
11075
+ Badge,
11076
+ {
11077
+ className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
11078
+ onClick: () => {
11079
+ window.parent.postMessage(
11080
+ {
11081
+ type: "ow:add-section",
11082
+ insertAfter: sectionGap.insertAfter,
11083
+ insertBefore: sectionGap.insertBefore
10079
11084
  },
10080
- children: "Add Section"
10081
- }
10082
- ),
10083
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10084
- ]
10085
- }
10086
- ),
10087
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10088
- LinkPopover,
10089
- {
10090
- panelRef: linkPopoverPanelRef,
10091
- portalContainer: dialogPortalContainer,
10092
- open: true,
10093
- mode: linkPopover.mode ?? "edit",
10094
- pages: sitePages,
10095
- sections: currentSections,
10096
- sectionsByPath,
10097
- initialTarget: linkPopover.target,
10098
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
10099
- onClose: closeLinkPopover,
10100
- onSubmit: handleLinkPopoverSubmit
10101
- },
10102
- linkPopover.key
10103
- ) : null
11085
+ "*"
11086
+ );
11087
+ },
11088
+ children: "Add Section"
11089
+ }
11090
+ ),
11091
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
11092
+ ] }),
11093
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(LinkPopover, { panelRef: linkPopoverPanelRef, portalContainer: dialogPortalContainer, open: true, mode: linkPopover.mode ?? "edit", pages: sitePages, sections: currentSections, sectionsByPath, initialTarget: linkPopover.target, existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [], onClose: closeLinkPopover, onSubmit: handleLinkPopoverSubmit }, linkPopover.key) : null
10104
11094
  ] }),
10105
11095
  bridgeRoot
10106
11096
  ) : null;
10107
11097
  }
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
11098
  // Annotate the CommonJS export names for ESM import in node:
10151
11099
  0 && (module.exports = {
10152
11100
  CustomToolbar,