@ohhwells/bridge 0.1.44 → 0.1.46-next.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -62,7 +62,7 @@ __export(index_exports, {
62
62
  module.exports = __toCommonJS(index_exports);
63
63
 
64
64
  // src/OhhwellsBridge.tsx
65
- var import_react8 = __toESM(require("react"), 1);
65
+ var import_react9 = __toESM(require("react"), 1);
66
66
  var import_client = require("react-dom/client");
67
67
  var import_react_dom2 = require("react-dom");
68
68
 
@@ -115,6 +115,7 @@ var import_react3 = require("react");
115
115
  var import_react2 = require("react");
116
116
  var import_radix_ui = require("radix-ui");
117
117
  var import_jsx_runtime = require("react/jsx-runtime");
118
+ var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
118
119
  function Spinner() {
119
120
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
120
121
  "svg",
@@ -148,12 +149,17 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
148
149
  const [error, setError] = (0, import_react2.useState)(null);
149
150
  const isBook = title === "Confirm your spot";
150
151
  const handleSubmit = async () => {
151
- if (!email.trim()) return;
152
+ const trimmed = email.trim();
153
+ if (!trimmed) return;
154
+ if (!isValidEmail(trimmed)) {
155
+ setError("Please enter a valid email address.");
156
+ return;
157
+ }
152
158
  setLoading(true);
153
159
  setError(null);
154
160
  try {
155
- await onSubmit(email.trim());
156
- setSubmittedEmail(email.trim());
161
+ await onSubmit(trimmed);
162
+ setSubmittedEmail(trimmed);
157
163
  setSuccess(true);
158
164
  } catch (e) {
159
165
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -228,7 +234,10 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
228
234
  {
229
235
  type: "email",
230
236
  value: email,
231
- onChange: (e) => setEmail(e.target.value),
237
+ onChange: (e) => {
238
+ setEmail(e.target.value);
239
+ if (error) setError(null);
240
+ },
232
241
  onKeyDown: handleKeyDown,
233
242
  placeholder: "hello@gmail.com",
234
243
  autoFocus: true,
@@ -285,12 +294,20 @@ function formatClassTime(cls) {
285
294
  const em = endTotal % 60;
286
295
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
287
296
  }
297
+ function formatBookingLabel(cls) {
298
+ const price = cls.price;
299
+ const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
300
+ return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
301
+ }
288
302
  function getBookingsOnDate(cls, date) {
289
303
  if (!cls.bookings?.length) return 0;
290
- const ds = date.toISOString().split("T")[0];
304
+ const target = new Date(date);
305
+ target.setHours(0, 0, 0, 0);
291
306
  return cls.bookings.filter((b) => {
292
307
  try {
293
- return new Date(b.classDate).toISOString().split("T")[0] === ds;
308
+ const bookingDate = new Date(b.classDate);
309
+ bookingDate.setHours(0, 0, 0, 0);
310
+ return bookingDate.getTime() === target.getTime();
294
311
  } catch {
295
312
  return false;
296
313
  }
@@ -555,7 +572,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
555
572
  }
556
573
  )
557
574
  ] }),
558
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
575
+ cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
559
576
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
560
577
  available,
561
578
  "/",
@@ -567,9 +584,17 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
567
584
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
568
585
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
569
586
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
570
- cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
587
+ cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
588
+ cls.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
589
+ "span",
590
+ {
591
+ className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block",
592
+ style: { wordBreak: "break-word" },
593
+ children: cls.description
594
+ }
595
+ )
571
596
  ] }),
572
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
597
+ cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
573
598
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
574
599
  available,
575
600
  "/",
@@ -594,7 +619,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
594
619
  if (!cls.id) return;
595
620
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
596
621
  },
597
- children: isFull ? "Join Waitlist" : "Book Now"
622
+ children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
598
623
  }
599
624
  )
600
625
  ] })
@@ -636,6 +661,17 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
636
661
  const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
637
662
  const [modalState, setModalState] = (0, import_react3.useState)(null);
638
663
  const switchScheduleIdRef = (0, import_react3.useRef)(null);
664
+ const liveScheduleIdRef = (0, import_react3.useRef)(null);
665
+ const refetchLiveSchedule = (0, import_react3.useCallback)(async () => {
666
+ const id = liveScheduleIdRef.current;
667
+ if (!id) return;
668
+ try {
669
+ const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
670
+ const data = await res.json();
671
+ if (data?.id) setSchedule(data);
672
+ } catch {
673
+ }
674
+ }, []);
639
675
  const dates = (0, import_react3.useMemo)(() => {
640
676
  const today = /* @__PURE__ */ new Date();
641
677
  today.setHours(0, 0, 0, 0);
@@ -655,6 +691,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
655
691
  }
656
692
  }
657
693
  }, [schedule, dates]);
694
+ (0, import_react3.useEffect)(() => {
695
+ if (typeof window === "undefined") return;
696
+ const onVisible = () => {
697
+ if (!document.hidden) void refetchLiveSchedule();
698
+ };
699
+ window.addEventListener("focus", refetchLiveSchedule);
700
+ document.addEventListener("visibilitychange", onVisible);
701
+ return () => {
702
+ window.removeEventListener("focus", refetchLiveSchedule);
703
+ document.removeEventListener("visibilitychange", onVisible);
704
+ };
705
+ }, [refetchLiveSchedule]);
658
706
  (0, import_react3.useEffect)(() => {
659
707
  if (typeof window === "undefined") return;
660
708
  const isInEditor = window.self !== window.top;
@@ -716,7 +764,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
716
764
  setLoading(false);
717
765
  return;
718
766
  }
719
- ;
767
+ liveScheduleIdRef.current = effectiveId;
720
768
  (async () => {
721
769
  try {
722
770
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -773,18 +821,14 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
773
821
  const data = await res.json().catch(() => ({}));
774
822
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
775
823
  }
824
+ await refetchLiveSchedule();
776
825
  };
777
826
  const handleReplaceSchedule = () => {
778
827
  setIsHovered(false);
779
- if (schedule) {
780
- window.parent.postMessage({
781
- type: "ow:schedule-connected",
782
- schedule: { id: schedule.id, name: schedule.name },
783
- insertAfter
784
- }, "*");
785
- } else {
786
- window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
787
- }
828
+ window.parent.postMessage(
829
+ { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
830
+ "*"
831
+ );
788
832
  };
789
833
  if (!inEditor && !loading && !schedule) return null;
790
834
  const sectionId = `scheduling-${insertAfter}`;
@@ -827,7 +871,19 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
827
871
  ),
828
872
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
829
873
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
830
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
874
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
875
+ "h2",
876
+ {
877
+ className: "font-display text-center m-0 text-(--color-dark,#200C02)",
878
+ style: {
879
+ fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
880
+ fontWeight: "var(--font-weight-heading, 400)",
881
+ lineHeight: 1.05,
882
+ letterSpacing: "-0.02em"
883
+ },
884
+ children: schedule?.name ?? "Book an appointment"
885
+ }
886
+ ),
831
887
  schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
832
888
  ] }),
833
889
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4360,33 +4416,39 @@ function Tooltip({ ...props }) {
4360
4416
  function TooltipTrigger({ ...props }) {
4361
4417
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
4362
4418
  }
4419
+ var arrowClassBySide = {
4420
+ top: "before:bottom-0 before:left-1/2 before:-translate-x-1/2 before:translate-y-1/2",
4421
+ bottom: "before:top-0 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2",
4422
+ left: "before:right-0 before:top-1/2 before:-translate-y-1/2 before:translate-x-1/2",
4423
+ right: "before:left-0 before:top-1/2 before:-translate-y-1/2 before:-translate-x-1/2"
4424
+ };
4363
4425
  function TooltipContent({
4364
4426
  className,
4365
- sideOffset = 3,
4427
+ sideOffset = 9,
4366
4428
  side = "bottom",
4367
4429
  children,
4368
4430
  hideArrow,
4369
4431
  ...props
4370
4432
  }) {
4371
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4433
+ const resolvedSide = side ?? "bottom";
4434
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4372
4435
  import_radix_ui3.Tooltip.Content,
4373
4436
  {
4374
4437
  "data-slot": "tooltip-content",
4375
4438
  side,
4376
4439
  sideOffset,
4377
4440
  className: cn(
4378
- "z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4441
+ "relative z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4379
4442
  "origin-[var(--radix-tooltip-content-transform-origin)] animate-in fade-in-0 zoom-in-95",
4380
4443
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4381
4444
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4382
4445
  "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
4446
+ !hideArrow && 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4447
+ !hideArrow && arrowClassBySide[resolvedSide],
4383
4448
  className
4384
4449
  ),
4385
4450
  ...props,
4386
- children: [
4387
- children,
4388
- !hideArrow && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Arrow, { className: "z-[2147483647] size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
4389
- ]
4451
+ children
4390
4452
  }
4391
4453
  ) });
4392
4454
  }
@@ -4403,7 +4465,7 @@ function ToolbarActionTooltip({
4403
4465
  const button = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, ...buttonProps, children });
4404
4466
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Tooltip, { children: [
4405
4467
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-flex", children: button }) : button }),
4406
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, sideOffset: 3, children: label })
4468
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, sideOffset: 9, children: label })
4407
4469
  ] });
4408
4470
  }
4409
4471
  function ItemActionToolbar({
@@ -4485,6 +4547,19 @@ var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0,
4485
4547
  var TOOLBAR_EDGE_MARGIN = 4;
4486
4548
  var SELECTION_CHROME_GAP = 4;
4487
4549
  var TOOLBAR_STROKE_GAP = 4;
4550
+ function getChromeZIndex(state) {
4551
+ switch (state) {
4552
+ case "active-top":
4553
+ case "active-bottom":
4554
+ case "dragging":
4555
+ return 2147483644;
4556
+ case "hover":
4557
+ case "sibling-hint":
4558
+ return 2147483643;
4559
+ default:
4560
+ return 2147483642;
4561
+ }
4562
+ }
4488
4563
  function getChromeStyle(state) {
4489
4564
  switch (state) {
4490
4565
  case "hover":
@@ -4506,7 +4581,7 @@ function getChromeStyle(state) {
4506
4581
  case "dragging":
4507
4582
  return {
4508
4583
  border: `2px solid ${PRIMARY}`,
4509
- boxShadow: DRAG_SHADOW
4584
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4510
4585
  };
4511
4586
  default:
4512
4587
  return {};
@@ -4575,6 +4650,9 @@ function ItemInteractionLayer({
4575
4650
  dragHandleLabel = "Reorder item",
4576
4651
  onDragHandleDragStart,
4577
4652
  onDragHandleDragEnd,
4653
+ onItemPointerDown,
4654
+ onItemClick,
4655
+ itemDragSurface = true,
4578
4656
  chromeGap = SELECTION_CHROME_GAP,
4579
4657
  className
4580
4658
  }) {
@@ -4582,7 +4660,8 @@ function ItemInteractionLayer({
4582
4660
  const isActive = state === "active-top" || state === "active-bottom";
4583
4661
  const isDragging = state === "dragging";
4584
4662
  const showToolbar = isActive && toolbar;
4585
- const showDragHandle = isActive && showHandle && !isDragging;
4663
+ const showDragHandle = (isActive || isDragging) && showHandle;
4664
+ const itemDragEnabled = itemDragSurface && isActive && showHandle && !isDragging && !dragDisabled;
4586
4665
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4587
4666
  "div",
4588
4667
  {
@@ -4596,7 +4675,7 @@ function ItemInteractionLayer({
4596
4675
  left: rect.left - chromeGap,
4597
4676
  width: rect.width + chromeGap * 2,
4598
4677
  height: rect.height + chromeGap * 2,
4599
- zIndex: 2147483646
4678
+ zIndex: getChromeZIndex(state)
4600
4679
  },
4601
4680
  children: [
4602
4681
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -4607,11 +4686,29 @@ function ItemInteractionLayer({
4607
4686
  style: getChromeStyle(state)
4608
4687
  }
4609
4688
  ),
4689
+ itemDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4690
+ "div",
4691
+ {
4692
+ "data-ohw-item-drag-surface": "",
4693
+ className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
4694
+ onPointerDown: (e) => {
4695
+ if (e.button !== 0) return;
4696
+ onItemPointerDown?.(e);
4697
+ },
4698
+ onClick: (e) => {
4699
+ e.preventDefault();
4700
+ e.stopPropagation();
4701
+ onItemClick?.(e.clientX, e.clientY);
4702
+ }
4703
+ }
4704
+ ),
4610
4705
  showDragHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4611
4706
  "div",
4612
4707
  {
4613
4708
  "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",
4709
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4710
+ style: isDragging ? { opacity: 0 } : void 0,
4711
+ "aria-hidden": isDragging || void 0,
4615
4712
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4616
4713
  DragHandle,
4617
4714
  {
@@ -4623,7 +4720,17 @@ function ItemInteractionLayer({
4623
4720
  e.preventDefault();
4624
4721
  return;
4625
4722
  }
4626
- e.dataTransfer?.setData("text/plain", dragHandleLabel);
4723
+ const blank = document.createElement("canvas");
4724
+ blank.width = 1;
4725
+ blank.height = 1;
4726
+ blank.style.cssText = "position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none";
4727
+ document.body.appendChild(blank);
4728
+ try {
4729
+ e.dataTransfer.setDragImage(blank, 0, 0);
4730
+ } finally {
4731
+ requestAnimationFrame(() => blank.remove());
4732
+ }
4733
+ e.dataTransfer.setData("text/plain", dragHandleLabel);
4627
4734
  e.dataTransfer.effectAllowed = "move";
4628
4735
  onDragHandleDragStart?.(e);
4629
4736
  },
@@ -4836,7 +4943,11 @@ function MediaOverlay({
4836
4943
  variant: "outline",
4837
4944
  size: "sm",
4838
4945
  className: "gap-1.5 cursor-pointer hover:bg-background",
4839
- style: OVERLAY_BUTTON_STYLE,
4946
+ style: {
4947
+ ...OVERLAY_BUTTON_STYLE,
4948
+ // Don't let Replace steal clicks when text (e.g. nav) sits on top of this media.
4949
+ pointerEvents: hover.hasTextOverlap ? "none" : "auto"
4950
+ },
4840
4951
  onMouseDown: (e) => e.preventDefault(),
4841
4952
  onClick: (e) => {
4842
4953
  e.stopPropagation();
@@ -6319,9 +6430,155 @@ function shouldUseDevFixtures() {
6319
6430
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6320
6431
  }
6321
6432
 
6433
+ // src/lib/nav-tree.ts
6434
+ var MAX_LEVEL = { nav: 1 };
6435
+ var ROOT = null;
6436
+ function cloneForest(nodes) {
6437
+ return nodes.map((n) => ({ id: n.id, children: cloneForest(n.children) }));
6438
+ }
6439
+ function findNode(nodes, id, parentId = ROOT) {
6440
+ for (let i = 0; i < nodes.length; i++) {
6441
+ const node = nodes[i];
6442
+ if (node.id === id) return { node, parentId, index: i, siblings: nodes };
6443
+ const nested = findNode(node.children, id, node.id);
6444
+ if (nested) return nested;
6445
+ }
6446
+ return null;
6447
+ }
6448
+ function getChildrenOf(nodes, parentId) {
6449
+ if (parentId === ROOT) return nodes;
6450
+ const found = findNode(nodes, parentId);
6451
+ return found ? found.node.children : null;
6452
+ }
6453
+ function isDescendant(nodes, ancestorId, maybeDescendantId) {
6454
+ const found = findNode(nodes, ancestorId);
6455
+ if (!found) return false;
6456
+ return findNode(found.node.children, maybeDescendantId) !== null;
6457
+ }
6458
+ function levelOf(nodes, id) {
6459
+ const loc = findNode(nodes, id);
6460
+ if (!loc) return -1;
6461
+ if (loc.parentId === ROOT) return 0;
6462
+ return 1 + Math.max(0, levelOf(nodes, loc.parentId));
6463
+ }
6464
+ function insertedLevel(nodes, parentId) {
6465
+ if (parentId === ROOT) return 0;
6466
+ const parentLevel = levelOf(nodes, parentId);
6467
+ if (parentLevel < 0) return -1;
6468
+ return parentLevel + 1;
6469
+ }
6470
+ function takeNode(nodes, id) {
6471
+ const forest = cloneForest(nodes);
6472
+ const loc = findNode(forest, id);
6473
+ if (!loc) return null;
6474
+ const [taken] = loc.siblings.splice(loc.index, 1);
6475
+ if (!taken) return null;
6476
+ return { forest, taken };
6477
+ }
6478
+ function insertInto(forest, node, parentId, index) {
6479
+ const siblings = getChildrenOf(forest, parentId);
6480
+ if (!siblings) return false;
6481
+ const clamped = Math.max(0, Math.min(index, siblings.length));
6482
+ siblings.splice(clamped, 0, node);
6483
+ return true;
6484
+ }
6485
+ function normalize(nodes) {
6486
+ const walk = (list) => list.map((n) => {
6487
+ const children = walk(n.children);
6488
+ return { id: n.id, children };
6489
+ });
6490
+ return walk(nodes);
6491
+ }
6492
+ function moveNode(model, draggedId, target) {
6493
+ const { parentId, index: targetIndex } = target;
6494
+ if (parentId === draggedId) return null;
6495
+ if (parentId !== ROOT && isDescendant(model, draggedId, parentId)) return null;
6496
+ const nextLevel = insertedLevel(model, parentId);
6497
+ if (nextLevel < 0 || nextLevel > MAX_LEVEL.nav) return null;
6498
+ const draggedLoc = findNode(model, draggedId);
6499
+ if (!draggedLoc) return null;
6500
+ if (parentId !== ROOT && draggedLoc.node.children.length > 0) return null;
6501
+ const taken = takeNode(model, draggedId);
6502
+ if (!taken) return null;
6503
+ let insertIndex = targetIndex;
6504
+ if (draggedLoc.parentId === parentId && draggedLoc.index < targetIndex) {
6505
+ insertIndex -= 1;
6506
+ }
6507
+ if (!insertInto(taken.forest, taken.taken, parentId, insertIndex)) return null;
6508
+ const next = normalize(taken.forest);
6509
+ if (forestsEqual(model, next)) return null;
6510
+ return next;
6511
+ }
6512
+ function forestsEqual(a, b) {
6513
+ if (a.length !== b.length) return false;
6514
+ for (let i = 0; i < a.length; i++) {
6515
+ const left = a[i];
6516
+ const right = b[i];
6517
+ if (left.id !== right.id) return false;
6518
+ if (!forestsEqual(left.children, right.children)) return false;
6519
+ }
6520
+ return true;
6521
+ }
6522
+ function forestFromFlatOrder(order) {
6523
+ return order.map((id) => ({ id, children: [] }));
6524
+ }
6525
+ function serializeNavOrder(nodes) {
6526
+ const hasNesting = nodes.some((n) => n.children.length > 0);
6527
+ if (!hasNesting) {
6528
+ return JSON.stringify(nodes.map((n) => n.id));
6529
+ }
6530
+ return JSON.stringify(nodes);
6531
+ }
6532
+ function parseNavOrderJson(raw) {
6533
+ if (!raw) return null;
6534
+ try {
6535
+ const parsed = JSON.parse(raw);
6536
+ if (!Array.isArray(parsed)) return null;
6537
+ if (parsed.every((x) => typeof x === "string")) {
6538
+ return forestFromFlatOrder(parsed.filter((x) => typeof x === "string" && x.length > 0));
6539
+ }
6540
+ return parseNavNodeList(parsed);
6541
+ } catch {
6542
+ return null;
6543
+ }
6544
+ }
6545
+ function parseNavNodeList(raw) {
6546
+ const out = [];
6547
+ for (const item of raw) {
6548
+ if (typeof item === "string" && item.length > 0) {
6549
+ out.push({ id: item, children: [] });
6550
+ continue;
6551
+ }
6552
+ if (!item || typeof item !== "object") return null;
6553
+ const rec = item;
6554
+ if (typeof rec.id !== "string" || !rec.id) return null;
6555
+ const children = Array.isArray(rec.children) ? parseNavNodeList(rec.children) : [];
6556
+ if (!children) return null;
6557
+ out.push({ id: rec.id, children });
6558
+ }
6559
+ return out;
6560
+ }
6561
+ function flattenNavOrder(nodes) {
6562
+ const out = [];
6563
+ const walk = (list) => {
6564
+ for (const n of list) {
6565
+ out.push(n.id);
6566
+ walk(n.children);
6567
+ }
6568
+ };
6569
+ walk(nodes);
6570
+ return out;
6571
+ }
6572
+
6322
6573
  // src/lib/nav-items.ts
6323
6574
  var NAV_COUNT_KEY = "__ohw_nav_count";
6324
6575
  var NAV_ORDER_KEY = "__ohw_nav_order";
6576
+ var NAV_HREF_RE = /^nav-(\d+)-href$/;
6577
+ function isNavbarHrefKey(key) {
6578
+ if (!key) return false;
6579
+ if (key === "nav-book-href") return false;
6580
+ return NAV_HREF_RE.test(key);
6581
+ }
6325
6582
  function getLinkHref(el) {
6326
6583
  const key = el.getAttribute("data-ohw-href-key");
6327
6584
  if (key) {
@@ -6358,7 +6615,7 @@ function listNavbarItems() {
6358
6615
  }
6359
6616
  function parseNavIndexFromKey(key) {
6360
6617
  if (!key) return null;
6361
- const match = key.match(/^nav-(\d+)-href$/);
6618
+ const match = key.match(NAV_HREF_RE);
6362
6619
  if (!match) return null;
6363
6620
  return parseInt(match[1], 10);
6364
6621
  }
@@ -6381,17 +6638,6 @@ function getNavbarExistingTargets() {
6381
6638
  function getNavOrderFromDom() {
6382
6639
  return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6383
6640
  }
6384
- function parseNavOrder(content) {
6385
- const raw = content[NAV_ORDER_KEY];
6386
- if (!raw) return null;
6387
- try {
6388
- const parsed = JSON.parse(raw);
6389
- if (!Array.isArray(parsed)) return null;
6390
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6391
- } catch {
6392
- return null;
6393
- }
6394
- }
6395
6641
  function findCounterpartByHrefKey(hrefKey, root) {
6396
6642
  return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6397
6643
  }
@@ -6455,6 +6701,13 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6455
6701
  }
6456
6702
  return primary;
6457
6703
  }
6704
+ function navOrderKeysEqual(a, b) {
6705
+ if (a.length !== b.length) return false;
6706
+ for (let i = 0; i < a.length; i++) {
6707
+ if (a[i] !== b[i]) return false;
6708
+ }
6709
+ return true;
6710
+ }
6458
6711
  function getNavbarLinkKeysInContainer(container) {
6459
6712
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6460
6713
  }
@@ -6463,21 +6716,32 @@ function applyNavOrderToContainer(container, order) {
6463
6716
  const current = getNavbarLinkKeysInContainer(container);
6464
6717
  const extras = current.filter((key) => !desired.includes(key));
6465
6718
  const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6719
+ const orderedEls = [];
6720
+ if (navOrderKeysEqual(current, expected)) return;
6466
6721
  if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6467
6722
  return;
6468
6723
  }
6724
+ const lastCurrentLinkEl = current.length > 0 ? findCounterpartByHrefKey(current[current.length - 1], container) : null;
6725
+ const anchor = lastCurrentLinkEl?.parentElement === container ? lastCurrentLinkEl.nextElementSibling : null;
6469
6726
  const seen = /* @__PURE__ */ new Set();
6470
6727
  for (const hrefKey of desired) {
6471
6728
  if (seen.has(hrefKey)) continue;
6472
6729
  seen.add(hrefKey);
6473
6730
  const el = findCounterpartByHrefKey(hrefKey, container);
6474
- if (el) container.appendChild(el);
6731
+ if (el && isNavbarLinkItem(el)) orderedEls.push(el);
6475
6732
  }
6476
6733
  for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6477
6734
  if (!isNavbarLinkItem(el)) continue;
6478
6735
  const key = el.getAttribute("data-ohw-href-key");
6479
6736
  if (!key || seen.has(key)) continue;
6480
- container.appendChild(el);
6737
+ orderedEls.push(el);
6738
+ }
6739
+ for (const el of orderedEls) {
6740
+ if (anchor) {
6741
+ if (el.nextSibling !== anchor) container.insertBefore(el, anchor);
6742
+ } else if (container.lastElementChild !== el) {
6743
+ container.appendChild(el);
6744
+ }
6481
6745
  }
6482
6746
  }
6483
6747
  function applyNavOrder(order) {
@@ -6486,6 +6750,75 @@ function applyNavOrder(order) {
6486
6750
  const drawer = getNavbarDrawerContainer();
6487
6751
  if (drawer) applyNavOrderToContainer(drawer, order);
6488
6752
  }
6753
+ function applyChildrenOrder(parentHrefKey, childKeys, root) {
6754
+ const parent = findCounterpartByHrefKey(parentHrefKey, root);
6755
+ if (!parent) return;
6756
+ const group = parent.closest("[data-ohw-nav-group]");
6757
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6758
+ if (!childrenRoot) return;
6759
+ const current = Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6760
+ if (navOrderKeysEqual(current, childKeys)) return;
6761
+ for (const key of childKeys) {
6762
+ const el = findCounterpartByHrefKey(key, root);
6763
+ if (!el) continue;
6764
+ if (childrenRoot.lastElementChild !== el) {
6765
+ childrenRoot.appendChild(el);
6766
+ }
6767
+ }
6768
+ }
6769
+ function applyNavForest(forest) {
6770
+ if (forestsEqual(forest, getNavForestFromDom())) return;
6771
+ const rootKeys = forest.map((n) => n.id);
6772
+ applyNavOrder(rootKeys);
6773
+ const desktop = getNavbarDesktopContainer();
6774
+ const drawer = getNavbarDrawerContainer();
6775
+ for (const node of forest) {
6776
+ if (node.children.length === 0) continue;
6777
+ const childKeys = node.children.map((c) => c.id);
6778
+ if (desktop) applyChildrenOrder(node.id, childKeys, desktop);
6779
+ if (drawer) applyChildrenOrder(node.id, childKeys, drawer);
6780
+ }
6781
+ }
6782
+ function getNavForestFromDom() {
6783
+ const items = listNavbarItems();
6784
+ const nestedChildKeys = /* @__PURE__ */ new Set();
6785
+ const roots = [];
6786
+ for (const item of items) {
6787
+ const key = item.getAttribute("data-ohw-href-key");
6788
+ if (!key || !isNavbarHrefKey(key)) continue;
6789
+ if (item.closest("[data-ohw-nav-children]")) continue;
6790
+ const group = item.closest("[data-ohw-nav-group]");
6791
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6792
+ const children = [];
6793
+ if (childrenRoot) {
6794
+ for (const child of childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")) {
6795
+ if (!isNavbarLinkItem(child)) continue;
6796
+ const childKey = child.getAttribute("data-ohw-href-key");
6797
+ if (!childKey || !isNavbarHrefKey(childKey)) continue;
6798
+ nestedChildKeys.add(childKey);
6799
+ children.push({ id: childKey, children: [] });
6800
+ }
6801
+ }
6802
+ roots.push({ id: key, children });
6803
+ }
6804
+ if (roots.length === 0) {
6805
+ return forestFromFlatOrder(
6806
+ getNavOrderFromDom().filter((k) => isNavbarHrefKey(k) && !nestedChildKeys.has(k))
6807
+ );
6808
+ }
6809
+ return roots;
6810
+ }
6811
+ function planNavItemMove(hrefKey, parentId, insertIndex) {
6812
+ const model = getNavForestFromDom();
6813
+ const next = moveNode(model, hrefKey, { parentId, index: insertIndex });
6814
+ if (!next) return null;
6815
+ if (forestsEqual(model, next)) return null;
6816
+ return {
6817
+ forest: next,
6818
+ orderJson: serializeNavOrder(next),
6819
+ flatOrder: flattenNavOrder(next)
6820
+ };
6821
+ }
6489
6822
  function collectNavbarIndicesFromContent(content) {
6490
6823
  const indices = /* @__PURE__ */ new Set();
6491
6824
  for (const key of Object.keys(content)) {
@@ -6513,8 +6846,10 @@ function reconcileNavbarItemsFromContent(content) {
6513
6846
  if (!href && !label) continue;
6514
6847
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6515
6848
  }
6516
- const order = parseNavOrder(content);
6517
- if (order?.length) applyNavOrder(order);
6849
+ const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
6850
+ if (orderForest?.length) {
6851
+ applyNavForest(orderForest);
6852
+ }
6518
6853
  }
6519
6854
  function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6520
6855
  const order = getNavOrderFromDom();
@@ -6554,146 +6889,1344 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6554
6889
  };
6555
6890
  }
6556
6891
 
6557
- // src/ui/navbar-container-chrome.tsx
6558
- var import_lucide_react10 = require("lucide-react");
6559
- var import_jsx_runtime21 = require("react/jsx-runtime");
6560
- function NavbarContainerChrome({
6561
- rect,
6562
- onAdd
6563
- }) {
6564
- const chromeGap = 6;
6565
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6566
- "div",
6567
- {
6568
- "data-ohw-navbar-container-chrome": "",
6569
- "data-ohw-bridge": "",
6570
- className: "pointer-events-none fixed z-[2147483647]",
6571
- style: {
6572
- top: rect.top - chromeGap,
6573
- left: rect.left - chromeGap,
6574
- width: rect.width + chromeGap * 2,
6575
- height: rect.height + chromeGap * 2
6576
- },
6577
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6578
- "button",
6579
- {
6580
- type: "button",
6581
- "data-ohw-navbar-add-button": "",
6582
- className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
6583
- style: { top: "70%" },
6584
- "aria-label": "Add item",
6585
- onMouseDown: (e) => {
6586
- e.preventDefault();
6587
- e.stopPropagation();
6588
- },
6589
- onClick: (e) => {
6590
- e.preventDefault();
6591
- e.stopPropagation();
6592
- onAdd();
6593
- },
6594
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6595
- }
6596
- )
6597
- }
6598
- );
6892
+ // src/lib/footer-items.ts
6893
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6894
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6895
+ function parseFooterHrefKey(key) {
6896
+ if (!key) return null;
6897
+ const match = key.match(FOOTER_HREF_RE);
6898
+ if (!match) return null;
6899
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6599
6900
  }
6600
-
6601
- // src/ui/badge.tsx
6602
- var import_jsx_runtime22 = require("react/jsx-runtime");
6603
- var badgeVariants = cva(
6604
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
6605
- {
6606
- variants: {
6607
- variant: {
6608
- default: "border-transparent bg-primary text-primary-foreground",
6609
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
6610
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
6611
- outline: "text-foreground"
6612
- }
6613
- },
6614
- defaultVariants: {
6615
- variant: "default"
6616
- }
6617
- }
6618
- );
6619
- function Badge({ className, variant, ...props }) {
6620
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6901
+ function isFooterHrefKey(key) {
6902
+ return parseFooterHrefKey(key) !== null;
6621
6903
  }
6622
-
6623
- // src/OhhwellsBridge.tsx
6624
- var import_lucide_react11 = require("lucide-react");
6625
- var import_jsx_runtime23 = require("react/jsx-runtime");
6626
- var PRIMARY2 = "#0885FE";
6627
- var IMAGE_FADE_MS = 300;
6628
- function runOpacityFade(el, onDone) {
6629
- const anim = el.animate([{ opacity: 0 }, { opacity: 1 }], {
6630
- duration: IMAGE_FADE_MS,
6631
- easing: "ease",
6632
- fill: "forwards"
6633
- });
6634
- let finished = false;
6635
- const finish = () => {
6636
- if (finished) return;
6637
- finished = true;
6638
- anim.cancel();
6639
- el.style.opacity = "";
6640
- onDone();
6641
- };
6642
- anim.finished.then(finish).catch(finish);
6643
- window.setTimeout(finish, IMAGE_FADE_MS + 100);
6904
+ function getFooterRoot() {
6905
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6644
6906
  }
6645
- function fadeInImageElement(img, onReady) {
6646
- onReady();
6647
- img.style.opacity = "0";
6648
- runOpacityFade(img, () => {
6649
- img.style.opacity = "";
6907
+ function getFooterLinksContainer() {
6908
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6909
+ }
6910
+ function isFooterLinkAnchor(el) {
6911
+ if (!el.matches("[data-ohw-href-key]")) return false;
6912
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6913
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6914
+ }
6915
+ function listFooterColumns() {
6916
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6917
+ if (!root) return [];
6918
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6919
+ if (explicit.length > 0) return explicit;
6920
+ return Array.from(root.children).filter((el) => {
6921
+ if (!(el instanceof HTMLElement)) return false;
6922
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6923
+ isFooterLinkAnchor
6924
+ );
6650
6925
  });
6651
6926
  }
6652
- function applyEditableImageSrc(img, url) {
6653
- img.removeAttribute("srcset");
6654
- img.removeAttribute("sizes");
6655
- img.src = url;
6927
+ function listFooterLinksInColumn(column) {
6928
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6929
+ isFooterLinkAnchor
6930
+ );
6656
6931
  }
6657
- function fadeInBgImage(el, url, onReady) {
6658
- const prevPos = el.style.position;
6659
- if (!prevPos || prevPos === "static") el.style.position = "relative";
6660
- const layer = document.createElement("div");
6661
- layer.setAttribute("data-ohw-bg-fade-layer", "");
6662
- Object.assign(layer.style, {
6663
- position: "absolute",
6664
- inset: "0",
6665
- zIndex: "0",
6666
- backgroundImage: `url('${url}')`,
6667
- backgroundSize: "cover",
6668
- backgroundPosition: "center",
6669
- backgroundRepeat: "no-repeat",
6670
- opacity: "0",
6671
- pointerEvents: "none"
6672
- });
6673
- el.prepend(layer);
6674
- onReady();
6675
- runOpacityFade(layer, () => {
6676
- el.style.backgroundImage = `url('${url}')`;
6677
- layer.remove();
6678
- if (!prevPos || prevPos === "static") el.style.position = prevPos;
6932
+ function findFooterColumnForLink(anchor) {
6933
+ const columns = listFooterColumns();
6934
+ return columns.find((col) => col.contains(anchor)) ?? null;
6935
+ }
6936
+ function getFooterOrderFromDom() {
6937
+ return listFooterColumns().map(
6938
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6939
+ );
6940
+ }
6941
+ function findFooterLinkByKey(hrefKey) {
6942
+ return document.querySelector(
6943
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6944
+ );
6945
+ }
6946
+ function syncFooterColumnIndices(columns) {
6947
+ columns.forEach((col, index) => {
6948
+ const next = String(index);
6949
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6950
+ col.setAttribute("data-ohw-footer-col", next);
6951
+ }
6679
6952
  });
6680
6953
  }
6681
- function getSectionsTracker() {
6682
- let el = document.querySelector("[data-ohw-sections-tracker]");
6683
- if (!el) {
6684
- el = document.createElement("div");
6685
- el.setAttribute("data-ohw-sections-tracker", "");
6686
- el.style.display = "none";
6687
- document.body.appendChild(el);
6954
+ function appendChildIfNeeded(parent, child) {
6955
+ if (parent.lastElementChild !== child) {
6956
+ parent.appendChild(child);
6688
6957
  }
6689
- return el;
6690
6958
  }
6691
- function updateSectionScheduleId(insertAfter, scheduleId) {
6692
- const tracker = getSectionsTracker();
6693
- let sections = [];
6694
- try {
6695
- sections = JSON.parse(tracker.textContent || "[]");
6696
- } catch {
6959
+ function applyFooterOrder(order) {
6960
+ const container = getFooterLinksContainer();
6961
+ if (!container) return;
6962
+ const columns = listFooterColumns();
6963
+ if (columns.length === 0) return;
6964
+ const currentOrder = getFooterOrderFromDom();
6965
+ if (ordersEqual(order, currentOrder)) {
6966
+ syncFooterColumnIndices(columns);
6967
+ return;
6968
+ }
6969
+ const used = /* @__PURE__ */ new Set();
6970
+ const orderedCols = [];
6971
+ for (const colOrder of order) {
6972
+ let matched = null;
6973
+ for (const hrefKey of colOrder) {
6974
+ const link = findFooterLinkByKey(hrefKey);
6975
+ if (!link) continue;
6976
+ const owner = findFooterColumnForLink(link);
6977
+ if (owner && !used.has(owner)) {
6978
+ matched = owner;
6979
+ break;
6980
+ }
6981
+ }
6982
+ if (!matched) {
6983
+ matched = columns.find((col) => !used.has(col)) ?? null;
6984
+ }
6985
+ if (matched) {
6986
+ used.add(matched);
6987
+ orderedCols.push(matched);
6988
+ }
6989
+ }
6990
+ for (const col of columns) {
6991
+ if (!used.has(col)) orderedCols.push(col);
6992
+ }
6993
+ for (const col of orderedCols) {
6994
+ appendChildIfNeeded(container, col);
6995
+ }
6996
+ const freshColumns = listFooterColumns();
6997
+ syncFooterColumnIndices(freshColumns);
6998
+ for (let c = 0; c < order.length; c++) {
6999
+ const col = freshColumns[c];
7000
+ if (!col) continue;
7001
+ const colOrder = order[c];
7002
+ for (const hrefKey of colOrder) {
7003
+ const el = findFooterLinkByKey(hrefKey);
7004
+ if (!el) continue;
7005
+ if (el.parentElement !== col || col.lastElementChild !== el) {
7006
+ col.appendChild(el);
7007
+ }
7008
+ }
7009
+ }
7010
+ }
7011
+ function ordersEqual(a, b) {
7012
+ if (a.length !== b.length) return false;
7013
+ for (let i = 0; i < a.length; i++) {
7014
+ const left = a[i];
7015
+ const right = b[i];
7016
+ if (left.length !== right.length) return false;
7017
+ for (let j = 0; j < left.length; j++) {
7018
+ if (left[j] !== right[j]) return false;
7019
+ }
7020
+ }
7021
+ return true;
7022
+ }
7023
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
7024
+ const order = getFooterOrderFromDom();
7025
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
7026
+ let fromCol = -1;
7027
+ let fromIdx = -1;
7028
+ for (let c = 0; c < order.length; c++) {
7029
+ const idx = order[c].indexOf(hrefKey);
7030
+ if (idx >= 0) {
7031
+ fromCol = c;
7032
+ fromIdx = idx;
7033
+ break;
7034
+ }
7035
+ }
7036
+ if (fromCol < 0) return null;
7037
+ const next = order.map((col) => [...col]);
7038
+ next[fromCol].splice(fromIdx, 1);
7039
+ let adjusted = insertIndex;
7040
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
7041
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
7042
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
7043
+ if (ordersEqual(next, order)) return null;
7044
+ return next;
7045
+ }
7046
+ function planFooterColumnMove(fromIndex, toIndex) {
7047
+ const order = getFooterOrderFromDom();
7048
+ const columns = listFooterColumns();
7049
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
7050
+ return null;
7051
+ }
7052
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
7053
+ const nextOrder = order.map((col) => [...col]);
7054
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
7055
+ if (!movedOrder) return null;
7056
+ let adjusted = toIndex;
7057
+ if (fromIndex < toIndex) adjusted -= 1;
7058
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
7059
+ nextOrder.splice(adjusted, 0, movedOrder);
7060
+ if (ordersEqual(nextOrder, order)) return null;
7061
+ return nextOrder;
7062
+ }
7063
+ function parseFooterOrder(content) {
7064
+ const raw = content[FOOTER_ORDER_KEY];
7065
+ if (!raw) return null;
7066
+ try {
7067
+ const parsed = JSON.parse(raw);
7068
+ if (!Array.isArray(parsed)) return null;
7069
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
7070
+ } catch {
7071
+ return null;
7072
+ }
7073
+ }
7074
+ var FOOTER_HEADING_RE = /^footer-(\d+)-heading$/;
7075
+ var FOOTER_LABEL_RE = /^footer-(\d+)-(\d+)-label$/;
7076
+ var DEFAULT_FOOTER_COLUMN_HEADING = "New";
7077
+ var DEFAULT_FOOTER_COLUMN_LINK_LABEL = "New link";
7078
+ var DEFAULT_FOOTER_COLUMN_LINK_HREF = "";
7079
+ function isFooterLinksContainer(el) {
7080
+ return el.hasAttribute("data-ohw-footer-links") || el.classList.contains("rb-footer-links");
7081
+ }
7082
+ function getFooterColumnIndicesInDom() {
7083
+ const indices = /* @__PURE__ */ new Set();
7084
+ for (const col of listFooterColumns()) {
7085
+ const attr = col.getAttribute("data-ohw-footer-col");
7086
+ if (attr != null) {
7087
+ const n = parseInt(attr, 10);
7088
+ if (Number.isFinite(n)) indices.add(n);
7089
+ }
7090
+ for (const link of listFooterLinksInColumn(col)) {
7091
+ const parsed = parseFooterHrefKey(link.getAttribute("data-ohw-href-key"));
7092
+ if (parsed) indices.add(parsed.col);
7093
+ }
7094
+ const heading = col.querySelector('[data-ohw-key^="footer-"][data-ohw-key$="-heading"]');
7095
+ const headingKey = heading?.getAttribute("data-ohw-key");
7096
+ if (headingKey) {
7097
+ const match = headingKey.match(FOOTER_HEADING_RE);
7098
+ if (match) indices.add(parseInt(match[1], 10));
7099
+ }
7100
+ }
7101
+ return [...indices].sort((a, b) => a - b);
7102
+ }
7103
+ function getNextFooterColumnIndex() {
7104
+ const indices = getFooterColumnIndicesInDom();
7105
+ if (indices.length === 0) return 0;
7106
+ return Math.max(...indices) + 1;
7107
+ }
7108
+ function cloneFooterLinkShell(template) {
7109
+ const anchor = document.createElement("a");
7110
+ if (template) {
7111
+ anchor.style.cssText = template.style.cssText;
7112
+ if (template.className) anchor.className = template.className;
7113
+ }
7114
+ anchor.style.cursor = "pointer";
7115
+ anchor.style.textDecoration = "none";
7116
+ return anchor;
7117
+ }
7118
+ function buildFooterHeading(colIndex, text) {
7119
+ const heading = document.createElement("p");
7120
+ heading.setAttribute("data-ohw-editable", "text");
7121
+ heading.setAttribute("data-ohw-key", `footer-${colIndex}-heading`);
7122
+ heading.textContent = text;
7123
+ heading.style.cssText = [
7124
+ "color: var(--espresso, #3d312b)",
7125
+ "font-size: 14px",
7126
+ "font-weight: 800",
7127
+ "opacity: 0.82",
7128
+ "margin: 0 0 6px",
7129
+ "padding: 0",
7130
+ "line-height: 1.2"
7131
+ ].join(";");
7132
+ return heading;
7133
+ }
7134
+ function buildFooterLink(colIndex, itemIndex, href, label, template) {
7135
+ const anchor = cloneFooterLinkShell(template);
7136
+ anchor.setAttribute("href", href);
7137
+ anchor.setAttribute("data-ohw-href-key", `footer-${colIndex}-${itemIndex}-href`);
7138
+ const span = document.createElement("span");
7139
+ span.setAttribute("data-ohw-editable", "text");
7140
+ span.setAttribute("data-ohw-key", `footer-${colIndex}-${itemIndex}-label`);
7141
+ span.textContent = label;
7142
+ anchor.appendChild(span);
7143
+ return anchor;
7144
+ }
7145
+ function footerColumnExistsInDom(colIndex) {
7146
+ return document.querySelector(`[data-ohw-footer-col="${colIndex}"]`) !== null || document.querySelector(`[data-ohw-href-key^="footer-${colIndex}-"]`) !== null || document.querySelector(`[data-ohw-key="footer-${colIndex}-heading"]`) !== null;
7147
+ }
7148
+ function getFooterLinkTemplate() {
7149
+ for (const col of listFooterColumns()) {
7150
+ const link = listFooterLinksInColumn(col)[0];
7151
+ if (link) return link;
7152
+ }
7153
+ return null;
7154
+ }
7155
+ function getFooterColumnTemplate() {
7156
+ return listFooterColumns()[0] ?? null;
7157
+ }
7158
+ function insertFooterColumnDom(colIndex, heading, items) {
7159
+ const container = getFooterLinksContainer();
7160
+ if (!container) return null;
7161
+ const colTemplate = getFooterColumnTemplate();
7162
+ const linkTemplate = getFooterLinkTemplate();
7163
+ const column = document.createElement("div");
7164
+ if (colTemplate?.className) column.className = colTemplate.className;
7165
+ else column.className = "rb-footer-link-col";
7166
+ column.setAttribute("data-ohw-footer-col", String(colIndex));
7167
+ if (colTemplate) {
7168
+ const gap = getComputedStyle(colTemplate).gap;
7169
+ if (gap && gap !== "normal") column.style.gap = gap;
7170
+ column.style.display = getComputedStyle(colTemplate).display || "flex";
7171
+ column.style.flexDirection = "column";
7172
+ }
7173
+ column.appendChild(buildFooterHeading(colIndex, heading));
7174
+ let firstLink = null;
7175
+ items.forEach((item, itemIndex) => {
7176
+ const link = buildFooterLink(colIndex, itemIndex, item.href, item.label, linkTemplate);
7177
+ column.appendChild(link);
7178
+ if (!firstLink) firstLink = link;
7179
+ });
7180
+ container.appendChild(column);
7181
+ return { column, firstLink };
7182
+ }
7183
+ function insertFooterColumn(heading = DEFAULT_FOOTER_COLUMN_HEADING, linkLabel = DEFAULT_FOOTER_COLUMN_LINK_LABEL, linkHref = DEFAULT_FOOTER_COLUMN_LINK_HREF) {
7184
+ const colIndex = getNextFooterColumnIndex();
7185
+ const inserted = insertFooterColumnDom(colIndex, heading, [{ href: linkHref, label: linkLabel }]);
7186
+ if (!inserted?.firstLink) throw new Error("Failed to insert footer column");
7187
+ syncFooterColumnIndices(listFooterColumns());
7188
+ return {
7189
+ column: inserted.column,
7190
+ firstLink: inserted.firstLink,
7191
+ colIndex,
7192
+ headingKey: `footer-${colIndex}-heading`,
7193
+ hrefKey: `footer-${colIndex}-0-href`,
7194
+ labelKey: `footer-${colIndex}-0-label`,
7195
+ heading,
7196
+ label: linkLabel,
7197
+ href: linkHref,
7198
+ order: getFooterOrderFromDom()
7199
+ };
7200
+ }
7201
+ function collectFooterColumnIndicesFromContent(content) {
7202
+ const indices = /* @__PURE__ */ new Set();
7203
+ for (const key of Object.keys(content)) {
7204
+ const href = parseFooterHrefKey(key);
7205
+ if (href) indices.add(href.col);
7206
+ const heading = key.match(FOOTER_HEADING_RE);
7207
+ if (heading) indices.add(parseInt(heading[1], 10));
7208
+ const label = key.match(FOOTER_LABEL_RE);
7209
+ if (label) indices.add(parseInt(label[1], 10));
7210
+ }
7211
+ const order = parseFooterOrder(content);
7212
+ if (order) {
7213
+ for (const col of order) {
7214
+ for (const hrefKey of col) {
7215
+ const parsed = parseFooterHrefKey(hrefKey);
7216
+ if (parsed) indices.add(parsed.col);
7217
+ }
7218
+ }
7219
+ }
7220
+ return [...indices].sort((a, b) => a - b);
7221
+ }
7222
+ function collectFooterItemsForColumn(content, colIndex) {
7223
+ const itemIndices = /* @__PURE__ */ new Set();
7224
+ for (const key of Object.keys(content)) {
7225
+ const href = parseFooterHrefKey(key);
7226
+ if (href?.col === colIndex) itemIndices.add(href.item);
7227
+ const label = key.match(FOOTER_LABEL_RE);
7228
+ if (label && parseInt(label[1], 10) === colIndex) {
7229
+ itemIndices.add(parseInt(label[2], 10));
7230
+ }
7231
+ }
7232
+ const sorted = [...itemIndices].sort((a, b) => a - b);
7233
+ if (sorted.length === 0) {
7234
+ return [
7235
+ {
7236
+ href: content[`footer-${colIndex}-0-href`] ?? DEFAULT_FOOTER_COLUMN_LINK_HREF,
7237
+ label: content[`footer-${colIndex}-0-label`] ?? DEFAULT_FOOTER_COLUMN_LINK_LABEL
7238
+ }
7239
+ ];
7240
+ }
7241
+ return sorted.map((itemIndex) => ({
7242
+ href: content[`footer-${colIndex}-${itemIndex}-href`] ?? DEFAULT_FOOTER_COLUMN_LINK_HREF,
7243
+ label: content[`footer-${colIndex}-${itemIndex}-label`] ?? DEFAULT_FOOTER_COLUMN_LINK_LABEL
7244
+ }));
7245
+ }
7246
+ function reconcileFooterColumnsFromContent(content) {
7247
+ const indices = collectFooterColumnIndicesFromContent(content);
7248
+ for (const colIndex of indices) {
7249
+ if (footerColumnExistsInDom(colIndex)) continue;
7250
+ const heading = content[`footer-${colIndex}-heading`] ?? DEFAULT_FOOTER_COLUMN_HEADING;
7251
+ const items = collectFooterItemsForColumn(content, colIndex);
7252
+ const hasPayload = Boolean(content[`footer-${colIndex}-heading`]) || items.some(
7253
+ (_, i) => content[`footer-${colIndex}-${i}-href`] !== void 0 || content[`footer-${colIndex}-${i}-label`] !== void 0
7254
+ ) || (parseFooterOrder(content)?.some(
7255
+ (col) => col.some((k) => parseFooterHrefKey(k)?.col === colIndex)
7256
+ ) ?? false);
7257
+ if (!hasPayload) continue;
7258
+ insertFooterColumnDom(colIndex, heading, items);
7259
+ }
7260
+ }
7261
+ function reconcileFooterOrderFromContent(content) {
7262
+ reconcileFooterColumnsFromContent(content);
7263
+ const order = parseFooterOrder(content);
7264
+ if (!order?.length) return;
7265
+ const current = getFooterOrderFromDom();
7266
+ if (ordersEqual(order, current)) {
7267
+ syncFooterColumnIndices(listFooterColumns());
7268
+ return;
7269
+ }
7270
+ applyFooterOrder(order);
7271
+ }
7272
+ function resolveFooterLinksContainerSelectionTarget(target, clientX, clientY, isPointOverItem) {
7273
+ const container = getFooterLinksContainer();
7274
+ if (!container) return null;
7275
+ const inContainer = target === container || container.contains(target) && Boolean(target.closest("[data-ohw-footer-links], .rb-footer-links"));
7276
+ if (!inContainer && target !== container) {
7277
+ const r2 = container.getBoundingClientRect();
7278
+ const over = clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
7279
+ if (!over) return null;
7280
+ }
7281
+ if (isPointOverItem(container, clientX, clientY)) return null;
7282
+ const column = target.closest("[data-ohw-footer-col], [data-ohw-footer-column]") ?? null;
7283
+ if (column && container.contains(column)) {
7284
+ if (target === column || column.contains(target)) return null;
7285
+ }
7286
+ if (target === container || container.contains(target) || inContainer) {
7287
+ if (target === container || isFooterLinksContainer(target)) return container;
7288
+ if (!column) return container;
7289
+ }
7290
+ return null;
7291
+ }
7292
+ function isRowLayoutColumn(links) {
7293
+ if (links.length < 2) return false;
7294
+ const firstTop = links[0].getBoundingClientRect().top;
7295
+ return links.every((link) => Math.abs(link.getBoundingClientRect().top - firstTop) < 4);
7296
+ }
7297
+ function buildLinkDropSlots(column, columnIndex) {
7298
+ const links = listFooterLinksInColumn(column);
7299
+ const colRect = column.getBoundingClientRect();
7300
+ const slots = [];
7301
+ const barThickness = 3;
7302
+ if (links.length === 0) {
7303
+ slots.push({
7304
+ insertIndex: 0,
7305
+ columnIndex,
7306
+ left: colRect.left,
7307
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
7308
+ width: colRect.width,
7309
+ height: barThickness,
7310
+ direction: "horizontal"
7311
+ });
7312
+ return slots;
7313
+ }
7314
+ if (isRowLayoutColumn(links)) {
7315
+ for (let i = 0; i <= links.length; i++) {
7316
+ let left;
7317
+ if (i === 0) {
7318
+ left = links[0].getBoundingClientRect().left - barThickness / 2;
7319
+ } else if (i === links.length) {
7320
+ const last = links[links.length - 1].getBoundingClientRect();
7321
+ left = last.right - barThickness / 2;
7322
+ } else {
7323
+ const prev = links[i - 1].getBoundingClientRect();
7324
+ const next = links[i].getBoundingClientRect();
7325
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7326
+ }
7327
+ const heightRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7328
+ slots.push({
7329
+ insertIndex: i,
7330
+ columnIndex,
7331
+ left,
7332
+ top: heightRef.top,
7333
+ width: barThickness,
7334
+ height: Math.max(heightRef.height, colRect.height * 0.8),
7335
+ direction: "vertical"
7336
+ });
7337
+ }
7338
+ return slots;
7339
+ }
7340
+ for (let i = 0; i <= links.length; i++) {
7341
+ let top;
7342
+ if (i === 0) {
7343
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
7344
+ } else if (i === links.length) {
7345
+ const last = links[links.length - 1].getBoundingClientRect();
7346
+ top = last.bottom - barThickness / 2;
7347
+ } else {
7348
+ const prev = links[i - 1].getBoundingClientRect();
7349
+ const next = links[i].getBoundingClientRect();
7350
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
7351
+ }
7352
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7353
+ slots.push({
7354
+ insertIndex: i,
7355
+ columnIndex,
7356
+ left: widthRef.left,
7357
+ top,
7358
+ width: Math.max(widthRef.width, colRect.width * 0.8),
7359
+ height: barThickness,
7360
+ direction: "horizontal"
7361
+ });
7362
+ }
7363
+ return slots;
7364
+ }
7365
+ function buildColumnDropSlots() {
7366
+ const columns = listFooterColumns();
7367
+ const slots = [];
7368
+ const barThickness = 3;
7369
+ if (columns.length === 0) return slots;
7370
+ for (let i = 0; i <= columns.length; i++) {
7371
+ let left;
7372
+ let height;
7373
+ let top;
7374
+ if (i === 0) {
7375
+ const first = columns[0].getBoundingClientRect();
7376
+ left = first.left - barThickness / 2;
7377
+ top = first.top;
7378
+ height = first.height;
7379
+ } else if (i === columns.length) {
7380
+ const last = columns[columns.length - 1].getBoundingClientRect();
7381
+ left = last.right - barThickness / 2;
7382
+ top = last.top;
7383
+ height = last.height;
7384
+ } else {
7385
+ const prev = columns[i - 1].getBoundingClientRect();
7386
+ const next = columns[i].getBoundingClientRect();
7387
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7388
+ top = Math.min(prev.top, next.top);
7389
+ height = Math.max(prev.bottom, next.bottom) - top;
7390
+ }
7391
+ slots.push({
7392
+ insertIndex: i,
7393
+ columnIndex: i,
7394
+ left,
7395
+ top,
7396
+ width: barThickness,
7397
+ height: Math.max(height, 24),
7398
+ direction: "vertical"
7399
+ });
7400
+ }
7401
+ return slots;
7402
+ }
7403
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
7404
+ const columns = listFooterColumns();
7405
+ let best = null;
7406
+ for (let c = 0; c < columns.length; c++) {
7407
+ const col = columns[c];
7408
+ const colRect = col.getBoundingClientRect();
7409
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
7410
+ if (!inColX) continue;
7411
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
7412
+ const links = listFooterLinksInColumn(col);
7413
+ const fromIdx = links.findIndex(
7414
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
7415
+ );
7416
+ if (fromIdx < 0) return true;
7417
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
7418
+ return true;
7419
+ }
7420
+ return true;
7421
+ });
7422
+ for (const slot of slots) {
7423
+ const dist = slot.direction === "vertical" ? Math.abs(clientX - (slot.left + slot.width / 2)) : Math.abs(clientY - (slot.top + slot.height / 2));
7424
+ if (!best || dist < best.dist) best = { slot, dist };
7425
+ }
7426
+ }
7427
+ return best?.slot ?? null;
7428
+ }
7429
+ function findColumnIndexForKey(hrefKey) {
7430
+ const order = getFooterOrderFromDom();
7431
+ for (let c = 0; c < order.length; c++) {
7432
+ if (order[c].includes(hrefKey)) return c;
7433
+ }
7434
+ return -1;
7435
+ }
7436
+ function hitTestColumnDropSlot(clientX, _clientY) {
7437
+ const slots = buildColumnDropSlots();
7438
+ let best = null;
7439
+ for (const slot of slots) {
7440
+ const cx2 = slot.left + slot.width / 2;
7441
+ const dist = Math.abs(clientX - cx2);
7442
+ if (!best || dist < best.dist) best = { slot, dist };
7443
+ }
7444
+ return best?.slot ?? null;
7445
+ }
7446
+
7447
+ // src/lib/add-footer-column.ts
7448
+ function buildFooterColumnEditContentPatch(result) {
7449
+ return {
7450
+ [result.headingKey]: result.heading,
7451
+ [result.hrefKey]: result.href,
7452
+ [result.labelKey]: result.label,
7453
+ [FOOTER_ORDER_KEY]: JSON.stringify(result.order)
7454
+ };
7455
+ }
7456
+ function addFooterColumnWithPersist({
7457
+ postToParent: postToParent2
7458
+ }) {
7459
+ const result = insertFooterColumn();
7460
+ const patch = buildFooterColumnEditContentPatch(result);
7461
+ setStoredLinkHref(result.hrefKey, result.href);
7462
+ postToParent2({
7463
+ type: "ow:change",
7464
+ nodes: Object.entries(patch).map(([key, text]) => ({ key, text }))
7465
+ });
7466
+ postToParent2({ type: "ow:toast", title: "Item added", toastType: "success" });
7467
+ enforceLinkHrefs();
7468
+ return result;
7469
+ }
7470
+
7471
+ // src/lib/item-drag-interaction.ts
7472
+ function disableNativeHrefDrag(el) {
7473
+ if (el.draggable) el.draggable = false;
7474
+ if (el.getAttribute("draggable") !== "false") {
7475
+ el.setAttribute("draggable", "false");
7476
+ }
7477
+ }
7478
+ function clearTextSelection() {
7479
+ const sel = window.getSelection();
7480
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7481
+ }
7482
+ function armItemPressDrag() {
7483
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7484
+ }
7485
+ function lockItemDuringDrag() {
7486
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7487
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7488
+ clearTextSelection();
7489
+ }
7490
+ function unlockItemDragInteraction() {
7491
+ const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
7492
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7493
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7494
+ if (wasDragging) clearTextSelection();
7495
+ }
7496
+ var armFooterPressDrag = armItemPressDrag;
7497
+ var lockFooterDuringDrag = lockItemDuringDrag;
7498
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
7499
+
7500
+ // src/lib/nav-dnd.ts
7501
+ function listReorderableNavItems() {
7502
+ return listNavbarItems().filter((el) => {
7503
+ const key = el.getAttribute("data-ohw-href-key");
7504
+ return isNavbarHrefKey(key) && !el.closest("[data-ohw-nav-children]");
7505
+ });
7506
+ }
7507
+ function listNavChildren(parentHrefKey) {
7508
+ const items = listNavbarItems();
7509
+ const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
7510
+ if (!parent) return [];
7511
+ const group = parent.closest("[data-ohw-nav-group]");
7512
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
7513
+ if (!childrenRoot) return [];
7514
+ return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
7515
+ (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
7516
+ );
7517
+ }
7518
+ function activeNavListContainer() {
7519
+ const desktop = getNavbarDesktopContainer();
7520
+ if (desktop && desktop.getClientRects().length > 0) {
7521
+ const style = window.getComputedStyle(desktop);
7522
+ if (style.display !== "none" && style.visibility !== "hidden") return desktop;
7523
+ }
7524
+ return getNavbarDrawerContainer();
7525
+ }
7526
+ function buildRootNavDropSlots() {
7527
+ const items = listReorderableNavItems();
7528
+ const slots = [];
7529
+ const barThickness = 3;
7530
+ if (items.length === 0) {
7531
+ const container = activeNavListContainer();
7532
+ if (!container) return slots;
7533
+ const rect = container.getBoundingClientRect();
7534
+ slots.push({
7535
+ insertIndex: 0,
7536
+ parentId: null,
7537
+ left: rect.left + rect.width / 2 - barThickness / 2,
7538
+ top: rect.top,
7539
+ width: barThickness,
7540
+ height: Math.max(rect.height, 24),
7541
+ direction: "vertical"
7542
+ });
7543
+ return slots;
7544
+ }
7545
+ const edgeGap = (() => {
7546
+ if (items.length < 2) return 32;
7547
+ const a = items[0].getBoundingClientRect();
7548
+ const b = items[1].getBoundingClientRect();
7549
+ return Math.max(0, b.left - a.right);
7550
+ })();
7551
+ for (let i = 0; i <= items.length; i++) {
7552
+ let left;
7553
+ let top;
7554
+ let height;
7555
+ if (i === 0) {
7556
+ const first = items[0].getBoundingClientRect();
7557
+ left = first.left - edgeGap / 2 - barThickness / 2;
7558
+ top = first.top;
7559
+ height = first.height;
7560
+ } else if (i === items.length) {
7561
+ const last = items[items.length - 1].getBoundingClientRect();
7562
+ const lastGap = items.length >= 2 ? Math.max(
7563
+ 0,
7564
+ last.left - items[items.length - 2].getBoundingClientRect().right
7565
+ ) : edgeGap;
7566
+ left = last.right + lastGap / 2 - barThickness / 2;
7567
+ top = last.top;
7568
+ height = last.height;
7569
+ } else {
7570
+ const prev = items[i - 1].getBoundingClientRect();
7571
+ const next = items[i].getBoundingClientRect();
7572
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7573
+ top = Math.min(prev.top, next.top);
7574
+ height = Math.max(prev.bottom, next.bottom) - top;
7575
+ }
7576
+ slots.push({
7577
+ insertIndex: i,
7578
+ parentId: null,
7579
+ left,
7580
+ top,
7581
+ width: barThickness,
7582
+ height: Math.max(height, 24),
7583
+ direction: "vertical"
7584
+ });
7585
+ }
7586
+ return slots;
7587
+ }
7588
+ function buildChildNavDropSlots(parentHrefKey) {
7589
+ const children = listNavChildren(parentHrefKey);
7590
+ const slots = [];
7591
+ const barThickness = 3;
7592
+ if (children.length === 0) return slots;
7593
+ const edgeGap = (() => {
7594
+ if (children.length < 2) return 16;
7595
+ const a = children[0].getBoundingClientRect();
7596
+ const b = children[1].getBoundingClientRect();
7597
+ return Math.max(0, b.top - a.bottom);
7598
+ })();
7599
+ for (let i = 0; i <= children.length; i++) {
7600
+ let top;
7601
+ let width;
7602
+ let left;
7603
+ if (i === 0) {
7604
+ const first = children[0].getBoundingClientRect();
7605
+ top = first.top - edgeGap / 2 - barThickness / 2;
7606
+ left = first.left;
7607
+ width = first.width;
7608
+ } else if (i === children.length) {
7609
+ const last = children[children.length - 1].getBoundingClientRect();
7610
+ const lastGap = children.length >= 2 ? Math.max(
7611
+ 0,
7612
+ last.top - children[children.length - 2].getBoundingClientRect().bottom
7613
+ ) : edgeGap;
7614
+ top = last.bottom + lastGap / 2 - barThickness / 2;
7615
+ left = last.left;
7616
+ width = last.width;
7617
+ } else {
7618
+ const prev = children[i - 1].getBoundingClientRect();
7619
+ const next = children[i].getBoundingClientRect();
7620
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
7621
+ left = Math.min(prev.left, next.left);
7622
+ width = Math.max(prev.right, next.right) - left;
7623
+ }
7624
+ slots.push({
7625
+ insertIndex: i,
7626
+ parentId: parentHrefKey,
7627
+ left,
7628
+ top,
7629
+ width: Math.max(width, 40),
7630
+ height: barThickness,
7631
+ direction: "horizontal"
7632
+ });
7633
+ }
7634
+ return slots;
7635
+ }
7636
+ function buildAllNavDropSlots(draggedHrefKey) {
7637
+ const slots = [...buildRootNavDropSlots()];
7638
+ for (const item of listReorderableNavItems()) {
7639
+ const key = item.getAttribute("data-ohw-href-key");
7640
+ if (!key || key === draggedHrefKey) continue;
7641
+ const group = item.closest("[data-ohw-nav-group]");
7642
+ if (!group?.querySelector(":scope > [data-ohw-nav-children]")) continue;
7643
+ slots.push(...buildChildNavDropSlots(key));
7644
+ }
7645
+ const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
7646
+ const parentGroup = dragged?.closest("[data-ohw-nav-children]")?.closest("[data-ohw-nav-group]");
7647
+ const parentTrigger = parentGroup?.querySelector(":scope > [data-ohw-href-key]");
7648
+ const parentKey = parentTrigger?.getAttribute("data-ohw-href-key");
7649
+ if (parentKey && isNavbarHrefKey(parentKey)) {
7650
+ const existing = slots.some((s) => s.parentId === parentKey);
7651
+ if (!existing) slots.push(...buildChildNavDropSlots(parentKey));
7652
+ }
7653
+ return slots;
7654
+ }
7655
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
7656
+ const slots = buildAllNavDropSlots(draggedHrefKey);
7657
+ let best = null;
7658
+ for (const slot of slots) {
7659
+ const cx2 = slot.left + slot.width / 2;
7660
+ const cy = slot.top + slot.height / 2;
7661
+ const dist = slot.direction === "vertical" ? Math.abs(clientX - cx2) + Math.abs(clientY - cy) * 0.25 : Math.abs(clientY - cy) + Math.abs(clientX - cx2) * 0.25;
7662
+ if (!best || dist < best.dist) best = { slot, dist };
7663
+ }
7664
+ return best?.slot ?? null;
7665
+ }
7666
+
7667
+ // src/useNavItemDrag.ts
7668
+ var import_react8 = require("react");
7669
+ function useNavItemDrag({
7670
+ isEditMode,
7671
+ editContentRef,
7672
+ selectedElRef,
7673
+ activeElRef,
7674
+ footerDragRef,
7675
+ suppressNextClickRef,
7676
+ suppressClickUntilRef,
7677
+ siblingHintElRef,
7678
+ postToParentRef,
7679
+ deselectRef,
7680
+ selectRef,
7681
+ deactivateRef,
7682
+ setLinkPopover,
7683
+ linkPopoverOpenRef,
7684
+ setIsItemDragging,
7685
+ setDraggedItemRect,
7686
+ setSiblingHintRect,
7687
+ setSiblingHintRects,
7688
+ setToolbarRect,
7689
+ getNavigationItemAnchor: getNavigationItemAnchor2,
7690
+ isDragHandleDisabled: isDragHandleDisabled2,
7691
+ isNavbarButton: isNavbarButton3
7692
+ }) {
7693
+ const navDragRef = (0, import_react8.useRef)(null);
7694
+ const [navDropSlots, setNavDropSlots] = (0, import_react8.useState)([]);
7695
+ const [activeNavDropIndex, setActiveNavDropIndex] = (0, import_react8.useState)(null);
7696
+ const navPointerDragRef = (0, import_react8.useRef)(null);
7697
+ const clearNavDragVisuals = (0, import_react8.useCallback)(() => {
7698
+ navDragRef.current = null;
7699
+ setNavDropSlots([]);
7700
+ setActiveNavDropIndex(null);
7701
+ setDraggedItemRect(null);
7702
+ setSiblingHintRects([]);
7703
+ setIsItemDragging(false);
7704
+ unlockItemDragInteraction();
7705
+ }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
7706
+ const refreshNavDragVisuals = (0, import_react8.useCallback)(
7707
+ (session, activeSlot, clientX, clientY) => {
7708
+ setDraggedItemRect(session.draggedEl.getBoundingClientRect());
7709
+ if (typeof clientX === "number" && typeof clientY === "number") {
7710
+ session.lastClientX = clientX;
7711
+ session.lastClientY = clientY;
7712
+ }
7713
+ session.activeSlot = activeSlot;
7714
+ setSiblingHintRects([]);
7715
+ const slots = buildAllNavDropSlots(session.hrefKey);
7716
+ setNavDropSlots(slots);
7717
+ const activeIdx = activeSlot ? slots.findIndex(
7718
+ (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
7719
+ ) : -1;
7720
+ setActiveNavDropIndex(activeIdx >= 0 ? activeIdx : null);
7721
+ },
7722
+ [setDraggedItemRect, setSiblingHintRects]
7723
+ );
7724
+ const refreshNavDragVisualsRef = (0, import_react8.useRef)(refreshNavDragVisuals);
7725
+ refreshNavDragVisualsRef.current = refreshNavDragVisuals;
7726
+ const commitNavDragRef = (0, import_react8.useRef)(() => {
7727
+ });
7728
+ const beginNavDragRef = (0, import_react8.useRef)(() => {
7729
+ });
7730
+ const beginNavDrag = (0, import_react8.useCallback)(
7731
+ (session) => {
7732
+ const rect = session.draggedEl.getBoundingClientRect();
7733
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
7734
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
7735
+ session.activeSlot = session.activeSlot ?? null;
7736
+ navDragRef.current = session;
7737
+ setIsItemDragging(true);
7738
+ lockItemDuringDrag();
7739
+ siblingHintElRef.current = null;
7740
+ setSiblingHintRect(null);
7741
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
7742
+ setToolbarRect(rect);
7743
+ }
7744
+ const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
7745
+ refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
7746
+ },
7747
+ [
7748
+ refreshNavDragVisuals,
7749
+ selectedElRef,
7750
+ setIsItemDragging,
7751
+ setSiblingHintRect,
7752
+ setToolbarRect,
7753
+ siblingHintElRef
7754
+ ]
7755
+ );
7756
+ beginNavDragRef.current = beginNavDrag;
7757
+ const commitNavDrag = (0, import_react8.useCallback)(
7758
+ (clientX, clientY) => {
7759
+ const session = navDragRef.current;
7760
+ if (!session) {
7761
+ clearNavDragVisuals();
7762
+ return;
7763
+ }
7764
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
7765
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
7766
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
7767
+ const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
7768
+ const wasSelected = session.wasSelected;
7769
+ const hrefKey = session.hrefKey;
7770
+ const applySelectionAfterDrop = () => {
7771
+ if (!wasSelected) {
7772
+ deselectRef.current();
7773
+ return;
7774
+ }
7775
+ const desktop = document.querySelector("[data-ohw-nav-container]");
7776
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
7777
+ const link = desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
7778
+ if (link) {
7779
+ selectRef.current(link);
7780
+ return;
7781
+ }
7782
+ deselectRef.current();
7783
+ };
7784
+ if (planned) {
7785
+ editContentRef.current = {
7786
+ ...editContentRef.current,
7787
+ [NAV_ORDER_KEY]: planned.orderJson
7788
+ };
7789
+ applyNavForest(planned.forest);
7790
+ document.querySelectorAll(
7791
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
7792
+ ).forEach((el) => {
7793
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
7794
+ disableNativeHrefDrag(el);
7795
+ }
7796
+ });
7797
+ postToParentRef.current({
7798
+ type: "ow:change",
7799
+ nodes: [{ key: NAV_ORDER_KEY, text: planned.orderJson }]
7800
+ });
7801
+ applySelectionAfterDrop();
7802
+ clearNavDragVisuals();
7803
+ requestAnimationFrame(() => {
7804
+ if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
7805
+ applyNavForest(planned.forest);
7806
+ }
7807
+ applySelectionAfterDrop();
7808
+ requestAnimationFrame(applySelectionAfterDrop);
7809
+ });
7810
+ return;
7811
+ }
7812
+ applySelectionAfterDrop();
7813
+ clearNavDragVisuals();
7814
+ },
7815
+ [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
7816
+ );
7817
+ commitNavDragRef.current = commitNavDrag;
7818
+ const startNavLinkDrag = (0, import_react8.useCallback)(
7819
+ (anchor, clientX, clientY, wasSelected) => {
7820
+ if (footerDragRef.current) return false;
7821
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
7822
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7823
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return false;
7824
+ beginNavDrag({
7825
+ hrefKey,
7826
+ wasSelected,
7827
+ draggedEl: anchor,
7828
+ lastClientX: clientX,
7829
+ lastClientY: clientY,
7830
+ activeSlot: null
7831
+ });
7832
+ return true;
7833
+ },
7834
+ [beginNavDrag, footerDragRef, isDragHandleDisabled2, isNavbarButton3]
7835
+ );
7836
+ const onNavDragOver = (0, import_react8.useCallback)(
7837
+ (e) => {
7838
+ const session = navDragRef.current;
7839
+ if (!session) return false;
7840
+ e.preventDefault();
7841
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
7842
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7843
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7844
+ return true;
7845
+ },
7846
+ []
7847
+ );
7848
+ (0, import_react8.useEffect)(() => {
7849
+ if (!isEditMode) return;
7850
+ const THRESHOLD = 10;
7851
+ const resolveWasSelected = (el) => {
7852
+ if (selectedElRef.current === el) return true;
7853
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor2(activeElRef.current) : null;
7854
+ return activeAnchor === el;
7855
+ };
7856
+ const onPointerDown = (e) => {
7857
+ if (e.button !== 0) return;
7858
+ if (navDragRef.current || footerDragRef.current) return;
7859
+ const target = e.target;
7860
+ if (!target) return;
7861
+ if (target.closest(
7862
+ '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]'
7863
+ )) {
7864
+ return;
7865
+ }
7866
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
7867
+ const anchor = getNavigationItemAnchor2(target);
7868
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
7869
+ if (!anchor || !isNavbarHrefKey(hrefKey)) return;
7870
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return;
7871
+ navPointerDragRef.current = {
7872
+ el: anchor,
7873
+ startX: e.clientX,
7874
+ startY: e.clientY,
7875
+ pointerId: e.pointerId,
7876
+ wasSelected: resolveWasSelected(anchor),
7877
+ started: false
7878
+ };
7879
+ };
7880
+ const onPointerMove = (e) => {
7881
+ const pending = navPointerDragRef.current;
7882
+ if (!pending) return;
7883
+ if (pending.started) {
7884
+ e.preventDefault();
7885
+ clearTextSelection();
7886
+ const session = navDragRef.current;
7887
+ if (!session) return;
7888
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7889
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7890
+ return;
7891
+ }
7892
+ const dx = e.clientX - pending.startX;
7893
+ const dy = e.clientY - pending.startY;
7894
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
7895
+ e.preventDefault();
7896
+ pending.started = true;
7897
+ armItemPressDrag();
7898
+ clearTextSelection();
7899
+ try {
7900
+ document.body.setPointerCapture(pending.pointerId);
7901
+ } catch {
7902
+ }
7903
+ if (linkPopoverOpenRef.current) setLinkPopover(null);
7904
+ if (activeElRef.current) deactivateRef.current();
7905
+ const key = pending.el.getAttribute("data-ohw-href-key");
7906
+ if (!key) return;
7907
+ beginNavDragRef.current({
7908
+ hrefKey: key,
7909
+ wasSelected: pending.wasSelected,
7910
+ draggedEl: pending.el,
7911
+ lastClientX: e.clientX,
7912
+ lastClientY: e.clientY,
7913
+ activeSlot: null
7914
+ });
7915
+ };
7916
+ const endPointerDrag = (e) => {
7917
+ const pending = navPointerDragRef.current;
7918
+ navPointerDragRef.current = null;
7919
+ try {
7920
+ if (document.body.hasPointerCapture(e.pointerId)) {
7921
+ document.body.releasePointerCapture(e.pointerId);
7922
+ }
7923
+ } catch {
7924
+ }
7925
+ if (!pending) return;
7926
+ if (!pending.started) {
7927
+ unlockItemDragInteraction();
7928
+ return;
7929
+ }
7930
+ suppressNextClickRef.current = true;
7931
+ suppressClickUntilRef.current = Date.now() + 500;
7932
+ commitNavDragRef.current(e.clientX, e.clientY);
7933
+ };
7934
+ const blockSelectStart = (e) => {
7935
+ if (navDragRef.current || navPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
7936
+ e.preventDefault();
7937
+ }
7938
+ };
7939
+ const onDragEnd = () => {
7940
+ if (!navDragRef.current) return;
7941
+ suppressNextClickRef.current = true;
7942
+ suppressClickUntilRef.current = Date.now() + 500;
7943
+ commitNavDragRef.current();
7944
+ };
7945
+ document.addEventListener("pointerdown", onPointerDown, true);
7946
+ document.addEventListener("pointermove", onPointerMove, true);
7947
+ document.addEventListener("pointerup", endPointerDrag, true);
7948
+ document.addEventListener("pointercancel", endPointerDrag, true);
7949
+ document.addEventListener("selectstart", blockSelectStart, true);
7950
+ document.addEventListener("dragend", onDragEnd, true);
7951
+ return () => {
7952
+ document.removeEventListener("pointerdown", onPointerDown, true);
7953
+ document.removeEventListener("pointermove", onPointerMove, true);
7954
+ document.removeEventListener("pointerup", endPointerDrag, true);
7955
+ document.removeEventListener("pointercancel", endPointerDrag, true);
7956
+ document.removeEventListener("selectstart", blockSelectStart, true);
7957
+ document.removeEventListener("dragend", onDragEnd, true);
7958
+ unlockItemDragInteraction();
7959
+ };
7960
+ }, [
7961
+ activeElRef,
7962
+ deactivateRef,
7963
+ footerDragRef,
7964
+ getNavigationItemAnchor2,
7965
+ isDragHandleDisabled2,
7966
+ isEditMode,
7967
+ isNavbarButton3,
7968
+ linkPopoverOpenRef,
7969
+ selectedElRef,
7970
+ setLinkPopover,
7971
+ suppressNextClickRef
7972
+ ]);
7973
+ const armNavPressFromChrome = (0, import_react8.useCallback)(
7974
+ (selected, clientX, clientY, pointerId) => {
7975
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
7976
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7977
+ if (isNavbarButton3(selected) || isDragHandleDisabled2(selected)) return false;
7978
+ navPointerDragRef.current = {
7979
+ el: selected,
7980
+ startX: clientX,
7981
+ startY: clientY,
7982
+ pointerId,
7983
+ wasSelected: true,
7984
+ started: false
7985
+ };
7986
+ return true;
7987
+ },
7988
+ [isDragHandleDisabled2, isNavbarButton3]
7989
+ );
7990
+ return {
7991
+ navDragRef,
7992
+ navDropSlots,
7993
+ activeNavDropIndex,
7994
+ startNavLinkDrag,
7995
+ commitNavDrag,
7996
+ onNavDragOver,
7997
+ armNavPressFromChrome,
7998
+ clearNavDragVisuals,
7999
+ refreshNavDragVisualsRef
8000
+ };
8001
+ }
8002
+
8003
+ // src/ui/footer-container-chrome.tsx
8004
+ var import_lucide_react10 = require("lucide-react");
8005
+ var import_jsx_runtime21 = require("react/jsx-runtime");
8006
+ function FooterContainerChrome({ rect, onAdd }) {
8007
+ const chromeGap = 6;
8008
+ const buttonMargin = 7;
8009
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8010
+ "div",
8011
+ {
8012
+ "data-ohw-footer-container-chrome": "",
8013
+ "data-ohw-bridge": "",
8014
+ className: "pointer-events-none fixed z-[2147483647]",
8015
+ style: {
8016
+ top: rect.top - chromeGap,
8017
+ left: rect.left - chromeGap,
8018
+ width: rect.width + chromeGap * 2,
8019
+ height: rect.height + chromeGap * 2
8020
+ },
8021
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
8022
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8023
+ "button",
8024
+ {
8025
+ type: "button",
8026
+ "data-ohw-footer-add-button": "",
8027
+ className: "pointer-events-auto absolute left-1/2 flex size-7 -translate-x-1/2 -translate-y-full items-center justify-center rounded-[10px] border border-border bg-background p-0.5 shadow-sm transition-colors hover:bg-muted/80",
8028
+ style: { top: chromeGap - buttonMargin },
8029
+ "aria-label": "Add item",
8030
+ onMouseDown: (e) => {
8031
+ e.preventDefault();
8032
+ e.stopPropagation();
8033
+ },
8034
+ onClick: (e) => {
8035
+ e.preventDefault();
8036
+ e.stopPropagation();
8037
+ onAdd();
8038
+ },
8039
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
8040
+ }
8041
+ ) }),
8042
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: "Add item" })
8043
+ ] })
8044
+ }
8045
+ ) });
8046
+ }
8047
+
8048
+ // src/ui/navbar-container-chrome.tsx
8049
+ var import_lucide_react11 = require("lucide-react");
8050
+ var import_jsx_runtime22 = require("react/jsx-runtime");
8051
+ function NavbarContainerChrome({
8052
+ rect,
8053
+ onAdd
8054
+ }) {
8055
+ const chromeGap = 6;
8056
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8057
+ "div",
8058
+ {
8059
+ "data-ohw-navbar-container-chrome": "",
8060
+ "data-ohw-bridge": "",
8061
+ className: "pointer-events-none fixed z-[2147483647]",
8062
+ style: {
8063
+ top: rect.top - chromeGap,
8064
+ left: rect.left - chromeGap,
8065
+ width: rect.width + chromeGap * 2,
8066
+ height: rect.height + chromeGap * 2
8067
+ },
8068
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8069
+ "button",
8070
+ {
8071
+ type: "button",
8072
+ "data-ohw-navbar-add-button": "",
8073
+ className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
8074
+ style: { top: "70%" },
8075
+ "aria-label": "Add item",
8076
+ onMouseDown: (e) => {
8077
+ e.preventDefault();
8078
+ e.stopPropagation();
8079
+ },
8080
+ onClick: (e) => {
8081
+ e.preventDefault();
8082
+ e.stopPropagation();
8083
+ onAdd();
8084
+ },
8085
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
8086
+ }
8087
+ )
8088
+ }
8089
+ );
8090
+ }
8091
+
8092
+ // src/ui/drop-indicator.tsx
8093
+ var React10 = __toESM(require("react"), 1);
8094
+ var import_jsx_runtime23 = require("react/jsx-runtime");
8095
+ var dropIndicatorVariants = cva(
8096
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
8097
+ {
8098
+ variants: {
8099
+ direction: {
8100
+ vertical: "h-6 w-[3px]",
8101
+ horizontal: "h-[3px] w-[200px]"
8102
+ },
8103
+ state: {
8104
+ default: "opacity-0",
8105
+ hover: "opacity-100",
8106
+ dragIdle: "opacity-40",
8107
+ dragActive: "opacity-100"
8108
+ }
8109
+ },
8110
+ defaultVariants: {
8111
+ direction: "vertical",
8112
+ state: "default"
8113
+ }
8114
+ }
8115
+ );
8116
+ var DropIndicator = React10.forwardRef(
8117
+ ({ className, direction, state, ...props }, ref) => {
8118
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8119
+ "div",
8120
+ {
8121
+ ref,
8122
+ "data-slot": "drop-indicator",
8123
+ "data-direction": direction ?? "vertical",
8124
+ "data-state": state ?? "default",
8125
+ "aria-hidden": "true",
8126
+ className: cn(dropIndicatorVariants({ direction, state }), className),
8127
+ ...props
8128
+ }
8129
+ );
8130
+ }
8131
+ );
8132
+ DropIndicator.displayName = "DropIndicator";
8133
+
8134
+ // src/ui/badge.tsx
8135
+ var import_jsx_runtime24 = require("react/jsx-runtime");
8136
+ var badgeVariants = cva(
8137
+ "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",
8138
+ {
8139
+ variants: {
8140
+ variant: {
8141
+ default: "border-transparent bg-primary text-primary-foreground",
8142
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
8143
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
8144
+ outline: "text-foreground"
8145
+ }
8146
+ },
8147
+ defaultVariants: {
8148
+ variant: "default"
8149
+ }
8150
+ }
8151
+ );
8152
+ function Badge({ className, variant, ...props }) {
8153
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
8154
+ }
8155
+
8156
+ // src/OhhwellsBridge.tsx
8157
+ var import_lucide_react12 = require("lucide-react");
8158
+ var import_jsx_runtime25 = require("react/jsx-runtime");
8159
+ var PRIMARY2 = "#0885FE";
8160
+ var IMAGE_FADE_MS = 300;
8161
+ function runOpacityFade(el, onDone) {
8162
+ const anim = el.animate([{ opacity: 0 }, { opacity: 1 }], {
8163
+ duration: IMAGE_FADE_MS,
8164
+ easing: "ease",
8165
+ fill: "forwards"
8166
+ });
8167
+ let finished = false;
8168
+ const finish = () => {
8169
+ if (finished) return;
8170
+ finished = true;
8171
+ anim.cancel();
8172
+ el.style.opacity = "";
8173
+ onDone();
8174
+ };
8175
+ anim.finished.then(finish).catch(finish);
8176
+ window.setTimeout(finish, IMAGE_FADE_MS + 100);
8177
+ }
8178
+ function fadeInImageElement(img, onReady) {
8179
+ onReady();
8180
+ img.style.opacity = "0";
8181
+ runOpacityFade(img, () => {
8182
+ img.style.opacity = "";
8183
+ });
8184
+ }
8185
+ function applyEditableImageSrc(img, url) {
8186
+ img.removeAttribute("srcset");
8187
+ img.removeAttribute("sizes");
8188
+ img.src = url;
8189
+ }
8190
+ function fadeInBgImage(el, url, onReady) {
8191
+ const prevPos = el.style.position;
8192
+ if (!prevPos || prevPos === "static") el.style.position = "relative";
8193
+ const layer = document.createElement("div");
8194
+ layer.setAttribute("data-ohw-bg-fade-layer", "");
8195
+ Object.assign(layer.style, {
8196
+ position: "absolute",
8197
+ inset: "0",
8198
+ zIndex: "0",
8199
+ backgroundImage: `url('${url}')`,
8200
+ backgroundSize: "cover",
8201
+ backgroundPosition: "center",
8202
+ backgroundRepeat: "no-repeat",
8203
+ opacity: "0",
8204
+ pointerEvents: "none"
8205
+ });
8206
+ el.prepend(layer);
8207
+ onReady();
8208
+ runOpacityFade(layer, () => {
8209
+ el.style.backgroundImage = `url('${url}')`;
8210
+ layer.remove();
8211
+ if (!prevPos || prevPos === "static") el.style.position = prevPos;
8212
+ });
8213
+ }
8214
+ function getSectionsTracker() {
8215
+ let el = document.querySelector("[data-ohw-sections-tracker]");
8216
+ if (!el) {
8217
+ el = document.createElement("div");
8218
+ el.setAttribute("data-ohw-sections-tracker", "");
8219
+ el.style.display = "none";
8220
+ document.body.appendChild(el);
8221
+ }
8222
+ return el;
8223
+ }
8224
+ function updateSectionScheduleId(insertAfter, scheduleId) {
8225
+ const tracker = getSectionsTracker();
8226
+ let sections = [];
8227
+ try {
8228
+ sections = JSON.parse(tracker.textContent || "[]");
8229
+ } catch {
6697
8230
  }
6698
8231
  const currentPath = window.location.pathname;
6699
8232
  const updated = sections.map((s) => {
@@ -6801,7 +8334,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6801
8334
  const root = (0, import_client.createRoot)(container);
6802
8335
  (0, import_react_dom2.flushSync)(() => {
6803
8336
  root.render(
6804
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8337
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6805
8338
  SchedulingWidget,
6806
8339
  {
6807
8340
  notifyOnConnect,
@@ -6876,7 +8409,7 @@ function syncNavigationDragCursorAttrs() {
6876
8409
  el.setAttribute("data-ohw-can-drag", "");
6877
8410
  });
6878
8411
  }
6879
- function collectEditableNodes() {
8412
+ function collectEditableNodes(extraContent) {
6880
8413
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6881
8414
  if (el.dataset.ohwEditable === "image") {
6882
8415
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6904,6 +8437,14 @@ function collectEditableNodes() {
6904
8437
  if (!key) return;
6905
8438
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6906
8439
  });
8440
+ if (extraContent) {
8441
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
8442
+ const text = extraContent[key];
8443
+ if (typeof text === "string" && text.length > 0) {
8444
+ nodes.push({ key, type: "meta", text });
8445
+ }
8446
+ }
8447
+ }
6907
8448
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6908
8449
  const key = el.dataset.ohwKey ?? "";
6909
8450
  const video = getVideoEl(el);
@@ -6994,7 +8535,7 @@ function applyLinkByKey(key, val) {
6994
8535
  }
6995
8536
  function isInsideLinkEditor(target) {
6996
8537
  return Boolean(
6997
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
8538
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
6998
8539
  );
6999
8540
  }
7000
8541
  function getHrefKeyFromElement(el) {
@@ -7017,6 +8558,11 @@ function getNavigationItemAnchor(el) {
7017
8558
  function isNavigationItem(el) {
7018
8559
  return getNavigationItemAnchor(el) !== null;
7019
8560
  }
8561
+ function listNavigationItems() {
8562
+ return Array.from(
8563
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
8564
+ ).filter((el) => isNavigationItem(el));
8565
+ }
7020
8566
  function getNavigationItemReorderState(anchor) {
7021
8567
  if (isNavbarButton2(anchor)) return { key: null, disabled: false };
7022
8568
  return getReorderHandleStateFromAnchor(anchor);
@@ -7049,7 +8595,7 @@ function countFooterNavItems(el) {
7049
8595
  return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
7050
8596
  }
7051
8597
  function findFooterItemGroup(item) {
7052
- const explicit = item.closest("[data-ohw-footer-column]");
8598
+ const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
7053
8599
  if (explicit) return explicit;
7054
8600
  const footer = item.closest("footer");
7055
8601
  if (!footer) return null;
@@ -7066,17 +8612,17 @@ function isNavigationRoot(el) {
7066
8612
  function isInferredFooterGroup(el) {
7067
8613
  const footer = el.closest("footer");
7068
8614
  if (!footer || el === footer) return false;
7069
- if (el.hasAttribute("data-ohw-footer-column")) return true;
8615
+ if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
7070
8616
  return countFooterNavItems(el) >= 2;
7071
8617
  }
7072
8618
  function isNavigationContainer(el) {
7073
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
8619
+ return el.hasAttribute("data-ohw-nav-container") || isFooterLinksContainer(el) || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
7074
8620
  }
7075
8621
  function isNavbarLinksContainer(el) {
7076
8622
  return el.hasAttribute("data-ohw-nav-container");
7077
8623
  }
7078
8624
  function getFooterColumn(el) {
7079
- return el.closest("[data-ohw-footer-column]");
8625
+ return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
7080
8626
  }
7081
8627
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
7082
8628
  if (getNavigationItemAnchor(target)) return null;
@@ -7089,6 +8635,9 @@ function isPointOverNavigation(x, y) {
7089
8635
  const roots = /* @__PURE__ */ new Set();
7090
8636
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
7091
8637
  if (navRoot) roots.add(navRoot);
8638
+ document.querySelectorAll("[data-ohw-nav-drawer], [data-ohw-nav-container]").forEach((el) => {
8639
+ roots.add(el);
8640
+ });
7092
8641
  const footer = document.querySelector("footer");
7093
8642
  if (footer) roots.add(footer);
7094
8643
  for (const root of roots) {
@@ -7100,7 +8649,7 @@ function isPointOverNavigation(x, y) {
7100
8649
  function findHoveredNavOrFooterContainer(x, y) {
7101
8650
  const candidates = [
7102
8651
  ...document.querySelectorAll("[data-ohw-nav-container]"),
7103
- ...document.querySelectorAll("[data-ohw-footer-column]")
8652
+ ...listFooterColumns()
7104
8653
  ];
7105
8654
  for (const container of candidates) {
7106
8655
  const r2 = container.getBoundingClientRect();
@@ -7117,6 +8666,12 @@ function isPointOverNavItem(container, x, y) {
7117
8666
  return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
7118
8667
  });
7119
8668
  }
8669
+ function isPointOverFooterColumn(x, y) {
8670
+ return listFooterColumns().some((col) => {
8671
+ const r2 = col.getBoundingClientRect();
8672
+ return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
8673
+ });
8674
+ }
7120
8675
  function resolveNavContainerSelectionTarget(target, clientX, clientY) {
7121
8676
  if (getNavigationItemAnchor(target)) return null;
7122
8677
  if (target.closest('[data-ohw-role="navbar-button"]')) return null;
@@ -7147,32 +8702,60 @@ function getNavigationSelectionParent(el) {
7147
8702
  if (footerGroup) return footerGroup;
7148
8703
  return getNavigationRoot(el);
7149
8704
  }
7150
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
8705
+ if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el))) {
8706
+ return getFooterLinksContainer();
8707
+ }
8708
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup(el)) {
7151
8709
  return getNavigationRoot(el);
7152
8710
  }
7153
8711
  return null;
7154
8712
  }
8713
+ function collectNavigationItemSiblingHintRects(selected) {
8714
+ if (!isNavigationItem(selected)) return [];
8715
+ const footerColumn = getFooterColumn(selected);
8716
+ if (footerColumn) {
8717
+ return listFooterLinksInColumn(footerColumn).filter((link) => link !== selected).map((link) => link.getBoundingClientRect());
8718
+ }
8719
+ const navContainer = selected.closest("[data-ohw-nav-container], [data-ohw-nav-drawer]");
8720
+ if (navContainer) {
8721
+ return Array.from(navContainer.querySelectorAll("[data-ohw-href-key]")).filter((el) => isNavigationItem(el) && el !== selected).map((el) => el.getBoundingClientRect());
8722
+ }
8723
+ return listNavigationItems().filter((el) => el !== selected).map((el) => el.getBoundingClientRect());
8724
+ }
7155
8725
  function placeCaretAtPoint(el, x, y) {
7156
8726
  const selection = window.getSelection();
7157
8727
  if (!selection) return;
7158
- if (typeof document.caretRangeFromPoint === "function") {
7159
- const range = document.caretRangeFromPoint(x, y);
7160
- if (range && el.contains(range.startContainer)) {
7161
- selection.removeAllRanges();
7162
- selection.addRange(range);
7163
- return;
8728
+ const overlays = Array.from(
8729
+ document.querySelectorAll(
8730
+ "[data-ohw-item-drag-surface], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-ohw-item-toolbar-anchor]"
8731
+ )
8732
+ );
8733
+ const prevPointerEvents = overlays.map((node) => node.style.pointerEvents);
8734
+ for (const node of overlays) node.style.pointerEvents = "none";
8735
+ try {
8736
+ if (typeof document.caretRangeFromPoint === "function") {
8737
+ const range = document.caretRangeFromPoint(x, y);
8738
+ if (range && el.contains(range.startContainer)) {
8739
+ selection.removeAllRanges();
8740
+ selection.addRange(range);
8741
+ return;
8742
+ }
7164
8743
  }
7165
- }
7166
- const caretPositionFromPoint = document.caretPositionFromPoint;
7167
- if (typeof caretPositionFromPoint === "function") {
7168
- const position = caretPositionFromPoint(x, y);
7169
- if (position && el.contains(position.offsetNode)) {
7170
- const range = document.createRange();
7171
- range.setStart(position.offsetNode, position.offset);
7172
- range.collapse(true);
7173
- selection.removeAllRanges();
7174
- selection.addRange(range);
8744
+ const caretPositionFromPoint = document.caretPositionFromPoint;
8745
+ if (typeof caretPositionFromPoint === "function") {
8746
+ const position = caretPositionFromPoint(x, y);
8747
+ if (position && el.contains(position.offsetNode)) {
8748
+ const range = document.createRange();
8749
+ range.setStart(position.offsetNode, position.offset);
8750
+ range.collapse(true);
8751
+ selection.removeAllRanges();
8752
+ selection.addRange(range);
8753
+ }
7175
8754
  }
8755
+ } finally {
8756
+ overlays.forEach((node, i) => {
8757
+ node.style.pointerEvents = prevPointerEvents[i] ?? "";
8758
+ });
7176
8759
  }
7177
8760
  }
7178
8761
  function selectAllTextInEditable(el) {
@@ -7293,9 +8876,16 @@ function sanitizeHtml(html) {
7293
8876
  if (!SAFE_TAGS.has(el.tagName)) {
7294
8877
  parent.replaceChild(document.createTextNode(el.textContent ?? ""), el);
7295
8878
  } else {
7296
- const textAlign = el.style?.textAlign || el.getAttribute("align") || "";
8879
+ const htmlEl = el;
8880
+ const textAlign = htmlEl.style?.textAlign || el.getAttribute("align") || "";
8881
+ const fontWeight = htmlEl.style?.fontWeight || "";
8882
+ const fontStyle = htmlEl.style?.fontStyle || "";
8883
+ const textDecorationLine = htmlEl.style?.textDecorationLine || htmlEl.style?.textDecoration || "";
7297
8884
  for (const attr of Array.from(el.attributes)) el.removeAttribute(attr.name);
7298
- if (textAlign) el.style.textAlign = textAlign;
8885
+ if (textAlign) htmlEl.style.textAlign = textAlign;
8886
+ if (fontWeight) htmlEl.style.fontWeight = fontWeight;
8887
+ if (fontStyle) htmlEl.style.fontStyle = fontStyle;
8888
+ if (textDecorationLine) htmlEl.style.textDecorationLine = textDecorationLine;
7299
8889
  walk(el);
7300
8890
  }
7301
8891
  }
@@ -7338,10 +8928,11 @@ function EditGlowChrome({
7338
8928
  rect,
7339
8929
  elRef,
7340
8930
  reorderHrefKey,
7341
- dragDisabled = false
8931
+ dragDisabled = false,
8932
+ hideHandle = false
7342
8933
  }) {
7343
8934
  const GAP = SELECTION_CHROME_GAP2;
7344
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8935
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
7345
8936
  "div",
7346
8937
  {
7347
8938
  ref: elRef,
@@ -7356,7 +8947,7 @@ function EditGlowChrome({
7356
8947
  zIndex: 2147483646
7357
8948
  },
7358
8949
  children: [
7359
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8950
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7360
8951
  "div",
7361
8952
  {
7362
8953
  style: {
@@ -7369,7 +8960,7 @@ function EditGlowChrome({
7369
8960
  }
7370
8961
  }
7371
8962
  ),
7372
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8963
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7373
8964
  "div",
7374
8965
  {
7375
8966
  "data-ohw-drag-handle-container": "",
@@ -7381,7 +8972,7 @@ function EditGlowChrome({
7381
8972
  transform: "translate(calc(-100% - 7px), -50%)",
7382
8973
  pointerEvents: dragDisabled ? "none" : "auto"
7383
8974
  },
7384
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8975
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7385
8976
  DragHandle,
7386
8977
  {
7387
8978
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7482,6 +9073,79 @@ function resolveItemInteractionState(rect, parentScroll) {
7482
9073
  const { transform } = calcToolbarPos(rect, parentScroll, 120);
7483
9074
  return transform.includes("translateY(-100%)") ? "active-top" : "active-bottom";
7484
9075
  }
9076
+ function resolveSelectionStartElement(sel) {
9077
+ if (!sel || sel.rangeCount === 0) return null;
9078
+ const range = sel.getRangeAt(0);
9079
+ let startNode = range.startContainer;
9080
+ if (startNode.nodeType === Node.ELEMENT_NODE) {
9081
+ const el = startNode;
9082
+ startNode = el.childNodes[range.startOffset] ?? el.childNodes[range.startOffset - 1] ?? el;
9083
+ }
9084
+ return startNode.nodeType === Node.TEXT_NODE ? startNode.parentElement : startNode;
9085
+ }
9086
+ function detectActiveFormats(startEl, activeRoot) {
9087
+ const formats = /* @__PURE__ */ new Set();
9088
+ const cs = getComputedStyle(startEl);
9089
+ const weight = parseInt(cs.fontWeight, 10);
9090
+ let hasBold = cs.fontWeight === "bold" || !Number.isNaN(weight) && weight >= 600;
9091
+ let hasItalic = cs.fontStyle === "italic" || cs.fontStyle === "oblique";
9092
+ let hasUnderline = false;
9093
+ let hasStrike = false;
9094
+ let hasUl = false;
9095
+ let hasOl = false;
9096
+ for (let el = startEl; el; el = el.parentElement) {
9097
+ if (el.tagName === "B" || el.tagName === "STRONG") hasBold = true;
9098
+ if (el.tagName === "I" || el.tagName === "EM") hasItalic = true;
9099
+ const decoration = getComputedStyle(el).textDecorationLine;
9100
+ if (decoration.includes("underline") || el.tagName === "U" || el.tagName === "INS") hasUnderline = true;
9101
+ if (decoration.includes("line-through") || el.tagName === "S" || el.tagName === "STRIKE" || el.tagName === "DEL") hasStrike = true;
9102
+ if (el.tagName === "UL") hasUl = true;
9103
+ if (el.tagName === "OL") hasOl = true;
9104
+ if (el === activeRoot) break;
9105
+ }
9106
+ if (hasBold) formats.add("bold");
9107
+ if (hasItalic) formats.add("italic");
9108
+ if (hasUnderline) formats.add("underline");
9109
+ if (hasStrike) formats.add("strikeThrough");
9110
+ if (hasUl) formats.add("insertUnorderedList");
9111
+ if (hasOl) formats.add("insertOrderedList");
9112
+ const block = startEl.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? startEl;
9113
+ const align = getComputedStyle(block).textAlign;
9114
+ if (align === "center") formats.add("justifyCenter");
9115
+ else if (align === "right" || align === "end") formats.add("justifyRight");
9116
+ else formats.add("justifyLeft");
9117
+ return formats;
9118
+ }
9119
+ function setsEqual(a, b) {
9120
+ if (a.size !== b.size) return false;
9121
+ for (const item of a) {
9122
+ if (!b.has(item)) return false;
9123
+ }
9124
+ return true;
9125
+ }
9126
+ function textOffsetInRoot(root, node, offset) {
9127
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
9128
+ let total = 0;
9129
+ let current;
9130
+ while (current = walker.nextNode()) {
9131
+ if (current === node) return total + offset;
9132
+ total += (current.textContent ?? "").length;
9133
+ }
9134
+ return total;
9135
+ }
9136
+ function pointAtTextOffset(root, targetOffset) {
9137
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
9138
+ let total = 0;
9139
+ let current;
9140
+ let last = null;
9141
+ while (current = walker.nextNode()) {
9142
+ const len = (current.textContent ?? "").length;
9143
+ if (total + len >= targetOffset) return { node: current, offset: targetOffset - total };
9144
+ total += len;
9145
+ last = current;
9146
+ }
9147
+ return last ? { node: last, offset: (last.textContent ?? "").length } : null;
9148
+ }
7485
9149
  function FloatingToolbar({
7486
9150
  rect,
7487
9151
  parentScroll,
@@ -7491,9 +9155,9 @@ function FloatingToolbar({
7491
9155
  showEditLink,
7492
9156
  onEditLink
7493
9157
  }) {
7494
- const localRef = import_react8.default.useRef(null);
7495
- const [measuredW, setMeasuredW] = import_react8.default.useState(330);
7496
- const setRefs = import_react8.default.useCallback(
9158
+ const localRef = import_react9.default.useRef(null);
9159
+ const [measuredW, setMeasuredW] = import_react9.default.useState(330);
9160
+ const setRefs = import_react9.default.useCallback(
7497
9161
  (node) => {
7498
9162
  localRef.current = node;
7499
9163
  if (typeof elRef === "function") elRef(node);
@@ -7505,7 +9169,7 @@ function FloatingToolbar({
7505
9169
  },
7506
9170
  [elRef]
7507
9171
  );
7508
- import_react8.default.useLayoutEffect(() => {
9172
+ import_react9.default.useLayoutEffect(() => {
7509
9173
  const node = localRef.current;
7510
9174
  if (!node) return;
7511
9175
  const update = () => {
@@ -7518,7 +9182,7 @@ function FloatingToolbar({
7518
9182
  return () => ro.disconnect();
7519
9183
  }, [showEditLink, activeCommands]);
7520
9184
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
7521
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9185
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7522
9186
  "div",
7523
9187
  {
7524
9188
  ref: setRefs,
@@ -7530,12 +9194,12 @@ function FloatingToolbar({
7530
9194
  zIndex: 2147483647,
7531
9195
  pointerEvents: "auto"
7532
9196
  },
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, {}),
9197
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CustomToolbar, { children: [
9198
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react9.default.Fragment, { children: [
9199
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(CustomToolbarDivider, {}),
7536
9200
  btns.map((btn) => {
7537
9201
  const isActive = activeCommands.has(btn.cmd);
7538
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9202
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7539
9203
  CustomToolbarButton,
7540
9204
  {
7541
9205
  title: btn.title,
@@ -7544,7 +9208,7 @@ function FloatingToolbar({
7544
9208
  e.preventDefault();
7545
9209
  onCommand(btn.cmd);
7546
9210
  },
7547
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9211
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7548
9212
  "svg",
7549
9213
  {
7550
9214
  width: "16",
@@ -7565,7 +9229,7 @@ function FloatingToolbar({
7565
9229
  );
7566
9230
  })
7567
9231
  ] }, gi)),
7568
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9232
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7569
9233
  CustomToolbarButton,
7570
9234
  {
7571
9235
  type: "button",
@@ -7579,7 +9243,7 @@ function FloatingToolbar({
7579
9243
  e.preventDefault();
7580
9244
  e.stopPropagation();
7581
9245
  },
7582
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
9246
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7583
9247
  }
7584
9248
  ) : null
7585
9249
  ] })
@@ -7596,7 +9260,7 @@ function StateToggle({
7596
9260
  states,
7597
9261
  onStateChange
7598
9262
  }) {
7599
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9263
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7600
9264
  ToggleGroup,
7601
9265
  {
7602
9266
  "data-ohw-state-toggle": "",
@@ -7610,7 +9274,7 @@ function StateToggle({
7610
9274
  left: rect.right - 8,
7611
9275
  transform: "translateX(-100%)"
7612
9276
  },
7613
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
9277
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7614
9278
  }
7615
9279
  );
7616
9280
  }
@@ -7637,8 +9301,8 @@ function OhhwellsBridge() {
7637
9301
  const router = (0, import_navigation2.useRouter)();
7638
9302
  const searchParams = (0, import_navigation2.useSearchParams)();
7639
9303
  const isEditMode = isEditSessionActive();
7640
- const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7641
- (0, import_react8.useEffect)(() => {
9304
+ const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
9305
+ (0, import_react9.useEffect)(() => {
7642
9306
  const figtreeFontId = "ohw-figtree-font";
7643
9307
  if (!document.getElementById(figtreeFontId)) {
7644
9308
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7666,92 +9330,137 @@ function OhhwellsBridge() {
7666
9330
  const subdomainFromQuery = searchParams.get("subdomain");
7667
9331
  const subdomain = resolveSubdomain(subdomainFromQuery);
7668
9332
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7669
- const postToParent2 = (0, import_react8.useCallback)((data) => {
9333
+ const postToParent2 = (0, import_react9.useCallback)((data) => {
7670
9334
  if (typeof window !== "undefined" && window.parent !== window) {
7671
9335
  window.parent.postMessage(data, "*");
7672
9336
  }
7673
9337
  }, []);
7674
- const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
7675
- const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
7676
- const activeElRef = (0, import_react8.useRef)(null);
7677
- const selectedElRef = (0, import_react8.useRef)(null);
7678
- const originalContentRef = (0, import_react8.useRef)(null);
7679
- const activeStateElRef = (0, import_react8.useRef)(null);
7680
- const parentScrollRef = (0, import_react8.useRef)(null);
7681
- const visibleViewportRef = (0, import_react8.useRef)(null);
7682
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
7683
- const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
9338
+ const [fetchState, setFetchState] = (0, import_react9.useState)("idle");
9339
+ const autoSaveTimers = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
9340
+ const activeElRef = (0, import_react9.useRef)(null);
9341
+ const pointerHeldRef = (0, import_react9.useRef)(false);
9342
+ const selectedElRef = (0, import_react9.useRef)(null);
9343
+ const selectedHrefKeyRef = (0, import_react9.useRef)(null);
9344
+ const selectedFooterColAttrRef = (0, import_react9.useRef)(null);
9345
+ const originalContentRef = (0, import_react9.useRef)(null);
9346
+ const activeStateElRef = (0, import_react9.useRef)(null);
9347
+ const parentScrollRef = (0, import_react9.useRef)(null);
9348
+ const visibleViewportRef = (0, import_react9.useRef)(null);
9349
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react9.useState)(null);
9350
+ const attachVisibleViewport = (0, import_react9.useCallback)((node) => {
7684
9351
  visibleViewportRef.current = node;
7685
9352
  setDialogPortalContainer(node);
7686
9353
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7687
9354
  }, []);
7688
- const toolbarElRef = (0, import_react8.useRef)(null);
7689
- const glowElRef = (0, import_react8.useRef)(null);
7690
- const hoveredImageRef = (0, import_react8.useRef)(null);
7691
- const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
7692
- const dragOverElRef = (0, import_react8.useRef)(null);
7693
- const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7694
- const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
7695
- const hoveredGapRef = (0, import_react8.useRef)(null);
7696
- const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7697
- const imageShowTimerRef = (0, import_react8.useRef)(null);
7698
- const editStylesRef = (0, import_react8.useRef)(null);
7699
- const activateRef = (0, import_react8.useRef)(() => {
9355
+ const toolbarElRef = (0, import_react9.useRef)(null);
9356
+ const glowElRef = (0, import_react9.useRef)(null);
9357
+ const hoveredImageRef = (0, import_react9.useRef)(null);
9358
+ const hoveredImageHasTextOverlapRef = (0, import_react9.useRef)(false);
9359
+ const dragOverElRef = (0, import_react9.useRef)(null);
9360
+ const [mediaHover, setMediaHover] = (0, import_react9.useState)(null);
9361
+ const [uploadingRects, setUploadingRects] = (0, import_react9.useState)({});
9362
+ const hoveredGapRef = (0, import_react9.useRef)(null);
9363
+ const imageUnhoverTimerRef = (0, import_react9.useRef)(null);
9364
+ const imageShowTimerRef = (0, import_react9.useRef)(null);
9365
+ const editStylesRef = (0, import_react9.useRef)(null);
9366
+ const activateRef = (0, import_react9.useRef)(() => {
7700
9367
  });
7701
- const deactivateRef = (0, import_react8.useRef)(() => {
9368
+ const deactivateRef = (0, import_react9.useRef)(() => {
7702
9369
  });
7703
- const selectRef = (0, import_react8.useRef)(() => {
9370
+ const selectRef = (0, import_react9.useRef)(() => {
7704
9371
  });
7705
- const selectFrameRef = (0, import_react8.useRef)(() => {
9372
+ const selectFrameRef = (0, import_react9.useRef)(() => {
7706
9373
  });
7707
- const deselectRef = (0, import_react8.useRef)(() => {
9374
+ const deselectRef = (0, import_react9.useRef)(() => {
7708
9375
  });
7709
- const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
9376
+ const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
7710
9377
  });
7711
- const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
9378
+ const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
7712
9379
  });
7713
- const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
9380
+ const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
7714
9381
  });
7715
- const postToParentRef = (0, import_react8.useRef)(postToParent2);
9382
+ const postToParentRef = (0, import_react9.useRef)(postToParent2);
7716
9383
  postToParentRef.current = postToParent2;
7717
- const sectionsLoadedRef = (0, import_react8.useRef)(false);
7718
- const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
7719
- const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
7720
- const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7721
- const toolbarVariantRef = (0, import_react8.useRef)("none");
9384
+ const sectionsLoadedRef = (0, import_react9.useRef)(false);
9385
+ const pendingScheduleConfigRequests = (0, import_react9.useRef)([]);
9386
+ const [toolbarRect, setToolbarRect] = (0, import_react9.useState)(null);
9387
+ const [toolbarVariant, setToolbarVariant] = (0, import_react9.useState)("none");
9388
+ const toolbarVariantRef = (0, import_react9.useRef)("none");
7722
9389
  toolbarVariantRef.current = toolbarVariant;
7723
- const [selectedIsCta, setSelectedIsCta] = (0, import_react8.useState)(false);
7724
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7725
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7726
- const [toggleState, setToggleState] = (0, import_react8.useState)(null);
7727
- const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
7728
- const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
7729
- const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
7730
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
7731
- const hoveredNavContainerRef = (0, import_react8.useRef)(null);
7732
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
7733
- const hoveredItemElRef = (0, import_react8.useRef)(null);
7734
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7735
- const siblingHintElRef = (0, import_react8.useRef)(null);
7736
- const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
7737
- const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
7738
- const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7739
- const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7740
- const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
7741
- const editContentRef = (0, import_react8.useRef)({});
7742
- const [sitePages, setSitePages] = (0, import_react8.useState)([]);
7743
- const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
7744
- const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
7745
- const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
7746
- const linkPopoverPanelRef = (0, import_react8.useRef)(null);
7747
- const linkPopoverOpenRef = (0, import_react8.useRef)(false);
7748
- const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
9390
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react9.useState)(false);
9391
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react9.useState)(null);
9392
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react9.useState)(false);
9393
+ const [toggleState, setToggleState] = (0, import_react9.useState)(null);
9394
+ const [maxBadge, setMaxBadge] = (0, import_react9.useState)(null);
9395
+ const [activeCommands, setActiveCommands] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
9396
+ const [sectionGap, setSectionGap] = (0, import_react9.useState)(null);
9397
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react9.useState)(false);
9398
+ const hoveredNavContainerRef = (0, import_react9.useRef)(null);
9399
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react9.useState)(null);
9400
+ const hoveredItemElRef = (0, import_react9.useRef)(null);
9401
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react9.useState)(null);
9402
+ const siblingHintElRef = (0, import_react9.useRef)(null);
9403
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react9.useState)(null);
9404
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react9.useState)([]);
9405
+ const [isItemDragging, setIsItemDragging] = (0, import_react9.useState)(false);
9406
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react9.useState)(false);
9407
+ const footerDragRef = (0, import_react9.useRef)(null);
9408
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react9.useState)([]);
9409
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react9.useState)(null);
9410
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react9.useState)(null);
9411
+ const footerPointerDragRef = (0, import_react9.useRef)(null);
9412
+ const suppressNextClickRef = (0, import_react9.useRef)(false);
9413
+ const suppressClickUntilRef = (0, import_react9.useRef)(0);
9414
+ const [linkPopover, setLinkPopover] = (0, import_react9.useState)(null);
9415
+ const linkPopoverSessionRef = (0, import_react9.useRef)(null);
9416
+ const addNavAfterAnchorRef = (0, import_react9.useRef)(null);
9417
+ const editContentRef = (0, import_react9.useRef)({});
9418
+ const [sitePages, setSitePages] = (0, import_react9.useState)([]);
9419
+ const [sectionsByPath, setSectionsByPath] = (0, import_react9.useState)({});
9420
+ const sectionsPrefetchGenRef = (0, import_react9.useRef)(0);
9421
+ const setLinkPopoverRef = (0, import_react9.useRef)(setLinkPopover);
9422
+ const linkPopoverPanelRef = (0, import_react9.useRef)(null);
9423
+ const linkPopoverOpenRef = (0, import_react9.useRef)(false);
9424
+ const linkPopoverGraceUntilRef = (0, import_react9.useRef)(0);
7749
9425
  setLinkPopoverRef.current = setLinkPopover;
7750
9426
  linkPopoverSessionRef.current = linkPopover;
9427
+ const {
9428
+ navDragRef,
9429
+ navDropSlots,
9430
+ activeNavDropIndex,
9431
+ startNavLinkDrag,
9432
+ commitNavDrag,
9433
+ onNavDragOver,
9434
+ armNavPressFromChrome,
9435
+ refreshNavDragVisualsRef
9436
+ } = useNavItemDrag({
9437
+ isEditMode,
9438
+ editContentRef,
9439
+ selectedElRef,
9440
+ activeElRef,
9441
+ footerDragRef,
9442
+ suppressNextClickRef,
9443
+ suppressClickUntilRef,
9444
+ siblingHintElRef,
9445
+ postToParentRef,
9446
+ deselectRef,
9447
+ selectRef,
9448
+ deactivateRef,
9449
+ setLinkPopover: () => setLinkPopover(null),
9450
+ linkPopoverOpenRef,
9451
+ setIsItemDragging,
9452
+ setDraggedItemRect,
9453
+ setSiblingHintRect,
9454
+ setSiblingHintRects,
9455
+ setToolbarRect,
9456
+ getNavigationItemAnchor,
9457
+ isDragHandleDisabled,
9458
+ isNavbarButton: isNavbarButton2
9459
+ });
7751
9460
  const bumpLinkPopoverGrace = () => {
7752
9461
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7753
9462
  };
7754
- const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
9463
+ const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
7755
9464
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7756
9465
  const gen = ++sectionsPrefetchGenRef.current;
7757
9466
  const paths = pages.map((p) => p.path);
@@ -7770,10 +9479,24 @@ function OhhwellsBridge() {
7770
9479
  );
7771
9480
  });
7772
9481
  }, [isEditMode, pathname]);
7773
- const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
9482
+ const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
7774
9483
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7775
- (0, import_react8.useEffect)(() => {
7776
- if (!linkPopover) return;
9484
+ (0, import_react9.useEffect)(() => {
9485
+ if (!linkPopover) {
9486
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
9487
+ return;
9488
+ }
9489
+ document.documentElement.setAttribute("data-ohw-link-popover-open", "");
9490
+ hoveredItemElRef.current = null;
9491
+ setHoveredItemRect(null);
9492
+ hoveredNavContainerRef.current = null;
9493
+ setHoveredNavContainerRect(null);
9494
+ siblingHintElRef.current = null;
9495
+ setSiblingHintRect(null);
9496
+ setSiblingHintRects([]);
9497
+ if (selectedElRef.current || toolbarVariantRef.current !== "none") {
9498
+ deselectRef.current();
9499
+ }
7777
9500
  if (hoveredImageRef.current) {
7778
9501
  hoveredImageRef.current = null;
7779
9502
  hoveredImageHasTextOverlapRef.current = false;
@@ -7800,6 +9523,7 @@ function OhhwellsBridge() {
7800
9523
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7801
9524
  postToParent2({ type: "ow:image-unhover" });
7802
9525
  return () => {
9526
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
7803
9527
  postToParent2({ type: "ow:link-modal-lock", locked: false });
7804
9528
  html.style.overflow = prevHtmlOverflow;
7805
9529
  body.style.overflow = prevBodyOverflow;
@@ -7807,7 +9531,7 @@ function OhhwellsBridge() {
7807
9531
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7808
9532
  };
7809
9533
  }, [linkPopover, postToParent2]);
7810
- (0, import_react8.useEffect)(() => {
9534
+ (0, import_react9.useEffect)(() => {
7811
9535
  if (!isEditMode) return;
7812
9536
  const useFixtures = shouldUseDevFixtures();
7813
9537
  if (useFixtures) {
@@ -7831,14 +9555,14 @@ function OhhwellsBridge() {
7831
9555
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
7832
9556
  return () => window.removeEventListener("message", onSitePages);
7833
9557
  }, [isEditMode, postToParent2]);
7834
- (0, import_react8.useEffect)(() => {
9558
+ (0, import_react9.useEffect)(() => {
7835
9559
  if (!isEditMode || shouldUseDevFixtures()) return;
7836
9560
  void loadAllSectionsManifest().then((manifest) => {
7837
9561
  if (Object.keys(manifest).length === 0) return;
7838
9562
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7839
9563
  });
7840
9564
  }, [isEditMode]);
7841
- (0, import_react8.useEffect)(() => {
9565
+ (0, import_react9.useEffect)(() => {
7842
9566
  const update = () => {
7843
9567
  const el = activeElRef.current ?? selectedElRef.current;
7844
9568
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7862,10 +9586,10 @@ function OhhwellsBridge() {
7862
9586
  vvp.removeEventListener("resize", update);
7863
9587
  };
7864
9588
  }, []);
7865
- const refreshStateRules = (0, import_react8.useCallback)(() => {
9589
+ const refreshStateRules = (0, import_react9.useCallback)(() => {
7866
9590
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7867
9591
  }, []);
7868
- const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
9592
+ const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
7869
9593
  const tracker = getSectionsTracker();
7870
9594
  let entries = [];
7871
9595
  try {
@@ -7888,7 +9612,7 @@ function OhhwellsBridge() {
7888
9612
  }
7889
9613
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7890
9614
  }, [isEditMode]);
7891
- const deactivate = (0, import_react8.useCallback)(() => {
9615
+ const deactivate = (0, import_react9.useCallback)(() => {
7892
9616
  const el = activeElRef.current;
7893
9617
  if (!el) return;
7894
9618
  const key = el.dataset.ohwKey;
@@ -7920,19 +9644,23 @@ function OhhwellsBridge() {
7920
9644
  setToolbarShowEditLink(false);
7921
9645
  postToParent2({ type: "ow:exit-edit" });
7922
9646
  }, [postToParent2]);
7923
- const clearSelectedAttr = (0, import_react8.useCallback)(() => {
9647
+ const clearSelectedAttr = (0, import_react9.useCallback)(() => {
7924
9648
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
7925
9649
  el.removeAttribute("data-ohw-selected");
7926
9650
  });
7927
9651
  }, []);
7928
- const deselect = (0, import_react8.useCallback)(() => {
9652
+ const deselect = (0, import_react9.useCallback)(() => {
7929
9653
  clearSelectedAttr();
7930
9654
  selectedElRef.current = null;
9655
+ selectedHrefKeyRef.current = null;
9656
+ selectedFooterColAttrRef.current = null;
7931
9657
  setSelectedIsCta(false);
7932
9658
  setReorderHrefKey(null);
7933
9659
  setReorderDragDisabled(false);
9660
+ setIsFooterFrameSelection(false);
7934
9661
  siblingHintElRef.current = null;
7935
9662
  setSiblingHintRect(null);
9663
+ setSiblingHintRects([]);
7936
9664
  setIsItemDragging(false);
7937
9665
  hoveredNavContainerRef.current = null;
7938
9666
  setHoveredNavContainerRect(null);
@@ -7941,12 +9669,57 @@ function OhhwellsBridge() {
7941
9669
  setToolbarVariant("none");
7942
9670
  }
7943
9671
  }, [clearSelectedAttr]);
7944
- const markSelected = (0, import_react8.useCallback)((el) => {
9672
+ const markSelected = (0, import_react9.useCallback)((el) => {
7945
9673
  clearSelectedAttr();
7946
9674
  el.setAttribute("data-ohw-selected", "");
7947
9675
  }, [clearSelectedAttr]);
7948
- const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
9676
+ const resolveHrefKeyElement = (0, import_react9.useCallback)((hrefKey) => {
9677
+ if (isFooterHrefKey(hrefKey)) {
9678
+ return document.querySelector(
9679
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9680
+ );
9681
+ }
9682
+ if (isNavbarHrefKey(hrefKey)) {
9683
+ const desktop = document.querySelector("[data-ohw-nav-container]");
9684
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
9685
+ return desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
9686
+ }
9687
+ return document.querySelector(
9688
+ `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9689
+ );
9690
+ }, []);
9691
+ const resyncSelectedNavigationItem = (0, import_react9.useCallback)(() => {
9692
+ const hrefKey = selectedHrefKeyRef.current;
9693
+ if (hrefKey) {
9694
+ const link = resolveHrefKeyElement(hrefKey);
9695
+ if (!link || !isNavigationItem(link)) return;
9696
+ selectedElRef.current = link;
9697
+ const { key, disabled } = getNavigationItemReorderState(link);
9698
+ setReorderHrefKey(key);
9699
+ setReorderDragDisabled(disabled);
9700
+ setIsFooterFrameSelection(false);
9701
+ setToolbarVariant("link-action");
9702
+ setToolbarRect(link.getBoundingClientRect());
9703
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(link));
9704
+ return;
9705
+ }
9706
+ const colAttr = selectedFooterColAttrRef.current;
9707
+ if (colAttr != null) {
9708
+ const column = document.querySelector(`[data-ohw-footer-col="${CSS.escape(colAttr)}"]`) ?? listFooterColumns()[Number(colAttr)] ?? null;
9709
+ if (!column || !isNavigationContainer(column)) return;
9710
+ selectedElRef.current = column;
9711
+ setIsFooterFrameSelection(true);
9712
+ setToolbarVariant("select-frame");
9713
+ setToolbarRect(column.getBoundingClientRect());
9714
+ setSiblingHintRects(
9715
+ listFooterColumns().filter((c) => c !== column).map((c) => c.getBoundingClientRect())
9716
+ );
9717
+ }
9718
+ }, [resolveHrefKeyElement]);
9719
+ const reselectNavigationItem = (0, import_react9.useCallback)((navAnchor) => {
7949
9720
  selectedElRef.current = navAnchor;
9721
+ selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9722
+ selectedFooterColAttrRef.current = null;
7950
9723
  markSelected(navAnchor);
7951
9724
  setSelectedIsCta(isNavbarButton2(navAnchor));
7952
9725
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
@@ -7954,10 +9727,11 @@ function OhhwellsBridge() {
7954
9727
  setReorderDragDisabled(disabled);
7955
9728
  setToolbarVariant("link-action");
7956
9729
  setToolbarRect(navAnchor.getBoundingClientRect());
9730
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(navAnchor));
7957
9731
  setToolbarShowEditLink(false);
7958
9732
  setActiveCommands(/* @__PURE__ */ new Set());
7959
9733
  }, [markSelected]);
7960
- const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
9734
+ const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
7961
9735
  const el = activeElRef.current;
7962
9736
  if (!el) return;
7963
9737
  const key = el.dataset.ohwKey;
@@ -7984,7 +9758,7 @@ function OhhwellsBridge() {
7984
9758
  postToParent2({ type: "ow:exit-edit" });
7985
9759
  reselectNavigationItem(navAnchor);
7986
9760
  }, [postToParent2, reselectNavigationItem]);
7987
- const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
9761
+ const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
7988
9762
  const items = listNavbarItems();
7989
9763
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7990
9764
  deselectRef.current();
@@ -7996,20 +9770,319 @@ function OhhwellsBridge() {
7996
9770
  intent: "add-nav"
7997
9771
  });
7998
9772
  }, []);
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)(() => {
9773
+ const handleAddFooterColumn = (0, import_react9.useCallback)(() => {
9774
+ deselectRef.current();
9775
+ deactivateRef.current();
9776
+ const result = addFooterColumnWithPersist({ postToParent: postToParent2 });
9777
+ editContentRef.current = {
9778
+ ...editContentRef.current,
9779
+ ...buildFooterColumnEditContentPatch(result)
9780
+ };
9781
+ requestAnimationFrame(() => {
9782
+ selectRef.current(result.firstLink);
9783
+ });
9784
+ }, [postToParent2]);
9785
+ const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
9786
+ footerDragRef.current = null;
9787
+ setSiblingHintRects([]);
9788
+ setFooterDropSlots([]);
9789
+ setActiveFooterDropIndex(null);
9790
+ setDraggedItemRect(null);
8005
9791
  setIsItemDragging(false);
9792
+ unlockFooterDragInteraction();
9793
+ }, []);
9794
+ const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
9795
+ const dragged = session.draggedEl;
9796
+ setDraggedItemRect(dragged.getBoundingClientRect());
9797
+ if (typeof clientX === "number" && typeof clientY === "number") {
9798
+ session.lastClientX = clientX;
9799
+ session.lastClientY = clientY;
9800
+ }
9801
+ session.activeSlot = activeSlot;
9802
+ setSiblingHintRects([]);
9803
+ if (session.kind === "link") {
9804
+ const columns = listFooterColumns();
9805
+ const slots2 = [];
9806
+ columns.forEach((col, i) => {
9807
+ slots2.push(...buildLinkDropSlots(col, i));
9808
+ });
9809
+ setFooterDropSlots(slots2);
9810
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
9811
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
9812
+ return;
9813
+ }
9814
+ const slots = buildColumnDropSlots();
9815
+ setFooterDropSlots(slots);
9816
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
9817
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
9818
+ }, []);
9819
+ const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
9820
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
9821
+ const commitFooterDragRef = (0, import_react9.useRef)(() => {
9822
+ });
9823
+ const beginFooterDragRef = (0, import_react9.useRef)(() => {
9824
+ });
9825
+ const beginFooterDrag = (0, import_react9.useCallback)(
9826
+ (session) => {
9827
+ const rect = session.draggedEl.getBoundingClientRect();
9828
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
9829
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
9830
+ session.activeSlot = session.activeSlot ?? null;
9831
+ footerDragRef.current = session;
9832
+ setIsItemDragging(true);
9833
+ lockFooterDuringDrag();
9834
+ siblingHintElRef.current = null;
9835
+ setSiblingHintRect(null);
9836
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
9837
+ setToolbarRect(rect);
9838
+ }
9839
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
9840
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
9841
+ },
9842
+ [refreshFooterDragVisuals]
9843
+ );
9844
+ beginFooterDragRef.current = beginFooterDrag;
9845
+ const commitFooterDrag = (0, import_react9.useCallback)(
9846
+ (clientX, clientY) => {
9847
+ const session = footerDragRef.current;
9848
+ if (!session) {
9849
+ clearFooterDragVisuals();
9850
+ return;
9851
+ }
9852
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
9853
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
9854
+ let nextOrder = null;
9855
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
9856
+ if (session.kind === "link" && session.hrefKey && slot) {
9857
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
9858
+ } else if (session.kind === "column" && slot) {
9859
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
9860
+ }
9861
+ const wasSelected = session.wasSelected;
9862
+ const draggedEl = session.draggedEl;
9863
+ const hrefKey = session.hrefKey;
9864
+ let movedColumnIndex = null;
9865
+ if (session.kind === "column" && slot && nextOrder) {
9866
+ let adjusted = slot.insertIndex;
9867
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
9868
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
9869
+ }
9870
+ const applySelectionAfterDrop = () => {
9871
+ if (!wasSelected) {
9872
+ deselectRef.current();
9873
+ return;
9874
+ }
9875
+ if (hrefKey) {
9876
+ selectedHrefKeyRef.current = hrefKey;
9877
+ selectedFooterColAttrRef.current = null;
9878
+ const link = resolveHrefKeyElement(hrefKey);
9879
+ if (link && isNavigationItem(link)) {
9880
+ selectRef.current(link);
9881
+ return;
9882
+ }
9883
+ }
9884
+ if (movedColumnIndex !== null && nextOrder) {
9885
+ const colKeys = nextOrder[movedColumnIndex] ?? [];
9886
+ let column = null;
9887
+ for (const key of colKeys) {
9888
+ const link = document.querySelector(
9889
+ `footer [data-ohw-href-key="${CSS.escape(key)}"]`
9890
+ );
9891
+ if (link) {
9892
+ column = findFooterColumnForLink(link);
9893
+ if (column) break;
9894
+ }
9895
+ }
9896
+ if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
9897
+ if (column && isNavigationContainer(column)) {
9898
+ selectFrameRef.current(column);
9899
+ return;
9900
+ }
9901
+ }
9902
+ if (document.body.contains(draggedEl)) {
9903
+ if (isNavigationItem(draggedEl)) {
9904
+ selectRef.current(draggedEl);
9905
+ return;
9906
+ }
9907
+ if (isNavigationContainer(draggedEl)) {
9908
+ selectFrameRef.current(draggedEl);
9909
+ return;
9910
+ }
9911
+ }
9912
+ deselectRef.current();
9913
+ };
9914
+ if (nextOrder) {
9915
+ const orderJson = JSON.stringify(nextOrder);
9916
+ editContentRef.current = {
9917
+ ...editContentRef.current,
9918
+ [FOOTER_ORDER_KEY]: orderJson
9919
+ };
9920
+ applyFooterOrder(nextOrder);
9921
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9922
+ postToParentRef.current({
9923
+ type: "ow:change",
9924
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
9925
+ });
9926
+ applySelectionAfterDrop();
9927
+ clearFooterDragVisuals();
9928
+ requestAnimationFrame(() => {
9929
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9930
+ applyFooterOrder(nextOrder);
9931
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9932
+ }
9933
+ applySelectionAfterDrop();
9934
+ requestAnimationFrame(() => {
9935
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9936
+ applyFooterOrder(nextOrder);
9937
+ }
9938
+ resyncSelectedNavigationItem();
9939
+ });
9940
+ });
9941
+ return;
9942
+ }
9943
+ applySelectionAfterDrop();
9944
+ clearFooterDragVisuals();
9945
+ },
9946
+ [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
9947
+ );
9948
+ commitFooterDragRef.current = commitFooterDrag;
9949
+ const startFooterLinkDrag = (0, import_react9.useCallback)(
9950
+ (anchor, clientX, clientY, wasSelected) => {
9951
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
9952
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
9953
+ const column = findFooterColumnForLink(anchor);
9954
+ const columns = listFooterColumns();
9955
+ beginFooterDrag({
9956
+ kind: "link",
9957
+ hrefKey,
9958
+ columnEl: column,
9959
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
9960
+ wasSelected,
9961
+ draggedEl: anchor,
9962
+ lastClientX: clientX,
9963
+ lastClientY: clientY,
9964
+ activeSlot: null
9965
+ });
9966
+ return true;
9967
+ },
9968
+ [beginFooterDrag]
9969
+ );
9970
+ const startFooterColumnDrag = (0, import_react9.useCallback)(
9971
+ (columnEl, clientX, clientY, wasSelected) => {
9972
+ const columns = listFooterColumns();
9973
+ const idx = columns.indexOf(columnEl);
9974
+ if (idx < 0) return false;
9975
+ beginFooterDrag({
9976
+ kind: "column",
9977
+ hrefKey: null,
9978
+ columnEl,
9979
+ sourceColumnIndex: idx,
9980
+ wasSelected,
9981
+ draggedEl: columnEl,
9982
+ lastClientX: clientX,
9983
+ lastClientY: clientY,
9984
+ activeSlot: null
9985
+ });
9986
+ return true;
9987
+ },
9988
+ [beginFooterDrag]
9989
+ );
9990
+ const handleItemDragStart = (0, import_react9.useCallback)(
9991
+ (e) => {
9992
+ const selected = selectedElRef.current;
9993
+ if (!selected) {
9994
+ setIsItemDragging(true);
9995
+ return;
9996
+ }
9997
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
9998
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
9999
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
10000
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
10001
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
10002
+ }
10003
+ if (startNavLinkDrag(selected, clientX, clientY, true)) return;
10004
+ siblingHintElRef.current = null;
10005
+ setSiblingHintRect(null);
10006
+ setIsItemDragging(true);
10007
+ },
10008
+ [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
10009
+ );
10010
+ const handleItemDragEnd = (0, import_react9.useCallback)(
10011
+ (e) => {
10012
+ if (footerDragRef.current) {
10013
+ const x = e?.clientX;
10014
+ const y = e?.clientY;
10015
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
10016
+ commitFooterDrag(x, y);
10017
+ } else {
10018
+ commitFooterDrag();
10019
+ }
10020
+ return;
10021
+ }
10022
+ if (navDragRef.current) {
10023
+ const x = e?.clientX;
10024
+ const y = e?.clientY;
10025
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
10026
+ commitNavDrag(x, y);
10027
+ } else {
10028
+ commitNavDrag();
10029
+ }
10030
+ return;
10031
+ }
10032
+ setIsItemDragging(false);
10033
+ },
10034
+ [commitFooterDrag, commitNavDrag, navDragRef]
10035
+ );
10036
+ const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
10037
+ if (e.button !== 0) return;
10038
+ const selected = selectedElRef.current;
10039
+ if (!selected) return;
10040
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
10041
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
10042
+ footerPointerDragRef.current = {
10043
+ el: selected,
10044
+ kind: "link",
10045
+ startX: e.clientX,
10046
+ startY: e.clientY,
10047
+ pointerId: e.pointerId,
10048
+ wasSelected: true,
10049
+ started: false
10050
+ };
10051
+ return;
10052
+ }
10053
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
10054
+ footerPointerDragRef.current = {
10055
+ el: selected,
10056
+ kind: "column",
10057
+ startX: e.clientX,
10058
+ startY: e.clientY,
10059
+ pointerId: e.pointerId,
10060
+ wasSelected: true,
10061
+ started: false
10062
+ };
10063
+ return;
10064
+ }
10065
+ if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
10066
+ }, [armNavPressFromChrome]);
10067
+ const handleItemChromeClick = (0, import_react9.useCallback)((clientX, clientY) => {
10068
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
10069
+ suppressNextClickRef.current = false;
10070
+ return;
10071
+ }
10072
+ const selected = selectedElRef.current;
10073
+ if (!selected || !isNavigationItem(selected)) return;
10074
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
10075
+ if (!editable) return;
10076
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
8006
10077
  }, []);
8007
10078
  reselectNavigationItemRef.current = reselectNavigationItem;
8008
10079
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
8009
- const select = (0, import_react8.useCallback)((anchor) => {
10080
+ const select = (0, import_react9.useCallback)((anchor) => {
8010
10081
  if (!isNavigationItem(anchor)) return;
8011
10082
  if (activeElRef.current) deactivate();
8012
10083
  selectedElRef.current = anchor;
10084
+ selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
10085
+ selectedFooterColAttrRef.current = null;
8013
10086
  markSelected(anchor);
8014
10087
  setSelectedIsCta(isNavbarButton2(anchor));
8015
10088
  clearHrefKeyHover(anchor);
@@ -8019,19 +10092,24 @@ function OhhwellsBridge() {
8019
10092
  hoveredItemElRef.current = null;
8020
10093
  siblingHintElRef.current = null;
8021
10094
  setSiblingHintRect(null);
10095
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(anchor));
8022
10096
  setIsItemDragging(false);
8023
10097
  const { key, disabled } = getNavigationItemReorderState(anchor);
8024
10098
  setReorderHrefKey(key);
8025
10099
  setReorderDragDisabled(disabled);
10100
+ setIsFooterFrameSelection(false);
8026
10101
  setToolbarVariant("link-action");
8027
10102
  setToolbarRect(anchor.getBoundingClientRect());
8028
10103
  setToolbarShowEditLink(false);
8029
10104
  setActiveCommands(/* @__PURE__ */ new Set());
8030
10105
  }, [deactivate, markSelected]);
8031
- const selectFrame = (0, import_react8.useCallback)((el) => {
10106
+ const selectFrame = (0, import_react9.useCallback)((el) => {
8032
10107
  if (!isNavigationContainer(el)) return;
8033
10108
  if (activeElRef.current) deactivate();
10109
+ const isFooterColumn = !isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el)));
8034
10110
  selectedElRef.current = el;
10111
+ selectedHrefKeyRef.current = null;
10112
+ selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
8035
10113
  markSelected(el);
8036
10114
  setSelectedIsCta(false);
8037
10115
  clearHrefKeyHover(el);
@@ -8041,18 +10119,24 @@ function OhhwellsBridge() {
8041
10119
  hoveredItemElRef.current = null;
8042
10120
  siblingHintElRef.current = null;
8043
10121
  setSiblingHintRect(null);
10122
+ setSiblingHintRects(
10123
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
10124
+ );
8044
10125
  setIsItemDragging(false);
8045
10126
  setReorderHrefKey(null);
8046
10127
  setReorderDragDisabled(false);
10128
+ setIsFooterFrameSelection(isFooterColumn);
8047
10129
  setToolbarVariant("select-frame");
8048
10130
  setToolbarRect(el.getBoundingClientRect());
8049
10131
  setToolbarShowEditLink(false);
8050
10132
  setActiveCommands(/* @__PURE__ */ new Set());
8051
10133
  }, [deactivate, markSelected]);
8052
- const activate = (0, import_react8.useCallback)((el, options) => {
10134
+ const activate = (0, import_react9.useCallback)((el, options) => {
8053
10135
  if (activeElRef.current === el) return;
8054
10136
  clearSelectedAttr();
8055
10137
  selectedElRef.current = null;
10138
+ selectedHrefKeyRef.current = null;
10139
+ selectedFooterColAttrRef.current = null;
8056
10140
  setSelectedIsCta(false);
8057
10141
  deactivate();
8058
10142
  if (hoveredImageRef.current) {
@@ -8062,7 +10146,10 @@ function OhhwellsBridge() {
8062
10146
  setToolbarVariant("rich-text");
8063
10147
  siblingHintElRef.current = null;
8064
10148
  setSiblingHintRect(null);
10149
+ setSiblingHintRects([]);
8065
10150
  setIsItemDragging(false);
10151
+ const preActivationSelection = window.getSelection();
10152
+ const preservedRange = preActivationSelection && preActivationSelection.rangeCount > 0 && !preActivationSelection.isCollapsed && el.contains(preActivationSelection.getRangeAt(0).commonAncestorContainer) ? preActivationSelection.getRangeAt(0).cloneRange() : null;
8066
10153
  el.setAttribute("contenteditable", "true");
8067
10154
  el.setAttribute("data-ohw-editing", "");
8068
10155
  el.removeAttribute("data-ohw-hovered");
@@ -8070,8 +10157,16 @@ function OhhwellsBridge() {
8070
10157
  activeElRef.current = el;
8071
10158
  originalContentRef.current = el.innerHTML;
8072
10159
  el.focus({ preventScroll: true });
8073
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8074
- placeCaretAtPoint(el, options.caretX, options.caretY);
10160
+ if (preservedRange) {
10161
+ const selection = window.getSelection();
10162
+ selection?.removeAllRanges();
10163
+ selection?.addRange(preservedRange);
10164
+ } else if (options?.caretX !== void 0 && options?.caretY !== void 0) {
10165
+ const { caretX, caretY } = options;
10166
+ placeCaretAtPoint(el, caretX, caretY);
10167
+ requestAnimationFrame(() => {
10168
+ if (activeElRef.current === el) placeCaretAtPoint(el, caretX, caretY);
10169
+ });
8075
10170
  } else {
8076
10171
  const selection = window.getSelection();
8077
10172
  if (selection && selection.rangeCount === 0) {
@@ -8101,7 +10196,7 @@ function OhhwellsBridge() {
8101
10196
  selectRef.current = select;
8102
10197
  selectFrameRef.current = selectFrame;
8103
10198
  deselectRef.current = deselect;
8104
- (0, import_react8.useLayoutEffect)(() => {
10199
+ (0, import_react9.useLayoutEffect)(() => {
8105
10200
  if (!subdomain || isEditMode) {
8106
10201
  setFetchState("done");
8107
10202
  return;
@@ -8142,6 +10237,7 @@ function OhhwellsBridge() {
8142
10237
  applyLinkByKey(key, val);
8143
10238
  }
8144
10239
  reconcileNavbarItemsFromContent(content);
10240
+ reconcileFooterOrderFromContent(content);
8145
10241
  enforceLinkHrefs();
8146
10242
  initSectionsFromContent(content, true);
8147
10243
  sectionsLoadedRef.current = true;
@@ -8170,7 +10266,7 @@ function OhhwellsBridge() {
8170
10266
  cancelled = true;
8171
10267
  };
8172
10268
  }, [subdomain, isEditMode]);
8173
- (0, import_react8.useEffect)(() => {
10269
+ (0, import_react9.useEffect)(() => {
8174
10270
  if (!subdomain || isEditMode) return;
8175
10271
  let debounceTimer = null;
8176
10272
  let observer = null;
@@ -8202,6 +10298,7 @@ function OhhwellsBridge() {
8202
10298
  applyLinkByKey(key, val);
8203
10299
  }
8204
10300
  reconcileNavbarItemsFromContent(content);
10301
+ reconcileFooterOrderFromContent(content);
8205
10302
  } finally {
8206
10303
  observer?.observe(document.body, { childList: true, subtree: true });
8207
10304
  }
@@ -8219,16 +10316,16 @@ function OhhwellsBridge() {
8219
10316
  if (debounceTimer) clearTimeout(debounceTimer);
8220
10317
  };
8221
10318
  }, [subdomain, isEditMode, pathname]);
8222
- (0, import_react8.useLayoutEffect)(() => {
10319
+ (0, import_react9.useLayoutEffect)(() => {
8223
10320
  const el = document.getElementById("ohw-loader");
8224
10321
  if (!el) return;
8225
10322
  const visible = Boolean(subdomain) && fetchState !== "done";
8226
10323
  el.style.display = visible ? "flex" : "none";
8227
10324
  }, [subdomain, fetchState]);
8228
- (0, import_react8.useEffect)(() => {
10325
+ (0, import_react9.useEffect)(() => {
8229
10326
  postToParent2({ type: "ow:navigation", path: pathname });
8230
10327
  }, [pathname, postToParent2]);
8231
- (0, import_react8.useEffect)(() => {
10328
+ (0, import_react9.useEffect)(() => {
8232
10329
  if (!isEditMode) return;
8233
10330
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8234
10331
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -8236,7 +10333,7 @@ function OhhwellsBridge() {
8236
10333
  deselectRef.current();
8237
10334
  deactivateRef.current();
8238
10335
  }, [pathname, isEditMode]);
8239
- (0, import_react8.useEffect)(() => {
10336
+ (0, import_react9.useEffect)(() => {
8240
10337
  const contentForNav = () => {
8241
10338
  if (isEditMode) return editContentRef.current;
8242
10339
  if (!subdomain) return {};
@@ -8248,6 +10345,7 @@ function OhhwellsBridge() {
8248
10345
  const roots = observeRoots();
8249
10346
  if (roots.length === 0) {
8250
10347
  reconcileNavbarItemsFromContent(contentForNav());
10348
+ reconcileFooterOrderFromContent(contentForNav());
8251
10349
  if (isEditMode) syncNavigationDragCursorAttrs();
8252
10350
  return;
8253
10351
  }
@@ -8260,11 +10358,26 @@ function OhhwellsBridge() {
8260
10358
  }
8261
10359
  };
8262
10360
  const run = () => {
8263
- if (applying) return;
10361
+ if (footerDragRef.current || navDragRef.current || applying) return;
8264
10362
  applying = true;
8265
10363
  observer?.disconnect();
8266
10364
  try {
8267
- reconcileNavbarItemsFromContent(contentForNav());
10365
+ const content = contentForNav();
10366
+ reconcileNavbarItemsFromContent(content);
10367
+ reconcileFooterOrderFromContent(content);
10368
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
10369
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
10370
+ disableNativeHrefDrag(el);
10371
+ }
10372
+ });
10373
+ document.querySelectorAll(
10374
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
10375
+ ).forEach((el) => {
10376
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
10377
+ disableNativeHrefDrag(el);
10378
+ }
10379
+ });
10380
+ resyncSelectedNavigationItem();
8268
10381
  if (isEditMode) syncNavigationDragCursorAttrs();
8269
10382
  } finally {
8270
10383
  applying = false;
@@ -8284,8 +10397,8 @@ function OhhwellsBridge() {
8284
10397
  if (rafId != null) cancelAnimationFrame(rafId);
8285
10398
  observer?.disconnect();
8286
10399
  };
8287
- }, [isEditMode, pathname, subdomain, fetchState]);
8288
- (0, import_react8.useEffect)(() => {
10400
+ }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
10401
+ (0, import_react9.useEffect)(() => {
8289
10402
  if (!isEditMode) return;
8290
10403
  const measure = () => {
8291
10404
  const h = document.body.scrollHeight;
@@ -8309,7 +10422,7 @@ function OhhwellsBridge() {
8309
10422
  window.removeEventListener("resize", handleResize);
8310
10423
  };
8311
10424
  }, [pathname, isEditMode, postToParent2]);
8312
- (0, import_react8.useEffect)(() => {
10425
+ (0, import_react9.useEffect)(() => {
8313
10426
  if (!subdomainFromQuery || isEditMode) return;
8314
10427
  const handleClick = (e) => {
8315
10428
  const anchor = e.target.closest("a");
@@ -8325,7 +10438,7 @@ function OhhwellsBridge() {
8325
10438
  document.addEventListener("click", handleClick, true);
8326
10439
  return () => document.removeEventListener("click", handleClick, true);
8327
10440
  }, [subdomainFromQuery, isEditMode, router]);
8328
- (0, import_react8.useEffect)(() => {
10441
+ (0, import_react9.useEffect)(() => {
8329
10442
  if (!isEditMode) {
8330
10443
  editStylesRef.current?.base.remove();
8331
10444
  editStylesRef.current?.forceHover.remove();
@@ -8387,6 +10500,21 @@ function OhhwellsBridge() {
8387
10500
  outline: none !important;
8388
10501
  outline-offset: 0 !important;
8389
10502
  }
10503
+ /* Editor chrome owns selection \u2014 suppress native focus rings on nav/footer items. */
10504
+ nav [data-ohw-href-key]:focus,
10505
+ nav [data-ohw-href-key]:focus-visible,
10506
+ nav [data-ohw-href-key]:focus-within,
10507
+ footer [data-ohw-href-key]:focus,
10508
+ footer [data-ohw-href-key]:focus-visible,
10509
+ footer [data-ohw-href-key]:focus-within,
10510
+ nav [data-ohw-href-key] [data-ohw-editable]:focus,
10511
+ nav [data-ohw-href-key] [data-ohw-editable]:focus-visible,
10512
+ footer [data-ohw-href-key] [data-ohw-editable]:focus,
10513
+ footer [data-ohw-href-key] [data-ohw-editable]:focus-visible {
10514
+ outline: none !important;
10515
+ outline-offset: 0 !important;
10516
+ box-shadow: none !important;
10517
+ }
8390
10518
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
8391
10519
  [data-ohw-editing],
8392
10520
  [data-ohw-editing] *,
@@ -8428,12 +10556,39 @@ function OhhwellsBridge() {
8428
10556
  syncNavigationDragCursorAttrs();
8429
10557
  refreshStateRules();
8430
10558
  const handleClick = (e) => {
10559
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
10560
+ suppressNextClickRef.current = false;
10561
+ e.preventDefault();
10562
+ e.stopPropagation();
10563
+ return;
10564
+ }
8431
10565
  const target = e.target;
8432
10566
  if (target.closest("[data-ohw-toolbar]")) return;
8433
10567
  if (target.closest("[data-ohw-state-toggle]")) return;
8434
10568
  if (target.closest("[data-ohw-max-badge]")) return;
8435
10569
  if (isInsideLinkEditor(target)) return;
8436
10570
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
10571
+ if (target.closest("[data-ohw-item-drag-surface]")) {
10572
+ e.preventDefault();
10573
+ e.stopPropagation();
10574
+ const selected = selectedElRef.current;
10575
+ if (selected && isNavigationItem(selected)) {
10576
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
10577
+ if (editable2) {
10578
+ activateRef.current(editable2, {
10579
+ caretX: e.clientX,
10580
+ caretY: e.clientY
10581
+ });
10582
+ }
10583
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
10584
+ const link = listFooterLinksInColumn(selected).find((el) => {
10585
+ const r2 = el.getBoundingClientRect();
10586
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
10587
+ });
10588
+ if (link) selectRef.current(link);
10589
+ }
10590
+ return;
10591
+ }
8437
10592
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8438
10593
  if (navFromChrome) {
8439
10594
  e.preventDefault();
@@ -8462,7 +10617,9 @@ function OhhwellsBridge() {
8462
10617
  if (inActive || inActiveNav) {
8463
10618
  e.preventDefault();
8464
10619
  e.stopPropagation();
8465
- if (e.detail < 2) {
10620
+ const selection = window.getSelection();
10621
+ const hasRangeSelection = Boolean(selection && !selection.isCollapsed);
10622
+ if (e.detail < 2 && !hasRangeSelection) {
8466
10623
  placeCaretAtPoint(active, e.clientX, e.clientY);
8467
10624
  refreshActiveCommandsRef.current();
8468
10625
  }
@@ -8505,17 +10662,45 @@ function OhhwellsBridge() {
8505
10662
  }
8506
10663
  e.preventDefault();
8507
10664
  e.stopPropagation();
8508
- activateRef.current(editable);
10665
+ activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
8509
10666
  return;
8510
10667
  }
8511
10668
  const hrefAnchor = getNavigationItemAnchor(target);
8512
10669
  if (hrefAnchor) {
8513
10670
  e.preventDefault();
8514
10671
  e.stopPropagation();
8515
- if (selectedElRef.current === hrefAnchor) return;
10672
+ if (selectedElRef.current === hrefAnchor) {
10673
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
10674
+ if (textEditable) {
10675
+ activateRef.current(textEditable, {
10676
+ caretX: e.clientX,
10677
+ caretY: e.clientY
10678
+ });
10679
+ }
10680
+ return;
10681
+ }
8516
10682
  selectRef.current(hrefAnchor);
8517
10683
  return;
8518
10684
  }
10685
+ const footerColClick = target.closest("[data-ohw-footer-col]");
10686
+ if (footerColClick) {
10687
+ e.preventDefault();
10688
+ e.stopPropagation();
10689
+ selectFrameRef.current(footerColClick);
10690
+ return;
10691
+ }
10692
+ const footerLinksToSelect = resolveFooterLinksContainerSelectionTarget(
10693
+ target,
10694
+ e.clientX,
10695
+ e.clientY,
10696
+ isPointOverNavItem
10697
+ );
10698
+ if (footerLinksToSelect) {
10699
+ e.preventDefault();
10700
+ e.stopPropagation();
10701
+ selectFrameRef.current(footerLinksToSelect);
10702
+ return;
10703
+ }
8519
10704
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8520
10705
  if (navContainerToSelect) {
8521
10706
  e.preventDefault();
@@ -8574,8 +10759,8 @@ function OhhwellsBridge() {
8574
10759
  return;
8575
10760
  }
8576
10761
  const navLabel = getNavigationLabelEditable(target);
8577
- if (!navLabel) return;
8578
- const { editable } = navLabel;
10762
+ const editable = navLabel?.editable ?? target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
10763
+ if (!editable || isMediaEditable(editable) || editable.dataset.ohwEditable === "link") return;
8579
10764
  e.preventDefault();
8580
10765
  e.stopPropagation();
8581
10766
  if (activeElRef.current !== editable) {
@@ -8589,16 +10774,45 @@ function OhhwellsBridge() {
8589
10774
  const handleMouseOver = (e) => {
8590
10775
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8591
10776
  const target = e.target;
8592
- if (toolbarVariantRef.current !== "select-frame") {
8593
- const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-column]");
8594
- if (frameContainer && !getNavigationItemAnchor(target)) {
8595
- hoveredNavContainerRef.current = frameContainer;
8596
- setHoveredNavContainerRect(frameContainer.getBoundingClientRect());
8597
- hoveredItemElRef.current = null;
8598
- setHoveredItemRect(null);
8599
- return;
10777
+ if (linkPopoverOpenRef.current) {
10778
+ hoveredItemElRef.current = null;
10779
+ setHoveredItemRect(null);
10780
+ hoveredNavContainerRef.current = null;
10781
+ setHoveredNavContainerRect(null);
10782
+ return;
10783
+ }
10784
+ {
10785
+ const selected2 = selectedElRef.current;
10786
+ const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup(selected2)));
10787
+ const allowNavContainerHover = toolbarVariantRef.current !== "select-frame";
10788
+ const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
10789
+ if (allowNavContainerHover) {
10790
+ const navContainer = target.closest("[data-ohw-nav-container]");
10791
+ if (navContainer && !getNavigationItemAnchor(target)) {
10792
+ hoveredNavContainerRef.current = navContainer;
10793
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
10794
+ hoveredItemElRef.current = null;
10795
+ setHoveredItemRect(null);
10796
+ return;
10797
+ }
8600
10798
  }
8601
- if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10799
+ if (allowFooterLinksHover) {
10800
+ const navContainer = target.closest("[data-ohw-nav-container]");
10801
+ if (!navContainer) {
10802
+ const footerLinks = target.closest("[data-ohw-footer-links], .rb-footer-links") ?? null;
10803
+ if (footerLinks && selected2 !== footerLinks && !getNavigationItemAnchor(target) && !target.closest("[data-ohw-footer-col], [data-ohw-footer-column]")) {
10804
+ hoveredNavContainerRef.current = footerLinks;
10805
+ setHoveredNavContainerRect(footerLinks.getBoundingClientRect());
10806
+ hoveredItemElRef.current = null;
10807
+ setHoveredItemRect(null);
10808
+ return;
10809
+ }
10810
+ if (!footerLinks) {
10811
+ hoveredNavContainerRef.current = null;
10812
+ setHoveredNavContainerRect(null);
10813
+ }
10814
+ }
10815
+ } else if (toolbarVariantRef.current === "select-frame") {
8602
10816
  hoveredNavContainerRef.current = null;
8603
10817
  setHoveredNavContainerRect(null);
8604
10818
  }
@@ -8608,12 +10822,21 @@ function OhhwellsBridge() {
8608
10822
  hoveredNavContainerRef.current = null;
8609
10823
  setHoveredNavContainerRect(null);
8610
10824
  const selected2 = selectedElRef.current;
8611
- if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
10825
+ if (selected2 === navAnchor) return;
8612
10826
  clearHrefKeyHover(navAnchor);
8613
10827
  hoveredItemElRef.current = navAnchor;
8614
10828
  setHoveredItemRect(navAnchor.getBoundingClientRect());
8615
10829
  return;
8616
10830
  }
10831
+ const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10832
+ if (footerCol) {
10833
+ hoveredNavContainerRef.current = null;
10834
+ setHoveredNavContainerRect(null);
10835
+ if (selectedElRef.current === footerCol) return;
10836
+ hoveredItemElRef.current = footerCol;
10837
+ setHoveredItemRect(footerCol.getBoundingClientRect());
10838
+ return;
10839
+ }
8617
10840
  const editable = target.closest("[data-ohw-editable]");
8618
10841
  if (!editable) return;
8619
10842
  const selected = selectedElRef.current;
@@ -8631,14 +10854,25 @@ function OhhwellsBridge() {
8631
10854
  };
8632
10855
  const handleMouseOut = (e) => {
8633
10856
  const target = e.target;
8634
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10857
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column], [data-ohw-footer-links], .rb-footer-links")) {
8635
10858
  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]")) {
10859
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column], [data-ohw-footer-links], .rb-footer-links, [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button], [data-ohw-footer-container-chrome], [data-ohw-footer-add-button]")) {
8637
10860
  return;
8638
10861
  }
8639
10862
  hoveredNavContainerRef.current = null;
8640
10863
  setHoveredNavContainerRect(null);
8641
10864
  }
10865
+ const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10866
+ if (footerCol && hoveredItemElRef.current === footerCol) {
10867
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10868
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
10869
+ if (related2?.closest("[data-ohw-footer-col], [data-ohw-footer-column]") === footerCol) {
10870
+ return;
10871
+ }
10872
+ hoveredItemElRef.current = null;
10873
+ setHoveredItemRect(null);
10874
+ return;
10875
+ }
8642
10876
  const navAnchor = getNavigationItemAnchor(target);
8643
10877
  if (navAnchor) {
8644
10878
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8717,6 +10951,15 @@ function OhhwellsBridge() {
8717
10951
  if (track) track.setAttribute("data-ohw-hover-paused", "");
8718
10952
  };
8719
10953
  const clearImageHover = () => setMediaHover(null);
10954
+ const dismissImageHover = () => {
10955
+ if (hoveredImageRef.current) {
10956
+ hoveredImageRef.current = null;
10957
+ hoveredImageHasTextOverlapRef.current = false;
10958
+ resumeAnimTracks();
10959
+ postToParentRef.current({ type: "ow:image-unhover" });
10960
+ }
10961
+ clearImageHover();
10962
+ };
8720
10963
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
8721
10964
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8722
10965
  const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
@@ -8760,26 +11003,21 @@ function OhhwellsBridge() {
8760
11003
  }
8761
11004
  return smallest;
8762
11005
  };
8763
- const dismissImageHover = () => {
8764
- if (hoveredImageRef.current) {
8765
- hoveredImageRef.current = null;
8766
- hoveredImageHasTextOverlapRef.current = false;
8767
- resumeAnimTracks();
8768
- postToParentRef.current({ type: "ow:image-unhover" });
8769
- }
8770
- };
8771
11006
  const probeNavigationHoverAt = (x, y) => {
8772
- if (toolbarVariantRef.current === "select-frame") {
11007
+ const selected = selectedElRef.current;
11008
+ const selectedIsFooterColumn = Boolean(selected) && !isFooterLinksContainer(selected) && (selected.hasAttribute("data-ohw-footer-col") || selected.hasAttribute("data-ohw-footer-column") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)));
11009
+ const selectedIsFooterLinks = Boolean(selected && isFooterLinksContainer(selected));
11010
+ const allowNavContainerHover = toolbarVariantRef.current !== "select-frame";
11011
+ const allowFooterLinksHover = (toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn) && !selectedIsFooterLinks;
11012
+ if (toolbarVariantRef.current === "select-frame" && !allowFooterLinksHover) {
8773
11013
  hoveredNavContainerRef.current = null;
8774
11014
  setHoveredNavContainerRect(null);
8775
- return;
8776
11015
  }
8777
- const frameContainers = [
8778
- ...document.querySelectorAll("[data-ohw-nav-container]"),
8779
- ...document.querySelectorAll("[data-ohw-footer-column]")
8780
- ];
8781
- let overFrame = null;
8782
- for (const container of frameContainers) {
11016
+ const navContainers = Array.from(
11017
+ document.querySelectorAll("[data-ohw-nav-container]")
11018
+ );
11019
+ const footerColumns = listFooterColumns();
11020
+ for (const container of [...navContainers, ...footerColumns]) {
8783
11021
  const containerRect = container.getBoundingClientRect();
8784
11022
  const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8785
11023
  if (!overContainer) continue;
@@ -8793,26 +11031,66 @@ function OhhwellsBridge() {
8793
11031
  if (navItemHit) {
8794
11032
  hoveredNavContainerRef.current = null;
8795
11033
  setHoveredNavContainerRect(null);
8796
- const selected = selectedElRef.current;
8797
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
11034
+ if (selected !== navItemHit) {
8798
11035
  clearHrefKeyHover(navItemHit);
8799
11036
  hoveredItemElRef.current = navItemHit;
8800
11037
  setHoveredItemRect(navItemHit.getBoundingClientRect());
11038
+ } else if (hoveredItemElRef.current === navItemHit) {
11039
+ hoveredItemElRef.current = null;
11040
+ setHoveredItemRect(null);
8801
11041
  }
8802
11042
  return;
8803
11043
  }
8804
- overFrame = container;
8805
- break;
8806
11044
  }
8807
- if (!overFrame) {
8808
- hoveredNavContainerRef.current = null;
8809
- setHoveredNavContainerRect(null);
11045
+ if (allowNavContainerHover) {
11046
+ for (const container of navContainers) {
11047
+ const containerRect = container.getBoundingClientRect();
11048
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
11049
+ if (!overContainer) continue;
11050
+ if (isPointOverNavItem(container, x, y)) continue;
11051
+ hoveredNavContainerRef.current = container;
11052
+ setHoveredNavContainerRect(container.getBoundingClientRect());
11053
+ hoveredItemElRef.current = null;
11054
+ setHoveredItemRect(null);
11055
+ return;
11056
+ }
11057
+ }
11058
+ if (allowFooterLinksHover) {
11059
+ const footerLinks = getFooterLinksContainer();
11060
+ if (footerLinks) {
11061
+ const containerRect = footerLinks.getBoundingClientRect();
11062
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
11063
+ if (overContainer && !isPointOverNavItem(footerLinks, x, y) && !isPointOverFooterColumn(x, y)) {
11064
+ hoveredNavContainerRef.current = footerLinks;
11065
+ setHoveredNavContainerRect(footerLinks.getBoundingClientRect());
11066
+ hoveredItemElRef.current = null;
11067
+ setHoveredItemRect(null);
11068
+ return;
11069
+ }
11070
+ }
11071
+ }
11072
+ hoveredNavContainerRef.current = null;
11073
+ setHoveredNavContainerRect(null);
11074
+ for (const column of footerColumns) {
11075
+ const containerRect = column.getBoundingClientRect();
11076
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
11077
+ if (!overContainer) continue;
11078
+ if (isPointOverNavItem(column, x, y)) continue;
11079
+ if (selectedElRef.current === column) {
11080
+ if (hoveredItemElRef.current === column) {
11081
+ hoveredItemElRef.current = null;
11082
+ setHoveredItemRect(null);
11083
+ }
11084
+ return;
11085
+ }
11086
+ hoveredItemElRef.current = column;
11087
+ setHoveredItemRect(column.getBoundingClientRect());
8810
11088
  return;
8811
11089
  }
8812
- hoveredNavContainerRef.current = overFrame;
8813
- setHoveredNavContainerRect(overFrame.getBoundingClientRect());
8814
- hoveredItemElRef.current = null;
8815
- setHoveredItemRect(null);
11090
+ if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
11091
+ hoveredItemElRef.current = null;
11092
+ setHoveredItemRect(null);
11093
+ }
8816
11094
  };
8817
11095
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
8818
11096
  if (linkPopoverOpenRef.current) {
@@ -8833,6 +11111,10 @@ function OhhwellsBridge() {
8833
11111
  }
8834
11112
  hoveredNavContainerRef.current = null;
8835
11113
  setHoveredNavContainerRect(null);
11114
+ if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
11115
+ hoveredItemElRef.current = null;
11116
+ setHoveredItemRect(null);
11117
+ }
8836
11118
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
8837
11119
  if (toggleEl) {
8838
11120
  const tr = toggleEl.getBoundingClientRect();
@@ -9080,6 +11362,15 @@ function OhhwellsBridge() {
9080
11362
  probeHoverCardsAt(clientX, clientY);
9081
11363
  };
9082
11364
  const handleDragOver = (e) => {
11365
+ const footerSession = footerDragRef.current;
11366
+ if (footerSession) {
11367
+ e.preventDefault();
11368
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
11369
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
11370
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
11371
+ return;
11372
+ }
11373
+ if (onNavDragOver(e)) return;
9083
11374
  e.preventDefault();
9084
11375
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9085
11376
  if (!el) {
@@ -9286,6 +11577,7 @@ function OhhwellsBridge() {
9286
11577
  }
9287
11578
  editContentRef.current = { ...editContentRef.current, ...content };
9288
11579
  reconcileNavbarItemsFromContent(editContentRef.current);
11580
+ reconcileFooterOrderFromContent(editContentRef.current);
9289
11581
  syncNavigationDragCursorAttrs();
9290
11582
  enforceLinkHrefs();
9291
11583
  postToParentRef.current({ type: "ow:hydrate-done" });
@@ -9340,6 +11632,11 @@ function OhhwellsBridge() {
9340
11632
  }
9341
11633
  if (selectedElRef.current) {
9342
11634
  if (toolbarVariantRef.current === "select-frame") {
11635
+ const parent2 = getNavigationSelectionParent(selectedElRef.current);
11636
+ if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
11637
+ selectFrameRef.current(parent2);
11638
+ return;
11639
+ }
9343
11640
  deselectRef.current();
9344
11641
  return;
9345
11642
  }
@@ -9355,13 +11652,10 @@ function OhhwellsBridge() {
9355
11652
  const handleKeyDown = (e) => {
9356
11653
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
9357
11654
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
9358
- const navAnchor = getNavigationItemAnchor(activeElRef.current);
9359
- if (navAnchor) {
9360
- e.preventDefault();
9361
- selectAllTextInEditable(activeElRef.current);
9362
- refreshActiveCommandsRef.current();
9363
- return;
9364
- }
11655
+ e.preventDefault();
11656
+ selectAllTextInEditable(activeElRef.current);
11657
+ refreshActiveCommandsRef.current();
11658
+ return;
9365
11659
  }
9366
11660
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
9367
11661
  setLinkPopoverRef.current(null);
@@ -9369,6 +11663,12 @@ function OhhwellsBridge() {
9369
11663
  }
9370
11664
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
9371
11665
  if (toolbarVariantRef.current === "select-frame") {
11666
+ const parent2 = getNavigationSelectionParent(selectedElRef.current);
11667
+ if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
11668
+ e.preventDefault();
11669
+ selectFrameRef.current(parent2);
11670
+ return;
11671
+ }
9372
11672
  deselectRef.current();
9373
11673
  return;
9374
11674
  }
@@ -9443,6 +11743,26 @@ function OhhwellsBridge() {
9443
11743
  if (siblingHintElRef.current) {
9444
11744
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9445
11745
  }
11746
+ const selected = selectedElRef.current;
11747
+ if (selected && !footerDragRef.current && !navDragRef.current) {
11748
+ if (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected))) {
11749
+ setSiblingHintRects(
11750
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
11751
+ );
11752
+ } else if (isNavigationItem(selected)) {
11753
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(selected));
11754
+ }
11755
+ }
11756
+ if (footerDragRef.current) {
11757
+ const session = footerDragRef.current;
11758
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
11759
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11760
+ }
11761
+ if (navDragRef.current) {
11762
+ const session = navDragRef.current;
11763
+ const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
11764
+ refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11765
+ }
9446
11766
  if (hoveredImageRef.current) {
9447
11767
  const el = hoveredImageRef.current;
9448
11768
  const r2 = el.getBoundingClientRect();
@@ -9453,7 +11773,7 @@ function OhhwellsBridge() {
9453
11773
  };
9454
11774
  const handleSave = (e) => {
9455
11775
  if (e.data?.type !== "ow:save") return;
9456
- const nodes = collectEditableNodes();
11776
+ const nodes = collectEditableNodes(editContentRef.current);
9457
11777
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9458
11778
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9459
11779
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9511,30 +11831,30 @@ function OhhwellsBridge() {
9511
11831
  const h = document.documentElement.scrollHeight;
9512
11832
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
9513
11833
  };
11834
+ let selectionChangeRaf = null;
11835
+ const runSelectionChange = () => {
11836
+ selectionChangeRaf = null;
11837
+ const activeRoot = activeElRef.current;
11838
+ if (!activeRoot) return;
11839
+ const startEl = resolveSelectionStartElement(window.getSelection());
11840
+ const next = startEl ? detectActiveFormats(startEl, activeRoot) : /* @__PURE__ */ new Set();
11841
+ setActiveCommands((prev) => setsEqual(prev, next) ? prev : next);
11842
+ };
9514
11843
  const handleSelectionChange = () => {
9515
- if (!activeElRef.current) return;
9516
- const next = /* @__PURE__ */ new Set();
9517
- for (const cmd of ["bold", "italic", "underline", "strikeThrough", "insertUnorderedList", "insertOrderedList"]) {
9518
- try {
9519
- if (document.queryCommandState(cmd)) next.add(cmd);
9520
- } catch {
9521
- }
9522
- }
9523
- const sel = window.getSelection();
9524
- const anchor = sel?.anchorNode;
9525
- if (anchor) {
9526
- const el = anchor.nodeType === Node.TEXT_NODE ? anchor.parentElement : anchor;
9527
- const block = el?.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? el;
9528
- if (block) {
9529
- const align = getComputedStyle(block).textAlign;
9530
- if (align === "center") next.add("justifyCenter");
9531
- else if (align === "right" || align === "end") next.add("justifyRight");
9532
- else next.add("justifyLeft");
9533
- }
9534
- }
9535
- setActiveCommands(next);
11844
+ if (pointerHeldRef.current) return;
11845
+ if (selectionChangeRaf !== null) return;
11846
+ selectionChangeRaf = requestAnimationFrame(runSelectionChange);
11847
+ };
11848
+ const markPointerHeld = (e) => {
11849
+ if (e.button !== 0) return;
11850
+ pointerHeldRef.current = true;
11851
+ };
11852
+ const markPointerReleased = () => {
11853
+ if (!pointerHeldRef.current) return;
11854
+ pointerHeldRef.current = false;
11855
+ handleSelectionChange();
9536
11856
  };
9537
- refreshActiveCommandsRef.current = handleSelectionChange;
11857
+ refreshActiveCommandsRef.current = runSelectionChange;
9538
11858
  const handleDocMouseLeave = () => {
9539
11859
  hoveredImageRef.current = null;
9540
11860
  setMediaHover(null);
@@ -9676,7 +11996,7 @@ function OhhwellsBridge() {
9676
11996
  }
9677
11997
  }
9678
11998
  const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
9679
- if (footerColumn?.hasAttribute("data-ohw-footer-column")) {
11999
+ if (footerColumn?.hasAttribute("data-ohw-footer-col") || footerColumn?.hasAttribute("data-ohw-footer-column")) {
9680
12000
  selectFrameRef.current(footerColumn);
9681
12001
  return;
9682
12002
  }
@@ -9751,7 +12071,161 @@ function OhhwellsBridge() {
9751
12071
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9752
12072
  };
9753
12073
  }, [isEditMode, refreshStateRules]);
9754
- (0, import_react8.useEffect)(() => {
12074
+ (0, import_react9.useEffect)(() => {
12075
+ if (!isEditMode) return;
12076
+ const THRESHOLD = 10;
12077
+ const resolveWasSelected = (el) => {
12078
+ if (selectedElRef.current === el) return true;
12079
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
12080
+ return activeAnchor === el;
12081
+ };
12082
+ const onPointerDown = (e) => {
12083
+ if (e.button !== 0) return;
12084
+ if (suppressNextClickRef.current && Date.now() >= suppressClickUntilRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
12085
+ suppressNextClickRef.current = false;
12086
+ }
12087
+ if (footerDragRef.current) return;
12088
+ const target = e.target;
12089
+ if (!target) return;
12090
+ 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]')) {
12091
+ return;
12092
+ }
12093
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
12094
+ const anchor = getNavigationItemAnchor(target);
12095
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
12096
+ if (anchor && isFooterHrefKey(hrefKey)) {
12097
+ footerPointerDragRef.current = {
12098
+ el: anchor,
12099
+ kind: "link",
12100
+ startX: e.clientX,
12101
+ startY: e.clientY,
12102
+ pointerId: e.pointerId,
12103
+ wasSelected: resolveWasSelected(anchor),
12104
+ started: false
12105
+ };
12106
+ return;
12107
+ }
12108
+ const col = target.closest("[data-ohw-footer-col]");
12109
+ if (col && !getNavigationItemAnchor(target)) {
12110
+ footerPointerDragRef.current = {
12111
+ el: col,
12112
+ kind: "column",
12113
+ startX: e.clientX,
12114
+ startY: e.clientY,
12115
+ pointerId: e.pointerId,
12116
+ wasSelected: resolveWasSelected(col),
12117
+ started: false
12118
+ };
12119
+ }
12120
+ };
12121
+ const onPointerMove = (e) => {
12122
+ const pending = footerPointerDragRef.current;
12123
+ if (!pending) return;
12124
+ if (pending.started) {
12125
+ e.preventDefault();
12126
+ clearTextSelection();
12127
+ const session = footerDragRef.current;
12128
+ if (!session) return;
12129
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
12130
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
12131
+ return;
12132
+ }
12133
+ const dx = e.clientX - pending.startX;
12134
+ const dy = e.clientY - pending.startY;
12135
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
12136
+ e.preventDefault();
12137
+ pending.started = true;
12138
+ armFooterPressDrag();
12139
+ clearTextSelection();
12140
+ try {
12141
+ document.body.setPointerCapture(pending.pointerId);
12142
+ } catch {
12143
+ }
12144
+ if (linkPopoverOpenRef.current) {
12145
+ setLinkPopoverRef.current(null);
12146
+ }
12147
+ if (activeElRef.current) {
12148
+ deactivateRef.current();
12149
+ }
12150
+ if (pending.kind === "link") {
12151
+ const key = pending.el.getAttribute("data-ohw-href-key");
12152
+ if (!key) return;
12153
+ const column = findFooterColumnForLink(pending.el);
12154
+ const columns2 = listFooterColumns();
12155
+ beginFooterDragRef.current({
12156
+ kind: "link",
12157
+ hrefKey: key,
12158
+ columnEl: column,
12159
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
12160
+ wasSelected: pending.wasSelected,
12161
+ draggedEl: pending.el,
12162
+ lastClientX: e.clientX,
12163
+ lastClientY: e.clientY,
12164
+ activeSlot: null
12165
+ });
12166
+ return;
12167
+ }
12168
+ const columns = listFooterColumns();
12169
+ const idx = columns.indexOf(pending.el);
12170
+ if (idx < 0) return;
12171
+ beginFooterDragRef.current({
12172
+ kind: "column",
12173
+ hrefKey: null,
12174
+ columnEl: pending.el,
12175
+ sourceColumnIndex: idx,
12176
+ wasSelected: pending.wasSelected,
12177
+ draggedEl: pending.el,
12178
+ lastClientX: e.clientX,
12179
+ lastClientY: e.clientY,
12180
+ activeSlot: null
12181
+ });
12182
+ };
12183
+ const endPointerDrag = (e) => {
12184
+ const pending = footerPointerDragRef.current;
12185
+ footerPointerDragRef.current = null;
12186
+ try {
12187
+ if (document.body.hasPointerCapture(e.pointerId)) {
12188
+ document.body.releasePointerCapture(e.pointerId);
12189
+ }
12190
+ } catch {
12191
+ }
12192
+ if (!pending) return;
12193
+ if (!pending.started) {
12194
+ unlockFooterDragInteraction();
12195
+ return;
12196
+ }
12197
+ suppressNextClickRef.current = true;
12198
+ suppressClickUntilRef.current = Date.now() + 500;
12199
+ commitFooterDragRef.current(e.clientX, e.clientY);
12200
+ };
12201
+ const blockSelectStart = (e) => {
12202
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
12203
+ e.preventDefault();
12204
+ }
12205
+ };
12206
+ const onDragEnd = (_e) => {
12207
+ if (!footerDragRef.current) return;
12208
+ suppressNextClickRef.current = true;
12209
+ suppressClickUntilRef.current = Date.now() + 500;
12210
+ commitFooterDragRef.current();
12211
+ };
12212
+ document.addEventListener("pointerdown", onPointerDown, true);
12213
+ document.addEventListener("pointermove", onPointerMove, true);
12214
+ document.addEventListener("pointerup", endPointerDrag, true);
12215
+ document.addEventListener("pointercancel", endPointerDrag, true);
12216
+ document.addEventListener("selectstart", blockSelectStart, true);
12217
+ document.addEventListener("dragend", onDragEnd, true);
12218
+ return () => {
12219
+ document.removeEventListener("pointerdown", onPointerDown, true);
12220
+ document.removeEventListener("pointermove", onPointerMove, true);
12221
+ document.removeEventListener("pointerup", endPointerDrag, true);
12222
+ document.removeEventListener("pointercancel", endPointerDrag, true);
12223
+ document.removeEventListener("selectstart", blockSelectStart, true);
12224
+ document.removeEventListener("dragend", onDragEnd, true);
12225
+ unlockFooterDragInteraction();
12226
+ };
12227
+ }, [isEditMode]);
12228
+ (0, import_react9.useEffect)(() => {
9755
12229
  const handler = (e) => {
9756
12230
  if (e.data?.type !== "ow:request-schedule-config") return;
9757
12231
  const insertAfterVal = e.data.insertAfter;
@@ -9767,7 +12241,7 @@ function OhhwellsBridge() {
9767
12241
  window.addEventListener("message", handler);
9768
12242
  return () => window.removeEventListener("message", handler);
9769
12243
  }, [processConfigRequest]);
9770
- (0, import_react8.useEffect)(() => {
12244
+ (0, import_react9.useEffect)(() => {
9771
12245
  if (!isEditMode) return;
9772
12246
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9773
12247
  el.removeAttribute("data-ohw-active-state");
@@ -9788,7 +12262,13 @@ function OhhwellsBridge() {
9788
12262
  const next = { ...prev, [pathKey]: sections };
9789
12263
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9790
12264
  });
9791
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
12265
+ postToParent2({
12266
+ type: "ow:ready",
12267
+ version: "1",
12268
+ path: pathname,
12269
+ nodes: collectEditableNodes(editContentRef.current),
12270
+ sections
12271
+ });
9792
12272
  postToParent2({ type: "ow:request-site-pages" });
9793
12273
  }, 150);
9794
12274
  return () => {
@@ -9796,19 +12276,49 @@ function OhhwellsBridge() {
9796
12276
  clearTimeout(timer);
9797
12277
  };
9798
12278
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9799
- (0, import_react8.useEffect)(() => {
12279
+ (0, import_react9.useEffect)(() => {
9800
12280
  scrollToHashSectionWhenReady();
9801
12281
  const onHashChange = () => scrollToHashSectionWhenReady();
9802
12282
  window.addEventListener("hashchange", onHashChange);
9803
12283
  return () => window.removeEventListener("hashchange", onHashChange);
9804
12284
  }, [pathname]);
9805
- const handleCommand = (0, import_react8.useCallback)((cmd) => {
12285
+ const handleCommand = (0, import_react9.useCallback)((cmd) => {
12286
+ const el = activeElRef.current;
12287
+ const selBefore = window.getSelection();
12288
+ let savedOffsets = null;
12289
+ if (el && selBefore && selBefore.rangeCount > 0 && !selBefore.isCollapsed) {
12290
+ const r2 = selBefore.getRangeAt(0);
12291
+ if (el.contains(r2.commonAncestorContainer)) {
12292
+ savedOffsets = {
12293
+ start: textOffsetInRoot(el, r2.startContainer, r2.startOffset),
12294
+ end: textOffsetInRoot(el, r2.endContainer, r2.endOffset)
12295
+ };
12296
+ }
12297
+ }
12298
+ document.execCommand("styleWithCSS", false, true);
9806
12299
  document.execCommand(cmd, false);
9807
- activeElRef.current?.focus();
9808
- if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
12300
+ if (el && document.activeElement !== el) {
12301
+ el.focus();
12302
+ }
12303
+ const selAfter = window.getSelection();
12304
+ if (el && savedOffsets && selAfter?.isCollapsed) {
12305
+ const start = pointAtTextOffset(el, savedOffsets.start);
12306
+ const end = pointAtTextOffset(el, savedOffsets.end);
12307
+ if (start && end) {
12308
+ try {
12309
+ const range = document.createRange();
12310
+ range.setStart(start.node, start.offset);
12311
+ range.setEnd(end.node, end.offset);
12312
+ selAfter.removeAllRanges();
12313
+ selAfter.addRange(range);
12314
+ } catch {
12315
+ }
12316
+ }
12317
+ }
12318
+ if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
9809
12319
  refreshActiveCommandsRef.current();
9810
12320
  }, []);
9811
- const handleStateChange = (0, import_react8.useCallback)((state) => {
12321
+ const handleStateChange = (0, import_react9.useCallback)((state) => {
9812
12322
  if (!activeStateElRef.current) return;
9813
12323
  const el = activeStateElRef.current;
9814
12324
  if (state === "Default") {
@@ -9821,11 +12331,11 @@ function OhhwellsBridge() {
9821
12331
  }
9822
12332
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9823
12333
  }, [deactivate]);
9824
- const closeLinkPopover = (0, import_react8.useCallback)(() => {
12334
+ const closeLinkPopover = (0, import_react9.useCallback)(() => {
9825
12335
  addNavAfterAnchorRef.current = null;
9826
12336
  setLinkPopover(null);
9827
12337
  }, []);
9828
- const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
12338
+ const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
9829
12339
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9830
12340
  if (!hrefCtx) return;
9831
12341
  bumpLinkPopoverGrace();
@@ -9836,7 +12346,7 @@ function OhhwellsBridge() {
9836
12346
  });
9837
12347
  deactivate();
9838
12348
  }, [deactivate]);
9839
- const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
12349
+ const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
9840
12350
  const anchor = selectedElRef.current;
9841
12351
  if (!anchor) return;
9842
12352
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9849,7 +12359,7 @@ function OhhwellsBridge() {
9849
12359
  });
9850
12360
  deselect();
9851
12361
  }, [deselect]);
9852
- const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
12362
+ const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
9853
12363
  (target) => {
9854
12364
  const session = linkPopoverSessionRef.current;
9855
12365
  if (!session) return;
@@ -9908,13 +12418,13 @@ function OhhwellsBridge() {
9908
12418
  const showEditLink = toolbarShowEditLink;
9909
12419
  const currentSections = sectionsByPath[pathname] ?? [];
9910
12420
  linkPopoverOpenRef.current = linkPopover !== null;
9911
- const handleMediaReplace = (0, import_react8.useCallback)(
12421
+ const handleMediaReplace = (0, import_react9.useCallback)(
9912
12422
  (key) => {
9913
12423
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9914
12424
  },
9915
12425
  [postToParent2, mediaHover?.elementType]
9916
12426
  );
9917
- const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
12427
+ const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
9918
12428
  setUploadingRects((prev) => {
9919
12429
  if (!(key in prev)) return prev;
9920
12430
  const next = { ...prev };
@@ -9922,7 +12432,7 @@ function OhhwellsBridge() {
9922
12432
  return next;
9923
12433
  });
9924
12434
  }, []);
9925
- const handleVideoSettingsChange = (0, import_react8.useCallback)(
12435
+ const handleVideoSettingsChange = (0, import_react9.useCallback)(
9926
12436
  (key, settings) => {
9927
12437
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9928
12438
  const video = getVideoEl(el);
@@ -9945,9 +12455,9 @@ function OhhwellsBridge() {
9945
12455
  [postToParent2]
9946
12456
  );
9947
12457
  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)(
12458
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
12459
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
12460
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9951
12461
  MediaOverlay,
9952
12462
  {
9953
12463
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -9958,7 +12468,7 @@ function OhhwellsBridge() {
9958
12468
  },
9959
12469
  `uploading-${key}`
9960
12470
  )),
9961
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12471
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9962
12472
  MediaOverlay,
9963
12473
  {
9964
12474
  hover: mediaHover,
@@ -9967,28 +12477,70 @@ function OhhwellsBridge() {
9967
12477
  onVideoSettingsChange: handleVideoSettingsChange
9968
12478
  }
9969
12479
  ),
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,
12480
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
12481
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
12482
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
12483
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
12484
+ "div",
9974
12485
  {
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)(
12486
+ className: "pointer-events-none fixed z-2147483646",
12487
+ style: {
12488
+ left: slot.left,
12489
+ top: slot.top,
12490
+ width: slot.width,
12491
+ height: slot.height
12492
+ },
12493
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
12494
+ DropIndicator,
12495
+ {
12496
+ direction: slot.direction,
12497
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
12498
+ className: "!h-full !w-full"
12499
+ }
12500
+ )
12501
+ },
12502
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
12503
+ )),
12504
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
12505
+ "div",
12506
+ {
12507
+ className: "pointer-events-none fixed z-2147483646",
12508
+ style: {
12509
+ left: slot.left,
12510
+ top: slot.top,
12511
+ width: slot.width,
12512
+ height: slot.height
12513
+ },
12514
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
12515
+ DropIndicator,
12516
+ {
12517
+ direction: slot.direction,
12518
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
12519
+ className: "!h-full !w-full"
12520
+ }
12521
+ )
12522
+ },
12523
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
12524
+ )),
12525
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
12526
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
12527
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
12528
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(FooterContainerChrome, { rect: toolbarRect, onAdd: handleAddFooterColumn }),
12529
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9981
12530
  ItemInteractionLayer,
9982
12531
  {
9983
- rect: toolbarRect,
12532
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
9984
12533
  elRef: glowElRef,
9985
12534
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9986
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
12535
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
9987
12536
  dragDisabled: reorderDragDisabled,
9988
12537
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9989
12538
  onDragHandleDragStart: handleItemDragStart,
9990
12539
  onDragHandleDragEnd: handleItemDragEnd,
9991
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12540
+ onItemPointerDown: handleItemChromePointerDown,
12541
+ onItemClick: handleItemChromeClick,
12542
+ itemDragSurface: !isFooterFrameSelection,
12543
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9992
12544
  ItemActionToolbar,
9993
12545
  {
9994
12546
  onEditLink: openLinkPopoverForSelected,
@@ -10001,17 +12553,18 @@ function OhhwellsBridge() {
10001
12553
  ) : void 0
10002
12554
  }
10003
12555
  ),
10004
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
10005
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12556
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
12557
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10006
12558
  EditGlowChrome,
10007
12559
  {
10008
12560
  rect: toolbarRect,
10009
12561
  elRef: glowElRef,
10010
12562
  reorderHrefKey,
10011
- dragDisabled: reorderDragDisabled
12563
+ dragDisabled: reorderDragDisabled,
12564
+ hideHandle: isItemDragging
10012
12565
  }
10013
12566
  ),
10014
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12567
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10015
12568
  FloatingToolbar,
10016
12569
  {
10017
12570
  rect: toolbarRect,
@@ -10024,7 +12577,7 @@ function OhhwellsBridge() {
10024
12577
  }
10025
12578
  )
10026
12579
  ] }),
10027
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12580
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
10028
12581
  "div",
10029
12582
  {
10030
12583
  "data-ohw-max-badge": "",
@@ -10050,7 +12603,7 @@ function OhhwellsBridge() {
10050
12603
  ]
10051
12604
  }
10052
12605
  ),
10053
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12606
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10054
12607
  StateToggle,
10055
12608
  {
10056
12609
  rect: toggleState.rect,
@@ -10059,32 +12612,36 @@ function OhhwellsBridge() {
10059
12612
  onStateChange: handleStateChange
10060
12613
  }
10061
12614
  ),
10062
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12615
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
10063
12616
  "div",
10064
12617
  {
10065
12618
  "data-ohw-section-insert-line": "",
10066
12619
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
10067
12620
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
10068
12621
  children: [
10069
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10070
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12622
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
12623
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10071
12624
  Badge,
10072
12625
  {
10073
12626
  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
12627
  onClick: () => {
10075
12628
  window.parent.postMessage(
10076
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
12629
+ {
12630
+ type: "ow:add-section",
12631
+ insertAfter: sectionGap.insertAfter,
12632
+ insertBefore: sectionGap.insertBefore
12633
+ },
10077
12634
  "*"
10078
12635
  );
10079
12636
  },
10080
12637
  children: "Add Section"
10081
12638
  }
10082
12639
  ),
10083
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12640
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10084
12641
  ]
10085
12642
  }
10086
12643
  ),
10087
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12644
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10088
12645
  LinkPopover,
10089
12646
  {
10090
12647
  panelRef: linkPopoverPanelRef,
@@ -10105,48 +12662,6 @@ function OhhwellsBridge() {
10105
12662
  bridgeRoot
10106
12663
  ) : null;
10107
12664
  }
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
12665
  // Annotate the CommonJS export names for ESM import in node:
10151
12666
  0 && (module.exports = {
10152
12667
  CustomToolbar,