@ohhwells/bridge 0.1.46 → 0.1.48-next.113

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
@@ -57,12 +57,13 @@ __export(index_exports, {
57
57
  isValidUrl: () => isValidUrl,
58
58
  parseTarget: () => parseTarget,
59
59
  toggleVariants: () => toggleVariants,
60
+ useOhwCarousel: () => useOhwCarousel,
60
61
  validateUrlInput: () => validateUrlInput
61
62
  });
62
63
  module.exports = __toCommonJS(index_exports);
63
64
 
64
65
  // src/OhhwellsBridge.tsx
65
- var import_react9 = __toESM(require("react"), 1);
66
+ var import_react10 = __toESM(require("react"), 1);
66
67
  var import_client = require("react-dom/client");
67
68
  var import_react_dom2 = require("react-dom");
68
69
 
@@ -115,6 +116,7 @@ var import_react3 = require("react");
115
116
  var import_react2 = require("react");
116
117
  var import_radix_ui = require("radix-ui");
117
118
  var import_jsx_runtime = require("react/jsx-runtime");
119
+ var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
118
120
  function Spinner() {
119
121
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
120
122
  "svg",
@@ -148,12 +150,17 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
148
150
  const [error, setError] = (0, import_react2.useState)(null);
149
151
  const isBook = title === "Confirm your spot";
150
152
  const handleSubmit = async () => {
151
- if (!email.trim()) return;
153
+ const trimmed = email.trim();
154
+ if (!trimmed) return;
155
+ if (!isValidEmail(trimmed)) {
156
+ setError("Please enter a valid email address.");
157
+ return;
158
+ }
152
159
  setLoading(true);
153
160
  setError(null);
154
161
  try {
155
- await onSubmit(email.trim());
156
- setSubmittedEmail(email.trim());
162
+ await onSubmit(trimmed);
163
+ setSubmittedEmail(trimmed);
157
164
  setSuccess(true);
158
165
  } catch (e) {
159
166
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -228,7 +235,10 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
228
235
  {
229
236
  type: "email",
230
237
  value: email,
231
- onChange: (e) => setEmail(e.target.value),
238
+ onChange: (e) => {
239
+ setEmail(e.target.value);
240
+ if (error) setError(null);
241
+ },
232
242
  onKeyDown: handleKeyDown,
233
243
  placeholder: "hello@gmail.com",
234
244
  autoFocus: true,
@@ -285,12 +295,20 @@ function formatClassTime(cls) {
285
295
  const em = endTotal % 60;
286
296
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
287
297
  }
298
+ function formatBookingLabel(cls) {
299
+ const price = cls.price;
300
+ const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
301
+ return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
302
+ }
288
303
  function getBookingsOnDate(cls, date) {
289
304
  if (!cls.bookings?.length) return 0;
290
- const ds = date.toISOString().split("T")[0];
305
+ const target = new Date(date);
306
+ target.setHours(0, 0, 0, 0);
291
307
  return cls.bookings.filter((b) => {
292
308
  try {
293
- return new Date(b.classDate).toISOString().split("T")[0] === ds;
309
+ const bookingDate = new Date(b.classDate);
310
+ bookingDate.setHours(0, 0, 0, 0);
311
+ return bookingDate.getTime() === target.getTime();
294
312
  } catch {
295
313
  return false;
296
314
  }
@@ -555,7 +573,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
555
573
  }
556
574
  )
557
575
  ] }),
558
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
576
+ cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
559
577
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
560
578
  available,
561
579
  "/",
@@ -567,9 +585,17 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
567
585
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
568
586
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
569
587
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
570
- cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
588
+ cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
589
+ cls.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
590
+ "span",
591
+ {
592
+ className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block",
593
+ style: { wordBreak: "break-word" },
594
+ children: cls.description
595
+ }
596
+ )
571
597
  ] }),
572
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
598
+ cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
573
599
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
574
600
  available,
575
601
  "/",
@@ -594,7 +620,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
594
620
  if (!cls.id) return;
595
621
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
596
622
  },
597
- children: isFull ? "Join Waitlist" : "Book Now"
623
+ children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
598
624
  }
599
625
  )
600
626
  ] })
@@ -636,6 +662,17 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
636
662
  const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
637
663
  const [modalState, setModalState] = (0, import_react3.useState)(null);
638
664
  const switchScheduleIdRef = (0, import_react3.useRef)(null);
665
+ const liveScheduleIdRef = (0, import_react3.useRef)(null);
666
+ const refetchLiveSchedule = (0, import_react3.useCallback)(async () => {
667
+ const id = liveScheduleIdRef.current;
668
+ if (!id) return;
669
+ try {
670
+ const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
671
+ const data = await res.json();
672
+ if (data?.id) setSchedule(data);
673
+ } catch {
674
+ }
675
+ }, []);
639
676
  const dates = (0, import_react3.useMemo)(() => {
640
677
  const today = /* @__PURE__ */ new Date();
641
678
  today.setHours(0, 0, 0, 0);
@@ -655,6 +692,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
655
692
  }
656
693
  }
657
694
  }, [schedule, dates]);
695
+ (0, import_react3.useEffect)(() => {
696
+ if (typeof window === "undefined") return;
697
+ const onVisible = () => {
698
+ if (!document.hidden) void refetchLiveSchedule();
699
+ };
700
+ window.addEventListener("focus", refetchLiveSchedule);
701
+ document.addEventListener("visibilitychange", onVisible);
702
+ return () => {
703
+ window.removeEventListener("focus", refetchLiveSchedule);
704
+ document.removeEventListener("visibilitychange", onVisible);
705
+ };
706
+ }, [refetchLiveSchedule]);
658
707
  (0, import_react3.useEffect)(() => {
659
708
  if (typeof window === "undefined") return;
660
709
  const isInEditor = window.self !== window.top;
@@ -716,7 +765,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
716
765
  setLoading(false);
717
766
  return;
718
767
  }
719
- ;
768
+ liveScheduleIdRef.current = effectiveId;
720
769
  (async () => {
721
770
  try {
722
771
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -773,18 +822,14 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
773
822
  const data = await res.json().catch(() => ({}));
774
823
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
775
824
  }
825
+ await refetchLiveSchedule();
776
826
  };
777
827
  const handleReplaceSchedule = () => {
778
828
  setIsHovered(false);
779
- if (schedule) {
780
- window.parent.postMessage({
781
- type: "ow:schedule-connected",
782
- schedule: { id: schedule.id, name: schedule.name },
783
- insertAfter
784
- }, "*");
785
- } else {
786
- window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
787
- }
829
+ window.parent.postMessage(
830
+ { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
831
+ "*"
832
+ );
788
833
  };
789
834
  if (!inEditor && !loading && !schedule) return null;
790
835
  const sectionId = `scheduling-${insertAfter}`;
@@ -827,7 +872,19 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
827
872
  ),
828
873
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
829
874
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
830
- /* @__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" }),
875
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
876
+ "h2",
877
+ {
878
+ className: "font-display text-center m-0 text-(--color-dark,#200C02)",
879
+ style: {
880
+ fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
881
+ fontWeight: "var(--font-weight-heading, 400)",
882
+ lineHeight: 1.05,
883
+ letterSpacing: "-0.02em"
884
+ },
885
+ children: schedule?.name ?? "Book an appointment"
886
+ }
887
+ ),
831
888
  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 })
832
889
  ] }),
833
890
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4887,7 +4944,11 @@ function MediaOverlay({
4887
4944
  variant: "outline",
4888
4945
  size: "sm",
4889
4946
  className: "gap-1.5 cursor-pointer hover:bg-background",
4890
- style: OVERLAY_BUTTON_STYLE,
4947
+ style: {
4948
+ ...OVERLAY_BUTTON_STYLE,
4949
+ // Don't let Replace steal clicks when text (e.g. nav) sits on top of this media.
4950
+ pointerEvents: hover.hasTextOverlap ? "none" : "auto"
4951
+ },
4891
4952
  onMouseDown: (e) => e.preventDefault(),
4892
4953
  onClick: (e) => {
4893
4954
  e.stopPropagation();
@@ -4904,6 +4965,61 @@ function MediaOverlay({
4904
4965
  ] });
4905
4966
  }
4906
4967
 
4968
+ // src/ui/CarouselOverlay.tsx
4969
+ var import_lucide_react4 = require("lucide-react");
4970
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4971
+ var OVERLAY_BUTTON_STYLE2 = {
4972
+ pointerEvents: "auto",
4973
+ fontFamily: "Inter, sans-serif",
4974
+ fontSize: 12,
4975
+ color: "#000"
4976
+ };
4977
+ function CarouselOverlay({
4978
+ hover,
4979
+ onEdit
4980
+ }) {
4981
+ const { rect } = hover;
4982
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4983
+ "div",
4984
+ {
4985
+ "data-ohw-bridge": "",
4986
+ "data-ohw-carousel-overlay": "",
4987
+ className: "flex cursor-pointer items-center justify-center",
4988
+ style: {
4989
+ position: "fixed",
4990
+ top: rect.top,
4991
+ left: rect.left,
4992
+ width: rect.width,
4993
+ height: rect.height,
4994
+ zIndex: 2147483646,
4995
+ pointerEvents: "auto",
4996
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4997
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4998
+ },
4999
+ onClick: () => onEdit(hover.key),
5000
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5001
+ Button,
5002
+ {
5003
+ "data-ohw-carousel-overlay": "",
5004
+ variant: "outline",
5005
+ size: "sm",
5006
+ className: "cursor-pointer gap-1.5 hover:bg-background",
5007
+ style: OVERLAY_BUTTON_STYLE2,
5008
+ onMouseDown: (e) => e.preventDefault(),
5009
+ onClick: (e) => {
5010
+ e.stopPropagation();
5011
+ onEdit(hover.key);
5012
+ },
5013
+ children: [
5014
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.GalleryHorizontal, { size: 14 }),
5015
+ "Edit gallery"
5016
+ ]
5017
+ }
5018
+ )
5019
+ }
5020
+ );
5021
+ }
5022
+
4907
5023
  // src/OhhwellsBridge.tsx
4908
5024
  var import_react_dom3 = require("react-dom");
4909
5025
  var import_navigation2 = require("next/navigation");
@@ -5189,23 +5305,23 @@ var import_react7 = require("react");
5189
5305
  // src/ui/dialog.tsx
5190
5306
  var React8 = __toESM(require("react"), 1);
5191
5307
  var import_radix_ui5 = require("radix-ui");
5192
- var import_lucide_react4 = require("lucide-react");
5193
- var import_jsx_runtime12 = require("react/jsx-runtime");
5308
+ var import_lucide_react5 = require("lucide-react");
5309
+ var import_jsx_runtime13 = require("react/jsx-runtime");
5194
5310
  function Dialog2({
5195
5311
  ...props
5196
5312
  }) {
5197
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5313
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5198
5314
  }
5199
5315
  function DialogPortal({
5200
5316
  ...props
5201
5317
  }) {
5202
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5318
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5203
5319
  }
5204
5320
  function DialogOverlay({
5205
5321
  className,
5206
5322
  ...props
5207
5323
  }) {
5208
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5324
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5209
5325
  import_radix_ui5.Dialog.Overlay,
5210
5326
  {
5211
5327
  "data-slot": "dialog-overlay",
@@ -5218,9 +5334,9 @@ function DialogOverlay({
5218
5334
  var DialogContent = React8.forwardRef(
5219
5335
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5220
5336
  const positionMode = container ? "absolute" : "fixed";
5221
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5222
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5223
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5337
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5338
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5339
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
5224
5340
  import_radix_ui5.Dialog.Content,
5225
5341
  {
5226
5342
  ref,
@@ -5236,13 +5352,13 @@ var DialogContent = React8.forwardRef(
5236
5352
  ...props,
5237
5353
  children: [
5238
5354
  children,
5239
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5355
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5240
5356
  import_radix_ui5.Dialog.Close,
5241
5357
  {
5242
5358
  type: "button",
5243
5359
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5244
5360
  "aria-label": "Close",
5245
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.X, { size: 16, "aria-hidden": true })
5361
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.X, { size: 16, "aria-hidden": true })
5246
5362
  }
5247
5363
  ) : null
5248
5364
  ]
@@ -5256,13 +5372,13 @@ function DialogHeader({
5256
5372
  className,
5257
5373
  ...props
5258
5374
  }) {
5259
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5375
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5260
5376
  }
5261
5377
  function DialogFooter({
5262
5378
  className,
5263
5379
  ...props
5264
5380
  }) {
5265
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5381
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5266
5382
  "div",
5267
5383
  {
5268
5384
  className: cn("flex items-center justify-end gap-2", className),
@@ -5270,7 +5386,7 @@ function DialogFooter({
5270
5386
  }
5271
5387
  );
5272
5388
  }
5273
- var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5389
+ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5274
5390
  import_radix_ui5.Dialog.Title,
5275
5391
  {
5276
5392
  ref,
@@ -5282,7 +5398,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
5282
5398
  }
5283
5399
  ));
5284
5400
  DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5285
- var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5401
+ var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5286
5402
  import_radix_ui5.Dialog.Description,
5287
5403
  {
5288
5404
  ref,
@@ -5294,63 +5410,63 @@ DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
5294
5410
  var DialogClose = import_radix_ui5.Dialog.Close;
5295
5411
 
5296
5412
  // src/ui/link-modal/LinkEditorPanel.tsx
5297
- var import_lucide_react8 = require("lucide-react");
5413
+ var import_lucide_react9 = require("lucide-react");
5298
5414
 
5299
5415
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5300
- var import_lucide_react5 = require("lucide-react");
5301
- var import_jsx_runtime13 = require("react/jsx-runtime");
5416
+ var import_lucide_react6 = require("lucide-react");
5417
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5302
5418
  function DestinationBreadcrumb({
5303
5419
  pageTitle,
5304
5420
  sectionLabel
5305
5421
  }) {
5306
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5307
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5308
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
5309
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
5310
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5311
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5422
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5423
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5424
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-3", children: [
5425
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
5426
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5427
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5312
5428
  ] }),
5313
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5314
- import_lucide_react5.ArrowRight,
5429
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5430
+ import_lucide_react6.ArrowRight,
5315
5431
  {
5316
5432
  size: 16,
5317
5433
  className: "shrink-0 text-muted-foreground",
5318
5434
  "aria-hidden": true
5319
5435
  }
5320
5436
  ),
5321
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5322
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5323
- import_lucide_react5.GalleryVertical,
5437
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5438
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5439
+ import_lucide_react6.GalleryVertical,
5324
5440
  {
5325
5441
  size: 16,
5326
5442
  className: "shrink-0 text-foreground",
5327
5443
  "aria-hidden": true
5328
5444
  }
5329
5445
  ),
5330
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5446
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5331
5447
  ] })
5332
5448
  ] })
5333
5449
  ] });
5334
5450
  }
5335
5451
 
5336
5452
  // src/ui/link-modal/SectionTreeItem.tsx
5337
- var import_lucide_react6 = require("lucide-react");
5338
- var import_jsx_runtime14 = require("react/jsx-runtime");
5453
+ var import_lucide_react7 = require("lucide-react");
5454
+ var import_jsx_runtime15 = require("react/jsx-runtime");
5339
5455
  function SectionTreeItem({
5340
5456
  section,
5341
5457
  onSelect,
5342
5458
  selected
5343
5459
  }) {
5344
5460
  const interactive = Boolean(onSelect);
5345
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5346
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5461
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5462
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5347
5463
  "div",
5348
5464
  {
5349
5465
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5350
5466
  "aria-hidden": true
5351
5467
  }
5352
5468
  ),
5353
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5469
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5354
5470
  "div",
5355
5471
  {
5356
5472
  role: interactive ? "button" : void 0,
@@ -5368,15 +5484,15 @@ function SectionTreeItem({
5368
5484
  interactive && selected && "border-primary"
5369
5485
  ),
5370
5486
  children: [
5371
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5372
- import_lucide_react6.GalleryVertical,
5487
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5488
+ import_lucide_react7.GalleryVertical,
5373
5489
  {
5374
5490
  size: 16,
5375
5491
  className: "shrink-0 text-foreground",
5376
5492
  "aria-hidden": true
5377
5493
  }
5378
5494
  ),
5379
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5495
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5380
5496
  ]
5381
5497
  }
5382
5498
  )
@@ -5388,10 +5504,10 @@ var import_react4 = require("react");
5388
5504
 
5389
5505
  // src/ui/input.tsx
5390
5506
  var React9 = __toESM(require("react"), 1);
5391
- var import_jsx_runtime15 = require("react/jsx-runtime");
5507
+ var import_jsx_runtime16 = require("react/jsx-runtime");
5392
5508
  var Input = React9.forwardRef(
5393
5509
  ({ className, type, ...props }, ref) => {
5394
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5510
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5395
5511
  "input",
5396
5512
  {
5397
5513
  type,
@@ -5410,9 +5526,9 @@ Input.displayName = "Input";
5410
5526
 
5411
5527
  // src/ui/label.tsx
5412
5528
  var import_radix_ui6 = require("radix-ui");
5413
- var import_jsx_runtime16 = require("react/jsx-runtime");
5529
+ var import_jsx_runtime17 = require("react/jsx-runtime");
5414
5530
  function Label({ className, ...props }) {
5415
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5531
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5416
5532
  import_radix_ui6.Label.Root,
5417
5533
  {
5418
5534
  "data-slot": "label",
@@ -5423,12 +5539,12 @@ function Label({ className, ...props }) {
5423
5539
  }
5424
5540
 
5425
5541
  // src/ui/link-modal/UrlOrPageInput.tsx
5426
- var import_lucide_react7 = require("lucide-react");
5427
- var import_jsx_runtime17 = require("react/jsx-runtime");
5542
+ var import_lucide_react8 = require("lucide-react");
5543
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5428
5544
  function FieldChevron({
5429
5545
  onClick
5430
5546
  }) {
5431
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5547
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5432
5548
  "button",
5433
5549
  {
5434
5550
  type: "button",
@@ -5436,7 +5552,7 @@ function FieldChevron({
5436
5552
  onClick,
5437
5553
  "aria-label": "Open page list",
5438
5554
  tabIndex: -1,
5439
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.ChevronDown, { size: 16 })
5555
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.ChevronDown, { size: 16 })
5440
5556
  }
5441
5557
  );
5442
5558
  }
@@ -5497,19 +5613,19 @@ function UrlOrPageInput({
5497
5613
  "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5498
5614
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5499
5615
  );
5500
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5501
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5502
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
5503
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5504
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5505
- import_lucide_react7.File,
5616
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5617
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5618
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
5619
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5620
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5621
+ import_lucide_react8.File,
5506
5622
  {
5507
5623
  size: 16,
5508
5624
  className: "shrink-0 text-foreground",
5509
5625
  "aria-hidden": true
5510
5626
  }
5511
5627
  ) }) : null,
5512
- 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)(
5628
+ readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5513
5629
  Input,
5514
5630
  {
5515
5631
  ref: inputRef,
@@ -5535,7 +5651,7 @@ function UrlOrPageInput({
5535
5651
  )
5536
5652
  }
5537
5653
  ),
5538
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5654
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5539
5655
  "button",
5540
5656
  {
5541
5657
  type: "button",
@@ -5543,26 +5659,26 @@ function UrlOrPageInput({
5543
5659
  onMouseDown: clearSelection,
5544
5660
  "aria-label": "Clear selected page",
5545
5661
  tabIndex: -1,
5546
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, "aria-hidden": true })
5662
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5547
5663
  }
5548
5664
  ) : null,
5549
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5665
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5550
5666
  ] }),
5551
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5667
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5552
5668
  "div",
5553
5669
  {
5554
5670
  "data-ohw-link-page-dropdown": "",
5555
5671
  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",
5556
5672
  onMouseDown: (e) => e.preventDefault(),
5557
- children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5673
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5558
5674
  "button",
5559
5675
  {
5560
5676
  type: "button",
5561
5677
  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",
5562
5678
  onClick: () => onPageSelect(page),
5563
5679
  children: [
5564
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5565
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: page.title })
5680
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5681
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate", children: page.title })
5566
5682
  ]
5567
5683
  },
5568
5684
  page.path
@@ -5570,34 +5686,34 @@ function UrlOrPageInput({
5570
5686
  }
5571
5687
  ) : null
5572
5688
  ] }),
5573
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5689
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5574
5690
  ] });
5575
5691
  }
5576
5692
 
5577
5693
  // src/ui/link-modal/LinkEditorPanel.tsx
5578
- var import_jsx_runtime18 = require("react/jsx-runtime");
5694
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5579
5695
  function LinkEditorPanel({ state, onClose }) {
5580
5696
  const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5581
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5582
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5697
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5698
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5583
5699
  "button",
5584
5700
  {
5585
5701
  type: "button",
5586
5702
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5587
5703
  "aria-label": "Close",
5588
5704
  onClick: onClose,
5589
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5705
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.X, { size: 16, "aria-hidden": true })
5590
5706
  }
5591
5707
  ) }),
5592
- /* @__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 }) }),
5593
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5594
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5708
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5709
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5710
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5595
5711
  DestinationBreadcrumb,
5596
5712
  {
5597
5713
  pageTitle: state.selectedPage.title,
5598
5714
  sectionLabel: state.selectedSection.label
5599
5715
  }
5600
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5716
+ ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5601
5717
  UrlOrPageInput,
5602
5718
  {
5603
5719
  value: state.searchValue,
@@ -5610,8 +5726,8 @@ function LinkEditorPanel({ state, onClose }) {
5610
5726
  urlError: state.urlError
5611
5727
  }
5612
5728
  ),
5613
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5614
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5729
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5730
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5615
5731
  Button,
5616
5732
  {
5617
5733
  type: "button",
@@ -5622,15 +5738,15 @@ function LinkEditorPanel({ state, onClose }) {
5622
5738
  children: "Choose a section"
5623
5739
  }
5624
5740
  ),
5625
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5626
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5627
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
5741
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5742
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5743
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Pick a section this link should scroll to." })
5628
5744
  ] })
5629
5745
  ] }) : null,
5630
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5746
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5631
5747
  ] }),
5632
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5633
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5748
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5749
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5634
5750
  Button,
5635
5751
  {
5636
5752
  type: "button",
@@ -5645,7 +5761,7 @@ function LinkEditorPanel({ state, onClose }) {
5645
5761
  children: state.secondaryLabel
5646
5762
  }
5647
5763
  ),
5648
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5764
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5649
5765
  Button,
5650
5766
  {
5651
5767
  type: "button",
@@ -5666,9 +5782,9 @@ function LinkEditorPanel({ state, onClose }) {
5666
5782
  // src/ui/link-modal/SectionPickerOverlay.tsx
5667
5783
  var import_react5 = require("react");
5668
5784
  var import_react_dom = require("react-dom");
5669
- var import_lucide_react9 = require("lucide-react");
5785
+ var import_lucide_react10 = require("lucide-react");
5670
5786
  var import_navigation = require("next/navigation");
5671
- var import_jsx_runtime19 = require("react/jsx-runtime");
5787
+ var import_jsx_runtime20 = require("react/jsx-runtime");
5672
5788
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5673
5789
  function rectsEqual(a, b) {
5674
5790
  if (a.size !== b.size) return false;
@@ -5897,7 +6013,7 @@ function SectionPickerOverlay({
5897
6013
  const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5898
6014
  if (!portalRoot) return null;
5899
6015
  return (0, import_react_dom.createPortal)(
5900
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
6016
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5901
6017
  "div",
5902
6018
  {
5903
6019
  "data-ohw-section-picker": "",
@@ -5907,12 +6023,12 @@ function SectionPickerOverlay({
5907
6023
  role: "dialog",
5908
6024
  "aria-label": "Choose a section",
5909
6025
  children: [
5910
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6026
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5911
6027
  "div",
5912
6028
  {
5913
6029
  className: "pointer-events-auto fixed left-5 z-[2]",
5914
6030
  style: { top: chromeClip.top + 20 },
5915
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
6031
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5916
6032
  Button,
5917
6033
  {
5918
6034
  type: "button",
@@ -5921,14 +6037,14 @@ function SectionPickerOverlay({
5921
6037
  className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5922
6038
  onClick: onBack,
5923
6039
  children: [
5924
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
6040
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5925
6041
  "Back"
5926
6042
  ]
5927
6043
  }
5928
6044
  )
5929
6045
  }
5930
6046
  ),
5931
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6047
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5932
6048
  "div",
5933
6049
  {
5934
6050
  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",
@@ -5941,7 +6057,7 @@ function SectionPickerOverlay({
5941
6057
  children: "Click on section to select"
5942
6058
  }
5943
6059
  ),
5944
- !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6060
+ !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5945
6061
  "div",
5946
6062
  {
5947
6063
  className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md px-3 py-2 text-sm text-muted-foreground shadow-sm",
@@ -5949,14 +6065,14 @@ function SectionPickerOverlay({
5949
6065
  children: "Loading page preview\u2026"
5950
6066
  }
5951
6067
  ) : null,
5952
- 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,
6068
+ isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5953
6069
  isOnTargetPage ? liveSections.map((section) => {
5954
6070
  const rect = rects.get(section.id);
5955
6071
  if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5956
6072
  const isSelected = selectedId === section.id;
5957
6073
  const isHovered = hoveredId === section.id;
5958
6074
  const isLit = isSelected || isHovered;
5959
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
6075
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5960
6076
  "button",
5961
6077
  {
5962
6078
  type: "button",
@@ -5971,7 +6087,7 @@ function SectionPickerOverlay({
5971
6087
  "aria-label": `Select section ${section.label}`,
5972
6088
  onClick: () => handleSelect(section),
5973
6089
  children: [
5974
- isLit ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6090
+ isLit ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5975
6091
  "span",
5976
6092
  {
5977
6093
  className: "pointer-events-none absolute",
@@ -5984,13 +6100,13 @@ function SectionPickerOverlay({
5984
6100
  "aria-hidden": true
5985
6101
  }
5986
6102
  ) : null,
5987
- isSelected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6103
+ isSelected ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5988
6104
  "span",
5989
6105
  {
5990
6106
  className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5991
6107
  style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5992
6108
  "aria-hidden": true,
5993
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Check, { className: "size-5" })
6109
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.Check, { className: "size-5" })
5994
6110
  }
5995
6111
  ) : null
5996
6112
  ]
@@ -6170,7 +6286,7 @@ function useLinkModalState({
6170
6286
  }
6171
6287
 
6172
6288
  // src/ui/link-modal/LinkPopover.tsx
6173
- var import_jsx_runtime20 = require("react/jsx-runtime");
6289
+ var import_jsx_runtime21 = require("react/jsx-runtime");
6174
6290
  function postToParent(data) {
6175
6291
  window.parent?.postMessage(data, "*");
6176
6292
  }
@@ -6266,15 +6382,15 @@ function LinkPopover({
6266
6382
  );
6267
6383
  };
6268
6384
  }, [open, sectionPickerActive]);
6269
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
6270
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6385
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6386
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6271
6387
  Dialog2,
6272
6388
  {
6273
6389
  open: open && !sectionPickerActive,
6274
6390
  onOpenChange: (next) => {
6275
6391
  if (!next) onClose?.();
6276
6392
  },
6277
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6393
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6278
6394
  DialogContent,
6279
6395
  {
6280
6396
  ref: panelRef,
@@ -6284,12 +6400,12 @@ function LinkPopover({
6284
6400
  "data-ohw-bridge": "",
6285
6401
  showCloseButton: false,
6286
6402
  className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6287
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { state, onClose })
6403
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LinkEditorPanel, { state, onClose })
6288
6404
  }
6289
6405
  )
6290
6406
  }
6291
6407
  ),
6292
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6408
+ sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6293
6409
  SectionPickerOverlay,
6294
6410
  {
6295
6411
  pagePath: state.selectedPage.path,
@@ -6661,6 +6777,8 @@ function applyNavOrderToContainer(container, order) {
6661
6777
  if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6662
6778
  return;
6663
6779
  }
6780
+ const lastCurrentLinkEl = current.length > 0 ? findCounterpartByHrefKey(current[current.length - 1], container) : null;
6781
+ const anchor = lastCurrentLinkEl?.parentElement === container ? lastCurrentLinkEl.nextElementSibling : null;
6664
6782
  const seen = /* @__PURE__ */ new Set();
6665
6783
  for (const hrefKey of desired) {
6666
6784
  if (seen.has(hrefKey)) continue;
@@ -6675,7 +6793,9 @@ function applyNavOrderToContainer(container, order) {
6675
6793
  orderedEls.push(el);
6676
6794
  }
6677
6795
  for (const el of orderedEls) {
6678
- if (container.lastElementChild !== el) {
6796
+ if (anchor) {
6797
+ if (el.nextSibling !== anchor) container.insertBefore(el, anchor);
6798
+ } else if (container.lastElementChild !== el) {
6679
6799
  container.appendChild(el);
6680
6800
  }
6681
6801
  }
@@ -7007,7 +7127,195 @@ function parseFooterOrder(content) {
7007
7127
  return null;
7008
7128
  }
7009
7129
  }
7130
+ var FOOTER_HEADING_RE = /^footer-(\d+)-heading$/;
7131
+ var FOOTER_LABEL_RE = /^footer-(\d+)-(\d+)-label$/;
7132
+ var DEFAULT_FOOTER_COLUMN_HEADING = "New";
7133
+ var DEFAULT_FOOTER_COLUMN_LINK_LABEL = "New link";
7134
+ var DEFAULT_FOOTER_COLUMN_LINK_HREF = "";
7135
+ function isFooterLinksContainer(el) {
7136
+ return el.hasAttribute("data-ohw-footer-links") || el.classList.contains("rb-footer-links");
7137
+ }
7138
+ function getFooterColumnIndicesInDom() {
7139
+ const indices = /* @__PURE__ */ new Set();
7140
+ for (const col of listFooterColumns()) {
7141
+ const attr = col.getAttribute("data-ohw-footer-col");
7142
+ if (attr != null) {
7143
+ const n = parseInt(attr, 10);
7144
+ if (Number.isFinite(n)) indices.add(n);
7145
+ }
7146
+ for (const link of listFooterLinksInColumn(col)) {
7147
+ const parsed = parseFooterHrefKey(link.getAttribute("data-ohw-href-key"));
7148
+ if (parsed) indices.add(parsed.col);
7149
+ }
7150
+ const heading = col.querySelector('[data-ohw-key^="footer-"][data-ohw-key$="-heading"]');
7151
+ const headingKey = heading?.getAttribute("data-ohw-key");
7152
+ if (headingKey) {
7153
+ const match = headingKey.match(FOOTER_HEADING_RE);
7154
+ if (match) indices.add(parseInt(match[1], 10));
7155
+ }
7156
+ }
7157
+ return [...indices].sort((a, b) => a - b);
7158
+ }
7159
+ function getNextFooterColumnIndex() {
7160
+ const indices = getFooterColumnIndicesInDom();
7161
+ if (indices.length === 0) return 0;
7162
+ return Math.max(...indices) + 1;
7163
+ }
7164
+ function cloneFooterLinkShell(template) {
7165
+ const anchor = document.createElement("a");
7166
+ if (template) {
7167
+ anchor.style.cssText = template.style.cssText;
7168
+ if (template.className) anchor.className = template.className;
7169
+ }
7170
+ anchor.style.cursor = "pointer";
7171
+ anchor.style.textDecoration = "none";
7172
+ return anchor;
7173
+ }
7174
+ function buildFooterHeading(colIndex, text) {
7175
+ const heading = document.createElement("p");
7176
+ heading.setAttribute("data-ohw-editable", "text");
7177
+ heading.setAttribute("data-ohw-key", `footer-${colIndex}-heading`);
7178
+ heading.textContent = text;
7179
+ heading.style.cssText = [
7180
+ "color: var(--espresso, #3d312b)",
7181
+ "font-size: 14px",
7182
+ "font-weight: 800",
7183
+ "opacity: 0.82",
7184
+ "margin: 0 0 6px",
7185
+ "padding: 0",
7186
+ "line-height: 1.2"
7187
+ ].join(";");
7188
+ return heading;
7189
+ }
7190
+ function buildFooterLink(colIndex, itemIndex, href, label, template) {
7191
+ const anchor = cloneFooterLinkShell(template);
7192
+ anchor.setAttribute("href", href);
7193
+ anchor.setAttribute("data-ohw-href-key", `footer-${colIndex}-${itemIndex}-href`);
7194
+ const span = document.createElement("span");
7195
+ span.setAttribute("data-ohw-editable", "text");
7196
+ span.setAttribute("data-ohw-key", `footer-${colIndex}-${itemIndex}-label`);
7197
+ span.textContent = label;
7198
+ anchor.appendChild(span);
7199
+ return anchor;
7200
+ }
7201
+ function footerColumnExistsInDom(colIndex) {
7202
+ return document.querySelector(`[data-ohw-footer-col="${colIndex}"]`) !== null || document.querySelector(`[data-ohw-href-key^="footer-${colIndex}-"]`) !== null || document.querySelector(`[data-ohw-key="footer-${colIndex}-heading"]`) !== null;
7203
+ }
7204
+ function getFooterLinkTemplate() {
7205
+ for (const col of listFooterColumns()) {
7206
+ const link = listFooterLinksInColumn(col)[0];
7207
+ if (link) return link;
7208
+ }
7209
+ return null;
7210
+ }
7211
+ function getFooterColumnTemplate() {
7212
+ return listFooterColumns()[0] ?? null;
7213
+ }
7214
+ function insertFooterColumnDom(colIndex, heading, items) {
7215
+ const container = getFooterLinksContainer();
7216
+ if (!container) return null;
7217
+ const colTemplate = getFooterColumnTemplate();
7218
+ const linkTemplate = getFooterLinkTemplate();
7219
+ const column = document.createElement("div");
7220
+ if (colTemplate?.className) column.className = colTemplate.className;
7221
+ else column.className = "rb-footer-link-col";
7222
+ column.setAttribute("data-ohw-footer-col", String(colIndex));
7223
+ if (colTemplate) {
7224
+ const gap = getComputedStyle(colTemplate).gap;
7225
+ if (gap && gap !== "normal") column.style.gap = gap;
7226
+ column.style.display = getComputedStyle(colTemplate).display || "flex";
7227
+ column.style.flexDirection = "column";
7228
+ }
7229
+ column.appendChild(buildFooterHeading(colIndex, heading));
7230
+ let firstLink = null;
7231
+ items.forEach((item, itemIndex) => {
7232
+ const link = buildFooterLink(colIndex, itemIndex, item.href, item.label, linkTemplate);
7233
+ column.appendChild(link);
7234
+ if (!firstLink) firstLink = link;
7235
+ });
7236
+ container.appendChild(column);
7237
+ return { column, firstLink };
7238
+ }
7239
+ function insertFooterColumn(heading = DEFAULT_FOOTER_COLUMN_HEADING, linkLabel = DEFAULT_FOOTER_COLUMN_LINK_LABEL, linkHref = DEFAULT_FOOTER_COLUMN_LINK_HREF) {
7240
+ const colIndex = getNextFooterColumnIndex();
7241
+ const inserted = insertFooterColumnDom(colIndex, heading, [{ href: linkHref, label: linkLabel }]);
7242
+ if (!inserted?.firstLink) throw new Error("Failed to insert footer column");
7243
+ syncFooterColumnIndices(listFooterColumns());
7244
+ return {
7245
+ column: inserted.column,
7246
+ firstLink: inserted.firstLink,
7247
+ colIndex,
7248
+ headingKey: `footer-${colIndex}-heading`,
7249
+ hrefKey: `footer-${colIndex}-0-href`,
7250
+ labelKey: `footer-${colIndex}-0-label`,
7251
+ heading,
7252
+ label: linkLabel,
7253
+ href: linkHref,
7254
+ order: getFooterOrderFromDom()
7255
+ };
7256
+ }
7257
+ function collectFooterColumnIndicesFromContent(content) {
7258
+ const indices = /* @__PURE__ */ new Set();
7259
+ for (const key of Object.keys(content)) {
7260
+ const href = parseFooterHrefKey(key);
7261
+ if (href) indices.add(href.col);
7262
+ const heading = key.match(FOOTER_HEADING_RE);
7263
+ if (heading) indices.add(parseInt(heading[1], 10));
7264
+ const label = key.match(FOOTER_LABEL_RE);
7265
+ if (label) indices.add(parseInt(label[1], 10));
7266
+ }
7267
+ const order = parseFooterOrder(content);
7268
+ if (order) {
7269
+ for (const col of order) {
7270
+ for (const hrefKey of col) {
7271
+ const parsed = parseFooterHrefKey(hrefKey);
7272
+ if (parsed) indices.add(parsed.col);
7273
+ }
7274
+ }
7275
+ }
7276
+ return [...indices].sort((a, b) => a - b);
7277
+ }
7278
+ function collectFooterItemsForColumn(content, colIndex) {
7279
+ const itemIndices = /* @__PURE__ */ new Set();
7280
+ for (const key of Object.keys(content)) {
7281
+ const href = parseFooterHrefKey(key);
7282
+ if (href?.col === colIndex) itemIndices.add(href.item);
7283
+ const label = key.match(FOOTER_LABEL_RE);
7284
+ if (label && parseInt(label[1], 10) === colIndex) {
7285
+ itemIndices.add(parseInt(label[2], 10));
7286
+ }
7287
+ }
7288
+ const sorted = [...itemIndices].sort((a, b) => a - b);
7289
+ if (sorted.length === 0) {
7290
+ return [
7291
+ {
7292
+ href: content[`footer-${colIndex}-0-href`] ?? DEFAULT_FOOTER_COLUMN_LINK_HREF,
7293
+ label: content[`footer-${colIndex}-0-label`] ?? DEFAULT_FOOTER_COLUMN_LINK_LABEL
7294
+ }
7295
+ ];
7296
+ }
7297
+ return sorted.map((itemIndex) => ({
7298
+ href: content[`footer-${colIndex}-${itemIndex}-href`] ?? DEFAULT_FOOTER_COLUMN_LINK_HREF,
7299
+ label: content[`footer-${colIndex}-${itemIndex}-label`] ?? DEFAULT_FOOTER_COLUMN_LINK_LABEL
7300
+ }));
7301
+ }
7302
+ function reconcileFooterColumnsFromContent(content) {
7303
+ const indices = collectFooterColumnIndicesFromContent(content);
7304
+ for (const colIndex of indices) {
7305
+ if (footerColumnExistsInDom(colIndex)) continue;
7306
+ const heading = content[`footer-${colIndex}-heading`] ?? DEFAULT_FOOTER_COLUMN_HEADING;
7307
+ const items = collectFooterItemsForColumn(content, colIndex);
7308
+ const hasPayload = Boolean(content[`footer-${colIndex}-heading`]) || items.some(
7309
+ (_, i) => content[`footer-${colIndex}-${i}-href`] !== void 0 || content[`footer-${colIndex}-${i}-label`] !== void 0
7310
+ ) || (parseFooterOrder(content)?.some(
7311
+ (col) => col.some((k) => parseFooterHrefKey(k)?.col === colIndex)
7312
+ ) ?? false);
7313
+ if (!hasPayload) continue;
7314
+ insertFooterColumnDom(colIndex, heading, items);
7315
+ }
7316
+ }
7010
7317
  function reconcileFooterOrderFromContent(content) {
7318
+ reconcileFooterColumnsFromContent(content);
7011
7319
  const order = parseFooterOrder(content);
7012
7320
  if (!order?.length) return;
7013
7321
  const current = getFooterOrderFromDom();
@@ -7017,6 +7325,31 @@ function reconcileFooterOrderFromContent(content) {
7017
7325
  }
7018
7326
  applyFooterOrder(order);
7019
7327
  }
7328
+ function resolveFooterLinksContainerSelectionTarget(target, clientX, clientY, isPointOverItem) {
7329
+ const container = getFooterLinksContainer();
7330
+ if (!container) return null;
7331
+ const inContainer = target === container || container.contains(target) && Boolean(target.closest("[data-ohw-footer-links], .rb-footer-links"));
7332
+ if (!inContainer && target !== container) {
7333
+ const r2 = container.getBoundingClientRect();
7334
+ const over = clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
7335
+ if (!over) return null;
7336
+ }
7337
+ if (isPointOverItem(container, clientX, clientY)) return null;
7338
+ const column = target.closest("[data-ohw-footer-col], [data-ohw-footer-column]") ?? null;
7339
+ if (column && container.contains(column)) {
7340
+ if (target === column || column.contains(target)) return null;
7341
+ }
7342
+ if (target === container || container.contains(target) || inContainer) {
7343
+ if (target === container || isFooterLinksContainer(target)) return container;
7344
+ if (!column) return container;
7345
+ }
7346
+ return null;
7347
+ }
7348
+ function isRowLayoutColumn(links) {
7349
+ if (links.length < 2) return false;
7350
+ const firstTop = links[0].getBoundingClientRect().top;
7351
+ return links.every((link) => Math.abs(link.getBoundingClientRect().top - firstTop) < 4);
7352
+ }
7020
7353
  function buildLinkDropSlots(column, columnIndex) {
7021
7354
  const links = listFooterLinksInColumn(column);
7022
7355
  const colRect = column.getBoundingClientRect();
@@ -7034,6 +7367,32 @@ function buildLinkDropSlots(column, columnIndex) {
7034
7367
  });
7035
7368
  return slots;
7036
7369
  }
7370
+ if (isRowLayoutColumn(links)) {
7371
+ for (let i = 0; i <= links.length; i++) {
7372
+ let left;
7373
+ if (i === 0) {
7374
+ left = links[0].getBoundingClientRect().left - barThickness / 2;
7375
+ } else if (i === links.length) {
7376
+ const last = links[links.length - 1].getBoundingClientRect();
7377
+ left = last.right - barThickness / 2;
7378
+ } else {
7379
+ const prev = links[i - 1].getBoundingClientRect();
7380
+ const next = links[i].getBoundingClientRect();
7381
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7382
+ }
7383
+ const heightRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7384
+ slots.push({
7385
+ insertIndex: i,
7386
+ columnIndex,
7387
+ left,
7388
+ top: heightRef.top,
7389
+ width: barThickness,
7390
+ height: Math.max(heightRef.height, colRect.height * 0.8),
7391
+ direction: "vertical"
7392
+ });
7393
+ }
7394
+ return slots;
7395
+ }
7037
7396
  for (let i = 0; i <= links.length; i++) {
7038
7397
  let top;
7039
7398
  if (i === 0) {
@@ -7117,8 +7476,7 @@ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
7117
7476
  return true;
7118
7477
  });
7119
7478
  for (const slot of slots) {
7120
- const cy = slot.top + slot.height / 2;
7121
- const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
7479
+ const dist = slot.direction === "vertical" ? Math.abs(clientX - (slot.left + slot.width / 2)) : Math.abs(clientY - (slot.top + slot.height / 2));
7122
7480
  if (!best || dist < best.dist) best = { slot, dist };
7123
7481
  }
7124
7482
  }
@@ -7142,6 +7500,30 @@ function hitTestColumnDropSlot(clientX, _clientY) {
7142
7500
  return best?.slot ?? null;
7143
7501
  }
7144
7502
 
7503
+ // src/lib/add-footer-column.ts
7504
+ function buildFooterColumnEditContentPatch(result) {
7505
+ return {
7506
+ [result.headingKey]: result.heading,
7507
+ [result.hrefKey]: result.href,
7508
+ [result.labelKey]: result.label,
7509
+ [FOOTER_ORDER_KEY]: JSON.stringify(result.order)
7510
+ };
7511
+ }
7512
+ function addFooterColumnWithPersist({
7513
+ postToParent: postToParent2
7514
+ }) {
7515
+ const result = insertFooterColumn();
7516
+ const patch = buildFooterColumnEditContentPatch(result);
7517
+ setStoredLinkHref(result.hrefKey, result.href);
7518
+ postToParent2({
7519
+ type: "ow:change",
7520
+ nodes: Object.entries(patch).map(([key, text]) => ({ key, text }))
7521
+ });
7522
+ postToParent2({ type: "ow:toast", title: "Item added", toastType: "success" });
7523
+ enforceLinkHrefs();
7524
+ return result;
7525
+ }
7526
+
7145
7527
  // src/lib/item-drag-interaction.ts
7146
7528
  function disableNativeHrefDrag(el) {
7147
7529
  if (el.draggable) el.draggable = false;
@@ -7162,9 +7544,10 @@ function lockItemDuringDrag() {
7162
7544
  clearTextSelection();
7163
7545
  }
7164
7546
  function unlockItemDragInteraction() {
7547
+ const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
7165
7548
  document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7166
7549
  document.documentElement.removeAttribute("data-ohw-item-dragging");
7167
- clearTextSelection();
7550
+ if (wasDragging) clearTextSelection();
7168
7551
  }
7169
7552
  var armFooterPressDrag = armItemPressDrag;
7170
7553
  var lockFooterDuringDrag = lockItemDuringDrag;
@@ -7541,7 +7924,6 @@ function useNavItemDrag({
7541
7924
  const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
7542
7925
  if (!anchor || !isNavbarHrefKey(hrefKey)) return;
7543
7926
  if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return;
7544
- armItemPressDrag();
7545
7927
  navPointerDragRef.current = {
7546
7928
  el: anchor,
7547
7929
  startX: e.clientX,
@@ -7568,6 +7950,7 @@ function useNavItemDrag({
7568
7950
  if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
7569
7951
  e.preventDefault();
7570
7952
  pending.started = true;
7953
+ armItemPressDrag();
7571
7954
  clearTextSelection();
7572
7955
  try {
7573
7956
  document.body.setPointerCapture(pending.pointerId);
@@ -7595,7 +7978,8 @@ function useNavItemDrag({
7595
7978
  }
7596
7979
  } catch {
7597
7980
  }
7598
- if (!pending?.started) {
7981
+ if (!pending) return;
7982
+ if (!pending.started) {
7599
7983
  unlockItemDragInteraction();
7600
7984
  return;
7601
7985
  }
@@ -7647,7 +8031,6 @@ function useNavItemDrag({
7647
8031
  const hrefKey = selected.getAttribute("data-ohw-href-key");
7648
8032
  if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7649
8033
  if (isNavbarButton3(selected) || isDragHandleDisabled2(selected)) return false;
7650
- armItemPressDrag();
7651
8034
  navPointerDragRef.current = {
7652
8035
  el: selected,
7653
8036
  startX: clientX,
@@ -7673,15 +8056,144 @@ function useNavItemDrag({
7673
8056
  };
7674
8057
  }
7675
8058
 
8059
+ // src/ui/footer-container-chrome.tsx
8060
+ var import_lucide_react11 = require("lucide-react");
8061
+ var import_jsx_runtime22 = require("react/jsx-runtime");
8062
+ function FooterContainerChrome({ rect, onAdd }) {
8063
+ const chromeGap = 6;
8064
+ const buttonMargin = 7;
8065
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8066
+ "div",
8067
+ {
8068
+ "data-ohw-footer-container-chrome": "",
8069
+ "data-ohw-bridge": "",
8070
+ className: "pointer-events-none fixed z-[2147483647]",
8071
+ style: {
8072
+ top: rect.top - chromeGap,
8073
+ left: rect.left - chromeGap,
8074
+ width: rect.width + chromeGap * 2,
8075
+ height: rect.height + chromeGap * 2
8076
+ },
8077
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
8078
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8079
+ "button",
8080
+ {
8081
+ type: "button",
8082
+ "data-ohw-footer-add-button": "",
8083
+ className: "pointer-events-auto absolute left-1/2 flex size-7 -translate-x-1/2 -translate-y-full items-center justify-center rounded-[10px] border border-border bg-background p-0.5 shadow-sm transition-colors hover:bg-muted/80",
8084
+ style: { top: chromeGap - buttonMargin },
8085
+ "aria-label": "Add item",
8086
+ onMouseDown: (e) => {
8087
+ e.preventDefault();
8088
+ e.stopPropagation();
8089
+ },
8090
+ onClick: (e) => {
8091
+ e.preventDefault();
8092
+ e.stopPropagation();
8093
+ onAdd();
8094
+ },
8095
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
8096
+ }
8097
+ ) }),
8098
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: "Add item" })
8099
+ ] })
8100
+ }
8101
+ ) });
8102
+ }
8103
+
8104
+ // src/lib/carousel.ts
8105
+ var import_react9 = require("react");
8106
+ var CAROUSEL_ATTR = "data-ohw-carousel";
8107
+ var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
8108
+ var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
8109
+ var CAROUSEL_EVENT = "ohw:carousel-change";
8110
+ function listCarouselKeys() {
8111
+ const keys = /* @__PURE__ */ new Set();
8112
+ document.querySelectorAll(`[${CAROUSEL_ATTR}]`).forEach((el) => {
8113
+ const key = el.getAttribute("data-ohw-key");
8114
+ if (key) keys.add(key);
8115
+ });
8116
+ return [...keys];
8117
+ }
8118
+ function containersForKey(key) {
8119
+ return Array.from(
8120
+ document.querySelectorAll(`[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`)
8121
+ );
8122
+ }
8123
+ function isCarouselKey(key) {
8124
+ return containersForKey(key).length > 0;
8125
+ }
8126
+ function parseSlides(raw) {
8127
+ if (!raw) return [];
8128
+ try {
8129
+ const parsed = JSON.parse(raw);
8130
+ if (!Array.isArray(parsed)) return [];
8131
+ return parsed.filter((s) => Boolean(s) && typeof s === "object").map((s) => ({ src: String(s.src ?? ""), alt: String(s.alt ?? "") }));
8132
+ } catch {
8133
+ return [];
8134
+ }
8135
+ }
8136
+ function readCarouselValue(key) {
8137
+ const container = containersForKey(key)[0];
8138
+ if (!container) return [];
8139
+ const fromAttr = parseSlides(container.getAttribute(CAROUSEL_VALUE_ATTR));
8140
+ if (fromAttr.length > 0) return fromAttr;
8141
+ return Array.from(container.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)).filter((slide) => slide.closest(`[${CAROUSEL_ATTR}]`) === container).sort((a, b) => slideIndex(a) - slideIndex(b)).map((slide) => {
8142
+ const img = slide instanceof HTMLImageElement ? slide : slide.querySelector("img");
8143
+ return { src: img?.src ?? "", alt: img?.alt ?? "" };
8144
+ });
8145
+ }
8146
+ function slideIndex(el) {
8147
+ const raw = el.getAttribute(CAROUSEL_SLIDE_ATTR);
8148
+ const n = raw ? parseInt(raw, 10) : NaN;
8149
+ return Number.isFinite(n) ? n : 0;
8150
+ }
8151
+ function applyCarouselValue(key, slides) {
8152
+ const value = JSON.stringify(slides);
8153
+ for (const container of containersForKey(key)) {
8154
+ if (container.getAttribute(CAROUSEL_VALUE_ATTR) === value) continue;
8155
+ container.setAttribute(CAROUSEL_VALUE_ATTR, value);
8156
+ container.dispatchEvent(
8157
+ new CustomEvent(CAROUSEL_EVENT, { detail: { images: slides } })
8158
+ );
8159
+ }
8160
+ }
8161
+ function applyCarouselNode(key, val) {
8162
+ if (!isCarouselKey(key)) return false;
8163
+ applyCarouselValue(key, parseSlides(val));
8164
+ return true;
8165
+ }
8166
+ function useOhwCarousel(key, initial) {
8167
+ const [images, setImages] = (0, import_react9.useState)(initial);
8168
+ (0, import_react9.useEffect)(() => {
8169
+ const el = document.querySelector(
8170
+ `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
8171
+ );
8172
+ if (!el) return;
8173
+ const onChange = (e) => {
8174
+ const detail = e.detail;
8175
+ if (Array.isArray(detail?.images)) setImages(detail.images);
8176
+ };
8177
+ el.addEventListener(CAROUSEL_EVENT, onChange);
8178
+ return () => el.removeEventListener(CAROUSEL_EVENT, onChange);
8179
+ }, [key]);
8180
+ const bind = {
8181
+ [CAROUSEL_ATTR]: "",
8182
+ "data-ohw-key": key,
8183
+ [CAROUSEL_VALUE_ATTR]: JSON.stringify(images)
8184
+ };
8185
+ return { images, setImages, bind };
8186
+ }
8187
+
7676
8188
  // src/ui/navbar-container-chrome.tsx
7677
- var import_lucide_react10 = require("lucide-react");
7678
- var import_jsx_runtime21 = require("react/jsx-runtime");
8189
+ var import_lucide_react12 = require("lucide-react");
8190
+ var import_jsx_runtime23 = require("react/jsx-runtime");
7679
8191
  function NavbarContainerChrome({
7680
8192
  rect,
7681
8193
  onAdd
7682
8194
  }) {
7683
8195
  const chromeGap = 6;
7684
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8196
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7685
8197
  "div",
7686
8198
  {
7687
8199
  "data-ohw-navbar-container-chrome": "",
@@ -7693,7 +8205,7 @@ function NavbarContainerChrome({
7693
8205
  width: rect.width + chromeGap * 2,
7694
8206
  height: rect.height + chromeGap * 2
7695
8207
  },
7696
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8208
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7697
8209
  "button",
7698
8210
  {
7699
8211
  type: "button",
@@ -7710,7 +8222,7 @@ function NavbarContainerChrome({
7710
8222
  e.stopPropagation();
7711
8223
  onAdd();
7712
8224
  },
7713
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
8225
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react12.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7714
8226
  }
7715
8227
  )
7716
8228
  }
@@ -7719,7 +8231,7 @@ function NavbarContainerChrome({
7719
8231
 
7720
8232
  // src/ui/drop-indicator.tsx
7721
8233
  var React10 = __toESM(require("react"), 1);
7722
- var import_jsx_runtime22 = require("react/jsx-runtime");
8234
+ var import_jsx_runtime24 = require("react/jsx-runtime");
7723
8235
  var dropIndicatorVariants = cva(
7724
8236
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7725
8237
  {
@@ -7743,7 +8255,7 @@ var dropIndicatorVariants = cva(
7743
8255
  );
7744
8256
  var DropIndicator = React10.forwardRef(
7745
8257
  ({ className, direction, state, ...props }, ref) => {
7746
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8258
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7747
8259
  "div",
7748
8260
  {
7749
8261
  ref,
@@ -7760,7 +8272,7 @@ var DropIndicator = React10.forwardRef(
7760
8272
  DropIndicator.displayName = "DropIndicator";
7761
8273
 
7762
8274
  // src/ui/badge.tsx
7763
- var import_jsx_runtime23 = require("react/jsx-runtime");
8275
+ var import_jsx_runtime25 = require("react/jsx-runtime");
7764
8276
  var badgeVariants = cva(
7765
8277
  "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",
7766
8278
  {
@@ -7778,12 +8290,12 @@ var badgeVariants = cva(
7778
8290
  }
7779
8291
  );
7780
8292
  function Badge({ className, variant, ...props }) {
7781
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
8293
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
7782
8294
  }
7783
8295
 
7784
8296
  // src/OhhwellsBridge.tsx
7785
- var import_lucide_react11 = require("lucide-react");
7786
- var import_jsx_runtime24 = require("react/jsx-runtime");
8297
+ var import_lucide_react13 = require("lucide-react");
8298
+ var import_jsx_runtime26 = require("react/jsx-runtime");
7787
8299
  var PRIMARY2 = "#0885FE";
7788
8300
  var IMAGE_FADE_MS = 300;
7789
8301
  function runOpacityFade(el, onDone) {
@@ -7962,7 +8474,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
7962
8474
  const root = (0, import_client.createRoot)(container);
7963
8475
  (0, import_react_dom2.flushSync)(() => {
7964
8476
  root.render(
7965
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
8477
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
7966
8478
  SchedulingWidget,
7967
8479
  {
7968
8480
  notifyOnConnect,
@@ -8080,6 +8592,9 @@ function collectEditableNodes(extraContent) {
8080
8592
  nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
8081
8593
  nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
8082
8594
  });
8595
+ for (const key of listCarouselKeys()) {
8596
+ nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
8597
+ }
8083
8598
  const seen = /* @__PURE__ */ new Set();
8084
8599
  return nodes.filter((node) => {
8085
8600
  if (!node.key) return true;
@@ -8163,7 +8678,7 @@ function applyLinkByKey(key, val) {
8163
8678
  }
8164
8679
  function isInsideLinkEditor(target) {
8165
8680
  return Boolean(
8166
- 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"]')
8681
+ 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-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
8167
8682
  );
8168
8683
  }
8169
8684
  function getHrefKeyFromElement(el) {
@@ -8244,7 +8759,7 @@ function isInferredFooterGroup(el) {
8244
8759
  return countFooterNavItems(el) >= 2;
8245
8760
  }
8246
8761
  function isNavigationContainer(el) {
8247
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
8762
+ return el.hasAttribute("data-ohw-nav-container") || isFooterLinksContainer(el) || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
8248
8763
  }
8249
8764
  function isNavbarLinksContainer(el) {
8250
8765
  return el.hasAttribute("data-ohw-nav-container");
@@ -8263,6 +8778,9 @@ function isPointOverNavigation(x, y) {
8263
8778
  const roots = /* @__PURE__ */ new Set();
8264
8779
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
8265
8780
  if (navRoot) roots.add(navRoot);
8781
+ document.querySelectorAll("[data-ohw-nav-drawer], [data-ohw-nav-container]").forEach((el) => {
8782
+ roots.add(el);
8783
+ });
8266
8784
  const footer = document.querySelector("footer");
8267
8785
  if (footer) roots.add(footer);
8268
8786
  for (const root of roots) {
@@ -8291,6 +8809,12 @@ function isPointOverNavItem(container, x, y) {
8291
8809
  return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8292
8810
  });
8293
8811
  }
8812
+ function isPointOverFooterColumn(x, y) {
8813
+ return listFooterColumns().some((col) => {
8814
+ const r2 = col.getBoundingClientRect();
8815
+ return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
8816
+ });
8817
+ }
8294
8818
  function resolveNavContainerSelectionTarget(target, clientX, clientY) {
8295
8819
  if (getNavigationItemAnchor(target)) return null;
8296
8820
  if (target.closest('[data-ohw-role="navbar-button"]')) return null;
@@ -8321,7 +8845,10 @@ function getNavigationSelectionParent(el) {
8321
8845
  if (footerGroup) return footerGroup;
8322
8846
  return getNavigationRoot(el);
8323
8847
  }
8324
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
8848
+ if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el))) {
8849
+ return getFooterLinksContainer();
8850
+ }
8851
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup(el)) {
8325
8852
  return getNavigationRoot(el);
8326
8853
  }
8327
8854
  return null;
@@ -8492,9 +9019,16 @@ function sanitizeHtml(html) {
8492
9019
  if (!SAFE_TAGS.has(el.tagName)) {
8493
9020
  parent.replaceChild(document.createTextNode(el.textContent ?? ""), el);
8494
9021
  } else {
8495
- const textAlign = el.style?.textAlign || el.getAttribute("align") || "";
9022
+ const htmlEl = el;
9023
+ const textAlign = htmlEl.style?.textAlign || el.getAttribute("align") || "";
9024
+ const fontWeight = htmlEl.style?.fontWeight || "";
9025
+ const fontStyle = htmlEl.style?.fontStyle || "";
9026
+ const textDecorationLine = htmlEl.style?.textDecorationLine || htmlEl.style?.textDecoration || "";
8496
9027
  for (const attr of Array.from(el.attributes)) el.removeAttribute(attr.name);
8497
- if (textAlign) el.style.textAlign = textAlign;
9028
+ if (textAlign) htmlEl.style.textAlign = textAlign;
9029
+ if (fontWeight) htmlEl.style.fontWeight = fontWeight;
9030
+ if (fontStyle) htmlEl.style.fontStyle = fontStyle;
9031
+ if (textDecorationLine) htmlEl.style.textDecorationLine = textDecorationLine;
8498
9032
  walk(el);
8499
9033
  }
8500
9034
  }
@@ -8541,7 +9075,7 @@ function EditGlowChrome({
8541
9075
  hideHandle = false
8542
9076
  }) {
8543
9077
  const GAP = SELECTION_CHROME_GAP2;
8544
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
9078
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
8545
9079
  "div",
8546
9080
  {
8547
9081
  ref: elRef,
@@ -8556,7 +9090,7 @@ function EditGlowChrome({
8556
9090
  zIndex: 2147483646
8557
9091
  },
8558
9092
  children: [
8559
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9093
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8560
9094
  "div",
8561
9095
  {
8562
9096
  style: {
@@ -8569,7 +9103,7 @@ function EditGlowChrome({
8569
9103
  }
8570
9104
  }
8571
9105
  ),
8572
- reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9106
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8573
9107
  "div",
8574
9108
  {
8575
9109
  "data-ohw-drag-handle-container": "",
@@ -8581,7 +9115,7 @@ function EditGlowChrome({
8581
9115
  transform: "translate(calc(-100% - 7px), -50%)",
8582
9116
  pointerEvents: dragDisabled ? "none" : "auto"
8583
9117
  },
8584
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9118
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8585
9119
  DragHandle,
8586
9120
  {
8587
9121
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -8682,6 +9216,79 @@ function resolveItemInteractionState(rect, parentScroll) {
8682
9216
  const { transform } = calcToolbarPos(rect, parentScroll, 120);
8683
9217
  return transform.includes("translateY(-100%)") ? "active-top" : "active-bottom";
8684
9218
  }
9219
+ function resolveSelectionStartElement(sel) {
9220
+ if (!sel || sel.rangeCount === 0) return null;
9221
+ const range = sel.getRangeAt(0);
9222
+ let startNode = range.startContainer;
9223
+ if (startNode.nodeType === Node.ELEMENT_NODE) {
9224
+ const el = startNode;
9225
+ startNode = el.childNodes[range.startOffset] ?? el.childNodes[range.startOffset - 1] ?? el;
9226
+ }
9227
+ return startNode.nodeType === Node.TEXT_NODE ? startNode.parentElement : startNode;
9228
+ }
9229
+ function detectActiveFormats(startEl, activeRoot) {
9230
+ const formats = /* @__PURE__ */ new Set();
9231
+ const cs = getComputedStyle(startEl);
9232
+ const weight = parseInt(cs.fontWeight, 10);
9233
+ let hasBold = cs.fontWeight === "bold" || !Number.isNaN(weight) && weight >= 600;
9234
+ let hasItalic = cs.fontStyle === "italic" || cs.fontStyle === "oblique";
9235
+ let hasUnderline = false;
9236
+ let hasStrike = false;
9237
+ let hasUl = false;
9238
+ let hasOl = false;
9239
+ for (let el = startEl; el; el = el.parentElement) {
9240
+ if (el.tagName === "B" || el.tagName === "STRONG") hasBold = true;
9241
+ if (el.tagName === "I" || el.tagName === "EM") hasItalic = true;
9242
+ const decoration = getComputedStyle(el).textDecorationLine;
9243
+ if (decoration.includes("underline") || el.tagName === "U" || el.tagName === "INS") hasUnderline = true;
9244
+ if (decoration.includes("line-through") || el.tagName === "S" || el.tagName === "STRIKE" || el.tagName === "DEL") hasStrike = true;
9245
+ if (el.tagName === "UL") hasUl = true;
9246
+ if (el.tagName === "OL") hasOl = true;
9247
+ if (el === activeRoot) break;
9248
+ }
9249
+ if (hasBold) formats.add("bold");
9250
+ if (hasItalic) formats.add("italic");
9251
+ if (hasUnderline) formats.add("underline");
9252
+ if (hasStrike) formats.add("strikeThrough");
9253
+ if (hasUl) formats.add("insertUnorderedList");
9254
+ if (hasOl) formats.add("insertOrderedList");
9255
+ const block = startEl.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? startEl;
9256
+ const align = getComputedStyle(block).textAlign;
9257
+ if (align === "center") formats.add("justifyCenter");
9258
+ else if (align === "right" || align === "end") formats.add("justifyRight");
9259
+ else formats.add("justifyLeft");
9260
+ return formats;
9261
+ }
9262
+ function setsEqual(a, b) {
9263
+ if (a.size !== b.size) return false;
9264
+ for (const item of a) {
9265
+ if (!b.has(item)) return false;
9266
+ }
9267
+ return true;
9268
+ }
9269
+ function textOffsetInRoot(root, node, offset) {
9270
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
9271
+ let total = 0;
9272
+ let current;
9273
+ while (current = walker.nextNode()) {
9274
+ if (current === node) return total + offset;
9275
+ total += (current.textContent ?? "").length;
9276
+ }
9277
+ return total;
9278
+ }
9279
+ function pointAtTextOffset(root, targetOffset) {
9280
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
9281
+ let total = 0;
9282
+ let current;
9283
+ let last = null;
9284
+ while (current = walker.nextNode()) {
9285
+ const len = (current.textContent ?? "").length;
9286
+ if (total + len >= targetOffset) return { node: current, offset: targetOffset - total };
9287
+ total += len;
9288
+ last = current;
9289
+ }
9290
+ return last ? { node: last, offset: (last.textContent ?? "").length } : null;
9291
+ }
8685
9292
  function FloatingToolbar({
8686
9293
  rect,
8687
9294
  parentScroll,
@@ -8691,9 +9298,9 @@ function FloatingToolbar({
8691
9298
  showEditLink,
8692
9299
  onEditLink
8693
9300
  }) {
8694
- const localRef = import_react9.default.useRef(null);
8695
- const [measuredW, setMeasuredW] = import_react9.default.useState(330);
8696
- const setRefs = import_react9.default.useCallback(
9301
+ const localRef = import_react10.default.useRef(null);
9302
+ const [measuredW, setMeasuredW] = import_react10.default.useState(330);
9303
+ const setRefs = import_react10.default.useCallback(
8697
9304
  (node) => {
8698
9305
  localRef.current = node;
8699
9306
  if (typeof elRef === "function") elRef(node);
@@ -8705,7 +9312,7 @@ function FloatingToolbar({
8705
9312
  },
8706
9313
  [elRef]
8707
9314
  );
8708
- import_react9.default.useLayoutEffect(() => {
9315
+ import_react10.default.useLayoutEffect(() => {
8709
9316
  const node = localRef.current;
8710
9317
  if (!node) return;
8711
9318
  const update = () => {
@@ -8718,7 +9325,7 @@ function FloatingToolbar({
8718
9325
  return () => ro.disconnect();
8719
9326
  }, [showEditLink, activeCommands]);
8720
9327
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
8721
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9328
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8722
9329
  "div",
8723
9330
  {
8724
9331
  ref: setRefs,
@@ -8730,12 +9337,12 @@ function FloatingToolbar({
8730
9337
  zIndex: 2147483647,
8731
9338
  pointerEvents: "auto"
8732
9339
  },
8733
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CustomToolbar, { children: [
8734
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react9.default.Fragment, { children: [
8735
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CustomToolbarDivider, {}),
9340
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CustomToolbar, { children: [
9341
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react10.default.Fragment, { children: [
9342
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(CustomToolbarDivider, {}),
8736
9343
  btns.map((btn) => {
8737
9344
  const isActive = activeCommands.has(btn.cmd);
8738
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9345
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8739
9346
  CustomToolbarButton,
8740
9347
  {
8741
9348
  title: btn.title,
@@ -8744,7 +9351,7 @@ function FloatingToolbar({
8744
9351
  e.preventDefault();
8745
9352
  onCommand(btn.cmd);
8746
9353
  },
8747
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9354
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8748
9355
  "svg",
8749
9356
  {
8750
9357
  width: "16",
@@ -8765,7 +9372,7 @@ function FloatingToolbar({
8765
9372
  );
8766
9373
  })
8767
9374
  ] }, gi)),
8768
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9375
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8769
9376
  CustomToolbarButton,
8770
9377
  {
8771
9378
  type: "button",
@@ -8779,7 +9386,7 @@ function FloatingToolbar({
8779
9386
  e.preventDefault();
8780
9387
  e.stopPropagation();
8781
9388
  },
8782
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
9389
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.Link, { className: "size-4 shrink-0", "aria-hidden": true })
8783
9390
  }
8784
9391
  ) : null
8785
9392
  ] })
@@ -8796,7 +9403,7 @@ function StateToggle({
8796
9403
  states,
8797
9404
  onStateChange
8798
9405
  }) {
8799
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9406
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8800
9407
  ToggleGroup,
8801
9408
  {
8802
9409
  "data-ohw-state-toggle": "",
@@ -8810,7 +9417,7 @@ function StateToggle({
8810
9417
  left: rect.right - 8,
8811
9418
  transform: "translateX(-100%)"
8812
9419
  },
8813
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
9420
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8814
9421
  }
8815
9422
  );
8816
9423
  }
@@ -8837,8 +9444,8 @@ function OhhwellsBridge() {
8837
9444
  const router = (0, import_navigation2.useRouter)();
8838
9445
  const searchParams = (0, import_navigation2.useSearchParams)();
8839
9446
  const isEditMode = isEditSessionActive();
8840
- const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
8841
- (0, import_react9.useEffect)(() => {
9447
+ const [bridgeRoot, setBridgeRoot] = (0, import_react10.useState)(null);
9448
+ (0, import_react10.useEffect)(() => {
8842
9449
  const figtreeFontId = "ohw-figtree-font";
8843
9450
  if (!document.getElementById(figtreeFontId)) {
8844
9451
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -8866,97 +9473,99 @@ function OhhwellsBridge() {
8866
9473
  const subdomainFromQuery = searchParams.get("subdomain");
8867
9474
  const subdomain = resolveSubdomain(subdomainFromQuery);
8868
9475
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
8869
- const postToParent2 = (0, import_react9.useCallback)((data) => {
9476
+ const postToParent2 = (0, import_react10.useCallback)((data) => {
8870
9477
  if (typeof window !== "undefined" && window.parent !== window) {
8871
9478
  window.parent.postMessage(data, "*");
8872
9479
  }
8873
9480
  }, []);
8874
- const [fetchState, setFetchState] = (0, import_react9.useState)("idle");
8875
- const autoSaveTimers = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
8876
- const activeElRef = (0, import_react9.useRef)(null);
8877
- const selectedElRef = (0, import_react9.useRef)(null);
8878
- const selectedHrefKeyRef = (0, import_react9.useRef)(null);
8879
- const selectedFooterColAttrRef = (0, import_react9.useRef)(null);
8880
- const originalContentRef = (0, import_react9.useRef)(null);
8881
- const activeStateElRef = (0, import_react9.useRef)(null);
8882
- const parentScrollRef = (0, import_react9.useRef)(null);
8883
- const visibleViewportRef = (0, import_react9.useRef)(null);
8884
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react9.useState)(null);
8885
- const attachVisibleViewport = (0, import_react9.useCallback)((node) => {
9481
+ const [fetchState, setFetchState] = (0, import_react10.useState)("idle");
9482
+ const autoSaveTimers = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
9483
+ const activeElRef = (0, import_react10.useRef)(null);
9484
+ const pointerHeldRef = (0, import_react10.useRef)(false);
9485
+ const selectedElRef = (0, import_react10.useRef)(null);
9486
+ const selectedHrefKeyRef = (0, import_react10.useRef)(null);
9487
+ const selectedFooterColAttrRef = (0, import_react10.useRef)(null);
9488
+ const originalContentRef = (0, import_react10.useRef)(null);
9489
+ const activeStateElRef = (0, import_react10.useRef)(null);
9490
+ const parentScrollRef = (0, import_react10.useRef)(null);
9491
+ const visibleViewportRef = (0, import_react10.useRef)(null);
9492
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react10.useState)(null);
9493
+ const attachVisibleViewport = (0, import_react10.useCallback)((node) => {
8886
9494
  visibleViewportRef.current = node;
8887
9495
  setDialogPortalContainer(node);
8888
9496
  if (node) applyVisibleViewport(node, parentScrollRef.current);
8889
9497
  }, []);
8890
- const toolbarElRef = (0, import_react9.useRef)(null);
8891
- const glowElRef = (0, import_react9.useRef)(null);
8892
- const hoveredImageRef = (0, import_react9.useRef)(null);
8893
- const hoveredImageHasTextOverlapRef = (0, import_react9.useRef)(false);
8894
- const dragOverElRef = (0, import_react9.useRef)(null);
8895
- const [mediaHover, setMediaHover] = (0, import_react9.useState)(null);
8896
- const [uploadingRects, setUploadingRects] = (0, import_react9.useState)({});
8897
- const hoveredGapRef = (0, import_react9.useRef)(null);
8898
- const imageUnhoverTimerRef = (0, import_react9.useRef)(null);
8899
- const imageShowTimerRef = (0, import_react9.useRef)(null);
8900
- const editStylesRef = (0, import_react9.useRef)(null);
8901
- const activateRef = (0, import_react9.useRef)(() => {
9498
+ const toolbarElRef = (0, import_react10.useRef)(null);
9499
+ const glowElRef = (0, import_react10.useRef)(null);
9500
+ const hoveredImageRef = (0, import_react10.useRef)(null);
9501
+ const hoveredImageHasTextOverlapRef = (0, import_react10.useRef)(false);
9502
+ const dragOverElRef = (0, import_react10.useRef)(null);
9503
+ const [mediaHover, setMediaHover] = (0, import_react10.useState)(null);
9504
+ const [carouselHover, setCarouselHover] = (0, import_react10.useState)(null);
9505
+ const [uploadingRects, setUploadingRects] = (0, import_react10.useState)({});
9506
+ const hoveredGapRef = (0, import_react10.useRef)(null);
9507
+ const imageUnhoverTimerRef = (0, import_react10.useRef)(null);
9508
+ const imageShowTimerRef = (0, import_react10.useRef)(null);
9509
+ const editStylesRef = (0, import_react10.useRef)(null);
9510
+ const activateRef = (0, import_react10.useRef)(() => {
8902
9511
  });
8903
- const deactivateRef = (0, import_react9.useRef)(() => {
9512
+ const deactivateRef = (0, import_react10.useRef)(() => {
8904
9513
  });
8905
- const selectRef = (0, import_react9.useRef)(() => {
9514
+ const selectRef = (0, import_react10.useRef)(() => {
8906
9515
  });
8907
- const selectFrameRef = (0, import_react9.useRef)(() => {
9516
+ const selectFrameRef = (0, import_react10.useRef)(() => {
8908
9517
  });
8909
- const deselectRef = (0, import_react9.useRef)(() => {
9518
+ const deselectRef = (0, import_react10.useRef)(() => {
8910
9519
  });
8911
- const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
9520
+ const reselectNavigationItemRef = (0, import_react10.useRef)(() => {
8912
9521
  });
8913
- const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
9522
+ const commitNavigationTextEditRef = (0, import_react10.useRef)(() => {
8914
9523
  });
8915
- const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
9524
+ const refreshActiveCommandsRef = (0, import_react10.useRef)(() => {
8916
9525
  });
8917
- const postToParentRef = (0, import_react9.useRef)(postToParent2);
9526
+ const postToParentRef = (0, import_react10.useRef)(postToParent2);
8918
9527
  postToParentRef.current = postToParent2;
8919
- const sectionsLoadedRef = (0, import_react9.useRef)(false);
8920
- const pendingScheduleConfigRequests = (0, import_react9.useRef)([]);
8921
- const [toolbarRect, setToolbarRect] = (0, import_react9.useState)(null);
8922
- const [toolbarVariant, setToolbarVariant] = (0, import_react9.useState)("none");
8923
- const toolbarVariantRef = (0, import_react9.useRef)("none");
9528
+ const sectionsLoadedRef = (0, import_react10.useRef)(false);
9529
+ const pendingScheduleConfigRequests = (0, import_react10.useRef)([]);
9530
+ const [toolbarRect, setToolbarRect] = (0, import_react10.useState)(null);
9531
+ const [toolbarVariant, setToolbarVariant] = (0, import_react10.useState)("none");
9532
+ const toolbarVariantRef = (0, import_react10.useRef)("none");
8924
9533
  toolbarVariantRef.current = toolbarVariant;
8925
- const [selectedIsCta, setSelectedIsCta] = (0, import_react9.useState)(false);
8926
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react9.useState)(null);
8927
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react9.useState)(false);
8928
- const [toggleState, setToggleState] = (0, import_react9.useState)(null);
8929
- const [maxBadge, setMaxBadge] = (0, import_react9.useState)(null);
8930
- const [activeCommands, setActiveCommands] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
8931
- const [sectionGap, setSectionGap] = (0, import_react9.useState)(null);
8932
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react9.useState)(false);
8933
- const hoveredNavContainerRef = (0, import_react9.useRef)(null);
8934
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react9.useState)(null);
8935
- const hoveredItemElRef = (0, import_react9.useRef)(null);
8936
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react9.useState)(null);
8937
- const siblingHintElRef = (0, import_react9.useRef)(null);
8938
- const [siblingHintRect, setSiblingHintRect] = (0, import_react9.useState)(null);
8939
- const [siblingHintRects, setSiblingHintRects] = (0, import_react9.useState)([]);
8940
- const [isItemDragging, setIsItemDragging] = (0, import_react9.useState)(false);
8941
- const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react9.useState)(false);
8942
- const footerDragRef = (0, import_react9.useRef)(null);
8943
- const [footerDropSlots, setFooterDropSlots] = (0, import_react9.useState)([]);
8944
- const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react9.useState)(null);
8945
- const [draggedItemRect, setDraggedItemRect] = (0, import_react9.useState)(null);
8946
- const footerPointerDragRef = (0, import_react9.useRef)(null);
8947
- const suppressNextClickRef = (0, import_react9.useRef)(false);
8948
- const suppressClickUntilRef = (0, import_react9.useRef)(0);
8949
- const [linkPopover, setLinkPopover] = (0, import_react9.useState)(null);
8950
- const linkPopoverSessionRef = (0, import_react9.useRef)(null);
8951
- const addNavAfterAnchorRef = (0, import_react9.useRef)(null);
8952
- const editContentRef = (0, import_react9.useRef)({});
8953
- const [sitePages, setSitePages] = (0, import_react9.useState)([]);
8954
- const [sectionsByPath, setSectionsByPath] = (0, import_react9.useState)({});
8955
- const sectionsPrefetchGenRef = (0, import_react9.useRef)(0);
8956
- const setLinkPopoverRef = (0, import_react9.useRef)(setLinkPopover);
8957
- const linkPopoverPanelRef = (0, import_react9.useRef)(null);
8958
- const linkPopoverOpenRef = (0, import_react9.useRef)(false);
8959
- const linkPopoverGraceUntilRef = (0, import_react9.useRef)(0);
9534
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react10.useState)(false);
9535
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react10.useState)(null);
9536
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react10.useState)(false);
9537
+ const [toggleState, setToggleState] = (0, import_react10.useState)(null);
9538
+ const [maxBadge, setMaxBadge] = (0, import_react10.useState)(null);
9539
+ const [activeCommands, setActiveCommands] = (0, import_react10.useState)(/* @__PURE__ */ new Set());
9540
+ const [sectionGap, setSectionGap] = (0, import_react10.useState)(null);
9541
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react10.useState)(false);
9542
+ const hoveredNavContainerRef = (0, import_react10.useRef)(null);
9543
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react10.useState)(null);
9544
+ const hoveredItemElRef = (0, import_react10.useRef)(null);
9545
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react10.useState)(null);
9546
+ const siblingHintElRef = (0, import_react10.useRef)(null);
9547
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react10.useState)(null);
9548
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react10.useState)([]);
9549
+ const [isItemDragging, setIsItemDragging] = (0, import_react10.useState)(false);
9550
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react10.useState)(false);
9551
+ const footerDragRef = (0, import_react10.useRef)(null);
9552
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react10.useState)([]);
9553
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react10.useState)(null);
9554
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react10.useState)(null);
9555
+ const footerPointerDragRef = (0, import_react10.useRef)(null);
9556
+ const suppressNextClickRef = (0, import_react10.useRef)(false);
9557
+ const suppressClickUntilRef = (0, import_react10.useRef)(0);
9558
+ const [linkPopover, setLinkPopover] = (0, import_react10.useState)(null);
9559
+ const linkPopoverSessionRef = (0, import_react10.useRef)(null);
9560
+ const addNavAfterAnchorRef = (0, import_react10.useRef)(null);
9561
+ const editContentRef = (0, import_react10.useRef)({});
9562
+ const [sitePages, setSitePages] = (0, import_react10.useState)([]);
9563
+ const [sectionsByPath, setSectionsByPath] = (0, import_react10.useState)({});
9564
+ const sectionsPrefetchGenRef = (0, import_react10.useRef)(0);
9565
+ const setLinkPopoverRef = (0, import_react10.useRef)(setLinkPopover);
9566
+ const linkPopoverPanelRef = (0, import_react10.useRef)(null);
9567
+ const linkPopoverOpenRef = (0, import_react10.useRef)(false);
9568
+ const linkPopoverGraceUntilRef = (0, import_react10.useRef)(0);
8960
9569
  setLinkPopoverRef.current = setLinkPopover;
8961
9570
  linkPopoverSessionRef.current = linkPopover;
8962
9571
  const {
@@ -8995,7 +9604,7 @@ function OhhwellsBridge() {
8995
9604
  const bumpLinkPopoverGrace = () => {
8996
9605
  linkPopoverGraceUntilRef.current = Date.now() + 350;
8997
9606
  };
8998
- const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
9607
+ const runSectionsPrefetch = (0, import_react10.useCallback)((pages) => {
8999
9608
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
9000
9609
  const gen = ++sectionsPrefetchGenRef.current;
9001
9610
  const paths = pages.map((p) => p.path);
@@ -9014,9 +9623,9 @@ function OhhwellsBridge() {
9014
9623
  );
9015
9624
  });
9016
9625
  }, [isEditMode, pathname]);
9017
- const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
9626
+ const runSectionsPrefetchRef = (0, import_react10.useRef)(runSectionsPrefetch);
9018
9627
  runSectionsPrefetchRef.current = runSectionsPrefetch;
9019
- (0, import_react9.useEffect)(() => {
9628
+ (0, import_react10.useEffect)(() => {
9020
9629
  if (!linkPopover) {
9021
9630
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
9022
9631
  return;
@@ -9066,7 +9675,7 @@ function OhhwellsBridge() {
9066
9675
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
9067
9676
  };
9068
9677
  }, [linkPopover, postToParent2]);
9069
- (0, import_react9.useEffect)(() => {
9678
+ (0, import_react10.useEffect)(() => {
9070
9679
  if (!isEditMode) return;
9071
9680
  const useFixtures = shouldUseDevFixtures();
9072
9681
  if (useFixtures) {
@@ -9090,14 +9699,14 @@ function OhhwellsBridge() {
9090
9699
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
9091
9700
  return () => window.removeEventListener("message", onSitePages);
9092
9701
  }, [isEditMode, postToParent2]);
9093
- (0, import_react9.useEffect)(() => {
9702
+ (0, import_react10.useEffect)(() => {
9094
9703
  if (!isEditMode || shouldUseDevFixtures()) return;
9095
9704
  void loadAllSectionsManifest().then((manifest) => {
9096
9705
  if (Object.keys(manifest).length === 0) return;
9097
9706
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
9098
9707
  });
9099
9708
  }, [isEditMode]);
9100
- (0, import_react9.useEffect)(() => {
9709
+ (0, import_react10.useEffect)(() => {
9101
9710
  const update = () => {
9102
9711
  const el = activeElRef.current ?? selectedElRef.current;
9103
9712
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -9121,10 +9730,10 @@ function OhhwellsBridge() {
9121
9730
  vvp.removeEventListener("resize", update);
9122
9731
  };
9123
9732
  }, []);
9124
- const refreshStateRules = (0, import_react9.useCallback)(() => {
9733
+ const refreshStateRules = (0, import_react10.useCallback)(() => {
9125
9734
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
9126
9735
  }, []);
9127
- const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
9736
+ const processConfigRequest = (0, import_react10.useCallback)((insertAfterVal) => {
9128
9737
  const tracker = getSectionsTracker();
9129
9738
  let entries = [];
9130
9739
  try {
@@ -9147,7 +9756,7 @@ function OhhwellsBridge() {
9147
9756
  }
9148
9757
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
9149
9758
  }, [isEditMode]);
9150
- const deactivate = (0, import_react9.useCallback)(() => {
9759
+ const deactivate = (0, import_react10.useCallback)(() => {
9151
9760
  const el = activeElRef.current;
9152
9761
  if (!el) return;
9153
9762
  const key = el.dataset.ohwKey;
@@ -9179,12 +9788,12 @@ function OhhwellsBridge() {
9179
9788
  setToolbarShowEditLink(false);
9180
9789
  postToParent2({ type: "ow:exit-edit" });
9181
9790
  }, [postToParent2]);
9182
- const clearSelectedAttr = (0, import_react9.useCallback)(() => {
9791
+ const clearSelectedAttr = (0, import_react10.useCallback)(() => {
9183
9792
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
9184
9793
  el.removeAttribute("data-ohw-selected");
9185
9794
  });
9186
9795
  }, []);
9187
- const deselect = (0, import_react9.useCallback)(() => {
9796
+ const deselect = (0, import_react10.useCallback)(() => {
9188
9797
  clearSelectedAttr();
9189
9798
  selectedElRef.current = null;
9190
9799
  selectedHrefKeyRef.current = null;
@@ -9204,11 +9813,11 @@ function OhhwellsBridge() {
9204
9813
  setToolbarVariant("none");
9205
9814
  }
9206
9815
  }, [clearSelectedAttr]);
9207
- const markSelected = (0, import_react9.useCallback)((el) => {
9816
+ const markSelected = (0, import_react10.useCallback)((el) => {
9208
9817
  clearSelectedAttr();
9209
9818
  el.setAttribute("data-ohw-selected", "");
9210
9819
  }, [clearSelectedAttr]);
9211
- const resolveHrefKeyElement = (0, import_react9.useCallback)((hrefKey) => {
9820
+ const resolveHrefKeyElement = (0, import_react10.useCallback)((hrefKey) => {
9212
9821
  if (isFooterHrefKey(hrefKey)) {
9213
9822
  return document.querySelector(
9214
9823
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -9223,7 +9832,7 @@ function OhhwellsBridge() {
9223
9832
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9224
9833
  );
9225
9834
  }, []);
9226
- const resyncSelectedNavigationItem = (0, import_react9.useCallback)(() => {
9835
+ const resyncSelectedNavigationItem = (0, import_react10.useCallback)(() => {
9227
9836
  const hrefKey = selectedHrefKeyRef.current;
9228
9837
  if (hrefKey) {
9229
9838
  const link = resolveHrefKeyElement(hrefKey);
@@ -9251,7 +9860,7 @@ function OhhwellsBridge() {
9251
9860
  );
9252
9861
  }
9253
9862
  }, [resolveHrefKeyElement]);
9254
- const reselectNavigationItem = (0, import_react9.useCallback)((navAnchor) => {
9863
+ const reselectNavigationItem = (0, import_react10.useCallback)((navAnchor) => {
9255
9864
  selectedElRef.current = navAnchor;
9256
9865
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9257
9866
  selectedFooterColAttrRef.current = null;
@@ -9266,7 +9875,7 @@ function OhhwellsBridge() {
9266
9875
  setToolbarShowEditLink(false);
9267
9876
  setActiveCommands(/* @__PURE__ */ new Set());
9268
9877
  }, [markSelected]);
9269
- const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
9878
+ const commitNavigationTextEdit = (0, import_react10.useCallback)((navAnchor) => {
9270
9879
  const el = activeElRef.current;
9271
9880
  if (!el) return;
9272
9881
  const key = el.dataset.ohwKey;
@@ -9293,7 +9902,7 @@ function OhhwellsBridge() {
9293
9902
  postToParent2({ type: "ow:exit-edit" });
9294
9903
  reselectNavigationItem(navAnchor);
9295
9904
  }, [postToParent2, reselectNavigationItem]);
9296
- const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
9905
+ const handleAddTopLevelNavItem = (0, import_react10.useCallback)(() => {
9297
9906
  const items = listNavbarItems();
9298
9907
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
9299
9908
  deselectRef.current();
@@ -9305,7 +9914,19 @@ function OhhwellsBridge() {
9305
9914
  intent: "add-nav"
9306
9915
  });
9307
9916
  }, []);
9308
- const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
9917
+ const handleAddFooterColumn = (0, import_react10.useCallback)(() => {
9918
+ deselectRef.current();
9919
+ deactivateRef.current();
9920
+ const result = addFooterColumnWithPersist({ postToParent: postToParent2 });
9921
+ editContentRef.current = {
9922
+ ...editContentRef.current,
9923
+ ...buildFooterColumnEditContentPatch(result)
9924
+ };
9925
+ requestAnimationFrame(() => {
9926
+ selectRef.current(result.firstLink);
9927
+ });
9928
+ }, [postToParent2]);
9929
+ const clearFooterDragVisuals = (0, import_react10.useCallback)(() => {
9309
9930
  footerDragRef.current = null;
9310
9931
  setSiblingHintRects([]);
9311
9932
  setFooterDropSlots([]);
@@ -9314,7 +9935,7 @@ function OhhwellsBridge() {
9314
9935
  setIsItemDragging(false);
9315
9936
  unlockFooterDragInteraction();
9316
9937
  }, []);
9317
- const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
9938
+ const refreshFooterDragVisuals = (0, import_react10.useCallback)((session, activeSlot, clientX, clientY) => {
9318
9939
  const dragged = session.draggedEl;
9319
9940
  setDraggedItemRect(dragged.getBoundingClientRect());
9320
9941
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -9339,13 +9960,13 @@ function OhhwellsBridge() {
9339
9960
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
9340
9961
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
9341
9962
  }, []);
9342
- const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
9963
+ const refreshFooterDragVisualsRef = (0, import_react10.useRef)(refreshFooterDragVisuals);
9343
9964
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
9344
- const commitFooterDragRef = (0, import_react9.useRef)(() => {
9965
+ const commitFooterDragRef = (0, import_react10.useRef)(() => {
9345
9966
  });
9346
- const beginFooterDragRef = (0, import_react9.useRef)(() => {
9967
+ const beginFooterDragRef = (0, import_react10.useRef)(() => {
9347
9968
  });
9348
- const beginFooterDrag = (0, import_react9.useCallback)(
9969
+ const beginFooterDrag = (0, import_react10.useCallback)(
9349
9970
  (session) => {
9350
9971
  const rect = session.draggedEl.getBoundingClientRect();
9351
9972
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -9365,7 +9986,7 @@ function OhhwellsBridge() {
9365
9986
  [refreshFooterDragVisuals]
9366
9987
  );
9367
9988
  beginFooterDragRef.current = beginFooterDrag;
9368
- const commitFooterDrag = (0, import_react9.useCallback)(
9989
+ const commitFooterDrag = (0, import_react10.useCallback)(
9369
9990
  (clientX, clientY) => {
9370
9991
  const session = footerDragRef.current;
9371
9992
  if (!session) {
@@ -9469,7 +10090,7 @@ function OhhwellsBridge() {
9469
10090
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
9470
10091
  );
9471
10092
  commitFooterDragRef.current = commitFooterDrag;
9472
- const startFooterLinkDrag = (0, import_react9.useCallback)(
10093
+ const startFooterLinkDrag = (0, import_react10.useCallback)(
9473
10094
  (anchor, clientX, clientY, wasSelected) => {
9474
10095
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
9475
10096
  if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
@@ -9490,7 +10111,7 @@ function OhhwellsBridge() {
9490
10111
  },
9491
10112
  [beginFooterDrag]
9492
10113
  );
9493
- const startFooterColumnDrag = (0, import_react9.useCallback)(
10114
+ const startFooterColumnDrag = (0, import_react10.useCallback)(
9494
10115
  (columnEl, clientX, clientY, wasSelected) => {
9495
10116
  const columns = listFooterColumns();
9496
10117
  const idx = columns.indexOf(columnEl);
@@ -9510,7 +10131,7 @@ function OhhwellsBridge() {
9510
10131
  },
9511
10132
  [beginFooterDrag]
9512
10133
  );
9513
- const handleItemDragStart = (0, import_react9.useCallback)(
10134
+ const handleItemDragStart = (0, import_react10.useCallback)(
9514
10135
  (e) => {
9515
10136
  const selected = selectedElRef.current;
9516
10137
  if (!selected) {
@@ -9530,7 +10151,7 @@ function OhhwellsBridge() {
9530
10151
  },
9531
10152
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
9532
10153
  );
9533
- const handleItemDragEnd = (0, import_react9.useCallback)(
10154
+ const handleItemDragEnd = (0, import_react10.useCallback)(
9534
10155
  (e) => {
9535
10156
  if (footerDragRef.current) {
9536
10157
  const x = e?.clientX;
@@ -9556,11 +10177,10 @@ function OhhwellsBridge() {
9556
10177
  },
9557
10178
  [commitFooterDrag, commitNavDrag, navDragRef]
9558
10179
  );
9559
- const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
10180
+ const handleItemChromePointerDown = (0, import_react10.useCallback)((e) => {
9560
10181
  if (e.button !== 0) return;
9561
10182
  const selected = selectedElRef.current;
9562
10183
  if (!selected) return;
9563
- armFooterPressDrag();
9564
10184
  const hrefKey = selected.getAttribute("data-ohw-href-key");
9565
10185
  if (hrefKey && isFooterHrefKey(hrefKey)) {
9566
10186
  footerPointerDragRef.current = {
@@ -9588,7 +10208,7 @@ function OhhwellsBridge() {
9588
10208
  }
9589
10209
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
9590
10210
  }, [armNavPressFromChrome]);
9591
- const handleItemChromeClick = (0, import_react9.useCallback)((clientX, clientY) => {
10211
+ const handleItemChromeClick = (0, import_react10.useCallback)((clientX, clientY) => {
9592
10212
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
9593
10213
  suppressNextClickRef.current = false;
9594
10214
  return;
@@ -9601,7 +10221,7 @@ function OhhwellsBridge() {
9601
10221
  }, []);
9602
10222
  reselectNavigationItemRef.current = reselectNavigationItem;
9603
10223
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
9604
- const select = (0, import_react9.useCallback)((anchor) => {
10224
+ const select = (0, import_react10.useCallback)((anchor) => {
9605
10225
  if (!isNavigationItem(anchor)) return;
9606
10226
  if (activeElRef.current) deactivate();
9607
10227
  selectedElRef.current = anchor;
@@ -9627,10 +10247,10 @@ function OhhwellsBridge() {
9627
10247
  setToolbarShowEditLink(false);
9628
10248
  setActiveCommands(/* @__PURE__ */ new Set());
9629
10249
  }, [deactivate, markSelected]);
9630
- const selectFrame = (0, import_react9.useCallback)((el) => {
10250
+ const selectFrame = (0, import_react10.useCallback)((el) => {
9631
10251
  if (!isNavigationContainer(el)) return;
9632
10252
  if (activeElRef.current) deactivate();
9633
- const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
10253
+ const isFooterColumn = !isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el)));
9634
10254
  selectedElRef.current = el;
9635
10255
  selectedHrefKeyRef.current = null;
9636
10256
  selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
@@ -9655,7 +10275,7 @@ function OhhwellsBridge() {
9655
10275
  setToolbarShowEditLink(false);
9656
10276
  setActiveCommands(/* @__PURE__ */ new Set());
9657
10277
  }, [deactivate, markSelected]);
9658
- const activate = (0, import_react9.useCallback)((el, options) => {
10278
+ const activate = (0, import_react10.useCallback)((el, options) => {
9659
10279
  if (activeElRef.current === el) return;
9660
10280
  clearSelectedAttr();
9661
10281
  selectedElRef.current = null;
@@ -9672,6 +10292,8 @@ function OhhwellsBridge() {
9672
10292
  setSiblingHintRect(null);
9673
10293
  setSiblingHintRects([]);
9674
10294
  setIsItemDragging(false);
10295
+ const preActivationSelection = window.getSelection();
10296
+ const preservedRange = preActivationSelection && preActivationSelection.rangeCount > 0 && !preActivationSelection.isCollapsed && el.contains(preActivationSelection.getRangeAt(0).commonAncestorContainer) ? preActivationSelection.getRangeAt(0).cloneRange() : null;
9675
10297
  el.setAttribute("contenteditable", "true");
9676
10298
  el.setAttribute("data-ohw-editing", "");
9677
10299
  el.removeAttribute("data-ohw-hovered");
@@ -9679,7 +10301,11 @@ function OhhwellsBridge() {
9679
10301
  activeElRef.current = el;
9680
10302
  originalContentRef.current = el.innerHTML;
9681
10303
  el.focus({ preventScroll: true });
9682
- if (options?.caretX !== void 0 && options?.caretY !== void 0) {
10304
+ if (preservedRange) {
10305
+ const selection = window.getSelection();
10306
+ selection?.removeAllRanges();
10307
+ selection?.addRange(preservedRange);
10308
+ } else if (options?.caretX !== void 0 && options?.caretY !== void 0) {
9683
10309
  const { caretX, caretY } = options;
9684
10310
  placeCaretAtPoint(el, caretX, caretY);
9685
10311
  requestAnimationFrame(() => {
@@ -9714,7 +10340,7 @@ function OhhwellsBridge() {
9714
10340
  selectRef.current = select;
9715
10341
  selectFrameRef.current = selectFrame;
9716
10342
  deselectRef.current = deselect;
9717
- (0, import_react9.useLayoutEffect)(() => {
10343
+ (0, import_react10.useLayoutEffect)(() => {
9718
10344
  if (!subdomain || isEditMode) {
9719
10345
  setFetchState("done");
9720
10346
  return;
@@ -9724,6 +10350,7 @@ function OhhwellsBridge() {
9724
10350
  for (const [key, val] of Object.entries(content)) {
9725
10351
  if (key === "__ohw_sections") continue;
9726
10352
  if (applyVideoSettingNode(key, val)) continue;
10353
+ if (applyCarouselNode(key, val)) continue;
9727
10354
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9728
10355
  if (el.dataset.ohwEditable === "image") {
9729
10356
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -9784,7 +10411,7 @@ function OhhwellsBridge() {
9784
10411
  cancelled = true;
9785
10412
  };
9786
10413
  }, [subdomain, isEditMode]);
9787
- (0, import_react9.useEffect)(() => {
10414
+ (0, import_react10.useEffect)(() => {
9788
10415
  if (!subdomain || isEditMode) return;
9789
10416
  let debounceTimer = null;
9790
10417
  let observer = null;
@@ -9797,6 +10424,7 @@ function OhhwellsBridge() {
9797
10424
  for (const [key, val] of Object.entries(content)) {
9798
10425
  if (key === "__ohw_sections") continue;
9799
10426
  if (applyVideoSettingNode(key, val)) continue;
10427
+ if (applyCarouselNode(key, val)) continue;
9800
10428
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9801
10429
  if (el.dataset.ohwEditable === "image") {
9802
10430
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -9834,16 +10462,16 @@ function OhhwellsBridge() {
9834
10462
  if (debounceTimer) clearTimeout(debounceTimer);
9835
10463
  };
9836
10464
  }, [subdomain, isEditMode, pathname]);
9837
- (0, import_react9.useLayoutEffect)(() => {
10465
+ (0, import_react10.useLayoutEffect)(() => {
9838
10466
  const el = document.getElementById("ohw-loader");
9839
10467
  if (!el) return;
9840
10468
  const visible = Boolean(subdomain) && fetchState !== "done";
9841
10469
  el.style.display = visible ? "flex" : "none";
9842
10470
  }, [subdomain, fetchState]);
9843
- (0, import_react9.useEffect)(() => {
10471
+ (0, import_react10.useEffect)(() => {
9844
10472
  postToParent2({ type: "ow:navigation", path: pathname });
9845
10473
  }, [pathname, postToParent2]);
9846
- (0, import_react9.useEffect)(() => {
10474
+ (0, import_react10.useEffect)(() => {
9847
10475
  if (!isEditMode) return;
9848
10476
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
9849
10477
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -9851,7 +10479,7 @@ function OhhwellsBridge() {
9851
10479
  deselectRef.current();
9852
10480
  deactivateRef.current();
9853
10481
  }, [pathname, isEditMode]);
9854
- (0, import_react9.useEffect)(() => {
10482
+ (0, import_react10.useEffect)(() => {
9855
10483
  const contentForNav = () => {
9856
10484
  if (isEditMode) return editContentRef.current;
9857
10485
  if (!subdomain) return {};
@@ -9916,7 +10544,7 @@ function OhhwellsBridge() {
9916
10544
  observer?.disconnect();
9917
10545
  };
9918
10546
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
9919
- (0, import_react9.useEffect)(() => {
10547
+ (0, import_react10.useEffect)(() => {
9920
10548
  if (!isEditMode) return;
9921
10549
  const measure = () => {
9922
10550
  const h = document.body.scrollHeight;
@@ -9940,7 +10568,7 @@ function OhhwellsBridge() {
9940
10568
  window.removeEventListener("resize", handleResize);
9941
10569
  };
9942
10570
  }, [pathname, isEditMode, postToParent2]);
9943
- (0, import_react9.useEffect)(() => {
10571
+ (0, import_react10.useEffect)(() => {
9944
10572
  if (!subdomainFromQuery || isEditMode) return;
9945
10573
  const handleClick = (e) => {
9946
10574
  const anchor = e.target.closest("a");
@@ -9956,7 +10584,7 @@ function OhhwellsBridge() {
9956
10584
  document.addEventListener("click", handleClick, true);
9957
10585
  return () => document.removeEventListener("click", handleClick, true);
9958
10586
  }, [subdomainFromQuery, isEditMode, router]);
9959
- (0, import_react9.useEffect)(() => {
10587
+ (0, import_react10.useEffect)(() => {
9960
10588
  if (!isEditMode) {
9961
10589
  editStylesRef.current?.base.remove();
9962
10590
  editStylesRef.current?.forceHover.remove();
@@ -10135,7 +10763,9 @@ function OhhwellsBridge() {
10135
10763
  if (inActive || inActiveNav) {
10136
10764
  e.preventDefault();
10137
10765
  e.stopPropagation();
10138
- if (e.detail < 2) {
10766
+ const selection = window.getSelection();
10767
+ const hasRangeSelection = Boolean(selection && !selection.isCollapsed);
10768
+ if (e.detail < 2 && !hasRangeSelection) {
10139
10769
  placeCaretAtPoint(active, e.clientX, e.clientY);
10140
10770
  refreshActiveCommandsRef.current();
10141
10771
  }
@@ -10178,7 +10808,7 @@ function OhhwellsBridge() {
10178
10808
  }
10179
10809
  e.preventDefault();
10180
10810
  e.stopPropagation();
10181
- activateRef.current(editable);
10811
+ activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
10182
10812
  return;
10183
10813
  }
10184
10814
  const hrefAnchor = getNavigationItemAnchor(target);
@@ -10205,6 +10835,18 @@ function OhhwellsBridge() {
10205
10835
  selectFrameRef.current(footerColClick);
10206
10836
  return;
10207
10837
  }
10838
+ const footerLinksToSelect = resolveFooterLinksContainerSelectionTarget(
10839
+ target,
10840
+ e.clientX,
10841
+ e.clientY,
10842
+ isPointOverNavItem
10843
+ );
10844
+ if (footerLinksToSelect) {
10845
+ e.preventDefault();
10846
+ e.stopPropagation();
10847
+ selectFrameRef.current(footerLinksToSelect);
10848
+ return;
10849
+ }
10208
10850
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
10209
10851
  if (navContainerToSelect) {
10210
10852
  e.preventDefault();
@@ -10263,8 +10905,8 @@ function OhhwellsBridge() {
10263
10905
  return;
10264
10906
  }
10265
10907
  const navLabel = getNavigationLabelEditable(target);
10266
- if (!navLabel) return;
10267
- const { editable } = navLabel;
10908
+ const editable = navLabel?.editable ?? target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
10909
+ if (!editable || isMediaEditable(editable) || editable.dataset.ohwEditable === "link") return;
10268
10910
  e.preventDefault();
10269
10911
  e.stopPropagation();
10270
10912
  if (activeElRef.current !== editable) {
@@ -10285,16 +10927,38 @@ function OhhwellsBridge() {
10285
10927
  setHoveredNavContainerRect(null);
10286
10928
  return;
10287
10929
  }
10288
- if (toolbarVariantRef.current !== "select-frame") {
10289
- const navContainer = target.closest("[data-ohw-nav-container]");
10290
- if (navContainer && !getNavigationItemAnchor(target)) {
10291
- hoveredNavContainerRef.current = navContainer;
10292
- setHoveredNavContainerRect(navContainer.getBoundingClientRect());
10293
- hoveredItemElRef.current = null;
10294
- setHoveredItemRect(null);
10295
- return;
10930
+ {
10931
+ const selected2 = selectedElRef.current;
10932
+ const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup(selected2)));
10933
+ const allowNavContainerHover = toolbarVariantRef.current !== "select-frame";
10934
+ const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
10935
+ if (allowNavContainerHover) {
10936
+ const navContainer = target.closest("[data-ohw-nav-container]");
10937
+ if (navContainer && !getNavigationItemAnchor(target)) {
10938
+ hoveredNavContainerRef.current = navContainer;
10939
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
10940
+ hoveredItemElRef.current = null;
10941
+ setHoveredItemRect(null);
10942
+ return;
10943
+ }
10296
10944
  }
10297
- if (!target.closest("[data-ohw-nav-container]")) {
10945
+ if (allowFooterLinksHover) {
10946
+ const navContainer = target.closest("[data-ohw-nav-container]");
10947
+ if (!navContainer) {
10948
+ const footerLinks = target.closest("[data-ohw-footer-links], .rb-footer-links") ?? null;
10949
+ if (footerLinks && selected2 !== footerLinks && !getNavigationItemAnchor(target) && !target.closest("[data-ohw-footer-col], [data-ohw-footer-column]")) {
10950
+ hoveredNavContainerRef.current = footerLinks;
10951
+ setHoveredNavContainerRect(footerLinks.getBoundingClientRect());
10952
+ hoveredItemElRef.current = null;
10953
+ setHoveredItemRect(null);
10954
+ return;
10955
+ }
10956
+ if (!footerLinks) {
10957
+ hoveredNavContainerRef.current = null;
10958
+ setHoveredNavContainerRect(null);
10959
+ }
10960
+ }
10961
+ } else if (toolbarVariantRef.current === "select-frame") {
10298
10962
  hoveredNavContainerRef.current = null;
10299
10963
  setHoveredNavContainerRect(null);
10300
10964
  }
@@ -10336,9 +11000,9 @@ function OhhwellsBridge() {
10336
11000
  };
10337
11001
  const handleMouseOut = (e) => {
10338
11002
  const target = e.target;
10339
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column]")) {
11003
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column], [data-ohw-footer-links], .rb-footer-links")) {
10340
11004
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10341
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
11005
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column], [data-ohw-footer-links], .rb-footer-links, [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button], [data-ohw-footer-container-chrome], [data-ohw-footer-add-button]")) {
10342
11006
  return;
10343
11007
  }
10344
11008
  hoveredNavContainerRef.current = null;
@@ -10433,6 +11097,15 @@ function OhhwellsBridge() {
10433
11097
  if (track) track.setAttribute("data-ohw-hover-paused", "");
10434
11098
  };
10435
11099
  const clearImageHover = () => setMediaHover(null);
11100
+ const dismissImageHover = () => {
11101
+ if (hoveredImageRef.current) {
11102
+ hoveredImageRef.current = null;
11103
+ hoveredImageHasTextOverlapRef.current = false;
11104
+ resumeAnimTracks();
11105
+ postToParentRef.current({ type: "ow:image-unhover" });
11106
+ }
11107
+ clearImageHover();
11108
+ };
10436
11109
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
10437
11110
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
10438
11111
  const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
@@ -10476,16 +11149,13 @@ function OhhwellsBridge() {
10476
11149
  }
10477
11150
  return smallest;
10478
11151
  };
10479
- const dismissImageHover = () => {
10480
- if (hoveredImageRef.current) {
10481
- hoveredImageRef.current = null;
10482
- hoveredImageHasTextOverlapRef.current = false;
10483
- resumeAnimTracks();
10484
- postToParentRef.current({ type: "ow:image-unhover" });
10485
- }
10486
- };
10487
11152
  const probeNavigationHoverAt = (x, y) => {
10488
- if (toolbarVariantRef.current === "select-frame") {
11153
+ const selected = selectedElRef.current;
11154
+ const selectedIsFooterColumn = Boolean(selected) && !isFooterLinksContainer(selected) && (selected.hasAttribute("data-ohw-footer-col") || selected.hasAttribute("data-ohw-footer-column") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected)));
11155
+ const selectedIsFooterLinks = Boolean(selected && isFooterLinksContainer(selected));
11156
+ const allowNavContainerHover = toolbarVariantRef.current !== "select-frame";
11157
+ const allowFooterLinksHover = (toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn) && !selectedIsFooterLinks;
11158
+ if (toolbarVariantRef.current === "select-frame" && !allowFooterLinksHover) {
10489
11159
  hoveredNavContainerRef.current = null;
10490
11160
  setHoveredNavContainerRect(null);
10491
11161
  }
@@ -10507,7 +11177,6 @@ function OhhwellsBridge() {
10507
11177
  if (navItemHit) {
10508
11178
  hoveredNavContainerRef.current = null;
10509
11179
  setHoveredNavContainerRect(null);
10510
- const selected = selectedElRef.current;
10511
11180
  if (selected !== navItemHit) {
10512
11181
  clearHrefKeyHover(navItemHit);
10513
11182
  hoveredItemElRef.current = navItemHit;
@@ -10519,7 +11188,7 @@ function OhhwellsBridge() {
10519
11188
  return;
10520
11189
  }
10521
11190
  }
10522
- if (toolbarVariantRef.current !== "select-frame") {
11191
+ if (allowNavContainerHover) {
10523
11192
  for (const container of navContainers) {
10524
11193
  const containerRect = container.getBoundingClientRect();
10525
11194
  const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
@@ -10531,12 +11200,23 @@ function OhhwellsBridge() {
10531
11200
  setHoveredItemRect(null);
10532
11201
  return;
10533
11202
  }
10534
- hoveredNavContainerRef.current = null;
10535
- setHoveredNavContainerRect(null);
10536
- } else {
10537
- hoveredNavContainerRef.current = null;
10538
- setHoveredNavContainerRect(null);
10539
11203
  }
11204
+ if (allowFooterLinksHover) {
11205
+ const footerLinks = getFooterLinksContainer();
11206
+ if (footerLinks) {
11207
+ const containerRect = footerLinks.getBoundingClientRect();
11208
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
11209
+ if (overContainer && !isPointOverNavItem(footerLinks, x, y) && !isPointOverFooterColumn(x, y)) {
11210
+ hoveredNavContainerRef.current = footerLinks;
11211
+ setHoveredNavContainerRect(footerLinks.getBoundingClientRect());
11212
+ hoveredItemElRef.current = null;
11213
+ setHoveredItemRect(null);
11214
+ return;
11215
+ }
11216
+ }
11217
+ }
11218
+ hoveredNavContainerRef.current = null;
11219
+ setHoveredNavContainerRect(null);
10540
11220
  for (const column of footerColumns) {
10541
11221
  const containerRect = column.getBoundingClientRect();
10542
11222
  const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
@@ -10798,6 +11478,7 @@ function OhhwellsBridge() {
10798
11478
  const ZONE = 20;
10799
11479
  for (let i = 0; i < sections.length; i++) {
10800
11480
  const a = sections[i];
11481
+ if (a.dataset.ohwSection === "footer") continue;
10801
11482
  const b = sections[i + 1] ?? null;
10802
11483
  const boundaryY = a.getBoundingClientRect().bottom;
10803
11484
  if (Math.abs(y - boundaryY) <= ZONE) {
@@ -11019,6 +11700,7 @@ function OhhwellsBridge() {
11019
11700
  continue;
11020
11701
  }
11021
11702
  if (applyVideoSettingNode(key, val)) continue;
11703
+ if (applyCarouselNode(key, val)) continue;
11022
11704
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11023
11705
  if (el.dataset.ohwEditable === "image") {
11024
11706
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -11043,6 +11725,7 @@ function OhhwellsBridge() {
11043
11725
  }
11044
11726
  editContentRef.current = { ...editContentRef.current, ...content };
11045
11727
  reconcileNavbarItemsFromContent(editContentRef.current);
11728
+ reconcileFooterOrderFromContent(editContentRef.current);
11046
11729
  syncNavigationDragCursorAttrs();
11047
11730
  enforceLinkHrefs();
11048
11731
  postToParentRef.current({ type: "ow:hydrate-done" });
@@ -11097,6 +11780,11 @@ function OhhwellsBridge() {
11097
11780
  }
11098
11781
  if (selectedElRef.current) {
11099
11782
  if (toolbarVariantRef.current === "select-frame") {
11783
+ const parent2 = getNavigationSelectionParent(selectedElRef.current);
11784
+ if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
11785
+ selectFrameRef.current(parent2);
11786
+ return;
11787
+ }
11100
11788
  deselectRef.current();
11101
11789
  return;
11102
11790
  }
@@ -11112,13 +11800,10 @@ function OhhwellsBridge() {
11112
11800
  const handleKeyDown = (e) => {
11113
11801
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
11114
11802
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
11115
- const navAnchor = getNavigationItemAnchor(activeElRef.current);
11116
- if (navAnchor) {
11117
- e.preventDefault();
11118
- selectAllTextInEditable(activeElRef.current);
11119
- refreshActiveCommandsRef.current();
11120
- return;
11121
- }
11803
+ e.preventDefault();
11804
+ selectAllTextInEditable(activeElRef.current);
11805
+ refreshActiveCommandsRef.current();
11806
+ return;
11122
11807
  }
11123
11808
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
11124
11809
  setLinkPopoverRef.current(null);
@@ -11126,6 +11811,12 @@ function OhhwellsBridge() {
11126
11811
  }
11127
11812
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
11128
11813
  if (toolbarVariantRef.current === "select-frame") {
11814
+ const parent2 = getNavigationSelectionParent(selectedElRef.current);
11815
+ if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
11816
+ e.preventDefault();
11817
+ selectFrameRef.current(parent2);
11818
+ return;
11819
+ }
11129
11820
  deselectRef.current();
11130
11821
  return;
11131
11822
  }
@@ -11288,33 +11979,34 @@ function OhhwellsBridge() {
11288
11979
  const h = document.documentElement.scrollHeight;
11289
11980
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
11290
11981
  };
11982
+ let selectionChangeRaf = null;
11983
+ const runSelectionChange = () => {
11984
+ selectionChangeRaf = null;
11985
+ const activeRoot = activeElRef.current;
11986
+ if (!activeRoot) return;
11987
+ const startEl = resolveSelectionStartElement(window.getSelection());
11988
+ const next = startEl ? detectActiveFormats(startEl, activeRoot) : /* @__PURE__ */ new Set();
11989
+ setActiveCommands((prev) => setsEqual(prev, next) ? prev : next);
11990
+ };
11291
11991
  const handleSelectionChange = () => {
11292
- if (!activeElRef.current) return;
11293
- const next = /* @__PURE__ */ new Set();
11294
- for (const cmd of ["bold", "italic", "underline", "strikeThrough", "insertUnorderedList", "insertOrderedList"]) {
11295
- try {
11296
- if (document.queryCommandState(cmd)) next.add(cmd);
11297
- } catch {
11298
- }
11299
- }
11300
- const sel = window.getSelection();
11301
- const anchor = sel?.anchorNode;
11302
- if (anchor) {
11303
- const el = anchor.nodeType === Node.TEXT_NODE ? anchor.parentElement : anchor;
11304
- const block = el?.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? el;
11305
- if (block) {
11306
- const align = getComputedStyle(block).textAlign;
11307
- if (align === "center") next.add("justifyCenter");
11308
- else if (align === "right" || align === "end") next.add("justifyRight");
11309
- else next.add("justifyLeft");
11310
- }
11311
- }
11312
- setActiveCommands(next);
11992
+ if (pointerHeldRef.current) return;
11993
+ if (selectionChangeRaf !== null) return;
11994
+ selectionChangeRaf = requestAnimationFrame(runSelectionChange);
11313
11995
  };
11314
- refreshActiveCommandsRef.current = handleSelectionChange;
11996
+ const markPointerHeld = (e) => {
11997
+ if (e.button !== 0) return;
11998
+ pointerHeldRef.current = true;
11999
+ };
12000
+ const markPointerReleased = () => {
12001
+ if (!pointerHeldRef.current) return;
12002
+ pointerHeldRef.current = false;
12003
+ handleSelectionChange();
12004
+ };
12005
+ refreshActiveCommandsRef.current = runSelectionChange;
11315
12006
  const handleDocMouseLeave = () => {
11316
12007
  hoveredImageRef.current = null;
11317
12008
  setMediaHover(null);
12009
+ setCarouselHover(null);
11318
12010
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
11319
12011
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
11320
12012
  activeStateElRef.current = null;
@@ -11354,6 +12046,48 @@ function OhhwellsBridge() {
11354
12046
  if (e.data?.type !== "ow:canvas-height" || typeof e.data.height !== "number") return;
11355
12047
  document.documentElement.style.setProperty("--ohw-canvas-h", `${e.data.height}px`);
11356
12048
  };
12049
+ const handleCarouselChange = (e) => {
12050
+ if (e.data?.type !== "ow:carousel-change") return;
12051
+ const { key, images } = e.data;
12052
+ if (!key || !Array.isArray(images)) return;
12053
+ const text = JSON.stringify(images);
12054
+ applyCarouselValue(key, images);
12055
+ editContentRef.current = { ...editContentRef.current, [key]: text };
12056
+ postToParentRef.current({ type: "ow:change", nodes: [{ key, text }] });
12057
+ setCarouselHover(null);
12058
+ };
12059
+ const findCarouselAtPoint = (clientX, clientY) => {
12060
+ const containers = Array.from(document.querySelectorAll(`[${CAROUSEL_ATTR}]`));
12061
+ for (let i = containers.length - 1; i >= 0; i--) {
12062
+ const r2 = containers[i].getBoundingClientRect();
12063
+ if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) {
12064
+ return containers[i];
12065
+ }
12066
+ }
12067
+ return null;
12068
+ };
12069
+ const isPointOverEditable = (scope, clientX, clientY) => {
12070
+ const editables = scope.querySelectorAll("[data-ohw-editable], [data-ohw-href-key]");
12071
+ for (const el of editables) {
12072
+ const r2 = el.getBoundingClientRect();
12073
+ if (clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom) return true;
12074
+ }
12075
+ return false;
12076
+ };
12077
+ const handleCarouselHover = (e) => {
12078
+ const container = findCarouselAtPoint(e.clientX, e.clientY);
12079
+ const scope = container?.closest("[data-ohw-section]") ?? container?.parentElement ?? null;
12080
+ if (!container || !scope || isPointOverEditable(scope, e.clientX, e.clientY)) {
12081
+ setCarouselHover((prev) => prev ? null : prev);
12082
+ return;
12083
+ }
12084
+ const key = container.getAttribute("data-ohw-key") ?? "";
12085
+ if (!key) return;
12086
+ const r2 = getVisibleRect(container);
12087
+ setCarouselHover(
12088
+ (prev) => prev && prev.key === key && prev.rect.top === r2.top && prev.rect.left === r2.left && prev.rect.width === r2.width && prev.rect.height === r2.height ? prev : { key, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
12089
+ );
12090
+ };
11357
12091
  const applyToolbarPos = (rect) => {
11358
12092
  const ps = parentScrollRef.current;
11359
12093
  const measuredW = toolbarElRef.current?.offsetWidth || 330;
@@ -11467,6 +12201,7 @@ function OhhwellsBridge() {
11467
12201
  window.addEventListener("message", handleRemoveSchedulingSection);
11468
12202
  window.addEventListener("message", handleImageUrl);
11469
12203
  window.addEventListener("message", handleImageUploading);
12204
+ window.addEventListener("message", handleCarouselChange);
11470
12205
  window.addEventListener("message", handleCanvasHeight);
11471
12206
  window.addEventListener("message", handleParentScroll);
11472
12207
  window.addEventListener("message", handlePointerSync);
@@ -11484,6 +12219,7 @@ function OhhwellsBridge() {
11484
12219
  document.addEventListener("mouseover", handleMouseOver, true);
11485
12220
  document.addEventListener("mouseout", handleMouseOut, true);
11486
12221
  document.addEventListener("mousemove", handleMouseMove, true);
12222
+ document.addEventListener("mousemove", handleCarouselHover, true);
11487
12223
  document.addEventListener("mouseleave", handleDocMouseLeave);
11488
12224
  document.addEventListener("dragover", handleDragOver, true);
11489
12225
  document.addEventListener("dragleave", handleDragLeave, true);
@@ -11499,6 +12235,7 @@ function OhhwellsBridge() {
11499
12235
  document.removeEventListener("mouseover", handleMouseOver, true);
11500
12236
  document.removeEventListener("mouseout", handleMouseOut, true);
11501
12237
  document.removeEventListener("mousemove", handleMouseMove, true);
12238
+ document.removeEventListener("mousemove", handleCarouselHover, true);
11502
12239
  document.removeEventListener("mouseleave", handleDocMouseLeave);
11503
12240
  document.removeEventListener("dragover", handleDragOver, true);
11504
12241
  document.removeEventListener("dragleave", handleDragLeave, true);
@@ -11514,6 +12251,7 @@ function OhhwellsBridge() {
11514
12251
  window.removeEventListener("message", handleRemoveSchedulingSection);
11515
12252
  window.removeEventListener("message", handleImageUrl);
11516
12253
  window.removeEventListener("message", handleImageUploading);
12254
+ window.removeEventListener("message", handleCarouselChange);
11517
12255
  window.removeEventListener("message", handleCanvasHeight);
11518
12256
  window.removeEventListener("message", handleParentScroll);
11519
12257
  window.removeEventListener("resize", handleViewportResize);
@@ -11528,7 +12266,7 @@ function OhhwellsBridge() {
11528
12266
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
11529
12267
  };
11530
12268
  }, [isEditMode, refreshStateRules]);
11531
- (0, import_react9.useEffect)(() => {
12269
+ (0, import_react10.useEffect)(() => {
11532
12270
  if (!isEditMode) return;
11533
12271
  const THRESHOLD = 10;
11534
12272
  const resolveWasSelected = (el) => {
@@ -11551,7 +12289,6 @@ function OhhwellsBridge() {
11551
12289
  const anchor = getNavigationItemAnchor(target);
11552
12290
  const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
11553
12291
  if (anchor && isFooterHrefKey(hrefKey)) {
11554
- armFooterPressDrag();
11555
12292
  footerPointerDragRef.current = {
11556
12293
  el: anchor,
11557
12294
  kind: "link",
@@ -11565,7 +12302,6 @@ function OhhwellsBridge() {
11565
12302
  }
11566
12303
  const col = target.closest("[data-ohw-footer-col]");
11567
12304
  if (col && !getNavigationItemAnchor(target)) {
11568
- armFooterPressDrag();
11569
12305
  footerPointerDragRef.current = {
11570
12306
  el: col,
11571
12307
  kind: "column",
@@ -11594,6 +12330,7 @@ function OhhwellsBridge() {
11594
12330
  if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
11595
12331
  e.preventDefault();
11596
12332
  pending.started = true;
12333
+ armFooterPressDrag();
11597
12334
  clearTextSelection();
11598
12335
  try {
11599
12336
  document.body.setPointerCapture(pending.pointerId);
@@ -11647,7 +12384,8 @@ function OhhwellsBridge() {
11647
12384
  }
11648
12385
  } catch {
11649
12386
  }
11650
- if (!pending?.started) {
12387
+ if (!pending) return;
12388
+ if (!pending.started) {
11651
12389
  unlockFooterDragInteraction();
11652
12390
  return;
11653
12391
  }
@@ -11682,7 +12420,7 @@ function OhhwellsBridge() {
11682
12420
  unlockFooterDragInteraction();
11683
12421
  };
11684
12422
  }, [isEditMode]);
11685
- (0, import_react9.useEffect)(() => {
12423
+ (0, import_react10.useEffect)(() => {
11686
12424
  const handler = (e) => {
11687
12425
  if (e.data?.type !== "ow:request-schedule-config") return;
11688
12426
  const insertAfterVal = e.data.insertAfter;
@@ -11698,7 +12436,7 @@ function OhhwellsBridge() {
11698
12436
  window.addEventListener("message", handler);
11699
12437
  return () => window.removeEventListener("message", handler);
11700
12438
  }, [processConfigRequest]);
11701
- (0, import_react9.useEffect)(() => {
12439
+ (0, import_react10.useEffect)(() => {
11702
12440
  if (!isEditMode) return;
11703
12441
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
11704
12442
  el.removeAttribute("data-ohw-active-state");
@@ -11733,19 +12471,49 @@ function OhhwellsBridge() {
11733
12471
  clearTimeout(timer);
11734
12472
  };
11735
12473
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
11736
- (0, import_react9.useEffect)(() => {
12474
+ (0, import_react10.useEffect)(() => {
11737
12475
  scrollToHashSectionWhenReady();
11738
12476
  const onHashChange = () => scrollToHashSectionWhenReady();
11739
12477
  window.addEventListener("hashchange", onHashChange);
11740
12478
  return () => window.removeEventListener("hashchange", onHashChange);
11741
12479
  }, [pathname]);
11742
- const handleCommand = (0, import_react9.useCallback)((cmd) => {
12480
+ const handleCommand = (0, import_react10.useCallback)((cmd) => {
12481
+ const el = activeElRef.current;
12482
+ const selBefore = window.getSelection();
12483
+ let savedOffsets = null;
12484
+ if (el && selBefore && selBefore.rangeCount > 0 && !selBefore.isCollapsed) {
12485
+ const r2 = selBefore.getRangeAt(0);
12486
+ if (el.contains(r2.commonAncestorContainer)) {
12487
+ savedOffsets = {
12488
+ start: textOffsetInRoot(el, r2.startContainer, r2.startOffset),
12489
+ end: textOffsetInRoot(el, r2.endContainer, r2.endOffset)
12490
+ };
12491
+ }
12492
+ }
12493
+ document.execCommand("styleWithCSS", false, true);
11743
12494
  document.execCommand(cmd, false);
11744
- activeElRef.current?.focus();
11745
- if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
12495
+ if (el && document.activeElement !== el) {
12496
+ el.focus();
12497
+ }
12498
+ const selAfter = window.getSelection();
12499
+ if (el && savedOffsets && selAfter?.isCollapsed) {
12500
+ const start = pointAtTextOffset(el, savedOffsets.start);
12501
+ const end = pointAtTextOffset(el, savedOffsets.end);
12502
+ if (start && end) {
12503
+ try {
12504
+ const range = document.createRange();
12505
+ range.setStart(start.node, start.offset);
12506
+ range.setEnd(end.node, end.offset);
12507
+ selAfter.removeAllRanges();
12508
+ selAfter.addRange(range);
12509
+ } catch {
12510
+ }
12511
+ }
12512
+ }
12513
+ if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
11746
12514
  refreshActiveCommandsRef.current();
11747
12515
  }, []);
11748
- const handleStateChange = (0, import_react9.useCallback)((state) => {
12516
+ const handleStateChange = (0, import_react10.useCallback)((state) => {
11749
12517
  if (!activeStateElRef.current) return;
11750
12518
  const el = activeStateElRef.current;
11751
12519
  if (state === "Default") {
@@ -11758,11 +12526,11 @@ function OhhwellsBridge() {
11758
12526
  }
11759
12527
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
11760
12528
  }, [deactivate]);
11761
- const closeLinkPopover = (0, import_react9.useCallback)(() => {
12529
+ const closeLinkPopover = (0, import_react10.useCallback)(() => {
11762
12530
  addNavAfterAnchorRef.current = null;
11763
12531
  setLinkPopover(null);
11764
12532
  }, []);
11765
- const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
12533
+ const openLinkPopoverForActive = (0, import_react10.useCallback)(() => {
11766
12534
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
11767
12535
  if (!hrefCtx) return;
11768
12536
  bumpLinkPopoverGrace();
@@ -11773,7 +12541,7 @@ function OhhwellsBridge() {
11773
12541
  });
11774
12542
  deactivate();
11775
12543
  }, [deactivate]);
11776
- const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
12544
+ const openLinkPopoverForSelected = (0, import_react10.useCallback)(() => {
11777
12545
  const anchor = selectedElRef.current;
11778
12546
  if (!anchor) return;
11779
12547
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -11786,7 +12554,7 @@ function OhhwellsBridge() {
11786
12554
  });
11787
12555
  deselect();
11788
12556
  }, [deselect]);
11789
- const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
12557
+ const handleLinkPopoverSubmit = (0, import_react10.useCallback)(
11790
12558
  (target) => {
11791
12559
  const session = linkPopoverSessionRef.current;
11792
12560
  if (!session) return;
@@ -11845,13 +12613,19 @@ function OhhwellsBridge() {
11845
12613
  const showEditLink = toolbarShowEditLink;
11846
12614
  const currentSections = sectionsByPath[pathname] ?? [];
11847
12615
  linkPopoverOpenRef.current = linkPopover !== null;
11848
- const handleMediaReplace = (0, import_react9.useCallback)(
12616
+ const handleMediaReplace = (0, import_react10.useCallback)(
11849
12617
  (key) => {
11850
12618
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
11851
12619
  },
11852
12620
  [postToParent2, mediaHover?.elementType]
11853
12621
  );
11854
- const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
12622
+ const handleEditCarousel = (0, import_react10.useCallback)(
12623
+ (key) => {
12624
+ postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
12625
+ },
12626
+ [postToParent2]
12627
+ );
12628
+ const handleMediaFadeOutComplete = (0, import_react10.useCallback)((key) => {
11855
12629
  setUploadingRects((prev) => {
11856
12630
  if (!(key in prev)) return prev;
11857
12631
  const next = { ...prev };
@@ -11859,7 +12633,7 @@ function OhhwellsBridge() {
11859
12633
  return next;
11860
12634
  });
11861
12635
  }, []);
11862
- const handleVideoSettingsChange = (0, import_react9.useCallback)(
12636
+ const handleVideoSettingsChange = (0, import_react10.useCallback)(
11863
12637
  (key, settings) => {
11864
12638
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11865
12639
  const video = getVideoEl(el);
@@ -11882,9 +12656,9 @@ function OhhwellsBridge() {
11882
12656
  [postToParent2]
11883
12657
  );
11884
12658
  return bridgeRoot ? (0, import_react_dom3.createPortal)(
11885
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11886
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
11887
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12659
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
12660
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
12661
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11888
12662
  MediaOverlay,
11889
12663
  {
11890
12664
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -11895,7 +12669,7 @@ function OhhwellsBridge() {
11895
12669
  },
11896
12670
  `uploading-${key}`
11897
12671
  )),
11898
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12672
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11899
12673
  MediaOverlay,
11900
12674
  {
11901
12675
  hover: mediaHover,
@@ -11904,10 +12678,11 @@ function OhhwellsBridge() {
11904
12678
  onVideoSettingsChange: handleVideoSettingsChange
11905
12679
  }
11906
12680
  ),
11907
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
11908
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
11909
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
11910
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12681
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
12682
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
12683
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
12684
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
12685
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11911
12686
  "div",
11912
12687
  {
11913
12688
  className: "pointer-events-none fixed z-2147483646",
@@ -11917,7 +12692,7 @@ function OhhwellsBridge() {
11917
12692
  width: slot.width,
11918
12693
  height: slot.height
11919
12694
  },
11920
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12695
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11921
12696
  DropIndicator,
11922
12697
  {
11923
12698
  direction: slot.direction,
@@ -11928,7 +12703,7 @@ function OhhwellsBridge() {
11928
12703
  },
11929
12704
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
11930
12705
  )),
11931
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12706
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11932
12707
  "div",
11933
12708
  {
11934
12709
  className: "pointer-events-none fixed z-2147483646",
@@ -11938,7 +12713,7 @@ function OhhwellsBridge() {
11938
12713
  width: slot.width,
11939
12714
  height: slot.height
11940
12715
  },
11941
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12716
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11942
12717
  DropIndicator,
11943
12718
  {
11944
12719
  direction: slot.direction,
@@ -11949,10 +12724,11 @@ function OhhwellsBridge() {
11949
12724
  },
11950
12725
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
11951
12726
  )),
11952
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
11953
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
11954
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
11955
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12727
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
12728
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
12729
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
12730
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(FooterContainerChrome, { rect: toolbarRect, onAdd: handleAddFooterColumn }),
12731
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11956
12732
  ItemInteractionLayer,
11957
12733
  {
11958
12734
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -11966,7 +12742,7 @@ function OhhwellsBridge() {
11966
12742
  onItemPointerDown: handleItemChromePointerDown,
11967
12743
  onItemClick: handleItemChromeClick,
11968
12744
  itemDragSurface: !isFooterFrameSelection,
11969
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12745
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11970
12746
  ItemActionToolbar,
11971
12747
  {
11972
12748
  onEditLink: openLinkPopoverForSelected,
@@ -11979,8 +12755,8 @@ function OhhwellsBridge() {
11979
12755
  ) : void 0
11980
12756
  }
11981
12757
  ),
11982
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
11983
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12758
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
12759
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11984
12760
  EditGlowChrome,
11985
12761
  {
11986
12762
  rect: toolbarRect,
@@ -11990,7 +12766,7 @@ function OhhwellsBridge() {
11990
12766
  hideHandle: isItemDragging
11991
12767
  }
11992
12768
  ),
11993
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12769
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11994
12770
  FloatingToolbar,
11995
12771
  {
11996
12772
  rect: toolbarRect,
@@ -12003,7 +12779,7 @@ function OhhwellsBridge() {
12003
12779
  }
12004
12780
  )
12005
12781
  ] }),
12006
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
12782
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
12007
12783
  "div",
12008
12784
  {
12009
12785
  "data-ohw-max-badge": "",
@@ -12029,7 +12805,7 @@ function OhhwellsBridge() {
12029
12805
  ]
12030
12806
  }
12031
12807
  ),
12032
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12808
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
12033
12809
  StateToggle,
12034
12810
  {
12035
12811
  rect: toggleState.rect,
@@ -12038,15 +12814,15 @@ function OhhwellsBridge() {
12038
12814
  onStateChange: handleStateChange
12039
12815
  }
12040
12816
  ),
12041
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
12817
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
12042
12818
  "div",
12043
12819
  {
12044
12820
  "data-ohw-section-insert-line": "",
12045
12821
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
12046
12822
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
12047
12823
  children: [
12048
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
12049
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12824
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
12825
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
12050
12826
  Badge,
12051
12827
  {
12052
12828
  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",
@@ -12063,11 +12839,11 @@ function OhhwellsBridge() {
12063
12839
  children: "Add Section"
12064
12840
  }
12065
12841
  ),
12066
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12842
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12067
12843
  ]
12068
12844
  }
12069
12845
  ),
12070
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12846
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
12071
12847
  LinkPopover,
12072
12848
  {
12073
12849
  panelRef: linkPopoverPanelRef,
@@ -12116,6 +12892,7 @@ function OhhwellsBridge() {
12116
12892
  isValidUrl,
12117
12893
  parseTarget,
12118
12894
  toggleVariants,
12895
+ useOhwCarousel,
12119
12896
  validateUrlInput
12120
12897
  });
12121
12898
  //# sourceMappingURL=index.cjs.map