@ohhwells/bridge 0.1.36-next.54 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React9, { useCallback as useCallback4, useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState6 } from "react";
4
+ import React8, { useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useRef as useRef3, 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
  }
@@ -550,7 +530,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
550
530
  if (!cls.id) return;
551
531
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
552
532
  },
553
- children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
533
+ children: isFull ? "Join Waitlist" : "Book Now"
554
534
  }
555
535
  )
556
536
  ] })
@@ -592,17 +572,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
592
572
  const [isHovered, setIsHovered] = useState2(false);
593
573
  const [modalState, setModalState] = useState2(null);
594
574
  const switchScheduleIdRef = useRef(null);
595
- const liveScheduleIdRef = useRef(null);
596
- const refetchLiveSchedule = useCallback(async () => {
597
- const id = liveScheduleIdRef.current;
598
- if (!id) return;
599
- try {
600
- const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
601
- const data = await res.json();
602
- if (data?.id) setSchedule(data);
603
- } catch {
604
- }
605
- }, []);
606
575
  const dates = useMemo(() => {
607
576
  const today = /* @__PURE__ */ new Date();
608
577
  today.setHours(0, 0, 0, 0);
@@ -622,18 +591,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
622
591
  }
623
592
  }
624
593
  }, [schedule, dates]);
625
- useEffect(() => {
626
- if (typeof window === "undefined") return;
627
- const onVisible = () => {
628
- if (!document.hidden) void refetchLiveSchedule();
629
- };
630
- window.addEventListener("focus", refetchLiveSchedule);
631
- document.addEventListener("visibilitychange", onVisible);
632
- return () => {
633
- window.removeEventListener("focus", refetchLiveSchedule);
634
- document.removeEventListener("visibilitychange", onVisible);
635
- };
636
- }, [refetchLiveSchedule]);
637
594
  useEffect(() => {
638
595
  if (typeof window === "undefined") return;
639
596
  const isInEditor = window.self !== window.top;
@@ -695,7 +652,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
695
652
  setLoading(false);
696
653
  return;
697
654
  }
698
- liveScheduleIdRef.current = effectiveId;
655
+ ;
699
656
  (async () => {
700
657
  try {
701
658
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -752,14 +709,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
752
709
  const data = await res.json().catch(() => ({}));
753
710
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
754
711
  }
755
- await refetchLiveSchedule();
756
712
  };
757
713
  const handleReplaceSchedule = () => {
758
714
  setIsHovered(false);
759
- window.parent.postMessage(
760
- { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
761
- "*"
762
- );
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
+ }
763
724
  };
764
725
  if (!inEditor && !loading && !schedule) return null;
765
726
  const sectionId = `scheduling-${insertAfter}`;
@@ -802,19 +763,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
802
763
  ),
803
764
  /* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
804
765
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
805
- /* @__PURE__ */ jsx2(
806
- "h2",
807
- {
808
- className: "font-display text-center m-0 text-(--color-dark,#200C02)",
809
- style: {
810
- fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
811
- fontWeight: "var(--font-weight-heading, 400)",
812
- lineHeight: 1.05,
813
- letterSpacing: "-0.02em"
814
- },
815
- children: schedule?.name ?? "Book an appointment"
816
- }
817
- ),
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" }),
818
767
  schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
819
768
  ] }),
820
769
  /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4400,7 +4349,6 @@ function ItemActionToolbar({
4400
4349
  onEditLink,
4401
4350
  onAddItem,
4402
4351
  onMore,
4403
- editLinkDisabled = false,
4404
4352
  addItemDisabled = true,
4405
4353
  moreDisabled = true,
4406
4354
  tooltipSide = "bottom"
@@ -4409,12 +4357,10 @@ function ItemActionToolbar({
4409
4357
  /* @__PURE__ */ jsx8(
4410
4358
  ToolbarActionTooltip,
4411
4359
  {
4412
- label: "Edit link",
4360
+ label: "Add link",
4413
4361
  side: tooltipSide,
4414
- disabled: editLinkDisabled,
4415
4362
  buttonProps: {
4416
4363
  onMouseDown: (e) => {
4417
- if (editLinkDisabled) return;
4418
4364
  e.preventDefault();
4419
4365
  e.stopPropagation();
4420
4366
  onEditLink?.();
@@ -4586,223 +4532,9 @@ function ItemInteractionLayer({
4586
4532
  );
4587
4533
  }
4588
4534
 
4589
- // src/ui/MediaOverlay.tsx
4590
- import * as React6 from "react";
4591
- import { Film, ImageIcon, Pause, Play, Volume2, VolumeX } from "lucide-react";
4592
-
4593
- // src/ui/button.tsx
4594
- import * as React5 from "react";
4595
- import { Slot } from "radix-ui";
4596
- import { jsx as jsx10 } from "react/jsx-runtime";
4597
- var buttonVariants = cva(
4598
- "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
4599
- {
4600
- variants: {
4601
- variant: {
4602
- default: "bg-primary text-primary-foreground hover:opacity-90",
4603
- outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
4604
- ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
4605
- },
4606
- size: {
4607
- default: "h-9",
4608
- sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
4609
- }
4610
- },
4611
- defaultVariants: {
4612
- variant: "default",
4613
- size: "default"
4614
- }
4615
- }
4616
- );
4617
- var Button = React5.forwardRef(
4618
- ({ className, variant, size, asChild = false, ...props }, ref) => {
4619
- const Comp = asChild ? Slot.Root : "button";
4620
- return /* @__PURE__ */ jsx10(
4621
- Comp,
4622
- {
4623
- ref,
4624
- "data-slot": "button",
4625
- className: cn(buttonVariants({ variant, size, className })),
4626
- ...props
4627
- }
4628
- );
4629
- }
4630
- );
4631
- Button.displayName = "Button";
4632
-
4633
- // src/ui/MediaOverlay.tsx
4634
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
4635
- var MEDIA_UPLOAD_FADE_MS = 300;
4636
- var VIDEO_SETTINGS_BAR_INSET = 8;
4637
- var OVERLAY_BUTTON_STYLE = {
4638
- pointerEvents: "auto",
4639
- fontFamily: "Inter, sans-serif",
4640
- fontSize: 12,
4641
- color: "#000"
4642
- };
4643
- var SKELETON_CSS = `
4644
- @keyframes ohw-media-shimmer {
4645
- 0% { transform: translateX(-100%); }
4646
- 100% { transform: translateX(100%); }
4647
- }
4648
- [data-ohw-media-skeleton] {
4649
- overflow: hidden;
4650
- background-color: #d1d5db; /* tailwind gray-300 */
4651
- }
4652
- [data-ohw-media-skeleton]::after {
4653
- content: "";
4654
- position: absolute;
4655
- inset: 0;
4656
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
4657
- animation: ohw-media-shimmer 1.4s infinite;
4658
- }
4659
- `;
4660
- function MediaOverlay({
4661
- hover,
4662
- isUploading,
4663
- fadingOut = false,
4664
- onFadeOutComplete,
4665
- onReplace,
4666
- onVideoSettingsChange
4667
- }) {
4668
- const { rect } = hover;
4669
- const skeletonRef = React6.useRef(null);
4670
- const isVideo = hover.elementType === "video";
4671
- const autoplay = hover.videoAutoplay ?? true;
4672
- const muted = hover.videoMuted ?? true;
4673
- const box = {
4674
- position: "fixed",
4675
- top: rect.top,
4676
- left: rect.left,
4677
- width: rect.width,
4678
- height: rect.height,
4679
- zIndex: 2147483646
4680
- };
4681
- React6.useEffect(() => {
4682
- if (!isUploading || !fadingOut || !skeletonRef.current) return;
4683
- const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4684
- duration: MEDIA_UPLOAD_FADE_MS,
4685
- easing: "ease-out",
4686
- fill: "forwards"
4687
- });
4688
- anim.finished.then(() => onFadeOutComplete?.(hover.key)).catch(() => onFadeOutComplete?.(hover.key));
4689
- return () => anim.cancel();
4690
- }, [isUploading, fadingOut, onFadeOutComplete, hover.key]);
4691
- if (isUploading) {
4692
- return /* @__PURE__ */ jsx11(
4693
- "div",
4694
- {
4695
- ref: skeletonRef,
4696
- "data-ohw-bridge": "",
4697
- "data-ohw-media-overlay": "",
4698
- "data-ohw-media-skeleton": "",
4699
- "aria-hidden": true,
4700
- style: { ...box, pointerEvents: "none" },
4701
- children: /* @__PURE__ */ jsx11("style", { children: SKELETON_CSS })
4702
- }
4703
- );
4704
- }
4705
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs5(
4706
- "div",
4707
- {
4708
- "data-ohw-bridge": "",
4709
- "data-ohw-media-overlay": "",
4710
- className: "flex items-center justify-center gap-1.5",
4711
- style: {
4712
- position: "fixed",
4713
- top: rect.top + VIDEO_SETTINGS_BAR_INSET,
4714
- left: rect.left,
4715
- width: rect.width,
4716
- zIndex: 2147483647,
4717
- pointerEvents: "auto"
4718
- },
4719
- onClick: (e) => e.stopPropagation(),
4720
- children: [
4721
- /* @__PURE__ */ jsx11(
4722
- Button,
4723
- {
4724
- "data-ohw-media-overlay": "",
4725
- variant: "outline",
4726
- size: "sm",
4727
- className: "cursor-pointer hover:bg-background",
4728
- style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4729
- "aria-label": autoplay ? "Disable autoplay" : "Enable autoplay",
4730
- title: autoplay ? "Autoplay on \u2014 click to disable" : "Autoplay off \u2014 click to enable",
4731
- onMouseDown: (e) => e.preventDefault(),
4732
- onClick: (e) => {
4733
- e.stopPropagation();
4734
- onVideoSettingsChange?.(hover.key, { autoplay: !autoplay });
4735
- },
4736
- children: autoplay ? /* @__PURE__ */ jsx11(Pause, { size: 14 }) : /* @__PURE__ */ jsx11(Play, { size: 14 })
4737
- }
4738
- ),
4739
- /* @__PURE__ */ jsx11(
4740
- Button,
4741
- {
4742
- "data-ohw-media-overlay": "",
4743
- variant: "outline",
4744
- size: "sm",
4745
- className: "cursor-pointer hover:bg-background",
4746
- style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4747
- "aria-label": muted ? "Unmute video" : "Mute video",
4748
- title: muted ? "Muted \u2014 click to unmute" : "Sound on \u2014 click to mute",
4749
- onMouseDown: (e) => e.preventDefault(),
4750
- onClick: (e) => {
4751
- e.stopPropagation();
4752
- onVideoSettingsChange?.(hover.key, { muted: !muted });
4753
- },
4754
- children: muted ? /* @__PURE__ */ jsx11(VolumeX, { size: 14 }) : /* @__PURE__ */ jsx11(Volume2, { size: 14 })
4755
- }
4756
- )
4757
- ]
4758
- }
4759
- ) : null;
4760
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
4761
- settingsBar,
4762
- /* @__PURE__ */ jsx11(
4763
- "div",
4764
- {
4765
- "data-ohw-bridge": "",
4766
- "data-ohw-media-overlay": "",
4767
- className: "flex items-center justify-center cursor-pointer",
4768
- style: {
4769
- ...box,
4770
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4771
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4772
- // in-document, pointer-events does it natively. The button below opts back in, so
4773
- // Replace still works.
4774
- pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4775
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4776
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4777
- },
4778
- onClick: () => onReplace(hover.key),
4779
- children: /* @__PURE__ */ jsxs5(
4780
- Button,
4781
- {
4782
- "data-ohw-media-overlay": "",
4783
- variant: "outline",
4784
- size: "sm",
4785
- className: "gap-1.5 cursor-pointer hover:bg-background",
4786
- style: OVERLAY_BUTTON_STYLE,
4787
- onMouseDown: (e) => e.preventDefault(),
4788
- onClick: (e) => {
4789
- e.stopPropagation();
4790
- onReplace(hover.key);
4791
- },
4792
- children: [
4793
- isVideo ? /* @__PURE__ */ jsx11(Film, { size: 14 }) : /* @__PURE__ */ jsx11(ImageIcon, { size: 14 }),
4794
- isVideo ? "Replace video" : "Replace image"
4795
- ]
4796
- }
4797
- )
4798
- }
4799
- )
4800
- ] });
4801
- }
4802
-
4803
4535
  // src/OhhwellsBridge.tsx
4804
- import { createPortal as createPortal2 } from "react-dom";
4805
- import { usePathname as usePathname2, useRouter as useRouter2, useSearchParams } from "next/navigation";
4536
+ import { createPortal } from "react-dom";
4537
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
4806
4538
 
4807
4539
  // src/lib/session-search.ts
4808
4540
  var STORAGE_KEY = "ohw-preserved-search";
@@ -5078,29 +4810,61 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5078
4810
  tick();
5079
4811
  }
5080
4812
 
5081
- // src/ui/link-modal/LinkPopover.tsx
5082
- import { useEffect as useEffect6 } from "react";
5083
-
5084
4813
  // src/ui/dialog.tsx
5085
- import * as React7 from "react";
4814
+ import * as React5 from "react";
5086
4815
  import { Dialog as DialogPrimitive } from "radix-ui";
5087
- import { X } from "lucide-react";
5088
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
5089
- function Dialog2({
5090
- ...props
5091
- }) {
5092
- return /* @__PURE__ */ jsx12(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
4816
+
4817
+ // src/ui/icons.tsx
4818
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
4819
+ function IconX({ className, ...props }) {
4820
+ return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4821
+ /* @__PURE__ */ jsx10("path", { d: "M18 6 6 18" }),
4822
+ /* @__PURE__ */ jsx10("path", { d: "m6 6 12 12" })
4823
+ ] });
5093
4824
  }
5094
- function DialogPortal({
5095
- ...props
5096
- }) {
5097
- return /* @__PURE__ */ jsx12(DialogPrimitive.Portal, { ...props });
4825
+ function IconChevronDown({ className, ...props }) {
4826
+ return /* @__PURE__ */ jsx10("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx10("path", { d: "m6 9 6 6 6-6" }) });
4827
+ }
4828
+ function IconFile({ className, ...props }) {
4829
+ return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4830
+ /* @__PURE__ */ jsx10("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4831
+ /* @__PURE__ */ jsx10("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4832
+ ] });
4833
+ }
4834
+ function IconInfo({ className, ...props }) {
4835
+ return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4836
+ /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "10" }),
4837
+ /* @__PURE__ */ jsx10("path", { d: "M12 16v-4" }),
4838
+ /* @__PURE__ */ jsx10("path", { d: "M12 8h.01" })
4839
+ ] });
4840
+ }
4841
+ function IconArrowRight({ className, ...props }) {
4842
+ return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4843
+ /* @__PURE__ */ jsx10("path", { d: "M5 12h14" }),
4844
+ /* @__PURE__ */ jsx10("path", { d: "m12 5 7 7-7 7" })
4845
+ ] });
4846
+ }
4847
+ function IconSection({ className, ...props }) {
4848
+ return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4849
+ /* @__PURE__ */ jsx10("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4850
+ /* @__PURE__ */ jsx10("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4851
+ /* @__PURE__ */ jsx10("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4852
+ ] });
4853
+ }
4854
+
4855
+ // src/ui/dialog.tsx
4856
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
4857
+ function Dialog2({ ...props }) {
4858
+ return /* @__PURE__ */ jsx11(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
4859
+ }
4860
+ function DialogPortal({ ...props }) {
4861
+ return /* @__PURE__ */ jsx11(DialogPrimitive.Portal, { ...props });
5098
4862
  }
5099
4863
  function DialogOverlay({
5100
4864
  className,
5101
4865
  ...props
5102
4866
  }) {
5103
- return /* @__PURE__ */ jsx12(
4867
+ return /* @__PURE__ */ jsx11(
5104
4868
  DialogPrimitive.Overlay,
5105
4869
  {
5106
4870
  "data-slot": "dialog-overlay",
@@ -5110,74 +4874,57 @@ function DialogOverlay({
5110
4874
  }
5111
4875
  );
5112
4876
  }
5113
- var DialogContent = React7.forwardRef(
5114
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5115
- const positionMode = container ? "absolute" : "fixed";
5116
- return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
5117
- /* @__PURE__ */ jsx12(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5118
- /* @__PURE__ */ jsxs6(
5119
- DialogPrimitive.Content,
5120
- {
5121
- ref,
5122
- "data-slot": "dialog-content",
5123
- className: cn(
5124
- positionMode,
5125
- "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-visible",
5126
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5127
- container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5128
- className
5129
- ),
5130
- onMouseDown: (e) => e.stopPropagation(),
5131
- ...props,
5132
- children: [
5133
- children,
5134
- showCloseButton ? /* @__PURE__ */ jsx12(
5135
- DialogPrimitive.Close,
5136
- {
5137
- type: "button",
5138
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5139
- "aria-label": "Close",
5140
- children: /* @__PURE__ */ jsx12(X, { size: 16, "aria-hidden": true })
5141
- }
5142
- ) : null
5143
- ]
5144
- }
5145
- )
5146
- ] });
5147
- }
5148
- );
4877
+ var DialogContent = React5.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4878
+ const positionMode = container ? "absolute" : "fixed";
4879
+ return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
4880
+ /* @__PURE__ */ jsx11(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4881
+ /* @__PURE__ */ jsxs6(
4882
+ DialogPrimitive.Content,
4883
+ {
4884
+ ref,
4885
+ "data-slot": "dialog-content",
4886
+ className: cn(
4887
+ positionMode,
4888
+ "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",
4889
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
4890
+ container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
4891
+ className
4892
+ ),
4893
+ onMouseDown: (e) => e.stopPropagation(),
4894
+ ...props,
4895
+ children: [
4896
+ children,
4897
+ showCloseButton ? /* @__PURE__ */ jsx11(
4898
+ DialogPrimitive.Close,
4899
+ {
4900
+ type: "button",
4901
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
4902
+ "aria-label": "Close",
4903
+ children: /* @__PURE__ */ jsx11(IconX, { "aria-hidden": true })
4904
+ }
4905
+ ) : null
4906
+ ]
4907
+ }
4908
+ )
4909
+ ] });
4910
+ });
5149
4911
  DialogContent.displayName = DialogPrimitive.Content.displayName;
5150
- function DialogHeader({
5151
- className,
5152
- ...props
5153
- }) {
5154
- return /* @__PURE__ */ jsx12("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4912
+ function DialogHeader({ className, ...props }) {
4913
+ return /* @__PURE__ */ jsx11("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5155
4914
  }
5156
- function DialogFooter({
5157
- className,
5158
- ...props
5159
- }) {
5160
- return /* @__PURE__ */ jsx12(
5161
- "div",
5162
- {
5163
- className: cn("flex items-center justify-end gap-2", className),
5164
- ...props
5165
- }
5166
- );
4915
+ function DialogFooter({ className, ...props }) {
4916
+ return /* @__PURE__ */ jsx11("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5167
4917
  }
5168
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
4918
+ var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
5169
4919
  DialogPrimitive.Title,
5170
4920
  {
5171
4921
  ref,
5172
- className: cn(
5173
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
5174
- className
5175
- ),
4922
+ className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5176
4923
  ...props
5177
4924
  }
5178
4925
  ));
5179
4926
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
5180
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
4927
+ var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
5181
4928
  DialogPrimitive.Description,
5182
4929
  {
5183
4930
  ref,
@@ -5188,40 +4935,59 @@ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @
5188
4935
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
5189
4936
  var DialogClose = DialogPrimitive.Close;
5190
4937
 
5191
- // src/ui/link-modal/LinkEditorPanel.tsx
5192
- import { Info, X as X3 } from "lucide-react";
4938
+ // src/ui/button.tsx
4939
+ import * as React6 from "react";
4940
+ import { Slot } from "radix-ui";
4941
+ import { jsx as jsx12 } from "react/jsx-runtime";
4942
+ var buttonVariants = cva(
4943
+ "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
4944
+ {
4945
+ variants: {
4946
+ variant: {
4947
+ default: "bg-primary text-primary-foreground hover:opacity-90",
4948
+ outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
4949
+ ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
4950
+ },
4951
+ size: {
4952
+ default: "h-9",
4953
+ sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
4954
+ }
4955
+ },
4956
+ defaultVariants: {
4957
+ variant: "default",
4958
+ size: "default"
4959
+ }
4960
+ }
4961
+ );
4962
+ var Button = React6.forwardRef(
4963
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
4964
+ const Comp = asChild ? Slot.Root : "button";
4965
+ return /* @__PURE__ */ jsx12(
4966
+ Comp,
4967
+ {
4968
+ ref,
4969
+ "data-slot": "button",
4970
+ className: cn(buttonVariants({ variant, size, className })),
4971
+ ...props
4972
+ }
4973
+ );
4974
+ }
4975
+ );
4976
+ Button.displayName = "Button";
5193
4977
 
5194
4978
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5195
- import { ArrowRight, File, GalleryVertical } from "lucide-react";
5196
4979
  import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5197
- function DestinationBreadcrumb({
5198
- pageTitle,
5199
- sectionLabel
5200
- }) {
4980
+ function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5201
4981
  return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2", children: [
5202
- /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
4982
+ /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5203
4983
  /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3", children: [
5204
4984
  /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
5205
- /* @__PURE__ */ jsx13(File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5206
- /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
4985
+ /* @__PURE__ */ jsx13(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4986
+ /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5207
4987
  ] }),
5208
- /* @__PURE__ */ jsx13(
5209
- ArrowRight,
5210
- {
5211
- size: 16,
5212
- className: "shrink-0 text-muted-foreground",
5213
- "aria-hidden": true
5214
- }
5215
- ),
4988
+ /* @__PURE__ */ jsx13(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5216
4989
  /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5217
- /* @__PURE__ */ jsx13(
5218
- GalleryVertical,
5219
- {
5220
- size: 16,
5221
- className: "shrink-0 text-foreground",
5222
- "aria-hidden": true
5223
- }
5224
- ),
4990
+ /* @__PURE__ */ jsx13(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5225
4991
  /* @__PURE__ */ jsx13("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5226
4992
  ] })
5227
4993
  ] })
@@ -5229,13 +4995,8 @@ function DestinationBreadcrumb({
5229
4995
  }
5230
4996
 
5231
4997
  // src/ui/link-modal/SectionTreeItem.tsx
5232
- import { GalleryVertical as GalleryVertical2 } from "lucide-react";
5233
4998
  import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5234
- function SectionTreeItem({
5235
- section,
5236
- onSelect,
5237
- selected
5238
- }) {
4999
+ function SectionTreeItem({ section, onSelect, selected }) {
5239
5000
  const interactive = Boolean(onSelect);
5240
5001
  return /* @__PURE__ */ jsxs8("div", { className: "flex h-9 w-full items-end pl-3", children: [
5241
5002
  /* @__PURE__ */ jsx14(
@@ -5263,28 +5024,30 @@ function SectionTreeItem({
5263
5024
  interactive && selected && "border-primary"
5264
5025
  ),
5265
5026
  children: [
5266
- /* @__PURE__ */ jsx14(
5267
- GalleryVertical2,
5268
- {
5269
- size: 16,
5270
- className: "shrink-0 text-foreground",
5271
- "aria-hidden": true
5272
- }
5273
- ),
5027
+ /* @__PURE__ */ jsx14(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5274
5028
  /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5275
5029
  ]
5276
5030
  }
5277
5031
  )
5278
5032
  ] });
5279
5033
  }
5034
+ function SectionPickerList({ sections, onSelect }) {
5035
+ if (sections.length === 0) {
5036
+ return /* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5037
+ }
5038
+ return /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1", children: [
5039
+ /* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5040
+ sections.map((section) => /* @__PURE__ */ jsx14(SectionTreeItem, { section, onSelect }, section.id))
5041
+ ] });
5042
+ }
5280
5043
 
5281
5044
  // src/ui/link-modal/UrlOrPageInput.tsx
5282
- import { useEffect as useEffect3, useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5045
+ import { useId as useId2, useRef as useRef2, useState as useState3 } from "react";
5283
5046
 
5284
5047
  // src/ui/input.tsx
5285
- import * as React8 from "react";
5048
+ import * as React7 from "react";
5286
5049
  import { jsx as jsx15 } from "react/jsx-runtime";
5287
- var Input = React8.forwardRef(
5050
+ var Input = React7.forwardRef(
5288
5051
  ({ className, type, ...props }, ref) => {
5289
5052
  return /* @__PURE__ */ jsx15(
5290
5053
  "input",
@@ -5318,11 +5081,8 @@ function Label({ className, ...props }) {
5318
5081
  }
5319
5082
 
5320
5083
  // src/ui/link-modal/UrlOrPageInput.tsx
5321
- import { ChevronDown, File as File2, X as X2 } from "lucide-react";
5322
5084
  import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
5323
- function FieldChevron({
5324
- onClick
5325
- }) {
5085
+ function FieldChevron({ onClick }) {
5326
5086
  return /* @__PURE__ */ jsx17(
5327
5087
  "button",
5328
5088
  {
@@ -5331,7 +5091,7 @@ function FieldChevron({
5331
5091
  onClick,
5332
5092
  "aria-label": "Open page list",
5333
5093
  tabIndex: -1,
5334
- children: /* @__PURE__ */ jsx17(ChevronDown, { size: 16 })
5094
+ children: /* @__PURE__ */ jsx17(IconChevronDown, {})
5335
5095
  }
5336
5096
  );
5337
5097
  }
@@ -5348,30 +5108,8 @@ function UrlOrPageInput({
5348
5108
  urlError
5349
5109
  }) {
5350
5110
  const inputId = useId2();
5351
- const inputRef = useRef3(null);
5352
- const rootRef = useRef3(null);
5111
+ const inputRef = useRef2(null);
5353
5112
  const [isFocused, setIsFocused] = useState3(false);
5354
- useEffect3(() => {
5355
- if (!dropdownOpen) return;
5356
- const isOutside = (e) => {
5357
- const root = rootRef.current;
5358
- if (!root) return true;
5359
- const path = typeof e.composedPath === "function" ? e.composedPath() : [];
5360
- if (path.includes(root)) return false;
5361
- const target = e.target;
5362
- return !(target instanceof Node && root.contains(target));
5363
- };
5364
- const closeIfOutside = (e) => {
5365
- if (!isOutside(e)) return;
5366
- onDropdownOpenChange(false);
5367
- };
5368
- document.addEventListener("pointerdown", closeIfOutside, true);
5369
- document.addEventListener("mousedown", closeIfOutside, true);
5370
- return () => {
5371
- document.removeEventListener("pointerdown", closeIfOutside, true);
5372
- document.removeEventListener("mousedown", closeIfOutside, true);
5373
- };
5374
- }, [dropdownOpen, onDropdownOpenChange]);
5375
5113
  const openDropdown = () => {
5376
5114
  if (readOnly || filteredPages.length === 0) return;
5377
5115
  onDropdownOpenChange(true);
@@ -5394,16 +5132,9 @@ function UrlOrPageInput({
5394
5132
  );
5395
5133
  return /* @__PURE__ */ jsxs9("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5396
5134
  /* @__PURE__ */ jsx17(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5397
- /* @__PURE__ */ jsxs9("div", { ref: rootRef, className: "relative w-full", children: [
5135
+ /* @__PURE__ */ jsxs9("div", { className: "relative w-full", children: [
5398
5136
  /* @__PURE__ */ jsxs9("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5399
- selectedPage ? /* @__PURE__ */ jsx17("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx17(
5400
- File2,
5401
- {
5402
- size: 16,
5403
- className: "shrink-0 text-foreground",
5404
- "aria-hidden": true
5405
- }
5406
- ) }) : null,
5137
+ selectedPage ? /* @__PURE__ */ jsx17("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx17(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5407
5138
  readOnly ? /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx17(
5408
5139
  Input,
5409
5140
  {
@@ -5415,14 +5146,7 @@ function UrlOrPageInput({
5415
5146
  setIsFocused(true);
5416
5147
  if (!selectedPage) openDropdown();
5417
5148
  },
5418
- onBlur: () => {
5419
- setIsFocused(false);
5420
- requestAnimationFrame(() => {
5421
- if (!rootRef.current?.contains(document.activeElement)) {
5422
- onDropdownOpenChange(false);
5423
- }
5424
- });
5425
- },
5149
+ onBlur: () => setIsFocused(false),
5426
5150
  placeholder,
5427
5151
  className: cn(
5428
5152
  "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",
@@ -5438,7 +5162,7 @@ function UrlOrPageInput({
5438
5162
  onMouseDown: clearSelection,
5439
5163
  "aria-label": "Clear selected page",
5440
5164
  tabIndex: -1,
5441
- children: /* @__PURE__ */ jsx17(X2, { size: 16, "aria-hidden": true })
5165
+ children: /* @__PURE__ */ jsx17(IconX, { "aria-hidden": true })
5442
5166
  }
5443
5167
  ) : null,
5444
5168
  !readOnly ? /* @__PURE__ */ jsx17(FieldChevron, { onClick: toggleDropdown }) : null
@@ -5447,7 +5171,7 @@ function UrlOrPageInput({
5447
5171
  "div",
5448
5172
  {
5449
5173
  "data-ohw-link-page-dropdown": "",
5450
- 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",
5174
+ 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",
5451
5175
  onMouseDown: (e) => e.preventDefault(),
5452
5176
  children: filteredPages.map((page) => /* @__PURE__ */ jsxs9(
5453
5177
  "button",
@@ -5456,7 +5180,7 @@ function UrlOrPageInput({
5456
5180
  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",
5457
5181
  onClick: () => onPageSelect(page),
5458
5182
  children: [
5459
- /* @__PURE__ */ jsx17(File2, { size: 16, className: "shrink-0", "aria-hidden": true }),
5183
+ /* @__PURE__ */ jsx17(IconFile, { className: "shrink-0", "aria-hidden": true }),
5460
5184
  /* @__PURE__ */ jsx17("span", { className: "truncate", children: page.title })
5461
5185
  ]
5462
5186
  },
@@ -5469,348 +5193,8 @@ function UrlOrPageInput({
5469
5193
  ] });
5470
5194
  }
5471
5195
 
5472
- // src/ui/link-modal/LinkEditorPanel.tsx
5473
- import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5474
- function LinkEditorPanel({ state, onClose }) {
5475
- const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5476
- return /* @__PURE__ */ jsxs10(Fragment3, { children: [
5477
- /* @__PURE__ */ jsx18(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx18(
5478
- "button",
5479
- {
5480
- type: "button",
5481
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5482
- "aria-label": "Close",
5483
- onClick: onClose,
5484
- children: /* @__PURE__ */ jsx18(X3, { size: 16, "aria-hidden": true })
5485
- }
5486
- ) }),
5487
- /* @__PURE__ */ jsx18(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx18(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5488
- /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5489
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx18(
5490
- DestinationBreadcrumb,
5491
- {
5492
- pageTitle: state.selectedPage.title,
5493
- sectionLabel: state.selectedSection.label
5494
- }
5495
- ) : /* @__PURE__ */ jsx18(
5496
- UrlOrPageInput,
5497
- {
5498
- value: state.searchValue,
5499
- selectedPage: state.selectedPage,
5500
- dropdownOpen: state.dropdownOpen,
5501
- onDropdownOpenChange: state.setDropdownOpen,
5502
- filteredPages: state.filteredPages,
5503
- onInputChange: state.handleInputChange,
5504
- onPageSelect: state.handlePageSelect,
5505
- urlError: state.urlError
5506
- }
5507
- ),
5508
- state.showChooseSection ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col justify-center gap-2", children: [
5509
- /* @__PURE__ */ jsx18(
5510
- Button,
5511
- {
5512
- type: "button",
5513
- variant: "outline",
5514
- className: "w-fit cursor-pointer",
5515
- size: "sm",
5516
- onClick: state.handleChooseSection,
5517
- children: "Choose a section"
5518
- }
5519
- ),
5520
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5521
- /* @__PURE__ */ jsx18(Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5522
- /* @__PURE__ */ jsx18("span", { children: "Pick a section this link should scroll to." })
5523
- ] })
5524
- ] }) : null,
5525
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx18(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5526
- ] }),
5527
- /* @__PURE__ */ jsxs10(DialogFooter, { className: "w-full px-6 pb-6", children: [
5528
- /* @__PURE__ */ jsx18(
5529
- Button,
5530
- {
5531
- type: "button",
5532
- variant: isCancel ? "outline" : "ghost",
5533
- className: "leading-6 shadow-none cursor-pointer",
5534
- style: isCancel ? {
5535
- backgroundColor: "var(--ohw-background)",
5536
- borderColor: "var(--ohw-border)",
5537
- color: "var(--ohw-foreground)"
5538
- } : void 0,
5539
- onClick: state.handleSecondary,
5540
- children: state.secondaryLabel
5541
- }
5542
- ),
5543
- /* @__PURE__ */ jsx18(
5544
- Button,
5545
- {
5546
- type: "button",
5547
- className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5548
- style: {
5549
- backgroundColor: "var(--ohw-primary)",
5550
- color: "var(--ohw-primary-foreground)"
5551
- },
5552
- disabled: !state.isValid,
5553
- onClick: state.handleSubmit,
5554
- children: state.submitLabel
5555
- }
5556
- )
5557
- ] })
5558
- ] });
5559
- }
5560
-
5561
- // src/ui/link-modal/SectionPickerOverlay.tsx
5562
- import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo2, useState as useState4 } from "react";
5563
- import { createPortal } from "react-dom";
5564
- import { ArrowLeft, Check } from "lucide-react";
5565
- import { usePathname, useRouter } from "next/navigation";
5566
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5567
- var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5568
- function rectsEqual(a, b) {
5569
- if (a.size !== b.size) return false;
5570
- for (const [id, rect] of a) {
5571
- const other = b.get(id);
5572
- if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
5573
- return false;
5574
- }
5575
- }
5576
- return true;
5577
- }
5578
- function useSectionRects(sectionIdsKey, enabled) {
5579
- const [rects, setRects] = useState4(/* @__PURE__ */ new Map());
5580
- const sectionIds = useMemo2(
5581
- () => sectionIdsKey ? sectionIdsKey.split("\0") : [],
5582
- [sectionIdsKey]
5583
- );
5584
- useEffect4(() => {
5585
- if (!enabled || sectionIds.length === 0) {
5586
- setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
5587
- return;
5588
- }
5589
- const update = () => {
5590
- const next = /* @__PURE__ */ new Map();
5591
- for (const id of sectionIds) {
5592
- const el = document.querySelector(
5593
- `[data-ohw-section="${CSS.escape(id)}"]`
5594
- );
5595
- if (el) next.set(id, el.getBoundingClientRect());
5596
- }
5597
- setRects((prev) => rectsEqual(prev, next) ? prev : next);
5598
- };
5599
- update();
5600
- const opts = { capture: true, passive: true };
5601
- window.addEventListener("scroll", update, opts);
5602
- window.addEventListener("resize", update);
5603
- const vv = window.visualViewport;
5604
- vv?.addEventListener("resize", update);
5605
- vv?.addEventListener("scroll", update);
5606
- const ro = new ResizeObserver(update);
5607
- for (const id of sectionIds) {
5608
- const el = document.querySelector(
5609
- `[data-ohw-section="${CSS.escape(id)}"]`
5610
- );
5611
- if (el) ro.observe(el);
5612
- }
5613
- return () => {
5614
- window.removeEventListener("scroll", update, opts);
5615
- window.removeEventListener("resize", update);
5616
- vv?.removeEventListener("resize", update);
5617
- vv?.removeEventListener("scroll", update);
5618
- ro.disconnect();
5619
- };
5620
- }, [sectionIds, enabled]);
5621
- return rects;
5622
- }
5623
- function SectionPickerOverlay({
5624
- pagePath,
5625
- sections,
5626
- onBack,
5627
- onSelect
5628
- }) {
5629
- const router = useRouter();
5630
- const pathname = usePathname();
5631
- const [selectedId, setSelectedId] = useState4(null);
5632
- const [hoveredId, setHoveredId] = useState4(null);
5633
- const [chromeClip, setChromeClip] = useState4(
5634
- () => ({
5635
- top: 0,
5636
- bottom: typeof window !== "undefined" ? window.innerHeight : 0
5637
- })
5638
- );
5639
- const normalizedTarget = normalizePath(pagePath);
5640
- const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
5641
- useEffect4(() => {
5642
- if (isOnTargetPage) return;
5643
- const search = readPreservedSearch();
5644
- router.push(`${normalizedTarget}${search}`);
5645
- }, [isOnTargetPage, normalizedTarget, router]);
5646
- useEffect4(() => {
5647
- document.documentElement.setAttribute("data-ohw-section-picking", "");
5648
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5649
- el.removeAttribute("data-ohw-hovered");
5650
- });
5651
- return () => {
5652
- document.documentElement.removeAttribute("data-ohw-section-picking");
5653
- };
5654
- }, []);
5655
- useEffect4(() => {
5656
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
5657
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
5658
- const bottom = Math.min(
5659
- window.innerHeight,
5660
- visibleCanvasTop + canvasH - iframeOffsetTop
5661
- );
5662
- setChromeClip(
5663
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
5664
- );
5665
- };
5666
- const onParentScroll = (e) => {
5667
- if (e.data?.type !== "ow:parent-scroll") return;
5668
- const { iframeOffsetTop, headerH, canvasH } = e.data;
5669
- applyClip(iframeOffsetTop, headerH, canvasH);
5670
- };
5671
- const onResize = () => {
5672
- setChromeClip((prev) => {
5673
- const bottom = Math.max(prev.top, window.innerHeight);
5674
- return prev.bottom === bottom ? prev : { ...prev, bottom };
5675
- });
5676
- };
5677
- window.addEventListener("message", onParentScroll);
5678
- window.addEventListener("resize", onResize);
5679
- return () => {
5680
- window.removeEventListener("message", onParentScroll);
5681
- window.removeEventListener("resize", onResize);
5682
- };
5683
- }, []);
5684
- const liveSections = useMemo2(() => {
5685
- if (!isOnTargetPage) return sections;
5686
- const live = collectSectionsFromDom();
5687
- if (live.length === 0) return sections;
5688
- const labels = new Map(sections.map((s) => [s.id, s.label]));
5689
- return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5690
- }, [isOnTargetPage, sections, pathname]);
5691
- const sectionIdsKey = useMemo2(
5692
- () => liveSections.map((s) => s.id).join("\0"),
5693
- [liveSections]
5694
- );
5695
- const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5696
- const handleSelect = useCallback2(
5697
- (section) => {
5698
- if (selectedId) return;
5699
- setSelectedId(section.id);
5700
- onSelect(section);
5701
- },
5702
- [selectedId, onSelect]
5703
- );
5704
- useEffect4(() => {
5705
- const onKeyDown = (e) => {
5706
- if (e.key === "Escape") {
5707
- e.preventDefault();
5708
- e.stopPropagation();
5709
- onBack();
5710
- }
5711
- };
5712
- window.addEventListener("keydown", onKeyDown, true);
5713
- return () => window.removeEventListener("keydown", onKeyDown, true);
5714
- }, [onBack]);
5715
- const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5716
- if (!portalRoot) return null;
5717
- return createPortal(
5718
- /* @__PURE__ */ jsxs11(
5719
- "div",
5720
- {
5721
- "data-ohw-section-picker": "",
5722
- "data-ohw-bridge": "",
5723
- className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
5724
- "aria-modal": true,
5725
- role: "dialog",
5726
- "aria-label": "Choose a section",
5727
- children: [
5728
- /* @__PURE__ */ jsx19(
5729
- "div",
5730
- {
5731
- className: "pointer-events-auto fixed left-5 z-[2]",
5732
- style: { top: chromeClip.top + 20 },
5733
- children: /* @__PURE__ */ jsxs11(
5734
- Button,
5735
- {
5736
- type: "button",
5737
- variant: "outline",
5738
- size: "sm",
5739
- className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5740
- onClick: onBack,
5741
- children: [
5742
- /* @__PURE__ */ jsx19(ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5743
- "Back"
5744
- ]
5745
- }
5746
- )
5747
- }
5748
- ),
5749
- /* @__PURE__ */ jsx19(
5750
- "div",
5751
- {
5752
- 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",
5753
- style: {
5754
- top: chromeClip.bottom - 20,
5755
- transform: "translate(-50%, -100%)",
5756
- backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
5757
- },
5758
- "data-ohw-section-picker-hint": "",
5759
- children: "Click on section to select"
5760
- }
5761
- ),
5762
- !isOnTargetPage ? /* @__PURE__ */ jsx19(
5763
- "div",
5764
- {
5765
- 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",
5766
- style: { top: chromeClip.top + 64 },
5767
- children: "Loading page preview\u2026"
5768
- }
5769
- ) : null,
5770
- isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ jsx19("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5771
- isOnTargetPage ? liveSections.map((section) => {
5772
- const rect = rects.get(section.id);
5773
- if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5774
- const isSelected = selectedId === section.id;
5775
- const isLit = isSelected || hoveredId === section.id;
5776
- return /* @__PURE__ */ jsx19(
5777
- "button",
5778
- {
5779
- type: "button",
5780
- className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
5781
- style: {
5782
- top: rect.top,
5783
- left: rect.left,
5784
- width: rect.width,
5785
- height: rect.height,
5786
- backgroundColor: isLit ? "transparent" : DIM_OVERLAY
5787
- },
5788
- "aria-label": `Select section ${section.label}`,
5789
- onMouseEnter: () => setHoveredId(section.id),
5790
- onMouseLeave: () => setHoveredId((prev) => prev === section.id ? null : prev),
5791
- onClick: () => handleSelect(section),
5792
- children: isSelected ? /* @__PURE__ */ jsx19(
5793
- "span",
5794
- {
5795
- className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5796
- style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5797
- "aria-hidden": true,
5798
- children: /* @__PURE__ */ jsx19(Check, { className: "size-5" })
5799
- }
5800
- ) : null
5801
- },
5802
- section.id
5803
- );
5804
- }) : null
5805
- ]
5806
- }
5807
- ),
5808
- portalRoot
5809
- );
5810
- }
5811
-
5812
5196
  // src/ui/link-modal/useLinkModalState.ts
5813
- import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo3, useState as useState5 } from "react";
5197
+ import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
5814
5198
  function useLinkModalState({
5815
5199
  open,
5816
5200
  mode,
@@ -5818,18 +5202,21 @@ function useLinkModalState({
5818
5202
  sections: _sections,
5819
5203
  sectionsByPath,
5820
5204
  initialTarget,
5821
- existingTargets: _existingTargets,
5205
+ existingTargets,
5822
5206
  onClose,
5823
5207
  onSubmit
5824
5208
  }) {
5825
- const availablePages = useMemo3(() => pages, [pages]);
5826
- const [searchValue, setSearchValue] = useState5("");
5827
- const [selectedPage, setSelectedPage] = useState5(null);
5828
- const [selectedSection, setSelectedSection] = useState5(null);
5829
- const [step, setStep] = useState5("input");
5830
- const [dropdownOpen, setDropdownOpen] = useState5(false);
5831
- const [urlError, setUrlError] = useState5("");
5832
- const reset = useCallback3(() => {
5209
+ const availablePages = useMemo2(
5210
+ () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
5211
+ [mode, pages, existingTargets]
5212
+ );
5213
+ const [searchValue, setSearchValue] = useState4("");
5214
+ const [selectedPage, setSelectedPage] = useState4(null);
5215
+ const [selectedSection, setSelectedSection] = useState4(null);
5216
+ const [step, setStep] = useState4("input");
5217
+ const [dropdownOpen, setDropdownOpen] = useState4(false);
5218
+ const [urlError, setUrlError] = useState4("");
5219
+ const reset = useCallback(() => {
5833
5220
  setSearchValue("");
5834
5221
  setSelectedPage(null);
5835
5222
  setSelectedSection(null);
@@ -5837,7 +5224,7 @@ function useLinkModalState({
5837
5224
  setDropdownOpen(false);
5838
5225
  setUrlError("");
5839
5226
  }, []);
5840
- useEffect5(() => {
5227
+ useEffect2(() => {
5841
5228
  if (!open) return;
5842
5229
  if (mode === "edit" && initialTarget) {
5843
5230
  const { pageRoute } = parseTarget(initialTarget);
@@ -5852,12 +5239,12 @@ function useLinkModalState({
5852
5239
  }
5853
5240
  setDropdownOpen(false);
5854
5241
  setUrlError("");
5855
- }, [open, mode, initialTarget, reset]);
5856
- const filteredPages = useMemo3(() => {
5242
+ }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
5243
+ const filteredPages = useMemo2(() => {
5857
5244
  if (!searchValue.trim()) return availablePages;
5858
5245
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
5859
5246
  }, [availablePages, searchValue]);
5860
- const activeSections = useMemo3(() => {
5247
+ const activeSections = useMemo2(() => {
5861
5248
  if (!selectedPage) return [];
5862
5249
  return getSectionsForPath(sectionsByPath, selectedPage.path);
5863
5250
  }, [selectedPage, sectionsByPath]);
@@ -5899,14 +5286,11 @@ function useLinkModalState({
5899
5286
  const handleBackToSections = () => {
5900
5287
  setStep("sectionPicker");
5901
5288
  };
5902
- const handleSectionPickerBack = () => {
5903
- setStep("input");
5904
- };
5905
5289
  const handleClose = () => {
5906
5290
  reset();
5907
5291
  onClose();
5908
5292
  };
5909
- const isValid = useMemo3(() => {
5293
+ const isValid = useMemo2(() => {
5910
5294
  if (urlError) return false;
5911
5295
  if (showBreadcrumb) return true;
5912
5296
  if (selectedPage) return true;
@@ -5939,7 +5323,7 @@ function useLinkModalState({
5939
5323
  onSubmit(normalizeUrl(searchValue));
5940
5324
  handleClose();
5941
5325
  };
5942
- const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
5326
+ const submitLabel = mode === "edit" ? "Save" : "Create";
5943
5327
  const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
5944
5328
  const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
5945
5329
  const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
@@ -5966,29 +5350,23 @@ function useLinkModalState({
5966
5350
  handleChooseSection,
5967
5351
  handleSectionSelect,
5968
5352
  handleBackToSections,
5969
- handleSectionPickerBack,
5970
5353
  handleClose,
5971
5354
  handleSecondary,
5972
5355
  handleSubmit
5973
5356
  };
5974
5357
  }
5975
5358
 
5976
- // src/ui/link-modal/LinkPopover.tsx
5977
- import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
5978
- function postToParent(data) {
5979
- window.parent?.postMessage(data, "*");
5980
- }
5981
- function LinkPopover({
5359
+ // src/ui/link-modal/LinkEditorPanel.tsx
5360
+ import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5361
+ function LinkEditorPanel({
5982
5362
  open = true,
5983
- panelRef,
5984
- portalContainer,
5985
- onClose,
5986
5363
  mode = "create",
5987
5364
  pages,
5988
5365
  sections = [],
5989
5366
  sectionsByPath,
5990
5367
  initialTarget,
5991
5368
  existingTargets = [],
5369
+ onClose,
5992
5370
  onSubmit
5993
5371
  }) {
5994
5372
  const state = useLinkModalState({
@@ -6002,100 +5380,115 @@ function LinkPopover({
6002
5380
  onClose,
6003
5381
  onSubmit
6004
5382
  });
6005
- const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
6006
- useEffect6(() => {
6007
- if (!open) return;
6008
- if (sectionPickerActive) {
6009
- postToParent({ type: "ow:link-modal-lock", locked: false });
6010
- postToParent({ type: "ow:section-picker", active: true });
6011
- document.documentElement.style.overflow = "";
6012
- document.body.style.overflow = "";
6013
- return () => {
6014
- postToParent({ type: "ow:section-picker", active: false });
6015
- };
6016
- }
6017
- postToParent({ type: "ow:section-picker", active: false });
6018
- postToParent({ type: "ow:link-modal-lock", locked: true });
6019
- const html = document.documentElement;
6020
- const body = document.body;
6021
- const prevHtmlOverflow = html.style.overflow;
6022
- const prevBodyOverflow = body.style.overflow;
6023
- const prevHtmlOverscroll = html.style.overscrollBehavior;
6024
- const prevBodyOverscroll = body.style.overscrollBehavior;
6025
- html.style.overflow = "hidden";
6026
- body.style.overflow = "hidden";
6027
- html.style.overscrollBehavior = "none";
6028
- body.style.overscrollBehavior = "none";
6029
- const canScrollElement = (el, deltaY) => {
6030
- const { overflowY } = getComputedStyle(el);
6031
- if (overflowY !== "auto" && overflowY !== "scroll") return false;
6032
- if (el.scrollHeight <= el.clientHeight + 1) return false;
6033
- if (deltaY < 0) return el.scrollTop > 0;
6034
- if (deltaY > 0) return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6035
- return true;
6036
- };
6037
- const allowsInternalScroll = (e) => {
6038
- const target = e.target;
6039
- if (!(target instanceof Element)) return false;
6040
- const scrollRoot = target.closest(
6041
- "[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
6042
- );
6043
- if (!scrollRoot) return false;
6044
- const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
6045
- return canScrollElement(scrollRoot, deltaY);
6046
- };
6047
- const preventBackgroundScroll = (e) => {
6048
- if (allowsInternalScroll(e)) return;
6049
- e.preventDefault();
6050
- };
6051
- const scrollOpts = { passive: false, capture: true };
6052
- document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6053
- document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6054
- return () => {
6055
- postToParent({ type: "ow:link-modal-lock", locked: false });
6056
- html.style.overflow = prevHtmlOverflow;
6057
- body.style.overflow = prevBodyOverflow;
6058
- html.style.overscrollBehavior = prevHtmlOverscroll;
6059
- body.style.overscrollBehavior = prevBodyOverscroll;
6060
- document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6061
- document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6062
- };
6063
- }, [open, sectionPickerActive]);
6064
- return /* @__PURE__ */ jsxs12(Fragment4, { children: [
6065
- /* @__PURE__ */ jsx20(
6066
- Dialog2,
6067
- {
6068
- open: open && !sectionPickerActive,
6069
- onOpenChange: (next) => {
6070
- if (!next) onClose?.();
6071
- },
6072
- children: /* @__PURE__ */ jsx20(
6073
- DialogContent,
6074
- {
6075
- ref: panelRef,
6076
- container: portalContainer,
6077
- "data-ohw-link-popover-root": "",
6078
- "data-ohw-link-modal-root": "",
6079
- "data-ohw-bridge": "",
6080
- showCloseButton: false,
6081
- className: "gap-0 p-0 w-full md:w-md pointer-events-auto z-[2147483646] overflow-visible",
6082
- children: /* @__PURE__ */ jsx20(LinkEditorPanel, { state, onClose })
6083
- }
6084
- )
6085
- }
6086
- ),
6087
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ jsx20(
6088
- SectionPickerOverlay,
5383
+ const isCancel = state.secondaryLabel === "Cancel";
5384
+ return /* @__PURE__ */ jsxs10(Fragment2, { children: [
5385
+ /* @__PURE__ */ jsx18(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx18(
5386
+ "button",
6089
5387
  {
6090
- pagePath: state.selectedPage.path,
6091
- sections: state.activeSections,
6092
- onBack: state.handleSectionPickerBack,
6093
- onSelect: state.handleSectionSelect
5388
+ type: "button",
5389
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5390
+ "aria-label": "Close",
5391
+ children: /* @__PURE__ */ jsx18(IconX, { "aria-hidden": true })
6094
5392
  }
6095
- ) : null
5393
+ ) }),
5394
+ /* @__PURE__ */ jsx18(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx18(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5395
+ /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5396
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx18(
5397
+ DestinationBreadcrumb,
5398
+ {
5399
+ pageTitle: state.selectedPage.title,
5400
+ sectionLabel: state.selectedSection.label
5401
+ }
5402
+ ) : /* @__PURE__ */ jsx18(
5403
+ UrlOrPageInput,
5404
+ {
5405
+ value: state.searchValue,
5406
+ selectedPage: state.selectedPage,
5407
+ dropdownOpen: state.dropdownOpen,
5408
+ onDropdownOpenChange: state.setDropdownOpen,
5409
+ filteredPages: state.filteredPages,
5410
+ onInputChange: state.handleInputChange,
5411
+ onPageSelect: state.handlePageSelect,
5412
+ urlError: state.urlError
5413
+ }
5414
+ ),
5415
+ state.showChooseSection ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col justify-center gap-2", children: [
5416
+ /* @__PURE__ */ jsx18(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5417
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5418
+ /* @__PURE__ */ jsx18(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5419
+ /* @__PURE__ */ jsx18("span", { children: "Pick a section this link should scroll to." })
5420
+ ] })
5421
+ ] }) : null,
5422
+ state.showSectionPicker ? /* @__PURE__ */ jsx18(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5423
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx18(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5424
+ ] }),
5425
+ /* @__PURE__ */ jsxs10(DialogFooter, { className: "w-full px-6 pb-6", children: [
5426
+ /* @__PURE__ */ jsx18(
5427
+ Button,
5428
+ {
5429
+ type: "button",
5430
+ variant: isCancel ? "outline" : "ghost",
5431
+ className: "leading-6 shadow-none cursor-pointer",
5432
+ style: isCancel ? {
5433
+ backgroundColor: "var(--ohw-background)",
5434
+ borderColor: "var(--ohw-border)",
5435
+ color: "var(--ohw-foreground)"
5436
+ } : void 0,
5437
+ onClick: state.handleSecondary,
5438
+ children: state.secondaryLabel
5439
+ }
5440
+ ),
5441
+ /* @__PURE__ */ jsx18(
5442
+ Button,
5443
+ {
5444
+ type: "button",
5445
+ className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5446
+ style: {
5447
+ backgroundColor: "var(--ohw-primary)",
5448
+ color: "var(--ohw-primary-foreground)"
5449
+ },
5450
+ disabled: !state.isValid,
5451
+ onClick: state.handleSubmit,
5452
+ children: state.submitLabel
5453
+ }
5454
+ )
5455
+ ] })
6096
5456
  ] });
6097
5457
  }
6098
5458
 
5459
+ // src/ui/link-modal/LinkPopover.tsx
5460
+ import { jsx as jsx19 } from "react/jsx-runtime";
5461
+ function LinkPopover({
5462
+ open = true,
5463
+ panelRef,
5464
+ portalContainer,
5465
+ onClose,
5466
+ ...editorProps
5467
+ }) {
5468
+ return /* @__PURE__ */ jsx19(
5469
+ Dialog2,
5470
+ {
5471
+ open,
5472
+ onOpenChange: (next) => {
5473
+ if (!next) onClose?.();
5474
+ },
5475
+ children: /* @__PURE__ */ jsx19(
5476
+ DialogContent,
5477
+ {
5478
+ ref: panelRef,
5479
+ container: portalContainer,
5480
+ "data-ohw-link-popover-root": "",
5481
+ "data-ohw-link-modal-root": "",
5482
+ "data-ohw-bridge": "",
5483
+ showCloseButton: false,
5484
+ className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5485
+ children: /* @__PURE__ */ jsx19(LinkEditorPanel, { ...editorProps, open, onClose })
5486
+ }
5487
+ )
5488
+ }
5489
+ );
5490
+ }
5491
+
6099
5492
  // src/ui/link-modal/devFixtures.ts
6100
5493
  var DEV_SITE_PAGES = [
6101
5494
  { path: "/", title: "Home" },
@@ -6115,21 +5508,16 @@ var DEV_SECTIONS_BY_PATH = {
6115
5508
  { id: "founder-teaser", label: "Founder" },
6116
5509
  { id: "plan-form", label: "Plan Form" },
6117
5510
  { id: "testimonials", label: "Testimonials" },
6118
- { id: "wordmark", label: "Wordmark" },
6119
- { id: "footer", label: "Footer" }
5511
+ { id: "wordmark", label: "Wordmark" }
6120
5512
  ],
6121
5513
  "/about": [
6122
5514
  { id: "manifesto", label: "Manifesto" },
6123
5515
  { id: "story-letter", label: "Our Story" },
6124
5516
  { id: "lagree-explainer", label: "Lagree Explainer" },
6125
5517
  { id: "waitlist-cta", label: "Waitlist" },
6126
- { id: "personal-training", label: "Personal training" },
6127
- { id: "footer", label: "Footer" }
6128
- ],
6129
- "/classes": [
6130
- { id: "class-library", label: "Class Library" },
6131
- { id: "footer", label: "Footer" }
5518
+ { id: "personal-training", label: "Personal training" }
6132
5519
  ],
5520
+ "/classes": [{ id: "class-library", label: "Class Library" }],
6133
5521
  "/pricing": [
6134
5522
  { id: "page-header", label: "Page Header" },
6135
5523
  { id: "free-class-cta", label: "Free Class" },
@@ -6137,25 +5525,19 @@ var DEV_SECTIONS_BY_PATH = {
6137
5525
  { id: "monthly-memberships", label: "Memberships" },
6138
5526
  { id: "specialty-programs", label: "Specialty Programs" },
6139
5527
  { id: "package-matcher", label: "Packages" },
6140
- { id: "wordmark", label: "Wordmark" },
6141
- { id: "footer", label: "Footer" }
5528
+ { id: "wordmark", label: "Wordmark" }
6142
5529
  ],
6143
5530
  "/studio": [
6144
5531
  { id: "studio-intro", label: "Studio Intro" },
6145
5532
  { id: "studio-features", label: "Studio Features" },
6146
5533
  { id: "studio-gallery", label: "Studio Gallery" },
6147
- { id: "studio-visit", label: "Visit Studio" },
6148
- { id: "footer", label: "Footer" }
6149
- ],
6150
- "/instructors": [
6151
- { id: "instructor-grid", label: "Instructors" },
6152
- { id: "footer", label: "Footer" }
5534
+ { id: "studio-visit", label: "Visit Studio" }
6153
5535
  ],
5536
+ "/instructors": [{ id: "instructor-grid", label: "Instructors" }],
6154
5537
  "/contact": [
6155
5538
  { id: "hello-hero", label: "Hello" },
6156
5539
  { id: "contact-form", label: "Contact Form" },
6157
- { id: "faq", label: "FAQ" },
6158
- { id: "footer", label: "Footer" }
5540
+ { id: "faq", label: "FAQ" }
6159
5541
  ]
6160
5542
  };
6161
5543
  function shouldUseDevFixtures() {
@@ -6165,277 +5547,8 @@ function shouldUseDevFixtures() {
6165
5547
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6166
5548
  }
6167
5549
 
6168
- // src/lib/nav-items.ts
6169
- var NAV_COUNT_KEY = "__ohw_nav_count";
6170
- var NAV_ORDER_KEY = "__ohw_nav_order";
6171
- function getLinkHref(el) {
6172
- const key = el.getAttribute("data-ohw-href-key");
6173
- if (key) {
6174
- const stored = getStoredLinkHref(key);
6175
- if (stored !== void 0) return stored;
6176
- }
6177
- return el.getAttribute("href") ?? "";
6178
- }
6179
- function isNavbarButton(el) {
6180
- return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
6181
- }
6182
- function isNavbarLinkItem(el) {
6183
- if (!el.matches("[data-ohw-href-key]")) return false;
6184
- if (isNavbarButton(el)) return false;
6185
- if (!el.querySelector('[data-ohw-editable="text"]')) return false;
6186
- return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
6187
- }
6188
- function getNavbarDesktopContainer() {
6189
- return document.querySelector("[data-ohw-nav-container]");
6190
- }
6191
- function getNavbarDrawerContainer() {
6192
- return document.querySelector("[data-ohw-nav-drawer]");
6193
- }
6194
- function listNavbarItems() {
6195
- const desktop = getNavbarDesktopContainer();
6196
- if (desktop) {
6197
- return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6198
- }
6199
- const drawer = getNavbarDrawerContainer();
6200
- if (drawer) {
6201
- return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6202
- }
6203
- return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
6204
- }
6205
- function parseNavIndexFromKey(key) {
6206
- if (!key) return null;
6207
- const match = key.match(/^nav-(\d+)-href$/);
6208
- if (!match) return null;
6209
- return parseInt(match[1], 10);
6210
- }
6211
- function getNavbarIndicesInDom() {
6212
- const indices = /* @__PURE__ */ new Set();
6213
- for (const item of listNavbarItems()) {
6214
- const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
6215
- if (idx !== null) indices.add(idx);
6216
- }
6217
- return [...indices].sort((a, b) => a - b);
6218
- }
6219
- function getNextNavbarIndex() {
6220
- const indices = getNavbarIndicesInDom();
6221
- if (indices.length === 0) return 0;
6222
- return Math.max(...indices) + 1;
6223
- }
6224
- function getNavbarExistingTargets() {
6225
- return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
6226
- }
6227
- function getNavOrderFromDom() {
6228
- return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6229
- }
6230
- function parseNavOrder(content) {
6231
- const raw = content[NAV_ORDER_KEY];
6232
- if (!raw) return null;
6233
- try {
6234
- const parsed = JSON.parse(raw);
6235
- if (!Array.isArray(parsed)) return null;
6236
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6237
- } catch {
6238
- return null;
6239
- }
6240
- }
6241
- function findCounterpartByHrefKey(hrefKey, root) {
6242
- return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6243
- }
6244
- function cloneNavLinkShell(template) {
6245
- const anchor = document.createElement("a");
6246
- if (template) {
6247
- anchor.style.cssText = template.style.cssText;
6248
- if (template.className) anchor.className = template.className;
6249
- }
6250
- anchor.style.cursor = "pointer";
6251
- anchor.style.textDecoration = "none";
6252
- return anchor;
6253
- }
6254
- function buildNavLink(index, href, label, template) {
6255
- const anchor = cloneNavLinkShell(template);
6256
- anchor.href = href;
6257
- anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
6258
- const span = document.createElement("span");
6259
- span.setAttribute("data-ohw-editable", "text");
6260
- span.setAttribute("data-ohw-key", `nav-${index}-label`);
6261
- span.textContent = label;
6262
- anchor.appendChild(span);
6263
- return anchor;
6264
- }
6265
- function insertNavLinkInContainer(container, anchor, afterHrefKey) {
6266
- if (!afterHrefKey) {
6267
- container.appendChild(anchor);
6268
- return;
6269
- }
6270
- const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
6271
- if (afterEl?.parentElement === container) {
6272
- afterEl.insertAdjacentElement("afterend", anchor);
6273
- return;
6274
- }
6275
- container.appendChild(anchor);
6276
- }
6277
- function insertNavbarItemDom(index, href, label, afterAnchor) {
6278
- const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
6279
- const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6280
- const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6281
- const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
6282
- const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
6283
- let primary = null;
6284
- const desktopContainer = getNavbarDesktopContainer();
6285
- if (desktopContainer) {
6286
- const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
6287
- insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
6288
- primary = desktopAnchor;
6289
- }
6290
- const drawerContainer = getNavbarDrawerContainer();
6291
- if (drawerContainer) {
6292
- const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
6293
- insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
6294
- if (!primary) primary = drawerAnchor;
6295
- }
6296
- if (!primary) {
6297
- const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
6298
- const nav = document.querySelector("nav");
6299
- nav?.appendChild(fallback);
6300
- primary = fallback;
6301
- }
6302
- return primary;
6303
- }
6304
- function applyNavOrderToContainer(container, order) {
6305
- const seen = /* @__PURE__ */ new Set();
6306
- for (const hrefKey of order) {
6307
- if (seen.has(hrefKey)) continue;
6308
- seen.add(hrefKey);
6309
- const el = findCounterpartByHrefKey(hrefKey, container);
6310
- if (el) container.appendChild(el);
6311
- }
6312
- for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6313
- if (!isNavbarLinkItem(el)) continue;
6314
- const key = el.getAttribute("data-ohw-href-key");
6315
- if (!key || seen.has(key)) continue;
6316
- container.appendChild(el);
6317
- }
6318
- }
6319
- function applyNavOrder(order) {
6320
- const desktop = getNavbarDesktopContainer();
6321
- if (desktop) applyNavOrderToContainer(desktop, order);
6322
- const drawer = getNavbarDrawerContainer();
6323
- if (drawer) applyNavOrderToContainer(drawer, order);
6324
- }
6325
- function collectNavbarIndicesFromContent(content) {
6326
- const indices = /* @__PURE__ */ new Set();
6327
- for (const key of Object.keys(content)) {
6328
- const idx = parseNavIndexFromKey(key);
6329
- if (idx !== null) indices.add(idx);
6330
- }
6331
- const countRaw = content[NAV_COUNT_KEY];
6332
- if (countRaw) {
6333
- const count = parseInt(countRaw, 10);
6334
- if (Number.isFinite(count) && count > 0) {
6335
- for (let i = 0; i < count; i++) indices.add(i);
6336
- }
6337
- }
6338
- return [...indices].sort((a, b) => a - b);
6339
- }
6340
- function navbarIndexExistsInDom(index) {
6341
- return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
6342
- }
6343
- function reconcileNavbarItemsFromContent(content) {
6344
- const indices = collectNavbarIndicesFromContent(content);
6345
- for (const index of indices) {
6346
- if (navbarIndexExistsInDom(index)) continue;
6347
- const href = content[`nav-${index}-href`];
6348
- const label = content[`nav-${index}-label`];
6349
- if (!href && !label) continue;
6350
- insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6351
- }
6352
- const order = parseNavOrder(content);
6353
- if (order?.length) applyNavOrder(order);
6354
- }
6355
- function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6356
- const order = getNavOrderFromDom();
6357
- if (!afterAnchor) {
6358
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6359
- return order;
6360
- }
6361
- const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
6362
- if (!afterKey) {
6363
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6364
- return order;
6365
- }
6366
- const withoutNew = order.filter((key) => key !== newHrefKey);
6367
- const pos = withoutNew.indexOf(afterKey);
6368
- if (pos >= 0) {
6369
- withoutNew.splice(pos + 1, 0, newHrefKey);
6370
- return withoutNew;
6371
- }
6372
- withoutNew.push(newHrefKey);
6373
- return withoutNew;
6374
- }
6375
- function insertNavbarItem(href, label, afterAnchor = null) {
6376
- const index = getNextNavbarIndex();
6377
- const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
6378
- if (!anchor) throw new Error("Failed to insert navbar item");
6379
- const hrefKey = `nav-${index}-href`;
6380
- const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
6381
- applyNavOrder(order);
6382
- return {
6383
- anchor,
6384
- index,
6385
- hrefKey,
6386
- labelKey: `nav-${index}-label`,
6387
- href,
6388
- label,
6389
- order
6390
- };
6391
- }
6392
-
6393
- // src/ui/navbar-container-chrome.tsx
6394
- import { Plus as Plus2 } from "lucide-react";
6395
- import { jsx as jsx21 } from "react/jsx-runtime";
6396
- function NavbarContainerChrome({
6397
- rect,
6398
- onAdd
6399
- }) {
6400
- const chromeGap = 6;
6401
- return /* @__PURE__ */ jsx21(
6402
- "div",
6403
- {
6404
- "data-ohw-navbar-container-chrome": "",
6405
- "data-ohw-bridge": "",
6406
- className: "pointer-events-none fixed z-[2147483647]",
6407
- style: {
6408
- top: rect.top - chromeGap,
6409
- left: rect.left - chromeGap,
6410
- width: rect.width + chromeGap * 2,
6411
- height: rect.height + chromeGap * 2
6412
- },
6413
- children: /* @__PURE__ */ jsx21(
6414
- "button",
6415
- {
6416
- type: "button",
6417
- "data-ohw-navbar-add-button": "",
6418
- 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",
6419
- style: { top: "70%" },
6420
- "aria-label": "Add item",
6421
- onMouseDown: (e) => {
6422
- e.preventDefault();
6423
- e.stopPropagation();
6424
- },
6425
- onClick: (e) => {
6426
- e.preventDefault();
6427
- e.stopPropagation();
6428
- onAdd();
6429
- },
6430
- children: /* @__PURE__ */ jsx21(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6431
- }
6432
- )
6433
- }
6434
- );
6435
- }
6436
-
6437
5550
  // src/ui/badge.tsx
6438
- import { jsx as jsx22 } from "react/jsx-runtime";
5551
+ import { jsx as jsx20 } from "react/jsx-runtime";
6439
5552
  var badgeVariants = cva(
6440
5553
  "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",
6441
5554
  {
@@ -6453,12 +5566,12 @@ var badgeVariants = cva(
6453
5566
  }
6454
5567
  );
6455
5568
  function Badge({ className, variant, ...props }) {
6456
- return /* @__PURE__ */ jsx22("div", { className: cn(badgeVariants({ variant }), className), ...props });
5569
+ return /* @__PURE__ */ jsx20("div", { className: cn(badgeVariants({ variant }), className), ...props });
6457
5570
  }
6458
5571
 
6459
5572
  // src/OhhwellsBridge.tsx
6460
5573
  import { Link as Link2 } from "lucide-react";
6461
- import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
5574
+ import { Fragment as Fragment3, jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
6462
5575
  var PRIMARY2 = "#0885FE";
6463
5576
  var IMAGE_FADE_MS = 300;
6464
5577
  function runOpacityFade(el, onDone) {
@@ -6637,7 +5750,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6637
5750
  const root = createRoot(container);
6638
5751
  flushSync(() => {
6639
5752
  root.render(
6640
- /* @__PURE__ */ jsx23(
5753
+ /* @__PURE__ */ jsx21(
6641
5754
  SchedulingWidget,
6642
5755
  {
6643
5756
  notifyOnConnect,
@@ -6677,7 +5790,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
6677
5790
  }
6678
5791
  }
6679
5792
  }
6680
- function getLinkHref2(el) {
5793
+ function getLinkHref(el) {
6681
5794
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
6682
5795
  return anchor?.getAttribute("href") ?? "";
6683
5796
  }
@@ -6710,11 +5823,8 @@ function collectEditableNodes() {
6710
5823
  const url = raw.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "");
6711
5824
  return { key: el.dataset.ohwKey ?? "", type: "bg-image", text: url };
6712
5825
  }
6713
- if (el.dataset.ohwEditable === "video") {
6714
- return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
6715
- }
6716
5826
  if (el.dataset.ohwEditable === "link") {
6717
- return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
5827
+ return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
6718
5828
  }
6719
5829
  return {
6720
5830
  key: el.dataset.ohwKey ?? "",
@@ -6723,65 +5833,11 @@ function collectEditableNodes() {
6723
5833
  };
6724
5834
  });
6725
5835
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
6726
- const key = el.getAttribute("data-ohw-href-key") ?? "";
6727
- if (!key) return;
6728
- nodes.push({ key, type: "link", text: getLinkHref2(el) });
6729
- });
6730
- document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6731
- const key = el.dataset.ohwKey ?? "";
6732
- const video = getVideoEl(el);
6733
- if (!key || !video) return;
6734
- nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
6735
- nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
6736
- });
6737
- return nodes;
6738
- }
6739
- function isMediaEditable(el) {
6740
- const t = el.dataset.ohwEditable;
6741
- return t === "image" || t === "bg-image" || t === "video";
6742
- }
6743
- var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
6744
- var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"])';
6745
- function getVideoEl(el) {
6746
- return el instanceof HTMLVideoElement ? el : el.querySelector("video");
6747
- }
6748
- var VIDEO_AUTOPLAY_SUFFIX = "__ohw_autoplay";
6749
- var VIDEO_MUTED_SUFFIX = "__ohw_muted";
6750
- function isBackgroundVideo(video) {
6751
- return video.autoplay && video.muted;
6752
- }
6753
- function syncVideoPlayback(video) {
6754
- const background = isBackgroundVideo(video);
6755
- video.controls = !background;
6756
- if (video.autoplay) void video.play().catch(() => {
6757
- });
6758
- else video.pause();
6759
- }
6760
- function applyVideoSrc(video, url) {
6761
- const { autoplay, muted } = video;
6762
- video.src = url;
6763
- video.loop = true;
6764
- video.playsInline = true;
6765
- video.autoplay = autoplay;
6766
- video.muted = muted;
6767
- video.load();
6768
- syncVideoPlayback(video);
6769
- }
6770
- function applyVideoSettingNode(key, val) {
6771
- const isAutoplay = key.endsWith(VIDEO_AUTOPLAY_SUFFIX);
6772
- const isMuted = key.endsWith(VIDEO_MUTED_SUFFIX);
6773
- if (!isAutoplay && !isMuted) return false;
6774
- const suffixLen = (isAutoplay ? VIDEO_AUTOPLAY_SUFFIX : VIDEO_MUTED_SUFFIX).length;
6775
- const baseKey = key.slice(0, key.length - suffixLen);
6776
- const on = val === "true";
6777
- document.querySelectorAll(`[data-ohw-key="${baseKey}"]`).forEach((el) => {
6778
- const video = getVideoEl(el);
6779
- if (!video) return;
6780
- if (isAutoplay) video.autoplay = on;
6781
- else video.muted = on;
6782
- syncVideoPlayback(video);
5836
+ const key = el.getAttribute("data-ohw-href-key") ?? "";
5837
+ if (!key) return;
5838
+ nodes.push({ key, type: "link", text: getLinkHref(el) });
6783
5839
  });
6784
- return true;
5840
+ return nodes;
6785
5841
  }
6786
5842
  function applyLinkByKey(key, val) {
6787
5843
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -6795,7 +5851,7 @@ function applyLinkByKey(key, val) {
6795
5851
  }
6796
5852
  function isInsideLinkEditor(target) {
6797
5853
  return Boolean(
6798
- 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"]')
5854
+ 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"]')
6799
5855
  );
6800
5856
  }
6801
5857
  function getHrefKeyFromElement(el) {
@@ -6806,7 +5862,7 @@ function getHrefKeyFromElement(el) {
6806
5862
  if (!key) return null;
6807
5863
  return { anchor, key };
6808
5864
  }
6809
- function isNavbarButton2(el) {
5865
+ function isNavbarButton(el) {
6810
5866
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
6811
5867
  }
6812
5868
  function getNavigationItemAnchor(el) {
@@ -6818,8 +5874,19 @@ function getNavigationItemAnchor(el) {
6818
5874
  function isNavigationItem(el) {
6819
5875
  return getNavigationItemAnchor(el) !== null;
6820
5876
  }
5877
+ function getNavigationItemAddHintTarget(anchor) {
5878
+ const items = listNavigationItems();
5879
+ const idx = items.indexOf(anchor);
5880
+ if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
5881
+ return anchor;
5882
+ }
5883
+ function listNavigationItems() {
5884
+ return Array.from(
5885
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
5886
+ ).filter((el) => isNavigationItem(el));
5887
+ }
6821
5888
  function getNavigationItemReorderState(anchor) {
6822
- if (isNavbarButton2(anchor)) return { key: null, disabled: false };
5889
+ if (isNavbarButton(anchor)) return { key: null, disabled: false };
6823
5890
  return getReorderHandleStateFromAnchor(anchor);
6824
5891
  }
6825
5892
  function getReorderHandleState(el) {
@@ -6841,120 +5908,6 @@ function clearHrefKeyHover(anchor) {
6841
5908
  function isInsideNavigationItem(el) {
6842
5909
  return getNavigationItemAnchor(el) !== null;
6843
5910
  }
6844
- function getNavigationRoot(el) {
6845
- const explicit = el.closest("[data-ohw-nav-root]");
6846
- if (explicit) return explicit;
6847
- return el.closest("nav, footer, aside");
6848
- }
6849
- function findFooterItemGroup(item) {
6850
- const footer = item.closest("footer");
6851
- if (!footer) return null;
6852
- let node = item.parentElement;
6853
- while (node && node !== footer) {
6854
- const hasDirectNavItems = Array.from(
6855
- node.querySelectorAll(":scope > [data-ohw-href-key]")
6856
- ).some(isNavigationItem);
6857
- if (hasDirectNavItems) return node;
6858
- node = node.parentElement;
6859
- }
6860
- return null;
6861
- }
6862
- function isNavigationRoot(el) {
6863
- return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
6864
- }
6865
- function isInferredFooterGroup(el) {
6866
- const footer = el.closest("footer");
6867
- if (!footer || el === footer) return false;
6868
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
6869
- }
6870
- function isNavigationContainer(el) {
6871
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
6872
- }
6873
- function isPointOverNavigation(x, y) {
6874
- const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
6875
- if (!navRoot) return false;
6876
- const r2 = navRoot.getBoundingClientRect();
6877
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
6878
- }
6879
- function isPointOverNavItem(container, x, y) {
6880
- return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
6881
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
6882
- const itemRect = el.getBoundingClientRect();
6883
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
6884
- });
6885
- }
6886
- function resolveNavContainerSelectionTarget(target, clientX, clientY) {
6887
- if (getNavigationItemAnchor(target)) return null;
6888
- if (target.closest('[data-ohw-role="navbar-button"]')) return null;
6889
- const plainLink = target.closest("a");
6890
- if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
6891
- return null;
6892
- }
6893
- const fromClosest = target.closest("[data-ohw-nav-container]");
6894
- if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
6895
- return fromClosest;
6896
- }
6897
- const navRoot = target.closest("[data-ohw-nav-root]");
6898
- if (!navRoot) return null;
6899
- const container = navRoot.querySelector("[data-ohw-nav-container]");
6900
- if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
6901
- if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
6902
- return container;
6903
- }
6904
- return null;
6905
- }
6906
- function getNavigationSelectionParent(el) {
6907
- if (isNavigationItem(el)) {
6908
- const explicit = el.closest("[data-ohw-nav-container]");
6909
- if (explicit) return explicit;
6910
- const footerGroup = findFooterItemGroup(el);
6911
- if (footerGroup) return footerGroup;
6912
- return getNavigationRoot(el);
6913
- }
6914
- if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
6915
- return getNavigationRoot(el);
6916
- }
6917
- return null;
6918
- }
6919
- function placeCaretAtPoint(el, x, y) {
6920
- const selection = window.getSelection();
6921
- if (!selection) return;
6922
- if (typeof document.caretRangeFromPoint === "function") {
6923
- const range = document.caretRangeFromPoint(x, y);
6924
- if (range && el.contains(range.startContainer)) {
6925
- selection.removeAllRanges();
6926
- selection.addRange(range);
6927
- return;
6928
- }
6929
- }
6930
- const caretPositionFromPoint = document.caretPositionFromPoint;
6931
- if (typeof caretPositionFromPoint === "function") {
6932
- const position = caretPositionFromPoint(x, y);
6933
- if (position && el.contains(position.offsetNode)) {
6934
- const range = document.createRange();
6935
- range.setStart(position.offsetNode, position.offset);
6936
- range.collapse(true);
6937
- selection.removeAllRanges();
6938
- selection.addRange(range);
6939
- }
6940
- }
6941
- }
6942
- function selectAllTextInEditable(el) {
6943
- const selection = window.getSelection();
6944
- if (!selection) return;
6945
- const range = document.createRange();
6946
- range.selectNodeContents(el);
6947
- selection.removeAllRanges();
6948
- selection.addRange(range);
6949
- }
6950
- function getNavigationLabelEditable(target) {
6951
- const editable = target.closest('[data-ohw-editable="text"]');
6952
- if (!editable) return null;
6953
- const hrefCtx = getHrefKeyFromElement(editable);
6954
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
6955
- if (!navAnchor) return null;
6956
- return { editable, navAnchor };
6957
- }
6958
5911
  function collectSections() {
6959
5912
  return collectSectionsFromDom();
6960
5913
  }
@@ -7078,8 +6031,6 @@ var ICONS = {
7078
6031
  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"/>',
7079
6032
  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"/>'
7080
6033
  };
7081
- var TOOLBAR_PILL_PADDING = 2;
7082
- var TOOLBAR_TOGGLE_GAP = 4;
7083
6034
  var TOOLBAR_GROUPS = [
7084
6035
  [
7085
6036
  { cmd: "bold", title: "Bold" },
@@ -7104,7 +6055,7 @@ function EditGlowChrome({
7104
6055
  dragDisabled = false
7105
6056
  }) {
7106
6057
  const GAP = 6;
7107
- return /* @__PURE__ */ jsxs13(
6058
+ return /* @__PURE__ */ jsxs11(
7108
6059
  "div",
7109
6060
  {
7110
6061
  ref: elRef,
@@ -7119,7 +6070,7 @@ function EditGlowChrome({
7119
6070
  zIndex: 2147483646
7120
6071
  },
7121
6072
  children: [
7122
- /* @__PURE__ */ jsx23(
6073
+ /* @__PURE__ */ jsx21(
7123
6074
  "div",
7124
6075
  {
7125
6076
  style: {
@@ -7132,7 +6083,7 @@ function EditGlowChrome({
7132
6083
  }
7133
6084
  }
7134
6085
  ),
7135
- reorderHrefKey && /* @__PURE__ */ jsx23(
6086
+ reorderHrefKey && /* @__PURE__ */ jsx21(
7136
6087
  "div",
7137
6088
  {
7138
6089
  "data-ohw-drag-handle-container": "",
@@ -7144,7 +6095,7 @@ function EditGlowChrome({
7144
6095
  transform: "translate(calc(-100% - 7px), -50%)",
7145
6096
  pointerEvents: dragDisabled ? "none" : "auto"
7146
6097
  },
7147
- children: /* @__PURE__ */ jsx23(
6098
+ children: /* @__PURE__ */ jsx21(
7148
6099
  DragHandle,
7149
6100
  {
7150
6101
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7248,7 +6199,7 @@ function FloatingToolbar({
7248
6199
  onEditLink
7249
6200
  }) {
7250
6201
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7251
- return /* @__PURE__ */ jsx23(
6202
+ return /* @__PURE__ */ jsx21(
7252
6203
  "div",
7253
6204
  {
7254
6205
  ref: elRef,
@@ -7258,23 +6209,14 @@ function FloatingToolbar({
7258
6209
  left,
7259
6210
  transform,
7260
6211
  zIndex: 2147483647,
7261
- background: "#fff",
7262
- border: "1px solid #E7E5E4",
7263
- borderRadius: 6,
7264
- boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
7265
- display: "flex",
7266
- alignItems: "center",
7267
- padding: TOOLBAR_PILL_PADDING,
7268
- gap: TOOLBAR_TOGGLE_GAP,
7269
- fontFamily: "sans-serif",
7270
6212
  pointerEvents: "auto"
7271
6213
  },
7272
- children: /* @__PURE__ */ jsxs13(CustomToolbar, { children: [
7273
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
7274
- gi > 0 && /* @__PURE__ */ jsx23(CustomToolbarDivider, {}),
6214
+ children: /* @__PURE__ */ jsxs11(CustomToolbar, { children: [
6215
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs11(React8.Fragment, { children: [
6216
+ gi > 0 && /* @__PURE__ */ jsx21(CustomToolbarDivider, {}),
7275
6217
  btns.map((btn) => {
7276
6218
  const isActive = activeCommands.has(btn.cmd);
7277
- return /* @__PURE__ */ jsx23(
6219
+ return /* @__PURE__ */ jsx21(
7278
6220
  CustomToolbarButton,
7279
6221
  {
7280
6222
  title: btn.title,
@@ -7283,7 +6225,7 @@ function FloatingToolbar({
7283
6225
  e.preventDefault();
7284
6226
  onCommand(btn.cmd);
7285
6227
  },
7286
- children: /* @__PURE__ */ jsx23(
6228
+ children: /* @__PURE__ */ jsx21(
7287
6229
  "svg",
7288
6230
  {
7289
6231
  width: "16",
@@ -7304,7 +6246,7 @@ function FloatingToolbar({
7304
6246
  );
7305
6247
  })
7306
6248
  ] }, gi)),
7307
- showEditLink ? /* @__PURE__ */ jsx23(
6249
+ showEditLink ? /* @__PURE__ */ jsx21(
7308
6250
  CustomToolbarButton,
7309
6251
  {
7310
6252
  type: "button",
@@ -7318,7 +6260,7 @@ function FloatingToolbar({
7318
6260
  e.preventDefault();
7319
6261
  e.stopPropagation();
7320
6262
  },
7321
- children: /* @__PURE__ */ jsx23(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
6263
+ children: /* @__PURE__ */ jsx21(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7322
6264
  }
7323
6265
  ) : null
7324
6266
  ] })
@@ -7335,7 +6277,7 @@ function StateToggle({
7335
6277
  states,
7336
6278
  onStateChange
7337
6279
  }) {
7338
- return /* @__PURE__ */ jsx23(
6280
+ return /* @__PURE__ */ jsx21(
7339
6281
  ToggleGroup,
7340
6282
  {
7341
6283
  "data-ohw-state-toggle": "",
@@ -7349,7 +6291,7 @@ function StateToggle({
7349
6291
  left: rect.right - 8,
7350
6292
  transform: "translateX(-100%)"
7351
6293
  },
7352
- children: states.map((state) => /* @__PURE__ */ jsx23(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6294
+ children: states.map((state) => /* @__PURE__ */ jsx21(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7353
6295
  }
7354
6296
  );
7355
6297
  }
@@ -7372,12 +6314,12 @@ function resolveSubdomain(subdomainFromQuery) {
7372
6314
  return "";
7373
6315
  }
7374
6316
  function OhhwellsBridge() {
7375
- const pathname = usePathname2();
7376
- const router = useRouter2();
6317
+ const pathname = usePathname();
6318
+ const router = useRouter();
7377
6319
  const searchParams = useSearchParams();
7378
6320
  const isEditMode = isEditSessionActive();
7379
- const [bridgeRoot, setBridgeRoot] = useState6(null);
7380
- useEffect7(() => {
6321
+ const [bridgeRoot, setBridgeRoot] = useState5(null);
6322
+ useEffect3(() => {
7381
6323
  const figtreeFontId = "ohw-figtree-font";
7382
6324
  if (!document.getElementById(figtreeFontId)) {
7383
6325
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7385,10 +6327,7 @@ function OhhwellsBridge() {
7385
6327
  const stylesheet = Object.assign(document.createElement("link"), {
7386
6328
  id: figtreeFontId,
7387
6329
  rel: "stylesheet",
7388
- // Inter is loaded alongside Figtree for the media overlay's Replace button. The bridge
7389
- // renders inside the customer's document, so it cannot assume any font is present — an
7390
- // unloaded family would silently fall back to whatever the template happens to use.
7391
- href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&family=Inter:wght@400;500;600&display=swap"
6330
+ href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
7392
6331
  });
7393
6332
  document.head.append(preconnect1, preconnect2, stylesheet);
7394
6333
  }
@@ -7405,90 +6344,80 @@ function OhhwellsBridge() {
7405
6344
  const subdomainFromQuery = searchParams.get("subdomain");
7406
6345
  const subdomain = resolveSubdomain(subdomainFromQuery);
7407
6346
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7408
- const postToParent2 = useCallback4((data) => {
6347
+ const postToParent = useCallback2((data) => {
7409
6348
  if (typeof window !== "undefined" && window.parent !== window) {
7410
6349
  window.parent.postMessage(data, "*");
7411
6350
  }
7412
6351
  }, []);
7413
- const [fetchState, setFetchState] = useState6("idle");
7414
- const autoSaveTimers = useRef4(/* @__PURE__ */ new Map());
7415
- const activeElRef = useRef4(null);
7416
- const selectedElRef = useRef4(null);
7417
- const originalContentRef = useRef4(null);
7418
- const activeStateElRef = useRef4(null);
7419
- const parentScrollRef = useRef4(null);
7420
- const visibleViewportRef = useRef4(null);
7421
- const [dialogPortalContainer, setDialogPortalContainer] = useState6(null);
7422
- const attachVisibleViewport = useCallback4((node) => {
6352
+ const [fetchState, setFetchState] = useState5("idle");
6353
+ const autoSaveTimers = useRef3(/* @__PURE__ */ new Map());
6354
+ const activeElRef = useRef3(null);
6355
+ const selectedElRef = useRef3(null);
6356
+ const originalContentRef = useRef3(null);
6357
+ const activeStateElRef = useRef3(null);
6358
+ const parentScrollRef = useRef3(null);
6359
+ const visibleViewportRef = useRef3(null);
6360
+ const [dialogPortalContainer, setDialogPortalContainer] = useState5(null);
6361
+ const attachVisibleViewport = useCallback2((node) => {
7423
6362
  visibleViewportRef.current = node;
7424
6363
  setDialogPortalContainer(node);
7425
6364
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7426
6365
  }, []);
7427
- const toolbarElRef = useRef4(null);
7428
- const glowElRef = useRef4(null);
7429
- const hoveredImageRef = useRef4(null);
7430
- const hoveredImageHasTextOverlapRef = useRef4(false);
7431
- const [mediaHover, setMediaHover] = useState6(null);
7432
- const [uploadingRects, setUploadingRects] = useState6({});
7433
- const hoveredGapRef = useRef4(null);
7434
- const imageUnhoverTimerRef = useRef4(null);
7435
- const imageShowTimerRef = useRef4(null);
7436
- const editStylesRef = useRef4(null);
7437
- const activateRef = useRef4(() => {
7438
- });
7439
- const deactivateRef = useRef4(() => {
6366
+ const toolbarElRef = useRef3(null);
6367
+ const glowElRef = useRef3(null);
6368
+ const hoveredImageRef = useRef3(null);
6369
+ const hoveredImageHasTextOverlapRef = useRef3(false);
6370
+ const hoveredGapRef = useRef3(null);
6371
+ const imageUnhoverTimerRef = useRef3(null);
6372
+ const imageShowTimerRef = useRef3(null);
6373
+ const editStylesRef = useRef3(null);
6374
+ const activateRef = useRef3(() => {
7440
6375
  });
7441
- const selectRef = useRef4(() => {
6376
+ const deactivateRef = useRef3(() => {
7442
6377
  });
7443
- const selectFrameRef = useRef4(() => {
6378
+ const selectRef = useRef3(() => {
7444
6379
  });
7445
- const deselectRef = useRef4(() => {
6380
+ const deselectRef = useRef3(() => {
7446
6381
  });
7447
- const reselectNavigationItemRef = useRef4(() => {
6382
+ const reselectNavigationItemRef = useRef3(() => {
7448
6383
  });
7449
- const commitNavigationTextEditRef = useRef4(() => {
6384
+ const commitNavigationTextEditRef = useRef3(() => {
7450
6385
  });
7451
- const refreshActiveCommandsRef = useRef4(() => {
6386
+ const refreshActiveCommandsRef = useRef3(() => {
7452
6387
  });
7453
- const postToParentRef = useRef4(postToParent2);
7454
- postToParentRef.current = postToParent2;
7455
- const sectionsLoadedRef = useRef4(false);
7456
- const pendingScheduleConfigRequests = useRef4([]);
7457
- const [toolbarRect, setToolbarRect] = useState6(null);
7458
- const [toolbarVariant, setToolbarVariant] = useState6("none");
7459
- const toolbarVariantRef = useRef4("none");
6388
+ const postToParentRef = useRef3(postToParent);
6389
+ postToParentRef.current = postToParent;
6390
+ const sectionsLoadedRef = useRef3(false);
6391
+ const pendingScheduleConfigRequests = useRef3([]);
6392
+ const [toolbarRect, setToolbarRect] = useState5(null);
6393
+ const [toolbarVariant, setToolbarVariant] = useState5("none");
6394
+ const toolbarVariantRef = useRef3("none");
7460
6395
  toolbarVariantRef.current = toolbarVariant;
7461
- const [reorderHrefKey, setReorderHrefKey] = useState6(null);
7462
- const [reorderDragDisabled, setReorderDragDisabled] = useState6(false);
7463
- const [toggleState, setToggleState] = useState6(null);
7464
- const [maxBadge, setMaxBadge] = useState6(null);
7465
- const [activeCommands, setActiveCommands] = useState6(/* @__PURE__ */ new Set());
7466
- const [sectionGap, setSectionGap] = useState6(null);
7467
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState6(false);
7468
- const hoveredNavContainerRef = useRef4(null);
7469
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState6(null);
7470
- const hoveredItemElRef = useRef4(null);
7471
- const [hoveredItemRect, setHoveredItemRect] = useState6(null);
7472
- const siblingHintElRef = useRef4(null);
7473
- const [siblingHintRect, setSiblingHintRect] = useState6(null);
7474
- const [isItemDragging, setIsItemDragging] = useState6(false);
7475
- const [linkPopover, setLinkPopover] = useState6(null);
7476
- const linkPopoverSessionRef = useRef4(null);
7477
- const addNavAfterAnchorRef = useRef4(null);
7478
- const editContentRef = useRef4({});
7479
- const [sitePages, setSitePages] = useState6([]);
7480
- const [sectionsByPath, setSectionsByPath] = useState6({});
7481
- const sectionsPrefetchGenRef = useRef4(0);
7482
- const setLinkPopoverRef = useRef4(setLinkPopover);
7483
- const linkPopoverPanelRef = useRef4(null);
7484
- const linkPopoverOpenRef = useRef4(false);
7485
- const linkPopoverGraceUntilRef = useRef4(0);
6396
+ const [reorderHrefKey, setReorderHrefKey] = useState5(null);
6397
+ const [reorderDragDisabled, setReorderDragDisabled] = useState5(false);
6398
+ const [toggleState, setToggleState] = useState5(null);
6399
+ const [maxBadge, setMaxBadge] = useState5(null);
6400
+ const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
6401
+ const [sectionGap, setSectionGap] = useState5(null);
6402
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
6403
+ const hoveredItemElRef = useRef3(null);
6404
+ const [hoveredItemRect, setHoveredItemRect] = useState5(null);
6405
+ const siblingHintElRef = useRef3(null);
6406
+ const [siblingHintRect, setSiblingHintRect] = useState5(null);
6407
+ const [isItemDragging, setIsItemDragging] = useState5(false);
6408
+ const [linkPopover, setLinkPopover] = useState5(null);
6409
+ const [sitePages, setSitePages] = useState5([]);
6410
+ const [sectionsByPath, setSectionsByPath] = useState5({});
6411
+ const sectionsPrefetchGenRef = useRef3(0);
6412
+ const setLinkPopoverRef = useRef3(setLinkPopover);
6413
+ const linkPopoverPanelRef = useRef3(null);
6414
+ const linkPopoverOpenRef = useRef3(false);
6415
+ const linkPopoverGraceUntilRef = useRef3(0);
7486
6416
  setLinkPopoverRef.current = setLinkPopover;
7487
- linkPopoverSessionRef.current = linkPopover;
7488
6417
  const bumpLinkPopoverGrace = () => {
7489
6418
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7490
6419
  };
7491
- const runSectionsPrefetch = useCallback4((pages) => {
6420
+ const runSectionsPrefetch = useCallback2((pages) => {
7492
6421
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7493
6422
  const gen = ++sectionsPrefetchGenRef.current;
7494
6423
  const paths = pages.map((p) => p.path);
@@ -7507,9 +6436,9 @@ function OhhwellsBridge() {
7507
6436
  );
7508
6437
  });
7509
6438
  }, [isEditMode, pathname]);
7510
- const runSectionsPrefetchRef = useRef4(runSectionsPrefetch);
6439
+ const runSectionsPrefetchRef = useRef3(runSectionsPrefetch);
7511
6440
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7512
- useEffect7(() => {
6441
+ useEffect3(() => {
7513
6442
  if (!linkPopover) return;
7514
6443
  if (hoveredImageRef.current) {
7515
6444
  hoveredImageRef.current = null;
@@ -7517,9 +6446,33 @@ function OhhwellsBridge() {
7517
6446
  }
7518
6447
  hoveredGapRef.current = null;
7519
6448
  setSectionGap(null);
7520
- postToParent2({ type: "ow:image-unhover" });
7521
- }, [linkPopover, postToParent2]);
7522
- useEffect7(() => {
6449
+ postToParent({ type: "ow:image-unhover" });
6450
+ postToParent({ type: "ow:link-modal-lock", locked: true });
6451
+ const html = document.documentElement;
6452
+ const body = document.body;
6453
+ const prevHtmlOverflow = html.style.overflow;
6454
+ const prevBodyOverflow = body.style.overflow;
6455
+ html.style.overflow = "hidden";
6456
+ body.style.overflow = "hidden";
6457
+ const preventBackgroundScroll = (e) => {
6458
+ const target = e.target;
6459
+ if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
6460
+ return;
6461
+ }
6462
+ e.preventDefault();
6463
+ };
6464
+ const scrollOpts = { passive: false, capture: true };
6465
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6466
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6467
+ return () => {
6468
+ postToParent({ type: "ow:link-modal-lock", locked: false });
6469
+ html.style.overflow = prevHtmlOverflow;
6470
+ body.style.overflow = prevBodyOverflow;
6471
+ document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6472
+ document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6473
+ };
6474
+ }, [linkPopover, postToParent]);
6475
+ useEffect3(() => {
7523
6476
  if (!isEditMode) return;
7524
6477
  const useFixtures = shouldUseDevFixtures();
7525
6478
  if (useFixtures) {
@@ -7540,17 +6493,17 @@ function OhhwellsBridge() {
7540
6493
  runSectionsPrefetchRef.current(mapped);
7541
6494
  };
7542
6495
  window.addEventListener("message", onSitePages);
7543
- if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6496
+ if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
7544
6497
  return () => window.removeEventListener("message", onSitePages);
7545
- }, [isEditMode, postToParent2]);
7546
- useEffect7(() => {
6498
+ }, [isEditMode, postToParent]);
6499
+ useEffect3(() => {
7547
6500
  if (!isEditMode || shouldUseDevFixtures()) return;
7548
6501
  void loadAllSectionsManifest().then((manifest) => {
7549
6502
  if (Object.keys(manifest).length === 0) return;
7550
6503
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7551
6504
  });
7552
6505
  }, [isEditMode]);
7553
- useEffect7(() => {
6506
+ useEffect3(() => {
7554
6507
  const update = () => {
7555
6508
  const el = activeElRef.current ?? selectedElRef.current;
7556
6509
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7574,10 +6527,10 @@ function OhhwellsBridge() {
7574
6527
  vvp.removeEventListener("resize", update);
7575
6528
  };
7576
6529
  }, []);
7577
- const refreshStateRules = useCallback4(() => {
6530
+ const refreshStateRules = useCallback2(() => {
7578
6531
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7579
6532
  }, []);
7580
- const processConfigRequest = useCallback4((insertAfterVal) => {
6533
+ const processConfigRequest = useCallback2((insertAfterVal) => {
7581
6534
  const tracker = getSectionsTracker();
7582
6535
  let entries = [];
7583
6536
  try {
@@ -7600,7 +6553,7 @@ function OhhwellsBridge() {
7600
6553
  }
7601
6554
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7602
6555
  }, [isEditMode]);
7603
- const deactivate = useCallback4(() => {
6556
+ const deactivate = useCallback2(() => {
7604
6557
  const el = activeElRef.current;
7605
6558
  if (!el) return;
7606
6559
  const key = el.dataset.ohwKey;
@@ -7629,23 +6582,21 @@ function OhhwellsBridge() {
7629
6582
  setMaxBadge(null);
7630
6583
  setActiveCommands(/* @__PURE__ */ new Set());
7631
6584
  setToolbarShowEditLink(false);
7632
- postToParent2({ type: "ow:exit-edit" });
7633
- }, [postToParent2]);
7634
- const deselect = useCallback4(() => {
6585
+ postToParent({ type: "ow:exit-edit" });
6586
+ }, [postToParent]);
6587
+ const deselect = useCallback2(() => {
7635
6588
  selectedElRef.current = null;
7636
6589
  setReorderHrefKey(null);
7637
6590
  setReorderDragDisabled(false);
7638
6591
  siblingHintElRef.current = null;
7639
6592
  setSiblingHintRect(null);
7640
6593
  setIsItemDragging(false);
7641
- hoveredNavContainerRef.current = null;
7642
- setHoveredNavContainerRect(null);
7643
6594
  if (!activeElRef.current) {
7644
6595
  setToolbarRect(null);
7645
6596
  setToolbarVariant("none");
7646
6597
  }
7647
6598
  }, []);
7648
- const reselectNavigationItem = useCallback4((navAnchor) => {
6599
+ const reselectNavigationItem = useCallback2((navAnchor) => {
7649
6600
  selectedElRef.current = navAnchor;
7650
6601
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
7651
6602
  setReorderHrefKey(key);
@@ -7655,7 +6606,7 @@ function OhhwellsBridge() {
7655
6606
  setToolbarShowEditLink(false);
7656
6607
  setActiveCommands(/* @__PURE__ */ new Set());
7657
6608
  }, []);
7658
- const commitNavigationTextEdit = useCallback4((navAnchor) => {
6609
+ const commitNavigationTextEdit = useCallback2((navAnchor) => {
7659
6610
  const el = activeElRef.current;
7660
6611
  if (!el) return;
7661
6612
  const key = el.dataset.ohwKey;
@@ -7668,9 +6619,9 @@ function OhhwellsBridge() {
7668
6619
  const html = sanitizeHtml(el.innerHTML);
7669
6620
  const original = originalContentRef.current ?? "";
7670
6621
  if (html !== sanitizeHtml(original)) {
7671
- postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
6622
+ postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
7672
6623
  const h = document.documentElement.scrollHeight;
7673
- if (h > 50) postToParent2({ type: "ow:height", height: h });
6624
+ if (h > 50) postToParent({ type: "ow:height", height: h });
7674
6625
  }
7675
6626
  }
7676
6627
  el.removeAttribute("contenteditable");
@@ -7678,38 +6629,31 @@ function OhhwellsBridge() {
7678
6629
  setMaxBadge(null);
7679
6630
  setActiveCommands(/* @__PURE__ */ new Set());
7680
6631
  setToolbarShowEditLink(false);
7681
- postToParent2({ type: "ow:exit-edit" });
6632
+ postToParent({ type: "ow:exit-edit" });
7682
6633
  reselectNavigationItem(navAnchor);
7683
- }, [postToParent2, reselectNavigationItem]);
7684
- const handleAddTopLevelNavItem = useCallback4(() => {
7685
- const items = listNavbarItems();
7686
- addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7687
- deselectRef.current();
7688
- deactivateRef.current();
7689
- bumpLinkPopoverGrace();
7690
- setLinkPopover({
7691
- key: "__add-nav__",
7692
- mode: "create",
7693
- intent: "add-nav"
7694
- });
6634
+ }, [postToParent, reselectNavigationItem]);
6635
+ const handleAddNavigationItem = useCallback2(() => {
6636
+ const selected = selectedElRef.current;
6637
+ if (!selected) return;
6638
+ const target = getNavigationItemAddHintTarget(selected);
6639
+ siblingHintElRef.current = target;
6640
+ setSiblingHintRect(target.getBoundingClientRect());
7695
6641
  }, []);
7696
- const handleItemDragStart = useCallback4(() => {
6642
+ const handleItemDragStart = useCallback2(() => {
7697
6643
  siblingHintElRef.current = null;
7698
6644
  setSiblingHintRect(null);
7699
6645
  setIsItemDragging(true);
7700
6646
  }, []);
7701
- const handleItemDragEnd = useCallback4(() => {
6647
+ const handleItemDragEnd = useCallback2(() => {
7702
6648
  setIsItemDragging(false);
7703
6649
  }, []);
7704
6650
  reselectNavigationItemRef.current = reselectNavigationItem;
7705
6651
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
7706
- const select = useCallback4((anchor) => {
6652
+ const select = useCallback2((anchor) => {
7707
6653
  if (!isNavigationItem(anchor)) return;
7708
6654
  if (activeElRef.current) deactivate();
7709
6655
  selectedElRef.current = anchor;
7710
6656
  clearHrefKeyHover(anchor);
7711
- hoveredNavContainerRef.current = null;
7712
- setHoveredNavContainerRect(null);
7713
6657
  setHoveredItemRect(null);
7714
6658
  hoveredItemElRef.current = null;
7715
6659
  siblingHintElRef.current = null;
@@ -7723,32 +6667,13 @@ function OhhwellsBridge() {
7723
6667
  setToolbarShowEditLink(false);
7724
6668
  setActiveCommands(/* @__PURE__ */ new Set());
7725
6669
  }, [deactivate]);
7726
- const selectFrame = useCallback4((el) => {
7727
- if (!isNavigationContainer(el)) return;
7728
- if (activeElRef.current) deactivate();
7729
- selectedElRef.current = el;
7730
- clearHrefKeyHover(el);
7731
- hoveredNavContainerRef.current = null;
7732
- setHoveredNavContainerRect(null);
7733
- setHoveredItemRect(null);
7734
- hoveredItemElRef.current = null;
7735
- siblingHintElRef.current = null;
7736
- setSiblingHintRect(null);
7737
- setIsItemDragging(false);
7738
- setReorderHrefKey(null);
7739
- setReorderDragDisabled(false);
7740
- setToolbarVariant("select-frame");
7741
- setToolbarRect(el.getBoundingClientRect());
7742
- setToolbarShowEditLink(false);
7743
- setActiveCommands(/* @__PURE__ */ new Set());
7744
- }, [deactivate]);
7745
- const activate = useCallback4((el, options) => {
6670
+ const activate = useCallback2((el) => {
7746
6671
  if (activeElRef.current === el) return;
7747
6672
  selectedElRef.current = null;
7748
6673
  deactivate();
7749
6674
  if (hoveredImageRef.current) {
7750
6675
  hoveredImageRef.current = null;
7751
- setMediaHover(null);
6676
+ postToParentRef.current({ type: "ow:image-unhover" });
7752
6677
  }
7753
6678
  setToolbarVariant("rich-text");
7754
6679
  siblingHintElRef.current = null;
@@ -7760,9 +6685,6 @@ function OhhwellsBridge() {
7760
6685
  activeElRef.current = el;
7761
6686
  originalContentRef.current = el.innerHTML;
7762
6687
  el.focus();
7763
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7764
- placeCaretAtPoint(el, options.caretX, options.caretY);
7765
- }
7766
6688
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7767
6689
  const navAnchor = getNavigationItemAnchor(el);
7768
6690
  if (navAnchor) {
@@ -7774,13 +6696,12 @@ function OhhwellsBridge() {
7774
6696
  setReorderDragDisabled(reorderDisabled);
7775
6697
  }
7776
6698
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7777
- postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
6699
+ postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7778
6700
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
7779
- }, [deactivate, postToParent2]);
6701
+ }, [deactivate, postToParent]);
7780
6702
  activateRef.current = activate;
7781
6703
  deactivateRef.current = deactivate;
7782
6704
  selectRef.current = select;
7783
- selectFrameRef.current = selectFrame;
7784
6705
  deselectRef.current = deselect;
7785
6706
  useLayoutEffect2(() => {
7786
6707
  if (!subdomain || isEditMode) {
@@ -7791,7 +6712,6 @@ function OhhwellsBridge() {
7791
6712
  const imageLoads = [];
7792
6713
  for (const [key, val] of Object.entries(content)) {
7793
6714
  if (key === "__ohw_sections") continue;
7794
- if (applyVideoSettingNode(key, val)) continue;
7795
6715
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7796
6716
  if (el.dataset.ohwEditable === "image") {
7797
6717
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7805,15 +6725,6 @@ function OhhwellsBridge() {
7805
6725
  } else if (el.dataset.ohwEditable === "bg-image") {
7806
6726
  const next = `url('${val}')`;
7807
6727
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
7808
- } else if (el.dataset.ohwEditable === "video") {
7809
- const video = getVideoEl(el);
7810
- if (video && video.src !== val) {
7811
- applyVideoSrc(video, val);
7812
- imageLoads.push(new Promise((resolve) => {
7813
- video.onloadeddata = () => resolve();
7814
- video.onerror = () => resolve();
7815
- }));
7816
- }
7817
6728
  } else if (el.dataset.ohwEditable === "link") {
7818
6729
  applyLinkHref(el, val);
7819
6730
  } else if (el.innerHTML !== val) {
@@ -7822,7 +6733,6 @@ function OhhwellsBridge() {
7822
6733
  });
7823
6734
  applyLinkByKey(key, val);
7824
6735
  }
7825
- reconcileNavbarItemsFromContent(content);
7826
6736
  enforceLinkHrefs();
7827
6737
  initSectionsFromContent(content, true);
7828
6738
  sectionsLoadedRef.current = true;
@@ -7851,7 +6761,7 @@ function OhhwellsBridge() {
7851
6761
  cancelled = true;
7852
6762
  };
7853
6763
  }, [subdomain, isEditMode]);
7854
- useEffect7(() => {
6764
+ useEffect3(() => {
7855
6765
  if (!subdomain || isEditMode) return;
7856
6766
  let debounceTimer = null;
7857
6767
  let observer = null;
@@ -7863,7 +6773,6 @@ function OhhwellsBridge() {
7863
6773
  try {
7864
6774
  for (const [key, val] of Object.entries(content)) {
7865
6775
  if (key === "__ohw_sections") continue;
7866
- if (applyVideoSettingNode(key, val)) continue;
7867
6776
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7868
6777
  if (el.dataset.ohwEditable === "image") {
7869
6778
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7871,9 +6780,6 @@ function OhhwellsBridge() {
7871
6780
  } else if (el.dataset.ohwEditable === "bg-image") {
7872
6781
  const next = `url('${val}')`;
7873
6782
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
7874
- } else if (el.dataset.ohwEditable === "video") {
7875
- const video = getVideoEl(el);
7876
- if (video && video.src !== val) applyVideoSrc(video, val);
7877
6783
  } else if (el.dataset.ohwEditable === "link") {
7878
6784
  applyLinkHref(el, val);
7879
6785
  } else if (el.innerHTML !== val) {
@@ -7882,7 +6788,6 @@ function OhhwellsBridge() {
7882
6788
  });
7883
6789
  applyLinkByKey(key, val);
7884
6790
  }
7885
- reconcileNavbarItemsFromContent(content);
7886
6791
  } finally {
7887
6792
  observer?.observe(document.body, { childList: true, subtree: true });
7888
6793
  }
@@ -7906,38 +6811,20 @@ function OhhwellsBridge() {
7906
6811
  const visible = Boolean(subdomain) && fetchState !== "done";
7907
6812
  el.style.display = visible ? "flex" : "none";
7908
6813
  }, [subdomain, fetchState]);
7909
- useEffect7(() => {
7910
- postToParent2({ type: "ow:navigation", path: pathname });
7911
- }, [pathname, postToParent2]);
7912
- useEffect7(() => {
6814
+ useEffect3(() => {
6815
+ postToParent({ type: "ow:navigation", path: pathname });
6816
+ }, [pathname, postToParent]);
6817
+ useEffect3(() => {
7913
6818
  if (!isEditMode) return;
7914
- if (linkPopoverSessionRef.current?.intent === "add-nav") return;
7915
- if (document.querySelector("[data-ohw-section-picker]")) return;
7916
6819
  setLinkPopover(null);
7917
6820
  deselectRef.current();
7918
6821
  deactivateRef.current();
7919
6822
  }, [pathname, isEditMode]);
7920
- useEffect7(() => {
7921
- const contentForNav = () => {
7922
- if (isEditMode) return editContentRef.current;
7923
- if (!subdomain) return {};
7924
- return contentCache.get(subdomain) ?? {};
7925
- };
7926
- const run = () => reconcileNavbarItemsFromContent(contentForNav());
7927
- run();
7928
- const nav = document.querySelector("nav");
7929
- if (!nav) return;
7930
- const observer = new MutationObserver(() => {
7931
- requestAnimationFrame(run);
7932
- });
7933
- observer.observe(nav, { childList: true, subtree: true });
7934
- return () => observer.disconnect();
7935
- }, [isEditMode, pathname, subdomain, fetchState]);
7936
- useEffect7(() => {
6823
+ useEffect3(() => {
7937
6824
  if (!isEditMode) return;
7938
6825
  const measure = () => {
7939
6826
  const h = document.body.scrollHeight;
7940
- if (h > 50) postToParent2({ type: "ow:height", height: h });
6827
+ if (h > 50) postToParent({ type: "ow:height", height: h });
7941
6828
  };
7942
6829
  const t1 = setTimeout(measure, 50);
7943
6830
  const t2 = setTimeout(measure, 500);
@@ -7956,8 +6843,8 @@ function OhhwellsBridge() {
7956
6843
  if (resizeTimer) clearTimeout(resizeTimer);
7957
6844
  window.removeEventListener("resize", handleResize);
7958
6845
  };
7959
- }, [pathname, isEditMode, postToParent2]);
7960
- useEffect7(() => {
6846
+ }, [pathname, isEditMode, postToParent]);
6847
+ useEffect3(() => {
7961
6848
  if (!subdomainFromQuery || isEditMode) return;
7962
6849
  const handleClick = (e) => {
7963
6850
  const anchor = e.target.closest("a");
@@ -7973,7 +6860,7 @@ function OhhwellsBridge() {
7973
6860
  document.addEventListener("click", handleClick, true);
7974
6861
  return () => document.removeEventListener("click", handleClick, true);
7975
6862
  }, [subdomainFromQuery, isEditMode, router]);
7976
- useEffect7(() => {
6863
+ useEffect3(() => {
7977
6864
  if (!isEditMode) {
7978
6865
  editStylesRef.current?.base.remove();
7979
6866
  editStylesRef.current?.forceHover.remove();
@@ -7996,9 +6883,8 @@ function OhhwellsBridge() {
7996
6883
  [data-ohw-editable] {
7997
6884
  display: block;
7998
6885
  }
7999
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
6886
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]) { cursor: text !important; }
8000
6887
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8001
- [data-ohw-editable="video"], [data-ohw-editable="video"] *,
8002
6888
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
8003
6889
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
8004
6890
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
@@ -8047,13 +6933,6 @@ function OhhwellsBridge() {
8047
6933
  if (target.closest("[data-ohw-max-badge]")) return;
8048
6934
  if (isInsideLinkEditor(target)) return;
8049
6935
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8050
- const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8051
- if (navFromChrome) {
8052
- e.preventDefault();
8053
- e.stopPropagation();
8054
- selectFrameRef.current(navFromChrome);
8055
- return;
8056
- }
8057
6936
  e.preventDefault();
8058
6937
  e.stopPropagation();
8059
6938
  return;
@@ -8068,15 +6947,14 @@ function OhhwellsBridge() {
8068
6947
  bumpLinkPopoverGrace();
8069
6948
  setLinkPopoverRef.current({
8070
6949
  key: editable.dataset.ohwKey ?? "",
8071
- mode: "edit",
8072
- target: getLinkHref2(editable)
6950
+ target: getLinkHref(editable)
8073
6951
  });
8074
6952
  return;
8075
6953
  }
8076
- if (isMediaEditable(editable)) {
6954
+ if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
8077
6955
  e.preventDefault();
8078
6956
  e.stopPropagation();
8079
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
6957
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
8080
6958
  return;
8081
6959
  }
8082
6960
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -8085,8 +6963,7 @@ function OhhwellsBridge() {
8085
6963
  e.preventDefault();
8086
6964
  e.stopPropagation();
8087
6965
  if (selectedElRef.current === navAnchor) {
8088
- if (e.detail >= 2) return;
8089
- activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
6966
+ activateRef.current(editable);
8090
6967
  return;
8091
6968
  }
8092
6969
  selectRef.current(navAnchor);
@@ -8105,22 +6982,6 @@ function OhhwellsBridge() {
8105
6982
  selectRef.current(hrefAnchor);
8106
6983
  return;
8107
6984
  }
8108
- const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8109
- if (navContainerToSelect) {
8110
- e.preventDefault();
8111
- e.stopPropagation();
8112
- selectFrameRef.current(navContainerToSelect);
8113
- return;
8114
- }
8115
- const selectedContainer = selectedElRef.current;
8116
- if (selectedContainer && isNavigationContainer(selectedContainer)) {
8117
- const navItem = getNavigationItemAnchor(target);
8118
- if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
8119
- e.preventDefault();
8120
- e.stopPropagation();
8121
- return;
8122
- }
8123
- }
8124
6985
  if (activeElRef.current) {
8125
6986
  const sel = window.getSelection();
8126
6987
  if (sel && !sel.isCollapsed) {
@@ -8146,48 +7007,10 @@ function OhhwellsBridge() {
8146
7007
  deselectRef.current();
8147
7008
  deactivateRef.current();
8148
7009
  };
8149
- const handleDblClick = (e) => {
8150
- const target = e.target;
8151
- if (target.closest("[data-ohw-toolbar]")) return;
8152
- if (target.closest("[data-ohw-state-toggle]")) return;
8153
- if (target.closest("[data-ohw-max-badge]")) return;
8154
- if (isInsideLinkEditor(target)) return;
8155
- if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8156
- return;
8157
- }
8158
- const navLabel = getNavigationLabelEditable(target);
8159
- if (!navLabel) return;
8160
- const { editable } = navLabel;
8161
- e.preventDefault();
8162
- e.stopPropagation();
8163
- if (activeElRef.current !== editable) {
8164
- activateRef.current(editable);
8165
- }
8166
- requestAnimationFrame(() => {
8167
- selectAllTextInEditable(editable);
8168
- refreshActiveCommandsRef.current();
8169
- });
8170
- };
8171
7010
  const handleMouseOver = (e) => {
8172
7011
  const target = e.target;
8173
- if (toolbarVariantRef.current !== "select-frame") {
8174
- const navContainer = target.closest("[data-ohw-nav-container]");
8175
- if (navContainer && !getNavigationItemAnchor(target)) {
8176
- hoveredNavContainerRef.current = navContainer;
8177
- setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8178
- hoveredItemElRef.current = null;
8179
- setHoveredItemRect(null);
8180
- return;
8181
- }
8182
- if (!target.closest("[data-ohw-nav-container]")) {
8183
- hoveredNavContainerRef.current = null;
8184
- setHoveredNavContainerRect(null);
8185
- }
8186
- }
8187
7012
  const navAnchor = getNavigationItemAnchor(target);
8188
7013
  if (navAnchor) {
8189
- hoveredNavContainerRef.current = null;
8190
- setHoveredNavContainerRect(null);
8191
7014
  const selected2 = selectedElRef.current;
8192
7015
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
8193
7016
  clearHrefKeyHover(navAnchor);
@@ -8199,7 +7022,7 @@ function OhhwellsBridge() {
8199
7022
  if (!editable) return;
8200
7023
  const selected = selectedElRef.current;
8201
7024
  if (selected && (selected === editable || selected.contains(editable))) return;
8202
- if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
7025
+ if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
8203
7026
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
8204
7027
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
8205
7028
  clearHrefKeyHover(hoverTarget);
@@ -8212,14 +7035,6 @@ function OhhwellsBridge() {
8212
7035
  };
8213
7036
  const handleMouseOut = (e) => {
8214
7037
  const target = e.target;
8215
- if (target.closest("[data-ohw-nav-container]")) {
8216
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8217
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8218
- return;
8219
- }
8220
- hoveredNavContainerRef.current = null;
8221
- setHoveredNavContainerRect(null);
8222
- }
8223
7038
  const navAnchor = getNavigationItemAnchor(target);
8224
7039
  if (navAnchor) {
8225
7040
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8235,7 +7050,7 @@ function OhhwellsBridge() {
8235
7050
  if (!editable) return;
8236
7051
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8237
7052
  if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
8238
- if (!isMediaEditable(editable)) {
7053
+ if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image") {
8239
7054
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
8240
7055
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
8241
7056
  if (!related?.closest("[data-ohw-href-key]")) {
@@ -8281,26 +7096,23 @@ function OhhwellsBridge() {
8281
7096
  }
8282
7097
  return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
8283
7098
  };
8284
- const showImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
7099
+ const postImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
8285
7100
  const r2 = getVisibleRect(imgEl);
8286
7101
  const hasTextOverlap = forceTextOverlap ?? false;
8287
7102
  hoveredImageHasTextOverlapRef.current = hasTextOverlap;
8288
- const video = imgEl.dataset.ohwEditable === "video" ? getVideoEl(imgEl) : null;
8289
- setMediaHover({
7103
+ postToParentRef.current({
7104
+ type: "ow:image-hover",
8290
7105
  key: imgEl.dataset.ohwKey ?? "",
8291
7106
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
8292
- elementType: imgEl.dataset.ohwEditable ?? "image",
8293
7107
  hasTextOverlap,
8294
- isDragOver,
8295
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
7108
+ ...isDragOver ? { isDragOver: true } : {}
8296
7109
  });
8297
7110
  const track = imgEl.closest("[data-ohw-hover-pause]");
8298
7111
  if (track) track.setAttribute("data-ohw-hover-paused", "");
8299
7112
  };
8300
- const clearImageHover = () => setMediaHover(null);
8301
7113
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
8302
7114
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8303
- const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
7115
+ const images = Array.from(document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]'));
8304
7116
  const matches = [];
8305
7117
  for (let i = images.length - 1; i >= 0; i--) {
8306
7118
  const el = images[i];
@@ -8316,9 +7128,7 @@ function OhhwellsBridge() {
8316
7128
  if (x >= r2.left && x <= r2.left + r2.width && y >= r2.top && y <= r2.top + r2.height) matches.push(el);
8317
7129
  }
8318
7130
  if (matches.length === 0) return null;
8319
- const imageTypeMatches = matches.filter(
8320
- (el) => el.dataset.ohwEditable === "image" || el.dataset.ohwEditable === "video"
8321
- );
7131
+ const imageTypeMatches = matches.filter((el) => el.dataset.ohwEditable === "image");
8322
7132
  const candidatePool = imageTypeMatches.length > 0 ? imageTypeMatches : matches;
8323
7133
  const smallest = candidatePool.reduce((best, el) => {
8324
7134
  const br = getVisibleRect(best);
@@ -8341,91 +7151,47 @@ function OhhwellsBridge() {
8341
7151
  }
8342
7152
  return smallest;
8343
7153
  };
8344
- const dismissImageHover = () => {
8345
- if (hoveredImageRef.current) {
8346
- hoveredImageRef.current = null;
8347
- hoveredImageHasTextOverlapRef.current = false;
8348
- resumeAnimTracks();
8349
- postToParentRef.current({ type: "ow:image-unhover" });
8350
- }
8351
- clearImageHover();
8352
- };
8353
- const probeNavigationHoverAt = (x, y) => {
8354
- if (toolbarVariantRef.current === "select-frame") {
8355
- hoveredNavContainerRef.current = null;
8356
- setHoveredNavContainerRect(null);
8357
- return;
8358
- }
8359
- const navContainer = document.querySelector("[data-ohw-nav-container]");
8360
- if (!navContainer) {
8361
- hoveredNavContainerRef.current = null;
8362
- setHoveredNavContainerRect(null);
8363
- return;
8364
- }
8365
- const containerRect = navContainer.getBoundingClientRect();
8366
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8367
- if (!overContainer) {
8368
- hoveredNavContainerRef.current = null;
8369
- setHoveredNavContainerRect(null);
8370
- return;
8371
- }
8372
- const navItemHit = Array.from(
8373
- navContainer.querySelectorAll("[data-ohw-href-key]")
8374
- ).find((el) => {
8375
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8376
- const itemRect = el.getBoundingClientRect();
8377
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8378
- });
8379
- if (navItemHit) {
8380
- hoveredNavContainerRef.current = null;
8381
- setHoveredNavContainerRect(null);
8382
- const selected = selectedElRef.current;
8383
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8384
- clearHrefKeyHover(navItemHit);
8385
- hoveredItemElRef.current = navItemHit;
8386
- setHoveredItemRect(navItemHit.getBoundingClientRect());
8387
- }
8388
- return;
8389
- }
8390
- hoveredNavContainerRef.current = navContainer;
8391
- setHoveredNavContainerRect(containerRect);
8392
- hoveredItemElRef.current = null;
8393
- setHoveredItemRect(null);
8394
- };
8395
7154
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
8396
7155
  if (linkPopoverOpenRef.current) {
8397
- dismissImageHover();
8398
- return;
8399
- }
8400
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8401
- if (isPointOverNavigation(x, y)) {
8402
- dismissImageHover();
8403
- probeNavigationHoverAt(x, y);
7156
+ if (hoveredImageRef.current) {
7157
+ hoveredImageRef.current = null;
7158
+ hoveredImageHasTextOverlapRef.current = false;
7159
+ resumeAnimTracks();
7160
+ postToParentRef.current({ type: "ow:image-unhover" });
7161
+ }
8404
7162
  return;
8405
7163
  }
8406
- hoveredNavContainerRef.current = null;
8407
- setHoveredNavContainerRect(null);
8408
7164
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
8409
7165
  if (toggleEl) {
7166
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8410
7167
  const tr = toggleEl.getBoundingClientRect();
8411
7168
  if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
8412
- dismissImageHover();
7169
+ if (hoveredImageRef.current) {
7170
+ hoveredImageRef.current = null;
7171
+ resumeAnimTracks();
7172
+ postToParentRef.current({ type: "ow:image-unhover" });
7173
+ }
8413
7174
  return;
8414
7175
  }
8415
7176
  }
8416
- const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
8417
- const activeEl = activeElRef.current;
8418
- if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
8419
- dismissImageHover();
7177
+ if (activeElRef.current) {
7178
+ if (hoveredImageRef.current) {
7179
+ hoveredImageRef.current = null;
7180
+ hoveredImageHasTextOverlapRef.current = false;
7181
+ resumeAnimTracks();
7182
+ postToParentRef.current({ type: "ow:image-unhover" });
7183
+ }
8420
7184
  return;
8421
7185
  }
7186
+ const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
8422
7187
  if (imgEl) {
7188
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8423
7189
  const topEl = document.elementFromPoint(x, y);
8424
7190
  if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8425
7191
  if (hoveredImageRef.current) {
8426
7192
  hoveredImageRef.current = null;
8427
7193
  resumeAnimTracks();
8428
- clearImageHover();
7194
+ postToParentRef.current({ type: "ow:image-unhover" });
8429
7195
  }
8430
7196
  return;
8431
7197
  }
@@ -8434,13 +7200,13 @@ function OhhwellsBridge() {
8434
7200
  hoveredImageRef.current = null;
8435
7201
  hoveredImageHasTextOverlapRef.current = false;
8436
7202
  resumeAnimTracks();
8437
- clearImageHover();
7203
+ postToParentRef.current({ type: "ow:image-unhover" });
8438
7204
  }
8439
7205
  return;
8440
7206
  }
8441
7207
  const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
8442
7208
  const textEditable = Array.from(
8443
- document.querySelectorAll(NON_MEDIA_SELECTOR)
7209
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
8444
7210
  ).find((el) => {
8445
7211
  const er = el.getBoundingClientRect();
8446
7212
  return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
@@ -8459,14 +7225,14 @@ function OhhwellsBridge() {
8459
7225
  hoveredImageRef.current = null;
8460
7226
  hoveredImageHasTextOverlapRef.current = false;
8461
7227
  resumeAnimTracks();
8462
- clearImageHover();
7228
+ postToParentRef.current({ type: "ow:image-unhover" });
8463
7229
  }
8464
7230
  return;
8465
7231
  }
8466
7232
  if (isStateCardImage) {
8467
7233
  if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
8468
7234
  hoveredImageRef.current = imgEl;
8469
- showImageHover(imgEl, isDragOver, true);
7235
+ postImageHover(imgEl, isDragOver, true);
8470
7236
  }
8471
7237
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8472
7238
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -8478,7 +7244,7 @@ function OhhwellsBridge() {
8478
7244
  hoveredImageRef.current = null;
8479
7245
  hoveredImageHasTextOverlapRef.current = false;
8480
7246
  resumeAnimTracks();
8481
- clearImageHover();
7247
+ postToParentRef.current({ type: "ow:image-unhover" });
8482
7248
  }
8483
7249
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8484
7250
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -8490,7 +7256,7 @@ function OhhwellsBridge() {
8490
7256
  if (hoveredImageRef.current) {
8491
7257
  hoveredImageRef.current = null;
8492
7258
  resumeAnimTracks();
8493
- clearImageHover();
7259
+ postToParentRef.current({ type: "ow:image-unhover" });
8494
7260
  }
8495
7261
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8496
7262
  return;
@@ -8498,9 +7264,9 @@ function OhhwellsBridge() {
8498
7264
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8499
7265
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
8500
7266
  hoveredImageRef.current = imgEl;
8501
- showImageHover(imgEl, isDragOver);
7267
+ postImageHover(imgEl, isDragOver);
8502
7268
  } else if (isDragOver) {
8503
- showImageHover(imgEl, true);
7269
+ postImageHover(imgEl, true);
8504
7270
  }
8505
7271
  } else {
8506
7272
  const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
@@ -8517,14 +7283,14 @@ function OhhwellsBridge() {
8517
7283
  hoveredImageRef.current = null;
8518
7284
  hoveredImageHasTextOverlapRef.current = false;
8519
7285
  resumeAnimTracks();
8520
- clearImageHover();
7286
+ postToParentRef.current({ type: "ow:image-unhover" });
8521
7287
  }
8522
- const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7288
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8523
7289
  const textEl = Array.from(
8524
- document.querySelectorAll(NON_MEDIA_SELECTOR)
7290
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
8525
7291
  ).find((el) => {
8526
7292
  const er = el.getBoundingClientRect();
8527
- return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7293
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8528
7294
  });
8529
7295
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8530
7296
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -8630,7 +7396,7 @@ function OhhwellsBridge() {
8630
7396
  if (hoveredImageRef.current !== el) {
8631
7397
  hoveredImageRef.current = el;
8632
7398
  const isStateCard = !!el.closest("[data-ohw-editable-state]");
8633
- showImageHover(el, true, isStateCard ? true : void 0);
7399
+ postImageHover(el, true, isStateCard ? true : void 0);
8634
7400
  }
8635
7401
  };
8636
7402
  const handleDragLeave = (e) => {
@@ -8638,7 +7404,7 @@ function OhhwellsBridge() {
8638
7404
  if (imgEl) return;
8639
7405
  hoveredImageRef.current = null;
8640
7406
  resumeAnimTracks();
8641
- clearImageHover();
7407
+ postToParentRef.current({ type: "ow:image-unhover" });
8642
7408
  };
8643
7409
  const handleDrop = (e) => {
8644
7410
  e.preventDefault();
@@ -8646,9 +7412,7 @@ function OhhwellsBridge() {
8646
7412
  if (!el) return;
8647
7413
  const file = e.dataTransfer?.files?.[0];
8648
7414
  if (file) {
8649
- const wantsVideo = el.dataset.ohwEditable === "video";
8650
- const accepted = wantsVideo ? file.type.startsWith("video/") : file.type.startsWith("image/");
8651
- if (accepted) {
7415
+ if (file.type.startsWith("image/")) {
8652
7416
  const key = el.dataset.ohwKey ?? "";
8653
7417
  const { name, type: mimeType } = file;
8654
7418
  const r2 = el.getBoundingClientRect();
@@ -8662,7 +7426,7 @@ function OhhwellsBridge() {
8662
7426
  }
8663
7427
  hoveredImageRef.current = null;
8664
7428
  resumeAnimTracks();
8665
- clearImageHover();
7429
+ postToParentRef.current({ type: "ow:image-unhover" });
8666
7430
  };
8667
7431
  const handleImageUrl = (e) => {
8668
7432
  if (e.data?.type !== "ow:image-url") return;
@@ -8680,11 +7444,7 @@ function OhhwellsBridge() {
8680
7444
  }
8681
7445
  });
8682
7446
  hoveredImageRef.current = null;
8683
- setUploadingRects((prev) => {
8684
- const entry = prev[key];
8685
- if (!entry || entry.fadingOut) return prev;
8686
- return { ...prev, [key]: { ...entry, fadingOut: true } };
8687
- });
7447
+ postToParentRef.current({ type: "ow:image-loaded", key });
8688
7448
  };
8689
7449
  let found = false;
8690
7450
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -8719,19 +7479,6 @@ function OhhwellsBridge() {
8719
7479
  requestAnimationFrame(() => onReady());
8720
7480
  }
8721
7481
  }
8722
- } else if (el.dataset.ohwEditable === "video") {
8723
- const video = getVideoEl(el);
8724
- if (video) {
8725
- found = true;
8726
- const onReady = () => {
8727
- video.onloadeddata = null;
8728
- video.onerror = null;
8729
- notify();
8730
- };
8731
- video.onloadeddata = onReady;
8732
- video.onerror = onReady;
8733
- applyVideoSrc(video, url);
8734
- }
8735
7482
  }
8736
7483
  });
8737
7484
  if (!found) notify();
@@ -8798,16 +7545,12 @@ function OhhwellsBridge() {
8798
7545
  sectionsJson = val;
8799
7546
  continue;
8800
7547
  }
8801
- if (applyVideoSettingNode(key, val)) continue;
8802
7548
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8803
7549
  if (el.dataset.ohwEditable === "image") {
8804
7550
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
8805
7551
  if (img) applyEditableImageSrc(img, val);
8806
7552
  } else if (el.dataset.ohwEditable === "bg-image") {
8807
7553
  el.style.backgroundImage = `url('${val}')`;
8808
- } else if (el.dataset.ohwEditable === "video") {
8809
- const video = getVideoEl(el);
8810
- if (video && video.src !== val) applyVideoSrc(video, val);
8811
7554
  } else if (el.dataset.ohwEditable === "link") {
8812
7555
  applyLinkHref(el, val);
8813
7556
  } else {
@@ -8821,8 +7564,6 @@ function OhhwellsBridge() {
8821
7564
  sectionsLoadedRef.current = true;
8822
7565
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
8823
7566
  }
8824
- editContentRef.current = { ...editContentRef.current, ...content };
8825
- reconcileNavbarItemsFromContent(editContentRef.current);
8826
7567
  enforceLinkHrefs();
8827
7568
  postToParentRef.current({ type: "ow:hydrate-done" });
8828
7569
  };
@@ -8830,7 +7571,6 @@ function OhhwellsBridge() {
8830
7571
  const handleDeactivate = (e) => {
8831
7572
  if (e.data?.type !== "ow:deactivate") return;
8832
7573
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
8833
- if (document.querySelector("[data-ohw-section-picker]")) return;
8834
7574
  if (linkPopoverOpenRef.current) {
8835
7575
  setLinkPopoverRef.current(null);
8836
7576
  return;
@@ -8840,32 +7580,12 @@ function OhhwellsBridge() {
8840
7580
  };
8841
7581
  window.addEventListener("message", handleDeactivate);
8842
7582
  const handleKeyDown = (e) => {
8843
- if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
8844
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
8845
- const navAnchor = getNavigationItemAnchor(activeElRef.current);
8846
- if (navAnchor) {
8847
- e.preventDefault();
8848
- selectAllTextInEditable(activeElRef.current);
8849
- refreshActiveCommandsRef.current();
8850
- return;
8851
- }
8852
- }
8853
7583
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
8854
7584
  setLinkPopoverRef.current(null);
8855
7585
  return;
8856
7586
  }
8857
7587
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
8858
- if (toolbarVariantRef.current === "select-frame") {
8859
- deselectRef.current();
8860
- return;
8861
- }
8862
- const parent = getNavigationSelectionParent(selectedElRef.current);
8863
- if (parent) {
8864
- e.preventDefault();
8865
- selectFrameRef.current(parent);
8866
- } else {
8867
- deselectRef.current();
8868
- }
7588
+ deselectRef.current();
8869
7589
  return;
8870
7590
  }
8871
7591
  if (e.key === "Escape" && activeElRef.current) {
@@ -8923,18 +7643,18 @@ function OhhwellsBridge() {
8923
7643
  if (hoveredItemElRef.current) {
8924
7644
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
8925
7645
  }
8926
- if (hoveredNavContainerRef.current) {
8927
- setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
8928
- }
8929
7646
  if (siblingHintElRef.current) {
8930
7647
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
8931
7648
  }
8932
7649
  if (hoveredImageRef.current) {
8933
7650
  const el = hoveredImageRef.current;
8934
7651
  const r2 = el.getBoundingClientRect();
8935
- setMediaHover(
8936
- (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
8937
- );
7652
+ postToParentRef.current({
7653
+ type: "ow:image-hover",
7654
+ key: el.dataset.ohwKey ?? "",
7655
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
7656
+ hasTextOverlap: hoveredImageHasTextOverlapRef.current
7657
+ });
8938
7658
  }
8939
7659
  };
8940
7660
  const handleSave = (e) => {
@@ -9023,39 +7743,19 @@ function OhhwellsBridge() {
9023
7743
  refreshActiveCommandsRef.current = handleSelectionChange;
9024
7744
  const handleDocMouseLeave = () => {
9025
7745
  hoveredImageRef.current = null;
9026
- setMediaHover(null);
9027
7746
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
9028
7747
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
9029
7748
  activeStateElRef.current = null;
9030
7749
  setToggleState(null);
9031
7750
  };
9032
- const handleImageUploading = (e) => {
9033
- if (e.data?.type !== "ow:image-uploading") return;
9034
- const { key, uploading } = e.data;
9035
- setUploadingRects((prev) => {
9036
- if (!uploading) {
9037
- if (!(key in prev)) return prev;
9038
- const next = { ...prev };
9039
- delete next[key];
9040
- return next;
9041
- }
9042
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
9043
- if (!el) return prev;
9044
- const r2 = getVisibleRect(el);
9045
- return {
9046
- ...prev,
9047
- [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
9048
- };
9049
- });
7751
+ const handleAnimLock = (e) => {
7752
+ if (e.data?.type !== "ow:anim-lock") return;
7753
+ const { key } = e.data;
9050
7754
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9051
7755
  const track = el.closest("[data-ohw-hover-pause]");
9052
- if (!track) return;
9053
- if (uploading) {
7756
+ if (track) {
9054
7757
  uploadLockedTracks.add(track);
9055
7758
  track.setAttribute("data-ohw-hover-paused", "");
9056
- } else {
9057
- uploadLockedTracks.delete(track);
9058
- track.removeAttribute("data-ohw-hover-paused");
9059
7759
  }
9060
7760
  });
9061
7761
  };
@@ -9097,7 +7797,7 @@ function OhhwellsBridge() {
9097
7797
  if (e.data?.type !== "ow:click-at") return;
9098
7798
  const { clientX, clientY } = e.data;
9099
7799
  const allImages = Array.from(
9100
- document.querySelectorAll(MEDIA_SELECTOR)
7800
+ document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]')
9101
7801
  ).filter((el) => !!el.closest("[data-ohw-editable-state]"));
9102
7802
  const stateCardImage = allImages.find((el) => {
9103
7803
  const ownerCard = el.closest("[data-ohw-editable-state]");
@@ -9112,55 +7812,21 @@ function OhhwellsBridge() {
9112
7812
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
9113
7813
  });
9114
7814
  if (stateCardImage) {
9115
- postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
7815
+ postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
9116
7816
  return;
9117
7817
  }
9118
7818
  const textEditable = Array.from(
9119
- document.querySelectorAll(NON_MEDIA_SELECTOR)
7819
+ document.querySelectorAll(
7820
+ '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
7821
+ )
9120
7822
  ).find((el) => {
9121
7823
  const r2 = el.getBoundingClientRect();
9122
7824
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
9123
7825
  });
9124
7826
  if (textEditable) {
9125
- const hrefCtx = getHrefKeyFromElement(textEditable);
9126
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
9127
- if (navAnchor) {
9128
- if (selectedElRef.current === navAnchor) {
9129
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
9130
- } else {
9131
- selectRef.current(navAnchor);
9132
- }
9133
- return;
9134
- }
9135
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
7827
+ activateRef.current(textEditable);
9136
7828
  return;
9137
7829
  }
9138
- const navContainer = document.querySelector("[data-ohw-nav-container]");
9139
- if (navContainer) {
9140
- const r2 = navContainer.getBoundingClientRect();
9141
- const pad = 8;
9142
- const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
9143
- if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
9144
- selectFrameRef.current(navContainer);
9145
- return;
9146
- }
9147
- }
9148
- const navRoot = document.querySelector("[data-ohw-nav-root]");
9149
- if (navRoot && navContainer) {
9150
- const rr = navRoot.getBoundingClientRect();
9151
- if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
9152
- const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
9153
- if (book) {
9154
- const br = book.getBoundingClientRect();
9155
- if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
9156
- deactivateRef.current();
9157
- return;
9158
- }
9159
- }
9160
- selectFrameRef.current(navContainer);
9161
- return;
9162
- }
9163
- }
9164
7830
  deactivateRef.current();
9165
7831
  };
9166
7832
  window.addEventListener("message", handleSave);
@@ -9170,7 +7836,7 @@ function OhhwellsBridge() {
9170
7836
  window.addEventListener("message", handleClearSchedulingWidget);
9171
7837
  window.addEventListener("message", handleRemoveSchedulingSection);
9172
7838
  window.addEventListener("message", handleImageUrl);
9173
- window.addEventListener("message", handleImageUploading);
7839
+ window.addEventListener("message", handleAnimLock);
9174
7840
  window.addEventListener("message", handleCanvasHeight);
9175
7841
  window.addEventListener("message", handleParentScroll);
9176
7842
  window.addEventListener("message", handlePointerSync);
@@ -9182,7 +7848,6 @@ function OhhwellsBridge() {
9182
7848
  };
9183
7849
  window.addEventListener("resize", handleViewportResize, { passive: true });
9184
7850
  document.addEventListener("click", handleClick, true);
9185
- document.addEventListener("dblclick", handleDblClick, true);
9186
7851
  document.addEventListener("paste", handlePaste, true);
9187
7852
  document.addEventListener("input", handleInput, true);
9188
7853
  document.addEventListener("mouseover", handleMouseOver, true);
@@ -9197,7 +7862,6 @@ function OhhwellsBridge() {
9197
7862
  window.addEventListener("scroll", handleScroll, true);
9198
7863
  return () => {
9199
7864
  document.removeEventListener("click", handleClick, true);
9200
- document.removeEventListener("dblclick", handleDblClick, true);
9201
7865
  document.removeEventListener("paste", handlePaste, true);
9202
7866
  document.removeEventListener("input", handleInput, true);
9203
7867
  document.removeEventListener("mouseover", handleMouseOver, true);
@@ -9217,7 +7881,7 @@ function OhhwellsBridge() {
9217
7881
  window.removeEventListener("message", handleClearSchedulingWidget);
9218
7882
  window.removeEventListener("message", handleRemoveSchedulingSection);
9219
7883
  window.removeEventListener("message", handleImageUrl);
9220
- window.removeEventListener("message", handleImageUploading);
7884
+ window.removeEventListener("message", handleAnimLock);
9221
7885
  window.removeEventListener("message", handleCanvasHeight);
9222
7886
  window.removeEventListener("message", handleParentScroll);
9223
7887
  window.removeEventListener("resize", handleViewportResize);
@@ -9231,7 +7895,7 @@ function OhhwellsBridge() {
9231
7895
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9232
7896
  };
9233
7897
  }, [isEditMode, refreshStateRules]);
9234
- useEffect7(() => {
7898
+ useEffect3(() => {
9235
7899
  const handler = (e) => {
9236
7900
  if (e.data?.type !== "ow:request-schedule-config") return;
9237
7901
  const insertAfterVal = e.data.insertAfter;
@@ -9247,7 +7911,7 @@ function OhhwellsBridge() {
9247
7911
  window.addEventListener("message", handler);
9248
7912
  return () => window.removeEventListener("message", handler);
9249
7913
  }, [processConfigRequest]);
9250
- useEffect7(() => {
7914
+ useEffect3(() => {
9251
7915
  if (!isEditMode) return;
9252
7916
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9253
7917
  el.removeAttribute("data-ohw-active-state");
@@ -9268,27 +7932,27 @@ function OhhwellsBridge() {
9268
7932
  const next = { ...prev, [pathKey]: sections };
9269
7933
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9270
7934
  });
9271
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
9272
- postToParent2({ type: "ow:request-site-pages" });
7935
+ postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
7936
+ postToParent({ type: "ow:request-site-pages" });
9273
7937
  }, 150);
9274
7938
  return () => {
9275
7939
  cancelAnimationFrame(raf);
9276
7940
  clearTimeout(timer);
9277
7941
  };
9278
- }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9279
- useEffect7(() => {
7942
+ }, [pathname, isEditMode, refreshStateRules, postToParent]);
7943
+ useEffect3(() => {
9280
7944
  scrollToHashSectionWhenReady();
9281
7945
  const onHashChange = () => scrollToHashSectionWhenReady();
9282
7946
  window.addEventListener("hashchange", onHashChange);
9283
7947
  return () => window.removeEventListener("hashchange", onHashChange);
9284
7948
  }, [pathname]);
9285
- const handleCommand = useCallback4((cmd) => {
7949
+ const handleCommand = useCallback2((cmd) => {
9286
7950
  document.execCommand(cmd, false);
9287
7951
  activeElRef.current?.focus();
9288
7952
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
9289
7953
  refreshActiveCommandsRef.current();
9290
7954
  }, []);
9291
- const handleStateChange = useCallback4((state) => {
7955
+ const handleStateChange = useCallback2((state) => {
9292
7956
  if (!activeStateElRef.current) return;
9293
7957
  const el = activeStateElRef.current;
9294
7958
  if (state === "Default") {
@@ -9301,22 +7965,18 @@ function OhhwellsBridge() {
9301
7965
  }
9302
7966
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9303
7967
  }, [deactivate]);
9304
- const closeLinkPopover = useCallback4(() => {
9305
- addNavAfterAnchorRef.current = null;
9306
- setLinkPopover(null);
9307
- }, []);
9308
- const openLinkPopoverForActive = useCallback4(() => {
7968
+ const closeLinkPopover = useCallback2(() => setLinkPopover(null), []);
7969
+ const openLinkPopoverForActive = useCallback2(() => {
9309
7970
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9310
7971
  if (!hrefCtx) return;
9311
7972
  bumpLinkPopoverGrace();
9312
7973
  setLinkPopover({
9313
7974
  key: hrefCtx.key,
9314
- mode: "edit",
9315
- target: getLinkHref2(hrefCtx.anchor)
7975
+ target: getLinkHref(hrefCtx.anchor)
9316
7976
  });
9317
7977
  deactivate();
9318
7978
  }, [deactivate]);
9319
- const openLinkPopoverForSelected = useCallback4(() => {
7979
+ const openLinkPopoverForSelected = useCallback2(() => {
9320
7980
  const anchor = selectedElRef.current;
9321
7981
  if (!anchor) return;
9322
7982
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9324,156 +7984,51 @@ function OhhwellsBridge() {
9324
7984
  bumpLinkPopoverGrace();
9325
7985
  setLinkPopover({
9326
7986
  key,
9327
- mode: "edit",
9328
- target: getLinkHref2(anchor)
7987
+ target: getLinkHref(anchor)
9329
7988
  });
9330
7989
  deselect();
9331
7990
  }, [deselect]);
9332
- const handleLinkPopoverSubmit = useCallback4(
7991
+ const handleLinkPopoverSubmit = useCallback2(
9333
7992
  (target) => {
9334
- const session = linkPopoverSessionRef.current;
9335
- if (!session) return;
9336
- if (session.intent === "add-nav") {
9337
- const { pageRoute, sectionId } = parseTarget(target);
9338
- const page = resolvePage(pageRoute, sitePages);
9339
- const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9340
- const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9341
- const label = section?.label ?? page.title;
9342
- const afterAnchor = addNavAfterAnchorRef.current;
9343
- const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(target, label, afterAnchor);
9344
- applyLinkByKey(hrefKey, target);
9345
- document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9346
- el.textContent = label;
9347
- });
9348
- const navCount = String(index + 1);
9349
- const orderJson = JSON.stringify(order);
9350
- editContentRef.current = {
9351
- ...editContentRef.current,
9352
- [hrefKey]: target,
9353
- [labelKey]: label,
9354
- [NAV_COUNT_KEY]: navCount,
9355
- [NAV_ORDER_KEY]: orderJson
9356
- };
9357
- postToParent2({
9358
- type: "ow:change",
9359
- nodes: [
9360
- { key: hrefKey, text: target },
9361
- { key: labelKey, text: label },
9362
- { key: NAV_COUNT_KEY, text: navCount },
9363
- { key: NAV_ORDER_KEY, text: orderJson }
9364
- ]
9365
- });
9366
- addNavAfterAnchorRef.current = null;
9367
- setLinkPopover(null);
9368
- enforceLinkHrefs();
9369
- requestAnimationFrame(() => {
9370
- selectRef.current(anchor);
9371
- });
9372
- return;
9373
- }
9374
- const { key } = session;
7993
+ if (!linkPopover) return;
7994
+ const { key } = linkPopover;
9375
7995
  applyLinkByKey(key, target);
9376
- postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
7996
+ postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
9377
7997
  setLinkPopover(null);
9378
7998
  },
9379
- [postToParent2, sitePages, sectionsByPath]
7999
+ [linkPopover, postToParent]
9380
8000
  );
9381
8001
  const showEditLink = toolbarShowEditLink;
9382
8002
  const currentSections = sectionsByPath[pathname] ?? [];
9383
8003
  linkPopoverOpenRef.current = linkPopover !== null;
9384
- const handleMediaReplace = useCallback4(
9385
- (key) => {
9386
- postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9387
- },
9388
- [postToParent2, mediaHover?.elementType]
9389
- );
9390
- const handleVideoSettingsChange = useCallback4(
9391
- (key, settings) => {
9392
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9393
- const video = getVideoEl(el);
9394
- if (!video) return;
9395
- if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
9396
- if (typeof settings.muted === "boolean") video.muted = settings.muted;
9397
- syncVideoPlayback(video);
9398
- postToParent2({
9399
- type: "ow:change",
9400
- nodes: [
9401
- { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
9402
- { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
9403
- ]
9404
- });
9405
- setMediaHover(
9406
- (prev) => prev && prev.key === key ? { ...prev, videoAutoplay: video.autoplay, videoMuted: video.muted } : prev
9407
- );
9408
- });
9409
- },
9410
- [postToParent2]
9411
- );
9412
- const handleMediaFadeOutComplete = useCallback4((key) => {
9413
- setUploadingRects((prev) => {
9414
- if (!(key in prev)) return prev;
9415
- const next = { ...prev };
9416
- delete next[key];
9417
- return next;
9418
- });
9419
- }, []);
9420
- return bridgeRoot ? createPortal2(
9421
- /* @__PURE__ */ jsxs13(Fragment5, { children: [
9422
- /* @__PURE__ */ jsx23("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9423
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx23(
9424
- MediaOverlay,
9425
- {
9426
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
9427
- isUploading: true,
9428
- fadingOut,
9429
- onFadeOutComplete: handleMediaFadeOutComplete,
9430
- onReplace: handleMediaReplace
9431
- },
9432
- `uploading-${key}`
9433
- )),
9434
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx23(
9435
- MediaOverlay,
9436
- {
9437
- hover: mediaHover,
9438
- isUploading: false,
9439
- onReplace: handleMediaReplace,
9440
- onVideoSettingsChange: handleVideoSettingsChange
9441
- }
9442
- ),
9443
- siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9444
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9445
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx23(
9446
- NavbarContainerChrome,
9447
- {
9448
- rect: toolbarRect,
9449
- onAdd: handleAddTopLevelNavItem
9450
- }
9451
- ),
9452
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9453
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx23(
8004
+ return bridgeRoot ? createPortal(
8005
+ /* @__PURE__ */ jsxs11(Fragment3, { children: [
8006
+ /* @__PURE__ */ jsx21("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8007
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8008
+ hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8009
+ toolbarRect && toolbarVariant === "link-action" && /* @__PURE__ */ jsx21(
9454
8010
  ItemInteractionLayer,
9455
8011
  {
9456
8012
  rect: toolbarRect,
9457
8013
  elRef: glowElRef,
9458
8014
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9459
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
8015
+ showHandle: Boolean(reorderHrefKey),
9460
8016
  dragDisabled: reorderDragDisabled,
9461
8017
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9462
8018
  onDragHandleDragStart: handleItemDragStart,
9463
8019
  onDragHandleDragEnd: handleItemDragEnd,
9464
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx23(
8020
+ toolbar: isItemDragging ? void 0 : /* @__PURE__ */ jsx21(
9465
8021
  ItemActionToolbar,
9466
8022
  {
9467
8023
  onEditLink: openLinkPopoverForSelected,
9468
- addItemDisabled: true,
9469
- editLinkDisabled: false,
9470
- moreDisabled: true
8024
+ onAddItem: handleAddNavigationItem,
8025
+ addItemDisabled: false
9471
8026
  }
9472
- ) : void 0
8027
+ )
9473
8028
  }
9474
8029
  ),
9475
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs13(Fragment5, { children: [
9476
- /* @__PURE__ */ jsx23(
8030
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs11(Fragment3, { children: [
8031
+ /* @__PURE__ */ jsx21(
9477
8032
  EditGlowChrome,
9478
8033
  {
9479
8034
  rect: toolbarRect,
@@ -9482,7 +8037,7 @@ function OhhwellsBridge() {
9482
8037
  dragDisabled: reorderDragDisabled
9483
8038
  }
9484
8039
  ),
9485
- /* @__PURE__ */ jsx23(
8040
+ /* @__PURE__ */ jsx21(
9486
8041
  FloatingToolbar,
9487
8042
  {
9488
8043
  rect: toolbarRect,
@@ -9495,7 +8050,7 @@ function OhhwellsBridge() {
9495
8050
  }
9496
8051
  )
9497
8052
  ] }),
9498
- maxBadge && /* @__PURE__ */ jsxs13(
8053
+ maxBadge && /* @__PURE__ */ jsxs11(
9499
8054
  "div",
9500
8055
  {
9501
8056
  "data-ohw-max-badge": "",
@@ -9521,7 +8076,7 @@ function OhhwellsBridge() {
9521
8076
  ]
9522
8077
  }
9523
8078
  ),
9524
- toggleState && !linkPopover && /* @__PURE__ */ jsx23(
8079
+ toggleState && /* @__PURE__ */ jsx21(
9525
8080
  StateToggle,
9526
8081
  {
9527
8082
  rect: toggleState.rect,
@@ -9530,15 +8085,15 @@ function OhhwellsBridge() {
9530
8085
  onStateChange: handleStateChange
9531
8086
  }
9532
8087
  ),
9533
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs13(
8088
+ sectionGap && /* @__PURE__ */ jsxs11(
9534
8089
  "div",
9535
8090
  {
9536
8091
  "data-ohw-section-insert-line": "",
9537
8092
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9538
8093
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
9539
8094
  children: [
9540
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9541
- /* @__PURE__ */ jsx23(
8095
+ /* @__PURE__ */ jsx21("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8096
+ /* @__PURE__ */ jsx21(
9542
8097
  Badge,
9543
8098
  {
9544
8099
  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",
@@ -9551,52 +8106,26 @@ function OhhwellsBridge() {
9551
8106
  children: "Add Section"
9552
8107
  }
9553
8108
  ),
9554
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8109
+ /* @__PURE__ */ jsx21("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9555
8110
  ]
9556
8111
  }
9557
8112
  ),
9558
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx23(
8113
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx21(
9559
8114
  LinkPopover,
9560
8115
  {
9561
8116
  panelRef: linkPopoverPanelRef,
9562
8117
  portalContainer: dialogPortalContainer,
9563
8118
  open: true,
9564
- mode: linkPopover.mode ?? "edit",
8119
+ mode: "edit",
9565
8120
  pages: sitePages,
9566
8121
  sections: currentSections,
9567
8122
  sectionsByPath,
9568
8123
  initialTarget: linkPopover.target,
9569
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
9570
8124
  onClose: closeLinkPopover,
9571
8125
  onSubmit: handleLinkPopoverSubmit
9572
8126
  },
9573
- linkPopover.key
9574
- ) : null,
9575
- sectionGap && /* @__PURE__ */ jsxs13(
9576
- "div",
9577
- {
9578
- "data-ohw-section-insert-line": "",
9579
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9580
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
9581
- children: [
9582
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9583
- /* @__PURE__ */ jsx23(
9584
- Badge,
9585
- {
9586
- 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",
9587
- onClick: () => {
9588
- window.parent.postMessage(
9589
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
9590
- "*"
9591
- );
9592
- },
9593
- children: "Add Section"
9594
- }
9595
- ),
9596
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9597
- ]
9598
- }
9599
- )
8127
+ `${linkPopover.key}-${pathname}`
8128
+ ) : null
9600
8129
  ] }),
9601
8130
  bridgeRoot
9602
8131
  ) : null;