@ohhwells/bridge 0.1.38-next.69 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React10, { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState7 } from "react";
4
+ import React9, { useCallback as useCallback2, useEffect as useEffect4, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState5 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
@@ -10,9 +10,6 @@ var linkHrefStore = /* @__PURE__ */ new Map();
10
10
  function setStoredLinkHref(key, href) {
11
11
  linkHrefStore.set(key, href);
12
12
  }
13
- function getStoredLinkHref(key) {
14
- return linkHrefStore.get(key);
15
- }
16
13
  function enforceLinkHrefs() {
17
14
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
18
15
  const key = el.getAttribute("data-ohw-href-key") ?? "";
@@ -48,13 +45,12 @@ function useLinkHrefGuardian(...deps) {
48
45
  }
49
46
 
50
47
  // src/ui/SchedulingWidget.tsx
51
- import { useCallback, useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
48
+ import { useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
52
49
 
53
50
  // src/ui/EmailCaptureModal.tsx
54
51
  import { useState } from "react";
55
52
  import { Dialog } from "radix-ui";
56
53
  import { jsx, jsxs } from "react/jsx-runtime";
57
- var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
58
54
  function Spinner() {
59
55
  return /* @__PURE__ */ jsxs(
60
56
  "svg",
@@ -88,17 +84,12 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
88
84
  const [error, setError] = useState(null);
89
85
  const isBook = title === "Confirm your spot";
90
86
  const handleSubmit = async () => {
91
- const trimmed = email.trim();
92
- if (!trimmed) return;
93
- if (!isValidEmail(trimmed)) {
94
- setError("Please enter a valid email address.");
95
- return;
96
- }
87
+ if (!email.trim()) return;
97
88
  setLoading(true);
98
89
  setError(null);
99
90
  try {
100
- await onSubmit(trimmed);
101
- setSubmittedEmail(trimmed);
91
+ await onSubmit(email.trim());
92
+ setSubmittedEmail(email.trim());
102
93
  setSuccess(true);
103
94
  } catch (e) {
104
95
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -173,10 +164,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
173
164
  {
174
165
  type: "email",
175
166
  value: email,
176
- onChange: (e) => {
177
- setEmail(e.target.value);
178
- if (error) setError(null);
179
- },
167
+ onChange: (e) => setEmail(e.target.value),
180
168
  onKeyDown: handleKeyDown,
181
169
  placeholder: "hello@gmail.com",
182
170
  autoFocus: true,
@@ -233,20 +221,12 @@ function formatClassTime(cls) {
233
221
  const em = endTotal % 60;
234
222
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
235
223
  }
236
- function formatBookingLabel(cls) {
237
- const price = cls.price;
238
- const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
239
- return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
240
- }
241
224
  function getBookingsOnDate(cls, date) {
242
225
  if (!cls.bookings?.length) return 0;
243
- const target = new Date(date);
244
- target.setHours(0, 0, 0, 0);
226
+ const ds = date.toISOString().split("T")[0];
245
227
  return cls.bookings.filter((b) => {
246
228
  try {
247
- const bookingDate = new Date(b.classDate);
248
- bookingDate.setHours(0, 0, 0, 0);
249
- return bookingDate.getTime() === target.getTime();
229
+ return new Date(b.classDate).toISOString().split("T")[0] === ds;
250
230
  } catch {
251
231
  return false;
252
232
  }
@@ -511,7 +491,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
511
491
  }
512
492
  )
513
493
  ] }),
514
- cls.showAvailability !== false && /* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
494
+ /* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
515
495
  /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
516
496
  available,
517
497
  "/",
@@ -523,10 +503,9 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
523
503
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
524
504
  /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
525
505
  /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
526
- cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
527
- cls.description && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-dark,#200C02) opacity-70 block truncate", children: cls.description })
506
+ cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
528
507
  ] }),
529
- cls.showAvailability !== false && /* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
508
+ /* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
530
509
  /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
531
510
  available,
532
511
  "/",
@@ -551,7 +530,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
551
530
  if (!cls.id) return;
552
531
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
553
532
  },
554
- children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
533
+ children: isFull ? "Join Waitlist" : "Book Now"
555
534
  }
556
535
  )
557
536
  ] })
@@ -593,17 +572,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
593
572
  const [isHovered, setIsHovered] = useState2(false);
594
573
  const [modalState, setModalState] = useState2(null);
595
574
  const switchScheduleIdRef = useRef(null);
596
- const liveScheduleIdRef = useRef(null);
597
- const refetchLiveSchedule = useCallback(async () => {
598
- const id = liveScheduleIdRef.current;
599
- if (!id) return;
600
- try {
601
- const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
602
- const data = await res.json();
603
- if (data?.id) setSchedule(data);
604
- } catch {
605
- }
606
- }, []);
607
575
  const dates = useMemo(() => {
608
576
  const today = /* @__PURE__ */ new Date();
609
577
  today.setHours(0, 0, 0, 0);
@@ -623,18 +591,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
623
591
  }
624
592
  }
625
593
  }, [schedule, dates]);
626
- useEffect(() => {
627
- if (typeof window === "undefined") return;
628
- const onVisible = () => {
629
- if (!document.hidden) void refetchLiveSchedule();
630
- };
631
- window.addEventListener("focus", refetchLiveSchedule);
632
- document.addEventListener("visibilitychange", onVisible);
633
- return () => {
634
- window.removeEventListener("focus", refetchLiveSchedule);
635
- document.removeEventListener("visibilitychange", onVisible);
636
- };
637
- }, [refetchLiveSchedule]);
638
594
  useEffect(() => {
639
595
  if (typeof window === "undefined") return;
640
596
  const isInEditor = window.self !== window.top;
@@ -696,7 +652,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
696
652
  setLoading(false);
697
653
  return;
698
654
  }
699
- liveScheduleIdRef.current = effectiveId;
655
+ ;
700
656
  (async () => {
701
657
  try {
702
658
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -753,14 +709,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
753
709
  const data = await res.json().catch(() => ({}));
754
710
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
755
711
  }
756
- await refetchLiveSchedule();
757
712
  };
758
713
  const handleReplaceSchedule = () => {
759
714
  setIsHovered(false);
760
- window.parent.postMessage(
761
- { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
762
- "*"
763
- );
715
+ if (schedule) {
716
+ window.parent.postMessage({
717
+ type: "ow:schedule-connected",
718
+ schedule: { id: schedule.id, name: schedule.name },
719
+ insertAfter
720
+ }, "*");
721
+ } else {
722
+ window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
723
+ }
764
724
  };
765
725
  if (!inEditor && !loading && !schedule) return null;
766
726
  const sectionId = `scheduling-${insertAfter}`;
@@ -803,19 +763,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
803
763
  ),
804
764
  /* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
805
765
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
806
- /* @__PURE__ */ jsx2(
807
- "h2",
808
- {
809
- className: "font-display text-center m-0 text-(--color-dark,#200C02)",
810
- style: {
811
- fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
812
- fontWeight: "var(--font-weight-heading, 400)",
813
- lineHeight: 1.05,
814
- letterSpacing: "-0.02em"
815
- },
816
- children: schedule?.name ?? "Book an appointment"
817
- }
818
- ),
766
+ /* @__PURE__ */ jsx2("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
819
767
  schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
820
768
  ] }),
821
769
  /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4401,7 +4349,6 @@ function ItemActionToolbar({
4401
4349
  onEditLink,
4402
4350
  onAddItem,
4403
4351
  onMore,
4404
- editLinkDisabled = false,
4405
4352
  addItemDisabled = true,
4406
4353
  moreDisabled = true,
4407
4354
  tooltipSide = "bottom"
@@ -4410,12 +4357,10 @@ function ItemActionToolbar({
4410
4357
  /* @__PURE__ */ jsx8(
4411
4358
  ToolbarActionTooltip,
4412
4359
  {
4413
- label: "Edit link",
4360
+ label: "Add link",
4414
4361
  side: tooltipSide,
4415
- disabled: editLinkDisabled,
4416
4362
  buttonProps: {
4417
4363
  onMouseDown: (e) => {
4418
- if (editLinkDisabled) return;
4419
4364
  e.preventDefault();
4420
4365
  e.stopPropagation();
4421
4366
  onEditLink?.();
@@ -4493,7 +4438,7 @@ function getChromeStyle(state) {
4493
4438
  case "dragging":
4494
4439
  return {
4495
4440
  border: `2px solid ${PRIMARY}`,
4496
- boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4441
+ boxShadow: DRAG_SHADOW
4497
4442
  };
4498
4443
  default:
4499
4444
  return {};
@@ -4509,8 +4454,6 @@ function ItemInteractionLayer({
4509
4454
  dragHandleLabel = "Reorder item",
4510
4455
  onDragHandleDragStart,
4511
4456
  onDragHandleDragEnd,
4512
- onItemPointerDown,
4513
- onItemClick,
4514
4457
  chromeGap = 6,
4515
4458
  className
4516
4459
  }) {
@@ -4518,8 +4461,7 @@ function ItemInteractionLayer({
4518
4461
  const isActive = state === "active-top" || state === "active-bottom";
4519
4462
  const isDragging = state === "dragging";
4520
4463
  const showToolbar = isActive && toolbar;
4521
- const showDragHandle = (isActive || isDragging) && showHandle;
4522
- const itemDragEnabled = isActive && showHandle && !isDragging && !dragDisabled;
4464
+ const showDragHandle = isActive && showHandle && !isDragging;
4523
4465
  return /* @__PURE__ */ jsxs4(
4524
4466
  "div",
4525
4467
  {
@@ -4544,28 +4486,11 @@ function ItemInteractionLayer({
4544
4486
  style: getChromeStyle(state)
4545
4487
  }
4546
4488
  ),
4547
- itemDragEnabled && /* @__PURE__ */ jsx9(
4548
- "div",
4549
- {
4550
- "data-ohw-item-drag-surface": "",
4551
- className: "pointer-events-auto absolute inset-0 cursor-grab rounded-lg active:cursor-grabbing",
4552
- onPointerDown: (e) => {
4553
- if (e.button !== 0) return;
4554
- onItemPointerDown?.(e);
4555
- },
4556
- onClick: (e) => {
4557
- e.preventDefault();
4558
- e.stopPropagation();
4559
- onItemClick?.(e.clientX, e.clientY);
4560
- }
4561
- }
4562
- ),
4563
4489
  showDragHandle && /* @__PURE__ */ jsx9(
4564
4490
  "div",
4565
4491
  {
4566
4492
  "data-ohw-drag-handle-container": "",
4567
- className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4568
- style: isDragging ? { visibility: "hidden" } : void 0,
4493
+ className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4569
4494
  children: /* @__PURE__ */ jsx9(
4570
4495
  DragHandle,
4571
4496
  {
@@ -4788,12 +4713,15 @@ function MediaOverlay({
4788
4713
  className: "flex items-center justify-center cursor-pointer",
4789
4714
  style: {
4790
4715
  ...box,
4791
- // Keep the full-size visual overlay click-through so carousel controls and editable
4792
- // content underneath remain interactive. The Replace button opts back in below.
4793
- pointerEvents: "none",
4716
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4717
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4718
+ // in-document, pointer-events does it natively. The button below opts back in, so
4719
+ // Replace still works.
4720
+ pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4794
4721
  boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4795
4722
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4796
4723
  },
4724
+ onClick: () => onReplace(hover.key),
4797
4725
  children: /* @__PURE__ */ jsxs5(
4798
4726
  Button,
4799
4727
  {
@@ -4818,64 +4746,9 @@ function MediaOverlay({
4818
4746
  ] });
4819
4747
  }
4820
4748
 
4821
- // src/ui/CarouselOverlay.tsx
4822
- import { GalleryHorizontal } from "lucide-react";
4823
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
4824
- var OVERLAY_BUTTON_STYLE2 = {
4825
- pointerEvents: "auto",
4826
- fontFamily: "Inter, sans-serif",
4827
- fontSize: 12,
4828
- color: "#000"
4829
- };
4830
- function CarouselOverlay({
4831
- hover,
4832
- onEdit
4833
- }) {
4834
- const { rect } = hover;
4835
- return /* @__PURE__ */ jsx12(
4836
- "div",
4837
- {
4838
- "data-ohw-bridge": "",
4839
- "data-ohw-carousel-overlay": "",
4840
- className: "flex cursor-pointer items-center justify-center",
4841
- style: {
4842
- position: "fixed",
4843
- top: rect.top,
4844
- left: rect.left,
4845
- width: rect.width,
4846
- height: rect.height,
4847
- zIndex: 2147483646,
4848
- pointerEvents: "auto",
4849
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4850
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4851
- },
4852
- onClick: () => onEdit(hover.key),
4853
- children: /* @__PURE__ */ jsxs6(
4854
- Button,
4855
- {
4856
- "data-ohw-carousel-overlay": "",
4857
- variant: "outline",
4858
- size: "sm",
4859
- className: "cursor-pointer gap-1.5 hover:bg-background",
4860
- style: OVERLAY_BUTTON_STYLE2,
4861
- onMouseDown: (e) => e.preventDefault(),
4862
- onClick: (e) => {
4863
- e.stopPropagation();
4864
- onEdit(hover.key);
4865
- },
4866
- children: [
4867
- /* @__PURE__ */ jsx12(GalleryHorizontal, { size: 14 }),
4868
- "Edit gallery"
4869
- ]
4870
- }
4871
- )
4872
- }
4873
- );
4874
- }
4875
-
4876
4749
  // src/OhhwellsBridge.tsx
4877
- import { createPortal as createPortal2 } from "react-dom";
4878
- import { usePathname as usePathname2, useRouter as useRouter2, useSearchParams } from "next/navigation";
4750
+ import { createPortal } from "react-dom";
4751
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
4879
4752
 
4880
4753
  // src/lib/session-search.ts
4881
4754
  var STORAGE_KEY = "ohw-preserved-search";
@@ -4905,7 +4778,6 @@ function parseSectionsFromRoot(root) {
4905
4778
  for (const el of root.querySelectorAll("[data-ohw-section]")) {
4906
4779
  const id = el.getAttribute("data-ohw-section") ?? "";
4907
4780
  if (!id || seen.has(id)) continue;
4908
- if (el.parentElement?.closest("[data-ohw-section]")) continue;
4909
4781
  seen.add(id);
4910
4782
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
4911
4783
  sections.push({ id, label });
@@ -5152,22 +5024,54 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5152
5024
  tick();
5153
5025
  }
5154
5026
 
5155
- // src/ui/link-modal/LinkPopover.tsx
5156
- import { useEffect as useEffect6 } from "react";
5157
-
5158
5027
  // src/ui/dialog.tsx
5159
5028
  import * as React7 from "react";
5160
5029
  import { Dialog as DialogPrimitive } from "radix-ui";
5161
- import { X } from "lucide-react";
5030
+
5031
+ // src/ui/icons.tsx
5032
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
5033
+ function IconX({ className, ...props }) {
5034
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5035
+ /* @__PURE__ */ jsx12("path", { d: "M18 6 6 18" }),
5036
+ /* @__PURE__ */ jsx12("path", { d: "m6 6 12 12" })
5037
+ ] });
5038
+ }
5039
+ function IconChevronDown({ className, ...props }) {
5040
+ return /* @__PURE__ */ jsx12("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) });
5041
+ }
5042
+ function IconFile({ className, ...props }) {
5043
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5044
+ /* @__PURE__ */ jsx12("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
5045
+ /* @__PURE__ */ jsx12("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
5046
+ ] });
5047
+ }
5048
+ function IconInfo({ className, ...props }) {
5049
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5050
+ /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "10" }),
5051
+ /* @__PURE__ */ jsx12("path", { d: "M12 16v-4" }),
5052
+ /* @__PURE__ */ jsx12("path", { d: "M12 8h.01" })
5053
+ ] });
5054
+ }
5055
+ function IconArrowRight({ className, ...props }) {
5056
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5057
+ /* @__PURE__ */ jsx12("path", { d: "M5 12h14" }),
5058
+ /* @__PURE__ */ jsx12("path", { d: "m12 5 7 7-7 7" })
5059
+ ] });
5060
+ }
5061
+ function IconSection({ className, ...props }) {
5062
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5063
+ /* @__PURE__ */ jsx12("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
5064
+ /* @__PURE__ */ jsx12("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
5065
+ /* @__PURE__ */ jsx12("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
5066
+ ] });
5067
+ }
5068
+
5069
+ // src/ui/dialog.tsx
5162
5070
  import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5163
- function Dialog2({
5164
- ...props
5165
- }) {
5071
+ function Dialog2({ ...props }) {
5166
5072
  return /* @__PURE__ */ jsx13(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
5167
5073
  }
5168
- function DialogPortal({
5169
- ...props
5170
- }) {
5074
+ function DialogPortal({ ...props }) {
5171
5075
  return /* @__PURE__ */ jsx13(DialogPrimitive.Portal, { ...props });
5172
5076
  }
5173
5077
  function DialogOverlay({
@@ -5184,69 +5088,52 @@ function DialogOverlay({
5184
5088
  }
5185
5089
  );
5186
5090
  }
5187
- var DialogContent = React7.forwardRef(
5188
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5189
- const positionMode = container ? "absolute" : "fixed";
5190
- return /* @__PURE__ */ jsxs7(DialogPortal, { container: container ?? void 0, children: [
5191
- /* @__PURE__ */ jsx13(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5192
- /* @__PURE__ */ jsxs7(
5193
- DialogPrimitive.Content,
5194
- {
5195
- ref,
5196
- "data-slot": "dialog-content",
5197
- className: cn(
5198
- positionMode,
5199
- "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",
5200
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5201
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
5202
- className
5203
- ),
5204
- onMouseDown: (e) => e.stopPropagation(),
5205
- ...props,
5206
- children: [
5207
- children,
5208
- showCloseButton ? /* @__PURE__ */ jsx13(
5209
- DialogPrimitive.Close,
5210
- {
5211
- type: "button",
5212
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5213
- "aria-label": "Close",
5214
- children: /* @__PURE__ */ jsx13(X, { size: 16, "aria-hidden": true })
5215
- }
5216
- ) : null
5217
- ]
5218
- }
5219
- )
5220
- ] });
5221
- }
5222
- );
5091
+ var DialogContent = React7.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
5092
+ const positionMode = container ? "absolute" : "fixed";
5093
+ return /* @__PURE__ */ jsxs7(DialogPortal, { container: container ?? void 0, children: [
5094
+ /* @__PURE__ */ jsx13(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5095
+ /* @__PURE__ */ jsxs7(
5096
+ DialogPrimitive.Content,
5097
+ {
5098
+ ref,
5099
+ "data-slot": "dialog-content",
5100
+ className: cn(
5101
+ positionMode,
5102
+ "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",
5103
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5104
+ container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5105
+ className
5106
+ ),
5107
+ onMouseDown: (e) => e.stopPropagation(),
5108
+ ...props,
5109
+ children: [
5110
+ children,
5111
+ showCloseButton ? /* @__PURE__ */ jsx13(
5112
+ DialogPrimitive.Close,
5113
+ {
5114
+ type: "button",
5115
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5116
+ "aria-label": "Close",
5117
+ children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
5118
+ }
5119
+ ) : null
5120
+ ]
5121
+ }
5122
+ )
5123
+ ] });
5124
+ });
5223
5125
  DialogContent.displayName = DialogPrimitive.Content.displayName;
5224
- function DialogHeader({
5225
- className,
5226
- ...props
5227
- }) {
5126
+ function DialogHeader({ className, ...props }) {
5228
5127
  return /* @__PURE__ */ jsx13("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5229
5128
  }
5230
- function DialogFooter({
5231
- className,
5232
- ...props
5233
- }) {
5234
- return /* @__PURE__ */ jsx13(
5235
- "div",
5236
- {
5237
- className: cn("flex items-center justify-end gap-2", className),
5238
- ...props
5239
- }
5240
- );
5129
+ function DialogFooter({ className, ...props }) {
5130
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5241
5131
  }
5242
5132
  var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
5243
5133
  DialogPrimitive.Title,
5244
5134
  {
5245
5135
  ref,
5246
- className: cn(
5247
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
5248
- className
5249
- ),
5136
+ className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5250
5137
  ...props
5251
5138
  }
5252
5139
  ));
@@ -5262,40 +5149,19 @@ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @
5262
5149
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
5263
5150
  var DialogClose = DialogPrimitive.Close;
5264
5151
 
5265
- // src/ui/link-modal/LinkEditorPanel.tsx
5266
- import { Info, X as X3 } from "lucide-react";
5267
-
5268
5152
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5269
- import { ArrowRight, File, GalleryVertical } from "lucide-react";
5270
5153
  import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5271
- function DestinationBreadcrumb({
5272
- pageTitle,
5273
- sectionLabel
5274
- }) {
5154
+ function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5275
5155
  return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-2", children: [
5276
- /* @__PURE__ */ jsx14("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5156
+ /* @__PURE__ */ jsx14("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5277
5157
  /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3", children: [
5278
5158
  /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
5279
- /* @__PURE__ */ jsx14(File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5280
- /* @__PURE__ */ jsx14("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5159
+ /* @__PURE__ */ jsx14(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5160
+ /* @__PURE__ */ jsx14("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5281
5161
  ] }),
5282
- /* @__PURE__ */ jsx14(
5283
- ArrowRight,
5284
- {
5285
- size: 16,
5286
- className: "shrink-0 text-muted-foreground",
5287
- "aria-hidden": true
5288
- }
5289
- ),
5162
+ /* @__PURE__ */ jsx14(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5290
5163
  /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5291
- /* @__PURE__ */ jsx14(
5292
- GalleryVertical,
5293
- {
5294
- size: 16,
5295
- className: "shrink-0 text-foreground",
5296
- "aria-hidden": true
5297
- }
5298
- ),
5164
+ /* @__PURE__ */ jsx14(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5299
5165
  /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5300
5166
  ] })
5301
5167
  ] })
@@ -5303,13 +5169,8 @@ function DestinationBreadcrumb({
5303
5169
  }
5304
5170
 
5305
5171
  // src/ui/link-modal/SectionTreeItem.tsx
5306
- import { GalleryVertical as GalleryVertical2 } from "lucide-react";
5307
5172
  import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
5308
- function SectionTreeItem({
5309
- section,
5310
- onSelect,
5311
- selected
5312
- }) {
5173
+ function SectionTreeItem({ section, onSelect, selected }) {
5313
5174
  const interactive = Boolean(onSelect);
5314
5175
  return /* @__PURE__ */ jsxs9("div", { className: "flex h-9 w-full items-end pl-3", children: [
5315
5176
  /* @__PURE__ */ jsx15(
@@ -5337,23 +5198,25 @@ function SectionTreeItem({
5337
5198
  interactive && selected && "border-primary"
5338
5199
  ),
5339
5200
  children: [
5340
- /* @__PURE__ */ jsx15(
5341
- GalleryVertical2,
5342
- {
5343
- size: 16,
5344
- className: "shrink-0 text-foreground",
5345
- "aria-hidden": true
5346
- }
5347
- ),
5201
+ /* @__PURE__ */ jsx15(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5348
5202
  /* @__PURE__ */ jsx15("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5349
5203
  ]
5350
5204
  }
5351
5205
  )
5352
5206
  ] });
5353
5207
  }
5208
+ function SectionPickerList({ sections, onSelect }) {
5209
+ if (sections.length === 0) {
5210
+ return /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5211
+ }
5212
+ return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
5213
+ /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5214
+ sections.map((section) => /* @__PURE__ */ jsx15(SectionTreeItem, { section, onSelect }, section.id))
5215
+ ] });
5216
+ }
5354
5217
 
5355
5218
  // src/ui/link-modal/UrlOrPageInput.tsx
5356
- import { useEffect as useEffect3, useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5219
+ import { useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5357
5220
 
5358
5221
  // src/ui/input.tsx
5359
5222
  import * as React8 from "react";
@@ -5392,11 +5255,8 @@ function Label({ className, ...props }) {
5392
5255
  }
5393
5256
 
5394
5257
  // src/ui/link-modal/UrlOrPageInput.tsx
5395
- import { ChevronDown, File as File2, X as X2 } from "lucide-react";
5396
5258
  import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5397
- function FieldChevron({
5398
- onClick
5399
- }) {
5259
+ function FieldChevron({ onClick }) {
5400
5260
  return /* @__PURE__ */ jsx18(
5401
5261
  "button",
5402
5262
  {
@@ -5405,7 +5265,7 @@ function FieldChevron({
5405
5265
  onClick,
5406
5266
  "aria-label": "Open page list",
5407
5267
  tabIndex: -1,
5408
- children: /* @__PURE__ */ jsx18(ChevronDown, { size: 16 })
5268
+ children: /* @__PURE__ */ jsx18(IconChevronDown, {})
5409
5269
  }
5410
5270
  );
5411
5271
  }
@@ -5423,29 +5283,7 @@ function UrlOrPageInput({
5423
5283
  }) {
5424
5284
  const inputId = useId2();
5425
5285
  const inputRef = useRef3(null);
5426
- const rootRef = useRef3(null);
5427
5286
  const [isFocused, setIsFocused] = useState3(false);
5428
- useEffect3(() => {
5429
- if (!dropdownOpen) return;
5430
- const isOutside = (e) => {
5431
- const root = rootRef.current;
5432
- if (!root) return true;
5433
- const path = typeof e.composedPath === "function" ? e.composedPath() : [];
5434
- if (path.includes(root)) return false;
5435
- const target = e.target;
5436
- return !(target instanceof Node && root.contains(target));
5437
- };
5438
- const closeIfOutside = (e) => {
5439
- if (!isOutside(e)) return;
5440
- onDropdownOpenChange(false);
5441
- };
5442
- document.addEventListener("pointerdown", closeIfOutside, true);
5443
- document.addEventListener("mousedown", closeIfOutside, true);
5444
- return () => {
5445
- document.removeEventListener("pointerdown", closeIfOutside, true);
5446
- document.removeEventListener("mousedown", closeIfOutside, true);
5447
- };
5448
- }, [dropdownOpen, onDropdownOpenChange]);
5449
5287
  const openDropdown = () => {
5450
5288
  if (readOnly || filteredPages.length === 0) return;
5451
5289
  onDropdownOpenChange(true);
@@ -5463,21 +5301,14 @@ function UrlOrPageInput({
5463
5301
  requestAnimationFrame(() => inputRef.current?.focus());
5464
5302
  };
5465
5303
  const fieldClassName = cn(
5466
- "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5304
+ "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]",
5467
5305
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5468
5306
  );
5469
5307
  return /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5470
5308
  /* @__PURE__ */ jsx18(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5471
- /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: "relative w-full", children: [
5309
+ /* @__PURE__ */ jsxs10("div", { className: "relative w-full", children: [
5472
5310
  /* @__PURE__ */ jsxs10("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5473
- selectedPage ? /* @__PURE__ */ jsx18("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx18(
5474
- File2,
5475
- {
5476
- size: 16,
5477
- className: "shrink-0 text-foreground",
5478
- "aria-hidden": true
5479
- }
5480
- ) }) : null,
5311
+ selectedPage ? /* @__PURE__ */ jsx18("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx18(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5481
5312
  readOnly ? /* @__PURE__ */ jsx18("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx18(
5482
5313
  Input,
5483
5314
  {
@@ -5489,14 +5320,7 @@ function UrlOrPageInput({
5489
5320
  setIsFocused(true);
5490
5321
  if (!selectedPage) openDropdown();
5491
5322
  },
5492
- onBlur: () => {
5493
- setIsFocused(false);
5494
- requestAnimationFrame(() => {
5495
- if (!rootRef.current?.contains(document.activeElement)) {
5496
- onDropdownOpenChange(false);
5497
- }
5498
- });
5499
- },
5323
+ onBlur: () => setIsFocused(false),
5500
5324
  placeholder,
5501
5325
  className: cn(
5502
5326
  "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",
@@ -5512,7 +5336,7 @@ function UrlOrPageInput({
5512
5336
  onMouseDown: clearSelection,
5513
5337
  "aria-label": "Clear selected page",
5514
5338
  tabIndex: -1,
5515
- children: /* @__PURE__ */ jsx18(X2, { size: 16, "aria-hidden": true })
5339
+ children: /* @__PURE__ */ jsx18(IconX, { "aria-hidden": true })
5516
5340
  }
5517
5341
  ) : null,
5518
5342
  !readOnly ? /* @__PURE__ */ jsx18(FieldChevron, { onClick: toggleDropdown }) : null
@@ -5521,7 +5345,7 @@ function UrlOrPageInput({
5521
5345
  "div",
5522
5346
  {
5523
5347
  "data-ohw-link-page-dropdown": "",
5524
- className: "absolute left-0 right-0 top-[calc(100%+4px)] z-50 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
5348
+ 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",
5525
5349
  onMouseDown: (e) => e.preventDefault(),
5526
5350
  children: filteredPages.map((page) => /* @__PURE__ */ jsxs10(
5527
5351
  "button",
@@ -5530,7 +5354,7 @@ function UrlOrPageInput({
5530
5354
  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",
5531
5355
  onClick: () => onPageSelect(page),
5532
5356
  children: [
5533
- /* @__PURE__ */ jsx18(File2, { size: 16, className: "shrink-0", "aria-hidden": true }),
5357
+ /* @__PURE__ */ jsx18(IconFile, { className: "shrink-0", "aria-hidden": true }),
5534
5358
  /* @__PURE__ */ jsx18("span", { className: "truncate", children: page.title })
5535
5359
  ]
5536
5360
  },
@@ -5543,458 +5367,30 @@ function UrlOrPageInput({
5543
5367
  ] });
5544
5368
  }
5545
5369
 
5546
- // src/ui/link-modal/LinkEditorPanel.tsx
5547
- import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5548
- function LinkEditorPanel({ state, onClose }) {
5549
- const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5550
- return /* @__PURE__ */ jsxs11(Fragment3, { children: [
5551
- /* @__PURE__ */ jsx19(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx19(
5552
- "button",
5553
- {
5554
- type: "button",
5555
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5556
- "aria-label": "Close",
5557
- onClick: onClose,
5558
- children: /* @__PURE__ */ jsx19(X3, { size: 16, "aria-hidden": true })
5559
- }
5560
- ) }),
5561
- /* @__PURE__ */ jsx19(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx19(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5562
- /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5563
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx19(
5564
- DestinationBreadcrumb,
5565
- {
5566
- pageTitle: state.selectedPage.title,
5567
- sectionLabel: state.selectedSection.label
5568
- }
5569
- ) : /* @__PURE__ */ jsx19(
5570
- UrlOrPageInput,
5571
- {
5572
- value: state.searchValue,
5573
- selectedPage: state.selectedPage,
5574
- dropdownOpen: state.dropdownOpen,
5575
- onDropdownOpenChange: state.setDropdownOpen,
5576
- filteredPages: state.filteredPages,
5577
- onInputChange: state.handleInputChange,
5578
- onPageSelect: state.handlePageSelect,
5579
- urlError: state.urlError
5580
- }
5581
- ),
5582
- state.showChooseSection ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col justify-center gap-2", children: [
5583
- /* @__PURE__ */ jsx19(
5584
- Button,
5585
- {
5586
- type: "button",
5587
- variant: "outline",
5588
- className: "w-fit cursor-pointer",
5589
- size: "sm",
5590
- onClick: state.handleChooseSection,
5591
- children: "Choose a section"
5592
- }
5593
- ),
5594
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5595
- /* @__PURE__ */ jsx19(Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5596
- /* @__PURE__ */ jsx19("span", { children: "Pick a section this link should scroll to." })
5597
- ] })
5598
- ] }) : null,
5599
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx19(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5600
- ] }),
5601
- /* @__PURE__ */ jsxs11(DialogFooter, { className: "w-full px-6 pb-6", children: [
5602
- /* @__PURE__ */ jsx19(
5603
- Button,
5604
- {
5605
- type: "button",
5606
- variant: isCancel ? "outline" : "ghost",
5607
- className: "leading-6 shadow-none cursor-pointer",
5608
- style: isCancel ? {
5609
- backgroundColor: "var(--ohw-background)",
5610
- borderColor: "var(--ohw-border)",
5611
- color: "var(--ohw-foreground)"
5612
- } : void 0,
5613
- onClick: state.handleSecondary,
5614
- children: state.secondaryLabel
5615
- }
5616
- ),
5617
- /* @__PURE__ */ jsx19(
5618
- Button,
5619
- {
5620
- type: "button",
5621
- className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5622
- style: {
5623
- backgroundColor: "var(--ohw-primary)",
5624
- color: "var(--ohw-primary-foreground)"
5625
- },
5626
- disabled: !state.isValid,
5627
- onClick: state.handleSubmit,
5628
- children: state.submitLabel
5629
- }
5630
- )
5631
- ] })
5632
- ] });
5633
- }
5634
-
5635
- // src/ui/link-modal/SectionPickerOverlay.tsx
5636
- import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
5637
- import { createPortal } from "react-dom";
5638
- import { ArrowLeft, Check } from "lucide-react";
5639
- import { usePathname, useRouter } from "next/navigation";
5640
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
5641
- var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5642
- function rectsEqual(a, b) {
5643
- if (a.size !== b.size) return false;
5644
- for (const [id, rect] of a) {
5645
- const other = b.get(id);
5646
- if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
5647
- return false;
5648
- }
5649
- }
5650
- return true;
5651
- }
5652
- function readSectionRects(sectionIds) {
5653
- const next = /* @__PURE__ */ new Map();
5654
- for (const id of sectionIds) {
5655
- const el = document.querySelector(
5656
- `[data-ohw-section="${CSS.escape(id)}"]`
5657
- );
5658
- if (el) next.set(id, el.getBoundingClientRect());
5659
- }
5660
- return next;
5661
- }
5662
- function hitTestSectionId(x, y, sectionIds, rects) {
5663
- for (const id of sectionIds) {
5664
- const rect = rects.get(id);
5665
- if (!rect || rect.width <= 0 || rect.height <= 0) continue;
5666
- if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
5667
- return id;
5668
- }
5669
- }
5670
- return null;
5671
- }
5672
- function useSectionRects(sectionIdsKey, enabled) {
5673
- const [rects, setRects] = useState4(/* @__PURE__ */ new Map());
5674
- const sectionIds = useMemo2(
5675
- () => sectionIdsKey ? sectionIdsKey.split("\0") : [],
5676
- [sectionIdsKey]
5370
+ // src/ui/link-modal/useLinkModalState.ts
5371
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
5372
+ function useLinkModalState({
5373
+ open,
5374
+ mode,
5375
+ pages,
5376
+ sections: _sections,
5377
+ sectionsByPath,
5378
+ initialTarget,
5379
+ existingTargets,
5380
+ onClose,
5381
+ onSubmit
5382
+ }) {
5383
+ const availablePages = useMemo2(
5384
+ () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
5385
+ [mode, pages, existingTargets]
5677
5386
  );
5678
- useEffect4(() => {
5679
- if (!enabled || sectionIds.length === 0) {
5680
- setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
5681
- return;
5682
- }
5683
- const update = () => {
5684
- const next = readSectionRects(sectionIds);
5685
- setRects((prev) => rectsEqual(prev, next) ? prev : next);
5686
- };
5687
- update();
5688
- const opts = { capture: true, passive: true };
5689
- window.addEventListener("scroll", update, opts);
5690
- window.addEventListener("resize", update);
5691
- const vv = window.visualViewport;
5692
- vv?.addEventListener("resize", update);
5693
- vv?.addEventListener("scroll", update);
5694
- const ro = new ResizeObserver(update);
5695
- for (const id of sectionIds) {
5696
- const el = document.querySelector(
5697
- `[data-ohw-section="${CSS.escape(id)}"]`
5698
- );
5699
- if (el) ro.observe(el);
5700
- }
5701
- return () => {
5702
- window.removeEventListener("scroll", update, opts);
5703
- window.removeEventListener("resize", update);
5704
- vv?.removeEventListener("resize", update);
5705
- vv?.removeEventListener("scroll", update);
5706
- ro.disconnect();
5707
- };
5708
- }, [sectionIds, enabled]);
5709
- return rects;
5710
- }
5711
- function SectionPickerOverlay({
5712
- pagePath,
5713
- sections,
5714
- onBack,
5715
- onSelect
5716
- }) {
5717
- const router = useRouter();
5718
- const pathname = usePathname();
5719
- const [selectedId, setSelectedId] = useState4(null);
5720
- const [hoveredId, setHoveredId] = useState4(null);
5721
- const pointerRef = useRef4(null);
5722
- const pointerScreenRef = useRef4(null);
5723
- const iframeOffsetTopRef = useRef4(null);
5724
- const sectionIdsRef = useRef4([]);
5725
- const [chromeClip, setChromeClip] = useState4(
5726
- () => ({
5727
- top: 0,
5728
- bottom: typeof window !== "undefined" ? window.innerHeight : 0
5729
- })
5730
- );
5731
- const normalizedTarget = normalizePath(pagePath);
5732
- const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
5733
- useEffect4(() => {
5734
- if (isOnTargetPage) return;
5735
- const search = readPreservedSearch();
5736
- router.push(`${normalizedTarget}${search}`);
5737
- }, [isOnTargetPage, normalizedTarget, router]);
5738
- useEffect4(() => {
5739
- document.documentElement.setAttribute("data-ohw-section-picking", "");
5740
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5741
- el.removeAttribute("data-ohw-hovered");
5742
- });
5743
- document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => {
5744
- el.removeAttribute("data-ohw-state-hovered");
5745
- });
5746
- return () => {
5747
- document.documentElement.removeAttribute("data-ohw-section-picking");
5748
- };
5749
- }, []);
5750
- const liveSections = useMemo2(() => {
5751
- if (!isOnTargetPage) return sections;
5752
- const live = collectSectionsFromDom();
5753
- if (live.length === 0) return sections;
5754
- const labels = new Map(sections.map((s) => [s.id, s.label]));
5755
- return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5756
- }, [isOnTargetPage, sections, pathname]);
5757
- const sectionIdsKey = useMemo2(
5758
- () => liveSections.map((s) => s.id).join("\0"),
5759
- [liveSections]
5760
- );
5761
- const sectionIds = useMemo2(
5762
- () => liveSections.map((s) => s.id),
5763
- [liveSections]
5764
- );
5765
- sectionIdsRef.current = sectionIds;
5766
- const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5767
- const applyHoverAt = useCallback2(
5768
- (x, y, liveRects) => {
5769
- const ids = sectionIdsRef.current;
5770
- const map = liveRects ?? readSectionRects(ids);
5771
- const next = hitTestSectionId(x, y, ids, map);
5772
- setHoveredId((prev) => prev === next ? prev : next);
5773
- },
5774
- []
5775
- );
5776
- useEffect4(() => {
5777
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
5778
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
5779
- const bottom = Math.min(
5780
- window.innerHeight,
5781
- visibleCanvasTop + canvasH - iframeOffsetTop
5782
- );
5783
- setChromeClip(
5784
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
5785
- );
5786
- };
5787
- const onParentScroll = (e) => {
5788
- if (e.data?.type !== "ow:parent-scroll") return;
5789
- const { iframeOffsetTop, headerH, canvasH } = e.data;
5790
- applyClip(iframeOffsetTop, headerH, canvasH);
5791
- iframeOffsetTopRef.current = iframeOffsetTop;
5792
- if (!pointerScreenRef.current && pointerRef.current) {
5793
- pointerScreenRef.current = {
5794
- x: pointerRef.current.x,
5795
- y: pointerRef.current.y + iframeOffsetTop
5796
- };
5797
- }
5798
- const screen = pointerScreenRef.current;
5799
- if (screen) {
5800
- const next = { x: screen.x, y: screen.y - iframeOffsetTop };
5801
- pointerRef.current = next;
5802
- applyHoverAt(next.x, next.y);
5803
- }
5804
- };
5805
- const onResize = () => {
5806
- setChromeClip((prev) => {
5807
- const bottom = Math.max(prev.top, window.innerHeight);
5808
- return prev.bottom === bottom ? prev : { ...prev, bottom };
5809
- });
5810
- };
5811
- window.addEventListener("message", onParentScroll);
5812
- window.addEventListener("resize", onResize);
5813
- return () => {
5814
- window.removeEventListener("message", onParentScroll);
5815
- window.removeEventListener("resize", onResize);
5816
- };
5817
- }, [applyHoverAt]);
5818
- useEffect4(() => {
5819
- const ptr = pointerRef.current;
5820
- if (!ptr) return;
5821
- applyHoverAt(ptr.x, ptr.y, rects);
5822
- }, [rects, applyHoverAt]);
5823
- useEffect4(() => {
5824
- const rememberPointer = (clientX, clientY) => {
5825
- pointerRef.current = { x: clientX, y: clientY };
5826
- const top = iframeOffsetTopRef.current;
5827
- if (top != null) {
5828
- pointerScreenRef.current = { x: clientX, y: clientY + top };
5829
- }
5830
- applyHoverAt(clientX, clientY, rects);
5831
- };
5832
- const onPointerMove = (e) => {
5833
- rememberPointer(e.clientX, e.clientY);
5834
- };
5835
- const onPointerSync = (e) => {
5836
- if (e.data?.type !== "ow:pointer-sync") return;
5837
- const { clientX, clientY } = e.data;
5838
- rememberPointer(clientX, clientY);
5839
- };
5840
- window.addEventListener("pointermove", onPointerMove, { passive: true });
5841
- window.addEventListener("message", onPointerSync);
5842
- return () => {
5843
- window.removeEventListener("pointermove", onPointerMove);
5844
- window.removeEventListener("message", onPointerSync);
5845
- };
5846
- }, [rects, applyHoverAt]);
5847
- const handleSelect = useCallback2(
5848
- (section) => {
5849
- if (selectedId) return;
5850
- setSelectedId(section.id);
5851
- onSelect(section);
5852
- },
5853
- [selectedId, onSelect]
5854
- );
5855
- useEffect4(() => {
5856
- const onKeyDown = (e) => {
5857
- if (e.key === "Escape") {
5858
- e.preventDefault();
5859
- e.stopPropagation();
5860
- onBack();
5861
- }
5862
- };
5863
- window.addEventListener("keydown", onKeyDown, true);
5864
- return () => window.removeEventListener("keydown", onKeyDown, true);
5865
- }, [onBack]);
5866
- const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5867
- if (!portalRoot) return null;
5868
- return createPortal(
5869
- /* @__PURE__ */ jsxs12(
5870
- "div",
5871
- {
5872
- "data-ohw-section-picker": "",
5873
- "data-ohw-bridge": "",
5874
- className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
5875
- "aria-modal": true,
5876
- role: "dialog",
5877
- "aria-label": "Choose a section",
5878
- children: [
5879
- /* @__PURE__ */ jsx20(
5880
- "div",
5881
- {
5882
- className: "pointer-events-auto fixed left-5 z-[2]",
5883
- style: { top: chromeClip.top + 20 },
5884
- children: /* @__PURE__ */ jsxs12(
5885
- Button,
5886
- {
5887
- type: "button",
5888
- variant: "outline",
5889
- size: "sm",
5890
- className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5891
- onClick: onBack,
5892
- children: [
5893
- /* @__PURE__ */ jsx20(ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5894
- "Back"
5895
- ]
5896
- }
5897
- )
5898
- }
5899
- ),
5900
- /* @__PURE__ */ jsx20(
5901
- "div",
5902
- {
5903
- className: "pointer-events-none fixed left-1/2 z-[2] rounded-lg px-4 py-3 text-xs leading-4 tracking-[0.18px] text-white shadow-md",
5904
- style: {
5905
- top: chromeClip.bottom - 20,
5906
- transform: "translate(-50%, -100%)",
5907
- backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
5908
- },
5909
- "data-ohw-section-picker-hint": "",
5910
- children: "Click on section to select"
5911
- }
5912
- ),
5913
- !isOnTargetPage ? /* @__PURE__ */ jsx20(
5914
- "div",
5915
- {
5916
- className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md bg-background/90 px-3 py-2 text-sm text-muted-foreground shadow-sm",
5917
- style: { top: chromeClip.top + 64 },
5918
- children: "Loading page preview\u2026"
5919
- }
5920
- ) : null,
5921
- isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ jsx20("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5922
- isOnTargetPage ? liveSections.map((section) => {
5923
- const rect = rects.get(section.id);
5924
- if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5925
- const isSelected = selectedId === section.id;
5926
- const isHovered = hoveredId === section.id;
5927
- const isLit = isSelected || isHovered;
5928
- return /* @__PURE__ */ jsxs12(
5929
- "button",
5930
- {
5931
- type: "button",
5932
- className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
5933
- style: {
5934
- top: rect.top,
5935
- left: rect.left,
5936
- width: rect.width,
5937
- height: rect.height,
5938
- backgroundColor: isLit ? "transparent" : DIM_OVERLAY
5939
- },
5940
- "aria-label": `Select section ${section.label}`,
5941
- onClick: () => handleSelect(section),
5942
- children: [
5943
- isLit ? /* @__PURE__ */ jsx20(
5944
- "span",
5945
- {
5946
- className: "pointer-events-none absolute",
5947
- style: {
5948
- inset: 4,
5949
- borderRadius: "var(--radius, 6px)",
5950
- border: "1.5px dashed var(--primary, #0885FE)",
5951
- boxSizing: "border-box"
5952
- },
5953
- "aria-hidden": true
5954
- }
5955
- ) : null,
5956
- isSelected ? /* @__PURE__ */ jsx20(
5957
- "span",
5958
- {
5959
- className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5960
- style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5961
- "aria-hidden": true,
5962
- children: /* @__PURE__ */ jsx20(Check, { className: "size-5" })
5963
- }
5964
- ) : null
5965
- ]
5966
- },
5967
- section.id
5968
- );
5969
- }) : null
5970
- ]
5971
- }
5972
- ),
5973
- portalRoot
5974
- );
5975
- }
5976
-
5977
- // src/ui/link-modal/useLinkModalState.ts
5978
- import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo3, useState as useState5 } from "react";
5979
- function useLinkModalState({
5980
- open,
5981
- mode,
5982
- pages,
5983
- sections: _sections,
5984
- sectionsByPath,
5985
- initialTarget,
5986
- existingTargets: _existingTargets,
5987
- onClose,
5988
- onSubmit
5989
- }) {
5990
- const availablePages = useMemo3(() => pages, [pages]);
5991
- const [searchValue, setSearchValue] = useState5("");
5992
- const [selectedPage, setSelectedPage] = useState5(null);
5993
- const [selectedSection, setSelectedSection] = useState5(null);
5994
- const [step, setStep] = useState5("input");
5995
- const [dropdownOpen, setDropdownOpen] = useState5(false);
5996
- const [urlError, setUrlError] = useState5("");
5997
- const reset = useCallback3(() => {
5387
+ const [searchValue, setSearchValue] = useState4("");
5388
+ const [selectedPage, setSelectedPage] = useState4(null);
5389
+ const [selectedSection, setSelectedSection] = useState4(null);
5390
+ const [step, setStep] = useState4("input");
5391
+ const [dropdownOpen, setDropdownOpen] = useState4(false);
5392
+ const [urlError, setUrlError] = useState4("");
5393
+ const reset = useCallback(() => {
5998
5394
  setSearchValue("");
5999
5395
  setSelectedPage(null);
6000
5396
  setSelectedSection(null);
@@ -6002,7 +5398,7 @@ function useLinkModalState({
6002
5398
  setDropdownOpen(false);
6003
5399
  setUrlError("");
6004
5400
  }, []);
6005
- useEffect5(() => {
5401
+ useEffect3(() => {
6006
5402
  if (!open) return;
6007
5403
  if (mode === "edit" && initialTarget) {
6008
5404
  const { pageRoute } = parseTarget(initialTarget);
@@ -6017,12 +5413,12 @@ function useLinkModalState({
6017
5413
  }
6018
5414
  setDropdownOpen(false);
6019
5415
  setUrlError("");
6020
- }, [open, mode, initialTarget, reset]);
6021
- const filteredPages = useMemo3(() => {
5416
+ }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
5417
+ const filteredPages = useMemo2(() => {
6022
5418
  if (!searchValue.trim()) return availablePages;
6023
5419
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
6024
5420
  }, [availablePages, searchValue]);
6025
- const activeSections = useMemo3(() => {
5421
+ const activeSections = useMemo2(() => {
6026
5422
  if (!selectedPage) return [];
6027
5423
  return getSectionsForPath(sectionsByPath, selectedPage.path);
6028
5424
  }, [selectedPage, sectionsByPath]);
@@ -6064,14 +5460,11 @@ function useLinkModalState({
6064
5460
  const handleBackToSections = () => {
6065
5461
  setStep("sectionPicker");
6066
5462
  };
6067
- const handleSectionPickerBack = () => {
6068
- setStep("input");
6069
- };
6070
5463
  const handleClose = () => {
6071
5464
  reset();
6072
5465
  onClose();
6073
5466
  };
6074
- const isValid = useMemo3(() => {
5467
+ const isValid = useMemo2(() => {
6075
5468
  if (urlError) return false;
6076
5469
  if (showBreadcrumb) return true;
6077
5470
  if (selectedPage) return true;
@@ -6104,7 +5497,7 @@ function useLinkModalState({
6104
5497
  onSubmit(normalizeUrl(searchValue));
6105
5498
  handleClose();
6106
5499
  };
6107
- const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
5500
+ const submitLabel = mode === "edit" ? "Save" : "Create";
6108
5501
  const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
6109
5502
  const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
6110
5503
  const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
@@ -6131,29 +5524,23 @@ function useLinkModalState({
6131
5524
  handleChooseSection,
6132
5525
  handleSectionSelect,
6133
5526
  handleBackToSections,
6134
- handleSectionPickerBack,
6135
5527
  handleClose,
6136
5528
  handleSecondary,
6137
5529
  handleSubmit
6138
5530
  };
6139
5531
  }
6140
5532
 
6141
- // src/ui/link-modal/LinkPopover.tsx
6142
- import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
6143
- function postToParent(data) {
6144
- window.parent?.postMessage(data, "*");
6145
- }
6146
- function LinkPopover({
5533
+ // src/ui/link-modal/LinkEditorPanel.tsx
5534
+ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5535
+ function LinkEditorPanel({
6147
5536
  open = true,
6148
- panelRef,
6149
- portalContainer,
6150
- onClose,
6151
5537
  mode = "create",
6152
5538
  pages,
6153
5539
  sections = [],
6154
5540
  sectionsByPath,
6155
5541
  initialTarget,
6156
5542
  existingTargets = [],
5543
+ onClose,
6157
5544
  onSubmit
6158
5545
  }) {
6159
5546
  const state = useLinkModalState({
@@ -6167,109 +5554,115 @@ function LinkPopover({
6167
5554
  onClose,
6168
5555
  onSubmit
6169
5556
  });
6170
- const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
6171
- useEffect6(() => {
6172
- if (!open) return;
6173
- if (sectionPickerActive) {
6174
- postToParent({ type: "ui:unlock" });
6175
- postToParent({ type: "ow:section-picker", active: true });
6176
- document.documentElement.style.overflow = "";
6177
- document.body.style.overflow = "";
6178
- return () => {
6179
- postToParent({ type: "ow:section-picker", active: false });
6180
- };
6181
- }
6182
- postToParent({ type: "ow:section-picker", active: false });
6183
- postToParent({ type: "ui:lock" });
6184
- const html = document.documentElement;
6185
- const body = document.body;
6186
- const prevHtmlOverflow = html.style.overflow;
6187
- const prevBodyOverflow = body.style.overflow;
6188
- const prevHtmlOverscroll = html.style.overscrollBehavior;
6189
- const prevBodyOverscroll = body.style.overscrollBehavior;
6190
- html.style.overflow = "hidden";
6191
- body.style.overflow = "hidden";
6192
- html.style.overscrollBehavior = "none";
6193
- body.style.overscrollBehavior = "none";
6194
- const canScrollElement = (el, deltaY) => {
6195
- const { overflowY } = getComputedStyle(el);
6196
- if (overflowY !== "auto" && overflowY !== "scroll") return false;
6197
- if (el.scrollHeight <= el.clientHeight + 1) return false;
6198
- if (deltaY < 0) return el.scrollTop > 0;
6199
- if (deltaY > 0)
6200
- return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6201
- return true;
6202
- };
6203
- const allowsInternalScroll = (e) => {
6204
- const target = e.target;
6205
- if (!(target instanceof Element)) return false;
6206
- const scrollRoot = target.closest(
6207
- "[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
6208
- );
6209
- if (!scrollRoot) return false;
6210
- const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
6211
- return canScrollElement(scrollRoot, deltaY);
6212
- };
6213
- const preventBackgroundScroll = (e) => {
6214
- if (allowsInternalScroll(e)) return;
6215
- e.preventDefault();
6216
- };
6217
- const scrollOpts = { passive: false, capture: true };
6218
- document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6219
- document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6220
- return () => {
6221
- postToParent({ type: "ui:unlock" });
6222
- html.style.overflow = prevHtmlOverflow;
6223
- body.style.overflow = prevBodyOverflow;
6224
- html.style.overscrollBehavior = prevHtmlOverscroll;
6225
- body.style.overscrollBehavior = prevBodyOverscroll;
6226
- document.removeEventListener(
6227
- "wheel",
6228
- preventBackgroundScroll,
6229
- scrollOpts
6230
- );
6231
- document.removeEventListener(
6232
- "touchmove",
6233
- preventBackgroundScroll,
6234
- scrollOpts
6235
- );
6236
- };
6237
- }, [open, sectionPickerActive]);
6238
- return /* @__PURE__ */ jsxs13(Fragment4, { children: [
6239
- /* @__PURE__ */ jsx21(
6240
- Dialog2,
6241
- {
6242
- open: open && !sectionPickerActive,
6243
- onOpenChange: (next) => {
6244
- if (!next) onClose?.();
6245
- },
6246
- children: /* @__PURE__ */ jsx21(
6247
- DialogContent,
6248
- {
6249
- ref: panelRef,
6250
- container: portalContainer,
6251
- "data-ohw-link-popover-root": "",
6252
- "data-ohw-link-modal-root": "",
6253
- "data-ohw-bridge": "",
6254
- showCloseButton: false,
6255
- className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6256
- children: /* @__PURE__ */ jsx21(LinkEditorPanel, { state, onClose })
6257
- }
6258
- )
6259
- }
6260
- ),
6261
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ jsx21(
6262
- SectionPickerOverlay,
5557
+ const isCancel = state.secondaryLabel === "Cancel";
5558
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
5559
+ /* @__PURE__ */ jsx19(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx19(
5560
+ "button",
6263
5561
  {
6264
- pagePath: state.selectedPage.path,
6265
- sections: state.activeSections,
6266
- onBack: state.handleSectionPickerBack,
6267
- onSelect: state.handleSectionSelect
5562
+ type: "button",
5563
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5564
+ "aria-label": "Close",
5565
+ children: /* @__PURE__ */ jsx19(IconX, { "aria-hidden": true })
6268
5566
  }
6269
- ) : null
5567
+ ) }),
5568
+ /* @__PURE__ */ jsx19(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx19(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5569
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5570
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx19(
5571
+ DestinationBreadcrumb,
5572
+ {
5573
+ pageTitle: state.selectedPage.title,
5574
+ sectionLabel: state.selectedSection.label
5575
+ }
5576
+ ) : /* @__PURE__ */ jsx19(
5577
+ UrlOrPageInput,
5578
+ {
5579
+ value: state.searchValue,
5580
+ selectedPage: state.selectedPage,
5581
+ dropdownOpen: state.dropdownOpen,
5582
+ onDropdownOpenChange: state.setDropdownOpen,
5583
+ filteredPages: state.filteredPages,
5584
+ onInputChange: state.handleInputChange,
5585
+ onPageSelect: state.handlePageSelect,
5586
+ urlError: state.urlError
5587
+ }
5588
+ ),
5589
+ state.showChooseSection ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col justify-center gap-2", children: [
5590
+ /* @__PURE__ */ jsx19(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5591
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5592
+ /* @__PURE__ */ jsx19(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5593
+ /* @__PURE__ */ jsx19("span", { children: "Pick a section this link should scroll to." })
5594
+ ] })
5595
+ ] }) : null,
5596
+ state.showSectionPicker ? /* @__PURE__ */ jsx19(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5597
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx19(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5598
+ ] }),
5599
+ /* @__PURE__ */ jsxs11(DialogFooter, { className: "w-full px-6 pb-6", children: [
5600
+ /* @__PURE__ */ jsx19(
5601
+ Button,
5602
+ {
5603
+ type: "button",
5604
+ variant: isCancel ? "outline" : "ghost",
5605
+ className: "leading-6 shadow-none cursor-pointer",
5606
+ style: isCancel ? {
5607
+ backgroundColor: "var(--ohw-background)",
5608
+ borderColor: "var(--ohw-border)",
5609
+ color: "var(--ohw-foreground)"
5610
+ } : void 0,
5611
+ onClick: state.handleSecondary,
5612
+ children: state.secondaryLabel
5613
+ }
5614
+ ),
5615
+ /* @__PURE__ */ jsx19(
5616
+ Button,
5617
+ {
5618
+ type: "button",
5619
+ className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5620
+ style: {
5621
+ backgroundColor: "var(--ohw-primary)",
5622
+ color: "var(--ohw-primary-foreground)"
5623
+ },
5624
+ disabled: !state.isValid,
5625
+ onClick: state.handleSubmit,
5626
+ children: state.submitLabel
5627
+ }
5628
+ )
5629
+ ] })
6270
5630
  ] });
6271
5631
  }
6272
5632
 
5633
+ // src/ui/link-modal/LinkPopover.tsx
5634
+ import { jsx as jsx20 } from "react/jsx-runtime";
5635
+ function LinkPopover({
5636
+ open = true,
5637
+ panelRef,
5638
+ portalContainer,
5639
+ onClose,
5640
+ ...editorProps
5641
+ }) {
5642
+ return /* @__PURE__ */ jsx20(
5643
+ Dialog2,
5644
+ {
5645
+ open,
5646
+ onOpenChange: (next) => {
5647
+ if (!next) onClose?.();
5648
+ },
5649
+ children: /* @__PURE__ */ jsx20(
5650
+ DialogContent,
5651
+ {
5652
+ ref: panelRef,
5653
+ container: portalContainer,
5654
+ "data-ohw-link-popover-root": "",
5655
+ "data-ohw-link-modal-root": "",
5656
+ "data-ohw-bridge": "",
5657
+ showCloseButton: false,
5658
+ className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5659
+ children: /* @__PURE__ */ jsx20(LinkEditorPanel, { ...editorProps, open, onClose })
5660
+ }
5661
+ )
5662
+ }
5663
+ );
5664
+ }
5665
+
6273
5666
  // src/ui/link-modal/devFixtures.ts
6274
5667
  var DEV_SITE_PAGES = [
6275
5668
  { path: "/", title: "Home" },
@@ -6289,766 +5682,47 @@ var DEV_SECTIONS_BY_PATH = {
6289
5682
  { id: "founder-teaser", label: "Founder" },
6290
5683
  { id: "plan-form", label: "Plan Form" },
6291
5684
  { id: "testimonials", label: "Testimonials" },
6292
- { id: "wordmark", label: "Wordmark" },
6293
- { id: "footer", label: "Footer" }
5685
+ { id: "wordmark", label: "Wordmark" }
6294
5686
  ],
6295
5687
  "/about": [
6296
5688
  { id: "manifesto", label: "Manifesto" },
6297
5689
  { id: "story-letter", label: "Our Story" },
6298
5690
  { id: "lagree-explainer", label: "Lagree Explainer" },
6299
5691
  { id: "waitlist-cta", label: "Waitlist" },
6300
- { id: "personal-training", label: "Personal training" },
6301
- { id: "footer", label: "Footer" }
5692
+ { id: "personal-training", label: "Personal training" }
6302
5693
  ],
6303
- "/classes": [
6304
- { id: "class-library", label: "Class Library" },
6305
- { id: "footer", label: "Footer" }
6306
- ],
6307
- "/pricing": [
6308
- { id: "page-header", label: "Page Header" },
6309
- { id: "free-class-cta", label: "Free Class" },
6310
- { id: "benefits-marquee", label: "Benefits" },
6311
- { id: "monthly-memberships", label: "Memberships" },
6312
- { id: "specialty-programs", label: "Specialty Programs" },
6313
- { id: "package-matcher", label: "Packages" },
6314
- { id: "wordmark", label: "Wordmark" },
6315
- { id: "footer", label: "Footer" }
6316
- ],
6317
- "/studio": [
6318
- { id: "studio-intro", label: "Studio Intro" },
6319
- { id: "studio-features", label: "Studio Features" },
6320
- { id: "studio-gallery", label: "Studio Gallery" },
6321
- { id: "studio-visit", label: "Visit Studio" },
6322
- { id: "footer", label: "Footer" }
6323
- ],
6324
- "/instructors": [
6325
- { id: "instructor-grid", label: "Instructors" },
6326
- { id: "footer", label: "Footer" }
6327
- ],
6328
- "/contact": [
6329
- { id: "hello-hero", label: "Hello" },
6330
- { id: "contact-form", label: "Contact Form" },
6331
- { id: "faq", label: "FAQ" },
6332
- { id: "footer", label: "Footer" }
6333
- ]
6334
- };
6335
- function shouldUseDevFixtures() {
6336
- if (typeof window === "undefined") return false;
6337
- const raw = readPreservedSearch();
6338
- const q = raw.startsWith("?") ? raw.slice(1) : raw;
6339
- return new URLSearchParams(q).get("ohw-fixtures") === "1";
6340
- }
6341
-
6342
- // src/lib/nav-items.ts
6343
- var NAV_COUNT_KEY = "__ohw_nav_count";
6344
- var NAV_ORDER_KEY = "__ohw_nav_order";
6345
- function getLinkHref(el) {
6346
- const key = el.getAttribute("data-ohw-href-key");
6347
- if (key) {
6348
- const stored = getStoredLinkHref(key);
6349
- if (stored !== void 0) return stored;
6350
- }
6351
- return el.getAttribute("href") ?? "";
6352
- }
6353
- function isNavbarButton(el) {
6354
- return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
6355
- }
6356
- function isNavbarLinkItem(el) {
6357
- if (!el.matches("[data-ohw-href-key]")) return false;
6358
- if (isNavbarButton(el)) return false;
6359
- if (!el.querySelector('[data-ohw-editable="text"]')) return false;
6360
- return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
6361
- }
6362
- function getNavbarDesktopContainer() {
6363
- return document.querySelector("[data-ohw-nav-container]");
6364
- }
6365
- function getNavbarDrawerContainer() {
6366
- return document.querySelector("[data-ohw-nav-drawer]");
6367
- }
6368
- function listNavbarItems() {
6369
- const desktop = getNavbarDesktopContainer();
6370
- if (desktop) {
6371
- return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6372
- }
6373
- const drawer = getNavbarDrawerContainer();
6374
- if (drawer) {
6375
- return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6376
- }
6377
- return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
6378
- }
6379
- function parseNavIndexFromKey(key) {
6380
- if (!key) return null;
6381
- const match = key.match(/^nav-(\d+)-href$/);
6382
- if (!match) return null;
6383
- return parseInt(match[1], 10);
6384
- }
6385
- function getNavbarIndicesInDom() {
6386
- const indices = /* @__PURE__ */ new Set();
6387
- for (const item of listNavbarItems()) {
6388
- const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
6389
- if (idx !== null) indices.add(idx);
6390
- }
6391
- return [...indices].sort((a, b) => a - b);
6392
- }
6393
- function getNextNavbarIndex() {
6394
- const indices = getNavbarIndicesInDom();
6395
- if (indices.length === 0) return 0;
6396
- return Math.max(...indices) + 1;
6397
- }
6398
- function getNavbarExistingTargets() {
6399
- return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
6400
- }
6401
- function getNavOrderFromDom() {
6402
- return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6403
- }
6404
- function parseNavOrder(content) {
6405
- const raw = content[NAV_ORDER_KEY];
6406
- if (!raw) return null;
6407
- try {
6408
- const parsed = JSON.parse(raw);
6409
- if (!Array.isArray(parsed)) return null;
6410
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6411
- } catch {
6412
- return null;
6413
- }
6414
- }
6415
- function findCounterpartByHrefKey(hrefKey, root) {
6416
- return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6417
- }
6418
- function cloneNavLinkShell(template) {
6419
- const anchor = document.createElement("a");
6420
- if (template) {
6421
- anchor.style.cssText = template.style.cssText;
6422
- if (template.className) anchor.className = template.className;
6423
- }
6424
- anchor.style.cursor = "pointer";
6425
- anchor.style.textDecoration = "none";
6426
- return anchor;
6427
- }
6428
- function buildNavLink(index, href, label, template) {
6429
- const anchor = cloneNavLinkShell(template);
6430
- anchor.href = href;
6431
- anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
6432
- const span = document.createElement("span");
6433
- span.setAttribute("data-ohw-editable", "text");
6434
- span.setAttribute("data-ohw-key", `nav-${index}-label`);
6435
- span.textContent = label;
6436
- anchor.appendChild(span);
6437
- return anchor;
6438
- }
6439
- function insertNavLinkInContainer(container, anchor, afterHrefKey) {
6440
- if (!afterHrefKey) {
6441
- container.appendChild(anchor);
6442
- return;
6443
- }
6444
- const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
6445
- if (afterEl?.parentElement === container) {
6446
- afterEl.insertAdjacentElement("afterend", anchor);
6447
- return;
6448
- }
6449
- container.appendChild(anchor);
6450
- }
6451
- function insertNavbarItemDom(index, href, label, afterAnchor) {
6452
- const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
6453
- const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6454
- const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6455
- const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
6456
- const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
6457
- let primary = null;
6458
- const desktopContainer = getNavbarDesktopContainer();
6459
- if (desktopContainer) {
6460
- const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
6461
- insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
6462
- primary = desktopAnchor;
6463
- }
6464
- const drawerContainer = getNavbarDrawerContainer();
6465
- if (drawerContainer) {
6466
- const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
6467
- insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
6468
- if (!primary) primary = drawerAnchor;
6469
- }
6470
- if (!primary) {
6471
- const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
6472
- const nav = document.querySelector("nav");
6473
- nav?.appendChild(fallback);
6474
- primary = fallback;
6475
- }
6476
- return primary;
6477
- }
6478
- function applyNavOrderToContainer(container, order) {
6479
- const seen = /* @__PURE__ */ new Set();
6480
- for (const hrefKey of order) {
6481
- if (seen.has(hrefKey)) continue;
6482
- seen.add(hrefKey);
6483
- const el = findCounterpartByHrefKey(hrefKey, container);
6484
- if (el) container.appendChild(el);
6485
- }
6486
- for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6487
- if (!isNavbarLinkItem(el)) continue;
6488
- const key = el.getAttribute("data-ohw-href-key");
6489
- if (!key || seen.has(key)) continue;
6490
- container.appendChild(el);
6491
- }
6492
- }
6493
- function applyNavOrder(order) {
6494
- const desktop = getNavbarDesktopContainer();
6495
- if (desktop) applyNavOrderToContainer(desktop, order);
6496
- const drawer = getNavbarDrawerContainer();
6497
- if (drawer) applyNavOrderToContainer(drawer, order);
6498
- }
6499
- function collectNavbarIndicesFromContent(content) {
6500
- const indices = /* @__PURE__ */ new Set();
6501
- for (const key of Object.keys(content)) {
6502
- const idx = parseNavIndexFromKey(key);
6503
- if (idx !== null) indices.add(idx);
6504
- }
6505
- const countRaw = content[NAV_COUNT_KEY];
6506
- if (countRaw) {
6507
- const count = parseInt(countRaw, 10);
6508
- if (Number.isFinite(count) && count > 0) {
6509
- for (let i = 0; i < count; i++) indices.add(i);
6510
- }
6511
- }
6512
- return [...indices].sort((a, b) => a - b);
6513
- }
6514
- function navbarIndexExistsInDom(index) {
6515
- return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
6516
- }
6517
- function reconcileNavbarItemsFromContent(content) {
6518
- const indices = collectNavbarIndicesFromContent(content);
6519
- for (const index of indices) {
6520
- if (navbarIndexExistsInDom(index)) continue;
6521
- const href = content[`nav-${index}-href`];
6522
- const label = content[`nav-${index}-label`];
6523
- if (!href && !label) continue;
6524
- insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6525
- }
6526
- const order = parseNavOrder(content);
6527
- if (order?.length) applyNavOrder(order);
6528
- }
6529
- function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6530
- const order = getNavOrderFromDom();
6531
- if (!afterAnchor) {
6532
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6533
- return order;
6534
- }
6535
- const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
6536
- if (!afterKey) {
6537
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6538
- return order;
6539
- }
6540
- const withoutNew = order.filter((key) => key !== newHrefKey);
6541
- const pos = withoutNew.indexOf(afterKey);
6542
- if (pos >= 0) {
6543
- withoutNew.splice(pos + 1, 0, newHrefKey);
6544
- return withoutNew;
6545
- }
6546
- withoutNew.push(newHrefKey);
6547
- return withoutNew;
6548
- }
6549
- function insertNavbarItem(href, label, afterAnchor = null) {
6550
- const index = getNextNavbarIndex();
6551
- const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
6552
- if (!anchor) throw new Error("Failed to insert navbar item");
6553
- const hrefKey = `nav-${index}-href`;
6554
- const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
6555
- applyNavOrder(order);
6556
- return {
6557
- anchor,
6558
- index,
6559
- hrefKey,
6560
- labelKey: `nav-${index}-label`,
6561
- href,
6562
- label,
6563
- order
6564
- };
6565
- }
6566
-
6567
- // src/lib/footer-items.ts
6568
- var FOOTER_ORDER_KEY = "__ohw_footer_order";
6569
- var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6570
- function parseFooterHrefKey(key) {
6571
- if (!key) return null;
6572
- const match = key.match(FOOTER_HREF_RE);
6573
- if (!match) return null;
6574
- return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6575
- }
6576
- function isFooterHrefKey(key) {
6577
- return parseFooterHrefKey(key) !== null;
6578
- }
6579
- function getFooterRoot() {
6580
- return document.querySelector('footer[data-ohw-section="footer"], footer');
6581
- }
6582
- function getFooterLinksContainer() {
6583
- return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6584
- }
6585
- function isFooterLinkAnchor(el) {
6586
- if (!el.matches("[data-ohw-href-key]")) return false;
6587
- if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6588
- return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6589
- }
6590
- function listFooterColumns() {
6591
- const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6592
- if (!root) return [];
6593
- const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6594
- if (explicit.length > 0) return explicit;
6595
- return Array.from(root.children).filter((el) => {
6596
- if (!(el instanceof HTMLElement)) return false;
6597
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6598
- isFooterLinkAnchor
6599
- );
6600
- });
6601
- }
6602
- function listFooterLinksInColumn(column) {
6603
- return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6604
- isFooterLinkAnchor
6605
- );
6606
- }
6607
- function findFooterColumnForLink(anchor) {
6608
- const columns = listFooterColumns();
6609
- return columns.find((col) => col.contains(anchor)) ?? null;
6610
- }
6611
- function getFooterOrderFromDom() {
6612
- return listFooterColumns().map(
6613
- (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6614
- );
6615
- }
6616
- function findFooterLinkByKey(hrefKey) {
6617
- return document.querySelector(
6618
- `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6619
- );
6620
- }
6621
- function syncFooterColumnIndices(columns) {
6622
- columns.forEach((col, index) => {
6623
- const next = String(index);
6624
- if (col.getAttribute("data-ohw-footer-col") !== next) {
6625
- col.setAttribute("data-ohw-footer-col", next);
6626
- }
6627
- });
6628
- }
6629
- function appendChildIfNeeded(parent, child) {
6630
- if (parent.lastElementChild !== child) {
6631
- parent.appendChild(child);
6632
- }
6633
- }
6634
- function insertLinkBeforeNext(col, el, nextEl) {
6635
- if (nextEl) {
6636
- if (el.nextElementSibling !== nextEl) {
6637
- col.insertBefore(el, nextEl);
6638
- }
6639
- return;
6640
- }
6641
- if (col.lastElementChild !== el) {
6642
- col.appendChild(el);
6643
- }
6644
- }
6645
- function applyFooterOrder(order) {
6646
- const container = getFooterLinksContainer();
6647
- if (!container) return;
6648
- const columns = listFooterColumns();
6649
- if (columns.length === 0) return;
6650
- const currentOrder = getFooterOrderFromDom();
6651
- if (ordersEqual(order, currentOrder)) {
6652
- syncFooterColumnIndices(columns);
6653
- return;
6654
- }
6655
- const colCount = Math.max(columns.length, order.length);
6656
- const working = columns.slice(0, colCount);
6657
- for (let i = 0; i < order.length && i < working.length; i++) {
6658
- appendChildIfNeeded(container, working[i]);
6659
- }
6660
- for (let i = order.length; i < working.length; i++) {
6661
- appendChildIfNeeded(container, working[i]);
6662
- }
6663
- const freshColumns = listFooterColumns();
6664
- syncFooterColumnIndices(freshColumns);
6665
- for (let c = 0; c < order.length; c++) {
6666
- const col = freshColumns[c];
6667
- if (!col) continue;
6668
- const colOrder = order[c];
6669
- for (let j = 0; j < colOrder.length; j++) {
6670
- const hrefKey = colOrder[j];
6671
- const el = findFooterLinkByKey(hrefKey);
6672
- if (!el) continue;
6673
- if (el.parentElement !== col) {
6674
- col.appendChild(el);
6675
- }
6676
- const nextKey = colOrder[j + 1];
6677
- const nextEl = nextKey ? findFooterLinkByKey(nextKey) : null;
6678
- insertLinkBeforeNext(col, el, nextEl?.parentElement === col ? nextEl : null);
6679
- }
6680
- }
6681
- }
6682
- function ordersEqual(a, b) {
6683
- if (a.length !== b.length) return false;
6684
- for (let i = 0; i < a.length; i++) {
6685
- const left = a[i];
6686
- const right = b[i];
6687
- if (left.length !== right.length) return false;
6688
- for (let j = 0; j < left.length; j++) {
6689
- if (left[j] !== right[j]) return false;
6690
- }
6691
- }
6692
- return true;
6693
- }
6694
- function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6695
- const order = getFooterOrderFromDom();
6696
- if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6697
- let fromCol = -1;
6698
- let fromIdx = -1;
6699
- for (let c = 0; c < order.length; c++) {
6700
- const idx = order[c].indexOf(hrefKey);
6701
- if (idx >= 0) {
6702
- fromCol = c;
6703
- fromIdx = idx;
6704
- break;
6705
- }
6706
- }
6707
- if (fromCol < 0) return null;
6708
- const next = order.map((col) => [...col]);
6709
- next[fromCol].splice(fromIdx, 1);
6710
- let adjusted = insertIndex;
6711
- if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6712
- adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6713
- next[targetColIndex].splice(adjusted, 0, hrefKey);
6714
- if (ordersEqual(next, order)) return null;
6715
- return next;
6716
- }
6717
- function planFooterColumnMove(fromIndex, toIndex) {
6718
- const order = getFooterOrderFromDom();
6719
- const columns = listFooterColumns();
6720
- if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6721
- return null;
6722
- }
6723
- if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6724
- const nextOrder = order.map((col) => [...col]);
6725
- const [movedOrder] = nextOrder.splice(fromIndex, 1);
6726
- if (!movedOrder) return null;
6727
- let adjusted = toIndex;
6728
- if (fromIndex < toIndex) adjusted -= 1;
6729
- adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6730
- nextOrder.splice(adjusted, 0, movedOrder);
6731
- if (ordersEqual(nextOrder, order)) return null;
6732
- return nextOrder;
6733
- }
6734
- function parseFooterOrder(content) {
6735
- const raw = content[FOOTER_ORDER_KEY];
6736
- if (!raw) return null;
6737
- try {
6738
- const parsed = JSON.parse(raw);
6739
- if (!Array.isArray(parsed)) return null;
6740
- return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
6741
- } catch {
6742
- return null;
6743
- }
6744
- }
6745
- function reconcileFooterOrderFromContent(content) {
6746
- const order = parseFooterOrder(content);
6747
- if (!order?.length) return;
6748
- const current = getFooterOrderFromDom();
6749
- if (ordersEqual(order, current)) {
6750
- syncFooterColumnIndices(listFooterColumns());
6751
- return;
6752
- }
6753
- applyFooterOrder(order);
6754
- }
6755
- function buildLinkDropSlots(column, columnIndex) {
6756
- const links = listFooterLinksInColumn(column);
6757
- const colRect = column.getBoundingClientRect();
6758
- const slots = [];
6759
- const barThickness = 3;
6760
- if (links.length === 0) {
6761
- slots.push({
6762
- insertIndex: 0,
6763
- columnIndex,
6764
- left: colRect.left,
6765
- top: colRect.top + colRect.height / 2 - barThickness / 2,
6766
- width: colRect.width,
6767
- height: barThickness,
6768
- direction: "horizontal"
6769
- });
6770
- return slots;
6771
- }
6772
- for (let i = 0; i <= links.length; i++) {
6773
- let top;
6774
- if (i === 0) {
6775
- top = links[0].getBoundingClientRect().top - barThickness / 2;
6776
- } else if (i === links.length) {
6777
- const last = links[links.length - 1].getBoundingClientRect();
6778
- top = last.bottom - barThickness / 2;
6779
- } else {
6780
- const prev = links[i - 1].getBoundingClientRect();
6781
- const next = links[i].getBoundingClientRect();
6782
- top = (prev.bottom + next.top) / 2 - barThickness / 2;
6783
- }
6784
- const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
6785
- slots.push({
6786
- insertIndex: i,
6787
- columnIndex,
6788
- left: widthRef.left,
6789
- top,
6790
- width: Math.max(widthRef.width, colRect.width * 0.8),
6791
- height: barThickness,
6792
- direction: "horizontal"
6793
- });
6794
- }
6795
- return slots;
6796
- }
6797
- function buildColumnDropSlots() {
6798
- const columns = listFooterColumns();
6799
- const slots = [];
6800
- const barThickness = 3;
6801
- if (columns.length === 0) return slots;
6802
- for (let i = 0; i <= columns.length; i++) {
6803
- let left;
6804
- let height;
6805
- let top;
6806
- if (i === 0) {
6807
- const first = columns[0].getBoundingClientRect();
6808
- left = first.left - barThickness / 2;
6809
- top = first.top;
6810
- height = first.height;
6811
- } else if (i === columns.length) {
6812
- const last = columns[columns.length - 1].getBoundingClientRect();
6813
- left = last.right - barThickness / 2;
6814
- top = last.top;
6815
- height = last.height;
6816
- } else {
6817
- const prev = columns[i - 1].getBoundingClientRect();
6818
- const next = columns[i].getBoundingClientRect();
6819
- left = (prev.right + next.left) / 2 - barThickness / 2;
6820
- top = Math.min(prev.top, next.top);
6821
- height = Math.max(prev.bottom, next.bottom) - top;
6822
- }
6823
- slots.push({
6824
- insertIndex: i,
6825
- columnIndex: i,
6826
- left,
6827
- top,
6828
- width: barThickness,
6829
- height: Math.max(height, 24),
6830
- direction: "vertical"
6831
- });
6832
- }
6833
- return slots;
6834
- }
6835
- function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
6836
- const columns = listFooterColumns();
6837
- let best = null;
6838
- for (let c = 0; c < columns.length; c++) {
6839
- const col = columns[c];
6840
- const colRect = col.getBoundingClientRect();
6841
- const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
6842
- if (!inColX) continue;
6843
- const slots = buildLinkDropSlots(col, c).filter((slot) => {
6844
- const links = listFooterLinksInColumn(col);
6845
- const fromIdx = links.findIndex(
6846
- (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
6847
- );
6848
- if (fromIdx < 0) return true;
6849
- if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
6850
- return true;
6851
- }
6852
- return true;
6853
- });
6854
- for (const slot of slots) {
6855
- const cy = slot.top + slot.height / 2;
6856
- const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
6857
- if (!best || dist < best.dist) best = { slot, dist };
6858
- }
6859
- }
6860
- return best?.slot ?? null;
6861
- }
6862
- function findColumnIndexForKey(hrefKey) {
6863
- const order = getFooterOrderFromDom();
6864
- for (let c = 0; c < order.length; c++) {
6865
- if (order[c].includes(hrefKey)) return c;
6866
- }
6867
- return -1;
6868
- }
6869
- function hitTestColumnDropSlot(clientX, _clientY) {
6870
- const slots = buildColumnDropSlots();
6871
- let best = null;
6872
- for (const slot of slots) {
6873
- const cx2 = slot.left + slot.width / 2;
6874
- const dist = Math.abs(clientX - cx2);
6875
- if (!best || dist < best.dist) best = { slot, dist };
6876
- }
6877
- return best?.slot ?? null;
6878
- }
6879
-
6880
- // src/lib/carousel.ts
6881
- import { useEffect as useEffect7, useState as useState6 } from "react";
6882
- var CAROUSEL_ATTR = "data-ohw-carousel";
6883
- var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
6884
- var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
6885
- var CAROUSEL_EVENT = "ohw:carousel-change";
6886
- function listCarouselKeys() {
6887
- const keys = /* @__PURE__ */ new Set();
6888
- document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
6889
- const key = el.getAttribute("data-ohw-key");
6890
- if (key) keys.add(key);
6891
- });
6892
- return [...keys];
6893
- }
6894
- function containersForKey(key) {
6895
- return Array.from(
6896
- document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
6897
- );
6898
- }
6899
- function isCarouselKey(key) {
6900
- return containersForKey(key).length > 0;
6901
- }
6902
- function parseSlides(raw) {
6903
- if (!raw) return [];
6904
- try {
6905
- const parsed = JSON.parse(raw);
6906
- if (!Array.isArray(parsed)) return [];
6907
- return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
6908
- } catch {
6909
- return [];
6910
- }
6911
- }
6912
- function readCarouselValue(key) {
6913
- const container = containersForKey(key)[0];
6914
- if (!container) return [];
6915
- const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
6916
- if (fromAttr.length > 0) return fromAttr;
6917
- return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
6918
- const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
6919
- return { src: img?.src ?? "", alt: img?.alt ?? "" };
6920
- });
6921
- }
6922
- function slideIndex(el) {
6923
- const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
6924
- const n = raw ? parseInt(raw, 10) : NaN;
6925
- return Number.isFinite(n) ? n : 0;
6926
- }
6927
- function applyCarouselValue(key, slides) {
6928
- const value = JSON.stringify(slides);
6929
- for (const container of containersForKey(key)) {
6930
- if (container.getAttribute(CAROUSEL_VALUE_ATTR) === value) continue;
6931
- container.setAttribute(CAROUSEL_VALUE_ATTR, value);
6932
- container.dispatchEvent(
6933
- new CustomEvent(CAROUSEL_EVENT, { detail: { images: slides } })
6934
- );
6935
- }
6936
- }
6937
- function applyCarouselNode(key, val) {
6938
- if (!isCarouselKey(key)) return false;
6939
- applyCarouselValue(key, parseSlides(val));
6940
- return true;
6941
- }
6942
- function useOhwCarousel(key, initial) {
6943
- const [images, setImages] = useState6(initial);
6944
- useEffect7(() => {
6945
- const el = document.querySelector(
6946
- `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
6947
- );
6948
- if (!el) return;
6949
- const onChange = (e) => {
6950
- const detail = e.detail;
6951
- if (Array.isArray(detail?.images)) setImages(detail.images);
6952
- };
6953
- el.addEventListener(CAROUSEL_EVENT, onChange);
6954
- return () => el.removeEventListener(CAROUSEL_EVENT, onChange);
6955
- }, [key]);
6956
- const bind = {
6957
- [CAROUSEL_ATTR]: "",
6958
- "data-ohw-key": key,
6959
- [CAROUSEL_VALUE_ATTR]: JSON.stringify(images)
6960
- };
6961
- return { images, setImages, bind };
6962
- }
6963
-
6964
- // src/ui/navbar-container-chrome.tsx
6965
- import { Plus as Plus2 } from "lucide-react";
6966
- import { jsx as jsx22 } from "react/jsx-runtime";
6967
- function NavbarContainerChrome({
6968
- rect,
6969
- onAdd
6970
- }) {
6971
- const chromeGap = 6;
6972
- return /* @__PURE__ */ jsx22(
6973
- "div",
6974
- {
6975
- "data-ohw-navbar-container-chrome": "",
6976
- "data-ohw-bridge": "",
6977
- className: "pointer-events-none fixed z-[2147483647]",
6978
- style: {
6979
- top: rect.top - chromeGap,
6980
- left: rect.left - chromeGap,
6981
- width: rect.width + chromeGap * 2,
6982
- height: rect.height + chromeGap * 2
6983
- },
6984
- children: /* @__PURE__ */ jsx22(
6985
- "button",
6986
- {
6987
- type: "button",
6988
- "data-ohw-navbar-add-button": "",
6989
- 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",
6990
- style: { top: "70%" },
6991
- "aria-label": "Add item",
6992
- onMouseDown: (e) => {
6993
- e.preventDefault();
6994
- e.stopPropagation();
6995
- },
6996
- onClick: (e) => {
6997
- e.preventDefault();
6998
- e.stopPropagation();
6999
- onAdd();
7000
- },
7001
- children: /* @__PURE__ */ jsx22(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7002
- }
7003
- )
7004
- }
7005
- );
5694
+ "/classes": [{ id: "class-library", label: "Class Library" }],
5695
+ "/pricing": [
5696
+ { id: "page-header", label: "Page Header" },
5697
+ { id: "free-class-cta", label: "Free Class" },
5698
+ { id: "benefits-marquee", label: "Benefits" },
5699
+ { id: "monthly-memberships", label: "Memberships" },
5700
+ { id: "specialty-programs", label: "Specialty Programs" },
5701
+ { id: "package-matcher", label: "Packages" },
5702
+ { id: "wordmark", label: "Wordmark" }
5703
+ ],
5704
+ "/studio": [
5705
+ { id: "studio-intro", label: "Studio Intro" },
5706
+ { id: "studio-features", label: "Studio Features" },
5707
+ { id: "studio-gallery", label: "Studio Gallery" },
5708
+ { id: "studio-visit", label: "Visit Studio" }
5709
+ ],
5710
+ "/instructors": [{ id: "instructor-grid", label: "Instructors" }],
5711
+ "/contact": [
5712
+ { id: "hello-hero", label: "Hello" },
5713
+ { id: "contact-form", label: "Contact Form" },
5714
+ { id: "faq", label: "FAQ" }
5715
+ ]
5716
+ };
5717
+ function shouldUseDevFixtures() {
5718
+ if (typeof window === "undefined") return false;
5719
+ const raw = readPreservedSearch();
5720
+ const q = raw.startsWith("?") ? raw.slice(1) : raw;
5721
+ return new URLSearchParams(q).get("ohw-fixtures") === "1";
7006
5722
  }
7007
5723
 
7008
- // src/ui/drop-indicator.tsx
7009
- import * as React9 from "react";
7010
- import { jsx as jsx23 } from "react/jsx-runtime";
7011
- var dropIndicatorVariants = cva(
7012
- "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7013
- {
7014
- variants: {
7015
- direction: {
7016
- vertical: "h-6 w-[3px]",
7017
- horizontal: "h-[3px] w-[200px]"
7018
- },
7019
- state: {
7020
- default: "opacity-0",
7021
- hover: "opacity-100",
7022
- dragIdle: "opacity-40",
7023
- dragActive: "opacity-100"
7024
- }
7025
- },
7026
- defaultVariants: {
7027
- direction: "vertical",
7028
- state: "default"
7029
- }
7030
- }
7031
- );
7032
- var DropIndicator = React9.forwardRef(
7033
- ({ className, direction, state, ...props }, ref) => {
7034
- return /* @__PURE__ */ jsx23(
7035
- "div",
7036
- {
7037
- ref,
7038
- "data-slot": "drop-indicator",
7039
- "data-direction": direction ?? "vertical",
7040
- "data-state": state ?? "default",
7041
- "aria-hidden": "true",
7042
- className: cn(dropIndicatorVariants({ direction, state }), className),
7043
- ...props
7044
- }
7045
- );
7046
- }
7047
- );
7048
- DropIndicator.displayName = "DropIndicator";
7049
-
7050
5724
  // src/ui/badge.tsx
7051
- import { jsx as jsx24 } from "react/jsx-runtime";
5725
+ import { jsx as jsx21 } from "react/jsx-runtime";
7052
5726
  var badgeVariants = cva(
7053
5727
  "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",
7054
5728
  {
@@ -7066,12 +5740,12 @@ var badgeVariants = cva(
7066
5740
  }
7067
5741
  );
7068
5742
  function Badge({ className, variant, ...props }) {
7069
- return /* @__PURE__ */ jsx24("div", { className: cn(badgeVariants({ variant }), className), ...props });
5743
+ return /* @__PURE__ */ jsx21("div", { className: cn(badgeVariants({ variant }), className), ...props });
7070
5744
  }
7071
5745
 
7072
5746
  // src/OhhwellsBridge.tsx
7073
5747
  import { Link as Link2 } from "lucide-react";
7074
- import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs14 } from "react/jsx-runtime";
5748
+ import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
7075
5749
  var PRIMARY2 = "#0885FE";
7076
5750
  var IMAGE_FADE_MS = 300;
7077
5751
  function runOpacityFade(el, onDone) {
@@ -7250,7 +5924,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
7250
5924
  const root = createRoot(container);
7251
5925
  flushSync(() => {
7252
5926
  root.render(
7253
- /* @__PURE__ */ jsx25(
5927
+ /* @__PURE__ */ jsx22(
7254
5928
  SchedulingWidget,
7255
5929
  {
7256
5930
  notifyOnConnect,
@@ -7290,7 +5964,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
7290
5964
  }
7291
5965
  }
7292
5966
  }
7293
- function getLinkHref2(el) {
5967
+ function getLinkHref(el) {
7294
5968
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
7295
5969
  return anchor?.getAttribute("href") ?? "";
7296
5970
  }
@@ -7312,7 +5986,7 @@ function isDragHandleDisabled(el) {
7312
5986
  return raw === "true" || raw === "";
7313
5987
  });
7314
5988
  }
7315
- function collectEditableNodes(extraContent) {
5989
+ function collectEditableNodes() {
7316
5990
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
7317
5991
  if (el.dataset.ohwEditable === "image") {
7318
5992
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7327,7 +6001,7 @@ function collectEditableNodes(extraContent) {
7327
6001
  return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
7328
6002
  }
7329
6003
  if (el.dataset.ohwEditable === "link") {
7330
- return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
6004
+ return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
7331
6005
  }
7332
6006
  return {
7333
6007
  key: el.dataset.ohwKey ?? "",
@@ -7338,16 +6012,8 @@ function collectEditableNodes(extraContent) {
7338
6012
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
7339
6013
  const key = el.getAttribute("data-ohw-href-key") ?? "";
7340
6014
  if (!key) return;
7341
- nodes.push({ key, type: "link", text: getLinkHref2(el) });
6015
+ nodes.push({ key, type: "link", text: getLinkHref(el) });
7342
6016
  });
7343
- if (extraContent) {
7344
- for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
7345
- const text = extraContent[key];
7346
- if (typeof text === "string" && text.length > 0) {
7347
- nodes.push({ key, type: "meta", text });
7348
- }
7349
- }
7350
- }
7351
6017
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
7352
6018
  const key = el.dataset.ohwKey ?? "";
7353
6019
  const video = getVideoEl(el);
@@ -7355,10 +6021,13 @@ function collectEditableNodes(extraContent) {
7355
6021
  nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
7356
6022
  nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
7357
6023
  });
7358
- for (const key of listCarouselKeys()) {
7359
- nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
7360
- }
7361
- return nodes;
6024
+ const seen = /* @__PURE__ */ new Set();
6025
+ return nodes.filter((node) => {
6026
+ if (!node.key) return true;
6027
+ if (seen.has(node.key)) return false;
6028
+ seen.add(node.key);
6029
+ return true;
6030
+ });
7362
6031
  }
7363
6032
  function isMediaEditable(el) {
7364
6033
  const t = el.dataset.ohwEditable;
@@ -7435,7 +6104,7 @@ function applyLinkByKey(key, val) {
7435
6104
  }
7436
6105
  function isInsideLinkEditor(target) {
7437
6106
  return Boolean(
7438
- 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"]')
6107
+ 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"]')
7439
6108
  );
7440
6109
  }
7441
6110
  function getHrefKeyFromElement(el) {
@@ -7446,30 +6115,7 @@ function getHrefKeyFromElement(el) {
7446
6115
  if (!key) return null;
7447
6116
  return { anchor, key };
7448
6117
  }
7449
- function disableNativeHrefDrag(el) {
7450
- if (el.draggable) el.draggable = false;
7451
- if (el.getAttribute("draggable") !== "false") {
7452
- el.setAttribute("draggable", "false");
7453
- }
7454
- }
7455
- function clearTextSelection() {
7456
- const sel = window.getSelection();
7457
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
7458
- }
7459
- function armFooterPressDrag() {
7460
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7461
- }
7462
- function lockFooterDuringDrag() {
7463
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7464
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
7465
- clearTextSelection();
7466
- }
7467
- function unlockFooterDragInteraction() {
7468
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7469
- document.documentElement.removeAttribute("data-ohw-item-dragging");
7470
- clearTextSelection();
7471
- }
7472
- function isNavbarButton2(el) {
6118
+ function isNavbarButton(el) {
7473
6119
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
7474
6120
  }
7475
6121
  function getNavigationItemAnchor(el) {
@@ -7481,8 +6127,19 @@ function getNavigationItemAnchor(el) {
7481
6127
  function isNavigationItem(el) {
7482
6128
  return getNavigationItemAnchor(el) !== null;
7483
6129
  }
6130
+ function getNavigationItemAddHintTarget(anchor) {
6131
+ const items = listNavigationItems();
6132
+ const idx = items.indexOf(anchor);
6133
+ if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
6134
+ return anchor;
6135
+ }
6136
+ function listNavigationItems() {
6137
+ return Array.from(
6138
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
6139
+ ).filter((el) => isNavigationItem(el));
6140
+ }
7484
6141
  function getNavigationItemReorderState(anchor) {
7485
- if (isNavbarButton2(anchor)) return { key: null, disabled: false };
6142
+ if (isNavbarButton(anchor)) return { key: null, disabled: false };
7486
6143
  return getReorderHandleStateFromAnchor(anchor);
7487
6144
  }
7488
6145
  function getReorderHandleState(el) {
@@ -7504,120 +6161,6 @@ function clearHrefKeyHover(anchor) {
7504
6161
  function isInsideNavigationItem(el) {
7505
6162
  return getNavigationItemAnchor(el) !== null;
7506
6163
  }
7507
- function getNavigationRoot(el) {
7508
- const explicit = el.closest("[data-ohw-nav-root]");
7509
- if (explicit) return explicit;
7510
- return el.closest("nav, footer, aside");
7511
- }
7512
- function findFooterItemGroup(item) {
7513
- const footer = item.closest("footer");
7514
- if (!footer) return null;
7515
- let node = item.parentElement;
7516
- while (node && node !== footer) {
7517
- const hasDirectNavItems = Array.from(
7518
- node.querySelectorAll(":scope > [data-ohw-href-key]")
7519
- ).some(isNavigationItem);
7520
- if (hasDirectNavItems) return node;
7521
- node = node.parentElement;
7522
- }
7523
- return null;
7524
- }
7525
- function isNavigationRoot(el) {
7526
- return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
7527
- }
7528
- function isInferredFooterGroup(el) {
7529
- const footer = el.closest("footer");
7530
- if (!footer || el === footer) return false;
7531
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
7532
- }
7533
- function isNavigationContainer(el) {
7534
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
7535
- }
7536
- function isPointOverNavigation(x, y) {
7537
- const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
7538
- if (!navRoot) return false;
7539
- const r2 = navRoot.getBoundingClientRect();
7540
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
7541
- }
7542
- function isPointOverNavItem(container, x, y) {
7543
- return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
7544
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
7545
- const itemRect = el.getBoundingClientRect();
7546
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
7547
- });
7548
- }
7549
- function resolveNavContainerSelectionTarget(target, clientX, clientY) {
7550
- if (getNavigationItemAnchor(target)) return null;
7551
- if (target.closest('[data-ohw-role="navbar-button"]')) return null;
7552
- const plainLink = target.closest("a");
7553
- if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
7554
- return null;
7555
- }
7556
- const fromClosest = target.closest("[data-ohw-nav-container]");
7557
- if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
7558
- return fromClosest;
7559
- }
7560
- const navRoot = target.closest("[data-ohw-nav-root]");
7561
- if (!navRoot) return null;
7562
- const container = navRoot.querySelector("[data-ohw-nav-container]");
7563
- if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
7564
- if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
7565
- return container;
7566
- }
7567
- return null;
7568
- }
7569
- function getNavigationSelectionParent(el) {
7570
- if (isNavigationItem(el)) {
7571
- const explicit = el.closest("[data-ohw-nav-container]");
7572
- if (explicit) return explicit;
7573
- const footerGroup = findFooterItemGroup(el);
7574
- if (footerGroup) return footerGroup;
7575
- return getNavigationRoot(el);
7576
- }
7577
- if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
7578
- return getNavigationRoot(el);
7579
- }
7580
- return null;
7581
- }
7582
- function placeCaretAtPoint(el, x, y) {
7583
- const selection = window.getSelection();
7584
- if (!selection) return;
7585
- if (typeof document.caretRangeFromPoint === "function") {
7586
- const range = document.caretRangeFromPoint(x, y);
7587
- if (range && el.contains(range.startContainer)) {
7588
- selection.removeAllRanges();
7589
- selection.addRange(range);
7590
- return;
7591
- }
7592
- }
7593
- const caretPositionFromPoint = document.caretPositionFromPoint;
7594
- if (typeof caretPositionFromPoint === "function") {
7595
- const position = caretPositionFromPoint(x, y);
7596
- if (position && el.contains(position.offsetNode)) {
7597
- const range = document.createRange();
7598
- range.setStart(position.offsetNode, position.offset);
7599
- range.collapse(true);
7600
- selection.removeAllRanges();
7601
- selection.addRange(range);
7602
- }
7603
- }
7604
- }
7605
- function selectAllTextInEditable(el) {
7606
- const selection = window.getSelection();
7607
- if (!selection) return;
7608
- const range = document.createRange();
7609
- range.selectNodeContents(el);
7610
- selection.removeAllRanges();
7611
- selection.addRange(range);
7612
- }
7613
- function getNavigationLabelEditable(target) {
7614
- const editable = target.closest('[data-ohw-editable="text"]');
7615
- if (!editable) return null;
7616
- const hrefCtx = getHrefKeyFromElement(editable);
7617
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
7618
- if (!navAnchor) return null;
7619
- return { editable, navAnchor };
7620
- }
7621
6164
  function collectSections() {
7622
6165
  return collectSectionsFromDom();
7623
6166
  }
@@ -7741,8 +6284,6 @@ var ICONS = {
7741
6284
  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"/>',
7742
6285
  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"/>'
7743
6286
  };
7744
- var TOOLBAR_PILL_PADDING = 2;
7745
- var TOOLBAR_TOGGLE_GAP = 4;
7746
6287
  var TOOLBAR_GROUPS = [
7747
6288
  [
7748
6289
  { cmd: "bold", title: "Bold" },
@@ -7767,7 +6308,7 @@ function EditGlowChrome({
7767
6308
  dragDisabled = false
7768
6309
  }) {
7769
6310
  const GAP = 6;
7770
- return /* @__PURE__ */ jsxs14(
6311
+ return /* @__PURE__ */ jsxs12(
7771
6312
  "div",
7772
6313
  {
7773
6314
  ref: elRef,
@@ -7782,7 +6323,7 @@ function EditGlowChrome({
7782
6323
  zIndex: 2147483646
7783
6324
  },
7784
6325
  children: [
7785
- /* @__PURE__ */ jsx25(
6326
+ /* @__PURE__ */ jsx22(
7786
6327
  "div",
7787
6328
  {
7788
6329
  style: {
@@ -7795,7 +6336,7 @@ function EditGlowChrome({
7795
6336
  }
7796
6337
  }
7797
6338
  ),
7798
- reorderHrefKey && /* @__PURE__ */ jsx25(
6339
+ reorderHrefKey && /* @__PURE__ */ jsx22(
7799
6340
  "div",
7800
6341
  {
7801
6342
  "data-ohw-drag-handle-container": "",
@@ -7807,7 +6348,7 @@ function EditGlowChrome({
7807
6348
  transform: "translate(calc(-100% - 7px), -50%)",
7808
6349
  pointerEvents: dragDisabled ? "none" : "auto"
7809
6350
  },
7810
- children: /* @__PURE__ */ jsx25(
6351
+ children: /* @__PURE__ */ jsx22(
7811
6352
  DragHandle,
7812
6353
  {
7813
6354
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7911,7 +6452,7 @@ function FloatingToolbar({
7911
6452
  onEditLink
7912
6453
  }) {
7913
6454
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7914
- return /* @__PURE__ */ jsx25(
6455
+ return /* @__PURE__ */ jsx22(
7915
6456
  "div",
7916
6457
  {
7917
6458
  ref: elRef,
@@ -7921,23 +6462,14 @@ function FloatingToolbar({
7921
6462
  left,
7922
6463
  transform,
7923
6464
  zIndex: 2147483647,
7924
- background: "#fff",
7925
- border: "1px solid #E7E5E4",
7926
- borderRadius: 6,
7927
- boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
7928
- display: "flex",
7929
- alignItems: "center",
7930
- padding: TOOLBAR_PILL_PADDING,
7931
- gap: TOOLBAR_TOGGLE_GAP,
7932
- fontFamily: "sans-serif",
7933
6465
  pointerEvents: "auto"
7934
6466
  },
7935
- children: /* @__PURE__ */ jsxs14(CustomToolbar, { children: [
7936
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs14(React10.Fragment, { children: [
7937
- gi > 0 && /* @__PURE__ */ jsx25(CustomToolbarDivider, {}),
6467
+ children: /* @__PURE__ */ jsxs12(CustomToolbar, { children: [
6468
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs12(React9.Fragment, { children: [
6469
+ gi > 0 && /* @__PURE__ */ jsx22(CustomToolbarDivider, {}),
7938
6470
  btns.map((btn) => {
7939
6471
  const isActive = activeCommands.has(btn.cmd);
7940
- return /* @__PURE__ */ jsx25(
6472
+ return /* @__PURE__ */ jsx22(
7941
6473
  CustomToolbarButton,
7942
6474
  {
7943
6475
  title: btn.title,
@@ -7946,7 +6478,7 @@ function FloatingToolbar({
7946
6478
  e.preventDefault();
7947
6479
  onCommand(btn.cmd);
7948
6480
  },
7949
- children: /* @__PURE__ */ jsx25(
6481
+ children: /* @__PURE__ */ jsx22(
7950
6482
  "svg",
7951
6483
  {
7952
6484
  width: "16",
@@ -7967,7 +6499,7 @@ function FloatingToolbar({
7967
6499
  );
7968
6500
  })
7969
6501
  ] }, gi)),
7970
- showEditLink ? /* @__PURE__ */ jsx25(
6502
+ showEditLink ? /* @__PURE__ */ jsx22(
7971
6503
  CustomToolbarButton,
7972
6504
  {
7973
6505
  type: "button",
@@ -7981,7 +6513,7 @@ function FloatingToolbar({
7981
6513
  e.preventDefault();
7982
6514
  e.stopPropagation();
7983
6515
  },
7984
- children: /* @__PURE__ */ jsx25(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
6516
+ children: /* @__PURE__ */ jsx22(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7985
6517
  }
7986
6518
  ) : null
7987
6519
  ] })
@@ -7998,7 +6530,7 @@ function StateToggle({
7998
6530
  states,
7999
6531
  onStateChange
8000
6532
  }) {
8001
- return /* @__PURE__ */ jsx25(
6533
+ return /* @__PURE__ */ jsx22(
8002
6534
  ToggleGroup,
8003
6535
  {
8004
6536
  "data-ohw-state-toggle": "",
@@ -8012,7 +6544,7 @@ function StateToggle({
8012
6544
  left: rect.right - 8,
8013
6545
  transform: "translateX(-100%)"
8014
6546
  },
8015
- children: states.map((state) => /* @__PURE__ */ jsx25(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6547
+ children: states.map((state) => /* @__PURE__ */ jsx22(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8016
6548
  }
8017
6549
  );
8018
6550
  }
@@ -8035,12 +6567,12 @@ function resolveSubdomain(subdomainFromQuery) {
8035
6567
  return "";
8036
6568
  }
8037
6569
  function OhhwellsBridge() {
8038
- const pathname = usePathname2();
8039
- const router = useRouter2();
6570
+ const pathname = usePathname();
6571
+ const router = useRouter();
8040
6572
  const searchParams = useSearchParams();
8041
6573
  const isEditMode = isEditSessionActive();
8042
- const [bridgeRoot, setBridgeRoot] = useState7(null);
8043
- useEffect8(() => {
6574
+ const [bridgeRoot, setBridgeRoot] = useState5(null);
6575
+ useEffect4(() => {
8044
6576
  const figtreeFontId = "ohw-figtree-font";
8045
6577
  if (!document.getElementById(figtreeFontId)) {
8046
6578
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -8068,100 +6600,83 @@ function OhhwellsBridge() {
8068
6600
  const subdomainFromQuery = searchParams.get("subdomain");
8069
6601
  const subdomain = resolveSubdomain(subdomainFromQuery);
8070
6602
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
8071
- const postToParent2 = useCallback4((data) => {
6603
+ const postToParent = useCallback2((data) => {
8072
6604
  if (typeof window !== "undefined" && window.parent !== window) {
8073
6605
  window.parent.postMessage(data, "*");
8074
6606
  }
8075
6607
  }, []);
8076
- const [fetchState, setFetchState] = useState7("idle");
8077
- const autoSaveTimers = useRef5(/* @__PURE__ */ new Map());
8078
- const activeElRef = useRef5(null);
8079
- const selectedElRef = useRef5(null);
8080
- const originalContentRef = useRef5(null);
8081
- const activeStateElRef = useRef5(null);
8082
- const parentScrollRef = useRef5(null);
8083
- const visibleViewportRef = useRef5(null);
8084
- const [dialogPortalContainer, setDialogPortalContainer] = useState7(null);
8085
- const attachVisibleViewport = useCallback4((node) => {
6608
+ const [fetchState, setFetchState] = useState5("idle");
6609
+ const autoSaveTimers = useRef4(/* @__PURE__ */ new Map());
6610
+ const activeElRef = useRef4(null);
6611
+ const selectedElRef = useRef4(null);
6612
+ const originalContentRef = useRef4(null);
6613
+ const activeStateElRef = useRef4(null);
6614
+ const parentScrollRef = useRef4(null);
6615
+ const visibleViewportRef = useRef4(null);
6616
+ const [dialogPortalContainer, setDialogPortalContainer] = useState5(null);
6617
+ const attachVisibleViewport = useCallback2((node) => {
8086
6618
  visibleViewportRef.current = node;
8087
6619
  setDialogPortalContainer(node);
8088
6620
  if (node) applyVisibleViewport(node, parentScrollRef.current);
8089
6621
  }, []);
8090
- const toolbarElRef = useRef5(null);
8091
- const glowElRef = useRef5(null);
8092
- const hoveredImageRef = useRef5(null);
8093
- const hoveredImageHasTextOverlapRef = useRef5(false);
8094
- const dragOverElRef = useRef5(null);
8095
- const [mediaHover, setMediaHover] = useState7(null);
8096
- const [carouselHover, setCarouselHover] = useState7(null);
8097
- const [uploadingRects, setUploadingRects] = useState7({});
8098
- const hoveredGapRef = useRef5(null);
8099
- const imageUnhoverTimerRef = useRef5(null);
8100
- const imageShowTimerRef = useRef5(null);
8101
- const editStylesRef = useRef5(null);
8102
- const activateRef = useRef5(() => {
8103
- });
8104
- const deactivateRef = useRef5(() => {
6622
+ const toolbarElRef = useRef4(null);
6623
+ const glowElRef = useRef4(null);
6624
+ const hoveredImageRef = useRef4(null);
6625
+ const hoveredImageHasTextOverlapRef = useRef4(false);
6626
+ const dragOverElRef = useRef4(null);
6627
+ const [mediaHover, setMediaHover] = useState5(null);
6628
+ const [uploadingRects, setUploadingRects] = useState5({});
6629
+ const hoveredGapRef = useRef4(null);
6630
+ const imageUnhoverTimerRef = useRef4(null);
6631
+ const imageShowTimerRef = useRef4(null);
6632
+ const editStylesRef = useRef4(null);
6633
+ const activateRef = useRef4(() => {
8105
6634
  });
8106
- const selectRef = useRef5(() => {
6635
+ const deactivateRef = useRef4(() => {
8107
6636
  });
8108
- const selectFrameRef = useRef5(() => {
6637
+ const selectRef = useRef4(() => {
8109
6638
  });
8110
- const deselectRef = useRef5(() => {
6639
+ const deselectRef = useRef4(() => {
8111
6640
  });
8112
- const reselectNavigationItemRef = useRef5(() => {
6641
+ const reselectNavigationItemRef = useRef4(() => {
8113
6642
  });
8114
- const commitNavigationTextEditRef = useRef5(() => {
6643
+ const commitNavigationTextEditRef = useRef4(() => {
8115
6644
  });
8116
- const refreshActiveCommandsRef = useRef5(() => {
6645
+ const refreshActiveCommandsRef = useRef4(() => {
8117
6646
  });
8118
- const postToParentRef = useRef5(postToParent2);
8119
- postToParentRef.current = postToParent2;
8120
- const sectionsLoadedRef = useRef5(false);
8121
- const pendingScheduleConfigRequests = useRef5([]);
8122
- const [toolbarRect, setToolbarRect] = useState7(null);
8123
- const [toolbarVariant, setToolbarVariant] = useState7("none");
8124
- const toolbarVariantRef = useRef5("none");
6647
+ const postToParentRef = useRef4(postToParent);
6648
+ postToParentRef.current = postToParent;
6649
+ const sectionsLoadedRef = useRef4(false);
6650
+ const pendingScheduleConfigRequests = useRef4([]);
6651
+ const [toolbarRect, setToolbarRect] = useState5(null);
6652
+ const [toolbarVariant, setToolbarVariant] = useState5("none");
6653
+ const toolbarVariantRef = useRef4("none");
8125
6654
  toolbarVariantRef.current = toolbarVariant;
8126
- const [reorderHrefKey, setReorderHrefKey] = useState7(null);
8127
- const [reorderDragDisabled, setReorderDragDisabled] = useState7(false);
8128
- const [toggleState, setToggleState] = useState7(null);
8129
- const [maxBadge, setMaxBadge] = useState7(null);
8130
- const [activeCommands, setActiveCommands] = useState7(/* @__PURE__ */ new Set());
8131
- const [sectionGap, setSectionGap] = useState7(null);
8132
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState7(false);
8133
- const hoveredNavContainerRef = useRef5(null);
8134
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState7(null);
8135
- const hoveredItemElRef = useRef5(null);
8136
- const [hoveredItemRect, setHoveredItemRect] = useState7(null);
8137
- const siblingHintElRef = useRef5(null);
8138
- const [siblingHintRect, setSiblingHintRect] = useState7(null);
8139
- const [siblingHintRects, setSiblingHintRects] = useState7([]);
8140
- const [isItemDragging, setIsItemDragging] = useState7(false);
8141
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState7(false);
8142
- const footerDragRef = useRef5(null);
8143
- const [footerDropSlots, setFooterDropSlots] = useState7([]);
8144
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState7(null);
8145
- const [draggedItemRect, setDraggedItemRect] = useState7(null);
8146
- const footerPointerDragRef = useRef5(null);
8147
- const suppressNextClickRef = useRef5(false);
8148
- const [linkPopover, setLinkPopover] = useState7(null);
8149
- const linkPopoverSessionRef = useRef5(null);
8150
- const addNavAfterAnchorRef = useRef5(null);
8151
- const editContentRef = useRef5({});
8152
- const [sitePages, setSitePages] = useState7([]);
8153
- const [sectionsByPath, setSectionsByPath] = useState7({});
8154
- const sectionsPrefetchGenRef = useRef5(0);
8155
- const setLinkPopoverRef = useRef5(setLinkPopover);
8156
- const linkPopoverPanelRef = useRef5(null);
8157
- const linkPopoverOpenRef = useRef5(false);
8158
- const linkPopoverGraceUntilRef = useRef5(0);
6655
+ const [reorderHrefKey, setReorderHrefKey] = useState5(null);
6656
+ const [reorderDragDisabled, setReorderDragDisabled] = useState5(false);
6657
+ const [toggleState, setToggleState] = useState5(null);
6658
+ const [maxBadge, setMaxBadge] = useState5(null);
6659
+ const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
6660
+ const [sectionGap, setSectionGap] = useState5(null);
6661
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
6662
+ const hoveredItemElRef = useRef4(null);
6663
+ const [hoveredItemRect, setHoveredItemRect] = useState5(null);
6664
+ const siblingHintElRef = useRef4(null);
6665
+ const [siblingHintRect, setSiblingHintRect] = useState5(null);
6666
+ const [isItemDragging, setIsItemDragging] = useState5(false);
6667
+ const [linkPopover, setLinkPopover] = useState5(null);
6668
+ const [sitePages, setSitePages] = useState5([]);
6669
+ const [sectionsByPath, setSectionsByPath] = useState5({});
6670
+ const sectionsPrefetchGenRef = useRef4(0);
6671
+ const setLinkPopoverRef = useRef4(setLinkPopover);
6672
+ const linkPopoverPanelRef = useRef4(null);
6673
+ const linkPopoverOpenRef = useRef4(false);
6674
+ const linkPopoverGraceUntilRef = useRef4(0);
8159
6675
  setLinkPopoverRef.current = setLinkPopover;
8160
- linkPopoverSessionRef.current = linkPopover;
8161
6676
  const bumpLinkPopoverGrace = () => {
8162
6677
  linkPopoverGraceUntilRef.current = Date.now() + 350;
8163
6678
  };
8164
- const runSectionsPrefetch = useCallback4((pages) => {
6679
+ const runSectionsPrefetch = useCallback2((pages) => {
8165
6680
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
8166
6681
  const gen = ++sectionsPrefetchGenRef.current;
8167
6682
  const paths = pages.map((p) => p.path);
@@ -8180,9 +6695,9 @@ function OhhwellsBridge() {
8180
6695
  );
8181
6696
  });
8182
6697
  }, [isEditMode, pathname]);
8183
- const runSectionsPrefetchRef = useRef5(runSectionsPrefetch);
6698
+ const runSectionsPrefetchRef = useRef4(runSectionsPrefetch);
8184
6699
  runSectionsPrefetchRef.current = runSectionsPrefetch;
8185
- useEffect8(() => {
6700
+ useEffect4(() => {
8186
6701
  if (!linkPopover) return;
8187
6702
  if (hoveredImageRef.current) {
8188
6703
  hoveredImageRef.current = null;
@@ -8190,9 +6705,33 @@ function OhhwellsBridge() {
8190
6705
  }
8191
6706
  hoveredGapRef.current = null;
8192
6707
  setSectionGap(null);
8193
- postToParent2({ type: "ow:image-unhover" });
8194
- }, [linkPopover, postToParent2]);
8195
- useEffect8(() => {
6708
+ setMediaHover(null);
6709
+ postToParent({ type: "ow:link-modal-lock", locked: true });
6710
+ const html = document.documentElement;
6711
+ const body = document.body;
6712
+ const prevHtmlOverflow = html.style.overflow;
6713
+ const prevBodyOverflow = body.style.overflow;
6714
+ html.style.overflow = "hidden";
6715
+ body.style.overflow = "hidden";
6716
+ const preventBackgroundScroll = (e) => {
6717
+ const target = e.target;
6718
+ if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
6719
+ return;
6720
+ }
6721
+ e.preventDefault();
6722
+ };
6723
+ const scrollOpts = { passive: false, capture: true };
6724
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6725
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6726
+ return () => {
6727
+ postToParent({ type: "ow:link-modal-lock", locked: false });
6728
+ html.style.overflow = prevHtmlOverflow;
6729
+ body.style.overflow = prevBodyOverflow;
6730
+ document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6731
+ document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6732
+ };
6733
+ }, [linkPopover, postToParent]);
6734
+ useEffect4(() => {
8196
6735
  if (!isEditMode) return;
8197
6736
  const useFixtures = shouldUseDevFixtures();
8198
6737
  if (useFixtures) {
@@ -8213,17 +6752,17 @@ function OhhwellsBridge() {
8213
6752
  runSectionsPrefetchRef.current(mapped);
8214
6753
  };
8215
6754
  window.addEventListener("message", onSitePages);
8216
- if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6755
+ if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
8217
6756
  return () => window.removeEventListener("message", onSitePages);
8218
- }, [isEditMode, postToParent2]);
8219
- useEffect8(() => {
6757
+ }, [isEditMode, postToParent]);
6758
+ useEffect4(() => {
8220
6759
  if (!isEditMode || shouldUseDevFixtures()) return;
8221
6760
  void loadAllSectionsManifest().then((manifest) => {
8222
6761
  if (Object.keys(manifest).length === 0) return;
8223
6762
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
8224
6763
  });
8225
6764
  }, [isEditMode]);
8226
- useEffect8(() => {
6765
+ useEffect4(() => {
8227
6766
  const update = () => {
8228
6767
  const el = activeElRef.current ?? selectedElRef.current;
8229
6768
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -8247,10 +6786,10 @@ function OhhwellsBridge() {
8247
6786
  vvp.removeEventListener("resize", update);
8248
6787
  };
8249
6788
  }, []);
8250
- const refreshStateRules = useCallback4(() => {
6789
+ const refreshStateRules = useCallback2(() => {
8251
6790
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
8252
6791
  }, []);
8253
- const processConfigRequest = useCallback4((insertAfterVal) => {
6792
+ const processConfigRequest = useCallback2((insertAfterVal) => {
8254
6793
  const tracker = getSectionsTracker();
8255
6794
  let entries = [];
8256
6795
  try {
@@ -8273,7 +6812,7 @@ function OhhwellsBridge() {
8273
6812
  }
8274
6813
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
8275
6814
  }, [isEditMode]);
8276
- const deactivate = useCallback4(() => {
6815
+ const deactivate = useCallback2(() => {
8277
6816
  const el = activeElRef.current;
8278
6817
  if (!el) return;
8279
6818
  const key = el.dataset.ohwKey;
@@ -8302,25 +6841,21 @@ function OhhwellsBridge() {
8302
6841
  setMaxBadge(null);
8303
6842
  setActiveCommands(/* @__PURE__ */ new Set());
8304
6843
  setToolbarShowEditLink(false);
8305
- postToParent2({ type: "ow:exit-edit" });
8306
- }, [postToParent2]);
8307
- const deselect = useCallback4(() => {
6844
+ postToParent({ type: "ow:exit-edit" });
6845
+ }, [postToParent]);
6846
+ const deselect = useCallback2(() => {
8308
6847
  selectedElRef.current = null;
8309
6848
  setReorderHrefKey(null);
8310
6849
  setReorderDragDisabled(false);
8311
- setIsFooterFrameSelection(false);
8312
6850
  siblingHintElRef.current = null;
8313
6851
  setSiblingHintRect(null);
8314
- setSiblingHintRects([]);
8315
6852
  setIsItemDragging(false);
8316
- hoveredNavContainerRef.current = null;
8317
- setHoveredNavContainerRect(null);
8318
6853
  if (!activeElRef.current) {
8319
6854
  setToolbarRect(null);
8320
6855
  setToolbarVariant("none");
8321
6856
  }
8322
6857
  }, []);
8323
- const reselectNavigationItem = useCallback4((navAnchor) => {
6858
+ const reselectNavigationItem = useCallback2((navAnchor) => {
8324
6859
  selectedElRef.current = navAnchor;
8325
6860
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
8326
6861
  setReorderHrefKey(key);
@@ -8330,7 +6865,7 @@ function OhhwellsBridge() {
8330
6865
  setToolbarShowEditLink(false);
8331
6866
  setActiveCommands(/* @__PURE__ */ new Set());
8332
6867
  }, []);
8333
- const commitNavigationTextEdit = useCallback4((navAnchor) => {
6868
+ const commitNavigationTextEdit = useCallback2((navAnchor) => {
8334
6869
  const el = activeElRef.current;
8335
6870
  if (!el) return;
8336
6871
  const key = el.dataset.ohwKey;
@@ -8343,9 +6878,9 @@ function OhhwellsBridge() {
8343
6878
  const html = sanitizeHtml(el.innerHTML);
8344
6879
  const original = originalContentRef.current ?? "";
8345
6880
  if (html !== sanitizeHtml(original)) {
8346
- postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
6881
+ postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
8347
6882
  const h = document.documentElement.scrollHeight;
8348
- if (h > 50) postToParent2({ type: "ow:height", height: h });
6883
+ if (h > 50) postToParent({ type: "ow:height", height: h });
8349
6884
  }
8350
6885
  }
8351
6886
  el.removeAttribute("contenteditable");
@@ -8353,364 +6888,79 @@ function OhhwellsBridge() {
8353
6888
  setMaxBadge(null);
8354
6889
  setActiveCommands(/* @__PURE__ */ new Set());
8355
6890
  setToolbarShowEditLink(false);
8356
- postToParent2({ type: "ow:exit-edit" });
6891
+ postToParent({ type: "ow:exit-edit" });
8357
6892
  reselectNavigationItem(navAnchor);
8358
- }, [postToParent2, reselectNavigationItem]);
8359
- const handleAddTopLevelNavItem = useCallback4(() => {
8360
- const items = listNavbarItems();
8361
- addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
8362
- deselectRef.current();
8363
- deactivateRef.current();
8364
- bumpLinkPopoverGrace();
8365
- setLinkPopover({
8366
- key: "__add-nav__",
8367
- mode: "create",
8368
- intent: "add-nav"
8369
- });
8370
- }, []);
8371
- const clearFooterDragVisuals = useCallback4(() => {
8372
- footerDragRef.current = null;
8373
- setSiblingHintRects([]);
8374
- setFooterDropSlots([]);
8375
- setActiveFooterDropIndex(null);
8376
- setDraggedItemRect(null);
8377
- setIsItemDragging(false);
8378
- unlockFooterDragInteraction();
8379
- }, []);
8380
- const refreshFooterDragVisuals = useCallback4((session, activeSlot, clientX, clientY) => {
8381
- const dragged = session.draggedEl;
8382
- setDraggedItemRect(dragged.getBoundingClientRect());
8383
- if (typeof clientX === "number" && typeof clientY === "number") {
8384
- session.lastClientX = clientX;
8385
- session.lastClientY = clientY;
8386
- }
8387
- session.activeSlot = activeSlot;
8388
- if (session.kind === "link") {
8389
- const columns2 = listFooterColumns();
8390
- const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
8391
- if (activeSlot) focusCols.add(activeSlot.columnIndex);
8392
- const siblingRects = [];
8393
- columns2.forEach((col, i) => {
8394
- if (!focusCols.has(i)) return;
8395
- for (const link of listFooterLinksInColumn(col)) {
8396
- if (link === dragged) continue;
8397
- siblingRects.push(link.getBoundingClientRect());
8398
- }
8399
- });
8400
- setSiblingHintRects(siblingRects);
8401
- const slots2 = [];
8402
- columns2.forEach((col, i) => {
8403
- slots2.push(...buildLinkDropSlots(col, i));
8404
- });
8405
- setFooterDropSlots(slots2);
8406
- const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
8407
- setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
8408
- return;
8409
- }
8410
- const columns = listFooterColumns();
8411
- setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
8412
- const slots = buildColumnDropSlots();
8413
- setFooterDropSlots(slots);
8414
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
8415
- setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
8416
- }, []);
8417
- const refreshFooterDragVisualsRef = useRef5(refreshFooterDragVisuals);
8418
- refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
8419
- const commitFooterDragRef = useRef5(() => {
8420
- });
8421
- const beginFooterDragRef = useRef5(() => {
8422
- });
8423
- const beginFooterDrag = useCallback4(
8424
- (session) => {
8425
- const rect = session.draggedEl.getBoundingClientRect();
8426
- session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
8427
- session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
8428
- session.activeSlot = session.activeSlot ?? null;
8429
- footerDragRef.current = session;
8430
- setIsItemDragging(true);
8431
- lockFooterDuringDrag();
8432
- siblingHintElRef.current = null;
8433
- setSiblingHintRect(null);
8434
- if (session.wasSelected && selectedElRef.current === session.draggedEl) {
8435
- setToolbarRect(rect);
8436
- }
8437
- const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
8438
- refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
8439
- },
8440
- [refreshFooterDragVisuals]
8441
- );
8442
- beginFooterDragRef.current = beginFooterDrag;
8443
- const commitFooterDrag = useCallback4(
8444
- (clientX, clientY) => {
8445
- const session = footerDragRef.current;
8446
- if (!session) {
8447
- clearFooterDragVisuals();
8448
- return;
8449
- }
8450
- const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
8451
- const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
8452
- let nextOrder = null;
8453
- const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
8454
- if (session.kind === "link" && session.hrefKey && slot) {
8455
- nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
8456
- } else if (session.kind === "column" && slot) {
8457
- nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
8458
- }
8459
- const wasSelected = session.wasSelected;
8460
- const draggedEl = session.draggedEl;
8461
- const hrefKey = session.hrefKey;
8462
- let movedColumnIndex = null;
8463
- if (session.kind === "column" && slot && nextOrder) {
8464
- let adjusted = slot.insertIndex;
8465
- if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
8466
- movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
8467
- }
8468
- clearFooterDragVisuals();
8469
- if (nextOrder) {
8470
- const orderJson = JSON.stringify(nextOrder);
8471
- editContentRef.current = {
8472
- ...editContentRef.current,
8473
- [FOOTER_ORDER_KEY]: orderJson
8474
- };
8475
- applyFooterOrder(nextOrder);
8476
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8477
- requestAnimationFrame(() => {
8478
- if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
8479
- applyFooterOrder(nextOrder);
8480
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
8481
- }
8482
- });
8483
- postToParentRef.current({
8484
- type: "ow:change",
8485
- nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
8486
- });
8487
- }
8488
- 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);
8489
- if (still && isNavigationItem(still)) {
8490
- selectRef.current(still);
8491
- } else if (still && isNavigationContainer(still)) {
8492
- selectFrameRef.current(still);
8493
- } else if (wasSelected) {
8494
- deselectRef.current();
8495
- }
8496
- },
8497
- [clearFooterDragVisuals]
8498
- );
8499
- commitFooterDragRef.current = commitFooterDrag;
8500
- const startFooterLinkDrag = useCallback4(
8501
- (anchor, clientX, clientY, wasSelected) => {
8502
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
8503
- if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
8504
- const column = findFooterColumnForLink(anchor);
8505
- const columns = listFooterColumns();
8506
- beginFooterDrag({
8507
- kind: "link",
8508
- hrefKey,
8509
- columnEl: column,
8510
- sourceColumnIndex: column ? columns.indexOf(column) : 0,
8511
- wasSelected,
8512
- draggedEl: anchor,
8513
- lastClientX: clientX,
8514
- lastClientY: clientY,
8515
- activeSlot: null
8516
- });
8517
- return true;
8518
- },
8519
- [beginFooterDrag]
8520
- );
8521
- const startFooterColumnDrag = useCallback4(
8522
- (columnEl, clientX, clientY, wasSelected) => {
8523
- const columns = listFooterColumns();
8524
- const idx = columns.indexOf(columnEl);
8525
- if (idx < 0) return false;
8526
- beginFooterDrag({
8527
- kind: "column",
8528
- hrefKey: null,
8529
- columnEl,
8530
- sourceColumnIndex: idx,
8531
- wasSelected,
8532
- draggedEl: columnEl,
8533
- lastClientX: clientX,
8534
- lastClientY: clientY,
8535
- activeSlot: null
8536
- });
8537
- return true;
8538
- },
8539
- [beginFooterDrag]
8540
- );
8541
- const handleItemDragStart = useCallback4(
8542
- (e) => {
8543
- const selected = selectedElRef.current;
8544
- if (!selected) {
8545
- setIsItemDragging(true);
8546
- return;
8547
- }
8548
- const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
8549
- const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
8550
- if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
8551
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8552
- if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
8553
- }
8554
- siblingHintElRef.current = null;
8555
- setSiblingHintRect(null);
8556
- setIsItemDragging(true);
8557
- },
8558
- [startFooterColumnDrag, startFooterLinkDrag]
8559
- );
8560
- const handleItemDragEnd = useCallback4(
8561
- (e) => {
8562
- if (footerDragRef.current) {
8563
- const x = e?.clientX;
8564
- const y = e?.clientY;
8565
- if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
8566
- commitFooterDrag(x, y);
8567
- } else {
8568
- commitFooterDrag();
8569
- }
8570
- return;
8571
- }
8572
- setIsItemDragging(false);
8573
- },
8574
- [commitFooterDrag]
8575
- );
8576
- const handleItemChromePointerDown = useCallback4((e) => {
8577
- if (e.button !== 0) return;
6893
+ }, [postToParent, reselectNavigationItem]);
6894
+ const handleAddNavigationItem = useCallback2(() => {
8578
6895
  const selected = selectedElRef.current;
8579
6896
  if (!selected) return;
8580
- armFooterPressDrag();
8581
- const hrefKey = selected.getAttribute("data-ohw-href-key");
8582
- if (hrefKey && isFooterHrefKey(hrefKey)) {
8583
- footerPointerDragRef.current = {
8584
- el: selected,
8585
- kind: "link",
8586
- startX: e.clientX,
8587
- startY: e.clientY,
8588
- pointerId: e.pointerId,
8589
- wasSelected: true,
8590
- started: false
8591
- };
8592
- return;
8593
- }
8594
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
8595
- footerPointerDragRef.current = {
8596
- el: selected,
8597
- kind: "column",
8598
- startX: e.clientX,
8599
- startY: e.clientY,
8600
- pointerId: e.pointerId,
8601
- wasSelected: true,
8602
- started: false
8603
- };
8604
- }
6897
+ const target = getNavigationItemAddHintTarget(selected);
6898
+ siblingHintElRef.current = target;
6899
+ setSiblingHintRect(target.getBoundingClientRect());
8605
6900
  }, []);
8606
- const handleItemChromeClick = useCallback4((clientX, clientY) => {
8607
- if (suppressNextClickRef.current) {
8608
- suppressNextClickRef.current = false;
8609
- return;
8610
- }
8611
- const selected = selectedElRef.current;
8612
- if (!selected || !isNavigationItem(selected)) return;
8613
- const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
8614
- if (!editable) return;
8615
- activateRef.current(editable, { caretX: clientX, caretY: clientY });
6901
+ const handleItemDragStart = useCallback2(() => {
6902
+ siblingHintElRef.current = null;
6903
+ setSiblingHintRect(null);
6904
+ setIsItemDragging(true);
6905
+ }, []);
6906
+ const handleItemDragEnd = useCallback2(() => {
6907
+ setIsItemDragging(false);
8616
6908
  }, []);
8617
6909
  reselectNavigationItemRef.current = reselectNavigationItem;
8618
6910
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
8619
- const select = useCallback4(
8620
- (anchor) => {
8621
- if (!isNavigationItem(anchor)) return;
8622
- if (activeElRef.current) deactivate();
8623
- selectedElRef.current = anchor;
8624
- clearHrefKeyHover(anchor);
8625
- hoveredNavContainerRef.current = null;
8626
- setHoveredNavContainerRect(null);
8627
- setHoveredItemRect(null);
8628
- hoveredItemElRef.current = null;
8629
- siblingHintElRef.current = null;
8630
- setSiblingHintRect(null);
8631
- setSiblingHintRects([]);
8632
- setIsItemDragging(false);
8633
- const { key, disabled } = getNavigationItemReorderState(anchor);
8634
- setReorderHrefKey(key);
8635
- setReorderDragDisabled(disabled);
8636
- setIsFooterFrameSelection(false);
8637
- setToolbarVariant("link-action");
8638
- setToolbarRect(anchor.getBoundingClientRect());
8639
- setToolbarShowEditLink(false);
8640
- setActiveCommands(/* @__PURE__ */ new Set());
8641
- },
8642
- [deactivate]
8643
- );
8644
- const selectFrame = useCallback4(
8645
- (el) => {
8646
- if (!isNavigationContainer(el)) return;
8647
- if (activeElRef.current) deactivate();
8648
- const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
8649
- selectedElRef.current = el;
8650
- clearHrefKeyHover(el);
8651
- hoveredNavContainerRef.current = null;
8652
- setHoveredNavContainerRect(null);
8653
- setHoveredItemRect(null);
8654
- hoveredItemElRef.current = null;
8655
- siblingHintElRef.current = null;
8656
- setSiblingHintRect(null);
8657
- setSiblingHintRects(
8658
- isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
8659
- );
8660
- setIsItemDragging(false);
6911
+ const select = useCallback2((anchor) => {
6912
+ if (!isNavigationItem(anchor)) return;
6913
+ if (activeElRef.current) deactivate();
6914
+ selectedElRef.current = anchor;
6915
+ clearHrefKeyHover(anchor);
6916
+ setHoveredItemRect(null);
6917
+ hoveredItemElRef.current = null;
6918
+ siblingHintElRef.current = null;
6919
+ setSiblingHintRect(null);
6920
+ setIsItemDragging(false);
6921
+ const { key, disabled } = getNavigationItemReorderState(anchor);
6922
+ setReorderHrefKey(key);
6923
+ setReorderDragDisabled(disabled);
6924
+ setToolbarVariant("link-action");
6925
+ setToolbarRect(anchor.getBoundingClientRect());
6926
+ setToolbarShowEditLink(false);
6927
+ setActiveCommands(/* @__PURE__ */ new Set());
6928
+ }, [deactivate]);
6929
+ const activate = useCallback2((el) => {
6930
+ if (activeElRef.current === el) return;
6931
+ selectedElRef.current = null;
6932
+ deactivate();
6933
+ if (hoveredImageRef.current) {
6934
+ hoveredImageRef.current = null;
6935
+ setMediaHover(null);
6936
+ }
6937
+ setToolbarVariant("rich-text");
6938
+ siblingHintElRef.current = null;
6939
+ setSiblingHintRect(null);
6940
+ setIsItemDragging(false);
6941
+ el.setAttribute("contenteditable", "true");
6942
+ el.removeAttribute("data-ohw-hovered");
6943
+ el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
6944
+ activeElRef.current = el;
6945
+ originalContentRef.current = el.innerHTML;
6946
+ el.focus();
6947
+ setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
6948
+ const navAnchor = getNavigationItemAnchor(el);
6949
+ if (navAnchor) {
8661
6950
  setReorderHrefKey(null);
8662
6951
  setReorderDragDisabled(false);
8663
- setIsFooterFrameSelection(isFooterColumn);
8664
- setToolbarVariant("select-frame");
8665
- setToolbarRect(el.getBoundingClientRect());
8666
- setToolbarShowEditLink(false);
8667
- setActiveCommands(/* @__PURE__ */ new Set());
8668
- },
8669
- [deactivate]
8670
- );
8671
- const activate = useCallback4(
8672
- (el, options) => {
8673
- if (activeElRef.current === el) return;
8674
- selectedElRef.current = null;
8675
- deactivate();
8676
- if (hoveredImageRef.current) {
8677
- hoveredImageRef.current = null;
8678
- postToParentRef.current({ type: "ow:image-unhover" });
8679
- }
8680
- setToolbarVariant("rich-text");
8681
- siblingHintElRef.current = null;
8682
- setSiblingHintRect(null);
8683
- setSiblingHintRects([]);
8684
- setIsItemDragging(false);
8685
- el.setAttribute("contenteditable", "true");
8686
- el.removeAttribute("data-ohw-hovered");
8687
- el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
8688
- activeElRef.current = el;
8689
- originalContentRef.current = el.innerHTML;
8690
- el.focus();
8691
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8692
- placeCaretAtPoint(el, options.caretX, options.caretY);
8693
- }
8694
- setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
8695
- const navAnchor = getNavigationItemAnchor(el);
8696
- if (navAnchor) {
8697
- setReorderHrefKey(null);
8698
- setReorderDragDisabled(false);
8699
- } else {
8700
- const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
8701
- setReorderHrefKey(reorderKey);
8702
- setReorderDragDisabled(reorderDisabled);
8703
- }
8704
- setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
8705
- postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
8706
- requestAnimationFrame(() => refreshActiveCommandsRef.current());
8707
- },
8708
- [deactivate, postToParent2]
8709
- );
6952
+ } else {
6953
+ const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
6954
+ setReorderHrefKey(reorderKey);
6955
+ setReorderDragDisabled(reorderDisabled);
6956
+ }
6957
+ setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6958
+ postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
6959
+ requestAnimationFrame(() => refreshActiveCommandsRef.current());
6960
+ }, [deactivate, postToParent]);
8710
6961
  activateRef.current = activate;
8711
6962
  deactivateRef.current = deactivate;
8712
6963
  selectRef.current = select;
8713
- selectFrameRef.current = selectFrame;
8714
6964
  deselectRef.current = deselect;
8715
6965
  useLayoutEffect2(() => {
8716
6966
  if (!subdomain || isEditMode) {
@@ -8722,7 +6972,6 @@ function OhhwellsBridge() {
8722
6972
  for (const [key, val] of Object.entries(content)) {
8723
6973
  if (key === "__ohw_sections") continue;
8724
6974
  if (applyVideoSettingNode(key, val)) continue;
8725
- if (applyCarouselNode(key, val)) continue;
8726
6975
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8727
6976
  if (el.dataset.ohwEditable === "image") {
8728
6977
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8753,8 +7002,6 @@ function OhhwellsBridge() {
8753
7002
  });
8754
7003
  applyLinkByKey(key, val);
8755
7004
  }
8756
- reconcileNavbarItemsFromContent(content);
8757
- reconcileFooterOrderFromContent(content);
8758
7005
  enforceLinkHrefs();
8759
7006
  initSectionsFromContent(content, true);
8760
7007
  sectionsLoadedRef.current = true;
@@ -8783,7 +7030,7 @@ function OhhwellsBridge() {
8783
7030
  cancelled = true;
8784
7031
  };
8785
7032
  }, [subdomain, isEditMode]);
8786
- useEffect8(() => {
7033
+ useEffect4(() => {
8787
7034
  if (!subdomain || isEditMode) return;
8788
7035
  let debounceTimer = null;
8789
7036
  let observer = null;
@@ -8796,7 +7043,6 @@ function OhhwellsBridge() {
8796
7043
  for (const [key, val] of Object.entries(content)) {
8797
7044
  if (key === "__ohw_sections") continue;
8798
7045
  if (applyVideoSettingNode(key, val)) continue;
8799
- if (applyCarouselNode(key, val)) continue;
8800
7046
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8801
7047
  if (el.dataset.ohwEditable === "image") {
8802
7048
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8815,8 +7061,6 @@ function OhhwellsBridge() {
8815
7061
  });
8816
7062
  applyLinkByKey(key, val);
8817
7063
  }
8818
- reconcileNavbarItemsFromContent(content);
8819
- reconcileFooterOrderFromContent(content);
8820
7064
  } finally {
8821
7065
  observer?.observe(document.body, { childList: true, subtree: true });
8822
7066
  }
@@ -8840,65 +7084,20 @@ function OhhwellsBridge() {
8840
7084
  const visible = Boolean(subdomain) && fetchState !== "done";
8841
7085
  el.style.display = visible ? "flex" : "none";
8842
7086
  }, [subdomain, fetchState]);
8843
- useEffect8(() => {
8844
- postToParent2({ type: "ow:navigation", path: pathname });
8845
- }, [pathname, postToParent2]);
8846
- useEffect8(() => {
7087
+ useEffect4(() => {
7088
+ postToParent({ type: "ow:navigation", path: pathname });
7089
+ }, [pathname, postToParent]);
7090
+ useEffect4(() => {
8847
7091
  if (!isEditMode) return;
8848
- if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8849
- if (document.querySelector("[data-ohw-section-picker]")) return;
8850
7092
  setLinkPopover(null);
8851
7093
  deselectRef.current();
8852
7094
  deactivateRef.current();
8853
7095
  }, [pathname, isEditMode]);
8854
- useEffect8(() => {
8855
- const contentForNav = () => {
8856
- if (isEditMode) return editContentRef.current;
8857
- if (!subdomain) return {};
8858
- return contentCache.get(subdomain) ?? {};
8859
- };
8860
- let applying = false;
8861
- let rafId = null;
8862
- const run = () => {
8863
- if (footerDragRef.current || applying) return;
8864
- applying = true;
8865
- try {
8866
- const content = contentForNav();
8867
- reconcileNavbarItemsFromContent(content);
8868
- reconcileFooterOrderFromContent(content);
8869
- document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
8870
- if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
8871
- disableNativeHrefDrag(el);
8872
- }
8873
- });
8874
- } finally {
8875
- applying = false;
8876
- }
8877
- };
8878
- const scheduleRun = () => {
8879
- if (rafId !== null) cancelAnimationFrame(rafId);
8880
- rafId = requestAnimationFrame(() => {
8881
- rafId = null;
8882
- run();
8883
- });
8884
- };
8885
- run();
8886
- const nav = document.querySelector("nav");
8887
- const footer = document.querySelector("footer");
8888
- const observer = new MutationObserver(scheduleRun);
8889
- if (nav) observer.observe(nav, { childList: true, subtree: true });
8890
- if (footer) observer.observe(footer, { childList: true, subtree: true });
8891
- if (!nav && !footer) return;
8892
- return () => {
8893
- observer.disconnect();
8894
- if (rafId !== null) cancelAnimationFrame(rafId);
8895
- };
8896
- }, [isEditMode, pathname, subdomain, fetchState]);
8897
- useEffect8(() => {
7096
+ useEffect4(() => {
8898
7097
  if (!isEditMode) return;
8899
7098
  const measure = () => {
8900
7099
  const h = document.body.scrollHeight;
8901
- if (h > 50) postToParent2({ type: "ow:height", height: h });
7100
+ if (h > 50) postToParent({ type: "ow:height", height: h });
8902
7101
  };
8903
7102
  const t1 = setTimeout(measure, 50);
8904
7103
  const t2 = setTimeout(measure, 500);
@@ -8917,8 +7116,8 @@ function OhhwellsBridge() {
8917
7116
  if (resizeTimer) clearTimeout(resizeTimer);
8918
7117
  window.removeEventListener("resize", handleResize);
8919
7118
  };
8920
- }, [pathname, isEditMode, postToParent2]);
8921
- useEffect8(() => {
7119
+ }, [pathname, isEditMode, postToParent]);
7120
+ useEffect4(() => {
8922
7121
  if (!subdomainFromQuery || isEditMode) return;
8923
7122
  const handleClick = (e) => {
8924
7123
  const anchor = e.target.closest("a");
@@ -8934,7 +7133,7 @@ function OhhwellsBridge() {
8934
7133
  document.addEventListener("click", handleClick, true);
8935
7134
  return () => document.removeEventListener("click", handleClick, true);
8936
7135
  }, [subdomainFromQuery, isEditMode, router]);
8937
- useEffect8(() => {
7136
+ useEffect4(() => {
8938
7137
  if (!isEditMode) {
8939
7138
  editStylesRef.current?.base.remove();
8940
7139
  editStylesRef.current?.forceHover.remove();
@@ -8957,29 +7156,6 @@ function OhhwellsBridge() {
8957
7156
  [data-ohw-editable] {
8958
7157
  display: block;
8959
7158
  }
8960
- /* Native <a> drag steals clicks \u2014 disable for edit links; reorder via press-to-drag / handle. */
8961
- footer [data-ohw-href-key] {
8962
- -webkit-user-drag: none !important;
8963
- }
8964
- footer [data-ohw-footer-col]:hover {
8965
- outline: 1.5px dashed ${PRIMARY2} !important;
8966
- outline-offset: 6px;
8967
- border-radius: 8px;
8968
- }
8969
- html[data-ohw-footer-press-drag] footer,
8970
- html[data-ohw-footer-press-drag] footer * {
8971
- user-select: none !important;
8972
- -webkit-user-select: none !important;
8973
- }
8974
- html[data-ohw-item-dragging] footer,
8975
- html[data-ohw-item-dragging] footer * {
8976
- user-select: none !important;
8977
- -webkit-user-select: none !important;
8978
- pointer-events: none !important;
8979
- }
8980
- html[data-ohw-item-dragging] {
8981
- cursor: grabbing !important;
8982
- }
8983
7159
  [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
8984
7160
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8985
7161
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
@@ -8988,7 +7164,6 @@ function OhhwellsBridge() {
8988
7164
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
8989
7165
  outline: 2px dashed ${PRIMARY2} !important;
8990
7166
  outline-offset: 4px;
8991
- border-radius: 2px;
8992
7167
  }
8993
7168
  [data-ohw-href-key] [data-ohw-hovered],
8994
7169
  [data-ohw-href-key][data-ohw-hovered],
@@ -9015,7 +7190,7 @@ function OhhwellsBridge() {
9015
7190
  stateViews.textContent = `
9016
7191
  [data-ohw-state-view]:not([data-ohw-state-view="default"]) { display: none; }
9017
7192
  [data-ohw-state-view="default"] [data-ohw-editable] { pointer-events: auto !important; }
9018
- [data-ohw-state-hovered] { outline: 2px dashed ${PRIMARY2} !important; outline-offset: 4px; border-radius: 2px; }
7193
+ [data-ohw-state-hovered] { outline: 2px dashed ${PRIMARY2} !important; outline-offset: 4px; }
9019
7194
  [data-ohw-state-hovered]:has([data-ohw-hovered]) { outline: none !important; }
9020
7195
  `;
9021
7196
  document.head.appendChild(base);
@@ -9025,46 +7200,12 @@ function OhhwellsBridge() {
9025
7200
  }
9026
7201
  refreshStateRules();
9027
7202
  const handleClick = (e) => {
9028
- if (suppressNextClickRef.current) {
9029
- suppressNextClickRef.current = false;
9030
- e.preventDefault();
9031
- e.stopPropagation();
9032
- return;
9033
- }
9034
7203
  const target = e.target;
9035
7204
  if (target.closest("[data-ohw-toolbar]")) return;
9036
7205
  if (target.closest("[data-ohw-state-toggle]")) return;
9037
7206
  if (target.closest("[data-ohw-max-badge]")) return;
9038
7207
  if (isInsideLinkEditor(target)) return;
9039
7208
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9040
- if (target.closest("[data-ohw-item-drag-surface]")) {
9041
- e.preventDefault();
9042
- e.stopPropagation();
9043
- const selected = selectedElRef.current;
9044
- if (selected && isNavigationItem(selected)) {
9045
- const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9046
- if (editable2) {
9047
- activateRef.current(editable2, {
9048
- caretX: e.clientX,
9049
- caretY: e.clientY
9050
- });
9051
- }
9052
- } else if (selected?.hasAttribute("data-ohw-footer-col")) {
9053
- const link = listFooterLinksInColumn(selected).find((el) => {
9054
- const r2 = el.getBoundingClientRect();
9055
- return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
9056
- });
9057
- if (link) selectRef.current(link);
9058
- }
9059
- return;
9060
- }
9061
- const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
9062
- if (navFromChrome) {
9063
- e.preventDefault();
9064
- e.stopPropagation();
9065
- selectFrameRef.current(navFromChrome);
9066
- return;
9067
- }
9068
7209
  e.preventDefault();
9069
7210
  e.stopPropagation();
9070
7211
  return;
@@ -9079,8 +7220,7 @@ function OhhwellsBridge() {
9079
7220
  bumpLinkPopoverGrace();
9080
7221
  setLinkPopoverRef.current({
9081
7222
  key: editable.dataset.ohwKey ?? "",
9082
- mode: "edit",
9083
- target: getLinkHref2(editable)
7223
+ target: getLinkHref(editable)
9084
7224
  });
9085
7225
  return;
9086
7226
  }
@@ -9096,58 +7236,25 @@ function OhhwellsBridge() {
9096
7236
  e.preventDefault();
9097
7237
  e.stopPropagation();
9098
7238
  if (selectedElRef.current === navAnchor) {
9099
- if (e.detail >= 2) return;
9100
- activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
9101
- return;
9102
- }
9103
- selectRef.current(navAnchor);
9104
- return;
9105
- }
9106
- e.preventDefault();
9107
- e.stopPropagation();
9108
- activateRef.current(editable);
9109
- return;
9110
- }
9111
- const hrefAnchor = getNavigationItemAnchor(target);
9112
- if (hrefAnchor) {
9113
- e.preventDefault();
9114
- e.stopPropagation();
9115
- if (selectedElRef.current === hrefAnchor) {
9116
- const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
9117
- if (textEditable) {
9118
- activateRef.current(textEditable, {
9119
- caretX: e.clientX,
9120
- caretY: e.clientY
9121
- });
7239
+ activateRef.current(editable);
7240
+ return;
9122
7241
  }
7242
+ selectRef.current(navAnchor);
9123
7243
  return;
9124
7244
  }
9125
- selectRef.current(hrefAnchor);
9126
- return;
9127
- }
9128
- const footerColClick = target.closest("[data-ohw-footer-col]");
9129
- if (footerColClick) {
9130
7245
  e.preventDefault();
9131
7246
  e.stopPropagation();
9132
- selectFrameRef.current(footerColClick);
7247
+ activateRef.current(editable);
9133
7248
  return;
9134
7249
  }
9135
- const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
9136
- if (navContainerToSelect) {
7250
+ const hrefAnchor = getNavigationItemAnchor(target);
7251
+ if (hrefAnchor) {
9137
7252
  e.preventDefault();
9138
7253
  e.stopPropagation();
9139
- selectFrameRef.current(navContainerToSelect);
7254
+ if (selectedElRef.current === hrefAnchor) return;
7255
+ selectRef.current(hrefAnchor);
9140
7256
  return;
9141
7257
  }
9142
- const selectedContainer = selectedElRef.current;
9143
- if (selectedContainer && isNavigationContainer(selectedContainer)) {
9144
- const navItem = getNavigationItemAnchor(target);
9145
- if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
9146
- e.preventDefault();
9147
- e.stopPropagation();
9148
- return;
9149
- }
9150
- }
9151
7258
  if (activeElRef.current) {
9152
7259
  const sel = window.getSelection();
9153
7260
  if (sel && !sel.isCollapsed) {
@@ -9173,58 +7280,10 @@ function OhhwellsBridge() {
9173
7280
  deselectRef.current();
9174
7281
  deactivateRef.current();
9175
7282
  };
9176
- const handleDblClick = (e) => {
9177
- const target = e.target;
9178
- if (target.closest("[data-ohw-toolbar]")) return;
9179
- if (target.closest("[data-ohw-state-toggle]")) return;
9180
- if (target.closest("[data-ohw-max-badge]")) return;
9181
- if (isInsideLinkEditor(target)) return;
9182
- if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9183
- return;
9184
- }
9185
- const navLabel = getNavigationLabelEditable(target);
9186
- if (!navLabel) return;
9187
- const { editable } = navLabel;
9188
- e.preventDefault();
9189
- e.stopPropagation();
9190
- if (activeElRef.current !== editable) {
9191
- activateRef.current(editable);
9192
- }
9193
- requestAnimationFrame(() => {
9194
- selectAllTextInEditable(editable);
9195
- refreshActiveCommandsRef.current();
9196
- });
9197
- };
9198
7283
  const handleMouseOver = (e) => {
9199
- if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
9200
7284
  const target = e.target;
9201
- if (toolbarVariantRef.current !== "select-frame") {
9202
- const navContainer = target.closest("[data-ohw-nav-container]");
9203
- if (navContainer && !getNavigationItemAnchor(target)) {
9204
- hoveredNavContainerRef.current = navContainer;
9205
- setHoveredNavContainerRect(navContainer.getBoundingClientRect());
9206
- hoveredItemElRef.current = null;
9207
- setHoveredItemRect(null);
9208
- return;
9209
- }
9210
- if (!target.closest("[data-ohw-nav-container]")) {
9211
- hoveredNavContainerRef.current = null;
9212
- setHoveredNavContainerRect(null);
9213
- }
9214
- }
9215
- const footerCol = target.closest("[data-ohw-footer-col]");
9216
- if (footerCol) {
9217
- hoveredNavContainerRef.current = null;
9218
- setHoveredNavContainerRect(null);
9219
- if (selectedElRef.current === footerCol) return;
9220
- hoveredItemElRef.current = footerCol;
9221
- setHoveredItemRect(footerCol.getBoundingClientRect());
9222
- return;
9223
- }
9224
7285
  const navAnchor = getNavigationItemAnchor(target);
9225
7286
  if (navAnchor) {
9226
- hoveredNavContainerRef.current = null;
9227
- setHoveredNavContainerRect(null);
9228
7287
  const selected2 = selectedElRef.current;
9229
7288
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
9230
7289
  clearHrefKeyHover(navAnchor);
@@ -9249,23 +7308,6 @@ function OhhwellsBridge() {
9249
7308
  };
9250
7309
  const handleMouseOut = (e) => {
9251
7310
  const target = e.target;
9252
- if (target.closest("[data-ohw-nav-container]")) {
9253
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9254
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
9255
- return;
9256
- }
9257
- hoveredNavContainerRef.current = null;
9258
- setHoveredNavContainerRect(null);
9259
- }
9260
- const footerCol = target.closest("[data-ohw-footer-col]");
9261
- if (footerCol && hoveredItemElRef.current === footerCol) {
9262
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
9263
- if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
9264
- if (related2?.closest("[data-ohw-footer-col]") === footerCol) return;
9265
- hoveredItemElRef.current = null;
9266
- setHoveredItemRect(null);
9267
- return;
9268
- }
9269
7311
  const navAnchor = getNavigationItemAnchor(target);
9270
7312
  if (navAnchor) {
9271
7313
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -9377,97 +7419,69 @@ function OhhwellsBridge() {
9377
7419
  const stickyPad = 48;
9378
7420
  const withinPad = x >= pr.left - stickyPad && x <= pr.left + pr.width + stickyPad && y >= pr.top - stickyPad && y <= pr.top + pr.height + stickyPad;
9379
7421
  if (withinPad) {
9380
- const yieldToSmaller = smallest !== pinned && candidatePool.includes(smallest) && (() => {
7422
+ if (smallest !== pinned && candidatePool.includes(smallest)) {
9381
7423
  const sr = getVisibleRect(smallest);
9382
- const pr2 = getVisibleRect(pinned);
9383
- return sr.width * sr.height < pr2.width * pr2.height;
9384
- })();
9385
- if (!yieldToSmaller) return pinned;
7424
+ const overOther = x >= sr.left && x <= sr.left + sr.width && y >= sr.top && y <= sr.top + sr.height;
7425
+ if (overOther) return smallest;
7426
+ }
7427
+ return pinned;
9386
7428
  }
9387
7429
  }
9388
7430
  return smallest;
9389
7431
  };
9390
- const dismissImageHover = () => {
9391
- if (hoveredImageRef.current) {
9392
- hoveredImageRef.current = null;
9393
- hoveredImageHasTextOverlapRef.current = false;
9394
- resumeAnimTracks();
9395
- postToParentRef.current({ type: "ow:image-unhover" });
9396
- }
9397
- clearImageHover();
9398
- };
9399
- const probeNavigationHoverAt = (x, y) => {
9400
- if (toolbarVariantRef.current === "select-frame") {
9401
- hoveredNavContainerRef.current = null;
9402
- setHoveredNavContainerRect(null);
9403
- return;
9404
- }
9405
- const navContainer = document.querySelector("[data-ohw-nav-container]");
9406
- if (!navContainer) {
9407
- hoveredNavContainerRef.current = null;
9408
- setHoveredNavContainerRect(null);
9409
- return;
9410
- }
9411
- const containerRect = navContainer.getBoundingClientRect();
9412
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
9413
- if (!overContainer) {
9414
- hoveredNavContainerRef.current = null;
9415
- setHoveredNavContainerRect(null);
9416
- return;
9417
- }
9418
- const navItemHit = Array.from(
9419
- navContainer.querySelectorAll("[data-ohw-href-key]")
9420
- ).find((el) => {
9421
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
9422
- const itemRect = el.getBoundingClientRect();
9423
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
9424
- });
9425
- if (navItemHit) {
9426
- hoveredNavContainerRef.current = null;
9427
- setHoveredNavContainerRect(null);
9428
- const selected = selectedElRef.current;
9429
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
9430
- clearHrefKeyHover(navItemHit);
9431
- hoveredItemElRef.current = navItemHit;
9432
- setHoveredItemRect(navItemHit.getBoundingClientRect());
9433
- }
9434
- return;
9435
- }
9436
- hoveredNavContainerRef.current = navContainer;
9437
- setHoveredNavContainerRect(containerRect);
9438
- hoveredItemElRef.current = null;
9439
- setHoveredItemRect(null);
9440
- };
9441
7432
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
9442
7433
  if (linkPopoverOpenRef.current) {
9443
- dismissImageHover();
9444
- return;
9445
- }
9446
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9447
- if (isPointOverNavigation(x, y)) {
9448
- dismissImageHover();
9449
- probeNavigationHoverAt(x, y);
7434
+ if (hoveredImageRef.current) {
7435
+ hoveredImageRef.current = null;
7436
+ hoveredImageHasTextOverlapRef.current = false;
7437
+ resumeAnimTracks();
7438
+ clearImageHover();
7439
+ }
9450
7440
  return;
9451
7441
  }
9452
- hoveredNavContainerRef.current = null;
9453
- setHoveredNavContainerRect(null);
9454
7442
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
9455
7443
  if (toggleEl) {
7444
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9456
7445
  const tr = toggleEl.getBoundingClientRect();
9457
7446
  if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
9458
- dismissImageHover();
7447
+ if (hoveredImageRef.current) {
7448
+ hoveredImageRef.current = null;
7449
+ resumeAnimTracks();
7450
+ clearImageHover();
7451
+ }
9459
7452
  return;
9460
7453
  }
9461
7454
  }
9462
7455
  const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
9463
7456
  const activeEl = activeElRef.current;
9464
7457
  if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
9465
- dismissImageHover();
7458
+ if (hoveredImageRef.current) {
7459
+ hoveredImageRef.current = null;
7460
+ hoveredImageHasTextOverlapRef.current = false;
7461
+ resumeAnimTracks();
7462
+ clearImageHover();
7463
+ }
9466
7464
  return;
9467
7465
  }
9468
7466
  if (imgEl) {
7467
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7468
+ const overMediaChrome = Array.from(
7469
+ document.querySelectorAll("[data-ohw-media-chrome]")
7470
+ ).some((el) => {
7471
+ const r2 = el.getBoundingClientRect();
7472
+ return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
7473
+ });
7474
+ if (overMediaChrome) {
7475
+ if (hoveredImageRef.current) {
7476
+ hoveredImageRef.current = null;
7477
+ hoveredImageHasTextOverlapRef.current = false;
7478
+ resumeAnimTracks();
7479
+ clearImageHover();
7480
+ }
7481
+ return;
7482
+ }
9469
7483
  const topEl = document.elementFromPoint(x, y);
9470
- 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]')) {
7484
+ if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
9471
7485
  if (hoveredImageRef.current) {
9472
7486
  hoveredImageRef.current = null;
9473
7487
  resumeAnimTracks();
@@ -9565,12 +7579,12 @@ function OhhwellsBridge() {
9565
7579
  resumeAnimTracks();
9566
7580
  clearImageHover();
9567
7581
  }
9568
- const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7582
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9569
7583
  const textEl = Array.from(
9570
7584
  document.querySelectorAll(NON_MEDIA_SELECTOR)
9571
7585
  ).find((el) => {
9572
7586
  const er = el.getBoundingClientRect();
9573
- return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7587
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
9574
7588
  });
9575
7589
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
9576
7590
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -9594,14 +7608,6 @@ function OhhwellsBridge() {
9594
7608
  }
9595
7609
  };
9596
7610
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
9597
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
9598
- if (activeStateElRef.current) {
9599
- activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
9600
- activeStateElRef.current = null;
9601
- setToggleState(null);
9602
- }
9603
- return;
9604
- }
9605
7611
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
9606
7612
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
9607
7613
  if (toggleEl) {
@@ -9674,14 +7680,6 @@ function OhhwellsBridge() {
9674
7680
  probeHoverCardsAt(clientX, clientY);
9675
7681
  };
9676
7682
  const handleDragOver = (e) => {
9677
- const footerSession = footerDragRef.current;
9678
- if (footerSession) {
9679
- e.preventDefault();
9680
- if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
9681
- const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
9682
- refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
9683
- return;
9684
- }
9685
7683
  e.preventDefault();
9686
7684
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9687
7685
  if (!el) {
@@ -9864,7 +7862,6 @@ function OhhwellsBridge() {
9864
7862
  continue;
9865
7863
  }
9866
7864
  if (applyVideoSettingNode(key, val)) continue;
9867
- if (applyCarouselNode(key, val)) continue;
9868
7865
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9869
7866
  if (el.dataset.ohwEditable === "image") {
9870
7867
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -9887,9 +7884,6 @@ function OhhwellsBridge() {
9887
7884
  sectionsLoadedRef.current = true;
9888
7885
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
9889
7886
  }
9890
- editContentRef.current = { ...editContentRef.current, ...content };
9891
- reconcileNavbarItemsFromContent(editContentRef.current);
9892
- reconcileFooterOrderFromContent(editContentRef.current);
9893
7887
  enforceLinkHrefs();
9894
7888
  postToParentRef.current({ type: "ow:hydrate-done" });
9895
7889
  };
@@ -9897,7 +7891,6 @@ function OhhwellsBridge() {
9897
7891
  const handleDeactivate = (e) => {
9898
7892
  if (e.data?.type !== "ow:deactivate") return;
9899
7893
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
9900
- if (document.querySelector("[data-ohw-section-picker]")) return;
9901
7894
  if (linkPopoverOpenRef.current) {
9902
7895
  setLinkPopoverRef.current(null);
9903
7896
  return;
@@ -9906,40 +7899,13 @@ function OhhwellsBridge() {
9906
7899
  deactivateRef.current();
9907
7900
  };
9908
7901
  window.addEventListener("message", handleDeactivate);
9909
- const handleUiEscape = (e) => {
9910
- if (e.data?.type !== "ui:escape") return;
9911
- if (linkPopoverOpenRef.current) {
9912
- setLinkPopoverRef.current(null);
9913
- }
9914
- };
9915
- window.addEventListener("message", handleUiEscape);
9916
7902
  const handleKeyDown = (e) => {
9917
- if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
9918
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
9919
- const navAnchor = getNavigationItemAnchor(activeElRef.current);
9920
- if (navAnchor) {
9921
- e.preventDefault();
9922
- selectAllTextInEditable(activeElRef.current);
9923
- refreshActiveCommandsRef.current();
9924
- return;
9925
- }
9926
- }
9927
7903
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
9928
7904
  setLinkPopoverRef.current(null);
9929
7905
  return;
9930
7906
  }
9931
7907
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
9932
- if (toolbarVariantRef.current === "select-frame") {
9933
- deselectRef.current();
9934
- return;
9935
- }
9936
- const parent = getNavigationSelectionParent(selectedElRef.current);
9937
- if (parent) {
9938
- e.preventDefault();
9939
- selectFrameRef.current(parent);
9940
- } else {
9941
- deselectRef.current();
9942
- }
7908
+ deselectRef.current();
9943
7909
  return;
9944
7910
  }
9945
7911
  if (e.key === "Escape" && activeElRef.current) {
@@ -9997,23 +7963,9 @@ function OhhwellsBridge() {
9997
7963
  if (hoveredItemElRef.current) {
9998
7964
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
9999
7965
  }
10000
- if (hoveredNavContainerRef.current) {
10001
- setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
10002
- }
10003
7966
  if (siblingHintElRef.current) {
10004
7967
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
10005
7968
  }
10006
- const selected = selectedElRef.current;
10007
- if (selected && !footerDragRef.current && (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)))) {
10008
- setSiblingHintRects(
10009
- listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
10010
- );
10011
- }
10012
- if (footerDragRef.current) {
10013
- const session = footerDragRef.current;
10014
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
10015
- refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
10016
- }
10017
7969
  if (hoveredImageRef.current) {
10018
7970
  const el = hoveredImageRef.current;
10019
7971
  const r2 = el.getBoundingClientRect();
@@ -10024,7 +7976,7 @@ function OhhwellsBridge() {
10024
7976
  };
10025
7977
  const handleSave = (e) => {
10026
7978
  if (e.data?.type !== "ow:save") return;
10027
- const nodes = collectEditableNodes(editContentRef.current);
7979
+ const nodes = collectEditableNodes();
10028
7980
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
10029
7981
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
10030
7982
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -10109,7 +8061,6 @@ function OhhwellsBridge() {
10109
8061
  const handleDocMouseLeave = () => {
10110
8062
  hoveredImageRef.current = null;
10111
8063
  setMediaHover(null);
10112
- setCarouselHover(null);
10113
8064
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
10114
8065
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
10115
8066
  activeStateElRef.current = null;
@@ -10149,48 +8100,6 @@ function OhhwellsBridge() {
10149
8100
  if (e.data?.type !== "ow:canvas-height" || typeof e.data.height !== "number") return;
10150
8101
  document.documentElement.style.setProperty("--ohw-canvas-h", `${e.data.height}px`);
10151
8102
  };
10152
- const handleCarouselChange = (e) => {
10153
- if (e.data?.type !== "ow:carousel-change") return;
10154
- const { key, images } = e.data;
10155
- if (!key || !Array.isArray(images)) return;
10156
- const text = JSON.stringify(images);
10157
- applyCarouselValue(key, images);
10158
- editContentRef.current = { ...editContentRef.current, [key]: text };
10159
- postToParentRef.current({ type: "ow:change", nodes: [{ key, text }] });
10160
- setCarouselHover(null);
10161
- };
10162
- const findCarouselAtPoint = (clientX, clientY) => {
10163
- const containers = Array.from(document.querySelectorAll(`[${CAROUSEL_ATTR}]`));
10164
- for (let i = containers.length - 1; i >= 0; i--) {
10165
- const r2 = containers[i].getBoundingClientRect();
10166
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) {
10167
- return containers[i];
10168
- }
10169
- }
10170
- return null;
10171
- };
10172
- const isPointOverEditable = (scope, clientX, clientY) => {
10173
- const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
10174
- for (const el of editables) {
10175
- const r2 = el.getBoundingClientRect();
10176
- if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
10177
- }
10178
- return false;
10179
- };
10180
- const handleCarouselHover = (e) => {
10181
- const container = findCarouselAtPoint(e.clientX, e.clientY);
10182
- const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
10183
- if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
10184
- setCarouselHover((prev) => prev ? null : prev);
10185
- return;
10186
- }
10187
- const key = container.getAttribute("data-ohw-key") ?? "";
10188
- if (!key) return;
10189
- const r2 = getVisibleRect(container);
10190
- setCarouselHover(
10191
- (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 } }
10192
- );
10193
- };
10194
8103
  const applyToolbarPos = (rect) => {
10195
8104
  const ps = parentScrollRef.current;
10196
8105
  const approxW = 330;
@@ -10250,45 +8159,9 @@ function OhhwellsBridge() {
10250
8159
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
10251
8160
  });
10252
8161
  if (textEditable) {
10253
- const hrefCtx = getHrefKeyFromElement(textEditable);
10254
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
10255
- if (navAnchor) {
10256
- if (selectedElRef.current === navAnchor) {
10257
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
10258
- } else {
10259
- selectRef.current(navAnchor);
10260
- }
10261
- return;
10262
- }
10263
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
8162
+ activateRef.current(textEditable);
10264
8163
  return;
10265
8164
  }
10266
- const navContainer = document.querySelector("[data-ohw-nav-container]");
10267
- if (navContainer) {
10268
- const r2 = navContainer.getBoundingClientRect();
10269
- const pad = 8;
10270
- const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
10271
- if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
10272
- selectFrameRef.current(navContainer);
10273
- return;
10274
- }
10275
- }
10276
- const navRoot = document.querySelector("[data-ohw-nav-root]");
10277
- if (navRoot && navContainer) {
10278
- const rr = navRoot.getBoundingClientRect();
10279
- if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
10280
- const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
10281
- if (book) {
10282
- const br = book.getBoundingClientRect();
10283
- if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
10284
- deactivateRef.current();
10285
- return;
10286
- }
10287
- }
10288
- selectFrameRef.current(navContainer);
10289
- return;
10290
- }
10291
- }
10292
8165
  deactivateRef.current();
10293
8166
  };
10294
8167
  window.addEventListener("message", handleSave);
@@ -10299,7 +8172,6 @@ function OhhwellsBridge() {
10299
8172
  window.addEventListener("message", handleRemoveSchedulingSection);
10300
8173
  window.addEventListener("message", handleImageUrl);
10301
8174
  window.addEventListener("message", handleImageUploading);
10302
- window.addEventListener("message", handleCarouselChange);
10303
8175
  window.addEventListener("message", handleCanvasHeight);
10304
8176
  window.addEventListener("message", handleParentScroll);
10305
8177
  window.addEventListener("message", handlePointerSync);
@@ -10311,13 +8183,11 @@ function OhhwellsBridge() {
10311
8183
  };
10312
8184
  window.addEventListener("resize", handleViewportResize, { passive: true });
10313
8185
  document.addEventListener("click", handleClick, true);
10314
- document.addEventListener("dblclick", handleDblClick, true);
10315
8186
  document.addEventListener("paste", handlePaste, true);
10316
8187
  document.addEventListener("input", handleInput, true);
10317
8188
  document.addEventListener("mouseover", handleMouseOver, true);
10318
8189
  document.addEventListener("mouseout", handleMouseOut, true);
10319
8190
  document.addEventListener("mousemove", handleMouseMove, true);
10320
- document.addEventListener("mousemove", handleCarouselHover, true);
10321
8191
  document.addEventListener("mouseleave", handleDocMouseLeave);
10322
8192
  document.addEventListener("dragover", handleDragOver, true);
10323
8193
  document.addEventListener("dragleave", handleDragLeave, true);
@@ -10327,13 +8197,11 @@ function OhhwellsBridge() {
10327
8197
  window.addEventListener("scroll", handleScroll, true);
10328
8198
  return () => {
10329
8199
  document.removeEventListener("click", handleClick, true);
10330
- document.removeEventListener("dblclick", handleDblClick, true);
10331
8200
  document.removeEventListener("paste", handlePaste, true);
10332
8201
  document.removeEventListener("input", handleInput, true);
10333
8202
  document.removeEventListener("mouseover", handleMouseOver, true);
10334
8203
  document.removeEventListener("mouseout", handleMouseOut, true);
10335
8204
  document.removeEventListener("mousemove", handleMouseMove, true);
10336
- document.removeEventListener("mousemove", handleCarouselHover, true);
10337
8205
  document.removeEventListener("mouseleave", handleDocMouseLeave);
10338
8206
  document.removeEventListener("dragover", handleDragOver, true);
10339
8207
  document.removeEventListener("dragleave", handleDragLeave, true);
@@ -10349,7 +8217,6 @@ function OhhwellsBridge() {
10349
8217
  window.removeEventListener("message", handleRemoveSchedulingSection);
10350
8218
  window.removeEventListener("message", handleImageUrl);
10351
8219
  window.removeEventListener("message", handleImageUploading);
10352
- window.removeEventListener("message", handleCarouselChange);
10353
8220
  window.removeEventListener("message", handleCanvasHeight);
10354
8221
  window.removeEventListener("message", handleParentScroll);
10355
8222
  window.removeEventListener("resize", handleViewportResize);
@@ -10357,162 +8224,13 @@ function OhhwellsBridge() {
10357
8224
  window.removeEventListener("message", handleClickAt);
10358
8225
  window.removeEventListener("message", handleHydrate);
10359
8226
  window.removeEventListener("message", handleDeactivate);
10360
- window.removeEventListener("message", handleUiEscape);
10361
8227
  autoSaveTimers.current.forEach(clearTimeout);
10362
8228
  autoSaveTimers.current.clear();
10363
8229
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
10364
8230
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
10365
8231
  };
10366
8232
  }, [isEditMode, refreshStateRules]);
10367
- useEffect8(() => {
10368
- if (!isEditMode) return;
10369
- const THRESHOLD = 10;
10370
- const resolveWasSelected = (el) => {
10371
- if (selectedElRef.current === el) return true;
10372
- const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
10373
- return activeAnchor === el;
10374
- };
10375
- const onPointerDown = (e) => {
10376
- if (e.button !== 0) return;
10377
- if (footerDragRef.current) return;
10378
- const target = e.target;
10379
- if (!target) return;
10380
- 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]')) {
10381
- return;
10382
- }
10383
- if (target.closest("[data-ohw-item-drag-surface]")) return;
10384
- const anchor = getNavigationItemAnchor(target);
10385
- const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
10386
- if (anchor && isFooterHrefKey(hrefKey)) {
10387
- armFooterPressDrag();
10388
- footerPointerDragRef.current = {
10389
- el: anchor,
10390
- kind: "link",
10391
- startX: e.clientX,
10392
- startY: e.clientY,
10393
- pointerId: e.pointerId,
10394
- wasSelected: resolveWasSelected(anchor),
10395
- started: false
10396
- };
10397
- return;
10398
- }
10399
- const col = target.closest("[data-ohw-footer-col]");
10400
- if (col && !getNavigationItemAnchor(target)) {
10401
- armFooterPressDrag();
10402
- footerPointerDragRef.current = {
10403
- el: col,
10404
- kind: "column",
10405
- startX: e.clientX,
10406
- startY: e.clientY,
10407
- pointerId: e.pointerId,
10408
- wasSelected: resolveWasSelected(col),
10409
- started: false
10410
- };
10411
- }
10412
- };
10413
- const onPointerMove = (e) => {
10414
- const pending = footerPointerDragRef.current;
10415
- if (!pending) return;
10416
- if (pending.started) {
10417
- e.preventDefault();
10418
- clearTextSelection();
10419
- const session = footerDragRef.current;
10420
- if (!session) return;
10421
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10422
- refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
10423
- return;
10424
- }
10425
- const dx = e.clientX - pending.startX;
10426
- const dy = e.clientY - pending.startY;
10427
- if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
10428
- e.preventDefault();
10429
- pending.started = true;
10430
- clearTextSelection();
10431
- try {
10432
- document.body.setPointerCapture(pending.pointerId);
10433
- } catch {
10434
- }
10435
- if (linkPopoverOpenRef.current) {
10436
- setLinkPopoverRef.current(null);
10437
- }
10438
- if (activeElRef.current) {
10439
- deactivateRef.current();
10440
- }
10441
- if (pending.kind === "link") {
10442
- const key = pending.el.getAttribute("data-ohw-href-key");
10443
- if (!key) return;
10444
- const column = findFooterColumnForLink(pending.el);
10445
- const columns2 = listFooterColumns();
10446
- beginFooterDragRef.current({
10447
- kind: "link",
10448
- hrefKey: key,
10449
- columnEl: column,
10450
- sourceColumnIndex: column ? columns2.indexOf(column) : 0,
10451
- wasSelected: pending.wasSelected,
10452
- draggedEl: pending.el,
10453
- lastClientX: e.clientX,
10454
- lastClientY: e.clientY,
10455
- activeSlot: null
10456
- });
10457
- return;
10458
- }
10459
- const columns = listFooterColumns();
10460
- const idx = columns.indexOf(pending.el);
10461
- if (idx < 0) return;
10462
- beginFooterDragRef.current({
10463
- kind: "column",
10464
- hrefKey: null,
10465
- columnEl: pending.el,
10466
- sourceColumnIndex: idx,
10467
- wasSelected: pending.wasSelected,
10468
- draggedEl: pending.el,
10469
- lastClientX: e.clientX,
10470
- lastClientY: e.clientY,
10471
- activeSlot: null
10472
- });
10473
- };
10474
- const endPointerDrag = (e) => {
10475
- const pending = footerPointerDragRef.current;
10476
- footerPointerDragRef.current = null;
10477
- try {
10478
- if (document.body.hasPointerCapture(e.pointerId)) {
10479
- document.body.releasePointerCapture(e.pointerId);
10480
- }
10481
- } catch {
10482
- }
10483
- if (!pending?.started) {
10484
- unlockFooterDragInteraction();
10485
- return;
10486
- }
10487
- suppressNextClickRef.current = true;
10488
- commitFooterDragRef.current(e.clientX, e.clientY);
10489
- };
10490
- const blockSelectStart = (e) => {
10491
- if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
10492
- e.preventDefault();
10493
- }
10494
- };
10495
- const onDragEnd = (_e) => {
10496
- if (!footerDragRef.current) return;
10497
- commitFooterDragRef.current();
10498
- };
10499
- document.addEventListener("pointerdown", onPointerDown, true);
10500
- document.addEventListener("pointermove", onPointerMove, true);
10501
- document.addEventListener("pointerup", endPointerDrag, true);
10502
- document.addEventListener("pointercancel", endPointerDrag, true);
10503
- document.addEventListener("selectstart", blockSelectStart, true);
10504
- document.addEventListener("dragend", onDragEnd, true);
10505
- return () => {
10506
- document.removeEventListener("pointerdown", onPointerDown, true);
10507
- document.removeEventListener("pointermove", onPointerMove, true);
10508
- document.removeEventListener("pointerup", endPointerDrag, true);
10509
- document.removeEventListener("pointercancel", endPointerDrag, true);
10510
- document.removeEventListener("selectstart", blockSelectStart, true);
10511
- document.removeEventListener("dragend", onDragEnd, true);
10512
- unlockFooterDragInteraction();
10513
- };
10514
- }, [isEditMode]);
10515
- useEffect8(() => {
8233
+ useEffect4(() => {
10516
8234
  const handler = (e) => {
10517
8235
  if (e.data?.type !== "ow:request-schedule-config") return;
10518
8236
  const insertAfterVal = e.data.insertAfter;
@@ -10528,7 +8246,7 @@ function OhhwellsBridge() {
10528
8246
  window.addEventListener("message", handler);
10529
8247
  return () => window.removeEventListener("message", handler);
10530
8248
  }, [processConfigRequest]);
10531
- useEffect8(() => {
8249
+ useEffect4(() => {
10532
8250
  if (!isEditMode) return;
10533
8251
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
10534
8252
  el.removeAttribute("data-ohw-active-state");
@@ -10549,33 +8267,27 @@ function OhhwellsBridge() {
10549
8267
  const next = { ...prev, [pathKey]: sections };
10550
8268
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
10551
8269
  });
10552
- postToParent2({
10553
- type: "ow:ready",
10554
- version: "1",
10555
- path: pathname,
10556
- nodes: collectEditableNodes(editContentRef.current),
10557
- sections
10558
- });
10559
- postToParent2({ type: "ow:request-site-pages" });
8270
+ postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
8271
+ postToParent({ type: "ow:request-site-pages" });
10560
8272
  }, 150);
10561
8273
  return () => {
10562
8274
  cancelAnimationFrame(raf);
10563
8275
  clearTimeout(timer);
10564
8276
  };
10565
- }, [pathname, isEditMode, refreshStateRules, postToParent2]);
10566
- useEffect8(() => {
8277
+ }, [pathname, isEditMode, refreshStateRules, postToParent]);
8278
+ useEffect4(() => {
10567
8279
  scrollToHashSectionWhenReady();
10568
8280
  const onHashChange = () => scrollToHashSectionWhenReady();
10569
8281
  window.addEventListener("hashchange", onHashChange);
10570
8282
  return () => window.removeEventListener("hashchange", onHashChange);
10571
8283
  }, [pathname]);
10572
- const handleCommand = useCallback4((cmd) => {
8284
+ const handleCommand = useCallback2((cmd) => {
10573
8285
  document.execCommand(cmd, false);
10574
8286
  activeElRef.current?.focus();
10575
8287
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
10576
8288
  refreshActiveCommandsRef.current();
10577
8289
  }, []);
10578
- const handleStateChange = useCallback4((state) => {
8290
+ const handleStateChange = useCallback2((state) => {
10579
8291
  if (!activeStateElRef.current) return;
10580
8292
  const el = activeStateElRef.current;
10581
8293
  if (state === "Default") {
@@ -10588,22 +8300,18 @@ function OhhwellsBridge() {
10588
8300
  }
10589
8301
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
10590
8302
  }, [deactivate]);
10591
- const closeLinkPopover = useCallback4(() => {
10592
- addNavAfterAnchorRef.current = null;
10593
- setLinkPopover(null);
10594
- }, []);
10595
- const openLinkPopoverForActive = useCallback4(() => {
8303
+ const closeLinkPopover = useCallback2(() => setLinkPopover(null), []);
8304
+ const openLinkPopoverForActive = useCallback2(() => {
10596
8305
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
10597
8306
  if (!hrefCtx) return;
10598
8307
  bumpLinkPopoverGrace();
10599
8308
  setLinkPopover({
10600
8309
  key: hrefCtx.key,
10601
- mode: "edit",
10602
- target: getLinkHref2(hrefCtx.anchor)
8310
+ target: getLinkHref(hrefCtx.anchor)
10603
8311
  });
10604
8312
  deactivate();
10605
8313
  }, [deactivate]);
10606
- const openLinkPopoverForSelected = useCallback4(() => {
8314
+ const openLinkPopoverForSelected = useCallback2(() => {
10607
8315
  const anchor = selectedElRef.current;
10608
8316
  if (!anchor) return;
10609
8317
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -10611,80 +8319,30 @@ function OhhwellsBridge() {
10611
8319
  bumpLinkPopoverGrace();
10612
8320
  setLinkPopover({
10613
8321
  key,
10614
- mode: "edit",
10615
- target: getLinkHref2(anchor)
8322
+ target: getLinkHref(anchor)
10616
8323
  });
10617
8324
  deselect();
10618
8325
  }, [deselect]);
10619
- const handleLinkPopoverSubmit = useCallback4(
8326
+ const handleLinkPopoverSubmit = useCallback2(
10620
8327
  (target) => {
10621
- const session = linkPopoverSessionRef.current;
10622
- if (!session) return;
10623
- if (session.intent === "add-nav") {
10624
- const trimmed = target.trim();
10625
- let href = trimmed;
10626
- let label = "Untitled";
10627
- if (trimmed) {
10628
- const { pageRoute, sectionId } = parseTarget(trimmed);
10629
- const page = resolvePage(pageRoute, sitePages);
10630
- const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
10631
- const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
10632
- label = section?.label ?? page.title;
10633
- href = trimmed;
10634
- }
10635
- const afterAnchor = addNavAfterAnchorRef.current;
10636
- const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(href, label, afterAnchor);
10637
- applyLinkByKey(hrefKey, href);
10638
- document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
10639
- el.textContent = label;
10640
- });
10641
- const navCount = String(index + 1);
10642
- const orderJson = JSON.stringify(order);
10643
- editContentRef.current = {
10644
- ...editContentRef.current,
10645
- [hrefKey]: href,
10646
- [labelKey]: label,
10647
- [NAV_COUNT_KEY]: navCount,
10648
- [NAV_ORDER_KEY]: orderJson
10649
- };
10650
- postToParent2({
10651
- type: "ow:change",
10652
- nodes: [
10653
- { key: hrefKey, text: href },
10654
- { key: labelKey, text: label },
10655
- { key: NAV_COUNT_KEY, text: navCount },
10656
- { key: NAV_ORDER_KEY, text: orderJson }
10657
- ]
10658
- });
10659
- postToParent2({ type: "ow:toast", title: "Link added", toastType: "success" });
10660
- addNavAfterAnchorRef.current = null;
10661
- setLinkPopover(null);
10662
- enforceLinkHrefs();
10663
- requestAnimationFrame(() => {
10664
- selectRef.current(anchor);
10665
- });
10666
- return;
10667
- }
10668
- const { key } = session;
8328
+ if (!linkPopover) return;
8329
+ const { key } = linkPopover;
10669
8330
  applyLinkByKey(key, target);
10670
- postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8331
+ postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
10671
8332
  setLinkPopover(null);
10672
8333
  },
10673
- [postToParent2, sitePages, sectionsByPath]
10674
- );
10675
- const handleMediaReplace = useCallback4(
10676
- (key) => {
10677
- postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
10678
- },
10679
- [postToParent2, mediaHover?.elementType]
8334
+ [linkPopover, postToParent]
10680
8335
  );
10681
- const handleEditCarousel = useCallback4(
8336
+ const showEditLink = toolbarShowEditLink;
8337
+ const currentSections = sectionsByPath[pathname] ?? [];
8338
+ linkPopoverOpenRef.current = linkPopover !== null;
8339
+ const handleMediaReplace = useCallback2(
10682
8340
  (key) => {
10683
- postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
8341
+ postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
10684
8342
  },
10685
- [postToParent2]
8343
+ [postToParent, mediaHover?.elementType]
10686
8344
  );
10687
- const handleMediaFadeOutComplete = useCallback4((key) => {
8345
+ const handleMediaFadeOutComplete = useCallback2((key) => {
10688
8346
  setUploadingRects((prev) => {
10689
8347
  if (!(key in prev)) return prev;
10690
8348
  const next = { ...prev };
@@ -10692,7 +8350,7 @@ function OhhwellsBridge() {
10692
8350
  return next;
10693
8351
  });
10694
8352
  }, []);
10695
- const handleVideoSettingsChange = useCallback4(
8353
+ const handleVideoSettingsChange = useCallback2(
10696
8354
  (key, settings) => {
10697
8355
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
10698
8356
  const video = getVideoEl(el);
@@ -10700,7 +8358,7 @@ function OhhwellsBridge() {
10700
8358
  if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
10701
8359
  if (typeof settings.muted === "boolean") video.muted = settings.muted;
10702
8360
  syncVideoPlayback(video);
10703
- postToParent2({
8361
+ postToParent({
10704
8362
  type: "ow:change",
10705
8363
  nodes: [
10706
8364
  { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
@@ -10712,15 +8370,12 @@ function OhhwellsBridge() {
10712
8370
  );
10713
8371
  });
10714
8372
  },
10715
- [postToParent2]
8373
+ [postToParent]
10716
8374
  );
10717
- const showEditLink = toolbarShowEditLink;
10718
- const currentSections = sectionsByPath[pathname] ?? [];
10719
- linkPopoverOpenRef.current = linkPopover !== null;
10720
- return bridgeRoot ? createPortal2(
10721
- /* @__PURE__ */ jsxs14(Fragment5, { children: [
10722
- /* @__PURE__ */ jsx25("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
10723
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx25(
8375
+ return bridgeRoot ? createPortal(
8376
+ /* @__PURE__ */ jsxs12(Fragment4, { children: [
8377
+ /* @__PURE__ */ jsx22("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8378
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx22(
10724
8379
  MediaOverlay,
10725
8380
  {
10726
8381
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -10731,7 +8386,7 @@ function OhhwellsBridge() {
10731
8386
  },
10732
8387
  `uploading-${key}`
10733
8388
  )),
10734
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx25(
8389
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx22(
10735
8390
  MediaOverlay,
10736
8391
  {
10737
8392
  hover: mediaHover,
@@ -10740,33 +8395,53 @@ function OhhwellsBridge() {
10740
8395
  onVideoSettingsChange: handleVideoSettingsChange
10741
8396
  }
10742
8397
  ),
10743
- carouselHover && /* @__PURE__ */ jsx25(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
10744
- siblingHintRect && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
10745
- siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
10746
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
10747
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx25(
10748
- "div",
8398
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8399
+ hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8400
+ toolbarRect && toolbarVariant === "link-action" && /* @__PURE__ */ jsx22(
8401
+ ItemInteractionLayer,
10749
8402
  {
10750
- className: "pointer-events-none fixed z-2147483646",
10751
- style: {
10752
- left: slot.left,
10753
- top: slot.top,
10754
- width: slot.width,
10755
- height: slot.height
10756
- },
10757
- children: /* @__PURE__ */ jsx25(DropIndicator, { direction: slot.direction, state: activeFooterDropIndex === i ? "dragActive" : "dragIdle", className: slot.direction === "horizontal" ? "!h-full !w-full" : "!h-full !w-full" })
10758
- },
10759
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
10760
- )),
10761
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && siblingHintRects.length === 0 && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
10762
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && /* @__PURE__ */ jsx25(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
10763
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && siblingHintRects.length === 0 && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
10764
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx25(ItemInteractionLayer, { rect: isItemDragging && draggedItemRect && footerDragRef.current?.wasSelected ? draggedItemRect : toolbarRect, elRef: glowElRef, state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current), showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection, dragDisabled: reorderDragDisabled, dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item", onDragHandleDragStart: handleItemDragStart, onDragHandleDragEnd: handleItemDragEnd, onItemPointerDown: handleItemChromePointerDown, onItemClick: handleItemChromeClick, toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx25(ItemActionToolbar, { onEditLink: openLinkPopoverForSelected, addItemDisabled: true, editLinkDisabled: false, moreDisabled: true }) : void 0 }),
10765
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs14(Fragment5, { children: [
10766
- /* @__PURE__ */ jsx25(EditGlowChrome, { rect: toolbarRect, elRef: glowElRef, reorderHrefKey, dragDisabled: reorderDragDisabled }),
10767
- /* @__PURE__ */ jsx25(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands, showEditLink, onEditLink: openLinkPopoverForActive })
8403
+ rect: toolbarRect,
8404
+ elRef: glowElRef,
8405
+ state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
8406
+ showHandle: Boolean(reorderHrefKey),
8407
+ dragDisabled: reorderDragDisabled,
8408
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8409
+ onDragHandleDragStart: handleItemDragStart,
8410
+ onDragHandleDragEnd: handleItemDragEnd,
8411
+ toolbar: isItemDragging ? void 0 : /* @__PURE__ */ jsx22(
8412
+ ItemActionToolbar,
8413
+ {
8414
+ onEditLink: openLinkPopoverForSelected,
8415
+ onAddItem: handleAddNavigationItem,
8416
+ addItemDisabled: false
8417
+ }
8418
+ )
8419
+ }
8420
+ ),
8421
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs12(Fragment4, { children: [
8422
+ /* @__PURE__ */ jsx22(
8423
+ EditGlowChrome,
8424
+ {
8425
+ rect: toolbarRect,
8426
+ elRef: glowElRef,
8427
+ reorderHrefKey,
8428
+ dragDisabled: reorderDragDisabled
8429
+ }
8430
+ ),
8431
+ /* @__PURE__ */ jsx22(
8432
+ FloatingToolbar,
8433
+ {
8434
+ rect: toolbarRect,
8435
+ parentScroll: parentScrollRef.current,
8436
+ elRef: toolbarElRef,
8437
+ onCommand: handleCommand,
8438
+ activeCommands,
8439
+ showEditLink,
8440
+ onEditLink: openLinkPopoverForActive
8441
+ }
8442
+ )
10768
8443
  ] }),
10769
- maxBadge && /* @__PURE__ */ jsxs14(
8444
+ maxBadge && /* @__PURE__ */ jsxs12(
10770
8445
  "div",
10771
8446
  {
10772
8447
  "data-ohw-max-badge": "",
@@ -10792,29 +8467,56 @@ function OhhwellsBridge() {
10792
8467
  ]
10793
8468
  }
10794
8469
  ),
10795
- toggleState && !linkPopover && /* @__PURE__ */ jsx25(StateToggle, { rect: toggleState.rect, activeState: toggleState.activeState, states: toggleState.states, onStateChange: handleStateChange }),
10796
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs14("div", { "data-ohw-section-insert-line": "", className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none", style: { top: sectionGap.y, transform: "translateY(-50%)" }, children: [
10797
- /* @__PURE__ */ jsx25("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10798
- /* @__PURE__ */ jsx25(
10799
- Badge,
10800
- {
10801
- 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",
10802
- onClick: () => {
10803
- window.parent.postMessage(
10804
- {
10805
- type: "ow:add-section",
10806
- insertAfter: sectionGap.insertAfter,
10807
- insertBefore: sectionGap.insertBefore
8470
+ toggleState && /* @__PURE__ */ jsx22(
8471
+ StateToggle,
8472
+ {
8473
+ rect: toggleState.rect,
8474
+ activeState: toggleState.activeState,
8475
+ states: toggleState.states,
8476
+ onStateChange: handleStateChange
8477
+ }
8478
+ ),
8479
+ sectionGap && /* @__PURE__ */ jsxs12(
8480
+ "div",
8481
+ {
8482
+ "data-ohw-section-insert-line": "",
8483
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8484
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
8485
+ children: [
8486
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8487
+ /* @__PURE__ */ jsx22(
8488
+ Badge,
8489
+ {
8490
+ 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",
8491
+ onClick: () => {
8492
+ window.parent.postMessage(
8493
+ { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
8494
+ "*"
8495
+ );
10808
8496
  },
10809
- "*"
10810
- );
10811
- },
10812
- children: "Add Section"
10813
- }
10814
- ),
10815
- /* @__PURE__ */ jsx25("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10816
- ] }),
10817
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx25(LinkPopover, { panelRef: linkPopoverPanelRef, portalContainer: dialogPortalContainer, open: true, mode: linkPopover.mode ?? "edit", pages: sitePages, sections: currentSections, sectionsByPath, initialTarget: linkPopover.target, existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [], onClose: closeLinkPopover, onSubmit: handleLinkPopoverSubmit }, linkPopover.key) : null
8497
+ children: "Add Section"
8498
+ }
8499
+ ),
8500
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8501
+ ]
8502
+ }
8503
+ ),
8504
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx22(
8505
+ LinkPopover,
8506
+ {
8507
+ panelRef: linkPopoverPanelRef,
8508
+ portalContainer: dialogPortalContainer,
8509
+ open: true,
8510
+ mode: "edit",
8511
+ pages: sitePages,
8512
+ sections: currentSections,
8513
+ sectionsByPath,
8514
+ initialTarget: linkPopover.target,
8515
+ onClose: closeLinkPopover,
8516
+ onSubmit: handleLinkPopoverSubmit
8517
+ },
8518
+ `${linkPopover.key}-${pathname}`
8519
+ ) : null
10818
8520
  ] }),
10819
8521
  bridgeRoot
10820
8522
  ) : null;
@@ -10824,7 +8526,6 @@ export {
10824
8526
  CustomToolbarButton,
10825
8527
  CustomToolbarDivider,
10826
8528
  DragHandle,
10827
- DropIndicator,
10828
8529
  ItemActionToolbar,
10829
8530
  ItemInteractionLayer,
10830
8531
  LinkEditorPanel,
@@ -10839,14 +8540,12 @@ export {
10839
8540
  TooltipProvider,
10840
8541
  TooltipTrigger,
10841
8542
  buildTarget,
10842
- dropIndicatorVariants,
10843
8543
  filterAvailablePages,
10844
8544
  getEditModeInitialState,
10845
8545
  isEditSessionActive,
10846
8546
  isValidUrl,
10847
8547
  parseTarget,
10848
8548
  toggleVariants,
10849
- useOhwCarousel,
10850
8549
  validateUrlInput
10851
8550
  };
10852
8551
  //# sourceMappingURL=index.js.map