@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.cjs CHANGED
@@ -60,18 +60,15 @@ __export(index_exports, {
60
60
  module.exports = __toCommonJS(index_exports);
61
61
 
62
62
  // src/OhhwellsBridge.tsx
63
- var import_react8 = __toESM(require("react"), 1);
63
+ var import_react6 = __toESM(require("react"), 1);
64
64
  var import_client = require("react-dom/client");
65
- var import_react_dom2 = require("react-dom");
65
+ var import_react_dom = require("react-dom");
66
66
 
67
67
  // src/linkHrefStore.ts
68
68
  var linkHrefStore = /* @__PURE__ */ new Map();
69
69
  function setStoredLinkHref(key, href) {
70
70
  linkHrefStore.set(key, href);
71
71
  }
72
- function getStoredLinkHref(key) {
73
- return linkHrefStore.get(key);
74
- }
75
72
  function enforceLinkHrefs() {
76
73
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
77
74
  const key = el.getAttribute("data-ohw-href-key") ?? "";
@@ -113,7 +110,6 @@ var import_react3 = require("react");
113
110
  var import_react2 = require("react");
114
111
  var import_radix_ui = require("radix-ui");
115
112
  var import_jsx_runtime = require("react/jsx-runtime");
116
- var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
117
113
  function Spinner() {
118
114
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
119
115
  "svg",
@@ -147,17 +143,12 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
147
143
  const [error, setError] = (0, import_react2.useState)(null);
148
144
  const isBook = title === "Confirm your spot";
149
145
  const handleSubmit = async () => {
150
- const trimmed = email.trim();
151
- if (!trimmed) return;
152
- if (!isValidEmail(trimmed)) {
153
- setError("Please enter a valid email address.");
154
- return;
155
- }
146
+ if (!email.trim()) return;
156
147
  setLoading(true);
157
148
  setError(null);
158
149
  try {
159
- await onSubmit(trimmed);
160
- setSubmittedEmail(trimmed);
150
+ await onSubmit(email.trim());
151
+ setSubmittedEmail(email.trim());
161
152
  setSuccess(true);
162
153
  } catch (e) {
163
154
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -232,10 +223,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
232
223
  {
233
224
  type: "email",
234
225
  value: email,
235
- onChange: (e) => {
236
- setEmail(e.target.value);
237
- if (error) setError(null);
238
- },
226
+ onChange: (e) => setEmail(e.target.value),
239
227
  onKeyDown: handleKeyDown,
240
228
  placeholder: "hello@gmail.com",
241
229
  autoFocus: true,
@@ -292,20 +280,12 @@ function formatClassTime(cls) {
292
280
  const em = endTotal % 60;
293
281
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
294
282
  }
295
- function formatBookingLabel(cls) {
296
- const price = cls.price;
297
- const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
298
- return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
299
- }
300
283
  function getBookingsOnDate(cls, date) {
301
284
  if (!cls.bookings?.length) return 0;
302
- const target = new Date(date);
303
- target.setHours(0, 0, 0, 0);
285
+ const ds = date.toISOString().split("T")[0];
304
286
  return cls.bookings.filter((b) => {
305
287
  try {
306
- const bookingDate = new Date(b.classDate);
307
- bookingDate.setHours(0, 0, 0, 0);
308
- return bookingDate.getTime() === target.getTime();
288
+ return new Date(b.classDate).toISOString().split("T")[0] === ds;
309
289
  } catch {
310
290
  return false;
311
291
  }
@@ -609,7 +589,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
609
589
  if (!cls.id) return;
610
590
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
611
591
  },
612
- children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
592
+ children: isFull ? "Join Waitlist" : "Book Now"
613
593
  }
614
594
  )
615
595
  ] })
@@ -651,17 +631,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
651
631
  const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
652
632
  const [modalState, setModalState] = (0, import_react3.useState)(null);
653
633
  const switchScheduleIdRef = (0, import_react3.useRef)(null);
654
- const liveScheduleIdRef = (0, import_react3.useRef)(null);
655
- const refetchLiveSchedule = (0, import_react3.useCallback)(async () => {
656
- const id = liveScheduleIdRef.current;
657
- if (!id) return;
658
- try {
659
- const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
660
- const data = await res.json();
661
- if (data?.id) setSchedule(data);
662
- } catch {
663
- }
664
- }, []);
665
634
  const dates = (0, import_react3.useMemo)(() => {
666
635
  const today = /* @__PURE__ */ new Date();
667
636
  today.setHours(0, 0, 0, 0);
@@ -681,18 +650,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
681
650
  }
682
651
  }
683
652
  }, [schedule, dates]);
684
- (0, import_react3.useEffect)(() => {
685
- if (typeof window === "undefined") return;
686
- const onVisible = () => {
687
- if (!document.hidden) void refetchLiveSchedule();
688
- };
689
- window.addEventListener("focus", refetchLiveSchedule);
690
- document.addEventListener("visibilitychange", onVisible);
691
- return () => {
692
- window.removeEventListener("focus", refetchLiveSchedule);
693
- document.removeEventListener("visibilitychange", onVisible);
694
- };
695
- }, [refetchLiveSchedule]);
696
653
  (0, import_react3.useEffect)(() => {
697
654
  if (typeof window === "undefined") return;
698
655
  const isInEditor = window.self !== window.top;
@@ -754,7 +711,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
754
711
  setLoading(false);
755
712
  return;
756
713
  }
757
- liveScheduleIdRef.current = effectiveId;
714
+ ;
758
715
  (async () => {
759
716
  try {
760
717
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -811,14 +768,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
811
768
  const data = await res.json().catch(() => ({}));
812
769
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
813
770
  }
814
- await refetchLiveSchedule();
815
771
  };
816
772
  const handleReplaceSchedule = () => {
817
773
  setIsHovered(false);
818
- window.parent.postMessage(
819
- { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
820
- "*"
821
- );
774
+ if (schedule) {
775
+ window.parent.postMessage({
776
+ type: "ow:schedule-connected",
777
+ schedule: { id: schedule.id, name: schedule.name },
778
+ insertAfter
779
+ }, "*");
780
+ } else {
781
+ window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
782
+ }
822
783
  };
823
784
  if (!inEditor && !loading && !schedule) return null;
824
785
  const sectionId = `scheduling-${insertAfter}`;
@@ -861,19 +822,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
861
822
  ),
862
823
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
863
824
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
864
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
865
- "h2",
866
- {
867
- className: "font-display text-center m-0 text-(--color-dark,#200C02)",
868
- style: {
869
- fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
870
- fontWeight: "var(--font-weight-heading, 400)",
871
- lineHeight: 1.05,
872
- letterSpacing: "-0.02em"
873
- },
874
- children: schedule?.name ?? "Book an appointment"
875
- }
876
- ),
825
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
877
826
  schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
878
827
  ] }),
879
828
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4459,7 +4408,6 @@ function ItemActionToolbar({
4459
4408
  onEditLink,
4460
4409
  onAddItem,
4461
4410
  onMore,
4462
- editLinkDisabled = false,
4463
4411
  addItemDisabled = true,
4464
4412
  moreDisabled = true,
4465
4413
  tooltipSide = "bottom"
@@ -4468,12 +4416,10 @@ function ItemActionToolbar({
4468
4416
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4469
4417
  ToolbarActionTooltip,
4470
4418
  {
4471
- label: "Edit link",
4419
+ label: "Add link",
4472
4420
  side: tooltipSide,
4473
- disabled: editLinkDisabled,
4474
4421
  buttonProps: {
4475
4422
  onMouseDown: (e) => {
4476
- if (editLinkDisabled) return;
4477
4423
  e.preventDefault();
4478
4424
  e.stopPropagation();
4479
4425
  onEditLink?.();
@@ -4645,223 +4591,9 @@ function ItemInteractionLayer({
4645
4591
  );
4646
4592
  }
4647
4593
 
4648
- // src/ui/MediaOverlay.tsx
4649
- var React6 = __toESM(require("react"), 1);
4650
- var import_lucide_react3 = require("lucide-react");
4651
-
4652
- // src/ui/button.tsx
4653
- var React5 = __toESM(require("react"), 1);
4654
- var import_radix_ui4 = require("radix-ui");
4655
- var import_jsx_runtime10 = require("react/jsx-runtime");
4656
- var buttonVariants = cva(
4657
- "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",
4658
- {
4659
- variants: {
4660
- variant: {
4661
- default: "bg-primary text-primary-foreground hover:opacity-90",
4662
- outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
4663
- ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
4664
- },
4665
- size: {
4666
- default: "h-9",
4667
- sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
4668
- }
4669
- },
4670
- defaultVariants: {
4671
- variant: "default",
4672
- size: "default"
4673
- }
4674
- }
4675
- );
4676
- var Button = React5.forwardRef(
4677
- ({ className, variant, size, asChild = false, ...props }, ref) => {
4678
- const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
4679
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4680
- Comp,
4681
- {
4682
- ref,
4683
- "data-slot": "button",
4684
- className: cn(buttonVariants({ variant, size, className })),
4685
- ...props
4686
- }
4687
- );
4688
- }
4689
- );
4690
- Button.displayName = "Button";
4691
-
4692
- // src/ui/MediaOverlay.tsx
4693
- var import_jsx_runtime11 = require("react/jsx-runtime");
4694
- var MEDIA_UPLOAD_FADE_MS = 300;
4695
- var VIDEO_SETTINGS_BAR_INSET = 8;
4696
- var OVERLAY_BUTTON_STYLE = {
4697
- pointerEvents: "auto",
4698
- fontFamily: "Inter, sans-serif",
4699
- fontSize: 12,
4700
- color: "#000"
4701
- };
4702
- var SKELETON_CSS = `
4703
- @keyframes ohw-media-shimmer {
4704
- 0% { transform: translateX(-100%); }
4705
- 100% { transform: translateX(100%); }
4706
- }
4707
- [data-ohw-media-skeleton] {
4708
- overflow: hidden;
4709
- background-color: #d1d5db; /* tailwind gray-300 */
4710
- }
4711
- [data-ohw-media-skeleton]::after {
4712
- content: "";
4713
- position: absolute;
4714
- inset: 0;
4715
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
4716
- animation: ohw-media-shimmer 1.4s infinite;
4717
- }
4718
- `;
4719
- function MediaOverlay({
4720
- hover,
4721
- isUploading,
4722
- fadingOut = false,
4723
- onFadeOutComplete,
4724
- onReplace,
4725
- onVideoSettingsChange
4726
- }) {
4727
- const { rect } = hover;
4728
- const skeletonRef = React6.useRef(null);
4729
- const isVideo = hover.elementType === "video";
4730
- const autoplay = hover.videoAutoplay ?? true;
4731
- const muted = hover.videoMuted ?? true;
4732
- const box = {
4733
- position: "fixed",
4734
- top: rect.top,
4735
- left: rect.left,
4736
- width: rect.width,
4737
- height: rect.height,
4738
- zIndex: 2147483646
4739
- };
4740
- React6.useEffect(() => {
4741
- if (!isUploading || !fadingOut || !skeletonRef.current) return;
4742
- const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4743
- duration: MEDIA_UPLOAD_FADE_MS,
4744
- easing: "ease-out",
4745
- fill: "forwards"
4746
- });
4747
- anim.finished.then(() => onFadeOutComplete?.(hover.key)).catch(() => onFadeOutComplete?.(hover.key));
4748
- return () => anim.cancel();
4749
- }, [isUploading, fadingOut, onFadeOutComplete, hover.key]);
4750
- if (isUploading) {
4751
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4752
- "div",
4753
- {
4754
- ref: skeletonRef,
4755
- "data-ohw-bridge": "",
4756
- "data-ohw-media-overlay": "",
4757
- "data-ohw-media-skeleton": "",
4758
- "aria-hidden": true,
4759
- style: { ...box, pointerEvents: "none" },
4760
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: SKELETON_CSS })
4761
- }
4762
- );
4763
- }
4764
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4765
- "div",
4766
- {
4767
- "data-ohw-bridge": "",
4768
- "data-ohw-media-overlay": "",
4769
- className: "flex items-center justify-center gap-1.5",
4770
- style: {
4771
- position: "fixed",
4772
- top: rect.top + VIDEO_SETTINGS_BAR_INSET,
4773
- left: rect.left,
4774
- width: rect.width,
4775
- zIndex: 2147483647,
4776
- pointerEvents: "auto"
4777
- },
4778
- onClick: (e) => e.stopPropagation(),
4779
- children: [
4780
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4781
- Button,
4782
- {
4783
- "data-ohw-media-overlay": "",
4784
- variant: "outline",
4785
- size: "sm",
4786
- className: "cursor-pointer hover:bg-background",
4787
- style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4788
- "aria-label": autoplay ? "Disable autoplay" : "Enable autoplay",
4789
- title: autoplay ? "Autoplay on \u2014 click to disable" : "Autoplay off \u2014 click to enable",
4790
- onMouseDown: (e) => e.preventDefault(),
4791
- onClick: (e) => {
4792
- e.stopPropagation();
4793
- onVideoSettingsChange?.(hover.key, { autoplay: !autoplay });
4794
- },
4795
- children: autoplay ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Pause, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Play, { size: 14 })
4796
- }
4797
- ),
4798
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4799
- Button,
4800
- {
4801
- "data-ohw-media-overlay": "",
4802
- variant: "outline",
4803
- size: "sm",
4804
- className: "cursor-pointer hover:bg-background",
4805
- style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4806
- "aria-label": muted ? "Unmute video" : "Mute video",
4807
- title: muted ? "Muted \u2014 click to unmute" : "Sound on \u2014 click to mute",
4808
- onMouseDown: (e) => e.preventDefault(),
4809
- onClick: (e) => {
4810
- e.stopPropagation();
4811
- onVideoSettingsChange?.(hover.key, { muted: !muted });
4812
- },
4813
- children: muted ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.VolumeX, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Volume2, { size: 14 })
4814
- }
4815
- )
4816
- ]
4817
- }
4818
- ) : null;
4819
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4820
- settingsBar,
4821
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4822
- "div",
4823
- {
4824
- "data-ohw-bridge": "",
4825
- "data-ohw-media-overlay": "",
4826
- className: "flex items-center justify-center cursor-pointer",
4827
- style: {
4828
- ...box,
4829
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4830
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4831
- // in-document, pointer-events does it natively. The button below opts back in, so
4832
- // Replace still works.
4833
- pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4834
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4835
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4836
- },
4837
- onClick: () => onReplace(hover.key),
4838
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4839
- Button,
4840
- {
4841
- "data-ohw-media-overlay": "",
4842
- variant: "outline",
4843
- size: "sm",
4844
- className: "gap-1.5 cursor-pointer hover:bg-background",
4845
- style: OVERLAY_BUTTON_STYLE,
4846
- onMouseDown: (e) => e.preventDefault(),
4847
- onClick: (e) => {
4848
- e.stopPropagation();
4849
- onReplace(hover.key);
4850
- },
4851
- children: [
4852
- isVideo ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.ImageIcon, { size: 14 }),
4853
- isVideo ? "Replace video" : "Replace image"
4854
- ]
4855
- }
4856
- )
4857
- }
4858
- )
4859
- ] });
4860
- }
4861
-
4862
4594
  // src/OhhwellsBridge.tsx
4863
- var import_react_dom3 = require("react-dom");
4864
- var import_navigation2 = require("next/navigation");
4595
+ var import_react_dom2 = require("react-dom");
4596
+ var import_navigation = require("next/navigation");
4865
4597
 
4866
4598
  // src/lib/session-search.ts
4867
4599
  var STORAGE_KEY = "ohw-preserved-search";
@@ -5137,30 +4869,62 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5137
4869
  tick();
5138
4870
  }
5139
4871
 
5140
- // src/ui/link-modal/LinkPopover.tsx
5141
- var import_react7 = require("react");
4872
+ // src/ui/dialog.tsx
4873
+ var React5 = __toESM(require("react"), 1);
4874
+ var import_radix_ui4 = require("radix-ui");
4875
+
4876
+ // src/ui/icons.tsx
4877
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4878
+ function IconX({ className, ...props }) {
4879
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4880
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M18 6 6 18" }),
4881
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m6 6 12 12" })
4882
+ ] });
4883
+ }
4884
+ function IconChevronDown({ className, ...props }) {
4885
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m6 9 6 6 6-6" }) });
4886
+ }
4887
+ function IconFile({ className, ...props }) {
4888
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4889
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4890
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4891
+ ] });
4892
+ }
4893
+ function IconInfo({ className, ...props }) {
4894
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4895
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
4896
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 16v-4" }),
4897
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 8h.01" })
4898
+ ] });
4899
+ }
4900
+ function IconArrowRight({ className, ...props }) {
4901
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4902
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M5 12h14" }),
4903
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m12 5 7 7-7 7" })
4904
+ ] });
4905
+ }
4906
+ function IconSection({ className, ...props }) {
4907
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4908
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4909
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4910
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4911
+ ] });
4912
+ }
5142
4913
 
5143
4914
  // src/ui/dialog.tsx
5144
- var React7 = __toESM(require("react"), 1);
5145
- var import_radix_ui5 = require("radix-ui");
5146
- var import_lucide_react4 = require("lucide-react");
5147
- var import_jsx_runtime12 = require("react/jsx-runtime");
5148
- function Dialog2({
5149
- ...props
5150
- }) {
5151
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
4915
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4916
+ function Dialog2({ ...props }) {
4917
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui4.Dialog.Root, { "data-slot": "dialog", ...props });
5152
4918
  }
5153
- function DialogPortal({
5154
- ...props
5155
- }) {
5156
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
4919
+ function DialogPortal({ ...props }) {
4920
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui4.Dialog.Portal, { ...props });
5157
4921
  }
5158
4922
  function DialogOverlay({
5159
4923
  className,
5160
4924
  ...props
5161
4925
  }) {
5162
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5163
- import_radix_ui5.Dialog.Overlay,
4926
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4927
+ import_radix_ui4.Dialog.Overlay,
5164
4928
  {
5165
4929
  "data-slot": "dialog-overlay",
5166
4930
  "data-ohw-link-modal-root": "",
@@ -5169,118 +4933,120 @@ function DialogOverlay({
5169
4933
  }
5170
4934
  );
5171
4935
  }
5172
- var DialogContent = React7.forwardRef(
5173
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5174
- const positionMode = container ? "absolute" : "fixed";
5175
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5176
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5177
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5178
- import_radix_ui5.Dialog.Content,
5179
- {
5180
- ref,
5181
- "data-slot": "dialog-content",
5182
- className: cn(
5183
- positionMode,
5184
- "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",
5185
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5186
- container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5187
- className
5188
- ),
5189
- onMouseDown: (e) => e.stopPropagation(),
5190
- ...props,
5191
- children: [
5192
- children,
5193
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5194
- import_radix_ui5.Dialog.Close,
5195
- {
5196
- type: "button",
5197
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5198
- "aria-label": "Close",
5199
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.X, { size: 16, "aria-hidden": true })
5200
- }
5201
- ) : null
5202
- ]
5203
- }
5204
- )
5205
- ] });
5206
- }
5207
- );
5208
- DialogContent.displayName = import_radix_ui5.Dialog.Content.displayName;
5209
- function DialogHeader({
5210
- className,
5211
- ...props
5212
- }) {
5213
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4936
+ var DialogContent = React5.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4937
+ const positionMode = container ? "absolute" : "fixed";
4938
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(DialogPortal, { container: container ?? void 0, children: [
4939
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4940
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4941
+ import_radix_ui4.Dialog.Content,
4942
+ {
4943
+ ref,
4944
+ "data-slot": "dialog-content",
4945
+ className: cn(
4946
+ positionMode,
4947
+ "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",
4948
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
4949
+ container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
4950
+ className
4951
+ ),
4952
+ onMouseDown: (e) => e.stopPropagation(),
4953
+ ...props,
4954
+ children: [
4955
+ children,
4956
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4957
+ import_radix_ui4.Dialog.Close,
4958
+ {
4959
+ type: "button",
4960
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
4961
+ "aria-label": "Close",
4962
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconX, { "aria-hidden": true })
4963
+ }
4964
+ ) : null
4965
+ ]
4966
+ }
4967
+ )
4968
+ ] });
4969
+ });
4970
+ DialogContent.displayName = import_radix_ui4.Dialog.Content.displayName;
4971
+ function DialogHeader({ className, ...props }) {
4972
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5214
4973
  }
5215
- function DialogFooter({
5216
- className,
5217
- ...props
5218
- }) {
5219
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5220
- "div",
5221
- {
5222
- className: cn("flex items-center justify-end gap-2", className),
5223
- ...props
5224
- }
5225
- );
4974
+ function DialogFooter({ className, ...props }) {
4975
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5226
4976
  }
5227
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5228
- import_radix_ui5.Dialog.Title,
4977
+ var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4978
+ import_radix_ui4.Dialog.Title,
5229
4979
  {
5230
4980
  ref,
5231
- className: cn(
5232
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
5233
- className
5234
- ),
4981
+ className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5235
4982
  ...props
5236
4983
  }
5237
4984
  ));
5238
- DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5239
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5240
- import_radix_ui5.Dialog.Description,
4985
+ DialogTitle.displayName = import_radix_ui4.Dialog.Title.displayName;
4986
+ var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4987
+ import_radix_ui4.Dialog.Description,
5241
4988
  {
5242
4989
  ref,
5243
4990
  className: cn("text-sm text-muted-foreground", className),
5244
4991
  ...props
5245
4992
  }
5246
4993
  ));
5247
- DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
5248
- var DialogClose = import_radix_ui5.Dialog.Close;
4994
+ DialogDescription.displayName = import_radix_ui4.Dialog.Description.displayName;
4995
+ var DialogClose = import_radix_ui4.Dialog.Close;
5249
4996
 
5250
- // src/ui/link-modal/LinkEditorPanel.tsx
5251
- var import_lucide_react8 = require("lucide-react");
4997
+ // src/ui/button.tsx
4998
+ var React6 = __toESM(require("react"), 1);
4999
+ var import_radix_ui5 = require("radix-ui");
5000
+ var import_jsx_runtime12 = require("react/jsx-runtime");
5001
+ var buttonVariants = cva(
5002
+ "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",
5003
+ {
5004
+ variants: {
5005
+ variant: {
5006
+ default: "bg-primary text-primary-foreground hover:opacity-90",
5007
+ outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
5008
+ ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
5009
+ },
5010
+ size: {
5011
+ default: "h-9",
5012
+ sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
5013
+ }
5014
+ },
5015
+ defaultVariants: {
5016
+ variant: "default",
5017
+ size: "default"
5018
+ }
5019
+ }
5020
+ );
5021
+ var Button = React6.forwardRef(
5022
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
5023
+ const Comp = asChild ? import_radix_ui5.Slot.Root : "button";
5024
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5025
+ Comp,
5026
+ {
5027
+ ref,
5028
+ "data-slot": "button",
5029
+ className: cn(buttonVariants({ variant, size, className })),
5030
+ ...props
5031
+ }
5032
+ );
5033
+ }
5034
+ );
5035
+ Button.displayName = "Button";
5252
5036
 
5253
5037
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5254
- var import_lucide_react5 = require("lucide-react");
5255
5038
  var import_jsx_runtime13 = require("react/jsx-runtime");
5256
- function DestinationBreadcrumb({
5257
- pageTitle,
5258
- sectionLabel
5259
- }) {
5039
+ function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5260
5040
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5261
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5041
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5262
5042
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
5263
5043
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
5264
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5265
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5044
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5045
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5266
5046
  ] }),
5267
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5268
- import_lucide_react5.ArrowRight,
5269
- {
5270
- size: 16,
5271
- className: "shrink-0 text-muted-foreground",
5272
- "aria-hidden": true
5273
- }
5274
- ),
5047
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5275
5048
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5276
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5277
- import_lucide_react5.GalleryVertical,
5278
- {
5279
- size: 16,
5280
- className: "shrink-0 text-foreground",
5281
- "aria-hidden": true
5282
- }
5283
- ),
5049
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5284
5050
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5285
5051
  ] })
5286
5052
  ] })
@@ -5288,13 +5054,8 @@ function DestinationBreadcrumb({
5288
5054
  }
5289
5055
 
5290
5056
  // src/ui/link-modal/SectionTreeItem.tsx
5291
- var import_lucide_react6 = require("lucide-react");
5292
5057
  var import_jsx_runtime14 = require("react/jsx-runtime");
5293
- function SectionTreeItem({
5294
- section,
5295
- onSelect,
5296
- selected
5297
- }) {
5058
+ function SectionTreeItem({ section, onSelect, selected }) {
5298
5059
  const interactive = Boolean(onSelect);
5299
5060
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5300
5061
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
@@ -5322,28 +5083,30 @@ function SectionTreeItem({
5322
5083
  interactive && selected && "border-primary"
5323
5084
  ),
5324
5085
  children: [
5325
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5326
- import_lucide_react6.GalleryVertical,
5327
- {
5328
- size: 16,
5329
- className: "shrink-0 text-foreground",
5330
- "aria-hidden": true
5331
- }
5332
- ),
5086
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5333
5087
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5334
5088
  ]
5335
5089
  }
5336
5090
  )
5337
5091
  ] });
5338
5092
  }
5093
+ function SectionPickerList({ sections, onSelect }) {
5094
+ if (sections.length === 0) {
5095
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5096
+ }
5097
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1", children: [
5098
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5099
+ sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionTreeItem, { section, onSelect }, section.id))
5100
+ ] });
5101
+ }
5339
5102
 
5340
5103
  // src/ui/link-modal/UrlOrPageInput.tsx
5341
5104
  var import_react4 = require("react");
5342
5105
 
5343
5106
  // src/ui/input.tsx
5344
- var React8 = __toESM(require("react"), 1);
5107
+ var React7 = __toESM(require("react"), 1);
5345
5108
  var import_jsx_runtime15 = require("react/jsx-runtime");
5346
- var Input = React8.forwardRef(
5109
+ var Input = React7.forwardRef(
5347
5110
  ({ className, type, ...props }, ref) => {
5348
5111
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5349
5112
  "input",
@@ -5377,11 +5140,8 @@ function Label({ className, ...props }) {
5377
5140
  }
5378
5141
 
5379
5142
  // src/ui/link-modal/UrlOrPageInput.tsx
5380
- var import_lucide_react7 = require("lucide-react");
5381
5143
  var import_jsx_runtime17 = require("react/jsx-runtime");
5382
- function FieldChevron({
5383
- onClick
5384
- }) {
5144
+ function FieldChevron({ onClick }) {
5385
5145
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5386
5146
  "button",
5387
5147
  {
@@ -5390,7 +5150,7 @@ function FieldChevron({
5390
5150
  onClick,
5391
5151
  "aria-label": "Open page list",
5392
5152
  tabIndex: -1,
5393
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.ChevronDown, { size: 16 })
5153
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconChevronDown, {})
5394
5154
  }
5395
5155
  );
5396
5156
  }
@@ -5408,29 +5168,7 @@ function UrlOrPageInput({
5408
5168
  }) {
5409
5169
  const inputId = (0, import_react4.useId)();
5410
5170
  const inputRef = (0, import_react4.useRef)(null);
5411
- const rootRef = (0, import_react4.useRef)(null);
5412
5171
  const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
5413
- (0, import_react4.useEffect)(() => {
5414
- if (!dropdownOpen) return;
5415
- const isOutside = (e) => {
5416
- const root = rootRef.current;
5417
- if (!root) return true;
5418
- const path = typeof e.composedPath === "function" ? e.composedPath() : [];
5419
- if (path.includes(root)) return false;
5420
- const target = e.target;
5421
- return !(target instanceof Node && root.contains(target));
5422
- };
5423
- const closeIfOutside = (e) => {
5424
- if (!isOutside(e)) return;
5425
- onDropdownOpenChange(false);
5426
- };
5427
- document.addEventListener("pointerdown", closeIfOutside, true);
5428
- document.addEventListener("mousedown", closeIfOutside, true);
5429
- return () => {
5430
- document.removeEventListener("pointerdown", closeIfOutside, true);
5431
- document.removeEventListener("mousedown", closeIfOutside, true);
5432
- };
5433
- }, [dropdownOpen, onDropdownOpenChange]);
5434
5172
  const openDropdown = () => {
5435
5173
  if (readOnly || filteredPages.length === 0) return;
5436
5174
  onDropdownOpenChange(true);
@@ -5453,16 +5191,9 @@ function UrlOrPageInput({
5453
5191
  );
5454
5192
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5455
5193
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5456
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
5194
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "relative w-full", children: [
5457
5195
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5458
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5459
- import_lucide_react7.File,
5460
- {
5461
- size: 16,
5462
- className: "shrink-0 text-foreground",
5463
- "aria-hidden": true
5464
- }
5465
- ) }) : null,
5196
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5466
5197
  readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5467
5198
  Input,
5468
5199
  {
@@ -5474,14 +5205,7 @@ function UrlOrPageInput({
5474
5205
  setIsFocused(true);
5475
5206
  if (!selectedPage) openDropdown();
5476
5207
  },
5477
- onBlur: () => {
5478
- setIsFocused(false);
5479
- requestAnimationFrame(() => {
5480
- if (!rootRef.current?.contains(document.activeElement)) {
5481
- onDropdownOpenChange(false);
5482
- }
5483
- });
5484
- },
5208
+ onBlur: () => setIsFocused(false),
5485
5209
  placeholder,
5486
5210
  className: cn(
5487
5211
  "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",
@@ -5497,7 +5221,7 @@ function UrlOrPageInput({
5497
5221
  onMouseDown: clearSelection,
5498
5222
  "aria-label": "Clear selected page",
5499
5223
  tabIndex: -1,
5500
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, "aria-hidden": true })
5224
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconX, { "aria-hidden": true })
5501
5225
  }
5502
5226
  ) : null,
5503
5227
  !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
@@ -5506,7 +5230,7 @@ function UrlOrPageInput({
5506
5230
  "div",
5507
5231
  {
5508
5232
  "data-ohw-link-page-dropdown": "",
5509
- 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",
5233
+ 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",
5510
5234
  onMouseDown: (e) => e.preventDefault(),
5511
5235
  children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5512
5236
  "button",
@@ -5515,7 +5239,7 @@ function UrlOrPageInput({
5515
5239
  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",
5516
5240
  onClick: () => onPageSelect(page),
5517
5241
  children: [
5518
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5242
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
5519
5243
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: page.title })
5520
5244
  ]
5521
5245
  },
@@ -5528,348 +5252,8 @@ function UrlOrPageInput({
5528
5252
  ] });
5529
5253
  }
5530
5254
 
5531
- // src/ui/link-modal/LinkEditorPanel.tsx
5532
- var import_jsx_runtime18 = require("react/jsx-runtime");
5533
- function LinkEditorPanel({ state, onClose }) {
5534
- const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5535
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5536
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5537
- "button",
5538
- {
5539
- type: "button",
5540
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5541
- "aria-label": "Close",
5542
- onClick: onClose,
5543
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5544
- }
5545
- ) }),
5546
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5547
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5548
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5549
- DestinationBreadcrumb,
5550
- {
5551
- pageTitle: state.selectedPage.title,
5552
- sectionLabel: state.selectedSection.label
5553
- }
5554
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5555
- UrlOrPageInput,
5556
- {
5557
- value: state.searchValue,
5558
- selectedPage: state.selectedPage,
5559
- dropdownOpen: state.dropdownOpen,
5560
- onDropdownOpenChange: state.setDropdownOpen,
5561
- filteredPages: state.filteredPages,
5562
- onInputChange: state.handleInputChange,
5563
- onPageSelect: state.handlePageSelect,
5564
- urlError: state.urlError
5565
- }
5566
- ),
5567
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5568
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5569
- Button,
5570
- {
5571
- type: "button",
5572
- variant: "outline",
5573
- className: "w-fit cursor-pointer",
5574
- size: "sm",
5575
- onClick: state.handleChooseSection,
5576
- children: "Choose a section"
5577
- }
5578
- ),
5579
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5580
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5581
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
5582
- ] })
5583
- ] }) : null,
5584
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5585
- ] }),
5586
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5587
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5588
- Button,
5589
- {
5590
- type: "button",
5591
- variant: isCancel ? "outline" : "ghost",
5592
- className: "leading-6 shadow-none cursor-pointer",
5593
- style: isCancel ? {
5594
- backgroundColor: "var(--ohw-background)",
5595
- borderColor: "var(--ohw-border)",
5596
- color: "var(--ohw-foreground)"
5597
- } : void 0,
5598
- onClick: state.handleSecondary,
5599
- children: state.secondaryLabel
5600
- }
5601
- ),
5602
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5603
- Button,
5604
- {
5605
- type: "button",
5606
- className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5607
- style: {
5608
- backgroundColor: "var(--ohw-primary)",
5609
- color: "var(--ohw-primary-foreground)"
5610
- },
5611
- disabled: !state.isValid,
5612
- onClick: state.handleSubmit,
5613
- children: state.submitLabel
5614
- }
5615
- )
5616
- ] })
5617
- ] });
5618
- }
5619
-
5620
- // src/ui/link-modal/SectionPickerOverlay.tsx
5621
- var import_react5 = require("react");
5622
- var import_react_dom = require("react-dom");
5623
- var import_lucide_react9 = require("lucide-react");
5624
- var import_navigation = require("next/navigation");
5625
- var import_jsx_runtime19 = require("react/jsx-runtime");
5626
- var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5627
- function rectsEqual(a, b) {
5628
- if (a.size !== b.size) return false;
5629
- for (const [id, rect] of a) {
5630
- const other = b.get(id);
5631
- if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
5632
- return false;
5633
- }
5634
- }
5635
- return true;
5636
- }
5637
- function useSectionRects(sectionIdsKey, enabled) {
5638
- const [rects, setRects] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
5639
- const sectionIds = (0, import_react5.useMemo)(
5640
- () => sectionIdsKey ? sectionIdsKey.split("\0") : [],
5641
- [sectionIdsKey]
5642
- );
5643
- (0, import_react5.useEffect)(() => {
5644
- if (!enabled || sectionIds.length === 0) {
5645
- setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
5646
- return;
5647
- }
5648
- const update = () => {
5649
- const next = /* @__PURE__ */ new Map();
5650
- for (const id of sectionIds) {
5651
- const el = document.querySelector(
5652
- `[data-ohw-section="${CSS.escape(id)}"]`
5653
- );
5654
- if (el) next.set(id, el.getBoundingClientRect());
5655
- }
5656
- setRects((prev) => rectsEqual(prev, next) ? prev : next);
5657
- };
5658
- update();
5659
- const opts = { capture: true, passive: true };
5660
- window.addEventListener("scroll", update, opts);
5661
- window.addEventListener("resize", update);
5662
- const vv = window.visualViewport;
5663
- vv?.addEventListener("resize", update);
5664
- vv?.addEventListener("scroll", update);
5665
- const ro = new ResizeObserver(update);
5666
- for (const id of sectionIds) {
5667
- const el = document.querySelector(
5668
- `[data-ohw-section="${CSS.escape(id)}"]`
5669
- );
5670
- if (el) ro.observe(el);
5671
- }
5672
- return () => {
5673
- window.removeEventListener("scroll", update, opts);
5674
- window.removeEventListener("resize", update);
5675
- vv?.removeEventListener("resize", update);
5676
- vv?.removeEventListener("scroll", update);
5677
- ro.disconnect();
5678
- };
5679
- }, [sectionIds, enabled]);
5680
- return rects;
5681
- }
5682
- function SectionPickerOverlay({
5683
- pagePath,
5684
- sections,
5685
- onBack,
5686
- onSelect
5687
- }) {
5688
- const router = (0, import_navigation.useRouter)();
5689
- const pathname = (0, import_navigation.usePathname)();
5690
- const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
5691
- const [hoveredId, setHoveredId] = (0, import_react5.useState)(null);
5692
- const [chromeClip, setChromeClip] = (0, import_react5.useState)(
5693
- () => ({
5694
- top: 0,
5695
- bottom: typeof window !== "undefined" ? window.innerHeight : 0
5696
- })
5697
- );
5698
- const normalizedTarget = normalizePath(pagePath);
5699
- const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
5700
- (0, import_react5.useEffect)(() => {
5701
- if (isOnTargetPage) return;
5702
- const search = readPreservedSearch();
5703
- router.push(`${normalizedTarget}${search}`);
5704
- }, [isOnTargetPage, normalizedTarget, router]);
5705
- (0, import_react5.useEffect)(() => {
5706
- document.documentElement.setAttribute("data-ohw-section-picking", "");
5707
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5708
- el.removeAttribute("data-ohw-hovered");
5709
- });
5710
- return () => {
5711
- document.documentElement.removeAttribute("data-ohw-section-picking");
5712
- };
5713
- }, []);
5714
- (0, import_react5.useEffect)(() => {
5715
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
5716
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
5717
- const bottom = Math.min(
5718
- window.innerHeight,
5719
- visibleCanvasTop + canvasH - iframeOffsetTop
5720
- );
5721
- setChromeClip(
5722
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
5723
- );
5724
- };
5725
- const onParentScroll = (e) => {
5726
- if (e.data?.type !== "ow:parent-scroll") return;
5727
- const { iframeOffsetTop, headerH, canvasH } = e.data;
5728
- applyClip(iframeOffsetTop, headerH, canvasH);
5729
- };
5730
- const onResize = () => {
5731
- setChromeClip((prev) => {
5732
- const bottom = Math.max(prev.top, window.innerHeight);
5733
- return prev.bottom === bottom ? prev : { ...prev, bottom };
5734
- });
5735
- };
5736
- window.addEventListener("message", onParentScroll);
5737
- window.addEventListener("resize", onResize);
5738
- return () => {
5739
- window.removeEventListener("message", onParentScroll);
5740
- window.removeEventListener("resize", onResize);
5741
- };
5742
- }, []);
5743
- const liveSections = (0, import_react5.useMemo)(() => {
5744
- if (!isOnTargetPage) return sections;
5745
- const live = collectSectionsFromDom();
5746
- if (live.length === 0) return sections;
5747
- const labels = new Map(sections.map((s) => [s.id, s.label]));
5748
- return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5749
- }, [isOnTargetPage, sections, pathname]);
5750
- const sectionIdsKey = (0, import_react5.useMemo)(
5751
- () => liveSections.map((s) => s.id).join("\0"),
5752
- [liveSections]
5753
- );
5754
- const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5755
- const handleSelect = (0, import_react5.useCallback)(
5756
- (section) => {
5757
- if (selectedId) return;
5758
- setSelectedId(section.id);
5759
- onSelect(section);
5760
- },
5761
- [selectedId, onSelect]
5762
- );
5763
- (0, import_react5.useEffect)(() => {
5764
- const onKeyDown = (e) => {
5765
- if (e.key === "Escape") {
5766
- e.preventDefault();
5767
- e.stopPropagation();
5768
- onBack();
5769
- }
5770
- };
5771
- window.addEventListener("keydown", onKeyDown, true);
5772
- return () => window.removeEventListener("keydown", onKeyDown, true);
5773
- }, [onBack]);
5774
- const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5775
- if (!portalRoot) return null;
5776
- return (0, import_react_dom.createPortal)(
5777
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5778
- "div",
5779
- {
5780
- "data-ohw-section-picker": "",
5781
- "data-ohw-bridge": "",
5782
- className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
5783
- "aria-modal": true,
5784
- role: "dialog",
5785
- "aria-label": "Choose a section",
5786
- children: [
5787
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5788
- "div",
5789
- {
5790
- className: "pointer-events-auto fixed left-5 z-[2]",
5791
- style: { top: chromeClip.top + 20 },
5792
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5793
- Button,
5794
- {
5795
- type: "button",
5796
- variant: "outline",
5797
- size: "sm",
5798
- className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5799
- onClick: onBack,
5800
- children: [
5801
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5802
- "Back"
5803
- ]
5804
- }
5805
- )
5806
- }
5807
- ),
5808
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5809
- "div",
5810
- {
5811
- 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",
5812
- style: {
5813
- top: chromeClip.bottom - 20,
5814
- transform: "translate(-50%, -100%)",
5815
- backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
5816
- },
5817
- "data-ohw-section-picker-hint": "",
5818
- children: "Click on section to select"
5819
- }
5820
- ),
5821
- !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5822
- "div",
5823
- {
5824
- 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",
5825
- style: { top: chromeClip.top + 64 },
5826
- children: "Loading page preview\u2026"
5827
- }
5828
- ) : null,
5829
- isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5830
- isOnTargetPage ? liveSections.map((section) => {
5831
- const rect = rects.get(section.id);
5832
- if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5833
- const isSelected = selectedId === section.id;
5834
- const isLit = isSelected || hoveredId === section.id;
5835
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5836
- "button",
5837
- {
5838
- type: "button",
5839
- className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
5840
- style: {
5841
- top: rect.top,
5842
- left: rect.left,
5843
- width: rect.width,
5844
- height: rect.height,
5845
- backgroundColor: isLit ? "transparent" : DIM_OVERLAY
5846
- },
5847
- "aria-label": `Select section ${section.label}`,
5848
- onMouseEnter: () => setHoveredId(section.id),
5849
- onMouseLeave: () => setHoveredId((prev) => prev === section.id ? null : prev),
5850
- onClick: () => handleSelect(section),
5851
- children: isSelected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5852
- "span",
5853
- {
5854
- className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5855
- style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5856
- "aria-hidden": true,
5857
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Check, { className: "size-5" })
5858
- }
5859
- ) : null
5860
- },
5861
- section.id
5862
- );
5863
- }) : null
5864
- ]
5865
- }
5866
- ),
5867
- portalRoot
5868
- );
5869
- }
5870
-
5871
5255
  // src/ui/link-modal/useLinkModalState.ts
5872
- var import_react6 = require("react");
5256
+ var import_react5 = require("react");
5873
5257
  function useLinkModalState({
5874
5258
  open,
5875
5259
  mode,
@@ -5877,18 +5261,21 @@ function useLinkModalState({
5877
5261
  sections: _sections,
5878
5262
  sectionsByPath,
5879
5263
  initialTarget,
5880
- existingTargets: _existingTargets,
5264
+ existingTargets,
5881
5265
  onClose,
5882
5266
  onSubmit
5883
5267
  }) {
5884
- const availablePages = (0, import_react6.useMemo)(() => pages, [pages]);
5885
- const [searchValue, setSearchValue] = (0, import_react6.useState)("");
5886
- const [selectedPage, setSelectedPage] = (0, import_react6.useState)(null);
5887
- const [selectedSection, setSelectedSection] = (0, import_react6.useState)(null);
5888
- const [step, setStep] = (0, import_react6.useState)("input");
5889
- const [dropdownOpen, setDropdownOpen] = (0, import_react6.useState)(false);
5890
- const [urlError, setUrlError] = (0, import_react6.useState)("");
5891
- const reset = (0, import_react6.useCallback)(() => {
5268
+ const availablePages = (0, import_react5.useMemo)(
5269
+ () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
5270
+ [mode, pages, existingTargets]
5271
+ );
5272
+ const [searchValue, setSearchValue] = (0, import_react5.useState)("");
5273
+ const [selectedPage, setSelectedPage] = (0, import_react5.useState)(null);
5274
+ const [selectedSection, setSelectedSection] = (0, import_react5.useState)(null);
5275
+ const [step, setStep] = (0, import_react5.useState)("input");
5276
+ const [dropdownOpen, setDropdownOpen] = (0, import_react5.useState)(false);
5277
+ const [urlError, setUrlError] = (0, import_react5.useState)("");
5278
+ const reset = (0, import_react5.useCallback)(() => {
5892
5279
  setSearchValue("");
5893
5280
  setSelectedPage(null);
5894
5281
  setSelectedSection(null);
@@ -5896,7 +5283,7 @@ function useLinkModalState({
5896
5283
  setDropdownOpen(false);
5897
5284
  setUrlError("");
5898
5285
  }, []);
5899
- (0, import_react6.useEffect)(() => {
5286
+ (0, import_react5.useEffect)(() => {
5900
5287
  if (!open) return;
5901
5288
  if (mode === "edit" && initialTarget) {
5902
5289
  const { pageRoute } = parseTarget(initialTarget);
@@ -5911,12 +5298,12 @@ function useLinkModalState({
5911
5298
  }
5912
5299
  setDropdownOpen(false);
5913
5300
  setUrlError("");
5914
- }, [open, mode, initialTarget, reset]);
5915
- const filteredPages = (0, import_react6.useMemo)(() => {
5301
+ }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
5302
+ const filteredPages = (0, import_react5.useMemo)(() => {
5916
5303
  if (!searchValue.trim()) return availablePages;
5917
5304
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
5918
5305
  }, [availablePages, searchValue]);
5919
- const activeSections = (0, import_react6.useMemo)(() => {
5306
+ const activeSections = (0, import_react5.useMemo)(() => {
5920
5307
  if (!selectedPage) return [];
5921
5308
  return getSectionsForPath(sectionsByPath, selectedPage.path);
5922
5309
  }, [selectedPage, sectionsByPath]);
@@ -5958,14 +5345,11 @@ function useLinkModalState({
5958
5345
  const handleBackToSections = () => {
5959
5346
  setStep("sectionPicker");
5960
5347
  };
5961
- const handleSectionPickerBack = () => {
5962
- setStep("input");
5963
- };
5964
5348
  const handleClose = () => {
5965
5349
  reset();
5966
5350
  onClose();
5967
5351
  };
5968
- const isValid = (0, import_react6.useMemo)(() => {
5352
+ const isValid = (0, import_react5.useMemo)(() => {
5969
5353
  if (urlError) return false;
5970
5354
  if (showBreadcrumb) return true;
5971
5355
  if (selectedPage) return true;
@@ -5998,7 +5382,7 @@ function useLinkModalState({
5998
5382
  onSubmit(normalizeUrl(searchValue));
5999
5383
  handleClose();
6000
5384
  };
6001
- const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
5385
+ const submitLabel = mode === "edit" ? "Save" : "Create";
6002
5386
  const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
6003
5387
  const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
6004
5388
  const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
@@ -6025,29 +5409,23 @@ function useLinkModalState({
6025
5409
  handleChooseSection,
6026
5410
  handleSectionSelect,
6027
5411
  handleBackToSections,
6028
- handleSectionPickerBack,
6029
5412
  handleClose,
6030
5413
  handleSecondary,
6031
5414
  handleSubmit
6032
5415
  };
6033
5416
  }
6034
5417
 
6035
- // src/ui/link-modal/LinkPopover.tsx
6036
- var import_jsx_runtime20 = require("react/jsx-runtime");
6037
- function postToParent(data) {
6038
- window.parent?.postMessage(data, "*");
6039
- }
6040
- function LinkPopover({
5418
+ // src/ui/link-modal/LinkEditorPanel.tsx
5419
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5420
+ function LinkEditorPanel({
6041
5421
  open = true,
6042
- panelRef,
6043
- portalContainer,
6044
- onClose,
6045
5422
  mode = "create",
6046
5423
  pages,
6047
5424
  sections = [],
6048
5425
  sectionsByPath,
6049
5426
  initialTarget,
6050
5427
  existingTargets = [],
5428
+ onClose,
6051
5429
  onSubmit
6052
5430
  }) {
6053
5431
  const state = useLinkModalState({
@@ -6061,100 +5439,115 @@ function LinkPopover({
6061
5439
  onClose,
6062
5440
  onSubmit
6063
5441
  });
6064
- const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
6065
- (0, import_react7.useEffect)(() => {
6066
- if (!open) return;
6067
- if (sectionPickerActive) {
6068
- postToParent({ type: "ow:link-modal-lock", locked: false });
6069
- postToParent({ type: "ow:section-picker", active: true });
6070
- document.documentElement.style.overflow = "";
6071
- document.body.style.overflow = "";
6072
- return () => {
6073
- postToParent({ type: "ow:section-picker", active: false });
6074
- };
6075
- }
6076
- postToParent({ type: "ow:section-picker", active: false });
6077
- postToParent({ type: "ow:link-modal-lock", locked: true });
6078
- const html = document.documentElement;
6079
- const body = document.body;
6080
- const prevHtmlOverflow = html.style.overflow;
6081
- const prevBodyOverflow = body.style.overflow;
6082
- const prevHtmlOverscroll = html.style.overscrollBehavior;
6083
- const prevBodyOverscroll = body.style.overscrollBehavior;
6084
- html.style.overflow = "hidden";
6085
- body.style.overflow = "hidden";
6086
- html.style.overscrollBehavior = "none";
6087
- body.style.overscrollBehavior = "none";
6088
- const canScrollElement = (el, deltaY) => {
6089
- const { overflowY } = getComputedStyle(el);
6090
- if (overflowY !== "auto" && overflowY !== "scroll") return false;
6091
- if (el.scrollHeight <= el.clientHeight + 1) return false;
6092
- if (deltaY < 0) return el.scrollTop > 0;
6093
- if (deltaY > 0) return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6094
- return true;
6095
- };
6096
- const allowsInternalScroll = (e) => {
6097
- const target = e.target;
6098
- if (!(target instanceof Element)) return false;
6099
- const scrollRoot = target.closest(
6100
- "[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
6101
- );
6102
- if (!scrollRoot) return false;
6103
- const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
6104
- return canScrollElement(scrollRoot, deltaY);
6105
- };
6106
- const preventBackgroundScroll = (e) => {
6107
- if (allowsInternalScroll(e)) return;
6108
- e.preventDefault();
6109
- };
6110
- const scrollOpts = { passive: false, capture: true };
6111
- document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6112
- document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6113
- return () => {
6114
- postToParent({ type: "ow:link-modal-lock", locked: false });
6115
- html.style.overflow = prevHtmlOverflow;
6116
- body.style.overflow = prevBodyOverflow;
6117
- html.style.overscrollBehavior = prevHtmlOverscroll;
6118
- body.style.overscrollBehavior = prevBodyOverscroll;
6119
- document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6120
- document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6121
- };
6122
- }, [open, sectionPickerActive]);
6123
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
6124
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6125
- Dialog2,
6126
- {
6127
- open: open && !sectionPickerActive,
6128
- onOpenChange: (next) => {
6129
- if (!next) onClose?.();
6130
- },
6131
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6132
- DialogContent,
6133
- {
6134
- ref: panelRef,
6135
- container: portalContainer,
6136
- "data-ohw-link-popover-root": "",
6137
- "data-ohw-link-modal-root": "",
6138
- "data-ohw-bridge": "",
6139
- showCloseButton: false,
6140
- className: "gap-0 p-0 w-full md:w-md pointer-events-auto z-[2147483646] overflow-visible",
6141
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { state, onClose })
6142
- }
6143
- )
6144
- }
6145
- ),
6146
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6147
- SectionPickerOverlay,
5442
+ const isCancel = state.secondaryLabel === "Cancel";
5443
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5444
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5445
+ "button",
6148
5446
  {
6149
- pagePath: state.selectedPage.path,
6150
- sections: state.activeSections,
6151
- onBack: state.handleSectionPickerBack,
6152
- onSelect: state.handleSectionSelect
5447
+ type: "button",
5448
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5449
+ "aria-label": "Close",
5450
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconX, { "aria-hidden": true })
6153
5451
  }
6154
- ) : null
5452
+ ) }),
5453
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5454
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5455
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5456
+ DestinationBreadcrumb,
5457
+ {
5458
+ pageTitle: state.selectedPage.title,
5459
+ sectionLabel: state.selectedSection.label
5460
+ }
5461
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5462
+ UrlOrPageInput,
5463
+ {
5464
+ value: state.searchValue,
5465
+ selectedPage: state.selectedPage,
5466
+ dropdownOpen: state.dropdownOpen,
5467
+ onDropdownOpenChange: state.setDropdownOpen,
5468
+ filteredPages: state.filteredPages,
5469
+ onInputChange: state.handleInputChange,
5470
+ onPageSelect: state.handlePageSelect,
5471
+ urlError: state.urlError
5472
+ }
5473
+ ),
5474
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5475
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5476
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5477
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5478
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
5479
+ ] })
5480
+ ] }) : null,
5481
+ state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5482
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5483
+ ] }),
5484
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5485
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5486
+ Button,
5487
+ {
5488
+ type: "button",
5489
+ variant: isCancel ? "outline" : "ghost",
5490
+ className: "leading-6 shadow-none cursor-pointer",
5491
+ style: isCancel ? {
5492
+ backgroundColor: "var(--ohw-background)",
5493
+ borderColor: "var(--ohw-border)",
5494
+ color: "var(--ohw-foreground)"
5495
+ } : void 0,
5496
+ onClick: state.handleSecondary,
5497
+ children: state.secondaryLabel
5498
+ }
5499
+ ),
5500
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5501
+ Button,
5502
+ {
5503
+ type: "button",
5504
+ className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5505
+ style: {
5506
+ backgroundColor: "var(--ohw-primary)",
5507
+ color: "var(--ohw-primary-foreground)"
5508
+ },
5509
+ disabled: !state.isValid,
5510
+ onClick: state.handleSubmit,
5511
+ children: state.submitLabel
5512
+ }
5513
+ )
5514
+ ] })
6155
5515
  ] });
6156
5516
  }
6157
5517
 
5518
+ // src/ui/link-modal/LinkPopover.tsx
5519
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5520
+ function LinkPopover({
5521
+ open = true,
5522
+ panelRef,
5523
+ portalContainer,
5524
+ onClose,
5525
+ ...editorProps
5526
+ }) {
5527
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5528
+ Dialog2,
5529
+ {
5530
+ open,
5531
+ onOpenChange: (next) => {
5532
+ if (!next) onClose?.();
5533
+ },
5534
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5535
+ DialogContent,
5536
+ {
5537
+ ref: panelRef,
5538
+ container: portalContainer,
5539
+ "data-ohw-link-popover-root": "",
5540
+ "data-ohw-link-modal-root": "",
5541
+ "data-ohw-bridge": "",
5542
+ showCloseButton: false,
5543
+ className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5544
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5545
+ }
5546
+ )
5547
+ }
5548
+ );
5549
+ }
5550
+
6158
5551
  // src/ui/link-modal/devFixtures.ts
6159
5552
  var DEV_SITE_PAGES = [
6160
5553
  { path: "/", title: "Home" },
@@ -6174,21 +5567,16 @@ var DEV_SECTIONS_BY_PATH = {
6174
5567
  { id: "founder-teaser", label: "Founder" },
6175
5568
  { id: "plan-form", label: "Plan Form" },
6176
5569
  { id: "testimonials", label: "Testimonials" },
6177
- { id: "wordmark", label: "Wordmark" },
6178
- { id: "footer", label: "Footer" }
5570
+ { id: "wordmark", label: "Wordmark" }
6179
5571
  ],
6180
5572
  "/about": [
6181
5573
  { id: "manifesto", label: "Manifesto" },
6182
5574
  { id: "story-letter", label: "Our Story" },
6183
5575
  { id: "lagree-explainer", label: "Lagree Explainer" },
6184
5576
  { id: "waitlist-cta", label: "Waitlist" },
6185
- { id: "personal-training", label: "Personal training" },
6186
- { id: "footer", label: "Footer" }
6187
- ],
6188
- "/classes": [
6189
- { id: "class-library", label: "Class Library" },
6190
- { id: "footer", label: "Footer" }
5577
+ { id: "personal-training", label: "Personal training" }
6191
5578
  ],
5579
+ "/classes": [{ id: "class-library", label: "Class Library" }],
6192
5580
  "/pricing": [
6193
5581
  { id: "page-header", label: "Page Header" },
6194
5582
  { id: "free-class-cta", label: "Free Class" },
@@ -6196,25 +5584,19 @@ var DEV_SECTIONS_BY_PATH = {
6196
5584
  { id: "monthly-memberships", label: "Memberships" },
6197
5585
  { id: "specialty-programs", label: "Specialty Programs" },
6198
5586
  { id: "package-matcher", label: "Packages" },
6199
- { id: "wordmark", label: "Wordmark" },
6200
- { id: "footer", label: "Footer" }
5587
+ { id: "wordmark", label: "Wordmark" }
6201
5588
  ],
6202
5589
  "/studio": [
6203
5590
  { id: "studio-intro", label: "Studio Intro" },
6204
5591
  { id: "studio-features", label: "Studio Features" },
6205
5592
  { id: "studio-gallery", label: "Studio Gallery" },
6206
- { id: "studio-visit", label: "Visit Studio" },
6207
- { id: "footer", label: "Footer" }
6208
- ],
6209
- "/instructors": [
6210
- { id: "instructor-grid", label: "Instructors" },
6211
- { id: "footer", label: "Footer" }
5593
+ { id: "studio-visit", label: "Visit Studio" }
6212
5594
  ],
5595
+ "/instructors": [{ id: "instructor-grid", label: "Instructors" }],
6213
5596
  "/contact": [
6214
5597
  { id: "hello-hero", label: "Hello" },
6215
5598
  { id: "contact-form", label: "Contact Form" },
6216
- { id: "faq", label: "FAQ" },
6217
- { id: "footer", label: "Footer" }
5599
+ { id: "faq", label: "FAQ" }
6218
5600
  ]
6219
5601
  };
6220
5602
  function shouldUseDevFixtures() {
@@ -6224,277 +5606,8 @@ function shouldUseDevFixtures() {
6224
5606
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6225
5607
  }
6226
5608
 
6227
- // src/lib/nav-items.ts
6228
- var NAV_COUNT_KEY = "__ohw_nav_count";
6229
- var NAV_ORDER_KEY = "__ohw_nav_order";
6230
- function getLinkHref(el) {
6231
- const key = el.getAttribute("data-ohw-href-key");
6232
- if (key) {
6233
- const stored = getStoredLinkHref(key);
6234
- if (stored !== void 0) return stored;
6235
- }
6236
- return el.getAttribute("href") ?? "";
6237
- }
6238
- function isNavbarButton(el) {
6239
- return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
6240
- }
6241
- function isNavbarLinkItem(el) {
6242
- if (!el.matches("[data-ohw-href-key]")) return false;
6243
- if (isNavbarButton(el)) return false;
6244
- if (!el.querySelector('[data-ohw-editable="text"]')) return false;
6245
- return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
6246
- }
6247
- function getNavbarDesktopContainer() {
6248
- return document.querySelector("[data-ohw-nav-container]");
6249
- }
6250
- function getNavbarDrawerContainer() {
6251
- return document.querySelector("[data-ohw-nav-drawer]");
6252
- }
6253
- function listNavbarItems() {
6254
- const desktop = getNavbarDesktopContainer();
6255
- if (desktop) {
6256
- return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6257
- }
6258
- const drawer = getNavbarDrawerContainer();
6259
- if (drawer) {
6260
- return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6261
- }
6262
- return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
6263
- }
6264
- function parseNavIndexFromKey(key) {
6265
- if (!key) return null;
6266
- const match = key.match(/^nav-(\d+)-href$/);
6267
- if (!match) return null;
6268
- return parseInt(match[1], 10);
6269
- }
6270
- function getNavbarIndicesInDom() {
6271
- const indices = /* @__PURE__ */ new Set();
6272
- for (const item of listNavbarItems()) {
6273
- const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
6274
- if (idx !== null) indices.add(idx);
6275
- }
6276
- return [...indices].sort((a, b) => a - b);
6277
- }
6278
- function getNextNavbarIndex() {
6279
- const indices = getNavbarIndicesInDom();
6280
- if (indices.length === 0) return 0;
6281
- return Math.max(...indices) + 1;
6282
- }
6283
- function getNavbarExistingTargets() {
6284
- return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
6285
- }
6286
- function getNavOrderFromDom() {
6287
- return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6288
- }
6289
- function parseNavOrder(content) {
6290
- const raw = content[NAV_ORDER_KEY];
6291
- if (!raw) return null;
6292
- try {
6293
- const parsed = JSON.parse(raw);
6294
- if (!Array.isArray(parsed)) return null;
6295
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6296
- } catch {
6297
- return null;
6298
- }
6299
- }
6300
- function findCounterpartByHrefKey(hrefKey, root) {
6301
- return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6302
- }
6303
- function cloneNavLinkShell(template) {
6304
- const anchor = document.createElement("a");
6305
- if (template) {
6306
- anchor.style.cssText = template.style.cssText;
6307
- if (template.className) anchor.className = template.className;
6308
- }
6309
- anchor.style.cursor = "pointer";
6310
- anchor.style.textDecoration = "none";
6311
- return anchor;
6312
- }
6313
- function buildNavLink(index, href, label, template) {
6314
- const anchor = cloneNavLinkShell(template);
6315
- anchor.href = href;
6316
- anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
6317
- const span = document.createElement("span");
6318
- span.setAttribute("data-ohw-editable", "text");
6319
- span.setAttribute("data-ohw-key", `nav-${index}-label`);
6320
- span.textContent = label;
6321
- anchor.appendChild(span);
6322
- return anchor;
6323
- }
6324
- function insertNavLinkInContainer(container, anchor, afterHrefKey) {
6325
- if (!afterHrefKey) {
6326
- container.appendChild(anchor);
6327
- return;
6328
- }
6329
- const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
6330
- if (afterEl?.parentElement === container) {
6331
- afterEl.insertAdjacentElement("afterend", anchor);
6332
- return;
6333
- }
6334
- container.appendChild(anchor);
6335
- }
6336
- function insertNavbarItemDom(index, href, label, afterAnchor) {
6337
- const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
6338
- const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6339
- const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6340
- const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
6341
- const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
6342
- let primary = null;
6343
- const desktopContainer = getNavbarDesktopContainer();
6344
- if (desktopContainer) {
6345
- const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
6346
- insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
6347
- primary = desktopAnchor;
6348
- }
6349
- const drawerContainer = getNavbarDrawerContainer();
6350
- if (drawerContainer) {
6351
- const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
6352
- insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
6353
- if (!primary) primary = drawerAnchor;
6354
- }
6355
- if (!primary) {
6356
- const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
6357
- const nav = document.querySelector("nav");
6358
- nav?.appendChild(fallback);
6359
- primary = fallback;
6360
- }
6361
- return primary;
6362
- }
6363
- function applyNavOrderToContainer(container, order) {
6364
- const seen = /* @__PURE__ */ new Set();
6365
- for (const hrefKey of order) {
6366
- if (seen.has(hrefKey)) continue;
6367
- seen.add(hrefKey);
6368
- const el = findCounterpartByHrefKey(hrefKey, container);
6369
- if (el) container.appendChild(el);
6370
- }
6371
- for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6372
- if (!isNavbarLinkItem(el)) continue;
6373
- const key = el.getAttribute("data-ohw-href-key");
6374
- if (!key || seen.has(key)) continue;
6375
- container.appendChild(el);
6376
- }
6377
- }
6378
- function applyNavOrder(order) {
6379
- const desktop = getNavbarDesktopContainer();
6380
- if (desktop) applyNavOrderToContainer(desktop, order);
6381
- const drawer = getNavbarDrawerContainer();
6382
- if (drawer) applyNavOrderToContainer(drawer, order);
6383
- }
6384
- function collectNavbarIndicesFromContent(content) {
6385
- const indices = /* @__PURE__ */ new Set();
6386
- for (const key of Object.keys(content)) {
6387
- const idx = parseNavIndexFromKey(key);
6388
- if (idx !== null) indices.add(idx);
6389
- }
6390
- const countRaw = content[NAV_COUNT_KEY];
6391
- if (countRaw) {
6392
- const count = parseInt(countRaw, 10);
6393
- if (Number.isFinite(count) && count > 0) {
6394
- for (let i = 0; i < count; i++) indices.add(i);
6395
- }
6396
- }
6397
- return [...indices].sort((a, b) => a - b);
6398
- }
6399
- function navbarIndexExistsInDom(index) {
6400
- return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
6401
- }
6402
- function reconcileNavbarItemsFromContent(content) {
6403
- const indices = collectNavbarIndicesFromContent(content);
6404
- for (const index of indices) {
6405
- if (navbarIndexExistsInDom(index)) continue;
6406
- const href = content[`nav-${index}-href`];
6407
- const label = content[`nav-${index}-label`];
6408
- if (!href && !label) continue;
6409
- insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6410
- }
6411
- const order = parseNavOrder(content);
6412
- if (order?.length) applyNavOrder(order);
6413
- }
6414
- function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6415
- const order = getNavOrderFromDom();
6416
- if (!afterAnchor) {
6417
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6418
- return order;
6419
- }
6420
- const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
6421
- if (!afterKey) {
6422
- if (!order.includes(newHrefKey)) order.push(newHrefKey);
6423
- return order;
6424
- }
6425
- const withoutNew = order.filter((key) => key !== newHrefKey);
6426
- const pos = withoutNew.indexOf(afterKey);
6427
- if (pos >= 0) {
6428
- withoutNew.splice(pos + 1, 0, newHrefKey);
6429
- return withoutNew;
6430
- }
6431
- withoutNew.push(newHrefKey);
6432
- return withoutNew;
6433
- }
6434
- function insertNavbarItem(href, label, afterAnchor = null) {
6435
- const index = getNextNavbarIndex();
6436
- const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
6437
- if (!anchor) throw new Error("Failed to insert navbar item");
6438
- const hrefKey = `nav-${index}-href`;
6439
- const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
6440
- applyNavOrder(order);
6441
- return {
6442
- anchor,
6443
- index,
6444
- hrefKey,
6445
- labelKey: `nav-${index}-label`,
6446
- href,
6447
- label,
6448
- order
6449
- };
6450
- }
6451
-
6452
- // src/ui/navbar-container-chrome.tsx
6453
- var import_lucide_react10 = require("lucide-react");
6454
- var import_jsx_runtime21 = require("react/jsx-runtime");
6455
- function NavbarContainerChrome({
6456
- rect,
6457
- onAdd
6458
- }) {
6459
- const chromeGap = 6;
6460
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6461
- "div",
6462
- {
6463
- "data-ohw-navbar-container-chrome": "",
6464
- "data-ohw-bridge": "",
6465
- className: "pointer-events-none fixed z-[2147483647]",
6466
- style: {
6467
- top: rect.top - chromeGap,
6468
- left: rect.left - chromeGap,
6469
- width: rect.width + chromeGap * 2,
6470
- height: rect.height + chromeGap * 2
6471
- },
6472
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6473
- "button",
6474
- {
6475
- type: "button",
6476
- "data-ohw-navbar-add-button": "",
6477
- 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",
6478
- style: { top: "70%" },
6479
- "aria-label": "Add item",
6480
- onMouseDown: (e) => {
6481
- e.preventDefault();
6482
- e.stopPropagation();
6483
- },
6484
- onClick: (e) => {
6485
- e.preventDefault();
6486
- e.stopPropagation();
6487
- onAdd();
6488
- },
6489
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6490
- }
6491
- )
6492
- }
6493
- );
6494
- }
6495
-
6496
5609
  // src/ui/badge.tsx
6497
- var import_jsx_runtime22 = require("react/jsx-runtime");
5610
+ var import_jsx_runtime20 = require("react/jsx-runtime");
6498
5611
  var badgeVariants = cva(
6499
5612
  "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",
6500
5613
  {
@@ -6512,12 +5625,12 @@ var badgeVariants = cva(
6512
5625
  }
6513
5626
  );
6514
5627
  function Badge({ className, variant, ...props }) {
6515
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5628
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6516
5629
  }
6517
5630
 
6518
5631
  // src/OhhwellsBridge.tsx
6519
- var import_lucide_react11 = require("lucide-react");
6520
- var import_jsx_runtime23 = require("react/jsx-runtime");
5632
+ var import_lucide_react3 = require("lucide-react");
5633
+ var import_jsx_runtime21 = require("react/jsx-runtime");
6521
5634
  var PRIMARY2 = "#0885FE";
6522
5635
  var IMAGE_FADE_MS = 300;
6523
5636
  function runOpacityFade(el, onDone) {
@@ -6694,9 +5807,9 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6694
5807
  tail.insertAdjacentElement("afterend", container);
6695
5808
  }
6696
5809
  const root = (0, import_client.createRoot)(container);
6697
- (0, import_react_dom2.flushSync)(() => {
5810
+ (0, import_react_dom.flushSync)(() => {
6698
5811
  root.render(
6699
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5812
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6700
5813
  SchedulingWidget,
6701
5814
  {
6702
5815
  notifyOnConnect,
@@ -6736,7 +5849,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
6736
5849
  }
6737
5850
  }
6738
5851
  }
6739
- function getLinkHref2(el) {
5852
+ function getLinkHref(el) {
6740
5853
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
6741
5854
  return anchor?.getAttribute("href") ?? "";
6742
5855
  }
@@ -6769,11 +5882,8 @@ function collectEditableNodes() {
6769
5882
  const url = raw.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "");
6770
5883
  return { key: el.dataset.ohwKey ?? "", type: "bg-image", text: url };
6771
5884
  }
6772
- if (el.dataset.ohwEditable === "video") {
6773
- return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
6774
- }
6775
5885
  if (el.dataset.ohwEditable === "link") {
6776
- return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
5886
+ return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
6777
5887
  }
6778
5888
  return {
6779
5889
  key: el.dataset.ohwKey ?? "",
@@ -6782,65 +5892,11 @@ function collectEditableNodes() {
6782
5892
  };
6783
5893
  });
6784
5894
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
6785
- const key = el.getAttribute("data-ohw-href-key") ?? "";
6786
- if (!key) return;
6787
- nodes.push({ key, type: "link", text: getLinkHref2(el) });
6788
- });
6789
- document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6790
- const key = el.dataset.ohwKey ?? "";
6791
- const video = getVideoEl(el);
6792
- if (!key || !video) return;
6793
- nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
6794
- nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
6795
- });
6796
- return nodes;
6797
- }
6798
- function isMediaEditable(el) {
6799
- const t = el.dataset.ohwEditable;
6800
- return t === "image" || t === "bg-image" || t === "video";
6801
- }
6802
- var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
6803
- var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"])';
6804
- function getVideoEl(el) {
6805
- return el instanceof HTMLVideoElement ? el : el.querySelector("video");
6806
- }
6807
- var VIDEO_AUTOPLAY_SUFFIX = "__ohw_autoplay";
6808
- var VIDEO_MUTED_SUFFIX = "__ohw_muted";
6809
- function isBackgroundVideo(video) {
6810
- return video.autoplay && video.muted;
6811
- }
6812
- function syncVideoPlayback(video) {
6813
- const background = isBackgroundVideo(video);
6814
- video.controls = !background;
6815
- if (video.autoplay) void video.play().catch(() => {
6816
- });
6817
- else video.pause();
6818
- }
6819
- function applyVideoSrc(video, url) {
6820
- const { autoplay, muted } = video;
6821
- video.src = url;
6822
- video.loop = true;
6823
- video.playsInline = true;
6824
- video.autoplay = autoplay;
6825
- video.muted = muted;
6826
- video.load();
6827
- syncVideoPlayback(video);
6828
- }
6829
- function applyVideoSettingNode(key, val) {
6830
- const isAutoplay = key.endsWith(VIDEO_AUTOPLAY_SUFFIX);
6831
- const isMuted = key.endsWith(VIDEO_MUTED_SUFFIX);
6832
- if (!isAutoplay && !isMuted) return false;
6833
- const suffixLen = (isAutoplay ? VIDEO_AUTOPLAY_SUFFIX : VIDEO_MUTED_SUFFIX).length;
6834
- const baseKey = key.slice(0, key.length - suffixLen);
6835
- const on = val === "true";
6836
- document.querySelectorAll(`[data-ohw-key="${baseKey}"]`).forEach((el) => {
6837
- const video = getVideoEl(el);
6838
- if (!video) return;
6839
- if (isAutoplay) video.autoplay = on;
6840
- else video.muted = on;
6841
- syncVideoPlayback(video);
5895
+ const key = el.getAttribute("data-ohw-href-key") ?? "";
5896
+ if (!key) return;
5897
+ nodes.push({ key, type: "link", text: getLinkHref(el) });
6842
5898
  });
6843
- return true;
5899
+ return nodes;
6844
5900
  }
6845
5901
  function applyLinkByKey(key, val) {
6846
5902
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -6854,7 +5910,7 @@ function applyLinkByKey(key, val) {
6854
5910
  }
6855
5911
  function isInsideLinkEditor(target) {
6856
5912
  return Boolean(
6857
- 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"]')
5913
+ 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"]')
6858
5914
  );
6859
5915
  }
6860
5916
  function getHrefKeyFromElement(el) {
@@ -6865,7 +5921,7 @@ function getHrefKeyFromElement(el) {
6865
5921
  if (!key) return null;
6866
5922
  return { anchor, key };
6867
5923
  }
6868
- function isNavbarButton2(el) {
5924
+ function isNavbarButton(el) {
6869
5925
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
6870
5926
  }
6871
5927
  function getNavigationItemAnchor(el) {
@@ -6877,8 +5933,19 @@ function getNavigationItemAnchor(el) {
6877
5933
  function isNavigationItem(el) {
6878
5934
  return getNavigationItemAnchor(el) !== null;
6879
5935
  }
5936
+ function getNavigationItemAddHintTarget(anchor) {
5937
+ const items = listNavigationItems();
5938
+ const idx = items.indexOf(anchor);
5939
+ if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
5940
+ return anchor;
5941
+ }
5942
+ function listNavigationItems() {
5943
+ return Array.from(
5944
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
5945
+ ).filter((el) => isNavigationItem(el));
5946
+ }
6880
5947
  function getNavigationItemReorderState(anchor) {
6881
- if (isNavbarButton2(anchor)) return { key: null, disabled: false };
5948
+ if (isNavbarButton(anchor)) return { key: null, disabled: false };
6882
5949
  return getReorderHandleStateFromAnchor(anchor);
6883
5950
  }
6884
5951
  function getReorderHandleState(el) {
@@ -6900,120 +5967,6 @@ function clearHrefKeyHover(anchor) {
6900
5967
  function isInsideNavigationItem(el) {
6901
5968
  return getNavigationItemAnchor(el) !== null;
6902
5969
  }
6903
- function getNavigationRoot(el) {
6904
- const explicit = el.closest("[data-ohw-nav-root]");
6905
- if (explicit) return explicit;
6906
- return el.closest("nav, footer, aside");
6907
- }
6908
- function findFooterItemGroup(item) {
6909
- const footer = item.closest("footer");
6910
- if (!footer) return null;
6911
- let node = item.parentElement;
6912
- while (node && node !== footer) {
6913
- const hasDirectNavItems = Array.from(
6914
- node.querySelectorAll(":scope > [data-ohw-href-key]")
6915
- ).some(isNavigationItem);
6916
- if (hasDirectNavItems) return node;
6917
- node = node.parentElement;
6918
- }
6919
- return null;
6920
- }
6921
- function isNavigationRoot(el) {
6922
- return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
6923
- }
6924
- function isInferredFooterGroup(el) {
6925
- const footer = el.closest("footer");
6926
- if (!footer || el === footer) return false;
6927
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
6928
- }
6929
- function isNavigationContainer(el) {
6930
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
6931
- }
6932
- function isPointOverNavigation(x, y) {
6933
- const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
6934
- if (!navRoot) return false;
6935
- const r2 = navRoot.getBoundingClientRect();
6936
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
6937
- }
6938
- function isPointOverNavItem(container, x, y) {
6939
- return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
6940
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
6941
- const itemRect = el.getBoundingClientRect();
6942
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
6943
- });
6944
- }
6945
- function resolveNavContainerSelectionTarget(target, clientX, clientY) {
6946
- if (getNavigationItemAnchor(target)) return null;
6947
- if (target.closest('[data-ohw-role="navbar-button"]')) return null;
6948
- const plainLink = target.closest("a");
6949
- if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
6950
- return null;
6951
- }
6952
- const fromClosest = target.closest("[data-ohw-nav-container]");
6953
- if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
6954
- return fromClosest;
6955
- }
6956
- const navRoot = target.closest("[data-ohw-nav-root]");
6957
- if (!navRoot) return null;
6958
- const container = navRoot.querySelector("[data-ohw-nav-container]");
6959
- if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
6960
- if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
6961
- return container;
6962
- }
6963
- return null;
6964
- }
6965
- function getNavigationSelectionParent(el) {
6966
- if (isNavigationItem(el)) {
6967
- const explicit = el.closest("[data-ohw-nav-container]");
6968
- if (explicit) return explicit;
6969
- const footerGroup = findFooterItemGroup(el);
6970
- if (footerGroup) return footerGroup;
6971
- return getNavigationRoot(el);
6972
- }
6973
- if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
6974
- return getNavigationRoot(el);
6975
- }
6976
- return null;
6977
- }
6978
- function placeCaretAtPoint(el, x, y) {
6979
- const selection = window.getSelection();
6980
- if (!selection) return;
6981
- if (typeof document.caretRangeFromPoint === "function") {
6982
- const range = document.caretRangeFromPoint(x, y);
6983
- if (range && el.contains(range.startContainer)) {
6984
- selection.removeAllRanges();
6985
- selection.addRange(range);
6986
- return;
6987
- }
6988
- }
6989
- const caretPositionFromPoint = document.caretPositionFromPoint;
6990
- if (typeof caretPositionFromPoint === "function") {
6991
- const position = caretPositionFromPoint(x, y);
6992
- if (position && el.contains(position.offsetNode)) {
6993
- const range = document.createRange();
6994
- range.setStart(position.offsetNode, position.offset);
6995
- range.collapse(true);
6996
- selection.removeAllRanges();
6997
- selection.addRange(range);
6998
- }
6999
- }
7000
- }
7001
- function selectAllTextInEditable(el) {
7002
- const selection = window.getSelection();
7003
- if (!selection) return;
7004
- const range = document.createRange();
7005
- range.selectNodeContents(el);
7006
- selection.removeAllRanges();
7007
- selection.addRange(range);
7008
- }
7009
- function getNavigationLabelEditable(target) {
7010
- const editable = target.closest('[data-ohw-editable="text"]');
7011
- if (!editable) return null;
7012
- const hrefCtx = getHrefKeyFromElement(editable);
7013
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
7014
- if (!navAnchor) return null;
7015
- return { editable, navAnchor };
7016
- }
7017
5970
  function collectSections() {
7018
5971
  return collectSectionsFromDom();
7019
5972
  }
@@ -7137,8 +6090,6 @@ var ICONS = {
7137
6090
  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"/>',
7138
6091
  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"/>'
7139
6092
  };
7140
- var TOOLBAR_PILL_PADDING = 2;
7141
- var TOOLBAR_TOGGLE_GAP = 4;
7142
6093
  var TOOLBAR_GROUPS = [
7143
6094
  [
7144
6095
  { cmd: "bold", title: "Bold" },
@@ -7163,7 +6114,7 @@ function EditGlowChrome({
7163
6114
  dragDisabled = false
7164
6115
  }) {
7165
6116
  const GAP = 6;
7166
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
6117
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
7167
6118
  "div",
7168
6119
  {
7169
6120
  ref: elRef,
@@ -7178,7 +6129,7 @@ function EditGlowChrome({
7178
6129
  zIndex: 2147483646
7179
6130
  },
7180
6131
  children: [
7181
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6132
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7182
6133
  "div",
7183
6134
  {
7184
6135
  style: {
@@ -7191,7 +6142,7 @@ function EditGlowChrome({
7191
6142
  }
7192
6143
  }
7193
6144
  ),
7194
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6145
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7195
6146
  "div",
7196
6147
  {
7197
6148
  "data-ohw-drag-handle-container": "",
@@ -7203,7 +6154,7 @@ function EditGlowChrome({
7203
6154
  transform: "translate(calc(-100% - 7px), -50%)",
7204
6155
  pointerEvents: dragDisabled ? "none" : "auto"
7205
6156
  },
7206
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6157
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7207
6158
  DragHandle,
7208
6159
  {
7209
6160
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7307,7 +6258,7 @@ function FloatingToolbar({
7307
6258
  onEditLink
7308
6259
  }) {
7309
6260
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7310
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6261
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7311
6262
  "div",
7312
6263
  {
7313
6264
  ref: elRef,
@@ -7317,23 +6268,14 @@ function FloatingToolbar({
7317
6268
  left,
7318
6269
  transform,
7319
6270
  zIndex: 2147483647,
7320
- background: "#fff",
7321
- border: "1px solid #E7E5E4",
7322
- borderRadius: 6,
7323
- boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
7324
- display: "flex",
7325
- alignItems: "center",
7326
- padding: TOOLBAR_PILL_PADDING,
7327
- gap: TOOLBAR_TOGGLE_GAP,
7328
- fontFamily: "sans-serif",
7329
6271
  pointerEvents: "auto"
7330
6272
  },
7331
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CustomToolbar, { children: [
7332
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react8.default.Fragment, { children: [
7333
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CustomToolbarDivider, {}),
6273
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(CustomToolbar, { children: [
6274
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react6.default.Fragment, { children: [
6275
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarDivider, {}),
7334
6276
  btns.map((btn) => {
7335
6277
  const isActive = activeCommands.has(btn.cmd);
7336
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6278
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7337
6279
  CustomToolbarButton,
7338
6280
  {
7339
6281
  title: btn.title,
@@ -7342,7 +6284,7 @@ function FloatingToolbar({
7342
6284
  e.preventDefault();
7343
6285
  onCommand(btn.cmd);
7344
6286
  },
7345
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6287
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7346
6288
  "svg",
7347
6289
  {
7348
6290
  width: "16",
@@ -7363,7 +6305,7 @@ function FloatingToolbar({
7363
6305
  );
7364
6306
  })
7365
6307
  ] }, gi)),
7366
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6308
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7367
6309
  CustomToolbarButton,
7368
6310
  {
7369
6311
  type: "button",
@@ -7377,7 +6319,7 @@ function FloatingToolbar({
7377
6319
  e.preventDefault();
7378
6320
  e.stopPropagation();
7379
6321
  },
7380
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
6322
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react3.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7381
6323
  }
7382
6324
  ) : null
7383
6325
  ] })
@@ -7394,7 +6336,7 @@ function StateToggle({
7394
6336
  states,
7395
6337
  onStateChange
7396
6338
  }) {
7397
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6339
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7398
6340
  ToggleGroup,
7399
6341
  {
7400
6342
  "data-ohw-state-toggle": "",
@@ -7408,7 +6350,7 @@ function StateToggle({
7408
6350
  left: rect.right - 8,
7409
6351
  transform: "translateX(-100%)"
7410
6352
  },
7411
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6353
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7412
6354
  }
7413
6355
  );
7414
6356
  }
@@ -7431,12 +6373,12 @@ function resolveSubdomain(subdomainFromQuery) {
7431
6373
  return "";
7432
6374
  }
7433
6375
  function OhhwellsBridge() {
7434
- const pathname = (0, import_navigation2.usePathname)();
7435
- const router = (0, import_navigation2.useRouter)();
7436
- const searchParams = (0, import_navigation2.useSearchParams)();
6376
+ const pathname = (0, import_navigation.usePathname)();
6377
+ const router = (0, import_navigation.useRouter)();
6378
+ const searchParams = (0, import_navigation.useSearchParams)();
7437
6379
  const isEditMode = isEditSessionActive();
7438
- const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7439
- (0, import_react8.useEffect)(() => {
6380
+ const [bridgeRoot, setBridgeRoot] = (0, import_react6.useState)(null);
6381
+ (0, import_react6.useEffect)(() => {
7440
6382
  const figtreeFontId = "ohw-figtree-font";
7441
6383
  if (!document.getElementById(figtreeFontId)) {
7442
6384
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7444,10 +6386,7 @@ function OhhwellsBridge() {
7444
6386
  const stylesheet = Object.assign(document.createElement("link"), {
7445
6387
  id: figtreeFontId,
7446
6388
  rel: "stylesheet",
7447
- // Inter is loaded alongside Figtree for the media overlay's Replace button. The bridge
7448
- // renders inside the customer's document, so it cannot assume any font is present — an
7449
- // unloaded family would silently fall back to whatever the template happens to use.
7450
- href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&family=Inter:wght@400;500;600&display=swap"
6389
+ href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
7451
6390
  });
7452
6391
  document.head.append(preconnect1, preconnect2, stylesheet);
7453
6392
  }
@@ -7464,90 +6403,80 @@ function OhhwellsBridge() {
7464
6403
  const subdomainFromQuery = searchParams.get("subdomain");
7465
6404
  const subdomain = resolveSubdomain(subdomainFromQuery);
7466
6405
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7467
- const postToParent2 = (0, import_react8.useCallback)((data) => {
6406
+ const postToParent = (0, import_react6.useCallback)((data) => {
7468
6407
  if (typeof window !== "undefined" && window.parent !== window) {
7469
6408
  window.parent.postMessage(data, "*");
7470
6409
  }
7471
6410
  }, []);
7472
- const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
7473
- const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
7474
- const activeElRef = (0, import_react8.useRef)(null);
7475
- const selectedElRef = (0, import_react8.useRef)(null);
7476
- const originalContentRef = (0, import_react8.useRef)(null);
7477
- const activeStateElRef = (0, import_react8.useRef)(null);
7478
- const parentScrollRef = (0, import_react8.useRef)(null);
7479
- const visibleViewportRef = (0, import_react8.useRef)(null);
7480
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
7481
- const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
6411
+ const [fetchState, setFetchState] = (0, import_react6.useState)("idle");
6412
+ const autoSaveTimers = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
6413
+ const activeElRef = (0, import_react6.useRef)(null);
6414
+ const selectedElRef = (0, import_react6.useRef)(null);
6415
+ const originalContentRef = (0, import_react6.useRef)(null);
6416
+ const activeStateElRef = (0, import_react6.useRef)(null);
6417
+ const parentScrollRef = (0, import_react6.useRef)(null);
6418
+ const visibleViewportRef = (0, import_react6.useRef)(null);
6419
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react6.useState)(null);
6420
+ const attachVisibleViewport = (0, import_react6.useCallback)((node) => {
7482
6421
  visibleViewportRef.current = node;
7483
6422
  setDialogPortalContainer(node);
7484
6423
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7485
6424
  }, []);
7486
- const toolbarElRef = (0, import_react8.useRef)(null);
7487
- const glowElRef = (0, import_react8.useRef)(null);
7488
- const hoveredImageRef = (0, import_react8.useRef)(null);
7489
- const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
7490
- const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7491
- const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
7492
- const hoveredGapRef = (0, import_react8.useRef)(null);
7493
- const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7494
- const imageShowTimerRef = (0, import_react8.useRef)(null);
7495
- const editStylesRef = (0, import_react8.useRef)(null);
7496
- const activateRef = (0, import_react8.useRef)(() => {
7497
- });
7498
- const deactivateRef = (0, import_react8.useRef)(() => {
6425
+ const toolbarElRef = (0, import_react6.useRef)(null);
6426
+ const glowElRef = (0, import_react6.useRef)(null);
6427
+ const hoveredImageRef = (0, import_react6.useRef)(null);
6428
+ const hoveredImageHasTextOverlapRef = (0, import_react6.useRef)(false);
6429
+ const hoveredGapRef = (0, import_react6.useRef)(null);
6430
+ const imageUnhoverTimerRef = (0, import_react6.useRef)(null);
6431
+ const imageShowTimerRef = (0, import_react6.useRef)(null);
6432
+ const editStylesRef = (0, import_react6.useRef)(null);
6433
+ const activateRef = (0, import_react6.useRef)(() => {
7499
6434
  });
7500
- const selectRef = (0, import_react8.useRef)(() => {
6435
+ const deactivateRef = (0, import_react6.useRef)(() => {
7501
6436
  });
7502
- const selectFrameRef = (0, import_react8.useRef)(() => {
6437
+ const selectRef = (0, import_react6.useRef)(() => {
7503
6438
  });
7504
- const deselectRef = (0, import_react8.useRef)(() => {
6439
+ const deselectRef = (0, import_react6.useRef)(() => {
7505
6440
  });
7506
- const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
6441
+ const reselectNavigationItemRef = (0, import_react6.useRef)(() => {
7507
6442
  });
7508
- const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
6443
+ const commitNavigationTextEditRef = (0, import_react6.useRef)(() => {
7509
6444
  });
7510
- const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
6445
+ const refreshActiveCommandsRef = (0, import_react6.useRef)(() => {
7511
6446
  });
7512
- const postToParentRef = (0, import_react8.useRef)(postToParent2);
7513
- postToParentRef.current = postToParent2;
7514
- const sectionsLoadedRef = (0, import_react8.useRef)(false);
7515
- const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
7516
- const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
7517
- const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7518
- const toolbarVariantRef = (0, import_react8.useRef)("none");
6447
+ const postToParentRef = (0, import_react6.useRef)(postToParent);
6448
+ postToParentRef.current = postToParent;
6449
+ const sectionsLoadedRef = (0, import_react6.useRef)(false);
6450
+ const pendingScheduleConfigRequests = (0, import_react6.useRef)([]);
6451
+ const [toolbarRect, setToolbarRect] = (0, import_react6.useState)(null);
6452
+ const [toolbarVariant, setToolbarVariant] = (0, import_react6.useState)("none");
6453
+ const toolbarVariantRef = (0, import_react6.useRef)("none");
7519
6454
  toolbarVariantRef.current = toolbarVariant;
7520
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7521
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7522
- const [toggleState, setToggleState] = (0, import_react8.useState)(null);
7523
- const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
7524
- const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
7525
- const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
7526
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
7527
- const hoveredNavContainerRef = (0, import_react8.useRef)(null);
7528
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
7529
- const hoveredItemElRef = (0, import_react8.useRef)(null);
7530
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7531
- const siblingHintElRef = (0, import_react8.useRef)(null);
7532
- const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
7533
- const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
7534
- const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7535
- const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7536
- const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
7537
- const editContentRef = (0, import_react8.useRef)({});
7538
- const [sitePages, setSitePages] = (0, import_react8.useState)([]);
7539
- const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
7540
- const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
7541
- const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
7542
- const linkPopoverPanelRef = (0, import_react8.useRef)(null);
7543
- const linkPopoverOpenRef = (0, import_react8.useRef)(false);
7544
- const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
6455
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react6.useState)(null);
6456
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react6.useState)(false);
6457
+ const [toggleState, setToggleState] = (0, import_react6.useState)(null);
6458
+ const [maxBadge, setMaxBadge] = (0, import_react6.useState)(null);
6459
+ const [activeCommands, setActiveCommands] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
6460
+ const [sectionGap, setSectionGap] = (0, import_react6.useState)(null);
6461
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react6.useState)(false);
6462
+ const hoveredItemElRef = (0, import_react6.useRef)(null);
6463
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react6.useState)(null);
6464
+ const siblingHintElRef = (0, import_react6.useRef)(null);
6465
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react6.useState)(null);
6466
+ const [isItemDragging, setIsItemDragging] = (0, import_react6.useState)(false);
6467
+ const [linkPopover, setLinkPopover] = (0, import_react6.useState)(null);
6468
+ const [sitePages, setSitePages] = (0, import_react6.useState)([]);
6469
+ const [sectionsByPath, setSectionsByPath] = (0, import_react6.useState)({});
6470
+ const sectionsPrefetchGenRef = (0, import_react6.useRef)(0);
6471
+ const setLinkPopoverRef = (0, import_react6.useRef)(setLinkPopover);
6472
+ const linkPopoverPanelRef = (0, import_react6.useRef)(null);
6473
+ const linkPopoverOpenRef = (0, import_react6.useRef)(false);
6474
+ const linkPopoverGraceUntilRef = (0, import_react6.useRef)(0);
7545
6475
  setLinkPopoverRef.current = setLinkPopover;
7546
- linkPopoverSessionRef.current = linkPopover;
7547
6476
  const bumpLinkPopoverGrace = () => {
7548
6477
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7549
6478
  };
7550
- const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
6479
+ const runSectionsPrefetch = (0, import_react6.useCallback)((pages) => {
7551
6480
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7552
6481
  const gen = ++sectionsPrefetchGenRef.current;
7553
6482
  const paths = pages.map((p) => p.path);
@@ -7566,9 +6495,9 @@ function OhhwellsBridge() {
7566
6495
  );
7567
6496
  });
7568
6497
  }, [isEditMode, pathname]);
7569
- const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
6498
+ const runSectionsPrefetchRef = (0, import_react6.useRef)(runSectionsPrefetch);
7570
6499
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7571
- (0, import_react8.useEffect)(() => {
6500
+ (0, import_react6.useEffect)(() => {
7572
6501
  if (!linkPopover) return;
7573
6502
  if (hoveredImageRef.current) {
7574
6503
  hoveredImageRef.current = null;
@@ -7576,9 +6505,33 @@ function OhhwellsBridge() {
7576
6505
  }
7577
6506
  hoveredGapRef.current = null;
7578
6507
  setSectionGap(null);
7579
- postToParent2({ type: "ow:image-unhover" });
7580
- }, [linkPopover, postToParent2]);
7581
- (0, import_react8.useEffect)(() => {
6508
+ postToParent({ type: "ow:image-unhover" });
6509
+ postToParent({ type: "ow:link-modal-lock", locked: true });
6510
+ const html = document.documentElement;
6511
+ const body = document.body;
6512
+ const prevHtmlOverflow = html.style.overflow;
6513
+ const prevBodyOverflow = body.style.overflow;
6514
+ html.style.overflow = "hidden";
6515
+ body.style.overflow = "hidden";
6516
+ const preventBackgroundScroll = (e) => {
6517
+ const target = e.target;
6518
+ if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
6519
+ return;
6520
+ }
6521
+ e.preventDefault();
6522
+ };
6523
+ const scrollOpts = { passive: false, capture: true };
6524
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6525
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6526
+ return () => {
6527
+ postToParent({ type: "ow:link-modal-lock", locked: false });
6528
+ html.style.overflow = prevHtmlOverflow;
6529
+ body.style.overflow = prevBodyOverflow;
6530
+ document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6531
+ document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6532
+ };
6533
+ }, [linkPopover, postToParent]);
6534
+ (0, import_react6.useEffect)(() => {
7582
6535
  if (!isEditMode) return;
7583
6536
  const useFixtures = shouldUseDevFixtures();
7584
6537
  if (useFixtures) {
@@ -7599,17 +6552,17 @@ function OhhwellsBridge() {
7599
6552
  runSectionsPrefetchRef.current(mapped);
7600
6553
  };
7601
6554
  window.addEventListener("message", onSitePages);
7602
- if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6555
+ if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
7603
6556
  return () => window.removeEventListener("message", onSitePages);
7604
- }, [isEditMode, postToParent2]);
7605
- (0, import_react8.useEffect)(() => {
6557
+ }, [isEditMode, postToParent]);
6558
+ (0, import_react6.useEffect)(() => {
7606
6559
  if (!isEditMode || shouldUseDevFixtures()) return;
7607
6560
  void loadAllSectionsManifest().then((manifest) => {
7608
6561
  if (Object.keys(manifest).length === 0) return;
7609
6562
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7610
6563
  });
7611
6564
  }, [isEditMode]);
7612
- (0, import_react8.useEffect)(() => {
6565
+ (0, import_react6.useEffect)(() => {
7613
6566
  const update = () => {
7614
6567
  const el = activeElRef.current ?? selectedElRef.current;
7615
6568
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7633,10 +6586,10 @@ function OhhwellsBridge() {
7633
6586
  vvp.removeEventListener("resize", update);
7634
6587
  };
7635
6588
  }, []);
7636
- const refreshStateRules = (0, import_react8.useCallback)(() => {
6589
+ const refreshStateRules = (0, import_react6.useCallback)(() => {
7637
6590
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7638
6591
  }, []);
7639
- const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
6592
+ const processConfigRequest = (0, import_react6.useCallback)((insertAfterVal) => {
7640
6593
  const tracker = getSectionsTracker();
7641
6594
  let entries = [];
7642
6595
  try {
@@ -7659,7 +6612,7 @@ function OhhwellsBridge() {
7659
6612
  }
7660
6613
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7661
6614
  }, [isEditMode]);
7662
- const deactivate = (0, import_react8.useCallback)(() => {
6615
+ const deactivate = (0, import_react6.useCallback)(() => {
7663
6616
  const el = activeElRef.current;
7664
6617
  if (!el) return;
7665
6618
  const key = el.dataset.ohwKey;
@@ -7688,23 +6641,21 @@ function OhhwellsBridge() {
7688
6641
  setMaxBadge(null);
7689
6642
  setActiveCommands(/* @__PURE__ */ new Set());
7690
6643
  setToolbarShowEditLink(false);
7691
- postToParent2({ type: "ow:exit-edit" });
7692
- }, [postToParent2]);
7693
- const deselect = (0, import_react8.useCallback)(() => {
6644
+ postToParent({ type: "ow:exit-edit" });
6645
+ }, [postToParent]);
6646
+ const deselect = (0, import_react6.useCallback)(() => {
7694
6647
  selectedElRef.current = null;
7695
6648
  setReorderHrefKey(null);
7696
6649
  setReorderDragDisabled(false);
7697
6650
  siblingHintElRef.current = null;
7698
6651
  setSiblingHintRect(null);
7699
6652
  setIsItemDragging(false);
7700
- hoveredNavContainerRef.current = null;
7701
- setHoveredNavContainerRect(null);
7702
6653
  if (!activeElRef.current) {
7703
6654
  setToolbarRect(null);
7704
6655
  setToolbarVariant("none");
7705
6656
  }
7706
6657
  }, []);
7707
- const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
6658
+ const reselectNavigationItem = (0, import_react6.useCallback)((navAnchor) => {
7708
6659
  selectedElRef.current = navAnchor;
7709
6660
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
7710
6661
  setReorderHrefKey(key);
@@ -7714,7 +6665,7 @@ function OhhwellsBridge() {
7714
6665
  setToolbarShowEditLink(false);
7715
6666
  setActiveCommands(/* @__PURE__ */ new Set());
7716
6667
  }, []);
7717
- const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
6668
+ const commitNavigationTextEdit = (0, import_react6.useCallback)((navAnchor) => {
7718
6669
  const el = activeElRef.current;
7719
6670
  if (!el) return;
7720
6671
  const key = el.dataset.ohwKey;
@@ -7727,9 +6678,9 @@ function OhhwellsBridge() {
7727
6678
  const html = sanitizeHtml(el.innerHTML);
7728
6679
  const original = originalContentRef.current ?? "";
7729
6680
  if (html !== sanitizeHtml(original)) {
7730
- postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
6681
+ postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
7731
6682
  const h = document.documentElement.scrollHeight;
7732
- if (h > 50) postToParent2({ type: "ow:height", height: h });
6683
+ if (h > 50) postToParent({ type: "ow:height", height: h });
7733
6684
  }
7734
6685
  }
7735
6686
  el.removeAttribute("contenteditable");
@@ -7737,38 +6688,31 @@ function OhhwellsBridge() {
7737
6688
  setMaxBadge(null);
7738
6689
  setActiveCommands(/* @__PURE__ */ new Set());
7739
6690
  setToolbarShowEditLink(false);
7740
- postToParent2({ type: "ow:exit-edit" });
6691
+ postToParent({ type: "ow:exit-edit" });
7741
6692
  reselectNavigationItem(navAnchor);
7742
- }, [postToParent2, reselectNavigationItem]);
7743
- const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
7744
- const items = listNavbarItems();
7745
- addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7746
- deselectRef.current();
7747
- deactivateRef.current();
7748
- bumpLinkPopoverGrace();
7749
- setLinkPopover({
7750
- key: "__add-nav__",
7751
- mode: "create",
7752
- intent: "add-nav"
7753
- });
6693
+ }, [postToParent, reselectNavigationItem]);
6694
+ const handleAddNavigationItem = (0, import_react6.useCallback)(() => {
6695
+ const selected = selectedElRef.current;
6696
+ if (!selected) return;
6697
+ const target = getNavigationItemAddHintTarget(selected);
6698
+ siblingHintElRef.current = target;
6699
+ setSiblingHintRect(target.getBoundingClientRect());
7754
6700
  }, []);
7755
- const handleItemDragStart = (0, import_react8.useCallback)(() => {
6701
+ const handleItemDragStart = (0, import_react6.useCallback)(() => {
7756
6702
  siblingHintElRef.current = null;
7757
6703
  setSiblingHintRect(null);
7758
6704
  setIsItemDragging(true);
7759
6705
  }, []);
7760
- const handleItemDragEnd = (0, import_react8.useCallback)(() => {
6706
+ const handleItemDragEnd = (0, import_react6.useCallback)(() => {
7761
6707
  setIsItemDragging(false);
7762
6708
  }, []);
7763
6709
  reselectNavigationItemRef.current = reselectNavigationItem;
7764
6710
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
7765
- const select = (0, import_react8.useCallback)((anchor) => {
6711
+ const select = (0, import_react6.useCallback)((anchor) => {
7766
6712
  if (!isNavigationItem(anchor)) return;
7767
6713
  if (activeElRef.current) deactivate();
7768
6714
  selectedElRef.current = anchor;
7769
6715
  clearHrefKeyHover(anchor);
7770
- hoveredNavContainerRef.current = null;
7771
- setHoveredNavContainerRect(null);
7772
6716
  setHoveredItemRect(null);
7773
6717
  hoveredItemElRef.current = null;
7774
6718
  siblingHintElRef.current = null;
@@ -7782,32 +6726,13 @@ function OhhwellsBridge() {
7782
6726
  setToolbarShowEditLink(false);
7783
6727
  setActiveCommands(/* @__PURE__ */ new Set());
7784
6728
  }, [deactivate]);
7785
- const selectFrame = (0, import_react8.useCallback)((el) => {
7786
- if (!isNavigationContainer(el)) return;
7787
- if (activeElRef.current) deactivate();
7788
- selectedElRef.current = el;
7789
- clearHrefKeyHover(el);
7790
- hoveredNavContainerRef.current = null;
7791
- setHoveredNavContainerRect(null);
7792
- setHoveredItemRect(null);
7793
- hoveredItemElRef.current = null;
7794
- siblingHintElRef.current = null;
7795
- setSiblingHintRect(null);
7796
- setIsItemDragging(false);
7797
- setReorderHrefKey(null);
7798
- setReorderDragDisabled(false);
7799
- setToolbarVariant("select-frame");
7800
- setToolbarRect(el.getBoundingClientRect());
7801
- setToolbarShowEditLink(false);
7802
- setActiveCommands(/* @__PURE__ */ new Set());
7803
- }, [deactivate]);
7804
- const activate = (0, import_react8.useCallback)((el, options) => {
6729
+ const activate = (0, import_react6.useCallback)((el) => {
7805
6730
  if (activeElRef.current === el) return;
7806
6731
  selectedElRef.current = null;
7807
6732
  deactivate();
7808
6733
  if (hoveredImageRef.current) {
7809
6734
  hoveredImageRef.current = null;
7810
- setMediaHover(null);
6735
+ postToParentRef.current({ type: "ow:image-unhover" });
7811
6736
  }
7812
6737
  setToolbarVariant("rich-text");
7813
6738
  siblingHintElRef.current = null;
@@ -7819,9 +6744,6 @@ function OhhwellsBridge() {
7819
6744
  activeElRef.current = el;
7820
6745
  originalContentRef.current = el.innerHTML;
7821
6746
  el.focus();
7822
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7823
- placeCaretAtPoint(el, options.caretX, options.caretY);
7824
- }
7825
6747
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7826
6748
  const navAnchor = getNavigationItemAnchor(el);
7827
6749
  if (navAnchor) {
@@ -7833,15 +6755,14 @@ function OhhwellsBridge() {
7833
6755
  setReorderDragDisabled(reorderDisabled);
7834
6756
  }
7835
6757
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7836
- postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
6758
+ postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7837
6759
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
7838
- }, [deactivate, postToParent2]);
6760
+ }, [deactivate, postToParent]);
7839
6761
  activateRef.current = activate;
7840
6762
  deactivateRef.current = deactivate;
7841
6763
  selectRef.current = select;
7842
- selectFrameRef.current = selectFrame;
7843
6764
  deselectRef.current = deselect;
7844
- (0, import_react8.useLayoutEffect)(() => {
6765
+ (0, import_react6.useLayoutEffect)(() => {
7845
6766
  if (!subdomain || isEditMode) {
7846
6767
  setFetchState("done");
7847
6768
  return;
@@ -7850,7 +6771,6 @@ function OhhwellsBridge() {
7850
6771
  const imageLoads = [];
7851
6772
  for (const [key, val] of Object.entries(content)) {
7852
6773
  if (key === "__ohw_sections") continue;
7853
- if (applyVideoSettingNode(key, val)) continue;
7854
6774
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7855
6775
  if (el.dataset.ohwEditable === "image") {
7856
6776
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7864,15 +6784,6 @@ function OhhwellsBridge() {
7864
6784
  } else if (el.dataset.ohwEditable === "bg-image") {
7865
6785
  const next = `url('${val}')`;
7866
6786
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
7867
- } else if (el.dataset.ohwEditable === "video") {
7868
- const video = getVideoEl(el);
7869
- if (video && video.src !== val) {
7870
- applyVideoSrc(video, val);
7871
- imageLoads.push(new Promise((resolve) => {
7872
- video.onloadeddata = () => resolve();
7873
- video.onerror = () => resolve();
7874
- }));
7875
- }
7876
6787
  } else if (el.dataset.ohwEditable === "link") {
7877
6788
  applyLinkHref(el, val);
7878
6789
  } else if (el.innerHTML !== val) {
@@ -7881,7 +6792,6 @@ function OhhwellsBridge() {
7881
6792
  });
7882
6793
  applyLinkByKey(key, val);
7883
6794
  }
7884
- reconcileNavbarItemsFromContent(content);
7885
6795
  enforceLinkHrefs();
7886
6796
  initSectionsFromContent(content, true);
7887
6797
  sectionsLoadedRef.current = true;
@@ -7910,7 +6820,7 @@ function OhhwellsBridge() {
7910
6820
  cancelled = true;
7911
6821
  };
7912
6822
  }, [subdomain, isEditMode]);
7913
- (0, import_react8.useEffect)(() => {
6823
+ (0, import_react6.useEffect)(() => {
7914
6824
  if (!subdomain || isEditMode) return;
7915
6825
  let debounceTimer = null;
7916
6826
  let observer = null;
@@ -7922,7 +6832,6 @@ function OhhwellsBridge() {
7922
6832
  try {
7923
6833
  for (const [key, val] of Object.entries(content)) {
7924
6834
  if (key === "__ohw_sections") continue;
7925
- if (applyVideoSettingNode(key, val)) continue;
7926
6835
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7927
6836
  if (el.dataset.ohwEditable === "image") {
7928
6837
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -7930,9 +6839,6 @@ function OhhwellsBridge() {
7930
6839
  } else if (el.dataset.ohwEditable === "bg-image") {
7931
6840
  const next = `url('${val}')`;
7932
6841
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
7933
- } else if (el.dataset.ohwEditable === "video") {
7934
- const video = getVideoEl(el);
7935
- if (video && video.src !== val) applyVideoSrc(video, val);
7936
6842
  } else if (el.dataset.ohwEditable === "link") {
7937
6843
  applyLinkHref(el, val);
7938
6844
  } else if (el.innerHTML !== val) {
@@ -7941,7 +6847,6 @@ function OhhwellsBridge() {
7941
6847
  });
7942
6848
  applyLinkByKey(key, val);
7943
6849
  }
7944
- reconcileNavbarItemsFromContent(content);
7945
6850
  } finally {
7946
6851
  observer?.observe(document.body, { childList: true, subtree: true });
7947
6852
  }
@@ -7959,44 +6864,26 @@ function OhhwellsBridge() {
7959
6864
  if (debounceTimer) clearTimeout(debounceTimer);
7960
6865
  };
7961
6866
  }, [subdomain, isEditMode, pathname]);
7962
- (0, import_react8.useLayoutEffect)(() => {
6867
+ (0, import_react6.useLayoutEffect)(() => {
7963
6868
  const el = document.getElementById("ohw-loader");
7964
6869
  if (!el) return;
7965
6870
  const visible = Boolean(subdomain) && fetchState !== "done";
7966
6871
  el.style.display = visible ? "flex" : "none";
7967
6872
  }, [subdomain, fetchState]);
7968
- (0, import_react8.useEffect)(() => {
7969
- postToParent2({ type: "ow:navigation", path: pathname });
7970
- }, [pathname, postToParent2]);
7971
- (0, import_react8.useEffect)(() => {
6873
+ (0, import_react6.useEffect)(() => {
6874
+ postToParent({ type: "ow:navigation", path: pathname });
6875
+ }, [pathname, postToParent]);
6876
+ (0, import_react6.useEffect)(() => {
7972
6877
  if (!isEditMode) return;
7973
- if (linkPopoverSessionRef.current?.intent === "add-nav") return;
7974
- if (document.querySelector("[data-ohw-section-picker]")) return;
7975
6878
  setLinkPopover(null);
7976
6879
  deselectRef.current();
7977
6880
  deactivateRef.current();
7978
6881
  }, [pathname, isEditMode]);
7979
- (0, import_react8.useEffect)(() => {
7980
- const contentForNav = () => {
7981
- if (isEditMode) return editContentRef.current;
7982
- if (!subdomain) return {};
7983
- return contentCache.get(subdomain) ?? {};
7984
- };
7985
- const run = () => reconcileNavbarItemsFromContent(contentForNav());
7986
- run();
7987
- const nav = document.querySelector("nav");
7988
- if (!nav) return;
7989
- const observer = new MutationObserver(() => {
7990
- requestAnimationFrame(run);
7991
- });
7992
- observer.observe(nav, { childList: true, subtree: true });
7993
- return () => observer.disconnect();
7994
- }, [isEditMode, pathname, subdomain, fetchState]);
7995
- (0, import_react8.useEffect)(() => {
6882
+ (0, import_react6.useEffect)(() => {
7996
6883
  if (!isEditMode) return;
7997
6884
  const measure = () => {
7998
6885
  const h = document.body.scrollHeight;
7999
- if (h > 50) postToParent2({ type: "ow:height", height: h });
6886
+ if (h > 50) postToParent({ type: "ow:height", height: h });
8000
6887
  };
8001
6888
  const t1 = setTimeout(measure, 50);
8002
6889
  const t2 = setTimeout(measure, 500);
@@ -8015,8 +6902,8 @@ function OhhwellsBridge() {
8015
6902
  if (resizeTimer) clearTimeout(resizeTimer);
8016
6903
  window.removeEventListener("resize", handleResize);
8017
6904
  };
8018
- }, [pathname, isEditMode, postToParent2]);
8019
- (0, import_react8.useEffect)(() => {
6905
+ }, [pathname, isEditMode, postToParent]);
6906
+ (0, import_react6.useEffect)(() => {
8020
6907
  if (!subdomainFromQuery || isEditMode) return;
8021
6908
  const handleClick = (e) => {
8022
6909
  const anchor = e.target.closest("a");
@@ -8032,7 +6919,7 @@ function OhhwellsBridge() {
8032
6919
  document.addEventListener("click", handleClick, true);
8033
6920
  return () => document.removeEventListener("click", handleClick, true);
8034
6921
  }, [subdomainFromQuery, isEditMode, router]);
8035
- (0, import_react8.useEffect)(() => {
6922
+ (0, import_react6.useEffect)(() => {
8036
6923
  if (!isEditMode) {
8037
6924
  editStylesRef.current?.base.remove();
8038
6925
  editStylesRef.current?.forceHover.remove();
@@ -8055,9 +6942,8 @@ function OhhwellsBridge() {
8055
6942
  [data-ohw-editable] {
8056
6943
  display: block;
8057
6944
  }
8058
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
6945
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]) { cursor: text !important; }
8059
6946
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8060
- [data-ohw-editable="video"], [data-ohw-editable="video"] *,
8061
6947
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
8062
6948
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
8063
6949
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
@@ -8106,13 +6992,6 @@ function OhhwellsBridge() {
8106
6992
  if (target.closest("[data-ohw-max-badge]")) return;
8107
6993
  if (isInsideLinkEditor(target)) return;
8108
6994
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8109
- const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8110
- if (navFromChrome) {
8111
- e.preventDefault();
8112
- e.stopPropagation();
8113
- selectFrameRef.current(navFromChrome);
8114
- return;
8115
- }
8116
6995
  e.preventDefault();
8117
6996
  e.stopPropagation();
8118
6997
  return;
@@ -8127,15 +7006,14 @@ function OhhwellsBridge() {
8127
7006
  bumpLinkPopoverGrace();
8128
7007
  setLinkPopoverRef.current({
8129
7008
  key: editable.dataset.ohwKey ?? "",
8130
- mode: "edit",
8131
- target: getLinkHref2(editable)
7009
+ target: getLinkHref(editable)
8132
7010
  });
8133
7011
  return;
8134
7012
  }
8135
- if (isMediaEditable(editable)) {
7013
+ if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
8136
7014
  e.preventDefault();
8137
7015
  e.stopPropagation();
8138
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
7016
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
8139
7017
  return;
8140
7018
  }
8141
7019
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -8144,8 +7022,7 @@ function OhhwellsBridge() {
8144
7022
  e.preventDefault();
8145
7023
  e.stopPropagation();
8146
7024
  if (selectedElRef.current === navAnchor) {
8147
- if (e.detail >= 2) return;
8148
- activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
7025
+ activateRef.current(editable);
8149
7026
  return;
8150
7027
  }
8151
7028
  selectRef.current(navAnchor);
@@ -8164,22 +7041,6 @@ function OhhwellsBridge() {
8164
7041
  selectRef.current(hrefAnchor);
8165
7042
  return;
8166
7043
  }
8167
- const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8168
- if (navContainerToSelect) {
8169
- e.preventDefault();
8170
- e.stopPropagation();
8171
- selectFrameRef.current(navContainerToSelect);
8172
- return;
8173
- }
8174
- const selectedContainer = selectedElRef.current;
8175
- if (selectedContainer && isNavigationContainer(selectedContainer)) {
8176
- const navItem = getNavigationItemAnchor(target);
8177
- if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
8178
- e.preventDefault();
8179
- e.stopPropagation();
8180
- return;
8181
- }
8182
- }
8183
7044
  if (activeElRef.current) {
8184
7045
  const sel = window.getSelection();
8185
7046
  if (sel && !sel.isCollapsed) {
@@ -8205,48 +7066,10 @@ function OhhwellsBridge() {
8205
7066
  deselectRef.current();
8206
7067
  deactivateRef.current();
8207
7068
  };
8208
- const handleDblClick = (e) => {
8209
- const target = e.target;
8210
- if (target.closest("[data-ohw-toolbar]")) return;
8211
- if (target.closest("[data-ohw-state-toggle]")) return;
8212
- if (target.closest("[data-ohw-max-badge]")) return;
8213
- if (isInsideLinkEditor(target)) return;
8214
- if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8215
- return;
8216
- }
8217
- const navLabel = getNavigationLabelEditable(target);
8218
- if (!navLabel) return;
8219
- const { editable } = navLabel;
8220
- e.preventDefault();
8221
- e.stopPropagation();
8222
- if (activeElRef.current !== editable) {
8223
- activateRef.current(editable);
8224
- }
8225
- requestAnimationFrame(() => {
8226
- selectAllTextInEditable(editable);
8227
- refreshActiveCommandsRef.current();
8228
- });
8229
- };
8230
7069
  const handleMouseOver = (e) => {
8231
7070
  const target = e.target;
8232
- if (toolbarVariantRef.current !== "select-frame") {
8233
- const navContainer = target.closest("[data-ohw-nav-container]");
8234
- if (navContainer && !getNavigationItemAnchor(target)) {
8235
- hoveredNavContainerRef.current = navContainer;
8236
- setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8237
- hoveredItemElRef.current = null;
8238
- setHoveredItemRect(null);
8239
- return;
8240
- }
8241
- if (!target.closest("[data-ohw-nav-container]")) {
8242
- hoveredNavContainerRef.current = null;
8243
- setHoveredNavContainerRect(null);
8244
- }
8245
- }
8246
7071
  const navAnchor = getNavigationItemAnchor(target);
8247
7072
  if (navAnchor) {
8248
- hoveredNavContainerRef.current = null;
8249
- setHoveredNavContainerRect(null);
8250
7073
  const selected2 = selectedElRef.current;
8251
7074
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
8252
7075
  clearHrefKeyHover(navAnchor);
@@ -8258,7 +7081,7 @@ function OhhwellsBridge() {
8258
7081
  if (!editable) return;
8259
7082
  const selected = selectedElRef.current;
8260
7083
  if (selected && (selected === editable || selected.contains(editable))) return;
8261
- if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
7084
+ if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
8262
7085
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
8263
7086
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
8264
7087
  clearHrefKeyHover(hoverTarget);
@@ -8271,14 +7094,6 @@ function OhhwellsBridge() {
8271
7094
  };
8272
7095
  const handleMouseOut = (e) => {
8273
7096
  const target = e.target;
8274
- if (target.closest("[data-ohw-nav-container]")) {
8275
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8276
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8277
- return;
8278
- }
8279
- hoveredNavContainerRef.current = null;
8280
- setHoveredNavContainerRect(null);
8281
- }
8282
7097
  const navAnchor = getNavigationItemAnchor(target);
8283
7098
  if (navAnchor) {
8284
7099
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8294,7 +7109,7 @@ function OhhwellsBridge() {
8294
7109
  if (!editable) return;
8295
7110
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8296
7111
  if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
8297
- if (!isMediaEditable(editable)) {
7112
+ if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image") {
8298
7113
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
8299
7114
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
8300
7115
  if (!related?.closest("[data-ohw-href-key]")) {
@@ -8340,26 +7155,23 @@ function OhhwellsBridge() {
8340
7155
  }
8341
7156
  return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
8342
7157
  };
8343
- const showImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
7158
+ const postImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
8344
7159
  const r2 = getVisibleRect(imgEl);
8345
7160
  const hasTextOverlap = forceTextOverlap ?? false;
8346
7161
  hoveredImageHasTextOverlapRef.current = hasTextOverlap;
8347
- const video = imgEl.dataset.ohwEditable === "video" ? getVideoEl(imgEl) : null;
8348
- setMediaHover({
7162
+ postToParentRef.current({
7163
+ type: "ow:image-hover",
8349
7164
  key: imgEl.dataset.ohwKey ?? "",
8350
7165
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
8351
- elementType: imgEl.dataset.ohwEditable ?? "image",
8352
7166
  hasTextOverlap,
8353
- isDragOver,
8354
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
7167
+ ...isDragOver ? { isDragOver: true } : {}
8355
7168
  });
8356
7169
  const track = imgEl.closest("[data-ohw-hover-pause]");
8357
7170
  if (track) track.setAttribute("data-ohw-hover-paused", "");
8358
7171
  };
8359
- const clearImageHover = () => setMediaHover(null);
8360
7172
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
8361
7173
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8362
- const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
7174
+ const images = Array.from(document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]'));
8363
7175
  const matches = [];
8364
7176
  for (let i = images.length - 1; i >= 0; i--) {
8365
7177
  const el = images[i];
@@ -8375,9 +7187,7 @@ function OhhwellsBridge() {
8375
7187
  if (x >= r2.left && x <= r2.left + r2.width && y >= r2.top && y <= r2.top + r2.height) matches.push(el);
8376
7188
  }
8377
7189
  if (matches.length === 0) return null;
8378
- const imageTypeMatches = matches.filter(
8379
- (el) => el.dataset.ohwEditable === "image" || el.dataset.ohwEditable === "video"
8380
- );
7190
+ const imageTypeMatches = matches.filter((el) => el.dataset.ohwEditable === "image");
8381
7191
  const candidatePool = imageTypeMatches.length > 0 ? imageTypeMatches : matches;
8382
7192
  const smallest = candidatePool.reduce((best, el) => {
8383
7193
  const br = getVisibleRect(best);
@@ -8400,91 +7210,47 @@ function OhhwellsBridge() {
8400
7210
  }
8401
7211
  return smallest;
8402
7212
  };
8403
- const dismissImageHover = () => {
8404
- if (hoveredImageRef.current) {
8405
- hoveredImageRef.current = null;
8406
- hoveredImageHasTextOverlapRef.current = false;
8407
- resumeAnimTracks();
8408
- postToParentRef.current({ type: "ow:image-unhover" });
8409
- }
8410
- clearImageHover();
8411
- };
8412
- const probeNavigationHoverAt = (x, y) => {
8413
- if (toolbarVariantRef.current === "select-frame") {
8414
- hoveredNavContainerRef.current = null;
8415
- setHoveredNavContainerRect(null);
8416
- return;
8417
- }
8418
- const navContainer = document.querySelector("[data-ohw-nav-container]");
8419
- if (!navContainer) {
8420
- hoveredNavContainerRef.current = null;
8421
- setHoveredNavContainerRect(null);
8422
- return;
8423
- }
8424
- const containerRect = navContainer.getBoundingClientRect();
8425
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8426
- if (!overContainer) {
8427
- hoveredNavContainerRef.current = null;
8428
- setHoveredNavContainerRect(null);
8429
- return;
8430
- }
8431
- const navItemHit = Array.from(
8432
- navContainer.querySelectorAll("[data-ohw-href-key]")
8433
- ).find((el) => {
8434
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8435
- const itemRect = el.getBoundingClientRect();
8436
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8437
- });
8438
- if (navItemHit) {
8439
- hoveredNavContainerRef.current = null;
8440
- setHoveredNavContainerRect(null);
8441
- const selected = selectedElRef.current;
8442
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8443
- clearHrefKeyHover(navItemHit);
8444
- hoveredItemElRef.current = navItemHit;
8445
- setHoveredItemRect(navItemHit.getBoundingClientRect());
8446
- }
8447
- return;
8448
- }
8449
- hoveredNavContainerRef.current = navContainer;
8450
- setHoveredNavContainerRect(containerRect);
8451
- hoveredItemElRef.current = null;
8452
- setHoveredItemRect(null);
8453
- };
8454
7213
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
8455
7214
  if (linkPopoverOpenRef.current) {
8456
- dismissImageHover();
8457
- return;
8458
- }
8459
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8460
- if (isPointOverNavigation(x, y)) {
8461
- dismissImageHover();
8462
- probeNavigationHoverAt(x, y);
7215
+ if (hoveredImageRef.current) {
7216
+ hoveredImageRef.current = null;
7217
+ hoveredImageHasTextOverlapRef.current = false;
7218
+ resumeAnimTracks();
7219
+ postToParentRef.current({ type: "ow:image-unhover" });
7220
+ }
8463
7221
  return;
8464
7222
  }
8465
- hoveredNavContainerRef.current = null;
8466
- setHoveredNavContainerRect(null);
8467
7223
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
8468
7224
  if (toggleEl) {
7225
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8469
7226
  const tr = toggleEl.getBoundingClientRect();
8470
7227
  if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
8471
- dismissImageHover();
7228
+ if (hoveredImageRef.current) {
7229
+ hoveredImageRef.current = null;
7230
+ resumeAnimTracks();
7231
+ postToParentRef.current({ type: "ow:image-unhover" });
7232
+ }
8472
7233
  return;
8473
7234
  }
8474
7235
  }
8475
- const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
8476
- const activeEl = activeElRef.current;
8477
- if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
8478
- dismissImageHover();
7236
+ if (activeElRef.current) {
7237
+ if (hoveredImageRef.current) {
7238
+ hoveredImageRef.current = null;
7239
+ hoveredImageHasTextOverlapRef.current = false;
7240
+ resumeAnimTracks();
7241
+ postToParentRef.current({ type: "ow:image-unhover" });
7242
+ }
8479
7243
  return;
8480
7244
  }
7245
+ const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
8481
7246
  if (imgEl) {
7247
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8482
7248
  const topEl = document.elementFromPoint(x, y);
8483
7249
  if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8484
7250
  if (hoveredImageRef.current) {
8485
7251
  hoveredImageRef.current = null;
8486
7252
  resumeAnimTracks();
8487
- clearImageHover();
7253
+ postToParentRef.current({ type: "ow:image-unhover" });
8488
7254
  }
8489
7255
  return;
8490
7256
  }
@@ -8493,13 +7259,13 @@ function OhhwellsBridge() {
8493
7259
  hoveredImageRef.current = null;
8494
7260
  hoveredImageHasTextOverlapRef.current = false;
8495
7261
  resumeAnimTracks();
8496
- clearImageHover();
7262
+ postToParentRef.current({ type: "ow:image-unhover" });
8497
7263
  }
8498
7264
  return;
8499
7265
  }
8500
7266
  const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
8501
7267
  const textEditable = Array.from(
8502
- document.querySelectorAll(NON_MEDIA_SELECTOR)
7268
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
8503
7269
  ).find((el) => {
8504
7270
  const er = el.getBoundingClientRect();
8505
7271
  return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
@@ -8518,14 +7284,14 @@ function OhhwellsBridge() {
8518
7284
  hoveredImageRef.current = null;
8519
7285
  hoveredImageHasTextOverlapRef.current = false;
8520
7286
  resumeAnimTracks();
8521
- clearImageHover();
7287
+ postToParentRef.current({ type: "ow:image-unhover" });
8522
7288
  }
8523
7289
  return;
8524
7290
  }
8525
7291
  if (isStateCardImage) {
8526
7292
  if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
8527
7293
  hoveredImageRef.current = imgEl;
8528
- showImageHover(imgEl, isDragOver, true);
7294
+ postImageHover(imgEl, isDragOver, true);
8529
7295
  }
8530
7296
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8531
7297
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -8537,7 +7303,7 @@ function OhhwellsBridge() {
8537
7303
  hoveredImageRef.current = null;
8538
7304
  hoveredImageHasTextOverlapRef.current = false;
8539
7305
  resumeAnimTracks();
8540
- clearImageHover();
7306
+ postToParentRef.current({ type: "ow:image-unhover" });
8541
7307
  }
8542
7308
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8543
7309
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -8549,7 +7315,7 @@ function OhhwellsBridge() {
8549
7315
  if (hoveredImageRef.current) {
8550
7316
  hoveredImageRef.current = null;
8551
7317
  resumeAnimTracks();
8552
- clearImageHover();
7318
+ postToParentRef.current({ type: "ow:image-unhover" });
8553
7319
  }
8554
7320
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8555
7321
  return;
@@ -8557,9 +7323,9 @@ function OhhwellsBridge() {
8557
7323
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8558
7324
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
8559
7325
  hoveredImageRef.current = imgEl;
8560
- showImageHover(imgEl, isDragOver);
7326
+ postImageHover(imgEl, isDragOver);
8561
7327
  } else if (isDragOver) {
8562
- showImageHover(imgEl, true);
7328
+ postImageHover(imgEl, true);
8563
7329
  }
8564
7330
  } else {
8565
7331
  const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
@@ -8576,14 +7342,14 @@ function OhhwellsBridge() {
8576
7342
  hoveredImageRef.current = null;
8577
7343
  hoveredImageHasTextOverlapRef.current = false;
8578
7344
  resumeAnimTracks();
8579
- clearImageHover();
7345
+ postToParentRef.current({ type: "ow:image-unhover" });
8580
7346
  }
8581
- const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7347
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8582
7348
  const textEl = Array.from(
8583
- document.querySelectorAll(NON_MEDIA_SELECTOR)
7349
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
8584
7350
  ).find((el) => {
8585
7351
  const er = el.getBoundingClientRect();
8586
- return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7352
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8587
7353
  });
8588
7354
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8589
7355
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -8689,7 +7455,7 @@ function OhhwellsBridge() {
8689
7455
  if (hoveredImageRef.current !== el) {
8690
7456
  hoveredImageRef.current = el;
8691
7457
  const isStateCard = !!el.closest("[data-ohw-editable-state]");
8692
- showImageHover(el, true, isStateCard ? true : void 0);
7458
+ postImageHover(el, true, isStateCard ? true : void 0);
8693
7459
  }
8694
7460
  };
8695
7461
  const handleDragLeave = (e) => {
@@ -8697,7 +7463,7 @@ function OhhwellsBridge() {
8697
7463
  if (imgEl) return;
8698
7464
  hoveredImageRef.current = null;
8699
7465
  resumeAnimTracks();
8700
- clearImageHover();
7466
+ postToParentRef.current({ type: "ow:image-unhover" });
8701
7467
  };
8702
7468
  const handleDrop = (e) => {
8703
7469
  e.preventDefault();
@@ -8705,9 +7471,7 @@ function OhhwellsBridge() {
8705
7471
  if (!el) return;
8706
7472
  const file = e.dataTransfer?.files?.[0];
8707
7473
  if (file) {
8708
- const wantsVideo = el.dataset.ohwEditable === "video";
8709
- const accepted = wantsVideo ? file.type.startsWith("video/") : file.type.startsWith("image/");
8710
- if (accepted) {
7474
+ if (file.type.startsWith("image/")) {
8711
7475
  const key = el.dataset.ohwKey ?? "";
8712
7476
  const { name, type: mimeType } = file;
8713
7477
  const r2 = el.getBoundingClientRect();
@@ -8721,7 +7485,7 @@ function OhhwellsBridge() {
8721
7485
  }
8722
7486
  hoveredImageRef.current = null;
8723
7487
  resumeAnimTracks();
8724
- clearImageHover();
7488
+ postToParentRef.current({ type: "ow:image-unhover" });
8725
7489
  };
8726
7490
  const handleImageUrl = (e) => {
8727
7491
  if (e.data?.type !== "ow:image-url") return;
@@ -8739,11 +7503,7 @@ function OhhwellsBridge() {
8739
7503
  }
8740
7504
  });
8741
7505
  hoveredImageRef.current = null;
8742
- setUploadingRects((prev) => {
8743
- const entry = prev[key];
8744
- if (!entry || entry.fadingOut) return prev;
8745
- return { ...prev, [key]: { ...entry, fadingOut: true } };
8746
- });
7506
+ postToParentRef.current({ type: "ow:image-loaded", key });
8747
7507
  };
8748
7508
  let found = false;
8749
7509
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -8778,19 +7538,6 @@ function OhhwellsBridge() {
8778
7538
  requestAnimationFrame(() => onReady());
8779
7539
  }
8780
7540
  }
8781
- } else if (el.dataset.ohwEditable === "video") {
8782
- const video = getVideoEl(el);
8783
- if (video) {
8784
- found = true;
8785
- const onReady = () => {
8786
- video.onloadeddata = null;
8787
- video.onerror = null;
8788
- notify();
8789
- };
8790
- video.onloadeddata = onReady;
8791
- video.onerror = onReady;
8792
- applyVideoSrc(video, url);
8793
- }
8794
7541
  }
8795
7542
  });
8796
7543
  if (!found) notify();
@@ -8857,16 +7604,12 @@ function OhhwellsBridge() {
8857
7604
  sectionsJson = val;
8858
7605
  continue;
8859
7606
  }
8860
- if (applyVideoSettingNode(key, val)) continue;
8861
7607
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8862
7608
  if (el.dataset.ohwEditable === "image") {
8863
7609
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
8864
7610
  if (img) applyEditableImageSrc(img, val);
8865
7611
  } else if (el.dataset.ohwEditable === "bg-image") {
8866
7612
  el.style.backgroundImage = `url('${val}')`;
8867
- } else if (el.dataset.ohwEditable === "video") {
8868
- const video = getVideoEl(el);
8869
- if (video && video.src !== val) applyVideoSrc(video, val);
8870
7613
  } else if (el.dataset.ohwEditable === "link") {
8871
7614
  applyLinkHref(el, val);
8872
7615
  } else {
@@ -8880,8 +7623,6 @@ function OhhwellsBridge() {
8880
7623
  sectionsLoadedRef.current = true;
8881
7624
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
8882
7625
  }
8883
- editContentRef.current = { ...editContentRef.current, ...content };
8884
- reconcileNavbarItemsFromContent(editContentRef.current);
8885
7626
  enforceLinkHrefs();
8886
7627
  postToParentRef.current({ type: "ow:hydrate-done" });
8887
7628
  };
@@ -8889,7 +7630,6 @@ function OhhwellsBridge() {
8889
7630
  const handleDeactivate = (e) => {
8890
7631
  if (e.data?.type !== "ow:deactivate") return;
8891
7632
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
8892
- if (document.querySelector("[data-ohw-section-picker]")) return;
8893
7633
  if (linkPopoverOpenRef.current) {
8894
7634
  setLinkPopoverRef.current(null);
8895
7635
  return;
@@ -8899,32 +7639,12 @@ function OhhwellsBridge() {
8899
7639
  };
8900
7640
  window.addEventListener("message", handleDeactivate);
8901
7641
  const handleKeyDown = (e) => {
8902
- if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
8903
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
8904
- const navAnchor = getNavigationItemAnchor(activeElRef.current);
8905
- if (navAnchor) {
8906
- e.preventDefault();
8907
- selectAllTextInEditable(activeElRef.current);
8908
- refreshActiveCommandsRef.current();
8909
- return;
8910
- }
8911
- }
8912
7642
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
8913
7643
  setLinkPopoverRef.current(null);
8914
7644
  return;
8915
7645
  }
8916
7646
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
8917
- if (toolbarVariantRef.current === "select-frame") {
8918
- deselectRef.current();
8919
- return;
8920
- }
8921
- const parent = getNavigationSelectionParent(selectedElRef.current);
8922
- if (parent) {
8923
- e.preventDefault();
8924
- selectFrameRef.current(parent);
8925
- } else {
8926
- deselectRef.current();
8927
- }
7647
+ deselectRef.current();
8928
7648
  return;
8929
7649
  }
8930
7650
  if (e.key === "Escape" && activeElRef.current) {
@@ -8982,18 +7702,18 @@ function OhhwellsBridge() {
8982
7702
  if (hoveredItemElRef.current) {
8983
7703
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
8984
7704
  }
8985
- if (hoveredNavContainerRef.current) {
8986
- setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
8987
- }
8988
7705
  if (siblingHintElRef.current) {
8989
7706
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
8990
7707
  }
8991
7708
  if (hoveredImageRef.current) {
8992
7709
  const el = hoveredImageRef.current;
8993
7710
  const r2 = el.getBoundingClientRect();
8994
- setMediaHover(
8995
- (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
8996
- );
7711
+ postToParentRef.current({
7712
+ type: "ow:image-hover",
7713
+ key: el.dataset.ohwKey ?? "",
7714
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
7715
+ hasTextOverlap: hoveredImageHasTextOverlapRef.current
7716
+ });
8997
7717
  }
8998
7718
  };
8999
7719
  const handleSave = (e) => {
@@ -9082,39 +7802,19 @@ function OhhwellsBridge() {
9082
7802
  refreshActiveCommandsRef.current = handleSelectionChange;
9083
7803
  const handleDocMouseLeave = () => {
9084
7804
  hoveredImageRef.current = null;
9085
- setMediaHover(null);
9086
7805
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
9087
7806
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
9088
7807
  activeStateElRef.current = null;
9089
7808
  setToggleState(null);
9090
7809
  };
9091
- const handleImageUploading = (e) => {
9092
- if (e.data?.type !== "ow:image-uploading") return;
9093
- const { key, uploading } = e.data;
9094
- setUploadingRects((prev) => {
9095
- if (!uploading) {
9096
- if (!(key in prev)) return prev;
9097
- const next = { ...prev };
9098
- delete next[key];
9099
- return next;
9100
- }
9101
- const el = document.querySelector(`[data-ohw-key="${key}"]`);
9102
- if (!el) return prev;
9103
- const r2 = getVisibleRect(el);
9104
- return {
9105
- ...prev,
9106
- [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
9107
- };
9108
- });
7810
+ const handleAnimLock = (e) => {
7811
+ if (e.data?.type !== "ow:anim-lock") return;
7812
+ const { key } = e.data;
9109
7813
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9110
7814
  const track = el.closest("[data-ohw-hover-pause]");
9111
- if (!track) return;
9112
- if (uploading) {
7815
+ if (track) {
9113
7816
  uploadLockedTracks.add(track);
9114
7817
  track.setAttribute("data-ohw-hover-paused", "");
9115
- } else {
9116
- uploadLockedTracks.delete(track);
9117
- track.removeAttribute("data-ohw-hover-paused");
9118
7818
  }
9119
7819
  });
9120
7820
  };
@@ -9156,7 +7856,7 @@ function OhhwellsBridge() {
9156
7856
  if (e.data?.type !== "ow:click-at") return;
9157
7857
  const { clientX, clientY } = e.data;
9158
7858
  const allImages = Array.from(
9159
- document.querySelectorAll(MEDIA_SELECTOR)
7859
+ document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]')
9160
7860
  ).filter((el) => !!el.closest("[data-ohw-editable-state]"));
9161
7861
  const stateCardImage = allImages.find((el) => {
9162
7862
  const ownerCard = el.closest("[data-ohw-editable-state]");
@@ -9171,55 +7871,21 @@ function OhhwellsBridge() {
9171
7871
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
9172
7872
  });
9173
7873
  if (stateCardImage) {
9174
- postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
7874
+ postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
9175
7875
  return;
9176
7876
  }
9177
7877
  const textEditable = Array.from(
9178
- document.querySelectorAll(NON_MEDIA_SELECTOR)
7878
+ document.querySelectorAll(
7879
+ '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
7880
+ )
9179
7881
  ).find((el) => {
9180
7882
  const r2 = el.getBoundingClientRect();
9181
7883
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
9182
7884
  });
9183
7885
  if (textEditable) {
9184
- const hrefCtx = getHrefKeyFromElement(textEditable);
9185
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
9186
- if (navAnchor) {
9187
- if (selectedElRef.current === navAnchor) {
9188
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
9189
- } else {
9190
- selectRef.current(navAnchor);
9191
- }
9192
- return;
9193
- }
9194
- activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
7886
+ activateRef.current(textEditable);
9195
7887
  return;
9196
7888
  }
9197
- const navContainer = document.querySelector("[data-ohw-nav-container]");
9198
- if (navContainer) {
9199
- const r2 = navContainer.getBoundingClientRect();
9200
- const pad = 8;
9201
- const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
9202
- if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
9203
- selectFrameRef.current(navContainer);
9204
- return;
9205
- }
9206
- }
9207
- const navRoot = document.querySelector("[data-ohw-nav-root]");
9208
- if (navRoot && navContainer) {
9209
- const rr = navRoot.getBoundingClientRect();
9210
- if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
9211
- const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
9212
- if (book) {
9213
- const br = book.getBoundingClientRect();
9214
- if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
9215
- deactivateRef.current();
9216
- return;
9217
- }
9218
- }
9219
- selectFrameRef.current(navContainer);
9220
- return;
9221
- }
9222
- }
9223
7889
  deactivateRef.current();
9224
7890
  };
9225
7891
  window.addEventListener("message", handleSave);
@@ -9229,7 +7895,7 @@ function OhhwellsBridge() {
9229
7895
  window.addEventListener("message", handleClearSchedulingWidget);
9230
7896
  window.addEventListener("message", handleRemoveSchedulingSection);
9231
7897
  window.addEventListener("message", handleImageUrl);
9232
- window.addEventListener("message", handleImageUploading);
7898
+ window.addEventListener("message", handleAnimLock);
9233
7899
  window.addEventListener("message", handleCanvasHeight);
9234
7900
  window.addEventListener("message", handleParentScroll);
9235
7901
  window.addEventListener("message", handlePointerSync);
@@ -9241,7 +7907,6 @@ function OhhwellsBridge() {
9241
7907
  };
9242
7908
  window.addEventListener("resize", handleViewportResize, { passive: true });
9243
7909
  document.addEventListener("click", handleClick, true);
9244
- document.addEventListener("dblclick", handleDblClick, true);
9245
7910
  document.addEventListener("paste", handlePaste, true);
9246
7911
  document.addEventListener("input", handleInput, true);
9247
7912
  document.addEventListener("mouseover", handleMouseOver, true);
@@ -9256,7 +7921,6 @@ function OhhwellsBridge() {
9256
7921
  window.addEventListener("scroll", handleScroll, true);
9257
7922
  return () => {
9258
7923
  document.removeEventListener("click", handleClick, true);
9259
- document.removeEventListener("dblclick", handleDblClick, true);
9260
7924
  document.removeEventListener("paste", handlePaste, true);
9261
7925
  document.removeEventListener("input", handleInput, true);
9262
7926
  document.removeEventListener("mouseover", handleMouseOver, true);
@@ -9276,7 +7940,7 @@ function OhhwellsBridge() {
9276
7940
  window.removeEventListener("message", handleClearSchedulingWidget);
9277
7941
  window.removeEventListener("message", handleRemoveSchedulingSection);
9278
7942
  window.removeEventListener("message", handleImageUrl);
9279
- window.removeEventListener("message", handleImageUploading);
7943
+ window.removeEventListener("message", handleAnimLock);
9280
7944
  window.removeEventListener("message", handleCanvasHeight);
9281
7945
  window.removeEventListener("message", handleParentScroll);
9282
7946
  window.removeEventListener("resize", handleViewportResize);
@@ -9290,7 +7954,7 @@ function OhhwellsBridge() {
9290
7954
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9291
7955
  };
9292
7956
  }, [isEditMode, refreshStateRules]);
9293
- (0, import_react8.useEffect)(() => {
7957
+ (0, import_react6.useEffect)(() => {
9294
7958
  const handler = (e) => {
9295
7959
  if (e.data?.type !== "ow:request-schedule-config") return;
9296
7960
  const insertAfterVal = e.data.insertAfter;
@@ -9306,7 +7970,7 @@ function OhhwellsBridge() {
9306
7970
  window.addEventListener("message", handler);
9307
7971
  return () => window.removeEventListener("message", handler);
9308
7972
  }, [processConfigRequest]);
9309
- (0, import_react8.useEffect)(() => {
7973
+ (0, import_react6.useEffect)(() => {
9310
7974
  if (!isEditMode) return;
9311
7975
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9312
7976
  el.removeAttribute("data-ohw-active-state");
@@ -9327,27 +7991,27 @@ function OhhwellsBridge() {
9327
7991
  const next = { ...prev, [pathKey]: sections };
9328
7992
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9329
7993
  });
9330
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
9331
- postToParent2({ type: "ow:request-site-pages" });
7994
+ postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
7995
+ postToParent({ type: "ow:request-site-pages" });
9332
7996
  }, 150);
9333
7997
  return () => {
9334
7998
  cancelAnimationFrame(raf);
9335
7999
  clearTimeout(timer);
9336
8000
  };
9337
- }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9338
- (0, import_react8.useEffect)(() => {
8001
+ }, [pathname, isEditMode, refreshStateRules, postToParent]);
8002
+ (0, import_react6.useEffect)(() => {
9339
8003
  scrollToHashSectionWhenReady();
9340
8004
  const onHashChange = () => scrollToHashSectionWhenReady();
9341
8005
  window.addEventListener("hashchange", onHashChange);
9342
8006
  return () => window.removeEventListener("hashchange", onHashChange);
9343
8007
  }, [pathname]);
9344
- const handleCommand = (0, import_react8.useCallback)((cmd) => {
8008
+ const handleCommand = (0, import_react6.useCallback)((cmd) => {
9345
8009
  document.execCommand(cmd, false);
9346
8010
  activeElRef.current?.focus();
9347
8011
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
9348
8012
  refreshActiveCommandsRef.current();
9349
8013
  }, []);
9350
- const handleStateChange = (0, import_react8.useCallback)((state) => {
8014
+ const handleStateChange = (0, import_react6.useCallback)((state) => {
9351
8015
  if (!activeStateElRef.current) return;
9352
8016
  const el = activeStateElRef.current;
9353
8017
  if (state === "Default") {
@@ -9360,22 +8024,18 @@ function OhhwellsBridge() {
9360
8024
  }
9361
8025
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9362
8026
  }, [deactivate]);
9363
- const closeLinkPopover = (0, import_react8.useCallback)(() => {
9364
- addNavAfterAnchorRef.current = null;
9365
- setLinkPopover(null);
9366
- }, []);
9367
- const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
8027
+ const closeLinkPopover = (0, import_react6.useCallback)(() => setLinkPopover(null), []);
8028
+ const openLinkPopoverForActive = (0, import_react6.useCallback)(() => {
9368
8029
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9369
8030
  if (!hrefCtx) return;
9370
8031
  bumpLinkPopoverGrace();
9371
8032
  setLinkPopover({
9372
8033
  key: hrefCtx.key,
9373
- mode: "edit",
9374
- target: getLinkHref2(hrefCtx.anchor)
8034
+ target: getLinkHref(hrefCtx.anchor)
9375
8035
  });
9376
8036
  deactivate();
9377
8037
  }, [deactivate]);
9378
- const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
8038
+ const openLinkPopoverForSelected = (0, import_react6.useCallback)(() => {
9379
8039
  const anchor = selectedElRef.current;
9380
8040
  if (!anchor) return;
9381
8041
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9383,156 +8043,51 @@ function OhhwellsBridge() {
9383
8043
  bumpLinkPopoverGrace();
9384
8044
  setLinkPopover({
9385
8045
  key,
9386
- mode: "edit",
9387
- target: getLinkHref2(anchor)
8046
+ target: getLinkHref(anchor)
9388
8047
  });
9389
8048
  deselect();
9390
8049
  }, [deselect]);
9391
- const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
8050
+ const handleLinkPopoverSubmit = (0, import_react6.useCallback)(
9392
8051
  (target) => {
9393
- const session = linkPopoverSessionRef.current;
9394
- if (!session) return;
9395
- if (session.intent === "add-nav") {
9396
- const { pageRoute, sectionId } = parseTarget(target);
9397
- const page = resolvePage(pageRoute, sitePages);
9398
- const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9399
- const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9400
- const label = section?.label ?? page.title;
9401
- const afterAnchor = addNavAfterAnchorRef.current;
9402
- const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(target, label, afterAnchor);
9403
- applyLinkByKey(hrefKey, target);
9404
- document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9405
- el.textContent = label;
9406
- });
9407
- const navCount = String(index + 1);
9408
- const orderJson = JSON.stringify(order);
9409
- editContentRef.current = {
9410
- ...editContentRef.current,
9411
- [hrefKey]: target,
9412
- [labelKey]: label,
9413
- [NAV_COUNT_KEY]: navCount,
9414
- [NAV_ORDER_KEY]: orderJson
9415
- };
9416
- postToParent2({
9417
- type: "ow:change",
9418
- nodes: [
9419
- { key: hrefKey, text: target },
9420
- { key: labelKey, text: label },
9421
- { key: NAV_COUNT_KEY, text: navCount },
9422
- { key: NAV_ORDER_KEY, text: orderJson }
9423
- ]
9424
- });
9425
- addNavAfterAnchorRef.current = null;
9426
- setLinkPopover(null);
9427
- enforceLinkHrefs();
9428
- requestAnimationFrame(() => {
9429
- selectRef.current(anchor);
9430
- });
9431
- return;
9432
- }
9433
- const { key } = session;
8052
+ if (!linkPopover) return;
8053
+ const { key } = linkPopover;
9434
8054
  applyLinkByKey(key, target);
9435
- postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8055
+ postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
9436
8056
  setLinkPopover(null);
9437
8057
  },
9438
- [postToParent2, sitePages, sectionsByPath]
8058
+ [linkPopover, postToParent]
9439
8059
  );
9440
8060
  const showEditLink = toolbarShowEditLink;
9441
8061
  const currentSections = sectionsByPath[pathname] ?? [];
9442
8062
  linkPopoverOpenRef.current = linkPopover !== null;
9443
- const handleMediaReplace = (0, import_react8.useCallback)(
9444
- (key) => {
9445
- postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9446
- },
9447
- [postToParent2, mediaHover?.elementType]
9448
- );
9449
- const handleVideoSettingsChange = (0, import_react8.useCallback)(
9450
- (key, settings) => {
9451
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9452
- const video = getVideoEl(el);
9453
- if (!video) return;
9454
- if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
9455
- if (typeof settings.muted === "boolean") video.muted = settings.muted;
9456
- syncVideoPlayback(video);
9457
- postToParent2({
9458
- type: "ow:change",
9459
- nodes: [
9460
- { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
9461
- { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
9462
- ]
9463
- });
9464
- setMediaHover(
9465
- (prev) => prev && prev.key === key ? { ...prev, videoAutoplay: video.autoplay, videoMuted: video.muted } : prev
9466
- );
9467
- });
9468
- },
9469
- [postToParent2]
9470
- );
9471
- const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
9472
- setUploadingRects((prev) => {
9473
- if (!(key in prev)) return prev;
9474
- const next = { ...prev };
9475
- delete next[key];
9476
- return next;
9477
- });
9478
- }, []);
9479
- return bridgeRoot ? (0, import_react_dom3.createPortal)(
9480
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9481
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9482
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9483
- MediaOverlay,
9484
- {
9485
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
9486
- isUploading: true,
9487
- fadingOut,
9488
- onFadeOutComplete: handleMediaFadeOutComplete,
9489
- onReplace: handleMediaReplace
9490
- },
9491
- `uploading-${key}`
9492
- )),
9493
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9494
- MediaOverlay,
9495
- {
9496
- hover: mediaHover,
9497
- isUploading: false,
9498
- onReplace: handleMediaReplace,
9499
- onVideoSettingsChange: handleVideoSettingsChange
9500
- }
9501
- ),
9502
- siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9503
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9504
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9505
- NavbarContainerChrome,
9506
- {
9507
- rect: toolbarRect,
9508
- onAdd: handleAddTopLevelNavItem
9509
- }
9510
- ),
9511
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9512
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8063
+ return bridgeRoot ? (0, import_react_dom2.createPortal)(
8064
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
8065
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8066
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8067
+ hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8068
+ toolbarRect && toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
9513
8069
  ItemInteractionLayer,
9514
8070
  {
9515
8071
  rect: toolbarRect,
9516
8072
  elRef: glowElRef,
9517
8073
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9518
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
8074
+ showHandle: Boolean(reorderHrefKey),
9519
8075
  dragDisabled: reorderDragDisabled,
9520
8076
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9521
8077
  onDragHandleDragStart: handleItemDragStart,
9522
8078
  onDragHandleDragEnd: handleItemDragEnd,
9523
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8079
+ toolbar: isItemDragging ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
9524
8080
  ItemActionToolbar,
9525
8081
  {
9526
8082
  onEditLink: openLinkPopoverForSelected,
9527
- addItemDisabled: true,
9528
- editLinkDisabled: false,
9529
- moreDisabled: true
8083
+ onAddItem: handleAddNavigationItem,
8084
+ addItemDisabled: false
9530
8085
  }
9531
- ) : void 0
8086
+ )
9532
8087
  }
9533
8088
  ),
9534
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9535
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8089
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
8090
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
9536
8091
  EditGlowChrome,
9537
8092
  {
9538
8093
  rect: toolbarRect,
@@ -9541,7 +8096,7 @@ function OhhwellsBridge() {
9541
8096
  dragDisabled: reorderDragDisabled
9542
8097
  }
9543
8098
  ),
9544
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8099
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
9545
8100
  FloatingToolbar,
9546
8101
  {
9547
8102
  rect: toolbarRect,
@@ -9554,7 +8109,7 @@ function OhhwellsBridge() {
9554
8109
  }
9555
8110
  )
9556
8111
  ] }),
9557
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8112
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
9558
8113
  "div",
9559
8114
  {
9560
8115
  "data-ohw-max-badge": "",
@@ -9580,7 +8135,7 @@ function OhhwellsBridge() {
9580
8135
  ]
9581
8136
  }
9582
8137
  ),
9583
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8138
+ toggleState && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
9584
8139
  StateToggle,
9585
8140
  {
9586
8141
  rect: toggleState.rect,
@@ -9589,15 +8144,15 @@ function OhhwellsBridge() {
9589
8144
  onStateChange: handleStateChange
9590
8145
  }
9591
8146
  ),
9592
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8147
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
9593
8148
  "div",
9594
8149
  {
9595
8150
  "data-ohw-section-insert-line": "",
9596
8151
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9597
8152
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
9598
8153
  children: [
9599
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9600
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8154
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8155
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
9601
8156
  Badge,
9602
8157
  {
9603
8158
  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",
@@ -9610,52 +8165,26 @@ function OhhwellsBridge() {
9610
8165
  children: "Add Section"
9611
8166
  }
9612
8167
  ),
9613
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8168
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9614
8169
  ]
9615
8170
  }
9616
8171
  ),
9617
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8172
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
9618
8173
  LinkPopover,
9619
8174
  {
9620
8175
  panelRef: linkPopoverPanelRef,
9621
8176
  portalContainer: dialogPortalContainer,
9622
8177
  open: true,
9623
- mode: linkPopover.mode ?? "edit",
8178
+ mode: "edit",
9624
8179
  pages: sitePages,
9625
8180
  sections: currentSections,
9626
8181
  sectionsByPath,
9627
8182
  initialTarget: linkPopover.target,
9628
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
9629
8183
  onClose: closeLinkPopover,
9630
8184
  onSubmit: handleLinkPopoverSubmit
9631
8185
  },
9632
- linkPopover.key
9633
- ) : null,
9634
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
9635
- "div",
9636
- {
9637
- "data-ohw-section-insert-line": "",
9638
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
9639
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
9640
- children: [
9641
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9642
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9643
- Badge,
9644
- {
9645
- 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",
9646
- onClick: () => {
9647
- window.parent.postMessage(
9648
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
9649
- "*"
9650
- );
9651
- },
9652
- children: "Add Section"
9653
- }
9654
- ),
9655
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9656
- ]
9657
- }
9658
- )
8186
+ `${linkPopover.key}-${pathname}`
8187
+ ) : null
9659
8188
  ] }),
9660
8189
  bridgeRoot
9661
8190
  ) : null;