@ohhwells/bridge 0.1.41 → 0.1.42-next.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  CustomToolbarButton: () => CustomToolbarButton,
36
36
  CustomToolbarDivider: () => CustomToolbarDivider,
37
37
  DragHandle: () => DragHandle,
38
+ DropIndicator: () => DropIndicator,
38
39
  ItemActionToolbar: () => ItemActionToolbar,
39
40
  ItemInteractionLayer: () => ItemInteractionLayer,
40
41
  LinkEditorPanel: () => LinkEditorPanel,
@@ -49,18 +50,20 @@ __export(index_exports, {
49
50
  TooltipProvider: () => TooltipProvider,
50
51
  TooltipTrigger: () => TooltipTrigger,
51
52
  buildTarget: () => buildTarget,
53
+ dropIndicatorVariants: () => dropIndicatorVariants,
52
54
  filterAvailablePages: () => filterAvailablePages,
53
55
  getEditModeInitialState: () => getEditModeInitialState,
54
56
  isEditSessionActive: () => isEditSessionActive,
55
57
  isValidUrl: () => isValidUrl,
56
58
  parseTarget: () => parseTarget,
57
59
  toggleVariants: () => toggleVariants,
60
+ useOhwCarousel: () => useOhwCarousel,
58
61
  validateUrlInput: () => validateUrlInput
59
62
  });
60
63
  module.exports = __toCommonJS(index_exports);
61
64
 
62
65
  // src/OhhwellsBridge.tsx
63
- var import_react8 = __toESM(require("react"), 1);
66
+ var import_react9 = __toESM(require("react"), 1);
64
67
  var import_client = require("react-dom/client");
65
68
  var import_react_dom2 = require("react-dom");
66
69
 
@@ -113,6 +116,7 @@ var import_react3 = require("react");
113
116
  var import_react2 = require("react");
114
117
  var import_radix_ui = require("radix-ui");
115
118
  var import_jsx_runtime = require("react/jsx-runtime");
119
+ var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
116
120
  function Spinner() {
117
121
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
118
122
  "svg",
@@ -146,12 +150,17 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
146
150
  const [error, setError] = (0, import_react2.useState)(null);
147
151
  const isBook = title === "Confirm your spot";
148
152
  const handleSubmit = async () => {
149
- if (!email.trim()) return;
153
+ const trimmed = email.trim();
154
+ if (!trimmed) return;
155
+ if (!isValidEmail(trimmed)) {
156
+ setError("Please enter a valid email address.");
157
+ return;
158
+ }
150
159
  setLoading(true);
151
160
  setError(null);
152
161
  try {
153
- await onSubmit(email.trim());
154
- setSubmittedEmail(email.trim());
162
+ await onSubmit(trimmed);
163
+ setSubmittedEmail(trimmed);
155
164
  setSuccess(true);
156
165
  } catch (e) {
157
166
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -226,7 +235,10 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
226
235
  {
227
236
  type: "email",
228
237
  value: email,
229
- onChange: (e) => setEmail(e.target.value),
238
+ onChange: (e) => {
239
+ setEmail(e.target.value);
240
+ if (error) setError(null);
241
+ },
230
242
  onKeyDown: handleKeyDown,
231
243
  placeholder: "hello@gmail.com",
232
244
  autoFocus: true,
@@ -283,12 +295,20 @@ function formatClassTime(cls) {
283
295
  const em = endTotal % 60;
284
296
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
285
297
  }
298
+ function formatBookingLabel(cls) {
299
+ const price = cls.price;
300
+ const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
301
+ return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
302
+ }
286
303
  function getBookingsOnDate(cls, date) {
287
304
  if (!cls.bookings?.length) return 0;
288
- const ds = date.toISOString().split("T")[0];
305
+ const target = new Date(date);
306
+ target.setHours(0, 0, 0, 0);
289
307
  return cls.bookings.filter((b) => {
290
308
  try {
291
- return new Date(b.classDate).toISOString().split("T")[0] === ds;
309
+ const bookingDate = new Date(b.classDate);
310
+ bookingDate.setHours(0, 0, 0, 0);
311
+ return bookingDate.getTime() === target.getTime();
292
312
  } catch {
293
313
  return false;
294
314
  }
@@ -553,7 +573,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
553
573
  }
554
574
  )
555
575
  ] }),
556
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
576
+ cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
557
577
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
558
578
  available,
559
579
  "/",
@@ -565,9 +585,10 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
565
585
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
566
586
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
567
587
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
568
- cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
588
+ cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
589
+ cls.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm font-normal text-(--color-dark,#200C02) opacity-70 block truncate", children: cls.description })
569
590
  ] }),
570
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
591
+ cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
571
592
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
572
593
  available,
573
594
  "/",
@@ -592,7 +613,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
592
613
  if (!cls.id) return;
593
614
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
594
615
  },
595
- children: isFull ? "Join Waitlist" : "Book Now"
616
+ children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
596
617
  }
597
618
  )
598
619
  ] })
@@ -634,6 +655,17 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
634
655
  const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
635
656
  const [modalState, setModalState] = (0, import_react3.useState)(null);
636
657
  const switchScheduleIdRef = (0, import_react3.useRef)(null);
658
+ const liveScheduleIdRef = (0, import_react3.useRef)(null);
659
+ const refetchLiveSchedule = (0, import_react3.useCallback)(async () => {
660
+ const id = liveScheduleIdRef.current;
661
+ if (!id) return;
662
+ try {
663
+ const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
664
+ const data = await res.json();
665
+ if (data?.id) setSchedule(data);
666
+ } catch {
667
+ }
668
+ }, []);
637
669
  const dates = (0, import_react3.useMemo)(() => {
638
670
  const today = /* @__PURE__ */ new Date();
639
671
  today.setHours(0, 0, 0, 0);
@@ -653,6 +685,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
653
685
  }
654
686
  }
655
687
  }, [schedule, dates]);
688
+ (0, import_react3.useEffect)(() => {
689
+ if (typeof window === "undefined") return;
690
+ const onVisible = () => {
691
+ if (!document.hidden) void refetchLiveSchedule();
692
+ };
693
+ window.addEventListener("focus", refetchLiveSchedule);
694
+ document.addEventListener("visibilitychange", onVisible);
695
+ return () => {
696
+ window.removeEventListener("focus", refetchLiveSchedule);
697
+ document.removeEventListener("visibilitychange", onVisible);
698
+ };
699
+ }, [refetchLiveSchedule]);
656
700
  (0, import_react3.useEffect)(() => {
657
701
  if (typeof window === "undefined") return;
658
702
  const isInEditor = window.self !== window.top;
@@ -714,7 +758,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
714
758
  setLoading(false);
715
759
  return;
716
760
  }
717
- ;
761
+ liveScheduleIdRef.current = effectiveId;
718
762
  (async () => {
719
763
  try {
720
764
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -771,18 +815,14 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
771
815
  const data = await res.json().catch(() => ({}));
772
816
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
773
817
  }
818
+ await refetchLiveSchedule();
774
819
  };
775
820
  const handleReplaceSchedule = () => {
776
821
  setIsHovered(false);
777
- if (schedule) {
778
- window.parent.postMessage({
779
- type: "ow:schedule-connected",
780
- schedule: { id: schedule.id, name: schedule.name },
781
- insertAfter
782
- }, "*");
783
- } else {
784
- window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
785
- }
822
+ window.parent.postMessage(
823
+ { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
824
+ "*"
825
+ );
786
826
  };
787
827
  if (!inEditor && !loading && !schedule) return null;
788
828
  const sectionId = `scheduling-${insertAfter}`;
@@ -825,7 +865,19 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
825
865
  ),
826
866
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
827
867
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
828
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
868
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
869
+ "h2",
870
+ {
871
+ className: "font-display text-center m-0 text-(--color-dark,#200C02)",
872
+ style: {
873
+ fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
874
+ fontWeight: "var(--font-weight-heading, 400)",
875
+ lineHeight: 1.05,
876
+ letterSpacing: "-0.02em"
877
+ },
878
+ children: schedule?.name ?? "Book an appointment"
879
+ }
880
+ ),
829
881
  schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
830
882
  ] }),
831
883
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4503,7 +4555,7 @@ function getChromeStyle(state) {
4503
4555
  case "dragging":
4504
4556
  return {
4505
4557
  border: `2px solid ${PRIMARY}`,
4506
- boxShadow: DRAG_SHADOW
4558
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4507
4559
  };
4508
4560
  default:
4509
4561
  return {};
@@ -4519,6 +4571,8 @@ function ItemInteractionLayer({
4519
4571
  dragHandleLabel = "Reorder item",
4520
4572
  onDragHandleDragStart,
4521
4573
  onDragHandleDragEnd,
4574
+ onItemPointerDown,
4575
+ onItemClick,
4522
4576
  chromeGap = 6,
4523
4577
  className
4524
4578
  }) {
@@ -4526,7 +4580,8 @@ function ItemInteractionLayer({
4526
4580
  const isActive = state === "active-top" || state === "active-bottom";
4527
4581
  const isDragging = state === "dragging";
4528
4582
  const showToolbar = isActive && toolbar;
4529
- const showDragHandle = isActive && showHandle && !isDragging;
4583
+ const showDragHandle = (isActive || isDragging) && showHandle;
4584
+ const itemDragEnabled = isActive && showHandle && !isDragging && !dragDisabled;
4530
4585
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4531
4586
  "div",
4532
4587
  {
@@ -4551,11 +4606,28 @@ function ItemInteractionLayer({
4551
4606
  style: getChromeStyle(state)
4552
4607
  }
4553
4608
  ),
4609
+ itemDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4610
+ "div",
4611
+ {
4612
+ "data-ohw-item-drag-surface": "",
4613
+ className: "pointer-events-auto absolute inset-0 cursor-grab rounded-lg active:cursor-grabbing",
4614
+ onPointerDown: (e) => {
4615
+ if (e.button !== 0) return;
4616
+ onItemPointerDown?.(e);
4617
+ },
4618
+ onClick: (e) => {
4619
+ e.preventDefault();
4620
+ e.stopPropagation();
4621
+ onItemClick?.(e.clientX, e.clientY);
4622
+ }
4623
+ }
4624
+ ),
4554
4625
  showDragHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4555
4626
  "div",
4556
4627
  {
4557
4628
  "data-ohw-drag-handle-container": "",
4558
- className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4629
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4630
+ style: isDragging ? { visibility: "hidden" } : void 0,
4559
4631
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4560
4632
  DragHandle,
4561
4633
  {
@@ -4778,15 +4850,12 @@ function MediaOverlay({
4778
4850
  className: "flex items-center justify-center cursor-pointer",
4779
4851
  style: {
4780
4852
  ...box,
4781
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4782
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4783
- // in-document, pointer-events does it natively. The button below opts back in, so
4784
- // Replace still works.
4785
- pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4853
+ // Keep the full-size visual overlay click-through so carousel controls and editable
4854
+ // content underneath remain interactive. The Replace button opts back in below.
4855
+ pointerEvents: "none",
4786
4856
  boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4787
4857
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4788
4858
  },
4789
- onClick: () => onReplace(hover.key),
4790
4859
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4791
4860
  Button,
4792
4861
  {
@@ -4811,6 +4880,61 @@ function MediaOverlay({
4811
4880
  ] });
4812
4881
  }
4813
4882
 
4883
+ // src/ui/CarouselOverlay.tsx
4884
+ var import_lucide_react4 = require("lucide-react");
4885
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4886
+ var OVERLAY_BUTTON_STYLE2 = {
4887
+ pointerEvents: "auto",
4888
+ fontFamily: "Inter, sans-serif",
4889
+ fontSize: 12,
4890
+ color: "#000"
4891
+ };
4892
+ function CarouselOverlay({
4893
+ hover,
4894
+ onEdit
4895
+ }) {
4896
+ const { rect } = hover;
4897
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4898
+ "div",
4899
+ {
4900
+ "data-ohw-bridge": "",
4901
+ "data-ohw-carousel-overlay": "",
4902
+ className: "flex cursor-pointer items-center justify-center",
4903
+ style: {
4904
+ position: "fixed",
4905
+ top: rect.top,
4906
+ left: rect.left,
4907
+ width: rect.width,
4908
+ height: rect.height,
4909
+ zIndex: 2147483646,
4910
+ pointerEvents: "auto",
4911
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4912
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4913
+ },
4914
+ onClick: () => onEdit(hover.key),
4915
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4916
+ Button,
4917
+ {
4918
+ "data-ohw-carousel-overlay": "",
4919
+ variant: "outline",
4920
+ size: "sm",
4921
+ className: "cursor-pointer gap-1.5 hover:bg-background",
4922
+ style: OVERLAY_BUTTON_STYLE2,
4923
+ onMouseDown: (e) => e.preventDefault(),
4924
+ onClick: (e) => {
4925
+ e.stopPropagation();
4926
+ onEdit(hover.key);
4927
+ },
4928
+ children: [
4929
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.GalleryHorizontal, { size: 14 }),
4930
+ "Edit gallery"
4931
+ ]
4932
+ }
4933
+ )
4934
+ }
4935
+ );
4936
+ }
4937
+
4814
4938
  // src/OhhwellsBridge.tsx
4815
4939
  var import_react_dom3 = require("react-dom");
4816
4940
  var import_navigation2 = require("next/navigation");
@@ -5096,23 +5220,23 @@ var import_react7 = require("react");
5096
5220
  // src/ui/dialog.tsx
5097
5221
  var React7 = __toESM(require("react"), 1);
5098
5222
  var import_radix_ui5 = require("radix-ui");
5099
- var import_lucide_react4 = require("lucide-react");
5100
- var import_jsx_runtime12 = require("react/jsx-runtime");
5223
+ var import_lucide_react5 = require("lucide-react");
5224
+ var import_jsx_runtime13 = require("react/jsx-runtime");
5101
5225
  function Dialog2({
5102
5226
  ...props
5103
5227
  }) {
5104
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5228
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5105
5229
  }
5106
5230
  function DialogPortal({
5107
5231
  ...props
5108
5232
  }) {
5109
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5233
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5110
5234
  }
5111
5235
  function DialogOverlay({
5112
5236
  className,
5113
5237
  ...props
5114
5238
  }) {
5115
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5239
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5116
5240
  import_radix_ui5.Dialog.Overlay,
5117
5241
  {
5118
5242
  "data-slot": "dialog-overlay",
@@ -5125,9 +5249,9 @@ function DialogOverlay({
5125
5249
  var DialogContent = React7.forwardRef(
5126
5250
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5127
5251
  const positionMode = container ? "absolute" : "fixed";
5128
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5129
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5130
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5252
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5253
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5254
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
5131
5255
  import_radix_ui5.Dialog.Content,
5132
5256
  {
5133
5257
  ref,
@@ -5143,13 +5267,13 @@ var DialogContent = React7.forwardRef(
5143
5267
  ...props,
5144
5268
  children: [
5145
5269
  children,
5146
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5270
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5147
5271
  import_radix_ui5.Dialog.Close,
5148
5272
  {
5149
5273
  type: "button",
5150
5274
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5151
5275
  "aria-label": "Close",
5152
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.X, { size: 16, "aria-hidden": true })
5276
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.X, { size: 16, "aria-hidden": true })
5153
5277
  }
5154
5278
  ) : null
5155
5279
  ]
@@ -5163,13 +5287,13 @@ function DialogHeader({
5163
5287
  className,
5164
5288
  ...props
5165
5289
  }) {
5166
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5290
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5167
5291
  }
5168
5292
  function DialogFooter({
5169
5293
  className,
5170
5294
  ...props
5171
5295
  }) {
5172
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5296
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5173
5297
  "div",
5174
5298
  {
5175
5299
  className: cn("flex items-center justify-end gap-2", className),
@@ -5177,7 +5301,7 @@ function DialogFooter({
5177
5301
  }
5178
5302
  );
5179
5303
  }
5180
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5304
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5181
5305
  import_radix_ui5.Dialog.Title,
5182
5306
  {
5183
5307
  ref,
@@ -5189,7 +5313,7 @@ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE
5189
5313
  }
5190
5314
  ));
5191
5315
  DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5192
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5316
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5193
5317
  import_radix_ui5.Dialog.Description,
5194
5318
  {
5195
5319
  ref,
@@ -5201,63 +5325,63 @@ DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
5201
5325
  var DialogClose = import_radix_ui5.Dialog.Close;
5202
5326
 
5203
5327
  // src/ui/link-modal/LinkEditorPanel.tsx
5204
- var import_lucide_react8 = require("lucide-react");
5328
+ var import_lucide_react9 = require("lucide-react");
5205
5329
 
5206
5330
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5207
- var import_lucide_react5 = require("lucide-react");
5208
- var import_jsx_runtime13 = require("react/jsx-runtime");
5331
+ var import_lucide_react6 = require("lucide-react");
5332
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5209
5333
  function DestinationBreadcrumb({
5210
5334
  pageTitle,
5211
5335
  sectionLabel
5212
5336
  }) {
5213
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5214
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5215
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
5216
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
5217
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5218
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5337
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5338
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5339
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-3", children: [
5340
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
5341
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5342
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5219
5343
  ] }),
5220
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5221
- import_lucide_react5.ArrowRight,
5344
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5345
+ import_lucide_react6.ArrowRight,
5222
5346
  {
5223
5347
  size: 16,
5224
5348
  className: "shrink-0 text-muted-foreground",
5225
5349
  "aria-hidden": true
5226
5350
  }
5227
5351
  ),
5228
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5229
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5230
- import_lucide_react5.GalleryVertical,
5352
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5353
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5354
+ import_lucide_react6.GalleryVertical,
5231
5355
  {
5232
5356
  size: 16,
5233
5357
  className: "shrink-0 text-foreground",
5234
5358
  "aria-hidden": true
5235
5359
  }
5236
5360
  ),
5237
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5361
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5238
5362
  ] })
5239
5363
  ] })
5240
5364
  ] });
5241
5365
  }
5242
5366
 
5243
5367
  // src/ui/link-modal/SectionTreeItem.tsx
5244
- var import_lucide_react6 = require("lucide-react");
5245
- var import_jsx_runtime14 = require("react/jsx-runtime");
5368
+ var import_lucide_react7 = require("lucide-react");
5369
+ var import_jsx_runtime15 = require("react/jsx-runtime");
5246
5370
  function SectionTreeItem({
5247
5371
  section,
5248
5372
  onSelect,
5249
5373
  selected
5250
5374
  }) {
5251
5375
  const interactive = Boolean(onSelect);
5252
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5253
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5376
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5377
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5254
5378
  "div",
5255
5379
  {
5256
5380
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5257
5381
  "aria-hidden": true
5258
5382
  }
5259
5383
  ),
5260
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5384
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5261
5385
  "div",
5262
5386
  {
5263
5387
  role: interactive ? "button" : void 0,
@@ -5275,15 +5399,15 @@ function SectionTreeItem({
5275
5399
  interactive && selected && "border-primary"
5276
5400
  ),
5277
5401
  children: [
5278
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5279
- import_lucide_react6.GalleryVertical,
5402
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5403
+ import_lucide_react7.GalleryVertical,
5280
5404
  {
5281
5405
  size: 16,
5282
5406
  className: "shrink-0 text-foreground",
5283
5407
  "aria-hidden": true
5284
5408
  }
5285
5409
  ),
5286
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5410
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5287
5411
  ]
5288
5412
  }
5289
5413
  )
@@ -5295,10 +5419,10 @@ var import_react4 = require("react");
5295
5419
 
5296
5420
  // src/ui/input.tsx
5297
5421
  var React8 = __toESM(require("react"), 1);
5298
- var import_jsx_runtime15 = require("react/jsx-runtime");
5422
+ var import_jsx_runtime16 = require("react/jsx-runtime");
5299
5423
  var Input = React8.forwardRef(
5300
5424
  ({ className, type, ...props }, ref) => {
5301
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5425
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5302
5426
  "input",
5303
5427
  {
5304
5428
  type,
@@ -5317,9 +5441,9 @@ Input.displayName = "Input";
5317
5441
 
5318
5442
  // src/ui/label.tsx
5319
5443
  var import_radix_ui6 = require("radix-ui");
5320
- var import_jsx_runtime16 = require("react/jsx-runtime");
5444
+ var import_jsx_runtime17 = require("react/jsx-runtime");
5321
5445
  function Label({ className, ...props }) {
5322
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5446
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5323
5447
  import_radix_ui6.Label.Root,
5324
5448
  {
5325
5449
  "data-slot": "label",
@@ -5330,12 +5454,12 @@ function Label({ className, ...props }) {
5330
5454
  }
5331
5455
 
5332
5456
  // src/ui/link-modal/UrlOrPageInput.tsx
5333
- var import_lucide_react7 = require("lucide-react");
5334
- var import_jsx_runtime17 = require("react/jsx-runtime");
5457
+ var import_lucide_react8 = require("lucide-react");
5458
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5335
5459
  function FieldChevron({
5336
5460
  onClick
5337
5461
  }) {
5338
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5462
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5339
5463
  "button",
5340
5464
  {
5341
5465
  type: "button",
@@ -5343,7 +5467,7 @@ function FieldChevron({
5343
5467
  onClick,
5344
5468
  "aria-label": "Open page list",
5345
5469
  tabIndex: -1,
5346
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.ChevronDown, { size: 16 })
5470
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.ChevronDown, { size: 16 })
5347
5471
  }
5348
5472
  );
5349
5473
  }
@@ -5404,19 +5528,19 @@ function UrlOrPageInput({
5404
5528
  "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]",
5405
5529
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5406
5530
  );
5407
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5408
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5409
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
5410
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5411
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5412
- import_lucide_react7.File,
5531
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5532
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5533
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
5534
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5535
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5536
+ import_lucide_react8.File,
5413
5537
  {
5414
5538
  size: 16,
5415
5539
  className: "shrink-0 text-foreground",
5416
5540
  "aria-hidden": true
5417
5541
  }
5418
5542
  ) }) : null,
5419
- readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5543
+ readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5420
5544
  Input,
5421
5545
  {
5422
5546
  ref: inputRef,
@@ -5442,7 +5566,7 @@ function UrlOrPageInput({
5442
5566
  )
5443
5567
  }
5444
5568
  ),
5445
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5569
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5446
5570
  "button",
5447
5571
  {
5448
5572
  type: "button",
@@ -5450,26 +5574,26 @@ function UrlOrPageInput({
5450
5574
  onMouseDown: clearSelection,
5451
5575
  "aria-label": "Clear selected page",
5452
5576
  tabIndex: -1,
5453
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, "aria-hidden": true })
5577
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5454
5578
  }
5455
5579
  ) : null,
5456
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5580
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5457
5581
  ] }),
5458
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5582
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5459
5583
  "div",
5460
5584
  {
5461
5585
  "data-ohw-link-page-dropdown": "",
5462
5586
  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",
5463
5587
  onMouseDown: (e) => e.preventDefault(),
5464
- children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5588
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5465
5589
  "button",
5466
5590
  {
5467
5591
  type: "button",
5468
5592
  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",
5469
5593
  onClick: () => onPageSelect(page),
5470
5594
  children: [
5471
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5472
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: page.title })
5595
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5596
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate", children: page.title })
5473
5597
  ]
5474
5598
  },
5475
5599
  page.path
@@ -5477,34 +5601,34 @@ function UrlOrPageInput({
5477
5601
  }
5478
5602
  ) : null
5479
5603
  ] }),
5480
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5604
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5481
5605
  ] });
5482
5606
  }
5483
5607
 
5484
5608
  // src/ui/link-modal/LinkEditorPanel.tsx
5485
- var import_jsx_runtime18 = require("react/jsx-runtime");
5609
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5486
5610
  function LinkEditorPanel({ state, onClose }) {
5487
5611
  const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5488
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5489
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5612
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5613
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5490
5614
  "button",
5491
5615
  {
5492
5616
  type: "button",
5493
5617
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5494
5618
  "aria-label": "Close",
5495
5619
  onClick: onClose,
5496
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5620
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.X, { size: 16, "aria-hidden": true })
5497
5621
  }
5498
5622
  ) }),
5499
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5500
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5501
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5623
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5624
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5625
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5502
5626
  DestinationBreadcrumb,
5503
5627
  {
5504
5628
  pageTitle: state.selectedPage.title,
5505
5629
  sectionLabel: state.selectedSection.label
5506
5630
  }
5507
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5631
+ ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5508
5632
  UrlOrPageInput,
5509
5633
  {
5510
5634
  value: state.searchValue,
@@ -5517,8 +5641,8 @@ function LinkEditorPanel({ state, onClose }) {
5517
5641
  urlError: state.urlError
5518
5642
  }
5519
5643
  ),
5520
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5521
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5644
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5645
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5522
5646
  Button,
5523
5647
  {
5524
5648
  type: "button",
@@ -5529,15 +5653,15 @@ function LinkEditorPanel({ state, onClose }) {
5529
5653
  children: "Choose a section"
5530
5654
  }
5531
5655
  ),
5532
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5533
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5534
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
5656
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5657
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5658
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Pick a section this link should scroll to." })
5535
5659
  ] })
5536
5660
  ] }) : null,
5537
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5661
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5538
5662
  ] }),
5539
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5540
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5663
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5664
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5541
5665
  Button,
5542
5666
  {
5543
5667
  type: "button",
@@ -5552,7 +5676,7 @@ function LinkEditorPanel({ state, onClose }) {
5552
5676
  children: state.secondaryLabel
5553
5677
  }
5554
5678
  ),
5555
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5679
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5556
5680
  Button,
5557
5681
  {
5558
5682
  type: "button",
@@ -5573,9 +5697,9 @@ function LinkEditorPanel({ state, onClose }) {
5573
5697
  // src/ui/link-modal/SectionPickerOverlay.tsx
5574
5698
  var import_react5 = require("react");
5575
5699
  var import_react_dom = require("react-dom");
5576
- var import_lucide_react9 = require("lucide-react");
5700
+ var import_lucide_react10 = require("lucide-react");
5577
5701
  var import_navigation = require("next/navigation");
5578
- var import_jsx_runtime19 = require("react/jsx-runtime");
5702
+ var import_jsx_runtime20 = require("react/jsx-runtime");
5579
5703
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5580
5704
  function rectsEqual(a, b) {
5581
5705
  if (a.size !== b.size) return false;
@@ -5804,7 +5928,7 @@ function SectionPickerOverlay({
5804
5928
  const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5805
5929
  if (!portalRoot) return null;
5806
5930
  return (0, import_react_dom.createPortal)(
5807
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5931
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5808
5932
  "div",
5809
5933
  {
5810
5934
  "data-ohw-section-picker": "",
@@ -5814,12 +5938,12 @@ function SectionPickerOverlay({
5814
5938
  role: "dialog",
5815
5939
  "aria-label": "Choose a section",
5816
5940
  children: [
5817
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5941
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5818
5942
  "div",
5819
5943
  {
5820
5944
  className: "pointer-events-auto fixed left-5 z-[2]",
5821
5945
  style: { top: chromeClip.top + 20 },
5822
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5946
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5823
5947
  Button,
5824
5948
  {
5825
5949
  type: "button",
@@ -5828,14 +5952,14 @@ function SectionPickerOverlay({
5828
5952
  className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5829
5953
  onClick: onBack,
5830
5954
  children: [
5831
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5955
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5832
5956
  "Back"
5833
5957
  ]
5834
5958
  }
5835
5959
  )
5836
5960
  }
5837
5961
  ),
5838
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5962
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5839
5963
  "div",
5840
5964
  {
5841
5965
  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",
@@ -5848,7 +5972,7 @@ function SectionPickerOverlay({
5848
5972
  children: "Click on section to select"
5849
5973
  }
5850
5974
  ),
5851
- !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5975
+ !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5852
5976
  "div",
5853
5977
  {
5854
5978
  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",
@@ -5856,14 +5980,14 @@ function SectionPickerOverlay({
5856
5980
  children: "Loading page preview\u2026"
5857
5981
  }
5858
5982
  ) : null,
5859
- isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5983
+ isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5860
5984
  isOnTargetPage ? liveSections.map((section) => {
5861
5985
  const rect = rects.get(section.id);
5862
5986
  if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5863
5987
  const isSelected = selectedId === section.id;
5864
5988
  const isHovered = hoveredId === section.id;
5865
5989
  const isLit = isSelected || isHovered;
5866
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5990
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5867
5991
  "button",
5868
5992
  {
5869
5993
  type: "button",
@@ -5878,7 +6002,7 @@ function SectionPickerOverlay({
5878
6002
  "aria-label": `Select section ${section.label}`,
5879
6003
  onClick: () => handleSelect(section),
5880
6004
  children: [
5881
- isLit ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6005
+ isLit ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5882
6006
  "span",
5883
6007
  {
5884
6008
  className: "pointer-events-none absolute",
@@ -5891,13 +6015,13 @@ function SectionPickerOverlay({
5891
6015
  "aria-hidden": true
5892
6016
  }
5893
6017
  ) : null,
5894
- isSelected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6018
+ isSelected ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5895
6019
  "span",
5896
6020
  {
5897
6021
  className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5898
6022
  style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5899
6023
  "aria-hidden": true,
5900
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Check, { className: "size-5" })
6024
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.Check, { className: "size-5" })
5901
6025
  }
5902
6026
  ) : null
5903
6027
  ]
@@ -6077,7 +6201,7 @@ function useLinkModalState({
6077
6201
  }
6078
6202
 
6079
6203
  // src/ui/link-modal/LinkPopover.tsx
6080
- var import_jsx_runtime20 = require("react/jsx-runtime");
6204
+ var import_jsx_runtime21 = require("react/jsx-runtime");
6081
6205
  function postToParent(data) {
6082
6206
  window.parent?.postMessage(data, "*");
6083
6207
  }
@@ -6173,15 +6297,15 @@ function LinkPopover({
6173
6297
  );
6174
6298
  };
6175
6299
  }, [open, sectionPickerActive]);
6176
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
6177
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6300
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6301
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6178
6302
  Dialog2,
6179
6303
  {
6180
6304
  open: open && !sectionPickerActive,
6181
6305
  onOpenChange: (next) => {
6182
6306
  if (!next) onClose?.();
6183
6307
  },
6184
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6308
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6185
6309
  DialogContent,
6186
6310
  {
6187
6311
  ref: panelRef,
@@ -6191,12 +6315,12 @@ function LinkPopover({
6191
6315
  "data-ohw-bridge": "",
6192
6316
  showCloseButton: false,
6193
6317
  className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6194
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { state, onClose })
6318
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LinkEditorPanel, { state, onClose })
6195
6319
  }
6196
6320
  )
6197
6321
  }
6198
6322
  ),
6199
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6323
+ sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6200
6324
  SectionPickerOverlay,
6201
6325
  {
6202
6326
  pagePath: state.selectedPage.path,
@@ -6502,15 +6626,412 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6502
6626
  };
6503
6627
  }
6504
6628
 
6629
+ // src/lib/footer-items.ts
6630
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6631
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6632
+ function parseFooterHrefKey(key) {
6633
+ if (!key) return null;
6634
+ const match = key.match(FOOTER_HREF_RE);
6635
+ if (!match) return null;
6636
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6637
+ }
6638
+ function isFooterHrefKey(key) {
6639
+ return parseFooterHrefKey(key) !== null;
6640
+ }
6641
+ function getFooterRoot() {
6642
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6643
+ }
6644
+ function getFooterLinksContainer() {
6645
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6646
+ }
6647
+ function isFooterLinkAnchor(el) {
6648
+ if (!el.matches("[data-ohw-href-key]")) return false;
6649
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6650
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6651
+ }
6652
+ function listFooterColumns() {
6653
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6654
+ if (!root) return [];
6655
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6656
+ if (explicit.length > 0) return explicit;
6657
+ return Array.from(root.children).filter((el) => {
6658
+ if (!(el instanceof HTMLElement)) return false;
6659
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6660
+ isFooterLinkAnchor
6661
+ );
6662
+ });
6663
+ }
6664
+ function listFooterLinksInColumn(column) {
6665
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6666
+ isFooterLinkAnchor
6667
+ );
6668
+ }
6669
+ function findFooterColumnForLink(anchor) {
6670
+ const columns = listFooterColumns();
6671
+ return columns.find((col) => col.contains(anchor)) ?? null;
6672
+ }
6673
+ function getFooterOrderFromDom() {
6674
+ return listFooterColumns().map(
6675
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6676
+ );
6677
+ }
6678
+ function findFooterLinkByKey(hrefKey) {
6679
+ return document.querySelector(
6680
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6681
+ );
6682
+ }
6683
+ function syncFooterColumnIndices(columns) {
6684
+ columns.forEach((col, index) => {
6685
+ const next = String(index);
6686
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6687
+ col.setAttribute("data-ohw-footer-col", next);
6688
+ }
6689
+ });
6690
+ }
6691
+ function appendChildIfNeeded(parent, child) {
6692
+ if (parent.lastElementChild !== child) {
6693
+ parent.appendChild(child);
6694
+ }
6695
+ }
6696
+ function insertLinkBeforeNext(col, el, nextEl) {
6697
+ if (nextEl) {
6698
+ if (el.nextElementSibling !== nextEl) {
6699
+ col.insertBefore(el, nextEl);
6700
+ }
6701
+ return;
6702
+ }
6703
+ if (col.lastElementChild !== el) {
6704
+ col.appendChild(el);
6705
+ }
6706
+ }
6707
+ function applyFooterOrder(order) {
6708
+ const container = getFooterLinksContainer();
6709
+ if (!container) return;
6710
+ const columns = listFooterColumns();
6711
+ if (columns.length === 0) return;
6712
+ const currentOrder = getFooterOrderFromDom();
6713
+ if (ordersEqual(order, currentOrder)) {
6714
+ syncFooterColumnIndices(columns);
6715
+ return;
6716
+ }
6717
+ const colCount = Math.max(columns.length, order.length);
6718
+ const working = columns.slice(0, colCount);
6719
+ for (let i = 0; i < order.length && i < working.length; i++) {
6720
+ appendChildIfNeeded(container, working[i]);
6721
+ }
6722
+ for (let i = order.length; i < working.length; i++) {
6723
+ appendChildIfNeeded(container, working[i]);
6724
+ }
6725
+ const freshColumns = listFooterColumns();
6726
+ syncFooterColumnIndices(freshColumns);
6727
+ for (let c = 0; c < order.length; c++) {
6728
+ const col = freshColumns[c];
6729
+ if (!col) continue;
6730
+ const colOrder = order[c];
6731
+ for (let j = 0; j < colOrder.length; j++) {
6732
+ const hrefKey = colOrder[j];
6733
+ const el = findFooterLinkByKey(hrefKey);
6734
+ if (!el) continue;
6735
+ if (el.parentElement !== col) {
6736
+ col.appendChild(el);
6737
+ }
6738
+ const nextKey = colOrder[j + 1];
6739
+ const nextEl = nextKey ? findFooterLinkByKey(nextKey) : null;
6740
+ insertLinkBeforeNext(col, el, nextEl?.parentElement === col ? nextEl : null);
6741
+ }
6742
+ }
6743
+ }
6744
+ function ordersEqual(a, b) {
6745
+ if (a.length !== b.length) return false;
6746
+ for (let i = 0; i < a.length; i++) {
6747
+ const left = a[i];
6748
+ const right = b[i];
6749
+ if (left.length !== right.length) return false;
6750
+ for (let j = 0; j < left.length; j++) {
6751
+ if (left[j] !== right[j]) return false;
6752
+ }
6753
+ }
6754
+ return true;
6755
+ }
6756
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6757
+ const order = getFooterOrderFromDom();
6758
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6759
+ let fromCol = -1;
6760
+ let fromIdx = -1;
6761
+ for (let c = 0; c < order.length; c++) {
6762
+ const idx = order[c].indexOf(hrefKey);
6763
+ if (idx >= 0) {
6764
+ fromCol = c;
6765
+ fromIdx = idx;
6766
+ break;
6767
+ }
6768
+ }
6769
+ if (fromCol < 0) return null;
6770
+ const next = order.map((col) => [...col]);
6771
+ next[fromCol].splice(fromIdx, 1);
6772
+ let adjusted = insertIndex;
6773
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6774
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6775
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
6776
+ if (ordersEqual(next, order)) return null;
6777
+ return next;
6778
+ }
6779
+ function planFooterColumnMove(fromIndex, toIndex) {
6780
+ const order = getFooterOrderFromDom();
6781
+ const columns = listFooterColumns();
6782
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6783
+ return null;
6784
+ }
6785
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6786
+ const nextOrder = order.map((col) => [...col]);
6787
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
6788
+ if (!movedOrder) return null;
6789
+ let adjusted = toIndex;
6790
+ if (fromIndex < toIndex) adjusted -= 1;
6791
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6792
+ nextOrder.splice(adjusted, 0, movedOrder);
6793
+ if (ordersEqual(nextOrder, order)) return null;
6794
+ return nextOrder;
6795
+ }
6796
+ function parseFooterOrder(content) {
6797
+ const raw = content[FOOTER_ORDER_KEY];
6798
+ if (!raw) return null;
6799
+ try {
6800
+ const parsed = JSON.parse(raw);
6801
+ if (!Array.isArray(parsed)) return null;
6802
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
6803
+ } catch {
6804
+ return null;
6805
+ }
6806
+ }
6807
+ function reconcileFooterOrderFromContent(content) {
6808
+ const order = parseFooterOrder(content);
6809
+ if (!order?.length) return;
6810
+ const current = getFooterOrderFromDom();
6811
+ if (ordersEqual(order, current)) {
6812
+ syncFooterColumnIndices(listFooterColumns());
6813
+ return;
6814
+ }
6815
+ applyFooterOrder(order);
6816
+ }
6817
+ function buildLinkDropSlots(column, columnIndex) {
6818
+ const links = listFooterLinksInColumn(column);
6819
+ const colRect = column.getBoundingClientRect();
6820
+ const slots = [];
6821
+ const barThickness = 3;
6822
+ if (links.length === 0) {
6823
+ slots.push({
6824
+ insertIndex: 0,
6825
+ columnIndex,
6826
+ left: colRect.left,
6827
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
6828
+ width: colRect.width,
6829
+ height: barThickness,
6830
+ direction: "horizontal"
6831
+ });
6832
+ return slots;
6833
+ }
6834
+ for (let i = 0; i <= links.length; i++) {
6835
+ let top;
6836
+ if (i === 0) {
6837
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
6838
+ } else if (i === links.length) {
6839
+ const last = links[links.length - 1].getBoundingClientRect();
6840
+ top = last.bottom - barThickness / 2;
6841
+ } else {
6842
+ const prev = links[i - 1].getBoundingClientRect();
6843
+ const next = links[i].getBoundingClientRect();
6844
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
6845
+ }
6846
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
6847
+ slots.push({
6848
+ insertIndex: i,
6849
+ columnIndex,
6850
+ left: widthRef.left,
6851
+ top,
6852
+ width: Math.max(widthRef.width, colRect.width * 0.8),
6853
+ height: barThickness,
6854
+ direction: "horizontal"
6855
+ });
6856
+ }
6857
+ return slots;
6858
+ }
6859
+ function buildColumnDropSlots() {
6860
+ const columns = listFooterColumns();
6861
+ const slots = [];
6862
+ const barThickness = 3;
6863
+ if (columns.length === 0) return slots;
6864
+ for (let i = 0; i <= columns.length; i++) {
6865
+ let left;
6866
+ let height;
6867
+ let top;
6868
+ if (i === 0) {
6869
+ const first = columns[0].getBoundingClientRect();
6870
+ left = first.left - barThickness / 2;
6871
+ top = first.top;
6872
+ height = first.height;
6873
+ } else if (i === columns.length) {
6874
+ const last = columns[columns.length - 1].getBoundingClientRect();
6875
+ left = last.right - barThickness / 2;
6876
+ top = last.top;
6877
+ height = last.height;
6878
+ } else {
6879
+ const prev = columns[i - 1].getBoundingClientRect();
6880
+ const next = columns[i].getBoundingClientRect();
6881
+ left = (prev.right + next.left) / 2 - barThickness / 2;
6882
+ top = Math.min(prev.top, next.top);
6883
+ height = Math.max(prev.bottom, next.bottom) - top;
6884
+ }
6885
+ slots.push({
6886
+ insertIndex: i,
6887
+ columnIndex: i,
6888
+ left,
6889
+ top,
6890
+ width: barThickness,
6891
+ height: Math.max(height, 24),
6892
+ direction: "vertical"
6893
+ });
6894
+ }
6895
+ return slots;
6896
+ }
6897
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
6898
+ const columns = listFooterColumns();
6899
+ let best = null;
6900
+ for (let c = 0; c < columns.length; c++) {
6901
+ const col = columns[c];
6902
+ const colRect = col.getBoundingClientRect();
6903
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
6904
+ if (!inColX) continue;
6905
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
6906
+ const links = listFooterLinksInColumn(col);
6907
+ const fromIdx = links.findIndex(
6908
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
6909
+ );
6910
+ if (fromIdx < 0) return true;
6911
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
6912
+ return true;
6913
+ }
6914
+ return true;
6915
+ });
6916
+ for (const slot of slots) {
6917
+ const cy = slot.top + slot.height / 2;
6918
+ const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
6919
+ if (!best || dist < best.dist) best = { slot, dist };
6920
+ }
6921
+ }
6922
+ return best?.slot ?? null;
6923
+ }
6924
+ function findColumnIndexForKey(hrefKey) {
6925
+ const order = getFooterOrderFromDom();
6926
+ for (let c = 0; c < order.length; c++) {
6927
+ if (order[c].includes(hrefKey)) return c;
6928
+ }
6929
+ return -1;
6930
+ }
6931
+ function hitTestColumnDropSlot(clientX, _clientY) {
6932
+ const slots = buildColumnDropSlots();
6933
+ let best = null;
6934
+ for (const slot of slots) {
6935
+ const cx2 = slot.left + slot.width / 2;
6936
+ const dist = Math.abs(clientX - cx2);
6937
+ if (!best || dist < best.dist) best = { slot, dist };
6938
+ }
6939
+ return best?.slot ?? null;
6940
+ }
6941
+
6942
+ // src/lib/carousel.ts
6943
+ var import_react8 = require("react");
6944
+ var CAROUSEL_ATTR = "data-ohw-carousel";
6945
+ var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
6946
+ var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
6947
+ var CAROUSEL_EVENT = "ohw:carousel-change";
6948
+ function listCarouselKeys() {
6949
+ const keys = /* @__PURE__ */ new Set();
6950
+ document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
6951
+ const key = el.getAttribute("data-ohw-key");
6952
+ if (key) keys.add(key);
6953
+ });
6954
+ return [...keys];
6955
+ }
6956
+ function containersForKey(key) {
6957
+ return Array.from(
6958
+ document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
6959
+ );
6960
+ }
6961
+ function isCarouselKey(key) {
6962
+ return containersForKey(key).length > 0;
6963
+ }
6964
+ function parseSlides(raw) {
6965
+ if (!raw) return [];
6966
+ try {
6967
+ const parsed = JSON.parse(raw);
6968
+ if (!Array.isArray(parsed)) return [];
6969
+ return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
6970
+ } catch {
6971
+ return [];
6972
+ }
6973
+ }
6974
+ function readCarouselValue(key) {
6975
+ const container = containersForKey(key)[0];
6976
+ if (!container) return [];
6977
+ const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
6978
+ if (fromAttr.length > 0) return fromAttr;
6979
+ return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
6980
+ const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
6981
+ return { src: img?.src ?? "", alt: img?.alt ?? "" };
6982
+ });
6983
+ }
6984
+ function slideIndex(el) {
6985
+ const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
6986
+ const n = raw ? parseInt(raw, 10) : NaN;
6987
+ return Number.isFinite(n) ? n : 0;
6988
+ }
6989
+ function applyCarouselValue(key, slides) {
6990
+ const value = JSON.stringify(slides);
6991
+ for (const container of containersForKey(key)) {
6992
+ if (container.getAttribute(CAROUSEL_VALUE_ATTR) === value) continue;
6993
+ container.setAttribute(CAROUSEL_VALUE_ATTR, value);
6994
+ container.dispatchEvent(
6995
+ new CustomEvent(CAROUSEL_EVENT, { detail: { images: slides } })
6996
+ );
6997
+ }
6998
+ }
6999
+ function applyCarouselNode(key, val) {
7000
+ if (!isCarouselKey(key)) return false;
7001
+ applyCarouselValue(key, parseSlides(val));
7002
+ return true;
7003
+ }
7004
+ function useOhwCarousel(key, initial) {
7005
+ const [images, setImages] = (0, import_react8.useState)(initial);
7006
+ (0, import_react8.useEffect)(() => {
7007
+ const el = document.querySelector(
7008
+ `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
7009
+ );
7010
+ if (!el) return;
7011
+ const onChange = (e) => {
7012
+ const detail = e.detail;
7013
+ if (Array.isArray(detail?.images)) setImages(detail.images);
7014
+ };
7015
+ el.addEventListener(CAROUSEL_EVENT, onChange);
7016
+ return () => el.removeEventListener(CAROUSEL_EVENT, onChange);
7017
+ }, [key]);
7018
+ const bind = {
7019
+ [CAROUSEL_ATTR]: "",
7020
+ "data-ohw-key": key,
7021
+ [CAROUSEL_VALUE_ATTR]: JSON.stringify(images)
7022
+ };
7023
+ return { images, setImages, bind };
7024
+ }
7025
+
6505
7026
  // src/ui/navbar-container-chrome.tsx
6506
- var import_lucide_react10 = require("lucide-react");
6507
- var import_jsx_runtime21 = require("react/jsx-runtime");
7027
+ var import_lucide_react11 = require("lucide-react");
7028
+ var import_jsx_runtime22 = require("react/jsx-runtime");
6508
7029
  function NavbarContainerChrome({
6509
7030
  rect,
6510
7031
  onAdd
6511
7032
  }) {
6512
7033
  const chromeGap = 6;
6513
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7034
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6514
7035
  "div",
6515
7036
  {
6516
7037
  "data-ohw-navbar-container-chrome": "",
@@ -6522,7 +7043,7 @@ function NavbarContainerChrome({
6522
7043
  width: rect.width + chromeGap * 2,
6523
7044
  height: rect.height + chromeGap * 2
6524
7045
  },
6525
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7046
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6526
7047
  "button",
6527
7048
  {
6528
7049
  type: "button",
@@ -6539,15 +7060,57 @@ function NavbarContainerChrome({
6539
7060
  e.stopPropagation();
6540
7061
  onAdd();
6541
7062
  },
6542
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7063
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6543
7064
  }
6544
7065
  )
6545
7066
  }
6546
7067
  );
6547
7068
  }
6548
7069
 
7070
+ // src/ui/drop-indicator.tsx
7071
+ var React9 = __toESM(require("react"), 1);
7072
+ var import_jsx_runtime23 = require("react/jsx-runtime");
7073
+ var dropIndicatorVariants = cva(
7074
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7075
+ {
7076
+ variants: {
7077
+ direction: {
7078
+ vertical: "h-6 w-[3px]",
7079
+ horizontal: "h-[3px] w-[200px]"
7080
+ },
7081
+ state: {
7082
+ default: "opacity-0",
7083
+ hover: "opacity-100",
7084
+ dragIdle: "opacity-40",
7085
+ dragActive: "opacity-100"
7086
+ }
7087
+ },
7088
+ defaultVariants: {
7089
+ direction: "vertical",
7090
+ state: "default"
7091
+ }
7092
+ }
7093
+ );
7094
+ var DropIndicator = React9.forwardRef(
7095
+ ({ className, direction, state, ...props }, ref) => {
7096
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7097
+ "div",
7098
+ {
7099
+ ref,
7100
+ "data-slot": "drop-indicator",
7101
+ "data-direction": direction ?? "vertical",
7102
+ "data-state": state ?? "default",
7103
+ "aria-hidden": "true",
7104
+ className: cn(dropIndicatorVariants({ direction, state }), className),
7105
+ ...props
7106
+ }
7107
+ );
7108
+ }
7109
+ );
7110
+ DropIndicator.displayName = "DropIndicator";
7111
+
6549
7112
  // src/ui/badge.tsx
6550
- var import_jsx_runtime22 = require("react/jsx-runtime");
7113
+ var import_jsx_runtime24 = require("react/jsx-runtime");
6551
7114
  var badgeVariants = cva(
6552
7115
  "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",
6553
7116
  {
@@ -6565,12 +7128,12 @@ var badgeVariants = cva(
6565
7128
  }
6566
7129
  );
6567
7130
  function Badge({ className, variant, ...props }) {
6568
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
7131
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6569
7132
  }
6570
7133
 
6571
7134
  // src/OhhwellsBridge.tsx
6572
- var import_lucide_react11 = require("lucide-react");
6573
- var import_jsx_runtime23 = require("react/jsx-runtime");
7135
+ var import_lucide_react12 = require("lucide-react");
7136
+ var import_jsx_runtime25 = require("react/jsx-runtime");
6574
7137
  var PRIMARY2 = "#0885FE";
6575
7138
  var IMAGE_FADE_MS = 300;
6576
7139
  function runOpacityFade(el, onDone) {
@@ -6749,7 +7312,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6749
7312
  const root = (0, import_client.createRoot)(container);
6750
7313
  (0, import_react_dom2.flushSync)(() => {
6751
7314
  root.render(
6752
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7315
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6753
7316
  SchedulingWidget,
6754
7317
  {
6755
7318
  notifyOnConnect,
@@ -6811,7 +7374,7 @@ function isDragHandleDisabled(el) {
6811
7374
  return raw === "true" || raw === "";
6812
7375
  });
6813
7376
  }
6814
- function collectEditableNodes() {
7377
+ function collectEditableNodes(extraContent) {
6815
7378
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6816
7379
  if (el.dataset.ohwEditable === "image") {
6817
7380
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6839,6 +7402,14 @@ function collectEditableNodes() {
6839
7402
  if (!key) return;
6840
7403
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6841
7404
  });
7405
+ if (extraContent) {
7406
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
7407
+ const text = extraContent[key];
7408
+ if (typeof text === "string" && text.length > 0) {
7409
+ nodes.push({ key, type: "meta", text });
7410
+ }
7411
+ }
7412
+ }
6842
7413
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6843
7414
  const key = el.dataset.ohwKey ?? "";
6844
7415
  const video = getVideoEl(el);
@@ -6846,13 +7417,10 @@ function collectEditableNodes() {
6846
7417
  nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
6847
7418
  nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
6848
7419
  });
6849
- const seen = /* @__PURE__ */ new Set();
6850
- return nodes.filter((node) => {
6851
- if (!node.key) return true;
6852
- if (seen.has(node.key)) return false;
6853
- seen.add(node.key);
6854
- return true;
6855
- });
7420
+ for (const key of listCarouselKeys()) {
7421
+ nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
7422
+ }
7423
+ return nodes;
6856
7424
  }
6857
7425
  function isMediaEditable(el) {
6858
7426
  const t = el.dataset.ohwEditable;
@@ -6940,6 +7508,29 @@ function getHrefKeyFromElement(el) {
6940
7508
  if (!key) return null;
6941
7509
  return { anchor, key };
6942
7510
  }
7511
+ function disableNativeHrefDrag(el) {
7512
+ if (el.draggable) el.draggable = false;
7513
+ if (el.getAttribute("draggable") !== "false") {
7514
+ el.setAttribute("draggable", "false");
7515
+ }
7516
+ }
7517
+ function clearTextSelection() {
7518
+ const sel = window.getSelection();
7519
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7520
+ }
7521
+ function armFooterPressDrag() {
7522
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7523
+ }
7524
+ function lockFooterDuringDrag() {
7525
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7526
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7527
+ clearTextSelection();
7528
+ }
7529
+ function unlockFooterDragInteraction() {
7530
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7531
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7532
+ clearTextSelection();
7533
+ }
6943
7534
  function isNavbarButton2(el) {
6944
7535
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
6945
7536
  }
@@ -7002,7 +7593,7 @@ function isInferredFooterGroup(el) {
7002
7593
  return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
7003
7594
  }
7004
7595
  function isNavigationContainer(el) {
7005
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
7596
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || isNavigationRoot(el) || isInferredFooterGroup(el);
7006
7597
  }
7007
7598
  function isPointOverNavigation(x, y) {
7008
7599
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
@@ -7212,6 +7803,8 @@ var ICONS = {
7212
7803
  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"/>',
7213
7804
  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"/>'
7214
7805
  };
7806
+ var TOOLBAR_PILL_PADDING = 2;
7807
+ var TOOLBAR_TOGGLE_GAP = 4;
7215
7808
  var TOOLBAR_GROUPS = [
7216
7809
  [
7217
7810
  { cmd: "bold", title: "Bold" },
@@ -7236,7 +7829,7 @@ function EditGlowChrome({
7236
7829
  dragDisabled = false
7237
7830
  }) {
7238
7831
  const GAP = 6;
7239
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7832
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
7240
7833
  "div",
7241
7834
  {
7242
7835
  ref: elRef,
@@ -7251,7 +7844,7 @@ function EditGlowChrome({
7251
7844
  zIndex: 2147483646
7252
7845
  },
7253
7846
  children: [
7254
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7847
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7255
7848
  "div",
7256
7849
  {
7257
7850
  style: {
@@ -7264,7 +7857,7 @@ function EditGlowChrome({
7264
7857
  }
7265
7858
  }
7266
7859
  ),
7267
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7860
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7268
7861
  "div",
7269
7862
  {
7270
7863
  "data-ohw-drag-handle-container": "",
@@ -7276,7 +7869,7 @@ function EditGlowChrome({
7276
7869
  transform: "translate(calc(-100% - 7px), -50%)",
7277
7870
  pointerEvents: dragDisabled ? "none" : "auto"
7278
7871
  },
7279
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7872
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7280
7873
  DragHandle,
7281
7874
  {
7282
7875
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7380,7 +7973,7 @@ function FloatingToolbar({
7380
7973
  onEditLink
7381
7974
  }) {
7382
7975
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7383
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7976
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7384
7977
  "div",
7385
7978
  {
7386
7979
  ref: elRef,
@@ -7390,14 +7983,23 @@ function FloatingToolbar({
7390
7983
  left,
7391
7984
  transform,
7392
7985
  zIndex: 2147483647,
7986
+ background: "#fff",
7987
+ border: "1px solid #E7E5E4",
7988
+ borderRadius: 6,
7989
+ boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
7990
+ display: "flex",
7991
+ alignItems: "center",
7992
+ padding: TOOLBAR_PILL_PADDING,
7993
+ gap: TOOLBAR_TOGGLE_GAP,
7994
+ fontFamily: "sans-serif",
7393
7995
  pointerEvents: "auto"
7394
7996
  },
7395
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CustomToolbar, { children: [
7396
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react8.default.Fragment, { children: [
7397
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CustomToolbarDivider, {}),
7997
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CustomToolbar, { children: [
7998
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react9.default.Fragment, { children: [
7999
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(CustomToolbarDivider, {}),
7398
8000
  btns.map((btn) => {
7399
8001
  const isActive = activeCommands.has(btn.cmd);
7400
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8002
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7401
8003
  CustomToolbarButton,
7402
8004
  {
7403
8005
  title: btn.title,
@@ -7406,7 +8008,7 @@ function FloatingToolbar({
7406
8008
  e.preventDefault();
7407
8009
  onCommand(btn.cmd);
7408
8010
  },
7409
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8011
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7410
8012
  "svg",
7411
8013
  {
7412
8014
  width: "16",
@@ -7427,7 +8029,7 @@ function FloatingToolbar({
7427
8029
  );
7428
8030
  })
7429
8031
  ] }, gi)),
7430
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8032
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7431
8033
  CustomToolbarButton,
7432
8034
  {
7433
8035
  type: "button",
@@ -7441,7 +8043,7 @@ function FloatingToolbar({
7441
8043
  e.preventDefault();
7442
8044
  e.stopPropagation();
7443
8045
  },
7444
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
8046
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7445
8047
  }
7446
8048
  ) : null
7447
8049
  ] })
@@ -7458,7 +8060,7 @@ function StateToggle({
7458
8060
  states,
7459
8061
  onStateChange
7460
8062
  }) {
7461
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8063
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7462
8064
  ToggleGroup,
7463
8065
  {
7464
8066
  "data-ohw-state-toggle": "",
@@ -7472,7 +8074,7 @@ function StateToggle({
7472
8074
  left: rect.right - 8,
7473
8075
  transform: "translateX(-100%)"
7474
8076
  },
7475
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8077
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7476
8078
  }
7477
8079
  );
7478
8080
  }
@@ -7499,8 +8101,8 @@ function OhhwellsBridge() {
7499
8101
  const router = (0, import_navigation2.useRouter)();
7500
8102
  const searchParams = (0, import_navigation2.useSearchParams)();
7501
8103
  const isEditMode = isEditSessionActive();
7502
- const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7503
- (0, import_react8.useEffect)(() => {
8104
+ const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
8105
+ (0, import_react9.useEffect)(() => {
7504
8106
  const figtreeFontId = "ohw-figtree-font";
7505
8107
  if (!document.getElementById(figtreeFontId)) {
7506
8108
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7528,91 +8130,101 @@ function OhhwellsBridge() {
7528
8130
  const subdomainFromQuery = searchParams.get("subdomain");
7529
8131
  const subdomain = resolveSubdomain(subdomainFromQuery);
7530
8132
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7531
- const postToParent2 = (0, import_react8.useCallback)((data) => {
8133
+ const postToParent2 = (0, import_react9.useCallback)((data) => {
7532
8134
  if (typeof window !== "undefined" && window.parent !== window) {
7533
8135
  window.parent.postMessage(data, "*");
7534
8136
  }
7535
8137
  }, []);
7536
- const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
7537
- const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
7538
- const activeElRef = (0, import_react8.useRef)(null);
7539
- const selectedElRef = (0, import_react8.useRef)(null);
7540
- const originalContentRef = (0, import_react8.useRef)(null);
7541
- const activeStateElRef = (0, import_react8.useRef)(null);
7542
- const parentScrollRef = (0, import_react8.useRef)(null);
7543
- const visibleViewportRef = (0, import_react8.useRef)(null);
7544
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
7545
- const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
8138
+ const [fetchState, setFetchState] = (0, import_react9.useState)("idle");
8139
+ const autoSaveTimers = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
8140
+ const activeElRef = (0, import_react9.useRef)(null);
8141
+ const selectedElRef = (0, import_react9.useRef)(null);
8142
+ const originalContentRef = (0, import_react9.useRef)(null);
8143
+ const activeStateElRef = (0, import_react9.useRef)(null);
8144
+ const parentScrollRef = (0, import_react9.useRef)(null);
8145
+ const visibleViewportRef = (0, import_react9.useRef)(null);
8146
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react9.useState)(null);
8147
+ const attachVisibleViewport = (0, import_react9.useCallback)((node) => {
7546
8148
  visibleViewportRef.current = node;
7547
8149
  setDialogPortalContainer(node);
7548
8150
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7549
8151
  }, []);
7550
- const toolbarElRef = (0, import_react8.useRef)(null);
7551
- const glowElRef = (0, import_react8.useRef)(null);
7552
- const hoveredImageRef = (0, import_react8.useRef)(null);
7553
- const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
7554
- const dragOverElRef = (0, import_react8.useRef)(null);
7555
- const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7556
- const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
7557
- const hoveredGapRef = (0, import_react8.useRef)(null);
7558
- const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7559
- const imageShowTimerRef = (0, import_react8.useRef)(null);
7560
- const editStylesRef = (0, import_react8.useRef)(null);
7561
- const activateRef = (0, import_react8.useRef)(() => {
8152
+ const toolbarElRef = (0, import_react9.useRef)(null);
8153
+ const glowElRef = (0, import_react9.useRef)(null);
8154
+ const hoveredImageRef = (0, import_react9.useRef)(null);
8155
+ const hoveredImageHasTextOverlapRef = (0, import_react9.useRef)(false);
8156
+ const dragOverElRef = (0, import_react9.useRef)(null);
8157
+ const [mediaHover, setMediaHover] = (0, import_react9.useState)(null);
8158
+ const [carouselHover, setCarouselHover] = (0, import_react9.useState)(null);
8159
+ const [uploadingRects, setUploadingRects] = (0, import_react9.useState)({});
8160
+ const pendingUploadRectRef = (0, import_react9.useRef)(null);
8161
+ const hoveredGapRef = (0, import_react9.useRef)(null);
8162
+ const imageUnhoverTimerRef = (0, import_react9.useRef)(null);
8163
+ const imageShowTimerRef = (0, import_react9.useRef)(null);
8164
+ const editStylesRef = (0, import_react9.useRef)(null);
8165
+ const activateRef = (0, import_react9.useRef)(() => {
7562
8166
  });
7563
- const deactivateRef = (0, import_react8.useRef)(() => {
8167
+ const deactivateRef = (0, import_react9.useRef)(() => {
7564
8168
  });
7565
- const selectRef = (0, import_react8.useRef)(() => {
8169
+ const selectRef = (0, import_react9.useRef)(() => {
7566
8170
  });
7567
- const selectFrameRef = (0, import_react8.useRef)(() => {
8171
+ const selectFrameRef = (0, import_react9.useRef)(() => {
7568
8172
  });
7569
- const deselectRef = (0, import_react8.useRef)(() => {
8173
+ const deselectRef = (0, import_react9.useRef)(() => {
7570
8174
  });
7571
- const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
8175
+ const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
7572
8176
  });
7573
- const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
8177
+ const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
7574
8178
  });
7575
- const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
8179
+ const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
7576
8180
  });
7577
- const postToParentRef = (0, import_react8.useRef)(postToParent2);
8181
+ const postToParentRef = (0, import_react9.useRef)(postToParent2);
7578
8182
  postToParentRef.current = postToParent2;
7579
- const sectionsLoadedRef = (0, import_react8.useRef)(false);
7580
- const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
7581
- const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
7582
- const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7583
- const toolbarVariantRef = (0, import_react8.useRef)("none");
8183
+ const sectionsLoadedRef = (0, import_react9.useRef)(false);
8184
+ const pendingScheduleConfigRequests = (0, import_react9.useRef)([]);
8185
+ const [toolbarRect, setToolbarRect] = (0, import_react9.useState)(null);
8186
+ const [toolbarVariant, setToolbarVariant] = (0, import_react9.useState)("none");
8187
+ const toolbarVariantRef = (0, import_react9.useRef)("none");
7584
8188
  toolbarVariantRef.current = toolbarVariant;
7585
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7586
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7587
- const [toggleState, setToggleState] = (0, import_react8.useState)(null);
7588
- const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
7589
- const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
7590
- const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
7591
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
7592
- const hoveredNavContainerRef = (0, import_react8.useRef)(null);
7593
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
7594
- const hoveredItemElRef = (0, import_react8.useRef)(null);
7595
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7596
- const siblingHintElRef = (0, import_react8.useRef)(null);
7597
- const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
7598
- const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
7599
- const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7600
- const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7601
- const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
7602
- const editContentRef = (0, import_react8.useRef)({});
7603
- const [sitePages, setSitePages] = (0, import_react8.useState)([]);
7604
- const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
7605
- const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
7606
- const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
7607
- const linkPopoverPanelRef = (0, import_react8.useRef)(null);
7608
- const linkPopoverOpenRef = (0, import_react8.useRef)(false);
7609
- const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
8189
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react9.useState)(null);
8190
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react9.useState)(false);
8191
+ const [toggleState, setToggleState] = (0, import_react9.useState)(null);
8192
+ const [maxBadge, setMaxBadge] = (0, import_react9.useState)(null);
8193
+ const [activeCommands, setActiveCommands] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
8194
+ const [sectionGap, setSectionGap] = (0, import_react9.useState)(null);
8195
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react9.useState)(false);
8196
+ const hoveredNavContainerRef = (0, import_react9.useRef)(null);
8197
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react9.useState)(null);
8198
+ const hoveredItemElRef = (0, import_react9.useRef)(null);
8199
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react9.useState)(null);
8200
+ const siblingHintElRef = (0, import_react9.useRef)(null);
8201
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react9.useState)(null);
8202
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react9.useState)([]);
8203
+ const [isItemDragging, setIsItemDragging] = (0, import_react9.useState)(false);
8204
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react9.useState)(false);
8205
+ const footerDragRef = (0, import_react9.useRef)(null);
8206
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react9.useState)([]);
8207
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react9.useState)(null);
8208
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react9.useState)(null);
8209
+ const footerPointerDragRef = (0, import_react9.useRef)(null);
8210
+ const suppressNextClickRef = (0, import_react9.useRef)(false);
8211
+ const [linkPopover, setLinkPopover] = (0, import_react9.useState)(null);
8212
+ const linkPopoverSessionRef = (0, import_react9.useRef)(null);
8213
+ const addNavAfterAnchorRef = (0, import_react9.useRef)(null);
8214
+ const editContentRef = (0, import_react9.useRef)({});
8215
+ const [sitePages, setSitePages] = (0, import_react9.useState)([]);
8216
+ const [sectionsByPath, setSectionsByPath] = (0, import_react9.useState)({});
8217
+ const sectionsPrefetchGenRef = (0, import_react9.useRef)(0);
8218
+ const setLinkPopoverRef = (0, import_react9.useRef)(setLinkPopover);
8219
+ const linkPopoverPanelRef = (0, import_react9.useRef)(null);
8220
+ const linkPopoverOpenRef = (0, import_react9.useRef)(false);
8221
+ const linkPopoverGraceUntilRef = (0, import_react9.useRef)(0);
7610
8222
  setLinkPopoverRef.current = setLinkPopover;
7611
8223
  linkPopoverSessionRef.current = linkPopover;
7612
8224
  const bumpLinkPopoverGrace = () => {
7613
8225
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7614
8226
  };
7615
- const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
8227
+ const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
7616
8228
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7617
8229
  const gen = ++sectionsPrefetchGenRef.current;
7618
8230
  const paths = pages.map((p) => p.path);
@@ -7631,10 +8243,14 @@ function OhhwellsBridge() {
7631
8243
  );
7632
8244
  });
7633
8245
  }, [isEditMode, pathname]);
7634
- const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
8246
+ const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
7635
8247
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7636
- (0, import_react8.useEffect)(() => {
7637
- if (!linkPopover) return;
8248
+ (0, import_react9.useEffect)(() => {
8249
+ if (!linkPopover) {
8250
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
8251
+ return;
8252
+ }
8253
+ document.documentElement.setAttribute("data-ohw-link-popover-open", "");
7638
8254
  if (hoveredImageRef.current) {
7639
8255
  hoveredImageRef.current = null;
7640
8256
  hoveredImageHasTextOverlapRef.current = false;
@@ -7661,14 +8277,10 @@ function OhhwellsBridge() {
7661
8277
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7662
8278
  postToParent2({ type: "ow:image-unhover" });
7663
8279
  return () => {
7664
- postToParent2({ type: "ow:link-modal-lock", locked: false });
7665
- html.style.overflow = prevHtmlOverflow;
7666
- body.style.overflow = prevBodyOverflow;
7667
- document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
7668
- document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
8280
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
7669
8281
  };
7670
8282
  }, [linkPopover, postToParent2]);
7671
- (0, import_react8.useEffect)(() => {
8283
+ (0, import_react9.useEffect)(() => {
7672
8284
  if (!isEditMode) return;
7673
8285
  const useFixtures = shouldUseDevFixtures();
7674
8286
  if (useFixtures) {
@@ -7692,14 +8304,14 @@ function OhhwellsBridge() {
7692
8304
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
7693
8305
  return () => window.removeEventListener("message", onSitePages);
7694
8306
  }, [isEditMode, postToParent2]);
7695
- (0, import_react8.useEffect)(() => {
8307
+ (0, import_react9.useEffect)(() => {
7696
8308
  if (!isEditMode || shouldUseDevFixtures()) return;
7697
8309
  void loadAllSectionsManifest().then((manifest) => {
7698
8310
  if (Object.keys(manifest).length === 0) return;
7699
8311
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7700
8312
  });
7701
8313
  }, [isEditMode]);
7702
- (0, import_react8.useEffect)(() => {
8314
+ (0, import_react9.useEffect)(() => {
7703
8315
  const update = () => {
7704
8316
  const el = activeElRef.current ?? selectedElRef.current;
7705
8317
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7723,10 +8335,10 @@ function OhhwellsBridge() {
7723
8335
  vvp.removeEventListener("resize", update);
7724
8336
  };
7725
8337
  }, []);
7726
- const refreshStateRules = (0, import_react8.useCallback)(() => {
8338
+ const refreshStateRules = (0, import_react9.useCallback)(() => {
7727
8339
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7728
8340
  }, []);
7729
- const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
8341
+ const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
7730
8342
  const tracker = getSectionsTracker();
7731
8343
  let entries = [];
7732
8344
  try {
@@ -7749,7 +8361,7 @@ function OhhwellsBridge() {
7749
8361
  }
7750
8362
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7751
8363
  }, [isEditMode]);
7752
- const deactivate = (0, import_react8.useCallback)(() => {
8364
+ const deactivate = (0, import_react9.useCallback)(() => {
7753
8365
  const el = activeElRef.current;
7754
8366
  if (!el) return;
7755
8367
  const key = el.dataset.ohwKey;
@@ -7780,12 +8392,14 @@ function OhhwellsBridge() {
7780
8392
  setToolbarShowEditLink(false);
7781
8393
  postToParent2({ type: "ow:exit-edit" });
7782
8394
  }, [postToParent2]);
7783
- const deselect = (0, import_react8.useCallback)(() => {
8395
+ const deselect = (0, import_react9.useCallback)(() => {
7784
8396
  selectedElRef.current = null;
7785
8397
  setReorderHrefKey(null);
7786
8398
  setReorderDragDisabled(false);
8399
+ setIsFooterFrameSelection(false);
7787
8400
  siblingHintElRef.current = null;
7788
8401
  setSiblingHintRect(null);
8402
+ setSiblingHintRects([]);
7789
8403
  setIsItemDragging(false);
7790
8404
  hoveredNavContainerRef.current = null;
7791
8405
  setHoveredNavContainerRect(null);
@@ -7794,7 +8408,7 @@ function OhhwellsBridge() {
7794
8408
  setToolbarVariant("none");
7795
8409
  }
7796
8410
  }, []);
7797
- const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
8411
+ const reselectNavigationItem = (0, import_react9.useCallback)((navAnchor) => {
7798
8412
  selectedElRef.current = navAnchor;
7799
8413
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
7800
8414
  setReorderHrefKey(key);
@@ -7804,7 +8418,7 @@ function OhhwellsBridge() {
7804
8418
  setToolbarShowEditLink(false);
7805
8419
  setActiveCommands(/* @__PURE__ */ new Set());
7806
8420
  }, []);
7807
- const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
8421
+ const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
7808
8422
  const el = activeElRef.current;
7809
8423
  if (!el) return;
7810
8424
  const key = el.dataset.ohwKey;
@@ -7830,7 +8444,7 @@ function OhhwellsBridge() {
7830
8444
  postToParent2({ type: "ow:exit-edit" });
7831
8445
  reselectNavigationItem(navAnchor);
7832
8446
  }, [postToParent2, reselectNavigationItem]);
7833
- const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
8447
+ const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
7834
8448
  const items = listNavbarItems();
7835
8449
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7836
8450
  deselectRef.current();
@@ -7842,96 +8456,393 @@ function OhhwellsBridge() {
7842
8456
  intent: "add-nav"
7843
8457
  });
7844
8458
  }, []);
7845
- const handleItemDragStart = (0, import_react8.useCallback)(() => {
7846
- siblingHintElRef.current = null;
7847
- setSiblingHintRect(null);
7848
- setIsItemDragging(true);
7849
- }, []);
7850
- const handleItemDragEnd = (0, import_react8.useCallback)(() => {
8459
+ const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
8460
+ footerDragRef.current = null;
8461
+ setSiblingHintRects([]);
8462
+ setFooterDropSlots([]);
8463
+ setActiveFooterDropIndex(null);
8464
+ setDraggedItemRect(null);
7851
8465
  setIsItemDragging(false);
8466
+ unlockFooterDragInteraction();
7852
8467
  }, []);
7853
- reselectNavigationItemRef.current = reselectNavigationItem;
7854
- commitNavigationTextEditRef.current = commitNavigationTextEdit;
7855
- const select = (0, import_react8.useCallback)((anchor) => {
7856
- if (!isNavigationItem(anchor)) return;
7857
- if (activeElRef.current) deactivate();
7858
- selectedElRef.current = anchor;
7859
- clearHrefKeyHover(anchor);
7860
- hoveredNavContainerRef.current = null;
7861
- setHoveredNavContainerRect(null);
7862
- setHoveredItemRect(null);
7863
- hoveredItemElRef.current = null;
7864
- siblingHintElRef.current = null;
7865
- setSiblingHintRect(null);
7866
- setIsItemDragging(false);
7867
- const { key, disabled } = getNavigationItemReorderState(anchor);
7868
- setReorderHrefKey(key);
7869
- setReorderDragDisabled(disabled);
7870
- setToolbarVariant("link-action");
7871
- setToolbarRect(anchor.getBoundingClientRect());
7872
- setToolbarShowEditLink(false);
7873
- setActiveCommands(/* @__PURE__ */ new Set());
7874
- }, [deactivate]);
7875
- const selectFrame = (0, import_react8.useCallback)((el) => {
7876
- if (!isNavigationContainer(el)) return;
7877
- if (activeElRef.current) deactivate();
7878
- selectedElRef.current = el;
7879
- clearHrefKeyHover(el);
7880
- hoveredNavContainerRef.current = null;
7881
- setHoveredNavContainerRect(null);
7882
- setHoveredItemRect(null);
7883
- hoveredItemElRef.current = null;
7884
- siblingHintElRef.current = null;
7885
- setSiblingHintRect(null);
7886
- setIsItemDragging(false);
7887
- setReorderHrefKey(null);
7888
- setReorderDragDisabled(false);
7889
- setToolbarVariant("select-frame");
7890
- setToolbarRect(el.getBoundingClientRect());
7891
- setToolbarShowEditLink(false);
7892
- setActiveCommands(/* @__PURE__ */ new Set());
7893
- }, [deactivate]);
7894
- const activate = (0, import_react8.useCallback)((el, options) => {
7895
- if (activeElRef.current === el) return;
7896
- selectedElRef.current = null;
7897
- deactivate();
7898
- if (hoveredImageRef.current) {
7899
- hoveredImageRef.current = null;
7900
- setMediaHover(null);
8468
+ const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
8469
+ const dragged = session.draggedEl;
8470
+ setDraggedItemRect(dragged.getBoundingClientRect());
8471
+ if (typeof clientX === "number" && typeof clientY === "number") {
8472
+ session.lastClientX = clientX;
8473
+ session.lastClientY = clientY;
7901
8474
  }
7902
- setToolbarVariant("rich-text");
7903
- siblingHintElRef.current = null;
7904
- setSiblingHintRect(null);
7905
- setIsItemDragging(false);
7906
- el.setAttribute("contenteditable", "true");
7907
- el.removeAttribute("data-ohw-hovered");
7908
- el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
7909
- activeElRef.current = el;
7910
- originalContentRef.current = el.innerHTML;
7911
- el.focus();
7912
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7913
- placeCaretAtPoint(el, options.caretX, options.caretY);
8475
+ session.activeSlot = activeSlot;
8476
+ if (session.kind === "link") {
8477
+ const columns2 = listFooterColumns();
8478
+ const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
8479
+ if (activeSlot) focusCols.add(activeSlot.columnIndex);
8480
+ const siblingRects = [];
8481
+ columns2.forEach((col, i) => {
8482
+ if (!focusCols.has(i)) return;
8483
+ for (const link of listFooterLinksInColumn(col)) {
8484
+ if (link === dragged) continue;
8485
+ siblingRects.push(link.getBoundingClientRect());
8486
+ }
8487
+ });
8488
+ setSiblingHintRects(siblingRects);
8489
+ const slots2 = [];
8490
+ columns2.forEach((col, i) => {
8491
+ slots2.push(...buildLinkDropSlots(col, i));
8492
+ });
8493
+ setFooterDropSlots(slots2);
8494
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
8495
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
8496
+ return;
8497
+ }
8498
+ const columns = listFooterColumns();
8499
+ setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
8500
+ const slots = buildColumnDropSlots();
8501
+ setFooterDropSlots(slots);
8502
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
8503
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
8504
+ }, []);
8505
+ const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
8506
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
8507
+ const commitFooterDragRef = (0, import_react9.useRef)(() => {
8508
+ });
8509
+ const beginFooterDragRef = (0, import_react9.useRef)(() => {
8510
+ });
8511
+ const beginFooterDrag = (0, import_react9.useCallback)(
8512
+ (session) => {
8513
+ const rect = session.draggedEl.getBoundingClientRect();
8514
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
8515
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
8516
+ session.activeSlot = session.activeSlot ?? null;
8517
+ footerDragRef.current = session;
8518
+ setIsItemDragging(true);
8519
+ lockFooterDuringDrag();
8520
+ siblingHintElRef.current = null;
8521
+ setSiblingHintRect(null);
8522
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
8523
+ setToolbarRect(rect);
8524
+ }
8525
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
8526
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
8527
+ },
8528
+ [refreshFooterDragVisuals]
8529
+ );
8530
+ beginFooterDragRef.current = beginFooterDrag;
8531
+ const commitFooterDrag = (0, import_react9.useCallback)(
8532
+ (clientX, clientY) => {
8533
+ const session = footerDragRef.current;
8534
+ if (!session) {
8535
+ clearFooterDragVisuals();
8536
+ return;
8537
+ }
8538
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
8539
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
8540
+ let nextOrder = null;
8541
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
8542
+ if (session.kind === "link" && session.hrefKey && slot) {
8543
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
8544
+ } else if (session.kind === "column" && slot) {
8545
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
8546
+ }
8547
+ const wasSelected = session.wasSelected;
8548
+ const draggedEl = session.draggedEl;
8549
+ const hrefKey = session.hrefKey;
8550
+ let movedColumnIndex = null;
8551
+ if (session.kind === "column" && slot && nextOrder) {
8552
+ let adjusted = slot.insertIndex;
8553
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
8554
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
8555
+ }
8556
+ clearFooterDragVisuals();
8557
+ const applySelectionAfterDrop = () => {
8558
+ if (!wasSelected) {
8559
+ deselectRef.current();
8560
+ return;
8561
+ }
8562
+ if (hrefKey) {
8563
+ const link = document.querySelector(
8564
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
8565
+ );
8566
+ if (link && isNavigationItem(link)) {
8567
+ selectRef.current(link);
8568
+ return;
8569
+ }
8570
+ }
8571
+ if (movedColumnIndex !== null && nextOrder) {
8572
+ const colKeys = nextOrder[movedColumnIndex] ?? [];
8573
+ let column = null;
8574
+ for (const key of colKeys) {
8575
+ const link = document.querySelector(
8576
+ `footer [data-ohw-href-key="${CSS.escape(key)}"]`
8577
+ );
8578
+ if (link) {
8579
+ column = findFooterColumnForLink(link);
8580
+ if (column) break;
8581
+ }
8582
+ }
8583
+ if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
8584
+ if (column && isNavigationContainer(column)) {
8585
+ selectFrameRef.current(column);
8586
+ return;
8587
+ }
8588
+ }
8589
+ if (document.body.contains(draggedEl)) {
8590
+ if (isNavigationItem(draggedEl)) {
8591
+ selectRef.current(draggedEl);
8592
+ return;
8593
+ }
8594
+ if (isNavigationContainer(draggedEl)) {
8595
+ selectFrameRef.current(draggedEl);
8596
+ return;
8597
+ }
8598
+ }
8599
+ deselectRef.current();
8600
+ };
8601
+ if (!wasSelected) {
8602
+ deselectRef.current();
8603
+ }
8604
+ if (nextOrder) {
8605
+ const orderJson = JSON.stringify(nextOrder);
8606
+ editContentRef.current = {
8607
+ ...editContentRef.current,
8608
+ [FOOTER_ORDER_KEY]: orderJson
8609
+ };
8610
+ applyFooterOrder(nextOrder);
8611
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8612
+ postToParentRef.current({
8613
+ type: "ow:change",
8614
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
8615
+ });
8616
+ requestAnimationFrame(() => {
8617
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
8618
+ applyFooterOrder(nextOrder);
8619
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8620
+ }
8621
+ requestAnimationFrame(applySelectionAfterDrop);
8622
+ });
8623
+ return;
8624
+ }
8625
+ applySelectionAfterDrop();
8626
+ },
8627
+ [clearFooterDragVisuals]
8628
+ );
8629
+ commitFooterDragRef.current = commitFooterDrag;
8630
+ const startFooterLinkDrag = (0, import_react9.useCallback)(
8631
+ (anchor, clientX, clientY, wasSelected) => {
8632
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
8633
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
8634
+ const column = findFooterColumnForLink(anchor);
8635
+ const columns = listFooterColumns();
8636
+ beginFooterDrag({
8637
+ kind: "link",
8638
+ hrefKey,
8639
+ columnEl: column,
8640
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
8641
+ wasSelected,
8642
+ draggedEl: anchor,
8643
+ lastClientX: clientX,
8644
+ lastClientY: clientY,
8645
+ activeSlot: null
8646
+ });
8647
+ return true;
8648
+ },
8649
+ [beginFooterDrag]
8650
+ );
8651
+ const startFooterColumnDrag = (0, import_react9.useCallback)(
8652
+ (columnEl, clientX, clientY, wasSelected) => {
8653
+ const columns = listFooterColumns();
8654
+ const idx = columns.indexOf(columnEl);
8655
+ if (idx < 0) return false;
8656
+ beginFooterDrag({
8657
+ kind: "column",
8658
+ hrefKey: null,
8659
+ columnEl,
8660
+ sourceColumnIndex: idx,
8661
+ wasSelected,
8662
+ draggedEl: columnEl,
8663
+ lastClientX: clientX,
8664
+ lastClientY: clientY,
8665
+ activeSlot: null
8666
+ });
8667
+ return true;
8668
+ },
8669
+ [beginFooterDrag]
8670
+ );
8671
+ const handleItemDragStart = (0, import_react9.useCallback)(
8672
+ (e) => {
8673
+ const selected = selectedElRef.current;
8674
+ if (!selected) {
8675
+ setIsItemDragging(true);
8676
+ return;
8677
+ }
8678
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
8679
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
8680
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
8681
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8682
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
8683
+ }
8684
+ siblingHintElRef.current = null;
8685
+ setSiblingHintRect(null);
8686
+ setIsItemDragging(true);
8687
+ },
8688
+ [startFooterColumnDrag, startFooterLinkDrag]
8689
+ );
8690
+ const handleItemDragEnd = (0, import_react9.useCallback)(
8691
+ (e) => {
8692
+ if (footerDragRef.current) {
8693
+ const x = e?.clientX;
8694
+ const y = e?.clientY;
8695
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
8696
+ commitFooterDrag(x, y);
8697
+ } else {
8698
+ commitFooterDrag();
8699
+ }
8700
+ return;
8701
+ }
8702
+ setIsItemDragging(false);
8703
+ },
8704
+ [commitFooterDrag]
8705
+ );
8706
+ const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
8707
+ if (e.button !== 0) return;
8708
+ const selected = selectedElRef.current;
8709
+ if (!selected) return;
8710
+ armFooterPressDrag();
8711
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
8712
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
8713
+ footerPointerDragRef.current = {
8714
+ el: selected,
8715
+ kind: "link",
8716
+ startX: e.clientX,
8717
+ startY: e.clientY,
8718
+ pointerId: e.pointerId,
8719
+ wasSelected: true,
8720
+ started: false
8721
+ };
8722
+ return;
8723
+ }
8724
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8725
+ footerPointerDragRef.current = {
8726
+ el: selected,
8727
+ kind: "column",
8728
+ startX: e.clientX,
8729
+ startY: e.clientY,
8730
+ pointerId: e.pointerId,
8731
+ wasSelected: true,
8732
+ started: false
8733
+ };
8734
+ }
8735
+ }, []);
8736
+ const handleItemChromeClick = (0, import_react9.useCallback)((clientX, clientY) => {
8737
+ if (suppressNextClickRef.current) {
8738
+ suppressNextClickRef.current = false;
8739
+ return;
7914
8740
  }
7915
- setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7916
- const navAnchor = getNavigationItemAnchor(el);
7917
- if (navAnchor) {
8741
+ const selected = selectedElRef.current;
8742
+ if (!selected || !isNavigationItem(selected)) return;
8743
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
8744
+ if (!editable) return;
8745
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
8746
+ }, []);
8747
+ reselectNavigationItemRef.current = reselectNavigationItem;
8748
+ commitNavigationTextEditRef.current = commitNavigationTextEdit;
8749
+ const select = (0, import_react9.useCallback)(
8750
+ (anchor) => {
8751
+ if (!isNavigationItem(anchor)) return;
8752
+ if (activeElRef.current) deactivate();
8753
+ selectedElRef.current = anchor;
8754
+ clearHrefKeyHover(anchor);
8755
+ hoveredNavContainerRef.current = null;
8756
+ setHoveredNavContainerRect(null);
8757
+ setHoveredItemRect(null);
8758
+ hoveredItemElRef.current = null;
8759
+ siblingHintElRef.current = null;
8760
+ setSiblingHintRect(null);
8761
+ setSiblingHintRects([]);
8762
+ setIsItemDragging(false);
8763
+ const { key, disabled } = getNavigationItemReorderState(anchor);
8764
+ setReorderHrefKey(key);
8765
+ setReorderDragDisabled(disabled);
8766
+ setIsFooterFrameSelection(false);
8767
+ setToolbarVariant("link-action");
8768
+ setToolbarRect(anchor.getBoundingClientRect());
8769
+ setToolbarShowEditLink(false);
8770
+ setActiveCommands(/* @__PURE__ */ new Set());
8771
+ },
8772
+ [deactivate]
8773
+ );
8774
+ const selectFrame = (0, import_react9.useCallback)(
8775
+ (el) => {
8776
+ if (!isNavigationContainer(el)) return;
8777
+ if (activeElRef.current) deactivate();
8778
+ const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8779
+ selectedElRef.current = el;
8780
+ clearHrefKeyHover(el);
8781
+ hoveredNavContainerRef.current = null;
8782
+ setHoveredNavContainerRect(null);
8783
+ setHoveredItemRect(null);
8784
+ hoveredItemElRef.current = null;
8785
+ siblingHintElRef.current = null;
8786
+ setSiblingHintRect(null);
8787
+ setSiblingHintRects(
8788
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
8789
+ );
8790
+ setIsItemDragging(false);
7918
8791
  setReorderHrefKey(null);
7919
8792
  setReorderDragDisabled(false);
7920
- } else {
7921
- const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
7922
- setReorderHrefKey(reorderKey);
7923
- setReorderDragDisabled(reorderDisabled);
7924
- }
7925
- setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7926
- postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7927
- requestAnimationFrame(() => refreshActiveCommandsRef.current());
7928
- }, [deactivate, postToParent2]);
8793
+ setIsFooterFrameSelection(isFooterColumn);
8794
+ setToolbarVariant("select-frame");
8795
+ setToolbarRect(el.getBoundingClientRect());
8796
+ setToolbarShowEditLink(false);
8797
+ setActiveCommands(/* @__PURE__ */ new Set());
8798
+ },
8799
+ [deactivate]
8800
+ );
8801
+ const activate = (0, import_react9.useCallback)(
8802
+ (el, options) => {
8803
+ if (activeElRef.current === el) return;
8804
+ selectedElRef.current = null;
8805
+ deactivate();
8806
+ if (hoveredImageRef.current) {
8807
+ hoveredImageRef.current = null;
8808
+ postToParentRef.current({ type: "ow:image-unhover" });
8809
+ }
8810
+ setToolbarVariant("rich-text");
8811
+ siblingHintElRef.current = null;
8812
+ setSiblingHintRect(null);
8813
+ setSiblingHintRects([]);
8814
+ setIsItemDragging(false);
8815
+ el.setAttribute("contenteditable", "true");
8816
+ el.removeAttribute("data-ohw-hovered");
8817
+ el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
8818
+ activeElRef.current = el;
8819
+ originalContentRef.current = el.innerHTML;
8820
+ el.focus();
8821
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8822
+ placeCaretAtPoint(el, options.caretX, options.caretY);
8823
+ }
8824
+ setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
8825
+ const navAnchor = getNavigationItemAnchor(el);
8826
+ if (navAnchor) {
8827
+ setReorderHrefKey(null);
8828
+ setReorderDragDisabled(false);
8829
+ } else {
8830
+ const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
8831
+ setReorderHrefKey(reorderKey);
8832
+ setReorderDragDisabled(reorderDisabled);
8833
+ }
8834
+ setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
8835
+ postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
8836
+ requestAnimationFrame(() => refreshActiveCommandsRef.current());
8837
+ },
8838
+ [deactivate, postToParent2]
8839
+ );
7929
8840
  activateRef.current = activate;
7930
8841
  deactivateRef.current = deactivate;
7931
8842
  selectRef.current = select;
7932
8843
  selectFrameRef.current = selectFrame;
7933
8844
  deselectRef.current = deselect;
7934
- (0, import_react8.useLayoutEffect)(() => {
8845
+ (0, import_react9.useLayoutEffect)(() => {
7935
8846
  if (!subdomain || isEditMode) {
7936
8847
  setFetchState("done");
7937
8848
  return;
@@ -7941,6 +8852,7 @@ function OhhwellsBridge() {
7941
8852
  for (const [key, val] of Object.entries(content)) {
7942
8853
  if (key === "__ohw_sections") continue;
7943
8854
  if (applyVideoSettingNode(key, val)) continue;
8855
+ if (applyCarouselNode(key, val)) continue;
7944
8856
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7945
8857
  if (el.dataset.ohwEditable === "image") {
7946
8858
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7972,6 +8884,7 @@ function OhhwellsBridge() {
7972
8884
  applyLinkByKey(key, val);
7973
8885
  }
7974
8886
  reconcileNavbarItemsFromContent(content);
8887
+ reconcileFooterOrderFromContent(content);
7975
8888
  enforceLinkHrefs();
7976
8889
  initSectionsFromContent(content, true);
7977
8890
  sectionsLoadedRef.current = true;
@@ -8000,7 +8913,7 @@ function OhhwellsBridge() {
8000
8913
  cancelled = true;
8001
8914
  };
8002
8915
  }, [subdomain, isEditMode]);
8003
- (0, import_react8.useEffect)(() => {
8916
+ (0, import_react9.useEffect)(() => {
8004
8917
  if (!subdomain || isEditMode) return;
8005
8918
  let debounceTimer = null;
8006
8919
  let observer = null;
@@ -8013,6 +8926,7 @@ function OhhwellsBridge() {
8013
8926
  for (const [key, val] of Object.entries(content)) {
8014
8927
  if (key === "__ohw_sections") continue;
8015
8928
  if (applyVideoSettingNode(key, val)) continue;
8929
+ if (applyCarouselNode(key, val)) continue;
8016
8930
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8017
8931
  if (el.dataset.ohwEditable === "image") {
8018
8932
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8032,6 +8946,7 @@ function OhhwellsBridge() {
8032
8946
  applyLinkByKey(key, val);
8033
8947
  }
8034
8948
  reconcileNavbarItemsFromContent(content);
8949
+ reconcileFooterOrderFromContent(content);
8035
8950
  } finally {
8036
8951
  observer?.observe(document.body, { childList: true, subtree: true });
8037
8952
  }
@@ -8049,16 +8964,16 @@ function OhhwellsBridge() {
8049
8964
  if (debounceTimer) clearTimeout(debounceTimer);
8050
8965
  };
8051
8966
  }, [subdomain, isEditMode, pathname]);
8052
- (0, import_react8.useLayoutEffect)(() => {
8967
+ (0, import_react9.useLayoutEffect)(() => {
8053
8968
  const el = document.getElementById("ohw-loader");
8054
8969
  if (!el) return;
8055
8970
  const visible = Boolean(subdomain) && fetchState !== "done";
8056
8971
  el.style.display = visible ? "flex" : "none";
8057
8972
  }, [subdomain, fetchState]);
8058
- (0, import_react8.useEffect)(() => {
8973
+ (0, import_react9.useEffect)(() => {
8059
8974
  postToParent2({ type: "ow:navigation", path: pathname });
8060
8975
  }, [pathname, postToParent2]);
8061
- (0, import_react8.useEffect)(() => {
8976
+ (0, import_react9.useEffect)(() => {
8062
8977
  if (!isEditMode) return;
8063
8978
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8064
8979
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -8066,23 +8981,50 @@ function OhhwellsBridge() {
8066
8981
  deselectRef.current();
8067
8982
  deactivateRef.current();
8068
8983
  }, [pathname, isEditMode]);
8069
- (0, import_react8.useEffect)(() => {
8984
+ (0, import_react9.useEffect)(() => {
8070
8985
  const contentForNav = () => {
8071
8986
  if (isEditMode) return editContentRef.current;
8072
8987
  if (!subdomain) return {};
8073
8988
  return contentCache.get(subdomain) ?? {};
8074
8989
  };
8075
- const run = () => reconcileNavbarItemsFromContent(contentForNav());
8990
+ let applying = false;
8991
+ let rafId = null;
8992
+ const run = () => {
8993
+ if (footerDragRef.current || applying) return;
8994
+ applying = true;
8995
+ try {
8996
+ const content = contentForNav();
8997
+ reconcileNavbarItemsFromContent(content);
8998
+ reconcileFooterOrderFromContent(content);
8999
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
9000
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
9001
+ disableNativeHrefDrag(el);
9002
+ }
9003
+ });
9004
+ } finally {
9005
+ applying = false;
9006
+ }
9007
+ };
9008
+ const scheduleRun = () => {
9009
+ if (rafId !== null) cancelAnimationFrame(rafId);
9010
+ rafId = requestAnimationFrame(() => {
9011
+ rafId = null;
9012
+ run();
9013
+ });
9014
+ };
8076
9015
  run();
8077
9016
  const nav = document.querySelector("nav");
8078
- if (!nav) return;
8079
- const observer = new MutationObserver(() => {
8080
- requestAnimationFrame(run);
8081
- });
8082
- observer.observe(nav, { childList: true, subtree: true });
8083
- return () => observer.disconnect();
9017
+ const footer = document.querySelector("footer");
9018
+ const observer = new MutationObserver(scheduleRun);
9019
+ if (nav) observer.observe(nav, { childList: true, subtree: true });
9020
+ if (footer) observer.observe(footer, { childList: true, subtree: true });
9021
+ if (!nav && !footer) return;
9022
+ return () => {
9023
+ observer.disconnect();
9024
+ if (rafId !== null) cancelAnimationFrame(rafId);
9025
+ };
8084
9026
  }, [isEditMode, pathname, subdomain, fetchState]);
8085
- (0, import_react8.useEffect)(() => {
9027
+ (0, import_react9.useEffect)(() => {
8086
9028
  if (!isEditMode) return;
8087
9029
  const measure = () => {
8088
9030
  const h = document.body.scrollHeight;
@@ -8106,7 +9048,7 @@ function OhhwellsBridge() {
8106
9048
  window.removeEventListener("resize", handleResize);
8107
9049
  };
8108
9050
  }, [pathname, isEditMode, postToParent2]);
8109
- (0, import_react8.useEffect)(() => {
9051
+ (0, import_react9.useEffect)(() => {
8110
9052
  if (!subdomainFromQuery || isEditMode) return;
8111
9053
  const handleClick = (e) => {
8112
9054
  const anchor = e.target.closest("a");
@@ -8122,7 +9064,7 @@ function OhhwellsBridge() {
8122
9064
  document.addEventListener("click", handleClick, true);
8123
9065
  return () => document.removeEventListener("click", handleClick, true);
8124
9066
  }, [subdomainFromQuery, isEditMode, router]);
8125
- (0, import_react8.useEffect)(() => {
9067
+ (0, import_react9.useEffect)(() => {
8126
9068
  if (!isEditMode) {
8127
9069
  editStylesRef.current?.base.remove();
8128
9070
  editStylesRef.current?.forceHover.remove();
@@ -8145,6 +9087,32 @@ function OhhwellsBridge() {
8145
9087
  [data-ohw-editable] {
8146
9088
  display: block;
8147
9089
  }
9090
+ /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
9091
+ footer [data-ohw-href-key] {
9092
+ -webkit-user-drag: none !important;
9093
+ }
9094
+ footer [data-ohw-footer-col]:hover {
9095
+ outline: 1.5px dashed ${PRIMARY2} !important;
9096
+ outline-offset: 6px;
9097
+ border-radius: 8px;
9098
+ }
9099
+ html[data-ohw-link-popover-open] footer [data-ohw-footer-col]:hover {
9100
+ outline: none !important;
9101
+ }
9102
+ html[data-ohw-footer-press-drag] footer,
9103
+ html[data-ohw-footer-press-drag] footer * {
9104
+ user-select: none !important;
9105
+ -webkit-user-select: none !important;
9106
+ }
9107
+ html[data-ohw-item-dragging] footer,
9108
+ html[data-ohw-item-dragging] footer * {
9109
+ user-select: none !important;
9110
+ -webkit-user-select: none !important;
9111
+ pointer-events: none !important;
9112
+ }
9113
+ html[data-ohw-item-dragging] {
9114
+ cursor: grabbing !important;
9115
+ }
8148
9116
  [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
8149
9117
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8150
9118
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
@@ -8189,12 +9157,39 @@ function OhhwellsBridge() {
8189
9157
  }
8190
9158
  refreshStateRules();
8191
9159
  const handleClick = (e) => {
9160
+ if (suppressNextClickRef.current) {
9161
+ suppressNextClickRef.current = false;
9162
+ e.preventDefault();
9163
+ e.stopPropagation();
9164
+ return;
9165
+ }
8192
9166
  const target = e.target;
8193
9167
  if (target.closest("[data-ohw-toolbar]")) return;
8194
9168
  if (target.closest("[data-ohw-state-toggle]")) return;
8195
9169
  if (target.closest("[data-ohw-max-badge]")) return;
8196
9170
  if (isInsideLinkEditor(target)) return;
8197
9171
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9172
+ if (target.closest("[data-ohw-item-drag-surface]")) {
9173
+ e.preventDefault();
9174
+ e.stopPropagation();
9175
+ const selected = selectedElRef.current;
9176
+ if (selected && isNavigationItem(selected)) {
9177
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9178
+ if (editable2) {
9179
+ activateRef.current(editable2, {
9180
+ caretX: e.clientX,
9181
+ caretY: e.clientY
9182
+ });
9183
+ }
9184
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
9185
+ const link = listFooterLinksInColumn(selected).find((el) => {
9186
+ const r2 = el.getBoundingClientRect();
9187
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
9188
+ });
9189
+ if (link) selectRef.current(link);
9190
+ }
9191
+ return;
9192
+ }
8198
9193
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8199
9194
  if (navFromChrome) {
8200
9195
  e.preventDefault();
@@ -8224,7 +9219,15 @@ function OhhwellsBridge() {
8224
9219
  if (isMediaEditable(editable)) {
8225
9220
  e.preventDefault();
8226
9221
  e.stopPropagation();
8227
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
9222
+ const key = editable.dataset.ohwKey ?? "";
9223
+ const r2 = editable.getBoundingClientRect();
9224
+ if (key && r2.width > 1 && r2.height > 1) {
9225
+ pendingUploadRectRef.current = {
9226
+ key,
9227
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height }
9228
+ };
9229
+ }
9230
+ postToParentRef.current({ type: "ow:image-pick", key, elementType: editable.dataset.ohwEditable ?? "image" });
8228
9231
  return;
8229
9232
  }
8230
9233
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -8249,10 +9252,26 @@ function OhhwellsBridge() {
8249
9252
  if (hrefAnchor) {
8250
9253
  e.preventDefault();
8251
9254
  e.stopPropagation();
8252
- if (selectedElRef.current === hrefAnchor) return;
9255
+ if (selectedElRef.current === hrefAnchor) {
9256
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
9257
+ if (textEditable) {
9258
+ activateRef.current(textEditable, {
9259
+ caretX: e.clientX,
9260
+ caretY: e.clientY
9261
+ });
9262
+ }
9263
+ return;
9264
+ }
8253
9265
  selectRef.current(hrefAnchor);
8254
9266
  return;
8255
9267
  }
9268
+ const footerColClick = target.closest("[data-ohw-footer-col]");
9269
+ if (footerColClick) {
9270
+ e.preventDefault();
9271
+ e.stopPropagation();
9272
+ selectFrameRef.current(footerColClick);
9273
+ return;
9274
+ }
8256
9275
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8257
9276
  if (navContainerToSelect) {
8258
9277
  e.preventDefault();
@@ -8319,6 +9338,13 @@ function OhhwellsBridge() {
8319
9338
  const handleMouseOver = (e) => {
8320
9339
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8321
9340
  const target = e.target;
9341
+ if (linkPopoverOpenRef.current) {
9342
+ hoveredItemElRef.current = null;
9343
+ setHoveredItemRect(null);
9344
+ hoveredNavContainerRef.current = null;
9345
+ setHoveredNavContainerRect(null);
9346
+ return;
9347
+ }
8322
9348
  if (toolbarVariantRef.current !== "select-frame") {
8323
9349
  const navContainer = target.closest("[data-ohw-nav-container]");
8324
9350
  if (navContainer && !getNavigationItemAnchor(target)) {
@@ -8333,6 +9359,15 @@ function OhhwellsBridge() {
8333
9359
  setHoveredNavContainerRect(null);
8334
9360
  }
8335
9361
  }
9362
+ const footerCol = target.closest("[data-ohw-footer-col]");
9363
+ if (footerCol) {
9364
+ hoveredNavContainerRef.current = null;
9365
+ setHoveredNavContainerRect(null);
9366
+ if (selectedElRef.current === footerCol) return;
9367
+ hoveredItemElRef.current = footerCol;
9368
+ setHoveredItemRect(footerCol.getBoundingClientRect());
9369
+ return;
9370
+ }
8336
9371
  const navAnchor = getNavigationItemAnchor(target);
8337
9372
  if (navAnchor) {
8338
9373
  hoveredNavContainerRef.current = null;
@@ -8369,6 +9404,15 @@ function OhhwellsBridge() {
8369
9404
  hoveredNavContainerRef.current = null;
8370
9405
  setHoveredNavContainerRect(null);
8371
9406
  }
9407
+ const footerCol = target.closest("[data-ohw-footer-col]");
9408
+ if (footerCol && hoveredItemElRef.current === footerCol) {
9409
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9410
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
9411
+ if (related2?.closest("[data-ohw-footer-col]") === footerCol) return;
9412
+ hoveredItemElRef.current = null;
9413
+ setHoveredItemRect(null);
9414
+ return;
9415
+ }
8372
9416
  const navAnchor = getNavigationItemAnchor(target);
8373
9417
  if (navAnchor) {
8374
9418
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8480,12 +9524,12 @@ function OhhwellsBridge() {
8480
9524
  const stickyPad = 48;
8481
9525
  const withinPad = x >= pr.left - stickyPad && x <= pr.left + pr.width + stickyPad && y >= pr.top - stickyPad && y <= pr.top + pr.height + stickyPad;
8482
9526
  if (withinPad) {
8483
- if (smallest !== pinned && candidatePool.includes(smallest)) {
9527
+ const yieldToSmaller = smallest !== pinned && candidatePool.includes(smallest) && (() => {
8484
9528
  const sr = getVisibleRect(smallest);
8485
- const overOther = x >= sr.left && x <= sr.left + sr.width && y >= sr.top && y <= sr.top + sr.height;
8486
- if (overOther) return smallest;
8487
- }
8488
- return pinned;
9529
+ const pr2 = getVisibleRect(pinned);
9530
+ return sr.width * sr.height < pr2.width * pr2.height;
9531
+ })();
9532
+ if (!yieldToSmaller) return pinned;
8489
9533
  }
8490
9534
  }
8491
9535
  return smallest;
@@ -8497,6 +9541,7 @@ function OhhwellsBridge() {
8497
9541
  resumeAnimTracks();
8498
9542
  postToParentRef.current({ type: "ow:image-unhover" });
8499
9543
  }
9544
+ clearImageHover();
8500
9545
  };
8501
9546
  const probeNavigationHoverAt = (x, y) => {
8502
9547
  if (toolbarVariantRef.current === "select-frame") {
@@ -8599,7 +9644,7 @@ function OhhwellsBridge() {
8599
9644
  return;
8600
9645
  }
8601
9646
  const topEl = document.elementFromPoint(x2, y2);
8602
- if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9647
+ 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]')) {
8603
9648
  if (hoveredImageRef.current) {
8604
9649
  hoveredImageRef.current = null;
8605
9650
  resumeAnimTracks();
@@ -8806,6 +9851,14 @@ function OhhwellsBridge() {
8806
9851
  probeHoverCardsAt(clientX, clientY);
8807
9852
  };
8808
9853
  const handleDragOver = (e) => {
9854
+ const footerSession = footerDragRef.current;
9855
+ if (footerSession) {
9856
+ e.preventDefault();
9857
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
9858
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
9859
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
9860
+ return;
9861
+ }
8809
9862
  e.preventDefault();
8810
9863
  const el = findImageAtPoint(e.clientX, e.clientY, false);
8811
9864
  if (!el) {
@@ -8874,6 +9927,7 @@ function OhhwellsBridge() {
8874
9927
  if (!entry || entry.fadingOut) return prev;
8875
9928
  return { ...prev, [key]: { ...entry, fadingOut: true } };
8876
9929
  });
9930
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
8877
9931
  };
8878
9932
  let found = false;
8879
9933
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -8988,6 +10042,7 @@ function OhhwellsBridge() {
8988
10042
  continue;
8989
10043
  }
8990
10044
  if (applyVideoSettingNode(key, val)) continue;
10045
+ if (applyCarouselNode(key, val)) continue;
8991
10046
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8992
10047
  if (el.dataset.ohwEditable === "image") {
8993
10048
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -9012,6 +10067,7 @@ function OhhwellsBridge() {
9012
10067
  }
9013
10068
  editContentRef.current = { ...editContentRef.current, ...content };
9014
10069
  reconcileNavbarItemsFromContent(editContentRef.current);
10070
+ reconcileFooterOrderFromContent(editContentRef.current);
9015
10071
  enforceLinkHrefs();
9016
10072
  postToParentRef.current({ type: "ow:hydrate-done" });
9017
10073
  };
@@ -9125,6 +10181,17 @@ function OhhwellsBridge() {
9125
10181
  if (siblingHintElRef.current) {
9126
10182
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9127
10183
  }
10184
+ const selected = selectedElRef.current;
10185
+ if (selected && !footerDragRef.current && (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)))) {
10186
+ setSiblingHintRects(
10187
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
10188
+ );
10189
+ }
10190
+ if (footerDragRef.current) {
10191
+ const session = footerDragRef.current;
10192
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
10193
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
10194
+ }
9128
10195
  if (hoveredImageRef.current) {
9129
10196
  const el = hoveredImageRef.current;
9130
10197
  const r2 = el.getBoundingClientRect();
@@ -9135,7 +10202,7 @@ function OhhwellsBridge() {
9135
10202
  };
9136
10203
  const handleSave = (e) => {
9137
10204
  if (e.data?.type !== "ow:save") return;
9138
- const nodes = collectEditableNodes();
10205
+ const nodes = collectEditableNodes(editContentRef.current);
9139
10206
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9140
10207
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9141
10208
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9220,28 +10287,49 @@ function OhhwellsBridge() {
9220
10287
  const handleDocMouseLeave = () => {
9221
10288
  hoveredImageRef.current = null;
9222
10289
  setMediaHover(null);
10290
+ setCarouselHover(null);
9223
10291
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
9224
10292
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
9225
10293
  activeStateElRef.current = null;
9226
10294
  setToggleState(null);
9227
10295
  };
10296
+ const resolveUploadRects = (key) => {
10297
+ const matches = Array.from(document.querySelectorAll(`[data-ohw-key="${key}"]`));
10298
+ const rects = matches.map((el) => {
10299
+ const r2 = el.getBoundingClientRect();
10300
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
10301
+ }).filter((r2) => r2.width > 1 && r2.height > 1);
10302
+ if (rects.length > 0) return rects;
10303
+ const pending = pendingUploadRectRef.current;
10304
+ if (pending?.key === key && pending.rect.width > 1 && pending.rect.height > 1) {
10305
+ return [pending.rect];
10306
+ }
10307
+ const hovered = hoveredImageRef.current;
10308
+ if (hovered?.dataset.ohwKey === key) {
10309
+ const r2 = hovered.getBoundingClientRect();
10310
+ if (r2.width > 1 && r2.height > 1) {
10311
+ return [{ top: r2.top, left: r2.left, width: r2.width, height: r2.height }];
10312
+ }
10313
+ }
10314
+ return [];
10315
+ };
9228
10316
  const handleImageUploading = (e) => {
9229
- if (e.data?.type !== "ow:image-uploading") return;
9230
- const { key, uploading } = e.data;
10317
+ const type = e.data?.type;
10318
+ if (type !== "ow:image-uploading" && type !== "ow:anim-lock") return;
10319
+ const key = e.data.key;
10320
+ if (!key) return;
10321
+ const uploading = type === "ow:anim-lock" ? true : !!e.data.uploading;
9231
10322
  setUploadingRects((prev) => {
9232
10323
  if (!uploading) {
10324
+ if (pendingUploadRectRef.current?.key === key) pendingUploadRectRef.current = null;
9233
10325
  if (!(key in prev)) return prev;
9234
10326
  const next = { ...prev };
9235
10327
  delete next[key];
9236
10328
  return next;
9237
10329
  }
9238
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
9239
- if (!el) return prev;
9240
- const r2 = getVisibleRect(el);
9241
- return {
9242
- ...prev,
9243
- [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
9244
- };
10330
+ const rects = resolveUploadRects(key);
10331
+ if (rects.length === 0) return prev;
10332
+ return { ...prev, [key]: { rects } };
9245
10333
  });
9246
10334
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9247
10335
  const track = el.closest("[data-ohw-hover-pause]");
@@ -9259,6 +10347,48 @@ function OhhwellsBridge() {
9259
10347
  if (e.data?.type !== "ow:canvas-height" || typeof e.data.height !== "number") return;
9260
10348
  document.documentElement.style.setProperty("--ohw-canvas-h", `${e.data.height}px`);
9261
10349
  };
10350
+ const handleCarouselChange = (e) => {
10351
+ if (e.data?.type !== "ow:carousel-change") return;
10352
+ const { key, images } = e.data;
10353
+ if (!key || !Array.isArray(images)) return;
10354
+ const text = JSON.stringify(images);
10355
+ applyCarouselValue(key, images);
10356
+ editContentRef.current = { ...editContentRef.current, [key]: text };
10357
+ postToParentRef.current({ type: "ow:change", nodes: [{ key, text }] });
10358
+ setCarouselHover(null);
10359
+ };
10360
+ const findCarouselAtPoint = (clientX, clientY) => {
10361
+ const containers = Array.from(document.querySelectorAll(`[${CAROUSEL_ATTR}]`));
10362
+ for (let i = containers.length - 1; i >= 0; i--) {
10363
+ const r2 = containers[i].getBoundingClientRect();
10364
+ if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) {
10365
+ return containers[i];
10366
+ }
10367
+ }
10368
+ return null;
10369
+ };
10370
+ const isPointOverEditable = (scope, clientX, clientY) => {
10371
+ const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
10372
+ for (const el of editables) {
10373
+ const r2 = el.getBoundingClientRect();
10374
+ if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
10375
+ }
10376
+ return false;
10377
+ };
10378
+ const handleCarouselHover = (e) => {
10379
+ const container = findCarouselAtPoint(e.clientX, e.clientY);
10380
+ const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
10381
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
10382
+ setCarouselHover((prev) => prev ? null : prev);
10383
+ return;
10384
+ }
10385
+ const key = container.getAttribute("data-ohw-key") ?? "";
10386
+ if (!key) return;
10387
+ const r2 = getVisibleRect(container);
10388
+ setCarouselHover(
10389
+ (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 } }
10390
+ );
10391
+ };
9262
10392
  const applyToolbarPos = (rect) => {
9263
10393
  const ps = parentScrollRef.current;
9264
10394
  const approxW = 330;
@@ -9367,6 +10497,7 @@ function OhhwellsBridge() {
9367
10497
  window.addEventListener("message", handleRemoveSchedulingSection);
9368
10498
  window.addEventListener("message", handleImageUrl);
9369
10499
  window.addEventListener("message", handleImageUploading);
10500
+ window.addEventListener("message", handleCarouselChange);
9370
10501
  window.addEventListener("message", handleCanvasHeight);
9371
10502
  window.addEventListener("message", handleParentScroll);
9372
10503
  window.addEventListener("message", handlePointerSync);
@@ -9384,6 +10515,7 @@ function OhhwellsBridge() {
9384
10515
  document.addEventListener("mouseover", handleMouseOver, true);
9385
10516
  document.addEventListener("mouseout", handleMouseOut, true);
9386
10517
  document.addEventListener("mousemove", handleMouseMove, true);
10518
+ document.addEventListener("mousemove", handleCarouselHover, true);
9387
10519
  document.addEventListener("mouseleave", handleDocMouseLeave);
9388
10520
  document.addEventListener("dragover", handleDragOver, true);
9389
10521
  document.addEventListener("dragleave", handleDragLeave, true);
@@ -9399,6 +10531,7 @@ function OhhwellsBridge() {
9399
10531
  document.removeEventListener("mouseover", handleMouseOver, true);
9400
10532
  document.removeEventListener("mouseout", handleMouseOut, true);
9401
10533
  document.removeEventListener("mousemove", handleMouseMove, true);
10534
+ document.removeEventListener("mousemove", handleCarouselHover, true);
9402
10535
  document.removeEventListener("mouseleave", handleDocMouseLeave);
9403
10536
  document.removeEventListener("dragover", handleDragOver, true);
9404
10537
  document.removeEventListener("dragleave", handleDragLeave, true);
@@ -9414,6 +10547,7 @@ function OhhwellsBridge() {
9414
10547
  window.removeEventListener("message", handleRemoveSchedulingSection);
9415
10548
  window.removeEventListener("message", handleImageUrl);
9416
10549
  window.removeEventListener("message", handleImageUploading);
10550
+ window.removeEventListener("message", handleCarouselChange);
9417
10551
  window.removeEventListener("message", handleCanvasHeight);
9418
10552
  window.removeEventListener("message", handleParentScroll);
9419
10553
  window.removeEventListener("resize", handleViewportResize);
@@ -9428,7 +10562,158 @@ function OhhwellsBridge() {
9428
10562
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9429
10563
  };
9430
10564
  }, [isEditMode, refreshStateRules]);
9431
- (0, import_react8.useEffect)(() => {
10565
+ (0, import_react9.useEffect)(() => {
10566
+ if (!isEditMode) return;
10567
+ const THRESHOLD = 10;
10568
+ const resolveWasSelected = (el) => {
10569
+ if (selectedElRef.current === el) return true;
10570
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
10571
+ return activeAnchor === el;
10572
+ };
10573
+ const onPointerDown = (e) => {
10574
+ if (e.button !== 0) return;
10575
+ if (suppressNextClickRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
10576
+ suppressNextClickRef.current = false;
10577
+ }
10578
+ if (footerDragRef.current) return;
10579
+ const target = e.target;
10580
+ if (!target) return;
10581
+ 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]')) {
10582
+ return;
10583
+ }
10584
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
10585
+ const anchor = getNavigationItemAnchor(target);
10586
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
10587
+ if (anchor && isFooterHrefKey(hrefKey)) {
10588
+ armFooterPressDrag();
10589
+ footerPointerDragRef.current = {
10590
+ el: anchor,
10591
+ kind: "link",
10592
+ startX: e.clientX,
10593
+ startY: e.clientY,
10594
+ pointerId: e.pointerId,
10595
+ wasSelected: resolveWasSelected(anchor),
10596
+ started: false
10597
+ };
10598
+ return;
10599
+ }
10600
+ const col = target.closest("[data-ohw-footer-col]");
10601
+ if (col && !getNavigationItemAnchor(target)) {
10602
+ armFooterPressDrag();
10603
+ footerPointerDragRef.current = {
10604
+ el: col,
10605
+ kind: "column",
10606
+ startX: e.clientX,
10607
+ startY: e.clientY,
10608
+ pointerId: e.pointerId,
10609
+ wasSelected: resolveWasSelected(col),
10610
+ started: false
10611
+ };
10612
+ }
10613
+ };
10614
+ const onPointerMove = (e) => {
10615
+ const pending = footerPointerDragRef.current;
10616
+ if (!pending) return;
10617
+ if (pending.started) {
10618
+ e.preventDefault();
10619
+ clearTextSelection();
10620
+ const session = footerDragRef.current;
10621
+ if (!session) return;
10622
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10623
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
10624
+ return;
10625
+ }
10626
+ const dx = e.clientX - pending.startX;
10627
+ const dy = e.clientY - pending.startY;
10628
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
10629
+ e.preventDefault();
10630
+ pending.started = true;
10631
+ clearTextSelection();
10632
+ try {
10633
+ document.body.setPointerCapture(pending.pointerId);
10634
+ } catch {
10635
+ }
10636
+ if (linkPopoverOpenRef.current) {
10637
+ setLinkPopoverRef.current(null);
10638
+ }
10639
+ if (activeElRef.current) {
10640
+ deactivateRef.current();
10641
+ }
10642
+ if (pending.kind === "link") {
10643
+ const key = pending.el.getAttribute("data-ohw-href-key");
10644
+ if (!key) return;
10645
+ const column = findFooterColumnForLink(pending.el);
10646
+ const columns2 = listFooterColumns();
10647
+ beginFooterDragRef.current({
10648
+ kind: "link",
10649
+ hrefKey: key,
10650
+ columnEl: column,
10651
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
10652
+ wasSelected: pending.wasSelected,
10653
+ draggedEl: pending.el,
10654
+ lastClientX: e.clientX,
10655
+ lastClientY: e.clientY,
10656
+ activeSlot: null
10657
+ });
10658
+ return;
10659
+ }
10660
+ const columns = listFooterColumns();
10661
+ const idx = columns.indexOf(pending.el);
10662
+ if (idx < 0) return;
10663
+ beginFooterDragRef.current({
10664
+ kind: "column",
10665
+ hrefKey: null,
10666
+ columnEl: pending.el,
10667
+ sourceColumnIndex: idx,
10668
+ wasSelected: pending.wasSelected,
10669
+ draggedEl: pending.el,
10670
+ lastClientX: e.clientX,
10671
+ lastClientY: e.clientY,
10672
+ activeSlot: null
10673
+ });
10674
+ };
10675
+ const endPointerDrag = (e) => {
10676
+ const pending = footerPointerDragRef.current;
10677
+ footerPointerDragRef.current = null;
10678
+ try {
10679
+ if (document.body.hasPointerCapture(e.pointerId)) {
10680
+ document.body.releasePointerCapture(e.pointerId);
10681
+ }
10682
+ } catch {
10683
+ }
10684
+ if (!pending?.started) {
10685
+ unlockFooterDragInteraction();
10686
+ return;
10687
+ }
10688
+ suppressNextClickRef.current = true;
10689
+ commitFooterDragRef.current(e.clientX, e.clientY);
10690
+ };
10691
+ const blockSelectStart = (e) => {
10692
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
10693
+ e.preventDefault();
10694
+ }
10695
+ };
10696
+ const onDragEnd = (_e) => {
10697
+ if (!footerDragRef.current) return;
10698
+ commitFooterDragRef.current();
10699
+ };
10700
+ document.addEventListener("pointerdown", onPointerDown, true);
10701
+ document.addEventListener("pointermove", onPointerMove, true);
10702
+ document.addEventListener("pointerup", endPointerDrag, true);
10703
+ document.addEventListener("pointercancel", endPointerDrag, true);
10704
+ document.addEventListener("selectstart", blockSelectStart, true);
10705
+ document.addEventListener("dragend", onDragEnd, true);
10706
+ return () => {
10707
+ document.removeEventListener("pointerdown", onPointerDown, true);
10708
+ document.removeEventListener("pointermove", onPointerMove, true);
10709
+ document.removeEventListener("pointerup", endPointerDrag, true);
10710
+ document.removeEventListener("pointercancel", endPointerDrag, true);
10711
+ document.removeEventListener("selectstart", blockSelectStart, true);
10712
+ document.removeEventListener("dragend", onDragEnd, true);
10713
+ unlockFooterDragInteraction();
10714
+ };
10715
+ }, [isEditMode]);
10716
+ (0, import_react9.useEffect)(() => {
9432
10717
  const handler = (e) => {
9433
10718
  if (e.data?.type !== "ow:request-schedule-config") return;
9434
10719
  const insertAfterVal = e.data.insertAfter;
@@ -9444,7 +10729,7 @@ function OhhwellsBridge() {
9444
10729
  window.addEventListener("message", handler);
9445
10730
  return () => window.removeEventListener("message", handler);
9446
10731
  }, [processConfigRequest]);
9447
- (0, import_react8.useEffect)(() => {
10732
+ (0, import_react9.useEffect)(() => {
9448
10733
  if (!isEditMode) return;
9449
10734
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9450
10735
  el.removeAttribute("data-ohw-active-state");
@@ -9465,7 +10750,13 @@ function OhhwellsBridge() {
9465
10750
  const next = { ...prev, [pathKey]: sections };
9466
10751
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9467
10752
  });
9468
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
10753
+ postToParent2({
10754
+ type: "ow:ready",
10755
+ version: "1",
10756
+ path: pathname,
10757
+ nodes: collectEditableNodes(editContentRef.current),
10758
+ sections
10759
+ });
9469
10760
  postToParent2({ type: "ow:request-site-pages" });
9470
10761
  }, 150);
9471
10762
  return () => {
@@ -9473,19 +10764,19 @@ function OhhwellsBridge() {
9473
10764
  clearTimeout(timer);
9474
10765
  };
9475
10766
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9476
- (0, import_react8.useEffect)(() => {
10767
+ (0, import_react9.useEffect)(() => {
9477
10768
  scrollToHashSectionWhenReady();
9478
10769
  const onHashChange = () => scrollToHashSectionWhenReady();
9479
10770
  window.addEventListener("hashchange", onHashChange);
9480
10771
  return () => window.removeEventListener("hashchange", onHashChange);
9481
10772
  }, [pathname]);
9482
- const handleCommand = (0, import_react8.useCallback)((cmd) => {
10773
+ const handleCommand = (0, import_react9.useCallback)((cmd) => {
9483
10774
  document.execCommand(cmd, false);
9484
10775
  activeElRef.current?.focus();
9485
10776
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
9486
10777
  refreshActiveCommandsRef.current();
9487
10778
  }, []);
9488
- const handleStateChange = (0, import_react8.useCallback)((state) => {
10779
+ const handleStateChange = (0, import_react9.useCallback)((state) => {
9489
10780
  if (!activeStateElRef.current) return;
9490
10781
  const el = activeStateElRef.current;
9491
10782
  if (state === "Default") {
@@ -9498,11 +10789,11 @@ function OhhwellsBridge() {
9498
10789
  }
9499
10790
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9500
10791
  }, [deactivate]);
9501
- const closeLinkPopover = (0, import_react8.useCallback)(() => {
10792
+ const closeLinkPopover = (0, import_react9.useCallback)(() => {
9502
10793
  addNavAfterAnchorRef.current = null;
9503
10794
  setLinkPopover(null);
9504
10795
  }, []);
9505
- const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
10796
+ const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
9506
10797
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9507
10798
  if (!hrefCtx) return;
9508
10799
  bumpLinkPopoverGrace();
@@ -9513,7 +10804,7 @@ function OhhwellsBridge() {
9513
10804
  });
9514
10805
  deactivate();
9515
10806
  }, [deactivate]);
9516
- const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
10807
+ const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
9517
10808
  const anchor = selectedElRef.current;
9518
10809
  if (!anchor) return;
9519
10810
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9526,7 +10817,7 @@ function OhhwellsBridge() {
9526
10817
  });
9527
10818
  deselect();
9528
10819
  }, [deselect]);
9529
- const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
10820
+ const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
9530
10821
  (target) => {
9531
10822
  const session = linkPopoverSessionRef.current;
9532
10823
  if (!session) return;
@@ -9582,16 +10873,22 @@ function OhhwellsBridge() {
9582
10873
  },
9583
10874
  [postToParent2, sitePages, sectionsByPath]
9584
10875
  );
9585
- const showEditLink = toolbarShowEditLink;
9586
- const currentSections = sectionsByPath[pathname] ?? [];
9587
- linkPopoverOpenRef.current = linkPopover !== null;
9588
- const handleMediaReplace = (0, import_react8.useCallback)(
10876
+ const handleMediaReplace = (0, import_react9.useCallback)(
9589
10877
  (key) => {
10878
+ if (mediaHover?.key === key) {
10879
+ pendingUploadRectRef.current = { key, rect: mediaHover.rect };
10880
+ }
9590
10881
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9591
10882
  },
9592
- [postToParent2, mediaHover?.elementType]
10883
+ [postToParent2, mediaHover]
10884
+ );
10885
+ const handleEditCarousel = (0, import_react9.useCallback)(
10886
+ (key) => {
10887
+ postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
10888
+ },
10889
+ [postToParent2]
9593
10890
  );
9594
- const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
10891
+ const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
9595
10892
  setUploadingRects((prev) => {
9596
10893
  if (!(key in prev)) return prev;
9597
10894
  const next = { ...prev };
@@ -9599,7 +10896,7 @@ function OhhwellsBridge() {
9599
10896
  return next;
9600
10897
  });
9601
10898
  }, []);
9602
- const handleVideoSettingsChange = (0, import_react8.useCallback)(
10899
+ const handleVideoSettingsChange = (0, import_react9.useCallback)(
9603
10900
  (key, settings) => {
9604
10901
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9605
10902
  const video = getVideoEl(el);
@@ -9621,21 +10918,26 @@ function OhhwellsBridge() {
9621
10918
  },
9622
10919
  [postToParent2]
9623
10920
  );
10921
+ const showEditLink = toolbarShowEditLink;
10922
+ const currentSections = sectionsByPath[pathname] ?? [];
10923
+ linkPopoverOpenRef.current = linkPopover !== null;
9624
10924
  return bridgeRoot ? (0, import_react_dom3.createPortal)(
9625
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9626
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9627
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9628
- MediaOverlay,
9629
- {
9630
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
9631
- isUploading: true,
9632
- fadingOut,
9633
- onFadeOutComplete: handleMediaFadeOutComplete,
9634
- onReplace: handleMediaReplace
9635
- },
9636
- `uploading-${key}`
9637
- )),
9638
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10925
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
10926
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
10927
+ Object.entries(uploadingRects).flatMap(
10928
+ ([key, { rects, fadingOut }]) => rects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10929
+ MediaOverlay,
10930
+ {
10931
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
10932
+ isUploading: true,
10933
+ fadingOut,
10934
+ onFadeOutComplete: handleMediaFadeOutComplete,
10935
+ onReplace: handleMediaReplace
10936
+ },
10937
+ `uploading-${key}-${i}`
10938
+ ))
10939
+ ),
10940
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9639
10941
  MediaOverlay,
9640
10942
  {
9641
10943
  hover: mediaHover,
@@ -9644,62 +10946,33 @@ function OhhwellsBridge() {
9644
10946
  onVideoSettingsChange: handleVideoSettingsChange
9645
10947
  }
9646
10948
  ),
9647
- siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9648
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9649
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9650
- NavbarContainerChrome,
9651
- {
9652
- rect: toolbarRect,
9653
- onAdd: handleAddTopLevelNavItem
9654
- }
9655
- ),
9656
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9657
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9658
- ItemInteractionLayer,
10949
+ carouselHover && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
10950
+ siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
10951
+ siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
10952
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
10953
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10954
+ "div",
9659
10955
  {
9660
- rect: toolbarRect,
9661
- elRef: glowElRef,
9662
- state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9663
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
9664
- dragDisabled: reorderDragDisabled,
9665
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9666
- onDragHandleDragStart: handleItemDragStart,
9667
- onDragHandleDragEnd: handleItemDragEnd,
9668
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9669
- ItemActionToolbar,
9670
- {
9671
- onEditLink: openLinkPopoverForSelected,
9672
- addItemDisabled: true,
9673
- editLinkDisabled: false,
9674
- moreDisabled: true
9675
- }
9676
- ) : void 0
9677
- }
9678
- ),
9679
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9680
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9681
- EditGlowChrome,
9682
- {
9683
- rect: toolbarRect,
9684
- elRef: glowElRef,
9685
- reorderHrefKey,
9686
- dragDisabled: reorderDragDisabled
9687
- }
9688
- ),
9689
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9690
- FloatingToolbar,
9691
- {
9692
- rect: toolbarRect,
9693
- parentScroll: parentScrollRef.current,
9694
- elRef: toolbarElRef,
9695
- onCommand: handleCommand,
9696
- activeCommands,
9697
- showEditLink,
9698
- onEditLink: openLinkPopoverForActive
9699
- }
9700
- )
10956
+ className: "pointer-events-none fixed z-2147483646",
10957
+ style: {
10958
+ left: slot.left,
10959
+ top: slot.top,
10960
+ width: slot.width,
10961
+ height: slot.height
10962
+ },
10963
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DropIndicator, { direction: slot.direction, state: activeFooterDropIndex === i ? "dragActive" : "dragIdle", className: slot.direction === "horizontal" ? "!h-full !w-full" : "!h-full !w-full" })
10964
+ },
10965
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
10966
+ )),
10967
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
10968
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
10969
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
10970
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(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__ */ (0, import_jsx_runtime25.jsx)(ItemActionToolbar, { onEditLink: openLinkPopoverForSelected, addItemDisabled: true, editLinkDisabled: false, moreDisabled: true }) : void 0 }),
10971
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
10972
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(EditGlowChrome, { rect: toolbarRect, elRef: glowElRef, reorderHrefKey, dragDisabled: reorderDragDisabled }),
10973
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands, showEditLink, onEditLink: openLinkPopoverForActive })
9701
10974
  ] }),
9702
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
10975
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
9703
10976
  "div",
9704
10977
  {
9705
10978
  "data-ohw-max-badge": "",
@@ -9725,57 +10998,29 @@ function OhhwellsBridge() {
9725
10998
  ]
9726
10999
  }
9727
11000
  ),
9728
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9729
- StateToggle,
9730
- {
9731
- rect: toggleState.rect,
9732
- activeState: toggleState.activeState,
9733
- states: toggleState.states,
9734
- onStateChange: handleStateChange
9735
- }
9736
- ),
9737
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
9738
- "div",
9739
- {
9740
- "data-ohw-section-insert-line": "",
9741
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9742
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
9743
- children: [
9744
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9745
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9746
- Badge,
9747
- {
9748
- 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",
9749
- onClick: () => {
9750
- window.parent.postMessage(
9751
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
9752
- "*"
9753
- );
11001
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StateToggle, { rect: toggleState.rect, activeState: toggleState.activeState, states: toggleState.states, onStateChange: handleStateChange }),
11002
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { "data-ohw-section-insert-line": "", className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none", style: { top: sectionGap.y, transform: "translateY(-50%)" }, children: [
11003
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
11004
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
11005
+ Badge,
11006
+ {
11007
+ 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",
11008
+ onClick: () => {
11009
+ window.parent.postMessage(
11010
+ {
11011
+ type: "ow:add-section",
11012
+ insertAfter: sectionGap.insertAfter,
11013
+ insertBefore: sectionGap.insertBefore
9754
11014
  },
9755
- children: "Add Section"
9756
- }
9757
- ),
9758
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9759
- ]
9760
- }
9761
- ),
9762
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9763
- LinkPopover,
9764
- {
9765
- panelRef: linkPopoverPanelRef,
9766
- portalContainer: dialogPortalContainer,
9767
- open: true,
9768
- mode: linkPopover.mode ?? "edit",
9769
- pages: sitePages,
9770
- sections: currentSections,
9771
- sectionsByPath,
9772
- initialTarget: linkPopover.target,
9773
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
9774
- onClose: closeLinkPopover,
9775
- onSubmit: handleLinkPopoverSubmit
9776
- },
9777
- linkPopover.key
9778
- ) : null
11015
+ "*"
11016
+ );
11017
+ },
11018
+ children: "Add Section"
11019
+ }
11020
+ ),
11021
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
11022
+ ] }),
11023
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LinkPopover, { panelRef: linkPopoverPanelRef, portalContainer: dialogPortalContainer, open: true, mode: linkPopover.mode ?? "edit", pages: sitePages, sections: currentSections, sectionsByPath, initialTarget: linkPopover.target, existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [], onClose: closeLinkPopover, onSubmit: handleLinkPopoverSubmit }, linkPopover.key) : null
9779
11024
  ] }),
9780
11025
  bridgeRoot
9781
11026
  ) : null;
@@ -9786,6 +11031,7 @@ function OhhwellsBridge() {
9786
11031
  CustomToolbarButton,
9787
11032
  CustomToolbarDivider,
9788
11033
  DragHandle,
11034
+ DropIndicator,
9789
11035
  ItemActionToolbar,
9790
11036
  ItemInteractionLayer,
9791
11037
  LinkEditorPanel,
@@ -9800,12 +11046,14 @@ function OhhwellsBridge() {
9800
11046
  TooltipProvider,
9801
11047
  TooltipTrigger,
9802
11048
  buildTarget,
11049
+ dropIndicatorVariants,
9803
11050
  filterAvailablePages,
9804
11051
  getEditModeInitialState,
9805
11052
  isEditSessionActive,
9806
11053
  isValidUrl,
9807
11054
  parseTarget,
9808
11055
  toggleVariants,
11056
+ useOhwCarousel,
9809
11057
  validateUrlInput
9810
11058
  });
9811
11059
  //# sourceMappingURL=index.cjs.map