@ohhwells/bridge 0.1.38-next.69 → 0.1.38

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,7 +35,6 @@ __export(index_exports, {
35
35
  CustomToolbarButton: () => CustomToolbarButton,
36
36
  CustomToolbarDivider: () => CustomToolbarDivider,
37
37
  DragHandle: () => DragHandle,
38
- DropIndicator: () => DropIndicator,
39
38
  ItemActionToolbar: () => ItemActionToolbar,
40
39
  ItemInteractionLayer: () => ItemInteractionLayer,
41
40
  LinkEditorPanel: () => LinkEditorPanel,
@@ -50,31 +49,26 @@ __export(index_exports, {
50
49
  TooltipProvider: () => TooltipProvider,
51
50
  TooltipTrigger: () => TooltipTrigger,
52
51
  buildTarget: () => buildTarget,
53
- dropIndicatorVariants: () => dropIndicatorVariants,
54
52
  filterAvailablePages: () => filterAvailablePages,
55
53
  getEditModeInitialState: () => getEditModeInitialState,
56
54
  isEditSessionActive: () => isEditSessionActive,
57
55
  isValidUrl: () => isValidUrl,
58
56
  parseTarget: () => parseTarget,
59
57
  toggleVariants: () => toggleVariants,
60
- useOhwCarousel: () => useOhwCarousel,
61
58
  validateUrlInput: () => validateUrlInput
62
59
  });
63
60
  module.exports = __toCommonJS(index_exports);
64
61
 
65
62
  // src/OhhwellsBridge.tsx
66
- var import_react9 = __toESM(require("react"), 1);
63
+ var import_react6 = __toESM(require("react"), 1);
67
64
  var import_client = require("react-dom/client");
68
- var import_react_dom2 = require("react-dom");
65
+ var import_react_dom = require("react-dom");
69
66
 
70
67
  // src/linkHrefStore.ts
71
68
  var linkHrefStore = /* @__PURE__ */ new Map();
72
69
  function setStoredLinkHref(key, href) {
73
70
  linkHrefStore.set(key, href);
74
71
  }
75
- function getStoredLinkHref(key) {
76
- return linkHrefStore.get(key);
77
- }
78
72
  function enforceLinkHrefs() {
79
73
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
80
74
  const key = el.getAttribute("data-ohw-href-key") ?? "";
@@ -116,7 +110,6 @@ var import_react3 = require("react");
116
110
  var import_react2 = require("react");
117
111
  var import_radix_ui = require("radix-ui");
118
112
  var import_jsx_runtime = require("react/jsx-runtime");
119
- var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
120
113
  function Spinner() {
121
114
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
122
115
  "svg",
@@ -150,17 +143,12 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
150
143
  const [error, setError] = (0, import_react2.useState)(null);
151
144
  const isBook = title === "Confirm your spot";
152
145
  const handleSubmit = async () => {
153
- const trimmed = email.trim();
154
- if (!trimmed) return;
155
- if (!isValidEmail(trimmed)) {
156
- setError("Please enter a valid email address.");
157
- return;
158
- }
146
+ if (!email.trim()) return;
159
147
  setLoading(true);
160
148
  setError(null);
161
149
  try {
162
- await onSubmit(trimmed);
163
- setSubmittedEmail(trimmed);
150
+ await onSubmit(email.trim());
151
+ setSubmittedEmail(email.trim());
164
152
  setSuccess(true);
165
153
  } catch (e) {
166
154
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -235,10 +223,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
235
223
  {
236
224
  type: "email",
237
225
  value: email,
238
- onChange: (e) => {
239
- setEmail(e.target.value);
240
- if (error) setError(null);
241
- },
226
+ onChange: (e) => setEmail(e.target.value),
242
227
  onKeyDown: handleKeyDown,
243
228
  placeholder: "hello@gmail.com",
244
229
  autoFocus: true,
@@ -295,20 +280,12 @@ function formatClassTime(cls) {
295
280
  const em = endTotal % 60;
296
281
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
297
282
  }
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
- }
303
283
  function getBookingsOnDate(cls, date) {
304
284
  if (!cls.bookings?.length) return 0;
305
- const target = new Date(date);
306
- target.setHours(0, 0, 0, 0);
285
+ const ds = date.toISOString().split("T")[0];
307
286
  return cls.bookings.filter((b) => {
308
287
  try {
309
- const bookingDate = new Date(b.classDate);
310
- bookingDate.setHours(0, 0, 0, 0);
311
- return bookingDate.getTime() === target.getTime();
288
+ return new Date(b.classDate).toISOString().split("T")[0] === ds;
312
289
  } catch {
313
290
  return false;
314
291
  }
@@ -573,7 +550,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
573
550
  }
574
551
  )
575
552
  ] }),
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: [
553
+ /* @__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: [
577
554
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
578
555
  available,
579
556
  "/",
@@ -585,10 +562,9 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
585
562
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
586
563
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
587
564
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
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 })
565
+ 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 })
590
566
  ] }),
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: [
567
+ /* @__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: [
592
568
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
593
569
  available,
594
570
  "/",
@@ -613,7 +589,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
613
589
  if (!cls.id) return;
614
590
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
615
591
  },
616
- children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
592
+ children: isFull ? "Join Waitlist" : "Book Now"
617
593
  }
618
594
  )
619
595
  ] })
@@ -655,17 +631,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
655
631
  const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
656
632
  const [modalState, setModalState] = (0, import_react3.useState)(null);
657
633
  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
- }, []);
669
634
  const dates = (0, import_react3.useMemo)(() => {
670
635
  const today = /* @__PURE__ */ new Date();
671
636
  today.setHours(0, 0, 0, 0);
@@ -685,18 +650,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
685
650
  }
686
651
  }
687
652
  }, [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]);
700
653
  (0, import_react3.useEffect)(() => {
701
654
  if (typeof window === "undefined") return;
702
655
  const isInEditor = window.self !== window.top;
@@ -758,7 +711,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
758
711
  setLoading(false);
759
712
  return;
760
713
  }
761
- liveScheduleIdRef.current = effectiveId;
714
+ ;
762
715
  (async () => {
763
716
  try {
764
717
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -815,14 +768,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
815
768
  const data = await res.json().catch(() => ({}));
816
769
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
817
770
  }
818
- await refetchLiveSchedule();
819
771
  };
820
772
  const handleReplaceSchedule = () => {
821
773
  setIsHovered(false);
822
- window.parent.postMessage(
823
- { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
824
- "*"
825
- );
774
+ if (schedule) {
775
+ window.parent.postMessage({
776
+ type: "ow:schedule-connected",
777
+ schedule: { id: schedule.id, name: schedule.name },
778
+ insertAfter
779
+ }, "*");
780
+ } else {
781
+ window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
782
+ }
826
783
  };
827
784
  if (!inEditor && !loading && !schedule) return null;
828
785
  const sectionId = `scheduling-${insertAfter}`;
@@ -865,19 +822,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
865
822
  ),
866
823
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
867
824
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
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
- ),
825
+ /* @__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" }),
881
826
  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 })
882
827
  ] }),
883
828
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4463,7 +4408,6 @@ function ItemActionToolbar({
4463
4408
  onEditLink,
4464
4409
  onAddItem,
4465
4410
  onMore,
4466
- editLinkDisabled = false,
4467
4411
  addItemDisabled = true,
4468
4412
  moreDisabled = true,
4469
4413
  tooltipSide = "bottom"
@@ -4472,12 +4416,10 @@ function ItemActionToolbar({
4472
4416
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4473
4417
  ToolbarActionTooltip,
4474
4418
  {
4475
- label: "Edit link",
4419
+ label: "Add link",
4476
4420
  side: tooltipSide,
4477
- disabled: editLinkDisabled,
4478
4421
  buttonProps: {
4479
4422
  onMouseDown: (e) => {
4480
- if (editLinkDisabled) return;
4481
4423
  e.preventDefault();
4482
4424
  e.stopPropagation();
4483
4425
  onEditLink?.();
@@ -4555,7 +4497,7 @@ function getChromeStyle(state) {
4555
4497
  case "dragging":
4556
4498
  return {
4557
4499
  border: `2px solid ${PRIMARY}`,
4558
- boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4500
+ boxShadow: DRAG_SHADOW
4559
4501
  };
4560
4502
  default:
4561
4503
  return {};
@@ -4571,8 +4513,6 @@ function ItemInteractionLayer({
4571
4513
  dragHandleLabel = "Reorder item",
4572
4514
  onDragHandleDragStart,
4573
4515
  onDragHandleDragEnd,
4574
- onItemPointerDown,
4575
- onItemClick,
4576
4516
  chromeGap = 6,
4577
4517
  className
4578
4518
  }) {
@@ -4580,8 +4520,7 @@ function ItemInteractionLayer({
4580
4520
  const isActive = state === "active-top" || state === "active-bottom";
4581
4521
  const isDragging = state === "dragging";
4582
4522
  const showToolbar = isActive && toolbar;
4583
- const showDragHandle = (isActive || isDragging) && showHandle;
4584
- const itemDragEnabled = isActive && showHandle && !isDragging && !dragDisabled;
4523
+ const showDragHandle = isActive && showHandle && !isDragging;
4585
4524
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4586
4525
  "div",
4587
4526
  {
@@ -4606,28 +4545,11 @@ function ItemInteractionLayer({
4606
4545
  style: getChromeStyle(state)
4607
4546
  }
4608
4547
  ),
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
- ),
4625
4548
  showDragHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4626
4549
  "div",
4627
4550
  {
4628
4551
  "data-ohw-drag-handle-container": "",
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,
4552
+ className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4631
4553
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4632
4554
  DragHandle,
4633
4555
  {
@@ -4850,12 +4772,15 @@ function MediaOverlay({
4850
4772
  className: "flex items-center justify-center cursor-pointer",
4851
4773
  style: {
4852
4774
  ...box,
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",
4775
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4776
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4777
+ // in-document, pointer-events does it natively. The button below opts back in, so
4778
+ // Replace still works.
4779
+ pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4856
4780
  boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4857
4781
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4858
4782
  },
4783
+ onClick: () => onReplace(hover.key),
4859
4784
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4860
4785
  Button,
4861
4786
  {
@@ -4880,64 +4805,9 @@ function MediaOverlay({
4880
4805
  ] });
4881
4806
  }
4882
4807
 
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
-
4938
4808
  // src/OhhwellsBridge.tsx
4939
- var import_react_dom3 = require("react-dom");
4940
- var import_navigation2 = require("next/navigation");
4809
+ var import_react_dom2 = require("react-dom");
4810
+ var import_navigation = require("next/navigation");
4941
4811
 
4942
4812
  // src/lib/session-search.ts
4943
4813
  var STORAGE_KEY = "ohw-preserved-search";
@@ -4967,7 +4837,6 @@ function parseSectionsFromRoot(root) {
4967
4837
  for (const el of root.querySelectorAll("[data-ohw-section]")) {
4968
4838
  const id = el.getAttribute("data-ohw-section") ?? "";
4969
4839
  if (!id || seen.has(id)) continue;
4970
- if (el.parentElement?.closest("[data-ohw-section]")) continue;
4971
4840
  seen.add(id);
4972
4841
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
4973
4842
  sections.push({ id, label });
@@ -5214,22 +5083,54 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5214
5083
  tick();
5215
5084
  }
5216
5085
 
5217
- // src/ui/link-modal/LinkPopover.tsx
5218
- var import_react7 = require("react");
5219
-
5220
5086
  // src/ui/dialog.tsx
5221
5087
  var React7 = __toESM(require("react"), 1);
5222
5088
  var import_radix_ui5 = require("radix-ui");
5223
- var import_lucide_react5 = require("lucide-react");
5089
+
5090
+ // src/ui/icons.tsx
5091
+ var import_jsx_runtime12 = require("react/jsx-runtime");
5092
+ function IconX({ className, ...props }) {
5093
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5094
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M18 6 6 18" }),
5095
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 6 12 12" })
5096
+ ] });
5097
+ }
5098
+ function IconChevronDown({ className, ...props }) {
5099
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" }) });
5100
+ }
5101
+ function IconFile({ className, ...props }) {
5102
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5103
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
5104
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
5105
+ ] });
5106
+ }
5107
+ function IconInfo({ className, ...props }) {
5108
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5109
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
5110
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 16v-4" }),
5111
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 8h.01" })
5112
+ ] });
5113
+ }
5114
+ function IconArrowRight({ className, ...props }) {
5115
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5116
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M5 12h14" }),
5117
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 5 7 7-7 7" })
5118
+ ] });
5119
+ }
5120
+ function IconSection({ className, ...props }) {
5121
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5122
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
5123
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
5124
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
5125
+ ] });
5126
+ }
5127
+
5128
+ // src/ui/dialog.tsx
5224
5129
  var import_jsx_runtime13 = require("react/jsx-runtime");
5225
- function Dialog2({
5226
- ...props
5227
- }) {
5130
+ function Dialog2({ ...props }) {
5228
5131
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5229
5132
  }
5230
- function DialogPortal({
5231
- ...props
5232
- }) {
5133
+ function DialogPortal({ ...props }) {
5233
5134
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5234
5135
  }
5235
5136
  function DialogOverlay({
@@ -5246,69 +5147,52 @@ function DialogOverlay({
5246
5147
  }
5247
5148
  );
5248
5149
  }
5249
- var DialogContent = React7.forwardRef(
5250
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5251
- const positionMode = container ? "absolute" : "fixed";
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)(
5255
- import_radix_ui5.Dialog.Content,
5256
- {
5257
- ref,
5258
- "data-slot": "dialog-content",
5259
- className: cn(
5260
- positionMode,
5261
- "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
5262
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5263
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
5264
- className
5265
- ),
5266
- onMouseDown: (e) => e.stopPropagation(),
5267
- ...props,
5268
- children: [
5269
- children,
5270
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5271
- import_radix_ui5.Dialog.Close,
5272
- {
5273
- type: "button",
5274
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5275
- "aria-label": "Close",
5276
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.X, { size: 16, "aria-hidden": true })
5277
- }
5278
- ) : null
5279
- ]
5280
- }
5281
- )
5282
- ] });
5283
- }
5284
- );
5150
+ var DialogContent = React7.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
5151
+ const positionMode = container ? "absolute" : "fixed";
5152
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5153
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5154
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
5155
+ import_radix_ui5.Dialog.Content,
5156
+ {
5157
+ ref,
5158
+ "data-slot": "dialog-content",
5159
+ className: cn(
5160
+ positionMode,
5161
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[483px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden",
5162
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5163
+ container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5164
+ className
5165
+ ),
5166
+ onMouseDown: (e) => e.stopPropagation(),
5167
+ ...props,
5168
+ children: [
5169
+ children,
5170
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5171
+ import_radix_ui5.Dialog.Close,
5172
+ {
5173
+ type: "button",
5174
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5175
+ "aria-label": "Close",
5176
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
5177
+ }
5178
+ ) : null
5179
+ ]
5180
+ }
5181
+ )
5182
+ ] });
5183
+ });
5285
5184
  DialogContent.displayName = import_radix_ui5.Dialog.Content.displayName;
5286
- function DialogHeader({
5287
- className,
5288
- ...props
5289
- }) {
5185
+ function DialogHeader({ className, ...props }) {
5290
5186
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5291
5187
  }
5292
- function DialogFooter({
5293
- className,
5294
- ...props
5295
- }) {
5296
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5297
- "div",
5298
- {
5299
- className: cn("flex items-center justify-end gap-2", className),
5300
- ...props
5301
- }
5302
- );
5188
+ function DialogFooter({ className, ...props }) {
5189
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5303
5190
  }
5304
5191
  var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5305
5192
  import_radix_ui5.Dialog.Title,
5306
5193
  {
5307
5194
  ref,
5308
- className: cn(
5309
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
5310
- className
5311
- ),
5195
+ className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5312
5196
  ...props
5313
5197
  }
5314
5198
  ));
@@ -5324,40 +5208,19 @@ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @
5324
5208
  DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
5325
5209
  var DialogClose = import_radix_ui5.Dialog.Close;
5326
5210
 
5327
- // src/ui/link-modal/LinkEditorPanel.tsx
5328
- var import_lucide_react9 = require("lucide-react");
5329
-
5330
5211
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5331
- var import_lucide_react6 = require("lucide-react");
5332
5212
  var import_jsx_runtime14 = require("react/jsx-runtime");
5333
- function DestinationBreadcrumb({
5334
- pageTitle,
5335
- sectionLabel
5336
- }) {
5213
+ function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5337
5214
  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" }),
5215
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5339
5216
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-3", children: [
5340
5217
  /* @__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 })
5218
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5219
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5343
5220
  ] }),
5344
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5345
- import_lucide_react6.ArrowRight,
5346
- {
5347
- size: 16,
5348
- className: "shrink-0 text-muted-foreground",
5349
- "aria-hidden": true
5350
- }
5351
- ),
5221
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5352
5222
  /* @__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,
5355
- {
5356
- size: 16,
5357
- className: "shrink-0 text-foreground",
5358
- "aria-hidden": true
5359
- }
5360
- ),
5223
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5361
5224
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5362
5225
  ] })
5363
5226
  ] })
@@ -5365,13 +5228,8 @@ function DestinationBreadcrumb({
5365
5228
  }
5366
5229
 
5367
5230
  // src/ui/link-modal/SectionTreeItem.tsx
5368
- var import_lucide_react7 = require("lucide-react");
5369
5231
  var import_jsx_runtime15 = require("react/jsx-runtime");
5370
- function SectionTreeItem({
5371
- section,
5372
- onSelect,
5373
- selected
5374
- }) {
5232
+ function SectionTreeItem({ section, onSelect, selected }) {
5375
5233
  const interactive = Boolean(onSelect);
5376
5234
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5377
5235
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
@@ -5399,20 +5257,22 @@ function SectionTreeItem({
5399
5257
  interactive && selected && "border-primary"
5400
5258
  ),
5401
5259
  children: [
5402
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5403
- import_lucide_react7.GalleryVertical,
5404
- {
5405
- size: 16,
5406
- className: "shrink-0 text-foreground",
5407
- "aria-hidden": true
5408
- }
5409
- ),
5260
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5410
5261
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5411
5262
  ]
5412
5263
  }
5413
5264
  )
5414
5265
  ] });
5415
5266
  }
5267
+ function SectionPickerList({ sections, onSelect }) {
5268
+ if (sections.length === 0) {
5269
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5270
+ }
5271
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
5272
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5273
+ sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionTreeItem, { section, onSelect }, section.id))
5274
+ ] });
5275
+ }
5416
5276
 
5417
5277
  // src/ui/link-modal/UrlOrPageInput.tsx
5418
5278
  var import_react4 = require("react");
@@ -5454,11 +5314,8 @@ function Label({ className, ...props }) {
5454
5314
  }
5455
5315
 
5456
5316
  // src/ui/link-modal/UrlOrPageInput.tsx
5457
- var import_lucide_react8 = require("lucide-react");
5458
5317
  var import_jsx_runtime18 = require("react/jsx-runtime");
5459
- function FieldChevron({
5460
- onClick
5461
- }) {
5318
+ function FieldChevron({ onClick }) {
5462
5319
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5463
5320
  "button",
5464
5321
  {
@@ -5467,7 +5324,7 @@ function FieldChevron({
5467
5324
  onClick,
5468
5325
  "aria-label": "Open page list",
5469
5326
  tabIndex: -1,
5470
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.ChevronDown, { size: 16 })
5327
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconChevronDown, {})
5471
5328
  }
5472
5329
  );
5473
5330
  }
@@ -5485,29 +5342,7 @@ function UrlOrPageInput({
5485
5342
  }) {
5486
5343
  const inputId = (0, import_react4.useId)();
5487
5344
  const inputRef = (0, import_react4.useRef)(null);
5488
- const rootRef = (0, import_react4.useRef)(null);
5489
5345
  const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
5490
- (0, import_react4.useEffect)(() => {
5491
- if (!dropdownOpen) return;
5492
- const isOutside = (e) => {
5493
- const root = rootRef.current;
5494
- if (!root) return true;
5495
- const path = typeof e.composedPath === "function" ? e.composedPath() : [];
5496
- if (path.includes(root)) return false;
5497
- const target = e.target;
5498
- return !(target instanceof Node && root.contains(target));
5499
- };
5500
- const closeIfOutside = (e) => {
5501
- if (!isOutside(e)) return;
5502
- onDropdownOpenChange(false);
5503
- };
5504
- document.addEventListener("pointerdown", closeIfOutside, true);
5505
- document.addEventListener("mousedown", closeIfOutside, true);
5506
- return () => {
5507
- document.removeEventListener("pointerdown", closeIfOutside, true);
5508
- document.removeEventListener("mousedown", closeIfOutside, true);
5509
- };
5510
- }, [dropdownOpen, onDropdownOpenChange]);
5511
5346
  const openDropdown = () => {
5512
5347
  if (readOnly || filteredPages.length === 0) return;
5513
5348
  onDropdownOpenChange(true);
@@ -5525,21 +5360,14 @@ function UrlOrPageInput({
5525
5360
  requestAnimationFrame(() => inputRef.current?.focus());
5526
5361
  };
5527
5362
  const fieldClassName = cn(
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]",
5363
+ "data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5529
5364
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5530
5365
  );
5531
5366
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5532
5367
  /* @__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: [
5368
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "relative w-full", children: [
5534
5369
  /* @__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,
5537
- {
5538
- size: 16,
5539
- className: "shrink-0 text-foreground",
5540
- "aria-hidden": true
5541
- }
5542
- ) }) : null,
5370
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5543
5371
  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)(
5544
5372
  Input,
5545
5373
  {
@@ -5551,14 +5379,7 @@ function UrlOrPageInput({
5551
5379
  setIsFocused(true);
5552
5380
  if (!selectedPage) openDropdown();
5553
5381
  },
5554
- onBlur: () => {
5555
- setIsFocused(false);
5556
- requestAnimationFrame(() => {
5557
- if (!rootRef.current?.contains(document.activeElement)) {
5558
- onDropdownOpenChange(false);
5559
- }
5560
- });
5561
- },
5382
+ onBlur: () => setIsFocused(false),
5562
5383
  placeholder,
5563
5384
  className: cn(
5564
5385
  "min-w-0 flex-1 truncate border-0 bg-transparent p-0 text-sm font-normal leading-5 shadow-none outline-none ring-0 caret-foreground",
@@ -5574,7 +5395,7 @@ function UrlOrPageInput({
5574
5395
  onMouseDown: clearSelection,
5575
5396
  "aria-label": "Clear selected page",
5576
5397
  tabIndex: -1,
5577
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5398
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconX, { "aria-hidden": true })
5578
5399
  }
5579
5400
  ) : null,
5580
5401
  !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
@@ -5583,7 +5404,7 @@ function UrlOrPageInput({
5583
5404
  "div",
5584
5405
  {
5585
5406
  "data-ohw-link-page-dropdown": "",
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",
5407
+ className: "absolute left-0 right-0 h-20 top-[calc(100%+4px)] z-10 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
5587
5408
  onMouseDown: (e) => e.preventDefault(),
5588
5409
  children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5589
5410
  "button",
@@ -5592,7 +5413,7 @@ function UrlOrPageInput({
5592
5413
  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",
5593
5414
  onClick: () => onPageSelect(page),
5594
5415
  children: [
5595
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5416
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
5596
5417
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate", children: page.title })
5597
5418
  ]
5598
5419
  },
@@ -5605,439 +5426,8 @@ function UrlOrPageInput({
5605
5426
  ] });
5606
5427
  }
5607
5428
 
5608
- // src/ui/link-modal/LinkEditorPanel.tsx
5609
- var import_jsx_runtime19 = require("react/jsx-runtime");
5610
- function LinkEditorPanel({ state, onClose }) {
5611
- const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
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)(
5614
- "button",
5615
- {
5616
- type: "button",
5617
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5618
- "aria-label": "Close",
5619
- onClick: onClose,
5620
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.X, { size: 16, "aria-hidden": true })
5621
- }
5622
- ) }),
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)(
5626
- DestinationBreadcrumb,
5627
- {
5628
- pageTitle: state.selectedPage.title,
5629
- sectionLabel: state.selectedSection.label
5630
- }
5631
- ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5632
- UrlOrPageInput,
5633
- {
5634
- value: state.searchValue,
5635
- selectedPage: state.selectedPage,
5636
- dropdownOpen: state.dropdownOpen,
5637
- onDropdownOpenChange: state.setDropdownOpen,
5638
- filteredPages: state.filteredPages,
5639
- onInputChange: state.handleInputChange,
5640
- onPageSelect: state.handlePageSelect,
5641
- urlError: state.urlError
5642
- }
5643
- ),
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)(
5646
- Button,
5647
- {
5648
- type: "button",
5649
- variant: "outline",
5650
- className: "w-fit cursor-pointer",
5651
- size: "sm",
5652
- onClick: state.handleChooseSection,
5653
- children: "Choose a section"
5654
- }
5655
- ),
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." })
5659
- ] })
5660
- ] }) : null,
5661
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5662
- ] }),
5663
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5664
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5665
- Button,
5666
- {
5667
- type: "button",
5668
- variant: isCancel ? "outline" : "ghost",
5669
- className: "leading-6 shadow-none cursor-pointer",
5670
- style: isCancel ? {
5671
- backgroundColor: "var(--ohw-background)",
5672
- borderColor: "var(--ohw-border)",
5673
- color: "var(--ohw-foreground)"
5674
- } : void 0,
5675
- onClick: state.handleSecondary,
5676
- children: state.secondaryLabel
5677
- }
5678
- ),
5679
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5680
- Button,
5681
- {
5682
- type: "button",
5683
- className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5684
- style: {
5685
- backgroundColor: "var(--ohw-primary)",
5686
- color: "var(--ohw-primary-foreground)"
5687
- },
5688
- disabled: !state.isValid,
5689
- onClick: state.handleSubmit,
5690
- children: state.submitLabel
5691
- }
5692
- )
5693
- ] })
5694
- ] });
5695
- }
5696
-
5697
- // src/ui/link-modal/SectionPickerOverlay.tsx
5429
+ // src/ui/link-modal/useLinkModalState.ts
5698
5430
  var import_react5 = require("react");
5699
- var import_react_dom = require("react-dom");
5700
- var import_lucide_react10 = require("lucide-react");
5701
- var import_navigation = require("next/navigation");
5702
- var import_jsx_runtime20 = require("react/jsx-runtime");
5703
- var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5704
- function rectsEqual(a, b) {
5705
- if (a.size !== b.size) return false;
5706
- for (const [id, rect] of a) {
5707
- const other = b.get(id);
5708
- if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
5709
- return false;
5710
- }
5711
- }
5712
- return true;
5713
- }
5714
- function readSectionRects(sectionIds) {
5715
- const next = /* @__PURE__ */ new Map();
5716
- for (const id of sectionIds) {
5717
- const el = document.querySelector(
5718
- `[data-ohw-section="${CSS.escape(id)}"]`
5719
- );
5720
- if (el) next.set(id, el.getBoundingClientRect());
5721
- }
5722
- return next;
5723
- }
5724
- function hitTestSectionId(x, y, sectionIds, rects) {
5725
- for (const id of sectionIds) {
5726
- const rect = rects.get(id);
5727
- if (!rect || rect.width <= 0 || rect.height <= 0) continue;
5728
- if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
5729
- return id;
5730
- }
5731
- }
5732
- return null;
5733
- }
5734
- function useSectionRects(sectionIdsKey, enabled) {
5735
- const [rects, setRects] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
5736
- const sectionIds = (0, import_react5.useMemo)(
5737
- () => sectionIdsKey ? sectionIdsKey.split("\0") : [],
5738
- [sectionIdsKey]
5739
- );
5740
- (0, import_react5.useEffect)(() => {
5741
- if (!enabled || sectionIds.length === 0) {
5742
- setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
5743
- return;
5744
- }
5745
- const update = () => {
5746
- const next = readSectionRects(sectionIds);
5747
- setRects((prev) => rectsEqual(prev, next) ? prev : next);
5748
- };
5749
- update();
5750
- const opts = { capture: true, passive: true };
5751
- window.addEventListener("scroll", update, opts);
5752
- window.addEventListener("resize", update);
5753
- const vv = window.visualViewport;
5754
- vv?.addEventListener("resize", update);
5755
- vv?.addEventListener("scroll", update);
5756
- const ro = new ResizeObserver(update);
5757
- for (const id of sectionIds) {
5758
- const el = document.querySelector(
5759
- `[data-ohw-section="${CSS.escape(id)}"]`
5760
- );
5761
- if (el) ro.observe(el);
5762
- }
5763
- return () => {
5764
- window.removeEventListener("scroll", update, opts);
5765
- window.removeEventListener("resize", update);
5766
- vv?.removeEventListener("resize", update);
5767
- vv?.removeEventListener("scroll", update);
5768
- ro.disconnect();
5769
- };
5770
- }, [sectionIds, enabled]);
5771
- return rects;
5772
- }
5773
- function SectionPickerOverlay({
5774
- pagePath,
5775
- sections,
5776
- onBack,
5777
- onSelect
5778
- }) {
5779
- const router = (0, import_navigation.useRouter)();
5780
- const pathname = (0, import_navigation.usePathname)();
5781
- const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
5782
- const [hoveredId, setHoveredId] = (0, import_react5.useState)(null);
5783
- const pointerRef = (0, import_react5.useRef)(null);
5784
- const pointerScreenRef = (0, import_react5.useRef)(null);
5785
- const iframeOffsetTopRef = (0, import_react5.useRef)(null);
5786
- const sectionIdsRef = (0, import_react5.useRef)([]);
5787
- const [chromeClip, setChromeClip] = (0, import_react5.useState)(
5788
- () => ({
5789
- top: 0,
5790
- bottom: typeof window !== "undefined" ? window.innerHeight : 0
5791
- })
5792
- );
5793
- const normalizedTarget = normalizePath(pagePath);
5794
- const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
5795
- (0, import_react5.useEffect)(() => {
5796
- if (isOnTargetPage) return;
5797
- const search = readPreservedSearch();
5798
- router.push(`${normalizedTarget}${search}`);
5799
- }, [isOnTargetPage, normalizedTarget, router]);
5800
- (0, import_react5.useEffect)(() => {
5801
- document.documentElement.setAttribute("data-ohw-section-picking", "");
5802
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5803
- el.removeAttribute("data-ohw-hovered");
5804
- });
5805
- document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => {
5806
- el.removeAttribute("data-ohw-state-hovered");
5807
- });
5808
- return () => {
5809
- document.documentElement.removeAttribute("data-ohw-section-picking");
5810
- };
5811
- }, []);
5812
- const liveSections = (0, import_react5.useMemo)(() => {
5813
- if (!isOnTargetPage) return sections;
5814
- const live = collectSectionsFromDom();
5815
- if (live.length === 0) return sections;
5816
- const labels = new Map(sections.map((s) => [s.id, s.label]));
5817
- return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5818
- }, [isOnTargetPage, sections, pathname]);
5819
- const sectionIdsKey = (0, import_react5.useMemo)(
5820
- () => liveSections.map((s) => s.id).join("\0"),
5821
- [liveSections]
5822
- );
5823
- const sectionIds = (0, import_react5.useMemo)(
5824
- () => liveSections.map((s) => s.id),
5825
- [liveSections]
5826
- );
5827
- sectionIdsRef.current = sectionIds;
5828
- const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5829
- const applyHoverAt = (0, import_react5.useCallback)(
5830
- (x, y, liveRects) => {
5831
- const ids = sectionIdsRef.current;
5832
- const map = liveRects ?? readSectionRects(ids);
5833
- const next = hitTestSectionId(x, y, ids, map);
5834
- setHoveredId((prev) => prev === next ? prev : next);
5835
- },
5836
- []
5837
- );
5838
- (0, import_react5.useEffect)(() => {
5839
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
5840
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
5841
- const bottom = Math.min(
5842
- window.innerHeight,
5843
- visibleCanvasTop + canvasH - iframeOffsetTop
5844
- );
5845
- setChromeClip(
5846
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
5847
- );
5848
- };
5849
- const onParentScroll = (e) => {
5850
- if (e.data?.type !== "ow:parent-scroll") return;
5851
- const { iframeOffsetTop, headerH, canvasH } = e.data;
5852
- applyClip(iframeOffsetTop, headerH, canvasH);
5853
- iframeOffsetTopRef.current = iframeOffsetTop;
5854
- if (!pointerScreenRef.current && pointerRef.current) {
5855
- pointerScreenRef.current = {
5856
- x: pointerRef.current.x,
5857
- y: pointerRef.current.y + iframeOffsetTop
5858
- };
5859
- }
5860
- const screen = pointerScreenRef.current;
5861
- if (screen) {
5862
- const next = { x: screen.x, y: screen.y - iframeOffsetTop };
5863
- pointerRef.current = next;
5864
- applyHoverAt(next.x, next.y);
5865
- }
5866
- };
5867
- const onResize = () => {
5868
- setChromeClip((prev) => {
5869
- const bottom = Math.max(prev.top, window.innerHeight);
5870
- return prev.bottom === bottom ? prev : { ...prev, bottom };
5871
- });
5872
- };
5873
- window.addEventListener("message", onParentScroll);
5874
- window.addEventListener("resize", onResize);
5875
- return () => {
5876
- window.removeEventListener("message", onParentScroll);
5877
- window.removeEventListener("resize", onResize);
5878
- };
5879
- }, [applyHoverAt]);
5880
- (0, import_react5.useEffect)(() => {
5881
- const ptr = pointerRef.current;
5882
- if (!ptr) return;
5883
- applyHoverAt(ptr.x, ptr.y, rects);
5884
- }, [rects, applyHoverAt]);
5885
- (0, import_react5.useEffect)(() => {
5886
- const rememberPointer = (clientX, clientY) => {
5887
- pointerRef.current = { x: clientX, y: clientY };
5888
- const top = iframeOffsetTopRef.current;
5889
- if (top != null) {
5890
- pointerScreenRef.current = { x: clientX, y: clientY + top };
5891
- }
5892
- applyHoverAt(clientX, clientY, rects);
5893
- };
5894
- const onPointerMove = (e) => {
5895
- rememberPointer(e.clientX, e.clientY);
5896
- };
5897
- const onPointerSync = (e) => {
5898
- if (e.data?.type !== "ow:pointer-sync") return;
5899
- const { clientX, clientY } = e.data;
5900
- rememberPointer(clientX, clientY);
5901
- };
5902
- window.addEventListener("pointermove", onPointerMove, { passive: true });
5903
- window.addEventListener("message", onPointerSync);
5904
- return () => {
5905
- window.removeEventListener("pointermove", onPointerMove);
5906
- window.removeEventListener("message", onPointerSync);
5907
- };
5908
- }, [rects, applyHoverAt]);
5909
- const handleSelect = (0, import_react5.useCallback)(
5910
- (section) => {
5911
- if (selectedId) return;
5912
- setSelectedId(section.id);
5913
- onSelect(section);
5914
- },
5915
- [selectedId, onSelect]
5916
- );
5917
- (0, import_react5.useEffect)(() => {
5918
- const onKeyDown = (e) => {
5919
- if (e.key === "Escape") {
5920
- e.preventDefault();
5921
- e.stopPropagation();
5922
- onBack();
5923
- }
5924
- };
5925
- window.addEventListener("keydown", onKeyDown, true);
5926
- return () => window.removeEventListener("keydown", onKeyDown, true);
5927
- }, [onBack]);
5928
- const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5929
- if (!portalRoot) return null;
5930
- return (0, import_react_dom.createPortal)(
5931
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5932
- "div",
5933
- {
5934
- "data-ohw-section-picker": "",
5935
- "data-ohw-bridge": "",
5936
- className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
5937
- "aria-modal": true,
5938
- role: "dialog",
5939
- "aria-label": "Choose a section",
5940
- children: [
5941
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5942
- "div",
5943
- {
5944
- className: "pointer-events-auto fixed left-5 z-[2]",
5945
- style: { top: chromeClip.top + 20 },
5946
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5947
- Button,
5948
- {
5949
- type: "button",
5950
- variant: "outline",
5951
- size: "sm",
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",
5953
- onClick: onBack,
5954
- children: [
5955
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5956
- "Back"
5957
- ]
5958
- }
5959
- )
5960
- }
5961
- ),
5962
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5963
- "div",
5964
- {
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",
5966
- style: {
5967
- top: chromeClip.bottom - 20,
5968
- transform: "translate(-50%, -100%)",
5969
- backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
5970
- },
5971
- "data-ohw-section-picker-hint": "",
5972
- children: "Click on section to select"
5973
- }
5974
- ),
5975
- !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5976
- "div",
5977
- {
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",
5979
- style: { top: chromeClip.top + 64 },
5980
- children: "Loading page preview\u2026"
5981
- }
5982
- ) : 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,
5984
- isOnTargetPage ? liveSections.map((section) => {
5985
- const rect = rects.get(section.id);
5986
- if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5987
- const isSelected = selectedId === section.id;
5988
- const isHovered = hoveredId === section.id;
5989
- const isLit = isSelected || isHovered;
5990
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5991
- "button",
5992
- {
5993
- type: "button",
5994
- className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
5995
- style: {
5996
- top: rect.top,
5997
- left: rect.left,
5998
- width: rect.width,
5999
- height: rect.height,
6000
- backgroundColor: isLit ? "transparent" : DIM_OVERLAY
6001
- },
6002
- "aria-label": `Select section ${section.label}`,
6003
- onClick: () => handleSelect(section),
6004
- children: [
6005
- isLit ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6006
- "span",
6007
- {
6008
- className: "pointer-events-none absolute",
6009
- style: {
6010
- inset: 4,
6011
- borderRadius: "var(--radius, 6px)",
6012
- border: "1.5px dashed var(--primary, #0885FE)",
6013
- boxSizing: "border-box"
6014
- },
6015
- "aria-hidden": true
6016
- }
6017
- ) : null,
6018
- isSelected ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6019
- "span",
6020
- {
6021
- className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
6022
- style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
6023
- "aria-hidden": true,
6024
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.Check, { className: "size-5" })
6025
- }
6026
- ) : null
6027
- ]
6028
- },
6029
- section.id
6030
- );
6031
- }) : null
6032
- ]
6033
- }
6034
- ),
6035
- portalRoot
6036
- );
6037
- }
6038
-
6039
- // src/ui/link-modal/useLinkModalState.ts
6040
- var import_react6 = require("react");
6041
5431
  function useLinkModalState({
6042
5432
  open,
6043
5433
  mode,
@@ -6045,18 +5435,21 @@ function useLinkModalState({
6045
5435
  sections: _sections,
6046
5436
  sectionsByPath,
6047
5437
  initialTarget,
6048
- existingTargets: _existingTargets,
5438
+ existingTargets,
6049
5439
  onClose,
6050
5440
  onSubmit
6051
5441
  }) {
6052
- const availablePages = (0, import_react6.useMemo)(() => pages, [pages]);
6053
- const [searchValue, setSearchValue] = (0, import_react6.useState)("");
6054
- const [selectedPage, setSelectedPage] = (0, import_react6.useState)(null);
6055
- const [selectedSection, setSelectedSection] = (0, import_react6.useState)(null);
6056
- const [step, setStep] = (0, import_react6.useState)("input");
6057
- const [dropdownOpen, setDropdownOpen] = (0, import_react6.useState)(false);
6058
- const [urlError, setUrlError] = (0, import_react6.useState)("");
6059
- const reset = (0, import_react6.useCallback)(() => {
5442
+ const availablePages = (0, import_react5.useMemo)(
5443
+ () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
5444
+ [mode, pages, existingTargets]
5445
+ );
5446
+ const [searchValue, setSearchValue] = (0, import_react5.useState)("");
5447
+ const [selectedPage, setSelectedPage] = (0, import_react5.useState)(null);
5448
+ const [selectedSection, setSelectedSection] = (0, import_react5.useState)(null);
5449
+ const [step, setStep] = (0, import_react5.useState)("input");
5450
+ const [dropdownOpen, setDropdownOpen] = (0, import_react5.useState)(false);
5451
+ const [urlError, setUrlError] = (0, import_react5.useState)("");
5452
+ const reset = (0, import_react5.useCallback)(() => {
6060
5453
  setSearchValue("");
6061
5454
  setSelectedPage(null);
6062
5455
  setSelectedSection(null);
@@ -6064,7 +5457,7 @@ function useLinkModalState({
6064
5457
  setDropdownOpen(false);
6065
5458
  setUrlError("");
6066
5459
  }, []);
6067
- (0, import_react6.useEffect)(() => {
5460
+ (0, import_react5.useEffect)(() => {
6068
5461
  if (!open) return;
6069
5462
  if (mode === "edit" && initialTarget) {
6070
5463
  const { pageRoute } = parseTarget(initialTarget);
@@ -6079,12 +5472,12 @@ function useLinkModalState({
6079
5472
  }
6080
5473
  setDropdownOpen(false);
6081
5474
  setUrlError("");
6082
- }, [open, mode, initialTarget, reset]);
6083
- const filteredPages = (0, import_react6.useMemo)(() => {
5475
+ }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
5476
+ const filteredPages = (0, import_react5.useMemo)(() => {
6084
5477
  if (!searchValue.trim()) return availablePages;
6085
5478
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
6086
5479
  }, [availablePages, searchValue]);
6087
- const activeSections = (0, import_react6.useMemo)(() => {
5480
+ const activeSections = (0, import_react5.useMemo)(() => {
6088
5481
  if (!selectedPage) return [];
6089
5482
  return getSectionsForPath(sectionsByPath, selectedPage.path);
6090
5483
  }, [selectedPage, sectionsByPath]);
@@ -6126,14 +5519,11 @@ function useLinkModalState({
6126
5519
  const handleBackToSections = () => {
6127
5520
  setStep("sectionPicker");
6128
5521
  };
6129
- const handleSectionPickerBack = () => {
6130
- setStep("input");
6131
- };
6132
5522
  const handleClose = () => {
6133
5523
  reset();
6134
5524
  onClose();
6135
5525
  };
6136
- const isValid = (0, import_react6.useMemo)(() => {
5526
+ const isValid = (0, import_react5.useMemo)(() => {
6137
5527
  if (urlError) return false;
6138
5528
  if (showBreadcrumb) return true;
6139
5529
  if (selectedPage) return true;
@@ -6166,7 +5556,7 @@ function useLinkModalState({
6166
5556
  onSubmit(normalizeUrl(searchValue));
6167
5557
  handleClose();
6168
5558
  };
6169
- const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
5559
+ const submitLabel = mode === "edit" ? "Save" : "Create";
6170
5560
  const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
6171
5561
  const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
6172
5562
  const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
@@ -6193,29 +5583,23 @@ function useLinkModalState({
6193
5583
  handleChooseSection,
6194
5584
  handleSectionSelect,
6195
5585
  handleBackToSections,
6196
- handleSectionPickerBack,
6197
5586
  handleClose,
6198
5587
  handleSecondary,
6199
5588
  handleSubmit
6200
5589
  };
6201
5590
  }
6202
5591
 
6203
- // src/ui/link-modal/LinkPopover.tsx
6204
- var import_jsx_runtime21 = require("react/jsx-runtime");
6205
- function postToParent(data) {
6206
- window.parent?.postMessage(data, "*");
6207
- }
6208
- function LinkPopover({
5592
+ // src/ui/link-modal/LinkEditorPanel.tsx
5593
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5594
+ function LinkEditorPanel({
6209
5595
  open = true,
6210
- panelRef,
6211
- portalContainer,
6212
- onClose,
6213
5596
  mode = "create",
6214
5597
  pages,
6215
5598
  sections = [],
6216
5599
  sectionsByPath,
6217
5600
  initialTarget,
6218
5601
  existingTargets = [],
5602
+ onClose,
6219
5603
  onSubmit
6220
5604
  }) {
6221
5605
  const state = useLinkModalState({
@@ -6229,109 +5613,115 @@ function LinkPopover({
6229
5613
  onClose,
6230
5614
  onSubmit
6231
5615
  });
6232
- const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
6233
- (0, import_react7.useEffect)(() => {
6234
- if (!open) return;
6235
- if (sectionPickerActive) {
6236
- postToParent({ type: "ui:unlock" });
6237
- postToParent({ type: "ow:section-picker", active: true });
6238
- document.documentElement.style.overflow = "";
6239
- document.body.style.overflow = "";
6240
- return () => {
6241
- postToParent({ type: "ow:section-picker", active: false });
6242
- };
6243
- }
6244
- postToParent({ type: "ow:section-picker", active: false });
6245
- postToParent({ type: "ui:lock" });
6246
- const html = document.documentElement;
6247
- const body = document.body;
6248
- const prevHtmlOverflow = html.style.overflow;
6249
- const prevBodyOverflow = body.style.overflow;
6250
- const prevHtmlOverscroll = html.style.overscrollBehavior;
6251
- const prevBodyOverscroll = body.style.overscrollBehavior;
6252
- html.style.overflow = "hidden";
6253
- body.style.overflow = "hidden";
6254
- html.style.overscrollBehavior = "none";
6255
- body.style.overscrollBehavior = "none";
6256
- const canScrollElement = (el, deltaY) => {
6257
- const { overflowY } = getComputedStyle(el);
6258
- if (overflowY !== "auto" && overflowY !== "scroll") return false;
6259
- if (el.scrollHeight <= el.clientHeight + 1) return false;
6260
- if (deltaY < 0) return el.scrollTop > 0;
6261
- if (deltaY > 0)
6262
- return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6263
- return true;
6264
- };
6265
- const allowsInternalScroll = (e) => {
6266
- const target = e.target;
6267
- if (!(target instanceof Element)) return false;
6268
- const scrollRoot = target.closest(
6269
- "[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
6270
- );
6271
- if (!scrollRoot) return false;
6272
- const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
6273
- return canScrollElement(scrollRoot, deltaY);
6274
- };
6275
- const preventBackgroundScroll = (e) => {
6276
- if (allowsInternalScroll(e)) return;
6277
- e.preventDefault();
6278
- };
6279
- const scrollOpts = { passive: false, capture: true };
6280
- document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6281
- document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6282
- return () => {
6283
- postToParent({ type: "ui:unlock" });
6284
- html.style.overflow = prevHtmlOverflow;
6285
- body.style.overflow = prevBodyOverflow;
6286
- html.style.overscrollBehavior = prevHtmlOverscroll;
6287
- body.style.overscrollBehavior = prevBodyOverscroll;
6288
- document.removeEventListener(
6289
- "wheel",
6290
- preventBackgroundScroll,
6291
- scrollOpts
6292
- );
6293
- document.removeEventListener(
6294
- "touchmove",
6295
- preventBackgroundScroll,
6296
- scrollOpts
6297
- );
6298
- };
6299
- }, [open, sectionPickerActive]);
6300
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6301
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6302
- Dialog2,
6303
- {
6304
- open: open && !sectionPickerActive,
6305
- onOpenChange: (next) => {
6306
- if (!next) onClose?.();
6307
- },
6308
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6309
- DialogContent,
6310
- {
6311
- ref: panelRef,
6312
- container: portalContainer,
6313
- "data-ohw-link-popover-root": "",
6314
- "data-ohw-link-modal-root": "",
6315
- "data-ohw-bridge": "",
6316
- showCloseButton: false,
6317
- className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6318
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LinkEditorPanel, { state, onClose })
6319
- }
6320
- )
6321
- }
6322
- ),
6323
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6324
- SectionPickerOverlay,
5616
+ const isCancel = state.secondaryLabel === "Cancel";
5617
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5618
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5619
+ "button",
6325
5620
  {
6326
- pagePath: state.selectedPage.path,
6327
- sections: state.activeSections,
6328
- onBack: state.handleSectionPickerBack,
6329
- onSelect: state.handleSectionSelect
5621
+ type: "button",
5622
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5623
+ "aria-label": "Close",
5624
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconX, { "aria-hidden": true })
6330
5625
  }
6331
- ) : null
5626
+ ) }),
5627
+ /* @__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 }) }),
5628
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5629
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5630
+ DestinationBreadcrumb,
5631
+ {
5632
+ pageTitle: state.selectedPage.title,
5633
+ sectionLabel: state.selectedSection.label
5634
+ }
5635
+ ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5636
+ UrlOrPageInput,
5637
+ {
5638
+ value: state.searchValue,
5639
+ selectedPage: state.selectedPage,
5640
+ dropdownOpen: state.dropdownOpen,
5641
+ onDropdownOpenChange: state.setDropdownOpen,
5642
+ filteredPages: state.filteredPages,
5643
+ onInputChange: state.handleInputChange,
5644
+ onPageSelect: state.handlePageSelect,
5645
+ urlError: state.urlError
5646
+ }
5647
+ ),
5648
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5649
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5650
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5651
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5652
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Pick a section this link should scroll to." })
5653
+ ] })
5654
+ ] }) : null,
5655
+ state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5656
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5657
+ ] }),
5658
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5659
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5660
+ Button,
5661
+ {
5662
+ type: "button",
5663
+ variant: isCancel ? "outline" : "ghost",
5664
+ className: "leading-6 shadow-none cursor-pointer",
5665
+ style: isCancel ? {
5666
+ backgroundColor: "var(--ohw-background)",
5667
+ borderColor: "var(--ohw-border)",
5668
+ color: "var(--ohw-foreground)"
5669
+ } : void 0,
5670
+ onClick: state.handleSecondary,
5671
+ children: state.secondaryLabel
5672
+ }
5673
+ ),
5674
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5675
+ Button,
5676
+ {
5677
+ type: "button",
5678
+ className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5679
+ style: {
5680
+ backgroundColor: "var(--ohw-primary)",
5681
+ color: "var(--ohw-primary-foreground)"
5682
+ },
5683
+ disabled: !state.isValid,
5684
+ onClick: state.handleSubmit,
5685
+ children: state.submitLabel
5686
+ }
5687
+ )
5688
+ ] })
6332
5689
  ] });
6333
5690
  }
6334
5691
 
5692
+ // src/ui/link-modal/LinkPopover.tsx
5693
+ var import_jsx_runtime20 = require("react/jsx-runtime");
5694
+ function LinkPopover({
5695
+ open = true,
5696
+ panelRef,
5697
+ portalContainer,
5698
+ onClose,
5699
+ ...editorProps
5700
+ }) {
5701
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5702
+ Dialog2,
5703
+ {
5704
+ open,
5705
+ onOpenChange: (next) => {
5706
+ if (!next) onClose?.();
5707
+ },
5708
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5709
+ DialogContent,
5710
+ {
5711
+ ref: panelRef,
5712
+ container: portalContainer,
5713
+ "data-ohw-link-popover-root": "",
5714
+ "data-ohw-link-modal-root": "",
5715
+ "data-ohw-bridge": "",
5716
+ showCloseButton: false,
5717
+ className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5718
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5719
+ }
5720
+ )
5721
+ }
5722
+ );
5723
+ }
5724
+
6335
5725
  // src/ui/link-modal/devFixtures.ts
6336
5726
  var DEV_SITE_PAGES = [
6337
5727
  { path: "/", title: "Home" },
@@ -6351,766 +5741,47 @@ var DEV_SECTIONS_BY_PATH = {
6351
5741
  { id: "founder-teaser", label: "Founder" },
6352
5742
  { id: "plan-form", label: "Plan Form" },
6353
5743
  { id: "testimonials", label: "Testimonials" },
6354
- { id: "wordmark", label: "Wordmark" },
6355
- { id: "footer", label: "Footer" }
5744
+ { id: "wordmark", label: "Wordmark" }
6356
5745
  ],
6357
5746
  "/about": [
6358
- { id: "manifesto", label: "Manifesto" },
6359
- { id: "story-letter", label: "Our Story" },
6360
- { id: "lagree-explainer", label: "Lagree Explainer" },
6361
- { id: "waitlist-cta", label: "Waitlist" },
6362
- { id: "personal-training", label: "Personal training" },
6363
- { id: "footer", label: "Footer" }
6364
- ],
6365
- "/classes": [
6366
- { id: "class-library", label: "Class Library" },
6367
- { id: "footer", label: "Footer" }
6368
- ],
6369
- "/pricing": [
6370
- { id: "page-header", label: "Page Header" },
6371
- { id: "free-class-cta", label: "Free Class" },
6372
- { id: "benefits-marquee", label: "Benefits" },
6373
- { id: "monthly-memberships", label: "Memberships" },
6374
- { id: "specialty-programs", label: "Specialty Programs" },
6375
- { id: "package-matcher", label: "Packages" },
6376
- { id: "wordmark", label: "Wordmark" },
6377
- { id: "footer", label: "Footer" }
6378
- ],
6379
- "/studio": [
6380
- { id: "studio-intro", label: "Studio Intro" },
6381
- { id: "studio-features", label: "Studio Features" },
6382
- { id: "studio-gallery", label: "Studio Gallery" },
6383
- { id: "studio-visit", label: "Visit Studio" },
6384
- { id: "footer", label: "Footer" }
6385
- ],
6386
- "/instructors": [
6387
- { id: "instructor-grid", label: "Instructors" },
6388
- { id: "footer", label: "Footer" }
6389
- ],
6390
- "/contact": [
6391
- { id: "hello-hero", label: "Hello" },
6392
- { id: "contact-form", label: "Contact Form" },
6393
- { id: "faq", label: "FAQ" },
6394
- { id: "footer", label: "Footer" }
6395
- ]
6396
- };
6397
- function shouldUseDevFixtures() {
6398
- if (typeof window === "undefined") return false;
6399
- const raw = readPreservedSearch();
6400
- const q = raw.startsWith("?") ? raw.slice(1) : raw;
6401
- return new URLSearchParams(q).get("ohw-fixtures") === "1";
6402
- }
6403
-
6404
- // src/lib/nav-items.ts
6405
- var NAV_COUNT_KEY = "__ohw_nav_count";
6406
- var NAV_ORDER_KEY = "__ohw_nav_order";
6407
- function getLinkHref(el) {
6408
- const key = el.getAttribute("data-ohw-href-key");
6409
- if (key) {
6410
- const stored = getStoredLinkHref(key);
6411
- if (stored !== void 0) return stored;
6412
- }
6413
- return el.getAttribute("href") ?? "";
6414
- }
6415
- function isNavbarButton(el) {
6416
- return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
6417
- }
6418
- function isNavbarLinkItem(el) {
6419
- if (!el.matches("[data-ohw-href-key]")) return false;
6420
- if (isNavbarButton(el)) return false;
6421
- if (!el.querySelector('[data-ohw-editable="text"]')) return false;
6422
- return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
6423
- }
6424
- function getNavbarDesktopContainer() {
6425
- return document.querySelector("[data-ohw-nav-container]");
6426
- }
6427
- function getNavbarDrawerContainer() {
6428
- return document.querySelector("[data-ohw-nav-drawer]");
6429
- }
6430
- function listNavbarItems() {
6431
- const desktop = getNavbarDesktopContainer();
6432
- if (desktop) {
6433
- return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6434
- }
6435
- const drawer = getNavbarDrawerContainer();
6436
- if (drawer) {
6437
- return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6438
- }
6439
- return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
6440
- }
6441
- function parseNavIndexFromKey(key) {
6442
- if (!key) return null;
6443
- const match = key.match(/^nav-(\d+)-href$/);
6444
- if (!match) return null;
6445
- return parseInt(match[1], 10);
6446
- }
6447
- function getNavbarIndicesInDom() {
6448
- const indices = /* @__PURE__ */ new Set();
6449
- for (const item of listNavbarItems()) {
6450
- const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
6451
- if (idx !== null) indices.add(idx);
6452
- }
6453
- return [...indices].sort((a, b) => a - b);
6454
- }
6455
- function getNextNavbarIndex() {
6456
- const indices = getNavbarIndicesInDom();
6457
- if (indices.length === 0) return 0;
6458
- return Math.max(...indices) + 1;
6459
- }
6460
- function getNavbarExistingTargets() {
6461
- return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
6462
- }
6463
- function getNavOrderFromDom() {
6464
- return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6465
- }
6466
- function parseNavOrder(content) {
6467
- const raw = content[NAV_ORDER_KEY];
6468
- if (!raw) return null;
6469
- try {
6470
- const parsed = JSON.parse(raw);
6471
- if (!Array.isArray(parsed)) return null;
6472
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6473
- } catch {
6474
- return null;
6475
- }
6476
- }
6477
- function findCounterpartByHrefKey(hrefKey, root) {
6478
- return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6479
- }
6480
- function cloneNavLinkShell(template) {
6481
- const anchor = document.createElement("a");
6482
- if (template) {
6483
- anchor.style.cssText = template.style.cssText;
6484
- if (template.className) anchor.className = template.className;
6485
- }
6486
- anchor.style.cursor = "pointer";
6487
- anchor.style.textDecoration = "none";
6488
- return anchor;
6489
- }
6490
- function buildNavLink(index, href, label, template) {
6491
- const anchor = cloneNavLinkShell(template);
6492
- anchor.href = href;
6493
- anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
6494
- const span = document.createElement("span");
6495
- span.setAttribute("data-ohw-editable", "text");
6496
- span.setAttribute("data-ohw-key", `nav-${index}-label`);
6497
- span.textContent = label;
6498
- anchor.appendChild(span);
6499
- return anchor;
6500
- }
6501
- function insertNavLinkInContainer(container, anchor, afterHrefKey) {
6502
- if (!afterHrefKey) {
6503
- container.appendChild(anchor);
6504
- return;
6505
- }
6506
- const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
6507
- if (afterEl?.parentElement === container) {
6508
- afterEl.insertAdjacentElement("afterend", anchor);
6509
- return;
6510
- }
6511
- container.appendChild(anchor);
6512
- }
6513
- function insertNavbarItemDom(index, href, label, afterAnchor) {
6514
- const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
6515
- const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6516
- const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6517
- const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
6518
- const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
6519
- let primary = null;
6520
- const desktopContainer = getNavbarDesktopContainer();
6521
- if (desktopContainer) {
6522
- const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
6523
- insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
6524
- primary = desktopAnchor;
6525
- }
6526
- const drawerContainer = getNavbarDrawerContainer();
6527
- if (drawerContainer) {
6528
- const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
6529
- insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
6530
- if (!primary) primary = drawerAnchor;
6531
- }
6532
- if (!primary) {
6533
- const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
6534
- const nav = document.querySelector("nav");
6535
- nav?.appendChild(fallback);
6536
- primary = fallback;
6537
- }
6538
- return primary;
6539
- }
6540
- function applyNavOrderToContainer(container, order) {
6541
- const seen = /* @__PURE__ */ new Set();
6542
- for (const hrefKey of order) {
6543
- if (seen.has(hrefKey)) continue;
6544
- seen.add(hrefKey);
6545
- const el = findCounterpartByHrefKey(hrefKey, container);
6546
- if (el) container.appendChild(el);
6547
- }
6548
- for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6549
- if (!isNavbarLinkItem(el)) continue;
6550
- const key = el.getAttribute("data-ohw-href-key");
6551
- if (!key || seen.has(key)) continue;
6552
- container.appendChild(el);
6553
- }
6554
- }
6555
- function applyNavOrder(order) {
6556
- const desktop = getNavbarDesktopContainer();
6557
- if (desktop) applyNavOrderToContainer(desktop, order);
6558
- const drawer = getNavbarDrawerContainer();
6559
- if (drawer) applyNavOrderToContainer(drawer, order);
6560
- }
6561
- function collectNavbarIndicesFromContent(content) {
6562
- const indices = /* @__PURE__ */ new Set();
6563
- for (const key of Object.keys(content)) {
6564
- const idx = parseNavIndexFromKey(key);
6565
- if (idx !== null) indices.add(idx);
6566
- }
6567
- const countRaw = content[NAV_COUNT_KEY];
6568
- if (countRaw) {
6569
- const count = parseInt(countRaw, 10);
6570
- if (Number.isFinite(count) && count > 0) {
6571
- for (let i = 0; i < count; i++) indices.add(i);
6572
- }
6573
- }
6574
- return [...indices].sort((a, b) => a - b);
6575
- }
6576
- function navbarIndexExistsInDom(index) {
6577
- return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
6578
- }
6579
- function reconcileNavbarItemsFromContent(content) {
6580
- const indices = collectNavbarIndicesFromContent(content);
6581
- for (const index of indices) {
6582
- if (navbarIndexExistsInDom(index)) continue;
6583
- const href = content[`nav-${index}-href`];
6584
- const label = content[`nav-${index}-label`];
6585
- if (!href && !label) continue;
6586
- insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6587
- }
6588
- const order = parseNavOrder(content);
6589
- if (order?.length) applyNavOrder(order);
6590
- }
6591
- function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6592
- const order = getNavOrderFromDom();
6593
- if (!afterAnchor) {
6594
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6595
- return order;
6596
- }
6597
- const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
6598
- if (!afterKey) {
6599
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6600
- return order;
6601
- }
6602
- const withoutNew = order.filter((key) => key !== newHrefKey);
6603
- const pos = withoutNew.indexOf(afterKey);
6604
- if (pos >= 0) {
6605
- withoutNew.splice(pos + 1, 0, newHrefKey);
6606
- return withoutNew;
6607
- }
6608
- withoutNew.push(newHrefKey);
6609
- return withoutNew;
6610
- }
6611
- function insertNavbarItem(href, label, afterAnchor = null) {
6612
- const index = getNextNavbarIndex();
6613
- const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
6614
- if (!anchor) throw new Error("Failed to insert navbar item");
6615
- const hrefKey = `nav-${index}-href`;
6616
- const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
6617
- applyNavOrder(order);
6618
- return {
6619
- anchor,
6620
- index,
6621
- hrefKey,
6622
- labelKey: `nav-${index}-label`,
6623
- href,
6624
- label,
6625
- order
6626
- };
6627
- }
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
-
7026
- // src/ui/navbar-container-chrome.tsx
7027
- var import_lucide_react11 = require("lucide-react");
7028
- var import_jsx_runtime22 = require("react/jsx-runtime");
7029
- function NavbarContainerChrome({
7030
- rect,
7031
- onAdd
7032
- }) {
7033
- const chromeGap = 6;
7034
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7035
- "div",
7036
- {
7037
- "data-ohw-navbar-container-chrome": "",
7038
- "data-ohw-bridge": "",
7039
- className: "pointer-events-none fixed z-[2147483647]",
7040
- style: {
7041
- top: rect.top - chromeGap,
7042
- left: rect.left - chromeGap,
7043
- width: rect.width + chromeGap * 2,
7044
- height: rect.height + chromeGap * 2
7045
- },
7046
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7047
- "button",
7048
- {
7049
- type: "button",
7050
- "data-ohw-navbar-add-button": "",
7051
- className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
7052
- style: { top: "70%" },
7053
- "aria-label": "Add item",
7054
- onMouseDown: (e) => {
7055
- e.preventDefault();
7056
- e.stopPropagation();
7057
- },
7058
- onClick: (e) => {
7059
- e.preventDefault();
7060
- e.stopPropagation();
7061
- onAdd();
7062
- },
7063
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7064
- }
7065
- )
7066
- }
7067
- );
5747
+ { id: "manifesto", label: "Manifesto" },
5748
+ { id: "story-letter", label: "Our Story" },
5749
+ { id: "lagree-explainer", label: "Lagree Explainer" },
5750
+ { id: "waitlist-cta", label: "Waitlist" },
5751
+ { id: "personal-training", label: "Personal training" }
5752
+ ],
5753
+ "/classes": [{ id: "class-library", label: "Class Library" }],
5754
+ "/pricing": [
5755
+ { id: "page-header", label: "Page Header" },
5756
+ { id: "free-class-cta", label: "Free Class" },
5757
+ { id: "benefits-marquee", label: "Benefits" },
5758
+ { id: "monthly-memberships", label: "Memberships" },
5759
+ { id: "specialty-programs", label: "Specialty Programs" },
5760
+ { id: "package-matcher", label: "Packages" },
5761
+ { id: "wordmark", label: "Wordmark" }
5762
+ ],
5763
+ "/studio": [
5764
+ { id: "studio-intro", label: "Studio Intro" },
5765
+ { id: "studio-features", label: "Studio Features" },
5766
+ { id: "studio-gallery", label: "Studio Gallery" },
5767
+ { id: "studio-visit", label: "Visit Studio" }
5768
+ ],
5769
+ "/instructors": [{ id: "instructor-grid", label: "Instructors" }],
5770
+ "/contact": [
5771
+ { id: "hello-hero", label: "Hello" },
5772
+ { id: "contact-form", label: "Contact Form" },
5773
+ { id: "faq", label: "FAQ" }
5774
+ ]
5775
+ };
5776
+ function shouldUseDevFixtures() {
5777
+ if (typeof window === "undefined") return false;
5778
+ const raw = readPreservedSearch();
5779
+ const q = raw.startsWith("?") ? raw.slice(1) : raw;
5780
+ return new URLSearchParams(q).get("ohw-fixtures") === "1";
7068
5781
  }
7069
5782
 
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
-
7112
5783
  // src/ui/badge.tsx
7113
- var import_jsx_runtime24 = require("react/jsx-runtime");
5784
+ var import_jsx_runtime21 = require("react/jsx-runtime");
7114
5785
  var badgeVariants = cva(
7115
5786
  "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",
7116
5787
  {
@@ -7128,12 +5799,12 @@ var badgeVariants = cva(
7128
5799
  }
7129
5800
  );
7130
5801
  function Badge({ className, variant, ...props }) {
7131
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5802
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
7132
5803
  }
7133
5804
 
7134
5805
  // src/OhhwellsBridge.tsx
7135
- var import_lucide_react12 = require("lucide-react");
7136
- var import_jsx_runtime25 = require("react/jsx-runtime");
5806
+ var import_lucide_react4 = require("lucide-react");
5807
+ var import_jsx_runtime22 = require("react/jsx-runtime");
7137
5808
  var PRIMARY2 = "#0885FE";
7138
5809
  var IMAGE_FADE_MS = 300;
7139
5810
  function runOpacityFade(el, onDone) {
@@ -7310,9 +5981,9 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
7310
5981
  tail.insertAdjacentElement("afterend", container);
7311
5982
  }
7312
5983
  const root = (0, import_client.createRoot)(container);
7313
- (0, import_react_dom2.flushSync)(() => {
5984
+ (0, import_react_dom.flushSync)(() => {
7314
5985
  root.render(
7315
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5986
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7316
5987
  SchedulingWidget,
7317
5988
  {
7318
5989
  notifyOnConnect,
@@ -7352,7 +6023,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
7352
6023
  }
7353
6024
  }
7354
6025
  }
7355
- function getLinkHref2(el) {
6026
+ function getLinkHref(el) {
7356
6027
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
7357
6028
  return anchor?.getAttribute("href") ?? "";
7358
6029
  }
@@ -7374,7 +6045,7 @@ function isDragHandleDisabled(el) {
7374
6045
  return raw === "true" || raw === "";
7375
6046
  });
7376
6047
  }
7377
- function collectEditableNodes(extraContent) {
6048
+ function collectEditableNodes() {
7378
6049
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
7379
6050
  if (el.dataset.ohwEditable === "image") {
7380
6051
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7389,7 +6060,7 @@ function collectEditableNodes(extraContent) {
7389
6060
  return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
7390
6061
  }
7391
6062
  if (el.dataset.ohwEditable === "link") {
7392
- return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
6063
+ return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
7393
6064
  }
7394
6065
  return {
7395
6066
  key: el.dataset.ohwKey ?? "",
@@ -7400,16 +6071,8 @@ function collectEditableNodes(extraContent) {
7400
6071
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
7401
6072
  const key = el.getAttribute("data-ohw-href-key") ?? "";
7402
6073
  if (!key) return;
7403
- nodes.push({ key, type: "link", text: getLinkHref2(el) });
6074
+ nodes.push({ key, type: "link", text: getLinkHref(el) });
7404
6075
  });
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
- }
7413
6076
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
7414
6077
  const key = el.dataset.ohwKey ?? "";
7415
6078
  const video = getVideoEl(el);
@@ -7417,9 +6080,6 @@ function collectEditableNodes(extraContent) {
7417
6080
  nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
7418
6081
  nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
7419
6082
  });
7420
- for (const key of listCarouselKeys()) {
7421
- nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
7422
- }
7423
6083
  return nodes;
7424
6084
  }
7425
6085
  function isMediaEditable(el) {
@@ -7497,7 +6157,7 @@ function applyLinkByKey(key, val) {
7497
6157
  }
7498
6158
  function isInsideLinkEditor(target) {
7499
6159
  return Boolean(
7500
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
6160
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
7501
6161
  );
7502
6162
  }
7503
6163
  function getHrefKeyFromElement(el) {
@@ -7508,30 +6168,7 @@ function getHrefKeyFromElement(el) {
7508
6168
  if (!key) return null;
7509
6169
  return { anchor, key };
7510
6170
  }
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
- }
7534
- function isNavbarButton2(el) {
6171
+ function isNavbarButton(el) {
7535
6172
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
7536
6173
  }
7537
6174
  function getNavigationItemAnchor(el) {
@@ -7543,8 +6180,19 @@ function getNavigationItemAnchor(el) {
7543
6180
  function isNavigationItem(el) {
7544
6181
  return getNavigationItemAnchor(el) !== null;
7545
6182
  }
6183
+ function getNavigationItemAddHintTarget(anchor) {
6184
+ const items = listNavigationItems();
6185
+ const idx = items.indexOf(anchor);
6186
+ if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
6187
+ return anchor;
6188
+ }
6189
+ function listNavigationItems() {
6190
+ return Array.from(
6191
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
6192
+ ).filter((el) => isNavigationItem(el));
6193
+ }
7546
6194
  function getNavigationItemReorderState(anchor) {
7547
- if (isNavbarButton2(anchor)) return { key: null, disabled: false };
6195
+ if (isNavbarButton(anchor)) return { key: null, disabled: false };
7548
6196
  return getReorderHandleStateFromAnchor(anchor);
7549
6197
  }
7550
6198
  function getReorderHandleState(el) {
@@ -7566,120 +6214,6 @@ function clearHrefKeyHover(anchor) {
7566
6214
  function isInsideNavigationItem(el) {
7567
6215
  return getNavigationItemAnchor(el) !== null;
7568
6216
  }
7569
- function getNavigationRoot(el) {
7570
- const explicit = el.closest("[data-ohw-nav-root]");
7571
- if (explicit) return explicit;
7572
- return el.closest("nav, footer, aside");
7573
- }
7574
- function findFooterItemGroup(item) {
7575
- const footer = item.closest("footer");
7576
- if (!footer) return null;
7577
- let node = item.parentElement;
7578
- while (node && node !== footer) {
7579
- const hasDirectNavItems = Array.from(
7580
- node.querySelectorAll(":scope > [data-ohw-href-key]")
7581
- ).some(isNavigationItem);
7582
- if (hasDirectNavItems) return node;
7583
- node = node.parentElement;
7584
- }
7585
- return null;
7586
- }
7587
- function isNavigationRoot(el) {
7588
- return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
7589
- }
7590
- function isInferredFooterGroup(el) {
7591
- const footer = el.closest("footer");
7592
- if (!footer || el === footer) return false;
7593
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
7594
- }
7595
- function isNavigationContainer(el) {
7596
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
7597
- }
7598
- function isPointOverNavigation(x, y) {
7599
- const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
7600
- if (!navRoot) return false;
7601
- const r2 = navRoot.getBoundingClientRect();
7602
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
7603
- }
7604
- function isPointOverNavItem(container, x, y) {
7605
- return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
7606
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
7607
- const itemRect = el.getBoundingClientRect();
7608
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
7609
- });
7610
- }
7611
- function resolveNavContainerSelectionTarget(target, clientX, clientY) {
7612
- if (getNavigationItemAnchor(target)) return null;
7613
- if (target.closest('[data-ohw-role="navbar-button"]')) return null;
7614
- const plainLink = target.closest("a");
7615
- if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
7616
- return null;
7617
- }
7618
- const fromClosest = target.closest("[data-ohw-nav-container]");
7619
- if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
7620
- return fromClosest;
7621
- }
7622
- const navRoot = target.closest("[data-ohw-nav-root]");
7623
- if (!navRoot) return null;
7624
- const container = navRoot.querySelector("[data-ohw-nav-container]");
7625
- if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
7626
- if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
7627
- return container;
7628
- }
7629
- return null;
7630
- }
7631
- function getNavigationSelectionParent(el) {
7632
- if (isNavigationItem(el)) {
7633
- const explicit = el.closest("[data-ohw-nav-container]");
7634
- if (explicit) return explicit;
7635
- const footerGroup = findFooterItemGroup(el);
7636
- if (footerGroup) return footerGroup;
7637
- return getNavigationRoot(el);
7638
- }
7639
- if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
7640
- return getNavigationRoot(el);
7641
- }
7642
- return null;
7643
- }
7644
- function placeCaretAtPoint(el, x, y) {
7645
- const selection = window.getSelection();
7646
- if (!selection) return;
7647
- if (typeof document.caretRangeFromPoint === "function") {
7648
- const range = document.caretRangeFromPoint(x, y);
7649
- if (range && el.contains(range.startContainer)) {
7650
- selection.removeAllRanges();
7651
- selection.addRange(range);
7652
- return;
7653
- }
7654
- }
7655
- const caretPositionFromPoint = document.caretPositionFromPoint;
7656
- if (typeof caretPositionFromPoint === "function") {
7657
- const position = caretPositionFromPoint(x, y);
7658
- if (position && el.contains(position.offsetNode)) {
7659
- const range = document.createRange();
7660
- range.setStart(position.offsetNode, position.offset);
7661
- range.collapse(true);
7662
- selection.removeAllRanges();
7663
- selection.addRange(range);
7664
- }
7665
- }
7666
- }
7667
- function selectAllTextInEditable(el) {
7668
- const selection = window.getSelection();
7669
- if (!selection) return;
7670
- const range = document.createRange();
7671
- range.selectNodeContents(el);
7672
- selection.removeAllRanges();
7673
- selection.addRange(range);
7674
- }
7675
- function getNavigationLabelEditable(target) {
7676
- const editable = target.closest('[data-ohw-editable="text"]');
7677
- if (!editable) return null;
7678
- const hrefCtx = getHrefKeyFromElement(editable);
7679
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
7680
- if (!navAnchor) return null;
7681
- return { editable, navAnchor };
7682
- }
7683
6217
  function collectSections() {
7684
6218
  return collectSectionsFromDom();
7685
6219
  }
@@ -7803,8 +6337,6 @@ var ICONS = {
7803
6337
  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"/>',
7804
6338
  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"/>'
7805
6339
  };
7806
- var TOOLBAR_PILL_PADDING = 2;
7807
- var TOOLBAR_TOGGLE_GAP = 4;
7808
6340
  var TOOLBAR_GROUPS = [
7809
6341
  [
7810
6342
  { cmd: "bold", title: "Bold" },
@@ -7829,7 +6361,7 @@ function EditGlowChrome({
7829
6361
  dragDisabled = false
7830
6362
  }) {
7831
6363
  const GAP = 6;
7832
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6364
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
7833
6365
  "div",
7834
6366
  {
7835
6367
  ref: elRef,
@@ -7844,7 +6376,7 @@ function EditGlowChrome({
7844
6376
  zIndex: 2147483646
7845
6377
  },
7846
6378
  children: [
7847
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6379
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7848
6380
  "div",
7849
6381
  {
7850
6382
  style: {
@@ -7857,7 +6389,7 @@ function EditGlowChrome({
7857
6389
  }
7858
6390
  }
7859
6391
  ),
7860
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6392
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7861
6393
  "div",
7862
6394
  {
7863
6395
  "data-ohw-drag-handle-container": "",
@@ -7869,7 +6401,7 @@ function EditGlowChrome({
7869
6401
  transform: "translate(calc(-100% - 7px), -50%)",
7870
6402
  pointerEvents: dragDisabled ? "none" : "auto"
7871
6403
  },
7872
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6404
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7873
6405
  DragHandle,
7874
6406
  {
7875
6407
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7973,7 +6505,7 @@ function FloatingToolbar({
7973
6505
  onEditLink
7974
6506
  }) {
7975
6507
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7976
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6508
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7977
6509
  "div",
7978
6510
  {
7979
6511
  ref: elRef,
@@ -7983,23 +6515,14 @@ function FloatingToolbar({
7983
6515
  left,
7984
6516
  transform,
7985
6517
  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",
7995
6518
  pointerEvents: "auto"
7996
6519
  },
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, {}),
6520
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CustomToolbar, { children: [
6521
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react6.default.Fragment, { children: [
6522
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarDivider, {}),
8000
6523
  btns.map((btn) => {
8001
6524
  const isActive = activeCommands.has(btn.cmd);
8002
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6525
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8003
6526
  CustomToolbarButton,
8004
6527
  {
8005
6528
  title: btn.title,
@@ -8008,7 +6531,7 @@ function FloatingToolbar({
8008
6531
  e.preventDefault();
8009
6532
  onCommand(btn.cmd);
8010
6533
  },
8011
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6534
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8012
6535
  "svg",
8013
6536
  {
8014
6537
  width: "16",
@@ -8029,7 +6552,7 @@ function FloatingToolbar({
8029
6552
  );
8030
6553
  })
8031
6554
  ] }, gi)),
8032
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6555
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8033
6556
  CustomToolbarButton,
8034
6557
  {
8035
6558
  type: "button",
@@ -8043,7 +6566,7 @@ function FloatingToolbar({
8043
6566
  e.preventDefault();
8044
6567
  e.stopPropagation();
8045
6568
  },
8046
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Link, { className: "size-4 shrink-0", "aria-hidden": true })
6569
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react4.Link, { className: "size-4 shrink-0", "aria-hidden": true })
8047
6570
  }
8048
6571
  ) : null
8049
6572
  ] })
@@ -8060,7 +6583,7 @@ function StateToggle({
8060
6583
  states,
8061
6584
  onStateChange
8062
6585
  }) {
8063
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6586
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8064
6587
  ToggleGroup,
8065
6588
  {
8066
6589
  "data-ohw-state-toggle": "",
@@ -8074,7 +6597,7 @@ function StateToggle({
8074
6597
  left: rect.right - 8,
8075
6598
  transform: "translateX(-100%)"
8076
6599
  },
8077
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6600
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8078
6601
  }
8079
6602
  );
8080
6603
  }
@@ -8097,12 +6620,12 @@ function resolveSubdomain(subdomainFromQuery) {
8097
6620
  return "";
8098
6621
  }
8099
6622
  function OhhwellsBridge() {
8100
- const pathname = (0, import_navigation2.usePathname)();
8101
- const router = (0, import_navigation2.useRouter)();
8102
- const searchParams = (0, import_navigation2.useSearchParams)();
6623
+ const pathname = (0, import_navigation.usePathname)();
6624
+ const router = (0, import_navigation.useRouter)();
6625
+ const searchParams = (0, import_navigation.useSearchParams)();
8103
6626
  const isEditMode = isEditSessionActive();
8104
- const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
8105
- (0, import_react9.useEffect)(() => {
6627
+ const [bridgeRoot, setBridgeRoot] = (0, import_react6.useState)(null);
6628
+ (0, import_react6.useEffect)(() => {
8106
6629
  const figtreeFontId = "ohw-figtree-font";
8107
6630
  if (!document.getElementById(figtreeFontId)) {
8108
6631
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -8130,100 +6653,83 @@ function OhhwellsBridge() {
8130
6653
  const subdomainFromQuery = searchParams.get("subdomain");
8131
6654
  const subdomain = resolveSubdomain(subdomainFromQuery);
8132
6655
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
8133
- const postToParent2 = (0, import_react9.useCallback)((data) => {
6656
+ const postToParent = (0, import_react6.useCallback)((data) => {
8134
6657
  if (typeof window !== "undefined" && window.parent !== window) {
8135
6658
  window.parent.postMessage(data, "*");
8136
6659
  }
8137
6660
  }, []);
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) => {
6661
+ const [fetchState, setFetchState] = (0, import_react6.useState)("idle");
6662
+ const autoSaveTimers = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
6663
+ const activeElRef = (0, import_react6.useRef)(null);
6664
+ const selectedElRef = (0, import_react6.useRef)(null);
6665
+ const originalContentRef = (0, import_react6.useRef)(null);
6666
+ const activeStateElRef = (0, import_react6.useRef)(null);
6667
+ const parentScrollRef = (0, import_react6.useRef)(null);
6668
+ const visibleViewportRef = (0, import_react6.useRef)(null);
6669
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react6.useState)(null);
6670
+ const attachVisibleViewport = (0, import_react6.useCallback)((node) => {
8148
6671
  visibleViewportRef.current = node;
8149
6672
  setDialogPortalContainer(node);
8150
6673
  if (node) applyVisibleViewport(node, parentScrollRef.current);
8151
6674
  }, []);
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 hoveredGapRef = (0, import_react9.useRef)(null);
8161
- const imageUnhoverTimerRef = (0, import_react9.useRef)(null);
8162
- const imageShowTimerRef = (0, import_react9.useRef)(null);
8163
- const editStylesRef = (0, import_react9.useRef)(null);
8164
- const activateRef = (0, import_react9.useRef)(() => {
6675
+ const toolbarElRef = (0, import_react6.useRef)(null);
6676
+ const glowElRef = (0, import_react6.useRef)(null);
6677
+ const hoveredImageRef = (0, import_react6.useRef)(null);
6678
+ const hoveredImageHasTextOverlapRef = (0, import_react6.useRef)(false);
6679
+ const dragOverElRef = (0, import_react6.useRef)(null);
6680
+ const [mediaHover, setMediaHover] = (0, import_react6.useState)(null);
6681
+ const [uploadingRects, setUploadingRects] = (0, import_react6.useState)({});
6682
+ const hoveredGapRef = (0, import_react6.useRef)(null);
6683
+ const imageUnhoverTimerRef = (0, import_react6.useRef)(null);
6684
+ const imageShowTimerRef = (0, import_react6.useRef)(null);
6685
+ const editStylesRef = (0, import_react6.useRef)(null);
6686
+ const activateRef = (0, import_react6.useRef)(() => {
8165
6687
  });
8166
- const deactivateRef = (0, import_react9.useRef)(() => {
6688
+ const deactivateRef = (0, import_react6.useRef)(() => {
8167
6689
  });
8168
- const selectRef = (0, import_react9.useRef)(() => {
6690
+ const selectRef = (0, import_react6.useRef)(() => {
8169
6691
  });
8170
- const selectFrameRef = (0, import_react9.useRef)(() => {
6692
+ const deselectRef = (0, import_react6.useRef)(() => {
8171
6693
  });
8172
- const deselectRef = (0, import_react9.useRef)(() => {
6694
+ const reselectNavigationItemRef = (0, import_react6.useRef)(() => {
8173
6695
  });
8174
- const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
6696
+ const commitNavigationTextEditRef = (0, import_react6.useRef)(() => {
8175
6697
  });
8176
- const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
6698
+ const refreshActiveCommandsRef = (0, import_react6.useRef)(() => {
8177
6699
  });
8178
- const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
8179
- });
8180
- const postToParentRef = (0, import_react9.useRef)(postToParent2);
8181
- postToParentRef.current = postToParent2;
8182
- const sectionsLoadedRef = (0, import_react9.useRef)(false);
8183
- const pendingScheduleConfigRequests = (0, import_react9.useRef)([]);
8184
- const [toolbarRect, setToolbarRect] = (0, import_react9.useState)(null);
8185
- const [toolbarVariant, setToolbarVariant] = (0, import_react9.useState)("none");
8186
- const toolbarVariantRef = (0, import_react9.useRef)("none");
6700
+ const postToParentRef = (0, import_react6.useRef)(postToParent);
6701
+ postToParentRef.current = postToParent;
6702
+ const sectionsLoadedRef = (0, import_react6.useRef)(false);
6703
+ const pendingScheduleConfigRequests = (0, import_react6.useRef)([]);
6704
+ const [toolbarRect, setToolbarRect] = (0, import_react6.useState)(null);
6705
+ const [toolbarVariant, setToolbarVariant] = (0, import_react6.useState)("none");
6706
+ const toolbarVariantRef = (0, import_react6.useRef)("none");
8187
6707
  toolbarVariantRef.current = toolbarVariant;
8188
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react9.useState)(null);
8189
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react9.useState)(false);
8190
- const [toggleState, setToggleState] = (0, import_react9.useState)(null);
8191
- const [maxBadge, setMaxBadge] = (0, import_react9.useState)(null);
8192
- const [activeCommands, setActiveCommands] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
8193
- const [sectionGap, setSectionGap] = (0, import_react9.useState)(null);
8194
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react9.useState)(false);
8195
- const hoveredNavContainerRef = (0, import_react9.useRef)(null);
8196
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react9.useState)(null);
8197
- const hoveredItemElRef = (0, import_react9.useRef)(null);
8198
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react9.useState)(null);
8199
- const siblingHintElRef = (0, import_react9.useRef)(null);
8200
- const [siblingHintRect, setSiblingHintRect] = (0, import_react9.useState)(null);
8201
- const [siblingHintRects, setSiblingHintRects] = (0, import_react9.useState)([]);
8202
- const [isItemDragging, setIsItemDragging] = (0, import_react9.useState)(false);
8203
- const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react9.useState)(false);
8204
- const footerDragRef = (0, import_react9.useRef)(null);
8205
- const [footerDropSlots, setFooterDropSlots] = (0, import_react9.useState)([]);
8206
- const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react9.useState)(null);
8207
- const [draggedItemRect, setDraggedItemRect] = (0, import_react9.useState)(null);
8208
- const footerPointerDragRef = (0, import_react9.useRef)(null);
8209
- const suppressNextClickRef = (0, import_react9.useRef)(false);
8210
- const [linkPopover, setLinkPopover] = (0, import_react9.useState)(null);
8211
- const linkPopoverSessionRef = (0, import_react9.useRef)(null);
8212
- const addNavAfterAnchorRef = (0, import_react9.useRef)(null);
8213
- const editContentRef = (0, import_react9.useRef)({});
8214
- const [sitePages, setSitePages] = (0, import_react9.useState)([]);
8215
- const [sectionsByPath, setSectionsByPath] = (0, import_react9.useState)({});
8216
- const sectionsPrefetchGenRef = (0, import_react9.useRef)(0);
8217
- const setLinkPopoverRef = (0, import_react9.useRef)(setLinkPopover);
8218
- const linkPopoverPanelRef = (0, import_react9.useRef)(null);
8219
- const linkPopoverOpenRef = (0, import_react9.useRef)(false);
8220
- const linkPopoverGraceUntilRef = (0, import_react9.useRef)(0);
6708
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react6.useState)(null);
6709
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react6.useState)(false);
6710
+ const [toggleState, setToggleState] = (0, import_react6.useState)(null);
6711
+ const [maxBadge, setMaxBadge] = (0, import_react6.useState)(null);
6712
+ const [activeCommands, setActiveCommands] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
6713
+ const [sectionGap, setSectionGap] = (0, import_react6.useState)(null);
6714
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react6.useState)(false);
6715
+ const hoveredItemElRef = (0, import_react6.useRef)(null);
6716
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react6.useState)(null);
6717
+ const siblingHintElRef = (0, import_react6.useRef)(null);
6718
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react6.useState)(null);
6719
+ const [isItemDragging, setIsItemDragging] = (0, import_react6.useState)(false);
6720
+ const [linkPopover, setLinkPopover] = (0, import_react6.useState)(null);
6721
+ const [sitePages, setSitePages] = (0, import_react6.useState)([]);
6722
+ const [sectionsByPath, setSectionsByPath] = (0, import_react6.useState)({});
6723
+ const sectionsPrefetchGenRef = (0, import_react6.useRef)(0);
6724
+ const setLinkPopoverRef = (0, import_react6.useRef)(setLinkPopover);
6725
+ const linkPopoverPanelRef = (0, import_react6.useRef)(null);
6726
+ const linkPopoverOpenRef = (0, import_react6.useRef)(false);
6727
+ const linkPopoverGraceUntilRef = (0, import_react6.useRef)(0);
8221
6728
  setLinkPopoverRef.current = setLinkPopover;
8222
- linkPopoverSessionRef.current = linkPopover;
8223
6729
  const bumpLinkPopoverGrace = () => {
8224
6730
  linkPopoverGraceUntilRef.current = Date.now() + 350;
8225
6731
  };
8226
- const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
6732
+ const runSectionsPrefetch = (0, import_react6.useCallback)((pages) => {
8227
6733
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
8228
6734
  const gen = ++sectionsPrefetchGenRef.current;
8229
6735
  const paths = pages.map((p) => p.path);
@@ -8242,9 +6748,9 @@ function OhhwellsBridge() {
8242
6748
  );
8243
6749
  });
8244
6750
  }, [isEditMode, pathname]);
8245
- const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
6751
+ const runSectionsPrefetchRef = (0, import_react6.useRef)(runSectionsPrefetch);
8246
6752
  runSectionsPrefetchRef.current = runSectionsPrefetch;
8247
- (0, import_react9.useEffect)(() => {
6753
+ (0, import_react6.useEffect)(() => {
8248
6754
  if (!linkPopover) return;
8249
6755
  if (hoveredImageRef.current) {
8250
6756
  hoveredImageRef.current = null;
@@ -8252,9 +6758,33 @@ function OhhwellsBridge() {
8252
6758
  }
8253
6759
  hoveredGapRef.current = null;
8254
6760
  setSectionGap(null);
8255
- postToParent2({ type: "ow:image-unhover" });
8256
- }, [linkPopover, postToParent2]);
8257
- (0, import_react9.useEffect)(() => {
6761
+ setMediaHover(null);
6762
+ postToParent({ type: "ow:link-modal-lock", locked: true });
6763
+ const html = document.documentElement;
6764
+ const body = document.body;
6765
+ const prevHtmlOverflow = html.style.overflow;
6766
+ const prevBodyOverflow = body.style.overflow;
6767
+ html.style.overflow = "hidden";
6768
+ body.style.overflow = "hidden";
6769
+ const preventBackgroundScroll = (e) => {
6770
+ const target = e.target;
6771
+ if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
6772
+ return;
6773
+ }
6774
+ e.preventDefault();
6775
+ };
6776
+ const scrollOpts = { passive: false, capture: true };
6777
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6778
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6779
+ return () => {
6780
+ postToParent({ type: "ow:link-modal-lock", locked: false });
6781
+ html.style.overflow = prevHtmlOverflow;
6782
+ body.style.overflow = prevBodyOverflow;
6783
+ document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6784
+ document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6785
+ };
6786
+ }, [linkPopover, postToParent]);
6787
+ (0, import_react6.useEffect)(() => {
8258
6788
  if (!isEditMode) return;
8259
6789
  const useFixtures = shouldUseDevFixtures();
8260
6790
  if (useFixtures) {
@@ -8275,17 +6805,17 @@ function OhhwellsBridge() {
8275
6805
  runSectionsPrefetchRef.current(mapped);
8276
6806
  };
8277
6807
  window.addEventListener("message", onSitePages);
8278
- if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6808
+ if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
8279
6809
  return () => window.removeEventListener("message", onSitePages);
8280
- }, [isEditMode, postToParent2]);
8281
- (0, import_react9.useEffect)(() => {
6810
+ }, [isEditMode, postToParent]);
6811
+ (0, import_react6.useEffect)(() => {
8282
6812
  if (!isEditMode || shouldUseDevFixtures()) return;
8283
6813
  void loadAllSectionsManifest().then((manifest) => {
8284
6814
  if (Object.keys(manifest).length === 0) return;
8285
6815
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
8286
6816
  });
8287
6817
  }, [isEditMode]);
8288
- (0, import_react9.useEffect)(() => {
6818
+ (0, import_react6.useEffect)(() => {
8289
6819
  const update = () => {
8290
6820
  const el = activeElRef.current ?? selectedElRef.current;
8291
6821
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -8309,10 +6839,10 @@ function OhhwellsBridge() {
8309
6839
  vvp.removeEventListener("resize", update);
8310
6840
  };
8311
6841
  }, []);
8312
- const refreshStateRules = (0, import_react9.useCallback)(() => {
6842
+ const refreshStateRules = (0, import_react6.useCallback)(() => {
8313
6843
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
8314
6844
  }, []);
8315
- const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
6845
+ const processConfigRequest = (0, import_react6.useCallback)((insertAfterVal) => {
8316
6846
  const tracker = getSectionsTracker();
8317
6847
  let entries = [];
8318
6848
  try {
@@ -8335,7 +6865,7 @@ function OhhwellsBridge() {
8335
6865
  }
8336
6866
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
8337
6867
  }, [isEditMode]);
8338
- const deactivate = (0, import_react9.useCallback)(() => {
6868
+ const deactivate = (0, import_react6.useCallback)(() => {
8339
6869
  const el = activeElRef.current;
8340
6870
  if (!el) return;
8341
6871
  const key = el.dataset.ohwKey;
@@ -8364,25 +6894,21 @@ function OhhwellsBridge() {
8364
6894
  setMaxBadge(null);
8365
6895
  setActiveCommands(/* @__PURE__ */ new Set());
8366
6896
  setToolbarShowEditLink(false);
8367
- postToParent2({ type: "ow:exit-edit" });
8368
- }, [postToParent2]);
8369
- const deselect = (0, import_react9.useCallback)(() => {
6897
+ postToParent({ type: "ow:exit-edit" });
6898
+ }, [postToParent]);
6899
+ const deselect = (0, import_react6.useCallback)(() => {
8370
6900
  selectedElRef.current = null;
8371
6901
  setReorderHrefKey(null);
8372
6902
  setReorderDragDisabled(false);
8373
- setIsFooterFrameSelection(false);
8374
6903
  siblingHintElRef.current = null;
8375
6904
  setSiblingHintRect(null);
8376
- setSiblingHintRects([]);
8377
6905
  setIsItemDragging(false);
8378
- hoveredNavContainerRef.current = null;
8379
- setHoveredNavContainerRect(null);
8380
6906
  if (!activeElRef.current) {
8381
6907
  setToolbarRect(null);
8382
6908
  setToolbarVariant("none");
8383
6909
  }
8384
6910
  }, []);
8385
- const reselectNavigationItem = (0, import_react9.useCallback)((navAnchor) => {
6911
+ const reselectNavigationItem = (0, import_react6.useCallback)((navAnchor) => {
8386
6912
  selectedElRef.current = navAnchor;
8387
6913
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
8388
6914
  setReorderHrefKey(key);
@@ -8392,7 +6918,7 @@ function OhhwellsBridge() {
8392
6918
  setToolbarShowEditLink(false);
8393
6919
  setActiveCommands(/* @__PURE__ */ new Set());
8394
6920
  }, []);
8395
- const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
6921
+ const commitNavigationTextEdit = (0, import_react6.useCallback)((navAnchor) => {
8396
6922
  const el = activeElRef.current;
8397
6923
  if (!el) return;
8398
6924
  const key = el.dataset.ohwKey;
@@ -8405,9 +6931,9 @@ function OhhwellsBridge() {
8405
6931
  const html = sanitizeHtml(el.innerHTML);
8406
6932
  const original = originalContentRef.current ?? "";
8407
6933
  if (html !== sanitizeHtml(original)) {
8408
- postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
6934
+ postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
8409
6935
  const h = document.documentElement.scrollHeight;
8410
- if (h > 50) postToParent2({ type: "ow:height", height: h });
6936
+ if (h > 50) postToParent({ type: "ow:height", height: h });
8411
6937
  }
8412
6938
  }
8413
6939
  el.removeAttribute("contenteditable");
@@ -8415,366 +6941,81 @@ function OhhwellsBridge() {
8415
6941
  setMaxBadge(null);
8416
6942
  setActiveCommands(/* @__PURE__ */ new Set());
8417
6943
  setToolbarShowEditLink(false);
8418
- postToParent2({ type: "ow:exit-edit" });
6944
+ postToParent({ type: "ow:exit-edit" });
8419
6945
  reselectNavigationItem(navAnchor);
8420
- }, [postToParent2, reselectNavigationItem]);
8421
- const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
8422
- const items = listNavbarItems();
8423
- addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
8424
- deselectRef.current();
8425
- deactivateRef.current();
8426
- bumpLinkPopoverGrace();
8427
- setLinkPopover({
8428
- key: "__add-nav__",
8429
- mode: "create",
8430
- intent: "add-nav"
8431
- });
8432
- }, []);
8433
- const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
8434
- footerDragRef.current = null;
8435
- setSiblingHintRects([]);
8436
- setFooterDropSlots([]);
8437
- setActiveFooterDropIndex(null);
8438
- setDraggedItemRect(null);
8439
- setIsItemDragging(false);
8440
- unlockFooterDragInteraction();
8441
- }, []);
8442
- const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
8443
- const dragged = session.draggedEl;
8444
- setDraggedItemRect(dragged.getBoundingClientRect());
8445
- if (typeof clientX === "number" && typeof clientY === "number") {
8446
- session.lastClientX = clientX;
8447
- session.lastClientY = clientY;
8448
- }
8449
- session.activeSlot = activeSlot;
8450
- if (session.kind === "link") {
8451
- const columns2 = listFooterColumns();
8452
- const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
8453
- if (activeSlot) focusCols.add(activeSlot.columnIndex);
8454
- const siblingRects = [];
8455
- columns2.forEach((col, i) => {
8456
- if (!focusCols.has(i)) return;
8457
- for (const link of listFooterLinksInColumn(col)) {
8458
- if (link === dragged) continue;
8459
- siblingRects.push(link.getBoundingClientRect());
8460
- }
8461
- });
8462
- setSiblingHintRects(siblingRects);
8463
- const slots2 = [];
8464
- columns2.forEach((col, i) => {
8465
- slots2.push(...buildLinkDropSlots(col, i));
8466
- });
8467
- setFooterDropSlots(slots2);
8468
- const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
8469
- setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
8470
- return;
8471
- }
8472
- const columns = listFooterColumns();
8473
- setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
8474
- const slots = buildColumnDropSlots();
8475
- setFooterDropSlots(slots);
8476
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
8477
- setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
8478
- }, []);
8479
- const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
8480
- refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
8481
- const commitFooterDragRef = (0, import_react9.useRef)(() => {
8482
- });
8483
- const beginFooterDragRef = (0, import_react9.useRef)(() => {
8484
- });
8485
- const beginFooterDrag = (0, import_react9.useCallback)(
8486
- (session) => {
8487
- const rect = session.draggedEl.getBoundingClientRect();
8488
- session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
8489
- session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
8490
- session.activeSlot = session.activeSlot ?? null;
8491
- footerDragRef.current = session;
8492
- setIsItemDragging(true);
8493
- lockFooterDuringDrag();
8494
- siblingHintElRef.current = null;
8495
- setSiblingHintRect(null);
8496
- if (session.wasSelected && selectedElRef.current === session.draggedEl) {
8497
- setToolbarRect(rect);
8498
- }
8499
- const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
8500
- refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
8501
- },
8502
- [refreshFooterDragVisuals]
8503
- );
8504
- beginFooterDragRef.current = beginFooterDrag;
8505
- const commitFooterDrag = (0, import_react9.useCallback)(
8506
- (clientX, clientY) => {
8507
- const session = footerDragRef.current;
8508
- if (!session) {
8509
- clearFooterDragVisuals();
8510
- return;
8511
- }
8512
- const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
8513
- const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
8514
- let nextOrder = null;
8515
- const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
8516
- if (session.kind === "link" && session.hrefKey && slot) {
8517
- nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
8518
- } else if (session.kind === "column" && slot) {
8519
- nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
8520
- }
8521
- const wasSelected = session.wasSelected;
8522
- const draggedEl = session.draggedEl;
8523
- const hrefKey = session.hrefKey;
8524
- let movedColumnIndex = null;
8525
- if (session.kind === "column" && slot && nextOrder) {
8526
- let adjusted = slot.insertIndex;
8527
- if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
8528
- movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
8529
- }
8530
- clearFooterDragVisuals();
8531
- if (nextOrder) {
8532
- const orderJson = JSON.stringify(nextOrder);
8533
- editContentRef.current = {
8534
- ...editContentRef.current,
8535
- [FOOTER_ORDER_KEY]: orderJson
8536
- };
8537
- applyFooterOrder(nextOrder);
8538
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8539
- requestAnimationFrame(() => {
8540
- if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
8541
- applyFooterOrder(nextOrder);
8542
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8543
- }
8544
- });
8545
- postToParentRef.current({
8546
- type: "ow:change",
8547
- nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
8548
- });
8549
- }
8550
- const still = (hrefKey ? document.querySelector(`footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`) : null) ?? (movedColumnIndex !== null ? listFooterColumns()[movedColumnIndex] ?? null : null) ?? (document.body.contains(draggedEl) ? draggedEl : null);
8551
- if (still && isNavigationItem(still)) {
8552
- selectRef.current(still);
8553
- } else if (still && isNavigationContainer(still)) {
8554
- selectFrameRef.current(still);
8555
- } else if (wasSelected) {
8556
- deselectRef.current();
8557
- }
8558
- },
8559
- [clearFooterDragVisuals]
8560
- );
8561
- commitFooterDragRef.current = commitFooterDrag;
8562
- const startFooterLinkDrag = (0, import_react9.useCallback)(
8563
- (anchor, clientX, clientY, wasSelected) => {
8564
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
8565
- if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
8566
- const column = findFooterColumnForLink(anchor);
8567
- const columns = listFooterColumns();
8568
- beginFooterDrag({
8569
- kind: "link",
8570
- hrefKey,
8571
- columnEl: column,
8572
- sourceColumnIndex: column ? columns.indexOf(column) : 0,
8573
- wasSelected,
8574
- draggedEl: anchor,
8575
- lastClientX: clientX,
8576
- lastClientY: clientY,
8577
- activeSlot: null
8578
- });
8579
- return true;
8580
- },
8581
- [beginFooterDrag]
8582
- );
8583
- const startFooterColumnDrag = (0, import_react9.useCallback)(
8584
- (columnEl, clientX, clientY, wasSelected) => {
8585
- const columns = listFooterColumns();
8586
- const idx = columns.indexOf(columnEl);
8587
- if (idx < 0) return false;
8588
- beginFooterDrag({
8589
- kind: "column",
8590
- hrefKey: null,
8591
- columnEl,
8592
- sourceColumnIndex: idx,
8593
- wasSelected,
8594
- draggedEl: columnEl,
8595
- lastClientX: clientX,
8596
- lastClientY: clientY,
8597
- activeSlot: null
8598
- });
8599
- return true;
8600
- },
8601
- [beginFooterDrag]
8602
- );
8603
- const handleItemDragStart = (0, import_react9.useCallback)(
8604
- (e) => {
8605
- const selected = selectedElRef.current;
8606
- if (!selected) {
8607
- setIsItemDragging(true);
8608
- return;
8609
- }
8610
- const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
8611
- const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
8612
- if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
8613
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8614
- if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
8615
- }
8616
- siblingHintElRef.current = null;
8617
- setSiblingHintRect(null);
8618
- setIsItemDragging(true);
8619
- },
8620
- [startFooterColumnDrag, startFooterLinkDrag]
8621
- );
8622
- const handleItemDragEnd = (0, import_react9.useCallback)(
8623
- (e) => {
8624
- if (footerDragRef.current) {
8625
- const x = e?.clientX;
8626
- const y = e?.clientY;
8627
- if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
8628
- commitFooterDrag(x, y);
8629
- } else {
8630
- commitFooterDrag();
8631
- }
8632
- return;
8633
- }
8634
- setIsItemDragging(false);
8635
- },
8636
- [commitFooterDrag]
8637
- );
8638
- const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
8639
- if (e.button !== 0) return;
6946
+ }, [postToParent, reselectNavigationItem]);
6947
+ const handleAddNavigationItem = (0, import_react6.useCallback)(() => {
8640
6948
  const selected = selectedElRef.current;
8641
6949
  if (!selected) return;
8642
- armFooterPressDrag();
8643
- const hrefKey = selected.getAttribute("data-ohw-href-key");
8644
- if (hrefKey && isFooterHrefKey(hrefKey)) {
8645
- footerPointerDragRef.current = {
8646
- el: selected,
8647
- kind: "link",
8648
- startX: e.clientX,
8649
- startY: e.clientY,
8650
- pointerId: e.pointerId,
8651
- wasSelected: true,
8652
- started: false
8653
- };
8654
- return;
8655
- }
8656
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8657
- footerPointerDragRef.current = {
8658
- el: selected,
8659
- kind: "column",
8660
- startX: e.clientX,
8661
- startY: e.clientY,
8662
- pointerId: e.pointerId,
8663
- wasSelected: true,
8664
- started: false
8665
- };
8666
- }
6950
+ const target = getNavigationItemAddHintTarget(selected);
6951
+ siblingHintElRef.current = target;
6952
+ setSiblingHintRect(target.getBoundingClientRect());
8667
6953
  }, []);
8668
- const handleItemChromeClick = (0, import_react9.useCallback)((clientX, clientY) => {
8669
- if (suppressNextClickRef.current) {
8670
- suppressNextClickRef.current = false;
8671
- return;
8672
- }
8673
- const selected = selectedElRef.current;
8674
- if (!selected || !isNavigationItem(selected)) return;
8675
- const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
8676
- if (!editable) return;
8677
- activateRef.current(editable, { caretX: clientX, caretY: clientY });
6954
+ const handleItemDragStart = (0, import_react6.useCallback)(() => {
6955
+ siblingHintElRef.current = null;
6956
+ setSiblingHintRect(null);
6957
+ setIsItemDragging(true);
6958
+ }, []);
6959
+ const handleItemDragEnd = (0, import_react6.useCallback)(() => {
6960
+ setIsItemDragging(false);
8678
6961
  }, []);
8679
6962
  reselectNavigationItemRef.current = reselectNavigationItem;
8680
6963
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
8681
- const select = (0, import_react9.useCallback)(
8682
- (anchor) => {
8683
- if (!isNavigationItem(anchor)) return;
8684
- if (activeElRef.current) deactivate();
8685
- selectedElRef.current = anchor;
8686
- clearHrefKeyHover(anchor);
8687
- hoveredNavContainerRef.current = null;
8688
- setHoveredNavContainerRect(null);
8689
- setHoveredItemRect(null);
8690
- hoveredItemElRef.current = null;
8691
- siblingHintElRef.current = null;
8692
- setSiblingHintRect(null);
8693
- setSiblingHintRects([]);
8694
- setIsItemDragging(false);
8695
- const { key, disabled } = getNavigationItemReorderState(anchor);
8696
- setReorderHrefKey(key);
8697
- setReorderDragDisabled(disabled);
8698
- setIsFooterFrameSelection(false);
8699
- setToolbarVariant("link-action");
8700
- setToolbarRect(anchor.getBoundingClientRect());
8701
- setToolbarShowEditLink(false);
8702
- setActiveCommands(/* @__PURE__ */ new Set());
8703
- },
8704
- [deactivate]
8705
- );
8706
- const selectFrame = (0, import_react9.useCallback)(
8707
- (el) => {
8708
- if (!isNavigationContainer(el)) return;
8709
- if (activeElRef.current) deactivate();
8710
- const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8711
- selectedElRef.current = el;
8712
- clearHrefKeyHover(el);
8713
- hoveredNavContainerRef.current = null;
8714
- setHoveredNavContainerRect(null);
8715
- setHoveredItemRect(null);
8716
- hoveredItemElRef.current = null;
8717
- siblingHintElRef.current = null;
8718
- setSiblingHintRect(null);
8719
- setSiblingHintRects(
8720
- isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
8721
- );
8722
- setIsItemDragging(false);
6964
+ const select = (0, import_react6.useCallback)((anchor) => {
6965
+ if (!isNavigationItem(anchor)) return;
6966
+ if (activeElRef.current) deactivate();
6967
+ selectedElRef.current = anchor;
6968
+ clearHrefKeyHover(anchor);
6969
+ setHoveredItemRect(null);
6970
+ hoveredItemElRef.current = null;
6971
+ siblingHintElRef.current = null;
6972
+ setSiblingHintRect(null);
6973
+ setIsItemDragging(false);
6974
+ const { key, disabled } = getNavigationItemReorderState(anchor);
6975
+ setReorderHrefKey(key);
6976
+ setReorderDragDisabled(disabled);
6977
+ setToolbarVariant("link-action");
6978
+ setToolbarRect(anchor.getBoundingClientRect());
6979
+ setToolbarShowEditLink(false);
6980
+ setActiveCommands(/* @__PURE__ */ new Set());
6981
+ }, [deactivate]);
6982
+ const activate = (0, import_react6.useCallback)((el) => {
6983
+ if (activeElRef.current === el) return;
6984
+ selectedElRef.current = null;
6985
+ deactivate();
6986
+ if (hoveredImageRef.current) {
6987
+ hoveredImageRef.current = null;
6988
+ setMediaHover(null);
6989
+ }
6990
+ setToolbarVariant("rich-text");
6991
+ siblingHintElRef.current = null;
6992
+ setSiblingHintRect(null);
6993
+ setIsItemDragging(false);
6994
+ el.setAttribute("contenteditable", "true");
6995
+ el.removeAttribute("data-ohw-hovered");
6996
+ el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
6997
+ activeElRef.current = el;
6998
+ originalContentRef.current = el.innerHTML;
6999
+ el.focus();
7000
+ setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7001
+ const navAnchor = getNavigationItemAnchor(el);
7002
+ if (navAnchor) {
8723
7003
  setReorderHrefKey(null);
8724
7004
  setReorderDragDisabled(false);
8725
- setIsFooterFrameSelection(isFooterColumn);
8726
- setToolbarVariant("select-frame");
8727
- setToolbarRect(el.getBoundingClientRect());
8728
- setToolbarShowEditLink(false);
8729
- setActiveCommands(/* @__PURE__ */ new Set());
8730
- },
8731
- [deactivate]
8732
- );
8733
- const activate = (0, import_react9.useCallback)(
8734
- (el, options) => {
8735
- if (activeElRef.current === el) return;
8736
- selectedElRef.current = null;
8737
- deactivate();
8738
- if (hoveredImageRef.current) {
8739
- hoveredImageRef.current = null;
8740
- postToParentRef.current({ type: "ow:image-unhover" });
8741
- }
8742
- setToolbarVariant("rich-text");
8743
- siblingHintElRef.current = null;
8744
- setSiblingHintRect(null);
8745
- setSiblingHintRects([]);
8746
- setIsItemDragging(false);
8747
- el.setAttribute("contenteditable", "true");
8748
- el.removeAttribute("data-ohw-hovered");
8749
- el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
8750
- activeElRef.current = el;
8751
- originalContentRef.current = el.innerHTML;
8752
- el.focus();
8753
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8754
- placeCaretAtPoint(el, options.caretX, options.caretY);
8755
- }
8756
- setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
8757
- const navAnchor = getNavigationItemAnchor(el);
8758
- if (navAnchor) {
8759
- setReorderHrefKey(null);
8760
- setReorderDragDisabled(false);
8761
- } else {
8762
- const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
8763
- setReorderHrefKey(reorderKey);
8764
- setReorderDragDisabled(reorderDisabled);
8765
- }
8766
- setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
8767
- postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
8768
- requestAnimationFrame(() => refreshActiveCommandsRef.current());
8769
- },
8770
- [deactivate, postToParent2]
8771
- );
7005
+ } else {
7006
+ const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
7007
+ setReorderHrefKey(reorderKey);
7008
+ setReorderDragDisabled(reorderDisabled);
7009
+ }
7010
+ setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7011
+ postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7012
+ requestAnimationFrame(() => refreshActiveCommandsRef.current());
7013
+ }, [deactivate, postToParent]);
8772
7014
  activateRef.current = activate;
8773
7015
  deactivateRef.current = deactivate;
8774
7016
  selectRef.current = select;
8775
- selectFrameRef.current = selectFrame;
8776
7017
  deselectRef.current = deselect;
8777
- (0, import_react9.useLayoutEffect)(() => {
7018
+ (0, import_react6.useLayoutEffect)(() => {
8778
7019
  if (!subdomain || isEditMode) {
8779
7020
  setFetchState("done");
8780
7021
  return;
@@ -8784,7 +7025,6 @@ function OhhwellsBridge() {
8784
7025
  for (const [key, val] of Object.entries(content)) {
8785
7026
  if (key === "__ohw_sections") continue;
8786
7027
  if (applyVideoSettingNode(key, val)) continue;
8787
- if (applyCarouselNode(key, val)) continue;
8788
7028
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8789
7029
  if (el.dataset.ohwEditable === "image") {
8790
7030
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8815,8 +7055,6 @@ function OhhwellsBridge() {
8815
7055
  });
8816
7056
  applyLinkByKey(key, val);
8817
7057
  }
8818
- reconcileNavbarItemsFromContent(content);
8819
- reconcileFooterOrderFromContent(content);
8820
7058
  enforceLinkHrefs();
8821
7059
  initSectionsFromContent(content, true);
8822
7060
  sectionsLoadedRef.current = true;
@@ -8845,7 +7083,7 @@ function OhhwellsBridge() {
8845
7083
  cancelled = true;
8846
7084
  };
8847
7085
  }, [subdomain, isEditMode]);
8848
- (0, import_react9.useEffect)(() => {
7086
+ (0, import_react6.useEffect)(() => {
8849
7087
  if (!subdomain || isEditMode) return;
8850
7088
  let debounceTimer = null;
8851
7089
  let observer = null;
@@ -8858,7 +7096,6 @@ function OhhwellsBridge() {
8858
7096
  for (const [key, val] of Object.entries(content)) {
8859
7097
  if (key === "__ohw_sections") continue;
8860
7098
  if (applyVideoSettingNode(key, val)) continue;
8861
- if (applyCarouselNode(key, val)) continue;
8862
7099
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8863
7100
  if (el.dataset.ohwEditable === "image") {
8864
7101
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8877,8 +7114,6 @@ function OhhwellsBridge() {
8877
7114
  });
8878
7115
  applyLinkByKey(key, val);
8879
7116
  }
8880
- reconcileNavbarItemsFromContent(content);
8881
- reconcileFooterOrderFromContent(content);
8882
7117
  } finally {
8883
7118
  observer?.observe(document.body, { childList: true, subtree: true });
8884
7119
  }
@@ -8896,71 +7131,26 @@ function OhhwellsBridge() {
8896
7131
  if (debounceTimer) clearTimeout(debounceTimer);
8897
7132
  };
8898
7133
  }, [subdomain, isEditMode, pathname]);
8899
- (0, import_react9.useLayoutEffect)(() => {
7134
+ (0, import_react6.useLayoutEffect)(() => {
8900
7135
  const el = document.getElementById("ohw-loader");
8901
7136
  if (!el) return;
8902
7137
  const visible = Boolean(subdomain) && fetchState !== "done";
8903
7138
  el.style.display = visible ? "flex" : "none";
8904
7139
  }, [subdomain, fetchState]);
8905
- (0, import_react9.useEffect)(() => {
8906
- postToParent2({ type: "ow:navigation", path: pathname });
8907
- }, [pathname, postToParent2]);
8908
- (0, import_react9.useEffect)(() => {
7140
+ (0, import_react6.useEffect)(() => {
7141
+ postToParent({ type: "ow:navigation", path: pathname });
7142
+ }, [pathname, postToParent]);
7143
+ (0, import_react6.useEffect)(() => {
8909
7144
  if (!isEditMode) return;
8910
- if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8911
- if (document.querySelector("[data-ohw-section-picker]")) return;
8912
7145
  setLinkPopover(null);
8913
7146
  deselectRef.current();
8914
7147
  deactivateRef.current();
8915
7148
  }, [pathname, isEditMode]);
8916
- (0, import_react9.useEffect)(() => {
8917
- const contentForNav = () => {
8918
- if (isEditMode) return editContentRef.current;
8919
- if (!subdomain) return {};
8920
- return contentCache.get(subdomain) ?? {};
8921
- };
8922
- let applying = false;
8923
- let rafId = null;
8924
- const run = () => {
8925
- if (footerDragRef.current || applying) return;
8926
- applying = true;
8927
- try {
8928
- const content = contentForNav();
8929
- reconcileNavbarItemsFromContent(content);
8930
- reconcileFooterOrderFromContent(content);
8931
- document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
8932
- if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
8933
- disableNativeHrefDrag(el);
8934
- }
8935
- });
8936
- } finally {
8937
- applying = false;
8938
- }
8939
- };
8940
- const scheduleRun = () => {
8941
- if (rafId !== null) cancelAnimationFrame(rafId);
8942
- rafId = requestAnimationFrame(() => {
8943
- rafId = null;
8944
- run();
8945
- });
8946
- };
8947
- run();
8948
- const nav = document.querySelector("nav");
8949
- const footer = document.querySelector("footer");
8950
- const observer = new MutationObserver(scheduleRun);
8951
- if (nav) observer.observe(nav, { childList: true, subtree: true });
8952
- if (footer) observer.observe(footer, { childList: true, subtree: true });
8953
- if (!nav && !footer) return;
8954
- return () => {
8955
- observer.disconnect();
8956
- if (rafId !== null) cancelAnimationFrame(rafId);
8957
- };
8958
- }, [isEditMode, pathname, subdomain, fetchState]);
8959
- (0, import_react9.useEffect)(() => {
7149
+ (0, import_react6.useEffect)(() => {
8960
7150
  if (!isEditMode) return;
8961
7151
  const measure = () => {
8962
7152
  const h = document.body.scrollHeight;
8963
- if (h > 50) postToParent2({ type: "ow:height", height: h });
7153
+ if (h > 50) postToParent({ type: "ow:height", height: h });
8964
7154
  };
8965
7155
  const t1 = setTimeout(measure, 50);
8966
7156
  const t2 = setTimeout(measure, 500);
@@ -8979,8 +7169,8 @@ function OhhwellsBridge() {
8979
7169
  if (resizeTimer) clearTimeout(resizeTimer);
8980
7170
  window.removeEventListener("resize", handleResize);
8981
7171
  };
8982
- }, [pathname, isEditMode, postToParent2]);
8983
- (0, import_react9.useEffect)(() => {
7172
+ }, [pathname, isEditMode, postToParent]);
7173
+ (0, import_react6.useEffect)(() => {
8984
7174
  if (!subdomainFromQuery || isEditMode) return;
8985
7175
  const handleClick = (e) => {
8986
7176
  const anchor = e.target.closest("a");
@@ -8996,7 +7186,7 @@ function OhhwellsBridge() {
8996
7186
  document.addEventListener("click", handleClick, true);
8997
7187
  return () => document.removeEventListener("click", handleClick, true);
8998
7188
  }, [subdomainFromQuery, isEditMode, router]);
8999
- (0, import_react9.useEffect)(() => {
7189
+ (0, import_react6.useEffect)(() => {
9000
7190
  if (!isEditMode) {
9001
7191
  editStylesRef.current?.base.remove();
9002
7192
  editStylesRef.current?.forceHover.remove();
@@ -9019,29 +7209,6 @@ function OhhwellsBridge() {
9019
7209
  [data-ohw-editable] {
9020
7210
  display: block;
9021
7211
  }
9022
- /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
9023
- footer [data-ohw-href-key] {
9024
- -webkit-user-drag: none !important;
9025
- }
9026
- footer [data-ohw-footer-col]:hover {
9027
- outline: 1.5px dashed ${PRIMARY2} !important;
9028
- outline-offset: 6px;
9029
- border-radius: 8px;
9030
- }
9031
- html[data-ohw-footer-press-drag] footer,
9032
- html[data-ohw-footer-press-drag] footer * {
9033
- user-select: none !important;
9034
- -webkit-user-select: none !important;
9035
- }
9036
- html[data-ohw-item-dragging] footer,
9037
- html[data-ohw-item-dragging] footer * {
9038
- user-select: none !important;
9039
- -webkit-user-select: none !important;
9040
- pointer-events: none !important;
9041
- }
9042
- html[data-ohw-item-dragging] {
9043
- cursor: grabbing !important;
9044
- }
9045
7212
  [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
9046
7213
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
9047
7214
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
@@ -9084,49 +7251,15 @@ function OhhwellsBridge() {
9084
7251
  document.head.appendChild(forceHover);
9085
7252
  document.head.appendChild(stateViews);
9086
7253
  editStylesRef.current = { base, forceHover, stateViews };
9087
- }
9088
- refreshStateRules();
9089
- const handleClick = (e) => {
9090
- if (suppressNextClickRef.current) {
9091
- suppressNextClickRef.current = false;
9092
- e.preventDefault();
9093
- e.stopPropagation();
9094
- return;
9095
- }
9096
- const target = e.target;
9097
- if (target.closest("[data-ohw-toolbar]")) return;
9098
- if (target.closest("[data-ohw-state-toggle]")) return;
9099
- if (target.closest("[data-ohw-max-badge]")) return;
9100
- if (isInsideLinkEditor(target)) return;
9101
- if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9102
- if (target.closest("[data-ohw-item-drag-surface]")) {
9103
- e.preventDefault();
9104
- e.stopPropagation();
9105
- const selected = selectedElRef.current;
9106
- if (selected && isNavigationItem(selected)) {
9107
- const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9108
- if (editable2) {
9109
- activateRef.current(editable2, {
9110
- caretX: e.clientX,
9111
- caretY: e.clientY
9112
- });
9113
- }
9114
- } else if (selected?.hasAttribute("data-ohw-footer-col")) {
9115
- const link = listFooterLinksInColumn(selected).find((el) => {
9116
- const r2 = el.getBoundingClientRect();
9117
- return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
9118
- });
9119
- if (link) selectRef.current(link);
9120
- }
9121
- return;
9122
- }
9123
- const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
9124
- if (navFromChrome) {
9125
- e.preventDefault();
9126
- e.stopPropagation();
9127
- selectFrameRef.current(navFromChrome);
9128
- return;
9129
- }
7254
+ }
7255
+ refreshStateRules();
7256
+ const handleClick = (e) => {
7257
+ const target = e.target;
7258
+ if (target.closest("[data-ohw-toolbar]")) return;
7259
+ if (target.closest("[data-ohw-state-toggle]")) return;
7260
+ if (target.closest("[data-ohw-max-badge]")) return;
7261
+ if (isInsideLinkEditor(target)) return;
7262
+ if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9130
7263
  e.preventDefault();
9131
7264
  e.stopPropagation();
9132
7265
  return;
@@ -9141,8 +7274,7 @@ function OhhwellsBridge() {
9141
7274
  bumpLinkPopoverGrace();
9142
7275
  setLinkPopoverRef.current({
9143
7276
  key: editable.dataset.ohwKey ?? "",
9144
- mode: "edit",
9145
- target: getLinkHref2(editable)
7277
+ target: getLinkHref(editable)
9146
7278
  });
9147
7279
  return;
9148
7280
  }
@@ -9158,8 +7290,7 @@ function OhhwellsBridge() {
9158
7290
  e.preventDefault();
9159
7291
  e.stopPropagation();
9160
7292
  if (selectedElRef.current === navAnchor) {
9161
- if (e.detail >= 2) return;
9162
- activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
7293
+ activateRef.current(editable);
9163
7294
  return;
9164
7295
  }
9165
7296
  selectRef.current(navAnchor);
@@ -9174,42 +7305,10 @@ function OhhwellsBridge() {
9174
7305
  if (hrefAnchor) {
9175
7306
  e.preventDefault();
9176
7307
  e.stopPropagation();
9177
- if (selectedElRef.current === hrefAnchor) {
9178
- const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
9179
- if (textEditable) {
9180
- activateRef.current(textEditable, {
9181
- caretX: e.clientX,
9182
- caretY: e.clientY
9183
- });
9184
- }
9185
- return;
9186
- }
7308
+ if (selectedElRef.current === hrefAnchor) return;
9187
7309
  selectRef.current(hrefAnchor);
9188
7310
  return;
9189
7311
  }
9190
- const footerColClick = target.closest("[data-ohw-footer-col]");
9191
- if (footerColClick) {
9192
- e.preventDefault();
9193
- e.stopPropagation();
9194
- selectFrameRef.current(footerColClick);
9195
- return;
9196
- }
9197
- const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
9198
- if (navContainerToSelect) {
9199
- e.preventDefault();
9200
- e.stopPropagation();
9201
- selectFrameRef.current(navContainerToSelect);
9202
- return;
9203
- }
9204
- const selectedContainer = selectedElRef.current;
9205
- if (selectedContainer && isNavigationContainer(selectedContainer)) {
9206
- const navItem = getNavigationItemAnchor(target);
9207
- if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
9208
- e.preventDefault();
9209
- e.stopPropagation();
9210
- return;
9211
- }
9212
- }
9213
7312
  if (activeElRef.current) {
9214
7313
  const sel = window.getSelection();
9215
7314
  if (sel && !sel.isCollapsed) {
@@ -9235,58 +7334,10 @@ function OhhwellsBridge() {
9235
7334
  deselectRef.current();
9236
7335
  deactivateRef.current();
9237
7336
  };
9238
- const handleDblClick = (e) => {
9239
- const target = e.target;
9240
- if (target.closest("[data-ohw-toolbar]")) return;
9241
- if (target.closest("[data-ohw-state-toggle]")) return;
9242
- if (target.closest("[data-ohw-max-badge]")) return;
9243
- if (isInsideLinkEditor(target)) return;
9244
- if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9245
- return;
9246
- }
9247
- const navLabel = getNavigationLabelEditable(target);
9248
- if (!navLabel) return;
9249
- const { editable } = navLabel;
9250
- e.preventDefault();
9251
- e.stopPropagation();
9252
- if (activeElRef.current !== editable) {
9253
- activateRef.current(editable);
9254
- }
9255
- requestAnimationFrame(() => {
9256
- selectAllTextInEditable(editable);
9257
- refreshActiveCommandsRef.current();
9258
- });
9259
- };
9260
7337
  const handleMouseOver = (e) => {
9261
- if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
9262
7338
  const target = e.target;
9263
- if (toolbarVariantRef.current !== "select-frame") {
9264
- const navContainer = target.closest("[data-ohw-nav-container]");
9265
- if (navContainer && !getNavigationItemAnchor(target)) {
9266
- hoveredNavContainerRef.current = navContainer;
9267
- setHoveredNavContainerRect(navContainer.getBoundingClientRect());
9268
- hoveredItemElRef.current = null;
9269
- setHoveredItemRect(null);
9270
- return;
9271
- }
9272
- if (!target.closest("[data-ohw-nav-container]")) {
9273
- hoveredNavContainerRef.current = null;
9274
- setHoveredNavContainerRect(null);
9275
- }
9276
- }
9277
- const footerCol = target.closest("[data-ohw-footer-col]");
9278
- if (footerCol) {
9279
- hoveredNavContainerRef.current = null;
9280
- setHoveredNavContainerRect(null);
9281
- if (selectedElRef.current === footerCol) return;
9282
- hoveredItemElRef.current = footerCol;
9283
- setHoveredItemRect(footerCol.getBoundingClientRect());
9284
- return;
9285
- }
9286
7339
  const navAnchor = getNavigationItemAnchor(target);
9287
7340
  if (navAnchor) {
9288
- hoveredNavContainerRef.current = null;
9289
- setHoveredNavContainerRect(null);
9290
7341
  const selected2 = selectedElRef.current;
9291
7342
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
9292
7343
  clearHrefKeyHover(navAnchor);
@@ -9311,23 +7362,6 @@ function OhhwellsBridge() {
9311
7362
  };
9312
7363
  const handleMouseOut = (e) => {
9313
7364
  const target = e.target;
9314
- if (target.closest("[data-ohw-nav-container]")) {
9315
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9316
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
9317
- return;
9318
- }
9319
- hoveredNavContainerRef.current = null;
9320
- setHoveredNavContainerRect(null);
9321
- }
9322
- const footerCol = target.closest("[data-ohw-footer-col]");
9323
- if (footerCol && hoveredItemElRef.current === footerCol) {
9324
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9325
- if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
9326
- if (related2?.closest("[data-ohw-footer-col]") === footerCol) return;
9327
- hoveredItemElRef.current = null;
9328
- setHoveredItemRect(null);
9329
- return;
9330
- }
9331
7365
  const navAnchor = getNavigationItemAnchor(target);
9332
7366
  if (navAnchor) {
9333
7367
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -9449,87 +7483,44 @@ function OhhwellsBridge() {
9449
7483
  }
9450
7484
  return smallest;
9451
7485
  };
9452
- const dismissImageHover = () => {
9453
- if (hoveredImageRef.current) {
9454
- hoveredImageRef.current = null;
9455
- hoveredImageHasTextOverlapRef.current = false;
9456
- resumeAnimTracks();
9457
- postToParentRef.current({ type: "ow:image-unhover" });
9458
- }
9459
- clearImageHover();
9460
- };
9461
- const probeNavigationHoverAt = (x, y) => {
9462
- if (toolbarVariantRef.current === "select-frame") {
9463
- hoveredNavContainerRef.current = null;
9464
- setHoveredNavContainerRect(null);
9465
- return;
9466
- }
9467
- const navContainer = document.querySelector("[data-ohw-nav-container]");
9468
- if (!navContainer) {
9469
- hoveredNavContainerRef.current = null;
9470
- setHoveredNavContainerRect(null);
9471
- return;
9472
- }
9473
- const containerRect = navContainer.getBoundingClientRect();
9474
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
9475
- if (!overContainer) {
9476
- hoveredNavContainerRef.current = null;
9477
- setHoveredNavContainerRect(null);
9478
- return;
9479
- }
9480
- const navItemHit = Array.from(
9481
- navContainer.querySelectorAll("[data-ohw-href-key]")
9482
- ).find((el) => {
9483
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
9484
- const itemRect = el.getBoundingClientRect();
9485
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
9486
- });
9487
- if (navItemHit) {
9488
- hoveredNavContainerRef.current = null;
9489
- setHoveredNavContainerRect(null);
9490
- const selected = selectedElRef.current;
9491
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
9492
- clearHrefKeyHover(navItemHit);
9493
- hoveredItemElRef.current = navItemHit;
9494
- setHoveredItemRect(navItemHit.getBoundingClientRect());
9495
- }
9496
- return;
9497
- }
9498
- hoveredNavContainerRef.current = navContainer;
9499
- setHoveredNavContainerRect(containerRect);
9500
- hoveredItemElRef.current = null;
9501
- setHoveredItemRect(null);
9502
- };
9503
7486
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
9504
7487
  if (linkPopoverOpenRef.current) {
9505
- dismissImageHover();
9506
- return;
9507
- }
9508
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9509
- if (isPointOverNavigation(x, y)) {
9510
- dismissImageHover();
9511
- probeNavigationHoverAt(x, y);
7488
+ if (hoveredImageRef.current) {
7489
+ hoveredImageRef.current = null;
7490
+ hoveredImageHasTextOverlapRef.current = false;
7491
+ resumeAnimTracks();
7492
+ clearImageHover();
7493
+ }
9512
7494
  return;
9513
7495
  }
9514
- hoveredNavContainerRef.current = null;
9515
- setHoveredNavContainerRect(null);
9516
7496
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
9517
7497
  if (toggleEl) {
7498
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9518
7499
  const tr = toggleEl.getBoundingClientRect();
9519
7500
  if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
9520
- dismissImageHover();
7501
+ if (hoveredImageRef.current) {
7502
+ hoveredImageRef.current = null;
7503
+ resumeAnimTracks();
7504
+ clearImageHover();
7505
+ }
9521
7506
  return;
9522
7507
  }
9523
7508
  }
9524
7509
  const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
9525
7510
  const activeEl = activeElRef.current;
9526
7511
  if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
9527
- dismissImageHover();
7512
+ if (hoveredImageRef.current) {
7513
+ hoveredImageRef.current = null;
7514
+ hoveredImageHasTextOverlapRef.current = false;
7515
+ resumeAnimTracks();
7516
+ clearImageHover();
7517
+ }
9528
7518
  return;
9529
7519
  }
9530
7520
  if (imgEl) {
7521
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9531
7522
  const topEl = document.elementFromPoint(x, y);
9532
- 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]')) {
7523
+ if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9533
7524
  if (hoveredImageRef.current) {
9534
7525
  hoveredImageRef.current = null;
9535
7526
  resumeAnimTracks();
@@ -9627,12 +7618,12 @@ function OhhwellsBridge() {
9627
7618
  resumeAnimTracks();
9628
7619
  clearImageHover();
9629
7620
  }
9630
- const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7621
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9631
7622
  const textEl = Array.from(
9632
7623
  document.querySelectorAll(NON_MEDIA_SELECTOR)
9633
7624
  ).find((el) => {
9634
7625
  const er = el.getBoundingClientRect();
9635
- return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7626
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
9636
7627
  });
9637
7628
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
9638
7629
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -9656,14 +7647,6 @@ function OhhwellsBridge() {
9656
7647
  }
9657
7648
  };
9658
7649
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
9659
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
9660
- if (activeStateElRef.current) {
9661
- activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
9662
- activeStateElRef.current = null;
9663
- setToggleState(null);
9664
- }
9665
- return;
9666
- }
9667
7650
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9668
7651
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
9669
7652
  if (toggleEl) {
@@ -9736,14 +7719,6 @@ function OhhwellsBridge() {
9736
7719
  probeHoverCardsAt(clientX, clientY);
9737
7720
  };
9738
7721
  const handleDragOver = (e) => {
9739
- const footerSession = footerDragRef.current;
9740
- if (footerSession) {
9741
- e.preventDefault();
9742
- if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
9743
- const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
9744
- refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
9745
- return;
9746
- }
9747
7722
  e.preventDefault();
9748
7723
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9749
7724
  if (!el) {
@@ -9926,7 +7901,6 @@ function OhhwellsBridge() {
9926
7901
  continue;
9927
7902
  }
9928
7903
  if (applyVideoSettingNode(key, val)) continue;
9929
- if (applyCarouselNode(key, val)) continue;
9930
7904
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9931
7905
  if (el.dataset.ohwEditable === "image") {
9932
7906
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -9949,9 +7923,6 @@ function OhhwellsBridge() {
9949
7923
  sectionsLoadedRef.current = true;
9950
7924
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
9951
7925
  }
9952
- editContentRef.current = { ...editContentRef.current, ...content };
9953
- reconcileNavbarItemsFromContent(editContentRef.current);
9954
- reconcileFooterOrderFromContent(editContentRef.current);
9955
7926
  enforceLinkHrefs();
9956
7927
  postToParentRef.current({ type: "ow:hydrate-done" });
9957
7928
  };
@@ -9959,7 +7930,6 @@ function OhhwellsBridge() {
9959
7930
  const handleDeactivate = (e) => {
9960
7931
  if (e.data?.type !== "ow:deactivate") return;
9961
7932
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
9962
- if (document.querySelector("[data-ohw-section-picker]")) return;
9963
7933
  if (linkPopoverOpenRef.current) {
9964
7934
  setLinkPopoverRef.current(null);
9965
7935
  return;
@@ -9968,40 +7938,13 @@ function OhhwellsBridge() {
9968
7938
  deactivateRef.current();
9969
7939
  };
9970
7940
  window.addEventListener("message", handleDeactivate);
9971
- const handleUiEscape = (e) => {
9972
- if (e.data?.type !== "ui:escape") return;
9973
- if (linkPopoverOpenRef.current) {
9974
- setLinkPopoverRef.current(null);
9975
- }
9976
- };
9977
- window.addEventListener("message", handleUiEscape);
9978
7941
  const handleKeyDown = (e) => {
9979
- if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
9980
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
9981
- const navAnchor = getNavigationItemAnchor(activeElRef.current);
9982
- if (navAnchor) {
9983
- e.preventDefault();
9984
- selectAllTextInEditable(activeElRef.current);
9985
- refreshActiveCommandsRef.current();
9986
- return;
9987
- }
9988
- }
9989
7942
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
9990
7943
  setLinkPopoverRef.current(null);
9991
7944
  return;
9992
7945
  }
9993
7946
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
9994
- if (toolbarVariantRef.current === "select-frame") {
9995
- deselectRef.current();
9996
- return;
9997
- }
9998
- const parent = getNavigationSelectionParent(selectedElRef.current);
9999
- if (parent) {
10000
- e.preventDefault();
10001
- selectFrameRef.current(parent);
10002
- } else {
10003
- deselectRef.current();
10004
- }
7947
+ deselectRef.current();
10005
7948
  return;
10006
7949
  }
10007
7950
  if (e.key === "Escape" && activeElRef.current) {
@@ -10059,23 +8002,9 @@ function OhhwellsBridge() {
10059
8002
  if (hoveredItemElRef.current) {
10060
8003
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
10061
8004
  }
10062
- if (hoveredNavContainerRef.current) {
10063
- setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
10064
- }
10065
8005
  if (siblingHintElRef.current) {
10066
8006
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
10067
8007
  }
10068
- const selected = selectedElRef.current;
10069
- if (selected && !footerDragRef.current && (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)))) {
10070
- setSiblingHintRects(
10071
- listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
10072
- );
10073
- }
10074
- if (footerDragRef.current) {
10075
- const session = footerDragRef.current;
10076
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
10077
- refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
10078
- }
10079
8008
  if (hoveredImageRef.current) {
10080
8009
  const el = hoveredImageRef.current;
10081
8010
  const r2 = el.getBoundingClientRect();
@@ -10086,7 +8015,7 @@ function OhhwellsBridge() {
10086
8015
  };
10087
8016
  const handleSave = (e) => {
10088
8017
  if (e.data?.type !== "ow:save") return;
10089
- const nodes = collectEditableNodes(editContentRef.current);
8018
+ const nodes = collectEditableNodes();
10090
8019
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
10091
8020
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
10092
8021
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -10171,7 +8100,6 @@ function OhhwellsBridge() {
10171
8100
  const handleDocMouseLeave = () => {
10172
8101
  hoveredImageRef.current = null;
10173
8102
  setMediaHover(null);
10174
- setCarouselHover(null);
10175
8103
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
10176
8104
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
10177
8105
  activeStateElRef.current = null;
@@ -10211,48 +8139,6 @@ function OhhwellsBridge() {
10211
8139
  if (e.data?.type !== "ow:canvas-height" || typeof e.data.height !== "number") return;
10212
8140
  document.documentElement.style.setProperty("--ohw-canvas-h", `${e.data.height}px`);
10213
8141
  };
10214
- const handleCarouselChange = (e) => {
10215
- if (e.data?.type !== "ow:carousel-change") return;
10216
- const { key, images } = e.data;
10217
- if (!key || !Array.isArray(images)) return;
10218
- const text = JSON.stringify(images);
10219
- applyCarouselValue(key, images);
10220
- editContentRef.current = { ...editContentRef.current, [key]: text };
10221
- postToParentRef.current({ type: "ow:change", nodes: [{ key, text }] });
10222
- setCarouselHover(null);
10223
- };
10224
- const findCarouselAtPoint = (clientX, clientY) => {
10225
- const containers = Array.from(document.querySelectorAll(`[${CAROUSEL_ATTR}]`));
10226
- for (let i = containers.length - 1; i >= 0; i--) {
10227
- const r2 = containers[i].getBoundingClientRect();
10228
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) {
10229
- return containers[i];
10230
- }
10231
- }
10232
- return null;
10233
- };
10234
- const isPointOverEditable = (scope, clientX, clientY) => {
10235
- const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
10236
- for (const el of editables) {
10237
- const r2 = el.getBoundingClientRect();
10238
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
10239
- }
10240
- return false;
10241
- };
10242
- const handleCarouselHover = (e) => {
10243
- const container = findCarouselAtPoint(e.clientX, e.clientY);
10244
- const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
10245
- if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
10246
- setCarouselHover((prev) => prev ? null : prev);
10247
- return;
10248
- }
10249
- const key = container.getAttribute("data-ohw-key") ?? "";
10250
- if (!key) return;
10251
- const r2 = getVisibleRect(container);
10252
- setCarouselHover(
10253
- (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 } }
10254
- );
10255
- };
10256
8142
  const applyToolbarPos = (rect) => {
10257
8143
  const ps = parentScrollRef.current;
10258
8144
  const approxW = 330;
@@ -10312,45 +8198,9 @@ function OhhwellsBridge() {
10312
8198
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
10313
8199
  });
10314
8200
  if (textEditable) {
10315
- const hrefCtx = getHrefKeyFromElement(textEditable);
10316
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
10317
- if (navAnchor) {
10318
- if (selectedElRef.current === navAnchor) {
10319
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
10320
- } else {
10321
- selectRef.current(navAnchor);
10322
- }
10323
- return;
10324
- }
10325
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
8201
+ activateRef.current(textEditable);
10326
8202
  return;
10327
8203
  }
10328
- const navContainer = document.querySelector("[data-ohw-nav-container]");
10329
- if (navContainer) {
10330
- const r2 = navContainer.getBoundingClientRect();
10331
- const pad = 8;
10332
- const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
10333
- if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
10334
- selectFrameRef.current(navContainer);
10335
- return;
10336
- }
10337
- }
10338
- const navRoot = document.querySelector("[data-ohw-nav-root]");
10339
- if (navRoot && navContainer) {
10340
- const rr = navRoot.getBoundingClientRect();
10341
- if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
10342
- const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
10343
- if (book) {
10344
- const br = book.getBoundingClientRect();
10345
- if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
10346
- deactivateRef.current();
10347
- return;
10348
- }
10349
- }
10350
- selectFrameRef.current(navContainer);
10351
- return;
10352
- }
10353
- }
10354
8204
  deactivateRef.current();
10355
8205
  };
10356
8206
  window.addEventListener("message", handleSave);
@@ -10361,7 +8211,6 @@ function OhhwellsBridge() {
10361
8211
  window.addEventListener("message", handleRemoveSchedulingSection);
10362
8212
  window.addEventListener("message", handleImageUrl);
10363
8213
  window.addEventListener("message", handleImageUploading);
10364
- window.addEventListener("message", handleCarouselChange);
10365
8214
  window.addEventListener("message", handleCanvasHeight);
10366
8215
  window.addEventListener("message", handleParentScroll);
10367
8216
  window.addEventListener("message", handlePointerSync);
@@ -10373,13 +8222,11 @@ function OhhwellsBridge() {
10373
8222
  };
10374
8223
  window.addEventListener("resize", handleViewportResize, { passive: true });
10375
8224
  document.addEventListener("click", handleClick, true);
10376
- document.addEventListener("dblclick", handleDblClick, true);
10377
8225
  document.addEventListener("paste", handlePaste, true);
10378
8226
  document.addEventListener("input", handleInput, true);
10379
8227
  document.addEventListener("mouseover", handleMouseOver, true);
10380
8228
  document.addEventListener("mouseout", handleMouseOut, true);
10381
8229
  document.addEventListener("mousemove", handleMouseMove, true);
10382
- document.addEventListener("mousemove", handleCarouselHover, true);
10383
8230
  document.addEventListener("mouseleave", handleDocMouseLeave);
10384
8231
  document.addEventListener("dragover", handleDragOver, true);
10385
8232
  document.addEventListener("dragleave", handleDragLeave, true);
@@ -10389,13 +8236,11 @@ function OhhwellsBridge() {
10389
8236
  window.addEventListener("scroll", handleScroll, true);
10390
8237
  return () => {
10391
8238
  document.removeEventListener("click", handleClick, true);
10392
- document.removeEventListener("dblclick", handleDblClick, true);
10393
8239
  document.removeEventListener("paste", handlePaste, true);
10394
8240
  document.removeEventListener("input", handleInput, true);
10395
8241
  document.removeEventListener("mouseover", handleMouseOver, true);
10396
8242
  document.removeEventListener("mouseout", handleMouseOut, true);
10397
8243
  document.removeEventListener("mousemove", handleMouseMove, true);
10398
- document.removeEventListener("mousemove", handleCarouselHover, true);
10399
8244
  document.removeEventListener("mouseleave", handleDocMouseLeave);
10400
8245
  document.removeEventListener("dragover", handleDragOver, true);
10401
8246
  document.removeEventListener("dragleave", handleDragLeave, true);
@@ -10411,7 +8256,6 @@ function OhhwellsBridge() {
10411
8256
  window.removeEventListener("message", handleRemoveSchedulingSection);
10412
8257
  window.removeEventListener("message", handleImageUrl);
10413
8258
  window.removeEventListener("message", handleImageUploading);
10414
- window.removeEventListener("message", handleCarouselChange);
10415
8259
  window.removeEventListener("message", handleCanvasHeight);
10416
8260
  window.removeEventListener("message", handleParentScroll);
10417
8261
  window.removeEventListener("resize", handleViewportResize);
@@ -10419,162 +8263,13 @@ function OhhwellsBridge() {
10419
8263
  window.removeEventListener("message", handleClickAt);
10420
8264
  window.removeEventListener("message", handleHydrate);
10421
8265
  window.removeEventListener("message", handleDeactivate);
10422
- window.removeEventListener("message", handleUiEscape);
10423
8266
  autoSaveTimers.current.forEach(clearTimeout);
10424
8267
  autoSaveTimers.current.clear();
10425
8268
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
10426
8269
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
10427
8270
  };
10428
8271
  }, [isEditMode, refreshStateRules]);
10429
- (0, import_react9.useEffect)(() => {
10430
- if (!isEditMode) return;
10431
- const THRESHOLD = 10;
10432
- const resolveWasSelected = (el) => {
10433
- if (selectedElRef.current === el) return true;
10434
- const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
10435
- return activeAnchor === el;
10436
- };
10437
- const onPointerDown = (e) => {
10438
- if (e.button !== 0) return;
10439
- if (footerDragRef.current) return;
10440
- const target = e.target;
10441
- if (!target) return;
10442
- 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]')) {
10443
- return;
10444
- }
10445
- if (target.closest("[data-ohw-item-drag-surface]")) return;
10446
- const anchor = getNavigationItemAnchor(target);
10447
- const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
10448
- if (anchor && isFooterHrefKey(hrefKey)) {
10449
- armFooterPressDrag();
10450
- footerPointerDragRef.current = {
10451
- el: anchor,
10452
- kind: "link",
10453
- startX: e.clientX,
10454
- startY: e.clientY,
10455
- pointerId: e.pointerId,
10456
- wasSelected: resolveWasSelected(anchor),
10457
- started: false
10458
- };
10459
- return;
10460
- }
10461
- const col = target.closest("[data-ohw-footer-col]");
10462
- if (col && !getNavigationItemAnchor(target)) {
10463
- armFooterPressDrag();
10464
- footerPointerDragRef.current = {
10465
- el: col,
10466
- kind: "column",
10467
- startX: e.clientX,
10468
- startY: e.clientY,
10469
- pointerId: e.pointerId,
10470
- wasSelected: resolveWasSelected(col),
10471
- started: false
10472
- };
10473
- }
10474
- };
10475
- const onPointerMove = (e) => {
10476
- const pending = footerPointerDragRef.current;
10477
- if (!pending) return;
10478
- if (pending.started) {
10479
- e.preventDefault();
10480
- clearTextSelection();
10481
- const session = footerDragRef.current;
10482
- if (!session) return;
10483
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10484
- refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
10485
- return;
10486
- }
10487
- const dx = e.clientX - pending.startX;
10488
- const dy = e.clientY - pending.startY;
10489
- if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
10490
- e.preventDefault();
10491
- pending.started = true;
10492
- clearTextSelection();
10493
- try {
10494
- document.body.setPointerCapture(pending.pointerId);
10495
- } catch {
10496
- }
10497
- if (linkPopoverOpenRef.current) {
10498
- setLinkPopoverRef.current(null);
10499
- }
10500
- if (activeElRef.current) {
10501
- deactivateRef.current();
10502
- }
10503
- if (pending.kind === "link") {
10504
- const key = pending.el.getAttribute("data-ohw-href-key");
10505
- if (!key) return;
10506
- const column = findFooterColumnForLink(pending.el);
10507
- const columns2 = listFooterColumns();
10508
- beginFooterDragRef.current({
10509
- kind: "link",
10510
- hrefKey: key,
10511
- columnEl: column,
10512
- sourceColumnIndex: column ? columns2.indexOf(column) : 0,
10513
- wasSelected: pending.wasSelected,
10514
- draggedEl: pending.el,
10515
- lastClientX: e.clientX,
10516
- lastClientY: e.clientY,
10517
- activeSlot: null
10518
- });
10519
- return;
10520
- }
10521
- const columns = listFooterColumns();
10522
- const idx = columns.indexOf(pending.el);
10523
- if (idx < 0) return;
10524
- beginFooterDragRef.current({
10525
- kind: "column",
10526
- hrefKey: null,
10527
- columnEl: pending.el,
10528
- sourceColumnIndex: idx,
10529
- wasSelected: pending.wasSelected,
10530
- draggedEl: pending.el,
10531
- lastClientX: e.clientX,
10532
- lastClientY: e.clientY,
10533
- activeSlot: null
10534
- });
10535
- };
10536
- const endPointerDrag = (e) => {
10537
- const pending = footerPointerDragRef.current;
10538
- footerPointerDragRef.current = null;
10539
- try {
10540
- if (document.body.hasPointerCapture(e.pointerId)) {
10541
- document.body.releasePointerCapture(e.pointerId);
10542
- }
10543
- } catch {
10544
- }
10545
- if (!pending?.started) {
10546
- unlockFooterDragInteraction();
10547
- return;
10548
- }
10549
- suppressNextClickRef.current = true;
10550
- commitFooterDragRef.current(e.clientX, e.clientY);
10551
- };
10552
- const blockSelectStart = (e) => {
10553
- if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
10554
- e.preventDefault();
10555
- }
10556
- };
10557
- const onDragEnd = (_e) => {
10558
- if (!footerDragRef.current) return;
10559
- commitFooterDragRef.current();
10560
- };
10561
- document.addEventListener("pointerdown", onPointerDown, true);
10562
- document.addEventListener("pointermove", onPointerMove, true);
10563
- document.addEventListener("pointerup", endPointerDrag, true);
10564
- document.addEventListener("pointercancel", endPointerDrag, true);
10565
- document.addEventListener("selectstart", blockSelectStart, true);
10566
- document.addEventListener("dragend", onDragEnd, true);
10567
- return () => {
10568
- document.removeEventListener("pointerdown", onPointerDown, true);
10569
- document.removeEventListener("pointermove", onPointerMove, true);
10570
- document.removeEventListener("pointerup", endPointerDrag, true);
10571
- document.removeEventListener("pointercancel", endPointerDrag, true);
10572
- document.removeEventListener("selectstart", blockSelectStart, true);
10573
- document.removeEventListener("dragend", onDragEnd, true);
10574
- unlockFooterDragInteraction();
10575
- };
10576
- }, [isEditMode]);
10577
- (0, import_react9.useEffect)(() => {
8272
+ (0, import_react6.useEffect)(() => {
10578
8273
  const handler = (e) => {
10579
8274
  if (e.data?.type !== "ow:request-schedule-config") return;
10580
8275
  const insertAfterVal = e.data.insertAfter;
@@ -10590,7 +8285,7 @@ function OhhwellsBridge() {
10590
8285
  window.addEventListener("message", handler);
10591
8286
  return () => window.removeEventListener("message", handler);
10592
8287
  }, [processConfigRequest]);
10593
- (0, import_react9.useEffect)(() => {
8288
+ (0, import_react6.useEffect)(() => {
10594
8289
  if (!isEditMode) return;
10595
8290
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
10596
8291
  el.removeAttribute("data-ohw-active-state");
@@ -10611,33 +8306,27 @@ function OhhwellsBridge() {
10611
8306
  const next = { ...prev, [pathKey]: sections };
10612
8307
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
10613
8308
  });
10614
- postToParent2({
10615
- type: "ow:ready",
10616
- version: "1",
10617
- path: pathname,
10618
- nodes: collectEditableNodes(editContentRef.current),
10619
- sections
10620
- });
10621
- postToParent2({ type: "ow:request-site-pages" });
8309
+ postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
8310
+ postToParent({ type: "ow:request-site-pages" });
10622
8311
  }, 150);
10623
8312
  return () => {
10624
8313
  cancelAnimationFrame(raf);
10625
8314
  clearTimeout(timer);
10626
8315
  };
10627
- }, [pathname, isEditMode, refreshStateRules, postToParent2]);
10628
- (0, import_react9.useEffect)(() => {
8316
+ }, [pathname, isEditMode, refreshStateRules, postToParent]);
8317
+ (0, import_react6.useEffect)(() => {
10629
8318
  scrollToHashSectionWhenReady();
10630
8319
  const onHashChange = () => scrollToHashSectionWhenReady();
10631
8320
  window.addEventListener("hashchange", onHashChange);
10632
8321
  return () => window.removeEventListener("hashchange", onHashChange);
10633
8322
  }, [pathname]);
10634
- const handleCommand = (0, import_react9.useCallback)((cmd) => {
8323
+ const handleCommand = (0, import_react6.useCallback)((cmd) => {
10635
8324
  document.execCommand(cmd, false);
10636
8325
  activeElRef.current?.focus();
10637
8326
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
10638
8327
  refreshActiveCommandsRef.current();
10639
8328
  }, []);
10640
- const handleStateChange = (0, import_react9.useCallback)((state) => {
8329
+ const handleStateChange = (0, import_react6.useCallback)((state) => {
10641
8330
  if (!activeStateElRef.current) return;
10642
8331
  const el = activeStateElRef.current;
10643
8332
  if (state === "Default") {
@@ -10650,22 +8339,18 @@ function OhhwellsBridge() {
10650
8339
  }
10651
8340
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
10652
8341
  }, [deactivate]);
10653
- const closeLinkPopover = (0, import_react9.useCallback)(() => {
10654
- addNavAfterAnchorRef.current = null;
10655
- setLinkPopover(null);
10656
- }, []);
10657
- const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
8342
+ const closeLinkPopover = (0, import_react6.useCallback)(() => setLinkPopover(null), []);
8343
+ const openLinkPopoverForActive = (0, import_react6.useCallback)(() => {
10658
8344
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
10659
8345
  if (!hrefCtx) return;
10660
8346
  bumpLinkPopoverGrace();
10661
8347
  setLinkPopover({
10662
8348
  key: hrefCtx.key,
10663
- mode: "edit",
10664
- target: getLinkHref2(hrefCtx.anchor)
8349
+ target: getLinkHref(hrefCtx.anchor)
10665
8350
  });
10666
8351
  deactivate();
10667
8352
  }, [deactivate]);
10668
- const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
8353
+ const openLinkPopoverForSelected = (0, import_react6.useCallback)(() => {
10669
8354
  const anchor = selectedElRef.current;
10670
8355
  if (!anchor) return;
10671
8356
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -10673,80 +8358,30 @@ function OhhwellsBridge() {
10673
8358
  bumpLinkPopoverGrace();
10674
8359
  setLinkPopover({
10675
8360
  key,
10676
- mode: "edit",
10677
- target: getLinkHref2(anchor)
8361
+ target: getLinkHref(anchor)
10678
8362
  });
10679
8363
  deselect();
10680
8364
  }, [deselect]);
10681
- const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
8365
+ const handleLinkPopoverSubmit = (0, import_react6.useCallback)(
10682
8366
  (target) => {
10683
- const session = linkPopoverSessionRef.current;
10684
- if (!session) return;
10685
- if (session.intent === "add-nav") {
10686
- const trimmed = target.trim();
10687
- let href = trimmed;
10688
- let label = "Untitled";
10689
- if (trimmed) {
10690
- const { pageRoute, sectionId } = parseTarget(trimmed);
10691
- const page = resolvePage(pageRoute, sitePages);
10692
- const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
10693
- const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
10694
- label = section?.label ?? page.title;
10695
- href = trimmed;
10696
- }
10697
- const afterAnchor = addNavAfterAnchorRef.current;
10698
- const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(href, label, afterAnchor);
10699
- applyLinkByKey(hrefKey, href);
10700
- document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
10701
- el.textContent = label;
10702
- });
10703
- const navCount = String(index + 1);
10704
- const orderJson = JSON.stringify(order);
10705
- editContentRef.current = {
10706
- ...editContentRef.current,
10707
- [hrefKey]: href,
10708
- [labelKey]: label,
10709
- [NAV_COUNT_KEY]: navCount,
10710
- [NAV_ORDER_KEY]: orderJson
10711
- };
10712
- postToParent2({
10713
- type: "ow:change",
10714
- nodes: [
10715
- { key: hrefKey, text: href },
10716
- { key: labelKey, text: label },
10717
- { key: NAV_COUNT_KEY, text: navCount },
10718
- { key: NAV_ORDER_KEY, text: orderJson }
10719
- ]
10720
- });
10721
- postToParent2({ type: "ow:toast", title: "Link added", toastType: "success" });
10722
- addNavAfterAnchorRef.current = null;
10723
- setLinkPopover(null);
10724
- enforceLinkHrefs();
10725
- requestAnimationFrame(() => {
10726
- selectRef.current(anchor);
10727
- });
10728
- return;
10729
- }
10730
- const { key } = session;
8367
+ if (!linkPopover) return;
8368
+ const { key } = linkPopover;
10731
8369
  applyLinkByKey(key, target);
10732
- postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8370
+ postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
10733
8371
  setLinkPopover(null);
10734
8372
  },
10735
- [postToParent2, sitePages, sectionsByPath]
10736
- );
10737
- const handleMediaReplace = (0, import_react9.useCallback)(
10738
- (key) => {
10739
- postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
10740
- },
10741
- [postToParent2, mediaHover?.elementType]
8373
+ [linkPopover, postToParent]
10742
8374
  );
10743
- const handleEditCarousel = (0, import_react9.useCallback)(
8375
+ const showEditLink = toolbarShowEditLink;
8376
+ const currentSections = sectionsByPath[pathname] ?? [];
8377
+ linkPopoverOpenRef.current = linkPopover !== null;
8378
+ const handleMediaReplace = (0, import_react6.useCallback)(
10744
8379
  (key) => {
10745
- postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
8380
+ postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
10746
8381
  },
10747
- [postToParent2]
8382
+ [postToParent, mediaHover?.elementType]
10748
8383
  );
10749
- const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
8384
+ const handleMediaFadeOutComplete = (0, import_react6.useCallback)((key) => {
10750
8385
  setUploadingRects((prev) => {
10751
8386
  if (!(key in prev)) return prev;
10752
8387
  const next = { ...prev };
@@ -10754,7 +8389,7 @@ function OhhwellsBridge() {
10754
8389
  return next;
10755
8390
  });
10756
8391
  }, []);
10757
- const handleVideoSettingsChange = (0, import_react9.useCallback)(
8392
+ const handleVideoSettingsChange = (0, import_react6.useCallback)(
10758
8393
  (key, settings) => {
10759
8394
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
10760
8395
  const video = getVideoEl(el);
@@ -10762,7 +8397,7 @@ function OhhwellsBridge() {
10762
8397
  if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
10763
8398
  if (typeof settings.muted === "boolean") video.muted = settings.muted;
10764
8399
  syncVideoPlayback(video);
10765
- postToParent2({
8400
+ postToParent({
10766
8401
  type: "ow:change",
10767
8402
  nodes: [
10768
8403
  { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
@@ -10774,15 +8409,12 @@ function OhhwellsBridge() {
10774
8409
  );
10775
8410
  });
10776
8411
  },
10777
- [postToParent2]
8412
+ [postToParent]
10778
8413
  );
10779
- const showEditLink = toolbarShowEditLink;
10780
- const currentSections = sectionsByPath[pathname] ?? [];
10781
- linkPopoverOpenRef.current = linkPopover !== null;
10782
- return bridgeRoot ? (0, import_react_dom3.createPortal)(
10783
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
10784
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
10785
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8414
+ return bridgeRoot ? (0, import_react_dom2.createPortal)(
8415
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8416
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8417
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
10786
8418
  MediaOverlay,
10787
8419
  {
10788
8420
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -10793,7 +8425,7 @@ function OhhwellsBridge() {
10793
8425
  },
10794
8426
  `uploading-${key}`
10795
8427
  )),
10796
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8428
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
10797
8429
  MediaOverlay,
10798
8430
  {
10799
8431
  hover: mediaHover,
@@ -10802,33 +8434,53 @@ function OhhwellsBridge() {
10802
8434
  onVideoSettingsChange: handleVideoSettingsChange
10803
8435
  }
10804
8436
  ),
10805
- carouselHover && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
10806
- siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
10807
- siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
10808
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
10809
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10810
- "div",
8437
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8438
+ hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8439
+ toolbarRect && toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8440
+ ItemInteractionLayer,
10811
8441
  {
10812
- className: "pointer-events-none fixed z-2147483646",
10813
- style: {
10814
- left: slot.left,
10815
- top: slot.top,
10816
- width: slot.width,
10817
- height: slot.height
10818
- },
10819
- 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" })
10820
- },
10821
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
10822
- )),
10823
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
10824
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
10825
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
10826
- 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 }),
10827
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
10828
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(EditGlowChrome, { rect: toolbarRect, elRef: glowElRef, reorderHrefKey, dragDisabled: reorderDragDisabled }),
10829
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands, showEditLink, onEditLink: openLinkPopoverForActive })
8442
+ rect: toolbarRect,
8443
+ elRef: glowElRef,
8444
+ state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
8445
+ showHandle: Boolean(reorderHrefKey),
8446
+ dragDisabled: reorderDragDisabled,
8447
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8448
+ onDragHandleDragStart: handleItemDragStart,
8449
+ onDragHandleDragEnd: handleItemDragEnd,
8450
+ toolbar: isItemDragging ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8451
+ ItemActionToolbar,
8452
+ {
8453
+ onEditLink: openLinkPopoverForSelected,
8454
+ onAddItem: handleAddNavigationItem,
8455
+ addItemDisabled: false
8456
+ }
8457
+ )
8458
+ }
8459
+ ),
8460
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8461
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8462
+ EditGlowChrome,
8463
+ {
8464
+ rect: toolbarRect,
8465
+ elRef: glowElRef,
8466
+ reorderHrefKey,
8467
+ dragDisabled: reorderDragDisabled
8468
+ }
8469
+ ),
8470
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8471
+ FloatingToolbar,
8472
+ {
8473
+ rect: toolbarRect,
8474
+ parentScroll: parentScrollRef.current,
8475
+ elRef: toolbarElRef,
8476
+ onCommand: handleCommand,
8477
+ activeCommands,
8478
+ showEditLink,
8479
+ onEditLink: openLinkPopoverForActive
8480
+ }
8481
+ )
10830
8482
  ] }),
10831
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
8483
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
10832
8484
  "div",
10833
8485
  {
10834
8486
  "data-ohw-max-badge": "",
@@ -10854,29 +8506,56 @@ function OhhwellsBridge() {
10854
8506
  ]
10855
8507
  }
10856
8508
  ),
10857
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StateToggle, { rect: toggleState.rect, activeState: toggleState.activeState, states: toggleState.states, onStateChange: handleStateChange }),
10858
- 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: [
10859
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10860
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10861
- Badge,
10862
- {
10863
- 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",
10864
- onClick: () => {
10865
- window.parent.postMessage(
10866
- {
10867
- type: "ow:add-section",
10868
- insertAfter: sectionGap.insertAfter,
10869
- insertBefore: sectionGap.insertBefore
8509
+ toggleState && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8510
+ StateToggle,
8511
+ {
8512
+ rect: toggleState.rect,
8513
+ activeState: toggleState.activeState,
8514
+ states: toggleState.states,
8515
+ onStateChange: handleStateChange
8516
+ }
8517
+ ),
8518
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
8519
+ "div",
8520
+ {
8521
+ "data-ohw-section-insert-line": "",
8522
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8523
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
8524
+ children: [
8525
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8526
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8527
+ Badge,
8528
+ {
8529
+ 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",
8530
+ onClick: () => {
8531
+ window.parent.postMessage(
8532
+ { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
8533
+ "*"
8534
+ );
10870
8535
  },
10871
- "*"
10872
- );
10873
- },
10874
- children: "Add Section"
10875
- }
10876
- ),
10877
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10878
- ] }),
10879
- 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
8536
+ children: "Add Section"
8537
+ }
8538
+ ),
8539
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8540
+ ]
8541
+ }
8542
+ ),
8543
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8544
+ LinkPopover,
8545
+ {
8546
+ panelRef: linkPopoverPanelRef,
8547
+ portalContainer: dialogPortalContainer,
8548
+ open: true,
8549
+ mode: "edit",
8550
+ pages: sitePages,
8551
+ sections: currentSections,
8552
+ sectionsByPath,
8553
+ initialTarget: linkPopover.target,
8554
+ onClose: closeLinkPopover,
8555
+ onSubmit: handleLinkPopoverSubmit
8556
+ },
8557
+ `${linkPopover.key}-${pathname}`
8558
+ ) : null
10880
8559
  ] }),
10881
8560
  bridgeRoot
10882
8561
  ) : null;
@@ -10887,7 +8566,6 @@ function OhhwellsBridge() {
10887
8566
  CustomToolbarButton,
10888
8567
  CustomToolbarDivider,
10889
8568
  DragHandle,
10890
- DropIndicator,
10891
8569
  ItemActionToolbar,
10892
8570
  ItemInteractionLayer,
10893
8571
  LinkEditorPanel,
@@ -10902,14 +8580,12 @@ function OhhwellsBridge() {
10902
8580
  TooltipProvider,
10903
8581
  TooltipTrigger,
10904
8582
  buildTarget,
10905
- dropIndicatorVariants,
10906
8583
  filterAvailablePages,
10907
8584
  getEditModeInitialState,
10908
8585
  isEditSessionActive,
10909
8586
  isValidUrl,
10910
8587
  parseTarget,
10911
8588
  toggleVariants,
10912
- useOhwCarousel,
10913
8589
  validateUrlInput
10914
8590
  });
10915
8591
  //# sourceMappingURL=index.cjs.map