@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.cjs CHANGED
@@ -4639,6 +4639,220 @@ function ItemInteractionLayer({
4639
4639
  );
4640
4640
  }
4641
4641
 
4642
+ // src/ui/MediaOverlay.tsx
4643
+ var React6 = __toESM(require("react"), 1);
4644
+ var import_lucide_react3 = require("lucide-react");
4645
+
4646
+ // src/ui/button.tsx
4647
+ var React5 = __toESM(require("react"), 1);
4648
+ var import_radix_ui4 = require("radix-ui");
4649
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4650
+ var buttonVariants = cva(
4651
+ "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",
4652
+ {
4653
+ variants: {
4654
+ variant: {
4655
+ default: "bg-primary text-primary-foreground hover:opacity-90",
4656
+ outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
4657
+ ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
4658
+ },
4659
+ size: {
4660
+ default: "h-9",
4661
+ sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
4662
+ }
4663
+ },
4664
+ defaultVariants: {
4665
+ variant: "default",
4666
+ size: "default"
4667
+ }
4668
+ }
4669
+ );
4670
+ var Button = React5.forwardRef(
4671
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
4672
+ const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
4673
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4674
+ Comp,
4675
+ {
4676
+ ref,
4677
+ "data-slot": "button",
4678
+ className: cn(buttonVariants({ variant, size, className })),
4679
+ ...props
4680
+ }
4681
+ );
4682
+ }
4683
+ );
4684
+ Button.displayName = "Button";
4685
+
4686
+ // src/ui/MediaOverlay.tsx
4687
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4688
+ var MEDIA_UPLOAD_FADE_MS = 300;
4689
+ var VIDEO_SETTINGS_BAR_INSET = 8;
4690
+ var OVERLAY_BUTTON_STYLE = {
4691
+ pointerEvents: "auto",
4692
+ fontFamily: "Inter, sans-serif",
4693
+ fontSize: 12,
4694
+ color: "#000"
4695
+ };
4696
+ var SKELETON_CSS = `
4697
+ @keyframes ohw-media-shimmer {
4698
+ 0% { transform: translateX(-100%); }
4699
+ 100% { transform: translateX(100%); }
4700
+ }
4701
+ [data-ohw-media-skeleton] {
4702
+ overflow: hidden;
4703
+ background-color: #d1d5db; /* tailwind gray-300 */
4704
+ }
4705
+ [data-ohw-media-skeleton]::after {
4706
+ content: "";
4707
+ position: absolute;
4708
+ inset: 0;
4709
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
4710
+ animation: ohw-media-shimmer 1.4s infinite;
4711
+ }
4712
+ `;
4713
+ function MediaOverlay({
4714
+ hover,
4715
+ isUploading,
4716
+ fadingOut = false,
4717
+ onFadeOutComplete,
4718
+ onReplace,
4719
+ onVideoSettingsChange
4720
+ }) {
4721
+ const { rect } = hover;
4722
+ const skeletonRef = React6.useRef(null);
4723
+ const isVideo = hover.elementType === "video";
4724
+ const autoplay = hover.videoAutoplay ?? true;
4725
+ const muted = hover.videoMuted ?? true;
4726
+ const box = {
4727
+ position: "fixed",
4728
+ top: rect.top,
4729
+ left: rect.left,
4730
+ width: rect.width,
4731
+ height: rect.height,
4732
+ zIndex: 2147483646
4733
+ };
4734
+ React6.useEffect(() => {
4735
+ if (!isUploading || !fadingOut || !skeletonRef.current) return;
4736
+ const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4737
+ duration: MEDIA_UPLOAD_FADE_MS,
4738
+ easing: "ease-out",
4739
+ fill: "forwards"
4740
+ });
4741
+ anim.finished.then(() => onFadeOutComplete?.(hover.key)).catch(() => onFadeOutComplete?.(hover.key));
4742
+ return () => anim.cancel();
4743
+ }, [isUploading, fadingOut, onFadeOutComplete, hover.key]);
4744
+ if (isUploading) {
4745
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4746
+ "div",
4747
+ {
4748
+ ref: skeletonRef,
4749
+ "data-ohw-bridge": "",
4750
+ "data-ohw-media-overlay": "",
4751
+ "data-ohw-media-skeleton": "",
4752
+ "aria-hidden": true,
4753
+ style: { ...box, pointerEvents: "none" },
4754
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: SKELETON_CSS })
4755
+ }
4756
+ );
4757
+ }
4758
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4759
+ "div",
4760
+ {
4761
+ "data-ohw-bridge": "",
4762
+ "data-ohw-media-overlay": "",
4763
+ className: "flex items-center justify-center gap-1.5",
4764
+ style: {
4765
+ position: "fixed",
4766
+ top: rect.top + VIDEO_SETTINGS_BAR_INSET,
4767
+ left: rect.left,
4768
+ width: rect.width,
4769
+ zIndex: 2147483647,
4770
+ pointerEvents: "auto"
4771
+ },
4772
+ onClick: (e) => e.stopPropagation(),
4773
+ children: [
4774
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4775
+ Button,
4776
+ {
4777
+ "data-ohw-media-overlay": "",
4778
+ variant: "outline",
4779
+ size: "sm",
4780
+ className: "cursor-pointer hover:bg-background",
4781
+ style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4782
+ "aria-label": autoplay ? "Disable autoplay" : "Enable autoplay",
4783
+ title: autoplay ? "Autoplay on \u2014 click to disable" : "Autoplay off \u2014 click to enable",
4784
+ onMouseDown: (e) => e.preventDefault(),
4785
+ onClick: (e) => {
4786
+ e.stopPropagation();
4787
+ onVideoSettingsChange?.(hover.key, { autoplay: !autoplay });
4788
+ },
4789
+ children: autoplay ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Pause, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Play, { size: 14 })
4790
+ }
4791
+ ),
4792
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4793
+ Button,
4794
+ {
4795
+ "data-ohw-media-overlay": "",
4796
+ variant: "outline",
4797
+ size: "sm",
4798
+ className: "cursor-pointer hover:bg-background",
4799
+ style: { ...OVERLAY_BUTTON_STYLE, minWidth: 0, padding: "0 8px" },
4800
+ "aria-label": muted ? "Unmute video" : "Mute video",
4801
+ title: muted ? "Muted \u2014 click to unmute" : "Sound on \u2014 click to mute",
4802
+ onMouseDown: (e) => e.preventDefault(),
4803
+ onClick: (e) => {
4804
+ e.stopPropagation();
4805
+ onVideoSettingsChange?.(hover.key, { muted: !muted });
4806
+ },
4807
+ children: muted ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.VolumeX, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Volume2, { size: 14 })
4808
+ }
4809
+ )
4810
+ ]
4811
+ }
4812
+ ) : null;
4813
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4814
+ settingsBar,
4815
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4816
+ "div",
4817
+ {
4818
+ "data-ohw-bridge": "",
4819
+ "data-ohw-media-overlay": "",
4820
+ className: "flex items-center justify-center cursor-pointer",
4821
+ style: {
4822
+ ...box,
4823
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
4824
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
4825
+ // in-document, pointer-events does it natively. The button below opts back in, so
4826
+ // Replace still works.
4827
+ pointerEvents: hover.hasTextOverlap ? "none" : "auto",
4828
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
4829
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4830
+ },
4831
+ onClick: () => onReplace(hover.key),
4832
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4833
+ Button,
4834
+ {
4835
+ "data-ohw-media-overlay": "",
4836
+ variant: "outline",
4837
+ size: "sm",
4838
+ className: "gap-1.5 cursor-pointer hover:bg-background",
4839
+ style: OVERLAY_BUTTON_STYLE,
4840
+ onMouseDown: (e) => e.preventDefault(),
4841
+ onClick: (e) => {
4842
+ e.stopPropagation();
4843
+ onReplace(hover.key);
4844
+ },
4845
+ children: [
4846
+ isVideo ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.ImageIcon, { size: 14 }),
4847
+ isVideo ? "Replace video" : "Replace image"
4848
+ ]
4849
+ }
4850
+ )
4851
+ }
4852
+ )
4853
+ ] });
4854
+ }
4855
+
4642
4856
  // src/OhhwellsBridge.tsx
4643
4857
  var import_react_dom2 = require("react-dom");
4644
4858
  var import_navigation = require("next/navigation");
@@ -4918,61 +5132,61 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
4918
5132
  }
4919
5133
 
4920
5134
  // src/ui/dialog.tsx
4921
- var React5 = __toESM(require("react"), 1);
4922
- var import_radix_ui4 = require("radix-ui");
5135
+ var React7 = __toESM(require("react"), 1);
5136
+ var import_radix_ui5 = require("radix-ui");
4923
5137
 
4924
5138
  // src/ui/icons.tsx
4925
- var import_jsx_runtime10 = require("react/jsx-runtime");
5139
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4926
5140
  function IconX({ className, ...props }) {
4927
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4928
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M18 6 6 18" }),
4929
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m6 6 12 12" })
5141
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5142
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M18 6 6 18" }),
5143
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 6 12 12" })
4930
5144
  ] });
4931
5145
  }
4932
5146
  function IconChevronDown({ className, ...props }) {
4933
- return /* @__PURE__ */ (0, import_jsx_runtime10.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_runtime10.jsx)("path", { d: "m6 9 6 6 6-6" }) });
5147
+ return /* @__PURE__ */ (0, import_jsx_runtime12.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_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" }) });
4934
5148
  }
4935
5149
  function IconFile({ className, ...props }) {
4936
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4937
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4938
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
5150
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5151
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
5152
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4939
5153
  ] });
4940
5154
  }
4941
5155
  function IconInfo({ className, ...props }) {
4942
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4943
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
4944
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 16v-4" }),
4945
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 8h.01" })
5156
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5157
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
5158
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 16v-4" }),
5159
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 8h.01" })
4946
5160
  ] });
4947
5161
  }
4948
5162
  function IconArrowRight({ className, ...props }) {
4949
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4950
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M5 12h14" }),
4951
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m12 5 7 7-7 7" })
5163
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5164
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M5 12h14" }),
5165
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 5 7 7-7 7" })
4952
5166
  ] });
4953
5167
  }
4954
5168
  function IconSection({ className, ...props }) {
4955
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4956
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4957
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4958
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
5169
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5170
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
5171
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
5172
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4959
5173
  ] });
4960
5174
  }
4961
5175
 
4962
5176
  // src/ui/dialog.tsx
4963
- var import_jsx_runtime11 = require("react/jsx-runtime");
5177
+ var import_jsx_runtime13 = require("react/jsx-runtime");
4964
5178
  function Dialog2({ ...props }) {
4965
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui4.Dialog.Root, { "data-slot": "dialog", ...props });
5179
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
4966
5180
  }
4967
5181
  function DialogPortal({ ...props }) {
4968
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui4.Dialog.Portal, { ...props });
5182
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
4969
5183
  }
4970
5184
  function DialogOverlay({
4971
5185
  className,
4972
5186
  ...props
4973
5187
  }) {
4974
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4975
- import_radix_ui4.Dialog.Overlay,
5188
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5189
+ import_radix_ui5.Dialog.Overlay,
4976
5190
  {
4977
5191
  "data-slot": "dialog-overlay",
4978
5192
  "data-ohw-link-modal-root": "",
@@ -4981,12 +5195,12 @@ function DialogOverlay({
4981
5195
  }
4982
5196
  );
4983
5197
  }
4984
- var DialogContent = React5.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
5198
+ var DialogContent = React7.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
4985
5199
  const positionMode = container ? "absolute" : "fixed";
4986
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(DialogPortal, { container: container ?? void 0, children: [
4987
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
4988
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
4989
- import_radix_ui4.Dialog.Content,
5200
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5201
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5202
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
5203
+ import_radix_ui5.Dialog.Content,
4990
5204
  {
4991
5205
  ref,
4992
5206
  "data-slot": "dialog-content",
@@ -5001,13 +5215,13 @@ var DialogContent = React5.forwardRef(({ className, children, showCloseButton =
5001
5215
  ...props,
5002
5216
  children: [
5003
5217
  children,
5004
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5005
- import_radix_ui4.Dialog.Close,
5218
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5219
+ import_radix_ui5.Dialog.Close,
5006
5220
  {
5007
5221
  type: "button",
5008
5222
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5009
5223
  "aria-label": "Close",
5010
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconX, { "aria-hidden": true })
5224
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
5011
5225
  }
5012
5226
  ) : null
5013
5227
  ]
@@ -5015,105 +5229,65 @@ var DialogContent = React5.forwardRef(({ className, children, showCloseButton =
5015
5229
  )
5016
5230
  ] });
5017
5231
  });
5018
- DialogContent.displayName = import_radix_ui4.Dialog.Content.displayName;
5232
+ DialogContent.displayName = import_radix_ui5.Dialog.Content.displayName;
5019
5233
  function DialogHeader({ className, ...props }) {
5020
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5234
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5021
5235
  }
5022
5236
  function DialogFooter({ className, ...props }) {
5023
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5237
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5024
5238
  }
5025
- var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5026
- import_radix_ui4.Dialog.Title,
5239
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5240
+ import_radix_ui5.Dialog.Title,
5027
5241
  {
5028
5242
  ref,
5029
5243
  className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5030
5244
  ...props
5031
5245
  }
5032
5246
  ));
5033
- DialogTitle.displayName = import_radix_ui4.Dialog.Title.displayName;
5034
- var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5035
- import_radix_ui4.Dialog.Description,
5247
+ DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5248
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5249
+ import_radix_ui5.Dialog.Description,
5036
5250
  {
5037
5251
  ref,
5038
5252
  className: cn("text-sm text-muted-foreground", className),
5039
5253
  ...props
5040
5254
  }
5041
5255
  ));
5042
- DialogDescription.displayName = import_radix_ui4.Dialog.Description.displayName;
5043
- var DialogClose = import_radix_ui4.Dialog.Close;
5044
-
5045
- // src/ui/button.tsx
5046
- var React6 = __toESM(require("react"), 1);
5047
- var import_radix_ui5 = require("radix-ui");
5048
- var import_jsx_runtime12 = require("react/jsx-runtime");
5049
- var buttonVariants = cva(
5050
- "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",
5051
- {
5052
- variants: {
5053
- variant: {
5054
- default: "bg-primary text-primary-foreground hover:opacity-90",
5055
- outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
5056
- ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
5057
- },
5058
- size: {
5059
- default: "h-9",
5060
- sm: "h-8 min-w-[64px] px-2 py-1.5 text-sm"
5061
- }
5062
- },
5063
- defaultVariants: {
5064
- variant: "default",
5065
- size: "default"
5066
- }
5067
- }
5068
- );
5069
- var Button = React6.forwardRef(
5070
- ({ className, variant, size, asChild = false, ...props }, ref) => {
5071
- const Comp = asChild ? import_radix_ui5.Slot.Root : "button";
5072
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5073
- Comp,
5074
- {
5075
- ref,
5076
- "data-slot": "button",
5077
- className: cn(buttonVariants({ variant, size, className })),
5078
- ...props
5079
- }
5080
- );
5081
- }
5082
- );
5083
- Button.displayName = "Button";
5256
+ DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
5257
+ var DialogClose = import_radix_ui5.Dialog.Close;
5084
5258
 
5085
5259
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5086
- var import_jsx_runtime13 = require("react/jsx-runtime");
5260
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5087
5261
  function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5088
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5089
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5090
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
5091
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
5092
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5093
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5262
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5263
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5264
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-3", children: [
5265
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
5266
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5267
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5094
5268
  ] }),
5095
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5096
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5097
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5098
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5269
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5270
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5271
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5272
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5099
5273
  ] })
5100
5274
  ] })
5101
5275
  ] });
5102
5276
  }
5103
5277
 
5104
5278
  // src/ui/link-modal/SectionTreeItem.tsx
5105
- var import_jsx_runtime14 = require("react/jsx-runtime");
5279
+ var import_jsx_runtime15 = require("react/jsx-runtime");
5106
5280
  function SectionTreeItem({ section, onSelect, selected }) {
5107
5281
  const interactive = Boolean(onSelect);
5108
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5109
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5282
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5283
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5110
5284
  "div",
5111
5285
  {
5112
5286
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5113
5287
  "aria-hidden": true
5114
5288
  }
5115
5289
  ),
5116
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5290
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5117
5291
  "div",
5118
5292
  {
5119
5293
  role: interactive ? "button" : void 0,
@@ -5131,8 +5305,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
5131
5305
  interactive && selected && "border-primary"
5132
5306
  ),
5133
5307
  children: [
5134
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5135
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5308
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5309
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5136
5310
  ]
5137
5311
  }
5138
5312
  )
@@ -5140,11 +5314,11 @@ function SectionTreeItem({ section, onSelect, selected }) {
5140
5314
  }
5141
5315
  function SectionPickerList({ sections, onSelect }) {
5142
5316
  if (sections.length === 0) {
5143
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5317
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5144
5318
  }
5145
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1", children: [
5146
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5147
- sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionTreeItem, { section, onSelect }, section.id))
5319
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
5320
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5321
+ sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionTreeItem, { section, onSelect }, section.id))
5148
5322
  ] });
5149
5323
  }
5150
5324
 
@@ -5152,11 +5326,11 @@ function SectionPickerList({ sections, onSelect }) {
5152
5326
  var import_react4 = require("react");
5153
5327
 
5154
5328
  // src/ui/input.tsx
5155
- var React7 = __toESM(require("react"), 1);
5156
- var import_jsx_runtime15 = require("react/jsx-runtime");
5157
- var Input = React7.forwardRef(
5329
+ var React8 = __toESM(require("react"), 1);
5330
+ var import_jsx_runtime16 = require("react/jsx-runtime");
5331
+ var Input = React8.forwardRef(
5158
5332
  ({ className, type, ...props }, ref) => {
5159
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5333
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5160
5334
  "input",
5161
5335
  {
5162
5336
  type,
@@ -5175,9 +5349,9 @@ Input.displayName = "Input";
5175
5349
 
5176
5350
  // src/ui/label.tsx
5177
5351
  var import_radix_ui6 = require("radix-ui");
5178
- var import_jsx_runtime16 = require("react/jsx-runtime");
5352
+ var import_jsx_runtime17 = require("react/jsx-runtime");
5179
5353
  function Label({ className, ...props }) {
5180
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5354
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5181
5355
  import_radix_ui6.Label.Root,
5182
5356
  {
5183
5357
  "data-slot": "label",
@@ -5188,9 +5362,9 @@ function Label({ className, ...props }) {
5188
5362
  }
5189
5363
 
5190
5364
  // src/ui/link-modal/UrlOrPageInput.tsx
5191
- var import_jsx_runtime17 = require("react/jsx-runtime");
5365
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5192
5366
  function FieldChevron({ onClick }) {
5193
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5367
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5194
5368
  "button",
5195
5369
  {
5196
5370
  type: "button",
@@ -5198,7 +5372,7 @@ function FieldChevron({ onClick }) {
5198
5372
  onClick,
5199
5373
  "aria-label": "Open page list",
5200
5374
  tabIndex: -1,
5201
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconChevronDown, {})
5375
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconChevronDown, {})
5202
5376
  }
5203
5377
  );
5204
5378
  }
@@ -5237,12 +5411,12 @@ function UrlOrPageInput({
5237
5411
  "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]",
5238
5412
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5239
5413
  );
5240
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5241
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5242
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "relative w-full", children: [
5243
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5244
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5245
- readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5414
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5415
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5416
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "relative w-full", children: [
5417
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5418
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5419
+ readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5246
5420
  Input,
5247
5421
  {
5248
5422
  ref: inputRef,
@@ -5261,7 +5435,7 @@ function UrlOrPageInput({
5261
5435
  )
5262
5436
  }
5263
5437
  ),
5264
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5438
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5265
5439
  "button",
5266
5440
  {
5267
5441
  type: "button",
@@ -5269,26 +5443,26 @@ function UrlOrPageInput({
5269
5443
  onMouseDown: clearSelection,
5270
5444
  "aria-label": "Clear selected page",
5271
5445
  tabIndex: -1,
5272
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconX, { "aria-hidden": true })
5446
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconX, { "aria-hidden": true })
5273
5447
  }
5274
5448
  ) : null,
5275
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5449
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5276
5450
  ] }),
5277
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5451
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5278
5452
  "div",
5279
5453
  {
5280
5454
  "data-ohw-link-page-dropdown": "",
5281
5455
  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",
5282
5456
  onMouseDown: (e) => e.preventDefault(),
5283
- children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5457
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5284
5458
  "button",
5285
5459
  {
5286
5460
  type: "button",
5287
5461
  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",
5288
5462
  onClick: () => onPageSelect(page),
5289
5463
  children: [
5290
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
5291
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: page.title })
5464
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
5465
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate", children: page.title })
5292
5466
  ]
5293
5467
  },
5294
5468
  page.path
@@ -5296,7 +5470,7 @@ function UrlOrPageInput({
5296
5470
  }
5297
5471
  ) : null
5298
5472
  ] }),
5299
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5473
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5300
5474
  ] });
5301
5475
  }
5302
5476
 
@@ -5464,7 +5638,7 @@ function useLinkModalState({
5464
5638
  }
5465
5639
 
5466
5640
  // src/ui/link-modal/LinkEditorPanel.tsx
5467
- var import_jsx_runtime18 = require("react/jsx-runtime");
5641
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5468
5642
  function LinkEditorPanel({
5469
5643
  open = true,
5470
5644
  mode = "create",
@@ -5488,25 +5662,25 @@ function LinkEditorPanel({
5488
5662
  onSubmit
5489
5663
  });
5490
5664
  const isCancel = state.secondaryLabel === "Cancel";
5491
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5492
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5665
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5666
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5493
5667
  "button",
5494
5668
  {
5495
5669
  type: "button",
5496
5670
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5497
5671
  "aria-label": "Close",
5498
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconX, { "aria-hidden": true })
5672
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconX, { "aria-hidden": true })
5499
5673
  }
5500
5674
  ) }),
5501
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5502
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5503
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5675
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5676
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5677
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5504
5678
  DestinationBreadcrumb,
5505
5679
  {
5506
5680
  pageTitle: state.selectedPage.title,
5507
5681
  sectionLabel: state.selectedSection.label
5508
5682
  }
5509
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5683
+ ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5510
5684
  UrlOrPageInput,
5511
5685
  {
5512
5686
  value: state.searchValue,
@@ -5519,18 +5693,18 @@ function LinkEditorPanel({
5519
5693
  urlError: state.urlError
5520
5694
  }
5521
5695
  ),
5522
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5523
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5524
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5525
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5526
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
5696
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5697
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5698
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5699
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5700
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Pick a section this link should scroll to." })
5527
5701
  ] })
5528
5702
  ] }) : null,
5529
- state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5530
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5703
+ state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5704
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5531
5705
  ] }),
5532
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5533
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5706
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5707
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5534
5708
  Button,
5535
5709
  {
5536
5710
  type: "button",
@@ -5545,7 +5719,7 @@ function LinkEditorPanel({
5545
5719
  children: state.secondaryLabel
5546
5720
  }
5547
5721
  ),
5548
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5722
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5549
5723
  Button,
5550
5724
  {
5551
5725
  type: "button",
@@ -5564,7 +5738,7 @@ function LinkEditorPanel({
5564
5738
  }
5565
5739
 
5566
5740
  // src/ui/link-modal/LinkPopover.tsx
5567
- var import_jsx_runtime19 = require("react/jsx-runtime");
5741
+ var import_jsx_runtime20 = require("react/jsx-runtime");
5568
5742
  function LinkPopover({
5569
5743
  open = true,
5570
5744
  panelRef,
@@ -5572,14 +5746,14 @@ function LinkPopover({
5572
5746
  onClose,
5573
5747
  ...editorProps
5574
5748
  }) {
5575
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5749
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5576
5750
  Dialog2,
5577
5751
  {
5578
5752
  open,
5579
5753
  onOpenChange: (next) => {
5580
5754
  if (!next) onClose?.();
5581
5755
  },
5582
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5756
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5583
5757
  DialogContent,
5584
5758
  {
5585
5759
  ref: panelRef,
@@ -5589,7 +5763,7 @@ function LinkPopover({
5589
5763
  "data-ohw-bridge": "",
5590
5764
  showCloseButton: false,
5591
5765
  className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5592
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5766
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5593
5767
  }
5594
5768
  )
5595
5769
  }
@@ -5655,7 +5829,7 @@ function shouldUseDevFixtures() {
5655
5829
  }
5656
5830
 
5657
5831
  // src/ui/badge.tsx
5658
- var import_jsx_runtime20 = require("react/jsx-runtime");
5832
+ var import_jsx_runtime21 = require("react/jsx-runtime");
5659
5833
  var badgeVariants = cva(
5660
5834
  "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",
5661
5835
  {
@@ -5673,12 +5847,12 @@ var badgeVariants = cva(
5673
5847
  }
5674
5848
  );
5675
5849
  function Badge({ className, variant, ...props }) {
5676
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5850
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5677
5851
  }
5678
5852
 
5679
5853
  // src/OhhwellsBridge.tsx
5680
- var import_lucide_react3 = require("lucide-react");
5681
- var import_jsx_runtime21 = require("react/jsx-runtime");
5854
+ var import_lucide_react4 = require("lucide-react");
5855
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5682
5856
  var PRIMARY2 = "#0885FE";
5683
5857
  var IMAGE_FADE_MS = 300;
5684
5858
  function runOpacityFade(el, onDone) {
@@ -5852,7 +6026,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
5852
6026
  const root = (0, import_client.createRoot)(container);
5853
6027
  (0, import_react_dom.flushSync)(() => {
5854
6028
  root.render(
5855
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6029
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5856
6030
  SchedulingWidget,
5857
6031
  {
5858
6032
  notifyOnConnect,
@@ -6297,7 +6471,7 @@ function EditGlowChrome({
6297
6471
  dragDisabled = false
6298
6472
  }) {
6299
6473
  const GAP = 6;
6300
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6474
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
6301
6475
  "div",
6302
6476
  {
6303
6477
  ref: elRef,
@@ -6312,7 +6486,7 @@ function EditGlowChrome({
6312
6486
  zIndex: 2147483646
6313
6487
  },
6314
6488
  children: [
6315
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6489
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6316
6490
  "div",
6317
6491
  {
6318
6492
  style: {
@@ -6325,7 +6499,7 @@ function EditGlowChrome({
6325
6499
  }
6326
6500
  }
6327
6501
  ),
6328
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6502
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6329
6503
  "div",
6330
6504
  {
6331
6505
  "data-ohw-drag-handle-container": "",
@@ -6337,7 +6511,7 @@ function EditGlowChrome({
6337
6511
  transform: "translate(calc(-100% - 7px), -50%)",
6338
6512
  pointerEvents: dragDisabled ? "none" : "auto"
6339
6513
  },
6340
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6514
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6341
6515
  DragHandle,
6342
6516
  {
6343
6517
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -6441,7 +6615,7 @@ function FloatingToolbar({
6441
6615
  onEditLink
6442
6616
  }) {
6443
6617
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
6444
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6618
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6445
6619
  "div",
6446
6620
  {
6447
6621
  ref: elRef,
@@ -6462,12 +6636,12 @@ function FloatingToolbar({
6462
6636
  fontFamily: "sans-serif",
6463
6637
  pointerEvents: "auto"
6464
6638
  },
6465
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(CustomToolbar, { children: [
6466
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react6.default.Fragment, { children: [
6467
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarDivider, {}),
6639
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CustomToolbar, { children: [
6640
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react6.default.Fragment, { children: [
6641
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarDivider, {}),
6468
6642
  btns.map((btn) => {
6469
6643
  const isActive = activeCommands.has(btn.cmd);
6470
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6644
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6471
6645
  CustomToolbarButton,
6472
6646
  {
6473
6647
  title: btn.title,
@@ -6476,7 +6650,7 @@ function FloatingToolbar({
6476
6650
  e.preventDefault();
6477
6651
  onCommand(btn.cmd);
6478
6652
  },
6479
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6653
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6480
6654
  "svg",
6481
6655
  {
6482
6656
  width: "16",
@@ -6497,7 +6671,7 @@ function FloatingToolbar({
6497
6671
  );
6498
6672
  })
6499
6673
  ] }, gi)),
6500
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6674
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6501
6675
  CustomToolbarButton,
6502
6676
  {
6503
6677
  type: "button",
@@ -6511,7 +6685,7 @@ function FloatingToolbar({
6511
6685
  e.preventDefault();
6512
6686
  e.stopPropagation();
6513
6687
  },
6514
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react3.Link, { className: "size-4 shrink-0", "aria-hidden": true })
6688
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react4.Link, { className: "size-4 shrink-0", "aria-hidden": true })
6515
6689
  }
6516
6690
  ) : null
6517
6691
  ] })
@@ -6528,7 +6702,7 @@ function StateToggle({
6528
6702
  states,
6529
6703
  onStateChange
6530
6704
  }) {
6531
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6705
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6532
6706
  ToggleGroup,
6533
6707
  {
6534
6708
  "data-ohw-state-toggle": "",
@@ -6542,7 +6716,7 @@ function StateToggle({
6542
6716
  left: rect.right - 8,
6543
6717
  transform: "translateX(-100%)"
6544
6718
  },
6545
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6719
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6546
6720
  }
6547
6721
  );
6548
6722
  }
@@ -6578,7 +6752,10 @@ function OhhwellsBridge() {
6578
6752
  const stylesheet = Object.assign(document.createElement("link"), {
6579
6753
  id: figtreeFontId,
6580
6754
  rel: "stylesheet",
6581
- href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
6755
+ // Inter is loaded alongside Figtree for the media overlay's Replace button. The bridge
6756
+ // renders inside the customer's document, so it cannot assume any font is present — an
6757
+ // unloaded family would silently fall back to whatever the template happens to use.
6758
+ href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&family=Inter:wght@400;500;600&display=swap"
6582
6759
  });
6583
6760
  document.head.append(preconnect1, preconnect2, stylesheet);
6584
6761
  }
@@ -6618,6 +6795,8 @@ function OhhwellsBridge() {
6618
6795
  const glowElRef = (0, import_react6.useRef)(null);
6619
6796
  const hoveredImageRef = (0, import_react6.useRef)(null);
6620
6797
  const hoveredImageHasTextOverlapRef = (0, import_react6.useRef)(false);
6798
+ const [mediaHover, setMediaHover] = (0, import_react6.useState)(null);
6799
+ const [uploadingRects, setUploadingRects] = (0, import_react6.useState)({});
6621
6800
  const hoveredGapRef = (0, import_react6.useRef)(null);
6622
6801
  const imageUnhoverTimerRef = (0, import_react6.useRef)(null);
6623
6802
  const imageShowTimerRef = (0, import_react6.useRef)(null);
@@ -6699,7 +6878,7 @@ function OhhwellsBridge() {
6699
6878
  }
6700
6879
  hoveredGapRef.current = null;
6701
6880
  setSectionGap(null);
6702
- postToParent({ type: "ow:image-unhover" });
6881
+ setMediaHover(null);
6703
6882
  postToParent({ type: "ow:link-modal-lock", locked: true });
6704
6883
  const html = document.documentElement;
6705
6884
  const body = document.body;
@@ -6943,7 +7122,7 @@ function OhhwellsBridge() {
6943
7122
  deactivate();
6944
7123
  if (hoveredImageRef.current) {
6945
7124
  hoveredImageRef.current = null;
6946
- postToParentRef.current({ type: "ow:image-unhover" });
7125
+ setMediaHover(null);
6947
7126
  }
6948
7127
  setToolbarVariant("rich-text");
6949
7128
  siblingHintElRef.current = null;
@@ -7243,7 +7422,7 @@ function OhhwellsBridge() {
7243
7422
  if (isMediaEditable(editable)) {
7244
7423
  e.preventDefault();
7245
7424
  e.stopPropagation();
7246
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
7425
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
7247
7426
  return;
7248
7427
  }
7249
7428
  const hrefCtx = getHrefKeyFromElement(editable);
@@ -7417,25 +7596,23 @@ function OhhwellsBridge() {
7417
7596
  }
7418
7597
  return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
7419
7598
  };
7420
- const postImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
7599
+ const showImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
7421
7600
  const r2 = getVisibleRect(imgEl);
7422
7601
  const hasTextOverlap = forceTextOverlap ?? false;
7423
7602
  hoveredImageHasTextOverlapRef.current = hasTextOverlap;
7424
7603
  const video = imgEl.dataset.ohwEditable === "video" ? getVideoEl(imgEl) : null;
7425
- postToParentRef.current({
7426
- type: "ow:image-hover",
7604
+ setMediaHover({
7427
7605
  key: imgEl.dataset.ohwKey ?? "",
7428
- // Lets the parent pick the file-picker `accept`, overlay label/icon and drop
7429
- // mime-check without a parallel ow:video-* message channel.
7430
- elementType: imgEl.dataset.ohwEditable ?? "image",
7431
7606
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
7607
+ elementType: imgEl.dataset.ohwEditable ?? "image",
7432
7608
  hasTextOverlap,
7433
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {},
7434
- ...isDragOver ? { isDragOver: true } : {}
7609
+ isDragOver,
7610
+ ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
7435
7611
  });
7436
7612
  const track = imgEl.closest("[data-ohw-hover-pause]");
7437
7613
  if (track) track.setAttribute("data-ohw-hover-paused", "");
7438
7614
  };
7615
+ const clearImageHover = () => setMediaHover(null);
7439
7616
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
7440
7617
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7441
7618
  const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
@@ -7485,7 +7662,7 @@ function OhhwellsBridge() {
7485
7662
  hoveredImageRef.current = null;
7486
7663
  hoveredImageHasTextOverlapRef.current = false;
7487
7664
  resumeAnimTracks();
7488
- postToParentRef.current({ type: "ow:image-unhover" });
7665
+ clearImageHover();
7489
7666
  }
7490
7667
  return;
7491
7668
  }
@@ -7497,21 +7674,22 @@ function OhhwellsBridge() {
7497
7674
  if (hoveredImageRef.current) {
7498
7675
  hoveredImageRef.current = null;
7499
7676
  resumeAnimTracks();
7500
- postToParentRef.current({ type: "ow:image-unhover" });
7677
+ clearImageHover();
7501
7678
  }
7502
7679
  return;
7503
7680
  }
7504
7681
  }
7505
- if (activeElRef.current) {
7682
+ const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
7683
+ const activeEl = activeElRef.current;
7684
+ if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
7506
7685
  if (hoveredImageRef.current) {
7507
7686
  hoveredImageRef.current = null;
7508
7687
  hoveredImageHasTextOverlapRef.current = false;
7509
7688
  resumeAnimTracks();
7510
- postToParentRef.current({ type: "ow:image-unhover" });
7689
+ clearImageHover();
7511
7690
  }
7512
7691
  return;
7513
7692
  }
7514
- const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
7515
7693
  if (imgEl) {
7516
7694
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7517
7695
  const topEl = document.elementFromPoint(x, y);
@@ -7519,7 +7697,7 @@ function OhhwellsBridge() {
7519
7697
  if (hoveredImageRef.current) {
7520
7698
  hoveredImageRef.current = null;
7521
7699
  resumeAnimTracks();
7522
- postToParentRef.current({ type: "ow:image-unhover" });
7700
+ clearImageHover();
7523
7701
  }
7524
7702
  return;
7525
7703
  }
@@ -7528,7 +7706,7 @@ function OhhwellsBridge() {
7528
7706
  hoveredImageRef.current = null;
7529
7707
  hoveredImageHasTextOverlapRef.current = false;
7530
7708
  resumeAnimTracks();
7531
- postToParentRef.current({ type: "ow:image-unhover" });
7709
+ clearImageHover();
7532
7710
  }
7533
7711
  return;
7534
7712
  }
@@ -7553,14 +7731,14 @@ function OhhwellsBridge() {
7553
7731
  hoveredImageRef.current = null;
7554
7732
  hoveredImageHasTextOverlapRef.current = false;
7555
7733
  resumeAnimTracks();
7556
- postToParentRef.current({ type: "ow:image-unhover" });
7734
+ clearImageHover();
7557
7735
  }
7558
7736
  return;
7559
7737
  }
7560
7738
  if (isStateCardImage) {
7561
7739
  if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
7562
7740
  hoveredImageRef.current = imgEl;
7563
- postImageHover(imgEl, isDragOver, true);
7741
+ showImageHover(imgEl, isDragOver, true);
7564
7742
  }
7565
7743
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7566
7744
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -7572,7 +7750,7 @@ function OhhwellsBridge() {
7572
7750
  hoveredImageRef.current = null;
7573
7751
  hoveredImageHasTextOverlapRef.current = false;
7574
7752
  resumeAnimTracks();
7575
- postToParentRef.current({ type: "ow:image-unhover" });
7753
+ clearImageHover();
7576
7754
  }
7577
7755
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7578
7756
  if (textEditable && !isInsideNavigationItem(textEditable)) {
@@ -7584,7 +7762,7 @@ function OhhwellsBridge() {
7584
7762
  if (hoveredImageRef.current) {
7585
7763
  hoveredImageRef.current = null;
7586
7764
  resumeAnimTracks();
7587
- postToParentRef.current({ type: "ow:image-unhover" });
7765
+ clearImageHover();
7588
7766
  }
7589
7767
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7590
7768
  return;
@@ -7592,9 +7770,9 @@ function OhhwellsBridge() {
7592
7770
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7593
7771
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
7594
7772
  hoveredImageRef.current = imgEl;
7595
- postImageHover(imgEl, isDragOver);
7773
+ showImageHover(imgEl, isDragOver);
7596
7774
  } else if (isDragOver) {
7597
- postImageHover(imgEl, true);
7775
+ showImageHover(imgEl, true);
7598
7776
  }
7599
7777
  } else {
7600
7778
  const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
@@ -7611,7 +7789,7 @@ function OhhwellsBridge() {
7611
7789
  hoveredImageRef.current = null;
7612
7790
  hoveredImageHasTextOverlapRef.current = false;
7613
7791
  resumeAnimTracks();
7614
- postToParentRef.current({ type: "ow:image-unhover" });
7792
+ clearImageHover();
7615
7793
  }
7616
7794
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7617
7795
  const textEl = Array.from(
@@ -7724,7 +7902,7 @@ function OhhwellsBridge() {
7724
7902
  if (hoveredImageRef.current !== el) {
7725
7903
  hoveredImageRef.current = el;
7726
7904
  const isStateCard = !!el.closest("[data-ohw-editable-state]");
7727
- postImageHover(el, true, isStateCard ? true : void 0);
7905
+ showImageHover(el, true, isStateCard ? true : void 0);
7728
7906
  }
7729
7907
  };
7730
7908
  const handleDragLeave = (e) => {
@@ -7732,7 +7910,7 @@ function OhhwellsBridge() {
7732
7910
  if (imgEl) return;
7733
7911
  hoveredImageRef.current = null;
7734
7912
  resumeAnimTracks();
7735
- postToParentRef.current({ type: "ow:image-unhover" });
7913
+ clearImageHover();
7736
7914
  };
7737
7915
  const handleDrop = (e) => {
7738
7916
  e.preventDefault();
@@ -7756,31 +7934,7 @@ function OhhwellsBridge() {
7756
7934
  }
7757
7935
  hoveredImageRef.current = null;
7758
7936
  resumeAnimTracks();
7759
- postToParentRef.current({ type: "ow:image-unhover" });
7760
- };
7761
- const handleVideoSettings = (e) => {
7762
- if (e.data?.type !== "ow:video-settings") return;
7763
- const { key, autoplay, muted } = e.data;
7764
- document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7765
- const video = getVideoEl(el);
7766
- if (!video) return;
7767
- if (typeof autoplay === "boolean") video.autoplay = autoplay;
7768
- if (typeof muted === "boolean") video.muted = muted;
7769
- syncVideoPlayback(video);
7770
- postToParentRef.current({
7771
- type: "ow:change",
7772
- nodes: [
7773
- { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
7774
- { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
7775
- ]
7776
- });
7777
- postToParentRef.current({
7778
- type: "ow:video-settings-applied",
7779
- key,
7780
- autoplay: video.autoplay,
7781
- muted: video.muted
7782
- });
7783
- });
7937
+ clearImageHover();
7784
7938
  };
7785
7939
  const handleImageUrl = (e) => {
7786
7940
  if (e.data?.type !== "ow:image-url") return;
@@ -7798,7 +7952,11 @@ function OhhwellsBridge() {
7798
7952
  }
7799
7953
  });
7800
7954
  hoveredImageRef.current = null;
7801
- postToParentRef.current({ type: "ow:image-loaded", key });
7955
+ setUploadingRects((prev) => {
7956
+ const entry = prev[key];
7957
+ if (!entry || entry.fadingOut) return prev;
7958
+ return { ...prev, [key]: { ...entry, fadingOut: true } };
7959
+ });
7802
7960
  };
7803
7961
  let found = false;
7804
7962
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -8034,12 +8192,9 @@ function OhhwellsBridge() {
8034
8192
  if (hoveredImageRef.current) {
8035
8193
  const el = hoveredImageRef.current;
8036
8194
  const r2 = el.getBoundingClientRect();
8037
- postToParentRef.current({
8038
- type: "ow:image-hover",
8039
- key: el.dataset.ohwKey ?? "",
8040
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
8041
- hasTextOverlap: hoveredImageHasTextOverlapRef.current
8042
- });
8195
+ setMediaHover(
8196
+ (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
8197
+ );
8043
8198
  }
8044
8199
  };
8045
8200
  const handleSave = (e) => {
@@ -8128,19 +8283,39 @@ function OhhwellsBridge() {
8128
8283
  refreshActiveCommandsRef.current = handleSelectionChange;
8129
8284
  const handleDocMouseLeave = () => {
8130
8285
  hoveredImageRef.current = null;
8286
+ setMediaHover(null);
8131
8287
  document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
8132
8288
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
8133
8289
  activeStateElRef.current = null;
8134
8290
  setToggleState(null);
8135
8291
  };
8136
- const handleAnimLock = (e) => {
8137
- if (e.data?.type !== "ow:anim-lock") return;
8138
- const { key } = e.data;
8292
+ const handleImageUploading = (e) => {
8293
+ if (e.data?.type !== "ow:image-uploading") return;
8294
+ const { key, uploading } = e.data;
8295
+ setUploadingRects((prev) => {
8296
+ if (!uploading) {
8297
+ if (!(key in prev)) return prev;
8298
+ const next = { ...prev };
8299
+ delete next[key];
8300
+ return next;
8301
+ }
8302
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
8303
+ if (!el) return prev;
8304
+ const r2 = getVisibleRect(el);
8305
+ return {
8306
+ ...prev,
8307
+ [key]: { rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } }
8308
+ };
8309
+ });
8139
8310
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8140
8311
  const track = el.closest("[data-ohw-hover-pause]");
8141
- if (track) {
8312
+ if (!track) return;
8313
+ if (uploading) {
8142
8314
  uploadLockedTracks.add(track);
8143
8315
  track.setAttribute("data-ohw-hover-paused", "");
8316
+ } else {
8317
+ uploadLockedTracks.delete(track);
8318
+ track.removeAttribute("data-ohw-hover-paused");
8144
8319
  }
8145
8320
  });
8146
8321
  };
@@ -8197,7 +8372,7 @@ function OhhwellsBridge() {
8197
8372
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
8198
8373
  });
8199
8374
  if (stateCardImage) {
8200
- postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
8375
+ postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
8201
8376
  return;
8202
8377
  }
8203
8378
  const textEditable = Array.from(
@@ -8229,8 +8404,7 @@ function OhhwellsBridge() {
8229
8404
  window.addEventListener("message", handleClearSchedulingWidget);
8230
8405
  window.addEventListener("message", handleRemoveSchedulingSection);
8231
8406
  window.addEventListener("message", handleImageUrl);
8232
- window.addEventListener("message", handleVideoSettings);
8233
- window.addEventListener("message", handleAnimLock);
8407
+ window.addEventListener("message", handleImageUploading);
8234
8408
  window.addEventListener("message", handleCanvasHeight);
8235
8409
  window.addEventListener("message", handleParentScroll);
8236
8410
  window.addEventListener("message", handlePointerSync);
@@ -8277,8 +8451,7 @@ function OhhwellsBridge() {
8277
8451
  window.removeEventListener("message", handleClearSchedulingWidget);
8278
8452
  window.removeEventListener("message", handleRemoveSchedulingSection);
8279
8453
  window.removeEventListener("message", handleImageUrl);
8280
- window.removeEventListener("message", handleVideoSettings);
8281
- window.removeEventListener("message", handleAnimLock);
8454
+ window.removeEventListener("message", handleImageUploading);
8282
8455
  window.removeEventListener("message", handleCanvasHeight);
8283
8456
  window.removeEventListener("message", handleParentScroll);
8284
8457
  window.removeEventListener("resize", handleViewportResize);
@@ -8398,12 +8571,68 @@ function OhhwellsBridge() {
8398
8571
  const showEditLink = toolbarShowEditLink;
8399
8572
  const currentSections = sectionsByPath[pathname] ?? [];
8400
8573
  linkPopoverOpenRef.current = linkPopover !== null;
8574
+ const handleMediaReplace = (0, import_react6.useCallback)(
8575
+ (key) => {
8576
+ postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
8577
+ },
8578
+ [postToParent, mediaHover?.elementType]
8579
+ );
8580
+ const handleVideoSettingsChange = (0, import_react6.useCallback)(
8581
+ (key, settings) => {
8582
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8583
+ const video = getVideoEl(el);
8584
+ if (!video) return;
8585
+ if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
8586
+ if (typeof settings.muted === "boolean") video.muted = settings.muted;
8587
+ syncVideoPlayback(video);
8588
+ postToParent({
8589
+ type: "ow:change",
8590
+ nodes: [
8591
+ { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
8592
+ { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
8593
+ ]
8594
+ });
8595
+ setMediaHover(
8596
+ (prev) => prev && prev.key === key ? { ...prev, videoAutoplay: video.autoplay, videoMuted: video.muted } : prev
8597
+ );
8598
+ });
8599
+ },
8600
+ [postToParent]
8601
+ );
8602
+ const handleMediaFadeOutComplete = (0, import_react6.useCallback)((key) => {
8603
+ setUploadingRects((prev) => {
8604
+ if (!(key in prev)) return prev;
8605
+ const next = { ...prev };
8606
+ delete next[key];
8607
+ return next;
8608
+ });
8609
+ }, []);
8401
8610
  return bridgeRoot ? (0, import_react_dom2.createPortal)(
8402
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
8403
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8404
- siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8405
- hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8406
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8611
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8612
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8613
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8614
+ MediaOverlay,
8615
+ {
8616
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
8617
+ isUploading: true,
8618
+ fadingOut,
8619
+ onFadeOutComplete: handleMediaFadeOutComplete,
8620
+ onReplace: handleMediaReplace
8621
+ },
8622
+ `uploading-${key}`
8623
+ )),
8624
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8625
+ MediaOverlay,
8626
+ {
8627
+ hover: mediaHover,
8628
+ isUploading: false,
8629
+ onReplace: handleMediaReplace,
8630
+ onVideoSettingsChange: handleVideoSettingsChange
8631
+ }
8632
+ ),
8633
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8634
+ hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8635
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8407
8636
  ItemInteractionLayer,
8408
8637
  {
8409
8638
  rect: toolbarRect,
@@ -8414,7 +8643,7 @@ function OhhwellsBridge() {
8414
8643
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8415
8644
  onDragHandleDragStart: handleItemDragStart,
8416
8645
  onDragHandleDragEnd: handleItemDragEnd,
8417
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8646
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8418
8647
  ItemActionToolbar,
8419
8648
  {
8420
8649
  onEditLink: openLinkPopoverForSelected,
@@ -8424,8 +8653,8 @@ function OhhwellsBridge() {
8424
8653
  ) : void 0
8425
8654
  }
8426
8655
  ),
8427
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
8428
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8656
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8657
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8429
8658
  EditGlowChrome,
8430
8659
  {
8431
8660
  rect: toolbarRect,
@@ -8434,7 +8663,7 @@ function OhhwellsBridge() {
8434
8663
  dragDisabled: reorderDragDisabled
8435
8664
  }
8436
8665
  ),
8437
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8666
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8438
8667
  FloatingToolbar,
8439
8668
  {
8440
8669
  rect: toolbarRect,
@@ -8447,7 +8676,7 @@ function OhhwellsBridge() {
8447
8676
  }
8448
8677
  )
8449
8678
  ] }),
8450
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
8679
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
8451
8680
  "div",
8452
8681
  {
8453
8682
  "data-ohw-max-badge": "",
@@ -8473,7 +8702,7 @@ function OhhwellsBridge() {
8473
8702
  ]
8474
8703
  }
8475
8704
  ),
8476
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8705
+ toggleState && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8477
8706
  StateToggle,
8478
8707
  {
8479
8708
  rect: toggleState.rect,
@@ -8482,15 +8711,15 @@ function OhhwellsBridge() {
8482
8711
  onStateChange: handleStateChange
8483
8712
  }
8484
8713
  ),
8485
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
8714
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
8486
8715
  "div",
8487
8716
  {
8488
8717
  "data-ohw-section-insert-line": "",
8489
8718
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8490
8719
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8491
8720
  children: [
8492
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8493
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8721
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8722
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8494
8723
  Badge,
8495
8724
  {
8496
8725
  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",
@@ -8503,11 +8732,11 @@ function OhhwellsBridge() {
8503
8732
  children: "Add Section"
8504
8733
  }
8505
8734
  ),
8506
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8735
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8507
8736
  ]
8508
8737
  }
8509
8738
  ),
8510
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8739
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8511
8740
  LinkPopover,
8512
8741
  {
8513
8742
  panelRef: linkPopoverPanelRef,
@@ -8523,15 +8752,15 @@ function OhhwellsBridge() {
8523
8752
  },
8524
8753
  `${linkPopover.key}-${pathname}`
8525
8754
  ) : null,
8526
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
8755
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
8527
8756
  "div",
8528
8757
  {
8529
8758
  "data-ohw-section-insert-line": "",
8530
8759
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8531
8760
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8532
8761
  children: [
8533
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8534
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8762
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8763
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8535
8764
  Badge,
8536
8765
  {
8537
8766
  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",
@@ -8544,7 +8773,7 @@ function OhhwellsBridge() {
8544
8773
  children: "Add Section"
8545
8774
  }
8546
8775
  ),
8547
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8776
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8548
8777
  ]
8549
8778
  }
8550
8779
  )