@ohhwells/bridge 0.1.30 → 0.1.31

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
@@ -4489,26 +4489,135 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
4489
4489
  tick();
4490
4490
  }
4491
4491
 
4492
- // src/ui/link-modal/calcPopoverPos.ts
4493
- function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
4494
- const GAP = 8;
4495
- const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
4496
- const visibleTop = rect.top - canvasTopInIframe;
4497
- let top = rect.top - GAP;
4498
- let transform = "translateX(-50%) translateY(-100%)";
4499
- if (visibleTop < approxH + GAP) {
4500
- top = rect.bottom + GAP;
4501
- transform = "translateX(-50%)";
4502
- }
4503
- const rawLeft = rect.left + rect.width / 2;
4504
- const left = Math.max(GAP + approxW / 2, Math.min(rawLeft, window.innerWidth - GAP - approxW / 2));
4505
- return { top, left, transform };
4506
- }
4507
-
4508
- // src/ui/button.tsx
4492
+ // src/ui/dialog.tsx
4509
4493
  var React3 = __toESM(require("react"), 1);
4510
4494
  var import_radix_ui3 = require("radix-ui");
4495
+
4496
+ // src/ui/icons.tsx
4511
4497
  var import_jsx_runtime5 = require("react/jsx-runtime");
4498
+ 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" })
4502
+ ] });
4503
+ }
4504
+ 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" }) });
4506
+ }
4507
+ 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" })
4511
+ ] });
4512
+ }
4513
+ 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" })
4518
+ ] });
4519
+ }
4520
+ 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" })
4524
+ ] });
4525
+ }
4526
+ 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" })
4531
+ ] });
4532
+ }
4533
+
4534
+ // src/ui/dialog.tsx
4535
+ var import_jsx_runtime6 = require("react/jsx-runtime");
4536
+ function Dialog2({ ...props }) {
4537
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui3.Dialog.Root, { "data-slot": "dialog", ...props });
4538
+ }
4539
+ function DialogPortal({ ...props }) {
4540
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui3.Dialog.Portal, { ...props });
4541
+ }
4542
+ function DialogOverlay({
4543
+ className,
4544
+ ...props
4545
+ }) {
4546
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4547
+ import_radix_ui3.Dialog.Overlay,
4548
+ {
4549
+ "data-slot": "dialog-overlay",
4550
+ "data-ohw-link-modal-root": "",
4551
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
4552
+ ...props
4553
+ }
4554
+ );
4555
+ }
4556
+ var DialogContent = React3.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4557
+ 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)(
4561
+ import_radix_ui3.Dialog.Content,
4562
+ {
4563
+ ref,
4564
+ "data-slot": "dialog-content",
4565
+ className: cn(
4566
+ positionMode,
4567
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[483px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden",
4568
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
4569
+ container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
4570
+ className
4571
+ ),
4572
+ onMouseDown: (e) => e.stopPropagation(),
4573
+ ...props,
4574
+ children: [
4575
+ children,
4576
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4577
+ import_radix_ui3.Dialog.Close,
4578
+ {
4579
+ type: "button",
4580
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
4581
+ "aria-label": "Close",
4582
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconX, { "aria-hidden": true })
4583
+ }
4584
+ ) : null
4585
+ ]
4586
+ }
4587
+ )
4588
+ ] });
4589
+ });
4590
+ DialogContent.displayName = import_radix_ui3.Dialog.Content.displayName;
4591
+ function DialogHeader({ className, ...props }) {
4592
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4593
+ }
4594
+ function DialogFooter({ className, ...props }) {
4595
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
4596
+ }
4597
+ var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4598
+ import_radix_ui3.Dialog.Title,
4599
+ {
4600
+ ref,
4601
+ className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
4602
+ ...props
4603
+ }
4604
+ ));
4605
+ DialogTitle.displayName = import_radix_ui3.Dialog.Title.displayName;
4606
+ var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4607
+ import_radix_ui3.Dialog.Description,
4608
+ {
4609
+ ref,
4610
+ className: cn("text-sm text-muted-foreground", className),
4611
+ ...props
4612
+ }
4613
+ ));
4614
+ DialogDescription.displayName = import_radix_ui3.Dialog.Description.displayName;
4615
+ var DialogClose = import_radix_ui3.Dialog.Close;
4616
+
4617
+ // src/ui/button.tsx
4618
+ var React4 = __toESM(require("react"), 1);
4619
+ var import_radix_ui4 = require("radix-ui");
4620
+ var import_jsx_runtime7 = require("react/jsx-runtime");
4512
4621
  var buttonVariants = cva(
4513
4622
  "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",
4514
4623
  {
@@ -4529,10 +4638,10 @@ var buttonVariants = cva(
4529
4638
  }
4530
4639
  }
4531
4640
  );
4532
- var Button = React3.forwardRef(
4641
+ var Button = React4.forwardRef(
4533
4642
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4534
- const Comp = asChild ? import_radix_ui3.Slot.Root : "button";
4535
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4643
+ const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
4644
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4536
4645
  Comp,
4537
4646
  {
4538
4647
  ref,
@@ -4545,76 +4654,38 @@ var Button = React3.forwardRef(
4545
4654
  );
4546
4655
  Button.displayName = "Button";
4547
4656
 
4548
- // src/ui/icons.tsx
4549
- var import_jsx_runtime6 = require("react/jsx-runtime");
4550
- function IconX({ className, ...props }) {
4551
- 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: [
4552
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M18 6 6 18" }),
4553
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 6 12 12" })
4554
- ] });
4555
- }
4556
- function IconChevronDown({ className, ...props }) {
4557
- 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" }) });
4558
- }
4559
- function IconFile({ className, ...props }) {
4560
- 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: [
4561
- /* @__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" }),
4562
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4563
- ] });
4564
- }
4565
- function IconInfo({ className, ...props }) {
4566
- 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: [
4567
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
4568
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 16v-4" }),
4569
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 8h.01" })
4570
- ] });
4571
- }
4572
- function IconArrowRight({ className, ...props }) {
4573
- 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: [
4574
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5 12h14" }),
4575
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m12 5 7 7-7 7" })
4576
- ] });
4577
- }
4578
- function IconSection({ className, ...props }) {
4579
- 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: [
4580
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4581
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4582
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4583
- ] });
4584
- }
4585
-
4586
4657
  // src/ui/link-modal/DestinationBreadcrumb.tsx
4587
- var import_jsx_runtime7 = require("react/jsx-runtime");
4658
+ var import_jsx_runtime8 = require("react/jsx-runtime");
4588
4659
  function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
4589
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
4590
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
4591
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-3", children: [
4592
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", children: [
4593
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4594
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
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 })
4595
4666
  ] }),
4596
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
4597
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
4598
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4599
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
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 })
4600
4671
  ] })
4601
4672
  ] })
4602
4673
  ] });
4603
4674
  }
4604
4675
 
4605
4676
  // src/ui/link-modal/SectionTreeItem.tsx
4606
- var import_jsx_runtime8 = require("react/jsx-runtime");
4677
+ var import_jsx_runtime9 = require("react/jsx-runtime");
4607
4678
  function SectionTreeItem({ section, onSelect, selected }) {
4608
4679
  const interactive = Boolean(onSelect);
4609
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
4610
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
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)(
4611
4682
  "div",
4612
4683
  {
4613
4684
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
4614
4685
  "aria-hidden": true
4615
4686
  }
4616
4687
  ),
4617
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4688
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4618
4689
  "div",
4619
4690
  {
4620
4691
  role: interactive ? "button" : void 0,
@@ -4632,8 +4703,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
4632
4703
  interactive && selected && "border-primary"
4633
4704
  ),
4634
4705
  children: [
4635
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4636
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
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 })
4637
4708
  ]
4638
4709
  }
4639
4710
  )
@@ -4641,11 +4712,11 @@ function SectionTreeItem({ section, onSelect, selected }) {
4641
4712
  }
4642
4713
  function SectionPickerList({ sections, onSelect }) {
4643
4714
  if (sections.length === 0) {
4644
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4715
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4645
4716
  }
4646
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col gap-1", children: [
4647
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
4648
- sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SectionTreeItem, { section, onSelect }, section.id))
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))
4649
4720
  ] });
4650
4721
  }
4651
4722
 
@@ -4653,11 +4724,11 @@ function SectionPickerList({ sections, onSelect }) {
4653
4724
  var import_react3 = require("react");
4654
4725
 
4655
4726
  // src/ui/input.tsx
4656
- var React4 = __toESM(require("react"), 1);
4657
- var import_jsx_runtime9 = require("react/jsx-runtime");
4658
- var Input = React4.forwardRef(
4727
+ var React5 = __toESM(require("react"), 1);
4728
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4729
+ var Input = React5.forwardRef(
4659
4730
  ({ className, type, ...props }, ref) => {
4660
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4731
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4661
4732
  "input",
4662
4733
  {
4663
4734
  type,
@@ -4675,11 +4746,11 @@ var Input = React4.forwardRef(
4675
4746
  Input.displayName = "Input";
4676
4747
 
4677
4748
  // src/ui/label.tsx
4678
- var import_radix_ui4 = require("radix-ui");
4679
- var import_jsx_runtime10 = require("react/jsx-runtime");
4749
+ var import_radix_ui5 = require("radix-ui");
4750
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4680
4751
  function Label({ className, ...props }) {
4681
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4682
- import_radix_ui4.Label.Root,
4752
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4753
+ import_radix_ui5.Label.Root,
4683
4754
  {
4684
4755
  "data-slot": "label",
4685
4756
  className: cn("text-sm font-medium leading-5 text-foreground", className),
@@ -4688,36 +4759,6 @@ function Label({ className, ...props }) {
4688
4759
  );
4689
4760
  }
4690
4761
 
4691
- // src/ui/popover.tsx
4692
- var import_radix_ui5 = require("radix-ui");
4693
- var import_jsx_runtime11 = require("react/jsx-runtime");
4694
- function Popover({ ...props }) {
4695
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui5.Popover.Root, { "data-slot": "popover", ...props });
4696
- }
4697
- function PopoverTrigger({ ...props }) {
4698
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui5.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
4699
- }
4700
- function PopoverContent({
4701
- className,
4702
- align = "start",
4703
- sideOffset = 6,
4704
- ...props
4705
- }) {
4706
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui5.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4707
- import_radix_ui5.Popover.Content,
4708
- {
4709
- "data-slot": "popover-content",
4710
- align,
4711
- sideOffset,
4712
- className: cn(
4713
- "z-[2147483647] w-[var(--radix-popover-trigger-width)] overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg outline-none",
4714
- className
4715
- ),
4716
- ...props
4717
- }
4718
- ) });
4719
- }
4720
-
4721
4762
  // src/ui/link-modal/UrlOrPageInput.tsx
4722
4763
  var import_jsx_runtime12 = require("react/jsx-runtime");
4723
4764
  function FieldChevron({ onClick }) {
@@ -4770,8 +4811,8 @@ function UrlOrPageInput({
4770
4811
  );
4771
4812
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
4772
4813
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4773
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
4774
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
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: [
4775
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,
4776
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)(
4777
4818
  Input,
@@ -4804,20 +4845,28 @@ function UrlOrPageInput({
4804
4845
  }
4805
4846
  ) : null,
4806
4847
  !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
4807
- ] }) }),
4808
- filteredPages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4809
- "button",
4848
+ ] }),
4849
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4850
+ "div",
4810
4851
  {
4811
- type: "button",
4812
- 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",
4813
- onClick: () => onPageSelect(page),
4814
- children: [
4815
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
4816
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate", children: page.title })
4817
- ]
4818
- },
4819
- page.path
4820
- )) })
4852
+ "data-ohw-link-page-dropdown": "",
4853
+ 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
+ onMouseDown: (e) => e.preventDefault(),
4855
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4856
+ "button",
4857
+ {
4858
+ type: "button",
4859
+ 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
+ onClick: () => onPageSelect(page),
4861
+ 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 })
4864
+ ]
4865
+ },
4866
+ page.path
4867
+ ))
4868
+ }
4869
+ ) : null
4821
4870
  ] }),
4822
4871
  urlError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4823
4872
  ] });
@@ -5012,17 +5061,16 @@ function LinkEditorPanel({
5012
5061
  });
5013
5062
  const isCancel = state.secondaryLabel === "Cancel";
5014
5063
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
5015
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5064
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5016
5065
  "button",
5017
5066
  {
5018
5067
  type: "button",
5019
5068
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5020
- onClick: state.handleClose,
5021
5069
  "aria-label": "Close",
5022
5070
  children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
5023
5071
  }
5024
- ),
5025
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
5072
+ ) }),
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 }) }),
5026
5074
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5027
5075
  state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5028
5076
  DestinationBreadcrumb,
@@ -5053,7 +5101,7 @@ function LinkEditorPanel({
5053
5101
  state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5054
5102
  state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5055
5103
  ] }),
5056
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
5104
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5057
5105
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5058
5106
  Button,
5059
5107
  {
@@ -5090,26 +5138,32 @@ function LinkEditorPanel({
5090
5138
  // src/ui/link-modal/LinkPopover.tsx
5091
5139
  var import_jsx_runtime14 = require("react/jsx-runtime");
5092
5140
  function LinkPopover({
5093
- rect,
5094
- parentScroll,
5141
+ open = true,
5095
5142
  panelRef,
5143
+ portalContainer,
5144
+ onClose,
5096
5145
  ...editorProps
5097
5146
  }) {
5098
- const { top, left, transform } = calcPopoverPos(rect, parentScroll);
5099
5147
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5100
- "div",
5148
+ Dialog2,
5101
5149
  {
5102
- ref: panelRef,
5103
- "data-ohw-link-popover-root": "",
5104
- "data-ohw-link-modal-root": "",
5105
- "data-ohw-bridge": "",
5106
- className: cn(
5107
- "pointer-events-auto fixed z-[2147483647] flex w-[483px] max-w-[calc(100vw-16px)] flex-col overflow-hidden",
5108
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none"
5109
- ),
5110
- style: { top, left, transform },
5111
- onMouseDown: (e) => e.stopPropagation(),
5112
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LinkEditorPanel, { ...editorProps, open: true })
5150
+ open,
5151
+ onOpenChange: (next) => {
5152
+ if (!next) onClose?.();
5153
+ },
5154
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5155
+ DialogContent,
5156
+ {
5157
+ ref: panelRef,
5158
+ container: portalContainer,
5159
+ "data-ohw-link-popover-root": "",
5160
+ "data-ohw-link-modal-root": "",
5161
+ "data-ohw-bridge": "",
5162
+ showCloseButton: false,
5163
+ 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 })
5165
+ }
5166
+ )
5113
5167
  }
5114
5168
  );
5115
5169
  }
@@ -5455,7 +5509,7 @@ function applyLinkByKey(key, val) {
5455
5509
  }
5456
5510
  function isInsideLinkEditor(target) {
5457
5511
  return Boolean(
5458
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest('[data-slot="popover-content"]')
5512
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
5459
5513
  );
5460
5514
  }
5461
5515
  function getHrefKeyFromElement(el) {
@@ -5466,6 +5520,15 @@ function getHrefKeyFromElement(el) {
5466
5520
  if (!key) return null;
5467
5521
  return { anchor, key };
5468
5522
  }
5523
+ function isNavbarButton(el) {
5524
+ return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
5525
+ }
5526
+ function clearHrefKeyHover(anchor) {
5527
+ anchor.removeAttribute("data-ohw-hovered");
5528
+ anchor.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5529
+ el.removeAttribute("data-ohw-hovered");
5530
+ });
5531
+ }
5469
5532
  function collectSections() {
5470
5533
  return collectSectionsFromDom();
5471
5534
  }
@@ -5627,40 +5690,190 @@ function GlowFrame({ rect, elRef }) {
5627
5690
  }
5628
5691
  );
5629
5692
  }
5630
- function calcToolbarPos(rect, parentScroll) {
5693
+ function getIframeVisibleClip(parentScroll) {
5694
+ if (!parentScroll) return null;
5695
+ const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
5696
+ const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
5697
+ const clipBottom = Math.min(
5698
+ window.innerHeight,
5699
+ visibleCanvasTop + canvasH - iframeOffsetTop
5700
+ );
5701
+ return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
5702
+ }
5703
+ function applyVisibleViewport(el, parentScroll) {
5704
+ const clip = getIframeVisibleClip(parentScroll);
5705
+ const top = clip?.top ?? 0;
5706
+ const height = clip ? clip.bottom - clip.top : window.innerHeight;
5707
+ el.style.top = `${top}px`;
5708
+ el.style.height = `${height}px`;
5709
+ }
5710
+ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
5711
+ const isAbove = transform.includes("translateY(-100%)");
5712
+ let visualTop = isAbove ? top - approxH : top;
5713
+ let visualBottom = isAbove ? top : top + approxH;
5714
+ const minTop = clip.top + gap;
5715
+ const maxBottom = clip.bottom - gap;
5716
+ if (visualTop >= minTop && visualBottom <= maxBottom) {
5717
+ return { top, transform };
5718
+ }
5719
+ const belowTop = rect.bottom + gap;
5720
+ if (belowTop + approxH <= maxBottom && belowTop >= minTop) {
5721
+ return { top: belowTop, transform: "translateX(-50%)" };
5722
+ }
5723
+ const aboveTop = rect.top - gap;
5724
+ if (aboveTop - approxH >= minTop && aboveTop <= maxBottom) {
5725
+ return { top: aboveTop, transform: "translateX(-50%) translateY(-100%)" };
5726
+ }
5727
+ if (belowTop + approxH <= maxBottom) {
5728
+ return { top: belowTop, transform: "translateX(-50%)" };
5729
+ }
5730
+ const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
5731
+ return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
5732
+ }
5733
+ function calcToolbarPos(rect, parentScroll, approxW = 330) {
5631
5734
  const GAP = 8;
5632
5735
  const APPROX_H = 36;
5633
- const APPROX_W = 330;
5736
+ const APPROX_W = approxW;
5737
+ const clip = getIframeVisibleClip(parentScroll);
5738
+ const clipTop = clip?.top ?? 0;
5634
5739
  const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
5635
5740
  const visibleTop = rect.top - canvasTopInIframe;
5636
5741
  const visibleBottom = rect.bottom - canvasTopInIframe;
5637
5742
  const isTall = rect.height > APPROX_H + GAP;
5743
+ const spaceAbove = rect.top - (clipTop + GAP);
5744
+ const fitsAbove = spaceAbove >= APPROX_H + GAP;
5638
5745
  let top;
5639
5746
  let transform;
5640
- if (visibleTop > 0 || !isTall) {
5747
+ if ((visibleTop > 0 || !isTall) && fitsAbove) {
5641
5748
  top = rect.top - GAP;
5642
5749
  transform = "translateX(-50%) translateY(-100%)";
5750
+ } else if (visibleTop > 0 || !isTall) {
5751
+ top = rect.bottom + GAP;
5752
+ transform = "translateX(-50%)";
5643
5753
  } else if (visibleBottom > APPROX_H + GAP) {
5644
- top = canvasTopInIframe + GAP;
5754
+ top = clipTop + GAP;
5645
5755
  transform = "translateX(-50%)";
5646
5756
  } else {
5647
5757
  top = rect.bottom + GAP;
5648
5758
  transform = "translateX(-50%)";
5649
5759
  }
5760
+ if (clip) {
5761
+ const clamped = clampToolbarToClip(top, transform, rect, clip, APPROX_H, GAP);
5762
+ top = clamped.top;
5763
+ transform = clamped.transform;
5764
+ }
5650
5765
  const rawLeft = rect.left + rect.width / 2;
5651
5766
  const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
5652
5767
  return { top, left, transform };
5653
5768
  }
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" })
5772
+ ] });
5654
5773
  function FloatingToolbar({
5655
5774
  rect,
5656
5775
  parentScroll,
5657
5776
  elRef,
5777
+ variant = "rich-text",
5658
5778
  onCommand,
5659
5779
  activeCommands,
5660
5780
  showEditLink,
5661
5781
  onEditLink
5662
5782
  }) {
5663
- const { top, left, transform } = calcToolbarPos(rect, parentScroll);
5783
+ const approxW = variant === "link-action" ? 120 : 330;
5784
+ const { top, left, transform } = calcToolbarPos(rect, parentScroll, approxW);
5785
+ if (variant === "link-action") {
5786
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
5787
+ "div",
5788
+ {
5789
+ ref: elRef,
5790
+ "data-ohw-toolbar": "",
5791
+ style: {
5792
+ position: "fixed",
5793
+ top,
5794
+ left,
5795
+ transform,
5796
+ zIndex: 2147483647,
5797
+ background: "#fff",
5798
+ border: "1px solid #E7E5E4",
5799
+ borderRadius: 6,
5800
+ boxShadow: "0px 2px 4px -2px rgba(0,0,0,.1),0px 4px 6px -1px rgba(0,0,0,.1)",
5801
+ display: "flex",
5802
+ alignItems: "center",
5803
+ padding: 4,
5804
+ gap: 6,
5805
+ fontFamily: "sans-serif",
5806
+ pointerEvents: "auto",
5807
+ whiteSpace: "nowrap"
5808
+ },
5809
+ onMouseDown: (e) => e.stopPropagation(),
5810
+ children: [
5811
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5812
+ "button",
5813
+ {
5814
+ type: "button",
5815
+ title: "Edit link",
5816
+ onMouseDown: (e) => {
5817
+ e.preventDefault();
5818
+ e.stopPropagation();
5819
+ onEditLink?.();
5820
+ },
5821
+ onClick: (e) => {
5822
+ e.preventDefault();
5823
+ e.stopPropagation();
5824
+ },
5825
+ onMouseEnter: (e) => {
5826
+ e.currentTarget.style.background = "#F5F5F4";
5827
+ },
5828
+ onMouseLeave: (e) => {
5829
+ e.currentTarget.style.background = "transparent";
5830
+ },
5831
+ style: {
5832
+ display: "flex",
5833
+ alignItems: "center",
5834
+ justifyContent: "center",
5835
+ border: "none",
5836
+ background: "transparent",
5837
+ borderRadius: 4,
5838
+ cursor: "pointer",
5839
+ color: "#1C1917",
5840
+ flexShrink: 0,
5841
+ padding: 6
5842
+ },
5843
+ children: EDIT_LINK_ICON
5844
+ }
5845
+ ),
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)(
5848
+ "button",
5849
+ {
5850
+ type: "button",
5851
+ title: "Coming soon",
5852
+ disabled: true,
5853
+ style: {
5854
+ display: "flex",
5855
+ alignItems: "center",
5856
+ justifyContent: "center",
5857
+ border: "none",
5858
+ background: "transparent",
5859
+ borderRadius: 4,
5860
+ cursor: "not-allowed",
5861
+ color: "#A8A29E",
5862
+ flexShrink: 0,
5863
+ padding: 6,
5864
+ opacity: 0.6
5865
+ },
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" })
5870
+ ] })
5871
+ }
5872
+ )
5873
+ ]
5874
+ }
5875
+ );
5876
+ }
5664
5877
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
5665
5878
  "div",
5666
5879
  {
@@ -5768,10 +5981,7 @@ function FloatingToolbar({
5768
5981
  flexShrink: 0,
5769
5982
  padding: 6
5770
5983
  },
5771
- children: /* @__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: [
5772
- /* @__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" }),
5773
- /* @__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" })
5774
- ] })
5984
+ children: EDIT_LINK_ICON
5775
5985
  }
5776
5986
  ) : null
5777
5987
  ]
@@ -5862,9 +6072,17 @@ function OhhwellsBridge() {
5862
6072
  const [fetchState, setFetchState] = (0, import_react5.useState)("idle");
5863
6073
  const autoSaveTimers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
5864
6074
  const activeElRef = (0, import_react5.useRef)(null);
6075
+ const selectedElRef = (0, import_react5.useRef)(null);
5865
6076
  const originalContentRef = (0, import_react5.useRef)(null);
5866
6077
  const activeStateElRef = (0, import_react5.useRef)(null);
5867
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) => {
6082
+ visibleViewportRef.current = node;
6083
+ setDialogPortalContainer(node);
6084
+ if (node) applyVisibleViewport(node, parentScrollRef.current);
6085
+ }, []);
5868
6086
  const toolbarElRef = (0, import_react5.useRef)(null);
5869
6087
  const glowElRef = (0, import_react5.useRef)(null);
5870
6088
  const hoveredImageRef = (0, import_react5.useRef)(null);
@@ -5877,6 +6095,10 @@ function OhhwellsBridge() {
5877
6095
  });
5878
6096
  const deactivateRef = (0, import_react5.useRef)(() => {
5879
6097
  });
6098
+ const selectRef = (0, import_react5.useRef)(() => {
6099
+ });
6100
+ const deselectRef = (0, import_react5.useRef)(() => {
6101
+ });
5880
6102
  const refreshActiveCommandsRef = (0, import_react5.useRef)(() => {
5881
6103
  });
5882
6104
  const postToParentRef = (0, import_react5.useRef)(postToParent);
@@ -5884,6 +6106,9 @@ function OhhwellsBridge() {
5884
6106
  const sectionsLoadedRef = (0, import_react5.useRef)(false);
5885
6107
  const pendingScheduleConfigRequests = (0, import_react5.useRef)([]);
5886
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");
6111
+ toolbarVariantRef.current = toolbarVariant;
5887
6112
  const [toggleState, setToggleState] = (0, import_react5.useState)(null);
5888
6113
  const [maxBadge, setMaxBadge] = (0, import_react5.useState)(null);
5889
6114
  const [activeCommands, setActiveCommands] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
@@ -5928,7 +6153,33 @@ function OhhwellsBridge() {
5928
6153
  hoveredImageRef.current = null;
5929
6154
  hoveredImageHasTextOverlapRef.current = false;
5930
6155
  }
6156
+ hoveredGapRef.current = null;
6157
+ setSectionGap(null);
5931
6158
  postToParent({ type: "ow:image-unhover" });
6159
+ postToParent({ type: "ow:link-modal-lock", locked: true });
6160
+ const html = document.documentElement;
6161
+ const body = document.body;
6162
+ const prevHtmlOverflow = html.style.overflow;
6163
+ const prevBodyOverflow = body.style.overflow;
6164
+ html.style.overflow = "hidden";
6165
+ body.style.overflow = "hidden";
6166
+ const preventBackgroundScroll = (e) => {
6167
+ const target = e.target;
6168
+ if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
6169
+ return;
6170
+ }
6171
+ e.preventDefault();
6172
+ };
6173
+ const scrollOpts = { passive: false, capture: true };
6174
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6175
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6176
+ return () => {
6177
+ postToParent({ type: "ow:link-modal-lock", locked: false });
6178
+ html.style.overflow = prevHtmlOverflow;
6179
+ body.style.overflow = prevBodyOverflow;
6180
+ document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6181
+ document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6182
+ };
5932
6183
  }, [linkPopover, postToParent]);
5933
6184
  (0, import_react5.useEffect)(() => {
5934
6185
  if (!isEditMode) return;
@@ -5963,7 +6214,7 @@ function OhhwellsBridge() {
5963
6214
  }, [isEditMode]);
5964
6215
  (0, import_react5.useEffect)(() => {
5965
6216
  const update = () => {
5966
- const el = activeElRef.current;
6217
+ const el = activeElRef.current ?? selectedElRef.current;
5967
6218
  if (el) setToolbarRect(el.getBoundingClientRect());
5968
6219
  setToggleState((prev) => {
5969
6220
  if (!prev || !activeStateElRef.current) return prev;
@@ -6031,19 +6282,41 @@ function OhhwellsBridge() {
6031
6282
  }
6032
6283
  el.removeAttribute("contenteditable");
6033
6284
  activeElRef.current = null;
6034
- setToolbarRect(null);
6285
+ if (!selectedElRef.current) {
6286
+ setToolbarRect(null);
6287
+ setToolbarVariant("none");
6288
+ }
6035
6289
  setMaxBadge(null);
6036
6290
  setActiveCommands(/* @__PURE__ */ new Set());
6037
6291
  setToolbarShowEditLink(false);
6038
6292
  postToParent({ type: "ow:exit-edit" });
6039
6293
  }, [postToParent]);
6294
+ const deselect = (0, import_react5.useCallback)(() => {
6295
+ selectedElRef.current = null;
6296
+ if (!activeElRef.current) {
6297
+ setToolbarRect(null);
6298
+ setToolbarVariant("none");
6299
+ }
6300
+ }, []);
6301
+ const select = (0, import_react5.useCallback)((anchor) => {
6302
+ if (!isNavbarButton(anchor)) return;
6303
+ if (activeElRef.current) deactivate();
6304
+ selectedElRef.current = anchor;
6305
+ clearHrefKeyHover(anchor);
6306
+ setToolbarVariant("link-action");
6307
+ setToolbarRect(anchor.getBoundingClientRect());
6308
+ setToolbarShowEditLink(false);
6309
+ setActiveCommands(/* @__PURE__ */ new Set());
6310
+ }, [deactivate]);
6040
6311
  const activate = (0, import_react5.useCallback)((el) => {
6041
6312
  if (activeElRef.current === el) return;
6313
+ selectedElRef.current = null;
6042
6314
  deactivate();
6043
6315
  if (hoveredImageRef.current) {
6044
6316
  hoveredImageRef.current = null;
6045
6317
  postToParentRef.current({ type: "ow:image-unhover" });
6046
6318
  }
6319
+ setToolbarVariant("rich-text");
6047
6320
  el.setAttribute("contenteditable", "true");
6048
6321
  el.removeAttribute("data-ohw-hovered");
6049
6322
  activeElRef.current = el;
@@ -6056,6 +6329,8 @@ function OhhwellsBridge() {
6056
6329
  }, [deactivate, postToParent]);
6057
6330
  activateRef.current = activate;
6058
6331
  deactivateRef.current = deactivate;
6332
+ selectRef.current = select;
6333
+ deselectRef.current = deselect;
6059
6334
  (0, import_react5.useLayoutEffect)(() => {
6060
6335
  if (!subdomain || isEditMode) {
6061
6336
  setFetchState("done");
@@ -6159,6 +6434,12 @@ function OhhwellsBridge() {
6159
6434
  (0, import_react5.useEffect)(() => {
6160
6435
  postToParent({ type: "ow:navigation", path: pathname });
6161
6436
  }, [pathname, postToParent]);
6437
+ (0, import_react5.useEffect)(() => {
6438
+ if (!isEditMode) return;
6439
+ setLinkPopover(null);
6440
+ deselectRef.current();
6441
+ deactivateRef.current();
6442
+ }, [pathname, isEditMode]);
6162
6443
  (0, import_react5.useEffect)(() => {
6163
6444
  if (!isEditMode) return;
6164
6445
  const measure = () => {
@@ -6269,12 +6550,12 @@ function OhhwellsBridge() {
6269
6550
  if (editable.dataset.ohwEditable === "link") {
6270
6551
  e.preventDefault();
6271
6552
  e.stopPropagation();
6553
+ deselectRef.current();
6272
6554
  deactivateRef.current();
6273
6555
  bumpLinkPopoverGrace();
6274
6556
  setLinkPopoverRef.current({
6275
6557
  key: editable.dataset.ohwKey ?? "",
6276
- target: getLinkHref(editable),
6277
- rect: editable.getBoundingClientRect()
6558
+ target: getLinkHref(editable)
6278
6559
  });
6279
6560
  return;
6280
6561
  }
@@ -6284,11 +6565,31 @@ function OhhwellsBridge() {
6284
6565
  postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
6285
6566
  return;
6286
6567
  }
6568
+ const hrefCtx = getHrefKeyFromElement(editable);
6569
+ if (hrefCtx && isNavbarButton(hrefCtx.anchor)) {
6570
+ e.preventDefault();
6571
+ e.stopPropagation();
6572
+ const { anchor: anchor2 } = hrefCtx;
6573
+ if (selectedElRef.current === anchor2) {
6574
+ activateRef.current(editable);
6575
+ return;
6576
+ }
6577
+ selectRef.current(anchor2);
6578
+ return;
6579
+ }
6287
6580
  e.preventDefault();
6288
6581
  e.stopPropagation();
6289
6582
  activateRef.current(editable);
6290
6583
  return;
6291
6584
  }
6585
+ const hrefAnchor = target.closest("[data-ohw-href-key]");
6586
+ if (hrefAnchor && isNavbarButton(hrefAnchor)) {
6587
+ e.preventDefault();
6588
+ e.stopPropagation();
6589
+ if (selectedElRef.current === hrefAnchor) return;
6590
+ selectRef.current(hrefAnchor);
6591
+ return;
6592
+ }
6292
6593
  if (activeElRef.current) {
6293
6594
  const sel = window.getSelection();
6294
6595
  if (sel && !sel.isCollapsed) {
@@ -6303,15 +6604,22 @@ function OhhwellsBridge() {
6303
6604
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
6304
6605
  if (hrefCtx && (hrefCtx.anchor === target || hrefCtx.anchor.contains(target))) return;
6305
6606
  }
6607
+ if (linkPopoverOpenRef.current && selectedElRef.current) {
6608
+ const selected = selectedElRef.current;
6609
+ if (selected === target || selected.contains(target)) return;
6610
+ }
6306
6611
  if (linkPopoverOpenRef.current) {
6307
6612
  setLinkPopoverRef.current(null);
6308
6613
  return;
6309
6614
  }
6615
+ deselectRef.current();
6310
6616
  deactivateRef.current();
6311
6617
  };
6312
6618
  const handleMouseOver = (e) => {
6313
6619
  const editable = e.target.closest("[data-ohw-editable]");
6314
6620
  if (!editable) return;
6621
+ const selected = selectedElRef.current;
6622
+ if (selected && (selected === editable || selected.contains(editable))) return;
6315
6623
  if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
6316
6624
  editable.setAttribute("data-ohw-hovered", "");
6317
6625
  }
@@ -6447,7 +6755,7 @@ function OhhwellsBridge() {
6447
6755
  }
6448
6756
  return;
6449
6757
  }
6450
- if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]")) {
6758
+ if (topEl?.closest("[data-ohw-link-popover-root]") || topEl?.closest("[data-ohw-link-modal-root]") || topEl?.closest("[data-ohw-link-page-dropdown]")) {
6451
6759
  if (hoveredImageRef.current) {
6452
6760
  hoveredImageRef.current = null;
6453
6761
  hoveredImageHasTextOverlapRef.current = false;
@@ -6575,6 +6883,13 @@ function OhhwellsBridge() {
6575
6883
  }
6576
6884
  };
6577
6885
  const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
6886
+ if (linkPopoverOpenRef.current) {
6887
+ if (hoveredGapRef.current) {
6888
+ hoveredGapRef.current = null;
6889
+ setSectionGap(null);
6890
+ }
6891
+ return;
6892
+ }
6578
6893
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
6579
6894
  const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
6580
6895
  const ZONE = 20;
@@ -6798,6 +7113,7 @@ function OhhwellsBridge() {
6798
7113
  setLinkPopoverRef.current(null);
6799
7114
  return;
6800
7115
  }
7116
+ deselectRef.current();
6801
7117
  deactivateRef.current();
6802
7118
  };
6803
7119
  window.addEventListener("message", handleDeactivate);
@@ -6806,6 +7122,10 @@ function OhhwellsBridge() {
6806
7122
  setLinkPopoverRef.current(null);
6807
7123
  return;
6808
7124
  }
7125
+ if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
7126
+ deselectRef.current();
7127
+ return;
7128
+ }
6809
7129
  if (e.key !== "Escape") return;
6810
7130
  const el = activeElRef.current;
6811
7131
  if (el && originalContentRef.current !== null) {
@@ -6815,13 +7135,16 @@ function OhhwellsBridge() {
6815
7135
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
6816
7136
  }
6817
7137
  }
7138
+ deselectRef.current();
6818
7139
  deactivateRef.current();
6819
7140
  };
6820
7141
  const handleScroll = () => {
6821
- if (activeElRef.current) {
6822
- const r2 = activeElRef.current.getBoundingClientRect();
7142
+ const focusEl = activeElRef.current ?? selectedElRef.current;
7143
+ if (focusEl) {
7144
+ const r2 = focusEl.getBoundingClientRect();
6823
7145
  applyToolbarPos(r2);
6824
- setMaxBadge((prev) => prev ? { ...prev, rect: r2 } : null);
7146
+ setToolbarRect(r2);
7147
+ setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
6825
7148
  }
6826
7149
  if (activeStateElRef.current) {
6827
7150
  const rect = activeStateElRef.current.getBoundingClientRect();
@@ -6946,8 +7269,9 @@ function OhhwellsBridge() {
6946
7269
  };
6947
7270
  const applyToolbarPos = (rect) => {
6948
7271
  const ps = parentScrollRef.current;
7272
+ const approxW = toolbarVariantRef.current === "link-action" ? 120 : 330;
6949
7273
  if (toolbarElRef.current) {
6950
- const { top, left, transform } = calcToolbarPos(rect, ps);
7274
+ const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
6951
7275
  toolbarElRef.current.style.top = `${top}px`;
6952
7276
  toolbarElRef.current.style.left = `${left}px`;
6953
7277
  toolbarElRef.current.style.transform = transform;
@@ -6962,7 +7286,11 @@ function OhhwellsBridge() {
6962
7286
  if (e.data?.type !== "ow:parent-scroll") return;
6963
7287
  const { iframeOffsetTop, headerH, canvasH } = e.data;
6964
7288
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
6965
- if (activeElRef.current) applyToolbarPos(activeElRef.current.getBoundingClientRect());
7289
+ if (visibleViewportRef.current) {
7290
+ applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
7291
+ }
7292
+ const focusEl = activeElRef.current ?? selectedElRef.current;
7293
+ if (focusEl) applyToolbarPos(focusEl.getBoundingClientRect());
6966
7294
  };
6967
7295
  const handleClickAt = (e) => {
6968
7296
  if (e.data?.type !== "ow:click-at") return;
@@ -7012,6 +7340,12 @@ function OhhwellsBridge() {
7012
7340
  window.addEventListener("message", handleParentScroll);
7013
7341
  window.addEventListener("message", handlePointerSync);
7014
7342
  window.addEventListener("message", handleClickAt);
7343
+ const handleViewportResize = () => {
7344
+ if (visibleViewportRef.current) {
7345
+ applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
7346
+ }
7347
+ };
7348
+ window.addEventListener("resize", handleViewportResize, { passive: true });
7015
7349
  document.addEventListener("click", handleClick, true);
7016
7350
  document.addEventListener("paste", handlePaste, true);
7017
7351
  document.addEventListener("input", handleInput, true);
@@ -7049,6 +7383,7 @@ function OhhwellsBridge() {
7049
7383
  window.removeEventListener("message", handleAnimLock);
7050
7384
  window.removeEventListener("message", handleCanvasHeight);
7051
7385
  window.removeEventListener("message", handleParentScroll);
7386
+ window.removeEventListener("resize", handleViewportResize);
7052
7387
  window.removeEventListener("message", handlePointerSync);
7053
7388
  window.removeEventListener("message", handleClickAt);
7054
7389
  window.removeEventListener("message", handleHydrate);
@@ -7136,10 +7471,22 @@ function OhhwellsBridge() {
7136
7471
  bumpLinkPopoverGrace();
7137
7472
  setLinkPopover({
7138
7473
  key: hrefCtx.key,
7139
- target: getLinkHref(hrefCtx.anchor),
7140
- rect: hrefCtx.anchor.getBoundingClientRect()
7474
+ target: getLinkHref(hrefCtx.anchor)
7141
7475
  });
7142
- }, []);
7476
+ deactivate();
7477
+ }, [deactivate]);
7478
+ const openLinkPopoverForSelected = (0, import_react5.useCallback)(() => {
7479
+ const anchor = selectedElRef.current;
7480
+ if (!anchor) return;
7481
+ const key = anchor.getAttribute("data-ohw-href-key");
7482
+ if (!key) return;
7483
+ bumpLinkPopoverGrace();
7484
+ setLinkPopover({
7485
+ key,
7486
+ target: getLinkHref(anchor)
7487
+ });
7488
+ deselect();
7489
+ }, [deselect]);
7143
7490
  const handleLinkPopoverSubmit = (0, import_react5.useCallback)(
7144
7491
  (target) => {
7145
7492
  if (!linkPopover) return;
@@ -7155,11 +7502,13 @@ function OhhwellsBridge() {
7155
7502
  linkPopoverOpenRef.current = linkPopover !== null;
7156
7503
  return bridgeRoot ? (0, import_react_dom2.createPortal)(
7157
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 }),
7158
7506
  toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
7159
7507
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
7160
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7508
+ toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7161
7509
  FloatingToolbar,
7162
7510
  {
7511
+ variant: "rich-text",
7163
7512
  rect: toolbarRect,
7164
7513
  parentScroll: parentScrollRef.current,
7165
7514
  elRef: toolbarElRef,
@@ -7168,6 +7517,18 @@ function OhhwellsBridge() {
7168
7517
  showEditLink,
7169
7518
  onEditLink: openLinkPopoverForActive
7170
7519
  }
7520
+ ),
7521
+ toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7522
+ FloatingToolbar,
7523
+ {
7524
+ variant: "link-action",
7525
+ rect: toolbarRect,
7526
+ parentScroll: parentScrollRef.current,
7527
+ elRef: toolbarElRef,
7528
+ onCommand: handleCommand,
7529
+ activeCommands,
7530
+ onEditLink: openLinkPopoverForSelected
7531
+ }
7171
7532
  )
7172
7533
  ] }),
7173
7534
  maxBadge && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
@@ -7230,12 +7591,11 @@ function OhhwellsBridge() {
7230
7591
  ]
7231
7592
  }
7232
7593
  ),
7233
- linkPopover ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7594
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7234
7595
  LinkPopover,
7235
7596
  {
7236
- rect: linkPopover.rect,
7237
- parentScroll: parentScrollRef.current,
7238
7597
  panelRef: linkPopoverPanelRef,
7598
+ portalContainer: dialogPortalContainer,
7239
7599
  open: true,
7240
7600
  mode: "edit",
7241
7601
  pages: sitePages,
@@ -7244,7 +7604,8 @@ function OhhwellsBridge() {
7244
7604
  initialTarget: linkPopover.target,
7245
7605
  onClose: closeLinkPopover,
7246
7606
  onSubmit: handleLinkPopoverSubmit
7247
- }
7607
+ },
7608
+ `${linkPopover.key}-${pathname}`
7248
7609
  ) : null
7249
7610
  ] }),
7250
7611
  bridgeRoot