@ohhwells/bridge 0.1.41 → 0.1.42-next.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React9, { useCallback as useCallback3, useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState6 } from "react";
4
+ import React10, { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState7 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
@@ -48,12 +48,13 @@ function useLinkHrefGuardian(...deps) {
48
48
  }
49
49
 
50
50
  // src/ui/SchedulingWidget.tsx
51
- import { useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
51
+ import { useCallback, useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
52
52
 
53
53
  // src/ui/EmailCaptureModal.tsx
54
54
  import { useState } from "react";
55
55
  import { Dialog } from "radix-ui";
56
56
  import { jsx, jsxs } from "react/jsx-runtime";
57
+ var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
57
58
  function Spinner() {
58
59
  return /* @__PURE__ */ jsxs(
59
60
  "svg",
@@ -87,12 +88,17 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
87
88
  const [error, setError] = useState(null);
88
89
  const isBook = title === "Confirm your spot";
89
90
  const handleSubmit = async () => {
90
- if (!email.trim()) return;
91
+ const trimmed = email.trim();
92
+ if (!trimmed) return;
93
+ if (!isValidEmail(trimmed)) {
94
+ setError("Please enter a valid email address.");
95
+ return;
96
+ }
91
97
  setLoading(true);
92
98
  setError(null);
93
99
  try {
94
- await onSubmit(email.trim());
95
- setSubmittedEmail(email.trim());
100
+ await onSubmit(trimmed);
101
+ setSubmittedEmail(trimmed);
96
102
  setSuccess(true);
97
103
  } catch (e) {
98
104
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -167,7 +173,10 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
167
173
  {
168
174
  type: "email",
169
175
  value: email,
170
- onChange: (e) => setEmail(e.target.value),
176
+ onChange: (e) => {
177
+ setEmail(e.target.value);
178
+ if (error) setError(null);
179
+ },
171
180
  onKeyDown: handleKeyDown,
172
181
  placeholder: "hello@gmail.com",
173
182
  autoFocus: true,
@@ -224,12 +233,20 @@ function formatClassTime(cls) {
224
233
  const em = endTotal % 60;
225
234
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
226
235
  }
236
+ function formatBookingLabel(cls) {
237
+ const price = cls.price;
238
+ const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
239
+ return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
240
+ }
227
241
  function getBookingsOnDate(cls, date) {
228
242
  if (!cls.bookings?.length) return 0;
229
- const ds = date.toISOString().split("T")[0];
243
+ const target = new Date(date);
244
+ target.setHours(0, 0, 0, 0);
230
245
  return cls.bookings.filter((b) => {
231
246
  try {
232
- return new Date(b.classDate).toISOString().split("T")[0] === ds;
247
+ const bookingDate = new Date(b.classDate);
248
+ bookingDate.setHours(0, 0, 0, 0);
249
+ return bookingDate.getTime() === target.getTime();
233
250
  } catch {
234
251
  return false;
235
252
  }
@@ -494,7 +511,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
494
511
  }
495
512
  )
496
513
  ] }),
497
- /* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
514
+ cls.showAvailability !== false && /* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
498
515
  /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
499
516
  available,
500
517
  "/",
@@ -506,9 +523,10 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
506
523
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
507
524
  /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
508
525
  /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
509
- cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
526
+ cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
527
+ cls.description && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-dark,#200C02) opacity-70 block truncate", children: cls.description })
510
528
  ] }),
511
- /* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
529
+ cls.showAvailability !== false && /* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
512
530
  /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
513
531
  available,
514
532
  "/",
@@ -533,7 +551,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
533
551
  if (!cls.id) return;
534
552
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
535
553
  },
536
- children: isFull ? "Join Waitlist" : "Book Now"
554
+ children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
537
555
  }
538
556
  )
539
557
  ] })
@@ -575,6 +593,17 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
575
593
  const [isHovered, setIsHovered] = useState2(false);
576
594
  const [modalState, setModalState] = useState2(null);
577
595
  const switchScheduleIdRef = useRef(null);
596
+ const liveScheduleIdRef = useRef(null);
597
+ const refetchLiveSchedule = useCallback(async () => {
598
+ const id = liveScheduleIdRef.current;
599
+ if (!id) return;
600
+ try {
601
+ const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
602
+ const data = await res.json();
603
+ if (data?.id) setSchedule(data);
604
+ } catch {
605
+ }
606
+ }, []);
578
607
  const dates = useMemo(() => {
579
608
  const today = /* @__PURE__ */ new Date();
580
609
  today.setHours(0, 0, 0, 0);
@@ -594,6 +623,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
594
623
  }
595
624
  }
596
625
  }, [schedule, dates]);
626
+ useEffect(() => {
627
+ if (typeof window === "undefined") return;
628
+ const onVisible = () => {
629
+ if (!document.hidden) void refetchLiveSchedule();
630
+ };
631
+ window.addEventListener("focus", refetchLiveSchedule);
632
+ document.addEventListener("visibilitychange", onVisible);
633
+ return () => {
634
+ window.removeEventListener("focus", refetchLiveSchedule);
635
+ document.removeEventListener("visibilitychange", onVisible);
636
+ };
637
+ }, [refetchLiveSchedule]);
597
638
  useEffect(() => {
598
639
  if (typeof window === "undefined") return;
599
640
  const isInEditor = window.self !== window.top;
@@ -655,7 +696,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
655
696
  setLoading(false);
656
697
  return;
657
698
  }
658
- ;
699
+ liveScheduleIdRef.current = effectiveId;
659
700
  (async () => {
660
701
  try {
661
702
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -712,18 +753,14 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
712
753
  const data = await res.json().catch(() => ({}));
713
754
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
714
755
  }
756
+ await refetchLiveSchedule();
715
757
  };
716
758
  const handleReplaceSchedule = () => {
717
759
  setIsHovered(false);
718
- if (schedule) {
719
- window.parent.postMessage({
720
- type: "ow:schedule-connected",
721
- schedule: { id: schedule.id, name: schedule.name },
722
- insertAfter
723
- }, "*");
724
- } else {
725
- window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
726
- }
760
+ window.parent.postMessage(
761
+ { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
762
+ "*"
763
+ );
727
764
  };
728
765
  if (!inEditor && !loading && !schedule) return null;
729
766
  const sectionId = `scheduling-${insertAfter}`;
@@ -766,7 +803,19 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
766
803
  ),
767
804
  /* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
768
805
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
769
- /* @__PURE__ */ jsx2("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" }),
806
+ /* @__PURE__ */ jsx2(
807
+ "h2",
808
+ {
809
+ className: "font-display text-center m-0 text-(--color-dark,#200C02)",
810
+ style: {
811
+ fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
812
+ fontWeight: "var(--font-weight-heading, 400)",
813
+ lineHeight: 1.05,
814
+ letterSpacing: "-0.02em"
815
+ },
816
+ children: schedule?.name ?? "Book an appointment"
817
+ }
818
+ ),
770
819
  schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
771
820
  ] }),
772
821
  /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4444,7 +4493,7 @@ function getChromeStyle(state) {
4444
4493
  case "dragging":
4445
4494
  return {
4446
4495
  border: `2px solid ${PRIMARY}`,
4447
- boxShadow: DRAG_SHADOW
4496
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4448
4497
  };
4449
4498
  default:
4450
4499
  return {};
@@ -4460,6 +4509,8 @@ function ItemInteractionLayer({
4460
4509
  dragHandleLabel = "Reorder item",
4461
4510
  onDragHandleDragStart,
4462
4511
  onDragHandleDragEnd,
4512
+ onItemPointerDown,
4513
+ onItemClick,
4463
4514
  chromeGap = 6,
4464
4515
  className
4465
4516
  }) {
@@ -4467,7 +4518,8 @@ function ItemInteractionLayer({
4467
4518
  const isActive = state === "active-top" || state === "active-bottom";
4468
4519
  const isDragging = state === "dragging";
4469
4520
  const showToolbar = isActive && toolbar;
4470
- const showDragHandle = isActive && showHandle && !isDragging;
4521
+ const showDragHandle = (isActive || isDragging) && showHandle;
4522
+ const itemDragEnabled = isActive && showHandle && !isDragging && !dragDisabled;
4471
4523
  return /* @__PURE__ */ jsxs4(
4472
4524
  "div",
4473
4525
  {
@@ -4492,11 +4544,28 @@ function ItemInteractionLayer({
4492
4544
  style: getChromeStyle(state)
4493
4545
  }
4494
4546
  ),
4547
+ itemDragEnabled && /* @__PURE__ */ jsx9(
4548
+ "div",
4549
+ {
4550
+ "data-ohw-item-drag-surface": "",
4551
+ className: "pointer-events-auto absolute inset-0 cursor-grab rounded-lg active:cursor-grabbing",
4552
+ onPointerDown: (e) => {
4553
+ if (e.button !== 0) return;
4554
+ onItemPointerDown?.(e);
4555
+ },
4556
+ onClick: (e) => {
4557
+ e.preventDefault();
4558
+ e.stopPropagation();
4559
+ onItemClick?.(e.clientX, e.clientY);
4560
+ }
4561
+ }
4562
+ ),
4495
4563
  showDragHandle && /* @__PURE__ */ jsx9(
4496
4564
  "div",
4497
4565
  {
4498
4566
  "data-ohw-drag-handle-container": "",
4499
- className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4567
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4568
+ style: isDragging ? { visibility: "hidden" } : void 0,
4500
4569
  children: /* @__PURE__ */ jsx9(
4501
4570
  DragHandle,
4502
4571
  {
@@ -4719,15 +4788,12 @@ function MediaOverlay({
4719
4788
  className: "flex items-center justify-center cursor-pointer",
4720
4789
  style: {
4721
4790
  ...box,
4722
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4723
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4724
- // in-document, pointer-events does it natively. The button below opts back in, so
4725
- // Replace still works.
4726
- pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4791
+ // Keep the full-size visual overlay click-through so carousel controls and editable
4792
+ // content underneath remain interactive. The Replace button opts back in below.
4793
+ pointerEvents: "none",
4727
4794
  boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4728
4795
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4729
4796
  },
4730
- onClick: () => onReplace(hover.key),
4731
4797
  children: /* @__PURE__ */ jsxs5(
4732
4798
  Button,
4733
4799
  {
@@ -4752,6 +4818,61 @@ function MediaOverlay({
4752
4818
  ] });
4753
4819
  }
4754
4820
 
4821
+ // src/ui/CarouselOverlay.tsx
4822
+ import { GalleryHorizontal } from "lucide-react";
4823
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
4824
+ var OVERLAY_BUTTON_STYLE2 = {
4825
+ pointerEvents: "auto",
4826
+ fontFamily: "Inter, sans-serif",
4827
+ fontSize: 12,
4828
+ color: "#000"
4829
+ };
4830
+ function CarouselOverlay({
4831
+ hover,
4832
+ onEdit
4833
+ }) {
4834
+ const { rect } = hover;
4835
+ return /* @__PURE__ */ jsx12(
4836
+ "div",
4837
+ {
4838
+ "data-ohw-bridge": "",
4839
+ "data-ohw-carousel-overlay": "",
4840
+ className: "flex cursor-pointer items-center justify-center",
4841
+ style: {
4842
+ position: "fixed",
4843
+ top: rect.top,
4844
+ left: rect.left,
4845
+ width: rect.width,
4846
+ height: rect.height,
4847
+ zIndex: 2147483646,
4848
+ pointerEvents: "auto",
4849
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4850
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4851
+ },
4852
+ onClick: () => onEdit(hover.key),
4853
+ children: /* @__PURE__ */ jsxs6(
4854
+ Button,
4855
+ {
4856
+ "data-ohw-carousel-overlay": "",
4857
+ variant: "outline",
4858
+ size: "sm",
4859
+ className: "cursor-pointer gap-1.5 hover:bg-background",
4860
+ style: OVERLAY_BUTTON_STYLE2,
4861
+ onMouseDown: (e) => e.preventDefault(),
4862
+ onClick: (e) => {
4863
+ e.stopPropagation();
4864
+ onEdit(hover.key);
4865
+ },
4866
+ children: [
4867
+ /* @__PURE__ */ jsx12(GalleryHorizontal, { size: 14 }),
4868
+ "Edit gallery"
4869
+ ]
4870
+ }
4871
+ )
4872
+ }
4873
+ );
4874
+ }
4875
+
4755
4876
  // src/OhhwellsBridge.tsx
4756
4877
  import { createPortal as createPortal2 } from "react-dom";
4757
4878
  import { usePathname as usePathname2, useRouter as useRouter2, useSearchParams } from "next/navigation";
@@ -5038,22 +5159,22 @@ import { useEffect as useEffect6 } from "react";
5038
5159
  import * as React7 from "react";
5039
5160
  import { Dialog as DialogPrimitive } from "radix-ui";
5040
5161
  import { X } from "lucide-react";
5041
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
5162
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5042
5163
  function Dialog2({
5043
5164
  ...props
5044
5165
  }) {
5045
- return /* @__PURE__ */ jsx12(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
5166
+ return /* @__PURE__ */ jsx13(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
5046
5167
  }
5047
5168
  function DialogPortal({
5048
5169
  ...props
5049
5170
  }) {
5050
- return /* @__PURE__ */ jsx12(DialogPrimitive.Portal, { ...props });
5171
+ return /* @__PURE__ */ jsx13(DialogPrimitive.Portal, { ...props });
5051
5172
  }
5052
5173
  function DialogOverlay({
5053
5174
  className,
5054
5175
  ...props
5055
5176
  }) {
5056
- return /* @__PURE__ */ jsx12(
5177
+ return /* @__PURE__ */ jsx13(
5057
5178
  DialogPrimitive.Overlay,
5058
5179
  {
5059
5180
  "data-slot": "dialog-overlay",
@@ -5066,9 +5187,9 @@ function DialogOverlay({
5066
5187
  var DialogContent = React7.forwardRef(
5067
5188
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5068
5189
  const positionMode = container ? "absolute" : "fixed";
5069
- return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
5070
- /* @__PURE__ */ jsx12(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5071
- /* @__PURE__ */ jsxs6(
5190
+ return /* @__PURE__ */ jsxs7(DialogPortal, { container: container ?? void 0, children: [
5191
+ /* @__PURE__ */ jsx13(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5192
+ /* @__PURE__ */ jsxs7(
5072
5193
  DialogPrimitive.Content,
5073
5194
  {
5074
5195
  ref,
@@ -5084,13 +5205,13 @@ var DialogContent = React7.forwardRef(
5084
5205
  ...props,
5085
5206
  children: [
5086
5207
  children,
5087
- showCloseButton ? /* @__PURE__ */ jsx12(
5208
+ showCloseButton ? /* @__PURE__ */ jsx13(
5088
5209
  DialogPrimitive.Close,
5089
5210
  {
5090
5211
  type: "button",
5091
5212
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5092
5213
  "aria-label": "Close",
5093
- children: /* @__PURE__ */ jsx12(X, { size: 16, "aria-hidden": true })
5214
+ children: /* @__PURE__ */ jsx13(X, { size: 16, "aria-hidden": true })
5094
5215
  }
5095
5216
  ) : null
5096
5217
  ]
@@ -5104,13 +5225,13 @@ function DialogHeader({
5104
5225
  className,
5105
5226
  ...props
5106
5227
  }) {
5107
- return /* @__PURE__ */ jsx12("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5228
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5108
5229
  }
5109
5230
  function DialogFooter({
5110
5231
  className,
5111
5232
  ...props
5112
5233
  }) {
5113
- return /* @__PURE__ */ jsx12(
5234
+ return /* @__PURE__ */ jsx13(
5114
5235
  "div",
5115
5236
  {
5116
5237
  className: cn("flex items-center justify-end gap-2", className),
@@ -5118,7 +5239,7 @@ function DialogFooter({
5118
5239
  }
5119
5240
  );
5120
5241
  }
5121
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5242
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
5122
5243
  DialogPrimitive.Title,
5123
5244
  {
5124
5245
  ref,
@@ -5130,7 +5251,7 @@ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE
5130
5251
  }
5131
5252
  ));
5132
5253
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
5133
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5254
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
5134
5255
  DialogPrimitive.Description,
5135
5256
  {
5136
5257
  ref,
@@ -5146,19 +5267,19 @@ import { Info, X as X3 } from "lucide-react";
5146
5267
 
5147
5268
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5148
5269
  import { ArrowRight, File, GalleryVertical } from "lucide-react";
5149
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5270
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5150
5271
  function DestinationBreadcrumb({
5151
5272
  pageTitle,
5152
5273
  sectionLabel
5153
5274
  }) {
5154
- return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2", children: [
5155
- /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5156
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3", children: [
5157
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
5158
- /* @__PURE__ */ jsx13(File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5159
- /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5275
+ return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-2", children: [
5276
+ /* @__PURE__ */ jsx14("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5277
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3", children: [
5278
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
5279
+ /* @__PURE__ */ jsx14(File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5280
+ /* @__PURE__ */ jsx14("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5160
5281
  ] }),
5161
- /* @__PURE__ */ jsx13(
5282
+ /* @__PURE__ */ jsx14(
5162
5283
  ArrowRight,
5163
5284
  {
5164
5285
  size: 16,
@@ -5166,8 +5287,8 @@ function DestinationBreadcrumb({
5166
5287
  "aria-hidden": true
5167
5288
  }
5168
5289
  ),
5169
- /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5170
- /* @__PURE__ */ jsx13(
5290
+ /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5291
+ /* @__PURE__ */ jsx14(
5171
5292
  GalleryVertical,
5172
5293
  {
5173
5294
  size: 16,
@@ -5175,7 +5296,7 @@ function DestinationBreadcrumb({
5175
5296
  "aria-hidden": true
5176
5297
  }
5177
5298
  ),
5178
- /* @__PURE__ */ jsx13("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5299
+ /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5179
5300
  ] })
5180
5301
  ] })
5181
5302
  ] });
@@ -5183,22 +5304,22 @@ function DestinationBreadcrumb({
5183
5304
 
5184
5305
  // src/ui/link-modal/SectionTreeItem.tsx
5185
5306
  import { GalleryVertical as GalleryVertical2 } from "lucide-react";
5186
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5307
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
5187
5308
  function SectionTreeItem({
5188
5309
  section,
5189
5310
  onSelect,
5190
5311
  selected
5191
5312
  }) {
5192
5313
  const interactive = Boolean(onSelect);
5193
- return /* @__PURE__ */ jsxs8("div", { className: "flex h-9 w-full items-end pl-3", children: [
5194
- /* @__PURE__ */ jsx14(
5314
+ return /* @__PURE__ */ jsxs9("div", { className: "flex h-9 w-full items-end pl-3", children: [
5315
+ /* @__PURE__ */ jsx15(
5195
5316
  "div",
5196
5317
  {
5197
5318
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5198
5319
  "aria-hidden": true
5199
5320
  }
5200
5321
  ),
5201
- /* @__PURE__ */ jsxs8(
5322
+ /* @__PURE__ */ jsxs9(
5202
5323
  "div",
5203
5324
  {
5204
5325
  role: interactive ? "button" : void 0,
@@ -5216,7 +5337,7 @@ function SectionTreeItem({
5216
5337
  interactive && selected && "border-primary"
5217
5338
  ),
5218
5339
  children: [
5219
- /* @__PURE__ */ jsx14(
5340
+ /* @__PURE__ */ jsx15(
5220
5341
  GalleryVertical2,
5221
5342
  {
5222
5343
  size: 16,
@@ -5224,7 +5345,7 @@ function SectionTreeItem({
5224
5345
  "aria-hidden": true
5225
5346
  }
5226
5347
  ),
5227
- /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5348
+ /* @__PURE__ */ jsx15("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5228
5349
  ]
5229
5350
  }
5230
5351
  )
@@ -5236,10 +5357,10 @@ import { useEffect as useEffect3, useId as useId2, useRef as useRef3, useState a
5236
5357
 
5237
5358
  // src/ui/input.tsx
5238
5359
  import * as React8 from "react";
5239
- import { jsx as jsx15 } from "react/jsx-runtime";
5360
+ import { jsx as jsx16 } from "react/jsx-runtime";
5240
5361
  var Input = React8.forwardRef(
5241
5362
  ({ className, type, ...props }, ref) => {
5242
- return /* @__PURE__ */ jsx15(
5363
+ return /* @__PURE__ */ jsx16(
5243
5364
  "input",
5244
5365
  {
5245
5366
  type,
@@ -5258,9 +5379,9 @@ Input.displayName = "Input";
5258
5379
 
5259
5380
  // src/ui/label.tsx
5260
5381
  import { Label as LabelPrimitive } from "radix-ui";
5261
- import { jsx as jsx16 } from "react/jsx-runtime";
5382
+ import { jsx as jsx17 } from "react/jsx-runtime";
5262
5383
  function Label({ className, ...props }) {
5263
- return /* @__PURE__ */ jsx16(
5384
+ return /* @__PURE__ */ jsx17(
5264
5385
  LabelPrimitive.Root,
5265
5386
  {
5266
5387
  "data-slot": "label",
@@ -5272,11 +5393,11 @@ function Label({ className, ...props }) {
5272
5393
 
5273
5394
  // src/ui/link-modal/UrlOrPageInput.tsx
5274
5395
  import { ChevronDown, File as File2, X as X2 } from "lucide-react";
5275
- import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
5396
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5276
5397
  function FieldChevron({
5277
5398
  onClick
5278
5399
  }) {
5279
- return /* @__PURE__ */ jsx17(
5400
+ return /* @__PURE__ */ jsx18(
5280
5401
  "button",
5281
5402
  {
5282
5403
  type: "button",
@@ -5284,7 +5405,7 @@ function FieldChevron({
5284
5405
  onClick,
5285
5406
  "aria-label": "Open page list",
5286
5407
  tabIndex: -1,
5287
- children: /* @__PURE__ */ jsx17(ChevronDown, { size: 16 })
5408
+ children: /* @__PURE__ */ jsx18(ChevronDown, { size: 16 })
5288
5409
  }
5289
5410
  );
5290
5411
  }
@@ -5345,11 +5466,11 @@ function UrlOrPageInput({
5345
5466
  "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5346
5467
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5347
5468
  );
5348
- return /* @__PURE__ */ jsxs9("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5349
- /* @__PURE__ */ jsx17(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5350
- /* @__PURE__ */ jsxs9("div", { ref: rootRef, className: "relative w-full", children: [
5351
- /* @__PURE__ */ jsxs9("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5352
- selectedPage ? /* @__PURE__ */ jsx17("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx17(
5469
+ return /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5470
+ /* @__PURE__ */ jsx18(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5471
+ /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: "relative w-full", children: [
5472
+ /* @__PURE__ */ jsxs10("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5473
+ selectedPage ? /* @__PURE__ */ jsx18("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx18(
5353
5474
  File2,
5354
5475
  {
5355
5476
  size: 16,
@@ -5357,7 +5478,7 @@ function UrlOrPageInput({
5357
5478
  "aria-hidden": true
5358
5479
  }
5359
5480
  ) }) : null,
5360
- readOnly ? /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx17(
5481
+ readOnly ? /* @__PURE__ */ jsx18("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx18(
5361
5482
  Input,
5362
5483
  {
5363
5484
  ref: inputRef,
@@ -5383,7 +5504,7 @@ function UrlOrPageInput({
5383
5504
  )
5384
5505
  }
5385
5506
  ),
5386
- selectedPage && !readOnly ? /* @__PURE__ */ jsx17(
5507
+ selectedPage && !readOnly ? /* @__PURE__ */ jsx18(
5387
5508
  "button",
5388
5509
  {
5389
5510
  type: "button",
@@ -5391,26 +5512,26 @@ function UrlOrPageInput({
5391
5512
  onMouseDown: clearSelection,
5392
5513
  "aria-label": "Clear selected page",
5393
5514
  tabIndex: -1,
5394
- children: /* @__PURE__ */ jsx17(X2, { size: 16, "aria-hidden": true })
5515
+ children: /* @__PURE__ */ jsx18(X2, { size: 16, "aria-hidden": true })
5395
5516
  }
5396
5517
  ) : null,
5397
- !readOnly ? /* @__PURE__ */ jsx17(FieldChevron, { onClick: toggleDropdown }) : null
5518
+ !readOnly ? /* @__PURE__ */ jsx18(FieldChevron, { onClick: toggleDropdown }) : null
5398
5519
  ] }),
5399
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx17(
5520
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx18(
5400
5521
  "div",
5401
5522
  {
5402
5523
  "data-ohw-link-page-dropdown": "",
5403
5524
  className: "absolute left-0 right-0 top-[calc(100%+4px)] z-50 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
5404
5525
  onMouseDown: (e) => e.preventDefault(),
5405
- children: filteredPages.map((page) => /* @__PURE__ */ jsxs9(
5526
+ children: filteredPages.map((page) => /* @__PURE__ */ jsxs10(
5406
5527
  "button",
5407
5528
  {
5408
5529
  type: "button",
5409
5530
  className: "flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm leading-5 text-foreground outline-none hover:bg-muted",
5410
5531
  onClick: () => onPageSelect(page),
5411
5532
  children: [
5412
- /* @__PURE__ */ jsx17(File2, { size: 16, className: "shrink-0", "aria-hidden": true }),
5413
- /* @__PURE__ */ jsx17("span", { className: "truncate", children: page.title })
5533
+ /* @__PURE__ */ jsx18(File2, { size: 16, className: "shrink-0", "aria-hidden": true }),
5534
+ /* @__PURE__ */ jsx18("span", { className: "truncate", children: page.title })
5414
5535
  ]
5415
5536
  },
5416
5537
  page.path
@@ -5418,34 +5539,34 @@ function UrlOrPageInput({
5418
5539
  }
5419
5540
  ) : null
5420
5541
  ] }),
5421
- urlError ? /* @__PURE__ */ jsx17("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5542
+ urlError ? /* @__PURE__ */ jsx18("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5422
5543
  ] });
5423
5544
  }
5424
5545
 
5425
5546
  // src/ui/link-modal/LinkEditorPanel.tsx
5426
- import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5547
+ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5427
5548
  function LinkEditorPanel({ state, onClose }) {
5428
5549
  const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5429
- return /* @__PURE__ */ jsxs10(Fragment3, { children: [
5430
- /* @__PURE__ */ jsx18(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx18(
5550
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
5551
+ /* @__PURE__ */ jsx19(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx19(
5431
5552
  "button",
5432
5553
  {
5433
5554
  type: "button",
5434
5555
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5435
5556
  "aria-label": "Close",
5436
5557
  onClick: onClose,
5437
- children: /* @__PURE__ */ jsx18(X3, { size: 16, "aria-hidden": true })
5558
+ children: /* @__PURE__ */ jsx19(X3, { size: 16, "aria-hidden": true })
5438
5559
  }
5439
5560
  ) }),
5440
- /* @__PURE__ */ jsx18(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx18(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5441
- /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5442
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx18(
5561
+ /* @__PURE__ */ jsx19(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx19(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5562
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5563
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx19(
5443
5564
  DestinationBreadcrumb,
5444
5565
  {
5445
5566
  pageTitle: state.selectedPage.title,
5446
5567
  sectionLabel: state.selectedSection.label
5447
5568
  }
5448
- ) : /* @__PURE__ */ jsx18(
5569
+ ) : /* @__PURE__ */ jsx19(
5449
5570
  UrlOrPageInput,
5450
5571
  {
5451
5572
  value: state.searchValue,
@@ -5458,8 +5579,8 @@ function LinkEditorPanel({ state, onClose }) {
5458
5579
  urlError: state.urlError
5459
5580
  }
5460
5581
  ),
5461
- state.showChooseSection ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col justify-center gap-2", children: [
5462
- /* @__PURE__ */ jsx18(
5582
+ state.showChooseSection ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col justify-center gap-2", children: [
5583
+ /* @__PURE__ */ jsx19(
5463
5584
  Button,
5464
5585
  {
5465
5586
  type: "button",
@@ -5470,15 +5591,15 @@ function LinkEditorPanel({ state, onClose }) {
5470
5591
  children: "Choose a section"
5471
5592
  }
5472
5593
  ),
5473
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5474
- /* @__PURE__ */ jsx18(Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5475
- /* @__PURE__ */ jsx18("span", { children: "Pick a section this link should scroll to." })
5594
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5595
+ /* @__PURE__ */ jsx19(Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5596
+ /* @__PURE__ */ jsx19("span", { children: "Pick a section this link should scroll to." })
5476
5597
  ] })
5477
5598
  ] }) : null,
5478
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx18(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5599
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx19(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5479
5600
  ] }),
5480
- /* @__PURE__ */ jsxs10(DialogFooter, { className: "w-full px-6 pb-6", children: [
5481
- /* @__PURE__ */ jsx18(
5601
+ /* @__PURE__ */ jsxs11(DialogFooter, { className: "w-full px-6 pb-6", children: [
5602
+ /* @__PURE__ */ jsx19(
5482
5603
  Button,
5483
5604
  {
5484
5605
  type: "button",
@@ -5493,7 +5614,7 @@ function LinkEditorPanel({ state, onClose }) {
5493
5614
  children: state.secondaryLabel
5494
5615
  }
5495
5616
  ),
5496
- /* @__PURE__ */ jsx18(
5617
+ /* @__PURE__ */ jsx19(
5497
5618
  Button,
5498
5619
  {
5499
5620
  type: "button",
@@ -5512,11 +5633,11 @@ function LinkEditorPanel({ state, onClose }) {
5512
5633
  }
5513
5634
 
5514
5635
  // src/ui/link-modal/SectionPickerOverlay.tsx
5515
- import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
5636
+ import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
5516
5637
  import { createPortal } from "react-dom";
5517
5638
  import { ArrowLeft, Check } from "lucide-react";
5518
5639
  import { usePathname, useRouter } from "next/navigation";
5519
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5640
+ import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
5520
5641
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5521
5642
  function rectsEqual(a, b) {
5522
5643
  if (a.size !== b.size) return false;
@@ -5643,7 +5764,7 @@ function SectionPickerOverlay({
5643
5764
  );
5644
5765
  sectionIdsRef.current = sectionIds;
5645
5766
  const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5646
- const applyHoverAt = useCallback(
5767
+ const applyHoverAt = useCallback2(
5647
5768
  (x, y, liveRects) => {
5648
5769
  const ids = sectionIdsRef.current;
5649
5770
  const map = liveRects ?? readSectionRects(ids);
@@ -5723,7 +5844,7 @@ function SectionPickerOverlay({
5723
5844
  window.removeEventListener("message", onPointerSync);
5724
5845
  };
5725
5846
  }, [rects, applyHoverAt]);
5726
- const handleSelect = useCallback(
5847
+ const handleSelect = useCallback2(
5727
5848
  (section) => {
5728
5849
  if (selectedId) return;
5729
5850
  setSelectedId(section.id);
@@ -5745,7 +5866,7 @@ function SectionPickerOverlay({
5745
5866
  const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5746
5867
  if (!portalRoot) return null;
5747
5868
  return createPortal(
5748
- /* @__PURE__ */ jsxs11(
5869
+ /* @__PURE__ */ jsxs12(
5749
5870
  "div",
5750
5871
  {
5751
5872
  "data-ohw-section-picker": "",
@@ -5755,12 +5876,12 @@ function SectionPickerOverlay({
5755
5876
  role: "dialog",
5756
5877
  "aria-label": "Choose a section",
5757
5878
  children: [
5758
- /* @__PURE__ */ jsx19(
5879
+ /* @__PURE__ */ jsx20(
5759
5880
  "div",
5760
5881
  {
5761
5882
  className: "pointer-events-auto fixed left-5 z-[2]",
5762
5883
  style: { top: chromeClip.top + 20 },
5763
- children: /* @__PURE__ */ jsxs11(
5884
+ children: /* @__PURE__ */ jsxs12(
5764
5885
  Button,
5765
5886
  {
5766
5887
  type: "button",
@@ -5769,14 +5890,14 @@ function SectionPickerOverlay({
5769
5890
  className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5770
5891
  onClick: onBack,
5771
5892
  children: [
5772
- /* @__PURE__ */ jsx19(ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5893
+ /* @__PURE__ */ jsx20(ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5773
5894
  "Back"
5774
5895
  ]
5775
5896
  }
5776
5897
  )
5777
5898
  }
5778
5899
  ),
5779
- /* @__PURE__ */ jsx19(
5900
+ /* @__PURE__ */ jsx20(
5780
5901
  "div",
5781
5902
  {
5782
5903
  className: "pointer-events-none fixed left-1/2 z-[2] rounded-lg px-4 py-3 text-xs leading-4 tracking-[0.18px] text-white shadow-md",
@@ -5789,7 +5910,7 @@ function SectionPickerOverlay({
5789
5910
  children: "Click on section to select"
5790
5911
  }
5791
5912
  ),
5792
- !isOnTargetPage ? /* @__PURE__ */ jsx19(
5913
+ !isOnTargetPage ? /* @__PURE__ */ jsx20(
5793
5914
  "div",
5794
5915
  {
5795
5916
  className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md bg-background/90 px-3 py-2 text-sm text-muted-foreground shadow-sm",
@@ -5797,14 +5918,14 @@ function SectionPickerOverlay({
5797
5918
  children: "Loading page preview\u2026"
5798
5919
  }
5799
5920
  ) : null,
5800
- isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ jsx19("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5921
+ isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ jsx20("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5801
5922
  isOnTargetPage ? liveSections.map((section) => {
5802
5923
  const rect = rects.get(section.id);
5803
5924
  if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5804
5925
  const isSelected = selectedId === section.id;
5805
5926
  const isHovered = hoveredId === section.id;
5806
5927
  const isLit = isSelected || isHovered;
5807
- return /* @__PURE__ */ jsxs11(
5928
+ return /* @__PURE__ */ jsxs12(
5808
5929
  "button",
5809
5930
  {
5810
5931
  type: "button",
@@ -5819,7 +5940,7 @@ function SectionPickerOverlay({
5819
5940
  "aria-label": `Select section ${section.label}`,
5820
5941
  onClick: () => handleSelect(section),
5821
5942
  children: [
5822
- isLit ? /* @__PURE__ */ jsx19(
5943
+ isLit ? /* @__PURE__ */ jsx20(
5823
5944
  "span",
5824
5945
  {
5825
5946
  className: "pointer-events-none absolute",
@@ -5832,13 +5953,13 @@ function SectionPickerOverlay({
5832
5953
  "aria-hidden": true
5833
5954
  }
5834
5955
  ) : null,
5835
- isSelected ? /* @__PURE__ */ jsx19(
5956
+ isSelected ? /* @__PURE__ */ jsx20(
5836
5957
  "span",
5837
5958
  {
5838
5959
  className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5839
5960
  style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5840
5961
  "aria-hidden": true,
5841
- children: /* @__PURE__ */ jsx19(Check, { className: "size-5" })
5962
+ children: /* @__PURE__ */ jsx20(Check, { className: "size-5" })
5842
5963
  }
5843
5964
  ) : null
5844
5965
  ]
@@ -5854,7 +5975,7 @@ function SectionPickerOverlay({
5854
5975
  }
5855
5976
 
5856
5977
  // src/ui/link-modal/useLinkModalState.ts
5857
- import { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo3, useState as useState5 } from "react";
5978
+ import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo3, useState as useState5 } from "react";
5858
5979
  function useLinkModalState({
5859
5980
  open,
5860
5981
  mode,
@@ -5873,7 +5994,7 @@ function useLinkModalState({
5873
5994
  const [step, setStep] = useState5("input");
5874
5995
  const [dropdownOpen, setDropdownOpen] = useState5(false);
5875
5996
  const [urlError, setUrlError] = useState5("");
5876
- const reset = useCallback2(() => {
5997
+ const reset = useCallback3(() => {
5877
5998
  setSearchValue("");
5878
5999
  setSelectedPage(null);
5879
6000
  setSelectedSection(null);
@@ -6018,7 +6139,7 @@ function useLinkModalState({
6018
6139
  }
6019
6140
 
6020
6141
  // src/ui/link-modal/LinkPopover.tsx
6021
- import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
6142
+ import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
6022
6143
  function postToParent(data) {
6023
6144
  window.parent?.postMessage(data, "*");
6024
6145
  }
@@ -6114,15 +6235,15 @@ function LinkPopover({
6114
6235
  );
6115
6236
  };
6116
6237
  }, [open, sectionPickerActive]);
6117
- return /* @__PURE__ */ jsxs12(Fragment4, { children: [
6118
- /* @__PURE__ */ jsx20(
6238
+ return /* @__PURE__ */ jsxs13(Fragment4, { children: [
6239
+ /* @__PURE__ */ jsx21(
6119
6240
  Dialog2,
6120
6241
  {
6121
6242
  open: open && !sectionPickerActive,
6122
6243
  onOpenChange: (next) => {
6123
6244
  if (!next) onClose?.();
6124
6245
  },
6125
- children: /* @__PURE__ */ jsx20(
6246
+ children: /* @__PURE__ */ jsx21(
6126
6247
  DialogContent,
6127
6248
  {
6128
6249
  ref: panelRef,
@@ -6132,12 +6253,12 @@ function LinkPopover({
6132
6253
  "data-ohw-bridge": "",
6133
6254
  showCloseButton: false,
6134
6255
  className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6135
- children: /* @__PURE__ */ jsx20(LinkEditorPanel, { state, onClose })
6256
+ children: /* @__PURE__ */ jsx21(LinkEditorPanel, { state, onClose })
6136
6257
  }
6137
6258
  )
6138
6259
  }
6139
6260
  ),
6140
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ jsx20(
6261
+ sectionPickerActive && state.selectedPage ? /* @__PURE__ */ jsx21(
6141
6262
  SectionPickerOverlay,
6142
6263
  {
6143
6264
  pagePath: state.selectedPage.path,
@@ -6443,15 +6564,412 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6443
6564
  };
6444
6565
  }
6445
6566
 
6567
+ // src/lib/footer-items.ts
6568
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6569
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6570
+ function parseFooterHrefKey(key) {
6571
+ if (!key) return null;
6572
+ const match = key.match(FOOTER_HREF_RE);
6573
+ if (!match) return null;
6574
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6575
+ }
6576
+ function isFooterHrefKey(key) {
6577
+ return parseFooterHrefKey(key) !== null;
6578
+ }
6579
+ function getFooterRoot() {
6580
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6581
+ }
6582
+ function getFooterLinksContainer() {
6583
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6584
+ }
6585
+ function isFooterLinkAnchor(el) {
6586
+ if (!el.matches("[data-ohw-href-key]")) return false;
6587
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6588
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6589
+ }
6590
+ function listFooterColumns() {
6591
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6592
+ if (!root) return [];
6593
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6594
+ if (explicit.length > 0) return explicit;
6595
+ return Array.from(root.children).filter((el) => {
6596
+ if (!(el instanceof HTMLElement)) return false;
6597
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6598
+ isFooterLinkAnchor
6599
+ );
6600
+ });
6601
+ }
6602
+ function listFooterLinksInColumn(column) {
6603
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6604
+ isFooterLinkAnchor
6605
+ );
6606
+ }
6607
+ function findFooterColumnForLink(anchor) {
6608
+ const columns = listFooterColumns();
6609
+ return columns.find((col) => col.contains(anchor)) ?? null;
6610
+ }
6611
+ function getFooterOrderFromDom() {
6612
+ return listFooterColumns().map(
6613
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6614
+ );
6615
+ }
6616
+ function findFooterLinkByKey(hrefKey) {
6617
+ return document.querySelector(
6618
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6619
+ );
6620
+ }
6621
+ function syncFooterColumnIndices(columns) {
6622
+ columns.forEach((col, index) => {
6623
+ const next = String(index);
6624
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6625
+ col.setAttribute("data-ohw-footer-col", next);
6626
+ }
6627
+ });
6628
+ }
6629
+ function appendChildIfNeeded(parent, child) {
6630
+ if (parent.lastElementChild !== child) {
6631
+ parent.appendChild(child);
6632
+ }
6633
+ }
6634
+ function insertLinkBeforeNext(col, el, nextEl) {
6635
+ if (nextEl) {
6636
+ if (el.nextElementSibling !== nextEl) {
6637
+ col.insertBefore(el, nextEl);
6638
+ }
6639
+ return;
6640
+ }
6641
+ if (col.lastElementChild !== el) {
6642
+ col.appendChild(el);
6643
+ }
6644
+ }
6645
+ function applyFooterOrder(order) {
6646
+ const container = getFooterLinksContainer();
6647
+ if (!container) return;
6648
+ const columns = listFooterColumns();
6649
+ if (columns.length === 0) return;
6650
+ const currentOrder = getFooterOrderFromDom();
6651
+ if (ordersEqual(order, currentOrder)) {
6652
+ syncFooterColumnIndices(columns);
6653
+ return;
6654
+ }
6655
+ const colCount = Math.max(columns.length, order.length);
6656
+ const working = columns.slice(0, colCount);
6657
+ for (let i = 0; i < order.length && i < working.length; i++) {
6658
+ appendChildIfNeeded(container, working[i]);
6659
+ }
6660
+ for (let i = order.length; i < working.length; i++) {
6661
+ appendChildIfNeeded(container, working[i]);
6662
+ }
6663
+ const freshColumns = listFooterColumns();
6664
+ syncFooterColumnIndices(freshColumns);
6665
+ for (let c = 0; c < order.length; c++) {
6666
+ const col = freshColumns[c];
6667
+ if (!col) continue;
6668
+ const colOrder = order[c];
6669
+ for (let j = 0; j < colOrder.length; j++) {
6670
+ const hrefKey = colOrder[j];
6671
+ const el = findFooterLinkByKey(hrefKey);
6672
+ if (!el) continue;
6673
+ if (el.parentElement !== col) {
6674
+ col.appendChild(el);
6675
+ }
6676
+ const nextKey = colOrder[j + 1];
6677
+ const nextEl = nextKey ? findFooterLinkByKey(nextKey) : null;
6678
+ insertLinkBeforeNext(col, el, nextEl?.parentElement === col ? nextEl : null);
6679
+ }
6680
+ }
6681
+ }
6682
+ function ordersEqual(a, b) {
6683
+ if (a.length !== b.length) return false;
6684
+ for (let i = 0; i < a.length; i++) {
6685
+ const left = a[i];
6686
+ const right = b[i];
6687
+ if (left.length !== right.length) return false;
6688
+ for (let j = 0; j < left.length; j++) {
6689
+ if (left[j] !== right[j]) return false;
6690
+ }
6691
+ }
6692
+ return true;
6693
+ }
6694
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6695
+ const order = getFooterOrderFromDom();
6696
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6697
+ let fromCol = -1;
6698
+ let fromIdx = -1;
6699
+ for (let c = 0; c < order.length; c++) {
6700
+ const idx = order[c].indexOf(hrefKey);
6701
+ if (idx >= 0) {
6702
+ fromCol = c;
6703
+ fromIdx = idx;
6704
+ break;
6705
+ }
6706
+ }
6707
+ if (fromCol < 0) return null;
6708
+ const next = order.map((col) => [...col]);
6709
+ next[fromCol].splice(fromIdx, 1);
6710
+ let adjusted = insertIndex;
6711
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6712
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6713
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
6714
+ if (ordersEqual(next, order)) return null;
6715
+ return next;
6716
+ }
6717
+ function planFooterColumnMove(fromIndex, toIndex) {
6718
+ const order = getFooterOrderFromDom();
6719
+ const columns = listFooterColumns();
6720
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6721
+ return null;
6722
+ }
6723
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6724
+ const nextOrder = order.map((col) => [...col]);
6725
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
6726
+ if (!movedOrder) return null;
6727
+ let adjusted = toIndex;
6728
+ if (fromIndex < toIndex) adjusted -= 1;
6729
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6730
+ nextOrder.splice(adjusted, 0, movedOrder);
6731
+ if (ordersEqual(nextOrder, order)) return null;
6732
+ return nextOrder;
6733
+ }
6734
+ function parseFooterOrder(content) {
6735
+ const raw = content[FOOTER_ORDER_KEY];
6736
+ if (!raw) return null;
6737
+ try {
6738
+ const parsed = JSON.parse(raw);
6739
+ if (!Array.isArray(parsed)) return null;
6740
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
6741
+ } catch {
6742
+ return null;
6743
+ }
6744
+ }
6745
+ function reconcileFooterOrderFromContent(content) {
6746
+ const order = parseFooterOrder(content);
6747
+ if (!order?.length) return;
6748
+ const current = getFooterOrderFromDom();
6749
+ if (ordersEqual(order, current)) {
6750
+ syncFooterColumnIndices(listFooterColumns());
6751
+ return;
6752
+ }
6753
+ applyFooterOrder(order);
6754
+ }
6755
+ function buildLinkDropSlots(column, columnIndex) {
6756
+ const links = listFooterLinksInColumn(column);
6757
+ const colRect = column.getBoundingClientRect();
6758
+ const slots = [];
6759
+ const barThickness = 3;
6760
+ if (links.length === 0) {
6761
+ slots.push({
6762
+ insertIndex: 0,
6763
+ columnIndex,
6764
+ left: colRect.left,
6765
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
6766
+ width: colRect.width,
6767
+ height: barThickness,
6768
+ direction: "horizontal"
6769
+ });
6770
+ return slots;
6771
+ }
6772
+ for (let i = 0; i <= links.length; i++) {
6773
+ let top;
6774
+ if (i === 0) {
6775
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
6776
+ } else if (i === links.length) {
6777
+ const last = links[links.length - 1].getBoundingClientRect();
6778
+ top = last.bottom - barThickness / 2;
6779
+ } else {
6780
+ const prev = links[i - 1].getBoundingClientRect();
6781
+ const next = links[i].getBoundingClientRect();
6782
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
6783
+ }
6784
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
6785
+ slots.push({
6786
+ insertIndex: i,
6787
+ columnIndex,
6788
+ left: widthRef.left,
6789
+ top,
6790
+ width: Math.max(widthRef.width, colRect.width * 0.8),
6791
+ height: barThickness,
6792
+ direction: "horizontal"
6793
+ });
6794
+ }
6795
+ return slots;
6796
+ }
6797
+ function buildColumnDropSlots() {
6798
+ const columns = listFooterColumns();
6799
+ const slots = [];
6800
+ const barThickness = 3;
6801
+ if (columns.length === 0) return slots;
6802
+ for (let i = 0; i <= columns.length; i++) {
6803
+ let left;
6804
+ let height;
6805
+ let top;
6806
+ if (i === 0) {
6807
+ const first = columns[0].getBoundingClientRect();
6808
+ left = first.left - barThickness / 2;
6809
+ top = first.top;
6810
+ height = first.height;
6811
+ } else if (i === columns.length) {
6812
+ const last = columns[columns.length - 1].getBoundingClientRect();
6813
+ left = last.right - barThickness / 2;
6814
+ top = last.top;
6815
+ height = last.height;
6816
+ } else {
6817
+ const prev = columns[i - 1].getBoundingClientRect();
6818
+ const next = columns[i].getBoundingClientRect();
6819
+ left = (prev.right + next.left) / 2 - barThickness / 2;
6820
+ top = Math.min(prev.top, next.top);
6821
+ height = Math.max(prev.bottom, next.bottom) - top;
6822
+ }
6823
+ slots.push({
6824
+ insertIndex: i,
6825
+ columnIndex: i,
6826
+ left,
6827
+ top,
6828
+ width: barThickness,
6829
+ height: Math.max(height, 24),
6830
+ direction: "vertical"
6831
+ });
6832
+ }
6833
+ return slots;
6834
+ }
6835
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
6836
+ const columns = listFooterColumns();
6837
+ let best = null;
6838
+ for (let c = 0; c < columns.length; c++) {
6839
+ const col = columns[c];
6840
+ const colRect = col.getBoundingClientRect();
6841
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
6842
+ if (!inColX) continue;
6843
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
6844
+ const links = listFooterLinksInColumn(col);
6845
+ const fromIdx = links.findIndex(
6846
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
6847
+ );
6848
+ if (fromIdx < 0) return true;
6849
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
6850
+ return true;
6851
+ }
6852
+ return true;
6853
+ });
6854
+ for (const slot of slots) {
6855
+ const cy = slot.top + slot.height / 2;
6856
+ const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
6857
+ if (!best || dist < best.dist) best = { slot, dist };
6858
+ }
6859
+ }
6860
+ return best?.slot ?? null;
6861
+ }
6862
+ function findColumnIndexForKey(hrefKey) {
6863
+ const order = getFooterOrderFromDom();
6864
+ for (let c = 0; c < order.length; c++) {
6865
+ if (order[c].includes(hrefKey)) return c;
6866
+ }
6867
+ return -1;
6868
+ }
6869
+ function hitTestColumnDropSlot(clientX, _clientY) {
6870
+ const slots = buildColumnDropSlots();
6871
+ let best = null;
6872
+ for (const slot of slots) {
6873
+ const cx2 = slot.left + slot.width / 2;
6874
+ const dist = Math.abs(clientX - cx2);
6875
+ if (!best || dist < best.dist) best = { slot, dist };
6876
+ }
6877
+ return best?.slot ?? null;
6878
+ }
6879
+
6880
+ // src/lib/carousel.ts
6881
+ import { useEffect as useEffect7, useState as useState6 } from "react";
6882
+ var CAROUSEL_ATTR = "data-ohw-carousel";
6883
+ var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
6884
+ var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
6885
+ var CAROUSEL_EVENT = "ohw:carousel-change";
6886
+ function listCarouselKeys() {
6887
+ const keys = /* @__PURE__ */ new Set();
6888
+ document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
6889
+ const key = el.getAttribute("data-ohw-key");
6890
+ if (key) keys.add(key);
6891
+ });
6892
+ return [...keys];
6893
+ }
6894
+ function containersForKey(key) {
6895
+ return Array.from(
6896
+ document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
6897
+ );
6898
+ }
6899
+ function isCarouselKey(key) {
6900
+ return containersForKey(key).length > 0;
6901
+ }
6902
+ function parseSlides(raw) {
6903
+ if (!raw) return [];
6904
+ try {
6905
+ const parsed = JSON.parse(raw);
6906
+ if (!Array.isArray(parsed)) return [];
6907
+ return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
6908
+ } catch {
6909
+ return [];
6910
+ }
6911
+ }
6912
+ function readCarouselValue(key) {
6913
+ const container = containersForKey(key)[0];
6914
+ if (!container) return [];
6915
+ const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
6916
+ if (fromAttr.length > 0) return fromAttr;
6917
+ return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
6918
+ const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
6919
+ return { src: img?.src ?? "", alt: img?.alt ?? "" };
6920
+ });
6921
+ }
6922
+ function slideIndex(el) {
6923
+ const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
6924
+ const n = raw ? parseInt(raw, 10) : NaN;
6925
+ return Number.isFinite(n) ? n : 0;
6926
+ }
6927
+ function applyCarouselValue(key, slides) {
6928
+ const value = JSON.stringify(slides);
6929
+ for (const container of containersForKey(key)) {
6930
+ if (container.getAttribute(CAROUSEL_VALUE_ATTR) === value) continue;
6931
+ container.setAttribute(CAROUSEL_VALUE_ATTR, value);
6932
+ container.dispatchEvent(
6933
+ new CustomEvent(CAROUSEL_EVENT, { detail: { images: slides } })
6934
+ );
6935
+ }
6936
+ }
6937
+ function applyCarouselNode(key, val) {
6938
+ if (!isCarouselKey(key)) return false;
6939
+ applyCarouselValue(key, parseSlides(val));
6940
+ return true;
6941
+ }
6942
+ function useOhwCarousel(key, initial) {
6943
+ const [images, setImages] = useState6(initial);
6944
+ useEffect7(() => {
6945
+ const el = document.querySelector(
6946
+ `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
6947
+ );
6948
+ if (!el) return;
6949
+ const onChange = (e) => {
6950
+ const detail = e.detail;
6951
+ if (Array.isArray(detail?.images)) setImages(detail.images);
6952
+ };
6953
+ el.addEventListener(CAROUSEL_EVENT, onChange);
6954
+ return () => el.removeEventListener(CAROUSEL_EVENT, onChange);
6955
+ }, [key]);
6956
+ const bind = {
6957
+ [CAROUSEL_ATTR]: "",
6958
+ "data-ohw-key": key,
6959
+ [CAROUSEL_VALUE_ATTR]: JSON.stringify(images)
6960
+ };
6961
+ return { images, setImages, bind };
6962
+ }
6963
+
6446
6964
  // src/ui/navbar-container-chrome.tsx
6447
6965
  import { Plus as Plus2 } from "lucide-react";
6448
- import { jsx as jsx21 } from "react/jsx-runtime";
6966
+ import { jsx as jsx22 } from "react/jsx-runtime";
6449
6967
  function NavbarContainerChrome({
6450
6968
  rect,
6451
6969
  onAdd
6452
6970
  }) {
6453
6971
  const chromeGap = 6;
6454
- return /* @__PURE__ */ jsx21(
6972
+ return /* @__PURE__ */ jsx22(
6455
6973
  "div",
6456
6974
  {
6457
6975
  "data-ohw-navbar-container-chrome": "",
@@ -6463,7 +6981,7 @@ function NavbarContainerChrome({
6463
6981
  width: rect.width + chromeGap * 2,
6464
6982
  height: rect.height + chromeGap * 2
6465
6983
  },
6466
- children: /* @__PURE__ */ jsx21(
6984
+ children: /* @__PURE__ */ jsx22(
6467
6985
  "button",
6468
6986
  {
6469
6987
  type: "button",
@@ -6480,15 +6998,57 @@ function NavbarContainerChrome({
6480
6998
  e.stopPropagation();
6481
6999
  onAdd();
6482
7000
  },
6483
- children: /* @__PURE__ */ jsx21(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7001
+ children: /* @__PURE__ */ jsx22(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6484
7002
  }
6485
7003
  )
6486
7004
  }
6487
7005
  );
6488
7006
  }
6489
7007
 
7008
+ // src/ui/drop-indicator.tsx
7009
+ import * as React9 from "react";
7010
+ import { jsx as jsx23 } from "react/jsx-runtime";
7011
+ var dropIndicatorVariants = cva(
7012
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7013
+ {
7014
+ variants: {
7015
+ direction: {
7016
+ vertical: "h-6 w-[3px]",
7017
+ horizontal: "h-[3px] w-[200px]"
7018
+ },
7019
+ state: {
7020
+ default: "opacity-0",
7021
+ hover: "opacity-100",
7022
+ dragIdle: "opacity-40",
7023
+ dragActive: "opacity-100"
7024
+ }
7025
+ },
7026
+ defaultVariants: {
7027
+ direction: "vertical",
7028
+ state: "default"
7029
+ }
7030
+ }
7031
+ );
7032
+ var DropIndicator = React9.forwardRef(
7033
+ ({ className, direction, state, ...props }, ref) => {
7034
+ return /* @__PURE__ */ jsx23(
7035
+ "div",
7036
+ {
7037
+ ref,
7038
+ "data-slot": "drop-indicator",
7039
+ "data-direction": direction ?? "vertical",
7040
+ "data-state": state ?? "default",
7041
+ "aria-hidden": "true",
7042
+ className: cn(dropIndicatorVariants({ direction, state }), className),
7043
+ ...props
7044
+ }
7045
+ );
7046
+ }
7047
+ );
7048
+ DropIndicator.displayName = "DropIndicator";
7049
+
6490
7050
  // src/ui/badge.tsx
6491
- import { jsx as jsx22 } from "react/jsx-runtime";
7051
+ import { jsx as jsx24 } from "react/jsx-runtime";
6492
7052
  var badgeVariants = cva(
6493
7053
  "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",
6494
7054
  {
@@ -6506,12 +7066,12 @@ var badgeVariants = cva(
6506
7066
  }
6507
7067
  );
6508
7068
  function Badge({ className, variant, ...props }) {
6509
- return /* @__PURE__ */ jsx22("div", { className: cn(badgeVariants({ variant }), className), ...props });
7069
+ return /* @__PURE__ */ jsx24("div", { className: cn(badgeVariants({ variant }), className), ...props });
6510
7070
  }
6511
7071
 
6512
7072
  // src/OhhwellsBridge.tsx
6513
7073
  import { Link as Link2 } from "lucide-react";
6514
- import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
7074
+ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs14 } from "react/jsx-runtime";
6515
7075
  var PRIMARY2 = "#0885FE";
6516
7076
  var IMAGE_FADE_MS = 300;
6517
7077
  function runOpacityFade(el, onDone) {
@@ -6690,7 +7250,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6690
7250
  const root = createRoot(container);
6691
7251
  flushSync(() => {
6692
7252
  root.render(
6693
- /* @__PURE__ */ jsx23(
7253
+ /* @__PURE__ */ jsx25(
6694
7254
  SchedulingWidget,
6695
7255
  {
6696
7256
  notifyOnConnect,
@@ -6752,7 +7312,7 @@ function isDragHandleDisabled(el) {
6752
7312
  return raw === "true" || raw === "";
6753
7313
  });
6754
7314
  }
6755
- function collectEditableNodes() {
7315
+ function collectEditableNodes(extraContent) {
6756
7316
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6757
7317
  if (el.dataset.ohwEditable === "image") {
6758
7318
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6780,6 +7340,14 @@ function collectEditableNodes() {
6780
7340
  if (!key) return;
6781
7341
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6782
7342
  });
7343
+ if (extraContent) {
7344
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
7345
+ const text = extraContent[key];
7346
+ if (typeof text === "string" && text.length > 0) {
7347
+ nodes.push({ key, type: "meta", text });
7348
+ }
7349
+ }
7350
+ }
6783
7351
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6784
7352
  const key = el.dataset.ohwKey ?? "";
6785
7353
  const video = getVideoEl(el);
@@ -6787,13 +7355,10 @@ function collectEditableNodes() {
6787
7355
  nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
6788
7356
  nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
6789
7357
  });
6790
- const seen = /* @__PURE__ */ new Set();
6791
- return nodes.filter((node) => {
6792
- if (!node.key) return true;
6793
- if (seen.has(node.key)) return false;
6794
- seen.add(node.key);
6795
- return true;
6796
- });
7358
+ for (const key of listCarouselKeys()) {
7359
+ nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
7360
+ }
7361
+ return nodes;
6797
7362
  }
6798
7363
  function isMediaEditable(el) {
6799
7364
  const t = el.dataset.ohwEditable;
@@ -6881,6 +7446,29 @@ function getHrefKeyFromElement(el) {
6881
7446
  if (!key) return null;
6882
7447
  return { anchor, key };
6883
7448
  }
7449
+ function disableNativeHrefDrag(el) {
7450
+ if (el.draggable) el.draggable = false;
7451
+ if (el.getAttribute("draggable") !== "false") {
7452
+ el.setAttribute("draggable", "false");
7453
+ }
7454
+ }
7455
+ function clearTextSelection() {
7456
+ const sel = window.getSelection();
7457
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7458
+ }
7459
+ function armFooterPressDrag() {
7460
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7461
+ }
7462
+ function lockFooterDuringDrag() {
7463
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7464
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7465
+ clearTextSelection();
7466
+ }
7467
+ function unlockFooterDragInteraction() {
7468
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7469
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7470
+ clearTextSelection();
7471
+ }
6884
7472
  function isNavbarButton2(el) {
6885
7473
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
6886
7474
  }
@@ -6943,7 +7531,7 @@ function isInferredFooterGroup(el) {
6943
7531
  return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
6944
7532
  }
6945
7533
  function isNavigationContainer(el) {
6946
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
7534
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || isNavigationRoot(el) || isInferredFooterGroup(el);
6947
7535
  }
6948
7536
  function isPointOverNavigation(x, y) {
6949
7537
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
@@ -7153,6 +7741,8 @@ var ICONS = {
7153
7741
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
7154
7742
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
7155
7743
  };
7744
+ var TOOLBAR_PILL_PADDING = 2;
7745
+ var TOOLBAR_TOGGLE_GAP = 4;
7156
7746
  var TOOLBAR_GROUPS = [
7157
7747
  [
7158
7748
  { cmd: "bold", title: "Bold" },
@@ -7177,7 +7767,7 @@ function EditGlowChrome({
7177
7767
  dragDisabled = false
7178
7768
  }) {
7179
7769
  const GAP = 6;
7180
- return /* @__PURE__ */ jsxs13(
7770
+ return /* @__PURE__ */ jsxs14(
7181
7771
  "div",
7182
7772
  {
7183
7773
  ref: elRef,
@@ -7192,7 +7782,7 @@ function EditGlowChrome({
7192
7782
  zIndex: 2147483646
7193
7783
  },
7194
7784
  children: [
7195
- /* @__PURE__ */ jsx23(
7785
+ /* @__PURE__ */ jsx25(
7196
7786
  "div",
7197
7787
  {
7198
7788
  style: {
@@ -7205,7 +7795,7 @@ function EditGlowChrome({
7205
7795
  }
7206
7796
  }
7207
7797
  ),
7208
- reorderHrefKey && /* @__PURE__ */ jsx23(
7798
+ reorderHrefKey && /* @__PURE__ */ jsx25(
7209
7799
  "div",
7210
7800
  {
7211
7801
  "data-ohw-drag-handle-container": "",
@@ -7217,7 +7807,7 @@ function EditGlowChrome({
7217
7807
  transform: "translate(calc(-100% - 7px), -50%)",
7218
7808
  pointerEvents: dragDisabled ? "none" : "auto"
7219
7809
  },
7220
- children: /* @__PURE__ */ jsx23(
7810
+ children: /* @__PURE__ */ jsx25(
7221
7811
  DragHandle,
7222
7812
  {
7223
7813
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7321,7 +7911,7 @@ function FloatingToolbar({
7321
7911
  onEditLink
7322
7912
  }) {
7323
7913
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7324
- return /* @__PURE__ */ jsx23(
7914
+ return /* @__PURE__ */ jsx25(
7325
7915
  "div",
7326
7916
  {
7327
7917
  ref: elRef,
@@ -7331,14 +7921,23 @@ function FloatingToolbar({
7331
7921
  left,
7332
7922
  transform,
7333
7923
  zIndex: 2147483647,
7924
+ background: "#fff",
7925
+ border: "1px solid #E7E5E4",
7926
+ borderRadius: 6,
7927
+ boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
7928
+ display: "flex",
7929
+ alignItems: "center",
7930
+ padding: TOOLBAR_PILL_PADDING,
7931
+ gap: TOOLBAR_TOGGLE_GAP,
7932
+ fontFamily: "sans-serif",
7334
7933
  pointerEvents: "auto"
7335
7934
  },
7336
- children: /* @__PURE__ */ jsxs13(CustomToolbar, { children: [
7337
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
7338
- gi > 0 && /* @__PURE__ */ jsx23(CustomToolbarDivider, {}),
7935
+ children: /* @__PURE__ */ jsxs14(CustomToolbar, { children: [
7936
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs14(React10.Fragment, { children: [
7937
+ gi > 0 && /* @__PURE__ */ jsx25(CustomToolbarDivider, {}),
7339
7938
  btns.map((btn) => {
7340
7939
  const isActive = activeCommands.has(btn.cmd);
7341
- return /* @__PURE__ */ jsx23(
7940
+ return /* @__PURE__ */ jsx25(
7342
7941
  CustomToolbarButton,
7343
7942
  {
7344
7943
  title: btn.title,
@@ -7347,7 +7946,7 @@ function FloatingToolbar({
7347
7946
  e.preventDefault();
7348
7947
  onCommand(btn.cmd);
7349
7948
  },
7350
- children: /* @__PURE__ */ jsx23(
7949
+ children: /* @__PURE__ */ jsx25(
7351
7950
  "svg",
7352
7951
  {
7353
7952
  width: "16",
@@ -7368,7 +7967,7 @@ function FloatingToolbar({
7368
7967
  );
7369
7968
  })
7370
7969
  ] }, gi)),
7371
- showEditLink ? /* @__PURE__ */ jsx23(
7970
+ showEditLink ? /* @__PURE__ */ jsx25(
7372
7971
  CustomToolbarButton,
7373
7972
  {
7374
7973
  type: "button",
@@ -7382,7 +7981,7 @@ function FloatingToolbar({
7382
7981
  e.preventDefault();
7383
7982
  e.stopPropagation();
7384
7983
  },
7385
- children: /* @__PURE__ */ jsx23(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7984
+ children: /* @__PURE__ */ jsx25(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7386
7985
  }
7387
7986
  ) : null
7388
7987
  ] })
@@ -7399,7 +7998,7 @@ function StateToggle({
7399
7998
  states,
7400
7999
  onStateChange
7401
8000
  }) {
7402
- return /* @__PURE__ */ jsx23(
8001
+ return /* @__PURE__ */ jsx25(
7403
8002
  ToggleGroup,
7404
8003
  {
7405
8004
  "data-ohw-state-toggle": "",
@@ -7413,7 +8012,7 @@ function StateToggle({
7413
8012
  left: rect.right - 8,
7414
8013
  transform: "translateX(-100%)"
7415
8014
  },
7416
- children: states.map((state) => /* @__PURE__ */ jsx23(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8015
+ children: states.map((state) => /* @__PURE__ */ jsx25(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7417
8016
  }
7418
8017
  );
7419
8018
  }
@@ -7440,8 +8039,8 @@ function OhhwellsBridge() {
7440
8039
  const router = useRouter2();
7441
8040
  const searchParams = useSearchParams();
7442
8041
  const isEditMode = isEditSessionActive();
7443
- const [bridgeRoot, setBridgeRoot] = useState6(null);
7444
- useEffect7(() => {
8042
+ const [bridgeRoot, setBridgeRoot] = useState7(null);
8043
+ useEffect8(() => {
7445
8044
  const figtreeFontId = "ohw-figtree-font";
7446
8045
  if (!document.getElementById(figtreeFontId)) {
7447
8046
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7469,12 +8068,12 @@ function OhhwellsBridge() {
7469
8068
  const subdomainFromQuery = searchParams.get("subdomain");
7470
8069
  const subdomain = resolveSubdomain(subdomainFromQuery);
7471
8070
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7472
- const postToParent2 = useCallback3((data) => {
8071
+ const postToParent2 = useCallback4((data) => {
7473
8072
  if (typeof window !== "undefined" && window.parent !== window) {
7474
8073
  window.parent.postMessage(data, "*");
7475
8074
  }
7476
8075
  }, []);
7477
- const [fetchState, setFetchState] = useState6("idle");
8076
+ const [fetchState, setFetchState] = useState7("idle");
7478
8077
  const autoSaveTimers = useRef5(/* @__PURE__ */ new Map());
7479
8078
  const activeElRef = useRef5(null);
7480
8079
  const selectedElRef = useRef5(null);
@@ -7482,8 +8081,8 @@ function OhhwellsBridge() {
7482
8081
  const activeStateElRef = useRef5(null);
7483
8082
  const parentScrollRef = useRef5(null);
7484
8083
  const visibleViewportRef = useRef5(null);
7485
- const [dialogPortalContainer, setDialogPortalContainer] = useState6(null);
7486
- const attachVisibleViewport = useCallback3((node) => {
8084
+ const [dialogPortalContainer, setDialogPortalContainer] = useState7(null);
8085
+ const attachVisibleViewport = useCallback4((node) => {
7487
8086
  visibleViewportRef.current = node;
7488
8087
  setDialogPortalContainer(node);
7489
8088
  if (node) applyVisibleViewport(node, parentScrollRef.current);
@@ -7493,8 +8092,10 @@ function OhhwellsBridge() {
7493
8092
  const hoveredImageRef = useRef5(null);
7494
8093
  const hoveredImageHasTextOverlapRef = useRef5(false);
7495
8094
  const dragOverElRef = useRef5(null);
7496
- const [mediaHover, setMediaHover] = useState6(null);
7497
- const [uploadingRects, setUploadingRects] = useState6({});
8095
+ const [mediaHover, setMediaHover] = useState7(null);
8096
+ const [carouselHover, setCarouselHover] = useState7(null);
8097
+ const [uploadingRects, setUploadingRects] = useState7({});
8098
+ const pendingUploadRectRef = useRef5(null);
7498
8099
  const hoveredGapRef = useRef5(null);
7499
8100
  const imageUnhoverTimerRef = useRef5(null);
7500
8101
  const imageShowTimerRef = useRef5(null);
@@ -7519,30 +8120,38 @@ function OhhwellsBridge() {
7519
8120
  postToParentRef.current = postToParent2;
7520
8121
  const sectionsLoadedRef = useRef5(false);
7521
8122
  const pendingScheduleConfigRequests = useRef5([]);
7522
- const [toolbarRect, setToolbarRect] = useState6(null);
7523
- const [toolbarVariant, setToolbarVariant] = useState6("none");
8123
+ const [toolbarRect, setToolbarRect] = useState7(null);
8124
+ const [toolbarVariant, setToolbarVariant] = useState7("none");
7524
8125
  const toolbarVariantRef = useRef5("none");
7525
8126
  toolbarVariantRef.current = toolbarVariant;
7526
- const [reorderHrefKey, setReorderHrefKey] = useState6(null);
7527
- const [reorderDragDisabled, setReorderDragDisabled] = useState6(false);
7528
- const [toggleState, setToggleState] = useState6(null);
7529
- const [maxBadge, setMaxBadge] = useState6(null);
7530
- const [activeCommands, setActiveCommands] = useState6(/* @__PURE__ */ new Set());
7531
- const [sectionGap, setSectionGap] = useState6(null);
7532
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState6(false);
8127
+ const [reorderHrefKey, setReorderHrefKey] = useState7(null);
8128
+ const [reorderDragDisabled, setReorderDragDisabled] = useState7(false);
8129
+ const [toggleState, setToggleState] = useState7(null);
8130
+ const [maxBadge, setMaxBadge] = useState7(null);
8131
+ const [activeCommands, setActiveCommands] = useState7(/* @__PURE__ */ new Set());
8132
+ const [sectionGap, setSectionGap] = useState7(null);
8133
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState7(false);
7533
8134
  const hoveredNavContainerRef = useRef5(null);
7534
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState6(null);
8135
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState7(null);
7535
8136
  const hoveredItemElRef = useRef5(null);
7536
- const [hoveredItemRect, setHoveredItemRect] = useState6(null);
8137
+ const [hoveredItemRect, setHoveredItemRect] = useState7(null);
7537
8138
  const siblingHintElRef = useRef5(null);
7538
- const [siblingHintRect, setSiblingHintRect] = useState6(null);
7539
- const [isItemDragging, setIsItemDragging] = useState6(false);
7540
- const [linkPopover, setLinkPopover] = useState6(null);
8139
+ const [siblingHintRect, setSiblingHintRect] = useState7(null);
8140
+ const [siblingHintRects, setSiblingHintRects] = useState7([]);
8141
+ const [isItemDragging, setIsItemDragging] = useState7(false);
8142
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState7(false);
8143
+ const footerDragRef = useRef5(null);
8144
+ const [footerDropSlots, setFooterDropSlots] = useState7([]);
8145
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState7(null);
8146
+ const [draggedItemRect, setDraggedItemRect] = useState7(null);
8147
+ const footerPointerDragRef = useRef5(null);
8148
+ const suppressNextClickRef = useRef5(false);
8149
+ const [linkPopover, setLinkPopover] = useState7(null);
7541
8150
  const linkPopoverSessionRef = useRef5(null);
7542
8151
  const addNavAfterAnchorRef = useRef5(null);
7543
8152
  const editContentRef = useRef5({});
7544
- const [sitePages, setSitePages] = useState6([]);
7545
- const [sectionsByPath, setSectionsByPath] = useState6({});
8153
+ const [sitePages, setSitePages] = useState7([]);
8154
+ const [sectionsByPath, setSectionsByPath] = useState7({});
7546
8155
  const sectionsPrefetchGenRef = useRef5(0);
7547
8156
  const setLinkPopoverRef = useRef5(setLinkPopover);
7548
8157
  const linkPopoverPanelRef = useRef5(null);
@@ -7553,7 +8162,7 @@ function OhhwellsBridge() {
7553
8162
  const bumpLinkPopoverGrace = () => {
7554
8163
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7555
8164
  };
7556
- const runSectionsPrefetch = useCallback3((pages) => {
8165
+ const runSectionsPrefetch = useCallback4((pages) => {
7557
8166
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7558
8167
  const gen = ++sectionsPrefetchGenRef.current;
7559
8168
  const paths = pages.map((p) => p.path);
@@ -7574,8 +8183,12 @@ function OhhwellsBridge() {
7574
8183
  }, [isEditMode, pathname]);
7575
8184
  const runSectionsPrefetchRef = useRef5(runSectionsPrefetch);
7576
8185
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7577
- useEffect7(() => {
7578
- if (!linkPopover) return;
8186
+ useEffect8(() => {
8187
+ if (!linkPopover) {
8188
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
8189
+ return;
8190
+ }
8191
+ document.documentElement.setAttribute("data-ohw-link-popover-open", "");
7579
8192
  if (hoveredImageRef.current) {
7580
8193
  hoveredImageRef.current = null;
7581
8194
  hoveredImageHasTextOverlapRef.current = false;
@@ -7602,14 +8215,10 @@ function OhhwellsBridge() {
7602
8215
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7603
8216
  postToParent2({ type: "ow:image-unhover" });
7604
8217
  return () => {
7605
- postToParent2({ type: "ow:link-modal-lock", locked: false });
7606
- html.style.overflow = prevHtmlOverflow;
7607
- body.style.overflow = prevBodyOverflow;
7608
- document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
7609
- document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
8218
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
7610
8219
  };
7611
8220
  }, [linkPopover, postToParent2]);
7612
- useEffect7(() => {
8221
+ useEffect8(() => {
7613
8222
  if (!isEditMode) return;
7614
8223
  const useFixtures = shouldUseDevFixtures();
7615
8224
  if (useFixtures) {
@@ -7633,14 +8242,14 @@ function OhhwellsBridge() {
7633
8242
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
7634
8243
  return () => window.removeEventListener("message", onSitePages);
7635
8244
  }, [isEditMode, postToParent2]);
7636
- useEffect7(() => {
8245
+ useEffect8(() => {
7637
8246
  if (!isEditMode || shouldUseDevFixtures()) return;
7638
8247
  void loadAllSectionsManifest().then((manifest) => {
7639
8248
  if (Object.keys(manifest).length === 0) return;
7640
8249
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7641
8250
  });
7642
8251
  }, [isEditMode]);
7643
- useEffect7(() => {
8252
+ useEffect8(() => {
7644
8253
  const update = () => {
7645
8254
  const el = activeElRef.current ?? selectedElRef.current;
7646
8255
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7664,10 +8273,10 @@ function OhhwellsBridge() {
7664
8273
  vvp.removeEventListener("resize", update);
7665
8274
  };
7666
8275
  }, []);
7667
- const refreshStateRules = useCallback3(() => {
8276
+ const refreshStateRules = useCallback4(() => {
7668
8277
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7669
8278
  }, []);
7670
- const processConfigRequest = useCallback3((insertAfterVal) => {
8279
+ const processConfigRequest = useCallback4((insertAfterVal) => {
7671
8280
  const tracker = getSectionsTracker();
7672
8281
  let entries = [];
7673
8282
  try {
@@ -7690,7 +8299,7 @@ function OhhwellsBridge() {
7690
8299
  }
7691
8300
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7692
8301
  }, [isEditMode]);
7693
- const deactivate = useCallback3(() => {
8302
+ const deactivate = useCallback4(() => {
7694
8303
  const el = activeElRef.current;
7695
8304
  if (!el) return;
7696
8305
  const key = el.dataset.ohwKey;
@@ -7721,12 +8330,14 @@ function OhhwellsBridge() {
7721
8330
  setToolbarShowEditLink(false);
7722
8331
  postToParent2({ type: "ow:exit-edit" });
7723
8332
  }, [postToParent2]);
7724
- const deselect = useCallback3(() => {
8333
+ const deselect = useCallback4(() => {
7725
8334
  selectedElRef.current = null;
7726
8335
  setReorderHrefKey(null);
7727
8336
  setReorderDragDisabled(false);
8337
+ setIsFooterFrameSelection(false);
7728
8338
  siblingHintElRef.current = null;
7729
8339
  setSiblingHintRect(null);
8340
+ setSiblingHintRects([]);
7730
8341
  setIsItemDragging(false);
7731
8342
  hoveredNavContainerRef.current = null;
7732
8343
  setHoveredNavContainerRect(null);
@@ -7735,7 +8346,7 @@ function OhhwellsBridge() {
7735
8346
  setToolbarVariant("none");
7736
8347
  }
7737
8348
  }, []);
7738
- const reselectNavigationItem = useCallback3((navAnchor) => {
8349
+ const reselectNavigationItem = useCallback4((navAnchor) => {
7739
8350
  selectedElRef.current = navAnchor;
7740
8351
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
7741
8352
  setReorderHrefKey(key);
@@ -7745,7 +8356,7 @@ function OhhwellsBridge() {
7745
8356
  setToolbarShowEditLink(false);
7746
8357
  setActiveCommands(/* @__PURE__ */ new Set());
7747
8358
  }, []);
7748
- const commitNavigationTextEdit = useCallback3((navAnchor) => {
8359
+ const commitNavigationTextEdit = useCallback4((navAnchor) => {
7749
8360
  const el = activeElRef.current;
7750
8361
  if (!el) return;
7751
8362
  const key = el.dataset.ohwKey;
@@ -7771,7 +8382,7 @@ function OhhwellsBridge() {
7771
8382
  postToParent2({ type: "ow:exit-edit" });
7772
8383
  reselectNavigationItem(navAnchor);
7773
8384
  }, [postToParent2, reselectNavigationItem]);
7774
- const handleAddTopLevelNavItem = useCallback3(() => {
8385
+ const handleAddTopLevelNavItem = useCallback4(() => {
7775
8386
  const items = listNavbarItems();
7776
8387
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7777
8388
  deselectRef.current();
@@ -7783,90 +8394,387 @@ function OhhwellsBridge() {
7783
8394
  intent: "add-nav"
7784
8395
  });
7785
8396
  }, []);
7786
- const handleItemDragStart = useCallback3(() => {
7787
- siblingHintElRef.current = null;
7788
- setSiblingHintRect(null);
7789
- setIsItemDragging(true);
7790
- }, []);
7791
- const handleItemDragEnd = useCallback3(() => {
8397
+ const clearFooterDragVisuals = useCallback4(() => {
8398
+ footerDragRef.current = null;
8399
+ setSiblingHintRects([]);
8400
+ setFooterDropSlots([]);
8401
+ setActiveFooterDropIndex(null);
8402
+ setDraggedItemRect(null);
7792
8403
  setIsItemDragging(false);
8404
+ unlockFooterDragInteraction();
7793
8405
  }, []);
7794
- reselectNavigationItemRef.current = reselectNavigationItem;
7795
- commitNavigationTextEditRef.current = commitNavigationTextEdit;
7796
- const select = useCallback3((anchor) => {
7797
- if (!isNavigationItem(anchor)) return;
7798
- if (activeElRef.current) deactivate();
7799
- selectedElRef.current = anchor;
7800
- clearHrefKeyHover(anchor);
7801
- hoveredNavContainerRef.current = null;
7802
- setHoveredNavContainerRect(null);
7803
- setHoveredItemRect(null);
7804
- hoveredItemElRef.current = null;
7805
- siblingHintElRef.current = null;
7806
- setSiblingHintRect(null);
7807
- setIsItemDragging(false);
7808
- const { key, disabled } = getNavigationItemReorderState(anchor);
7809
- setReorderHrefKey(key);
7810
- setReorderDragDisabled(disabled);
7811
- setToolbarVariant("link-action");
7812
- setToolbarRect(anchor.getBoundingClientRect());
7813
- setToolbarShowEditLink(false);
7814
- setActiveCommands(/* @__PURE__ */ new Set());
7815
- }, [deactivate]);
7816
- const selectFrame = useCallback3((el) => {
7817
- if (!isNavigationContainer(el)) return;
7818
- if (activeElRef.current) deactivate();
7819
- selectedElRef.current = el;
7820
- clearHrefKeyHover(el);
7821
- hoveredNavContainerRef.current = null;
7822
- setHoveredNavContainerRect(null);
7823
- setHoveredItemRect(null);
7824
- hoveredItemElRef.current = null;
7825
- siblingHintElRef.current = null;
7826
- setSiblingHintRect(null);
7827
- setIsItemDragging(false);
7828
- setReorderHrefKey(null);
7829
- setReorderDragDisabled(false);
7830
- setToolbarVariant("select-frame");
7831
- setToolbarRect(el.getBoundingClientRect());
7832
- setToolbarShowEditLink(false);
7833
- setActiveCommands(/* @__PURE__ */ new Set());
7834
- }, [deactivate]);
7835
- const activate = useCallback3((el, options) => {
7836
- if (activeElRef.current === el) return;
7837
- selectedElRef.current = null;
7838
- deactivate();
7839
- if (hoveredImageRef.current) {
7840
- hoveredImageRef.current = null;
7841
- setMediaHover(null);
7842
- }
7843
- setToolbarVariant("rich-text");
7844
- siblingHintElRef.current = null;
7845
- setSiblingHintRect(null);
7846
- setIsItemDragging(false);
7847
- el.setAttribute("contenteditable", "true");
7848
- el.removeAttribute("data-ohw-hovered");
7849
- el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
7850
- activeElRef.current = el;
7851
- originalContentRef.current = el.innerHTML;
7852
- el.focus();
7853
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7854
- placeCaretAtPoint(el, options.caretX, options.caretY);
8406
+ const refreshFooterDragVisuals = useCallback4((session, activeSlot, clientX, clientY) => {
8407
+ const dragged = session.draggedEl;
8408
+ setDraggedItemRect(dragged.getBoundingClientRect());
8409
+ if (typeof clientX === "number" && typeof clientY === "number") {
8410
+ session.lastClientX = clientX;
8411
+ session.lastClientY = clientY;
7855
8412
  }
7856
- setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7857
- const navAnchor = getNavigationItemAnchor(el);
7858
- if (navAnchor) {
7859
- setReorderHrefKey(null);
7860
- setReorderDragDisabled(false);
7861
- } else {
7862
- const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
7863
- setReorderHrefKey(reorderKey);
7864
- setReorderDragDisabled(reorderDisabled);
8413
+ session.activeSlot = activeSlot;
8414
+ if (session.kind === "link") {
8415
+ const columns2 = listFooterColumns();
8416
+ const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
8417
+ if (activeSlot) focusCols.add(activeSlot.columnIndex);
8418
+ const siblingRects = [];
8419
+ columns2.forEach((col, i) => {
8420
+ if (!focusCols.has(i)) return;
8421
+ for (const link of listFooterLinksInColumn(col)) {
8422
+ if (link === dragged) continue;
8423
+ siblingRects.push(link.getBoundingClientRect());
8424
+ }
8425
+ });
8426
+ setSiblingHintRects(siblingRects);
8427
+ const slots2 = [];
8428
+ columns2.forEach((col, i) => {
8429
+ slots2.push(...buildLinkDropSlots(col, i));
8430
+ });
8431
+ setFooterDropSlots(slots2);
8432
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
8433
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
8434
+ return;
8435
+ }
8436
+ const columns = listFooterColumns();
8437
+ setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
8438
+ const slots = buildColumnDropSlots();
8439
+ setFooterDropSlots(slots);
8440
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
8441
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
8442
+ }, []);
8443
+ const refreshFooterDragVisualsRef = useRef5(refreshFooterDragVisuals);
8444
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
8445
+ const commitFooterDragRef = useRef5(() => {
8446
+ });
8447
+ const beginFooterDragRef = useRef5(() => {
8448
+ });
8449
+ const beginFooterDrag = useCallback4(
8450
+ (session) => {
8451
+ const rect = session.draggedEl.getBoundingClientRect();
8452
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
8453
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
8454
+ session.activeSlot = session.activeSlot ?? null;
8455
+ footerDragRef.current = session;
8456
+ setIsItemDragging(true);
8457
+ lockFooterDuringDrag();
8458
+ siblingHintElRef.current = null;
8459
+ setSiblingHintRect(null);
8460
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
8461
+ setToolbarRect(rect);
8462
+ }
8463
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
8464
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
8465
+ },
8466
+ [refreshFooterDragVisuals]
8467
+ );
8468
+ beginFooterDragRef.current = beginFooterDrag;
8469
+ const commitFooterDrag = useCallback4(
8470
+ (clientX, clientY) => {
8471
+ const session = footerDragRef.current;
8472
+ if (!session) {
8473
+ clearFooterDragVisuals();
8474
+ return;
8475
+ }
8476
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
8477
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
8478
+ let nextOrder = null;
8479
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
8480
+ if (session.kind === "link" && session.hrefKey && slot) {
8481
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
8482
+ } else if (session.kind === "column" && slot) {
8483
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
8484
+ }
8485
+ const wasSelected = session.wasSelected;
8486
+ const draggedEl = session.draggedEl;
8487
+ const hrefKey = session.hrefKey;
8488
+ let movedColumnIndex = null;
8489
+ if (session.kind === "column" && slot && nextOrder) {
8490
+ let adjusted = slot.insertIndex;
8491
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
8492
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
8493
+ }
8494
+ clearFooterDragVisuals();
8495
+ const applySelectionAfterDrop = () => {
8496
+ if (!wasSelected) {
8497
+ deselectRef.current();
8498
+ return;
8499
+ }
8500
+ if (hrefKey) {
8501
+ const link = document.querySelector(
8502
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
8503
+ );
8504
+ if (link && isNavigationItem(link)) {
8505
+ selectRef.current(link);
8506
+ return;
8507
+ }
8508
+ }
8509
+ if (movedColumnIndex !== null && nextOrder) {
8510
+ const colKeys = nextOrder[movedColumnIndex] ?? [];
8511
+ let column = null;
8512
+ for (const key of colKeys) {
8513
+ const link = document.querySelector(
8514
+ `footer [data-ohw-href-key="${CSS.escape(key)}"]`
8515
+ );
8516
+ if (link) {
8517
+ column = findFooterColumnForLink(link);
8518
+ if (column) break;
8519
+ }
8520
+ }
8521
+ if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
8522
+ if (column && isNavigationContainer(column)) {
8523
+ selectFrameRef.current(column);
8524
+ return;
8525
+ }
8526
+ }
8527
+ if (document.body.contains(draggedEl)) {
8528
+ if (isNavigationItem(draggedEl)) {
8529
+ selectRef.current(draggedEl);
8530
+ return;
8531
+ }
8532
+ if (isNavigationContainer(draggedEl)) {
8533
+ selectFrameRef.current(draggedEl);
8534
+ return;
8535
+ }
8536
+ }
8537
+ deselectRef.current();
8538
+ };
8539
+ if (!wasSelected) {
8540
+ deselectRef.current();
8541
+ }
8542
+ if (nextOrder) {
8543
+ const orderJson = JSON.stringify(nextOrder);
8544
+ editContentRef.current = {
8545
+ ...editContentRef.current,
8546
+ [FOOTER_ORDER_KEY]: orderJson
8547
+ };
8548
+ applyFooterOrder(nextOrder);
8549
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8550
+ postToParentRef.current({
8551
+ type: "ow:change",
8552
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
8553
+ });
8554
+ requestAnimationFrame(() => {
8555
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
8556
+ applyFooterOrder(nextOrder);
8557
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8558
+ }
8559
+ requestAnimationFrame(applySelectionAfterDrop);
8560
+ });
8561
+ return;
8562
+ }
8563
+ applySelectionAfterDrop();
8564
+ },
8565
+ [clearFooterDragVisuals]
8566
+ );
8567
+ commitFooterDragRef.current = commitFooterDrag;
8568
+ const startFooterLinkDrag = useCallback4(
8569
+ (anchor, clientX, clientY, wasSelected) => {
8570
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
8571
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
8572
+ const column = findFooterColumnForLink(anchor);
8573
+ const columns = listFooterColumns();
8574
+ beginFooterDrag({
8575
+ kind: "link",
8576
+ hrefKey,
8577
+ columnEl: column,
8578
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
8579
+ wasSelected,
8580
+ draggedEl: anchor,
8581
+ lastClientX: clientX,
8582
+ lastClientY: clientY,
8583
+ activeSlot: null
8584
+ });
8585
+ return true;
8586
+ },
8587
+ [beginFooterDrag]
8588
+ );
8589
+ const startFooterColumnDrag = useCallback4(
8590
+ (columnEl, clientX, clientY, wasSelected) => {
8591
+ const columns = listFooterColumns();
8592
+ const idx = columns.indexOf(columnEl);
8593
+ if (idx < 0) return false;
8594
+ beginFooterDrag({
8595
+ kind: "column",
8596
+ hrefKey: null,
8597
+ columnEl,
8598
+ sourceColumnIndex: idx,
8599
+ wasSelected,
8600
+ draggedEl: columnEl,
8601
+ lastClientX: clientX,
8602
+ lastClientY: clientY,
8603
+ activeSlot: null
8604
+ });
8605
+ return true;
8606
+ },
8607
+ [beginFooterDrag]
8608
+ );
8609
+ const handleItemDragStart = useCallback4(
8610
+ (e) => {
8611
+ const selected = selectedElRef.current;
8612
+ if (!selected) {
8613
+ setIsItemDragging(true);
8614
+ return;
8615
+ }
8616
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
8617
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
8618
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
8619
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8620
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
8621
+ }
8622
+ siblingHintElRef.current = null;
8623
+ setSiblingHintRect(null);
8624
+ setIsItemDragging(true);
8625
+ },
8626
+ [startFooterColumnDrag, startFooterLinkDrag]
8627
+ );
8628
+ const handleItemDragEnd = useCallback4(
8629
+ (e) => {
8630
+ if (footerDragRef.current) {
8631
+ const x = e?.clientX;
8632
+ const y = e?.clientY;
8633
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
8634
+ commitFooterDrag(x, y);
8635
+ } else {
8636
+ commitFooterDrag();
8637
+ }
8638
+ return;
8639
+ }
8640
+ setIsItemDragging(false);
8641
+ },
8642
+ [commitFooterDrag]
8643
+ );
8644
+ const handleItemChromePointerDown = useCallback4((e) => {
8645
+ if (e.button !== 0) return;
8646
+ const selected = selectedElRef.current;
8647
+ if (!selected) return;
8648
+ armFooterPressDrag();
8649
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
8650
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
8651
+ footerPointerDragRef.current = {
8652
+ el: selected,
8653
+ kind: "link",
8654
+ startX: e.clientX,
8655
+ startY: e.clientY,
8656
+ pointerId: e.pointerId,
8657
+ wasSelected: true,
8658
+ started: false
8659
+ };
8660
+ return;
8661
+ }
8662
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8663
+ footerPointerDragRef.current = {
8664
+ el: selected,
8665
+ kind: "column",
8666
+ startX: e.clientX,
8667
+ startY: e.clientY,
8668
+ pointerId: e.pointerId,
8669
+ wasSelected: true,
8670
+ started: false
8671
+ };
7865
8672
  }
7866
- setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7867
- postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7868
- requestAnimationFrame(() => refreshActiveCommandsRef.current());
7869
- }, [deactivate, postToParent2]);
8673
+ }, []);
8674
+ const handleItemChromeClick = useCallback4((clientX, clientY) => {
8675
+ if (suppressNextClickRef.current) {
8676
+ suppressNextClickRef.current = false;
8677
+ return;
8678
+ }
8679
+ const selected = selectedElRef.current;
8680
+ if (!selected || !isNavigationItem(selected)) return;
8681
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
8682
+ if (!editable) return;
8683
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
8684
+ }, []);
8685
+ reselectNavigationItemRef.current = reselectNavigationItem;
8686
+ commitNavigationTextEditRef.current = commitNavigationTextEdit;
8687
+ const select = useCallback4(
8688
+ (anchor) => {
8689
+ if (!isNavigationItem(anchor)) return;
8690
+ if (activeElRef.current) deactivate();
8691
+ selectedElRef.current = anchor;
8692
+ clearHrefKeyHover(anchor);
8693
+ hoveredNavContainerRef.current = null;
8694
+ setHoveredNavContainerRect(null);
8695
+ setHoveredItemRect(null);
8696
+ hoveredItemElRef.current = null;
8697
+ siblingHintElRef.current = null;
8698
+ setSiblingHintRect(null);
8699
+ setSiblingHintRects([]);
8700
+ setIsItemDragging(false);
8701
+ const { key, disabled } = getNavigationItemReorderState(anchor);
8702
+ setReorderHrefKey(key);
8703
+ setReorderDragDisabled(disabled);
8704
+ setIsFooterFrameSelection(false);
8705
+ setToolbarVariant("link-action");
8706
+ setToolbarRect(anchor.getBoundingClientRect());
8707
+ setToolbarShowEditLink(false);
8708
+ setActiveCommands(/* @__PURE__ */ new Set());
8709
+ },
8710
+ [deactivate]
8711
+ );
8712
+ const selectFrame = useCallback4(
8713
+ (el) => {
8714
+ if (!isNavigationContainer(el)) return;
8715
+ if (activeElRef.current) deactivate();
8716
+ const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8717
+ selectedElRef.current = el;
8718
+ clearHrefKeyHover(el);
8719
+ hoveredNavContainerRef.current = null;
8720
+ setHoveredNavContainerRect(null);
8721
+ setHoveredItemRect(null);
8722
+ hoveredItemElRef.current = null;
8723
+ siblingHintElRef.current = null;
8724
+ setSiblingHintRect(null);
8725
+ setSiblingHintRects(
8726
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
8727
+ );
8728
+ setIsItemDragging(false);
8729
+ setReorderHrefKey(null);
8730
+ setReorderDragDisabled(false);
8731
+ setIsFooterFrameSelection(isFooterColumn);
8732
+ setToolbarVariant("select-frame");
8733
+ setToolbarRect(el.getBoundingClientRect());
8734
+ setToolbarShowEditLink(false);
8735
+ setActiveCommands(/* @__PURE__ */ new Set());
8736
+ },
8737
+ [deactivate]
8738
+ );
8739
+ const activate = useCallback4(
8740
+ (el, options) => {
8741
+ if (activeElRef.current === el) return;
8742
+ selectedElRef.current = null;
8743
+ deactivate();
8744
+ if (hoveredImageRef.current) {
8745
+ hoveredImageRef.current = null;
8746
+ postToParentRef.current({ type: "ow:image-unhover" });
8747
+ }
8748
+ setToolbarVariant("rich-text");
8749
+ siblingHintElRef.current = null;
8750
+ setSiblingHintRect(null);
8751
+ setSiblingHintRects([]);
8752
+ setIsItemDragging(false);
8753
+ el.setAttribute("contenteditable", "true");
8754
+ el.removeAttribute("data-ohw-hovered");
8755
+ el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
8756
+ activeElRef.current = el;
8757
+ originalContentRef.current = el.innerHTML;
8758
+ el.focus();
8759
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8760
+ placeCaretAtPoint(el, options.caretX, options.caretY);
8761
+ }
8762
+ setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
8763
+ const navAnchor = getNavigationItemAnchor(el);
8764
+ if (navAnchor) {
8765
+ setReorderHrefKey(null);
8766
+ setReorderDragDisabled(false);
8767
+ } else {
8768
+ const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
8769
+ setReorderHrefKey(reorderKey);
8770
+ setReorderDragDisabled(reorderDisabled);
8771
+ }
8772
+ setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
8773
+ postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
8774
+ requestAnimationFrame(() => refreshActiveCommandsRef.current());
8775
+ },
8776
+ [deactivate, postToParent2]
8777
+ );
7870
8778
  activateRef.current = activate;
7871
8779
  deactivateRef.current = deactivate;
7872
8780
  selectRef.current = select;
@@ -7882,6 +8790,7 @@ function OhhwellsBridge() {
7882
8790
  for (const [key, val] of Object.entries(content)) {
7883
8791
  if (key === "__ohw_sections") continue;
7884
8792
  if (applyVideoSettingNode(key, val)) continue;
8793
+ if (applyCarouselNode(key, val)) continue;
7885
8794
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7886
8795
  if (el.dataset.ohwEditable === "image") {
7887
8796
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7913,6 +8822,7 @@ function OhhwellsBridge() {
7913
8822
  applyLinkByKey(key, val);
7914
8823
  }
7915
8824
  reconcileNavbarItemsFromContent(content);
8825
+ reconcileFooterOrderFromContent(content);
7916
8826
  enforceLinkHrefs();
7917
8827
  initSectionsFromContent(content, true);
7918
8828
  sectionsLoadedRef.current = true;
@@ -7941,7 +8851,7 @@ function OhhwellsBridge() {
7941
8851
  cancelled = true;
7942
8852
  };
7943
8853
  }, [subdomain, isEditMode]);
7944
- useEffect7(() => {
8854
+ useEffect8(() => {
7945
8855
  if (!subdomain || isEditMode) return;
7946
8856
  let debounceTimer = null;
7947
8857
  let observer = null;
@@ -7954,6 +8864,7 @@ function OhhwellsBridge() {
7954
8864
  for (const [key, val] of Object.entries(content)) {
7955
8865
  if (key === "__ohw_sections") continue;
7956
8866
  if (applyVideoSettingNode(key, val)) continue;
8867
+ if (applyCarouselNode(key, val)) continue;
7957
8868
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7958
8869
  if (el.dataset.ohwEditable === "image") {
7959
8870
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7973,6 +8884,7 @@ function OhhwellsBridge() {
7973
8884
  applyLinkByKey(key, val);
7974
8885
  }
7975
8886
  reconcileNavbarItemsFromContent(content);
8887
+ reconcileFooterOrderFromContent(content);
7976
8888
  } finally {
7977
8889
  observer?.observe(document.body, { childList: true, subtree: true });
7978
8890
  }
@@ -7996,10 +8908,10 @@ function OhhwellsBridge() {
7996
8908
  const visible = Boolean(subdomain) && fetchState !== "done";
7997
8909
  el.style.display = visible ? "flex" : "none";
7998
8910
  }, [subdomain, fetchState]);
7999
- useEffect7(() => {
8911
+ useEffect8(() => {
8000
8912
  postToParent2({ type: "ow:navigation", path: pathname });
8001
8913
  }, [pathname, postToParent2]);
8002
- useEffect7(() => {
8914
+ useEffect8(() => {
8003
8915
  if (!isEditMode) return;
8004
8916
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8005
8917
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -8007,23 +8919,50 @@ function OhhwellsBridge() {
8007
8919
  deselectRef.current();
8008
8920
  deactivateRef.current();
8009
8921
  }, [pathname, isEditMode]);
8010
- useEffect7(() => {
8922
+ useEffect8(() => {
8011
8923
  const contentForNav = () => {
8012
8924
  if (isEditMode) return editContentRef.current;
8013
8925
  if (!subdomain) return {};
8014
8926
  return contentCache.get(subdomain) ?? {};
8015
8927
  };
8016
- const run = () => reconcileNavbarItemsFromContent(contentForNav());
8928
+ let applying = false;
8929
+ let rafId = null;
8930
+ const run = () => {
8931
+ if (footerDragRef.current || applying) return;
8932
+ applying = true;
8933
+ try {
8934
+ const content = contentForNav();
8935
+ reconcileNavbarItemsFromContent(content);
8936
+ reconcileFooterOrderFromContent(content);
8937
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
8938
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
8939
+ disableNativeHrefDrag(el);
8940
+ }
8941
+ });
8942
+ } finally {
8943
+ applying = false;
8944
+ }
8945
+ };
8946
+ const scheduleRun = () => {
8947
+ if (rafId !== null) cancelAnimationFrame(rafId);
8948
+ rafId = requestAnimationFrame(() => {
8949
+ rafId = null;
8950
+ run();
8951
+ });
8952
+ };
8017
8953
  run();
8018
8954
  const nav = document.querySelector("nav");
8019
- if (!nav) return;
8020
- const observer = new MutationObserver(() => {
8021
- requestAnimationFrame(run);
8022
- });
8023
- observer.observe(nav, { childList: true, subtree: true });
8024
- return () => observer.disconnect();
8955
+ const footer = document.querySelector("footer");
8956
+ const observer = new MutationObserver(scheduleRun);
8957
+ if (nav) observer.observe(nav, { childList: true, subtree: true });
8958
+ if (footer) observer.observe(footer, { childList: true, subtree: true });
8959
+ if (!nav && !footer) return;
8960
+ return () => {
8961
+ observer.disconnect();
8962
+ if (rafId !== null) cancelAnimationFrame(rafId);
8963
+ };
8025
8964
  }, [isEditMode, pathname, subdomain, fetchState]);
8026
- useEffect7(() => {
8965
+ useEffect8(() => {
8027
8966
  if (!isEditMode) return;
8028
8967
  const measure = () => {
8029
8968
  const h = document.body.scrollHeight;
@@ -8047,7 +8986,7 @@ function OhhwellsBridge() {
8047
8986
  window.removeEventListener("resize", handleResize);
8048
8987
  };
8049
8988
  }, [pathname, isEditMode, postToParent2]);
8050
- useEffect7(() => {
8989
+ useEffect8(() => {
8051
8990
  if (!subdomainFromQuery || isEditMode) return;
8052
8991
  const handleClick = (e) => {
8053
8992
  const anchor = e.target.closest("a");
@@ -8063,7 +9002,7 @@ function OhhwellsBridge() {
8063
9002
  document.addEventListener("click", handleClick, true);
8064
9003
  return () => document.removeEventListener("click", handleClick, true);
8065
9004
  }, [subdomainFromQuery, isEditMode, router]);
8066
- useEffect7(() => {
9005
+ useEffect8(() => {
8067
9006
  if (!isEditMode) {
8068
9007
  editStylesRef.current?.base.remove();
8069
9008
  editStylesRef.current?.forceHover.remove();
@@ -8086,7 +9025,39 @@ function OhhwellsBridge() {
8086
9025
  [data-ohw-editable] {
8087
9026
  display: block;
8088
9027
  }
8089
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
9028
+ /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
9029
+ footer [data-ohw-href-key] {
9030
+ -webkit-user-drag: none !important;
9031
+ }
9032
+ footer [data-ohw-footer-col]:hover {
9033
+ outline: 1.5px dashed ${PRIMARY2} !important;
9034
+ outline-offset: 6px;
9035
+ border-radius: 8px;
9036
+ }
9037
+ html[data-ohw-link-popover-open] footer [data-ohw-footer-col]:hover {
9038
+ outline: none !important;
9039
+ }
9040
+ html[data-ohw-footer-press-drag] footer,
9041
+ html[data-ohw-footer-press-drag] footer * {
9042
+ user-select: none !important;
9043
+ -webkit-user-select: none !important;
9044
+ }
9045
+ html[data-ohw-item-dragging] footer,
9046
+ html[data-ohw-item-dragging] footer * {
9047
+ user-select: none !important;
9048
+ -webkit-user-select: none !important;
9049
+ pointer-events: none !important;
9050
+ }
9051
+ html[data-ohw-item-dragging] {
9052
+ cursor: grabbing !important;
9053
+ }
9054
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not(nav [data-ohw-href-key] *):not(footer [data-ohw-href-key] *) { cursor: text !important; }
9055
+ nav [data-ohw-href-key],
9056
+ nav [data-ohw-href-key] [data-ohw-editable]:not([contenteditable]),
9057
+ footer [data-ohw-href-key],
9058
+ footer [data-ohw-href-key] [data-ohw-editable]:not([contenteditable]) {
9059
+ cursor: grab !important;
9060
+ }
8090
9061
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8091
9062
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
8092
9063
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -8130,12 +9101,39 @@ function OhhwellsBridge() {
8130
9101
  }
8131
9102
  refreshStateRules();
8132
9103
  const handleClick = (e) => {
9104
+ if (suppressNextClickRef.current) {
9105
+ suppressNextClickRef.current = false;
9106
+ e.preventDefault();
9107
+ e.stopPropagation();
9108
+ return;
9109
+ }
8133
9110
  const target = e.target;
8134
9111
  if (target.closest("[data-ohw-toolbar]")) return;
8135
9112
  if (target.closest("[data-ohw-state-toggle]")) return;
8136
9113
  if (target.closest("[data-ohw-max-badge]")) return;
8137
9114
  if (isInsideLinkEditor(target)) return;
8138
9115
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9116
+ if (target.closest("[data-ohw-item-drag-surface]")) {
9117
+ e.preventDefault();
9118
+ e.stopPropagation();
9119
+ const selected = selectedElRef.current;
9120
+ if (selected && isNavigationItem(selected)) {
9121
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9122
+ if (editable2) {
9123
+ activateRef.current(editable2, {
9124
+ caretX: e.clientX,
9125
+ caretY: e.clientY
9126
+ });
9127
+ }
9128
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
9129
+ const link = listFooterLinksInColumn(selected).find((el) => {
9130
+ const r2 = el.getBoundingClientRect();
9131
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
9132
+ });
9133
+ if (link) selectRef.current(link);
9134
+ }
9135
+ return;
9136
+ }
8139
9137
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8140
9138
  if (navFromChrome) {
8141
9139
  e.preventDefault();
@@ -8165,7 +9163,15 @@ function OhhwellsBridge() {
8165
9163
  if (isMediaEditable(editable)) {
8166
9164
  e.preventDefault();
8167
9165
  e.stopPropagation();
8168
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
9166
+ const key = editable.dataset.ohwKey ?? "";
9167
+ const r2 = editable.getBoundingClientRect();
9168
+ if (key && r2.width > 1 && r2.height > 1) {
9169
+ pendingUploadRectRef.current = {
9170
+ key,
9171
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height }
9172
+ };
9173
+ }
9174
+ postToParentRef.current({ type: "ow:image-pick", key, elementType: editable.dataset.ohwEditable ?? "image" });
8169
9175
  return;
8170
9176
  }
8171
9177
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -8190,10 +9196,26 @@ function OhhwellsBridge() {
8190
9196
  if (hrefAnchor) {
8191
9197
  e.preventDefault();
8192
9198
  e.stopPropagation();
8193
- if (selectedElRef.current === hrefAnchor) return;
9199
+ if (selectedElRef.current === hrefAnchor) {
9200
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
9201
+ if (textEditable) {
9202
+ activateRef.current(textEditable, {
9203
+ caretX: e.clientX,
9204
+ caretY: e.clientY
9205
+ });
9206
+ }
9207
+ return;
9208
+ }
8194
9209
  selectRef.current(hrefAnchor);
8195
9210
  return;
8196
9211
  }
9212
+ const footerColClick = target.closest("[data-ohw-footer-col]");
9213
+ if (footerColClick) {
9214
+ e.preventDefault();
9215
+ e.stopPropagation();
9216
+ selectFrameRef.current(footerColClick);
9217
+ return;
9218
+ }
8197
9219
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8198
9220
  if (navContainerToSelect) {
8199
9221
  e.preventDefault();
@@ -8260,6 +9282,13 @@ function OhhwellsBridge() {
8260
9282
  const handleMouseOver = (e) => {
8261
9283
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8262
9284
  const target = e.target;
9285
+ if (linkPopoverOpenRef.current) {
9286
+ hoveredItemElRef.current = null;
9287
+ setHoveredItemRect(null);
9288
+ hoveredNavContainerRef.current = null;
9289
+ setHoveredNavContainerRect(null);
9290
+ return;
9291
+ }
8263
9292
  if (toolbarVariantRef.current !== "select-frame") {
8264
9293
  const navContainer = target.closest("[data-ohw-nav-container]");
8265
9294
  if (navContainer && !getNavigationItemAnchor(target)) {
@@ -8274,6 +9303,15 @@ function OhhwellsBridge() {
8274
9303
  setHoveredNavContainerRect(null);
8275
9304
  }
8276
9305
  }
9306
+ const footerCol = target.closest("[data-ohw-footer-col]");
9307
+ if (footerCol) {
9308
+ hoveredNavContainerRef.current = null;
9309
+ setHoveredNavContainerRect(null);
9310
+ if (selectedElRef.current === footerCol) return;
9311
+ hoveredItemElRef.current = footerCol;
9312
+ setHoveredItemRect(footerCol.getBoundingClientRect());
9313
+ return;
9314
+ }
8277
9315
  const navAnchor = getNavigationItemAnchor(target);
8278
9316
  if (navAnchor) {
8279
9317
  hoveredNavContainerRef.current = null;
@@ -8310,6 +9348,15 @@ function OhhwellsBridge() {
8310
9348
  hoveredNavContainerRef.current = null;
8311
9349
  setHoveredNavContainerRect(null);
8312
9350
  }
9351
+ const footerCol = target.closest("[data-ohw-footer-col]");
9352
+ if (footerCol && hoveredItemElRef.current === footerCol) {
9353
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9354
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
9355
+ if (related2?.closest("[data-ohw-footer-col]") === footerCol) return;
9356
+ hoveredItemElRef.current = null;
9357
+ setHoveredItemRect(null);
9358
+ return;
9359
+ }
8313
9360
  const navAnchor = getNavigationItemAnchor(target);
8314
9361
  if (navAnchor) {
8315
9362
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8421,12 +9468,12 @@ function OhhwellsBridge() {
8421
9468
  const stickyPad = 48;
8422
9469
  const withinPad = x >= pr.left - stickyPad && x <= pr.left + pr.width + stickyPad && y >= pr.top - stickyPad && y <= pr.top + pr.height + stickyPad;
8423
9470
  if (withinPad) {
8424
- if (smallest !== pinned && candidatePool.includes(smallest)) {
9471
+ const yieldToSmaller = smallest !== pinned && candidatePool.includes(smallest) && (() => {
8425
9472
  const sr = getVisibleRect(smallest);
8426
- const overOther = x >= sr.left && x <= sr.left + sr.width && y >= sr.top && y <= sr.top + sr.height;
8427
- if (overOther) return smallest;
8428
- }
8429
- return pinned;
9473
+ const pr2 = getVisibleRect(pinned);
9474
+ return sr.width * sr.height < pr2.width * pr2.height;
9475
+ })();
9476
+ if (!yieldToSmaller) return pinned;
8430
9477
  }
8431
9478
  }
8432
9479
  return smallest;
@@ -8438,6 +9485,7 @@ function OhhwellsBridge() {
8438
9485
  resumeAnimTracks();
8439
9486
  postToParentRef.current({ type: "ow:image-unhover" });
8440
9487
  }
9488
+ clearImageHover();
8441
9489
  };
8442
9490
  const probeNavigationHoverAt = (x, y) => {
8443
9491
  if (toolbarVariantRef.current === "select-frame") {
@@ -8540,7 +9588,7 @@ function OhhwellsBridge() {
8540
9588
  return;
8541
9589
  }
8542
9590
  const topEl = document.elementFromPoint(x2, y2);
8543
- if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9591
+ if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-media-chrome]')) {
8544
9592
  if (hoveredImageRef.current) {
8545
9593
  hoveredImageRef.current = null;
8546
9594
  resumeAnimTracks();
@@ -8747,6 +9795,14 @@ function OhhwellsBridge() {
8747
9795
  probeHoverCardsAt(clientX, clientY);
8748
9796
  };
8749
9797
  const handleDragOver = (e) => {
9798
+ const footerSession = footerDragRef.current;
9799
+ if (footerSession) {
9800
+ e.preventDefault();
9801
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
9802
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
9803
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
9804
+ return;
9805
+ }
8750
9806
  e.preventDefault();
8751
9807
  const el = findImageAtPoint(e.clientX, e.clientY, false);
8752
9808
  if (!el) {
@@ -8815,6 +9871,7 @@ function OhhwellsBridge() {
8815
9871
  if (!entry || entry.fadingOut) return prev;
8816
9872
  return { ...prev, [key]: { ...entry, fadingOut: true } };
8817
9873
  });
9874
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
8818
9875
  };
8819
9876
  let found = false;
8820
9877
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -8929,6 +9986,7 @@ function OhhwellsBridge() {
8929
9986
  continue;
8930
9987
  }
8931
9988
  if (applyVideoSettingNode(key, val)) continue;
9989
+ if (applyCarouselNode(key, val)) continue;
8932
9990
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8933
9991
  if (el.dataset.ohwEditable === "image") {
8934
9992
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8953,6 +10011,7 @@ function OhhwellsBridge() {
8953
10011
  }
8954
10012
  editContentRef.current = { ...editContentRef.current, ...content };
8955
10013
  reconcileNavbarItemsFromContent(editContentRef.current);
10014
+ reconcileFooterOrderFromContent(editContentRef.current);
8956
10015
  enforceLinkHrefs();
8957
10016
  postToParentRef.current({ type: "ow:hydrate-done" });
8958
10017
  };
@@ -9066,6 +10125,17 @@ function OhhwellsBridge() {
9066
10125
  if (siblingHintElRef.current) {
9067
10126
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9068
10127
  }
10128
+ const selected = selectedElRef.current;
10129
+ if (selected && !footerDragRef.current && (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)))) {
10130
+ setSiblingHintRects(
10131
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
10132
+ );
10133
+ }
10134
+ if (footerDragRef.current) {
10135
+ const session = footerDragRef.current;
10136
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
10137
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
10138
+ }
9069
10139
  if (hoveredImageRef.current) {
9070
10140
  const el = hoveredImageRef.current;
9071
10141
  const r2 = el.getBoundingClientRect();
@@ -9076,7 +10146,7 @@ function OhhwellsBridge() {
9076
10146
  };
9077
10147
  const handleSave = (e) => {
9078
10148
  if (e.data?.type !== "ow:save") return;
9079
- const nodes = collectEditableNodes();
10149
+ const nodes = collectEditableNodes(editContentRef.current);
9080
10150
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9081
10151
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9082
10152
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9161,28 +10231,49 @@ function OhhwellsBridge() {
9161
10231
  const handleDocMouseLeave = () => {
9162
10232
  hoveredImageRef.current = null;
9163
10233
  setMediaHover(null);
10234
+ setCarouselHover(null);
9164
10235
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
9165
10236
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
9166
10237
  activeStateElRef.current = null;
9167
10238
  setToggleState(null);
9168
10239
  };
10240
+ const resolveUploadRects = (key) => {
10241
+ const matches = Array.from(document.querySelectorAll(`[data-ohw-key="${key}"]`));
10242
+ const rects = matches.map((el) => {
10243
+ const r2 = el.getBoundingClientRect();
10244
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
10245
+ }).filter((r2) => r2.width > 1 && r2.height > 1);
10246
+ if (rects.length > 0) return rects;
10247
+ const pending = pendingUploadRectRef.current;
10248
+ if (pending?.key === key && pending.rect.width > 1 && pending.rect.height > 1) {
10249
+ return [pending.rect];
10250
+ }
10251
+ const hovered = hoveredImageRef.current;
10252
+ if (hovered?.dataset.ohwKey === key) {
10253
+ const r2 = hovered.getBoundingClientRect();
10254
+ if (r2.width > 1 && r2.height > 1) {
10255
+ return [{ top: r2.top, left: r2.left, width: r2.width, height: r2.height }];
10256
+ }
10257
+ }
10258
+ return [];
10259
+ };
9169
10260
  const handleImageUploading = (e) => {
9170
- if (e.data?.type !== "ow:image-uploading") return;
9171
- const { key, uploading } = e.data;
10261
+ const type = e.data?.type;
10262
+ if (type !== "ow:image-uploading" && type !== "ow:anim-lock") return;
10263
+ const key = e.data.key;
10264
+ if (!key) return;
10265
+ const uploading = type === "ow:anim-lock" ? true : !!e.data.uploading;
9172
10266
  setUploadingRects((prev) => {
9173
10267
  if (!uploading) {
10268
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
9174
10269
  if (!(key in prev)) return prev;
9175
10270
  const next = { ...prev };
9176
10271
  delete next[key];
9177
10272
  return next;
9178
10273
  }
9179
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
9180
- if (!el) return prev;
9181
- const r2 = getVisibleRect(el);
9182
- return {
9183
- ...prev,
9184
- [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
9185
- };
10274
+ const rects = resolveUploadRects(key);
10275
+ if (rects.length === 0) return prev;
10276
+ return { ...prev, [key]: { rects } };
9186
10277
  });
9187
10278
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9188
10279
  const track = el.closest("[data-ohw-hover-pause]");
@@ -9200,6 +10291,48 @@ function OhhwellsBridge() {
9200
10291
  if (e.data?.type !== "ow:canvas-height" || typeof e.data.height !== "number") return;
9201
10292
  document.documentElement.style.setProperty("--ohw-canvas-h", `${e.data.height}px`);
9202
10293
  };
10294
+ const handleCarouselChange = (e) => {
10295
+ if (e.data?.type !== "ow:carousel-change") return;
10296
+ const { key, images } = e.data;
10297
+ if (!key || !Array.isArray(images)) return;
10298
+ const text = JSON.stringify(images);
10299
+ applyCarouselValue(key, images);
10300
+ editContentRef.current = { ...editContentRef.current, [key]: text };
10301
+ postToParentRef.current({ type: "ow:change", nodes: [{ key, text }] });
10302
+ setCarouselHover(null);
10303
+ };
10304
+ const findCarouselAtPoint = (clientX, clientY) => {
10305
+ const containers = Array.from(document.querySelectorAll(`[${CAROUSEL_ATTR}]`));
10306
+ for (let i = containers.length - 1; i >= 0; i--) {
10307
+ const r2 = containers[i].getBoundingClientRect();
10308
+ if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) {
10309
+ return containers[i];
10310
+ }
10311
+ }
10312
+ return null;
10313
+ };
10314
+ const isPointOverEditable = (scope, clientX, clientY) => {
10315
+ const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
10316
+ for (const el of editables) {
10317
+ const r2 = el.getBoundingClientRect();
10318
+ if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
10319
+ }
10320
+ return false;
10321
+ };
10322
+ const handleCarouselHover = (e) => {
10323
+ const container = findCarouselAtPoint(e.clientX, e.clientY);
10324
+ const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
10325
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
10326
+ setCarouselHover((prev) => prev ? null : prev);
10327
+ return;
10328
+ }
10329
+ const key = container.getAttribute("data-ohw-key") ?? "";
10330
+ if (!key) return;
10331
+ const r2 = getVisibleRect(container);
10332
+ setCarouselHover(
10333
+ (prev) => prev && prev.key === key && prev.rect.top === r2.top && prev.rect.left === r2.left && prev.rect.width === r2.width && prev.rect.height === r2.height ? prev : { key, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
10334
+ );
10335
+ };
9203
10336
  const applyToolbarPos = (rect) => {
9204
10337
  const ps = parentScrollRef.current;
9205
10338
  const approxW = 330;
@@ -9308,6 +10441,7 @@ function OhhwellsBridge() {
9308
10441
  window.addEventListener("message", handleRemoveSchedulingSection);
9309
10442
  window.addEventListener("message", handleImageUrl);
9310
10443
  window.addEventListener("message", handleImageUploading);
10444
+ window.addEventListener("message", handleCarouselChange);
9311
10445
  window.addEventListener("message", handleCanvasHeight);
9312
10446
  window.addEventListener("message", handleParentScroll);
9313
10447
  window.addEventListener("message", handlePointerSync);
@@ -9325,6 +10459,7 @@ function OhhwellsBridge() {
9325
10459
  document.addEventListener("mouseover", handleMouseOver, true);
9326
10460
  document.addEventListener("mouseout", handleMouseOut, true);
9327
10461
  document.addEventListener("mousemove", handleMouseMove, true);
10462
+ document.addEventListener("mousemove", handleCarouselHover, true);
9328
10463
  document.addEventListener("mouseleave", handleDocMouseLeave);
9329
10464
  document.addEventListener("dragover", handleDragOver, true);
9330
10465
  document.addEventListener("dragleave", handleDragLeave, true);
@@ -9340,6 +10475,7 @@ function OhhwellsBridge() {
9340
10475
  document.removeEventListener("mouseover", handleMouseOver, true);
9341
10476
  document.removeEventListener("mouseout", handleMouseOut, true);
9342
10477
  document.removeEventListener("mousemove", handleMouseMove, true);
10478
+ document.removeEventListener("mousemove", handleCarouselHover, true);
9343
10479
  document.removeEventListener("mouseleave", handleDocMouseLeave);
9344
10480
  document.removeEventListener("dragover", handleDragOver, true);
9345
10481
  document.removeEventListener("dragleave", handleDragLeave, true);
@@ -9355,6 +10491,7 @@ function OhhwellsBridge() {
9355
10491
  window.removeEventListener("message", handleRemoveSchedulingSection);
9356
10492
  window.removeEventListener("message", handleImageUrl);
9357
10493
  window.removeEventListener("message", handleImageUploading);
10494
+ window.removeEventListener("message", handleCarouselChange);
9358
10495
  window.removeEventListener("message", handleCanvasHeight);
9359
10496
  window.removeEventListener("message", handleParentScroll);
9360
10497
  window.removeEventListener("resize", handleViewportResize);
@@ -9369,7 +10506,158 @@ function OhhwellsBridge() {
9369
10506
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9370
10507
  };
9371
10508
  }, [isEditMode, refreshStateRules]);
9372
- useEffect7(() => {
10509
+ useEffect8(() => {
10510
+ if (!isEditMode) return;
10511
+ const THRESHOLD = 10;
10512
+ const resolveWasSelected = (el) => {
10513
+ if (selectedElRef.current === el) return true;
10514
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
10515
+ return activeAnchor === el;
10516
+ };
10517
+ const onPointerDown = (e) => {
10518
+ if (e.button !== 0) return;
10519
+ if (suppressNextClickRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
10520
+ suppressNextClickRef.current = false;
10521
+ }
10522
+ if (footerDragRef.current) return;
10523
+ const target = e.target;
10524
+ if (!target) return;
10525
+ 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]')) {
10526
+ return;
10527
+ }
10528
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
10529
+ const anchor = getNavigationItemAnchor(target);
10530
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
10531
+ if (anchor && isFooterHrefKey(hrefKey)) {
10532
+ armFooterPressDrag();
10533
+ footerPointerDragRef.current = {
10534
+ el: anchor,
10535
+ kind: "link",
10536
+ startX: e.clientX,
10537
+ startY: e.clientY,
10538
+ pointerId: e.pointerId,
10539
+ wasSelected: resolveWasSelected(anchor),
10540
+ started: false
10541
+ };
10542
+ return;
10543
+ }
10544
+ const col = target.closest("[data-ohw-footer-col]");
10545
+ if (col && !getNavigationItemAnchor(target)) {
10546
+ armFooterPressDrag();
10547
+ footerPointerDragRef.current = {
10548
+ el: col,
10549
+ kind: "column",
10550
+ startX: e.clientX,
10551
+ startY: e.clientY,
10552
+ pointerId: e.pointerId,
10553
+ wasSelected: resolveWasSelected(col),
10554
+ started: false
10555
+ };
10556
+ }
10557
+ };
10558
+ const onPointerMove = (e) => {
10559
+ const pending = footerPointerDragRef.current;
10560
+ if (!pending) return;
10561
+ if (pending.started) {
10562
+ e.preventDefault();
10563
+ clearTextSelection();
10564
+ const session = footerDragRef.current;
10565
+ if (!session) return;
10566
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10567
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
10568
+ return;
10569
+ }
10570
+ const dx = e.clientX - pending.startX;
10571
+ const dy = e.clientY - pending.startY;
10572
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
10573
+ e.preventDefault();
10574
+ pending.started = true;
10575
+ clearTextSelection();
10576
+ try {
10577
+ document.body.setPointerCapture(pending.pointerId);
10578
+ } catch {
10579
+ }
10580
+ if (linkPopoverOpenRef.current) {
10581
+ setLinkPopoverRef.current(null);
10582
+ }
10583
+ if (activeElRef.current) {
10584
+ deactivateRef.current();
10585
+ }
10586
+ if (pending.kind === "link") {
10587
+ const key = pending.el.getAttribute("data-ohw-href-key");
10588
+ if (!key) return;
10589
+ const column = findFooterColumnForLink(pending.el);
10590
+ const columns2 = listFooterColumns();
10591
+ beginFooterDragRef.current({
10592
+ kind: "link",
10593
+ hrefKey: key,
10594
+ columnEl: column,
10595
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
10596
+ wasSelected: pending.wasSelected,
10597
+ draggedEl: pending.el,
10598
+ lastClientX: e.clientX,
10599
+ lastClientY: e.clientY,
10600
+ activeSlot: null
10601
+ });
10602
+ return;
10603
+ }
10604
+ const columns = listFooterColumns();
10605
+ const idx = columns.indexOf(pending.el);
10606
+ if (idx < 0) return;
10607
+ beginFooterDragRef.current({
10608
+ kind: "column",
10609
+ hrefKey: null,
10610
+ columnEl: pending.el,
10611
+ sourceColumnIndex: idx,
10612
+ wasSelected: pending.wasSelected,
10613
+ draggedEl: pending.el,
10614
+ lastClientX: e.clientX,
10615
+ lastClientY: e.clientY,
10616
+ activeSlot: null
10617
+ });
10618
+ };
10619
+ const endPointerDrag = (e) => {
10620
+ const pending = footerPointerDragRef.current;
10621
+ footerPointerDragRef.current = null;
10622
+ try {
10623
+ if (document.body.hasPointerCapture(e.pointerId)) {
10624
+ document.body.releasePointerCapture(e.pointerId);
10625
+ }
10626
+ } catch {
10627
+ }
10628
+ if (!pending?.started) {
10629
+ unlockFooterDragInteraction();
10630
+ return;
10631
+ }
10632
+ suppressNextClickRef.current = true;
10633
+ commitFooterDragRef.current(e.clientX, e.clientY);
10634
+ };
10635
+ const blockSelectStart = (e) => {
10636
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
10637
+ e.preventDefault();
10638
+ }
10639
+ };
10640
+ const onDragEnd = (_e) => {
10641
+ if (!footerDragRef.current) return;
10642
+ commitFooterDragRef.current();
10643
+ };
10644
+ document.addEventListener("pointerdown", onPointerDown, true);
10645
+ document.addEventListener("pointermove", onPointerMove, true);
10646
+ document.addEventListener("pointerup", endPointerDrag, true);
10647
+ document.addEventListener("pointercancel", endPointerDrag, true);
10648
+ document.addEventListener("selectstart", blockSelectStart, true);
10649
+ document.addEventListener("dragend", onDragEnd, true);
10650
+ return () => {
10651
+ document.removeEventListener("pointerdown", onPointerDown, true);
10652
+ document.removeEventListener("pointermove", onPointerMove, true);
10653
+ document.removeEventListener("pointerup", endPointerDrag, true);
10654
+ document.removeEventListener("pointercancel", endPointerDrag, true);
10655
+ document.removeEventListener("selectstart", blockSelectStart, true);
10656
+ document.removeEventListener("dragend", onDragEnd, true);
10657
+ unlockFooterDragInteraction();
10658
+ };
10659
+ }, [isEditMode]);
10660
+ useEffect8(() => {
9373
10661
  const handler = (e) => {
9374
10662
  if (e.data?.type !== "ow:request-schedule-config") return;
9375
10663
  const insertAfterVal = e.data.insertAfter;
@@ -9385,7 +10673,7 @@ function OhhwellsBridge() {
9385
10673
  window.addEventListener("message", handler);
9386
10674
  return () => window.removeEventListener("message", handler);
9387
10675
  }, [processConfigRequest]);
9388
- useEffect7(() => {
10676
+ useEffect8(() => {
9389
10677
  if (!isEditMode) return;
9390
10678
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9391
10679
  el.removeAttribute("data-ohw-active-state");
@@ -9406,7 +10694,13 @@ function OhhwellsBridge() {
9406
10694
  const next = { ...prev, [pathKey]: sections };
9407
10695
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9408
10696
  });
9409
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
10697
+ postToParent2({
10698
+ type: "ow:ready",
10699
+ version: "1",
10700
+ path: pathname,
10701
+ nodes: collectEditableNodes(editContentRef.current),
10702
+ sections
10703
+ });
9410
10704
  postToParent2({ type: "ow:request-site-pages" });
9411
10705
  }, 150);
9412
10706
  return () => {
@@ -9414,19 +10708,19 @@ function OhhwellsBridge() {
9414
10708
  clearTimeout(timer);
9415
10709
  };
9416
10710
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9417
- useEffect7(() => {
10711
+ useEffect8(() => {
9418
10712
  scrollToHashSectionWhenReady();
9419
10713
  const onHashChange = () => scrollToHashSectionWhenReady();
9420
10714
  window.addEventListener("hashchange", onHashChange);
9421
10715
  return () => window.removeEventListener("hashchange", onHashChange);
9422
10716
  }, [pathname]);
9423
- const handleCommand = useCallback3((cmd) => {
10717
+ const handleCommand = useCallback4((cmd) => {
9424
10718
  document.execCommand(cmd, false);
9425
10719
  activeElRef.current?.focus();
9426
10720
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
9427
10721
  refreshActiveCommandsRef.current();
9428
10722
  }, []);
9429
- const handleStateChange = useCallback3((state) => {
10723
+ const handleStateChange = useCallback4((state) => {
9430
10724
  if (!activeStateElRef.current) return;
9431
10725
  const el = activeStateElRef.current;
9432
10726
  if (state === "Default") {
@@ -9439,11 +10733,11 @@ function OhhwellsBridge() {
9439
10733
  }
9440
10734
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9441
10735
  }, [deactivate]);
9442
- const closeLinkPopover = useCallback3(() => {
10736
+ const closeLinkPopover = useCallback4(() => {
9443
10737
  addNavAfterAnchorRef.current = null;
9444
10738
  setLinkPopover(null);
9445
10739
  }, []);
9446
- const openLinkPopoverForActive = useCallback3(() => {
10740
+ const openLinkPopoverForActive = useCallback4(() => {
9447
10741
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9448
10742
  if (!hrefCtx) return;
9449
10743
  bumpLinkPopoverGrace();
@@ -9454,7 +10748,7 @@ function OhhwellsBridge() {
9454
10748
  });
9455
10749
  deactivate();
9456
10750
  }, [deactivate]);
9457
- const openLinkPopoverForSelected = useCallback3(() => {
10751
+ const openLinkPopoverForSelected = useCallback4(() => {
9458
10752
  const anchor = selectedElRef.current;
9459
10753
  if (!anchor) return;
9460
10754
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9467,7 +10761,7 @@ function OhhwellsBridge() {
9467
10761
  });
9468
10762
  deselect();
9469
10763
  }, [deselect]);
9470
- const handleLinkPopoverSubmit = useCallback3(
10764
+ const handleLinkPopoverSubmit = useCallback4(
9471
10765
  (target) => {
9472
10766
  const session = linkPopoverSessionRef.current;
9473
10767
  if (!session) return;
@@ -9523,16 +10817,22 @@ function OhhwellsBridge() {
9523
10817
  },
9524
10818
  [postToParent2, sitePages, sectionsByPath]
9525
10819
  );
9526
- const showEditLink = toolbarShowEditLink;
9527
- const currentSections = sectionsByPath[pathname] ?? [];
9528
- linkPopoverOpenRef.current = linkPopover !== null;
9529
- const handleMediaReplace = useCallback3(
10820
+ const handleMediaReplace = useCallback4(
9530
10821
  (key) => {
10822
+ if (mediaHover?.key === key) {
10823
+ pendingUploadRectRef.current = { key, rect: mediaHover.rect };
10824
+ }
9531
10825
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9532
10826
  },
9533
- [postToParent2, mediaHover?.elementType]
10827
+ [postToParent2, mediaHover]
9534
10828
  );
9535
- const handleMediaFadeOutComplete = useCallback3((key) => {
10829
+ const handleEditCarousel = useCallback4(
10830
+ (key) => {
10831
+ postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
10832
+ },
10833
+ [postToParent2]
10834
+ );
10835
+ const handleMediaFadeOutComplete = useCallback4((key) => {
9536
10836
  setUploadingRects((prev) => {
9537
10837
  if (!(key in prev)) return prev;
9538
10838
  const next = { ...prev };
@@ -9540,7 +10840,7 @@ function OhhwellsBridge() {
9540
10840
  return next;
9541
10841
  });
9542
10842
  }, []);
9543
- const handleVideoSettingsChange = useCallback3(
10843
+ const handleVideoSettingsChange = useCallback4(
9544
10844
  (key, settings) => {
9545
10845
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9546
10846
  const video = getVideoEl(el);
@@ -9562,21 +10862,26 @@ function OhhwellsBridge() {
9562
10862
  },
9563
10863
  [postToParent2]
9564
10864
  );
10865
+ const showEditLink = toolbarShowEditLink;
10866
+ const currentSections = sectionsByPath[pathname] ?? [];
10867
+ linkPopoverOpenRef.current = linkPopover !== null;
9565
10868
  return bridgeRoot ? createPortal2(
9566
- /* @__PURE__ */ jsxs13(Fragment5, { children: [
9567
- /* @__PURE__ */ jsx23("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9568
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx23(
9569
- MediaOverlay,
9570
- {
9571
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
9572
- isUploading: true,
9573
- fadingOut,
9574
- onFadeOutComplete: handleMediaFadeOutComplete,
9575
- onReplace: handleMediaReplace
9576
- },
9577
- `uploading-${key}`
9578
- )),
9579
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx23(
10869
+ /* @__PURE__ */ jsxs14(Fragment5, { children: [
10870
+ /* @__PURE__ */ jsx25("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
10871
+ Object.entries(uploadingRects).flatMap(
10872
+ ([key, { rects, fadingOut }]) => rects.map((rect, i) => /* @__PURE__ */ jsx25(
10873
+ MediaOverlay,
10874
+ {
10875
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
10876
+ isUploading: true,
10877
+ fadingOut,
10878
+ onFadeOutComplete: handleMediaFadeOutComplete,
10879
+ onReplace: handleMediaReplace
10880
+ },
10881
+ `uploading-${key}-${i}`
10882
+ ))
10883
+ ),
10884
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx25(
9580
10885
  MediaOverlay,
9581
10886
  {
9582
10887
  hover: mediaHover,
@@ -9585,62 +10890,33 @@ function OhhwellsBridge() {
9585
10890
  onVideoSettingsChange: handleVideoSettingsChange
9586
10891
  }
9587
10892
  ),
9588
- siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9589
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9590
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx23(
9591
- NavbarContainerChrome,
9592
- {
9593
- rect: toolbarRect,
9594
- onAdd: handleAddTopLevelNavItem
9595
- }
9596
- ),
9597
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9598
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx23(
9599
- ItemInteractionLayer,
10893
+ carouselHover && /* @__PURE__ */ jsx25(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
10894
+ siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
10895
+ siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
10896
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
10897
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx25(
10898
+ "div",
9600
10899
  {
9601
- rect: toolbarRect,
9602
- elRef: glowElRef,
9603
- state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9604
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
9605
- dragDisabled: reorderDragDisabled,
9606
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9607
- onDragHandleDragStart: handleItemDragStart,
9608
- onDragHandleDragEnd: handleItemDragEnd,
9609
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx23(
9610
- ItemActionToolbar,
9611
- {
9612
- onEditLink: openLinkPopoverForSelected,
9613
- addItemDisabled: true,
9614
- editLinkDisabled: false,
9615
- moreDisabled: true
9616
- }
9617
- ) : void 0
9618
- }
9619
- ),
9620
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs13(Fragment5, { children: [
9621
- /* @__PURE__ */ jsx23(
9622
- EditGlowChrome,
9623
- {
9624
- rect: toolbarRect,
9625
- elRef: glowElRef,
9626
- reorderHrefKey,
9627
- dragDisabled: reorderDragDisabled
9628
- }
9629
- ),
9630
- /* @__PURE__ */ jsx23(
9631
- FloatingToolbar,
9632
- {
9633
- rect: toolbarRect,
9634
- parentScroll: parentScrollRef.current,
9635
- elRef: toolbarElRef,
9636
- onCommand: handleCommand,
9637
- activeCommands,
9638
- showEditLink,
9639
- onEditLink: openLinkPopoverForActive
9640
- }
9641
- )
10900
+ className: "pointer-events-none fixed z-2147483646",
10901
+ style: {
10902
+ left: slot.left,
10903
+ top: slot.top,
10904
+ width: slot.width,
10905
+ height: slot.height
10906
+ },
10907
+ children: /* @__PURE__ */ jsx25(DropIndicator, { direction: slot.direction, state: activeFooterDropIndex === i ? "dragActive" : "dragIdle", className: slot.direction === "horizontal" ? "!h-full !w-full" : "!h-full !w-full" })
10908
+ },
10909
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
10910
+ )),
10911
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
10912
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ jsx25(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
10913
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
10914
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: isItemDragging && draggedItemRect && footerDragRef.current?.wasSelected ? draggedItemRect : toolbarRect, elRef: glowElRef, state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current), showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection, dragDisabled: reorderDragDisabled, dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item", onDragHandleDragStart: handleItemDragStart, onDragHandleDragEnd: handleItemDragEnd, onItemPointerDown: handleItemChromePointerDown, onItemClick: handleItemChromeClick, toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx25(ItemActionToolbar, { onEditLink: openLinkPopoverForSelected, addItemDisabled: true, editLinkDisabled: false, moreDisabled: true }) : void 0 }),
10915
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs14(Fragment5, { children: [
10916
+ /* @__PURE__ */ jsx25(EditGlowChrome, { rect: toolbarRect, elRef: glowElRef, reorderHrefKey, dragDisabled: reorderDragDisabled }),
10917
+ /* @__PURE__ */ jsx25(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands, showEditLink, onEditLink: openLinkPopoverForActive })
9642
10918
  ] }),
9643
- maxBadge && /* @__PURE__ */ jsxs13(
10919
+ maxBadge && /* @__PURE__ */ jsxs14(
9644
10920
  "div",
9645
10921
  {
9646
10922
  "data-ohw-max-badge": "",
@@ -9666,57 +10942,29 @@ function OhhwellsBridge() {
9666
10942
  ]
9667
10943
  }
9668
10944
  ),
9669
- toggleState && !linkPopover && /* @__PURE__ */ jsx23(
9670
- StateToggle,
9671
- {
9672
- rect: toggleState.rect,
9673
- activeState: toggleState.activeState,
9674
- states: toggleState.states,
9675
- onStateChange: handleStateChange
9676
- }
9677
- ),
9678
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs13(
9679
- "div",
9680
- {
9681
- "data-ohw-section-insert-line": "",
9682
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9683
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
9684
- children: [
9685
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9686
- /* @__PURE__ */ jsx23(
9687
- Badge,
9688
- {
9689
- 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",
9690
- onClick: () => {
9691
- window.parent.postMessage(
9692
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
9693
- "*"
9694
- );
10945
+ toggleState && !linkPopover && /* @__PURE__ */ jsx25(StateToggle, { rect: toggleState.rect, activeState: toggleState.activeState, states: toggleState.states, onStateChange: handleStateChange }),
10946
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs14("div", { "data-ohw-section-insert-line": "", className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none", style: { top: sectionGap.y, transform: "translateY(-50%)" }, children: [
10947
+ /* @__PURE__ */ jsx25("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10948
+ /* @__PURE__ */ jsx25(
10949
+ Badge,
10950
+ {
10951
+ 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",
10952
+ onClick: () => {
10953
+ window.parent.postMessage(
10954
+ {
10955
+ type: "ow:add-section",
10956
+ insertAfter: sectionGap.insertAfter,
10957
+ insertBefore: sectionGap.insertBefore
9695
10958
  },
9696
- children: "Add Section"
9697
- }
9698
- ),
9699
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9700
- ]
9701
- }
9702
- ),
9703
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx23(
9704
- LinkPopover,
9705
- {
9706
- panelRef: linkPopoverPanelRef,
9707
- portalContainer: dialogPortalContainer,
9708
- open: true,
9709
- mode: linkPopover.mode ?? "edit",
9710
- pages: sitePages,
9711
- sections: currentSections,
9712
- sectionsByPath,
9713
- initialTarget: linkPopover.target,
9714
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
9715
- onClose: closeLinkPopover,
9716
- onSubmit: handleLinkPopoverSubmit
9717
- },
9718
- linkPopover.key
9719
- ) : null
10959
+ "*"
10960
+ );
10961
+ },
10962
+ children: "Add Section"
10963
+ }
10964
+ ),
10965
+ /* @__PURE__ */ jsx25("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10966
+ ] }),
10967
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx25(LinkPopover, { panelRef: linkPopoverPanelRef, portalContainer: dialogPortalContainer, open: true, mode: linkPopover.mode ?? "edit", pages: sitePages, sections: currentSections, sectionsByPath, initialTarget: linkPopover.target, existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [], onClose: closeLinkPopover, onSubmit: handleLinkPopoverSubmit }, linkPopover.key) : null
9720
10968
  ] }),
9721
10969
  bridgeRoot
9722
10970
  ) : null;
@@ -9726,6 +10974,7 @@ export {
9726
10974
  CustomToolbarButton,
9727
10975
  CustomToolbarDivider,
9728
10976
  DragHandle,
10977
+ DropIndicator,
9729
10978
  ItemActionToolbar,
9730
10979
  ItemInteractionLayer,
9731
10980
  LinkEditorPanel,
@@ -9740,12 +10989,14 @@ export {
9740
10989
  TooltipProvider,
9741
10990
  TooltipTrigger,
9742
10991
  buildTarget,
10992
+ dropIndicatorVariants,
9743
10993
  filterAvailablePages,
9744
10994
  getEditModeInitialState,
9745
10995
  isEditSessionActive,
9746
10996
  isValidUrl,
9747
10997
  parseTarget,
9748
10998
  toggleVariants,
10999
+ useOhwCarousel,
9749
11000
  validateUrlInput
9750
11001
  };
9751
11002
  //# sourceMappingURL=index.js.map