@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.js CHANGED
@@ -1,10 +1,49 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React6, { useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useState as useState5 } from "react";
4
+ import React7, { useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState5 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
8
+ // src/linkHrefStore.ts
9
+ var linkHrefStore = /* @__PURE__ */ new Map();
10
+ function setStoredLinkHref(key, href) {
11
+ linkHrefStore.set(key, href);
12
+ }
13
+ function enforceLinkHrefs() {
14
+ document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
15
+ const key = el.getAttribute("data-ohw-href-key") ?? "";
16
+ if (!key) return;
17
+ const saved = linkHrefStore.get(key);
18
+ if (saved === void 0) return;
19
+ if (el.getAttribute("href") !== saved) el.setAttribute("href", saved);
20
+ });
21
+ }
22
+
23
+ // src/useLinkHrefGuardian.ts
24
+ import { useLayoutEffect } from "react";
25
+ function useLinkHrefGuardian(...deps) {
26
+ useLayoutEffect(() => {
27
+ let debounceTimer = null;
28
+ const scheduleEnforce = () => {
29
+ if (debounceTimer) clearTimeout(debounceTimer);
30
+ debounceTimer = setTimeout(enforceLinkHrefs, 50);
31
+ };
32
+ enforceLinkHrefs();
33
+ const observer = new MutationObserver(scheduleEnforce);
34
+ observer.observe(document.body, {
35
+ childList: true,
36
+ subtree: true,
37
+ attributes: true,
38
+ attributeFilter: ["href"]
39
+ });
40
+ return () => {
41
+ observer.disconnect();
42
+ if (debounceTimer) clearTimeout(debounceTimer);
43
+ };
44
+ }, deps);
45
+ }
46
+
8
47
  // src/ui/SchedulingWidget.tsx
9
48
  import { useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
10
49
 
@@ -4163,6 +4202,34 @@ function ToggleGroupItem({
4163
4202
  );
4164
4203
  }
4165
4204
 
4205
+ // src/ui/drag-handle.tsx
4206
+ import * as React3 from "react";
4207
+ import { GripVertical } from "lucide-react";
4208
+ import { jsx as jsx5 } from "react/jsx-runtime";
4209
+ var DragHandle = React3.forwardRef(
4210
+ ({ className, type = "button", ...props }, ref) => {
4211
+ return /* @__PURE__ */ jsx5(
4212
+ "button",
4213
+ {
4214
+ ref,
4215
+ type,
4216
+ "data-slot": "drag-handle",
4217
+ className: cn(
4218
+ "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-[30px]",
4219
+ "bg-white border border-transparent text-stone-500 shadow-md cursor-grab",
4220
+ "enabled:hover:border enabled:hover:border-stone-200 enabled:hover:text-stone-950",
4221
+ "enabled:active:border enabled:active:border-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
4222
+ "disabled:cursor-not-allowed disabled:opacity-40 disabled:text-stone-950 disabled:pointer-events-none",
4223
+ className
4224
+ ),
4225
+ ...props,
4226
+ children: /* @__PURE__ */ jsx5(GripVertical, { className: "size-4 shrink-0", "aria-hidden": "true" })
4227
+ }
4228
+ );
4229
+ }
4230
+ );
4231
+ DragHandle.displayName = "DragHandle";
4232
+
4166
4233
  // src/OhhwellsBridge.tsx
4167
4234
  import { createPortal } from "react-dom";
4168
4235
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
@@ -4442,60 +4509,60 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
4442
4509
  }
4443
4510
 
4444
4511
  // src/ui/dialog.tsx
4445
- import * as React3 from "react";
4512
+ import * as React4 from "react";
4446
4513
  import { Dialog as DialogPrimitive } from "radix-ui";
4447
4514
 
4448
4515
  // src/ui/icons.tsx
4449
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
4516
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
4450
4517
  function IconX({ className, ...props }) {
4451
4518
  return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4452
- /* @__PURE__ */ jsx5("path", { d: "M18 6 6 18" }),
4453
- /* @__PURE__ */ jsx5("path", { d: "m6 6 12 12" })
4519
+ /* @__PURE__ */ jsx6("path", { d: "M18 6 6 18" }),
4520
+ /* @__PURE__ */ jsx6("path", { d: "m6 6 12 12" })
4454
4521
  ] });
4455
4522
  }
4456
4523
  function IconChevronDown({ className, ...props }) {
4457
- return /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx5("path", { d: "m6 9 6 6 6-6" }) });
4524
+ return /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx6("path", { d: "m6 9 6 6 6-6" }) });
4458
4525
  }
4459
4526
  function IconFile({ className, ...props }) {
4460
4527
  return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4461
- /* @__PURE__ */ jsx5("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4462
- /* @__PURE__ */ jsx5("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4528
+ /* @__PURE__ */ jsx6("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4529
+ /* @__PURE__ */ jsx6("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4463
4530
  ] });
4464
4531
  }
4465
4532
  function IconInfo({ className, ...props }) {
4466
4533
  return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4467
- /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10" }),
4468
- /* @__PURE__ */ jsx5("path", { d: "M12 16v-4" }),
4469
- /* @__PURE__ */ jsx5("path", { d: "M12 8h.01" })
4534
+ /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
4535
+ /* @__PURE__ */ jsx6("path", { d: "M12 16v-4" }),
4536
+ /* @__PURE__ */ jsx6("path", { d: "M12 8h.01" })
4470
4537
  ] });
4471
4538
  }
4472
4539
  function IconArrowRight({ className, ...props }) {
4473
4540
  return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4474
- /* @__PURE__ */ jsx5("path", { d: "M5 12h14" }),
4475
- /* @__PURE__ */ jsx5("path", { d: "m12 5 7 7-7 7" })
4541
+ /* @__PURE__ */ jsx6("path", { d: "M5 12h14" }),
4542
+ /* @__PURE__ */ jsx6("path", { d: "m12 5 7 7-7 7" })
4476
4543
  ] });
4477
4544
  }
4478
4545
  function IconSection({ className, ...props }) {
4479
4546
  return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4480
- /* @__PURE__ */ jsx5("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4481
- /* @__PURE__ */ jsx5("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4482
- /* @__PURE__ */ jsx5("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4547
+ /* @__PURE__ */ jsx6("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4548
+ /* @__PURE__ */ jsx6("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4549
+ /* @__PURE__ */ jsx6("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4483
4550
  ] });
4484
4551
  }
4485
4552
 
4486
4553
  // src/ui/dialog.tsx
4487
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
4554
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
4488
4555
  function Dialog2({ ...props }) {
4489
- return /* @__PURE__ */ jsx6(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
4556
+ return /* @__PURE__ */ jsx7(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
4490
4557
  }
4491
4558
  function DialogPortal({ ...props }) {
4492
- return /* @__PURE__ */ jsx6(DialogPrimitive.Portal, { ...props });
4559
+ return /* @__PURE__ */ jsx7(DialogPrimitive.Portal, { ...props });
4493
4560
  }
4494
4561
  function DialogOverlay({
4495
4562
  className,
4496
4563
  ...props
4497
4564
  }) {
4498
- return /* @__PURE__ */ jsx6(
4565
+ return /* @__PURE__ */ jsx7(
4499
4566
  DialogPrimitive.Overlay,
4500
4567
  {
4501
4568
  "data-slot": "dialog-overlay",
@@ -4505,10 +4572,10 @@ function DialogOverlay({
4505
4572
  }
4506
4573
  );
4507
4574
  }
4508
- var DialogContent = React3.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4575
+ var DialogContent = React4.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4509
4576
  const positionMode = container ? "absolute" : "fixed";
4510
4577
  return /* @__PURE__ */ jsxs4(DialogPortal, { container: container ?? void 0, children: [
4511
- /* @__PURE__ */ jsx6(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4578
+ /* @__PURE__ */ jsx7(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4512
4579
  /* @__PURE__ */ jsxs4(
4513
4580
  DialogPrimitive.Content,
4514
4581
  {
@@ -4525,13 +4592,13 @@ var DialogContent = React3.forwardRef(({ className, children, showCloseButton =
4525
4592
  ...props,
4526
4593
  children: [
4527
4594
  children,
4528
- showCloseButton ? /* @__PURE__ */ jsx6(
4595
+ showCloseButton ? /* @__PURE__ */ jsx7(
4529
4596
  DialogPrimitive.Close,
4530
4597
  {
4531
4598
  type: "button",
4532
4599
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
4533
4600
  "aria-label": "Close",
4534
- children: /* @__PURE__ */ jsx6(IconX, { "aria-hidden": true })
4601
+ children: /* @__PURE__ */ jsx7(IconX, { "aria-hidden": true })
4535
4602
  }
4536
4603
  ) : null
4537
4604
  ]
@@ -4541,12 +4608,12 @@ var DialogContent = React3.forwardRef(({ className, children, showCloseButton =
4541
4608
  });
4542
4609
  DialogContent.displayName = DialogPrimitive.Content.displayName;
4543
4610
  function DialogHeader({ className, ...props }) {
4544
- return /* @__PURE__ */ jsx6("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4611
+ return /* @__PURE__ */ jsx7("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4545
4612
  }
4546
4613
  function DialogFooter({ className, ...props }) {
4547
- return /* @__PURE__ */ jsx6("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
4614
+ return /* @__PURE__ */ jsx7("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
4548
4615
  }
4549
- var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
4616
+ var DialogTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
4550
4617
  DialogPrimitive.Title,
4551
4618
  {
4552
4619
  ref,
@@ -4555,7 +4622,7 @@ var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE
4555
4622
  }
4556
4623
  ));
4557
4624
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
4558
- var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
4625
+ var DialogDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
4559
4626
  DialogPrimitive.Description,
4560
4627
  {
4561
4628
  ref,
@@ -4567,9 +4634,9 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
4567
4634
  var DialogClose = DialogPrimitive.Close;
4568
4635
 
4569
4636
  // src/ui/button.tsx
4570
- import * as React4 from "react";
4637
+ import * as React5 from "react";
4571
4638
  import { Slot } from "radix-ui";
4572
- import { jsx as jsx7 } from "react/jsx-runtime";
4639
+ import { jsx as jsx8 } from "react/jsx-runtime";
4573
4640
  var buttonVariants = cva(
4574
4641
  "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",
4575
4642
  {
@@ -4590,10 +4657,10 @@ var buttonVariants = cva(
4590
4657
  }
4591
4658
  }
4592
4659
  );
4593
- var Button = React4.forwardRef(
4660
+ var Button = React5.forwardRef(
4594
4661
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4595
4662
  const Comp = asChild ? Slot.Root : "button";
4596
- return /* @__PURE__ */ jsx7(
4663
+ return /* @__PURE__ */ jsx8(
4597
4664
  Comp,
4598
4665
  {
4599
4666
  ref,
@@ -4607,30 +4674,30 @@ var Button = React4.forwardRef(
4607
4674
  Button.displayName = "Button";
4608
4675
 
4609
4676
  // src/ui/link-modal/DestinationBreadcrumb.tsx
4610
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
4677
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
4611
4678
  function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
4612
4679
  return /* @__PURE__ */ jsxs5("div", { className: "flex w-full flex-col gap-2", children: [
4613
- /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
4680
+ /* @__PURE__ */ jsx9("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
4614
4681
  /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
4615
4682
  /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
4616
- /* @__PURE__ */ jsx8(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4617
- /* @__PURE__ */ jsx8("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
4683
+ /* @__PURE__ */ jsx9(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4684
+ /* @__PURE__ */ jsx9("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
4618
4685
  ] }),
4619
- /* @__PURE__ */ jsx8(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
4686
+ /* @__PURE__ */ jsx9(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
4620
4687
  /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
4621
- /* @__PURE__ */ jsx8(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4622
- /* @__PURE__ */ jsx8("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
4688
+ /* @__PURE__ */ jsx9(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4689
+ /* @__PURE__ */ jsx9("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
4623
4690
  ] })
4624
4691
  ] })
4625
4692
  ] });
4626
4693
  }
4627
4694
 
4628
4695
  // src/ui/link-modal/SectionTreeItem.tsx
4629
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
4696
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
4630
4697
  function SectionTreeItem({ section, onSelect, selected }) {
4631
4698
  const interactive = Boolean(onSelect);
4632
4699
  return /* @__PURE__ */ jsxs6("div", { className: "flex h-9 w-full items-end pl-3", children: [
4633
- /* @__PURE__ */ jsx9(
4700
+ /* @__PURE__ */ jsx10(
4634
4701
  "div",
4635
4702
  {
4636
4703
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
@@ -4655,8 +4722,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
4655
4722
  interactive && selected && "border-primary"
4656
4723
  ),
4657
4724
  children: [
4658
- /* @__PURE__ */ jsx9(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4659
- /* @__PURE__ */ jsx9("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
4725
+ /* @__PURE__ */ jsx10(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4726
+ /* @__PURE__ */ jsx10("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
4660
4727
  ]
4661
4728
  }
4662
4729
  )
@@ -4664,11 +4731,11 @@ function SectionTreeItem({ section, onSelect, selected }) {
4664
4731
  }
4665
4732
  function SectionPickerList({ sections, onSelect }) {
4666
4733
  if (sections.length === 0) {
4667
- return /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4734
+ return /* @__PURE__ */ jsx10("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4668
4735
  }
4669
4736
  return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", children: [
4670
- /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
4671
- sections.map((section) => /* @__PURE__ */ jsx9(SectionTreeItem, { section, onSelect }, section.id))
4737
+ /* @__PURE__ */ jsx10("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
4738
+ sections.map((section) => /* @__PURE__ */ jsx10(SectionTreeItem, { section, onSelect }, section.id))
4672
4739
  ] });
4673
4740
  }
4674
4741
 
@@ -4676,11 +4743,11 @@ function SectionPickerList({ sections, onSelect }) {
4676
4743
  import { useId as useId2, useRef as useRef2, useState as useState3 } from "react";
4677
4744
 
4678
4745
  // src/ui/input.tsx
4679
- import * as React5 from "react";
4680
- import { jsx as jsx10 } from "react/jsx-runtime";
4681
- var Input = React5.forwardRef(
4746
+ import * as React6 from "react";
4747
+ import { jsx as jsx11 } from "react/jsx-runtime";
4748
+ var Input = React6.forwardRef(
4682
4749
  ({ className, type, ...props }, ref) => {
4683
- return /* @__PURE__ */ jsx10(
4750
+ return /* @__PURE__ */ jsx11(
4684
4751
  "input",
4685
4752
  {
4686
4753
  type,
@@ -4699,9 +4766,9 @@ Input.displayName = "Input";
4699
4766
 
4700
4767
  // src/ui/label.tsx
4701
4768
  import { Label as LabelPrimitive } from "radix-ui";
4702
- import { jsx as jsx11 } from "react/jsx-runtime";
4769
+ import { jsx as jsx12 } from "react/jsx-runtime";
4703
4770
  function Label({ className, ...props }) {
4704
- return /* @__PURE__ */ jsx11(
4771
+ return /* @__PURE__ */ jsx12(
4705
4772
  LabelPrimitive.Root,
4706
4773
  {
4707
4774
  "data-slot": "label",
@@ -4712,9 +4779,9 @@ function Label({ className, ...props }) {
4712
4779
  }
4713
4780
 
4714
4781
  // src/ui/link-modal/UrlOrPageInput.tsx
4715
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
4782
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
4716
4783
  function FieldChevron({ onClick }) {
4717
- return /* @__PURE__ */ jsx12(
4784
+ return /* @__PURE__ */ jsx13(
4718
4785
  "button",
4719
4786
  {
4720
4787
  type: "button",
@@ -4722,7 +4789,7 @@ function FieldChevron({ onClick }) {
4722
4789
  onClick,
4723
4790
  "aria-label": "Open page list",
4724
4791
  tabIndex: -1,
4725
- children: /* @__PURE__ */ jsx12(IconChevronDown, {})
4792
+ children: /* @__PURE__ */ jsx13(IconChevronDown, {})
4726
4793
  }
4727
4794
  );
4728
4795
  }
@@ -4762,11 +4829,11 @@ function UrlOrPageInput({
4762
4829
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
4763
4830
  );
4764
4831
  return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2 p-0", children: [
4765
- /* @__PURE__ */ jsx12(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4832
+ /* @__PURE__ */ jsx13(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4766
4833
  /* @__PURE__ */ jsxs7("div", { className: "relative w-full", children: [
4767
4834
  /* @__PURE__ */ jsxs7("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
4768
- selectedPage ? /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
4769
- readOnly ? /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx12(
4835
+ selectedPage ? /* @__PURE__ */ jsx13("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx13(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
4836
+ readOnly ? /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx13(
4770
4837
  Input,
4771
4838
  {
4772
4839
  ref: inputRef,
@@ -4785,7 +4852,7 @@ function UrlOrPageInput({
4785
4852
  )
4786
4853
  }
4787
4854
  ),
4788
- selectedPage && !readOnly ? /* @__PURE__ */ jsx12(
4855
+ selectedPage && !readOnly ? /* @__PURE__ */ jsx13(
4789
4856
  "button",
4790
4857
  {
4791
4858
  type: "button",
@@ -4793,12 +4860,12 @@ function UrlOrPageInput({
4793
4860
  onMouseDown: clearSelection,
4794
4861
  "aria-label": "Clear selected page",
4795
4862
  tabIndex: -1,
4796
- children: /* @__PURE__ */ jsx12(IconX, { "aria-hidden": true })
4863
+ children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
4797
4864
  }
4798
4865
  ) : null,
4799
- !readOnly ? /* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown }) : null
4866
+ !readOnly ? /* @__PURE__ */ jsx13(FieldChevron, { onClick: toggleDropdown }) : null
4800
4867
  ] }),
4801
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx12(
4868
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx13(
4802
4869
  "div",
4803
4870
  {
4804
4871
  "data-ohw-link-page-dropdown": "",
@@ -4811,8 +4878,8 @@ function UrlOrPageInput({
4811
4878
  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",
4812
4879
  onClick: () => onPageSelect(page),
4813
4880
  children: [
4814
- /* @__PURE__ */ jsx12(IconFile, { className: "shrink-0", "aria-hidden": true }),
4815
- /* @__PURE__ */ jsx12("span", { className: "truncate", children: page.title })
4881
+ /* @__PURE__ */ jsx13(IconFile, { className: "shrink-0", "aria-hidden": true }),
4882
+ /* @__PURE__ */ jsx13("span", { className: "truncate", children: page.title })
4816
4883
  ]
4817
4884
  },
4818
4885
  page.path
@@ -4820,7 +4887,7 @@ function UrlOrPageInput({
4820
4887
  }
4821
4888
  ) : null
4822
4889
  ] }),
4823
- urlError ? /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4890
+ urlError ? /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4824
4891
  ] });
4825
4892
  }
4826
4893
 
@@ -4988,7 +5055,7 @@ function useLinkModalState({
4988
5055
  }
4989
5056
 
4990
5057
  // src/ui/link-modal/LinkEditorPanel.tsx
4991
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
5058
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
4992
5059
  function LinkEditorPanel({
4993
5060
  open = true,
4994
5061
  mode = "create",
@@ -5013,24 +5080,24 @@ function LinkEditorPanel({
5013
5080
  });
5014
5081
  const isCancel = state.secondaryLabel === "Cancel";
5015
5082
  return /* @__PURE__ */ jsxs8(Fragment2, { children: [
5016
- /* @__PURE__ */ jsx13(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx13(
5083
+ /* @__PURE__ */ jsx14(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx14(
5017
5084
  "button",
5018
5085
  {
5019
5086
  type: "button",
5020
5087
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5021
5088
  "aria-label": "Close",
5022
- children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
5089
+ children: /* @__PURE__ */ jsx14(IconX, { "aria-hidden": true })
5023
5090
  }
5024
5091
  ) }),
5025
- /* @__PURE__ */ jsx13(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx13(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5092
+ /* @__PURE__ */ jsx14(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx14(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5026
5093
  /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5027
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx13(
5094
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx14(
5028
5095
  DestinationBreadcrumb,
5029
5096
  {
5030
5097
  pageTitle: state.selectedPage.title,
5031
5098
  sectionLabel: state.selectedSection.label
5032
5099
  }
5033
- ) : /* @__PURE__ */ jsx13(
5100
+ ) : /* @__PURE__ */ jsx14(
5034
5101
  UrlOrPageInput,
5035
5102
  {
5036
5103
  value: state.searchValue,
@@ -5044,17 +5111,17 @@ function LinkEditorPanel({
5044
5111
  }
5045
5112
  ),
5046
5113
  state.showChooseSection ? /* @__PURE__ */ jsxs8("div", { className: "flex flex-col justify-center gap-2", children: [
5047
- /* @__PURE__ */ jsx13(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5114
+ /* @__PURE__ */ jsx14(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5048
5115
  /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5049
- /* @__PURE__ */ jsx13(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5050
- /* @__PURE__ */ jsx13("span", { children: "Pick a section this link should scroll to." })
5116
+ /* @__PURE__ */ jsx14(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5117
+ /* @__PURE__ */ jsx14("span", { children: "Pick a section this link should scroll to." })
5051
5118
  ] })
5052
5119
  ] }) : null,
5053
- state.showSectionPicker ? /* @__PURE__ */ jsx13(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5054
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx13(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5120
+ state.showSectionPicker ? /* @__PURE__ */ jsx14(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5121
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx14(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5055
5122
  ] }),
5056
5123
  /* @__PURE__ */ jsxs8(DialogFooter, { className: "w-full px-6 pb-6", children: [
5057
- /* @__PURE__ */ jsx13(
5124
+ /* @__PURE__ */ jsx14(
5058
5125
  Button,
5059
5126
  {
5060
5127
  type: "button",
@@ -5069,7 +5136,7 @@ function LinkEditorPanel({
5069
5136
  children: state.secondaryLabel
5070
5137
  }
5071
5138
  ),
5072
- /* @__PURE__ */ jsx13(
5139
+ /* @__PURE__ */ jsx14(
5073
5140
  Button,
5074
5141
  {
5075
5142
  type: "button",
@@ -5088,7 +5155,7 @@ function LinkEditorPanel({
5088
5155
  }
5089
5156
 
5090
5157
  // src/ui/link-modal/LinkPopover.tsx
5091
- import { jsx as jsx14 } from "react/jsx-runtime";
5158
+ import { jsx as jsx15 } from "react/jsx-runtime";
5092
5159
  function LinkPopover({
5093
5160
  open = true,
5094
5161
  panelRef,
@@ -5096,14 +5163,14 @@ function LinkPopover({
5096
5163
  onClose,
5097
5164
  ...editorProps
5098
5165
  }) {
5099
- return /* @__PURE__ */ jsx14(
5166
+ return /* @__PURE__ */ jsx15(
5100
5167
  Dialog2,
5101
5168
  {
5102
5169
  open,
5103
5170
  onOpenChange: (next) => {
5104
5171
  if (!next) onClose?.();
5105
5172
  },
5106
- children: /* @__PURE__ */ jsx14(
5173
+ children: /* @__PURE__ */ jsx15(
5107
5174
  DialogContent,
5108
5175
  {
5109
5176
  ref: panelRef,
@@ -5113,7 +5180,7 @@ function LinkPopover({
5113
5180
  "data-ohw-bridge": "",
5114
5181
  showCloseButton: false,
5115
5182
  className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5116
- children: /* @__PURE__ */ jsx14(LinkEditorPanel, { ...editorProps, open, onClose })
5183
+ children: /* @__PURE__ */ jsx15(LinkEditorPanel, { ...editorProps, open, onClose })
5117
5184
  }
5118
5185
  )
5119
5186
  }
@@ -5179,7 +5246,7 @@ function shouldUseDevFixtures() {
5179
5246
  }
5180
5247
 
5181
5248
  // src/ui/badge.tsx
5182
- import { jsx as jsx15 } from "react/jsx-runtime";
5249
+ import { jsx as jsx16 } from "react/jsx-runtime";
5183
5250
  var badgeVariants = cva(
5184
5251
  "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",
5185
5252
  {
@@ -5197,11 +5264,11 @@ var badgeVariants = cva(
5197
5264
  }
5198
5265
  );
5199
5266
  function Badge({ className, variant, ...props }) {
5200
- return /* @__PURE__ */ jsx15("div", { className: cn(badgeVariants({ variant }), className), ...props });
5267
+ return /* @__PURE__ */ jsx16("div", { className: cn(badgeVariants({ variant }), className), ...props });
5201
5268
  }
5202
5269
 
5203
5270
  // src/OhhwellsBridge.tsx
5204
- import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
5271
+ import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
5205
5272
  var PRIMARY = "#0885FE";
5206
5273
  var IMAGE_FADE_MS = 300;
5207
5274
  function runOpacityFade(el, onDone) {
@@ -5375,7 +5442,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
5375
5442
  const root = createRoot(container);
5376
5443
  flushSync(() => {
5377
5444
  root.render(
5378
- /* @__PURE__ */ jsx16(
5445
+ /* @__PURE__ */ jsx17(
5379
5446
  SchedulingWidget,
5380
5447
  {
5381
5448
  notifyOnConnect,
@@ -5423,6 +5490,20 @@ function applyLinkHref(el, val) {
5423
5490
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
5424
5491
  if (anchor) anchor.setAttribute("href", val);
5425
5492
  }
5493
+ function getEditMeasureEl(editable) {
5494
+ return editable.closest("[data-ohw-href-key]") ?? editable;
5495
+ }
5496
+ function isDragHandleDisabled(el) {
5497
+ const carriers = [
5498
+ el.closest("[data-ohw-href-key]"),
5499
+ el.closest("[data-ohw-editable]")
5500
+ ];
5501
+ return carriers.some((carrier) => {
5502
+ if (!carrier) return false;
5503
+ const raw = carrier.getAttribute("data-ohw-drag-disabled");
5504
+ return raw === "true" || raw === "";
5505
+ });
5506
+ }
5426
5507
  function collectEditableNodes() {
5427
5508
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
5428
5509
  if (el.dataset.ohwEditable === "image") {
@@ -5454,10 +5535,11 @@ function applyLinkByKey(key, val) {
5454
5535
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
5455
5536
  if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
5456
5537
  });
5457
- document.querySelectorAll(`[data-ohw-href-key="${key}"]`).forEach((el) => {
5458
- applyLinkHref(el, val);
5459
- });
5460
- window.dispatchEvent(new CustomEvent("ohw:link-href", { detail: { key, href: val } }));
5538
+ const hrefAnchors = document.querySelectorAll(`[data-ohw-href-key="${key}"]`);
5539
+ if (hrefAnchors.length > 0) {
5540
+ setStoredLinkHref(key, val);
5541
+ hrefAnchors.forEach((el) => applyLinkHref(el, val));
5542
+ }
5461
5543
  }
5462
5544
  function isInsideLinkEditor(target) {
5463
5545
  return Boolean(
@@ -5475,6 +5557,14 @@ function getHrefKeyFromElement(el) {
5475
5557
  function isNavbarButton(el) {
5476
5558
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
5477
5559
  }
5560
+ function getReorderHandleState(el) {
5561
+ const linkEl = el.closest("[data-ohw-href-key]");
5562
+ if (!linkEl || isNavbarButton(el)) return { key: null, disabled: false };
5563
+ return {
5564
+ key: linkEl.dataset.ohwHrefKey ?? null,
5565
+ disabled: isDragHandleDisabled(el)
5566
+ };
5567
+ }
5478
5568
  function clearHrefKeyHover(anchor) {
5479
5569
  anchor.removeAttribute("data-ohw-hovered");
5480
5570
  anchor.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
@@ -5604,6 +5694,8 @@ var ICONS = {
5604
5694
  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"/>',
5605
5695
  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"/>'
5606
5696
  };
5697
+ var TOOLBAR_PILL_PADDING = 2;
5698
+ var TOOLBAR_TOGGLE_GAP = 4;
5607
5699
  var TOOLBAR_GROUPS = [
5608
5700
  [
5609
5701
  { cmd: "bold", title: "Bold" },
@@ -5621,24 +5713,64 @@ var TOOLBAR_GROUPS = [
5621
5713
  { cmd: "insertOrderedList", title: "Numbered List" }
5622
5714
  ]
5623
5715
  ];
5624
- function GlowFrame({ rect, elRef }) {
5716
+ function EditGlowChrome({
5717
+ rect,
5718
+ elRef,
5719
+ reorderHrefKey,
5720
+ dragDisabled = false
5721
+ }) {
5625
5722
  const GAP = 6;
5626
- return /* @__PURE__ */ jsx16(
5723
+ return /* @__PURE__ */ jsxs9(
5627
5724
  "div",
5628
5725
  {
5629
5726
  ref: elRef,
5727
+ "data-ohw-edit-chrome": "",
5630
5728
  style: {
5631
5729
  position: "fixed",
5632
5730
  top: rect.top - GAP,
5633
5731
  left: rect.left - GAP,
5634
5732
  width: rect.width + GAP * 2,
5635
5733
  height: rect.height + GAP * 2,
5636
- border: "2px solid #0885FE",
5637
- borderRadius: 8,
5638
- boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
5639
5734
  pointerEvents: "none",
5640
5735
  zIndex: 2147483646
5641
- }
5736
+ },
5737
+ children: [
5738
+ /* @__PURE__ */ jsx17(
5739
+ "div",
5740
+ {
5741
+ style: {
5742
+ position: "absolute",
5743
+ inset: 0,
5744
+ border: "2px solid #0885FE",
5745
+ borderRadius: 8,
5746
+ boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
5747
+ pointerEvents: "none"
5748
+ }
5749
+ }
5750
+ ),
5751
+ reorderHrefKey && /* @__PURE__ */ jsx17(
5752
+ "div",
5753
+ {
5754
+ "data-ohw-drag-handle-container": "",
5755
+ "data-ohw-drag-disabled": dragDisabled ? "" : void 0,
5756
+ style: {
5757
+ position: "absolute",
5758
+ left: 0,
5759
+ top: "50%",
5760
+ transform: "translate(calc(-100% - 7px), -50%)",
5761
+ pointerEvents: dragDisabled ? "none" : "auto"
5762
+ },
5763
+ children: /* @__PURE__ */ jsx17(
5764
+ DragHandle,
5765
+ {
5766
+ "aria-label": `Reorder ${reorderHrefKey}`,
5767
+ disabled: dragDisabled,
5768
+ "aria-disabled": dragDisabled
5769
+ }
5770
+ )
5771
+ }
5772
+ )
5773
+ ]
5642
5774
  }
5643
5775
  );
5644
5776
  }
@@ -5682,9 +5814,9 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
5682
5814
  const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
5683
5815
  return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
5684
5816
  }
5685
- function calcToolbarPos(rect, parentScroll, approxW = 330) {
5817
+ function calcToolbarPos(rect, parentScroll, approxW = 306) {
5686
5818
  const GAP = 8;
5687
- const APPROX_H = 36;
5819
+ const APPROX_H = 32;
5688
5820
  const APPROX_W = approxW;
5689
5821
  const clip = getIframeVisibleClip(parentScroll);
5690
5822
  const clipTop = clip?.top ?? 0;
@@ -5719,8 +5851,8 @@ function calcToolbarPos(rect, parentScroll, approxW = 330) {
5719
5851
  return { top, left, transform };
5720
5852
  }
5721
5853
  var EDIT_LINK_ICON = /* @__PURE__ */ jsxs9("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: [
5722
- /* @__PURE__ */ jsx16("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5723
- /* @__PURE__ */ jsx16("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5854
+ /* @__PURE__ */ jsx17("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5855
+ /* @__PURE__ */ jsx17("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5724
5856
  ] });
5725
5857
  function FloatingToolbar({
5726
5858
  rect,
@@ -5732,7 +5864,7 @@ function FloatingToolbar({
5732
5864
  showEditLink,
5733
5865
  onEditLink
5734
5866
  }) {
5735
- const approxW = variant === "link-action" ? 120 : 330;
5867
+ const approxW = variant === "link-action" ? 112 : 306;
5736
5868
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, approxW);
5737
5869
  if (variant === "link-action") {
5738
5870
  return /* @__PURE__ */ jsxs9(
@@ -5752,15 +5884,15 @@ function FloatingToolbar({
5752
5884
  boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
5753
5885
  display: "flex",
5754
5886
  alignItems: "center",
5755
- padding: 4,
5756
- gap: 6,
5887
+ padding: TOOLBAR_PILL_PADDING,
5888
+ gap: TOOLBAR_TOGGLE_GAP,
5757
5889
  fontFamily: "sans-serif",
5758
5890
  pointerEvents: "auto",
5759
5891
  whiteSpace: "nowrap"
5760
5892
  },
5761
5893
  onMouseDown: (e) => e.stopPropagation(),
5762
5894
  children: [
5763
- /* @__PURE__ */ jsx16(
5895
+ /* @__PURE__ */ jsx17(
5764
5896
  "button",
5765
5897
  {
5766
5898
  type: "button",
@@ -5795,8 +5927,8 @@ function FloatingToolbar({
5795
5927
  children: EDIT_LINK_ICON
5796
5928
  }
5797
5929
  ),
5798
- /* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5799
- /* @__PURE__ */ jsx16(
5930
+ /* @__PURE__ */ jsx17("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5931
+ /* @__PURE__ */ jsx17(
5800
5932
  "button",
5801
5933
  {
5802
5934
  type: "button",
@@ -5816,9 +5948,9 @@ function FloatingToolbar({
5816
5948
  opacity: 0.6
5817
5949
  },
5818
5950
  children: /* @__PURE__ */ jsxs9("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: [
5819
- /* @__PURE__ */ jsx16("circle", { cx: "5", cy: "12", r: "1.5" }),
5820
- /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "1.5" }),
5821
- /* @__PURE__ */ jsx16("circle", { cx: "19", cy: "12", r: "1.5" })
5951
+ /* @__PURE__ */ jsx17("circle", { cx: "5", cy: "12", r: "1.5" }),
5952
+ /* @__PURE__ */ jsx17("circle", { cx: "12", cy: "12", r: "1.5" }),
5953
+ /* @__PURE__ */ jsx17("circle", { cx: "19", cy: "12", r: "1.5" })
5822
5954
  ] })
5823
5955
  }
5824
5956
  )
@@ -5843,19 +5975,19 @@ function FloatingToolbar({
5843
5975
  boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
5844
5976
  display: "flex",
5845
5977
  alignItems: "center",
5846
- padding: 4,
5847
- gap: 6,
5978
+ padding: TOOLBAR_PILL_PADDING,
5979
+ gap: TOOLBAR_TOGGLE_GAP,
5848
5980
  fontFamily: "sans-serif",
5849
5981
  pointerEvents: "auto",
5850
5982
  whiteSpace: "nowrap"
5851
5983
  },
5852
5984
  onMouseDown: (e) => e.stopPropagation(),
5853
5985
  children: [
5854
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs9(React6.Fragment, { children: [
5855
- gi > 0 && /* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5986
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs9(React7.Fragment, { children: [
5987
+ gi > 0 && /* @__PURE__ */ jsx17("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5856
5988
  btns.map((btn) => {
5857
5989
  const isActive = activeCommands.has(btn.cmd);
5858
- return /* @__PURE__ */ jsx16(
5990
+ return /* @__PURE__ */ jsx17(
5859
5991
  "button",
5860
5992
  {
5861
5993
  title: btn.title,
@@ -5881,7 +6013,7 @@ function FloatingToolbar({
5881
6013
  flexShrink: 0,
5882
6014
  padding: 6
5883
6015
  },
5884
- children: /* @__PURE__ */ jsx16(
6016
+ children: /* @__PURE__ */ jsx17(
5885
6017
  "svg",
5886
6018
  {
5887
6019
  width: "16",
@@ -5901,7 +6033,7 @@ function FloatingToolbar({
5901
6033
  );
5902
6034
  })
5903
6035
  ] }, gi)),
5904
- showEditLink ? /* @__PURE__ */ jsx16(
6036
+ showEditLink ? /* @__PURE__ */ jsx17(
5905
6037
  "button",
5906
6038
  {
5907
6039
  type: "button",
@@ -5950,7 +6082,7 @@ function StateToggle({
5950
6082
  states,
5951
6083
  onStateChange
5952
6084
  }) {
5953
- return /* @__PURE__ */ jsx16(
6085
+ return /* @__PURE__ */ jsx17(
5954
6086
  ToggleGroup,
5955
6087
  {
5956
6088
  "data-ohw-state-toggle": "",
@@ -5964,7 +6096,7 @@ function StateToggle({
5964
6096
  left: rect.right - 8,
5965
6097
  transform: "translateX(-100%)"
5966
6098
  },
5967
- children: states.map((state) => /* @__PURE__ */ jsx16(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6099
+ children: states.map((state) => /* @__PURE__ */ jsx17(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
5968
6100
  }
5969
6101
  );
5970
6102
  }
@@ -6016,6 +6148,7 @@ function OhhwellsBridge() {
6016
6148
  }, []);
6017
6149
  const subdomainFromQuery = searchParams.get("subdomain");
6018
6150
  const subdomain = resolveSubdomain(subdomainFromQuery);
6151
+ useLinkHrefGuardian(pathname, subdomain, isEditMode);
6019
6152
  const postToParent = useCallback2((data) => {
6020
6153
  if (typeof window !== "undefined" && window.parent !== window) {
6021
6154
  window.parent.postMessage(data, "*");
@@ -6061,6 +6194,8 @@ function OhhwellsBridge() {
6061
6194
  const [toolbarVariant, setToolbarVariant] = useState5("none");
6062
6195
  const toolbarVariantRef = useRef3("none");
6063
6196
  toolbarVariantRef.current = toolbarVariant;
6197
+ const [reorderHrefKey, setReorderHrefKey] = useState5(null);
6198
+ const [reorderDragDisabled, setReorderDragDisabled] = useState5(false);
6064
6199
  const [toggleState, setToggleState] = useState5(null);
6065
6200
  const [maxBadge, setMaxBadge] = useState5(null);
6066
6201
  const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
@@ -6167,7 +6302,7 @@ function OhhwellsBridge() {
6167
6302
  useEffect3(() => {
6168
6303
  const update = () => {
6169
6304
  const el = activeElRef.current ?? selectedElRef.current;
6170
- if (el) setToolbarRect(el.getBoundingClientRect());
6305
+ if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6171
6306
  setToggleState((prev) => {
6172
6307
  if (!prev || !activeStateElRef.current) return prev;
6173
6308
  return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
@@ -6234,6 +6369,8 @@ function OhhwellsBridge() {
6234
6369
  }
6235
6370
  el.removeAttribute("contenteditable");
6236
6371
  activeElRef.current = null;
6372
+ setReorderHrefKey(null);
6373
+ setReorderDragDisabled(false);
6237
6374
  if (!selectedElRef.current) {
6238
6375
  setToolbarRect(null);
6239
6376
  setToolbarVariant("none");
@@ -6271,11 +6408,15 @@ function OhhwellsBridge() {
6271
6408
  setToolbarVariant("rich-text");
6272
6409
  el.setAttribute("contenteditable", "true");
6273
6410
  el.removeAttribute("data-ohw-hovered");
6411
+ el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
6274
6412
  activeElRef.current = el;
6275
6413
  originalContentRef.current = el.innerHTML;
6276
6414
  el.focus();
6277
- setToolbarRect(el.getBoundingClientRect());
6278
6415
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
6416
+ const { key: reorderKey, disabled: reorderDisabled } = getReorderHandleState(el);
6417
+ setReorderHrefKey(reorderKey);
6418
+ setReorderDragDisabled(reorderDisabled);
6419
+ setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6279
6420
  postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
6280
6421
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
6281
6422
  }, [deactivate, postToParent]);
@@ -6283,7 +6424,7 @@ function OhhwellsBridge() {
6283
6424
  deactivateRef.current = deactivate;
6284
6425
  selectRef.current = select;
6285
6426
  deselectRef.current = deselect;
6286
- useLayoutEffect(() => {
6427
+ useLayoutEffect2(() => {
6287
6428
  if (!subdomain || isEditMode) {
6288
6429
  setFetchState("done");
6289
6430
  return;
@@ -6313,6 +6454,7 @@ function OhhwellsBridge() {
6313
6454
  });
6314
6455
  applyLinkByKey(key, val);
6315
6456
  }
6457
+ enforceLinkHrefs();
6316
6458
  initSectionsFromContent(content, true);
6317
6459
  sectionsLoadedRef.current = true;
6318
6460
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
@@ -6343,41 +6485,48 @@ function OhhwellsBridge() {
6343
6485
  useEffect3(() => {
6344
6486
  if (!subdomain || isEditMode) return;
6345
6487
  let debounceTimer = null;
6488
+ let observer = null;
6346
6489
  const applyFromCache = () => {
6347
6490
  const content = contentCache.get(subdomain);
6348
6491
  if (!content) return;
6349
6492
  retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
6350
- for (const [key, val] of Object.entries(content)) {
6351
- if (key === "__ohw_sections") continue;
6352
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6353
- if (el.dataset.ohwEditable === "image") {
6354
- const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
6355
- if (img && img.src !== val) img.src = val;
6356
- } else if (el.dataset.ohwEditable === "bg-image") {
6357
- const next = `url('${val}')`;
6358
- if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
6359
- } else if (el.dataset.ohwEditable === "link") {
6360
- applyLinkHref(el, val);
6361
- } else if (el.innerHTML !== val) {
6362
- el.innerHTML = val;
6363
- }
6364
- });
6365
- applyLinkByKey(key, val);
6493
+ observer?.disconnect();
6494
+ try {
6495
+ for (const [key, val] of Object.entries(content)) {
6496
+ if (key === "__ohw_sections") continue;
6497
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6498
+ if (el.dataset.ohwEditable === "image") {
6499
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
6500
+ if (img && img.src !== val) img.src = val;
6501
+ } else if (el.dataset.ohwEditable === "bg-image") {
6502
+ const next = `url('${val}')`;
6503
+ if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
6504
+ } else if (el.dataset.ohwEditable === "link") {
6505
+ applyLinkHref(el, val);
6506
+ } else if (el.innerHTML !== val) {
6507
+ el.innerHTML = val;
6508
+ }
6509
+ });
6510
+ applyLinkByKey(key, val);
6511
+ }
6512
+ } finally {
6513
+ observer?.observe(document.body, { childList: true, subtree: true });
6366
6514
  }
6515
+ enforceLinkHrefs();
6367
6516
  };
6368
6517
  const scheduleApply = () => {
6369
6518
  if (debounceTimer) clearTimeout(debounceTimer);
6370
6519
  debounceTimer = setTimeout(applyFromCache, 150);
6371
6520
  };
6372
6521
  scheduleApply();
6373
- const observer = new MutationObserver(scheduleApply);
6522
+ observer = new MutationObserver(scheduleApply);
6374
6523
  observer.observe(document.body, { childList: true, subtree: true });
6375
6524
  return () => {
6376
- observer.disconnect();
6525
+ observer?.disconnect();
6377
6526
  if (debounceTimer) clearTimeout(debounceTimer);
6378
6527
  };
6379
6528
  }, [subdomain, isEditMode, pathname]);
6380
- useLayoutEffect(() => {
6529
+ useLayoutEffect2(() => {
6381
6530
  const el = document.getElementById("ohw-loader");
6382
6531
  if (!el) return;
6383
6532
  const visible = Boolean(subdomain) && fetchState !== "done";
@@ -6497,6 +6646,11 @@ function OhhwellsBridge() {
6497
6646
  if (target.closest("[data-ohw-state-toggle]")) return;
6498
6647
  if (target.closest("[data-ohw-max-badge]")) return;
6499
6648
  if (isInsideLinkEditor(target)) return;
6649
+ if (target.closest('[data-ohw-edit-chrome], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
6650
+ e.preventDefault();
6651
+ e.stopPropagation();
6652
+ return;
6653
+ }
6500
6654
  const editable = target.closest("[data-ohw-editable]");
6501
6655
  if (editable) {
6502
6656
  if (editable.dataset.ohwEditable === "link") {
@@ -6573,14 +6727,18 @@ function OhhwellsBridge() {
6573
6727
  const selected = selectedElRef.current;
6574
6728
  if (selected && (selected === editable || selected.contains(editable))) return;
6575
6729
  if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
6576
- editable.setAttribute("data-ohw-hovered", "");
6730
+ const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
6731
+ hoverTarget.setAttribute("data-ohw-hovered", "");
6577
6732
  }
6578
6733
  };
6579
6734
  const handleMouseOut = (e) => {
6580
6735
  const editable = e.target.closest("[data-ohw-editable]");
6581
6736
  if (!editable) return;
6737
+ const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
6738
+ if (related?.closest("[data-ohw-drag-handle-container]")) return;
6582
6739
  if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image") {
6583
- editable.removeAttribute("data-ohw-hovered");
6740
+ const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
6741
+ hoverTarget.removeAttribute("data-ohw-hovered");
6584
6742
  }
6585
6743
  };
6586
6744
  const toProbeCoords = (clientX, clientY, fromParentViewport) => {
@@ -6695,11 +6853,20 @@ function OhhwellsBridge() {
6695
6853
  return;
6696
6854
  }
6697
6855
  }
6856
+ if (activeElRef.current) {
6857
+ if (hoveredImageRef.current) {
6858
+ hoveredImageRef.current = null;
6859
+ hoveredImageHasTextOverlapRef.current = false;
6860
+ resumeAnimTracks();
6861
+ postToParentRef.current({ type: "ow:image-unhover" });
6862
+ }
6863
+ return;
6864
+ }
6698
6865
  const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
6699
6866
  if (imgEl) {
6700
6867
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
6701
6868
  const topEl = document.elementFromPoint(x, y);
6702
- if (topEl?.closest("[data-ohw-toolbar]")) {
6869
+ if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
6703
6870
  if (hoveredImageRef.current) {
6704
6871
  hoveredImageRef.current = null;
6705
6872
  resumeAnimTracks();
@@ -6992,7 +7159,7 @@ function OhhwellsBridge() {
6992
7159
  const el = e.target;
6993
7160
  const key = el.dataset.ohwKey;
6994
7161
  if (!key) return;
6995
- if (el === activeElRef.current) setToolbarRect(el.getBoundingClientRect());
7162
+ if (el === activeElRef.current) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6996
7163
  const maxLen = el.dataset.ohwMaxLength ? parseInt(el.dataset.ohwMaxLength, 10) : null;
6997
7164
  if (maxLen) {
6998
7165
  const current = el.innerText.replace(/\n$/, "").length;
@@ -7055,6 +7222,7 @@ function OhhwellsBridge() {
7055
7222
  sectionsLoadedRef.current = true;
7056
7223
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
7057
7224
  }
7225
+ enforceLinkHrefs();
7058
7226
  postToParentRef.current({ type: "ow:hydrate-done" });
7059
7227
  };
7060
7228
  window.addEventListener("message", handleHydrate);
@@ -7221,7 +7389,7 @@ function OhhwellsBridge() {
7221
7389
  };
7222
7390
  const applyToolbarPos = (rect) => {
7223
7391
  const ps = parentScrollRef.current;
7224
- const approxW = toolbarVariantRef.current === "link-action" ? 120 : 330;
7392
+ const approxW = toolbarVariantRef.current === "link-action" ? 112 : 306;
7225
7393
  if (toolbarElRef.current) {
7226
7394
  const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
7227
7395
  toolbarElRef.current.style.top = `${top}px`;
@@ -7400,7 +7568,7 @@ function OhhwellsBridge() {
7400
7568
  const handleCommand = useCallback2((cmd) => {
7401
7569
  document.execCommand(cmd, false);
7402
7570
  activeElRef.current?.focus();
7403
- if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
7571
+ if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
7404
7572
  refreshActiveCommandsRef.current();
7405
7573
  }, []);
7406
7574
  const handleStateChange = useCallback2((state) => {
@@ -7454,10 +7622,18 @@ function OhhwellsBridge() {
7454
7622
  linkPopoverOpenRef.current = linkPopover !== null;
7455
7623
  return bridgeRoot ? createPortal(
7456
7624
  /* @__PURE__ */ jsxs9(Fragment3, { children: [
7457
- /* @__PURE__ */ jsx16("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
7625
+ /* @__PURE__ */ jsx17("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
7458
7626
  toolbarRect && /* @__PURE__ */ jsxs9(Fragment3, { children: [
7459
- /* @__PURE__ */ jsx16(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
7460
- toolbarVariant === "rich-text" && /* @__PURE__ */ jsx16(
7627
+ /* @__PURE__ */ jsx17(
7628
+ EditGlowChrome,
7629
+ {
7630
+ rect: toolbarRect,
7631
+ elRef: glowElRef,
7632
+ reorderHrefKey,
7633
+ dragDisabled: reorderDragDisabled
7634
+ }
7635
+ ),
7636
+ toolbarVariant === "rich-text" && /* @__PURE__ */ jsx17(
7461
7637
  FloatingToolbar,
7462
7638
  {
7463
7639
  variant: "rich-text",
@@ -7470,7 +7646,7 @@ function OhhwellsBridge() {
7470
7646
  onEditLink: openLinkPopoverForActive
7471
7647
  }
7472
7648
  ),
7473
- toolbarVariant === "link-action" && /* @__PURE__ */ jsx16(
7649
+ toolbarVariant === "link-action" && /* @__PURE__ */ jsx17(
7474
7650
  FloatingToolbar,
7475
7651
  {
7476
7652
  variant: "link-action",
@@ -7509,7 +7685,7 @@ function OhhwellsBridge() {
7509
7685
  ]
7510
7686
  }
7511
7687
  ),
7512
- toggleState && /* @__PURE__ */ jsx16(
7688
+ toggleState && /* @__PURE__ */ jsx17(
7513
7689
  StateToggle,
7514
7690
  {
7515
7691
  rect: toggleState.rect,
@@ -7525,8 +7701,8 @@ function OhhwellsBridge() {
7525
7701
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
7526
7702
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
7527
7703
  children: [
7528
- /* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7529
- /* @__PURE__ */ jsx16(
7704
+ /* @__PURE__ */ jsx17("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7705
+ /* @__PURE__ */ jsx17(
7530
7706
  Badge,
7531
7707
  {
7532
7708
  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",
@@ -7539,11 +7715,11 @@ function OhhwellsBridge() {
7539
7715
  children: "Add Section"
7540
7716
  }
7541
7717
  ),
7542
- /* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7718
+ /* @__PURE__ */ jsx17("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7543
7719
  ]
7544
7720
  }
7545
7721
  ),
7546
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx16(
7722
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx17(
7547
7723
  LinkPopover,
7548
7724
  {
7549
7725
  panelRef: linkPopoverPanelRef,
@@ -7558,12 +7734,38 @@ function OhhwellsBridge() {
7558
7734
  onSubmit: handleLinkPopoverSubmit
7559
7735
  },
7560
7736
  `${linkPopover.key}-${pathname}`
7561
- ) : null
7737
+ ) : null,
7738
+ sectionGap && /* @__PURE__ */ jsxs9(
7739
+ "div",
7740
+ {
7741
+ "data-ohw-section-insert-line": "",
7742
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
7743
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
7744
+ children: [
7745
+ /* @__PURE__ */ jsx17("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7746
+ /* @__PURE__ */ jsx17(
7747
+ Badge,
7748
+ {
7749
+ 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",
7750
+ onClick: () => {
7751
+ window.parent.postMessage(
7752
+ { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
7753
+ "*"
7754
+ );
7755
+ },
7756
+ children: "Add Section"
7757
+ }
7758
+ ),
7759
+ /* @__PURE__ */ jsx17("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7760
+ ]
7761
+ }
7762
+ )
7562
7763
  ] }),
7563
7764
  bridgeRoot
7564
7765
  ) : null;
7565
7766
  }
7566
7767
  export {
7768
+ DragHandle,
7567
7769
  LinkEditorPanel,
7568
7770
  LinkPopover,
7569
7771
  OhhwellsBridge,
@@ -7574,6 +7776,7 @@ export {
7574
7776
  buildTarget,
7575
7777
  filterAvailablePages,
7576
7778
  getEditModeInitialState,
7779
+ isEditSessionActive,
7577
7780
  isValidUrl,
7578
7781
  parseTarget,
7579
7782
  toggleVariants,