@ohhwells/bridge 0.1.31 → 0.1.32-next.35

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
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
+ DragHandle: () => DragHandle,
34
35
  LinkEditorPanel: () => LinkEditorPanel,
35
36
  LinkPopover: () => LinkPopover,
36
37
  OhhwellsBridge: () => OhhwellsBridge,
@@ -41,6 +42,7 @@ __export(index_exports, {
41
42
  buildTarget: () => buildTarget,
42
43
  filterAvailablePages: () => filterAvailablePages,
43
44
  getEditModeInitialState: () => getEditModeInitialState,
45
+ isEditSessionActive: () => isEditSessionActive,
44
46
  isValidUrl: () => isValidUrl,
45
47
  parseTarget: () => parseTarget,
46
48
  toggleVariants: () => toggleVariants,
@@ -49,15 +51,54 @@ __export(index_exports, {
49
51
  module.exports = __toCommonJS(index_exports);
50
52
 
51
53
  // src/OhhwellsBridge.tsx
52
- var import_react5 = __toESM(require("react"), 1);
54
+ var import_react6 = __toESM(require("react"), 1);
53
55
  var import_client = require("react-dom/client");
54
56
  var import_react_dom = require("react-dom");
55
57
 
58
+ // src/linkHrefStore.ts
59
+ var linkHrefStore = /* @__PURE__ */ new Map();
60
+ function setStoredLinkHref(key, href) {
61
+ linkHrefStore.set(key, href);
62
+ }
63
+ function enforceLinkHrefs() {
64
+ document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
65
+ const key = el.getAttribute("data-ohw-href-key") ?? "";
66
+ if (!key) return;
67
+ const saved = linkHrefStore.get(key);
68
+ if (saved === void 0) return;
69
+ if (el.getAttribute("href") !== saved) el.setAttribute("href", saved);
70
+ });
71
+ }
72
+
73
+ // src/useLinkHrefGuardian.ts
74
+ var import_react = require("react");
75
+ function useLinkHrefGuardian(...deps) {
76
+ (0, import_react.useLayoutEffect)(() => {
77
+ let debounceTimer = null;
78
+ const scheduleEnforce = () => {
79
+ if (debounceTimer) clearTimeout(debounceTimer);
80
+ debounceTimer = setTimeout(enforceLinkHrefs, 50);
81
+ };
82
+ enforceLinkHrefs();
83
+ const observer = new MutationObserver(scheduleEnforce);
84
+ observer.observe(document.body, {
85
+ childList: true,
86
+ subtree: true,
87
+ attributes: true,
88
+ attributeFilter: ["href"]
89
+ });
90
+ return () => {
91
+ observer.disconnect();
92
+ if (debounceTimer) clearTimeout(debounceTimer);
93
+ };
94
+ }, deps);
95
+ }
96
+
56
97
  // src/ui/SchedulingWidget.tsx
57
- var import_react2 = require("react");
98
+ var import_react3 = require("react");
58
99
 
59
100
  // src/ui/EmailCaptureModal.tsx
60
- var import_react = require("react");
101
+ var import_react2 = require("react");
61
102
  var import_radix_ui = require("radix-ui");
62
103
  var import_jsx_runtime = require("react/jsx-runtime");
63
104
  function Spinner() {
@@ -86,11 +127,11 @@ function Spinner() {
86
127
  );
87
128
  }
88
129
  function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
89
- const [email, setEmail] = (0, import_react.useState)("");
90
- const [submittedEmail, setSubmittedEmail] = (0, import_react.useState)("");
91
- const [loading, setLoading] = (0, import_react.useState)(false);
92
- const [success, setSuccess] = (0, import_react.useState)(false);
93
- const [error, setError] = (0, import_react.useState)(null);
130
+ const [email, setEmail] = (0, import_react2.useState)("");
131
+ const [submittedEmail, setSubmittedEmail] = (0, import_react2.useState)("");
132
+ const [loading, setLoading] = (0, import_react2.useState)(false);
133
+ const [success, setSuccess] = (0, import_react2.useState)(false);
134
+ const [error, setError] = (0, import_react2.useState)(null);
94
135
  const isBook = title === "Confirm your spot";
95
136
  const handleSubmit = async () => {
96
137
  if (!email.trim()) return;
@@ -338,21 +379,21 @@ function LoadingSkeleton() {
338
379
  ] });
339
380
  }
340
381
  function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
341
- const todayMs = (0, import_react2.useMemo)(() => {
382
+ const todayMs = (0, import_react3.useMemo)(() => {
342
383
  const d = /* @__PURE__ */ new Date();
343
384
  d.setHours(0, 0, 0, 0);
344
385
  return d.getTime();
345
386
  }, []);
346
- const scrollRef = (0, import_react2.useRef)(null);
347
- const [canScrollLeft, setCanScrollLeft] = (0, import_react2.useState)(false);
348
- const [canScrollRight, setCanScrollRight] = (0, import_react2.useState)(false);
387
+ const scrollRef = (0, import_react3.useRef)(null);
388
+ const [canScrollLeft, setCanScrollLeft] = (0, import_react3.useState)(false);
389
+ const [canScrollRight, setCanScrollRight] = (0, import_react3.useState)(false);
349
390
  const updateScrollState = () => {
350
391
  const el = scrollRef.current;
351
392
  if (!el) return;
352
393
  setCanScrollLeft(el.scrollLeft > 0);
353
394
  setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
354
395
  };
355
- (0, import_react2.useEffect)(() => {
396
+ (0, import_react3.useEffect)(() => {
356
397
  const el = scrollRef.current;
357
398
  if (!el) return;
358
399
  updateScrollState();
@@ -367,9 +408,9 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
367
408
  const scroll = (dir) => {
368
409
  scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
369
410
  };
370
- const isDragging = (0, import_react2.useRef)(false);
371
- const dragStartX = (0, import_react2.useRef)(0);
372
- const dragStartScrollLeft = (0, import_react2.useRef)(0);
411
+ const isDragging = (0, import_react3.useRef)(false);
412
+ const dragStartX = (0, import_react3.useRef)(0);
413
+ const dragStartScrollLeft = (0, import_react3.useRef)(0);
373
414
  const onDragStart = (e) => {
374
415
  if (!scrollRef.current) return;
375
416
  isDragging.current = true;
@@ -389,14 +430,14 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
389
430
  scrollRef.current.style.cursor = "grab";
390
431
  scrollRef.current.style.userSelect = "";
391
432
  };
392
- const datesWithClasses = (0, import_react2.useMemo)(
433
+ const datesWithClasses = (0, import_react3.useMemo)(
393
434
  () => new Set(dates.map(
394
435
  (d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
395
436
  ).filter((i) => i >= 0)),
396
437
  [dates, schedule]
397
438
  );
398
439
  const selectedDate = dates[selectedIdx];
399
- const selectedClasses = (0, import_react2.useMemo)(
440
+ const selectedClasses = (0, import_react3.useMemo)(
400
441
  () => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
401
442
  [schedule, selectedDate]
402
443
  );
@@ -573,15 +614,15 @@ function CalendarFoldIcon() {
573
614
  );
574
615
  }
575
616
  function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter: insertAfterProp }) {
576
- const autoId = (0, import_react2.useId)();
617
+ const autoId = (0, import_react3.useId)();
577
618
  const insertAfter = insertAfterProp ?? autoId;
578
- const [schedule, setSchedule] = (0, import_react2.useState)(null);
579
- const [loading, setLoading] = (0, import_react2.useState)(true);
580
- const [inEditor, setInEditor] = (0, import_react2.useState)(false);
581
- const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
582
- const [modalState, setModalState] = (0, import_react2.useState)(null);
583
- const switchScheduleIdRef = (0, import_react2.useRef)(null);
584
- const dates = (0, import_react2.useMemo)(() => {
619
+ const [schedule, setSchedule] = (0, import_react3.useState)(null);
620
+ const [loading, setLoading] = (0, import_react3.useState)(true);
621
+ const [inEditor, setInEditor] = (0, import_react3.useState)(false);
622
+ const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
623
+ const [modalState, setModalState] = (0, import_react3.useState)(null);
624
+ const switchScheduleIdRef = (0, import_react3.useRef)(null);
625
+ const dates = (0, import_react3.useMemo)(() => {
585
626
  const today = /* @__PURE__ */ new Date();
586
627
  today.setHours(0, 0, 0, 0);
587
628
  return Array.from({ length: 14 }, (_, i) => {
@@ -590,8 +631,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
590
631
  return d;
591
632
  });
592
633
  }, []);
593
- const [selectedIdx, setSelectedIdx] = (0, import_react2.useState)(0);
594
- (0, import_react2.useEffect)(() => {
634
+ const [selectedIdx, setSelectedIdx] = (0, import_react3.useState)(0);
635
+ (0, import_react3.useEffect)(() => {
595
636
  if (!schedule?.classes) return;
596
637
  for (let i = 0; i < dates.length; i++) {
597
638
  if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
@@ -600,7 +641,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
600
641
  }
601
642
  }
602
643
  }, [schedule, dates]);
603
- (0, import_react2.useEffect)(() => {
644
+ (0, import_react3.useEffect)(() => {
604
645
  if (typeof window === "undefined") return;
605
646
  const isInEditor = window.self !== window.top;
606
647
  setInEditor(isInEditor);
@@ -4211,6 +4252,34 @@ function ToggleGroupItem({
4211
4252
  );
4212
4253
  }
4213
4254
 
4255
+ // src/ui/drag-handle.tsx
4256
+ var React3 = __toESM(require("react"), 1);
4257
+ var import_lucide_react = require("lucide-react");
4258
+ var import_jsx_runtime5 = require("react/jsx-runtime");
4259
+ var DragHandle = React3.forwardRef(
4260
+ ({ className, type = "button", ...props }, ref) => {
4261
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4262
+ "button",
4263
+ {
4264
+ ref,
4265
+ type,
4266
+ "data-slot": "drag-handle",
4267
+ className: cn(
4268
+ "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-[30px]",
4269
+ "bg-white border border-transparent text-stone-500 shadow-md cursor-grab",
4270
+ "enabled:hover:border enabled:hover:border-stone-200 enabled:hover:text-stone-950",
4271
+ "enabled:active:border enabled:active:border-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
4272
+ "disabled:cursor-not-allowed disabled:opacity-40 disabled:text-stone-950 disabled:pointer-events-none",
4273
+ className
4274
+ ),
4275
+ ...props,
4276
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.GripVertical, { className: "size-4 shrink-0", "aria-hidden": "true" })
4277
+ }
4278
+ );
4279
+ }
4280
+ );
4281
+ DragHandle.displayName = "DragHandle";
4282
+
4214
4283
  // src/OhhwellsBridge.tsx
4215
4284
  var import_react_dom2 = require("react-dom");
4216
4285
  var import_navigation = require("next/navigation");
@@ -4490,60 +4559,60 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
4490
4559
  }
4491
4560
 
4492
4561
  // src/ui/dialog.tsx
4493
- var React3 = __toESM(require("react"), 1);
4562
+ var React4 = __toESM(require("react"), 1);
4494
4563
  var import_radix_ui3 = require("radix-ui");
4495
4564
 
4496
4565
  // src/ui/icons.tsx
4497
- var import_jsx_runtime5 = require("react/jsx-runtime");
4566
+ var import_jsx_runtime6 = require("react/jsx-runtime");
4498
4567
  function IconX({ className, ...props }) {
4499
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4500
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M18 6 6 18" }),
4501
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m6 6 12 12" })
4568
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4569
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M18 6 6 18" }),
4570
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 6 12 12" })
4502
4571
  ] });
4503
4572
  }
4504
4573
  function IconChevronDown({ className, ...props }) {
4505
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m6 9 6 6 6-6" }) });
4574
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 9 6 6 6-6" }) });
4506
4575
  }
4507
4576
  function IconFile({ className, ...props }) {
4508
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4509
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4510
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4577
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4578
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4579
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4511
4580
  ] });
4512
4581
  }
4513
4582
  function IconInfo({ className, ...props }) {
4514
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4515
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
4516
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 16v-4" }),
4517
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 8h.01" })
4583
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4584
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
4585
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 16v-4" }),
4586
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 8h.01" })
4518
4587
  ] });
4519
4588
  }
4520
4589
  function IconArrowRight({ className, ...props }) {
4521
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4522
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M5 12h14" }),
4523
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m12 5 7 7-7 7" })
4590
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4591
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5 12h14" }),
4592
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m12 5 7 7-7 7" })
4524
4593
  ] });
4525
4594
  }
4526
4595
  function IconSection({ className, ...props }) {
4527
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4528
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4529
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4530
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4596
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4597
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4598
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4599
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4531
4600
  ] });
4532
4601
  }
4533
4602
 
4534
4603
  // src/ui/dialog.tsx
4535
- var import_jsx_runtime6 = require("react/jsx-runtime");
4604
+ var import_jsx_runtime7 = require("react/jsx-runtime");
4536
4605
  function Dialog2({ ...props }) {
4537
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui3.Dialog.Root, { "data-slot": "dialog", ...props });
4606
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Dialog.Root, { "data-slot": "dialog", ...props });
4538
4607
  }
4539
4608
  function DialogPortal({ ...props }) {
4540
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui3.Dialog.Portal, { ...props });
4609
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Dialog.Portal, { ...props });
4541
4610
  }
4542
4611
  function DialogOverlay({
4543
4612
  className,
4544
4613
  ...props
4545
4614
  }) {
4546
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4615
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4547
4616
  import_radix_ui3.Dialog.Overlay,
4548
4617
  {
4549
4618
  "data-slot": "dialog-overlay",
@@ -4553,11 +4622,11 @@ function DialogOverlay({
4553
4622
  }
4554
4623
  );
4555
4624
  }
4556
- var DialogContent = React3.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4625
+ var DialogContent = React4.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4557
4626
  const positionMode = container ? "absolute" : "fixed";
4558
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(DialogPortal, { container: container ?? void 0, children: [
4559
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4560
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4627
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(DialogPortal, { container: container ?? void 0, children: [
4628
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4629
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4561
4630
  import_radix_ui3.Dialog.Content,
4562
4631
  {
4563
4632
  ref,
@@ -4573,13 +4642,13 @@ var DialogContent = React3.forwardRef(({ className, children, showCloseButton =
4573
4642
  ...props,
4574
4643
  children: [
4575
4644
  children,
4576
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4645
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4577
4646
  import_radix_ui3.Dialog.Close,
4578
4647
  {
4579
4648
  type: "button",
4580
4649
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
4581
4650
  "aria-label": "Close",
4582
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconX, { "aria-hidden": true })
4651
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconX, { "aria-hidden": true })
4583
4652
  }
4584
4653
  ) : null
4585
4654
  ]
@@ -4589,12 +4658,12 @@ var DialogContent = React3.forwardRef(({ className, children, showCloseButton =
4589
4658
  });
4590
4659
  DialogContent.displayName = import_radix_ui3.Dialog.Content.displayName;
4591
4660
  function DialogHeader({ className, ...props }) {
4592
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4661
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4593
4662
  }
4594
4663
  function DialogFooter({ className, ...props }) {
4595
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
4664
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
4596
4665
  }
4597
- var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4666
+ var DialogTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4598
4667
  import_radix_ui3.Dialog.Title,
4599
4668
  {
4600
4669
  ref,
@@ -4603,7 +4672,7 @@ var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE
4603
4672
  }
4604
4673
  ));
4605
4674
  DialogTitle.displayName = import_radix_ui3.Dialog.Title.displayName;
4606
- var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4675
+ var DialogDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4607
4676
  import_radix_ui3.Dialog.Description,
4608
4677
  {
4609
4678
  ref,
@@ -4615,9 +4684,9 @@ DialogDescription.displayName = import_radix_ui3.Dialog.Description.displayName;
4615
4684
  var DialogClose = import_radix_ui3.Dialog.Close;
4616
4685
 
4617
4686
  // src/ui/button.tsx
4618
- var React4 = __toESM(require("react"), 1);
4687
+ var React5 = __toESM(require("react"), 1);
4619
4688
  var import_radix_ui4 = require("radix-ui");
4620
- var import_jsx_runtime7 = require("react/jsx-runtime");
4689
+ var import_jsx_runtime8 = require("react/jsx-runtime");
4621
4690
  var buttonVariants = cva(
4622
4691
  "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
4623
4692
  {
@@ -4638,10 +4707,10 @@ var buttonVariants = cva(
4638
4707
  }
4639
4708
  }
4640
4709
  );
4641
- var Button = React4.forwardRef(
4710
+ var Button = React5.forwardRef(
4642
4711
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4643
4712
  const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
4644
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4713
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4645
4714
  Comp,
4646
4715
  {
4647
4716
  ref,
@@ -4655,37 +4724,37 @@ var Button = React4.forwardRef(
4655
4724
  Button.displayName = "Button";
4656
4725
 
4657
4726
  // src/ui/link-modal/DestinationBreadcrumb.tsx
4658
- var import_jsx_runtime8 = require("react/jsx-runtime");
4727
+ var import_jsx_runtime9 = require("react/jsx-runtime");
4659
4728
  function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
4660
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
4661
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
4662
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3", children: [
4663
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
4664
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4665
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
4729
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
4730
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
4731
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-3", children: [
4732
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
4733
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4734
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
4666
4735
  ] }),
4667
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
4668
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
4669
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4670
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
4736
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
4737
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
4738
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4739
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
4671
4740
  ] })
4672
4741
  ] })
4673
4742
  ] });
4674
4743
  }
4675
4744
 
4676
4745
  // src/ui/link-modal/SectionTreeItem.tsx
4677
- var import_jsx_runtime9 = require("react/jsx-runtime");
4746
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4678
4747
  function SectionTreeItem({ section, onSelect, selected }) {
4679
4748
  const interactive = Boolean(onSelect);
4680
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
4681
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4749
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
4750
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4682
4751
  "div",
4683
4752
  {
4684
4753
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
4685
4754
  "aria-hidden": true
4686
4755
  }
4687
4756
  ),
4688
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4757
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4689
4758
  "div",
4690
4759
  {
4691
4760
  role: interactive ? "button" : void 0,
@@ -4703,8 +4772,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
4703
4772
  interactive && selected && "border-primary"
4704
4773
  ),
4705
4774
  children: [
4706
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4707
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
4775
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4776
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
4708
4777
  ]
4709
4778
  }
4710
4779
  )
@@ -4712,23 +4781,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
4712
4781
  }
4713
4782
  function SectionPickerList({ sections, onSelect }) {
4714
4783
  if (sections.length === 0) {
4715
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4784
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4716
4785
  }
4717
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-1", children: [
4718
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
4719
- sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SectionTreeItem, { section, onSelect }, section.id))
4786
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-1", children: [
4787
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
4788
+ sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SectionTreeItem, { section, onSelect }, section.id))
4720
4789
  ] });
4721
4790
  }
4722
4791
 
4723
4792
  // src/ui/link-modal/UrlOrPageInput.tsx
4724
- var import_react3 = require("react");
4793
+ var import_react4 = require("react");
4725
4794
 
4726
4795
  // src/ui/input.tsx
4727
- var React5 = __toESM(require("react"), 1);
4728
- var import_jsx_runtime10 = require("react/jsx-runtime");
4729
- var Input = React5.forwardRef(
4796
+ var React6 = __toESM(require("react"), 1);
4797
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4798
+ var Input = React6.forwardRef(
4730
4799
  ({ className, type, ...props }, ref) => {
4731
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4800
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4732
4801
  "input",
4733
4802
  {
4734
4803
  type,
@@ -4747,9 +4816,9 @@ Input.displayName = "Input";
4747
4816
 
4748
4817
  // src/ui/label.tsx
4749
4818
  var import_radix_ui5 = require("radix-ui");
4750
- var import_jsx_runtime11 = require("react/jsx-runtime");
4819
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4751
4820
  function Label({ className, ...props }) {
4752
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4821
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4753
4822
  import_radix_ui5.Label.Root,
4754
4823
  {
4755
4824
  "data-slot": "label",
@@ -4760,9 +4829,9 @@ function Label({ className, ...props }) {
4760
4829
  }
4761
4830
 
4762
4831
  // src/ui/link-modal/UrlOrPageInput.tsx
4763
- var import_jsx_runtime12 = require("react/jsx-runtime");
4832
+ var import_jsx_runtime13 = require("react/jsx-runtime");
4764
4833
  function FieldChevron({ onClick }) {
4765
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4834
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4766
4835
  "button",
4767
4836
  {
4768
4837
  type: "button",
@@ -4770,7 +4839,7 @@ function FieldChevron({ onClick }) {
4770
4839
  onClick,
4771
4840
  "aria-label": "Open page list",
4772
4841
  tabIndex: -1,
4773
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconChevronDown, {})
4842
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconChevronDown, {})
4774
4843
  }
4775
4844
  );
4776
4845
  }
@@ -4786,9 +4855,9 @@ function UrlOrPageInput({
4786
4855
  readOnly = false,
4787
4856
  urlError
4788
4857
  }) {
4789
- const inputId = (0, import_react3.useId)();
4790
- const inputRef = (0, import_react3.useRef)(null);
4791
- const [isFocused, setIsFocused] = (0, import_react3.useState)(false);
4858
+ const inputId = (0, import_react4.useId)();
4859
+ const inputRef = (0, import_react4.useRef)(null);
4860
+ const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
4792
4861
  const openDropdown = () => {
4793
4862
  if (readOnly || filteredPages.length === 0) return;
4794
4863
  onDropdownOpenChange(true);
@@ -4809,12 +4878,12 @@ function UrlOrPageInput({
4809
4878
  "data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
4810
4879
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
4811
4880
  );
4812
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
4813
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4814
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative w-full", children: [
4815
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
4816
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
4817
- readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4881
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
4882
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4883
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative w-full", children: [
4884
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
4885
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
4886
+ readOnly ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4818
4887
  Input,
4819
4888
  {
4820
4889
  ref: inputRef,
@@ -4833,7 +4902,7 @@ function UrlOrPageInput({
4833
4902
  )
4834
4903
  }
4835
4904
  ),
4836
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4905
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4837
4906
  "button",
4838
4907
  {
4839
4908
  type: "button",
@@ -4841,26 +4910,26 @@ function UrlOrPageInput({
4841
4910
  onMouseDown: clearSelection,
4842
4911
  "aria-label": "Clear selected page",
4843
4912
  tabIndex: -1,
4844
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconX, { "aria-hidden": true })
4913
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
4845
4914
  }
4846
4915
  ) : null,
4847
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
4916
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
4848
4917
  ] }),
4849
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4918
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4850
4919
  "div",
4851
4920
  {
4852
4921
  "data-ohw-link-page-dropdown": "",
4853
4922
  className: "absolute left-0 right-0 h-20 top-[calc(100%+4px)] z-10 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
4854
4923
  onMouseDown: (e) => e.preventDefault(),
4855
- children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4924
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
4856
4925
  "button",
4857
4926
  {
4858
4927
  type: "button",
4859
4928
  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",
4860
4929
  onClick: () => onPageSelect(page),
4861
4930
  children: [
4862
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
4863
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate", children: page.title })
4931
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
4932
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate", children: page.title })
4864
4933
  ]
4865
4934
  },
4866
4935
  page.path
@@ -4868,12 +4937,12 @@ function UrlOrPageInput({
4868
4937
  }
4869
4938
  ) : null
4870
4939
  ] }),
4871
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4940
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4872
4941
  ] });
4873
4942
  }
4874
4943
 
4875
4944
  // src/ui/link-modal/useLinkModalState.ts
4876
- var import_react4 = require("react");
4945
+ var import_react5 = require("react");
4877
4946
  function useLinkModalState({
4878
4947
  open,
4879
4948
  mode,
@@ -4885,17 +4954,17 @@ function useLinkModalState({
4885
4954
  onClose,
4886
4955
  onSubmit
4887
4956
  }) {
4888
- const availablePages = (0, import_react4.useMemo)(
4957
+ const availablePages = (0, import_react5.useMemo)(
4889
4958
  () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
4890
4959
  [mode, pages, existingTargets]
4891
4960
  );
4892
- const [searchValue, setSearchValue] = (0, import_react4.useState)("");
4893
- const [selectedPage, setSelectedPage] = (0, import_react4.useState)(null);
4894
- const [selectedSection, setSelectedSection] = (0, import_react4.useState)(null);
4895
- const [step, setStep] = (0, import_react4.useState)("input");
4896
- const [dropdownOpen, setDropdownOpen] = (0, import_react4.useState)(false);
4897
- const [urlError, setUrlError] = (0, import_react4.useState)("");
4898
- const reset = (0, import_react4.useCallback)(() => {
4961
+ const [searchValue, setSearchValue] = (0, import_react5.useState)("");
4962
+ const [selectedPage, setSelectedPage] = (0, import_react5.useState)(null);
4963
+ const [selectedSection, setSelectedSection] = (0, import_react5.useState)(null);
4964
+ const [step, setStep] = (0, import_react5.useState)("input");
4965
+ const [dropdownOpen, setDropdownOpen] = (0, import_react5.useState)(false);
4966
+ const [urlError, setUrlError] = (0, import_react5.useState)("");
4967
+ const reset = (0, import_react5.useCallback)(() => {
4899
4968
  setSearchValue("");
4900
4969
  setSelectedPage(null);
4901
4970
  setSelectedSection(null);
@@ -4903,7 +4972,7 @@ function useLinkModalState({
4903
4972
  setDropdownOpen(false);
4904
4973
  setUrlError("");
4905
4974
  }, []);
4906
- (0, import_react4.useEffect)(() => {
4975
+ (0, import_react5.useEffect)(() => {
4907
4976
  if (!open) return;
4908
4977
  if (mode === "edit" && initialTarget) {
4909
4978
  const { pageRoute } = parseTarget(initialTarget);
@@ -4919,11 +4988,11 @@ function useLinkModalState({
4919
4988
  setDropdownOpen(false);
4920
4989
  setUrlError("");
4921
4990
  }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
4922
- const filteredPages = (0, import_react4.useMemo)(() => {
4991
+ const filteredPages = (0, import_react5.useMemo)(() => {
4923
4992
  if (!searchValue.trim()) return availablePages;
4924
4993
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
4925
4994
  }, [availablePages, searchValue]);
4926
- const activeSections = (0, import_react4.useMemo)(() => {
4995
+ const activeSections = (0, import_react5.useMemo)(() => {
4927
4996
  if (!selectedPage) return [];
4928
4997
  return getSectionsForPath(sectionsByPath, selectedPage.path);
4929
4998
  }, [selectedPage, sectionsByPath]);
@@ -4969,7 +5038,7 @@ function useLinkModalState({
4969
5038
  reset();
4970
5039
  onClose();
4971
5040
  };
4972
- const isValid = (0, import_react4.useMemo)(() => {
5041
+ const isValid = (0, import_react5.useMemo)(() => {
4973
5042
  if (urlError) return false;
4974
5043
  if (showBreadcrumb) return true;
4975
5044
  if (selectedPage) return true;
@@ -5036,7 +5105,7 @@ function useLinkModalState({
5036
5105
  }
5037
5106
 
5038
5107
  // src/ui/link-modal/LinkEditorPanel.tsx
5039
- var import_jsx_runtime13 = require("react/jsx-runtime");
5108
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5040
5109
  function LinkEditorPanel({
5041
5110
  open = true,
5042
5111
  mode = "create",
@@ -5060,25 +5129,25 @@ function LinkEditorPanel({
5060
5129
  onSubmit
5061
5130
  });
5062
5131
  const isCancel = state.secondaryLabel === "Cancel";
5063
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
5064
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5132
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
5133
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5065
5134
  "button",
5066
5135
  {
5067
5136
  type: "button",
5068
5137
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5069
5138
  "aria-label": "Close",
5070
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
5139
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconX, { "aria-hidden": true })
5071
5140
  }
5072
5141
  ) }),
5073
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5074
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5075
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5142
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5143
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5144
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5076
5145
  DestinationBreadcrumb,
5077
5146
  {
5078
5147
  pageTitle: state.selectedPage.title,
5079
5148
  sectionLabel: state.selectedSection.label
5080
5149
  }
5081
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5150
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5082
5151
  UrlOrPageInput,
5083
5152
  {
5084
5153
  value: state.searchValue,
@@ -5091,18 +5160,18 @@ function LinkEditorPanel({
5091
5160
  urlError: state.urlError
5092
5161
  }
5093
5162
  ),
5094
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5095
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5096
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5097
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5098
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Pick a section this link should scroll to." })
5163
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5164
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5165
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5166
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5167
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Pick a section this link should scroll to." })
5099
5168
  ] })
5100
5169
  ] }) : null,
5101
- state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5102
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5170
+ state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5171
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5103
5172
  ] }),
5104
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5105
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5173
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5174
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5106
5175
  Button,
5107
5176
  {
5108
5177
  type: "button",
@@ -5117,7 +5186,7 @@ function LinkEditorPanel({
5117
5186
  children: state.secondaryLabel
5118
5187
  }
5119
5188
  ),
5120
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5189
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5121
5190
  Button,
5122
5191
  {
5123
5192
  type: "button",
@@ -5136,7 +5205,7 @@ function LinkEditorPanel({
5136
5205
  }
5137
5206
 
5138
5207
  // src/ui/link-modal/LinkPopover.tsx
5139
- var import_jsx_runtime14 = require("react/jsx-runtime");
5208
+ var import_jsx_runtime15 = require("react/jsx-runtime");
5140
5209
  function LinkPopover({
5141
5210
  open = true,
5142
5211
  panelRef,
@@ -5144,14 +5213,14 @@ function LinkPopover({
5144
5213
  onClose,
5145
5214
  ...editorProps
5146
5215
  }) {
5147
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5216
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5148
5217
  Dialog2,
5149
5218
  {
5150
5219
  open,
5151
5220
  onOpenChange: (next) => {
5152
5221
  if (!next) onClose?.();
5153
5222
  },
5154
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5223
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5155
5224
  DialogContent,
5156
5225
  {
5157
5226
  ref: panelRef,
@@ -5161,7 +5230,7 @@ function LinkPopover({
5161
5230
  "data-ohw-bridge": "",
5162
5231
  showCloseButton: false,
5163
5232
  className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5164
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5233
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5165
5234
  }
5166
5235
  )
5167
5236
  }
@@ -5227,7 +5296,7 @@ function shouldUseDevFixtures() {
5227
5296
  }
5228
5297
 
5229
5298
  // src/ui/badge.tsx
5230
- var import_jsx_runtime15 = require("react/jsx-runtime");
5299
+ var import_jsx_runtime16 = require("react/jsx-runtime");
5231
5300
  var badgeVariants = cva(
5232
5301
  "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",
5233
5302
  {
@@ -5245,11 +5314,11 @@ var badgeVariants = cva(
5245
5314
  }
5246
5315
  );
5247
5316
  function Badge({ className, variant, ...props }) {
5248
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5317
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5249
5318
  }
5250
5319
 
5251
5320
  // src/OhhwellsBridge.tsx
5252
- var import_jsx_runtime16 = require("react/jsx-runtime");
5321
+ var import_jsx_runtime17 = require("react/jsx-runtime");
5253
5322
  var PRIMARY = "#0885FE";
5254
5323
  var IMAGE_FADE_MS = 300;
5255
5324
  function runOpacityFade(el, onDone) {
@@ -5423,7 +5492,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
5423
5492
  const root = (0, import_client.createRoot)(container);
5424
5493
  (0, import_react_dom.flushSync)(() => {
5425
5494
  root.render(
5426
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5495
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5427
5496
  SchedulingWidget,
5428
5497
  {
5429
5498
  notifyOnConnect,
@@ -5471,6 +5540,20 @@ function applyLinkHref(el, val) {
5471
5540
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
5472
5541
  if (anchor) anchor.setAttribute("href", val);
5473
5542
  }
5543
+ function getEditMeasureEl(editable) {
5544
+ return editable.closest("[data-ohw-href-key]") ?? editable;
5545
+ }
5546
+ function isDragHandleDisabled(el) {
5547
+ const carriers = [
5548
+ el.closest("[data-ohw-href-key]"),
5549
+ el.closest("[data-ohw-editable]")
5550
+ ];
5551
+ return carriers.some((carrier) => {
5552
+ if (!carrier) return false;
5553
+ const raw = carrier.getAttribute("data-ohw-drag-disabled");
5554
+ return raw === "true" || raw === "";
5555
+ });
5556
+ }
5474
5557
  function collectEditableNodes() {
5475
5558
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
5476
5559
  if (el.dataset.ohwEditable === "image") {
@@ -5502,10 +5585,11 @@ function applyLinkByKey(key, val) {
5502
5585
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
5503
5586
  if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
5504
5587
  });
5505
- document.querySelectorAll(`[data-ohw-href-key="${key}"]`).forEach((el) => {
5506
- applyLinkHref(el, val);
5507
- });
5508
- window.dispatchEvent(new CustomEvent("ohw:link-href", { detail: { key, href: val } }));
5588
+ const hrefAnchors = document.querySelectorAll(`[data-ohw-href-key="${key}"]`);
5589
+ if (hrefAnchors.length > 0) {
5590
+ setStoredLinkHref(key, val);
5591
+ hrefAnchors.forEach((el) => applyLinkHref(el, val));
5592
+ }
5509
5593
  }
5510
5594
  function isInsideLinkEditor(target) {
5511
5595
  return Boolean(
@@ -5523,6 +5607,14 @@ function getHrefKeyFromElement(el) {
5523
5607
  function isNavbarButton(el) {
5524
5608
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
5525
5609
  }
5610
+ function getReorderHandleState(el) {
5611
+ const linkEl = el.closest("[data-ohw-href-key]");
5612
+ if (!linkEl || isNavbarButton(el)) return { key: null, disabled: false };
5613
+ return {
5614
+ key: linkEl.dataset.ohwHrefKey ?? null,
5615
+ disabled: isDragHandleDisabled(el)
5616
+ };
5617
+ }
5526
5618
  function clearHrefKeyHover(anchor) {
5527
5619
  anchor.removeAttribute("data-ohw-hovered");
5528
5620
  anchor.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
@@ -5652,6 +5744,8 @@ var ICONS = {
5652
5744
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
5653
5745
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
5654
5746
  };
5747
+ var TOOLBAR_PILL_PADDING = 2;
5748
+ var TOOLBAR_TOGGLE_GAP = 4;
5655
5749
  var TOOLBAR_GROUPS = [
5656
5750
  [
5657
5751
  { cmd: "bold", title: "Bold" },
@@ -5669,24 +5763,64 @@ var TOOLBAR_GROUPS = [
5669
5763
  { cmd: "insertOrderedList", title: "Numbered List" }
5670
5764
  ]
5671
5765
  ];
5672
- function GlowFrame({ rect, elRef }) {
5766
+ function EditGlowChrome({
5767
+ rect,
5768
+ elRef,
5769
+ reorderHrefKey,
5770
+ dragDisabled = false
5771
+ }) {
5673
5772
  const GAP = 6;
5674
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5773
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5675
5774
  "div",
5676
5775
  {
5677
5776
  ref: elRef,
5777
+ "data-ohw-edit-chrome": "",
5678
5778
  style: {
5679
5779
  position: "fixed",
5680
5780
  top: rect.top - GAP,
5681
5781
  left: rect.left - GAP,
5682
5782
  width: rect.width + GAP * 2,
5683
5783
  height: rect.height + GAP * 2,
5684
- border: "2px solid #0885FE",
5685
- borderRadius: 8,
5686
- boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
5687
5784
  pointerEvents: "none",
5688
5785
  zIndex: 2147483646
5689
- }
5786
+ },
5787
+ children: [
5788
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5789
+ "div",
5790
+ {
5791
+ style: {
5792
+ position: "absolute",
5793
+ inset: 0,
5794
+ border: "2px solid #0885FE",
5795
+ borderRadius: 8,
5796
+ boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
5797
+ pointerEvents: "none"
5798
+ }
5799
+ }
5800
+ ),
5801
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5802
+ "div",
5803
+ {
5804
+ "data-ohw-drag-handle-container": "",
5805
+ "data-ohw-drag-disabled": dragDisabled ? "" : void 0,
5806
+ style: {
5807
+ position: "absolute",
5808
+ left: 0,
5809
+ top: "50%",
5810
+ transform: "translate(calc(-100% - 7px), -50%)",
5811
+ pointerEvents: dragDisabled ? "none" : "auto"
5812
+ },
5813
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5814
+ DragHandle,
5815
+ {
5816
+ "aria-label": `Reorder ${reorderHrefKey}`,
5817
+ disabled: dragDisabled,
5818
+ "aria-disabled": dragDisabled
5819
+ }
5820
+ )
5821
+ }
5822
+ )
5823
+ ]
5690
5824
  }
5691
5825
  );
5692
5826
  }
@@ -5730,9 +5864,9 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
5730
5864
  const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
5731
5865
  return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
5732
5866
  }
5733
- function calcToolbarPos(rect, parentScroll, approxW = 330) {
5867
+ function calcToolbarPos(rect, parentScroll, approxW = 306) {
5734
5868
  const GAP = 8;
5735
- const APPROX_H = 36;
5869
+ const APPROX_H = 32;
5736
5870
  const APPROX_W = approxW;
5737
5871
  const clip = getIframeVisibleClip(parentScroll);
5738
5872
  const clipTop = clip?.top ?? 0;
@@ -5766,9 +5900,9 @@ function calcToolbarPos(rect, parentScroll, approxW = 330) {
5766
5900
  const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
5767
5901
  return { top, left, transform };
5768
5902
  }
5769
- var EDIT_LINK_ICON = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
5770
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5771
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5903
+ var EDIT_LINK_ICON = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
5904
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5905
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5772
5906
  ] });
5773
5907
  function FloatingToolbar({
5774
5908
  rect,
@@ -5780,10 +5914,10 @@ function FloatingToolbar({
5780
5914
  showEditLink,
5781
5915
  onEditLink
5782
5916
  }) {
5783
- const approxW = variant === "link-action" ? 120 : 330;
5917
+ const approxW = variant === "link-action" ? 112 : 306;
5784
5918
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, approxW);
5785
5919
  if (variant === "link-action") {
5786
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
5920
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5787
5921
  "div",
5788
5922
  {
5789
5923
  ref: elRef,
@@ -5800,15 +5934,15 @@ function FloatingToolbar({
5800
5934
  boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
5801
5935
  display: "flex",
5802
5936
  alignItems: "center",
5803
- padding: 4,
5804
- gap: 6,
5937
+ padding: TOOLBAR_PILL_PADDING,
5938
+ gap: TOOLBAR_TOGGLE_GAP,
5805
5939
  fontFamily: "sans-serif",
5806
5940
  pointerEvents: "auto",
5807
5941
  whiteSpace: "nowrap"
5808
5942
  },
5809
5943
  onMouseDown: (e) => e.stopPropagation(),
5810
5944
  children: [
5811
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5945
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5812
5946
  "button",
5813
5947
  {
5814
5948
  type: "button",
@@ -5843,8 +5977,8 @@ function FloatingToolbar({
5843
5977
  children: EDIT_LINK_ICON
5844
5978
  }
5845
5979
  ),
5846
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5847
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5980
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5981
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5848
5982
  "button",
5849
5983
  {
5850
5984
  type: "button",
@@ -5863,10 +5997,10 @@ function FloatingToolbar({
5863
5997
  padding: 6,
5864
5998
  opacity: 0.6
5865
5999
  },
5866
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: [
5867
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "5", cy: "12", r: "1.5" }),
5868
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "12", cy: "12", r: "1.5" }),
5869
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "19", cy: "12", r: "1.5" })
6000
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: [
6001
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "5", cy: "12", r: "1.5" }),
6002
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "12", cy: "12", r: "1.5" }),
6003
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "19", cy: "12", r: "1.5" })
5870
6004
  ] })
5871
6005
  }
5872
6006
  )
@@ -5874,7 +6008,7 @@ function FloatingToolbar({
5874
6008
  }
5875
6009
  );
5876
6010
  }
5877
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
6011
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5878
6012
  "div",
5879
6013
  {
5880
6014
  ref: elRef,
@@ -5891,19 +6025,19 @@ function FloatingToolbar({
5891
6025
  boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
5892
6026
  display: "flex",
5893
6027
  alignItems: "center",
5894
- padding: 4,
5895
- gap: 6,
6028
+ padding: TOOLBAR_PILL_PADDING,
6029
+ gap: TOOLBAR_TOGGLE_GAP,
5896
6030
  fontFamily: "sans-serif",
5897
6031
  pointerEvents: "auto",
5898
6032
  whiteSpace: "nowrap"
5899
6033
  },
5900
6034
  onMouseDown: (e) => e.stopPropagation(),
5901
6035
  children: [
5902
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react5.default.Fragment, { children: [
5903
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
6036
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react6.default.Fragment, { children: [
6037
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5904
6038
  btns.map((btn) => {
5905
6039
  const isActive = activeCommands.has(btn.cmd);
5906
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6040
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5907
6041
  "button",
5908
6042
  {
5909
6043
  title: btn.title,
@@ -5929,7 +6063,7 @@ function FloatingToolbar({
5929
6063
  flexShrink: 0,
5930
6064
  padding: 6
5931
6065
  },
5932
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6066
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5933
6067
  "svg",
5934
6068
  {
5935
6069
  width: "16",
@@ -5949,7 +6083,7 @@ function FloatingToolbar({
5949
6083
  );
5950
6084
  })
5951
6085
  ] }, gi)),
5952
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6086
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5953
6087
  "button",
5954
6088
  {
5955
6089
  type: "button",
@@ -5998,7 +6132,7 @@ function StateToggle({
5998
6132
  states,
5999
6133
  onStateChange
6000
6134
  }) {
6001
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6135
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6002
6136
  ToggleGroup,
6003
6137
  {
6004
6138
  "data-ohw-state-toggle": "",
@@ -6012,7 +6146,7 @@ function StateToggle({
6012
6146
  left: rect.right - 8,
6013
6147
  transform: "translateX(-100%)"
6014
6148
  },
6015
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6149
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6016
6150
  }
6017
6151
  );
6018
6152
  }
@@ -6039,8 +6173,8 @@ function OhhwellsBridge() {
6039
6173
  const router = (0, import_navigation.useRouter)();
6040
6174
  const searchParams = (0, import_navigation.useSearchParams)();
6041
6175
  const isEditMode = isEditSessionActive();
6042
- const [bridgeRoot, setBridgeRoot] = (0, import_react5.useState)(null);
6043
- (0, import_react5.useEffect)(() => {
6176
+ const [bridgeRoot, setBridgeRoot] = (0, import_react6.useState)(null);
6177
+ (0, import_react6.useEffect)(() => {
6044
6178
  const figtreeFontId = "ohw-figtree-font";
6045
6179
  if (!document.getElementById(figtreeFontId)) {
6046
6180
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -6064,69 +6198,72 @@ function OhhwellsBridge() {
6064
6198
  }, []);
6065
6199
  const subdomainFromQuery = searchParams.get("subdomain");
6066
6200
  const subdomain = resolveSubdomain(subdomainFromQuery);
6067
- const postToParent = (0, import_react5.useCallback)((data) => {
6201
+ useLinkHrefGuardian(pathname, subdomain, isEditMode);
6202
+ const postToParent = (0, import_react6.useCallback)((data) => {
6068
6203
  if (typeof window !== "undefined" && window.parent !== window) {
6069
6204
  window.parent.postMessage(data, "*");
6070
6205
  }
6071
6206
  }, []);
6072
- const [fetchState, setFetchState] = (0, import_react5.useState)("idle");
6073
- const autoSaveTimers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
6074
- const activeElRef = (0, import_react5.useRef)(null);
6075
- const selectedElRef = (0, import_react5.useRef)(null);
6076
- const originalContentRef = (0, import_react5.useRef)(null);
6077
- const activeStateElRef = (0, import_react5.useRef)(null);
6078
- const parentScrollRef = (0, import_react5.useRef)(null);
6079
- const visibleViewportRef = (0, import_react5.useRef)(null);
6080
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react5.useState)(null);
6081
- const attachVisibleViewport = (0, import_react5.useCallback)((node) => {
6207
+ const [fetchState, setFetchState] = (0, import_react6.useState)("idle");
6208
+ const autoSaveTimers = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
6209
+ const activeElRef = (0, import_react6.useRef)(null);
6210
+ const selectedElRef = (0, import_react6.useRef)(null);
6211
+ const originalContentRef = (0, import_react6.useRef)(null);
6212
+ const activeStateElRef = (0, import_react6.useRef)(null);
6213
+ const parentScrollRef = (0, import_react6.useRef)(null);
6214
+ const visibleViewportRef = (0, import_react6.useRef)(null);
6215
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react6.useState)(null);
6216
+ const attachVisibleViewport = (0, import_react6.useCallback)((node) => {
6082
6217
  visibleViewportRef.current = node;
6083
6218
  setDialogPortalContainer(node);
6084
6219
  if (node) applyVisibleViewport(node, parentScrollRef.current);
6085
6220
  }, []);
6086
- const toolbarElRef = (0, import_react5.useRef)(null);
6087
- const glowElRef = (0, import_react5.useRef)(null);
6088
- const hoveredImageRef = (0, import_react5.useRef)(null);
6089
- const hoveredImageHasTextOverlapRef = (0, import_react5.useRef)(false);
6090
- const hoveredGapRef = (0, import_react5.useRef)(null);
6091
- const imageUnhoverTimerRef = (0, import_react5.useRef)(null);
6092
- const imageShowTimerRef = (0, import_react5.useRef)(null);
6093
- const editStylesRef = (0, import_react5.useRef)(null);
6094
- const activateRef = (0, import_react5.useRef)(() => {
6221
+ const toolbarElRef = (0, import_react6.useRef)(null);
6222
+ const glowElRef = (0, import_react6.useRef)(null);
6223
+ const hoveredImageRef = (0, import_react6.useRef)(null);
6224
+ const hoveredImageHasTextOverlapRef = (0, import_react6.useRef)(false);
6225
+ const hoveredGapRef = (0, import_react6.useRef)(null);
6226
+ const imageUnhoverTimerRef = (0, import_react6.useRef)(null);
6227
+ const imageShowTimerRef = (0, import_react6.useRef)(null);
6228
+ const editStylesRef = (0, import_react6.useRef)(null);
6229
+ const activateRef = (0, import_react6.useRef)(() => {
6095
6230
  });
6096
- const deactivateRef = (0, import_react5.useRef)(() => {
6231
+ const deactivateRef = (0, import_react6.useRef)(() => {
6097
6232
  });
6098
- const selectRef = (0, import_react5.useRef)(() => {
6233
+ const selectRef = (0, import_react6.useRef)(() => {
6099
6234
  });
6100
- const deselectRef = (0, import_react5.useRef)(() => {
6235
+ const deselectRef = (0, import_react6.useRef)(() => {
6101
6236
  });
6102
- const refreshActiveCommandsRef = (0, import_react5.useRef)(() => {
6237
+ const refreshActiveCommandsRef = (0, import_react6.useRef)(() => {
6103
6238
  });
6104
- const postToParentRef = (0, import_react5.useRef)(postToParent);
6239
+ const postToParentRef = (0, import_react6.useRef)(postToParent);
6105
6240
  postToParentRef.current = postToParent;
6106
- const sectionsLoadedRef = (0, import_react5.useRef)(false);
6107
- const pendingScheduleConfigRequests = (0, import_react5.useRef)([]);
6108
- const [toolbarRect, setToolbarRect] = (0, import_react5.useState)(null);
6109
- const [toolbarVariant, setToolbarVariant] = (0, import_react5.useState)("none");
6110
- const toolbarVariantRef = (0, import_react5.useRef)("none");
6241
+ const sectionsLoadedRef = (0, import_react6.useRef)(false);
6242
+ const pendingScheduleConfigRequests = (0, import_react6.useRef)([]);
6243
+ const [toolbarRect, setToolbarRect] = (0, import_react6.useState)(null);
6244
+ const [toolbarVariant, setToolbarVariant] = (0, import_react6.useState)("none");
6245
+ const toolbarVariantRef = (0, import_react6.useRef)("none");
6111
6246
  toolbarVariantRef.current = toolbarVariant;
6112
- const [toggleState, setToggleState] = (0, import_react5.useState)(null);
6113
- const [maxBadge, setMaxBadge] = (0, import_react5.useState)(null);
6114
- const [activeCommands, setActiveCommands] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
6115
- const [sectionGap, setSectionGap] = (0, import_react5.useState)(null);
6116
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react5.useState)(false);
6117
- const [linkPopover, setLinkPopover] = (0, import_react5.useState)(null);
6118
- const [sitePages, setSitePages] = (0, import_react5.useState)([]);
6119
- const [sectionsByPath, setSectionsByPath] = (0, import_react5.useState)({});
6120
- const sectionsPrefetchGenRef = (0, import_react5.useRef)(0);
6121
- const setLinkPopoverRef = (0, import_react5.useRef)(setLinkPopover);
6122
- const linkPopoverPanelRef = (0, import_react5.useRef)(null);
6123
- const linkPopoverOpenRef = (0, import_react5.useRef)(false);
6124
- const linkPopoverGraceUntilRef = (0, import_react5.useRef)(0);
6247
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react6.useState)(null);
6248
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react6.useState)(false);
6249
+ const [toggleState, setToggleState] = (0, import_react6.useState)(null);
6250
+ const [maxBadge, setMaxBadge] = (0, import_react6.useState)(null);
6251
+ const [activeCommands, setActiveCommands] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
6252
+ const [sectionGap, setSectionGap] = (0, import_react6.useState)(null);
6253
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react6.useState)(false);
6254
+ const [linkPopover, setLinkPopover] = (0, import_react6.useState)(null);
6255
+ const [sitePages, setSitePages] = (0, import_react6.useState)([]);
6256
+ const [sectionsByPath, setSectionsByPath] = (0, import_react6.useState)({});
6257
+ const sectionsPrefetchGenRef = (0, import_react6.useRef)(0);
6258
+ const setLinkPopoverRef = (0, import_react6.useRef)(setLinkPopover);
6259
+ const linkPopoverPanelRef = (0, import_react6.useRef)(null);
6260
+ const linkPopoverOpenRef = (0, import_react6.useRef)(false);
6261
+ const linkPopoverGraceUntilRef = (0, import_react6.useRef)(0);
6125
6262
  setLinkPopoverRef.current = setLinkPopover;
6126
6263
  const bumpLinkPopoverGrace = () => {
6127
6264
  linkPopoverGraceUntilRef.current = Date.now() + 350;
6128
6265
  };
6129
- const runSectionsPrefetch = (0, import_react5.useCallback)((pages) => {
6266
+ const runSectionsPrefetch = (0, import_react6.useCallback)((pages) => {
6130
6267
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
6131
6268
  const gen = ++sectionsPrefetchGenRef.current;
6132
6269
  const paths = pages.map((p) => p.path);
@@ -6145,9 +6282,9 @@ function OhhwellsBridge() {
6145
6282
  );
6146
6283
  });
6147
6284
  }, [isEditMode, pathname]);
6148
- const runSectionsPrefetchRef = (0, import_react5.useRef)(runSectionsPrefetch);
6285
+ const runSectionsPrefetchRef = (0, import_react6.useRef)(runSectionsPrefetch);
6149
6286
  runSectionsPrefetchRef.current = runSectionsPrefetch;
6150
- (0, import_react5.useEffect)(() => {
6287
+ (0, import_react6.useEffect)(() => {
6151
6288
  if (!linkPopover) return;
6152
6289
  if (hoveredImageRef.current) {
6153
6290
  hoveredImageRef.current = null;
@@ -6181,7 +6318,7 @@ function OhhwellsBridge() {
6181
6318
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6182
6319
  };
6183
6320
  }, [linkPopover, postToParent]);
6184
- (0, import_react5.useEffect)(() => {
6321
+ (0, import_react6.useEffect)(() => {
6185
6322
  if (!isEditMode) return;
6186
6323
  const useFixtures = shouldUseDevFixtures();
6187
6324
  if (useFixtures) {
@@ -6205,17 +6342,17 @@ function OhhwellsBridge() {
6205
6342
  if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
6206
6343
  return () => window.removeEventListener("message", onSitePages);
6207
6344
  }, [isEditMode, postToParent]);
6208
- (0, import_react5.useEffect)(() => {
6345
+ (0, import_react6.useEffect)(() => {
6209
6346
  if (!isEditMode || shouldUseDevFixtures()) return;
6210
6347
  void loadAllSectionsManifest().then((manifest) => {
6211
6348
  if (Object.keys(manifest).length === 0) return;
6212
6349
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
6213
6350
  });
6214
6351
  }, [isEditMode]);
6215
- (0, import_react5.useEffect)(() => {
6352
+ (0, import_react6.useEffect)(() => {
6216
6353
  const update = () => {
6217
6354
  const el = activeElRef.current ?? selectedElRef.current;
6218
- if (el) setToolbarRect(el.getBoundingClientRect());
6355
+ if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6219
6356
  setToggleState((prev) => {
6220
6357
  if (!prev || !activeStateElRef.current) return prev;
6221
6358
  return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
@@ -6236,10 +6373,10 @@ function OhhwellsBridge() {
6236
6373
  vvp.removeEventListener("resize", update);
6237
6374
  };
6238
6375
  }, []);
6239
- const refreshStateRules = (0, import_react5.useCallback)(() => {
6376
+ const refreshStateRules = (0, import_react6.useCallback)(() => {
6240
6377
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
6241
6378
  }, []);
6242
- const processConfigRequest = (0, import_react5.useCallback)((insertAfterVal) => {
6379
+ const processConfigRequest = (0, import_react6.useCallback)((insertAfterVal) => {
6243
6380
  const tracker = getSectionsTracker();
6244
6381
  let entries = [];
6245
6382
  try {
@@ -6262,7 +6399,7 @@ function OhhwellsBridge() {
6262
6399
  }
6263
6400
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
6264
6401
  }, [isEditMode]);
6265
- const deactivate = (0, import_react5.useCallback)(() => {
6402
+ const deactivate = (0, import_react6.useCallback)(() => {
6266
6403
  const el = activeElRef.current;
6267
6404
  if (!el) return;
6268
6405
  const key = el.dataset.ohwKey;
@@ -6282,6 +6419,8 @@ function OhhwellsBridge() {
6282
6419
  }
6283
6420
  el.removeAttribute("contenteditable");
6284
6421
  activeElRef.current = null;
6422
+ setReorderHrefKey(null);
6423
+ setReorderDragDisabled(false);
6285
6424
  if (!selectedElRef.current) {
6286
6425
  setToolbarRect(null);
6287
6426
  setToolbarVariant("none");
@@ -6291,14 +6430,14 @@ function OhhwellsBridge() {
6291
6430
  setToolbarShowEditLink(false);
6292
6431
  postToParent({ type: "ow:exit-edit" });
6293
6432
  }, [postToParent]);
6294
- const deselect = (0, import_react5.useCallback)(() => {
6433
+ const deselect = (0, import_react6.useCallback)(() => {
6295
6434
  selectedElRef.current = null;
6296
6435
  if (!activeElRef.current) {
6297
6436
  setToolbarRect(null);
6298
6437
  setToolbarVariant("none");
6299
6438
  }
6300
6439
  }, []);
6301
- const select = (0, import_react5.useCallback)((anchor) => {
6440
+ const select = (0, import_react6.useCallback)((anchor) => {
6302
6441
  if (!isNavbarButton(anchor)) return;
6303
6442
  if (activeElRef.current) deactivate();
6304
6443
  selectedElRef.current = anchor;
@@ -6308,7 +6447,7 @@ function OhhwellsBridge() {
6308
6447
  setToolbarShowEditLink(false);
6309
6448
  setActiveCommands(/* @__PURE__ */ new Set());
6310
6449
  }, [deactivate]);
6311
- const activate = (0, import_react5.useCallback)((el) => {
6450
+ const activate = (0, import_react6.useCallback)((el) => {
6312
6451
  if (activeElRef.current === el) return;
6313
6452
  selectedElRef.current = null;
6314
6453
  deactivate();
@@ -6319,11 +6458,15 @@ function OhhwellsBridge() {
6319
6458
  setToolbarVariant("rich-text");
6320
6459
  el.setAttribute("contenteditable", "true");
6321
6460
  el.removeAttribute("data-ohw-hovered");
6461
+ el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
6322
6462
  activeElRef.current = el;
6323
6463
  originalContentRef.current = el.innerHTML;
6324
6464
  el.focus();
6325
- setToolbarRect(el.getBoundingClientRect());
6326
6465
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
6466
+ const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
6467
+ setReorderHrefKey(reorderKey);
6468
+ setReorderDragDisabled(reorderDisabled);
6469
+ setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6327
6470
  postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
6328
6471
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
6329
6472
  }, [deactivate, postToParent]);
@@ -6331,7 +6474,7 @@ function OhhwellsBridge() {
6331
6474
  deactivateRef.current = deactivate;
6332
6475
  selectRef.current = select;
6333
6476
  deselectRef.current = deselect;
6334
- (0, import_react5.useLayoutEffect)(() => {
6477
+ (0, import_react6.useLayoutEffect)(() => {
6335
6478
  if (!subdomain || isEditMode) {
6336
6479
  setFetchState("done");
6337
6480
  return;
@@ -6361,6 +6504,7 @@ function OhhwellsBridge() {
6361
6504
  });
6362
6505
  applyLinkByKey(key, val);
6363
6506
  }
6507
+ enforceLinkHrefs();
6364
6508
  initSectionsFromContent(content, true);
6365
6509
  sectionsLoadedRef.current = true;
6366
6510
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
@@ -6388,59 +6532,66 @@ function OhhwellsBridge() {
6388
6532
  cancelled = true;
6389
6533
  };
6390
6534
  }, [subdomain, isEditMode, pathname]);
6391
- (0, import_react5.useEffect)(() => {
6535
+ (0, import_react6.useEffect)(() => {
6392
6536
  if (!subdomain || isEditMode) return;
6393
6537
  let debounceTimer = null;
6538
+ let observer = null;
6394
6539
  const applyFromCache = () => {
6395
6540
  const content = contentCache.get(subdomain);
6396
6541
  if (!content) return;
6397
6542
  retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
6398
- for (const [key, val] of Object.entries(content)) {
6399
- if (key === "__ohw_sections") continue;
6400
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6401
- if (el.dataset.ohwEditable === "image") {
6402
- const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
6403
- if (img && img.src !== val) img.src = val;
6404
- } else if (el.dataset.ohwEditable === "bg-image") {
6405
- const next = `url('${val}')`;
6406
- if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
6407
- } else if (el.dataset.ohwEditable === "link") {
6408
- applyLinkHref(el, val);
6409
- } else if (el.innerHTML !== val) {
6410
- el.innerHTML = val;
6411
- }
6412
- });
6413
- applyLinkByKey(key, val);
6543
+ observer?.disconnect();
6544
+ try {
6545
+ for (const [key, val] of Object.entries(content)) {
6546
+ if (key === "__ohw_sections") continue;
6547
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6548
+ if (el.dataset.ohwEditable === "image") {
6549
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
6550
+ if (img && img.src !== val) img.src = val;
6551
+ } else if (el.dataset.ohwEditable === "bg-image") {
6552
+ const next = `url('${val}')`;
6553
+ if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
6554
+ } else if (el.dataset.ohwEditable === "link") {
6555
+ applyLinkHref(el, val);
6556
+ } else if (el.innerHTML !== val) {
6557
+ el.innerHTML = val;
6558
+ }
6559
+ });
6560
+ applyLinkByKey(key, val);
6561
+ }
6562
+ } finally {
6563
+ observer?.observe(document.body, { childList: true, subtree: true });
6414
6564
  }
6565
+ enforceLinkHrefs();
6415
6566
  };
6416
6567
  const scheduleApply = () => {
6417
6568
  if (debounceTimer) clearTimeout(debounceTimer);
6418
6569
  debounceTimer = setTimeout(applyFromCache, 150);
6419
6570
  };
6420
6571
  scheduleApply();
6421
- const observer = new MutationObserver(scheduleApply);
6572
+ observer = new MutationObserver(scheduleApply);
6422
6573
  observer.observe(document.body, { childList: true, subtree: true });
6423
6574
  return () => {
6424
- observer.disconnect();
6575
+ observer?.disconnect();
6425
6576
  if (debounceTimer) clearTimeout(debounceTimer);
6426
6577
  };
6427
6578
  }, [subdomain, isEditMode, pathname]);
6428
- (0, import_react5.useLayoutEffect)(() => {
6579
+ (0, import_react6.useLayoutEffect)(() => {
6429
6580
  const el = document.getElementById("ohw-loader");
6430
6581
  if (!el) return;
6431
6582
  const visible = Boolean(subdomain) && fetchState !== "done";
6432
6583
  el.style.display = visible ? "flex" : "none";
6433
6584
  }, [subdomain, fetchState]);
6434
- (0, import_react5.useEffect)(() => {
6585
+ (0, import_react6.useEffect)(() => {
6435
6586
  postToParent({ type: "ow:navigation", path: pathname });
6436
6587
  }, [pathname, postToParent]);
6437
- (0, import_react5.useEffect)(() => {
6588
+ (0, import_react6.useEffect)(() => {
6438
6589
  if (!isEditMode) return;
6439
6590
  setLinkPopover(null);
6440
6591
  deselectRef.current();
6441
6592
  deactivateRef.current();
6442
6593
  }, [pathname, isEditMode]);
6443
- (0, import_react5.useEffect)(() => {
6594
+ (0, import_react6.useEffect)(() => {
6444
6595
  if (!isEditMode) return;
6445
6596
  const measure = () => {
6446
6597
  const h = document.body.scrollHeight;
@@ -6464,7 +6615,7 @@ function OhhwellsBridge() {
6464
6615
  window.removeEventListener("resize", handleResize);
6465
6616
  };
6466
6617
  }, [pathname, isEditMode, postToParent]);
6467
- (0, import_react5.useEffect)(() => {
6618
+ (0, import_react6.useEffect)(() => {
6468
6619
  if (!subdomainFromQuery || isEditMode) return;
6469
6620
  const handleClick = (e) => {
6470
6621
  const anchor = e.target.closest("a");
@@ -6480,7 +6631,7 @@ function OhhwellsBridge() {
6480
6631
  document.addEventListener("click", handleClick, true);
6481
6632
  return () => document.removeEventListener("click", handleClick, true);
6482
6633
  }, [subdomainFromQuery, isEditMode, router]);
6483
- (0, import_react5.useEffect)(() => {
6634
+ (0, import_react6.useEffect)(() => {
6484
6635
  if (!isEditMode) {
6485
6636
  editStylesRef.current?.base.remove();
6486
6637
  editStylesRef.current?.forceHover.remove();
@@ -6545,6 +6696,11 @@ function OhhwellsBridge() {
6545
6696
  if (target.closest("[data-ohw-state-toggle]")) return;
6546
6697
  if (target.closest("[data-ohw-max-badge]")) return;
6547
6698
  if (isInsideLinkEditor(target)) return;
6699
+ if (target.closest('[data-ohw-edit-chrome], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
6700
+ e.preventDefault();
6701
+ e.stopPropagation();
6702
+ return;
6703
+ }
6548
6704
  const editable = target.closest("[data-ohw-editable]");
6549
6705
  if (editable) {
6550
6706
  if (editable.dataset.ohwEditable === "link") {
@@ -6621,14 +6777,18 @@ function OhhwellsBridge() {
6621
6777
  const selected = selectedElRef.current;
6622
6778
  if (selected && (selected === editable || selected.contains(editable))) return;
6623
6779
  if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
6624
- editable.setAttribute("data-ohw-hovered", "");
6780
+ const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
6781
+ hoverTarget.setAttribute("data-ohw-hovered", "");
6625
6782
  }
6626
6783
  };
6627
6784
  const handleMouseOut = (e) => {
6628
6785
  const editable = e.target.closest("[data-ohw-editable]");
6629
6786
  if (!editable) return;
6787
+ const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
6788
+ if (related?.closest("[data-ohw-drag-handle-container]")) return;
6630
6789
  if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image") {
6631
- editable.removeAttribute("data-ohw-hovered");
6790
+ const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
6791
+ hoverTarget.removeAttribute("data-ohw-hovered");
6632
6792
  }
6633
6793
  };
6634
6794
  const toProbeCoords = (clientX, clientY, fromParentViewport) => {
@@ -6743,11 +6903,20 @@ function OhhwellsBridge() {
6743
6903
  return;
6744
6904
  }
6745
6905
  }
6906
+ if (activeElRef.current) {
6907
+ if (hoveredImageRef.current) {
6908
+ hoveredImageRef.current = null;
6909
+ hoveredImageHasTextOverlapRef.current = false;
6910
+ resumeAnimTracks();
6911
+ postToParentRef.current({ type: "ow:image-unhover" });
6912
+ }
6913
+ return;
6914
+ }
6746
6915
  const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
6747
6916
  if (imgEl) {
6748
6917
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
6749
6918
  const topEl = document.elementFromPoint(x, y);
6750
- if (topEl?.closest("[data-ohw-toolbar]")) {
6919
+ if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
6751
6920
  if (hoveredImageRef.current) {
6752
6921
  hoveredImageRef.current = null;
6753
6922
  resumeAnimTracks();
@@ -7040,7 +7209,7 @@ function OhhwellsBridge() {
7040
7209
  const el = e.target;
7041
7210
  const key = el.dataset.ohwKey;
7042
7211
  if (!key) return;
7043
- if (el === activeElRef.current) setToolbarRect(el.getBoundingClientRect());
7212
+ if (el === activeElRef.current) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7044
7213
  const maxLen = el.dataset.ohwMaxLength ? parseInt(el.dataset.ohwMaxLength, 10) : null;
7045
7214
  if (maxLen) {
7046
7215
  const current = el.innerText.replace(/\n$/, "").length;
@@ -7103,6 +7272,7 @@ function OhhwellsBridge() {
7103
7272
  sectionsLoadedRef.current = true;
7104
7273
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
7105
7274
  }
7275
+ enforceLinkHrefs();
7106
7276
  postToParentRef.current({ type: "ow:hydrate-done" });
7107
7277
  };
7108
7278
  window.addEventListener("message", handleHydrate);
@@ -7269,7 +7439,7 @@ function OhhwellsBridge() {
7269
7439
  };
7270
7440
  const applyToolbarPos = (rect) => {
7271
7441
  const ps = parentScrollRef.current;
7272
- const approxW = toolbarVariantRef.current === "link-action" ? 120 : 330;
7442
+ const approxW = toolbarVariantRef.current === "link-action" ? 112 : 306;
7273
7443
  if (toolbarElRef.current) {
7274
7444
  const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
7275
7445
  toolbarElRef.current.style.top = `${top}px`;
@@ -7394,7 +7564,7 @@ function OhhwellsBridge() {
7394
7564
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
7395
7565
  };
7396
7566
  }, [isEditMode, refreshStateRules]);
7397
- (0, import_react5.useEffect)(() => {
7567
+ (0, import_react6.useEffect)(() => {
7398
7568
  const handler = (e) => {
7399
7569
  if (e.data?.type !== "ow:request-schedule-config") return;
7400
7570
  const insertAfterVal = e.data.insertAfter;
@@ -7410,7 +7580,7 @@ function OhhwellsBridge() {
7410
7580
  window.addEventListener("message", handler);
7411
7581
  return () => window.removeEventListener("message", handler);
7412
7582
  }, [processConfigRequest]);
7413
- (0, import_react5.useEffect)(() => {
7583
+ (0, import_react6.useEffect)(() => {
7414
7584
  if (!isEditMode) return;
7415
7585
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
7416
7586
  el.removeAttribute("data-ohw-active-state");
@@ -7439,19 +7609,19 @@ function OhhwellsBridge() {
7439
7609
  clearTimeout(timer);
7440
7610
  };
7441
7611
  }, [pathname, isEditMode, refreshStateRules, postToParent]);
7442
- (0, import_react5.useEffect)(() => {
7612
+ (0, import_react6.useEffect)(() => {
7443
7613
  scrollToHashSectionWhenReady();
7444
7614
  const onHashChange = () => scrollToHashSectionWhenReady();
7445
7615
  window.addEventListener("hashchange", onHashChange);
7446
7616
  return () => window.removeEventListener("hashchange", onHashChange);
7447
7617
  }, [pathname]);
7448
- const handleCommand = (0, import_react5.useCallback)((cmd) => {
7618
+ const handleCommand = (0, import_react6.useCallback)((cmd) => {
7449
7619
  document.execCommand(cmd, false);
7450
7620
  activeElRef.current?.focus();
7451
- if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
7621
+ if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
7452
7622
  refreshActiveCommandsRef.current();
7453
7623
  }, []);
7454
- const handleStateChange = (0, import_react5.useCallback)((state) => {
7624
+ const handleStateChange = (0, import_react6.useCallback)((state) => {
7455
7625
  if (!activeStateElRef.current) return;
7456
7626
  const el = activeStateElRef.current;
7457
7627
  if (state === "Default") {
@@ -7464,8 +7634,8 @@ function OhhwellsBridge() {
7464
7634
  }
7465
7635
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
7466
7636
  }, [deactivate]);
7467
- const closeLinkPopover = (0, import_react5.useCallback)(() => setLinkPopover(null), []);
7468
- const openLinkPopoverForActive = (0, import_react5.useCallback)(() => {
7637
+ const closeLinkPopover = (0, import_react6.useCallback)(() => setLinkPopover(null), []);
7638
+ const openLinkPopoverForActive = (0, import_react6.useCallback)(() => {
7469
7639
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
7470
7640
  if (!hrefCtx) return;
7471
7641
  bumpLinkPopoverGrace();
@@ -7475,7 +7645,7 @@ function OhhwellsBridge() {
7475
7645
  });
7476
7646
  deactivate();
7477
7647
  }, [deactivate]);
7478
- const openLinkPopoverForSelected = (0, import_react5.useCallback)(() => {
7648
+ const openLinkPopoverForSelected = (0, import_react6.useCallback)(() => {
7479
7649
  const anchor = selectedElRef.current;
7480
7650
  if (!anchor) return;
7481
7651
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -7487,7 +7657,7 @@ function OhhwellsBridge() {
7487
7657
  });
7488
7658
  deselect();
7489
7659
  }, [deselect]);
7490
- const handleLinkPopoverSubmit = (0, import_react5.useCallback)(
7660
+ const handleLinkPopoverSubmit = (0, import_react6.useCallback)(
7491
7661
  (target) => {
7492
7662
  if (!linkPopover) return;
7493
7663
  const { key } = linkPopover;
@@ -7501,11 +7671,19 @@ function OhhwellsBridge() {
7501
7671
  const currentSections = sectionsByPath[pathname] ?? [];
7502
7672
  linkPopoverOpenRef.current = linkPopover !== null;
7503
7673
  return bridgeRoot ? (0, import_react_dom2.createPortal)(
7504
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
7505
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
7506
- toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
7507
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
7508
- toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7674
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
7675
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
7676
+ toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
7677
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7678
+ EditGlowChrome,
7679
+ {
7680
+ rect: toolbarRect,
7681
+ elRef: glowElRef,
7682
+ reorderHrefKey,
7683
+ dragDisabled: reorderDragDisabled
7684
+ }
7685
+ ),
7686
+ toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7509
7687
  FloatingToolbar,
7510
7688
  {
7511
7689
  variant: "rich-text",
@@ -7518,7 +7696,7 @@ function OhhwellsBridge() {
7518
7696
  onEditLink: openLinkPopoverForActive
7519
7697
  }
7520
7698
  ),
7521
- toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7699
+ toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7522
7700
  FloatingToolbar,
7523
7701
  {
7524
7702
  variant: "link-action",
@@ -7531,7 +7709,7 @@ function OhhwellsBridge() {
7531
7709
  }
7532
7710
  )
7533
7711
  ] }),
7534
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
7712
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
7535
7713
  "div",
7536
7714
  {
7537
7715
  "data-ohw-max-badge": "",
@@ -7557,7 +7735,7 @@ function OhhwellsBridge() {
7557
7735
  ]
7558
7736
  }
7559
7737
  ),
7560
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7738
+ toggleState && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7561
7739
  StateToggle,
7562
7740
  {
7563
7741
  rect: toggleState.rect,
@@ -7566,15 +7744,15 @@ function OhhwellsBridge() {
7566
7744
  onStateChange: handleStateChange
7567
7745
  }
7568
7746
  ),
7569
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
7747
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
7570
7748
  "div",
7571
7749
  {
7572
7750
  "data-ohw-section-insert-line": "",
7573
7751
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
7574
7752
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
7575
7753
  children: [
7576
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7577
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7754
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7755
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7578
7756
  Badge,
7579
7757
  {
7580
7758
  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",
@@ -7587,11 +7765,11 @@ function OhhwellsBridge() {
7587
7765
  children: "Add Section"
7588
7766
  }
7589
7767
  ),
7590
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7768
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7591
7769
  ]
7592
7770
  }
7593
7771
  ),
7594
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7772
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7595
7773
  LinkPopover,
7596
7774
  {
7597
7775
  panelRef: linkPopoverPanelRef,
@@ -7606,13 +7784,39 @@ function OhhwellsBridge() {
7606
7784
  onSubmit: handleLinkPopoverSubmit
7607
7785
  },
7608
7786
  `${linkPopover.key}-${pathname}`
7609
- ) : null
7787
+ ) : null,
7788
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
7789
+ "div",
7790
+ {
7791
+ "data-ohw-section-insert-line": "",
7792
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
7793
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
7794
+ children: [
7795
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7796
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7797
+ Badge,
7798
+ {
7799
+ 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",
7800
+ onClick: () => {
7801
+ window.parent.postMessage(
7802
+ { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
7803
+ "*"
7804
+ );
7805
+ },
7806
+ children: "Add Section"
7807
+ }
7808
+ ),
7809
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7810
+ ]
7811
+ }
7812
+ )
7610
7813
  ] }),
7611
7814
  bridgeRoot
7612
7815
  ) : null;
7613
7816
  }
7614
7817
  // Annotate the CommonJS export names for ESM import in node:
7615
7818
  0 && (module.exports = {
7819
+ DragHandle,
7616
7820
  LinkEditorPanel,
7617
7821
  LinkPopover,
7618
7822
  OhhwellsBridge,
@@ -7623,6 +7827,7 @@ function OhhwellsBridge() {
7623
7827
  buildTarget,
7624
7828
  filterAvailablePages,
7625
7829
  getEditModeInitialState,
7830
+ isEditSessionActive,
7626
7831
  isValidUrl,
7627
7832
  parseTarget,
7628
7833
  toggleVariants,