@ohhwells/bridge 0.1.36-next.49 → 0.1.36-next.50

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,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React8, { useCallback as useCallback3, useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState5 } from "react";
4
+ import React9, { useCallback as useCallback3, useEffect as useEffect4, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState5 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
@@ -4580,6 +4580,220 @@ function ItemInteractionLayer({
4580
4580
  );
4581
4581
  }
4582
4582
 
4583
+ // src/ui/MediaOverlay.tsx
4584
+ import * as React6 from "react";
4585
+ import { Film, ImageIcon, Pause, Play, Volume2, VolumeX } from "lucide-react";
4586
+
4587
+ // src/ui/button.tsx
4588
+ import * as React5 from "react";
4589
+ import { Slot } from "radix-ui";
4590
+ import { jsx as jsx10 } from "react/jsx-runtime";
4591
+ var buttonVariants = cva(
4592
+ "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",
4593
+ {
4594
+ variants: {
4595
+ variant: {
4596
+ default: "bg-primary text-primary-foreground hover:opacity-90",
4597
+ outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
4598
+ ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
4599
+ },
4600
+ size: {
4601
+ default: "h-9",
4602
+ sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
4603
+ }
4604
+ },
4605
+ defaultVariants: {
4606
+ variant: "default",
4607
+ size: "default"
4608
+ }
4609
+ }
4610
+ );
4611
+ var Button = React5.forwardRef(
4612
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
4613
+ const Comp = asChild ? Slot.Root : "button";
4614
+ return /* @__PURE__ */ jsx10(
4615
+ Comp,
4616
+ {
4617
+ ref,
4618
+ "data-slot": "button",
4619
+ className: cn(buttonVariants({ variant, size, className })),
4620
+ ...props
4621
+ }
4622
+ );
4623
+ }
4624
+ );
4625
+ Button.displayName = "Button";
4626
+
4627
+ // src/ui/MediaOverlay.tsx
4628
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
4629
+ var MEDIA_UPLOAD_FADE_MS = 300;
4630
+ var VIDEO_SETTINGS_BAR_INSET = 8;
4631
+ var OVERLAY_BUTTON_STYLE = {
4632
+ pointerEvents: "auto",
4633
+ fontFamily: "Inter, sans-serif",
4634
+ fontSize: 12,
4635
+ color: "#000"
4636
+ };
4637
+ var SKELETON_CSS = `
4638
+ @keyframes ohw-media-shimmer {
4639
+ 0% { transform: translateX(-100%); }
4640
+ 100% { transform: translateX(100%); }
4641
+ }
4642
+ [data-ohw-media-skeleton] {
4643
+ overflow: hidden;
4644
+ background-color: #d1d5db; /* tailwind gray-300 */
4645
+ }
4646
+ [data-ohw-media-skeleton]::after {
4647
+ content: "";
4648
+ position: absolute;
4649
+ inset: 0;
4650
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
4651
+ animation: ohw-media-shimmer 1.4s infinite;
4652
+ }
4653
+ `;
4654
+ function MediaOverlay({
4655
+ hover,
4656
+ isUploading,
4657
+ fadingOut = false,
4658
+ onFadeOutComplete,
4659
+ onReplace,
4660
+ onVideoSettingsChange
4661
+ }) {
4662
+ const { rect } = hover;
4663
+ const skeletonRef = React6.useRef(null);
4664
+ const isVideo = hover.elementType === "video";
4665
+ const autoplay = hover.videoAutoplay ?? true;
4666
+ const muted = hover.videoMuted ?? true;
4667
+ const box = {
4668
+ position: "fixed",
4669
+ top: rect.top,
4670
+ left: rect.left,
4671
+ width: rect.width,
4672
+ height: rect.height,
4673
+ zIndex: 2147483646
4674
+ };
4675
+ React6.useEffect(() => {
4676
+ if (!isUploading || !fadingOut || !skeletonRef.current) return;
4677
+ const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4678
+ duration: MEDIA_UPLOAD_FADE_MS,
4679
+ easing: "ease-out",
4680
+ fill: "forwards"
4681
+ });
4682
+ anim.finished.then(() => onFadeOutComplete?.(hover.key)).catch(() => onFadeOutComplete?.(hover.key));
4683
+ return () => anim.cancel();
4684
+ }, [isUploading, fadingOut, onFadeOutComplete, hover.key]);
4685
+ if (isUploading) {
4686
+ return /* @__PURE__ */ jsx11(
4687
+ "div",
4688
+ {
4689
+ ref: skeletonRef,
4690
+ "data-ohw-bridge": "",
4691
+ "data-ohw-media-overlay": "",
4692
+ "data-ohw-media-skeleton": "",
4693
+ "aria-hidden": true,
4694
+ style: { ...box, pointerEvents: "none" },
4695
+ children: /* @__PURE__ */ jsx11("style", { children: SKELETON_CSS })
4696
+ }
4697
+ );
4698
+ }
4699
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs5(
4700
+ "div",
4701
+ {
4702
+ "data-ohw-bridge": "",
4703
+ "data-ohw-media-overlay": "",
4704
+ className: "flex items-center justify-center gap-1.5",
4705
+ style: {
4706
+ position: "fixed",
4707
+ top: rect.top + VIDEO_SETTINGS_BAR_INSET,
4708
+ left: rect.left,
4709
+ width: rect.width,
4710
+ zIndex: 2147483647,
4711
+ pointerEvents: "auto"
4712
+ },
4713
+ onClick: (e) => e.stopPropagation(),
4714
+ children: [
4715
+ /* @__PURE__ */ jsx11(
4716
+ Button,
4717
+ {
4718
+ "data-ohw-media-overlay": "",
4719
+ variant: "outline",
4720
+ size: "sm",
4721
+ className: "cursor-pointer hover:bg-background",
4722
+ style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4723
+ "aria-label": autoplay ? "Disable autoplay" : "Enable autoplay",
4724
+ title: autoplay ? "Autoplay on \u2014 click to disable" : "Autoplay off \u2014 click to enable",
4725
+ onMouseDown: (e) => e.preventDefault(),
4726
+ onClick: (e) => {
4727
+ e.stopPropagation();
4728
+ onVideoSettingsChange?.(hover.key, { autoplay: !autoplay });
4729
+ },
4730
+ children: autoplay ? /* @__PURE__ */ jsx11(Pause, { size: 14 }) : /* @__PURE__ */ jsx11(Play, { size: 14 })
4731
+ }
4732
+ ),
4733
+ /* @__PURE__ */ jsx11(
4734
+ Button,
4735
+ {
4736
+ "data-ohw-media-overlay": "",
4737
+ variant: "outline",
4738
+ size: "sm",
4739
+ className: "cursor-pointer hover:bg-background",
4740
+ style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4741
+ "aria-label": muted ? "Unmute video" : "Mute video",
4742
+ title: muted ? "Muted \u2014 click to unmute" : "Sound on \u2014 click to mute",
4743
+ onMouseDown: (e) => e.preventDefault(),
4744
+ onClick: (e) => {
4745
+ e.stopPropagation();
4746
+ onVideoSettingsChange?.(hover.key, { muted: !muted });
4747
+ },
4748
+ children: muted ? /* @__PURE__ */ jsx11(VolumeX, { size: 14 }) : /* @__PURE__ */ jsx11(Volume2, { size: 14 })
4749
+ }
4750
+ )
4751
+ ]
4752
+ }
4753
+ ) : null;
4754
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
4755
+ settingsBar,
4756
+ /* @__PURE__ */ jsx11(
4757
+ "div",
4758
+ {
4759
+ "data-ohw-bridge": "",
4760
+ "data-ohw-media-overlay": "",
4761
+ className: "flex items-center justify-center cursor-pointer",
4762
+ style: {
4763
+ ...box,
4764
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4765
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4766
+ // in-document, pointer-events does it natively. The button below opts back in, so
4767
+ // Replace still works.
4768
+ pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4769
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4770
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4771
+ },
4772
+ onClick: () => onReplace(hover.key),
4773
+ children: /* @__PURE__ */ jsxs5(
4774
+ Button,
4775
+ {
4776
+ "data-ohw-media-overlay": "",
4777
+ variant: "outline",
4778
+ size: "sm",
4779
+ className: "gap-1.5 cursor-pointer hover:bg-background",
4780
+ style: OVERLAY_BUTTON_STYLE,
4781
+ onMouseDown: (e) => e.preventDefault(),
4782
+ onClick: (e) => {
4783
+ e.stopPropagation();
4784
+ onReplace(hover.key);
4785
+ },
4786
+ children: [
4787
+ isVideo ? /* @__PURE__ */ jsx11(Film, { size: 14 }) : /* @__PURE__ */ jsx11(ImageIcon, { size: 14 }),
4788
+ isVideo ? "Replace video" : "Replace image"
4789
+ ]
4790
+ }
4791
+ )
4792
+ }
4793
+ )
4794
+ ] });
4795
+ }
4796
+
4583
4797
  // src/OhhwellsBridge.tsx
4584
4798
  import { createPortal } from "react-dom";
4585
4799
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
@@ -4859,60 +5073,60 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
4859
5073
  }
4860
5074
 
4861
5075
  // src/ui/dialog.tsx
4862
- import * as React5 from "react";
5076
+ import * as React7 from "react";
4863
5077
  import { Dialog as DialogPrimitive } from "radix-ui";
4864
5078
 
4865
5079
  // src/ui/icons.tsx
4866
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
5080
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
4867
5081
  function IconX({ className, ...props }) {
4868
- return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4869
- /* @__PURE__ */ jsx10("path", { d: "M18 6 6 18" }),
4870
- /* @__PURE__ */ jsx10("path", { d: "m6 6 12 12" })
5082
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5083
+ /* @__PURE__ */ jsx12("path", { d: "M18 6 6 18" }),
5084
+ /* @__PURE__ */ jsx12("path", { d: "m6 6 12 12" })
4871
5085
  ] });
4872
5086
  }
4873
5087
  function IconChevronDown({ className, ...props }) {
4874
- return /* @__PURE__ */ jsx10("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx10("path", { d: "m6 9 6 6 6-6" }) });
5088
+ return /* @__PURE__ */ jsx12("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) });
4875
5089
  }
4876
5090
  function IconFile({ className, ...props }) {
4877
- return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4878
- /* @__PURE__ */ jsx10("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4879
- /* @__PURE__ */ jsx10("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
5091
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5092
+ /* @__PURE__ */ jsx12("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
5093
+ /* @__PURE__ */ jsx12("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4880
5094
  ] });
4881
5095
  }
4882
5096
  function IconInfo({ className, ...props }) {
4883
- return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4884
- /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "10" }),
4885
- /* @__PURE__ */ jsx10("path", { d: "M12 16v-4" }),
4886
- /* @__PURE__ */ jsx10("path", { d: "M12 8h.01" })
5097
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5098
+ /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "10" }),
5099
+ /* @__PURE__ */ jsx12("path", { d: "M12 16v-4" }),
5100
+ /* @__PURE__ */ jsx12("path", { d: "M12 8h.01" })
4887
5101
  ] });
4888
5102
  }
4889
5103
  function IconArrowRight({ className, ...props }) {
4890
- return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4891
- /* @__PURE__ */ jsx10("path", { d: "M5 12h14" }),
4892
- /* @__PURE__ */ jsx10("path", { d: "m12 5 7 7-7 7" })
5104
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5105
+ /* @__PURE__ */ jsx12("path", { d: "M5 12h14" }),
5106
+ /* @__PURE__ */ jsx12("path", { d: "m12 5 7 7-7 7" })
4893
5107
  ] });
4894
5108
  }
4895
5109
  function IconSection({ className, ...props }) {
4896
- return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4897
- /* @__PURE__ */ jsx10("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4898
- /* @__PURE__ */ jsx10("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4899
- /* @__PURE__ */ jsx10("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
5110
+ return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5111
+ /* @__PURE__ */ jsx12("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
5112
+ /* @__PURE__ */ jsx12("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
5113
+ /* @__PURE__ */ jsx12("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4900
5114
  ] });
4901
5115
  }
4902
5116
 
4903
5117
  // src/ui/dialog.tsx
4904
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
5118
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
4905
5119
  function Dialog2({ ...props }) {
4906
- return /* @__PURE__ */ jsx11(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
5120
+ return /* @__PURE__ */ jsx13(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
4907
5121
  }
4908
5122
  function DialogPortal({ ...props }) {
4909
- return /* @__PURE__ */ jsx11(DialogPrimitive.Portal, { ...props });
5123
+ return /* @__PURE__ */ jsx13(DialogPrimitive.Portal, { ...props });
4910
5124
  }
4911
5125
  function DialogOverlay({
4912
5126
  className,
4913
5127
  ...props
4914
5128
  }) {
4915
- return /* @__PURE__ */ jsx11(
5129
+ return /* @__PURE__ */ jsx13(
4916
5130
  DialogPrimitive.Overlay,
4917
5131
  {
4918
5132
  "data-slot": "dialog-overlay",
@@ -4922,11 +5136,11 @@ function DialogOverlay({
4922
5136
  }
4923
5137
  );
4924
5138
  }
4925
- var DialogContent = React5.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
5139
+ var DialogContent = React7.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4926
5140
  const positionMode = container ? "absolute" : "fixed";
4927
- return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
4928
- /* @__PURE__ */ jsx11(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4929
- /* @__PURE__ */ jsxs6(
5141
+ return /* @__PURE__ */ jsxs7(DialogPortal, { container: container ?? void 0, children: [
5142
+ /* @__PURE__ */ jsx13(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5143
+ /* @__PURE__ */ jsxs7(
4930
5144
  DialogPrimitive.Content,
4931
5145
  {
4932
5146
  ref,
@@ -4942,13 +5156,13 @@ var DialogContent = React5.forwardRef(({ className, children, showCloseButton =
4942
5156
  ...props,
4943
5157
  children: [
4944
5158
  children,
4945
- showCloseButton ? /* @__PURE__ */ jsx11(
5159
+ showCloseButton ? /* @__PURE__ */ jsx13(
4946
5160
  DialogPrimitive.Close,
4947
5161
  {
4948
5162
  type: "button",
4949
5163
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
4950
5164
  "aria-label": "Close",
4951
- children: /* @__PURE__ */ jsx11(IconX, { "aria-hidden": true })
5165
+ children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
4952
5166
  }
4953
5167
  ) : null
4954
5168
  ]
@@ -4958,12 +5172,12 @@ var DialogContent = React5.forwardRef(({ className, children, showCloseButton =
4958
5172
  });
4959
5173
  DialogContent.displayName = DialogPrimitive.Content.displayName;
4960
5174
  function DialogHeader({ className, ...props }) {
4961
- return /* @__PURE__ */ jsx11("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5175
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex flex-col gap-1.5", className), ...props });
4962
5176
  }
4963
5177
  function DialogFooter({ className, ...props }) {
4964
- return /* @__PURE__ */ jsx11("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5178
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
4965
5179
  }
4966
- var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
5180
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
4967
5181
  DialogPrimitive.Title,
4968
5182
  {
4969
5183
  ref,
@@ -4972,7 +5186,7 @@ var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE
4972
5186
  }
4973
5187
  ));
4974
5188
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
4975
- var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
5189
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
4976
5190
  DialogPrimitive.Description,
4977
5191
  {
4978
5192
  ref,
@@ -4983,78 +5197,38 @@ var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @
4983
5197
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
4984
5198
  var DialogClose = DialogPrimitive.Close;
4985
5199
 
4986
- // src/ui/button.tsx
4987
- import * as React6 from "react";
4988
- import { Slot } from "radix-ui";
4989
- import { jsx as jsx12 } from "react/jsx-runtime";
4990
- var buttonVariants = cva(
4991
- "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",
4992
- {
4993
- variants: {
4994
- variant: {
4995
- default: "bg-primary text-primary-foreground hover:opacity-90",
4996
- outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
4997
- ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
4998
- },
4999
- size: {
5000
- default: "h-9",
5001
- sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
5002
- }
5003
- },
5004
- defaultVariants: {
5005
- variant: "default",
5006
- size: "default"
5007
- }
5008
- }
5009
- );
5010
- var Button = React6.forwardRef(
5011
- ({ className, variant, size, asChild = false, ...props }, ref) => {
5012
- const Comp = asChild ? Slot.Root : "button";
5013
- return /* @__PURE__ */ jsx12(
5014
- Comp,
5015
- {
5016
- ref,
5017
- "data-slot": "button",
5018
- className: cn(buttonVariants({ variant, size, className })),
5019
- ...props
5020
- }
5021
- );
5022
- }
5023
- );
5024
- Button.displayName = "Button";
5025
-
5026
5200
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5027
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5201
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5028
5202
  function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5029
- return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2", children: [
5030
- /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5031
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3", children: [
5032
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
5033
- /* @__PURE__ */ jsx13(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5034
- /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5203
+ return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-2", children: [
5204
+ /* @__PURE__ */ jsx14("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5205
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3", children: [
5206
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
5207
+ /* @__PURE__ */ jsx14(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5208
+ /* @__PURE__ */ jsx14("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5035
5209
  ] }),
5036
- /* @__PURE__ */ jsx13(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5037
- /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5038
- /* @__PURE__ */ jsx13(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5039
- /* @__PURE__ */ jsx13("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5210
+ /* @__PURE__ */ jsx14(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5211
+ /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5212
+ /* @__PURE__ */ jsx14(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5213
+ /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5040
5214
  ] })
5041
5215
  ] })
5042
5216
  ] });
5043
5217
  }
5044
5218
 
5045
5219
  // src/ui/link-modal/SectionTreeItem.tsx
5046
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5220
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
5047
5221
  function SectionTreeItem({ section, onSelect, selected }) {
5048
5222
  const interactive = Boolean(onSelect);
5049
- return /* @__PURE__ */ jsxs8("div", { className: "flex h-9 w-full items-end pl-3", children: [
5050
- /* @__PURE__ */ jsx14(
5223
+ return /* @__PURE__ */ jsxs9("div", { className: "flex h-9 w-full items-end pl-3", children: [
5224
+ /* @__PURE__ */ jsx15(
5051
5225
  "div",
5052
5226
  {
5053
5227
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5054
5228
  "aria-hidden": true
5055
5229
  }
5056
5230
  ),
5057
- /* @__PURE__ */ jsxs8(
5231
+ /* @__PURE__ */ jsxs9(
5058
5232
  "div",
5059
5233
  {
5060
5234
  role: interactive ? "button" : void 0,
@@ -5072,8 +5246,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
5072
5246
  interactive && selected && "border-primary"
5073
5247
  ),
5074
5248
  children: [
5075
- /* @__PURE__ */ jsx14(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5076
- /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5249
+ /* @__PURE__ */ jsx15(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5250
+ /* @__PURE__ */ jsx15("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5077
5251
  ]
5078
5252
  }
5079
5253
  )
@@ -5081,23 +5255,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
5081
5255
  }
5082
5256
  function SectionPickerList({ sections, onSelect }) {
5083
5257
  if (sections.length === 0) {
5084
- return /* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5258
+ return /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5085
5259
  }
5086
- return /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1", children: [
5087
- /* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5088
- sections.map((section) => /* @__PURE__ */ jsx14(SectionTreeItem, { section, onSelect }, section.id))
5260
+ return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
5261
+ /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5262
+ sections.map((section) => /* @__PURE__ */ jsx15(SectionTreeItem, { section, onSelect }, section.id))
5089
5263
  ] });
5090
5264
  }
5091
5265
 
5092
5266
  // src/ui/link-modal/UrlOrPageInput.tsx
5093
- import { useId as useId2, useRef as useRef2, useState as useState3 } from "react";
5267
+ import { useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5094
5268
 
5095
5269
  // src/ui/input.tsx
5096
- import * as React7 from "react";
5097
- import { jsx as jsx15 } from "react/jsx-runtime";
5098
- var Input = React7.forwardRef(
5270
+ import * as React8 from "react";
5271
+ import { jsx as jsx16 } from "react/jsx-runtime";
5272
+ var Input = React8.forwardRef(
5099
5273
  ({ className, type, ...props }, ref) => {
5100
- return /* @__PURE__ */ jsx15(
5274
+ return /* @__PURE__ */ jsx16(
5101
5275
  "input",
5102
5276
  {
5103
5277
  type,
@@ -5116,9 +5290,9 @@ Input.displayName = "Input";
5116
5290
 
5117
5291
  // src/ui/label.tsx
5118
5292
  import { Label as LabelPrimitive } from "radix-ui";
5119
- import { jsx as jsx16 } from "react/jsx-runtime";
5293
+ import { jsx as jsx17 } from "react/jsx-runtime";
5120
5294
  function Label({ className, ...props }) {
5121
- return /* @__PURE__ */ jsx16(
5295
+ return /* @__PURE__ */ jsx17(
5122
5296
  LabelPrimitive.Root,
5123
5297
  {
5124
5298
  "data-slot": "label",
@@ -5129,9 +5303,9 @@ function Label({ className, ...props }) {
5129
5303
  }
5130
5304
 
5131
5305
  // src/ui/link-modal/UrlOrPageInput.tsx
5132
- import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
5306
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5133
5307
  function FieldChevron({ onClick }) {
5134
- return /* @__PURE__ */ jsx17(
5308
+ return /* @__PURE__ */ jsx18(
5135
5309
  "button",
5136
5310
  {
5137
5311
  type: "button",
@@ -5139,7 +5313,7 @@ function FieldChevron({ onClick }) {
5139
5313
  onClick,
5140
5314
  "aria-label": "Open page list",
5141
5315
  tabIndex: -1,
5142
- children: /* @__PURE__ */ jsx17(IconChevronDown, {})
5316
+ children: /* @__PURE__ */ jsx18(IconChevronDown, {})
5143
5317
  }
5144
5318
  );
5145
5319
  }
@@ -5156,7 +5330,7 @@ function UrlOrPageInput({
5156
5330
  urlError
5157
5331
  }) {
5158
5332
  const inputId = useId2();
5159
- const inputRef = useRef2(null);
5333
+ const inputRef = useRef3(null);
5160
5334
  const [isFocused, setIsFocused] = useState3(false);
5161
5335
  const openDropdown = () => {
5162
5336
  if (readOnly || filteredPages.length === 0) return;
@@ -5178,12 +5352,12 @@ function UrlOrPageInput({
5178
5352
  "data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5179
5353
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5180
5354
  );
5181
- return /* @__PURE__ */ jsxs9("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5182
- /* @__PURE__ */ jsx17(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5183
- /* @__PURE__ */ jsxs9("div", { className: "relative w-full", children: [
5184
- /* @__PURE__ */ jsxs9("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5185
- selectedPage ? /* @__PURE__ */ jsx17("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx17(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5186
- readOnly ? /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx17(
5355
+ return /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5356
+ /* @__PURE__ */ jsx18(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5357
+ /* @__PURE__ */ jsxs10("div", { className: "relative w-full", children: [
5358
+ /* @__PURE__ */ jsxs10("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5359
+ selectedPage ? /* @__PURE__ */ jsx18("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx18(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5360
+ readOnly ? /* @__PURE__ */ jsx18("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx18(
5187
5361
  Input,
5188
5362
  {
5189
5363
  ref: inputRef,
@@ -5202,7 +5376,7 @@ function UrlOrPageInput({
5202
5376
  )
5203
5377
  }
5204
5378
  ),
5205
- selectedPage && !readOnly ? /* @__PURE__ */ jsx17(
5379
+ selectedPage && !readOnly ? /* @__PURE__ */ jsx18(
5206
5380
  "button",
5207
5381
  {
5208
5382
  type: "button",
@@ -5210,26 +5384,26 @@ function UrlOrPageInput({
5210
5384
  onMouseDown: clearSelection,
5211
5385
  "aria-label": "Clear selected page",
5212
5386
  tabIndex: -1,
5213
- children: /* @__PURE__ */ jsx17(IconX, { "aria-hidden": true })
5387
+ children: /* @__PURE__ */ jsx18(IconX, { "aria-hidden": true })
5214
5388
  }
5215
5389
  ) : null,
5216
- !readOnly ? /* @__PURE__ */ jsx17(FieldChevron, { onClick: toggleDropdown }) : null
5390
+ !readOnly ? /* @__PURE__ */ jsx18(FieldChevron, { onClick: toggleDropdown }) : null
5217
5391
  ] }),
5218
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx17(
5392
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx18(
5219
5393
  "div",
5220
5394
  {
5221
5395
  "data-ohw-link-page-dropdown": "",
5222
5396
  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",
5223
5397
  onMouseDown: (e) => e.preventDefault(),
5224
- children: filteredPages.map((page) => /* @__PURE__ */ jsxs9(
5398
+ children: filteredPages.map((page) => /* @__PURE__ */ jsxs10(
5225
5399
  "button",
5226
5400
  {
5227
5401
  type: "button",
5228
5402
  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",
5229
5403
  onClick: () => onPageSelect(page),
5230
5404
  children: [
5231
- /* @__PURE__ */ jsx17(IconFile, { className: "shrink-0", "aria-hidden": true }),
5232
- /* @__PURE__ */ jsx17("span", { className: "truncate", children: page.title })
5405
+ /* @__PURE__ */ jsx18(IconFile, { className: "shrink-0", "aria-hidden": true }),
5406
+ /* @__PURE__ */ jsx18("span", { className: "truncate", children: page.title })
5233
5407
  ]
5234
5408
  },
5235
5409
  page.path
@@ -5237,12 +5411,12 @@ function UrlOrPageInput({
5237
5411
  }
5238
5412
  ) : null
5239
5413
  ] }),
5240
- urlError ? /* @__PURE__ */ jsx17("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5414
+ urlError ? /* @__PURE__ */ jsx18("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5241
5415
  ] });
5242
5416
  }
5243
5417
 
5244
5418
  // src/ui/link-modal/useLinkModalState.ts
5245
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
5419
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
5246
5420
  function useLinkModalState({
5247
5421
  open,
5248
5422
  mode,
@@ -5272,7 +5446,7 @@ function useLinkModalState({
5272
5446
  setDropdownOpen(false);
5273
5447
  setUrlError("");
5274
5448
  }, []);
5275
- useEffect2(() => {
5449
+ useEffect3(() => {
5276
5450
  if (!open) return;
5277
5451
  if (mode === "edit" && initialTarget) {
5278
5452
  const { pageRoute } = parseTarget(initialTarget);
@@ -5405,7 +5579,7 @@ function useLinkModalState({
5405
5579
  }
5406
5580
 
5407
5581
  // src/ui/link-modal/LinkEditorPanel.tsx
5408
- import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5582
+ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5409
5583
  function LinkEditorPanel({
5410
5584
  open = true,
5411
5585
  mode = "create",
@@ -5429,25 +5603,25 @@ function LinkEditorPanel({
5429
5603
  onSubmit
5430
5604
  });
5431
5605
  const isCancel = state.secondaryLabel === "Cancel";
5432
- return /* @__PURE__ */ jsxs10(Fragment2, { children: [
5433
- /* @__PURE__ */ jsx18(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx18(
5606
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
5607
+ /* @__PURE__ */ jsx19(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx19(
5434
5608
  "button",
5435
5609
  {
5436
5610
  type: "button",
5437
5611
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5438
5612
  "aria-label": "Close",
5439
- children: /* @__PURE__ */ jsx18(IconX, { "aria-hidden": true })
5613
+ children: /* @__PURE__ */ jsx19(IconX, { "aria-hidden": true })
5440
5614
  }
5441
5615
  ) }),
5442
- /* @__PURE__ */ jsx18(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx18(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5443
- /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5444
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx18(
5616
+ /* @__PURE__ */ jsx19(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx19(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5617
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5618
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx19(
5445
5619
  DestinationBreadcrumb,
5446
5620
  {
5447
5621
  pageTitle: state.selectedPage.title,
5448
5622
  sectionLabel: state.selectedSection.label
5449
5623
  }
5450
- ) : /* @__PURE__ */ jsx18(
5624
+ ) : /* @__PURE__ */ jsx19(
5451
5625
  UrlOrPageInput,
5452
5626
  {
5453
5627
  value: state.searchValue,
@@ -5460,18 +5634,18 @@ function LinkEditorPanel({
5460
5634
  urlError: state.urlError
5461
5635
  }
5462
5636
  ),
5463
- state.showChooseSection ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col justify-center gap-2", children: [
5464
- /* @__PURE__ */ jsx18(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5465
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5466
- /* @__PURE__ */ jsx18(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5467
- /* @__PURE__ */ jsx18("span", { children: "Pick a section this link should scroll to." })
5637
+ state.showChooseSection ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col justify-center gap-2", children: [
5638
+ /* @__PURE__ */ jsx19(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5639
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5640
+ /* @__PURE__ */ jsx19(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5641
+ /* @__PURE__ */ jsx19("span", { children: "Pick a section this link should scroll to." })
5468
5642
  ] })
5469
5643
  ] }) : null,
5470
- state.showSectionPicker ? /* @__PURE__ */ jsx18(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5471
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx18(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5644
+ state.showSectionPicker ? /* @__PURE__ */ jsx19(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5645
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx19(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5472
5646
  ] }),
5473
- /* @__PURE__ */ jsxs10(DialogFooter, { className: "w-full px-6 pb-6", children: [
5474
- /* @__PURE__ */ jsx18(
5647
+ /* @__PURE__ */ jsxs11(DialogFooter, { className: "w-full px-6 pb-6", children: [
5648
+ /* @__PURE__ */ jsx19(
5475
5649
  Button,
5476
5650
  {
5477
5651
  type: "button",
@@ -5486,7 +5660,7 @@ function LinkEditorPanel({
5486
5660
  children: state.secondaryLabel
5487
5661
  }
5488
5662
  ),
5489
- /* @__PURE__ */ jsx18(
5663
+ /* @__PURE__ */ jsx19(
5490
5664
  Button,
5491
5665
  {
5492
5666
  type: "button",
@@ -5505,7 +5679,7 @@ function LinkEditorPanel({
5505
5679
  }
5506
5680
 
5507
5681
  // src/ui/link-modal/LinkPopover.tsx
5508
- import { jsx as jsx19 } from "react/jsx-runtime";
5682
+ import { jsx as jsx20 } from "react/jsx-runtime";
5509
5683
  function LinkPopover({
5510
5684
  open = true,
5511
5685
  panelRef,
@@ -5513,14 +5687,14 @@ function LinkPopover({
5513
5687
  onClose,
5514
5688
  ...editorProps
5515
5689
  }) {
5516
- return /* @__PURE__ */ jsx19(
5690
+ return /* @__PURE__ */ jsx20(
5517
5691
  Dialog2,
5518
5692
  {
5519
5693
  open,
5520
5694
  onOpenChange: (next) => {
5521
5695
  if (!next) onClose?.();
5522
5696
  },
5523
- children: /* @__PURE__ */ jsx19(
5697
+ children: /* @__PURE__ */ jsx20(
5524
5698
  DialogContent,
5525
5699
  {
5526
5700
  ref: panelRef,
@@ -5530,7 +5704,7 @@ function LinkPopover({
5530
5704
  "data-ohw-bridge": "",
5531
5705
  showCloseButton: false,
5532
5706
  className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5533
- children: /* @__PURE__ */ jsx19(LinkEditorPanel, { ...editorProps, open, onClose })
5707
+ children: /* @__PURE__ */ jsx20(LinkEditorPanel, { ...editorProps, open, onClose })
5534
5708
  }
5535
5709
  )
5536
5710
  }
@@ -5596,7 +5770,7 @@ function shouldUseDevFixtures() {
5596
5770
  }
5597
5771
 
5598
5772
  // src/ui/badge.tsx
5599
- import { jsx as jsx20 } from "react/jsx-runtime";
5773
+ import { jsx as jsx21 } from "react/jsx-runtime";
5600
5774
  var badgeVariants = cva(
5601
5775
  "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",
5602
5776
  {
@@ -5614,12 +5788,12 @@ var badgeVariants = cva(
5614
5788
  }
5615
5789
  );
5616
5790
  function Badge({ className, variant, ...props }) {
5617
- return /* @__PURE__ */ jsx20("div", { className: cn(badgeVariants({ variant }), className), ...props });
5791
+ return /* @__PURE__ */ jsx21("div", { className: cn(badgeVariants({ variant }), className), ...props });
5618
5792
  }
5619
5793
 
5620
5794
  // src/OhhwellsBridge.tsx
5621
5795
  import { Link as Link2 } from "lucide-react";
5622
- import { Fragment as Fragment3, jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
5796
+ import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
5623
5797
  var PRIMARY2 = "#0885FE";
5624
5798
  var IMAGE_FADE_MS = 300;
5625
5799
  function runOpacityFade(el, onDone) {
@@ -5793,7 +5967,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
5793
5967
  const root = createRoot(container);
5794
5968
  flushSync(() => {
5795
5969
  root.render(
5796
- /* @__PURE__ */ jsx21(
5970
+ /* @__PURE__ */ jsx22(
5797
5971
  SchedulingWidget,
5798
5972
  {
5799
5973
  notifyOnConnect,
@@ -6238,7 +6412,7 @@ function EditGlowChrome({
6238
6412
  dragDisabled = false
6239
6413
  }) {
6240
6414
  const GAP = 6;
6241
- return /* @__PURE__ */ jsxs11(
6415
+ return /* @__PURE__ */ jsxs12(
6242
6416
  "div",
6243
6417
  {
6244
6418
  ref: elRef,
@@ -6253,7 +6427,7 @@ function EditGlowChrome({
6253
6427
  zIndex: 2147483646
6254
6428
  },
6255
6429
  children: [
6256
- /* @__PURE__ */ jsx21(
6430
+ /* @__PURE__ */ jsx22(
6257
6431
  "div",
6258
6432
  {
6259
6433
  style: {
@@ -6266,7 +6440,7 @@ function EditGlowChrome({
6266
6440
  }
6267
6441
  }
6268
6442
  ),
6269
- reorderHrefKey && /* @__PURE__ */ jsx21(
6443
+ reorderHrefKey && /* @__PURE__ */ jsx22(
6270
6444
  "div",
6271
6445
  {
6272
6446
  "data-ohw-drag-handle-container": "",
@@ -6278,7 +6452,7 @@ function EditGlowChrome({
6278
6452
  transform: "translate(calc(-100% - 7px), -50%)",
6279
6453
  pointerEvents: dragDisabled ? "none" : "auto"
6280
6454
  },
6281
- children: /* @__PURE__ */ jsx21(
6455
+ children: /* @__PURE__ */ jsx22(
6282
6456
  DragHandle,
6283
6457
  {
6284
6458
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -6382,7 +6556,7 @@ function FloatingToolbar({
6382
6556
  onEditLink
6383
6557
  }) {
6384
6558
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
6385
- return /* @__PURE__ */ jsx21(
6559
+ return /* @__PURE__ */ jsx22(
6386
6560
  "div",
6387
6561
  {
6388
6562
  ref: elRef,
@@ -6403,12 +6577,12 @@ function FloatingToolbar({
6403
6577
  fontFamily: "sans-serif",
6404
6578
  pointerEvents: "auto"
6405
6579
  },
6406
- children: /* @__PURE__ */ jsxs11(CustomToolbar, { children: [
6407
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs11(React8.Fragment, { children: [
6408
- gi > 0 && /* @__PURE__ */ jsx21(CustomToolbarDivider, {}),
6580
+ children: /* @__PURE__ */ jsxs12(CustomToolbar, { children: [
6581
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs12(React9.Fragment, { children: [
6582
+ gi > 0 && /* @__PURE__ */ jsx22(CustomToolbarDivider, {}),
6409
6583
  btns.map((btn) => {
6410
6584
  const isActive = activeCommands.has(btn.cmd);
6411
- return /* @__PURE__ */ jsx21(
6585
+ return /* @__PURE__ */ jsx22(
6412
6586
  CustomToolbarButton,
6413
6587
  {
6414
6588
  title: btn.title,
@@ -6417,7 +6591,7 @@ function FloatingToolbar({
6417
6591
  e.preventDefault();
6418
6592
  onCommand(btn.cmd);
6419
6593
  },
6420
- children: /* @__PURE__ */ jsx21(
6594
+ children: /* @__PURE__ */ jsx22(
6421
6595
  "svg",
6422
6596
  {
6423
6597
  width: "16",
@@ -6438,7 +6612,7 @@ function FloatingToolbar({
6438
6612
  );
6439
6613
  })
6440
6614
  ] }, gi)),
6441
- showEditLink ? /* @__PURE__ */ jsx21(
6615
+ showEditLink ? /* @__PURE__ */ jsx22(
6442
6616
  CustomToolbarButton,
6443
6617
  {
6444
6618
  type: "button",
@@ -6452,7 +6626,7 @@ function FloatingToolbar({
6452
6626
  e.preventDefault();
6453
6627
  e.stopPropagation();
6454
6628
  },
6455
- children: /* @__PURE__ */ jsx21(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
6629
+ children: /* @__PURE__ */ jsx22(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
6456
6630
  }
6457
6631
  ) : null
6458
6632
  ] })
@@ -6469,7 +6643,7 @@ function StateToggle({
6469
6643
  states,
6470
6644
  onStateChange
6471
6645
  }) {
6472
- return /* @__PURE__ */ jsx21(
6646
+ return /* @__PURE__ */ jsx22(
6473
6647
  ToggleGroup,
6474
6648
  {
6475
6649
  "data-ohw-state-toggle": "",
@@ -6483,7 +6657,7 @@ function StateToggle({
6483
6657
  left: rect.right - 8,
6484
6658
  transform: "translateX(-100%)"
6485
6659
  },
6486
- children: states.map((state) => /* @__PURE__ */ jsx21(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6660
+ children: states.map((state) => /* @__PURE__ */ jsx22(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6487
6661
  }
6488
6662
  );
6489
6663
  }
@@ -6511,7 +6685,7 @@ function OhhwellsBridge() {
6511
6685
  const searchParams = useSearchParams();
6512
6686
  const isEditMode = isEditSessionActive();
6513
6687
  const [bridgeRoot, setBridgeRoot] = useState5(null);
6514
- useEffect3(() => {
6688
+ useEffect4(() => {
6515
6689
  const figtreeFontId = "ohw-figtree-font";
6516
6690
  if (!document.getElementById(figtreeFontId)) {
6517
6691
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -6519,7 +6693,10 @@ function OhhwellsBridge() {
6519
6693
  const stylesheet = Object.assign(document.createElement("link"), {
6520
6694
  id: figtreeFontId,
6521
6695
  rel: "stylesheet",
6522
- href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
6696
+ // Inter is loaded alongside Figtree for the media overlay's Replace button. The bridge
6697
+ // renders inside the customer's document, so it cannot assume any font is present — an
6698
+ // unloaded family would silently fall back to whatever the template happens to use.
6699
+ href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&family=Inter:wght@400;500;600&display=swap"
6523
6700
  });
6524
6701
  document.head.append(preconnect1, preconnect2, stylesheet);
6525
6702
  }
@@ -6542,50 +6719,52 @@ function OhhwellsBridge() {
6542
6719
  }
6543
6720
  }, []);
6544
6721
  const [fetchState, setFetchState] = useState5("idle");
6545
- const autoSaveTimers = useRef3(/* @__PURE__ */ new Map());
6546
- const activeElRef = useRef3(null);
6547
- const selectedElRef = useRef3(null);
6548
- const originalContentRef = useRef3(null);
6549
- const activeStateElRef = useRef3(null);
6550
- const parentScrollRef = useRef3(null);
6551
- const visibleViewportRef = useRef3(null);
6722
+ const autoSaveTimers = useRef4(/* @__PURE__ */ new Map());
6723
+ const activeElRef = useRef4(null);
6724
+ const selectedElRef = useRef4(null);
6725
+ const originalContentRef = useRef4(null);
6726
+ const activeStateElRef = useRef4(null);
6727
+ const parentScrollRef = useRef4(null);
6728
+ const visibleViewportRef = useRef4(null);
6552
6729
  const [dialogPortalContainer, setDialogPortalContainer] = useState5(null);
6553
6730
  const attachVisibleViewport = useCallback3((node) => {
6554
6731
  visibleViewportRef.current = node;
6555
6732
  setDialogPortalContainer(node);
6556
6733
  if (node) applyVisibleViewport(node, parentScrollRef.current);
6557
6734
  }, []);
6558
- const toolbarElRef = useRef3(null);
6559
- const glowElRef = useRef3(null);
6560
- const hoveredImageRef = useRef3(null);
6561
- const hoveredImageHasTextOverlapRef = useRef3(false);
6562
- const hoveredGapRef = useRef3(null);
6563
- const imageUnhoverTimerRef = useRef3(null);
6564
- const imageShowTimerRef = useRef3(null);
6565
- const editStylesRef = useRef3(null);
6566
- const activateRef = useRef3(() => {
6735
+ const toolbarElRef = useRef4(null);
6736
+ const glowElRef = useRef4(null);
6737
+ const hoveredImageRef = useRef4(null);
6738
+ const hoveredImageHasTextOverlapRef = useRef4(false);
6739
+ const [mediaHover, setMediaHover] = useState5(null);
6740
+ const [uploadingRects, setUploadingRects] = useState5({});
6741
+ const hoveredGapRef = useRef4(null);
6742
+ const imageUnhoverTimerRef = useRef4(null);
6743
+ const imageShowTimerRef = useRef4(null);
6744
+ const editStylesRef = useRef4(null);
6745
+ const activateRef = useRef4(() => {
6567
6746
  });
6568
- const deactivateRef = useRef3(() => {
6747
+ const deactivateRef = useRef4(() => {
6569
6748
  });
6570
- const selectRef = useRef3(() => {
6749
+ const selectRef = useRef4(() => {
6571
6750
  });
6572
- const selectFrameRef = useRef3(() => {
6751
+ const selectFrameRef = useRef4(() => {
6573
6752
  });
6574
- const deselectRef = useRef3(() => {
6753
+ const deselectRef = useRef4(() => {
6575
6754
  });
6576
- const reselectNavigationItemRef = useRef3(() => {
6755
+ const reselectNavigationItemRef = useRef4(() => {
6577
6756
  });
6578
- const commitNavigationTextEditRef = useRef3(() => {
6757
+ const commitNavigationTextEditRef = useRef4(() => {
6579
6758
  });
6580
- const refreshActiveCommandsRef = useRef3(() => {
6759
+ const refreshActiveCommandsRef = useRef4(() => {
6581
6760
  });
6582
- const postToParentRef = useRef3(postToParent);
6761
+ const postToParentRef = useRef4(postToParent);
6583
6762
  postToParentRef.current = postToParent;
6584
- const sectionsLoadedRef = useRef3(false);
6585
- const pendingScheduleConfigRequests = useRef3([]);
6763
+ const sectionsLoadedRef = useRef4(false);
6764
+ const pendingScheduleConfigRequests = useRef4([]);
6586
6765
  const [toolbarRect, setToolbarRect] = useState5(null);
6587
6766
  const [toolbarVariant, setToolbarVariant] = useState5("none");
6588
- const toolbarVariantRef = useRef3("none");
6767
+ const toolbarVariantRef = useRef4("none");
6589
6768
  toolbarVariantRef.current = toolbarVariant;
6590
6769
  const [reorderHrefKey, setReorderHrefKey] = useState5(null);
6591
6770
  const [reorderDragDisabled, setReorderDragDisabled] = useState5(false);
@@ -6594,19 +6773,19 @@ function OhhwellsBridge() {
6594
6773
  const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
6595
6774
  const [sectionGap, setSectionGap] = useState5(null);
6596
6775
  const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
6597
- const hoveredItemElRef = useRef3(null);
6776
+ const hoveredItemElRef = useRef4(null);
6598
6777
  const [hoveredItemRect, setHoveredItemRect] = useState5(null);
6599
- const siblingHintElRef = useRef3(null);
6778
+ const siblingHintElRef = useRef4(null);
6600
6779
  const [siblingHintRect, setSiblingHintRect] = useState5(null);
6601
6780
  const [isItemDragging, setIsItemDragging] = useState5(false);
6602
6781
  const [linkPopover, setLinkPopover] = useState5(null);
6603
6782
  const [sitePages, setSitePages] = useState5([]);
6604
6783
  const [sectionsByPath, setSectionsByPath] = useState5({});
6605
- const sectionsPrefetchGenRef = useRef3(0);
6606
- const setLinkPopoverRef = useRef3(setLinkPopover);
6607
- const linkPopoverPanelRef = useRef3(null);
6608
- const linkPopoverOpenRef = useRef3(false);
6609
- const linkPopoverGraceUntilRef = useRef3(0);
6784
+ const sectionsPrefetchGenRef = useRef4(0);
6785
+ const setLinkPopoverRef = useRef4(setLinkPopover);
6786
+ const linkPopoverPanelRef = useRef4(null);
6787
+ const linkPopoverOpenRef = useRef4(false);
6788
+ const linkPopoverGraceUntilRef = useRef4(0);
6610
6789
  setLinkPopoverRef.current = setLinkPopover;
6611
6790
  const bumpLinkPopoverGrace = () => {
6612
6791
  linkPopoverGraceUntilRef.current = Date.now() + 350;
@@ -6630,9 +6809,9 @@ function OhhwellsBridge() {
6630
6809
  );
6631
6810
  });
6632
6811
  }, [isEditMode, pathname]);
6633
- const runSectionsPrefetchRef = useRef3(runSectionsPrefetch);
6812
+ const runSectionsPrefetchRef = useRef4(runSectionsPrefetch);
6634
6813
  runSectionsPrefetchRef.current = runSectionsPrefetch;
6635
- useEffect3(() => {
6814
+ useEffect4(() => {
6636
6815
  if (!linkPopover) return;
6637
6816
  if (hoveredImageRef.current) {
6638
6817
  hoveredImageRef.current = null;
@@ -6640,7 +6819,7 @@ function OhhwellsBridge() {
6640
6819
  }
6641
6820
  hoveredGapRef.current = null;
6642
6821
  setSectionGap(null);
6643
- postToParent({ type: "ow:image-unhover" });
6822
+ setMediaHover(null);
6644
6823
  postToParent({ type: "ow:link-modal-lock", locked: true });
6645
6824
  const html = document.documentElement;
6646
6825
  const body = document.body;
@@ -6666,7 +6845,7 @@ function OhhwellsBridge() {
6666
6845
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6667
6846
  };
6668
6847
  }, [linkPopover, postToParent]);
6669
- useEffect3(() => {
6848
+ useEffect4(() => {
6670
6849
  if (!isEditMode) return;
6671
6850
  const useFixtures = shouldUseDevFixtures();
6672
6851
  if (useFixtures) {
@@ -6690,14 +6869,14 @@ function OhhwellsBridge() {
6690
6869
  if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
6691
6870
  return () => window.removeEventListener("message", onSitePages);
6692
6871
  }, [isEditMode, postToParent]);
6693
- useEffect3(() => {
6872
+ useEffect4(() => {
6694
6873
  if (!isEditMode || shouldUseDevFixtures()) return;
6695
6874
  void loadAllSectionsManifest().then((manifest) => {
6696
6875
  if (Object.keys(manifest).length === 0) return;
6697
6876
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
6698
6877
  });
6699
6878
  }, [isEditMode]);
6700
- useEffect3(() => {
6879
+ useEffect4(() => {
6701
6880
  const update = () => {
6702
6881
  const el = activeElRef.current ?? selectedElRef.current;
6703
6882
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -6884,7 +7063,7 @@ function OhhwellsBridge() {
6884
7063
  deactivate();
6885
7064
  if (hoveredImageRef.current) {
6886
7065
  hoveredImageRef.current = null;
6887
- postToParentRef.current({ type: "ow:image-unhover" });
7066
+ setMediaHover(null);
6888
7067
  }
6889
7068
  setToolbarVariant("rich-text");
6890
7069
  siblingHintElRef.current = null;
@@ -6986,7 +7165,7 @@ function OhhwellsBridge() {
6986
7165
  cancelled = true;
6987
7166
  };
6988
7167
  }, [subdomain, isEditMode]);
6989
- useEffect3(() => {
7168
+ useEffect4(() => {
6990
7169
  if (!subdomain || isEditMode) return;
6991
7170
  let debounceTimer = null;
6992
7171
  let observer = null;
@@ -7040,16 +7219,16 @@ function OhhwellsBridge() {
7040
7219
  const visible = Boolean(subdomain) && fetchState !== "done";
7041
7220
  el.style.display = visible ? "flex" : "none";
7042
7221
  }, [subdomain, fetchState]);
7043
- useEffect3(() => {
7222
+ useEffect4(() => {
7044
7223
  postToParent({ type: "ow:navigation", path: pathname });
7045
7224
  }, [pathname, postToParent]);
7046
- useEffect3(() => {
7225
+ useEffect4(() => {
7047
7226
  if (!isEditMode) return;
7048
7227
  setLinkPopover(null);
7049
7228
  deselectRef.current();
7050
7229
  deactivateRef.current();
7051
7230
  }, [pathname, isEditMode]);
7052
- useEffect3(() => {
7231
+ useEffect4(() => {
7053
7232
  if (!isEditMode) return;
7054
7233
  const measure = () => {
7055
7234
  const h = document.body.scrollHeight;
@@ -7073,7 +7252,7 @@ function OhhwellsBridge() {
7073
7252
  window.removeEventListener("resize", handleResize);
7074
7253
  };
7075
7254
  }, [pathname, isEditMode, postToParent]);
7076
- useEffect3(() => {
7255
+ useEffect4(() => {
7077
7256
  if (!subdomainFromQuery || isEditMode) return;
7078
7257
  const handleClick = (e) => {
7079
7258
  const anchor = e.target.closest("a");
@@ -7089,7 +7268,7 @@ function OhhwellsBridge() {
7089
7268
  document.addEventListener("click", handleClick, true);
7090
7269
  return () => document.removeEventListener("click", handleClick, true);
7091
7270
  }, [subdomainFromQuery, isEditMode, router]);
7092
- useEffect3(() => {
7271
+ useEffect4(() => {
7093
7272
  if (!isEditMode) {
7094
7273
  editStylesRef.current?.base.remove();
7095
7274
  editStylesRef.current?.forceHover.remove();
@@ -7184,7 +7363,7 @@ function OhhwellsBridge() {
7184
7363
  if (isMediaEditable(editable)) {
7185
7364
  e.preventDefault();
7186
7365
  e.stopPropagation();
7187
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
7366
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
7188
7367
  return;
7189
7368
  }
7190
7369
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -7358,25 +7537,23 @@ function OhhwellsBridge() {
7358
7537
  }
7359
7538
  return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
7360
7539
  };
7361
- const postImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
7540
+ const showImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
7362
7541
  const r2 = getVisibleRect(imgEl);
7363
7542
  const hasTextOverlap = forceTextOverlap ?? false;
7364
7543
  hoveredImageHasTextOverlapRef.current = hasTextOverlap;
7365
7544
  const video = imgEl.dataset.ohwEditable === "video" ? getVideoEl(imgEl) : null;
7366
- postToParentRef.current({
7367
- type: "ow:image-hover",
7545
+ setMediaHover({
7368
7546
  key: imgEl.dataset.ohwKey ?? "",
7369
- // Lets the parent pick the file-picker `accept`, overlay label/icon and drop
7370
- // mime-check without a parallel ow:video-* message channel.
7371
- elementType: imgEl.dataset.ohwEditable ?? "image",
7372
7547
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
7548
+ elementType: imgEl.dataset.ohwEditable ?? "image",
7373
7549
  hasTextOverlap,
7374
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {},
7375
- ...isDragOver ? { isDragOver: true } : {}
7550
+ isDragOver,
7551
+ ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
7376
7552
  });
7377
7553
  const track = imgEl.closest("[data-ohw-hover-pause]");
7378
7554
  if (track) track.setAttribute("data-ohw-hover-paused", "");
7379
7555
  };
7556
+ const clearImageHover = () => setMediaHover(null);
7380
7557
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
7381
7558
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7382
7559
  const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
@@ -7426,7 +7603,7 @@ function OhhwellsBridge() {
7426
7603
  hoveredImageRef.current = null;
7427
7604
  hoveredImageHasTextOverlapRef.current = false;
7428
7605
  resumeAnimTracks();
7429
- postToParentRef.current({ type: "ow:image-unhover" });
7606
+ clearImageHover();
7430
7607
  }
7431
7608
  return;
7432
7609
  }
@@ -7438,21 +7615,22 @@ function OhhwellsBridge() {
7438
7615
  if (hoveredImageRef.current) {
7439
7616
  hoveredImageRef.current = null;
7440
7617
  resumeAnimTracks();
7441
- postToParentRef.current({ type: "ow:image-unhover" });
7618
+ clearImageHover();
7442
7619
  }
7443
7620
  return;
7444
7621
  }
7445
7622
  }
7446
- if (activeElRef.current) {
7623
+ const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
7624
+ const activeEl = activeElRef.current;
7625
+ if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
7447
7626
  if (hoveredImageRef.current) {
7448
7627
  hoveredImageRef.current = null;
7449
7628
  hoveredImageHasTextOverlapRef.current = false;
7450
7629
  resumeAnimTracks();
7451
- postToParentRef.current({ type: "ow:image-unhover" });
7630
+ clearImageHover();
7452
7631
  }
7453
7632
  return;
7454
7633
  }
7455
- const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
7456
7634
  if (imgEl) {
7457
7635
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7458
7636
  const topEl = document.elementFromPoint(x, y);
@@ -7460,7 +7638,7 @@ function OhhwellsBridge() {
7460
7638
  if (hoveredImageRef.current) {
7461
7639
  hoveredImageRef.current = null;
7462
7640
  resumeAnimTracks();
7463
- postToParentRef.current({ type: "ow:image-unhover" });
7641
+ clearImageHover();
7464
7642
  }
7465
7643
  return;
7466
7644
  }
@@ -7469,7 +7647,7 @@ function OhhwellsBridge() {
7469
7647
  hoveredImageRef.current = null;
7470
7648
  hoveredImageHasTextOverlapRef.current = false;
7471
7649
  resumeAnimTracks();
7472
- postToParentRef.current({ type: "ow:image-unhover" });
7650
+ clearImageHover();
7473
7651
  }
7474
7652
  return;
7475
7653
  }
@@ -7494,14 +7672,14 @@ function OhhwellsBridge() {
7494
7672
  hoveredImageRef.current = null;
7495
7673
  hoveredImageHasTextOverlapRef.current = false;
7496
7674
  resumeAnimTracks();
7497
- postToParentRef.current({ type: "ow:image-unhover" });
7675
+ clearImageHover();
7498
7676
  }
7499
7677
  return;
7500
7678
  }
7501
7679
  if (isStateCardImage) {
7502
7680
  if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
7503
7681
  hoveredImageRef.current = imgEl;
7504
- postImageHover(imgEl, isDragOver, true);
7682
+ showImageHover(imgEl, isDragOver, true);
7505
7683
  }
7506
7684
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7507
7685
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -7513,7 +7691,7 @@ function OhhwellsBridge() {
7513
7691
  hoveredImageRef.current = null;
7514
7692
  hoveredImageHasTextOverlapRef.current = false;
7515
7693
  resumeAnimTracks();
7516
- postToParentRef.current({ type: "ow:image-unhover" });
7694
+ clearImageHover();
7517
7695
  }
7518
7696
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7519
7697
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -7525,7 +7703,7 @@ function OhhwellsBridge() {
7525
7703
  if (hoveredImageRef.current) {
7526
7704
  hoveredImageRef.current = null;
7527
7705
  resumeAnimTracks();
7528
- postToParentRef.current({ type: "ow:image-unhover" });
7706
+ clearImageHover();
7529
7707
  }
7530
7708
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7531
7709
  return;
@@ -7533,9 +7711,9 @@ function OhhwellsBridge() {
7533
7711
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7534
7712
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
7535
7713
  hoveredImageRef.current = imgEl;
7536
- postImageHover(imgEl, isDragOver);
7714
+ showImageHover(imgEl, isDragOver);
7537
7715
  } else if (isDragOver) {
7538
- postImageHover(imgEl, true);
7716
+ showImageHover(imgEl, true);
7539
7717
  }
7540
7718
  } else {
7541
7719
  const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
@@ -7552,7 +7730,7 @@ function OhhwellsBridge() {
7552
7730
  hoveredImageRef.current = null;
7553
7731
  hoveredImageHasTextOverlapRef.current = false;
7554
7732
  resumeAnimTracks();
7555
- postToParentRef.current({ type: "ow:image-unhover" });
7733
+ clearImageHover();
7556
7734
  }
7557
7735
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7558
7736
  const textEl = Array.from(
@@ -7665,7 +7843,7 @@ function OhhwellsBridge() {
7665
7843
  if (hoveredImageRef.current !== el) {
7666
7844
  hoveredImageRef.current = el;
7667
7845
  const isStateCard = !!el.closest("[data-ohw-editable-state]");
7668
- postImageHover(el, true, isStateCard ? true : void 0);
7846
+ showImageHover(el, true, isStateCard ? true : void 0);
7669
7847
  }
7670
7848
  };
7671
7849
  const handleDragLeave = (e) => {
@@ -7673,7 +7851,7 @@ function OhhwellsBridge() {
7673
7851
  if (imgEl) return;
7674
7852
  hoveredImageRef.current = null;
7675
7853
  resumeAnimTracks();
7676
- postToParentRef.current({ type: "ow:image-unhover" });
7854
+ clearImageHover();
7677
7855
  };
7678
7856
  const handleDrop = (e) => {
7679
7857
  e.preventDefault();
@@ -7697,31 +7875,7 @@ function OhhwellsBridge() {
7697
7875
  }
7698
7876
  hoveredImageRef.current = null;
7699
7877
  resumeAnimTracks();
7700
- postToParentRef.current({ type: "ow:image-unhover" });
7701
- };
7702
- const handleVideoSettings = (e) => {
7703
- if (e.data?.type !== "ow:video-settings") return;
7704
- const { key, autoplay, muted } = e.data;
7705
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7706
- const video = getVideoEl(el);
7707
- if (!video) return;
7708
- if (typeof autoplay === "boolean") video.autoplay = autoplay;
7709
- if (typeof muted === "boolean") video.muted = muted;
7710
- syncVideoPlayback(video);
7711
- postToParentRef.current({
7712
- type: "ow:change",
7713
- nodes: [
7714
- { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
7715
- { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
7716
- ]
7717
- });
7718
- postToParentRef.current({
7719
- type: "ow:video-settings-applied",
7720
- key,
7721
- autoplay: video.autoplay,
7722
- muted: video.muted
7723
- });
7724
- });
7878
+ clearImageHover();
7725
7879
  };
7726
7880
  const handleImageUrl = (e) => {
7727
7881
  if (e.data?.type !== "ow:image-url") return;
@@ -7739,7 +7893,11 @@ function OhhwellsBridge() {
7739
7893
  }
7740
7894
  });
7741
7895
  hoveredImageRef.current = null;
7742
- postToParentRef.current({ type: "ow:image-loaded", key });
7896
+ setUploadingRects((prev) => {
7897
+ const entry = prev[key];
7898
+ if (!entry || entry.fadingOut) return prev;
7899
+ return { ...prev, [key]: { ...entry, fadingOut: true } };
7900
+ });
7743
7901
  };
7744
7902
  let found = false;
7745
7903
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -7975,12 +8133,9 @@ function OhhwellsBridge() {
7975
8133
  if (hoveredImageRef.current) {
7976
8134
  const el = hoveredImageRef.current;
7977
8135
  const r2 = el.getBoundingClientRect();
7978
- postToParentRef.current({
7979
- type: "ow:image-hover",
7980
- key: el.dataset.ohwKey ?? "",
7981
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
7982
- hasTextOverlap: hoveredImageHasTextOverlapRef.current
7983
- });
8136
+ setMediaHover(
8137
+ (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
8138
+ );
7984
8139
  }
7985
8140
  };
7986
8141
  const handleSave = (e) => {
@@ -8069,19 +8224,39 @@ function OhhwellsBridge() {
8069
8224
  refreshActiveCommandsRef.current = handleSelectionChange;
8070
8225
  const handleDocMouseLeave = () => {
8071
8226
  hoveredImageRef.current = null;
8227
+ setMediaHover(null);
8072
8228
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
8073
8229
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8074
8230
  activeStateElRef.current = null;
8075
8231
  setToggleState(null);
8076
8232
  };
8077
- const handleAnimLock = (e) => {
8078
- if (e.data?.type !== "ow:anim-lock") return;
8079
- const { key } = e.data;
8233
+ const handleImageUploading = (e) => {
8234
+ if (e.data?.type !== "ow:image-uploading") return;
8235
+ const { key, uploading } = e.data;
8236
+ setUploadingRects((prev) => {
8237
+ if (!uploading) {
8238
+ if (!(key in prev)) return prev;
8239
+ const next = { ...prev };
8240
+ delete next[key];
8241
+ return next;
8242
+ }
8243
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
8244
+ if (!el) return prev;
8245
+ const r2 = getVisibleRect(el);
8246
+ return {
8247
+ ...prev,
8248
+ [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
8249
+ };
8250
+ });
8080
8251
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8081
8252
  const track = el.closest("[data-ohw-hover-pause]");
8082
- if (track) {
8253
+ if (!track) return;
8254
+ if (uploading) {
8083
8255
  uploadLockedTracks.add(track);
8084
8256
  track.setAttribute("data-ohw-hover-paused", "");
8257
+ } else {
8258
+ uploadLockedTracks.delete(track);
8259
+ track.removeAttribute("data-ohw-hover-paused");
8085
8260
  }
8086
8261
  });
8087
8262
  };
@@ -8138,7 +8313,7 @@ function OhhwellsBridge() {
8138
8313
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
8139
8314
  });
8140
8315
  if (stateCardImage) {
8141
- postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
8316
+ postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
8142
8317
  return;
8143
8318
  }
8144
8319
  const textEditable = Array.from(
@@ -8170,8 +8345,7 @@ function OhhwellsBridge() {
8170
8345
  window.addEventListener("message", handleClearSchedulingWidget);
8171
8346
  window.addEventListener("message", handleRemoveSchedulingSection);
8172
8347
  window.addEventListener("message", handleImageUrl);
8173
- window.addEventListener("message", handleVideoSettings);
8174
- window.addEventListener("message", handleAnimLock);
8348
+ window.addEventListener("message", handleImageUploading);
8175
8349
  window.addEventListener("message", handleCanvasHeight);
8176
8350
  window.addEventListener("message", handleParentScroll);
8177
8351
  window.addEventListener("message", handlePointerSync);
@@ -8218,8 +8392,7 @@ function OhhwellsBridge() {
8218
8392
  window.removeEventListener("message", handleClearSchedulingWidget);
8219
8393
  window.removeEventListener("message", handleRemoveSchedulingSection);
8220
8394
  window.removeEventListener("message", handleImageUrl);
8221
- window.removeEventListener("message", handleVideoSettings);
8222
- window.removeEventListener("message", handleAnimLock);
8395
+ window.removeEventListener("message", handleImageUploading);
8223
8396
  window.removeEventListener("message", handleCanvasHeight);
8224
8397
  window.removeEventListener("message", handleParentScroll);
8225
8398
  window.removeEventListener("resize", handleViewportResize);
@@ -8233,7 +8406,7 @@ function OhhwellsBridge() {
8233
8406
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
8234
8407
  };
8235
8408
  }, [isEditMode, refreshStateRules]);
8236
- useEffect3(() => {
8409
+ useEffect4(() => {
8237
8410
  const handler = (e) => {
8238
8411
  if (e.data?.type !== "ow:request-schedule-config") return;
8239
8412
  const insertAfterVal = e.data.insertAfter;
@@ -8249,7 +8422,7 @@ function OhhwellsBridge() {
8249
8422
  window.addEventListener("message", handler);
8250
8423
  return () => window.removeEventListener("message", handler);
8251
8424
  }, [processConfigRequest]);
8252
- useEffect3(() => {
8425
+ useEffect4(() => {
8253
8426
  if (!isEditMode) return;
8254
8427
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
8255
8428
  el.removeAttribute("data-ohw-active-state");
@@ -8278,7 +8451,7 @@ function OhhwellsBridge() {
8278
8451
  clearTimeout(timer);
8279
8452
  };
8280
8453
  }, [pathname, isEditMode, refreshStateRules, postToParent]);
8281
- useEffect3(() => {
8454
+ useEffect4(() => {
8282
8455
  scrollToHashSectionWhenReady();
8283
8456
  const onHashChange = () => scrollToHashSectionWhenReady();
8284
8457
  window.addEventListener("hashchange", onHashChange);
@@ -8339,12 +8512,68 @@ function OhhwellsBridge() {
8339
8512
  const showEditLink = toolbarShowEditLink;
8340
8513
  const currentSections = sectionsByPath[pathname] ?? [];
8341
8514
  linkPopoverOpenRef.current = linkPopover !== null;
8515
+ const handleMediaReplace = useCallback3(
8516
+ (key) => {
8517
+ postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
8518
+ },
8519
+ [postToParent, mediaHover?.elementType]
8520
+ );
8521
+ const handleVideoSettingsChange = useCallback3(
8522
+ (key, settings) => {
8523
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8524
+ const video = getVideoEl(el);
8525
+ if (!video) return;
8526
+ if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
8527
+ if (typeof settings.muted === "boolean") video.muted = settings.muted;
8528
+ syncVideoPlayback(video);
8529
+ postToParent({
8530
+ type: "ow:change",
8531
+ nodes: [
8532
+ { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
8533
+ { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
8534
+ ]
8535
+ });
8536
+ setMediaHover(
8537
+ (prev) => prev && prev.key === key ? { ...prev, videoAutoplay: video.autoplay, videoMuted: video.muted } : prev
8538
+ );
8539
+ });
8540
+ },
8541
+ [postToParent]
8542
+ );
8543
+ const handleMediaFadeOutComplete = useCallback3((key) => {
8544
+ setUploadingRects((prev) => {
8545
+ if (!(key in prev)) return prev;
8546
+ const next = { ...prev };
8547
+ delete next[key];
8548
+ return next;
8549
+ });
8550
+ }, []);
8342
8551
  return bridgeRoot ? createPortal(
8343
- /* @__PURE__ */ jsxs11(Fragment3, { children: [
8344
- /* @__PURE__ */ jsx21("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8345
- siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8346
- hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8347
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx21(
8552
+ /* @__PURE__ */ jsxs12(Fragment4, { children: [
8553
+ /* @__PURE__ */ jsx22("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8554
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx22(
8555
+ MediaOverlay,
8556
+ {
8557
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
8558
+ isUploading: true,
8559
+ fadingOut,
8560
+ onFadeOutComplete: handleMediaFadeOutComplete,
8561
+ onReplace: handleMediaReplace
8562
+ },
8563
+ `uploading-${key}`
8564
+ )),
8565
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx22(
8566
+ MediaOverlay,
8567
+ {
8568
+ hover: mediaHover,
8569
+ isUploading: false,
8570
+ onReplace: handleMediaReplace,
8571
+ onVideoSettingsChange: handleVideoSettingsChange
8572
+ }
8573
+ ),
8574
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8575
+ hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8576
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx22(
8348
8577
  ItemInteractionLayer,
8349
8578
  {
8350
8579
  rect: toolbarRect,
@@ -8355,7 +8584,7 @@ function OhhwellsBridge() {
8355
8584
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8356
8585
  onDragHandleDragStart: handleItemDragStart,
8357
8586
  onDragHandleDragEnd: handleItemDragEnd,
8358
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx21(
8587
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx22(
8359
8588
  ItemActionToolbar,
8360
8589
  {
8361
8590
  onEditLink: openLinkPopoverForSelected,
@@ -8365,8 +8594,8 @@ function OhhwellsBridge() {
8365
8594
  ) : void 0
8366
8595
  }
8367
8596
  ),
8368
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs11(Fragment3, { children: [
8369
- /* @__PURE__ */ jsx21(
8597
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs12(Fragment4, { children: [
8598
+ /* @__PURE__ */ jsx22(
8370
8599
  EditGlowChrome,
8371
8600
  {
8372
8601
  rect: toolbarRect,
@@ -8375,7 +8604,7 @@ function OhhwellsBridge() {
8375
8604
  dragDisabled: reorderDragDisabled
8376
8605
  }
8377
8606
  ),
8378
- /* @__PURE__ */ jsx21(
8607
+ /* @__PURE__ */ jsx22(
8379
8608
  FloatingToolbar,
8380
8609
  {
8381
8610
  rect: toolbarRect,
@@ -8388,7 +8617,7 @@ function OhhwellsBridge() {
8388
8617
  }
8389
8618
  )
8390
8619
  ] }),
8391
- maxBadge && /* @__PURE__ */ jsxs11(
8620
+ maxBadge && /* @__PURE__ */ jsxs12(
8392
8621
  "div",
8393
8622
  {
8394
8623
  "data-ohw-max-badge": "",
@@ -8414,7 +8643,7 @@ function OhhwellsBridge() {
8414
8643
  ]
8415
8644
  }
8416
8645
  ),
8417
- toggleState && /* @__PURE__ */ jsx21(
8646
+ toggleState && /* @__PURE__ */ jsx22(
8418
8647
  StateToggle,
8419
8648
  {
8420
8649
  rect: toggleState.rect,
@@ -8423,15 +8652,15 @@ function OhhwellsBridge() {
8423
8652
  onStateChange: handleStateChange
8424
8653
  }
8425
8654
  ),
8426
- sectionGap && /* @__PURE__ */ jsxs11(
8655
+ sectionGap && /* @__PURE__ */ jsxs12(
8427
8656
  "div",
8428
8657
  {
8429
8658
  "data-ohw-section-insert-line": "",
8430
8659
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8431
8660
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8432
8661
  children: [
8433
- /* @__PURE__ */ jsx21("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8434
- /* @__PURE__ */ jsx21(
8662
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8663
+ /* @__PURE__ */ jsx22(
8435
8664
  Badge,
8436
8665
  {
8437
8666
  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",
@@ -8444,11 +8673,11 @@ function OhhwellsBridge() {
8444
8673
  children: "Add Section"
8445
8674
  }
8446
8675
  ),
8447
- /* @__PURE__ */ jsx21("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8676
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8448
8677
  ]
8449
8678
  }
8450
8679
  ),
8451
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx21(
8680
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx22(
8452
8681
  LinkPopover,
8453
8682
  {
8454
8683
  panelRef: linkPopoverPanelRef,
@@ -8464,15 +8693,15 @@ function OhhwellsBridge() {
8464
8693
  },
8465
8694
  `${linkPopover.key}-${pathname}`
8466
8695
  ) : null,
8467
- sectionGap && /* @__PURE__ */ jsxs11(
8696
+ sectionGap && /* @__PURE__ */ jsxs12(
8468
8697
  "div",
8469
8698
  {
8470
8699
  "data-ohw-section-insert-line": "",
8471
8700
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8472
8701
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8473
8702
  children: [
8474
- /* @__PURE__ */ jsx21("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8475
- /* @__PURE__ */ jsx21(
8703
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8704
+ /* @__PURE__ */ jsx22(
8476
8705
  Badge,
8477
8706
  {
8478
8707
  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",
@@ -8485,7 +8714,7 @@ function OhhwellsBridge() {
8485
8714
  children: "Add Section"
8486
8715
  }
8487
8716
  ),
8488
- /* @__PURE__ */ jsx21("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8717
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8489
8718
  ]
8490
8719
  }
8491
8720
  )